Skip to content

Commit fe311b0

Browse files
committed
Set correct packet length after defragmenting a packet
1 parent 39cd44e commit fe311b0

File tree

2 files changed

+128
-5
lines changed

2 files changed

+128
-5
lines changed

src/iface/interface/ipv4.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ impl InterfaceInner {
100100
ipv4_packet: &Ipv4Packet<&'a [u8]>,
101101
frag: &'a mut FragmentsBuffer,
102102
) -> Option<Packet<'a>> {
103-
let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum));
103+
let mut ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum));
104104
if !self.is_unicast_v4(ipv4_repr.src_addr) && !ipv4_repr.src_addr.is_unspecified() {
105105
// Discard packets with non-unicast source addresses but allow unspecified
106106
net_debug!("non-unicast or unspecified source address");
@@ -133,10 +133,10 @@ impl InterfaceInner {
133133
return None;
134134
}
135135

136-
// NOTE: according to the standard, the total length needs to be
137-
// recomputed, as well as the checksum. However, we don't really use
138-
// the IPv4 header after the packet is reassembled.
139-
f.assemble()?
136+
let payload = f.assemble()?;
137+
// Update the payload length, so that the raw sockets get the correct value.
138+
ipv4_repr.payload_len = payload.len();
139+
payload
140140
} else {
141141
ipv4_packet.payload()
142142
}

src/iface/interface/tests/ipv4.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,6 +1183,129 @@ fn test_raw_socket_tx_fragmentation(#[case] medium: Medium) {
11831183
}
11841184
}
11851185

1186+
#[rstest]
1187+
#[case(Medium::Ip)]
1188+
#[cfg(all(
1189+
feature = "socket-raw",
1190+
feature = "proto-ipv4-fragmentation",
1191+
feature = "medium-ip"
1192+
))]
1193+
#[case(Medium::Ethernet)]
1194+
#[cfg(all(
1195+
feature = "socket-raw",
1196+
feature = "proto-ipv4-fragmentation",
1197+
feature = "medium-ethernet"
1198+
))]
1199+
fn test_raw_socket_rx_fragmentation(#[case] medium: Medium) {
1200+
use crate::wire::{IpProtocol, IpVersion, Ipv4Address, Ipv4Packet, Ipv4Repr};
1201+
1202+
let (mut iface, mut sockets, _device) = setup(medium);
1203+
1204+
// Raw socket bound to IPv4 and a custom protocol.
1205+
let packets = 1;
1206+
let rx_buffer = raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 64]);
1207+
let tx_buffer = raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 64]);
1208+
let raw_socket = raw::Socket::new(
1209+
Some(IpVersion::Ipv4),
1210+
Some(IpProtocol::Unknown(99)),
1211+
rx_buffer,
1212+
tx_buffer,
1213+
);
1214+
let handle = sockets.add(raw_socket);
1215+
1216+
// Build two IPv4 fragments that together form one packet.
1217+
let src_addr = Ipv4Address::new(127, 0, 0, 2);
1218+
let dst_addr = Ipv4Address::new(127, 0, 0, 1);
1219+
let proto = IpProtocol::Unknown(99);
1220+
let ident: u16 = 0x1234;
1221+
1222+
let total_payload_len = 30usize;
1223+
let first_payload_len = 24usize; // must be a multiple of 8
1224+
let last_payload_len = total_payload_len - first_payload_len;
1225+
1226+
// Helper to build one fragment as on-the-wire bytes
1227+
let build_fragment = |payload_len: usize,
1228+
more_frags: bool,
1229+
frag_offset_octets: u16,
1230+
payload_byte: u8|
1231+
-> Vec<u8> {
1232+
let repr = Ipv4Repr {
1233+
src_addr,
1234+
dst_addr,
1235+
next_header: proto,
1236+
hop_limit: 64,
1237+
payload_len,
1238+
};
1239+
let header_len = repr.buffer_len();
1240+
let mut bytes = vec![0u8; header_len + payload_len];
1241+
{
1242+
let mut pkt = Ipv4Packet::new_unchecked(&mut bytes[..]);
1243+
repr.emit(&mut pkt, &ChecksumCapabilities::default());
1244+
pkt.set_ident(ident);
1245+
pkt.set_dont_frag(false);
1246+
pkt.set_more_frags(more_frags);
1247+
pkt.set_frag_offset(frag_offset_octets);
1248+
// Recompute checksum after changing fragmentation fields.
1249+
pkt.fill_checksum();
1250+
}
1251+
// Fill payload with a simple pattern for validation
1252+
for b in &mut bytes[header_len..] {
1253+
*b = payload_byte;
1254+
}
1255+
bytes
1256+
};
1257+
1258+
let frag1_bytes = build_fragment(first_payload_len, true, 0, 0xAA);
1259+
let frag2_bytes = build_fragment(last_payload_len, false, first_payload_len as u16, 0xBB);
1260+
1261+
let frag1 = Ipv4Packet::new_unchecked(&frag1_bytes[..]);
1262+
let frag2 = Ipv4Packet::new_unchecked(&frag2_bytes[..]);
1263+
1264+
// First fragment alone should not be delivered to the raw socket.
1265+
assert_eq!(
1266+
iface.inner.process_ipv4(
1267+
&mut sockets,
1268+
PacketMeta::default(),
1269+
HardwareAddress::default(),
1270+
&frag1,
1271+
&mut iface.fragments
1272+
),
1273+
None
1274+
);
1275+
{
1276+
let socket = sockets.get_mut::<raw::Socket>(handle);
1277+
assert!(!socket.can_recv());
1278+
}
1279+
1280+
// After the last fragment, the reassembled packet should be delivered.
1281+
assert_eq!(
1282+
iface.inner.process_ipv4(
1283+
&mut sockets,
1284+
PacketMeta::default(),
1285+
HardwareAddress::default(),
1286+
&frag2,
1287+
&mut iface.fragments
1288+
),
1289+
None
1290+
);
1291+
1292+
// Validate the raw socket received one defragmented packet with correct payload.
1293+
let socket = sockets.get_mut::<raw::Socket>(handle);
1294+
assert!(socket.can_recv());
1295+
let data = socket.recv().expect("raw socket should have a packet");
1296+
let packet = Ipv4Packet::new_unchecked(data);
1297+
let repr = Ipv4Repr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
1298+
assert_eq!(repr.src_addr, src_addr);
1299+
assert_eq!(repr.dst_addr, dst_addr);
1300+
assert_eq!(repr.next_header, proto);
1301+
assert_eq!(repr.payload_len, total_payload_len);
1302+
1303+
let payload = packet.payload();
1304+
assert_eq!(payload.len(), total_payload_len);
1305+
assert!(payload[..first_payload_len].iter().all(|&b| b == 0xAA));
1306+
assert!(payload[first_payload_len..].iter().all(|&b| b == 0xBB));
1307+
}
1308+
11861309
#[rstest]
11871310
#[case(Medium::Ip)]
11881311
#[cfg(all(feature = "socket-udp", feature = "medium-ip"))]

0 commit comments

Comments
 (0)