C - Writing structs to a file (.pcap)

KaiserJohaan picture KaiserJohaan · Sep 5, 2011 · Viewed 13.8k times · Source

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;

Answer

user862787 picture user862787 · Feb 17, 2012

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".)