use std::env;
use std::error::Error;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::str::FromStr;
use std::time::Duration;
use netmap_rs::prelude::*;
const ETH_HDR_LEN: usize = 14;
const IPV4_HDR_LEN: usize = 20;
const UDP_HDR_LEN: usize = 8;
fn ip_checksum(data: &[u8]) -> u16 {
let mut sum = 0u32;
let mut i = 0;
while i < data.len() {
let word = if i + 1 < data.len() {
(data[i] as u16) << 8 | (data[i+1] as u16)
} else {
(data[i] as u16) << 8
};
sum += word as u32;
i += 2;
}
while sum >> 16 != 0 {
sum = (sum & 0xFFFF) + (sum >> 16);
}
!sum as u16
}
fn build_udp_packet(
src_mac: [u8; 6],
dst_mac: [u8; 6],
src_ip: Ipv4Addr,
dst_ip: Ipv4Addr,
src_port: u16,
dst_port: u16,
payload: &[u8],
) -> Vec<u8> {
let mut packet = Vec::with_capacity(ETH_HDR_LEN + IPV4_HDR_LEN + UDP_HDR_LEN + payload.len());
packet.extend_from_slice(&dst_mac); packet.extend_from_slice(&src_mac); packet.extend_from_slice(&[0x08, 0x00]);
let ipv4_start = packet.len();
packet.push(0x45); packet.push(0x00); let total_len = (IPV4_HDR_LEN + UDP_HDR_LEN + payload.len()) as u16;
packet.extend_from_slice(&total_len.to_be_bytes()); packet.extend_from_slice(&[0x00, 0x01]); packet.extend_from_slice(&[0x00, 0x00]); packet.push(64); packet.push(17); packet.extend_from_slice(&[0x00, 0x00]); packet.extend_from_slice(&src_ip.octets()); packet.extend_from_slice(&dst_ip.octets());
let ipv4_header_slice = &packet[ipv4_start..ipv4_start + IPV4_HDR_LEN];
let checksum = ip_checksum(ipv4_header_slice);
packet[ipv4_start + 10..ipv4_start + 12].copy_from_slice(&checksum.to_be_bytes());
packet.extend_from_slice(&src_port.to_be_bytes()); packet.extend_from_slice(&dst_port.to_be_bytes()); let udp_len = (UDP_HDR_LEN + payload.len()) as u16;
packet.extend_from_slice(&udp_len.to_be_bytes()); packet.extend_from_slice(&[0x00, 0x00]);
packet.extend_from_slice(payload);
packet
}
fn main() -> Result<(), Box<dyn Error>> {
let args: Vec<String> = env::args().collect();
if args.len() < 5 {
eprintln!(
"Usage: {} <interface_name_with_caret> <src_ip> <dst_ip> <dst_port>",
args[0]
);
eprintln!("Example: {} netmap:eth0^ 192.168.1.100 192.168.1.1 5000", args[0]);
return Err("Invalid arguments".into());
}
let if_name = &args[1];
if !if_name.contains('^') {
return Err("Invalid arguments: Interface name must include '^' suffix for host stack.".into());
}
let src_ip = Ipv4Addr::from_str(&args[2])?;
let dst_ip = Ipv4Addr::from_str(&args[3])?;
let dst_port = args[4].parse::<u16>()?;
let src_port: u16 = 12345;
println!(
"Attempting to open host stack rings for interface: {}",
if_name
);
let nm_desc = NetmapBuilder::new(if_name)
.num_tx_rings(1) .num_rx_rings(0) .build()?;
println!(
"Successfully opened interface {}. Number of host TX rings available: {}",
if_name,
nm_desc.num_tx_rings()
);
if nm_desc.num_tx_rings() == 0 {
eprintln!("No host TX rings available for interface {}. Exiting.", if_name);
return Ok(());
}
let mut tx_ring = nm_desc.tx_ring(0)?;
println!("Preparing to send packet to host TX ring 0...");
let src_mac: [u8; 6] = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x01];
let dst_mac: [u8; 6] = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x02];
let payload = b"Hello from netmap-rs to host stack!";
let packet = build_udp_packet(src_mac, dst_mac, src_ip, dst_ip, src_port, dst_port, payload);
println!("Sending packet ({} bytes) to host stack: {:02X?}", packet.len(), &packet[..std::cmp::min(packet.len(), 48)]);
match tx_ring.send(&packet) {
Ok(_) => {
tx_ring.sync(); println!("Packet sent successfully to host stack via Netmap.");
println!("Try listening with: sudo tcpdump -i <base_if_name> -n udp port {} and host {}", dst_port, dst_ip);
}
Err(e) => {
eprintln!("Failed to send packet: {:?}", e);
return Err(Box::new(e));
}
}
for i in 0..3 {
let dynamic_payload = format!("Hello #{} from netmap-rs to host stack!", i);
let packet = build_udp_packet(src_mac, dst_mac, src_ip, dst_ip, src_port, dst_port, dynamic_payload.as_bytes());
if tx_ring.send(&packet).is_ok() {
println!("Sent dynamic packet #{}", i);
} else {
eprintln!("Failed to send dynamic packet #{}", i);
break;
}
std::thread::sleep(Duration::from_millis(10));
}
tx_ring.sync();
println!("Finished sending dynamic packets.");
Ok(())
}