diff --git a/Cargo.lock b/Cargo.lock index 4b23724..66cb53b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -697,9 +697,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "linux-raw-sys" @@ -1257,6 +1257,7 @@ dependencies = [ "defmt", "heapless", "libc", + "log", "managed", ] @@ -1417,6 +1418,7 @@ name = "tcp-test" version = "0.1.0" dependencies = [ "anyhow", + "libc", "nl-sys", "packets", "pcap-sys", diff --git a/nl-sys/src/route.rs b/nl-sys/src/route.rs index 3824325..9fbd512 100644 --- a/nl-sys/src/route.rs +++ b/nl-sys/src/route.rs @@ -146,7 +146,7 @@ pub fn get_macs_and_src_for_ip( neighs: &Cache, links: &Cache, addr: Ipv4Addr, -) -> Option<(String, Ipv4Addr, [u8; 6], [u8; 6])> { +) -> Option<(String, i32, Ipv4Addr, [u8; 6], [u8; 6], u8)> { let mut sorted_routes = routes.iter().collect::>(); sorted_routes.sort_by(|r1, r2| { @@ -182,6 +182,7 @@ pub fn get_macs_and_src_for_ip( #[cfg(debug_assertions)] { + println!("Link index: {link_ind}\n"); for link in links.iter() { println!( "Link {}: {:?} ({})", @@ -202,7 +203,6 @@ pub fn get_macs_and_src_for_ip( .iter() .filter(|neigh| neigh.ifindex() == link.ifindex()) { - println!("\t\ttest {:?}, {:?}", neigh.ifindex(), neigh.dst()); println!("\t\t{:?}, {:?}", neigh.dst(), neigh.lladdr()); } } @@ -221,9 +221,11 @@ pub fn get_macs_and_src_for_ip( Some(( link.name(), + link_ind, (&srcip.local()?).try_into().ok()?, link.addr().hw_address().try_into().ok()?, neigh, + route.dst().unwrap().cidrlen() as u8, )) } diff --git a/tcp-test/client/Cargo.toml b/tcp-test/client/Cargo.toml index 7f3a623..f28ac47 100644 --- a/tcp-test/client/Cargo.toml +++ b/tcp-test/client/Cargo.toml @@ -13,4 +13,5 @@ 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"] } +smoltcp = { version = "0.10.0", features = ["socket-tcp", "phy-raw_socket", "std", "async", "medium-ethernet", "proto-ipv4", "reassembly-buffer-size-65536", "fragmentation-buffer-size-65536", "proto-ipv4-fragmentation"] } +libc = "0.2.148" diff --git a/tcp-test/client/src/main.rs b/tcp-test/client/src/main.rs index 953ccfa..0aa1e8d 100644 --- a/tcp-test/client/src/main.rs +++ b/tcp-test/client/src/main.rs @@ -1,11 +1,17 @@ -use std::{net::Ipv4Addr, sync::Arc}; +use std::{net::Ipv4Addr, os::fd::AsRawFd, sync::Arc}; use anyhow::anyhow; use nl_sys::{netlink, route}; -use packets::{ - EthernetPacket, IPv4Packet, Layer3Packet, Layer4Packet, TCPPacketBuilder, UDPPacket, + +use smoltcp::{ + iface::{Config, Interface, Route, SocketSet}, + phy::{wait as phy_wait, Medium, RawSocket}, + socket::tcp, + time::Instant, + wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}, }; +use tokio::io::{unix::AsyncFd, Interest}; #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -15,7 +21,7 @@ async fn main() -> anyhow::Result<()> { .ok_or(anyhow!("could not get target IP"))? .parse::()?; - let (ifname, srcip, src_mac, dst_mac) = { + let (routes, (ifname, ifindex, srcip, src_mac, _, src_snmask)) = { let socket = netlink::Socket::new()?; let routes = socket.get_routes()?; @@ -23,51 +29,136 @@ async fn main() -> anyhow::Result<()> { let links = socket.get_links()?; let addrs = socket.get_addrs()?; - route::get_macs_and_src_for_ip(&addrs, &routes, &neighs, &links, ip) - .ok_or(anyhow!("unable to find a route to the IP"))? + let res = route::get_macs_and_src_for_ip(&addrs, &routes, &neighs, &links, ip) + .ok_or(anyhow!("unable to find a route to the IP"))?; + + (routes, res) }; - dbg!((&ifname, srcip, src_mac, dst_mac)); + // dbg!((&ifname, srcip, src_mac, dst_mac)); - let mut interface = pcap_sys::Interface::::new(&ifname)?; + let mut device = RawSocket::new(&ifname, Medium::Ethernet)?; - interface.set_buffer_size(8192)?; - interface.set_non_blocking(true)?; - interface.set_promisc(false)?; - interface.set_timeout(10)?; + let mut config = Config::new(EthernetAddress(src_mac).into()); + config.random_seed = rand::random(); - let interface = Arc::new(interface.activate()?); + let mut iface = Interface::new(config, &mut device, Instant::now()); + iface.update_ip_addrs(|ip_addrs| { + let o = srcip.octets(); + ip_addrs + .push(IpCidr::new( + IpAddress::v4(o[0], o[1], o[2], o[3]), + src_snmask, + )) + .unwrap(); + }); - let port: u16 = loop { - let port = rand::random(); - if port > 30_000 { + for route in routes.iter() { + let Some(dst) = route.dst() else { + continue; + }; + + let Some(hop) = route.hop_iter().next() else { + continue; + }; + if hop.ifindex() != ifindex { + continue; + } + let Some(laddr) = hop.gateway() else { + continue; + }; + if laddr.atype() != Some(libc::AF_INET) { + continue; + } + let Some(raddr) = route.dst() else { + continue; + }; + if raddr.atype() != Some(libc::AF_INET) { + continue; + } + + if dst.cidrlen() == 0 { + iface + .routes_mut() + .add_default_ipv4_route(Ipv4Address::from_bytes(&laddr.hw_address()))?; + } else { + iface.routes_mut().update(|routes| { + let lip = laddr.hw_address(); + let rip = raddr.hw_address(); + + _ = routes.push(Route { + cidr: IpCidr::new( + IpAddress::v4(rip[0], rip[1], rip[2], rip[3]), + dst.cidrlen() as u8, + ), + via_router: IpAddress::v4(lip[0], lip[1], lip[2], lip[3]), + expires_at: None, + preferred_until: None, + }) + }); + } + } + + let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; 1500]); + let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; 1500]); + let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer); + let mut sockets = SocketSet::new(vec![]); + let tcp_handle = sockets.add(tcp_socket); + + let port = loop { + let port = rand::random::(); + + if port > 40_000 { break port; } }; - let packets_to_ack: Vec = vec![]; + let socket = sockets.get_mut::(tcp_handle); + socket.connect(iface.context(), (ip, 54248), port)?; - interface.set_filter(&format!("inbound and tcp port {}", port), true, None)?; + let fd = device.as_raw_fd(); + let interest = Interest::WRITABLE + .add(Interest::READABLE) + .add(Interest::ERROR) + .add(Interest::PRIORITY); + let afd = AsyncFd::with_interest(fd, interest)?; - let seq: u32 = rand::random(); + let mut tcp_active = false; + let mut tcp_data_sent = false; + let mut tcp_data_recvd = false; - let udppacket = UDPPacket::construct(port, 54248, vec![]); + loop { + let timestamp = Instant::now(); + iface.poll(timestamp, &mut device, &mut sockets); - let tcppacket = TCPPacketBuilder::default() - .srcport(port) - .dstport(54248) - .seqnumber(seq) - .acknumber(0) - .syn(true) - .window(65495) - .build(srcip.clone(), ip.clone(), vec![]); + let socket = sockets.get_mut::(tcp_handle); + if socket.is_active() && !tcp_active { + println!("connected!"); + } else if !socket.is_active() && tcp_active { + println!("disconnected"); + break Ok(()); + } + tcp_active = socket.is_active(); - let ippacket = IPv4Packet::construct(srcip, ip, &Layer4Packet::UDP(udppacket)); - let ethpacket = EthernetPacket::construct(src_mac, dst_mac, &Layer3Packet::IPv4(ippacket)); + if !tcp_data_sent && socket.can_send() { + socket.send_slice(b"ping")?; + tcp_data_sent = true; + } + if !tcp_data_recvd && socket.may_recv() { + socket.recv(|data| { + if !data.is_empty() { + match std::str::from_utf8(&data) { + Ok(s) => println!("Data received: {}", s), + Err(_) => println!("Data received: {:?}", data), + } + } + (data.len(), data) + })?; + tcp_data_recvd = true; + } - println!("sending packet..."); + // phy_wait(fd, iface.poll_delay(timestamp, &sockets))?; - interface.sendpacket(ethpacket.pkt())?; - - Ok(()) + drop(afd.ready(interest).await?); + } } diff --git a/tcp-test/server.py b/tcp-test/server.py index ce1ef96..08a56c5 100644 --- a/tcp-test/server.py +++ b/tcp-test/server.py @@ -4,6 +4,9 @@ import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +server.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1500) +server.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1500) + server.bind(("0.0.0.0", 54248)) server.listen(32)