if_vlan.c revision 72084
1208538Sraj/*
2208538Sraj * Copyright 1998 Massachusetts Institute of Technology
3208538Sraj *
4208538Sraj * Permission to use, copy, modify, and distribute this software and
5208538Sraj * its documentation for any purpose and without fee is hereby
6208538Sraj * granted, provided that both the above copyright notice and this
7208538Sraj * permission notice appear in all copies, that both the above
8208538Sraj * copyright notice and this permission notice appear in all
9208538Sraj * supporting documentation, and that the name of M.I.T. not be used
10208538Sraj * in advertising or publicity pertaining to distribution of the
11208538Sraj * software without specific, written prior permission.  M.I.T. makes
12208538Sraj * no representations about the suitability of this software for any
13208538Sraj * purpose.  It is provided "as is" without express or implied
14208538Sraj * warranty.
15208538Sraj *
16208538Sraj * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17208538Sraj * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18208538Sraj * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19208538Sraj * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20208538Sraj * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21208538Sraj * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22208538Sraj * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23208538Sraj * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24208538Sraj * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25208538Sraj * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26208538Sraj * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27208538Sraj * SUCH DAMAGE.
28208538Sraj *
29208538Sraj * $FreeBSD: head/sys/net/if_vlan.c 72084 2001-02-06 10:12:15Z phk $
30208538Sraj */
31208538Sraj
32208538Sraj/*
33208538Sraj * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
34208538Sraj * Might be extended some day to also handle IEEE 802.1p priority
35208538Sraj * tagging.  This is sort of sneaky in the implementation, since
36233230Sraj * we need to pretend to be enough of an Ethernet implementation
37233230Sraj * to make arp work.  The way we do this is by telling everyone
38233230Sraj * that we are an Ethernet, and then catch the packets that
39208538Sraj * ether_output() left on our output queue when it calls
40208538Sraj * if_start(), rewrite them for use by the real outgoing interface,
41208538Sraj * and ask it to send them.
42208538Sraj *
43208538Sraj *
44208538Sraj * XXX It's incorrect to assume that we must always kludge up
45208538Sraj * headers on the physical device's behalf: some devices support
46208538Sraj * VLAN tag insersion and extraction in firmware. For these cases,
47208538Sraj * one can change the behavior of the vlan interface by setting
48208538Sraj * the LINK0 flag on it (that is setting the vlan interface's LINK0
49208538Sraj * flag, _not_ the parent's LINK0 flag; we try to leave the parent
50208538Sraj * alone). If the interface as the LINK0 flag set, then it will
51208538Sraj * not modify the ethernet header on output because the parent
52208538Sraj * can do that for itself. On input, the parent can call vlan_input_tag()
53208538Sraj * directly in order to supply us with an incoming mbuf and the vlan
54208538Sraj * tag value that goes with it.
55208538Sraj */
56208538Sraj
57208538Sraj#include "vlan.h"
58235529Skientzle#include "opt_inet.h"
59235529Skientzle
60233230Sraj#include <sys/param.h>
61233230Sraj#include <sys/kernel.h>
62233230Sraj#include <sys/malloc.h>
63243693Sgonzo#include <sys/mbuf.h>
64243693Sgonzo#include <sys/module.h>
65247201Skientzle#include <sys/queue.h>
66247250Skientzle#include <sys/socket.h>
67247201Skientzle#include <sys/sockio.h>
68247250Skientzle#include <sys/sysctl.h>
69247250Skientzle#include <sys/systm.h>
70208538Sraj
71235529Skientzle#include <net/bpf.h>
72235529Skientzle#include <net/ethernet.h>
73247250Skientzle#include <net/if.h>
74247250Skientzle#include <net/if_arp.h>
75247250Skientzle#include <net/if_dl.h>
76235529Skientzle#include <net/if_types.h>
77208538Sraj#include <net/if_vlan_var.h>
78243693Sgonzo
79243693Sgonzo#ifdef INET
80208538Sraj#include <netinet/in.h>
81208538Sraj#include <netinet/if_ether.h>
82243693Sgonzo#endif
83208538Sraj
84208538SrajSYSCTL_DECL(_net_link);
85208538SrajSYSCTL_NODE(_net_link, IFT_8021_VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
86208538SrajSYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency");
87208538Sraj
88208538Sraju_int	vlan_proto = ETHERTYPE_VLAN;
89208538SrajSYSCTL_INT(_net_link_vlan_link, VLANCTL_PROTO, proto, CTLFLAG_RW, &vlan_proto,
90208538Sraj	   0, "Ethernet protocol used for VLAN encapsulation");
91243693Sgonzo
92208538Srajstatic	struct ifvlan ifv_softc[NVLAN];
93208538Sraj
94208538Srajstatic	void vlan_start(struct ifnet *ifp);
95208538Srajstatic	void vlan_ifinit(void *foo);
96208538Srajstatic	int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
97208538Srajstatic	int vlan_setmulti(struct ifnet *ifp);
98243693Sgonzostatic	int vlan_unconfig(struct ifnet *ifp);
99208538Srajstatic	int vlan_config(struct ifvlan *ifv, struct ifnet *p);
100208538Sraj
101208538Sraj/*
102243693Sgonzo * Program our multicast filter. What we're actually doing is
103243693Sgonzo * programming the multicast filter of the parent. This has the
104243693Sgonzo * side effect of causing the parent interface to receive multicast
105243693Sgonzo * traffic that it doesn't really want, which ends up being discarded
106243693Sgonzo * later by the upper protocol layers. Unfortunately, there's no way
107243693Sgonzo * to avoid this: there really is only one physical interface.
108243693Sgonzo */
109243693Sgonzostatic int
110243693Sgonzovlan_setmulti(struct ifnet *ifp)
111243693Sgonzo{
112243693Sgonzo	struct ifnet		*ifp_p;
113208538Sraj	struct ifmultiaddr	*ifma, *rifma = NULL;
114208538Sraj	struct ifvlan		*sc;
115208538Sraj	struct vlan_mc_entry	*mc = NULL;
116208538Sraj	struct sockaddr_dl	sdl;
117208538Sraj	int			error;
118233230Sraj
119235529Skientzle	/* Find the parent. */
120233230Sraj	sc = ifp->if_softc;
121248121Sian	ifp_p = sc->ifv_p;
122248121Sian
123233230Sraj	sdl.sdl_len = ETHER_ADDR_LEN;
124248121Sian	sdl.sdl_family = AF_LINK;
125233230Sraj
126233230Sraj	/* First, remove any existing filter entries. */
127233230Sraj	while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) {
128235529Skientzle		mc = SLIST_FIRST(&sc->vlan_mc_listhead);
129248121Sian		bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
130233230Sraj		error = if_delmulti(ifp_p, (struct sockaddr *)&sdl);
131248934Skientzle		if (error)
132235529Skientzle			return(error);
133233230Sraj		SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
134233230Sraj		free(mc, M_DEVBUF);
135233230Sraj	}
136233230Sraj
137233230Sraj	/* Now program new ones. */
138233230Sraj	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
139233230Sraj		if (ifma->ifma_addr->sa_family != AF_LINK)
140248121Sian			continue;
141248121Sian		mc = malloc(sizeof(struct vlan_mc_entry), M_DEVBUF, M_NOWAIT);
142233230Sraj		bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
143233230Sraj		    (char *)&mc->mc_addr, ETHER_ADDR_LEN);
144248121Sian		SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
145233230Sraj		error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma);
146248121Sian		if (error)
147233230Sraj			return(error);
148233230Sraj	}
149248121Sian
150233230Sraj	return(0);
151248121Sian}
152248121Sian
153248121Sianstatic void
154248121Sianvlaninit(void)
155248121Sian{
156248121Sian	int i;
157233230Sraj
158233230Sraj	for (i = 0; i < NVLAN; i++) {
159233230Sraj		struct ifnet *ifp = &ifv_softc[i].ifv_if;
160233230Sraj
161233230Sraj		ifp->if_softc = &ifv_softc[i];
162233230Sraj		ifp->if_name = "vlan";
163233230Sraj		ifp->if_unit = i;
164233230Sraj		/* NB: flags are not set here */
165233230Sraj		ifp->if_linkmib = &ifv_softc[i].ifv_mib;
166233230Sraj		ifp->if_linkmiblen = sizeof ifv_softc[i].ifv_mib;
167233230Sraj		/* NB: mtu is not set here */
168233230Sraj
169233230Sraj		ifp->if_init = vlan_ifinit;
170235529Skientzle		ifp->if_start = vlan_start;
171235529Skientzle		ifp->if_ioctl = vlan_ioctl;
172235529Skientzle		ifp->if_output = ether_output;
173235529Skientzle		ifp->if_snd.ifq_maxlen = ifqmaxlen;
174235529Skientzle		ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
175233230Sraj		/* Now undo some of the damage... */
176233230Sraj		ifp->if_data.ifi_type = IFT_8021_VLAN;
177233230Sraj		ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN;
178235529Skientzle		ifp->if_resolvemulti = 0;
179235529Skientzle	}
180235529Skientzle}
181233230Sraj
182233230Srajstatic int
183235529Skientzlevlan_modevent(module_t mod, int type, void *data)
184233230Sraj{
185233230Sraj	switch (type) {
186208538Sraj	case MOD_LOAD:
187243693Sgonzo		vlaninit();
188208538Sraj		break;
189235529Skientzle	case MOD_UNLOAD:
190208538Sraj		printf("if_vlan module unload - not possible for this module type\n");
191208538Sraj		return EINVAL;
192243693Sgonzo	}
193243693Sgonzo	return 0;
194243693Sgonzo}
195243693Sgonzo
196243693Sgonzostatic moduledata_t vlan_mod = {
197243693Sgonzo	"if_vlan",
198243693Sgonzo	vlan_modevent,
199243693Sgonzo	0
200243693Sgonzo};
201243693Sgonzo
202243693SgonzoDECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
203243693Sgonzo
204243693Sgonzostatic void
205243693Sgonzovlan_ifinit(void *foo)
206208538Sraj{
207243693Sgonzo	return;
208208538Sraj}
209243693Sgonzo
210243693Sgonzostatic void
211208538Srajvlan_start(struct ifnet *ifp)
212235529Skientzle{
213235529Skientzle	struct ifvlan *ifv;
214243693Sgonzo	struct ifnet *p;
215235529Skientzle	struct ether_vlan_header *evl;
216235529Skientzle	struct mbuf *m;
217243693Sgonzo
218235529Skientzle	ifv = ifp->if_softc;
219235529Skientzle	p = ifv->ifv_p;
220243693Sgonzo
221243693Sgonzo	ifp->if_flags |= IFF_OACTIVE;
222243693Sgonzo	for (;;) {
223208538Sraj		IF_DEQUEUE(&ifp->if_snd, m);
224243693Sgonzo		if (m == 0)
225243693Sgonzo			break;
226243693Sgonzo		if (ifp->if_bpf)
227243693Sgonzo			bpf_mtap(ifp, m);
228247045Skientzle
229243693Sgonzo		/*
230265065Sian		 * If the LINK0 flag is set, it means the underlying interface
231243693Sgonzo		 * can do VLAN tag insertion itself and doesn't require us to
232247250Skientzle	 	 * create a special header for it. In this case, we just pass
233265065Sian		 * the packet along. However, we need some way to tell the
234265065Sian		 * interface where the packet came from so that it knows how
235265065Sian		 * to find the VLAN tag to use, so we set the rcvif in the
236265065Sian		 * mbuf header to our ifnet.
237265065Sian		 *
238265065Sian		 * Note: we also set the M_PROTO1 flag in the mbuf to let
239247250Skientzle		 * the parent driver know that the rcvif pointer is really
240247250Skientzle		 * valid. We need to do this because sometimes mbufs will
241247250Skientzle		 * be allocated by other parts of the system that contain
242247045Skientzle		 * garbage in the rcvif pointer. Using the M_PROTO1 flag
243208538Sraj		 * lets the driver perform a proper sanity check and avoid
244247250Skientzle		 * following potentially bogus rcvif pointers off into
245247250Skientzle		 * never-never land.
246247250Skientzle		 */
247247250Skientzle		if (ifp->if_flags & IFF_LINK0) {
248247045Skientzle			m->m_pkthdr.rcvif = ifp;
249243693Sgonzo			m->m_flags |= M_PROTO1;
250247045Skientzle		} else {
251247045Skientzle			M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
252247045Skientzle			if (m == NULL) {
253247045Skientzle				printf("vlan%d: M_PREPEND failed", ifp->if_unit);
254247045Skientzle				ifp->if_ierrors++;
255247201Skientzle				continue;
256247201Skientzle			}
257247045Skientzle			/* M_PREPEND takes care of m_len, m_pkthdr.len for us */
258247045Skientzle
259247045Skientzle			m = m_pullup(m, ETHER_HDR_LEN + EVL_ENCAPLEN);
260247045Skientzle			if (m == NULL) {
261247045Skientzle				printf("vlan%d: m_pullup failed", ifp->if_unit);
262247201Skientzle				ifp->if_ierrors++;
263247045Skientzle				continue;
264247201Skientzle			}
265247201Skientzle
266247201Skientzle			/*
267247201Skientzle			 * Transform the Ethernet header into an Ethernet header
268247201Skientzle			 * with 802.1Q encapsulation.
269247201Skientzle			 */
270247045Skientzle			bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *),
271247045Skientzle			      sizeof(struct ether_header));
272247045Skientzle			evl = mtod(m, struct ether_vlan_header *);
273247045Skientzle			evl->evl_proto = evl->evl_encap_proto;
274247045Skientzle			evl->evl_encap_proto = htons(vlan_proto);
275247045Skientzle			evl->evl_tag = htons(ifv->ifv_tag);
276247045Skientzle#ifdef DEBUG
277247045Skientzle			printf("vlan_start: %*D\n", sizeof *evl,
278247045Skientzle			    (char *)evl, ":");
279247045Skientzle#endif
280247045Skientzle		}
281247045Skientzle
282247045Skientzle		/*
283247045Skientzle		 * Send it, precisely as ether_output() would have.
284247045Skientzle		 * We are already running at splimp.
285247045Skientzle		 */
286208538Sraj		if (IF_HANDOFF(&p->if_snd, m, p))
287208538Sraj			ifp->if_opackets++;
288208538Sraj		else
289208538Sraj			ifp->if_oerrors++;
290208538Sraj	}
291208538Sraj	ifp->if_flags &= ~IFF_OACTIVE;
292208538Sraj
293208538Sraj	return;
294208538Sraj}
295208538Sraj
296208538Srajint
297208538Srajvlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t)
298208538Sraj{
299208538Sraj	int i;
300208538Sraj	struct ifvlan *ifv;
301208538Sraj
302208538Sraj	for (i = 0; i < NVLAN; i++) {
303208538Sraj		ifv = &ifv_softc[i];
304208538Sraj		if (ifv->ifv_tag == t)
305208538Sraj			break;
306208538Sraj	}
307208538Sraj
308208538Sraj	if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
309208538Sraj		m_free(m);
310208538Sraj		return -1;	/* So the parent can take note */
311208538Sraj	}
312208538Sraj
313208538Sraj	/*
314208538Sraj	 * Having found a valid vlan interface corresponding to
315208538Sraj	 * the given source interface and vlan tag, run the
316208538Sraj	 * the real packet through ethert_input().
317208538Sraj	 */
318208538Sraj	m->m_pkthdr.rcvif = &ifv->ifv_if;
319208538Sraj
320208538Sraj	ifv->ifv_if.if_ipackets++;
321208538Sraj	ether_input(&ifv->ifv_if, eh, m);
322208538Sraj	return 0;
323208538Sraj}
324208538Sraj
325208538Srajint
326208538Srajvlan_input(struct ether_header *eh, struct mbuf *m)
327208538Sraj{
328208538Sraj	int i;
329208538Sraj	struct ifvlan *ifv;
330208538Sraj
331208538Sraj	for (i = 0; i < NVLAN; i++) {
332208538Sraj		ifv = &ifv_softc[i];
333208538Sraj		if (m->m_pkthdr.rcvif == ifv->ifv_p
334208538Sraj		    && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *)))
335208538Sraj			== ifv->ifv_tag))
336208538Sraj			break;
337208538Sraj	}
338208538Sraj
339208538Sraj	if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
340247250Skientzle		m_freem(m);
341208538Sraj		return -1;	/* so ether_input can take note */
342208538Sraj	}
343208538Sraj
344208538Sraj	/*
345208538Sraj	 * Having found a valid vlan interface corresponding to
346208538Sraj	 * the given source interface and vlan tag, remove the
347208538Sraj	 * encapsulation, and run the real packet through
348208538Sraj	 * ether_input() a second time (it had better be
349208538Sraj	 * reentrant!).
350208538Sraj	 */
351208538Sraj	m->m_pkthdr.rcvif = &ifv->ifv_if;
352208538Sraj	eh->ether_type = mtod(m, u_int16_t *)[1];
353208538Sraj	m->m_data += EVL_ENCAPLEN;
354208538Sraj	m->m_len -= EVL_ENCAPLEN;
355208538Sraj	m->m_pkthdr.len -= EVL_ENCAPLEN;
356208538Sraj
357208538Sraj	ifv->ifv_if.if_ipackets++;
358208538Sraj	ether_input(&ifv->ifv_if, eh, m);
359208538Sraj	return 0;
360208538Sraj}
361208538Sraj
362208538Srajstatic int
363208538Srajvlan_config(struct ifvlan *ifv, struct ifnet *p)
364208538Sraj{
365208538Sraj	struct ifaddr *ifa1, *ifa2;
366208538Sraj	struct sockaddr_dl *sdl1, *sdl2;
367208538Sraj
368208538Sraj	if (p->if_data.ifi_type != IFT_ETHER)
369208538Sraj		return EPROTONOSUPPORT;
370208538Sraj	if (ifv->ifv_p)
371208538Sraj		return EBUSY;
372208538Sraj	ifv->ifv_p = p;
373208538Sraj	if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header))
374208538Sraj		ifv->ifv_if.if_mtu = p->if_mtu;
375208538Sraj	else
376208538Sraj		ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN;
377247250Skientzle
378208538Sraj	/*
379208538Sraj	 * Preserve the state of the LINK0 flag for ourselves.
380208538Sraj	 */
381208538Sraj	ifv->ifv_if.if_flags = (p->if_flags & ~(IFF_LINK0));
382208538Sraj
383208538Sraj	/*
384208538Sraj	 * Set up our ``Ethernet address'' to reflect the underlying
385235261Skientzle	 * physical interface's.
386235261Skientzle	 */
387208538Sraj	ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1];
388208538Sraj	ifa2 = ifnet_addrs[p->if_index - 1];
389208538Sraj	sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
390208538Sraj	sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
391208538Sraj	sdl1->sdl_type = IFT_ETHER;
392208538Sraj	sdl1->sdl_alen = ETHER_ADDR_LEN;
393208538Sraj	bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
394208538Sraj	bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
395208538Sraj	return 0;
396208538Sraj}
397208538Sraj
398208538Srajstatic int
399208538Srajvlan_unconfig(struct ifnet *ifp)
400208538Sraj{
401208538Sraj	struct ifaddr *ifa;
402208538Sraj	struct sockaddr_dl *sdl;
403208538Sraj	struct vlan_mc_entry *mc;
404208538Sraj	struct ifvlan *ifv;
405208538Sraj	struct ifnet *p;
406208538Sraj	int error;
407208538Sraj
408208538Sraj	ifv = ifp->if_softc;
409208538Sraj	p = ifv->ifv_p;
410208538Sraj
411208538Sraj	/*
412208538Sraj 	 * Since the interface is being unconfigured, we need to
413208538Sraj	 * empty the list of multicast groups that we may have joined
414208538Sraj	 * while we were alive and remove them from the parent's list
415208538Sraj	 * as well.
416208538Sraj	 */
417208538Sraj	while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) {
418208538Sraj		struct sockaddr_dl	sdl;
419208538Sraj
420208538Sraj		sdl.sdl_len = ETHER_ADDR_LEN;
421208538Sraj		sdl.sdl_family = AF_LINK;
422208538Sraj		mc = SLIST_FIRST(&ifv->vlan_mc_listhead);
423208538Sraj		bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
424208538Sraj		error = if_delmulti(p, (struct sockaddr *)&sdl);
425247250Skientzle		error = if_delmulti(ifp, (struct sockaddr *)&sdl);
426208538Sraj		if (error)
427208538Sraj			return(error);
428208538Sraj		SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
429208538Sraj		free(mc, M_DEVBUF);
430208538Sraj	}
431208538Sraj
432208538Sraj	/* Disconnect from parent. */
433208538Sraj	ifv->ifv_p = NULL;
434208538Sraj	ifv->ifv_if.if_mtu = ETHERMTU;
435208538Sraj
436208538Sraj	/* Clear our MAC address. */
437208538Sraj	ifa = ifnet_addrs[ifv->ifv_if.if_index - 1];
438208538Sraj	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
439208538Sraj	sdl->sdl_type = IFT_ETHER;
440208538Sraj	sdl->sdl_alen = ETHER_ADDR_LEN;
441208538Sraj	bzero(LLADDR(sdl), ETHER_ADDR_LEN);
442208538Sraj	bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
443208538Sraj
444208538Sraj	return 0;
445208538Sraj}
446208538Sraj
447208538Srajstatic int
448208538Srajvlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
449208538Sraj{
450208538Sraj	struct ifaddr *ifa;
451208538Sraj	struct ifnet *p;
452208538Sraj	struct ifreq *ifr;
453208538Sraj	struct ifvlan *ifv;
454208538Sraj	struct vlanreq vlr;
455208538Sraj	int error = 0;
456208538Sraj
457247250Skientzle	ifr = (struct ifreq *)data;
458208538Sraj	ifa = (struct ifaddr *)data;
459208538Sraj	ifv = ifp->if_softc;
460208538Sraj
461208538Sraj	switch (cmd) {
462208538Sraj	case SIOCSIFADDR:
463208538Sraj		ifp->if_flags |= IFF_UP;
464208538Sraj
465243693Sgonzo		switch (ifa->ifa_addr->sa_family) {
466243693Sgonzo#ifdef INET
467208538Sraj		case AF_INET:
468208538Sraj			arp_ifinit(&ifv->ifv_ac, ifa);
469208538Sraj			break;
470208538Sraj#endif
471208538Sraj		default:
472208538Sraj			break;
473208538Sraj		}
474208538Sraj		break;
475208538Sraj
476208538Sraj	case SIOCGIFADDR:
477208538Sraj		{
478208538Sraj			struct sockaddr *sa;
479208538Sraj
480208538Sraj			sa = (struct sockaddr *) &ifr->ifr_data;
481208538Sraj			bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
482208538Sraj			      (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
483208538Sraj		}
484208538Sraj		break;
485208538Sraj
486208538Sraj	case SIOCSIFMTU:
487208538Sraj		/*
488208538Sraj		 * Set the interface MTU.
489208538Sraj		 * This is bogus. The underlying interface might support
490208538Sraj	 	 * jumbo frames.
491208538Sraj		 */
492208538Sraj		if (ifr->ifr_mtu > ETHERMTU) {
493208538Sraj			error = EINVAL;
494208538Sraj		} else {
495208538Sraj			ifp->if_mtu = ifr->ifr_mtu;
496208538Sraj		}
497208538Sraj		break;
498208538Sraj
499208538Sraj	case SIOCSETVLAN:
500208538Sraj		error = copyin(ifr->ifr_data, &vlr, sizeof vlr);
501208538Sraj		if (error)
502208538Sraj			break;
503208538Sraj		if (vlr.vlr_parent[0] == '\0') {
504208538Sraj			vlan_unconfig(ifp);
505208538Sraj			if_down(ifp);
506243693Sgonzo			ifp->if_flags &= ~(IFF_UP|IFF_RUNNING);
507243693Sgonzo			break;
508243693Sgonzo		}
509243693Sgonzo		p = ifunit(vlr.vlr_parent);
510243693Sgonzo		if (p == 0) {
511243693Sgonzo			error = ENOENT;
512243693Sgonzo			break;
513243693Sgonzo		}
514243693Sgonzo		error = vlan_config(ifv, p);
515243693Sgonzo		if (error)
516243693Sgonzo			break;
517243693Sgonzo		ifv->ifv_tag = vlr.vlr_tag;
518243693Sgonzo		ifp->if_flags |= IFF_RUNNING;
519243693Sgonzo		break;
520243693Sgonzo
521243693Sgonzo	case SIOCGETVLAN:
522243693Sgonzo		bzero(&vlr, sizeof vlr);
523243693Sgonzo		if (ifv->ifv_p) {
524243693Sgonzo			snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
525243693Sgonzo			    "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit);
526243693Sgonzo			vlr.vlr_tag = ifv->ifv_tag;
527243693Sgonzo		}
528243693Sgonzo		error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
529243693Sgonzo		break;
530243693Sgonzo
531243693Sgonzo	case SIOCSIFFLAGS:
532243693Sgonzo		/*
533243693Sgonzo		 * We don't support promiscuous mode
534243693Sgonzo		 * right now because it would require help from the
535243693Sgonzo		 * underlying drivers, which hasn't been implemented.
536243693Sgonzo		 */
537243693Sgonzo		if (ifr->ifr_flags & (IFF_PROMISC)) {
538243693Sgonzo			ifp->if_flags &= ~(IFF_PROMISC);
539243693Sgonzo			error = EINVAL;
540243693Sgonzo		}
541243693Sgonzo		break;
542243693Sgonzo	case SIOCADDMULTI:
543243693Sgonzo	case SIOCDELMULTI:
544243693Sgonzo		error = vlan_setmulti(ifp);
545243693Sgonzo		break;
546243693Sgonzo	default:
547243693Sgonzo		error = EINVAL;
548243693Sgonzo	}
549243693Sgonzo	return error;
550243693Sgonzo}
551243693Sgonzo