if_vlan.c revision 92081
1193323Sed/* 2193323Sed * Copyright 1998 Massachusetts Institute of Technology 3193323Sed * 4193323Sed * Permission to use, copy, modify, and distribute this software and 5193323Sed * its documentation for any purpose and without fee is hereby 6193323Sed * granted, provided that both the above copyright notice and this 7193323Sed * permission notice appear in all copies, that both the above 8193323Sed * copyright notice and this permission notice appear in all 9193323Sed * supporting documentation, and that the name of M.I.T. not be used 10193323Sed * in advertising or publicity pertaining to distribution of the 11193323Sed * software without specific, written prior permission. M.I.T. makes 12193323Sed * no representations about the suitability of this software for any 13193323Sed * purpose. It is provided "as is" without express or implied 14193323Sed * warranty. 15193323Sed * 16193323Sed * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 17193323Sed * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 18193323Sed * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19193323Sed * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 20193323Sed * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21193323Sed * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22193323Sed * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 23193323Sed * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24193323Sed * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25193323Sed * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 26218893Sdim * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27193323Sed * SUCH DAMAGE. 28193323Sed * 29193323Sed * $FreeBSD: head/sys/net/if_vlan.c 92081 2002-03-11 09:26:07Z mux $ 30218893Sdim */ 31193323Sed 32193323Sed/* 33193323Sed * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. 34193323Sed * Might be extended some day to also handle IEEE 802.1p priority 35193323Sed * tagging. This is sort of sneaky in the implementation, since 36193323Sed * we need to pretend to be enough of an Ethernet implementation 37193323Sed * to make arp work. The way we do this is by telling everyone 38193323Sed * that we are an Ethernet, and then catch the packets that 39193323Sed * ether_output() left on our output queue when it calls 40193323Sed * if_start(), rewrite them for use by the real outgoing interface, 41193323Sed * and ask it to send them. 42193323Sed * 43193323Sed * 44193323Sed * XXX It's incorrect to assume that we must always kludge up 45193323Sed * headers on the physical device's behalf: some devices support 46193323Sed * VLAN tag insertion and extraction in firmware. For these cases, 47193323Sed * one can change the behavior of the vlan interface by setting 48193323Sed * the LINK0 flag on it (that is setting the vlan interface's LINK0 49193323Sed * flag, _not_ the parent's LINK0 flag; we try to leave the parent 50193323Sed * alone). If the interface has the LINK0 flag set, then it will 51193323Sed * not modify the ethernet header on output, because the parent 52193323Sed * can do that for itself. On input, the parent can call vlan_input_tag() 53193323Sed * directly in order to supply us with an incoming mbuf and the vlan 54193323Sed * tag value that goes with it. 55193323Sed */ 56193323Sed 57193323Sed#include "opt_inet.h" 58193323Sed 59193323Sed#include <sys/param.h> 60193323Sed#include <sys/kernel.h> 61193323Sed#include <sys/malloc.h> 62193323Sed#include <sys/mbuf.h> 63218893Sdim#include <sys/module.h> 64193323Sed#include <sys/queue.h> 65193323Sed#include <sys/socket.h> 66195340Sed#include <sys/sockio.h> 67193323Sed#include <sys/sysctl.h> 68193323Sed#include <sys/systm.h> 69193323Sed 70193323Sed#include <net/bpf.h> 71193323Sed#include <net/ethernet.h> 72193323Sed#include <net/if.h> 73198090Srdivacky#include <net/if_arp.h> 74193323Sed#include <net/if_dl.h> 75193323Sed#include <net/if_types.h> 76195340Sed#include <net/if_vlan_var.h> 77195340Sed 78195340Sed#ifdef INET 79195340Sed#include <netinet/in.h> 80195340Sed#include <netinet/if_ether.h> 81195340Sed#endif 82193323Sed 83193323Sed#define VLANNAME "vlan" 84195340Sed 85195340SedSYSCTL_DECL(_net_link); 86195340SedSYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); 87195340SedSYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); 88193323Sed 89193323Sedstatic MALLOC_DEFINE(M_VLAN, "vlan", "802.1Q Virtual LAN Interface"); 90193323Sedstatic LIST_HEAD(, ifvlan) ifv_list; 91195340Sed 92193323Sedstatic int vlan_clone_create(struct if_clone *, int); 93193323Sedstatic int vlan_clone_destroy(struct ifnet *); 94193323Sedstatic void vlan_start(struct ifnet *ifp); 95193323Sedstatic void vlan_ifinit(void *foo); 96193323Sedstatic int vlan_input(struct ether_header *eh, struct mbuf *m); 97193323Sedstatic int vlan_input_tag(struct ether_header *eh, struct mbuf *m, 98193323Sed u_int16_t t); 99198090Srdivackystatic int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); 100193323Sedstatic int vlan_setmulti(struct ifnet *ifp); 101193323Sedstatic int vlan_unconfig(struct ifnet *ifp); 102193323Sedstatic int vlan_config(struct ifvlan *ifv, struct ifnet *p); 103193323Sed 104193323Sedstruct if_clone vlan_cloner = IF_CLONE_INITIALIZER("vlan", 105193323Sed vlan_clone_create, vlan_clone_destroy, IF_MAXUNIT); 106193323Sed 107195340Sed/* 108193323Sed * Program our multicast filter. What we're actually doing is 109193323Sed * programming the multicast filter of the parent. This has the 110193323Sed * side effect of causing the parent interface to receive multicast 111193323Sed * traffic that it doesn't really want, which ends up being discarded 112193323Sed * later by the upper protocol layers. Unfortunately, there's no way 113195340Sed * to avoid this: there really is only one physical interface. 114193323Sed */ 115193323Sedstatic int 116193323Sedvlan_setmulti(struct ifnet *ifp) 117193323Sed{ 118193323Sed struct ifnet *ifp_p; 119195340Sed struct ifmultiaddr *ifma, *rifma = NULL; 120195340Sed struct ifvlan *sc; 121195340Sed struct vlan_mc_entry *mc = NULL; 122195340Sed struct sockaddr_dl sdl; 123193323Sed int error; 124195340Sed 125193323Sed /* Find the parent. */ 126195340Sed sc = ifp->if_softc; 127193323Sed ifp_p = sc->ifv_p; 128198090Srdivacky 129198090Srdivacky /* 130193323Sed * If we don't have a parent, just remember the membership for 131193323Sed * when we do. 132198892Srdivacky */ 133193323Sed if (ifp_p == NULL) 134193323Sed return(0); 135218893Sdim 136193323Sed bzero((char *)&sdl, sizeof sdl); 137193323Sed sdl.sdl_len = sizeof sdl; 138218893Sdim sdl.sdl_family = AF_LINK; 139193323Sed sdl.sdl_index = ifp_p->if_index; 140193323Sed sdl.sdl_type = IFT_ETHER; 141218893Sdim sdl.sdl_alen = ETHER_ADDR_LEN; 142193323Sed 143193323Sed /* First, remove any existing filter entries. */ 144218893Sdim while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) { 145193323Sed mc = SLIST_FIRST(&sc->vlan_mc_listhead); 146193323Sed bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 147218893Sdim error = if_delmulti(ifp_p, (struct sockaddr *)&sdl); 148193323Sed if (error) 149193323Sed return(error); 150218893Sdim SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); 151193323Sed free(mc, M_VLAN); 152193323Sed } 153193323Sed 154218893Sdim /* Now program new ones. */ 155193323Sed TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 156193323Sed if (ifma->ifma_addr->sa_family != AF_LINK) 157193323Sed continue; 158193323Sed mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_WAITOK); 159218893Sdim bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 160193323Sed (char *)&mc->mc_addr, ETHER_ADDR_LEN); 161193323Sed SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); 162193323Sed bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 163193323Sed LLADDR(&sdl), ETHER_ADDR_LEN); 164193323Sed error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma); 165193323Sed if (error) 166193323Sed return(error); 167193323Sed } 168193323Sed 169193323Sed return(0); 170193323Sed} 171193323Sed 172193323Sedstatic int 173193323Sedvlan_modevent(module_t mod, int type, void *data) 174193323Sed{ 175193323Sed 176193323Sed switch (type) { 177193323Sed case MOD_LOAD: 178193323Sed LIST_INIT(&ifv_list); 179193323Sed vlan_input_p = vlan_input; 180193323Sed vlan_input_tag_p = vlan_input_tag; 181193323Sed if_clone_attach(&vlan_cloner); 182193323Sed break; 183193323Sed case MOD_UNLOAD: 184193323Sed if_clone_detach(&vlan_cloner); 185193323Sed vlan_input_p = NULL; 186193323Sed vlan_input_tag_p = NULL; 187193323Sed while (!LIST_EMPTY(&ifv_list)) 188193323Sed vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if); 189193323Sed break; 190193323Sed } 191193323Sed return 0; 192193323Sed} 193193323Sed 194193323Sedstatic moduledata_t vlan_mod = { 195193323Sed "if_vlan", 196193323Sed vlan_modevent, 197193323Sed 0 198193323Sed}; 199193323Sed 200193323SedDECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 201193323Sed 202193323Sedstatic int 203193323Sedvlan_clone_create(struct if_clone *ifc, int unit) 204193323Sed{ 205193323Sed struct ifvlan *ifv; 206193323Sed struct ifnet *ifp; 207193323Sed int s; 208193323Sed 209193323Sed ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO); 210193323Sed ifp = &ifv->ifv_if; 211193323Sed SLIST_INIT(&ifv->vlan_mc_listhead); 212193323Sed 213193323Sed s = splnet(); 214193323Sed LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list); 215193323Sed splx(s); 216193323Sed 217193323Sed ifp->if_softc = ifv; 218193323Sed ifp->if_name = "vlan"; 219193323Sed ifp->if_unit = unit; 220193323Sed /* NB: flags are not set here */ 221193323Sed ifp->if_linkmib = &ifv->ifv_mib; 222193323Sed ifp->if_linkmiblen = sizeof ifv->ifv_mib; 223193323Sed /* NB: mtu is not set here */ 224193323Sed 225193323Sed ifp->if_init = vlan_ifinit; 226193323Sed ifp->if_start = vlan_start; 227193323Sed ifp->if_ioctl = vlan_ioctl; 228193323Sed ifp->if_output = ether_output; 229193323Sed ifp->if_snd.ifq_maxlen = ifqmaxlen; 230193323Sed ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 231193323Sed /* Now undo some of the damage... */ 232193323Sed ifp->if_baudrate = 0; 233193323Sed ifp->if_data.ifi_type = IFT_L2VLAN; 234193323Sed ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN; 235193323Sed 236193323Sed return (0); 237218893Sdim} 238193323Sed 239193323Sedstatic int 240193323Sedvlan_clone_destroy(struct ifnet *ifp) 241193323Sed{ 242193323Sed struct ifvlan *ifv = ifp->if_softc; 243193323Sed int s; 244193323Sed 245193323Sed s = splnet(); 246193323Sed LIST_REMOVE(ifv, ifv_list); 247193323Sed vlan_unconfig(ifp); 248193323Sed splx(s); 249193323Sed 250193323Sed ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 251193323Sed 252193323Sed free(ifv, M_VLAN); 253193323Sed return (0); 254193323Sed} 255193323Sed 256193323Sedstatic void 257193323Sedvlan_ifinit(void *foo) 258193323Sed{ 259193323Sed return; 260193323Sed} 261193323Sed 262193323Sedstatic void 263193323Sedvlan_start(struct ifnet *ifp) 264193323Sed{ 265193323Sed struct ifvlan *ifv; 266193323Sed struct ifnet *p; 267193323Sed struct ether_vlan_header *evl; 268193323Sed struct mbuf *m; 269193323Sed 270193323Sed ifv = ifp->if_softc; 271193323Sed p = ifv->ifv_p; 272193323Sed 273193323Sed ifp->if_flags |= IFF_OACTIVE; 274193323Sed for (;;) { 275193323Sed IF_DEQUEUE(&ifp->if_snd, m); 276193323Sed if (m == 0) 277193323Sed break; 278193323Sed if (ifp->if_bpf) 279193323Sed bpf_mtap(ifp, m); 280193323Sed 281193323Sed /* 282193323Sed * Do not run parent's if_start() if the parent is not up, 283193323Sed * or parent's driver will cause a system crash. 284193323Sed */ 285193323Sed if ((p->if_flags & (IFF_UP | IFF_RUNNING)) != 286193323Sed (IFF_UP | IFF_RUNNING)) { 287193323Sed m_freem(m); 288193323Sed ifp->if_data.ifi_collisions++; 289193323Sed continue; 290193323Sed } 291193323Sed 292193323Sed /* 293193323Sed * If the LINK0 flag is set, it means the underlying interface 294193323Sed * can do VLAN tag insertion itself and doesn't require us to 295193323Sed * create a special header for it. In this case, we just pass 296193323Sed * the packet along. However, we need some way to tell the 297193323Sed * interface where the packet came from so that it knows how 298193323Sed * to find the VLAN tag to use, so we set the rcvif in the 299193323Sed * mbuf header to our ifnet. 300193323Sed * 301193323Sed * Note: we also set the M_PROTO1 flag in the mbuf to let 302193323Sed * the parent driver know that the rcvif pointer is really 303193323Sed * valid. We need to do this because sometimes mbufs will 304193323Sed * be allocated by other parts of the system that contain 305193323Sed * garbage in the rcvif pointer. Using the M_PROTO1 flag 306193323Sed * lets the driver perform a proper sanity check and avoid 307193323Sed * following potentially bogus rcvif pointers off into 308193323Sed * never-never land. 309193323Sed */ 310193323Sed if (ifp->if_flags & IFF_LINK0) { 311193323Sed m->m_pkthdr.rcvif = ifp; 312193323Sed m->m_flags |= M_PROTO1; 313193323Sed } else { 314193323Sed M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT); 315193323Sed if (m == NULL) { 316193323Sed printf("vlan%d: M_PREPEND failed", ifp->if_unit); 317193323Sed ifp->if_ierrors++; 318193323Sed continue; 319193323Sed } 320193323Sed /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ 321193323Sed 322193323Sed m = m_pullup(m, ETHER_HDR_LEN + EVL_ENCAPLEN); 323193323Sed if (m == NULL) { 324193323Sed printf("vlan%d: m_pullup failed", ifp->if_unit); 325198090Srdivacky ifp->if_ierrors++; 326193323Sed continue; 327193323Sed } 328193323Sed 329193323Sed /* 330198090Srdivacky * Transform the Ethernet header into an Ethernet header 331198090Srdivacky * with 802.1Q encapsulation. 332198090Srdivacky */ 333198090Srdivacky bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *), 334193323Sed sizeof(struct ether_header)); 335193323Sed evl = mtod(m, struct ether_vlan_header *); 336193323Sed evl->evl_proto = evl->evl_encap_proto; 337193323Sed evl->evl_encap_proto = htons(ETHERTYPE_VLAN); 338193323Sed evl->evl_tag = htons(ifv->ifv_tag); 339193323Sed#ifdef DEBUG 340193323Sed printf("vlan_start: %*D\n", sizeof *evl, 341198090Srdivacky (unsigned char *)evl, ":"); 342193323Sed#endif 343193323Sed } 344193323Sed 345193323Sed /* 346193323Sed * Send it, precisely as ether_output() would have. 347193323Sed * We are already running at splimp. 348193323Sed */ 349198090Srdivacky if (IF_HANDOFF(&p->if_snd, m, p)) 350198090Srdivacky ifp->if_opackets++; 351198090Srdivacky else 352198090Srdivacky ifp->if_oerrors++; 353198090Srdivacky } 354198090Srdivacky ifp->if_flags &= ~IFF_OACTIVE; 355198090Srdivacky 356198090Srdivacky return; 357198090Srdivacky} 358198090Srdivacky 359198090Srdivackystatic int 360193323Sedvlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t) 361193323Sed{ 362204642Srdivacky struct ifvlan *ifv; 363193323Sed 364193323Sed /* 365193323Sed * Fake up a header and send the packet to the physical interface's 366198090Srdivacky * bpf tap if active. 367198090Srdivacky */ 368198090Srdivacky if (m->m_pkthdr.rcvif->if_bpf != NULL) { 369193323Sed struct m_hdr mh; 370193323Sed struct ether_vlan_header evh; 371204642Srdivacky 372193323Sed bcopy(eh, &evh, 2*ETHER_ADDR_LEN); 373193323Sed evh.evl_encap_proto = htons(ETHERTYPE_VLAN); 374193323Sed evh.evl_tag = htons(t); 375198090Srdivacky evh.evl_proto = eh->ether_type; 376193323Sed 377193323Sed /* This kludge is OK; BPF treats the "mbuf" as read-only */ 378193323Sed mh.mh_next = m; 379193323Sed mh.mh_data = (char *)&evh; 380193323Sed mh.mh_len = ETHER_HDR_LEN + EVL_ENCAPLEN; 381193323Sed bpf_mtap(m->m_pkthdr.rcvif, (struct mbuf *)&mh); 382193323Sed } 383193323Sed 384193323Sed for (ifv = LIST_FIRST(&ifv_list); ifv != NULL; 385193323Sed ifv = LIST_NEXT(ifv, ifv_list)) { 386193323Sed if (m->m_pkthdr.rcvif == ifv->ifv_p 387193323Sed && ifv->ifv_tag == t) 388193323Sed break; 389193323Sed } 390193323Sed 391193323Sed if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 392193323Sed m_free(m); 393193323Sed return -1; /* So the parent can take note */ 394193323Sed } 395193323Sed 396193323Sed /* 397193323Sed * Having found a valid vlan interface corresponding to 398193323Sed * the given source interface and vlan tag, run the 399193323Sed * the real packet through ether_input(). 400198090Srdivacky */ 401193323Sed m->m_pkthdr.rcvif = &ifv->ifv_if; 402193323Sed 403193323Sed ifv->ifv_if.if_ipackets++; 404198090Srdivacky ether_input(&ifv->ifv_if, eh, m); 405193323Sed return 0; 406193323Sed} 407193323Sed 408193323Sedstatic int 409193323Sedvlan_input(struct ether_header *eh, struct mbuf *m) 410193323Sed{ 411193323Sed struct ifvlan *ifv; 412193323Sed 413193323Sed for (ifv = LIST_FIRST(&ifv_list); ifv != NULL; 414193323Sed ifv = LIST_NEXT(ifv, ifv_list)) { 415193323Sed if (m->m_pkthdr.rcvif == ifv->ifv_p 416193323Sed && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *))) 417193323Sed == ifv->ifv_tag)) 418193323Sed break; 419193323Sed } 420193323Sed 421193323Sed if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 422193323Sed m_freem(m); 423193323Sed return -1; /* so ether_input can take note */ 424198090Srdivacky } 425193323Sed 426193323Sed /* 427193323Sed * Having found a valid vlan interface corresponding to 428198090Srdivacky * the given source interface and vlan tag, remove the 429193323Sed * encapsulation, and run the real packet through 430193323Sed * ether_input() a second time (it had better be 431193323Sed * reentrant!). 432193323Sed */ 433193323Sed m->m_pkthdr.rcvif = &ifv->ifv_if; 434193323Sed eh->ether_type = mtod(m, u_int16_t *)[1]; 435193323Sed m->m_data += EVL_ENCAPLEN; 436193323Sed m->m_len -= EVL_ENCAPLEN; 437193323Sed m->m_pkthdr.len -= EVL_ENCAPLEN; 438193323Sed 439193323Sed ifv->ifv_if.if_ipackets++; 440193323Sed ether_input(&ifv->ifv_if, eh, m); 441193323Sed return 0; 442193323Sed} 443193323Sed 444193323Sedstatic int 445193323Sedvlan_config(struct ifvlan *ifv, struct ifnet *p) 446193323Sed{ 447193323Sed struct ifaddr *ifa1, *ifa2; 448193323Sed struct sockaddr_dl *sdl1, *sdl2; 449193323Sed 450193323Sed if (p->if_data.ifi_type != IFT_ETHER) 451193323Sed return EPROTONOSUPPORT; 452193323Sed if (ifv->ifv_p) 453193323Sed return EBUSY; 454193323Sed ifv->ifv_p = p; 455193323Sed if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header)) 456193323Sed ifv->ifv_if.if_mtu = p->if_mtu; 457193323Sed else 458193323Sed ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN; 459193323Sed 460193323Sed /* 461193323Sed * Copy only a selected subset of flags from the parent. 462193323Sed * Other flags are none of our business. 463193323Sed */ 464193323Sed ifv->ifv_if.if_flags = (p->if_flags & 465193323Sed (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT)); 466193323Sed 467193323Sed /* 468193323Sed * Set up our ``Ethernet address'' to reflect the underlying 469193323Sed * physical interface's. 470193323Sed */ 471193323Sed ifa1 = ifaddr_byindex(ifv->ifv_if.if_index); 472193323Sed ifa2 = ifaddr_byindex(p->if_index); 473193323Sed sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; 474193323Sed sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; 475193323Sed sdl1->sdl_type = IFT_ETHER; 476193323Sed sdl1->sdl_alen = ETHER_ADDR_LEN; 477193323Sed bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); 478193323Sed bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 479193323Sed 480193323Sed /* 481193323Sed * Configure multicast addresses that may already be 482193323Sed * joined on the vlan device. 483193323Sed */ 484193323Sed (void)vlan_setmulti(&ifv->ifv_if); 485193323Sed 486193323Sed return 0; 487193323Sed} 488193323Sed 489193323Sedstatic int 490193323Sedvlan_unconfig(struct ifnet *ifp) 491193323Sed{ 492193323Sed struct ifaddr *ifa; 493193323Sed struct sockaddr_dl *sdl; 494193323Sed struct vlan_mc_entry *mc; 495193323Sed struct ifvlan *ifv; 496218893Sdim struct ifnet *p; 497193323Sed int error; 498193323Sed 499193323Sed ifv = ifp->if_softc; 500193323Sed p = ifv->ifv_p; 501193323Sed 502193323Sed if (p) { 503193323Sed struct sockaddr_dl sdl; 504193323Sed 505193323Sed /* 506193323Sed * Since the interface is being unconfigured, we need to 507193323Sed * empty the list of multicast groups that we may have joined 508193323Sed * while we were alive from the parent's list. 509193323Sed */ 510193323Sed bzero((char *)&sdl, sizeof sdl); 511193323Sed sdl.sdl_len = sizeof sdl; 512193323Sed sdl.sdl_family = AF_LINK; 513193323Sed sdl.sdl_index = p->if_index; 514193323Sed sdl.sdl_type = IFT_ETHER; 515193323Sed sdl.sdl_alen = ETHER_ADDR_LEN; 516193323Sed 517193323Sed while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) { 518193323Sed mc = SLIST_FIRST(&ifv->vlan_mc_listhead); 519193323Sed bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 520193323Sed error = if_delmulti(p, (struct sockaddr *)&sdl); 521193323Sed if (error) 522193323Sed return(error); 523193323Sed SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); 524193323Sed free(mc, M_VLAN); 525193323Sed } 526193323Sed } 527193323Sed 528193323Sed /* Disconnect from parent. */ 529193323Sed ifv->ifv_p = NULL; 530193323Sed ifv->ifv_if.if_mtu = ETHERMTU; 531193323Sed 532193323Sed /* Clear our MAC address. */ 533193323Sed ifa = ifaddr_byindex(ifv->ifv_if.if_index); 534193323Sed sdl = (struct sockaddr_dl *)ifa->ifa_addr; 535193323Sed sdl->sdl_type = IFT_ETHER; 536193323Sed sdl->sdl_alen = ETHER_ADDR_LEN; 537193323Sed bzero(LLADDR(sdl), ETHER_ADDR_LEN); 538193323Sed bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 539193323Sed 540193323Sed return 0; 541193323Sed} 542193323Sed 543193323Sedstatic int 544193323Sedvlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 545193323Sed{ 546193323Sed struct ifaddr *ifa; 547193323Sed struct ifnet *p; 548193323Sed struct ifreq *ifr; 549193323Sed struct ifvlan *ifv; 550193323Sed struct vlanreq vlr; 551218893Sdim int error = 0; 552218893Sdim 553218893Sdim ifr = (struct ifreq *)data; 554198090Srdivacky ifa = (struct ifaddr *)data; 555218893Sdim ifv = ifp->if_softc; 556218893Sdim 557201360Srdivacky switch (cmd) { 558201360Srdivacky case SIOCSIFADDR: 559201360Srdivacky ifp->if_flags |= IFF_UP; 560218893Sdim 561218893Sdim switch (ifa->ifa_addr->sa_family) { 562201360Srdivacky#ifdef INET 563201360Srdivacky case AF_INET: 564218893Sdim arp_ifinit(&ifv->ifv_if, ifa); 565193323Sed break; 566193323Sed#endif 567193323Sed default: 568193323Sed break; 569193323Sed } 570193323Sed break; 571193323Sed 572193323Sed case SIOCGIFADDR: 573193323Sed { 574193323Sed struct sockaddr *sa; 575193323Sed 576193323Sed sa = (struct sockaddr *) &ifr->ifr_data; 577201360Srdivacky bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, 578201360Srdivacky (caddr_t) sa->sa_data, ETHER_ADDR_LEN); 579201360Srdivacky } 580201360Srdivacky break; 581201360Srdivacky 582193323Sed case SIOCSIFMTU: 583193323Sed /* 584193323Sed * Set the interface MTU. 585193323Sed * This is bogus. The underlying interface might support 586193323Sed * jumbo frames. 587193323Sed */ 588193323Sed if (ifr->ifr_mtu > ETHERMTU) { 589193323Sed error = EINVAL; 590193323Sed } else { 591193323Sed ifp->if_mtu = ifr->ifr_mtu; 592193323Sed } 593193323Sed break; 594193323Sed 595193323Sed case SIOCSETVLAN: 596193323Sed error = copyin(ifr->ifr_data, &vlr, sizeof vlr); 597193323Sed if (error) 598193323Sed break; 599193323Sed if (vlr.vlr_parent[0] == '\0') { 600193323Sed vlan_unconfig(ifp); 601193323Sed if (ifp->if_flags & IFF_UP) { 602193323Sed int s = splimp(); 603193323Sed if_down(ifp); 604193323Sed splx(s); 605193323Sed } 606193323Sed ifp->if_flags &= ~IFF_RUNNING; 607193323Sed break; 608193323Sed } 609193323Sed p = ifunit(vlr.vlr_parent); 610193323Sed if (p == 0) { 611193323Sed error = ENOENT; 612193323Sed break; 613193323Sed } 614193323Sed error = vlan_config(ifv, p); 615193323Sed if (error) 616193323Sed break; 617218893Sdim ifv->ifv_tag = vlr.vlr_tag; 618193323Sed ifp->if_flags |= IFF_RUNNING; 619193323Sed break; 620193323Sed 621193323Sed case SIOCGETVLAN: 622193323Sed bzero(&vlr, sizeof vlr); 623193323Sed if (ifv->ifv_p) { 624218893Sdim snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), 625193323Sed "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit); 626193323Sed vlr.vlr_tag = ifv->ifv_tag; 627193323Sed } 628193323Sed error = copyout(&vlr, ifr->ifr_data, sizeof vlr); 629193323Sed break; 630193323Sed 631193323Sed case SIOCSIFFLAGS: 632218893Sdim /* 633218893Sdim * We don't support promiscuous mode 634193323Sed * right now because it would require help from the 635193323Sed * underlying drivers, which hasn't been implemented. 636193323Sed */ 637193323Sed if (ifr->ifr_flags & (IFF_PROMISC)) { 638193323Sed ifp->if_flags &= ~(IFF_PROMISC); 639193323Sed error = EINVAL; 640193323Sed } 641193323Sed break; 642193323Sed case SIOCADDMULTI: 643193323Sed case SIOCDELMULTI: 644193323Sed error = vlan_setmulti(ifp); 645193323Sed break; 646193323Sed default: 647193323Sed error = EINVAL; 648193323Sed } 649193323Sed return error; 650193323Sed} 651193323Sed