]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
netlink: use netlink to retrieve bridge/bond/vlan information
authorVincent Bernat <vincent@bernat.im>
Sat, 25 Jul 2015 15:23:01 +0000 (17:23 +0200)
committerVincent Bernat <vincent@bernat.im>
Sat, 25 Jul 2015 15:35:39 +0000 (17:35 +0200)
On Linux, Netlink is more efficient to get this kind of information. Use
it all the time, unless we are configured to run on old kernels. In this
case, fallback to the previous functions.

An old kernel is now < 2.6.32 (RHEL 6, Ubuntu Lucid, Debian Squeeze).

NEWS
configure.ac
src/daemon/interfaces-linux.c
src/daemon/netlink.c

diff --git a/NEWS b/NEWS
index d71485b144257085efa294b547eb4ac4a9edf2e6..ce748c0e7a713950c55db186294b85e94e762fe8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,9 @@
 lldpd (0.7.16)
+  * Change:
+    + For Linux, 2.6.32 is now the minimal required kernel. When using
+      an older kernel, use `--enable-oldies`.
+    + For Linux, use netlink to retrieve information about bridges,
+      VLAN and bonds. The code was contributed by Cumulus Networks.
   * Features:
     + Use symbol versioning for liblldpctl.so.
     + Ability to get local chassis information with "show
index ee8e54f228c40a8a3c6e80cd50b8ef8ba3fff9c5..e1ddb011e819730dbcd23ef34adc778b4781e70b 100644 (file)
@@ -297,7 +297,7 @@ lldp_ARG_ENABLE([dot3], [Dot3 extension (PHY stuff)], [yes])
 lldp_ARG_ENABLE([custom], [Custom TLV support], [yes])
 
 # Oldies
-lldp_ARG_ENABLE([oldies], [compatibility with Linux kernel older than 2.6.18], [no])
+lldp_ARG_ENABLE([oldies], [compatibility with Linux kernel older than 2.6.32], [no])
 
 #######################
 # Output results
index 4bae8a2a10ce90692f4b1078d18249cc05ba22ea..c9342d48a77e3737119985f95b27b41e1052282c 100644 (file)
@@ -111,12 +111,12 @@ static struct lldpd_ops eth_ops = {
        .cleanup = iflinux_eth_close,
 };
 
+#ifdef ENABLE_OLDIES
 static int
 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] = {
@@ -154,16 +154,15 @@ old_iflinux_is_bridge(struct lldpd *cfg,
                }
        }
        return 1;
-#else
-       return 0;
-#endif
 }
+#endif
 
 static int
 iflinux_is_bridge(struct lldpd *cfg,
     struct interfaces_device_list *interfaces,
     struct interfaces_device *iface)
 {
+#ifdef ENABLE_OLDIES
        struct interfaces_device *port;
        char path[SYSFS_PATH_MAX];
        int f;
@@ -193,6 +192,9 @@ iflinux_is_bridge(struct lldpd *cfg,
        }
 
        return 1;
+#else
+       return 0;
+#endif
 }
 
 static int
@@ -200,6 +202,7 @@ iflinux_is_vlan(struct lldpd *cfg,
     struct interfaces_device_list *interfaces,
     struct interfaces_device *iface)
 {
+#ifdef ENABLE_OLDIES
        struct vlan_ioctl_args ifv = {};
        ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
        strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
@@ -228,6 +231,7 @@ iflinux_is_vlan(struct lldpd *cfg,
                iface->vlanid = ifv.u.VID;
                return 1;
        }
+#endif
        return 0;
 }
 
@@ -236,6 +240,7 @@ iflinux_is_bond(struct lldpd *cfg,
     struct interfaces_device_list *interfaces,
     struct interfaces_device *master)
 {
+#ifdef ENABLE_OLDIES
        /* Shortcut if we detect the new team driver. Upper and lower links
         * should already be set with netlink in this case.  */
        if (master->driver && !strcmp(master->driver, "team")) {
@@ -268,6 +273,7 @@ iflinux_is_bond(struct lldpd *cfg,
                }
                return 1;
        }
+#endif
        return 0;
 }
 
index 46d653f99a84e69a4d4f0ba1ce7f50336df684a9..0e3dba8a73c36ac8bca600357a501066761502e2 100644 (file)
@@ -114,6 +114,66 @@ netlink_send(int s, int type, int family)
     return 0;
 }
 
+static void
+netlink_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+       while (RTA_OK(rta, len)) {
+               if ((rta->rta_type <= max) && (!tb[rta->rta_type]))
+                       tb[rta->rta_type] = rta;
+               rta = RTA_NEXT(rta,len);
+       }
+}
+
+/**
+ * Parse a `linkinfo` attributes.
+ *
+ * @param iff where to put the result
+ * @param rta linkinfo attribute
+ * @param len length of attributes
+ */
+static void
+netlink_parse_linkinfo(struct interfaces_device *iff, struct rtattr *rta, int len)
+{
+       struct rtattr *link_info_attrs[IFLA_INFO_MAX+1] = {};
+       char *kind = NULL;
+
+       netlink_parse_rtattr(link_info_attrs, IFLA_INFO_MAX, rta, len);
+
+       if (link_info_attrs[IFLA_INFO_KIND]) {
+               kind = strdup(RTA_DATA(link_info_attrs[IFLA_INFO_KIND]));
+               if (kind) {
+                       if (!strcmp(kind, "vlan")) {
+                               log_debug("netlink", "interface %s is a VLAN",
+                                   iff->name);
+                               iff->type |= IFACE_VLAN_T;
+                       } else if (!strcmp(kind, "bridge")) {
+                               log_debug("netlink", "interface %s is a bridge",
+                                   iff->name);
+                               iff->type |= IFACE_BRIDGE_T;
+                       } else if (!strcmp(kind, "bond")) {
+                               log_debug("netlink", "interface %s is a bond",
+                                   iff->name);
+                               iff->type |= IFACE_BOND_T;
+                       }
+               }
+       }
+
+       if (kind && !strcmp(kind, "vlan") && link_info_attrs[IFLA_INFO_DATA]) {
+               struct rtattr *vlan_link_info_data_attrs[IFLA_VLAN_MAX+1] = {};
+               netlink_parse_rtattr(vlan_link_info_data_attrs, IFLA_VLAN_MAX,
+                   RTA_DATA(link_info_attrs[IFLA_INFO_DATA]),
+                   RTA_PAYLOAD(link_info_attrs[IFLA_INFO_DATA]));
+
+               if (vlan_link_info_data_attrs[IFLA_VLAN_ID]) {
+                       iff->vlanid = *(uint16_t *)RTA_DATA(vlan_link_info_data_attrs[IFLA_VLAN_ID]);
+                       log_debug("netlink", "VLAN ID for interface %s is %d",
+                           iff->name, iff->vlanid);
+               }
+       }
+
+       free(kind);
+}
+
 /**
  * Parse a `link` netlink message.
  *
@@ -181,6 +241,9 @@ netlink_parse_link(struct nlmsghdr *msg,
             /* Maximum Transmission Unit */
             iff->mtu = *(int*)RTA_DATA(attribute);
             break;
+        case IFLA_LINKINFO:
+            netlink_parse_linkinfo(iff, RTA_DATA(attribute), RTA_PAYLOAD(attribute));
+            break;
         default:
             log_debug("netlink", "unhandled link attribute type %d for iface %s",
                       attribute->rta_type, iff->name ? iff->name : "(unknown)");