Эх сурвалжийг харах

Fix IPv4 broadcast handling

A regression meant broadcast IPv4 packets were ignored. Restore
handling those packets and also reply to broadcast ICMPv4 echo requests.

Closes #287.

Closes: #288
Approved by: whitequark
Adam Greig 6 жил өмнө
parent
commit
5334b93158
1 өөрчлөгдсөн 85 нэмэгдсэн , 4 устгасан
  1. 85 4
      src/iface/ethernet.rs

+ 85 - 4
src/iface/ethernet.rs

@@ -941,8 +941,10 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> {
         #[cfg(feature = "socket-raw")]
         let handled_by_raw_socket = self.raw_socket_filter(sockets, &ip_repr, ip_payload);
 
-        if !self.has_ip_addr(ipv4_repr.dst_addr) && !self.has_multicast_group(ipv4_repr.dst_addr) {
-            // Ignore IP packets not directed at us or any of the multicast groups
+        if !self.has_ip_addr(ipv4_repr.dst_addr) &&
+           !ipv4_repr.dst_addr.is_broadcast() &&
+           !self.has_multicast_group(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 {
                 return Ok(Packet::None);
@@ -1242,7 +1244,11 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> {
                    (&self, ipv4_repr: Ipv4Repr, icmp_repr: Icmpv4Repr<'icmp>) ->
                    Packet<'frame>
     {
-        if ipv4_repr.dst_addr.is_unicast() {
+        if !ipv4_repr.src_addr.is_unicast() {
+            // Do not send ICMP replies to non-unicast sources
+            Packet::None
+        } else if ipv4_repr.dst_addr.is_unicast() {
+            // Reply as normal when src_addr and dst_addr are both unicast
             let ipv4_reply_repr = Ipv4Repr {
                 src_addr:    ipv4_repr.dst_addr,
                 dst_addr:    ipv4_repr.src_addr,
@@ -1251,8 +1257,25 @@ impl<'b, 'c, 'e> InterfaceInner<'b, 'c, 'e> {
                 hop_limit:   64
             };
             Packet::Icmpv4((ipv4_reply_repr, icmp_repr))
+        } else if ipv4_repr.dst_addr.is_broadcast() {
+            // Only reply to broadcasts for echo replies and not other ICMP messages
+            match icmp_repr {
+                Icmpv4Repr::EchoReply {..} => match self.ipv4_address() {
+                    Some(src_addr) => {
+                        let ipv4_reply_repr = Ipv4Repr {
+                            src_addr:    src_addr,
+                            dst_addr:    ipv4_repr.src_addr,
+                            protocol:    IpProtocol::Icmp,
+                            payload_len: icmp_repr.buffer_len(),
+                            hop_limit:   64
+                        };
+                        Packet::Icmpv4((ipv4_reply_repr, icmp_repr))
+                    },
+                    None => Packet::None,
+                },
+                _ => Packet::None,
+            }
         } else {
-            // Do not send any ICMP replies to a broadcast destination address.
             Packet::None
         }
     }
@@ -2017,6 +2040,64 @@ mod test {
         }
     }
 
+    #[test]
+    #[cfg(feature = "proto-ipv4")]
+    fn test_handle_ipv4_broadcast() {
+        use wire::{Ipv4Packet, Icmpv4Repr, Icmpv4Packet};
+
+        let (mut iface, mut socket_set) = create_loopback();
+
+        let our_ipv4_addr = iface.ipv4_address().unwrap();
+        let src_ipv4_addr = Ipv4Address([127, 0, 0, 2]);
+
+        // ICMPv4 echo request
+        let icmpv4_data: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
+        let icmpv4_repr = Icmpv4Repr::EchoRequest {
+            ident: 0x1234, seq_no: 0xabcd, data: &icmpv4_data
+        };
+
+        // Send to IPv4 broadcast address
+        let ipv4_repr = Ipv4Repr {
+            src_addr:    src_ipv4_addr,
+            dst_addr:    Ipv4Address::BROADCAST,
+            protocol:    IpProtocol::Icmp,
+            hop_limit:   64,
+            payload_len: icmpv4_repr.buffer_len(),
+        };
+
+        // Emit to ethernet frame
+        let mut eth_bytes = vec![0u8;
+            EthernetFrame::<&[u8]>::header_len() +
+            ipv4_repr.buffer_len() + icmpv4_repr.buffer_len()
+        ];
+        let frame = {
+            let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes);
+            ipv4_repr.emit(
+                &mut Ipv4Packet::new_unchecked(frame.payload_mut()),
+                &ChecksumCapabilities::default());
+            icmpv4_repr.emit(
+                &mut Icmpv4Packet::new_unchecked(
+                    &mut frame.payload_mut()[ipv4_repr.buffer_len()..]),
+                &ChecksumCapabilities::default());
+            EthernetFrame::new_unchecked(&*frame.into_inner())
+        };
+
+        // Expected ICMPv4 echo reply
+        let expected_icmpv4_repr = Icmpv4Repr::EchoReply {
+            ident: 0x1234, seq_no: 0xabcd, data: &icmpv4_data };
+        let expected_ipv4_repr = Ipv4Repr {
+            src_addr: our_ipv4_addr,
+            dst_addr: src_ipv4_addr,
+            protocol: IpProtocol::Icmp,
+            hop_limit: 64,
+            payload_len: expected_icmpv4_repr.buffer_len(),
+        };
+        let expected_packet = Packet::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr));
+
+        assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame),
+                   Ok(expected_packet));
+    }
+
     #[test]
     #[cfg(feature = "socket-udp")]
     fn test_icmp_reply_size() {