From: Vincent Bernat Date: Tue, 1 Dec 2015 08:01:17 +0000 (+0100) Subject: interfaces: query permanent MAC address through ethtool X-Git-Tag: 0.8.0~30 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d535fe053e0dee82199a1630c95512acbcc1e3ce;p=thirdparty%2Flldpd.git interfaces: query permanent MAC address through ethtool This is possible since 2.6.14. No need to parse a file. We still need to do that as root as this is only allowed for mere mortals since 2.6.19 (and we would like to support 2.6.18). As a side note, it's possible to query settings since 2.6.36 (so priv_ethtool is still needed). --- diff --git a/NEWS b/NEWS index 973e49e8..55718b29 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ lldpd (0.8.0) + Change the numeric value for LLDP-MED policy L2 priority value 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. + 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 8ac58c15..afad6485 100644 --- a/src/daemon/interfaces-linux.c +++ b/src/daemon/interfaces-linux.c @@ -237,88 +237,23 @@ iflinux_get_permanent_mac(struct lldpd *cfg, 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]; + /* We could do that for any interface, but let's do that only for + * aggregates. */ if ((master = iface->upper) == NULL || master->type != IFACE_BOND_T) return; log_debug("interfaces", "get MAC address for %s", 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", - 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); + if (priv_iface_mac(iface->name, mac, ETHER_ADDR_LEN) != 0) { + log_warnx("interfaces", "unable to get permanent MAC address for %s", + iface->name); 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); + memcpy(iface->address, mac, + ETHER_ADDR_LEN); } /* Fill up MAC/PHY for a given hardware port */ diff --git a/src/daemon/lldpd.h b/src/daemon/lldpd.h index 07b93569..06ecf3bf 100644 --- a/src/daemon/lldpd.h +++ b/src/daemon/lldpd.h @@ -200,6 +200,8 @@ int priv_open(char*); void asroot_open(void); int priv_ethtool(char*, void*, size_t); void asroot_ethtool(void); +int priv_iface_mac(char*, void*, size_t); +void asroot_iface_mac(void); #endif int priv_iface_init(int, char *); int asroot_iface_init_os(int, char *, int *); @@ -220,6 +222,7 @@ enum priv_cmd { PRIV_IFACE_MULTICAST, PRIV_IFACE_DESCRIPTION, PRIV_IFACE_PROMISC, + PRIV_IFACE_MAC, PRIV_SNMP_SOCKET, }; diff --git a/src/daemon/priv-linux.c b/src/daemon/priv-linux.c index b919ba1b..0ae9a8af 100644 --- a/src/daemon/priv-linux.c +++ b/src/daemon/priv-linux.c @@ -53,7 +53,7 @@ priv_open(char *file) return receive_fd(PRIV_UNPRIVILEGED); } -/* Proxy for ethtool ioctl */ +/* Proxy for ethtool ioctl (GSET only) */ int priv_ethtool(char *ifname, void *ethc, size_t length) { @@ -71,13 +71,29 @@ priv_ethtool(char *ifname, void *ethc, size_t length) return rc; } +/* Proxy to get permanent MAC address */ +int +priv_iface_mac(char *ifname, void *mac, size_t length) +{ + int rc, len; + enum priv_cmd cmd = PRIV_IFACE_MAC; + must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd)); + len = strlen(ifname); + must_write(PRIV_UNPRIVILEGED, &len, sizeof(int)); + must_write(PRIV_UNPRIVILEGED, ifname, len); + priv_wait(); + must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int)); + if (rc != 0) + return rc; + must_read(PRIV_UNPRIVILEGED, mac, length); + return rc; +} + void 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", @@ -159,6 +175,40 @@ asroot_ethtool() must_write(PRIV_PRIVILEGED, ðc, sizeof(struct ethtool_cmd)); } +void +asroot_iface_mac() +{ + struct ifreq ifr = {}; + int len, rc, sock = -1; + char *ifname; + struct ethtool_perm_addr *epaddr; + + must_read(PRIV_PRIVILEGED, &len, sizeof(int)); + if ((ifname = (char*)malloc(len + 1)) == NULL) + fatal("privsep", NULL); + must_read(PRIV_PRIVILEGED, ifname, len); + ifname[len] = '\0'; + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + free(ifname); + + if ((epaddr = malloc(sizeof(struct ethtool_perm_addr) + ETHER_ADDR_LEN)) == NULL) + fatal("privsep", NULL); + epaddr->cmd = ETHTOOL_GPERMADDR; + epaddr->size = ETHER_ADDR_LEN; + ifr.ifr_data = (caddr_t)epaddr; + if (((rc = sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) || + (rc = ioctl(sock, SIOCETHTOOL, &ifr)) != 0) { + if (sock != -1) close(sock); + free(epaddr); + must_write(PRIV_PRIVILEGED, &rc, sizeof(int)); + return; + } + close(sock); + must_write(PRIV_PRIVILEGED, &rc, sizeof(int)); + must_write(PRIV_PRIVILEGED, epaddr->data, ETHER_ADDR_LEN); + free(epaddr); +} + int asroot_iface_init_os(int ifindex, char *name, int *fd) { diff --git a/src/daemon/priv.c b/src/daemon/priv.c index 5345f230..321e365b 100644 --- a/src/daemon/priv.c +++ b/src/daemon/priv.c @@ -388,6 +388,7 @@ static struct dispatch_actions actions[] = { #ifdef HOST_OS_LINUX {PRIV_OPEN, asroot_open}, {PRIV_ETHTOOL, asroot_ethtool}, + {PRIV_IFACE_MAC, asroot_iface_mac}, #endif {PRIV_IFACE_INIT, asroot_iface_init}, {PRIV_IFACE_MULTICAST, asroot_iface_multicast}, diff --git a/src/daemon/usr.sbin.lldpd.in b/src/daemon/usr.sbin.lldpd.in index d459cd44..8d313f5a 100644 --- a/src/daemon/usr.sbin.lldpd.in +++ b/src/daemon/usr.sbin.lldpd.in @@ -58,8 +58,6 @@ # 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, }