]> git.meshlink.io Git - catta/blob - avahi-core/iface.c
Add support for server state change callbacks
[catta] / avahi-core / iface.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
6   avahi is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   avahi is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14   Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with avahi; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <string.h>
27 #include <sys/socket.h>
28 #include <asm/types.h>
29 #include <linux/netlink.h>
30 #include <linux/rtnetlink.h>
31 #include <errno.h>
32 #include <net/if.h>
33
34 #include "iface.h"
35 #include "netlink.h"
36 #include "dns.h"
37 #include "socket.h"
38 #include "announce.h"
39
40 static void update_address_rr(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a, gboolean remove) {
41     g_assert(m);
42     g_assert(a);
43
44     
45     if (avahi_interface_address_relevant(a) &&
46         !remove &&
47         m->server->config.register_addresses &&
48         (m->server->state == AVAHI_SERVER_RUNNING ||
49         m->server->state == AVAHI_SERVER_REGISTERING)) {
50         
51         if (!a->entry_group) {
52             a->entry_group = avahi_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL);
53             avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, AF_UNSPEC, 0, NULL, &a->address); 
54             avahi_entry_group_commit(a->entry_group);
55         }
56     } else {
57
58         if (a->entry_group) {
59
60             if (avahi_entry_group_get_state(a->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING)
61                 avahi_server_decrease_host_rr_pending(m->server);
62             
63             avahi_entry_group_free(a->entry_group);
64             a->entry_group = NULL;
65         }
66     } 
67 }
68
69 static void update_interface_rr(AvahiInterfaceMonitor *m, AvahiInterface *i, gboolean remove) {
70     AvahiInterfaceAddress *a;
71     g_assert(m);
72     g_assert(i);
73
74     for (a = i->addresses; a; a = a->address_next)
75         update_address_rr(m, a, remove);
76 }
77
78 static void update_hw_interface_rr(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, gboolean remove) {
79     AvahiInterface *i;
80
81     g_assert(m);
82     g_assert(hw);
83
84     for (i = hw->interfaces; i; i = i->by_hardware_next)
85         update_interface_rr(m, i, remove);
86 }
87
88 static void free_address(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a) {
89     g_assert(m);
90     g_assert(a);
91     g_assert(a->interface);
92
93     AVAHI_LLIST_REMOVE(AvahiInterfaceAddress, address, a->interface->addresses, a);
94
95     if (a->entry_group)
96         avahi_entry_group_free(a->entry_group);
97     
98     g_free(a);
99 }
100
101 static void free_interface(AvahiInterfaceMonitor *m, AvahiInterface *i, gboolean send_goodbye) {
102     g_assert(m);
103     g_assert(i);
104
105     g_message("removing interface %s.%i", i->hardware->name, i->protocol);
106     avahi_goodbye_interface(m->server, i, send_goodbye);
107     g_message("forcing responses...");
108     avahi_response_scheduler_force(i->response_scheduler);
109     g_message("done");
110     
111     g_assert(!i->announcements);
112
113     while (i->addresses)
114         free_address(m, i->addresses);
115
116     avahi_response_scheduler_free(i->response_scheduler);
117     avahi_query_scheduler_free(i->query_scheduler);
118     avahi_probe_scheduler_free(i->probe_scheduler);
119     avahi_cache_free(i->cache);
120     
121     AVAHI_LLIST_REMOVE(AvahiInterface, interface, m->interfaces, i);
122     AVAHI_LLIST_REMOVE(AvahiInterface, by_hardware, i->hardware->interfaces, i);
123     
124     g_free(i);
125 }
126
127 static void free_hw_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, gboolean send_goodbye) {
128     g_assert(m);
129     g_assert(hw);
130
131     while (hw->interfaces)
132         free_interface(m, hw->interfaces, send_goodbye);
133
134     AVAHI_LLIST_REMOVE(AvahiHwInterface, hardware, m->hw_interfaces, hw);
135     g_hash_table_remove(m->hash_table, &hw->index);
136
137     g_free(hw->name);
138     g_free(hw);
139 }
140
141 static AvahiInterfaceAddress* get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr) {
142     AvahiInterfaceAddress *ia;
143     
144     g_assert(m);
145     g_assert(i);
146     g_assert(raddr);
147
148     for (ia = i->addresses; ia; ia = ia->address_next)
149         if (avahi_address_cmp(&ia->address, raddr) == 0)
150             return ia;
151
152     return NULL;
153 }
154
155 static int netlink_list_items(AvahiNetlink *nl, guint16 type, guint *ret_seq) {
156     struct nlmsghdr *n;
157     struct rtgenmsg *gen;
158     guint8 req[1024];
159     
160     memset(&req, 0, sizeof(req));
161     n = (struct nlmsghdr*) req;
162     n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
163     n->nlmsg_type = type;
164     n->nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
165     n->nlmsg_pid = 0;
166
167     gen = NLMSG_DATA(n);
168     memset(gen, 0, sizeof(struct rtgenmsg));
169     gen->rtgen_family = AF_UNSPEC;
170
171     return avahi_netlink_send(nl, n, ret_seq);
172 }
173
174 static void new_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, guchar protocol) {
175     AvahiInterface *i;
176     
177     g_assert(m);
178     g_assert(hw);
179     g_assert(protocol != AF_UNSPEC);
180
181     i = g_new(AvahiInterface, 1);
182     i->monitor = m;
183     i->hardware = hw;
184     i->protocol = protocol;
185     i->announcing = FALSE;
186
187     AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses);
188     AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, i->announcements);
189
190     i->cache = avahi_cache_new(m->server, i);
191     i->response_scheduler = avahi_response_scheduler_new(i);
192     i->query_scheduler = avahi_query_scheduler_new(i);
193     i->probe_scheduler = avahi_probe_scheduler_new(i);
194
195     AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i);
196     AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i);
197 }
198
199 static void check_interface_relevant(AvahiInterfaceMonitor *m, AvahiInterface *i) {
200     gboolean b;
201
202     g_assert(m);
203     g_assert(i);
204
205     b = avahi_interface_relevant(i);
206
207     if (b && !i->announcing) {
208         g_message("New relevant interface %s.%i", i->hardware->name, i->protocol);
209
210         if (i->protocol == AF_INET)
211             avahi_mdns_mcast_join_ipv4 (i->hardware->index, m->server->fd_ipv4);
212         if (i->protocol == AF_INET6)
213             avahi_mdns_mcast_join_ipv6 (i->hardware->index, m->server->fd_ipv6);
214
215         i->announcing = TRUE;
216         avahi_announce_interface(m->server, i);
217     } else if (!b && i->announcing) {
218         g_message("Interface %s.%i no longer relevant", i->hardware->name, i->protocol);
219
220         if (i->protocol == AF_INET)
221             avahi_mdns_mcast_leave_ipv4 (i->hardware->index, m->server->fd_ipv4);
222         if (i->protocol == AF_INET6)
223             avahi_mdns_mcast_leave_ipv6 (i->hardware->index, m->server->fd_ipv6);
224
225         avahi_goodbye_interface(m->server, i, FALSE);
226         avahi_response_scheduler_clear(i->response_scheduler);
227         avahi_query_scheduler_clear(i->query_scheduler);
228         avahi_probe_scheduler_clear(i->probe_scheduler);
229         avahi_cache_flush(i->cache);
230
231         i->announcing = FALSE;
232     }
233 }
234
235 static void check_hw_interface_relevant(AvahiInterfaceMonitor *m, AvahiHwInterface *hw) {
236     AvahiInterface *i;
237     
238     g_assert(m);
239     g_assert(hw);
240
241     for (i = hw->interfaces; i; i = i->by_hardware_next)
242         check_interface_relevant(m, i);
243 }
244
245 static void callback(AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata) {
246     AvahiInterfaceMonitor *m = userdata;
247     
248     g_assert(m);
249     g_assert(n);
250     g_assert(m->netlink == nl);
251
252     if (n->nlmsg_type == RTM_NEWLINK) {
253         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
254         AvahiHwInterface *hw;
255         struct rtattr *a = NULL;
256         size_t l;
257
258         if (ifinfomsg->ifi_family != AF_UNSPEC)
259             return;
260
261         if (!(hw = g_hash_table_lookup(m->hash_table, &ifinfomsg->ifi_index))) {
262             hw = g_new(AvahiHwInterface, 1);
263             hw->monitor = m;
264             hw->name = NULL;
265             hw->flags = 0;
266             hw->mtu = 1500;
267             hw->index = ifinfomsg->ifi_index;
268
269             AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces);
270             AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw);
271             
272             g_hash_table_insert(m->hash_table, &hw->index, hw);
273
274             if (m->server->fd_ipv4 >= 0)
275                 new_interface(m, hw, AF_INET);
276             if (m->server->fd_ipv6 >= 0)
277                 new_interface(m, hw, AF_INET6);
278         }
279         
280         hw->flags = ifinfomsg->ifi_flags;
281
282         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
283         a = IFLA_RTA(ifinfomsg);
284
285         while (RTA_OK(a, l)) {
286             switch(a->rta_type) {
287                 case IFLA_IFNAME:
288                     g_free(hw->name);
289                     hw->name = g_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
290                     break;
291
292                 case IFLA_MTU:
293                     g_assert(RTA_PAYLOAD(a) == sizeof(unsigned int));
294                     hw->mtu = *((unsigned int*) RTA_DATA(a));
295                     break;
296                     
297                 default:
298                     ;
299             }
300
301             a = RTA_NEXT(a, l);
302         }
303
304         update_hw_interface_rr(m, hw, FALSE);
305         check_hw_interface_relevant(m, hw);
306         
307     } else if (n->nlmsg_type == RTM_DELLINK) {
308         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
309         AvahiHwInterface *hw;
310
311         if (ifinfomsg->ifi_family != AF_UNSPEC)
312             return;
313         
314         if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index)))
315             return;
316
317         update_hw_interface_rr(m, hw, TRUE);
318         free_hw_interface(m, hw, FALSE);
319         
320     } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
321
322         struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
323         AvahiInterface *i;
324         struct rtattr *a = NULL;
325         size_t l;
326         AvahiAddress raddr;
327         int raddr_valid = 0;
328
329         if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
330             return;
331
332         if (!(i = (AvahiInterface*) avahi_interface_monitor_get_interface(m, ifaddrmsg->ifa_index, ifaddrmsg->ifa_family)))
333             return;
334
335         raddr.family = ifaddrmsg->ifa_family;
336
337         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
338         a = IFA_RTA(ifaddrmsg);
339
340         while (RTA_OK(a, l)) {
341             switch(a->rta_type) {
342                 case IFA_ADDRESS:
343                     if ((raddr.family == AF_INET6 && RTA_PAYLOAD(a) != 16) ||
344                         (raddr.family == AF_INET && RTA_PAYLOAD(a) != 4))
345                         return;
346
347                     memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
348                     raddr_valid = 1;
349
350                     break;
351                     
352                 default:
353                     ;
354             }
355             
356             a = RTA_NEXT(a, l);
357         }
358
359         
360         if (!raddr_valid)
361             return;
362
363         if (n->nlmsg_type == RTM_NEWADDR) {
364             AvahiInterfaceAddress *addr;
365             
366             if (!(addr = get_address(m, i, &raddr))) {
367                 addr = g_new(AvahiInterfaceAddress, 1);
368                 addr->monitor = m;
369                 addr->address = raddr;
370                 addr->interface = i;
371                 addr->entry_group = NULL;
372
373                 AVAHI_LLIST_PREPEND(AvahiInterfaceAddress, address, i->addresses, addr);
374             }
375             
376             addr->flags = ifaddrmsg->ifa_flags;
377             addr->scope = ifaddrmsg->ifa_scope;
378
379             update_address_rr(m, addr, FALSE);
380             check_interface_relevant(m, i);
381         } else {
382             AvahiInterfaceAddress *addr;
383             
384             if (!(addr = get_address(m, i, &raddr)))
385                 return;
386
387             update_address_rr(m, addr, TRUE);
388             free_address(m, addr);
389
390             check_interface_relevant(m, i);
391         }
392                 
393     } else if (n->nlmsg_type == NLMSG_DONE) {
394         
395         if (m->list == LIST_IFACE) {
396             m->list = LIST_DONE;
397             
398             if (netlink_list_items(m->netlink, RTM_GETADDR, &m->query_addr_seq) < 0)
399                 g_warning("NETLINK: Failed to list addrs: %s", strerror(errno));
400             else
401                 m->list = LIST_ADDR;
402         } else {
403             m->list = LIST_DONE;
404             g_message("Enumeration complete");
405         }
406         
407     } else if (n->nlmsg_type == NLMSG_ERROR && (n->nlmsg_seq == m->query_link_seq || n->nlmsg_seq == m->query_addr_seq)) {
408         struct nlmsgerr *e = NLMSG_DATA (n);
409                     
410         if (e->error)
411             g_warning("NETLINK: Failed to browse: %s", strerror(-e->error));
412     }
413 }
414
415 AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *s) {
416     AvahiInterfaceMonitor *m = NULL;
417
418     m = g_new0(AvahiInterfaceMonitor, 1);
419     m->server = s;
420     if (!(m->netlink = avahi_netlink_new(s->context, G_PRIORITY_DEFAULT-10, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, callback, m)))
421         goto fail;
422
423     m->hash_table = g_hash_table_new(g_int_hash, g_int_equal);
424
425     AVAHI_LLIST_HEAD_INIT(AvahiInterface, m->interfaces);
426     AVAHI_LLIST_HEAD_INIT(AvahiHwInterface, m->hw_interfaces);
427
428     if (netlink_list_items(m->netlink, RTM_GETLINK, &m->query_link_seq) < 0)
429         goto fail;
430
431     m->list = LIST_IFACE;
432
433     return m;
434
435 fail:
436     avahi_interface_monitor_free(m);
437     return NULL;
438 }
439
440 void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
441     g_assert(m);
442     
443     while (m->list != LIST_DONE) {
444         if (!avahi_netlink_work(m->netlink, TRUE))
445             break;
446     } 
447 }
448
449 void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) {
450     g_assert(m);
451
452     while (m->hw_interfaces)
453         free_hw_interface(m, m->hw_interfaces, TRUE);
454
455     g_assert(!m->interfaces);
456
457     
458     if (m->netlink)
459         avahi_netlink_free(m->netlink);
460     
461     if (m->hash_table)
462         g_hash_table_destroy(m->hash_table);
463
464     g_free(m);
465 }
466
467
468 AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, gint index, guchar protocol) {
469     AvahiHwInterface *hw;
470     AvahiInterface *i;
471     
472     g_assert(m);
473     g_assert(index > 0);
474     g_assert(protocol != AF_UNSPEC);
475
476     if (!(hw = avahi_interface_monitor_get_hw_interface(m, index)))
477         return NULL;
478
479     for (i = hw->interfaces; i; i = i->by_hardware_next)
480         if (i->protocol == protocol)
481             return i;
482
483     return NULL;
484 }
485
486 AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, gint index) {
487     g_assert(m);
488     g_assert(index > 0);
489
490     return g_hash_table_lookup(m->hash_table, &index);
491 }
492
493
494 void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port) {
495     g_assert(i);
496     g_assert(p);
497 /*     char t[64]; */
498
499     if (!avahi_interface_relevant(i))
500         return;
501     
502     g_assert(!a || a->family == i->protocol);
503
504 /*     if (a) */
505 /*         g_message("unicast sending on '%s.%i' to %s:%u", i->hardware->name, i->protocol, avahi_address_snprint(t, sizeof(t), a), port); */
506 /*     else */
507 /*         g_message("multicast sending on '%s.%i'", i->hardware->name, i->protocol); */
508     
509     if (i->protocol == AF_INET && i->monitor->server->fd_ipv4 >= 0)
510         avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p, a ? &a->data.ipv4 : NULL, port);
511     else if (i->protocol == AF_INET6 && i->monitor->server->fd_ipv6 >= 0)
512         avahi_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p, a ? &a->data.ipv6 : NULL, port);
513 }
514
515 void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) {
516     g_assert(i);
517     g_assert(p);
518
519     avahi_interface_send_packet_unicast(i, p, NULL, 0);
520 }
521
522 gboolean avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, gboolean immediately) {
523     g_assert(i);
524     g_assert(key);
525
526     if (avahi_interface_relevant(i))
527         return avahi_query_scheduler_post(i->query_scheduler, key, immediately);
528
529     return FALSE;
530 }
531
532 gboolean avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, gboolean flush_cache, const AvahiAddress *querier, gboolean immediately) {
533     g_assert(i);
534     g_assert(record);
535
536     if (avahi_interface_relevant(i))
537         return avahi_response_scheduler_post(i->response_scheduler, record, flush_cache, querier, immediately);
538
539     return FALSE;
540 }
541
542 gboolean avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *record, gboolean immediately) {
543     g_assert(i);
544     g_assert(record);
545     
546     if (avahi_interface_relevant(i))
547         return avahi_probe_scheduler_post(i->probe_scheduler, record, immediately);
548
549     return FALSE;
550 }
551
552 void avahi_dump_caches(AvahiInterfaceMonitor *m, FILE *f) {
553     AvahiInterface *i;
554     g_assert(m);
555
556     for (i = m->interfaces; i; i = i->interface_next) {
557         if (avahi_interface_relevant(i)) {
558             fprintf(f, "\n;;; INTERFACE %s.%i ;;;\n", i->hardware->name, i->protocol);
559             avahi_cache_dump(i->cache, f);
560         }
561     }
562     fprintf(f, "\n");
563 }
564
565 gboolean avahi_interface_relevant(AvahiInterface *i) {
566     g_assert(i);
567
568     return
569         (i->hardware->flags & IFF_UP) &&
570         (i->hardware->flags & IFF_RUNNING) &&
571         !(i->hardware->flags & IFF_LOOPBACK) &&
572         (i->hardware->flags & IFF_MULTICAST) &&
573         i->addresses;
574 }
575
576 gboolean avahi_interface_address_relevant(AvahiInterfaceAddress *a) { 
577     g_assert(a);
578
579     return a->scope == RT_SCOPE_UNIVERSE;
580 }
581
582
583 gboolean avahi_interface_match(AvahiInterface *i, gint index, guchar protocol) {
584     g_assert(i);
585     
586     if (index > 0 && index != i->hardware->index)
587         return FALSE;
588
589     if (protocol != AF_UNSPEC && protocol != i->protocol)
590         return FALSE;
591
592     return TRUE;
593 }
594
595 void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, gint interface, guchar protocol, AvahiInterfaceMonitorWalkCallback callback, gpointer userdata) {
596     g_assert(m);
597     g_assert(callback);
598     
599     if (interface > 0) {
600         if (protocol != AF_UNSPEC) {
601             AvahiInterface *i;
602             
603             if ((i = avahi_interface_monitor_get_interface(m, interface, protocol)))
604                 callback(m, i, userdata);
605             
606         } else {
607             AvahiHwInterface *hw;
608             AvahiInterface *i;
609
610             if ((hw = avahi_interface_monitor_get_hw_interface(m, interface)))
611                 for (i = hw->interfaces; i; i = i->by_hardware_next)
612                     if (avahi_interface_match(i, interface, protocol))
613                         callback(m, i, userdata);
614         }
615         
616     } else {
617         AvahiInterface *i;
618         
619         for (i = m->interfaces; i; i = i->interface_next)
620             if (avahi_interface_match(i, interface, protocol))
621                 callback(m, i, userdata);
622     }
623 }
624
625 void avahi_update_host_rrs(AvahiInterfaceMonitor *m, gboolean remove) {
626     AvahiInterface *i;
627
628     g_assert(m);
629
630     for (i = m->interfaces; i; i = i->interface_next)
631         update_interface_rr(m, i, remove);
632 }