-
Notifications
You must be signed in to change notification settings - Fork 0
/
ipv6.c
718 lines (614 loc) · 18 KB
/
ipv6.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
/*
* 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: Nasseef Abukamail
* School of Electrical Engineering and Computer Science
* Ohio University
* Athens, OH
* 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/ipv6.c,v 5.26 2004/11/04 21:55:37 sdo Exp $";
/* the names of IPv6 extensions that we understand */
char *
ipv6_header_name(
u_char nextheader)
{
switch (nextheader) {
case IPPROTO_DSTOPTS: return("Destinations options");
case IPPROTO_FRAGMENT: return("Fragment header");
case IPPROTO_HOPOPTS: return("Hop by hop");
case IPPROTO_NONE: return("No next header");
case IPPROTO_ROUTING: return("Routing header");
case IPPROTO_ICMPV6: return("IPv6 ICMP");
case IPPROTO_TCP: return("TCP");
case IPPROTO_UDP: return("UDP");
default: return("<unknown>");
}
}
/* given a next header type and a pointer to the header, return a pointer
to the next extension header and type */
struct ipv6_ext *
ipv6_nextheader(
void *pheader0,
u_char *pnextheader)
{
struct ipv6_ext *pheader = pheader0;
switch (*pnextheader) {
/* nothing follows these... */
case IPPROTO_TCP:
case IPPROTO_NONE:
case IPPROTO_ICMPV6:
case IPPROTO_UDP:
return(NULL);
/* somebody follows these */
case IPPROTO_HOPOPTS:
case IPPROTO_ROUTING:
case IPPROTO_DSTOPTS:
*pnextheader = pheader->ip6ext_nheader;
/* sanity check, if length is 0, terminate */
/* As per RFC 2460 : ip6ext_len specifies the extended
header length, in units of 8 octets *not including* the
first 8 octets. So ip6ext_len can be 0 and hence,
we cannot perform the sanity check any more.
Hence commenting out the sanity check - Mani*/
/* if (pheader->ip6ext_len == 0)
return(NULL); */
return((struct ipv6_ext *)
((char *)pheader + 8 + (pheader->ip6ext_len)*8));
/* I don't understand them. Just save the type and return a NULL */
default:
*pnextheader = pheader->ip6ext_nheader;
return(NULL);
}
}
/*
* findheader: find and return a pointer to a header.
* Skips either ip or ipv6 headers
* return values: 0 - found header
* 1 - correct protocol, invalid packet, cannot return header
* -1 - different protocol, cannot return header
*/
static int
findheader(
u_int ipproto,
struct ip *pip,
void **pphdr,
void **pplast)
{
struct ipv6 *pip6 = (struct ipv6 *)pip;
char nextheader;
struct ipv6_ext *pheader;
void *theheader;
/* IPv4 is easy */
if (PIP_ISV4(pip)) {
/* make sure it's what we want */
if (pip->ip_p != ipproto)
return (-1);
/* check the fragment field, if it's not the first fragment,
it's useless (offset part of field must be 0 */
if ((ntohs(pip->ip_off)&0x1fff) != 0) {
if (debug>1) {
printf("findheader: Skipping IPv4 non-initial fragment\n");
if (debug > 2) {
printpacket(100,100,NULL,0,pip,*pplast,NULL);
}
}
return (1);
}
/* OK, it starts here */
theheader = ((char *)pip + 4*IP_HL(pip));
/* adjust plast in accordance with ip_len (really short packets get garbage) */
if (((char *)pip + ntohs(pip->ip_len) - 1) < (char *)(*pplast)) {
*pplast = (char *)((char *)pip + ntohs(pip->ip_len));
}
#ifdef OLD
/* this is better verified when used, the error message is better */
/* make sure the whole header is there */
if ((char *)ptcp + (sizeof struct tcphdr) - 1 > (char *)*pplast) {
/* part of the header is missing */
return (1);
}
#endif
*pphdr = theheader;
return (0);
}
/* otherwise, we only understand IPv6 */
if (!PIP_ISV6(pip))
return (-1);
/* find the first header */
nextheader = pip6->ip6_nheader;
pheader = (struct ipv6_ext *)(pip6+1);
/* loop until we find the header we want or give up */
while (1) {
/* sanity check, if we're reading bogus header, the length might */
/* be wonky, so make sure before you dereference anything!! */
if ((char *)pheader < (char *)pip) {
if (debug>1)
printf("findheader: bad extension header math, skipping packet\n");
return (1);
}
/* make sure we're still within the packet */
/* might be truncated, or might be bad header math */
if ((char *)pheader > (char *)*pplast) {
if (debug>3)
printf("findheader: packet truncated before finding header\n");
return (1);
}
/* this is what we want */
if (nextheader == ipproto) {
*pphdr = pheader;
return (0);
}
switch (nextheader) {
case IPPROTO_TCP:
return (-1); /* didn't find it */
case IPPROTO_UDP:
return (-1); /* didn't find it */
/* fragmentation */
case IPPROTO_FRAGMENT:
{
struct ipv6_ext_frag *pfrag = (struct ipv6_ext_frag *)pheader;
/* if this isn't the FIRST fragment, there won't be a TCP header
anyway */
if ((pfrag->ip6ext_fr_offset&0xfc) != 0) {
/* the offset is non-zero */
if (debug>1)
printf("findheader: Skipping IPv6 non-initial fragment\n");
return (1);
}
/* otherwise it's either an entire segment or the first fragment */
nextheader = pfrag->ip6ext_fr_nheader;
/* Pass to the next octet following the fragmentation
header */
pheader = (struct ipv6_ext *)
((char *)pheader + sizeof(struct ipv6_ext_frag));
break;
}
/* headers we just skip over */
case IPPROTO_HOPOPTS:
case IPPROTO_ROUTING:
case IPPROTO_DSTOPTS:
nextheader = pheader->ip6ext_nheader;
/* As per RFC 2460 : ip6ext_len specifies the extended
header length, in units of 8 octets *not including* the
first 8 octets. */
pheader = (struct ipv6_ext *)
((char *)pheader + 8 + (pheader->ip6ext_len)*8);
break;
/* non-tcp protocols, so we're finished. */
case IPPROTO_NONE:
case IPPROTO_ICMPV6:
return (-1); /* didn't find it */
/* I "think" that we can just skip over it, but better be careful */
default:
nextheader = pheader->ip6ext_nheader;
pheader = (struct ipv6_ext *)
((char *)pheader + 8 + (pheader->ip6ext_len)*8);
break;
} /* end switch */
} /* end loop */
/* shouldn't get here, but just in case :-) */
return (-1);
}
/* Added Aug 31, 2001 -- Avinash.
* getroutingheader: return a pointer to the routing header in an ipv6 packet.
* Looks through all the IPv6 extension headers for the routing header.
* Used while computing the IPv6 checksums.
*/
int
getroutingheader(
struct ip *pip,
struct ipv6_ext **ppipv6_ext,
void **pplast)
{
int ret_val = findheader(IPPROTO_ROUTING, pip, (void **)ppipv6_ext, pplast);
return (ret_val);
}
/*
* gettcp: return a pointer to a tcp header.
* Skips either ip or ipv6 headers
*/
int
gettcp(
struct ip *pip,
struct tcphdr **pptcp,
void **pplast)
{
int ret_val = findheader(IPPROTO_TCP, pip, (void **)pptcp, pplast);
return (ret_val);
}
/*
* getudp: return a pointer to a udp header.
* Skips either ip or ipv6 headers
*/
int
getudp(
struct ip *pip,
struct udphdr **ppudp,
void **pplast)
{
int ret_val = findheader(IPPROTO_UDP, pip, (void **)ppudp, pplast);
return (ret_val);
}
/*
* gethdrlength: returns the length of the header in the case of ipv4
* returns the length of all the headers in the case of ipv6
*/
int gethdrlength (struct ip *pip, void *plast)
{
int length, nextheader;
char *pheader;
struct ipv6 *pipv6;
if (PIP_ISV6(pip)) {
length = 40;
pheader = (char *) pip;
nextheader = *(pheader + 6);
pheader += 40;
pipv6 = (struct ipv6 *) pip;
while (1)
{
if (nextheader == IPPROTO_NONE)
return length;
if (nextheader == IPPROTO_TCP)
return length;
if (nextheader == IPPROTO_UDP)
return length;
if (nextheader == IPPROTO_FRAGMENT)
{
nextheader = *pheader;
pheader += 8;
length += 8;
}
if ((nextheader == IPPROTO_HOPOPTS) ||
(nextheader == IPPROTO_ROUTING) ||
(nextheader == IPPROTO_DSTOPTS))
{
// Thanks to patch sent by Thomas Bohnert
// Header length field in these IPv6 extension headers
// stores the length of the header in units of 8 bytes,
// *without* counting the mandatory 8 bytes
nextheader = *pheader;
length += (*(pheader+1) + 1) * 8;
pheader += (*(pheader+1) + 1) * 8;
}
// IPv6 encapsulated in IPv6
if (nextheader == IPPROTO_IPV6)
{
pheader += 40;
nextheader=*(pheader+6);
length += 40;
}
if (pheader > (char *)plast)
return -1;
}
}
else
{
return IP_HL(pip) * 4;
}
}
/*
* getpayloadlength: returns the length of the packet without the header.
*/
int getpayloadlength (struct ip *pip, void *plast)
{
struct ipv6 *pipv6;
if (PIP_ISV6(pip)) {
pipv6 = (struct ipv6 *) pip; /* how about all headers */
return ntohs(pipv6->ip6_lngth);
}
return ntohs(pip->ip_len) - (IP_HL(pip) * 4);
}
#ifdef OLD_THESE_MOVED_TO_TRACE_C
/*
* ipcopyaddr: copy an IPv4 or IPv6 address
* (note - this is obsolete in favor of the inline-able
* IP_COPYADDR in tcptrace.h)
*/
void ip_copyaddr (ipaddr *ptoaddr, ipaddr *pfromaddr)
{
if (ADDR_ISV6(pfromaddr)) {
memcpy(ptoaddr->un.ip6.s6_addr, pfromaddr->un.ip6.s6_addr, 16);
ptoaddr->addr_vers = 6;
} else {
ptoaddr->un.ip4.s_addr = pfromaddr->un.ip4.s_addr;
ptoaddr->addr_vers = 4;
}
}
/*
* ipsameaddr: test for equality of two IPv4 or IPv6 addresses
* (note - this is obsolete in favor of the inline-able
* IP_SAMEADDR in tcptrace.h)
*/
int ip_sameaddr (ipaddr *paddr1, ipaddr *paddr2)
{
int ret = 0;
if (ADDR_ISV6(paddr1)) {
if (ADDR_ISV6(paddr2))
ret = (memcmp(paddr1->un.ip6.s6_addr,
paddr2->un.ip6.s6_addr,16) == 0);
} else {
if (ADDR_ISV4(paddr2))
ret = (paddr1->un.ip4.s_addr == paddr2->un.ip4.s_addr);
}
if (debug > 3)
printf("SameAddr(%s(%d),%s(%d)) returns %d\n",
HostName(*paddr1), ADDR_VERSION(paddr1),
HostName(*paddr2), ADDR_VERSION(paddr2),
ret);
return ret;
}
/*
* iplowaddr: test if one IPv4 or IPv6 address is lower than the second one
* (note - this is obsolete in favor of the inline-able
* IP_LOWADDR in tcptrace.h)
*/
int ip_lowaddr (ipaddr *paddr1, ipaddr *paddr2)
{
int ret = 0;
if (ADDR_ISV6(paddr1)) {
if (ADDR_ISV6(paddr2))
ret = (memcmp(paddr1->un.ip6.s6_addr,
paddr2->un.ip6.s6_addr,16) < 0);
} else {
/* already know ADDR_ISV4(paddr1) */
if (ADDR_ISV4(paddr2))
ret = (paddr1->un.ip4.s_addr < paddr2->un.ip4.s_addr);
}
if (debug > 3)
printf("LowAddr(%s(%d),%s(%d)) returns %d\n",
HostName(*paddr1), ADDR_VERSION(paddr1),
HostName(*paddr2), ADDR_VERSION(paddr2),
ret);
return ret;
}
#endif /* OLD_THESE_MOVED_TO_TRACE_C */
#ifndef HAVE_INET_PTON
int
inet_pton(int af, const char *src, void *dst)
{
if (af == AF_INET) {
/* use standard function */
long answer = inet_addr(src);
if (answer != -1) {
*((long *)dst) = answer;
return(1);
}
} else if (af == AF_INET6) {
/* YUCC - lazy for now, not fully supported */
int shorts[8];
if (sscanf(src,"%x:%x:%x:%x:%x:%x:%x:%x",
&shorts[0], &shorts[1], &shorts[2], &shorts[3],
&shorts[4], &shorts[5], &shorts[6], &shorts[7]) == 8) {
int i;
for (i=0; i < 8; ++i)
((u_short *)dst)[i] = (u_short)shorts[i];
return(1);
}
}
/* else, it failed */
return(0);
}
#endif /* HAVE_INET_PTON */
/*
* my_inet_ntop: makes a string address of the 16 byte ipv6 address
* We use our own because various machines print them differently
* and I wanted them to all be the same
*/
char *
my_inet_ntop(int af, const char *src, char *dst, size_t size)
{
int i;
u_short *src_shorts = (u_short *)src;
char *ret = dst;
Bool did_shorthand = FALSE;
Bool doing_shorthand = FALSE;
/* sanity check, this isn't general, but doesn't need to be */
if (size != INET6_ADDRSTRLEN) {
fprintf(stderr,"my_inet_ntop: invalid size argument\n");
exit(-1);
}
/* address is 128 bits == 16 bytes == 8 shorts */
for (i = 0; i < 8; i++) {
u_short twobytes = ntohs(src_shorts[i]);
/* handle shorthand notation */
if (twobytes == 0) {
if (doing_shorthand) {
/* just eat it and continue (except last 2 bytes) */
if (i != 7)
continue;
} else if (!did_shorthand) {
/* start shorthand */
doing_shorthand = TRUE;
continue;
}
}
/* terminate shorthand (on non-zero or last 2 bytes) */
if (doing_shorthand) {
doing_shorthand = FALSE;
did_shorthand = TRUE;
sprintf(dst, ":");
dst += 1;
}
sprintf(dst, "%04x:", twobytes);
dst += 5;
}
/* nuke the trailing ':' */
*(dst-1) = '\0';
return(ret);
}
/* given an IPv4 IP address, return a pointer to a (static) ipaddr struct */
struct ipaddr *
IPV4ADDR2ADDR(
struct in_addr *addr4)
{
static struct ipaddr addr;
addr.addr_vers = 4;
addr.un.ip4.s_addr = addr4->s_addr;
return(&addr);
}
/* given an IPv6 IP address, return a pointer to a (static) ipaddr struct */
struct ipaddr *
IPV6ADDR2ADDR(
struct in6_addr *addr6)
{
static struct ipaddr addr;
addr.addr_vers = 6;
memcpy(&addr.un.ip6.s6_addr,&addr6->s6_addr, 16);
return(&addr);
}
/* given an internet address (IPv4 dotted decimal or IPv6 hex colon),
return an "ipaddr" (allocated from heap) */
ipaddr *
str2ipaddr(
char *str)
{
ipaddr *pipaddr;
/* allocate space */
pipaddr = MallocZ(sizeof(ipaddr));
/* N.B. - uses standard IPv6 facility inet_pton from RFC draft */
if (strchr(str,'.') != NULL) {
/* has dots, better be IPv4 */
pipaddr->addr_vers = 4;
if (inet_pton(AF_INET, str,
&pipaddr->un.ip4.s_addr) != 1) {
if (debug)
fprintf(stderr,"Address string '%s' unparsable as IPv4\n",
str);
return(NULL);
}
} else if (strchr(str,':') != NULL) {
/* has colons, better be IPv6 */
pipaddr->addr_vers = 6;
if (inet_pton(AF_INET6, str,
&pipaddr->un.ip6.s6_addr) != 1) {
if (debug)
fprintf(stderr,"Address string '%s' unparsable as IPv6\n",
str);
return(NULL);
}
} else {
if (debug)
fprintf(stderr,"Address string '%s' unparsable\n", str);
return(NULL);
}
return(pipaddr);
}
/* compare two IP addresses */
/* result: */
/* -2: different address types */
/* -1: A < B */
/* 0: A = B */
/* 1: A > B */
int IPcmp(
ipaddr *pipA,
ipaddr *pipB)
{
int i;
int len = (pipA->addr_vers == 4)?4:6;
u_char *left = (u_char *)&pipA->un.ip4;
u_char *right = (u_char *)&pipB->un.ip4;
/* always returns -2 unless both same type */
if (pipA->addr_vers != pipB->addr_vers) {
if (debug>1) {
printf("IPcmp %s", HostAddr(*pipA));
printf("%s fails, different addr types\n",
HostAddr(*pipB));
}
return(-2);
}
for (i=0; i < len; ++i) {
if (left[i] < right[i]) {
return(-1);
} else if (left[i] > right[i]) {
return(1);
}
/* else ==, keep going */
}
/* if we got here, they're the same */
return(0);
}
/* Added Aug 31, 2001 -- Avinash
* computes the total length of all the extension headers
*/
int total_length_ext_headers(
struct ipv6 *pip6)
{
char nextheader;
struct ipv6_ext *pheader;
u_int total_length = 0;
/* find the first header */
nextheader = pip6->ip6_nheader;
pheader = (struct ipv6_ext *)(pip6+1);
while(1) {
switch(nextheader) {
case IPPROTO_HOPOPTS:
case IPPROTO_ROUTING:
case IPPROTO_DSTOPTS:
total_length = 8 + (pheader->ip6ext_len * 8);
nextheader = pheader->ip6ext_nheader;
pheader = (struct ipv6_ext *)
((char *)pheader + 8 + (pheader->ip6ext_len)*8);
break;
case IPPROTO_FRAGMENT:
total_length += 8;
nextheader = pheader->ip6ext_nheader;
pheader = (struct ipv6_ext *)((char *)pheader + 8);
break;
case IPPROTO_NONE: /* End of extension headers */
return(total_length);
case IPPROTO_TCP: /* No extension headers */
return(0);
default: /* Unknown type */
return(-1);
}
}
}