92afce5087b4e5c472eddcffd148d5c266660e9d
[darkstat.git] / localip.c
1 /* darkstat 3
2  * copyright (c) 2001-2008 Emil Mikulic.
3  *
4  * localip.c: determine local IP of our capture interface
5  *
6  * You may use, modify and redistribute this file under the terms of the
7  * GNU General Public License version 2. (see COPYING.GPL)
8  */
9
10 #include "darkstat.h"
11 #include "addr.h"
12 #include "conv.h" /* for strlcpy */
13 #include "err.h"
14 #include "localip.h"
15
16 #include <sys/types.h> /* OpenBSD needs this */
17 #include <sys/socket.h>
18 #include <net/if.h>
19 #include <ifaddrs.h>
20 #include <errno.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 static const char *iface = NULL;
25 struct addr localip4, localip6;
26 static struct addr last_localip4, last_localip6;
27
28 void
29 localip_init(const char *interface)
30 {
31    iface = interface;
32    localip_update();
33 }
34
35 void
36 localip_update(void)
37 {
38    struct ifaddrs *ifas, *ifa;
39    int got_v4 = 0, got_v6 = 0;
40
41    localip4.family = IPv4;
42    localip6.family = IPv6;
43
44    if (iface == NULL) {
45       /* reading from capfile */
46       localip4.ip.v4 = 0;
47       memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6));
48       return;
49    }
50
51    if (getifaddrs(&ifas) < 0)
52       err(1, "can't get own IP address on interface \"%s\"", iface);
53
54    for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
55       if (got_v4 && got_v6)
56          break;   /* Task is already complete. */
57
58       if (strncmp(ifa->ifa_name, iface, IFNAMSIZ))
59          continue;   /* Wrong interface. */
60
61       /* The first IPv4 name is always functional. */
62       if ((ifa->ifa_addr->sa_family == AF_INET) && !got_v4)
63       {
64          /* Good IPv4 address. */
65          localip4.ip.v4 =
66             ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
67          got_v4 = 1;
68          continue;
69       }
70
71       /* IPv6 needs some obvious exceptions. */
72       if ( ifa->ifa_addr->sa_family == AF_INET6 ) {
73          struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
74
75          if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))
76               || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) )
77             continue;
78
79          /* Only standard IPv6 can reach this point. */
80          memcpy(&(localip6.ip.v6), &sa6->sin6_addr, sizeof(localip6.ip.v6));
81          got_v6 = 1;
82       }
83    }
84
85    freeifaddrs(ifas);
86
87    /* Report an error if IPv4 address could not be found. */
88    if (!got_v4)
89        err(1, "can't get own IPv4 address on interface \"%s\"", iface);
90
91    if (!addr_equal(&last_localip4, &localip4)) {
92       verbosef("localip4 update(%s) = %s", iface, addr_to_str(&localip4));
93       last_localip4 = localip4;
94    }
95    if (!addr_equal(&last_localip6, &localip6)) {
96       verbosef("localip6 update(%s) = %s", iface, addr_to_str(&localip6));
97       last_localip6 = localip6;
98    }
99 }
100
101 /* vim:set ts=3 sw=3 tw=78 expandtab: */