]> git.meshlink.io Git - catta/blob - iface.c
6ea7054e38439ad4b2270e8f7b5a56635ec91983
[catta] / iface.c
1 #include <string.h>
2 #include <sys/socket.h>
3 #include <asm/types.h>
4 #include <linux/netlink.h>
5 #include <linux/rtnetlink.h>
6 #include <errno.h>
7 #include <net/if.h>
8
9 #include "iface.h"
10 #include "netlink.h"
11
12 static void update_address_rr(flxInterfaceMonitor *m, flxInterfaceAddress *a, int remove) {
13     g_assert(m);
14     g_assert(a);
15
16     if (!flx_address_is_relevant(a) || remove) {
17         if (a->rr_id >= 0) {
18             flx_server_remove(m->server, a->rr_id);
19             a->rr_id = -1;
20         }
21     } else {
22         if (a->rr_id < 0) {
23             a->rr_id = flx_server_get_next_id(m->server);
24             flx_server_add_address(m->server, a->rr_id, a->interface->index, AF_UNSPEC, FALSE, m->server->hostname, &a->address);
25         }
26     }
27 }
28
29 static void update_interface_rr(flxInterfaceMonitor *m, flxInterface *i, int remove) {
30     flxInterfaceAddress *a;
31     g_assert(m);
32     g_assert(i);
33
34     for (a = i->addresses; a; a = a->address_next)
35         update_address_rr(m, a, remove);
36 }
37
38 static void free_address(flxInterfaceMonitor *m, flxInterfaceAddress *a) {
39     g_assert(m);
40     g_assert(a);
41     g_assert(a->interface);
42
43     if (a->address.family == AF_INET)
44         a->interface->n_ipv4_addrs --;
45     else if (a->address.family == AF_INET6)
46         a->interface->n_ipv6_addrs --;
47
48     FLX_LLIST_REMOVE(flxInterfaceAddress, address, a->interface->addresses, a);
49
50     flx_server_remove(m->server, a->rr_id);
51     
52     g_free(a);
53 }
54
55 static void free_interface(flxInterfaceMonitor *m, flxInterface *i) {
56     g_assert(m);
57     g_assert(i);
58
59     while (i->addresses)
60         free_address(m, i->addresses);
61
62     if (i->ipv4_cache)
63         flx_cache_free(i->ipv4_cache);
64     if (i->ipv6_cache)
65         flx_cache_free(i->ipv6_cache);
66     
67     g_assert(i->n_ipv6_addrs == 0);
68     g_assert(i->n_ipv4_addrs == 0);
69
70     FLX_LLIST_REMOVE(flxInterface, interface, m->interfaces, i);
71     g_hash_table_remove(m->hash_table, &i->index);
72
73     flx_cache_free(i->ipv4_cache);
74     flx_cache_free(i->ipv6_cache);
75     
76     g_free(i->name);
77     g_free(i);
78 }
79
80 static flxInterfaceAddress* get_address(flxInterfaceMonitor *m, flxInterface *i, const flxAddress *raddr) {
81     flxInterfaceAddress *ia;
82     
83     g_assert(m);
84     g_assert(i);
85     g_assert(raddr);
86
87     for (ia = i->addresses; ia; ia = ia->address_next)
88         if (flx_address_cmp(&ia->address, raddr) == 0)
89             return ia;
90
91     return NULL;
92 }
93
94 static int netlink_list_items(flxNetlink *nl, guint16 type, guint *ret_seq) {
95     struct nlmsghdr *n;
96     struct rtgenmsg *gen;
97     guint8 req[1024];
98     
99     memset(&req, 0, sizeof(req));
100     n = (struct nlmsghdr*) req;
101     n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
102     n->nlmsg_type = type;
103     n->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
104     n->nlmsg_pid = 0;
105
106     gen = NLMSG_DATA(n);
107     memset(gen, 0, sizeof(struct rtgenmsg));
108     gen->rtgen_family = AF_UNSPEC;
109
110     return flx_netlink_send(nl, n, ret_seq);
111 }
112
113 static void callback(flxNetlink *nl, struct nlmsghdr *n, gpointer userdata) {
114     flxInterfaceMonitor *m = userdata;
115     
116     g_assert(m);
117     g_assert(n);
118     g_assert(m->netlink == nl);
119
120     if (n->nlmsg_type == RTM_NEWLINK) {
121         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
122         flxInterface *i;
123         struct rtattr *a = NULL;
124         size_t l;
125         int changed;
126
127         if (ifinfomsg->ifi_family != AF_UNSPEC)
128             return;
129
130         if ((i = (flxInterface*) flx_interface_monitor_get_interface(m, ifinfomsg->ifi_index)))
131             changed = 1;
132         else {
133             i = g_new(flxInterface, 1);
134             i->monitor = m;
135             i->name = NULL;
136             i->index = ifinfomsg->ifi_index;
137             i->addresses = NULL;
138             i->n_ipv4_addrs = i->n_ipv6_addrs = 0;
139             FLX_LLIST_PREPEND(flxInterface, interface, m->interfaces, i);
140             g_hash_table_insert(m->hash_table, &i->index, i);
141             i->ipv4_cache = flx_cache_new(m->server, i);
142             i->ipv6_cache = flx_cache_new(m->server, i);
143             
144             changed = 0;
145         }
146         
147         i->flags = ifinfomsg->ifi_flags;
148
149         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
150         a = IFLA_RTA(ifinfomsg);
151
152         while (RTA_OK(a, l)) {
153             switch(a->rta_type) {
154                 case IFLA_IFNAME:
155                     g_free(i->name);
156                     i->name = g_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
157                     break;
158                     
159                 default:
160                     ;
161             }
162
163             a = RTA_NEXT(a, l);
164         }
165
166         update_interface_rr(m, i, 0);
167     } else if (n->nlmsg_type == RTM_DELLINK) {
168         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
169         flxInterface *i;
170
171         if (ifinfomsg->ifi_family != AF_UNSPEC)
172             return;
173         
174         if (!(i = (flxInterface*) flx_interface_monitor_get_interface(m, ifinfomsg->ifi_index)))
175             return;
176
177         update_interface_rr(m, i, 1);
178         free_interface(m, i);
179         
180     } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
181
182         struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
183         flxInterface *i;
184         struct rtattr *a = NULL;
185         size_t l;
186         int changed;
187         flxAddress raddr;
188         int raddr_valid = 0;
189
190         if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
191             return;
192
193         if (!(i = (flxInterface*) flx_interface_monitor_get_interface(m, ifaddrmsg->ifa_index)))
194             return;
195
196         raddr.family = ifaddrmsg->ifa_family;
197
198         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
199         a = IFA_RTA(ifaddrmsg);
200
201         while (RTA_OK(a, l)) {
202             switch(a->rta_type) {
203                 case IFA_ADDRESS:
204                     if ((raddr.family == AF_INET6 && RTA_PAYLOAD(a) != 16) ||
205                         (raddr.family == AF_INET && RTA_PAYLOAD(a) != 4))
206                         return;
207
208                     memcpy(raddr.data, RTA_DATA(a), RTA_PAYLOAD(a));
209                     raddr_valid = 1;
210
211                     break;
212                     
213                 default:
214                     ;
215             }
216
217             a = RTA_NEXT(a, l);
218         }
219
220
221         if (!raddr_valid)
222             return;
223
224         if (n->nlmsg_type == RTM_NEWADDR) {
225             flxInterfaceAddress *addr;
226             
227             if ((addr = get_address(m, i, &raddr)))
228                 changed = 1;
229             else {
230                 addr = g_new(flxInterfaceAddress, 1);
231                 addr->address = raddr;
232                 addr->interface = i;
233
234                 if (raddr.family == AF_INET)
235                     i->n_ipv4_addrs++;
236                 else if (raddr.family == AF_INET6)
237                     i->n_ipv6_addrs++;
238
239                 addr->rr_id = -1;
240
241                 FLX_LLIST_PREPEND(flxInterfaceAddress, address, i->addresses, addr);
242                 
243                 changed = 0;
244             }
245             
246             addr->flags = ifaddrmsg->ifa_flags;
247             addr->scope = ifaddrmsg->ifa_scope;
248
249             update_address_rr(m, addr, 0);
250         } else {
251             flxInterfaceAddress *addr;
252             
253             if (!(addr = get_address(m, i, &raddr)))
254                 return;
255
256             update_address_rr(m, addr, 1);
257             free_address(m, addr);
258         }
259                 
260     } else if (n->nlmsg_type == NLMSG_DONE) {
261
262         if (m->list == LIST_IFACE) {
263             m->list = LIST_DONE;
264             
265             if (netlink_list_items(m->netlink, RTM_GETADDR, &m->query_addr_seq) < 0) {
266                 g_warning("NETLINK: Failed to list addrs: %s", strerror(errno));
267             } else
268                 m->list = LIST_ADDR;
269         } else
270             m->list = LIST_DONE;
271         
272     } else if (n->nlmsg_type == NLMSG_ERROR && (n->nlmsg_seq == m->query_link_seq || n->nlmsg_seq == m->query_addr_seq)) {
273         struct nlmsgerr *e = NLMSG_DATA (n);
274                     
275         if (e->error)
276             g_warning("NETLINK: Failed to browse: %s", strerror(-e->error));
277     }
278 }
279
280 flxInterfaceMonitor *flx_interface_monitor_new(flxServer *s) {
281     flxInterfaceMonitor *m = NULL;
282
283     m = g_new0(flxInterfaceMonitor, 1);
284     m->server = s;
285     if (!(m->netlink = flx_netlink_new(s->context, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, callback, m)))
286         goto fail;
287
288     m->hash_table = g_hash_table_new(g_int_hash, g_int_equal);
289     m->interfaces = NULL;
290
291     if (netlink_list_items(m->netlink, RTM_GETLINK, &m->query_link_seq) < 0)
292         goto fail;
293
294     m->list = LIST_IFACE;
295     
296     return m;
297
298 fail:
299     flx_interface_monitor_free(m);
300     return NULL;
301 }
302
303 void flx_interface_monitor_free(flxInterfaceMonitor *m) {
304     g_assert(m);
305
306     if (m->netlink)
307         flx_netlink_free(m->netlink);
308
309     if (m->hash_table)
310         g_hash_table_destroy(m->hash_table);
311
312     g_free(m);
313 }
314
315
316 flxInterface* flx_interface_monitor_get_interface(flxInterfaceMonitor *m, gint index) {
317     g_assert(m);
318     g_assert(index > 0);
319
320     return g_hash_table_lookup(m->hash_table, &index);
321 }
322
323 flxInterface* flx_interface_monitor_get_first(flxInterfaceMonitor *m) {
324     g_assert(m);
325     return m->interfaces;
326 }
327
328 int flx_interface_is_relevant(flxInterface *i) {
329     g_assert(i);
330
331     return
332         (i->flags & IFF_UP) &&
333         (i->flags & IFF_RUNNING) &&
334         !(i->flags & IFF_LOOPBACK);
335 }
336
337 int flx_address_is_relevant(flxInterfaceAddress *a) {
338     g_assert(a);
339
340     return
341         a->scope == RT_SCOPE_UNIVERSE &&
342         flx_interface_is_relevant(a->interface);
343 }
344
345 void flx_interface_send_query(flxInterface *i, guchar protocol, flxKey *k) {
346     g_assert(i);
347     g_assert(k);
348 }