Reputation: 41
I'm trying to parse a pcap file in C. I don't want to use libpcap. But for some reason, I'm unable to. Do you know how can I do this ? Here is my attempt :
fseek(f,24,0);
while(count<20)//reading 20 packets
{
fread(header,sizeof(struct pcap_pkthdr),1,f);
//after this I'm printing values header fields
fseek(f,ntohl(header->caplen),1);
count++;
}
Output is not the same as libpcap.
Upvotes: 1
Views: 2289
Reputation:
struct pcap_pkthdr
is NOT the structure that defines the format of packet headers in a pcap file; it's the structure that defines the format of packet headers as provided to programs using libpcap or WinPcap.
The structure that defines the format of packet headers in a pcap file is NOT in any of the pcap include files, because libpcap/WinPcap provides routines that read those headers and transforms them as necessary to pcap_pkthdr
headers. Unlike struct pcap_pkthdr
, the time stamp in packet headers in a pcap file always have a 32-bit "seconds" field and a 32-bit "microseconds" field, even on systems where time_t
and the tv_sec
value in a struct timeval
are 64 bits.
I.e., the structure is defined by
struct pcap_timeval {
bpf_int32 tv_sec; /* seconds */
bpf_int32 tv_usec; /* microseconds */
};
struct pcap_sf_pkthdr {
struct pcap_timeval ts; /* time stamp */
bpf_u_int32 caplen; /* length of portion present */
bpf_u_int32 len; /* length this packet (off wire) */
};
where struct pcap_sf_pkthdr
is the structure in the file.
Note also that, if you read a struct pcap_sf_pkthdr
from a file, you will have to byte-swap ts.tv_sec
, ts.tv_usec
, caplen
, and len
IF the file was written on a machine whose byte order differs from the machine on which you're reading the file. Something as simple as ntohl()
will NOT work - if, for example, your file was written on the same machine as the one on which you are reading it, no byte-swapping is necessary.
The ONLY way to make that work would be to READ the file header, rather than just skipping over it with fseek()
. If the "magic number" in the file header has the value 0xa1b2c3d4
, then you do not need to do any byte swapping; if it has the value 0xd4c3b2a1
, you will need to byte-swap the values in the file header and the struct pcap_sf_pkthdr
. Note that ntohl()
and htonl()
will NOT byte-swap if you're running on a big-endian machine such as a PowerPC/Power Architecture machine or a MIPS machine or an IBM mainframe or....
Note also that not all capture files are going to be pcap files; if they're pcap-NG files, they have to be read in a completely different fashion. Libpcap 1.1 and later know how to read pcap-NG files (the libpcap/WinPcap API isn't powerful enough to handle all pcap-NG files, but it can, for example, handle pcap-NG files that only have one section and only have packets from one network adapter, and libpcap 1.1 and later can read those). I would suggest, just as unwind suggested, that you use libpcap/WinPcap to read capture files, rather than writing your own code to do it.
Upvotes: 4
Reputation: 400069
Make sure that you are correctly handling the endianness of the pcap file format.
Also, directly loading entire structures from disk is rarely safe, since compilers are free to insert padding between structure fields, which will make the bytes from disk not match the bytes in memory.
I would suggest you use the official library, since then those problems will already have been taken care of.
Upvotes: 1
Reputation: 796
Try something like that:
struct dump_pcap_pkthdr {
struct timeval ts; /* time stamp */
unsigned int caplen; /* length of portion present */
unsigned int len; /* length this packet (off wire) */
};
struct dump_pcap_file_header {
unsigned int magic;
unsigned short version_major;
unsigned short version_minor;
int thiszone; /* gmt to local correction */
unsigned int sigfigs; /* accuracy of timestamps */
unsigned int snaplen; /* max length saved portion of each pkt */
unsigned int linktype; /* data link type (LINKTYPE_*) */
};
static void read_pcap_file(char *file)
{
int fd = -1;
struct dump_pcap_pkthdr packet_header = {0};
struct dump_pcap_file_header pcap_header = {0};
fd = open(file, O_RDONLY);
if(fd < 0) {
printf("Fail open file: %s\n", file);
return;
}
if(read(fd, &pcap_header, sizeof(pcap_header)) != sizeof(pcap_header)) {
printf("Failed to read TCPDump Header from file: %s\n", file);
return;
}
while(1)
{
r = read(fd, &packet_header, sizeof(packet_header));
if(r != sizeof(packet_header))
break;
//print data
lseek(fd, packet_header.caplen, SEEK_CUR);
}
close(fd);
}
Upvotes: 0