12cc2df49SGarrett Wollman /* 22cc2df49SGarrett Wollman * Copyright 1998 Massachusetts Institute of Technology 32cc2df49SGarrett Wollman * 42cc2df49SGarrett Wollman * Permission to use, copy, modify, and distribute this software and 52cc2df49SGarrett Wollman * its documentation for any purpose and without fee is hereby 62cc2df49SGarrett Wollman * granted, provided that both the above copyright notice and this 72cc2df49SGarrett Wollman * permission notice appear in all copies, that both the above 82cc2df49SGarrett Wollman * copyright notice and this permission notice appear in all 92cc2df49SGarrett Wollman * supporting documentation, and that the name of M.I.T. not be used 102cc2df49SGarrett Wollman * in advertising or publicity pertaining to distribution of the 112cc2df49SGarrett Wollman * software without specific, written prior permission. M.I.T. makes 122cc2df49SGarrett Wollman * no representations about the suitability of this software for any 132cc2df49SGarrett Wollman * purpose. It is provided "as is" without express or implied 142cc2df49SGarrett Wollman * warranty. 152cc2df49SGarrett Wollman * 162cc2df49SGarrett Wollman * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 172cc2df49SGarrett Wollman * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 182cc2df49SGarrett Wollman * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 192cc2df49SGarrett Wollman * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 202cc2df49SGarrett Wollman * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 212cc2df49SGarrett Wollman * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 222cc2df49SGarrett Wollman * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 232cc2df49SGarrett Wollman * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 242cc2df49SGarrett Wollman * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 252cc2df49SGarrett Wollman * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 262cc2df49SGarrett Wollman * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 272cc2df49SGarrett Wollman * SUCH DAMAGE. 282cc2df49SGarrett Wollman * 29c3aac50fSPeter Wemm * $FreeBSD$ 302cc2df49SGarrett Wollman */ 312cc2df49SGarrett Wollman 322cc2df49SGarrett Wollman /* 332cc2df49SGarrett Wollman * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. 342cc2df49SGarrett Wollman * Might be extended some day to also handle IEEE 802.1p priority 352cc2df49SGarrett Wollman * tagging. This is sort of sneaky in the implementation, since 362cc2df49SGarrett Wollman * we need to pretend to be enough of an Ethernet implementation 372cc2df49SGarrett Wollman * to make arp work. The way we do this is by telling everyone 382cc2df49SGarrett Wollman * that we are an Ethernet, and then catch the packets that 3946a32e61SPhilippe Charnier * ether_output() left on our output queue when it calls 402cc2df49SGarrett Wollman * if_start(), rewrite them for use by the real outgoing interface, 412cc2df49SGarrett Wollman * and ask it to send them. 42f731f104SBill Paul * 4397ed1257SBill Paul * 44f731f104SBill Paul * XXX It's incorrect to assume that we must always kludge up 45f731f104SBill Paul * headers on the physical device's behalf: some devices support 46befdaf4eSJeroen Ruigrok van der Werven * VLAN tag insertion and extraction in firmware. For these cases, 47f731f104SBill Paul * one can change the behavior of the vlan interface by setting 48f731f104SBill Paul * the LINK0 flag on it (that is setting the vlan interface's LINK0 49f731f104SBill Paul * flag, _not_ the parent's LINK0 flag; we try to leave the parent 502d89d40aSJeroen Ruigrok van der Werven * alone). If the interface has the LINK0 flag set, then it will 512d89d40aSJeroen Ruigrok van der Werven * not modify the ethernet header on output, because the parent 52f731f104SBill Paul * can do that for itself. On input, the parent can call vlan_input_tag() 53f731f104SBill Paul * directly in order to supply us with an incoming mbuf and the vlan 54f731f104SBill Paul * tag value that goes with it. 552cc2df49SGarrett Wollman */ 562cc2df49SGarrett Wollman 572cc2df49SGarrett Wollman #include "vlan.h" 582cc2df49SGarrett Wollman #include "opt_inet.h" 592cc2df49SGarrett Wollman 602cc2df49SGarrett Wollman #include <sys/param.h> 612cc2df49SGarrett Wollman #include <sys/kernel.h> 62f731f104SBill Paul #include <sys/malloc.h> 632cc2df49SGarrett Wollman #include <sys/mbuf.h> 642b120974SPeter Wemm #include <sys/module.h> 65f731f104SBill Paul #include <sys/queue.h> 662cc2df49SGarrett Wollman #include <sys/socket.h> 672cc2df49SGarrett Wollman #include <sys/sockio.h> 682cc2df49SGarrett Wollman #include <sys/sysctl.h> 692cc2df49SGarrett Wollman #include <sys/systm.h> 702cc2df49SGarrett Wollman 712cc2df49SGarrett Wollman #include <net/bpf.h> 722cc2df49SGarrett Wollman #include <net/ethernet.h> 732cc2df49SGarrett Wollman #include <net/if.h> 742cc2df49SGarrett Wollman #include <net/if_arp.h> 752cc2df49SGarrett Wollman #include <net/if_dl.h> 762cc2df49SGarrett Wollman #include <net/if_types.h> 772cc2df49SGarrett Wollman #include <net/if_vlan_var.h> 782cc2df49SGarrett Wollman 792cc2df49SGarrett Wollman #ifdef INET 802cc2df49SGarrett Wollman #include <netinet/in.h> 812cc2df49SGarrett Wollman #include <netinet/if_ether.h> 822cc2df49SGarrett Wollman #endif 832cc2df49SGarrett Wollman 844a408dcbSBill Paul SYSCTL_DECL(_net_link); 85d2a75853SBill Fenner SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); 862cc2df49SGarrett Wollman SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); 872cc2df49SGarrett Wollman 882cc2df49SGarrett Wollman u_int vlan_proto = ETHERTYPE_VLAN; 892cc2df49SGarrett Wollman SYSCTL_INT(_net_link_vlan_link, VLANCTL_PROTO, proto, CTLFLAG_RW, &vlan_proto, 902cc2df49SGarrett Wollman 0, "Ethernet protocol used for VLAN encapsulation"); 912cc2df49SGarrett Wollman 922cc2df49SGarrett Wollman static struct ifvlan ifv_softc[NVLAN]; 932cc2df49SGarrett Wollman 942cc2df49SGarrett Wollman static void vlan_start(struct ifnet *ifp); 952cc2df49SGarrett Wollman static void vlan_ifinit(void *foo); 96cfe8b629SGarrett Wollman static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); 97f731f104SBill Paul static int vlan_setmulti(struct ifnet *ifp); 98f731f104SBill Paul static int vlan_unconfig(struct ifnet *ifp); 99f731f104SBill Paul static int vlan_config(struct ifvlan *ifv, struct ifnet *p); 100f731f104SBill Paul 101f731f104SBill Paul /* 102f731f104SBill Paul * Program our multicast filter. What we're actually doing is 103f731f104SBill Paul * programming the multicast filter of the parent. This has the 104f731f104SBill Paul * side effect of causing the parent interface to receive multicast 105f731f104SBill Paul * traffic that it doesn't really want, which ends up being discarded 106f731f104SBill Paul * later by the upper protocol layers. Unfortunately, there's no way 107f731f104SBill Paul * to avoid this: there really is only one physical interface. 108f731f104SBill Paul */ 1092b120974SPeter Wemm static int 1102b120974SPeter Wemm vlan_setmulti(struct ifnet *ifp) 111f731f104SBill Paul { 112f731f104SBill Paul struct ifnet *ifp_p; 113f731f104SBill Paul struct ifmultiaddr *ifma, *rifma = NULL; 114f731f104SBill Paul struct ifvlan *sc; 115f731f104SBill Paul struct vlan_mc_entry *mc = NULL; 116f731f104SBill Paul struct sockaddr_dl sdl; 117f731f104SBill Paul int error; 118f731f104SBill Paul 119f731f104SBill Paul /* Find the parent. */ 120f731f104SBill Paul sc = ifp->if_softc; 121f731f104SBill Paul ifp_p = sc->ifv_p; 122f731f104SBill Paul 1231b2a4f7aSBill Fenner /* 1241b2a4f7aSBill Fenner * If we don't have a parent, just remember the membership for 1251b2a4f7aSBill Fenner * when we do. 1261b2a4f7aSBill Fenner */ 1271b2a4f7aSBill Fenner if (ifp_p == NULL) 1281b2a4f7aSBill Fenner return(0); 1291b2a4f7aSBill Fenner 13024993214SYaroslav Tykhiy bzero((char *)&sdl, sizeof sdl); 13124993214SYaroslav Tykhiy sdl.sdl_len = sizeof sdl; 132f731f104SBill Paul sdl.sdl_family = AF_LINK; 13326e30963SBill Fenner sdl.sdl_index = ifp_p->if_index; 13426e30963SBill Fenner sdl.sdl_type = IFT_ETHER; 13524993214SYaroslav Tykhiy sdl.sdl_alen = ETHER_ADDR_LEN; 136f731f104SBill Paul 137f731f104SBill Paul /* First, remove any existing filter entries. */ 13822f29826SPoul-Henning Kamp while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) { 13922f29826SPoul-Henning Kamp mc = SLIST_FIRST(&sc->vlan_mc_listhead); 140f731f104SBill Paul bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 141f731f104SBill Paul error = if_delmulti(ifp_p, (struct sockaddr *)&sdl); 142f731f104SBill Paul if (error) 143f731f104SBill Paul return(error); 144f731f104SBill Paul SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); 145f731f104SBill Paul free(mc, M_DEVBUF); 146f731f104SBill Paul } 147f731f104SBill Paul 148f731f104SBill Paul /* Now program new ones. */ 1496817526dSPoul-Henning Kamp TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 150f731f104SBill Paul if (ifma->ifma_addr->sa_family != AF_LINK) 151f731f104SBill Paul continue; 15224993214SYaroslav Tykhiy mc = malloc(sizeof(struct vlan_mc_entry), M_DEVBUF, M_WAITOK); 153f731f104SBill Paul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 154f731f104SBill Paul (char *)&mc->mc_addr, ETHER_ADDR_LEN); 155f731f104SBill Paul SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); 15626e30963SBill Fenner bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 15726e30963SBill Fenner LLADDR(&sdl), ETHER_ADDR_LEN); 158f731f104SBill Paul error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma); 159f731f104SBill Paul if (error) 160f731f104SBill Paul return(error); 161f731f104SBill Paul } 162f731f104SBill Paul 163f731f104SBill Paul return(0); 164f731f104SBill Paul } 1652cc2df49SGarrett Wollman 1662cc2df49SGarrett Wollman static void 1672b120974SPeter Wemm vlaninit(void) 1682cc2df49SGarrett Wollman { 1692cc2df49SGarrett Wollman int i; 1702cc2df49SGarrett Wollman 1712cc2df49SGarrett Wollman for (i = 0; i < NVLAN; i++) { 1722cc2df49SGarrett Wollman struct ifnet *ifp = &ifv_softc[i].ifv_if; 1732cc2df49SGarrett Wollman 1742cc2df49SGarrett Wollman ifp->if_softc = &ifv_softc[i]; 1752cc2df49SGarrett Wollman ifp->if_name = "vlan"; 1762cc2df49SGarrett Wollman ifp->if_unit = i; 1772cc2df49SGarrett Wollman /* NB: flags are not set here */ 1782cc2df49SGarrett Wollman ifp->if_linkmib = &ifv_softc[i].ifv_mib; 1792cc2df49SGarrett Wollman ifp->if_linkmiblen = sizeof ifv_softc[i].ifv_mib; 1802cc2df49SGarrett Wollman /* NB: mtu is not set here */ 1812cc2df49SGarrett Wollman 1822cc2df49SGarrett Wollman ifp->if_init = vlan_ifinit; 1832cc2df49SGarrett Wollman ifp->if_start = vlan_start; 1842cc2df49SGarrett Wollman ifp->if_ioctl = vlan_ioctl; 1852cc2df49SGarrett Wollman ifp->if_output = ether_output; 186f731f104SBill Paul ifp->if_snd.ifq_maxlen = ifqmaxlen; 18721b8ebd9SArchie Cobbs ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 1882cc2df49SGarrett Wollman /* Now undo some of the damage... */ 189d2a75853SBill Fenner ifp->if_data.ifi_type = IFT_L2VLAN; 1902cc2df49SGarrett Wollman ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN; 1912cc2df49SGarrett Wollman } 1922cc2df49SGarrett Wollman } 1932b120974SPeter Wemm 1942b120974SPeter Wemm static int 1952b120974SPeter Wemm vlan_modevent(module_t mod, int type, void *data) 1962b120974SPeter Wemm { 1972b120974SPeter Wemm switch (type) { 1982b120974SPeter Wemm case MOD_LOAD: 1992b120974SPeter Wemm vlaninit(); 2002b120974SPeter Wemm break; 2012b120974SPeter Wemm case MOD_UNLOAD: 2022b120974SPeter Wemm printf("if_vlan module unload - not possible for this module type\n"); 2032b120974SPeter Wemm return EINVAL; 2042b120974SPeter Wemm } 2052b120974SPeter Wemm return 0; 2062b120974SPeter Wemm } 2072b120974SPeter Wemm 2082b120974SPeter Wemm static moduledata_t vlan_mod = { 2092b120974SPeter Wemm "if_vlan", 2102b120974SPeter Wemm vlan_modevent, 2112b120974SPeter Wemm 0 2122b120974SPeter Wemm }; 2132b120974SPeter Wemm 2142b120974SPeter Wemm DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 2152cc2df49SGarrett Wollman 2162cc2df49SGarrett Wollman static void 2172cc2df49SGarrett Wollman vlan_ifinit(void *foo) 2182cc2df49SGarrett Wollman { 219f731f104SBill Paul return; 2202cc2df49SGarrett Wollman } 2212cc2df49SGarrett Wollman 2222cc2df49SGarrett Wollman static void 2232cc2df49SGarrett Wollman vlan_start(struct ifnet *ifp) 2242cc2df49SGarrett Wollman { 2252cc2df49SGarrett Wollman struct ifvlan *ifv; 2262cc2df49SGarrett Wollman struct ifnet *p; 2272cc2df49SGarrett Wollman struct ether_vlan_header *evl; 2285326b23cSMatthew N. Dodd struct mbuf *m; 2292cc2df49SGarrett Wollman 2302cc2df49SGarrett Wollman ifv = ifp->if_softc; 2312cc2df49SGarrett Wollman p = ifv->ifv_p; 2322cc2df49SGarrett Wollman 2332cc2df49SGarrett Wollman ifp->if_flags |= IFF_OACTIVE; 2342cc2df49SGarrett Wollman for (;;) { 2352cc2df49SGarrett Wollman IF_DEQUEUE(&ifp->if_snd, m); 2362cc2df49SGarrett Wollman if (m == 0) 2372cc2df49SGarrett Wollman break; 2382cc2df49SGarrett Wollman if (ifp->if_bpf) 239eb92a347SGarrett Wollman bpf_mtap(ifp, m); 2402cc2df49SGarrett Wollman 241f731f104SBill Paul /* 24224993214SYaroslav Tykhiy * Do not run parent's if_start() if the parent is not up, 24324993214SYaroslav Tykhiy * or parent's driver will cause a system crash. 24424993214SYaroslav Tykhiy */ 24524993214SYaroslav Tykhiy if ((p->if_flags & (IFF_UP | IFF_RUNNING)) != 24624993214SYaroslav Tykhiy (IFF_UP | IFF_RUNNING)) { 24724993214SYaroslav Tykhiy m_freem(m); 24824993214SYaroslav Tykhiy ifp->if_data.ifi_collisions++; 24924993214SYaroslav Tykhiy continue; 25024993214SYaroslav Tykhiy } 25124993214SYaroslav Tykhiy 25224993214SYaroslav Tykhiy /* 253f731f104SBill Paul * If the LINK0 flag is set, it means the underlying interface 254f731f104SBill Paul * can do VLAN tag insertion itself and doesn't require us to 255f731f104SBill Paul * create a special header for it. In this case, we just pass 256f731f104SBill Paul * the packet along. However, we need some way to tell the 257f731f104SBill Paul * interface where the packet came from so that it knows how 258f731f104SBill Paul * to find the VLAN tag to use, so we set the rcvif in the 259f731f104SBill Paul * mbuf header to our ifnet. 260f731f104SBill Paul * 261f731f104SBill Paul * Note: we also set the M_PROTO1 flag in the mbuf to let 262f731f104SBill Paul * the parent driver know that the rcvif pointer is really 263f731f104SBill Paul * valid. We need to do this because sometimes mbufs will 264f731f104SBill Paul * be allocated by other parts of the system that contain 265f731f104SBill Paul * garbage in the rcvif pointer. Using the M_PROTO1 flag 266f731f104SBill Paul * lets the driver perform a proper sanity check and avoid 267f731f104SBill Paul * following potentially bogus rcvif pointers off into 268f731f104SBill Paul * never-never land. 269f731f104SBill Paul */ 270f731f104SBill Paul if (ifp->if_flags & IFF_LINK0) { 271f731f104SBill Paul m->m_pkthdr.rcvif = ifp; 272f731f104SBill Paul m->m_flags |= M_PROTO1; 273f731f104SBill Paul } else { 2742cc2df49SGarrett Wollman M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT); 2754af90a4dSMatthew N. Dodd if (m == NULL) { 2764af90a4dSMatthew N. Dodd printf("vlan%d: M_PREPEND failed", ifp->if_unit); 2774af90a4dSMatthew N. Dodd ifp->if_ierrors++; 2782cc2df49SGarrett Wollman continue; 2794af90a4dSMatthew N. Dodd } 2802cc2df49SGarrett Wollman /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ 2812cc2df49SGarrett Wollman 2825326b23cSMatthew N. Dodd m = m_pullup(m, ETHER_HDR_LEN + EVL_ENCAPLEN); 2835326b23cSMatthew N. Dodd if (m == NULL) { 2844af90a4dSMatthew N. Dodd printf("vlan%d: m_pullup failed", ifp->if_unit); 2854af90a4dSMatthew N. Dodd ifp->if_ierrors++; 2864af90a4dSMatthew N. Dodd continue; 2874af90a4dSMatthew N. Dodd } 2884af90a4dSMatthew N. Dodd 2892cc2df49SGarrett Wollman /* 2902cc2df49SGarrett Wollman * Transform the Ethernet header into an Ethernet header 2912cc2df49SGarrett Wollman * with 802.1Q encapsulation. 2922cc2df49SGarrett Wollman */ 2932cc2df49SGarrett Wollman bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *), 2942cc2df49SGarrett Wollman sizeof(struct ether_header)); 2952cc2df49SGarrett Wollman evl = mtod(m, struct ether_vlan_header *); 2962cc2df49SGarrett Wollman evl->evl_proto = evl->evl_encap_proto; 2972cc2df49SGarrett Wollman evl->evl_encap_proto = htons(vlan_proto); 2982cc2df49SGarrett Wollman evl->evl_tag = htons(ifv->ifv_tag); 299f731f104SBill Paul #ifdef DEBUG 300f731f104SBill Paul printf("vlan_start: %*D\n", sizeof *evl, 30165f1bb65SPeter Wemm (unsigned char *)evl, ":"); 302f731f104SBill Paul #endif 303f731f104SBill Paul } 3042cc2df49SGarrett Wollman 3052cc2df49SGarrett Wollman /* 3062cc2df49SGarrett Wollman * Send it, precisely as ether_output() would have. 3072cc2df49SGarrett Wollman * We are already running at splimp. 3082cc2df49SGarrett Wollman */ 309df5e1987SJonathan Lemon if (IF_HANDOFF(&p->if_snd, m, p)) 310f731f104SBill Paul ifp->if_opackets++; 311df5e1987SJonathan Lemon else 312df5e1987SJonathan Lemon ifp->if_oerrors++; 3132cc2df49SGarrett Wollman } 3142cc2df49SGarrett Wollman ifp->if_flags &= ~IFF_OACTIVE; 315f731f104SBill Paul 316f731f104SBill Paul return; 317f731f104SBill Paul } 318f731f104SBill Paul 319ae290324SJordan K. Hubbard int 320f731f104SBill Paul vlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t) 321f731f104SBill Paul { 322f731f104SBill Paul int i; 323f731f104SBill Paul struct ifvlan *ifv; 324f731f104SBill Paul 325f731f104SBill Paul for (i = 0; i < NVLAN; i++) { 326f731f104SBill Paul ifv = &ifv_softc[i]; 327f731f104SBill Paul if (ifv->ifv_tag == t) 328f731f104SBill Paul break; 329f731f104SBill Paul } 330f731f104SBill Paul 331f731f104SBill Paul if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 332c0230c1bSJordan K. Hubbard m_free(m); 333c0230c1bSJordan K. Hubbard return -1; /* So the parent can take note */ 334f731f104SBill Paul } 335f731f104SBill Paul 336f731f104SBill Paul /* 337f731f104SBill Paul * Having found a valid vlan interface corresponding to 338f731f104SBill Paul * the given source interface and vlan tag, run the 339f731f104SBill Paul * the real packet through ethert_input(). 340f731f104SBill Paul */ 341f731f104SBill Paul m->m_pkthdr.rcvif = &ifv->ifv_if; 342f731f104SBill Paul 343f731f104SBill Paul ifv->ifv_if.if_ipackets++; 344f731f104SBill Paul ether_input(&ifv->ifv_if, eh, m); 345c0230c1bSJordan K. Hubbard return 0; 3462cc2df49SGarrett Wollman } 3472cc2df49SGarrett Wollman 3482cc2df49SGarrett Wollman int 3492cc2df49SGarrett Wollman vlan_input(struct ether_header *eh, struct mbuf *m) 3502cc2df49SGarrett Wollman { 3512cc2df49SGarrett Wollman int i; 3522cc2df49SGarrett Wollman struct ifvlan *ifv; 3532cc2df49SGarrett Wollman 3542cc2df49SGarrett Wollman for (i = 0; i < NVLAN; i++) { 3552cc2df49SGarrett Wollman ifv = &ifv_softc[i]; 3562cc2df49SGarrett Wollman if (m->m_pkthdr.rcvif == ifv->ifv_p 3572cc2df49SGarrett Wollman && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *))) 3582cc2df49SGarrett Wollman == ifv->ifv_tag)) 3592cc2df49SGarrett Wollman break; 3602cc2df49SGarrett Wollman } 3612cc2df49SGarrett Wollman 3622cc2df49SGarrett Wollman if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 3632cc2df49SGarrett Wollman m_freem(m); 3642cc2df49SGarrett Wollman return -1; /* so ether_input can take note */ 3652cc2df49SGarrett Wollman } 3662cc2df49SGarrett Wollman 3672cc2df49SGarrett Wollman /* 3682cc2df49SGarrett Wollman * Having found a valid vlan interface corresponding to 3692cc2df49SGarrett Wollman * the given source interface and vlan tag, remove the 3702cc2df49SGarrett Wollman * encapsulation, and run the real packet through 3712cc2df49SGarrett Wollman * ether_input() a second time (it had better be 3722cc2df49SGarrett Wollman * reentrant!). 3732cc2df49SGarrett Wollman */ 3742cc2df49SGarrett Wollman m->m_pkthdr.rcvif = &ifv->ifv_if; 3752cc2df49SGarrett Wollman eh->ether_type = mtod(m, u_int16_t *)[1]; 3762cc2df49SGarrett Wollman m->m_data += EVL_ENCAPLEN; 3772cc2df49SGarrett Wollman m->m_len -= EVL_ENCAPLEN; 3782cc2df49SGarrett Wollman m->m_pkthdr.len -= EVL_ENCAPLEN; 3792cc2df49SGarrett Wollman 380f731f104SBill Paul ifv->ifv_if.if_ipackets++; 3812cc2df49SGarrett Wollman ether_input(&ifv->ifv_if, eh, m); 3822cc2df49SGarrett Wollman return 0; 3832cc2df49SGarrett Wollman } 3842cc2df49SGarrett Wollman 3852cc2df49SGarrett Wollman static int 3862cc2df49SGarrett Wollman vlan_config(struct ifvlan *ifv, struct ifnet *p) 3872cc2df49SGarrett Wollman { 3882cc2df49SGarrett Wollman struct ifaddr *ifa1, *ifa2; 3892cc2df49SGarrett Wollman struct sockaddr_dl *sdl1, *sdl2; 3902cc2df49SGarrett Wollman 3912cc2df49SGarrett Wollman if (p->if_data.ifi_type != IFT_ETHER) 3922cc2df49SGarrett Wollman return EPROTONOSUPPORT; 3932cc2df49SGarrett Wollman if (ifv->ifv_p) 3942cc2df49SGarrett Wollman return EBUSY; 3952cc2df49SGarrett Wollman ifv->ifv_p = p; 3962cc2df49SGarrett Wollman if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header)) 3972cc2df49SGarrett Wollman ifv->ifv_if.if_mtu = p->if_mtu; 3982cc2df49SGarrett Wollman else 3992cc2df49SGarrett Wollman ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN; 4002cc2df49SGarrett Wollman 4012cc2df49SGarrett Wollman /* 40224993214SYaroslav Tykhiy * Copy only a selected subset of flags from the parent. 40324993214SYaroslav Tykhiy * Other flags are none of our business. 4042cc2df49SGarrett Wollman */ 40524993214SYaroslav Tykhiy ifv->ifv_if.if_flags = (p->if_flags & 40624993214SYaroslav Tykhiy (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT)); 4072cc2df49SGarrett Wollman 4082cc2df49SGarrett Wollman /* 4092cc2df49SGarrett Wollman * Set up our ``Ethernet address'' to reflect the underlying 4102cc2df49SGarrett Wollman * physical interface's. 4112cc2df49SGarrett Wollman */ 4122cc2df49SGarrett Wollman ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1]; 4132cc2df49SGarrett Wollman ifa2 = ifnet_addrs[p->if_index - 1]; 4142cc2df49SGarrett Wollman sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; 4152cc2df49SGarrett Wollman sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; 4162cc2df49SGarrett Wollman sdl1->sdl_type = IFT_ETHER; 4172cc2df49SGarrett Wollman sdl1->sdl_alen = ETHER_ADDR_LEN; 4182cc2df49SGarrett Wollman bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); 4192cc2df49SGarrett Wollman bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 4201b2a4f7aSBill Fenner 4211b2a4f7aSBill Fenner /* 4221b2a4f7aSBill Fenner * Configure multicast addresses that may already be 4231b2a4f7aSBill Fenner * joined on the vlan device. 4241b2a4f7aSBill Fenner */ 4251b2a4f7aSBill Fenner (void)vlan_setmulti(&ifv->ifv_if); 4261b2a4f7aSBill Fenner 4272cc2df49SGarrett Wollman return 0; 4282cc2df49SGarrett Wollman } 4292cc2df49SGarrett Wollman 4302cc2df49SGarrett Wollman static int 431f731f104SBill Paul vlan_unconfig(struct ifnet *ifp) 432f731f104SBill Paul { 433f731f104SBill Paul struct ifaddr *ifa; 434f731f104SBill Paul struct sockaddr_dl *sdl; 435f731f104SBill Paul struct vlan_mc_entry *mc; 436f731f104SBill Paul struct ifvlan *ifv; 437f731f104SBill Paul struct ifnet *p; 438f731f104SBill Paul int error; 439f731f104SBill Paul 440f731f104SBill Paul ifv = ifp->if_softc; 441f731f104SBill Paul p = ifv->ifv_p; 442f731f104SBill Paul 4431b2a4f7aSBill Fenner if (p) { 4441b2a4f7aSBill Fenner struct sockaddr_dl sdl; 4451b2a4f7aSBill Fenner 446f731f104SBill Paul /* 447f731f104SBill Paul * Since the interface is being unconfigured, we need to 448f731f104SBill Paul * empty the list of multicast groups that we may have joined 4491b2a4f7aSBill Fenner * while we were alive from the parent's list. 450f731f104SBill Paul */ 4511b2a4f7aSBill Fenner bzero((char *)&sdl, sizeof sdl); 4521b2a4f7aSBill Fenner sdl.sdl_len = sizeof sdl; 453f731f104SBill Paul sdl.sdl_family = AF_LINK; 4541b2a4f7aSBill Fenner sdl.sdl_index = p->if_index; 4551b2a4f7aSBill Fenner sdl.sdl_type = IFT_ETHER; 4561b2a4f7aSBill Fenner sdl.sdl_alen = ETHER_ADDR_LEN; 4571b2a4f7aSBill Fenner 4581b2a4f7aSBill Fenner while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) { 45922f29826SPoul-Henning Kamp mc = SLIST_FIRST(&ifv->vlan_mc_listhead); 460f731f104SBill Paul bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 461f731f104SBill Paul error = if_delmulti(p, (struct sockaddr *)&sdl); 462f731f104SBill Paul if (error) 463f731f104SBill Paul return(error); 464f731f104SBill Paul SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); 465f731f104SBill Paul free(mc, M_DEVBUF); 466f731f104SBill Paul } 4671b2a4f7aSBill Fenner } 468f731f104SBill Paul 469f731f104SBill Paul /* Disconnect from parent. */ 470f731f104SBill Paul ifv->ifv_p = NULL; 471f731f104SBill Paul ifv->ifv_if.if_mtu = ETHERMTU; 472f731f104SBill Paul 473f731f104SBill Paul /* Clear our MAC address. */ 474f731f104SBill Paul ifa = ifnet_addrs[ifv->ifv_if.if_index - 1]; 475f731f104SBill Paul sdl = (struct sockaddr_dl *)ifa->ifa_addr; 476f731f104SBill Paul sdl->sdl_type = IFT_ETHER; 477f731f104SBill Paul sdl->sdl_alen = ETHER_ADDR_LEN; 478f731f104SBill Paul bzero(LLADDR(sdl), ETHER_ADDR_LEN); 479f731f104SBill Paul bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 480f731f104SBill Paul 481f731f104SBill Paul return 0; 482f731f104SBill Paul } 483f731f104SBill Paul 484f731f104SBill Paul static int 485cfe8b629SGarrett Wollman vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 4862cc2df49SGarrett Wollman { 4872cc2df49SGarrett Wollman struct ifaddr *ifa; 4882cc2df49SGarrett Wollman struct ifnet *p; 4892cc2df49SGarrett Wollman struct ifreq *ifr; 4902cc2df49SGarrett Wollman struct ifvlan *ifv; 4912cc2df49SGarrett Wollman struct vlanreq vlr; 4922cc2df49SGarrett Wollman int error = 0; 4932cc2df49SGarrett Wollman 4942cc2df49SGarrett Wollman ifr = (struct ifreq *)data; 4952cc2df49SGarrett Wollman ifa = (struct ifaddr *)data; 4962cc2df49SGarrett Wollman ifv = ifp->if_softc; 4972cc2df49SGarrett Wollman 4982cc2df49SGarrett Wollman switch (cmd) { 4992cc2df49SGarrett Wollman case SIOCSIFADDR: 5002cc2df49SGarrett Wollman ifp->if_flags |= IFF_UP; 5012cc2df49SGarrett Wollman 5022cc2df49SGarrett Wollman switch (ifa->ifa_addr->sa_family) { 5032cc2df49SGarrett Wollman #ifdef INET 5042cc2df49SGarrett Wollman case AF_INET: 5052cc2df49SGarrett Wollman arp_ifinit(&ifv->ifv_ac, ifa); 5062cc2df49SGarrett Wollman break; 5072cc2df49SGarrett Wollman #endif 5082cc2df49SGarrett Wollman default: 5092cc2df49SGarrett Wollman break; 5102cc2df49SGarrett Wollman } 5112cc2df49SGarrett Wollman break; 5122cc2df49SGarrett Wollman 5132cc2df49SGarrett Wollman case SIOCGIFADDR: 5142cc2df49SGarrett Wollman { 5152cc2df49SGarrett Wollman struct sockaddr *sa; 5162cc2df49SGarrett Wollman 5172cc2df49SGarrett Wollman sa = (struct sockaddr *) &ifr->ifr_data; 5182cc2df49SGarrett Wollman bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, 5192cc2df49SGarrett Wollman (caddr_t) sa->sa_data, ETHER_ADDR_LEN); 5202cc2df49SGarrett Wollman } 5212cc2df49SGarrett Wollman break; 5222cc2df49SGarrett Wollman 5232cc2df49SGarrett Wollman case SIOCSIFMTU: 5242cc2df49SGarrett Wollman /* 5252cc2df49SGarrett Wollman * Set the interface MTU. 526f731f104SBill Paul * This is bogus. The underlying interface might support 527f731f104SBill Paul * jumbo frames. 5282cc2df49SGarrett Wollman */ 5292cc2df49SGarrett Wollman if (ifr->ifr_mtu > ETHERMTU) { 5302cc2df49SGarrett Wollman error = EINVAL; 5312cc2df49SGarrett Wollman } else { 5322cc2df49SGarrett Wollman ifp->if_mtu = ifr->ifr_mtu; 5332cc2df49SGarrett Wollman } 5342cc2df49SGarrett Wollman break; 5352cc2df49SGarrett Wollman 5362cc2df49SGarrett Wollman case SIOCSETVLAN: 5372cc2df49SGarrett Wollman error = copyin(ifr->ifr_data, &vlr, sizeof vlr); 5382cc2df49SGarrett Wollman if (error) 5392cc2df49SGarrett Wollman break; 5402cc2df49SGarrett Wollman if (vlr.vlr_parent[0] == '\0') { 541f731f104SBill Paul vlan_unconfig(ifp); 54224993214SYaroslav Tykhiy if (ifp->if_flags & IFF_UP) { 54324993214SYaroslav Tykhiy int s = splimp(); 5442cc2df49SGarrett Wollman if_down(ifp); 54524993214SYaroslav Tykhiy splx(s); 54624993214SYaroslav Tykhiy } 54724993214SYaroslav Tykhiy ifp->if_flags &= ~IFF_RUNNING; 5482cc2df49SGarrett Wollman break; 5492cc2df49SGarrett Wollman } 5502cc2df49SGarrett Wollman p = ifunit(vlr.vlr_parent); 5512cc2df49SGarrett Wollman if (p == 0) { 5522cc2df49SGarrett Wollman error = ENOENT; 5532cc2df49SGarrett Wollman break; 5542cc2df49SGarrett Wollman } 5552cc2df49SGarrett Wollman error = vlan_config(ifv, p); 5562cc2df49SGarrett Wollman if (error) 5572cc2df49SGarrett Wollman break; 5582cc2df49SGarrett Wollman ifv->ifv_tag = vlr.vlr_tag; 559ae290324SJordan K. Hubbard ifp->if_flags |= IFF_RUNNING; 5602cc2df49SGarrett Wollman break; 5612cc2df49SGarrett Wollman 5622cc2df49SGarrett Wollman case SIOCGETVLAN: 5632cc2df49SGarrett Wollman bzero(&vlr, sizeof vlr); 5642cc2df49SGarrett Wollman if (ifv->ifv_p) { 5652127f260SArchie Cobbs snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), 5662127f260SArchie Cobbs "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit); 5672cc2df49SGarrett Wollman vlr.vlr_tag = ifv->ifv_tag; 5682cc2df49SGarrett Wollman } 5692cc2df49SGarrett Wollman error = copyout(&vlr, ifr->ifr_data, sizeof vlr); 5702cc2df49SGarrett Wollman break; 5712cc2df49SGarrett Wollman 5722cc2df49SGarrett Wollman case SIOCSIFFLAGS: 5732cc2df49SGarrett Wollman /* 574f731f104SBill Paul * We don't support promiscuous mode 5752cc2df49SGarrett Wollman * right now because it would require help from the 5762cc2df49SGarrett Wollman * underlying drivers, which hasn't been implemented. 5772cc2df49SGarrett Wollman */ 578f731f104SBill Paul if (ifr->ifr_flags & (IFF_PROMISC)) { 579f731f104SBill Paul ifp->if_flags &= ~(IFF_PROMISC); 5802cc2df49SGarrett Wollman error = EINVAL; 5812cc2df49SGarrett Wollman } 5822cc2df49SGarrett Wollman break; 583f731f104SBill Paul case SIOCADDMULTI: 584f731f104SBill Paul case SIOCDELMULTI: 585f731f104SBill Paul error = vlan_setmulti(ifp); 586f731f104SBill Paul break; 5872cc2df49SGarrett Wollman default: 5882cc2df49SGarrett Wollman error = EINVAL; 5892cc2df49SGarrett Wollman } 5902cc2df49SGarrett Wollman return error; 5912cc2df49SGarrett Wollman } 592