use std::os::fd::AsRawFd;
use vm_rs::network::NetworkSwitch;
fn build_frame(dst: [u8; 6], src: [u8; 6], payload: &[u8]) -> Vec<u8> {
let mut frame = Vec::with_capacity(14 + payload.len());
frame.extend_from_slice(&dst);
frame.extend_from_slice(&src);
frame.extend_from_slice(&[0x08, 0x00]); frame.extend_from_slice(payload);
frame
}
fn send_raw(fd: &impl AsRawFd, data: &[u8]) -> isize {
unsafe {
libc::send(
fd.as_raw_fd(),
data.as_ptr() as *const libc::c_void,
data.len(),
0,
)
}
}
fn recv_raw(fd: &impl AsRawFd, buf: &mut [u8]) -> isize {
unsafe {
libc::recv(
fd.as_raw_fd(),
buf.as_mut_ptr() as *mut libc::c_void,
buf.len(),
libc::MSG_DONTWAIT,
)
}
}
const BROADCAST: [u8; 6] = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
const MAC_A: [u8; 6] = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
const MAC_B: [u8; 6] = [0x02, 0x00, 0x00, 0x00, 0x00, 0x02];
#[allow(dead_code)]
const MAC_C: [u8; 6] = [0x02, 0x00, 0x00, 0x00, 0x00, 0x03];
#[test]
fn broadcast_floods_to_all_ports_on_same_network() {
let switch = NetworkSwitch::new();
let fd_a = switch.add_port("net0", "a").expect("add port a");
let fd_b = switch.add_port("net0", "b").expect("add port b");
let fd_c = switch.add_port("net0", "c").expect("add port c");
switch.start().expect("start switch");
let frame = build_frame(BROADCAST, MAC_A, b"hello");
assert_eq!(send_raw(&fd_a, &frame), frame.len() as isize);
std::thread::sleep(std::time::Duration::from_millis(200));
let mut buf = vec![0u8; 1518];
let n_b = recv_raw(&fd_b, &mut buf);
assert_eq!(n_b, frame.len() as isize, "port B should receive broadcast");
let n_c = recv_raw(&fd_c, &mut buf);
assert_eq!(n_c, frame.len() as isize, "port C should receive broadcast");
switch.stop();
}
#[test]
fn unicast_to_learned_mac() {
let switch = NetworkSwitch::new();
let fd_a = switch.add_port("net0", "a").expect("add port a");
let fd_b = switch.add_port("net0", "b").expect("add port b");
let fd_c = switch.add_port("net0", "c").expect("add port c");
switch.start().expect("start switch");
let frame_from_b = build_frame(BROADCAST, MAC_B, b"learn-me");
send_raw(&fd_b, &frame_from_b);
std::thread::sleep(std::time::Duration::from_millis(200));
let mut buf = vec![0u8; 1518];
recv_raw(&fd_a, &mut buf);
recv_raw(&fd_c, &mut buf);
let unicast = build_frame(MAC_B, MAC_A, b"just-for-b");
send_raw(&fd_a, &unicast);
std::thread::sleep(std::time::Duration::from_millis(200));
let n_b = recv_raw(&fd_b, &mut buf);
assert_eq!(n_b, unicast.len() as isize, "B should receive unicast");
let n_c = recv_raw(&fd_c, &mut buf);
assert!(
n_c <= 0,
"C should NOT receive unicast destined for B, got {} bytes",
n_c
);
switch.stop();
}
#[test]
fn different_networks_are_isolated() {
let switch = NetworkSwitch::new();
let fd_frontend = switch
.add_port("frontend", "web")
.expect("add frontend port");
let fd_backend = switch.add_port("backend", "db").expect("add backend port");
switch.start().expect("start switch");
let frame = build_frame(BROADCAST, MAC_A, b"secret-data");
send_raw(&fd_frontend, &frame);
std::thread::sleep(std::time::Duration::from_millis(200));
let mut buf = vec![0u8; 1518];
let n = recv_raw(&fd_backend, &mut buf);
assert!(
n <= 0,
"backend network should NOT receive frames from frontend network"
);
switch.stop();
}
#[test]
fn broadcast_does_not_echo_back_to_sender() {
let switch = NetworkSwitch::new();
let fd_a = switch.add_port("net0", "a").expect("add port a");
let _fd_b = switch.add_port("net0", "b").expect("add port b");
switch.start().expect("start switch");
let frame = build_frame(BROADCAST, MAC_A, b"data");
send_raw(&fd_a, &frame);
std::thread::sleep(std::time::Duration::from_millis(200));
let mut buf = vec![0u8; 1518];
let n = recv_raw(&fd_a, &mut buf);
assert!(n <= 0, "sender should NOT receive its own broadcast back");
switch.stop();
}
#[test]
fn runt_frames_are_dropped() {
let switch = NetworkSwitch::new();
let fd_a = switch.add_port("net0", "a").expect("add port a");
let fd_b = switch.add_port("net0", "b").expect("add port b");
switch.start().expect("start switch");
let runt = vec![0xff; 10];
send_raw(&fd_a, &runt);
std::thread::sleep(std::time::Duration::from_millis(200));
let mut buf = vec![0u8; 1518];
let n = recv_raw(&fd_b, &mut buf);
assert!(n <= 0, "runt frame should be dropped, not forwarded");
switch.stop();
}
#[test]
fn handles_burst_of_frames() {
let switch = NetworkSwitch::new();
let fd_a = switch.add_port("net0", "a").expect("add port a");
let fd_b = switch.add_port("net0", "b").expect("add port b");
switch.start().expect("start switch");
let learn = build_frame(BROADCAST, MAC_B, b"learn");
send_raw(&fd_b, &learn);
std::thread::sleep(std::time::Duration::from_millis(100));
let mut drain = vec![0u8; 1518];
recv_raw(&fd_a, &mut drain);
let frame = build_frame(MAC_B, MAC_A, &[0xAA; 100]);
let count = 10;
for _ in 0..count {
send_raw(&fd_a, &frame);
std::thread::sleep(std::time::Duration::from_millis(60));
}
std::thread::sleep(std::time::Duration::from_millis(200));
let mut received = 0;
let mut buf = vec![0u8; 1518];
loop {
let n = recv_raw(&fd_b, &mut buf);
if n <= 0 {
break;
}
received += 1;
}
assert!(
received >= count - 1,
"expected nearly all frames delivered with paced sending, got {}/{} frames",
received,
count
);
switch.stop();
}
#[test]
fn multiple_networks_operate_independently() {
let switch = NetworkSwitch::new();
let fd_a1 = switch.add_port("net-a", "a1").expect("add net-a port a1");
let fd_a2 = switch.add_port("net-a", "a2").expect("add net-a port a2");
let fd_b1 = switch.add_port("net-b", "b1").expect("add net-b port b1");
let fd_b2 = switch.add_port("net-b", "b2").expect("add net-b port b2");
switch.start().expect("start switch");
let frame_a = build_frame(BROADCAST, MAC_A, b"net-a-data");
let frame_b = build_frame(BROADCAST, MAC_B, b"net-b-data");
send_raw(&fd_a1, &frame_a);
send_raw(&fd_b1, &frame_b);
std::thread::sleep(std::time::Duration::from_millis(200));
let mut buf = vec![0u8; 1518];
let n = recv_raw(&fd_a2, &mut buf);
assert_eq!(
n,
frame_a.len() as isize,
"a2 should receive net-a broadcast"
);
let n = recv_raw(&fd_b2, &mut buf);
assert_eq!(
n,
frame_b.len() as isize,
"b2 should receive net-b broadcast"
);
let n = recv_raw(&fd_a2, &mut buf);
assert!(n <= 0, "net-a should not receive net-b frames");
switch.stop();
}
#[test]
fn switch_restart() {
let switch = NetworkSwitch::new();
let fd_a = switch.add_port("net0", "a").expect("add port a");
let fd_b = switch.add_port("net0", "b").expect("add port b");
switch.start().expect("start switch");
let frame = build_frame(BROADCAST, MAC_A, b"first");
send_raw(&fd_a, &frame);
std::thread::sleep(std::time::Duration::from_millis(200));
let mut buf = vec![0u8; 1518];
let n = recv_raw(&fd_b, &mut buf);
assert_eq!(n, frame.len() as isize);
switch.stop();
std::thread::sleep(std::time::Duration::from_millis(100));
switch.start().expect("restart switch");
let frame2 = build_frame(BROADCAST, MAC_A, b"second");
send_raw(&fd_a, &frame2);
std::thread::sleep(std::time::Duration::from_millis(200));
let n = recv_raw(&fd_b, &mut buf);
assert_eq!(n, frame2.len() as isize, "switch should work after restart");
switch.stop();
}