if_vlan.c revision 139823
110627Sopensolaris@drydog.com/*- 210627Sopensolaris@drydog.com * Copyright 1998 Massachusetts Institute of Technology 310627Sopensolaris@drydog.com * 410627Sopensolaris@drydog.com * Permission to use, copy, modify, and distribute this software and 510627Sopensolaris@drydog.com * its documentation for any purpose and without fee is hereby 610627Sopensolaris@drydog.com * granted, provided that both the above copyright notice and this 710627Sopensolaris@drydog.com * permission notice appear in all copies, that both the above 810627Sopensolaris@drydog.com * copyright notice and this permission notice appear in all 910627Sopensolaris@drydog.com * supporting documentation, and that the name of M.I.T. not be used 1010627Sopensolaris@drydog.com * in advertising or publicity pertaining to distribution of the 1110627Sopensolaris@drydog.com * software without specific, written prior permission. M.I.T. makes 1210627Sopensolaris@drydog.com * no representations about the suitability of this software for any 1310627Sopensolaris@drydog.com * purpose. It is provided "as is" without express or implied 1410627Sopensolaris@drydog.com * warranty. 1510627Sopensolaris@drydog.com * 1610627Sopensolaris@drydog.com * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 1710627Sopensolaris@drydog.com * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 1810627Sopensolaris@drydog.com * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1910627Sopensolaris@drydog.com * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 2010627Sopensolaris@drydog.com * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2110627Sopensolaris@drydog.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2210627Sopensolaris@drydog.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2310627Sopensolaris@drydog.com * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2410627Sopensolaris@drydog.com * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2510627Sopensolaris@drydog.com * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2610627Sopensolaris@drydog.com * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2710627Sopensolaris@drydog.com * SUCH DAMAGE. 2810627Sopensolaris@drydog.com * 2910627Sopensolaris@drydog.com * $FreeBSD: head/sys/net/if_vlan.c 139823 2005-01-07 01:45:51Z imp $ 3010627Sopensolaris@drydog.com */ 3110627Sopensolaris@drydog.com 3210627Sopensolaris@drydog.com/* 3310627Sopensolaris@drydog.com * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. 3410627Sopensolaris@drydog.com * Might be extended some day to also handle IEEE 802.1p priority 3510627Sopensolaris@drydog.com * tagging. This is sort of sneaky in the implementation, since 3610627Sopensolaris@drydog.com * we need to pretend to be enough of an Ethernet implementation 3710627Sopensolaris@drydog.com * to make arp work. The way we do this is by telling everyone 3810627Sopensolaris@drydog.com * that we are an Ethernet, and then catch the packets that 3910627Sopensolaris@drydog.com * ether_output() left on our output queue when it calls 4010627Sopensolaris@drydog.com * if_start(), rewrite them for use by the real outgoing interface, 4110627Sopensolaris@drydog.com * and ask it to send them. 4210627Sopensolaris@drydog.com */ 4310627Sopensolaris@drydog.com 4410627Sopensolaris@drydog.com#include "opt_inet.h" 4510627Sopensolaris@drydog.com 4610627Sopensolaris@drydog.com#include <sys/param.h> 4710627Sopensolaris@drydog.com#include <sys/kernel.h> 4810627Sopensolaris@drydog.com#include <sys/malloc.h> 4910627Sopensolaris@drydog.com#include <sys/mbuf.h> 5010627Sopensolaris@drydog.com#include <sys/module.h> 5110627Sopensolaris@drydog.com#include <sys/queue.h> 5210627Sopensolaris@drydog.com#include <sys/socket.h> 5310627Sopensolaris@drydog.com#include <sys/sockio.h> 5410627Sopensolaris@drydog.com#include <sys/sysctl.h> 5510627Sopensolaris@drydog.com#include <sys/systm.h> 5610627Sopensolaris@drydog.com 5710627Sopensolaris@drydog.com#include <net/bpf.h> 5810627Sopensolaris@drydog.com#include <net/ethernet.h> 5911141Sopensolaris@drydog.com#include <net/if.h> 6010627Sopensolaris@drydog.com#include <net/if_clone.h> 6110627Sopensolaris@drydog.com#include <net/if_arp.h> 6210627Sopensolaris@drydog.com#include <net/if_dl.h> 6310627Sopensolaris@drydog.com#include <net/if_types.h> 6410627Sopensolaris@drydog.com#include <net/if_vlan_var.h> 6511141Sopensolaris@drydog.com#include <net/route.h> 6610627Sopensolaris@drydog.com 6710627Sopensolaris@drydog.com#ifdef INET 6810627Sopensolaris@drydog.com#include <netinet/in.h> 6911141Sopensolaris@drydog.com#include <netinet/if_ether.h> 7010627Sopensolaris@drydog.com#endif 7111141Sopensolaris@drydog.com 7210627Sopensolaris@drydog.com#define VLANNAME "vlan" 7310627Sopensolaris@drydog.com 7411141Sopensolaris@drydog.comstruct vlan_mc_entry { 7510627Sopensolaris@drydog.com struct ether_addr mc_addr; 7610627Sopensolaris@drydog.com SLIST_ENTRY(vlan_mc_entry) mc_entries; 7710627Sopensolaris@drydog.com}; 7810627Sopensolaris@drydog.com 7910627Sopensolaris@drydog.comstruct ifvlan { 8010627Sopensolaris@drydog.com struct arpcom ifv_ac; /* make this an interface */ 8110627Sopensolaris@drydog.com struct ifnet *ifv_p; /* parent inteface of this vlan */ 8210627Sopensolaris@drydog.com struct ifv_linkmib { 8310627Sopensolaris@drydog.com int ifvm_parent; 8410627Sopensolaris@drydog.com int ifvm_encaplen; /* encapsulation length */ 8510627Sopensolaris@drydog.com int ifvm_mtufudge; /* MTU fudged by this much */ 8610627Sopensolaris@drydog.com int ifvm_mintu; /* min transmission unit */ 8710627Sopensolaris@drydog.com u_int16_t ifvm_proto; /* encapsulation ethertype */ 8810627Sopensolaris@drydog.com u_int16_t ifvm_tag; /* tag to apply on packets leaving if */ 8910627Sopensolaris@drydog.com } ifv_mib; 9010627Sopensolaris@drydog.com SLIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead; 9110627Sopensolaris@drydog.com LIST_ENTRY(ifvlan) ifv_list; 9210627Sopensolaris@drydog.com int ifv_flags; 9310627Sopensolaris@drydog.com}; 9410627Sopensolaris@drydog.com#define ifv_if ifv_ac.ac_if 9510627Sopensolaris@drydog.com#define ifv_tag ifv_mib.ifvm_tag 9610627Sopensolaris@drydog.com#define ifv_encaplen ifv_mib.ifvm_encaplen 9710627Sopensolaris@drydog.com#define ifv_mtufudge ifv_mib.ifvm_mtufudge 9810627Sopensolaris@drydog.com#define ifv_mintu ifv_mib.ifvm_mintu 9910627Sopensolaris@drydog.com 10010627Sopensolaris@drydog.com#define IFVF_PROMISC 0x01 /* promiscuous mode enabled */ 10110627Sopensolaris@drydog.com 10210627Sopensolaris@drydog.comSYSCTL_DECL(_net_link); 10310627Sopensolaris@drydog.comSYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); 10410627Sopensolaris@drydog.comSYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); 10510627Sopensolaris@drydog.com 10610627Sopensolaris@drydog.comstatic MALLOC_DEFINE(M_VLAN, VLANNAME, "802.1Q Virtual LAN Interface"); 10710627Sopensolaris@drydog.comstatic LIST_HEAD(, ifvlan) ifv_list; 10810627Sopensolaris@drydog.com 10910627Sopensolaris@drydog.com/* 11010627Sopensolaris@drydog.com * Locking: one lock is used to guard both the ifv_list and modification 11110627Sopensolaris@drydog.com * to vlan data structures. We are rather conservative here; probably 11210627Sopensolaris@drydog.com * more than necessary. 11310627Sopensolaris@drydog.com */ 11410627Sopensolaris@drydog.comstatic struct mtx ifv_mtx; 11510627Sopensolaris@drydog.com#define VLAN_LOCK_INIT() mtx_init(&ifv_mtx, VLANNAME, NULL, MTX_DEF) 11610627Sopensolaris@drydog.com#define VLAN_LOCK_DESTROY() mtx_destroy(&ifv_mtx) 11710627Sopensolaris@drydog.com#define VLAN_LOCK_ASSERT() mtx_assert(&ifv_mtx, MA_OWNED) 11810627Sopensolaris@drydog.com#define VLAN_LOCK() mtx_lock(&ifv_mtx) 11910627Sopensolaris@drydog.com#define VLAN_UNLOCK() mtx_unlock(&ifv_mtx) 12010627Sopensolaris@drydog.com 12110627Sopensolaris@drydog.comstatic void vlan_start(struct ifnet *ifp); 12210627Sopensolaris@drydog.comstatic void vlan_ifinit(void *foo); 12310627Sopensolaris@drydog.comstatic void vlan_input(struct ifnet *ifp, struct mbuf *m); 12410627Sopensolaris@drydog.comstatic int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); 12510627Sopensolaris@drydog.comstatic int vlan_setmulti(struct ifnet *ifp); 12610627Sopensolaris@drydog.comstatic int vlan_unconfig(struct ifnet *ifp); 12711141Sopensolaris@drydog.comstatic int vlan_config(struct ifvlan *ifv, struct ifnet *p); 12810627Sopensolaris@drydog.comstatic void vlan_link_state(struct ifnet *ifp, int link); 12910627Sopensolaris@drydog.comstatic int vlan_set_promisc(struct ifnet *ifp); 13010627Sopensolaris@drydog.com 13110627Sopensolaris@drydog.comstatic struct ifnet *vlan_clone_match_ethertag(struct if_clone *, 13210627Sopensolaris@drydog.com const char *, int *); 13310627Sopensolaris@drydog.comstatic int vlan_clone_match(struct if_clone *, const char *); 13410627Sopensolaris@drydog.comstatic int vlan_clone_create(struct if_clone *, char *, size_t); 13510627Sopensolaris@drydog.comstatic int vlan_clone_destroy(struct if_clone *, struct ifnet *); 13610627Sopensolaris@drydog.com 13710627Sopensolaris@drydog.comstruct if_clone vlan_cloner = IFC_CLONE_INITIALIZER(VLANNAME, NULL, IF_MAXUNIT, 13810627Sopensolaris@drydog.com NULL, vlan_clone_match, vlan_clone_create, vlan_clone_destroy); 13910627Sopensolaris@drydog.com 14010627Sopensolaris@drydog.com/* 14110627Sopensolaris@drydog.com * Program our multicast filter. What we're actually doing is 14210627Sopensolaris@drydog.com * programming the multicast filter of the parent. This has the 14310627Sopensolaris@drydog.com * side effect of causing the parent interface to receive multicast 14410627Sopensolaris@drydog.com * traffic that it doesn't really want, which ends up being discarded 14510627Sopensolaris@drydog.com * later by the upper protocol layers. Unfortunately, there's no way 14610627Sopensolaris@drydog.com * to avoid this: there really is only one physical interface. 14710627Sopensolaris@drydog.com * 14810627Sopensolaris@drydog.com * XXX: There is a possible race here if more than one thread is 14910627Sopensolaris@drydog.com * modifying the multicast state of the vlan interface at the same time. 15010627Sopensolaris@drydog.com */ 15110627Sopensolaris@drydog.comstatic int 15210627Sopensolaris@drydog.comvlan_setmulti(struct ifnet *ifp) 15310627Sopensolaris@drydog.com{ 15410627Sopensolaris@drydog.com struct ifnet *ifp_p; 15510627Sopensolaris@drydog.com struct ifmultiaddr *ifma, *rifma = NULL; 15610627Sopensolaris@drydog.com struct ifvlan *sc; 15710627Sopensolaris@drydog.com struct vlan_mc_entry *mc = NULL; 15810627Sopensolaris@drydog.com struct sockaddr_dl sdl; 15910627Sopensolaris@drydog.com int error; 16010627Sopensolaris@drydog.com 16110627Sopensolaris@drydog.com /*VLAN_LOCK_ASSERT();*/ 16210627Sopensolaris@drydog.com 16310627Sopensolaris@drydog.com /* Find the parent. */ 16410627Sopensolaris@drydog.com sc = ifp->if_softc; 16510627Sopensolaris@drydog.com ifp_p = sc->ifv_p; 16610627Sopensolaris@drydog.com 16710627Sopensolaris@drydog.com /* 16810627Sopensolaris@drydog.com * If we don't have a parent, just remember the membership for 16910627Sopensolaris@drydog.com * when we do. 17010627Sopensolaris@drydog.com */ 17110627Sopensolaris@drydog.com if (ifp_p == NULL) 17210627Sopensolaris@drydog.com return (0); 17310627Sopensolaris@drydog.com 17410627Sopensolaris@drydog.com bzero((char *)&sdl, sizeof(sdl)); 17510627Sopensolaris@drydog.com sdl.sdl_len = sizeof(sdl); 17610627Sopensolaris@drydog.com sdl.sdl_family = AF_LINK; 17710627Sopensolaris@drydog.com sdl.sdl_index = ifp_p->if_index; 17810627Sopensolaris@drydog.com sdl.sdl_type = IFT_ETHER; 17910627Sopensolaris@drydog.com sdl.sdl_alen = ETHER_ADDR_LEN; 18010627Sopensolaris@drydog.com 18110627Sopensolaris@drydog.com /* First, remove any existing filter entries. */ 18210627Sopensolaris@drydog.com while (SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) { 18310627Sopensolaris@drydog.com mc = SLIST_FIRST(&sc->vlan_mc_listhead); 18410627Sopensolaris@drydog.com bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 18510627Sopensolaris@drydog.com error = if_delmulti(ifp_p, (struct sockaddr *)&sdl); 18610627Sopensolaris@drydog.com if (error) 18710627Sopensolaris@drydog.com return (error); 18810627Sopensolaris@drydog.com SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); 18910627Sopensolaris@drydog.com free(mc, M_VLAN); 19010627Sopensolaris@drydog.com } 19110627Sopensolaris@drydog.com 19210627Sopensolaris@drydog.com /* Now program new ones. */ 19310627Sopensolaris@drydog.com TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 19410627Sopensolaris@drydog.com if (ifma->ifma_addr->sa_family != AF_LINK) 19510627Sopensolaris@drydog.com continue; 19610627Sopensolaris@drydog.com mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_NOWAIT); 19710627Sopensolaris@drydog.com if (mc == NULL) 19811141Sopensolaris@drydog.com return (ENOMEM); 19910627Sopensolaris@drydog.com bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 20010627Sopensolaris@drydog.com (char *)&mc->mc_addr, ETHER_ADDR_LEN); 20110627Sopensolaris@drydog.com SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); 20210627Sopensolaris@drydog.com bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 20310627Sopensolaris@drydog.com LLADDR(&sdl), ETHER_ADDR_LEN); 20410627Sopensolaris@drydog.com error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma); 20510627Sopensolaris@drydog.com if (error) 20610627Sopensolaris@drydog.com return (error); 20710627Sopensolaris@drydog.com } 20811141Sopensolaris@drydog.com 20910627Sopensolaris@drydog.com return (0); 21010627Sopensolaris@drydog.com} 21110627Sopensolaris@drydog.com 21210627Sopensolaris@drydog.com/* 21310627Sopensolaris@drydog.com * VLAN support can be loaded as a module. The only place in the 21410627Sopensolaris@drydog.com * system that's intimately aware of this is ether_input. We hook 21510627Sopensolaris@drydog.com * into this code through vlan_input_p which is defined there and 21610627Sopensolaris@drydog.com * set here. Noone else in the system should be aware of this so 21710627Sopensolaris@drydog.com * we use an explicit reference here. 21810627Sopensolaris@drydog.com * 21910627Sopensolaris@drydog.com * NB: Noone should ever need to check if vlan_input_p is null or 22010627Sopensolaris@drydog.com * not. This is because interfaces have a count of the number 22110627Sopensolaris@drydog.com * of active vlans (if_nvlans) and this should never be bumped 22210627Sopensolaris@drydog.com * except by vlan_config--which is in this module so therefore 22310627Sopensolaris@drydog.com * the module must be loaded and vlan_input_p must be non-NULL. 22410627Sopensolaris@drydog.com */ 22510627Sopensolaris@drydog.comextern void (*vlan_input_p)(struct ifnet *, struct mbuf *); 22610627Sopensolaris@drydog.com 22710627Sopensolaris@drydog.com/* For MII eyes only... */ 22810627Sopensolaris@drydog.comextern void (*vlan_link_state_p)(struct ifnet *, int); 22910627Sopensolaris@drydog.com 23010627Sopensolaris@drydog.comstatic int 23110627Sopensolaris@drydog.comvlan_modevent(module_t mod, int type, void *data) 23211141Sopensolaris@drydog.com{ 23311141Sopensolaris@drydog.com 23410627Sopensolaris@drydog.com switch (type) { 23510627Sopensolaris@drydog.com case MOD_LOAD: 23610627Sopensolaris@drydog.com LIST_INIT(&ifv_list); 23710627Sopensolaris@drydog.com VLAN_LOCK_INIT(); 23810627Sopensolaris@drydog.com vlan_input_p = vlan_input; 23910627Sopensolaris@drydog.com vlan_link_state_p = vlan_link_state; 24011141Sopensolaris@drydog.com if_clone_attach(&vlan_cloner); 24111141Sopensolaris@drydog.com break; 24210627Sopensolaris@drydog.com case MOD_UNLOAD: 24310627Sopensolaris@drydog.com if_clone_detach(&vlan_cloner); 24410627Sopensolaris@drydog.com vlan_input_p = NULL; 24510627Sopensolaris@drydog.com vlan_link_state_p = NULL; 24610627Sopensolaris@drydog.com while (!LIST_EMPTY(&ifv_list)) 24710627Sopensolaris@drydog.com vlan_clone_destroy(&vlan_cloner, 24811141Sopensolaris@drydog.com &LIST_FIRST(&ifv_list)->ifv_if); 24910627Sopensolaris@drydog.com VLAN_LOCK_DESTROY(); 25010627Sopensolaris@drydog.com break; 25111141Sopensolaris@drydog.com default: 25210627Sopensolaris@drydog.com return (EOPNOTSUPP); 25310627Sopensolaris@drydog.com } 25411141Sopensolaris@drydog.com return (0); 25510627Sopensolaris@drydog.com} 25611141Sopensolaris@drydog.com 25710627Sopensolaris@drydog.comstatic moduledata_t vlan_mod = { 25810627Sopensolaris@drydog.com "if_vlan", 25910627Sopensolaris@drydog.com vlan_modevent, 26010627Sopensolaris@drydog.com 0 26110627Sopensolaris@drydog.com}; 26210627Sopensolaris@drydog.com 26310627Sopensolaris@drydog.comDECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 26410627Sopensolaris@drydog.comMODULE_DEPEND(if_vlan, miibus, 1, 1, 1); 26510627Sopensolaris@drydog.com 26610627Sopensolaris@drydog.comstatic struct ifnet * 26710627Sopensolaris@drydog.comvlan_clone_match_ethertag(struct if_clone *ifc, const char *name, int *tag) 26810627Sopensolaris@drydog.com{ 26910627Sopensolaris@drydog.com const char *cp; 27010627Sopensolaris@drydog.com struct ifnet *ifp; 27110627Sopensolaris@drydog.com int t = 0; 27210627Sopensolaris@drydog.com 27310627Sopensolaris@drydog.com /* Check for <etherif>.<vlan> style interface names. */ 27410627Sopensolaris@drydog.com IFNET_RLOCK(); 27510627Sopensolaris@drydog.com TAILQ_FOREACH(ifp, &ifnet, if_link) { 27610627Sopensolaris@drydog.com if (ifp->if_type != IFT_ETHER) 27710627Sopensolaris@drydog.com continue; 27810627Sopensolaris@drydog.com if (strncmp(ifp->if_xname, name, strlen(ifp->if_xname)) != 0) 27910627Sopensolaris@drydog.com continue; 28010627Sopensolaris@drydog.com cp = name + strlen(ifp->if_xname); 28110627Sopensolaris@drydog.com if (*cp != '.') 28210627Sopensolaris@drydog.com continue; 28310627Sopensolaris@drydog.com for(; *cp != '\0'; cp++) { 28410627Sopensolaris@drydog.com if (*cp < '0' || *cp > '9') 28510627Sopensolaris@drydog.com continue; 28610627Sopensolaris@drydog.com t = (t * 10) + (*cp - '0'); 28710627Sopensolaris@drydog.com } 28810627Sopensolaris@drydog.com if (tag != NULL) 28910627Sopensolaris@drydog.com *tag = t; 29010627Sopensolaris@drydog.com break; 29110627Sopensolaris@drydog.com } 29210627Sopensolaris@drydog.com IFNET_RUNLOCK(); 29310627Sopensolaris@drydog.com 29410627Sopensolaris@drydog.com return (ifp); 29510627Sopensolaris@drydog.com} 29610627Sopensolaris@drydog.com 29710627Sopensolaris@drydog.comstatic int 29810627Sopensolaris@drydog.comvlan_clone_match(struct if_clone *ifc, const char *name) 29910627Sopensolaris@drydog.com{ 30010627Sopensolaris@drydog.com const char *cp; 30110627Sopensolaris@drydog.com 30210627Sopensolaris@drydog.com if (vlan_clone_match_ethertag(ifc, name, NULL) != NULL) 30310627Sopensolaris@drydog.com return (1); 30410627Sopensolaris@drydog.com 30510627Sopensolaris@drydog.com if (strncmp(VLANNAME, name, strlen(VLANNAME)) != 0) 30610627Sopensolaris@drydog.com return (0); 30710627Sopensolaris@drydog.com for (cp = name + 4; *cp != '\0'; cp++) { 30810627Sopensolaris@drydog.com if (*cp < '0' || *cp > '9') 30910627Sopensolaris@drydog.com return (0); 31010627Sopensolaris@drydog.com } 31110627Sopensolaris@drydog.com 31210627Sopensolaris@drydog.com return (1); 31310627Sopensolaris@drydog.com} 31410627Sopensolaris@drydog.com 31510627Sopensolaris@drydog.comstatic int 31610627Sopensolaris@drydog.comvlan_clone_create(struct if_clone *ifc, char *name, size_t len) 31710627Sopensolaris@drydog.com{ 31810627Sopensolaris@drydog.com char *dp; 31910627Sopensolaris@drydog.com int wildcard; 32010627Sopensolaris@drydog.com int unit; 32110627Sopensolaris@drydog.com int error; 32211141Sopensolaris@drydog.com int tag; 32310627Sopensolaris@drydog.com int ethertag; 32410627Sopensolaris@drydog.com struct ifvlan *ifv; 32510627Sopensolaris@drydog.com struct ifnet *ifp; 32610627Sopensolaris@drydog.com struct ifnet *p; 32711141Sopensolaris@drydog.com 32810627Sopensolaris@drydog.com if ((p = vlan_clone_match_ethertag(ifc, name, &tag)) != NULL) { 32910627Sopensolaris@drydog.com ethertag = 1; 33010627Sopensolaris@drydog.com unit = -1; 33110627Sopensolaris@drydog.com wildcard = 0; 33210627Sopensolaris@drydog.com 33310627Sopensolaris@drydog.com /* 33410627Sopensolaris@drydog.com * Don't let the caller set up a VLAN tag with 33510627Sopensolaris@drydog.com * anything except VLID bits. 33610627Sopensolaris@drydog.com */ 33711141Sopensolaris@drydog.com if (tag & ~EVL_VLID_MASK) 338 return (EINVAL); 339 } else { 340 ethertag = 0; 341 342 error = ifc_name2unit(name, &unit); 343 if (error != 0) 344 return (error); 345 346 wildcard = (unit < 0); 347 } 348 349 error = ifc_alloc_unit(ifc, &unit); 350 if (error != 0) 351 return (error); 352 353 /* In the wildcard case, we need to update the name. */ 354 if (wildcard) { 355 for (dp = name; *dp != '\0'; dp++); 356 if (snprintf(dp, len - (dp-name), "%d", unit) > 357 len - (dp-name) - 1) { 358 panic("%s: interface name too long", __func__); 359 } 360 } 361 362 ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO); 363 ifp = &ifv->ifv_if; 364 SLIST_INIT(&ifv->vlan_mc_listhead); 365 366 ifp->if_softc = ifv; 367 /* 368 * Set the name manually rather then using if_initname because 369 * we don't conform to the default naming convention for interfaces. 370 */ 371 strlcpy(ifp->if_xname, name, IFNAMSIZ); 372 ifp->if_dname = ifc->ifc_name; 373 ifp->if_dunit = unit; 374 /* NB: flags are not set here */ 375 ifp->if_linkmib = &ifv->ifv_mib; 376 ifp->if_linkmiblen = sizeof(ifv->ifv_mib); 377 /* NB: mtu is not set here */ 378 379 ifp->if_init = vlan_ifinit; 380 ifp->if_start = vlan_start; 381 ifp->if_ioctl = vlan_ioctl; 382 ifp->if_snd.ifq_maxlen = ifqmaxlen; 383 ether_ifattach(ifp, ifv->ifv_ac.ac_enaddr); 384 /* Now undo some of the damage... */ 385 ifp->if_baudrate = 0; 386 ifp->if_type = IFT_L2VLAN; 387 ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN; 388 389 VLAN_LOCK(); 390 LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list); 391 VLAN_UNLOCK(); 392 393 if (ethertag) { 394 VLAN_LOCK(); 395 error = vlan_config(ifv, p); 396 if (error != 0) { 397 /* 398 * Since we've partialy failed, we need to back 399 * out all the way, otherwise userland could get 400 * confused. Thus, we destroy the interface. 401 */ 402 LIST_REMOVE(ifv, ifv_list); 403 vlan_unconfig(ifp); 404 VLAN_UNLOCK(); 405 ether_ifdetach(ifp); 406 free(ifv, M_VLAN); 407 408 return (error); 409 } 410 ifv->ifv_tag = tag; 411 ifp->if_flags |= IFF_RUNNING; 412 VLAN_UNLOCK(); 413 414 /* Update promiscuous mode, if necessary. */ 415 vlan_set_promisc(ifp); 416 } 417 418 return (0); 419} 420 421static int 422vlan_clone_destroy(struct if_clone *ifc, struct ifnet *ifp) 423{ 424 int unit; 425 struct ifvlan *ifv = ifp->if_softc; 426 427 unit = ifp->if_dunit; 428 429 VLAN_LOCK(); 430 LIST_REMOVE(ifv, ifv_list); 431 vlan_unconfig(ifp); 432 VLAN_UNLOCK(); 433 434 ether_ifdetach(ifp); 435 436 free(ifv, M_VLAN); 437 438 ifc_free_unit(ifc, unit); 439 440 return (0); 441} 442 443/* 444 * The ifp->if_init entry point for vlan(4) is a no-op. 445 */ 446static void 447vlan_ifinit(void *foo) 448{ 449 450} 451 452static void 453vlan_start(struct ifnet *ifp) 454{ 455 struct ifvlan *ifv; 456 struct ifnet *p; 457 struct ether_vlan_header *evl; 458 struct mbuf *m; 459 int error; 460 461 ifv = ifp->if_softc; 462 p = ifv->ifv_p; 463 464 ifp->if_flags |= IFF_OACTIVE; 465 for (;;) { 466 IF_DEQUEUE(&ifp->if_snd, m); 467 if (m == 0) 468 break; 469 BPF_MTAP(ifp, m); 470 471 /* 472 * Do not run parent's if_start() if the parent is not up, 473 * or parent's driver will cause a system crash. 474 */ 475 if ((p->if_flags & (IFF_UP | IFF_RUNNING)) != 476 (IFF_UP | IFF_RUNNING)) { 477 m_freem(m); 478 ifp->if_collisions++; 479 continue; 480 } 481 482 /* 483 * If underlying interface can do VLAN tag insertion itself, 484 * just pass the packet along. However, we need some way to 485 * tell the interface where the packet came from so that it 486 * knows how to find the VLAN tag to use, so we attach a 487 * packet tag that holds it. 488 */ 489 if (p->if_capenable & IFCAP_VLAN_HWTAGGING) { 490 struct m_tag *mtag = m_tag_alloc(MTAG_VLAN, 491 MTAG_VLAN_TAG, 492 sizeof(u_int), 493 M_NOWAIT); 494 if (mtag == NULL) { 495 ifp->if_oerrors++; 496 m_freem(m); 497 continue; 498 } 499 *(u_int*)(mtag + 1) = ifv->ifv_tag; 500 m_tag_prepend(m, mtag); 501 } else { 502 M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT); 503 if (m == NULL) { 504 if_printf(ifp, 505 "unable to prepend VLAN header\n"); 506 ifp->if_oerrors++; 507 continue; 508 } 509 /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ 510 511 if (m->m_len < sizeof(*evl)) { 512 m = m_pullup(m, sizeof(*evl)); 513 if (m == NULL) { 514 if_printf(ifp, 515 "cannot pullup VLAN header\n"); 516 ifp->if_oerrors++; 517 continue; 518 } 519 } 520 521 /* 522 * Transform the Ethernet header into an Ethernet header 523 * with 802.1Q encapsulation. 524 */ 525 bcopy(mtod(m, char *) + ifv->ifv_encaplen, 526 mtod(m, char *), ETHER_HDR_LEN); 527 evl = mtod(m, struct ether_vlan_header *); 528 evl->evl_proto = evl->evl_encap_proto; 529 evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 530 evl->evl_tag = htons(ifv->ifv_tag); 531#ifdef DEBUG 532 printf("vlan_start: %*D\n", (int)sizeof(*evl), 533 (unsigned char *)evl, ":"); 534#endif 535 } 536 537 /* 538 * Send it, precisely as ether_output() would have. 539 * We are already running at splimp. 540 */ 541 IFQ_HANDOFF(p, m, error); 542 if (!error) 543 ifp->if_opackets++; 544 else 545 ifp->if_oerrors++; 546 } 547 ifp->if_flags &= ~IFF_OACTIVE; 548} 549 550static void 551vlan_input(struct ifnet *ifp, struct mbuf *m) 552{ 553 struct ether_vlan_header *evl; 554 struct ifvlan *ifv; 555 struct m_tag *mtag; 556 u_int tag; 557 558 mtag = m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL); 559 if (mtag != NULL) { 560 /* 561 * Packet is tagged, m contains a normal 562 * Ethernet frame; the tag is stored out-of-band. 563 */ 564 tag = EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag)); 565 m_tag_delete(m, mtag); 566 } else { 567 switch (ifp->if_type) { 568 case IFT_ETHER: 569 if (m->m_len < sizeof(*evl) && 570 (m = m_pullup(m, sizeof(*evl))) == NULL) { 571 if_printf(ifp, "cannot pullup VLAN header\n"); 572 return; 573 } 574 evl = mtod(m, struct ether_vlan_header *); 575 KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN, 576 ("vlan_input: bad encapsulated protocols (%u)", 577 ntohs(evl->evl_encap_proto))); 578 579 tag = EVL_VLANOFTAG(ntohs(evl->evl_tag)); 580 581 /* 582 * Restore the original ethertype. We'll remove 583 * the encapsulation after we've found the vlan 584 * interface corresponding to the tag. 585 */ 586 evl->evl_encap_proto = evl->evl_proto; 587 break; 588 default: 589 tag = (u_int) -1; 590#ifdef DIAGNOSTIC 591 panic("vlan_input: unsupported if type %u", 592 ifp->if_type); 593#endif 594 break; 595 } 596 } 597 598 VLAN_LOCK(); 599 LIST_FOREACH(ifv, &ifv_list, ifv_list) 600 if (ifp == ifv->ifv_p && tag == ifv->ifv_tag) 601 break; 602 603 if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 604 VLAN_UNLOCK(); 605 m_freem(m); 606 ifp->if_noproto++; 607#ifdef DEBUG 608 printf("vlan_input: tag %d, no interface\n", tag); 609#endif 610 return; 611 } 612 VLAN_UNLOCK(); /* XXX extend below? */ 613#ifdef DEBUG 614 printf("vlan_input: tag %d, parent %s\n", tag, ifv->ifv_p->if_xname); 615#endif 616 617 if (mtag == NULL) { 618 /* 619 * Packet had an in-line encapsulation header; 620 * remove it. The original header has already 621 * been fixed up above. 622 */ 623 bcopy(mtod(m, caddr_t), 624 mtod(m, caddr_t) + ETHER_VLAN_ENCAP_LEN, 625 ETHER_HDR_LEN); 626 m_adj(m, ETHER_VLAN_ENCAP_LEN); 627 } 628 629 m->m_pkthdr.rcvif = &ifv->ifv_if; 630 ifv->ifv_if.if_ipackets++; 631 632 /* Pass it back through the parent's input routine. */ 633 (*ifp->if_input)(&ifv->ifv_if, m); 634} 635 636static int 637vlan_config(struct ifvlan *ifv, struct ifnet *p) 638{ 639 struct ifaddr *ifa1, *ifa2; 640 struct sockaddr_dl *sdl1, *sdl2; 641 642 VLAN_LOCK_ASSERT(); 643 644 if (p->if_data.ifi_type != IFT_ETHER) 645 return (EPROTONOSUPPORT); 646 if (ifv->ifv_p) 647 return (EBUSY); 648 649 ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; 650 ifv->ifv_mintu = ETHERMIN; 651 ifv->ifv_flags = 0; 652 653 /* 654 * The active VLAN counter on the parent is used 655 * at various places to see if there is a vlan(4) 656 * attached to this physical interface. 657 */ 658 p->if_nvlans++; 659 660 /* 661 * If the parent supports the VLAN_MTU capability, 662 * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames, 663 * use it. 664 */ 665 if (p->if_capenable & IFCAP_VLAN_MTU) { 666 /* 667 * No need to fudge the MTU since the parent can 668 * handle extended frames. 669 */ 670 ifv->ifv_mtufudge = 0; 671 } else { 672 /* 673 * Fudge the MTU by the encapsulation size. This 674 * makes us incompatible with strictly compliant 675 * 802.1Q implementations, but allows us to use 676 * the feature with other NetBSD implementations, 677 * which might still be useful. 678 */ 679 ifv->ifv_mtufudge = ifv->ifv_encaplen; 680 } 681 682 ifv->ifv_p = p; 683 ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge; 684 /* 685 * Copy only a selected subset of flags from the parent. 686 * Other flags are none of our business. 687 */ 688 ifv->ifv_if.if_flags = (p->if_flags & 689 (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT)); 690 ifv->ifv_if.if_link_state = p->if_link_state; 691 692#if 0 693 /* 694 * Not ready yet. We need notification from the parent 695 * when hw checksumming flags in its if_capenable change. 696 * Flags set in if_capabilities only are useless. 697 */ 698 /* 699 * If the parent interface can do hardware-assisted 700 * VLAN encapsulation, then propagate its hardware- 701 * assisted checksumming flags. 702 */ 703 if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) 704 ifv->ifv_if.if_capabilities |= p->if_capabilities & IFCAP_HWCSUM; 705#endif 706 707 /* 708 * Set up our ``Ethernet address'' to reflect the underlying 709 * physical interface's. 710 */ 711 ifa1 = ifaddr_byindex(ifv->ifv_if.if_index); 712 ifa2 = ifaddr_byindex(p->if_index); 713 sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; 714 sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; 715 sdl1->sdl_type = IFT_ETHER; 716 sdl1->sdl_alen = ETHER_ADDR_LEN; 717 bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); 718 bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 719 720 /* 721 * Configure multicast addresses that may already be 722 * joined on the vlan device. 723 */ 724 (void)vlan_setmulti(&ifv->ifv_if); /* XXX: VLAN lock held */ 725 726 return (0); 727} 728 729static int 730vlan_unconfig(struct ifnet *ifp) 731{ 732 struct ifaddr *ifa; 733 struct sockaddr_dl *sdl; 734 struct vlan_mc_entry *mc; 735 struct ifvlan *ifv; 736 struct ifnet *p; 737 int error; 738 739 VLAN_LOCK_ASSERT(); 740 741 ifv = ifp->if_softc; 742 p = ifv->ifv_p; 743 744 if (p) { 745 struct sockaddr_dl sdl; 746 747 /* 748 * Since the interface is being unconfigured, we need to 749 * empty the list of multicast groups that we may have joined 750 * while we were alive from the parent's list. 751 */ 752 bzero((char *)&sdl, sizeof(sdl)); 753 sdl.sdl_len = sizeof(sdl); 754 sdl.sdl_family = AF_LINK; 755 sdl.sdl_index = p->if_index; 756 sdl.sdl_type = IFT_ETHER; 757 sdl.sdl_alen = ETHER_ADDR_LEN; 758 759 while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) { 760 mc = SLIST_FIRST(&ifv->vlan_mc_listhead); 761 bcopy((char *)&mc->mc_addr, LLADDR(&sdl), 762 ETHER_ADDR_LEN); 763 error = if_delmulti(p, (struct sockaddr *)&sdl); 764 if (error) 765 return (error); 766 SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); 767 free(mc, M_VLAN); 768 } 769 770 p->if_nvlans--; 771 } 772 773 /* Disconnect from parent. */ 774 ifv->ifv_p = NULL; 775 ifv->ifv_if.if_mtu = ETHERMTU; /* XXX why not 0? */ 776 ifv->ifv_flags = 0; 777 ifv->ifv_if.if_link_state = LINK_STATE_UNKNOWN; 778 779 /* Clear our MAC address. */ 780 ifa = ifaddr_byindex(ifv->ifv_if.if_index); 781 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 782 sdl->sdl_type = IFT_ETHER; 783 sdl->sdl_alen = ETHER_ADDR_LEN; 784 bzero(LLADDR(sdl), ETHER_ADDR_LEN); 785 bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 786 787 return (0); 788} 789 790static int 791vlan_set_promisc(struct ifnet *ifp) 792{ 793 struct ifvlan *ifv = ifp->if_softc; 794 int error = 0; 795 796 if ((ifp->if_flags & IFF_PROMISC) != 0) { 797 if ((ifv->ifv_flags & IFVF_PROMISC) == 0) { 798 error = ifpromisc(ifv->ifv_p, 1); 799 if (error == 0) 800 ifv->ifv_flags |= IFVF_PROMISC; 801 } 802 } else { 803 if ((ifv->ifv_flags & IFVF_PROMISC) != 0) { 804 error = ifpromisc(ifv->ifv_p, 0); 805 if (error == 0) 806 ifv->ifv_flags &= ~IFVF_PROMISC; 807 } 808 } 809 810 return (error); 811} 812 813/* Inform all vlans that their parent has changed link state */ 814static void 815vlan_link_state(struct ifnet *ifp, int link) 816{ 817 struct ifvlan *ifv; 818 819 VLAN_LOCK(); 820 LIST_FOREACH(ifv, &ifv_list, ifv_list) { 821 if (ifv->ifv_p == ifp) { 822 ifv->ifv_if.if_link_state = ifv->ifv_p->if_link_state; 823 rt_ifmsg(&(ifv->ifv_if)); 824 KNOTE_UNLOCKED(&ifp->if_klist, link); 825 } 826 } 827 VLAN_UNLOCK(); 828} 829 830static int 831vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 832{ 833 struct ifaddr *ifa; 834 struct ifnet *p; 835 struct ifreq *ifr; 836 struct ifvlan *ifv; 837 struct vlanreq vlr; 838 int error = 0; 839 840 ifr = (struct ifreq *)data; 841 ifa = (struct ifaddr *)data; 842 ifv = ifp->if_softc; 843 844 switch (cmd) { 845 case SIOCSIFADDR: 846 ifp->if_flags |= IFF_UP; 847 848 switch (ifa->ifa_addr->sa_family) { 849#ifdef INET 850 case AF_INET: 851 arp_ifinit(&ifv->ifv_if, ifa); 852 break; 853#endif 854 default: 855 break; 856 } 857 break; 858 859 case SIOCGIFADDR: 860 { 861 struct sockaddr *sa; 862 863 sa = (struct sockaddr *) &ifr->ifr_data; 864 bcopy(IFP2AC(ifp)->ac_enaddr, (caddr_t)sa->sa_data, 865 ETHER_ADDR_LEN); 866 } 867 break; 868 869 case SIOCGIFMEDIA: 870 VLAN_LOCK(); 871 if (ifv->ifv_p != NULL) { 872 error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, 873 SIOCGIFMEDIA, data); 874 VLAN_UNLOCK(); 875 /* Limit the result to the parent's current config. */ 876 if (error == 0) { 877 struct ifmediareq *ifmr; 878 879 ifmr = (struct ifmediareq *)data; 880 if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) { 881 ifmr->ifm_count = 1; 882 error = copyout(&ifmr->ifm_current, 883 ifmr->ifm_ulist, 884 sizeof(int)); 885 } 886 } 887 } else { 888 VLAN_UNLOCK(); 889 error = EINVAL; 890 } 891 break; 892 893 case SIOCSIFMEDIA: 894 error = EINVAL; 895 break; 896 897 case SIOCSIFMTU: 898 /* 899 * Set the interface MTU. 900 */ 901 VLAN_LOCK(); 902 if (ifv->ifv_p != NULL) { 903 if (ifr->ifr_mtu > 904 (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) || 905 ifr->ifr_mtu < 906 (ifv->ifv_mintu - ifv->ifv_mtufudge)) 907 error = EINVAL; 908 else 909 ifp->if_mtu = ifr->ifr_mtu; 910 } else 911 error = EINVAL; 912 VLAN_UNLOCK(); 913 break; 914 915 case SIOCSETVLAN: 916 error = copyin(ifr->ifr_data, &vlr, sizeof(vlr)); 917 if (error) 918 break; 919 if (vlr.vlr_parent[0] == '\0') { 920 VLAN_LOCK(); 921 vlan_unconfig(ifp); 922 if (ifp->if_flags & IFF_UP) 923 if_down(ifp); 924 ifp->if_flags &= ~IFF_RUNNING; 925 VLAN_UNLOCK(); 926 break; 927 } 928 p = ifunit(vlr.vlr_parent); 929 if (p == 0) { 930 error = ENOENT; 931 break; 932 } 933 /* 934 * Don't let the caller set up a VLAN tag with 935 * anything except VLID bits. 936 */ 937 if (vlr.vlr_tag & ~EVL_VLID_MASK) { 938 error = EINVAL; 939 break; 940 } 941 VLAN_LOCK(); 942 error = vlan_config(ifv, p); 943 if (error) { 944 VLAN_UNLOCK(); 945 break; 946 } 947 ifv->ifv_tag = vlr.vlr_tag; 948 ifp->if_flags |= IFF_RUNNING; 949 VLAN_UNLOCK(); 950 951 /* Update promiscuous mode, if necessary. */ 952 vlan_set_promisc(ifp); 953 break; 954 955 case SIOCGETVLAN: 956 bzero(&vlr, sizeof(vlr)); 957 VLAN_LOCK(); 958 if (ifv->ifv_p) { 959 strlcpy(vlr.vlr_parent, ifv->ifv_p->if_xname, 960 sizeof(vlr.vlr_parent)); 961 vlr.vlr_tag = ifv->ifv_tag; 962 } 963 VLAN_UNLOCK(); 964 error = copyout(&vlr, ifr->ifr_data, sizeof(vlr)); 965 break; 966 967 case SIOCSIFFLAGS: 968 /* 969 * For promiscuous mode, we enable promiscuous mode on 970 * the parent if we need promiscuous on the VLAN interface. 971 */ 972 if (ifv->ifv_p != NULL) 973 error = vlan_set_promisc(ifp); 974 break; 975 976 case SIOCADDMULTI: 977 case SIOCDELMULTI: 978 /*VLAN_LOCK();*/ 979 error = vlan_setmulti(ifp); 980 /*VLAN_UNLOCK();*/ 981 break; 982 default: 983 error = EINVAL; 984 } 985 986 return (error); 987} 988