summaryrefslogtreecommitdiffstats
path: root/sys
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
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')
-rw-r--r--sys/net/if.c197
-rw-r--r--sys/net/if.h38
-rw-r--r--sys/net/if_var.h8
-rw-r--r--sys/sys/sockio.h4
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_ */
OpenPOWER on IntegriCloud