2 * Routines for opening EtherPeek and AiroPeek (and TokenPeek?) V5, V6,
4 * Copyright (c) 2001, Daniel Thompson <d.thompson@gmx.net>
9 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 #include "file_wrappers.h"
34 #include "etherpeek.h"
37 * This file decoder could not have been writen without examining how
38 * tcptrace (http://www.tcptrace.org/) handles EtherPeek files.
42 * NOTE: it says "etherpeek" because the first files seen that use this
43 * format were EtherPeek files; however, AiroPeek files using it have
44 * also been seen, and I suspect TokenPeek uses it as well.
48 typedef struct etherpeek_master_header {
51 } etherpeek_master_header_t;
52 #define ETHERPEEK_MASTER_HDR_SIZE 2
54 /* secondary header (V5,V6,V7) */
55 typedef struct etherpeek_v567_header {
61 guint32 mediaType; /* Media Type Ethernet=0 Token Ring = 1 */
62 guint32 physMedium; /* Physical Medium native=0 802.1=1 */
63 guint32 appVers; /* App Version Number Maj.Min.Bug.Build */
64 guint32 linkSpeed; /* Link Speed Bits/sec */
66 } etherpeek_v567_header_t;
67 #define ETHERPEEK_V567_HDR_SIZE 48
70 typedef struct etherpeek_header {
71 etherpeek_master_header_t master;
73 etherpeek_v567_header_t v567;
78 * Packet header (V5, V6).
80 * NOTE: the time stamp, although it's a 32-bit number, is only aligned
81 * on a 16-bit boundary. (Does this date back to 68K Macs? The 68000
82 * only required 16-bit alignment of 32-bit quantities, as did the 68010,
83 * and the 68020/68030/68040 required no alignment.)
85 * As such, we cannot declare this as a C structure, as compilers on
86 * most platforms will put 2 bytes of padding before the time stamp to
87 * align it on a 32-bit boundary.
89 * So, instead, we #define numbers as the offsets of the fields.
91 #define ETHERPEEK_V56_LENGTH_OFFSET 0
92 #define ETHERPEEK_V56_SLICE_LENGTH_OFFSET 2
93 #define ETHERPEEK_V56_FLAGS_OFFSET 4
94 #define ETHERPEEK_V56_STATUS_OFFSET 5
95 #define ETHERPEEK_V56_TIMESTAMP_OFFSET 6
96 #define ETHERPEEK_V56_DESTNUM_OFFSET 10
97 #define ETHERPEEK_V56_SRCNUM_OFFSET 12
98 #define ETHERPEEK_V56_PROTONUM_OFFSET 14
99 #define ETHERPEEK_V56_PROTOSTR_OFFSET 16
100 #define ETHERPEEK_V56_FILTERNUM_OFFSET 24
101 #define ETHERPEEK_V56_PKT_SIZE 26
103 /* 64-bit time in micro seconds from the (Mac) epoch */
104 typedef struct etherpeek_utime {
110 * Packet header (V7).
112 * This doesn't have the same alignment problem, but we do it with
115 #define ETHERPEEK_V7_PROTONUM_OFFSET 0
116 #define ETHERPEEK_V7_LENGTH_OFFSET 2
117 #define ETHERPEEK_V7_SLICE_LENGTH_OFFSET 4
118 #define ETHERPEEK_V7_FLAGS_OFFSET 6
119 #define ETHERPEEK_V7_STATUS_OFFSET 7
120 #define ETHERPEEK_V7_TIMESTAMP_OFFSET 8
121 #define ETHERPEEK_V7_PKT_SIZE 16
124 * AiroPeek radio information, at the beginning of every packet.
131 } airopeek_radio_hdr_t;
133 typedef struct etherpeek_encap_lookup {
136 } etherpeek_encap_lookup_t;
138 static const unsigned int mac2unix = 2082844800u;
139 static const etherpeek_encap_lookup_t etherpeek_encap[] = {
140 { 1400, WTAP_ENCAP_ETHERNET }
142 #define NUM_ETHERPEEK_ENCAPS \
143 (sizeof (etherpeek_encap) / sizeof (etherpeek_encap[0]))
145 static gboolean etherpeek_read_v7(wtap *wth, int *err, gchar **err_info,
146 gint64 *data_offset);
147 static gboolean etherpeek_seek_read_v7(wtap *wth, gint64 seek_off,
148 union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
149 int *err, gchar **err_info);
150 static void etherpeek_fill_pseudo_header_v7(
151 union wtap_pseudo_header *pseudo_header, airopeek_radio_hdr_t *radio_hdr);
152 static gboolean etherpeek_read_v56(wtap *wth, int *err, gchar **err_info,
153 gint64 *data_offset);
154 static gboolean etherpeek_seek_read_v56(wtap *wth, gint64 seek_off,
155 union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
156 int *err, gchar **err_info);
157 static void etherpeek_close(wtap *wth);
159 int etherpeek_open(wtap *wth, int *err, gchar **err_info _U_)
161 etherpeek_header_t ep_hdr;
162 struct timeval reference_time;
165 /* EtherPeek files do not start with a magic value large enough
166 * to be unique; hence we use the following algorithm to determine
167 * the type of an unknown file:
168 * - populate the master header and reject file if there is no match
169 * - populate the secondary header and check that the reserved space
170 * is zero, and check some other fields; this isn't perfect,
171 * and we may have to add more checks at some point.
173 g_assert(sizeof(ep_hdr.master) == ETHERPEEK_MASTER_HDR_SIZE);
174 wtap_file_read_unknown_bytes(
175 &ep_hdr.master, sizeof(ep_hdr.master), wth->fh, err);
176 wth->data_offset += sizeof(ep_hdr.master);
179 * It appears that EtherHelp (a free application from WildPackets
180 * that did blind capture, saving to a file, so that you could
181 * give the resulting file to somebody with EtherPeek) saved
182 * captures in EtherPeek format except that it ORed the 0x80
183 * bit on in the version number.
185 * We therefore strip off the 0x80 bit in the version number.
186 * Perhaps there's some reason to care whether the capture
187 * came from EtherHelp; if we discover one, we should check
190 ep_hdr.master.version &= ~0x80;
192 /* switch on the file version */
193 switch (ep_hdr.master.version) {
198 /* get the secondary header */
199 g_assert(sizeof(ep_hdr.secondary.v567) ==
200 ETHERPEEK_V567_HDR_SIZE);
201 wtap_file_read_unknown_bytes(
202 &ep_hdr.secondary.v567,
203 sizeof(ep_hdr.secondary.v567), wth->fh, err);
204 wth->data_offset += sizeof(ep_hdr.secondary.v567);
206 if ((0 != ep_hdr.secondary.v567.reserved[0]) ||
207 (0 != ep_hdr.secondary.v567.reserved[1]) ||
208 (0 != ep_hdr.secondary.v567.reserved[2])) {
214 * Check the mediaType and physMedium fields.
215 * We assume it's not an EtherPeek/TokenPeek/AiroPeek
216 * file if these aren't values we know, rather than
217 * reporting them as invalid *Peek files, as, given
218 * the lack of a magic number, we need all the checks
221 ep_hdr.secondary.v567.mediaType =
222 g_ntohl(ep_hdr.secondary.v567.mediaType);
223 ep_hdr.secondary.v567.physMedium =
224 g_ntohl(ep_hdr.secondary.v567.physMedium);
226 switch (ep_hdr.secondary.v567.physMedium) {
230 * "Native" format, presumably meaning
231 * Ethernet or Token Ring.
233 switch (ep_hdr.secondary.v567.mediaType) {
236 file_encap = WTAP_ENCAP_ETHERNET;
240 file_encap = WTAP_ENCAP_TOKEN_RING;
245 * Assume this isn't a *Peek file.
252 switch (ep_hdr.secondary.v567.mediaType) {
256 * 802.11, with a private header giving
257 * some radio information. Presumably
258 * this is from AiroPeek.
260 * We supply the private header as
261 * the WTAP_ENCAP_IEEE_802_11_WITH_RADIO
262 * pseudo-header, rather than as frame
265 file_encap = WTAP_ENCAP_IEEE_802_11_WITH_RADIO;
270 * Assume this isn't a *Peek file.
278 * Assume this isn't a *Peek file.
285 * Assume this is a V5, V6 or V7 *Peek file, and byte
286 * swap the rest of the fields in the secondary header.
288 * XXX - we could check the file length if the file were
289 * uncompressed, but it might be compressed.
291 ep_hdr.secondary.v567.filelength =
292 g_ntohl(ep_hdr.secondary.v567.filelength);
293 ep_hdr.secondary.v567.numPackets =
294 g_ntohl(ep_hdr.secondary.v567.numPackets);
295 ep_hdr.secondary.v567.timeDate =
296 g_ntohl(ep_hdr.secondary.v567.timeDate);
297 ep_hdr.secondary.v567.timeStart =
298 g_ntohl(ep_hdr.secondary.v567.timeStart);
299 ep_hdr.secondary.v567.timeStop =
300 g_ntohl(ep_hdr.secondary.v567.timeStop);
301 ep_hdr.secondary.v567.appVers =
302 g_ntohl(ep_hdr.secondary.v567.appVers);
303 ep_hdr.secondary.v567.linkSpeed =
304 g_ntohl(ep_hdr.secondary.v567.linkSpeed);
306 /* Get the reference time as a "struct timeval" */
307 reference_time.tv_sec =
308 ep_hdr.secondary.v567.timeDate - mac2unix;
309 reference_time.tv_usec = 0;
314 * Assume this isn't a *Peek file.
320 * This is an EtherPeek (or TokenPeek or AiroPeek?) file.
322 * At this point we have recognised the file type and have populated
323 * the whole ep_hdr structure in host byte order.
325 wth->capture.etherpeek = g_malloc(sizeof(etherpeek_t));
326 wth->capture.etherpeek->reference_time = reference_time;
327 wth->subtype_close = etherpeek_close;
328 switch (ep_hdr.master.version) {
332 wth->file_type = WTAP_FILE_ETHERPEEK_V56;
334 * XXX - can we get the file encapsulation from the
335 * header in the same way we do for V7 files?
337 wth->file_encap = WTAP_ENCAP_PER_PACKET;
338 wth->subtype_read = etherpeek_read_v56;
339 wth->subtype_seek_read = etherpeek_seek_read_v56;
343 wth->file_type = WTAP_FILE_ETHERPEEK_V7;
344 wth->file_encap = file_encap;
345 wth->subtype_read = etherpeek_read_v7;
346 wth->subtype_seek_read = etherpeek_seek_read_v7;
350 /* this is impossible */
351 g_assert_not_reached();
354 wth->snapshot_length = 0; /* not available in header */
355 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
360 static void etherpeek_close(wtap *wth)
362 g_free(wth->capture.etherpeek);
365 static gboolean etherpeek_read_v7(wtap *wth, int *err, gchar **err_info,
368 guchar ep_pkt[ETHERPEEK_V7_PKT_SIZE];
377 airopeek_radio_hdr_t radio_hdr;
379 *data_offset = wth->data_offset;
381 wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->fh, err);
382 wth->data_offset += sizeof(ep_pkt);
384 /* Extract the fields from the packet */
385 protoNum = pntohs(&ep_pkt[ETHERPEEK_V7_PROTONUM_OFFSET]);
386 length = pntohs(&ep_pkt[ETHERPEEK_V7_LENGTH_OFFSET]);
387 sliceLength = pntohs(&ep_pkt[ETHERPEEK_V7_SLICE_LENGTH_OFFSET]);
388 flags = ep_pkt[ETHERPEEK_V7_FLAGS_OFFSET];
389 status = ep_pkt[ETHERPEEK_V7_STATUS_OFFSET];
390 timestamp = pntohll(&ep_pkt[ETHERPEEK_V7_TIMESTAMP_OFFSET]);
392 /* force sliceLength to be the actual length of the packet */
393 if (0 == sliceLength) {
394 sliceLength = length;
397 /* fill in packet header length values before slicelength may be
399 wth->phdr.len = length;
400 wth->phdr.caplen = sliceLength;
402 if (sliceLength % 2) /* packets are padded to an even length */
405 switch (wth->file_encap) {
407 case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
409 * The first 4 bytes of the packet data are radio
410 * information (including a reserved byte).
412 if (sliceLength < 4) {
414 * We don't *have* 4 bytes of packet data.
416 *err = WTAP_ERR_BAD_RECORD;
417 *err_info = g_strdup("etherpeek: packet not long enough for 802.11 radio header");
420 wtap_file_read_expected_bytes(&radio_hdr, 4, wth->fh, err);
423 * We don't treat the radio information as packet data.
427 wth->phdr.caplen -= 4;
428 wth->data_offset += 4;
430 etherpeek_fill_pseudo_header_v7(&wth->pseudo_header,
434 case WTAP_ENCAP_ETHERNET:
435 /* XXX - it appears that if the low-order bit of
436 "status" is 0, there's an FCS in this frame,
437 and if it's 1, there's 4 bytes of 0. */
438 wth->pseudo_header.eth.fcs_len = (status & 0x01) ? 0 : 4;
442 /* read the frame data */
443 buffer_assure_space(wth->frame_buffer, sliceLength);
444 wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
445 sliceLength, wth->fh, err);
446 wth->data_offset += sliceLength;
448 /* fill in packet header values */
449 tsecs = (time_t) (timestamp/1000000);
450 tusecs = (guint32) (timestamp - tsecs*1000000);
451 wth->phdr.ts.secs = tsecs - mac2unix;
452 wth->phdr.ts.nsecs = tusecs * 1000;
454 if (wth->file_encap == WTAP_ENCAP_IEEE_802_11_WITH_RADIO) {
456 * The last 4 bytes appear to be random data - the length
457 * might include the FCS - so we reduce the length by 4.
459 * Or maybe this is just the same kind of random 4 bytes
460 * of junk at the end you get in Wireless Sniffer
464 wth->phdr.caplen -= 4;
471 etherpeek_seek_read_v7(wtap *wth, gint64 seek_off,
472 union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
473 int *err, gchar **err_info)
475 guchar ep_pkt[ETHERPEEK_V7_PKT_SIZE];
477 airopeek_radio_hdr_t radio_hdr;
479 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
482 /* Read the packet header. */
483 wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->random_fh,
485 status = ep_pkt[ETHERPEEK_V7_STATUS_OFFSET];
487 switch (wth->file_encap) {
489 case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
491 * The first 4 bytes of the packet data are radio
492 * information (including a reserved byte).
496 * We don't *have* 4 bytes of packet data.
498 *err = WTAP_ERR_BAD_RECORD;
499 *err_info = g_strdup("etherpeek: packet not long enough for 802.11 radio header");
502 wtap_file_read_expected_bytes(&radio_hdr, 4, wth->random_fh,
505 etherpeek_fill_pseudo_header_v7(pseudo_header,
509 case WTAP_ENCAP_ETHERNET:
510 /* XXX - it appears that if the low-order bit of
511 "status" is 0, there's an FCS in this frame,
512 and if it's 1, there's 4 bytes of 0. */
513 pseudo_header->eth.fcs_len = (status & 0x01) ? 0 : 4;
518 * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
520 errno = WTAP_ERR_CANT_READ;
521 wtap_file_read_expected_bytes(pd, length, wth->random_fh, err);
526 etherpeek_fill_pseudo_header_v7(union wtap_pseudo_header *pseudo_header,
527 airopeek_radio_hdr_t *radio_hdr)
529 pseudo_header->ieee_802_11.fcs_len = 0; /* no FCS */
530 pseudo_header->ieee_802_11.channel = radio_hdr->channel;
531 pseudo_header->ieee_802_11.data_rate = radio_hdr->data_rate;
532 pseudo_header->ieee_802_11.signal_level = radio_hdr->signal_level;
535 static gboolean etherpeek_read_v56(wtap *wth, int *err, gchar **err_info _U_,
538 guchar ep_pkt[ETHERPEEK_V56_PKT_SIZE];
551 * XXX - in order to figure out whether this packet is an
552 * Ethernet packet or not, we have to look at the packet
553 * header, so we have to remember the address of the header,
554 * not the address of the data, for random access.
556 * If we can determine that from the file header, rather than
557 * the packet header, we can remember the offset of the data,
558 * and not have the seek_read routine read the header.
560 *data_offset = wth->data_offset;
562 wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->fh, err);
563 wth->data_offset += sizeof(ep_pkt);
565 /* Extract the fields from the packet */
566 length = pntohs(&ep_pkt[ETHERPEEK_V56_LENGTH_OFFSET]);
567 sliceLength = pntohs(&ep_pkt[ETHERPEEK_V56_SLICE_LENGTH_OFFSET]);
568 flags = ep_pkt[ETHERPEEK_V56_FLAGS_OFFSET];
569 status = ep_pkt[ETHERPEEK_V56_STATUS_OFFSET];
570 timestamp = pntohl(&ep_pkt[ETHERPEEK_V56_TIMESTAMP_OFFSET]);
571 destNum = pntohs(&ep_pkt[ETHERPEEK_V56_DESTNUM_OFFSET]);
572 srcNum = pntohs(&ep_pkt[ETHERPEEK_V56_SRCNUM_OFFSET]);
573 protoNum = pntohs(&ep_pkt[ETHERPEEK_V56_PROTONUM_OFFSET]);
574 memcpy(protoStr, &ep_pkt[ETHERPEEK_V56_PROTOSTR_OFFSET],
578 * XXX - is the captured packet data padded to a multiple
582 /* force sliceLength to be the actual length of the packet */
583 if (0 == sliceLength) {
584 sliceLength = length;
587 /* read the frame data */
588 buffer_assure_space(wth->frame_buffer, sliceLength);
589 wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
590 sliceLength, wth->fh, err);
591 wth->data_offset += sliceLength;
593 /* fill in packet header values */
594 wth->phdr.len = length;
595 wth->phdr.caplen = sliceLength;
596 /* timestamp is in milliseconds since reference_time */
597 wth->phdr.ts.secs = wth->capture.etherpeek->reference_time.tv_sec
598 + (timestamp / 1000);
599 wth->phdr.ts.nsecs = 1000 * (timestamp % 1000) * 1000;
601 wth->phdr.pkt_encap = WTAP_ENCAP_UNKNOWN;
602 for (i=0; i<NUM_ETHERPEEK_ENCAPS; i++) {
603 if (etherpeek_encap[i].protoNum == protoNum) {
604 wth->phdr.pkt_encap = etherpeek_encap[i].encap;
608 switch (wth->phdr.pkt_encap) {
610 case WTAP_ENCAP_ETHERNET:
611 /* We assume there's no FCS in this frame. */
612 wth->pseudo_header.eth.fcs_len = 0;
619 etherpeek_seek_read_v56(wtap *wth, gint64 seek_off,
620 union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
621 int *err, gchar **err_info _U_)
623 guchar ep_pkt[ETHERPEEK_V56_PKT_SIZE];
628 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
631 wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->random_fh,
634 protoNum = pntohs(&ep_pkt[ETHERPEEK_V56_PROTONUM_OFFSET]);
635 pkt_encap = WTAP_ENCAP_UNKNOWN;
636 for (i=0; i<NUM_ETHERPEEK_ENCAPS; i++) {
637 if (etherpeek_encap[i].protoNum == protoNum) {
638 pkt_encap = etherpeek_encap[i].encap;
644 case WTAP_ENCAP_ETHERNET:
645 /* We assume there's no FCS in this frame. */
646 pseudo_header->eth.fcs_len = 0;
651 * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
653 errno = WTAP_ERR_CANT_READ;
654 wtap_file_read_expected_bytes(pd, length, wth->random_fh, err);