Index: head/sbin/ifconfig/af_link.c =================================================================== --- head/sbin/ifconfig/af_link.c +++ head/sbin/ifconfig/af_link.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -67,7 +68,7 @@ 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 @@ 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); + } } } Index: head/sys/net/if.c =================================================================== --- head/sys/net/if.c +++ head/sys/net/if.c @@ -744,6 +744,11 @@ /* 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 @@ * 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 @@ 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 @@ } /* + * 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 Index: head/sys/net/if_ethersubr.c =================================================================== --- head/sys/net/if_ethersubr.c +++ head/sys/net/if_ethersubr.c @@ -916,6 +916,8 @@ 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); Index: head/sys/net/if_var.h =================================================================== --- head/sys/net/if_var.h +++ head/sys/net/if_var.h @@ -281,6 +281,7 @@ 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_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); Index: head/sys/sys/sockio.h =================================================================== --- head/sys/sys/sockio.h +++ head/sys/sys/sockio.h @@ -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 */