问题
I've got this code which is working fine to write a pcap file (with just the Ethernet protocol for the test) with libpcap:
struct ethernet {
u_char mac1[6];
u_char mac2[6];
u_short protocol;
};
int main() {
pcap_t *pd;
pcap_dumper_t *pdumper;
pd = pcap_open_dead(DLT_EN10MB, 65535);
pdumper = pcap_dump_open(pd, "test.pcap");
struct pcap_pkthdr packet_header;
struct timeval ts;
packet_header.ts = ts;
packet_header.caplen = sizeof(struct ethernet);
packet_header.len = sizeof(struct ethernet);
struct ethernet ethernet;
bzero(ethernet.mac1, 6);
bzero(ethernet.mac2, 6);
ethernet.protocol = 8977; // randomly choose
pcap_dump((u_char*)pdumper, &packet_header, (const u_char*)ðernet);
pcap_close(pd);
pcap_dump_close(pdumper);
return 0;
}
But I wanted to try not using the pcap functions, so I started decorticating the functions used above.
Here are the sources I found:
pcap_open_dead function: http://www.wand.net.nz/trac/libtrace/browser/lib/pcap_open_dead.c?rev=808a478a2459f3cf0e8bf927fcaad371138efb20
pcap_dump_open, pcap_dump and others: http://www.opensource.apple.com/source/libpcap/libpcap-2.1/libpcap/savefile.c
So, here is my thought:
The pcap_open_dead function is kind of useless, just instanciating a pcap_t struct (which I don't want to use in my code) and filling its values with the parameters.
The pcap_dump_open, returning a pcap_dumper_t (which seems to be just like a FILE*), is just opening the file and writing the header in it (if we don't care about the errors handling). By the way, seems like its giving "p->tzoff" as parameter to sf_write_header, which has not been initialized in my code (and it's still working). About the linktype, in our case it's just equal to 1.
Finally, the pcap_dump function take the pcap_dumper_t variable as first parameter, which is implicitly cast to u_char* then explicitly cast to FILE* (why not use a FILE* from the beginning?) Then it take a packet header and a packet data to write them into the file with fwrite.
So here is what I did:
FILE *fd = fopen("test.pcap", "w");
struct pcap_file_header header;
header.magic = 0xa1b2c3d4;
header.version_major = 2;
header.version_minor = 4;
header.thiszone = 0;
header.sigfigs = 0;
header.snaplen = 65535;
header.linktype = DLT_EN10MB;
struct pcap_pkthdr packet_header;
struct timeval ts;
packet_header.ts = ts;
packet_header.caplen = sizeof(struct ethernet);
packet_header.len = sizeof(struct ethernet);
struct ethernet ethernet;
bzero(ethernet.mac1, 6);
bzero(ethernet.mac2, 6);
ethernet.protocol = 8977;
fwrite((char*)&header, sizeof(header), 1, fd);
fwrite((char*)&packet_header, sizeof(struct pcap_pkthdr), 1, fd);
fwrite((char*)ðernet, sizeof(struct ethernet), 1, fd);
close(fd);
No need to use a pcap_t structure, I put the snaplen and linktype values directly in the pcap_file_header structure.
And then I use fwrite the same way they do in the pcap functions.
The header file is good, i'm able to open the file with wireshark if I just write the header in the file. But when I add the 2 last fwrite (to write the packet header and the packet), wireshark tells me:
The capture file appears to be damaged or corrupt.
(pcap: File has 4195245-byte packet, bigger than maximum of 65535)
I can't find where is my mistake, and I don't know where he's seeing that much bytes.
EDIT
Instead of using an uninitialized timeval, I did :
packet_header.ts = (struct timeval){0};
And now that's working, but how do you explain it was working when using the pcap functions? "ts" was still uninitialized.
And what's the meaning of this timeval? Does it make sense to set it to zero?
EDIT: corrected magic number to 0xa1b2c3d4
回答1:
struct pcap_pkthdr packet_header;
That's the format of a packet header as it's provided to a program. It is not necessarily the format of a packet header as it's stored in the file; it contains a struct timeval
, whose size differs depending on whether a time_t
is 32 bits or 64 bits.
What you need is:
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) */
};
and use struct pcap_sf_pkthdr
instead. Writing struct pcap_pkthdr
structures to a file will produce invalid pcap files on any system where the tv_sec
or tv_usec
field of struct timeval
isn't 32 bits; that will probably be the case on any 64-bit system. In that case, you may well get errors like the "damaged or corrupt" error you saw.
As for what the meaning of the time stamp is, in an actual capture it represents the time at which the packet was seen by whatever part of the capture code path time stamped it, which is an approximation to the time at which the packet arrived at the machine capturing it. It's a UN*X time stamp, so tv_sec
is a count of seconds since January 1, 1970, 00:00:00 UTC, and tv_usec
is a count of microseconds since that second.
来源:https://stackoverflow.com/questions/33522705/how-to-write-pcap-file-from-scratch-decorticating-libpcap-functions