From 01293dc402ebc413a84a1d555b4531ad1b165143 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 1 Dec 2015 12:36:14 +0100 Subject: [PATCH] interfaces: fallback to old method to get original MAC on bond The new method may be unsucessful when the lower layer is a veth interface. --- NEWS | 3 +- src/daemon/interfaces-linux.c | 99 +++++++++++++++++++++++++++++++++-- src/daemon/priv-linux.c | 2 + src/daemon/usr.sbin.lldpd.in | 2 + 4 files changed, 100 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 55718b29..bce629f3 100644 --- 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". diff --git a/src/daemon/interfaces-linux.c b/src/daemon/interfaces-linux.c index 733d49fe..24805ca9 100644 --- a/src/daemon/interfaces-linux.c +++ b/src/daemon/interfaces-linux.c @@ -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, diff --git a/src/daemon/priv-linux.c b/src/daemon/priv-linux.c index 0ae9a8af..a21e1220 100644 --- a/src/daemon/priv-linux.c +++ b/src/daemon/priv-linux.c @@ -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", diff --git a/src/daemon/usr.sbin.lldpd.in b/src/daemon/usr.sbin.lldpd.in index 8d313f5a..d459cd44 100644 --- a/src/daemon/usr.sbin.lldpd.in +++ b/src/daemon/usr.sbin.lldpd.in @@ -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, } -- 2.39.5