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]))
146 struct timeval reference_time;
149 static gboolean etherpeek_read_v7(wtap *wth, int *err, gchar **err_info,
150 gint64 *data_offset);
151 static gboolean etherpeek_seek_read_v7(wtap *wth, gint64 seek_off,
152 union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
153 int *err, gchar **err_info);
154 static void etherpeek_fill_pseudo_header_v7(
155 union wtap_pseudo_header *pseudo_header, airopeek_radio_hdr_t *radio_hdr);
156 static gboolean etherpeek_read_v56(wtap *wth, int *err, gchar **err_info,
157 gint64 *data_offset);
158 static gboolean etherpeek_seek_read_v56(wtap *wth, gint64 seek_off,
159 union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
160 int *err, gchar **err_info);
162 int etherpeek_open(wtap *wth, int *err, gchar **err_info)
164 etherpeek_header_t ep_hdr;
165 struct timeval reference_time;
167 etherpeek_t *etherpeek;
169 /* EtherPeek files do not start with a magic value large enough
170 * to be unique; hence we use the following algorithm to determine
171 * the type of an unknown file:
172 * - populate the master header and reject file if there is no match
173 * - populate the secondary header and check that the reserved space
174 * is zero, and check some other fields; this isn't perfect,
175 * and we may have to add more checks at some point.
177 g_assert(sizeof(ep_hdr.master) == ETHERPEEK_MASTER_HDR_SIZE);
178 wtap_file_read_unknown_bytes(
179 &ep_hdr.master, sizeof(ep_hdr.master), wth->fh, err, err_info);
180 wth->data_offset += sizeof(ep_hdr.master);
183 * It appears that EtherHelp (a free application from WildPackets
184 * that did blind capture, saving to a file, so that you could
185 * give the resulting file to somebody with EtherPeek) saved
186 * captures in EtherPeek format except that it ORed the 0x80
187 * bit on in the version number.
189 * We therefore strip off the 0x80 bit in the version number.
190 * Perhaps there's some reason to care whether the capture
191 * came from EtherHelp; if we discover one, we should check
194 ep_hdr.master.version &= ~0x80;
196 /* switch on the file version */
197 switch (ep_hdr.master.version) {
202 /* get the secondary header */
203 g_assert(sizeof(ep_hdr.secondary.v567) ==
204 ETHERPEEK_V567_HDR_SIZE);
205 wtap_file_read_unknown_bytes(
206 &ep_hdr.secondary.v567,
207 sizeof(ep_hdr.secondary.v567), wth->fh, err, err_info);
208 wth->data_offset += sizeof(ep_hdr.secondary.v567);
210 if ((0 != ep_hdr.secondary.v567.reserved[0]) ||
211 (0 != ep_hdr.secondary.v567.reserved[1]) ||
212 (0 != ep_hdr.secondary.v567.reserved[2])) {
218 * Check the mediaType and physMedium fields.
219 * We assume it's not an EtherPeek/TokenPeek/AiroPeek
220 * file if these aren't values we know, rather than
221 * reporting them as invalid *Peek files, as, given
222 * the lack of a magic number, we need all the checks
225 ep_hdr.secondary.v567.mediaType =
226 g_ntohl(ep_hdr.secondary.v567.mediaType);
227 ep_hdr.secondary.v567.physMedium =
228 g_ntohl(ep_hdr.secondary.v567.physMedium);
230 switch (ep_hdr.secondary.v567.physMedium) {
234 * "Native" format, presumably meaning
235 * Ethernet or Token Ring.
237 switch (ep_hdr.secondary.v567.mediaType) {
240 file_encap = WTAP_ENCAP_ETHERNET;
244 file_encap = WTAP_ENCAP_TOKEN_RING;
249 * Assume this isn't a *Peek file.
256 switch (ep_hdr.secondary.v567.mediaType) {
260 * 802.11, with a private header giving
261 * some radio information. Presumably
262 * this is from AiroPeek.
264 * We supply the private header as
265 * the WTAP_ENCAP_IEEE_802_11_WITH_RADIO
266 * pseudo-header, rather than as frame
269 file_encap = WTAP_ENCAP_IEEE_802_11_WITH_RADIO;
274 * Assume this isn't a *Peek file.
282 * Assume this isn't a *Peek file.
289 * Assume this is a V5, V6 or V7 *Peek file, and byte
290 * swap the rest of the fields in the secondary header.
292 * XXX - we could check the file length if the file were
293 * uncompressed, but it might be compressed.
295 ep_hdr.secondary.v567.filelength =
296 g_ntohl(ep_hdr.secondary.v567.filelength);
297 ep_hdr.secondary.v567.numPackets =
298 g_ntohl(ep_hdr.secondary.v567.numPackets);
299 ep_hdr.secondary.v567.timeDate =
300 g_ntohl(ep_hdr.secondary.v567.timeDate);
301 ep_hdr.secondary.v567.timeStart =
302 g_ntohl(ep_hdr.secondary.v567.timeStart);
303 ep_hdr.secondary.v567.timeStop =
304 g_ntohl(ep_hdr.secondary.v567.timeStop);
305 ep_hdr.secondary.v567.appVers =
306 g_ntohl(ep_hdr.secondary.v567.appVers);
307 ep_hdr.secondary.v567.linkSpeed =
308 g_ntohl(ep_hdr.secondary.v567.linkSpeed);
310 /* Get the reference time as a "struct timeval" */
311 reference_time.tv_sec =
312 ep_hdr.secondary.v567.timeDate - mac2unix;
313 reference_time.tv_usec = 0;
318 * Assume this isn't a *Peek file.
324 * This is an EtherPeek (or TokenPeek or AiroPeek?) file.
326 * At this point we have recognised the file type and have populated
327 * the whole ep_hdr structure in host byte order.
329 etherpeek = (etherpeek_t *)g_malloc(sizeof(etherpeek_t));
330 wth->priv = (void *)etherpeek;
331 etherpeek->reference_time = reference_time;
332 switch (ep_hdr.master.version) {
336 wth->file_type = WTAP_FILE_ETHERPEEK_V56;
338 * XXX - can we get the file encapsulation from the
339 * header in the same way we do for V7 files?
341 wth->file_encap = WTAP_ENCAP_PER_PACKET;
342 wth->subtype_read = etherpeek_read_v56;
343 wth->subtype_seek_read = etherpeek_seek_read_v56;
347 wth->file_type = WTAP_FILE_ETHERPEEK_V7;
348 wth->file_encap = file_encap;
349 wth->subtype_read = etherpeek_read_v7;
350 wth->subtype_seek_read = etherpeek_seek_read_v7;
354 /* this is impossible */
355 g_assert_not_reached();
358 wth->snapshot_length = 0; /* not available in header */
359 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
364 static gboolean etherpeek_read_v7(wtap *wth, int *err, gchar **err_info,
367 guint8 ep_pkt[ETHERPEEK_V7_PKT_SIZE];
380 airopeek_radio_hdr_t radio_hdr;
382 *data_offset = wth->data_offset;
384 wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->fh, err,
386 wth->data_offset += sizeof(ep_pkt);
388 /* Extract the fields from the packet */
390 protoNum = pntohs(&ep_pkt[ETHERPEEK_V7_PROTONUM_OFFSET]);
392 length = pntohs(&ep_pkt[ETHERPEEK_V7_LENGTH_OFFSET]);
393 sliceLength = pntohs(&ep_pkt[ETHERPEEK_V7_SLICE_LENGTH_OFFSET]);
395 flags = ep_pkt[ETHERPEEK_V7_FLAGS_OFFSET];
397 status = ep_pkt[ETHERPEEK_V7_STATUS_OFFSET];
398 timestamp = pntohll(&ep_pkt[ETHERPEEK_V7_TIMESTAMP_OFFSET]);
400 /* force sliceLength to be the actual length of the packet */
401 if (0 == sliceLength) {
402 sliceLength = length;
405 /* fill in packet header length values before slicelength may be
407 wth->phdr.len = length;
408 wth->phdr.caplen = sliceLength;
410 if (sliceLength % 2) /* packets are padded to an even length */
413 switch (wth->file_encap) {
415 case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
417 * The first 4 bytes of the packet data are radio
418 * information (including a reserved byte).
420 if (sliceLength < 4) {
422 * We don't *have* 4 bytes of packet data.
424 *err = WTAP_ERR_BAD_FILE;
425 *err_info = g_strdup("etherpeek: packet not long enough for 802.11 radio header");
428 wtap_file_read_expected_bytes(&radio_hdr, 4, wth->fh, err,
432 * We don't treat the radio information as packet data.
436 wth->phdr.caplen -= 4;
437 wth->data_offset += 4;
439 etherpeek_fill_pseudo_header_v7(&wth->pseudo_header,
443 case WTAP_ENCAP_ETHERNET:
444 /* XXX - it appears that if the low-order bit of
445 "status" is 0, there's an FCS in this frame,
446 and if it's 1, there's 4 bytes of 0. */
447 wth->pseudo_header.eth.fcs_len = (status & 0x01) ? 0 : 4;
451 /* read the frame data */
452 buffer_assure_space(wth->frame_buffer, sliceLength);
453 wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
454 sliceLength, wth->fh, err, err_info);
455 wth->data_offset += sliceLength;
457 /* fill in packet header values */
458 tsecs = (time_t) (timestamp/1000000);
459 tusecs = (guint32) (timestamp - tsecs*1000000);
460 wth->phdr.ts.secs = tsecs - mac2unix;
461 wth->phdr.ts.nsecs = tusecs * 1000;
463 if (wth->file_encap == WTAP_ENCAP_IEEE_802_11_WITH_RADIO) {
465 * The last 4 bytes appear to be random data - the length
466 * might include the FCS - so we reduce the length by 4.
468 * Or maybe this is just the same kind of random 4 bytes
469 * of junk at the end you get in Wireless Sniffer
473 wth->phdr.caplen -= 4;
480 etherpeek_seek_read_v7(wtap *wth, gint64 seek_off,
481 union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
482 int *err, gchar **err_info)
484 guint8 ep_pkt[ETHERPEEK_V7_PKT_SIZE];
486 airopeek_radio_hdr_t radio_hdr;
488 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
491 /* Read the packet header. */
492 wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->random_fh,
494 status = ep_pkt[ETHERPEEK_V7_STATUS_OFFSET];
496 switch (wth->file_encap) {
498 case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
500 * The first 4 bytes of the packet data are radio
501 * information (including a reserved byte).
505 * We don't *have* 4 bytes of packet data.
507 *err = WTAP_ERR_BAD_FILE;
508 *err_info = g_strdup("etherpeek: packet not long enough for 802.11 radio header");
511 wtap_file_read_expected_bytes(&radio_hdr, 4, wth->random_fh,
514 etherpeek_fill_pseudo_header_v7(pseudo_header,
518 case WTAP_ENCAP_ETHERNET:
519 /* XXX - it appears that if the low-order bit of
520 "status" is 0, there's an FCS in this frame,
521 and if it's 1, there's 4 bytes of 0. */
522 pseudo_header->eth.fcs_len = (status & 0x01) ? 0 : 4;
527 * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
529 errno = WTAP_ERR_CANT_READ;
530 wtap_file_read_expected_bytes(pd, length, wth->random_fh, err,
536 etherpeek_fill_pseudo_header_v7(union wtap_pseudo_header *pseudo_header,
537 airopeek_radio_hdr_t *radio_hdr)
539 pseudo_header->ieee_802_11.fcs_len = 0; /* no FCS */
540 pseudo_header->ieee_802_11.channel = radio_hdr->channel;
541 pseudo_header->ieee_802_11.data_rate = radio_hdr->data_rate;
542 pseudo_header->ieee_802_11.signal_level = radio_hdr->signal_level;
545 static gboolean etherpeek_read_v56(wtap *wth, int *err, gchar **err_info,
548 etherpeek_t *etherpeek = (etherpeek_t *)wth->priv;
549 guint8 ep_pkt[ETHERPEEK_V56_PKT_SIZE];
566 * XXX - in order to figure out whether this packet is an
567 * Ethernet packet or not, we have to look at the packet
568 * header, so we have to remember the address of the header,
569 * not the address of the data, for random access.
571 * If we can determine that from the file header, rather than
572 * the packet header, we can remember the offset of the data,
573 * and not have the seek_read routine read the header.
575 *data_offset = wth->data_offset;
577 wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->fh, err,
579 wth->data_offset += sizeof(ep_pkt);
581 /* Extract the fields from the packet */
582 length = pntohs(&ep_pkt[ETHERPEEK_V56_LENGTH_OFFSET]);
583 sliceLength = pntohs(&ep_pkt[ETHERPEEK_V56_SLICE_LENGTH_OFFSET]);
585 flags = ep_pkt[ETHERPEEK_V56_FLAGS_OFFSET];
586 status = ep_pkt[ETHERPEEK_V56_STATUS_OFFSET];
588 timestamp = pntohl(&ep_pkt[ETHERPEEK_V56_TIMESTAMP_OFFSET]);
590 destNum = pntohs(&ep_pkt[ETHERPEEK_V56_DESTNUM_OFFSET]);
591 srcNum = pntohs(&ep_pkt[ETHERPEEK_V56_SRCNUM_OFFSET]);
593 protoNum = pntohs(&ep_pkt[ETHERPEEK_V56_PROTONUM_OFFSET]);
594 memcpy(protoStr, &ep_pkt[ETHERPEEK_V56_PROTOSTR_OFFSET],
598 * XXX - is the captured packet data padded to a multiple
602 /* force sliceLength to be the actual length of the packet */
603 if (0 == sliceLength) {
604 sliceLength = length;
607 /* read the frame data */
608 buffer_assure_space(wth->frame_buffer, sliceLength);
609 wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
610 sliceLength, wth->fh, err, err_info);
611 wth->data_offset += sliceLength;
613 /* fill in packet header values */
614 wth->phdr.len = length;
615 wth->phdr.caplen = sliceLength;
616 /* timestamp is in milliseconds since reference_time */
617 wth->phdr.ts.secs = etherpeek->reference_time.tv_sec
618 + (timestamp / 1000);
619 wth->phdr.ts.nsecs = 1000 * (timestamp % 1000) * 1000;
621 wth->phdr.pkt_encap = WTAP_ENCAP_UNKNOWN;
622 for (i=0; i<NUM_ETHERPEEK_ENCAPS; i++) {
623 if (etherpeek_encap[i].protoNum == protoNum) {
624 wth->phdr.pkt_encap = etherpeek_encap[i].encap;
628 switch (wth->phdr.pkt_encap) {
630 case WTAP_ENCAP_ETHERNET:
631 /* We assume there's no FCS in this frame. */
632 wth->pseudo_header.eth.fcs_len = 0;
639 etherpeek_seek_read_v56(wtap *wth, gint64 seek_off,
640 union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
641 int *err, gchar **err_info)
643 guint8 ep_pkt[ETHERPEEK_V56_PKT_SIZE];
648 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
651 wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->random_fh,
654 protoNum = pntohs(&ep_pkt[ETHERPEEK_V56_PROTONUM_OFFSET]);
655 pkt_encap = WTAP_ENCAP_UNKNOWN;
656 for (i=0; i<NUM_ETHERPEEK_ENCAPS; i++) {
657 if (etherpeek_encap[i].protoNum == protoNum) {
658 pkt_encap = etherpeek_encap[i].encap;
664 case WTAP_ENCAP_ETHERNET:
665 /* We assume there's no FCS in this frame. */
666 pseudo_header->eth.fcs_len = 0;
671 * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
673 errno = WTAP_ERR_CANT_READ;
674 wtap_file_read_expected_bytes(pd, length, wth->random_fh, err,