/* * TCPVIEW * * Author: Martin Hunt * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: martinh@cac.washington.edu * * * Copyright 1992 by the University of Washington * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appears in all copies and that both the * above copyright notice and this permission notice appear in supporting * documentation, and that the name of the University of Washington not be * used in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. This software is made * available "as is", and * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #ifndef lint static char rcsid[] = "@(#) $Header: /usr/staff/martinh/tcpview/RCS/sniffer_file.c,v 1.3 1993/04/22 20:35:19 martinh Exp $ (UW)"; #endif /* * routines to write and playback a Network General "Sniffer" compatible file * through tcpdump. Most of the information about Sniffer files came from the * Sniffer documentation. Some was gained by squinting at hex dumps. * * This code is brought to you by Corey Satten, corey@cac.wasnington.edu * and Martin Hunt, martinh@cac.washington.edu. The ifdefs are optional * and for speed, of course. */ #include #include #include #include #include #include #include "version.h" #include "savefile.h" #define MAXPKT 1600 /* >= any ether pkt */ static short byte_order = 0x100; static int low_offset; /* where is low byte in short */ static int high_offset; /* where is hi byte in short */ /* * The following moves byte-order determination from runtime to compile-time * if known. Don't worry, if you don't know, it will still work. */ #if defined(vax)||defined(mips)||defined(i386) # define LOW_OFFSET 0 # define HIGH_OFFSET 1 #endif #if defined(mc68000)||defined(sparc) # define LOW_OFFSET 1 # define HIGH_OFFSET 0 #endif #if !defined(HIGH_OFFSET)||!defined(LOW_OFFSET) # define LOW_OFFSET low_offset # define HIGH_OFFSET high_offset #endif /* * x should be of static storage class. If HIGH/LOW_OFFSET is constant, * these should result in compile-time access to the proper byte. */ #define low(x) (*(((char *)&(x))+LOW_OFFSET)) #define high(x) (*(((char *)&(x))+HIGH_OFFSET)) #define putbs(x,f) putc(low(x),f); putc(high(x),f) #define getbs(x,f) low(x)=getc(f); high(x)=getc(f) static void hdr_load(), hdr_print(), dh_print(), ver_print(), dh_load(); static FILE *sfd; /* sniffer output file descriptor */ static FILE *rfd; /* sniffer input file descriptor */ static int TimeZoneOffset; /* number of seconds from GMT */ static int frame_count = 0; /* number of records written to sniffer file */ static long last_offset = 0; /* position of start of last complete record */ static char s_magic[] = { /* magic header */ 'T', 'R', 'S', 'N', 'I', 'F', 'F', ' ', 'd', 'a', 't', 'a', ' ', ' ', ' ', ' ', 0x1a }; /* * putting the two structures all in one big structure allows us to * fwrite the entire structure at once for a substantial speedup on * little-endian machines (vax, etc) */ static struct s { struct s_hdr { /* data record header */ short type; short len; short rsvd; } s_hdr; struct s_dh { /* record data header */ ushort time_low; ushort time_med; ushort time_high; short size; char fs; char flags; short truesize; short rsvd; } s_dh; } s = { {4, 0, 0}, {0, 0, 0, 0, 0, 0x1c, 0, 0} }; static struct v { short maj_vers; /* major version number */ short min_vers; /* minor version number */ short time; short date; char type; char network; /* network type */ char format; char timeunit; /* timestamp units */ short rsvd[3]; /* reserved */ } V; /* values for V.timeunit */ double Usec[] = { 15.0, 0.838096, 15.0, 0.5, 2.0 }; double Timeunit; /* initializes reading. ** returns SFERR_BADF if incorrect file type, -1 on open failure, 0 otherwise */ int sniff_rinit(name, linktypep, thiszonep, snaplenp, precisionp) char *name; int *linktypep, *thiszonep, *snaplenp, *precisionp; { char buf[128]; void print2hexwindow(); /* Sniffer saves times in local format */ *thiszonep = 0; low_offset = *(((char *)&(byte_order))+0); high_offset = *(((char *)&(byte_order))+1); /* the Sniffer file format doesn't give the max frame size */ /* Each frame has a true size and a stored size, so we */ /* have to set 'snaplen' to the max possible size */ *snaplenp = MAXPKT; if (name[0]=='-' && name[1]=='\0') rfd = stdin; else if ( !(rfd=fopen(name, "r")) ) { fprintf(stderr,"tcpdump: fopen: "); perror(name); return(1); } /* read in magic header */ fread(buf,sizeof(s_magic),1,rfd); if(strncmp(buf,s_magic,sizeof(s_magic))) return SFERR_BADF; /* no magic header found */ hdr_load(); if (s.s_hdr.type != 1 ) /* version record should be next */ return SFERR_BADF; getbs(V.maj_vers, rfd); getbs(V.min_vers, rfd); getbs(V.time, rfd); getbs(V.date, rfd); V.type = getc(rfd); V.network = getc(rfd); V.format = getc(rfd); V.timeunit = getc(rfd); getbs(V.rsvd[0], rfd); getbs(V.rsvd[1], rfd); getbs(V.rsvd[2], rfd); sprintf(buf,"Sniffer save file\nCreated by Version %d.%d on %d-%d-%d at %d:%d:%d", V.maj_vers,V.min_vers, ((V.date&0x1e0)>>5),(V.date&0x1f), ((V.date&0xfe00)>>9)+1980, (V.time&0xfc00)>>11, (V.time&0x7e0)>>5, (V.time&0x1f)<<1); #ifdef TCPVIEW print2hexwindow(buf); #else fputs(buf,stderr); #endif if(V.timeunit==2) *precisionp = 5; else *precisionp = 6; Timeunit = Usec[V.timeunit]; if (linktypep) { switch (V.network) { case 1: *linktypep = DLT_EN10MB; break; case 2: *linktypep = DLT_ARCNET; break; case 7: *linktypep = DLT_PPP; break; case 8: *linktypep = DLT_SLIP; break; default: *linktypep = 0; } } return 0; } int sniff_winit(name, linktype, thiszone, snaplen, precision) char *name; int linktype, thiszone, snaplen, precision; { time_t tt; struct tm *t; TimeZoneOffset = thiszone; low_offset = *(((char *)&(byte_order))+0); high_offset = *(((char *)&(byte_order))+1); frame_count = 0; if (name[0]=='-' && name[1]=='\0') sfd = stdout; else if ( !(sfd=fopen(name ? name : "snif.enc", "w")) ) { fprintf(stderr,"tcpdump: fopen: "); perror(name); #ifdef TCPVIEW return(1); #else exit(1); #endif } /* write the magic header */ fwrite(s_magic, sizeof(s_magic), 1, sfd); /* write a version header */ s.s_hdr.type = 1; s.s_hdr.len = 18; hdr_print(); /* write the version record */ V.maj_vers = 3; V.min_vers = 9; (void)time( &tt ); t = localtime( &tt ); V.time = t->tm_sec>>1 | t->tm_min<<5 | t->tm_hour<<11; V.date = t->tm_mday | (t->tm_mon+1)<<5 | (t->tm_year-80)<<9; V.type = (char)4; switch ( linktype ) { case DLT_EN10MB: V.network = (char)1; break; case DLT_ARCNET: V.network = (char)2; break; case DLT_SLIP: V.network = (char)7; break; case DLT_PPP: V.network = (char)8; break; default: V.network = (char)9; break; } V.format = (char)1; if( precision > 5) V.timeunit = (char)4; else V.timeunit = (char)2; Timeunit = Usec[V.timeunit]; ver_print(); /* write fake type 6 record */ /* fwrite(s_sixrec, sizeof(s_sixrec), 1, sfd); last_offset = sizeof(s_magic) + sizeof(s_sixrec) + sizeof(struct v) + 6; */ last_offset = sizeof(s_magic) + sizeof(struct v) + 6; s.s_hdr.type = 4; return 0; } void sniff_end(mode) char *mode; { if (*mode == 'w') { /* end writing sniffer file */ s.s_hdr.type = 3; /* EOF */ s.s_hdr.len = 0; fseek(sfd, last_offset, 0); /* undo any partial records */ hdr_print(); fclose(sfd); fprintf(stderr,"wrote %d sniffer frames\n", frame_count); } if (*mode == 'r') { /* end re-reading a sniffer file */ fclose(rfd); } } void sniff_write(sp, tvp, length, caplen) u_char *sp; struct timeval *tvp; int length; int caplen; { double t, x; int days; extern int Wflag; tvp->tv_sec += TimeZoneOffset; x = 4.0*(double)(1<<30); t = (double)(tvp->tv_sec)*1.0e6 + (double)(tvp->tv_usec); days = t/8.64e10; t -= days*8.64e10; t /= Timeunit; s.s_dh.time_high = (u_short)(t/x); t -= (double)(s.s_dh.time_high)*x; s.s_dh.time_med = (u_short)(t/(double)(1<<16)); s.s_dh.time_low = (u_short)(t - (double)(s.s_dh.time_med)*(double)(1<<16)); s.s_hdr.len = (s.s_dh.size = caplen) + 14; s.s_dh.truesize = s.s_dh.size < length ? length : 0; if (HIGH_OFFSET) fwrite(&s, sizeof(s), 1, sfd); else hdr_print(), dh_print(); fwrite(sp, s.s_dh.size, 1, sfd); last_offset += sizeof(s.s_dh) + sizeof(s.s_hdr) + s.s_dh.size; ++frame_count; #ifndef TCPVIEW switch (Wflag) { case 2: /* a dot to stderr */ write(2,".",1); break; case 3: /* the normal print too */ ether_if_print(sp, tvp, length, caplen); break; } #endif return; } #ifndef TCPVIEW int sniff_read(filtp, cnt, snaplen, printit) struct bpf_program *filtp; int cnt, snaplen; void (*printit)(); { struct packet_header h; u_char *buf; struct bpf_insn *fcode = filtp->bf_insns; buf = (u_char *)malloc(snaplen); if (buf==NULL) return 1; while(sniff_next_packet(&h, buf) == 0) { if (bpf_filter(fcode,buf,h.len,h.caplen)) { if (cnt >= 0 && --cnt < 0 ) break; (*printit)(buf, &h.ts, h.len, h.caplen); } } free((char *)buf); return 0; } #endif /* not TCPVIEW */ int sniff_next_packet(hdr, buf, buflen) struct packet_header *hdr; u_char *buf; int buflen; { double t,x; hdr_load(); if (s.s_hdr.type == 3 || s.s_hdr.type == EOF ) return 1; if (s.s_hdr.type == 6 ) { /* infamous type 6 header detected */ if(fread(buf,s.s_hdr.len,1,rfd)==0) return 1; return (sniff_next_packet(hdr,buf)); } dh_load(); if(fread(buf, s.s_dh.size, 1, rfd)==0) return 1; x = 4.0*(double)(1<<30); t = (double)s.s_dh.time_low+(double)(s.s_dh.time_med)*65536.0 + (double)s.s_dh.time_high*x; t = t/1000000.0*Timeunit; /* set t equal to number of secs */ hdr->ts.tv_sec = (long)t; hdr->ts.tv_usec = (u_long)((t-(double)(hdr->ts.tv_sec))*1.0e6); hdr->len = s.s_dh.truesize ? s.s_dh.truesize : s.s_dh.size; hdr->caplen = s.s_dh.size; return 0; } static void hdr_print() { register FILE *f = sfd; putbs(s.s_hdr.type, f); putbs(s.s_hdr.len, f); putbs(s.s_hdr.rsvd, f); } static void hdr_load() { register FILE *f = rfd; getbs(s.s_hdr.type, f); getbs(s.s_hdr.len, f); getbs(s.s_hdr.rsvd, f); } static void dh_print() { register FILE *f = sfd; putbs(s.s_dh.time_low, f); putbs(s.s_dh.time_med, f); putbs(s.s_dh.time_high, f); putbs(s.s_dh.size, f); putc(s.s_dh.fs, f); putc(s.s_dh.flags, f); putbs(s.s_dh.truesize, f); putbs(s.s_dh.rsvd, f); } static void ver_print() { register FILE *f = sfd; putbs(V.maj_vers, f); putbs(V.min_vers, f); putbs(V.time, f); putbs(V.date, f); putc(V.type, f); putc(V.network, f); putc(V.format, f); putc(V.timeunit, f); putbs(V.rsvd[1], f); putbs(V.rsvd[2], f); putbs(V.rsvd[3], f); } static void dh_load() { register FILE *f = rfd; getbs(s.s_dh.time_low, f); getbs(s.s_dh.time_med, f); getbs(s.s_dh.time_high, f); getbs(s.s_dh.size, f); s.s_dh.fs = getc(f); s.s_dh.flags = getc(f); getbs(s.s_dh.truesize, f); getbs(s.s_dh.rsvd, f); }