if_vlan.c revision 109711
1214456Srpaulo/* 2214456Srpaulo * Copyright 1998 Massachusetts Institute of Technology 3214456Srpaulo * 4214456Srpaulo * Permission to use, copy, modify, and distribute this software and 5214456Srpaulo * its documentation for any purpose and without fee is hereby 6214456Srpaulo * granted, provided that both the above copyright notice and this 7214456Srpaulo * permission notice appear in all copies, that both the above 8214456Srpaulo * copyright notice and this permission notice appear in all 9214456Srpaulo * supporting documentation, and that the name of M.I.T. not be used 10214456Srpaulo * in advertising or publicity pertaining to distribution of the 11214456Srpaulo * software without specific, written prior permission. M.I.T. makes 12214456Srpaulo * no representations about the suitability of this software for any 13214456Srpaulo * purpose. It is provided "as is" without express or implied 14214456Srpaulo * warranty. 15214456Srpaulo * 16214456Srpaulo * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 17214456Srpaulo * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 18214456Srpaulo * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19214456Srpaulo * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 20214456Srpaulo * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21214456Srpaulo * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22214456Srpaulo * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 23214456Srpaulo * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24214456Srpaulo * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25214456Srpaulo * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 26214456Srpaulo * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27214456Srpaulo * SUCH DAMAGE. 28214456Srpaulo * 29214456Srpaulo * $FreeBSD: head/sys/net/if_vlan.c 109711 2003-01-22 23:30:26Z fenner $ 30214456Srpaulo */ 31214456Srpaulo 32214456Srpaulo/* 33214456Srpaulo * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. 34214456Srpaulo * Might be extended some day to also handle IEEE 802.1p priority 35214456Srpaulo * tagging. This is sort of sneaky in the implementation, since 36214456Srpaulo * we need to pretend to be enough of an Ethernet implementation 37214456Srpaulo * to make arp work. The way we do this is by telling everyone 38214456Srpaulo * that we are an Ethernet, and then catch the packets that 39214456Srpaulo * ether_output() left on our output queue when it calls 40214456Srpaulo * if_start(), rewrite them for use by the real outgoing interface, 41214456Srpaulo * and ask it to send them. 42214456Srpaulo */ 43214456Srpaulo 44214456Srpaulo#include "opt_inet.h" 45214456Srpaulo 46214456Srpaulo#include <sys/param.h> 47214456Srpaulo#include <sys/kernel.h> 48214456Srpaulo#include <sys/malloc.h> 49214456Srpaulo#include <sys/mbuf.h> 50214456Srpaulo#include <sys/module.h> 51214456Srpaulo#include <sys/queue.h> 52214456Srpaulo#include <sys/socket.h> 53214456Srpaulo#include <sys/sockio.h> 54214456Srpaulo#include <sys/sysctl.h> 55214456Srpaulo#include <sys/systm.h> 56214456Srpaulo 57214456Srpaulo#include <net/bpf.h> 58214456Srpaulo#include <net/ethernet.h> 59214456Srpaulo#include <net/if.h> 60214456Srpaulo#include <net/if_arp.h> 61214456Srpaulo#include <net/if_dl.h> 62214456Srpaulo#include <net/if_types.h> 63214456Srpaulo#include <net/if_vlan_var.h> 64214456Srpaulo 65214456Srpaulo#ifdef INET 66214456Srpaulo#include <netinet/in.h> 67214456Srpaulo#include <netinet/if_ether.h> 68214456Srpaulo#endif 69214456Srpaulo 70214456Srpaulo#define VLANNAME "vlan" 71214456Srpaulo 72214456Srpaulostruct vlan_mc_entry { 73214456Srpaulo struct ether_addr mc_addr; 74214456Srpaulo SLIST_ENTRY(vlan_mc_entry) mc_entries; 75214456Srpaulo}; 76214456Srpaulo 77214456Srpaulostruct ifvlan { 78214456Srpaulo struct arpcom ifv_ac; /* make this an interface */ 79214456Srpaulo struct ifnet *ifv_p; /* parent inteface of this vlan */ 80214456Srpaulo struct ifv_linkmib { 81214456Srpaulo int ifvm_parent; 82214456Srpaulo int ifvm_encaplen; /* encapsulation length */ 83214456Srpaulo int ifvm_mtufudge; /* MTU fudged by this much */ 84214456Srpaulo int ifvm_mintu; /* min transmission unit */ 85214456Srpaulo u_int16_t ifvm_proto; /* encapsulation ethertype */ 86214456Srpaulo u_int16_t ifvm_tag; /* tag to apply on packets leaving if */ 87214456Srpaulo } ifv_mib; 88214456Srpaulo SLIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead; 89214456Srpaulo LIST_ENTRY(ifvlan) ifv_list; 90214456Srpaulo int ifv_flags; 91214456Srpaulo}; 92214456Srpaulo#define ifv_if ifv_ac.ac_if 93214456Srpaulo#define ifv_tag ifv_mib.ifvm_tag 94214456Srpaulo#define ifv_encaplen ifv_mib.ifvm_encaplen 95214456Srpaulo#define ifv_mtufudge ifv_mib.ifvm_mtufudge 96214456Srpaulo#define ifv_mintu ifv_mib.ifvm_mintu 97214456Srpaulo 98214456Srpaulo#define IFVF_PROMISC 0x01 /* promiscuous mode enabled */ 99214456Srpaulo 100214456SrpauloSYSCTL_DECL(_net_link); 101214456SrpauloSYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); 102214456SrpauloSYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); 103214456Srpaulo 104214456Srpaulostatic MALLOC_DEFINE(M_VLAN, "vlan", "802.1Q Virtual LAN Interface"); 105214456Srpaulostatic LIST_HEAD(, ifvlan) ifv_list; 106214456Srpaulo 107214456Srpaulostatic int vlan_clone_create(struct if_clone *, int); 108214456Srpaulostatic void vlan_clone_destroy(struct ifnet *); 109214456Srpaulostatic void vlan_start(struct ifnet *ifp); 110214456Srpaulostatic void vlan_ifinit(void *foo); 111214456Srpaulostatic void vlan_input(struct ifnet *ifp, struct mbuf *m); 112214456Srpaulostatic int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); 113214456Srpaulostatic int vlan_setmulti(struct ifnet *ifp); 114214456Srpaulostatic int vlan_unconfig(struct ifnet *ifp); 115214456Srpaulostatic int vlan_config(struct ifvlan *ifv, struct ifnet *p); 116214456Srpaulo 117214456Srpaulostruct if_clone vlan_cloner = IF_CLONE_INITIALIZER("vlan", 118214456Srpaulo vlan_clone_create, vlan_clone_destroy, 0, IF_MAXUNIT); 119214456Srpaulo 120214456Srpaulo/* 121214456Srpaulo * Program our multicast filter. What we're actually doing is 122214456Srpaulo * programming the multicast filter of the parent. This has the 123214456Srpaulo * side effect of causing the parent interface to receive multicast 124214456Srpaulo * traffic that it doesn't really want, which ends up being discarded 125214456Srpaulo * later by the upper protocol layers. Unfortunately, there's no way 126214456Srpaulo * to avoid this: there really is only one physical interface. 127214456Srpaulo */ 128214456Srpaulostatic int 129214456Srpaulovlan_setmulti(struct ifnet *ifp) 130214456Srpaulo{ 131214456Srpaulo struct ifnet *ifp_p; 132214456Srpaulo struct ifmultiaddr *ifma, *rifma = NULL; 133214456Srpaulo struct ifvlan *sc; 134214456Srpaulo struct vlan_mc_entry *mc = NULL; 135214456Srpaulo struct sockaddr_dl sdl; 136214456Srpaulo int error; 137214456Srpaulo 138214456Srpaulo /* Find the parent. */ 139214456Srpaulo sc = ifp->if_softc; 140214456Srpaulo ifp_p = sc->ifv_p; 141214456Srpaulo 142214456Srpaulo /* 143214456Srpaulo * If we don't have a parent, just remember the membership for 144214456Srpaulo * when we do. 145214456Srpaulo */ 146214456Srpaulo if (ifp_p == NULL) 147214456Srpaulo return(0); 148214456Srpaulo 149214456Srpaulo bzero((char *)&sdl, sizeof sdl); 150214456Srpaulo sdl.sdl_len = sizeof sdl; 151214456Srpaulo sdl.sdl_family = AF_LINK; 152214456Srpaulo sdl.sdl_index = ifp_p->if_index; 153214456Srpaulo sdl.sdl_type = IFT_ETHER; 154214456Srpaulo sdl.sdl_alen = ETHER_ADDR_LEN; 155214456Srpaulo 156214456Srpaulo /* First, remove any existing filter entries. */ 157214456Srpaulo while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) { 158214456Srpaulo mc = SLIST_FIRST(&sc->vlan_mc_listhead); 159214456Srpaulo bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 160214456Srpaulo error = if_delmulti(ifp_p, (struct sockaddr *)&sdl); 161214456Srpaulo if (error) 162214456Srpaulo return(error); 163214456Srpaulo SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); 164214456Srpaulo free(mc, M_VLAN); 165214456Srpaulo } 166214456Srpaulo 167214456Srpaulo /* Now program new ones. */ 168214456Srpaulo TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 169214456Srpaulo if (ifma->ifma_addr->sa_family != AF_LINK) 170214456Srpaulo continue; 171214456Srpaulo mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, 0); 172214456Srpaulo bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 173214456Srpaulo (char *)&mc->mc_addr, ETHER_ADDR_LEN); 174214456Srpaulo SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); 175214456Srpaulo bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 176214456Srpaulo LLADDR(&sdl), ETHER_ADDR_LEN); 177214456Srpaulo error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma); 178214456Srpaulo if (error) 179214456Srpaulo return(error); 180214456Srpaulo } 181214456Srpaulo 182214456Srpaulo return(0); 183214456Srpaulo} 184214456Srpaulo 185214456Srpaulo/* 186214456Srpaulo * VLAN support can be loaded as a module. The only place in the 187214456Srpaulo * system that's intimately aware of this is ether_input. We hook 188214456Srpaulo * into this code through vlan_input_p which is defined there and 189214456Srpaulo * set here. Noone else in the system should be aware of this so 190214456Srpaulo * we use an explicit reference here. 191214456Srpaulo * 192214456Srpaulo * NB: Noone should ever need to check if vlan_input_p is null or 193214456Srpaulo * not. This is because interfaces have a count of the number 194214456Srpaulo * of active vlans (if_nvlans) and this should never be bumped 195214456Srpaulo * except by vlan_config--which is in this module so therefore 196214456Srpaulo * the module must be loaded and vlan_input_p must be non-NULL. 197214456Srpaulo */ 198214456Srpauloextern void (*vlan_input_p)(struct ifnet *, struct mbuf *); 199214456Srpaulo 200214456Srpaulostatic int 201214456Srpaulovlan_modevent(module_t mod, int type, void *data) 202214456Srpaulo{ 203214456Srpaulo 204214456Srpaulo switch (type) { 205214456Srpaulo case MOD_LOAD: 206214456Srpaulo LIST_INIT(&ifv_list); 207214456Srpaulo vlan_input_p = vlan_input; 208214456Srpaulo if_clone_attach(&vlan_cloner); 209214456Srpaulo break; 210214456Srpaulo case MOD_UNLOAD: 211214456Srpaulo if_clone_detach(&vlan_cloner); 212214456Srpaulo vlan_input_p = NULL; 213214456Srpaulo while (!LIST_EMPTY(&ifv_list)) 214214456Srpaulo vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if); 215214456Srpaulo break; 216214456Srpaulo } 217214456Srpaulo return 0; 218214456Srpaulo} 219214456Srpaulo 220214456Srpaulostatic moduledata_t vlan_mod = { 221214456Srpaulo "if_vlan", 222214456Srpaulo vlan_modevent, 223214456Srpaulo 0 224214456Srpaulo}; 225214456Srpaulo 226214456SrpauloDECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 227214456Srpaulo 228214456Srpaulostatic int 229214456Srpaulovlan_clone_create(struct if_clone *ifc, int unit) 230214456Srpaulo{ 231214456Srpaulo struct ifvlan *ifv; 232214456Srpaulo struct ifnet *ifp; 233214456Srpaulo int s; 234214456Srpaulo 235214456Srpaulo ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_ZERO); 236214456Srpaulo ifp = &ifv->ifv_if; 237214456Srpaulo SLIST_INIT(&ifv->vlan_mc_listhead); 238214456Srpaulo 239214456Srpaulo s = splnet(); 240214456Srpaulo LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list); 241214456Srpaulo splx(s); 242214456Srpaulo 243214456Srpaulo ifp->if_softc = ifv; 244214456Srpaulo ifp->if_name = "vlan"; 245214456Srpaulo ifp->if_unit = unit; 246214456Srpaulo /* NB: flags are not set here */ 247214456Srpaulo ifp->if_linkmib = &ifv->ifv_mib; 248214456Srpaulo ifp->if_linkmiblen = sizeof ifv->ifv_mib; 249214456Srpaulo /* NB: mtu is not set here */ 250214456Srpaulo 251214456Srpaulo ifp->if_init = vlan_ifinit; 252214456Srpaulo ifp->if_start = vlan_start; 253214456Srpaulo ifp->if_ioctl = vlan_ioctl; 254214456Srpaulo ifp->if_snd.ifq_maxlen = ifqmaxlen; 255214456Srpaulo ether_ifattach(ifp, ifv->ifv_ac.ac_enaddr); 256214456Srpaulo /* Now undo some of the damage... */ 257214456Srpaulo ifp->if_baudrate = 0; 258214456Srpaulo ifp->if_type = IFT_L2VLAN; 259214456Srpaulo ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN; 260214456Srpaulo 261214456Srpaulo return (0); 262214456Srpaulo} 263214456Srpaulo 264214456Srpaulostatic void 265214456Srpaulovlan_clone_destroy(struct ifnet *ifp) 266214456Srpaulo{ 267214456Srpaulo struct ifvlan *ifv = ifp->if_softc; 268214456Srpaulo int s; 269214456Srpaulo 270214456Srpaulo s = splnet(); 271214456Srpaulo LIST_REMOVE(ifv, ifv_list); 272214456Srpaulo vlan_unconfig(ifp); 273214456Srpaulo splx(s); 274214456Srpaulo 275214456Srpaulo ether_ifdetach(ifp); 276214456Srpaulo 277214456Srpaulo free(ifv, M_VLAN); 278214456Srpaulo} 279214456Srpaulo 280214456Srpaulostatic void 281214456Srpaulovlan_ifinit(void *foo) 282214456Srpaulo{ 283214456Srpaulo return; 284214456Srpaulo} 285214456Srpaulo 286214456Srpaulostatic void 287214456Srpaulovlan_start(struct ifnet *ifp) 288214456Srpaulo{ 289214456Srpaulo struct ifvlan *ifv; 290214456Srpaulo struct ifnet *p; 291214456Srpaulo struct ether_vlan_header *evl; 292214456Srpaulo struct mbuf *m; 293214456Srpaulo 294214456Srpaulo ifv = ifp->if_softc; 295214456Srpaulo p = ifv->ifv_p; 296214456Srpaulo 297214456Srpaulo ifp->if_flags |= IFF_OACTIVE; 298214456Srpaulo for (;;) { 299214456Srpaulo IF_DEQUEUE(&ifp->if_snd, m); 300214456Srpaulo if (m == 0) 301214456Srpaulo break; 302214456Srpaulo BPF_MTAP(ifp, m); 303214456Srpaulo 304214456Srpaulo /* 305214456Srpaulo * Do not run parent's if_start() if the parent is not up, 306214456Srpaulo * or parent's driver will cause a system crash. 307214456Srpaulo */ 308214456Srpaulo if ((p->if_flags & (IFF_UP | IFF_RUNNING)) != 309214456Srpaulo (IFF_UP | IFF_RUNNING)) { 310214456Srpaulo m_freem(m); 311241235Sdelphij ifp->if_collisions++; 312214456Srpaulo continue; 313214456Srpaulo } 314214456Srpaulo 315214456Srpaulo /* 316214456Srpaulo * If underlying interface can do VLAN tag insertion itself, 317214456Srpaulo * just pass the packet along. However, we need some way to 318214456Srpaulo * tell the interface where the packet came from so that it 319214456Srpaulo * knows how to find the VLAN tag to use, so we attach a 320214456Srpaulo * packet tag that holds it. 321214456Srpaulo */ 322214456Srpaulo if (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) { 323214456Srpaulo struct m_tag *mtag = m_tag_alloc(MTAG_VLAN, 324214456Srpaulo MTAG_VLAN_TAG, 325214456Srpaulo sizeof (u_int), 326214456Srpaulo M_NOWAIT); 327214456Srpaulo if (mtag == NULL) { 328214456Srpaulo ifp->if_oerrors++; 329214456Srpaulo m_freem(m); 330214456Srpaulo continue; 331214456Srpaulo } 332214456Srpaulo *(u_int*)(mtag+1) = ifv->ifv_tag; 333214456Srpaulo m_tag_prepend(m, mtag); 334214456Srpaulo } else { 335214456Srpaulo M_PREPEND(m, ifv->ifv_encaplen, M_NOWAIT); 336214456Srpaulo if (m == NULL) { 337214456Srpaulo if_printf(ifp, "unable to prepend VLAN header"); 338214456Srpaulo ifp->if_ierrors++; 339214456Srpaulo continue; 340214456Srpaulo } 341214456Srpaulo /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ 342214456Srpaulo 343214456Srpaulo if (m->m_len < sizeof(*evl)) { 344214456Srpaulo m = m_pullup(m, sizeof(*evl)); 345214456Srpaulo if (m == NULL) { 346214456Srpaulo if_printf(ifp, 347214456Srpaulo "cannot pullup VLAN header"); 348214456Srpaulo ifp->if_ierrors++; 349214456Srpaulo continue; 350214456Srpaulo } 351214456Srpaulo } 352214456Srpaulo 353214456Srpaulo /* 354214456Srpaulo * Transform the Ethernet header into an Ethernet header 355214456Srpaulo * with 802.1Q encapsulation. 356214456Srpaulo */ 357214456Srpaulo bcopy(mtod(m, char *) + ifv->ifv_encaplen, 358214456Srpaulo mtod(m, char *), sizeof(struct ether_header)); 359214456Srpaulo evl = mtod(m, struct ether_vlan_header *); 360214456Srpaulo evl->evl_proto = evl->evl_encap_proto; 361214456Srpaulo evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 362214456Srpaulo evl->evl_tag = htons(ifv->ifv_tag); 363214456Srpaulo#ifdef DEBUG 364214456Srpaulo printf("vlan_start: %*D\n", (int)sizeof *evl, 365214456Srpaulo (unsigned char *)evl, ":"); 366214456Srpaulo#endif 367214456Srpaulo } 368214456Srpaulo 369214456Srpaulo /* 370214456Srpaulo * Send it, precisely as ether_output() would have. 371214456Srpaulo * We are already running at splimp. 372214456Srpaulo */ 373214456Srpaulo if (IF_HANDOFF(&p->if_snd, m, p)) 374214456Srpaulo ifp->if_opackets++; 375214456Srpaulo else 376214456Srpaulo ifp->if_oerrors++; 377214456Srpaulo } 378214456Srpaulo ifp->if_flags &= ~IFF_OACTIVE; 379214456Srpaulo 380214456Srpaulo return; 381214456Srpaulo} 382214456Srpaulo 383214456Srpaulostatic void 384214456Srpaulovlan_input(struct ifnet *ifp, struct mbuf *m) 385214456Srpaulo{ 386214456Srpaulo struct ether_vlan_header *evl; 387214456Srpaulo struct ifvlan *ifv; 388214456Srpaulo struct m_tag *mtag; 389214456Srpaulo u_int tag; 390214456Srpaulo 391214456Srpaulo mtag = m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL); 392214456Srpaulo if (mtag != NULL) { 393214456Srpaulo /* 394214456Srpaulo * Packet is tagged, m contains a normal 395214456Srpaulo * Ethernet frame; the tag is stored out-of-band. 396214456Srpaulo */ 397214456Srpaulo tag = *(u_int*)(mtag+1); 398214456Srpaulo m_tag_delete(m, mtag); 399214456Srpaulo } else { 400214456Srpaulo switch (ifp->if_type) { 401214456Srpaulo case IFT_ETHER: 402214456Srpaulo if (m->m_len < sizeof (*evl) && 403214456Srpaulo (m = m_pullup(m, sizeof (*evl))) == NULL) { 404214456Srpaulo if_printf(ifp, "cannot pullup VLAN header\n"); 405214456Srpaulo return; 406214456Srpaulo } 407214456Srpaulo evl = mtod(m, struct ether_vlan_header *); 408214456Srpaulo KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN, 409214456Srpaulo ("vlan_input: bad encapsulated protocols (%u)", 410214456Srpaulo ntohs(evl->evl_encap_proto))); 411214456Srpaulo 412214456Srpaulo tag = EVL_VLANOFTAG(ntohs(evl->evl_tag)); 413214456Srpaulo 414214456Srpaulo /* 415214456Srpaulo * Restore the original ethertype. We'll remove 416214456Srpaulo * the encapsulation after we've found the vlan 417214456Srpaulo * interface corresponding to the tag. 418214456Srpaulo */ 419214456Srpaulo evl->evl_encap_proto = evl->evl_proto; 420214456Srpaulo break; 421214456Srpaulo default: 422214456Srpaulo tag = (u_int) -1; 423214456Srpaulo#ifdef DIAGNOSTIC 424214456Srpaulo panic("vlan_input: unsupported if type %u", ifp->if_type); 425214456Srpaulo#endif 426214456Srpaulo break; 427214456Srpaulo } 428214456Srpaulo } 429214456Srpaulo 430214456Srpaulo for (ifv = LIST_FIRST(&ifv_list); ifv != NULL; 431214456Srpaulo ifv = LIST_NEXT(ifv, ifv_list)) 432214456Srpaulo if (ifp == ifv->ifv_p && tag == ifv->ifv_tag) 433214456Srpaulo break; 434214456Srpaulo 435214456Srpaulo if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 436214456Srpaulo m_freem(m); 437214456Srpaulo ifp->if_noproto++; 438214456Srpaulo return; 439214456Srpaulo } 440214456Srpaulo 441214456Srpaulo if (mtag == NULL) { 442214456Srpaulo /* 443214456Srpaulo * Packet had an in-line encapsulation header; 444214456Srpaulo * remove it. The original header has already 445214456Srpaulo * been fixed up above. 446214456Srpaulo */ 447214456Srpaulo bcopy(mtod(m, caddr_t), 448214456Srpaulo mtod(m, caddr_t) + ETHER_VLAN_ENCAP_LEN, 449214456Srpaulo sizeof (struct ether_header)); 450214456Srpaulo m_adj(m, ETHER_VLAN_ENCAP_LEN); 451214456Srpaulo } 452214456Srpaulo 453214456Srpaulo m->m_pkthdr.rcvif = &ifv->ifv_if; 454214456Srpaulo ifv->ifv_if.if_ipackets++; 455214456Srpaulo 456214456Srpaulo /* Pass it back through the parent's input routine. */ 457214456Srpaulo (*ifp->if_input)(&ifv->ifv_if, m); 458214456Srpaulo} 459214456Srpaulo 460214456Srpaulostatic int 461214456Srpaulovlan_config(struct ifvlan *ifv, struct ifnet *p) 462214456Srpaulo{ 463214456Srpaulo struct ifaddr *ifa1, *ifa2; 464214456Srpaulo struct sockaddr_dl *sdl1, *sdl2; 465214456Srpaulo 466214456Srpaulo if (p->if_data.ifi_type != IFT_ETHER) 467214456Srpaulo return EPROTONOSUPPORT; 468214456Srpaulo if (ifv->ifv_p) 469214456Srpaulo return EBUSY; 470214456Srpaulo 471214456Srpaulo ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; 472214456Srpaulo ifv->ifv_mintu = ETHERMIN; 473214456Srpaulo ifv->ifv_flags = 0; 474214456Srpaulo 475214456Srpaulo /* 476214456Srpaulo * If the parent supports the VLAN_MTU capability, 477214456Srpaulo * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames, 478214456Srpaulo * enable it. 479214456Srpaulo */ 480214456Srpaulo p->if_nvlans++; 481214456Srpaulo if (p->if_nvlans == 1 && (p->if_capabilities & IFCAP_VLAN_MTU) != 0) { 482214456Srpaulo /* 483214456Srpaulo * Enable Tx/Rx of VLAN-sized frames. 484214456Srpaulo */ 485214456Srpaulo p->if_capenable |= IFCAP_VLAN_MTU; 486214456Srpaulo if (p->if_flags & IFF_UP) { 487214456Srpaulo struct ifreq ifr; 488214456Srpaulo int error; 489214456Srpaulo 490214456Srpaulo ifr.ifr_flags = p->if_flags; 491214456Srpaulo error = (*p->if_ioctl)(p, SIOCSIFFLAGS, 492214456Srpaulo (caddr_t) &ifr); 493214456Srpaulo if (error) { 494214456Srpaulo p->if_nvlans--; 495214456Srpaulo if (p->if_nvlans == 0) 496214456Srpaulo p->if_capenable &= ~IFCAP_VLAN_MTU; 497214456Srpaulo return (error); 498214456Srpaulo } 499214456Srpaulo } 500214456Srpaulo ifv->ifv_mtufudge = 0; 501214456Srpaulo } else if ((p->if_capabilities & IFCAP_VLAN_MTU) == 0) { 502214456Srpaulo /* 503214456Srpaulo * Fudge the MTU by the encapsulation size. This 504214456Srpaulo * makes us incompatible with strictly compliant 505214456Srpaulo * 802.1Q implementations, but allows us to use 506214456Srpaulo * the feature with other NetBSD implementations, 507214456Srpaulo * which might still be useful. 508214456Srpaulo */ 509214456Srpaulo ifv->ifv_mtufudge = ifv->ifv_encaplen; 510214456Srpaulo } 511214456Srpaulo 512214456Srpaulo ifv->ifv_p = p; 513214456Srpaulo ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge; 514214456Srpaulo /* 515214456Srpaulo * Copy only a selected subset of flags from the parent. 516214456Srpaulo * Other flags are none of our business. 517214456Srpaulo */ 518214456Srpaulo ifv->ifv_if.if_flags = (p->if_flags & 519214456Srpaulo (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT)); 520214456Srpaulo 521214456Srpaulo /* 522214456Srpaulo * If the parent interface can do hardware-assisted 523214456Srpaulo * VLAN encapsulation, then propagate its hardware- 524214456Srpaulo * assisted checksumming flags. 525214456Srpaulo */ 526214456Srpaulo if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) 527214456Srpaulo ifv->ifv_if.if_capabilities |= p->if_capabilities & IFCAP_HWCSUM; 528214456Srpaulo 529214456Srpaulo /* 530214456Srpaulo * Set up our ``Ethernet address'' to reflect the underlying 531214456Srpaulo * physical interface's. 532214456Srpaulo */ 533214456Srpaulo ifa1 = ifaddr_byindex(ifv->ifv_if.if_index); 534214456Srpaulo ifa2 = ifaddr_byindex(p->if_index); 535214456Srpaulo sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; 536214456Srpaulo sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; 537214456Srpaulo sdl1->sdl_type = IFT_ETHER; 538214456Srpaulo sdl1->sdl_alen = ETHER_ADDR_LEN; 539214456Srpaulo bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); 540214456Srpaulo bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 541214456Srpaulo 542214456Srpaulo /* 543214456Srpaulo * Configure multicast addresses that may already be 544214456Srpaulo * joined on the vlan device. 545214456Srpaulo */ 546214456Srpaulo (void)vlan_setmulti(&ifv->ifv_if); 547214456Srpaulo 548214456Srpaulo return 0; 549214456Srpaulo} 550214456Srpaulo 551214456Srpaulostatic int 552214456Srpaulovlan_unconfig(struct ifnet *ifp) 553214456Srpaulo{ 554214456Srpaulo struct ifaddr *ifa; 555214456Srpaulo struct sockaddr_dl *sdl; 556214456Srpaulo struct vlan_mc_entry *mc; 557214456Srpaulo struct ifvlan *ifv; 558214456Srpaulo struct ifnet *p; 559214456Srpaulo int error; 560214456Srpaulo 561214456Srpaulo ifv = ifp->if_softc; 562214456Srpaulo p = ifv->ifv_p; 563214456Srpaulo 564214456Srpaulo if (p) { 565214456Srpaulo struct sockaddr_dl sdl; 566214456Srpaulo 567214456Srpaulo /* 568214456Srpaulo * Since the interface is being unconfigured, we need to 569214456Srpaulo * empty the list of multicast groups that we may have joined 570214456Srpaulo * while we were alive from the parent's list. 571214456Srpaulo */ 572214456Srpaulo bzero((char *)&sdl, sizeof sdl); 573214456Srpaulo sdl.sdl_len = sizeof sdl; 574214456Srpaulo sdl.sdl_family = AF_LINK; 575214456Srpaulo sdl.sdl_index = p->if_index; 576214456Srpaulo sdl.sdl_type = IFT_ETHER; 577214456Srpaulo sdl.sdl_alen = ETHER_ADDR_LEN; 578214456Srpaulo 579214456Srpaulo while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) { 580214456Srpaulo mc = SLIST_FIRST(&ifv->vlan_mc_listhead); 581214456Srpaulo bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 582214456Srpaulo error = if_delmulti(p, (struct sockaddr *)&sdl); 583214456Srpaulo if (error) 584214456Srpaulo return(error); 585214456Srpaulo SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); 586214456Srpaulo free(mc, M_VLAN); 587214456Srpaulo } 588235530Sdelphij 589214456Srpaulo p->if_nvlans--; 590214456Srpaulo if (p->if_nvlans == 0) { 591214456Srpaulo /* 592214456Srpaulo * Disable Tx/Rx of VLAN-sized frames. 593214456Srpaulo */ 594214456Srpaulo p->if_capenable &= ~IFCAP_VLAN_MTU; 595214456Srpaulo if (p->if_flags & IFF_UP) { 596214456Srpaulo struct ifreq ifr; 597214456Srpaulo 598214456Srpaulo ifr.ifr_flags = p->if_flags; 599214456Srpaulo (*p->if_ioctl)(p, SIOCSIFFLAGS, (caddr_t) &ifr); 600214456Srpaulo } 601214456Srpaulo } 602214456Srpaulo } 603214456Srpaulo 604214456Srpaulo /* Disconnect from parent. */ 605214456Srpaulo ifv->ifv_p = NULL; 606214456Srpaulo ifv->ifv_if.if_mtu = ETHERMTU; /* XXX why not 0? */ 607214456Srpaulo ifv->ifv_flags = 0; 608214456Srpaulo 609214456Srpaulo /* Clear our MAC address. */ 610214456Srpaulo ifa = ifaddr_byindex(ifv->ifv_if.if_index); 611214456Srpaulo sdl = (struct sockaddr_dl *)ifa->ifa_addr; 612214456Srpaulo sdl->sdl_type = IFT_ETHER; 613214456Srpaulo sdl->sdl_alen = ETHER_ADDR_LEN; 614214456Srpaulo bzero(LLADDR(sdl), ETHER_ADDR_LEN); 615214456Srpaulo bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 616214456Srpaulo 617214456Srpaulo return 0; 618214456Srpaulo} 619214456Srpaulo 620214456Srpaulostatic int 621214456Srpaulovlan_set_promisc(struct ifnet *ifp) 622214456Srpaulo{ 623214456Srpaulo struct ifvlan *ifv = ifp->if_softc; 624214456Srpaulo int error = 0; 625214456Srpaulo 626214456Srpaulo if ((ifp->if_flags & IFF_PROMISC) != 0) { 627214456Srpaulo if ((ifv->ifv_flags & IFVF_PROMISC) == 0) { 628214456Srpaulo error = ifpromisc(ifv->ifv_p, 1); 629214456Srpaulo if (error == 0) 630214456Srpaulo ifv->ifv_flags |= IFVF_PROMISC; 631214456Srpaulo } 632214456Srpaulo } else { 633214456Srpaulo if ((ifv->ifv_flags & IFVF_PROMISC) != 0) { 634214456Srpaulo error = ifpromisc(ifv->ifv_p, 0); 635214456Srpaulo if (error == 0) 636214456Srpaulo ifv->ifv_flags &= ~IFVF_PROMISC; 637214456Srpaulo } 638214456Srpaulo } 639214456Srpaulo 640214456Srpaulo return (error); 641214456Srpaulo} 642214456Srpaulo 643214456Srpaulostatic int 644214456Srpaulovlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 645214456Srpaulo{ 646214456Srpaulo struct ifaddr *ifa; 647214456Srpaulo struct ifnet *p; 648214456Srpaulo struct ifreq *ifr; 649214456Srpaulo struct ifvlan *ifv; 650214456Srpaulo struct vlanreq vlr; 651214456Srpaulo int error = 0; 652214456Srpaulo 653214456Srpaulo ifr = (struct ifreq *)data; 654214456Srpaulo ifa = (struct ifaddr *)data; 655214456Srpaulo ifv = ifp->if_softc; 656214456Srpaulo 657214456Srpaulo switch (cmd) { 658214456Srpaulo case SIOCSIFADDR: 659214456Srpaulo ifp->if_flags |= IFF_UP; 660214456Srpaulo 661214456Srpaulo switch (ifa->ifa_addr->sa_family) { 662214456Srpaulo#ifdef INET 663214456Srpaulo case AF_INET: 664214456Srpaulo arp_ifinit(&ifv->ifv_if, ifa); 665214456Srpaulo break; 666214456Srpaulo#endif 667214456Srpaulo default: 668214456Srpaulo break; 669214456Srpaulo } 670214456Srpaulo break; 671214456Srpaulo 672214456Srpaulo case SIOCGIFADDR: 673214456Srpaulo { 674214456Srpaulo struct sockaddr *sa; 675214456Srpaulo 676214456Srpaulo sa = (struct sockaddr *) &ifr->ifr_data; 677214456Srpaulo bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, 678214456Srpaulo (caddr_t) sa->sa_data, ETHER_ADDR_LEN); 679214456Srpaulo } 680 break; 681 682 case SIOCGIFMEDIA: 683 if (ifv->ifv_p != NULL) { 684 error = (ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCGIFMEDIA, data); 685 /* Limit the result to the parent's current config. */ 686 if (error == 0) { 687 struct ifmediareq *ifmr; 688 689 ifmr = (struct ifmediareq *) data; 690 if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) { 691 ifmr->ifm_count = 1; 692 error = copyout(&ifmr->ifm_current, 693 ifmr->ifm_ulist, 694 sizeof(int)); 695 } 696 } 697 } else 698 error = EINVAL; 699 break; 700 701 case SIOCSIFMEDIA: 702 error = EINVAL; 703 break; 704 705 case SIOCSIFMTU: 706 /* 707 * Set the interface MTU. 708 */ 709 if (ifv->ifv_p != NULL) { 710 if (ifr->ifr_mtu > 711 (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) || 712 ifr->ifr_mtu < 713 (ifv->ifv_mintu - ifv->ifv_mtufudge)) 714 error = EINVAL; 715 else 716 ifp->if_mtu = ifr->ifr_mtu; 717 } else 718 error = EINVAL; 719 break; 720 721 case SIOCSETVLAN: 722 error = copyin(ifr->ifr_data, &vlr, sizeof vlr); 723 if (error) 724 break; 725 if (vlr.vlr_parent[0] == '\0') { 726 vlan_unconfig(ifp); 727 if (ifp->if_flags & IFF_UP) { 728 int s = splimp(); 729 if_down(ifp); 730 splx(s); 731 } 732 ifp->if_flags &= ~IFF_RUNNING; 733 break; 734 } 735 p = ifunit(vlr.vlr_parent); 736 if (p == 0) { 737 error = ENOENT; 738 break; 739 } 740 error = vlan_config(ifv, p); 741 if (error) 742 break; 743 ifv->ifv_tag = vlr.vlr_tag; 744 ifp->if_flags |= IFF_RUNNING; 745 746 /* Update promiscuous mode, if necessary. */ 747 vlan_set_promisc(ifp); 748 break; 749 750 case SIOCGETVLAN: 751 bzero(&vlr, sizeof vlr); 752 if (ifv->ifv_p) { 753 snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), 754 "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit); 755 vlr.vlr_tag = ifv->ifv_tag; 756 } 757 error = copyout(&vlr, ifr->ifr_data, sizeof vlr); 758 break; 759 760 case SIOCSIFFLAGS: 761 /* 762 * For promiscuous mode, we enable promiscuous mode on 763 * the parent if we need promiscuous on the VLAN interface. 764 */ 765 if (ifv->ifv_p != NULL) 766 error = vlan_set_promisc(ifp); 767 break; 768 769 case SIOCADDMULTI: 770 case SIOCDELMULTI: 771 error = vlan_setmulti(ifp); 772 break; 773 default: 774 error = EINVAL; 775 } 776 return error; 777} 778