]>
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 * 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_WAITOK
);
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
);
177 vlaninit(void *dummy
)
181 for (i
= 0; i
< NVLAN
; i
++) {
182 struct ifnet
*ifp
= &ifv_softc
[i
].ifv_if
;
184 ifp
->if_softc
= &ifv_softc
[i
];
185 ifp
->if_name
= "vlan";
186 ifp
->if_family
= APPLE_IF_FAM_VLAN
;
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 */
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
;
201 bpfattach(ifp
, DLT_EN10MB
, sizeof(struct ether_header
));
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;
209 PSEUDO_SET(vlaninit
, if_vlan
);
212 vlan_ifinit(void *foo
)
218 vlan_start(struct ifnet
*ifp
)
222 struct ether_vlan_header
*evl
;
228 ifp
->if_flags
|= IFF_OACTIVE
;
230 IF_DEQUEUE(&ifp
->if_snd
, m
);
236 #endif /* NBPFILTER > 0 */
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.
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
256 if (ifp
->if_flags
& IFF_LINK0
) {
257 m
->m_pkthdr
.rcvif
= ifp
;
258 m
->m_flags
|= M_PROTO1
;
260 M_PREPEND(m
, EVL_ENCAPLEN
, M_DONTWAIT
);
263 /* M_PREPEND takes care of m_len, m_pkthdr.len for us */
266 * Transform the Ethernet header into an Ethernet header
267 * with 802.1Q encapsulation.
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
);
276 printf("vlan_start: %*D\n", sizeof *evl
,
282 * Send it, precisely as ether_output() would have.
283 * We are already running at splimp.
285 if (IF_QFULL(&p
->if_snd
)) {
292 IF_ENQUEUE(&p
->if_snd
, m
);
293 if ((p
->if_flags
& IFF_OACTIVE
) == 0) {
298 ifp
->if_flags
&= ~IFF_OACTIVE
;
304 vlan_input_tag(struct ether_header
*eh
, struct mbuf
*m
, u_int16_t t
)
309 for (i
= 0; i
< NVLAN
; i
++) {
311 if (ifv
->ifv_tag
== t
)
315 if (i
>= NVLAN
|| (ifv
->ifv_if
.if_flags
& IFF_UP
) == 0) {
317 ifv
->ifv_p
->if_data
.ifi_noproto
++;
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().
326 m
->m_pkthdr
.rcvif
= &ifv
->ifv_if
;
329 if (ifv
->ifv_if
.if_bpf
) {
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
338 m0
.m_len
= sizeof(struct ether_header
);
339 m0
.m_data
= (char *)eh
;
340 bpf_mtap(&ifv
->ifv_if
, &m0
);
343 ifv
->ifv_if
.if_ipackets
++;
344 ether_input(&ifv
->ifv_if
, eh
, m
);
349 vlan_input(struct ether_header
*eh
, struct mbuf
*m
)
354 for (i
= 0; i
< NVLAN
; i
++) {
356 if (m
->m_pkthdr
.rcvif
== ifv
->ifv_p
357 && (EVL_VLANOFTAG(ntohs(*mtod(m
, u_int16_t
*)))
362 if (i
>= NVLAN
|| (ifv
->ifv_if
.if_flags
& IFF_UP
) == 0) {
364 return -1; /* so ether_input can take note */
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
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
;
381 if (ifv
->ifv_if
.if_bpf
) {
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
390 m0
.m_len
= sizeof(struct ether_header
);
391 m0
.m_data
= (char *)eh
;
392 bpf_mtap(&ifv
->ifv_if
, &m0
);
395 ifv
->ifv_if
.if_ipackets
++;
396 ether_input(&ifv
->ifv_if
, eh
, m
);
401 vlan_config(struct ifvlan
*ifv
, struct ifnet
*p
)
403 struct ifaddr
*ifa1
, *ifa2
;
404 struct sockaddr_dl
*sdl1
, *sdl2
;
406 if (p
->if_data
.ifi_type
!= IFT_ETHER
)
407 return EPROTONOSUPPORT
;
411 if (p
->if_data
.ifi_hdrlen
== sizeof(struct ether_vlan_header
))
412 ifv
->ifv_if
.if_mtu
= p
->if_mtu
;
414 ifv
->ifv_if
.if_mtu
= p
->if_data
.ifi_mtu
- EVL_ENCAPLEN
;
417 * Preserve the state of the LINK0 flag for ourselves.
419 ifv
->ifv_if
.if_flags
= (p
->if_flags
& ~(IFF_LINK0
));
422 * Set up our ``Ethernet address'' to reflect the underlying
423 * physical interface's.
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
);
437 vlan_unconfig(struct ifnet
*ifp
)
440 struct sockaddr_dl
*sdl
;
441 struct vlan_mc_entry
*mc
;
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
455 while(ifv
->vlan_mc_listhead
.slh_first
!= NULL
) {
456 struct sockaddr_dl sdl
;
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
);
466 SLIST_REMOVE_HEAD(&ifv
->vlan_mc_listhead
, mc_entries
);
470 /* Disconnect from parent. */
472 ifv
->ifv_if
.if_mtu
= ETHERMTU
;
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
);
486 vlan_ioctl(struct ifnet
*ifp
, u_long cmd
, caddr_t data
)
495 ifr
= (struct ifreq
*)data
;
496 ifa
= (struct ifaddr
*)data
;
501 ifp
->if_flags
|= IFF_UP
;
503 switch (ifa
->ifa_addr
->sa_family
) {
506 arp_ifinit(&ifv
->ifv_ac
, ifa
);
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
);
526 * Set the interface MTU.
527 * This is bogus. The underlying interface might support
530 if (ifr
->ifr_mtu
> ETHERMTU
) {
533 ifp
->if_mtu
= ifr
->ifr_mtu
;
538 error
= copyin(ifr
->ifr_data
, &vlr
, sizeof vlr
);
541 if (vlr
.vlr_parent
[0] == '\0') {
547 p
= ifunit(vlr
.vlr_parent
);
552 error
= vlan_config(ifv
, p
);
555 ifv
->ifv_tag
= vlr
.vlr_tag
;
559 bzero(&vlr
, sizeof vlr
);
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
;
565 error
= copyout(&vlr
, ifr
->ifr_data
, sizeof vlr
);
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.
574 if (ifr
->ifr_flags
& (IFF_PROMISC
)) {
575 ifp
->if_flags
&= ~(IFF_PROMISC
);
581 error
= vlan_setmulti(ifp
);
589 #endif /* NVLAN > 0 */