/*
* 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 <stdio.h>
#ifdef GROK_TCPDUMP
#include "tcpdump.h"
#include <pcap.h>
/* 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 = "<unknown>";
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 */
syntax highlighted by Code2HTML, v. 0.9.1