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