if_vlan.c revision 86749
1219820Sjeff/* 2219820Sjeff * Copyright 1998 Massachusetts Institute of Technology 3219820Sjeff * 4219820Sjeff * Permission to use, copy, modify, and distribute this software and 5219820Sjeff * its documentation for any purpose and without fee is hereby 6219820Sjeff * granted, provided that both the above copyright notice and this 7219820Sjeff * permission notice appear in all copies, that both the above 8219820Sjeff * copyright notice and this permission notice appear in all 9219820Sjeff * supporting documentation, and that the name of M.I.T. not be used 10219820Sjeff * in advertising or publicity pertaining to distribution of the 11219820Sjeff * software without specific, written prior permission. M.I.T. makes 12219820Sjeff * no representations about the suitability of this software for any 13219820Sjeff * purpose. It is provided "as is" without express or implied 14219820Sjeff * warranty. 15219820Sjeff * 16219820Sjeff * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 17219820Sjeff * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 18219820Sjeff * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19219820Sjeff * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 20219820Sjeff * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21219820Sjeff * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22219820Sjeff * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 23219820Sjeff * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24219820Sjeff * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25219820Sjeff * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 26219820Sjeff * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27219820Sjeff * SUCH DAMAGE. 28219820Sjeff * 29219820Sjeff * $FreeBSD: head/sys/net/if_vlan.c 86749 2001-11-21 20:29:08Z arr $ 30219820Sjeff */ 31219820Sjeff 32219820Sjeff/* 33219820Sjeff * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. 34219820Sjeff * Might be extended some day to also handle IEEE 802.1p priority 35219820Sjeff * tagging. This is sort of sneaky in the implementation, since 36219820Sjeff * we need to pretend to be enough of an Ethernet implementation 37219820Sjeff * to make arp work. The way we do this is by telling everyone 38219820Sjeff * that we are an Ethernet, and then catch the packets that 39219820Sjeff * ether_output() left on our output queue when it calls 40219820Sjeff * if_start(), rewrite them for use by the real outgoing interface, 41219820Sjeff * and ask it to send them. 42219820Sjeff * 43219820Sjeff * 44219820Sjeff * XXX It's incorrect to assume that we must always kludge up 45219820Sjeff * headers on the physical device's behalf: some devices support 46219820Sjeff * VLAN tag insertion and extraction in firmware. For these cases, 47219820Sjeff * one can change the behavior of the vlan interface by setting 48219820Sjeff * the LINK0 flag on it (that is setting the vlan interface's LINK0 49219820Sjeff * flag, _not_ the parent's LINK0 flag; we try to leave the parent 50219820Sjeff * alone). If the interface has the LINK0 flag set, then it will 51219820Sjeff * not modify the ethernet header on output, because the parent 52219820Sjeff * can do that for itself. On input, the parent can call vlan_input_tag() 53275448Shselasky * directly in order to supply us with an incoming mbuf and the vlan 54219820Sjeff * tag value that goes with it. 55219820Sjeff */ 56219820Sjeff 57219820Sjeff#include "opt_inet.h" 58219820Sjeff 59219820Sjeff#include <sys/param.h> 60219820Sjeff#include <sys/kernel.h> 61219820Sjeff#include <sys/malloc.h> 62219820Sjeff#include <sys/mbuf.h> 63219820Sjeff#include <sys/module.h> 64219820Sjeff#include <sys/queue.h> 65219820Sjeff#include <sys/socket.h> 66219820Sjeff#include <sys/sockio.h> 67219820Sjeff#include <sys/sysctl.h> 68219820Sjeff#include <sys/systm.h> 69219820Sjeff#include <machine/bus.h> /* XXX: Shouldn't really be required! */ 70219820Sjeff#include <sys/rman.h> 71219820Sjeff 72219820Sjeff#include <net/bpf.h> 73219820Sjeff#include <net/ethernet.h> 74219820Sjeff#include <net/if.h> 75219820Sjeff#include <net/if_arp.h> 76219820Sjeff#include <net/if_dl.h> 77219820Sjeff#include <net/if_types.h> 78219820Sjeff#include <net/if_vlan_var.h> 79219820Sjeff 80219820Sjeff#ifdef INET 81219820Sjeff#include <netinet/in.h> 82219820Sjeff#include <netinet/if_ether.h> 83219820Sjeff#endif 84219820Sjeff 85219820Sjeff#define VLANNAME "vlan" 86219820Sjeff#define VLAN_MAXUNIT 0x7fff /* ifp->if_unit is only 15 bits */ 87219820Sjeff 88219820SjeffSYSCTL_DECL(_net_link); 89219820SjeffSYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); 90219820SjeffSYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); 91219820Sjeff 92219820Sjeffstatic MALLOC_DEFINE(M_VLAN, "vlan", "802.1Q Virtual LAN Interface"); 93219820Sjeffstatic struct rman vlanunits[1]; 94219820Sjeffstatic LIST_HEAD(, ifvlan) ifv_list; 95219820Sjeff 96219820Sjeffstatic int vlan_clone_create(struct if_clone *, int *); 97219820Sjeffstatic void vlan_clone_destroy(struct ifnet *); 98219820Sjeffstatic void vlan_start(struct ifnet *ifp); 99219820Sjeffstatic void vlan_ifinit(void *foo); 100219820Sjeffstatic int vlan_input(struct ether_header *eh, struct mbuf *m); 101219820Sjeffstatic int vlan_input_tag(struct ether_header *eh, struct mbuf *m, 102219820Sjeff u_int16_t t); 103219820Sjeffstatic int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); 104219820Sjeffstatic int vlan_setmulti(struct ifnet *ifp); 105219820Sjeffstatic int vlan_unconfig(struct ifnet *ifp); 106219820Sjeffstatic int vlan_config(struct ifvlan *ifv, struct ifnet *p); 107219820Sjeff 108219820Sjeffstruct if_clone vlan_cloner = 109219820Sjeff IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy); 110219820Sjeff 111219820Sjeff/* 112219820Sjeff * Program our multicast filter. What we're actually doing is 113219820Sjeff * programming the multicast filter of the parent. This has the 114219820Sjeff * side effect of causing the parent interface to receive multicast 115219820Sjeff * traffic that it doesn't really want, which ends up being discarded 116219820Sjeff * later by the upper protocol layers. Unfortunately, there's no way 117219820Sjeff * to avoid this: there really is only one physical interface. 118219820Sjeff */ 119219820Sjeffstatic int 120219820Sjeffvlan_setmulti(struct ifnet *ifp) 121219820Sjeff{ 122219820Sjeff struct ifnet *ifp_p; 123219820Sjeff struct ifmultiaddr *ifma, *rifma = NULL; 124219820Sjeff struct ifvlan *sc; 125219820Sjeff struct vlan_mc_entry *mc = NULL; 126219820Sjeff struct sockaddr_dl sdl; 127219820Sjeff int error; 128219820Sjeff 129219820Sjeff /* Find the parent. */ 130219820Sjeff sc = ifp->if_softc; 131219820Sjeff ifp_p = sc->ifv_p; 132219820Sjeff 133219820Sjeff /* 134219820Sjeff * If we don't have a parent, just remember the membership for 135219820Sjeff * when we do. 136219820Sjeff */ 137219820Sjeff if (ifp_p == NULL) 138219820Sjeff return(0); 139219820Sjeff 140219820Sjeff bzero((char *)&sdl, sizeof sdl); 141219820Sjeff sdl.sdl_len = sizeof sdl; 142219820Sjeff sdl.sdl_family = AF_LINK; 143219820Sjeff sdl.sdl_index = ifp_p->if_index; 144219820Sjeff sdl.sdl_type = IFT_ETHER; 145219820Sjeff sdl.sdl_alen = ETHER_ADDR_LEN; 146219820Sjeff 147219820Sjeff /* First, remove any existing filter entries. */ 148219820Sjeff while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) { 149219820Sjeff mc = SLIST_FIRST(&sc->vlan_mc_listhead); 150219820Sjeff bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 151219820Sjeff error = if_delmulti(ifp_p, (struct sockaddr *)&sdl); 152219820Sjeff if (error) 153219820Sjeff return(error); 154219820Sjeff SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); 155219820Sjeff free(mc, M_VLAN); 156219820Sjeff } 157219820Sjeff 158219820Sjeff /* Now program new ones. */ 159219820Sjeff TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 160219820Sjeff if (ifma->ifma_addr->sa_family != AF_LINK) 161219820Sjeff continue; 162219820Sjeff mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_WAITOK); 163219820Sjeff bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 164219820Sjeff (char *)&mc->mc_addr, ETHER_ADDR_LEN); 165219820Sjeff SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); 166219820Sjeff bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 167219820Sjeff LLADDR(&sdl), ETHER_ADDR_LEN); 168219820Sjeff error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma); 169219820Sjeff if (error) 170219820Sjeff return(error); 171219820Sjeff } 172219820Sjeff 173219820Sjeff return(0); 174219820Sjeff} 175219820Sjeff 176219820Sjeffstatic int 177219820Sjeffvlan_modevent(module_t mod, int type, void *data) 178219820Sjeff{ 179219820Sjeff int err; 180219820Sjeff 181219820Sjeff switch (type) { 182219820Sjeff case MOD_LOAD: 183219820Sjeff vlanunits->rm_type = RMAN_ARRAY; 184219820Sjeff vlanunits->rm_descr = "configurable if_vlan units"; 185219820Sjeff err = rman_init(vlanunits); 186219820Sjeff if (err != 0) 187219820Sjeff return (err); 188219820Sjeff err = rman_manage_region(vlanunits, 0, VLAN_MAXUNIT); 189219820Sjeff if (err != 0) { 190219820Sjeff printf("%s: vlanunits: rman_manage_region: Failed %d\n", 191219820Sjeff VLANNAME, err); 192219820Sjeff rman_fini(vlanunits); 193219820Sjeff return (err); 194219820Sjeff } 195219820Sjeff LIST_INIT(&ifv_list); 196219820Sjeff vlan_input_p = vlan_input; 197219820Sjeff vlan_input_tag_p = vlan_input_tag; 198219820Sjeff if_clone_attach(&vlan_cloner); 199219820Sjeff break; 200219820Sjeff case MOD_UNLOAD: 201219820Sjeff if_clone_detach(&vlan_cloner); 202219820Sjeff vlan_input_p = NULL; 203219820Sjeff vlan_input_tag_p = NULL; 204219820Sjeff while (!LIST_EMPTY(&ifv_list)) 205219820Sjeff vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if); 206219820Sjeff err = rman_fini(vlanunits); 207219820Sjeff if (err != 0) 208219820Sjeff return (err); 209219820Sjeff break; 210219820Sjeff } 211219820Sjeff return 0; 212219820Sjeff} 213219820Sjeff 214219820Sjeffstatic moduledata_t vlan_mod = { 215219820Sjeff "if_vlan", 216219820Sjeff vlan_modevent, 217219820Sjeff 0 218219820Sjeff}; 219219820Sjeff 220219820SjeffDECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 221219820Sjeff 222219820Sjeffstatic int 223219820Sjeffvlan_clone_create(struct if_clone *ifc, int *unit) 224219820Sjeff{ 225219820Sjeff struct resource *r; 226219820Sjeff struct ifvlan *ifv; 227219820Sjeff struct ifnet *ifp; 228219820Sjeff int s; 229219820Sjeff 230219820Sjeff if (*unit > VLAN_MAXUNIT) 231219820Sjeff return (ENXIO); 232219820Sjeff 233219820Sjeff if (*unit < 0) { 234219820Sjeff r = rman_reserve_resource(vlanunits, 0, VLAN_MAXUNIT, 1, 235219820Sjeff RF_ALLOCATED | RF_ACTIVE, NULL); 236219820Sjeff if (r == NULL) 237219820Sjeff return (ENOSPC); 238219820Sjeff *unit = rman_get_start(r); 239219820Sjeff } else { 240219820Sjeff r = rman_reserve_resource(vlanunits, *unit, *unit, 1, 241219820Sjeff RF_ALLOCATED | RF_ACTIVE, NULL); 242219820Sjeff if (r == NULL) 243219820Sjeff return (EEXIST); 244219820Sjeff } 245219820Sjeff 246219820Sjeff ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO); 247219820Sjeff ifp = &ifv->ifv_if; 248219820Sjeff SLIST_INIT(&ifv->vlan_mc_listhead); 249219820Sjeff 250219820Sjeff s = splnet(); 251219820Sjeff LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list); 252219820Sjeff splx(s); 253219820Sjeff 254219820Sjeff ifp->if_softc = ifv; 255219820Sjeff ifp->if_name = "vlan"; 256219820Sjeff ifp->if_unit = *unit; 257219820Sjeff ifv->r_unit = r; 258219820Sjeff /* NB: flags are not set here */ 259219820Sjeff ifp->if_linkmib = &ifv->ifv_mib; 260219820Sjeff ifp->if_linkmiblen = sizeof ifv->ifv_mib; 261219820Sjeff /* NB: mtu is not set here */ 262219820Sjeff 263219820Sjeff ifp->if_init = vlan_ifinit; 264219820Sjeff ifp->if_start = vlan_start; 265219820Sjeff ifp->if_ioctl = vlan_ioctl; 266219820Sjeff ifp->if_output = ether_output; 267219820Sjeff ifp->if_snd.ifq_maxlen = ifqmaxlen; 268219820Sjeff ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 269219820Sjeff /* Now undo some of the damage... */ 270219820Sjeff ifp->if_baudrate = 0; 271219820Sjeff ifp->if_data.ifi_type = IFT_L2VLAN; 272219820Sjeff ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN; 273219820Sjeff 274219820Sjeff return (0); 275219820Sjeff} 276219820Sjeff 277219820Sjeffstatic void 278219820Sjeffvlan_clone_destroy(struct ifnet *ifp) 279219820Sjeff{ 280219820Sjeff struct ifvlan *ifv = ifp->if_softc; 281219820Sjeff int s; 282219820Sjeff int err; 283219820Sjeff 284219820Sjeff s = splnet(); 285219820Sjeff LIST_REMOVE(ifv, ifv_list); 286219820Sjeff vlan_unconfig(ifp); 287219820Sjeff splx(s); 288219820Sjeff 289219820Sjeff ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 290219820Sjeff 291219820Sjeff err = rman_release_resource(ifv->r_unit); 292219820Sjeff KASSERT(err == 0, ("Unexpected error freeing resource")); 293219820Sjeff free(ifv, M_VLAN); 294219820Sjeff} 295219820Sjeff 296219820Sjeffstatic void 297219820Sjeffvlan_ifinit(void *foo) 298219820Sjeff{ 299219820Sjeff return; 300219820Sjeff} 301219820Sjeff 302219820Sjeffstatic void 303219820Sjeffvlan_start(struct ifnet *ifp) 304219820Sjeff{ 305219820Sjeff struct ifvlan *ifv; 306219820Sjeff struct ifnet *p; 307219820Sjeff struct ether_vlan_header *evl; 308219820Sjeff struct mbuf *m; 309219820Sjeff 310219820Sjeff ifv = ifp->if_softc; 311219820Sjeff p = ifv->ifv_p; 312219820Sjeff 313219820Sjeff ifp->if_flags |= IFF_OACTIVE; 314219820Sjeff for (;;) { 315219820Sjeff IF_DEQUEUE(&ifp->if_snd, m); 316219820Sjeff if (m == 0) 317219820Sjeff break; 318219820Sjeff if (ifp->if_bpf) 319219820Sjeff bpf_mtap(ifp, m); 320219820Sjeff 321219820Sjeff /* 322219820Sjeff * Do not run parent's if_start() if the parent is not up, 323219820Sjeff * or parent's driver will cause a system crash. 324219820Sjeff */ 325219820Sjeff if ((p->if_flags & (IFF_UP | IFF_RUNNING)) != 326219820Sjeff (IFF_UP | IFF_RUNNING)) { 327219820Sjeff m_freem(m); 328219820Sjeff ifp->if_data.ifi_collisions++; 329219820Sjeff continue; 330219820Sjeff } 331219820Sjeff 332219820Sjeff /* 333219820Sjeff * If the LINK0 flag is set, it means the underlying interface 334219820Sjeff * can do VLAN tag insertion itself and doesn't require us to 335219820Sjeff * create a special header for it. In this case, we just pass 336219820Sjeff * the packet along. However, we need some way to tell the 337219820Sjeff * interface where the packet came from so that it knows how 338219820Sjeff * to find the VLAN tag to use, so we set the rcvif in the 339219820Sjeff * mbuf header to our ifnet. 340219820Sjeff * 341219820Sjeff * Note: we also set the M_PROTO1 flag in the mbuf to let 342219820Sjeff * the parent driver know that the rcvif pointer is really 343219820Sjeff * valid. We need to do this because sometimes mbufs will 344219820Sjeff * be allocated by other parts of the system that contain 345219820Sjeff * garbage in the rcvif pointer. Using the M_PROTO1 flag 346219820Sjeff * lets the driver perform a proper sanity check and avoid 347219820Sjeff * following potentially bogus rcvif pointers off into 348219820Sjeff * never-never land. 349219820Sjeff */ 350219820Sjeff if (ifp->if_flags & IFF_LINK0) { 351219820Sjeff m->m_pkthdr.rcvif = ifp; 352219820Sjeff m->m_flags |= M_PROTO1; 353219820Sjeff } else { 354219820Sjeff M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT); 355219820Sjeff if (m == NULL) { 356219820Sjeff printf("vlan%d: M_PREPEND failed", ifp->if_unit); 357219820Sjeff ifp->if_ierrors++; 358219820Sjeff continue; 359219820Sjeff } 360219820Sjeff /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ 361219820Sjeff 362219820Sjeff m = m_pullup(m, ETHER_HDR_LEN + EVL_ENCAPLEN); 363219820Sjeff if (m == NULL) { 364219820Sjeff printf("vlan%d: m_pullup failed", ifp->if_unit); 365219820Sjeff ifp->if_ierrors++; 366219820Sjeff continue; 367219820Sjeff } 368219820Sjeff 369219820Sjeff /* 370219820Sjeff * Transform the Ethernet header into an Ethernet header 371219820Sjeff * with 802.1Q encapsulation. 372219820Sjeff */ 373219820Sjeff bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *), 374219820Sjeff sizeof(struct ether_header)); 375219820Sjeff evl = mtod(m, struct ether_vlan_header *); 376219820Sjeff evl->evl_proto = evl->evl_encap_proto; 377219820Sjeff evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 378219820Sjeff evl->evl_tag = htons(ifv->ifv_tag); 379219820Sjeff#ifdef DEBUG 380219820Sjeff printf("vlan_start: %*D\n", sizeof *evl, 381219820Sjeff (unsigned char *)evl, ":"); 382219820Sjeff#endif 383219820Sjeff } 384219820Sjeff 385219820Sjeff /* 386219820Sjeff * Send it, precisely as ether_output() would have. 387219820Sjeff * We are already running at splimp. 388219820Sjeff */ 389219820Sjeff if (IF_HANDOFF(&p->if_snd, m, p)) 390219820Sjeff ifp->if_opackets++; 391219820Sjeff else 392219820Sjeff ifp->if_oerrors++; 393219820Sjeff } 394219820Sjeff ifp->if_flags &= ~IFF_OACTIVE; 395219820Sjeff 396219820Sjeff return; 397219820Sjeff} 398219820Sjeff 399219820Sjeffstatic int 400219820Sjeffvlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t) 401219820Sjeff{ 402219820Sjeff struct ifvlan *ifv; 403219820Sjeff 404219820Sjeff for (ifv = LIST_FIRST(&ifv_list); ifv != NULL; 405219820Sjeff ifv = LIST_NEXT(ifv, ifv_list)) { 406219820Sjeff if (m->m_pkthdr.rcvif == ifv->ifv_p 407219820Sjeff && ifv->ifv_tag == t) 408219820Sjeff break; 409219820Sjeff } 410219820Sjeff 411219820Sjeff if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 412219820Sjeff m_free(m); 413219820Sjeff return -1; /* So the parent can take note */ 414219820Sjeff } 415219820Sjeff 416219820Sjeff /* 417219820Sjeff * Having found a valid vlan interface corresponding to 418219820Sjeff * the given source interface and vlan tag, run the 419219820Sjeff * the real packet through ether_input(). 420219820Sjeff */ 421219820Sjeff m->m_pkthdr.rcvif = &ifv->ifv_if; 422219820Sjeff 423219820Sjeff ifv->ifv_if.if_ipackets++; 424219820Sjeff ether_input(&ifv->ifv_if, eh, m); 425219820Sjeff return 0; 426219820Sjeff} 427219820Sjeff 428219820Sjeffstatic int 429219820Sjeffvlan_input(struct ether_header *eh, struct mbuf *m) 430219820Sjeff{ 431219820Sjeff struct ifvlan *ifv; 432219820Sjeff 433219820Sjeff for (ifv = LIST_FIRST(&ifv_list); ifv != NULL; 434219820Sjeff ifv = LIST_NEXT(ifv, ifv_list)) { 435219820Sjeff if (m->m_pkthdr.rcvif == ifv->ifv_p 436219820Sjeff && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *))) 437219820Sjeff == ifv->ifv_tag)) 438219820Sjeff break; 439219820Sjeff } 440219820Sjeff 441219820Sjeff if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 442219820Sjeff m_freem(m); 443219820Sjeff return -1; /* so ether_input can take note */ 444219820Sjeff } 445219820Sjeff 446219820Sjeff /* 447219820Sjeff * Having found a valid vlan interface corresponding to 448219820Sjeff * the given source interface and vlan tag, remove the 449219820Sjeff * encapsulation, and run the real packet through 450219820Sjeff * ether_input() a second time (it had better be 451219820Sjeff * reentrant!). 452219820Sjeff */ 453219820Sjeff m->m_pkthdr.rcvif = &ifv->ifv_if; 454219820Sjeff eh->ether_type = mtod(m, u_int16_t *)[1]; 455219820Sjeff m->m_data += EVL_ENCAPLEN; 456219820Sjeff m->m_len -= EVL_ENCAPLEN; 457219820Sjeff m->m_pkthdr.len -= EVL_ENCAPLEN; 458219820Sjeff 459219820Sjeff ifv->ifv_if.if_ipackets++; 460219820Sjeff ether_input(&ifv->ifv_if, eh, m); 461219820Sjeff return 0; 462219820Sjeff} 463219820Sjeff 464219820Sjeffstatic int 465219820Sjeffvlan_config(struct ifvlan *ifv, struct ifnet *p) 466219820Sjeff{ 467219820Sjeff struct ifaddr *ifa1, *ifa2; 468219820Sjeff struct sockaddr_dl *sdl1, *sdl2; 469219820Sjeff 470219820Sjeff if (p->if_data.ifi_type != IFT_ETHER) 471219820Sjeff return EPROTONOSUPPORT; 472219820Sjeff if (ifv->ifv_p) 473219820Sjeff return EBUSY; 474219820Sjeff ifv->ifv_p = p; 475219820Sjeff if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header)) 476219820Sjeff ifv->ifv_if.if_mtu = p->if_mtu; 477219820Sjeff else 478219820Sjeff ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN; 479219820Sjeff 480219820Sjeff /* 481219820Sjeff * Copy only a selected subset of flags from the parent. 482219820Sjeff * Other flags are none of our business. 483219820Sjeff */ 484219820Sjeff ifv->ifv_if.if_flags = (p->if_flags & 485219820Sjeff (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT)); 486219820Sjeff 487219820Sjeff /* 488219820Sjeff * Set up our ``Ethernet address'' to reflect the underlying 489219820Sjeff * physical interface's. 490219820Sjeff */ 491219820Sjeff ifa1 = ifaddr_byindex(ifv->ifv_if.if_index); 492219820Sjeff ifa2 = ifaddr_byindex(p->if_index); 493219820Sjeff sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; 494219820Sjeff sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; 495219820Sjeff sdl1->sdl_type = IFT_ETHER; 496219820Sjeff sdl1->sdl_alen = ETHER_ADDR_LEN; 497219820Sjeff bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); 498219820Sjeff bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 499219820Sjeff 500219820Sjeff /* 501219820Sjeff * Configure multicast addresses that may already be 502219820Sjeff * joined on the vlan device. 503219820Sjeff */ 504219820Sjeff (void)vlan_setmulti(&ifv->ifv_if); 505219820Sjeff 506219820Sjeff return 0; 507219820Sjeff} 508219820Sjeff 509219820Sjeffstatic int 510219820Sjeffvlan_unconfig(struct ifnet *ifp) 511219820Sjeff{ 512219820Sjeff struct ifaddr *ifa; 513219820Sjeff struct sockaddr_dl *sdl; 514219820Sjeff struct vlan_mc_entry *mc; 515219820Sjeff struct ifvlan *ifv; 516219820Sjeff struct ifnet *p; 517219820Sjeff int error; 518219820Sjeff 519219820Sjeff ifv = ifp->if_softc; 520219820Sjeff p = ifv->ifv_p; 521219820Sjeff 522219820Sjeff if (p) { 523219820Sjeff struct sockaddr_dl sdl; 524219820Sjeff 525219820Sjeff /* 526219820Sjeff * Since the interface is being unconfigured, we need to 527219820Sjeff * empty the list of multicast groups that we may have joined 528219820Sjeff * while we were alive from the parent's list. 529219820Sjeff */ 530219820Sjeff bzero((char *)&sdl, sizeof sdl); 531219820Sjeff sdl.sdl_len = sizeof sdl; 532219820Sjeff sdl.sdl_family = AF_LINK; 533219820Sjeff sdl.sdl_index = p->if_index; 534219820Sjeff sdl.sdl_type = IFT_ETHER; 535219820Sjeff sdl.sdl_alen = ETHER_ADDR_LEN; 536219820Sjeff 537219820Sjeff while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) { 538219820Sjeff mc = SLIST_FIRST(&ifv->vlan_mc_listhead); 539219820Sjeff bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 540219820Sjeff error = if_delmulti(p, (struct sockaddr *)&sdl); 541219820Sjeff if (error) 542219820Sjeff return(error); 543219820Sjeff SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); 544219820Sjeff free(mc, M_VLAN); 545219820Sjeff } 546219820Sjeff } 547219820Sjeff 548219820Sjeff /* Disconnect from parent. */ 549219820Sjeff ifv->ifv_p = NULL; 550219820Sjeff ifv->ifv_if.if_mtu = ETHERMTU; 551219820Sjeff 552219820Sjeff /* Clear our MAC address. */ 553219820Sjeff ifa = ifaddr_byindex(ifv->ifv_if.if_index); 554219820Sjeff sdl = (struct sockaddr_dl *)ifa->ifa_addr; 555219820Sjeff sdl->sdl_type = IFT_ETHER; 556219820Sjeff sdl->sdl_alen = ETHER_ADDR_LEN; 557219820Sjeff bzero(LLADDR(sdl), ETHER_ADDR_LEN); 558219820Sjeff bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 559219820Sjeff 560219820Sjeff return 0; 561219820Sjeff} 562219820Sjeff 563219820Sjeffstatic int 564219820Sjeffvlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 565219820Sjeff{ 566219820Sjeff struct ifaddr *ifa; 567219820Sjeff struct ifnet *p; 568219820Sjeff struct ifreq *ifr; 569219820Sjeff struct ifvlan *ifv; 570219820Sjeff struct vlanreq vlr; 571219820Sjeff int error = 0; 572219820Sjeff 573219820Sjeff ifr = (struct ifreq *)data; 574219820Sjeff ifa = (struct ifaddr *)data; 575219820Sjeff ifv = ifp->if_softc; 576219820Sjeff 577219820Sjeff switch (cmd) { 578219820Sjeff case SIOCSIFADDR: 579219820Sjeff ifp->if_flags |= IFF_UP; 580219820Sjeff 581219820Sjeff switch (ifa->ifa_addr->sa_family) { 582219820Sjeff#ifdef INET 583219820Sjeff case AF_INET: 584219820Sjeff arp_ifinit(&ifv->ifv_if, ifa); 585219820Sjeff break; 586219820Sjeff#endif 587219820Sjeff default: 588219820Sjeff break; 589219820Sjeff } 590219820Sjeff break; 591219820Sjeff 592219820Sjeff case SIOCGIFADDR: 593219820Sjeff { 594219820Sjeff struct sockaddr *sa; 595219820Sjeff 596219820Sjeff sa = (struct sockaddr *) &ifr->ifr_data; 597219820Sjeff bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, 598219820Sjeff (caddr_t) sa->sa_data, ETHER_ADDR_LEN); 599219820Sjeff } 600219820Sjeff break; 601219820Sjeff 602219820Sjeff case SIOCSIFMTU: 603219820Sjeff /* 604219820Sjeff * Set the interface MTU. 605219820Sjeff * This is bogus. The underlying interface might support 606219820Sjeff * jumbo frames. 607219820Sjeff */ 608219820Sjeff if (ifr->ifr_mtu > ETHERMTU) { 609219820Sjeff error = EINVAL; 610219820Sjeff } else { 611219820Sjeff ifp->if_mtu = ifr->ifr_mtu; 612219820Sjeff } 613219820Sjeff break; 614219820Sjeff 615219820Sjeff case SIOCSETVLAN: 616219820Sjeff error = copyin(ifr->ifr_data, &vlr, sizeof vlr); 617219820Sjeff if (error) 618219820Sjeff break; 619219820Sjeff if (vlr.vlr_parent[0] == '\0') { 620219820Sjeff vlan_unconfig(ifp); 621219820Sjeff if (ifp->if_flags & IFF_UP) { 622219820Sjeff int s = splimp(); 623219820Sjeff if_down(ifp); 624219820Sjeff splx(s); 625219820Sjeff } 626219820Sjeff ifp->if_flags &= ~IFF_RUNNING; 627219820Sjeff break; 628219820Sjeff } 629219820Sjeff p = ifunit(vlr.vlr_parent); 630219820Sjeff if (p == 0) { 631219820Sjeff error = ENOENT; 632219820Sjeff break; 633219820Sjeff } 634219820Sjeff error = vlan_config(ifv, p); 635219820Sjeff if (error) 636219820Sjeff break; 637219820Sjeff ifv->ifv_tag = vlr.vlr_tag; 638219820Sjeff ifp->if_flags |= IFF_RUNNING; 639219820Sjeff break; 640219820Sjeff 641219820Sjeff case SIOCGETVLAN: 642219820Sjeff bzero(&vlr, sizeof vlr); 643219820Sjeff if (ifv->ifv_p) { 644219820Sjeff snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), 645219820Sjeff "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit); 646219820Sjeff vlr.vlr_tag = ifv->ifv_tag; 647219820Sjeff } 648219820Sjeff error = copyout(&vlr, ifr->ifr_data, sizeof vlr); 649219820Sjeff break; 650219820Sjeff 651219820Sjeff case SIOCSIFFLAGS: 652219820Sjeff /* 653219820Sjeff * We don't support promiscuous mode 654219820Sjeff * right now because it would require help from the 655219820Sjeff * underlying drivers, which hasn't been implemented. 656219820Sjeff */ 657219820Sjeff if (ifr->ifr_flags & (IFF_PROMISC)) { 658219820Sjeff ifp->if_flags &= ~(IFF_PROMISC); 659219820Sjeff error = EINVAL; 660219820Sjeff } 661219820Sjeff break; 662219820Sjeff case SIOCADDMULTI: 663219820Sjeff case SIOCDELMULTI: 664219820Sjeff error = vlan_setmulti(ifp); 665219820Sjeff break; 666219820Sjeff default: 667219820Sjeff error = EINVAL; 668219820Sjeff } 669219820Sjeff return error; 670219820Sjeff} 671219820Sjeff