iface.c revision 907bf2598220828a50b22cc00395524bb74feb8e
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#include <stdio.h>
34
35#include <avahi-common/error.h>
36#include <avahi-common/malloc.h>
37
38#include "iface.h"
39#include "netlink.h"
40#include "dns.h"
41#include "socket.h"
42#include "announce.h"
43#include "util.h"
44#include "log.h"
45
46static void update_address_rr(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a, int remove_rrs) {
47    assert(m);
48    assert(a);
49
50    if (a->interface->announcing &&
51        m->list == LIST_DONE &&
52        avahi_interface_address_relevant(a) &&
53        !remove_rrs &&
54        m->server->config.publish_addresses &&
55        (m->server->state == AVAHI_SERVER_RUNNING ||
56        m->server->state == AVAHI_SERVER_REGISTERING)) {
57
58        /* Fill the entry group */
59        if (!a->entry_group)
60            a->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL);
61
62        if (!a->entry_group) /* OOM */
63            return;
64
65        if (avahi_s_entry_group_is_empty(a->entry_group)) {
66            char t[64];
67            avahi_address_snprint(t, sizeof(t), &a->address);
68
69            avahi_log_info("Registering new address %s on %s.", t, a->interface->hardware->name);
70
71            if (avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, a->interface->protocol, 0, NULL, &a->address) < 0) {
72                avahi_log_warn(__FILE__": avahi_server_add_address() failed: %s", avahi_strerror(m->server->error));
73                avahi_s_entry_group_free(a->entry_group);
74                a->entry_group = NULL;
75                return;
76            }
77
78            avahi_s_entry_group_commit(a->entry_group);
79        }
80    } else {
81
82        /* Clear the entry group */
83
84        if (a->entry_group && !avahi_s_entry_group_is_empty(a->entry_group)) {
85            char t[64];
86            avahi_address_snprint(t, sizeof(t), &a->address);
87
88            if (avahi_s_entry_group_get_state(a->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING &&
89		m->server->state == AVAHI_SERVER_REGISTERING)
90                avahi_server_decrease_host_rr_pending(m->server);
91
92            avahi_log_info("Withdrawing address %s on %s.", t, a->interface->hardware->name);
93
94            avahi_s_entry_group_reset(a->entry_group);
95        }
96    }
97}
98
99static void update_interface_rr(AvahiInterfaceMonitor *m, AvahiInterface *i, int remove_rrs) {
100    AvahiInterfaceAddress *a;
101
102    assert(m);
103    assert(i);
104
105    for (a = i->addresses; a; a = a->address_next)
106        update_address_rr(m, a, remove_rrs);
107}
108
109static void update_hw_interface_rr(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, int remove_rrs) {
110    AvahiInterface *i;
111
112    assert(m);
113    assert(hw);
114
115    for (i = hw->interfaces; i; i = i->by_hardware_next)
116        update_interface_rr(m, i, remove_rrs);
117
118    if (m->list == LIST_DONE &&
119        !remove_rrs &&
120        m->server->config.publish_workstation &&
121        (m->server->state == AVAHI_SERVER_RUNNING ||
122        m->server->state == AVAHI_SERVER_REGISTERING)) {
123
124        if (!hw->entry_group)
125            hw->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL);
126
127        if (!hw->entry_group)
128            return; /* OOM */
129
130        if (avahi_s_entry_group_is_empty(hw->entry_group)) {
131            char *name;
132            char *t;
133
134            if (!(t = avahi_format_mac_address(hw->mac_address, hw->mac_address_size)))
135                return; /* OOM */
136
137            name = avahi_strdup_printf("%s [%s]", m->server->host_name, t);
138            avahi_free(t);
139
140            if (!name)
141                return; /* OOM */
142
143            if (avahi_server_add_service(m->server, hw->entry_group, hw->index, AVAHI_PROTO_UNSPEC, name, "_workstation._tcp", NULL, NULL, 9, NULL) < 0) {
144                avahi_log_warn(__FILE__": avahi_server_add_service() failed.");
145                avahi_s_entry_group_free(hw->entry_group);
146                hw->entry_group = NULL;
147            } else
148                avahi_s_entry_group_commit(hw->entry_group);
149
150            avahi_free(name);
151        }
152
153    } else {
154
155        if (hw->entry_group && !avahi_s_entry_group_is_empty(hw->entry_group)) {
156
157            if (avahi_s_entry_group_get_state(hw->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING)
158                avahi_server_decrease_host_rr_pending(m->server);
159
160            avahi_s_entry_group_reset(hw->entry_group);
161        }
162    }
163}
164
165static void free_address(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a) {
166    assert(m);
167    assert(a);
168    assert(a->interface);
169
170    update_address_rr(m, a, 1);
171    AVAHI_LLIST_REMOVE(AvahiInterfaceAddress, address, a->interface->addresses, a);
172
173    if (a->entry_group)
174        avahi_s_entry_group_free(a->entry_group);
175
176    avahi_free(a);
177}
178
179static void free_interface(AvahiInterfaceMonitor *m, AvahiInterface *i, int send_goodbye) {
180    assert(m);
181    assert(i);
182
183    avahi_goodbye_interface(m->server, i, send_goodbye);
184    avahi_response_scheduler_force(i->response_scheduler);
185
186    assert(!i->announcements);
187
188    update_interface_rr(m, i, 1);
189
190    while (i->addresses)
191        free_address(m, i->addresses);
192
193    avahi_response_scheduler_free(i->response_scheduler);
194    avahi_query_scheduler_free(i->query_scheduler);
195    avahi_probe_scheduler_free(i->probe_scheduler);
196    avahi_cache_free(i->cache);
197
198    AVAHI_LLIST_REMOVE(AvahiInterface, interface, m->interfaces, i);
199    AVAHI_LLIST_REMOVE(AvahiInterface, by_hardware, i->hardware->interfaces, i);
200
201    avahi_free(i);
202}
203
204static void free_hw_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, int send_goodbye) {
205    assert(m);
206    assert(hw);
207
208    update_hw_interface_rr(m, hw, 1);
209
210    while (hw->interfaces)
211        free_interface(m, hw->interfaces, send_goodbye);
212
213    if (hw->entry_group)
214        avahi_s_entry_group_free(hw->entry_group);
215
216    AVAHI_LLIST_REMOVE(AvahiHwInterface, hardware, m->hw_interfaces, hw);
217    avahi_hashmap_remove(m->hashmap, &hw->index);
218
219    avahi_free(hw->name);
220    avahi_free(hw);
221}
222
223static AvahiInterfaceAddress* get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr) {
224    AvahiInterfaceAddress *ia;
225
226    assert(m);
227    assert(i);
228    assert(raddr);
229
230    for (ia = i->addresses; ia; ia = ia->address_next)
231        if (avahi_address_cmp(&ia->address, raddr) == 0)
232            return ia;
233
234    return NULL;
235}
236
237static int netlink_list_items(AvahiNetlink *nl, uint16_t type, unsigned *ret_seq) {
238    struct nlmsghdr *n;
239    struct rtgenmsg *gen;
240    uint8_t req[1024];
241
242    memset(&req, 0, sizeof(req));
243    n = (struct nlmsghdr*) req;
244    n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
245    n->nlmsg_type = type;
246    n->nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
247    n->nlmsg_pid = 0;
248
249    gen = NLMSG_DATA(n);
250    memset(gen, 0, sizeof(struct rtgenmsg));
251    gen->rtgen_family = AF_UNSPEC;
252
253    return avahi_netlink_send(nl, n, ret_seq);
254}
255
256static void new_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiProtocol protocol) {
257    AvahiInterface *i;
258
259    assert(m);
260    assert(hw);
261    assert(protocol != AVAHI_PROTO_UNSPEC);
262
263    if (!(i = avahi_new(AvahiInterface, 1)))
264        goto fail; /* OOM */
265
266    i->monitor = m;
267    i->hardware = hw;
268    i->protocol = protocol;
269    i->announcing = 0;
270
271    AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses);
272    AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, i->announcements);
273
274    i->cache = avahi_cache_new(m->server, i);
275    i->response_scheduler = avahi_response_scheduler_new(i);
276    i->query_scheduler = avahi_query_scheduler_new(i);
277    i->probe_scheduler = avahi_probe_scheduler_new(i);
278
279    if (!i->cache || !i->response_scheduler || !i->query_scheduler || !i->probe_scheduler)
280        goto fail; /* OOM */
281
282    AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i);
283    AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i);
284
285    return;
286fail:
287
288    if (i) {
289        if (i->cache)
290            avahi_cache_free(i->cache);
291        if (i->response_scheduler)
292            avahi_response_scheduler_free(i->response_scheduler);
293        if (i->query_scheduler)
294            avahi_query_scheduler_free(i->query_scheduler);
295        if (i->probe_scheduler)
296            avahi_probe_scheduler_free(i->probe_scheduler);
297    }
298
299}
300
301static void check_interface_relevant(AvahiInterfaceMonitor *m, AvahiInterface *i) {
302    int b;
303
304    assert(m);
305    assert(i);
306
307    b = avahi_interface_relevant(i);
308
309    if (m->list == LIST_DONE && b && !i->announcing) {
310        avahi_log_info("New relevant interface %s.%s.", i->hardware->name, avahi_proto_to_string(i->protocol));
311
312        if (i->protocol == AVAHI_PROTO_INET)
313            avahi_mdns_mcast_join_ipv4(m->server->fd_ipv4, i->hardware->index);
314        if (i->protocol == AVAHI_PROTO_INET6)
315            avahi_mdns_mcast_join_ipv6(m->server->fd_ipv6, i->hardware->index);
316
317        i->announcing = 1;
318        avahi_announce_interface(m->server, i);
319        avahi_browser_new_interface(m->server, i);
320    } else if (!b && i->announcing) {
321        avahi_log_info("Interface %s.%s no longer relevant.", i->hardware->name, avahi_proto_to_string(i->protocol));
322
323        if (i->protocol == AVAHI_PROTO_INET)
324            avahi_mdns_mcast_leave_ipv4(m->server->fd_ipv4, i->hardware->index);
325        if (i->protocol == AVAHI_PROTO_INET6)
326            avahi_mdns_mcast_leave_ipv6(m->server->fd_ipv6, i->hardware->index);
327
328        avahi_goodbye_interface(m->server, i, 0);
329        avahi_response_scheduler_clear(i->response_scheduler);
330        avahi_query_scheduler_clear(i->query_scheduler);
331        avahi_probe_scheduler_clear(i->probe_scheduler);
332        avahi_cache_flush(i->cache);
333
334        i->announcing = 0;
335    }
336}
337
338static void check_hw_interface_relevant(AvahiInterfaceMonitor *m, AvahiHwInterface *hw) {
339    AvahiInterface *i;
340
341    assert(m);
342    assert(hw);
343
344    for (i = hw->interfaces; i; i = i->by_hardware_next)
345        check_interface_relevant(m, i);
346}
347
348static void check_all_interfaces_relevant(AvahiInterfaceMonitor *m) {
349    AvahiInterface *i;
350    assert(m);
351
352    for (i = m->interfaces; i; i = i->interface_next)
353        check_interface_relevant(m, i);
354}
355
356static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdata) {
357    AvahiInterfaceMonitor *m = userdata;
358
359    assert(m);
360    assert(n);
361    assert(m->netlink == nl);
362
363    if (n->nlmsg_type == RTM_NEWLINK) {
364        struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
365        AvahiHwInterface *hw;
366        struct rtattr *a = NULL;
367        size_t l;
368
369        if (ifinfomsg->ifi_family != AF_UNSPEC)
370            return;
371
372        if (!(hw = avahi_hashmap_lookup(m->hashmap, &ifinfomsg->ifi_index))) {
373
374            if (!(hw = avahi_new(AvahiHwInterface, 1)))
375                return; /* OOM */
376
377            hw->monitor = m;
378            hw->name = NULL;
379            hw->flags = 0;
380            hw->mtu = 1500;
381            hw->index = (AvahiIfIndex) ifinfomsg->ifi_index;
382            hw->mac_address_size = 0;
383            hw->entry_group = NULL;
384
385            AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces);
386            AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw);
387
388            avahi_hashmap_insert(m->hashmap, &hw->index, hw);
389
390            if (m->server->fd_ipv4 >= 0)
391                new_interface(m, hw, AVAHI_PROTO_INET);
392            if (m->server->fd_ipv6 >= 0)
393                new_interface(m, hw, AVAHI_PROTO_INET6);
394        }
395
396        hw->flags = ifinfomsg->ifi_flags;
397
398        l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
399        a = IFLA_RTA(ifinfomsg);
400
401        while (RTA_OK(a, l)) {
402            switch(a->rta_type) {
403                case IFLA_IFNAME:
404                    avahi_free(hw->name);
405                    hw->name = avahi_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
406                    break;
407
408                case IFLA_MTU:
409                    assert(RTA_PAYLOAD(a) == sizeof(unsigned int));
410                    hw->mtu = *((unsigned int*) RTA_DATA(a));
411                    break;
412
413                case IFLA_ADDRESS: {
414                    hw->mac_address_size = RTA_PAYLOAD(a);
415                    if (hw->mac_address_size > AVAHI_MAX_MAC_ADDRESS)
416                        hw->mac_address_size = AVAHI_MAX_MAC_ADDRESS;
417
418                    memcpy(hw->mac_address, RTA_DATA(a), hw->mac_address_size);
419                    break;
420                }
421
422                default:
423                    ;
424            }
425
426            a = RTA_NEXT(a, l);
427        }
428
429        check_hw_interface_relevant(m, hw);
430        update_hw_interface_rr(m, hw, 0);
431
432    } else if (n->nlmsg_type == RTM_DELLINK) {
433        struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
434        AvahiHwInterface *hw;
435
436        if (ifinfomsg->ifi_family != AF_UNSPEC)
437            return;
438
439        if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
440            return;
441
442        free_hw_interface(m, hw, 0);
443
444    } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
445
446        struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
447        AvahiInterface *i;
448        struct rtattr *a = NULL;
449        size_t l;
450        AvahiAddress raddr;
451        int raddr_valid = 0;
452
453        if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
454            return;
455
456        if (!(i = (AvahiInterface*) avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifaddrmsg->ifa_index, avahi_af_to_proto(ifaddrmsg->ifa_family))))
457            return;
458
459        raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family);
460
461        l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg));
462        a = IFA_RTA(ifaddrmsg);
463
464        while (RTA_OK(a, l)) {
465
466            switch(a->rta_type) {
467                case IFA_ADDRESS:
468                    if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||
469                        (raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4))
470                        return;
471
472                    memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
473                    raddr_valid = 1;
474
475                    break;
476
477                default:
478                    ;
479            }
480
481            a = RTA_NEXT(a, l);
482        }
483
484        if (!raddr_valid)
485            return;
486
487        if (n->nlmsg_type == RTM_NEWADDR) {
488            AvahiInterfaceAddress *addr;
489
490            if (!(addr = get_address(m, i, &raddr))) {
491                if (!(addr = avahi_new(AvahiInterfaceAddress, 1)))
492                    return; /* OOM */
493
494                addr->monitor = m;
495                addr->address = raddr;
496                addr->interface = i;
497                addr->entry_group = NULL;
498
499                AVAHI_LLIST_PREPEND(AvahiInterfaceAddress, address, i->addresses, addr);
500            }
501
502            addr->flags = ifaddrmsg->ifa_flags;
503            addr->scope = ifaddrmsg->ifa_scope;
504            addr->prefix_len = ifaddrmsg->ifa_prefixlen;
505        } else {
506            AvahiInterfaceAddress *addr;
507
508            if (!(addr = get_address(m, i, &raddr)))
509                return;
510
511            free_address(m, addr);
512        }
513
514        check_interface_relevant(m, i);
515        update_interface_rr(m, i, 0);
516
517    } else if (n->nlmsg_type == NLMSG_DONE) {
518
519        if (m->list == LIST_IFACE) {
520
521            if (netlink_list_items(m->netlink, RTM_GETADDR, &m->query_addr_seq) < 0) {
522                avahi_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno));
523                m->list = LIST_DONE;
524            } else
525                m->list = LIST_ADDR;
526
527        } else
528            /* We're through */
529            m->list = LIST_DONE;
530
531        if (m->list == LIST_DONE) {
532            check_all_interfaces_relevant(m);
533            avahi_update_host_rrs(m, 0);
534            avahi_log_info("Network interface enumeration completed.");
535        }
536
537    } else if (n->nlmsg_type == NLMSG_ERROR && (n->nlmsg_seq == m->query_link_seq || n->nlmsg_seq == m->query_addr_seq)) {
538        struct nlmsgerr *e = NLMSG_DATA (n);
539
540        if (e->error)
541            avahi_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error));
542    }
543}
544
545AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *s) {
546    AvahiInterfaceMonitor *m = NULL;
547
548    if (!(m = avahi_new0(AvahiInterfaceMonitor, 1)))
549        return NULL; /* OOM */
550
551    m->server = s;
552    if (!(m->netlink = avahi_netlink_new(s->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m)))
553        goto fail;
554
555    m->hashmap = avahi_hashmap_new(avahi_int_hash, avahi_int_equal, NULL, NULL);
556
557    AVAHI_LLIST_HEAD_INIT(AvahiInterface, m->interfaces);
558    AVAHI_LLIST_HEAD_INIT(AvahiHwInterface, m->hw_interfaces);
559
560    if (netlink_list_items(m->netlink, RTM_GETLINK, &m->query_link_seq) < 0)
561        goto fail;
562
563    m->list = LIST_IFACE;
564
565    return m;
566
567fail:
568    avahi_interface_monitor_free(m);
569    return NULL;
570}
571
572void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
573    assert(m);
574
575    while (m->list != LIST_DONE) {
576        if (!avahi_netlink_work(m->netlink, 1))
577            break;
578    }
579}
580
581void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) {
582    assert(m);
583
584    while (m->hw_interfaces)
585        free_hw_interface(m, m->hw_interfaces, 1);
586
587    assert(!m->interfaces);
588
589
590    if (m->netlink)
591        avahi_netlink_free(m->netlink);
592
593    if (m->hashmap)
594        avahi_hashmap_free(m->hashmap);
595
596    avahi_free(m);
597}
598
599
600AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol) {
601    AvahiHwInterface *hw;
602    AvahiInterface *i;
603
604    assert(m);
605    assert(idx > 0);
606    assert(protocol != AVAHI_PROTO_UNSPEC);
607
608    if (!(hw = avahi_interface_monitor_get_hw_interface(m, idx)))
609        return NULL;
610
611    for (i = hw->interfaces; i; i = i->by_hardware_next)
612        if (i->protocol == protocol)
613            return i;
614
615    return NULL;
616}
617
618AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx) {
619    assert(m);
620    assert(idx > 0);
621
622    return avahi_hashmap_lookup(m->hashmap, &idx);
623}
624
625void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port) {
626    assert(i);
627    assert(p);
628/*     char t[64]; */
629
630    if (!avahi_interface_relevant(i))
631        return;
632
633    assert(!a || a->proto == i->protocol);
634
635/*     if (a) */
636/*         avahi_log_debug("unicast sending on '%s.%i' to %s:%u", i->hardware->name, i->protocol, avahi_address_snprint(t, sizeof(t), a), port); */
637/*     else */
638/*         avahi_log_debug("multicast sending on '%s.%i'", i->hardware->name, i->protocol); */
639
640    if (i->protocol == AVAHI_PROTO_INET && i->monitor->server->fd_ipv4 >= 0)
641        avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p, a ? &a->data.ipv4 : NULL, port);
642    else if (i->protocol == AVAHI_PROTO_INET6 && i->monitor->server->fd_ipv6 >= 0)
643        avahi_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p, a ? &a->data.ipv6 : NULL, port);
644}
645
646void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) {
647    assert(i);
648    assert(p);
649
650    avahi_interface_send_packet_unicast(i, p, NULL, 0);
651}
652
653int avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, int immediately) {
654    assert(i);
655    assert(key);
656
657    if (avahi_interface_relevant(i))
658        return avahi_query_scheduler_post(i->query_scheduler, key, immediately);
659
660    return 0;
661}
662
663int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) {
664    assert(i);
665    assert(record);
666
667    if (avahi_interface_relevant(i))
668        return avahi_response_scheduler_post(i->response_scheduler, record, flush_cache, querier, immediately);
669
670    return 0;
671}
672
673int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *record, int immediately) {
674    assert(i);
675    assert(record);
676
677    if (avahi_interface_relevant(i))
678        return avahi_probe_scheduler_post(i->probe_scheduler, record, immediately);
679
680    return 0;
681}
682
683int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata) {
684    AvahiInterface *i;
685    assert(m);
686
687    for (i = m->interfaces; i; i = i->interface_next) {
688        if (avahi_interface_relevant(i)) {
689            char ln[256];
690            snprintf(ln, sizeof(ln), ";;; INTERFACE %s.%s ;;;", i->hardware->name, avahi_proto_to_string(i->protocol));
691            callback(ln, userdata);
692            if (avahi_cache_dump(i->cache, callback, userdata) < 0)
693                return -1;
694        }
695    }
696
697    return 0;
698}
699
700int avahi_interface_relevant(AvahiInterface *i) {
701    AvahiInterfaceAddress *a;
702    int relevant_address;
703
704    assert(i);
705
706    relevant_address = 0;
707
708    for (a = i->addresses; a; a = a->address_next)
709        if (avahi_interface_address_relevant(a)) {
710            relevant_address = 1;
711            break;
712        }
713
714/*     avahi_log_debug("%p. iface-relevant: %i %i %i %i %i %i", i, relevant_address, */
715/*               (i->hardware->flags & IFF_UP), */
716/*               (i->hardware->flags & IFF_RUNNING), */
717/*               !(i->hardware->flags & IFF_LOOPBACK), */
718/*               (i->hardware->flags & IFF_MULTICAST), */
719/*               !(i->hardware->flags & IFF_POINTOPOINT)); */
720
721    return
722        (i->hardware->flags & IFF_UP) &&
723        (!i->monitor->server->config.use_iff_running || (i->hardware->flags & IFF_RUNNING)) &&
724        !(i->hardware->flags & IFF_LOOPBACK) &&
725        (i->hardware->flags & IFF_MULTICAST) &&
726        !(i->hardware->flags & IFF_POINTOPOINT) &&
727        relevant_address;
728}
729
730int avahi_interface_address_relevant(AvahiInterfaceAddress *a) {
731    AvahiInterfaceAddress *b;
732    assert(a);
733
734    /* Publish public IP addresses */
735    if (a->scope == RT_SCOPE_UNIVERSE ||
736        a->scope == RT_SCOPE_SITE)
737        return 1;
738
739    if (a->scope == RT_SCOPE_LINK) {
740
741        /* Publish link local IP addresses if they are the only ones on the link */
742        for (b = a->interface->addresses; b; b = b->address_next) {
743            if (b == a)
744                continue;
745
746            if (b->scope == RT_SCOPE_UNIVERSE ||
747                b->scope == RT_SCOPE_SITE)
748                return 0;
749        }
750
751        return 1;
752    }
753
754    return 0;
755}
756
757int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol) {
758    assert(i);
759
760    if (idx > 0 && idx != i->hardware->index)
761        return 0;
762
763    if (protocol != AVAHI_PROTO_UNSPEC && protocol != i->protocol)
764        return 0;
765
766    return 1;
767}
768
769void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex interface, AvahiProtocol protocol, AvahiInterfaceMonitorWalkCallback callback, void* userdata) {
770    assert(m);
771    assert(callback);
772
773    if (interface > 0) {
774        if (protocol != AVAHI_PROTO_UNSPEC) {
775            AvahiInterface *i;
776
777            if ((i = avahi_interface_monitor_get_interface(m, interface, protocol)))
778                callback(m, i, userdata);
779
780        } else {
781            AvahiHwInterface *hw;
782            AvahiInterface *i;
783
784            if ((hw = avahi_interface_monitor_get_hw_interface(m, interface)))
785                for (i = hw->interfaces; i; i = i->by_hardware_next)
786                    if (avahi_interface_match(i, interface, protocol))
787                        callback(m, i, userdata);
788        }
789
790    } else {
791        AvahiInterface *i;
792
793        for (i = m->interfaces; i; i = i->interface_next)
794            if (avahi_interface_match(i, interface, protocol))
795                callback(m, i, userdata);
796    }
797}
798
799void avahi_update_host_rrs(AvahiInterfaceMonitor *m, int remove_rrs) {
800    AvahiHwInterface *hw;
801
802    assert(m);
803
804    for (hw = m->hw_interfaces; hw; hw = hw->hardware_next)
805        update_hw_interface_rr(m, hw, remove_rrs);
806}
807
808int avahi_address_is_local(AvahiInterfaceMonitor *m, const AvahiAddress *a) {
809    AvahiInterface *i;
810    AvahiInterfaceAddress *ia;
811    assert(m);
812    assert(a);
813
814    for (i = m->interfaces; i; i = i->interface_next)
815        for (ia = i->addresses; ia; ia = ia->address_next)
816            if (avahi_address_cmp(a, &ia->address) == 0)
817                return 1;
818
819    return 0;
820}
821
822int avahi_interface_address_on_link(AvahiInterface *i, const AvahiAddress *a) {
823    AvahiInterfaceAddress *ia;
824
825    assert(i);
826    assert(a);
827
828    if (a->proto != i->protocol)
829        return 0;
830
831    for (ia = i->addresses; ia; ia = ia->address_next) {
832
833        if (a->proto == AVAHI_PROTO_INET) {
834            uint32_t m;
835
836            m = ~(((uint32_t) -1) >> ia->prefix_len);
837
838            if ((ntohl(a->data.ipv4.address) & m) == (ntohl(ia->address.data.ipv4.address) & m))
839                return 1;
840        } else {
841            unsigned j;
842            unsigned char pl;
843            assert(a->proto == AVAHI_PROTO_INET6);
844
845            pl = ia->prefix_len;
846
847            for (j = 0; j < 16; j++) {
848                uint8_t m;
849
850                if (pl == 0)
851                    return 1;
852
853                if (pl >= 8) {
854                    m = 0xFF;
855                    pl -= 8;
856                } else {
857                    m = ~(0xFF >> pl);
858                    pl = 0;
859                }
860
861                if ((a->data.ipv6.address[j] & m) != (ia->address.data.ipv6.address[j] & m))
862                    break;
863            }
864        }
865    }
866
867    return 0;
868}
869
870int avahi_interface_has_address(AvahiInterfaceMonitor *m, AvahiIfIndex iface, const AvahiAddress *a) {
871    AvahiInterface *i;
872    AvahiInterfaceAddress *j;
873
874    assert(m);
875    assert(iface != AVAHI_IF_UNSPEC);
876    assert(a);
877
878    if (!(i = avahi_interface_monitor_get_interface(m, iface, a->proto)))
879        return 0;
880
881    for (j = i->addresses; j; j = j->address_next)
882        if (avahi_address_cmp(a, &j->address) == 0)
883            return 1;
884
885    return 0;
886}
887