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