Skip to main content

netstack_testing_common/
pcap.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! PCAP parsing and validation helpers.
6
7use fuchsia_async as fasync;
8use net_types::ip::IpAddress;
9use netemul::RealmUdpSocket as _;
10use packet::ParsablePacket as _;
11use packet_formats::ip::IpExt;
12use pcap;
13
14/// Matcher for a UDP packet.
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub struct ExpectedUdpPacket<'a, A> {
17    /// Source IP address.
18    pub src_ip: A,
19    /// Destination IP address.
20    pub dst_ip: A,
21    /// Source UDP port.
22    pub src_port: u16,
23    /// Destination UDP port.
24    pub dst_port: u16,
25    /// Expected UDP payload.
26    pub payload: &'a [u8],
27}
28
29/// Asserts that the packets yielded by `packets_iter` match `expected_packets`.
30pub fn assert_udp_packets<A: IpAddress>(
31    packets_iter: pcap::PcapNgPacketIter<'_>,
32    expected_packets: &[ExpectedUdpPacket<'_, A>],
33    force_skip_checksum_validation: bool,
34) where
35    A::Version: IpExt,
36{
37    let actual_packets: Vec<ExpectedUdpPacket<'_, A>> = packets_iter
38        .map(|epb| {
39            let epb = epb.expect("EPB parse error");
40            assert_eq!(epb.interface_id, 0);
41            assert_eq!(usize::try_from(epb.original_length).unwrap(), epb.packet_data.len());
42            assert_eq!(usize::try_from(epb.captured_length).unwrap(), epb.packet_data.len());
43            let buf = &epb.packet_data;
44            let (mut body, _src_mac, _dst_mac, src_ip, dst_ip, proto, _ttl) =
45                packet_formats::testutil::parse_ip_packet_in_ethernet_frame::<A::Version>(
46                    &buf,
47                    packet_formats::ethernet::EthernetFrameLengthCheck::NoCheck,
48                )
49                .expect("failed to parse IP packet");
50
51            assert_eq!(proto, packet_formats::ip::IpProto::Udp.into());
52            let udp = packet_formats::udp::UdpPacket::parse(
53                &mut body,
54                packet_formats::udp::UdpParseArgs::with_context(
55                    src_ip,
56                    dst_ip,
57                    packet_formats::testutil::ForceSkipChecksumValidation(
58                        force_skip_checksum_validation,
59                    ),
60                ),
61            )
62            .expect("failed to parse UDP packet");
63
64            ExpectedUdpPacket {
65                src_ip,
66                dst_ip,
67                src_port: udp.src_port().expect("missing src_port").get(),
68                dst_port: udp.dst_port().get(),
69                payload: body,
70            }
71        })
72        .collect();
73
74    pretty_assertions::assert_eq!(&actual_packets[..], expected_packets);
75}
76
77/// Binds a UDP socket in `realm` to `bind_addr`, sends `payload` to `bind_addr` itself, and
78/// receives it.
79pub async fn send_udp_to_self_and_recv(
80    realm: &netemul::TestRealm<'_>,
81    bind_addr: std::net::SocketAddr,
82    payload: &[u8],
83) {
84    let sock =
85        fasync::net::UdpSocket::bind_in_realm(realm, bind_addr).await.expect("create socket");
86    let sent = sock.send_to(payload, bind_addr).await.expect("send_to failed");
87    assert_eq!(sent, payload.len());
88
89    let mut recv_buf = vec![0u8; payload.len()];
90    let (received, from_addr) = sock.recv_from(&mut recv_buf).await.expect("recv_from failed");
91    assert_eq!(received, payload.len());
92    assert_eq!(recv_buf.as_slice(), payload);
93    assert_eq!(from_addr, bind_addr);
94}