From 596e73a92e3bed1058c3a75deceea0bf8ede11da Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Wed, 8 Jul 2026 21:20:32 +0300 Subject: [PATCH] bridge: 5 unit tests for FDB learn/age/lookup Bridge MAC learning tests: - learn_stores_unicast_mapping: stored MAC resolves on correct port - learn_ignores_multicast: broadcast/multicast never enters FDB - learn_replaces_existing_entry_on_new_port: MAC moves update port - age_entries_removes_expired_macs: 300s timeout respected - lookup_returns_none_for_unknown_mac: unknown MAC returns None Total tests across netstack: 20 (5 table + 4 conntrack + 6 nat + 5 bridge) All passing. --- netstack/src/link/bridge.rs | 60 ++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/netstack/src/link/bridge.rs b/netstack/src/link/bridge.rs index 7127970596..015fb2d2ce 100644 --- a/netstack/src/link/bridge.rs +++ b/netstack/src/link/bridge.rs @@ -259,4 +259,62 @@ impl LinkDevice for BridgeDevice { } out } -} \ No newline at end of file +} +#[cfg(test)] +mod tests { + use super::*; + use smoltcp::wire::EthernetAddress; + + fn mac(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> EthernetAddress { + EthernetAddress([a, b, c, d, e, f]) + } + + #[test] + fn learn_stores_unicast_mapping() { + let b = BridgeDevice::new("br0"); + let m = mac(0x00, 0x11, 0x22, 0x33, 0x44, 0x55); + b.learn(m, 2, Instant::from_secs(0)); + assert_eq!(b.lookup(m), Some(2), + "Learned MAC must be found on port 2"); + } + + #[test] + fn learn_ignores_multicast() { + let b = BridgeDevice::new("br0"); + let multicast = EthernetAddress::BROADCAST; + b.learn(multicast, 1, Instant::from_secs(0)); + assert_eq!(b.lookup(multicast), None, + "Multicast MACs must not be learned"); + } + + #[test] + fn learn_replaces_existing_entry_on_new_port() { + let b = BridgeDevice::new("br0"); + let m = mac(0x00, 0x11, 0x22, 0x33, 0x44, 0x55); + b.learn(m, 1, Instant::from_secs(0)); + assert_eq!(b.lookup(m), Some(1)); + b.learn(m, 2, Instant::from_secs(10)); + assert_eq!(b.lookup(m), Some(2), + "MAC move from port 1 to port 2 must update FDB"); + } + + #[test] + fn age_entries_removes_expired_macs() { + let b = BridgeDevice::new("br0"); + let m1 = mac(0x00, 0x11, 0x22, 0x33, 0x44, 0x55); + let m2 = mac(0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE); + b.learn(m1, 1, Instant::from_secs(0)); + b.learn(m2, 2, Instant::from_secs(MAC_AGE_TIMEOUT.secs() as i64)); // Age boundary + // Age just past the 300s timeout + b.age_entries(Instant::from_secs(MAC_AGE_TIMEOUT.secs() as i64 + 1)); + assert_eq!(b.lookup(m1), None, "m1 (learned at 0) must be aged out"); + assert_eq!(b.lookup(m2), Some(2), + "m2 (learned at 300s) must still be valid"); + } + + #[test] + fn lookup_returns_none_for_unknown_mac() { + let b = BridgeDevice::new("br0"); + assert_eq!(b.lookup(mac(0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01)), None); + } +}