]> git.meshlink.io Git - catta/blobdiff - avahi-core/iface-linux.c
forgot to pull the publish_no_reverse change to the example.
[catta] / avahi-core / iface-linux.c
index 5e16d6abd355fa1ccff4c94c74c097f022bd0c0b..da497bc4dcbadb4816d5dc301acb25d3fd55e135 100644 (file)
@@ -1,18 +1,16 @@
-/* $Id$ */
-
 /***
   This file is part of avahi.
+
   avahi is free software; you can redistribute it and/or modify it
   under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation; either version 2.1 of the
   License, or (at your option) any later version.
+
   avahi is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
   Public License for more details.
+
   You should have received a copy of the GNU Lesser General Public
   License along with avahi; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 #include "iface.h"
 #include "iface-linux.h"
 
+#ifndef IFLA_RTA
+#include <linux/if_addr.h>
+#define IFLA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#endif
+
+#ifndef IFA_RTA
+#include <linux/if_addr.h>
+#define IFA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#endif
+
 static int netlink_list_items(AvahiNetlink *nl, uint16_t type, unsigned *ret_seq) {
     struct nlmsghdr *n;
     struct rtgenmsg *gen;
     uint8_t req[1024];
-    
+
+    /* Issue a wild dump NETLINK request */
+
     memset(&req, 0, sizeof(req));
     n = (struct nlmsghdr*) req;
     n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
     n->nlmsg_type = type;
-    n->nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
+    n->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP;
     n->nlmsg_pid = 0;
 
     gen = NLMSG_DATA(n);
@@ -55,21 +65,38 @@ static int netlink_list_items(AvahiNetlink *nl, uint16_t type, unsigned *ret_seq
 
 static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdata) {
     AvahiInterfaceMonitor *m = userdata;
-    
+
+    /* This routine is called for every RTNETLINK response packet */
+
     assert(m);
     assert(n);
     assert(m->osdep.netlink == nl);
 
     if (n->nlmsg_type == RTM_NEWLINK) {
+
+        /* A new interface appeared or an existing one has been modified */
+
         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
         AvahiHwInterface *hw;
         struct rtattr *a = NULL;
         size_t l;
-        
+
+        /* A (superfluous?) sanity check */
         if (ifinfomsg->ifi_family != AF_UNSPEC)
             return;
 
+        /* Check whether there already is an AvahiHwInterface object
+         * for this link, so that we can update its data. Note that
+         * Netlink sends us an RTM_NEWLINK not only when a new
+         * interface appears, but when it changes, too */
+
         if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index)))
+
+            /* No object found, so let's create a new
+             * one. avahi_hw_interface_new() will call
+             * avahi_interface_new() internally twice for IPv4 and
+             * IPv6, so there is no need for us to do that
+             * ourselves */
             if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
                 return; /* OOM */
 
@@ -79,33 +106,38 @@ static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdat
             (!m->server->config.use_iff_running || (ifinfomsg->ifi_flags & IFF_RUNNING)) &&
             !(ifinfomsg->ifi_flags & IFF_LOOPBACK) &&
             (ifinfomsg->ifi_flags & IFF_MULTICAST) &&
-            !(ifinfomsg->ifi_flags & IFF_POINTOPOINT);
-            
+            (m->server->config.allow_point_to_point || !(ifinfomsg->ifi_flags & IFF_POINTOPOINT));
 
+        /* Handle interface attributes */
         l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
         a = IFLA_RTA(ifinfomsg);
 
         while (RTA_OK(a, l)) {
             switch(a->rta_type) {
                 case IFLA_IFNAME:
+
+                    /* Fill in interface name */
                     avahi_free(hw->name);
                     hw->name = avahi_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
                     break;
 
                 case IFLA_MTU:
+
+                    /* Fill in MTU */
                     assert(RTA_PAYLOAD(a) == sizeof(unsigned int));
                     hw->mtu = *((unsigned int*) RTA_DATA(a));
                     break;
 
-                case IFLA_ADDRESS: {
+                case IFLA_ADDRESS:
+
+                    /* Fill in hardware (MAC) address */
                     hw->mac_address_size = RTA_PAYLOAD(a);
-                    if (hw->mac_address_size > AVAHI_MAX_MAC_ADDRESS)
-                        hw->mac_address_size = AVAHI_MAX_MAC_ADDRESS;
-                    
+                    if (hw->mac_address_size > AVAHI_MAC_ADDRESS_MAX)
+                        hw->mac_address_size = AVAHI_MAC_ADDRESS_MAX;
+
                     memcpy(hw->mac_address, RTA_DATA(a), hw->mac_address_size);
                     break;
-                }
-                    
+
                 default:
                     ;
             }
@@ -113,37 +145,57 @@ static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdat
             a = RTA_NEXT(a, l);
         }
 
+        /* Check whether this interface is now "relevant" for us. If
+         * it is Avahi will start to announce its records on this
+         * interface and send out queries for subscribed records on
+         * it */
         avahi_hw_interface_check_relevant(hw);
+
+        /* Update any associated RRs of this interface. (i.e. the
+         * _workstation._tcp record containing the MAC address) */
         avahi_hw_interface_update_rrs(hw, 0);
-        
+
     } else if (n->nlmsg_type == RTM_DELLINK) {
+
+        /* An interface has been removed */
+
         struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
         AvahiHwInterface *hw;
 
+        /* A (superfluous?) sanity check */
         if (ifinfomsg->ifi_family != AF_UNSPEC)
             return;
-        
+
+        /* Get a reference to our AvahiHwInterface object of this interface */
         if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
             return;
 
+        /* Free our object */
         avahi_hw_interface_free(hw, 0);
-        
+
     } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
 
+        /* An address has been added, modified or removed */
+
         struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
         AvahiInterface *i;
         struct rtattr *a = NULL;
         size_t l;
-        AvahiAddress raddr;
-        int raddr_valid = 0;
+        AvahiAddress raddr, rlocal, *r;
+        int raddr_valid = 0, rlocal_valid = 0;
 
+        /* We are only interested in IPv4 and IPv6 */
         if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
             return;
 
+        /* Try to get a reference to our AvahiInterface object for the
+         * interface this address is assigned to. If ther is no object
+         * for this interface, we ignore this address. */
         if (!(i = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifaddrmsg->ifa_index, avahi_af_to_proto(ifaddrmsg->ifa_family))))
             return;
 
-        raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family);
+        /* Fill in address family for our new address */
+        rlocal.proto = raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family);
 
         l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg));
         a = IFA_RTA(ifaddrmsg);
@@ -151,7 +203,26 @@ static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdat
         while (RTA_OK(a, l)) {
 
             switch(a->rta_type) {
+
                 case IFA_ADDRESS:
+
+                    if ((rlocal.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||
+                        (rlocal.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4))
+                        return;
+
+                    memcpy(rlocal.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
+                    rlocal_valid = 1;
+
+                    break;
+
+                case IFA_LOCAL:
+
+                    /* Fill in local address data. Usually this is
+                     * preferable over IFA_ADDRESS if both are set,
+                     * since this refers to the local address of a PPP
+                     * link while IFA_ADDRESS refers to the other
+                     * end. */
+
                     if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||
                         (raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4))
                         return;
@@ -164,58 +235,99 @@ static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdat
                 default:
                     ;
             }
-            
+
             a = RTA_NEXT(a, l);
         }
-        
-        if (!raddr_valid)
+
+        /* If there was no adress attached to this message, let's quit. */
+        if (rlocal_valid)
+            r = &rlocal;
+        else if (raddr_valid)
+            r = &raddr;
+        else
             return;
 
         if (n->nlmsg_type == RTM_NEWADDR) {
             AvahiInterfaceAddress *addr;
-            
-            if (!(addr = avahi_interface_monitor_get_address(m, i, &raddr)))
-                if (!(addr = avahi_interface_address_new(m, i, &raddr, ifaddrmsg->ifa_prefixlen)))
+
+            /* This address is new or has been modified, so let's get an object for it */
+            if (!(addr = avahi_interface_monitor_get_address(m, i, r)))
+
+                /* Mmm, no object existing yet, so let's create a new one */
+                if (!(addr = avahi_interface_address_new(m, i, r, ifaddrmsg->ifa_prefixlen)))
                     return; /* OOM */
-            
+
+            /* Update the scope field for the address */
             addr->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE;
+            addr->deprecated = !!(ifaddrmsg->ifa_flags & IFA_F_DEPRECATED);
         } else {
             AvahiInterfaceAddress *addr;
-            
-            if (!(addr = avahi_interface_monitor_get_address(m, i, &raddr)))
+            assert(n->nlmsg_type == RTM_DELADDR);
+
+            /* Try to get a reference to our AvahiInterfaceAddress object for this address */
+            if (!(addr = avahi_interface_monitor_get_address(m, i, r)))
                 return;
 
+            /* And free it */
             avahi_interface_address_free(addr);
         }
 
+        /* Avahi only considers interfaces with at least one address
+         * attached relevant. Since we migh have added or removed an
+         * address, let's have it check again whether the interface is
+         * now relevant */
         avahi_interface_check_relevant(i);
+
+        /* Update any associated RRs, like A or AAAA for our new/removed address */
         avahi_interface_update_rrs(i, 0);
-        
+
     } else if (n->nlmsg_type == NLMSG_DONE) {
-        
+
+        /* This wild dump request ended, so let's see what we do next */
+
         if (m->osdep.list == LIST_IFACE) {
-            
+
+            /* Mmmm, interfaces have been wild dumped already, so
+             * let's go on with wild dumping the addresses */
+
             if (netlink_list_items(m->osdep.netlink, RTM_GETADDR, &m->osdep.query_addr_seq) < 0) {
                 avahi_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno));
                 m->osdep.list = LIST_DONE;
             } else
+
+                /* Update state information */
                 m->osdep.list = LIST_ADDR;
 
         } else
-            /* We're through */
+            /* We're done. Tell avahi_interface_monitor_sync() to finish. */
             m->osdep.list = LIST_DONE;
 
         if (m->osdep.list == LIST_DONE) {
+
+            /* Only after this boolean variable has been set, Avahi
+             * will start to announce or browse on all interfaces. It
+             * is originaly set to 0, which means that relevancy
+             * checks and RR updates are disabled during the wild
+             * dumps. */
             m->list_complete = 1;
+
+            /* So let's check if any interfaces are relevant now */
             avahi_interface_monitor_check_relevant(m);
+
+            /* And update all RRs attached to any interface */
             avahi_interface_monitor_update_rrs(m, 0);
+
+            /* Tell the user that the wild dump is complete */
             avahi_log_info("Network interface enumeration completed.");
         }
-        
+
     } else if (n->nlmsg_type == NLMSG_ERROR &&
                (n->nlmsg_seq == m->osdep.query_link_seq || n->nlmsg_seq == m->osdep.query_addr_seq)) {
         struct nlmsgerr *e = NLMSG_DATA (n);
-                    
+
+        /* Some kind of error happened. Let's just tell the user and
+         * ignore it otherwise */
+
         if (e->error)
             avahi_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error));
     }
@@ -224,17 +336,25 @@ static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdat
 int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) {
     assert(m);
 
+    /* Initialize our own data */
+
     m->osdep.netlink = NULL;
     m->osdep.query_addr_seq = m->osdep.query_link_seq = 0;
-    
+
+    /* Create a netlink object for us. It abstracts some things and
+     * makes netlink easier to use. It will attach to the main loop
+     * for us and call netlink_callback() whenever an event
+     * happens. */
     if (!(m->osdep.netlink = avahi_netlink_new(m->server->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m)))
         goto fail;
 
+    /* Set the initial state. */
+    m->osdep.list = LIST_IFACE;
+
+    /* Start the wild dump for the interfaces */
     if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0)
         goto fail;
 
-    m->osdep.list = LIST_IFACE;
-
     return 0;
 
 fail:
@@ -258,9 +378,14 @@ void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) {
 
 void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
     assert(m);
-    
-    while (m->osdep.list != LIST_DONE) {
-        if (!avahi_netlink_work(m->osdep.netlink, 1))
+
+    /* Let's handle netlink events until we are done with wild
+     * dumping */
+
+    while (!m->list_complete)
+        if (!avahi_netlink_work(m->osdep.netlink, 1) == 0)
             break;
-    } 
+
+    /* At this point Avahi knows about all local interfaces and
+     * addresses in existance. */
 }