]>
git.saurik.com Git - apple/xnu.git/blob - bsd/net/if_vlan.c
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * Copyright 1998 Massachusetts Institute of Technology
28 * Permission to use, copy, modify, and distribute this software and
29 * its documentation for any purpose and without fee is hereby
30 * granted, provided that both the above copyright notice and this
31 * permission notice appear in all copies, that both the above
32 * copyright notice and this permission notice appear in all
33 * supporting documentation, and that the name of M.I.T. not be used
34 * in advertising or publicity pertaining to distribution of the
35 * software without specific, written prior permission. M.I.T. makes
36 * no representations about the suitability of this software for any
37 * purpose. It is provided "as is" without express or implied
40 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
41 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
42 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
43 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
44 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
46 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
57 * Might be extended some day to also handle IEEE 802.1p priority
58 * tagging. This is sort of sneaky in the implementation, since
59 * we need to pretend to be enough of an Ethernet implementation
60 * to make arp work. The way we do this is by telling everyone
61 * that we are an Ethernet, and then catch the packets that
62 * ether_output() left on our output queue queue when it calls
63 * if_start(), rewrite them for use by the real outgoing interface,
64 * and ask it to send them.
67 * XXX It's incorrect to assume that we must always kludge up
68 * headers on the physical device's behalf: some devices support
69 * VLAN tag insersion and extraction in firmware. For these cases,
70 * one can change the behavior of the vlan interface by setting
71 * the LINK0 flag on it (that is setting the vlan interface's LINK0
72 * flag, _not_ the parent's LINK0 flag; we try to leave the parent
73 * alone). If the interface as the LINK0 flag set, then it will
74 * not modify the ethernet header on output because the parent
75 * can do that for itself. On input, the parent can call vlan_input_tag()
76 * directly in order to supply us with an incoming mbuf and the vlan
77 * tag value that goes with it.
85 #include <sys/param.h>
86 #include <sys/kernel.h>
88 #include <sys/socket.h>
89 #include <sys/sockio.h>
90 #include <sys/sysctl.h>
91 #include <sys/systm.h>
96 #include <net/ethernet.h>
98 #include <net/if_arp.h>
99 #include <net/if_dl.h>
100 #include <net/if_types.h>
101 #include <net/if_vlan_var.h>
104 #include <netinet/in.h>
105 #include <netinet/if_ether.h>
108 SYSCTL_DECL(_net_link
);
109 SYSCTL_NODE(_net_link
, IFT_8021_VLAN
, vlan
, CTLFLAG_RW
, 0, "IEEE 802.1Q VLAN");
110 SYSCTL_NODE(_net_link_vlan
, PF_LINK
, link
, CTLFLAG_RW
, 0, "for consistency");
112 u_int vlan_proto
= ETHERTYPE_VLAN
;
113 SYSCTL_INT(_net_link_vlan_link
, VLANCTL_PROTO
, proto
, CTLFLAG_RW
, &vlan_proto
,
114 0, "Ethernet protocol used for VLAN encapsulation");
116 static struct ifvlan ifv_softc
[NVLAN
];
118 static void vlan_start(struct ifnet
*ifp
);
119 static void vlan_ifinit(void *foo
);
120 static int vlan_ioctl(struct ifnet
*ifp
, u_long cmd
, caddr_t addr
);
121 static int vlan_setmulti(struct ifnet
*ifp
);
122 static int vlan_unconfig(struct ifnet
*ifp
);
123 static int vlan_config(struct ifvlan
*ifv
, struct ifnet
*p
);
126 * Program our multicast filter. What we're actually doing is
127 * programming the multicast filter of the parent. This has the
128 * side effect of causing the parent interface to receive multicast
129 * traffic that it doesn't really want, which ends up being discarded
130 * later by the upper protocol layers. Unfortunately, there's no way
131 * to avoid this: there really is only one physical interface.
133 static int vlan_setmulti(struct ifnet
*ifp
)
136 struct ifmultiaddr
*ifma
, *rifma
= NULL
;
138 struct vlan_mc_entry
*mc
= NULL
;
139 struct sockaddr_dl sdl
;
142 /* Find the parent. */
146 sdl
.sdl_len
= ETHER_ADDR_LEN
;
147 sdl
.sdl_family
= AF_LINK
;
149 /* First, remove any existing filter entries. */
150 while(sc
->vlan_mc_listhead
.slh_first
!= NULL
) {
151 mc
= sc
->vlan_mc_listhead
.slh_first
;
152 bcopy((char *)&mc
->mc_addr
, LLADDR(&sdl
), ETHER_ADDR_LEN
);
153 error
= if_delmulti(ifp_p
, (struct sockaddr
*)&sdl
);
156 SLIST_REMOVE_HEAD(&sc
->vlan_mc_listhead
, mc_entries
);
160 /* Now program new ones. */
161 for (ifma
= ifp
->if_multiaddrs
.lh_first
;
162 ifma
!= NULL
;ifma
= ifma
->ifma_link
.le_next
) {
163 if (ifma
->ifma_addr
->sa_family
!= AF_LINK
)
165 mc
= _MALLOC(sizeof(struct vlan_mc_entry
), M_DEVBUF
, M_WAITOK
);
168 bcopy(LLADDR((struct sockaddr_dl
*)ifma
->ifma_addr
),
169 (char *)&mc
->mc_addr
, ETHER_ADDR_LEN
);
170 SLIST_INSERT_HEAD(&sc
->vlan_mc_listhead
, mc
, mc_entries
);
171 error
= if_addmulti(ifp_p
, (struct sockaddr
*)&sdl
, &rifma
);
180 vlaninit(void *dummy
)
184 for (i
= 0; i
< NVLAN
; i
++) {
185 struct ifnet
*ifp
= &ifv_softc
[i
].ifv_if
;
187 ifp
->if_softc
= &ifv_softc
[i
];
188 ifp
->if_name
= "vlan";
189 ifp
->if_family
= APPLE_IF_FAM_VLAN
;
191 /* NB: flags are not set here */
192 ifp
->if_linkmib
= &ifv_softc
[i
].ifv_mib
;
193 ifp
->if_linkmiblen
= sizeof ifv_softc
[i
].ifv_mib
;
194 /* NB: mtu is not set here */
196 ifp
->if_init
= vlan_ifinit
;
197 ifp
->if_start
= vlan_start
;
198 ifp
->if_ioctl
= vlan_ioctl
;
199 ifp
->if_output
= ether_output
;
200 ifp
->if_snd
.ifq_maxlen
= ifqmaxlen
;
204 bpfattach(ifp
, DLT_EN10MB
, sizeof(struct ether_header
));
206 /* Now undo some of the damage... */
207 ifp
->if_data
.ifi_type
= IFT_8021_VLAN
;
208 ifp
->if_data
.ifi_hdrlen
= EVL_ENCAPLEN
;
209 ifp
->if_resolvemulti
= 0;
212 PSEUDO_SET(vlaninit
, if_vlan
);
215 vlan_ifinit(void *foo
)
221 vlan_start(struct ifnet
*ifp
)
225 struct ether_vlan_header
*evl
;
231 ifp
->if_flags
|= IFF_OACTIVE
;
233 IF_DEQUEUE(&ifp
->if_snd
, m
);
239 #endif /* NBPFILTER > 0 */
242 * If the LINK0 flag is set, it means the underlying interface
243 * can do VLAN tag insertion itself and doesn't require us to
244 * create a special header for it. In this case, we just pass
245 * the packet along. However, we need some way to tell the
246 * interface where the packet came from so that it knows how
247 * to find the VLAN tag to use, so we set the rcvif in the
248 * mbuf header to our ifnet.
250 * Note: we also set the M_PROTO1 flag in the mbuf to let
251 * the parent driver know that the rcvif pointer is really
252 * valid. We need to do this because sometimes mbufs will
253 * be allocated by other parts of the system that contain
254 * garbage in the rcvif pointer. Using the M_PROTO1 flag
255 * lets the driver perform a proper sanity check and avoid
256 * following potentially bogus rcvif pointers off into
259 if (ifp
->if_flags
& IFF_LINK0
) {
260 m
->m_pkthdr
.rcvif
= ifp
;
261 m
->m_flags
|= M_PROTO1
;
263 M_PREPEND(m
, EVL_ENCAPLEN
, M_DONTWAIT
);
266 /* M_PREPEND takes care of m_len, m_pkthdr.len for us */
269 * Transform the Ethernet header into an Ethernet header
270 * with 802.1Q encapsulation.
272 bcopy(mtod(m
, char *) + EVL_ENCAPLEN
, mtod(m
, char *),
273 sizeof(struct ether_header
));
274 evl
= mtod(m
, struct ether_vlan_header
*);
275 evl
->evl_proto
= evl
->evl_encap_proto
;
276 evl
->evl_encap_proto
= htons(vlan_proto
);
277 evl
->evl_tag
= htons(ifv
->ifv_tag
);
279 printf("vlan_start: %*D\n", sizeof *evl
,
285 * Send it, precisely as ether_output() would have.
286 * We are already running at splimp.
288 if (IF_QFULL(&p
->if_snd
)) {
295 IF_ENQUEUE(&p
->if_snd
, m
);
296 if ((p
->if_flags
& IFF_OACTIVE
) == 0) {
301 ifp
->if_flags
&= ~IFF_OACTIVE
;
307 vlan_input_tag(struct ether_header
*eh
, struct mbuf
*m
, u_int16_t t
)
312 for (i
= 0; i
< NVLAN
; i
++) {
314 if (ifv
->ifv_tag
== t
)
318 if (i
>= NVLAN
|| (ifv
->ifv_if
.if_flags
& IFF_UP
) == 0) {
320 ifv
->ifv_p
->if_data
.ifi_noproto
++;
325 * Having found a valid vlan interface corresponding to
326 * the given source interface and vlan tag, run the
327 * the real packet through ethert_input().
329 m
->m_pkthdr
.rcvif
= &ifv
->ifv_if
;
332 if (ifv
->ifv_if
.if_bpf
) {
334 * Do the usual BPF fakery. Note that we don't support
335 * promiscuous mode here, since it would require the
336 * drivers to know about VLANs and we're not ready for
341 m0
.m_len
= sizeof(struct ether_header
);
342 m0
.m_data
= (char *)eh
;
343 bpf_mtap(&ifv
->ifv_if
, &m0
);
346 ifv
->ifv_if
.if_ipackets
++;
347 ether_input(&ifv
->ifv_if
, eh
, m
);
352 vlan_input(struct ether_header
*eh
, struct mbuf
*m
)
357 for (i
= 0; i
< NVLAN
; i
++) {
359 if (m
->m_pkthdr
.rcvif
== ifv
->ifv_p
360 && (EVL_VLANOFTAG(ntohs(*mtod(m
, u_int16_t
*)))
365 if (i
>= NVLAN
|| (ifv
->ifv_if
.if_flags
& IFF_UP
) == 0) {
367 return -1; /* so ether_input can take note */
371 * Having found a valid vlan interface corresponding to
372 * the given source interface and vlan tag, remove the
373 * encapsulation, and run the real packet through
374 * ether_input() a second time (it had better be
377 m
->m_pkthdr
.rcvif
= &ifv
->ifv_if
;
378 eh
->ether_type
= mtod(m
, u_int16_t
*)[1];
379 m
->m_data
+= EVL_ENCAPLEN
;
380 m
->m_len
-= EVL_ENCAPLEN
;
381 m
->m_pkthdr
.len
-= EVL_ENCAPLEN
;
384 if (ifv
->ifv_if
.if_bpf
) {
386 * Do the usual BPF fakery. Note that we don't support
387 * promiscuous mode here, since it would require the
388 * drivers to know about VLANs and we're not ready for
393 m0
.m_len
= sizeof(struct ether_header
);
394 m0
.m_data
= (char *)eh
;
395 bpf_mtap(&ifv
->ifv_if
, &m0
);
398 ifv
->ifv_if
.if_ipackets
++;
399 ether_input(&ifv
->ifv_if
, eh
, m
);
404 vlan_config(struct ifvlan
*ifv
, struct ifnet
*p
)
406 struct ifaddr
*ifa1
, *ifa2
;
407 struct sockaddr_dl
*sdl1
, *sdl2
;
409 if (p
->if_data
.ifi_type
!= IFT_ETHER
)
410 return EPROTONOSUPPORT
;
414 if (p
->if_data
.ifi_hdrlen
== sizeof(struct ether_vlan_header
))
415 ifv
->ifv_if
.if_mtu
= p
->if_mtu
;
417 ifv
->ifv_if
.if_mtu
= p
->if_data
.ifi_mtu
- EVL_ENCAPLEN
;
420 * Preserve the state of the LINK0 flag for ourselves.
422 ifv
->ifv_if
.if_flags
= (p
->if_flags
& ~(IFF_LINK0
));
425 * Set up our ``Ethernet address'' to reflect the underlying
426 * physical interface's.
428 ifa1
= ifnet_addrs
[ifv
->ifv_if
.if_index
- 1];
429 ifa2
= ifnet_addrs
[p
->if_index
- 1];
430 sdl1
= (struct sockaddr_dl
*)ifa1
->ifa_addr
;
431 sdl2
= (struct sockaddr_dl
*)ifa2
->ifa_addr
;
432 sdl1
->sdl_type
= IFT_ETHER
;
433 sdl1
->sdl_alen
= ETHER_ADDR_LEN
;
434 bcopy(LLADDR(sdl2
), LLADDR(sdl1
), ETHER_ADDR_LEN
);
435 bcopy(LLADDR(sdl2
), ifv
->ifv_ac
.ac_enaddr
, ETHER_ADDR_LEN
);
440 vlan_unconfig(struct ifnet
*ifp
)
443 struct sockaddr_dl
*sdl
;
444 struct vlan_mc_entry
*mc
;
453 * Since the interface is being unconfigured, we need to
454 * empty the list of multicast groups that we may have joined
455 * while we were alive and remove them from the parent's list
458 while(ifv
->vlan_mc_listhead
.slh_first
!= NULL
) {
459 struct sockaddr_dl sdl
;
461 sdl
.sdl_len
= ETHER_ADDR_LEN
;
462 sdl
.sdl_family
= AF_LINK
;
463 mc
= ifv
->vlan_mc_listhead
.slh_first
;
464 bcopy((char *)&mc
->mc_addr
, LLADDR(&sdl
), ETHER_ADDR_LEN
);
465 error
= if_delmulti(p
, (struct sockaddr
*)&sdl
);
466 error
= if_delmulti(ifp
, (struct sockaddr
*)&sdl
);
469 SLIST_REMOVE_HEAD(&ifv
->vlan_mc_listhead
, mc_entries
);
473 /* Disconnect from parent. */
475 ifv
->ifv_if
.if_mtu
= ETHERMTU
;
477 /* Clear our MAC address. */
478 ifa
= ifnet_addrs
[ifv
->ifv_if
.if_index
- 1];
479 sdl
= (struct sockaddr_dl
*)ifa
->ifa_addr
;
480 sdl
->sdl_type
= IFT_ETHER
;
481 sdl
->sdl_alen
= ETHER_ADDR_LEN
;
482 bzero(LLADDR(sdl
), ETHER_ADDR_LEN
);
483 bzero(ifv
->ifv_ac
.ac_enaddr
, ETHER_ADDR_LEN
);
489 vlan_ioctl(struct ifnet
*ifp
, u_long cmd
, caddr_t data
)
498 ifr
= (struct ifreq
*)data
;
499 ifa
= (struct ifaddr
*)data
;
504 ifp
->if_flags
|= IFF_UP
;
506 switch (ifa
->ifa_addr
->sa_family
) {
509 arp_ifinit(&ifv
->ifv_ac
, ifa
);
521 sa
= (struct sockaddr
*) &ifr
->ifr_data
;
522 bcopy(((struct arpcom
*)ifp
->if_softc
)->ac_enaddr
,
523 (caddr_t
) sa
->sa_data
, ETHER_ADDR_LEN
);
529 * Set the interface MTU.
530 * This is bogus. The underlying interface might support
533 if (ifr
->ifr_mtu
> ETHERMTU
) {
536 ifp
->if_mtu
= ifr
->ifr_mtu
;
541 error
= copyin(ifr
->ifr_data
, &vlr
, sizeof vlr
);
544 if (vlr
.vlr_parent
[0] == '\0') {
550 p
= ifunit(vlr
.vlr_parent
);
555 error
= vlan_config(ifv
, p
);
558 ifv
->ifv_tag
= vlr
.vlr_tag
;
562 bzero(&vlr
, sizeof vlr
);
564 snprintf(vlr
.vlr_parent
, sizeof(vlr
.vlr_parent
),
565 "%s%d", ifv
->ifv_p
->if_name
, ifv
->ifv_p
->if_unit
);
566 vlr
.vlr_tag
= ifv
->ifv_tag
;
568 error
= copyout(&vlr
, ifr
->ifr_data
, sizeof vlr
);
573 * We don't support promiscuous mode
574 * right now because it would require help from the
575 * underlying drivers, which hasn't been implemented.
577 if (ifr
->ifr_flags
& (IFF_PROMISC
)) {
578 ifp
->if_flags
&= ~(IFF_PROMISC
);
584 error
= vlan_setmulti(ifp
);
592 #endif /* NVLAN > 0 */