#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;
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 */
(!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:
;
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;
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));
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;
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));
}
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:
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))
+ 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. */
}