Fine tuning for OpenBSD and GNU/kfreebsd.
[darkstat] / localip.c
1 /* darkstat 3
2 * copyright (c) 2001-2008 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 "darkstat.h"
11 #include "conv.h" /* for strlcpy */
12 #include "decode.h" /* for ip_to_str, ip6_to_str */
13 #include "err.h"
14 #include "localip.h"
15
16 #include <sys/socket.h>
17 #include <net/if.h>
18 #include <ifaddrs.h>
19 #include <errno.h>
20 #include <string.h>
21 #include <unistd.h>
22
23 static const char *iface = NULL;
24
25 in_addr_t localip = 0;
26 static in_addr_t last_localip = 0;
27
28 struct in6_addr localip6;
29 static struct in6_addr last_localip6;
30
31 void
32 localip_init(const char *interface)
33 {
34 iface = interface;
35 localip_update();
36 }
37
38 void
39 localip_update(void)
40 {
41 struct ifaddrs *ifas, *ifa;
42 int flags = 0;
43
44 #define HAS_IPV4 0x01
45 #define HAS_IPV6 0x02
46
47 if (iface == NULL) {
48 /* reading from capfile */
49 localip = 0;
50 memset(&localip6, '\0', sizeof(localip6));
51 return;
52 }
53
54 if (getifaddrs(&ifas) < 0)
55 err(1, "can't get own IP address on interface \"%s\"", iface);
56
57 for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
58 if (flags == (HAS_IPV4 | HAS_IPV6))
59 break; /* Task is already complete. */
60
61 if (strncmp(ifa->ifa_name, iface, IFNAMSIZ))
62 continue; /* Wrong interface. */
63
64 /* The first IPv4 name is always functional. */
65 if ( (ifa->ifa_addr->sa_family == AF_INET)
66 && ! (flags & HAS_IPV4) ) {
67 /* Good IPv4 address. */
68 localip = ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr.s_addr;
69 flags |= HAS_IPV4;
70 continue;
71 }
72
73 /* IPv6 needs some obvious exceptions. */
74 if ( ifa->ifa_addr->sa_family == AF_INET6 ) {
75 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
76
77 if ( IN6_IS_ADDR_LINKLOCAL(&(sa6->sin6_addr))
78 || IN6_IS_ADDR_SITELOCAL(&(sa6->sin6_addr)) )
79 continue;
80 else
81 /* Only standard IPv6 can reach this point. */
82 memcpy(&localip6, &sa6->sin6_addr, sizeof(localip6));
83 flags |= HAS_IPV6;
84 }
85 }
86
87 freeifaddrs(ifas);
88
89 /* Repport an error if IPv4 address could not be found. */
90 if ( !(flags & HAS_IPV4) )
91 err(1, "can't get own IPv4 address on interface \"%s\"", iface);
92
93 /* struct sockaddr {
94 * sa_family_t sa_family; * address family, AF_xxx
95 * char sa_data[14]; * 14 bytes of protocol address
96 *
97 * struct sockaddr_in {
98 * sa_family_t sin_family; * Address family
99 * unsigned short int sin_port; * Port number
100 * struct in_addr sin_addr; * Internet address
101 *
102 * struct in_addr {
103 * __u32 s_addr;
104 */
105
106 if (last_localip != localip) {
107 verbosef("local_ip update(%s) = %s", iface, ip_to_str(localip));
108 last_localip = localip;
109 }
110 if (memcmp(&last_localip6, &localip6, sizeof(localip6))) {
111 verbosef("local_ip6 update(%s) = %s", iface, ip6_to_str(&localip6));
112 memcpy(&last_localip6, &localip6, sizeof(localip6));
113 }
114 }
115
116 /* vim:set ts=3 sw=3 tw=78 expandtab: */