diff --git a/src/node/handlers/forwarding.rs b/src/node/handlers/forwarding.rs index 0cc5440..dd84087 100644 --- a/src/node/handlers/forwarding.rs +++ b/src/node/handlers/forwarding.rs @@ -64,7 +64,11 @@ impl Node { && let Some(tid) = peer.transport_id() && let Some(transport) = self.transports.get(&tid) { - datagram.path_mtu = datagram.path_mtu.min(transport.mtu()); + if let Some(addr) = peer.current_addr() { + datagram.path_mtu = datagram.path_mtu.min(transport.link_mtu(addr)); + } else { + datagram.path_mtu = datagram.path_mtu.min(transport.mtu()); + } } // Forward: re-encode (includes 0x00 type byte) and send diff --git a/src/node/handlers/session.rs b/src/node/handlers/session.rs index a5803bc..c872714 100644 --- a/src/node/handlers/session.rs +++ b/src/node/handlers/session.rs @@ -1062,7 +1062,11 @@ impl Node { && let Some(tid) = peer.transport_id() && let Some(transport) = self.transports.get(&tid) { - datagram.path_mtu = datagram.path_mtu.min(transport.mtu()); + if let Some(addr) = peer.current_addr() { + datagram.path_mtu = datagram.path_mtu.min(transport.link_mtu(addr)); + } else { + datagram.path_mtu = datagram.path_mtu.min(transport.mtu()); + } } // Source-side: seed our PathMtuState.current_mtu from the outbound diff --git a/src/transport/mod.rs b/src/transport/mod.rs index 7c7d5b0..3b667d3 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -740,6 +740,16 @@ pub trait Transport { /// Get the MTU for this transport. fn mtu(&self) -> u16; + /// Get the MTU for a specific link. + /// + /// Returns the MTU negotiated for the given transport address, or + /// falls back to the transport-wide default if the address is unknown + /// or the transport doesn't support per-link MTU negotiation. + fn link_mtu(&self, addr: &TransportAddr) -> u16 { + let _ = addr; + self.mtu() + } + /// Start the transport. fn start(&mut self) -> Result<(), TransportError>; @@ -824,6 +834,16 @@ impl TransportHandle { } } + /// Get the MTU for a specific link address. + /// + /// Falls back to transport-wide MTU if the transport doesn't + /// support per-link MTU or the address is unknown. + pub fn link_mtu(&self, addr: &TransportAddr) -> u16 { + match self { + TransportHandle::Udp(t) => t.link_mtu(addr), + } + } + /// Get the local bound address (only valid after start). pub fn local_addr(&self) -> Option { match self { @@ -1116,4 +1136,156 @@ mod tests { let received = rx.recv().await.unwrap(); assert_eq!(received.data, vec![1, 2, 3]); } + + // ======================================================================== + // link_mtu tests + // ======================================================================== + + /// Minimal mock transport for testing the default link_mtu() behavior. + struct MockTransport { + id: TransportId, + mtu_value: u16, + } + + impl MockTransport { + fn new(mtu: u16) -> Self { + Self { + id: TransportId::new(99), + mtu_value: mtu, + } + } + } + + impl Transport for MockTransport { + fn transport_id(&self) -> TransportId { + self.id + } + fn transport_type(&self) -> &TransportType { + &TransportType::UDP + } + fn state(&self) -> TransportState { + TransportState::Up + } + fn mtu(&self) -> u16 { + self.mtu_value + } + fn start(&mut self) -> Result<(), TransportError> { + Ok(()) + } + fn stop(&mut self) -> Result<(), TransportError> { + Ok(()) + } + fn send(&self, _addr: &TransportAddr, _data: &[u8]) -> Result<(), TransportError> { + Ok(()) + } + fn discover(&self) -> Result, TransportError> { + Ok(vec![]) + } + } + + /// Mock transport that overrides link_mtu() to return per-link values. + struct PerLinkMtuTransport { + id: TransportId, + default_mtu: u16, + /// Address-specific MTU overrides. + overrides: Vec<(TransportAddr, u16)>, + } + + impl PerLinkMtuTransport { + fn new(default_mtu: u16, overrides: Vec<(TransportAddr, u16)>) -> Self { + Self { + id: TransportId::new(100), + default_mtu, + overrides, + } + } + } + + impl Transport for PerLinkMtuTransport { + fn transport_id(&self) -> TransportId { + self.id + } + fn transport_type(&self) -> &TransportType { + &TransportType::UDP + } + fn state(&self) -> TransportState { + TransportState::Up + } + fn mtu(&self) -> u16 { + self.default_mtu + } + fn link_mtu(&self, addr: &TransportAddr) -> u16 { + for (a, mtu) in &self.overrides { + if a == addr { + return *mtu; + } + } + self.mtu() + } + fn start(&mut self) -> Result<(), TransportError> { + Ok(()) + } + fn stop(&mut self) -> Result<(), TransportError> { + Ok(()) + } + fn send(&self, _addr: &TransportAddr, _data: &[u8]) -> Result<(), TransportError> { + Ok(()) + } + fn discover(&self) -> Result, TransportError> { + Ok(vec![]) + } + } + + #[test] + fn test_link_mtu_default_falls_back_to_mtu() { + let transport = MockTransport::new(1280); + let addr = TransportAddr::from_string("192.168.1.1:4000"); + + // Default link_mtu() should return the transport-wide mtu() + assert_eq!(transport.link_mtu(&addr), 1280); + assert_eq!(transport.link_mtu(&addr), transport.mtu()); + + // Any address should return the same value + let other_addr = TransportAddr::from_string("10.0.0.1:5000"); + assert_eq!(transport.link_mtu(&other_addr), 1280); + } + + #[test] + fn test_link_mtu_per_link_override() { + let addr_a = TransportAddr::from_string("192.168.1.1:4000"); + let addr_b = TransportAddr::from_string("10.0.0.1:5000"); + let addr_unknown = TransportAddr::from_string("172.16.0.1:6000"); + + let transport = PerLinkMtuTransport::new( + 1280, + vec![(addr_a.clone(), 512), (addr_b.clone(), 247)], + ); + + // Known addresses return their per-link MTU + assert_eq!(transport.link_mtu(&addr_a), 512); + assert_eq!(transport.link_mtu(&addr_b), 247); + + // Unknown address falls back to transport-wide default + assert_eq!(transport.link_mtu(&addr_unknown), 1280); + assert_eq!(transport.mtu(), 1280); + } + + #[test] + fn test_transport_handle_link_mtu_delegation() { + use crate::config::UdpConfig; + use crate::transport::udp::UdpTransport; + + let config = UdpConfig::default(); + let expected_mtu = config.mtu(); + let (tx, _rx) = packet_channel(1); + let transport = UdpTransport::new(TransportId::new(1), None, config, tx); + let handle = TransportHandle::Udp(transport); + + let addr = TransportAddr::from_string("192.168.1.1:4000"); + + // TransportHandle::link_mtu() should delegate and return the same + // as TransportHandle::mtu() for UDP (no per-link overrides) + assert_eq!(handle.link_mtu(&addr), expected_mtu); + assert_eq!(handle.link_mtu(&addr), handle.mtu()); + } }