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
This commit is contained in:
Adam Greig 2019-04-14 18:05:29 +01:00 committed by Homu
parent d9e0c8246c
commit 59f5cbbe64
1 changed files with 85 additions and 4 deletions

View File

@ -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() {