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