/* Packet tracing - top level and generic routines, including hex/ascii * Copyright 1991 Phil Karn, KA9Q */ #include #include #include #include "global.h" #ifdef ANSIPROTO #include #endif #include "mbuf.h" #include "iface.h" #include "pktdrvr.h" #include "commands.h" #include "trace.h" static void ascii_dump __ARGS((FILE *fp,struct mbuf **bpp)); static void ctohex __ARGS((char *buf,int16 c)); static void fmtline __ARGS((FILE *fp,int16 addr,char *buf,int16 len)); static void hex_dump __ARGS((FILE *fp,struct mbuf **bpp)); static void showtrace __ARGS((struct iface *ifp)); /* Redefined here so that programs calling dump in the library won't pull * in the rest of the package */ static char nospace[] = "No space!!\n"; struct tracecmd Tracecmd[] = { "input", IF_TRACE_IN, IF_TRACE_IN, "-input", 0, IF_TRACE_IN, "output", IF_TRACE_OUT, IF_TRACE_OUT, "-output", 0, IF_TRACE_OUT, "broadcast", 0, IF_TRACE_NOBC, "-broadcast", IF_TRACE_NOBC, IF_TRACE_NOBC, "raw", IF_TRACE_RAW, IF_TRACE_RAW, "-raw", 0, IF_TRACE_RAW, "ascii", IF_TRACE_ASCII, IF_TRACE_ASCII|IF_TRACE_HEX, "-ascii", 0, IF_TRACE_ASCII|IF_TRACE_HEX, "hex", IF_TRACE_HEX, IF_TRACE_ASCII|IF_TRACE_HEX, "-hex", IF_TRACE_ASCII, IF_TRACE_ASCII|IF_TRACE_HEX, "off", 0, 0xffff, NULLCHAR, 0, 0 }; void dump(ifp,direction,type,bp) register struct iface *ifp; int direction; unsigned type; struct mbuf *bp; { struct mbuf *tbp; void (*func) __ARGS((FILE *,struct mbuf **,int)); int16 size; time_t timer; char *cp; if(ifp == NULL || (ifp->trace & direction) == 0) return; /* Nothing to trace */ switch(direction){ case IF_TRACE_IN: if((ifp->trace & IF_TRACE_NOBC) && (Tracef[type].addrtest != NULLFP) && (*Tracef[type].addrtest)(ifp,bp) == 0) return; /* broadcasts are suppressed */ time(&timer); cp = ctime(&timer); cp[24] = '\0'; fprintf(ifp->trfp,"\n%s - %s recv:\n",cp,ifp->name); break; case IF_TRACE_OUT: time(&timer); cp = ctime(&timer); cp[24] = '\0'; fprintf(ifp->trfp,"\n%s - %s sent:\n",cp,ifp->name); break; } if(bp == NULLBUF || (size = len_p(bp)) == 0){ fprintf(ifp->trfp,"empty packet!!\n"); return; } if(type < NCLASS) func = Tracef[type].tracef; else func = NULLVFP; dup_p(&tbp,bp,0,size); if(tbp == NULLBUF){ fprintf(ifp->trfp,nospace); return; } if(func != NULLVFP) (*func)(ifp->trfp,&tbp,1); if(ifp->trace & IF_TRACE_ASCII){ /* Dump only data portion of packet in ascii */ ascii_dump(ifp->trfp,&tbp); } else if(ifp->trace & IF_TRACE_HEX){ /* Dump entire packet in hex/ascii */ free_p(tbp); dup_p(&tbp,bp,0,len_p(bp)); if(tbp != NULLBUF) hex_dump(ifp->trfp,&tbp); else fprintf(ifp->trfp,nospace); } free_p(tbp); } /* Dump packet bytes, no interpretation */ void raw_dump(ifp,direction,bp) struct iface *ifp; int direction; struct mbuf *bp; { struct mbuf *tbp; /* Dump entire packet in hex/ascii */ fprintf(ifp->trfp,"\n******* raw packet dump (%s %s)\n", ((direction & IF_TRACE_OUT) ? "send" : "recv"),ifp->name); dup_p(&tbp,bp,0,len_p(bp)); if(tbp != NULLBUF) hex_dump(ifp->trfp,&tbp); else fprintf(ifp->trfp,nospace); fprintf(ifp->trfp,"*******\n"); free_p(tbp); return; } /* Dump an mbuf in hex */ static void hex_dump(fp,bpp) FILE *fp; register struct mbuf **bpp; { int16 n; int16 address; char buf[16]; if(bpp == NULLBUFP || *bpp == NULLBUF) return; address = 0; while((n = pullup(bpp,buf,sizeof(buf))) != 0){ fmtline(fp,address,buf,n); address += n; } } /* Dump an mbuf in ascii */ static void ascii_dump(fp,bpp) FILE *fp; register struct mbuf **bpp; { int c; register int16 tot; if(bpp == NULLBUFP || *bpp == NULLBUF) return; tot = 0; while((c = PULLCHAR(bpp)) != -1){ if((tot % 64) == 0) fprintf(fp,"%04x ",tot); putc(isprint(uchar(c)) ? c : '.',fp); if((++tot % 64) == 0) fprintf(fp,"\n"); } if((tot % 64) != 0) fprintf(fp,"\n"); } /* Print a buffer up to 16 bytes long in formatted hex with ascii * translation, e.g., * 0000: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>? */ static void fmtline(fp,addr,buf,len) FILE *fp; int16 addr; char *buf; int16 len; { char line[80]; register char *aptr,*cptr; register char c; memset(line,' ',sizeof(line)); ctohex(line,(int16)hibyte(addr)); ctohex(line+2,(int16)lobyte(addr)); aptr = &line[6]; cptr = &line[55]; while(len-- != 0){ c = *buf++; ctohex(aptr,(int16)uchar(c)); aptr += 3; c &= 0x7f; *cptr++ = isprint(uchar(c)) ? c : '.'; } *cptr++ = '\n'; fwrite(line,1,(unsigned)(cptr-line),fp); } /* Convert byte to two ascii-hex characters */ static void ctohex(buf,c) register char *buf; register int16 c; { static char hex[] = "0123456789abcdef"; *buf++ = hex[hinibble(c)]; *buf = hex[lonibble(c)]; } /* Modify or displace interface trace flags */ int dotrace(argc,argv,p) int argc; char *argv[]; void *p; { struct iface *ifp; struct tracecmd *tp; if(argc < 2){ for(ifp = Ifaces; ifp != NULLIF; ifp = ifp->next) showtrace(ifp); return 0; } if((ifp = if_lookup(argv[1])) == NULLIF){ tprintf("Interface %s unknown\n",argv[1]); return 1; } if(argc == 2){ showtrace(ifp); return 0; } /* MODIFY THIS TO HANDLE MULTIPLE OPTIONS */ if(argc >= 3){ for(tp = Tracecmd;tp->name != NULLCHAR;tp++) if(strncmp(tp->name,argv[2],strlen(argv[2])) == 0) break; if(tp->name != NULLCHAR) ifp->trace = (ifp->trace & ~tp->mask) | tp->val; else ifp->trace = htoi(argv[2]); } /* Always default to stdout unless trace file is given */ if(ifp->trfp != NULLFILE && ifp->trfp != stdout) fclose(ifp->trfp); ifp->trfp = stdout; if(ifp->trfile != NULLCHAR) free(ifp->trfile); ifp->trfile = NULLCHAR; if(argc >= 4){ if((ifp->trfp = fopen(argv[3],APPEND_TEXT)) == NULLFILE){ tprintf("Can't write to %s\n",argv[3]); ifp->trfp = stdout; } else { ifp->trfile = strdup(argv[3]); } } showtrace(ifp); return 0; } /* Display the trace flags for a particular interface */ static void showtrace(ifp) register struct iface *ifp; { if(ifp == NULLIF) return; tprintf("%s:",ifp->name); if(ifp->trace & (IF_TRACE_IN | IF_TRACE_OUT | IF_TRACE_RAW)){ if(ifp->trace & IF_TRACE_IN) tprintf(" input"); if(ifp->trace & IF_TRACE_OUT) tprintf(" output"); if(ifp->trace & IF_TRACE_NOBC) tprintf(" - no broadcasts"); if(ifp->trace & IF_TRACE_HEX) tprintf(" (Hex/ASCII dump)"); else if(ifp->trace & IF_TRACE_ASCII) tprintf(" (ASCII dump)"); else tprintf(" (headers only)"); if(ifp->trace & IF_TRACE_RAW) tprintf(" Raw output"); if(ifp->trfile != NULLCHAR) tprintf(" trace file: %s",ifp->trfile); tprintf("\n"); } else tprintf(" tracing off\n"); } /* shut down all trace files */ void shuttrace() { struct iface *ifp; for(ifp = Ifaces; ifp != NULLIF; ifp = ifp->next){ if(ifp->trfp != NULLFILE && ifp->trfp != stdout) fclose(ifp->trfp); if(ifp->trfile != NULLCHAR) free(ifp->trfile); ifp->trfile = NULLCHAR; ifp->trfp = NULLFILE; } } /* Log messages of the form * Tue Jan 31 00:00:00 1987 44.64.0.7:1003 open FTP */ #if defined(ANSIPROTO) void trace_log(struct iface *ifp,char *fmt, ...) { va_list ap; char *cp; long t; if(ifp->trfp == NULLFILE) return; time(&t); cp = ctime(&t); rip(cp); fprintf(ifp->trfp,"%s",cp); fprintf(ifp->trfp," - "); va_start(ap,fmt); vfprintf(ifp->trfp,fmt,ap); va_end(ap); fprintf(ifp->trfp,"\n"); } #else /*VARARGS2*/ void trace_log(ifp,fmt,arg1,arg2,arg3,arg4,arg5) struct iface *ifp; char *fmt; int arg1,arg2,arg3,arg4,arg5; { char *cp; long t; if(ifp->trfp == NULLFILE) return; time(&t); cp = ctime(&t); rip(cp); fprintf(ifp->trfp,"%s",cp); fprintf(ifp->trfp," - "); fprintf(ifp->trfp,fmt,arg1,arg2,arg3,arg4,arg5); fprintf(ifp->trfp,"\n"); } #endif