1139823Simp/*- 234649Swollman * Copyright 1998 Massachusetts Institute of Technology 334649Swollman * 434649Swollman * Permission to use, copy, modify, and distribute this software and 534649Swollman * its documentation for any purpose and without fee is hereby 634649Swollman * granted, provided that both the above copyright notice and this 734649Swollman * permission notice appear in all copies, that both the above 834649Swollman * copyright notice and this permission notice appear in all 934649Swollman * supporting documentation, and that the name of M.I.T. not be used 1034649Swollman * in advertising or publicity pertaining to distribution of the 1134649Swollman * software without specific, written prior permission. M.I.T. makes 1234649Swollman * no representations about the suitability of this software for any 1334649Swollman * purpose. It is provided "as is" without express or implied 1434649Swollman * warranty. 1534649Swollman * 1634649Swollman * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 1734649Swollman * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 1834649Swollman * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1934649Swollman * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 2034649Swollman * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2134649Swollman * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2234649Swollman * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2334649Swollman * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2434649Swollman * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2534649Swollman * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2634649Swollman * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2734649Swollman * SUCH DAMAGE. 2834649Swollman */ 2934649Swollman 3034649Swollman/* 3134649Swollman * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. 3234649Swollman * Might be extended some day to also handle IEEE 802.1p priority 3334649Swollman * tagging. This is sort of sneaky in the implementation, since 3434649Swollman * we need to pretend to be enough of an Ethernet implementation 3534649Swollman * to make arp work. The way we do this is by telling everyone 3634649Swollman * that we are an Ethernet, and then catch the packets that 37228089Sjhb * ether_output() sends to us via if_transmit(), rewrite them for 38228089Sjhb * use by the real outgoing interface, and ask it to send them. 3934649Swollman */ 4034649Swollman 41204156Syongari#include <sys/cdefs.h> 42204156Syongari__FBSDID("$FreeBSD$"); 43204156Syongari 44249628Soleg#include "opt_inet.h" 45155051Sglebius#include "opt_vlan.h" 4634649Swollman 4734649Swollman#include <sys/param.h> 4834649Swollman#include <sys/kernel.h> 49155051Sglebius#include <sys/lock.h> 5044763Swpaul#include <sys/malloc.h> 5134649Swollman#include <sys/mbuf.h> 5271862Speter#include <sys/module.h> 53155051Sglebius#include <sys/rwlock.h> 5444763Swpaul#include <sys/queue.h> 5534649Swollman#include <sys/socket.h> 5634649Swollman#include <sys/sockio.h> 5734649Swollman#include <sys/sysctl.h> 5834649Swollman#include <sys/systm.h> 59219819Sjeff#include <sys/sx.h> 6034649Swollman 6134649Swollman#include <net/bpf.h> 6234649Swollman#include <net/ethernet.h> 6334649Swollman#include <net/if.h> 64130933Sbrooks#include <net/if_clone.h> 6534649Swollman#include <net/if_dl.h> 6634649Swollman#include <net/if_types.h> 6734649Swollman#include <net/if_vlan_var.h> 68185571Sbz#include <net/vnet.h> 6934649Swollman 70249628Soleg#ifdef INET 71249628Soleg#include <netinet/in.h> 72249628Soleg#include <netinet/if_ether.h> 73249628Soleg#endif 74249628Soleg 75155051Sglebius#define VLAN_DEF_HWIDTH 4 76155114Syar#define VLAN_IFFLAGS (IFF_BROADCAST | IFF_MULTICAST) 7783115Sbrooks 78165662Syar#define UP_AND_RUNNING(ifp) \ 79165662Syar ((ifp)->if_flags & IFF_UP && (ifp)->if_drv_flags & IFF_DRV_RUNNING) 80165662Syar 81155051SglebiusLIST_HEAD(ifvlanhead, ifvlan); 82155051Sglebius 83155051Sglebiusstruct ifvlantrunk { 84155051Sglebius struct ifnet *parent; /* parent interface of this trunk */ 85155051Sglebius struct rwlock rw; 86155051Sglebius#ifdef VLAN_ARRAY 87159823Syar#define VLAN_ARRAY_SIZE (EVL_VLID_MASK + 1) 88159823Syar struct ifvlan *vlans[VLAN_ARRAY_SIZE]; /* static table */ 89155051Sglebius#else 90155051Sglebius struct ifvlanhead *hash; /* dynamic hash-list table */ 91155051Sglebius uint16_t hmask; 92155051Sglebius uint16_t hwidth; 93155051Sglebius#endif 94155051Sglebius int refcnt; 95155051Sglebius}; 96155051Sglebius 97106932Ssamstruct vlan_mc_entry { 98219819Sjeff struct sockaddr_dl mc_addr; 99106932Ssam SLIST_ENTRY(vlan_mc_entry) mc_entries; 100106932Ssam}; 101106932Ssam 102106932Ssamstruct ifvlan { 103155051Sglebius struct ifvlantrunk *ifv_trunk; 104147256Sbrooks struct ifnet *ifv_ifp; 105219819Sjeff void *ifv_cookie; 106155051Sglebius#define TRUNK(ifv) ((ifv)->ifv_trunk) 107155051Sglebius#define PARENT(ifv) ((ifv)->ifv_trunk->parent) 108150846Syar int ifv_pflags; /* special flags we have set on parent */ 109106932Ssam struct ifv_linkmib { 110106932Ssam int ifvm_encaplen; /* encapsulation length */ 111106932Ssam int ifvm_mtufudge; /* MTU fudged by this much */ 112106932Ssam int ifvm_mintu; /* min transmission unit */ 113161326Syar uint16_t ifvm_proto; /* encapsulation ethertype */ 114155051Sglebius uint16_t ifvm_tag; /* tag to apply on packets leaving if */ 115106932Ssam } ifv_mib; 116160019Syar SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead; 117167483Syar#ifndef VLAN_ARRAY 118106932Ssam LIST_ENTRY(ifvlan) ifv_list; 119167483Syar#endif 120106932Ssam}; 121161326Syar#define ifv_proto ifv_mib.ifvm_proto 122230026Srwatson#define ifv_vid ifv_mib.ifvm_tag 123106932Ssam#define ifv_encaplen ifv_mib.ifvm_encaplen 124106932Ssam#define ifv_mtufudge ifv_mib.ifvm_mtufudge 125106932Ssam#define ifv_mintu ifv_mib.ifvm_mintu 126106932Ssam 127155051Sglebius/* Special flags we should propagate to parent. */ 128150846Syarstatic struct { 129150846Syar int flag; 130150846Syar int (*func)(struct ifnet *, int); 131150846Syar} vlan_pflags[] = { 132150846Syar {IFF_PROMISC, ifpromisc}, 133150846Syar {IFF_ALLMULTI, if_allmulti}, 134150846Syar {0, NULL} 135150846Syar}; 136106932Ssam 13745451SwpaulSYSCTL_DECL(_net_link); 138227309Sedstatic SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, 139227309Sed "IEEE 802.1Q VLAN"); 140227309Sedstatic SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, 141227309Sed "for consistency"); 14234649Swollman 143161210Syarstatic int soft_pad = 0; 144161210SyarSYSCTL_INT(_net_link_vlan, OID_AUTO, soft_pad, CTLFLAG_RW, &soft_pad, 0, 145161210Syar "pad short frames before tagging"); 146161210Syar 147241610Sglebiusstatic const char vlanname[] = "vlan"; 148241610Sglebiusstatic MALLOC_DEFINE(M_VLAN, vlanname, "802.1Q Virtual LAN Interface"); 14934649Swollman 150159823Syarstatic eventhandler_tag ifdetach_tag; 151202588Sthompsastatic eventhandler_tag iflladdr_tag; 152159823Syar 153119780Ssam/* 154155051Sglebius * We have a global mutex, that is used to serialize configuration 155155051Sglebius * changes and isn't used in normal packet delivery. 156155051Sglebius * 157155051Sglebius * We also have a per-trunk rwlock, that is locked shared on packet 158155051Sglebius * processing and exclusive when configuration is changed. 159155051Sglebius * 160155051Sglebius * The VLAN_ARRAY substitutes the dynamic hash with a static array 161161603Sthompsa * with 4096 entries. In theory this can give a boost in processing, 162155051Sglebius * however on practice it does not. Probably this is because array 163155051Sglebius * is too big to fit into CPU cache. 164119780Ssam */ 165219819Sjeffstatic struct sx ifv_lock; 166219819Sjeff#define VLAN_LOCK_INIT() sx_init(&ifv_lock, "vlan_global") 167219819Sjeff#define VLAN_LOCK_DESTROY() sx_destroy(&ifv_lock) 168219819Sjeff#define VLAN_LOCK_ASSERT() sx_assert(&ifv_lock, SA_LOCKED) 169219819Sjeff#define VLAN_LOCK() sx_xlock(&ifv_lock) 170219819Sjeff#define VLAN_UNLOCK() sx_xunlock(&ifv_lock) 171241610Sglebius#define TRUNK_LOCK_INIT(trunk) rw_init(&(trunk)->rw, vlanname) 172155051Sglebius#define TRUNK_LOCK_DESTROY(trunk) rw_destroy(&(trunk)->rw) 173155051Sglebius#define TRUNK_LOCK(trunk) rw_wlock(&(trunk)->rw) 174155051Sglebius#define TRUNK_UNLOCK(trunk) rw_wunlock(&(trunk)->rw) 175155051Sglebius#define TRUNK_LOCK_ASSERT(trunk) rw_assert(&(trunk)->rw, RA_WLOCKED) 176155051Sglebius#define TRUNK_RLOCK(trunk) rw_rlock(&(trunk)->rw) 177155051Sglebius#define TRUNK_RUNLOCK(trunk) rw_runlock(&(trunk)->rw) 178155051Sglebius#define TRUNK_LOCK_RASSERT(trunk) rw_assert(&(trunk)->rw, RA_RLOCKED) 179119780Ssam 180155051Sglebius#ifndef VLAN_ARRAY 181155051Sglebiusstatic void vlan_inithash(struct ifvlantrunk *trunk); 182155051Sglebiusstatic void vlan_freehash(struct ifvlantrunk *trunk); 183155051Sglebiusstatic int vlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv); 184155051Sglebiusstatic int vlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv); 185155051Sglebiusstatic void vlan_growhash(struct ifvlantrunk *trunk, int howmuch); 186155051Sglebiusstatic __inline struct ifvlan * vlan_gethash(struct ifvlantrunk *trunk, 187230026Srwatson uint16_t vid); 188155051Sglebius#endif 189155051Sglebiusstatic void trunk_destroy(struct ifvlantrunk *trunk); 190155051Sglebius 191160019Syarstatic void vlan_init(void *foo); 192106932Ssamstatic void vlan_input(struct ifnet *ifp, struct mbuf *m); 19338482Swollmanstatic int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); 194228089Sjhbstatic void vlan_qflush(struct ifnet *ifp); 195150846Syarstatic int vlan_setflag(struct ifnet *ifp, int flag, int status, 196150846Syar int (*func)(struct ifnet *, int)); 197150846Syarstatic int vlan_setflags(struct ifnet *ifp, int status); 19844763Swpaulstatic int vlan_setmulti(struct ifnet *ifp); 199228089Sjhbstatic int vlan_transmit(struct ifnet *ifp, struct mbuf *m); 200208212Sjhbstatic void vlan_unconfig(struct ifnet *ifp); 201239440Sjhbstatic void vlan_unconfig_locked(struct ifnet *ifp, int departing); 202155051Sglebiusstatic int vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag); 203201350Sbrooksstatic void vlan_link_state(struct ifnet *ifp); 204155051Sglebiusstatic void vlan_capabilities(struct ifvlan *ifv); 205155051Sglebiusstatic void vlan_trunk_capabilities(struct ifnet *ifp); 20634649Swollman 207230026Srwatsonstatic struct ifnet *vlan_clone_match_ethervid(struct if_clone *, 208130933Sbrooks const char *, int *); 209130933Sbrooksstatic int vlan_clone_match(struct if_clone *, const char *); 210160195Ssamstatic int vlan_clone_create(struct if_clone *, char *, size_t, caddr_t); 211130933Sbrooksstatic int vlan_clone_destroy(struct if_clone *, struct ifnet *); 21283115Sbrooks 213159823Syarstatic void vlan_ifdetach(void *arg, struct ifnet *ifp); 214202588Sthompsastatic void vlan_iflladdr(void *arg, struct ifnet *ifp); 215159823Syar 216241610Sglebiusstatic struct if_clone *vlan_cloner; 217130933Sbrooks 218215726Szec#ifdef VIMAGE 219241610Sglebiusstatic VNET_DEFINE(struct if_clone *, vlan_cloner); 220215726Szec#define V_vlan_cloner VNET(vlan_cloner) 221215726Szec#endif 222215726Szec 223155051Sglebius#ifndef VLAN_ARRAY 224155051Sglebius#define HASH(n, m) ((((n) >> 8) ^ ((n) >> 4) ^ (n)) & (m)) 225160019Syar 226155051Sglebiusstatic void 227155051Sglebiusvlan_inithash(struct ifvlantrunk *trunk) 228155051Sglebius{ 229155051Sglebius int i, n; 230155051Sglebius 231155051Sglebius /* 232155051Sglebius * The trunk must not be locked here since we call malloc(M_WAITOK). 233155051Sglebius * It is OK in case this function is called before the trunk struct 234155051Sglebius * gets hooked up and becomes visible from other threads. 235155051Sglebius */ 236155051Sglebius 237155051Sglebius KASSERT(trunk->hwidth == 0 && trunk->hash == NULL, 238155051Sglebius ("%s: hash already initialized", __func__)); 239155051Sglebius 240155051Sglebius trunk->hwidth = VLAN_DEF_HWIDTH; 241155051Sglebius n = 1 << trunk->hwidth; 242155051Sglebius trunk->hmask = n - 1; 243155051Sglebius trunk->hash = malloc(sizeof(struct ifvlanhead) * n, M_VLAN, M_WAITOK); 244155051Sglebius for (i = 0; i < n; i++) 245155051Sglebius LIST_INIT(&trunk->hash[i]); 246155051Sglebius} 247155051Sglebius 248155051Sglebiusstatic void 249155051Sglebiusvlan_freehash(struct ifvlantrunk *trunk) 250155051Sglebius{ 251155051Sglebius#ifdef INVARIANTS 252155051Sglebius int i; 253155051Sglebius 254155051Sglebius KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__)); 255155051Sglebius for (i = 0; i < (1 << trunk->hwidth); i++) 256155051Sglebius KASSERT(LIST_EMPTY(&trunk->hash[i]), 257155051Sglebius ("%s: hash table not empty", __func__)); 258155051Sglebius#endif 259155051Sglebius free(trunk->hash, M_VLAN); 260155051Sglebius trunk->hash = NULL; 261155051Sglebius trunk->hwidth = trunk->hmask = 0; 262155051Sglebius} 263155051Sglebius 264155051Sglebiusstatic int 265155051Sglebiusvlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv) 266155051Sglebius{ 267155051Sglebius int i, b; 268155051Sglebius struct ifvlan *ifv2; 269155051Sglebius 270155051Sglebius TRUNK_LOCK_ASSERT(trunk); 271155051Sglebius KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__)); 272155051Sglebius 273155051Sglebius b = 1 << trunk->hwidth; 274230026Srwatson i = HASH(ifv->ifv_vid, trunk->hmask); 275155051Sglebius LIST_FOREACH(ifv2, &trunk->hash[i], ifv_list) 276230026Srwatson if (ifv->ifv_vid == ifv2->ifv_vid) 277155051Sglebius return (EEXIST); 278155051Sglebius 279155051Sglebius /* 280155051Sglebius * Grow the hash when the number of vlans exceeds half of the number of 281155051Sglebius * hash buckets squared. This will make the average linked-list length 282155051Sglebius * buckets/2. 283155051Sglebius */ 284155051Sglebius if (trunk->refcnt > (b * b) / 2) { 285155051Sglebius vlan_growhash(trunk, 1); 286230026Srwatson i = HASH(ifv->ifv_vid, trunk->hmask); 287155051Sglebius } 288155051Sglebius LIST_INSERT_HEAD(&trunk->hash[i], ifv, ifv_list); 289155051Sglebius trunk->refcnt++; 290155051Sglebius 291155051Sglebius return (0); 292155051Sglebius} 293155051Sglebius 294155051Sglebiusstatic int 295155051Sglebiusvlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv) 296155051Sglebius{ 297155051Sglebius int i, b; 298155051Sglebius struct ifvlan *ifv2; 299155051Sglebius 300155051Sglebius TRUNK_LOCK_ASSERT(trunk); 301155051Sglebius KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__)); 302155051Sglebius 303155051Sglebius b = 1 << trunk->hwidth; 304230026Srwatson i = HASH(ifv->ifv_vid, trunk->hmask); 305155051Sglebius LIST_FOREACH(ifv2, &trunk->hash[i], ifv_list) 306155051Sglebius if (ifv2 == ifv) { 307155051Sglebius trunk->refcnt--; 308155051Sglebius LIST_REMOVE(ifv2, ifv_list); 309155051Sglebius if (trunk->refcnt < (b * b) / 2) 310155051Sglebius vlan_growhash(trunk, -1); 311155051Sglebius return (0); 312155051Sglebius } 313155051Sglebius 314155051Sglebius panic("%s: vlan not found\n", __func__); 315155051Sglebius return (ENOENT); /*NOTREACHED*/ 316155051Sglebius} 317155051Sglebius 31844763Swpaul/* 319155051Sglebius * Grow the hash larger or smaller if memory permits. 320155051Sglebius */ 321155051Sglebiusstatic void 322155051Sglebiusvlan_growhash(struct ifvlantrunk *trunk, int howmuch) 323155051Sglebius{ 324155051Sglebius struct ifvlan *ifv; 325155051Sglebius struct ifvlanhead *hash2; 326155051Sglebius int hwidth2, i, j, n, n2; 327155051Sglebius 328155051Sglebius TRUNK_LOCK_ASSERT(trunk); 329155051Sglebius KASSERT(trunk->hwidth > 0, ("%s: hwidth not positive", __func__)); 330155051Sglebius 331155051Sglebius if (howmuch == 0) { 332155051Sglebius /* Harmless yet obvious coding error */ 333155051Sglebius printf("%s: howmuch is 0\n", __func__); 334155051Sglebius return; 335155051Sglebius } 336155051Sglebius 337155051Sglebius hwidth2 = trunk->hwidth + howmuch; 338155051Sglebius n = 1 << trunk->hwidth; 339155051Sglebius n2 = 1 << hwidth2; 340155051Sglebius /* Do not shrink the table below the default */ 341155051Sglebius if (hwidth2 < VLAN_DEF_HWIDTH) 342155051Sglebius return; 343155051Sglebius 344155051Sglebius /* M_NOWAIT because we're called with trunk mutex held */ 345155051Sglebius hash2 = malloc(sizeof(struct ifvlanhead) * n2, M_VLAN, M_NOWAIT); 346155051Sglebius if (hash2 == NULL) { 347155051Sglebius printf("%s: out of memory -- hash size not changed\n", 348155051Sglebius __func__); 349155051Sglebius return; /* We can live with the old hash table */ 350155051Sglebius } 351155051Sglebius for (j = 0; j < n2; j++) 352155051Sglebius LIST_INIT(&hash2[j]); 353155051Sglebius for (i = 0; i < n; i++) 354167483Syar while ((ifv = LIST_FIRST(&trunk->hash[i])) != NULL) { 355155051Sglebius LIST_REMOVE(ifv, ifv_list); 356230026Srwatson j = HASH(ifv->ifv_vid, n2 - 1); 357155051Sglebius LIST_INSERT_HEAD(&hash2[j], ifv, ifv_list); 358155051Sglebius } 359155051Sglebius free(trunk->hash, M_VLAN); 360155051Sglebius trunk->hash = hash2; 361155051Sglebius trunk->hwidth = hwidth2; 362155051Sglebius trunk->hmask = n2 - 1; 363167601Syar 364167601Syar if (bootverbose) 365167601Syar if_printf(trunk->parent, 366167601Syar "VLAN hash table resized from %d to %d buckets\n", n, n2); 367155051Sglebius} 368155051Sglebius 369155051Sglebiusstatic __inline struct ifvlan * 370230026Srwatsonvlan_gethash(struct ifvlantrunk *trunk, uint16_t vid) 371155051Sglebius{ 372155051Sglebius struct ifvlan *ifv; 373155051Sglebius 374155051Sglebius TRUNK_LOCK_RASSERT(trunk); 375155051Sglebius 376230026Srwatson LIST_FOREACH(ifv, &trunk->hash[HASH(vid, trunk->hmask)], ifv_list) 377230026Srwatson if (ifv->ifv_vid == vid) 378155051Sglebius return (ifv); 379155051Sglebius return (NULL); 380155051Sglebius} 381155051Sglebius 382155051Sglebius#if 0 383155051Sglebius/* Debugging code to view the hashtables. */ 384155051Sglebiusstatic void 385155051Sglebiusvlan_dumphash(struct ifvlantrunk *trunk) 386155051Sglebius{ 387155051Sglebius int i; 388155051Sglebius struct ifvlan *ifv; 389155051Sglebius 390155051Sglebius for (i = 0; i < (1 << trunk->hwidth); i++) { 391155051Sglebius printf("%d: ", i); 392155051Sglebius LIST_FOREACH(ifv, &trunk->hash[i], ifv_list) 393155051Sglebius printf("%s ", ifv->ifv_ifp->if_xname); 394155051Sglebius printf("\n"); 395155051Sglebius } 396155051Sglebius} 397155051Sglebius#endif /* 0 */ 398219819Sjeff#else 399219819Sjeff 400219819Sjeffstatic __inline struct ifvlan * 401230026Srwatsonvlan_gethash(struct ifvlantrunk *trunk, uint16_t vid) 402219819Sjeff{ 403219819Sjeff 404230026Srwatson return trunk->vlans[vid]; 405219819Sjeff} 406219819Sjeff 407219819Sjeffstatic __inline int 408219819Sjeffvlan_inshash(struct ifvlantrunk *trunk, struct ifvlan *ifv) 409219819Sjeff{ 410219819Sjeff 411230026Srwatson if (trunk->vlans[ifv->ifv_vid] != NULL) 412219819Sjeff return EEXIST; 413230026Srwatson trunk->vlans[ifv->ifv_vid] = ifv; 414219819Sjeff trunk->refcnt++; 415219819Sjeff 416219819Sjeff return (0); 417219819Sjeff} 418219819Sjeff 419219819Sjeffstatic __inline int 420219819Sjeffvlan_remhash(struct ifvlantrunk *trunk, struct ifvlan *ifv) 421219819Sjeff{ 422219819Sjeff 423230026Srwatson trunk->vlans[ifv->ifv_vid] = NULL; 424219819Sjeff trunk->refcnt--; 425219819Sjeff 426219819Sjeff return (0); 427219819Sjeff} 428219819Sjeff 429219819Sjeffstatic __inline void 430219819Sjeffvlan_freehash(struct ifvlantrunk *trunk) 431219819Sjeff{ 432219819Sjeff} 433219819Sjeff 434219819Sjeffstatic __inline void 435219819Sjeffvlan_inithash(struct ifvlantrunk *trunk) 436219819Sjeff{ 437219819Sjeff} 438219819Sjeff 439155051Sglebius#endif /* !VLAN_ARRAY */ 440155051Sglebius 441155051Sglebiusstatic void 442155051Sglebiustrunk_destroy(struct ifvlantrunk *trunk) 443155051Sglebius{ 444155051Sglebius VLAN_LOCK_ASSERT(); 445155051Sglebius 446155051Sglebius TRUNK_LOCK(trunk); 447155051Sglebius vlan_freehash(trunk); 448155986Syar trunk->parent->if_vlantrunk = NULL; 449155986Syar TRUNK_UNLOCK(trunk); 450155051Sglebius TRUNK_LOCK_DESTROY(trunk); 451155051Sglebius free(trunk, M_VLAN); 452155051Sglebius} 453155051Sglebius 454155051Sglebius/* 45544763Swpaul * Program our multicast filter. What we're actually doing is 45644763Swpaul * programming the multicast filter of the parent. This has the 45744763Swpaul * side effect of causing the parent interface to receive multicast 45844763Swpaul * traffic that it doesn't really want, which ends up being discarded 45944763Swpaul * later by the upper protocol layers. Unfortunately, there's no way 46044763Swpaul * to avoid this: there really is only one physical interface. 46144763Swpaul */ 46271862Speterstatic int 46371862Spetervlan_setmulti(struct ifnet *ifp) 46444763Swpaul{ 46544763Swpaul struct ifnet *ifp_p; 466270136Smav struct ifmultiaddr *ifma; 46744763Swpaul struct ifvlan *sc; 468167483Syar struct vlan_mc_entry *mc; 46944763Swpaul int error; 47044763Swpaul 47144763Swpaul /* Find the parent. */ 47244763Swpaul sc = ifp->if_softc; 473270136Smav TRUNK_LOCK_ASSERT(TRUNK(sc)); 474155051Sglebius ifp_p = PARENT(sc); 47544763Swpaul 476183550Szec CURVNET_SET_QUIET(ifp_p->if_vnet); 477183550Szec 47844763Swpaul /* First, remove any existing filter entries. */ 479167483Syar while ((mc = SLIST_FIRST(&sc->vlan_mc_listhead)) != NULL) { 48044763Swpaul SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); 481270136Smav (void)if_delmulti(ifp_p, (struct sockaddr *)&mc->mc_addr); 48283115Sbrooks free(mc, M_VLAN); 48344763Swpaul } 48444763Swpaul 48544763Swpaul /* Now program new ones. */ 486270136Smav IF_ADDR_WLOCK(ifp); 48772084Sphk TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 48844763Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 48944763Swpaul continue; 490131586Sbms mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_NOWAIT); 491270136Smav if (mc == NULL) { 492270136Smav IF_ADDR_WUNLOCK(ifp); 493131586Sbms return (ENOMEM); 494270136Smav } 495219819Sjeff bcopy(ifma->ifma_addr, &mc->mc_addr, ifma->ifma_addr->sa_len); 496219819Sjeff mc->mc_addr.sdl_index = ifp_p->if_index; 49744763Swpaul SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); 498270136Smav } 499270136Smav IF_ADDR_WUNLOCK(ifp); 500270136Smav SLIST_FOREACH (mc, &sc->vlan_mc_listhead, mc_entries) { 501219819Sjeff error = if_addmulti(ifp_p, (struct sockaddr *)&mc->mc_addr, 502270136Smav NULL); 50344763Swpaul if (error) 504131580Sbms return (error); 50544763Swpaul } 50644763Swpaul 507183550Szec CURVNET_RESTORE(); 508131580Sbms return (0); 50944763Swpaul} 51044763Swpaul 511106932Ssam/* 512202588Sthompsa * A handler for parent interface link layer address changes. 513202588Sthompsa * If the parent interface link layer address is changed we 514202588Sthompsa * should also change it on all children vlans. 515202588Sthompsa */ 516202588Sthompsastatic void 517202588Sthompsavlan_iflladdr(void *arg __unused, struct ifnet *ifp) 518202588Sthompsa{ 519202588Sthompsa struct ifvlan *ifv; 520202611Sthompsa#ifndef VLAN_ARRAY 521202611Sthompsa struct ifvlan *next; 522202611Sthompsa#endif 523202588Sthompsa int i; 524202588Sthompsa 525202588Sthompsa /* 526202588Sthompsa * Check if it's a trunk interface first of all 527202588Sthompsa * to avoid needless locking. 528202588Sthompsa */ 529202588Sthompsa if (ifp->if_vlantrunk == NULL) 530202588Sthompsa return; 531202588Sthompsa 532202588Sthompsa VLAN_LOCK(); 533202588Sthompsa /* 534202588Sthompsa * OK, it's a trunk. Loop over and change all vlan's lladdrs on it. 535202588Sthompsa */ 536202588Sthompsa#ifdef VLAN_ARRAY 537202588Sthompsa for (i = 0; i < VLAN_ARRAY_SIZE; i++) 538202611Sthompsa if ((ifv = ifp->if_vlantrunk->vlans[i])) { 539202588Sthompsa#else /* VLAN_ARRAY */ 540202588Sthompsa for (i = 0; i < (1 << ifp->if_vlantrunk->hwidth); i++) 541202611Sthompsa LIST_FOREACH_SAFE(ifv, &ifp->if_vlantrunk->hash[i], ifv_list, next) { 542202611Sthompsa#endif /* VLAN_ARRAY */ 543202611Sthompsa VLAN_UNLOCK(); 544219819Sjeff if_setlladdr(ifv->ifv_ifp, IF_LLADDR(ifp), 545219819Sjeff ifp->if_addrlen); 546202611Sthompsa VLAN_LOCK(); 547202611Sthompsa } 548202588Sthompsa VLAN_UNLOCK(); 549202588Sthompsa 550202588Sthompsa} 551202588Sthompsa 552202588Sthompsa/* 553159823Syar * A handler for network interface departure events. 554159823Syar * Track departure of trunks here so that we don't access invalid 555159823Syar * pointers or whatever if a trunk is ripped from under us, e.g., 556201196Sjhb * by ejecting its hot-plug card. However, if an ifnet is simply 557201196Sjhb * being renamed, then there's no need to tear down the state. 558159823Syar */ 559159823Syarstatic void 560159823Syarvlan_ifdetach(void *arg __unused, struct ifnet *ifp) 561159823Syar{ 562159823Syar struct ifvlan *ifv; 563159823Syar int i; 564159823Syar 565159823Syar /* 566159823Syar * Check if it's a trunk interface first of all 567159823Syar * to avoid needless locking. 568159823Syar */ 569159823Syar if (ifp->if_vlantrunk == NULL) 570159823Syar return; 571159823Syar 572201196Sjhb /* If the ifnet is just being renamed, don't do anything. */ 573201196Sjhb if (ifp->if_flags & IFF_RENAMING) 574201196Sjhb return; 575201196Sjhb 576159823Syar VLAN_LOCK(); 577159823Syar /* 578159823Syar * OK, it's a trunk. Loop over and detach all vlan's on it. 579159823Syar * Check trunk pointer after each vlan_unconfig() as it will 580159823Syar * free it and set to NULL after the last vlan was detached. 581159823Syar */ 582159823Syar#ifdef VLAN_ARRAY 583159823Syar for (i = 0; i < VLAN_ARRAY_SIZE; i++) 584159823Syar if ((ifv = ifp->if_vlantrunk->vlans[i])) { 585239440Sjhb vlan_unconfig_locked(ifv->ifv_ifp, 1); 586159823Syar if (ifp->if_vlantrunk == NULL) 587159823Syar break; 588159823Syar } 589159823Syar#else /* VLAN_ARRAY */ 590159823Syarrestart: 591159823Syar for (i = 0; i < (1 << ifp->if_vlantrunk->hwidth); i++) 592159823Syar if ((ifv = LIST_FIRST(&ifp->if_vlantrunk->hash[i]))) { 593239440Sjhb vlan_unconfig_locked(ifv->ifv_ifp, 1); 594159823Syar if (ifp->if_vlantrunk) 595159823Syar goto restart; /* trunk->hwidth can change */ 596159823Syar else 597159823Syar break; 598159823Syar } 599159823Syar#endif /* VLAN_ARRAY */ 600159823Syar /* Trunk should have been destroyed in vlan_unconfig(). */ 601159823Syar KASSERT(ifp->if_vlantrunk == NULL, ("%s: purge failed", __func__)); 602159823Syar VLAN_UNLOCK(); 603159823Syar} 604159823Syar 605159823Syar/* 606219819Sjeff * Return the trunk device for a virtual interface. 607219819Sjeff */ 608219819Sjeffstatic struct ifnet * 609219819Sjeffvlan_trunkdev(struct ifnet *ifp) 610219819Sjeff{ 611219819Sjeff struct ifvlan *ifv; 612219819Sjeff 613219819Sjeff if (ifp->if_type != IFT_L2VLAN) 614219819Sjeff return (NULL); 615219819Sjeff ifv = ifp->if_softc; 616219819Sjeff ifp = NULL; 617219819Sjeff VLAN_LOCK(); 618219819Sjeff if (ifv->ifv_trunk) 619219819Sjeff ifp = PARENT(ifv); 620219819Sjeff VLAN_UNLOCK(); 621219819Sjeff return (ifp); 622219819Sjeff} 623219819Sjeff 624219819Sjeff/* 625230026Srwatson * Return the 12-bit VLAN VID for this interface, for use by external 626230026Srwatson * components such as Infiniband. 627230026Srwatson * 628230026Srwatson * XXXRW: Note that the function name here is historical; it should be named 629230026Srwatson * vlan_vid(). 630219819Sjeff */ 631219819Sjeffstatic int 632230026Srwatsonvlan_tag(struct ifnet *ifp, uint16_t *vidp) 633219819Sjeff{ 634219819Sjeff struct ifvlan *ifv; 635219819Sjeff 636219819Sjeff if (ifp->if_type != IFT_L2VLAN) 637219819Sjeff return (EINVAL); 638219819Sjeff ifv = ifp->if_softc; 639230026Srwatson *vidp = ifv->ifv_vid; 640219819Sjeff return (0); 641219819Sjeff} 642219819Sjeff 643219819Sjeff/* 644219819Sjeff * Return a driver specific cookie for this interface. Synchronization 645219819Sjeff * with setcookie must be provided by the driver. 646219819Sjeff */ 647219819Sjeffstatic void * 648219819Sjeffvlan_cookie(struct ifnet *ifp) 649219819Sjeff{ 650219819Sjeff struct ifvlan *ifv; 651219819Sjeff 652219819Sjeff if (ifp->if_type != IFT_L2VLAN) 653219819Sjeff return (NULL); 654219819Sjeff ifv = ifp->if_softc; 655219819Sjeff return (ifv->ifv_cookie); 656219819Sjeff} 657219819Sjeff 658219819Sjeff/* 659219819Sjeff * Store a cookie in our softc that drivers can use to store driver 660219819Sjeff * private per-instance data in. 661219819Sjeff */ 662219819Sjeffstatic int 663219819Sjeffvlan_setcookie(struct ifnet *ifp, void *cookie) 664219819Sjeff{ 665219819Sjeff struct ifvlan *ifv; 666219819Sjeff 667219819Sjeff if (ifp->if_type != IFT_L2VLAN) 668219819Sjeff return (EINVAL); 669219819Sjeff ifv = ifp->if_softc; 670219819Sjeff ifv->ifv_cookie = cookie; 671219819Sjeff return (0); 672219819Sjeff} 673219819Sjeff 674219819Sjeff/* 675230026Srwatson * Return the vlan device present at the specific VID. 676219819Sjeff */ 677219819Sjeffstatic struct ifnet * 678230026Srwatsonvlan_devat(struct ifnet *ifp, uint16_t vid) 679219819Sjeff{ 680219819Sjeff struct ifvlantrunk *trunk; 681219819Sjeff struct ifvlan *ifv; 682219819Sjeff 683219819Sjeff trunk = ifp->if_vlantrunk; 684219819Sjeff if (trunk == NULL) 685219819Sjeff return (NULL); 686219819Sjeff ifp = NULL; 687219819Sjeff TRUNK_RLOCK(trunk); 688230026Srwatson ifv = vlan_gethash(trunk, vid); 689219819Sjeff if (ifv) 690219819Sjeff ifp = ifv->ifv_ifp; 691219819Sjeff TRUNK_RUNLOCK(trunk); 692219819Sjeff return (ifp); 693219819Sjeff} 694219819Sjeff 695219819Sjeff/* 696106932Ssam * VLAN support can be loaded as a module. The only place in the 697106932Ssam * system that's intimately aware of this is ether_input. We hook 698106932Ssam * into this code through vlan_input_p which is defined there and 699106932Ssam * set here. Noone else in the system should be aware of this so 700106932Ssam * we use an explicit reference here. 701106932Ssam */ 702106932Ssamextern void (*vlan_input_p)(struct ifnet *, struct mbuf *); 703106932Ssam 704145323Sglebius/* For if_link_state_change() eyes only... */ 705201350Sbrooksextern void (*vlan_link_state_p)(struct ifnet *); 706128871Sandre 70771862Speterstatic int 708131580Sbmsvlan_modevent(module_t mod, int type, void *data) 709131580Sbms{ 71083115Sbrooks 711131580Sbms switch (type) { 712131580Sbms case MOD_LOAD: 713159823Syar ifdetach_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, 714159823Syar vlan_ifdetach, NULL, EVENTHANDLER_PRI_ANY); 715159823Syar if (ifdetach_tag == NULL) 716159823Syar return (ENOMEM); 717202588Sthompsa iflladdr_tag = EVENTHANDLER_REGISTER(iflladdr_event, 718202588Sthompsa vlan_iflladdr, NULL, EVENTHANDLER_PRI_ANY); 719202588Sthompsa if (iflladdr_tag == NULL) 720202588Sthompsa return (ENOMEM); 721119780Ssam VLAN_LOCK_INIT(); 72283115Sbrooks vlan_input_p = vlan_input; 723128871Sandre vlan_link_state_p = vlan_link_state; 724155051Sglebius vlan_trunk_cap_p = vlan_trunk_capabilities; 725219819Sjeff vlan_trunkdev_p = vlan_trunkdev; 726219819Sjeff vlan_cookie_p = vlan_cookie; 727219819Sjeff vlan_setcookie_p = vlan_setcookie; 728219819Sjeff vlan_tag_p = vlan_tag; 729219819Sjeff vlan_devat_p = vlan_devat; 730215726Szec#ifndef VIMAGE 731241610Sglebius vlan_cloner = if_clone_advanced(vlanname, 0, vlan_clone_match, 732241610Sglebius vlan_clone_create, vlan_clone_destroy); 733215726Szec#endif 734167484Syar if (bootverbose) 735167484Syar printf("vlan: initialized, using " 736167484Syar#ifdef VLAN_ARRAY 737167484Syar "full-size arrays" 738167484Syar#else 739167484Syar "hash tables with chaining" 740167484Syar#endif 741167484Syar 742167484Syar "\n"); 743131580Sbms break; 744131580Sbms case MOD_UNLOAD: 745215726Szec#ifndef VIMAGE 746241610Sglebius if_clone_detach(vlan_cloner); 747215726Szec#endif 748159823Syar EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifdetach_tag); 749202588Sthompsa EVENTHANDLER_DEREGISTER(iflladdr_event, iflladdr_tag); 75083115Sbrooks vlan_input_p = NULL; 751128871Sandre vlan_link_state_p = NULL; 752155051Sglebius vlan_trunk_cap_p = NULL; 753219819Sjeff vlan_trunkdev_p = NULL; 754219819Sjeff vlan_tag_p = NULL; 755237263Snp vlan_cookie_p = NULL; 756237263Snp vlan_setcookie_p = NULL; 757219819Sjeff vlan_devat_p = NULL; 758119780Ssam VLAN_LOCK_DESTROY(); 759167484Syar if (bootverbose) 760167484Syar printf("vlan: unloaded\n"); 76183115Sbrooks break; 762132199Sphk default: 763132199Sphk return (EOPNOTSUPP); 764131580Sbms } 765131580Sbms return (0); 766131580Sbms} 76771862Speter 768131580Sbmsstatic moduledata_t vlan_mod = { 769131580Sbms "if_vlan", 770131580Sbms vlan_modevent, 771241394Skevlo 0 772131580Sbms}; 77371862Speter 77471862SpeterDECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 775155509SemasteMODULE_VERSION(if_vlan, 3); 77671862Speter 777215726Szec#ifdef VIMAGE 778215726Szecstatic void 779215726Szecvnet_vlan_init(const void *unused __unused) 780215726Szec{ 781215726Szec 782241610Sglebius vlan_cloner = if_clone_advanced(vlanname, 0, vlan_clone_match, 783241610Sglebius vlan_clone_create, vlan_clone_destroy); 784215726Szec V_vlan_cloner = vlan_cloner; 785215726Szec} 786215726SzecVNET_SYSINIT(vnet_vlan_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 787215726Szec vnet_vlan_init, NULL); 788215726Szec 789215726Szecstatic void 790215726Szecvnet_vlan_uninit(const void *unused __unused) 791215726Szec{ 792215726Szec 793241610Sglebius if_clone_detach(V_vlan_cloner); 794215726Szec} 795215726SzecVNET_SYSUNINIT(vnet_vlan_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_FIRST, 796215726Szec vnet_vlan_uninit, NULL); 797215726Szec#endif 798215726Szec 799130933Sbrooksstatic struct ifnet * 800230026Srwatsonvlan_clone_match_ethervid(struct if_clone *ifc, const char *name, int *vidp) 801130933Sbrooks{ 802130933Sbrooks const char *cp; 803130933Sbrooks struct ifnet *ifp; 804230026Srwatson int vid; 805130933Sbrooks 806130933Sbrooks /* Check for <etherif>.<vlan> style interface names. */ 807196481Srwatson IFNET_RLOCK_NOSLEEP(); 808181803Sbz TAILQ_FOREACH(ifp, &V_ifnet, if_link) { 809219819Sjeff /* 810219819Sjeff * We can handle non-ethernet hardware types as long as 811219819Sjeff * they handle the tagging and headers themselves. 812219819Sjeff */ 813219819Sjeff if (ifp->if_type != IFT_ETHER && 814219819Sjeff (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) 815130933Sbrooks continue; 816130933Sbrooks if (strncmp(ifp->if_xname, name, strlen(ifp->if_xname)) != 0) 817130933Sbrooks continue; 818130933Sbrooks cp = name + strlen(ifp->if_xname); 819201351Sjhb if (*cp++ != '.') 820130933Sbrooks continue; 821201351Sjhb if (*cp == '\0') 822201351Sjhb continue; 823230026Srwatson vid = 0; 824201351Sjhb for(; *cp >= '0' && *cp <= '9'; cp++) 825230026Srwatson vid = (vid * 10) + (*cp - '0'); 826201351Sjhb if (*cp != '\0') 827201351Sjhb continue; 828230026Srwatson if (vidp != NULL) 829230026Srwatson *vidp = vid; 830130933Sbrooks break; 831130933Sbrooks } 832196481Srwatson IFNET_RUNLOCK_NOSLEEP(); 833130933Sbrooks 834131580Sbms return (ifp); 835130933Sbrooks} 836130933Sbrooks 83783115Sbrooksstatic int 838130933Sbrooksvlan_clone_match(struct if_clone *ifc, const char *name) 83983115Sbrooks{ 840130933Sbrooks const char *cp; 841130933Sbrooks 842230026Srwatson if (vlan_clone_match_ethervid(ifc, name, NULL) != NULL) 843130933Sbrooks return (1); 844130933Sbrooks 845241610Sglebius if (strncmp(vlanname, name, strlen(vlanname)) != 0) 846130933Sbrooks return (0); 847130933Sbrooks for (cp = name + 4; *cp != '\0'; cp++) { 848130933Sbrooks if (*cp < '0' || *cp > '9') 849130933Sbrooks return (0); 850130933Sbrooks } 851130933Sbrooks 852130933Sbrooks return (1); 853130933Sbrooks} 854130933Sbrooks 855130933Sbrooksstatic int 856160195Ssamvlan_clone_create(struct if_clone *ifc, char *name, size_t len, caddr_t params) 857130933Sbrooks{ 858130933Sbrooks char *dp; 859130933Sbrooks int wildcard; 860130933Sbrooks int unit; 861130933Sbrooks int error; 862230026Srwatson int vid; 863130933Sbrooks int ethertag; 86483115Sbrooks struct ifvlan *ifv; 86583115Sbrooks struct ifnet *ifp; 866130933Sbrooks struct ifnet *p; 867210937Sjhb struct ifaddr *ifa; 868210937Sjhb struct sockaddr_dl *sdl; 869160195Ssam struct vlanreq vlr; 870167708Syar static const u_char eaddr[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ 87183115Sbrooks 872160195Ssam /* 873160195Ssam * There are 3 (ugh) ways to specify the cloned device: 874160195Ssam * o pass a parameter block with the clone request. 875160195Ssam * o specify parameters in the text of the clone device name 876160195Ssam * o specify no parameters and get an unattached device that 877160195Ssam * must be configured separately. 878160195Ssam * The first technique is preferred; the latter two are 879160195Ssam * supported for backwards compatibilty. 880230026Srwatson * 881230026Srwatson * XXXRW: Note historic use of the word "tag" here. New ioctls may be 882230026Srwatson * called for. 883160195Ssam */ 884160195Ssam if (params) { 885160195Ssam error = copyin(params, &vlr, sizeof(vlr)); 886160195Ssam if (error) 887160195Ssam return error; 888160195Ssam p = ifunit(vlr.vlr_parent); 889160195Ssam if (p == NULL) 890160195Ssam return ENXIO; 891160195Ssam /* 892230026Srwatson * Don't let the caller set up a VLAN VID with 893160195Ssam * anything except VLID bits. 894160195Ssam */ 895160195Ssam if (vlr.vlr_tag & ~EVL_VLID_MASK) 896160195Ssam return (EINVAL); 897160195Ssam error = ifc_name2unit(name, &unit); 898160195Ssam if (error != 0) 899160195Ssam return (error); 900160195Ssam 901130933Sbrooks ethertag = 1; 902230026Srwatson vid = vlr.vlr_tag; 903160195Ssam wildcard = (unit < 0); 904230026Srwatson } else if ((p = vlan_clone_match_ethervid(ifc, name, &vid)) != NULL) { 905160195Ssam ethertag = 1; 906130933Sbrooks unit = -1; 907130933Sbrooks wildcard = 0; 908130933Sbrooks 909130933Sbrooks /* 910230026Srwatson * Don't let the caller set up a VLAN VID with 911130933Sbrooks * anything except VLID bits. 912130933Sbrooks */ 913230026Srwatson if (vid & ~EVL_VLID_MASK) 914130933Sbrooks return (EINVAL); 915130933Sbrooks } else { 916130933Sbrooks ethertag = 0; 917130933Sbrooks 918130933Sbrooks error = ifc_name2unit(name, &unit); 919130933Sbrooks if (error != 0) 920130933Sbrooks return (error); 921130933Sbrooks 922130933Sbrooks wildcard = (unit < 0); 923130933Sbrooks } 924130933Sbrooks 925130933Sbrooks error = ifc_alloc_unit(ifc, &unit); 926130933Sbrooks if (error != 0) 927130933Sbrooks return (error); 928130933Sbrooks 929130933Sbrooks /* In the wildcard case, we need to update the name. */ 930130933Sbrooks if (wildcard) { 931130933Sbrooks for (dp = name; *dp != '\0'; dp++); 932130933Sbrooks if (snprintf(dp, len - (dp-name), "%d", unit) > 933130933Sbrooks len - (dp-name) - 1) { 934130933Sbrooks panic("%s: interface name too long", __func__); 935130933Sbrooks } 936130933Sbrooks } 937130933Sbrooks 938111119Simp ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO); 939147256Sbrooks ifp = ifv->ifv_ifp = if_alloc(IFT_ETHER); 940147256Sbrooks if (ifp == NULL) { 941147256Sbrooks ifc_free_unit(ifc, unit); 942147256Sbrooks free(ifv, M_VLAN); 943147256Sbrooks return (ENOSPC); 944147256Sbrooks } 94583115Sbrooks SLIST_INIT(&ifv->vlan_mc_listhead); 94683115Sbrooks 94783115Sbrooks ifp->if_softc = ifv; 948130933Sbrooks /* 949140745Syar * Set the name manually rather than using if_initname because 950130933Sbrooks * we don't conform to the default naming convention for interfaces. 951130933Sbrooks */ 952130933Sbrooks strlcpy(ifp->if_xname, name, IFNAMSIZ); 953241610Sglebius ifp->if_dname = vlanname; 954130933Sbrooks ifp->if_dunit = unit; 95583115Sbrooks /* NB: flags are not set here */ 95683115Sbrooks ifp->if_linkmib = &ifv->ifv_mib; 957131580Sbms ifp->if_linkmiblen = sizeof(ifv->ifv_mib); 95883115Sbrooks /* NB: mtu is not set here */ 95983115Sbrooks 960160019Syar ifp->if_init = vlan_init; 961228089Sjhb ifp->if_transmit = vlan_transmit; 962228089Sjhb ifp->if_qflush = vlan_qflush; 96383115Sbrooks ifp->if_ioctl = vlan_ioctl; 964155114Syar ifp->if_flags = VLAN_IFFLAGS; 965147256Sbrooks ether_ifattach(ifp, eaddr); 96683115Sbrooks /* Now undo some of the damage... */ 96785005Sfenner ifp->if_baudrate = 0; 968106932Ssam ifp->if_type = IFT_L2VLAN; 969106932Ssam ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN; 970210937Sjhb ifa = ifp->if_addr; 971210937Sjhb sdl = (struct sockaddr_dl *)ifa->ifa_addr; 972210937Sjhb sdl->sdl_type = IFT_L2VLAN; 97383115Sbrooks 974130933Sbrooks if (ethertag) { 975230026Srwatson error = vlan_config(ifv, p, vid); 976130933Sbrooks if (error != 0) { 977130933Sbrooks /* 978239440Sjhb * Since we've partially failed, we need to back 979130933Sbrooks * out all the way, otherwise userland could get 980130933Sbrooks * confused. Thus, we destroy the interface. 981130933Sbrooks */ 982160020Syar ether_ifdetach(ifp); 983130933Sbrooks vlan_unconfig(ifp); 984227459Sbrooks if_free(ifp); 985188575Smaxim ifc_free_unit(ifc, unit); 986130933Sbrooks free(ifv, M_VLAN); 987130933Sbrooks 988130933Sbrooks return (error); 989130933Sbrooks } 990130933Sbrooks 991150846Syar /* Update flags on the parent, if necessary. */ 992150846Syar vlan_setflags(ifp, 1); 993130933Sbrooks } 994130933Sbrooks 99583115Sbrooks return (0); 99683115Sbrooks} 99783115Sbrooks 998130933Sbrooksstatic int 999130933Sbrooksvlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) 100083115Sbrooks{ 100183115Sbrooks struct ifvlan *ifv = ifp->if_softc; 1002160019Syar int unit = ifp->if_dunit; 100383115Sbrooks 1004160020Syar ether_ifdetach(ifp); /* first, remove it from system-wide lists */ 1005160020Syar vlan_unconfig(ifp); /* now it can be unconfigured and freed */ 1006227459Sbrooks if_free(ifp); 100783115Sbrooks free(ifv, M_VLAN); 1008132557Sbrooks ifc_free_unit(ifc, unit); 1009132557Sbrooks 1010130933Sbrooks return (0); 101183115Sbrooks} 101283115Sbrooks 1013131580Sbms/* 1014131580Sbms * The ifp->if_init entry point for vlan(4) is a no-op. 1015131580Sbms */ 101683115Sbrooksstatic void 1017160019Syarvlan_init(void *foo __unused) 101834649Swollman{ 101934649Swollman} 102034649Swollman 1021152139Sglebius/* 1022228089Sjhb * The if_transmit method for vlan(4) interface. 1023152139Sglebius */ 1024228089Sjhbstatic int 1025228089Sjhbvlan_transmit(struct ifnet *ifp, struct mbuf *m) 102634649Swollman{ 102734649Swollman struct ifvlan *ifv; 102834649Swollman struct ifnet *p; 1029228967Syongari int error, len, mcast; 103034649Swollman 103134649Swollman ifv = ifp->if_softc; 1032155051Sglebius p = PARENT(ifv); 1033228967Syongari len = m->m_pkthdr.len; 1034228967Syongari mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1 : 0; 103534649Swollman 1036228089Sjhb BPF_MTAP(ifp, m); 103734649Swollman 1038228089Sjhb /* 1039228089Sjhb * Do not run parent's if_transmit() if the parent is not up, 1040228089Sjhb * or parent's driver will cause a system crash. 1041228089Sjhb */ 1042228089Sjhb if (!UP_AND_RUNNING(p)) { 1043228089Sjhb m_freem(m); 1044228967Syongari ifp->if_oerrors++; 1045251799Shrs return (ENETDOWN); 1046228089Sjhb } 104774943Syar 1048228089Sjhb /* 1049228089Sjhb * Pad the frame to the minimum size allowed if told to. 1050228089Sjhb * This option is in accord with IEEE Std 802.1Q, 2003 Ed., 1051228089Sjhb * paragraph C.4.4.3.b. It can help to work around buggy 1052228089Sjhb * bridges that violate paragraph C.4.4.3.a from the same 1053228089Sjhb * document, i.e., fail to pad short frames after untagging. 1054228089Sjhb * E.g., a tagged frame 66 bytes long (incl. FCS) is OK, but 1055228089Sjhb * untagging it will produce a 62-byte frame, which is a runt 1056228089Sjhb * and requires padding. There are VLAN-enabled network 1057228089Sjhb * devices that just discard such runts instead or mishandle 1058228089Sjhb * them somehow. 1059228089Sjhb */ 1060228089Sjhb if (soft_pad && p->if_type == IFT_ETHER) { 1061228089Sjhb static char pad[8]; /* just zeros */ 1062228089Sjhb int n; 1063161210Syar 1064228089Sjhb for (n = ETHERMIN + ETHER_HDR_LEN - m->m_pkthdr.len; 1065228089Sjhb n > 0; n -= sizeof(pad)) 1066228089Sjhb if (!m_append(m, min(n, sizeof(pad)), pad)) 1067228089Sjhb break; 1068161210Syar 1069228089Sjhb if (n > 0) { 1070228089Sjhb if_printf(ifp, "cannot pad short frame\n"); 1071228089Sjhb ifp->if_oerrors++; 1072228089Sjhb m_freem(m); 1073228089Sjhb return (0); 1074161210Syar } 1075228089Sjhb } 1076161210Syar 1077228089Sjhb /* 1078228089Sjhb * If underlying interface can do VLAN tag insertion itself, 1079228089Sjhb * just pass the packet along. However, we need some way to 1080228089Sjhb * tell the interface where the packet came from so that it 1081228089Sjhb * knows how to find the VLAN tag to use, so we attach a 1082228089Sjhb * packet tag that holds it. 1083228089Sjhb */ 1084228089Sjhb if (p->if_capenable & IFCAP_VLAN_HWTAGGING) { 1085230026Srwatson m->m_pkthdr.ether_vtag = ifv->ifv_vid; 1086228089Sjhb m->m_flags |= M_VLANTAG; 1087228089Sjhb } else { 1088230026Srwatson m = ether_vlanencap(m, ifv->ifv_vid); 1089228089Sjhb if (m == NULL) { 1090228089Sjhb if_printf(ifp, "unable to prepend VLAN header\n"); 1091228089Sjhb ifp->if_oerrors++; 1092228089Sjhb return (0); 109344763Swpaul } 1094228089Sjhb } 109544763Swpaul 1096228089Sjhb /* 1097228089Sjhb * Send it, precisely as ether_output() would have. 1098228089Sjhb */ 1099228089Sjhb error = (p->if_transmit)(p, m); 1100228967Syongari if (!error) { 1101228089Sjhb ifp->if_opackets++; 1102228967Syongari ifp->if_omcasts += mcast; 1103228967Syongari ifp->if_obytes += len; 1104228967Syongari } else 1105228089Sjhb ifp->if_oerrors++; 1106228089Sjhb return (error); 110734649Swollman} 110834649Swollman 1109228089Sjhb/* 1110228089Sjhb * The ifp->if_qflush entry point for vlan(4) is a no-op. 1111228089Sjhb */ 1112106932Ssamstatic void 1113228089Sjhbvlan_qflush(struct ifnet *ifp __unused) 1114228089Sjhb{ 1115228089Sjhb} 1116228089Sjhb 1117228089Sjhbstatic void 1118106932Ssamvlan_input(struct ifnet *ifp, struct mbuf *m) 111944763Swpaul{ 1120155051Sglebius struct ifvlantrunk *trunk = ifp->if_vlantrunk; 112144763Swpaul struct ifvlan *ifv; 1122230026Srwatson uint16_t vid; 112344763Swpaul 1124155051Sglebius KASSERT(trunk != NULL, ("%s: no trunk", __func__)); 1125155051Sglebius 1126150216Syar if (m->m_flags & M_VLANTAG) { 1127106932Ssam /* 1128150217Syar * Packet is tagged, but m contains a normal 1129106932Ssam * Ethernet frame; the tag is stored out-of-band. 1130106932Ssam */ 1131230026Srwatson vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag); 1132142069Sru m->m_flags &= ~M_VLANTAG; 1133106932Ssam } else { 1134155051Sglebius struct ether_vlan_header *evl; 1135155051Sglebius 1136150217Syar /* 1137150217Syar * Packet is tagged in-band as specified by 802.1q. 1138150217Syar */ 1139106932Ssam switch (ifp->if_type) { 1140106932Ssam case IFT_ETHER: 1141131580Sbms if (m->m_len < sizeof(*evl) && 1142131580Sbms (m = m_pullup(m, sizeof(*evl))) == NULL) { 1143106932Ssam if_printf(ifp, "cannot pullup VLAN header\n"); 1144106932Ssam return; 1145106932Ssam } 1146106932Ssam evl = mtod(m, struct ether_vlan_header *); 1147230026Srwatson vid = EVL_VLANOFTAG(ntohs(evl->evl_tag)); 1148160950Syar 1149160950Syar /* 1150165662Syar * Remove the 802.1q header by copying the Ethernet 1151165662Syar * addresses over it and adjusting the beginning of 1152165662Syar * the data in the mbuf. The encapsulated Ethernet 1153165662Syar * type field is already in place. 1154160950Syar */ 1155165662Syar bcopy((char *)evl, (char *)evl + ETHER_VLAN_ENCAP_LEN, 1156165662Syar ETHER_HDR_LEN - ETHER_TYPE_LEN); 1157165662Syar m_adj(m, ETHER_VLAN_ENCAP_LEN); 1158106932Ssam break; 1159165662Syar 1160106932Ssam default: 1161160950Syar#ifdef INVARIANTS 1162160951Syar panic("%s: %s has unsupported if_type %u", 1163160951Syar __func__, ifp->if_xname, ifp->if_type); 1164106932Ssam#endif 1165160951Syar m_freem(m); 1166160951Syar ifp->if_noproto++; 1167160951Syar return; 1168106932Ssam } 116991272Sbrooks } 117091272Sbrooks 1171159838Syar TRUNK_RLOCK(trunk); 1172230026Srwatson ifv = vlan_gethash(trunk, vid); 1173165662Syar if (ifv == NULL || !UP_AND_RUNNING(ifv->ifv_ifp)) { 1174155051Sglebius TRUNK_RUNLOCK(trunk); 1175155051Sglebius m_freem(m); 1176155051Sglebius ifp->if_noproto++; 1177155051Sglebius return; 1178155051Sglebius } 1179155051Sglebius TRUNK_RUNLOCK(trunk); 118044763Swpaul 1181147256Sbrooks m->m_pkthdr.rcvif = ifv->ifv_ifp; 1182147256Sbrooks ifv->ifv_ifp->if_ipackets++; 118334649Swollman 1184106932Ssam /* Pass it back through the parent's input routine. */ 1185147256Sbrooks (*ifp->if_input)(ifv->ifv_ifp, m); 118634649Swollman} 118734649Swollman 118834649Swollmanstatic int 1189230026Srwatsonvlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid) 119034649Swollman{ 1191155051Sglebius struct ifvlantrunk *trunk; 1192150846Syar struct ifnet *ifp; 1193155051Sglebius int error = 0; 119434649Swollman 1195155051Sglebius /* VID numbers 0x0 and 0xFFF are reserved */ 1196230026Srwatson if (vid == 0 || vid == 0xFFF) 1197155051Sglebius return (EINVAL); 1198219819Sjeff if (p->if_type != IFT_ETHER && 1199219819Sjeff (p->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) 1200131580Sbms return (EPROTONOSUPPORT); 1201155114Syar if ((p->if_flags & VLAN_IFFLAGS) != VLAN_IFFLAGS) 1202155114Syar return (EPROTONOSUPPORT); 1203155051Sglebius if (ifv->ifv_trunk) 1204131580Sbms return (EBUSY); 120534649Swollman 1206155051Sglebius if (p->if_vlantrunk == NULL) { 1207155051Sglebius trunk = malloc(sizeof(struct ifvlantrunk), 1208155051Sglebius M_VLAN, M_WAITOK | M_ZERO); 1209155231Sglebius vlan_inithash(trunk); 1210155051Sglebius VLAN_LOCK(); 1211155051Sglebius if (p->if_vlantrunk != NULL) { 1212155051Sglebius /* A race that that is very unlikely to be hit. */ 1213155231Sglebius vlan_freehash(trunk); 1214155051Sglebius free(trunk, M_VLAN); 1215155051Sglebius goto exists; 1216155051Sglebius } 1217155051Sglebius TRUNK_LOCK_INIT(trunk); 1218155051Sglebius TRUNK_LOCK(trunk); 1219155051Sglebius p->if_vlantrunk = trunk; 1220155051Sglebius trunk->parent = p; 1221155051Sglebius } else { 1222155051Sglebius VLAN_LOCK(); 1223155051Sglebiusexists: 1224155051Sglebius trunk = p->if_vlantrunk; 1225155051Sglebius TRUNK_LOCK(trunk); 1226155051Sglebius } 1227155051Sglebius 1228230026Srwatson ifv->ifv_vid = vid; /* must set this before vlan_inshash() */ 1229155051Sglebius error = vlan_inshash(trunk, ifv); 1230155051Sglebius if (error) 1231155051Sglebius goto done; 1232161326Syar ifv->ifv_proto = ETHERTYPE_VLAN; 1233106932Ssam ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; 1234106932Ssam ifv->ifv_mintu = ETHERMIN; 1235150846Syar ifv->ifv_pflags = 0; 1236106932Ssam 123734649Swollman /* 1238106932Ssam * If the parent supports the VLAN_MTU capability, 1239106932Ssam * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames, 1240129717Syar * use it. 1241106932Ssam */ 1242129717Syar if (p->if_capenable & IFCAP_VLAN_MTU) { 1243106932Ssam /* 1244129717Syar * No need to fudge the MTU since the parent can 1245129717Syar * handle extended frames. 1246106932Ssam */ 1247106932Ssam ifv->ifv_mtufudge = 0; 1248129717Syar } else { 1249106932Ssam /* 1250106932Ssam * Fudge the MTU by the encapsulation size. This 1251106932Ssam * makes us incompatible with strictly compliant 1252106932Ssam * 802.1Q implementations, but allows us to use 1253106932Ssam * the feature with other NetBSD implementations, 1254106932Ssam * which might still be useful. 1255106932Ssam */ 1256106932Ssam ifv->ifv_mtufudge = ifv->ifv_encaplen; 1257106932Ssam } 1258106932Ssam 1259155051Sglebius ifv->ifv_trunk = trunk; 1260150846Syar ifp = ifv->ifv_ifp; 1261219819Sjeff /* 1262219819Sjeff * Initialize fields from our parent. This duplicates some 1263219819Sjeff * work with ether_ifattach() but allows for non-ethernet 1264219819Sjeff * interfaces to also work. 1265219819Sjeff */ 1266150846Syar ifp->if_mtu = p->if_mtu - ifv->ifv_mtufudge; 1267155051Sglebius ifp->if_baudrate = p->if_baudrate; 1268219819Sjeff ifp->if_output = p->if_output; 1269219819Sjeff ifp->if_input = p->if_input; 1270219819Sjeff ifp->if_resolvemulti = p->if_resolvemulti; 1271219819Sjeff ifp->if_addrlen = p->if_addrlen; 1272219819Sjeff ifp->if_broadcastaddr = p->if_broadcastaddr; 1273219819Sjeff 1274106932Ssam /* 127574943Syar * Copy only a selected subset of flags from the parent. 127674943Syar * Other flags are none of our business. 127734649Swollman */ 1278155114Syar#define VLAN_COPY_FLAGS (IFF_SIMPLEX) 1279150846Syar ifp->if_flags &= ~VLAN_COPY_FLAGS; 1280150846Syar ifp->if_flags |= p->if_flags & VLAN_COPY_FLAGS; 1281150846Syar#undef VLAN_COPY_FLAGS 128234649Swollman 1283150846Syar ifp->if_link_state = p->if_link_state; 1284150846Syar 1285155051Sglebius vlan_capabilities(ifv); 1286106932Ssam 1287106932Ssam /* 1288219819Sjeff * Set up our interface address to reflect the underlying 128934649Swollman * physical interface's. 129034649Swollman */ 1291219819Sjeff bcopy(IF_LLADDR(p), IF_LLADDR(ifp), p->if_addrlen); 1292219819Sjeff ((struct sockaddr_dl *)ifp->if_addr->ifa_addr)->sdl_alen = 1293219819Sjeff p->if_addrlen; 129480296Sfenner 129580296Sfenner /* 129680296Sfenner * Configure multicast addresses that may already be 129780296Sfenner * joined on the vlan device. 129880296Sfenner */ 1299150846Syar (void)vlan_setmulti(ifp); /* XXX: VLAN lock held */ 1300161321Syar 1301161321Syar /* We are ready for operation now. */ 1302161321Syar ifp->if_drv_flags |= IFF_DRV_RUNNING; 1303155051Sglebiusdone: 1304155051Sglebius TRUNK_UNLOCK(trunk); 1305180511Sjfv if (error == 0) 1306230026Srwatson EVENTHANDLER_INVOKE(vlan_config, p, ifv->ifv_vid); 1307155051Sglebius VLAN_UNLOCK(); 1308155051Sglebius 1309155051Sglebius return (error); 131034649Swollman} 131134649Swollman 1312208212Sjhbstatic void 131344763Swpaulvlan_unconfig(struct ifnet *ifp) 131444763Swpaul{ 1315159823Syar 1316159823Syar VLAN_LOCK(); 1317239440Sjhb vlan_unconfig_locked(ifp, 0); 1318159823Syar VLAN_UNLOCK(); 1319159823Syar} 1320159823Syar 1321208212Sjhbstatic void 1322239440Sjhbvlan_unconfig_locked(struct ifnet *ifp, int departing) 1323159823Syar{ 1324155051Sglebius struct ifvlantrunk *trunk; 132544763Swpaul struct vlan_mc_entry *mc; 132644763Swpaul struct ifvlan *ifv; 1327180511Sjfv struct ifnet *parent; 1328239440Sjhb int error; 132944763Swpaul 1330159823Syar VLAN_LOCK_ASSERT(); 1331119780Ssam 133244763Swpaul ifv = ifp->if_softc; 1333155051Sglebius trunk = ifv->ifv_trunk; 1334182413Sjfv parent = NULL; 133544763Swpaul 1336182413Sjfv if (trunk != NULL) { 133744763Swpaul 1338155051Sglebius TRUNK_LOCK(trunk); 1339182413Sjfv parent = trunk->parent; 1340155051Sglebius 134180296Sfenner /* 134280296Sfenner * Since the interface is being unconfigured, we need to 134380296Sfenner * empty the list of multicast groups that we may have joined 134480296Sfenner * while we were alive from the parent's list. 134580296Sfenner */ 1346167483Syar while ((mc = SLIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) { 1347208212Sjhb /* 1348239440Sjhb * If the parent interface is being detached, 1349239519Sjhb * all its multicast addresses have already 1350239440Sjhb * been removed. Warn about errors if 1351239440Sjhb * if_delmulti() does fail, but don't abort as 1352239440Sjhb * all callers expect vlan destruction to 1353239440Sjhb * succeed. 1354208212Sjhb */ 1355239440Sjhb if (!departing) { 1356239440Sjhb error = if_delmulti(parent, 1357239440Sjhb (struct sockaddr *)&mc->mc_addr); 1358239440Sjhb if (error) 1359239440Sjhb if_printf(ifp, 1360239440Sjhb "Failed to delete multicast address from parent: %d\n", 1361239440Sjhb error); 1362239440Sjhb } 136380296Sfenner SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); 136483115Sbrooks free(mc, M_VLAN); 136580296Sfenner } 1366106932Ssam 1367150846Syar vlan_setflags(ifp, 0); /* clear special flags on parent */ 1368155051Sglebius vlan_remhash(trunk, ifv); 1369155051Sglebius ifv->ifv_trunk = NULL; 1370155051Sglebius 1371155051Sglebius /* 1372155051Sglebius * Check if we were the last. 1373155051Sglebius */ 1374155051Sglebius if (trunk->refcnt == 0) { 1375270136Smav parent->if_vlantrunk = NULL; 1376155051Sglebius /* 1377155051Sglebius * XXXGL: If some ithread has already entered 1378155051Sglebius * vlan_input() and is now blocked on the trunk 1379155051Sglebius * lock, then it should preempt us right after 1380155051Sglebius * unlock and finish its work. Then we will acquire 1381155051Sglebius * lock again in trunk_destroy(). 1382155051Sglebius */ 1383155051Sglebius TRUNK_UNLOCK(trunk); 1384155051Sglebius trunk_destroy(trunk); 1385155051Sglebius } else 1386155051Sglebius TRUNK_UNLOCK(trunk); 138744763Swpaul } 138844763Swpaul 138944763Swpaul /* Disconnect from parent. */ 1390150846Syar if (ifv->ifv_pflags) 1391150846Syar if_printf(ifp, "%s: ifv_pflags unclean\n", __func__); 1392159823Syar ifp->if_mtu = ETHERMTU; 1393159823Syar ifp->if_link_state = LINK_STATE_UNKNOWN; 1394159823Syar ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 139544763Swpaul 1396182413Sjfv /* 1397182413Sjfv * Only dispatch an event if vlan was 1398182413Sjfv * attached, otherwise there is nothing 1399182413Sjfv * to cleanup anyway. 1400182413Sjfv */ 1401182413Sjfv if (parent != NULL) 1402230026Srwatson EVENTHANDLER_INVOKE(vlan_unconfig, parent, ifv->ifv_vid); 140344763Swpaul} 140444763Swpaul 1405150846Syar/* Handle a reference counted flag that should be set on the parent as well */ 140644763Swpaulstatic int 1407150846Syarvlan_setflag(struct ifnet *ifp, int flag, int status, 1408150846Syar int (*func)(struct ifnet *, int)) 1409106932Ssam{ 1410150846Syar struct ifvlan *ifv; 1411150846Syar int error; 1412106932Ssam 1413150846Syar /* XXX VLAN_LOCK_ASSERT(); */ 1414150846Syar 1415150846Syar ifv = ifp->if_softc; 1416150846Syar status = status ? (ifp->if_flags & flag) : 0; 1417150846Syar /* Now "status" contains the flag value or 0 */ 1418150846Syar 1419150846Syar /* 1420150846Syar * See if recorded parent's status is different from what 1421150846Syar * we want it to be. If it is, flip it. We record parent's 1422150846Syar * status in ifv_pflags so that we won't clear parent's flag 1423150846Syar * we haven't set. In fact, we don't clear or set parent's 1424150846Syar * flags directly, but get or release references to them. 1425150846Syar * That's why we can be sure that recorded flags still are 1426150846Syar * in accord with actual parent's flags. 1427150846Syar */ 1428150846Syar if (status != (ifv->ifv_pflags & flag)) { 1429155051Sglebius error = (*func)(PARENT(ifv), status); 1430150846Syar if (error) 1431150846Syar return (error); 1432150846Syar ifv->ifv_pflags &= ~flag; 1433150846Syar ifv->ifv_pflags |= status; 1434106932Ssam } 1435150846Syar return (0); 1436150846Syar} 1437106932Ssam 1438150846Syar/* 1439150846Syar * Handle IFF_* flags that require certain changes on the parent: 1440150846Syar * if "status" is true, update parent's flags respective to our if_flags; 1441150846Syar * if "status" is false, forcedly clear the flags set on parent. 1442150846Syar */ 1443150846Syarstatic int 1444150846Syarvlan_setflags(struct ifnet *ifp, int status) 1445150846Syar{ 1446150846Syar int error, i; 1447150846Syar 1448150846Syar for (i = 0; vlan_pflags[i].flag; i++) { 1449150846Syar error = vlan_setflag(ifp, vlan_pflags[i].flag, 1450150846Syar status, vlan_pflags[i].func); 1451150846Syar if (error) 1452150846Syar return (error); 1453150846Syar } 1454150846Syar return (0); 1455106932Ssam} 1456106932Ssam 1457128871Sandre/* Inform all vlans that their parent has changed link state */ 1458128871Sandrestatic void 1459201350Sbrooksvlan_link_state(struct ifnet *ifp) 1460128871Sandre{ 1461155051Sglebius struct ifvlantrunk *trunk = ifp->if_vlantrunk; 1462128871Sandre struct ifvlan *ifv; 1463155051Sglebius int i; 1464128871Sandre 1465155051Sglebius TRUNK_LOCK(trunk); 1466155051Sglebius#ifdef VLAN_ARRAY 1467159838Syar for (i = 0; i < VLAN_ARRAY_SIZE; i++) 1468155051Sglebius if (trunk->vlans[i] != NULL) { 1469155051Sglebius ifv = trunk->vlans[i]; 1470155051Sglebius#else 1471163232Sglebius for (i = 0; i < (1 << trunk->hwidth); i++) 1472163232Sglebius LIST_FOREACH(ifv, &trunk->hash[i], ifv_list) { 1473155051Sglebius#endif 1474163232Sglebius ifv->ifv_ifp->if_baudrate = trunk->parent->if_baudrate; 1475147256Sbrooks if_link_state_change(ifv->ifv_ifp, 1476155051Sglebius trunk->parent->if_link_state); 1477163232Sglebius } 1478155051Sglebius TRUNK_UNLOCK(trunk); 1479128871Sandre} 1480128871Sandre 1481155051Sglebiusstatic void 1482155051Sglebiusvlan_capabilities(struct ifvlan *ifv) 1483155051Sglebius{ 1484155051Sglebius struct ifnet *p = PARENT(ifv); 1485155051Sglebius struct ifnet *ifp = ifv->ifv_ifp; 1486155051Sglebius 1487155051Sglebius TRUNK_LOCK_ASSERT(TRUNK(ifv)); 1488155051Sglebius 1489155051Sglebius /* 1490155051Sglebius * If the parent interface can do checksum offloading 1491155051Sglebius * on VLANs, then propagate its hardware-assisted 1492155051Sglebius * checksumming flags. Also assert that checksum 1493155051Sglebius * offloading requires hardware VLAN tagging. 1494155051Sglebius */ 1495155051Sglebius if (p->if_capabilities & IFCAP_VLAN_HWCSUM) 1496155051Sglebius ifp->if_capabilities = p->if_capabilities & IFCAP_HWCSUM; 1497155051Sglebius 1498155051Sglebius if (p->if_capenable & IFCAP_VLAN_HWCSUM && 1499155051Sglebius p->if_capenable & IFCAP_VLAN_HWTAGGING) { 1500155051Sglebius ifp->if_capenable = p->if_capenable & IFCAP_HWCSUM; 1501204149Syongari ifp->if_hwassist = p->if_hwassist & (CSUM_IP | CSUM_TCP | 1502243624Sandre CSUM_UDP | CSUM_SCTP | CSUM_FRAGMENT); 1503155051Sglebius } else { 1504155051Sglebius ifp->if_capenable = 0; 1505155051Sglebius ifp->if_hwassist = 0; 1506155051Sglebius } 1507204149Syongari /* 1508204149Syongari * If the parent interface can do TSO on VLANs then 1509204149Syongari * propagate the hardware-assisted flag. TSO on VLANs 1510204149Syongari * does not necessarily require hardware VLAN tagging. 1511204149Syongari */ 1512265413Srmacklem if (p->if_hw_tsomax > 0) 1513265413Srmacklem ifp->if_hw_tsomax = p->if_hw_tsomax; 1514204149Syongari if (p->if_capabilities & IFCAP_VLAN_HWTSO) 1515204149Syongari ifp->if_capabilities |= p->if_capabilities & IFCAP_TSO; 1516204149Syongari if (p->if_capenable & IFCAP_VLAN_HWTSO) { 1517204149Syongari ifp->if_capenable |= p->if_capenable & IFCAP_TSO; 1518204149Syongari ifp->if_hwassist |= p->if_hwassist & CSUM_TSO; 1519204149Syongari } else { 1520204149Syongari ifp->if_capenable &= ~(p->if_capenable & IFCAP_TSO); 1521204149Syongari ifp->if_hwassist &= ~(p->if_hwassist & CSUM_TSO); 1522204149Syongari } 1523237263Snp 1524237263Snp /* 1525237263Snp * If the parent interface can offload TCP connections over VLANs then 1526237263Snp * propagate its TOE capability to the VLAN interface. 1527237263Snp * 1528237263Snp * All TOE drivers in the tree today can deal with VLANs. If this 1529237263Snp * changes then IFCAP_VLAN_TOE should be promoted to a full capability 1530237263Snp * with its own bit. 1531237263Snp */ 1532237263Snp#define IFCAP_VLAN_TOE IFCAP_TOE 1533237263Snp if (p->if_capabilities & IFCAP_VLAN_TOE) 1534237263Snp ifp->if_capabilities |= p->if_capabilities & IFCAP_TOE; 1535237263Snp if (p->if_capenable & IFCAP_VLAN_TOE) { 1536237263Snp TOEDEV(ifp) = TOEDEV(p); 1537237263Snp ifp->if_capenable |= p->if_capenable & IFCAP_TOE; 1538237263Snp } 1539155051Sglebius} 1540155051Sglebius 1541155051Sglebiusstatic void 1542155051Sglebiusvlan_trunk_capabilities(struct ifnet *ifp) 1543155051Sglebius{ 1544155051Sglebius struct ifvlantrunk *trunk = ifp->if_vlantrunk; 1545155051Sglebius struct ifvlan *ifv; 1546155051Sglebius int i; 1547155051Sglebius 1548155051Sglebius TRUNK_LOCK(trunk); 1549155051Sglebius#ifdef VLAN_ARRAY 1550159838Syar for (i = 0; i < VLAN_ARRAY_SIZE; i++) 1551155051Sglebius if (trunk->vlans[i] != NULL) { 1552155051Sglebius ifv = trunk->vlans[i]; 1553155051Sglebius#else 1554155051Sglebius for (i = 0; i < (1 << trunk->hwidth); i++) { 1555155051Sglebius LIST_FOREACH(ifv, &trunk->hash[i], ifv_list) 1556155051Sglebius#endif 1557155051Sglebius vlan_capabilities(ifv); 1558155051Sglebius } 1559155051Sglebius TRUNK_UNLOCK(trunk); 1560155051Sglebius} 1561155051Sglebius 1562106932Ssamstatic int 156338482Swollmanvlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 156434649Swollman{ 156534649Swollman struct ifnet *p; 156634649Swollman struct ifreq *ifr; 1567219819Sjeff struct ifaddr *ifa; 156834649Swollman struct ifvlan *ifv; 1569270136Smav struct ifvlantrunk *trunk; 157034649Swollman struct vlanreq vlr; 157134649Swollman int error = 0; 157234649Swollman 157334649Swollman ifr = (struct ifreq *)data; 1574219819Sjeff ifa = (struct ifaddr *) data; 157534649Swollman ifv = ifp->if_softc; 157634649Swollman 157734649Swollman switch (cmd) { 1578219819Sjeff case SIOCSIFADDR: 1579219819Sjeff ifp->if_flags |= IFF_UP; 1580219819Sjeff#ifdef INET 1581219819Sjeff if (ifa->ifa_addr->sa_family == AF_INET) 1582219819Sjeff arp_ifinit(ifp, ifa); 1583219819Sjeff#endif 1584219819Sjeff break; 1585219819Sjeff case SIOCGIFADDR: 1586219819Sjeff { 1587219819Sjeff struct sockaddr *sa; 1588219819Sjeff 1589219819Sjeff sa = (struct sockaddr *)&ifr->ifr_data; 1590219819Sjeff bcopy(IF_LLADDR(ifp), sa->sa_data, ifp->if_addrlen); 1591219819Sjeff } 1592219819Sjeff break; 1593109711Sfenner case SIOCGIFMEDIA: 1594119780Ssam VLAN_LOCK(); 1595155051Sglebius if (TRUNK(ifv) != NULL) { 1596205411Semaste p = PARENT(ifv); 1597119780Ssam VLAN_UNLOCK(); 1598205411Semaste error = (*p->if_ioctl)(p, SIOCGIFMEDIA, data); 1599109711Sfenner /* Limit the result to the parent's current config. */ 1600109711Sfenner if (error == 0) { 1601109711Sfenner struct ifmediareq *ifmr; 1602109711Sfenner 1603131580Sbms ifmr = (struct ifmediareq *)data; 1604109711Sfenner if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) { 1605109711Sfenner ifmr->ifm_count = 1; 1606109711Sfenner error = copyout(&ifmr->ifm_current, 1607131580Sbms ifmr->ifm_ulist, 1608109711Sfenner sizeof(int)); 1609109711Sfenner } 1610109711Sfenner } 1611119780Ssam } else { 1612119780Ssam VLAN_UNLOCK(); 1613109711Sfenner error = EINVAL; 1614119780Ssam } 1615109711Sfenner break; 1616109711Sfenner 1617109711Sfenner case SIOCSIFMEDIA: 1618109711Sfenner error = EINVAL; 1619109711Sfenner break; 1620109711Sfenner 162134649Swollman case SIOCSIFMTU: 162234649Swollman /* 162334649Swollman * Set the interface MTU. 162434649Swollman */ 1625119780Ssam VLAN_LOCK(); 1626155051Sglebius if (TRUNK(ifv) != NULL) { 1627106932Ssam if (ifr->ifr_mtu > 1628155051Sglebius (PARENT(ifv)->if_mtu - ifv->ifv_mtufudge) || 1629106932Ssam ifr->ifr_mtu < 1630106932Ssam (ifv->ifv_mintu - ifv->ifv_mtufudge)) 1631106932Ssam error = EINVAL; 1632106932Ssam else 1633106932Ssam ifp->if_mtu = ifr->ifr_mtu; 1634106932Ssam } else 163534649Swollman error = EINVAL; 1636119780Ssam VLAN_UNLOCK(); 163734649Swollman break; 163834649Swollman 163934649Swollman case SIOCSETVLAN: 1640215726Szec#ifdef VIMAGE 1641229586Srwatson /* 1642229586Srwatson * XXXRW/XXXBZ: The goal in these checks is to allow a VLAN 1643229586Srwatson * interface to be delegated to a jail without allowing the 1644229586Srwatson * jail to change what underlying interface/VID it is 1645229586Srwatson * associated with. We are not entirely convinced that this 1646229587Srwatson * is the right way to accomplish that policy goal. 1647229586Srwatson */ 1648215726Szec if (ifp->if_vnet != ifp->if_home_vnet) { 1649215726Szec error = EPERM; 1650215726Szec break; 1651215726Szec } 1652215726Szec#endif 1653131580Sbms error = copyin(ifr->ifr_data, &vlr, sizeof(vlr)); 165434649Swollman if (error) 165534649Swollman break; 165634649Swollman if (vlr.vlr_parent[0] == '\0') { 165744763Swpaul vlan_unconfig(ifp); 165834649Swollman break; 165934649Swollman } 166034649Swollman p = ifunit(vlr.vlr_parent); 1661197010Semaste if (p == NULL) { 166234649Swollman error = ENOENT; 166334649Swollman break; 166434649Swollman } 1665117343Swpaul /* 1666230026Srwatson * Don't let the caller set up a VLAN VID with 1667117343Swpaul * anything except VLID bits. 1668117343Swpaul */ 1669117343Swpaul if (vlr.vlr_tag & ~EVL_VLID_MASK) { 1670117343Swpaul error = EINVAL; 1671117343Swpaul break; 1672117343Swpaul } 1673155051Sglebius error = vlan_config(ifv, p, vlr.vlr_tag); 1674155051Sglebius if (error) 167534649Swollman break; 1676106932Ssam 1677150846Syar /* Update flags on the parent, if necessary. */ 1678150846Syar vlan_setflags(ifp, 1); 167934649Swollman break; 1680131580Sbms 168134649Swollman case SIOCGETVLAN: 1682215726Szec#ifdef VIMAGE 1683215726Szec if (ifp->if_vnet != ifp->if_home_vnet) { 1684215726Szec error = EPERM; 1685215726Szec break; 1686215726Szec } 1687215726Szec#endif 1688131580Sbms bzero(&vlr, sizeof(vlr)); 1689119780Ssam VLAN_LOCK(); 1690155051Sglebius if (TRUNK(ifv) != NULL) { 1691155051Sglebius strlcpy(vlr.vlr_parent, PARENT(ifv)->if_xname, 1692121816Sbrooks sizeof(vlr.vlr_parent)); 1693230026Srwatson vlr.vlr_tag = ifv->ifv_vid; 169434649Swollman } 1695119780Ssam VLAN_UNLOCK(); 1696131580Sbms error = copyout(&vlr, ifr->ifr_data, sizeof(vlr)); 169734649Swollman break; 169834649Swollman 169934649Swollman case SIOCSIFFLAGS: 170034649Swollman /* 1701150846Syar * We should propagate selected flags to the parent, 1702150846Syar * e.g., promiscuous mode. 170334649Swollman */ 1704155051Sglebius if (TRUNK(ifv) != NULL) 1705150846Syar error = vlan_setflags(ifp, 1); 170634649Swollman break; 1707106932Ssam 170844763Swpaul case SIOCADDMULTI: 170944763Swpaul case SIOCDELMULTI: 1710155051Sglebius /* 1711155051Sglebius * If we don't have a parent, just remember the membership for 1712155051Sglebius * when we do. 1713155051Sglebius */ 1714270136Smav trunk = TRUNK(ifv); 1715270136Smav if (trunk != NULL) { 1716270136Smav TRUNK_LOCK(trunk); 1717155051Sglebius error = vlan_setmulti(ifp); 1718270136Smav TRUNK_UNLOCK(trunk); 1719270136Smav } 172044763Swpaul break; 1721155051Sglebius 172234649Swollman default: 1723219819Sjeff error = EINVAL; 1724219819Sjeff break; 172534649Swollman } 1726131580Sbms 1727131580Sbms return (error); 172834649Swollman} 1729