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 36089104e0SAlexander V. Chernikov #include "opt_netlink.h" 37089104e0SAlexander V. Chernikov 38f889d2efSBrooks Davis #include <sys/param.h> 39c3322cb9SGleb Smirnoff #include <sys/eventhandler.h> 40f889d2efSBrooks Davis #include <sys/malloc.h> 41434dbbb3SRuslan Ermilov #include <sys/limits.h> 42f889d2efSBrooks Davis #include <sys/lock.h> 43f889d2efSBrooks Davis #include <sys/mutex.h> 44f889d2efSBrooks Davis #include <sys/kernel.h> 45f889d2efSBrooks Davis #include <sys/systm.h> 46f889d2efSBrooks Davis #include <sys/types.h> 47f889d2efSBrooks Davis #include <sys/socket.h> 48f889d2efSBrooks Davis 49f889d2efSBrooks Davis #include <net/if.h> 50f889d2efSBrooks Davis #include <net/if_var.h> 512c2b37adSJustin Hibbits #include <net/if_private.h> 5276039bc8SGleb Smirnoff #include <net/if_clone.h> 53f889d2efSBrooks Davis #include <net/radix.h> 54f889d2efSBrooks Davis #include <net/route.h> 5521ca7b57SMarko Zec #include <net/vnet.h> 56f889d2efSBrooks Davis 57089104e0SAlexander V. Chernikov #include <netlink/netlink.h> 58089104e0SAlexander V. Chernikov #include <netlink/netlink_ctl.h> 59089104e0SAlexander V. Chernikov #include <netlink/netlink_route.h> 60089104e0SAlexander V. Chernikov #include <netlink/route/route_var.h> 61089104e0SAlexander V. Chernikov 6242a58907SGleb Smirnoff /* Current IF_MAXUNIT expands maximum to 5 characters. */ 6342a58907SGleb Smirnoff #define IFCLOSIZ (IFNAMSIZ - 5) 6442a58907SGleb Smirnoff 6542a58907SGleb Smirnoff /* 6642a58907SGleb Smirnoff * Structure describing a `cloning' interface. 6742a58907SGleb Smirnoff * 6842a58907SGleb Smirnoff * List of locks 6942a58907SGleb Smirnoff * (c) const until freeing 7042a58907SGleb Smirnoff * (d) driver specific data, may need external protection. 7142a58907SGleb Smirnoff * (e) locked by if_cloners_mtx 7242a58907SGleb Smirnoff * (i) locked by ifc_mtx mtx 7342a58907SGleb Smirnoff */ 7442a58907SGleb Smirnoff struct if_clone { 7542a58907SGleb Smirnoff char ifc_name[IFCLOSIZ]; /* (c) Name of device, e.g. `gif' */ 7642a58907SGleb Smirnoff struct unrhdr *ifc_unrhdr; /* (c) alloc_unr(9) header */ 7742a58907SGleb Smirnoff int ifc_maxunit; /* (c) maximum unit number */ 7809f6ff4fSMatt Macy int ifc_flags; 7942a58907SGleb Smirnoff long ifc_refcnt; /* (i) Reference count. */ 8042a58907SGleb Smirnoff LIST_HEAD(, ifnet) ifc_iflist; /* (i) List of cloned interfaces */ 8142a58907SGleb Smirnoff struct mtx ifc_mtx; /* Mutex to protect members. */ 8242a58907SGleb Smirnoff 8309ee0fc0SAlexander V. Chernikov ifc_match_f *ifc_match; /* (c) Matcher function */ 8409ee0fc0SAlexander V. Chernikov ifc_create_f *ifc_create; /* (c) Creates new interface */ 8509ee0fc0SAlexander V. Chernikov ifc_destroy_f *ifc_destroy; /* (c) Destroys cloned interface */ 8642a58907SGleb Smirnoff 87089104e0SAlexander V. Chernikov ifc_create_nl_f *create_nl; /* (c) Netlink creation handler */ 88089104e0SAlexander V. Chernikov ifc_modify_nl_f *modify_nl; /* (c) Netlink modification handler */ 89089104e0SAlexander V. Chernikov ifc_dump_nl_f *dump_nl; /* (c) Netlink dump handler */ 90089104e0SAlexander V. Chernikov 9109ee0fc0SAlexander V. Chernikov #ifdef CLONE_COMPAT_13 9242a58907SGleb Smirnoff /* (c) Driver specific cloning functions. Called with no locks held. */ 9342a58907SGleb Smirnoff union { 9442a58907SGleb Smirnoff struct { /* advanced cloner */ 9542a58907SGleb Smirnoff ifc_create_t *_ifc_create; 9642a58907SGleb Smirnoff ifc_destroy_t *_ifc_destroy; 9742a58907SGleb Smirnoff } A; 9842a58907SGleb Smirnoff struct { /* simple cloner */ 9942a58907SGleb Smirnoff ifcs_create_t *_ifcs_create; 10042a58907SGleb Smirnoff ifcs_destroy_t *_ifcs_destroy; 10142a58907SGleb Smirnoff int _ifcs_minifs; /* minimum ifs */ 10242a58907SGleb Smirnoff 10342a58907SGleb Smirnoff } S; 10442a58907SGleb Smirnoff } U; 10509ee0fc0SAlexander V. Chernikov #define ifca_create U.A._ifc_create 10609ee0fc0SAlexander V. Chernikov #define ifca_destroy U.A._ifc_destroy 10742a58907SGleb Smirnoff #define ifcs_create U.S._ifcs_create 10842a58907SGleb Smirnoff #define ifcs_destroy U.S._ifcs_destroy 10942a58907SGleb Smirnoff #define ifcs_minifs U.S._ifcs_minifs 11009ee0fc0SAlexander V. Chernikov #endif 11142a58907SGleb Smirnoff 11242a58907SGleb Smirnoff LIST_ENTRY(if_clone) ifc_list; /* (e) On list of cloners */ 11342a58907SGleb Smirnoff }; 11442a58907SGleb Smirnoff 11509ee0fc0SAlexander V. Chernikov 11609ee0fc0SAlexander V. Chernikov 117f889d2efSBrooks Davis static void if_clone_free(struct if_clone *ifc); 118089104e0SAlexander V. Chernikov static int if_clone_createif_nl(struct if_clone *ifc, const char *name, 119089104e0SAlexander V. Chernikov struct ifc_data_nl *ifd); 120f889d2efSBrooks Davis 12109ee0fc0SAlexander V. Chernikov static int ifc_simple_match(struct if_clone *ifc, const char *name); 12209ee0fc0SAlexander V. Chernikov static int ifc_handle_unit(struct if_clone *ifc, char *name, size_t len, int *punit); 12383b5c80cSAlexander V. Chernikov static struct if_clone *ifc_find_cloner(const char *name); 12483b5c80cSAlexander V. Chernikov static struct if_clone *ifc_find_cloner_match(const char *name); 12509ee0fc0SAlexander V. Chernikov 12609ee0fc0SAlexander V. Chernikov #ifdef CLONE_COMPAT_13 12709ee0fc0SAlexander V. Chernikov static int ifc_simple_create_wrapper(struct if_clone *ifc, char *name, size_t maxlen, 12809ee0fc0SAlexander V. Chernikov struct ifc_data *ifc_data, struct ifnet **ifpp); 12909ee0fc0SAlexander V. Chernikov static int ifc_advanced_create_wrapper(struct if_clone *ifc, char *name, size_t maxlen, 13009ee0fc0SAlexander V. Chernikov struct ifc_data *ifc_data, struct ifnet **ifpp); 13109ee0fc0SAlexander V. Chernikov #endif 13242a58907SGleb Smirnoff 133f889d2efSBrooks Davis static struct mtx if_cloners_mtx; 1344ea05db8SGleb Smirnoff MTX_SYSINIT(if_cloners_lock, &if_cloners_mtx, "if_cloners lock", MTX_DEF); 1355f901c92SAndrew Turner VNET_DEFINE_STATIC(int, if_cloners_count); 136eddfbb76SRobert Watson VNET_DEFINE(LIST_HEAD(, if_clone), if_cloners); 137eddfbb76SRobert Watson 1381e77c105SRobert Watson #define V_if_cloners_count VNET(if_cloners_count) 1391e77c105SRobert Watson #define V_if_cloners VNET(if_cloners) 140f889d2efSBrooks Davis 141f889d2efSBrooks Davis #define IF_CLONERS_LOCK_ASSERT() mtx_assert(&if_cloners_mtx, MA_OWNED) 142f889d2efSBrooks Davis #define IF_CLONERS_LOCK() mtx_lock(&if_cloners_mtx) 143f889d2efSBrooks Davis #define IF_CLONERS_UNLOCK() mtx_unlock(&if_cloners_mtx) 144f889d2efSBrooks Davis 145f889d2efSBrooks Davis #define IF_CLONE_LOCK_INIT(ifc) \ 146f889d2efSBrooks Davis mtx_init(&(ifc)->ifc_mtx, "if_clone lock", NULL, MTX_DEF) 147f889d2efSBrooks Davis #define IF_CLONE_LOCK_DESTROY(ifc) mtx_destroy(&(ifc)->ifc_mtx) 148f889d2efSBrooks Davis #define IF_CLONE_LOCK_ASSERT(ifc) mtx_assert(&(ifc)->ifc_mtx, MA_OWNED) 149f889d2efSBrooks Davis #define IF_CLONE_LOCK(ifc) mtx_lock(&(ifc)->ifc_mtx) 150f889d2efSBrooks Davis #define IF_CLONE_UNLOCK(ifc) mtx_unlock(&(ifc)->ifc_mtx) 151f889d2efSBrooks Davis 152f889d2efSBrooks Davis #define IF_CLONE_ADDREF(ifc) \ 153f889d2efSBrooks Davis do { \ 154f889d2efSBrooks Davis IF_CLONE_LOCK(ifc); \ 155f889d2efSBrooks Davis IF_CLONE_ADDREF_LOCKED(ifc); \ 156f889d2efSBrooks Davis IF_CLONE_UNLOCK(ifc); \ 157f889d2efSBrooks Davis } while (0) 158f889d2efSBrooks Davis #define IF_CLONE_ADDREF_LOCKED(ifc) \ 159f889d2efSBrooks Davis do { \ 160f889d2efSBrooks Davis IF_CLONE_LOCK_ASSERT(ifc); \ 161f889d2efSBrooks Davis KASSERT((ifc)->ifc_refcnt >= 0, \ 162f889d2efSBrooks Davis ("negative refcnt %ld", (ifc)->ifc_refcnt)); \ 163f889d2efSBrooks Davis (ifc)->ifc_refcnt++; \ 164f889d2efSBrooks Davis } while (0) 165f889d2efSBrooks Davis #define IF_CLONE_REMREF(ifc) \ 166f889d2efSBrooks Davis do { \ 167f889d2efSBrooks Davis IF_CLONE_LOCK(ifc); \ 168f889d2efSBrooks Davis IF_CLONE_REMREF_LOCKED(ifc); \ 169f889d2efSBrooks Davis } while (0) 170f889d2efSBrooks Davis #define IF_CLONE_REMREF_LOCKED(ifc) \ 171f889d2efSBrooks Davis do { \ 172f889d2efSBrooks Davis IF_CLONE_LOCK_ASSERT(ifc); \ 173f889d2efSBrooks Davis KASSERT((ifc)->ifc_refcnt > 0, \ 174f889d2efSBrooks Davis ("bogus refcnt %ld", (ifc)->ifc_refcnt)); \ 175f889d2efSBrooks Davis if (--(ifc)->ifc_refcnt == 0) { \ 176f889d2efSBrooks Davis IF_CLONE_UNLOCK(ifc); \ 177f889d2efSBrooks Davis if_clone_free(ifc); \ 178ca64c799SMax Laier } else { \ 179f889d2efSBrooks Davis /* silently free the lock */ \ 180f889d2efSBrooks Davis IF_CLONE_UNLOCK(ifc); \ 181ca64c799SMax Laier } \ 182f889d2efSBrooks Davis } while (0) 183f889d2efSBrooks Davis 1844e7e0183SAndrew Thompson #define IFC_IFLIST_INSERT(_ifc, _ifp) \ 1854e7e0183SAndrew Thompson LIST_INSERT_HEAD(&_ifc->ifc_iflist, _ifp, if_clones) 1864e7e0183SAndrew Thompson #define IFC_IFLIST_REMOVE(_ifc, _ifp) \ 1874e7e0183SAndrew Thompson LIST_REMOVE(_ifp, if_clones) 1884e7e0183SAndrew Thompson 189c711aea6SPoul-Henning Kamp static MALLOC_DEFINE(M_CLONE, "clone", "interface cloning framework"); 190f889d2efSBrooks Davis 191d0728d71SRobert Watson void 192d0728d71SRobert Watson vnet_if_clone_init(void) 19337f17770SMarko Zec { 19437f17770SMarko Zec 19537f17770SMarko Zec LIST_INIT(&V_if_cloners); 19637f17770SMarko Zec } 19737f17770SMarko Zec 198f889d2efSBrooks Davis /* 1994e7e0183SAndrew Thompson * Lookup and create a clone network interface. 200f889d2efSBrooks Davis */ 201f889d2efSBrooks Davis int 202089104e0SAlexander V. Chernikov ifc_create_ifp(const char *name, struct ifc_data *ifd, struct ifnet **ifpp) 203f889d2efSBrooks Davis { 204089104e0SAlexander V. Chernikov struct if_clone *ifc = ifc_find_cloner_match(name); 205f889d2efSBrooks Davis 206f889d2efSBrooks Davis if (ifc == NULL) 207f889d2efSBrooks Davis return (EINVAL); 208f889d2efSBrooks Davis 209089104e0SAlexander V. Chernikov struct ifc_data_nl ifd_new = { 210089104e0SAlexander V. Chernikov .flags = ifd->flags, 211089104e0SAlexander V. Chernikov .unit = ifd->unit, 212089104e0SAlexander V. Chernikov .params = ifd->params, 213089104e0SAlexander V. Chernikov }; 214089104e0SAlexander V. Chernikov 215089104e0SAlexander V. Chernikov int error = if_clone_createif_nl(ifc, name, &ifd_new); 216089104e0SAlexander V. Chernikov 21709ee0fc0SAlexander V. Chernikov if (ifpp != NULL) 218089104e0SAlexander V. Chernikov *ifpp = ifd_new.ifp; 21909ee0fc0SAlexander V. Chernikov 22009ee0fc0SAlexander V. Chernikov return (error); 22109ee0fc0SAlexander V. Chernikov } 22209ee0fc0SAlexander V. Chernikov 223089104e0SAlexander V. Chernikov bool 224089104e0SAlexander V. Chernikov ifc_create_ifp_nl(const char *name, struct ifc_data_nl *ifd) 225089104e0SAlexander V. Chernikov { 226089104e0SAlexander V. Chernikov struct if_clone *ifc = ifc_find_cloner_match(name); 227089104e0SAlexander V. Chernikov if (ifc == NULL) { 228089104e0SAlexander V. Chernikov ifd->error = EINVAL; 229089104e0SAlexander V. Chernikov return (false); 230089104e0SAlexander V. Chernikov } 231089104e0SAlexander V. Chernikov 232089104e0SAlexander V. Chernikov ifd->error = if_clone_createif_nl(ifc, name, ifd); 233089104e0SAlexander V. Chernikov 234089104e0SAlexander V. Chernikov return (true); 235089104e0SAlexander V. Chernikov } 236089104e0SAlexander V. Chernikov 23709ee0fc0SAlexander V. Chernikov int 23809ee0fc0SAlexander V. Chernikov if_clone_create(char *name, size_t len, caddr_t params) 23909ee0fc0SAlexander V. Chernikov { 24009ee0fc0SAlexander V. Chernikov struct ifc_data ifd = { .params = params }; 24109ee0fc0SAlexander V. Chernikov struct ifnet *ifp; 24209ee0fc0SAlexander V. Chernikov 24309ee0fc0SAlexander V. Chernikov int error = ifc_create_ifp(name, &ifd, &ifp); 24409ee0fc0SAlexander V. Chernikov 24509ee0fc0SAlexander V. Chernikov if (error == 0) 24609ee0fc0SAlexander V. Chernikov strlcpy(name, if_name(ifp), len); 24709ee0fc0SAlexander V. Chernikov 24809ee0fc0SAlexander V. Chernikov return (error); 2494e7e0183SAndrew Thompson } 2504e7e0183SAndrew Thompson 251089104e0SAlexander V. Chernikov bool 252089104e0SAlexander V. Chernikov ifc_modify_ifp_nl(struct ifnet *ifp, struct ifc_data_nl *ifd) 253089104e0SAlexander V. Chernikov { 254089104e0SAlexander V. Chernikov struct if_clone *ifc = ifc_find_cloner(ifp->if_dname); 255089104e0SAlexander V. Chernikov if (ifc == NULL) { 256089104e0SAlexander V. Chernikov ifd->error = EINVAL; 257089104e0SAlexander V. Chernikov return (false); 258089104e0SAlexander V. Chernikov } 259089104e0SAlexander V. Chernikov 260089104e0SAlexander V. Chernikov ifd->error = (*ifc->modify_nl)(ifp, ifd); 261089104e0SAlexander V. Chernikov return (true); 262089104e0SAlexander V. Chernikov } 263089104e0SAlexander V. Chernikov 264089104e0SAlexander V. Chernikov bool 265089104e0SAlexander V. Chernikov ifc_dump_ifp_nl(struct ifnet *ifp, struct nl_writer *nw) 266089104e0SAlexander V. Chernikov { 267089104e0SAlexander V. Chernikov struct if_clone *ifc = ifc_find_cloner(ifp->if_dname); 268089104e0SAlexander V. Chernikov if (ifc == NULL) 269089104e0SAlexander V. Chernikov return (false); 270089104e0SAlexander V. Chernikov 271089104e0SAlexander V. Chernikov (*ifc->dump_nl)(ifp, nw); 272089104e0SAlexander V. Chernikov return (true); 273089104e0SAlexander V. Chernikov } 274089104e0SAlexander V. Chernikov 275089104e0SAlexander V. Chernikov static int 276089104e0SAlexander V. Chernikov ifc_create_ifp_nl_default(struct if_clone *ifc, char *name, size_t len, 277089104e0SAlexander V. Chernikov struct ifc_data_nl *ifd) 278089104e0SAlexander V. Chernikov { 279089104e0SAlexander V. Chernikov struct ifc_data ifd_new = { 280089104e0SAlexander V. Chernikov .flags = ifd->flags, 281089104e0SAlexander V. Chernikov .unit = ifd->unit, 282089104e0SAlexander V. Chernikov .params = ifd->params, 283089104e0SAlexander V. Chernikov }; 284089104e0SAlexander V. Chernikov 285089104e0SAlexander V. Chernikov return ((*ifc->ifc_create)(ifc, name, len, &ifd_new, &ifd->ifp)); 286089104e0SAlexander V. Chernikov } 287089104e0SAlexander V. Chernikov 288089104e0SAlexander V. Chernikov static int 289089104e0SAlexander V. Chernikov ifc_modify_ifp_nl_default(struct ifnet *ifp, struct ifc_data_nl *ifd) 290089104e0SAlexander V. Chernikov { 291089104e0SAlexander V. Chernikov if (ifd->lattrs != NULL) 292089104e0SAlexander V. Chernikov return (nl_modify_ifp_generic(ifp, ifd->lattrs, ifd->bm, ifd->npt)); 293089104e0SAlexander V. Chernikov return (0); 294089104e0SAlexander V. Chernikov } 295089104e0SAlexander V. Chernikov 296089104e0SAlexander V. Chernikov static void 297089104e0SAlexander V. Chernikov ifc_dump_ifp_nl_default(struct ifnet *ifp, struct nl_writer *nw) 298089104e0SAlexander V. Chernikov { 299089104e0SAlexander V. Chernikov int off = nlattr_add_nested(nw, IFLA_LINKINFO); 300089104e0SAlexander V. Chernikov 301089104e0SAlexander V. Chernikov if (off != 0) { 302089104e0SAlexander V. Chernikov nlattr_add_string(nw, IFLA_INFO_KIND, ifp->if_dname); 303089104e0SAlexander V. Chernikov nlattr_set_len(nw, off); 304089104e0SAlexander V. Chernikov } 305089104e0SAlexander V. Chernikov } 306089104e0SAlexander V. Chernikov 307b02fd8b7SKristof Provost void 30826c190d2SAlexander V. Chernikov ifc_link_ifp(struct if_clone *ifc, struct ifnet *ifp) 309b02fd8b7SKristof Provost { 310b02fd8b7SKristof Provost 311b02fd8b7SKristof Provost if ((ifc->ifc_flags & IFC_NOGROUP) == 0) 312b02fd8b7SKristof Provost if_addgroup(ifp, ifc->ifc_name); 313b02fd8b7SKristof Provost 314b02fd8b7SKristof Provost IF_CLONE_LOCK(ifc); 315b02fd8b7SKristof Provost IFC_IFLIST_INSERT(ifc, ifp); 316b02fd8b7SKristof Provost IF_CLONE_UNLOCK(ifc); 317b02fd8b7SKristof Provost } 318b02fd8b7SKristof Provost 31926c190d2SAlexander V. Chernikov void 32026c190d2SAlexander V. Chernikov if_clone_addif(struct if_clone *ifc, struct ifnet *ifp) 32126c190d2SAlexander V. Chernikov { 32226c190d2SAlexander V. Chernikov ifc_link_ifp(ifc, ifp); 32326c190d2SAlexander V. Chernikov } 32426c190d2SAlexander V. Chernikov 32526c190d2SAlexander V. Chernikov bool 32626c190d2SAlexander V. Chernikov ifc_unlink_ifp(struct if_clone *ifc, struct ifnet *ifp) 32726c190d2SAlexander V. Chernikov { 32826c190d2SAlexander V. Chernikov struct ifnet *ifcifp; 32926c190d2SAlexander V. Chernikov 33026c190d2SAlexander V. Chernikov IF_CLONE_LOCK(ifc); 33126c190d2SAlexander V. Chernikov LIST_FOREACH(ifcifp, &ifc->ifc_iflist, if_clones) { 33226c190d2SAlexander V. Chernikov if (ifcifp == ifp) { 33326c190d2SAlexander V. Chernikov IFC_IFLIST_REMOVE(ifc, ifp); 33426c190d2SAlexander V. Chernikov break; 33526c190d2SAlexander V. Chernikov } 33626c190d2SAlexander V. Chernikov } 33726c190d2SAlexander V. Chernikov IF_CLONE_UNLOCK(ifc); 33826c190d2SAlexander V. Chernikov 33926c190d2SAlexander V. Chernikov if (ifcifp != NULL && (ifc->ifc_flags & IFC_F_NOGROUP) == 0) 34026c190d2SAlexander V. Chernikov if_delgroup(ifp, ifc->ifc_name); 34126c190d2SAlexander V. Chernikov 34226c190d2SAlexander V. Chernikov return (ifcifp != NULL); 34326c190d2SAlexander V. Chernikov } 34426c190d2SAlexander V. Chernikov 34526c190d2SAlexander V. Chernikov static struct if_clone * 34683b5c80cSAlexander V. Chernikov ifc_find_cloner_match(const char *name) 34726c190d2SAlexander V. Chernikov { 34826c190d2SAlexander V. Chernikov struct if_clone *ifc; 34926c190d2SAlexander V. Chernikov 35083b5c80cSAlexander V. Chernikov IF_CLONERS_LOCK(); 35183b5c80cSAlexander V. Chernikov LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { 35283b5c80cSAlexander V. Chernikov if (ifc->ifc_match(ifc, name)) 35383b5c80cSAlexander V. Chernikov break; 35483b5c80cSAlexander V. Chernikov } 35583b5c80cSAlexander V. Chernikov IF_CLONERS_UNLOCK(); 35683b5c80cSAlexander V. Chernikov 35783b5c80cSAlexander V. Chernikov return (ifc); 35883b5c80cSAlexander V. Chernikov } 35983b5c80cSAlexander V. Chernikov 36083b5c80cSAlexander V. Chernikov static struct if_clone * 36183b5c80cSAlexander V. Chernikov ifc_find_cloner(const char *name) 36283b5c80cSAlexander V. Chernikov { 36383b5c80cSAlexander V. Chernikov struct if_clone *ifc; 36483b5c80cSAlexander V. Chernikov 36526c190d2SAlexander V. Chernikov IF_CLONERS_LOCK(); 36626c190d2SAlexander V. Chernikov LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { 36726c190d2SAlexander V. Chernikov if (strcmp(ifc->ifc_name, name) == 0) { 36826c190d2SAlexander V. Chernikov break; 36926c190d2SAlexander V. Chernikov } 37026c190d2SAlexander V. Chernikov } 37126c190d2SAlexander V. Chernikov IF_CLONERS_UNLOCK(); 37283b5c80cSAlexander V. Chernikov 37383b5c80cSAlexander V. Chernikov return (ifc); 37483b5c80cSAlexander V. Chernikov } 37583b5c80cSAlexander V. Chernikov 37683b5c80cSAlexander V. Chernikov static struct if_clone * 37783b5c80cSAlexander V. Chernikov ifc_find_cloner_in_vnet(const char *name, struct vnet *vnet) 37883b5c80cSAlexander V. Chernikov { 37983b5c80cSAlexander V. Chernikov CURVNET_SET_QUIET(vnet); 38083b5c80cSAlexander V. Chernikov struct if_clone *ifc = ifc_find_cloner(name); 38126c190d2SAlexander V. Chernikov CURVNET_RESTORE(); 38226c190d2SAlexander V. Chernikov 38326c190d2SAlexander V. Chernikov return (ifc); 38426c190d2SAlexander V. Chernikov } 38526c190d2SAlexander V. Chernikov 3864e7e0183SAndrew Thompson /* 3874e7e0183SAndrew Thompson * Create a clone network interface. 3884e7e0183SAndrew Thompson */ 3894e7e0183SAndrew Thompson static int 390089104e0SAlexander V. Chernikov if_clone_createif_nl(struct if_clone *ifc, const char *ifname, struct ifc_data_nl *ifd) 3914e7e0183SAndrew Thompson { 392089104e0SAlexander V. Chernikov char name[IFNAMSIZ]; 393089104e0SAlexander V. Chernikov int error; 394089104e0SAlexander V. Chernikov 395089104e0SAlexander V. Chernikov strlcpy(name, ifname, sizeof(name)); 3964e7e0183SAndrew Thompson 3974e7e0183SAndrew Thompson if (ifunit(name) != NULL) 3984e7e0183SAndrew Thompson return (EEXIST); 3994e7e0183SAndrew Thompson 40009ee0fc0SAlexander V. Chernikov if (ifc->ifc_flags & IFC_F_AUTOUNIT) { 401089104e0SAlexander V. Chernikov if ((error = ifc_handle_unit(ifc, name, sizeof(name), &ifd->unit)) != 0) 402089104e0SAlexander V. Chernikov return (error); 4034e7e0183SAndrew Thompson } 40409ee0fc0SAlexander V. Chernikov 405089104e0SAlexander V. Chernikov if (ifd->lattrs != NULL) 406089104e0SAlexander V. Chernikov error = (*ifc->create_nl)(ifc, name, sizeof(name), ifd); 407089104e0SAlexander V. Chernikov else 408089104e0SAlexander V. Chernikov error = ifc_create_ifp_nl_default(ifc, name, sizeof(name), ifd); 409089104e0SAlexander V. Chernikov if (error != 0) { 410089104e0SAlexander V. Chernikov if (ifc->ifc_flags & IFC_F_AUTOUNIT) 411089104e0SAlexander V. Chernikov ifc_free_unit(ifc, ifd->unit); 412089104e0SAlexander V. Chernikov return (error); 413089104e0SAlexander V. Chernikov } 4144e7e0183SAndrew Thompson 415089104e0SAlexander V. Chernikov MPASS(ifd->ifp != NULL); 416089104e0SAlexander V. Chernikov if_clone_addif(ifc, ifd->ifp); 417089104e0SAlexander V. Chernikov 418089104e0SAlexander V. Chernikov if (ifd->lattrs != NULL) 419089104e0SAlexander V. Chernikov error = (*ifc->modify_nl)(ifd->ifp, ifd); 420089104e0SAlexander V. Chernikov 421089104e0SAlexander V. Chernikov return (error); 422f889d2efSBrooks Davis } 423f889d2efSBrooks Davis 424f889d2efSBrooks Davis /* 4254e7e0183SAndrew Thompson * Lookup and destroy a clone network interface. 426f889d2efSBrooks Davis */ 427f889d2efSBrooks Davis int 428f889d2efSBrooks Davis if_clone_destroy(const char *name) 429f889d2efSBrooks Davis { 430d0088cdeSBjoern A. Zeeb int err; 431f889d2efSBrooks Davis struct if_clone *ifc; 432f889d2efSBrooks Davis struct ifnet *ifp; 433f889d2efSBrooks Davis 434d0088cdeSBjoern A. Zeeb ifp = ifunit_ref(name); 435f889d2efSBrooks Davis if (ifp == NULL) 436f889d2efSBrooks Davis return (ENXIO); 437f889d2efSBrooks Davis 43883b5c80cSAlexander V. Chernikov ifc = ifc_find_cloner_in_vnet(ifp->if_dname, ifp->if_home_vnet); 439d0088cdeSBjoern A. Zeeb if (ifc == NULL) { 440d0088cdeSBjoern A. Zeeb if_rele(ifp); 441f889d2efSBrooks Davis return (EINVAL); 442d0088cdeSBjoern A. Zeeb } 443f889d2efSBrooks Davis 444d0088cdeSBjoern A. Zeeb err = if_clone_destroyif(ifc, ifp); 445d0088cdeSBjoern A. Zeeb if_rele(ifp); 446d0088cdeSBjoern A. Zeeb return err; 4474e7e0183SAndrew Thompson } 4484e7e0183SAndrew Thompson 4494e7e0183SAndrew Thompson /* 4504e7e0183SAndrew Thompson * Destroy a clone network interface. 4514e7e0183SAndrew Thompson */ 45209ee0fc0SAlexander V. Chernikov static int 45309ee0fc0SAlexander V. Chernikov if_clone_destroyif_flags(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags) 4544e7e0183SAndrew Thompson { 4554e7e0183SAndrew Thompson int err; 4564e7e0183SAndrew Thompson 45737f17770SMarko Zec /* 45837f17770SMarko Zec * Given that the cloned ifnet might be attached to a different 45937f17770SMarko Zec * vnet from where its cloner was registered, we have to 46037f17770SMarko Zec * switch to the vnet context of the target vnet. 46137f17770SMarko Zec */ 46237f17770SMarko Zec CURVNET_SET_QUIET(ifp->if_vnet); 46337f17770SMarko Zec 46426c190d2SAlexander V. Chernikov if (!ifc_unlink_ifp(ifc, ifp)) { 465c769e1beSBjoern A. Zeeb CURVNET_RESTORE(); 466c769e1beSBjoern A. Zeeb return (ENXIO); /* ifp is not on the list. */ 467c769e1beSBjoern A. Zeeb } 4680dad3f0eSMax Laier 46909ee0fc0SAlexander V. Chernikov int unit = ifp->if_dunit; 47009ee0fc0SAlexander V. Chernikov err = (*ifc->ifc_destroy)(ifc, ifp, flags); 471f889d2efSBrooks Davis 47226c190d2SAlexander V. Chernikov if (err != 0) 47326c190d2SAlexander V. Chernikov ifc_link_ifp(ifc, ifp); 47426c190d2SAlexander V. Chernikov else if (ifc->ifc_flags & IFC_F_AUTOUNIT) 47509ee0fc0SAlexander V. Chernikov ifc_free_unit(ifc, unit); 47621ca7b57SMarko Zec CURVNET_RESTORE(); 477f889d2efSBrooks Davis return (err); 478f889d2efSBrooks Davis } 479f889d2efSBrooks Davis 48009ee0fc0SAlexander V. Chernikov int 48109ee0fc0SAlexander V. Chernikov if_clone_destroyif(struct if_clone *ifc, struct ifnet *ifp) 48209ee0fc0SAlexander V. Chernikov { 48309ee0fc0SAlexander V. Chernikov return (if_clone_destroyif_flags(ifc, ifp, 0)); 48409ee0fc0SAlexander V. Chernikov } 48509ee0fc0SAlexander V. Chernikov 48642a58907SGleb Smirnoff static struct if_clone * 48742a58907SGleb Smirnoff if_clone_alloc(const char *name, int maxunit) 48842a58907SGleb Smirnoff { 48942a58907SGleb Smirnoff struct if_clone *ifc; 49042a58907SGleb Smirnoff 49142a58907SGleb Smirnoff KASSERT(name != NULL, ("%s: no name\n", __func__)); 49242a58907SGleb Smirnoff 49342a58907SGleb Smirnoff ifc = malloc(sizeof(struct if_clone), M_CLONE, M_WAITOK | M_ZERO); 49442a58907SGleb Smirnoff strncpy(ifc->ifc_name, name, IFCLOSIZ-1); 49542a58907SGleb Smirnoff IF_CLONE_LOCK_INIT(ifc); 49642a58907SGleb Smirnoff IF_CLONE_ADDREF(ifc); 49742a58907SGleb Smirnoff ifc->ifc_maxunit = maxunit ? maxunit : IF_MAXUNIT; 49842a58907SGleb Smirnoff ifc->ifc_unrhdr = new_unrhdr(0, ifc->ifc_maxunit, &ifc->ifc_mtx); 49942a58907SGleb Smirnoff LIST_INIT(&ifc->ifc_iflist); 50042a58907SGleb Smirnoff 501089104e0SAlexander V. Chernikov ifc->create_nl = ifc_create_ifp_nl_default; 502089104e0SAlexander V. Chernikov ifc->modify_nl = ifc_modify_ifp_nl_default; 503089104e0SAlexander V. Chernikov ifc->dump_nl = ifc_dump_ifp_nl_default; 504089104e0SAlexander V. Chernikov 50542a58907SGleb Smirnoff return (ifc); 50642a58907SGleb Smirnoff } 50742a58907SGleb Smirnoff 50842a58907SGleb Smirnoff static int 509f889d2efSBrooks Davis if_clone_attach(struct if_clone *ifc) 510f889d2efSBrooks Davis { 5112e9fff5bSGleb Smirnoff struct if_clone *ifc1; 512f889d2efSBrooks Davis 513f889d2efSBrooks Davis IF_CLONERS_LOCK(); 5142e9fff5bSGleb Smirnoff LIST_FOREACH(ifc1, &V_if_cloners, ifc_list) 5152e9fff5bSGleb Smirnoff if (strcmp(ifc->ifc_name, ifc1->ifc_name) == 0) { 5162e9fff5bSGleb Smirnoff IF_CLONERS_UNLOCK(); 5172e9fff5bSGleb Smirnoff IF_CLONE_REMREF(ifc); 5182e9fff5bSGleb Smirnoff return (EEXIST); 5192e9fff5bSGleb Smirnoff } 52037f17770SMarko Zec LIST_INSERT_HEAD(&V_if_cloners, ifc, ifc_list); 52137f17770SMarko Zec V_if_cloners_count++; 522f889d2efSBrooks Davis IF_CLONERS_UNLOCK(); 523f889d2efSBrooks Davis 52442a58907SGleb Smirnoff return (0); 52542a58907SGleb Smirnoff } 52642a58907SGleb Smirnoff 52742a58907SGleb Smirnoff struct if_clone * 52809ee0fc0SAlexander V. Chernikov ifc_attach_cloner(const char *name, struct if_clone_addreq *req) 52942a58907SGleb Smirnoff { 53009ee0fc0SAlexander V. Chernikov if (req->create_f == NULL || req->destroy_f == NULL) 53109ee0fc0SAlexander V. Chernikov return (NULL); 53209ee0fc0SAlexander V. Chernikov if (strnlen(name, IFCLOSIZ) >= (IFCLOSIZ - 1)) 53309ee0fc0SAlexander V. Chernikov return (NULL); 53442a58907SGleb Smirnoff 53509ee0fc0SAlexander V. Chernikov struct if_clone *ifc = if_clone_alloc(name, req->maxunit); 53609ee0fc0SAlexander V. Chernikov ifc->ifc_match = req->match_f != NULL ? req->match_f : ifc_simple_match; 53709ee0fc0SAlexander V. Chernikov ifc->ifc_create = req->create_f; 53809ee0fc0SAlexander V. Chernikov ifc->ifc_destroy = req->destroy_f; 53909ee0fc0SAlexander V. Chernikov ifc->ifc_flags = (req->flags & (IFC_F_AUTOUNIT | IFC_F_NOGROUP)); 54042a58907SGleb Smirnoff 541089104e0SAlexander V. Chernikov if (req->version == 2) { 542089104e0SAlexander V. Chernikov struct if_clone_addreq_v2 *req2 = (struct if_clone_addreq_v2 *)req; 543089104e0SAlexander V. Chernikov 544089104e0SAlexander V. Chernikov ifc->create_nl = req2->create_nl_f; 545089104e0SAlexander V. Chernikov ifc->modify_nl = req2->modify_nl_f; 546089104e0SAlexander V. Chernikov ifc->dump_nl = req2->dump_nl_f; 547089104e0SAlexander V. Chernikov } 548089104e0SAlexander V. Chernikov 549089104e0SAlexander V. Chernikov ifc->dump_nl = ifc_dump_ifp_nl_default; 550089104e0SAlexander V. Chernikov 5513395dd6eSAlexander Kabaev if (if_clone_attach(ifc) != 0) 55242a58907SGleb Smirnoff return (NULL); 55342a58907SGleb Smirnoff 554f889d2efSBrooks Davis EVENTHANDLER_INVOKE(if_clone_event, ifc); 5552e9fff5bSGleb Smirnoff 55642a58907SGleb Smirnoff return (ifc); 55742a58907SGleb Smirnoff } 55842a58907SGleb Smirnoff 55909ee0fc0SAlexander V. Chernikov void 56009ee0fc0SAlexander V. Chernikov ifc_detach_cloner(struct if_clone *ifc) 56109ee0fc0SAlexander V. Chernikov { 56209ee0fc0SAlexander V. Chernikov if_clone_detach(ifc); 56309ee0fc0SAlexander V. Chernikov } 56409ee0fc0SAlexander V. Chernikov 56509ee0fc0SAlexander V. Chernikov 56609ee0fc0SAlexander V. Chernikov #ifdef CLONE_COMPAT_13 56709ee0fc0SAlexander V. Chernikov 56809ee0fc0SAlexander V. Chernikov static int 56909ee0fc0SAlexander V. Chernikov ifc_advanced_create_wrapper(struct if_clone *ifc, char *name, size_t maxlen, 57009ee0fc0SAlexander V. Chernikov struct ifc_data *ifc_data, struct ifnet **ifpp) 57109ee0fc0SAlexander V. Chernikov { 57209ee0fc0SAlexander V. Chernikov int error = ifc->ifca_create(ifc, name, maxlen, ifc_data->params); 57309ee0fc0SAlexander V. Chernikov 57409ee0fc0SAlexander V. Chernikov if (error == 0) 57509ee0fc0SAlexander V. Chernikov *ifpp = ifunit(name); 57609ee0fc0SAlexander V. Chernikov return (error); 57709ee0fc0SAlexander V. Chernikov } 57809ee0fc0SAlexander V. Chernikov 57909ee0fc0SAlexander V. Chernikov static int 58009ee0fc0SAlexander V. Chernikov ifc_advanced_destroy_wrapper(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags) 58109ee0fc0SAlexander V. Chernikov { 58209ee0fc0SAlexander V. Chernikov if (ifc->ifca_destroy == NULL) 58309ee0fc0SAlexander V. Chernikov return (ENOTSUP); 58409ee0fc0SAlexander V. Chernikov return (ifc->ifca_destroy(ifc, ifp)); 58509ee0fc0SAlexander V. Chernikov } 58609ee0fc0SAlexander V. Chernikov 58709ee0fc0SAlexander V. Chernikov struct if_clone * 58809ee0fc0SAlexander V. Chernikov if_clone_advanced(const char *name, u_int maxunit, ifc_match_t match, 58909ee0fc0SAlexander V. Chernikov ifc_create_t create, ifc_destroy_t destroy) 59009ee0fc0SAlexander V. Chernikov { 59109ee0fc0SAlexander V. Chernikov struct if_clone *ifc; 59209ee0fc0SAlexander V. Chernikov 59309ee0fc0SAlexander V. Chernikov ifc = if_clone_alloc(name, maxunit); 59409ee0fc0SAlexander V. Chernikov ifc->ifc_match = match; 59509ee0fc0SAlexander V. Chernikov ifc->ifc_create = ifc_advanced_create_wrapper; 59609ee0fc0SAlexander V. Chernikov ifc->ifc_destroy = ifc_advanced_destroy_wrapper; 59709ee0fc0SAlexander V. Chernikov ifc->ifca_destroy = destroy; 59809ee0fc0SAlexander V. Chernikov ifc->ifca_create = create; 59909ee0fc0SAlexander V. Chernikov 60009ee0fc0SAlexander V. Chernikov if (if_clone_attach(ifc) != 0) 60109ee0fc0SAlexander V. Chernikov return (NULL); 60209ee0fc0SAlexander V. Chernikov 60309ee0fc0SAlexander V. Chernikov EVENTHANDLER_INVOKE(if_clone_event, ifc); 60409ee0fc0SAlexander V. Chernikov 60509ee0fc0SAlexander V. Chernikov return (ifc); 60609ee0fc0SAlexander V. Chernikov } 60709ee0fc0SAlexander V. Chernikov 60809ee0fc0SAlexander V. Chernikov static int 60909ee0fc0SAlexander V. Chernikov ifc_simple_create_wrapper(struct if_clone *ifc, char *name, size_t maxlen, 61009ee0fc0SAlexander V. Chernikov struct ifc_data *ifc_data, struct ifnet **ifpp) 61109ee0fc0SAlexander V. Chernikov { 61209ee0fc0SAlexander V. Chernikov int unit = 0; 61309ee0fc0SAlexander V. Chernikov 61409ee0fc0SAlexander V. Chernikov ifc_name2unit(name, &unit); 61509ee0fc0SAlexander V. Chernikov int error = ifc->ifcs_create(ifc, unit, ifc_data->params); 61609ee0fc0SAlexander V. Chernikov if (error == 0) 61709ee0fc0SAlexander V. Chernikov *ifpp = ifunit(name); 61809ee0fc0SAlexander V. Chernikov return (error); 61909ee0fc0SAlexander V. Chernikov } 62009ee0fc0SAlexander V. Chernikov 62109ee0fc0SAlexander V. Chernikov static int 62209ee0fc0SAlexander V. Chernikov ifc_simple_destroy_wrapper(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags) 62309ee0fc0SAlexander V. Chernikov { 62409ee0fc0SAlexander V. Chernikov if (ifp->if_dunit < ifc->ifcs_minifs && (flags & IFC_F_FORCE) == 0) 62509ee0fc0SAlexander V. Chernikov return (EINVAL); 62609ee0fc0SAlexander V. Chernikov 62709ee0fc0SAlexander V. Chernikov ifc->ifcs_destroy(ifp); 62809ee0fc0SAlexander V. Chernikov return (0); 62909ee0fc0SAlexander V. Chernikov } 63009ee0fc0SAlexander V. Chernikov 63142a58907SGleb Smirnoff struct if_clone * 63242a58907SGleb Smirnoff if_clone_simple(const char *name, ifcs_create_t create, ifcs_destroy_t destroy, 63342a58907SGleb Smirnoff u_int minifs) 63442a58907SGleb Smirnoff { 63542a58907SGleb Smirnoff struct if_clone *ifc; 63642a58907SGleb Smirnoff u_int unit; 63742a58907SGleb Smirnoff 63842a58907SGleb Smirnoff ifc = if_clone_alloc(name, 0); 63909ee0fc0SAlexander V. Chernikov ifc->ifc_match = ifc_simple_match; 64009ee0fc0SAlexander V. Chernikov ifc->ifc_create = ifc_simple_create_wrapper; 64109ee0fc0SAlexander V. Chernikov ifc->ifc_destroy = ifc_simple_destroy_wrapper; 64242a58907SGleb Smirnoff ifc->ifcs_create = create; 64342a58907SGleb Smirnoff ifc->ifcs_destroy = destroy; 64442a58907SGleb Smirnoff ifc->ifcs_minifs = minifs; 64509ee0fc0SAlexander V. Chernikov ifc->ifc_flags = IFC_F_AUTOUNIT; 64642a58907SGleb Smirnoff 6473395dd6eSAlexander Kabaev if (if_clone_attach(ifc) != 0) 64842a58907SGleb Smirnoff return (NULL); 64942a58907SGleb Smirnoff 65042a58907SGleb Smirnoff for (unit = 0; unit < minifs; unit++) { 65142a58907SGleb Smirnoff char name[IFNAMSIZ]; 65246d0f824SMatt Macy int error __unused; 653089104e0SAlexander V. Chernikov struct ifc_data_nl ifd = {}; 65442a58907SGleb Smirnoff 65542a58907SGleb Smirnoff snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, unit); 656089104e0SAlexander V. Chernikov error = if_clone_createif_nl(ifc, name, &ifd); 65742a58907SGleb Smirnoff KASSERT(error == 0, 65842a58907SGleb Smirnoff ("%s: failed to create required interface %s", 65942a58907SGleb Smirnoff __func__, name)); 66042a58907SGleb Smirnoff } 66142a58907SGleb Smirnoff 66242a58907SGleb Smirnoff EVENTHANDLER_INVOKE(if_clone_event, ifc); 66342a58907SGleb Smirnoff 66442a58907SGleb Smirnoff return (ifc); 665f889d2efSBrooks Davis } 66609ee0fc0SAlexander V. Chernikov #endif 667f889d2efSBrooks Davis 668f889d2efSBrooks Davis /* 669f889d2efSBrooks Davis * Unregister a network interface cloner. 670f889d2efSBrooks Davis */ 671f889d2efSBrooks Davis void 672f889d2efSBrooks Davis if_clone_detach(struct if_clone *ifc) 673f889d2efSBrooks Davis { 674f889d2efSBrooks Davis 675f889d2efSBrooks Davis IF_CLONERS_LOCK(); 676f889d2efSBrooks Davis LIST_REMOVE(ifc, ifc_list); 67737f17770SMarko Zec V_if_cloners_count--; 678f889d2efSBrooks Davis IF_CLONERS_UNLOCK(); 679f889d2efSBrooks Davis 6804e7e0183SAndrew Thompson /* destroy all interfaces for this cloner */ 6814e7e0183SAndrew Thompson while (!LIST_EMPTY(&ifc->ifc_iflist)) 68209ee0fc0SAlexander V. Chernikov if_clone_destroyif_flags(ifc, LIST_FIRST(&ifc->ifc_iflist), IFC_F_FORCE); 6834e7e0183SAndrew Thompson 684f889d2efSBrooks Davis IF_CLONE_REMREF(ifc); 685f889d2efSBrooks Davis } 686f889d2efSBrooks Davis 687f889d2efSBrooks Davis static void 688f889d2efSBrooks Davis if_clone_free(struct if_clone *ifc) 689f889d2efSBrooks Davis { 690f889d2efSBrooks Davis 6914e7e0183SAndrew Thompson KASSERT(LIST_EMPTY(&ifc->ifc_iflist), 6924e7e0183SAndrew Thompson ("%s: ifc_iflist not empty", __func__)); 6934e7e0183SAndrew Thompson 694f889d2efSBrooks Davis IF_CLONE_LOCK_DESTROY(ifc); 6952e9fff5bSGleb Smirnoff delete_unrhdr(ifc->ifc_unrhdr); 69642a58907SGleb Smirnoff free(ifc, M_CLONE); 697f889d2efSBrooks Davis } 698f889d2efSBrooks Davis 699f889d2efSBrooks Davis /* 700f889d2efSBrooks Davis * Provide list of interface cloners to userspace. 701f889d2efSBrooks Davis */ 702f889d2efSBrooks Davis int 703f889d2efSBrooks Davis if_clone_list(struct if_clonereq *ifcr) 704f889d2efSBrooks Davis { 705c859ef97SBrooks Davis char *buf, *dst, *outbuf = NULL; 706f889d2efSBrooks Davis struct if_clone *ifc; 707c859ef97SBrooks Davis int buf_count, count, err = 0; 708c859ef97SBrooks Davis 709a6d00835SMaxim Konovalov if (ifcr->ifcr_count < 0) 710a6d00835SMaxim Konovalov return (EINVAL); 711a6d00835SMaxim Konovalov 712c859ef97SBrooks Davis IF_CLONERS_LOCK(); 713c859ef97SBrooks Davis /* 714c859ef97SBrooks Davis * Set our internal output buffer size. We could end up not 715c859ef97SBrooks Davis * reporting a cloner that is added between the unlock and lock 716c859ef97SBrooks Davis * below, but that's not a major problem. Not caping our 717c859ef97SBrooks Davis * allocation to the number of cloners actually in the system 718c859ef97SBrooks Davis * could be because that would let arbitrary users cause us to 719a4641f4eSPedro F. Giffuni * allocate arbitrary amounts of kernel memory. 720c859ef97SBrooks Davis */ 72137f17770SMarko Zec buf_count = (V_if_cloners_count < ifcr->ifcr_count) ? 72237f17770SMarko Zec V_if_cloners_count : ifcr->ifcr_count; 723c859ef97SBrooks Davis IF_CLONERS_UNLOCK(); 724c859ef97SBrooks Davis 725c859ef97SBrooks Davis outbuf = malloc(IFNAMSIZ*buf_count, M_CLONE, M_WAITOK | M_ZERO); 726f889d2efSBrooks Davis 727f889d2efSBrooks Davis IF_CLONERS_LOCK(); 728f889d2efSBrooks Davis 72937f17770SMarko Zec ifcr->ifcr_total = V_if_cloners_count; 730f889d2efSBrooks Davis if ((dst = ifcr->ifcr_buffer) == NULL) { 731f889d2efSBrooks Davis /* Just asking how many there are. */ 732f889d2efSBrooks Davis goto done; 733f889d2efSBrooks Davis } 73437f17770SMarko Zec count = (V_if_cloners_count < buf_count) ? 73537f17770SMarko Zec V_if_cloners_count : buf_count; 736f889d2efSBrooks Davis 73737f17770SMarko Zec for (ifc = LIST_FIRST(&V_if_cloners), buf = outbuf; 738c859ef97SBrooks Davis ifc != NULL && count != 0; 739c859ef97SBrooks Davis ifc = LIST_NEXT(ifc, ifc_list), count--, buf += IFNAMSIZ) { 740c859ef97SBrooks Davis strlcpy(buf, ifc->ifc_name, IFNAMSIZ); 741f889d2efSBrooks Davis } 742f889d2efSBrooks Davis 743f889d2efSBrooks Davis done: 744f889d2efSBrooks Davis IF_CLONERS_UNLOCK(); 74592f19df4SAlexander Kabaev if (err == 0 && dst != NULL) 746c859ef97SBrooks Davis err = copyout(outbuf, dst, buf_count*IFNAMSIZ); 747c859ef97SBrooks Davis if (outbuf != NULL) 748c859ef97SBrooks Davis free(outbuf, M_CLONE); 749f889d2efSBrooks Davis return (err); 750f889d2efSBrooks Davis } 751f889d2efSBrooks Davis 75254712fc4SGleb Smirnoff #ifdef VIMAGE 753f889d2efSBrooks Davis /* 75454712fc4SGleb Smirnoff * if_clone_restoregroup() is used in context of if_vmove(). 75554712fc4SGleb Smirnoff * 75654712fc4SGleb Smirnoff * Since if_detach_internal() has removed the interface from ALL groups, we 75754712fc4SGleb Smirnoff * need to "restore" interface membership in the cloner's group. Note that 75854712fc4SGleb Smirnoff * interface belongs to cloner in its home vnet, so we first find the original 75954712fc4SGleb Smirnoff * cloner, and then we confirm that cloner with the same name exists in the 76054712fc4SGleb Smirnoff * current vnet. 761c92a456bSHiroki Sato */ 76254712fc4SGleb Smirnoff void 76354712fc4SGleb Smirnoff if_clone_restoregroup(struct ifnet *ifp) 764c92a456bSHiroki Sato { 76554712fc4SGleb Smirnoff struct if_clone *ifc; 766c92a456bSHiroki Sato struct ifnet *ifcifp; 76754712fc4SGleb Smirnoff char ifc_name[IFCLOSIZ] = { [0] = '\0' }; 768c92a456bSHiroki Sato 76954712fc4SGleb Smirnoff CURVNET_SET_QUIET(ifp->if_home_vnet); 770c92a456bSHiroki Sato IF_CLONERS_LOCK(); 771c92a456bSHiroki Sato LIST_FOREACH(ifc, &V_if_cloners, ifc_list) { 772c92a456bSHiroki Sato IF_CLONE_LOCK(ifc); 773c92a456bSHiroki Sato LIST_FOREACH(ifcifp, &ifc->ifc_iflist, if_clones) { 774c92a456bSHiroki Sato if (ifp == ifcifp) { 77554712fc4SGleb Smirnoff strncpy(ifc_name, ifc->ifc_name, IFCLOSIZ-1); 776c92a456bSHiroki Sato break; 777c92a456bSHiroki Sato } 778c92a456bSHiroki Sato } 779c92a456bSHiroki Sato IF_CLONE_UNLOCK(ifc); 78054712fc4SGleb Smirnoff if (ifc_name[0] != '\0') 781c92a456bSHiroki Sato break; 782c92a456bSHiroki Sato } 78354712fc4SGleb Smirnoff CURVNET_RESTORE(); 78454712fc4SGleb Smirnoff LIST_FOREACH(ifc, &V_if_cloners, ifc_list) 78554712fc4SGleb Smirnoff if (strcmp(ifc->ifc_name, ifc_name) == 0 && 78654712fc4SGleb Smirnoff ((ifc->ifc_flags & IFC_NOGROUP) == 0)) 78754712fc4SGleb Smirnoff break; 788c92a456bSHiroki Sato IF_CLONERS_UNLOCK(); 789c92a456bSHiroki Sato 79054712fc4SGleb Smirnoff if (ifc != NULL) 79154712fc4SGleb Smirnoff if_addgroup(ifp, ifc_name); 792c92a456bSHiroki Sato } 79354712fc4SGleb Smirnoff #endif 794c92a456bSHiroki Sato 795c92a456bSHiroki Sato /* 796f889d2efSBrooks Davis * A utility function to extract unit numbers from interface names of 79753729367SAlexander V. Chernikov * the form name###. 798f889d2efSBrooks Davis * 799f889d2efSBrooks Davis * Returns 0 on success and an error on failure. 800f889d2efSBrooks Davis */ 801f889d2efSBrooks Davis int 802f889d2efSBrooks Davis ifc_name2unit(const char *name, int *unit) 803f889d2efSBrooks Davis { 804f889d2efSBrooks Davis const char *cp; 805434dbbb3SRuslan Ermilov int cutoff = INT_MAX / 10; 806434dbbb3SRuslan Ermilov int cutlim = INT_MAX % 10; 807f889d2efSBrooks Davis 80853729367SAlexander V. Chernikov for (cp = name; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++) 80953729367SAlexander V. Chernikov ; 810f889d2efSBrooks Davis if (*cp == '\0') { 811f889d2efSBrooks Davis *unit = -1; 812434dbbb3SRuslan Ermilov } else if (cp[0] == '0' && cp[1] != '\0') { 813434dbbb3SRuslan Ermilov /* Disallow leading zeroes. */ 814434dbbb3SRuslan Ermilov return (EINVAL); 815f889d2efSBrooks Davis } else { 816f889d2efSBrooks Davis for (*unit = 0; *cp != '\0'; cp++) { 817f889d2efSBrooks Davis if (*cp < '0' || *cp > '9') { 818f889d2efSBrooks Davis /* Bogus unit number. */ 819f889d2efSBrooks Davis return (EINVAL); 820f889d2efSBrooks Davis } 821434dbbb3SRuslan Ermilov if (*unit > cutoff || 822434dbbb3SRuslan Ermilov (*unit == cutoff && *cp - '0' > cutlim)) 823434dbbb3SRuslan Ermilov return (EINVAL); 824f889d2efSBrooks Davis *unit = (*unit * 10) + (*cp - '0'); 825f889d2efSBrooks Davis } 826f889d2efSBrooks Davis } 827f889d2efSBrooks Davis 828f889d2efSBrooks Davis return (0); 829f889d2efSBrooks Davis } 830f889d2efSBrooks Davis 831c64c1f95SAndriy Voskoboinyk static int 832c64c1f95SAndriy Voskoboinyk ifc_alloc_unit_specific(struct if_clone *ifc, int *unit) 833f889d2efSBrooks Davis { 8342e9fff5bSGleb Smirnoff char name[IFNAMSIZ]; 835f889d2efSBrooks Davis 8363932d760SGleb Smirnoff if (*unit > ifc->ifc_maxunit) 8373932d760SGleb Smirnoff return (ENOSPC); 838c64c1f95SAndriy Voskoboinyk 839c64c1f95SAndriy Voskoboinyk if (alloc_unr_specific(ifc->ifc_unrhdr, *unit) == -1) 8402e9fff5bSGleb Smirnoff return (EEXIST); 841f889d2efSBrooks Davis 8422e9fff5bSGleb Smirnoff snprintf(name, IFNAMSIZ, "%s%d", ifc->ifc_name, *unit); 8432e9fff5bSGleb Smirnoff if (ifunit(name) != NULL) { 8443932d760SGleb Smirnoff free_unr(ifc->ifc_unrhdr, *unit); 8452e9fff5bSGleb Smirnoff return (EEXIST); 846f889d2efSBrooks Davis } 847f889d2efSBrooks Davis 8482e9fff5bSGleb Smirnoff IF_CLONE_ADDREF(ifc); 849f889d2efSBrooks Davis 8502e9fff5bSGleb Smirnoff return (0); 851f889d2efSBrooks Davis } 852f889d2efSBrooks Davis 853c64c1f95SAndriy Voskoboinyk static int 854c64c1f95SAndriy Voskoboinyk ifc_alloc_unit_next(struct if_clone *ifc, int *unit) 855c64c1f95SAndriy Voskoboinyk { 856c64c1f95SAndriy Voskoboinyk int error; 857c64c1f95SAndriy Voskoboinyk 858c64c1f95SAndriy Voskoboinyk *unit = alloc_unr(ifc->ifc_unrhdr); 859c64c1f95SAndriy Voskoboinyk if (*unit == -1) 860c64c1f95SAndriy Voskoboinyk return (ENOSPC); 861c64c1f95SAndriy Voskoboinyk 862c64c1f95SAndriy Voskoboinyk free_unr(ifc->ifc_unrhdr, *unit); 863c64c1f95SAndriy Voskoboinyk for (;;) { 864c64c1f95SAndriy Voskoboinyk error = ifc_alloc_unit_specific(ifc, unit); 865c64c1f95SAndriy Voskoboinyk if (error != EEXIST) 866c64c1f95SAndriy Voskoboinyk break; 867c64c1f95SAndriy Voskoboinyk 868c64c1f95SAndriy Voskoboinyk (*unit)++; 869c64c1f95SAndriy Voskoboinyk } 870c64c1f95SAndriy Voskoboinyk 871c64c1f95SAndriy Voskoboinyk return (error); 872c64c1f95SAndriy Voskoboinyk } 873c64c1f95SAndriy Voskoboinyk 874c64c1f95SAndriy Voskoboinyk int 875c64c1f95SAndriy Voskoboinyk ifc_alloc_unit(struct if_clone *ifc, int *unit) 876c64c1f95SAndriy Voskoboinyk { 877c64c1f95SAndriy Voskoboinyk if (*unit < 0) 878c64c1f95SAndriy Voskoboinyk return (ifc_alloc_unit_next(ifc, unit)); 879c64c1f95SAndriy Voskoboinyk else 880c64c1f95SAndriy Voskoboinyk return (ifc_alloc_unit_specific(ifc, unit)); 881c64c1f95SAndriy Voskoboinyk } 882c64c1f95SAndriy Voskoboinyk 883f889d2efSBrooks Davis void 884f889d2efSBrooks Davis ifc_free_unit(struct if_clone *ifc, int unit) 885f889d2efSBrooks Davis { 886f889d2efSBrooks Davis 8872e9fff5bSGleb Smirnoff free_unr(ifc->ifc_unrhdr, unit); 8882e9fff5bSGleb Smirnoff IF_CLONE_REMREF(ifc); 889f889d2efSBrooks Davis } 890f889d2efSBrooks Davis 89142a58907SGleb Smirnoff static int 892f889d2efSBrooks Davis ifc_simple_match(struct if_clone *ifc, const char *name) 893f889d2efSBrooks Davis { 894f889d2efSBrooks Davis const char *cp; 895f889d2efSBrooks Davis int i; 896f889d2efSBrooks Davis 897f889d2efSBrooks Davis /* Match the name */ 898f889d2efSBrooks Davis for (cp = name, i = 0; i < strlen(ifc->ifc_name); i++, cp++) { 899f889d2efSBrooks Davis if (ifc->ifc_name[i] != *cp) 900f889d2efSBrooks Davis return (0); 901f889d2efSBrooks Davis } 902f889d2efSBrooks Davis 903f889d2efSBrooks Davis /* Make sure there's a unit number or nothing after the name */ 904f889d2efSBrooks Davis for (; *cp != '\0'; cp++) { 905f889d2efSBrooks Davis if (*cp < '0' || *cp > '9') 906f889d2efSBrooks Davis return (0); 907f889d2efSBrooks Davis } 908f889d2efSBrooks Davis 909f889d2efSBrooks Davis return (1); 910f889d2efSBrooks Davis } 911f889d2efSBrooks Davis 91242a58907SGleb Smirnoff static int 91309ee0fc0SAlexander V. Chernikov ifc_handle_unit(struct if_clone *ifc, char *name, size_t len, int *punit) 914f889d2efSBrooks Davis { 915f889d2efSBrooks Davis char *dp; 916f889d2efSBrooks Davis int wildcard; 917f889d2efSBrooks Davis int unit; 918f889d2efSBrooks Davis int err; 919f889d2efSBrooks Davis 920f889d2efSBrooks Davis err = ifc_name2unit(name, &unit); 921f889d2efSBrooks Davis if (err != 0) 922f889d2efSBrooks Davis return (err); 923f889d2efSBrooks Davis 924f889d2efSBrooks Davis wildcard = (unit < 0); 925f889d2efSBrooks Davis 926f889d2efSBrooks Davis err = ifc_alloc_unit(ifc, &unit); 927f889d2efSBrooks Davis if (err != 0) 928f889d2efSBrooks Davis return (err); 929f889d2efSBrooks Davis 930f889d2efSBrooks Davis /* In the wildcard case, we need to update the name. */ 931f889d2efSBrooks Davis if (wildcard) { 932f889d2efSBrooks Davis for (dp = name; *dp != '\0'; dp++); 933f889d2efSBrooks Davis if (snprintf(dp, len - (dp-name), "%d", unit) > 934f889d2efSBrooks Davis len - (dp-name) - 1) { 935f889d2efSBrooks Davis /* 936f889d2efSBrooks Davis * This can only be a programmer error and 937f889d2efSBrooks Davis * there's no straightforward way to recover if 938f889d2efSBrooks Davis * it happens. 939f889d2efSBrooks Davis */ 940f889d2efSBrooks Davis panic("if_clone_create(): interface name too long"); 941f889d2efSBrooks Davis } 942f889d2efSBrooks Davis } 94309ee0fc0SAlexander V. Chernikov *punit = unit; 944f889d2efSBrooks Davis 945f889d2efSBrooks Davis return (0); 946f889d2efSBrooks Davis } 947f889d2efSBrooks Davis 94809ee0fc0SAlexander V. Chernikov int 94909ee0fc0SAlexander V. Chernikov ifc_copyin(const struct ifc_data *ifd, void *target, size_t len) 950f889d2efSBrooks Davis { 95109ee0fc0SAlexander V. Chernikov if (ifd->params == NULL) 952f889d2efSBrooks Davis return (EINVAL); 953f889d2efSBrooks Davis 95409ee0fc0SAlexander V. Chernikov if (ifd->flags & IFC_F_SYSSPACE) { 95509ee0fc0SAlexander V. Chernikov memcpy(target, ifd->params, len); 956f889d2efSBrooks Davis return (0); 95709ee0fc0SAlexander V. Chernikov } else 95809ee0fc0SAlexander V. Chernikov return (copyin(ifd->params, target, len)); 959f889d2efSBrooks Davis } 96009f6ff4fSMatt Macy 96109f6ff4fSMatt Macy const char * 96209f6ff4fSMatt Macy ifc_name(struct if_clone *ifc) 96309f6ff4fSMatt Macy { 96409f6ff4fSMatt Macy return (ifc->ifc_name); 96509f6ff4fSMatt Macy } 96609f6ff4fSMatt Macy 96709f6ff4fSMatt Macy void 96809f6ff4fSMatt Macy ifc_flags_set(struct if_clone *ifc, int flags) 96909f6ff4fSMatt Macy { 97009f6ff4fSMatt Macy ifc->ifc_flags = flags; 97109f6ff4fSMatt Macy } 97209f6ff4fSMatt Macy 97309f6ff4fSMatt Macy int 97409f6ff4fSMatt Macy ifc_flags_get(struct if_clone *ifc) 97509f6ff4fSMatt Macy { 97609f6ff4fSMatt Macy return (ifc->ifc_flags); 97709f6ff4fSMatt Macy } 978