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