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 | |
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')
-rw-r--r-- | sys/net/if.c | 197 | ||||
-rw-r--r-- | sys/net/if.h | 38 | ||||
-rw-r--r-- | sys/net/if_var.h | 8 | ||||
-rw-r--r-- | sys/sys/sockio.h | 4 |
4 files changed, 242 insertions, 5 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); diff --git a/sys/net/if.h b/sys/net/if.h index 6cdaa53..088c7ef 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -37,6 +37,8 @@ #ifndef _NET_IF_H_ #define _NET_IF_H_ +#include <sys/queue.h> + /* * <net/if.h> does not depend on <sys/time.h> on most other systems. This * helps userland compatibility. (struct timeval ifi_lastchange) @@ -45,6 +47,40 @@ #include <sys/time.h> #endif +struct ifnet; + +/* + * Length of interface external name, including terminating '\0'. + * Note: this is the same size as a generic device's external name. + */ +#define IFNAMSIZ 16 +#define IF_NAMESIZE IFNAMSIZ + +/* + * Structure describing a `cloning' interface. + */ +struct if_clone { + LIST_ENTRY(if_clone) ifc_list; /* on list of cloners */ + const char *ifc_name; /* name of device, e.g. `gif' */ + size_t ifc_namelen; /* length of name */ + + int (*ifc_create)(struct if_clone *, int *); + void (*ifc_destroy)(struct ifnet *); +}; + +#define IF_CLONE_INITIALIZER(name, create, destroy) \ + { { 0 }, name, sizeof(name) - 1, create, destroy } + +/* + * Structure used to query names of interface cloners. + */ + +struct if_clonereq { + int ifcr_total; /* total cloners (out) */ + int ifcr_count; /* room for this many in user buffer */ + char *ifcr_buffer; /* buffer for cloner names */ +}; + /* * Structure describing information about an interface * which may be of interest to management entities. @@ -151,8 +187,6 @@ struct ifma_msghdr { * remainder may be interface specific. */ struct ifreq { -#define IFNAMSIZ 16 -#define IF_NAMESIZE IFNAMSIZ char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */ union { struct sockaddr ifru_addr; diff --git a/sys/net/if_var.h b/sys/net/if_var.h index 21cf174..7e760c7 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -401,7 +401,7 @@ void if_up __P((struct ifnet *)); /*void ifinit __P((void));*/ /* declared in systm.h for main() */ int ifioctl __P((struct socket *, u_long, caddr_t, struct proc *)); int ifpromisc __P((struct ifnet *, int)); -struct ifnet *ifunit __P((char *)); +struct ifnet *ifunit __P((const char *)); struct ifnet *if_withname __P((struct sockaddr *)); int if_poll_recv_slow __P((struct ifnet *ifp, int *quotap)); @@ -423,6 +423,12 @@ struct ifmultiaddr *ifmaof_ifpforaddr __P((struct sockaddr *, struct ifnet *)); int if_simloop __P((struct ifnet *ifp, struct mbuf *m, int af, int hlen)); +void if_clone_attach __P((struct if_clone *)); +void if_clone_detach __P((struct if_clone *)); + +int if_clone_create __P((char *, int)); +int if_clone_destroy __P((const char *)); + #endif /* _KERNEL */ #endif /* !_NET_IF_VAR_H_ */ diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h index 74c2a95..60ef17a 100644 --- a/sys/sys/sockio.h +++ b/sys/sys/sockio.h @@ -100,4 +100,8 @@ #define SIOCGIFSTATUS _IOWR('i', 59, struct ifstat) /* get IF status */ #define SIOCSIFLLADDR _IOW('i', 60, struct ifreq) /* set link level addr */ +#define SIOCIFCREATE _IOWR('i', 122, struct ifreq) /* create clone if */ +#define SIOCIFDESTROY _IOW('i', 121, struct ifreq) /* destroy clone if */ +#define SIOCIFGCLONERS _IOWR('i', 120, struct if_clonereq) /* get cloners */ + #endif /* !_SYS_SOCKIO_H_ */ |