Author: rpokala
Date: Wed May 10 22:13:47 2017
New Revision: 318160
URL: https://svnweb.freebsd.org/changeset/base/318160

Log:
  Persistently store NIC's hardware MAC address, and add a way to retrive it
  
  The MAC address reported by `ifconfig ${nic} ether' does not always match
  the address in the hardware, as reported by the driver during attach. In
  particular, NICs which are components of a lagg(4) interface all report the
  same MAC.
  
  When attaching, the NIC driver passes the MAC address it read from the
  hardware as an argument to ether_ifattach(). Keep a second copy of it, and
  create ioctl(SIOCGHWADDR) to return it. Teach `ifconfig' to report it along
  with the active MAC address.
  
  PR:           194386
  Reviewed by:  glebius
  MFC after:    1 week
  Sponsored by: Panasas
  Differential Revision:        https://reviews.freebsd.org/D10609

Modified:
  head/sbin/ifconfig/af_link.c
  head/sys/net/if.c
  head/sys/net/if_ethersubr.c
  head/sys/net/if_var.h
  head/sys/sys/sockio.h

Modified: head/sbin/ifconfig/af_link.c
==============================================================================
--- head/sbin/ifconfig/af_link.c        Wed May 10 21:42:16 2017        
(r318159)
+++ head/sbin/ifconfig/af_link.c        Wed May 10 22:13:47 2017        
(r318160)
@@ -42,6 +42,7 @@ static const char rcsid[] =
 #include <stdlib.h>
 #include <string.h>
 #include <ifaddrs.h>
+#include <unistd.h>
 
 #include <net/if_dl.h>
 #include <net/if_types.h>
@@ -67,7 +68,7 @@ link_status(int s __unused, const struct
                    sdl->sdl_alen == ETHER_ADDR_LEN) {
                        ether_format = ether_ntoa((struct ether_addr 
*)LLADDR(sdl));
                        if (f_ether != NULL && strcmp(f_ether, "dash") == 0) {
-                               for (format_char = strchr(ether_format, ':'); 
+                               for (format_char = strchr(ether_format, ':');
                                    format_char != NULL; 
                                    format_char = strchr(ether_format, ':'))
                                        *format_char = '-';
@@ -78,6 +79,48 @@ link_status(int s __unused, const struct
 
                        printf("\tlladdr %s\n", link_ntoa(sdl) + n);
                }
+               /* Best-effort (i.e. failures are silent) to get original
+                * hardware address, as read by NIC driver at attach time. Only
+                * applies to Ethernet NICs (IFT_ETHER). However, laggX
+                * interfaces claim to be IFT_ETHER, and re-type their component
+                * Ethernet NICs as IFT_IEEE8023ADLAG. So, check for both. If
+                * the MAC is zeroed, then it's actually a lagg.
+                */
+               if ((sdl->sdl_type == IFT_ETHER ||
+                   sdl->sdl_type == IFT_IEEE8023ADLAG) &&
+                   sdl->sdl_alen == ETHER_ADDR_LEN) {
+                       struct ifreq ifr;
+                       int sock_hw;
+                       int rc;
+                       static const u_char laggaddr[6] = {0};
+
+                       strncpy(ifr.ifr_name, ifa->ifa_name,
+                           sizeof(ifr.ifr_name));
+                       memcpy(&ifr.ifr_addr, ifa->ifa_addr,
+                           sizeof(ifa->ifa_addr->sa_len));
+                       ifr.ifr_addr.sa_family = AF_LOCAL;
+                       if ((sock_hw = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) {
+                               warn("socket(AF_LOCAL,SOCK_DGRAM)");
+                               return;
+                       }
+                       rc = ioctl(sock_hw, SIOCGHWADDR, &ifr);
+                       close(sock_hw);
+                       if (rc != 0) {
+                               return;
+                       }
+                       if (memcmp(ifr.ifr_addr.sa_data, laggaddr, 
sdl->sdl_alen) == 0) {
+                               return;
+                       }
+                       ether_format = ether_ntoa((const struct ether_addr *)
+                           &ifr.ifr_addr.sa_data);
+                       if (f_ether != NULL && strcmp(f_ether, "dash") == 0) {
+                               for (format_char = strchr(ether_format, ':');
+                                   format_char != NULL; 
+                                   format_char = strchr(ether_format, ':'))
+                                       *format_char = '-';
+                       }
+                       printf("\thwaddr %s\n", ether_format);
+               }
        }
 }
 

Modified: head/sys/net/if.c
==============================================================================
--- head/sys/net/if.c   Wed May 10 21:42:16 2017        (r318159)
+++ head/sys/net/if.c   Wed May 10 22:13:47 2017        (r318160)
@@ -744,6 +744,11 @@ if_attach_internal(struct ifnet *ifp, in
                /* Reliably crash if used uninitialized. */
                ifp->if_broadcastaddr = NULL;
 
+               if (ifp->if_type == IFT_ETHER) {
+                       ifp->if_hw_addr = malloc(ifp->if_addrlen, M_IFADDR,
+                           M_WAITOK | M_ZERO);
+               }
+
 #if defined(INET) || defined(INET6)
                /* Use defaults for TSO, if nothing is set */
                if (ifp->if_hw_tsomax == 0 &&
@@ -1059,6 +1064,8 @@ if_detach_internal(struct ifnet *ifp, in
                 * Remove link ifaddr pointer and maybe decrement if_index.
                 * Clean up all addresses.
                 */
+               free(ifp->if_hw_addr, M_IFADDR);
+               ifp->if_hw_addr = NULL;
                ifp->if_addr = NULL;
 
                /* We can now free link ifaddr. */
@@ -2667,6 +2674,10 @@ ifhwioctl(u_long cmd, struct ifnet *ifp,
                    ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len);
                break;
 
+       case SIOCGHWADDR:
+               error = if_gethwaddr(ifp, ifr);
+               break;
+
        case SIOCAIFGROUP:
        {
                struct ifgroupreq *ifgr = (struct ifgroupreq *)ifr;
@@ -3584,6 +3595,29 @@ if_requestencap_default(struct ifnet *if
 }
 
 /*
+ * Get the link layer address that was read from the hardware at attach.
+ *
+ * This is only set by Ethernet NICs (IFT_ETHER), but laggX interfaces re-type
+ * their component interfaces as IFT_IEEE8023ADLAG.
+ */
+int
+if_gethwaddr(struct ifnet *ifp, struct ifreq *ifr)
+{
+
+       if (ifp->if_hw_addr == NULL)
+               return (ENODEV);
+
+       switch (ifp->if_type) {
+       case IFT_ETHER:
+       case IFT_IEEE8023ADLAG:
+               bcopy(ifp->if_hw_addr, ifr->ifr_addr.sa_data, ifp->if_addrlen);
+               return (0);
+       default:
+               return (ENODEV);
+       }
+}
+
+/*
  * The name argument must be a pointer to storage which will last as
  * long as the interface does.  For physical devices, the result of
  * device_get_name(dev) is a good choice and for pseudo-devices a

Modified: head/sys/net/if_ethersubr.c
==============================================================================
--- head/sys/net/if_ethersubr.c Wed May 10 21:42:16 2017        (r318159)
+++ head/sys/net/if_ethersubr.c Wed May 10 22:13:47 2017        (r318160)
@@ -916,6 +916,8 @@ ether_ifattach(struct ifnet *ifp, const 
        sdl->sdl_alen = ifp->if_addrlen;
        bcopy(lla, LLADDR(sdl), ifp->if_addrlen);
 
+       bcopy(lla, ifp->if_hw_addr, ifp->if_addrlen);
+
        bpfattach(ifp, DLT_EN10MB, ETHER_HDR_LEN);
        if (ng_ether_attach_p != NULL)
                (*ng_ether_attach_p)(ifp);

Modified: head/sys/net/if_var.h
==============================================================================
--- head/sys/net/if_var.h       Wed May 10 21:42:16 2017        (r318159)
+++ head/sys/net/if_var.h       Wed May 10 22:13:47 2017        (r318160)
@@ -281,6 +281,7 @@ struct ifnet {
        struct  ifmultihead if_multiaddrs; /* multicast addresses configured */
        int     if_amcount;             /* number of all-multicast requests */
        struct  ifaddr  *if_addr;       /* pointer to link-level address */
+       void    *if_hw_addr;            /* hardware link-level address */
        const u_int8_t *if_broadcastaddr; /* linklevel broadcast bytestring */
        struct  rwlock if_afdata_lock;
        void    *if_afdata[AF_MAX];
@@ -650,6 +651,7 @@ int if_gethwassist(if_t ifp);
 int if_setsoftc(if_t ifp, void *softc);
 void *if_getsoftc(if_t ifp);
 int if_setflags(if_t ifp, int flags);
+int if_gethwaddr(if_t ifp, struct ifreq *);
 int if_setmtu(if_t ifp, int mtu);
 int if_getmtu(if_t ifp);
 int if_getmtu_family(if_t ifp, int family);

Modified: head/sys/sys/sockio.h
==============================================================================
--- head/sys/sys/sockio.h       Wed May 10 21:42:16 2017        (r318159)
+++ head/sys/sys/sockio.h       Wed May 10 22:13:47 2017        (r318160)
@@ -97,6 +97,7 @@
 #define        SIOCGIFSTATUS   _IOWR('i', 59, struct ifstat)   /* get IF 
status */
 #define        SIOCSIFLLADDR    _IOW('i', 60, struct ifreq)    /* set 
linklevel addr */
 #define        SIOCGI2C        _IOWR('i', 61, struct ifreq)    /* get I2C data 
 */
+#define        SIOCGHWADDR     _IOWR('i', 62, struct ifreq)    /* get hardware 
lladdr */
 
 #define        SIOCSIFPHYADDR   _IOW('i', 70, struct ifaliasreq) /* set gif 
address */
 #define        SIOCGIFPSRCADDR _IOWR('i', 71, struct ifreq)    /* get gif psrc 
addr */
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to