Use a monotonic clock where appropriate.
[darkstat] / localip.c
1 /* darkstat 3
2 * copyright (c) 2001-2011 Emil Mikulic.
3 *
4 * localip.c: determine local IPs of an 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 "addr.h"
11 #include "bsd.h" /* for strlcpy */
12 #include "config.h" /* for HAVE_IFADDRS_H */
13 #include "conv.h"
14 #include "err.h"
15 #include "localip.h"
16 #include "now.h"
17
18 #include <sys/socket.h>
19 #include <net/if.h>
20 #include <assert.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25 #include <unistd.h>
26
27 #ifdef HAVE_IFADDRS_H
28 # include <ifaddrs.h>
29 #else
30 # ifdef HAVE_SYS_SOCKIO_H
31 # include <sys/sockio.h> /* for SIOCGIFADDR, especially on Solaris */
32 # endif
33 # include <sys/ioctl.h>
34 #endif
35
36 struct local_ips *local_ips;
37
38 struct local_ips *localip_make(void) {
39 struct local_ips *ips = xmalloc(sizeof(*ips));
40
41 ips->is_valid = 0;
42 ips->last_update_mono = 0;
43 ips->num_addrs = 0;
44 ips->addrs = NULL;
45 return ips;
46 }
47
48 void localip_free(struct local_ips *ips) {
49 if (ips->addrs != NULL)
50 free(ips->addrs);
51 free(ips);
52 }
53
54 static void add_ip(const char *iface, struct local_ips *ips,
55 int *index, struct addr *a) {
56 if (ips->num_addrs <= *index) {
57 /* Grow. */
58 ips->addrs = xrealloc(ips->addrs, sizeof(*(ips->addrs)) * (*index + 1));
59 ips->num_addrs++;
60 assert(ips->num_addrs > *index);
61 verbosef("interface '%s' gained new address %s", iface, addr_to_str(a));
62 } else {
63 /* Warn about changed address. */
64 if (!addr_equal(ips->addrs + *index, a)) {
65 static char before[INET6_ADDRSTRLEN];
66 strncpy(before, addr_to_str(ips->addrs + *index), INET6_ADDRSTRLEN);
67 verbosef("interface '%s' address %d/%d changed from %s to %s",
68 iface, *index+1, ips->num_addrs, before, addr_to_str(a));
69 }
70 }
71 ips->addrs[*index] = *a;
72 (*index)++;
73 }
74
75 /* Returns 0 on failure. */
76 void localip_update(const char *iface, struct local_ips *ips) {
77 struct addr a;
78 int new_addrs = 0;
79
80 if (iface == NULL) {
81 /* reading from capfile */
82 ips->is_valid = 0;
83 return;
84 }
85
86 if (ips->last_update_mono == now_mono()) {
87 /* Too soon, bail out. */
88 return;
89 }
90 ips->last_update_mono = now_mono();
91
92 #ifdef HAVE_IFADDRS_H
93 {
94 struct ifaddrs *ifas, *ifa;
95
96 if (getifaddrs(&ifas) < 0)
97 err(1, "getifaddrs() failed");
98
99 for (ifa=ifas; ifa; ifa=ifa->ifa_next) {
100 if (strncmp(ifa->ifa_name, iface, IFNAMSIZ))
101 continue; /* Wrong interface. */
102
103 if (!ifa->ifa_addr)
104 continue; /* This can be NULL, e.g. for ppp0. */
105
106 if (ifa->ifa_addr->sa_family == AF_INET) {
107 a.family = IPv4;
108 a.ip.v4 = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
109 add_ip(iface, ips, &new_addrs, &a);
110 } else if (ifa->ifa_addr->sa_family == AF_INET6) {
111 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ifa->ifa_addr;
112 # if 0
113 if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))
114 || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) )
115 continue;
116 # endif
117 a.family = IPv6;
118 memcpy(&(a.ip.v6), &sa6->sin6_addr, sizeof(a.ip.v6));
119 add_ip(iface, ips, &new_addrs, &a);
120 # ifdef AF_PACKET
121 } else if (ifa->ifa_addr->sa_family == AF_PACKET) {
122 /* ignore */
123 # endif
124 } else
125 verbosef("unknown sa_family=%d on interface '%s'",
126 (int)ifa->ifa_addr->sa_family, iface);
127 }
128 freeifaddrs(ifas);
129 }
130 #else /* don't HAVE_IFADDRS_H */
131 {
132 int tmp = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
133 struct ifreq ifr;
134 struct sockaddr sa;
135
136 strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
137 ifr.ifr_addr.sa_family = AF_INET;
138 if (ioctl(tmp, SIOCGIFADDR, &ifr) != -1) {
139 sa = ifr.ifr_addr;
140 a.family = IPv4;
141 a.ip.v4 = ((struct sockaddr_in*)(&ifr.ifr_addr))->sin_addr.s_addr;
142 add_ip(iface, ips, &new_addrs, &a);
143 }
144 close(tmp);
145 }
146 #endif
147 if (new_addrs == 0) {
148 if (ips->is_valid)
149 verbosef("interface '%s' no longer has any addresses", iface);
150 ips->is_valid = 0;
151 } else {
152 if (!ips->is_valid)
153 verbosef("interface '%s' now has addresses", iface);
154 ips->is_valid = 1;
155 if (ips->num_addrs != new_addrs)
156 verbosef("interface '%s' number of addresses decreased from %d to %d",
157 iface, ips->num_addrs, new_addrs);
158 ips->num_addrs = new_addrs;
159 }
160 }
161
162 int is_localip(const struct addr * const a,
163 const struct local_ips * const ips) {
164 int i;
165
166 for (i=0; i<ips->num_addrs; i++) {
167 if (addr_equal(a, ips->addrs+i))
168 return 1;
169 }
170 return 0;
171 }
172
173 /* vim:set ts=3 sw=3 tw=80 et: */