Ensure ICMPv4 error replies comply with size requirements
- Add MIN_MTU constants to the IP version modules. - Ensure that ICMPv4 error replies comply with the size requirements specified in RFC 1812 § 4.3.2.3.
This commit is contained in:
parent
98679f9a1b
commit
22b048c225
|
@ -9,16 +9,16 @@ use phy::{Device, DeviceCapabilities, RxToken, TxToken};
|
|||
use wire::pretty_print::PrettyPrinter;
|
||||
use wire::{EthernetAddress, EthernetProtocol, EthernetFrame};
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use wire::{Ipv4Address};
|
||||
use wire::{Ipv4Address, Ipv4Packet, Ipv4Repr};
|
||||
use wire::{IpAddress, IpProtocol, IpRepr, IpCidr};
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use wire::{ArpPacket, ArpRepr, ArpOperation};
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use wire::{Ipv4Packet, Ipv4Repr};
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use wire::{Icmpv4Packet, Icmpv4Repr, Icmpv4DstUnreachable};
|
||||
#[cfg(feature = "socket-udp")]
|
||||
use wire::{UdpPacket, UdpRepr};
|
||||
#[cfg(all(feature = "proto-ipv4", feature = "socket-udp"))]
|
||||
use wire::IPV4_MIN_MTU;
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
use wire::{TcpPacket, TcpRepr, TcpControl};
|
||||
|
||||
|
@ -715,9 +715,18 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||
match ip_repr {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
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);
|
||||
// Send back as much of the original payload as will fit within
|
||||
// the minimum MTU required by IPv4. See RFC 1812 § 4.3.2.3 for
|
||||
// more details.
|
||||
//
|
||||
// Since the entire network layer packet must fit within 576
|
||||
// bytes, the payload must not exceed the following:
|
||||
//
|
||||
// 576 - New IP hdr size - Old IP hdr size - ICMPv4 DstUnreachable hdr size
|
||||
//
|
||||
// We do no support IP options, so this becomes 576 - 20 - 20 - 8.
|
||||
const DST_UNREACHABLE_HDR_SIZE: usize = 48;
|
||||
let payload_len = cmp::min(ip_payload.len(), IPV4_MIN_MTU - DST_UNREACHABLE_HDR_SIZE);
|
||||
let icmpv4_reply_repr = Icmpv4Repr::DstUnreachable {
|
||||
reason: Icmpv4DstUnreachable::PortUnreachable,
|
||||
header: ipv4_repr,
|
||||
|
@ -1246,6 +1255,62 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "socket-udp")]
|
||||
fn test_icmpv4_reply_size() {
|
||||
use wire::IPV4_MIN_MTU;
|
||||
|
||||
let (iface, mut socket_set) = create_loopback();
|
||||
|
||||
let src_addr = Ipv4Address([192, 168, 1, 1]);
|
||||
let dst_addr = Ipv4Address([192, 168, 1, 2]);
|
||||
|
||||
// UDP packet that if not tructated will cause a icmp port unreachable reply
|
||||
// to exeed 576 bytes in length.
|
||||
let udp_repr = UdpRepr {
|
||||
src_port: 67,
|
||||
dst_port: 68,
|
||||
payload: &[0x2a; 524]
|
||||
};
|
||||
let mut bytes = vec![0xff; udp_repr.buffer_len()];
|
||||
let mut packet = UdpPacket::new(&mut bytes[..]);
|
||||
udp_repr.emit(&mut packet, &src_addr.into(), &dst_addr.into(), &ChecksumCapabilities::default());
|
||||
let ipv4_repr = Ipv4Repr {
|
||||
src_addr: src_addr,
|
||||
dst_addr: dst_addr,
|
||||
protocol: IpProtocol::Udp,
|
||||
hop_limit: 64,
|
||||
payload_len: udp_repr.buffer_len()
|
||||
};
|
||||
let payload = packet.into_inner();
|
||||
|
||||
// Expected packets
|
||||
let expected_icmpv4_repr = Icmpv4Repr::DstUnreachable {
|
||||
reason: Icmpv4DstUnreachable::PortUnreachable,
|
||||
header: ipv4_repr,
|
||||
// We only include 520 bytes of the original payload
|
||||
// in the expected packets payload. We must only send
|
||||
// ICMPv4 replies that do not exceed 576 bytes in length.
|
||||
//
|
||||
// 528 + 2 * sizeof(IPv4 Header) + sizeof(DstUnreachable Header) = 576
|
||||
data: &payload[..528]
|
||||
};
|
||||
let expected_ipv4_repr = Ipv4Repr {
|
||||
src_addr: dst_addr,
|
||||
dst_addr: src_addr,
|
||||
protocol: IpProtocol::Icmp,
|
||||
hop_limit: 64,
|
||||
payload_len: expected_icmpv4_repr.buffer_len()
|
||||
};
|
||||
|
||||
// The expected packet does not exceed the IPV4_MIN_MTU
|
||||
assert_eq!(expected_ipv4_repr.buffer_len() + expected_icmpv4_repr.buffer_len(),
|
||||
IPV4_MIN_MTU);
|
||||
// The expected packet and the generated packet are equal
|
||||
assert_eq!(iface.inner.process_udp(&mut socket_set, ipv4_repr.into(), payload),
|
||||
Ok(Packet::Icmpv4((expected_ipv4_repr, expected_icmpv4_repr))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_handle_valid_arp_request() {
|
||||
let (mut iface, mut socket_set) = create_loopback();
|
||||
|
|
|
@ -7,6 +7,20 @@ use super::ip::{checksum, pretty_print_ip_payload};
|
|||
|
||||
pub use super::IpProtocol as Protocol;
|
||||
|
||||
/// Minimum MTU required of all links supporting IPv4. See [RFC 791 § 3.1].
|
||||
///
|
||||
/// [RFC 791 § 3.1]: https://tools.ietf.org/html/rfc791#section-3.1
|
||||
// RFC 791 states the following:
|
||||
//
|
||||
// > Every internet module must be able to forward a datagram of 68
|
||||
// > octets without further fragmentation... Every internet destination
|
||||
// > must be able to receive a datagram of 576 octets either in one piece
|
||||
// > or in fragments to be reassembled.
|
||||
//
|
||||
// As a result, we can assume that every host we send packets to can
|
||||
// accept a packet of the following size.
|
||||
pub const MIN_MTU: usize = 576;
|
||||
|
||||
/// A four-octet IPv4 address.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
|
||||
pub struct Address(pub [u8; 4]);
|
||||
|
|
|
@ -7,6 +7,11 @@ use {Error, Result};
|
|||
pub use super::IpProtocol as Protocol;
|
||||
use super::ip::pretty_print_ip_payload;
|
||||
|
||||
/// Minimum MTU required of all links supporting IPv6. See [RFC 8200 § 5].
|
||||
///
|
||||
/// [RFC 8200 § 5]: https://tools.ietf.org/html/rfc8200#section-5
|
||||
pub const MIN_MTU: usize = 1280;
|
||||
|
||||
/// A sixteen-octet IPv6 address.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
|
||||
pub struct Address(pub [u8; 16]);
|
||||
|
|
|
@ -116,13 +116,15 @@ pub use self::ip::{Version as IpVersion,
|
|||
pub use self::ipv4::{Address as Ipv4Address,
|
||||
Packet as Ipv4Packet,
|
||||
Repr as Ipv4Repr,
|
||||
Cidr as Ipv4Cidr};
|
||||
Cidr as Ipv4Cidr,
|
||||
MIN_MTU as IPV4_MIN_MTU};
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub use self::ipv6::{Address as Ipv6Address,
|
||||
Packet as Ipv6Packet,
|
||||
Repr as Ipv6Repr,
|
||||
Cidr as Ipv6Cidr};
|
||||
Cidr as Ipv6Cidr,
|
||||
MIN_MTU as IPV6_MIN_MTU};
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
pub use self::icmpv4::{Message as Icmpv4Message,
|
||||
|
|
Loading…
Reference in New Issue