Commit | Line | Data |
---|---|---|
984263bc MD |
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: src/sys/net/if_vlan.c,v 1.15.2.13 2003/02/14 22:25:58 fenner Exp $ | |
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 queue when it calls | |
40 | * if_start(), rewrite them for use by the real outgoing interface, | |
41 | * and ask it to send them. | |
082c62ad SZ |
42 | * |
43 | * | |
44 | * Note about vlan's MP safe approach: | |
45 | * | |
46 | * - All configuration operation, e.g. config, unconfig and change flags, | |
47 | * is serialized by netisr0; not by vlan's serializer | |
48 | * | |
49 | * - Parent interface's trunk and vlans are linked in the following | |
50 | * fashion: | |
51 | * CPU0 CPU1 CPU2 CPU3 | |
52 | * +--------------+--------+--------+--------+--------+ | |
53 | * | parent ifnet |trunk[0]|trunk[1]|trunk[2]|trunk[3]| | |
54 | * +--------------+--------+--------+--------+--------+ | |
55 | * | | | | | |
56 | * V V V V | |
57 | * +--------------+--------+--------+--------+--------+ | |
58 | * | vlan ifnet |entry[0]|entry[1]|entry[2]|entry[3]| | |
59 | * +--------------+--------+--------+--------+--------+ | |
60 | * | | | | | |
61 | * V V V V | |
62 | * +--------------+--------+--------+--------+--------+ | |
63 | * | vlan ifnet |entry[0]|entry[1]|entry[2]|entry[3]| | |
64 | * +--------------+--------+--------+--------+--------+ | |
65 | * | |
66 | * - Vlan is linked/unlinked onto parent interface's trunk using following | |
67 | * way: | |
68 | * | |
f658941a | 69 | * CPU0 CPU1 CPU2 CPU3 |
082c62ad | 70 | * |
f658941a SZ |
71 | * netisr0 <----------------------------------------------+ |
72 | * (config/unconfig) | | |
73 | * | | | |
74 | * | domsg | replymsg | |
75 | * : (link/unlink) | | |
76 | * : | | |
77 | * : fwdmsg fwdmsg fwdmsg | | |
78 | * :-----------> netisr1 --------> netisr2 --------> netisr3 | |
79 | * (link/unlink) (link/unlink) (link/unlink) | |
082c62ad SZ |
80 | * |
81 | * - Parent interface's trunk is destroyed in the following lockless way: | |
82 | * | |
83 | * old_trunk = ifp->if_vlantrunks; | |
84 | * ifp->if_vlantrunks = NULL; | |
85 | * netmsg_service_sync(); | |
86 | * (*) | |
87 | * free(old_trunk); | |
88 | * | |
89 | * Since all of the accessing of if_vlantrunks only happens in network | |
90 | * threads (percpu netisr and ifnet threads), after netmsg_service_sync() | |
91 | * the network threads are promised to see only NULL if_vlantrunks; we | |
92 | * are safe to free the "to be destroyed" parent interface's trunk | |
93 | * afterwards. | |
984263bc MD |
94 | */ |
95 | ||
96 | #ifndef NVLAN | |
1f2de5d4 | 97 | #include "use_vlan.h" |
984263bc MD |
98 | #endif |
99 | #include "opt_inet.h" | |
100 | ||
101 | #include <sys/param.h> | |
1f7ab7c9 | 102 | #include <sys/systm.h> |
984263bc MD |
103 | #include <sys/kernel.h> |
104 | #include <sys/malloc.h> | |
105 | #include <sys/mbuf.h> | |
106 | #include <sys/module.h> | |
107 | #include <sys/queue.h> | |
108 | #include <sys/socket.h> | |
109 | #include <sys/sockio.h> | |
110 | #include <sys/sysctl.h> | |
1f7ab7c9 | 111 | #include <sys/bus.h> |
126fc6c4 | 112 | #include <sys/thread2.h> |
984263bc MD |
113 | |
114 | #include <net/bpf.h> | |
115 | #include <net/ethernet.h> | |
116 | #include <net/if.h> | |
117 | #include <net/if_arp.h> | |
118 | #include <net/if_dl.h> | |
119 | #include <net/if_types.h> | |
4d723e5a | 120 | #include <net/ifq_var.h> |
65a24520 | 121 | #include <net/if_clone.h> |
83790f85 | 122 | #include <net/netmsg2.h> |
5337421c | 123 | #include <net/netisr2.h> |
984263bc MD |
124 | |
125 | #ifdef INET | |
126 | #include <netinet/in.h> | |
127 | #include <netinet/if_ether.h> | |
128 | #endif | |
129 | ||
4f7ea046 SZ |
130 | #include <net/vlan/if_vlan_var.h> |
131 | #include <net/vlan/if_vlan_ether.h> | |
132 | ||
9433ab01 SZ |
133 | struct ifvlan; |
134 | ||
83790f85 SZ |
135 | struct vlan_mc_entry { |
136 | struct ether_addr mc_addr; | |
137 | SLIST_ENTRY(vlan_mc_entry) mc_entries; | |
138 | }; | |
139 | ||
9433ab01 SZ |
140 | struct vlan_entry { |
141 | struct ifvlan *ifv; | |
142 | LIST_ENTRY(vlan_entry) ifv_link; | |
143 | }; | |
144 | ||
83790f85 SZ |
145 | struct ifvlan { |
146 | struct arpcom ifv_ac; /* make this an interface */ | |
147 | struct ifnet *ifv_p; /* parent inteface of this vlan */ | |
18f6e883 | 148 | int ifv_pflags; /* special flags we have set on parent */ |
83790f85 SZ |
149 | struct ifv_linkmib { |
150 | int ifvm_parent; | |
9433ab01 SZ |
151 | uint16_t ifvm_proto; /* encapsulation ethertype */ |
152 | uint16_t ifvm_tag; /* tag to apply on packets leaving if */ | |
83790f85 | 153 | } ifv_mib; |
9433ab01 | 154 | SLIST_HEAD(, vlan_mc_entry) vlan_mc_listhead; |
83790f85 | 155 | LIST_ENTRY(ifvlan) ifv_list; |
9433ab01 | 156 | struct vlan_entry ifv_entries[1]; |
83790f85 SZ |
157 | }; |
158 | #define ifv_if ifv_ac.ac_if | |
159 | #define ifv_tag ifv_mib.ifvm_tag | |
160 | ||
9433ab01 SZ |
161 | struct vlan_trunk { |
162 | LIST_HEAD(, vlan_entry) vlan_list; | |
163 | }; | |
164 | ||
165 | struct netmsg_vlan { | |
002c1265 | 166 | struct netmsg_base base; |
9433ab01 SZ |
167 | struct ifvlan *nv_ifv; |
168 | struct ifnet *nv_ifp_p; | |
169 | const char *nv_parent_name; | |
170 | uint16_t nv_vlantag; | |
171 | }; | |
172 | ||
984263bc | 173 | #define VLANNAME "vlan" |
984263bc MD |
174 | |
175 | SYSCTL_DECL(_net_link); | |
176 | SYSCTL_NODE(_net_link, IFT_L2VLAN, vlan, CTLFLAG_RW, 0, "IEEE 802.1Q VLAN"); | |
177 | SYSCTL_NODE(_net_link_vlan, PF_LINK, link, CTLFLAG_RW, 0, "for consistency"); | |
178 | ||
179 | static MALLOC_DEFINE(M_VLAN, "vlan", "802.1Q Virtual LAN Interface"); | |
984263bc MD |
180 | static LIST_HEAD(, ifvlan) ifv_list; |
181 | ||
bb54c3a2 | 182 | static int vlan_clone_create(struct if_clone *, int, caddr_t, caddr_t); |
a7e9152e | 183 | static int vlan_clone_destroy(struct ifnet *); |
9433ab01 SZ |
184 | static void vlan_ifdetach(void *, struct ifnet *); |
185 | ||
186 | static void vlan_init(void *); | |
f0a26983 | 187 | static void vlan_start(struct ifnet *, struct ifaltq_subque *); |
9433ab01 | 188 | static int vlan_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *); |
50098e2e | 189 | static void vlan_input(struct mbuf *); |
9433ab01 | 190 | |
18f6e883 SZ |
191 | static int vlan_setflags(struct ifvlan *, struct ifnet *, int); |
192 | static int vlan_setflag(struct ifvlan *, struct ifnet *, int, int, | |
193 | int (*)(struct ifnet *, int)); | |
194 | static int vlan_config_flags(struct ifvlan *ifv); | |
9433ab01 SZ |
195 | static void vlan_clrmulti(struct ifvlan *, struct ifnet *); |
196 | static int vlan_setmulti(struct ifvlan *, struct ifnet *); | |
197 | static int vlan_config_multi(struct ifvlan *); | |
198 | static int vlan_config(struct ifvlan *, const char *, uint16_t); | |
199 | static int vlan_unconfig(struct ifvlan *); | |
200 | static void vlan_link(struct ifvlan *, struct ifnet *); | |
201 | static void vlan_unlink(struct ifvlan *, struct ifnet *); | |
202 | ||
002c1265 MD |
203 | static void vlan_config_dispatch(netmsg_t); |
204 | static void vlan_unconfig_dispatch(netmsg_t); | |
205 | static void vlan_link_dispatch(netmsg_t); | |
206 | static void vlan_unlink_dispatch(netmsg_t); | |
207 | static void vlan_multi_dispatch(netmsg_t); | |
208 | static void vlan_flags_dispatch(netmsg_t); | |
209 | static void vlan_ifdetach_dispatch(netmsg_t); | |
9433ab01 | 210 | |
18f6e883 SZ |
211 | /* Special flags we should propagate to parent */ |
212 | static struct { | |
213 | int flag; | |
214 | int (*func)(struct ifnet *, int); | |
215 | } vlan_pflags[] = { | |
216 | { IFF_PROMISC, ifpromisc }, | |
217 | { IFF_ALLMULTI, if_allmulti }, | |
218 | { 0, NULL } | |
219 | }; | |
220 | ||
9433ab01 SZ |
221 | static eventhandler_tag vlan_ifdetach_cookie; |
222 | static struct if_clone vlan_cloner = | |
223 | IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy, | |
224 | NVLAN, IF_MAXUNIT); | |
225 | ||
18f6e883 SZ |
226 | /* |
227 | * Handle IFF_* flags that require certain changes on the parent: | |
228 | * if "set" is true, update parent's flags respective to our if_flags; | |
229 | * if "set" is false, forcedly clear the flags set on parent. | |
230 | */ | |
231 | static int | |
232 | vlan_setflags(struct ifvlan *ifv, struct ifnet *ifp_p, int set) | |
233 | { | |
234 | int error, i; | |
235 | ||
2c9effcf | 236 | ASSERT_IFNET_NOT_SERIALIZED_ALL(&ifv->ifv_if); |
18f6e883 SZ |
237 | |
238 | for (i = 0; vlan_pflags[i].func != NULL; i++) { | |
239 | error = vlan_setflag(ifv, ifp_p, vlan_pflags[i].flag, | |
240 | set, vlan_pflags[i].func); | |
241 | if (error) | |
242 | return error; | |
243 | } | |
244 | return 0; | |
245 | } | |
246 | ||
247 | /* Handle a reference counted flag that should be set on the parent as well */ | |
248 | static int | |
249 | vlan_setflag(struct ifvlan *ifv, struct ifnet *ifp_p, int flag, int set, | |
250 | int (*func)(struct ifnet *, int)) | |
251 | { | |
252 | struct ifnet *ifp = &ifv->ifv_if; | |
253 | int error, ifv_flag; | |
254 | ||
2c9effcf | 255 | ASSERT_IFNET_NOT_SERIALIZED_ALL(ifp); |
18f6e883 SZ |
256 | |
257 | ifv_flag = set ? (ifp->if_flags & flag) : 0; | |
258 | ||
259 | /* | |
260 | * See if recorded parent's status is different from what | |
261 | * we want it to be. If it is, flip it. We record parent's | |
262 | * status in ifv_pflags so that we won't clear parent's flag | |
263 | * we haven't set. In fact, we don't clear or set parent's | |
264 | * flags directly, but get or release references to them. | |
265 | * That's why we can be sure that recorded flags still are | |
266 | * in accord with actual parent's flags. | |
267 | */ | |
268 | if (ifv_flag != (ifv->ifv_pflags & flag)) { | |
269 | error = func(ifp_p, ifv_flag); | |
270 | if (error) | |
271 | return error; | |
272 | ifv->ifv_pflags &= ~flag; | |
273 | ifv->ifv_pflags |= ifv_flag; | |
274 | } | |
275 | return 0; | |
276 | } | |
277 | ||
984263bc MD |
278 | /* |
279 | * Program our multicast filter. What we're actually doing is | |
280 | * programming the multicast filter of the parent. This has the | |
281 | * side effect of causing the parent interface to receive multicast | |
282 | * traffic that it doesn't really want, which ends up being discarded | |
283 | * later by the upper protocol layers. Unfortunately, there's no way | |
284 | * to avoid this: there really is only one physical interface. | |
285 | */ | |
286 | static int | |
9433ab01 | 287 | vlan_setmulti(struct ifvlan *ifv, struct ifnet *ifp_p) |
984263bc | 288 | { |
72659ed0 | 289 | struct ifmultiaddr *ifma; |
9433ab01 SZ |
290 | struct vlan_mc_entry *mc = NULL; |
291 | struct sockaddr_dl sdl; | |
292 | struct ifnet *ifp = &ifv->ifv_if; | |
984263bc | 293 | |
2c9effcf | 294 | ASSERT_IFNET_NOT_SERIALIZED_ALL(ifp); |
984263bc MD |
295 | |
296 | /* | |
9433ab01 | 297 | * First, remove any existing filter entries. |
984263bc | 298 | */ |
9433ab01 | 299 | vlan_clrmulti(ifv, ifp_p); |
984263bc | 300 | |
9433ab01 | 301 | /* |
72659ed0 SZ |
302 | * Save the filter entries to be added to parent. |
303 | * | |
304 | * TODO: need ifnet_serialize_main | |
9433ab01 | 305 | */ |
72659ed0 | 306 | ifnet_serialize_all(ifp); |
441d34b2 | 307 | TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { |
984263bc MD |
308 | if (ifma->ifma_addr->sa_family != AF_LINK) |
309 | continue; | |
9433ab01 SZ |
310 | |
311 | /* Save a copy */ | |
efda3bd0 | 312 | mc = kmalloc(sizeof(struct vlan_mc_entry), M_VLAN, M_WAITOK); |
984263bc | 313 | bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), |
9433ab01 SZ |
314 | &mc->mc_addr, ETHER_ADDR_LEN); |
315 | SLIST_INSERT_HEAD(&ifv->vlan_mc_listhead, mc, mc_entries); | |
72659ed0 SZ |
316 | } |
317 | ifnet_deserialize_all(ifp); | |
9433ab01 | 318 | |
72659ed0 SZ |
319 | /* |
320 | * Now program new ones. | |
321 | */ | |
322 | bzero(&sdl, sizeof(sdl)); | |
323 | sdl.sdl_len = sizeof(sdl); | |
324 | sdl.sdl_family = AF_LINK; | |
325 | sdl.sdl_index = ifp_p->if_index; | |
326 | sdl.sdl_type = IFT_ETHER; | |
327 | sdl.sdl_alen = ETHER_ADDR_LEN; | |
328 | ||
329 | /* | |
330 | * Program the parent multicast filter | |
331 | */ | |
332 | SLIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries) { | |
333 | int error; | |
334 | ||
335 | bcopy(&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); | |
336 | error = if_addmulti(ifp_p, (struct sockaddr *)&sdl, NULL); | |
337 | if (error) { | |
338 | /* XXX probably should keep going */ | |
9433ab01 | 339 | return error; |
72659ed0 | 340 | } |
984263bc | 341 | } |
9433ab01 SZ |
342 | return 0; |
343 | } | |
344 | ||
345 | static void | |
346 | vlan_clrmulti(struct ifvlan *ifv, struct ifnet *ifp_p) | |
347 | { | |
348 | struct vlan_mc_entry *mc; | |
349 | struct sockaddr_dl sdl; | |
984263bc | 350 | |
2c9effcf | 351 | ASSERT_IFNET_NOT_SERIALIZED_ALL(&ifv->ifv_if); |
9433ab01 SZ |
352 | |
353 | bzero(&sdl, sizeof(sdl)); | |
354 | sdl.sdl_len = sizeof(sdl); | |
355 | sdl.sdl_family = AF_LINK; | |
356 | sdl.sdl_index = ifp_p->if_index; | |
357 | sdl.sdl_type = IFT_ETHER; | |
358 | sdl.sdl_alen = ETHER_ADDR_LEN; | |
359 | ||
360 | while ((mc = SLIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) { | |
361 | bcopy(&mc->mc_addr, LLADDR(&sdl), ETHER_ADDR_LEN); | |
362 | if_delmulti(ifp_p, (struct sockaddr *)&sdl); /* ignore error */ | |
363 | ||
364 | SLIST_REMOVE_HEAD(&ifv->vlan_mc_listhead, mc_entries); | |
365 | kfree(mc, M_VLAN); | |
366 | } | |
984263bc MD |
367 | } |
368 | ||
369 | static int | |
9433ab01 SZ |
370 | vlan_modevent(module_t mod, int type, void *data) |
371 | { | |
372 | switch (type) { | |
373 | case MOD_LOAD: | |
984263bc | 374 | LIST_INIT(&ifv_list); |
50098e2e | 375 | vlan_input_p = vlan_input; |
9433ab01 SZ |
376 | vlan_ifdetach_cookie = |
377 | EVENTHANDLER_REGISTER(ifnet_detach_event, | |
378 | vlan_ifdetach, NULL, | |
379 | EVENTHANDLER_PRI_ANY); | |
984263bc | 380 | if_clone_attach(&vlan_cloner); |
9433ab01 SZ |
381 | break; |
382 | ||
383 | case MOD_UNLOAD: | |
984263bc | 384 | if_clone_detach(&vlan_cloner); |
b327296f | 385 | |
50098e2e | 386 | vlan_input_p = NULL; |
b327296f | 387 | /* |
696880f7 | 388 | * Make sure that all protocol threads see vlan_input_p change. |
b327296f SZ |
389 | */ |
390 | netmsg_service_sync(); | |
391 | ||
9433ab01 SZ |
392 | EVENTHANDLER_DEREGISTER(ifnet_detach_event, |
393 | vlan_ifdetach_cookie); | |
984263bc MD |
394 | while (!LIST_EMPTY(&ifv_list)) |
395 | vlan_clone_destroy(&LIST_FIRST(&ifv_list)->ifv_if); | |
984263bc | 396 | break; |
9433ab01 SZ |
397 | } |
398 | return 0; | |
399 | } | |
984263bc | 400 | |
9433ab01 SZ |
401 | static moduledata_t vlan_mod = { |
402 | "if_vlan", | |
403 | vlan_modevent, | |
984263bc | 404 | 0 |
9433ab01 | 405 | }; |
984263bc MD |
406 | |
407 | DECLARE_MODULE(if_vlan, vlan_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); | |
408 | ||
9433ab01 | 409 | static void |
002c1265 | 410 | vlan_ifdetach_dispatch(netmsg_t msg) |
9433ab01 | 411 | { |
002c1265 | 412 | struct netmsg_vlan *vmsg = (struct netmsg_vlan *)msg; |
9433ab01 SZ |
413 | struct ifnet *ifp_p = vmsg->nv_ifp_p; |
414 | struct vlan_trunk *vlantrunks, *trunk; | |
415 | struct vlan_entry *ifve; | |
416 | ||
417 | vlantrunks = ifp_p->if_vlantrunks; | |
418 | if (vlantrunks == NULL) | |
419 | goto reply; | |
420 | trunk = &vlantrunks[mycpuid]; | |
421 | ||
422 | while (ifp_p->if_vlantrunks && | |
423 | (ifve = LIST_FIRST(&trunk->vlan_list)) != NULL) | |
91edeaed | 424 | { |
9433ab01 | 425 | vlan_unconfig(ifve->ifv); |
91edeaed | 426 | } |
9433ab01 | 427 | reply: |
002c1265 | 428 | lwkt_replymsg(&vmsg->base.lmsg, 0); |
9433ab01 SZ |
429 | } |
430 | ||
431 | static void | |
432 | vlan_ifdetach(void *arg __unused, struct ifnet *ifp) | |
433 | { | |
434 | struct netmsg_vlan vmsg; | |
9433ab01 | 435 | |
2c9effcf | 436 | ASSERT_IFNET_NOT_SERIALIZED_ALL(ifp); |
9433ab01 SZ |
437 | |
438 | bzero(&vmsg, sizeof(vmsg)); | |
9433ab01 | 439 | |
002c1265 | 440 | netmsg_init(&vmsg.base, NULL, &curthread->td_msgport, |
48e7b118 | 441 | 0, vlan_ifdetach_dispatch); |
9433ab01 SZ |
442 | vmsg.nv_ifp_p = ifp; |
443 | ||
ec7f7fc8 | 444 | lwkt_domsg(netisr_cpuport(0), &vmsg.base.lmsg, 0); |
9433ab01 SZ |
445 | } |
446 | ||
984263bc | 447 | static int |
bb54c3a2 AL |
448 | vlan_clone_create(struct if_clone *ifc, int unit, |
449 | caddr_t params __unused, caddr_t data __unused) | |
984263bc | 450 | { |
984263bc MD |
451 | struct ifvlan *ifv; |
452 | struct ifnet *ifp; | |
9433ab01 | 453 | int vlan_size, i; |
984263bc | 454 | |
9433ab01 | 455 | vlan_size = sizeof(struct ifvlan) |
83c2fc18 | 456 | + ((netisr_ncpus - 1) * sizeof(struct vlan_entry)); |
9433ab01 | 457 | ifv = kmalloc(vlan_size, M_VLAN, M_WAITOK | M_ZERO); |
984263bc | 458 | SLIST_INIT(&ifv->vlan_mc_listhead); |
83c2fc18 | 459 | for (i = 0; i < netisr_ncpus; ++i) |
9433ab01 | 460 | ifv->ifv_entries[i].ifv = ifv; |
984263bc | 461 | |
9433ab01 | 462 | crit_enter(); /* XXX not MP safe */ |
984263bc | 463 | LIST_INSERT_HEAD(&ifv_list, ifv, ifv_list); |
126fc6c4 | 464 | crit_exit(); |
984263bc | 465 | |
9433ab01 | 466 | ifp = &ifv->ifv_if; |
984263bc | 467 | ifp->if_softc = ifv; |
cdb7d804 | 468 | if_initname(ifp, "vlan", unit); |
984263bc MD |
469 | /* NB: flags are not set here */ |
470 | ifp->if_linkmib = &ifv->ifv_mib; | |
471 | ifp->if_linkmiblen = sizeof ifv->ifv_mib; | |
472 | /* NB: mtu is not set here */ | |
473 | ||
9433ab01 | 474 | ifp->if_init = vlan_init; |
984263bc MD |
475 | ifp->if_start = vlan_start; |
476 | ifp->if_ioctl = vlan_ioctl; | |
4d723e5a JS |
477 | ifq_set_maxlen(&ifp->if_snd, ifqmaxlen); |
478 | ifq_set_ready(&ifp->if_snd); | |
78195a76 | 479 | ether_ifattach(ifp, ifv->ifv_ac.ac_enaddr, NULL); |
984263bc MD |
480 | /* Now undo some of the damage... */ |
481 | ifp->if_data.ifi_type = IFT_L2VLAN; | |
482 | ifp->if_data.ifi_hdrlen = EVL_ENCAPLEN; | |
483 | ||
484 | return (0); | |
485 | } | |
486 | ||
a7e9152e | 487 | static int |
984263bc MD |
488 | vlan_clone_destroy(struct ifnet *ifp) |
489 | { | |
490 | struct ifvlan *ifv = ifp->if_softc; | |
984263bc | 491 | |
9433ab01 | 492 | crit_enter(); /* XXX not MP safe */ |
984263bc | 493 | LIST_REMOVE(ifv, ifv_list); |
126fc6c4 JS |
494 | crit_exit(); |
495 | ||
9433ab01 SZ |
496 | vlan_unconfig(ifv); |
497 | ether_ifdetach(ifp); | |
498 | ||
efda3bd0 | 499 | kfree(ifv, M_VLAN); |
a7e9152e SZ |
500 | |
501 | return 0; | |
984263bc MD |
502 | } |
503 | ||
504 | static void | |
9433ab01 | 505 | vlan_init(void *xsc) |
984263bc | 506 | { |
9433ab01 SZ |
507 | struct ifvlan *ifv = xsc; |
508 | struct ifnet *ifp = &ifv->ifv_if; | |
509 | ||
2c9effcf | 510 | ASSERT_IFNET_SERIALIZED_ALL(ifp); |
9433ab01 SZ |
511 | |
512 | if (ifv->ifv_p != NULL) | |
513 | ifp->if_flags |= IFF_RUNNING; | |
984263bc MD |
514 | } |
515 | ||
516 | static void | |
f0a26983 | 517 | vlan_start(struct ifnet *ifp, struct ifaltq_subque *ifsq) |
984263bc | 518 | { |
9433ab01 SZ |
519 | struct ifvlan *ifv = ifp->if_softc; |
520 | struct ifnet *ifp_p = ifv->ifv_p; | |
984263bc | 521 | struct mbuf *m; |
f0a26983 | 522 | lwkt_port_t p_port; |
984263bc | 523 | |
f0a26983 | 524 | ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq); |
bfefe4a6 | 525 | ASSERT_ALTQ_SQ_SERIALIZED_HW(ifsq); |
9433ab01 | 526 | |
c7ecdcdf | 527 | if (ifp_p == NULL) { |
f0a26983 | 528 | ifsq_purge(ifsq); |
c7ecdcdf SZ |
529 | return; |
530 | } | |
531 | ||
532 | if ((ifp->if_flags & IFF_RUNNING) == 0) | |
9433ab01 | 533 | return; |
984263bc | 534 | |
ec7f7fc8 | 535 | p_port = netisr_cpuport( |
f0a26983 | 536 | ifsq_get_cpuid(ifq_get_subq_default(&ifp_p->if_snd))); |
984263bc | 537 | for (;;) { |
83790f85 | 538 | struct netmsg_packet *nmp; |
83790f85 | 539 | |
ac9843a1 | 540 | m = ifsq_dequeue(ifsq); |
d2c71fa0 | 541 | if (m == NULL) |
984263bc | 542 | break; |
7600679e | 543 | BPF_MTAP(ifp, m); |
984263bc MD |
544 | |
545 | /* | |
546 | * Do not run parent's if_start() if the parent is not up, | |
547 | * or parent's driver will cause a system crash. | |
548 | */ | |
83790f85 SZ |
549 | if ((ifp_p->if_flags & (IFF_UP | IFF_RUNNING)) != |
550 | (IFF_UP | IFF_RUNNING)) { | |
984263bc | 551 | m_freem(m); |
d40991ef | 552 | IFNET_STAT_INC(ifp, collisions, 1); |
984263bc MD |
553 | continue; |
554 | } | |
555 | ||
4d723e5a | 556 | /* |
83790f85 SZ |
557 | * We need some way to tell the interface where the packet |
558 | * came from so that it knows how to find the VLAN tag to | |
559 | * use, so we set the ether_vlantag in the mbuf packet header | |
560 | * to our vlan tag. We also set the M_VLANTAG flag in the | |
561 | * mbuf to let the parent driver know that the ether_vlantag | |
562 | * is really valid. | |
4d723e5a | 563 | */ |
83790f85 SZ |
564 | m->m_pkthdr.ether_vlantag = ifv->ifv_tag; |
565 | m->m_flags |= M_VLANTAG; | |
4d723e5a | 566 | |
83790f85 | 567 | nmp = &m->m_hdr.mh_netmsg; |
984263bc | 568 | |
002c1265 | 569 | netmsg_init(&nmp->base, NULL, &netisr_apanic_rport, |
48e7b118 | 570 | 0, vlan_start_dispatch); |
83790f85 | 571 | nmp->nm_packet = m; |
002c1265 | 572 | nmp->base.lmsg.u.ms_resultp = ifp_p; |
83790f85 | 573 | |
f0a26983 | 574 | lwkt_sendmsg(p_port, &nmp->base.lmsg); |
d40991ef | 575 | IFNET_STAT_INC(ifp, opackets, 1); |
984263bc | 576 | } |
984263bc MD |
577 | } |
578 | ||
7af50411 | 579 | static void |
50098e2e | 580 | vlan_input(struct mbuf *m) |
7af50411 SZ |
581 | { |
582 | struct ifvlan *ifv = NULL; | |
b9ed4403 | 583 | struct ifnet *rcvif; |
7af50411 SZ |
584 | struct vlan_trunk *vlantrunks; |
585 | struct vlan_entry *entry; | |
83c2fc18 SZ |
586 | int cpuid = mycpuid; |
587 | ||
5204e13c | 588 | ASSERT_NETISR_NCPUS(cpuid); |
7af50411 SZ |
589 | |
590 | rcvif = m->m_pkthdr.rcvif; | |
591 | KKASSERT(m->m_flags & M_VLANTAG); | |
592 | ||
83c2fc18 | 593 | /* Make sure 'vlantrunks' is really used. */ |
91edeaed | 594 | vlantrunks = rcvif->if_vlantrunks; |
83c2fc18 | 595 | cpu_ccfence(); |
7af50411 | 596 | if (vlantrunks == NULL) { |
d40991ef | 597 | IFNET_STAT_INC(rcvif, noproto, 1); |
7af50411 SZ |
598 | m_freem(m); |
599 | return; | |
600 | } | |
601 | ||
91edeaed MD |
602 | /* |
603 | * Locate the associated vlan | |
604 | */ | |
83c2fc18 | 605 | LIST_FOREACH(entry, &vlantrunks[cpuid].vlan_list, ifv_link) { |
7af50411 | 606 | if (entry->ifv->ifv_tag == |
91edeaed MD |
607 | EVL_VLANOFTAG(m->m_pkthdr.ether_vlantag)) |
608 | { | |
7af50411 SZ |
609 | ifv = entry->ifv; |
610 | break; | |
611 | } | |
612 | } | |
7af50411 SZ |
613 | |
614 | /* | |
91edeaed MD |
615 | * Discard packets to unknown vlans, if the vlan interface is |
616 | * not completely initialized yet, or it is being destroyed. | |
7af50411 | 617 | */ |
7ed38756 | 618 | if (ifv == NULL || ifv->ifv_p != rcvif) { |
d40991ef | 619 | IFNET_STAT_INC(rcvif, noproto, 1); |
7af50411 SZ |
620 | m_freem(m); |
621 | return; | |
622 | } | |
7af50411 SZ |
623 | |
624 | /* | |
91edeaed MD |
625 | * Clear M_VLANTAG, then hand the vlan-stripped packet to the |
626 | * vlan(4) interface. | |
7af50411 SZ |
627 | */ |
628 | m->m_flags &= ~M_VLANTAG; | |
629 | ||
4ee4f753 | 630 | ether_reinput_oncpu(&ifv->ifv_if, m, REINPUT_RUNBPF); |
7af50411 SZ |
631 | } |
632 | ||
9433ab01 | 633 | static void |
002c1265 | 634 | vlan_link_dispatch(netmsg_t msg) |
9433ab01 | 635 | { |
002c1265 | 636 | struct netmsg_vlan *vmsg = (struct netmsg_vlan *)msg; |
9433ab01 SZ |
637 | struct ifvlan *ifv = vmsg->nv_ifv; |
638 | struct ifnet *ifp_p = vmsg->nv_ifp_p; | |
639 | struct vlan_entry *entry; | |
640 | struct vlan_trunk *vlantrunks, *trunk; | |
641 | int cpu = mycpuid; | |
642 | ||
643 | vlantrunks = ifp_p->if_vlantrunks; | |
644 | KASSERT(vlantrunks != NULL, | |
ed20d0e3 | 645 | ("vlan trunk has not been initialized yet")); |
9433ab01 SZ |
646 | |
647 | entry = &ifv->ifv_entries[cpu]; | |
648 | trunk = &vlantrunks[cpu]; | |
649 | ||
91edeaed MD |
650 | /* |
651 | * Critical section protects per-cpu list | |
652 | */ | |
9433ab01 SZ |
653 | crit_enter(); |
654 | LIST_INSERT_HEAD(&trunk->vlan_list, entry, ifv_link); | |
655 | crit_exit(); | |
656 | ||
83c2fc18 | 657 | netisr_forwardmsg(&vmsg->base, cpu + 1); |
9433ab01 SZ |
658 | } |
659 | ||
660 | static void | |
661 | vlan_link(struct ifvlan *ifv, struct ifnet *ifp_p) | |
984263bc | 662 | { |
9433ab01 | 663 | struct netmsg_vlan vmsg; |
9433ab01 SZ |
664 | |
665 | /* Assert in netisr0 */ | |
2c9effcf | 666 | ASSERT_IFNET_NOT_SERIALIZED_ALL(&ifv->ifv_if); |
9433ab01 SZ |
667 | |
668 | if (ifp_p->if_vlantrunks == NULL) { | |
669 | struct vlan_trunk *vlantrunks; | |
670 | int i; | |
671 | ||
91edeaed MD |
672 | vlantrunks = kmalloc(sizeof(*vlantrunks) * netisr_ncpus, |
673 | M_VLAN, | |
9433ab01 | 674 | M_WAITOK | M_ZERO); |
83c2fc18 | 675 | for (i = 0; i < netisr_ncpus; ++i) |
9433ab01 SZ |
676 | LIST_INIT(&vlantrunks[i].vlan_list); |
677 | ||
678 | ifp_p->if_vlantrunks = vlantrunks; | |
679 | } | |
680 | ||
681 | bzero(&vmsg, sizeof(vmsg)); | |
9433ab01 | 682 | |
002c1265 | 683 | netmsg_init(&vmsg.base, NULL, &curthread->td_msgport, |
48e7b118 | 684 | 0, vlan_link_dispatch); |
9433ab01 SZ |
685 | vmsg.nv_ifv = ifv; |
686 | vmsg.nv_ifp_p = ifp_p; | |
687 | ||
a8778f91 | 688 | netisr_domsg(&vmsg.base, 0); |
9433ab01 SZ |
689 | } |
690 | ||
691 | static void | |
002c1265 | 692 | vlan_config_dispatch(netmsg_t msg) |
9433ab01 | 693 | { |
002c1265 | 694 | struct netmsg_vlan *vmsg = (struct netmsg_vlan *)msg; |
9433ab01 SZ |
695 | struct ifvlan *ifv; |
696 | struct ifnet *ifp_p, *ifp; | |
984263bc | 697 | struct sockaddr_dl *sdl1, *sdl2; |
9433ab01 SZ |
698 | int error; |
699 | ||
700 | /* Assert in netisr0 */ | |
701 | ||
b4051e25 | 702 | ifp_p = ifunit_netisr(vmsg->nv_parent_name); |
9433ab01 SZ |
703 | if (ifp_p == NULL) { |
704 | error = ENOENT; | |
705 | goto reply; | |
706 | } | |
707 | ||
708 | if (ifp_p->if_data.ifi_type != IFT_ETHER) { | |
709 | error = EPROTONOSUPPORT; | |
710 | goto reply; | |
711 | } | |
712 | ||
713 | ifv = vmsg->nv_ifv; | |
714 | ifp = &ifv->ifv_if; | |
984263bc | 715 | |
9433ab01 SZ |
716 | if (ifv->ifv_p) { |
717 | error = EBUSY; | |
718 | goto reply; | |
719 | } | |
720 | ||
721 | /* Link vlan into parent's vlantrunk */ | |
722 | vlan_link(ifv, ifp_p); | |
723 | ||
a3dd34d2 | 724 | ifnet_serialize_all(ifp); |
9433ab01 SZ |
725 | |
726 | ifv->ifv_tag = vmsg->nv_vlantag; | |
727 | if (ifp_p->if_capenable & IFCAP_VLAN_MTU) | |
728 | ifp->if_mtu = ifp_p->if_mtu; | |
984263bc | 729 | else |
9433ab01 | 730 | ifp->if_mtu = ifp_p->if_data.ifi_mtu - EVL_ENCAPLEN; |
984263bc MD |
731 | |
732 | /* | |
733 | * Copy only a selected subset of flags from the parent. | |
734 | * Other flags are none of our business. | |
735 | */ | |
18f6e883 SZ |
736 | #define VLAN_INHERIT_FLAGS (IFF_BROADCAST | IFF_MULTICAST | \ |
737 | IFF_SIMPLEX | IFF_POINTOPOINT) | |
738 | ||
739 | ifp->if_flags &= ~VLAN_INHERIT_FLAGS; | |
740 | ifp->if_flags |= (ifp_p->if_flags & VLAN_INHERIT_FLAGS); | |
741 | ||
742 | #undef VLAN_INHERIT_FLAGS | |
984263bc MD |
743 | |
744 | /* | |
745 | * Set up our ``Ethernet address'' to reflect the underlying | |
746 | * physical interface's. | |
747 | */ | |
9433ab01 SZ |
748 | sdl1 = IF_LLSOCKADDR(ifp); |
749 | sdl2 = IF_LLSOCKADDR(ifp_p); | |
984263bc MD |
750 | sdl1->sdl_type = IFT_ETHER; |
751 | sdl1->sdl_alen = ETHER_ADDR_LEN; | |
752 | bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN); | |
753 | bcopy(LLADDR(sdl2), ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); | |
754 | ||
9433ab01 SZ |
755 | /* |
756 | * Release vlan's serializer before reprogramming parent's | |
757 | * multicast filter to avoid possible dead lock. | |
758 | */ | |
a3dd34d2 | 759 | ifnet_deserialize_all(ifp); |
9433ab01 | 760 | |
984263bc MD |
761 | /* |
762 | * Configure multicast addresses that may already be | |
763 | * joined on the vlan device. | |
764 | */ | |
9433ab01 | 765 | vlan_setmulti(ifv, ifp_p); |
984263bc | 766 | |
18f6e883 SZ |
767 | /* |
768 | * Set flags on the parent, if necessary. | |
769 | */ | |
770 | vlan_setflags(ifv, ifp_p, 1); | |
771 | ||
9433ab01 SZ |
772 | /* |
773 | * Connect to parent after everything have been set up, | |
774 | * so input/output could know that vlan is ready to go | |
775 | */ | |
776 | ifv->ifv_p = ifp_p; | |
777 | error = 0; | |
778 | reply: | |
002c1265 | 779 | lwkt_replymsg(&vmsg->base.lmsg, error); |
984263bc MD |
780 | } |
781 | ||
782 | static int | |
9433ab01 SZ |
783 | vlan_config(struct ifvlan *ifv, const char *parent_name, uint16_t vlantag) |
784 | { | |
785 | struct netmsg_vlan vmsg; | |
9433ab01 | 786 | |
2c9effcf | 787 | ASSERT_IFNET_NOT_SERIALIZED_ALL(&ifv->ifv_if); |
9433ab01 SZ |
788 | |
789 | bzero(&vmsg, sizeof(vmsg)); | |
9433ab01 | 790 | |
002c1265 | 791 | netmsg_init(&vmsg.base, NULL, &curthread->td_msgport, |
48e7b118 | 792 | 0, vlan_config_dispatch); |
9433ab01 SZ |
793 | vmsg.nv_ifv = ifv; |
794 | vmsg.nv_parent_name = parent_name; | |
795 | vmsg.nv_vlantag = vlantag; | |
796 | ||
ec7f7fc8 | 797 | return lwkt_domsg(netisr_cpuport(0), &vmsg.base.lmsg, 0); |
9433ab01 SZ |
798 | } |
799 | ||
800 | static void | |
002c1265 | 801 | vlan_unlink_dispatch(netmsg_t msg) |
9433ab01 | 802 | { |
002c1265 | 803 | struct netmsg_vlan *vmsg = (struct netmsg_vlan *)msg; |
9433ab01 | 804 | struct ifvlan *ifv = vmsg->nv_ifv; |
9433ab01 SZ |
805 | struct vlan_entry *entry; |
806 | int cpu = mycpuid; | |
807 | ||
2700fbed | 808 | KASSERT(vmsg->nv_ifp_p->if_vlantrunks != NULL, |
ed20d0e3 | 809 | ("vlan trunk has not been initialized yet")); |
9433ab01 SZ |
810 | entry = &ifv->ifv_entries[cpu]; |
811 | ||
812 | crit_enter(); | |
813 | LIST_REMOVE(entry, ifv_link); | |
814 | crit_exit(); | |
815 | ||
83c2fc18 | 816 | netisr_forwardmsg(&vmsg->base, cpu + 1); |
9433ab01 SZ |
817 | } |
818 | ||
819 | static void | |
820 | vlan_unlink(struct ifvlan *ifv, struct ifnet *ifp_p) | |
984263bc | 821 | { |
9433ab01 SZ |
822 | struct vlan_trunk *vlantrunks = ifp_p->if_vlantrunks; |
823 | struct netmsg_vlan vmsg; | |
9433ab01 SZ |
824 | |
825 | /* Assert in netisr0 */ | |
2c9effcf | 826 | ASSERT_IFNET_NOT_SERIALIZED_ALL(&ifv->ifv_if); |
9433ab01 SZ |
827 | |
828 | KASSERT(ifp_p->if_vlantrunks != NULL, | |
ed20d0e3 | 829 | ("vlan trunk has not been initialized yet")); |
9433ab01 SZ |
830 | |
831 | bzero(&vmsg, sizeof(vmsg)); | |
9433ab01 | 832 | |
002c1265 | 833 | netmsg_init(&vmsg.base, NULL, &curthread->td_msgport, |
48e7b118 | 834 | 0, vlan_unlink_dispatch); |
9433ab01 SZ |
835 | vmsg.nv_ifv = ifv; |
836 | vmsg.nv_ifp_p = ifp_p; | |
837 | ||
a8778f91 | 838 | netisr_domsg(&vmsg.base, 0); |
9433ab01 SZ |
839 | |
840 | crit_enter(); | |
841 | if (LIST_EMPTY(&vlantrunks[mycpuid].vlan_list)) { | |
9433ab01 | 842 | ifp_p->if_vlantrunks = NULL; |
297c8124 SZ |
843 | |
844 | /* | |
696880f7 | 845 | * Make sure that all protocol threads see if_vlantrunks change. |
297c8124 | 846 | */ |
9433ab01 SZ |
847 | netmsg_service_sync(); |
848 | kfree(vlantrunks, M_VLAN); | |
9433ab01 SZ |
849 | } |
850 | crit_exit(); | |
851 | } | |
852 | ||
853 | static void | |
002c1265 | 854 | vlan_unconfig_dispatch(netmsg_t msg) |
9433ab01 | 855 | { |
002c1265 | 856 | struct netmsg_vlan *vmsg = (struct netmsg_vlan *)msg; |
984263bc | 857 | struct sockaddr_dl *sdl; |
984263bc | 858 | struct ifvlan *ifv; |
9433ab01 | 859 | struct ifnet *ifp_p, *ifp; |
984263bc MD |
860 | int error; |
861 | ||
9433ab01 SZ |
862 | /* Assert in netisr0 */ |
863 | ||
864 | ifv = vmsg->nv_ifv; | |
865 | ifp = &ifv->ifv_if; | |
866 | ||
867 | if (ifp->if_flags & IFF_UP) | |
868 | if_down(ifp); | |
984263bc | 869 | |
a3dd34d2 | 870 | ifnet_serialize_all(ifp); |
9433ab01 SZ |
871 | |
872 | ifp->if_flags &= ~IFF_RUNNING; | |
873 | ||
874 | /* | |
875 | * Save parent ifnet pointer and disconnect from parent. | |
876 | * | |
877 | * This is done early in this function, so input/output could | |
878 | * know that we are disconnecting. | |
879 | */ | |
880 | ifp_p = ifv->ifv_p; | |
881 | ifv->ifv_p = NULL; | |
882 | ||
883 | /* | |
884 | * Release vlan's serializer before reprogramming parent's | |
885 | * multicast filter to avoid possible dead lock. | |
886 | */ | |
a3dd34d2 | 887 | ifnet_deserialize_all(ifp); |
984263bc | 888 | |
9433ab01 | 889 | if (ifp_p) { |
984263bc MD |
890 | /* |
891 | * Since the interface is being unconfigured, we need to | |
892 | * empty the list of multicast groups that we may have joined | |
893 | * while we were alive from the parent's list. | |
894 | */ | |
9433ab01 | 895 | vlan_clrmulti(ifv, ifp_p); |
18f6e883 SZ |
896 | |
897 | /* Clear parent's flags which was set by us. */ | |
898 | vlan_setflags(ifv, ifp_p, 0); | |
984263bc MD |
899 | } |
900 | ||
a3dd34d2 | 901 | ifnet_serialize_all(ifp); |
9433ab01 SZ |
902 | |
903 | ifp->if_mtu = ETHERMTU; | |
984263bc MD |
904 | |
905 | /* Clear our MAC address. */ | |
9433ab01 | 906 | sdl = IF_LLSOCKADDR(ifp); |
984263bc MD |
907 | sdl->sdl_type = IFT_ETHER; |
908 | sdl->sdl_alen = ETHER_ADDR_LEN; | |
909 | bzero(LLADDR(sdl), ETHER_ADDR_LEN); | |
910 | bzero(ifv->ifv_ac.ac_enaddr, ETHER_ADDR_LEN); | |
911 | ||
a3dd34d2 | 912 | ifnet_deserialize_all(ifp); |
9433ab01 SZ |
913 | |
914 | /* Unlink vlan from parent's vlantrunk */ | |
915 | if (ifp_p != NULL && ifp_p->if_vlantrunks != NULL) | |
916 | vlan_unlink(ifv, ifp_p); | |
917 | ||
918 | error = 0; | |
002c1265 | 919 | lwkt_replymsg(&vmsg->base.lmsg, error); |
9433ab01 SZ |
920 | } |
921 | ||
922 | static int | |
923 | vlan_unconfig(struct ifvlan *ifv) | |
924 | { | |
925 | struct netmsg_vlan vmsg; | |
9433ab01 | 926 | |
2c9effcf | 927 | ASSERT_IFNET_NOT_SERIALIZED_ALL(&ifv->ifv_if); |
9433ab01 SZ |
928 | |
929 | bzero(&vmsg, sizeof(vmsg)); | |
9433ab01 | 930 | |
002c1265 | 931 | netmsg_init(&vmsg.base, NULL, &curthread->td_msgport, |
48e7b118 | 932 | 0, vlan_unconfig_dispatch); |
9433ab01 SZ |
933 | vmsg.nv_ifv = ifv; |
934 | ||
ec7f7fc8 | 935 | return lwkt_domsg(netisr_cpuport(0), &vmsg.base.lmsg, 0); |
984263bc MD |
936 | } |
937 | ||
938 | static int | |
bd4539cc | 939 | vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) |
984263bc | 940 | { |
9433ab01 SZ |
941 | struct ifvlan *ifv = ifp->if_softc; |
942 | struct ifreq *ifr = (struct ifreq *)data; | |
943 | struct ifnet *ifp_p; | |
984263bc MD |
944 | struct vlanreq vlr; |
945 | int error = 0; | |
946 | ||
2c9effcf | 947 | ASSERT_IFNET_SERIALIZED_ALL(ifp); |
126fc6c4 | 948 | |
984263bc | 949 | switch (cmd) { |
984263bc | 950 | case SIOCGIFMEDIA: |
9433ab01 SZ |
951 | ifp_p = ifv->ifv_p; |
952 | if (ifp_p != NULL) { | |
7d50fe58 SZ |
953 | /* |
954 | * Release vlan interface's serializer to void | |
955 | * possible dead lock. | |
956 | */ | |
a3dd34d2 | 957 | ifnet_deserialize_all(ifp); |
9433ab01 | 958 | |
a3dd34d2 | 959 | ifnet_serialize_all(ifp_p); |
9433ab01 | 960 | error = ifp_p->if_ioctl(ifp_p, SIOCGIFMEDIA, data, cr); |
a3dd34d2 | 961 | ifnet_deserialize_all(ifp_p); |
9433ab01 | 962 | |
a3dd34d2 | 963 | ifnet_serialize_all(ifp); |
7d50fe58 | 964 | |
f01abbe8 | 965 | if (ifv->ifv_p == NULL || ifv->ifv_p != ifp_p) { |
7d50fe58 SZ |
966 | /* |
967 | * We are disconnected from the original | |
968 | * parent interface or the parent interface | |
969 | * is changed, after vlan interface's | |
970 | * serializer is released. | |
971 | */ | |
972 | error = EINVAL; | |
973 | } | |
974 | ||
984263bc MD |
975 | /* Limit the result to the parent's current config. */ |
976 | if (error == 0) { | |
977 | struct ifmediareq *ifmr; | |
978 | ||
979 | ifmr = (struct ifmediareq *) data; | |
980 | if (ifmr->ifm_count >= 1 && ifmr->ifm_ulist) { | |
981 | ifmr->ifm_count = 1; | |
982 | error = copyout(&ifmr->ifm_current, | |
983 | ifmr->ifm_ulist, | |
984 | sizeof(int)); | |
985 | } | |
986 | } | |
9433ab01 | 987 | } else { |
984263bc | 988 | error = EINVAL; |
9433ab01 | 989 | } |
984263bc MD |
990 | break; |
991 | ||
992 | case SIOCSIFMEDIA: | |
993 | error = EINVAL; | |
994 | break; | |
995 | ||
984263bc MD |
996 | case SIOCSETVLAN: |
997 | error = copyin(ifr->ifr_data, &vlr, sizeof vlr); | |
998 | if (error) | |
999 | break; | |
9433ab01 | 1000 | |
a3dd34d2 | 1001 | ifnet_deserialize_all(ifp); |
9433ab01 SZ |
1002 | if (vlr.vlr_parent[0] == '\0') |
1003 | error = vlan_unconfig(ifv); | |
1004 | else | |
1005 | error = vlan_config(ifv, vlr.vlr_parent, vlr.vlr_tag); | |
a3dd34d2 | 1006 | ifnet_serialize_all(ifp); |
984263bc | 1007 | break; |
9433ab01 | 1008 | |
984263bc | 1009 | case SIOCGETVLAN: |
9433ab01 | 1010 | bzero(&vlr, sizeof(vlr)); |
984263bc | 1011 | if (ifv->ifv_p) { |
cdb7d804 MD |
1012 | strlcpy(vlr.vlr_parent, ifv->ifv_p->if_xname, |
1013 | sizeof(vlr.vlr_parent)); | |
984263bc MD |
1014 | vlr.vlr_tag = ifv->ifv_tag; |
1015 | } | |
1016 | error = copyout(&vlr, ifr->ifr_data, sizeof vlr); | |
1017 | break; | |
7d50fe58 | 1018 | |
984263bc | 1019 | case SIOCSIFFLAGS: |
9433ab01 SZ |
1020 | if (ifp->if_flags & IFF_UP) |
1021 | ifp->if_init(ifp); | |
1022 | else | |
1023 | ifp->if_flags &= ~IFF_RUNNING; | |
1024 | ||
984263bc | 1025 | /* |
18f6e883 SZ |
1026 | * We should propagate selected flags to the parent, |
1027 | * e.g., promiscuous mode. | |
984263bc | 1028 | */ |
a3dd34d2 | 1029 | ifnet_deserialize_all(ifp); |
18f6e883 | 1030 | error = vlan_config_flags(ifv); |
a3dd34d2 | 1031 | ifnet_serialize_all(ifp); |
984263bc | 1032 | break; |
212db56c | 1033 | |
984263bc MD |
1034 | case SIOCADDMULTI: |
1035 | case SIOCDELMULTI: | |
a3dd34d2 | 1036 | ifnet_deserialize_all(ifp); |
9433ab01 | 1037 | error = vlan_config_multi(ifv); |
a3dd34d2 | 1038 | ifnet_serialize_all(ifp); |
984263bc | 1039 | break; |
212db56c | 1040 | |
984263bc | 1041 | default: |
212db56c SZ |
1042 | error = ether_ioctl(ifp, cmd, data); |
1043 | break; | |
984263bc | 1044 | } |
9433ab01 SZ |
1045 | return error; |
1046 | } | |
126fc6c4 | 1047 | |
9433ab01 | 1048 | static void |
002c1265 | 1049 | vlan_multi_dispatch(netmsg_t msg) |
9433ab01 | 1050 | { |
002c1265 | 1051 | struct netmsg_vlan *vmsg = (struct netmsg_vlan *)msg; |
9433ab01 SZ |
1052 | struct ifvlan *ifv = vmsg->nv_ifv; |
1053 | int error = 0; | |
126fc6c4 | 1054 | |
9433ab01 SZ |
1055 | /* |
1056 | * If we don't have a parent, just remember the membership for | |
1057 | * when we do. | |
1058 | */ | |
1059 | if (ifv->ifv_p != NULL) | |
1060 | error = vlan_setmulti(ifv, ifv->ifv_p); | |
002c1265 | 1061 | lwkt_replymsg(&vmsg->base.lmsg, error); |
9433ab01 SZ |
1062 | } |
1063 | ||
1064 | static int | |
1065 | vlan_config_multi(struct ifvlan *ifv) | |
1066 | { | |
1067 | struct netmsg_vlan vmsg; | |
9433ab01 | 1068 | |
2c9effcf | 1069 | ASSERT_IFNET_NOT_SERIALIZED_ALL(&ifv->ifv_if); |
9433ab01 SZ |
1070 | |
1071 | bzero(&vmsg, sizeof(vmsg)); | |
9433ab01 | 1072 | |
002c1265 | 1073 | netmsg_init(&vmsg.base, NULL, &curthread->td_msgport, |
48e7b118 | 1074 | 0, vlan_multi_dispatch); |
9433ab01 SZ |
1075 | vmsg.nv_ifv = ifv; |
1076 | ||
ec7f7fc8 | 1077 | return lwkt_domsg(netisr_cpuport(0), &vmsg.base.lmsg, 0); |
984263bc | 1078 | } |
18f6e883 SZ |
1079 | |
1080 | static void | |
002c1265 | 1081 | vlan_flags_dispatch(netmsg_t msg) |
18f6e883 | 1082 | { |
002c1265 | 1083 | struct netmsg_vlan *vmsg = (struct netmsg_vlan *)msg; |
18f6e883 SZ |
1084 | struct ifvlan *ifv = vmsg->nv_ifv; |
1085 | int error = 0; | |
1086 | ||
1087 | /* | |
1088 | * If we don't have a parent, just remember the flags for | |
1089 | * when we do. | |
1090 | */ | |
1091 | if (ifv->ifv_p != NULL) | |
1092 | error = vlan_setflags(ifv, ifv->ifv_p, 1); | |
002c1265 | 1093 | lwkt_replymsg(&vmsg->base.lmsg, error); |
18f6e883 SZ |
1094 | } |
1095 | ||
1096 | static int | |
1097 | vlan_config_flags(struct ifvlan *ifv) | |
1098 | { | |
1099 | struct netmsg_vlan vmsg; | |
18f6e883 | 1100 | |
2c9effcf | 1101 | ASSERT_IFNET_NOT_SERIALIZED_ALL(&ifv->ifv_if); |
18f6e883 SZ |
1102 | |
1103 | bzero(&vmsg, sizeof(vmsg)); | |
18f6e883 | 1104 | |
002c1265 | 1105 | netmsg_init(&vmsg.base, NULL, &curthread->td_msgport, |
48e7b118 | 1106 | 0, vlan_flags_dispatch); |
18f6e883 SZ |
1107 | vmsg.nv_ifv = ifv; |
1108 | ||
ec7f7fc8 | 1109 | return lwkt_domsg(netisr_cpuport(0), &vmsg.base.lmsg, 0); |
18f6e883 | 1110 | } |