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); 852cc2df49SGarrett Wollman SYSCTL_NODE(_net_link, IFT_8021_VLAN, 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 12324993214SYaroslav Tykhiy bzero((char *)&sdl, sizeof sdl); 12424993214SYaroslav Tykhiy sdl.sdl_len = sizeof sdl; 125f731f104SBill Paul sdl.sdl_family = AF_LINK; 12624993214SYaroslav Tykhiy sdl.sdl_alen = ETHER_ADDR_LEN; 127f731f104SBill Paul 128f731f104SBill Paul /* First, remove any existing filter entries. */ 12922f29826SPoul-Henning Kamp while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) { 13022f29826SPoul-Henning Kamp mc = SLIST_FIRST(&sc->vlan_mc_listhead); 131f731f104SBill Paul bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 132f731f104SBill Paul error = if_delmulti(ifp_p, (struct sockaddr *)&sdl); 133f731f104SBill Paul if (error) 134f731f104SBill Paul return(error); 135f731f104SBill Paul SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); 136f731f104SBill Paul free(mc, M_DEVBUF); 137f731f104SBill Paul } 138f731f104SBill Paul 139f731f104SBill Paul /* Now program new ones. */ 1406817526dSPoul-Henning Kamp TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 141f731f104SBill Paul if (ifma->ifma_addr->sa_family != AF_LINK) 142f731f104SBill Paul continue; 14324993214SYaroslav Tykhiy mc = malloc(sizeof(struct vlan_mc_entry), M_DEVBUF, M_WAITOK); 144f731f104SBill Paul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 145f731f104SBill Paul (char *)&mc->mc_addr, ETHER_ADDR_LEN); 146f731f104SBill Paul SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); 147f731f104SBill Paul error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma); 148f731f104SBill Paul if (error) 149f731f104SBill Paul return(error); 150f731f104SBill Paul } 151f731f104SBill Paul 152f731f104SBill Paul return(0); 153f731f104SBill Paul } 1542cc2df49SGarrett Wollman 1552cc2df49SGarrett Wollman static void 1562b120974SPeter Wemm vlaninit(void) 1572cc2df49SGarrett Wollman { 1582cc2df49SGarrett Wollman int i; 1592cc2df49SGarrett Wollman 1602cc2df49SGarrett Wollman for (i = 0; i < NVLAN; i++) { 1612cc2df49SGarrett Wollman struct ifnet *ifp = &ifv_softc[i].ifv_if; 1622cc2df49SGarrett Wollman 1632cc2df49SGarrett Wollman ifp->if_softc = &ifv_softc[i]; 1642cc2df49SGarrett Wollman ifp->if_name = "vlan"; 1652cc2df49SGarrett Wollman ifp->if_unit = i; 1662cc2df49SGarrett Wollman /* NB: flags are not set here */ 1672cc2df49SGarrett Wollman ifp->if_linkmib = &ifv_softc[i].ifv_mib; 1682cc2df49SGarrett Wollman ifp->if_linkmiblen = sizeof ifv_softc[i].ifv_mib; 1692cc2df49SGarrett Wollman /* NB: mtu is not set here */ 1702cc2df49SGarrett Wollman 1712cc2df49SGarrett Wollman ifp->if_init = vlan_ifinit; 1722cc2df49SGarrett Wollman ifp->if_start = vlan_start; 1732cc2df49SGarrett Wollman ifp->if_ioctl = vlan_ioctl; 1742cc2df49SGarrett Wollman ifp->if_output = ether_output; 175f731f104SBill Paul ifp->if_snd.ifq_maxlen = ifqmaxlen; 17621b8ebd9SArchie Cobbs ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 1772cc2df49SGarrett Wollman /* Now undo some of the damage... */ 1782cc2df49SGarrett Wollman ifp->if_data.ifi_type = IFT_8021_VLAN; 1792cc2df49SGarrett Wollman ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN; 1802cc2df49SGarrett Wollman ifp->if_resolvemulti = 0; 1812cc2df49SGarrett Wollman } 1822cc2df49SGarrett Wollman } 1832b120974SPeter Wemm 1842b120974SPeter Wemm static int 1852b120974SPeter Wemm vlan_modevent(module_t mod, int type, void *data) 1862b120974SPeter Wemm { 1872b120974SPeter Wemm switch (type) { 1882b120974SPeter Wemm case MOD_LOAD: 1892b120974SPeter Wemm vlaninit(); 1902b120974SPeter Wemm break; 1912b120974SPeter Wemm case MOD_UNLOAD: 1922b120974SPeter Wemm printf("if_vlan module unload - not possible for this module type\n"); 1932b120974SPeter Wemm return EINVAL; 1942b120974SPeter Wemm } 1952b120974SPeter Wemm return 0; 1962b120974SPeter Wemm } 1972b120974SPeter Wemm 1982b120974SPeter Wemm static moduledata_t vlan_mod = { 1992b120974SPeter Wemm "if_vlan", 2002b120974SPeter Wemm vlan_modevent, 2012b120974SPeter Wemm 0 2022b120974SPeter Wemm }; 2032b120974SPeter Wemm 2042b120974SPeter Wemm DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 2052cc2df49SGarrett Wollman 2062cc2df49SGarrett Wollman static void 2072cc2df49SGarrett Wollman vlan_ifinit(void *foo) 2082cc2df49SGarrett Wollman { 209f731f104SBill Paul return; 2102cc2df49SGarrett Wollman } 2112cc2df49SGarrett Wollman 2122cc2df49SGarrett Wollman static void 2132cc2df49SGarrett Wollman vlan_start(struct ifnet *ifp) 2142cc2df49SGarrett Wollman { 2152cc2df49SGarrett Wollman struct ifvlan *ifv; 2162cc2df49SGarrett Wollman struct ifnet *p; 2172cc2df49SGarrett Wollman struct ether_vlan_header *evl; 2185326b23cSMatthew N. Dodd struct mbuf *m; 2192cc2df49SGarrett Wollman 2202cc2df49SGarrett Wollman ifv = ifp->if_softc; 2212cc2df49SGarrett Wollman p = ifv->ifv_p; 2222cc2df49SGarrett Wollman 2232cc2df49SGarrett Wollman ifp->if_flags |= IFF_OACTIVE; 2242cc2df49SGarrett Wollman for (;;) { 2252cc2df49SGarrett Wollman IF_DEQUEUE(&ifp->if_snd, m); 2262cc2df49SGarrett Wollman if (m == 0) 2272cc2df49SGarrett Wollman break; 2282cc2df49SGarrett Wollman if (ifp->if_bpf) 229eb92a347SGarrett Wollman bpf_mtap(ifp, m); 2302cc2df49SGarrett Wollman 231f731f104SBill Paul /* 23224993214SYaroslav Tykhiy * Do not run parent's if_start() if the parent is not up, 23324993214SYaroslav Tykhiy * or parent's driver will cause a system crash. 23424993214SYaroslav Tykhiy */ 23524993214SYaroslav Tykhiy if ((p->if_flags & (IFF_UP | IFF_RUNNING)) != 23624993214SYaroslav Tykhiy (IFF_UP | IFF_RUNNING)) { 23724993214SYaroslav Tykhiy m_freem(m); 23824993214SYaroslav Tykhiy ifp->if_data.ifi_collisions++; 23924993214SYaroslav Tykhiy continue; 24024993214SYaroslav Tykhiy } 24124993214SYaroslav Tykhiy 24224993214SYaroslav Tykhiy /* 243f731f104SBill Paul * If the LINK0 flag is set, it means the underlying interface 244f731f104SBill Paul * can do VLAN tag insertion itself and doesn't require us to 245f731f104SBill Paul * create a special header for it. In this case, we just pass 246f731f104SBill Paul * the packet along. However, we need some way to tell the 247f731f104SBill Paul * interface where the packet came from so that it knows how 248f731f104SBill Paul * to find the VLAN tag to use, so we set the rcvif in the 249f731f104SBill Paul * mbuf header to our ifnet. 250f731f104SBill Paul * 251f731f104SBill Paul * Note: we also set the M_PROTO1 flag in the mbuf to let 252f731f104SBill Paul * the parent driver know that the rcvif pointer is really 253f731f104SBill Paul * valid. We need to do this because sometimes mbufs will 254f731f104SBill Paul * be allocated by other parts of the system that contain 255f731f104SBill Paul * garbage in the rcvif pointer. Using the M_PROTO1 flag 256f731f104SBill Paul * lets the driver perform a proper sanity check and avoid 257f731f104SBill Paul * following potentially bogus rcvif pointers off into 258f731f104SBill Paul * never-never land. 259f731f104SBill Paul */ 260f731f104SBill Paul if (ifp->if_flags & IFF_LINK0) { 261f731f104SBill Paul m->m_pkthdr.rcvif = ifp; 262f731f104SBill Paul m->m_flags |= M_PROTO1; 263f731f104SBill Paul } else { 2642cc2df49SGarrett Wollman M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT); 2654af90a4dSMatthew N. Dodd if (m == NULL) { 2664af90a4dSMatthew N. Dodd printf("vlan%d: M_PREPEND failed", ifp->if_unit); 2674af90a4dSMatthew N. Dodd ifp->if_ierrors++; 2682cc2df49SGarrett Wollman continue; 2694af90a4dSMatthew N. Dodd } 2702cc2df49SGarrett Wollman /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ 2712cc2df49SGarrett Wollman 2725326b23cSMatthew N. Dodd m = m_pullup(m, ETHER_HDR_LEN + EVL_ENCAPLEN); 2735326b23cSMatthew N. Dodd if (m == NULL) { 2744af90a4dSMatthew N. Dodd printf("vlan%d: m_pullup failed", ifp->if_unit); 2754af90a4dSMatthew N. Dodd ifp->if_ierrors++; 2764af90a4dSMatthew N. Dodd continue; 2774af90a4dSMatthew N. Dodd } 2784af90a4dSMatthew N. Dodd 2792cc2df49SGarrett Wollman /* 2802cc2df49SGarrett Wollman * Transform the Ethernet header into an Ethernet header 2812cc2df49SGarrett Wollman * with 802.1Q encapsulation. 2822cc2df49SGarrett Wollman */ 2832cc2df49SGarrett Wollman bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *), 2842cc2df49SGarrett Wollman sizeof(struct ether_header)); 2852cc2df49SGarrett Wollman evl = mtod(m, struct ether_vlan_header *); 2862cc2df49SGarrett Wollman evl->evl_proto = evl->evl_encap_proto; 2872cc2df49SGarrett Wollman evl->evl_encap_proto = htons(vlan_proto); 2882cc2df49SGarrett Wollman evl->evl_tag = htons(ifv->ifv_tag); 289f731f104SBill Paul #ifdef DEBUG 290f731f104SBill Paul printf("vlan_start: %*D\n", sizeof *evl, 291f731f104SBill Paul (char *)evl, ":"); 292f731f104SBill Paul #endif 293f731f104SBill Paul } 2942cc2df49SGarrett Wollman 2952cc2df49SGarrett Wollman /* 2962cc2df49SGarrett Wollman * Send it, precisely as ether_output() would have. 2972cc2df49SGarrett Wollman * We are already running at splimp. 2982cc2df49SGarrett Wollman */ 299df5e1987SJonathan Lemon if (IF_HANDOFF(&p->if_snd, m, p)) 300f731f104SBill Paul ifp->if_opackets++; 301df5e1987SJonathan Lemon else 302df5e1987SJonathan Lemon ifp->if_oerrors++; 3032cc2df49SGarrett Wollman } 3042cc2df49SGarrett Wollman ifp->if_flags &= ~IFF_OACTIVE; 305f731f104SBill Paul 306f731f104SBill Paul return; 307f731f104SBill Paul } 308f731f104SBill Paul 309ae290324SJordan K. Hubbard int 310f731f104SBill Paul vlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t) 311f731f104SBill Paul { 312f731f104SBill Paul int i; 313f731f104SBill Paul struct ifvlan *ifv; 314f731f104SBill Paul 315f731f104SBill Paul for (i = 0; i < NVLAN; i++) { 316f731f104SBill Paul ifv = &ifv_softc[i]; 317f731f104SBill Paul if (ifv->ifv_tag == t) 318f731f104SBill Paul break; 319f731f104SBill Paul } 320f731f104SBill Paul 321f731f104SBill Paul if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 322c0230c1bSJordan K. Hubbard m_free(m); 323c0230c1bSJordan K. Hubbard return -1; /* So the parent can take note */ 324f731f104SBill Paul } 325f731f104SBill Paul 326f731f104SBill Paul /* 327f731f104SBill Paul * Having found a valid vlan interface corresponding to 328f731f104SBill Paul * the given source interface and vlan tag, run the 329f731f104SBill Paul * the real packet through ethert_input(). 330f731f104SBill Paul */ 331f731f104SBill Paul m->m_pkthdr.rcvif = &ifv->ifv_if; 332f731f104SBill Paul 333f731f104SBill Paul ifv->ifv_if.if_ipackets++; 334f731f104SBill Paul ether_input(&ifv->ifv_if, eh, m); 335c0230c1bSJordan K. Hubbard return 0; 3362cc2df49SGarrett Wollman } 3372cc2df49SGarrett Wollman 3382cc2df49SGarrett Wollman int 3392cc2df49SGarrett Wollman vlan_input(struct ether_header *eh, struct mbuf *m) 3402cc2df49SGarrett Wollman { 3412cc2df49SGarrett Wollman int i; 3422cc2df49SGarrett Wollman struct ifvlan *ifv; 3432cc2df49SGarrett Wollman 3442cc2df49SGarrett Wollman for (i = 0; i < NVLAN; i++) { 3452cc2df49SGarrett Wollman ifv = &ifv_softc[i]; 3462cc2df49SGarrett Wollman if (m->m_pkthdr.rcvif == ifv->ifv_p 3472cc2df49SGarrett Wollman && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *))) 3482cc2df49SGarrett Wollman == ifv->ifv_tag)) 3492cc2df49SGarrett Wollman break; 3502cc2df49SGarrett Wollman } 3512cc2df49SGarrett Wollman 3522cc2df49SGarrett Wollman if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 3532cc2df49SGarrett Wollman m_freem(m); 3542cc2df49SGarrett Wollman return -1; /* so ether_input can take note */ 3552cc2df49SGarrett Wollman } 3562cc2df49SGarrett Wollman 3572cc2df49SGarrett Wollman /* 3582cc2df49SGarrett Wollman * Having found a valid vlan interface corresponding to 3592cc2df49SGarrett Wollman * the given source interface and vlan tag, remove the 3602cc2df49SGarrett Wollman * encapsulation, and run the real packet through 3612cc2df49SGarrett Wollman * ether_input() a second time (it had better be 3622cc2df49SGarrett Wollman * reentrant!). 3632cc2df49SGarrett Wollman */ 3642cc2df49SGarrett Wollman m->m_pkthdr.rcvif = &ifv->ifv_if; 3652cc2df49SGarrett Wollman eh->ether_type = mtod(m, u_int16_t *)[1]; 3662cc2df49SGarrett Wollman m->m_data += EVL_ENCAPLEN; 3672cc2df49SGarrett Wollman m->m_len -= EVL_ENCAPLEN; 3682cc2df49SGarrett Wollman m->m_pkthdr.len -= EVL_ENCAPLEN; 3692cc2df49SGarrett Wollman 370f731f104SBill Paul ifv->ifv_if.if_ipackets++; 3712cc2df49SGarrett Wollman ether_input(&ifv->ifv_if, eh, m); 3722cc2df49SGarrett Wollman return 0; 3732cc2df49SGarrett Wollman } 3742cc2df49SGarrett Wollman 3752cc2df49SGarrett Wollman static int 3762cc2df49SGarrett Wollman vlan_config(struct ifvlan *ifv, struct ifnet *p) 3772cc2df49SGarrett Wollman { 3782cc2df49SGarrett Wollman struct ifaddr *ifa1, *ifa2; 3792cc2df49SGarrett Wollman struct sockaddr_dl *sdl1, *sdl2; 3802cc2df49SGarrett Wollman 3812cc2df49SGarrett Wollman if (p->if_data.ifi_type != IFT_ETHER) 3822cc2df49SGarrett Wollman return EPROTONOSUPPORT; 3832cc2df49SGarrett Wollman if (ifv->ifv_p) 3842cc2df49SGarrett Wollman return EBUSY; 3852cc2df49SGarrett Wollman ifv->ifv_p = p; 3862cc2df49SGarrett Wollman if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header)) 3872cc2df49SGarrett Wollman ifv->ifv_if.if_mtu = p->if_mtu; 3882cc2df49SGarrett Wollman else 3892cc2df49SGarrett Wollman ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN; 3902cc2df49SGarrett Wollman 3912cc2df49SGarrett Wollman /* 39224993214SYaroslav Tykhiy * Copy only a selected subset of flags from the parent. 39324993214SYaroslav Tykhiy * Other flags are none of our business. 3942cc2df49SGarrett Wollman */ 39524993214SYaroslav Tykhiy ifv->ifv_if.if_flags = (p->if_flags & 39624993214SYaroslav Tykhiy (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT)); 3972cc2df49SGarrett Wollman 3982cc2df49SGarrett Wollman /* 3992cc2df49SGarrett Wollman * Set up our ``Ethernet address'' to reflect the underlying 4002cc2df49SGarrett Wollman * physical interface's. 4012cc2df49SGarrett Wollman */ 4022cc2df49SGarrett Wollman ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1]; 4032cc2df49SGarrett Wollman ifa2 = ifnet_addrs[p->if_index - 1]; 4042cc2df49SGarrett Wollman sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; 4052cc2df49SGarrett Wollman sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; 4062cc2df49SGarrett Wollman sdl1->sdl_type = IFT_ETHER; 4072cc2df49SGarrett Wollman sdl1->sdl_alen = ETHER_ADDR_LEN; 4082cc2df49SGarrett Wollman bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); 4092cc2df49SGarrett Wollman bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 4102cc2df49SGarrett Wollman return 0; 4112cc2df49SGarrett Wollman } 4122cc2df49SGarrett Wollman 4132cc2df49SGarrett Wollman static int 414f731f104SBill Paul vlan_unconfig(struct ifnet *ifp) 415f731f104SBill Paul { 416f731f104SBill Paul struct ifaddr *ifa; 417f731f104SBill Paul struct sockaddr_dl *sdl; 418f731f104SBill Paul struct vlan_mc_entry *mc; 419f731f104SBill Paul struct ifvlan *ifv; 420f731f104SBill Paul struct ifnet *p; 421f731f104SBill Paul int error; 422f731f104SBill Paul 423f731f104SBill Paul ifv = ifp->if_softc; 424f731f104SBill Paul p = ifv->ifv_p; 425f731f104SBill Paul 426f731f104SBill Paul /* 427f731f104SBill Paul * Since the interface is being unconfigured, we need to 428f731f104SBill Paul * empty the list of multicast groups that we may have joined 429f731f104SBill Paul * while we were alive and remove them from the parent's list 430f731f104SBill Paul * as well. 431f731f104SBill Paul */ 43222f29826SPoul-Henning Kamp while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) { 433f731f104SBill Paul struct sockaddr_dl sdl; 434f731f104SBill Paul 435f731f104SBill Paul sdl.sdl_len = ETHER_ADDR_LEN; 436f731f104SBill Paul sdl.sdl_family = AF_LINK; 43722f29826SPoul-Henning Kamp mc = SLIST_FIRST(&ifv->vlan_mc_listhead); 438f731f104SBill Paul bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 439f731f104SBill Paul error = if_delmulti(p, (struct sockaddr *)&sdl); 440f731f104SBill Paul error = if_delmulti(ifp, (struct sockaddr *)&sdl); 441f731f104SBill Paul if (error) 442f731f104SBill Paul return(error); 443f731f104SBill Paul SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); 444f731f104SBill Paul free(mc, M_DEVBUF); 445f731f104SBill Paul } 446f731f104SBill Paul 447f731f104SBill Paul /* Disconnect from parent. */ 448f731f104SBill Paul ifv->ifv_p = NULL; 449f731f104SBill Paul ifv->ifv_if.if_mtu = ETHERMTU; 450f731f104SBill Paul 451f731f104SBill Paul /* Clear our MAC address. */ 452f731f104SBill Paul ifa = ifnet_addrs[ifv->ifv_if.if_index - 1]; 453f731f104SBill Paul sdl = (struct sockaddr_dl *)ifa->ifa_addr; 454f731f104SBill Paul sdl->sdl_type = IFT_ETHER; 455f731f104SBill Paul sdl->sdl_alen = ETHER_ADDR_LEN; 456f731f104SBill Paul bzero(LLADDR(sdl), ETHER_ADDR_LEN); 457f731f104SBill Paul bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); 458f731f104SBill Paul 459f731f104SBill Paul return 0; 460f731f104SBill Paul } 461f731f104SBill Paul 462f731f104SBill Paul static int 463cfe8b629SGarrett Wollman vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 4642cc2df49SGarrett Wollman { 4652cc2df49SGarrett Wollman struct ifaddr *ifa; 4662cc2df49SGarrett Wollman struct ifnet *p; 4672cc2df49SGarrett Wollman struct ifreq *ifr; 4682cc2df49SGarrett Wollman struct ifvlan *ifv; 4692cc2df49SGarrett Wollman struct vlanreq vlr; 4702cc2df49SGarrett Wollman int error = 0; 4712cc2df49SGarrett Wollman 4722cc2df49SGarrett Wollman ifr = (struct ifreq *)data; 4732cc2df49SGarrett Wollman ifa = (struct ifaddr *)data; 4742cc2df49SGarrett Wollman ifv = ifp->if_softc; 4752cc2df49SGarrett Wollman 4762cc2df49SGarrett Wollman switch (cmd) { 4772cc2df49SGarrett Wollman case SIOCSIFADDR: 4782cc2df49SGarrett Wollman ifp->if_flags |= IFF_UP; 4792cc2df49SGarrett Wollman 4802cc2df49SGarrett Wollman switch (ifa->ifa_addr->sa_family) { 4812cc2df49SGarrett Wollman #ifdef INET 4822cc2df49SGarrett Wollman case AF_INET: 4832cc2df49SGarrett Wollman arp_ifinit(&ifv->ifv_ac, ifa); 4842cc2df49SGarrett Wollman break; 4852cc2df49SGarrett Wollman #endif 4862cc2df49SGarrett Wollman default: 4872cc2df49SGarrett Wollman break; 4882cc2df49SGarrett Wollman } 4892cc2df49SGarrett Wollman break; 4902cc2df49SGarrett Wollman 4912cc2df49SGarrett Wollman case SIOCGIFADDR: 4922cc2df49SGarrett Wollman { 4932cc2df49SGarrett Wollman struct sockaddr *sa; 4942cc2df49SGarrett Wollman 4952cc2df49SGarrett Wollman sa = (struct sockaddr *) &ifr->ifr_data; 4962cc2df49SGarrett Wollman bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, 4972cc2df49SGarrett Wollman (caddr_t) sa->sa_data, ETHER_ADDR_LEN); 4982cc2df49SGarrett Wollman } 4992cc2df49SGarrett Wollman break; 5002cc2df49SGarrett Wollman 5012cc2df49SGarrett Wollman case SIOCSIFMTU: 5022cc2df49SGarrett Wollman /* 5032cc2df49SGarrett Wollman * Set the interface MTU. 504f731f104SBill Paul * This is bogus. The underlying interface might support 505f731f104SBill Paul * jumbo frames. 5062cc2df49SGarrett Wollman */ 5072cc2df49SGarrett Wollman if (ifr->ifr_mtu > ETHERMTU) { 5082cc2df49SGarrett Wollman error = EINVAL; 5092cc2df49SGarrett Wollman } else { 5102cc2df49SGarrett Wollman ifp->if_mtu = ifr->ifr_mtu; 5112cc2df49SGarrett Wollman } 5122cc2df49SGarrett Wollman break; 5132cc2df49SGarrett Wollman 5142cc2df49SGarrett Wollman case SIOCSETVLAN: 5152cc2df49SGarrett Wollman error = copyin(ifr->ifr_data, &vlr, sizeof vlr); 5162cc2df49SGarrett Wollman if (error) 5172cc2df49SGarrett Wollman break; 5182cc2df49SGarrett Wollman if (vlr.vlr_parent[0] == '\0') { 519f731f104SBill Paul vlan_unconfig(ifp); 52024993214SYaroslav Tykhiy if (ifp->if_flags & IFF_UP) { 52124993214SYaroslav Tykhiy int s = splimp(); 5222cc2df49SGarrett Wollman if_down(ifp); 52324993214SYaroslav Tykhiy splx(s); 52424993214SYaroslav Tykhiy } 52524993214SYaroslav Tykhiy ifp->if_flags &= ~IFF_RUNNING; 5262cc2df49SGarrett Wollman break; 5272cc2df49SGarrett Wollman } 5282cc2df49SGarrett Wollman p = ifunit(vlr.vlr_parent); 5292cc2df49SGarrett Wollman if (p == 0) { 5302cc2df49SGarrett Wollman error = ENOENT; 5312cc2df49SGarrett Wollman break; 5322cc2df49SGarrett Wollman } 5332cc2df49SGarrett Wollman error = vlan_config(ifv, p); 5342cc2df49SGarrett Wollman if (error) 5352cc2df49SGarrett Wollman break; 5362cc2df49SGarrett Wollman ifv->ifv_tag = vlr.vlr_tag; 537ae290324SJordan K. Hubbard ifp->if_flags |= IFF_RUNNING; 5382cc2df49SGarrett Wollman break; 5392cc2df49SGarrett Wollman 5402cc2df49SGarrett Wollman case SIOCGETVLAN: 5412cc2df49SGarrett Wollman bzero(&vlr, sizeof vlr); 5422cc2df49SGarrett Wollman if (ifv->ifv_p) { 5432127f260SArchie Cobbs snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), 5442127f260SArchie Cobbs "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit); 5452cc2df49SGarrett Wollman vlr.vlr_tag = ifv->ifv_tag; 5462cc2df49SGarrett Wollman } 5472cc2df49SGarrett Wollman error = copyout(&vlr, ifr->ifr_data, sizeof vlr); 5482cc2df49SGarrett Wollman break; 5492cc2df49SGarrett Wollman 5502cc2df49SGarrett Wollman case SIOCSIFFLAGS: 5512cc2df49SGarrett Wollman /* 552f731f104SBill Paul * We don't support promiscuous mode 5532cc2df49SGarrett Wollman * right now because it would require help from the 5542cc2df49SGarrett Wollman * underlying drivers, which hasn't been implemented. 5552cc2df49SGarrett Wollman */ 556f731f104SBill Paul if (ifr->ifr_flags & (IFF_PROMISC)) { 557f731f104SBill Paul ifp->if_flags &= ~(IFF_PROMISC); 5582cc2df49SGarrett Wollman error = EINVAL; 5592cc2df49SGarrett Wollman } 5602cc2df49SGarrett Wollman break; 561f731f104SBill Paul case SIOCADDMULTI: 562f731f104SBill Paul case SIOCDELMULTI: 563f731f104SBill Paul error = vlan_setmulti(ifp); 564f731f104SBill Paul break; 5652cc2df49SGarrett Wollman default: 5662cc2df49SGarrett Wollman error = EINVAL; 5672cc2df49SGarrett Wollman } 5682cc2df49SGarrett Wollman return error; 5692cc2df49SGarrett Wollman } 570