Skip to content

Commit 4d383dc

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

File tree

2 files changed

+151
-5
lines changed

2 files changed

+151
-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: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,6 +1183,152 @@ 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+
fn build_fragment(
1228+
src: Ipv4Address,
1229+
dst: Ipv4Address,
1230+
proto: IpProtocol,
1231+
ident: u16,
1232+
payload_len: usize,
1233+
more_frags: bool,
1234+
frag_offset_octets: u16,
1235+
payload_byte: u8,
1236+
) -> Vec<u8> {
1237+
let repr = Ipv4Repr {
1238+
src_addr: src,
1239+
dst_addr: dst,
1240+
next_header: proto,
1241+
hop_limit: 64,
1242+
payload_len,
1243+
};
1244+
let header_len = repr.buffer_len();
1245+
let mut bytes = vec![0u8; header_len + payload_len];
1246+
{
1247+
let mut pkt = Ipv4Packet::new_unchecked(&mut bytes[..]);
1248+
repr.emit(&mut pkt, &ChecksumCapabilities::default());
1249+
pkt.set_ident(ident);
1250+
pkt.set_dont_frag(false);
1251+
pkt.set_more_frags(more_frags);
1252+
pkt.set_frag_offset(frag_offset_octets);
1253+
// Recompute checksum after changing fragmentation fields.
1254+
pkt.fill_checksum();
1255+
}
1256+
// Fill payload with a simple pattern for validation
1257+
for b in &mut bytes[header_len..] {
1258+
*b = payload_byte;
1259+
}
1260+
bytes
1261+
}
1262+
1263+
let frag1_bytes = build_fragment(
1264+
src_addr,
1265+
dst_addr,
1266+
proto,
1267+
ident,
1268+
first_payload_len,
1269+
true,
1270+
0,
1271+
0xAA,
1272+
);
1273+
let frag2_bytes = build_fragment(
1274+
src_addr,
1275+
dst_addr,
1276+
proto,
1277+
ident,
1278+
last_payload_len,
1279+
false,
1280+
first_payload_len as u16,
1281+
0xBB,
1282+
);
1283+
1284+
let frag1 = Ipv4Packet::new_unchecked(&frag1_bytes[..]);
1285+
let frag2 = Ipv4Packet::new_unchecked(&frag2_bytes[..]);
1286+
1287+
// First fragment alone should not be delivered to the raw socket.
1288+
assert_eq!(
1289+
iface.inner.process_ipv4(
1290+
&mut sockets,
1291+
PacketMeta::default(),
1292+
HardwareAddress::default(),
1293+
&frag1,
1294+
&mut iface.fragments
1295+
),
1296+
None
1297+
);
1298+
{
1299+
let socket = sockets.get_mut::<raw::Socket>(handle);
1300+
assert!(!socket.can_recv());
1301+
}
1302+
1303+
// After the last fragment, the reassembled packet should be delivered.
1304+
assert_eq!(
1305+
iface.inner.process_ipv4(
1306+
&mut sockets,
1307+
PacketMeta::default(),
1308+
HardwareAddress::default(),
1309+
&frag2,
1310+
&mut iface.fragments
1311+
),
1312+
None
1313+
);
1314+
1315+
// Validate the raw socket received one defragmented packet with correct payload.
1316+
let socket = sockets.get_mut::<raw::Socket>(handle);
1317+
assert!(socket.can_recv());
1318+
let data = socket.recv().expect("raw socket should have a packet");
1319+
let packet = Ipv4Packet::new_unchecked(data);
1320+
let repr = Ipv4Repr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
1321+
assert_eq!(repr.src_addr, src_addr);
1322+
assert_eq!(repr.dst_addr, dst_addr);
1323+
assert_eq!(repr.next_header, proto);
1324+
assert_eq!(repr.payload_len, total_payload_len);
1325+
1326+
let payload = packet.payload();
1327+
assert_eq!(payload.len(), total_payload_len);
1328+
assert!(payload[..first_payload_len].iter().all(|&b| b == 0xAA));
1329+
assert!(payload[first_payload_len..].iter().all(|&b| b == 0xBB));
1330+
}
1331+
11861332
#[rstest]
11871333
#[case(Medium::Ip)]
11881334
#[cfg(all(feature = "socket-udp", feature = "medium-ip"))]

0 commit comments

Comments
 (0)