feat: trying out smoltcp

This commit is contained in:
Andrew Rioux 2023-09-19 19:19:29 -04:00
parent 35bcf5352b
commit f5b31954d4
Signed by: andrew.rioux
GPG Key ID: 9B8BAC47C17ABB94
5 changed files with 138 additions and 39 deletions

6
Cargo.lock generated
View File

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

View File

@ -146,7 +146,7 @@ pub fn get_macs_and_src_for_ip(
neighs: &Cache<Neigh>,
links: &Cache<Link>,
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::<Vec<_>>();
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,
))
}

View File

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

View File

@ -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::<Ipv4Addr>()?;
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::<pcap_sys::DevDisabled>::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::<u16>();
if port > 40_000 {
break port;
}
};
let packets_to_ack: Vec<EthernetPacket> = vec![];
let socket = sockets.get_mut::<tcp::Socket>(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 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(())
let socket = sockets.get_mut::<tcp::Socket>(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();
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;
}
// phy_wait(fd, iface.poll_delay(timestamp, &sockets))?;
drop(afd.ready(interest).await?);
}
}

View File

@ -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)