diff --git a/Cargo.lock b/Cargo.lock index b326157adc..96a656e90d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ - "memchr 2.8.2", + "memchr 2.8.3", ] [[package]] @@ -495,11 +495,11 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" dependencies = [ - "crossbeam-utils 0.8.21", + "crossbeam-utils 0.8.22", ] [[package]] @@ -515,9 +515,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "daemon" @@ -908,7 +908,7 @@ dependencies = [ "futures-macro", "futures-sink", "futures-task", - "memchr 2.8.2", + "memchr 2.8.3", "pin-project-lite", "slab", ] @@ -1445,9 +1445,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "nb" @@ -2024,7 +2024,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", - "memchr 2.8.2", + "memchr 2.8.3", "regex-automata", "regex-syntax", ] @@ -2036,7 +2036,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", - "memchr 2.8.2", + "memchr 2.8.3", "regex-syntax", ] @@ -2128,9 +2128,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "sb16d" @@ -2243,7 +2243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", - "memchr 2.8.2", + "memchr 2.8.3", "serde", "serde_core", "zmij", @@ -2532,11 +2532,7 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "usb-core" -<<<<<<< HEAD version = "0.2.5" -======= -version = "0.3.0" ->>>>>>> 1c80937e (networking: comprehensive TCP/IP stack implementation (Phases 1-6)) [[package]] name = "usbctl" @@ -2984,18 +2980,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71" dependencies = [ "proc-macro2", "quote", diff --git a/netstack/src/link/bridge.rs b/netstack/src/link/bridge.rs index dd3de95b58..95fa2d323c 100644 --- a/netstack/src/link/bridge.rs +++ b/netstack/src/link/bridge.rs @@ -21,6 +21,7 @@ use smoltcp::wire::{ EthernetAddress, EthernetFrame, EthernetProtocol, EthernetRepr, IpAddress, IpCidr, }; +use super::stp::{self, BPDU_MAC, PortState, StpState}; use super::LinkDevice; const MAC_AGE_TIMEOUT: Duration = Duration::from_secs(300); @@ -34,6 +35,7 @@ pub struct BridgeDevice { name: Rc, ports: RefCell>>, mac_table: RefCell>, + stp: RefCell>, recv_buffer: Vec, ip_address: Option, } @@ -44,13 +46,26 @@ impl BridgeDevice { name: name.into(), ports: RefCell::new(Vec::new()), mac_table: RefCell::new(BTreeMap::new()), + stp: RefCell::new(None), recv_buffer: Vec::with_capacity(1500), ip_address: None, } } pub fn add_port(&self, dev: T) { + let mac = dev.mac_address(); self.ports.borrow_mut().push(Box::new(dev)); + if let Some(stp) = self.stp.borrow_mut().as_mut() { + stp.port_states.push(PortState::Forwarding); + } + let _ = mac; + } + + /// Enable STP with the given bridge priority and MAC. + /// Must be called after all ports are added. + pub fn enable_stp(&self, priority: u16, bridge_mac: EthernetAddress) { + let port_count = self.ports.borrow().len(); + *self.stp.borrow_mut() = Some(StpState::new(priority, bridge_mac, port_count)); } fn learn(&self, mac: EthernetAddress, port: usize, now: Instant) { @@ -81,6 +96,9 @@ impl BridgeDevice { if Some(idx) == except_port { continue; } + if self.stp.borrow().as_ref().is_some_and(|s| s.is_blocked(idx)) { + continue; + } port.send(IpAddress::Ipv4(smoltcp::wire::Ipv4Address::UNSPECIFIED), packet, now); } } @@ -110,6 +128,19 @@ impl LinkDevice for BridgeDevice { fn recv(&mut self, now: Instant) -> Option<&[u8]> { self.age_entries(now); + // STP hello timer — send periodic BPDUs if we're the root bridge + if self.stp.borrow_mut().as_mut().is_some_and(|s| s.send_hello(now)) { + let bpdu = { + let stp = self.stp.borrow(); + stp.as_ref().map(|s| s.build_bpdu()) + }; + if let Some(bpdu) = bpdu { + for port in self.ports.borrow_mut().iter_mut() { + port.send(IpAddress::Ipv4(smoltcp::wire::Ipv4Address::UNSPECIFIED), &bpdu, now); + } + } + } + let mut received: Option<(usize, Vec, EthernetAddress, EthernetProtocol)> = None; { let mut ports = self.ports.borrow_mut(); @@ -127,6 +158,18 @@ impl LinkDevice for BridgeDevice { } if let Some((port_idx, packet, dst_mac, ethertype)) = received { + // BPDU: process via STP, don't forward + if dst_mac == BPDU_MAC { + let response = self.stp.borrow_mut().as_mut() + .and_then(|s| s.process_bpdu(port_idx, &packet, now)); + if let Some(rsp) = response { + if let Some(port) = self.ports.borrow_mut().get_mut(port_idx) { + port.send(IpAddress::Ipv4(smoltcp::wire::Ipv4Address::UNSPECIFIED), &rsp, now); + } + } + return None; + } + if ethertype == EthernetProtocol::Arp || ethertype == EthernetProtocol::Ipv4 || ethertype == EthernetProtocol::Ipv6 diff --git a/netstack/src/link/mod.rs b/netstack/src/link/mod.rs index c02a28ecd4..11e9f1255f 100644 --- a/netstack/src/link/mod.rs +++ b/netstack/src/link/mod.rs @@ -5,6 +5,7 @@ pub mod gre; pub mod ipip; pub mod loopback; pub mod qdisc; +pub mod stp; pub mod tun; pub mod vlan; pub mod vxlan; diff --git a/netstack/src/link/stp.rs b/netstack/src/link/stp.rs index bc6314eef6..e2142e7c1b 100644 --- a/netstack/src/link/stp.rs +++ b/netstack/src/link/stp.rs @@ -107,7 +107,7 @@ impl StpState { 4 } - fn build_bpdu(&self) -> Vec { + pub fn build_bpdu(&self) -> Vec { let mut buf = vec![0u8; 35]; buf[0..2].copy_from_slice(&[0x00, 0x00]); buf[2] = 0x00;