Common storage of original MAC address
Pokala, Ravi
rpokala at panasas.com
Wed Aug 27 06:35:27 UTC 2014
-----Original Message-----
From: <Pokala>, Ravi Pokala <rpokala at panasas.com>
Date: Monday, August 18, 2014 at 10:19 PM
To: Ryan Stone <rysto32 at gmail.com>
Cc: Brooks Davis <brooks at freebsd.org>, "freebsd-hackers at freebsd.org"
<freebsd-hackers at freebsd.org>
Subject: Re: Common storage of original MAC address
> -----Original Message-----
> From: Ryan Stone <rysto32 at gmail.com>
> Date: Monday, August 18, 2014 at 11:14 AM
> To: Ravi Pokala <rpokala at panasas.com>
> Cc: Brooks Davis <brooks at freebsd.org>, "freebsd-hackers at freebsd.org"
> <freebsd-hackers at freebsd.org>
> Subject: Re: Common storage of original MAC address
>
>> On Mon, Aug 18, 2014 at 11:59 AM, Pokala, Ravi <rpokala at panasas.com>
>> wrote:
>>
>>> ...
>>
>> Personally I think that it would be better to save it in binary format
>> and convert it to a string as needed.
>
> I'm fine with that too; the reason I suggested a string is because that's
> what's already getting passed to ether_ifattach(). I suppose it's easy
> enough to run the string through ether_aton() to get the binary version.
Actually, it turns out ether_aton() is defined in <net/ethernet.h>... but
only for userspace. But that's okay - the 'lla' passed to ether_ifattach()
isn't a string, it's a byte array; I mis-read it. So, a simple bcopy()
DTRT.
> But again, not everything is Ethernet, so we might need to have different
> binary representations; if we're doing that, we might as well just use
> the string version, and let the caller decide how to parse the string.
> (I'm specifically thinking about IP-over-Infiniband - while I'm sure IB
> cards must have some type of hardware address, it's probably not the same
> format as an Ethernet MAC address.)
I was right and wrong - the IB hardware address format is different from
the Ethernet MAC address format:
sys/ofed/drivers/net/mlx4/mlx4_en.h
485 struct mlx4_en_priv {
...
524 u64 mac;
But the OFED driver maps the IB hardware address to an Ethernet MAC
address:
sys/ofed/drivers/net/mlx4/en_netdev.c: mlx4_en_init_netdev()
...
1530 struct mlx4_en_priv *priv;
1531 uint8_t dev_addr[ETHER_ADDR_LEN];
...
1642 /* Set default MAC */
1643 for (i = 0; i < ETHER_ADDR_LEN; i++)
1644 dev_addr[ETHER_ADDR_LEN - 1 - i] = (u8) (priv->mac >> (8 * i));
1645
1646 ether_ifattach(dev, dev_addr);
So just using a 'struct ether_addr' seems to be fine.
>> It would be useful to have the MAC address saved aside somewhere so that
>> a "Restore MAC to HW default" ioctl could be implemented; this would be
>> useful in the if_lagg driver to restore the MAC on a port after it has
>> been removed from a lagg.
Doesn't this restore the original MAC on LAGG teardown?
sys/net/if_lagg.c: lagg_port_destroy()
729 /*
730 * Remove multicast addresses and interface flags from this port and
731 * reset the MAC address, skip if the interface is being detached.
732 */
733 if (!lp->lp_detaching) {
734 lagg_ether_cmdmulti(lp, 0);
735 lagg_setflags(lp, 0);
736 lagg_port_lladdr(lp, lp->lp_lladdr);
737 }
If someone can enumerate the places where we want to restore the original
LLADDR but currently don't, then I'll add that.
> Or how about an ioctl to get the original MAC (rather than a sysctl).
> Then the "restore to default" code would be a two-step process - get the
> original MAC with the new ioctl (say "SIOCGHWLLADDR" for "Get Hardware
> LLADDR"?), and then set the working MAC to that value w/ the existing
> ioctl (SIOCSIFLLADDR).
>
> I actually like this idea more than the sysctl, because it could be done
> in one place (probably in net/if.c, next to if_setlladdr() (which is what
> implements the guts of SIOCSIFLLADDR)).
I've done the easy stuff - added the field to 'struct arpcom', populated
it, added the ioctl and a function to implement it, and created a simple
test program. See the patch and test code (at end of message); the test
output:
root at fbsd-X:~ # ifconfig em1 ether
em1: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 1500
options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
ether 08:00:27:f0:8f:8a
root at fbsd-X:~ # ./ghwlladdr-test em1
em1 current lladdr: 08:00:27:f0:8f:8a
em1 hardware lladdr: 08:00:27:f0:8f:8a
root at fbsd-X:~ # ifconfig em1 ether 12:34:56:78:9a:bc
root at fbsd-X:~ # ifconfig em1 ether
em1: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 1500
options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
ether 12:34:56:78:9a:bc
root at fbsd-X:~ # ./ghwlladdr-test em1
em1 current lladdr: 12:34:56:78:9a:bc
em1 hardware lladdr: 08:00:27:f0:8f:8a
> Does that sound like a plan?
If what I've done so far looks good, I'll continue to the next steps:
changing the places where we want to auto-restore the HW LLADDR (see my
question above), and updating `ifconfig' to report and restore the HW
LLADDR.
Thanks,
Ravi
--------------------------- ghwlladdr.patch ----------------------------
Index: sys/net/if.c
===================================================================
--- sys/net/if.c (revision 270646)
+++ sys/net/if.c (working copy)
@@ -2480,6 +2480,10 @@
EVENTHANDLER_INVOKE(iflladdr_event, ifp);
break;
+ case SIOCGHWLLADDR:
+ error = if_gethwlladdr(ifp, ifr);
+ break;
+
case SIOCAIFGROUP:
{
struct ifgroupreq *ifgr = (struct ifgroupreq *)ifr;
@@ -3353,6 +3357,33 @@
}
/*
+ * Get the link layer address that was read from the hardware at probe.
+ *
+ * At this time we only support certain types of interfaces.
+ */
+int
+if_gethwlladdr(struct ifnet *ifp, struct ifreq *ifr)
+{
+ switch (ifp->if_type) {
+ case IFT_ETHER:
+ case IFT_FDDI:
+ case IFT_XETHER:
+ case IFT_ISO88025:
+ case IFT_L2VLAN:
+ case IFT_BRIDGE:
+ case IFT_ARCNET:
+ case IFT_IEEE8023ADLAG:
+ case IFT_IEEE80211:
+ bcopy(&IFP2AC(ifp)->hw_lladdr, ifr->ifr_addr.sa_data, ETHER_ADDR_LEN);
+ ifr->ifr_addr.sa_len = ETHER_ADDR_LEN;
+ break;
+ default:
+ return (ENODEV);
+ }
+ return (0);
+}
+
+/*
* 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: sys/net/if_arp.h
===================================================================
--- sys/net/if_arp.h (revision 270646)
+++ sys/net/if_arp.h (working copy)
@@ -102,9 +102,11 @@
* Structure shared between the ethernet driver modules and
* the address resolution code.
*/
+#include <net/ethernet.h>
struct arpcom {
struct ifnet *ac_ifp; /* network-visible interface */
void *ac_netgraph; /* ng_ether(4) netgraph node info */
+ struct ether_addr hw_lladdr; /* original lladdr from hardware */
};
#define IFP2AC(ifp) ((struct arpcom *)(ifp->if_l2com))
#define AC2IFP(ac) ((ac)->ac_ifp)
Index: sys/net/if_ethersubr.c
===================================================================
--- sys/net/if_ethersubr.c (revision 270646)
+++ sys/net/if_ethersubr.c (working copy)
@@ -841,6 +841,7 @@
sdl->sdl_type = IFT_ETHER;
sdl->sdl_alen = ifp->if_addrlen;
bcopy(lla, LLADDR(sdl), ifp->if_addrlen);
+ bcopy(lla, &IFP2AC(ifp)->hw_lladdr, ifp->if_addrlen);
bpfattach(ifp, DLT_EN10MB, ETHER_HDR_LEN);
if (ng_ether_attach_p != NULL)
Index: sys/net/if_var.h
===================================================================
--- sys/net/if_var.h (revision 270646)
+++ sys/net/if_var.h (working copy)
@@ -485,6 +485,7 @@
void if_ref(struct ifnet *);
void if_rele(struct ifnet *);
int if_setlladdr(struct ifnet *, const u_char *, int);
+int if_gethwlladdr(struct ifnet *, struct ifreq *);
void if_up(struct ifnet *);
int ifioctl(struct socket *, u_long, caddr_t, struct thread *);
int ifpromisc(struct ifnet *, int);
Index: sys/sys/sockio.h
===================================================================
--- sys/sys/sockio.h (revision 270646)
+++ sys/sys/sockio.h (working copy)
@@ -96,6 +96,7 @@
#define SIOCGIFSTATUS _IOWR('i', 59, struct ifstat) /* get IF status */
#define SIOCSIFLLADDR _IOW('i', 60, struct ifreq) /* set linklevel addr
*/
+#define SIOCGHWLLADDR _IOWR('i', 61, struct ifreq) /* get hw lladdr */
#define SIOCSIFPHYADDR _IOW('i', 70, struct ifaliasreq) /* set gif
addres */
#define SIOCGIFPSRCADDR _IOWR('i', 71, struct ifreq) /* get gif psrc addr
*/
--------------------------- ghwlladdr-test.c ---------------------------
#include <errno.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
int
main(
int argc,
char **argv)
{
char *ifname = NULL;
struct ifaddrs *ifap = NULL;
struct ifaddrs *ifa = NULL;
struct sockaddr_dl *sdl = NULL;
struct ifreq ifr;
int sock = -1;
int error = 0;
if (argc != 2) {
printf("usage: %s <ifname>\n", argv[0]);
error = 1;
goto cleanup;
}
ifname = argv[1];
error = getifaddrs(&ifap);
if (error != 0) {
error = errno;
printf("getifaddrs(): %d (%s)\n", error, strerror(error));
goto cleanup;
}
for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
if (strcmp(ifa->ifa_name, ifname) == 0) {
sdl = (struct sockaddr_dl *) ifa->ifa_addr;
printf("%s current lladdr: %s\n", ifname,
ether_ntoa((struct ether_addr *) LLADDR(sdl)));
break;
}
}
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;
sock = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0);
if (sock == -1) {
error = errno;
printf("socket(): %d (%s)\n", error, strerror(error));
goto cleanup;
}
error = ioctl(sock, SIOCGHWLLADDR, &ifr);
if (error == -1) {
printf("ioctl(): %d (%s)\n", error, strerror(error));
goto cleanup;
}
printf("%s hardware lladdr: %s\n", ifname,
ether_ntoa((const struct ether_addr *) &ifr.ifr_addr.sa_data));
cleanup:
if (sock != -1) {
close(sock);
}
return error;
}
------------------------------------------------------------------------
More information about the freebsd-hackers
mailing list