]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
interfaces: query permanent MAC address through ethtool
authorVincent Bernat <vincent@bernat.im>
Tue, 1 Dec 2015 08:01:17 +0000 (09:01 +0100)
committerVincent Bernat <vincent@bernat.im>
Tue, 1 Dec 2015 08:01:17 +0000 (09:01 +0100)
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).

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

diff --git a/NEWS b/NEWS
index 973e49e84f6f39686557d4beb8b0c28f4fb7cd19..55718b297f591c576e564336825e4463a99b4237 100644 (file)
--- 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".
index 8ac58c15d06fd9155c857e21a56a5acf9b47c3b0..afad6485258beb2aa7a14b12913d38a03bfda63b 100644 (file)
@@ -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 */
index 07b935696d11f30b98fd850438cc930c2b9087de..06ecf3bf8b14f1f8693f4d3201e5a8a0f49420f3 100644 (file)
@@ -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,
 };
 
index b919ba1bab07ce4eb4ec3eac1fd756a527a93c6c..0ae9a8af0057d17d94cc12d488e3911698b7246d 100644 (file)
@@ -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, &ethc, 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)
 {
index 5345f230f34069db49ec2b95284f0f6e70d1893d..321e365bdf8a2181013b4520ce99174950871a66 100644 (file)
@@ -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},
index d459cd44e07ede420c284f73db4335f199fc559c..8d313f5a05fc049588e1d2e89067f958b91bc2c6 100644 (file)
@@ -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,
 }