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