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