From 47d27472b9be937f3efeabbdf615ceefba77f4f3 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Sat, 16 Jan 2021 01:22:16 +0000 Subject: [PATCH] subnet_broadcasts Adds `is_subnet_broadcast` to the ethernet interface which checks for subnet broadcasts, which are discussed on page 8 in https://tools.ietf.org/html/rfc917. The subnet broadcast addresses are derived from the interfaces ipv4 addresses. --- src/iface/ethernet.rs | 53 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/iface/ethernet.rs b/src/iface/ethernet.rs index 1be01c7..4aa2e7e 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/ethernet.rs @@ -1023,7 +1023,9 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { if !self.has_ip_addr(ipv4_repr.dst_addr) && !ipv4_repr.dst_addr.is_broadcast() && - !self.has_multicast_group(ipv4_repr.dst_addr) { + !self.has_multicast_group(ipv4_repr.dst_addr) && + !self.is_subnet_broadcast(ipv4_repr.dst_addr) { + // Ignore IP packets not directed at us, or broadcast, or any of the multicast groups. // If AnyIP is enabled, also check if the packet is routed locally. if !self.any_ip || @@ -1066,6 +1068,19 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> { } } + /// Checks if an incoming packet has a broadcast address for the interfaces + /// associated ipv4 addresses. + #[cfg(feature = "proto-ipv4")] + fn is_subnet_broadcast(&self, address: Ipv4Address) -> bool { + self.ip_addrs.iter() + .filter_map(|own_cidr| match own_cidr { + IpCidr::Ipv4(own_ip) => Some(own_ip.broadcast()?), + #[cfg(feature = "proto-ipv6")] + IpCidr::Ipv6(_) => None + }) + .any(|broadcast_address| address == broadcast_address) + } + /// Host duties of the **IGMPv2** protocol. /// /// Sets up `igmp_report_state` for responding to IGMP general/specific membership queries. @@ -1695,7 +1710,7 @@ mod test { use crate::wire::{EthernetAddress, EthernetFrame, EthernetProtocol}; use crate::wire::{IpAddress, IpCidr, IpProtocol, IpRepr}; #[cfg(feature = "proto-ipv4")] - use crate::wire::{Ipv4Address, Ipv4Repr}; + use crate::wire::{Ipv4Address, Ipv4Repr, Ipv4Cidr}; #[cfg(feature = "proto-igmp")] use crate::wire::Ipv4Packet; #[cfg(feature = "proto-ipv4")] @@ -1875,6 +1890,40 @@ mod test { Ok(Some(expected_repr))); } + #[test] + #[cfg(feature = "proto-ipv4")] + fn test_local_subnet_broadcasts() { + let (mut iface, _) = create_loopback(); + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 23]), 24)); + }); + }); + + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 1, 255])), true); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 1, 254])), false); + + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 16)); + }); + }); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 23, 255])), false); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 23, 254])), false); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 255, 254])), false); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 168, 255, 255])), true); + + iface.update_ip_addrs(|addrs| { + addrs.iter_mut().next().map(|addr| { + *addr = IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address([192, 168, 23, 24]), 8)); + }); + }); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 23, 1, 255])), false); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 23, 1, 254])), false); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 255, 255, 254])), false); + assert_eq!(iface.inner.is_subnet_broadcast(Ipv4Address([192, 255, 255, 255])), true); + } + #[test] #[cfg(all(feature = "socket-udp", feature = "proto-ipv4"))] fn test_icmp_error_port_unreachable() {