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