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