if_vlan.c revision 1.10 1 /* $NetBSD: if_vlan.c,v 1.10 2000/10/03 23:33:38 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Andrew Doran and Jason R. Thorpe of Zembu Labs, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Copyright 1998 Massachusetts Institute of Technology
41 *
42 * Permission to use, copy, modify, and distribute this software and
43 * its documentation for any purpose and without fee is hereby
44 * granted, provided that both the above copyright notice and this
45 * permission notice appear in all copies, that both the above
46 * copyright notice and this permission notice appear in all
47 * supporting documentation, and that the name of M.I.T. not be used
48 * in advertising or publicity pertaining to distribution of the
49 * software without specific, written prior permission. M.I.T. makes
50 * no representations about the suitability of this software for any
51 * purpose. It is provided "as is" without express or implied
52 * warranty.
53 *
54 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
55 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
56 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
57 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
58 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
60 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
61 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
62 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
63 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
64 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 *
67 * from FreeBSD: if_vlan.c,v 1.16 2000/03/26 15:21:40 charnier Exp
68 * via OpenBSD: if_vlan.c,v 1.4 2000/05/15 19:15:00 chris Exp
69 */
70
71 /*
72 * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. Might be
73 * extended some day to also handle IEEE 802.1P priority tagging. This is
74 * sort of sneaky in the implementation, since we need to pretend to be
75 * enough of an Ethernet implementation to make ARP work. The way we do
76 * this is by telling everyone that we are an Ethernet interface, and then
77 * catch the packets that ether_output() left on our output queue when it
78 * calls if_start(), rewrite them for use by the real outgoing interface,
79 * and ask it to send them.
80 *
81 * TODO:
82 *
83 * - Need some way to notify vlan interfaces when the parent
84 * interface changes MTU.
85 *
86 * - Need to notify VLANs when an interface detaches.
87 *
88 * - Need a way to facilitate parent interfaces that can do
89 * tag insertion and/or extraction in hardware.
90 *
91 * - Need to make promiscuous mode work.
92 */
93
94 #include "opt_inet.h"
95 #include "bpfilter.h"
96
97 #include <sys/param.h>
98 #include <sys/kernel.h>
99 #include <sys/mbuf.h>
100 #include <sys/queue.h>
101 #include <sys/socket.h>
102 #include <sys/sockio.h>
103 #include <sys/systm.h>
104 #include <sys/proc.h>
105
106 #if NBPFILTER > 0
107 #include <net/bpf.h>
108 #endif
109 #include <net/if.h>
110 #include <net/if_dl.h>
111 #include <net/if_types.h>
112 #include <net/if_ether.h>
113 #include <net/if_vlanvar.h>
114
115 #ifdef INET
116 #include <netinet/in.h>
117 #include <netinet/if_inarp.h>
118 #endif
119
120 extern struct ifaddr **ifnet_addrs; /* XXX if.c */
121
122 struct vlan_mc_entry {
123 LIST_ENTRY(vlan_mc_entry) mc_entries;
124 /*
125 * A key to identify this entry. The mc_addr below can't be
126 * used since multiple sockaddr may mapped into the same
127 * ether_multi (e.g., AF_UNSPEC).
128 */
129 union {
130 struct ether_multi *mcu_enm;
131 } mc_u;
132 struct sockaddr_storage mc_addr;
133 };
134
135 #define mc_enm mc_u.mcu_enm
136
137 struct ifvlan {
138 union {
139 struct ethercom ifvu_ec;
140 } ifv_u;
141 struct ifnet *ifv_p; /* parent interface of this vlan */
142 struct ifv_linkmib {
143 const struct vlan_multisw *ifvm_msw;
144 int ifvm_encaplen; /* encapsulation length */
145 int ifvm_mtufudge; /* MTU fudged by this much */
146 int ifvm_mintu; /* min transmission unit */
147 u_int16_t ifvm_proto; /* encapsulation ethertype */
148 u_int16_t ifvm_tag; /* tag to apply on packets */
149 } ifv_mib;
150 LIST_HEAD(__vlan_mchead, vlan_mc_entry) ifv_mc_listhead;
151 LIST_ENTRY(ifvlan) ifv_list;
152 };
153
154 #define ifv_ec ifv_u.ifvu_ec
155
156 #define ifv_if ifv_ec.ec_if
157
158 #define ifv_msw ifv_mib.ifvm_msw
159 #define ifv_encaplen ifv_mib.ifvm_encaplen
160 #define ifv_mtufudge ifv_mib.ifvm_mtufudge
161 #define ifv_mintu ifv_mib.ifvm_mintu
162 #define ifv_tag ifv_mib.ifvm_tag
163
164 struct vlan_multisw {
165 int (*vmsw_addmulti)(struct ifvlan *, struct ifreq *);
166 int (*vmsw_delmulti)(struct ifvlan *, struct ifreq *);
167 void (*vmsw_purgemulti)(struct ifvlan *);
168 };
169
170 static int vlan_ether_addmulti(struct ifvlan *, struct ifreq *);
171 static int vlan_ether_delmulti(struct ifvlan *, struct ifreq *);
172 static void vlan_ether_purgemulti(struct ifvlan *);
173
174 const struct vlan_multisw vlan_ether_multisw = {
175 vlan_ether_addmulti,
176 vlan_ether_delmulti,
177 vlan_ether_purgemulti,
178 };
179
180 static int vlan_clone_create(struct if_clone *, int);
181 static void vlan_clone_destroy(struct ifnet *);
182 static int vlan_config(struct ifvlan *, struct ifnet *);
183 static int vlan_ioctl(struct ifnet *, u_long, caddr_t);
184 static void vlan_start(struct ifnet *);
185 static int vlan_unconfig(struct ifnet *);
186
187 void vlanattach(int);
188
189 /* XXX This should be a hash table with the tag as the basis of the key. */
190 static LIST_HEAD(, ifvlan) ifv_list;
191
192 struct if_clone vlan_cloner =
193 IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
194
195 void
196 vlanattach(int n)
197 {
198
199 LIST_INIT(&ifv_list);
200 if_clone_attach(&vlan_cloner);
201 }
202
203 static int
204 vlan_clone_create(struct if_clone *ifc, int unit)
205 {
206 struct ifvlan *ifv;
207 struct ifnet *ifp;
208
209 ifv = malloc(sizeof(struct ifvlan), M_DEVBUF, M_WAITOK);
210 memset(ifv, 0, sizeof(struct ifvlan));
211 ifp = &ifv->ifv_ec.ec_if;
212 LIST_INIT(&ifv->ifv_mc_listhead);
213 LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list);
214
215 sprintf(ifp->if_xname, "%s%d", ifc->ifc_name, unit);
216 ifp->if_softc = ifv;
217 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
218 ifp->if_start = vlan_start;
219 ifp->if_ioctl = vlan_ioctl;
220
221 if_attach(ifp);
222
223 return (0);
224 }
225
226 static void
227 vlan_clone_destroy(struct ifnet *ifp)
228 {
229 struct ifvlan *ifv;
230 int s;
231
232 ifv = (struct ifvlan *)ifp->if_softc;
233 s = splsoftnet();
234
235 LIST_REMOVE(ifv, ifv_list);
236 vlan_unconfig(ifp);
237
238 #if NBPFILTER > 0
239 bpfdetach(ifp);
240 #endif
241 ether_ifdetach(ifp);
242 if_detach(ifp);
243 free(ifv, M_DEVBUF);
244
245 splx(s);
246 }
247
248 static int
249 vlan_config(struct ifvlan *ifv, struct ifnet *p)
250 {
251 struct ifnet *ifp = &ifv->ifv_if;
252 int error;
253
254 if (ifv->ifv_p != NULL)
255 return (EBUSY);
256
257 switch (p->if_type) {
258 case IFT_ETHER:
259 {
260 struct ethercom *ec = (void *) p;
261
262 ifv->ifv_msw = &vlan_ether_multisw;
263 ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN;
264 ifv->ifv_mintu = ETHERMIN;
265
266 /*
267 * If the parent supports the VLAN_MTU capability,
268 * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames,
269 * enable it.
270 */
271 if (ec->ec_nvlans++ == 0 &&
272 (ec->ec_capabilities & ETHERCAP_VLAN_MTU) != 0) {
273 /*
274 * Enable Tx/Rx of VLAN-sized frames.
275 */
276 ec->ec_capenable |= ETHERCAP_VLAN_MTU;
277 if (p->if_flags & IFF_UP) {
278 struct ifreq ifr;
279
280 ifr.ifr_flags = p->if_flags;
281 error = (*p->if_ioctl)(p, SIOCSIFFLAGS,
282 (caddr_t) &ifr);
283 if (error) {
284 if (ec->ec_nvlans-- == 1)
285 ec->ec_capenable &=
286 ~ETHERCAP_VLAN_MTU;
287 return (error);
288 }
289 }
290 ifv->ifv_mtufudge = 0;
291 } else if ((ec->ec_capabilities & ETHERCAP_VLAN_MTU) == 0) {
292 /*
293 * Fudge the MTU by the encapsulation size. This
294 * makes us incompatible with strictly compliant
295 * 802.1Q implementations, but allows us to use
296 * the feature with other NetBSD implementations,
297 * which might still be useful.
298 */
299 ifv->ifv_mtufudge = ifv->ifv_encaplen;
300 }
301
302 /*
303 * We inherit the parent's Ethernet address.
304 */
305 ether_ifattach(ifp, LLADDR(p->if_sadl));
306 ifp->if_hdrlen = sizeof(struct ether_vlan_header); /* XXX? */
307 #if NBPFILTER > 0
308 bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB,
309 sizeof(struct ether_header));
310 #endif
311 break;
312 }
313
314 default:
315 return (EPROTONOSUPPORT);
316 }
317
318 ifv->ifv_p = p;
319 ifv->ifv_if.if_mtu = p->if_mtu - ifv->ifv_mtufudge;
320 ifv->ifv_if.if_flags = p->if_flags;
321
322 /*
323 * Inherit the if_type from the parent. This allows us
324 * to participate in bridges of that type.
325 */
326 ifv->ifv_if.if_type = p->if_type;
327
328 return (0);
329 }
330
331 static int
332 vlan_unconfig(struct ifnet *ifp)
333 {
334 struct ifvlan *ifv = ifp->if_softc;
335 int s;
336
337 ifv = ifp->if_softc;
338 if (ifv->ifv_p == NULL)
339 return (0);
340
341 s = splsoftnet();
342
343 /*
344 * Since the interface is being unconfigured, we need to empty the
345 * list of multicast groups that we may have joined while we were
346 * alive and remove them from the parent's list also.
347 */
348 (*ifv->ifv_msw->vmsw_purgemulti)(ifv);
349
350 /* Disconnect from parent. */
351 switch (ifv->ifv_p->if_type) {
352 case IFT_ETHER:
353 {
354 struct ethercom *ec = (void *) ifv->ifv_p;
355
356 if (ec->ec_nvlans-- == 1) {
357 /*
358 * Disable Tx/Rx of VLAN-sized frames.
359 */
360 ec->ec_capenable &= ~ETHERCAP_VLAN_MTU;
361 if (ifv->ifv_p->if_flags & IFF_UP) {
362 struct ifreq ifr;
363
364 ifr.ifr_flags = ifv->ifv_p->if_flags;
365 (void) (*ifv->ifv_p->if_ioctl)(ifv->ifv_p,
366 SIOCSIFFLAGS, (caddr_t) &ifr);
367 }
368 }
369
370 #if NBPFILTER > 0
371 bpfdetach(ifp);
372 #endif
373 ether_ifdetach(ifp);
374 break;
375 }
376
377 #ifdef DIAGNOSTIC
378 default:
379 panic("vlan_unconfig: impossible");
380 #endif
381 }
382
383 ifv->ifv_p = NULL;
384 ifv->ifv_if.if_mtu = 0;
385
386 splx(s);
387 return (0);
388 }
389
390 static int
391 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
392 {
393 struct proc *p = curproc; /* XXX */
394 struct ifaddr *ifa;
395 struct ifnet *pr;
396 struct ifreq *ifr;
397 struct ifvlan *ifv;
398 struct vlanreq vlr;
399 struct sockaddr *sa;
400 int error;
401
402 error = 0;
403 ifr = (struct ifreq *)data;
404 ifa = (struct ifaddr *)data;
405 ifv = ifp->if_softc;
406
407 switch (cmd) {
408 case SIOCSIFADDR:
409 ifp->if_flags |= IFF_UP;
410
411 switch (ifa->ifa_addr->sa_family) {
412 #ifdef INET
413 case AF_INET:
414 arp_ifinit(ifp, ifa);
415 break;
416 #endif
417 default:
418 break;
419 }
420 break;
421
422 case SIOCGIFADDR:
423 sa = (struct sockaddr *)&ifr->ifr_data;
424 memcpy(sa->sa_data, LLADDR(ifp->if_sadl), ETHER_ADDR_LEN);
425 break;
426
427 case SIOCSIFMTU:
428 if (ifv->ifv_p != NULL) {
429 if (ifr->ifr_mtu >
430 (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) ||
431 ifr->ifr_mtu <
432 (ifv->ifv_mintu - ifv->ifv_mtufudge))
433 error = EINVAL;
434 else
435 ifp->if_mtu = ifr->ifr_mtu;
436 } else
437 error = EINVAL;
438 break;
439
440 case SIOCSETVLAN:
441 if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
442 break;
443 if ((error = copyin(ifr->ifr_data, &vlr, sizeof(vlr))) != 0)
444 break;
445 if (vlr.vlr_parent[0] == '\0') {
446 vlan_unconfig(ifp);
447 if_down(ifp);
448 ifp->if_flags &= ~(IFF_UP|IFF_RUNNING);
449 break;
450 }
451 if (vlr.vlr_tag != EVL_VLANOFTAG(vlr.vlr_tag)) {
452 error = EINVAL; /* check for valid tag */
453 break;
454 }
455 if ((pr = ifunit(vlr.vlr_parent)) == 0) {
456 error = ENOENT;
457 break;
458 }
459 if ((error = vlan_config(ifv, pr)) != 0)
460 break;
461 ifv->ifv_tag = vlr.vlr_tag;
462 ifp->if_flags |= IFF_RUNNING;
463 break;
464
465 case SIOCGETVLAN:
466 memset(&vlr, 0, sizeof(vlr));
467 if (ifv->ifv_p != NULL) {
468 snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), "%s",
469 ifv->ifv_p->if_xname);
470 vlr.vlr_tag = ifv->ifv_tag;
471 }
472 error = copyout(&vlr, ifr->ifr_data, sizeof(vlr));
473 break;
474
475 case SIOCSIFFLAGS:
476 /*
477 * XXX We don't support promiscuous mode right now because
478 * it would require help from the underlying drivers, which
479 * hasn't been implemented.
480 */
481 if ((ifr->ifr_flags & IFF_PROMISC) != 0) {
482 ifp->if_flags &= ~(IFF_PROMISC);
483 error = EINVAL;
484 }
485 break;
486
487 case SIOCADDMULTI:
488 error = (*ifv->ifv_msw->vmsw_addmulti)(ifv, ifr);
489 break;
490
491 case SIOCDELMULTI:
492 error = (*ifv->ifv_msw->vmsw_delmulti)(ifv, ifr);
493 break;
494
495 default:
496 error = EINVAL;
497 }
498
499 return (error);
500 }
501
502 static int
503 vlan_ether_addmulti(struct ifvlan *ifv, struct ifreq *ifr)
504 {
505 struct vlan_mc_entry *mc;
506 u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
507 int error;
508
509 if (ifr->ifr_addr.sa_len > sizeof(struct sockaddr_storage))
510 return (EINVAL);
511
512 error = ether_addmulti(ifr, &ifv->ifv_ec);
513 if (error != ENETRESET)
514 return (error);
515
516 /*
517 * This is new multicast address. We have to tell parent
518 * about it. Also, remember this multicast address so that
519 * we can delete them on unconfigure.
520 */
521 MALLOC(mc, struct vlan_mc_entry *, sizeof(struct vlan_mc_entry),
522 M_DEVBUF, M_NOWAIT);
523 if (mc == NULL) {
524 error = ENOMEM;
525 goto alloc_failed;
526 }
527
528 /*
529 * As ether_addmulti() returns ENETRESET, following two
530 * statement shouldn't fail.
531 */
532 (void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
533 ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, mc->mc_enm);
534 memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
535 LIST_INSERT_HEAD(&ifv->ifv_mc_listhead, mc, mc_entries);
536
537 error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCADDMULTI,
538 (caddr_t)ifr);
539 if (error != 0)
540 goto ioctl_failed;
541 return (error);
542
543 ioctl_failed:
544 LIST_REMOVE(mc, mc_entries);
545 FREE(mc, M_DEVBUF);
546 alloc_failed:
547 (void)ether_delmulti(ifr, &ifv->ifv_ec);
548 return (error);
549 }
550
551 static int
552 vlan_ether_delmulti(struct ifvlan *ifv, struct ifreq *ifr)
553 {
554 struct ether_multi *enm;
555 struct vlan_mc_entry *mc;
556 u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
557 int error;
558
559 /*
560 * Find a key to lookup vlan_mc_entry. We have to do this
561 * before calling ether_delmulti for obvious reason.
562 */
563 if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
564 return (error);
565 ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ec, enm);
566
567 error = ether_delmulti(ifr, &ifv->ifv_ec);
568 if (error != ENETRESET)
569 return (error);
570
571 /* We no longer use this multicast address. Tell parent so. */
572 error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, SIOCDELMULTI,
573 (caddr_t)ifr);
574 if (error == 0) {
575 /* And forget about this address. */
576 for (mc = LIST_FIRST(&ifv->ifv_mc_listhead); mc != NULL;
577 mc = LIST_NEXT(mc, mc_entries)) {
578 if (mc->mc_enm == enm) {
579 LIST_REMOVE(mc, mc_entries);
580 FREE(mc, M_DEVBUF);
581 break;
582 }
583 }
584 KASSERT(mc != NULL);
585 } else
586 (void)ether_addmulti(ifr, &ifv->ifv_ec);
587 return (error);
588 }
589
590 /*
591 * Delete any multicast address we have asked to add form parent
592 * interface. Called when the vlan is being unconfigured.
593 */
594 static void
595 vlan_ether_purgemulti(struct ifvlan *ifv)
596 {
597 struct ifnet *ifp = ifv->ifv_p; /* Parent. */
598 struct vlan_mc_entry *mc;
599 union {
600 struct ifreq ifreq;
601 struct {
602 char ifr_name[IFNAMSIZ];
603 struct sockaddr_storage;
604 } ifreq_storage;
605 } ifreq;
606 struct ifreq *ifr = &ifreq.ifreq;
607
608 memcpy(ifr->ifr_name, ifp->if_xname, IFNAMSIZ);
609 while ((mc = LIST_FIRST(&ifv->ifv_mc_listhead)) != NULL) {
610 memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
611 (void)(*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)ifr);
612 LIST_REMOVE(mc->mc_enm, enm_list);
613 free(mc->mc_enm, M_IFMADDR);
614 LIST_REMOVE(mc, mc_entries);
615 FREE(mc, M_DEVBUF);
616 }
617
618 KASSERT(LIST_FIRST(&ifv->ifv_ec.ec_multiaddrs) == NULL);
619 }
620
621 static void
622 vlan_start(struct ifnet *ifp)
623 {
624 struct ifvlan *ifv;
625 struct ifnet *p;
626 struct mbuf *m;
627
628 ifv = ifp->if_softc;
629 p = ifv->ifv_p;
630 ifp->if_flags |= IFF_OACTIVE;
631
632 for (;;) {
633 IF_DEQUEUE(&ifp->if_snd, m);
634 if (m == NULL)
635 break;
636
637 #if NBPFILTER > 0
638 if (ifp->if_bpf)
639 bpf_mtap(ifp->if_bpf, m);
640 #endif
641
642 /*
643 * XXX Should handle the case where the underlying hardware
644 * interface can do VLAN tag insertion itself.
645 */
646 M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT);
647 if (m == NULL) {
648 printf("%s: unable to prepend encap header",
649 ifv->ifv_p->if_xname);
650 ifp->if_oerrors++;
651 continue;
652 }
653
654 switch (p->if_type) {
655 case IFT_ETHER:
656 {
657 struct ether_vlan_header *evl;
658
659 if (m->m_len < sizeof(struct ether_vlan_header) &&
660 (m = m_pullup(m,
661 sizeof(struct ether_vlan_header))) == NULL) {
662 printf("%s: unable to pullup encap header",
663 ifv->ifv_p->if_xname);
664 ifp->if_oerrors++;
665 continue;
666 }
667
668 /*
669 * Transform the Ethernet header into an Ethernet
670 * header with 802.1Q encapsulation.
671 */
672 memmove(mtod(m, caddr_t),
673 mtod(m, caddr_t) + ifv->ifv_encaplen,
674 sizeof(struct ether_header));
675 evl = mtod(m, struct ether_vlan_header *);
676 evl->evl_proto = evl->evl_encap_proto;
677 evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
678 evl->evl_tag = htons(ifv->ifv_tag);
679 break;
680 }
681
682 #ifdef DIAGNOSTIC
683 default:
684 panic("vlan_start: impossible");
685 #endif
686 }
687
688 /*
689 * Send it, precisely as the parent's output routine
690 * would have. We are already running at splimp.
691 */
692 if (IF_QFULL(&p->if_snd)) {
693 IF_DROP(&p->if_snd);
694 /* XXX stats */
695 ifp->if_oerrors++;
696 m_freem(m);
697 continue;
698 }
699
700 IF_ENQUEUE(&p->if_snd, m);
701 if ((p->if_flags & IFF_OACTIVE) == 0) {
702 p->if_start(p);
703 ifp->if_opackets++;
704 }
705 }
706
707 ifp->if_flags &= ~IFF_OACTIVE;
708 }
709
710 /*
711 * Given an Ethernet frame, find a valid vlan interface corresponding to the
712 * given source interface and tag, then run the the real packet through
713 * the parent's input routine.
714 */
715 void
716 vlan_input(struct ifnet *ifp, struct mbuf *m)
717 {
718 struct ifvlan *ifv;
719 u_int tag;
720
721 switch (ifp->if_type) {
722 case IFT_ETHER:
723 {
724 struct ether_vlan_header *evl;
725
726 if (m->m_len < sizeof(struct ether_vlan_header) &&
727 (m = m_pullup(m,
728 sizeof(struct ether_vlan_header))) == NULL) {
729 printf("%s: no memory for VLAN header, "
730 "dropping packet.\n", ifp->if_xname);
731 return;
732 }
733 evl = mtod(m, struct ether_vlan_header *);
734 KASSERT(ntohs(evl->evl_encap_proto) == ETHERTYPE_VLAN);
735
736 tag = EVL_VLANOFTAG(ntohs(evl->evl_tag));
737
738 /*
739 * Restore the original ethertype. We'll remove
740 * the encapsulation after we've found the vlan
741 * interface corresponding to the tag.
742 */
743 evl->evl_encap_proto = evl->evl_proto;
744 break;
745 }
746
747 default:
748 tag = (u_int) -1; /* XXX GCC */
749 #ifdef DIAGNOSTIC
750 panic("vlan_input: impossible");
751 #endif
752 }
753
754 for (ifv = LIST_FIRST(&ifv_list); ifv != NULL;
755 ifv = LIST_NEXT(ifv, ifv_list))
756 if (ifp == ifv->ifv_p && tag == ifv->ifv_tag)
757 break;
758
759 if (ifv == NULL ||
760 (ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
761 (IFF_UP|IFF_RUNNING)) {
762 m_free(m);
763 ifp->if_data.ifi_noproto++;
764 return;
765 }
766
767 /*
768 * Now, remove the encapsulation header. The original
769 * header has already been fixed up above.
770 */
771 memmove(mtod(m, caddr_t) + ifv->ifv_encaplen, mtod(m, caddr_t),
772 ifv->ifv_encaplen);
773 m_adj(m, ifv->ifv_encaplen);
774
775 m->m_pkthdr.rcvif = &ifv->ifv_if;
776 ifv->ifv_if.if_ipackets++;
777
778 #if NBPFILTER > 0
779 if (ifv->ifv_if.if_bpf)
780 bpf_mtap(ifv->ifv_if.if_bpf, m);
781 #endif
782
783 /* Pass it back through the parent's input routine. */
784 (*ifp->if_input)(&ifv->ifv_if, m);
785 }
786