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 * 29f731f104SBill Paul * $Id: if_vlan.c,v 1.9 1999/03/15 00:33:02 wpaul Exp $ 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 392cc2df49SGarrett Wollman * ether_output() left on our output queue 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 * 43f731f104SBill Paul * XXX It's incorrect to assume that we must always kludge up 44f731f104SBill Paul * headers on the physical device's behalf: some devices support 45f731f104SBill Paul * VLAN tag insersion and extraction in firmware. For these cases, 46f731f104SBill Paul * one can change the behavior of the vlan interface by setting 47f731f104SBill Paul * the LINK0 flag on it (that is setting the vlan interface's LINK0 48f731f104SBill Paul * flag, _not_ the parent's LINK0 flag; we try to leave the parent 49f731f104SBill Paul * alone). If the interface as the LINK0 flag set, then it will 50f731f104SBill Paul * not modify the ethernet header on output because the parent 51f731f104SBill Paul * can do that for itself. On input, the parent can call vlan_input_tag() 52f731f104SBill Paul * directly in order to supply us with an incoming mbuf and the vlan 53f731f104SBill Paul * tag value that goes with it. 542cc2df49SGarrett Wollman */ 552cc2df49SGarrett Wollman 562cc2df49SGarrett Wollman #include "vlan.h" 572cc2df49SGarrett Wollman #if NVLAN > 0 582cc2df49SGarrett Wollman #include "opt_inet.h" 592cc2df49SGarrett Wollman #include "bpfilter.h" 602cc2df49SGarrett Wollman 612cc2df49SGarrett Wollman #include <sys/param.h> 622cc2df49SGarrett Wollman #include <sys/kernel.h> 63f731f104SBill Paul #include <sys/malloc.h> 642cc2df49SGarrett Wollman #include <sys/mbuf.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 #if NBPFILTER > 0 722cc2df49SGarrett Wollman #include <net/bpf.h> 732cc2df49SGarrett Wollman #endif 742cc2df49SGarrett Wollman #include <net/ethernet.h> 752cc2df49SGarrett Wollman #include <net/if.h> 762cc2df49SGarrett Wollman #include <net/if_arp.h> 772cc2df49SGarrett Wollman #include <net/if_dl.h> 782cc2df49SGarrett Wollman #include <net/if_types.h> 792cc2df49SGarrett Wollman #include <net/if_vlan_var.h> 802cc2df49SGarrett Wollman 812cc2df49SGarrett Wollman #ifdef INET 822cc2df49SGarrett Wollman #include <netinet/in.h> 832cc2df49SGarrett Wollman #include <netinet/if_ether.h> 842cc2df49SGarrett Wollman #endif 852cc2df49SGarrett Wollman 862cc2df49SGarrett Wollman SYSCTL_NODE(_net_link, IFT_8021_VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); 872cc2df49SGarrett Wollman SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); 882cc2df49SGarrett Wollman 892cc2df49SGarrett Wollman u_int vlan_proto = ETHERTYPE_VLAN; 902cc2df49SGarrett Wollman SYSCTL_INT(_net_link_vlan_link, VLANCTL_PROTO, proto, CTLFLAG_RW, &vlan_proto, 912cc2df49SGarrett Wollman 0, "Ethernet protocol used for VLAN encapsulation"); 922cc2df49SGarrett Wollman 932cc2df49SGarrett Wollman static struct ifvlan ifv_softc[NVLAN]; 942cc2df49SGarrett Wollman 952cc2df49SGarrett Wollman static void vlan_start(struct ifnet *ifp); 962cc2df49SGarrett Wollman static void vlan_ifinit(void *foo); 97cfe8b629SGarrett Wollman static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr); 98f731f104SBill Paul static int vlan_setmulti(struct ifnet *ifp); 99f731f104SBill Paul static int vlan_unconfig(struct ifnet *ifp); 100f731f104SBill Paul static int vlan_config(struct ifvlan *ifv, struct ifnet *p); 101f731f104SBill Paul 102f731f104SBill Paul /* 103f731f104SBill Paul * Program our multicast filter. What we're actually doing is 104f731f104SBill Paul * programming the multicast filter of the parent. This has the 105f731f104SBill Paul * side effect of causing the parent interface to receive multicast 106f731f104SBill Paul * traffic that it doesn't really want, which ends up being discarded 107f731f104SBill Paul * later by the upper protocol layers. Unfortunately, there's no way 108f731f104SBill Paul * to avoid this: there really is only one physical interface. 109f731f104SBill Paul */ 110f731f104SBill Paul static int 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 123f731f104SBill Paul sdl.sdl_len = ETHER_ADDR_LEN; 124f731f104SBill Paul sdl.sdl_family = AF_LINK; 125f731f104SBill Paul 126f731f104SBill Paul /* First, remove any existing filter entries. */ 127f731f104SBill Paul while(sc->vlan_mc_listhead.slh_first != NULL) { 128f731f104SBill Paul mc = sc->vlan_mc_listhead.slh_first; 129f731f104SBill Paul bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); 130f731f104SBill Paul error = if_delmulti(ifp_p, (struct sockaddr *)&sdl); 131f731f104SBill Paul if (error) 132f731f104SBill Paul return(error); 133f731f104SBill Paul SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); 134f731f104SBill Paul free(mc, M_DEVBUF); 135f731f104SBill Paul } 136f731f104SBill Paul 137f731f104SBill Paul /* Now program new ones. */ 138f731f104SBill Paul for (ifma = ifp->if_multiaddrs.lh_first; 139f731f104SBill Paul ifma != NULL;ifma = ifma->ifma_link.le_next) { 140f731f104SBill Paul if (ifma->ifma_addr->sa_family != AF_LINK) 141f731f104SBill Paul continue; 142f731f104SBill Paul mc = malloc(sizeof(struct vlan_mc_entry), M_DEVBUF, M_NOWAIT); 143f731f104SBill Paul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 144f731f104SBill Paul (char *)&mc->mc_addr, ETHER_ADDR_LEN); 145f731f104SBill Paul SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); 146f731f104SBill Paul error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma); 147f731f104SBill Paul if (error) 148f731f104SBill Paul return(error); 149f731f104SBill Paul } 150f731f104SBill Paul 151f731f104SBill Paul return(0); 152f731f104SBill Paul } 1532cc2df49SGarrett Wollman 1542cc2df49SGarrett Wollman static void 1552cc2df49SGarrett Wollman vlaninit(void *dummy) 1562cc2df49SGarrett Wollman { 1572cc2df49SGarrett Wollman int i; 1582cc2df49SGarrett Wollman 1592cc2df49SGarrett Wollman for (i = 0; i < NVLAN; i++) { 1602cc2df49SGarrett Wollman struct ifnet *ifp = &ifv_softc[i].ifv_if; 1612cc2df49SGarrett Wollman 1622cc2df49SGarrett Wollman ifp->if_softc = &ifv_softc[i]; 1632cc2df49SGarrett Wollman ifp->if_name = "vlan"; 1642cc2df49SGarrett Wollman ifp->if_unit = i; 1652cc2df49SGarrett Wollman /* NB: flags are not set here */ 1662cc2df49SGarrett Wollman ifp->if_linkmib = &ifv_softc[i].ifv_mib; 1672cc2df49SGarrett Wollman ifp->if_linkmiblen = sizeof ifv_softc[i].ifv_mib; 1682cc2df49SGarrett Wollman /* NB: mtu is not set here */ 1692cc2df49SGarrett Wollman 1702cc2df49SGarrett Wollman ifp->if_init = vlan_ifinit; 1712cc2df49SGarrett Wollman ifp->if_start = vlan_start; 1722cc2df49SGarrett Wollman ifp->if_ioctl = vlan_ioctl; 1732cc2df49SGarrett Wollman ifp->if_output = ether_output; 174f731f104SBill Paul ifp->if_snd.ifq_maxlen = ifqmaxlen; 1752cc2df49SGarrett Wollman if_attach(ifp); 1762cc2df49SGarrett Wollman ether_ifattach(ifp); 1772cc2df49SGarrett Wollman #if NBPFILTER > 0 1782cc2df49SGarrett Wollman bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 1792cc2df49SGarrett Wollman #endif 1802cc2df49SGarrett Wollman /* Now undo some of the damage... */ 1812cc2df49SGarrett Wollman ifp->if_data.ifi_type = IFT_8021_VLAN; 1822cc2df49SGarrett Wollman ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN; 1832cc2df49SGarrett Wollman ifp->if_resolvemulti = 0; 1842cc2df49SGarrett Wollman } 1852cc2df49SGarrett Wollman } 1862cc2df49SGarrett Wollman PSEUDO_SET(vlaninit, if_vlan); 1872cc2df49SGarrett Wollman 1882cc2df49SGarrett Wollman static void 1892cc2df49SGarrett Wollman vlan_ifinit(void *foo) 1902cc2df49SGarrett Wollman { 191f731f104SBill Paul return; 1922cc2df49SGarrett Wollman } 1932cc2df49SGarrett Wollman 1942cc2df49SGarrett Wollman static void 1952cc2df49SGarrett Wollman vlan_start(struct ifnet *ifp) 1962cc2df49SGarrett Wollman { 1972cc2df49SGarrett Wollman struct ifvlan *ifv; 1982cc2df49SGarrett Wollman struct ifnet *p; 1992cc2df49SGarrett Wollman struct ether_vlan_header *evl; 2002cc2df49SGarrett Wollman struct mbuf *m; 2012cc2df49SGarrett Wollman 2022cc2df49SGarrett Wollman ifv = ifp->if_softc; 2032cc2df49SGarrett Wollman p = ifv->ifv_p; 2042cc2df49SGarrett Wollman 2052cc2df49SGarrett Wollman ifp->if_flags |= IFF_OACTIVE; 2062cc2df49SGarrett Wollman for (;;) { 2072cc2df49SGarrett Wollman IF_DEQUEUE(&ifp->if_snd, m); 2082cc2df49SGarrett Wollman if (m == 0) 2092cc2df49SGarrett Wollman break; 2102cc2df49SGarrett Wollman #if NBPFILTER > 0 2112cc2df49SGarrett Wollman if (ifp->if_bpf) 212eb92a347SGarrett Wollman bpf_mtap(ifp, m); 2132cc2df49SGarrett Wollman #endif /* NBPFILTER > 0 */ 2142cc2df49SGarrett Wollman 215f731f104SBill Paul /* 216f731f104SBill Paul * If the LINK0 flag is set, it means the underlying interface 217f731f104SBill Paul * can do VLAN tag insertion itself and doesn't require us to 218f731f104SBill Paul * create a special header for it. In this case, we just pass 219f731f104SBill Paul * the packet along. However, we need some way to tell the 220f731f104SBill Paul * interface where the packet came from so that it knows how 221f731f104SBill Paul * to find the VLAN tag to use, so we set the rcvif in the 222f731f104SBill Paul * mbuf header to our ifnet. 223f731f104SBill Paul * 224f731f104SBill Paul * Note: we also set the M_PROTO1 flag in the mbuf to let 225f731f104SBill Paul * the parent driver know that the rcvif pointer is really 226f731f104SBill Paul * valid. We need to do this because sometimes mbufs will 227f731f104SBill Paul * be allocated by other parts of the system that contain 228f731f104SBill Paul * garbage in the rcvif pointer. Using the M_PROTO1 flag 229f731f104SBill Paul * lets the driver perform a proper sanity check and avoid 230f731f104SBill Paul * following potentially bogus rcvif pointers off into 231f731f104SBill Paul * never-never land. 232f731f104SBill Paul */ 233f731f104SBill Paul if (ifp->if_flags & IFF_LINK0) { 234f731f104SBill Paul m->m_pkthdr.rcvif = ifp; 235f731f104SBill Paul m->m_flags |= M_PROTO1; 236f731f104SBill Paul } else { 2372cc2df49SGarrett Wollman M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT); 2382cc2df49SGarrett Wollman if (m == 0) 2392cc2df49SGarrett Wollman continue; 2402cc2df49SGarrett Wollman /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ 2412cc2df49SGarrett Wollman 2422cc2df49SGarrett Wollman /* 2432cc2df49SGarrett Wollman * Transform the Ethernet header into an Ethernet header 2442cc2df49SGarrett Wollman * with 802.1Q encapsulation. 2452cc2df49SGarrett Wollman */ 2462cc2df49SGarrett Wollman bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *), 2472cc2df49SGarrett Wollman sizeof(struct ether_header)); 2482cc2df49SGarrett Wollman evl = mtod(m, struct ether_vlan_header *); 2492cc2df49SGarrett Wollman evl->evl_proto = evl->evl_encap_proto; 2502cc2df49SGarrett Wollman evl->evl_encap_proto = htons(vlan_proto); 2512cc2df49SGarrett Wollman evl->evl_tag = htons(ifv->ifv_tag); 252f731f104SBill Paul #ifdef DEBUG 253f731f104SBill Paul printf("vlan_start: %*D\n", sizeof *evl, 254f731f104SBill Paul (char *)evl, ":"); 255f731f104SBill Paul #endif 256f731f104SBill Paul } 2572cc2df49SGarrett Wollman 2582cc2df49SGarrett Wollman /* 2592cc2df49SGarrett Wollman * Send it, precisely as ether_output() would have. 2602cc2df49SGarrett Wollman * We are already running at splimp. 2612cc2df49SGarrett Wollman */ 2622cc2df49SGarrett Wollman if (IF_QFULL(&p->if_snd)) { 2632cc2df49SGarrett Wollman IF_DROP(&p->if_snd); 2642cc2df49SGarrett Wollman /* XXX stats */ 265f731f104SBill Paul ifp->if_oerrors++; 266f731f104SBill Paul m_freem(m); 267f731f104SBill Paul continue; 2682cc2df49SGarrett Wollman } 2692cc2df49SGarrett Wollman IF_ENQUEUE(&p->if_snd, m); 270f731f104SBill Paul if ((p->if_flags & IFF_OACTIVE) == 0) { 2712cc2df49SGarrett Wollman p->if_start(p); 272f731f104SBill Paul ifp->if_opackets++; 273f731f104SBill Paul } 2742cc2df49SGarrett Wollman } 2752cc2df49SGarrett Wollman ifp->if_flags &= ~IFF_OACTIVE; 276f731f104SBill Paul 277f731f104SBill Paul return; 278f731f104SBill Paul } 279f731f104SBill Paul 280f731f104SBill Paul void 281f731f104SBill Paul vlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t) 282f731f104SBill Paul { 283f731f104SBill Paul int i; 284f731f104SBill Paul struct ifvlan *ifv; 285f731f104SBill Paul 286f731f104SBill Paul for (i = 0; i < NVLAN; i++) { 287f731f104SBill Paul ifv = &ifv_softc[i]; 288f731f104SBill Paul if (ifv->ifv_tag == t) 289f731f104SBill Paul break; 290f731f104SBill Paul } 291f731f104SBill Paul 292f731f104SBill Paul if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 293f731f104SBill Paul m_freem(m); 294f731f104SBill Paul ifv->ifv_p->if_data.ifi_noproto++; 295f731f104SBill Paul return; 296f731f104SBill Paul } 297f731f104SBill Paul 298f731f104SBill Paul /* 299f731f104SBill Paul * Having found a valid vlan interface corresponding to 300f731f104SBill Paul * the given source interface and vlan tag, run the 301f731f104SBill Paul * the real packet through ethert_input(). 302f731f104SBill Paul */ 303f731f104SBill Paul m->m_pkthdr.rcvif = &ifv->ifv_if; 304f731f104SBill Paul 305f731f104SBill Paul #if NBPFILTER > 0 306f731f104SBill Paul if (ifv->ifv_if.if_bpf) { 307f731f104SBill Paul /* 308f731f104SBill Paul * Do the usual BPF fakery. Note that we don't support 309f731f104SBill Paul * promiscuous mode here, since it would require the 310f731f104SBill Paul * drivers to know about VLANs and we're not ready for 311f731f104SBill Paul * that yet. 312f731f104SBill Paul */ 313f731f104SBill Paul struct mbuf m0; 314f731f104SBill Paul m0.m_next = m; 315f731f104SBill Paul m0.m_len = sizeof(struct ether_header); 316f731f104SBill Paul m0.m_data = (char *)eh; 317f731f104SBill Paul bpf_mtap(&ifv->ifv_if, &m0); 318f731f104SBill Paul } 319f731f104SBill Paul #endif 320f731f104SBill Paul ifv->ifv_if.if_ipackets++; 321f731f104SBill Paul ether_input(&ifv->ifv_if, eh, m); 322f731f104SBill Paul return; 3232cc2df49SGarrett Wollman } 3242cc2df49SGarrett Wollman 3252cc2df49SGarrett Wollman int 3262cc2df49SGarrett Wollman vlan_input(struct ether_header *eh, struct mbuf *m) 3272cc2df49SGarrett Wollman { 3282cc2df49SGarrett Wollman int i; 3292cc2df49SGarrett Wollman struct ifvlan *ifv; 3302cc2df49SGarrett Wollman 3312cc2df49SGarrett Wollman for (i = 0; i < NVLAN; i++) { 3322cc2df49SGarrett Wollman ifv = &ifv_softc[i]; 3332cc2df49SGarrett Wollman if (m->m_pkthdr.rcvif == ifv->ifv_p 3342cc2df49SGarrett Wollman && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *))) 3352cc2df49SGarrett Wollman == ifv->ifv_tag)) 3362cc2df49SGarrett Wollman break; 3372cc2df49SGarrett Wollman } 3382cc2df49SGarrett Wollman 3392cc2df49SGarrett Wollman if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) { 3402cc2df49SGarrett Wollman m_freem(m); 3412cc2df49SGarrett Wollman return -1; /* so ether_input can take note */ 3422cc2df49SGarrett Wollman } 3432cc2df49SGarrett Wollman 3442cc2df49SGarrett Wollman /* 3452cc2df49SGarrett Wollman * Having found a valid vlan interface corresponding to 3462cc2df49SGarrett Wollman * the given source interface and vlan tag, remove the 3472cc2df49SGarrett Wollman * encapsulation, and run the real packet through 3482cc2df49SGarrett Wollman * ether_input() a second time (it had better be 3492cc2df49SGarrett Wollman * reentrant!). 3502cc2df49SGarrett Wollman */ 3512cc2df49SGarrett Wollman m->m_pkthdr.rcvif = &ifv->ifv_if; 3522cc2df49SGarrett Wollman eh->ether_type = mtod(m, u_int16_t *)[1]; 3532cc2df49SGarrett Wollman m->m_data += EVL_ENCAPLEN; 3542cc2df49SGarrett Wollman m->m_len -= EVL_ENCAPLEN; 3552cc2df49SGarrett Wollman m->m_pkthdr.len -= EVL_ENCAPLEN; 3562cc2df49SGarrett Wollman 3572cc2df49SGarrett Wollman #if NBPFILTER > 0 3582cc2df49SGarrett Wollman if (ifv->ifv_if.if_bpf) { 3592cc2df49SGarrett Wollman /* 3602cc2df49SGarrett Wollman * Do the usual BPF fakery. Note that we don't support 3612cc2df49SGarrett Wollman * promiscuous mode here, since it would require the 3622cc2df49SGarrett Wollman * drivers to know about VLANs and we're not ready for 3632cc2df49SGarrett Wollman * that yet. 3642cc2df49SGarrett Wollman */ 3652cc2df49SGarrett Wollman struct mbuf m0; 3662cc2df49SGarrett Wollman m0.m_next = m; 3672cc2df49SGarrett Wollman m0.m_len = sizeof(struct ether_header); 3682cc2df49SGarrett Wollman m0.m_data = (char *)eh; 3692cc2df49SGarrett Wollman bpf_mtap(&ifv->ifv_if, &m0); 3702cc2df49SGarrett Wollman } 3712cc2df49SGarrett Wollman #endif 372f731f104SBill Paul ifv->ifv_if.if_ipackets++; 3732cc2df49SGarrett Wollman ether_input(&ifv->ifv_if, eh, m); 3742cc2df49SGarrett Wollman return 0; 3752cc2df49SGarrett Wollman } 3762cc2df49SGarrett Wollman 3772cc2df49SGarrett Wollman static int 3782cc2df49SGarrett Wollman vlan_config(struct ifvlan *ifv, struct ifnet *p) 3792cc2df49SGarrett Wollman { 3802cc2df49SGarrett Wollman struct ifaddr *ifa1, *ifa2; 3812cc2df49SGarrett Wollman struct sockaddr_dl *sdl1, *sdl2; 3822cc2df49SGarrett Wollman 3832cc2df49SGarrett Wollman if (p->if_data.ifi_type != IFT_ETHER) 3842cc2df49SGarrett Wollman return EPROTONOSUPPORT; 3852cc2df49SGarrett Wollman if (ifv->ifv_p) 3862cc2df49SGarrett Wollman return EBUSY; 3872cc2df49SGarrett Wollman ifv->ifv_p = p; 3882cc2df49SGarrett Wollman if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header)) 3892cc2df49SGarrett Wollman ifv->ifv_if.if_mtu = p->if_mtu; 3902cc2df49SGarrett Wollman else 3912cc2df49SGarrett Wollman ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN; 3922cc2df49SGarrett Wollman 3932cc2df49SGarrett Wollman /* 394f731f104SBill Paul * Preserve the state of the LINK0 flag for ourselves. 3952cc2df49SGarrett Wollman */ 396f731f104SBill Paul ifv->ifv_if.if_flags = (p->if_flags & ~(IFF_LINK0)); 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 */ 432f731f104SBill Paul while(ifv->vlan_mc_listhead.slh_first != NULL) { 433f731f104SBill Paul struct sockaddr_dl sdl; 434f731f104SBill Paul 435f731f104SBill Paul sdl.sdl_len = ETHER_ADDR_LEN; 436f731f104SBill Paul sdl.sdl_family = AF_LINK; 437f731f104SBill Paul mc = ifv->vlan_mc_listhead.slh_first; 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); 5202cc2df49SGarrett Wollman if_down(ifp); 521f731f104SBill Paul ifp->if_flags = 0; 5222cc2df49SGarrett Wollman break; 5232cc2df49SGarrett Wollman } 5242cc2df49SGarrett Wollman p = ifunit(vlr.vlr_parent); 5252cc2df49SGarrett Wollman if (p == 0) { 5262cc2df49SGarrett Wollman error = ENOENT; 5272cc2df49SGarrett Wollman break; 5282cc2df49SGarrett Wollman } 5292cc2df49SGarrett Wollman error = vlan_config(ifv, p); 5302cc2df49SGarrett Wollman if (error) 5312cc2df49SGarrett Wollman break; 5322cc2df49SGarrett Wollman ifv->ifv_tag = vlr.vlr_tag; 5332cc2df49SGarrett Wollman break; 5342cc2df49SGarrett Wollman 5352cc2df49SGarrett Wollman case SIOCGETVLAN: 5362cc2df49SGarrett Wollman bzero(&vlr, sizeof vlr); 5372cc2df49SGarrett Wollman if (ifv->ifv_p) { 5382127f260SArchie Cobbs snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), 5392127f260SArchie Cobbs "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit); 5402cc2df49SGarrett Wollman vlr.vlr_tag = ifv->ifv_tag; 5412cc2df49SGarrett Wollman } 5422cc2df49SGarrett Wollman error = copyout(&vlr, ifr->ifr_data, sizeof vlr); 5432cc2df49SGarrett Wollman break; 5442cc2df49SGarrett Wollman 5452cc2df49SGarrett Wollman case SIOCSIFFLAGS: 5462cc2df49SGarrett Wollman /* 547f731f104SBill Paul * We don't support promiscuous mode 5482cc2df49SGarrett Wollman * right now because it would require help from the 5492cc2df49SGarrett Wollman * underlying drivers, which hasn't been implemented. 5502cc2df49SGarrett Wollman */ 551f731f104SBill Paul if (ifr->ifr_flags & (IFF_PROMISC)) { 552f731f104SBill Paul ifp->if_flags &= ~(IFF_PROMISC); 5532cc2df49SGarrett Wollman error = EINVAL; 5542cc2df49SGarrett Wollman } 5552cc2df49SGarrett Wollman break; 556f731f104SBill Paul case SIOCADDMULTI: 557f731f104SBill Paul case SIOCDELMULTI: 558f731f104SBill Paul error = vlan_setmulti(ifp); 559f731f104SBill Paul break; 5602cc2df49SGarrett Wollman default: 5612cc2df49SGarrett Wollman error = EINVAL; 5622cc2df49SGarrett Wollman } 5632cc2df49SGarrett Wollman return error; 5642cc2df49SGarrett Wollman } 5652cc2df49SGarrett Wollman 5662cc2df49SGarrett Wollman #endif /* NVLAN > 0 */ 567