[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::os::unix::net::UnixListener;
|
||||
use std::io;
|
||||
use std::os::unix::net::SocketAddr;
|
||||
use std::os::unix::net::{UnixListener, UnixStream};
|
||||
|
||||
pub enum PacketThreadCommand {
|
||||
UpdateConfig(UpdateConfigCommand),
|
||||
|
@ -8,8 +10,36 @@ pub enum PacketThreadCommand {
|
|||
|
||||
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>> {
|
||||
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(())
|
||||
}
|
|
@ -100,6 +100,7 @@ fn main() {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
match tx.send(PacketThreadCommand::Stop) {
|
||||
Ok(_) => (),
|
||||
Err(e) => {
|
||||
|
|
|
@ -37,6 +37,9 @@ pub fn packet_thread(mut device: TunDevice, rx: Receiver<PacketThreadCommand>) -
|
|||
|
||||
if let Some(p) = packet {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,4 +4,7 @@
|
|||
#[path = "linux.rs"]
|
||||
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 qcrypto;
|
||||
pub mod noise;
|
||||
pub mod stack;
|
||||
|
||||
/// Gets the compile-time versioning information for the engine build.
|
||||
pub const fn version() -> &'static str {
|
||||
|
|
|
@ -4,4 +4,5 @@ pub mod error;
|
|||
pub mod data;
|
||||
|
||||
#[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