/*
* 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);
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1