Skip to content

Commit de4cced

Browse files
committed
icmpv6: truncate the packet to MTU
This follows RFC4443 section 2.4.
1 parent cfc17ba commit de4cced

File tree

1 file changed

+83
-4
lines changed

1 file changed

+83
-4
lines changed

src/wire/icmpv6.rs

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::wire::NdiscRepr;
1010
#[cfg(feature = "proto-rpl")]
1111
use crate::wire::RplRepr;
1212
use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
13+
use crate::wire::{IPV6_HEADER_LEN, IPV6_MIN_MTU};
1314

1415
enum_with_unknown! {
1516
/// Internet protocol control message type.
@@ -617,7 +618,20 @@ impl<'a> Repr<'a> {
617618
where
618619
T: AsRef<[u8]> + ?Sized,
619620
{
620-
let ip_packet = Ipv6Packet::new_checked(packet.payload())?;
621+
// The packet must be truncated to fit the min MTU. In case the packet is potentially
622+
// truncated, only perform a basic sanity check to ensure the original header is
623+
// still intact.
624+
let ip_packet = if packet.buffer.as_ref().len() >= super::IPV6_MIN_MTU {
625+
let ip_packet = Ipv6Packet::new_unchecked(packet.payload());
626+
627+
if packet.payload().len() < ip_packet.header_len() {
628+
return Err(Error);
629+
}
630+
631+
ip_packet
632+
} else {
633+
Ipv6Packet::new_checked(packet.payload())?
634+
};
621635

622636
let payload = &packet.payload()[ip_packet.header_len()..];
623637
if payload.len() < 8 {
@@ -627,7 +641,7 @@ impl<'a> Repr<'a> {
627641
src_addr: ip_packet.src_addr(),
628642
dst_addr: ip_packet.dst_addr(),
629643
next_header: ip_packet.next_header(),
630-
payload_len: payload.len(),
644+
payload_len: ip_packet.payload_len().into(),
631645
hop_limit: ip_packet.hop_limit(),
632646
};
633647
Ok((payload, repr))
@@ -697,7 +711,15 @@ impl<'a> Repr<'a> {
697711
| &Repr::PktTooBig { header, data, .. }
698712
| &Repr::TimeExceeded { header, data, .. }
699713
| &Repr::ParamProblem { header, data, .. } => {
700-
field::UNUSED.end + header.buffer_len() + data.len()
714+
/* (c) Every ICMPv6 error message (type < 128) MUST include as much of
715+
* the IPv6 offending (invoking) packet (the packet that caused the
716+
* error) as possible without making the error message packet exceed
717+
* the minimum IPv6 MTU [IPv6].
718+
*/
719+
cmp::min(
720+
field::UNUSED.end + header.buffer_len() + data.len(),
721+
IPV6_MIN_MTU - IPV6_HEADER_LEN,
722+
)
701723
}
702724
&Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
703725
field::ECHO_SEQNO.end + data.len()
@@ -725,7 +747,8 @@ impl<'a> Repr<'a> {
725747
let mut ip_packet = Ipv6Packet::new_unchecked(buffer);
726748
header.emit(&mut ip_packet);
727749
let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
728-
payload.copy_from_slice(data);
750+
let data_len = cmp::min(payload.len(), data.len());
751+
payload[..data_len].copy_from_slice(&data[..data_len]);
729752
}
730753

731754
match *self {
@@ -981,4 +1004,60 @@ mod test {
9811004
);
9821005
assert_eq!(&*packet.into_inner(), &PKT_TOO_BIG_BYTES[..]);
9831006
}
1007+
1008+
#[test]
1009+
fn test_buffer_length_is_truncated_to_mtu() {
1010+
let repr = Repr::PktTooBig {
1011+
mtu: 1280,
1012+
header: Ipv6Repr {
1013+
src_addr: Default::default(),
1014+
dst_addr: Default::default(),
1015+
next_header: IpProtocol::Tcp,
1016+
hop_limit: 64,
1017+
payload_len: 1280,
1018+
},
1019+
data: &vec![0; 9999],
1020+
};
1021+
assert_eq!(repr.buffer_len(), 1280 - IPV6_HEADER_LEN);
1022+
}
1023+
1024+
#[test]
1025+
fn test_mtu_truncated_payload_roundtrip() {
1026+
let ip_packet_repr = Ipv6Repr {
1027+
src_addr: Default::default(),
1028+
dst_addr: Default::default(),
1029+
next_header: IpProtocol::Tcp,
1030+
hop_limit: 64,
1031+
payload_len: 1280,
1032+
};
1033+
let mut ip_packet = Ipv6Packet::new_unchecked(vec![0; 1280 + IPV6_HEADER_LEN]);
1034+
ip_packet_repr.emit(&mut ip_packet);
1035+
1036+
let repr1 = Repr::PktTooBig {
1037+
mtu: 1280,
1038+
header: ip_packet_repr,
1039+
// this is needed to make sure roundtrip gives the same value
1040+
// it is not needed for ensuring the correct bytes get emitted
1041+
data: &ip_packet.as_ref()[..1280 - field::HEADER_END - IPV6_HEADER_LEN],
1042+
};
1043+
let mut data = vec![0; 1280];
1044+
let mut packet = Packet::new_unchecked(&mut data);
1045+
repr1.emit(
1046+
&MOCK_IP_ADDR_1,
1047+
&MOCK_IP_ADDR_2,
1048+
&mut packet,
1049+
&ChecksumCapabilities::default(),
1050+
);
1051+
1052+
let packet = Packet::new_unchecked(&data);
1053+
let repr2 = Repr::parse(
1054+
&MOCK_IP_ADDR_1,
1055+
&MOCK_IP_ADDR_2,
1056+
&packet,
1057+
&ChecksumCapabilities::default(),
1058+
)
1059+
.unwrap();
1060+
1061+
assert_eq!(repr1, repr2);
1062+
}
9841063
}

0 commit comments

Comments
 (0)