Subject: kern/30665: ifconfig lladdr support for SIOCSIFLLADDR ioctl to change link address
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <richy@fatkid.org>
List: netbsd-bugs
Date: 07/05/2005 06:04:00
>Number:         30665
>Category:       kern
>Synopsis:       ifconfig lladdr support for SIOCSIFLLADDR ioctl to change link address
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Tue Jul 05 06:04:00 +0000 2005
>Originator:     Richy Kim
>Release:        NetBSD-current July 4, 2005
>Organization:
>Environment:
NetBSD chunk 3.99.7 NetBSD 3.99.7 (GENERIC.MP) #3: Mon Jul  4 16:19:19 PDT 2005  richy@chunk:/home/richy/sandbox/netbsd/obj/sys/arch/i386/compile/GENERIC.MP i386
>Description:
Based off of existing patches for FreeBSD and OpenBSD, attached below are the diffs to support SIOCSFLLADDR ioctl functionality into the kernel and the corresponding ifconfig lladdr command to change the ethernet (mac,link) address for a given interface.

-r.
>How-To-Repeat:
N/A
>Fix:

http://fatkid.org/netbsd/netbsd.lladdr.diff
http://fatkid.org/netbsd/netbsd.ifconfig.diff

Patch against -current (as of July 4, 2005)

Index: sys/sys/sockio.h
===================================================================
RCS file: /cvsroot/src/sys/sys/sockio.h,v
retrieving revision 1.22
diff -u -r1.22 sockio.h
--- sys/sys/sockio.h	26 Feb 2005 22:25:34 -0000	1.22
+++ sys/sys/sockio.h	5 Jul 2005 05:51:58 -0000
@@ -81,6 +81,7 @@
 #define	SIOCALIFADDR	 _IOW('i', 28, struct if_laddrreq) /* add IF addr */
 #define	SIOCGLIFADDR	_IOWR('i', 29, struct if_laddrreq) /* get IF addr */
 #define	SIOCDLIFADDR	 _IOW('i', 30, struct if_laddrreq) /* delete IF addr */
+#define SIOCSIFLLADDR   _IOW('i', 31, struct ifreq) /* set link level addr */
 
 #define	SIOCADDMULTI	 _IOW('i', 49, struct ifreq)	/* add m'cast addr */
 #define	SIOCDELMULTI	 _IOW('i', 50, struct ifreq)	/* del m'cast addr */
Index: sys/net/if.c
===================================================================
RCS file: /cvsroot/src/sys/net/if.c,v
retrieving revision 1.159
diff -u -r1.159 if.c
--- sys/net/if.c	22 Jun 2005 06:16:02 -0000	1.159
+++ sys/net/if.c	5 Jul 2005 05:52:06 -0000
@@ -1580,6 +1580,48 @@
 		error = (*ifp->if_ioctl)(ifp, cmd, data);
 		break;
 
+  case SIOCSIFLLADDR:
+  {
+    if ((error = suser(p->p_ucred, &p->p_acflag)))
+      return (error);
+    struct ifaddr *ifa = ifnet_addrs[ifp->if_index];
+    if (ifa == NULL)
+      return (EINVAL);
+    struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+    if (sdl == NULL)
+      return (EINVAL);
+    if (ifr->ifr_addr.sa_len != ETHER_ADDR_LEN)
+      return (EINVAL);
+    if (ETHER_IS_MULTICAST(ifr->ifr_addr.sa_data))
+      return (EINVAL);
+    switch (ifp->if_type) {
+      case IFT_ETHER:
+      case IFT_FDDI:
+      case IFT_XETHER:
+      case IFT_ISO88025:
+      case IFT_L2VLAN:
+      case IFT_ARCNET:
+        bcopy((caddr_t)ifr->ifr_addr.sa_data, LLADDR(sdl), ETHER_ADDR_LEN);
+        break;
+      default:
+        return (ENODEV);
+    }
+    if (ifp->if_flags & IFF_UP) {
+      int x = splnet();
+      ifp->if_flags &= ~IFF_UP;
+      (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr);
+      ifp->if_flags |= IFF_UP;
+      (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, (caddr_t)&ifr);
+      splx(x);
+      TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
+        if (ifa->ifa_addr != NULL &&
+            ifa->ifa_addr->sa_family == AF_INET)
+          arp_ifinit(ifp, ifa);
+      }
+    }
+    break;
+  }
+
 	case SIOCSDRVSPEC:
 	case SIOCS80211NWID:
 	case SIOCS80211NWKEY:
Index: sbin/ifconfig/ifconfig.8
===================================================================
RCS file: /cvsroot/src/sbin/ifconfig/ifconfig.8,v
retrieving revision 1.78
diff -u -r1.78 ifconfig.8
--- sbin/ifconfig/ifconfig.8	2 May 2005 15:37:06 -0000	1.78
+++ sbin/ifconfig/ifconfig.8	5 Jul 2005 05:55:57 -0000
@@ -238,6 +238,16 @@
 and 802.11g
 .Pq Dq 11g
 operating modes.
+.It Cm lladdr Ar addr
+Set the link-level address on an interface. This can be used to
+e.g. set a new MAC address on an ethernet interface, though the
+mechanism used is not ethernet-specific. The address
+.Ar addr
+is specified as a series of colon-separated hex digits. If the
+interface is already up when this option is used, it will be
+briefly brought down and then brought back up again in order to
+ensure that the receive filter in the underlying ethernet
+hardware is properly reprogrammed.
 .It Cm instance Ar minst
 Set the media instance to
 .Ar minst .
Index: sbin/ifconfig/ifconfig.c
===================================================================
RCS file: /cvsroot/src/sbin/ifconfig/ifconfig.c,v
retrieving revision 1.168
diff -u -r1.168 ifconfig.c
--- sbin/ifconfig/ifconfig.c	2 May 2005 15:35:16 -0000	1.168
+++ sbin/ifconfig/ifconfig.c	5 Jul 2005 05:56:05 -0000
@@ -146,6 +146,7 @@
 void 	notealias(const char *, int);
 void 	notrailers(const char *, int);
 void 	setifaddr(const char *, int);
+void 	setiflladdr(const char *, int);
 void 	setifdstaddr(const char *, int);
 void 	setifflags(const char *, int);
 void	setifcaps(const char *, int);
@@ -267,6 +268,7 @@
 	{ "mode",	NEXTARG,	A_MEDIAMODE,	setmediamode },
 	{ "instance",	NEXTARG,	A_MEDIAINST,	setmediainst },
 	{ "inst",	NEXTARG,	A_MEDIAINST,	setmediainst },
+	{ "lladdr",	NEXTARG, 0,	setiflladdr},
 	{ "ip4csum-tx",	IFCAP_CSUM_IPv4_Tx,0,		setifcaps },
 	{ "-ip4csum-tx",-IFCAP_CSUM_IPv4_Tx,0,		setifcaps },
 	{ "ip4csum-rx",	IFCAP_CSUM_IPv4_Rx,0,		setifcaps },
@@ -840,6 +842,25 @@
 }
 
 void
+setiflladdr(const char *addr, int param)
+{
+  struct ether_addr *eap;
+
+  eap = ether_aton(addr);
+  if (eap == NULL) {
+    warn("malformed link-level address");
+    return;
+  }
+  strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+  ifr.ifr_addr.sa_len = ETHER_ADDR_LEN;
+  ifr.ifr_addr.sa_family = AF_LINK;
+  bcopy(eap, ifr.ifr_addr.sa_data, ETHER_ADDR_LEN);
+  if (ioctl(s, SIOCSIFLLADDR, (caddr_t)&ifr) < 0)
+    warn("ioctl (set lladdr)");
+  return;
+}
+
+void
 setifnetmask(const char *addr, int d)
 {
 	(*afp->af_getaddr)(addr, MASK);
@@ -1488,7 +1509,7 @@
 		"interface\n"
 		"\t[ af [ address [ dest_addr ] ] [ netmask mask ] [ prefixlen n ]\n"
 		"\t\t[ alias | -alias ] ]\n"
-		"\t[ up ] [ down ] [ metric n ] [ mtu n ]\n"
+		"\t[ up ] [ down ] [ metric n ] [ mtu n ] [ lladdr addr ]\n"
 		"\t[ nwid network_id ] [ nwkey network_key | -nwkey ]\n"
 		"\t[ powersave | -powersave ] [ powersavesleep duration ]\n"
 		"\t[ [ af ] tunnel src_addr dest_addr ] [ deletetunnel ]\n"