]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
4a249263 | 2 | * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. |
1c79356b A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
e5568f75 A |
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. | |
1c79356b | 11 | * |
e5568f75 A |
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 | |
1c79356b A |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
e5568f75 A |
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. | |
1c79356b A |
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 | * | |
4a249263 | 50 | * $FreeBSD: src/sys/net/if_vlan.c,v 1.54 2003/10/31 18:32:08 brooks Exp $ |
1c79356b A |
51 | */ |
52 | ||
53 | /* | |
54 | * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs. | |
55 | * Might be extended some day to also handle IEEE 802.1p priority | |
56 | * tagging. This is sort of sneaky in the implementation, since | |
57 | * we need to pretend to be enough of an Ethernet implementation | |
58 | * to make arp work. The way we do this is by telling everyone | |
59 | * that we are an Ethernet, and then catch the packets that | |
4a249263 | 60 | * ether_output() left on our output queue when it calls |
1c79356b A |
61 | * if_start(), rewrite them for use by the real outgoing interface, |
62 | * and ask it to send them. | |
1c79356b A |
63 | */ |
64 | ||
1c79356b A |
65 | |
66 | #include <sys/param.h> | |
67 | #include <sys/kernel.h> | |
4a249263 | 68 | #include <sys/malloc.h> |
1c79356b | 69 | #include <sys/mbuf.h> |
4a249263 | 70 | #include <sys/queue.h> |
1c79356b A |
71 | #include <sys/socket.h> |
72 | #include <sys/sockio.h> | |
73 | #include <sys/sysctl.h> | |
74 | #include <sys/systm.h> | |
4a249263 | 75 | #include <sys/kern_event.h> |
1c79356b | 76 | |
1c79356b | 77 | #include <net/bpf.h> |
1c79356b A |
78 | #include <net/ethernet.h> |
79 | #include <net/if.h> | |
80 | #include <net/if_arp.h> | |
81 | #include <net/if_dl.h> | |
82 | #include <net/if_types.h> | |
83 | #include <net/if_vlan_var.h> | |
84 | ||
4a249263 A |
85 | #include <net/dlil.h> |
86 | ||
87 | #ifdef INET | |
1c79356b A |
88 | #include <netinet/in.h> |
89 | #include <netinet/if_ether.h> | |
90 | #endif | |
91 | ||
4a249263 A |
92 | #include <net/if_media.h> |
93 | ||
94 | #define ETHER_VLAN_ENCAP_LEN 4 /* len of 802.1Q VLAN encapsulation */ | |
95 | #define IF_MAXUNIT 0x7fff /* historical value */ | |
96 | ||
97 | #define IFP2AC(p) ((struct arpcom *)p) | |
98 | ||
99 | #define VLAN_PROTO_FAMILY 0x766c616e /* 'vlan' */ | |
100 | ||
101 | #define VLANNAME "vlan" | |
102 | ||
103 | typedef int (bpf_callback_func)(struct ifnet *, struct mbuf *); | |
104 | typedef int (if_set_bpf_tap_func)(struct ifnet *ifp, int mode, bpf_callback_func * func); | |
105 | ||
106 | struct vlan_mc_entry { | |
107 | struct ether_addr mc_addr; | |
108 | SLIST_ENTRY(vlan_mc_entry) mc_entries; | |
109 | }; | |
110 | ||
111 | struct ifvlan { | |
112 | char ifv_name[IFNAMSIZ]; /* our unique id */ | |
113 | struct ifnet *ifv_ifp; /* our interface */ | |
114 | struct ifnet *ifv_p; /* parent interface of this vlan */ | |
115 | struct ifv_linkmib { | |
116 | int ifvm_parent; | |
117 | int ifvm_encaplen; /* encapsulation length */ | |
118 | int ifvm_mtufudge; /* MTU fudged by this much */ | |
119 | int ifvm_mintu; /* min transmission unit */ | |
120 | u_int16_t ifvm_proto; /* encapsulation ethertype */ | |
121 | u_int16_t ifvm_tag; /* tag to apply on packets leaving if */ | |
122 | } ifv_mib; | |
123 | SLIST_HEAD(__vlan_mchead, vlan_mc_entry) vlan_mc_listhead; | |
124 | LIST_ENTRY(ifvlan) ifv_list; | |
125 | int ifv_flags; | |
126 | int ifv_detaching; | |
127 | u_long ifv_filter_id; | |
128 | int ifv_filter_valid; | |
129 | bpf_callback_func * ifv_bpf_input; | |
130 | bpf_callback_func * ifv_bpf_output; | |
131 | }; | |
132 | ||
133 | #define ifv_tag ifv_mib.ifvm_tag | |
134 | #define ifv_encaplen ifv_mib.ifvm_encaplen | |
135 | #define ifv_mtufudge ifv_mib.ifvm_mtufudge | |
136 | #define ifv_mintu ifv_mib.ifvm_mintu | |
137 | ||
138 | #define IFVF_PROMISC 0x01 /* promiscuous mode enabled */ | |
139 | ||
140 | #if 0 | |
1c79356b | 141 | SYSCTL_DECL(_net_link); |
4a249263 | 142 | SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); |
1c79356b | 143 | SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); |
4a249263 A |
144 | #endif 0 |
145 | ||
146 | #define M_VLAN M_DEVBUF | |
1c79356b | 147 | |
4a249263 | 148 | MALLOC_DEFINE(M_VLAN, VLANNAME, "802.1Q Virtual LAN Interface"); |
1c79356b | 149 | |
4a249263 | 150 | static LIST_HEAD(, ifvlan) ifv_list; |
1c79356b | 151 | |
4a249263 A |
152 | #if 0 |
153 | /* | |
154 | * Locking: one lock is used to guard both the ifv_list and modification | |
155 | * to vlan data structures. We are rather conservative here; probably | |
156 | * more than necessary. | |
157 | */ | |
158 | static struct mtx ifv_mtx; | |
159 | #define VLAN_LOCK_INIT() mtx_init(&ifv_mtx, VLANNAME, NULL, MTX_DEF) | |
160 | #define VLAN_LOCK_DESTROY() mtx_destroy(&ifv_mtx) | |
161 | #define VLAN_LOCK_ASSERT() mtx_assert(&ifv_mtx, MA_OWNED) | |
162 | #define VLAN_LOCK() mtx_lock(&ifv_mtx) | |
163 | #define VLAN_UNLOCK() mtx_unlock(&ifv_mtx) | |
164 | #else | |
165 | #define VLAN_LOCK_INIT() | |
166 | #define VLAN_LOCK_DESTROY() | |
167 | #define VLAN_LOCK_ASSERT() | |
168 | #define VLAN_LOCK() | |
169 | #define VLAN_UNLOCK() | |
170 | #endif 0 | |
171 | ||
172 | static int vlan_clone_create(struct if_clone *, int); | |
173 | static void vlan_clone_destroy(struct ifnet *); | |
174 | static int vlan_output(struct ifnet *ifp, struct mbuf *m); | |
1c79356b | 175 | static void vlan_ifinit(void *foo); |
4a249263 A |
176 | static int vlan_ioctl(struct ifnet *ifp, u_long cmd, void * addr); |
177 | static int vlan_set_bpf_tap(struct ifnet * ifp, int mode, | |
178 | bpf_callback_func * func); | |
179 | static int vlan_attach_protocol(struct ifnet *ifp); | |
180 | static int vlan_detach_protocol(struct ifnet *ifp); | |
181 | static int vlan_attach_filter(struct ifnet * ifp, u_long * filter_id); | |
182 | static int vlan_detach_filter(u_long filter_id); | |
1c79356b A |
183 | static int vlan_setmulti(struct ifnet *ifp); |
184 | static int vlan_unconfig(struct ifnet *ifp); | |
4a249263 A |
185 | static int vlan_config(struct ifvlan *ifv, struct ifnet *p, int tag); |
186 | static int vlan_if_free(struct ifnet * ifp); | |
187 | ||
188 | static struct if_clone vlan_cloner = IF_CLONE_INITIALIZER(VLANNAME, | |
189 | vlan_clone_create, vlan_clone_destroy, 0, IF_MAXUNIT); | |
190 | ||
191 | static if_set_bpf_tap_func nop_if_bpf; | |
192 | static int nop_if_free(struct ifnet *); | |
193 | static int nop_if_ioctl(struct ifnet *, u_long, void *); | |
194 | static int nop_if_output(struct ifnet * ifp, struct mbuf * m); | |
195 | ||
196 | static void interface_link_event(struct ifnet * ifp, u_long event_code); | |
197 | ||
198 | static __inline__ void | |
199 | vlan_bpf_output(struct ifnet * ifp, struct mbuf * m, | |
200 | bpf_callback_func func) | |
201 | { | |
202 | if (func != NULL) { | |
203 | func(ifp, m); | |
204 | } | |
205 | return; | |
206 | } | |
207 | ||
208 | static __inline__ void | |
209 | vlan_bpf_input(struct ifnet * ifp, struct mbuf * m, | |
210 | bpf_callback_func func, char * frame_header, | |
211 | int frame_header_len, int encap_len) | |
212 | { | |
213 | if (func != NULL) { | |
214 | if (encap_len > 0) { | |
215 | /* present the right header to bpf */ | |
216 | bcopy(frame_header, frame_header + encap_len, frame_header_len); | |
217 | } | |
218 | m->m_data -= frame_header_len; | |
219 | m->m_len += frame_header_len; | |
220 | func(ifp, m); | |
221 | m->m_data += frame_header_len; | |
222 | m->m_len -= frame_header_len; | |
223 | if (encap_len > 0) { | |
224 | /* restore the header */ | |
225 | bcopy(frame_header + encap_len, frame_header, frame_header_len); | |
226 | } | |
227 | } | |
228 | return; | |
229 | } | |
230 | ||
231 | static struct ifaddr * | |
232 | ifaddr_byindex(unsigned int i) | |
233 | { | |
234 | if (i > if_index || i == 0) { | |
235 | return (NULL); | |
236 | } | |
237 | return (ifnet_addrs[i - 1]); | |
238 | } | |
1c79356b A |
239 | |
240 | /* | |
241 | * Program our multicast filter. What we're actually doing is | |
242 | * programming the multicast filter of the parent. This has the | |
243 | * side effect of causing the parent interface to receive multicast | |
244 | * traffic that it doesn't really want, which ends up being discarded | |
245 | * later by the upper protocol layers. Unfortunately, there's no way | |
246 | * to avoid this: there really is only one physical interface. | |
247 | */ | |
4a249263 A |
248 | static int |
249 | vlan_setmulti(struct ifnet *ifp) | |
1c79356b | 250 | { |
4a249263 A |
251 | struct ifnet *p; |
252 | struct ifmultiaddr *ifma, *rifma = NULL; | |
253 | struct ifvlan *sc; | |
254 | struct vlan_mc_entry *mc = NULL; | |
255 | struct sockaddr_dl sdl; | |
256 | int error; | |
1c79356b | 257 | |
4a249263 A |
258 | /* Find the parent. */ |
259 | sc = ifp->if_private; | |
260 | p = sc->ifv_p; | |
261 | if (p == NULL) { | |
262 | /* no parent, so no need to program the multicast filter */ | |
263 | return (0); | |
264 | } | |
1c79356b | 265 | |
4a249263 A |
266 | bzero((char *)&sdl, sizeof sdl); |
267 | sdl.sdl_len = sizeof sdl; | |
268 | sdl.sdl_family = AF_LINK; | |
269 | sdl.sdl_index = p->if_index; | |
270 | sdl.sdl_type = IFT_ETHER; | |
271 | sdl.sdl_alen = ETHER_ADDR_LEN; | |
1c79356b | 272 | |
4a249263 A |
273 | /* First, remove any existing filter entries. */ |
274 | while (SLIST_FIRST(&sc->vlan_mc_listhead) != NULL) { | |
275 | mc = SLIST_FIRST(&sc->vlan_mc_listhead); | |
276 | bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); | |
277 | error = if_delmulti(p, (struct sockaddr *)&sdl); | |
278 | if (error) | |
279 | return(error); | |
280 | SLIST_REMOVE_HEAD(&sc->vlan_mc_listhead, mc_entries); | |
281 | FREE(mc, M_VLAN); | |
282 | } | |
283 | ||
284 | /* Now program new ones. */ | |
285 | LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { | |
286 | if (ifma->ifma_addr->sa_family != AF_LINK) | |
287 | continue; | |
288 | mc = _MALLOC(sizeof(struct vlan_mc_entry), M_VLAN, M_WAITOK); | |
289 | bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), | |
290 | (char *)&mc->mc_addr, ETHER_ADDR_LEN); | |
291 | SLIST_INSERT_HEAD(&sc->vlan_mc_listhead, mc, mc_entries); | |
292 | bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), | |
293 | LLADDR(&sdl), ETHER_ADDR_LEN); | |
294 | error = if_addmulti(p, (struct sockaddr *)&sdl, &rifma); | |
295 | if (error) | |
296 | return(error); | |
297 | } | |
298 | ||
299 | return(0); | |
300 | } | |
301 | ||
302 | #if 0 | |
303 | /* | |
304 | * VLAN support can be loaded as a module. The only place in the | |
305 | * system that's intimately aware of this is ether_input. We hook | |
306 | * into this code through vlan_input_p which is defined there and | |
307 | * set here. Noone else in the system should be aware of this so | |
308 | * we use an explicit reference here. | |
309 | * | |
310 | * NB: Noone should ever need to check if vlan_input_p is null or | |
311 | * not. This is because interfaces have a count of the number | |
312 | * of active vlans (if_nvlans) and this should never be bumped | |
313 | * except by vlan_config--which is in this module so therefore | |
314 | * the module must be loaded and vlan_input_p must be non-NULL. | |
315 | */ | |
316 | extern void (*vlan_input_p)(struct ifnet *, struct mbuf *); | |
317 | ||
318 | static int | |
319 | vlan_modevent(module_t mod, int type, void *data) | |
320 | { | |
321 | ||
322 | switch (type) { | |
323 | case MOD_LOAD: | |
324 | LIST_INIT(&ifv_list); | |
325 | VLAN_LOCK_INIT(); | |
326 | vlan_input_p = vlan_input; | |
327 | if_clone_attach(&vlan_cloner); | |
328 | break; | |
329 | case MOD_UNLOAD: | |
330 | if_clone_detach(&vlan_cloner); | |
331 | vlan_input_p = NULL; | |
332 | while (!LIST_EMPTY(&ifv_list)) | |
333 | vlan_clone_destroy(LIST_FIRST(&ifv_list)->ifv_ifp); | |
334 | VLAN_LOCK_DESTROY(); | |
335 | break; | |
336 | } | |
337 | return 0; | |
338 | } | |
339 | ||
340 | static moduledata_t vlan_mod = { | |
341 | "if_vlan", | |
342 | vlan_modevent, | |
343 | 0 | |
344 | }; | |
345 | ||
346 | DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); | |
347 | ||
348 | #endif 0 | |
349 | ||
350 | static struct ifvlan * | |
351 | vlan_lookup_ifp_and_tag(struct ifnet * ifp, int tag) | |
352 | { | |
353 | struct ifvlan * ifv; | |
1c79356b | 354 | |
4a249263 A |
355 | LIST_FOREACH(ifv, &ifv_list, ifv_list) { |
356 | if (ifp == ifv->ifv_p && tag == ifv->ifv_tag) { | |
357 | return (ifv); | |
1c79356b | 358 | } |
4a249263 A |
359 | } |
360 | return (NULL); | |
361 | } | |
1c79356b | 362 | |
4a249263 A |
363 | static struct ifvlan * |
364 | vlan_lookup_ifp(struct ifnet * ifp) | |
365 | { | |
366 | struct ifvlan * ifv; | |
367 | ||
368 | LIST_FOREACH(ifv, &ifv_list, ifv_list) { | |
369 | if (ifp == ifv->ifv_p) { | |
370 | return (ifv); | |
371 | } | |
372 | } | |
373 | return (NULL); | |
1c79356b A |
374 | } |
375 | ||
376 | static void | |
4a249263 A |
377 | vlan_clone_attach(void) |
378 | { | |
379 | if_clone_attach(&vlan_cloner); | |
380 | return; | |
381 | } | |
382 | ||
383 | static int | |
384 | vlan_clone_create(struct if_clone *ifc, int unit) | |
385 | { | |
386 | int error; | |
387 | struct ifvlan *ifv; | |
388 | struct ifnet *ifp; | |
389 | ||
390 | ifv = _MALLOC(sizeof(struct ifvlan), M_VLAN, M_WAITOK); | |
391 | bzero(ifv, sizeof(struct ifvlan)); | |
392 | SLIST_INIT(&ifv->vlan_mc_listhead); | |
393 | ||
394 | /* use the interface name as the unique id for ifp recycle */ | |
395 | if (snprintf(ifv->ifv_name, sizeof(ifv->ifv_name), "%s%d", | |
396 | ifc->ifc_name, unit) >= sizeof(ifv->ifv_name)) { | |
397 | FREE(ifv, M_VLAN); | |
398 | return (EINVAL); | |
399 | } | |
400 | error = dlil_if_acquire(APPLE_IF_FAM_VLAN, | |
401 | ifv->ifv_name, | |
402 | strlen(ifv->ifv_name), | |
403 | &ifp); | |
404 | if (error) { | |
405 | FREE(ifv, M_VLAN); | |
406 | return (error); | |
407 | } | |
408 | ifv->ifv_ifp = ifp; | |
409 | ifp->if_private = ifv; | |
410 | ifp->if_name = (char *)ifc->ifc_name; | |
411 | ifp->if_unit = unit; | |
412 | ifp->if_family = APPLE_IF_FAM_VLAN; | |
413 | ||
414 | #if 0 | |
415 | /* NB: flags are not set here */ | |
416 | ifp->if_linkmib = &ifv->ifv_mib; | |
417 | ifp->if_linkmiblen = sizeof ifv->ifv_mib; | |
418 | /* NB: mtu is not set here */ | |
419 | #endif 0 | |
420 | ||
421 | ifp->if_ioctl = vlan_ioctl; | |
422 | ifp->if_set_bpf_tap = vlan_set_bpf_tap; | |
423 | ifp->if_free = nop_if_free; | |
424 | ifp->if_output = nop_if_output; | |
425 | ifp->if_hwassist = 0; | |
426 | ifp->if_addrlen = ETHER_ADDR_LEN; /* XXX ethernet specific */ | |
427 | ifp->if_baudrate = 0; | |
428 | ifp->if_type = IFT_L2VLAN; | |
429 | ifp->if_hdrlen = ETHER_VLAN_ENCAP_LEN; | |
430 | error = dlil_if_attach(ifp); | |
431 | if (error) { | |
432 | dlil_if_release(ifp); | |
433 | FREE(ifv, M_VLAN); | |
434 | return (error); | |
435 | } | |
436 | ||
437 | /* attach as ethernet */ | |
438 | bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); | |
439 | ||
440 | VLAN_LOCK(); | |
441 | LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list); | |
442 | VLAN_UNLOCK(); | |
443 | ||
444 | return (0); | |
1c79356b | 445 | } |
1c79356b A |
446 | |
447 | static void | |
4a249263 | 448 | vlan_remove(struct ifvlan * ifv) |
1c79356b | 449 | { |
4a249263 A |
450 | VLAN_LOCK_ASSERT(); |
451 | ifv->ifv_detaching = 1; | |
452 | vlan_unconfig(ifv->ifv_ifp); | |
453 | LIST_REMOVE(ifv, ifv_list); | |
454 | return; | |
1c79356b A |
455 | } |
456 | ||
457 | static void | |
4a249263 A |
458 | vlan_if_detach(struct ifnet * ifp) |
459 | { | |
460 | ifp->if_output = nop_if_output; | |
461 | ifp->if_ioctl = nop_if_ioctl; | |
462 | ifp->if_set_bpf_tap = &nop_if_bpf; | |
463 | if (dlil_if_detach(ifp) == DLIL_WAIT_FOR_FREE) { | |
464 | ifp->if_free = vlan_if_free; | |
465 | } else { | |
466 | vlan_if_free(ifp); | |
467 | } | |
468 | return; | |
469 | } | |
1c79356b | 470 | |
4a249263 A |
471 | static void |
472 | vlan_clone_destroy(struct ifnet *ifp) | |
473 | { | |
474 | struct ifvlan *ifv = ifp->if_private; | |
1c79356b | 475 | |
4a249263 A |
476 | if (ifv == NULL || ifp->if_type != IFT_L2VLAN) { |
477 | return; | |
478 | } | |
479 | VLAN_LOCK(); | |
480 | if (ifv->ifv_detaching) { | |
481 | VLAN_UNLOCK(); | |
1c79356b | 482 | return; |
4a249263 A |
483 | } |
484 | vlan_remove(ifv); | |
485 | VLAN_UNLOCK(); | |
486 | vlan_if_detach(ifp); | |
487 | return; | |
1c79356b A |
488 | } |
489 | ||
4a249263 A |
490 | static int |
491 | vlan_set_bpf_tap(struct ifnet * ifp, int mode, bpf_callback_func * func) | |
1c79356b | 492 | { |
4a249263 | 493 | struct ifvlan *ifv = ifp->if_private; |
1c79356b | 494 | |
4a249263 A |
495 | switch (mode) { |
496 | case BPF_TAP_DISABLE: | |
497 | ifv->ifv_bpf_input = ifv->ifv_bpf_output = NULL; | |
498 | break; | |
1c79356b | 499 | |
4a249263 A |
500 | case BPF_TAP_INPUT: |
501 | ifv->ifv_bpf_input = func; | |
502 | break; | |
503 | ||
504 | case BPF_TAP_OUTPUT: | |
505 | ifv->ifv_bpf_output = func; | |
506 | break; | |
507 | ||
508 | case BPF_TAP_INPUT_OUTPUT: | |
509 | ifv->ifv_bpf_input = ifv->ifv_bpf_output = func; | |
510 | break; | |
511 | default: | |
512 | break; | |
513 | } | |
514 | return 0; | |
515 | } | |
516 | ||
517 | static void | |
518 | vlan_ifinit(void *foo) | |
519 | { | |
520 | return; | |
521 | } | |
522 | ||
523 | static int | |
524 | vlan_output(struct ifnet *ifp, struct mbuf *m) | |
525 | { | |
526 | struct ifvlan *ifv; | |
527 | struct ifnet *p; | |
528 | struct ether_vlan_header *evl; | |
529 | int soft_vlan; | |
1c79356b | 530 | |
4a249263 A |
531 | ifv = ifp->if_private; |
532 | p = ifv->ifv_p; | |
533 | if (p == NULL) { | |
534 | return (nop_if_output(ifp, m)); | |
535 | } | |
536 | if (m == 0) { | |
537 | printf("%s: NULL output mbuf\n", ifv->ifv_name); | |
538 | return (EINVAL); | |
539 | } | |
540 | if ((m->m_flags & M_PKTHDR) == 0) { | |
541 | printf("%s: M_PKTHDR bit not set\n", ifv->ifv_name); | |
542 | m_freem(m); | |
543 | return (EINVAL); | |
544 | } | |
545 | ifp->if_obytes += m->m_pkthdr.len; | |
546 | ifp->if_opackets++; | |
547 | soft_vlan = (p->if_hwassist & IF_HWASSIST_VLAN_TAGGING) == 0; | |
548 | vlan_bpf_output(ifp, m, ifv->ifv_bpf_output); | |
549 | ||
550 | /* do not run parent's if_output() if the parent is not up */ | |
551 | if ((p->if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) { | |
552 | m_freem(m); | |
553 | ifp->if_collisions++; | |
554 | return (0); | |
555 | } | |
556 | /* | |
557 | * If underlying interface can do VLAN tag insertion itself, | |
558 | * just pass the packet along. However, we need some way to | |
559 | * tell the interface where the packet came from so that it | |
560 | * knows how to find the VLAN tag to use. We use a field in | |
561 | * the mbuf header to store the VLAN tag, and a bit in the | |
562 | * csum_flags field to mark the field as valid. | |
563 | */ | |
564 | if (soft_vlan == 0) { | |
565 | m->m_pkthdr.csum_flags |= CSUM_VLAN_TAG_VALID; | |
566 | m->m_pkthdr.vlan_tag = ifv->ifv_tag; | |
567 | } else { | |
568 | M_PREPEND(m, ifv->ifv_encaplen, M_DONTWAIT); | |
569 | if (m == NULL) { | |
570 | printf("%s: unable to prepend VLAN header\n", | |
571 | ifv->ifv_name); | |
572 | ifp->if_ierrors++; | |
573 | return (0); | |
574 | } | |
575 | /* M_PREPEND takes care of m_len, m_pkthdr.len for us */ | |
576 | if (m->m_len < sizeof(*evl)) { | |
577 | m = m_pullup(m, sizeof(*evl)); | |
578 | if (m == NULL) { | |
579 | printf("%s: cannot pullup VLAN header\n", | |
580 | ifv->ifv_name); | |
581 | ifp->if_ierrors++; | |
582 | return (0); | |
583 | } | |
584 | } | |
585 | ||
1c79356b | 586 | /* |
4a249263 A |
587 | * Transform the Ethernet header into an Ethernet header |
588 | * with 802.1Q encapsulation. | |
1c79356b | 589 | */ |
4a249263 A |
590 | bcopy(mtod(m, char *) + ifv->ifv_encaplen, |
591 | mtod(m, char *), ETHER_HDR_LEN); | |
592 | evl = mtod(m, struct ether_vlan_header *); | |
593 | evl->evl_proto = evl->evl_encap_proto; | |
594 | evl->evl_encap_proto = htons(ETHERTYPE_VLAN); | |
595 | evl->evl_tag = htons(ifv->ifv_tag); | |
596 | m->m_pkthdr.len += ifv->ifv_encaplen; | |
597 | } | |
598 | ||
599 | /* | |
600 | * Send it, precisely as ether_output() would have. | |
601 | * We are already running at splimp. | |
602 | */ | |
603 | return ((*p->if_output)(p, m)); | |
1c79356b A |
604 | } |
605 | ||
4a249263 A |
606 | extern int |
607 | vlan_demux(struct ifnet * ifp, struct mbuf * m, | |
608 | char * frame_header, struct if_proto * * proto) | |
1c79356b | 609 | { |
4a249263 A |
610 | register struct ether_header *eh = (struct ether_header *)frame_header; |
611 | struct ether_vlan_header *evl; | |
612 | struct ifvlan *ifv = NULL; | |
613 | int soft_vlan = 0; | |
614 | u_int tag; | |
1c79356b | 615 | |
4a249263 A |
616 | if (m->m_pkthdr.csum_flags & CSUM_VLAN_TAG_VALID) { |
617 | /* | |
618 | * Packet is tagged, m contains a normal | |
619 | * Ethernet frame; the tag is stored out-of-band. | |
620 | */ | |
621 | m->m_pkthdr.csum_flags &= ~CSUM_VLAN_TAG_VALID; | |
622 | tag = EVL_VLANOFTAG(m->m_pkthdr.vlan_tag); | |
623 | m->m_pkthdr.vlan_tag = 0; | |
624 | } else { | |
625 | soft_vlan = 1; | |
1c79356b | 626 | |
4a249263 A |
627 | switch (ifp->if_type) { |
628 | case IFT_ETHER: | |
629 | if (m->m_len < ETHER_VLAN_ENCAP_LEN) { | |
1c79356b | 630 | m_freem(m); |
4a249263 A |
631 | return (EJUSTRETURN); |
632 | } | |
633 | evl = (struct ether_vlan_header *)frame_header; | |
634 | if (ntohs(evl->evl_proto) == ETHERTYPE_VLAN) { | |
635 | /* don't allow VLAN within VLAN */ | |
636 | m_freem(m); | |
637 | return (EJUSTRETURN); | |
638 | } | |
639 | tag = EVL_VLANOFTAG(ntohs(evl->evl_tag)); | |
1c79356b | 640 | |
4a249263 A |
641 | /* |
642 | * Restore the original ethertype. We'll remove | |
643 | * the encapsulation after we've found the vlan | |
644 | * interface corresponding to the tag. | |
645 | */ | |
646 | evl->evl_encap_proto = evl->evl_proto; | |
647 | break; | |
648 | default: | |
649 | printf("vlan_demux: unsupported if type %u", | |
650 | ifp->if_type); | |
651 | m_freem(m); | |
652 | return (EJUSTRETURN); | |
653 | break; | |
654 | } | |
655 | } | |
656 | if (tag != 0) { | |
657 | if (ifp->if_nvlans == 0) { | |
658 | /* don't bother looking through the VLAN list */ | |
659 | m_freem(m); | |
660 | ifp->if_noproto++; | |
661 | return (EJUSTRETURN); | |
662 | } | |
663 | VLAN_LOCK(); | |
664 | ifv = vlan_lookup_ifp_and_tag(ifp, tag); | |
665 | if (ifv == NULL || (ifv->ifv_ifp->if_flags & IFF_UP) == 0) { | |
666 | VLAN_UNLOCK(); | |
667 | m_freem(m); | |
668 | ifp->if_noproto++; | |
669 | return (EJUSTRETURN); | |
670 | } | |
671 | VLAN_UNLOCK(); /* XXX extend below? */ | |
672 | } | |
673 | if (soft_vlan) { | |
1c79356b | 674 | /* |
4a249263 A |
675 | * Packet had an in-line encapsulation header; |
676 | * remove it. The original header has already | |
677 | * been fixed up above. | |
1c79356b | 678 | */ |
4a249263 A |
679 | m->m_len -= ETHER_VLAN_ENCAP_LEN; |
680 | m->m_data += ETHER_VLAN_ENCAP_LEN; | |
681 | m->m_pkthdr.len -= ETHER_VLAN_ENCAP_LEN; | |
682 | m->m_pkthdr.csum_flags = 0; /* can't trust hardware checksum */ | |
683 | } | |
684 | if (tag != 0) { | |
685 | /* we found a vlan interface above, so send it up */ | |
686 | m->m_pkthdr.rcvif = ifv->ifv_ifp; | |
687 | ifv->ifv_ifp->if_ipackets++; | |
688 | ifv->ifv_ifp->if_ibytes += m->m_pkthdr.len; | |
689 | ||
690 | vlan_bpf_input(ifv->ifv_ifp, m, ifv->ifv_bpf_input, frame_header, | |
691 | ETHER_HDR_LEN, soft_vlan ? ETHER_VLAN_ENCAP_LEN : 0); | |
692 | ||
693 | /* Pass it back through the parent's demux routine. */ | |
694 | return ((*ifp->if_demux)(ifv->ifv_ifp, m, frame_header, proto)); | |
695 | } | |
696 | /* Pass it back through calling demux routine. */ | |
697 | return ((*ifp->if_demux)(ifp, m, frame_header, proto)); | |
1c79356b A |
698 | } |
699 | ||
700 | static int | |
4a249263 | 701 | vlan_config(struct ifvlan *ifv, struct ifnet *p, int tag) |
1c79356b | 702 | { |
4a249263 A |
703 | struct ifnet * ifp; |
704 | struct ifaddr *ifa1, *ifa2; | |
705 | struct sockaddr_dl *sdl1, *sdl2; | |
706 | int supports_vlan_mtu = 0; | |
1c79356b | 707 | |
4a249263 A |
708 | VLAN_LOCK_ASSERT(); |
709 | if (p->if_data.ifi_type != IFT_ETHER) | |
710 | return EPROTONOSUPPORT; | |
711 | if (ifv->ifv_p != NULL || ifv->ifv_detaching) { | |
712 | return EBUSY; | |
713 | } | |
714 | if (vlan_lookup_ifp_and_tag(p, tag) != NULL) { | |
715 | /* already a VLAN with that tag on this interface */ | |
716 | return (EADDRINUSE); | |
717 | } | |
718 | ifp = ifv->ifv_ifp; | |
719 | ifv->ifv_encaplen = ETHER_VLAN_ENCAP_LEN; | |
720 | ifv->ifv_mintu = ETHERMIN; | |
721 | ifv->ifv_flags = 0; | |
1c79356b | 722 | |
4a249263 A |
723 | /* |
724 | * If the parent supports the VLAN_MTU capability, | |
725 | * i.e. can Tx/Rx larger than ETHER_MAX_LEN frames, | |
726 | * enable it. | |
727 | */ | |
728 | if (p->if_hwassist & (IF_HWASSIST_VLAN_MTU | IF_HWASSIST_VLAN_TAGGING)) { | |
729 | supports_vlan_mtu = 1; | |
730 | } | |
731 | if (p->if_nvlans == 0) { | |
732 | u_long dltag; | |
733 | u_long filter_id; | |
734 | int error; | |
735 | ||
736 | /* attach our VLAN "interface filter" to the interface */ | |
737 | error = vlan_attach_filter(p, &filter_id); | |
738 | if (error) { | |
739 | return (error); | |
740 | } | |
741 | ||
742 | /* attach our VLAN "protocol" to the interface */ | |
743 | error = vlan_attach_protocol(p); | |
744 | if (error) { | |
745 | (void)vlan_detach_filter(filter_id); | |
746 | return (error); | |
747 | } | |
748 | ifv->ifv_filter_id = filter_id; | |
749 | ifv->ifv_filter_valid = TRUE; | |
750 | #if 0 | |
751 | if (supports_vlan_mtu) { | |
752 | /* | |
753 | * Enable Tx/Rx of VLAN-sized frames. | |
754 | */ | |
755 | p->if_capenable |= IFCAP_VLAN_MTU; | |
756 | if (p->if_flags & IFF_UP) { | |
757 | struct ifreq ifr; | |
758 | int error; | |
759 | ||
760 | ifr.ifr_flags = p->if_flags; | |
761 | error = (*p->if_ioctl)(p, SIOCSIFFLAGS, | |
762 | (caddr_t) &ifr); | |
763 | if (error) { | |
764 | if (p->if_nvlans == 0) | |
765 | p->if_capenable &= ~IFCAP_VLAN_MTU; | |
766 | return (error); | |
767 | } | |
768 | } | |
769 | } | |
770 | #endif 0 | |
771 | } else { | |
772 | struct ifvlan * other_ifv; | |
1c79356b | 773 | |
4a249263 A |
774 | other_ifv = vlan_lookup_ifp(p); |
775 | if (other_ifv == NULL) { | |
776 | printf("vlan: other_ifv can't be NULL\n"); | |
777 | return (EINVAL); | |
778 | } | |
779 | ifv->ifv_filter_id = other_ifv->ifv_filter_id; | |
780 | ifv->ifv_filter_valid = TRUE; | |
781 | } | |
782 | p->if_nvlans++; | |
783 | if (supports_vlan_mtu) { | |
784 | ifv->ifv_mtufudge = 0; | |
785 | } else { | |
1c79356b | 786 | /* |
4a249263 A |
787 | * Fudge the MTU by the encapsulation size. This |
788 | * makes us incompatible with strictly compliant | |
789 | * 802.1Q implementations, but allows us to use | |
790 | * the feature with other NetBSD implementations, | |
791 | * which might still be useful. | |
1c79356b | 792 | */ |
4a249263 A |
793 | ifv->ifv_mtufudge = ifv->ifv_encaplen; |
794 | } | |
795 | ||
796 | ifv->ifv_p = p; | |
797 | ifp->if_mtu = p->if_mtu - ifv->ifv_mtufudge; | |
798 | /* | |
799 | * Copy only a selected subset of flags from the parent. | |
800 | * Other flags are none of our business. | |
801 | */ | |
802 | ifp->if_flags |= (p->if_flags & | |
803 | (IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX)); | |
804 | /* | |
805 | * If the parent interface can do hardware-assisted | |
806 | * VLAN encapsulation, then propagate its hardware- | |
807 | * assisted checksumming flags. | |
808 | */ | |
809 | if (p->if_hwassist & IF_HWASSIST_VLAN_TAGGING) { | |
810 | ifp->if_hwassist |= IF_HWASSIST_CSUM_FLAGS(p->if_hwassist); | |
811 | } | |
812 | /* | |
813 | * Set up our ``Ethernet address'' to reflect the underlying | |
814 | * physical interface's. | |
815 | */ | |
816 | ifa1 = ifaddr_byindex(ifp->if_index); | |
817 | ifa2 = ifaddr_byindex(p->if_index); | |
818 | sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr; | |
819 | sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr; | |
820 | sdl1->sdl_type = IFT_ETHER; | |
821 | sdl1->sdl_alen = ETHER_ADDR_LEN; | |
822 | bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); | |
823 | bcopy(LLADDR(sdl2), IFP2AC(ifp)->ac_enaddr, ETHER_ADDR_LEN); | |
824 | ||
825 | /* | |
826 | * Configure multicast addresses that may already be | |
827 | * joined on the vlan device. | |
828 | */ | |
829 | (void)vlan_setmulti(ifp); | |
830 | ifp->if_output = vlan_output; | |
831 | ifv->ifv_tag = tag; | |
832 | ||
833 | return 0; | |
834 | } | |
835 | ||
836 | static void | |
837 | vlan_link_event(struct ifnet * ifp, struct ifnet * p) | |
838 | { | |
839 | struct ifmediareq ifmr; | |
840 | ||
841 | /* generate a link event based on the state of the underlying interface */ | |
842 | bzero(&ifmr, sizeof(ifmr)); | |
843 | snprintf(ifmr.ifm_name, sizeof(ifmr.ifm_name), | |
844 | "%s%d", p->if_name, p->if_unit); | |
845 | if ((*p->if_ioctl)(p, SIOCGIFMEDIA, (caddr_t)&ifmr) == 0 | |
846 | && ifmr.ifm_count > 0 && ifmr.ifm_status & IFM_AVALID) { | |
847 | u_long event; | |
848 | ||
849 | event = (ifmr.ifm_status & IFM_ACTIVE) | |
850 | ? KEV_DL_LINK_ON : KEV_DL_LINK_OFF; | |
851 | interface_link_event(ifp, event); | |
852 | } | |
853 | return; | |
1c79356b A |
854 | } |
855 | ||
856 | static int | |
857 | vlan_unconfig(struct ifnet *ifp) | |
858 | { | |
4a249263 A |
859 | struct ifaddr *ifa; |
860 | struct sockaddr_dl *sdl; | |
861 | struct vlan_mc_entry *mc; | |
862 | struct ifvlan *ifv; | |
863 | struct ifnet *p; | |
864 | int error; | |
865 | ||
866 | VLAN_LOCK_ASSERT(); | |
867 | ||
868 | ifv = ifp->if_private; | |
1c79356b | 869 | |
4a249263 A |
870 | /* Disconnect from parent. */ |
871 | p = ifv->ifv_p; | |
872 | ifv->ifv_p = NULL; | |
873 | ||
874 | if (p != NULL) { | |
875 | struct sockaddr_dl sdl; | |
1c79356b A |
876 | |
877 | /* | |
4a249263 | 878 | * Since the interface is being unconfigured, we need to |
1c79356b | 879 | * empty the list of multicast groups that we may have joined |
4a249263 | 880 | * while we were alive from the parent's list. |
1c79356b | 881 | */ |
4a249263 A |
882 | bzero((char *)&sdl, sizeof sdl); |
883 | sdl.sdl_len = sizeof sdl; | |
884 | sdl.sdl_family = AF_LINK; | |
885 | sdl.sdl_index = p->if_index; | |
886 | sdl.sdl_type = IFT_ETHER; | |
887 | sdl.sdl_alen = ETHER_ADDR_LEN; | |
888 | ||
889 | while (SLIST_FIRST(&ifv->vlan_mc_listhead) != NULL) { | |
890 | mc = SLIST_FIRST(&ifv->vlan_mc_listhead); | |
891 | bcopy((char *)&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); | |
892 | error = if_delmulti(p, (struct sockaddr *)&sdl); | |
893 | if (error) { | |
894 | printf("vlan_unconfig: if_delmulti %s failed, %d\n", | |
895 | ifv->ifv_name, error); | |
896 | } | |
897 | SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); | |
898 | FREE(mc, M_VLAN); | |
899 | } | |
900 | p->if_nvlans--; | |
901 | if (p->if_nvlans == 0) { | |
902 | /* detach our VLAN "protocol" from the interface */ | |
903 | if (ifv->ifv_filter_valid) { | |
904 | (void)vlan_detach_filter(ifv->ifv_filter_id); | |
905 | } | |
906 | (void)vlan_detach_protocol(p); | |
907 | #if 0 | |
908 | /* | |
909 | * Disable Tx/Rx of VLAN-sized frames. | |
910 | */ | |
911 | p->if_capenable &= ~IFCAP_VLAN_MTU; | |
912 | if (p->if_flags & IFF_UP) { | |
913 | struct ifreq ifr; | |
914 | ||
915 | ifr.ifr_flags = p->if_flags; | |
916 | (*p->if_ioctl)(p, SIOCSIFFLAGS, (caddr_t) &ifr); | |
917 | } | |
918 | #endif 0 | |
1c79356b | 919 | } |
4a249263 | 920 | } |
1c79356b | 921 | |
4a249263 A |
922 | /* return to the state we were in before SETVLAN */ |
923 | ifp->if_mtu = 0; | |
924 | ifp->if_flags &= ~(IFF_BROADCAST | IFF_MULTICAST | |
925 | | IFF_SIMPLEX | IFF_RUNNING); | |
926 | ifv->ifv_ifp->if_hwassist = 0; | |
927 | ifv->ifv_flags = 0; | |
928 | ifv->ifv_ifp->if_output = nop_if_output; | |
929 | ifv->ifv_mtufudge = 0; | |
930 | ifv->ifv_filter_valid = FALSE; | |
1c79356b | 931 | |
4a249263 A |
932 | /* Clear our MAC address. */ |
933 | ifa = ifaddr_byindex(ifv->ifv_ifp->if_index); | |
934 | sdl = (struct sockaddr_dl *)(ifa->ifa_addr); | |
935 | sdl->sdl_type = IFT_L2VLAN; | |
936 | sdl->sdl_alen = 0; | |
937 | bzero(LLADDR(sdl), ETHER_ADDR_LEN); | |
938 | bzero(IFP2AC(ifv->ifv_ifp)->ac_enaddr, ETHER_ADDR_LEN); | |
1c79356b | 939 | |
4a249263 A |
940 | /* send a link down event */ |
941 | if (p != NULL) { | |
942 | interface_link_event(ifv->ifv_ifp, KEV_DL_LINK_OFF); | |
943 | } | |
944 | return 0; | |
1c79356b A |
945 | } |
946 | ||
947 | static int | |
4a249263 A |
948 | vlan_set_promisc(struct ifnet *ifp) |
949 | { | |
950 | struct ifvlan *ifv = ifp->if_private; | |
951 | int error = 0; | |
1c79356b | 952 | |
4a249263 A |
953 | if ((ifp->if_flags & IFF_PROMISC) != 0) { |
954 | if ((ifv->ifv_flags & IFVF_PROMISC) == 0) { | |
955 | error = ifpromisc(ifv->ifv_p, 1); | |
956 | if (error == 0) | |
957 | ifv->ifv_flags |= IFVF_PROMISC; | |
958 | } | |
959 | } else { | |
960 | if ((ifv->ifv_flags & IFVF_PROMISC) != 0) { | |
961 | error = ifpromisc(ifv->ifv_p, 0); | |
962 | if (error == 0) | |
963 | ifv->ifv_flags &= ~IFVF_PROMISC; | |
964 | } | |
965 | } | |
1c79356b | 966 | |
4a249263 A |
967 | return (error); |
968 | } | |
969 | ||
970 | static int | |
971 | vlan_ioctl(struct ifnet *ifp, u_long cmd, void * data) | |
972 | { | |
973 | struct ifaddr *ifa; | |
974 | struct ifnet *p; | |
975 | struct ifreq *ifr; | |
976 | struct ifvlan *ifv; | |
977 | struct vlanreq vlr; | |
978 | int error = 0; | |
979 | ||
980 | ifr = (struct ifreq *)data; | |
981 | ifa = (struct ifaddr *)data; | |
982 | ifv = (struct ifvlan *)ifp->if_private; | |
983 | ||
984 | switch (cmd) { | |
985 | case SIOCSIFADDR: | |
986 | ifp->if_flags |= IFF_UP; | |
987 | break; | |
988 | ||
989 | case SIOCGIFMEDIA: | |
990 | VLAN_LOCK(); | |
991 | if (ifv->ifv_p != NULL) { | |
992 | error = (*ifv->ifv_p->if_ioctl)(ifv->ifv_p, | |
993 | SIOCGIFMEDIA, data); | |
994 | VLAN_UNLOCK(); | |
995 | /* Limit the result to the parent's current config. */ | |
996 | if (error == 0) { | |
997 | struct ifmediareq *ifmr; | |
998 | ||
999 | ifmr = (struct ifmediareq *) data; | |
1000 | if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) { | |
1001 | ifmr->ifm_count = 1; | |
1002 | error = copyout(&ifmr->ifm_current, | |
1003 | ifmr->ifm_ulist, | |
1004 | sizeof(int)); | |
1c79356b | 1005 | } |
4a249263 A |
1006 | } |
1007 | } else { | |
1008 | struct ifmediareq *ifmr; | |
1009 | VLAN_UNLOCK(); | |
1010 | ||
1011 | ifmr = (struct ifmediareq *) data; | |
1012 | ifmr->ifm_current = 0; | |
1013 | ifmr->ifm_mask = 0; | |
1014 | ifmr->ifm_status = IFM_AVALID; | |
1015 | ifmr->ifm_active = 0; | |
1016 | ifmr->ifm_count = 1; | |
1017 | if (ifmr->ifm_ulist) { | |
1018 | error = copyout(&ifmr->ifm_current, | |
1019 | ifmr->ifm_ulist, | |
1020 | sizeof(int)); | |
1021 | } | |
1022 | error = 0; | |
1023 | } | |
1024 | break; | |
1025 | ||
1026 | case SIOCSIFMEDIA: | |
1027 | error = EINVAL; | |
1028 | break; | |
1029 | ||
1030 | case SIOCSIFMTU: | |
1031 | /* | |
1032 | * Set the interface MTU. | |
1033 | */ | |
1034 | VLAN_LOCK(); | |
1035 | if (ifv->ifv_p != NULL) { | |
1036 | if (ifr->ifr_mtu > (ifv->ifv_p->if_mtu - ifv->ifv_mtufudge) | |
1037 | || ifr->ifr_mtu < (ifv->ifv_mintu - ifv->ifv_mtufudge)) { | |
1038 | error = EINVAL; | |
1039 | } else { | |
1040 | ifp->if_mtu = ifr->ifr_mtu; | |
1041 | } | |
1042 | } else { | |
1043 | error = EINVAL; | |
1044 | } | |
1045 | VLAN_UNLOCK(); | |
1046 | break; | |
1047 | ||
1048 | case SIOCSETVLAN: | |
1049 | error = copyin(ifr->ifr_data, &vlr, sizeof(vlr)); | |
1050 | if (error) | |
1051 | break; | |
1052 | if (vlr.vlr_parent[0] == '\0') { | |
1053 | VLAN_LOCK(); | |
1054 | vlan_unconfig(ifp); | |
1055 | #if 0 | |
1056 | if (ifp->if_flags & IFF_UP) | |
1057 | if_down(ifp); | |
1058 | ifp->if_flags &= ~IFF_RUNNING; | |
1059 | #endif 0 | |
1060 | VLAN_UNLOCK(); | |
1061 | break; | |
1062 | } | |
1063 | p = ifunit(vlr.vlr_parent); | |
1064 | if (p == 0) { | |
1065 | error = ENOENT; | |
1066 | break; | |
1067 | } | |
1068 | /* | |
1069 | * Don't let the caller set up a VLAN tag with | |
1070 | * anything except VLID bits. | |
1071 | */ | |
1072 | if (vlr.vlr_tag & ~EVL_VLID_MASK) { | |
1073 | error = EINVAL; | |
1074 | break; | |
1075 | } | |
1076 | VLAN_LOCK(); | |
1077 | error = vlan_config(ifv, p, vlr.vlr_tag); | |
1078 | if (error) { | |
1079 | VLAN_UNLOCK(); | |
1080 | break; | |
1081 | } | |
1082 | ifp->if_flags |= IFF_RUNNING; | |
1083 | VLAN_UNLOCK(); | |
1084 | ||
1085 | /* Update promiscuous mode, if necessary. */ | |
1086 | vlan_set_promisc(ifp); | |
1087 | ||
1088 | /* generate a link event */ | |
1089 | vlan_link_event(ifp, p); | |
1090 | break; | |
1c79356b | 1091 | |
4a249263 A |
1092 | case SIOCGETVLAN: |
1093 | bzero(&vlr, sizeof vlr); | |
1094 | VLAN_LOCK(); | |
1095 | if (ifv->ifv_p != NULL) { | |
1096 | snprintf(vlr.vlr_parent, sizeof(vlr.vlr_parent), | |
1097 | "%s%d", ifv->ifv_p->if_name, | |
1098 | ifv->ifv_p->if_unit); | |
1099 | vlr.vlr_tag = ifv->ifv_tag; | |
1100 | } | |
1101 | VLAN_UNLOCK(); | |
1102 | error = copyout(&vlr, ifr->ifr_data, sizeof vlr); | |
1103 | break; | |
1c79356b | 1104 | |
4a249263 A |
1105 | case SIOCSIFFLAGS: |
1106 | /* | |
1107 | * For promiscuous mode, we enable promiscuous mode on | |
1108 | * the parent if we need promiscuous on the VLAN interface. | |
1109 | */ | |
1110 | if (ifv->ifv_p != NULL) | |
1111 | error = vlan_set_promisc(ifp); | |
1112 | break; | |
1113 | ||
1114 | case SIOCADDMULTI: | |
1115 | case SIOCDELMULTI: | |
1116 | error = vlan_setmulti(ifp); | |
1117 | break; | |
1118 | default: | |
1119 | error = EOPNOTSUPP; | |
1120 | } | |
1121 | return error; | |
1122 | } | |
1123 | ||
1124 | static int | |
1125 | nop_if_ioctl(struct ifnet * ifp, u_long cmd, void * data) | |
1126 | { | |
1127 | return EOPNOTSUPP; | |
1128 | } | |
1129 | ||
1130 | static int | |
1131 | nop_if_bpf(struct ifnet *ifp, int mode, bpf_callback_func * func) | |
1132 | { | |
1133 | return ENODEV; | |
1134 | } | |
1135 | ||
1136 | static int | |
1137 | nop_if_free(struct ifnet * ifp) | |
1138 | { | |
1139 | return 0; | |
1140 | } | |
1141 | ||
1142 | static int | |
1143 | nop_if_output(struct ifnet * ifp, struct mbuf * m) | |
1144 | { | |
1145 | if (m != NULL) { | |
1146 | m_freem_list(m); | |
1147 | } | |
1148 | return 0; | |
1149 | } | |
1150 | ||
1151 | static int | |
1152 | vlan_if_free(struct ifnet * ifp) | |
1153 | { | |
1154 | struct ifvlan *ifv; | |
1155 | ||
1156 | if (ifp == NULL) { | |
1157 | return 0; | |
1158 | } | |
1159 | ifv = (struct ifvlan *)ifp->if_private; | |
1160 | if (ifv == NULL) { | |
1161 | return 0; | |
1162 | } | |
1163 | ifp->if_private = NULL; | |
1164 | dlil_if_release(ifp); | |
1165 | FREE(ifv, M_VLAN); | |
1166 | return 0; | |
1167 | } | |
1168 | ||
1169 | /* | |
1170 | * Function: vlan_if_filter_detach | |
1171 | * Purpose: | |
1172 | * Destroy all vlan interfaces that refer to the interface | |
1173 | */ | |
1174 | static int | |
1175 | vlan_if_filter_detach(caddr_t cookie) | |
1176 | { | |
1177 | struct ifnet * ifp; | |
1178 | struct ifvlan * ifv; | |
1179 | struct ifnet * p = (struct ifnet *)cookie; | |
1180 | ||
1181 | VLAN_LOCK(); | |
1182 | while (TRUE) { | |
1183 | ifv = vlan_lookup_ifp(p); | |
1184 | if (ifv == NULL) { | |
1185 | break; | |
1186 | } | |
1187 | if (ifv->ifv_detaching) { | |
1188 | continue; | |
1189 | } | |
1190 | /* make sure we don't invoke vlan_detach_filter */ | |
1191 | ifv->ifv_filter_valid = FALSE; | |
1192 | vlan_remove(ifv); | |
1193 | ifp = ifv->ifv_ifp; | |
1194 | VLAN_UNLOCK(); | |
1195 | vlan_if_detach(ifp); | |
1196 | VLAN_LOCK(); | |
1197 | } | |
1198 | VLAN_UNLOCK(); | |
1199 | return (0); | |
1200 | } | |
1201 | ||
1202 | /* | |
1203 | * Function: vlan_attach_filter | |
1204 | * Purpose: | |
1205 | * We attach an interface filter to detect when the underlying interface | |
1206 | * goes away. We are forced to do that because dlil does not call our | |
1207 | * protocol's dl_event function for KEV_DL_IF_DETACHING. | |
1208 | */ | |
1209 | ||
1210 | static int | |
1211 | vlan_attach_filter(struct ifnet * ifp, u_long * filter_id) | |
1212 | { | |
1213 | int error; | |
1214 | struct dlil_if_flt_str filt; | |
1215 | ||
1216 | bzero(&filt, sizeof(filt)); | |
1217 | filt.filter_detach = vlan_if_filter_detach; | |
1218 | filt.cookie = (caddr_t)ifp; | |
1219 | error = dlil_attach_interface_filter(ifp, &filt, filter_id, | |
1220 | DLIL_LAST_FILTER); | |
1221 | if (error) { | |
1222 | printf("vlan: dlil_attach_interface_filter(%s%d) failed, %d\n", | |
1223 | ifp->if_name, ifp->if_unit, error); | |
1224 | } | |
1225 | return (error); | |
1226 | } | |
1227 | ||
1228 | /* | |
1229 | * Function: vlan_detach_filter | |
1230 | * Purpose: | |
1231 | * Remove our interface filter. | |
1232 | */ | |
1233 | static int | |
1234 | vlan_detach_filter(u_long filter_id) | |
1235 | { | |
1236 | int error; | |
1237 | ||
1238 | error = dlil_detach_filter(filter_id); | |
1239 | if (error) { | |
1240 | printf("vlan: dlil_detach_filter failed, %d\n", error); | |
1241 | } | |
1242 | return (error); | |
1243 | } | |
1244 | ||
1245 | /* | |
1246 | * Function: vlan_proto_input | |
1247 | * Purpose: | |
1248 | * This function is never called. We aren't allowed to leave the | |
1249 | * function pointer NULL, so this function simply free's the mbuf. | |
1250 | */ | |
1251 | static int | |
1252 | vlan_proto_input(m, frame_header, ifp, dl_tag, sync_ok) | |
1253 | struct mbuf *m; | |
1254 | char *frame_header; | |
1255 | struct ifnet *ifp; | |
1256 | u_long dl_tag; | |
1257 | int sync_ok; | |
1258 | { | |
1259 | m_freem(m); | |
1260 | return (EJUSTRETURN); | |
1261 | } | |
1262 | ||
1263 | static struct ifnet * | |
1264 | find_if_name_unit(const char * if_name, int unit) | |
1265 | { | |
1266 | struct ifnet * ifp; | |
1267 | ||
1268 | TAILQ_FOREACH(ifp, &ifnet, if_link) { | |
1269 | if (strcmp(if_name, ifp->if_name) == 0 && unit == ifp->if_unit) { | |
1270 | return (ifp); | |
1271 | } | |
1272 | } | |
1273 | return (ifp); | |
1274 | } | |
1275 | ||
1276 | static void | |
1277 | interface_link_event(struct ifnet * ifp, u_long event_code) | |
1278 | { | |
1279 | struct { | |
1280 | struct kern_event_msg header; | |
1281 | u_long unit; | |
1282 | char if_name[IFNAMSIZ]; | |
1283 | } event; | |
1284 | ||
1285 | event.header.total_size = sizeof(event); | |
1286 | event.header.vendor_code = KEV_VENDOR_APPLE; | |
1287 | event.header.kev_class = KEV_NETWORK_CLASS; | |
1288 | event.header.kev_subclass = KEV_DL_SUBCLASS; | |
1289 | event.header.event_code = event_code; | |
1290 | event.header.event_data[0] = ifp->if_family; | |
1291 | event.unit = (u_long) ifp->if_unit; | |
1292 | strncpy(event.if_name, ifp->if_name, IFNAMSIZ); | |
1293 | dlil_event(ifp, &event.header); | |
1294 | return; | |
1295 | } | |
1296 | ||
1297 | static void | |
1298 | parent_link_event(struct ifnet * p, u_long event_code) | |
1299 | { | |
1300 | struct ifvlan * ifv; | |
1301 | ||
1302 | LIST_FOREACH(ifv, &ifv_list, ifv_list) { | |
1303 | if (p == ifv->ifv_p) { | |
1304 | interface_link_event(ifv->ifv_ifp, event_code); | |
1305 | } | |
1306 | } | |
1307 | return; | |
1308 | ||
1309 | } | |
1310 | ||
1311 | /* | |
1312 | * Function: vlan_dl_event | |
1313 | * Purpose: | |
1314 | * Process DLIL events that interest us. Currently, that is | |
1315 | * just the interface UP and DOWN. Ideally, this would also | |
1316 | * include the KEV_DL_IF_DETACH{ING} messages, which would eliminate | |
1317 | * the need for an interface filter. | |
1318 | */ | |
1319 | static int | |
1320 | vlan_dl_event(struct kern_event_msg * event, u_long dl_tag) | |
1321 | { | |
1322 | struct ifnet * p; | |
1323 | struct net_event_data * net_event; | |
1324 | ||
1325 | if (event->vendor_code != KEV_VENDOR_APPLE | |
1326 | || event->kev_class != KEV_NETWORK_CLASS | |
1327 | || event->kev_subclass != KEV_DL_SUBCLASS) { | |
1328 | goto done; | |
1329 | } | |
1330 | net_event = (struct net_event_data *)(event->event_data); | |
1331 | switch (event->event_code) { | |
1332 | case KEV_DL_LINK_OFF: | |
1333 | case KEV_DL_LINK_ON: | |
1334 | p = find_if_name_unit(net_event->if_name, net_event->if_unit); | |
1335 | if (p != NULL) { | |
1336 | parent_link_event(p, event->event_code); | |
1c79356b | 1337 | } |
4a249263 A |
1338 | break; |
1339 | #if 0 | |
1340 | case KEV_DL_IF_DETACHING: | |
1341 | case KEV_DL_IF_DETACHED: | |
1342 | /* we don't get these, unfortunately */ | |
1343 | break; | |
1344 | #endif 0 | |
1345 | default: | |
1346 | break; | |
1347 | } | |
1348 | ||
1349 | done: | |
1350 | return (0); | |
1351 | } | |
1352 | ||
1353 | /* | |
1354 | * Function: vlan_attach_protocol | |
1355 | * Purpose: | |
1356 | * Attach a DLIL protocol to the interface, using the ETHERTYPE_VLAN | |
1357 | * demux ether type. We're not a real protocol, we'll never receive | |
1358 | * any packets because they're intercepted by ether_demux before | |
1359 | * our input routine would be called. | |
1360 | * | |
1361 | * The reasons for attaching a protocol to the interface are: | |
1362 | * 1) add a protocol reference to the interface so that the underlying | |
1363 | * interface automatically gets marked up while we're attached | |
1364 | * 2) receive link status events which we can propagate to our | |
1365 | * VLAN interfaces. | |
1366 | */ | |
1367 | static int | |
1368 | vlan_attach_protocol(struct ifnet *ifp) | |
1369 | { | |
1370 | struct dlil_demux_desc desc; | |
1371 | u_long dl_tag; | |
1372 | u_short en_native = ETHERTYPE_VLAN; | |
1373 | int error; | |
1374 | int i; | |
1375 | struct dlil_proto_reg_str reg; | |
1376 | ||
1377 | TAILQ_INIT(®.demux_desc_head); | |
1378 | desc.type = DLIL_DESC_RAW; | |
1379 | desc.variants.bitmask.proto_id_length = 0; | |
1380 | desc.variants.bitmask.proto_id = 0; | |
1381 | desc.variants.bitmask.proto_id_mask = 0; | |
1382 | desc.native_type = (char *) &en_native; | |
1383 | TAILQ_INSERT_TAIL(®.demux_desc_head, &desc, next); | |
1384 | reg.interface_family = ifp->if_family; | |
1385 | reg.unit_number = ifp->if_unit; | |
1386 | reg.input = vlan_proto_input; | |
1387 | reg.pre_output = 0; | |
1388 | reg.event = vlan_dl_event; | |
1389 | reg.offer = 0; | |
1390 | reg.ioctl = 0; | |
1391 | reg.default_proto = 0; | |
1392 | reg.protocol_family = VLAN_PROTO_FAMILY; | |
1393 | ||
1394 | error = dlil_attach_protocol(®, &dl_tag); | |
1395 | if (error) { | |
1396 | printf("vlan_proto_attach(%s%d) dlil_attach_protocol failed, %d\n", | |
1397 | ifp->if_name, ifp->if_unit, error); | |
1398 | } | |
1399 | return (error); | |
1400 | } | |
1401 | ||
1402 | /* | |
1403 | * Function: vlan_detach_protocol | |
1404 | * Purpose: | |
1405 | * Detach our DLIL protocol from an interface | |
1406 | */ | |
1407 | static int | |
1408 | vlan_detach_protocol(struct ifnet *ifp) | |
1409 | { | |
1410 | u_long dl_tag; | |
1411 | int error; | |
1412 | ||
1413 | error = dlil_find_dltag(ifp->if_family, ifp->if_unit, | |
1414 | VLAN_PROTO_FAMILY, &dl_tag); | |
1415 | if (error) { | |
1416 | printf("vlan_proto_detach(%s%d) dlil_find_dltag failed, %d\n", | |
1417 | ifp->if_name, ifp->if_unit, error); | |
1418 | } else { | |
1419 | error = dlil_detach_protocol(dl_tag); | |
1420 | if (error) { | |
1421 | printf("vlan_proto_detach(%s%d) dlil_detach_protocol failed, %d\n", | |
1422 | ifp->if_name, ifp->if_unit, error); | |
1423 | } | |
1424 | } | |
1425 | return (error); | |
1426 | } | |
1427 | ||
1428 | /* | |
1429 | * DLIL interface family functions | |
1430 | * We use the ethernet dlil functions, since that's all we support. | |
1431 | * If we wanted to handle multiple LAN types (tokenring, etc.), we'd | |
1432 | * call the appropriate routines for that LAN type instead of hard-coding | |
1433 | * ethernet. | |
1434 | */ | |
1435 | extern int ether_add_if(struct ifnet *ifp); | |
1436 | extern int ether_del_if(struct ifnet *ifp); | |
1437 | extern int ether_init_if(struct ifnet *ifp); | |
1438 | extern int ether_add_proto(struct ddesc_head_str *desc_head, | |
1439 | struct if_proto *proto, u_long dl_tag); | |
1440 | extern int ether_del_proto(struct if_proto *proto, u_long dl_tag); | |
1441 | extern int ether_ifmod_ioctl(struct ifnet *ifp, u_long command, | |
1442 | caddr_t data); | |
1443 | extern int ether_del_proto(struct if_proto *proto, u_long dl_tag); | |
1444 | extern int ether_add_proto(struct ddesc_head_str *desc_head, struct if_proto *proto, u_long dl_tag); | |
1445 | ||
1446 | extern int ether_attach_inet(struct ifnet *ifp, u_long *dl_tag); | |
1447 | extern int ether_detach_inet(struct ifnet *ifp, u_long dl_tag); | |
1448 | extern int ether_attach_inet6(struct ifnet *ifp, u_long *dl_tag); | |
1449 | extern int ether_detach_inet6(struct ifnet *ifp, u_long dl_tag); | |
1450 | ||
1451 | static int | |
1452 | vlan_attach_inet(struct ifnet *ifp, u_long *dl_tag) | |
1453 | { | |
1454 | return (ether_attach_inet(ifp, dl_tag)); | |
1455 | } | |
1456 | ||
1457 | static int | |
1458 | vlan_detach_inet(struct ifnet *ifp, u_long dl_tag) | |
1459 | { | |
1460 | return (ether_detach_inet(ifp, dl_tag)); | |
1461 | } | |
1462 | ||
1463 | static int | |
1464 | vlan_attach_inet6(struct ifnet *ifp, u_long *dl_tag) | |
1465 | { | |
1466 | return (ether_attach_inet6(ifp, dl_tag)); | |
1467 | } | |
1468 | ||
1469 | static int | |
1470 | vlan_detach_inet6(struct ifnet *ifp, u_long dl_tag) | |
1471 | { | |
1472 | return (ether_detach_inet6(ifp, dl_tag)); | |
1473 | } | |
1474 | ||
1475 | static int | |
1476 | vlan_add_if(struct ifnet *ifp) | |
1477 | { | |
1478 | return (ether_add_if(ifp)); | |
1c79356b A |
1479 | } |
1480 | ||
4a249263 A |
1481 | static int |
1482 | vlan_del_if(struct ifnet *ifp) | |
1483 | { | |
1484 | return (ether_del_if(ifp)); | |
1485 | } | |
1486 | ||
1487 | static int | |
1488 | vlan_init_if(struct ifnet *ifp) | |
1489 | { | |
1490 | return (0); | |
1491 | } | |
1492 | ||
1493 | static int | |
1494 | vlan_shutdown() | |
1495 | { | |
1496 | return 0; | |
1497 | } | |
1498 | ||
1499 | __private_extern__ int | |
1500 | vlan_family_init() | |
1501 | { | |
1502 | int i, error=0; | |
1503 | struct dlil_ifmod_reg_str ifmod_reg; | |
1504 | struct dlil_protomod_reg_str vlan_protoreg; | |
1505 | ||
1506 | #if 0 | |
1507 | /* VLAN family is built-in, called from ether_family_init */ | |
1508 | thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL); | |
1509 | #endif 0 | |
1510 | ||
1511 | bzero(&ifmod_reg, sizeof(ifmod_reg)); | |
1512 | ifmod_reg.add_if = vlan_add_if; | |
1513 | ifmod_reg.del_if = vlan_del_if; | |
1514 | ifmod_reg.init_if = vlan_init_if; | |
1515 | ifmod_reg.add_proto = ether_add_proto; | |
1516 | ifmod_reg.del_proto = ether_del_proto; | |
1517 | ifmod_reg.ifmod_ioctl = ether_ifmod_ioctl; | |
1518 | ifmod_reg.shutdown = vlan_shutdown; | |
1519 | ||
1520 | if (dlil_reg_if_modules(APPLE_IF_FAM_VLAN, &ifmod_reg)) { | |
1521 | printf("WARNING: vlan_family_init -- " | |
1522 | "Can't register if family modules\n"); | |
1523 | error = EIO; | |
1524 | goto done; | |
1525 | } | |
1526 | ||
1527 | /* Register protocol registration functions */ | |
1528 | bzero(&vlan_protoreg, sizeof(vlan_protoreg)); | |
1529 | vlan_protoreg.attach_proto = vlan_attach_inet; | |
1530 | vlan_protoreg.detach_proto = vlan_detach_inet; | |
1531 | ||
1532 | if (error = dlil_reg_proto_module(PF_INET, APPLE_IF_FAM_VLAN, | |
1533 | &vlan_protoreg) != 0) { | |
1534 | kprintf("dlil_reg_proto_module failed for AF_INET6 error=%d\n", | |
1535 | error); | |
1536 | goto done; | |
1537 | } | |
1538 | vlan_protoreg.attach_proto = vlan_attach_inet6; | |
1539 | vlan_protoreg.detach_proto = vlan_detach_inet6; | |
1540 | ||
1541 | if (error = dlil_reg_proto_module(PF_INET6, APPLE_IF_FAM_VLAN, | |
1542 | &vlan_protoreg) != 0) { | |
1543 | kprintf("dlil_reg_proto_module failed for AF_INET6 error=%d\n", | |
1544 | error); | |
1545 | goto done; | |
1546 | } | |
1547 | vlan_clone_attach(); | |
1548 | ||
1549 | done: | |
1550 | #if 0 | |
1551 | thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL); | |
1552 | #endif 0 | |
1553 | return (error); | |
1554 | } |