]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/net/if_vlan.c
Fix the bug that prevented DMA from working on old Acer chips.
[FreeBSD/FreeBSD.git] / sys / net / if_vlan.c
1 /*
2  * Copyright 1998 Massachusetts Institute of Technology
3  *
4  * Permission to use, copy, modify, and distribute this software and
5  * its documentation for any purpose and without fee is hereby
6  * granted, provided that both the above copyright notice and this
7  * permission notice appear in all copies, that both the above
8  * copyright notice and this permission notice appear in all
9  * supporting documentation, and that the name of M.I.T. not be used
10  * in advertising or publicity pertaining to distribution of the
11  * software without specific, written prior permission.  M.I.T. makes
12  * no representations about the suitability of this software for any
13  * purpose.  It is provided "as is" without express or implied
14  * warranty.
15  * 
16  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31
32 /*
33  * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
34  * Might be extended some day to also handle IEEE 802.1p priority
35  * tagging.  This is sort of sneaky in the implementation, since
36  * we need to pretend to be enough of an Ethernet implementation
37  * to make arp work.  The way we do this is by telling everyone
38  * that we are an Ethernet, and then catch the packets that
39  * ether_output() left on our output queue when it calls
40  * if_start(), rewrite them for use by the real outgoing interface,
41  * and ask it to send them.
42  */
43
44 #include "opt_inet.h"
45
46 #include <sys/param.h>
47 #include <sys/kernel.h>
48 #include <sys/malloc.h>
49 #include <sys/mbuf.h>
50 #include <sys/module.h>
51 #include <sys/queue.h>
52 #include <sys/socket.h>
53 #include <sys/sockio.h>
54 #include <sys/sysctl.h>
55 #include <sys/systm.h>
56
57 #include <net/bpf.h>
58 #include <net/ethernet.h>
59 #include <net/if.h>
60 #include <net/if_arp.h>
61 #include <net/if_dl.h>
62 #include <net/if_types.h>
63 #include <net/if_vlan_var.h>
64
65 #ifdef INET
66 #include <netinet/in.h>
67 #include <netinet/if_ether.h>
68 #endif
69
70 #define VLANNAME        "vlan"
71
72 struct vlan_mc_entry {
73         struct ether_addr               mc_addr;
74         SLIST_ENTRY(vlan_mc_entry)      mc_entries;
75 };
76
77 struct  ifvlan {
78         struct  arpcom ifv_ac;  /* make this an interface */
79         struct  ifnet *ifv_p;   /* parent inteface of this vlan */
80         struct  ifv_linkmib {
81                 int     ifvm_parent;
82                 int     ifvm_encaplen;  /* encapsulation length */
83                 int     ifvm_mtufudge;  /* MTU fudged by this much */
84                 int     ifvm_mintu;     /* min transmission unit */
85                 u_int16_t ifvm_proto; /* encapsulation ethertype */
86                 u_int16_t ifvm_tag; /* tag to apply on packets leaving if */
87         }       ifv_mib;
88         SLIST_HEAD(__vlan_mchead, vlan_mc_entry)        vlan_mc_listhead;
89         LIST_ENTRY(ifvlan) ifv_list;
90         int     ifv_flags;
91 };
92 #define ifv_if  ifv_ac.ac_if
93 #define ifv_tag ifv_mib.ifvm_tag
94 #define ifv_encaplen    ifv_mib.ifvm_encaplen
95 #define ifv_mtufudge    ifv_mib.ifvm_mtufudge
96 #define ifv_mintu       ifv_mib.ifvm_mintu
97
98 #define IFVF_PROMISC    0x01            /* promiscuous mode enabled */
99
100 SYSCTL_DECL(_net_link);
101 SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
102 SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency");
103
104 static MALLOC_DEFINE(M_VLAN, "vlan", "802.1Q Virtual LAN Interface");
105 static LIST_HEAD(, ifvlan) ifv_list;
106
107 /*
108  * Locking: one lock is used to guard both the ifv_list and modification
109  * to vlan data structures.  We are rather conservative here; probably
110  * more than necessary.
111  */
112 static struct mtx ifv_mtx;
113 #define VLAN_LOCK_INIT()        mtx_init(&ifv_mtx, "vlan", NULL, MTX_DEF)
114 #define VLAN_LOCK_DESTROY()     mtx_destroy(&ifv_mtx)
115 #define VLAN_LOCK_ASSERT()      mtx_assert(&ifv_mtx, MA_OWNED)
116 #define VLAN_LOCK()     mtx_lock(&ifv_mtx)
117 #define VLAN_UNLOCK()   mtx_unlock(&ifv_mtx)
118
119 static  int vlan_clone_create(struct if_clone *, int);
120 static  void vlan_clone_destroy(struct ifnet *);
121 static  void vlan_start(struct ifnet *ifp);
122 static  void vlan_ifinit(void *foo);
123 static  void vlan_input(struct ifnet *ifp, struct mbuf *m);
124 static  int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
125 static  int vlan_setmulti(struct ifnet *ifp);
126 static  int vlan_unconfig(struct ifnet *ifp);
127 static  int vlan_config(struct ifvlan *ifv, struct ifnet *p);
128
129 struct if_clone vlan_cloner = IF_CLONE_INITIALIZER("vlan",
130     vlan_clone_create, vlan_clone_destroy, 0, IF_MAXUNIT);
131
132 /*
133  * Program our multicast filter. What we're actually doing is
134  * programming the multicast filter of the parent. This has the
135  * side effect of causing the parent interface to receive multicast
136  * traffic that it doesn't really want, which ends up being discarded
137  * later by the upper protocol layers. Unfortunately, there's no way
138  * to avoid this: there really is only one physical interface.
139  */
140 static int
141 vlan_setmulti(struct ifnet *ifp)
142 {
143         struct ifnet            *ifp_p;
144         struct ifmultiaddr      *ifma, *rifma = NULL;
145         struct ifvlan           *sc;
146         struct vlan_mc_entry    *mc = NULL;
147         struct sockaddr_dl      sdl;
148         int                     error;
149
150         /* Find the parent. */
151         sc = ifp->if_softc;
152         ifp_p = sc->ifv_p;
153
154         /*
155          * If we don't have a parent, just remember the membership for
156          * when we do.
157          */
158         if (ifp_p == NULL)
159                 return(0);
160
161         bzero((char *)&sdl, sizeof sdl);
162         sdl.sdl_len = sizeof sdl;
163         sdl.sdl_family = AF_LINK;
164         sdl.sdl_index = ifp_p->if_index;
165         sdl.sdl_type = IFT_ETHER;
166         sdl.sdl_alen = ETHER_ADDR_LEN;
167
168         /* First, remove any existing filter entries. */
169         while(SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) {
170                 mc = SLIST_FIRST(&sc->vlan_mc_listhead);
171                 bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
172                 error = if_delmulti(ifp_p, (struct sockaddr *)&sdl);
173                 if (error)
174                         return(error);
175                 SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
176                 free(mc, M_VLAN);
177         }
178
179         /* Now program new ones. */
180         TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
181                 if (ifma->ifma_addr->sa_family != AF_LINK)
182                         continue;
183                 mc = malloc(sizeof(struct vlan_mc_entry), M_VLAN, M_WAITOK);
184                 bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
185                     (char *)&mc->mc_addr, ETHER_ADDR_LEN);
186                 SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
187                 bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
188                     LLADDR(&sdl), ETHER_ADDR_LEN);
189                 error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma);
190                 if (error)
191                         return(error);
192         }
193
194         return(0);
195 }
196
197 /*
198  * VLAN support can be loaded as a module.  The only place in the
199  * system that's intimately aware of this is ether_input.  We hook
200  * into this code through vlan_input_p which is defined there and
201  * set here.  Noone else in the system should be aware of this so
202  * we use an explicit reference here.
203  *
204  * NB: Noone should ever need to check if vlan_input_p is null or
205  *     not.  This is because interfaces have a count of the number
206  *     of active vlans (if_nvlans) and this should never be bumped
207  *     except by vlan_config--which is in this module so therefore
208  *     the module must be loaded and vlan_input_p must be non-NULL.
209  */
210 extern  void (*vlan_input_p)(struct ifnet *, struct mbuf *);
211
212 static int
213 vlan_modevent(module_t mod, int type, void *data) 
214
215
216         switch (type) { 
217         case MOD_LOAD: 
218                 LIST_INIT(&ifv_list);
219                 VLAN_LOCK_INIT();
220                 vlan_input_p = vlan_input;
221                 if_clone_attach(&vlan_cloner);
222                 break; 
223         case MOD_UNLOAD: 
224                 if_clone_detach(&vlan_cloner);
225                 vlan_input_p = NULL;
226                 while (!LIST_EMPTY(&ifv_list))
227                         vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if);
228                 VLAN_LOCK_DESTROY();
229                 break;
230         } 
231         return 0; 
232
233
234 static moduledata_t vlan_mod = { 
235         "if_vlan", 
236         vlan_modevent, 
237         0
238 }; 
239
240 DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
241
242 static int
243 vlan_clone_create(struct if_clone *ifc, int unit)
244 {
245         struct ifvlan *ifv;
246         struct ifnet *ifp;
247
248         ifv = malloc(sizeof(struct ifvlan), M_VLAN, M_WAITOK | M_ZERO);
249         ifp = &ifv->ifv_if;
250         SLIST_INIT(&ifv->vlan_mc_listhead);
251
252         ifp->if_softc = ifv;
253         ifp->if_name = "vlan";
254         ifp->if_unit = unit;
255         /* NB: flags are not set here */
256         ifp->if_linkmib = &ifv->ifv_mib;
257         ifp->if_linkmiblen = sizeof ifv->ifv_mib;
258         /* NB: mtu is not set here */
259
260         ifp->if_init = vlan_ifinit;
261         ifp->if_start = vlan_start;
262         ifp->if_ioctl = vlan_ioctl;
263         ifp->if_snd.ifq_maxlen = ifqmaxlen;
264         ether_ifattach(ifp, ifv->ifv_ac.ac_enaddr);
265         /* Now undo some of the damage... */
266         ifp->if_baudrate = 0;
267         ifp->if_type = IFT_L2VLAN;
268         ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN;
269
270         VLAN_LOCK();
271         LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
272         VLAN_UNLOCK();
273
274         return (0);
275 }
276
277 static void
278 vlan_clone_destroy(struct ifnet *ifp)
279 {
280         struct ifvlan *ifv = ifp->if_softc;
281
282         VLAN_LOCK();
283         LIST_REMOVE(ifv, ifv_list);
284         vlan_unconfig(ifp);
285         VLAN_UNLOCK();
286
287         ether_ifdetach(ifp);
288
289         free(ifv, M_VLAN);
290 }
291
292 static void
293 vlan_ifinit(void *foo)
294 {
295         return;
296 }
297
298 static void
299 vlan_start(struct ifnet *ifp)
300 {
301         struct ifvlan *ifv;
302         struct ifnet *p;
303         struct ether_vlan_header *evl;
304         struct mbuf *m;
305
306         ifv = ifp->if_softc;
307         p = ifv->ifv_p;
308
309         ifp->if_flags |= IFF_OACTIVE;
310         for (;;) {
311                 IF_DEQUEUE(&ifp->if_snd, m);
312                 if (m == 0)
313                         break;
314                 BPF_MTAP(ifp, m);
315
316                 /*
317                  * Do not run parent's if_start() if the parent is not up,
318                  * or parent's driver will cause a system crash.
319                  */
320                 if ((p->if_flags & (IFF_UP | IFF_RUNNING)) !=
321                                         (IFF_UP | IFF_RUNNING)) {
322                         m_freem(m);
323                         ifp->if_collisions++;
324                         continue;
325                 }
326
327                 /*
328                  * If underlying interface can do VLAN tag insertion itself,
329                  * just pass the packet along. However, we need some way to
330                  * tell the interface where the packet came from so that it
331                  * knows how to find the VLAN tag to use, so we attach a
332                  * packet tag that holds it.
333                  */
334                 if (p->if_capabilities & IFCAP_VLAN_HWTAGGING) {
335                         struct m_tag *mtag = m_tag_alloc(MTAG_VLAN,
336                                                          MTAG_VLAN_TAG,
337                                                          sizeof (u_int),
338                                                          M_NOWAIT);
339                         if (mtag == NULL) {
340                                 ifp->if_oerrors++;
341                                 m_freem(m);
342                                 continue;
343                         }
344                         *(u_int*)(mtag+1) = ifv->ifv_tag;
345                         m_tag_prepend(m, mtag);
346                 } else {
347                         M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
348                         if (m == NULL) {
349                                 if_printf(ifp, "unable to prepend VLAN header");
350                                 ifp->if_ierrors++;
351                                 continue;
352                         }
353                         /* M_PREPEND takes care of m_len, m_pkthdr.len for us */
354
355                         if (m->m_len < sizeof(*evl)) {
356                                 m = m_pullup(m, sizeof(*evl));
357                                 if (m == NULL) {
358                                         if_printf(ifp,
359                                             "cannot pullup VLAN header");
360                                         ifp->if_ierrors++;
361                                         continue;
362                                 }
363                         }
364
365                         /*
366                          * Transform the Ethernet header into an Ethernet header
367                          * with 802.1Q encapsulation.
368                          */
369                         bcopy(mtod(m, char *) + ifv->ifv_encaplen,
370                               mtod(m, char *), ETHER_HDR_LEN);
371                         evl = mtod(m, struct ether_vlan_header *);
372                         evl->evl_proto = evl->evl_encap_proto;
373                         evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
374                         evl->evl_tag = htons(ifv->ifv_tag);
375 #ifdef DEBUG
376                         printf("vlan_start: %*D\n", (int)sizeof *evl,
377                             (unsigned char *)evl, ":");
378 #endif
379                 }
380
381                 /*
382                  * Send it, precisely as ether_output() would have.
383                  * We are already running at splimp.
384                  */
385                 if (IF_HANDOFF(&p->if_snd, m, p))
386                         ifp->if_opackets++;
387                 else
388                         ifp->if_oerrors++;
389         }
390         ifp->if_flags &= ~IFF_OACTIVE;
391
392         return;
393 }
394
395 static void
396 vlan_input(struct ifnet *ifp, struct mbuf *m)
397 {
398         struct ether_vlan_header *evl;
399         struct ifvlan *ifv;
400         struct m_tag *mtag;
401         u_int tag;
402
403         mtag = m_tag_locate(m, MTAG_VLAN, MTAG_VLAN_TAG, NULL);
404         if (mtag != NULL) {
405                 /*
406                  * Packet is tagged, m contains a normal
407                  * Ethernet frame; the tag is stored out-of-band.
408                  */
409                 tag = EVL_VLANOFTAG(*(u_int*)(mtag+1));
410                 m_tag_delete(m, mtag);
411         } else {
412                 switch (ifp->if_type) {
413                 case IFT_ETHER:
414                         if (m->m_len < sizeof (*evl) &&
415                             (m = m_pullup(m, sizeof (*evl))) == NULL) {
416                                 if_printf(ifp, "cannot pullup VLAN header\n");
417                                 return;
418                         }
419                         evl = mtod(m, struct ether_vlan_header *);
420                         KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN,
421                                 ("vlan_input: bad encapsulated protocols (%u)",
422                                  ntohs(evl->evl_encap_proto)));
423
424                         tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
425
426                         /*
427                          * Restore the original ethertype.  We'll remove
428                          * the encapsulation after we've found the vlan
429                          * interface corresponding to the tag.
430                          */
431                         evl->evl_encap_proto = evl->evl_proto;
432                         break;
433                 default:
434                         tag = (u_int) -1;
435 #ifdef DIAGNOSTIC
436                         panic("vlan_input: unsupported if type %u", ifp->if_type);
437 #endif
438                         break;
439                 }
440         }
441
442         VLAN_LOCK();
443         LIST_FOREACH(ifv, &ifv_list, ifv_list)
444                 if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
445                         break;
446
447         if (ifv == NULL || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
448                 VLAN_UNLOCK();
449                 m_freem(m);
450                 ifp->if_noproto++;
451                 return; 
452         }
453         VLAN_UNLOCK();          /* XXX extend below? */
454
455         if (mtag == NULL) {
456                 /*
457                  * Packet had an in-line encapsulation header;
458                  * remove it.  The original header has already
459                  * been fixed up above.
460                  */
461                 bcopy(mtod(m, caddr_t),
462                       mtod(m, caddr_t) + ETHER_VLAN_ENCAP_LEN,
463                       ETHER_HDR_LEN);
464                 m_adj(m, ETHER_VLAN_ENCAP_LEN);
465         }
466
467         m->m_pkthdr.rcvif = &ifv->ifv_if;
468         ifv->ifv_if.if_ipackets++;
469
470         /* Pass it back through the parent's input routine. */
471         (*ifp->if_input)(&ifv->ifv_if, m);
472 }
473
474 static int
475 vlan_config(struct ifvlan *ifv, struct ifnet *p)
476 {
477         struct ifaddr *ifa1, *ifa2;
478         struct sockaddr_dl *sdl1, *sdl2;
479
480         VLAN_LOCK_ASSERT();
481
482         if (p->if_data.ifi_type != IFT_ETHER)
483                 return EPROTONOSUPPORT;
484         if (ifv->ifv_p)
485                 return EBUSY;
486
487         ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
488         ifv->ifv_mintu = ETHERMIN;
489         ifv->ifv_flags = 0;
490
491         /*
492          * If the parent supports the VLAN_MTU capability,
493          * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
494          * enable it.
495          */
496         p->if_nvlans++;
497         if (p->if_nvlans == 1 && (p->if_capabilities & IFCAP_VLAN_MTU) != 0) {
498                 /*
499                  * Enable Tx/Rx of VLAN-sized frames.
500                  */
501                 p->if_capenable |= IFCAP_VLAN_MTU;
502                 if (p->if_flags & IFF_UP) {
503                         struct ifreq ifr;
504                         int error;
505
506                         ifr.ifr_flags = p->if_flags;
507                         error = (*p->if_ioctl)(p, SIOCSIFFLAGS,
508                             (caddr_t) &ifr);
509                         if (error) {
510                                 p->if_nvlans--;
511                                 if (p->if_nvlans == 0)
512                                         p->if_capenable &= ~IFCAP_VLAN_MTU;
513                                 return (error);
514                         }
515                 }
516                 ifv->ifv_mtufudge = 0;
517         } else if ((p->if_capabilities & IFCAP_VLAN_MTU) == 0) {
518                 /*
519                  * Fudge the MTU by the encapsulation size.  This
520                  * makes us incompatible with strictly compliant
521                  * 802.1Q implementations, but allows us to use
522                  * the feature with other NetBSD implementations,
523                  * which might still be useful.
524                  */
525                 ifv->ifv_mtufudge = ifv->ifv_encaplen;
526         }
527
528         ifv->ifv_p = p;
529         ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge;
530         /*
531          * Copy only a selected subset of flags from the parent.
532          * Other flags are none of our business.
533          */
534         ifv->ifv_if.if_flags = (p->if_flags &
535             (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | IFF_POINTOPOINT));
536
537         /*
538          * If the parent interface can do hardware-assisted
539          * VLAN encapsulation, then propagate its hardware-
540          * assisted checksumming flags.
541          */
542         if (p->if_capabilities & IFCAP_VLAN_HWTAGGING)
543                 ifv->ifv_if.if_capabilities |= p->if_capabilities & IFCAP_HWCSUM;
544
545         /*
546          * Set up our ``Ethernet address'' to reflect the underlying
547          * physical interface's.
548          */
549         ifa1 = ifaddr_byindex(ifv->ifv_if.if_index);
550         ifa2 = ifaddr_byindex(p->if_index);
551         sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
552         sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
553         sdl1->sdl_type = IFT_ETHER;
554         sdl1->sdl_alen = ETHER_ADDR_LEN;
555         bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
556         bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
557
558         /*
559          * Configure multicast addresses that may already be
560          * joined on the vlan device.
561          */
562         (void)vlan_setmulti(&ifv->ifv_if);
563
564         return 0;
565 }
566
567 static int
568 vlan_unconfig(struct ifnet *ifp)
569 {
570         struct ifaddr *ifa;
571         struct sockaddr_dl *sdl;
572         struct vlan_mc_entry *mc;
573         struct ifvlan *ifv;
574         struct ifnet *p;
575         int error;
576
577         VLAN_LOCK_ASSERT();
578
579         ifv = ifp->if_softc;
580         p = ifv->ifv_p;
581
582         if (p) {
583                 struct sockaddr_dl sdl;
584
585                 /*
586                  * Since the interface is being unconfigured, we need to
587                  * empty the list of multicast groups that we may have joined
588                  * while we were alive from the parent's list.
589                  */
590                 bzero((char *)&sdl, sizeof sdl);
591                 sdl.sdl_len = sizeof sdl;
592                 sdl.sdl_family = AF_LINK;
593                 sdl.sdl_index = p->if_index;
594                 sdl.sdl_type = IFT_ETHER;
595                 sdl.sdl_alen = ETHER_ADDR_LEN;
596
597                 while(SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) {
598                         mc = SLIST_FIRST(&ifv->vlan_mc_listhead);
599                         bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
600                         error = if_delmulti(p, (struct sockaddr *)&sdl);
601                         if (error)
602                                 return(error);
603                         SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
604                         free(mc, M_VLAN);
605                 }
606
607                 p->if_nvlans--;
608                 if (p->if_nvlans == 0) {
609                         /*
610                          * Disable Tx/Rx of VLAN-sized frames.
611                          */
612                         p->if_capenable &= ~IFCAP_VLAN_MTU;
613                         if (p->if_flags & IFF_UP) {
614                                 struct ifreq ifr;
615
616                                 ifr.ifr_flags = p->if_flags;
617                                 (*p->if_ioctl)(p, SIOCSIFFLAGS, (caddr_t) &ifr);
618                         }
619                 }
620         }
621
622         /* Disconnect from parent. */
623         ifv->ifv_p = NULL;
624         ifv->ifv_if.if_mtu = ETHERMTU;          /* XXX why not 0? */
625         ifv->ifv_flags = 0;
626
627         /* Clear our MAC address. */
628         ifa = ifaddr_byindex(ifv->ifv_if.if_index);
629         sdl = (struct sockaddr_dl *)ifa->ifa_addr;
630         sdl->sdl_type = IFT_ETHER;
631         sdl->sdl_alen = ETHER_ADDR_LEN;
632         bzero(LLADDR(sdl), ETHER_ADDR_LEN);
633         bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
634
635         return 0;
636 }
637
638 static int
639 vlan_set_promisc(struct ifnet *ifp)
640 {
641         struct ifvlan *ifv = ifp->if_softc;
642         int error = 0;
643
644         if ((ifp->if_flags & IFF_PROMISC) != 0) {
645                 if ((ifv->ifv_flags & IFVF_PROMISC) == 0) {
646                         error = ifpromisc(ifv->ifv_p, 1);
647                         if (error == 0)
648                                 ifv->ifv_flags |= IFVF_PROMISC;
649                 }
650         } else {
651                 if ((ifv->ifv_flags & IFVF_PROMISC) != 0) {
652                         error = ifpromisc(ifv->ifv_p, 0);
653                         if (error == 0)
654                                 ifv->ifv_flags &= ~IFVF_PROMISC;
655                 }
656         }
657
658         return (error);
659 }
660
661 static int
662 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
663 {
664         struct ifaddr *ifa;
665         struct ifnet *p;
666         struct ifreq *ifr;
667         struct ifvlan *ifv;
668         struct vlanreq vlr;
669         int error = 0;
670
671         ifr = (struct ifreq *)data;
672         ifa = (struct ifaddr *)data;
673         ifv = ifp->if_softc;
674
675         switch (cmd) {
676         case SIOCSIFADDR:
677                 ifp->if_flags |= IFF_UP;
678
679                 switch (ifa->ifa_addr->sa_family) {
680 #ifdef INET
681                 case AF_INET:
682                         arp_ifinit(&ifv->ifv_if, ifa);
683                         break;
684 #endif
685                 default:
686                         break;
687                 }
688                 break;
689
690         case SIOCGIFADDR:
691                 {
692                         struct sockaddr *sa;
693
694                         sa = (struct sockaddr *) &ifr->ifr_data;
695                         bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
696                               (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
697                 }
698                 break;
699
700         case SIOCGIFMEDIA:
701                 VLAN_LOCK();
702                 if (ifv->ifv_p != NULL) {
703                         error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p,
704                                         SIOCGIFMEDIA, data);
705                         VLAN_UNLOCK();
706                         /* Limit the result to the parent's current config. */
707                         if (error == 0) {
708                                 struct ifmediareq *ifmr;
709
710                                 ifmr = (struct ifmediareq *) data;
711                                 if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) {
712                                         ifmr->ifm_count = 1;
713                                         error = copyout(&ifmr->ifm_current,
714                                                 ifmr->ifm_ulist, 
715                                                 sizeof(int));
716                                 }
717                         }
718                 } else {
719                         VLAN_UNLOCK();
720                         error = EINVAL;
721                 }
722                 break;
723
724         case SIOCSIFMEDIA:
725                 error = EINVAL;
726                 break;
727
728         case SIOCSIFMTU:
729                 /*
730                  * Set the interface MTU.
731                  */
732                 VLAN_LOCK();
733                 if (ifv->ifv_p != NULL) {
734                         if (ifr->ifr_mtu >
735                              (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) ||
736                             ifr->ifr_mtu <
737                              (ifv->ifv_mintu - ifv->ifv_mtufudge))
738                                 error = EINVAL;
739                         else
740                                 ifp->if_mtu = ifr->ifr_mtu;
741                 } else
742                         error = EINVAL;
743                 VLAN_UNLOCK();
744                 break;
745
746         case SIOCSETVLAN:
747                 error = copyin(ifr->ifr_data, &vlr, sizeof vlr);
748                 if (error)
749                         break;
750                 if (vlr.vlr_parent[0] == '\0') {
751                         VLAN_LOCK();
752                         vlan_unconfig(ifp);
753                         if (ifp->if_flags & IFF_UP)
754                                 if_down(ifp);
755                         ifp->if_flags &= ~IFF_RUNNING;
756                         VLAN_UNLOCK();
757                         break;
758                 }
759                 p = ifunit(vlr.vlr_parent);
760                 if (p == 0) {
761                         error = ENOENT;
762                         break;
763                 }
764                 /*
765                  * Don't let the caller set up a VLAN tag with
766                  * anything except VLID bits.
767                  */
768                 if (vlr.vlr_tag & ~EVL_VLID_MASK) {
769                         error = EINVAL;
770                         break;
771                 }
772                 VLAN_LOCK();
773                 error = vlan_config(ifv, p);
774                 if (error) {
775                         VLAN_UNLOCK();
776                         break;
777                 }
778                 ifv->ifv_tag = vlr.vlr_tag;
779                 ifp->if_flags |= IFF_RUNNING;
780                 VLAN_UNLOCK();
781
782                 /* Update promiscuous mode, if necessary. */
783                 vlan_set_promisc(ifp);
784                 break;
785                 
786         case SIOCGETVLAN:
787                 bzero(&vlr, sizeof vlr);
788                 VLAN_LOCK();
789                 if (ifv->ifv_p) {
790                         snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
791                             "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit);
792                         vlr.vlr_tag = ifv->ifv_tag;
793                 }
794                 VLAN_UNLOCK();
795                 error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
796                 break;
797                 
798         case SIOCSIFFLAGS:
799                 /*
800                  * For promiscuous mode, we enable promiscuous mode on
801                  * the parent if we need promiscuous on the VLAN interface.
802                  */
803                 if (ifv->ifv_p != NULL)
804                         error = vlan_set_promisc(ifp);
805                 break;
806
807         case SIOCADDMULTI:
808         case SIOCDELMULTI:
809                 error = vlan_setmulti(ifp);
810                 break;
811         default:
812                 error = EINVAL;
813         }
814         return error;
815 }