1
votes

I have a pcap::Packet and would like to write it to file without the pcap file header and add the file header later in Python. I know about pcap::Savefile but unfortunately I cannot use this because it auto writes the pcap file header.

How the pcap crate writes the Packet

Description of the pcap data format

I have tried something like

extern crate pcap;

use std::{fs::OpenOptions, io::Write, mem, slice};

const DLT_IEEE802_11_RADIO: i32 = 127;
const SNAPLEN: i32 = 4096;

unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
    slice::from_raw_parts((p as *const T) as *const u8, mem::size_of::<T>())
}

fn main() {
    let mut capture = pcap::Capture::from_device(pcap::Device::lookup().unwrap())
        .unwrap()
        .timeout(1)
        .rfmon(true)
        .snaplen(SNAPLEN)
        .open()
        .unwrap();

    capture
        .set_datalink(pcap::Linktype(DLT_IEEE802_11_RADIO))
        .unwrap();

    let mut temp = OpenOptions::new()
        .create(true)
        .append(true)
        .open("temp.rawpcap")
        .unwrap();

    let mut count = 0;
    while count < 10 {
        match capture.next() {
            Ok(packet) => {
                count += 1;
                unsafe {
                    temp.write_all(any_as_u8_slice(packet.header)).unwrap();
                }
                temp.write_all(&packet.data).unwrap();
            }
            Err(pcap::Error::TimeoutExpired) => continue,
            Err(e) => {
                panic!("unhandled error: {:?}", e);
            }
        }
    }
}

And am adding the header with

import struct

DLT_IEEE802_11_RADIO = 127
SNAPLEN = 4096

pcap_file_header = struct.pack('IHHiIII', 0xa1b2c3d4, 0x2, 0x4, 0, 0, SNAPLEN, DLT_IEEE802_11_RADIO)

with open('temp.rawpcap', 'rb') as f:
    data = f.read()

with open('temp.pcap', 'wb') as f:
    f.write(pcap_file_header + data)

When I open the result .pcap file in Wireshark I get

The capture file appears to be damaged or corrupt.
(pcap: File has 560197-byte packet, bigger than maximum of 262144)

Here is a hexdump of each file (1 packet taken at a SNAPLEN of 256):

$ hexdump -n 56 temp.rawpcap
0000000 d4 c5 8e 5b 00 00 00 00 43 78 02 00 00 00 00 00
0000010 00 01 00 00 50 01 00 00 14 a0 2e 09 01 00 00 00
0000020

$ hexdump -n 56 temp.pcap
0000000 d4 c3 b2 a1 02 00 04 00 00 00 00 00 00 00 00 00
0000010 00 01 00 00 7f 00 00 00 d4 c5 8e 5b 00 00 00 00
0000020 43 78 02 00 00 00 00 00 00 01 00 00 50 01 00 00
0000030 14 a0 2e 09 01 00 00 00
0000038
1
Could you add the output of hexdump -n 56 temp.rawpcap to your post? It should display the first few bytes (56) of your .pcap file and will make debugging much easier for anyone reading your question.pchaigno

1 Answers

3
votes

According to the pcap data file specification, the timestamp consists of two 32-bit values, but pcap::PacketHeader uses a timeval, which consists of two 64-bit values.

You can't write the header as raw, you will need to write its fields by hand:

temp.write_all(any_as_u8_slice(&(packet.header.ts.tv_sec as u32))).unwrap();
temp.write_all(any_as_u8_slice(&(packet.header.ts.tv_usec as u32))).unwrap();
temp.write_all(any_as_u8_slice(&packet.header.caplen)).unwrap();
temp.write_all(any_as_u8_slice(&packet.header.len)).unwrap();

Since you're not specifying byte-orders anywhere, you will also need to make sure that you run your Python script on a machine with the same endianness as the machine that runs the Rust code.