diff options
Diffstat (limited to 'sys/net/if_clone.c')
-rw-r--r-- | sys/net/if_clone.c | 224 |
1 files changed, 162 insertions, 62 deletions
diff --git a/sys/net/if_clone.c b/sys/net/if_clone.c index 3543e2f..5f68d29 100644 --- a/sys/net/if_clone.c +++ b/sys/net/if_clone.c @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2012 Gleb Smirnoff <glebius@FreeBSD.org> * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * @@ -42,19 +43,65 @@ #include <net/if.h> #include <net/if_clone.h> -#if 0 -#include <net/if_dl.h> -#endif -#include <net/if_types.h> #include <net/if_var.h> #include <net/radix.h> #include <net/route.h> #include <net/vnet.h> +/* Current IF_MAXUNIT expands maximum to 5 characters. */ +#define IFCLOSIZ (IFNAMSIZ - 5) + +/* + * Structure describing a `cloning' interface. + * + * List of locks + * (c) const until freeing + * (d) driver specific data, may need external protection. + * (e) locked by if_cloners_mtx + * (i) locked by ifc_mtx mtx + */ +struct if_clone { + char ifc_name[IFCLOSIZ]; /* (c) Name of device, e.g. `gif' */ + struct unrhdr *ifc_unrhdr; /* (c) alloc_unr(9) header */ + int ifc_maxunit; /* (c) maximum unit number */ + long ifc_refcnt; /* (i) Reference count. */ + LIST_HEAD(, ifnet) ifc_iflist; /* (i) List of cloned interfaces */ + struct mtx ifc_mtx; /* Mutex to protect members. */ + + enum { SIMPLE, ADVANCED } ifc_type; /* (c) */ + + /* (c) Driver specific cloning functions. Called with no locks held. */ + union { + struct { /* advanced cloner */ + ifc_match_t *_ifc_match; + ifc_create_t *_ifc_create; + ifc_destroy_t *_ifc_destroy; + } A; + struct { /* simple cloner */ + ifcs_create_t *_ifcs_create; + ifcs_destroy_t *_ifcs_destroy; + int _ifcs_minifs; /* minimum ifs */ + + } S; + } U; +#define ifc_match U.A._ifc_match +#define ifc_create U.A._ifc_create +#define ifc_destroy U.A._ifc_destroy +#define ifcs_create U.S._ifcs_create +#define ifcs_destroy U.S._ifcs_destroy +#define ifcs_minifs U.S._ifcs_minifs + + LIST_ENTRY(if_clone) ifc_list; /* (e) On list of cloners */ +}; + static void if_clone_free(struct if_clone *ifc); static int if_clone_createif(struct if_clone *ifc, char *name, size_t len, caddr_t params); +static int ifc_simple_match(struct if_clone *, const char *); +static int ifc_simple_create(struct if_clone *, char *, size_t, caddr_t); +static int ifc_simple_destroy(struct if_clone *, struct ifnet *); + static struct mtx if_cloners_mtx; static VNET_DEFINE(int, if_cloners_count); VNET_DEFINE(LIST_HEAD(, if_clone), if_cloners); @@ -138,18 +185,25 @@ if_clone_create(char *name, size_t len, caddr_t params) /* Try to find an applicable cloner for this request */ IF_CLONERS_LOCK(); - LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { - if (ifc->ifc_match(ifc, name)) { - break; + LIST_FOREACH(ifc, &V_if_cloners, ifc_list) + if (ifc->ifc_type == SIMPLE) { + if (ifc_simple_match(ifc, name)) + break; + } else { + if (ifc->ifc_match(ifc, name)) + break; } - } #ifdef VIMAGE if (ifc == NULL && !IS_DEFAULT_VNET(curvnet)) { CURVNET_SET_QUIET(vnet0); - LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { - if (ifc->ifc_match(ifc, name)) - break; - } + LIST_FOREACH(ifc, &V_if_cloners, ifc_list) + if (ifc->ifc_type == SIMPLE) { + if (ifc_simple_match(ifc, name)) + break; + } else { + if (ifc->ifc_match(ifc, name)) + break; + } CURVNET_RESTORE(); } #endif @@ -173,7 +227,10 @@ if_clone_createif(struct if_clone *ifc, char *name, size_t len, caddr_t params) if (ifunit(name) != NULL) return (EEXIST); - err = (*ifc->ifc_create)(ifc, name, len, params); + if (ifc->ifc_type == SIMPLE) + err = ifc_simple_create(ifc, name, len, params); + else + err = (*ifc->ifc_create)(ifc, name, len, params); if (!err) { ifp = ifunit(name); @@ -214,10 +271,14 @@ if_clone_destroy(const char *name) #ifdef VIMAGE if (ifc == NULL && !IS_DEFAULT_VNET(curvnet)) { CURVNET_SET_QUIET(vnet0); - LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { - if (ifc->ifc_match(ifc, name)) - break; - } + LIST_FOREACH(ifc, &V_if_cloners, ifc_list) + if (ifc->type == SIMPLE) { + if (ifc_simple_match(ifc, name)) + break; + } else { + if (ifc->ifc_match(ifc, name)) + break; + } CURVNET_RESTORE(); } #endif @@ -241,7 +302,7 @@ if_clone_destroyif(struct if_clone *ifc, struct ifnet *ifp) int err; struct ifnet *ifcifp; - if (ifc->ifc_destroy == NULL) + if (ifc->ifc_type == ADVANCED && ifc->ifc_destroy == NULL) return(EOPNOTSUPP); /* @@ -266,7 +327,10 @@ if_clone_destroyif(struct if_clone *ifc, struct ifnet *ifp) if_delgroup(ifp, ifc->ifc_name); - err = (*ifc->ifc_destroy)(ifc, ifp); + if (ifc->ifc_type == SIMPLE) + err = ifc_simple_destroy(ifc, ifp); + else + err = (*ifc->ifc_destroy)(ifc, ifp); if (err != 0) { if_addgroup(ifp, ifc->ifc_name); @@ -279,21 +343,29 @@ if_clone_destroyif(struct if_clone *ifc, struct ifnet *ifp) return (err); } -/* - * Register a network interface cloner. - */ -int -if_clone_attach(struct if_clone *ifc) +static struct if_clone * +if_clone_alloc(const char *name, int maxunit) { - struct if_clone *ifc1; + struct if_clone *ifc; - KASSERT(ifc->ifc_name != NULL, ("%s: no name\n", __func__)); + KASSERT(name != NULL, ("%s: no name\n", __func__)); + ifc = malloc(sizeof(struct if_clone), M_CLONE, M_WAITOK | M_ZERO); + strncpy(ifc->ifc_name, name, IFCLOSIZ-1); IF_CLONE_LOCK_INIT(ifc); IF_CLONE_ADDREF(ifc); + ifc->ifc_maxunit = maxunit ? maxunit : IF_MAXUNIT; ifc->ifc_unrhdr = new_unrhdr(0, ifc->ifc_maxunit, &ifc->ifc_mtx); LIST_INIT(&ifc->ifc_iflist); + return (ifc); +} + +static int +if_clone_attach(struct if_clone *ifc) +{ + struct if_clone *ifc1; + IF_CLONERS_LOCK(); LIST_FOREACH(ifc1, &V_if_cloners, ifc_list) if (strcmp(ifc->ifc_name, ifc1->ifc_name) == 0) { @@ -305,11 +377,63 @@ if_clone_attach(struct if_clone *ifc) V_if_cloners_count++; IF_CLONERS_UNLOCK(); - if (ifc->ifc_attach != NULL) - (*ifc->ifc_attach)(ifc); + return (0); +} + +struct if_clone * +if_clone_advanced(const char *name, u_int maxunit, ifc_match_t match, + ifc_create_t create, ifc_destroy_t destroy) +{ + struct if_clone *ifc; + + ifc = if_clone_alloc(name, maxunit); + ifc->ifc_type = ADVANCED; + ifc->ifc_match = match; + ifc->ifc_create = create; + ifc->ifc_destroy = destroy; + + if (if_clone_attach(ifc) != 0) { + if_clone_free(ifc); + return (NULL); + } + EVENTHANDLER_INVOKE(if_clone_event, ifc); - return (0); + return (ifc); +} + +struct if_clone * +if_clone_simple(const char *name, ifcs_create_t create, ifcs_destroy_t destroy, + u_int minifs) +{ + struct if_clone *ifc; + u_int unit; + + ifc = if_clone_alloc(name, 0); + ifc->ifc_type = SIMPLE; + ifc->ifcs_create = create; + ifc->ifcs_destroy = destroy; + ifc->ifcs_minifs = minifs; + + if (if_clone_attach(ifc) != 0) { + if_clone_free(ifc); + return (NULL); + } + + for (unit = 0; unit < minifs; unit++) { + char name[IFNAMSIZ]; + int error; + + snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, unit); + error = if_clone_createif(ifc, name, IFNAMSIZ, NULL); + KASSERT(error == 0, + ("%s: failed to create required interface %s", + __func__, name)); + } + + EVENTHANDLER_INVOKE(if_clone_event, ifc); + + return (ifc); } /* @@ -318,7 +442,6 @@ if_clone_attach(struct if_clone *ifc) void if_clone_detach(struct if_clone *ifc) { - struct ifc_simple_data *ifcs = ifc->ifc_data; IF_CLONERS_LOCK(); LIST_REMOVE(ifc, ifc_list); @@ -326,8 +449,8 @@ if_clone_detach(struct if_clone *ifc) IF_CLONERS_UNLOCK(); /* Allow all simples to be destroyed */ - if (ifc->ifc_attach == ifc_simple_attach) - ifcs->ifcs_minifs = 0; + if (ifc->ifc_type == SIMPLE) + ifc->ifcs_minifs = 0; /* destroy all interfaces for this cloner */ while (!LIST_EMPTY(&ifc->ifc_iflist)) @@ -345,6 +468,7 @@ if_clone_free(struct if_clone *ifc) IF_CLONE_LOCK_DESTROY(ifc); delete_unrhdr(ifc->ifc_unrhdr); + free(ifc, M_CLONE); } /* @@ -483,29 +607,7 @@ ifc_free_unit(struct if_clone *ifc, int unit) IF_CLONE_REMREF(ifc); } -void -ifc_simple_attach(struct if_clone *ifc) -{ - int err; - int unit; - char name[IFNAMSIZ]; - struct ifc_simple_data *ifcs = ifc->ifc_data; - - KASSERT(ifcs->ifcs_minifs - 1 <= ifc->ifc_maxunit, - ("%s: %s requested more units than allowed (%d > %d)", - __func__, ifc->ifc_name, ifcs->ifcs_minifs, - ifc->ifc_maxunit + 1)); - - for (unit = 0; unit < ifcs->ifcs_minifs; unit++) { - snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, unit); - err = if_clone_createif(ifc, name, IFNAMSIZ, NULL); - KASSERT(err == 0, - ("%s: failed to create required interface %s", - __func__, name)); - } -} - -int +static int ifc_simple_match(struct if_clone *ifc, const char *name) { const char *cp; @@ -526,14 +628,13 @@ ifc_simple_match(struct if_clone *ifc, const char *name) return (1); } -int +static int ifc_simple_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) { char *dp; int wildcard; int unit; int err; - struct ifc_simple_data *ifcs = ifc->ifc_data; err = ifc_name2unit(name, &unit); if (err != 0) @@ -545,7 +646,7 @@ ifc_simple_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) if (err != 0) return (err); - err = ifcs->ifcs_create(ifc, unit, params); + err = ifc->ifcs_create(ifc, unit, params); if (err != 0) { ifc_free_unit(ifc, unit); return (err); @@ -569,18 +670,17 @@ ifc_simple_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) return (0); } -int +static int ifc_simple_destroy(struct if_clone *ifc, struct ifnet *ifp) { int unit; - struct ifc_simple_data *ifcs = ifc->ifc_data; unit = ifp->if_dunit; - if (unit < ifcs->ifcs_minifs) + if (unit < ifc->ifcs_minifs) return (EINVAL); - ifcs->ifcs_destroy(ifp); + ifc->ifcs_destroy(ifp); ifc_free_unit(ifc, unit); |