1c398230bSWarner Losh /*- 242a58907SGleb Smirnoff * Copyright (c) 2012 Gleb Smirnoff <glebius@FreeBSD.org> 3f889d2efSBrooks Davis * Copyright (c) 1980, 1986, 1993 4f889d2efSBrooks Davis * The Regents of the University of California. All rights reserved. 5f889d2efSBrooks Davis * 6f889d2efSBrooks Davis * Redistribution and use in source and binary forms, with or without 7f889d2efSBrooks Davis * modification, are permitted provided that the following conditions 8f889d2efSBrooks Davis * are met: 9f889d2efSBrooks Davis * 1. Redistributions of source code must retain the above copyright 10f889d2efSBrooks Davis * notice, this list of conditions and the following disclaimer. 11f889d2efSBrooks Davis * 2. Redistributions in binary form must reproduce the above copyright 12f889d2efSBrooks Davis * notice, this list of conditions and the following disclaimer in the 13f889d2efSBrooks Davis * documentation and/or other materials provided with the distribution. 14f889d2efSBrooks Davis * 4. Neither the name of the University nor the names of its contributors 15f889d2efSBrooks Davis * may be used to endorse or promote products derived from this software 16f889d2efSBrooks Davis * without specific prior written permission. 17f889d2efSBrooks Davis * 18f889d2efSBrooks Davis * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19f889d2efSBrooks Davis * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20f889d2efSBrooks Davis * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21f889d2efSBrooks Davis * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22f889d2efSBrooks Davis * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23f889d2efSBrooks Davis * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24f889d2efSBrooks Davis * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25f889d2efSBrooks Davis * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26f889d2efSBrooks Davis * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27f889d2efSBrooks Davis * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28f889d2efSBrooks Davis * SUCH DAMAGE. 29f889d2efSBrooks Davis * 30f889d2efSBrooks Davis * @(#)if.c 8.5 (Berkeley) 1/9/95 31f889d2efSBrooks Davis * $FreeBSD$ 32f889d2efSBrooks Davis */ 33f889d2efSBrooks Davis 34f889d2efSBrooks Davis #include <sys/param.h> 35c3322cb9SGleb Smirnoff #include <sys/eventhandler.h> 36f889d2efSBrooks Davis #include <sys/malloc.h> 37434dbbb3SRuslan Ermilov #include <sys/limits.h> 38f889d2efSBrooks Davis #include <sys/lock.h> 39f889d2efSBrooks Davis #include <sys/mutex.h> 40f889d2efSBrooks Davis #include <sys/kernel.h> 41f889d2efSBrooks Davis #include <sys/systm.h> 42f889d2efSBrooks Davis #include <sys/types.h> 43f889d2efSBrooks Davis #include <sys/socket.h> 44f889d2efSBrooks Davis 45f889d2efSBrooks Davis #include <net/if.h> 46f889d2efSBrooks Davis #include <net/if_var.h> 4776039bc8SGleb Smirnoff #include <net/if_clone.h> 48f889d2efSBrooks Davis #include <net/radix.h> 49f889d2efSBrooks Davis #include <net/route.h> 5021ca7b57SMarko Zec #include <net/vnet.h> 51f889d2efSBrooks Davis 5242a58907SGleb Smirnoff /* Current IF_MAXUNIT expands maximum to 5 characters. */ 5342a58907SGleb Smirnoff #define IFCLOSIZ (IFNAMSIZ - 5) 5442a58907SGleb Smirnoff 5542a58907SGleb Smirnoff /* 5642a58907SGleb Smirnoff * Structure describing a `cloning' interface. 5742a58907SGleb Smirnoff * 5842a58907SGleb Smirnoff * List of locks 5942a58907SGleb Smirnoff * (c) const until freeing 6042a58907SGleb Smirnoff * (d) driver specific data, may need external protection. 6142a58907SGleb Smirnoff * (e) locked by if_cloners_mtx 6242a58907SGleb Smirnoff * (i) locked by ifc_mtx mtx 6342a58907SGleb Smirnoff */ 6442a58907SGleb Smirnoff struct if_clone { 6542a58907SGleb Smirnoff char ifc_name[IFCLOSIZ]; /* (c) Name of device, e.g. `gif' */ 6642a58907SGleb Smirnoff struct unrhdr *ifc_unrhdr; /* (c) alloc_unr(9) header */ 6742a58907SGleb Smirnoff int ifc_maxunit; /* (c) maximum unit number */ 6842a58907SGleb Smirnoff long ifc_refcnt; /* (i) Reference count. */ 6942a58907SGleb Smirnoff LIST_HEAD(, ifnet) ifc_iflist; /* (i) List of cloned interfaces */ 7042a58907SGleb Smirnoff struct mtx ifc_mtx; /* Mutex to protect members. */ 7142a58907SGleb Smirnoff 7242a58907SGleb Smirnoff enum { SIMPLE, ADVANCED } ifc_type; /* (c) */ 7342a58907SGleb Smirnoff 7442a58907SGleb Smirnoff /* (c) Driver specific cloning functions. Called with no locks held. */ 7542a58907SGleb Smirnoff union { 7642a58907SGleb Smirnoff struct { /* advanced cloner */ 7742a58907SGleb Smirnoff ifc_match_t *_ifc_match; 7842a58907SGleb Smirnoff ifc_create_t *_ifc_create; 7942a58907SGleb Smirnoff ifc_destroy_t *_ifc_destroy; 8042a58907SGleb Smirnoff } A; 8142a58907SGleb Smirnoff struct { /* simple cloner */ 8242a58907SGleb Smirnoff ifcs_create_t *_ifcs_create; 8342a58907SGleb Smirnoff ifcs_destroy_t *_ifcs_destroy; 8442a58907SGleb Smirnoff int _ifcs_minifs; /* minimum ifs */ 8542a58907SGleb Smirnoff 8642a58907SGleb Smirnoff } S; 8742a58907SGleb Smirnoff } U; 8842a58907SGleb Smirnoff #define ifc_match U.A._ifc_match 8942a58907SGleb Smirnoff #define ifc_create U.A._ifc_create 9042a58907SGleb Smirnoff #define ifc_destroy U.A._ifc_destroy 9142a58907SGleb Smirnoff #define ifcs_create U.S._ifcs_create 9242a58907SGleb Smirnoff #define ifcs_destroy U.S._ifcs_destroy 9342a58907SGleb Smirnoff #define ifcs_minifs U.S._ifcs_minifs 9442a58907SGleb Smirnoff 9542a58907SGleb Smirnoff LIST_ENTRY(if_clone) ifc_list; /* (e) On list of cloners */ 9642a58907SGleb Smirnoff }; 9742a58907SGleb Smirnoff 98f889d2efSBrooks Davis static void if_clone_free(struct if_clone *ifc); 996b7330e2SSam Leffler static int if_clone_createif(struct if_clone *ifc, char *name, size_t len, 1006b7330e2SSam Leffler caddr_t params); 101f889d2efSBrooks Davis 10242a58907SGleb Smirnoff static int ifc_simple_match(struct if_clone *, const char *); 10342a58907SGleb Smirnoff static int ifc_simple_create(struct if_clone *, char *, size_t, caddr_t); 10442a58907SGleb Smirnoff static int ifc_simple_destroy(struct if_clone *, struct ifnet *); 10542a58907SGleb Smirnoff 106f889d2efSBrooks Davis static struct mtx if_cloners_mtx; 1074ea05db8SGleb Smirnoff MTX_SYSINIT(if_cloners_lock, &if_cloners_mtx, "if_cloners lock", MTX_DEF); 1083e288e62SDimitry Andric static VNET_DEFINE(int, if_cloners_count); 109eddfbb76SRobert Watson VNET_DEFINE(LIST_HEAD(, if_clone), if_cloners); 110eddfbb76SRobert Watson 1111e77c105SRobert Watson #define V_if_cloners_count VNET(if_cloners_count) 1121e77c105SRobert Watson #define V_if_cloners VNET(if_cloners) 113f889d2efSBrooks Davis 114f889d2efSBrooks Davis #define IF_CLONERS_LOCK_ASSERT() mtx_assert(&if_cloners_mtx, MA_OWNED) 115f889d2efSBrooks Davis #define IF_CLONERS_LOCK() mtx_lock(&if_cloners_mtx) 116f889d2efSBrooks Davis #define IF_CLONERS_UNLOCK() mtx_unlock(&if_cloners_mtx) 117f889d2efSBrooks Davis 118f889d2efSBrooks Davis #define IF_CLONE_LOCK_INIT(ifc) \ 119f889d2efSBrooks Davis mtx_init(&(ifc)->ifc_mtx, "if_clone lock", NULL, MTX_DEF) 120f889d2efSBrooks Davis #define IF_CLONE_LOCK_DESTROY(ifc) mtx_destroy(&(ifc)->ifc_mtx) 121f889d2efSBrooks Davis #define IF_CLONE_LOCK_ASSERT(ifc) mtx_assert(&(ifc)->ifc_mtx, MA_OWNED) 122f889d2efSBrooks Davis #define IF_CLONE_LOCK(ifc) mtx_lock(&(ifc)->ifc_mtx) 123f889d2efSBrooks Davis #define IF_CLONE_UNLOCK(ifc) mtx_unlock(&(ifc)->ifc_mtx) 124f889d2efSBrooks Davis 125f889d2efSBrooks Davis #define IF_CLONE_ADDREF(ifc) \ 126f889d2efSBrooks Davis do { \ 127f889d2efSBrooks Davis IF_CLONE_LOCK(ifc); \ 128f889d2efSBrooks Davis IF_CLONE_ADDREF_LOCKED(ifc); \ 129f889d2efSBrooks Davis IF_CLONE_UNLOCK(ifc); \ 130f889d2efSBrooks Davis } while (0) 131f889d2efSBrooks Davis #define IF_CLONE_ADDREF_LOCKED(ifc) \ 132f889d2efSBrooks Davis do { \ 133f889d2efSBrooks Davis IF_CLONE_LOCK_ASSERT(ifc); \ 134f889d2efSBrooks Davis KASSERT((ifc)->ifc_refcnt >= 0, \ 135f889d2efSBrooks Davis ("negative refcnt %ld", (ifc)->ifc_refcnt)); \ 136f889d2efSBrooks Davis (ifc)->ifc_refcnt++; \ 137f889d2efSBrooks Davis } while (0) 138f889d2efSBrooks Davis #define IF_CLONE_REMREF(ifc) \ 139f889d2efSBrooks Davis do { \ 140f889d2efSBrooks Davis IF_CLONE_LOCK(ifc); \ 141f889d2efSBrooks Davis IF_CLONE_REMREF_LOCKED(ifc); \ 142f889d2efSBrooks Davis } while (0) 143f889d2efSBrooks Davis #define IF_CLONE_REMREF_LOCKED(ifc) \ 144f889d2efSBrooks Davis do { \ 145f889d2efSBrooks Davis IF_CLONE_LOCK_ASSERT(ifc); \ 146f889d2efSBrooks Davis KASSERT((ifc)->ifc_refcnt > 0, \ 147f889d2efSBrooks Davis ("bogus refcnt %ld", (ifc)->ifc_refcnt)); \ 148f889d2efSBrooks Davis if (--(ifc)->ifc_refcnt == 0) { \ 149f889d2efSBrooks Davis IF_CLONE_UNLOCK(ifc); \ 150f889d2efSBrooks Davis if_clone_free(ifc); \ 151ca64c799SMax Laier } else { \ 152f889d2efSBrooks Davis /* silently free the lock */ \ 153f889d2efSBrooks Davis IF_CLONE_UNLOCK(ifc); \ 154ca64c799SMax Laier } \ 155f889d2efSBrooks Davis } while (0) 156f889d2efSBrooks Davis 1574e7e0183SAndrew Thompson #define IFC_IFLIST_INSERT(_ifc, _ifp) \ 1584e7e0183SAndrew Thompson LIST_INSERT_HEAD(&_ifc->ifc_iflist, _ifp, if_clones) 1594e7e0183SAndrew Thompson #define IFC_IFLIST_REMOVE(_ifc, _ifp) \ 1604e7e0183SAndrew Thompson LIST_REMOVE(_ifp, if_clones) 1614e7e0183SAndrew Thompson 162c711aea6SPoul-Henning Kamp static MALLOC_DEFINE(M_CLONE, "clone", "interface cloning framework"); 163f889d2efSBrooks Davis 164d0728d71SRobert Watson void 165d0728d71SRobert Watson vnet_if_clone_init(void) 16637f17770SMarko Zec { 16737f17770SMarko Zec 16837f17770SMarko Zec LIST_INIT(&V_if_cloners); 16937f17770SMarko Zec } 17037f17770SMarko Zec 171f889d2efSBrooks Davis /* 1724e7e0183SAndrew Thompson * Lookup and create a clone network interface. 173f889d2efSBrooks Davis */ 174f889d2efSBrooks Davis int 1756b7330e2SSam Leffler if_clone_create(char *name, size_t len, caddr_t params) 176f889d2efSBrooks Davis { 177f889d2efSBrooks Davis struct if_clone *ifc; 178f889d2efSBrooks Davis 179f889d2efSBrooks Davis /* Try to find an applicable cloner for this request */ 180f889d2efSBrooks Davis IF_CLONERS_LOCK(); 18142a58907SGleb Smirnoff LIST_FOREACH(ifc, &V_if_cloners, ifc_list) 18242a58907SGleb Smirnoff if (ifc->ifc_type == SIMPLE) { 18342a58907SGleb Smirnoff if (ifc_simple_match(ifc, name)) 184f889d2efSBrooks Davis break; 18542a58907SGleb Smirnoff } else { 18642a58907SGleb Smirnoff if (ifc->ifc_match(ifc, name)) 18742a58907SGleb Smirnoff break; 188f889d2efSBrooks Davis } 18937f17770SMarko Zec #ifdef VIMAGE 19037f17770SMarko Zec if (ifc == NULL && !IS_DEFAULT_VNET(curvnet)) { 19137f17770SMarko Zec CURVNET_SET_QUIET(vnet0); 19242a58907SGleb Smirnoff LIST_FOREACH(ifc, &V_if_cloners, ifc_list) 19342a58907SGleb Smirnoff if (ifc->ifc_type == SIMPLE) { 19442a58907SGleb Smirnoff if (ifc_simple_match(ifc, name)) 19542a58907SGleb Smirnoff break; 19642a58907SGleb Smirnoff } else { 19737f17770SMarko Zec if (ifc->ifc_match(ifc, name)) 19837f17770SMarko Zec break; 19937f17770SMarko Zec } 20037f17770SMarko Zec CURVNET_RESTORE(); 20137f17770SMarko Zec } 20237f17770SMarko Zec #endif 203f889d2efSBrooks Davis IF_CLONERS_UNLOCK(); 204f889d2efSBrooks Davis 205f889d2efSBrooks Davis if (ifc == NULL) 206f889d2efSBrooks Davis return (EINVAL); 207f889d2efSBrooks Davis 2086b7330e2SSam Leffler return (if_clone_createif(ifc, name, len, params)); 2094e7e0183SAndrew Thompson } 2104e7e0183SAndrew Thompson 2114e7e0183SAndrew Thompson /* 2124e7e0183SAndrew Thompson * Create a clone network interface. 2134e7e0183SAndrew Thompson */ 2144e7e0183SAndrew Thompson static int 2156b7330e2SSam Leffler if_clone_createif(struct if_clone *ifc, char *name, size_t len, caddr_t params) 2164e7e0183SAndrew Thompson { 2174e7e0183SAndrew Thompson int err; 2184e7e0183SAndrew Thompson struct ifnet *ifp; 2194e7e0183SAndrew Thompson 2204e7e0183SAndrew Thompson if (ifunit(name) != NULL) 2214e7e0183SAndrew Thompson return (EEXIST); 2224e7e0183SAndrew Thompson 22342a58907SGleb Smirnoff if (ifc->ifc_type == SIMPLE) 22442a58907SGleb Smirnoff err = ifc_simple_create(ifc, name, len, params); 22542a58907SGleb Smirnoff else 2266b7330e2SSam Leffler err = (*ifc->ifc_create)(ifc, name, len, params); 2274e7e0183SAndrew Thompson 2284e7e0183SAndrew Thompson if (!err) { 2294e7e0183SAndrew Thompson ifp = ifunit(name); 2304e7e0183SAndrew Thompson if (ifp == NULL) 2314e7e0183SAndrew Thompson panic("%s: lookup failed for %s", __func__, name); 2324e7e0183SAndrew Thompson 2330dad3f0eSMax Laier if_addgroup(ifp, ifc->ifc_name); 2340dad3f0eSMax Laier 2354e7e0183SAndrew Thompson IF_CLONE_LOCK(ifc); 2364e7e0183SAndrew Thompson IFC_IFLIST_INSERT(ifc, ifp); 2374e7e0183SAndrew Thompson IF_CLONE_UNLOCK(ifc); 2384e7e0183SAndrew Thompson } 2394e7e0183SAndrew Thompson 240f889d2efSBrooks Davis return (err); 241f889d2efSBrooks Davis } 242f889d2efSBrooks Davis 243f889d2efSBrooks Davis /* 2444e7e0183SAndrew Thompson * Lookup and destroy a clone network interface. 245f889d2efSBrooks Davis */ 246f889d2efSBrooks Davis int 247f889d2efSBrooks Davis if_clone_destroy(const char *name) 248f889d2efSBrooks Davis { 249d0088cdeSBjoern A. Zeeb int err; 250f889d2efSBrooks Davis struct if_clone *ifc; 251f889d2efSBrooks Davis struct ifnet *ifp; 252f889d2efSBrooks Davis 253d0088cdeSBjoern A. Zeeb ifp = ifunit_ref(name); 254f889d2efSBrooks Davis if (ifp == NULL) 255f889d2efSBrooks Davis return (ENXIO); 256f889d2efSBrooks Davis 257f889d2efSBrooks Davis /* Find the cloner for this interface */ 258f889d2efSBrooks Davis IF_CLONERS_LOCK(); 25937f17770SMarko Zec LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { 260f889d2efSBrooks Davis if (strcmp(ifc->ifc_name, ifp->if_dname) == 0) { 261f889d2efSBrooks Davis break; 262f889d2efSBrooks Davis } 263f889d2efSBrooks Davis } 26437f17770SMarko Zec #ifdef VIMAGE 26537f17770SMarko Zec if (ifc == NULL && !IS_DEFAULT_VNET(curvnet)) { 26637f17770SMarko Zec CURVNET_SET_QUIET(vnet0); 26742a58907SGleb Smirnoff LIST_FOREACH(ifc, &V_if_cloners, ifc_list) 2683aaf0159SGleb Smirnoff if (ifc->ifc_type == SIMPLE) { 26942a58907SGleb Smirnoff if (ifc_simple_match(ifc, name)) 27042a58907SGleb Smirnoff break; 27142a58907SGleb Smirnoff } else { 27237f17770SMarko Zec if (ifc->ifc_match(ifc, name)) 27337f17770SMarko Zec break; 27437f17770SMarko Zec } 27537f17770SMarko Zec CURVNET_RESTORE(); 27637f17770SMarko Zec } 27737f17770SMarko Zec #endif 278f889d2efSBrooks Davis IF_CLONERS_UNLOCK(); 279d0088cdeSBjoern A. Zeeb if (ifc == NULL) { 280d0088cdeSBjoern A. Zeeb if_rele(ifp); 281f889d2efSBrooks Davis return (EINVAL); 282d0088cdeSBjoern A. Zeeb } 283f889d2efSBrooks Davis 284d0088cdeSBjoern A. Zeeb err = if_clone_destroyif(ifc, ifp); 285d0088cdeSBjoern A. Zeeb if_rele(ifp); 286d0088cdeSBjoern A. Zeeb return err; 2874e7e0183SAndrew Thompson } 2884e7e0183SAndrew Thompson 2894e7e0183SAndrew Thompson /* 2904e7e0183SAndrew Thompson * Destroy a clone network interface. 2914e7e0183SAndrew Thompson */ 292cb959fa2SAndrew Thompson int 2934e7e0183SAndrew Thompson if_clone_destroyif(struct if_clone *ifc, struct ifnet *ifp) 2944e7e0183SAndrew Thompson { 2954e7e0183SAndrew Thompson int err; 296c769e1beSBjoern A. Zeeb struct ifnet *ifcifp; 2974e7e0183SAndrew Thompson 29842a58907SGleb Smirnoff if (ifc->ifc_type == ADVANCED && ifc->ifc_destroy == NULL) 29921ca7b57SMarko Zec return(EOPNOTSUPP); 300f889d2efSBrooks Davis 30137f17770SMarko Zec /* 30237f17770SMarko Zec * Given that the cloned ifnet might be attached to a different 30337f17770SMarko Zec * vnet from where its cloner was registered, we have to 30437f17770SMarko Zec * switch to the vnet context of the target vnet. 30537f17770SMarko Zec */ 30637f17770SMarko Zec CURVNET_SET_QUIET(ifp->if_vnet); 30737f17770SMarko Zec 308434dbbb3SRuslan Ermilov IF_CLONE_LOCK(ifc); 309c769e1beSBjoern A. Zeeb LIST_FOREACH(ifcifp, &ifc->ifc_iflist, if_clones) { 310c769e1beSBjoern A. Zeeb if (ifcifp == ifp) { 311434dbbb3SRuslan Ermilov IFC_IFLIST_REMOVE(ifc, ifp); 312c769e1beSBjoern A. Zeeb break; 313c769e1beSBjoern A. Zeeb } 314c769e1beSBjoern A. Zeeb } 315434dbbb3SRuslan Ermilov IF_CLONE_UNLOCK(ifc); 316c769e1beSBjoern A. Zeeb if (ifcifp == NULL) { 317c769e1beSBjoern A. Zeeb CURVNET_RESTORE(); 318c769e1beSBjoern A. Zeeb return (ENXIO); /* ifp is not on the list. */ 319c769e1beSBjoern A. Zeeb } 320434dbbb3SRuslan Ermilov 3210dad3f0eSMax Laier if_delgroup(ifp, ifc->ifc_name); 3220dad3f0eSMax Laier 32342a58907SGleb Smirnoff if (ifc->ifc_type == SIMPLE) 32442a58907SGleb Smirnoff err = ifc_simple_destroy(ifc, ifp); 32542a58907SGleb Smirnoff else 326f889d2efSBrooks Davis err = (*ifc->ifc_destroy)(ifc, ifp); 327f889d2efSBrooks Davis 328434dbbb3SRuslan Ermilov if (err != 0) { 3290dad3f0eSMax Laier if_addgroup(ifp, ifc->ifc_name); 3300dad3f0eSMax Laier 331434dbbb3SRuslan Ermilov IF_CLONE_LOCK(ifc); 332434dbbb3SRuslan Ermilov IFC_IFLIST_INSERT(ifc, ifp); 333434dbbb3SRuslan Ermilov IF_CLONE_UNLOCK(ifc); 334434dbbb3SRuslan Ermilov } 33521ca7b57SMarko Zec CURVNET_RESTORE(); 336f889d2efSBrooks Davis return (err); 337f889d2efSBrooks Davis } 338f889d2efSBrooks Davis 33942a58907SGleb Smirnoff static struct if_clone * 34042a58907SGleb Smirnoff if_clone_alloc(const char *name, int maxunit) 34142a58907SGleb Smirnoff { 34242a58907SGleb Smirnoff struct if_clone *ifc; 34342a58907SGleb Smirnoff 34442a58907SGleb Smirnoff KASSERT(name != NULL, ("%s: no name\n", __func__)); 34542a58907SGleb Smirnoff 34642a58907SGleb Smirnoff ifc = malloc(sizeof(struct if_clone), M_CLONE, M_WAITOK | M_ZERO); 34742a58907SGleb Smirnoff strncpy(ifc->ifc_name, name, IFCLOSIZ-1); 34842a58907SGleb Smirnoff IF_CLONE_LOCK_INIT(ifc); 34942a58907SGleb Smirnoff IF_CLONE_ADDREF(ifc); 35042a58907SGleb Smirnoff ifc->ifc_maxunit = maxunit ? maxunit : IF_MAXUNIT; 35142a58907SGleb Smirnoff ifc->ifc_unrhdr = new_unrhdr(0, ifc->ifc_maxunit, &ifc->ifc_mtx); 35242a58907SGleb Smirnoff LIST_INIT(&ifc->ifc_iflist); 35342a58907SGleb Smirnoff 35442a58907SGleb Smirnoff return (ifc); 35542a58907SGleb Smirnoff } 35642a58907SGleb Smirnoff 35742a58907SGleb Smirnoff static int 358f889d2efSBrooks Davis if_clone_attach(struct if_clone *ifc) 359f889d2efSBrooks Davis { 3602e9fff5bSGleb Smirnoff struct if_clone *ifc1; 361f889d2efSBrooks Davis 362f889d2efSBrooks Davis IF_CLONERS_LOCK(); 3632e9fff5bSGleb Smirnoff LIST_FOREACH(ifc1, &V_if_cloners, ifc_list) 3642e9fff5bSGleb Smirnoff if (strcmp(ifc->ifc_name, ifc1->ifc_name) == 0) { 3652e9fff5bSGleb Smirnoff IF_CLONERS_UNLOCK(); 3662e9fff5bSGleb Smirnoff IF_CLONE_REMREF(ifc); 3672e9fff5bSGleb Smirnoff return (EEXIST); 3682e9fff5bSGleb Smirnoff } 36937f17770SMarko Zec LIST_INSERT_HEAD(&V_if_cloners, ifc, ifc_list); 37037f17770SMarko Zec V_if_cloners_count++; 371f889d2efSBrooks Davis IF_CLONERS_UNLOCK(); 372f889d2efSBrooks Davis 37342a58907SGleb Smirnoff return (0); 37442a58907SGleb Smirnoff } 37542a58907SGleb Smirnoff 37642a58907SGleb Smirnoff struct if_clone * 37742a58907SGleb Smirnoff if_clone_advanced(const char *name, u_int maxunit, ifc_match_t match, 37842a58907SGleb Smirnoff ifc_create_t create, ifc_destroy_t destroy) 37942a58907SGleb Smirnoff { 38042a58907SGleb Smirnoff struct if_clone *ifc; 38142a58907SGleb Smirnoff 38242a58907SGleb Smirnoff ifc = if_clone_alloc(name, maxunit); 38342a58907SGleb Smirnoff ifc->ifc_type = ADVANCED; 38442a58907SGleb Smirnoff ifc->ifc_match = match; 38542a58907SGleb Smirnoff ifc->ifc_create = create; 38642a58907SGleb Smirnoff ifc->ifc_destroy = destroy; 38742a58907SGleb Smirnoff 38842a58907SGleb Smirnoff if (if_clone_attach(ifc) != 0) { 38942a58907SGleb Smirnoff if_clone_free(ifc); 39042a58907SGleb Smirnoff return (NULL); 39142a58907SGleb Smirnoff } 39242a58907SGleb Smirnoff 393f889d2efSBrooks Davis EVENTHANDLER_INVOKE(if_clone_event, ifc); 3942e9fff5bSGleb Smirnoff 39542a58907SGleb Smirnoff return (ifc); 39642a58907SGleb Smirnoff } 39742a58907SGleb Smirnoff 39842a58907SGleb Smirnoff struct if_clone * 39942a58907SGleb Smirnoff if_clone_simple(const char *name, ifcs_create_t create, ifcs_destroy_t destroy, 40042a58907SGleb Smirnoff u_int minifs) 40142a58907SGleb Smirnoff { 40242a58907SGleb Smirnoff struct if_clone *ifc; 40342a58907SGleb Smirnoff u_int unit; 40442a58907SGleb Smirnoff 40542a58907SGleb Smirnoff ifc = if_clone_alloc(name, 0); 40642a58907SGleb Smirnoff ifc->ifc_type = SIMPLE; 40742a58907SGleb Smirnoff ifc->ifcs_create = create; 40842a58907SGleb Smirnoff ifc->ifcs_destroy = destroy; 40942a58907SGleb Smirnoff ifc->ifcs_minifs = minifs; 41042a58907SGleb Smirnoff 41142a58907SGleb Smirnoff if (if_clone_attach(ifc) != 0) { 41242a58907SGleb Smirnoff if_clone_free(ifc); 41342a58907SGleb Smirnoff return (NULL); 41442a58907SGleb Smirnoff } 41542a58907SGleb Smirnoff 41642a58907SGleb Smirnoff for (unit = 0; unit < minifs; unit++) { 41742a58907SGleb Smirnoff char name[IFNAMSIZ]; 41842a58907SGleb Smirnoff int error; 41942a58907SGleb Smirnoff 42042a58907SGleb Smirnoff snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, unit); 42142a58907SGleb Smirnoff error = if_clone_createif(ifc, name, IFNAMSIZ, NULL); 42242a58907SGleb Smirnoff KASSERT(error == 0, 42342a58907SGleb Smirnoff ("%s: failed to create required interface %s", 42442a58907SGleb Smirnoff __func__, name)); 42542a58907SGleb Smirnoff } 42642a58907SGleb Smirnoff 42742a58907SGleb Smirnoff EVENTHANDLER_INVOKE(if_clone_event, ifc); 42842a58907SGleb Smirnoff 42942a58907SGleb Smirnoff return (ifc); 430f889d2efSBrooks Davis } 431f889d2efSBrooks Davis 432f889d2efSBrooks Davis /* 433f889d2efSBrooks Davis * Unregister a network interface cloner. 434f889d2efSBrooks Davis */ 435f889d2efSBrooks Davis void 436f889d2efSBrooks Davis if_clone_detach(struct if_clone *ifc) 437f889d2efSBrooks Davis { 438f889d2efSBrooks Davis 439f889d2efSBrooks Davis IF_CLONERS_LOCK(); 440f889d2efSBrooks Davis LIST_REMOVE(ifc, ifc_list); 44137f17770SMarko Zec V_if_cloners_count--; 442f889d2efSBrooks Davis IF_CLONERS_UNLOCK(); 443f889d2efSBrooks Davis 444febd0759SAndrew Thompson /* Allow all simples to be destroyed */ 44542a58907SGleb Smirnoff if (ifc->ifc_type == SIMPLE) 44642a58907SGleb Smirnoff ifc->ifcs_minifs = 0; 447febd0759SAndrew Thompson 4484e7e0183SAndrew Thompson /* destroy all interfaces for this cloner */ 4494e7e0183SAndrew Thompson while (!LIST_EMPTY(&ifc->ifc_iflist)) 4504e7e0183SAndrew Thompson if_clone_destroyif(ifc, LIST_FIRST(&ifc->ifc_iflist)); 4514e7e0183SAndrew Thompson 452f889d2efSBrooks Davis IF_CLONE_REMREF(ifc); 453f889d2efSBrooks Davis } 454f889d2efSBrooks Davis 455f889d2efSBrooks Davis static void 456f889d2efSBrooks Davis if_clone_free(struct if_clone *ifc) 457f889d2efSBrooks Davis { 458f889d2efSBrooks Davis 4594e7e0183SAndrew Thompson KASSERT(LIST_EMPTY(&ifc->ifc_iflist), 4604e7e0183SAndrew Thompson ("%s: ifc_iflist not empty", __func__)); 4614e7e0183SAndrew Thompson 462f889d2efSBrooks Davis IF_CLONE_LOCK_DESTROY(ifc); 4632e9fff5bSGleb Smirnoff delete_unrhdr(ifc->ifc_unrhdr); 46442a58907SGleb Smirnoff free(ifc, M_CLONE); 465f889d2efSBrooks Davis } 466f889d2efSBrooks Davis 467f889d2efSBrooks Davis /* 468f889d2efSBrooks Davis * Provide list of interface cloners to userspace. 469f889d2efSBrooks Davis */ 470f889d2efSBrooks Davis int 471f889d2efSBrooks Davis if_clone_list(struct if_clonereq *ifcr) 472f889d2efSBrooks Davis { 473c859ef97SBrooks Davis char *buf, *dst, *outbuf = NULL; 474f889d2efSBrooks Davis struct if_clone *ifc; 475c859ef97SBrooks Davis int buf_count, count, err = 0; 476c859ef97SBrooks Davis 477a6d00835SMaxim Konovalov if (ifcr->ifcr_count < 0) 478a6d00835SMaxim Konovalov return (EINVAL); 479a6d00835SMaxim Konovalov 480c859ef97SBrooks Davis IF_CLONERS_LOCK(); 481c859ef97SBrooks Davis /* 482c859ef97SBrooks Davis * Set our internal output buffer size. We could end up not 483c859ef97SBrooks Davis * reporting a cloner that is added between the unlock and lock 484c859ef97SBrooks Davis * below, but that's not a major problem. Not caping our 485c859ef97SBrooks Davis * allocation to the number of cloners actually in the system 486c859ef97SBrooks Davis * could be because that would let arbitrary users cause us to 487c859ef97SBrooks Davis * allocate abritrary amounts of kernel memory. 488c859ef97SBrooks Davis */ 48937f17770SMarko Zec buf_count = (V_if_cloners_count < ifcr->ifcr_count) ? 49037f17770SMarko Zec V_if_cloners_count : ifcr->ifcr_count; 491c859ef97SBrooks Davis IF_CLONERS_UNLOCK(); 492c859ef97SBrooks Davis 493c859ef97SBrooks Davis outbuf = malloc(IFNAMSIZ*buf_count, M_CLONE, M_WAITOK | M_ZERO); 494f889d2efSBrooks Davis 495f889d2efSBrooks Davis IF_CLONERS_LOCK(); 496f889d2efSBrooks Davis 49737f17770SMarko Zec ifcr->ifcr_total = V_if_cloners_count; 498f889d2efSBrooks Davis if ((dst = ifcr->ifcr_buffer) == NULL) { 499f889d2efSBrooks Davis /* Just asking how many there are. */ 500f889d2efSBrooks Davis goto done; 501f889d2efSBrooks Davis } 50237f17770SMarko Zec count = (V_if_cloners_count < buf_count) ? 50337f17770SMarko Zec V_if_cloners_count : buf_count; 504f889d2efSBrooks Davis 50537f17770SMarko Zec for (ifc = LIST_FIRST(&V_if_cloners), buf = outbuf; 506c859ef97SBrooks Davis ifc != NULL && count != 0; 507c859ef97SBrooks Davis ifc = LIST_NEXT(ifc, ifc_list), count--, buf += IFNAMSIZ) { 508c859ef97SBrooks Davis strlcpy(buf, ifc->ifc_name, IFNAMSIZ); 509f889d2efSBrooks Davis } 510f889d2efSBrooks Davis 511f889d2efSBrooks Davis done: 512f889d2efSBrooks Davis IF_CLONERS_UNLOCK(); 513c859ef97SBrooks Davis if (err == 0) 514c859ef97SBrooks Davis err = copyout(outbuf, dst, buf_count*IFNAMSIZ); 515c859ef97SBrooks Davis if (outbuf != NULL) 516c859ef97SBrooks Davis free(outbuf, M_CLONE); 517f889d2efSBrooks Davis return (err); 518f889d2efSBrooks Davis } 519f889d2efSBrooks Davis 520f889d2efSBrooks Davis /* 521f889d2efSBrooks Davis * A utility function to extract unit numbers from interface names of 522f889d2efSBrooks Davis * the form name###. 523f889d2efSBrooks Davis * 524f889d2efSBrooks Davis * Returns 0 on success and an error on failure. 525f889d2efSBrooks Davis */ 526f889d2efSBrooks Davis int 527f889d2efSBrooks Davis ifc_name2unit(const char *name, int *unit) 528f889d2efSBrooks Davis { 529f889d2efSBrooks Davis const char *cp; 530434dbbb3SRuslan Ermilov int cutoff = INT_MAX / 10; 531434dbbb3SRuslan Ermilov int cutlim = INT_MAX % 10; 532f889d2efSBrooks Davis 533f889d2efSBrooks Davis for (cp = name; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++); 534f889d2efSBrooks Davis if (*cp == '\0') { 535f889d2efSBrooks Davis *unit = -1; 536434dbbb3SRuslan Ermilov } else if (cp[0] == '0' && cp[1] != '\0') { 537434dbbb3SRuslan Ermilov /* Disallow leading zeroes. */ 538434dbbb3SRuslan Ermilov return (EINVAL); 539f889d2efSBrooks Davis } else { 540f889d2efSBrooks Davis for (*unit = 0; *cp != '\0'; cp++) { 541f889d2efSBrooks Davis if (*cp < '0' || *cp > '9') { 542f889d2efSBrooks Davis /* Bogus unit number. */ 543f889d2efSBrooks Davis return (EINVAL); 544f889d2efSBrooks Davis } 545434dbbb3SRuslan Ermilov if (*unit > cutoff || 546434dbbb3SRuslan Ermilov (*unit == cutoff && *cp - '0' > cutlim)) 547434dbbb3SRuslan Ermilov return (EINVAL); 548f889d2efSBrooks Davis *unit = (*unit * 10) + (*cp - '0'); 549f889d2efSBrooks Davis } 550f889d2efSBrooks Davis } 551f889d2efSBrooks Davis 552f889d2efSBrooks Davis return (0); 553f889d2efSBrooks Davis } 554f889d2efSBrooks Davis 555f889d2efSBrooks Davis int 556f889d2efSBrooks Davis ifc_alloc_unit(struct if_clone *ifc, int *unit) 557f889d2efSBrooks Davis { 5582e9fff5bSGleb Smirnoff char name[IFNAMSIZ]; 5592e9fff5bSGleb Smirnoff int wildcard; 560f889d2efSBrooks Davis 561f889d2efSBrooks Davis wildcard = (*unit < 0); 5622e9fff5bSGleb Smirnoff retry: 5633932d760SGleb Smirnoff if (*unit > ifc->ifc_maxunit) 5643932d760SGleb Smirnoff return (ENOSPC); 5653932d760SGleb Smirnoff if (*unit < 0) { 5662e9fff5bSGleb Smirnoff *unit = alloc_unr(ifc->ifc_unrhdr); 5672e9fff5bSGleb Smirnoff if (*unit == -1) 5682e9fff5bSGleb Smirnoff return (ENOSPC); 5692e9fff5bSGleb Smirnoff } else { 5702e9fff5bSGleb Smirnoff *unit = alloc_unr_specific(ifc->ifc_unrhdr, *unit); 5713932d760SGleb Smirnoff if (*unit == -1) { 5723932d760SGleb Smirnoff if (wildcard) { 5733932d760SGleb Smirnoff (*unit)++; 5743932d760SGleb Smirnoff goto retry; 5753932d760SGleb Smirnoff } else 5762e9fff5bSGleb Smirnoff return (EEXIST); 577f889d2efSBrooks Davis } 5783932d760SGleb Smirnoff } 579f889d2efSBrooks Davis 5802e9fff5bSGleb Smirnoff snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, *unit); 5812e9fff5bSGleb Smirnoff if (ifunit(name) != NULL) { 5823932d760SGleb Smirnoff free_unr(ifc->ifc_unrhdr, *unit); 5833932d760SGleb Smirnoff if (wildcard) { 5843932d760SGleb Smirnoff (*unit)++; 5853932d760SGleb Smirnoff goto retry; 5863932d760SGleb Smirnoff } else 5872e9fff5bSGleb Smirnoff return (EEXIST); 588f889d2efSBrooks Davis } 589f889d2efSBrooks Davis 5902e9fff5bSGleb Smirnoff IF_CLONE_ADDREF(ifc); 591f889d2efSBrooks Davis 5922e9fff5bSGleb Smirnoff return (0); 593f889d2efSBrooks Davis } 594f889d2efSBrooks Davis 595f889d2efSBrooks Davis void 596f889d2efSBrooks Davis ifc_free_unit(struct if_clone *ifc, int unit) 597f889d2efSBrooks Davis { 598f889d2efSBrooks Davis 5992e9fff5bSGleb Smirnoff free_unr(ifc->ifc_unrhdr, unit); 6002e9fff5bSGleb Smirnoff IF_CLONE_REMREF(ifc); 601f889d2efSBrooks Davis } 602f889d2efSBrooks Davis 60342a58907SGleb Smirnoff static int 604f889d2efSBrooks Davis ifc_simple_match(struct if_clone *ifc, const char *name) 605f889d2efSBrooks Davis { 606f889d2efSBrooks Davis const char *cp; 607f889d2efSBrooks Davis int i; 608f889d2efSBrooks Davis 609f889d2efSBrooks Davis /* Match the name */ 610f889d2efSBrooks Davis for (cp = name, i = 0; i < strlen(ifc->ifc_name); i++, cp++) { 611f889d2efSBrooks Davis if (ifc->ifc_name[i] != *cp) 612f889d2efSBrooks Davis return (0); 613f889d2efSBrooks Davis } 614f889d2efSBrooks Davis 615f889d2efSBrooks Davis /* Make sure there's a unit number or nothing after the name */ 616f889d2efSBrooks Davis for (; *cp != '\0'; cp++) { 617f889d2efSBrooks Davis if (*cp < '0' || *cp > '9') 618f889d2efSBrooks Davis return (0); 619f889d2efSBrooks Davis } 620f889d2efSBrooks Davis 621f889d2efSBrooks Davis return (1); 622f889d2efSBrooks Davis } 623f889d2efSBrooks Davis 62442a58907SGleb Smirnoff static int 6256b7330e2SSam Leffler ifc_simple_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) 626f889d2efSBrooks Davis { 627f889d2efSBrooks Davis char *dp; 628f889d2efSBrooks Davis int wildcard; 629f889d2efSBrooks Davis int unit; 630f889d2efSBrooks Davis int err; 631f889d2efSBrooks Davis 632f889d2efSBrooks Davis err = ifc_name2unit(name, &unit); 633f889d2efSBrooks Davis if (err != 0) 634f889d2efSBrooks Davis return (err); 635f889d2efSBrooks Davis 636f889d2efSBrooks Davis wildcard = (unit < 0); 637f889d2efSBrooks Davis 638f889d2efSBrooks Davis err = ifc_alloc_unit(ifc, &unit); 639f889d2efSBrooks Davis if (err != 0) 640f889d2efSBrooks Davis return (err); 641f889d2efSBrooks Davis 64242a58907SGleb Smirnoff err = ifc->ifcs_create(ifc, unit, params); 643f889d2efSBrooks Davis if (err != 0) { 644f889d2efSBrooks Davis ifc_free_unit(ifc, unit); 645f889d2efSBrooks Davis return (err); 646f889d2efSBrooks Davis } 647f889d2efSBrooks Davis 648f889d2efSBrooks Davis /* In the wildcard case, we need to update the name. */ 649f889d2efSBrooks Davis if (wildcard) { 650f889d2efSBrooks Davis for (dp = name; *dp != '\0'; dp++); 651f889d2efSBrooks Davis if (snprintf(dp, len - (dp-name), "%d", unit) > 652f889d2efSBrooks Davis len - (dp-name) - 1) { 653f889d2efSBrooks Davis /* 654f889d2efSBrooks Davis * This can only be a programmer error and 655f889d2efSBrooks Davis * there's no straightforward way to recover if 656f889d2efSBrooks Davis * it happens. 657f889d2efSBrooks Davis */ 658f889d2efSBrooks Davis panic("if_clone_create(): interface name too long"); 659f889d2efSBrooks Davis } 660f889d2efSBrooks Davis 661f889d2efSBrooks Davis } 662f889d2efSBrooks Davis 663f889d2efSBrooks Davis return (0); 664f889d2efSBrooks Davis } 665f889d2efSBrooks Davis 66642a58907SGleb Smirnoff static int 667f889d2efSBrooks Davis ifc_simple_destroy(struct if_clone *ifc, struct ifnet *ifp) 668f889d2efSBrooks Davis { 669f889d2efSBrooks Davis int unit; 670f889d2efSBrooks Davis 671f889d2efSBrooks Davis unit = ifp->if_dunit; 672f889d2efSBrooks Davis 67342a58907SGleb Smirnoff if (unit < ifc->ifcs_minifs) 674f889d2efSBrooks Davis return (EINVAL); 675f889d2efSBrooks Davis 67642a58907SGleb Smirnoff ifc->ifcs_destroy(ifp); 677f889d2efSBrooks Davis 678f889d2efSBrooks Davis ifc_free_unit(ifc, unit); 679f889d2efSBrooks Davis 680f889d2efSBrooks Davis return (0); 681f889d2efSBrooks Davis } 682