2 * Routines for opening EtherPeek and AiroPeek V9 files
7 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 #include "file_wrappers.h"
33 #include "airopeek9.h"
37 * This file decoder could not have been writen without examining
38 * http://www.varsanofiev.com/inside/airopeekv9.htm, the help from
39 * Martin Regner and Guy Harris, and the etherpeek.c file.
43 * NOTE: it says "airopeek" because the first files seen that use this
44 * format were AiroPeek files; however, EtherPeek files using it have
49 typedef struct airopeek_section_header {
52 guint32 section_const;
53 } airopeek_section_header_t;
56 * Network subtype values.
58 * XXX - do different network subtype values for 802.11 indicate different
59 * network adapter types, with some adapters supplying the FCS and others
60 * not supplying the FCS?
62 #define AIROPEEK_V9_NST_ETHERNET 0
63 #define AIROPEEK_V9_NST_802_11 1 /* 802.11 with 0's at the end */
64 #define AIROPEEK_V9_NST_802_11_2 2 /* 802.11 with 0's at the end */
65 #define AIROPEEK_V9_NST_802_11_WITH_FCS 3 /* 802.11 with FCS at the end */
67 /* tags for fields in packet header */
68 #define TAG_AIROPEEK_V9_LENGTH 0x0000
69 #define TAG_AIROPEEK_V9_TIMESTAMP_LOWER 0x0001
70 #define TAG_AIROPEEK_V9_TIMESTAMP_UPPER 0x0002
71 #define TAG_AIROPEEK_V9_FLAGS_AND_STATUS 0x0003
72 #define TAG_AIROPEEK_V9_CHANNEL 0x0004
73 #define TAG_AIROPEEK_V9_RATE 0x0005
74 #define TAG_AIROPEEK_V9_SIGNAL_PERC 0x0006
75 #define TAG_AIROPEEK_V9_SIGNAL_DBM 0x0007
76 #define TAG_AIROPEEK_V9_NOISE_PERC 0x0008
77 #define TAG_AIROPEEK_V9_NOISE_DBM 0x0009
78 #define TAG_AIROPEEK_V9_UNKNOWN_0x000D 0x000D
79 #define TAG_AIROPEEK_V9_SLICE_LENGTH 0xffff
81 /* 64-bit time in nanoseconds from the (Windows FILETIME) epoch */
82 typedef struct airopeek_utime {
91 static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info,
93 static gboolean airopeekv9_seek_read(wtap *wth, gint64 seek_off,
94 union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
95 int *err, gchar **err_info);
97 static int wtap_file_read_pattern (wtap *wth, const char *pattern, int *err,
106 c = file_getc(wth->fh);
108 if (file_eof(wth->fh))
111 *err = file_error(wth->fh, err_info);
112 return -1; /* error */
125 return (*cp == '\0' ? 1 : 0);
129 static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
130 const char *separators, int *err,
137 for (cp = buffer, i = 0; i < buflen; i++, cp++)
139 c = file_getc(wth->fh);
141 if (file_eof(wth->fh))
144 *err = file_error(wth->fh, err_info);
145 return -1; /* error */
148 if (strchr (separators, c) != NULL)
160 static int wtap_file_read_number (wtap *wth, guint32 *num, int *err,
168 ret = wtap_file_read_till_separator (wth, str_num, sizeof (str_num)-1, "<",
171 /* 0 means EOF, which means "not a valid AiroPeek V9 file";
172 -1 means error, and "err" has been set. */
175 value = strtoul (str_num, &p, 10);
176 if (p == str_num || value > G_MAXUINT32)
178 *num = (guint32)value;
183 int airopeek9_open(wtap *wth, int *err, gchar **err_info)
185 airopeek_section_header_t ap_hdr;
189 guint32 mediaSubType = 0;
191 static const int airopeek9_encap[] = {
193 WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
194 WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
195 WTAP_ENCAP_IEEE_802_11_WITH_RADIO
197 #define NUM_AIROPEEK9_ENCAPS (sizeof airopeek9_encap / sizeof airopeek9_encap[0])
198 airopeek9_t *airopeek9;
200 wtap_file_read_unknown_bytes(&ap_hdr, sizeof(ap_hdr), wth->fh, err,
203 if (memcmp (ap_hdr.section_id, "\177ver", sizeof(ap_hdr.section_id)) != 0)
204 return 0; /* doesn't begin with a "\177ver" section */
207 * XXX - we should get the length of the "\177ver" section, check
208 * that it's followed by a little-endian 0x00000200, and then,
209 * when reading the XML, make sure we don't go past the end of
210 * that section, and skip to the end of that section when
211 * we have the file version (and possibly check to make sure all
212 * tags are properly opened and closed).
214 ret = wtap_file_read_pattern (wth, "<FileVersion>", err, err_info);
216 /* 0 means EOF, which means "not a valid AiroPeek V9 file";
217 -1 means error, and "err" has been set. */
220 ret = wtap_file_read_number (wth, &fileVersion, err, err_info);
222 /* 0 means EOF, which means "not a valid AiroPeek V9 file";
223 -1 means error, and "err" has been set. */
227 /* If we got this far, we assume it's an AiroPeek V9 file. */
228 if (fileVersion != 9) {
229 /* We only support version 9. */
230 *err = WTAP_ERR_UNSUPPORTED;
231 *err_info = g_strdup_printf("airopeekv9: version %u unsupported",
237 * XXX - once we've skipped the "\177ver" section, we should
238 * check for a "sess" section and fail if we don't see it.
239 * Then we should get the length of the "sess" section, check
240 * that it's followed by a little-endian 0x00000200, and then,
241 * when reading the XML, make sure we don't go past the end of
242 * that section, and skip to the end of the section when
243 * we have the file version (and possibly check to make sure all
244 * tags are properly opened and closed).
246 ret = wtap_file_read_pattern (wth, "<MediaType>", err, err_info);
250 *err = WTAP_ERR_BAD_FILE;
251 *err_info = g_strdup("airopeekv9: <MediaType> tag not found");
254 /* XXX - this appears to be 0 in both the EtherPeek and AiroPeek
255 files we've seen; should we require it to be 0? */
256 ret = wtap_file_read_number (wth, &mediaType, err, err_info);
260 *err = WTAP_ERR_BAD_FILE;
261 *err_info = g_strdup("airopeekv9: <MediaType> value not found");
265 ret = wtap_file_read_pattern (wth, "<MediaSubType>", err, err_info);
269 *err = WTAP_ERR_BAD_FILE;
270 *err_info = g_strdup("airopeekv9: <MediaSubType> tag not found");
273 ret = wtap_file_read_number (wth, &mediaSubType, err, err_info);
277 *err = WTAP_ERR_BAD_FILE;
278 *err_info = g_strdup("airopeekv9: <MediaSubType> value not found");
281 if (mediaSubType >= NUM_AIROPEEK9_ENCAPS
282 || airopeek9_encap[mediaSubType] == WTAP_ENCAP_UNKNOWN) {
283 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
284 *err_info = g_strdup_printf("airopeekv9: network type %u unknown or unsupported",
289 ret = wtap_file_read_pattern (wth, "pkts", err, err_info);
293 *err = WTAP_ERR_SHORT_READ;
297 /* skip 8 zero bytes */
298 if (file_seek (wth->fh, 8L, SEEK_CUR, err) == -1)
302 * This is an EtherPeek or AiroPeek V9 file.
304 wth->data_offset = file_tell (wth->fh);
306 file_encap = airopeek9_encap[mediaSubType];
308 wth->file_type = WTAP_FILE_AIROPEEK_V9;
309 wth->file_encap = file_encap;
310 wth->subtype_read = airopeekv9_read;
311 wth->subtype_seek_read = airopeekv9_seek_read;
312 wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
314 airopeek9 = (airopeek9_t *)g_malloc(sizeof(airopeek9_t));
315 wth->priv = (void *)airopeek9;
316 switch (mediaSubType) {
318 case AIROPEEK_V9_NST_ETHERNET:
319 case AIROPEEK_V9_NST_802_11:
320 case AIROPEEK_V9_NST_802_11_2:
321 airopeek9->has_fcs = FALSE;
324 case AIROPEEK_V9_NST_802_11_WITH_FCS:
325 airopeek9->has_fcs = TRUE;
329 wth->snapshot_length = 0; /* not available in header */
337 airopeek_utime timestamp;
338 struct ieee_802_11_phdr ieee_802_11;
342 * Process the packet header.
344 * XXX - we should supply the additional radio information;
345 * the pseudo-header should probably be supplied in a fashion
346 * similar to the new BSD radio header, so that the 802.11
347 * dissector can determine which, if any, information items
351 airopeekv9_process_header(FILE_T fh, hdr_info_t *hdr_info, int *err,
358 gboolean saw_length = FALSE;
359 gboolean saw_timestamp_lower = FALSE;
360 gboolean saw_timestamp_upper = FALSE;
362 /* Extract the fields from the packet header */
364 /* Get the tag and value.
365 XXX - this assumes all values are 4 bytes long. */
366 bytes_read = file_read(tag_value, sizeof tag_value, fh);
367 if (bytes_read != (int) sizeof tag_value) {
368 *err = file_error(fh, err_info);
371 *err = WTAP_ERR_SHORT_READ;
372 else if (bytes_read == 0) {
374 * Short read if we've read something already;
375 * just an EOF if we haven't.
378 *err = WTAP_ERR_SHORT_READ;
383 header_len += (int) sizeof(tag_value);
384 tag = pletohs(&tag_value[0]);
387 case TAG_AIROPEEK_V9_LENGTH:
389 *err = WTAP_ERR_BAD_FILE;
390 *err_info = g_strdup("airopeekv9: record has two length fields");
393 hdr_info->length = pletohl(&tag_value[2]);
397 case TAG_AIROPEEK_V9_TIMESTAMP_LOWER:
398 if (saw_timestamp_lower) {
399 *err = WTAP_ERR_BAD_FILE;
400 *err_info = g_strdup("airopeekv9: record has two timestamp-lower fields");
403 hdr_info->timestamp.lower = pletohl(&tag_value[2]);
404 saw_timestamp_lower = TRUE;
407 case TAG_AIROPEEK_V9_TIMESTAMP_UPPER:
408 if (saw_timestamp_upper) {
409 *err = WTAP_ERR_BAD_FILE;
410 *err_info = g_strdup("airopeekv9: record has two timestamp-upper fields");
413 hdr_info->timestamp.upper = pletohl(&tag_value[2]);
414 saw_timestamp_upper = TRUE;
417 case TAG_AIROPEEK_V9_FLAGS_AND_STATUS:
418 /* XXX - not used yet */
421 case TAG_AIROPEEK_V9_CHANNEL:
422 hdr_info->ieee_802_11.channel = pletohl(&tag_value[2]);
425 case TAG_AIROPEEK_V9_RATE:
426 hdr_info->ieee_802_11.data_rate = pletohl(&tag_value[2]);
429 case TAG_AIROPEEK_V9_SIGNAL_PERC:
430 hdr_info->ieee_802_11.signal_level = pletohl(&tag_value[2]);
433 case TAG_AIROPEEK_V9_SIGNAL_DBM:
434 /* XXX - not used yet */
437 case TAG_AIROPEEK_V9_NOISE_PERC:
438 /* XXX - not used yet */
441 case TAG_AIROPEEK_V9_NOISE_DBM:
442 /* XXX - not used yet */
445 case TAG_AIROPEEK_V9_UNKNOWN_0x000D:
446 /* XXX - seen in an EtherPeek capture; value unknown */
449 case TAG_AIROPEEK_V9_SLICE_LENGTH:
450 hdr_info->sliceLength = pletohl(&tag_value[2]);
456 } while (tag != TAG_AIROPEEK_V9_SLICE_LENGTH); /* last tag */
459 *err = WTAP_ERR_BAD_FILE;
460 *err_info = g_strdup("airopeekv9: record has no length field");
463 if (!saw_timestamp_lower) {
464 *err = WTAP_ERR_BAD_FILE;
465 *err_info = g_strdup("airopeekv9: record has no timestamp-lower field");
468 if (!saw_timestamp_upper) {
469 *err = WTAP_ERR_BAD_FILE;
470 *err_info = g_strdup("airopeekv9: record has no timestamp-upper field");
478 * Time stamps appear to be in nanoseconds since the Windows epoch
479 * as used in FILETIMEs, i.e. midnight, January 1, 1601.
481 * This magic number came from "nt_time_to_nstime()" in "packet-smb.c".
482 * 1970-1601 is 369; I'm not sure what the extra 3 days and 6 hours are
483 * that are being subtracted.
485 #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
487 static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info,
490 airopeek9_t *airopeek9 = (airopeek9_t *)wth->priv;
495 *data_offset = wth->data_offset;
497 /* Process the packet header. */
498 hdrlen = airopeekv9_process_header(wth->fh, &hdr_info, err, err_info);
501 wth->data_offset += hdrlen;
504 * If sliceLength is 0, force it to be the actual length of the packet.
506 if (hdr_info.sliceLength == 0)
507 hdr_info.sliceLength = hdr_info.length;
509 if (hdr_info.sliceLength > WTAP_MAX_PACKET_SIZE) {
511 * Probably a corrupt capture file; don't blow up trying
512 * to allocate space for an immensely-large packet.
514 *err = WTAP_ERR_BAD_FILE;
515 *err_info = g_strdup_printf("airopeek9: File has %u-byte packet, bigger than maximum of %u",
516 hdr_info.sliceLength, WTAP_MAX_PACKET_SIZE);
520 /* fill in packet header length values before slicelength may be
522 wth->phdr.len = hdr_info.length;
523 wth->phdr.caplen = hdr_info.sliceLength;
525 /* read the frame data */
526 buffer_assure_space(wth->frame_buffer, hdr_info.sliceLength);
527 wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
528 hdr_info.sliceLength, wth->fh, err,
530 wth->data_offset += hdr_info.sliceLength;
532 /* recalculate and fill in packet time stamp */
533 t = (double) hdr_info.timestamp.lower +
534 (double) hdr_info.timestamp.upper * 4294967296.0;
537 t -= TIME_FIXUP_CONSTANT;
538 wth->phdr.ts.secs = (time_t) t;
539 wth->phdr.ts.nsecs = (guint32) ((t - wth->phdr.ts.secs)*1000000000);
541 switch (wth->file_encap) {
543 case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
545 * The last 4 bytes sometimes contains the FCS but on a lot of
546 * interfaces these are zero. Is there some way to determine
547 * from the packet header whether it's an FCS or not?
549 * For now, we just discard those bytes; if we can determine
550 * whether it's an FCS or not, we should use that to determine
551 * whether to supply it as an FCS or discard it.
553 wth->pseudo_header.ieee_802_11 = hdr_info.ieee_802_11;
554 if (airopeek9->has_fcs)
555 wth->pseudo_header.ieee_802_11.fcs_len = 4;
557 wth->pseudo_header.ieee_802_11.fcs_len = 0;
559 wth->phdr.caplen -= 4;
563 case WTAP_ENCAP_ETHERNET:
565 * The last 4 bytes appear to be 0 in the captures I've seen;
566 * are there any captures where it's an FCS?
568 wth->pseudo_header.eth.fcs_len = 0;
570 wth->phdr.caplen -= 4;
579 airopeekv9_seek_read(wtap *wth, gint64 seek_off,
580 union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
581 int *err, gchar **err_info)
583 airopeek9_t *airopeek9 = (airopeek9_t *)wth->priv;
586 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
589 /* Process the packet header. */
590 if (airopeekv9_process_header(wth->random_fh, &hdr_info, err, err_info) == -1)
593 switch (wth->file_encap) {
595 case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
596 pseudo_header->ieee_802_11 = hdr_info.ieee_802_11;
597 if (airopeek9->has_fcs)
598 pseudo_header->ieee_802_11.fcs_len = 4;
600 pseudo_header->ieee_802_11.fcs_len = 0;
603 case WTAP_ENCAP_ETHERNET:
604 pseudo_header->eth.fcs_len = 0;
609 * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
611 errno = WTAP_ERR_CANT_READ;
612 wtap_file_read_expected_bytes(pd, length, wth->random_fh, err, err_info);