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.v0.7.x
parent
fe6b04a29a
commit
b64257812b
|
@ -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]
|
||||
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: icmpv4_reply_repr.buffer_len(),
|
||||
ttl: 64,
|
||||
};
|
||||
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();
|
||||
|
|
Loading…
Reference in New Issue