Support multiple local IPs on an interface.
authorEmil Mikulic <emikulic@gmail.com>
Sun, 1 Jul 2012 14:26:18 +0000 (00:26 +1000)
committerEmil Mikulic <emikulic@gmail.com>
Sat, 7 Jul 2012 09:35:50 +0000 (19:35 +1000)
Makefile.in
acct.c
cap.c
darkstat.c
localip.c
localip.h

index ceec6e8..58cf4d6 100644 (file)
@@ -144,7 +144,7 @@ hosts_sort.o: hosts_sort.c cdefs.h err.h hosts_db.h addr.h
 html.o: html.c config.h str.h html.h opt.h
 http.o: http.c cdefs.h config.h conv.h err.h graph_db.h hosts_db.h addr.h \
  http.h now.h queue.h str.h stylecss.h graphjs.h
-localip.o: localip.c addr.h config.h err.h cdefs.h localip.h bsd.h
+localip.o: localip.c addr.h config.h conv.h err.h cdefs.h localip.h bsd.h
 ncache.o: ncache.c conv.h err.h cdefs.h ncache.h tree.h bsd.h config.h
 pidfile.o: pidfile.c err.h cdefs.h str.h pidfile.h
 str.o: str.c conv.h err.h cdefs.h str.h
diff --git a/acct.c b/acct.c
index 62ad076..653fbef 100644 (file)
--- a/acct.c
+++ b/acct.c
@@ -136,23 +136,14 @@ acct_init_localnet(const char *spec)
 static int
 addr_is_local(const struct addr * const a)
 {
-   if (a->family == IPv4) {
-      if (using_localnet4) {
-         if (addr_inside(a, &localnet4, &localmask4))
-            return 1;
-      } else {
-         if (addr_equal(a, &localip4))
-            return 1;
-      }
-   } else {
-      assert(a->family == IPv6);
-      if (using_localnet6) {
-         if (addr_inside(a, &localnet6, &localmask6))
-            return 1;
-      } else {
-         if (addr_equal(a, &localip6))
-            return 1;
-      }
+   if (is_localip(a, local_ips))
+      return 1;
+   if (a->family == IPv4 && using_localnet4) {
+      if (addr_inside(a, &localnet4, &localmask4))
+         return 1;
+   } else if (a->family == IPv6 && using_localnet6) {
+      if (addr_inside(a, &localnet6, &localmask6))
+         return 1;
    }
    return 0;
 }
diff --git a/cap.c b/cap.c
index d07bf9c..1686ae9 100644 (file)
--- a/cap.c
+++ b/cap.c
@@ -299,7 +299,8 @@ cap_poll(fd_set *read_set
     * Once per capture poll, check our IP address.  It's used in accounting
     * for traffic graphs.
     */
-   localip_update(); /* FIXME: this might even be too often */
+   localip_update(opt_interface, local_ips);
+   /* FIXME: ^ this might run too often */
 
    total = 0;
    for (;;) {
index 0f4b70d..26f289b 100644 (file)
@@ -438,7 +438,9 @@ main(int argc, char **argv)
    graph_init();
    hosts_db_init();
    if (import_fn != NULL) db_import(import_fn);
-   localip_init(opt_interface);
+
+   local_ips = localip_make();
+   localip_update(opt_interface, local_ips);
 
    if (signal(SIGTERM, sig_shutdown) == SIG_ERR)
       errx(1, "signal(SIGTERM) failed");
@@ -511,6 +513,7 @@ main(int argc, char **argv)
    if (daylog_fn != NULL) daylog_free();
    ncache_free();
    if (pid_fn) pidfile_unlink();
+   localip_free(local_ips);
    verbosef("shut down");
    return (EXIT_SUCCESS);
 }
index 0c15409..53820d6 100644 (file)
--- a/localip.c
+++ b/localip.c
@@ -1,7 +1,7 @@
 /* darkstat 3
  * copyright (c) 2001-2011 Emil Mikulic.
  *
- * localip.c: determine local IP of our capture interface
+ * localip.c: determine local IPs of an interface
  *
  * You may use, modify and redistribute this file under the terms of the
  * GNU General Public License version 2. (see COPYING.GPL)
@@ -9,14 +9,18 @@
 
 #include "addr.h"
 #include "config.h" /* for HAVE_IFADDRS_H */
+#include "conv.h"
 #include "err.h"
 #include "localip.h"
 #include "bsd.h" /* for strlcpy */
 
 #include <sys/socket.h>
 #include <net/if.h>
+#include <assert.h>
 #include <errno.h>
+#include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
 
 #ifdef HAVE_IFADDRS_H
 # include <sys/ioctl.h>
 #endif
 
-static const char *iface = NULL;
-struct addr localip4, localip6;
-static struct addr last_localip4, last_localip6;
+struct local_ips *local_ips;
 
-void
-localip_init(const char *interface)
-{
-   iface = interface;
+struct local_ips *localip_make(void) {
+   struct local_ips *ips = xmalloc(sizeof(*ips));
 
-   /* defaults */
-   localip4.family = IPv4;
-   localip4.ip.v4 = 0;
-
-   localip6.family = IPv6;
-   memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6));
-
-   last_localip4 = localip4;
-   last_localip6 = localip6;
+   ips->is_valid = 0;
+   ips->last_update = 0;
+   ips->num_addrs = 0;
+   ips->addrs = NULL;
+   return ips;
+}
 
-   /* initial update */
-   localip_update();
+void localip_free(struct local_ips *ips) {
+   if (ips->addrs != NULL)
+      free(ips->addrs);
+   free(ips);
 }
 
-static void
-localip_update_helper(void)
-{
-   /* defaults */
-   localip4.family = IPv4;
-   localip4.ip.v4 = 0;
+static void add_ip(const char *iface, struct local_ips *ips,
+                   int *index, struct addr *a) {
+   if (ips->num_addrs <= *index) {
+      /* Grow. */
+      ips->addrs = xrealloc(ips->addrs, sizeof(*(ips->addrs)) * (*index + 1));
+      ips->num_addrs++;
+      assert(ips->num_addrs > *index);
+      verbosef("interface '%s' gained new address %s", iface, addr_to_str(a));
+   } else {
+      /* Warn about changed address. */
+      if (!addr_equal(ips->addrs + *index, a)) {
+         static char before[INET6_ADDRSTRLEN];
+         strncpy(before, addr_to_str(ips->addrs + *index), INET6_ADDRSTRLEN);
+         verbosef("interface '%s' address %d/%d changed from %s to %s",
+            iface, *index+1, ips->num_addrs, before, addr_to_str(a));
+      }
+   }
+   ips->addrs[*index] = *a;
+   (*index)++;
+}
 
-   localip6.family = IPv6;
-   memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6));
+/* Returns 0 on failure. */
+void localip_update(const char *iface, struct local_ips *ips) {
+   struct addr a;
+   int new_addrs = 0;
 
-   if (iface == NULL)
-      return; /* reading from capfile */
+   if (iface == NULL) {
+      /* reading from capfile */
+      ips->is_valid = 0;
+      return;
+   }
 
 #ifdef HAVE_IFADDRS_H
    {
-      int got_v4 = 0, got_v6 = 0;
       struct ifaddrs *ifas, *ifa;
 
-      if (getifaddrs(&ifas) < 0) {
-         warn("can't getifaddrs() on interface \"%s\"", iface);
-         return;
-      }
-
-      for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
-         if (got_v4 && got_v6)
-            break;   /* Task is already complete. */
+      if (getifaddrs(&ifas) < 0)
+         err(1, "getifaddrs() failed");
 
+      for (ifa=ifas; ifa; ifa=ifa->ifa_next) {
          if (strncmp(ifa->ifa_name, iface, IFNAMSIZ))
             continue;   /* Wrong interface. */
 
          if (!ifa->ifa_addr)
             continue;   /* This can be NULL, e.g. for ppp0. */
 
-         /* The first IPv4 name is always functional. */
-         if ((ifa->ifa_addr->sa_family == AF_INET) && !got_v4)
-         {
-            /* Good IPv4 address. */
-            localip4.ip.v4 =
-               ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
-            got_v4 = 1;
-            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 (ifa->ifa_addr->sa_family == AF_INET) {
+            a.family = IPv4;
+            a.ip.v4 = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
+            add_ip(iface, ips, &new_addrs, &a);
+         } else if (ifa->ifa_addr->sa_family == AF_INET6) {
+            struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+# if 0
             if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))
                  || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) )
                continue;
-
-            /* Only standard IPv6 can reach this point. */
-            memcpy(&(localip6.ip.v6), &sa6->sin6_addr, sizeof(localip6.ip.v6));
-            got_v6 = 1;
-         }
+# endif
+            a.family = IPv6;
+            memcpy(&(a.ip.v6), &sa6->sin6_addr, sizeof(a.ip.v6));
+            add_ip(iface, ips, &new_addrs, &a);
+# ifdef AF_PACKET
+         } else if (ifa->ifa_addr->sa_family == AF_PACKET) {
+            /* ignore */
+# endif
+         } else
+            verbosef("unknown sa_family=%d on interface '%s'",
+               (int)ifa->ifa_addr->sa_family, iface);
       }
-
       freeifaddrs(ifas);
-
-      if (!got_v4)
-          warnx("can't get own IPv4 address on interface \"%s\"", iface);
    }
 #else /* don't HAVE_IFADDRS_H */
    {
@@ -121,34 +128,40 @@ localip_update_helper(void)
 
       strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
       ifr.ifr_addr.sa_family = AF_INET;
-      if (ioctl(tmp, SIOCGIFADDR, &ifr) == -1) {
-         if (errno == EADDRNOTAVAIL) {
-            verbosef("lost local IP");
-         } else
-            warn("can't get own IP address on interface \"%s\"", iface);
-      } else {
-         /* success! */
+      if (ioctl(tmp, SIOCGIFADDR, &ifr) != -1) {
          sa = ifr.ifr_addr;
-         localip4.ip.v4 = ((struct sockaddr_in*)&sa)->sin_addr.s_addr;
+         a.family = IPv4;
+         a.ip.v4 = ((struct sockaddr_in*)(&ifr.ifr_addr))->sin_addr.s_addr;
+         add_ip(iface, ips, &new_addrs, &a);
       }
       close(tmp);
    }
 #endif
+   if (new_addrs == 0) {
+      if (ips->is_valid)
+         verbosef("interface '%s' no longer has any addresses", iface);
+      ips->is_valid = 0;
+   } else {
+      if (!ips->is_valid)
+         verbosef("interface '%s' now has addresses", iface);
+      ips->is_valid = 1;
+      if (ips->num_addrs != new_addrs)
+         verbosef("interface '%s' number of addresses decreased from %d to %d",
+            iface, ips->num_addrs, new_addrs);
+      ips->num_addrs = new_addrs;
+   }
+   ips->last_update = time(NULL);
 }
 
-void
-localip_update(void)
-{
-   localip_update_helper();
+int is_localip(const struct addr * const a,
+               const struct local_ips * const ips) {
+   int i;
 
-   if (!addr_equal(&last_localip4, &localip4)) {
-      verbosef("%s ip4 update: %s", iface, addr_to_str(&localip4));
-      last_localip4 = localip4;
-   }
-   if (!addr_equal(&last_localip6, &localip6)) {
-      verbosef("%s ip6 update: %s", iface, addr_to_str(&localip6));
-      last_localip6 = localip6;
+   for (i=0; i<ips->num_addrs; i++) {
+      if (addr_equal(a, ips->addrs+i))
+         return 1;
    }
+   return 0;
 }
 
-/* vim:set ts=3 sw=3 tw=78 expandtab: */
+/* vim:set ts=3 sw=3 tw=80 et: */
index 21231b6..392a642 100644 (file)
--- a/localip.h
+++ b/localip.h
@@ -1,15 +1,31 @@
 /* darkstat 3
  * copyright (c) 2001-2011 Emil Mikulic.
  *
- * localip.h: determine local IP of our capture interface
+ * localip.h: determine the local IPs of an interface
  *
  * You may use, modify and redistribute this file under the terms of the
  * GNU General Public License version 2. (see COPYING.GPL)
  */
+#ifndef __DARKSTAT_LOCALIP_H
+#define __DARKSTAT_LOCALIP_H
 
-extern struct addr localip4, localip6;
+#include <time.h>
 
-void localip_init(const char *interface);
-void localip_update(void);
+struct local_ips {
+   int is_valid;
+   time_t last_update;
+   int num_addrs;
+   struct addr *addrs;
+};
 
-/* vim:set ts=3 sw=3 tw=78 expandtab: */
+extern struct local_ips *local_ips;
+
+struct local_ips *localip_make(void);
+void localip_free(struct local_ips *ips);
+
+void localip_update(const char *iface, struct local_ips *ips);
+int is_localip(const struct addr * const a,
+               const struct local_ips * const ips);
+
+#endif
+/* vim:set ts=3 sw=3 tw=80 et: */