summaryrefslogtreecommitdiffstats
path: root/sys/net/if.c
diff options
context:
space:
mode:
authorbrooks <brooks@FreeBSD.org>2001-07-02 20:49:25 +0000
committerbrooks <brooks@FreeBSD.org>2001-07-02 20:49:25 +0000
commit5da97d80e2d7042b9d86959519aca3d58066ca21 (patch)
tree5b65dfbb642b566dad2288425cf42bcebf948b13 /sys/net/if.c
parent10671e2b791d0b1e548f15151858169fc47c05cd (diff)
downloadFreeBSD-src-5da97d80e2d7042b9d86959519aca3d58066ca21.zip
FreeBSD-src-5da97d80e2d7042b9d86959519aca3d58066ca21.tar.gz
Add kernel infrastructure for network device cloning.
Reviewed by: ru, ume Obtained from: NetBSD MFC after: 1 week
Diffstat (limited to 'sys/net/if.c')
-rw-r--r--sys/net/if.c197
1 files changed, 195 insertions, 2 deletions
diff --git a/sys/net/if.c b/sys/net/if.c
index f52d74d..e3f58b8 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -56,6 +56,7 @@
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_types.h>
+#include <net/if_var.h>
#include <net/radix.h>
#include <net/route.h>
@@ -96,6 +97,12 @@ struct ifnethead ifnet; /* depend on static init XXX */
extern void nd6_setmtu __P((struct ifnet *));
#endif
+struct if_clone *if_clone_lookup __P((const char *, int *));
+int if_clone_list __P((struct if_clonereq *));
+
+LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners);
+int if_cloners_count;
+
/*
* Network interface utility routines.
*
@@ -350,6 +357,178 @@ if_rtdel(rn, arg)
}
/*
+ * Create a clone network interface.
+ */
+int
+if_clone_create(name, len)
+ char *name;
+ int len;
+{
+ struct if_clone *ifc;
+ char *dp;
+ int wildcard;
+ int unit;
+ int err;
+
+ ifc = if_clone_lookup(name, &unit);
+ if (ifc == NULL)
+ return (EINVAL);
+
+ if (ifunit(name) != NULL)
+ return (EEXIST);
+
+ wildcard = (unit < 0);
+
+ err = (*ifc->ifc_create)(ifc, &unit);
+ if (err != 0)
+ return (err);
+
+ /* In the wildcard case, we need to update the name. */
+ if (wildcard) {
+ for (dp = name; *dp != '\0'; dp++);
+ if (snprintf(dp, len - (dp-name), "%d", unit) >
+ len - (dp-name) - 1) {
+ /*
+ * This can only be a programmer error and
+ * there's no straightforward way to recover if
+ * it happens.
+ */
+ panic("if_clone_create(): interface name too long");
+ }
+
+ }
+
+ return (0);
+}
+
+/*
+ * Destroy a clone network interface.
+ */
+int
+if_clone_destroy(name)
+ const char *name;
+{
+ struct if_clone *ifc;
+ struct ifnet *ifp;
+
+ ifc = if_clone_lookup(name, NULL);
+ if (ifc == NULL)
+ return (EINVAL);
+
+ ifp = ifunit(name);
+ if (ifp == NULL)
+ return (ENXIO);
+
+ if (ifc->ifc_destroy == NULL)
+ return (EOPNOTSUPP);
+
+ (*ifc->ifc_destroy)(ifp);
+ return (0);
+}
+
+/*
+ * Look up a network interface cloner.
+ */
+struct if_clone *
+if_clone_lookup(name, unitp)
+ const char *name;
+ int *unitp;
+{
+ struct if_clone *ifc;
+ const char *cp;
+ int i;
+
+ for (ifc = LIST_FIRST(&if_cloners); ifc != NULL;) {
+ for (cp = name, i = 0; i < ifc->ifc_namelen; i++, cp++) {
+ if (ifc->ifc_name[i] != *cp)
+ goto next_ifc;
+ }
+ goto found_name;
+ next_ifc:
+ ifc = LIST_NEXT(ifc, ifc_list);
+ }
+
+ /* No match. */
+ return ((struct if_clone *)NULL);
+
+ found_name:
+ if (*cp == '\0') {
+ i = -1;
+ } else {
+ for (i = 0; *cp != '\0'; cp++) {
+ if (*cp < '0' || *cp > '9') {
+ /* Bogus unit number. */
+ return (NULL);
+ }
+ i = (i * 10) + (*cp - '0');
+ }
+ }
+
+ if (unitp != NULL)
+ *unitp = i;
+ return (ifc);
+}
+
+/*
+ * Register a network interface cloner.
+ */
+void
+if_clone_attach(ifc)
+ struct if_clone *ifc;
+{
+
+ LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list);
+ if_cloners_count++;
+}
+
+/*
+ * Unregister a network interface cloner.
+ */
+void
+if_clone_detach(ifc)
+ struct if_clone *ifc;
+{
+
+ LIST_REMOVE(ifc, ifc_list);
+ if_cloners_count--;
+}
+
+/*
+ * Provide list of interface cloners to userspace.
+ */
+int
+if_clone_list(ifcr)
+ struct if_clonereq *ifcr;
+{
+ char outbuf[IFNAMSIZ], *dst;
+ struct if_clone *ifc;
+ int count, error = 0;
+
+ ifcr->ifcr_total = if_cloners_count;
+ if ((dst = ifcr->ifcr_buffer) == NULL) {
+ /* Just asking how many there are. */
+ return (0);
+ }
+
+ if (ifcr->ifcr_count < 0)
+ return (EINVAL);
+
+ count = (if_cloners_count < ifcr->ifcr_count) ?
+ if_cloners_count : ifcr->ifcr_count;
+
+ for (ifc = LIST_FIRST(&if_cloners); ifc != NULL && count != 0;
+ ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) {
+ strncpy(outbuf, ifc->ifc_name, IFNAMSIZ);
+ outbuf[IFNAMSIZ - 1] = '\0'; /* sanity */
+ error = copyout(outbuf, dst, IFNAMSIZ);
+ if (error)
+ break;
+ }
+
+ return (error);
+}
+
+/*
* Locate an interface based on a complete address.
*/
/*ARGSUSED*/
@@ -687,10 +866,10 @@ if_slowtimo(arg)
* interface structure pointer.
*/
struct ifnet *
-ifunit(char *name)
+ifunit(const char *name)
{
char namebuf[IFNAMSIZ + 1];
- char *cp;
+ const char *cp;
struct ifnet *ifp;
int unit;
unsigned len, m;
@@ -781,6 +960,20 @@ ifioctl(so, cmd, data, p)
return (ifconf(cmd, data));
}
ifr = (struct ifreq *)data;
+
+ switch (cmd) {
+ case SIOCIFCREATE:
+ case SIOCIFDESTROY:
+ if ((error = suser(p)) != 0)
+ return (error);
+ return ((cmd == SIOCIFCREATE) ?
+ if_clone_create(ifr->ifr_name, sizeof(ifr->ifr_name)) :
+ if_clone_destroy(ifr->ifr_name));
+
+ case SIOCIFGCLONERS:
+ return (if_clone_list((struct if_clonereq *)data));
+ }
+
ifp = ifunit(ifr->ifr_name);
if (ifp == 0)
return (ENXIO);
OpenPOWER on IntegriCloud