/* * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)if.c 8.3 (Berkeley) 1/4/94 * $FreeBSD: src/sys/net/if.c,v 1.185 2004/03/13 02:35:03 brooks Exp $ */ #include #include #include #include #include #include #include #include static LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners); static int if_cloners_count; MALLOC_DEFINE(M_CLONE, "clone", "interface cloning framework"); static int if_name2unit(const char *, int *); static bool if_clone_match(struct if_clone *, const char *); static struct if_clone *if_clone_lookup(const char *); static int if_clone_alloc_unit(struct if_clone *, int *); static void if_clone_free_unit(struct if_clone *, int); static int if_clone_createif(struct if_clone *, int, caddr_t); /* * Lookup the cloner and create a clone network interface. */ int if_clone_create(char *name, int len, caddr_t params) { struct if_clone *ifc; char ifname[IFNAMSIZ]; bool wildcard; int unit; int err; if ((ifc = if_clone_lookup(name)) == NULL) return (EINVAL); if ((err = if_name2unit(name, &unit)) != 0) return (err); wildcard = (unit < 0); if ((err = if_clone_alloc_unit(ifc, &unit)) != 0) return (err); ksnprintf(ifname, IFNAMSIZ, "%s%d", ifc->ifc_name, unit); /* * Update the name with the allocated unit for the caller, * who must preserve enough space. */ if (wildcard && strlcpy(name, ifname, len) >= len) return (ENOSPC); if ((err = if_clone_createif(ifc, unit, params)) != 0) { if_clone_free_unit(ifc, unit); return (err); } return (0); } /* * Lookup the cloner and destroy a clone network interface. */ int if_clone_destroy(const char *name) { struct if_clone *ifc; struct ifnet *ifp; int unit, error; ifnet_lock(); ifp = ifunit(name); ifnet_unlock(); if (ifp == NULL) return (ENXIO); if ((ifc = if_clone_lookup(ifp->if_dname)) == NULL) return (EINVAL); unit = ifp->if_dunit; if (unit < ifc->ifc_minifs) return (EINVAL); if (ifc->ifc_destroy == NULL) return (EOPNOTSUPP); ifnet_lock(); error = ifc->ifc_destroy(ifp); ifnet_unlock(); if (error) return (error); if_clone_free_unit(ifc, unit); return (0); } /* * Register a network interface cloner. */ int if_clone_attach(struct if_clone *ifc) { struct if_clone *ifct; int len, maxclone; int unit; LIST_FOREACH(ifct, &if_cloners, ifc_list) { if (strcmp(ifct->ifc_name, ifc->ifc_name) == 0) return (EEXIST); } KASSERT(ifc->ifc_minifs - 1 <= ifc->ifc_maxunit, ("%s: %s requested more units then allowed (%d > %d)", __func__, ifc->ifc_name, ifc->ifc_minifs, ifc->ifc_maxunit + 1)); /* * Compute bitmap size and allocate it. */ maxclone = ifc->ifc_maxunit + 1; len = maxclone >> 3; if ((len << 3) < maxclone) len++; ifc->ifc_units = kmalloc(len, M_CLONE, M_WAITOK | M_ZERO); ifc->ifc_bmlen = len; LIST_INSERT_HEAD(&if_cloners, ifc, ifc_list); if_cloners_count++; for (unit = 0; unit < ifc->ifc_minifs; unit++) { if_clone_alloc_unit(ifc, &unit); if (if_clone_createif(ifc, unit, NULL) != 0) { panic("%s: failed to create required interface %s%d", __func__, ifc->ifc_name, unit); } } EVENTHANDLER_INVOKE(if_clone_event, ifc); return (0); } /* * Unregister a network interface cloner. */ void if_clone_detach(struct if_clone *ifc) { LIST_REMOVE(ifc, ifc_list); kfree(ifc->ifc_units, M_CLONE); if_cloners_count--; } /* * Provide list of interface cloners to userspace. */ int if_clone_list(struct if_clonereq *ifcr) { char outbuf[IFNAMSIZ], *dst; struct if_clone *ifc; int count, error = 0; ifcr->ifcr_total = if_cloners_count; if ((dst = ifcr->ifcr_buffer) == NULL) { /* Just asking how many there are. */ return (0); } if (ifcr->ifcr_count < 0) return (EINVAL); count = (if_cloners_count < ifcr->ifcr_count) ? if_cloners_count : ifcr->ifcr_count; for (ifc = LIST_FIRST(&if_cloners); ifc != NULL && count != 0; ifc = LIST_NEXT(ifc, ifc_list), count--, dst += IFNAMSIZ) { strlcpy(outbuf, ifc->ifc_name, IFNAMSIZ); error = copyout(outbuf, dst, IFNAMSIZ); if (error) break; } return (error); } /* * Extract the unit number from interface name of the form "name###". * A unit of -1 is stored if the given name doesn't have a unit. * * Returns 0 on success and an error on failure. */ static int if_name2unit(const char *name, int *unit) { const char *cp; int cutoff = INT_MAX / 10; int cutlim = INT_MAX % 10; for (cp = name; *cp != '\0' && (*cp < '0' || *cp > '9'); cp++) ; if (*cp == '\0') { *unit = -1; } else if (cp[0] == '0' && cp[1] != '\0') { /* Disallow leading zeroes. */ return (EINVAL); } else { for (*unit = 0; *cp != '\0'; cp++) { if (*cp < '0' || *cp > '9') { /* Bogus unit number. */ return (EINVAL); } if (*unit > cutoff || (*unit == cutoff && *cp - '0' > cutlim)) return (EINVAL); *unit = (*unit * 10) + (*cp - '0'); } } return (0); } /* * Check whether the interface cloner matches the name. */ static bool if_clone_match(struct if_clone *ifc, const char *name) { const char *cp; int i; /* Match the name */ for (cp = name, i = 0; i < strlen(ifc->ifc_name); i++, cp++) { if (ifc->ifc_name[i] != *cp) return (false); } /* Make sure there's a unit number or nothing after the name */ for ( ; *cp != '\0'; cp++) { if (*cp < '0' || *cp > '9') return (false); } return (true); } /* * Look up a network interface cloner. */ static struct if_clone * if_clone_lookup(const char *name) { struct if_clone *ifc; LIST_FOREACH(ifc, &if_cloners, ifc_list) { if (if_clone_match(ifc, name)) return ifc; } return (NULL); } /* * Allocate a unit number. * * Returns 0 on success and an error on failure. */ static int if_clone_alloc_unit(struct if_clone *ifc, int *unit) { int bytoff, bitoff; if (*unit < 0) { /* * Wildcard mode: find a free unit. */ bytoff = bitoff = 0; while (bytoff < ifc->ifc_bmlen && ifc->ifc_units[bytoff] == 0xff) bytoff++; if (bytoff >= ifc->ifc_bmlen) return (ENOSPC); while ((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0) bitoff++; *unit = (bytoff << 3) + bitoff; } else { bytoff = *unit >> 3; bitoff = *unit - (bytoff << 3); } if (*unit > ifc->ifc_maxunit) return (ENXIO); /* * Allocate the unit in the bitmap. */ KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) == 0, ("%s: bit is already set", __func__)); ifc->ifc_units[bytoff] |= (1 << bitoff); return (0); } /* * Free an allocated unit number. */ static void if_clone_free_unit(struct if_clone *ifc, int unit) { int bytoff, bitoff; bytoff = unit >> 3; bitoff = unit - (bytoff << 3); KASSERT((ifc->ifc_units[bytoff] & (1 << bitoff)) != 0, ("%s: bit is already cleared", __func__)); ifc->ifc_units[bytoff] &= ~(1 << bitoff); } /* * Create a clone network interface. */ static int if_clone_createif(struct if_clone *ifc, int unit, caddr_t params) { struct ifnet *ifp; char ifname[IFNAMSIZ]; int err; ksnprintf(ifname, IFNAMSIZ, "%s%d", ifc->ifc_name, unit); ifnet_lock(); ifp = ifunit(ifname); ifnet_unlock(); if (ifp != NULL) return (EEXIST); err = (*ifc->ifc_create)(ifc, unit, params); if (err != 0) return (err); ifnet_lock(); ifp = ifunit(ifname); ifnet_unlock(); if (ifp == NULL) return (ENXIO); err = if_addgroup(ifp, ifc->ifc_name); if (err != 0) return (err); return (0); }