]> git.ipfire.org Git - thirdparty/lldpd.git/blobdiff - src/daemon/interfaces-linux.c
interfaces: switch to Netlink to gather interface-related information
[thirdparty/lldpd.git] / src / daemon / interfaces-linux.c
similarity index 75%
rename from src/daemon/interfaces.c
rename to src/daemon/interfaces-linux.c
index 7978d5260bdfc60c0ce53085ac84950e71e17193..6c99d8778d2ea12e0b522922e953bf0d70eb14ec 100644 (file)
@@ -15,7 +15,6 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define INCLUDE_LINUX_IF_H
 #include "lldpd.h"
 
 #include <stdio.h>
@@ -37,6 +36,7 @@
 #include <linux/sockios.h>
 #include <linux/filter.h>
 #include <linux/if_packet.h>
+#include <linux/ethtool.h>
 
 #define SYSFS_PATH_MAX 256
 #define MAX_PORTS 1024
 
 static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F };
 
-/* net/if.h */
-extern unsigned int if_nametoindex (__const char *__ifname) __THROW;
-extern char *if_indextoname (unsigned int __ifindex, char *__ifname) __THROW;
-
 struct lldpd_ops eth_ops;
 struct lldpd_ops bond_ops;
 
@@ -122,7 +118,39 @@ pattern_match(char *iface, char *list, int found)
 }
 
 static int
-old_iface_is_bridge(struct lldpd *cfg, const char *name)
+iface_nametoindex(struct netlink_interface_list *interfaces,
+    const char *device)
+{
+       struct netlink_interface *iface;
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (!strcmp(iface->name, device))
+                       return iface->index;
+       }
+       log_debug("interfaces", "cannot get interface index for %s",
+           device);
+       return 0;
+}
+
+static int
+iface_indextoname(struct netlink_interface_list *interfaces,
+    int index, char *name)
+{
+       struct netlink_interface *iface;
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface->index == index) {
+                       strncpy(name, iface->name, IFNAMSIZ);
+                       return 0;
+               }
+       }
+       log_debug("interfaces", "cannot get interface name for index %d",
+           index);
+       return -1;
+}
+
+static int
+old_iface_is_bridge(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    const char *name)
 {
        int ifindices[MAX_BRIDGES];
        char ifname[IFNAMSIZ];
@@ -135,7 +163,7 @@ old_iface_is_bridge(struct lldpd *cfg, const char *name)
                   to fill logs. */
                return 0;
        for (i = 0; i < num; i++) {
-               if (if_indextoname(ifindices[i], ifname) == NULL)
+               if (iface_indextoname(interfaces, ifindices[i], ifname) == -1)
                        log_info("interfaces", "unable to get name of interface %d",
                            ifindices[i]);
                else if (strncmp(name, ifname, IFNAMSIZ) == 0)
@@ -145,7 +173,9 @@ old_iface_is_bridge(struct lldpd *cfg, const char *name)
 }
 
 static int
-iface_is_bridge(struct lldpd *cfg, const char *name)
+iface_is_bridge(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    const char *name)
 {
 #ifdef SYSFS_BRIDGE_FDB
        char path[SYSFS_PATH_MAX];
@@ -155,24 +185,27 @@ iface_is_bridge(struct lldpd *cfg, const char *name)
                    SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB, name)) >= SYSFS_PATH_MAX)
                log_warnx("interfaces", "path truncated");
        if ((f = priv_open(path)) < 0) {
-               return old_iface_is_bridge(cfg, name);
+               return old_iface_is_bridge(cfg, interfaces, name);
        }
        close(f);
        return 1;
 #else
-       return old_iface_is_bridge(cfg, name);
+       return old_iface_is_bridge(cfg, interfaces, name);
 #endif
 }
 
 #ifdef ENABLE_DOT1
 static int
-old_iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master)
+old_iface_is_bridged_to(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    const char *slave, const char *master)
 {
-       int j, index = if_nametoindex(slave);
+       int j, index = iface_nametoindex(interfaces, slave);
        int ifptindices[MAX_PORTS];
        unsigned long args2[4] = { BRCTL_GET_PORT_LIST,
                                   (unsigned long)ifptindices, MAX_PORTS, 0 };
        struct ifreq ifr;
+       if (index == 0) return 0;
 
        strncpy(ifr.ifr_name, master, IFNAMSIZ);
        memset(ifptindices, 0, sizeof(ifptindices));
@@ -193,26 +226,28 @@ old_iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master
 }
 
 static int
-iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master)
+iface_is_bridged_to(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    const char *slave, const char *master)
 {
 #ifdef SYSFS_BRIDGE_PORT_SUBDIR
        char path[SYSFS_PATH_MAX];
        int f;
 
        /* Master should be a bridge, first */
-       if (!iface_is_bridge(cfg, master)) return 0;
+       if (!iface_is_bridge(cfg, interfaces, master)) return 0;
 
        if (snprintf(path, SYSFS_PATH_MAX,
                SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_SUBDIR "/%s/port_no",
                master, slave) >= SYSFS_PATH_MAX)
                log_warnx("interfaces", "path truncated");
        if ((f = priv_open(path)) < 0) {
-               return old_iface_is_bridged_to(cfg, slave, master);
+               return old_iface_is_bridged_to(cfg, interfaces, slave, master);
        }
        close(f);
        return 1;
 #else
-       return old_iface_is_bridged_to(cfg, slave, master);
+       return old_iface_is_bridged_to(cfg, interfaces, slave, master);
 #endif
 }
 #endif
@@ -285,28 +320,26 @@ iface_is_bond_slave(struct lldpd *cfg, const char *slave, const char *master,
 }
 
 static int
-iface_is_enslaved(struct lldpd *cfg, const char *name)
+iface_is_enslaved(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    const char *name)
 {
-       struct ifaddrs *ifap, *ifa;
+       struct netlink_interface *iface;
        int master;
 
-       if (getifaddrs(&ifap) != 0) {
-               log_warn("interfaces", "unable to get interface list");
-               return -1;
-       }
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (iface_is_bond_slave(cfg, name, ifa->ifa_name, NULL)) {
-                       master = if_nametoindex(ifa->ifa_name);
-                       freeifaddrs(ifap);
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface_is_bond_slave(cfg, name, iface->name, NULL)) {
+                       master = iface->index;
                        return master;
                }
        }
-       freeifaddrs(ifap);
        return -1;
 }
 
 static void
-iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware)
+iface_get_permanent_mac(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    struct lldpd_hardware *hardware)
 {
        int master, f, state = 0;
        FILE *netbond;
@@ -320,10 +353,11 @@ iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware)
        log_debug("interfaces", "get MAC address for %s",
            hardware->h_ifname);
 
-       if ((master = iface_is_enslaved(cfg, hardware->h_ifname)) == -1)
+       if ((master = iface_is_enslaved(cfg, interfaces,
+                   hardware->h_ifname)) == -1)
                return;
        /* We have a bond, we need to query it to get real MAC addresses */
-       if ((if_indextoname(master, bond)) == NULL) {
+       if ((iface_indextoname(interfaces, master, bond)) == -1) {
                log_warnx("interfaces", "unable to get bond name");
                return;
        }
@@ -399,9 +433,10 @@ iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware)
 
 /* Generic minimal checks to handle a given interface. */
 static int
-iface_minimal_checks(struct lldpd *cfg, struct ifaddrs *ifa)
+iface_minimal_checks(struct lldpd *cfg,
+    struct netlink_interface_list *interfaces,
+    struct netlink_interface *iface)
 {
-       struct sockaddr_ll sdl;
        struct ifreq ifr;
        struct ethtool_drvinfo ethc;
        const char * const *rif;
@@ -413,57 +448,53 @@ iface_minimal_checks(struct lldpd *cfg, struct ifaddrs *ifa)
                NULL
        };
 
-       int is_bridge = iface_is_bridge(cfg, ifa->ifa_name);
+       int is_bridge = iface_is_bridge(cfg, interfaces, iface->name);
 
-       log_debug("interfaces", "minimal checks for %s", ifa->ifa_name);
+       log_debug("interfaces", "minimal checks for %s", iface->name);
 
        if (!(LOCAL_CHASSIS(cfg)->c_cap_enabled & LLDP_CAP_BRIDGE) &&
            is_bridge) {
                log_debug("interfaces", "skip %s: is a bridge",
-                   ifa->ifa_name);
+                   iface->name);
                LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE;
                return 0;
        }
 
        if (!(LOCAL_CHASSIS(cfg)->c_cap_enabled & LLDP_CAP_WLAN) &&
-           iface_is_wireless(cfg, ifa->ifa_name))
+           iface_is_wireless(cfg, iface->name))
                LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
 
        /* First, check if this interface has already been handled */
-       if (!ifa->ifa_flags) {
+       if (!iface->flags) {
                log_debug("interfaces", "skip %s: already been handled",
-                   ifa->ifa_name);
+                   iface->name);
                return 0;
        }
 
-       if (ifa->ifa_addr == NULL) {
-               if (ifa->ifa_addr->sa_family != PF_PACKET) {
-                       log_debug("interfaces", "skip %s: address family is %d and not PF_PACKET",
-                           ifa->ifa_name, ifa->ifa_addr->sa_family);
-                       return 0;
-               }
-               log_debug("interfaces", "skip %s: no address",
-                   ifa->ifa_name);
-       }
-
-       memcpy(&sdl, ifa->ifa_addr, sizeof(struct sockaddr_ll));
-       if (sdl.sll_hatype != ARPHRD_ETHER || !sdl.sll_halen) {
+       if (iface->type != ARPHRD_ETHER) {
                log_debug("interfaces", "skip %s: not an Ethernet device",
-                   ifa->ifa_name);
+                   iface->name);
                return 0;
        }
 
        /* We request that the interface is able to do either multicast
         * or broadcast to be able to send discovery frames. */
-       if (!(ifa->ifa_flags & (IFF_MULTICAST|IFF_BROADCAST))) {
+       if (!(iface->flags & (IFF_MULTICAST|IFF_BROADCAST))) {
                log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
-                   ifa->ifa_name);
+                   iface->name);
+               return 0;
+       }
+
+       /* If the interface is linked to another one, skip it too. */
+       if (iface->link != -1 && iface->index != iface->link) {
+               log_debug("interfaces", "skip %s: there is a lower interface (%d)",
+                   iface->name, iface->link);
                return 0;
        }
 
        /* Check if the driver is whitelisted */
        memset(&ifr, 0, sizeof(ifr));
-       strcpy(ifr.ifr_name, ifa->ifa_name);
+       strcpy(ifr.ifr_name, iface->name);
        memset(&ethc, 0, sizeof(ethc));
        ifr.ifr_data = (caddr_t) &ethc;
        ethc.cmd = ETHTOOL_GDRVINFO;
@@ -472,43 +503,41 @@ iface_minimal_checks(struct lldpd *cfg, struct ifaddrs *ifa)
                        if (strcmp(ethc.driver, *rif) == 0) {
                                /* White listed! */
                                log_debug("interfaces", "accept %s: whitelisted",
-                                   ifa->ifa_name);
+                                   iface->name);
                                return 1;
                        }
                }
        }
-       log_debug("interfaces", "keep %s: not whitelisted",
-           ifa->ifa_name);
+       log_debug("interfaces", "keep checking %s: not whitelisted",
+           iface->name);
 
        /* Check queue len. If no queue, this usually means that this
           is not a "real" interface. */
-       memset(&ifr, 0, sizeof(ifr));
-       strcpy(ifr.ifr_name, ifa->ifa_name);
-       if ((ioctl(cfg->g_sock, SIOCGIFTXQLEN, &ifr) < 0) || !ifr.ifr_qlen) {
+       if (iface->txqueue == 0) {
                log_debug("interfaces", "skip %s: no queue",
-                   ifa->ifa_name);
+                   iface->name);
                return 0;
        }
 
        /* Don't handle bond and VLAN, nor bridge  */
-       if (iface_is_vlan(cfg, ifa->ifa_name)) {
+       if (iface_is_vlan(cfg, iface->name)) {
                log_debug("interfaces", "skip %s: is a VLAN",
-                   ifa->ifa_name);
+                   iface->name);
                return 0;
        }
-       if (iface_is_bond(cfg, ifa->ifa_name)) {
+       if (iface_is_bond(cfg, iface->name)) {
                log_debug("interfaces", "skip %s: is a bond",
-                   ifa->ifa_name);
+                   iface->name);
                return 0;
        }
        if (is_bridge) {
                log_debug("interfaces", "skip %s: is a bridge",
-                   ifa->ifa_name);
+                   iface->name);
                return 0;
        }
 
        log_debug("interfaces", "%s passes the minimal checks",
-           ifa->ifa_name);
+           iface->name);
        return 1;
 }
 
@@ -532,12 +561,9 @@ iface_set_filter(const char *name, int fd)
 
 /* Fill up port name and description */
 static void
-iface_port_name_desc(struct lldpd_hardware *hardware)
+iface_port_name_desc(struct lldpd_hardware *hardware, struct netlink_interface *iface)
 {
        struct lldpd_port *port = &hardware->h_lport;
-       char buffer[256];       /* 256 = IFALIASZ */
-       char path[SYSFS_PATH_MAX];
-       int f;
 
        /* There are two cases:
 
@@ -549,13 +575,8 @@ iface_port_name_desc(struct lldpd_hardware *hardware)
             port name in description.
        */
 
-       if ((snprintf(path, SYSFS_PATH_MAX,
-                   SYSFS_CLASS_NET "%s/ifalias", hardware->h_ifname)) >= SYSFS_PATH_MAX)
-               log_warnx("interfaces", "path truncated");
-       memset(buffer, 0, sizeof(buffer));
-       if (((f = priv_open(path)) < 0) || (read(f, buffer, sizeof(buffer)-1) < 1)) {
+       if (iface->alias == NULL || strlen(iface->alias) == 0) {
                /* Case 2: MAC address and port name */
-               close(f);
                log_debug("interfaces", "use ifname and MAC address for %s",
                    hardware->h_ifname);
                port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
@@ -569,7 +590,6 @@ iface_port_name_desc(struct lldpd_hardware *hardware)
                return;
        }
        /* Case 1: port name and port description */
-       close(f);
        log_debug("interfaces", "use ifname and ifalias for %s",
            hardware->h_ifname);
        port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
@@ -578,9 +598,7 @@ iface_port_name_desc(struct lldpd_hardware *hardware)
                calloc(1, port->p_id_len)) == NULL)
                fatal("interfaces", NULL);
        memcpy(port->p_id, hardware->h_ifname, port->p_id_len);
-       if (buffer[strlen(buffer) - 1] == '\n')
-               buffer[strlen(buffer) - 1] = '\0';
-       port->p_descr = strdup(buffer);
+       port->p_descr = strdup(iface->alias);
 }
 
 /* Fill up MAC/PHY for a given hardware port */
@@ -606,7 +624,7 @@ iface_macphy(struct lldpd_hardware *hardware)
 
        log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
            hardware->h_ifname);
-       if (priv_ethtool(hardware->h_ifname, &ethc) == 0) {
+       if (priv_ethtool(hardware->h_ifname, &ethc, sizeof(struct ethtool_cmd)) == 0) {
                port->p_macphy.autoneg_support = (ethc.supported & SUPPORTED_Autoneg) ? 1 : 0;
                port->p_macphy.autoneg_enabled = (ethc.autoneg == AUTONEG_DISABLE) ? 0 : 1;
                for (j=0; advertised_ethtool_to_rfc3636[j][0]; j++) {
@@ -650,21 +668,6 @@ iface_macphy(struct lldpd_hardware *hardware)
 #endif
 }
 
-static void
-iface_mtu(struct lldpd *cfg, struct lldpd_hardware *hardware)
-{
-       struct ifreq ifr;
-
-       /* get MTU */
-       memset(&ifr, 0, sizeof(ifr));
-       strlcpy(ifr.ifr_name, hardware->h_ifname, sizeof(ifr.ifr_name));
-       if (ioctl(cfg->g_sock, SIOCGIFMTU, (char*)&ifr) == -1) {
-               log_warn("interfaces", "unable to get MTU of %s, using 1500", hardware->h_ifname);
-               hardware->h_mtu = 1500;
-       } else
-               hardware->h_mtu = ifr.ifr_mtu;
-}
-
 static void
 iface_multicast(struct lldpd *cfg, const char *name, int remove)
 {
@@ -691,7 +694,7 @@ iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
 
        log_debug("interfaces", "initialize ethernet device %s",
            hardware->h_ifname);
-       if ((fd = priv_iface_init(hardware->h_ifname)) == -1)
+       if ((fd = priv_iface_init(hardware->h_ifindex)) == -1)
                return -1;
        hardware->h_sendfd = fd; /* Send */
 
@@ -713,8 +716,8 @@ static int
 iface_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware,
     char *buffer, size_t size)
 {
-       log_debug("interfaces", "send PDU to ethernet device %s",
-           hardware->h_ifname);
+       log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)",
+           hardware->h_ifname, hardware->h_sendfd);
        return write(hardware->h_sendfd,
            buffer, size);
 }
@@ -754,26 +757,29 @@ iface_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
        return 0;
 }
 
-void
-lldpd_ifh_eth(struct lldpd *cfg, struct ifaddrs *ifap)
+static void
+lldpd_ifh_eth(struct lldpd *cfg, struct netlink_interface_list *interfaces)
 {
-       struct ifaddrs *ifa;
+       struct netlink_interface *iface;
        struct lldpd_hardware *hardware;
 
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (!iface_minimal_checks(cfg, ifa))
+       TAILQ_FOREACH(iface, interfaces, next) {
+               log_debug("interfaces", "check if %s is a real ethernet device",
+                   iface->name);
+               if (!iface_minimal_checks(cfg, interfaces, iface))
                        continue;
 
                log_debug("interfaces", "%s is an acceptable ethernet device",
-                   ifa->ifa_name);
+                   iface->name);
                if ((hardware = lldpd_get_hardware(cfg,
-                           ifa->ifa_name,
-                           if_nametoindex(ifa->ifa_name),
+                           iface->name,
+                           iface->index,
                            &eth_ops)) == NULL) {
                        if  ((hardware = lldpd_alloc_hardware(cfg,
-                                   ifa->ifa_name)) == NULL) {
+                                   iface->name,
+                                   iface->index)) == NULL) {
                                log_warnx("interfaces", "Unable to allocate space for %s",
-                                   ifa->ifa_name);
+                                   iface->name);
                                continue;
                        }
                        if (iface_eth_init(cfg, hardware) != 0) {
@@ -782,66 +788,67 @@ lldpd_ifh_eth(struct lldpd *cfg, struct ifaddrs *ifap)
                                continue;
                        }
                        hardware->h_ops = &eth_ops;
-                       hardware->h_ifindex = if_nametoindex(ifa->ifa_name);
                        TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
                } else {
                        if (hardware->h_flags) continue; /* Already seen this time */
                        lldpd_port_cleanup(&hardware->h_lport, 0);
                }
 
-               hardware->h_flags = ifa->ifa_flags; /* Should be non-zero */
-               ifa->ifa_flags = 0;                 /* Future handlers
+               hardware->h_flags = iface->flags;   /* Should be non-zero */
+               iface->flags = 0;                   /* Future handlers
                                                       don't have to
                                                       care about this
                                                       interface. */
 
                /* Get local address */
-               memcpy(&hardware->h_lladdr,
-                   (u_int8_t*)((u_int8_t*)ifa->ifa_addr +
-                       offsetof(struct sockaddr_ll, sll_addr)),
-                   sizeof(hardware->h_lladdr));
+               memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
 
                /* Fill information about port */
-               iface_port_name_desc(hardware);
+               iface_port_name_desc(hardware, iface);
 
                /* Fill additional info */
                iface_macphy(hardware);
-               iface_mtu(cfg, hardware);
+               hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
        }
 }
 
-void
-lldpd_ifh_whitelist(struct lldpd *cfg, struct ifaddrs *ifap)
+static void
+lldpd_ifh_whitelist(struct lldpd *cfg, struct netlink_interface_list *interfaces)
 {
-       struct ifaddrs *ifa;
+       struct netlink_interface *iface;
 
        if (!cfg->g_config.c_iface_pattern)
                return;
 
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (ifa->ifa_flags == 0) continue; /* Already handled by someone else */
-               if (!pattern_match(ifa->ifa_name, cfg->g_config.c_iface_pattern, 0)) {
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface->flags == 0) continue; /* Already handled by someone else */
+               if (!pattern_match(iface->name, cfg->g_config.c_iface_pattern, 0)) {
                        /* This interface was not found. We flag it. */
-                       log_debug("interfaces", "blacklist %s", ifa->ifa_name);
-                       ifa->ifa_flags = 0;
+                       log_debug("interfaces", "blacklist %s", iface->name);
+                       iface->flags = 0;
                }
        }
 }
 
+struct bond_master {
+       char name[IFNAMSIZ];
+       int  index;
+};
+
 static int
 iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
-       char *mastername = (char *)hardware->h_data; /* Master name */
+       struct bond_master *master = hardware->h_data;
        int fd, status;
        int un = 1;
 
-       if (!mastername) return -1;
+       if (!master) return -1;
 
        log_debug("interfaces", "initialize bonded device %s",
            hardware->h_ifname);
 
        /* First, we get a socket to the raw physical interface */
-       if ((fd = priv_iface_init(hardware->h_ifname)) == -1)
+       if ((fd = priv_iface_init(hardware->h_ifindex)) == -1)
                return -1;
        hardware->h_sendfd = fd;
        if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) {
@@ -851,11 +858,11 @@ iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
        iface_multicast(cfg, hardware->h_ifname, 0);
 
        /* Then, we open a raw interface for the master */
-       if ((fd = priv_iface_init(mastername)) == -1) {
+       if ((fd = priv_iface_init(master->index)) == -1) {
                close(hardware->h_sendfd);
                return -1;
        }
-       if ((status = iface_set_filter(mastername, fd)) != 0) {
+       if ((status = iface_set_filter(master->name, fd)) != 0) {
                close(hardware->h_sendfd);
                close(fd);
                return status;
@@ -865,19 +872,19 @@ iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
         * physical device instead of bond device (works with >=
         * 2.6.24). */
        if (setsockopt(fd, SOL_PACKET,
-                      PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
-               log_debug("interfaces", "unable to setsockopt for master bonding device of %s. "
-                          "You will get inaccurate results",
-                          hardware->h_ifname);
+               PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
+               log_info("interfaces", "unable to setsockopt for master bonding device of %s. "
+                   "You will get inaccurate results",
+                   hardware->h_ifname);
        }
-       iface_multicast(cfg, mastername, 0);
+       iface_multicast(cfg, master->name, 0);
 
        levent_hardware_add_fd(hardware, hardware->h_sendfd);
        levent_hardware_add_fd(hardware, fd);
        log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
            hardware->h_ifname,
            hardware->h_sendfd,
-           mastername, fd);
+           master->name, fd);
        return 0;
 }
 
@@ -888,11 +895,11 @@ iface_bond_send(struct lldpd *cfg, struct lldpd_hardware *hardware,
        /* With bonds, we have duplicate MAC address on different physical
         * interfaces. We need to alter the source MAC address when we send on
         * an inactive slave. */
-       char *master = (char*)hardware->h_data;
+       struct bond_master *master = hardware->h_data;
        int active;
        log_debug("interfaces", "send PDU to bonded device %s",
            hardware->h_ifname);
-       if (!iface_is_bond_slave(cfg, hardware->h_ifname, master, &active)) {
+       if (!iface_is_bond_slave(cfg, hardware->h_ifname, master->name, &active)) {
                log_warnx("interfaces", "%s seems to not be enslaved anymore?",
                    hardware->h_ifname);
                return 0;
@@ -917,6 +924,7 @@ iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
        int n;
        struct sockaddr_ll from;
        socklen_t fromlen;
+       struct bond_master *master = hardware->h_data;
 
        log_debug("interfaces", "receive PDU from bonded device %s",
            hardware->h_ifname);
@@ -938,7 +946,7 @@ iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
        if (from.sll_ifindex == hardware->h_ifindex)
                /* This is for us */
                return n;
-       if (from.sll_ifindex == if_nametoindex((char*)hardware->h_data))
+       if (from.sll_ifindex == master->index)
                /* We don't know from which physical interface it comes (kernel
                 * < 2.6.24). In doubt, this is for us. */
                return n;
@@ -948,40 +956,55 @@ iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
 static int
 iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
 {
+       struct bond_master *master = hardware->h_data;
        log_debug("interfaces", "closing bonded device %s",
            hardware->h_ifname);
        iface_multicast(cfg, hardware->h_ifname, 1);
-       iface_multicast(cfg, (char*)hardware->h_data, 1);
+       iface_multicast(cfg, master->name, 1);
        free(hardware->h_data);
        return 0;
 }
 
-void
-lldpd_ifh_bond(struct lldpd *cfg, struct ifaddrs *ifap)
+static void
+lldpd_ifh_bond(struct lldpd *cfg, struct netlink_interface_list *interfaces)
 {
-       struct ifaddrs *ifa;
+       struct netlink_interface *iface;
        struct lldpd_hardware *hardware;
-       int master;
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (!iface_minimal_checks(cfg, ifa))
+       struct bond_master *master;
+       int idx;
+       TAILQ_FOREACH(iface, interfaces, next) {
+               log_debug("interfaces", "check if %s is part of a bond",
+                   iface->name);
+               if (!iface_minimal_checks(cfg, interfaces, iface))
                        continue;
-               if ((master = iface_is_enslaved(cfg, ifa->ifa_name)) == -1)
+               if ((idx = iface_is_enslaved(cfg, interfaces,
+                           iface->name)) == -1)
                        continue;
 
                log_debug("interfaces", "%s is an acceptable bonded device (master=%d)",
-                   ifa->ifa_name, master);
+                   iface->name, idx);
                if ((hardware = lldpd_get_hardware(cfg,
-                           ifa->ifa_name,
-                           if_nametoindex(ifa->ifa_name),
+                           iface->name,
+                           iface->index,
                            &bond_ops)) == NULL) {
                        if  ((hardware = lldpd_alloc_hardware(cfg,
-                                   ifa->ifa_name)) == NULL) {
+                                   iface->name,
+                                   iface->index)) == NULL) {
                                log_warnx("interfaces", "Unable to allocate space for %s",
-                                   ifa->ifa_name);
+                                   iface->name);
+                               continue;
+                       }
+                       hardware->h_data = master = calloc(1, sizeof(struct bond_master));
+                       if (!hardware->h_data) {
+                               log_warn("interfaces", "not enough memory");
+                               lldpd_hardware_cleanup(cfg, hardware);
+                               continue;
+                       }
+                       master->index = idx;
+                       if (iface_indextoname(interfaces, master->index, master->name) == -1) {
+                               lldpd_hardware_cleanup(cfg, hardware);
                                continue;
                        }
-                       hardware->h_data = (char *)calloc(1, IFNAMSIZ);
-                       if_indextoname(master, hardware->h_data);
                        if (iface_bond_init(cfg, hardware) != 0) {
                                log_warn("interfaces", "unable to initialize %s",
                                    hardware->h_ifname);
@@ -989,37 +1012,38 @@ lldpd_ifh_bond(struct lldpd *cfg, struct ifaddrs *ifap)
                                continue;
                        }
                        hardware->h_ops = &bond_ops;
-                       hardware->h_ifindex = if_nametoindex(ifa->ifa_name);
                        TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
                } else {
                        if (hardware->h_flags) continue; /* Already seen this time */
-                       memset(hardware->h_data, 0, IFNAMSIZ);
-                       if_indextoname(master, hardware->h_data);
+                       master = hardware->h_data;
+                       memset(hardware->h_data, 0, sizeof(struct bond_master));
+                       master->index = idx;
+                       iface_indextoname(interfaces, master->index, master->name);
                        lldpd_port_cleanup(&hardware->h_lport, 0);
                }
-               
-               hardware->h_flags = ifa->ifa_flags;
-               ifa->ifa_flags = 0;
+
+               hardware->h_flags = iface->flags;
+               iface->flags = 0;
 
                /* Get local address */
-               iface_get_permanent_mac(cfg, hardware);
-               
+               iface_get_permanent_mac(cfg, interfaces, hardware);
+
                /* Fill information about port */
-               iface_port_name_desc(hardware);
-               
+               iface_port_name_desc(hardware, iface);
+
                /* Fill additional info */
 #ifdef ENABLE_DOT3
-               hardware->h_lport.p_aggregid = master;
+               hardware->h_lport.p_aggregid = master->index;
 #endif
                iface_macphy(hardware);
-               iface_mtu(cfg, hardware);
+               hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
        }
 }
 
 #ifdef ENABLE_DOT1
 static void
 iface_append_vlan(struct lldpd *cfg,
-    struct lldpd_hardware *hardware, struct ifaddrs *ifa)
+    struct lldpd_hardware *hardware, struct netlink_interface *iface)
 {
        struct lldpd_port *port = &hardware->h_lport;
        struct lldpd_vlan *vlan;
@@ -1027,18 +1051,18 @@ iface_append_vlan(struct lldpd *cfg,
 
        /* Check if the VLAN is already here. */
        TAILQ_FOREACH(vlan, &port->p_vlans, v_entries)
-           if (strncmp(ifa->ifa_name, vlan->v_name, IFNAMSIZ) == 0)
+           if (strncmp(iface->name, vlan->v_name, IFNAMSIZ) == 0)
                    return;
        if ((vlan = (struct lldpd_vlan *)
                calloc(1, sizeof(struct lldpd_vlan))) == NULL)
                return;
-       if ((vlan->v_name = strdup(ifa->ifa_name)) == NULL) {
+       if ((vlan->v_name = strdup(iface->name)) == NULL) {
                free(vlan);
                return;
        }
        memset(&ifv, 0, sizeof(ifv));
        ifv.cmd = GET_VLAN_VID_CMD;
-       strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1));
+       strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
        if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) {
                /* Dunno what happened */
                free(vlan->v_name);
@@ -1052,35 +1076,38 @@ iface_append_vlan(struct lldpd *cfg,
        TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries);
 }
 
-void
-lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
+static void
+lldpd_ifh_vlan(struct lldpd *cfg, struct netlink_interface_list *interfaces)
 {
-       struct ifaddrs *ifa;
+       struct netlink_interface *iface;
        struct vlan_ioctl_args ifv;
        struct lldpd_hardware *hardware;
-       
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (!ifa->ifa_flags)
+
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (!iface->flags)
                        continue;
-               if (!iface_is_vlan(cfg, ifa->ifa_name))
+               if (!iface_is_vlan(cfg, iface->name))
                        continue;
 
                /* We need to find the physical interfaces of this
                   vlan, through bonds and bridges. */
+               /* TODO: use iflink instead? Possible since 2.6.17 only. */
                log_debug("interfaces", "search physical interface for VLAN interface %s",
-                   ifa->ifa_name);
+                   iface->name);
                memset(&ifv, 0, sizeof(ifv));
                ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
-               strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1));
+               strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
                if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) {
                        /* Three cases:
                            1. we get a real device
                            2. we get a bond
                            3. we get a bridge
                        */
+                       int idx = iface_nametoindex(interfaces, ifv.u.device2);
+                       if (idx == 0) continue;
                        if ((hardware = lldpd_get_hardware(cfg,
                                    ifv.u.device2,
-                                   if_nametoindex(ifv.u.device2),
+                                   idx,
                                    NULL)) == NULL) {
                                if (iface_is_bond(cfg, ifv.u.device2)) {
                                        TAILQ_FOREACH(hardware, &cfg->g_hardware,
@@ -1093,12 +1120,13 @@ lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
                                                        hardware->h_ifname,
                                                        ifv.u.device2);
                                                    iface_append_vlan(cfg,
-                                                       hardware, ifa);
+                                                       hardware, iface);
                                            }
-                               } else if (iface_is_bridge(cfg, ifv.u.device2)) {
+                               } else if (iface_is_bridge(cfg, interfaces, ifv.u.device2)) {
                                        TAILQ_FOREACH(hardware, &cfg->g_hardware,
                                            h_entries)
                                            if (iface_is_bridged_to(cfg,
+                                                   interfaces,
                                                    hardware->h_ifname,
                                                    ifv.u.device2)) {
                                                    log_debug("interfaces",
@@ -1106,11 +1134,11 @@ lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
                                                        hardware->h_ifname,
                                                        ifv.u.device2);
                                                    iface_append_vlan(cfg,
-                                                       hardware, ifa);
+                                                       hardware, iface);
                                            }
                                }
                        } else iface_append_vlan(cfg,
-                           hardware, ifa);
+                           hardware, iface);
                }
        }
 }
@@ -1123,18 +1151,18 @@ lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
 #define IN_IS_ADDR_GLOBAL(a) (!IN_IS_ADDR_LOOPBACK(a))
 #endif
 #ifndef IN6_IS_ADDR_GLOBAL
-#define IN6_IS_ADDR_GLOBAL(a) (!IN6_IS_ADDR_LOOPBACK(a) && \
-                                                               !IN6_IS_ADDR_LINKLOCAL(a))
+#define IN6_IS_ADDR_GLOBAL(a) \
+       (!IN6_IS_ADDR_LOOPBACK(a) && !IN6_IS_ADDR_LINKLOCAL(a))
 #endif
 
 /* Find a management address in all available interfaces, even those that were
    already handled. This is a special interface handler because it does not
    really handle interface related information (management address is attached
    to the local chassis). */
-void
-lldpd_ifh_mgmt(struct lldpd *cfg, struct ifaddrs *ifap)
+static void
+lldpd_ifh_mgmt(struct lldpd *cfg, struct netlink_address_list *addrs)
 {
-       struct ifaddrs *ifa;
+       struct netlink_address *addr;
        char addrstrbuf[INET6_ADDRSTRLEN];
        struct lldpd_mgmt *mgmt;
        void *sin_addr_ptr;
@@ -1162,23 +1190,19 @@ lldpd_ifh_mgmt(struct lldpd *cfg, struct ifaddrs *ifap)
                   example !*:*,!10.* will only blacklist
                   addresses. We will pick the first IPv4 address not
                   matching 10.*. */
-               for (ifa = ifap;
-                    ifa != NULL;
-                    ifa = ifa->ifa_next) {
-                       if (ifa->ifa_addr == NULL)
-                               continue;
-                       if (ifa->ifa_addr->sa_family != lldpd_af(af))
+               TAILQ_FOREACH(addr, addrs, next) {
+                       if (addr->address.ss_family != lldpd_af(af))
                                continue;
 
                        switch (af) {
                        case LLDPD_AF_IPV4:
-                               sin_addr_ptr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
+                               sin_addr_ptr = &((struct sockaddr_in *)&addr->address)->sin_addr;
                                sin_addr_size = sizeof(struct in_addr);
                                if (!IN_IS_ADDR_GLOBAL((struct in_addr *)sin_addr_ptr))
                                        continue;
                                break;
                        case LLDPD_AF_IPV6:
-                               sin_addr_ptr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
+                               sin_addr_ptr = &((struct sockaddr_in6 *)&addr->address)->sin6_addr;
                                sin_addr_size = sizeof(struct in6_addr);
                                if (!IN6_IS_ADDR_GLOBAL((struct in6_addr *)sin_addr_ptr))
                                        continue;
@@ -1188,14 +1212,14 @@ lldpd_ifh_mgmt(struct lldpd *cfg, struct ifaddrs *ifap)
                                continue;
                        }
                        if (inet_ntop(lldpd_af(af), sin_addr_ptr,
-                                     addrstrbuf, sizeof(addrstrbuf)) == NULL) {
+                               addrstrbuf, sizeof(addrstrbuf)) == NULL) {
                                log_warn("interfaces", "unable to convert IP address to a string");
                                continue;
                        }
                        if (cfg->g_config.c_mgmt_pattern == NULL ||
                            pattern_match(addrstrbuf, cfg->g_config.c_mgmt_pattern, allnegative)) {
                                mgmt = lldpd_alloc_mgmt(af, sin_addr_ptr, sin_addr_size,
-                                                       if_nametoindex(ifa->ifa_name));
+                                                       addr->index);
                                if (mgmt == NULL) {
                                        assert(errno == ENOMEM); /* anything else is a bug */
                                        log_warn("interfaces", "out of memory error");
@@ -1214,10 +1238,10 @@ lldpd_ifh_mgmt(struct lldpd *cfg, struct ifaddrs *ifap)
 
 /* Fill out chassis ID if not already done. This handler is special
    because we will only handle interfaces that are already handled. */
-void
-lldpd_ifh_chassis(struct lldpd *cfg, struct ifaddrs *ifap)
+static void
+lldpd_ifh_chassis(struct lldpd *cfg, struct netlink_interface_list *interfaces)
 {
-       struct ifaddrs *ifa;
+       struct netlink_interface *iface;
        struct lldpd_hardware *hardware;
        char *name = NULL;
 
@@ -1225,14 +1249,14 @@ lldpd_ifh_chassis(struct lldpd *cfg, struct ifaddrs *ifap)
            LOCAL_CHASSIS(cfg)->c_id_subtype == LLDP_CHASSISID_SUBTYPE_LLADDR)
                return;         /* We already have one */
 
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (ifa->ifa_flags) continue;
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface->flags) continue;
                if (cfg->g_config.c_cid_pattern &&
-                   !pattern_match(ifa->ifa_name, cfg->g_config.c_cid_pattern, 0)) continue;
+                   !pattern_match(iface->name, cfg->g_config.c_cid_pattern, 0)) continue;
 
                if ((hardware = lldpd_get_hardware(cfg,
-                           ifa->ifa_name,
-                           if_nametoindex(ifa->ifa_name),
+                           iface->name,
+                           iface->index,
                            NULL)) == NULL)
                        /* That's odd. Let's skip. */
                        continue;
@@ -1261,3 +1285,31 @@ struct lldpd_ops bond_ops = {
        .recv = iface_bond_recv,
        .cleanup = iface_bond_close,
 };
+
+void
+interfaces_update(struct lldpd *cfg)
+{
+       struct netlink_interface_list *interfaces = NULL;
+       struct netlink_address_list *addresses = NULL;
+       interfaces = netlink_get_interfaces();
+       addresses = netlink_get_addresses();
+       if (interfaces == NULL || addresses == NULL) {
+               log_warnx("interfaces", "cannot update the list of local interfaces");
+               goto end;
+       }
+
+       lldpd_ifh_whitelist(cfg, interfaces);
+       lldpd_ifh_bond(cfg, interfaces);
+       lldpd_ifh_eth(cfg, interfaces);
+#ifdef ENABLE_DOT1
+       lldpd_ifh_vlan(cfg, interfaces);
+#endif
+       lldpd_ifh_mgmt(cfg, addresses);
+       lldpd_ifh_chassis(cfg, interfaces);
+
+end:
+       netlink_free_interfaces(interfaces);
+       netlink_free_addresses(addresses);
+       return;
+
+}