]> git.meshlink.io Git - catta/blob - iface.c
c1dec19acc3a75c9c596169fee6f786f59eb8aad
[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 #include "dns.h"
12 #include "socket.h"
13 #include "announce.h"
14
15 static void update_address_rr(flxInterfaceMonitor *m, flxInterfaceAddress *a, int remove) {
16     g_assert(m);
17     g_assert(a);
18
19     if (!flx_interface_address_relevant(a) || remove) {
20         if (a->rr_id >= 0) {
21             flx_server_remove(m->server, a->rr_id);
22             a->rr_id = -1;
23         }
24     } else {
25         if (a->rr_id < 0) {
26             a->rr_id = flx_server_get_next_id(m->server);
27             flx_server_add_address(m->server, a->rr_id, a->interface->hardware->index, AF_UNSPEC, 0, NULL, &a->address);
28         }
29     }
30 }
31
32 static void update_interface_rr(flxInterfaceMonitor *m, flxInterface *i, int remove) {
33     flxInterfaceAddress *a;
34     g_assert(m);
35     g_assert(i);
36
37     for (a = i->addresses; a; a = a->address_next)
38         update_address_rr(m, a, remove);
39 }
40
41 static void update_hw_interface_rr(flxInterfaceMonitor *m, flxHwInterface *hw, int remove) {
42     flxInterface *i;
43
44     g_assert(m);
45     g_assert(hw);
46
47     for (i = hw->interfaces; i; i = i->by_hardware_next)
48         update_interface_rr(m, i, remove);
49 }
50
51 static void free_address(flxInterfaceMonitor *m, flxInterfaceAddress *a) {
52     g_assert(m);
53     g_assert(a);
54     g_assert(a->interface);
55
56     FLX_LLIST_REMOVE(flxInterfaceAddress, address, a->interface->addresses, a);
57     flx_server_remove(m->server, a->rr_id);
58     
59     g_free(a);
60 }
61
62 static void free_interface(flxInterfaceMonitor *m, flxInterface *i, gboolean send_goodbye) {
63     g_assert(m);
64     g_assert(i);
65
66     g_message("removing interface %s.%i", i->hardware->name, i->protocol);
67     flx_goodbye_interface(m->server, i, send_goodbye);
68     g_message("flushing...");
69     flx_packet_scheduler_flush_responses(i->scheduler);
70     g_message("done");
71     
72     g_assert(!i->announcements);
73
74     while (i->addresses)
75         free_address(m, i->addresses);
76
77     flx_packet_scheduler_free(i->scheduler);
78     flx_cache_free(i->cache);
79     
80     FLX_LLIST_REMOVE(flxInterface, interface, m->interfaces, i);
81     FLX_LLIST_REMOVE(flxInterface, by_hardware, i->hardware->interfaces, i);
82     
83     g_free(i);
84 }
85
86 static void free_hw_interface(flxInterfaceMonitor *m, flxHwInterface *hw, gboolean send_goodbye) {
87     g_assert(m);
88     g_assert(hw);
89
90     while (hw->interfaces)
91         free_interface(m, hw->interfaces, send_goodbye);
92
93     FLX_LLIST_REMOVE(flxHwInterface, hardware, m->hw_interfaces, hw);
94     g_hash_table_remove(m->hash_table, &hw->index);
95
96     g_free(hw->name);
97     g_free(hw);
98 }
99
100 static flxInterfaceAddress* get_address(flxInterfaceMonitor *m, flxInterface *i, const flxAddress *raddr) {
101     flxInterfaceAddress *ia;
102     
103     g_assert(m);
104     g_assert(i);
105     g_assert(raddr);
106
107     for (ia = i->addresses; ia; ia = ia->address_next)
108         if (flx_address_cmp(&ia->address, raddr) == 0)
109             return ia;
110
111     return NULL;
112 }
113
114 static int netlink_list_items(flxNetlink *nl, guint16 type, guint *ret_seq) {
115     struct nlmsghdr *n;
116     struct rtgenmsg *gen;
117     guint8 req[1024];
118     
119     memset(&req, 0, sizeof(req));
120     n = (struct nlmsghdr*) req;
121     n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
122     n->nlmsg_type = type;
123     n->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
124     n->nlmsg_pid = 0;
125
126     gen = NLMSG_DATA(n);
127     memset(gen, 0, sizeof(struct rtgenmsg));
128     gen->rtgen_family = AF_UNSPEC;
129
130     return flx_netlink_send(nl, n, ret_seq);
131 }
132
133 static void new_interface(flxInterfaceMonitor *m, flxHwInterface *hw, guchar protocol) {
134     flxInterface *i;
135     
136     g_assert(m);
137     g_assert(hw);
138     g_assert(protocol != AF_UNSPEC);
139
140     i = g_new(flxInterface, 1);
141     i->monitor = m;
142     i->hardware = hw;
143     i->protocol = protocol;
144     i->announcing = FALSE;
145
146     FLX_LLIST_HEAD_INIT(flxInterfaceAddress, i->addresses);
147     FLX_LLIST_HEAD_INIT(flxAnnouncement, i->announcements);
148
149     i->cache = flx_cache_new(m->server, i);
150     i->scheduler = flx_packet_scheduler_new(m->server, i);
151
152     FLX_LLIST_PREPEND(flxInterface, by_hardware, hw->interfaces, i);
153     FLX_LLIST_PREPEND(flxInterface, interface, m->interfaces, i);
154 }
155
156 static void check_interface_relevant(flxInterfaceMonitor *m, flxInterface *i) {
157     gboolean b;
158
159     g_assert(m);
160     g_assert(i);
161
162     b = flx_interface_relevant(i);
163
164     if (b && !i->announcing) {
165         g_message("New relevant interface %s.%i", i->hardware->name, i->protocol);
166
167         if (i->protocol == AF_INET)
168             flx_mdns_mcast_join_ipv4 (i->hardware->index, m->server->fd_ipv4);
169         if (i->protocol == AF_INET6)
170             flx_mdns_mcast_join_ipv6 (i->hardware->index, m->server->fd_ipv6);
171
172         i->announcing = TRUE;
173         flx_announce_interface(m->server, i);
174     } else if (!b && i->announcing) {
175         g_message("Interface %s.%i no longer relevant", i->hardware->name, i->protocol);
176
177         flx_goodbye_interface(m->server, i, FALSE);
178
179         if (i->protocol == AF_INET)
180             flx_mdns_mcast_leave_ipv4 (i->hardware->index, m->server->fd_ipv4);
181         if (i->protocol == AF_INET6)
182             flx_mdns_mcast_leave_ipv6 (i->hardware->index, m->server->fd_ipv6);
183
184         i->announcing = FALSE;
185     }
186 }
187
188 static void check_hw_interface_relevant(flxInterfaceMonitor *m, flxHwInterface *hw) {
189     flxInterface *i;
190     
191     g_assert(m);
192     g_assert(hw);
193
194     for (i = hw->interfaces; i; i = i->by_hardware_next)
195         check_interface_relevant(m, i);
196 }
197
198 static void callback(flxNetlink *nl, struct nlmsghdr *n, gpointer userdata) {
199     flxInterfaceMonitor *m = userdata;
200     
201     g_assert(m);
202     g_assert(n);
203     g_assert(m->netlink == nl);
204
205     if (n->nlmsg_type == RTM_NEWLINK) {
206         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
207         flxHwInterface *hw;
208         struct rtattr *a = NULL;
209         size_t l;
210
211         if (ifinfomsg->ifi_family != AF_UNSPEC)
212             return;
213
214         if (!(hw = g_hash_table_lookup(m->hash_table, &ifinfomsg->ifi_index))) {
215             hw = g_new(flxHwInterface, 1);
216             hw->monitor = m;
217             hw->name = NULL;
218             hw->flags = 0;
219             hw->mtu = 1500;
220             hw->index = ifinfomsg->ifi_index;
221
222             FLX_LLIST_HEAD_INIT(flxInterface, hw->interfaces);
223             FLX_LLIST_PREPEND(flxHwInterface, hardware, m->hw_interfaces, hw);
224             
225             g_hash_table_insert(m->hash_table, &hw->index, hw);
226
227             if (m->server->fd_ipv4 >= 0)
228                 new_interface(m, hw, AF_INET);
229             if (m->server->fd_ipv6 >= 0)
230                 new_interface(m, hw, AF_INET6);
231         }
232         
233         hw->flags = ifinfomsg->ifi_flags;
234
235         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
236         a = IFLA_RTA(ifinfomsg);
237
238         while (RTA_OK(a, l)) {
239             switch(a->rta_type) {
240                 case IFLA_IFNAME:
241                     g_free(hw->name);
242                     hw->name = g_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
243                     break;
244
245                 case IFLA_MTU:
246                     g_assert(RTA_PAYLOAD(a) == sizeof(unsigned int));
247                     hw->mtu = *((unsigned int*) RTA_DATA(a));
248                     break;
249                     
250                 default:
251                     ;
252             }
253
254             a = RTA_NEXT(a, l);
255         }
256
257         update_hw_interface_rr(m, hw, FALSE);
258         check_hw_interface_relevant(m, hw);
259         
260     } else if (n->nlmsg_type == RTM_DELLINK) {
261         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
262         flxHwInterface *hw;
263         flxInterface *i;
264
265         if (ifinfomsg->ifi_family != AF_UNSPEC)
266             return;
267         
268         if (!(hw = flx_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index)))
269             return;
270
271         update_hw_interface_rr(m, hw, TRUE);
272         free_hw_interface(m, hw, FALSE);
273         
274     } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
275
276         struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
277         flxInterface *i;
278         struct rtattr *a = NULL;
279         size_t l;
280         flxAddress raddr;
281         int raddr_valid = 0;
282
283         if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
284             return;
285
286         if (!(i = (flxInterface*) flx_interface_monitor_get_interface(m, ifaddrmsg->ifa_index, ifaddrmsg->ifa_family)))
287             return;
288
289         raddr.family = ifaddrmsg->ifa_family;
290
291         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
292         a = IFA_RTA(ifaddrmsg);
293
294         while (RTA_OK(a, l)) {
295             switch(a->rta_type) {
296                 case IFA_ADDRESS:
297                     if ((raddr.family == AF_INET6 && RTA_PAYLOAD(a) != 16) ||
298                         (raddr.family == AF_INET && RTA_PAYLOAD(a) != 4))
299                         return;
300
301                     memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
302                     raddr_valid = 1;
303
304                     break;
305                     
306                 default:
307                     ;
308             }
309             
310             a = RTA_NEXT(a, l);
311         }
312
313         
314         if (!raddr_valid)
315             return;
316
317         if (n->nlmsg_type == RTM_NEWADDR) {
318             flxInterfaceAddress *addr;
319             
320             if (!(addr = get_address(m, i, &raddr))) {
321                 addr = g_new(flxInterfaceAddress, 1);
322                 addr->monitor = m;
323                 addr->address = raddr;
324                 addr->interface = i;
325                 addr->rr_id = -1;
326
327                 FLX_LLIST_PREPEND(flxInterfaceAddress, address, i->addresses, addr);
328             }
329             
330             addr->flags = ifaddrmsg->ifa_flags;
331             addr->scope = ifaddrmsg->ifa_scope;
332
333             update_address_rr(m, addr, FALSE);
334             check_interface_relevant(m, i);
335         } else {
336             flxInterfaceAddress *addr;
337             
338             if (!(addr = get_address(m, i, &raddr)))
339                 return;
340
341             update_address_rr(m, addr, TRUE);
342             free_address(m, addr);
343
344             check_interface_relevant(m, i);
345         }
346                 
347     } else if (n->nlmsg_type == NLMSG_DONE) {
348         
349         if (m->list == LIST_IFACE) {
350             m->list = LIST_DONE;
351             
352             if (netlink_list_items(m->netlink, RTM_GETADDR, &m->query_addr_seq) < 0)
353                 g_warning("NETLINK: Failed to list addrs: %s", strerror(errno));
354             else
355                 m->list = LIST_ADDR;
356         } else {
357             m->list = LIST_DONE;
358             g_message("Enumeration complete");
359         }
360         
361     } else if (n->nlmsg_type == NLMSG_ERROR && (n->nlmsg_seq == m->query_link_seq || n->nlmsg_seq == m->query_addr_seq)) {
362         struct nlmsgerr *e = NLMSG_DATA (n);
363                     
364         if (e->error)
365             g_warning("NETLINK: Failed to browse: %s", strerror(-e->error));
366     }
367 }
368
369 flxInterfaceMonitor *flx_interface_monitor_new(flxServer *s) {
370     flxInterfaceMonitor *m = NULL;
371
372     m = g_new0(flxInterfaceMonitor, 1);
373     m->server = s;
374     if (!(m->netlink = flx_netlink_new(s->context, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, callback, m)))
375         goto fail;
376
377     m->hash_table = g_hash_table_new(g_int_hash, g_int_equal);
378
379     FLX_LLIST_HEAD_INIT(flxInterface, m->interfaces);
380     FLX_LLIST_HEAD_INIT(flxHwInterface, m->hw_interfaces);
381
382     if (netlink_list_items(m->netlink, RTM_GETLINK, &m->query_link_seq) < 0)
383         goto fail;
384
385     m->list = LIST_IFACE;
386     
387     return m;
388
389 fail:
390     flx_interface_monitor_free(m);
391     return NULL;
392 }
393
394 void flx_interface_monitor_free(flxInterfaceMonitor *m) {
395     g_assert(m);
396
397     while (m->hw_interfaces)
398         free_hw_interface(m, m->hw_interfaces, TRUE);
399
400     g_assert(!m->interfaces);
401
402     
403     if (m->netlink)
404         flx_netlink_free(m->netlink);
405     
406     if (m->hash_table)
407         g_hash_table_destroy(m->hash_table);
408
409     g_free(m);
410 }
411
412
413 flxInterface* flx_interface_monitor_get_interface(flxInterfaceMonitor *m, gint index, guchar protocol) {
414     flxHwInterface *hw;
415     flxInterface *i;
416     
417     g_assert(m);
418     g_assert(index > 0);
419     g_assert(protocol != AF_UNSPEC);
420
421     if (!(hw = flx_interface_monitor_get_hw_interface(m, index)))
422         return NULL;
423
424     for (i = hw->interfaces; i; i = i->by_hardware_next)
425         if (i->protocol == protocol)
426             return i;
427
428     return NULL;
429 }
430
431 flxHwInterface* flx_interface_monitor_get_hw_interface(flxInterfaceMonitor *m, gint index) {
432     g_assert(m);
433     g_assert(index > 0);
434
435     return g_hash_table_lookup(m->hash_table, &index);
436 }
437
438
439 void flx_interface_send_packet(flxInterface *i, flxDnsPacket *p) {
440     g_assert(i);
441     g_assert(p);
442
443     if (flx_interface_relevant(i)) {
444         g_message("sending on '%s.%i'", i->hardware->name, i->protocol);
445
446         if (i->protocol == AF_INET && i->monitor->server->fd_ipv4 >= 0)
447             flx_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p);
448         else if (i->protocol == AF_INET6 && i->monitor->server->fd_ipv6 >= 0)
449             flx_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p);
450     }
451 }
452
453 void flx_interface_post_query(flxInterface *i, flxKey *key, gboolean immediately) {
454     g_assert(i);
455     g_assert(key);
456
457     if (flx_interface_relevant(i))
458         flx_packet_scheduler_post_query(i->scheduler, key, immediately);
459 }
460
461
462 void flx_interface_post_response(flxInterface *i, const flxAddress *a, flxRecord *record, gboolean flush_cache, gboolean immediately) {
463     g_assert(i);
464     g_assert(record);
465
466     if (flx_interface_relevant(i))
467         flx_packet_scheduler_post_response(i->scheduler, a, record, flush_cache, immediately);
468 }
469
470 void flx_interface_post_probe(flxInterface *i, flxRecord *record, gboolean immediately) {
471     g_assert(i);
472     g_assert(record);
473     
474     if (flx_interface_relevant(i))
475         flx_packet_scheduler_post_probe(i->scheduler, record, immediately);
476 }
477
478 void flx_dump_caches(flxInterfaceMonitor *m, FILE *f) {
479     flxInterface *i;
480     g_assert(m);
481
482     for (i = m->interfaces; i; i = i->interface_next) {
483         if (flx_interface_relevant(i)) {
484             fprintf(f, "\n;;; INTERFACE %s.%i ;;;\n", i->hardware->name, i->protocol);
485             flx_cache_dump(i->cache, f);
486         }
487     }
488     fprintf(f, "\n");
489 }
490
491 gboolean flx_interface_relevant(flxInterface *i) {
492     g_assert(i);
493
494     return
495         (i->hardware->flags & IFF_UP) &&
496         (i->hardware->flags & IFF_RUNNING) &&
497         !(i->hardware->flags & IFF_LOOPBACK) &&
498         (i->hardware->flags & IFF_MULTICAST) &&
499         i->addresses;
500 }
501
502 gboolean flx_interface_address_relevant(flxInterfaceAddress *a) { 
503     g_assert(a);
504
505     return a->scope == RT_SCOPE_UNIVERSE;
506 }
507
508
509 gboolean flx_interface_match(flxInterface *i, gint index, guchar protocol) {
510     g_assert(i);
511     
512     if (index > 0 && index != i->hardware->index)
513         return FALSE;
514
515     if (protocol != AF_UNSPEC && protocol != i->protocol)
516         return FALSE;
517
518     return TRUE;
519 }
520
521
522 void flx_interface_monitor_walk(flxInterfaceMonitor *m, gint interface, guchar protocol, flxInterfaceMonitorWalkCallback callback, gpointer userdata) {
523     g_assert(m);
524     g_assert(callback);
525     
526     if (interface > 0) {
527         if (protocol != AF_UNSPEC) {
528             flxInterface *i;
529             
530             if ((i = flx_interface_monitor_get_interface(m, interface, protocol)))
531                 callback(m, i, userdata);
532             
533         } else {
534             flxHwInterface *hw;
535             flxInterface *i;
536
537             if ((hw = flx_interface_monitor_get_hw_interface(m, interface)))
538                 for (i = hw->interfaces; i; i = i->by_hardware_next)
539                     if (flx_interface_match(i, interface, protocol))
540                         callback(m, i, userdata);
541         }
542         
543     } else {
544         flxInterface *i;
545         
546         for (i = m->interfaces; i; i = i->interface_next)
547             if (flx_interface_match(i, interface, protocol))
548                 callback(m, i, userdata);
549     }
550 }