libpnet icon indicating copy to clipboard operation
libpnet copied to clipboard

Reassembling fragmented packets

Open dllu opened this issue 4 years ago • 1 comments

Hi I would like to reassemble packets that have undergone IPV4 fragmentation. I searched around and couldn't find anything. Is this something that libpnet would be interested in having?

For example, some code may look like this janky first attempt:

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() {
            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, construct header for reassembled packet, 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());
    }
}

For reference, for C++'s libtins library it is pretty painless:

#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;
        }
    }
}

int main() {
    const std::string pcap_path = "os-992114000702.pcap";
    read_packets(pcap_path);
}

dllu avatar Jun 17 '21 18:06 dllu

Fuschia's recently open sourced netstack3 also has an implementation for packet reassembly which has a BSD-like license. Perhaps that can be integrated into pnet?

dllu avatar Jul 12 '21 17:07 dllu