]> git.saurik.com Git - apple/xnu.git/blob - bsd/net/if_vlan.c
xnu-201.tar.gz
[apple/xnu.git] / bsd / net / if_vlan.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * Copyright 1998 Massachusetts Institute of Technology
24 *
25 * Permission to use, copy, modify, and distribute this software and
26 * its documentation for any purpose and without fee is hereby
27 * granted, provided that both the above copyright notice and this
28 * permission notice appear in all copies, that both the above
29 * copyright notice and this permission notice appear in all
30 * supporting documentation, and that the name of M.I.T. not be used
31 * in advertising or publicity pertaining to distribution of the
32 * software without specific, written prior permission. M.I.T. makes
33 * no representations about the suitability of this software for any
34 * purpose. It is provided "as is" without express or implied
35 * warranty.
36 *
37 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
38 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
39 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
40 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
41 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 *
50 */
51
52 /*
53 * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
54 * Might be extended some day to also handle IEEE 802.1p priority
55 * tagging. This is sort of sneaky in the implementation, since
56 * we need to pretend to be enough of an Ethernet implementation
57 * to make arp work. The way we do this is by telling everyone
58 * that we are an Ethernet, and then catch the packets that
59 * ether_output() left on our output queue queue when it calls
60 * if_start(), rewrite them for use by the real outgoing interface,
61 * and ask it to send them.
62 *
63 *
64 * XXX It's incorrect to assume that we must always kludge up
65 * headers on the physical device's behalf: some devices support
66 * VLAN tag insersion and extraction in firmware. For these cases,
67 * one can change the behavior of the vlan interface by setting
68 * the LINK0 flag on it (that is setting the vlan interface's LINK0
69 * flag, _not_ the parent's LINK0 flag; we try to leave the parent
70 * alone). If the interface as the LINK0 flag set, then it will
71 * not modify the ethernet header on output because the parent
72 * can do that for itself. On input, the parent can call vlan_input_tag()
73 * directly in order to supply us with an incoming mbuf and the vlan
74 * tag value that goes with it.
75 */
76
77 #include "vlan.h"
78 #if NVLAN > 0
79 #include "opt_inet.h"
80 #include "bpfilter.h"
81
82 #include <sys/param.h>
83 #include <sys/kernel.h>
84 #include <sys/mbuf.h>
85 #include <sys/socket.h>
86 #include <sys/sockio.h>
87 #include <sys/sysctl.h>
88 #include <sys/systm.h>
89
90 #if NBPFILTER > 0
91 #include <net/bpf.h>
92 #endif
93 #include <net/ethernet.h>
94 #include <net/if.h>
95 #include <net/if_arp.h>
96 #include <net/if_dl.h>
97 #include <net/if_types.h>
98 #include <net/if_vlan_var.h>
99
100 #if INET
101 #include <netinet/in.h>
102 #include <netinet/if_ether.h>
103 #endif
104
105 SYSCTL_DECL(_net_link);
106 SYSCTL_NODE(_net_link, IFT_8021_VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN");
107 SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency");
108
109 u_int vlan_proto = ETHERTYPE_VLAN;
110 SYSCTL_INT(_net_link_vlan_link, VLANCTL_PROTO, proto, CTLFLAG_RW, &vlan_proto,
111 0, "Ethernet protocol used for VLAN encapsulation");
112
113 static struct ifvlan ifv_softc[NVLAN];
114
115 static void vlan_start(struct ifnet *ifp);
116 static void vlan_ifinit(void *foo);
117 static int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
118 static int vlan_setmulti(struct ifnet *ifp);
119 static int vlan_unconfig(struct ifnet *ifp);
120 static int vlan_config(struct ifvlan *ifv, struct ifnet *p);
121
122 /*
123 * Program our multicast filter. What we're actually doing is
124 * programming the multicast filter of the parent. This has the
125 * side effect of causing the parent interface to receive multicast
126 * traffic that it doesn't really want, which ends up being discarded
127 * later by the upper protocol layers. Unfortunately, there's no way
128 * to avoid this: there really is only one physical interface.
129 */
130 static int vlan_setmulti(struct ifnet *ifp)
131 {
132 struct ifnet *ifp_p;
133 struct ifmultiaddr *ifma, *rifma = NULL;
134 struct ifvlan *sc;
135 struct vlan_mc_entry *mc = NULL;
136 struct sockaddr_dl sdl;
137 int error;
138
139 /* Find the parent. */
140 sc = ifp->if_softc;
141 ifp_p = sc->ifv_p;
142
143 sdl.sdl_len = ETHER_ADDR_LEN;
144 sdl.sdl_family = AF_LINK;
145
146 /* First, remove any existing filter entries. */
147 while(sc->vlan_mc_listhead.slh_first != NULL) {
148 mc = sc->vlan_mc_listhead.slh_first;
149 bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
150 error = if_delmulti(ifp_p, (struct sockaddr *)&sdl);
151 if (error)
152 return(error);
153 SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries);
154 FREE(mc, M_DEVBUF);
155 }
156
157 /* Now program new ones. */
158 for (ifma = ifp->if_multiaddrs.lh_first;
159 ifma != NULL;ifma = ifma->ifma_link.le_next) {
160 if (ifma->ifma_addr->sa_family != AF_LINK)
161 continue;
162 mc = _MALLOC(sizeof(struct vlan_mc_entry), M_DEVBUF, M_WAITOK);
163 if (mc == NULL)
164 return (ENOMEM);
165 bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
166 (char *)&mc->mc_addr, ETHER_ADDR_LEN);
167 SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries);
168 error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, &rifma);
169 if (error)
170 return(error);
171 }
172
173 return(0);
174 }
175
176 static void
177 vlaninit(void *dummy)
178 {
179 int i;
180
181 for (i = 0; i < NVLAN; i++) {
182 struct ifnet *ifp = &ifv_softc[i].ifv_if;
183
184 ifp->if_softc = &ifv_softc[i];
185 ifp->if_name = "vlan";
186 ifp->if_family = APPLE_IF_FAM_VLAN;
187 ifp->if_unit = i;
188 /* NB: flags are not set here */
189 ifp->if_linkmib = &ifv_softc[i].ifv_mib;
190 ifp->if_linkmiblen = sizeof ifv_softc[i].ifv_mib;
191 /* NB: mtu is not set here */
192
193 ifp->if_init = vlan_ifinit;
194 ifp->if_start = vlan_start;
195 ifp->if_ioctl = vlan_ioctl;
196 ifp->if_output = ether_output;
197 ifp->if_snd.ifq_maxlen = ifqmaxlen;
198 if_attach(ifp);
199 ether_ifattach(ifp);
200 #if NBPFILTER > 0
201 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
202 #endif
203 /* Now undo some of the damage... */
204 ifp->if_data.ifi_type = IFT_8021_VLAN;
205 ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN;
206 ifp->if_resolvemulti = 0;
207 }
208 }
209 PSEUDO_SET(vlaninit, if_vlan);
210
211 static void
212 vlan_ifinit(void *foo)
213 {
214 return;
215 }
216
217 static void
218 vlan_start(struct ifnet *ifp)
219 {
220 struct ifvlan *ifv;
221 struct ifnet *p;
222 struct ether_vlan_header *evl;
223 struct mbuf *m;
224
225 ifv = ifp->if_softc;
226 p = ifv->ifv_p;
227
228 ifp->if_flags |= IFF_OACTIVE;
229 for (;;) {
230 IF_DEQUEUE(&ifp->if_snd, m);
231 if (m == 0)
232 break;
233 #if NBPFILTER > 0
234 if (ifp->if_bpf)
235 bpf_mtap(ifp, m);
236 #endif /* NBPFILTER > 0 */
237
238 /*
239 * If the LINK0 flag is set, it means the underlying interface
240 * can do VLAN tag insertion itself and doesn't require us to
241 * create a special header for it. In this case, we just pass
242 * the packet along. However, we need some way to tell the
243 * interface where the packet came from so that it knows how
244 * to find the VLAN tag to use, so we set the rcvif in the
245 * mbuf header to our ifnet.
246 *
247 * Note: we also set the M_PROTO1 flag in the mbuf to let
248 * the parent driver know that the rcvif pointer is really
249 * valid. We need to do this because sometimes mbufs will
250 * be allocated by other parts of the system that contain
251 * garbage in the rcvif pointer. Using the M_PROTO1 flag
252 * lets the driver perform a proper sanity check and avoid
253 * following potentially bogus rcvif pointers off into
254 * never-never land.
255 */
256 if (ifp->if_flags & IFF_LINK0) {
257 m->m_pkthdr.rcvif = ifp;
258 m->m_flags |= M_PROTO1;
259 } else {
260 M_PREPEND(m, EVL_ENCAPLEN, M_DONTWAIT);
261 if (m == 0)
262 continue;
263 /* M_PREPEND takes care of m_len, m_pkthdr.len for us */
264
265 /*
266 * Transform the Ethernet header into an Ethernet header
267 * with 802.1Q encapsulation.
268 */
269 bcopy(mtod(m, char *) + EVL_ENCAPLEN, mtod(m, char *),
270 sizeof(struct ether_header));
271 evl = mtod(m, struct ether_vlan_header *);
272 evl->evl_proto = evl->evl_encap_proto;
273 evl->evl_encap_proto = htons(vlan_proto);
274 evl->evl_tag = htons(ifv->ifv_tag);
275 #ifdef DEBUG
276 printf("vlan_start: %*D\n", sizeof *evl,
277 (char *)evl, ":");
278 #endif
279 }
280
281 /*
282 * Send it, precisely as ether_output() would have.
283 * We are already running at splimp.
284 */
285 if (IF_QFULL(&p->if_snd)) {
286 IF_DROP(&p->if_snd);
287 /* XXX stats */
288 ifp->if_oerrors++;
289 m_freem(m);
290 continue;
291 }
292 IF_ENQUEUE(&p->if_snd, m);
293 if ((p->if_flags & IFF_OACTIVE) == 0) {
294 p->if_start(p);
295 ifp->if_opackets++;
296 }
297 }
298 ifp->if_flags &= ~IFF_OACTIVE;
299
300 return;
301 }
302
303 void
304 vlan_input_tag(struct ether_header *eh, struct mbuf *m, u_int16_t t)
305 {
306 int i;
307 struct ifvlan *ifv;
308
309 for (i = 0; i < NVLAN; i++) {
310 ifv = &ifv_softc[i];
311 if (ifv->ifv_tag == t)
312 break;
313 }
314
315 if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
316 m_freem(m);
317 ifv->ifv_p->if_data.ifi_noproto++;
318 return;
319 }
320
321 /*
322 * Having found a valid vlan interface corresponding to
323 * the given source interface and vlan tag, run the
324 * the real packet through ethert_input().
325 */
326 m->m_pkthdr.rcvif = &ifv->ifv_if;
327
328 #if NBPFILTER > 0
329 if (ifv->ifv_if.if_bpf) {
330 /*
331 * Do the usual BPF fakery. Note that we don't support
332 * promiscuous mode here, since it would require the
333 * drivers to know about VLANs and we're not ready for
334 * that yet.
335 */
336 struct mbuf m0;
337 m0.m_next = m;
338 m0.m_len = sizeof(struct ether_header);
339 m0.m_data = (char *)eh;
340 bpf_mtap(&ifv->ifv_if, &m0);
341 }
342 #endif
343 ifv->ifv_if.if_ipackets++;
344 ether_input(&ifv->ifv_if, eh, m);
345 return;
346 }
347
348 int
349 vlan_input(struct ether_header *eh, struct mbuf *m)
350 {
351 int i;
352 struct ifvlan *ifv;
353
354 for (i = 0; i < NVLAN; i++) {
355 ifv = &ifv_softc[i];
356 if (m->m_pkthdr.rcvif == ifv->ifv_p
357 && (EVL_VLANOFTAG(ntohs(*mtod(m, u_int16_t *)))
358 == ifv->ifv_tag))
359 break;
360 }
361
362 if (i >= NVLAN || (ifv->ifv_if.if_flags & IFF_UP) == 0) {
363 m_freem(m);
364 return -1; /* so ether_input can take note */
365 }
366
367 /*
368 * Having found a valid vlan interface corresponding to
369 * the given source interface and vlan tag, remove the
370 * encapsulation, and run the real packet through
371 * ether_input() a second time (it had better be
372 * reentrant!).
373 */
374 m->m_pkthdr.rcvif = &ifv->ifv_if;
375 eh->ether_type = mtod(m, u_int16_t *)[1];
376 m->m_data += EVL_ENCAPLEN;
377 m->m_len -= EVL_ENCAPLEN;
378 m->m_pkthdr.len -= EVL_ENCAPLEN;
379
380 #if NBPFILTER > 0
381 if (ifv->ifv_if.if_bpf) {
382 /*
383 * Do the usual BPF fakery. Note that we don't support
384 * promiscuous mode here, since it would require the
385 * drivers to know about VLANs and we're not ready for
386 * that yet.
387 */
388 struct mbuf m0;
389 m0.m_next = m;
390 m0.m_len = sizeof(struct ether_header);
391 m0.m_data = (char *)eh;
392 bpf_mtap(&ifv->ifv_if, &m0);
393 }
394 #endif
395 ifv->ifv_if.if_ipackets++;
396 ether_input(&ifv->ifv_if, eh, m);
397 return 0;
398 }
399
400 static int
401 vlan_config(struct ifvlan *ifv, struct ifnet *p)
402 {
403 struct ifaddr *ifa1, *ifa2;
404 struct sockaddr_dl *sdl1, *sdl2;
405
406 if (p->if_data.ifi_type != IFT_ETHER)
407 return EPROTONOSUPPORT;
408 if (ifv->ifv_p)
409 return EBUSY;
410 ifv->ifv_p = p;
411 if (p->if_data.ifi_hdrlen == sizeof(struct ether_vlan_header))
412 ifv->ifv_if.if_mtu = p->if_mtu;
413 else
414 ifv->ifv_if.if_mtu = p->if_data.ifi_mtu - EVL_ENCAPLEN;
415
416 /*
417 * Preserve the state of the LINK0 flag for ourselves.
418 */
419 ifv->ifv_if.if_flags = (p->if_flags & ~(IFF_LINK0));
420
421 /*
422 * Set up our ``Ethernet address'' to reflect the underlying
423 * physical interface's.
424 */
425 ifa1 = ifnet_addrs[ifv->ifv_if.if_index - 1];
426 ifa2 = ifnet_addrs[p->if_index - 1];
427 sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
428 sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
429 sdl1->sdl_type = IFT_ETHER;
430 sdl1->sdl_alen = ETHER_ADDR_LEN;
431 bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
432 bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
433 return 0;
434 }
435
436 static int
437 vlan_unconfig(struct ifnet *ifp)
438 {
439 struct ifaddr *ifa;
440 struct sockaddr_dl *sdl;
441 struct vlan_mc_entry *mc;
442 struct ifvlan *ifv;
443 struct ifnet *p;
444 int error;
445
446 ifv = ifp->if_softc;
447 p = ifv->ifv_p;
448
449 /*
450 * Since the interface is being unconfigured, we need to
451 * empty the list of multicast groups that we may have joined
452 * while we were alive and remove them from the parent's list
453 * as well.
454 */
455 while(ifv->vlan_mc_listhead.slh_first != NULL) {
456 struct sockaddr_dl sdl;
457
458 sdl.sdl_len = ETHER_ADDR_LEN;
459 sdl.sdl_family = AF_LINK;
460 mc = ifv->vlan_mc_listhead.slh_first;
461 bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN);
462 error = if_delmulti(p, (struct sockaddr *)&sdl);
463 error = if_delmulti(ifp, (struct sockaddr *)&sdl);
464 if (error)
465 return(error);
466 SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries);
467 FREE(mc, M_DEVBUF);
468 }
469
470 /* Disconnect from parent. */
471 ifv->ifv_p = NULL;
472 ifv->ifv_if.if_mtu = ETHERMTU;
473
474 /* Clear our MAC address. */
475 ifa = ifnet_addrs[ifv->ifv_if.if_index - 1];
476 sdl = (struct sockaddr_dl *)ifa->ifa_addr;
477 sdl->sdl_type = IFT_ETHER;
478 sdl->sdl_alen = ETHER_ADDR_LEN;
479 bzero(LLADDR(sdl), ETHER_ADDR_LEN);
480 bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN);
481
482 return 0;
483 }
484
485 static int
486 vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
487 {
488 struct ifaddr *ifa;
489 struct ifnet *p;
490 struct ifreq *ifr;
491 struct ifvlan *ifv;
492 struct vlanreq vlr;
493 int error = 0;
494
495 ifr = (struct ifreq *)data;
496 ifa = (struct ifaddr *)data;
497 ifv = ifp->if_softc;
498
499 switch (cmd) {
500 case SIOCSIFADDR:
501 ifp->if_flags |= IFF_UP;
502
503 switch (ifa->ifa_addr->sa_family) {
504 #if INET
505 case AF_INET:
506 arp_ifinit(&ifv->ifv_ac, ifa);
507 break;
508 #endif
509 default:
510 break;
511 }
512 break;
513
514 case SIOCGIFADDR:
515 {
516 struct sockaddr *sa;
517
518 sa = (struct sockaddr *) &ifr->ifr_data;
519 bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
520 (caddr_t) sa->sa_data, ETHER_ADDR_LEN);
521 }
522 break;
523
524 case SIOCSIFMTU:
525 /*
526 * Set the interface MTU.
527 * This is bogus. The underlying interface might support
528 * jumbo frames.
529 */
530 if (ifr->ifr_mtu > ETHERMTU) {
531 error = EINVAL;
532 } else {
533 ifp->if_mtu = ifr->ifr_mtu;
534 }
535 break;
536
537 case SIOCSETVLAN:
538 error = copyin(ifr->ifr_data, &vlr, sizeof vlr);
539 if (error)
540 break;
541 if (vlr.vlr_parent[0] == '\0') {
542 vlan_unconfig(ifp);
543 if_down(ifp);
544 ifp->if_flags = 0;
545 break;
546 }
547 p = ifunit(vlr.vlr_parent);
548 if (p == 0) {
549 error = ENOENT;
550 break;
551 }
552 error = vlan_config(ifv, p);
553 if (error)
554 break;
555 ifv->ifv_tag = vlr.vlr_tag;
556 break;
557
558 case SIOCGETVLAN:
559 bzero(&vlr, sizeof vlr);
560 if (ifv->ifv_p) {
561 snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent),
562 "%s%d", ifv->ifv_p->if_name, ifv->ifv_p->if_unit);
563 vlr.vlr_tag = ifv->ifv_tag;
564 }
565 error = copyout(&vlr, ifr->ifr_data, sizeof vlr);
566 break;
567
568 case SIOCSIFFLAGS:
569 /*
570 * We don't support promiscuous mode
571 * right now because it would require help from the
572 * underlying drivers, which hasn't been implemented.
573 */
574 if (ifr->ifr_flags & (IFF_PROMISC)) {
575 ifp->if_flags &= ~(IFF_PROMISC);
576 error = EINVAL;
577 }
578 break;
579 case SIOCADDMULTI:
580 case SIOCDELMULTI:
581 error = vlan_setmulti(ifp);
582 break;
583 default:
584 error = EINVAL;
585 }
586 return error;
587 }
588
589 #endif /* NVLAN > 0 */