Sophie

Sophie

distrib > Fedora > 13 > i386 > media > updates-src > by-pkgid > d3da47e473396ce88b22c4103ac14adc > files > 6

iftop-0.17-12.fc13.src.rpm

Backported patch from Mats Erik Andersson <mats.andersson@gisladisker.se>
for iftop >= 0.17, which adds IPv6 support, uses a better hash algorithm
in address pairs and fix link-level address detection on kfreebsd-*. For
further information, please have a look to Debian bug IDs #427852, #595169
and #598367.

--- iftop-0.17/iftop.c			2010-11-18 23:34:24.766118393 +0100
+++ iftop-0.17/iftop.c.ipv6-ll		2010-11-18 23:47:21.521807671 +0100
@@ -46,6 +46,8 @@
 #include "cfgfile.h"
 #include "ppp.h"
 
+#include <netinet/ip6.h>
+
 
 /* ethernet address of interface. */
 int have_hw_addr = 0;
@@ -53,7 +55,9 @@
 
 /* IP address of interface */
 int have_ip_addr = 0;
+int have_ip6_addr = 0;
 struct in_addr if_ip_addr;
+struct in6_addr if_ip6_addr;
 
 extern options_t options;
 
@@ -77,8 +81,9 @@
 
 
 
-/* Only need ethernet (plus optional 4 byte VLAN) and IP headers (48) + first 2 bytes of tcp/udp header */
-#define CAPTURE_LENGTH 72
+/* Only need ethernet (plus optional 4 byte VLAN) and IP headers (48) + first 2 bytes of tcp/udp header
+   -> Increase with a further 20 to account for IPv6 header length. */
+#define CAPTURE_LENGTH 92
 
 void init_history() {
     history = addr_hash_create();
@@ -148,10 +153,14 @@
     return ret;
 }
 
-int ip_addr_match(struct in_addr addr) {
+int __inline__ ip_addr_match(struct in_addr addr) {
     return addr.s_addr == if_ip_addr.s_addr;
 }
 
+int __inline__ ip6_addr_match(struct in6_addr *addr) {
+    return IN6_ARE_ADDR_EQUAL(addr, &if_ip6_addr);
+}
+
 /**
  * Creates an addr_pair from an ip (and tcp/udp) header, swapping src and dst
  * if required
@@ -160,29 +169,61 @@
   unsigned short int src_port = 0;
   unsigned short int dst_port = 0;
 
-  /* Does this protocol use ports? */
-  if(iptr->ip_p == IPPROTO_TCP || iptr->ip_p == IPPROTO_UDP) {
-    /* We take a slight liberty here by treating UDP the same as TCP */
-
-    /* Find the TCP/UDP header */
-    struct tcphdr* thdr = ((void*)iptr) + IP_HL(iptr) * 4;
-    src_port = ntohs(thdr->th_sport);
-    dst_port = ntohs(thdr->th_dport);
-  }
+  /* Arrange for predictable values. */
+  memset(ap, '\0', sizeof(*ap));
 
-  if(flip == 0) {
-    ap->src = iptr->ip_src;
-    ap->src_port = src_port;
-    ap->dst = iptr->ip_dst;
-    ap->dst_port = dst_port;
-  }
-  else {
-    ap->src = iptr->ip_dst;
-    ap->src_port = dst_port;
-    ap->dst = iptr->ip_src;
-    ap->dst_port = src_port;
-  }
+  if(IP_V(iptr) == 4) {
+    ap->af = AF_INET;
 
+    /* Does this protocol use ports? */
+    if(iptr->ip_p == IPPROTO_TCP || iptr->ip_p == IPPROTO_UDP) {
+      /* We take a slight liberty here by treating UDP the same as TCP */
+
+      /* Find the TCP/UDP header */
+      struct tcphdr* thdr = ((void*)iptr) + IP_HL(iptr) * 4;
+      src_port = ntohs(thdr->th_sport);
+      dst_port = ntohs(thdr->th_dport);
+    }
+
+    if(flip == 0) {
+      ap->src = iptr->ip_src;
+      ap->src_port = src_port;
+      ap->dst = iptr->ip_dst;
+      ap->dst_port = dst_port;
+    }
+    else {
+      ap->src = iptr->ip_dst;
+      ap->src_port = dst_port;
+      ap->dst = iptr->ip_src;
+      ap->dst_port = src_port;
+    }
+  } /* IPv4 */
+  else if (IP_V(iptr) == 6) {
+    /* IPv6 packet seen. */
+    struct ip6_hdr *ip6tr = (struct ip6_hdr *) iptr;
+
+    ap->af = AF_INET6;
+
+    if( (ip6tr->ip6_nxt == IPPROTO_TCP) || (ip6tr->ip6_nxt == IPPROTO_UDP) ) {
+      struct tcphdr *thdr = ((void *) ip6tr) + 40;
+
+      src_port = ntohs(thdr->th_sport);
+      dst_port = ntohs(thdr->th_dport);
+    }
+
+    if(flip == 0) {
+      memcpy(&ap->src6, &ip6tr->ip6_src, sizeof(ap->src6));
+      ap->src_port = src_port;
+      memcpy(&ap->dst6, &ip6tr->ip6_dst, sizeof(ap->dst6));
+      ap->dst_port = dst_port;
+    }
+    else {
+      memcpy(&ap->src6, &ip6tr->ip6_dst, sizeof(ap->src6));
+      ap->src_port = dst_port;
+      memcpy(&ap->dst6, &ip6tr->ip6_src, sizeof(ap->dst6));
+      ap->dst_port = src_port;
+    }
+  }
 }
 
 static void handle_ip_packet(struct ip* iptr, int hw_dir)
@@ -194,9 +235,16 @@
 	void **void_pp;
     } u_ht = { &ht };
     addr_pair ap;
-    int len;
+    unsigned int len = 0;
+    struct in6_addr scribdst;   /* Scratch pad. */
+    struct in6_addr scribsrc;   /* Scratch pad. */
+    /* Reinterpret packet type. */
+    struct ip6_hdr* ip6tr = (struct ip6_hdr *) iptr;
+
+    memset(&ap, '\0', sizeof(ap));
 
-    if(options.netfilter == 0) { 
+    if( (IP_V(iptr) ==4 && options.netfilter == 0)
+            || (IP_V(iptr) == 6 && options.netfilter6 == 0) ) { 
         /*
          * Net filter is off, so assign direction based on MAC address
          */
@@ -213,12 +261,22 @@
         /* Packet direction is not given away by h/ware layer.  Try IP
          * layer
          */
-        else if(have_ip_addr && ip_addr_match(iptr->ip_src)) {
+        else if((IP_V(iptr) == 4) && have_ip_addr && ip_addr_match(iptr->ip_src)) {
             /* outgoing */
             assign_addr_pair(&ap, iptr, 0);
             direction = 1;
         }
-        else if(have_ip_addr && ip_addr_match(iptr->ip_dst)) {
+        else if((IP_V(iptr) == 4) && have_ip_addr && ip_addr_match(iptr->ip_dst)) {
+            /* incoming */
+            assign_addr_pair(&ap, iptr, 1);
+            direction = 0;
+        }
+        else if((IP_V(iptr) == 6) && have_ip6_addr && ip6_addr_match(&ip6tr->ip6_src)) {
+            /* outgoing */
+            assign_addr_pair(&ap, iptr, 0);
+            direction = 1;
+        }
+        else if((IP_V(iptr) == 6) && have_ip6_addr && ip6_addr_match(&ip6tr->ip6_dst)) {
             /* incoming */
             assign_addr_pair(&ap, iptr, 1);
             direction = 0;
@@ -232,16 +290,19 @@
 	else if (options.promiscuous_but_choosy) {
 	    return;		/* junk it */
 	}
-        else if(iptr->ip_src.s_addr < iptr->ip_dst.s_addr) {
+        else if((IP_V(iptr) == 4) && (iptr->ip_src.s_addr < iptr->ip_dst.s_addr)) {
             assign_addr_pair(&ap, iptr, 1);
             direction = 0;
         }
-        else {
+        else if(IP_V(iptr) == 4) {
             assign_addr_pair(&ap, iptr, 0);
             direction = 0;
         }
+        /* Drop other uncertain packages. */
     }
-    else {
+
+
+    if(IP_V(iptr) == 4 && options.netfilter != 0) {
         /* 
          * Net filter on, assign direction according to netmask 
          */ 
@@ -261,22 +322,91 @@
         }
     }
 
-    ap.protocol = iptr->ip_p;
+    if(IP_V(iptr) == 6 && options.netfilter6 != 0) {
+        /*
+         * Net filter IPv6 active.
+         */
+        int j;
+
+        /* First reduce the participating addresses using the netfilter prefix.
+         * We need scratch pads to do this.
+         */
+        for (j=0; j < 4; ++j) {
+            scribdst.s6_addr32[j] = ip6tr->ip6_dst.s6_addr32[j]
+                                        & options.netfilter6mask.s6_addr32[j];
+            scribsrc.s6_addr32[j] = ip6tr->ip6_src.s6_addr32[j]
+                                        & options.netfilter6mask.s6_addr32[j];
+        }
+
+        /* Now look for any hits. */
+        if (IN6_ARE_ADDR_EQUAL(&scribsrc, &options.netfilter6net)
+                && ! IN6_ARE_ADDR_EQUAL(&scribdst, &options.netfilter6net)) {
+            /* out of network */
+            assign_addr_pair(&ap, iptr, 0);
+            direction = 1;
+        }
+        else if (! IN6_ARE_ADDR_EQUAL(&scribsrc, &options.netfilter6net)
+                    && IN6_ARE_ADDR_EQUAL(&scribdst, &options.netfilter6net)) {
+            /* into network */
+            assign_addr_pair(&ap, iptr, 1);
+            direction = 0;
+        }
+        else {
+            /* drop packet */
+            return ;
+        }
+    }
+
+    /* Test if link-local IPv6 packets should be dropped. */
+    if( IP_V(iptr) == 6 && !options.link_local
+            && (IN6_IS_ADDR_LINKLOCAL(&ip6tr->ip6_dst)
+                || IN6_IS_ADDR_LINKLOCAL(&ip6tr->ip6_src)) )
+        return;
 
-    /* Add the addresses to be resolved */
-    resolve(&iptr->ip_dst, NULL, 0);
-    resolve(&iptr->ip_src, NULL, 0);
+    /* Do address resolving. */
+    switch (IP_V(iptr)) {
+      case 4:
+          ap.protocol = iptr->ip_p;
+          /* Add the addresses to be resolved */
+          /* The IPv4 address is embedded in a in6_addr structure,
+           * so it need be copied, and delivered to resolve(). */
+          memset(&scribdst, '\0', sizeof(scribdst));
+          memcpy(&scribdst, &iptr->ip_dst, sizeof(struct in_addr));
+          resolve(ap.af, &scribdst, NULL, 0);
+          memset(&scribsrc, '\0', sizeof(scribsrc));
+          memcpy(&scribsrc, &iptr->ip_src, sizeof(struct in_addr));
+          resolve(ap.af, &scribsrc, NULL, 0);
+          break;
+      case 6:
+          ap.protocol = ip6tr->ip6_nxt;
+          /* Add the addresses to be resolved */
+          resolve(ap.af, &ip6tr->ip6_dst, NULL, 0);
+          resolve(ap.af, &ip6tr->ip6_src, NULL, 0);
+      default:
+          break;
+    }
 
     if(hash_find(history, &ap, u_ht.void_pp) == HASH_STATUS_KEY_NOT_FOUND) {
         ht = history_create();
         hash_insert(history, &ap, ht);
     }
 
-    len = ntohs(iptr->ip_len);
+    /* Do accounting. */
+    switch (IP_V(iptr)) {
+      case 4:
+          len = ntohs(iptr->ip_len);
+          break;
+      case 6:
+          len = ntohs(ip6tr->ip6_plen) + 40;
+      default:
+          break;
+    }
 
     /* Update record */
     ht->last_write = history_pos;
-    if(iptr->ip_src.s_addr == ap.src.s_addr) {
+    if( ((IP_V(iptr) == 4) && (iptr->ip_src.s_addr == ap.src.s_addr))
+       || ((IP_V(iptr) == 6) && !memcmp(&ip6tr->ip6_src, &ap.src6, sizeof(ap.src6))) )
+    {
         ht->sent[history_pos] += len;
         ht->total_sent += len;
     }
@@ -375,7 +505,7 @@
 		packet += 2;
 		length -= 2;
 
-        if(proto == PPP_IP || proto == ETHERTYPE_IP) {
+        if(proto == PPP_IP || proto == ETHERTYPE_IP || proto == ETHERTYPE_IPV6) {
             handle_ip_packet((struct ip*)packet, -1);
         }
     }
@@ -421,7 +551,7 @@
         payload += sizeof(struct vlan_8021q_header);
     }
 
-    if(ether_type == ETHERTYPE_IP) {
+    if(ether_type == ETHERTYPE_IP || ether_type == ETHERTYPE_IPV6) {
         struct ip* iptr;
         int dir = -1;
         
@@ -441,6 +571,7 @@
             dir = 0;
         }
 
+        /* Distinguishing ip_hdr and ip6_hdr will be done later. */
         iptr = (struct ip*)(payload); /* alignment? */
         handle_ip_packet(iptr, dir);
     }
@@ -453,10 +584,10 @@
 char *set_filter_code(const char *filter) {
     char *x;
     if (filter) {
-        x = xmalloc(strlen(filter) + sizeof "() and ip");
-        sprintf(x, "(%s) and ip", filter);
+        x = xmalloc(strlen(filter) + sizeof "() and (ip or ip6)");
+        sprintf(x, "(%s) and (ip or ip6)", filter);
     } else
-        x = xstrdup("ip");
+        x = xstrdup("ip or ip6");
     if (pcap_compile(pd, &pcap_filter, x, 1, 0) == -1) {
         xfree(x);
         return pcap_geterr(pd);
@@ -486,20 +617,30 @@
 #ifdef HAVE_DLPI
     result = get_addrs_dlpi(options.interface, if_hw_addr, &if_ip_addr);
 #else
-    result = get_addrs_ioctl(options.interface, if_hw_addr, &if_ip_addr);
+    result = get_addrs_ioctl(options.interface, if_hw_addr,
+          &if_ip_addr, &if_ip6_addr);
 #endif
 
     if (result < 0) {
       exit(1);
     }
 
-    have_hw_addr = result & 1;
-    have_ip_addr = result & 2;
+    have_hw_addr = result & 0x01;
+    have_ip_addr = result & 0x02;
+    have_ip6_addr = result & 0x04;
     
     if(have_ip_addr) {
       fprintf(stderr, "IP address is: %s\n", inet_ntoa(if_ip_addr));
     }
 
+    if(have_ip6_addr) {
+      char ip6str[INET6_ADDRSTRLEN];
+
+      ip6str[0] = '\0';
+      inet_ntop(AF_INET6, &if_ip6_addr, ip6str, sizeof(ip6str));
+      fprintf(stderr, "IPv6 address is: %s\n", ip6str);
+    }
+
     if(have_hw_addr) {
       fprintf(stderr, "MAC address is:");
       for (i = 0; i < 6; ++i)
--- iftop-0.17/addrs_ioctl.c		2005-12-24 12:37:43.000000000 +0100
+++ iftop-0.17/addrs_ioctl.c.ipv6-ll	2010-11-18 23:50:44.682918268 +0100
@@ -18,12 +18,17 @@
 #include <net/if.h>
 #include <netinet/in.h>
 
-#if defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__
+#if defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ \
+      || ( defined __GNUC__ && ! defined __linux__ )
 #include <sys/param.h>
 #include <sys/sysctl.h>
 #include <net/if_dl.h>
 #endif
 
+#ifdef USE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
+
 #include "iftop.h"
 
 /*
@@ -40,16 +45,20 @@
  */
 
 int
-get_addrs_ioctl(char *interface, char if_hw_addr[], struct in_addr *if_ip_addr)
+get_addrs_ioctl(char *interface, char if_hw_addr[], struct in_addr *if_ip_addr, struct in6_addr *if_ip6_addr)
 {
   int s;
   struct ifreq ifr = {};
   int got_hw_addr = 0;
   int got_ip_addr = 0;
+  int got_ip6_addr = 0;
+#ifdef USE_GETIFADDRS
+  struct ifaddrs *ifa, *ifas;
+#endif
 
   /* -- */
 
-  s = socket(PF_INET, SOCK_DGRAM, 0); /* any sort of IP socket will do */
+  s = socket(AF_INET, SOCK_DGRAM, 0); /* any sort of IP socket will do */
 
   if (s == -1) {
     perror("socket");
@@ -71,7 +80,8 @@
     got_hw_addr = 1;
   }
 #else
-#if defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__
+#if defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ \
+      || ( defined __GNUC__ && ! defined __linux__ )
   {
     int sysctlparam[6] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
     size_t needed = 0;
@@ -109,7 +119,45 @@
 #endif
   
   /* Get the IP address of the interface */
-#ifdef SIOCGIFADDR
+#ifdef USE_GETIFADDRS
+  if (getifaddrs(&ifas) == -1) {
+    fprintf(stderr, "Unable to get IP address for interface: %s\n", interface); 
+    perror("getifaddrs()");
+  }
+  else {
+     for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next) {
+        if (got_ip_addr && got_ip6_addr)
+           break; /* Search is already complete. */
+
+        if (strcmp(ifa->ifa_name, interface))
+           continue; /* Not our interface. */
+
+        if ( (ifa->ifa_addr->sa_family != AF_INET)
+              && (ifa->ifa_addr->sa_family != AF_INET6) )
+           continue; /* AF_PACKET is beyond our scope. */
+
+        if ( (ifa->ifa_addr->sa_family == AF_INET)
+              && !got_ip_addr ) {
+           got_ip_addr = 2;
+           memcpy(if_ip_addr,
+                 &(((struct sockaddr_in *) ifa->ifa_addr)->sin_addr),
+                 sizeof(*if_ip_addr));
+           continue;
+        }
+        /* Must be a IPv6 address at this point. */
+        struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
+
+        if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))
+              || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) )
+           continue;
+
+        /* A useful IPv6 address. */
+        memcpy(if_ip6_addr, &(sa6->sin6_addr), sizeof(*if_ip6_addr));
+        got_ip6_addr = 4;
+     }
+     freeifaddrs(ifas);
+  } /* getifaddrs() */
+#elif defined(SIOCGIFADDR)
   (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = AF_INET;
   if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
     fprintf(stderr, "Unable to get IP address for interface: %s\n", interface); 
@@ -125,5 +173,5 @@
   
   close(s);
 
-  return got_hw_addr + got_ip_addr;
+  return got_hw_addr + got_ip_addr + got_ip6_addr;
 }
--- iftop-0.17/options.c		2010-11-18 23:34:24.779199570 +0100
+++ iftop-0.17/options.c.ipv6-ll	2010-11-18 23:57:12.383808795 +0100
@@ -30,7 +30,7 @@
 
 options_t options;
 
-char optstr[] = "+i:f:nNF:hpbBPm:c:";
+char optstr[] = "+i:f:nNF:G:lhpbBPm:c:";
 
 /* Global options. */
 
@@ -118,6 +118,10 @@
     options.netfilter = 0;
     inet_aton("10.0.1.0", &options.netfilternet);
     inet_aton("255.255.255.0", &options.netfiltermask);
+    options.netfilter6 = 0;
+    inet_pton(AF_INET6, "fe80::", &options.netfilter6net);     /* Link-local */
+    inet_pton(AF_INET6, "ffff::", &options.netfilter6mask);
+    options.link_local = 0;
     options.dnsresolution = 1;
     options.portresolution = 1;
 #ifdef NEED_PROMISCUOUS_FOR_OUTGOING
@@ -237,7 +241,8 @@
     fprintf(fp,
 "iftop: display bandwidth usage on an interface by host\n"
 "\n"
-"Synopsis: iftop -h | [-npbNBP] [-i interface] [-f filter code] [-F net/mask]\n"
+"Synopsis: iftop -h | [-npblNBP] [-i interface] [-f filter code]\n"
+"                                [-F net/mask] [-G net6/mask6]\n"
 "\n"
 "   -h                  display this message\n"
 "   -n                  don't do hostname lookups\n"
@@ -249,7 +254,9 @@
 "   -i interface        listen on named interface\n"
 "   -f filter code      use filter code to select packets to count\n"
 "                       (default: none, but only IP packets are counted)\n"
-"   -F net/mask         show traffic flows in/out of network\n"
+"   -F net/mask         show traffic flows in/out of IPv4 network\n"
+"   -G net6/mask6       show traffic flows in/out of IPv6 network\n"
+"   -l                  display and count link-local IPv6 traffic (default: off)\n"
 "   -P                  show ports as well as hosts\n"
 "   -m limit            sets the upper limit for the bandwidth scale\n"
 "   -c config file      specifies an alternative configuration file\n"
@@ -285,6 +292,10 @@
                 config_set_string("filter-code", optarg);
                 break;
 
+            case 'l':
+                config_set_string("link-local", "true");
+                break;
+
             case 'p':
                 config_set_string("promiscuous", "true");
                 break;
@@ -297,6 +308,10 @@
                 config_set_string("net-filter", optarg);
                 break;
             
+            case 'G':
+                config_set_string("net-filter6", optarg);
+                break;
+
             case 'm':
                 config_set_string("max-bandwidth", optarg);
                 break;
@@ -437,6 +452,8 @@
     if(s) {
         char* mask;
 
+        options.netfilter = 0;
+
         mask = strchr(s, '/');
         if (mask == NULL) {
             fprintf(stderr, "Could not parse net/mask: %s\n", s);
@@ -454,7 +471,7 @@
             int n;
             n = atoi(mask);
             if (n > 32) {
-                fprintf(stderr, "Invalid netmask: %s\n", s);
+                fprintf(stderr, "Invalid netmask length: %s\n", mask);
             }
             else {
                 if(n == 32) {
@@ -469,12 +486,83 @@
                   options.netfiltermask.s_addr = htonl(~mm);
                 }
             }
-        } 
-        else if (inet_aton(mask, &options.netfiltermask) == 0) {
-            fprintf(stderr, "Invalid netmask: %s\n", s);
+            options.netfilter = 1;
+        }
+        else {
+            if (inet_aton(mask, &options.netfiltermask) != 0)
+                options.netfilter = 1;
+            else {
+                fprintf(stderr, "Invalid netmask: %s\n", s);
+                return 0;
+            }
         }
         options.netfilternet.s_addr = options.netfilternet.s_addr & options.netfiltermask.s_addr;
-        options.netfilter = 1;
+        return 1;
+    }
+    return 0;
+}
+
+
+/*
+ * Read the net filter IPv6 option.  
+ */
+int options_config_get_net_filter6() {
+    char* s;
+    int j;
+
+    s = config_get_string("net-filter6");
+    if(s) {
+        char* mask;
+
+        options.netfilter6 = 0;
+
+        mask = strchr(s, '/');
+        if (mask == NULL) {
+            fprintf(stderr, "Could not parse IPv6 net/prefix: %s\n", s);
+            return 0;
+        }
+        *mask = '\0';
+        mask++;
+        if (inet_pton(AF_INET6, s, &options.netfilter6net) == 0) {
+            fprintf(stderr, "Invalid IPv6 network address: %s\n", s);
+            return 0;
+        }
+        /* Accept prefix lengths and address expressions. */
+        if (mask[strspn(mask, "0123456789")] == '\0') {
+            /* Whole string is numeric */
+            unsigned int n;
+
+            n = atoi(mask);
+            if (n > 128 || n < 1) {
+                fprintf(stderr, "Invalid IPv6 prefix length: %s\n", mask);
+            }
+            else {
+                int bl, rem;
+                const uint32_t mm = 0xffffffff;
+                uint32_t part = mm;
+
+                bl = n / 32;
+                rem = n % 32;
+                part <<= 32 - rem;
+                for (j=0; j < bl; ++j)
+                    options.netfilter6mask.s6_addr32[j] = htonl(mm);
+                if (rem > 0)
+                    options.netfilter6mask.s6_addr32[bl] = htonl(part);
+                options.netfilter6 = 1;
+            }
+        }
+        else {
+            if (inet_pton(AF_INET6, mask, &options.netfilter6mask) != 0)
+                options.netfilter6 = 1;
+            else {
+                fprintf(stderr, "Invalid IPv6 netmask: %s\n", s);
+                return 0;
+            }
+        }
+        /* Prepare any comparison by masking the provided filtered net. */
+        for (j=0; j < 4; ++j)
+            options.netfilter6net.s6_addr32[j] &= options.netfilter6mask.s6_addr32[j];
+
         return 1;
     }
     return 0;
@@ -498,5 +586,7 @@
     options_config_get_bw_rate("max-bandwidth", &options.max_bandwidth);
     options_config_get_enum("port-display", showports_enumeration, (int*)&options.showports);
     options_config_get_string("screen-filter", &options.screenfilter);
+    options_config_get_bool("link-local", &options.link_local);
     options_config_get_net_filter();
+    options_config_get_net_filter6();
 };
--- iftop-0.17/addr_hash.c		2002-10-10 16:11:12.000000000 +0200
+++ iftop-0.17/addr_hash.c.ipv6-ll	2010-11-18 23:59:15.663807073 +0100
@@ -11,6 +11,19 @@
 int compare(void* a, void* b) {
     addr_pair* aa = (addr_pair*)a;
     addr_pair* bb = (addr_pair*)b;
+
+    if (aa->af != bb->af)
+        return 0;
+
+    if (aa->af == AF_INET6) {
+       return (IN6_ARE_ADDR_EQUAL(&aa->src6, &bb->src6)
+               && aa->src_port == bb->src_port
+               && IN6_ARE_ADDR_EQUAL(&aa->dst6, &bb->dst6)
+               && aa->dst_port == bb->dst_port
+               && aa->protocol == bb->protocol);
+    }
+
+    /* AF_INET or unknown. */
     return (aa->src.s_addr == bb->src.s_addr 
             && aa->src_port == bb->src_port
             && aa->dst.s_addr == bb->dst.s_addr
@@ -18,25 +31,42 @@
             && aa->protocol == bb->protocol);
 }
 
+static int __inline__ hash_uint32(uint32_t n) {
+    return ((n & 0x000000FF)
+            + ((n & 0x0000FF00) >> 8)
+            + ((n & 0x00FF0000) >> 16)
+            + ((n & 0xFF000000) >> 24));
+}
+
 int hash(void* key) {
     int hash;
-    long addr;
     addr_pair* ap = (addr_pair*)key;
         
-    addr = (long)ap->src.s_addr;
+    if (ap->af == AF_INET6) {
+        uint32_t* addr6 = ap->src6.s6_addr32;
 
-    hash = ((addr & 0x000000FF)
-            + (addr & 0x0000FF00 >> 8)
-            + (addr & 0x00FF0000 >> 16)
-            + (addr & 0xFF000000 >> 24)
-            + ap->src_port) % 0xFF;
-
-    addr = (long)ap->dst.s_addr;
-    hash = ( hash + (addr & 0x000000FF)
-            + (addr & 0x0000FF00 >> 8)
-            + (addr & 0x00FF0000 >> 16)
-            + (addr & 0xFF000000 >> 24)
-            + ap->dst_port) % 0xFF;
+        hash = ( hash_uint32(addr6[0])
+                + hash_uint32(addr6[1])
+                + hash_uint32(addr6[2])
+                + hash_uint32(addr6[3])
+                + ap->src_port) % 0xFF;
+
+        addr6 = ap->dst6.s6_addr32;
+        hash = ( hash + hash_uint32(addr6[0])
+                + hash_uint32(addr6[1])
+                + hash_uint32(addr6[2])
+                + hash_uint32(addr6[3])
+                + ap->dst_port) % 0xFF;
+    } else {
+        in_addr_t addr = ap->src.s_addr;
+
+        hash = ( hash_uint32(addr)
+                + ap->src_port) % 0xFF;
+
+        addr = ap->dst.s_addr;
+        hash = ( hash + hash_uint32(addr)
+                + ap->dst_port) % 0xFF;
+    }
 
     return hash;
 }
--- iftop-0.17/options.h		2006-02-08 03:57:43.000000000 +0100
+++ iftop-0.17/options.h.ipv6-ll	2010-11-18 23:59:55.331875892 +0100
@@ -78,6 +78,13 @@
     struct in_addr netfilternet;
     struct in_addr netfiltermask;
 
+    int netfilter6;
+    struct in6_addr netfilter6net;
+    struct in6_addr netfilter6mask;
+
+    /* Account for link-local traffic. */
+    int link_local;
+
     char *config_file;
     int config_file_specified;
 
--- iftop-0.17/addr_hash.h		2003-02-06 11:51:24.000000000 +0100
+++ iftop-0.17/addr_hash.h.ipv6-ll	2010-11-19 00:01:01.432806966 +0100
@@ -12,11 +12,18 @@
 #include "hash.h"
 
 typedef struct {
+    int af;
     unsigned short int protocol;
     unsigned short int src_port;
-    struct in_addr src;
+    union {
+        struct in_addr src;
+        struct in6_addr src6;
+    };
     unsigned short int dst_port;
-    struct in_addr dst;
+    union {
+        struct in_addr dst;
+        struct in6_addr dst6;
+    };
 } addr_pair;
 
 typedef addr_pair key_type;      /* index into hash table */
--- iftop-0.17/ns_hash.c		2002-10-21 15:08:23.000000000 +0200
+++ iftop-0.17/ns_hash.c.ipv6-ll	2010-11-19 00:03:09.777872505 +0100
@@ -2,6 +2,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in_systm.h>
@@ -14,29 +15,36 @@
 #define hash_table_size 256
 
 int ns_hash_compare(void* a, void* b) {
-    struct in_addr* aa = (struct in_addr*)a;
-    struct in_addr* bb = (struct in_addr*)b;
-    return (aa->s_addr == bb->s_addr);
+    struct in6_addr* aa = (struct in6_addr*)a;
+    struct in6_addr* bb = (struct in6_addr*)b;
+    return IN6_ARE_ADDR_EQUAL(aa, bb);
+}
+
+static int __inline__ hash_uint32(uint32_t n) {
+    return ((n & 0x000000FF)
+            + ((n & 0x0000FF00) >> 8)
+            + ((n & 0x00FF0000) >> 16)
+            + ((n & 0xFF000000) >> 24));
 }
 
 int ns_hash_hash(void* key) {
     int hash;
-    long addr;
-        
-    addr = (long)((struct in_addr*)key)->s_addr;
-
-    hash = ((addr & 0x000000FF)
-            + (addr & 0x0000FF00 >> 8)
-            + (addr & 0x00FF0000 >> 16)
-            + (addr & 0xFF000000 >> 24)) % 0xFF;
+    uint32_t* addr6 = ((struct in6_addr *) key)->s6_addr32;
+
+    hash = ( hash_uint32(addr6[0])
+            + hash_uint32(addr6[1])
+            + hash_uint32(addr6[2])
+            + hash_uint32(addr6[3])) % 0xFF;
 
     return hash;
 }
 
 void* ns_hash_copy_key(void* orig) {
-    struct in_addr* copy;
+    struct in6_addr* copy;
+
     copy = xmalloc(sizeof *copy);
-    *copy = *(struct in_addr*)orig;
+    memcpy(copy, orig, sizeof *copy);
+
     return copy;
 }
 
--- iftop-0.17/cfgfile.c		2010-11-18 23:34:24.782250224 +0100
+++ iftop-0.17/cfgfile.c.ipv6-ll	2010-11-19 00:03:37.471809737 +0100
@@ -36,6 +36,8 @@
 	"log-scale", 
 	"max-bandwidth",
 	"net-filter", 
+        "net-filter6", 
+        "link-local",
 	"port-display", 
 	NULL
 };
--- iftop-0.17/resolver.h		2003-02-06 11:51:24.000000000 +0100
+++ iftop-0.17/resolver.h.ipv6-ll	2010-11-19 00:04:01.286932084 +0100
@@ -14,6 +14,6 @@
 
 void resolver_initialise(void);
 
-void resolve(struct in_addr* addr, char* result, int buflen);
+void resolve(int af, struct in6_addr* addr, char* result, int buflen);
 
 #endif /* __RESOLVER_H_ */
--- iftop-0.17/ui.c			2010-11-18 23:34:24.776128888 +0100
+++ iftop-0.17/ui.c.ipv6-ll		2010-11-19 00:07:32.473809978 +0100
@@ -109,19 +109,19 @@
  * Compare two screen lines based on hostname / IP.  Fall over to compare by
  * bandwidth.
  */
-int screen_line_host_compare(struct in_addr* a, struct in_addr* b, host_pair_line* aa, host_pair_line* bb) {
+int screen_line_host_compare(void* a, void* b, host_pair_line* aa, host_pair_line* bb) {
     char hosta[HOSTNAME_LENGTH], hostb[HOSTNAME_LENGTH];
     int r;
 
     /* This isn't overly efficient because we resolve again before 
        display. */
     if (options.dnsresolution) {
-        resolve(a, hosta, HOSTNAME_LENGTH);
-        resolve(b, hostb, HOSTNAME_LENGTH);
+        resolve(aa->ap.af, a, hosta, HOSTNAME_LENGTH);
+        resolve(bb->ap.af, b, hostb, HOSTNAME_LENGTH);
     }
     else {
-        strcpy(hosta, inet_ntoa(*a));
-        strcpy(hostb, inet_ntoa(*b));
+        inet_ntop(aa->ap.af, a, hosta, sizeof(hosta));
+        inet_ntop(bb->ap.af, b, hostb, sizeof(hostb));
     }
 
     r = strcmp(hosta, hostb);
@@ -149,10 +149,10 @@
       return screen_line_bandwidth_compare(aa, bb, 2);
     }
     else if(options.sort == OPTION_SORT_SRC) {
-      return screen_line_host_compare(&(aa->ap.src), &(bb->ap.src), aa, bb);
+      return screen_line_host_compare(&(aa->ap.src6), &(bb->ap.src6), aa, bb);
     }
     else if(options.sort == OPTION_SORT_DEST) {
-      return screen_line_host_compare(&(aa->ap.dst), &(bb->ap.dst), aa, bb);
+      return screen_line_host_compare(&(aa->ap.dst6), &(bb->ap.dst6), aa, bb);
     }
 
     return 1;
@@ -487,10 +487,10 @@
 
         /* Aggregate hosts, if required */
         if(options.aggregate_src) {
-            ap.src.s_addr = 0;
+            memset(&ap.src6, '\0', sizeof(ap.src6));
         }
         if(options.aggregate_dest) {
-            ap.dst.s_addr = 0;
+            memset(&ap.dst6, '\0', sizeof(ap.dst6));
         }
 
         /* Aggregate ports, if required */
@@ -535,7 +535,7 @@
 
 }
 
-void sprint_host(char * line, struct in_addr* addr, unsigned int port, unsigned int protocol, int L) {
+void sprint_host(char * line, int af, struct in6_addr* addr, unsigned int port, unsigned int protocol, int L) {
     char hostname[HOSTNAME_LENGTH];
     char service[HOSTNAME_LENGTH];
     char* s_name;
@@ -546,14 +546,15 @@
 
     ip_service skey;
     int left;
-    if(addr->s_addr == 0) {
+
+    if(IN6_IS_ADDR_UNSPECIFIED(addr)) {
         sprintf(hostname, " * ");
     }
     else {
         if (options.dnsresolution)
-            resolve(addr, hostname, L);
+            resolve(af, addr, hostname, L);
         else
-            strcpy(hostname, inet_ntoa(*addr));
+            inet_ntop(af, addr, hostname, sizeof(hostname));
     }
     left = strlen(hostname);
 
@@ -637,8 +638,15 @@
                     L = HOSTNAME_LENGTH;
                 }
 
-                sprint_host(host1, &(screen_line->ap.src), screen_line->ap.src_port, screen_line->ap.protocol, L);
-                sprint_host(host2, &(screen_line->ap.dst), screen_line->ap.dst_port, screen_line->ap.protocol, L);
+                sprint_host(host1, screen_line->ap.af,
+                            &(screen_line->ap.src6),
+                            screen_line->ap.src_port,
+                            screen_line->ap.protocol, L);
+                sprint_host(host2, screen_line->ap.af,
+                            &(screen_line->ap.dst6),
+                            screen_line->ap.dst_port,
+                            screen_line->ap.protocol, L);
+
                 if(!screen_filter_match(host1) && !screen_filter_match(host2)) {
                   continue;
                 }
--- iftop-0.17/resolver.c		2005-12-24 12:37:43.000000000 +0100
+++ iftop-0.17/resolver.c.ipv6-ll	2010-11-19 00:10:28.356875754 +0100
@@ -25,7 +25,7 @@
 
 #define RESOLVE_QUEUE_LENGTH 20
 
-struct in_addr resolve_queue[RESOLVE_QUEUE_LENGTH];
+struct in6_addr resolve_queue[RESOLVE_QUEUE_LENGTH];
 
 pthread_cond_t resolver_queue_cond;
 pthread_mutex_t resolver_queue_mutex;
@@ -55,18 +55,48 @@
  * as NetBSD break the RFC and implement it in a non-thread-safe fashion, so
  * for the moment, the configure script won't try to use it.
  */
-char *do_resolve(struct in_addr *addr) {
-    struct sockaddr_in sin = {0};
+char *do_resolve(struct in6_addr *addr) {
+    struct sockaddr_in sin;
+    struct sockaddr_in6 sin6;
     char buf[NI_MAXHOST]; /* 1025 */
-    int res;
-    sin.sin_family = AF_INET;
-    sin.sin_addr = *addr;
-    sin.sin_port = 0;
+    int res, af;
+    uint32_t* probe;
 
-    if (getnameinfo((struct sockaddr*)&sin, sizeof sin, buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0)
-        return xstrdup(buf);
-    else
-        return NULL;
+    memset(&sin, '\0', sizeof(sin));
+    memset(&sin6, '\0', sizeof(sin6));
+
+    /* If the upper three (network byte order) uint32-parts
+     * are null, then there ought to be an IPv4 address here.
+     * Any such IPv6 would have to be 'xxxx::'. Neglectable? */
+    probe = (uint32_t *) addr;
+    af = (probe[1] || probe[2] || probe[3]) ? AF_INET6 : AF_INET;
+
+    switch (af) {
+        case AF_INET:
+            sin.sin_family = af;
+            sin.sin_port = 0;
+            memcpy(&sin.sin_addr, addr, sizeof(sin.sin_addr));
+
+            if (getnameinfo((struct sockaddr*)&sin, sizeof sin,
+                            buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0)
+                return xstrdup(buf);
+            else
+                return NULL;
+            break;
+        case AF_INET6:
+            sin6.sin6_family = af;
+            sin6.sin6_port = 0;
+            memcpy(&sin6.sin6_addr, addr, sizeof(sin6.sin6_addr));
+
+            if (getnameinfo((struct sockaddr*)&sin6, sizeof sin6,
+                            buf, sizeof buf, NULL, 0, NI_NAMEREQD) == 0)
+                return xstrdup(buf);
+            else
+                return NULL;
+            break;
+        default:
+            return NULL;
+    }
 }
 
 #elif defined(USE_GETHOSTBYADDR_R)
@@ -376,7 +406,7 @@
         /* Keep resolving until the queue is empty */
         while(head != tail) {
             char * hostname;
-            struct in_addr addr = resolve_queue[tail];
+            struct in6_addr addr = resolve_queue[tail];
 
             /* mutex always locked at this point */
 
@@ -427,7 +457,7 @@
 
 }
 
-void resolve(struct in_addr* addr, char* result, int buflen) {
+void resolve(int af, struct in6_addr* addr, char* result, int buflen) {
     char* hostname;
     union {
 	char **ch_pp;
@@ -443,12 +473,18 @@
             /* Found => already resolved, or on the queue */
         }
         else {
-            hostname = strdup(inet_ntoa(*addr));
+            hostname = xmalloc(INET6_ADDRSTRLEN);
+            inet_ntop(af, addr, hostname, INET6_ADDRSTRLEN);
             hash_insert(ns_hash, addr, hostname);
 
             if(((head + 1) % RESOLVE_QUEUE_LENGTH) == tail) {
                 /* queue full */
             }
+            else if((af == AF_INET6)
+                        && (IN6_IS_ADDR_LINKLOCAL(addr)
+                            || IN6_IS_ADDR_SITELOCAL(addr))) {
+                /* Link-local and site-local stay numerical. */
+            }
             else {
                 resolve_queue[head] = *addr;
                 head = (head + 1) % RESOLVE_QUEUE_LENGTH;
--- iftop-0.17/iftop.8			2010-11-18 23:34:24.763118260 +0100
+++ iftop-0.17/iftop.8.ipv6-ll		2010-11-19 00:12:51.523808338 +0100
@@ -11,7 +11,8 @@
 
 .SH SYNOPSIS
 \fBiftop\fP \fB-h\fP |
-[\fB-nNpbBP\fP] [\fB-i\fP \fIinterface\fP] [\fB-f\fP \fIfilter code\fP] [\fB-F\fP \fInet\fP/\fImask\fP]
+[\fB-nNpblBP\fP] [\fB-i\fP \fIinterface\fP] [\fB-f\fP \fIfilter code\fP] [\fB-F\fP \fInet\fP/\fImask\fP]
+[\fB-G\fP \fInet6\fP/\fImask6\fP]
 
 .SH DESCRIPTION
 \fBiftop\fP listens to network traffic on a named \fIinterface\fP, or on the
@@ -65,6 +66,10 @@
 \fB-P\fP
 Turn on port display.
 .TP
+\fB-l\fP
+Display and count datagrams addressed to or from link-local IPv6 addresses.
+The default is not to display that address category.
+.TP
 \fB-b\fP
 Don't display bar graphs of traffic. 
 .TP
@@ -79,12 +84,17 @@
 counted, so the specified code is evaluated as \fB(\fP\fIfilter code\fP\fB) and ip\fP.
 .TP
 \fB-F\fP \fInet\fP/\fImask\fP
-Specifies a network for traffic analysis.  If specified, iftop will only
+Specifies an IPv4 network for traffic analysis.  If specified, iftop will only
 include packets flowing in to or out of the given network, and packet direction
 is determined relative to the network boundary, rather than to the interface.
 You may specify \fImask\fP as a dotted quad, such as /255.255.255.0, or as a
 single number specifying the number of bits set in the netmask, such as /24.
 .TP
+\fB-G\fP \fInet6\fP/\fImask6\fP
+Specifies an IPv6 network for traffic analysis. The value of \fImask6\fP can be
+given as a prefix length or as a numerical address string for more compound
+bitmasking.
+.TP
 \fB-c\fP \fIconfig file\fP
 Specifies an alternate config file.  If not specified, iftop will use
 \fB~/.iftoprc\fP if it exists.  See below for a description of config files
@@ -212,6 +222,9 @@
 \fBport-display:\fP \fI(off|source-only|destination-only|on)\fP
 Controls display of port numbers.
 .TP
+\fBlink-local:\fP \fI(yes|no)\fP
+Determines displaying of link-local IPv6 addresses.
+.TP
 \fBhide-source:\fP \fI(yes|no)\fP
 Hides source host names.
 .TP
@@ -241,6 +254,9 @@
 \fBnet-filter:\fP \fInet/mask\fP
 Defines an IP network boundary for determining packet direction.
 .TP
+\fBnet-filter6:\fP \fInet6/mask6\fP
+Defines an IPv6 network boundary for determining packet direction.
+.TP
 \fBscreen-filter:\fP \fIregexp\fP
 Sets a regular expression to filter screen output.