Add kernel infrastructure for network device cloning.

Reviewed by:	ru, ume
Obtained from:	NetBSD
MFC after:	1 week
This commit is contained in:
Brooks Davis 2001-07-02 20:49:25 +00:00
parent cf7ae120d9
commit 30aad87d72
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=79103
4 changed files with 242 additions and 5 deletions

View File

@ -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.
*
@ -349,6 +356,178 @@ if_rtdel(rn, arg)
return (0);
}
/*
* 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.
*/
@ -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);

View File

@ -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;

View File

@ -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_ */

View File

@ -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_ */