]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
interfaces: abstraction of interfaces/devices
authorVincent Bernat <bernat@luffy.cx>
Mon, 24 Dec 2012 16:56:01 +0000 (17:56 +0100)
committerVincent Bernat <bernat@luffy.cx>
Mon, 24 Dec 2012 16:56:01 +0000 (17:56 +0100)
To prepare support of additional OS, interfaces have been abstracted
into a proper structure. It is still expected that each OS should have
its own `update_interfaces()` that will discover interfaces and set
the appropriate `lldpd_hardware` structures. However, helper functions
have been setup with the assumption that interfaces are put in an
abstract `interfaces_device_list`. It is expected that
`update_interfaces()` build such a list with all sensible
information (VLAN, bond, bridge, drivers, ...). Once this is done,
`interfaces_helper_*` function can be called to do most of the work.

Another change is that VLAN stuff, bridge stuff and bonding stuff is
discovered only once instead of using functions each time we need to
know something. This should lower the number of ioctl call (which can
be costly on some special hardware, for example with Marvell
DSA). This also enable to discover some of those stuff through
Netlink.

Supporting FreeBSD is the next step.

src/daemon/Makefile.am
src/daemon/interfaces-linux.c
src/daemon/interfaces.c [new file with mode: 0644]
src/daemon/lldpd.h
src/daemon/netlink.c

index 7f91de04cb242f09474e415d4f717a40675126fb..d5eb5055283e49a2793782f7e264e2a7baa8810c 100644 (file)
@@ -15,6 +15,7 @@ liblldpd_la_SOURCES  = \
        client.c \
        priv.c privsep_fdpass.c \
        dmi.c \
+       interfaces.c \
        event.c lldpd.c
 liblldpd_la_CFLAGS   = $(AM_CFLAGS) @LIBEVENT_CFLAGS@
 liblldpd_la_LIBADD   = \
index 9a1c11389fbdb9b81e2145067e099fd795285863..57c86c23ebcfdcb1aef229b6569299b0b0a5ef0f 100644 (file)
 #include "lldpd.h"
 
 #include <stdio.h>
-#include <stddef.h>
 #include <unistd.h>
 #include <errno.h>
-#include <assert.h>
 #include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <fnmatch.h>
-#include <arpa/inet.h>
-#include <net/if_arp.h>
 #include <linux/if_vlan.h>
 #include <linux/if_bonding.h>
 #include <linux/if_bridge.h>
 #define MAX_PORTS 1024
 #define MAX_BRIDGES 1024
 
-/* BPF filter to get revelant information from interfaces */
-/* LLDP: "ether proto 0x88cc and ether dst 01:80:c2:00:00:0e" */
-/* FDP: "ether dst 01:e0:52:cc:cc:cc" */
-/* CDP: "ether dst 01:00:0c:cc:cc:cc" */
-/* SONMP: "ether dst 01:00:81:00:01:00" */
-/* EDP: "ether dst 00:e0:2b:00:00:00" */
-/* For optimization purpose, we first check if the first bit of the
-   first byte is 1. if not, this can only be an EDP packet:
-
-   tcpdump -dd "(ether[0] & 1 = 1 and
-                 ((ether proto 0x88cc and ether dst 01:80:c2:00:00:0e) or
-                  (ether dst 01:e0:52:cc:cc:cc) or
-                  (ether dst 01:00:0c:cc:cc:cc) or
-                  (ether dst 01:00:81:00:01:00))) or
-                (ether dst 00:e0:2b:00:00:00)"
-*/
-
-#define LLDPD_FILTER_F                         \
-       { 0x30, 0, 0, 0x00000000 },             \
-       { 0x54, 0, 0, 0x00000001 },             \
-       { 0x15, 0, 14, 0x00000001 },            \
-       { 0x28, 0, 0, 0x0000000c },             \
-       { 0x15, 0, 4, 0x000088cc },             \
-       { 0x20, 0, 0, 0x00000002 },             \
-       { 0x15, 0, 2, 0xc200000e },             \
-       { 0x28, 0, 0, 0x00000000 },             \
-       { 0x15, 12, 13, 0x00000180 },           \
-       { 0x20, 0, 0, 0x00000002 },             \
-       { 0x15, 0, 2, 0x52cccccc },             \
-       { 0x28, 0, 0, 0x00000000 },             \
-       { 0x15, 8, 9, 0x000001e0 },             \
-       { 0x15, 1, 0, 0x0ccccccc },             \
-       { 0x15, 0, 2, 0x81000100 },             \
-       { 0x28, 0, 0, 0x00000000 },             \
-       { 0x15, 4, 5, 0x00000100 },             \
-       { 0x20, 0, 0, 0x00000002 },             \
-       { 0x15, 0, 3, 0x2b000000 },             \
-       { 0x28, 0, 0, 0x00000000 },             \
-       { 0x15, 0, 1, 0x000000e0 },             \
-       { 0x6, 0, 0, 0x0000ffff },              \
-       { 0x6, 0, 0, 0x00000000 },
-
 static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F };
-
-struct lldpd_ops eth_ops;
-struct lldpd_ops bond_ops;
-
-static int
-pattern_match(char *iface, char *list, int found)
+int
+interfaces_set_filter(const char *name, int fd)
 {
-       char *interfaces = NULL;
-       char *pattern;
-
-       if ((interfaces = strdup(list)) == NULL) {
-               log_warnx("interfaces", "unable to allocate memory");
-               return 0;
-       }
-
-       for (pattern = strtok(interfaces, ",");
-            pattern != NULL;
-            pattern = strtok(NULL, ",")) {
-               if ((pattern[0] == '!') &&
-                   ((fnmatch(pattern + 1, iface, 0) == 0))) {
-                       /* Blacklisted. No need to search further. */
-                       found = 0;
-                       break;
-               }
-               if (fnmatch(pattern, iface, 0) == 0)
-                       found = 1;
-       }
-
-       free(interfaces);
-       return found;
-}
+       struct sock_fprog prog;
+       log_debug("interfaces", "set BPF filter for %s", name);
 
-static struct netlink_interface*
-iface_nametointerface(struct netlink_interface_list *interfaces,
-    const char *device)
-{
-       struct netlink_interface *iface;
-       TAILQ_FOREACH(iface, interfaces, next) {
-               if (!strcmp(iface->name, device))
-                       return iface;
-       }
-       log_debug("interfaces", "cannot get interface for index %s",
-           device);
-       return NULL;
-}
+       memset(&prog, 0, sizeof(struct sock_fprog));
+       prog.filter = lldpd_filter_f;
+       prog.len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter);
 
-static struct netlink_interface*
-iface_indextointerface(struct netlink_interface_list *interfaces,
-    int index)
-{
-       struct netlink_interface *iface;
-       TAILQ_FOREACH(iface, interfaces, next) {
-               if (iface->index == index)
-                       return iface;
+       if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
+                &prog, sizeof(prog)) < 0) {
+               log_info("interfaces", "unable to change filter for %s", name);
+               return ENETDOWN;
        }
-       log_debug("interfaces", "cannot get interface for index %d",
-           index);
-       return NULL;
-}
-
-#ifdef ENABLE_OLDIES
-static int
-iface_indextoname(struct netlink_interface_list *interfaces,
-    int index, char *name)
-{
-       struct netlink_interface *iface =
-           iface_indextointerface(interfaces, index);
-       if (iface == NULL) return -1;
-       strncpy(name, iface->name, IFNAMSIZ);
        return 0;
 }
-#endif
 
 static int
-old_iface_is_bridge(struct lldpd *cfg,
-    struct netlink_interface_list *interfaces,
-    struct netlink_interface *iface)
-{
-#ifdef ENABLE_OLDIES
-       int ifindices[MAX_BRIDGES];
-       char ifname[IFNAMSIZ];
-       int num, i;
-       unsigned long args[3] = { BRCTL_GET_BRIDGES,
-                                 (unsigned long)ifindices, MAX_BRIDGES };
-       if ((num = ioctl(cfg->g_sock, SIOCGIFBR, args)) < 0)
-               /* This can happen with a 64bit kernel and 32bit
-                  userland, don't output anything about this to avoid
-                  to fill logs. */
-               return 0;
-       for (i = 0; i < num; i++) {
-               if (iface_indextoname(interfaces, ifindices[i], ifname) == -1)
-                       log_info("interfaces", "unable to get name of interface %d",
-                           ifindices[i]);
-               else if (strncmp(iface->name, ifname, IFNAMSIZ) == 0)
-                       return 1;
-       }
-#endif
-       return 0;
-}
-
-static int
-iface_is_bridge(struct lldpd *cfg,
-    struct netlink_interface_list *interfaces,
-    struct netlink_interface *iface)
-{
-       char path[SYSFS_PATH_MAX];
-       int f;
-
-       if ((snprintf(path, SYSFS_PATH_MAX,
-                   SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB,
-                   iface->name)) >= SYSFS_PATH_MAX)
-               log_warnx("interfaces", "path truncated");
-       if ((f = priv_open(path)) < 0) {
-               return old_iface_is_bridge(cfg, interfaces, iface);
-       }
-       close(f);
-       return 1;
-}
-
-#ifdef ENABLE_DOT1
-static int
-old_iface_is_bridged_to(struct lldpd *cfg,
-    struct netlink_interface_list *interfaces,
-    struct netlink_interface *slave,
-    struct netlink_interface *master)
+old_iflinux_is_bridge(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces,
+    struct interfaces_device *iface)
 {
 #ifdef ENABLE_OLDIES
        int j;
        int ifptindices[MAX_PORTS];
-       unsigned long args2[4] = { BRCTL_GET_PORT_LIST,
-                                  (unsigned long)ifptindices, MAX_PORTS, 0 };
+       unsigned long args2[4] = {
+               BRCTL_GET_PORT_LIST,
+               (unsigned long)ifptindices,
+               MAX_PORTS,
+               0
+       };
        struct ifreq ifr;
-       if (slave->index == 0) return 0;
 
-       strncpy(ifr.ifr_name, master->name, IFNAMSIZ);
+       strncpy(ifr.ifr_name, iface->name, IFNAMSIZ);
        memset(ifptindices, 0, sizeof(ifptindices));
        ifr.ifr_data = (char *)&args2;
 
@@ -230,144 +80,155 @@ old_iface_is_bridged_to(struct lldpd *cfg,
                return 0;
 
        for (j = 0; j < MAX_PORTS; j++) {
-               if (ifptindices[j] == slave->index)
-                       return 1;
+               struct interfaces_device *port;
+               if (!ifptindices[j]) continue;
+               port = interfaces_indextointerface(interfaces, ifptindices[j]);
+               if (!port) continue;
+               if (port->upper) {
+                       log_debug("interfaces",
+                           "strange, port %s for bridge %s already has upper interface %s",
+                           port->name, iface->name, port->upper->name);
+               } else {
+                       log_debug("interfaces",
+                           "port %s is bridged to %s",
+                           port->name, iface->name);
+                       port->upper = iface;
+               }
        }
-#endif
+       return 1;
+#else
        return 0;
+#endif
 }
 
 static int
-iface_is_bridged_to(struct lldpd *cfg,
-    struct netlink_interface_list *interfaces,
-    struct netlink_interface *slave,
-    struct netlink_interface *master)
+iflinux_is_bridge(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces,
+    struct interfaces_device *iface)
 {
+       struct interfaces_device *port;
        char path[SYSFS_PATH_MAX];
        int f;
 
-       /* Master should be a bridge, first */
-       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->name, slave->name) >= SYSFS_PATH_MAX)
+       if ((snprintf(path, SYSFS_PATH_MAX,
+                   SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB,
+                   iface->name)) >= SYSFS_PATH_MAX)
                log_warnx("interfaces", "path truncated");
-       if ((f = priv_open(path)) < 0) {
-               return old_iface_is_bridged_to(cfg, interfaces, slave, master);
-       }
+       if ((f = priv_open(path)) < 0)
+               return old_iflinux_is_bridge(cfg, interfaces, iface);
        close(f);
+
+       /* Also grab all ports */
+       TAILQ_FOREACH(port, interfaces, next) {
+               if (port->upper) continue;
+               if (snprintf(path, SYSFS_PATH_MAX,
+                       SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_SUBDIR "/%s/port_no",
+                       iface->name, port->name) >= SYSFS_PATH_MAX)
+                       log_warnx("interfaces", "path truncated");
+               if ((f = priv_open(path)) < 0)
+                       continue;
+               log_debug("interfaces",
+                   "port %s is bridged to %s",
+                   port->name, iface->name);
+               port->upper = iface;
+               close(f);
+       }
+
        return 1;
 }
-#endif
 
 static int
-iface_is_vlan(struct lldpd *cfg,
-       struct netlink_interface *iface)
+iflinux_is_vlan(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces,
+    struct interfaces_device *iface)
 {
        struct vlan_ioctl_args ifv;
        memset(&ifv, 0, sizeof(ifv));
        ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
-       if ((strlcpy(ifv.device1, iface->name, sizeof(ifv.device1))) >=
-           sizeof(ifv.device1))
-               log_warnx("interfaces", "device name truncated");
-       if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0)
-               return 1;
-       return 0;
-}
+       strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
+       if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) {
+               /* This is a VLAN, get the lower interface and the VID */
+               struct interfaces_device *lower =
+                   interfaces_nametointerface(interfaces, ifv.u.device2);
+               if (!lower) {
+                       log_debug("interfaces",
+                           "unable to find lower interface for VLAN %s",
+                           iface->name);
+                       return 0;
+               }
 
-static int
-iface_is_wireless(struct lldpd *cfg,
-    struct netlink_interface *iface)
-{
-       struct iwreq iwr;
-       strlcpy(iwr.ifr_name, iface->name, IFNAMSIZ);
-       if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0)
-               return 1;
-       return 0;
-}
+               memset(&ifv, 0, sizeof(ifv));
+               ifv.cmd = GET_VLAN_VID_CMD;
+               strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
+               if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) {
+                       log_debug("interfaces",
+                           "unable to find VID for VLAN %s",
+                           iface->name);
+                       return 0;
+               }
 
-static int
-iface_is_bond(struct lldpd *cfg,
-    struct netlink_interface *iface)
-{
-       struct ifreq ifr;
-       struct ifbond ifb;
-       memset(&ifr, 0, sizeof(ifr));
-       memset(&ifb, 0, sizeof(ifb));
-       strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
-       ifr.ifr_data = (char *)&ifb;
-       if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0)
+               iface->lower = lower;
+               iface->vlanid = ifv.u.VID;
                return 1;
+       }
        return 0;
 }
 
 static int
-iface_is_bond_slave(struct lldpd *cfg,
-    struct netlink_interface *slave,
-    struct netlink_interface *master,
-    int *active)
+iflinux_is_bond(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces,
+    struct interfaces_device *master)
 {
        struct ifreq ifr;
        struct ifbond ifb;
-       struct ifslave ifs;
        memset(&ifr, 0, sizeof(ifr));
        memset(&ifb, 0, sizeof(ifb));
        strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
        ifr.ifr_data = (char *)&ifb;
        if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) {
                while (ifb.num_slaves--) {
+                       struct ifslave ifs;
                        memset(&ifr, 0, sizeof(ifr));
                        memset(&ifs, 0, sizeof(ifs));
                        strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
                        ifr.ifr_data = (char *)&ifs;
                        ifs.slave_id = ifb.num_slaves;
-                       if ((ioctl(cfg->g_sock, SIOCBONDSLAVEINFOQUERY, &ifr) >= 0) &&
-                           (strncmp(ifs.slave_name, slave->name, sizeof(ifs.slave_name)) == 0)) {
-                               if (active)
-                                       *active = ifs.state;
-                               return 1;
+                       if (ioctl(cfg->g_sock, SIOCBONDSLAVEINFOQUERY, &ifr) >= 0) {
+                               struct interfaces_device *slave =
+                                   interfaces_nametointerface(interfaces,
+                                       ifs.slave_name);
+                               if (slave == NULL) continue;
+                               if (slave->upper) continue;
+                               log_debug("interfaces",
+                                   "interface %s is enslaved to %s",
+                                   slave->name, master->name);
+                               slave->upper = master;
                        }
                }
+               return 1;
        }
        return 0;
 }
 
-static struct netlink_interface*
-iface_is_enslaved(struct lldpd *cfg,
-    struct netlink_interface_list *interfaces,
-    struct netlink_interface *iface)
-{
-       struct netlink_interface *master;
-
-       TAILQ_FOREACH(master, interfaces, next) {
-               if (iface_is_bond_slave(cfg, iface, master, NULL))
-                       return master;
-       }
-       return NULL;
-}
-
 static void
-iface_get_permanent_mac(struct lldpd *cfg,
-    struct netlink_interface_list *interfaces,
-    struct netlink_interface *iface,
-    struct lldpd_hardware *hardware)
+iflinux_get_permanent_mac(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces,
+    struct interfaces_device *iface)
 {
-       struct netlink_interface *master;
+       struct interfaces_device *master;
        int f, state = 0;
        FILE *netbond;
-       const char *slaveif = "Slave Interface: ";
-       const char *hwaddr = "Permanent HW addr: ";
+       const char const *slaveif = "Slave Interface: ";
+       const char const *hwaddr = "Permanent HW addr: ";
        u_int8_t mac[ETHER_ADDR_LEN];
        char path[SYSFS_PATH_MAX];
        char line[100];
 
-       if ((master = iface_is_enslaved(cfg, interfaces,
-                   iface)) == NULL)
+       if ((master = iface->upper) == NULL)
                return;
 
        log_debug("interfaces", "get MAC address for %s",
-           hardware->h_ifname);
+           iface->name);
 
        /* We have a bond, we need to query it to get real MAC addresses */
        if (snprintf(path, SYSFS_PATH_MAX, "/proc/net/bonding/%s",
@@ -426,7 +287,7 @@ iface_get_permanent_mac(struct lldpd *cfg,
                                        fclose(netbond);
                                        return;
                                }
-                               memcpy(hardware->h_lladdr, mac,
+                               memcpy(iface->address, mac,
                                    ETHER_ADDR_LEN);
                                fclose(netbond);
                                return;
@@ -439,180 +300,9 @@ iface_get_permanent_mac(struct lldpd *cfg,
        fclose(netbond);
 }
 
-/* Generic minimal checks to handle a given interface. */
-static int
-iface_minimal_checks(struct lldpd *cfg,
-    struct netlink_interface_list *interfaces,
-    struct netlink_interface *iface)
-{
-       struct ifreq ifr;
-       struct ethtool_drvinfo ethc;
-       const char * const *rif;
-
-       /* White-list some drivers */
-       const char * const regular_interfaces[] = {
-               "dsa",
-               "veth",
-               NULL
-       };
-
-       int is_bridge = iface_is_bridge(cfg, interfaces, iface);
-
-       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",
-                   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, iface))
-               LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
-
-       /* First, check if this interface has already been handled */
-       if (!iface->flags) {
-               log_debug("interfaces", "skip %s: already been handled",
-                   iface->name);
-               return 0;
-       }
-
-       if (iface->type != ARPHRD_ETHER) {
-               log_debug("interfaces", "skip %s: not an Ethernet device",
-                   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 (!(iface->flags & (IFF_MULTICAST|IFF_BROADCAST))) {
-               log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
-                   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, iface->name);
-       memset(&ethc, 0, sizeof(ethc));
-       ifr.ifr_data = (caddr_t) &ethc;
-       ethc.cmd = ETHTOOL_GDRVINFO;
-       if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) {
-               for (rif = regular_interfaces; *rif; rif++) {
-                       if (strcmp(ethc.driver, *rif) == 0) {
-                               /* White listed! */
-                               log_debug("interfaces", "accept %s: whitelisted",
-                                   iface->name);
-                               return 1;
-                       }
-               }
-       }
-       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. */
-       if (iface->txqueue == 0) {
-               log_debug("interfaces", "skip %s: no queue",
-                   iface->name);
-               return 0;
-       }
-
-       /* Don't handle bond and VLAN, nor bridge  */
-       if (iface_is_vlan(cfg, iface)) {
-               log_debug("interfaces", "skip %s: is a VLAN",
-                   iface->name);
-               return 0;
-       }
-       if (iface_is_bond(cfg, iface)) {
-               log_debug("interfaces", "skip %s: is a bond",
-                   iface->name);
-               return 0;
-       }
-       if (is_bridge) {
-               log_debug("interfaces", "skip %s: is a bridge",
-                   iface->name);
-               return 0;
-       }
-
-       log_debug("interfaces", "%s passes the minimal checks",
-           iface->name);
-       return 1;
-}
-
-static int
-iface_set_filter(const char *name, int fd)
-{
-       struct sock_fprog prog;
-       log_debug("interfaces", "set BPF filter for %s", name);
-
-       memset(&prog, 0, sizeof(struct sock_fprog));
-       prog.filter = lldpd_filter_f;
-       prog.len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter);
-
-       if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
-                &prog, sizeof(prog)) < 0) {
-               log_info("interfaces", "unable to change filter for %s", name);
-               return ENETDOWN;
-       }
-       return 0;
-}
-
-/* Fill up port name and description */
-static void
-iface_port_name_desc(struct lldpd_hardware *hardware,
-    struct netlink_interface *iface)
-{
-       struct lldpd_port *port = &hardware->h_lport;
-
-       /* There are two cases:
-
-            1. We have a kernel recent enough to support ifAlias
-            _and_ a non empty ifAlias, then we will use it for
-            description and use ifname for port ID.
-
-            2. Otherwise, we will use the MAC address as ID and the
-            port name in description.
-       */
-
-       if (iface->alias == NULL || strlen(iface->alias) == 0) {
-               /* Case 2: MAC address and port name */
-               log_debug("interfaces", "use ifname and MAC address for %s",
-                   hardware->h_ifname);
-               port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
-               if ((port->p_id =
-                       calloc(1, sizeof(hardware->h_lladdr))) == NULL)
-                       fatal("interfaces", NULL);
-               memcpy(port->p_id, hardware->h_lladdr,
-                   sizeof(hardware->h_lladdr));
-               port->p_id_len = sizeof(hardware->h_lladdr);
-               port->p_descr = strdup(hardware->h_ifname);
-               return;
-       }
-       /* Case 1: port name and port description */
-       log_debug("interfaces", "use ifname and ifalias for %s",
-           hardware->h_ifname);
-       port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
-       port->p_id_len = strlen(hardware->h_ifname);
-       if ((port->p_id =
-               calloc(1, port->p_id_len)) == NULL)
-               fatal("interfaces", NULL);
-       memcpy(port->p_id, hardware->h_ifname, port->p_id_len);
-       port->p_descr = strdup(iface->alias);
-}
-
 /* Fill up MAC/PHY for a given hardware port */
 static void
-iface_macphy(struct lldpd_hardware *hardware)
+iflinux_macphy(struct lldpd_hardware *hardware)
 {
 #ifdef ENABLE_DOT3
        struct ethtool_cmd ethc;
@@ -677,168 +367,6 @@ iface_macphy(struct lldpd_hardware *hardware)
 #endif
 }
 
-static void
-iface_multicast(struct lldpd *cfg, const char *name, int remove)
-{
-       int i, rc;
-
-       for (i=0; cfg->g_protocols[i].mode != 0; i++) {
-               if (!cfg->g_protocols[i].enabled) continue;
-               if ((rc = priv_iface_multicast(name,
-                           cfg->g_protocols[i].mac, !remove)) != 0) {
-                       errno = rc;
-                       if (errno != ENOENT)
-                               log_info("interfaces", "unable to %s %s address to multicast filter for %s",
-                                   (remove)?"delete":"add",
-                                   cfg->g_protocols[i].name,
-                                   name);
-               }
-       }
-}
-
-static int
-iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
-{
-       int fd, status;
-
-       log_debug("interfaces", "initialize ethernet device %s",
-           hardware->h_ifname);
-       if ((fd = priv_iface_init(hardware->h_ifindex)) == -1)
-               return -1;
-       hardware->h_sendfd = fd; /* Send */
-
-       /* Set filter */
-       if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) {
-               close(fd);
-               return status;
-       }
-
-       iface_multicast(cfg, hardware->h_ifname, 0);
-
-       levent_hardware_add_fd(hardware, fd); /* Receive */
-       log_debug("interfaces", "interface %s initialized (fd=%d)", hardware->h_ifname,
-           fd);
-       return 0;
-}
-
-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 (fd=%d)",
-           hardware->h_ifname, hardware->h_sendfd);
-       return write(hardware->h_sendfd,
-           buffer, size);
-}
-
-static int
-iface_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
-    int fd, char *buffer, size_t size)
-{
-       int n;
-       struct sockaddr_ll from;
-       socklen_t fromlen;
-
-       log_debug("interfaces", "receive PDU from ethernet device %s",
-           hardware->h_ifname);
-       fromlen = sizeof(from);
-       if ((n = recvfrom(fd,
-                   buffer,
-                   size, 0,
-                   (struct sockaddr *)&from,
-                   &fromlen)) == -1) {
-               log_warn("interfaces", "error while receiving frame on %s",
-                   hardware->h_ifname);
-               hardware->h_rx_discarded_cnt++;
-               return -1;
-       }
-       if (from.sll_pkttype == PACKET_OUTGOING)
-               return -1;
-       return n;
-}
-
-static int
-iface_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
-{
-       log_debug("interfaces", "close ethernet device %s",
-           hardware->h_ifname);
-       iface_multicast(cfg, hardware->h_ifname, 1);
-       return 0;
-}
-
-static void
-lldpd_ifh_eth(struct lldpd *cfg, struct netlink_interface_list *interfaces)
-{
-       struct netlink_interface *iface;
-       struct lldpd_hardware *hardware;
-
-       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",
-                   iface->name);
-               if ((hardware = lldpd_get_hardware(cfg,
-                           iface->name,
-                           iface->index,
-                           &eth_ops)) == NULL) {
-                       if  ((hardware = lldpd_alloc_hardware(cfg,
-                                   iface->name,
-                                   iface->index)) == NULL) {
-                               log_warnx("interfaces", "Unable to allocate space for %s",
-                                   iface->name);
-                               continue;
-                       }
-                       if (iface_eth_init(cfg, hardware) != 0) {
-                               log_warn("interfaces", "unable to initialize %s", hardware->h_ifname);
-                               lldpd_hardware_cleanup(cfg, hardware);
-                               continue;
-                       }
-                       hardware->h_ops = &eth_ops;
-                       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 = 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, iface->address, ETHER_ADDR_LEN);
-
-               /* Fill information about port */
-               iface_port_name_desc(hardware, iface);
-
-               /* Fill additional info */
-               iface_macphy(hardware);
-               hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
-       }
-}
-
-static void
-lldpd_ifh_whitelist(struct lldpd *cfg, struct netlink_interface_list *interfaces)
-{
-       struct netlink_interface *iface;
-
-       if (!cfg->g_config.c_iface_pattern)
-               return;
-
-       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", iface->name);
-                       iface->flags = 0;
-               }
-       }
-}
-
 struct bond_master {
        char name[IFNAMSIZ];
        int  index;
@@ -860,18 +388,18 @@ iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
        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) {
+       if ((status = interfaces_set_filter(hardware->h_ifname, fd)) != 0) {
                close(fd);
                return status;
        }
-       iface_multicast(cfg, hardware->h_ifname, 0);
+       interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
 
        /* Then, we open a raw interface for the master */
        if ((fd = priv_iface_init(master->index)) == -1) {
                close(hardware->h_sendfd);
                return -1;
        }
-       if ((status = iface_set_filter(master->name, fd)) != 0) {
+       if ((status = interfaces_set_filter(master->name, fd)) != 0) {
                close(hardware->h_sendfd);
                close(fd);
                return status;
@@ -886,7 +414,7 @@ iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
                    "You will get inaccurate results",
                    hardware->h_ifname);
        }
-       iface_multicast(cfg, master->name, 0);
+       interfaces_setup_multicast(cfg, master->name, 0);
 
        levent_hardware_add_fd(hardware, hardware->h_sendfd);
        levent_hardware_add_fd(hardware, fd);
@@ -960,28 +488,31 @@ 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, master->name, 1);
+       interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
+       interfaces_setup_multicast(cfg, master->name, 1);
        free(hardware->h_data);
        return 0;
 }
 
+struct lldpd_ops bond_ops = {
+       .send = iface_bond_send,
+       .recv = iface_bond_recv,
+       .cleanup = iface_bond_close,
+};
+
 static void
-lldpd_ifh_bond(struct lldpd *cfg, struct netlink_interface_list *interfaces)
+iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces)
 {
-       struct netlink_interface *iface;
-       struct netlink_interface *master;
+       struct interfaces_device *iface;
+       struct interfaces_device *master;
        struct lldpd_hardware *hardware;
        struct bond_master *bmaster;
        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, interfaces,
-                           iface)) == NULL)
-                       continue;
+               if (!(iface->type & IFACE_PHYSICAL_T)) continue;
+               if (!iface->flags) continue;
+               if (!iface->upper || !(iface->upper->type & IFACE_BOND_T)) continue;
 
+               master = iface->upper;
                log_debug("interfaces", "%s is an acceptable bonded device (master=%s)",
                    iface->name, master->name);
                if ((hardware = lldpd_get_hardware(cfg,
@@ -1022,329 +553,192 @@ lldpd_ifh_bond(struct lldpd *cfg, struct netlink_interface_list *interfaces)
                iface->flags = 0;
 
                /* Get local address */
-               iface_get_permanent_mac(cfg, interfaces, iface, hardware);
+               memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
 
                /* Fill information about port */
-               iface_port_name_desc(hardware, iface);
+               interfaces_helper_port_name_desc(hardware, iface);
 
                /* Fill additional info */
 #ifdef ENABLE_DOT3
                hardware->h_lport.p_aggregid = master->index;
 #endif
-               iface_macphy(hardware);
                hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
        }
 }
 
-#ifdef ENABLE_DOT1
+/* Query each interface to get the appropriate driver */
 static void
-iface_append_vlan(struct lldpd *cfg,
-    struct netlink_interface *vlan,
-    struct netlink_interface *lower)
+iflinux_add_driver(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces)
 {
-       struct lldpd_hardware *hardware =
-           lldpd_get_hardware(cfg, lower->name, lower->index, NULL);
-       struct lldpd_port *port;
-       struct lldpd_vlan *v;
-       struct vlan_ioctl_args ifv;
-
-       if (hardware == NULL) {
-               log_debug("interfaces",
-                   "cannot find real interface %s for VLAN %s",
-                   lower->name, vlan->name);
-               return;
-       }
-
-       /* Check if the VLAN is already here. */
-       port = &hardware->h_lport;
-       TAILQ_FOREACH(v, &port->p_vlans, v_entries)
-           if (strncmp(vlan->name, v->v_name, IFNAMSIZ) == 0)
-                   return;
-       if ((v = (struct lldpd_vlan *)
-               calloc(1, sizeof(struct lldpd_vlan))) == NULL)
-               return;
-       if ((v->v_name = strdup(vlan->name)) == NULL) {
-               free(v);
-               return;
-       }
-       memset(&ifv, 0, sizeof(ifv));
-       ifv.cmd = GET_VLAN_VID_CMD;
-       strlcpy(ifv.device1, vlan->name, sizeof(ifv.device1));
-       if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) {
-               /* Dunno what happened */
-               free(v->v_name);
-               free(v);
-               return;
+       struct interfaces_device *iface;
+       TAILQ_FOREACH(iface, interfaces, next) {
+               struct ethtool_drvinfo ethc = {
+                       .cmd = ETHTOOL_GDRVINFO
+               };
+               struct ifreq ifr = {
+                       .ifr_data = (caddr_t)&ethc
+               };
+               if (iface->driver) continue;
+
+               strlcpy(ifr.ifr_name, iface->name, IFNAMSIZ);
+               if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) {
+                       iface->driver = strdup(ethc.driver);
+                       log_debug("interfaces", "driver for %s is `%s`",
+                           iface->name, iface->driver);
+               }
        }
-       v->v_vid = ifv.u.VID;
-       log_debug("interfaces", "append VLAN %s for %s",
-           v->v_name,
-           hardware->h_ifname);
-       TAILQ_INSERT_TAIL(&port->p_vlans, v, v_entries);
 }
 
-/**
- * Append VLAN to the lowest possible interface.
- *
- * @param vlan  The VLAN interface (used to get VLAN ID).
- * @param upper The upper interface we are currently examining.
- *
- * Initially, upper == vlan. This function will be called recursively.
- */
+/* Query each interface to see if it is a wireless one */
 static void
-iface_append_vlan_to_lower(struct lldpd *cfg,
-    struct netlink_interface_list *interfaces,
-    struct netlink_interface *vlan,
-    struct netlink_interface *upper)
+iflinux_add_wireless(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces)
 {
-       log_debug("interfaces",
-           "looking to apply VLAN %s to physical interface behind %s",
-           vlan->name, upper->name);
-
-       /* Easy: check if we have a physical link. */
-       if (upper->link != -1 && upper->link != upper->index) {
-               struct netlink_interface *lower =
-                   iface_indextointerface(interfaces, upper->link);
-               if (lower) {
-                       iface_append_vlan_to_lower(cfg,
-                           interfaces, vlan,
-                           lower);
-                       return;
-               }
-               log_debug("interfaces", "unknown lower interface for %s",
-                   upper->name);
-               return;
-       }
-
-       /* Less easy, it can be a bond, a bridge, or a VLAN ! */
-       /* Check if it is a VLAN */
-       if (vlan == upper || iface_is_vlan(cfg, upper)) {
-               struct vlan_ioctl_args ifv;
-               log_debug("interfaces", "VLAN %s on VLAN %s",
-                   vlan->name, upper->name);
-               memset(&ifv, 0, sizeof(ifv));
-               ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
-               strlcpy(ifv.device1, vlan->name, sizeof(ifv.device1));
-               if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) {
-                       struct netlink_interface *lower =
-                           iface_nametointerface(interfaces, ifv.u.device2);
-                       if (lower) {
-                               iface_append_vlan_to_lower(cfg,
-                                   interfaces, vlan, lower);
-                               return;
-                       }
+       struct interfaces_device *iface;
+       TAILQ_FOREACH(iface, interfaces, next) {
+               struct iwreq iwr;
+               memset(&iwr, 0, sizeof(struct iwreq));
+               strlcpy(iwr.ifr_name, iface->name, IFNAMSIZ);
+               if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0) {
+                       log_debug("interfaces", "%s is wireless",
+                           iface->name);
+                       iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T;
                }
-               log_debug("interfaces",
-                   "unknown lower interface for VLAN %s",
-                   upper->name);
-               return;
        }
+}
 
-       /* Check if it is a bond. */
-       if (iface_is_bond(cfg, upper)) {
-               struct netlink_interface *lower;
-               log_debug("interfaces", "VLAN %s on bond %s",
-                   vlan->name, upper->name);
-               TAILQ_FOREACH(lower, interfaces, next) {
-                       if (iface_is_bond_slave(cfg,
-                               lower, upper, NULL)) {
-                               iface_append_vlan_to_lower(cfg,
-                                   interfaces, vlan, lower);
-                       }
-               }
-               return;
-       }
+/* Query each interface to see if it is a bridge */
+static void
+iflinux_add_bridge(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces)
+{
+       struct interfaces_device *iface;
 
-       /* Check if it is a bridge. */
-       if (iface_is_bridge(cfg, interfaces, upper)) {
-               struct netlink_interface *lower;
-               log_debug("interfaces", "VLAN %s on bridge %s",
-                   vlan->name, upper->name);
-               TAILQ_FOREACH(lower, interfaces, next) {
-                       if (iface_is_bridged_to(cfg,
-                               interfaces,
-                               lower, upper)) {
-                               iface_append_vlan_to_lower(cfg,
-                                   interfaces, vlan, lower);
-                       }
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface->type & (IFACE_PHYSICAL_T|
+                       IFACE_VLAN_T|IFACE_BOND_T|IFACE_BRIDGE_T))
+                       continue;
+               if (iflinux_is_bridge(cfg, interfaces, iface)) {
+                       log_debug("interfaces",
+                           "interface %s is a bridge",
+                           iface->name);
+                       iface->type |= IFACE_BRIDGE_T;
                }
-               return;
        }
-
-       log_debug("interfaces", "VLAN %s on physical interface %s",
-           vlan->name, upper->name);
-       iface_append_vlan(cfg, vlan, upper);
 }
 
+/* Query each interface to see if it is a bond */
 static void
-lldpd_ifh_vlan(struct lldpd *cfg,
-    struct netlink_interface_list *interfaces)
+iflinux_add_bond(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces)
 {
-       struct netlink_interface *iface;
+       struct interfaces_device *iface;
 
        TAILQ_FOREACH(iface, interfaces, next) {
-               if (!iface->flags)
+               if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
+                       IFACE_BOND_T|IFACE_BRIDGE_T))
                        continue;
-               if (!iface_is_vlan(cfg, iface))
-                       continue;
-
-               /* We need to find the physical interfaces of this
-                  vlan, through bonds and bridges. */
-               log_debug("interfaces", "search physical interface for VLAN interface %s",
-                   iface->name);
-               iface_append_vlan_to_lower(cfg, interfaces,
-                   iface, iface);
+               if (iflinux_is_bond(cfg, interfaces, iface)) {
+                       log_debug("interfaces",
+                           "interface %s is a bond",
+                           iface->name);
+                       iface->type |= IFACE_BOND_T;
+                       iflinux_get_permanent_mac(cfg,
+                           interfaces, iface);
+               }
        }
 }
-#endif
 
-#ifndef IN_IS_ADDR_LOOPBACK
-#define IN_IS_ADDR_LOOPBACK(a) ((a)->s_addr == htonl(INADDR_LOOPBACK))
-#endif
-#ifndef IN_IS_ADDR_GLOBAL
-#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))
-#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). */
+/* Query each interface to see if it is a vlan */
 static void
-lldpd_ifh_mgmt(struct lldpd *cfg, struct netlink_address_list *addrs)
+iflinux_add_vlan(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces)
 {
-       struct netlink_address *addr;
-       char addrstrbuf[INET6_ADDRSTRLEN];
-       struct lldpd_mgmt *mgmt;
-       void *sin_addr_ptr;
-       size_t sin_addr_size;
-       int af;
-       int allnegative = 0;
-
-       lldpd_chassis_mgmt_cleanup(LOCAL_CHASSIS(cfg));
-
-       /* Is the pattern provided all negative? */
-       if (cfg->g_config.c_mgmt_pattern == NULL) allnegative = 1;
-       else if (cfg->g_config.c_mgmt_pattern[0] == '!') {
-               /* If each comma is followed by '!', its an all
-                  negative pattern */
-               char *sep = cfg->g_config.c_mgmt_pattern;
-               while ((sep = strchr(sep, ',')) &&
-                      (*(++sep) == '!'));
-               if (sep == NULL) allnegative = 1;
-       }
-
-       /* Find management addresses */
-       for (af = LLDPD_AF_UNSPEC + 1; af != LLDPD_AF_LAST; af++) {
-               /* We only take one of each address family, unless a
-                  pattern is provided and is not all negative. For
-                  example !*:*,!10.* will only blacklist
-                  addresses. We will pick the first IPv4 address not
-                  matching 10.*. */
-               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 *)&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 *)&addr->address)->sin6_addr;
-                               sin_addr_size = sizeof(struct in6_addr);
-                               if (!IN6_IS_ADDR_GLOBAL((struct in6_addr *)sin_addr_ptr))
-                                       continue;
-                               break;
-                       default:
-                               assert(0);
-                               continue;
-                       }
-                       if (inet_ntop(lldpd_af(af), sin_addr_ptr,
-                               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,
-                                                       addr->index);
-                               if (mgmt == NULL) {
-                                       assert(errno == ENOMEM); /* anything else is a bug */
-                                       log_warn("interfaces", "out of memory error");
-                                       return;
-                               }
-                               log_debug("interfaces", "add management address %s", addrstrbuf);
-                               TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries);
+       struct interfaces_device *iface;
 
-                               /* Don't take additional address if the pattern is all negative. */
-                               if (allnegative) break;
-                       }
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
+                       IFACE_BOND_T|IFACE_BRIDGE_T))
+                       continue;
+               if (iflinux_is_vlan(cfg, interfaces, iface)) {
+                       log_debug("interfaces",
+                           "interface %s is a VLAN",
+                           iface->name);
+                       iface->type |= IFACE_VLAN_T;
                }
        }
 }
 
-
-/* Fill out chassis ID if not already done. This handler is special
-   because we will only handle interfaces that are already handled. */
 static void
-lldpd_ifh_chassis(struct lldpd *cfg, struct netlink_interface_list *interfaces)
+iflinux_add_physical(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces)
 {
-       struct netlink_interface *iface;
-       struct lldpd_hardware *hardware;
-       char *name = NULL;
-
-       if (LOCAL_CHASSIS(cfg)->c_id != NULL &&
-           LOCAL_CHASSIS(cfg)->c_id_subtype == LLDP_CHASSISID_SUBTYPE_LLADDR)
-               return;         /* We already have one */
+       struct interfaces_device *iface;
+       /* White-list some drivers */
+       const char * const *rif;
+       const char * const regular_interfaces[] = {
+               "dsa",
+               "veth",
+               NULL
+       };
 
        TAILQ_FOREACH(iface, interfaces, next) {
-               if (iface->flags) continue;
-               if (cfg->g_config.c_cid_pattern &&
-                   !pattern_match(iface->name, cfg->g_config.c_cid_pattern, 0)) continue;
+               if (iface->type & (IFACE_VLAN_T|
+                       IFACE_BOND_T|IFACE_BRIDGE_T))
+                       continue;
 
-               if ((hardware = lldpd_get_hardware(cfg,
-                           iface->name,
-                           iface->index,
-                           NULL)) == NULL)
-                       /* That's odd. Let's skip. */
+               iface->type &= ~IFACE_PHYSICAL_T;
+
+               /* We request that the interface is able to do either multicast
+                * or broadcast to be able to send discovery frames. */
+               if (!(iface->flags & (IFF_MULTICAST|IFF_BROADCAST))) {
+                       log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
+                           iface->name);
                        continue;
+               }
 
-               name = malloc(sizeof(hardware->h_lladdr));
-               if (!name) {
-                       log_warn("interfaces", "not enough memory for chassis ID");
-                       return;
+               /* If the interface is linked to another one, skip it too. */
+               if (iface->lower) {
+                       log_debug("interfaces", "skip %s: there is a lower interface (%s)",
+                           iface->name, iface->lower->name);
+                       continue;
                }
-               free(LOCAL_CHASSIS(cfg)->c_id);
-               memcpy(name, hardware->h_lladdr, sizeof(hardware->h_lladdr));
-               LOCAL_CHASSIS(cfg)->c_id = name;
-               LOCAL_CHASSIS(cfg)->c_id_len = sizeof(hardware->h_lladdr);
-               LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
-               return;
+
+               /* Check if the driver is whitelisted */
+               if (iface->driver) {
+                       for (rif = regular_interfaces; *rif; rif++) {
+                               if (strcmp(iface->driver, *rif) == 0) {
+                                       /* White listed! */
+                                       log_debug("interfaces", "accept %s: whitelisted",
+                                           iface->name);
+                                       iface->type |= IFACE_PHYSICAL_T;
+                                       continue;
+                               }
+                       }
+               }
+
+               /* Check queue len. If no queue, this usually means that this
+                  is not a "real" interface. */
+               if (iface->txqueue == 0) {
+                       log_debug("interfaces", "skip %s: no queue",
+                           iface->name);
+                       continue;
+               }
+
+               log_debug("interfaces",
+                   "%s is a physical interface",
+                   iface->name);
+               iface->type |= IFACE_PHYSICAL_T;
        }
 }
 
-struct lldpd_ops eth_ops = {
-       .send = iface_eth_send,
-       .recv = iface_eth_recv,
-       .cleanup = iface_eth_close,
-};
-struct lldpd_ops bond_ops = {
-       .send = iface_bond_send,
-       .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;
+       struct lldpd_hardware *hardware;
+       struct interfaces_device_list *interfaces = NULL;
+       struct interfaces_address_list *addresses = NULL;
        interfaces = netlink_get_interfaces();
        addresses = netlink_get_addresses();
        if (interfaces == NULL || addresses == NULL) {
@@ -1352,18 +746,32 @@ interfaces_update(struct lldpd *cfg)
                goto end;
        }
 
-       lldpd_ifh_whitelist(cfg, interfaces);
-       lldpd_ifh_bond(cfg, interfaces);
-       lldpd_ifh_eth(cfg, interfaces);
+       /* Add missing bits to list of interfaces */
+       iflinux_add_driver(cfg, interfaces);
+       iflinux_add_wireless(cfg, interfaces);
+       iflinux_add_bridge(cfg, interfaces);
+       iflinux_add_bond(cfg, interfaces);
+       iflinux_add_vlan(cfg, interfaces);
+       iflinux_add_physical(cfg, interfaces);
+
+       interfaces_helper_whitelist(cfg, interfaces);
+       iflinux_handle_bond(cfg, interfaces);
+       interfaces_helper_physical(cfg, interfaces);
 #ifdef ENABLE_DOT1
-       lldpd_ifh_vlan(cfg, interfaces);
+       interfaces_helper_vlan(cfg, interfaces);
 #endif
-       lldpd_ifh_mgmt(cfg, addresses);
-       lldpd_ifh_chassis(cfg, interfaces);
+       interfaces_helper_mgmt(cfg, addresses);
+       interfaces_helper_chassis(cfg, interfaces);
+
+       /* Mac/PHY */
+       TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
+               if (!hardware->h_flags) continue;
+               iflinux_macphy(hardware);
+       }
 
 end:
-       netlink_free_interfaces(interfaces);
-       netlink_free_addresses(addresses);
+       interfaces_free_devices(interfaces);
+       interfaces_free_addresses(addresses);
        return;
 
 }
diff --git a/src/daemon/interfaces.c b/src/daemon/interfaces.c
new file mode 100644 (file)
index 0000000..bf4695d
--- /dev/null
@@ -0,0 +1,626 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "lldpd.h"
+
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <fnmatch.h>
+#include <netpacket/packet.h>
+#include <arpa/inet.h>
+
+/* Generic ethernet send/receive */
+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 (fd=%d)",
+           hardware->h_ifname, hardware->h_sendfd);
+       return write(hardware->h_sendfd,
+           buffer, size);
+}
+
+static int
+iface_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
+    int fd, char *buffer, size_t size)
+{
+       int n;
+       struct sockaddr_ll from;
+       socklen_t fromlen;
+
+       log_debug("interfaces", "receive PDU from ethernet device %s",
+           hardware->h_ifname);
+       fromlen = sizeof(from);
+       if ((n = recvfrom(fd,
+                   buffer,
+                   size, 0,
+                   (struct sockaddr *)&from,
+                   &fromlen)) == -1) {
+               log_warn("interfaces", "error while receiving frame on %s",
+                   hardware->h_ifname);
+               hardware->h_rx_discarded_cnt++;
+               return -1;
+       }
+       if (from.sll_pkttype == PACKET_OUTGOING)
+               return -1;
+       return n;
+}
+
+static int
+iface_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
+{
+       log_debug("interfaces", "close ethernet device %s",
+           hardware->h_ifname);
+       interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
+       return 0;
+}
+
+struct lldpd_ops eth_ops = {
+       .send = iface_eth_send,
+       .recv = iface_eth_recv,
+       .cleanup = iface_eth_close,
+};
+
+/* Generic ethernet interface initialization */
+/**
+ * Enable multicast on the given interface.
+ */
+void
+interfaces_setup_multicast(struct lldpd *cfg, const char *name,
+    int remove)
+{
+       int i, rc;
+
+       for (i=0; cfg->g_protocols[i].mode != 0; i++) {
+               if (!cfg->g_protocols[i].enabled) continue;
+               if ((rc = priv_iface_multicast(name,
+                           cfg->g_protocols[i].mac, !remove)) != 0) {
+                       errno = rc;
+                       if (errno != ENOENT)
+                               log_info("interfaces",
+                                   "unable to %s %s address to multicast filter for %s",
+                                   (remove)?"delete":"add",
+                                   cfg->g_protocols[i].name,
+                                   name);
+               }
+       }
+}
+
+static int
+iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
+{
+       int fd, status;
+
+       log_debug("interfaces", "initialize ethernet device %s",
+           hardware->h_ifname);
+       if ((fd = priv_iface_init(hardware->h_ifindex)) == -1)
+               return -1;
+       hardware->h_sendfd = fd; /* Send */
+
+       /* Set filter */
+       if ((status = interfaces_set_filter(hardware->h_ifname, fd)) != 0) {
+               close(fd);
+               return status;
+       }
+
+       interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
+
+       levent_hardware_add_fd(hardware, fd); /* Receive */
+       log_debug("interfaces", "interface %s initialized (fd=%d)", hardware->h_ifname,
+           fd);
+       return 0;
+}
+
+/**
+ * Free an interface.
+ *
+ * @param iff interface to be freed
+ */
+void
+interfaces_free_device(struct interfaces_device *iff)
+{
+       if (!iff) return;
+       free(iff->name);
+       free(iff->alias);
+       free(iff->address);
+       free(iff->driver);
+       free(iff);
+}
+
+/**
+ * Free a list of interfaces.
+ *
+ * @param ifs list of interfaces to be freed
+ */
+void
+interfaces_free_devices(struct interfaces_device_list *ifs)
+{
+       struct interfaces_device *iff, *iff_next;
+       if (!ifs) return;
+       for (iff = TAILQ_FIRST(ifs);
+            iff != NULL;
+            iff = iff_next) {
+               iff_next = TAILQ_NEXT(iff, next);
+               interfaces_free_device(iff);
+       }
+       free(ifs);
+}
+
+/**
+ * Free one address
+ *
+ * @param ifaddr Address to be freed
+ */
+void
+interfaces_free_address(struct interfaces_address *ifaddr)
+{
+       free(ifaddr);
+}
+
+/**
+ * Free a list of addresses.
+ *
+ * @param ifaddrs list of addresses
+ */
+void
+interfaces_free_addresses(struct interfaces_address_list *ifaddrs)
+{
+       struct interfaces_address *ifa, *ifa_next;
+       if (!ifaddrs) return;
+       for (ifa = TAILQ_FIRST(ifaddrs);
+            ifa != NULL;
+            ifa = ifa_next) {
+               ifa_next = TAILQ_NEXT(ifa, next);
+               interfaces_free_address(ifa);
+       }
+       free(ifaddrs);
+}
+
+/**
+ * Find the appropriate interface from the name.
+ *
+ * @param interfaces List of available interfaces
+ * @param device     Name of the device we search for
+ * @param return The interface or NULL if not found
+ */
+struct interfaces_device*
+interfaces_nametointerface(struct interfaces_device_list *interfaces,
+    const char *device)
+{
+       struct interfaces_device *iface;
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (!strncmp(iface->name, device, IFNAMSIZ))
+                       return iface;
+       }
+       log_debug("interfaces", "cannot get interface for index %s",
+           device);
+       return NULL;
+}
+
+/**
+ * Find the appropriate interface from the index.
+ *
+ * @param interfaces List of available interfaces
+ * @param index      Index of the device we search for
+ * @param return The interface or NULL if not found
+ */
+struct interfaces_device*
+interfaces_indextointerface(struct interfaces_device_list *interfaces,
+    int index)
+{
+       struct interfaces_device *iface;
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface->index == index)
+                       return iface;
+       }
+       log_debug("interfaces", "cannot get interface for index %d",
+           index);
+       return NULL;
+}
+
+static int
+pattern_match(char *iface, char *list, int found)
+{
+       char *interfaces = NULL;
+       char *pattern;
+
+       if ((interfaces = strdup(list)) == NULL) {
+               log_warnx("interfaces", "unable to allocate memory");
+               return 0;
+       }
+
+       for (pattern = strtok(interfaces, ",");
+            pattern != NULL;
+            pattern = strtok(NULL, ",")) {
+               if ((pattern[0] == '!') &&
+                   ((fnmatch(pattern + 1, iface, 0) == 0))) {
+                       /* Blacklisted. No need to search further. */
+                       found = 0;
+                       break;
+               }
+               if (fnmatch(pattern, iface, 0) == 0)
+                       found = 1;
+       }
+
+       free(interfaces);
+       return found;
+}
+
+void
+interfaces_helper_whitelist(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces)
+{
+       struct interfaces_device *iface;
+
+       if (!cfg->g_config.c_iface_pattern)
+               return;
+
+       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", iface->name);
+                       iface->flags = 0;
+               }
+       }
+}
+
+#ifdef ENABLE_DOT1
+static void
+iface_append_vlan(struct lldpd *cfg,
+    struct interfaces_device *vlan,
+    struct interfaces_device *lower)
+{
+       struct lldpd_hardware *hardware =
+           lldpd_get_hardware(cfg, lower->name, lower->index, NULL);
+       struct lldpd_port *port;
+       struct lldpd_vlan *v;
+
+       if (hardware == NULL) {
+               log_debug("interfaces",
+                   "cannot find real interface %s for VLAN %s",
+                   lower->name, vlan->name);
+               return;
+       }
+
+       /* Check if the VLAN is already here. */
+       port = &hardware->h_lport;
+       TAILQ_FOREACH(v, &port->p_vlans, v_entries)
+           if (strncmp(vlan->name, v->v_name, IFNAMSIZ) == 0)
+                   return;
+       if ((v = (struct lldpd_vlan *)
+               calloc(1, sizeof(struct lldpd_vlan))) == NULL)
+               return;
+       if ((v->v_name = strdup(vlan->name)) == NULL) {
+               free(v);
+               return;
+       }
+       v->v_vid = vlan->vlanid;
+       log_debug("interfaces", "append VLAN %s for %s",
+           v->v_name,
+           hardware->h_ifname);
+       TAILQ_INSERT_TAIL(&port->p_vlans, v, v_entries);
+}
+
+/**
+ * Append VLAN to the lowest possible interface.
+ *
+ * @param vlan  The VLAN interface (used to get VLAN ID).
+ * @param upper The upper interface we are currently examining.
+ *
+ * Initially, upper == vlan. This function will be called recursively.
+ */
+static void
+iface_append_vlan_to_lower(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces,
+    struct interfaces_device *vlan,
+    struct interfaces_device *upper)
+{
+       struct interfaces_device *lower;
+       log_debug("interfaces",
+           "looking to apply VLAN %s to physical interface behind %s",
+           vlan->name, upper->name);
+
+       /* Easy: check if we have a lower interface. */
+       if (upper->lower) {
+               log_debug("interfaces", "VLAN %s on lower interface %s",
+                   vlan->name, upper->name);
+               iface_append_vlan_to_lower(cfg,
+                   interfaces, vlan,
+                   upper->lower);
+               return;
+       }
+
+       /* Other easy case, we have a physical interface. */
+       if (upper->type & IFACE_PHYSICAL_T) {
+               log_debug("interfaces", "VLAN %s on physical interface %s",
+                   vlan->name, upper->name);
+               iface_append_vlan(cfg, vlan, upper);
+               return;
+       }
+
+       /* We can now search for interfaces that have our interface as an upper
+        * interface. */
+       TAILQ_FOREACH(lower, interfaces, next) {
+               if (lower->upper != upper) continue;
+               log_debug("interfaces", "VLAN %s on lower interface %s",
+                   vlan->name, upper->name);
+               iface_append_vlan_to_lower(cfg,
+                   interfaces, vlan, lower);
+       }
+}
+
+void
+interfaces_helper_vlan(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces)
+{
+       struct interfaces_device *iface;
+
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (!iface->flags)
+                       continue;
+               if (!(iface->type & IFACE_VLAN_T))
+                       continue;
+
+               /* We need to find the physical interfaces of this
+                  vlan, through bonds and bridges. */
+               log_debug("interfaces", "search physical interface for VLAN interface %s",
+                   iface->name);
+               iface_append_vlan_to_lower(cfg, interfaces,
+                   iface, iface);
+       }
+}
+#endif
+
+/* Fill out chassis ID if not already done. This handler is special
+   because we will only handle interfaces that are already handled. */
+void
+interfaces_helper_chassis(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces)
+{
+       struct interfaces_device *iface;
+       struct lldpd_hardware *hardware;
+       char *name = NULL;
+
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface->type & IFACE_BRIDGE_T)
+                       LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE;
+               if (iface->type & IFACE_WIRELESS_T)
+                       LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
+       }
+
+       if (LOCAL_CHASSIS(cfg)->c_id != NULL &&
+           LOCAL_CHASSIS(cfg)->c_id_subtype == LLDP_CHASSISID_SUBTYPE_LLADDR)
+               return;         /* We already have one */
+
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (iface->flags) continue;
+               if (cfg->g_config.c_cid_pattern &&
+                   !pattern_match(iface->name, cfg->g_config.c_cid_pattern, 0)) continue;
+
+               if ((hardware = lldpd_get_hardware(cfg,
+                           iface->name,
+                           iface->index,
+                           NULL)) == NULL)
+                       /* That's odd. Let's skip. */
+                       continue;
+
+               name = malloc(sizeof(hardware->h_lladdr));
+               if (!name) {
+                       log_warn("interfaces", "not enough memory for chassis ID");
+                       return;
+               }
+               free(LOCAL_CHASSIS(cfg)->c_id);
+               memcpy(name, hardware->h_lladdr, sizeof(hardware->h_lladdr));
+               LOCAL_CHASSIS(cfg)->c_id = name;
+               LOCAL_CHASSIS(cfg)->c_id_len = sizeof(hardware->h_lladdr);
+               LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
+               return;
+       }
+}
+
+#ifndef IN_IS_ADDR_LOOPBACK
+#define IN_IS_ADDR_LOOPBACK(a) ((a)->s_addr == htonl(INADDR_LOOPBACK))
+#endif
+#ifndef IN_IS_ADDR_GLOBAL
+#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))
+#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
+interfaces_helper_mgmt(struct lldpd *cfg,
+    struct interfaces_address_list *addrs)
+{
+       struct interfaces_address *addr;
+       char addrstrbuf[INET6_ADDRSTRLEN];
+       struct lldpd_mgmt *mgmt;
+       void *sin_addr_ptr;
+       size_t sin_addr_size;
+       int af;
+       int allnegative = 0;
+
+       lldpd_chassis_mgmt_cleanup(LOCAL_CHASSIS(cfg));
+
+       /* Is the pattern provided all negative? */
+       if (cfg->g_config.c_mgmt_pattern == NULL) allnegative = 1;
+       else if (cfg->g_config.c_mgmt_pattern[0] == '!') {
+               /* If each comma is followed by '!', its an all
+                  negative pattern */
+               char *sep = cfg->g_config.c_mgmt_pattern;
+               while ((sep = strchr(sep, ',')) &&
+                      (*(++sep) == '!'));
+               if (sep == NULL) allnegative = 1;
+       }
+
+       /* Find management addresses */
+       for (af = LLDPD_AF_UNSPEC + 1; af != LLDPD_AF_LAST; af++) {
+               /* We only take one of each address family, unless a
+                  pattern is provided and is not all negative. For
+                  example !*:*,!10.* will only blacklist
+                  addresses. We will pick the first IPv4 address not
+                  matching 10.*. */
+               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 *)&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 *)&addr->address)->sin6_addr;
+                               sin_addr_size = sizeof(struct in6_addr);
+                               if (!IN6_IS_ADDR_GLOBAL((struct in6_addr *)sin_addr_ptr))
+                                       continue;
+                               break;
+                       default:
+                               assert(0);
+                               continue;
+                       }
+                       if (inet_ntop(lldpd_af(af), sin_addr_ptr,
+                               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,
+                                                       addr->index);
+                               if (mgmt == NULL) {
+                                       assert(errno == ENOMEM); /* anything else is a bug */
+                                       log_warn("interfaces", "out of memory error");
+                                       return;
+                               }
+                               log_debug("interfaces", "add management address %s", addrstrbuf);
+                               TAILQ_INSERT_TAIL(&LOCAL_CHASSIS(cfg)->c_mgmt, mgmt, m_entries);
+
+                               /* Don't take additional address if the pattern is all negative. */
+                               if (allnegative) break;
+                       }
+               }
+       }
+}
+
+/* Fill up port name and description */
+void
+interfaces_helper_port_name_desc(struct lldpd_hardware *hardware,
+    struct interfaces_device *iface)
+{
+       struct lldpd_port *port = &hardware->h_lport;
+
+       /* There are two cases:
+
+            1. We have a kernel recent enough to support ifAlias
+            _and_ a non empty ifAlias, then we will use it for
+            description and use ifname for port ID.
+
+            2. Otherwise, we will use the MAC address as ID and the
+            port name in description.
+       */
+
+       if (iface->alias == NULL || strlen(iface->alias) == 0) {
+               /* Case 2: MAC address and port name */
+               log_debug("interfaces", "use ifname and MAC address for %s",
+                   hardware->h_ifname);
+               port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
+               if ((port->p_id =
+                       calloc(1, sizeof(hardware->h_lladdr))) == NULL)
+                       fatal("interfaces", NULL);
+               memcpy(port->p_id, hardware->h_lladdr,
+                   sizeof(hardware->h_lladdr));
+               port->p_id_len = sizeof(hardware->h_lladdr);
+               port->p_descr = strdup(hardware->h_ifname);
+               return;
+       }
+       /* Case 1: port name and port description */
+       log_debug("interfaces", "use ifname and ifalias for %s",
+           hardware->h_ifname);
+       port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
+       port->p_id_len = strlen(hardware->h_ifname);
+       if ((port->p_id =
+               calloc(1, port->p_id_len)) == NULL)
+               fatal("interfaces", NULL);
+       memcpy(port->p_id, hardware->h_ifname, port->p_id_len);
+       port->p_descr = strdup(iface->alias);
+}
+
+void
+interfaces_helper_physical(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces)
+{
+       struct interfaces_device *iface;
+       struct lldpd_hardware *hardware;
+
+       TAILQ_FOREACH(iface, interfaces, next) {
+               if (!(iface->type & IFACE_PHYSICAL_T)) continue;
+               if (!iface->flags) continue;
+
+               log_debug("interfaces", "%s is an acceptable ethernet device",
+                   iface->name);
+               if ((hardware = lldpd_get_hardware(cfg,
+                           iface->name,
+                           iface->index,
+                           &eth_ops)) == NULL) {
+                       if  ((hardware = lldpd_alloc_hardware(cfg,
+                                   iface->name,
+                                   iface->index)) == NULL) {
+                               log_warnx("interfaces", "Unable to allocate space for %s",
+                                   iface->name);
+                               continue;
+                       }
+                       if (iface_eth_init(cfg, hardware) != 0) {
+                               log_warn("interfaces",
+                                   "unable to initialize %s",
+                                   hardware->h_ifname);
+                               lldpd_hardware_cleanup(cfg, hardware);
+                               continue;
+                       }
+                       hardware->h_ops = &eth_ops;
+                       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 = 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, iface->address, ETHER_ADDR_LEN);
+
+               /* Fill information about port */
+               interfaces_helper_port_name_desc(hardware, iface);
+
+               /* Fill additional info */
+               hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
+       }
+}
index ccbee06b64dee33b9552f08dfb8313a14983c5e6..cd6e63721d321a7650d9e7e80c9b061e6157af8a 100644 (file)
@@ -209,35 +209,127 @@ int       receive_fd(int);
 void    send_fd(int, int);
 
 /* interfaces-*.c */
-void     interfaces_update(struct lldpd *);
 
-#ifdef HOST_OS_LINUX
-/* netlink stuff */
-struct netlink_interface {
-       TAILQ_ENTRY(netlink_interface) next;
+/* BPF filter to get revelant information from interfaces */
+/* LLDP: "ether proto 0x88cc and ether dst 01:80:c2:00:00:0e" */
+/* FDP: "ether dst 01:e0:52:cc:cc:cc" */
+/* CDP: "ether dst 01:00:0c:cc:cc:cc" */
+/* SONMP: "ether dst 01:00:81:00:01:00" */
+/* EDP: "ether dst 00:e0:2b:00:00:00" */
+/* For optimization purpose, we first check if the first bit of the
+   first byte is 1. if not, this can only be an EDP packet:
+
+   tcpdump -dd "(ether[0] & 1 = 1 and
+                 ((ether proto 0x88cc and ether dst 01:80:c2:00:00:0e) or
+                  (ether dst 01:e0:52:cc:cc:cc) or
+                  (ether dst 01:00:0c:cc:cc:cc) or
+                  (ether dst 01:00:81:00:01:00))) or
+                (ether dst 00:e0:2b:00:00:00)"
+*/
+
+#define LLDPD_FILTER_F                         \
+       { 0x30, 0, 0, 0x00000000 },             \
+       { 0x54, 0, 0, 0x00000001 },             \
+       { 0x15, 0, 14, 0x00000001 },            \
+       { 0x28, 0, 0, 0x0000000c },             \
+       { 0x15, 0, 4, 0x000088cc },             \
+       { 0x20, 0, 0, 0x00000002 },             \
+       { 0x15, 0, 2, 0xc200000e },             \
+       { 0x28, 0, 0, 0x00000000 },             \
+       { 0x15, 12, 13, 0x00000180 },           \
+       { 0x20, 0, 0, 0x00000002 },             \
+       { 0x15, 0, 2, 0x52cccccc },             \
+       { 0x28, 0, 0, 0x00000000 },             \
+       { 0x15, 8, 9, 0x000001e0 },             \
+       { 0x15, 1, 0, 0x0ccccccc },             \
+       { 0x15, 0, 2, 0x81000100 },             \
+       { 0x28, 0, 0, 0x00000000 },             \
+       { 0x15, 4, 5, 0x00000100 },             \
+       { 0x20, 0, 0, 0x00000002 },             \
+       { 0x15, 0, 3, 0x2b000000 },             \
+       { 0x28, 0, 0, 0x00000000 },             \
+       { 0x15, 0, 1, 0x000000e0 },             \
+       { 0x6, 0, 0, 0x0000ffff },              \
+       { 0x6, 0, 0, 0x00000000 },
+
+/* This function is responsible to refresh information about interfaces. It is
+ * OS specific but should be present for each OS. It can use the functions in
+ * `interfaces.c` as helper by providing a list of OS-independent interface
+ * devices. */
+void     interfaces_update(struct lldpd *);
+int      interfaces_set_filter(const char *name, int fd);
+
+/* interfaces.c */
+/* An interface cannot be both physical and (bridge or bond or vlan) */
+#define IFACE_PHYSICAL_T (1 << 0) /* Physical interface */
+#define IFACE_BRIDGE_T   (1 << 1) /* Bridge interface */
+#define IFACE_BOND_T     (1 << 2) /* Bond interface */
+#define IFACE_VLAN_T     (1 << 3) /* VLAN interface */
+#define IFACE_WIRELESS_T (1 << 4) /* Wireless interface */
+struct interfaces_device {
+       TAILQ_ENTRY(interfaces_device) next;
        int   index;            /* Index */
        char *name;             /* Name */
        char *alias;            /* Alias */
-       int   flags;            /* Flags */
-       int   mtu;              /* MTU */
        char *address;          /* MAC address */
-       int   type;             /* Type (ARPHDR_*) */
-       int   link;             /* Support interface */
-       int   master;           /* Master interface */
-       int   txqueue;          /* TX queue len */
+       char *driver;           /* Driver (for whitelisting purpose) */
+       int   flags;            /* Flags (IFF_*) */
+       int   mtu;              /* MTU */
+       int   type;             /* Type (see IFACE_*_T) */
+       int   vlanid;           /* If a VLAN, what is the VLAN ID? */
+       struct interfaces_device *lower; /* Lower interface (for a VLAN for example) */
+       struct interfaces_device *upper; /* Upper interface (for a bridge or a bond) */
+
+       /* The following are OS specific. Should be static (no free function) */
+#ifdef HOST_OS_LINUX
+       int lower_idx;          /* Index to lower interface */
+       int upper_idx;          /* Index to upper interface */
+       int txqueue;            /* Transmit queue length */
+#endif
 };
-struct netlink_address {
-       TAILQ_ENTRY(netlink_address) next;
-       int index;              /* Index */
-       int flags;              /* Flags */
+struct interfaces_address {
+       TAILQ_ENTRY(interfaces_address) next;
+       int index;                       /* Index */
+       int flags;                       /* Flags */
        struct sockaddr_storage address; /* Address */
+
+       /* The following are OS specific. */
+       /* Nothing yet. */
 };
-TAILQ_HEAD(netlink_interface_list, netlink_interface);
-TAILQ_HEAD(netlink_address_list, netlink_address);
-struct netlink_interface_list *netlink_get_interfaces(void);
-struct netlink_address_list *netlink_get_addresses(void);
-void netlink_free_interfaces(struct netlink_interface_list *);
-void netlink_free_addresses(struct netlink_address_list *);
+TAILQ_HEAD(interfaces_device_list,  interfaces_device);
+TAILQ_HEAD(interfaces_address_list, interfaces_address);
+void interfaces_free_device(struct interfaces_device *);
+void interfaces_free_address(struct interfaces_address *);
+void interfaces_free_devices(struct interfaces_device_list *);
+void interfaces_free_addresses(struct interfaces_address_list *);
+struct interfaces_device* interfaces_indextointerface(
+       struct interfaces_device_list *,
+       int);
+struct interfaces_device* interfaces_nametointerface(
+       struct interfaces_device_list *,
+       const char *);
+
+void interfaces_helper_whitelist(struct lldpd *,
+    struct interfaces_device_list *);
+void interfaces_helper_chassis(struct lldpd *,
+    struct interfaces_device_list *);
+void interfaces_helper_physical(struct lldpd *,
+    struct interfaces_device_list *);
+void interfaces_helper_port_name_desc(struct lldpd_hardware *,
+    struct interfaces_device *);
+void interfaces_helper_mgmt(struct lldpd *,
+    struct interfaces_address_list *);
+#ifdef ENABLE_DOT1
+void interfaces_helper_vlan(struct lldpd *,
+    struct interfaces_device_list *);
+#endif
+
+void interfaces_setup_multicast(struct lldpd *, const char *, int);
+
+#ifdef HOST_OS_LINUX
+/* netlink.c */
+struct interfaces_device_list  *netlink_get_interfaces(void);
+struct interfaces_address_list *netlink_get_addresses(void);
 #endif
 
 #endif /* _LLDPD_H */
index 898ee1c823b6a358713d5c7aa627641943d5db83..595dc0ab29082ee620e1834c425a32f4ca150e6b 100644 (file)
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+/* Grabbing interfaces information with netlink only. */
+
 #include "lldpd.h"
 
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <net/if_arp.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 
@@ -110,59 +113,6 @@ netlink_send(int s, int type, int family)
     return 0;
 }
 
-/**
- * Free an interface.
- *
- * @param iff interface to be freed
- */
-static void
-netlink_free_interface(struct netlink_interface *iff)
-{
-    if (!iff) return;
-    free(iff->name);
-    free(iff->alias);
-    free(iff->address);
-    free(iff);
-}
-
-/**
- * Free a list of interfaces.
- *
- * @param ifs list of interfaces to be freed
- */
-void
-netlink_free_interfaces(struct netlink_interface_list *ifs)
-{
-    struct netlink_interface *iff, *iff_next;
-    if (!ifs) return;
-    for (iff = TAILQ_FIRST(ifs);
-         iff != NULL;
-         iff = iff_next) {
-        iff_next = TAILQ_NEXT(iff, next);
-        netlink_free_interface(iff);
-    }
-    free(ifs);
-}
-
-/**
- * Free a list of addresses.
- *
- * @param ifaddrs list of addresses
- */
-void
-netlink_free_addresses(struct netlink_address_list *ifaddrs)
-{
-    struct netlink_address *ifa, *ifa_next;
-    if (!ifaddrs) return;
-    for (ifa = TAILQ_FIRST(ifaddrs);
-         ifa != NULL;
-         ifa = ifa_next) {
-        ifa_next = TAILQ_NEXT(ifa, next);
-        free(ifa);
-    }
-    free(ifaddrs);
-}
-
 /**
  * Parse a `link` netlink message.
  *
@@ -172,7 +122,7 @@ netlink_free_addresses(struct netlink_address_list *ifaddrs)
  */
 static int
 netlink_parse_link(struct nlmsghdr *msg,
-    struct netlink_interface *iff)
+    struct interfaces_device *iff)
 {
     struct ifinfomsg *ifi;
     struct rtattr *attribute;
@@ -180,10 +130,21 @@ netlink_parse_link(struct nlmsghdr *msg,
     ifi = NLMSG_DATA(msg);
     len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));
 
+    if (!(ifi->ifi_flags & IFF_UP)) {
+        log_debug("netlink", "skip down interface at index %d",
+          ifi->ifi_index);
+        return -1;
+    }
+    if (ifi->ifi_type != ARPHRD_ETHER) {
+        log_debug("netlink", "skip non Ethernet interface at index %d",
+          ifi->ifi_index);
+        return -1;
+    }
+
     iff->index = ifi->ifi_index;
-    iff->type  = ifi->ifi_type;
     iff->flags = ifi->ifi_flags;
-    iff->link  = -1;
+    iff->lower_idx = -1;
+    iff->upper_idx = -1;
 
     for (attribute = IFLA_RTA(ifi);
          RTA_OK(attribute, len);
@@ -204,12 +165,12 @@ netlink_parse_link(struct nlmsghdr *msg,
                 memcpy(iff->address, RTA_DATA(attribute), RTA_PAYLOAD(attribute));
             break;
         case IFLA_LINK:
-            /* Index of "inferior" interface */
-            iff->link = *(int*)RTA_DATA(attribute);
+            /* Index of "lower" interface */
+            iff->lower_idx = *(int*)RTA_DATA(attribute);
             break;
         case IFLA_MASTER:
             /* Index of master interface */
-            iff->master = *(int*)RTA_DATA(attribute);
+            iff->upper_idx = *(int*)RTA_DATA(attribute);
             break;
         case IFLA_TXQLEN:
             /* Transmit queue length */
@@ -220,7 +181,7 @@ netlink_parse_link(struct nlmsghdr *msg,
             iff->mtu = *(int*)RTA_DATA(attribute);
             break;
         default:
-            log_debug("netlink", "unhandled attribute type %d for iface %s",
+            log_debug("netlink", "unhandled link attribute type %d for iface %s",
                       attribute->rta_type, iff->name ? iff->name : "(unknown)");
             break;
         }
@@ -242,7 +203,7 @@ netlink_parse_link(struct nlmsghdr *msg,
  */
 static int
 netlink_parse_address(struct nlmsghdr *msg,
-    struct netlink_address *ifa)
+    struct interfaces_address *ifa)
 {
     struct ifaddrmsg *ifi;
     struct rtattr *attribute;
@@ -280,7 +241,7 @@ netlink_parse_address(struct nlmsghdr *msg,
             }
             break;
         default:
-            log_debug("netlink", "unhandled attribute type %d for iface %d",
+            log_debug("netlink", "unhandled address attribute type %d for iface %d",
                       attribute->rta_type, ifa->index);
             break;
         }
@@ -303,14 +264,14 @@ netlink_parse_address(struct nlmsghdr *msg,
  */
 static int
 netlink_recv(int s,
-  struct netlink_interface_list *ifs,
-  struct netlink_address_list *ifas)
+  struct interfaces_device_list *ifs,
+  struct interfaces_address_list *ifas)
 {
     char reply[NETLINK_BUFFER] __attribute__ ((aligned));
     int  end = 0;
 
-    struct netlink_interface *iff;
-    struct netlink_address *ifa;
+    struct interfaces_device *iff;
+    struct interfaces_address *ifa;
 
     while (!end) {
         int len;
@@ -344,7 +305,7 @@ netlink_recv(int s,
             case RTM_NEWLINK:
                 if (!ifs) break;
                 log_debug("netlink", "received link information");
-                iff = calloc(1, sizeof(struct netlink_interface));
+                iff = calloc(1, sizeof(struct interfaces_device));
                 if (iff == NULL) {
                     log_warn("netlink", "not enough memory for another interface, give what we have");
                     return 0;
@@ -352,12 +313,12 @@ netlink_recv(int s,
                 if (netlink_parse_link(msg, iff) == 0)
                     TAILQ_INSERT_TAIL(ifs, iff, next);
                 else
-                    netlink_free_interface(iff);
+                    interfaces_free_device(iff);
                 break;
             case RTM_NEWADDR:
                 if (!ifas) break;
                 log_debug("netlink", "received address information");
-                ifa = calloc(1, sizeof(struct netlink_address));
+                ifa = calloc(1, sizeof(struct interfaces_address));
                 if (ifa == NULL) {
                     log_warn("netlink", "not enough memory for another address, give what we have");
                     return 0;
@@ -365,7 +326,7 @@ netlink_recv(int s,
                 if (netlink_parse_address(msg, ifa) == 0)
                     TAILQ_INSERT_TAIL(ifas, ifa, next);
                 else
-                    free(ifa);
+                    interfaces_free_address(ifa);
                 break;
             default:
                 log_debug("netlink",
@@ -382,11 +343,12 @@ netlink_recv(int s,
  *
  * @return a list of interfaces.
  */
-struct netlink_interface_list*
+struct interfaces_device_list*
 netlink_get_interfaces()
 {
     int s;
-    struct netlink_interface_list *ifs;
+    struct interfaces_device_list *ifs;
+    struct interfaces_device *iface1, *iface2;
 
     if ((s = netlink_connect(NETLINK_ROUTE)) == -1)
         return NULL;
@@ -396,7 +358,7 @@ netlink_get_interfaces()
     }
 
     log_debug("netlink", "get the list of available interfaces");
-    ifs = malloc(sizeof(struct netlink_interface_list));
+    ifs = malloc(sizeof(struct interfaces_device_list));
     if (ifs == NULL) {
         log_warn("netlink", "not enough memory for interface list");
         return NULL;
@@ -404,6 +366,24 @@ netlink_get_interfaces()
     TAILQ_INIT(ifs);
     netlink_recv(s, ifs, NULL);
 
+    /* Fill out lower/upper */
+    TAILQ_FOREACH(iface1, ifs, next) {
+        if (iface1->upper_idx != -1 && iface1->upper_idx != iface1->index)
+            TAILQ_FOREACH(iface2, ifs, next) {
+                if (iface1->upper_idx == iface2->index) {
+                    iface1->upper = iface2;
+                    break;
+                }
+            }
+        if (iface1->lower_idx != -1 && iface1->lower_idx != iface1->index)
+            TAILQ_FOREACH(iface2, ifs, next) {
+                if (iface1->lower_idx == iface2->index) {
+                    iface1->lower = iface2;
+                    break;
+                }
+            }
+    }
+
     close(s);
     return ifs;
 }
@@ -413,11 +393,11 @@ netlink_get_interfaces()
  *
  * @return a list of addresses.
  */
-struct netlink_address_list*
+struct interfaces_address_list*
 netlink_get_addresses()
 {
     int s;
-    struct netlink_address_list *ifaddrs;
+    struct interfaces_address_list *ifaddrs;
 
     if ((s = netlink_connect(NETLINK_ROUTE)) == -1)
         return NULL;
@@ -427,7 +407,7 @@ netlink_get_addresses()
     }
 
     log_debug("netlink", "get the list of available addresses");
-    ifaddrs = malloc(sizeof(struct netlink_address_list));
+    ifaddrs = malloc(sizeof(struct interfaces_address_list));
     if (ifaddrs == NULL) {
         log_warn("netlink", "not enough memory for address list");
         return NULL;