summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/net/if.c55
-rw-r--r--sys/net/if_var.h1
-rw-r--r--sys/sys/sockio.h1
3 files changed, 57 insertions, 0 deletions
diff --git a/sys/net/if.c b/sys/net/if.c
index 982592df8463..829ede27b94d 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -52,7 +52,9 @@
#include <sys/sysctl.h>
#include <net/if.h>
+#include <net/if_arp.h>
#include <net/if_dl.h>
+#include <net/if_types.h>
#include <net/radix.h>
#include <net/route.h>
@@ -910,6 +912,13 @@ ifioctl(so, cmd, data, p)
return (EOPNOTSUPP);
return ((*ifp->if_ioctl)(ifp, cmd, data));
+ case SIOCSIFLLADDR:
+ error = suser(p);
+ if (error)
+ return (error);
+ return if_setlladdr(ifp,
+ ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len);
+
default:
oif_flags = ifp->if_flags;
if (so->so_proto == 0)
@@ -1309,6 +1318,52 @@ if_delmulti(ifp, sa)
return 0;
}
+/*
+ * Set the link layer address on an interface.
+ *
+ * At this time we only support certain types of interfaces,
+ * and we don't allow the length of the address to change.
+ */
+int
+if_setlladdr(struct ifnet *ifp, const u_char *lladdr, int len)
+{
+ struct sockaddr_dl *sdl;
+ struct ifaddr *ifa;
+
+ ifa = ifnet_addrs[ifp->if_index - 1];
+ if (ifa == NULL)
+ return (EINVAL);
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ if (sdl == NULL)
+ return (EINVAL);
+ if (len != sdl->sdl_alen) /* don't allow length to change */
+ return (EINVAL);
+ switch (ifp->if_type) {
+ case IFT_ETHER: /* these types use struct arpcom */
+ case IFT_FDDI:
+ case IFT_XETHER:
+ case IFT_ISO88025:
+ case IFT_PROPVIRTUAL: /* XXX waiting for IFT_8021_VLAN */
+ bcopy(lladdr, ((struct arpcom *)ifp->if_softc)->ac_enaddr, len);
+ bcopy(lladdr, LLADDR(sdl), len);
+ break;
+ default:
+ return (ENODEV);
+ }
+ /*
+ * If the interface is already up, we need
+ * to re-init it in order to reprogram its
+ * address filter.
+ */
+ if ((ifp->if_flags & IFF_UP) != 0) {
+ ifp->if_flags &= ~IFF_UP;
+ (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, NULL);
+ ifp->if_flags |= IFF_UP;
+ (*ifp->if_ioctl)(ifp, SIOCSIFFLAGS, NULL);
+ }
+ return (0);
+}
+
struct ifmultiaddr *
ifmaof_ifpforaddr(sa, ifp)
struct sockaddr *sa;
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index e6f07853622e..d16fc00c4313 100644
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -340,6 +340,7 @@ int if_delmulti __P((struct ifnet *, struct sockaddr *));
void if_detach __P((struct ifnet *));
void if_down __P((struct ifnet *));
void if_route __P((struct ifnet *, int flag, int fam));
+int if_setlladdr __P((struct ifnet *, const u_char *, int));
void if_unroute __P((struct ifnet *, int flag, int fam));
void if_up __P((struct ifnet *));
/*void ifinit __P((void));*/ /* declared in systm.h for main() */
diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h
index 7aee3dffcaa6..c107562eaec3 100644
--- a/sys/sys/sockio.h
+++ b/sys/sys/sockio.h
@@ -96,5 +96,6 @@
#define SIOCGIFGENERIC _IOWR('i', 58, struct ifreq) /* generic IF get op */
#define SIOCGIFSTATUS _IOWR('i', 59, struct ifstat) /* get IF status */
+#define SIOCSIFLLADDR _IOW('i', 60, struct ifreq) /* set link level addr */
#endif /* !_SYS_SOCKIO_H_ */