diff options
author | brooks <brooks@FreeBSD.org> | 2001-07-02 20:49:25 +0000 |
---|---|---|
committer | brooks <brooks@FreeBSD.org> | 2001-07-02 20:49:25 +0000 |
commit | 5da97d80e2d7042b9d86959519aca3d58066ca21 (patch) | |
tree | 5b65dfbb642b566dad2288425cf42bcebf948b13 /sys/net/if.c | |
parent | 10671e2b791d0b1e548f15151858169fc47c05cd (diff) | |
download | FreeBSD-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.c | 197 |
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); |