From 929eadb651ba41bb72ba8f85a0d68c0cbad18661 Mon Sep 17 00:00:00 2001 From: Mathias Hall-Andersen Date: Sun, 1 Sep 2019 17:16:01 +0200 Subject: Outbound cryptkey routing --- src/router/device.rs | 88 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 12 deletions(-) (limited to 'src/router/device.rs') diff --git a/src/router/device.rs b/src/router/device.rs index f04cf97..4fb0334 100644 --- a/src/router/device.rs +++ b/src/router/device.rs @@ -1,6 +1,7 @@ +use std::cmp; use std::collections::HashMap; use std::net::{Ipv4Addr, Ipv6Addr}; -use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::{Arc, Weak}; use std::thread; use std::time::Instant; @@ -14,10 +15,21 @@ use super::super::types::{Bind, KeyPair, Tun}; use super::anti_replay::AntiReplay; use super::peer; use super::peer::{Peer, PeerInner}; +use super::SIZE_MESSAGE_PREFIX; -use super::types::{Callback, Callbacks, KeyCallback, Opaque, PhantomCallbacks}; +use super::types::{Callback, Callbacks, KeyCallback, Opaque, PhantomCallbacks, RouterError}; use super::workers::{worker_parallel, JobParallel}; +// minimum sizes for IP headers +const SIZE_IP4_HEADER: usize = 16; +const SIZE_IP6_HEADER: usize = 36; + +const VERSION_IP4: u8 = 4; +const VERSION_IP6: u8 = 6; + +const OFFSET_IP4_DST: usize = 16; +const OFFSET_IP6_DST: usize = 24; + pub struct DeviceInner { // IO & timer generics pub tun: T, @@ -27,9 +39,9 @@ pub struct DeviceInner { pub call_need_key: C::CallbackKey, // threading and workers - pub running: AtomicBool, // workers running? - pub parked: AtomicBool, // any workers parked? - pub injector: Injector, // parallel enc/dec task injector + pub running: AtomicBool, // workers running? + pub parked: AtomicBool, // any workers parked? + pub injector: Injector>, // parallel enc/dec task injector // routing pub recv: spin::RwLock>>, // receiver id -> decryption state @@ -38,11 +50,10 @@ pub struct DeviceInner { } pub struct EncryptionState { - pub key: [u8; 32], // encryption key - pub id: u32, // sender id - pub nonce: u64, // next available nonce - pub death: Instant, // time when the key no longer can be used for encryption - // (birth + reject-after-time - keepalive-timeout - rekey-timeout) + pub key: [u8; 32], // encryption key + pub id: u32, // receiver id + pub nonce: u64, // next available nonce + pub death: Instant, // (birth + reject-after-time - keepalive-timeout - rekey-timeout) } pub struct DecryptionState { @@ -144,8 +155,61 @@ impl Device { /// /// - pt_msg: IP packet to cryptkey route /// - pub fn send(&self, pt_msg: &mut [u8]) { - unimplemented!(); + pub fn send(&self, msg: Vec) -> Result<(), RouterError> { + // ensure that the type field access is within bounds + if msg.len() < cmp::min(SIZE_IP4_HEADER, SIZE_IP6_HEADER) + SIZE_MESSAGE_PREFIX { + return Err(RouterError::MalformedIPHeader); + } + + // ignore header prefix (for in-place transport message construction) + let packet = &msg[SIZE_MESSAGE_PREFIX..]; + + // lookup peer based on IP packet destination address + let peer = match packet[0] >> 4 { + VERSION_IP4 => { + if msg.len() >= SIZE_IP4_HEADER { + // extract IPv4 destination address + let mut dst = [0u8; 4]; + dst.copy_from_slice(&packet[OFFSET_IP4_DST..OFFSET_IP4_DST + 4]); + let dst = Ipv4Addr::from(dst); + + // lookup peer (project unto and clone "value" field) + self.0 + .ipv4 + .read() + .longest_match(dst) + .and_then(|(_, _, p)| p.upgrade()) + .ok_or(RouterError::NoCryptKeyRoute) + } else { + Err(RouterError::MalformedIPHeader) + } + } + VERSION_IP6 => { + if msg.len() >= SIZE_IP6_HEADER { + // extract IPv6 destination address + let mut dst = [0u8; 16]; + dst.copy_from_slice(&packet[OFFSET_IP6_DST..OFFSET_IP6_DST + 16]); + let dst = Ipv6Addr::from(dst); + + // lookup peer (project unto and clone "value" field) + self.0 + .ipv6 + .read() + .longest_match(dst) + .and_then(|(_, _, p)| p.upgrade()) + .ok_or(RouterError::NoCryptKeyRoute) + } else { + Err(RouterError::MalformedIPHeader) + } + } + _ => Err(RouterError::MalformedIPHeader), + }?; + + // schedule for encryption and transmission to peer + if let Some(job) = peer.send_job(msg) { + self.0.injector.push((peer.clone(), job)); + } + Ok(()) } /// Receive an encrypted transport message -- cgit v1.2.3-59-g8ed1b