Skip to content

Commit d995fcb

Browse files
aaronddaviesxyzzyz
andauthored
Set correct packet length after defragmenting a packet (#2)
Co-authored-by: Adam Michalik <[email protected]>
1 parent 35fd66b commit d995fcb

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
@@ -101,7 +101,7 @@ impl InterfaceInner {
101101
ipv4_packet: &Ipv4Packet<&'a [u8]>,
102102
frag: &'a mut FragmentsBuffer,
103103
) -> Option<Packet<'a>> {
104-
let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum));
104+
let mut ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum));
105105
if !self.is_unicast_v4(ipv4_repr.src_addr) && !ipv4_repr.src_addr.is_unspecified() {
106106
// Discard packets with non-unicast source addresses but allow unspecified
107107
net_debug!("non-unicast or unspecified source address");
@@ -134,10 +134,10 @@ impl InterfaceInner {
134134
return None;
135135
}
136136

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

src/iface/interface/tests/ipv4.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,6 +1409,129 @@ fn test_raw_socket_tx_fragmentation(#[case] medium: Medium) {
14091409
}
14101410
}
14111411

1412+
#[rstest]
1413+
#[case(Medium::Ip)]
1414+
#[cfg(all(
1415+
feature = "socket-raw",
1416+
feature = "proto-ipv4-fragmentation",
1417+
feature = "medium-ip"
1418+
))]
1419+
#[case(Medium::Ethernet)]
1420+
#[cfg(all(
1421+
feature = "socket-raw",
1422+
feature = "proto-ipv4-fragmentation",
1423+
feature = "medium-ethernet"
1424+
))]
1425+
fn test_raw_socket_rx_fragmentation(#[case] medium: Medium) {
1426+
use crate::wire::{IpProtocol, IpVersion, Ipv4Address, Ipv4Packet, Ipv4Repr};
1427+
1428+
let (mut iface, mut sockets, _device) = setup(medium);
1429+
1430+
// Raw socket bound to IPv4 and a custom protocol.
1431+
let packets = 1;
1432+
let rx_buffer = raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 64]);
1433+
let tx_buffer = raw::PacketBuffer::new(vec![raw::PacketMetadata::EMPTY; packets], vec![0; 64]);
1434+
let raw_socket = raw::Socket::new(
1435+
Some(IpVersion::Ipv4),
1436+
Some(IpProtocol::Unknown(99)),
1437+
rx_buffer,
1438+
tx_buffer,
1439+
);
1440+
let handle = sockets.add(raw_socket);
1441+
1442+
// Build two IPv4 fragments that together form one packet.
1443+
let src_addr = Ipv4Address::new(127, 0, 0, 2);
1444+
let dst_addr = Ipv4Address::new(127, 0, 0, 1);
1445+
let proto = IpProtocol::Unknown(99);
1446+
let ident: u16 = 0x1234;
1447+
1448+
let total_payload_len = 30usize;
1449+
let first_payload_len = 24usize; // must be a multiple of 8
1450+
let last_payload_len = total_payload_len - first_payload_len;
1451+
1452+
// Helper to build one fragment as on-the-wire bytes
1453+
let build_fragment = |payload_len: usize,
1454+
more_frags: bool,
1455+
frag_offset_octets: u16,
1456+
payload_byte: u8|
1457+
-> Vec<u8> {
1458+
let repr = Ipv4Repr {
1459+
src_addr,
1460+
dst_addr,
1461+
next_header: proto,
1462+
hop_limit: 64,
1463+
payload_len,
1464+
};
1465+
let header_len = repr.buffer_len();
1466+
let mut bytes = vec![0u8; header_len + payload_len];
1467+
{
1468+
let mut pkt = Ipv4Packet::new_unchecked(&mut bytes[..]);
1469+
repr.emit(&mut pkt, &ChecksumCapabilities::default());
1470+
pkt.set_ident(ident);
1471+
pkt.set_dont_frag(false);
1472+
pkt.set_more_frags(more_frags);
1473+
pkt.set_frag_offset(frag_offset_octets);
1474+
// Recompute checksum after changing fragmentation fields.
1475+
pkt.fill_checksum();
1476+
}
1477+
// Fill payload with a simple pattern for validation
1478+
for b in &mut bytes[header_len..] {
1479+
*b = payload_byte;
1480+
}
1481+
bytes
1482+
};
1483+
1484+
let frag1_bytes = build_fragment(first_payload_len, true, 0, 0xAA);
1485+
let frag2_bytes = build_fragment(last_payload_len, false, first_payload_len as u16, 0xBB);
1486+
1487+
let frag1 = Ipv4Packet::new_unchecked(&frag1_bytes[..]);
1488+
let frag2 = Ipv4Packet::new_unchecked(&frag2_bytes[..]);
1489+
1490+
// First fragment alone should not be delivered to the raw socket.
1491+
assert_eq!(
1492+
iface.inner.process_ipv4(
1493+
&mut sockets,
1494+
PacketMeta::default(),
1495+
HardwareAddress::default(),
1496+
&frag1,
1497+
&mut iface.fragments
1498+
),
1499+
None
1500+
);
1501+
{
1502+
let socket = sockets.get_mut::<raw::Socket>(handle);
1503+
assert!(!socket.can_recv());
1504+
}
1505+
1506+
// After the last fragment, the reassembled packet should be delivered.
1507+
assert_eq!(
1508+
iface.inner.process_ipv4(
1509+
&mut sockets,
1510+
PacketMeta::default(),
1511+
HardwareAddress::default(),
1512+
&frag2,
1513+
&mut iface.fragments
1514+
),
1515+
None
1516+
);
1517+
1518+
// Validate the raw socket received one defragmented packet with correct payload.
1519+
let socket = sockets.get_mut::<raw::Socket>(handle);
1520+
assert!(socket.can_recv());
1521+
let data = socket.recv().expect("raw socket should have a packet");
1522+
let packet = Ipv4Packet::new_unchecked(data);
1523+
let repr = Ipv4Repr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
1524+
assert_eq!(repr.src_addr, src_addr);
1525+
assert_eq!(repr.dst_addr, dst_addr);
1526+
assert_eq!(repr.next_header, proto);
1527+
assert_eq!(repr.payload_len, total_payload_len);
1528+
1529+
let payload = packet.payload();
1530+
assert_eq!(payload.len(), total_payload_len);
1531+
assert!(payload[..first_payload_len].iter().all(|&b| b == 0xAA));
1532+
assert!(payload[first_payload_len..].iter().all(|&b| b == 0xBB));
1533+
}
1534+
14121535
#[rstest]
14131536
#[case(Medium::Ip)]
14141537
#[cfg(all(feature = "socket-udp", feature = "medium-ip"))]

0 commit comments

Comments
 (0)