]>
git.saurik.com Git - apple/xnu.git/blob - bsd/net/if_vlan.c
e4a3585a5f452925e9d97a0d5bb27083d9cb2dfb
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright 1998 Massachusetts Institute of Technology
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
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
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.
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.
82 #include <sys/param.h>
83 #include <sys/kernel.h>
85 #include <sys/socket.h>
86 #include <sys/sockio.h>
87 #include <sys/sysctl.h>
88 #include <sys/systm.h>
93 #include <net/ethernet.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>
101 #include <netinet/in.h>
102 #include <netinet/if_ether.h>
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");
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");
113 static struct ifvlan ifv_softc
[NVLAN
];
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
);
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.
130 static int vlan_setmulti(struct ifnet
*ifp
)
133 struct ifmultiaddr
*ifma
, *rifma
= NULL
;
135 struct vlan_mc_entry
*mc
= NULL
;
136 struct sockaddr_dl sdl
;
139 /* Find the parent. */
143 sdl
.sdl_len
= ETHER_ADDR_LEN
;
144 sdl
.sdl_family
= AF_LINK
;
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
);
153 SLIST_REMOVE_HEAD(&sc
->vlan_mc_listhead
, mc_entries
);
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
)
162 mc
= _MALLOC(sizeof(struct vlan_mc_entry
), M_DEVBUF
, M_NOWAIT
);
163 bcopy(LLADDR((struct sockaddr_dl
*)ifma
->ifma_addr
),
164 (char *)&mc
->mc_addr
, ETHER_ADDR_LEN
);
165 SLIST_INSERT_HEAD(&sc
->vlan_mc_listhead
, mc
, mc_entries
);
166 error
= if_addmulti(ifp_p
, (struct sockaddr
*)&sdl
, &rifma
);
175 vlaninit(void *dummy
)
179 for (i
= 0; i
< NVLAN
; i
++) {
180 struct ifnet
*ifp
= &ifv_softc
[i
].ifv_if
;
182 ifp
->if_softc
= &ifv_softc
[i
];
183 ifp
->if_name
= "vlan";
184 ifp
->if_family
= APPLE_IF_FAM_VLAN
;
186 /* NB: flags are not set here */
187 ifp
->if_linkmib
= &ifv_softc
[i
].ifv_mib
;
188 ifp
->if_linkmiblen
= sizeof ifv_softc
[i
].ifv_mib
;
189 /* NB: mtu is not set here */
191 ifp
->if_init
= vlan_ifinit
;
192 ifp
->if_start
= vlan_start
;
193 ifp
->if_ioctl
= vlan_ioctl
;
194 ifp
->if_output
= ether_output
;
195 ifp
->if_snd
.ifq_maxlen
= ifqmaxlen
;
199 bpfattach(ifp
, DLT_EN10MB
, sizeof(struct ether_header
));
201 /* Now undo some of the damage... */
202 ifp
->if_data
.ifi_type
= IFT_8021_VLAN
;
203 ifp
->if_data
.ifi_hdrlen
= EVL_ENCAPLEN
;
204 ifp
->if_resolvemulti
= 0;
207 PSEUDO_SET(vlaninit
, if_vlan
);
210 vlan_ifinit(void *foo
)
216 vlan_start(struct ifnet
*ifp
)
220 struct ether_vlan_header
*evl
;
226 ifp
->if_flags
|= IFF_OACTIVE
;
228 IF_DEQUEUE(&ifp
->if_snd
, m
);
234 #endif /* NBPFILTER > 0 */
237 * If the LINK0 flag is set, it means the underlying interface
238 * can do VLAN tag insertion itself and doesn't require us to
239 * create a special header for it. In this case, we just pass
240 * the packet along. However, we need some way to tell the
241 * interface where the packet came from so that it knows how
242 * to find the VLAN tag to use, so we set the rcvif in the
243 * mbuf header to our ifnet.
245 * Note: we also set the M_PROTO1 flag in the mbuf to let
246 * the parent driver know that the rcvif pointer is really
247 * valid. We need to do this because sometimes mbufs will
248 * be allocated by other parts of the system that contain
249 * garbage in the rcvif pointer. Using the M_PROTO1 flag
250 * lets the driver perform a proper sanity check and avoid
251 * following potentially bogus rcvif pointers off into
254 if (ifp
->if_flags
& IFF_LINK0
) {
255 m
->m_pkthdr
.rcvif
= ifp
;
256 m
->m_flags
|= M_PROTO1
;
258 M_PREPEND(m
, EVL_ENCAPLEN
, M_DONTWAIT
);
261 /* M_PREPEND takes care of m_len, m_pkthdr.len for us */
264 * Transform the Ethernet header into an Ethernet header
265 * with 802.1Q encapsulation.
267 bcopy(mtod(m
, char *) + EVL_ENCAPLEN
, mtod(m
, char *),
268 sizeof(struct ether_header
));
269 evl
= mtod(m
, struct ether_vlan_header
*);
270 evl
->evl_proto
= evl
->evl_encap_proto
;
271 evl
->evl_encap_proto
= htons(vlan_proto
);
272 evl
->evl_tag
= htons(ifv
->ifv_tag
);
274 printf("vlan_start: %*D\n", sizeof *evl
,
280 * Send it, precisely as ether_output() would have.
281 * We are already running at splimp.
283 if (IF_QFULL(&p
->if_snd
)) {
290 IF_ENQUEUE(&p
->if_snd
, m
);
291 if ((p
->if_flags
& IFF_OACTIVE
) == 0) {
296 ifp
->if_flags
&= ~IFF_OACTIVE
;
302 vlan_input_tag(struct ether_header
*eh
, struct mbuf
*m
, u_int16_t t
)
307 for (i
= 0; i
< NVLAN
; i
++) {
309 if (ifv
->ifv_tag
== t
)
313 if (i
>= NVLAN
|| (ifv
->ifv_if
.if_flags
& IFF_UP
) == 0) {
315 ifv
->ifv_p
->if_data
.ifi_noproto
++;
320 * Having found a valid vlan interface corresponding to
321 * the given source interface and vlan tag, run the
322 * the real packet through ethert_input().
324 m
->m_pkthdr
.rcvif
= &ifv
->ifv_if
;
327 if (ifv
->ifv_if
.if_bpf
) {
329 * Do the usual BPF fakery. Note that we don't support
330 * promiscuous mode here, since it would require the
331 * drivers to know about VLANs and we're not ready for
336 m0
.m_len
= sizeof(struct ether_header
);
337 m0
.m_data
= (char *)eh
;
338 bpf_mtap(&ifv
->ifv_if
, &m0
);
341 ifv
->ifv_if
.if_ipackets
++;
342 ether_input(&ifv
->ifv_if
, eh
, m
);
347 vlan_input(struct ether_header
*eh
, struct mbuf
*m
)
352 for (i
= 0; i
< NVLAN
; i
++) {
354 if (m
->m_pkthdr
.rcvif
== ifv
->ifv_p
355 && (EVL_VLANOFTAG(ntohs(*mtod(m
, u_int16_t
*)))
360 if (i
>= NVLAN
|| (ifv
->ifv_if
.if_flags
& IFF_UP
) == 0) {
362 return -1; /* so ether_input can take note */
366 * Having found a valid vlan interface corresponding to
367 * the given source interface and vlan tag, remove the
368 * encapsulation, and run the real packet through
369 * ether_input() a second time (it had better be
372 m
->m_pkthdr
.rcvif
= &ifv
->ifv_if
;
373 eh
->ether_type
= mtod(m
, u_int16_t
*)[1];
374 m
->m_data
+= EVL_ENCAPLEN
;
375 m
->m_len
-= EVL_ENCAPLEN
;
376 m
->m_pkthdr
.len
-= EVL_ENCAPLEN
;
379 if (ifv
->ifv_if
.if_bpf
) {
381 * Do the usual BPF fakery. Note that we don't support
382 * promiscuous mode here, since it would require the
383 * drivers to know about VLANs and we're not ready for
388 m0
.m_len
= sizeof(struct ether_header
);
389 m0
.m_data
= (char *)eh
;
390 bpf_mtap(&ifv
->ifv_if
, &m0
);
393 ifv
->ifv_if
.if_ipackets
++;
394 ether_input(&ifv
->ifv_if
, eh
, m
);
399 vlan_config(struct ifvlan
*ifv
, struct ifnet
*p
)
401 struct ifaddr
*ifa1
, *ifa2
;
402 struct sockaddr_dl
*sdl1
, *sdl2
;
404 if (p
->if_data
.ifi_type
!= IFT_ETHER
)
405 return EPROTONOSUPPORT
;
409 if (p
->if_data
.ifi_hdrlen
== sizeof(struct ether_vlan_header
))
410 ifv
->ifv_if
.if_mtu
= p
->if_mtu
;
412 ifv
->ifv_if
.if_mtu
= p
->if_data
.ifi_mtu
- EVL_ENCAPLEN
;
415 * Preserve the state of the LINK0 flag for ourselves.
417 ifv
->ifv_if
.if_flags
= (p
->if_flags
& ~(IFF_LINK0
));
420 * Set up our ``Ethernet address'' to reflect the underlying
421 * physical interface's.
423 ifa1
= ifnet_addrs
[ifv
->ifv_if
.if_index
- 1];
424 ifa2
= ifnet_addrs
[p
->if_index
- 1];
425 sdl1
= (struct sockaddr_dl
*)ifa1
->ifa_addr
;
426 sdl2
= (struct sockaddr_dl
*)ifa2
->ifa_addr
;
427 sdl1
->sdl_type
= IFT_ETHER
;
428 sdl1
->sdl_alen
= ETHER_ADDR_LEN
;
429 bcopy(LLADDR(sdl2
), LLADDR(sdl1
), ETHER_ADDR_LEN
);
430 bcopy(LLADDR(sdl2
), ifv
->ifv_ac
.ac_enaddr
, ETHER_ADDR_LEN
);
435 vlan_unconfig(struct ifnet
*ifp
)
438 struct sockaddr_dl
*sdl
;
439 struct vlan_mc_entry
*mc
;
448 * Since the interface is being unconfigured, we need to
449 * empty the list of multicast groups that we may have joined
450 * while we were alive and remove them from the parent's list
453 while(ifv
->vlan_mc_listhead
.slh_first
!= NULL
) {
454 struct sockaddr_dl sdl
;
456 sdl
.sdl_len
= ETHER_ADDR_LEN
;
457 sdl
.sdl_family
= AF_LINK
;
458 mc
= ifv
->vlan_mc_listhead
.slh_first
;
459 bcopy((char *)&mc
->mc_addr
, LLADDR(&sdl
), ETHER_ADDR_LEN
);
460 error
= if_delmulti(p
, (struct sockaddr
*)&sdl
);
461 error
= if_delmulti(ifp
, (struct sockaddr
*)&sdl
);
464 SLIST_REMOVE_HEAD(&ifv
->vlan_mc_listhead
, mc_entries
);
468 /* Disconnect from parent. */
470 ifv
->ifv_if
.if_mtu
= ETHERMTU
;
472 /* Clear our MAC address. */
473 ifa
= ifnet_addrs
[ifv
->ifv_if
.if_index
- 1];
474 sdl
= (struct sockaddr_dl
*)ifa
->ifa_addr
;
475 sdl
->sdl_type
= IFT_ETHER
;
476 sdl
->sdl_alen
= ETHER_ADDR_LEN
;
477 bzero(LLADDR(sdl
), ETHER_ADDR_LEN
);
478 bzero(ifv
->ifv_ac
.ac_enaddr
, ETHER_ADDR_LEN
);
484 vlan_ioctl(struct ifnet
*ifp
, u_long cmd
, caddr_t data
)
493 ifr
= (struct ifreq
*)data
;
494 ifa
= (struct ifaddr
*)data
;
499 ifp
->if_flags
|= IFF_UP
;
501 switch (ifa
->ifa_addr
->sa_family
) {
504 arp_ifinit(&ifv
->ifv_ac
, ifa
);
516 sa
= (struct sockaddr
*) &ifr
->ifr_data
;
517 bcopy(((struct arpcom
*)ifp
->if_softc
)->ac_enaddr
,
518 (caddr_t
) sa
->sa_data
, ETHER_ADDR_LEN
);
524 * Set the interface MTU.
525 * This is bogus. The underlying interface might support
528 if (ifr
->ifr_mtu
> ETHERMTU
) {
531 ifp
->if_mtu
= ifr
->ifr_mtu
;
536 error
= copyin(ifr
->ifr_data
, &vlr
, sizeof vlr
);
539 if (vlr
.vlr_parent
[0] == '\0') {
545 p
= ifunit(vlr
.vlr_parent
);
550 error
= vlan_config(ifv
, p
);
553 ifv
->ifv_tag
= vlr
.vlr_tag
;
557 bzero(&vlr
, sizeof vlr
);
559 snprintf(vlr
.vlr_parent
, sizeof(vlr
.vlr_parent
),
560 "%s%d", ifv
->ifv_p
->if_name
, ifv
->ifv_p
->if_unit
);
561 vlr
.vlr_tag
= ifv
->ifv_tag
;
563 error
= copyout(&vlr
, ifr
->ifr_data
, sizeof vlr
);
568 * We don't support promiscuous mode
569 * right now because it would require help from the
570 * underlying drivers, which hasn't been implemented.
572 if (ifr
->ifr_flags
& (IFF_PROMISC
)) {
573 ifp
->if_flags
&= ~(IFF_PROMISC
);
579 error
= vlan_setmulti(ifp
);
587 #endif /* NVLAN > 0 */