浏览代码

Do not send ICMPv4 responses to broadcasts

 - Do not send ICMPv4 responses for packets with a broadcast destination
   address.
   - Do not send DstUnreachable with ProtoUnreachable on receipt of a
     packet with an unknown protocol with a non-unicast destination
     address.
   - Do not send DstUnreachable with PortUnreachable on receipt of a
     UDP packet when no sockets are listening on the destination port
     and the destination address is a non-unicast address.
 - Send the correct amount of the original datagram when sending Destination
   Unreachable error responses.
   - Do not assume that a ip datagram has a payload when sending a proto
     unreachable ICMPv4 error response.
 - Add tests to iface tests.
   - Ensure ICMP error responses are correctly formed when the
     datagram has no payload.
   - Ensure ICMP error responses are correctly handled for UDP packets
     when no socket is listening on the destination port.
   - Ensure the correct amount of the original payload is returned in
     Destination Unreachable responses.
Dan Robertson 7 年之前
父节点
当前提交
d1d80ca121
共有 1 个文件被更改,包括 177 次插入27 次删除
  1. 177 27
      src/iface/ethernet.rs

+ 177 - 27
src/iface/ethernet.rs

@@ -1,6 +1,7 @@
 // Heads up! Before working on this file you should read the parts
 // of RFC 1122 that discuss Ethernet, ARP and IP.
 
+use core::cmp;
 use managed::{Managed, ManagedSlice};
 
 use {Error, Result};
@@ -161,6 +162,26 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
         }
     }
 
+    fn icmpv4_reply<'frame, 'icmp: 'frame>(&self,
+                                           ipv4_repr: Ipv4Repr,
+                                           icmp_repr: Icmpv4Repr<'icmp>)
+            -> Packet<'frame> {
+        if ipv4_repr.dst_addr.is_unicast() {
+            let ipv4_reply_repr = Ipv4Repr {
+                src_addr:    ipv4_repr.dst_addr,
+                dst_addr:    ipv4_repr.src_addr,
+                protocol:    IpProtocol::Icmp,
+                payload_len: icmp_repr.buffer_len(),
+                ttl:         64
+            };
+            Packet::Icmpv4(ipv4_reply_repr, icmp_repr)
+        } else {
+            // Do not send Protocol Unreachable ICMPv4 error responses to datagrams
+            // with a broadcast destination address.
+            Packet::None
+        }
+    }
+
     fn socket_ingress(&mut self, sockets: &mut SocketSet, timestamp: u64) -> Result<bool> {
         let mut processed_any = false;
         loop {
@@ -363,19 +384,15 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
                 Ok(Packet::None),
 
             _ => {
+                // Send back as much of the original payload as we can
+                let payload_len = cmp::min(
+                    ip_payload.len(), self.device.capabilities().max_transmission_unit);
                 let icmp_reply_repr = Icmpv4Repr::DstUnreachable {
                     reason: Icmpv4DstUnreachable::ProtoUnreachable,
                     header: ipv4_repr,
-                    data:   &ip_payload[0..8]
+                    data:   &ip_payload[0..payload_len]
                 };
-                let ipv4_reply_repr = Ipv4Repr {
-                    src_addr:    ipv4_repr.dst_addr,
-                    dst_addr:    ipv4_repr.src_addr,
-                    protocol:    IpProtocol::Icmp,
-                    payload_len: icmp_reply_repr.buffer_len(),
-                    ttl:         64,
-                };
-                Ok(Packet::Icmpv4(ipv4_reply_repr, icmp_reply_repr))
+                Ok(self.icmpv4_reply(ipv4_repr, icmp_reply_repr))
             }
         }
     }
@@ -394,14 +411,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
                     seq_no: seq_no,
                     data:   data
                 };
-                let ipv4_reply_repr = Ipv4Repr {
-                    src_addr:    ipv4_repr.dst_addr,
-                    dst_addr:    ipv4_repr.src_addr,
-                    protocol:    IpProtocol::Icmp,
-                    payload_len: icmp_reply_repr.buffer_len(),
-                    ttl:         64
-                };
-                Ok(Packet::Icmpv4(ipv4_reply_repr, icmp_reply_repr))
+                Ok(self.icmpv4_reply(ipv4_repr, icmp_reply_repr))
             }
 
             // Ignore any echo replies.
@@ -435,19 +445,15 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
         // The packet wasn't handled by a socket, send an ICMP port unreachable packet.
         match ip_repr {
             IpRepr::Ipv4(ipv4_repr) => {
+                // Send back as much of the original payload as we can
+                let payload_len = cmp::min(
+                    ip_payload.len(), self.device.capabilities().max_transmission_unit);
                 let icmpv4_reply_repr = Icmpv4Repr::DstUnreachable {
                     reason: Icmpv4DstUnreachable::PortUnreachable,
                     header: ipv4_repr,
-                    data:   &ip_payload[0..8]
-                };
-                let ipv4_reply_repr = Ipv4Repr {
-                    src_addr:    ipv4_repr.dst_addr,
-                    dst_addr:    ipv4_repr.src_addr,
-                    protocol:    IpProtocol::Icmp,
-                    payload_len: icmpv4_reply_repr.buffer_len(),
-                    ttl:         64,
+                    data:   &ip_payload[0..payload_len]
                 };
-                Ok(Packet::Icmpv4(ipv4_reply_repr, icmpv4_reply_repr))
+                Ok(self.icmpv4_reply(ipv4_repr, icmpv4_reply_repr))
             },
             IpRepr::Unspecified { .. } |
             IpRepr::__Nonexhaustive =>
@@ -647,7 +653,7 @@ impl<'a, 'b, 'c, DeviceT: Device + 'a> Interface<'a, 'b, 'c, DeviceT> {
 }
 
 #[cfg(test)]
-mod tests {
+mod test {
     use std::boxed::Box;
     use super::Packet;
     use phy::{Loopback, ChecksumCapabilities};
@@ -655,6 +661,8 @@ mod tests {
     use wire::{EthernetAddress, EthernetFrame, EthernetProtocol};
     use wire::{IpAddress, IpCidr, IpProtocol, IpRepr};
     use wire::{Ipv4Address, Ipv4Repr};
+    use wire::{Icmpv4Repr, Icmpv4DstUnreachable};
+    use wire::{UdpPacket, UdpRepr};
     use iface::{ArpCache, SliceArpCache, EthernetInterface};
     use socket::SocketSet;
 
@@ -706,6 +714,148 @@ mod tests {
                    Ok(Packet::None));
     }
 
+    #[test]
+    fn icmp_error_no_payload() {
+        static NO_BYTES: [u8; 0] = [];
+        let (mut iface, mut socket_set) = create_loopback();
+
+        let mut eth_bytes = vec![0u8; 34];
+
+        // Unknown Ipv4 Protocol with no payload
+        let repr = IpRepr::Ipv4(Ipv4Repr {
+            src_addr:    Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
+            dst_addr:    Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
+            protocol:    IpProtocol::Unknown(0x0c),
+            payload_len: 0,
+            ttl:         0x40
+        });
+
+        // emit the above repr to a frame
+        let frame = {
+            let mut frame = EthernetFrame::new(&mut eth_bytes);
+            frame.set_dst_addr(EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
+            frame.set_src_addr(EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]));
+            frame.set_ethertype(EthernetProtocol::Ipv4);
+            repr.emit(frame.payload_mut(), &ChecksumCapabilities::default());
+            EthernetFrame::new(&*frame.into_inner())
+        };
+
+        // The expected Destination Unreachable response due to the
+        // unknown protocol
+        let icmp_repr = Icmpv4Repr::DstUnreachable {
+            reason: Icmpv4DstUnreachable::ProtoUnreachable,
+            header: Ipv4Repr {
+                src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
+                dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
+                protocol: IpProtocol::Unknown(12),
+                payload_len: 0,
+                ttl: 64
+            },
+            data: &NO_BYTES
+        };
+
+        let expected_repr = Packet::Icmpv4(
+            Ipv4Repr {
+                src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
+                dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
+                protocol: IpProtocol::Icmp,
+                payload_len: icmp_repr.buffer_len(),
+                ttl: 64
+            },
+            icmp_repr
+        );
+
+        // Ensure that the unknown protocol triggers an error response.
+        // And we correctly handle no payload.
+        assert_eq!(iface.process_ipv4(&mut socket_set, 0, &frame),
+                   Ok(expected_repr));
+    }
+
+    #[test]
+    fn icmp_error_port_unreachable() {
+        static UDP_PAYLOAD: [u8; 12] = [
+            0x48, 0x65, 0x6c, 0x6c,
+            0x6f, 0x2c, 0x20, 0x57,
+            0x6f, 0x6c, 0x64, 0x21
+        ];
+        let (iface, mut socket_set) = create_loopback();
+
+        let mut udp_bytes_unicast = vec![0u8; 20];
+        let mut udp_bytes_broadcast = vec![0u8; 20];
+        let mut packet_unicast = UdpPacket::new(&mut udp_bytes_unicast);
+        let mut packet_broadcast = UdpPacket::new(&mut udp_bytes_broadcast);
+
+        // Unknown Ipv4 Protocol with no payload
+        let udp_repr = UdpRepr {
+            src_port: 67,
+            dst_port: 68,
+            payload:  &UDP_PAYLOAD
+        };
+
+        let ip_repr = IpRepr::Ipv4(Ipv4Repr {
+            src_addr:    Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
+            dst_addr:    Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
+            protocol:    IpProtocol::Udp,
+            payload_len: udp_repr.buffer_len(),
+            ttl:         64
+        });
+
+        // Emit the representations to a packet
+        udp_repr.emit(&mut packet_unicast, &ip_repr.src_addr(),
+                      &ip_repr.dst_addr(), &ChecksumCapabilities::default());
+
+        let data = packet_unicast.into_inner();
+
+        // The expected Destination Unreachable ICMPv4 error response due
+        // to no sockets listening on the destination port.
+        let icmp_repr = Icmpv4Repr::DstUnreachable {
+            reason: Icmpv4DstUnreachable::PortUnreachable,
+            header: Ipv4Repr {
+                src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
+                dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
+                protocol: IpProtocol::Udp,
+                payload_len: udp_repr.buffer_len(),
+                ttl: 64
+            },
+            data: &data
+        };
+        let expected_repr = Packet::Icmpv4(
+            Ipv4Repr {
+                src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
+                dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
+                protocol: IpProtocol::Icmp,
+                payload_len: icmp_repr.buffer_len(),
+                ttl: 64
+            },
+            icmp_repr
+        );
+
+        // Ensure that the unknown protocol triggers an error response.
+        // And we correctly handle no payload.
+        assert_eq!(iface.process_udp(&mut socket_set, ip_repr, data),
+                   Ok(expected_repr));
+
+        let ip_repr = IpRepr::Ipv4(Ipv4Repr {
+            src_addr:    Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
+            dst_addr:    Ipv4Address::BROADCAST,
+            protocol:    IpProtocol::Udp,
+            payload_len: udp_repr.buffer_len(),
+            ttl:         64
+        });
+
+        // Emit the representations to a packet
+        udp_repr.emit(&mut packet_broadcast, &ip_repr.src_addr(),
+                      &IpAddress::Ipv4(Ipv4Address::BROADCAST),
+                      &ChecksumCapabilities::default());
+
+        // Ensure that the port unreachable error does not trigger an
+        // ICMP error response when the destination address is a
+        // broadcast address
+        assert_eq!(iface.process_udp(&mut socket_set, ip_repr, packet_broadcast.into_inner()),
+                   Ok(Packet::None));
+
+    }
+
     #[test]
     fn handle_valid_arp_request() {
         let (mut iface, mut socket_set) = create_loopback();