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