/* * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002, 2003, 2004 * Ohio University. * * --- * * Starting with the release of tcptrace version 6 in 2001, tcptrace * is licensed under the GNU General Public License (GPL). We believe * that, among the available licenses, the GPL will do the best job of * allowing tcptrace to continue to be a valuable, freely-available * and well-maintained tool for the networking community. * * Previous versions of tcptrace were released under a license that * was much less restrictive with respect to how tcptrace could be * used in commercial products. Because of this, I am willing to * consider alternate license arrangements as allowed in Section 10 of * the GNU GPL. Before I would consider licensing tcptrace under an * alternate agreement with a particular individual or company, * however, I would have to be convinced that such an alternative * would be to the greater benefit of the networking community. * * --- * * This file is part of Tcptrace. * * Tcptrace was originally written and continues to be maintained by * Shawn Ostermann with the help of a group of devoted students and * users (see the file 'THANKS'). The work on tcptrace has been made * possible over the years through the generous support of NASA GRC, * the National Science Foundation, and Sun Microsystems. * * Tcptrace 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. * * Tcptrace 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 Tcptrace (in the file 'COPYING'); if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * Author: Shawn Ostermann * School of Electrical Engineering and Computer Science * Ohio University * Athens, OH * ostermann@cs.ohiou.edu * http://www.tcptrace.org/ */ #include "tcptrace.h" static char const GCC_UNUSED copyright[] = "@(#)Copyright (c) 2004 -- Ohio University.\n"; static char const GCC_UNUSED rcsid[] = "@(#)$Header: /usr/local/cvs/tcptrace/tcpdump.c,v 5.24 2004/10/07 20:07:30 mramadas Exp $"; #include #ifdef GROK_TCPDUMP #include "tcpdump.h" #include /* external ref, in case missing in older version */ extern int pcap_offline_read(void *, int, pcap_handler, u_char *); /* global pointer, the pcap info header */ static pcap_t *pcap; /* Interaction with pcap */ static struct ether_header eth_header; #define EH_SIZE sizeof(struct ether_header) static char *ip_buf; /* [IP_MAXPACKET] */ static struct pcap_pkthdr *callback_phdr; static void *callback_plast; static int callback( char *user, struct pcap_pkthdr *phdr, char *buf) { int type; int iplen; static int offset = -1; struct vlanh{ tt_uint16 vlan_num; tt_uint16 vlan_proto; } *vlanhptr; iplen = phdr->caplen; if (iplen > IP_MAXPACKET) iplen = IP_MAXPACKET; type = pcap_datalink(pcap); /* remember the stuff we always save */ callback_phdr = phdr; /* kindof ugly, but about the only way to make them fit together :-( */ switch (type) { case 100: /* for some reason, the windows version of tcpdump is using */ /* this. It looks just like ethernet to me */ case PCAP_DLT_EN10MB: offset = find_ip_eth(buf); /* Here we check if we are dealing with Straight Ethernet encapsulation, PPPoE or .1q VLAN encapsulation */ memcpy(ð_header, buf, EH_SIZE); /* save ether header */ switch (offset) { case EH_SIZE + ETHER_VLAN_ENCAP_LEN: memcpy(ð_header.ether_type, buf+EH_SIZE+2, 2); /* FALLTHROUGH */ case EH_SIZE: /* straight Ethernet encapsulation */ memcpy((char *)ip_buf,buf+offset,iplen-offset); callback_plast = ip_buf+iplen-offset-1; break; case PPPOE_SIZE: /* PPPoE encapsulation */ /* we use a fake ether type here */ eth_header.ether_type = htons(ETHERTYPE_IP); memcpy((char *)ip_buf,buf+offset,iplen-offset); callback_plast = ip_buf+iplen-offset-1; break; case -1: /* Not an IP packet */ /* Let's check if it is a VLAN header that * caused us to receive -1, and if we had an IP * packet buried inside */ if (eth_header.ether_type == htons(ETHERTYPE_VLAN)) { vlanhptr=(struct vlanh*) (buf+EH_SIZE); if ( (ntohs(vlanhptr->vlan_proto) == ETHERTYPE_IP) || (ntohs(vlanhptr->vlan_proto) == ETHERTYPE_IPV6) ) { offset=EH_SIZE+sizeof(struct vlanh); memcpy((char *)ip_buf,buf+offset,iplen-offset); callback_plast = ip_buf+iplen-offset-1; /* Set ethernet type as whatever followed the dumb * VLAN header to prevent the rest of the code * from ignoring us. */ eth_header.ether_type=vlanhptr->vlan_proto; break; } } return (-1); default: /* should not be used, but we never know ... */ return (-1); } break; case PCAP_DLT_IEEE802: /* just pretend it's "normal" ethernet */ offset = 14; /* 22 bytes of IEEE cruft */ memcpy(ð_header,buf,EH_SIZE); /* save ether header */ memcpy(ip_buf,buf+offset,iplen-offset); callback_plast = (char *)ip_buf+iplen-offset-1; break; case PCAP_DLT_SLIP: memcpy(ip_buf,buf+16,iplen-16); callback_plast = (char *)ip_buf+iplen-16-1; break; case PCAP_DLT_PPP: /* deals with raw PPP and also with HDLC PPP frames */ offset = find_ip_ppp(buf); if (offset < 0) /* Not an IP packet */ return (-1); memcpy((char *)ip_buf,buf+offset,iplen-offset); callback_plast = ip_buf+iplen-offset-1; break; case PCAP_DLT_FDDI: if (offset < 0) offset = find_ip_fddi(buf,iplen); if (offset < 0) return(-1); memcpy((char *)ip_buf,buf+offset,iplen-offset); callback_plast = ip_buf+iplen-offset-1; break; case PCAP_DLT_NULL: /* no phys header attached */ offset = 4; memcpy((char *)ip_buf,buf+offset,iplen-offset); callback_plast = ip_buf+iplen-offset-1; break; case PCAP_DLT_ATM_RFC1483: /* ATM RFC1483 - LLC/SNAP ecapsulated atm */ memcpy((char*)ip_buf,buf+8,iplen-8); callback_plast = ip_buf+iplen-8-1; break; case PCAP_DLT_RAW: /* raw IP */ offset = 0; memcpy((char *)ip_buf,buf+offset,iplen-offset); callback_plast = ip_buf+iplen-offset-1; break; case PCAP_DLT_LINUX_SLL: /* linux cooked socket */ offset = 16; memcpy((char *)ip_buf, buf+offset, iplen-offset); callback_plast = ip_buf+iplen-offset-1; break; // Patch sent by Brandon Eisenamann to passby 802.11, LLC/SNAP // and Prism2 headers to get to the IP packet. case PCAP_DLT_IEEE802_11: offset=24+8;// 802.11 header + LLC/SNAP header memcpy((char *)ip_buf, buf+offset, iplen-offset); callback_plast = ip_buf+iplen-offset-1; break; case PCAP_DLT_IEEE802_11_RADIO: offset=64+24;//WLAN header + 802.11 header memcpy(ð_header,buf,EH_SIZE); // save ethernet header memcpy((char *)ip_buf, buf+offset, iplen-offset); callback_plast = ip_buf+iplen-offset-1; break; case PCAP_DLT_PRISM2: offset=144+24+8; // PRISM2+IEEE 802.11+ LLC/SNAP headers memcpy((char *)ip_buf, buf+offset, iplen-offset); callback_plast = ip_buf+iplen-offset-1; break; case PCAP_DLT_C_HDLC: offset=4; memcpy((char *)ip_buf, buf+offset, iplen-offset); callback_plast = (char *)ip_buf+iplen-offset-1; break; default: fprintf(stderr,"Don't understand link-level format (%d)\n", type); exit(1); } return(0); } /* currently only works for ETHERNET and FDDI */ static int pread_tcpdump( struct timeval *ptime, int *plen, int *ptlen, void **pphys, int *pphystype, struct ip **ppip, void **pplast) { int ret; while (1) { if ((ret = pcap_offline_read(pcap,1,(pcap_handler)callback,0)) != 1) { /* prob EOF */ if (ret == -1) { char *error; error = pcap_geterr(pcap); if (error && *error) fprintf(stderr,"PCAP error: '%s'\n",pcap_geterr(pcap)); /* else, it's just EOF */ } return(0); } /* at least one tcpdump implementation (AIX) seems to be */ /* storing NANOseconds in the usecs field of the timestamp. */ /* This confuses EVERYTHING. Try to compensate. */ { static Bool bogus_nanoseconds = FALSE; if ((callback_phdr->ts.tv_usec >= US_PER_SEC) || (bogus_nanoseconds)) { if (!bogus_nanoseconds) { fprintf(stderr, "tcpdump: attempting to adapt to bogus nanosecond timestamps\n"); bogus_nanoseconds = TRUE; } callback_phdr->ts.tv_usec /= 1000; } } /* fill in all of the return values */ *pphys = ð_header;/* everything assumed to be ethernet */ *pphystype = PHYS_ETHER; /* everything assumed to be ethernet */ *ppip = (struct ip *) ip_buf; *pplast = callback_plast; /* last byte in IP packet */ /* (copying time structure in 2 steps to avoid RedHat brain damage) */ ptime->tv_usec = callback_phdr->ts.tv_usec; ptime->tv_sec = callback_phdr->ts.tv_sec; *plen = callback_phdr->len; *ptlen = callback_phdr->caplen; /* if it's not IP, then skip it */ if ((ntohs(eth_header.ether_type) != ETHERTYPE_IP) && (ntohs(eth_header.ether_type) != ETHERTYPE_IPV6)) { if (debug > 2) fprintf(stderr,"pread_tcpdump: not an IP packet\n"); continue; } return(1); } } pread_f *is_tcpdump(char *filename) { char errbuf[100]; char *physname = ""; int type; #ifdef __WIN32 if ((pcap = pcap_open_offline(filename, errbuf)) == NULL) { #else if ((pcap = pcap_open_offline("-", errbuf)) == NULL) { #endif /* __WIN32 */ if (debug > 2) fprintf(stderr,"PCAP said: '%s'\n", errbuf); rewind(stdin); return(NULL); } if (debug) { printf("Using 'pcap' version of tcpdump\n"); if (debug > 1) { printf("\tversion_major: %d\n", pcap_major_version(pcap)); printf("\tversion_minor: %d\n", pcap_minor_version(pcap)); printf("\tsnaplen: %d\n", pcap_snapshot(pcap)); printf("\tlinktype: %d\n", pcap_datalink(pcap)); printf("\tswapped: %d\n", pcap_is_swapped(pcap)); } } /* check the phys type (pretend everything is ethernet) */ memset(ð_header,0,EH_SIZE); switch (type = pcap_datalink(pcap)) { case 100: case PCAP_DLT_EN10MB: /* OK, we understand this one */ physname = "Ethernet"; break; case PCAP_DLT_IEEE802: /* just pretend it's normal ethernet */ physname = "Ethernet"; break; case PCAP_DLT_SLIP: eth_header.ether_type = htons(ETHERTYPE_IP); physname = "Slip"; break; case PCAP_DLT_PPP: eth_header.ether_type = htons(ETHERTYPE_IP); physname = "PPP or HDLC PPP"; break; case PCAP_DLT_FDDI: eth_header.ether_type = htons(ETHERTYPE_IP); physname = "FDDI"; break; case PCAP_DLT_NULL: eth_header.ether_type = htons(ETHERTYPE_IP); physname = "NULL"; break; case PCAP_DLT_ATM_RFC1483: eth_header.ether_type = htons(ETHERTYPE_IP); physname = "ATM, LLC/SNAP encapsulated"; break; case PCAP_DLT_RAW: eth_header.ether_type = htons(ETHERTYPE_IP); physname = "RAW_IP"; break; case PCAP_DLT_LINUX_SLL: /* linux cooked socket type */ eth_header.ether_type = htons(ETHERTYPE_IP); physname = "Linux Cooked Socket"; break; case PCAP_DLT_IEEE802_11: eth_header.ether_type = htons(ETHERTYPE_IP); physname = "IEEE802_11"; break; case PCAP_DLT_IEEE802_11_RADIO: eth_header.ether_type = htons(ETHERTYPE_IP); physname = "IEEE802_11_RADIO"; break; case PCAP_DLT_PRISM2: eth_header.ether_type = htons(ETHERTYPE_IP); physname = "PRISM2"; break; case PCAP_DLT_C_HDLC: eth_header.ether_type = htons(ETHERTYPE_IP); physname = "Cisco HDLC"; break; default: fprintf(stderr,"tcptrace did not understand link format (%d)!\n",type); fprintf(stderr, "\t If you can give us a capture file with this link format\n\ \t or even better, a patch to decipher this format, we shall add it in, \n\ \t in a future release.\n"); rewind(stdin); return(NULL); } if (debug) fprintf(stderr,"Tcpdump format, physical type is %d (%s)\n", type, physname); /* set up some stuff */ ip_buf = MallocZ(IP_MAXPACKET); return(pread_tcpdump); } /* support for writing a new pcap file */ void PcapSavePacket( char *filename, struct ip *pip, void *plast) { static MFILE *f_savefile = NULL; struct pcap_pkthdr phdr; int wlen; if (f_savefile == NULL) { struct pcap_file_header fhdr; /* try to open the file */ if ((f_savefile = Mfopen(filename, "w")) == NULL) { perror(filename); exit(-1); } /* make up the header info it wants */ /* this comes from version 2.4, no pcap routine handy :-( */ fhdr.magic = TCPDUMP_MAGIC; fhdr.version_major = PCAP_VERSION_MAJOR; fhdr.version_minor = PCAP_VERSION_MINOR; fhdr.thiszone = 0; /* don't have this info, just make it up */ fhdr.snaplen = 1000000; /* don't have this info, just make it up */ fhdr.linktype = PCAP_DLT_EN10MB; /* always Ethernet (10Mb) */ fhdr.sigfigs = 0; /* write the header */ Mfwrite((char *)&fhdr, sizeof(fhdr), 1, f_savefile); if (debug) fprintf(stderr,"Created pcap save file '%s'\n", filename); } /* create the packet header */ /* (copying time structure in 2 steps to avoid RedHat brain damage) */ phdr.ts.tv_sec = current_time.tv_sec; phdr.ts.tv_usec = current_time.tv_usec; phdr.caplen = (char *)plast - (char *)pip + 1; phdr.caplen += EH_SIZE; /* add in the ether header */ phdr.len = EH_SIZE + ntohs(PIP_LEN(pip)); /* probably this */ /* write the packet header */ Mfwrite(&phdr, sizeof(phdr), 1, f_savefile); /* write a (bogus) ethernet header */ memset(ð_header,0,EH_SIZE); eth_header.ether_type = htons(ETHERTYPE_IP); Mfwrite(ð_header, sizeof(eth_header), 1, f_savefile); /* write the IP/TCP parts */ wlen = phdr.caplen - EH_SIZE; /* remove the ether header */ Mfwrite(pip, wlen, 1, f_savefile); } #else /* GROK_TCPDUMP */ void PcapSavePacket( char *filename, struct ip *pip, void *plast) { fprintf(stderr,"\ Sorry, packet writing only supported with the pcap library\n\ compiled into the program (See GROK_TCPDUMP)\n"); exit(-2); } #endif /* GROK_TCPDUMP */