5 /***************************************************************************
6 NetworkInstruments.c - description
8 begin : Wed Oct 29 2003
9 copyright : (C) 2003 by root
10 email : scotte[AT}netinst.com
11 ***************************************************************************/
13 /***************************************************************************
15 * This program is free software; you can redistribute it and/or modify *
16 * it under the terms of the GNU General Public License as published by *
17 * the Free Software Foundation; either version 2 of the License, or *
18 * (at your option) any later version. *
20 ***************************************************************************/
30 #include "file_wrappers.h"
32 #include "network_instruments.h"
34 static const char network_instruments_magic[] = {"ObserverPktBufferVersion=09.00"};
35 static const int true_magic_length = 17;
37 static const guint32 observer_packet_magic = 0x88888888;
39 static const int observer_encap[] = {
43 #define NUM_OBSERVER_ENCAPS (sizeof observer_encap / sizeof observer_encap[0])
45 static const int from_wtap_encap[] = {
50 #define NUM_FROM_WTAP_ENCAPS (sizeof from_wtap_encap / sizeof observer_encap[0])
52 #define CAPTUREFILE_HEADER_SIZE sizeof(capture_file_header)
55 * The time in Observer files is in nanoseconds since midnight, January 1,
56 * 2000, 00:00:00 local time.
58 * We want the seconds portion to be seconds since midnight, January 1,
61 * To do that, we add the number of seconds between midnight, January 1,
62 * 2000, 00:00:00 local time and midnight, January 1, 1970, 00:00:00 GMT.
63 * (That gets the wrong answer if the time zone is being read in a different
64 * time zone, but there's not much we can do about that.)
66 static gboolean have_time_offset;
67 static time_t seconds1970to2000;
69 static void init_time_offset(void)
71 if (!have_time_offset) {
72 struct tm midnight_2000_01_01;
75 * Get the number of seconds between midnight, January 1,
76 * 2000, 00:00:00 local time - that's just the UNIX
77 * time stamp for 2000-01-01 00:00:00 local time.
79 midnight_2000_01_01.tm_year = 2000 - 1900;
80 midnight_2000_01_01.tm_mon = 0;
81 midnight_2000_01_01.tm_mday = 1;
82 midnight_2000_01_01.tm_hour = 0;
83 midnight_2000_01_01.tm_min = 0;
84 midnight_2000_01_01.tm_sec = 0;
85 midnight_2000_01_01.tm_isdst = -1;
86 seconds1970to2000 = mktime(&midnight_2000_01_01);
87 have_time_offset = TRUE;
91 static gboolean observer_read(wtap *wth, int *err, gchar **err_info,
93 static gboolean observer_seek_read(wtap *wth, gint64 seek_off,
94 union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
95 int *err, gchar **err_info);
96 static int read_packet_header(FILE_T fh, packet_entry_header *packet_header,
97 int *err, gchar **err_info);
98 static gboolean read_packet_data(FILE_T fh, int offset_to_frame, int offset,
99 guint8 *pd, int length, int *err, char **err_info);
100 static gboolean skip_to_next_packet(wtap *wth, int offset,
101 int offset_to_next_packet, int *err, char **err_info);
102 static gboolean observer_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
103 const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err);
105 int network_instruments_open(wtap *wth, int *err, gchar **err_info)
109 capture_file_header file_header;
113 packet_entry_header packet_header;
115 errno = WTAP_ERR_CANT_READ;
118 /* read in the buffer file header */
119 bytes_read = file_read(&file_header, sizeof file_header, wth->fh);
120 if (bytes_read != sizeof file_header) {
121 *err = file_error(wth->fh);
126 offset += bytes_read;
128 /* check the magic number */
129 if (memcmp(file_header.observer_version, network_instruments_magic, true_magic_length)!=0) {
133 /* check the version */
134 if (strncmp(network_instruments_magic, file_header.observer_version, 30)!=0) {
135 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
136 *err_info = g_strdup_printf("Observer: unsupported file version %s", file_header.observer_version);
140 /* process extra information */
141 for (i = 0; i < file_header.number_of_information_elements; i++) {
142 /* read the TLV header */
143 bytes_read = file_read(&tlvh, sizeof tlvh, wth->fh);
144 if (bytes_read != sizeof tlvh) {
145 *err = file_error(wth->fh);
147 *err = WTAP_ERR_SHORT_READ;
150 offset += bytes_read;
152 tlvh.length = GUINT16_FROM_LE(tlvh.length);
153 if (tlvh.length < sizeof tlvh) {
154 *err = WTAP_ERR_BAD_RECORD;
155 *err_info = g_strdup_printf("Observer: bad record (TLV length %u < %lu)",
156 tlvh.length, (unsigned long)sizeof tlvh);
160 /* skip the TLV data */
161 seek_increment = tlvh.length - (int)sizeof tlvh;
162 if (seek_increment > 0) {
163 if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
166 offset += seek_increment;
169 /* get to the first packet */
170 file_header.offset_to_first_packet =
171 GUINT16_FROM_LE(file_header.offset_to_first_packet);
172 if (file_header.offset_to_first_packet < offset) {
173 *err = WTAP_ERR_BAD_RECORD;
174 *err_info = g_strdup_printf("Observer: bad record (offset to first packet %d < %d)",
175 file_header.offset_to_first_packet, offset);
178 seek_increment = file_header.offset_to_first_packet - offset;
179 if (seek_increment > 0) {
180 if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
184 /* pull off the packet header */
185 bytes_read = file_read(&packet_header, sizeof packet_header, wth->fh);
186 if (bytes_read != sizeof packet_header) {
187 *err = file_error(wth->fh);
193 /* check the packet's magic number; the magic number is all 8's,
194 so the byte order doesn't matter */
195 if (packet_header.packet_magic != observer_packet_magic) {
196 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
197 *err_info = g_strdup_printf("Observer: unsupported packet version %ul", packet_header.packet_magic);
201 /* check the data link type */
202 if (packet_header.network_type >= NUM_OBSERVER_ENCAPS) {
203 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
204 *err_info = g_strdup_printf("Observer: network type %u unknown or unsupported", packet_header.network_type);
207 wth->file_encap = observer_encap[packet_header.network_type];
209 wth->file_type = WTAP_FILE_NETWORK_INSTRUMENTS_V9;
211 /* set up the rest of the capture parameters */
212 wth->subtype_read = observer_read;
213 wth->subtype_seek_read = observer_seek_read;
214 wth->subtype_close = NULL;
215 wth->subtype_sequential_close = NULL;
216 wth->snapshot_length = 0; /* not available in header */
217 wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
219 /* reset the pointer to the first packet */
220 if (file_seek(wth->fh, file_header.offset_to_first_packet, SEEK_SET,
223 wth->data_offset = file_header.offset_to_first_packet;
230 /* Reads the next packet. */
231 static gboolean observer_read(wtap *wth, int *err, gchar **err_info,
235 packet_entry_header packet_header;
237 /* skip records other than data records */
239 *data_offset = wth->data_offset;
241 /* process the packet header, including TLVs */
242 offset = read_packet_header(wth->fh, &packet_header, err,
245 return FALSE; /* EOF or error */
247 wth->data_offset += offset;
249 if (packet_header.packet_type == PACKET_TYPE_DATA_PACKET)
252 /* skip to next packet */
253 if (!skip_to_next_packet(wth, offset,
254 packet_header.offset_to_next_packet, err, err_info))
255 return FALSE; /* EOF or error */
258 /* neglect frame markers for wiretap */
259 if (packet_header.network_size < 4) {
260 *err = WTAP_ERR_BAD_RECORD;
261 *err_info = g_strdup_printf("Observer: bad record: Packet length %u < 4",
262 packet_header.network_size);
266 /* set the wiretap packet header fields */
267 wth->phdr.pkt_encap = observer_encap[packet_header.network_type];
268 wth->phdr.len = packet_header.network_size - 4;
269 wth->phdr.caplen = MIN(packet_header.captured_size, wth->phdr.len);
271 (time_t) (packet_header.nano_seconds_since_2000/1000000000 + seconds1970to2000);
272 wth->phdr.ts.nsecs = (int) (packet_header.nano_seconds_since_2000%1000000000);
274 /* update the pseudo header */
275 switch (wth->file_encap) {
277 case WTAP_ENCAP_ETHERNET:
278 /* There is no FCS in the frame */
279 wth->pseudo_header.eth.fcs_len = 0;
283 /* set-up the packet buffer */
284 buffer_assure_space(wth->frame_buffer, packet_header.captured_size);
286 /* read the frame data */
287 if (!read_packet_data(wth->fh, packet_header.offset_to_frame, offset,
288 buffer_start_ptr(wth->frame_buffer), packet_header.captured_size,
291 wth->data_offset += packet_header.captured_size;
292 offset += packet_header.captured_size;
294 /* skip over any extra bytes following the frame data */
295 if (!skip_to_next_packet(wth, offset, packet_header.offset_to_next_packet,
302 /* Reads a packet at an offset. */
303 static gboolean observer_seek_read(wtap *wth, gint64 seek_off,
304 union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
305 int *err, gchar **err_info)
307 packet_entry_header packet_header;
310 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
313 /* process the packet header, including TLVs */
314 offset = read_packet_header(wth->random_fh, &packet_header, err,
317 return FALSE; /* EOF or error */
319 /* update the pseudo header */
320 switch (wth->file_encap) {
322 case WTAP_ENCAP_ETHERNET:
323 /* There is no FCS in the frame */
324 pseudo_header->eth.fcs_len = 0;
328 /* read the frame data */
329 if (!read_packet_data(wth->random_fh, packet_header.offset_to_frame,
330 offset, pd, length, err, err_info))
337 read_packet_header(FILE_T fh, packet_entry_header *packet_header, int *err,
348 /* pull off the packet header */
349 bytes_read = file_read(packet_header, sizeof *packet_header, fh);
350 if (bytes_read != sizeof *packet_header) {
351 *err = file_error(fh);
356 offset += bytes_read;
358 /* swap all multi-byte fields immediately */
359 packet_header->packet_magic = GUINT32_FROM_LE(packet_header->packet_magic);
360 packet_header->network_speed = GUINT32_FROM_LE(packet_header->network_speed);
361 packet_header->captured_size = GUINT16_FROM_LE(packet_header->captured_size);
362 packet_header->network_size = GUINT16_FROM_LE(packet_header->network_size);
363 packet_header->offset_to_frame = GUINT16_FROM_LE(packet_header->offset_to_frame);
364 packet_header->offset_to_next_packet = GUINT16_FROM_LE(packet_header->offset_to_next_packet);
365 packet_header->errors = GUINT16_FROM_LE(packet_header->errors);
366 packet_header->reserved = GUINT16_FROM_LE(packet_header->reserved);
367 packet_header->packet_number = GUINT64_FROM_LE(packet_header->packet_number);
368 packet_header->original_packet_number = GUINT64_FROM_LE(packet_header->original_packet_number);
369 packet_header->nano_seconds_since_2000 = GUINT64_FROM_LE(packet_header->nano_seconds_since_2000);
371 /* check the packet's magic number */
372 if (packet_header->packet_magic != observer_packet_magic) {
375 * Some files are zero-padded at the end. There is no warning of this
376 * in the previous packet header information, such as setting
377 * offset_to_next_packet to zero. So detect this situation by treating
378 * an all-zero header as a sentinel. Return EOF when it is encountered,
379 * rather than treat it as a bad record.
381 for (i = 0; i < sizeof *packet_header; i++) {
382 if (((guint8*) packet_header)[i] != 0)
385 if (i == sizeof *packet_header) {
390 *err = WTAP_ERR_BAD_RECORD;
391 *err_info = g_strdup_printf("Observer: bad record: Invalid magic number 0x%08x",
392 GUINT32_FROM_LE(packet_header->packet_magic));
396 /* process extra information */
397 for (i = 0; i < packet_header->number_of_information_elements; i++) {
398 /* read the TLV header */
399 bytes_read = file_read(&tlvh, sizeof tlvh, fh);
400 if (bytes_read != sizeof tlvh) {
401 *err = file_error(fh);
403 *err = WTAP_ERR_SHORT_READ;
406 offset += bytes_read;
408 tlvh.length = GUINT16_FROM_LE(tlvh.length);
409 if (tlvh.length < sizeof tlvh) {
410 *err = WTAP_ERR_BAD_RECORD;
411 *err_info = g_strdup_printf("Observer: bad record (TLV length %u < %lu)",
412 tlvh.length, (unsigned long)sizeof tlvh);
416 /* skip the TLV data */
417 seek_increment = tlvh.length - (int)sizeof tlvh;
418 if (seek_increment > 0) {
419 if (file_seek(fh, seek_increment, SEEK_CUR, err) == -1)
422 offset += seek_increment;
429 read_packet_data(FILE_T fh, int offset_to_frame, int offset, guint8 *pd,
430 int length, int *err, char **err_info)
434 /* get to the packet data */
435 if (offset_to_frame < offset) {
436 *err = WTAP_ERR_BAD_RECORD;
437 *err_info = g_strdup_printf("Observer: bad record (offset to packet data %d < %d)",
438 offset_to_frame, offset);
441 seek_increment = offset_to_frame - offset;
442 if (seek_increment > 0) {
443 if (file_seek(fh, seek_increment, SEEK_CUR, err) == -1)
447 /* read in the packet */
448 wtap_file_read_expected_bytes(pd, length, fh, err);
453 skip_to_next_packet(wtap *wth, int offset, int offset_to_next_packet, int *err,
458 if (offset_to_next_packet < offset) {
459 *err = WTAP_ERR_BAD_RECORD;
460 *err_info = g_strdup_printf("Observer: bad record (offset to next packet %d < %d)",
461 offset_to_next_packet, offset);
464 seek_increment = offset_to_next_packet - offset;
465 if (seek_increment > 0) {
466 if (file_seek(wth->fh, seek_increment, SEEK_CUR, err) == -1)
469 wth->data_offset += seek_increment;
474 guint64 packet_count;
478 /* Returns 0 if we could write the specified encapsulation type,
479 an error indication otherwise. */
480 int network_instruments_dump_can_write_encap(int encap)
482 /* per-packet encapsulations aren't supported */
483 if (encap == WTAP_ENCAP_PER_PACKET)
484 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
486 if (encap < 0 || (unsigned) encap >= NUM_FROM_WTAP_ENCAPS || from_wtap_encap[encap] == OBSERVER_UNDEFINED)
487 return WTAP_ERR_UNSUPPORTED_ENCAP;
492 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
494 gboolean network_instruments_dump_open(wtap_dumper *wdh, int *err)
496 capture_file_header file_header;
497 tlv_header comment_header;
499 struct tm *current_time;
501 niobserver_dump_t *niobserver;
503 wdh->subtype_write = observer_dump;
505 niobserver = (niobserver_dump_t *)g_malloc(sizeof(niobserver_dump_t));
506 wdh->priv = (void *)niobserver;
507 niobserver->packet_count = 0;
508 niobserver->network_type = from_wtap_encap[wdh->encap];
510 /* create the file comment */
512 current_time = localtime(&system_time);
513 memset(&comment, 0x00, sizeof(comment));
514 g_snprintf(comment, 64, "This capture was saved from Wireshark on %s", asctime(current_time));
516 /* create the file header */
517 if (fseek(wdh->fh, 0, SEEK_SET) == -1) {
521 memset(&file_header, 0x00, sizeof(capture_file_header));
522 g_strlcpy(file_header.observer_version, network_instruments_magic, 32);
523 file_header.offset_to_first_packet = (guint16) (sizeof(capture_file_header) + sizeof(tlv_header) + strlen(comment));
524 file_header.offset_to_first_packet = GUINT16_TO_LE(file_header.offset_to_first_packet);
525 file_header.number_of_information_elements = 1;
526 if(!wtap_dump_file_write(wdh, &file_header, sizeof(capture_file_header), err))
529 /* create the comment entry */
530 comment_header.type = GUINT16_TO_LE(INFORMATION_TYPE_COMMENT);
531 comment_header.length = (guint16) (sizeof(tlv_header) + strlen(comment));
532 comment_header.length = GUINT16_TO_LE(comment_header.length);
533 if(!wtap_dump_file_write(wdh, &comment_header, sizeof(tlv_header), err))
535 if(!wtap_dump_file_write(wdh, &comment, strlen(comment), err))
543 /* Write a record for a packet to a dump file.
544 Returns TRUE on success, FALSE on failure. */
545 static gboolean observer_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
546 const union wtap_pseudo_header *pseudo_header _U_, const guchar *pd,
549 niobserver_dump_t *niobserver = (niobserver_dump_t *)wdh->priv;
550 packet_entry_header packet_header;
551 guint64 capture_nanoseconds;
553 if (phdr->ts.secs < seconds1970to2000) {
554 if (phdr->ts.secs < 0)
555 capture_nanoseconds = 0;
557 capture_nanoseconds = phdr->ts.secs;
559 capture_nanoseconds = phdr->ts.secs - seconds1970to2000;
560 capture_nanoseconds = capture_nanoseconds*1000000000 + phdr->ts.nsecs;
562 memset(&packet_header, 0x00, sizeof(packet_entry_header));
563 packet_header.packet_magic = GUINT32_TO_LE(observer_packet_magic);
564 packet_header.network_speed = GUINT32_TO_LE(1000000);
565 packet_header.captured_size = GUINT16_TO_LE((guint16)phdr->caplen);
566 packet_header.network_size = GUINT16_TO_LE((guint16)(phdr->len+4));
567 packet_header.offset_to_frame = GUINT16_TO_LE(sizeof(packet_entry_header));
568 packet_header.offset_to_next_packet = GUINT16_TO_LE(sizeof(packet_entry_header) + phdr->caplen);
569 packet_header.network_type = niobserver->network_type;
570 packet_header.flags = 0x00;
571 packet_header.number_of_information_elements = 0;
572 packet_header.packet_type = PACKET_TYPE_DATA_PACKET;
573 packet_header.packet_number = GUINT64_TO_LE(niobserver->packet_count);
574 packet_header.original_packet_number = GUINT64_TO_LE(niobserver->packet_count);
575 niobserver->packet_count++;
576 packet_header.nano_seconds_since_2000 = GUINT64_TO_LE(capture_nanoseconds);
578 if (!wtap_dump_file_write(wdh, &packet_header, sizeof(packet_header),
582 if (!wtap_dump_file_write(wdh, pd, phdr->caplen, err))