diff options
| -rw-r--r-- | avahi-core/iface-linux.c | 108 | ||||
| -rw-r--r-- | avahi-core/netlink.c | 6 | 
2 files changed, 98 insertions, 16 deletions
diff --git a/avahi-core/iface-linux.c b/avahi-core/iface-linux.c index 5e16d6a..64bb0ae 100644 --- a/avahi-core/iface-linux.c +++ b/avahi-core/iface-linux.c @@ -69,7 +69,18 @@ static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdat          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 */ @@ -80,31 +91,36 @@ static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdat              !(ifinfomsg->ifi_flags & IFF_LOOPBACK) &&              (ifinfomsg->ifi_flags & IFF_MULTICAST) &&              !(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;                      memcpy(hw->mac_address, RTA_DATA(a), hw->mac_address_size);                      break; -                }                  default:                      ; @@ -113,7 +129,14 @@ 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) { @@ -122,10 +145,12 @@ static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdat          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) { @@ -137,12 +162,17 @@ static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdat          AvahiAddress raddr;          int raddr_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; +        /* Fill in address family for our new address */          raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family);          l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg)); @@ -152,6 +182,8 @@ static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdat              switch(a->rta_type) {                  case IFA_ADDRESS: +                    /* Fill in address data */ +                                          if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||                          (raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4))                          return; @@ -167,55 +199,91 @@ static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdat              a = RTA_NEXT(a, l);          } -         + +        /* If there was no adress attached to this message, let's quit. */          if (!raddr_valid)              return;          if (n->nlmsg_type == RTM_NEWADDR) {              AvahiInterfaceAddress *addr; -             + +            /* 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, &raddr))) + +                /* Mmm, no object existing yet, so let's create a new one */                  if (!(addr = avahi_interface_address_new(m, i, &raddr, 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;          } else {              AvahiInterfaceAddress *addr; -             +            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, &raddr)))                  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,15 +292,23 @@ 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; +    /* Start the wild dump for the interfaces */      if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0)          goto fail; +    /* Set the initial state. */      m->osdep.list = LIST_IFACE;      return 0; @@ -258,9 +334,15 @@ void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) {  void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {      assert(m); + +    /* Let's handle netlink events until we are done with wild +     * dumping */      while (m->osdep.list != LIST_DONE) { -        if (!avahi_netlink_work(m->osdep.netlink, 1)) +        if (!avahi_netlink_work(m->osdep.netlink, 1) == 0)              break; -    }  +    } + +    /* At this point Avahi knows about all local interfaces and +     * addresses in existance. */  } diff --git a/avahi-core/netlink.c b/avahi-core/netlink.c index ccb4d83..d5d2295 100644 --- a/avahi-core/netlink.c +++ b/avahi-core/netlink.c @@ -56,10 +56,10 @@ int avahi_netlink_work(AvahiNetlink *nl, int block) {              if ((bytes = recv(nl->fd, nl->buffer, nl->buffer_length, block ? 0 : MSG_DONTWAIT)) < 0) {                  if (errno == EAGAIN || errno == EINTR) -                    return 1; +                    return 0;                  avahi_log_error(__FILE__": recv() failed: %s", strerror(errno)); -                return 0; +                return -1;              }              break; @@ -71,7 +71,7 @@ int avahi_netlink_work(AvahiNetlink *nl, int block) {              for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {                  if (!NLMSG_OK(p, (size_t) bytes)) {                      avahi_log_warn(__FILE__": packet truncated"); -                    return 0; +                    return -1;                  }                  nl->callback(nl, p, nl->userdata);  | 
