/* * * Copyright (c) 2003 Endace Technology Ltd, Hamilton, New Zealand. * All rights reserved. * * This software and documentation has been developed by Endace Technology Ltd. * along with the DAG PCI network capture cards. For further information please * visit http://www.endace.com/. * * Redistribution and use of software in source and binary forms and * documentation, with or without modification, are permitted provided * that the following conditions are met: * * 1. Redistributions of source code and documentation must retain the above * copyright notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Endace Technology Ltd., * Hamilton, New Zealand, and its contributors. * 4. Neither the name of Endace Technology nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY ENDACE TECHNOLOGY AND * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ENDACE TECHNOLOGY * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id: erf.c,v 5.4 2003/11/19 14:38:01 sdo Exp $ */ #include "tcptrace.h" static char const GCC_UNUSED copyright[] = "@(#)Copyright (c) 2003 -- Endace Technology Ltd, Hamilton, New Zealand\n"; static char const GCC_UNUSED rcsid[] = "@(#)$Header: /usr/local/cvs/tcptrace/erf.c,v 5.4 2003/11/19 14:38:01 sdo Exp $"; /* * erf - Endace ERF (Extensible Record Format) specific file reading stuff */ #ifdef GROK_ERF /* Defining SYS_STDIN which is fp for Windows and stdin for all other systems */ #ifdef __WIN32 static FILE *fp; #define SYS_STDIN fp #else #define SYS_STDIN stdin #endif /* __WIN32 */ /* Record type defines */ #define TYPE_LEGACY 0 #define TYPE_HDLC_POS 1 #define TYPE_ETH 2 #define TYPE_ATM 3 #define TYPE_AAL5 4 typedef struct pos_rec { unsigned int hdlc; unsigned char pload[1]; } pos_rec_t; typedef struct eth_rec { unsigned char offset; unsigned char pad; unsigned char dst[6]; unsigned char src[6]; unsigned short etype; unsigned char pload[1]; } eth_rec_t; typedef struct atm_rec { unsigned int header; unsigned char pload[1]; } atm_rec_t; #ifdef HAVE_LONG_LONG typedef unsigned long long erf_timestamp_t; #else typedef unsigned long erf_timestamp_t[2]; #endif typedef struct erf_record { erf_timestamp_t ts; unsigned char type; unsigned char flags; unsigned short rlen; unsigned short lctr; unsigned short wlen; union { pos_rec_t pos; eth_rec_t eth; atm_rec_t atm; } rec; } erf_record_t; #define ERF_HEADER_LEN 16 #define MAX_RECORD_LEN 0x10000 /* 64k */ #define RECORDS_FOR_ERF_CHECK 3 #define FCS_BITS 32 #ifndef min #define min(a, b) ((a) > (b) ? (b) : (a)) #endif /* * ATM snaplength */ #define ATM_SNAPLEN 48 /* * Size of ATM payload */ #define ATM_SLEN(h) ATM_SNAPLEN #define ATM_WLEN(h) ATM_SNAPLEN /* * Size of Ethernet payload */ #define ETHERNET_WLEN(h) (ntohs((h)->wlen) - (fcs_bits >> 3)) #define ETHERNET_SLEN(h) min(ETHERNET_WLEN(h), ntohs((h)->rlen) - ERF_HEADER_LEN - 2) /* * Size of HDLC payload */ #define HDLC_WLEN(h) (ntohs((h)->wlen) - (fcs_bits >> 3)) #define HDLC_SLEN(h) min(HDLC_WLEN(h), ntohs((h)->rlen) - ERF_HEADER_LEN) static struct ether_header eth_header; static erf_record_t *record; static int records_for_erf_check = RECORDS_FOR_ERF_CHECK; static int fcs_bits = FCS_BITS; /* * Convert little-endian to host order. */ #ifdef HAVE_LONG_LONG #define pletohll(p) ((unsigned long long)*((const unsigned char *)(p)+7)<<56| \ (unsigned long long)*((const unsigned char *)(p)+6)<<48| \ (unsigned long long)*((const unsigned char *)(p)+5)<<40| \ (unsigned long long)*((const unsigned char *)(p)+4)<<32| \ (unsigned long long)*((const unsigned char *)(p)+3)<<24| \ (unsigned long long)*((const unsigned char *)(p)+2)<<16| \ (unsigned long long)*((const unsigned char *)(p)+1)<<8| \ (unsigned long long)*((const unsigned char *)(p)+0)<<0) #else #define pletohl(p) ((unsigned long)*((const unsigned char *)(p)+3)<<24| \ (unsigned long)*((const unsigned char *)(p)+2)<<16| \ (unsigned long)*((const unsigned char *)(p)+1)<<8| \ (unsigned long)*((const unsigned char *)(p)+0)<<0) #endif /* return the next packet header */ static int pread_erf( struct timeval *ptime, int *plen, int *ptlen, void **pphys, int *pphystype, struct ip **ppip, void **pplast) { int rlen, psize; unsigned short ether_type = 0; /* read the next frames */ while (1) { if ((rlen=fread(record,1,ERF_HEADER_LEN,SYS_STDIN)) != ERF_HEADER_LEN) { if (debug && (rlen != 0)) fprintf(stderr,"Bad ERF packet header (len:%d)\n", rlen); return(0); } psize = ntohs(record->rlen) - ERF_HEADER_LEN; if ((rlen=fread((char *)record+ERF_HEADER_LEN,1,psize,SYS_STDIN)) != psize) { if (debug && (rlen != 0)) fprintf(stderr,"Bad ERF packet payload (len:%d)\n", rlen); return(0); } #ifdef HAVE_LONG_LONG { unsigned long long ts = pletohll(&record->ts); ptime->tv_sec = ts >> 32; ts = ((ts & 0xffffffffULL) * 1000 * 1000); ts += (ts & 0x80000000ULL) << 1; /* rounding */ ptime->tv_usec = ts >> 32; if (ptime->tv_usec >= 1000000) { ptime->tv_usec -= 1000000; ptime->tv_sec += 1; } } #else ptime->tv_sec = pletohl(&record->ts[1]); ptime->tv_usec = (unsigned long)((pletohl(&record->ts[0])*1000000.0)/0xffffffffUL); #endif switch (record->type) { case TYPE_ATM: *ptlen = ATM_SLEN(record); *plen = ATM_WLEN(record); *pphys = ð_header; ether_type = ntohs(((unsigned short *)&record->rec.atm.pload)[3]); *ppip = (struct ip *)&record->rec.atm.pload[8]; /* skip snap/llc */ *pplast = ((char *)*ppip)+*ptlen-8-1; break; case TYPE_ETH: *ptlen = ETHERNET_SLEN(record); *plen = ETHERNET_WLEN(record); *pphys = &record->rec.eth.dst; ether_type = ntohs(record->rec.eth.etype); *ppip = (struct ip *)&record->rec.eth.pload[0]; *pplast = ((char *)*ppip)+*ptlen-sizeof(struct ether_header)-1; break; case TYPE_HDLC_POS: *ptlen = HDLC_SLEN(record); *plen = HDLC_WLEN(record); *pphys = ð_header; /* Detect PPP and convert the Ethertype value */ if (ntohs(((unsigned short *)&record->rec.pos.hdlc)[0]) == 0xff03) { if (ntohs(((unsigned short *)&record->rec.pos.hdlc)[1]) == 0x0021) { ether_type = ETHERTYPE_IP; } } else { ether_type = ntohs(((unsigned short *)&record->rec.pos.hdlc)[1]); } *ppip = (struct ip *)&record->rec.pos.pload[0]; *pplast = ((char *)*ppip)+*ptlen-4-1; break; default: fprintf(stderr,"Unsupported ERF record type %d\n", record->type); exit(1); } *pphystype = PHYS_ETHER; /* if it's not IP, then skip it */ if (ether_type != ETHERTYPE_IP && ether_type != ETHERTYPE_IPV6) { if (debug > 2) fprintf(stderr,"pread_erf: not an IP packet\n"); continue; } return(1); } } /* * is_erf() is the input file in ERF format? */ pread_f * is_erf( char *filename) { int i, rlen; int psize, n; char *s; erf_timestamp_t prevts; memset(&prevts, 0, sizeof(prevts)); #ifdef __WIN32 if((fp = fopen(filename, "r")) == NULL) { perror(filename); exit(-1); } #endif /* __WIN32 */ /* * The wirelength value in the ERF file is needed to calculate the * original payload length. Unfortunately the value includes a * 0, 2 or 4 byte checksum for Ethernet and PoS, and there is no * way of telling which it is. So assume 4 bytes checksum unless * told differently through the environment. */ if ((s = getenv("ERF_FCS_BITS")) != NULL) { if ((n = atoi(s)) == 0 || n == 16|| n == 32) { fcs_bits = n; } } /* number of records to scan before deciding if this really is ERF (dflt=3) */ if ((s = getenv("ERF_RECORDS_TO_CHECK")) != NULL) { if ((n = atoi(s)) > 0 && n < 101) { records_for_erf_check = n; } } if (record == NULL && (record = malloc(MAX_RECORD_LEN)) == NULL) { fprintf(stderr,"No memory for ERF record buffer\n"); exit(1); } /* ERF is a little hard because there's no magic number */ for (i = 0; i < records_for_erf_check; i++) { erf_timestamp_t ts; if ((rlen=fread(record,1,ERF_HEADER_LEN,SYS_STDIN)) != ERF_HEADER_LEN) { if (rlen == 0) { break; /* eof */ } else { rewind(SYS_STDIN); return(NULL); } } /* fail on invalid record type, decreasing timestamps or non-zero pad-bits */ if (record->type == 0 || record->type > TYPE_AAL5 || (record->flags & 0xc0) != 0) { rewind(SYS_STDIN); return(NULL); } #ifdef HAVE_LONG_LONG if ((ts = pletohll(&record->ts)) < prevts) { rewind(SYS_STDIN); return(NULL); } #else ts[0] = pletohl(&record->ts[0]); /* frac */ ts[1] = pletohl(&record->ts[1]); /* sec */ if ((ts[1] < prevts[1]) || (ts[1] == prevts[1] && ts[0] < prevts[0])) { rewind(SYS_STDIN); return(NULL); } #endif memcpy(&prevts, &ts, sizeof(prevts)); psize = ntohs(record->rlen) - ERF_HEADER_LEN; if ((rlen=fread((char *)record+ERF_HEADER_LEN,1,psize,SYS_STDIN)) != psize) { rewind(SYS_STDIN); return(NULL); } } rewind(SYS_STDIN); /* There may be no physical header present, so make up one */ memset(ð_header, 0, sizeof(eth_header)); eth_header.ether_type = htons(ETHERTYPE_IP); if (debug) fprintf(stderr,"ERF format\n"); return(pread_erf); } #endif /* GROK_ERF */