10

I am trying to write a .pcap file, which is something that can be used in Wireshark. In order to do that, I have a couple of structs with various data types I need to write to a file. (see code)

So, I create the struct instances, fill in the data, use FILE* fp = fopen("test.pcap","w"), and then I'm unsure how to properly write it to the file. I believe I should use memcpy but I'm not sure of the best way to do it. I have mostly resorted to C++ libraries in the past to do this. Any suggestions?

typedef struct pcap_hdr_s {
        uint32_t magic_number;   /* magic number */
        uint16_t version_major;  /* major version number */
        uint16_t version_minor;  /* minor version number */
        int32_t  thiszone;       /* GMT to local correction */
        uint32_t sigfigs;        /* accuracy of timestamps */
        uint32_t snaplen;        /* max length of captured packets, in octets */
        uint32_t network;        /* data link type */
} pcap_hdr_t;

typedef struct pcaprec_hdr_s {
   uint32_t ts_sec;         /* timestamp seconds */
   uint32_t ts_usec;        /* timestamp microseconds */
   uint32_t incl_len;       /* number of octets of packet saved in file */
   uint32_t orig_len;       /* actual length of packet */
} pcaprec_hdr_t;

typedef struct ethernet_hdr_s {
   uint8_t dst[6];    /* destination host address */
   uint8_t src[6];    /* source host address */
   uint16_t type;     /* IP? ARP? RARP? etc */
} ethernet_hdr_t;

typedef struct ip_hdr_s {
   uint8_t  ip_hl:4, /* both fields are 4 bits */
            ip_v:4;
   uint8_t        ip_tos;
   uint16_t       ip_len;
   uint16_t       ip_id;
   uint16_t       ip_off;
   uint8_t        ip_ttl;
   uint8_t        ip_p;
   uint16_t       ip_sum;
   uint32_t ip_src;
   uint32_t ip_dst;
}ip_hdr_t;

typedef struct udp_header
{
  uint16_t src;
  uint16_t dst;
  uint16_t length;
  uint16_t checksum;
} udp_header_t;
KaiserJohaan
  • 9,028
  • 20
  • 112
  • 199
  • Be careful with byte order -- I think the captured packets always use network byte order, but you might need to check the byte order of the header. Maybe not a concern, if the file format uses the `magic_number` field to determine the byte order of the header. – tomlogic Sep 05 '11 at 23:43
  • The magic number lets you write in either big- or little-endian, you can even switch back and forth after every packet if you want. The magic number also alerts you to damage caused by improperly transferring the file. – Seth Feb 08 '12 at 17:12
  • 1
    The magic number does *not* let you write out the packet *data* in big-endian or little-endian order; you have to write that out in the order in which it would appear on the wire, which, for Ethernet, IP, TCP, and UDP multi-byte integral fields, is big-endian. It *does* let you write out the fields in the file and record header in your native byte order, but you might as well just let libpcap/WinPcap do that work. And, sadly, the magic number is not affected by, for example, FTPing the file in ASCII mode between Windows and UN\*X. –  Feb 17 '12 at 08:54

3 Answers3

16

Use libpcap or WinPcap - pcap_open_dead() to get a "fake" pcap_t to use with pcap_dump_open() to specify the link-layer header type (for Ethernet, use DLT_EN10MB) and snapshot length (use 65535), pcap_dump_open() to open the file for writing, pcap_dump() to write out a packet, and pcap_dump_close() to close the file. MUCH easier than directly using fopen(), fwrite(), and fclose() (which are what libpcap/WinPcap use "under the hood").

And, yes, you have to get the byte order in the packets correct. The byte order depends on the protocol; for the type field in the Ethernet header, and for all multi-byte fields in IP, TCP, and UDP headers, they have to be in big-endian order. (The magic number in the pcap file is irrelevant to this - it only indicates the byte order of the fields in the file header and the per-packet record header, NOT the byte order of the fields in the packet, as well as, due to the way it's implemented in Linux, the meta-data at the beginning of packets in Linux USB captures. The packet data is supposed to look exactly as it would "on the wire".)

4

Here's my understanding of what Guy Harris is suggesting. So, as per Kyslik's request, we have:

#include <libpcap/pcap.h>

/* Ethernet/IP/SCTP INIT chunk */
static const unsigned char pkt1[82] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ........ */
  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, /* ......E. */
  0x00, 0x44, 0x55, 0xb1, 0x00, 0x00, 0x40, 0x84, /* .DU...@. */
  0x26, 0x83, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, /* &....... */
  0x00, 0x01, 0x00, 0x01, 0x1f, 0x90, 0x00, 0x00, /* ........ */
  0x00, 0x00, 0x68, 0xe5, 0x88, 0x1b, 0x01, 0x00, /* ..h..... */
  0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, /* .$...... */
  0xa0, 0x00, 0x00, 0x04, 0xff, 0xff, 0x00, 0x00, /* ........ */
  0x16, 0x2e, 0x80, 0x00, 0x00, 0x04, 0xc0, 0x00, /* ........ */
  0x00, 0x04, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x05, /* ........ */
  0x00, 0x00                                      /* .. */
};


int main(int argc, char *argv[]) {

  pcap_t *handle = pcap_open_dead(DLT_EN10MB, 1 << 16);
  pcap_dumper_t *dumper = pcap_dump_open(handle, "/tmp/pktcap/cap.pcap");

  struct pcap_pkthdr pcap_hdr;
  pcap_hdr.caplen = sizeof(pkt1);
  pcap_hdr.len = pcap_hdr.caplen;

  pcap_dump((u_char *)dumper, &pcap_hdr, pkt1);
  pcap_dump_close(dumper);

  return 0;
}
elhadi dp ıpɐɥןǝ
  • 4,763
  • 2
  • 30
  • 34
Asblarf
  • 483
  • 1
  • 4
  • 14
  • I had to modify `pcap_pkthdr pcap_hdr;` to `struct pcap_pkthdr pcap_hdr;` to make this compile with `gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609` – Jaakko Jan 18 '17 at 10:37
4

Use fwrite(). You need to check this info but I think .pcap files are written in binary mode.

Example:

pcaprec_hdr_t pcaprec_hdr;
// fill pcaprec_hdr with valid info

FILE* pFile = NULL;
pFile = fopen ("myfile.pcap" , "wb"); // open for writing in binary mode

fwrite (&pcaprec_hdr, 1, sizeof(pcaprec_hdr_t) , pFile);

fclose(pFile);
karlphillip
  • 92,053
  • 36
  • 243
  • 426
  • 1
    -1 for forgetting to write out the file header and for not just saying "use libpcap/WinPcap, they already know how to write out the file header and the record header". –  Feb 17 '12 at 08:52
  • *You assume* they already know. But do they? Getting this answer selected as the official demonstrates something else. – karlphillip Feb 17 '12 at 12:06
  • 2
    "You assume they already know. But do they?" Yes, they do. They were the first implementation of the file format ever. –  Mar 28 '13 at 00:02
  • I am having troubles with this, can you edit your answer with "full" example? When I open my pcap file in wireshark I get error saying `The capture file appears to have been cut short in the middle of a packet.` And I can see only "frame" row. Thanks. – Kyslik Apr 02 '14 at 17:11