FreeBSD build warning fixes.
[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 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 } else if (ifa->ifa_addr->sa_family == AF_INET6) {
107 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)ifa->ifa_addr;
108 # if 0
109 if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))
110 || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) )
111 continue;
112 # endif
113 a.family = IPv6;
114 memcpy(&(a.ip.v6), &sa6->sin6_addr, sizeof(a.ip.v6));
115 add_ip(iface, ips, &new_addrs, &a);
116 # ifdef AF_PACKET
117 } else if (ifa->ifa_addr->sa_family == AF_PACKET) {
118 /* ignore */
119 # endif
120 } else
121 verbosef("unknown sa_family=%d on interface '%s'",
122 (int)ifa->ifa_addr->sa_family, iface);
123 }
124 freeifaddrs(ifas);
125 }
126 #else /* don't HAVE_IFADDRS_H */
127 {
128 int tmp = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
129 struct ifreq ifr;
130 struct sockaddr sa;
131
132 strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
133 ifr.ifr_addr.sa_family = AF_INET;
134 if (ioctl(tmp, SIOCGIFADDR, &ifr) != -1) {
135 sa = ifr.ifr_addr;
136 a.family = IPv4;
137 a.ip.v4 = ((struct sockaddr_in*)(&ifr.ifr_addr))->sin_addr.s_addr;
138 add_ip(iface, ips, &new_addrs, &a);
139 }
140 close(tmp);
141 }
142 #endif
143 if (new_addrs == 0) {
144 if (ips->is_valid)
145 verbosef("interface '%s' no longer has any addresses", iface);
146 ips->is_valid = 0;
147 } else {
148 if (!ips->is_valid)
149 verbosef("interface '%s' now has addresses", iface);
150 ips->is_valid = 1;
151 if (ips->num_addrs != new_addrs)
152 verbosef("interface '%s' number of addresses decreased from %d to %d",
153 iface, ips->num_addrs, new_addrs);
154 ips->num_addrs = new_addrs;
155 }
156 }
157
158 int is_localip(const struct addr * const a,
159 const struct local_ips * const ips) {
160 int i;
161
162 for (i=0; i<ips->num_addrs; i++) {
163 if (addr_equal(a, ips->addrs+i))
164 return 1;
165 }
166 return 0;
167 }
168
169 /* vim:set ts=3 sw=3 tw=80 et: */