/* etherpeek.c * Routines for opening etherpeek files * Copyright (c) 2001, Daniel Thompson * * $Id: etherpeek.c,v 1.7 2001/12/05 07:19:11 guy Exp $ * * Wiretap Library * Copyright (c) 1998 by Gilbert Ramirez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "wtap-int.h" #include "file_wrappers.h" #include "buffer.h" #include "etherpeek.h" #ifdef HAVE_NETINET_IN_H #include #endif /* CREDITS * * This file decoder could not have been writen without examining how * tcptrace (http://www.tcptrace.org/) handles etherpeek files. */ /* master header */ typedef struct etherpeek_master_header { guint8 version; guint8 status; } etherpeek_master_header_t; #define ETHERPEEK_MASTER_HDR_SIZE 2 /* secondary header (Mac V5,V6,V7) */ typedef struct etherpeek_m567_header { guint32 filelength; guint32 numPackets; guint32 timeDate; guint32 timeStart; guint32 timeStop; guint32 reserved[7]; } etherpeek_m567_header_t; #define ETHERPEEK_M567_HDR_SIZE 48 /* full header */ typedef struct etherpeek_header { etherpeek_master_header_t master; union { etherpeek_m567_header_t m567; } secondary; } etherpeek_header_t; /* * Packet header (Mac V5, V6). * * NOTE: the time stamp, although it's a 32-bit number, is only aligned * on a 16-bit boundary. (Does this date back to 68K Macs? The 68000 * only required 16-bit alignment of 32-bit quantities, as did the 68010, * and the 68020/68030/68040 required no alignment.) * * As such, we cannot declare this as a C structure, as compilers on * most platforms will put 2 bytes of padding before the time stamp to * align it on a 32-bit boundary. * * So, instead, we #define numbers as the offsets of the fields. */ #define ETHERPEEK_M56_LENGTH_OFFSET 0 #define ETHERPEEK_M56_SLICE_LENGTH_OFFSET 2 #define ETHERPEEK_M56_FLAGS_OFFSET 4 #define ETHERPEEK_M56_STATUS_OFFSET 5 #define ETHERPEEK_M56_TIMESTAMP_OFFSET 6 #define ETHERPEEK_M56_DESTNUM_OFFSET 10 #define ETHERPEEK_M56_SRCNUM_OFFSET 12 #define ETHERPEEK_M56_PROTONUM_OFFSET 14 #define ETHERPEEK_M56_PROTOSTR_OFFSET 16 #define ETHERPEEK_M56_PKT_SIZE 24 /* 64-bit time in micro seconds from the (Mac) epoch */ typedef struct etherpeek_utime { guint32 upper; guint32 lower; } etherpeek_utime; /* * Packet header (Mac V7). * * This doesn't have the same alignment problem, but we do it with * #defines anyway. */ #define ETHERPEEK_M7_PROTONUM_OFFSET 0 #define ETHERPEEK_M7_LENGTH_OFFSET 2 #define ETHERPEEK_M7_SLICE_LENGTH_OFFSET 4 #define ETHERPEEK_M7_FLAGS_OFFSET 6 #define ETHERPEEK_M7_STATUS_OFFSET 7 #define ETHERPEEK_M7_TIMESTAMP_UPPER_OFFSET 8 #define ETHERPEEK_M7_TIMESTAMP_LOWER_OFFSET 12 #define ETHERPEEK_M7_PKT_SIZE 16 typedef struct etherpeek_encap_lookup { guint16 protoNum; int encap; } etherpeek_encap_lookup_t; static const unsigned int mac2unix = 2082844800u; static const etherpeek_encap_lookup_t etherpeek_encap[] = { { 1400, WTAP_ENCAP_ETHERNET } }; #define NUM_ETHERPEEK_ENCAPS \ (sizeof (etherpeek_encap) / sizeof (etherpeek_encap[0])) static gboolean etherpeek_read_m7(wtap *wth, int *err, long *data_offset); static gboolean etherpeek_read_m56(wtap *wth, int *err, long *data_offset); int etherpeek_open(wtap *wth, int *err) { etherpeek_header_t ep_hdr; /* etherpeek files to not start with a magic value large enough * to be unique hence we use the following algorithm to determine * the type of an unknown file * - populate the master header and reject file if there is no match * - populate the secondary header and check that the reserved space * is zero; there is an obvious flaw here so this algorithm will * probably need to be revisiting when improving etherpeek * support */ g_assert(sizeof(ep_hdr.master) == ETHERPEEK_MASTER_HDR_SIZE); wtap_file_read_unknown_bytes( &ep_hdr.master, sizeof(ep_hdr.master), wth->fh, err); wth->data_offset += sizeof(ep_hdr.master); /* switch on the file version */ switch (ep_hdr.master.version) { case 5: case 6: case 7: /* get the secondary header */ g_assert(sizeof(ep_hdr.secondary.m567) == ETHERPEEK_M567_HDR_SIZE); wtap_file_read_unknown_bytes( &ep_hdr.secondary.m567, sizeof(ep_hdr.secondary.m567), wth->fh, err); wth->data_offset += sizeof(ep_hdr.secondary.m567); if ((0 != ep_hdr.secondary.m567.reserved[0]) || (0 != ep_hdr.secondary.m567.reserved[1]) || (0 != ep_hdr.secondary.m567.reserved[2]) || (0 != ep_hdr.secondary.m567.reserved[3])) { /* still unknown */ return 0; } /* we have a match for a Mac V5, V6 or V7, * so it is worth preforming byte swaps */ ep_hdr.secondary.m567.filelength = ntohl(ep_hdr.secondary.m567.filelength); ep_hdr.secondary.m567.numPackets = ntohl(ep_hdr.secondary.m567.numPackets); ep_hdr.secondary.m567.timeDate = ntohl(ep_hdr.secondary.m567.timeDate); ep_hdr.secondary.m567.timeStart = ntohl(ep_hdr.secondary.m567.timeStart); ep_hdr.secondary.m567.timeStop = ntohl(ep_hdr.secondary.m567.timeStop); /* populate the pseudo header */ wth->pseudo_header.etherpeek.reference_time.tv_sec = ep_hdr.secondary.m567.timeDate - mac2unix; wth->pseudo_header.etherpeek.reference_time.tv_usec = 0; break; default: return 0; } /* at this point we have recognised the file type and have populated * the whole ep_hdr structure in host byte order */ switch (ep_hdr.master.version) { case 5: case 6: wth->file_type = WTAP_FILE_ETHERPEEK_MAC_V56; wth->subtype_read = etherpeek_read_m56; wth->subtype_seek_read = wtap_def_seek_read; break; case 7: wth->file_type = WTAP_FILE_ETHERPEEK_MAC_V7; wth->subtype_read = etherpeek_read_m7; wth->subtype_seek_read = wtap_def_seek_read; break; default: /* this is impossible */ g_assert_not_reached(); }; wth->file_encap = WTAP_ENCAP_PER_PACKET; wth->snapshot_length = 16384; /* just guessing */ return 1; } static gboolean etherpeek_read_m7(wtap *wth, int *err, long *data_offset) { guchar ep_pkt[ETHERPEEK_M7_PKT_SIZE]; guint16 protoNum; guint16 length; guint16 sliceLength; guint8 flags; guint8 status; etherpeek_utime timestamp; double t; unsigned int i; wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->fh, err); wth->data_offset += sizeof(ep_pkt); /* Extract the fields from the packet */ protoNum = pntohs(&ep_pkt[ETHERPEEK_M7_PROTONUM_OFFSET]); length = pntohs(&ep_pkt[ETHERPEEK_M7_LENGTH_OFFSET]); sliceLength = pntohs(&ep_pkt[ETHERPEEK_M7_SLICE_LENGTH_OFFSET]); flags = ep_pkt[ETHERPEEK_M7_FLAGS_OFFSET]; status = ep_pkt[ETHERPEEK_M7_STATUS_OFFSET]; timestamp.upper = pntohl(&ep_pkt[ETHERPEEK_M7_TIMESTAMP_UPPER_OFFSET]); timestamp.lower = pntohl(&ep_pkt[ETHERPEEK_M7_TIMESTAMP_LOWER_OFFSET]); /* force sliceLength to be the actual length of the packet */ if (0 == sliceLength) { sliceLength = length; } /* test for corrupt data */ if (sliceLength > WTAP_MAX_PACKET_SIZE) { *err = WTAP_ERR_BAD_RECORD; return FALSE; } *data_offset = wth->data_offset; /* read the frame data */ buffer_assure_space(wth->frame_buffer, sliceLength); wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer), sliceLength, wth->fh, err); wth->data_offset += sliceLength; /* fill in packet header values */ wth->phdr.len = length; wth->phdr.caplen = sliceLength; t = (double) timestamp.lower + (double) timestamp.upper * 4294967296.0; t -= (double) mac2unix * 1000000.0; wth->phdr.ts.tv_sec = (time_t) (t/1000000.0); wth->phdr.ts.tv_usec = (guint32) (t - (double) wth->phdr.ts.tv_sec * 1000000.0); wth->phdr.pkt_encap = WTAP_ENCAP_UNKNOWN; for (i=0; iphdr.pkt_encap = etherpeek_encap[i].encap; } } return TRUE; } static gboolean etherpeek_read_m56(wtap *wth, int *err, long *data_offset) { guchar ep_pkt[ETHERPEEK_M56_PKT_SIZE]; guint16 length; guint16 sliceLength; guint8 flags; guint8 status; guint32 timestamp; guint16 destNum; guint16 srcNum; guint16 protoNum; char protoStr[8]; unsigned int i; wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), wth->fh, err); wth->data_offset += sizeof(ep_pkt); /* Extract the fields from the packet */ length = pntohs(&ep_pkt[ETHERPEEK_M56_LENGTH_OFFSET]); sliceLength = pntohs(&ep_pkt[ETHERPEEK_M56_SLICE_LENGTH_OFFSET]); flags = ep_pkt[ETHERPEEK_M56_FLAGS_OFFSET]; status = ep_pkt[ETHERPEEK_M56_STATUS_OFFSET]; timestamp = pntohl(&ep_pkt[ETHERPEEK_M56_TIMESTAMP_OFFSET]); destNum = pntohs(&ep_pkt[ETHERPEEK_M56_DESTNUM_OFFSET]); srcNum = pntohs(&ep_pkt[ETHERPEEK_M56_SRCNUM_OFFSET]); protoNum = pntohs(&ep_pkt[ETHERPEEK_M56_PROTONUM_OFFSET]); memcpy(protoStr, &ep_pkt[ETHERPEEK_M56_PROTOSTR_OFFSET], sizeof protoStr); /* force sliceLength to be the actual length of the packet */ if (0 == sliceLength) { sliceLength = length; } /* test for corrupt data */ if (sliceLength > WTAP_MAX_PACKET_SIZE) { *err = WTAP_ERR_BAD_RECORD; return FALSE; } *data_offset = wth->data_offset; /* fill in packet header values */ wth->phdr.len = length; wth->phdr.caplen = sliceLength; /* timestamp is in milliseconds since reference_time */ wth->phdr.ts.tv_sec = wth->pseudo_header.etherpeek. reference_time.tv_sec + (timestamp / 1000); wth->phdr.ts.tv_usec = 1000 * (timestamp % 1000); wth->phdr.pkt_encap = WTAP_ENCAP_UNKNOWN; for (i=0; iphdr.pkt_encap = etherpeek_encap[i].encap; } } return TRUE; }