First graphical accounting of IPv6.
authorMats Erik Andersson <mats.andersson@gisladisker.se>
Sat, 29 May 2010 10:06:52 +0000 (12:06 +0200)
committerEmil Mikulic <emikulic@gmail.com>
Mon, 9 May 2011 12:40:19 +0000 (22:40 +1000)
There are two small changes to "http.c" that corrects
the format string and that reuses the exact length of
the structure in use.

The main changes, however, deal with detection, reporting,
and accounting of IPv6 traffic over the chosen interface.

The present code is only able to match the exact
IPv6 address of the interface to the packet, be it
inbound or outbound. No possibility to set the
netmask length.

The accounting only adds sizes of the packets,
it never goes on to deal with fine grained information.
You will see a conditional in 'acct.c' with a comment
stating that IPv6 proceeds no further.

acct.c
decode.c
decode.h
http.c
localip.c
localip.h

diff --git a/acct.c b/acct.c
index fb56034..ef6d514 100644 (file)
--- a/acct.c
+++ b/acct.c
@@ -104,18 +104,26 @@ acct_for(const pktsummary *sm)
    /* Graphs. */
    dir_in = dir_out = 0;
 
    /* Graphs. */
    dir_in = dir_out = 0;
 
-   if (using_localnet) {
-      if ((sm->src_ip & localmask) == localnet)
-         dir_out = 1;
-      if ((sm->dest_ip & localmask) == localnet)
-         dir_in = 1;
-      if (dir_in == 1 && dir_out == 1)
-         /* Traffic staying within the network isn't counted. */
-         dir_in = dir_out = 0;
-   } else {
-      if (sm->src_ip == localip)
+   if (sm->af == AF_INET) {
+      if (using_localnet) {
+         if ((sm->src_ip & localmask) == localnet)
+            dir_out = 1;
+         if ((sm->dest_ip & localmask) == localnet)
+            dir_in = 1;
+         if (dir_in == 1 && dir_out == 1)
+            /* Traffic staying within the network isn't counted. */
+            dir_in = dir_out = 0;
+      } else {
+         if (sm->src_ip == localip)
+            dir_out = 1;
+         if (sm->dest_ip == localip)
+            dir_in = 1;
+      }
+   } else if (sm->af == AF_INET6) {
+      /* Only exact address has been implemented. */
+      if (memcmp(&sm->src_ip6, &localip6, sizeof(localip6)) == 0)
          dir_out = 1;
          dir_out = 1;
-      if (sm->dest_ip == localip)
+      if (memcmp(&sm->dest_ip6, &localip6, sizeof(localip6)) == 0)
          dir_in = 1;
    }
 
          dir_in = 1;
    }
 
@@ -128,6 +136,8 @@ acct_for(const pktsummary *sm)
       graph_acct((uint64_t)sm->len, GRAPH_IN);
    }
 
       graph_acct((uint64_t)sm->len, GRAPH_IN);
    }
 
+   if (sm->af == AF_INET6) return; /* Still no continuation for IPv6! */
+
    if (hosts_max == 0) return; /* skip per-host accounting */
 
    /* Hosts. */
    if (hosts_max == 0) return; /* skip per-host accounting */
 
    /* Hosts. */
@@ -195,6 +205,7 @@ acct_for(const pktsummary *sm)
       break;
 
    case IPPROTO_ICMP:
       break;
 
    case IPPROTO_ICMP:
+   case IPPROTO_ICMPV6:
       /* known protocol, don't complain about it */
       break;
 
       /* known protocol, don't complain about it */
       break;
 
index 54ada43..e20b0b3 100644 (file)
--- a/decode.c
+++ b/decode.c
@@ -49,6 +49,7 @@
 #include <net/if.h> /* struct ifreq */
 #include <netinet/in_systm.h> /* n_long */
 #include <netinet/ip.h> /* struct ip */
 #include <net/if.h> /* struct ifreq */
 #include <netinet/in_systm.h> /* n_long */
 #include <netinet/ip.h> /* struct ip */
+#include <netinet/ip6.h> /* struct ip6_hdr */
 #define __FAVOR_BSD
 #include <netinet/tcp.h> /* struct tcphdr */
 #include <netinet/udp.h> /* struct udphdr */
 #define __FAVOR_BSD
 #include <netinet/tcp.h> /* struct tcphdr */
 #include <netinet/udp.h> /* struct udphdr */
@@ -71,6 +72,8 @@ static void decode_raw(u_char *, const struct pcap_pkthdr *,
    const u_char *);
 static void decode_ip(const u_char *pdata, const uint32_t len,
    pktsummary *sm);
    const u_char *);
 static void decode_ip(const u_char *pdata, const uint32_t len,
    pktsummary *sm);
+static void decode_ipv6(const u_char *pdata, const uint32_t len,
+   pktsummary *sm);
 
 /* Link-type header information */
 static const linkhdr_t linkhdrs[] = {
 
 /* Link-type header information */
 static const linkhdr_t linkhdrs[] = {
@@ -130,6 +133,17 @@ ip_to_str(const in_addr_t ip)
    return (inet_ntoa(in));
 }
 
    return (inet_ntoa(in));
 }
 
+char ip6str[INET6_ADDRSTRLEN];
+
+char *
+ip6_to_str(const struct in6_addr *ip6)
+{
+   ip6str[0] = '\0';
+   inet_ntop(AF_INET6, ip6, ip6str, sizeof(ip6str));
+
+   return (ip6str);
+}
+
 /* Decoding functions. */
 static void
 decode_ether(u_char *user _unused_,
 /* Decoding functions. */
 static void
 decode_ether(u_char *user _unused_,
@@ -158,6 +172,7 @@ decode_ether(u_char *user _unused_,
    type = ntohs( hdr->ether_type );
    switch (type) {
    case ETHERTYPE_IP:
    type = ntohs( hdr->ether_type );
    switch (type) {
    case ETHERTYPE_IP:
+   case ETHERTYPE_IPV6:
       if (!want_pppoe) {
          decode_ip(pdata + ETHER_HDR_LEN,
                    pheader->caplen - ETHER_HDR_LEN, &sm);
       if (!want_pppoe) {
          decode_ip(pdata + ETHER_HDR_LEN,
                    pheader->caplen - ETHER_HDR_LEN, &sm);
@@ -203,6 +218,12 @@ decode_loop(u_char *user _unused_,
       sm.time = pheader->ts.tv_sec;
       acct_for(&sm);
    }
       sm.time = pheader->ts.tv_sec;
       acct_for(&sm);
    }
+   else if (family == AF_INET6) {
+      /* XXX: Check this! */
+      decode_ip(pdata + NULL_HDR_LEN, pheader->caplen - NULL_HDR_LEN, &sm);
+      sm.time = pheader->ts.tv_sec;
+      acct_for(&sm);
+   }
    else
       verbosef("loop: unknown family (%x)", family);
 }
    else
       verbosef("loop: unknown family (%x)", family);
 }
@@ -290,6 +311,7 @@ decode_linux_sll(u_char *user _unused_,
    type = ntohs( hdr->ether_type );
    switch (type) {
    case ETHERTYPE_IP:
    type = ntohs( hdr->ether_type );
    switch (type) {
    case ETHERTYPE_IP:
+   case ETHERTYPE_IPV6:
       decode_ip(pdata + SLL_HDR_LEN, pheader->caplen - SLL_HDR_LEN, &sm);
       sm.time = pheader->ts.tv_sec;
       acct_for(&sm);
       decode_ip(pdata + SLL_HDR_LEN, pheader->caplen - SLL_HDR_LEN, &sm);
       sm.time = pheader->ts.tv_sec;
       acct_for(&sm);
@@ -320,6 +342,11 @@ decode_ip(const u_char *pdata, const uint32_t len, pktsummary *sm)
 {
    const struct ip *hdr = (const struct ip *)pdata;
 
 {
    const struct ip *hdr = (const struct ip *)pdata;
 
+   if (hdr->ip_v == 6) {
+      /* Redirect parsing of IPv6 packets. */
+      decode_ipv6(pdata, len, sm);
+      return;
+   }
    if (len < IP_HDR_LEN) {
       verbosef("ip: packet too short (%u bytes)", len);
       return;
    if (len < IP_HDR_LEN) {
       verbosef("ip: packet too short (%u bytes)", len);
       return;
@@ -330,6 +357,7 @@ decode_ip(const u_char *pdata, const uint32_t len, pktsummary *sm)
    }
 
    sm->len = ntohs(hdr->ip_len);
    }
 
    sm->len = ntohs(hdr->ip_len);
+   sm->af = AF_INET;
    sm->proto = hdr->ip_p;
    sm->src_ip = hdr->ip_src.s_addr;
    sm->dest_ip = hdr->ip_dst.s_addr;
    sm->proto = hdr->ip_p;
    sm->src_ip = hdr->ip_src.s_addr;
    sm->dest_ip = hdr->ip_dst.s_addr;
@@ -370,4 +398,56 @@ decode_ip(const u_char *pdata, const uint32_t len, pktsummary *sm)
    }
 }
 
    }
 }
 
+static void
+decode_ipv6(const u_char *pdata, const uint32_t len, pktsummary *sm)
+{
+   const struct ip6_hdr *hdr = (const struct ip6_hdr *)pdata;
+
+   if (len < IPV6_HDR_LEN) {
+      verbosef("ipv6: packet too short (%u bytes)", len);
+      return;
+   }
+
+   sm->len = ntohs(hdr->ip6_plen) + IPV6_HDR_LEN;
+   sm->af = AF_INET6;
+   sm->proto = hdr->ip6_nxt;
+   memcpy(&sm->src_ip6, &hdr->ip6_src, sizeof(sm->src_ip6));
+   memcpy(&sm->dest_ip6, &hdr->ip6_dst, sizeof(sm->dest_ip6));
+
+   switch (sm->proto) {
+      case IPPROTO_TCP: {
+         const struct tcphdr *thdr =
+            (const struct tcphdr *)(pdata + IPV6_HDR_LEN);
+         if (len < IPV6_HDR_LEN + TCP_HDR_LEN) {
+            verbosef("tcp6: packet too short (%u bytes)", len);
+            return;
+         }
+         sm->src_port = ntohs(thdr->th_sport);
+         sm->dest_port = ntohs(thdr->th_dport);
+         sm->tcp_flags = thdr->th_flags &
+            (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG);
+         break;
+      }
+
+      case IPPROTO_UDP: {
+         const struct udphdr *uhdr =
+            (const struct udphdr *)(pdata + IPV6_HDR_LEN);
+         if (len < IPV6_HDR_LEN + UDP_HDR_LEN) {
+            verbosef("udp6: packet too short (%u bytes)", len);
+            return;
+         }
+         sm->src_port = ntohs(uhdr->uh_sport);
+         sm->dest_port = ntohs(uhdr->uh_dport);
+         break;
+      }
+
+      case IPPROTO_ICMPV6:
+         /* known protocol, don't complain about it */
+         break;
+
+      default:
+         verbosef("ipv6: unknown protocol %d", sm->proto);
+   }
+}
+
 /* vim:set ts=3 sw=3 tw=78 expandtab: */
 /* vim:set ts=3 sw=3 tw=78 expandtab: */
index abc4715..73c20e7 100644 (file)
--- a/decode.h
+++ b/decode.h
@@ -9,12 +9,14 @@
 
 #include <pcap.h>
 #include <netinet/in.h> /* in_addr_t */
 
 #include <pcap.h>
 #include <netinet/in.h> /* in_addr_t */
+#include <netinet/ip.h> /* struct ip */
 
 #define PPP_HDR_LEN     4
 #define FDDI_HDR_LEN    21
 
 #define PPP_HDR_LEN     4
 #define FDDI_HDR_LEN    21
-#define IP_HDR_LEN      20
-#define TCP_HDR_LEN     20
-#define UDP_HDR_LEN     8
+#define IP_HDR_LEN      sizeof(struct ip)
+#define IPV6_HDR_LEN    sizeof(struct ip6_hdr)
+#define TCP_HDR_LEN     sizeof(struct tcphdr)
+#define UDP_HDR_LEN     sizeof(struct udphdr)
 #define NULL_HDR_LEN    4
 #define PPPOE_HDR_LEN   8
 #define SLL_HDR_LEN     16
 #define NULL_HDR_LEN    4
 #define PPPOE_HDR_LEN   8
 #define SLL_HDR_LEN     16
@@ -33,12 +35,21 @@ typedef struct {
 const linkhdr_t *getlinkhdr(int linktype);
 int getsnaplen(const linkhdr_t *lh);
 char *ip_to_str(const in_addr_t ip);
 const linkhdr_t *getlinkhdr(int linktype);
 int getsnaplen(const linkhdr_t *lh);
 char *ip_to_str(const in_addr_t ip);
+char *ip6_to_str(const struct in6_addr *ip6);
 
 typedef struct {
    /* Fields are in host byte order (except IPs) */
 
 typedef struct {
    /* Fields are in host byte order (except IPs) */
-   in_addr_t src_ip, dest_ip;
+   union {
+      in_addr_t src_ip;
+      struct in6_addr src_ip6;
+   };
+   union {
+      in_addr_t dest_ip;
+      struct in6_addr dest_ip6;
+   };
    time_t time;
    uint16_t len;
    time_t time;
    uint16_t len;
+   uint8_t af;                   /* AF_{UNSPEC, INET, INET6} */
    uint8_t proto;                /* IPPROTO_{TCP, UDP, ICMP} */
    uint8_t tcp_flags;            /* only for TCP */
    uint16_t src_port, dest_port; /* only for TCP, UDP */
    uint8_t proto;                /* IPPROTO_{TCP, UDP, ICMP} */
    uint8_t tcp_flags;            /* only for TCP */
    uint16_t src_port, dest_port; /* only for TCP, UDP */
diff --git a/http.c b/http.c
index b9d8c58..b232705 100644 (file)
--- a/http.c
+++ b/http.c
@@ -324,10 +324,10 @@ static void accept_connection(void)
     memcpy(&conn->client, &addrin, sizeof(conn->client));
     LIST_INSERT_HEAD(&connlist, conn, entries);
 
     memcpy(&conn->client, &addrin, sizeof(conn->client));
     LIST_INSERT_HEAD(&connlist, conn, entries);
 
-    getnameinfo((struct sockaddr *) &addrin, sizeof(addrin),
+    getnameinfo((struct sockaddr *) &addrin, sin_size,
             ipaddr, sizeof(ipaddr), portstr, sizeof(portstr),
             NI_NUMERICHOST | NI_NUMERICSERV);
             ipaddr, sizeof(ipaddr), portstr, sizeof(portstr),
             NI_NUMERICHOST | NI_NUMERICSERV);
-    verbosef("accepted connection from %s:%u", ipaddr, portstr);
+    verbosef("accepted connection from %s:%s", ipaddr, portstr);
 }
 
 
 }
 
 
index 3e6fd38..96d5b87 100644 (file)
--- a/localip.c
+++ b/localip.c
@@ -9,24 +9,24 @@
 
 #include "darkstat.h"
 #include "conv.h" /* for strlcpy */
 
 #include "darkstat.h"
 #include "conv.h" /* for strlcpy */
-#include "decode.h" /* for ip_to_str */
+#include "decode.h" /* for ip_to_str, ip6_to_str */
 #include "err.h"
 #include "localip.h"
 
 #include "err.h"
 #include "localip.h"
 
-#ifdef HAVE_SYS_SOCKIO_H
-# include <sys/sockio.h> /* for SIOCGIFADDR, especially on Solaris */
-#endif
-#include <sys/socket.h>
-#include <sys/ioctl.h>
 #include <net/if.h>
 #include <net/if.h>
+#include <ifaddrs.h>
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
 
 static const char *iface = NULL;
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
 
 static const char *iface = NULL;
+
 in_addr_t localip = 0;
 static in_addr_t last_localip = 0;
 
 in_addr_t localip = 0;
 static in_addr_t last_localip = 0;
 
+struct in6_addr localip6;
+static struct in6_addr last_localip6;
+
 void
 localip_init(const char *interface)
 {
 void
 localip_init(const char *interface)
 {
@@ -37,29 +37,57 @@ localip_init(const char *interface)
 void
 localip_update(void)
 {
 void
 localip_update(void)
 {
-   int tmp = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
-   struct ifreq ifr;
-   struct sockaddr sa;
+   struct ifaddrs *ifas, *ifa;
+   int flags = 0;
+
+#define HAS_IPV4  0x01
+#define HAS_IPV6  0x02
 
    if (iface == NULL) {
       /* reading from capfile */
       localip = 0;
 
    if (iface == NULL) {
       /* reading from capfile */
       localip = 0;
+      memset(&localip6, '\0', sizeof(localip6));
       return;
    }
 
       return;
    }
 
-   strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
-   ifr.ifr_addr.sa_family = AF_INET;
-   if (ioctl(tmp, SIOCGIFADDR, &ifr) == -1) {
-      if (errno == EADDRNOTAVAIL) {
-         /* lost IP, e.g. ifconfig eth0 delete, don't die */
-         localip = 0;
-         close(tmp);
-         return;
-      } else
-         err(1, "can't get own IP address on interface \"%s\"", iface);
+   if (getifaddrs(&ifas) < 0)
+      err(1, "can't get own IP address on interface \"%s\"", iface);
+
+   for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
+      if (flags == (HAS_IPV4 | HAS_IPV6))
+         break;   /* Task is already complete. */
+
+      if (strncmp(ifa->ifa_name, iface, IFNAMSIZ))
+         continue;   /* Wrong interface. */
+
+      /* The first IPv4 name is always functional. */
+      if ( (ifa->ifa_addr->sa_family == AF_INET)
+            && ! (flags & HAS_IPV4) ) {
+         /* Good IPv4 address. */
+         localip = ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr.s_addr;
+         flags |= HAS_IPV4;
+         continue;
+      }
+
+      /* IPv6 needs some obvious exceptions. */
+      if( ifa->ifa_addr->sa_family == AF_INET6 ) {
+         struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
+
+         if( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr.s6_addr))
+            || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr.s6_addr)) )
+            continue;
+         else
+            /* Only standard IPv6 can reach this point. */
+            memcpy(&localip6, &sa6->sin6_addr, sizeof(localip6));
+            flags |= HAS_IPV6;
+      }
    }
    }
-   close(tmp);
-   sa = ifr.ifr_addr;
+
+   freeifaddrs(ifas);
+
+   /* Repport an error if IPv4 address could not be found. */
+   if ( !(flags & HAS_IPV4) )
+       err(1, "can't get own IPv4 address on interface \"%s\"", iface);
 
    /* struct sockaddr {
     *      sa_family_t     sa_family;      * address family, AF_xxx
 
    /* struct sockaddr {
     *      sa_family_t     sa_family;      * address family, AF_xxx
@@ -74,11 +102,14 @@ localip_update(void)
     *      __u32   s_addr;
     */
 
     *      __u32   s_addr;
     */
 
-   localip = ((struct sockaddr_in*)&sa)->sin_addr.s_addr;
    if (last_localip != localip) {
       verbosef("local_ip update(%s) = %s", iface, ip_to_str(localip));
       last_localip = localip;
    }
    if (last_localip != localip) {
       verbosef("local_ip update(%s) = %s", iface, ip_to_str(localip));
       last_localip = localip;
    }
+   if (memcmp(&last_localip6, &localip6, sizeof(localip6))) {
+      verbosef("local_ip6 update(%s) = %s", iface, ip6_to_str(&localip6));
+      memcpy(&last_localip6, &localip6, sizeof(localip6));
+   }
 }
 
 /* vim:set ts=3 sw=3 tw=78 expandtab: */
 }
 
 /* vim:set ts=3 sw=3 tw=78 expandtab: */
index 95a90bd..07fff19 100644 (file)
--- a/localip.h
+++ b/localip.h
@@ -10,6 +10,7 @@
 #include <netinet/in.h> /* for in_addr_t */
 
 extern in_addr_t localip;
 #include <netinet/in.h> /* for in_addr_t */
 
 extern in_addr_t localip;
+extern struct in6_addr localip6;
 
 void localip_init(const char *interface);
 void localip_update(void);
 
 void localip_init(const char *interface);
 void localip_update(void);