2 * Routines for opening AiroPeek V9 files
4 * $Id: airopeek9.c,v 1.2 2003/12/02 20:27:14 guy Exp $
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 typedef struct airopeek_section_header {
46 guint32 section_const;
47 } airopeek_section_header_t;
49 #define AIROPEEK_V9_LENGTH_OFFSET 2
50 #define AIROPEEK_V9_TIMESTAMP_LOWER_OFFSET 8
51 #define AIROPEEK_V9_TIMESTAMP_UPPER_OFFSET 14
52 #define AIROPEEK_V9_FLAGS_OFFSET 20
53 #define AIROPEEK_V9_STATUS_OFFSET 22
54 #define AIROPEEK_V9_CHANNEL_OFFSET 26
55 #define AIROPEEK_V9_RATE_OFFSET 32
56 #define AIROPEEK_V9_SIGNAL_PERC_OFFSET 38
57 #define AIROPEEK_V9_SIGNAL_DBM_OFFSET 44
58 #define AIROPEEK_V9_NOISE_PERC_OFFSET 50
59 #define AIROPEEK_V9_NOISE_DBM_OFFSET 56
60 #define AIROPEEK_V9_SLICE_LENGTH_OFFSET 62
62 #define AIROPEEK_V9_PKT_SIZE 66
64 /* 64-bit time in nano seconds from the (Mac) epoch */
65 typedef struct airopeek_utime {
70 static const unsigned int mac2unix = 2082844800u;
72 static gboolean airopeek_read_v9(wtap *wth, int *err, long *data_offset);
73 static gboolean airopeek_seek_read_v9(wtap *wth, long seek_off,
74 union wtap_pseudo_header *pseudo_header, guchar *pd, int length, int *err);
76 static int wtap_file_read_pattern (wtap *wth, char *pattern, int *err)
84 c = file_getc(wth->fh);
86 if (file_eof(wth->fh))
89 /* We (presumably) got an error (there's no equivalent to
90 "ferror()" in zlib, alas, so we don't have a wrapper
91 to check for an error). */
92 *err = file_error(wth->fh);
93 return -1; /* error */
106 return (*cp == '\0' ? 1 : 0);
110 static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
111 char *separators, int *err)
117 for (cp = buffer, i = 0; i < buflen; i++, cp++)
119 c = file_getc(wth->fh);
121 if (file_eof(wth->fh))
124 /* We (presumably) got an error (there's no equivalent to
125 "ferror()" in zlib, alas, so we don't have a wrapper
126 to check for an error). */
127 *err = file_error(wth->fh);
128 return -1; /* error */
131 if (strchr (separators, c))
143 static int wtap_file_read_number (wtap *wth, guint32 *num, int *err)
147 unsigned long long value;
150 ret = wtap_file_read_till_separator (wth, str_num, sizeof (str_num)-1, "<",
153 /* 0 means EOF, which means "not a valid AiroPeek V9 file";
154 -1 means error, and "err" has been set. */
157 value = strtoul (str_num, &p, 10);
158 if (p == str_num || value > UINT_MAX)
165 int airopeek9_open(wtap *wth, int *err)
167 airopeek_section_header_t ap_hdr;
173 wtap_file_read_unknown_bytes(&ap_hdr, sizeof(ap_hdr), wth->fh, err);
175 if (memcmp (ap_hdr.section_id, "\177ver", sizeof(ap_hdr.section_id)) != 0)
176 return 0; /* doesn't begin with a "\177ver" section */
179 * XXX - we should get the length of the "\177ver" section, check
180 * that it's followed by a little-endian 0x00000200, and then,
181 * when reading the XML, make sure we don't go past the end of
182 * that section, and skip to the end of tha section when
183 * we have the file version (and possibly check to make sure all
184 * tags are properly opened and closed).
186 ret = wtap_file_read_pattern (wth, "<FileVersion>", err);
188 /* 0 means EOF, which means "not a valid AiroPeek V9 file";
189 -1 means error, and "err" has been set. */
192 ret = wtap_file_read_number (wth, &fileVersion, err);
194 /* 0 means EOF, which means "not a valid AiroPeek V9 file";
195 -1 means error, and "err" has been set. */
199 /* If we got this far, we assume it's an AiroPeek V9 file. */
200 if (fileVersion != 9) {
201 /* We only support version 9 and later. */
202 g_message("airopeekv9: version %u unsupported", fileVersion);
203 *err = WTAP_ERR_UNSUPPORTED;
208 * XXX - once we've skipped the "\177ver" section, we should
209 * check for a "sess" section and fail if we don't see it.
210 * Then we should get the length of the "sess" section, check
211 * that it's followed by a little-endian 0x00000200, and then,
212 * when reading the XML, make sure we don't go past the end of
213 * that section, and skip to the end of tha section when
214 * we have the file version (and possibly check to make sure all
215 * tags are properly opened and closed).
217 ret = wtap_file_read_pattern (wth, "<MediaType>", err);
221 g_message("airopeekv9: <MediaType> tag not found");
222 *err = WTAP_ERR_UNSUPPORTED;
225 /* XXX - this appears to be 0, which is also the media type for
226 802.11 in the older AiroPeek format; should we require it to be
227 0? And should we check the MediaSubType value as well? */
228 ret = wtap_file_read_number (wth, &mediaType, err);
232 g_message("airopeekv9: <MediaType> value not found");
233 *err = WTAP_ERR_UNSUPPORTED;
237 ret = wtap_file_read_pattern (wth, "pkts", err);
241 *err = WTAP_ERR_SHORT_READ;
245 /* skip 8 zero bytes */
246 if (file_seek (wth->fh, 8L, SEEK_CUR, err) == -1)
250 * This is an AiroPeek V9 file.
253 wth->data_offset = file_tell (wth->fh);
255 file_encap = WTAP_ENCAP_IEEE_802_11_WITH_RADIO;
257 wth->file_type = WTAP_FILE_AIROPEEK_V9;
258 wth->file_encap = file_encap;
259 wth->subtype_read = airopeek_read_v9;
260 wth->subtype_seek_read = airopeek_seek_read_v9;
262 wth->snapshot_length = 0; /* not available in header */
267 static gboolean airopeek_read_v9(wtap *wth, int *err, long *data_offset)
269 guchar ap_pkt[AIROPEEK_V9_PKT_SIZE];
272 airopeek_utime timestamp;
275 *data_offset = wth->data_offset;
277 wtap_file_read_expected_bytes(ap_pkt, sizeof(ap_pkt), wth->fh, err);
278 wth->data_offset += sizeof(ap_pkt);
280 /* Extract the fields from the packet */
281 length = pletohl(&ap_pkt[AIROPEEK_V9_LENGTH_OFFSET]);
282 sliceLength = pletohl(&ap_pkt[AIROPEEK_V9_SLICE_LENGTH_OFFSET]);
283 timestamp.upper = pletohl(&ap_pkt[AIROPEEK_V9_TIMESTAMP_UPPER_OFFSET]);
284 timestamp.lower = pletohl(&ap_pkt[AIROPEEK_V9_TIMESTAMP_LOWER_OFFSET]);
286 /* force sliceLength to be the actual length of the packet */
287 if (sliceLength == 0) {
288 sliceLength = length;
291 /* fill in packet header length values before slicelength may be
293 wth->phdr.len = length;
294 wth->phdr.caplen = sliceLength;
297 * Fill the pseudo header with radio information.
298 * XXX - we should supply the additional information;
299 * the pseudo-header should probably be supplied in a fashion
300 * similar to the new BSD radio header, so that the 802.11
301 * dissector can determine which, if any, information items
304 wth->pseudo_header.ieee_802_11.channel =
305 pletohl(&ap_pkt[AIROPEEK_V9_CHANNEL_OFFSET]);
306 wth->pseudo_header.ieee_802_11.data_rate =
307 pletohl(&ap_pkt[AIROPEEK_V9_RATE_OFFSET]);
308 wth->pseudo_header.ieee_802_11.signal_level =
309 pletohl(&ap_pkt[AIROPEEK_V9_SIGNAL_PERC_OFFSET]);
311 /* read the frame data */
312 buffer_assure_space(wth->frame_buffer, sliceLength);
313 wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
314 sliceLength, wth->fh, err);
315 wth->data_offset += sliceLength;
317 /* recalculate and fill in packet time stamp */
318 t = (double) timestamp.lower +
319 (double) timestamp.upper * 4294967296.0;
321 t = t / 1000.0; /* nano seconds -> micro seconds */
322 t -= (double) mac2unix * 1000000.0;
323 wth->phdr.ts.tv_sec = (time_t) (t/1000000.0);
324 wth->phdr.ts.tv_usec = (guint32) (t - (double) wth->phdr.ts.tv_sec *
328 * The last 4 bytes sometimes contains the FCS but on a lot of
329 * interfaces these are zero. To eleminate problems we reduce
332 * XXX - is there any way to find out whether it's an FCS or not?
335 wth->phdr.caplen -= 4;
337 wth->phdr.pkt_encap = wth->file_encap;
343 airopeek_seek_read_v9(wtap *wth, long seek_off,
344 union wtap_pseudo_header *pseudo_header, guchar *pd, int length, int *err)
346 guchar ap_pkt[AIROPEEK_V9_PKT_SIZE];
348 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
351 /* Read the packet header. */
352 wtap_file_read_expected_bytes(ap_pkt, sizeof(ap_pkt), wth->random_fh, err);
354 pseudo_header->ieee_802_11.channel =
355 pletohl(&ap_pkt[AIROPEEK_V9_CHANNEL_OFFSET]);
356 pseudo_header->ieee_802_11.data_rate =
357 pletohl(&ap_pkt[AIROPEEK_V9_RATE_OFFSET]);
358 pseudo_header->ieee_802_11.signal_level =
359 pletohl(&ap_pkt[AIROPEEK_V9_SIGNAL_PERC_OFFSET]);
362 * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
364 errno = WTAP_ERR_CANT_READ;
365 wtap_file_read_expected_bytes(pd, length, wth->random_fh, err);