feat: added test tcp client

This commit is contained in:
Andrew Rioux 2023-09-19 10:24:51 -04:00
parent e5f6c2aa7e
commit 35bcf5352b
Signed by: andrew.rioux
GPG Key ID: 9B8BAC47C17ABB94
6 changed files with 251 additions and 67 deletions

125
Cargo.lock generated
View File

@ -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",
]

View File

@ -170,7 +170,9 @@ pub fn get_macs_and_src_for_ip(
0
};
let Ok(dst_addr): Result<Ipv4Addr, _> = (&dst).try_into() else { return false };
let Ok(dst_addr): Result<Ipv4Addr, _> = (&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<c_int> {
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<Route>, ip: Ipv4Addr) -> Option<Ipv4Ad
0
};
let Ok(dst_addr): Result<Ipv4Addr, _> = (&dst).try_into() else { return false };
let Ok(dst_addr): Result<Ipv4Addr, _> = (&dst).try_into() else {
return false;
};
let dst_addr: u32 = dst_addr.into();
(mask & dst_addr) == (mask & ip_int)

View File

@ -290,6 +290,61 @@ impl<'a> TCPPkt<'a> {
pub fn data(&self) -> &[u8] {
&self.data[(self.len() as usize)..]
}
pub fn verify_checksum<I1, I2>(&self, srcip: I1, dstip: I2) -> bool
where
I1: Into<Ipv4Addr>,
I2: Into<Ipv4Addr>,
{
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
]
);
}
}

View File

@ -255,7 +255,7 @@ impl<T: Activated> Interface<T> {
}
pub fn set_filter(
&mut self,
&self,
filter: &str,
optimize: bool,
mask: Option<u32>,

View File

@ -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"] }

View File

@ -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<EthernetPacket>,
}
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::<Ipv4Addr>()?;
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)
.ok_or(anyhow!("unable to find a route to the IP"))?
};
dbg!((&ifname, srcip, src_mac, dst_mac));
let mut interface = pcap_sys::Interface::<pcap_sys::DevDisabled>::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<EthernetPacket> = 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(())
}