Add assert()s to tree.h to soothe Clang static analyzer.
[darkstat] / localip.c
index 15daa46..f7b7f93 100644 (file)
--- a/localip.c
+++ b/localip.c
@@ -1,22 +1,27 @@
 /* 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)
  */
 
 #include "addr.h"
+#include "bsd.h" /* for strlcpy */
 #include "config.h" /* for HAVE_IFADDRS_H */
-#include "conv.h" /* for strlcpy */
+#include "conv.h"
 #include "err.h"
 #include "localip.h"
+#include "now.h"
 
 #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;
+void localip_init(struct local_ips *ips) {
+   ips->is_valid = 0;
+   ips->last_update_mono = 0;
+   ips->num_addrs = 0;
+   ips->addrs = NULL;
+}
+
+void localip_free(struct local_ips *ips) {
+   if (ips->addrs != NULL)
+      free(ips->addrs);
+}
 
-void
-localip_init(const char *interface)
-{
-   iface = interface;
-   localip_update();
+static void add_ip(const char *iface,
+                   struct local_ips *ips,
+                   int *idx,
+                   struct addr *a) {
+   if (ips->num_addrs <= *idx) {
+      /* Grow. */
+      ips->addrs = xrealloc(ips->addrs, sizeof(*(ips->addrs)) * (*idx + 1));
+      ips->num_addrs++;
+      assert(ips->num_addrs > *idx);
+      verbosef("interface '%s' gained new address %s", iface, addr_to_str(a));
+   } else {
+      /* Warn about changed address. */
+      if (!addr_equal(ips->addrs + *idx, a)) {
+         static char before[INET6_ADDRSTRLEN];
+         strncpy(before, addr_to_str(ips->addrs + *idx), INET6_ADDRSTRLEN);
+         verbosef("interface '%s' address %d/%d changed from %s to %s",
+            iface, *idx+1, ips->num_addrs, before, addr_to_str(a));
+      }
+   }
+   ips->addrs[*idx] = *a;
+   (*idx)++;
 }
 
-void
-localip_update(void)
-{
-   /* defaults */
-   localip4.family = IPv4;
-   localip4.ip.v4 = 0;
+/* Returns 0 on failure. */
+void localip_update(const char *iface, struct local_ips *ips) {
+   struct addr a;
+   int new_addrs = 0;
+
+   if (iface == NULL) {
+      /* reading from capfile */
+      ips->is_valid = 0;
+      return;
+   }
 
-   localip6.family = IPv6;
-   memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6));
+   if (ips->last_update_mono == now_mono()) {
+      /* Too soon, bail out. */
+      return;
+   }
+   ips->last_update_mono = now_mono();
 
-   if (iface != NULL) {
-      /* not reading from capfile */
 #ifdef HAVE_IFADDRS_H
-      int got_v4 = 0, got_v6 = 0;
+   {
       struct ifaddrs *ifas, *ifa;
 
       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 (got_v4 && got_v6)
-            break;   /* Task is already complete. */
+         err(1, "getifaddrs() failed");
 
+      for (ifa=ifas; ifa; ifa=ifa->ifa_next) {
          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) && !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)
+            continue;   /* This can be NULL, e.g. for ppp0. */
 
+         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);
+         }
+         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);
          }
       }
-
       freeifaddrs(ifas);
-
-      /* Report an error if IPv4 address could not be found. */
-      if (!got_v4)
-          err(1, "can't get own IPv4 address on interface \"%s\"", iface);
-
+   }
 #else /* don't HAVE_IFADDRS_H */
-
+   {
       int tmp = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
       struct ifreq ifr;
       struct sockaddr sa;
 
       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
-            err(1, "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;
    }
+}
 
-   if (!addr_equal(&last_localip4, &localip4)) {
-      verbosef("localip4 update(%s) = %s", iface, addr_to_str(&localip4));
-      last_localip4 = localip4;
-   }
-   if (!addr_equal(&last_localip6, &localip6)) {
-      verbosef("localip6 update(%s) = %s", iface, addr_to_str(&localip6));
-      last_localip6 = localip6;
+int is_localip(const struct addr * const a,
+               const struct local_ips * const ips) {
+   int i;
+
+   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: */