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