]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
interfaces: fallback to old method to get original MAC on bond
authorVincent Bernat <vincent@bernat.im>
Tue, 1 Dec 2015 11:36:14 +0000 (12:36 +0100)
committerVincent Bernat <vincent@bernat.im>
Tue, 1 Dec 2015 11:36:14 +0000 (12:36 +0100)
The new method may be unsucessful when the lower layer is a veth
interface.

NEWS
src/daemon/interfaces-linux.c
src/daemon/priv-linux.c
src/daemon/usr.sbin.lldpd.in

diff --git a/NEWS b/NEWS
index 55718b297f591c576e564336825e4463a99b4237..bce629f3736e6d57f5cd684b1773eae1e16434b9 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -7,8 +7,7 @@ lldpd (0.8.0)
       for "Best effort" to 0 to match 802.1D-2004.
   * Change:
     + Retrieve the permanent MAC address of an interface through
-      ethtool for Linux. This is is possible since 2.6.14, so no
-      compatibility layer is kept.
+      ethtool for Linux. Fallback to the old method when not available.
     + Running lldpd with "-d" will keep the process in foreground but
       logs will still go to syslog. To log to the console, add at
       least one "-d".
index 733d49fe2d181cf4ebe6b4ff7a6f3b8fbffbe936..24805ca9eeef28b1ca8286ea644e8ea26ecab5a0 100644 (file)
@@ -231,6 +231,99 @@ iflinux_is_bond(struct lldpd *cfg,
        return 0;
 }
 
+static void
+old_iflinux_get_permanent_mac(struct lldpd *cfg,
+    struct interfaces_device_list *interfaces,
+    struct interfaces_device *iface)
+{
+       struct interfaces_device *master;
+       int f, state = 0;
+       FILE *netbond;
+       const char *slaveif = "Slave Interface: ";
+       const char *hwaddr = "Permanent HW addr: ";
+       u_int8_t mac[ETHER_ADDR_LEN];
+       char path[SYSFS_PATH_MAX];
+       char line[100];
+
+       if ((master = iface->upper) == NULL || master->type != IFACE_BOND_T)
+               return;
+
+       /* We have a bond, we need to query it to get real MAC addresses */
+       if (snprintf(path, SYSFS_PATH_MAX, "/proc/net/bonding/%s",
+               master->name) >= SYSFS_PATH_MAX) {
+               log_warnx("interfaces", "path truncated");
+               return;
+       }
+       if ((f = priv_open(path)) < 0) {
+               if (snprintf(path, SYSFS_PATH_MAX, "/proc/self/net/bonding/%s",
+                       master->name) >= SYSFS_PATH_MAX) {
+                       log_warnx("interfaces", "path truncated");
+                       return;
+               }
+               f = priv_open(path);
+       }
+       if (f < 0) {
+               log_warnx("interfaces",
+                   "unable to find %s in /proc/net/bonding or /proc/self/net/bonding",
+                   master->name);
+               return;
+       }
+       if ((netbond = fdopen(f, "r")) == NULL) {
+               log_warn("interfaces", "unable to read stream from %s", path);
+               close(f);
+               return;
+       }
+       /* State 0:
+          We parse the file to search "Slave Interface: ". If found, go to
+          state 1.
+          State 1:
+          We parse the file to search "Permanent HW addr: ". If found, we get
+          the mac.
+       */
+       while (fgets(line, sizeof(line), netbond)) {
+               switch (state) {
+               case 0:
+                       if (strncmp(line, slaveif, strlen(slaveif)) == 0) {
+                               if (line[strlen(line)-1] == '\n')
+                                       line[strlen(line)-1] = '\0';
+                               if (strcmp(iface->name,
+                                       line + strlen(slaveif)) == 0)
+                                       state++;
+                       }
+                       break;
+               case 1:
+                       if (strncmp(line, hwaddr, strlen(hwaddr)) == 0) {
+                               if (line[strlen(line)-1] == '\n')
+                                       line[strlen(line)-1] = '\0';
+                               if (sscanf(line + strlen(hwaddr),
+                                       "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+                                       &mac[0], &mac[1], &mac[2],
+                                       &mac[3], &mac[4], &mac[5]) !=
+                                   ETHER_ADDR_LEN) {
+                                       log_warn("interfaces", "unable to parse %s",
+                                           line + strlen(hwaddr));
+                                       fclose(netbond);
+                                       return;
+                               }
+                               memcpy(iface->address, mac,
+                                   ETHER_ADDR_LEN);
+                               fclose(netbond);
+                               return;
+                       }
+                       break;
+               }
+       }
+       log_warnx("interfaces", "unable to find real mac address for %s",
+           iface->name);
+       fclose(netbond);
+}
+
+/* Get the permanent MAC address using ethtool. Fallback to /proc/net/bonding if
+ * not possible through ethtool. There is a slight difference between the two
+ * methods. The /proc/net/bonding method will retrieve the MAC address of the
+ * interface before it was added to the bond. The ethtool method will retrieve
+ * the real permanent MAC address. For some devices, there is no such address
+ * (for example, virtual devices like veth). */
 static void
 iflinux_get_permanent_mac(struct lldpd *cfg,
     struct interfaces_device_list *interfaces,
@@ -248,16 +341,14 @@ iflinux_get_permanent_mac(struct lldpd *cfg,
            iface->name);
 
        if (priv_iface_mac(iface->name, mac, ETHER_ADDR_LEN) != 0) {
-               log_warnx("interfaces", "unable to get permanent MAC address for %s",
-                   iface->name);
+               old_iflinux_get_permanent_mac(cfg, interfaces, iface);
                return;
        }
        size_t i;
        for (i = 0; i < ETHER_ADDR_LEN; i++)
                if (mac[i] != 0) break;
        if (i == ETHER_ADDR_LEN) {
-               log_warnx("interfaces", "driver for %s do not provide a permanent MAC address",
-                   iface->name);
+               old_iflinux_get_permanent_mac(cfg, interfaces, iface);
                return;
        }
        memcpy(iface->address, mac,
index 0ae9a8af0057d17d94cc12d488e3911698b7246d..a21e12200494dbc3cf262bfcfdb53f454d825e4e 100644 (file)
@@ -94,6 +94,8 @@ asroot_open()
 {
        const char* authorized[] = {
                "/proc/sys/net/ipv4/ip_forward",
+               "/proc/net/bonding/[^.][^/]*",
+               "/proc/self/net/bonding/[^.][^/]*",
 #ifdef ENABLE_OLDIES
                SYSFS_CLASS_NET "[^.][^/]*/brforward",
                SYSFS_CLASS_NET "[^.][^/]*/brport",
index 8d313f5a05fc049588e1d2e89067f958b91bc2c6..d459cd44e07ede420c284f73db4335f199fc559c 100644 (file)
@@ -58,6 +58,8 @@
 
   # Gather network information
   @{PROC}/sys/net/ipv4/ip_forward r,
+  @{PROC}/net/bonding/* r,
+  @{PROC}/self/net/bonding/* r,
   /sys/devices/virtual/dmi/** r,
   /sys/devices/pci**/net/*/ifalias r,
 }