/*
*
* 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 */
syntax highlighted by Code2HTML, v. 0.9.1