1 /*
2  * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
3  *               2002, 2003, 2004
4  *	Ohio University.
5  *
6  * ---
7  *
8  * Starting with the release of tcptrace version 6 in 2001, tcptrace
9  * is licensed under the GNU General Public License (GPL).  We believe
10  * that, among the available licenses, the GPL will do the best job of
11  * allowing tcptrace to continue to be a valuable, freely-available
12  * and well-maintained tool for the networking community.
13  *
14  * Previous versions of tcptrace were released under a license that
15  * was much less restrictive with respect to how tcptrace could be
16  * used in commercial products.  Because of this, I am willing to
17  * consider alternate license arrangements as allowed in Section 10 of
18  * the GNU GPL.  Before I would consider licensing tcptrace under an
19  * alternate agreement with a particular individual or company,
20  * however, I would have to be convinced that such an alternative
21  * would be to the greater benefit of the networking community.
22  *
23  * ---
24  *
25  * This file is part of Tcptrace.
26  *
27  * Tcptrace was originally written and continues to be maintained by
28  * Shawn Ostermann with the help of a group of devoted students and
29  * users (see the file 'THANKS').  The work on tcptrace has been made
30  * possible over the years through the generous support of NASA GRC,
31  * the National Science Foundation, and Sun Microsystems.
32  *
33  * Tcptrace is free software; you can redistribute it and/or modify it
34  * under the terms of the GNU General Public License as published by
35  * the Free Software Foundation; either version 2 of the License, or
36  * (at your option) any later version.
37  *
38  * Tcptrace is distributed in the hope that it will be useful, but
39  * WITHOUT ANY WARRANTY; without even the implied warranty of
40  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
41  * General Public License for more details.
42  *
43  * You should have received a copy of the GNU General Public License
44  * along with Tcptrace (in the file 'COPYING'); if not, write to the
45  * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
46  * MA 02111-1307 USA
47  *
48  * Author:	Shawn Ostermann
49  * 		School of Electrical Engineering and Computer Science
50  * 		Ohio University
51  * 		Athens, OH
52  *		ostermann@cs.ohiou.edu
53  *		http://www.tcptrace.org/
54  */
55 #include "tcptrace.h"
56 static char const GCC_UNUSED copyright[] =
57     "@(#)Copyright (c) 2004 -- Ohio University.\n";
58 static char const GCC_UNUSED rcsid[] =
59     "@(#)$Header$";
60 
61 #include <stdio.h>
62 
63 #ifdef GROK_TCPDUMP
64 
65 #include "tcpdump.h"
66 #include <pcap.h>
67 
68 
69 
70 
71 /* external ref, in case missing in older version */
72 extern int pcap_offline_read(void *, int, pcap_handler, u_char *);
73 
74 /* global pointer, the pcap info header */
75 static pcap_t *pcap;
76 
77 
78 /* Interaction with pcap */
79 static struct ether_header eth_header;
80 #define EH_SIZE sizeof(struct ether_header)
81 static char *ip_buf;  /* [IP_MAXPACKET] */
82 static struct pcap_pkthdr *callback_phdr;
83 static void *callback_plast;
84 
85 
callback(char * user,struct pcap_pkthdr * phdr,char * buf)86 static int callback(
87     char *user,
88     struct pcap_pkthdr *phdr,
89     char *buf)
90 {
91     int type;
92     int iplen;
93     static int offset = -1;
94 
95     struct vlanh{
96       tt_uint16 vlan_num;
97       tt_uint16 vlan_proto;
98     } *vlanhptr;
99 
100     iplen = phdr->caplen;
101     if (iplen > IP_MAXPACKET)
102 	iplen = IP_MAXPACKET;
103     if (iplen < sizeof(struct ether_header) + sizeof(struct ip))
104       return(-1);
105 
106     type = pcap_datalink(pcap);
107 
108     /* remember the stuff we always save */
109     callback_phdr = phdr;
110 
111     /* kindof ugly, but about the only way to make them fit together :-( */
112     switch (type) {
113       case 100:
114 	/* for some reason, the windows version of tcpdump is using */
115 	/* this.  It looks just like ethernet to me */
116       case PCAP_DLT_EN10MB:
117 	offset = find_ip_eth(buf); /* Here we check if we are dealing with Straight Ethernet encapsulation, PPPoE or .1q VLAN encapsulation */
118 	memcpy(&eth_header, buf, EH_SIZE); /* save ether header */
119 	switch (offset)
120 	{
121 		case EH_SIZE + ETHER_VLAN_ENCAP_LEN:
122 			memcpy(&eth_header.ether_type, buf+EH_SIZE+2, 2);
123 			/* FALLTHROUGH */
124 		case EH_SIZE: /* straight Ethernet encapsulation */
125 			memcpy((char *)ip_buf,buf+offset,iplen-offset);
126 			callback_plast = ip_buf+iplen-offset-1;
127 			break;
128 		case PPPOE_SIZE: /* PPPoE encapsulation */
129 			/* we use a fake ether type here */
130 			eth_header.ether_type = htons(ETHERTYPE_IP);
131 			memcpy((char *)ip_buf,buf+offset,iplen-offset);
132 			callback_plast = ip_buf+iplen-offset-1;
133 			break;
134 	        case -1: /* Not an IP packet */
135 	                 /* Let's check if it is a VLAN header that
136 			  * caused us to receive -1, and if we had an IP
137 			  * packet buried inside */
138 	                if (eth_header.ether_type == htons(ETHERTYPE_VLAN)) {
139 			  vlanhptr=(struct vlanh*) (buf+EH_SIZE);
140 			  if ( (ntohs(vlanhptr->vlan_proto) == ETHERTYPE_IP) ||
141 			       (ntohs(vlanhptr->vlan_proto) == ETHERTYPE_IPV6)
142 			     ) {
143 			    offset=EH_SIZE+sizeof(struct vlanh);
144 			    memcpy((char *)ip_buf,buf+offset,iplen-offset);
145 			    callback_plast = ip_buf+iplen-offset-1;
146 			    /* Set ethernet type as whatever followed the dumb
147 			     * VLAN header to prevent the rest of the code
148 			     * from ignoring us.
149 			     */
150 			    eth_header.ether_type=vlanhptr->vlan_proto;
151 			    break;
152 			    }
153 			}
154 			return (-1);
155 		default: /* should not be used, but we never know ... */
156 			return (-1);
157 	}
158 	break;
159       case PCAP_DLT_IEEE802:
160 	/* just pretend it's "normal" ethernet */
161 	offset = 14;		/* 22 bytes of IEEE cruft */
162 	memcpy(&eth_header,buf,EH_SIZE);  /* save ether header */
163 	memcpy(ip_buf,buf+offset,iplen-offset);
164 	callback_plast = (char *)ip_buf+iplen-offset-1;
165 	break;
166       case PCAP_DLT_SLIP:
167 	memcpy(ip_buf,buf+16,iplen-16);
168 	callback_plast = (char *)ip_buf+iplen-16-1;
169 	break;
170       case PCAP_DLT_PPP:
171 	/* deals with raw PPP and also with HDLC PPP frames */
172 	offset = find_ip_ppp(buf);
173 	if (offset < 0) /* Not an IP packet */
174 		return (-1);
175 	memcpy((char *)ip_buf,buf+offset,iplen-offset);
176 	callback_plast = ip_buf+iplen-offset-1;
177 	break;
178       case PCAP_DLT_FDDI:
179 	if (offset < 0)
180 	      offset = find_ip_fddi(buf,iplen);
181 	if (offset < 0)
182 	      return(-1);
183 	memcpy((char *)ip_buf,buf+offset,iplen-offset);
184 	callback_plast = ip_buf+iplen-offset-1;
185 	break;
186       case PCAP_DLT_NULL:
187 	/* no phys header attached */
188 	offset = 4;
189 	memcpy((char *)ip_buf,buf+offset,iplen-offset);
190 	callback_plast = ip_buf+iplen-offset-1;
191 	break;
192       case PCAP_DLT_ATM_RFC1483:
193 	/* ATM RFC1483 - LLC/SNAP ecapsulated atm */
194 	memcpy((char*)ip_buf,buf+8,iplen-8);
195 	callback_plast = ip_buf+iplen-8-1;
196 	break;
197       case PCAP_DLT_RAW:
198 	/* raw IP */
199 	offset = 0;
200 	memcpy((char *)ip_buf,buf+offset,iplen-offset);
201 	callback_plast = ip_buf+iplen-offset-1;
202 	break;
203       case PCAP_DLT_LINUX_SLL:
204 	/* linux cooked socket */
205 	offset = 16;
206 	memcpy((char *)ip_buf, buf+offset, iplen-offset);
207 	callback_plast = ip_buf+iplen-offset-1;
208 	break;
209       // Patch sent by Brandon Eisenamann to passby 802.11, LLC/SNAP
210       // and Prism2 headers to get to the IP packet.
211       case PCAP_DLT_IEEE802_11:
212 	offset=24+8;// 802.11 header + LLC/SNAP header
213 	memcpy((char *)ip_buf, buf+offset, iplen-offset);
214 	callback_plast = ip_buf+iplen-offset-1;
215 	break;
216       case PCAP_DLT_IEEE802_11_RADIO:
217 	offset=64+24;//WLAN header + 802.11 header
218 	memcpy(&eth_header,buf,EH_SIZE); // save ethernet header
219 	memcpy((char *)ip_buf, buf+offset, iplen-offset);
220 	callback_plast = ip_buf+iplen-offset-1;
221 	break;
222       case PCAP_DLT_PRISM2:
223 	offset=144+24+8; // PRISM2+IEEE 802.11+ LLC/SNAP headers
224 	memcpy((char *)ip_buf, buf+offset, iplen-offset);
225 	callback_plast = ip_buf+iplen-offset-1;
226 	break;
227       case PCAP_DLT_C_HDLC:
228 	offset=4;
229 	memcpy((char *)ip_buf, buf+offset, iplen-offset);
230 	callback_plast = (char *)ip_buf+iplen-offset-1;
231 	break;
232       default:
233 	fprintf(stderr,"Don't understand link-level format (%d)\n", type);
234 
235 	exit(1);
236     }
237 
238     return(0);
239 }
240 
241 
242 /* currently only works for ETHERNET and FDDI */
243 static int
pread_tcpdump(struct timeval * ptime,int * plen,int * ptlen,void ** pphys,int * pphystype,struct ip ** ppip,void ** pplast)244 pread_tcpdump(
245     struct timeval	*ptime,
246     int		 	*plen,
247     int		 	*ptlen,
248     void		**pphys,
249     int			*pphystype,
250     struct ip		**ppip,
251     void		**pplast)
252 {
253     int ret;
254 
255     while (1) {
256 	if ((ret = pcap_offline_read(pcap,1,(pcap_handler)callback,0)) != 1) {
257 	    /* prob EOF */
258 
259 	    if (ret == -1) {
260 		char *error;
261 		error = pcap_geterr(pcap);
262 
263 		if (error && *error)
264 		    fprintf(stderr,"PCAP error: '%s'\n",pcap_geterr(pcap));
265 		/* else, it's just EOF */
266 	    }
267 
268 	    return(0);
269 	}
270 
271 	/* at least one tcpdump implementation (AIX) seems to be */
272 	/* storing NANOseconds in the usecs field of the timestamp. */
273 	/* This confuses EVERYTHING.  Try to compensate. */
274 	{
275 	    static Bool bogus_nanoseconds = FALSE;
276 	    if ((callback_phdr->ts.tv_usec >= US_PER_SEC) ||
277 		(bogus_nanoseconds)) {
278 		if (!bogus_nanoseconds) {
279 		    fprintf(stderr,
280 			    "tcpdump: attempting to adapt to bogus nanosecond timestamps\n");
281 		    bogus_nanoseconds = TRUE;
282 		}
283 		callback_phdr->ts.tv_usec /= 1000;
284 	    }
285 	}
286 
287 	/* fill in all of the return values */
288 	*pphys     = &eth_header;/* everything assumed to be ethernet */
289 	*pphystype = PHYS_ETHER; /* everything assumed to be ethernet */
290 	*ppip      = (struct ip *) ip_buf;
291 	*pplast    = callback_plast; /* last byte in IP packet */
292 	/* (copying time structure in 2 steps to avoid RedHat brain damage) */
293 	ptime->tv_usec = callback_phdr->ts.tv_usec;
294 	ptime->tv_sec = callback_phdr->ts.tv_sec;
295 	*plen      = callback_phdr->len;
296 	*ptlen     = callback_phdr->caplen;
297 
298 	/* if it's not IP, then skip it */
299 	if ((ntohs(eth_header.ether_type) != ETHERTYPE_IP) &&
300 	    (ntohs(eth_header.ether_type) != ETHERTYPE_IPV6)) {
301 	    if (debug > 2)
302 		fprintf(stderr,"pread_tcpdump: not an IP packet\n");
303 	    continue;
304 	}
305 
306 	return(1);
307     }
308 }
309 
310 
is_tcpdump(char * filename)311 pread_f *is_tcpdump(char *filename)
312 {
313     char errbuf[100];
314     char *physname = "<unknown>";
315     int type;
316 
317 #ifdef __WIN32
318       if ((pcap = pcap_open_offline(filename, errbuf)) == NULL) {
319 #else
320       if ((pcap = pcap_open_offline("-", errbuf)) == NULL) {
321 #endif /* __WIN32 */
322 	if (debug > 2)
323 	    fprintf(stderr,"PCAP said: '%s'\n", errbuf);
324 	rewind(stdin);
325 	return(NULL);
326     }
327 
328 
329     if (debug) {
330 	printf("Using 'pcap' version of tcpdump\n");
331 	if (debug > 1) {
332 	    printf("\tversion_major: %d\n", pcap_major_version(pcap));
333 	    printf("\tversion_minor: %d\n", pcap_minor_version(pcap));
334 	    printf("\tsnaplen: %d\n", pcap_snapshot(pcap));
335 	    printf("\tlinktype: %d\n", pcap_datalink(pcap));
336 	    printf("\tswapped: %d\n", pcap_is_swapped(pcap));
337 	}
338     }
339 
340     /* check the phys type (pretend everything is ethernet) */
341     memset(&eth_header,0,EH_SIZE);
342     switch (type = pcap_datalink(pcap)) {
343       case 100:
344       case PCAP_DLT_EN10MB:
345 	/* OK, we understand this one */
346 	physname = "Ethernet";
347 	break;
348       case PCAP_DLT_IEEE802:
349 	/* just pretend it's normal ethernet */
350 	physname = "Ethernet";
351 	break;
352       case PCAP_DLT_SLIP:
353 	eth_header.ether_type = htons(ETHERTYPE_IP);
354 	physname = "Slip";
355 	break;
356       case PCAP_DLT_PPP:
357 	eth_header.ether_type = htons(ETHERTYPE_IP);
358 	physname = "PPP or HDLC PPP";
359 	break;
360       case PCAP_DLT_FDDI:
361 	eth_header.ether_type = htons(ETHERTYPE_IP);
362 	physname = "FDDI";
363 	break;
364       case PCAP_DLT_NULL:
365 	eth_header.ether_type = htons(ETHERTYPE_IP);
366 	physname = "NULL";
367 	break;
368       case PCAP_DLT_ATM_RFC1483:
369 	eth_header.ether_type = htons(ETHERTYPE_IP);
370 	physname = "ATM, LLC/SNAP encapsulated";
371 	break;
372       case PCAP_DLT_RAW:
373 	eth_header.ether_type = htons(ETHERTYPE_IP);
374 	physname = "RAW_IP";
375 	break;
376       case PCAP_DLT_LINUX_SLL:
377 	/* linux cooked socket type */
378 	eth_header.ether_type = htons(ETHERTYPE_IP);
379 	physname = "Linux Cooked Socket";
380 	break;
381       case PCAP_DLT_IEEE802_11:
382 	eth_header.ether_type = htons(ETHERTYPE_IP);
383 	physname = "IEEE802_11";
384 	break;
385       case PCAP_DLT_IEEE802_11_RADIO:
386 	eth_header.ether_type = htons(ETHERTYPE_IP);
387 	physname = "IEEE802_11_RADIO";
388 	break;
389       case PCAP_DLT_PRISM2:
390 	eth_header.ether_type = htons(ETHERTYPE_IP);
391 	physname = "PRISM2";
392 	break;
393       case PCAP_DLT_C_HDLC:
394 	eth_header.ether_type = htons(ETHERTYPE_IP);
395 	physname = "Cisco HDLC";
396 	break;
397       default:
398         fprintf(stderr,"tcptrace did not understand link format (%d)!\n",type);
399         fprintf(stderr,
400 		"\t If you can give us a capture file with this link format\n\
401 \t or even better, a patch to decipher this format, we shall add it in, \n\
402 \t in a future release.\n");
403 	rewind(stdin);
404 	return(NULL);
405     }
406 
407     if (debug)
408 	fprintf(stderr,"Tcpdump format, physical type is %d (%s)\n",
409 		type, physname);
410 
411     /* set up some stuff */
412     ip_buf = MallocZ(IP_MAXPACKET);
413 
414 
415     return(pread_tcpdump);
416 }
417 
418 
419 /* support for writing a new pcap file */
420 
421 void
422 PcapSavePacket(
423     char *filename,
424     struct ip *pip,
425     void *plast)
426 {
427     static MFILE *f_savefile = NULL;
428     struct pcap_pkthdr phdr;
429     int wlen;
430 
431     if (f_savefile == NULL) {
432 	struct pcap_file_header fhdr;
433 
434 	/* try to open the file */
435 	if ((f_savefile = Mfopen(filename, "w")) == NULL) {
436 	    perror(filename);
437 	    exit(-1);
438 	}
439 
440 	/* make up the header info it wants */
441 	/* this comes from version 2.4, no pcap routine handy :-(  */
442 	fhdr.magic = TCPDUMP_MAGIC;
443 	fhdr.version_major = PCAP_VERSION_MAJOR;
444 	fhdr.version_minor = PCAP_VERSION_MINOR;
445 
446 	fhdr.thiszone = 0;	/* don't have this info, just make it up */
447 	fhdr.snaplen = 1000000;	/* don't have this info, just make it up */
448 	fhdr.linktype = PCAP_DLT_EN10MB; /* always Ethernet (10Mb) */
449 	fhdr.sigfigs = 0;
450 
451 	/* write the header */
452 	Mfwrite((char *)&fhdr, sizeof(fhdr), 1, f_savefile);
453 
454 	if (debug)
455 	    fprintf(stderr,"Created pcap save file '%s'\n", filename);
456     }
457 
458     /* create the packet header */
459     /* (copying time structure in 2 steps to avoid RedHat brain damage) */
460     phdr.ts.tv_sec = current_time.tv_sec;
461     phdr.ts.tv_usec = current_time.tv_usec;
462     phdr.caplen = (char *)plast - (char *)pip + 1;
463     phdr.caplen += EH_SIZE;	/* add in the ether header */
464     phdr.len = EH_SIZE + ntohs(PIP_LEN(pip));	/* probably this */
465 
466     /* write the packet header */
467     Mfwrite(&phdr, sizeof(phdr), 1, f_savefile);
468 
469     /* write a (bogus) ethernet header */
470     memset(&eth_header,0,EH_SIZE);
471     eth_header.ether_type = htons(ETHERTYPE_IP);
472     Mfwrite(&eth_header, sizeof(eth_header), 1, f_savefile);
473 
474     /* write the IP/TCP parts */
475     wlen = phdr.caplen - EH_SIZE;	/* remove the ether header */
476     Mfwrite(pip, wlen, 1, f_savefile);
477 }
478 
479 
480 
481 #else /* GROK_TCPDUMP */
482 
483 void
484 PcapSavePacket(
485     char *filename,
486     struct ip *pip,
487     void *plast)
488 {
489     fprintf(stderr,"\
490 Sorry, packet writing only supported with the pcap library\n\
491 compiled into the program (See GROK_TCPDUMP)\n");
492     exit(-2);
493 }
494 
495 
496 #endif /* GROK_TCPDUMP */
497