[noise/drivers/stack] add rfc6479 / work on packet entry for UDP / start working on pproc stack
This commit is contained in:
parent
1f5a812262
commit
e1093cab1b
|
@ -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(())
|
||||||
}
|
}
|
|
@ -100,6 +100,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
match tx.send(PacketThreadCommand::Stop) {
|
match tx.send(PacketThreadCommand::Stop) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,3 +71,4 @@ impl GenericDriver for TunDevice {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,4 +4,7 @@
|
||||||
#[path = "linux.rs"]
|
#[path = "linux.rs"]
|
||||||
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;
|
|
@ -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(())
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -4,4 +4,5 @@ pub mod error;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
|
pub mod rfc6479;
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue