0c15409906ab3dd8899be4122af33664b3d6ff1b
[darkstat-debian] / localip.c
1 /* darkstat 3
2 * copyright (c) 2001-2011 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 "addr.h"
11 #include "config.h" /* for HAVE_IFADDRS_H */
12 #include "err.h"
13 #include "localip.h"
14 #include "bsd.h" /* for strlcpy */
15
16 #include <sys/socket.h>
17 #include <net/if.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <unistd.h>
21
22 #ifdef HAVE_IFADDRS_H
23 # include <ifaddrs.h>
24 #else
25 # ifdef HAVE_SYS_SOCKIO_H
26 # include <sys/sockio.h> /* for SIOCGIFADDR, especially on Solaris */
27 # endif
28 # include <sys/ioctl.h>
29 #endif
30
31 static const char *iface = NULL;
32 struct addr localip4, localip6;
33 static struct addr last_localip4, last_localip6;
34
35 void
36 localip_init(const char *interface)
37 {
38 iface = interface;
39
40 /* defaults */
41 localip4.family = IPv4;
42 localip4.ip.v4 = 0;
43
44 localip6.family = IPv6;
45 memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6));
46
47 last_localip4 = localip4;
48 last_localip6 = localip6;
49
50 /* initial update */
51 localip_update();
52 }
53
54 static void
55 localip_update_helper(void)
56 {
57 /* defaults */
58 localip4.family = IPv4;
59 localip4.ip.v4 = 0;
60
61 localip6.family = IPv6;
62 memset(&(localip6.ip.v6), 0, sizeof(localip6.ip.v6));
63
64 if (iface == NULL)
65 return; /* reading from capfile */
66
67 #ifdef HAVE_IFADDRS_H
68 {
69 int got_v4 = 0, got_v6 = 0;
70 struct ifaddrs *ifas, *ifa;
71
72 if (getifaddrs(&ifas) < 0) {
73 warn("can't getifaddrs() on interface \"%s\"", iface);
74 return;
75 }
76
77 for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
78 if (got_v4 && got_v6)
79 break; /* Task is already complete. */
80
81 if (strncmp(ifa->ifa_name, iface, IFNAMSIZ))
82 continue; /* Wrong interface. */
83
84 if (!ifa->ifa_addr)
85 continue; /* This can be NULL, e.g. for ppp0. */
86
87 /* The first IPv4 name is always functional. */
88 if ((ifa->ifa_addr->sa_family == AF_INET) && !got_v4)
89 {
90 /* Good IPv4 address. */
91 localip4.ip.v4 =
92 ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
93 got_v4 = 1;
94 continue;
95 }
96
97 /* IPv6 needs some obvious exceptions. */
98 if ( ifa->ifa_addr->sa_family == AF_INET6 ) {
99 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
100
101 if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))
102 || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) )
103 continue;
104
105 /* Only standard IPv6 can reach this point. */
106 memcpy(&(localip6.ip.v6), &sa6->sin6_addr, sizeof(localip6.ip.v6));
107 got_v6 = 1;
108 }
109 }
110
111 freeifaddrs(ifas);
112
113 if (!got_v4)
114 warnx("can't get own IPv4 address on interface \"%s\"", iface);
115 }
116 #else /* don't HAVE_IFADDRS_H */
117 {
118 int tmp = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
119 struct ifreq ifr;
120 struct sockaddr sa;
121
122 strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
123 ifr.ifr_addr.sa_family = AF_INET;
124 if (ioctl(tmp, SIOCGIFADDR, &ifr) == -1) {
125 if (errno == EADDRNOTAVAIL) {
126 verbosef("lost local IP");
127 } else
128 warn("can't get own IP address on interface \"%s\"", iface);
129 } else {
130 /* success! */
131 sa = ifr.ifr_addr;
132 localip4.ip.v4 = ((struct sockaddr_in*)&sa)->sin_addr.s_addr;
133 }
134 close(tmp);
135 }
136 #endif
137 }
138
139 void
140 localip_update(void)
141 {
142 localip_update_helper();
143
144 if (!addr_equal(&last_localip4, &localip4)) {
145 verbosef("%s ip4 update: %s", iface, addr_to_str(&localip4));
146 last_localip4 = localip4;
147 }
148 if (!addr_equal(&last_localip6, &localip6)) {
149 verbosef("%s ip6 update: %s", iface, addr_to_str(&localip6));
150 last_localip6 = localip6;
151 }
152 }
153
154 /* vim:set ts=3 sw=3 tw=78 expandtab: */