首页 > 解决方案 > 如何读取 tcpdump 生成的包含大型 UDP 数据包的 pcap 文件并重新组装 IP 分段数据包?

问题描述

我想读取一个由 tcpdump 生成的 pcap 文件,其中包含经历了IPV4 碎片的大型 UDP 数据包。原始数据包的大小约为 22000 字节。

在 C++ 中,我会使用 libtins 及其 IPV4Reassembler。有没有办法可以在 Rust 中做类似的事情?

目前在 Rust 中,这是我迄今为止所写的:一个高度不完整的首次尝试(使用 crate pnet):

use pnet::packet::{
    ethernet::{EtherTypes, EthernetPacket},
    ip::IpNextHeaderProtocols,
    ipv4::Ipv4Packet,
    udp::UdpPacket,
    Packet,
};

struct Ipv4Reassembler {
    cap: pcap::Capture<pcap::Offline>,
}

impl Iterator for Ipv4Reassembler {
    type Item = Vec<u8>;

    fn next(&mut self) -> Option<Self::Item> {
        let mut payload = Vec::<u8>::new();
        while let Some(packet) = self.cap.next().ok() {
            // todo: handle packets other than Ethernet packets
            let ethernet = EthernetPacket::new(packet.data).unwrap();
            match ethernet.get_ethertype() {
                EtherTypes::Ipv4 => {
                    let ipv4_packet = Ipv4Packet::new(ethernet.payload()).unwrap();
                    // dbg!(&ipv4_packet);
                    // todo: discard incomplete packets
                    // todo: construct header for reassembled packet
                    // todo: check id, etc
                    let off: usize = 8 * ipv4_packet.get_fragment_offset() as usize;
                    let end = off + ipv4_packet.payload().len();
                    if payload.len() < end {
                        payload.resize(end, 0);
                    }
                    payload[off..end].clone_from_slice(ipv4_packet.payload());
                    if ipv4_packet.get_flags() & 1 == 0 {
                        return Some(payload);
                    }
                }
                _ => {}
            }
        }
        None
    }
}

fn main() {
    let pcap_path = "os-992114000702.pcap";

    let reass = Ipv4Reassembler {
        cap: pcap::Capture::from_file(&pcap_path).unwrap(),
    };

    for payload in reass {
        let udp_packet = UdpPacket::new(&payload).unwrap();
        dbg!(&udp_packet);
        dbg!(&udp_packet.payload().len());
    }
}

在 C++ 中,这是我将使用的代码(使用 libtins):

#include <tins/ip_reassembler.h>
#include <tins/packet.h>
#include <tins/rawpdu.h>
#include <tins/sniffer.h>
#include <tins/tins.h>
#include <tins/udp.h>

#include <iostream>
#include <string>

void read_packets(const std::string &pcap_filename) {
    Tins::IPv4Reassembler reassembler;
    Tins::FileSniffer sniffer(pcap_filename);

    while (Tins::Packet packet = sniffer.next_packet()) {
        auto &pdu = *packet.pdu();
        const Tins::Timestamp &timestamp = packet.timestamp();
        if (reassembler.process(pdu) != Tins::IPv4Reassembler::FRAGMENTED) {
            const Tins::UDP *udp = pdu.find_pdu<Tins::UDP>();
            if (!udp) {
                continue;
            }
            const Tins::RawPDU *raw = pdu.find_pdu<Tins::RawPDU>();
            if (!raw) {
                continue;
            }
            const Tins::RawPDU::payload_type &payload = raw->payload();
            std::cout << "Packet: " << payload.size() << std::endl;
            // do something with the reassembled packet here
        }
    }
}

int main() {
    const std::string pcap_path = "os-992114000702.pcap";
    read_packets(pcap_path);
}
g++ -O3 -o pcap pcap.cpp -ltins

似乎一种解决方案是实现RFC815,但我不确定如何在 Rust 中做到这一点。我已经找到:

标签: networkingrustudpipip-fragmentation

解决方案


推荐阅读