Home | History | Annotate | Download | only in avahi-core
      1 /***
      2   This file is part of avahi.
      3 
      4   avahi is free software; you can redistribute it and/or modify it
      5   under the terms of the GNU Lesser General Public License as
      6   published by the Free Software Foundation; either version 2.1 of the
      7   License, or (at your option) any later version.
      8 
      9   avahi is distributed in the hope that it will be useful, but WITHOUT
     10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
     12   Public License for more details.
     13 
     14   You should have received a copy of the GNU Lesser General Public
     15   License along with avahi; if not, write to the Free Software
     16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
     17   USA.
     18 ***/
     19 
     20 #ifdef HAVE_CONFIG_H
     21 #include <config.h>
     22 #endif
     23 
     24 #include <string.h>
     25 #include <net/if.h>
     26 #include <errno.h>
     27 #include <string.h>
     28 
     29 #include "avahi-common/avahi-malloc.h"
     30 
     31 #include "log.h"
     32 #include "iface.h"
     33 #include "iface-linux.h"
     34 
     35 #ifndef IFLA_RTA
     36 #include <linux/if_addr.h>
     37 #define IFLA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
     38 #endif
     39 
     40 #ifndef IFA_RTA
     41 #include <linux/if_addr.h>
     42 #define IFA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
     43 #endif
     44 
     45 static int netlink_list_items(AvahiNetlink *nl, uint16_t type, unsigned *ret_seq) {
     46     struct nlmsghdr *n;
     47     struct rtgenmsg *gen;
     48     uint8_t req[1024];
     49 
     50     /* Issue a wild dump NETLINK request */
     51 
     52     memset(&req, 0, sizeof(req));
     53     n = (struct nlmsghdr*) req;
     54     n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
     55     n->nlmsg_type = type;
     56     n->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
     57     n->nlmsg_pid = 0;
     58 
     59     gen = NLMSG_DATA(n);
     60     memset(gen, 0, sizeof(struct rtgenmsg));
     61     gen->rtgen_family = AF_UNSPEC;
     62 
     63     return avahi_netlink_send(nl, n, ret_seq);
     64 }
     65 
     66 static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdata) {
     67     AvahiInterfaceMonitor *m = userdata;
     68 
     69     /* This routine is called for every RTNETLINK response packet */
     70 
     71     assert(m);
     72     assert(n);
     73     assert(m->osdep.netlink == nl);
     74 
     75     if (n->nlmsg_type == RTM_NEWLINK) {
     76 
     77         /* A new interface appeared or an existing one has been modified */
     78 
     79         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
     80         AvahiHwInterface *hw;
     81         struct rtattr *a = NULL;
     82         size_t l;
     83 
     84         /* A (superfluous?) sanity check */
     85         if (ifinfomsg->ifi_family != AF_UNSPEC)
     86             return;
     87 
     88         /* Check whether there already is an AvahiHwInterface object
     89          * for this link, so that we can update its data. Note that
     90          * Netlink sends us an RTM_NEWLINK not only when a new
     91          * interface appears, but when it changes, too */
     92 
     93         if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index)))
     94 
     95             /* No object found, so let's create a new
     96              * one. avahi_hw_interface_new() will call
     97              * avahi_interface_new() internally twice for IPv4 and
     98              * IPv6, so there is no need for us to do that
     99              * ourselves */
    100             if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
    101                 return; /* OOM */
    102 
    103         /* Check whether the flags of this interface are OK for us */
    104         hw->flags_ok =
    105             (ifinfomsg->ifi_flags & IFF_UP) &&
    106             (!m->server->config.use_iff_running || (ifinfomsg->ifi_flags & IFF_RUNNING)) &&
    107             !(ifinfomsg->ifi_flags & IFF_LOOPBACK) &&
    108             (ifinfomsg->ifi_flags & IFF_MULTICAST) &&
    109             (m->server->config.allow_point_to_point || !(ifinfomsg->ifi_flags & IFF_POINTOPOINT));
    110 
    111         /* Handle interface attributes */
    112         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
    113         a = IFLA_RTA(ifinfomsg);
    114 
    115         while (RTA_OK(a, l)) {
    116             switch(a->rta_type) {
    117                 case IFLA_IFNAME:
    118 
    119                     /* Fill in interface name */
    120                     avahi_free(hw->name);
    121                     hw->name = avahi_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
    122                     break;
    123 
    124                 case IFLA_MTU:
    125 
    126                     /* Fill in MTU */
    127                     assert(RTA_PAYLOAD(a) == sizeof(unsigned int));
    128                     hw->mtu = *((unsigned int*) RTA_DATA(a));
    129                     break;
    130 
    131                 case IFLA_ADDRESS:
    132 
    133                     /* Fill in hardware (MAC) address */
    134                     hw->mac_address_size = RTA_PAYLOAD(a);
    135                     if (hw->mac_address_size > AVAHI_MAC_ADDRESS_MAX)
    136                         hw->mac_address_size = AVAHI_MAC_ADDRESS_MAX;
    137 
    138                     memcpy(hw->mac_address, RTA_DATA(a), hw->mac_address_size);
    139                     break;
    140 
    141                 default:
    142                     ;
    143             }
    144 
    145             a = RTA_NEXT(a, l);
    146         }
    147 
    148         /* Check whether this interface is now "relevant" for us. If
    149          * it is Avahi will start to announce its records on this
    150          * interface and send out queries for subscribed records on
    151          * it */
    152         avahi_hw_interface_check_relevant(hw);
    153 
    154         /* Update any associated RRs of this interface. (i.e. the
    155          * _workstation._tcp record containing the MAC address) */
    156         avahi_hw_interface_update_rrs(hw, 0);
    157 
    158     } else if (n->nlmsg_type == RTM_DELLINK) {
    159 
    160         /* An interface has been removed */
    161 
    162         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
    163         AvahiHwInterface *hw;
    164 
    165         /* A (superfluous?) sanity check */
    166         if (ifinfomsg->ifi_family != AF_UNSPEC)
    167             return;
    168 
    169         /* Get a reference to our AvahiHwInterface object of this interface */
    170         if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
    171             return;
    172 
    173         /* Free our object */
    174         avahi_hw_interface_free(hw, 0);
    175 
    176     } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
    177 
    178         /* An address has been added, modified or removed */
    179 
    180         struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
    181         AvahiInterface *i;
    182         struct rtattr *a = NULL;
    183         size_t l;
    184         AvahiAddress raddr, rlocal, *r;
    185         int raddr_valid = 0, rlocal_valid = 0;
    186 
    187         /* We are only interested in IPv4 and IPv6 */
    188         if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
    189             return;
    190 
    191         /* Try to get a reference to our AvahiInterface object for the
    192          * interface this address is assigned to. If ther is no object
    193          * for this interface, we ignore this address. */
    194         if (!(i = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifaddrmsg->ifa_index, avahi_af_to_proto(ifaddrmsg->ifa_family))))
    195             return;
    196 
    197         /* Fill in address family for our new address */
    198         rlocal.proto = raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family);
    199 
    200         l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg));
    201         a = IFA_RTA(ifaddrmsg);
    202 
    203         while (RTA_OK(a, l)) {
    204 
    205             switch(a->rta_type) {
    206 
    207                 case IFA_ADDRESS:
    208 
    209                     if ((rlocal.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||
    210                         (rlocal.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4))
    211                         return;
    212 
    213                     memcpy(rlocal.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
    214                     rlocal_valid = 1;
    215 
    216                     break;
    217 
    218                 case IFA_LOCAL:
    219 
    220                     /* Fill in local address data. Usually this is
    221                      * preferable over IFA_ADDRESS if both are set,
    222                      * since this refers to the local address of a PPP
    223                      * link while IFA_ADDRESS refers to the other
    224                      * end. */
    225 
    226                     if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||
    227                         (raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4))
    228                         return;
    229 
    230                     memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
    231                     raddr_valid = 1;
    232 
    233                     break;
    234 
    235                 default:
    236                     ;
    237             }
    238 
    239             a = RTA_NEXT(a, l);
    240         }
    241 
    242         /* If there was no adress attached to this message, let's quit. */
    243         if (rlocal_valid)
    244             r = &rlocal;
    245         else if (raddr_valid)
    246             r = &raddr;
    247         else
    248             return;
    249 
    250         if (n->nlmsg_type == RTM_NEWADDR) {
    251             AvahiInterfaceAddress *addr;
    252 
    253             /* This address is new or has been modified, so let's get an object for it */
    254             if (!(addr = avahi_interface_monitor_get_address(m, i, r)))
    255 
    256                 /* Mmm, no object existing yet, so let's create a new one */
    257                 if (!(addr = avahi_interface_address_new(m, i, r, ifaddrmsg->ifa_prefixlen)))
    258                     return; /* OOM */
    259 
    260             /* Update the scope field for the address */
    261             addr->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE;
    262             addr->deprecated = !!(ifaddrmsg->ifa_flags & IFA_F_DEPRECATED);
    263         } else {
    264             AvahiInterfaceAddress *addr;
    265             assert(n->nlmsg_type == RTM_DELADDR);
    266 
    267             /* Try to get a reference to our AvahiInterfaceAddress object for this address */
    268             if (!(addr = avahi_interface_monitor_get_address(m, i, r)))
    269                 return;
    270 
    271             /* And free it */
    272             avahi_interface_address_free(addr);
    273         }
    274 
    275         /* Avahi only considers interfaces with at least one address
    276          * attached relevant. Since we migh have added or removed an
    277          * address, let's have it check again whether the interface is
    278          * now relevant */
    279         avahi_interface_check_relevant(i);
    280 
    281         /* Update any associated RRs, like A or AAAA for our new/removed address */
    282         avahi_interface_update_rrs(i, 0);
    283 
    284     } else if (n->nlmsg_type == NLMSG_DONE) {
    285 
    286         /* This wild dump request ended, so let's see what we do next */
    287 
    288         if (m->osdep.list == LIST_IFACE) {
    289 
    290             /* Mmmm, interfaces have been wild dumped already, so
    291              * let's go on with wild dumping the addresses */
    292 
    293             if (netlink_list_items(m->osdep.netlink, RTM_GETADDR, &m->osdep.query_addr_seq) < 0) {
    294                 avahi_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno));
    295                 m->osdep.list = LIST_DONE;
    296             } else
    297 
    298                 /* Update state information */
    299                 m->osdep.list = LIST_ADDR;
    300 
    301         } else
    302             /* We're done. Tell avahi_interface_monitor_sync() to finish. */
    303             m->osdep.list = LIST_DONE;
    304 
    305         if (m->osdep.list == LIST_DONE) {
    306 
    307             /* Only after this boolean variable has been set, Avahi
    308              * will start to announce or browse on all interfaces. It
    309              * is originaly set to 0, which means that relevancy
    310              * checks and RR updates are disabled during the wild
    311              * dumps. */
    312             m->list_complete = 1;
    313 
    314             /* So let's check if any interfaces are relevant now */
    315             avahi_interface_monitor_check_relevant(m);
    316 
    317             /* And update all RRs attached to any interface */
    318             avahi_interface_monitor_update_rrs(m, 0);
    319 
    320             /* Tell the user that the wild dump is complete */
    321             avahi_log_info("Network interface enumeration completed.");
    322         }
    323 
    324     } else if (n->nlmsg_type == NLMSG_ERROR &&
    325                (n->nlmsg_seq == m->osdep.query_link_seq || n->nlmsg_seq == m->osdep.query_addr_seq)) {
    326         struct nlmsgerr *e = NLMSG_DATA (n);
    327 
    328         /* Some kind of error happened. Let's just tell the user and
    329          * ignore it otherwise */
    330 
    331         if (e->error)
    332             avahi_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error));
    333     }
    334 }
    335 
    336 int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) {
    337     assert(m);
    338 
    339     /* Initialize our own data */
    340 
    341     m->osdep.netlink = NULL;
    342     m->osdep.query_addr_seq = m->osdep.query_link_seq = 0;
    343 
    344     /* Create a netlink object for us. It abstracts some things and
    345      * makes netlink easier to use. It will attach to the main loop
    346      * for us and call netlink_callback() whenever an event
    347      * happens. */
    348     if (!(m->osdep.netlink = avahi_netlink_new(m->server->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m)))
    349         goto fail;
    350 
    351     /* Set the initial state. */
    352     m->osdep.list = LIST_IFACE;
    353 
    354     /* Start the wild dump for the interfaces */
    355     if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0)
    356         goto fail;
    357 
    358     return 0;
    359 
    360 fail:
    361 
    362     if (m->osdep.netlink) {
    363         avahi_netlink_free(m->osdep.netlink);
    364         m->osdep.netlink = NULL;
    365     }
    366 
    367     return -1;
    368 }
    369 
    370 void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) {
    371     assert(m);
    372 
    373     if (m->osdep.netlink) {
    374         avahi_netlink_free(m->osdep.netlink);
    375         m->osdep.netlink = NULL;
    376     }
    377 }
    378 
    379 void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
    380     assert(m);
    381 
    382     /* Let's handle netlink events until we are done with wild
    383      * dumping */
    384 
    385     while (!m->list_complete)
    386         if (!avahi_netlink_work(m->osdep.netlink, 1) == 0)
    387             break;
    388 
    389     /* At this point Avahi knows about all local interfaces and
    390      * addresses in existance. */
    391 }
    392