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(ð_header, buf, EH_SIZE); /* save ether header */
119 switch (offset)
120 {
121 case EH_SIZE + ETHER_VLAN_ENCAP_LEN:
122 memcpy(ð_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(ð_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(ð_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 = ð_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(ð_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(ð_header,0,EH_SIZE);
471 eth_header.ether_type = htons(ETHERTYPE_IP);
472 Mfwrite(ð_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