1c398230bSWarner Losh /*- 251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 351369649SPedro F. Giffuni * 442a58907SGleb Smirnoff * Copyright (c) 2012 Gleb Smirnoff <glebius@FreeBSD.org> 5f889d2efSBrooks Davis * Copyright (c) 1980, 1986, 1993 6f889d2efSBrooks Davis * The Regents of the University of California. All rights reserved. 7f889d2efSBrooks Davis * 8f889d2efSBrooks Davis * Redistribution and use in source and binary forms, with or without 9f889d2efSBrooks Davis * modification, are permitted provided that the following conditions 10f889d2efSBrooks Davis * are met: 11f889d2efSBrooks Davis * 1. Redistributions of source code must retain the above copyright 12f889d2efSBrooks Davis * notice, this list of conditions and the following disclaimer. 13f889d2efSBrooks Davis * 2. Redistributions in binary form must reproduce the above copyright 14f889d2efSBrooks Davis * notice, this list of conditions and the following disclaimer in the 15f889d2efSBrooks Davis * documentation and/or other materials provided with the distribution. 16fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 17f889d2efSBrooks Davis * may be used to endorse or promote products derived from this software 18f889d2efSBrooks Davis * without specific prior written permission. 19f889d2efSBrooks Davis * 20f889d2efSBrooks Davis * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21f889d2efSBrooks Davis * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22f889d2efSBrooks Davis * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23f889d2efSBrooks Davis * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24f889d2efSBrooks Davis * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25f889d2efSBrooks Davis * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26f889d2efSBrooks Davis * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27f889d2efSBrooks Davis * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28f889d2efSBrooks Davis * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29f889d2efSBrooks Davis * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30f889d2efSBrooks Davis * SUCH DAMAGE. 31f889d2efSBrooks Davis * 32f889d2efSBrooks Davis * @(#)if.c 8.5 (Berkeley) 1/9/95 33f889d2efSBrooks Davis * $FreeBSD$ 34f889d2efSBrooks Davis */ 35f889d2efSBrooks Davis 36f889d2efSBrooks Davis #include <sys/param.h> 37c3322cb9SGleb Smirnoff #include <sys/eventhandler.h> 38f889d2efSBrooks Davis #include <sys/malloc.h> 39434dbbb3SRuslan Ermilov #include <sys/limits.h> 40f889d2efSBrooks Davis #include <sys/lock.h> 41f889d2efSBrooks Davis #include <sys/mutex.h> 42f889d2efSBrooks Davis #include <sys/kernel.h> 43f889d2efSBrooks Davis #include <sys/systm.h> 44f889d2efSBrooks Davis #include <sys/types.h> 45f889d2efSBrooks Davis #include <sys/socket.h> 46f889d2efSBrooks Davis 47f889d2efSBrooks Davis #include <net/if.h> 48f889d2efSBrooks Davis #include <net/if_var.h> 49*2c2b37adSJustin Hibbits #include <net/if_private.h> 5076039bc8SGleb Smirnoff #include <net/if_clone.h> 51f889d2efSBrooks Davis #include <net/radix.h> 52f889d2efSBrooks Davis #include <net/route.h> 5321ca7b57SMarko Zec #include <net/vnet.h> 54f889d2efSBrooks Davis 5542a58907SGleb Smirnoff /* Current IF_MAXUNIT expands maximum to 5 characters. */ 5642a58907SGleb Smirnoff #define IFCLOSIZ (IFNAMSIZ - 5) 5742a58907SGleb Smirnoff 5842a58907SGleb Smirnoff /* 5942a58907SGleb Smirnoff * Structure describing a `cloning' interface. 6042a58907SGleb Smirnoff * 6142a58907SGleb Smirnoff * List of locks 6242a58907SGleb Smirnoff * (c) const until freeing 6342a58907SGleb Smirnoff * (d) driver specific data, may need external protection. 6442a58907SGleb Smirnoff * (e) locked by if_cloners_mtx 6542a58907SGleb Smirnoff * (i) locked by ifc_mtx mtx 6642a58907SGleb Smirnoff */ 6742a58907SGleb Smirnoff struct if_clone { 6842a58907SGleb Smirnoff char ifc_name[IFCLOSIZ]; /* (c) Name of device, e.g. `gif' */ 6942a58907SGleb Smirnoff struct unrhdr *ifc_unrhdr; /* (c) alloc_unr(9) header */ 7042a58907SGleb Smirnoff int ifc_maxunit; /* (c) maximum unit number */ 7109f6ff4fSMatt Macy int ifc_flags; 7242a58907SGleb Smirnoff long ifc_refcnt; /* (i) Reference count. */ 7342a58907SGleb Smirnoff LIST_HEAD(, ifnet) ifc_iflist; /* (i) List of cloned interfaces */ 7442a58907SGleb Smirnoff struct mtx ifc_mtx; /* Mutex to protect members. */ 7542a58907SGleb Smirnoff 7609ee0fc0SAlexander V. Chernikov ifc_match_f *ifc_match; /* (c) Matcher function */ 7709ee0fc0SAlexander V. Chernikov ifc_create_f *ifc_create; /* (c) Creates new interface */ 7809ee0fc0SAlexander V. Chernikov ifc_destroy_f *ifc_destroy; /* (c) Destroys cloned interface */ 7942a58907SGleb Smirnoff 8009ee0fc0SAlexander V. Chernikov #ifdef CLONE_COMPAT_13 8142a58907SGleb Smirnoff /* (c) Driver specific cloning functions. Called with no locks held. */ 8242a58907SGleb Smirnoff union { 8342a58907SGleb Smirnoff struct { /* advanced cloner */ 8442a58907SGleb Smirnoff ifc_create_t *_ifc_create; 8542a58907SGleb Smirnoff ifc_destroy_t *_ifc_destroy; 8642a58907SGleb Smirnoff } A; 8742a58907SGleb Smirnoff struct { /* simple cloner */ 8842a58907SGleb Smirnoff ifcs_create_t *_ifcs_create; 8942a58907SGleb Smirnoff ifcs_destroy_t *_ifcs_destroy; 9042a58907SGleb Smirnoff int _ifcs_minifs; /* minimum ifs */ 9142a58907SGleb Smirnoff 9242a58907SGleb Smirnoff } S; 9342a58907SGleb Smirnoff } U; 9409ee0fc0SAlexander V. Chernikov #define ifca_create U.A._ifc_create 9509ee0fc0SAlexander V. Chernikov #define ifca_destroy U.A._ifc_destroy 9642a58907SGleb Smirnoff #define ifcs_create U.S._ifcs_create 9742a58907SGleb Smirnoff #define ifcs_destroy U.S._ifcs_destroy 9842a58907SGleb Smirnoff #define ifcs_minifs U.S._ifcs_minifs 9909ee0fc0SAlexander V. Chernikov #endif 10042a58907SGleb Smirnoff 10142a58907SGleb Smirnoff LIST_ENTRY(if_clone) ifc_list; /* (e) On list of cloners */ 10242a58907SGleb Smirnoff }; 10342a58907SGleb Smirnoff 10409ee0fc0SAlexander V. Chernikov 10509ee0fc0SAlexander V. Chernikov 106f889d2efSBrooks Davis static void if_clone_free(struct if_clone *ifc); 1076b7330e2SSam Leffler static int if_clone_createif(struct if_clone *ifc, char *name, size_t len, 10809ee0fc0SAlexander V. Chernikov struct ifc_data *ifd, struct ifnet **ifpp); 109f889d2efSBrooks Davis 11009ee0fc0SAlexander V. Chernikov static int ifc_simple_match(struct if_clone *ifc, const char *name); 11109ee0fc0SAlexander V. Chernikov static int ifc_handle_unit(struct if_clone *ifc, char *name, size_t len, int *punit); 11209ee0fc0SAlexander V. Chernikov 11309ee0fc0SAlexander V. Chernikov #ifdef CLONE_COMPAT_13 11409ee0fc0SAlexander V. Chernikov static int ifc_simple_create_wrapper(struct if_clone *ifc, char *name, size_t maxlen, 11509ee0fc0SAlexander V. Chernikov struct ifc_data *ifc_data, struct ifnet **ifpp); 11609ee0fc0SAlexander V. Chernikov static int ifc_advanced_create_wrapper(struct if_clone *ifc, char *name, size_t maxlen, 11709ee0fc0SAlexander V. Chernikov struct ifc_data *ifc_data, struct ifnet **ifpp); 11809ee0fc0SAlexander V. Chernikov #endif 11942a58907SGleb Smirnoff 120f889d2efSBrooks Davis static struct mtx if_cloners_mtx; 1214ea05db8SGleb Smirnoff MTX_SYSINIT(if_cloners_lock, &if_cloners_mtx, "if_cloners lock", MTX_DEF); 1225f901c92SAndrew Turner VNET_DEFINE_STATIC(int, if_cloners_count); 123eddfbb76SRobert Watson VNET_DEFINE(LIST_HEAD(, if_clone), if_cloners); 124eddfbb76SRobert Watson 1251e77c105SRobert Watson #define V_if_cloners_count VNET(if_cloners_count) 1261e77c105SRobert Watson #define V_if_cloners VNET(if_cloners) 127f889d2efSBrooks Davis 128f889d2efSBrooks Davis #define IF_CLONERS_LOCK_ASSERT() mtx_assert(&if_cloners_mtx, MA_OWNED) 129f889d2efSBrooks Davis #define IF_CLONERS_LOCK() mtx_lock(&if_cloners_mtx) 130f889d2efSBrooks Davis #define IF_CLONERS_UNLOCK() mtx_unlock(&if_cloners_mtx) 131f889d2efSBrooks Davis 132f889d2efSBrooks Davis #define IF_CLONE_LOCK_INIT(ifc) \ 133f889d2efSBrooks Davis mtx_init(&(ifc)->ifc_mtx, "if_clone lock", NULL, MTX_DEF) 134f889d2efSBrooks Davis #define IF_CLONE_LOCK_DESTROY(ifc) mtx_destroy(&(ifc)->ifc_mtx) 135f889d2efSBrooks Davis #define IF_CLONE_LOCK_ASSERT(ifc) mtx_assert(&(ifc)->ifc_mtx, MA_OWNED) 136f889d2efSBrooks Davis #define IF_CLONE_LOCK(ifc) mtx_lock(&(ifc)->ifc_mtx) 137f889d2efSBrooks Davis #define IF_CLONE_UNLOCK(ifc) mtx_unlock(&(ifc)->ifc_mtx) 138f889d2efSBrooks Davis 139f889d2efSBrooks Davis #define IF_CLONE_ADDREF(ifc) \ 140f889d2efSBrooks Davis do { \ 141f889d2efSBrooks Davis IF_CLONE_LOCK(ifc); \ 142f889d2efSBrooks Davis IF_CLONE_ADDREF_LOCKED(ifc); \ 143f889d2efSBrooks Davis IF_CLONE_UNLOCK(ifc); \ 144f889d2efSBrooks Davis } while (0) 145f889d2efSBrooks Davis #define IF_CLONE_ADDREF_LOCKED(ifc) \ 146f889d2efSBrooks Davis do { \ 147f889d2efSBrooks Davis IF_CLONE_LOCK_ASSERT(ifc); \ 148f889d2efSBrooks Davis KASSERT((ifc)->ifc_refcnt >= 0, \ 149f889d2efSBrooks Davis ("negative refcnt %ld", (ifc)->ifc_refcnt)); \ 150f889d2efSBrooks Davis (ifc)->ifc_refcnt++; \ 151f889d2efSBrooks Davis } while (0) 152f889d2efSBrooks Davis #define IF_CLONE_REMREF(ifc) \ 153f889d2efSBrooks Davis do { \ 154f889d2efSBrooks Davis IF_CLONE_LOCK(ifc); \ 155f889d2efSBrooks Davis IF_CLONE_REMREF_LOCKED(ifc); \ 156f889d2efSBrooks Davis } while (0) 157f889d2efSBrooks Davis #define IF_CLONE_REMREF_LOCKED(ifc) \ 158f889d2efSBrooks Davis do { \ 159f889d2efSBrooks Davis IF_CLONE_LOCK_ASSERT(ifc); \ 160f889d2efSBrooks Davis KASSERT((ifc)->ifc_refcnt > 0, \ 161f889d2efSBrooks Davis ("bogus refcnt %ld", (ifc)->ifc_refcnt)); \ 162f889d2efSBrooks Davis if (--(ifc)->ifc_refcnt == 0) { \ 163f889d2efSBrooks Davis IF_CLONE_UNLOCK(ifc); \ 164f889d2efSBrooks Davis if_clone_free(ifc); \ 165ca64c799SMax Laier } else { \ 166f889d2efSBrooks Davis /* silently free the lock */ \ 167f889d2efSBrooks Davis IF_CLONE_UNLOCK(ifc); \ 168ca64c799SMax Laier } \ 169f889d2efSBrooks Davis } while (0) 170f889d2efSBrooks Davis 1714e7e0183SAndrew Thompson #define IFC_IFLIST_INSERT(_ifc, _ifp) \ 1724e7e0183SAndrew Thompson LIST_INSERT_HEAD(&_ifc->ifc_iflist, _ifp, if_clones) 1734e7e0183SAndrew Thompson #define IFC_IFLIST_REMOVE(_ifc, _ifp) \ 1744e7e0183SAndrew Thompson LIST_REMOVE(_ifp, if_clones) 1754e7e0183SAndrew Thompson 176c711aea6SPoul-Henning Kamp static MALLOC_DEFINE(M_CLONE, "clone", "interface cloning framework"); 177f889d2efSBrooks Davis 178d0728d71SRobert Watson void 179d0728d71SRobert Watson vnet_if_clone_init(void) 18037f17770SMarko Zec { 18137f17770SMarko Zec 18237f17770SMarko Zec LIST_INIT(&V_if_cloners); 18337f17770SMarko Zec } 18437f17770SMarko Zec 185f889d2efSBrooks Davis /* 1864e7e0183SAndrew Thompson * Lookup and create a clone network interface. 187f889d2efSBrooks Davis */ 188f889d2efSBrooks Davis int 18909ee0fc0SAlexander V. Chernikov ifc_create_ifp(const char *name, struct ifc_data *ifd, 19009ee0fc0SAlexander V. Chernikov struct ifnet **ifpp) 191f889d2efSBrooks Davis { 192f889d2efSBrooks Davis struct if_clone *ifc; 19309ee0fc0SAlexander V. Chernikov char ifname[IFNAMSIZ]; 19409ee0fc0SAlexander V. Chernikov struct ifnet *ifp = NULL; 19509ee0fc0SAlexander V. Chernikov int error; 196f889d2efSBrooks Davis 197f889d2efSBrooks Davis /* Try to find an applicable cloner for this request */ 198f889d2efSBrooks Davis IF_CLONERS_LOCK(); 19909ee0fc0SAlexander V. Chernikov LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { 20042a58907SGleb Smirnoff if (ifc->ifc_match(ifc, name)) 20142a58907SGleb Smirnoff break; 202f889d2efSBrooks Davis } 203f889d2efSBrooks Davis IF_CLONERS_UNLOCK(); 204f889d2efSBrooks Davis 205f889d2efSBrooks Davis if (ifc == NULL) 206f889d2efSBrooks Davis return (EINVAL); 207f889d2efSBrooks Davis 20809ee0fc0SAlexander V. Chernikov strlcpy(ifname, name, IFNAMSIZ); 20909ee0fc0SAlexander V. Chernikov error = if_clone_createif(ifc, ifname, IFNAMSIZ, ifd, &ifp); 21009ee0fc0SAlexander V. Chernikov if (ifpp != NULL) 21109ee0fc0SAlexander V. Chernikov *ifpp = ifp; 21209ee0fc0SAlexander V. Chernikov 21309ee0fc0SAlexander V. Chernikov return (error); 21409ee0fc0SAlexander V. Chernikov } 21509ee0fc0SAlexander V. Chernikov 21609ee0fc0SAlexander V. Chernikov int 21709ee0fc0SAlexander V. Chernikov if_clone_create(char *name, size_t len, caddr_t params) 21809ee0fc0SAlexander V. Chernikov { 21909ee0fc0SAlexander V. Chernikov struct ifc_data ifd = { .params = params }; 22009ee0fc0SAlexander V. Chernikov struct ifnet *ifp; 22109ee0fc0SAlexander V. Chernikov 22209ee0fc0SAlexander V. Chernikov int error = ifc_create_ifp(name, &ifd, &ifp); 22309ee0fc0SAlexander V. Chernikov 22409ee0fc0SAlexander V. Chernikov if (error == 0) 22509ee0fc0SAlexander V. Chernikov strlcpy(name, if_name(ifp), len); 22609ee0fc0SAlexander V. Chernikov 22709ee0fc0SAlexander V. Chernikov return (error); 2284e7e0183SAndrew Thompson } 2294e7e0183SAndrew Thompson 230b02fd8b7SKristof Provost void 23126c190d2SAlexander V. Chernikov ifc_link_ifp(struct if_clone *ifc, struct ifnet *ifp) 232b02fd8b7SKristof Provost { 233b02fd8b7SKristof Provost 234b02fd8b7SKristof Provost if ((ifc->ifc_flags & IFC_NOGROUP) == 0) 235b02fd8b7SKristof Provost if_addgroup(ifp, ifc->ifc_name); 236b02fd8b7SKristof Provost 237b02fd8b7SKristof Provost IF_CLONE_LOCK(ifc); 238b02fd8b7SKristof Provost IFC_IFLIST_INSERT(ifc, ifp); 239b02fd8b7SKristof Provost IF_CLONE_UNLOCK(ifc); 240b02fd8b7SKristof Provost } 241b02fd8b7SKristof Provost 24226c190d2SAlexander V. Chernikov void 24326c190d2SAlexander V. Chernikov if_clone_addif(struct if_clone *ifc, struct ifnet *ifp) 24426c190d2SAlexander V. Chernikov { 24526c190d2SAlexander V. Chernikov ifc_link_ifp(ifc, ifp); 24626c190d2SAlexander V. Chernikov } 24726c190d2SAlexander V. Chernikov 24826c190d2SAlexander V. Chernikov bool 24926c190d2SAlexander V. Chernikov ifc_unlink_ifp(struct if_clone *ifc, struct ifnet *ifp) 25026c190d2SAlexander V. Chernikov { 25126c190d2SAlexander V. Chernikov struct ifnet *ifcifp; 25226c190d2SAlexander V. Chernikov 25326c190d2SAlexander V. Chernikov IF_CLONE_LOCK(ifc); 25426c190d2SAlexander V. Chernikov LIST_FOREACH(ifcifp, &ifc->ifc_iflist, if_clones) { 25526c190d2SAlexander V. Chernikov if (ifcifp == ifp) { 25626c190d2SAlexander V. Chernikov IFC_IFLIST_REMOVE(ifc, ifp); 25726c190d2SAlexander V. Chernikov break; 25826c190d2SAlexander V. Chernikov } 25926c190d2SAlexander V. Chernikov } 26026c190d2SAlexander V. Chernikov IF_CLONE_UNLOCK(ifc); 26126c190d2SAlexander V. Chernikov 26226c190d2SAlexander V. Chernikov if (ifcifp != NULL && (ifc->ifc_flags & IFC_F_NOGROUP) == 0) 26326c190d2SAlexander V. Chernikov if_delgroup(ifp, ifc->ifc_name); 26426c190d2SAlexander V. Chernikov 26526c190d2SAlexander V. Chernikov return (ifcifp != NULL); 26626c190d2SAlexander V. Chernikov } 26726c190d2SAlexander V. Chernikov 26826c190d2SAlexander V. Chernikov static struct if_clone * 26926c190d2SAlexander V. Chernikov ifc_find_cloner(const char *name, struct vnet *vnet) 27026c190d2SAlexander V. Chernikov { 27126c190d2SAlexander V. Chernikov struct if_clone *ifc; 27226c190d2SAlexander V. Chernikov 27326c190d2SAlexander V. Chernikov CURVNET_SET_QUIET(vnet); 27426c190d2SAlexander V. Chernikov IF_CLONERS_LOCK(); 27526c190d2SAlexander V. Chernikov LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { 27626c190d2SAlexander V. Chernikov if (strcmp(ifc->ifc_name, name) == 0) { 27726c190d2SAlexander V. Chernikov break; 27826c190d2SAlexander V. Chernikov } 27926c190d2SAlexander V. Chernikov } 28026c190d2SAlexander V. Chernikov IF_CLONERS_UNLOCK(); 28126c190d2SAlexander V. Chernikov CURVNET_RESTORE(); 28226c190d2SAlexander V. Chernikov 28326c190d2SAlexander V. Chernikov return (ifc); 28426c190d2SAlexander V. Chernikov } 28526c190d2SAlexander V. Chernikov 2864e7e0183SAndrew Thompson /* 2874e7e0183SAndrew Thompson * Create a clone network interface. 2884e7e0183SAndrew Thompson */ 2894e7e0183SAndrew Thompson static int 29009ee0fc0SAlexander V. Chernikov if_clone_createif(struct if_clone *ifc, char *name, size_t len, 29109ee0fc0SAlexander V. Chernikov struct ifc_data *ifd, struct ifnet **ifpp) 2924e7e0183SAndrew Thompson { 29309ee0fc0SAlexander V. Chernikov int err, unit = 0; 2944e7e0183SAndrew Thompson 2954e7e0183SAndrew Thompson if (ifunit(name) != NULL) 2964e7e0183SAndrew Thompson return (EEXIST); 2974e7e0183SAndrew Thompson 29809ee0fc0SAlexander V. Chernikov if (ifc->ifc_flags & IFC_F_AUTOUNIT) { 29909ee0fc0SAlexander V. Chernikov if ((err = ifc_handle_unit(ifc, name, len, &unit)) != 0) 30009ee0fc0SAlexander V. Chernikov return (err); 30109ee0fc0SAlexander V. Chernikov ifd->unit = unit; 3024e7e0183SAndrew Thompson } 30309ee0fc0SAlexander V. Chernikov *ifpp = NULL; 30409ee0fc0SAlexander V. Chernikov err = (*ifc->ifc_create)(ifc, name, len, ifd, ifpp); 30509ee0fc0SAlexander V. Chernikov 30609ee0fc0SAlexander V. Chernikov if (err == 0) { 30709ee0fc0SAlexander V. Chernikov MPASS(*ifpp != NULL); 30809ee0fc0SAlexander V. Chernikov if_clone_addif(ifc, *ifpp); 30909ee0fc0SAlexander V. Chernikov } else if (ifc->ifc_flags & IFC_F_AUTOUNIT) 31009ee0fc0SAlexander V. Chernikov ifc_free_unit(ifc, unit); 3114e7e0183SAndrew Thompson 312f889d2efSBrooks Davis return (err); 313f889d2efSBrooks Davis } 314f889d2efSBrooks Davis 315f889d2efSBrooks Davis /* 3164e7e0183SAndrew Thompson * Lookup and destroy a clone network interface. 317f889d2efSBrooks Davis */ 318f889d2efSBrooks Davis int 319f889d2efSBrooks Davis if_clone_destroy(const char *name) 320f889d2efSBrooks Davis { 321d0088cdeSBjoern A. Zeeb int err; 322f889d2efSBrooks Davis struct if_clone *ifc; 323f889d2efSBrooks Davis struct ifnet *ifp; 324f889d2efSBrooks Davis 325d0088cdeSBjoern A. Zeeb ifp = ifunit_ref(name); 326f889d2efSBrooks Davis if (ifp == NULL) 327f889d2efSBrooks Davis return (ENXIO); 328f889d2efSBrooks Davis 32926c190d2SAlexander V. Chernikov ifc = ifc_find_cloner(ifp->if_dname, ifp->if_home_vnet); 330d0088cdeSBjoern A. Zeeb if (ifc == NULL) { 331d0088cdeSBjoern A. Zeeb if_rele(ifp); 332f889d2efSBrooks Davis return (EINVAL); 333d0088cdeSBjoern A. Zeeb } 334f889d2efSBrooks Davis 335d0088cdeSBjoern A. Zeeb err = if_clone_destroyif(ifc, ifp); 336d0088cdeSBjoern A. Zeeb if_rele(ifp); 337d0088cdeSBjoern A. Zeeb return err; 3384e7e0183SAndrew Thompson } 3394e7e0183SAndrew Thompson 3404e7e0183SAndrew Thompson /* 3414e7e0183SAndrew Thompson * Destroy a clone network interface. 3424e7e0183SAndrew Thompson */ 34309ee0fc0SAlexander V. Chernikov static int 34409ee0fc0SAlexander V. Chernikov if_clone_destroyif_flags(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags) 3454e7e0183SAndrew Thompson { 3464e7e0183SAndrew Thompson int err; 3474e7e0183SAndrew Thompson 34837f17770SMarko Zec /* 34937f17770SMarko Zec * Given that the cloned ifnet might be attached to a different 35037f17770SMarko Zec * vnet from where its cloner was registered, we have to 35137f17770SMarko Zec * switch to the vnet context of the target vnet. 35237f17770SMarko Zec */ 35337f17770SMarko Zec CURVNET_SET_QUIET(ifp->if_vnet); 35437f17770SMarko Zec 35526c190d2SAlexander V. Chernikov if (!ifc_unlink_ifp(ifc, ifp)) { 356c769e1beSBjoern A. Zeeb CURVNET_RESTORE(); 357c769e1beSBjoern A. Zeeb return (ENXIO); /* ifp is not on the list. */ 358c769e1beSBjoern A. Zeeb } 3590dad3f0eSMax Laier 36009ee0fc0SAlexander V. Chernikov int unit = ifp->if_dunit; 36109ee0fc0SAlexander V. Chernikov err = (*ifc->ifc_destroy)(ifc, ifp, flags); 362f889d2efSBrooks Davis 36326c190d2SAlexander V. Chernikov if (err != 0) 36426c190d2SAlexander V. Chernikov ifc_link_ifp(ifc, ifp); 36526c190d2SAlexander V. Chernikov else if (ifc->ifc_flags & IFC_F_AUTOUNIT) 36609ee0fc0SAlexander V. Chernikov ifc_free_unit(ifc, unit); 36721ca7b57SMarko Zec CURVNET_RESTORE(); 368f889d2efSBrooks Davis return (err); 369f889d2efSBrooks Davis } 370f889d2efSBrooks Davis 37109ee0fc0SAlexander V. Chernikov int 37209ee0fc0SAlexander V. Chernikov if_clone_destroyif(struct if_clone *ifc, struct ifnet *ifp) 37309ee0fc0SAlexander V. Chernikov { 37409ee0fc0SAlexander V. Chernikov return (if_clone_destroyif_flags(ifc, ifp, 0)); 37509ee0fc0SAlexander V. Chernikov } 37609ee0fc0SAlexander V. Chernikov 37742a58907SGleb Smirnoff static struct if_clone * 37842a58907SGleb Smirnoff if_clone_alloc(const char *name, int maxunit) 37942a58907SGleb Smirnoff { 38042a58907SGleb Smirnoff struct if_clone *ifc; 38142a58907SGleb Smirnoff 38242a58907SGleb Smirnoff KASSERT(name != NULL, ("%s: no name\n", __func__)); 38342a58907SGleb Smirnoff 38442a58907SGleb Smirnoff ifc = malloc(sizeof(struct if_clone), M_CLONE, M_WAITOK | M_ZERO); 38542a58907SGleb Smirnoff strncpy(ifc->ifc_name, name, IFCLOSIZ-1); 38642a58907SGleb Smirnoff IF_CLONE_LOCK_INIT(ifc); 38742a58907SGleb Smirnoff IF_CLONE_ADDREF(ifc); 38842a58907SGleb Smirnoff ifc->ifc_maxunit = maxunit ? maxunit : IF_MAXUNIT; 38942a58907SGleb Smirnoff ifc->ifc_unrhdr = new_unrhdr(0, ifc->ifc_maxunit, &ifc->ifc_mtx); 39042a58907SGleb Smirnoff LIST_INIT(&ifc->ifc_iflist); 39142a58907SGleb Smirnoff 39242a58907SGleb Smirnoff return (ifc); 39342a58907SGleb Smirnoff } 39442a58907SGleb Smirnoff 39542a58907SGleb Smirnoff static int 396f889d2efSBrooks Davis if_clone_attach(struct if_clone *ifc) 397f889d2efSBrooks Davis { 3982e9fff5bSGleb Smirnoff struct if_clone *ifc1; 399f889d2efSBrooks Davis 400f889d2efSBrooks Davis IF_CLONERS_LOCK(); 4012e9fff5bSGleb Smirnoff LIST_FOREACH(ifc1, &V_if_cloners, ifc_list) 4022e9fff5bSGleb Smirnoff if (strcmp(ifc->ifc_name, ifc1->ifc_name) == 0) { 4032e9fff5bSGleb Smirnoff IF_CLONERS_UNLOCK(); 4042e9fff5bSGleb Smirnoff IF_CLONE_REMREF(ifc); 4052e9fff5bSGleb Smirnoff return (EEXIST); 4062e9fff5bSGleb Smirnoff } 40737f17770SMarko Zec LIST_INSERT_HEAD(&V_if_cloners, ifc, ifc_list); 40837f17770SMarko Zec V_if_cloners_count++; 409f889d2efSBrooks Davis IF_CLONERS_UNLOCK(); 410f889d2efSBrooks Davis 41142a58907SGleb Smirnoff return (0); 41242a58907SGleb Smirnoff } 41342a58907SGleb Smirnoff 41442a58907SGleb Smirnoff struct if_clone * 41509ee0fc0SAlexander V. Chernikov ifc_attach_cloner(const char *name, struct if_clone_addreq *req) 41642a58907SGleb Smirnoff { 41709ee0fc0SAlexander V. Chernikov if (req->create_f == NULL || req->destroy_f == NULL) 41809ee0fc0SAlexander V. Chernikov return (NULL); 41909ee0fc0SAlexander V. Chernikov if (strnlen(name, IFCLOSIZ) >= (IFCLOSIZ - 1)) 42009ee0fc0SAlexander V. Chernikov return (NULL); 42142a58907SGleb Smirnoff 42209ee0fc0SAlexander V. Chernikov struct if_clone *ifc = if_clone_alloc(name, req->maxunit); 42309ee0fc0SAlexander V. Chernikov ifc->ifc_match = req->match_f != NULL ? req->match_f : ifc_simple_match; 42409ee0fc0SAlexander V. Chernikov ifc->ifc_create = req->create_f; 42509ee0fc0SAlexander V. Chernikov ifc->ifc_destroy = req->destroy_f; 42609ee0fc0SAlexander V. Chernikov ifc->ifc_flags = (req->flags & (IFC_F_AUTOUNIT | IFC_F_NOGROUP)); 42742a58907SGleb Smirnoff 4283395dd6eSAlexander Kabaev if (if_clone_attach(ifc) != 0) 42942a58907SGleb Smirnoff return (NULL); 43042a58907SGleb Smirnoff 431f889d2efSBrooks Davis EVENTHANDLER_INVOKE(if_clone_event, ifc); 4322e9fff5bSGleb Smirnoff 43342a58907SGleb Smirnoff return (ifc); 43442a58907SGleb Smirnoff } 43542a58907SGleb Smirnoff 43609ee0fc0SAlexander V. Chernikov void 43709ee0fc0SAlexander V. Chernikov ifc_detach_cloner(struct if_clone *ifc) 43809ee0fc0SAlexander V. Chernikov { 43909ee0fc0SAlexander V. Chernikov if_clone_detach(ifc); 44009ee0fc0SAlexander V. Chernikov } 44109ee0fc0SAlexander V. Chernikov 44209ee0fc0SAlexander V. Chernikov 44309ee0fc0SAlexander V. Chernikov #ifdef CLONE_COMPAT_13 44409ee0fc0SAlexander V. Chernikov 44509ee0fc0SAlexander V. Chernikov static int 44609ee0fc0SAlexander V. Chernikov ifc_advanced_create_wrapper(struct if_clone *ifc, char *name, size_t maxlen, 44709ee0fc0SAlexander V. Chernikov struct ifc_data *ifc_data, struct ifnet **ifpp) 44809ee0fc0SAlexander V. Chernikov { 44909ee0fc0SAlexander V. Chernikov int error = ifc->ifca_create(ifc, name, maxlen, ifc_data->params); 45009ee0fc0SAlexander V. Chernikov 45109ee0fc0SAlexander V. Chernikov if (error == 0) 45209ee0fc0SAlexander V. Chernikov *ifpp = ifunit(name); 45309ee0fc0SAlexander V. Chernikov return (error); 45409ee0fc0SAlexander V. Chernikov } 45509ee0fc0SAlexander V. Chernikov 45609ee0fc0SAlexander V. Chernikov static int 45709ee0fc0SAlexander V. Chernikov ifc_advanced_destroy_wrapper(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags) 45809ee0fc0SAlexander V. Chernikov { 45909ee0fc0SAlexander V. Chernikov if (ifc->ifca_destroy == NULL) 46009ee0fc0SAlexander V. Chernikov return (ENOTSUP); 46109ee0fc0SAlexander V. Chernikov return (ifc->ifca_destroy(ifc, ifp)); 46209ee0fc0SAlexander V. Chernikov } 46309ee0fc0SAlexander V. Chernikov 46409ee0fc0SAlexander V. Chernikov struct if_clone * 46509ee0fc0SAlexander V. Chernikov if_clone_advanced(const char *name, u_int maxunit, ifc_match_t match, 46609ee0fc0SAlexander V. Chernikov ifc_create_t create, ifc_destroy_t destroy) 46709ee0fc0SAlexander V. Chernikov { 46809ee0fc0SAlexander V. Chernikov struct if_clone *ifc; 46909ee0fc0SAlexander V. Chernikov 47009ee0fc0SAlexander V. Chernikov ifc = if_clone_alloc(name, maxunit); 47109ee0fc0SAlexander V. Chernikov ifc->ifc_match = match; 47209ee0fc0SAlexander V. Chernikov ifc->ifc_create = ifc_advanced_create_wrapper; 47309ee0fc0SAlexander V. Chernikov ifc->ifc_destroy = ifc_advanced_destroy_wrapper; 47409ee0fc0SAlexander V. Chernikov ifc->ifca_destroy = destroy; 47509ee0fc0SAlexander V. Chernikov ifc->ifca_create = create; 47609ee0fc0SAlexander V. Chernikov 47709ee0fc0SAlexander V. Chernikov if (if_clone_attach(ifc) != 0) 47809ee0fc0SAlexander V. Chernikov return (NULL); 47909ee0fc0SAlexander V. Chernikov 48009ee0fc0SAlexander V. Chernikov EVENTHANDLER_INVOKE(if_clone_event, ifc); 48109ee0fc0SAlexander V. Chernikov 48209ee0fc0SAlexander V. Chernikov return (ifc); 48309ee0fc0SAlexander V. Chernikov } 48409ee0fc0SAlexander V. Chernikov 48509ee0fc0SAlexander V. Chernikov static int 48609ee0fc0SAlexander V. Chernikov ifc_simple_create_wrapper(struct if_clone *ifc, char *name, size_t maxlen, 48709ee0fc0SAlexander V. Chernikov struct ifc_data *ifc_data, struct ifnet **ifpp) 48809ee0fc0SAlexander V. Chernikov { 48909ee0fc0SAlexander V. Chernikov int unit = 0; 49009ee0fc0SAlexander V. Chernikov 49109ee0fc0SAlexander V. Chernikov ifc_name2unit(name, &unit); 49209ee0fc0SAlexander V. Chernikov int error = ifc->ifcs_create(ifc, unit, ifc_data->params); 49309ee0fc0SAlexander V. Chernikov if (error == 0) 49409ee0fc0SAlexander V. Chernikov *ifpp = ifunit(name); 49509ee0fc0SAlexander V. Chernikov return (error); 49609ee0fc0SAlexander V. Chernikov } 49709ee0fc0SAlexander V. Chernikov 49809ee0fc0SAlexander V. Chernikov static int 49909ee0fc0SAlexander V. Chernikov ifc_simple_destroy_wrapper(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags) 50009ee0fc0SAlexander V. Chernikov { 50109ee0fc0SAlexander V. Chernikov if (ifp->if_dunit < ifc->ifcs_minifs && (flags & IFC_F_FORCE) == 0) 50209ee0fc0SAlexander V. Chernikov return (EINVAL); 50309ee0fc0SAlexander V. Chernikov 50409ee0fc0SAlexander V. Chernikov ifc->ifcs_destroy(ifp); 50509ee0fc0SAlexander V. Chernikov return (0); 50609ee0fc0SAlexander V. Chernikov } 50709ee0fc0SAlexander V. Chernikov 50842a58907SGleb Smirnoff struct if_clone * 50942a58907SGleb Smirnoff if_clone_simple(const char *name, ifcs_create_t create, ifcs_destroy_t destroy, 51042a58907SGleb Smirnoff u_int minifs) 51142a58907SGleb Smirnoff { 51242a58907SGleb Smirnoff struct if_clone *ifc; 51342a58907SGleb Smirnoff u_int unit; 51442a58907SGleb Smirnoff 51542a58907SGleb Smirnoff ifc = if_clone_alloc(name, 0); 51609ee0fc0SAlexander V. Chernikov ifc->ifc_match = ifc_simple_match; 51709ee0fc0SAlexander V. Chernikov ifc->ifc_create = ifc_simple_create_wrapper; 51809ee0fc0SAlexander V. Chernikov ifc->ifc_destroy = ifc_simple_destroy_wrapper; 51942a58907SGleb Smirnoff ifc->ifcs_create = create; 52042a58907SGleb Smirnoff ifc->ifcs_destroy = destroy; 52142a58907SGleb Smirnoff ifc->ifcs_minifs = minifs; 52209ee0fc0SAlexander V. Chernikov ifc->ifc_flags = IFC_F_AUTOUNIT; 52342a58907SGleb Smirnoff 5243395dd6eSAlexander Kabaev if (if_clone_attach(ifc) != 0) 52542a58907SGleb Smirnoff return (NULL); 52642a58907SGleb Smirnoff 52742a58907SGleb Smirnoff for (unit = 0; unit < minifs; unit++) { 52842a58907SGleb Smirnoff char name[IFNAMSIZ]; 52946d0f824SMatt Macy int error __unused; 53009ee0fc0SAlexander V. Chernikov struct ifc_data ifd = {}; 53109ee0fc0SAlexander V. Chernikov struct ifnet *ifp; 53242a58907SGleb Smirnoff 53342a58907SGleb Smirnoff snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, unit); 53409ee0fc0SAlexander V. Chernikov error = if_clone_createif(ifc, name, IFNAMSIZ, &ifd, &ifp); 53542a58907SGleb Smirnoff KASSERT(error == 0, 53642a58907SGleb Smirnoff ("%s: failed to create required interface %s", 53742a58907SGleb Smirnoff __func__, name)); 53842a58907SGleb Smirnoff } 53942a58907SGleb Smirnoff 54042a58907SGleb Smirnoff EVENTHANDLER_INVOKE(if_clone_event, ifc); 54142a58907SGleb Smirnoff 54242a58907SGleb Smirnoff return (ifc); 543f889d2efSBrooks Davis } 54409ee0fc0SAlexander V. Chernikov #endif 545f889d2efSBrooks Davis 546f889d2efSBrooks Davis /* 547f889d2efSBrooks Davis * Unregister a network interface cloner. 548f889d2efSBrooks Davis */ 549f889d2efSBrooks Davis void 550f889d2efSBrooks Davis if_clone_detach(struct if_clone *ifc) 551f889d2efSBrooks Davis { 552f889d2efSBrooks Davis 553f889d2efSBrooks Davis IF_CLONERS_LOCK(); 554f889d2efSBrooks Davis LIST_REMOVE(ifc, ifc_list); 55537f17770SMarko Zec V_if_cloners_count--; 556f889d2efSBrooks Davis IF_CLONERS_UNLOCK(); 557f889d2efSBrooks Davis 5584e7e0183SAndrew Thompson /* destroy all interfaces for this cloner */ 5594e7e0183SAndrew Thompson while (!LIST_EMPTY(&ifc->ifc_iflist)) 56009ee0fc0SAlexander V. Chernikov if_clone_destroyif_flags(ifc, LIST_FIRST(&ifc->ifc_iflist), IFC_F_FORCE); 5614e7e0183SAndrew Thompson 562f889d2efSBrooks Davis IF_CLONE_REMREF(ifc); 563f889d2efSBrooks Davis } 564f889d2efSBrooks Davis 565f889d2efSBrooks Davis static void 566f889d2efSBrooks Davis if_clone_free(struct if_clone *ifc) 567f889d2efSBrooks Davis { 568f889d2efSBrooks Davis 5694e7e0183SAndrew Thompson KASSERT(LIST_EMPTY(&ifc->ifc_iflist), 5704e7e0183SAndrew Thompson ("%s: ifc_iflist not empty", __func__)); 5714e7e0183SAndrew Thompson 572f889d2efSBrooks Davis IF_CLONE_LOCK_DESTROY(ifc); 5732e9fff5bSGleb Smirnoff delete_unrhdr(ifc->ifc_unrhdr); 57442a58907SGleb Smirnoff free(ifc, M_CLONE); 575f889d2efSBrooks Davis } 576f889d2efSBrooks Davis 577f889d2efSBrooks Davis /* 578f889d2efSBrooks Davis * Provide list of interface cloners to userspace. 579f889d2efSBrooks Davis */ 580f889d2efSBrooks Davis int 581f889d2efSBrooks Davis if_clone_list(struct if_clonereq *ifcr) 582f889d2efSBrooks Davis { 583c859ef97SBrooks Davis char *buf, *dst, *outbuf = NULL; 584f889d2efSBrooks Davis struct if_clone *ifc; 585c859ef97SBrooks Davis int buf_count, count, err = 0; 586c859ef97SBrooks Davis 587a6d00835SMaxim Konovalov if (ifcr->ifcr_count < 0) 588a6d00835SMaxim Konovalov return (EINVAL); 589a6d00835SMaxim Konovalov 590c859ef97SBrooks Davis IF_CLONERS_LOCK(); 591c859ef97SBrooks Davis /* 592c859ef97SBrooks Davis * Set our internal output buffer size. We could end up not 593c859ef97SBrooks Davis * reporting a cloner that is added between the unlock and lock 594c859ef97SBrooks Davis * below, but that's not a major problem. Not caping our 595c859ef97SBrooks Davis * allocation to the number of cloners actually in the system 596c859ef97SBrooks Davis * could be because that would let arbitrary users cause us to 597a4641f4eSPedro F. Giffuni * allocate arbitrary amounts of kernel memory. 598c859ef97SBrooks Davis */ 59937f17770SMarko Zec buf_count = (V_if_cloners_count < ifcr->ifcr_count) ? 60037f17770SMarko Zec V_if_cloners_count : ifcr->ifcr_count; 601c859ef97SBrooks Davis IF_CLONERS_UNLOCK(); 602c859ef97SBrooks Davis 603c859ef97SBrooks Davis outbuf = malloc(IFNAMSIZ*buf_count, M_CLONE, M_WAITOK | M_ZERO); 604f889d2efSBrooks Davis 605f889d2efSBrooks Davis IF_CLONERS_LOCK(); 606f889d2efSBrooks Davis 60737f17770SMarko Zec ifcr->ifcr_total = V_if_cloners_count; 608f889d2efSBrooks Davis if ((dst = ifcr->ifcr_buffer) == NULL) { 609f889d2efSBrooks Davis /* Just asking how many there are. */ 610f889d2efSBrooks Davis goto done; 611f889d2efSBrooks Davis } 61237f17770SMarko Zec count = (V_if_cloners_count < buf_count) ? 61337f17770SMarko Zec V_if_cloners_count : buf_count; 614f889d2efSBrooks Davis 61537f17770SMarko Zec for (ifc = LIST_FIRST(&V_if_cloners), buf = outbuf; 616c859ef97SBrooks Davis ifc != NULL && count != 0; 617c859ef97SBrooks Davis ifc = LIST_NEXT(ifc, ifc_list), count--, buf += IFNAMSIZ) { 618c859ef97SBrooks Davis strlcpy(buf, ifc->ifc_name, IFNAMSIZ); 619f889d2efSBrooks Davis } 620f889d2efSBrooks Davis 621f889d2efSBrooks Davis done: 622f889d2efSBrooks Davis IF_CLONERS_UNLOCK(); 62392f19df4SAlexander Kabaev if (err == 0 && dst != NULL) 624c859ef97SBrooks Davis err = copyout(outbuf, dst, buf_count*IFNAMSIZ); 625c859ef97SBrooks Davis if (outbuf != NULL) 626c859ef97SBrooks Davis free(outbuf, M_CLONE); 627f889d2efSBrooks Davis return (err); 628f889d2efSBrooks Davis } 629f889d2efSBrooks Davis 63054712fc4SGleb Smirnoff #ifdef VIMAGE 631f889d2efSBrooks Davis /* 63254712fc4SGleb Smirnoff * if_clone_restoregroup() is used in context of if_vmove(). 63354712fc4SGleb Smirnoff * 63454712fc4SGleb Smirnoff * Since if_detach_internal() has removed the interface from ALL groups, we 63554712fc4SGleb Smirnoff * need to "restore" interface membership in the cloner's group. Note that 63654712fc4SGleb Smirnoff * interface belongs to cloner in its home vnet, so we first find the original 63754712fc4SGleb Smirnoff * cloner, and then we confirm that cloner with the same name exists in the 63854712fc4SGleb Smirnoff * current vnet. 639c92a456bSHiroki Sato */ 64054712fc4SGleb Smirnoff void 64154712fc4SGleb Smirnoff if_clone_restoregroup(struct ifnet *ifp) 642c92a456bSHiroki Sato { 64354712fc4SGleb Smirnoff struct if_clone *ifc; 644c92a456bSHiroki Sato struct ifnet *ifcifp; 64554712fc4SGleb Smirnoff char ifc_name[IFCLOSIZ] = { [0] = '\0' }; 646c92a456bSHiroki Sato 64754712fc4SGleb Smirnoff CURVNET_SET_QUIET(ifp->if_home_vnet); 648c92a456bSHiroki Sato IF_CLONERS_LOCK(); 649c92a456bSHiroki Sato LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { 650c92a456bSHiroki Sato IF_CLONE_LOCK(ifc); 651c92a456bSHiroki Sato LIST_FOREACH(ifcifp, &ifc->ifc_iflist, if_clones) { 652c92a456bSHiroki Sato if (ifp == ifcifp) { 65354712fc4SGleb Smirnoff strncpy(ifc_name, ifc->ifc_name, IFCLOSIZ-1); 654c92a456bSHiroki Sato break; 655c92a456bSHiroki Sato } 656c92a456bSHiroki Sato } 657c92a456bSHiroki Sato IF_CLONE_UNLOCK(ifc); 65854712fc4SGleb Smirnoff if (ifc_name[0] != '\0') 659c92a456bSHiroki Sato break; 660c92a456bSHiroki Sato } 66154712fc4SGleb Smirnoff CURVNET_RESTORE(); 66254712fc4SGleb Smirnoff LIST_FOREACH(ifc, &V_if_cloners, ifc_list) 66354712fc4SGleb Smirnoff if (strcmp(ifc->ifc_name, ifc_name) == 0 && 66454712fc4SGleb Smirnoff ((ifc->ifc_flags & IFC_NOGROUP) == 0)) 66554712fc4SGleb Smirnoff break; 666c92a456bSHiroki Sato IF_CLONERS_UNLOCK(); 667c92a456bSHiroki Sato 66854712fc4SGleb Smirnoff if (ifc != NULL) 66954712fc4SGleb Smirnoff if_addgroup(ifp, ifc_name); 670c92a456bSHiroki Sato } 67154712fc4SGleb Smirnoff #endif 672c92a456bSHiroki Sato 673c92a456bSHiroki Sato /* 674f889d2efSBrooks Davis * A utility function to extract unit numbers from interface names of 67553729367SAlexander V. Chernikov * the form name###. 676f889d2efSBrooks Davis * 677f889d2efSBrooks Davis * Returns 0 on success and an error on failure. 678f889d2efSBrooks Davis */ 679f889d2efSBrooks Davis int 680f889d2efSBrooks Davis ifc_name2unit(const char *name, int *unit) 681f889d2efSBrooks Davis { 682f889d2efSBrooks Davis const char *cp; 683434dbbb3SRuslan Ermilov int cutoff = INT_MAX / 10; 684434dbbb3SRuslan Ermilov int cutlim = INT_MAX % 10; 685f889d2efSBrooks Davis 68653729367SAlexander V. Chernikov for (cp = name; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++) 68753729367SAlexander V. Chernikov ; 688f889d2efSBrooks Davis if (*cp == '\0') { 689f889d2efSBrooks Davis *unit = -1; 690434dbbb3SRuslan Ermilov } else if (cp[0] == '0' && cp[1] != '\0') { 691434dbbb3SRuslan Ermilov /* Disallow leading zeroes. */ 692434dbbb3SRuslan Ermilov return (EINVAL); 693f889d2efSBrooks Davis } else { 694f889d2efSBrooks Davis for (*unit = 0; *cp != '\0'; cp++) { 695f889d2efSBrooks Davis if (*cp < '0' || *cp > '9') { 696f889d2efSBrooks Davis /* Bogus unit number. */ 697f889d2efSBrooks Davis return (EINVAL); 698f889d2efSBrooks Davis } 699434dbbb3SRuslan Ermilov if (*unit > cutoff || 700434dbbb3SRuslan Ermilov (*unit == cutoff && *cp - '0' > cutlim)) 701434dbbb3SRuslan Ermilov return (EINVAL); 702f889d2efSBrooks Davis *unit = (*unit * 10) + (*cp - '0'); 703f889d2efSBrooks Davis } 704f889d2efSBrooks Davis } 705f889d2efSBrooks Davis 706f889d2efSBrooks Davis return (0); 707f889d2efSBrooks Davis } 708f889d2efSBrooks Davis 709c64c1f95SAndriy Voskoboinyk static int 710c64c1f95SAndriy Voskoboinyk ifc_alloc_unit_specific(struct if_clone *ifc, int *unit) 711f889d2efSBrooks Davis { 7122e9fff5bSGleb Smirnoff char name[IFNAMSIZ]; 713f889d2efSBrooks Davis 7143932d760SGleb Smirnoff if (*unit > ifc->ifc_maxunit) 7153932d760SGleb Smirnoff return (ENOSPC); 716c64c1f95SAndriy Voskoboinyk 717c64c1f95SAndriy Voskoboinyk if (alloc_unr_specific(ifc->ifc_unrhdr, *unit) == -1) 7182e9fff5bSGleb Smirnoff return (EEXIST); 719f889d2efSBrooks Davis 7202e9fff5bSGleb Smirnoff snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, *unit); 7212e9fff5bSGleb Smirnoff if (ifunit(name) != NULL) { 7223932d760SGleb Smirnoff free_unr(ifc->ifc_unrhdr, *unit); 7232e9fff5bSGleb Smirnoff return (EEXIST); 724f889d2efSBrooks Davis } 725f889d2efSBrooks Davis 7262e9fff5bSGleb Smirnoff IF_CLONE_ADDREF(ifc); 727f889d2efSBrooks Davis 7282e9fff5bSGleb Smirnoff return (0); 729f889d2efSBrooks Davis } 730f889d2efSBrooks Davis 731c64c1f95SAndriy Voskoboinyk static int 732c64c1f95SAndriy Voskoboinyk ifc_alloc_unit_next(struct if_clone *ifc, int *unit) 733c64c1f95SAndriy Voskoboinyk { 734c64c1f95SAndriy Voskoboinyk int error; 735c64c1f95SAndriy Voskoboinyk 736c64c1f95SAndriy Voskoboinyk *unit = alloc_unr(ifc->ifc_unrhdr); 737c64c1f95SAndriy Voskoboinyk if (*unit == -1) 738c64c1f95SAndriy Voskoboinyk return (ENOSPC); 739c64c1f95SAndriy Voskoboinyk 740c64c1f95SAndriy Voskoboinyk free_unr(ifc->ifc_unrhdr, *unit); 741c64c1f95SAndriy Voskoboinyk for (;;) { 742c64c1f95SAndriy Voskoboinyk error = ifc_alloc_unit_specific(ifc, unit); 743c64c1f95SAndriy Voskoboinyk if (error != EEXIST) 744c64c1f95SAndriy Voskoboinyk break; 745c64c1f95SAndriy Voskoboinyk 746c64c1f95SAndriy Voskoboinyk (*unit)++; 747c64c1f95SAndriy Voskoboinyk } 748c64c1f95SAndriy Voskoboinyk 749c64c1f95SAndriy Voskoboinyk return (error); 750c64c1f95SAndriy Voskoboinyk } 751c64c1f95SAndriy Voskoboinyk 752c64c1f95SAndriy Voskoboinyk int 753c64c1f95SAndriy Voskoboinyk ifc_alloc_unit(struct if_clone *ifc, int *unit) 754c64c1f95SAndriy Voskoboinyk { 755c64c1f95SAndriy Voskoboinyk if (*unit < 0) 756c64c1f95SAndriy Voskoboinyk return (ifc_alloc_unit_next(ifc, unit)); 757c64c1f95SAndriy Voskoboinyk else 758c64c1f95SAndriy Voskoboinyk return (ifc_alloc_unit_specific(ifc, unit)); 759c64c1f95SAndriy Voskoboinyk } 760c64c1f95SAndriy Voskoboinyk 761f889d2efSBrooks Davis void 762f889d2efSBrooks Davis ifc_free_unit(struct if_clone *ifc, int unit) 763f889d2efSBrooks Davis { 764f889d2efSBrooks Davis 7652e9fff5bSGleb Smirnoff free_unr(ifc->ifc_unrhdr, unit); 7662e9fff5bSGleb Smirnoff IF_CLONE_REMREF(ifc); 767f889d2efSBrooks Davis } 768f889d2efSBrooks Davis 76942a58907SGleb Smirnoff static int 770f889d2efSBrooks Davis ifc_simple_match(struct if_clone *ifc, const char *name) 771f889d2efSBrooks Davis { 772f889d2efSBrooks Davis const char *cp; 773f889d2efSBrooks Davis int i; 774f889d2efSBrooks Davis 775f889d2efSBrooks Davis /* Match the name */ 776f889d2efSBrooks Davis for (cp = name, i = 0; i < strlen(ifc->ifc_name); i++, cp++) { 777f889d2efSBrooks Davis if (ifc->ifc_name[i] != *cp) 778f889d2efSBrooks Davis return (0); 779f889d2efSBrooks Davis } 780f889d2efSBrooks Davis 781f889d2efSBrooks Davis /* Make sure there's a unit number or nothing after the name */ 782f889d2efSBrooks Davis for (; *cp != '\0'; cp++) { 783f889d2efSBrooks Davis if (*cp < '0' || *cp > '9') 784f889d2efSBrooks Davis return (0); 785f889d2efSBrooks Davis } 786f889d2efSBrooks Davis 787f889d2efSBrooks Davis return (1); 788f889d2efSBrooks Davis } 789f889d2efSBrooks Davis 79042a58907SGleb Smirnoff static int 79109ee0fc0SAlexander V. Chernikov ifc_handle_unit(struct if_clone *ifc, char *name, size_t len, int *punit) 792f889d2efSBrooks Davis { 793f889d2efSBrooks Davis char *dp; 794f889d2efSBrooks Davis int wildcard; 795f889d2efSBrooks Davis int unit; 796f889d2efSBrooks Davis int err; 797f889d2efSBrooks Davis 798f889d2efSBrooks Davis err = ifc_name2unit(name, &unit); 799f889d2efSBrooks Davis if (err != 0) 800f889d2efSBrooks Davis return (err); 801f889d2efSBrooks Davis 802f889d2efSBrooks Davis wildcard = (unit < 0); 803f889d2efSBrooks Davis 804f889d2efSBrooks Davis err = ifc_alloc_unit(ifc, &unit); 805f889d2efSBrooks Davis if (err != 0) 806f889d2efSBrooks Davis return (err); 807f889d2efSBrooks Davis 808f889d2efSBrooks Davis /* In the wildcard case, we need to update the name. */ 809f889d2efSBrooks Davis if (wildcard) { 810f889d2efSBrooks Davis for (dp = name; *dp != '\0'; dp++); 811f889d2efSBrooks Davis if (snprintf(dp, len - (dp-name), "%d", unit) > 812f889d2efSBrooks Davis len - (dp-name) - 1) { 813f889d2efSBrooks Davis /* 814f889d2efSBrooks Davis * This can only be a programmer error and 815f889d2efSBrooks Davis * there's no straightforward way to recover if 816f889d2efSBrooks Davis * it happens. 817f889d2efSBrooks Davis */ 818f889d2efSBrooks Davis panic("if_clone_create(): interface name too long"); 819f889d2efSBrooks Davis } 820f889d2efSBrooks Davis } 82109ee0fc0SAlexander V. Chernikov *punit = unit; 822f889d2efSBrooks Davis 823f889d2efSBrooks Davis return (0); 824f889d2efSBrooks Davis } 825f889d2efSBrooks Davis 82609ee0fc0SAlexander V. Chernikov int 82709ee0fc0SAlexander V. Chernikov ifc_copyin(const struct ifc_data *ifd, void *target, size_t len) 828f889d2efSBrooks Davis { 82909ee0fc0SAlexander V. Chernikov if (ifd->params == NULL) 830f889d2efSBrooks Davis return (EINVAL); 831f889d2efSBrooks Davis 83209ee0fc0SAlexander V. Chernikov if (ifd->flags & IFC_F_SYSSPACE) { 83309ee0fc0SAlexander V. Chernikov memcpy(target, ifd->params, len); 834f889d2efSBrooks Davis return (0); 83509ee0fc0SAlexander V. Chernikov } else 83609ee0fc0SAlexander V. Chernikov return (copyin(ifd->params, target, len)); 837f889d2efSBrooks Davis } 83809f6ff4fSMatt Macy 83909f6ff4fSMatt Macy const char * 84009f6ff4fSMatt Macy ifc_name(struct if_clone *ifc) 84109f6ff4fSMatt Macy { 84209f6ff4fSMatt Macy return (ifc->ifc_name); 84309f6ff4fSMatt Macy } 84409f6ff4fSMatt Macy 84509f6ff4fSMatt Macy void 84609f6ff4fSMatt Macy ifc_flags_set(struct if_clone *ifc, int flags) 84709f6ff4fSMatt Macy { 84809f6ff4fSMatt Macy ifc->ifc_flags = flags; 84909f6ff4fSMatt Macy } 85009f6ff4fSMatt Macy 85109f6ff4fSMatt Macy int 85209f6ff4fSMatt Macy ifc_flags_get(struct if_clone *ifc) 85309f6ff4fSMatt Macy { 85409f6ff4fSMatt Macy return (ifc->ifc_flags); 85509f6ff4fSMatt Macy } 856