Add per-link MTU support to Transport trait

Add link_mtu(&TransportAddr) method to the Transport trait with a
default implementation that falls back to the transport-wide mtu().
This enables transports like BLE to report per-connection MTU values
while maintaining backward compatibility for UDP and other transports
that use a single MTU for all links.

Update the forwarding and session send paths to query link_mtu() with
the next-hop peer's current address, falling back to transport-wide
mtu() when no address is available.
This commit is contained in:
Johnathan Corgan
2026-02-22 20:44:57 +00:00
parent f920526ece
commit 5410bf3490
3 changed files with 182 additions and 2 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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<std::net::SocketAddr> {
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<Vec<DiscoveredPeer>, 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<Vec<DiscoveredPeer>, 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());
}
}