Update portability notes.
[darkstat] / localip.c
1 /* darkstat 3
2 * copyright (c) 2001-2012 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 void localip_init(struct local_ips *ips) {
37 ips->is_valid = 0;
38 ips->last_update_mono = 0;
39 ips->num_addrs = 0;
40 ips->addrs = NULL;
41 }
42
43 void localip_free(struct local_ips *ips) {
44 if (ips->addrs != NULL)
45 free(ips->addrs);
46 }
47
48 static void add_ip(const char *iface,
49 struct local_ips *ips,
50 int *idx,
51 struct addr *a) {
52 if (ips->num_addrs <= *idx) {
53 /* Grow. */
54 ips->addrs = xrealloc(ips->addrs, sizeof(*(ips->addrs)) * (*idx + 1));
55 ips->num_addrs++;
56 assert(ips->num_addrs > *idx);
57 verbosef("interface '%s' gained new address %s", iface, addr_to_str(a));
58 } else {
59 /* Warn about changed address. */
60 if (!addr_equal(ips->addrs + *idx, a)) {
61 static char before[INET6_ADDRSTRLEN];
62 strncpy(before, addr_to_str(ips->addrs + *idx), INET6_ADDRSTRLEN);
63 verbosef("interface '%s' address %d/%d changed from %s to %s",
64 iface, *idx+1, ips->num_addrs, before, addr_to_str(a));
65 }
66 }
67 ips->addrs[*idx] = *a;
68 (*idx)++;
69 }
70
71 /* Returns 0 on failure. */
72 void localip_update(const char *iface, struct local_ips *ips) {
73 struct addr a;
74 int new_addrs = 0;
75
76 if (iface == NULL) {
77 /* reading from capfile */
78 ips->is_valid = 0;
79 return;
80 }
81
82 if (ips->last_update_mono == now_mono()) {
83 /* Too soon, bail out. */
84 return;
85 }
86 ips->last_update_mono = now_mono();
87
88 #ifdef HAVE_IFADDRS_H
89 {
90 struct ifaddrs *ifas, *ifa;
91
92 if (getifaddrs(&ifas) < 0)
93 err(1, "getifaddrs() failed");
94
95 for (ifa=ifas; ifa; ifa=ifa->ifa_next) {
96 if (strncmp(ifa->ifa_name, iface, IFNAMSIZ))
97 continue; /* Wrong interface. */
98
99 if (!ifa->ifa_addr)
100 continue; /* This can be NULL, e.g. for ppp0. */
101
102 if (ifa->ifa_addr->sa_family == AF_INET) {
103 a.family = IPv4;
104 a.ip.v4 = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
105 add_ip(iface, ips, &new_addrs, &a);
106 }
107 if (ifa->ifa_addr->sa_family == AF_INET6) {
108 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ifa->ifa_addr;
109 # if 0
110 if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))
111 || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) )
112 continue;
113 # endif
114 a.family = IPv6;
115 memcpy(&(a.ip.v6), &sa6->sin6_addr, sizeof(a.ip.v6));
116 add_ip(iface, ips, &new_addrs, &a);
117 }
118 }
119 freeifaddrs(ifas);
120 }
121 #else /* don't HAVE_IFADDRS_H */
122 {
123 int tmp = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
124 struct ifreq ifr;
125 struct sockaddr sa;
126
127 strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
128 ifr.ifr_addr.sa_family = AF_INET;
129 if (ioctl(tmp, SIOCGIFADDR, &ifr) != -1) {
130 sa = ifr.ifr_addr;
131 a.family = IPv4;
132 a.ip.v4 = ((struct sockaddr_in*)(&ifr.ifr_addr))->sin_addr.s_addr;
133 add_ip(iface, ips, &new_addrs, &a);
134 }
135 close(tmp);
136 }
137 #endif
138 if (new_addrs == 0) {
139 if (ips->is_valid)
140 verbosef("interface '%s' no longer has any addresses", iface);
141 ips->is_valid = 0;
142 } else {
143 if (!ips->is_valid)
144 verbosef("interface '%s' now has addresses", iface);
145 ips->is_valid = 1;
146 if (ips->num_addrs != new_addrs)
147 verbosef("interface '%s' number of addresses decreased from %d to %d",
148 iface, ips->num_addrs, new_addrs);
149 ips->num_addrs = new_addrs;
150 }
151 }
152
153 int is_localip(const struct addr * const a,
154 const struct local_ips * const ips) {
155 int i;
156
157 for (i=0; i<ips->num_addrs; i++) {
158 if (addr_equal(a, ips->addrs+i))
159 return 1;
160 }
161 return 0;
162 }
163
164 /* vim:set ts=3 sw=3 tw=80 et: */