From 2687ecabc2392bc017d23c4a1a3bcd7a3d9f59b4 Mon Sep 17 00:00:00 2001 From: Lee Smet Date: Mon, 5 May 2025 13:40:40 +0200 Subject: [PATCH] Fix #614: Properly reply to local packets for local subnet Previously packets for the local subnet would trigger a route lookup, which leads to a no-route entry being generated. Instead, reply to such packets with an ICMP host unreachable - address uncreachable. This can be done unilaterally since IF the address would exist on the host, the packet would not be pushed to the TUN interface by the kernel and thus, by extension, if such a packet is handled by mycelium, it means there is no address or local subnet configuration. Signed-off-by: Lee Smet --- CHANGELOG.md | 2 ++ mycelium/src/data.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a945037..a79d146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed an unsoundness issue in the routing table clone implementation. - Clear dead peer buffer once peers have been removed from the routing table. +- Properly reply with an address unreachable ICMP when pinging an IP in the local + subnet which does not exist. ## [0.6.0] - 2025-04-25 diff --git a/mycelium/src/data.rs b/mycelium/src/data.rs index c8fcb5d..cd24f80 100644 --- a/mycelium/src/data.rs +++ b/mycelium/src/data.rs @@ -102,6 +102,8 @@ where T: Sink + Clone + Send + Unpin + 'static, T::Error: std::fmt::Display, { + let node_subnet = self.router.node_tun_subnet(); + while let Some(packet) = l3_packet_stream.next().await { let mut packet = match packet { Err(e) => { @@ -146,6 +148,31 @@ where .expect("Static range bounds on slice are correct length"), ); + // If this is a packet for our own Subnet, it means there is no local configuration for + // the destination ip or /64 subnet, and the IP is unreachable + if node_subnet.contains_ip(dst_ip.into()) { + trace!( + "Replying to local packet for unexisting address: {}", + dst_ip + ); + + let mut icmp_packet = PacketBuffer::new(); + let host = self.router.node_public_key().address().octets(); + let icmp = PacketBuilder::ipv6(host, src_ip.octets(), 64).icmpv6( + Icmpv6Type::DestinationUnreachable(DestUnreachableCode::Address), + ); + icmp_packet.set_size(icmp.size(packet.len().min(1280 - 48))); + let mut writer = &mut icmp_packet.buffer_mut()[..]; + if let Err(e) = icmp.write(&mut writer, &packet[..packet.len().min(1280 - 48)]) { + error!("Failed to construct ICMP packet: {e}"); + continue; + } + if let Err(e) = l3_packet_sink.send(icmp_packet).await { + error!("Failed to send ICMP packet to host: {e}"); + } + continue; + } + trace!("Received packet from TUN with dest addr: {:?}", dst_ip); // Check if the source address is part of 400::/7 let first_src_byte = src_ip.segments()[0] >> 8;