Subject: a patch to make trafshow 3.1 IPv6 ready From: Jun-ichiro itojun Hagino Date: Wed, 21 Mar 2001 04:46:17 +0900 Sender: itojun@itojun.org Message-Id: <20010320194618.019727E73@starfruit.itojun.org> the following patch makes trafshow 3.1 IPv6 ready. there are a lot of internal changes, also i did not check if it is compilable on systems with 4.3BSD-like sockaddr (without sa_len). let me know if it makes sense to you. note: (1) color does not work for IPv6. (2) pcap_lookupnet() is skipped as the function does not work with IPv6-only machines. basically, libpcap is to be blamed. itojun Index: addrtoname.c =================================================================== RCS file: /cvsroot/apps/trafshow/addrtoname.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- addrtoname.c 2001/03/20 01:48:18 1.1.1.1 +++ addrtoname.c 2001/03/20 19:37:56 1.2 @@ -86,6 +86,33 @@ static u_int32_t f_localnet; static u_int32_t netmask; +#ifdef INET6 +struct h6namemem { + struct in6_addr addr; + char *name; + struct h6namemem *nxt; +}; + +struct h6namemem h6nametable[HASHNAMESIZE]; + +char * +satoa(sa) + struct sockaddr *sa; +{ + static char buf[NI_MAXHOST]; +#ifdef NI_WITHSCOPEID + const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID; +#else + const int niflags = NI_NUMERICHOST; +#endif + + if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, + niflags) != 0) + strcpy(buf, "?"); + return buf; +} +#endif /* INET6 */ + /* * A faster replacement for inet_ntoa(). */ @@ -186,6 +213,75 @@ /* empty */ } #endif + +#ifdef INET6 +extern char * getname6(); + +char * +getnamebysa(sa) + struct sockaddr *sa; +{ + + if (sa->sa_family == AF_INET) + return getname((u_char *)&((struct sockaddr_in *)sa)->sin_addr); + else /* sa->sa_family == AF_INET6 */ + return getname6(&((struct sockaddr_in6 *)sa)->sin6_addr); +} + +char * +getname6(addr) + struct in6_addr *addr; +{ + register struct h6namemem *p; + register struct hostent *hp = NULL; + register char *cp; + u_long temp[4]; + char buf[INET6_ADDRSTRLEN]; + static int oldtimer; + static sigfunc oldalarm; + + bcopy(addr, temp, sizeof(temp)); + temp[0] ^= temp[1] ^ temp[2] ^ temp[3]; + + p = &h6nametable[temp[0] & (HASHNAMESIZE-1)]; + for (; p->nxt; p = p->nxt) { + if (bcmp(&p->addr, addr, sizeof(*addr)) == 0) + return (p->name); + } + p->addr = *addr; + p->nxt = (struct h6namemem *)calloc(1, sizeof (*p)); + + /* + * Only print names when: + * (1) -n was not given. + * (2) Address is foreign and -f was given. If -f was not + * present, f_netmask and f_local are 0 and the second + * test will succeed. + * (3) The host portion is not 0 (i.e., a network address). + * (4) The host portion is not broadcast. + */ + if (!nflag && !IN6_IS_ADDR_UNSPECIFIED(addr) && + !IN6_IS_ADDR_LINKLOCAL(addr)) { + oldtimer = alarm(dns_timeout); + oldalarm = signal(SIGALRM, nohostname); +#ifdef HAVE_SIGINTERRUPT + if (!setjmp(getname_env)) +#endif + hp = gethostbyaddr((char *)&addr, sizeof(*addr), AF_INET6); + (void)signal(SIGALRM, oldalarm); + if (oldtimer < 1) oldtimer = 1; + (void)alarm(oldtimer); + } + if (hp) { + if (Nflag && (cp = index(hp->h_name, '.')) != NULL) *cp = '\0'; + cp = hp->h_name; + } else cp = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); + + p->name = (char *)malloc((unsigned)(strlen(cp) + 1)); + return strcpy(p->name, cp); +} + +#endif /* INET6 */ /* * Return a name for the IP address pointed to by ap. This address Index: color.c =================================================================== RCS file: /cvsroot/apps/trafshow/color.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- color.c 2001/03/20 01:48:18 1.1.1.1 +++ color.c 2001/03/20 19:37:56 1.2 @@ -386,15 +386,19 @@ register struct m_entry *m; for (m = color_mask, i = 0; m != NULL, i < n_masks; m++, i++) { - if ((e->src.s_addr & m->sm.s_addr) ^ m->src.s_addr) + if (e->src.ss_family != AF_INET || e->dst.ss_family != AF_INET) continue; - if ((e->dst.s_addr & m->dm.s_addr) ^ m->dst.s_addr) + if ((((struct sockaddr_in *)&e->src)->sin_addr.s_addr & m->sm.s_addr) ^ m->src.s_addr) continue; + if ((((struct sockaddr_in *)&e->dst)->sin_addr.s_addr & m->dm.s_addr) ^ m->dst.s_addr) + continue; if (m->proto && e->proto != m->proto) continue; - if (m->sport && e->sport != m->sport) + if (m->sport && + ((struct sockaddr_in *)&e->src)->sin_port == m->sport) continue; - if (m->dport && e->dport != m->dport) + if (m->dport && + ((struct sockaddr_in *)&e->dst)->sin_port == m->dport) continue; #ifdef HAVE_SLCURSES attron(COLOR_PAIR(m->pair)); Index: display.c =================================================================== RCS file: /cvsroot/apps/trafshow/display.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- display.c 2001/03/20 01:48:18 1.1.1.1 +++ display.c 2001/03/20 19:37:56 1.2 @@ -34,6 +34,10 @@ #include #include #include +#ifdef INET6 +#include +#include +#endif #include #include #include @@ -41,6 +45,7 @@ #ifdef TIME_WITH_SYS_TIME #include #endif +#include #include "trafshow.h" @@ -120,9 +125,9 @@ * Pretty print an Internet address (net address + port). */ static char * -inet_print(in, port, proto) - struct in_addr in; - u_short port, proto; +inet_print(sa, proto) + struct sockaddr *sa; + u_short proto; { register char *cp; char pline[20]; @@ -134,9 +139,17 @@ "rtrsolicit", "timeexceed", "paramprobl", "stampreqst", "stampreply", "inforeqst", "inforeply", "maskreqst", "maskreply" }; + struct in_addr in; + u_short port; + struct protoent *p; + + in = ((struct sockaddr_in *)sa)->sin_addr; + port = ((struct sockaddr_in *)sa)->sin_port; - if (l_nflag) cp = intoa(in.s_addr); - else cp = ipaddr_string(&in); + if (l_nflag) + cp = satoa(sa); + else + cp = saddr_string(sa); (void) sprintf(aline, "%-*.*s", addr_size, addr_size, cp); if (port) { @@ -146,10 +159,16 @@ cp = udpport_string(port); else if (proto == IPPROTO_ICMP && port <= ICMP_MAXTYPE) cp = icmp_type[port]; - else cp = "unknown"; + else if ((p = getprotobynumber(proto)) != NULL) + cp = p->p_name; + else + cp = NULL; + + if (cp) + plen = sprintf(pline, "..%.10s", cp); + else + plen = sprintf(pline, "..%10u", proto); - plen = sprintf(pline, "..%.10s", cp); - if ((cp = strchr(aline, ' ')) != NULL) alen = cp - aline; else alen = addr_size; @@ -183,9 +202,9 @@ proto = etherproto_string(entries[i].eh.ether_type); } else { - addstr(inet_print(entries[i].src, entries[i].sport, entries[i].proto)); + addstr(inet_print(&entries[i].src, entries[i].proto)); addch(' '); - addstr(inet_print(entries[i].dst, entries[i].dport, entries[i].proto)); + addstr(inet_print(&entries[i].dst, entries[i].proto)); proto = getprotoname(entries[i].proto); if (proto == NULL) proto = "unknown"; @@ -270,6 +289,43 @@ refresh(); } +int +ipaddr_compar(s1, s2) + struct sockaddr *s1, *s2; +{ + + if (s1->sa_family != s2->sa_family) + return 0; + if (s1->sa_family == AF_INET) { + struct sockaddr_in *sin1, *sin2; + + sin1 = (struct sockaddr_in *)s1; + sin2 = (struct sockaddr_in *)s2; + if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) + return 0; + if (sin1->sin_port != sin2->sin_port) + return 0; + return 1; + } +#ifdef INET6 + if (s1->sa_family == AF_INET6) { + struct sockaddr_in6 *sin1, *sin2; + + sin1 = (struct sockaddr_in6 *)s1; + sin2 = (struct sockaddr_in6 *)s2; + if (!IN6_ARE_ADDR_EQUAL(&sin1->sin6_addr, &sin2->sin6_addr)) + return 0; + if (sin1->sin6_port != sin2->sin6_port) + return 0; + if (sin1->sin6_scope_id != sin2->sin6_scope_id) + return 0; + return 1; + } +#endif + + return 0; +} + /* * Add new entry or add bytes to existed record. */ @@ -284,10 +340,8 @@ j = page * page_size; for (i = 0; i < n_entry; i++) { if (memcmp(&e->eh, &entries[i].eh, sizeof(e->eh)) == 0 && - e->src.s_addr == entries[i].src.s_addr && - e->sport == entries[i].sport && - e->dst.s_addr == entries[i].dst.s_addr && - e->dport == entries[i].dport && + ipaddr_compar(&e->src, &e->src) && + ipaddr_compar(&e->dst, &e->dst) && e->proto == entries[i].proto) { entries[i].bytes += e->bytes; if (i >= j && i < j + page_size) @@ -310,20 +364,51 @@ } void -display(eh, ip, length) +display(eh, top, length) struct ether_header *eh; - register struct ip *ip; + void *top; int length; { + struct ip *ip; +#ifdef INET6 + struct ip6_hdr *ip6; +#endif register iplen; int hlen, not_ip = 0; register u_char *cp; struct t_entry te; + in_port_t sport, dport; + struct sockaddr_in *sin; +#ifdef INET6 + struct sockaddr_in6 *sin6; +#endif + int minlen; + int af; - if ((u_char *)(ip + 1) > snapend) { + ip = (struct ip *)top; + switch (ip->ip_v) { + case 4: + af = AF_INET; + minlen = sizeof(*ip); + iplen = ntohs(ip->ip_len); + break; + case 6: + af = AF_INET6; + ip = NULL; + ip6 = (struct ip6_hdr *)top; + minlen = sizeof(*ip6); + iplen = ntohs(ip6->ip6_plen) + sizeof(*ip6); + break; + default: + af = 0; + ip = NULL; + break; + } + + if ((u_char *)top + minlen > snapend) { if (!eflag) return; /* not ip proto? discard silently */ not_ip++; - } else if (length < sizeof(*ip)) { + } else if (length < minlen) { if (!eflag) { mvprintw(LINES-1, err_pos, "\ truncated-ip: discard %d bytes", length); @@ -331,15 +416,15 @@ goto refresh_screen; } not_ip++; - } else if (ip->ip_v != IPVERSION) { + } else if (af == 0) { if (!eflag) { mvprintw(LINES-1, err_pos, "\ -ip ver != %d: discard %d bytes", IPVERSION, length); +unsupported ip ver: discard %d bytes", length); clrtoeol(); goto refresh_screen; } not_ip++; - } else if ((iplen = ntohs(ip->ip_len)) < 1) { + } else if (iplen < 1) { if (!eflag) return; /* empty ip packet? discard silently */ not_ip++; } else if (length < iplen) { @@ -355,9 +440,9 @@ if (eh) te.eh = *eh; else memset(&te.eh, 0, sizeof(te.eh)); - te.sport = te.dport = 0; + sport = dport = 0; - if (!not_ip) { /* parse ip packet */ + if (ip) { /* parse ip packet */ /* * If this is fragment zero, hand it to the next higher level @@ -376,8 +461,8 @@ clrtoeol(); goto refresh_screen; } - te.sport = ntohs(((struct tcphdr *)cp)->th_sport); - te.dport = ntohs(((struct tcphdr *)cp)->th_dport); + sport = ntohs(((struct tcphdr *)cp)->th_sport); + dport = ntohs(((struct tcphdr *)cp)->th_dport); } else if (ip->ip_p == IPPROTO_UDP) { if (cp + sizeof(struct udphdr) > snapend || iplen - hlen < sizeof(struct udphdr)) { @@ -386,8 +471,8 @@ clrtoeol(); goto refresh_screen; } - te.sport = ntohs(((struct udphdr *)cp)->uh_sport); - te.dport = ntohs(((struct udphdr *)cp)->uh_dport); + sport = ntohs(((struct udphdr *)cp)->uh_sport); + dport = ntohs(((struct udphdr *)cp)->uh_dport); } else if (ip->ip_p == IPPROTO_ICMP) { if (cp + sizeof(struct icmp) > snapend || iplen - hlen < sizeof(struct icmp)) { @@ -396,15 +481,90 @@ clrtoeol(); goto refresh_screen; } - te.sport = ((struct icmp *)cp)->icmp_type; + sport = ((struct icmp *)cp)->icmp_type; } } - te.src.s_addr = ip->ip_src.s_addr; - te.dst.s_addr = ip->ip_dst.s_addr; + sin = (struct sockaddr_in *)&te.src; + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + sin->sin_addr.s_addr = ip->ip_src.s_addr; + sin->sin_port = sport; + sin = (struct sockaddr_in *)&te.dst; + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + sin->sin_addr.s_addr = ip->ip_dst.s_addr; + sin->sin_port = dport; te.proto = ip->ip_p; te.bytes = iplen; - } else { /* other than ip protocol packets */ - te.src.s_addr = te.dst.s_addr = 0; + } +#ifdef INET6 + else if (ip6) { + /* XXX should chase header chain */ + cp = (u_char *)(ip6 + 1); + hlen = sizeof(*ip6); + switch (ip6->ip6_nxt) { + case IPPROTO_TCP: + if (cp + sizeof(struct tcphdr) > snapend || + iplen - hlen < sizeof(struct tcphdr)) { + mvprintw(LINES-1, err_pos, "\ +truncated-tcp: wrong ip hdrlen"); + clrtoeol(); + goto refresh_screen; + } + sport = ntohs(((struct tcphdr *)cp)->th_sport); + dport = ntohs(((struct tcphdr *)cp)->th_dport); + break; + case IPPROTO_UDP: + if (cp + sizeof(struct udphdr) > snapend || + iplen - hlen < sizeof(struct udphdr)) { + mvprintw(LINES-1, err_pos, "\ +truncated-udp: wrong ip hdrlen"); + clrtoeol(); + goto refresh_screen; + } + sport = ntohs(((struct udphdr *)cp)->uh_sport); + dport = ntohs(((struct udphdr *)cp)->uh_dport); + break; + case IPPROTO_ICMPV6: + if (cp + sizeof(struct icmp6_hdr) > snapend || + iplen - hlen < sizeof(struct icmp6_hdr)) { + mvprintw(LINES-1, err_pos, "\ +truncated-icmp6: wrong ip hdrlen"); + clrtoeol(); + goto refresh_screen; + } + sport = ((struct icmp6_hdr *)cp)->icmp6_type; + } + + sin6 = (struct sockaddr_in6 *)&te.src; + memset(sin6, 0, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_addr = ip6->ip6_src; + sin6->sin6_port = sport; + sin6 = (struct sockaddr_in6 *)&te.dst; + memset(sin6, 0, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_addr = ip6->ip6_dst; + sin6->sin6_port = dport; + te.proto = ip6->ip6_nxt; + te.bytes = iplen; + } +#endif + else { /* other than ip protocol packets */ + sin = (struct sockaddr_in *)&te.src; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + sin->sin_addr.s_addr = 0; + sin->sin_port = sport; + sin = (struct sockaddr_in *)&te.src; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + sin->sin_addr.s_addr = 0; + sin->sin_port = dport; te.proto = 0; te.bytes = length; } Index: getarptab.c =================================================================== RCS file: /cvsroot/apps/trafshow/getarptab.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- getarptab.c 2001/03/20 01:48:21 1.1.1.1 +++ getarptab.c 2001/03/20 19:37:56 1.2 @@ -14,8 +14,6 @@ #include #endif -#ifdef HAVE_RTF_LLINFO /* BSD systems */ - #include #include #include @@ -26,6 +24,8 @@ #include #include #include + +#ifdef HAVE_RTF_LLINFO /* BSD systems */ #ifdef ETHER_ADDR_LEN #define MAC_ADDR_LEN ETHER_ADDR_LEN Index: interfaces.c =================================================================== RCS file: /cvsroot/apps/trafshow/interfaces.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- interfaces.c 2001/03/20 01:48:18 1.1.1.1 +++ interfaces.c 2001/03/20 19:37:56 1.2 @@ -77,12 +77,23 @@ { u_int caplen = h->caplen; u_int length = h->len; + int grab; if (caplen < ETHER_HDRLEN) return; snapend = p + caplen; - if (eflag || - ntohs(((struct ether_header *)p)->ether_type) == ETHERTYPE_IP) + grab = 0; + if (eflag) + grab++; + switch (ntohs(((struct ether_header *)p)->ether_type)) { + case ETHERTYPE_IP: +#ifdef INET6 + case ETHERTYPE_IPV6: +#endif + grab++; + break; + } + if (grab) display(p, p + ETHER_HDRLEN, length - ETHER_HDRLEN); } @@ -135,12 +146,17 @@ p += 2; hdrlen += 2; } - if (type == 0x21) { /* IP protocol */ + switch (type) { + case 0x21: /* IPv4 */ +#ifdef INET6 + case 0x57: /* IPv6 */ +#endif #ifdef SLC_BPFHDR p = packetp + SLC_BPFHDR; /* skip bpf pseudo header */ hdrlen = SLC_BPFHDR; #endif display(NULL, p, length - hdrlen); + break; } } @@ -157,8 +173,14 @@ if (caplen < CHDLC_HDRLEN) return; snapend = p + caplen; - if (ntohs(*(u_short *)(p + 2)) == 0x0800) /* IP protocol */ + switch (ntohs(*(u_short *)(p + 2))) { + case 0x0800: /* IP protocol */ +#ifdef INET6 + case 0x86dd: /* IP protocol */ +#endif display(NULL, p + CHDLC_HDRLEN, length - CHDLC_HDRLEN); + break; + } } #endif @@ -189,8 +211,14 @@ memcpy(&family, p, sizeof(family)); snapend = p + caplen; - if (family == AF_INET) + switch (family) { + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif display(NULL, p + NULL_HDRLEN, length - NULL_HDRLEN); + break; + } } /* Index: trafshow.c =================================================================== RCS file: /cvsroot/apps/trafshow/trafshow.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- trafshow.c 2001/03/20 01:48:19 1.1.1.1 +++ trafshow.c 2001/03/20 19:37:57 1.2 @@ -69,7 +69,11 @@ { int op, cnt; pcap_t *pd; +#ifdef INET6 + bpf_u_int32 netmask = 0; +#else bpf_u_int32 localnet, netmask; +#endif char *cp, *infile, *expr, ebuf[PCAP_ERRBUF_SIZE]; struct bpf_program fcode; extern char *optarg; @@ -160,8 +164,10 @@ #endif ) error(0, "interface %s not an Ethernet", device_name); +#ifndef INET6 if (pcap_lookupnet(device_name, &localnet, &netmask, ebuf) < 0) error(0, ebuf); +#endif /* Get back to user process after socket has been opened */ setuid(getuid()); @@ -175,7 +181,11 @@ pcap_setfilter(pd, &fcode) < 0) error(0, pcap_geterr(pd)); +#ifdef INET6 + init_addrtoname(0, 0); +#else init_addrtoname(localnet, netmask); +#endif init_term(FALSE); Index: trafshow.h =================================================================== RCS file: /cvsroot/apps/trafshow/trafshow.h,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -u -r1.1.1.1 -r1.2 --- trafshow.h 2001/03/20 01:48:19 1.1.1.1 +++ trafshow.h 2001/03/20 19:37:57 1.2 @@ -20,7 +20,11 @@ #define DEFAULT_DNS 2 /* max timeout for gethostbyaddr() */ #define SCR_OFFS 2 /* first show line on screen */ #define MAX_PAGES 20 /* max pages on screen */ +#ifdef INET6 +#define DEFAULT_SNAPLEN 88 /* length of saved portion of packet */ +#else #define DEFAULT_SNAPLEN 68 /* length of saved portion of packet */ +#endif #define DEFAULT_COLS 80 /* full screen width */ #define ADDR_SIZE 28 /* host..port size */ @@ -67,10 +71,8 @@ /* traffic entry */ struct t_entry { struct ether_header eh; - struct in_addr src; /* source ip address */ - u_short sport; /* source port */ - struct in_addr dst; /* destination ip address */ - u_short dport; /* destination port */ + struct sockaddr_storage src; /* source ip address */ + struct sockaddr_storage dst; /* destination ip address */ u_short proto; /* ip protocol */ u_long bytes; /* bytes in ip datagram */ u_long obytes; /* old bytes */ @@ -151,9 +153,16 @@ extern void init_addrtoname(u_int32_t, u_int32_t); extern char *getname(u_char *); extern char *intoa(u_int32_t); +#ifdef INET6 +extern char *satoa(struct sockaddr *); +extern char *getnamebysa(struct sockaddr *); +#endif extern char *tcpport_string(u_short); extern char *udpport_string(u_short); #define ipaddr_string(p) getname((u_char *)(p)) +#ifdef INET6 +#define saddr_string(p) getnamebysa(p) +#endif extern char *entoa(u_char *); extern char *etheraddr_string(u_char *); extern char *etherproto_string(u_short);