[noise/drivers/stack] add rfc6479 / work on packet entry for UDP / start working on pproc stack

This commit is contained in:
c0repwn3r 2023-01-11 14:45:37 -05:00
parent 1f5a812262
commit e1093cab1b
Signed by: core
GPG Key ID: FDBF740DADDCEECF
12 changed files with 274 additions and 3 deletions

View File

@ -1,5 +1,7 @@
use std::error::Error; use std::error::Error;
use std::os::unix::net::UnixListener; use std::io;
use std::os::unix::net::SocketAddr;
use std::os::unix::net::{UnixListener, UnixStream};
pub enum PacketThreadCommand { pub enum PacketThreadCommand {
UpdateConfig(UpdateConfigCommand), UpdateConfig(UpdateConfigCommand),
@ -8,8 +10,36 @@ pub enum PacketThreadCommand {
pub struct UpdateConfigCommand {} pub struct UpdateConfigCommand {}
pub struct CommandClient {
stream: UnixStream,
addr: SocketAddr,
current_command: CommandType
}
pub enum CommandType {
WaitingForCommand,
Read,
Write
}
pub fn command_handler(listener: UnixListener) -> Result<(), Box<dyn Error>> { pub fn command_handler(listener: UnixListener) -> Result<(), Box<dyn Error>> {
listener.set_nonblocking(true)?; listener.set_nonblocking(true)?;
let mut clients: Vec<CommandClient> = vec![];
match listener.accept() {
Ok((stream, addr)) => {
clients.push(CommandClient { stream, addr, current_command: CommandType::WaitingForCommand })
},
Err(e) if matches!(e.kind(), io::ErrorKind::WouldBlock) => {
// ignore.
},
Err(e) => {
// okay, real error, knock it up
return Err(e.into())
}
}
// uh lets play ignorey ignorey for now
Ok(()) Ok(())
} }

View File

@ -100,6 +100,7 @@ fn main() {
} }
}; };
match tx.send(PacketThreadCommand::Stop) { match tx.send(PacketThreadCommand::Stop) {
Ok(_) => (), Ok(_) => (),
Err(e) => { Err(e) => {

View File

@ -37,6 +37,9 @@ pub fn packet_thread(mut device: TunDevice, rx: Receiver<PacketThreadCommand>) -
if let Some(p) = packet { if let Some(p) = packet {
info!("recv_packet_on_tun {:?}", p.header); info!("recv_packet_on_tun {:?}", p.header);
// parse and route
} }
} }
} }

View File

@ -0,0 +1,20 @@
//! Defines the error type for the glue layer
use std::error::Error;
use std::fmt::{Display, Formatter};
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
/// Represents an error in the driver (glue) portion of quicktap
pub enum DriverError {
/// An invalid packet type has been received on the interface, and quicktap does not know how to process it
/// This is raised in the PREENTRY stage of packet processing
InvalidPacketTypeRecvOnInterface
}
impl Error for DriverError {}
impl Display for DriverError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidPacketTypeRecvOnInterface => write!(f, "Invalid packet type received on interface")
}
}
}

View File

@ -71,3 +71,4 @@ impl GenericDriver for TunDevice {
Ok(()) Ok(())
} }
} }

View File

@ -5,3 +5,6 @@
pub mod tun; // Tun/tap drivers for Linux pub mod tun; // Tun/tap drivers for Linux
pub mod tungeneric; pub mod tungeneric;
pub mod udp_listener;
pub mod error;

View File

@ -0,0 +1,82 @@
//! The logic for the WireGuard UDP socket.
use std::error::Error;
use std::io;
use std::net::UdpSocket;
use crate::drivers::error::DriverError;
use crate::stack::entry::PacketEntryStage;
use crate::stack::PacketProcessingStage;
/// The maximum amount of bytes that can be taken up by a Handshake Initiation packet.
pub const MSG1_MAX_BYTES: usize = 148;
/// The maximum amount of bytes that can be taken up by a Handshake Response packet.
pub const MSG2_MAX_BYTES: usize = 92;
/// The maximum amount of bytes that can be taken up by a data packet.
pub const MSGDATA_MAX_BYTES: usize = 65567;
/// The maximum amount of bytes that can be taken up by a cookie reply packet.
pub const MSGCOOKIE_MAX_BYTES: usize = 64;
/// Represents the result of a socket processing round
pub enum SocketRoundResult {
/// Represents that an io-specific error was received
IoError(io::Error),
/// Represents that a non-io-specific error was received
GenericAnyError(Box<dyn Error>)
}
/// Run a socket update round on the (nonblocking!) UDP socket provided. Checks for any packets that may be present and send them off to get processed.
/// **Warning!** This only runs *one* update round - therefore only one packet will be processed if any are present.
/// If you're writing a frontend, this is probably not what you want. Use `qtap_socket_rounds_until_empty` instead.
/// # Errors
/// This function will error if a socket read fails or the processing of the packet fails. See `PacketEntryStage::process_packet()` for more details on packet processing.
pub fn qtap_socket_round(socket: &mut UdpSocket) -> Result<(), SocketRoundResult> {
let mut packet_id = [0u8; 1];
match socket.peek(&mut packet_id) {
Ok(_) => (),
Err(e) => return Err(SocketRoundResult::IoError(e))
}; // Intentionally peek into a 1-byte vector. This will NOT remove the datagram from the queue.
// We WANT to discard the rest of the packet; right now we only care about the first byte so we know how much memory we need to allocate for the buffer.
// The packet type tells us this.
let buf_size = match packet_id[0] {
1 => MSG1_MAX_BYTES,
2 => MSG2_MAX_BYTES,
4 => MSGDATA_MAX_BYTES,
3 => MSGCOOKIE_MAX_BYTES,
_ => return Err(SocketRoundResult::GenericAnyError(DriverError::InvalidPacketTypeRecvOnInterface.into()))
};
let mut packet_buf = vec![0u8; buf_size];
let (read_bytes, from) = match socket.recv_from(&mut packet_buf) {
Ok(r) => r,
Err(e) => return Err(SocketRoundResult::IoError(e))
};
let packet = packet_buf[..read_bytes].to_vec();
match PacketEntryStage::process_packet(packet, &from) {
Ok(_) => Ok(()),
Err(e) => Err(SocketRoundResult::GenericAnyError(e))
}
}
/// Run several socket update rounds on the (nonblocking!) UDP socket provided. Will continually call `qtap_socket_round` in a loop until `io::WouldBlock` is returned.
/// **Warning!** This runs as many socket rounds are needed to clear the UDP datagram queue. If there is a long queue, this function will take a very long time.
/// If you only want to run one update, use `qtap_socket_round` instead.
/// # Errors
/// See `qtap_socket_round`
pub fn qtap_socket_rounds_until_empty(socket: &mut UdpSocket) -> Result<(), Box<dyn Error>> {
loop {
match qtap_socket_round(socket) {
Ok(_) => (),
Err(e) => {
match e {
SocketRoundResult::IoError(e) if matches!(e.kind(), io::ErrorKind::WouldBlock) => break,
SocketRoundResult::GenericAnyError(e) => return Err(e),
SocketRoundResult::IoError(e) => return Err(e.into())
}
}
}
}
Ok(())
}

View File

@ -16,6 +16,7 @@ pub use cidr;
pub mod drivers; pub mod drivers;
pub mod qcrypto; pub mod qcrypto;
pub mod noise; pub mod noise;
pub mod stack;
/// Gets the compile-time versioning information for the engine build. /// Gets the compile-time versioning information for the engine build.
pub const fn version() -> &'static str { pub const fn version() -> &'static str {

View File

@ -5,3 +5,4 @@ pub mod data;
#[cfg(test)] #[cfg(test)]
pub mod tests; pub mod tests;
pub mod rfc6479;

View File

@ -0,0 +1,76 @@
//! A Rust implementation of the anti-replay algorithm described in [RFC 6479](https://rfc-editor.org/rfc6479).
const SIZE_OF_INTEGER: usize = std::mem::size_of::<usize>() * 8;
const BITMAP_LEN: usize = 1024 / SIZE_OF_INTEGER;
const BITMAP_INDEX_MASK: u64 = BITMAP_LEN as u64 - 1;
const REDUNDANT_BIT_SHIFTS: u64 = 5;
const REDUNDANT_BITS: u64 = 1 << REDUNDANT_BIT_SHIFTS;
const BITMAP_LOC_MASK: u64 = REDUNDANT_BITS - 1;
const WINDOW_SIZE: u64 = 512;
#[derive(Default)]
/// An implementation of the RFC6479 anti-replay algorithm
pub struct ShiftWindow {
replaywin_lastseq: u64,
replaywin_bitmap: [usize; BITMAP_LEN],
}
impl ShiftWindow {
/// Create a new ShiftWindow with default values
pub fn new() -> Self {
Self::default()
}
/// Check if a given sequence value is okay given the current state of the shift window
pub const fn check_replay_window(&self, seq: u64) -> bool {
// first == 0 or wrapped
if seq == 0 {
return false;
}
// larger is always good
if seq > self.replaywin_lastseq {
return true;
}
// too old
if (seq + WINDOW_SIZE) < self.replaywin_lastseq {
return false;
}
let bit_location = seq & BITMAP_LOC_MASK;
let index = (seq >> REDUNDANT_BIT_SHIFTS) & BITMAP_INDEX_MASK;
self.replaywin_bitmap[index as usize] & (1 << bit_location) == 0
}
/// Update the internal state of the replay window to mark the given sequence number as used and disallow future use of it.
pub fn update_replay_window(&mut self, seq: u64) {
if !self.check_replay_window(seq) {
return;
}
let index = seq >> REDUNDANT_BIT_SHIFTS;
if seq > self.replaywin_lastseq {
let index_cur = self.replaywin_lastseq >> REDUNDANT_BIT_SHIFTS;
let mut diff = index - index_cur;
if diff > BITMAP_LEN as u64 {
diff = BITMAP_LEN as u64;
}
for id in 0..diff {
let iindex = (id + index_cur + 1) & BITMAP_INDEX_MASK;
self.replaywin_bitmap[iindex as usize] = 0;
}
self.replaywin_lastseq = seq;
}
let index = index & BITMAP_INDEX_MASK;
let bit_location = seq & BITMAP_LOC_MASK;
self.replaywin_bitmap[index as usize] |= 1 << bit_location;
}
}

View File

@ -0,0 +1,14 @@
//! The ENTRY stage of the packet processing stack. Provides a `PacketEntryStage` which implements `PacketProcessingStage`.
use std::error::Error;
use std::net::SocketAddr;
use crate::stack::{PacketProcessingStage};
/// The ENTRY stage of the packet processing stack
pub struct PacketEntryStage {}
impl PacketProcessingStage for PacketEntryStage {
fn process_packet(pkt: Vec<u8>, from: &SocketAddr) -> Result<(), Box<dyn Error>> {
Ok(())
}
}

39
quicktap/src/stack/mod.rs Normal file
View File

@ -0,0 +1,39 @@
//! quicktap's packet processing stack
//! Packet processing is done in several *stages*:
//! - ENTRY
//! - CRYPT
//! - ROUTING
//! - PROCESSING
//! - EXIT
//! # UDP Packet Flow
//! For a packet received on the UDP socket, the following route is taken through the processing stack:
//! ```text
//! UDP Socket -> ENTRY -> CRYPT -> (drop) <-----+
//! | | ^ |
//! | | | |
//! | +-----> ROUTING -> CRYPT -> EXIT
//! | v
//! +---> PROCESSING -> (end flow)
//! ```
//! # TUN Packet Flow
//! For a packet received on the TUN interface, the following route is taken through the processing stack:
//! ```text
//! TUN Interface -> ENTRY -> ROUTING -> CRYPT -> EXIT
//! | |
//! v |
//! (drop) <----+
//! ```
use std::error::Error;
use std::net::SocketAddr;
pub mod entry;
/// The methods and functions that need to be implemented by a stage in the packet processor
pub trait PacketProcessingStage {
/// Process a raw datagram. This should go down the WHOLE routing chain - assume previous steps have already done.
/// For example, if you are the CRYPT layer, and are processing a tun packet, assume that the ENTRY and ROUTING stack stages have already been completed and you are being given the result of that processing.
/// # Errors
/// This function will error if an error occurs
fn process_packet(pkt: Vec<u8>, from: &SocketAddr) -> Result<(), Box<dyn Error>> where Self: Sized;
}