diff --git a/Cargo.lock b/Cargo.lock index b802102..4b23724 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,6 +86,15 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "atomic-polyfill" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +dependencies = [ + "critical-section", +] + [[package]] name = "atty" version = "0.2.14" @@ -237,6 +246,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + [[package]] name = "crypto-mac" version = "0.10.1" @@ -269,6 +284,38 @@ dependencies = [ "zeroize", ] +[[package]] +name = "defmt" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2d011b2fee29fb7d659b83c43fce9a2cb4df453e16d441a51448e448f3f98" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54f0216f6c5acb5ae1a47050a6645024e6edafc2ee32d421955eccfef12ef92e" +dependencies = [ + "defmt-parser", + "proc-macro-error", + "proc-macro2 1.0.66", + "quote 1.0.33", + "syn 2.0.29", +] + +[[package]] +name = "defmt-parser" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "269924c02afd7f94bc4cecbfa5c379f6ffcf9766b3408fe63d22c728654eccd0" +dependencies = [ + "thiserror", +] + [[package]] name = "derive_more" version = "0.14.1" @@ -277,7 +324,7 @@ checksum = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" dependencies = [ "proc-macro2 0.4.30", "quote 0.6.13", - "rustc_version", + "rustc_version 0.2.3", "syn 0.15.44", ] @@ -561,6 +608,28 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version 0.4.0", + "spin", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.3.3" @@ -657,6 +726,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + [[package]] name = "memchr" version = "2.5.0" @@ -1022,7 +1097,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.18", ] [[package]] @@ -1053,6 +1137,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + [[package]] name = "semver-parser" version = "0.7.0" @@ -1155,6 +1245,21 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +[[package]] +name = "smoltcp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2e3a36ac8fea7b94e666dfa3871063d6e0a5c9d5d4fec9a1a6b7b6760f0229" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "cfg-if", + "defmt", + "heapless", + "libc", + "managed", +] + [[package]] name = "socket2" version = "0.5.3" @@ -1222,6 +1327,21 @@ dependencies = [ "pcap-sys", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.8.0" @@ -1301,6 +1421,7 @@ dependencies = [ "packets", "pcap-sys", "rand 0.8.5", + "smoltcp", "tokio", "tokio-stream", ] diff --git a/nl-sys/src/route.rs b/nl-sys/src/route.rs index f973b53..3824325 100644 --- a/nl-sys/src/route.rs +++ b/nl-sys/src/route.rs @@ -170,7 +170,9 @@ pub fn get_macs_and_src_for_ip( 0 }; - let Ok(dst_addr): Result = (&dst).try_into() else { return false }; + let Ok(dst_addr): Result = (&dst).try_into() else { + return false; + }; let dst_addr: u32 = dst_addr.into(); (mask & dst_addr) == (mask & ip_int) @@ -181,7 +183,12 @@ pub fn get_macs_and_src_for_ip( #[cfg(debug_assertions)] { for link in links.iter() { - println!("Link {}: {:?} ({})", link.name(), link.addr(), link.ifindex()); + println!( + "Link {}: {:?} ({})", + link.name(), + link.addr(), + link.ifindex() + ); println!("\tAddrs:"); for addr in addrs.iter().filter(|addr| addr.ifindex() == link.ifindex()) { @@ -191,7 +198,11 @@ pub fn get_macs_and_src_for_ip( } println!("\tNeighbors:"); - for neigh in neighs.iter().filter(|neigh| neigh.ifindex() == link.ifindex()) { + for neigh in neighs + .iter() + .filter(|neigh| neigh.ifindex() == link.ifindex()) + { + println!("\t\ttest {:?}, {:?}", neigh.ifindex(), neigh.dst()); println!("\t\t{:?}, {:?}", neigh.dst(), neigh.lladdr()); } } @@ -224,7 +235,9 @@ pub fn get_neigh_for_addr( addr: &Addr, ) -> Option<(Ipv4Addr, Link, [u8; 6])> { for link in links.iter() { - let Some(neigh) = link.get_neigh(&neighs, addr) else { continue; }; + let Some(neigh) = link.get_neigh(&neighs, addr) else { + continue; + }; return Some((addr.try_into().ok()?, link, neigh)); } @@ -239,7 +252,7 @@ pub fn get_neigh_for_addr( }; let Some(first_hop) = def_neigh.hop_iter().next() else { - return None + return None; }; if n.ifindex() != first_hop.ifindex() { @@ -322,8 +335,12 @@ impl Addr { } // Determines the type of data in [`Addr::hw_address`] - pub fn atype(&self) -> c_int { - unsafe { nl_addr_get_family(self.addr) } + pub fn atype(&self) -> Option { + if self.addr.is_null() { + None + } else { + Some(unsafe { nl_addr_get_family(self.addr) }) + } } /// Returns the length of the subnet mask applying to this address @@ -334,8 +351,8 @@ impl Addr { impl Debug for Addr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.atype() { - AF_INET => { + let res = match self.atype() { + Some(AF_INET) => { let octets = self.hw_address(); f.debug_struct("Addr") .field( @@ -351,7 +368,7 @@ impl Debug for Addr { ) .finish() } - AF_LLC => { + Some(AF_LLC) => { let octets = self.hw_address(); f.debug_struct("Addr") @@ -359,22 +376,23 @@ impl Debug for Addr { "addr", &format!( "{:02X?}:{:02X?}:{:02X?}:{:02X?}:{:02X?}:{:02X?}", - octets[0], - octets[1], - octets[2], - octets[3], - octets[4], - octets[5], - ) + octets[0], octets[1], octets[2], octets[3], octets[4], octets[5], + ), ) .finish() } + None => f + .debug_struct("Addr") + .field("addr", &"unknown") + .field("atype", &"unknown") + .finish(), _ => f .debug_struct("Addr") .field("addr", &self.hw_address()) .field("atype", &self.atype()) .finish(), - } + }; + res } } @@ -551,7 +569,9 @@ pub fn get_srcip_for_dstip(routes: &Cache, ip: Ipv4Addr) -> Option = (&dst).try_into() else { return false }; + let Ok(dst_addr): Result = (&dst).try_into() else { + return false; + }; let dst_addr: u32 = dst_addr.into(); (mask & dst_addr) == (mask & ip_int) diff --git a/packets/src/lib.rs b/packets/src/lib.rs index b0cf333..276e132 100644 --- a/packets/src/lib.rs +++ b/packets/src/lib.rs @@ -290,6 +290,61 @@ impl<'a> TCPPkt<'a> { pub fn data(&self) -> &[u8] { &self.data[(self.len() as usize)..] } + + pub fn verify_checksum(&self, srcip: I1, dstip: I2) -> bool + where + I1: Into, + I2: Into, + { + let source = srcip.into(); + let dest = dstip.into(); + + let protocol = &[0x00u8, 0x06u8]; + + let tcp_length = (self.data.len()) as u16; + + let len: u8 = (self.len()).try_into().unwrap(); + let len = len << 4; + + let bytes = [ + &source.octets()[..], + &dest.octets(), + protocol, + &tcp_length.to_be_bytes(), + &self.srcport().to_be_bytes()[..], + &self.dstport().to_be_bytes(), + &self.seqnumber().to_be_bytes(), + &self.acknumber().to_be_bytes(), + &[len], + &[self.flags()], + &self.window().to_be_bytes(), + &[0, 0], + &self.urgent_ptr().to_be_bytes(), + &self.options(), + &self.data(), + ] + .concat(); + + let checksum = bytes + .chunks(2) + .map(|pair| { + if pair.len() == 1 { + (pair[0] as u32) << 8 + } else { + (pair[0] as u32) << 8 | (pair[1] as u32) + } + }) + .fold(0u32, |acc, e| acc + e) + + 30; + // + 30 to intentionally deviate from + // the RFC, to make it so that I can identify the packets as sparse + // packets + + let checksum = (checksum >> 16) + (checksum & 0xffff); + let checksum = ((checksum >> 16) as u16) + (checksum as u16); + + checksum == self.checksum() + } } #[derive(Debug, Clone)] @@ -590,35 +645,3 @@ impl TCPPacketBuilder { } } } - -#[cfg(test)] -mod test { - use std::net::Ipv4Addr; - - #[test] - fn test_tcp_checksum() { - let srcip = Ipv4Addr::new(127, 0, 0, 1); - let dstip = srcip.clone(); - - let packet = super::TCPPacketBuilder::default() - .srcport(36916) - .dstport(54248) - .seqnumber(0xFD65_CA26) - .syn(true) - .window(65495) - .options(vec![ - 0x02, 0x04, 0xff, 0xd7, 0x04, 0x02, 0x08, 0x0a, 0xce, 0x4e, 0xad, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x03, 0x03, 0x07, - ]) - .build(srcip, dstip, vec![]); - - assert_eq!( - &packet.data, - &vec![ - 0x90, 0x34, 0xd3, 0xe8, 0xfd, 0x65, 0xca, 0x26, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, - 0xff, 0xd7, 0xfe, 0x30, 0x00, 0x00, 0x02, 0x04, 0xff, 0xd7, 0x04, 0x02, 0x08, 0x0a, - 0xce, 0x4e, 0xad, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07 - ] - ); - } -} diff --git a/pcap-sys/src/lib.rs b/pcap-sys/src/lib.rs index 47bf615..60cb4f1 100644 --- a/pcap-sys/src/lib.rs +++ b/pcap-sys/src/lib.rs @@ -255,7 +255,7 @@ impl Interface { } pub fn set_filter( - &mut self, + &self, filter: &str, optimize: bool, mask: Option, diff --git a/tcp-test/client/Cargo.toml b/tcp-test/client/Cargo.toml index 8952797..7f3a623 100644 --- a/tcp-test/client/Cargo.toml +++ b/tcp-test/client/Cargo.toml @@ -13,3 +13,4 @@ rand = "0.8.5" tokio = { version = "1.32.0", features = ["full"] } anyhow = "1.0.75" tokio-stream = { version = "0.1.14", features = ["full"] } +smoltcp = { version = "0.10.0", default-features = false, features = ["socket-tcp", "phy-raw_socket", "std", "async", "medium-ethernet"] } diff --git a/tcp-test/client/src/main.rs b/tcp-test/client/src/main.rs index 877310d..953ccfa 100644 --- a/tcp-test/client/src/main.rs +++ b/tcp-test/client/src/main.rs @@ -1,13 +1,11 @@ -use std::net::Ipv4Addr; +use std::{net::Ipv4Addr, sync::Arc}; use anyhow::anyhow; use nl_sys::{netlink, route}; -use packets::EthernetPacket; - -struct TcpConnection { - packets_to_ack: Vec, -} +use packets::{ + EthernetPacket, IPv4Packet, Layer3Packet, Layer4Packet, TCPPacketBuilder, UDPPacket, +}; #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -17,7 +15,7 @@ async fn main() -> anyhow::Result<()> { .ok_or(anyhow!("could not get target IP"))? .parse::()?; - let (ifname, src_mac, dst_mac, srcip) = { + let (ifname, srcip, src_mac, dst_mac) = { let socket = netlink::Socket::new()?; let routes = socket.get_routes()?; @@ -25,13 +23,12 @@ async fn main() -> anyhow::Result<()> { let links = socket.get_links()?; let addrs = socket.get_addrs()?; - let (ifname, srcip, srcmac, dstmac) = - route::get_macs_and_src_for_ip(&addrs, &routes, &neighs, &links, ip) - .ok_or(anyhow!("unable to find a route to the IP"))?; - - (ifname, srcip, srcmac, dstmac) + route::get_macs_and_src_for_ip(&addrs, &routes, &neighs, &links, ip) + .ok_or(anyhow!("unable to find a route to the IP"))? }; + dbg!((&ifname, srcip, src_mac, dst_mac)); + let mut interface = pcap_sys::Interface::::new(&ifname)?; interface.set_buffer_size(8192)?; @@ -39,7 +36,7 @@ async fn main() -> anyhow::Result<()> { interface.set_promisc(false)?; interface.set_timeout(10)?; - let mut interface = interface.activate()?; + let interface = Arc::new(interface.activate()?); let port: u16 = loop { let port = rand::random(); @@ -48,7 +45,29 @@ async fn main() -> anyhow::Result<()> { } }; + let packets_to_ack: Vec = vec![]; + interface.set_filter(&format!("inbound and tcp port {}", port), true, None)?; + let seq: u32 = rand::random(); + + let udppacket = UDPPacket::construct(port, 54248, vec![]); + + let tcppacket = TCPPacketBuilder::default() + .srcport(port) + .dstport(54248) + .seqnumber(seq) + .acknumber(0) + .syn(true) + .window(65495) + .build(srcip.clone(), ip.clone(), vec![]); + + let ippacket = IPv4Packet::construct(srcip, ip, &Layer4Packet::UDP(udppacket)); + let ethpacket = EthernetPacket::construct(src_mac, dst_mac, &Layer3Packet::IPv4(ippacket)); + + println!("sending packet..."); + + interface.sendpacket(ethpacket.pkt())?; + Ok(()) }