iface.c revision a29887070855153ac64a3503e2f0004c2056f8e8
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
40static 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
68static 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
78static 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
88static 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
101static 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
124static 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
138static 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
152static 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
171static 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
196static 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
232static 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
242static 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
412AvahiInterfaceMonitor *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
432fail:
433    avahi_interface_monitor_free(m);
434    return NULL;
435}
436
437void 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
446void 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
465AvahiInterface* 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
483AvahiHwInterface* 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
491void 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
512void 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
519gboolean 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
529gboolean 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
539gboolean 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
549void 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
562gboolean 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
592gboolean avahi_interface_address_relevant(AvahiInterfaceAddress *a) {
593    g_assert(a);
594
595    return a->scope == RT_SCOPE_UNIVERSE;
596}
597
598
599gboolean 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
611void 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
641void 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}
649