1139823Simp/*- 2241610Sglebius * Copyright (c) 2012 Gleb Smirnoff <glebius@FreeBSD.org> 3130933Sbrooks * Copyright (c) 1980, 1986, 1993 4130933Sbrooks * The Regents of the University of California. All rights reserved. 5130933Sbrooks * 6130933Sbrooks * Redistribution and use in source and binary forms, with or without 7130933Sbrooks * modification, are permitted provided that the following conditions 8130933Sbrooks * are met: 9130933Sbrooks * 1. Redistributions of source code must retain the above copyright 10130933Sbrooks * notice, this list of conditions and the following disclaimer. 11130933Sbrooks * 2. Redistributions in binary form must reproduce the above copyright 12130933Sbrooks * notice, this list of conditions and the following disclaimer in the 13130933Sbrooks * documentation and/or other materials provided with the distribution. 14130933Sbrooks * 4. Neither the name of the University nor the names of its contributors 15130933Sbrooks * may be used to endorse or promote products derived from this software 16130933Sbrooks * without specific prior written permission. 17130933Sbrooks * 18130933Sbrooks * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19130933Sbrooks * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20130933Sbrooks * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21130933Sbrooks * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22130933Sbrooks * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23130933Sbrooks * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24130933Sbrooks * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25130933Sbrooks * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26130933Sbrooks * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27130933Sbrooks * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28130933Sbrooks * SUCH DAMAGE. 29130933Sbrooks * 30130933Sbrooks * @(#)if.c 8.5 (Berkeley) 1/9/95 31130933Sbrooks * $FreeBSD: stable/10/sys/net/if_clone.c 324813 2017-10-21 10:48:06Z avos $ 32130933Sbrooks */ 33130933Sbrooks 34130933Sbrooks#include <sys/param.h> 35130933Sbrooks#include <sys/malloc.h> 36152779Sru#include <sys/limits.h> 37130933Sbrooks#include <sys/lock.h> 38130933Sbrooks#include <sys/mutex.h> 39130933Sbrooks#include <sys/kernel.h> 40130933Sbrooks#include <sys/systm.h> 41130933Sbrooks#include <sys/types.h> 42130933Sbrooks#include <sys/socket.h> 43130933Sbrooks 44130933Sbrooks#include <net/if.h> 45130933Sbrooks#include <net/if_clone.h> 46130933Sbrooks#include <net/if_var.h> 47130933Sbrooks#include <net/radix.h> 48130933Sbrooks#include <net/route.h> 49191816Szec#include <net/vnet.h> 50130933Sbrooks 51241610Sglebius/* Current IF_MAXUNIT expands maximum to 5 characters. */ 52241610Sglebius#define IFCLOSIZ (IFNAMSIZ - 5) 53241610Sglebius 54241610Sglebius/* 55241610Sglebius * Structure describing a `cloning' interface. 56241610Sglebius * 57241610Sglebius * List of locks 58241610Sglebius * (c) const until freeing 59241610Sglebius * (d) driver specific data, may need external protection. 60241610Sglebius * (e) locked by if_cloners_mtx 61241610Sglebius * (i) locked by ifc_mtx mtx 62241610Sglebius */ 63241610Sglebiusstruct if_clone { 64241610Sglebius char ifc_name[IFCLOSIZ]; /* (c) Name of device, e.g. `gif' */ 65241610Sglebius struct unrhdr *ifc_unrhdr; /* (c) alloc_unr(9) header */ 66241610Sglebius int ifc_maxunit; /* (c) maximum unit number */ 67241610Sglebius long ifc_refcnt; /* (i) Reference count. */ 68241610Sglebius LIST_HEAD(, ifnet) ifc_iflist; /* (i) List of cloned interfaces */ 69241610Sglebius struct mtx ifc_mtx; /* Mutex to protect members. */ 70241610Sglebius 71241610Sglebius enum { SIMPLE, ADVANCED } ifc_type; /* (c) */ 72241610Sglebius 73241610Sglebius /* (c) Driver specific cloning functions. Called with no locks held. */ 74241610Sglebius union { 75241610Sglebius struct { /* advanced cloner */ 76241610Sglebius ifc_match_t *_ifc_match; 77241610Sglebius ifc_create_t *_ifc_create; 78241610Sglebius ifc_destroy_t *_ifc_destroy; 79241610Sglebius } A; 80241610Sglebius struct { /* simple cloner */ 81241610Sglebius ifcs_create_t *_ifcs_create; 82241610Sglebius ifcs_destroy_t *_ifcs_destroy; 83241610Sglebius int _ifcs_minifs; /* minimum ifs */ 84241610Sglebius 85241610Sglebius } S; 86241610Sglebius } U; 87241610Sglebius#define ifc_match U.A._ifc_match 88241610Sglebius#define ifc_create U.A._ifc_create 89241610Sglebius#define ifc_destroy U.A._ifc_destroy 90241610Sglebius#define ifcs_create U.S._ifcs_create 91241610Sglebius#define ifcs_destroy U.S._ifcs_destroy 92241610Sglebius#define ifcs_minifs U.S._ifcs_minifs 93241610Sglebius 94241610Sglebius LIST_ENTRY(if_clone) ifc_list; /* (e) On list of cloners */ 95241610Sglebius}; 96241610Sglebius 97152209Sthompsastatic void if_clone_free(struct if_clone *ifc); 98160195Ssamstatic int if_clone_createif(struct if_clone *ifc, char *name, size_t len, 99160195Ssam caddr_t params); 100130933Sbrooks 101241610Sglebiusstatic int ifc_simple_match(struct if_clone *, const char *); 102241610Sglebiusstatic int ifc_simple_create(struct if_clone *, char *, size_t, caddr_t); 103241610Sglebiusstatic int ifc_simple_destroy(struct if_clone *, struct ifnet *); 104241610Sglebius 105130933Sbrooksstatic struct mtx if_cloners_mtx; 106215701Sdimstatic VNET_DEFINE(int, if_cloners_count); 107195699SrwatsonVNET_DEFINE(LIST_HEAD(, if_clone), if_cloners); 108130933Sbrooks 109195727Srwatson#define V_if_cloners_count VNET(if_cloners_count) 110195727Srwatson#define V_if_cloners VNET(if_cloners) 111195699Srwatson 112130933Sbrooks#define IF_CLONERS_LOCK_INIT() \ 113130933Sbrooks mtx_init(&if_cloners_mtx, "if_cloners lock", NULL, MTX_DEF) 114130933Sbrooks#define IF_CLONERS_LOCK_ASSERT() mtx_assert(&if_cloners_mtx, MA_OWNED) 115130933Sbrooks#define IF_CLONERS_LOCK() mtx_lock(&if_cloners_mtx) 116130933Sbrooks#define IF_CLONERS_UNLOCK() mtx_unlock(&if_cloners_mtx) 117130933Sbrooks 118130933Sbrooks#define IF_CLONE_LOCK_INIT(ifc) \ 119130933Sbrooks mtx_init(&(ifc)->ifc_mtx, "if_clone lock", NULL, MTX_DEF) 120130933Sbrooks#define IF_CLONE_LOCK_DESTROY(ifc) mtx_destroy(&(ifc)->ifc_mtx) 121130933Sbrooks#define IF_CLONE_LOCK_ASSERT(ifc) mtx_assert(&(ifc)->ifc_mtx, MA_OWNED) 122130933Sbrooks#define IF_CLONE_LOCK(ifc) mtx_lock(&(ifc)->ifc_mtx) 123130933Sbrooks#define IF_CLONE_UNLOCK(ifc) mtx_unlock(&(ifc)->ifc_mtx) 124130933Sbrooks 125130933Sbrooks#define IF_CLONE_ADDREF(ifc) \ 126130933Sbrooks do { \ 127130933Sbrooks IF_CLONE_LOCK(ifc); \ 128130933Sbrooks IF_CLONE_ADDREF_LOCKED(ifc); \ 129130933Sbrooks IF_CLONE_UNLOCK(ifc); \ 130130933Sbrooks } while (0) 131130933Sbrooks#define IF_CLONE_ADDREF_LOCKED(ifc) \ 132130933Sbrooks do { \ 133130933Sbrooks IF_CLONE_LOCK_ASSERT(ifc); \ 134130933Sbrooks KASSERT((ifc)->ifc_refcnt >= 0, \ 135130933Sbrooks ("negative refcnt %ld", (ifc)->ifc_refcnt)); \ 136130933Sbrooks (ifc)->ifc_refcnt++; \ 137130933Sbrooks } while (0) 138130933Sbrooks#define IF_CLONE_REMREF(ifc) \ 139130933Sbrooks do { \ 140130933Sbrooks IF_CLONE_LOCK(ifc); \ 141130933Sbrooks IF_CLONE_REMREF_LOCKED(ifc); \ 142130933Sbrooks } while (0) 143130933Sbrooks#define IF_CLONE_REMREF_LOCKED(ifc) \ 144130933Sbrooks do { \ 145130933Sbrooks IF_CLONE_LOCK_ASSERT(ifc); \ 146130933Sbrooks KASSERT((ifc)->ifc_refcnt > 0, \ 147130933Sbrooks ("bogus refcnt %ld", (ifc)->ifc_refcnt)); \ 148130933Sbrooks if (--(ifc)->ifc_refcnt == 0) { \ 149130933Sbrooks IF_CLONE_UNLOCK(ifc); \ 150130933Sbrooks if_clone_free(ifc); \ 151132470Smlaier } else { \ 152132470Smlaier /* silently free the lock */ \ 153132470Smlaier IF_CLONE_UNLOCK(ifc); \ 154130933Sbrooks } \ 155130933Sbrooks } while (0) 156130933Sbrooks 157152209Sthompsa#define IFC_IFLIST_INSERT(_ifc, _ifp) \ 158152209Sthompsa LIST_INSERT_HEAD(&_ifc->ifc_iflist, _ifp, if_clones) 159152209Sthompsa#define IFC_IFLIST_REMOVE(_ifc, _ifp) \ 160152209Sthompsa LIST_REMOVE(_ifp, if_clones) 161152209Sthompsa 162141616Sphkstatic MALLOC_DEFINE(M_CLONE, "clone", "interface cloning framework"); 163130933Sbrooks 164195837Srwatsonvoid 165195837Srwatsonvnet_if_clone_init(void) 166192669Szec{ 167192669Szec 168192669Szec LIST_INIT(&V_if_cloners); 169192669Szec} 170192669Szec 171130933Sbrooksvoid 172130933Sbrooksif_clone_init(void) 173130933Sbrooks{ 174192669Szec 175130933Sbrooks IF_CLONERS_LOCK_INIT(); 176130933Sbrooks} 177130933Sbrooks 178130933Sbrooks/* 179152209Sthompsa * Lookup and create a clone network interface. 180130933Sbrooks */ 181130933Sbrooksint 182160195Ssamif_clone_create(char *name, size_t len, caddr_t params) 183130933Sbrooks{ 184130933Sbrooks struct if_clone *ifc; 185130933Sbrooks 186130933Sbrooks /* Try to find an applicable cloner for this request */ 187130933Sbrooks IF_CLONERS_LOCK(); 188241610Sglebius LIST_FOREACH(ifc, &V_if_cloners, ifc_list) 189241610Sglebius if (ifc->ifc_type == SIMPLE) { 190241610Sglebius if (ifc_simple_match(ifc, name)) 191241610Sglebius break; 192241610Sglebius } else { 193241610Sglebius if (ifc->ifc_match(ifc, name)) 194241610Sglebius break; 195130933Sbrooks } 196192669Szec#ifdef VIMAGE 197192669Szec if (ifc == NULL && !IS_DEFAULT_VNET(curvnet)) { 198192669Szec CURVNET_SET_QUIET(vnet0); 199241610Sglebius LIST_FOREACH(ifc, &V_if_cloners, ifc_list) 200241610Sglebius if (ifc->ifc_type == SIMPLE) { 201241610Sglebius if (ifc_simple_match(ifc, name)) 202241610Sglebius break; 203241610Sglebius } else { 204241610Sglebius if (ifc->ifc_match(ifc, name)) 205241610Sglebius break; 206241610Sglebius } 207192669Szec CURVNET_RESTORE(); 208192669Szec } 209192669Szec#endif 210130933Sbrooks IF_CLONERS_UNLOCK(); 211130933Sbrooks 212130933Sbrooks if (ifc == NULL) 213130933Sbrooks return (EINVAL); 214130933Sbrooks 215160195Ssam return (if_clone_createif(ifc, name, len, params)); 216152209Sthompsa} 217152209Sthompsa 218152209Sthompsa/* 219152209Sthompsa * Create a clone network interface. 220152209Sthompsa */ 221152209Sthompsastatic int 222160195Ssamif_clone_createif(struct if_clone *ifc, char *name, size_t len, caddr_t params) 223152209Sthompsa{ 224152209Sthompsa int err; 225152209Sthompsa struct ifnet *ifp; 226152209Sthompsa 227152209Sthompsa if (ifunit(name) != NULL) 228152209Sthompsa return (EEXIST); 229152209Sthompsa 230241610Sglebius if (ifc->ifc_type == SIMPLE) 231241610Sglebius err = ifc_simple_create(ifc, name, len, params); 232241610Sglebius else 233241610Sglebius err = (*ifc->ifc_create)(ifc, name, len, params); 234152209Sthompsa 235152209Sthompsa if (!err) { 236152209Sthompsa ifp = ifunit(name); 237152209Sthompsa if (ifp == NULL) 238152209Sthompsa panic("%s: lookup failed for %s", __func__, name); 239152209Sthompsa 240159781Smlaier if_addgroup(ifp, ifc->ifc_name); 241159781Smlaier 242152209Sthompsa IF_CLONE_LOCK(ifc); 243152209Sthompsa IFC_IFLIST_INSERT(ifc, ifp); 244152209Sthompsa IF_CLONE_UNLOCK(ifc); 245152209Sthompsa } 246152209Sthompsa 247130933Sbrooks return (err); 248130933Sbrooks} 249130933Sbrooks 250130933Sbrooks/* 251152209Sthompsa * Lookup and destroy a clone network interface. 252130933Sbrooks */ 253130933Sbrooksint 254130933Sbrooksif_clone_destroy(const char *name) 255130933Sbrooks{ 256206488Sbz int err; 257130933Sbrooks struct if_clone *ifc; 258130933Sbrooks struct ifnet *ifp; 259130933Sbrooks 260206488Sbz ifp = ifunit_ref(name); 261130933Sbrooks if (ifp == NULL) 262130933Sbrooks return (ENXIO); 263130933Sbrooks 264130933Sbrooks /* Find the cloner for this interface */ 265130933Sbrooks IF_CLONERS_LOCK(); 266192669Szec LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { 267130933Sbrooks if (strcmp(ifc->ifc_name, ifp->if_dname) == 0) { 268130933Sbrooks break; 269130933Sbrooks } 270130933Sbrooks } 271192669Szec#ifdef VIMAGE 272192669Szec if (ifc == NULL && !IS_DEFAULT_VNET(curvnet)) { 273192669Szec CURVNET_SET_QUIET(vnet0); 274241610Sglebius LIST_FOREACH(ifc, &V_if_cloners, ifc_list) 275241650Sglebius if (ifc->ifc_type == SIMPLE) { 276241610Sglebius if (ifc_simple_match(ifc, name)) 277241610Sglebius break; 278241610Sglebius } else { 279241610Sglebius if (ifc->ifc_match(ifc, name)) 280241610Sglebius break; 281241610Sglebius } 282192669Szec CURVNET_RESTORE(); 283192669Szec } 284192669Szec#endif 285130933Sbrooks IF_CLONERS_UNLOCK(); 286206488Sbz if (ifc == NULL) { 287206488Sbz if_rele(ifp); 288130933Sbrooks return (EINVAL); 289206488Sbz } 290130933Sbrooks 291206488Sbz err = if_clone_destroyif(ifc, ifp); 292206488Sbz if_rele(ifp); 293206488Sbz return err; 294152209Sthompsa} 295152209Sthompsa 296152209Sthompsa/* 297152209Sthompsa * Destroy a clone network interface. 298152209Sthompsa */ 299183210Sthompsaint 300152209Sthompsaif_clone_destroyif(struct if_clone *ifc, struct ifnet *ifp) 301152209Sthompsa{ 302152209Sthompsa int err; 303206486Sbz struct ifnet *ifcifp; 304152209Sthompsa 305241610Sglebius if (ifc->ifc_type == ADVANCED && ifc->ifc_destroy == NULL) 306191816Szec return(EOPNOTSUPP); 307130933Sbrooks 308192669Szec /* 309192669Szec * Given that the cloned ifnet might be attached to a different 310192669Szec * vnet from where its cloner was registered, we have to 311192669Szec * switch to the vnet context of the target vnet. 312192669Szec */ 313192669Szec CURVNET_SET_QUIET(ifp->if_vnet); 314192669Szec 315152779Sru IF_CLONE_LOCK(ifc); 316206486Sbz LIST_FOREACH(ifcifp, &ifc->ifc_iflist, if_clones) { 317206486Sbz if (ifcifp == ifp) { 318206486Sbz IFC_IFLIST_REMOVE(ifc, ifp); 319206486Sbz break; 320206486Sbz } 321206486Sbz } 322152779Sru IF_CLONE_UNLOCK(ifc); 323206486Sbz if (ifcifp == NULL) { 324206486Sbz CURVNET_RESTORE(); 325206486Sbz return (ENXIO); /* ifp is not on the list. */ 326206486Sbz } 327152779Sru 328159781Smlaier if_delgroup(ifp, ifc->ifc_name); 329159781Smlaier 330241610Sglebius if (ifc->ifc_type == SIMPLE) 331241610Sglebius err = ifc_simple_destroy(ifc, ifp); 332241610Sglebius else 333241610Sglebius err = (*ifc->ifc_destroy)(ifc, ifp); 334130933Sbrooks 335152779Sru if (err != 0) { 336159781Smlaier if_addgroup(ifp, ifc->ifc_name); 337159781Smlaier 338152779Sru IF_CLONE_LOCK(ifc); 339152779Sru IFC_IFLIST_INSERT(ifc, ifp); 340152779Sru IF_CLONE_UNLOCK(ifc); 341152779Sru } 342191816Szec CURVNET_RESTORE(); 343130933Sbrooks return (err); 344130933Sbrooks} 345130933Sbrooks 346241610Sglebiusstatic struct if_clone * 347241610Sglebiusif_clone_alloc(const char *name, int maxunit) 348130933Sbrooks{ 349241610Sglebius struct if_clone *ifc; 350130933Sbrooks 351241610Sglebius KASSERT(name != NULL, ("%s: no name\n", __func__)); 352228071Sglebius 353241610Sglebius ifc = malloc(sizeof(struct if_clone), M_CLONE, M_WAITOK | M_ZERO); 354241610Sglebius strncpy(ifc->ifc_name, name, IFCLOSIZ-1); 355130933Sbrooks IF_CLONE_LOCK_INIT(ifc); 356130933Sbrooks IF_CLONE_ADDREF(ifc); 357241610Sglebius ifc->ifc_maxunit = maxunit ? maxunit : IF_MAXUNIT; 358228071Sglebius ifc->ifc_unrhdr = new_unrhdr(0, ifc->ifc_maxunit, &ifc->ifc_mtx); 359228071Sglebius LIST_INIT(&ifc->ifc_iflist); 360130933Sbrooks 361241610Sglebius return (ifc); 362241610Sglebius} 363241610Sglebius 364241610Sglebiusstatic int 365241610Sglebiusif_clone_attach(struct if_clone *ifc) 366241610Sglebius{ 367241610Sglebius struct if_clone *ifc1; 368241610Sglebius 369130933Sbrooks IF_CLONERS_LOCK(); 370228071Sglebius LIST_FOREACH(ifc1, &V_if_cloners, ifc_list) 371228071Sglebius if (strcmp(ifc->ifc_name, ifc1->ifc_name) == 0) { 372228071Sglebius IF_CLONERS_UNLOCK(); 373228071Sglebius IF_CLONE_REMREF(ifc); 374228071Sglebius return (EEXIST); 375228071Sglebius } 376192669Szec LIST_INSERT_HEAD(&V_if_cloners, ifc, ifc_list); 377192669Szec V_if_cloners_count++; 378130933Sbrooks IF_CLONERS_UNLOCK(); 379130933Sbrooks 380241610Sglebius return (0); 381241610Sglebius} 382241610Sglebius 383241610Sglebiusstruct if_clone * 384241610Sglebiusif_clone_advanced(const char *name, u_int maxunit, ifc_match_t match, 385241610Sglebius ifc_create_t create, ifc_destroy_t destroy) 386241610Sglebius{ 387241610Sglebius struct if_clone *ifc; 388241610Sglebius 389241610Sglebius ifc = if_clone_alloc(name, maxunit); 390241610Sglebius ifc->ifc_type = ADVANCED; 391241610Sglebius ifc->ifc_match = match; 392241610Sglebius ifc->ifc_create = create; 393241610Sglebius ifc->ifc_destroy = destroy; 394241610Sglebius 395241610Sglebius if (if_clone_attach(ifc) != 0) { 396241610Sglebius if_clone_free(ifc); 397241610Sglebius return (NULL); 398241610Sglebius } 399241610Sglebius 400130933Sbrooks EVENTHANDLER_INVOKE(if_clone_event, ifc); 401228071Sglebius 402241610Sglebius return (ifc); 403130933Sbrooks} 404130933Sbrooks 405241610Sglebiusstruct if_clone * 406241610Sglebiusif_clone_simple(const char *name, ifcs_create_t create, ifcs_destroy_t destroy, 407241610Sglebius u_int minifs) 408241610Sglebius{ 409241610Sglebius struct if_clone *ifc; 410241610Sglebius u_int unit; 411241610Sglebius 412241610Sglebius ifc = if_clone_alloc(name, 0); 413241610Sglebius ifc->ifc_type = SIMPLE; 414241610Sglebius ifc->ifcs_create = create; 415241610Sglebius ifc->ifcs_destroy = destroy; 416241610Sglebius ifc->ifcs_minifs = minifs; 417241610Sglebius 418241610Sglebius if (if_clone_attach(ifc) != 0) { 419241610Sglebius if_clone_free(ifc); 420241610Sglebius return (NULL); 421241610Sglebius } 422241610Sglebius 423241610Sglebius for (unit = 0; unit < minifs; unit++) { 424241610Sglebius char name[IFNAMSIZ]; 425241610Sglebius int error; 426241610Sglebius 427241610Sglebius snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, unit); 428241610Sglebius error = if_clone_createif(ifc, name, IFNAMSIZ, NULL); 429241610Sglebius KASSERT(error == 0, 430241610Sglebius ("%s: failed to create required interface %s", 431241610Sglebius __func__, name)); 432241610Sglebius } 433241610Sglebius 434241610Sglebius EVENTHANDLER_INVOKE(if_clone_event, ifc); 435241610Sglebius 436241610Sglebius return (ifc); 437241610Sglebius} 438241610Sglebius 439130933Sbrooks/* 440130933Sbrooks * Unregister a network interface cloner. 441130933Sbrooks */ 442130933Sbrooksvoid 443130933Sbrooksif_clone_detach(struct if_clone *ifc) 444130933Sbrooks{ 445130933Sbrooks 446130933Sbrooks IF_CLONERS_LOCK(); 447130933Sbrooks LIST_REMOVE(ifc, ifc_list); 448192669Szec V_if_cloners_count--; 449130933Sbrooks IF_CLONERS_UNLOCK(); 450130933Sbrooks 451151266Sthompsa /* Allow all simples to be destroyed */ 452241610Sglebius if (ifc->ifc_type == SIMPLE) 453241610Sglebius ifc->ifcs_minifs = 0; 454151266Sthompsa 455152209Sthompsa /* destroy all interfaces for this cloner */ 456152209Sthompsa while (!LIST_EMPTY(&ifc->ifc_iflist)) 457152209Sthompsa if_clone_destroyif(ifc, LIST_FIRST(&ifc->ifc_iflist)); 458152209Sthompsa 459130933Sbrooks IF_CLONE_REMREF(ifc); 460130933Sbrooks} 461130933Sbrooks 462130933Sbrooksstatic void 463130933Sbrooksif_clone_free(struct if_clone *ifc) 464130933Sbrooks{ 465130933Sbrooks 466152209Sthompsa KASSERT(LIST_EMPTY(&ifc->ifc_iflist), 467152209Sthompsa ("%s: ifc_iflist not empty", __func__)); 468152209Sthompsa 469130933Sbrooks IF_CLONE_LOCK_DESTROY(ifc); 470228071Sglebius delete_unrhdr(ifc->ifc_unrhdr); 471241610Sglebius free(ifc, M_CLONE); 472130933Sbrooks} 473130933Sbrooks 474130933Sbrooks/* 475130933Sbrooks * Provide list of interface cloners to userspace. 476130933Sbrooks */ 477130933Sbrooksint 478130933Sbrooksif_clone_list(struct if_clonereq *ifcr) 479130933Sbrooks{ 480135256Sbrooks char *buf, *dst, *outbuf = NULL; 481130933Sbrooks struct if_clone *ifc; 482135256Sbrooks int buf_count, count, err = 0; 483130933Sbrooks 484142378Smaxim if (ifcr->ifcr_count < 0) 485142378Smaxim return (EINVAL); 486142378Smaxim 487130933Sbrooks IF_CLONERS_LOCK(); 488135256Sbrooks /* 489135256Sbrooks * Set our internal output buffer size. We could end up not 490135256Sbrooks * reporting a cloner that is added between the unlock and lock 491135256Sbrooks * below, but that's not a major problem. Not caping our 492135256Sbrooks * allocation to the number of cloners actually in the system 493135256Sbrooks * could be because that would let arbitrary users cause us to 494135256Sbrooks * allocate abritrary amounts of kernel memory. 495135256Sbrooks */ 496192669Szec buf_count = (V_if_cloners_count < ifcr->ifcr_count) ? 497192669Szec V_if_cloners_count : ifcr->ifcr_count; 498135256Sbrooks IF_CLONERS_UNLOCK(); 499130933Sbrooks 500135256Sbrooks outbuf = malloc(IFNAMSIZ*buf_count, M_CLONE, M_WAITOK | M_ZERO); 501135256Sbrooks 502135256Sbrooks IF_CLONERS_LOCK(); 503135256Sbrooks 504192669Szec ifcr->ifcr_total = V_if_cloners_count; 505130933Sbrooks if ((dst = ifcr->ifcr_buffer) == NULL) { 506130933Sbrooks /* Just asking how many there are. */ 507130933Sbrooks goto done; 508130933Sbrooks } 509192669Szec count = (V_if_cloners_count < buf_count) ? 510192669Szec V_if_cloners_count : buf_count; 511130933Sbrooks 512192669Szec for (ifc = LIST_FIRST(&V_if_cloners), buf = outbuf; 513135256Sbrooks ifc != NULL && count != 0; 514135256Sbrooks ifc = LIST_NEXT(ifc, ifc_list), count--, buf += IFNAMSIZ) { 515135256Sbrooks strlcpy(buf, ifc->ifc_name, IFNAMSIZ); 516130933Sbrooks } 517130933Sbrooks 518130933Sbrooksdone: 519130933Sbrooks IF_CLONERS_UNLOCK(); 520135256Sbrooks if (err == 0) 521135256Sbrooks err = copyout(outbuf, dst, buf_count*IFNAMSIZ); 522135256Sbrooks if (outbuf != NULL) 523135256Sbrooks free(outbuf, M_CLONE); 524130933Sbrooks return (err); 525130933Sbrooks} 526130933Sbrooks 527130933Sbrooks/* 528285824Shrs * if_clone_findifc() looks up ifnet from the current 529285824Shrs * cloner list, and returns ifc if found. Note that ifc_refcnt 530285824Shrs * is incremented. 531285824Shrs */ 532285824Shrsstruct if_clone * 533285824Shrsif_clone_findifc(struct ifnet *ifp) 534285824Shrs{ 535285824Shrs struct if_clone *ifc, *ifc0; 536285824Shrs struct ifnet *ifcifp; 537285824Shrs 538285824Shrs ifc0 = NULL; 539285824Shrs IF_CLONERS_LOCK(); 540285824Shrs LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { 541285824Shrs IF_CLONE_LOCK(ifc); 542285824Shrs LIST_FOREACH(ifcifp, &ifc->ifc_iflist, if_clones) { 543285824Shrs if (ifp == ifcifp) { 544285824Shrs ifc0 = ifc; 545285824Shrs IF_CLONE_ADDREF_LOCKED(ifc); 546285824Shrs break; 547285824Shrs } 548285824Shrs } 549285824Shrs IF_CLONE_UNLOCK(ifc); 550285824Shrs if (ifc0 != NULL) 551285824Shrs break; 552285824Shrs } 553285824Shrs IF_CLONERS_UNLOCK(); 554285824Shrs 555285824Shrs return (ifc0); 556285824Shrs} 557285824Shrs 558285824Shrs/* 559285824Shrs * if_clone_addgroup() decrements ifc_refcnt because it is called after 560285824Shrs * if_clone_findifc(). 561285824Shrs */ 562285824Shrsvoid 563285824Shrsif_clone_addgroup(struct ifnet *ifp, struct if_clone *ifc) 564285824Shrs{ 565285824Shrs 566285824Shrs if_addgroup(ifp, ifc->ifc_name); 567285824Shrs IF_CLONE_REMREF(ifc); 568285824Shrs} 569285824Shrs 570285824Shrs/* 571130933Sbrooks * A utility function to extract unit numbers from interface names of 572130933Sbrooks * the form name###. 573130933Sbrooks * 574130933Sbrooks * Returns 0 on success and an error on failure. 575130933Sbrooks */ 576130933Sbrooksint 577130933Sbrooksifc_name2unit(const char *name, int *unit) 578130933Sbrooks{ 579130933Sbrooks const char *cp; 580152779Sru int cutoff = INT_MAX / 10; 581152779Sru int cutlim = INT_MAX % 10; 582130933Sbrooks 583130933Sbrooks for (cp = name; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++); 584130933Sbrooks if (*cp == '\0') { 585130933Sbrooks *unit = -1; 586152779Sru } else if (cp[0] == '0' && cp[1] != '\0') { 587152779Sru /* Disallow leading zeroes. */ 588152779Sru return (EINVAL); 589130933Sbrooks } else { 590130933Sbrooks for (*unit = 0; *cp != '\0'; cp++) { 591130933Sbrooks if (*cp < '0' || *cp > '9') { 592130933Sbrooks /* Bogus unit number. */ 593130933Sbrooks return (EINVAL); 594130933Sbrooks } 595152779Sru if (*unit > cutoff || 596152779Sru (*unit == cutoff && *cp - '0' > cutlim)) 597152779Sru return (EINVAL); 598130933Sbrooks *unit = (*unit * 10) + (*cp - '0'); 599130933Sbrooks } 600130933Sbrooks } 601130933Sbrooks 602130933Sbrooks return (0); 603130933Sbrooks} 604130933Sbrooks 605324813Savosstatic int 606324813Savosifc_alloc_unit_specific(struct if_clone *ifc, int *unit) 607130933Sbrooks{ 608228071Sglebius char name[IFNAMSIZ]; 609130933Sbrooks 610239905Sglebius if (*unit > ifc->ifc_maxunit) 611239905Sglebius return (ENOSPC); 612130933Sbrooks 613324813Savos if (alloc_unr_specific(ifc->ifc_unrhdr, *unit) == -1) 614324813Savos return (EEXIST); 615324813Savos 616228071Sglebius snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, *unit); 617228071Sglebius if (ifunit(name) != NULL) { 618239905Sglebius free_unr(ifc->ifc_unrhdr, *unit); 619324813Savos return (EEXIST); 620130933Sbrooks } 621130933Sbrooks 622228071Sglebius IF_CLONE_ADDREF(ifc); 623130933Sbrooks 624228071Sglebius return (0); 625130933Sbrooks} 626130933Sbrooks 627324813Savosstatic int 628324813Savosifc_alloc_unit_next(struct if_clone *ifc, int *unit) 629324813Savos{ 630324813Savos int error; 631324813Savos 632324813Savos *unit = alloc_unr(ifc->ifc_unrhdr); 633324813Savos if (*unit == -1) 634324813Savos return (ENOSPC); 635324813Savos 636324813Savos free_unr(ifc->ifc_unrhdr, *unit); 637324813Savos for (;;) { 638324813Savos error = ifc_alloc_unit_specific(ifc, unit); 639324813Savos if (error != EEXIST) 640324813Savos break; 641324813Savos 642324813Savos (*unit)++; 643324813Savos } 644324813Savos 645324813Savos return (error); 646324813Savos} 647324813Savos 648324813Savosint 649324813Savosifc_alloc_unit(struct if_clone *ifc, int *unit) 650324813Savos{ 651324813Savos if (*unit < 0) 652324813Savos return (ifc_alloc_unit_next(ifc, unit)); 653324813Savos else 654324813Savos return (ifc_alloc_unit_specific(ifc, unit)); 655324813Savos} 656324813Savos 657130933Sbrooksvoid 658130933Sbrooksifc_free_unit(struct if_clone *ifc, int unit) 659130933Sbrooks{ 660130933Sbrooks 661228071Sglebius free_unr(ifc->ifc_unrhdr, unit); 662228071Sglebius IF_CLONE_REMREF(ifc); 663130933Sbrooks} 664130933Sbrooks 665241610Sglebiusstatic int 666130933Sbrooksifc_simple_match(struct if_clone *ifc, const char *name) 667130933Sbrooks{ 668130933Sbrooks const char *cp; 669130933Sbrooks int i; 670130933Sbrooks 671130933Sbrooks /* Match the name */ 672130933Sbrooks for (cp = name, i = 0; i < strlen(ifc->ifc_name); i++, cp++) { 673130933Sbrooks if (ifc->ifc_name[i] != *cp) 674130933Sbrooks return (0); 675130933Sbrooks } 676130933Sbrooks 677130933Sbrooks /* Make sure there's a unit number or nothing after the name */ 678130933Sbrooks for (; *cp != '\0'; cp++) { 679130933Sbrooks if (*cp < '0' || *cp > '9') 680130933Sbrooks return (0); 681130933Sbrooks } 682130933Sbrooks 683130933Sbrooks return (1); 684130933Sbrooks} 685130933Sbrooks 686241610Sglebiusstatic int 687160195Ssamifc_simple_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) 688130933Sbrooks{ 689130933Sbrooks char *dp; 690130933Sbrooks int wildcard; 691130933Sbrooks int unit; 692130933Sbrooks int err; 693130933Sbrooks 694130933Sbrooks err = ifc_name2unit(name, &unit); 695130933Sbrooks if (err != 0) 696130933Sbrooks return (err); 697130933Sbrooks 698130933Sbrooks wildcard = (unit < 0); 699130933Sbrooks 700130933Sbrooks err = ifc_alloc_unit(ifc, &unit); 701130933Sbrooks if (err != 0) 702130933Sbrooks return (err); 703130933Sbrooks 704241610Sglebius err = ifc->ifcs_create(ifc, unit, params); 705130933Sbrooks if (err != 0) { 706130933Sbrooks ifc_free_unit(ifc, unit); 707130933Sbrooks return (err); 708130933Sbrooks } 709130933Sbrooks 710130933Sbrooks /* In the wildcard case, we need to update the name. */ 711130933Sbrooks if (wildcard) { 712130933Sbrooks for (dp = name; *dp != '\0'; dp++); 713130933Sbrooks if (snprintf(dp, len - (dp-name), "%d", unit) > 714130933Sbrooks len - (dp-name) - 1) { 715130933Sbrooks /* 716130933Sbrooks * This can only be a programmer error and 717130933Sbrooks * there's no straightforward way to recover if 718130933Sbrooks * it happens. 719130933Sbrooks */ 720130933Sbrooks panic("if_clone_create(): interface name too long"); 721130933Sbrooks } 722130933Sbrooks 723130933Sbrooks } 724130933Sbrooks 725130933Sbrooks return (0); 726130933Sbrooks} 727130933Sbrooks 728241610Sglebiusstatic int 729130933Sbrooksifc_simple_destroy(struct if_clone *ifc, struct ifnet *ifp) 730130933Sbrooks{ 731130933Sbrooks int unit; 732130933Sbrooks 733130933Sbrooks unit = ifp->if_dunit; 734130933Sbrooks 735241610Sglebius if (unit < ifc->ifcs_minifs) 736130933Sbrooks return (EINVAL); 737130933Sbrooks 738241610Sglebius ifc->ifcs_destroy(ifp); 739130933Sbrooks 740130933Sbrooks ifc_free_unit(ifc, unit); 741130933Sbrooks 742130933Sbrooks return (0); 743130933Sbrooks} 744