FreeBSD/Linux Kernel Cross Reference
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 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 * XXX It's incorrect to assume that we must always kludge up
45 * headers on the physical device's behalf: some devices support
46 * VLAN tag insersion and extraction in firmware. For these cases,
47 * one can change the behavior of the vlan interface by setting
48 * the LINK0 flag on it (that is setting the vlan interface's LINK0
49 * flag, _not_ the parent's LINK0 flag; we try to leave the parent
50 * alone). If the interface as the LINK0 flag set, then it will
51 * not modify the ethernet header on output because the parent
52 * can do that for itself. On input, the parent can call vlan_input_tag()
53 * directly in order to supply us with an incoming mbuf and the vlan
54 * tag value that goes with it.
55 */
56
57 #include "vlan.h"
58 #if NVLAN > 0
59 #include "opt_inet.h"
60 #include "bpfilter.h"
61
62 #include <sys/param.h>
63 #include <sys/kernel.h>
64 #include <sys/malloc.h>
65 #include <sys/mbuf.h>
66 #include <sys/queue.h>
67 #include <sys/socket.h>
68 #include <sys/sockio.h>
69 #include <sys/sysctl.h>
70 #include <sys/systm.h>
71
72 #if NBPFILTER > 0
73 #include <net/bpf.h>
74 #endif
75 #include <net/ethernet.h>
76 #include <net/if.h>
77 #include <net/if_arp.h>
78 #include <net/if_dl.h>
79 #include <net/if_types.h>
80 #include <net/if_vlan_var.h>
81
82 #ifdef INET
83 #include <netinet/in.h>
84 #include <netinet/if_ether.h>
85 #endif
86
87 SYSCTL_NODE(_net_link, IFT_8021_VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
88 SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency");
89
90 u_int vlan_proto = ETHERTYPE_VLAN;
91 SYSCTL_INT(_net_link_vlan_link, VLANCTL_PROTO, proto, CTLFLAG_RW, &vlan_proto,
92 0, "Ethernet protocol used for VLAN encapsulation");
93
94 static struct ifvlan ifv_softc[NVLAN];
95
96 static void vlan_start(struct ifnet *ifp);
97 static void vlan_ifinit(void *foo);
98 static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
99 static int vlan_setmulti(struct ifnet *ifp);
100 static int vlan_unconfig(struct ifnet *ifp);
101 static int vlan_config(struct ifvlan *ifv, struct ifnet *p);
102
103 /*
104 * Program our multicast filter. What we're actually doing is
105 * programming the multicast filter of the parent. This has the
106 * side effect of causing the parent interface to receive multicast
107 * traffic that it doesn't really want, which ends up being discarded
108 * later by the upper protocol layers. Unfortunately, there's no way
109 * to avoid this: there really is only one physical interface.
110 */
111 static int vlan_setmulti(struct ifnet *ifp)
112 {
113 struct ifnet *ifp_p;
114 struct ifmultiaddr *ifma, *rifma = NULL;
115 struct ifvlan *sc;
116 struct vlan_mc_entry *mc = NULL;
117 struct sockaddr_dl sdl;
118 int error;
119
120 /* Find the parent. */
121 sc = ifp->if_softc;
122 ifp_p = sc->ifv_p;
123
124 sdl.sdl_len = ETHER_ADDR_LEN;
125 sdl.sdl_family = AF_LINK;
126
127 /* First, remove any existing filter entries. */
128 while(sc->vlan_mc_listhead.slh_first != NULL) {
129 mc = sc->vlan_mc_listhead.slh_first;
130 bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
131 error = if_delmulti(ifp_p, (struct sockaddr *)&sdl);
132 if (error)
133 return(error);
134 SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
135 free(mc, M_DEVBUF);
136 }
137
138 /* Now program new ones. */
139 for (ifma = ifp->if_multiaddrs.lh_first;
140 ifma != NULL;ifma = ifma->ifma_link.le_next) {
141 if (ifma->ifma_addr->sa_family != AF_LINK)
142 continue;
143 mc = malloc(sizeof(struct vlan_mc_entry), M_DEVBUF, M_NOWAIT);
144 bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
145 (char *)&mc->mc_addr, ETHER_ADDR_LEN);
146 SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
147 error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma);
148 if (error)
149 return(error);
150 }
151
152 return(0);
153 }
154
155 static void
156 vlaninit(void *dummy)
157 {
158 int i;
159
160 for (i = 0; i < NVLAN; i++) {
161 struct ifnet *ifp = &ifv_softc[i].ifv_if;
162
163 ifp->if_softc = &ifv_softc[i];
164 ifp->if_name = "vlan";
165 ifp->if_unit = i;
166 /* NB: flags are not set here */
167 ifp->if_linkmib = &ifv_softc[i].ifv_mib;
168 ifp->if_linkmiblen = sizeof ifv_softc[i].ifv_mib;
169 /* NB: mtu is not set here */
170
171 ifp->if_init = vlan_ifinit;
172 ifp->if_start = vlan_start;
173 ifp->if_ioctl = vlan_ioctl;
174 ifp->if_output = ether_output;
175 ifp->if_snd.ifq_maxlen = ifqmaxlen;
176 if_attach(ifp);
177 ether_ifattach(ifp);
178 #if NBPFILTER > 0
179 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
180 #endif
181 /* Now undo some of the damage... */
182 ifp->if_data.ifi_type = IFT_8021_VLAN;
183 ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN;
184 ifp->if_resolvemulti = 0;
185 }
186 }
187 PSEUDO_SET(vlaninit, if_vlan);
188
189 static void
190 vlan_ifinit(void *foo)
191 {
192 return;
193 }
194
195 static void
196 vlan_start(struct ifnet *ifp)
197 {
198 struct ifvlan *ifv;
199 struct ifnet *p;
200 struct ether_vlan_header *evl;
201 struct mbuf *m;
202
203 ifv = ifp->if_softc;
204 p = ifv->ifv_p;
205
206 ifp->if_flags |= IFF_OACTIVE;
207 for (;;) {
208 IF_DEQUEUE(&ifp->if_snd, m);
209 if (m == 0)
210 break;
211 #if NBPFILTER > 0
212 if (ifp->if_bpf)
213 bpf_mtap(ifp, m);
214 #endif /* NBPFILTER > 0 */
215
216 /*
217 * If the LINK0 flag is set, it means the underlying interface
218 * can do VLAN tag insertion itself and doesn't require us to
219 * create a special header for it. In this case, we just pass
220 * the packet along. However, we need some way to tell the
221 * interface where the packet came from so that it knows how
222 * to find the VLAN tag to use, so we set the rcvif in the
223 * mbuf header to our ifnet.
224 *
225 * Note: we also set the M_PROTO1 flag in the mbuf to let
226 * the parent driver know that the rcvif pointer is really
227 * valid. We need to do this because sometimes mbufs will
228 * be allocated by other parts of the system that contain
229 * garbage in the rcvif pointer. Using the M_PROTO1 flag
230 * lets the driver perform a proper sanity check and avoid
231 * following potentially bogus rcvif pointers off into
232 * never-never land.
233 */
234 if (ifp->if_flags & IFF_LINK0) {
235 m->m_pkthdr.rcvif = ifp;
236 m->m_flags |= M_PROTO1;
237 } else {
238 M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
239 if (m == 0)
240 continue;
241 /* M_PREPEND takes care of m_len, m_pkthdr.len for us */
242
243 /*
244 * Transform the Ethernet header into an Ethernet header
245 * with 802.1Q encapsulation.
246 */
247 bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *),
248 sizeof(struct ether_header));
249 evl = mtod(m, struct ether_vlan_header *);
250 evl->evl_proto = evl->evl_encap_proto;
251 evl->evl_encap_proto = htons(vlan_proto);
252 evl->evl_tag = htons(ifv->ifv_tag);
253 #ifdef DEBUG
254 printf("vlan_start: %*D\n", sizeof *evl,
255 (char *)evl, ":");
256 #endif
257 }
258
259 /*
260 * Send it, precisely as ether_output() would have.
261 * We are already running at splimp.
262 */
263 if (IF_QFULL(&p->if_snd)) {
264 IF_DROP(&p->if_snd);
265 /* XXX stats */
266 ifp->if_oerrors++;
267 m_freem(m);
268 continue;
269 }
270 IF_ENQUEUE(&p->if_snd, m);
271 if ((p->if_flags & IFF_OACTIVE) == 0) {
272 p->if_start(p);
273 ifp->if_opackets++;
274 }
275 }
276 ifp->if_flags &= ~IFF_OACTIVE;
277 }
278
279 int
280 vlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t)
281 {
282 int i;
283 struct ifvlan *ifv;
284
285 for (i = 0; i < NVLAN; i++) {
286 ifv = &ifv_softc[i];
287 if (ifv->ifv_tag == t)
288 break;
289 }
290
291 if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
292 m_free(m);
293 return -1; /* So the parent can take note */
294 }
295
296 /*
297 * Having found a valid vlan interface corresponding to
298 * the given source interface and vlan tag, run the
299 * the real packet through ethert_input().
300 */
301 m->m_pkthdr.rcvif = &ifv->ifv_if;
302
303 if (ifv->ifv_if.if_bpf) {
304 /*
305 * Do the usual BPF fakery. Note that we don't support
306 * promiscuous mode here, since it would require the
307 * drivers to know about VLANs and we're not ready for
308 * that yet.
309 */
310 struct mbuf m0;
311 m0.m_next = m;
312 m0.m_len = sizeof(struct ether_header);
313 m0.m_data = (char *)eh;
314 bpf_mtap(&ifv->ifv_if, &m0);
315 }
316 ifv->ifv_if.if_ipackets++;
317 ether_input(&ifv->ifv_if, eh, m);
318 return 0;
319 }
320
321 int
322 vlan_input(struct ether_header *eh, struct mbuf *m)
323 {
324 int i;
325 struct ifvlan *ifv;
326
327 for (i = 0; i < NVLAN; i++) {
328 ifv = &ifv_softc[i];
329 if (m->m_pkthdr.rcvif == ifv->ifv_p
330 && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *)))
331 == ifv->ifv_tag))
332 break;
333 }
334
335 if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
336 m_freem(m);
337 return -1; /* so ether_input can take note */
338 }
339
340 /*
341 * Having found a valid vlan interface corresponding to
342 * the given source interface and vlan tag, remove the
343 * encapsulation, and run the real packet through
344 * ether_input() a second time (it had better be
345 * reentrant!).
346 */
347 m->m_pkthdr.rcvif = &ifv->ifv_if;
348 eh->ether_type = mtod(m, u_int16_t *)[1];
349 m->m_data += EVL_ENCAPLEN;
350 m->m_len -= EVL_ENCAPLEN;
351 m->m_pkthdr.len -= EVL_ENCAPLEN;
352
353 #if NBPFILTER > 0
354 if (ifv->ifv_if.if_bpf) {
355 /*
356 * Do the usual BPF fakery. Note that we don't support
357 * promiscuous mode here, since it would require the
358 * drivers to know about VLANs and we're not ready for
359 * that yet.
360 */
361 struct mbuf m0;
362 m0.m_next = m;
363 m0.m_len = sizeof(struct ether_header);
364 m0.m_data = (char *)eh;
365 bpf_mtap(&ifv->ifv_if, &m0);
366 }
367 #endif
368 ether_input(&ifv->ifv_if, eh, m);
369 return 0;
370 }
371
372 static int
373 vlan_config(struct ifvlan *ifv, struct ifnet *p)
374 {
375 struct ifaddr *ifa1, *ifa2;
376 struct sockaddr_dl *sdl1, *sdl2;
377
378 if (p->if_data.ifi_type != IFT_ETHER)
379 return EPROTONOSUPPORT;
380 if (ifv->ifv_p)
381 return EBUSY;
382 ifv->ifv_p = p;
383 if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header))
384 ifv->ifv_if.if_mtu = p->if_mtu;
385 else
386 ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN;
387
388 /*
389 * Preserve the state of the LINK0 flag for ourselves.
390 */
391 ifv->ifv_if.if_flags = (p->if_flags & ~(IFF_LINK0));
392
393 /*
394 * Set up our ``Ethernet address'' to reflect the underlying
395 * physical interface's.
396 */
397 ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1];
398 ifa2 = ifnet_addrs[p->if_index - 1];
399 sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
400 sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
401 sdl1->sdl_type = IFT_ETHER;
402 sdl1->sdl_alen = ETHER_ADDR_LEN;
403 bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
404 bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
405 return 0;
406 }
407
408 static int
409 vlan_unconfig(struct ifnet *ifp)
410 {
411 struct ifaddr *ifa;
412 struct sockaddr_dl *sdl;
413 struct vlan_mc_entry *mc;
414 struct ifvlan *ifv;
415 struct ifnet *p;
416 int error;
417
418 ifv = ifp->if_softc;
419 p = ifv->ifv_p;
420
421 /*
422 * Since the interface is being unconfigured, we need to
423 * empty the list of multicast groups that we may have joined
424 * while we were alive and remove them from the parent's list
425 * as well.
426 */
427 while(ifv->vlan_mc_listhead.slh_first != NULL) {
428 struct sockaddr_dl sdl;
429
430 sdl.sdl_len = ETHER_ADDR_LEN;
431 sdl.sdl_family = AF_LINK;
432 mc = ifv->vlan_mc_listhead.slh_first;
433 bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
434 error = if_delmulti(p, (struct sockaddr *)&sdl);
435 error = if_delmulti(ifp, (struct sockaddr *)&sdl);
436 if (error)
437 return(error);
438 SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
439 free(mc, M_DEVBUF);
440 }
441
442 /* Disconnect from parent. */
443 ifv->ifv_p = NULL;
444 ifv->ifv_if.if_mtu = ETHERMTU;
445
446 /* Clear our MAC address. */
447 ifa = ifnet_addrs[ifv->ifv_if.if_index - 1];
448 sdl = (struct sockaddr_dl *)ifa->ifa_addr;
449 sdl->sdl_type = IFT_ETHER;
450 sdl->sdl_alen = ETHER_ADDR_LEN;
451 bzero(LLADDR(sdl), ETHER_ADDR_LEN);
452 bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
453
454 return 0;
455 }
456
457 static int
458 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
459 {
460 struct ifaddr *ifa;
461 struct ifnet *p;
462 struct ifreq *ifr;
463 struct ifvlan *ifv;
464 struct vlanreq vlr;
465 int error = 0;
466
467 ifr = (struct ifreq *)data;
468 ifa = (struct ifaddr *)data;
469 ifv = ifp->if_softc;
470
471 switch (cmd) {
472 case SIOCSIFADDR:
473 ifp->if_flags |= IFF_UP;
474
475 switch (ifa->ifa_addr->sa_family) {
476 #ifdef INET
477 case AF_INET:
478 arp_ifinit(&ifv->ifv_ac, ifa);
479 break;
480 #endif
481 default:
482 break;
483 }
484 break;
485
486 case SIOCGIFADDR:
487 {
488 struct sockaddr *sa;
489
490 sa = (struct sockaddr *) &ifr->ifr_data;
491 bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
492 (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
493 }
494 break;
495
496 case SIOCSIFMTU:
497 /*
498 * Set the interface MTU.
499 * This is bogus. The underlying interface might support
500 * jumbo frames.
501 */
502 if (ifr->ifr_mtu > ETHERMTU) {
503 error = EINVAL;
504 } else {
505 ifp->if_mtu = ifr->ifr_mtu;
506 }
507 break;
508
509 case SIOCSETVLAN:
510 error = copyin(ifr->ifr_data, &vlr, sizeof vlr);
511 if (error)
512 break;
513 if (vlr.vlr_parent[0] == '\0') {
514 vlan_unconfig(ifp);
515 ifv->ifv_p = 0;
516 if_down(ifp);
517 ifp->if_flags &= ~(IFF_UP|IFF_RUNNING);
518 break;
519 }
520 p = ifunit(vlr.vlr_parent);
521 if (p == 0) {
522 error = ENOENT;
523 break;
524 }
525 error = vlan_config(ifv, p);
526 if (error)
527 break;
528 ifv->ifv_tag = vlr.vlr_tag;
529 ifp->if_flags |= IFF_RUNNING;
530 break;
531
532 case SIOCGETVLAN:
533 bzero(&vlr, sizeof vlr);
534 if (ifv->ifv_p) {
535 snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
536 "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit);
537 vlr.vlr_tag = ifv->ifv_tag;
538 }
539 error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
540 break;
541
542 case SIOCSIFFLAGS:
543 /*
544 * We don't support all-multicast or promiscuous modes
545 * right now because it would require help from the
546 * underlying drivers, which hasn't been implemented.
547 */
548 if (ifr->ifr_flags & (IFF_PROMISC|IFF_ALLMULTI)) {
549 ifp->if_flags &= ~(IFF_PROMISC|IFF_ALLMULTI);
550 error = EINVAL;
551 }
552 break;
553 case SIOCADDMULTI:
554 case SIOCDELMULTI:
555 error = vlan_setmulti(ifp);
556 break;
557 default:
558 error = EINVAL;
559 }
560 return error;
561 }
562
563 #endif /* NVLAN > 0 */
Cache object: 0d3d7dc365e34db5d518a35bd145e1f4
|