Home | History | Annotate | Line # | Download | only in net
if_vlan.c revision 1.7
      1  1.7    enami /*	$NetBSD: if_vlan.c,v 1.7 2000/09/28 08:28:56 enami Exp $	*/
      2  1.1  thorpej 
      3  1.1  thorpej /*-
      4  1.1  thorpej  * Copyright (c) 2000 The NetBSD Foundation, Inc.
      5  1.1  thorpej  * All rights reserved.
      6  1.1  thorpej  *
      7  1.1  thorpej  * This code is derived from software contributed to The NetBSD Foundation
      8  1.1  thorpej  * by Andrew Doran and Jason R. Thorpe.
      9  1.1  thorpej  *
     10  1.1  thorpej  * Redistribution and use in source and binary forms, with or without
     11  1.1  thorpej  * modification, are permitted provided that the following conditions
     12  1.1  thorpej  * are met:
     13  1.1  thorpej  * 1. Redistributions of source code must retain the above copyright
     14  1.1  thorpej  *    notice, this list of conditions and the following disclaimer.
     15  1.1  thorpej  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.1  thorpej  *    notice, this list of conditions and the following disclaimer in the
     17  1.1  thorpej  *    documentation and/or other materials provided with the distribution.
     18  1.1  thorpej  * 3. All advertising materials mentioning features or use of this software
     19  1.1  thorpej  *    must display the following acknowledgement:
     20  1.1  thorpej  *	This product includes software developed by the NetBSD
     21  1.1  thorpej  *	Foundation, Inc. and its contributors.
     22  1.1  thorpej  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  1.1  thorpej  *    contributors may be used to endorse or promote products derived
     24  1.1  thorpej  *    from this software without specific prior written permission.
     25  1.1  thorpej  *
     26  1.1  thorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  1.1  thorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  1.1  thorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  1.1  thorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  1.1  thorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  1.1  thorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  1.1  thorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  1.1  thorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  1.1  thorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  1.1  thorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  1.1  thorpej  * POSSIBILITY OF SUCH DAMAGE.
     37  1.1  thorpej  */
     38  1.1  thorpej 
     39  1.1  thorpej /*
     40  1.1  thorpej  * Copyright 1998 Massachusetts Institute of Technology
     41  1.1  thorpej  *
     42  1.1  thorpej  * Permission to use, copy, modify, and distribute this software and
     43  1.1  thorpej  * its documentation for any purpose and without fee is hereby
     44  1.1  thorpej  * granted, provided that both the above copyright notice and this
     45  1.1  thorpej  * permission notice appear in all copies, that both the above
     46  1.1  thorpej  * copyright notice and this permission notice appear in all
     47  1.1  thorpej  * supporting documentation, and that the name of M.I.T. not be used
     48  1.1  thorpej  * in advertising or publicity pertaining to distribution of the
     49  1.1  thorpej  * software without specific, written prior permission.  M.I.T. makes
     50  1.1  thorpej  * no representations about the suitability of this software for any
     51  1.1  thorpej  * purpose.  It is provided "as is" without express or implied
     52  1.1  thorpej  * warranty.
     53  1.1  thorpej  *
     54  1.1  thorpej  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
     55  1.1  thorpej  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
     56  1.1  thorpej  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     57  1.1  thorpej  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
     58  1.1  thorpej  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     59  1.1  thorpej  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     60  1.1  thorpej  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
     61  1.1  thorpej  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     62  1.1  thorpej  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     63  1.1  thorpej  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     64  1.1  thorpej  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     65  1.1  thorpej  * SUCH DAMAGE.
     66  1.1  thorpej  *
     67  1.1  thorpej  * from FreeBSD: if_vlan.c,v 1.16 2000/03/26 15:21:40 charnier Exp
     68  1.1  thorpej  * via OpenBSD: if_vlan.c,v 1.4 2000/05/15 19:15:00 chris Exp
     69  1.1  thorpej  */
     70  1.1  thorpej 
     71  1.1  thorpej /*
     72  1.1  thorpej  * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.  Might be
     73  1.1  thorpej  * extended some day to also handle IEEE 802.1P priority tagging.  This is
     74  1.1  thorpej  * sort of sneaky in the implementation, since we need to pretend to be
     75  1.1  thorpej  * enough of an Ethernet implementation to make ARP work.  The way we do
     76  1.1  thorpej  * this is by telling everyone that we are an Ethernet interface, and then
     77  1.1  thorpej  * catch the packets that ether_output() left on our output queue when it
     78  1.1  thorpej  * calls if_start(), rewrite them for use by the real outgoing interface,
     79  1.1  thorpej  * and ask it to send them.
     80  1.1  thorpej  *
     81  1.1  thorpej  * TODO:
     82  1.1  thorpej  *
     83  1.1  thorpej  *	- Need some way to notify vlan interfaces when the parent
     84  1.1  thorpej  *	  interface changes MTU.
     85  1.1  thorpej  *
     86  1.1  thorpej  *	- Need to make promiscuous mode work.
     87  1.1  thorpej  */
     88  1.1  thorpej 
     89  1.1  thorpej #include "opt_inet.h"
     90  1.1  thorpej #include "bpfilter.h"
     91  1.1  thorpej 
     92  1.1  thorpej #include <sys/param.h>
     93  1.1  thorpej #include <sys/kernel.h>
     94  1.1  thorpej #include <sys/mbuf.h>
     95  1.1  thorpej #include <sys/queue.h>
     96  1.1  thorpej #include <sys/socket.h>
     97  1.1  thorpej #include <sys/sockio.h>
     98  1.1  thorpej #include <sys/systm.h>
     99  1.1  thorpej #include <sys/proc.h>
    100  1.1  thorpej 
    101  1.1  thorpej #if NBPFILTER > 0
    102  1.1  thorpej #include <net/bpf.h>
    103  1.1  thorpej #endif
    104  1.1  thorpej #include <net/if.h>
    105  1.1  thorpej #include <net/if_dl.h>
    106  1.1  thorpej #include <net/if_types.h>
    107  1.1  thorpej #include <net/if_ether.h>
    108  1.1  thorpej #include <net/if_vlanvar.h>
    109  1.1  thorpej 
    110  1.1  thorpej #ifdef INET
    111  1.1  thorpej #include <netinet/in.h>
    112  1.1  thorpej #include <netinet/if_inarp.h>
    113  1.1  thorpej #endif
    114  1.1  thorpej 
    115  1.1  thorpej extern struct	ifaddr **ifnet_addrs;	/* XXX if.c */
    116  1.1  thorpej 
    117  1.1  thorpej static int	vlan_clone_create(struct if_clone *, int);
    118  1.1  thorpej static void	vlan_clone_destroy(struct ifnet *);
    119  1.1  thorpej static int	vlan_config(struct ifvlan *, struct ifnet *);
    120  1.1  thorpej static int	vlan_ioctl(struct ifnet *, u_long, caddr_t);
    121  1.5    enami static int	vlan_addmulti(struct ifvlan *, struct ifreq *);
    122  1.5    enami static int	vlan_delmulti(struct ifvlan *, struct ifreq *);
    123  1.5    enami static void	vlan_purgemulti(struct ifvlan *);
    124  1.1  thorpej static void	vlan_start(struct ifnet *);
    125  1.1  thorpej static int	vlan_unconfig(struct ifnet *);
    126  1.1  thorpej void	vlanattach(int);
    127  1.1  thorpej 
    128  1.1  thorpej /* XXX This should be a hash table with the tag as the basis of the key. */
    129  1.1  thorpej static LIST_HEAD(, ifvlan) ifv_list;
    130  1.1  thorpej 
    131  1.1  thorpej struct if_clone vlan_cloner =
    132  1.1  thorpej     IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
    133  1.1  thorpej 
    134  1.1  thorpej void
    135  1.1  thorpej vlanattach(int n)
    136  1.1  thorpej {
    137  1.1  thorpej 
    138  1.1  thorpej 	LIST_INIT(&ifv_list);
    139  1.1  thorpej 	if_clone_attach(&vlan_cloner);
    140  1.1  thorpej }
    141  1.1  thorpej 
    142  1.1  thorpej static int
    143  1.1  thorpej vlan_clone_create(struct if_clone *ifc, int unit)
    144  1.1  thorpej {
    145  1.1  thorpej 	struct ifvlan *ifv;
    146  1.1  thorpej 	struct ifnet *ifp;
    147  1.4    enami 	u_int8_t eaddr[ETHER_ADDR_LEN];
    148  1.1  thorpej 
    149  1.1  thorpej 	ifv = malloc(sizeof(struct ifvlan), M_DEVBUF, M_WAIT);
    150  1.1  thorpej 	memset(ifv, 0, sizeof(struct ifvlan));
    151  1.1  thorpej 	ifp = &ifv->ifv_ec.ec_if;
    152  1.5    enami 	LIST_INIT(&ifv->ifv_mc_listhead);
    153  1.1  thorpej 	LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
    154  1.1  thorpej 
    155  1.1  thorpej 	sprintf(ifp->if_xname, "%s%d", ifc->ifc_name, unit);
    156  1.1  thorpej 	ifp->if_softc = ifv;
    157  1.1  thorpej 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
    158  1.1  thorpej 	ifp->if_start = vlan_start;
    159  1.1  thorpej 	ifp->if_ioctl = vlan_ioctl;
    160  1.1  thorpej 
    161  1.1  thorpej 	if_attach(ifp);
    162  1.1  thorpej 	memset(eaddr, 0, sizeof(eaddr));
    163  1.1  thorpej 	ether_ifattach(ifp, eaddr);
    164  1.1  thorpej 
    165  1.1  thorpej 	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
    166  1.1  thorpej 	ifp->if_mtu = ETHERMTU - EVL_ENCAPLEN;
    167  1.1  thorpej 
    168  1.1  thorpej #if NBPFILTER > 0
    169  1.1  thorpej 	bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB,
    170  1.1  thorpej 	    sizeof(struct ether_header));
    171  1.1  thorpej #endif
    172  1.1  thorpej 
    173  1.1  thorpej 	return (0);
    174  1.1  thorpej }
    175  1.1  thorpej 
    176  1.1  thorpej static void
    177  1.1  thorpej vlan_clone_destroy(struct ifnet *ifp)
    178  1.1  thorpej {
    179  1.1  thorpej 	struct ifvlan *ifv;
    180  1.1  thorpej 	int s;
    181  1.1  thorpej 
    182  1.1  thorpej 	ifv = (struct ifvlan *)ifp->if_softc;
    183  1.1  thorpej 	s = splsoftnet();
    184  1.1  thorpej 
    185  1.1  thorpej 	LIST_REMOVE(ifv, ifv_list);
    186  1.1  thorpej 	vlan_unconfig(ifp);
    187  1.1  thorpej 
    188  1.1  thorpej #if NBPFILTER > 0
    189  1.1  thorpej 	bpfdetach(ifp);
    190  1.1  thorpej #endif
    191  1.1  thorpej 	ether_ifdetach(ifp);
    192  1.1  thorpej 	if_detach(ifp);
    193  1.1  thorpej 	free(ifv, M_DEVBUF);
    194  1.1  thorpej 
    195  1.1  thorpej 	splx(s);
    196  1.1  thorpej }
    197  1.1  thorpej 
    198  1.1  thorpej static int
    199  1.1  thorpej vlan_config(struct ifvlan *ifv, struct ifnet *p)
    200  1.1  thorpej {
    201  1.1  thorpej 	struct ifaddr *ifa1, *ifa2;
    202  1.1  thorpej 	struct sockaddr_dl *sdl1, *sdl2;
    203  1.1  thorpej 
    204  1.1  thorpej 	if (p->if_data.ifi_type != IFT_ETHER)
    205  1.1  thorpej 		return (EPROTONOSUPPORT);
    206  1.1  thorpej 	if (ifv->ifv_p != NULL)
    207  1.1  thorpej 		return (EBUSY);
    208  1.1  thorpej 	ifv->ifv_p = p;
    209  1.1  thorpej 	ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN;
    210  1.1  thorpej 	ifv->ifv_if.if_flags = p->if_flags;
    211  1.1  thorpej 
    212  1.1  thorpej 	/*
    213  1.1  thorpej 	 * Set up our ``Ethernet address'' to match the underlying
    214  1.1  thorpej 	 * physical interface's.
    215  1.1  thorpej 	 */
    216  1.1  thorpej 	ifa1 = ifnet_addrs[ifv->ifv_if.if_index];
    217  1.1  thorpej 	ifa2 = ifnet_addrs[p->if_index];
    218  1.1  thorpej 	sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
    219  1.1  thorpej 	sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
    220  1.1  thorpej 	sdl1->sdl_type = IFT_ETHER;
    221  1.1  thorpej 	sdl1->sdl_alen = ETHER_ADDR_LEN;
    222  1.1  thorpej 	memcpy(LLADDR(sdl1), LLADDR(sdl2), ETHER_ADDR_LEN);
    223  1.1  thorpej 	memcpy(LLADDR(ifv->ifv_ec.ec_if.if_sadl), LLADDR(sdl2), ETHER_ADDR_LEN);
    224  1.1  thorpej 	return (0);
    225  1.1  thorpej }
    226  1.1  thorpej 
    227  1.1  thorpej static int
    228  1.1  thorpej vlan_unconfig(struct ifnet *ifp)
    229  1.1  thorpej {
    230  1.1  thorpej 	struct ifaddr *ifa;
    231  1.1  thorpej 	struct sockaddr_dl *sdl;
    232  1.1  thorpej 	struct ifvlan *ifv;
    233  1.5    enami 	int s;
    234  1.1  thorpej 
    235  1.7    enami 	ifv = ifp->if_softc;
    236  1.7    enami 	if (ifv->ifv_p == NULL)
    237  1.7    enami 		return (0);
    238  1.7    enami 
    239  1.1  thorpej 	s = splsoftnet();
    240  1.1  thorpej 
    241  1.1  thorpej 	/*
    242  1.1  thorpej  	 * Since the interface is being unconfigured, we need to empty the
    243  1.1  thorpej 	 * list of multicast groups that we may have joined while we were
    244  1.1  thorpej 	 * alive and remove them from the parent's list also.
    245  1.1  thorpej 	 */
    246  1.5    enami 	vlan_purgemulti(ifv);
    247  1.1  thorpej 
    248  1.1  thorpej 	/* Disconnect from parent. */
    249  1.1  thorpej 	ifv->ifv_p = NULL;
    250  1.1  thorpej 	ifv->ifv_if.if_mtu = ETHERMTU - EVL_ENCAPLEN;
    251  1.1  thorpej 
    252  1.1  thorpej 	/* Clear our MAC address. */
    253  1.1  thorpej 	ifa = ifnet_addrs[ifv->ifv_if.if_index];
    254  1.1  thorpej 	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
    255  1.1  thorpej 	sdl->sdl_type = IFT_ETHER;
    256  1.1  thorpej 	sdl->sdl_alen = ETHER_ADDR_LEN;
    257  1.1  thorpej 	memset(LLADDR(sdl), 0, ETHER_ADDR_LEN);
    258  1.1  thorpej 	memset(LLADDR(ifv->ifv_ec.ec_if.if_sadl), 0, ETHER_ADDR_LEN);
    259  1.1  thorpej 
    260  1.1  thorpej 	splx(s);
    261  1.1  thorpej 	return (0);
    262  1.1  thorpej }
    263  1.1  thorpej 
    264  1.1  thorpej static int
    265  1.1  thorpej vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
    266  1.1  thorpej {
    267  1.1  thorpej 	struct proc *p = curproc;	/* XXX */
    268  1.1  thorpej 	struct ifaddr *ifa;
    269  1.1  thorpej 	struct ifnet *pr;
    270  1.1  thorpej 	struct ifreq *ifr;
    271  1.1  thorpej 	struct ifvlan *ifv;
    272  1.1  thorpej 	struct vlanreq vlr;
    273  1.1  thorpej 	struct sockaddr *sa;
    274  1.1  thorpej 	int error;
    275  1.1  thorpej 
    276  1.1  thorpej 	error = 0;
    277  1.1  thorpej 	ifr = (struct ifreq *)data;
    278  1.1  thorpej 	ifa = (struct ifaddr *)data;
    279  1.1  thorpej 	ifv = ifp->if_softc;
    280  1.1  thorpej 
    281  1.1  thorpej 	switch (cmd) {
    282  1.1  thorpej 	case SIOCSIFADDR:
    283  1.1  thorpej 		ifp->if_flags |= IFF_UP;
    284  1.1  thorpej 
    285  1.1  thorpej 		switch (ifa->ifa_addr->sa_family) {
    286  1.1  thorpej #ifdef INET
    287  1.1  thorpej 		case AF_INET:
    288  1.1  thorpej 			arp_ifinit(ifp, ifa);
    289  1.1  thorpej 			break;
    290  1.1  thorpej #endif
    291  1.1  thorpej 		default:
    292  1.1  thorpej 			break;
    293  1.1  thorpej 		}
    294  1.1  thorpej 		break;
    295  1.1  thorpej 
    296  1.1  thorpej 	case SIOCGIFADDR:
    297  1.1  thorpej 		sa = (struct sockaddr *)&ifr->ifr_data;
    298  1.1  thorpej 		memcpy(sa->sa_data, LLADDR(ifp->if_sadl), ETHER_ADDR_LEN);
    299  1.1  thorpej 		break;
    300  1.1  thorpej 
    301  1.1  thorpej 	case SIOCSIFMTU:
    302  1.1  thorpej 		if (ifv->ifv_p != NULL) {
    303  1.1  thorpej 			if (ifr->ifr_mtu > ifv->ifv_p->if_mtu - EVL_ENCAPLEN ||
    304  1.1  thorpej 			    ifr->ifr_mtu < ETHERMIN + EVL_ENCAPLEN)
    305  1.1  thorpej 				error = EINVAL;
    306  1.1  thorpej 			else
    307  1.1  thorpej 				ifp->if_mtu = ifr->ifr_mtu;
    308  1.1  thorpej 		} else
    309  1.1  thorpej 			error = EINVAL;
    310  1.1  thorpej 		break;
    311  1.1  thorpej 
    312  1.1  thorpej 	case SIOCSETVLAN:
    313  1.1  thorpej 		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
    314  1.1  thorpej 			break;
    315  1.1  thorpej 		if ((error = copyin(ifr->ifr_data, &vlr, sizeof(vlr))) != 0)
    316  1.1  thorpej 			break;
    317  1.1  thorpej 		if (vlr.vlr_parent[0] == '\0') {
    318  1.1  thorpej 			vlan_unconfig(ifp);
    319  1.1  thorpej 			if_down(ifp);
    320  1.1  thorpej 			ifp->if_flags &= ~(IFF_UP|IFF_RUNNING);
    321  1.1  thorpej 			break;
    322  1.1  thorpej 		}
    323  1.1  thorpej 		if (vlr.vlr_tag != EVL_VLANOFTAG(vlr.vlr_tag)) {
    324  1.1  thorpej 			error = EINVAL;		 /* check for valid tag */
    325  1.1  thorpej 			break;
    326  1.1  thorpej 		}
    327  1.1  thorpej 		if ((pr = ifunit(vlr.vlr_parent)) == 0) {
    328  1.1  thorpej 			error = ENOENT;
    329  1.1  thorpej 			break;
    330  1.1  thorpej 		}
    331  1.1  thorpej 		if ((error = vlan_config(ifv, pr)) != 0)
    332  1.1  thorpej 			break;
    333  1.1  thorpej 		ifv->ifv_tag = vlr.vlr_tag;
    334  1.1  thorpej 		ifp->if_flags |= IFF_RUNNING;
    335  1.1  thorpej 		break;
    336  1.1  thorpej 
    337  1.1  thorpej 	case SIOCGETVLAN:
    338  1.1  thorpej 		memset(&vlr, 0, sizeof(vlr));
    339  1.1  thorpej 		if (ifv->ifv_p != NULL) {
    340  1.1  thorpej 			snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), "%s",
    341  1.1  thorpej 			    ifv->ifv_p->if_xname);
    342  1.1  thorpej 			vlr.vlr_tag = ifv->ifv_tag;
    343  1.1  thorpej 		}
    344  1.1  thorpej 		error = copyout(&vlr, ifr->ifr_data, sizeof(vlr));
    345  1.1  thorpej 		break;
    346  1.1  thorpej 
    347  1.1  thorpej 	case SIOCSIFFLAGS:
    348  1.1  thorpej 		/*
    349  1.1  thorpej 		 * XXX We don't support promiscuous mode right now because
    350  1.1  thorpej 		 * it would require help from the underlying drivers, which
    351  1.1  thorpej 		 * hasn't been implemented.
    352  1.1  thorpej 		 */
    353  1.1  thorpej 		if ((ifr->ifr_flags & IFF_PROMISC) != 0) {
    354  1.1  thorpej 			ifp->if_flags &= ~(IFF_PROMISC);
    355  1.1  thorpej 			error = EINVAL;
    356  1.1  thorpej 		}
    357  1.1  thorpej 		break;
    358  1.1  thorpej 
    359  1.1  thorpej 	case SIOCADDMULTI:
    360  1.6    enami 		error = vlan_addmulti(ifv, ifr);
    361  1.6    enami 		break;
    362  1.6    enami 
    363  1.1  thorpej 	case SIOCDELMULTI:
    364  1.6    enami 		error = vlan_delmulti(ifv, ifr);
    365  1.1  thorpej 		break;
    366  1.1  thorpej 
    367  1.1  thorpej 	default:
    368  1.1  thorpej 		error = EINVAL;
    369  1.1  thorpej 	}
    370  1.1  thorpej 
    371  1.1  thorpej 	return (error);
    372  1.1  thorpej }
    373  1.1  thorpej 
    374  1.1  thorpej static int
    375  1.5    enami vlan_addmulti(struct ifvlan *ifv, struct ifreq *ifr)
    376  1.1  thorpej {
    377  1.1  thorpej 	struct vlan_mc_entry *mc;
    378  1.5    enami 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
    379  1.1  thorpej 	int error;
    380  1.1  thorpej 
    381  1.5    enami 	if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr_storage))
    382  1.5    enami 		return (EINVAL);
    383  1.5    enami 
    384  1.5    enami 	error = ether_addmulti(ifr, &ifv->ifv_ec);
    385  1.5    enami 	if (error != ENETRESET)
    386  1.5    enami 		return (error);
    387  1.1  thorpej 
    388  1.5    enami 	/*
    389  1.5    enami 	 * This is new multicast address.  We have to tell parent
    390  1.5    enami 	 * about it.  Also, remember this multicast address so that
    391  1.5    enami 	 * we can delete them on unconfigure.
    392  1.5    enami 	 */
    393  1.5    enami 	MALLOC(mc, struct vlan_mc_entry *, sizeof(struct vlan_mc_entry),
    394  1.5    enami 	    M_DEVBUF, M_NOWAIT);
    395  1.5    enami 	if (mc == NULL) {
    396  1.5    enami 		error = ENOMEM;
    397  1.5    enami 		goto alloc_failed;
    398  1.1  thorpej 	}
    399  1.1  thorpej 
    400  1.5    enami 	/*
    401  1.5    enami 	 * As ether_addmulti() returns ENETRESET, following two
    402  1.5    enami 	 * statement shouldn't fail.
    403  1.5    enami 	 */
    404  1.5    enami 	(void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
    405  1.5    enami 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, mc->mc_enm);
    406  1.5    enami 	memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
    407  1.5    enami 	LIST_INSERT_HEAD(&ifv->ifv_mc_listhead, mc, mc_entries);
    408  1.5    enami 
    409  1.5    enami 	error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCADDMULTI,
    410  1.5    enami 	    (caddr_t)ifr);
    411  1.5    enami 	if (error != 0)
    412  1.5    enami 		goto ioctl_failed;
    413  1.5    enami 	return (error);
    414  1.5    enami 
    415  1.5    enami  ioctl_failed:
    416  1.5    enami 	LIST_REMOVE(mc, mc_entries);
    417  1.5    enami 	FREE(mc, M_DEVBUF);
    418  1.5    enami  alloc_failed:
    419  1.5    enami 	(void)ether_delmulti(ifr, &ifv->ifv_ec);
    420  1.5    enami 	return (error);
    421  1.5    enami }
    422  1.5    enami 
    423  1.5    enami static int
    424  1.5    enami vlan_delmulti(struct ifvlan *ifv, struct ifreq *ifr)
    425  1.5    enami {
    426  1.5    enami 	struct ether_multi *enm;
    427  1.5    enami 	struct vlan_mc_entry *mc;
    428  1.5    enami 	u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
    429  1.5    enami 	int error;
    430  1.5    enami 
    431  1.5    enami 	/*
    432  1.5    enami 	 * Find a key to lookup vlan_mc_entry.  We have to do this
    433  1.5    enami 	 * before calling ether_delmulti for obvious reason.
    434  1.5    enami 	 */
    435  1.5    enami 	if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
    436  1.5    enami 		return (error);
    437  1.5    enami 	ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, enm);
    438  1.5    enami 
    439  1.5    enami 	error = ether_delmulti(ifr, &ifv->ifv_ec);
    440  1.5    enami 	if (error != ENETRESET)
    441  1.5    enami 		return (error);
    442  1.5    enami 
    443  1.5    enami 	/* We no longer use this multicast address.  Tell parent so. */
    444  1.5    enami 	error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCDELMULTI,
    445  1.5    enami 	    (caddr_t)ifr);
    446  1.5    enami 	if (error == 0) {
    447  1.5    enami 		/* And forget about this address. */
    448  1.5    enami 		for (mc = LIST_FIRST(&ifv->ifv_mc_listhead); mc != NULL;
    449  1.5    enami 		    mc = LIST_NEXT(mc, mc_entries)) {
    450  1.5    enami 			if (mc->mc_enm == enm) {
    451  1.5    enami 				LIST_REMOVE(mc, mc_entries);
    452  1.5    enami 				FREE(mc, M_DEVBUF);
    453  1.5    enami 				break;
    454  1.5    enami 			}
    455  1.5    enami 		}
    456  1.5    enami 		KASSERT(mc != NULL);
    457  1.5    enami 	} else
    458  1.5    enami 		(void)ether_addmulti(ifr, &ifv->ifv_ec);
    459  1.5    enami 	return (error);
    460  1.5    enami }
    461  1.5    enami 
    462  1.5    enami /*
    463  1.5    enami  * Delete any multicast address we have asked to add form parent
    464  1.5    enami  * interface.  Called when the vlan is being unconfigured.
    465  1.5    enami  */
    466  1.5    enami static void
    467  1.5    enami vlan_purgemulti(struct ifvlan *ifv)
    468  1.5    enami {
    469  1.5    enami 	struct ifnet *ifp = ifv->ifv_p;		/* Parent. */
    470  1.5    enami 	struct vlan_mc_entry *mc;
    471  1.5    enami 	union {
    472  1.5    enami 		struct ifreq ifreq;
    473  1.5    enami 		struct {
    474  1.5    enami 			char ifr_name[IFNAMSIZ];
    475  1.5    enami 			struct sockaddr_storage;
    476  1.5    enami 		} ifreq_storage;
    477  1.5    enami 	} ifreq;
    478  1.5    enami 	struct ifreq *ifr = &ifreq.ifreq;
    479  1.5    enami 
    480  1.5    enami 	memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
    481  1.5    enami 	while ((mc = LIST_FIRST(&ifv->ifv_mc_listhead)) != NULL) {
    482  1.5    enami 		memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
    483  1.5    enami 		(void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
    484  1.5    enami 		LIST_REMOVE(mc->mc_enm, enm_list);
    485  1.5    enami 		free(mc->mc_enm, M_IFMADDR);
    486  1.5    enami 		LIST_REMOVE(mc, mc_entries);
    487  1.5    enami 		FREE(mc, M_DEVBUF);
    488  1.1  thorpej 	}
    489  1.1  thorpej 
    490  1.5    enami 	KASSERT(LIST_FIRST(&ifv->ifv_ec.ec_multiaddrs) == NULL);
    491  1.1  thorpej }
    492  1.1  thorpej 
    493  1.1  thorpej static void
    494  1.1  thorpej vlan_start(struct ifnet *ifp)
    495  1.1  thorpej {
    496  1.1  thorpej 	struct ifvlan *ifv;
    497  1.1  thorpej 	struct ifnet *p;
    498  1.1  thorpej 	struct ether_vlan_header *evl;
    499  1.1  thorpej 	struct mbuf *m;
    500  1.1  thorpej 
    501  1.1  thorpej 	ifv = ifp->if_softc;
    502  1.1  thorpej 	p = ifv->ifv_p;
    503  1.1  thorpej 	ifp->if_flags |= IFF_OACTIVE;
    504  1.1  thorpej 
    505  1.1  thorpej 	for (;;) {
    506  1.1  thorpej 		IF_DEQUEUE(&ifp->if_snd, m);
    507  1.1  thorpej 		if (m == NULL)
    508  1.1  thorpej 			break;
    509  1.1  thorpej 
    510  1.1  thorpej #if NBPFILTER > 0
    511  1.1  thorpej 		if (ifp->if_bpf)
    512  1.1  thorpej 			bpf_mtap(ifp->if_bpf, m);
    513  1.1  thorpej #endif
    514  1.1  thorpej 
    515  1.1  thorpej 		/*
    516  1.1  thorpej 		 * XXX Should handle the case where the underlying hardware
    517  1.1  thorpej 		 * interface can do VLAN tag insertion itself.
    518  1.1  thorpej 		 */
    519  1.1  thorpej 		M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
    520  1.1  thorpej 		if (m == NULL) {
    521  1.1  thorpej 			printf("%s: M_PREPEND failed", ifv->ifv_p->if_xname);
    522  1.1  thorpej 			ifp->if_ierrors++;
    523  1.1  thorpej 			continue;
    524  1.1  thorpej 		}
    525  1.1  thorpej 
    526  1.1  thorpej 		if (m->m_len < sizeof(struct ether_vlan_header) &&
    527  1.1  thorpej 		    (m = m_pullup(m,
    528  1.1  thorpej 		     sizeof(struct ether_vlan_header))) == NULL) {
    529  1.1  thorpej 			printf("%s: m_pullup failed", ifv->ifv_p->if_xname);
    530  1.1  thorpej 			ifp->if_ierrors++;
    531  1.1  thorpej 			continue;
    532  1.1  thorpej 		}
    533  1.1  thorpej 
    534  1.1  thorpej 		/*
    535  1.1  thorpej 		 * Transform the Ethernet header into an Ethernet header
    536  1.1  thorpej 		 * with 802.1Q encapsulation.
    537  1.1  thorpej 		 */
    538  1.1  thorpej 		memmove(mtod(m, caddr_t), mtod(m, caddr_t) + EVL_ENCAPLEN,
    539  1.1  thorpej 		    sizeof(struct ether_header));
    540  1.1  thorpej 		evl = mtod(m, struct ether_vlan_header *);
    541  1.1  thorpej 		evl->evl_proto = evl->evl_encap_proto;
    542  1.1  thorpej 		evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
    543  1.1  thorpej 		evl->evl_tag = htons(ifv->ifv_tag);
    544  1.1  thorpej 
    545  1.1  thorpej 		/*
    546  1.1  thorpej 		 * Send it, precisely as ether_output() would have.  We are
    547  1.1  thorpej 		 * already running at splimp.
    548  1.1  thorpej 		 */
    549  1.1  thorpej 		if (IF_QFULL(&p->if_snd)) {
    550  1.1  thorpej 			IF_DROP(&p->if_snd);
    551  1.1  thorpej 			/* XXX stats */
    552  1.1  thorpej 			ifp->if_oerrors++;
    553  1.1  thorpej 			m_freem(m);
    554  1.1  thorpej 			continue;
    555  1.1  thorpej 		}
    556  1.1  thorpej 
    557  1.1  thorpej 		IF_ENQUEUE(&p->if_snd, m);
    558  1.1  thorpej 		if ((p->if_flags & IFF_OACTIVE) == 0) {
    559  1.1  thorpej 			p->if_start(p);
    560  1.1  thorpej 			ifp->if_opackets++;
    561  1.1  thorpej 		}
    562  1.1  thorpej 	}
    563  1.1  thorpej 
    564  1.1  thorpej 	ifp->if_flags &= ~IFF_OACTIVE;
    565  1.1  thorpej }
    566  1.1  thorpej 
    567  1.1  thorpej /*
    568  1.1  thorpej  * Given an Ethernet frame, find a valid vlan interface corresponding to the
    569  1.1  thorpej  * given source interface and tag, then run the the real packet through
    570  1.1  thorpej  * the parent's input routine.
    571  1.1  thorpej  */
    572  1.1  thorpej void
    573  1.1  thorpej vlan_input(struct ifnet *ifp, struct mbuf *m)
    574  1.1  thorpej {
    575  1.1  thorpej 	struct ether_vlan_header *evl;
    576  1.1  thorpej 	struct ifvlan *ifv;
    577  1.1  thorpej 	u_int tag;
    578  1.1  thorpej 
    579  1.1  thorpej 	if (m->m_len < sizeof(struct ether_vlan_header) &&
    580  1.1  thorpej 	    (m = m_pullup(m, sizeof(struct ether_vlan_header))) == NULL) {
    581  1.1  thorpej 		printf("%s: no memory for VLAN header, dropping packet.\n",
    582  1.1  thorpej 		    ifp->if_xname);
    583  1.1  thorpej 		return;
    584  1.1  thorpej 	}
    585  1.1  thorpej 	evl = mtod(m, struct ether_vlan_header *);
    586  1.1  thorpej 	KASSERT(htons(evl->evl_encap_proto) == ETHERTYPE_VLAN);
    587  1.1  thorpej 
    588  1.1  thorpej 	tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
    589  1.1  thorpej 
    590  1.1  thorpej 	for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
    591  1.1  thorpej 	    ifv = LIST_NEXT(ifv, ifv_list))
    592  1.1  thorpej 		if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
    593  1.1  thorpej 			break;
    594  1.1  thorpej 
    595  1.1  thorpej 	if (ifv == NULL || (ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
    596  1.1  thorpej 	    (IFF_UP|IFF_RUNNING)) {
    597  1.1  thorpej 		m_free(m);
    598  1.1  thorpej 		ifp->if_data.ifi_noproto++;
    599  1.1  thorpej 		return;
    600  1.1  thorpej 	}
    601  1.1  thorpej 
    602  1.1  thorpej 	/*
    603  1.1  thorpej 	 * Having found a valid vlan interface corresponding to the given
    604  1.1  thorpej 	 * source interface and vlan tag, remove the encapsulation.
    605  1.1  thorpej 	 */
    606  1.1  thorpej 	evl->evl_encap_proto = evl->evl_proto;
    607  1.1  thorpej 	memmove(mtod(m, caddr_t) + EVL_ENCAPLEN, mtod(m, caddr_t),
    608  1.1  thorpej 	    EVL_ENCAPLEN);
    609  1.1  thorpej 	m_adj(m, EVL_ENCAPLEN);
    610  1.1  thorpej 
    611  1.1  thorpej 	m->m_pkthdr.rcvif = &ifv->ifv_if;
    612  1.1  thorpej 	ifv->ifv_if.if_ipackets++;
    613  1.1  thorpej 
    614  1.1  thorpej #if NBPFILTER > 0
    615  1.1  thorpej 	if (ifv->ifv_if.if_bpf)
    616  1.1  thorpej 		bpf_mtap(ifv->ifv_if.if_bpf, m);
    617  1.1  thorpej #endif
    618  1.1  thorpej 
    619  1.1  thorpej 	/* Pass it back through the parent's input routine. */
    620  1.1  thorpej 	(*ifp->if_input)(&ifv->ifv_if, m);
    621  1.1  thorpej }
    622