Line data Source code
1 : /* $OpenBSD: if_vlan.c,v 1.178 2018/07/11 14:20:18 sf Exp $ */
2 :
3 : /*
4 : * Copyright 1998 Massachusetts Institute of Technology
5 : *
6 : * Permission to use, copy, modify, and distribute this software and
7 : * its documentation for any purpose and without fee is hereby
8 : * granted, provided that both the above copyright notice and this
9 : * permission notice appear in all copies, that both the above
10 : * copyright notice and this permission notice appear in all
11 : * supporting documentation, and that the name of M.I.T. not be used
12 : * in advertising or publicity pertaining to distribution of the
13 : * software without specific, written prior permission. M.I.T. makes
14 : * no representations about the suitability of this software for any
15 : * purpose. It is provided "as is" without express or implied
16 : * warranty.
17 : *
18 : * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
19 : * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
20 : * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 : * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
22 : * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 : * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 : * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 : * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 : * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 : * SUCH DAMAGE.
30 : *
31 : * $FreeBSD: src/sys/net/if_vlan.c,v 1.16 2000/03/26 15:21:40 charnier Exp $
32 : */
33 :
34 : /*
35 : * if_vlan.c - pseudo-device driver for IEEE 802.1Q virtual LANs.
36 : * This is sort of sneaky in the implementation, since
37 : * we need to pretend to be enough of an Ethernet implementation
38 : * to make arp work. The way we do this is by telling everyone
39 : * that we are an Ethernet, and then catch the packets that
40 : * ether_output() left on our output queue when it calls
41 : * if_start(), rewrite them for use by the real outgoing interface,
42 : * and ask it to send them.
43 : *
44 : * Some devices support 802.1Q tag insertion in firmware. The
45 : * vlan interface behavior changes when the IFCAP_VLAN_HWTAGGING
46 : * capability is set on the parent. In this case, vlan_start()
47 : * will not modify the ethernet header.
48 : */
49 :
50 : #include "mpw.h"
51 :
52 : #include <sys/param.h>
53 : #include <sys/kernel.h>
54 : #include <sys/malloc.h>
55 : #include <sys/mbuf.h>
56 : #include <sys/queue.h>
57 : #include <sys/socket.h>
58 : #include <sys/sockio.h>
59 : #include <sys/systm.h>
60 : #include <sys/rwlock.h>
61 :
62 : #include <net/if.h>
63 : #include <net/if_dl.h>
64 : #include <net/if_types.h>
65 :
66 : #include <netinet/in.h>
67 : #include <netinet/if_ether.h>
68 :
69 : #include <net/if_vlan_var.h>
70 :
71 : #include "bpfilter.h"
72 : #if NBPFILTER > 0
73 : #include <net/bpf.h>
74 : #endif
75 :
76 : #define TAG_HASH_BITS 5
77 : #define TAG_HASH_SIZE (1 << TAG_HASH_BITS)
78 : #define TAG_HASH_MASK (TAG_HASH_SIZE - 1)
79 : #define TAG_HASH(tag) (tag & TAG_HASH_MASK)
80 : SRPL_HEAD(, ifvlan) *vlan_tagh, *svlan_tagh;
81 : struct rwlock vlan_tagh_lk = RWLOCK_INITIALIZER("vlantag");
82 :
83 : void vlanattach(int count);
84 : int vlan_clone_create(struct if_clone *, int);
85 : int vlan_clone_destroy(struct ifnet *);
86 :
87 : int vlan_input(struct ifnet *, struct mbuf *, void *);
88 : void vlan_start(struct ifqueue *ifq);
89 : int vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr);
90 :
91 : int vlan_up(struct ifvlan *);
92 : int vlan_parent_up(struct ifvlan *, struct ifnet *);
93 : int vlan_down(struct ifvlan *);
94 :
95 : void vlan_ifdetach(void *);
96 : void vlan_link_hook(void *);
97 : void vlan_link_state(struct ifvlan *, u_char, u_int64_t);
98 :
99 : int vlan_set_vnetid(struct ifvlan *, uint16_t);
100 : int vlan_set_parent(struct ifvlan *, const char *);
101 : int vlan_del_parent(struct ifvlan *);
102 : int vlan_inuse(uint16_t, unsigned int, uint16_t);
103 : int vlan_inuse_locked(uint16_t, unsigned int, uint16_t);
104 :
105 : int vlan_multi_add(struct ifvlan *, struct ifreq *);
106 : int vlan_multi_del(struct ifvlan *, struct ifreq *);
107 : void vlan_multi_apply(struct ifvlan *, struct ifnet *, u_long);
108 : void vlan_multi_free(struct ifvlan *);
109 :
110 : int vlan_media_get(struct ifvlan *, struct ifreq *);
111 :
112 : int vlan_iff(struct ifvlan *);
113 : int vlan_setlladdr(struct ifvlan *, struct ifreq *);
114 :
115 : int vlan_set_compat(struct ifnet *, struct ifreq *);
116 : int vlan_get_compat(struct ifnet *, struct ifreq *);
117 :
118 : struct if_clone vlan_cloner =
119 : IF_CLONE_INITIALIZER("vlan", vlan_clone_create, vlan_clone_destroy);
120 : struct if_clone svlan_cloner =
121 : IF_CLONE_INITIALIZER("svlan", vlan_clone_create, vlan_clone_destroy);
122 :
123 : void vlan_ref(void *, void *);
124 : void vlan_unref(void *, void *);
125 :
126 : struct srpl_rc vlan_tagh_rc = SRPL_RC_INITIALIZER(vlan_ref, vlan_unref, NULL);
127 :
128 : void
129 0 : vlanattach(int count)
130 : {
131 : u_int i;
132 :
133 : /* Normal VLAN */
134 0 : vlan_tagh = mallocarray(TAG_HASH_SIZE, sizeof(*vlan_tagh),
135 : M_DEVBUF, M_NOWAIT);
136 0 : if (vlan_tagh == NULL)
137 0 : panic("vlanattach: hashinit");
138 :
139 : /* Service-VLAN for QinQ/802.1ad provider bridges */
140 0 : svlan_tagh = mallocarray(TAG_HASH_SIZE, sizeof(*svlan_tagh),
141 : M_DEVBUF, M_NOWAIT);
142 0 : if (svlan_tagh == NULL)
143 0 : panic("vlanattach: hashinit");
144 :
145 0 : for (i = 0; i < TAG_HASH_SIZE; i++) {
146 0 : SRPL_INIT(&vlan_tagh[i]);
147 0 : SRPL_INIT(&svlan_tagh[i]);
148 : }
149 :
150 0 : if_clone_attach(&vlan_cloner);
151 0 : if_clone_attach(&svlan_cloner);
152 0 : }
153 :
154 : int
155 0 : vlan_clone_create(struct if_clone *ifc, int unit)
156 : {
157 : struct ifvlan *ifv;
158 : struct ifnet *ifp;
159 :
160 0 : ifv = malloc(sizeof(*ifv), M_DEVBUF, M_WAITOK|M_ZERO);
161 0 : LIST_INIT(&ifv->vlan_mc_listhead);
162 0 : ifp = &ifv->ifv_if;
163 0 : ifp->if_softc = ifv;
164 0 : snprintf(ifp->if_xname, sizeof ifp->if_xname, "%s%d", ifc->ifc_name,
165 : unit);
166 : /* NB: flags are not set here */
167 : /* NB: mtu is not set here */
168 :
169 : /* Special handling for the IEEE 802.1ad QinQ variant */
170 0 : if (strcmp("svlan", ifc->ifc_name) == 0)
171 0 : ifv->ifv_type = ETHERTYPE_QINQ;
172 : else
173 0 : ifv->ifv_type = ETHERTYPE_VLAN;
174 :
175 0 : refcnt_init(&ifv->ifv_refcnt);
176 :
177 0 : ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST;
178 0 : ifp->if_xflags = IFXF_CLONED|IFXF_MPSAFE;
179 0 : ifp->if_qstart = vlan_start;
180 0 : ifp->if_ioctl = vlan_ioctl;
181 0 : ifp->if_hardmtu = 0xffff;
182 0 : ifp->if_link_state = LINK_STATE_DOWN;
183 0 : if_attach(ifp);
184 0 : ether_ifattach(ifp);
185 0 : ifp->if_hdrlen = EVL_ENCAPLEN;
186 :
187 0 : return (0);
188 : }
189 :
190 : void
191 0 : vlan_ref(void *null, void *v)
192 : {
193 0 : struct ifvlan *ifv = v;
194 :
195 0 : refcnt_take(&ifv->ifv_refcnt);
196 0 : }
197 :
198 : void
199 0 : vlan_unref(void *null, void *v)
200 : {
201 0 : struct ifvlan *ifv = v;
202 :
203 0 : refcnt_rele_wake(&ifv->ifv_refcnt);
204 0 : }
205 :
206 : int
207 0 : vlan_clone_destroy(struct ifnet *ifp)
208 : {
209 0 : struct ifvlan *ifv = ifp->if_softc;
210 :
211 0 : if (ISSET(ifp->if_flags, IFF_RUNNING))
212 0 : vlan_down(ifv);
213 :
214 0 : ether_ifdetach(ifp);
215 0 : if_detach(ifp);
216 0 : refcnt_finalize(&ifv->ifv_refcnt, "vlanrefs");
217 0 : vlan_multi_free(ifv);
218 0 : free(ifv, M_DEVBUF, sizeof(*ifv));
219 :
220 0 : return (0);
221 : }
222 :
223 : static inline int
224 0 : vlan_mplstunnel(int ifidx)
225 : {
226 : #if NMPW > 0
227 : struct ifnet *ifp;
228 : int rv = 0;
229 :
230 0 : ifp = if_get(ifidx);
231 0 : if (ifp != NULL) {
232 0 : rv = ifp->if_type == IFT_MPLSTUNNEL;
233 0 : if_put(ifp);
234 0 : }
235 0 : return (rv);
236 : #else
237 : return (0);
238 : #endif
239 : }
240 :
241 : void
242 0 : vlan_start(struct ifqueue *ifq)
243 : {
244 0 : struct ifnet *ifp = ifq->ifq_if;
245 : struct ifvlan *ifv;
246 : struct ifnet *ifp0;
247 : struct mbuf *m;
248 : uint8_t prio;
249 :
250 0 : ifv = ifp->if_softc;
251 0 : ifp0 = if_get(ifv->ifv_ifp0);
252 0 : if (ifp0 == NULL || (ifp0->if_flags & (IFF_UP|IFF_RUNNING)) !=
253 : (IFF_UP|IFF_RUNNING)) {
254 0 : ifq_purge(ifq);
255 0 : goto leave;
256 : }
257 :
258 0 : while ((m = ifq_dequeue(ifq)) != NULL) {
259 : #if NBPFILTER > 0
260 0 : if (ifp->if_bpf)
261 0 : bpf_mtap_ether(ifp->if_bpf, m, BPF_DIRECTION_OUT);
262 : #endif /* NBPFILTER > 0 */
263 :
264 0 : prio = ISSET(ifp->if_flags, IFF_LINK0) ?
265 0 : ifp->if_llprio : m->m_pkthdr.pf.prio;
266 :
267 : /* IEEE 802.1p has prio 0 and 1 swapped */
268 0 : if (prio <= 1)
269 0 : prio = !prio;
270 :
271 : /*
272 : * If this packet came from a pseudowire it means it already
273 : * has all tags it needs, so just output it.
274 : */
275 0 : if (vlan_mplstunnel(m->m_pkthdr.ph_ifidx)) {
276 : /* NOTHING */
277 :
278 : /*
279 : * If the underlying interface cannot do VLAN tag insertion
280 : * itself, create an encapsulation header.
281 : */
282 0 : } else if ((ifp0->if_capabilities & IFCAP_VLAN_HWTAGGING) &&
283 0 : (ifv->ifv_type == ETHERTYPE_VLAN)) {
284 0 : m->m_pkthdr.ether_vtag = ifv->ifv_tag +
285 0 : (prio << EVL_PRIO_BITS);
286 0 : m->m_flags |= M_VLANTAG;
287 0 : } else {
288 0 : m = vlan_inject(m, ifv->ifv_type, ifv->ifv_tag |
289 0 : (prio << EVL_PRIO_BITS));
290 0 : if (m == NULL) {
291 0 : ifp->if_oerrors++;
292 0 : continue;
293 : }
294 : }
295 :
296 0 : if (if_enqueue(ifp0, m)) {
297 0 : ifp->if_oerrors++;
298 0 : ifq->ifq_errors++;
299 0 : continue;
300 : }
301 : }
302 :
303 : leave:
304 0 : if_put(ifp0);
305 0 : }
306 :
307 : struct mbuf *
308 0 : vlan_inject(struct mbuf *m, uint16_t type, uint16_t tag)
309 : {
310 0 : struct ether_vlan_header evh;
311 :
312 0 : m_copydata(m, 0, ETHER_HDR_LEN, (caddr_t)&evh);
313 0 : evh.evl_proto = evh.evl_encap_proto;
314 0 : evh.evl_encap_proto = htons(type);
315 0 : evh.evl_tag = htons(tag);
316 0 : m_adj(m, ETHER_HDR_LEN);
317 0 : M_PREPEND(m, sizeof(evh) + ETHER_ALIGN, M_DONTWAIT);
318 0 : if (m == NULL)
319 0 : return (NULL);
320 :
321 0 : m_adj(m, ETHER_ALIGN);
322 :
323 0 : m_copyback(m, 0, sizeof(evh), &evh, M_NOWAIT);
324 0 : CLR(m->m_flags, M_VLANTAG);
325 :
326 0 : return (m);
327 0 : }
328 :
329 : /*
330 : * vlan_input() returns 1 if it has consumed the packet, 0 otherwise.
331 : */
332 : int
333 0 : vlan_input(struct ifnet *ifp0, struct mbuf *m, void *cookie)
334 : {
335 : struct ifvlan *ifv;
336 : struct ether_vlan_header *evl;
337 : struct ether_header *eh;
338 : SRPL_HEAD(, ifvlan) *tagh, *list;
339 0 : struct srp_ref sr;
340 : u_int tag;
341 0 : struct mbuf_list ml = MBUF_LIST_INITIALIZER();
342 : u_int16_t etype;
343 :
344 0 : eh = mtod(m, struct ether_header *);
345 0 : etype = ntohs(eh->ether_type);
346 :
347 0 : if (m->m_flags & M_VLANTAG) {
348 : etype = ETHERTYPE_VLAN;
349 0 : tagh = vlan_tagh;
350 0 : } else if ((etype == ETHERTYPE_VLAN) || (etype == ETHERTYPE_QINQ)) {
351 0 : if (m->m_len < sizeof(*evl) &&
352 0 : (m = m_pullup(m, sizeof(*evl))) == NULL) {
353 0 : ifp0->if_ierrors++;
354 0 : return (1);
355 : }
356 :
357 0 : evl = mtod(m, struct ether_vlan_header *);
358 0 : m->m_pkthdr.ether_vtag = ntohs(evl->evl_tag);
359 0 : tagh = etype == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
360 : } else {
361 : /* Skip non-VLAN packets. */
362 0 : return (0);
363 : }
364 :
365 : /* From now on ether_vtag is fine */
366 0 : tag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
367 0 : m->m_pkthdr.pf.prio = EVL_PRIOFTAG(m->m_pkthdr.ether_vtag);
368 :
369 : /* IEEE 802.1p has prio 0 and 1 swapped */
370 0 : if (m->m_pkthdr.pf.prio <= 1)
371 0 : m->m_pkthdr.pf.prio = !m->m_pkthdr.pf.prio;
372 :
373 0 : list = &tagh[TAG_HASH(tag)];
374 0 : SRPL_FOREACH(ifv, &sr, list, ifv_list) {
375 0 : if (ifp0->if_index == ifv->ifv_ifp0 && tag == ifv->ifv_tag &&
376 0 : etype == ifv->ifv_type)
377 : break;
378 : }
379 :
380 0 : if (ifv == NULL) {
381 0 : ifp0->if_noproto++;
382 0 : goto drop;
383 : }
384 :
385 0 : if ((ifv->ifv_if.if_flags & (IFF_UP|IFF_RUNNING)) !=
386 : (IFF_UP|IFF_RUNNING))
387 : goto drop;
388 :
389 : /*
390 : * Having found a valid vlan interface corresponding to
391 : * the given source interface and vlan tag, remove the
392 : * encapsulation.
393 : */
394 0 : if (m->m_flags & M_VLANTAG) {
395 0 : m->m_flags &= ~M_VLANTAG;
396 0 : } else {
397 0 : eh->ether_type = evl->evl_proto;
398 0 : memmove((char *)eh + EVL_ENCAPLEN, eh, sizeof(*eh));
399 0 : m_adj(m, EVL_ENCAPLEN);
400 : }
401 :
402 0 : ml_enqueue(&ml, m);
403 0 : if_input(&ifv->ifv_if, &ml);
404 0 : SRPL_LEAVE(&sr);
405 0 : return (1);
406 :
407 : drop:
408 0 : SRPL_LEAVE(&sr);
409 0 : m_freem(m);
410 0 : return (1);
411 0 : }
412 :
413 : int
414 0 : vlan_parent_up(struct ifvlan *ifv, struct ifnet *ifp0)
415 : {
416 : int error;
417 :
418 0 : if (ISSET(ifv->ifv_flags, IFVF_PROMISC)) {
419 0 : error = ifpromisc(ifp0, 1);
420 0 : if (error != 0)
421 0 : return (error);
422 : }
423 :
424 : /* Register callback for physical link state changes */
425 0 : ifv->lh_cookie = hook_establish(ifp0->if_linkstatehooks, 1,
426 0 : vlan_link_hook, ifv);
427 :
428 : /* Register callback if parent wants to unregister */
429 0 : ifv->dh_cookie = hook_establish(ifp0->if_detachhooks, 0,
430 : vlan_ifdetach, ifv);
431 :
432 0 : vlan_multi_apply(ifv, ifp0, SIOCADDMULTI);
433 :
434 0 : if_ih_insert(ifp0, vlan_input, NULL);
435 :
436 0 : return (0);
437 0 : }
438 :
439 : int
440 0 : vlan_up(struct ifvlan *ifv)
441 : {
442 : SRPL_HEAD(, ifvlan) *tagh, *list;
443 0 : struct ifnet *ifp = &ifv->ifv_if;
444 : struct ifnet *ifp0;
445 : int error = 0;
446 : u_int hardmtu;
447 :
448 0 : KASSERT(!ISSET(ifp->if_flags, IFF_RUNNING));
449 :
450 0 : tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
451 0 : list = &tagh[TAG_HASH(ifv->ifv_tag)];
452 :
453 0 : ifp0 = if_get(ifv->ifv_ifp0);
454 0 : if (ifp0 == NULL)
455 0 : return (ENXIO);
456 :
457 : /* check vlan will work on top of the parent */
458 0 : if (ifp0->if_type != IFT_ETHER) {
459 : error = EPROTONOSUPPORT;
460 0 : goto put;
461 : }
462 :
463 0 : hardmtu = ifp0->if_hardmtu;
464 0 : if (!ISSET(ifp0->if_capabilities, IFCAP_VLAN_MTU))
465 0 : hardmtu -= EVL_ENCAPLEN;
466 :
467 0 : if (ifp->if_mtu > hardmtu) {
468 : error = ENOBUFS;
469 0 : goto put;
470 : }
471 :
472 : /* parent is fine, let's prepare the ifv to handle packets */
473 0 : ifp->if_hardmtu = hardmtu;
474 0 : SET(ifp->if_flags, ifp0->if_flags & IFF_SIMPLEX);
475 :
476 : /*
477 : * Note: In cases like vio(4) and em(4) where the offsets of the
478 : * csum can be freely defined, we could actually do csum offload
479 : * for VLAN and QINQ packets.
480 : */
481 0 : if (ifv->ifv_type != ETHERTYPE_VLAN) {
482 : /*
483 : * Hardware offload only works with the default VLAN
484 : * ethernet type (0x8100).
485 : */
486 0 : ifp->if_capabilities = 0;
487 0 : } else if (ISSET(ifp0->if_capabilities, IFCAP_VLAN_HWTAGGING)) {
488 : /*
489 : * Chips that can do hardware-assisted VLAN encapsulation, can
490 : * calculate the correct checksum for VLAN tagged packets.
491 : */
492 0 : ifp->if_capabilities = ifp0->if_capabilities & IFCAP_CSUM_MASK;
493 0 : }
494 :
495 : /* commit the ifv */
496 0 : error = rw_enter(&vlan_tagh_lk, RW_WRITE | RW_INTR);
497 0 : if (error != 0)
498 : goto scrub;
499 :
500 0 : error = vlan_inuse_locked(ifv->ifv_type, ifv->ifv_ifp0, ifv->ifv_tag);
501 0 : if (error != 0)
502 : goto leave;
503 :
504 0 : SRPL_INSERT_HEAD_LOCKED(&vlan_tagh_rc, list, ifv, ifv_list);
505 0 : rw_exit(&vlan_tagh_lk);
506 :
507 : /* configure the parent to handle packets for this vlan */
508 0 : error = vlan_parent_up(ifv, ifp0);
509 0 : if (error != 0)
510 : goto remove;
511 :
512 : /* we're running now */
513 0 : SET(ifp->if_flags, IFF_RUNNING);
514 0 : vlan_link_state(ifv, ifp0->if_link_state, ifp0->if_baudrate);
515 :
516 0 : if_put(ifp0);
517 :
518 0 : return (0);
519 :
520 : remove:
521 0 : rw_enter(&vlan_tagh_lk, RW_WRITE);
522 0 : SRPL_REMOVE_LOCKED(&vlan_tagh_rc, list, ifv, ifvlan, ifv_list);
523 : leave:
524 0 : rw_exit(&vlan_tagh_lk);
525 : scrub:
526 0 : ifp->if_capabilities = 0;
527 0 : CLR(ifp->if_flags, IFF_SIMPLEX);
528 0 : ifp->if_hardmtu = 0xffff;
529 : put:
530 0 : if_put(ifp0);
531 :
532 0 : return (error);
533 0 : }
534 :
535 : int
536 0 : vlan_down(struct ifvlan *ifv)
537 : {
538 : SRPL_HEAD(, ifvlan) *tagh, *list;
539 0 : struct ifnet *ifp = &ifv->ifv_if;
540 : struct ifnet *ifp0;
541 :
542 0 : tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
543 0 : list = &tagh[TAG_HASH(ifv->ifv_tag)];
544 :
545 0 : KASSERT(ISSET(ifp->if_flags, IFF_RUNNING));
546 :
547 0 : vlan_link_state(ifv, LINK_STATE_DOWN, 0);
548 0 : CLR(ifp->if_flags, IFF_RUNNING);
549 :
550 0 : ifq_barrier(&ifp->if_snd);
551 :
552 0 : ifp0 = if_get(ifv->ifv_ifp0);
553 0 : if (ifp0 != NULL) {
554 0 : if_ih_remove(ifp0, vlan_input, NULL);
555 0 : if (ISSET(ifv->ifv_flags, IFVF_PROMISC))
556 0 : ifpromisc(ifp0, 0);
557 0 : vlan_multi_apply(ifv, ifp0, SIOCDELMULTI);
558 0 : hook_disestablish(ifp0->if_detachhooks, ifv->dh_cookie);
559 0 : hook_disestablish(ifp0->if_linkstatehooks, ifv->lh_cookie);
560 0 : }
561 0 : if_put(ifp0);
562 :
563 0 : rw_enter_write(&vlan_tagh_lk);
564 0 : SRPL_REMOVE_LOCKED(&vlan_tagh_rc, list, ifv, ifvlan, ifv_list);
565 0 : rw_exit_write(&vlan_tagh_lk);
566 :
567 0 : ifp->if_capabilities = 0;
568 0 : CLR(ifp->if_flags, IFF_SIMPLEX);
569 0 : ifp->if_hardmtu = 0xffff;
570 :
571 0 : return (0);
572 : }
573 :
574 : void
575 0 : vlan_ifdetach(void *v)
576 : {
577 0 : struct ifvlan *ifv = v;
578 0 : struct ifnet *ifp = &ifv->ifv_if;
579 :
580 0 : if (ISSET(ifp->if_flags, IFF_RUNNING)) {
581 0 : vlan_down(ifv);
582 0 : CLR(ifp->if_flags, IFF_UP);
583 0 : }
584 :
585 0 : ifv->ifv_ifp0 = 0;
586 0 : }
587 :
588 : void
589 0 : vlan_link_hook(void *v)
590 : {
591 0 : struct ifvlan *ifv = v;
592 : struct ifnet *ifp0;
593 :
594 : u_char link = LINK_STATE_DOWN;
595 : uint64_t baud = 0;
596 :
597 0 : ifp0 = if_get(ifv->ifv_ifp0);
598 0 : if (ifp0 != NULL) {
599 0 : link = ifp0->if_link_state;
600 0 : baud = ifp0->if_baudrate;
601 0 : }
602 0 : if_put(ifp0);
603 :
604 0 : vlan_link_state(ifv, link, baud);
605 0 : }
606 :
607 : void
608 0 : vlan_link_state(struct ifvlan *ifv, u_char link, uint64_t baud)
609 : {
610 0 : if (ifv->ifv_if.if_link_state == link)
611 : return;
612 :
613 0 : ifv->ifv_if.if_link_state = link;
614 0 : ifv->ifv_if.if_baudrate = baud;
615 :
616 0 : if_link_state_change(&ifv->ifv_if);
617 0 : }
618 :
619 : int
620 0 : vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
621 : {
622 0 : struct ifvlan *ifv = ifp->if_softc;
623 0 : struct ifreq *ifr = (struct ifreq *)data;
624 0 : struct if_parent *parent = (struct if_parent *)data;
625 : struct ifnet *ifp0;
626 : uint16_t tag;
627 : int error = 0;
628 :
629 0 : switch (cmd) {
630 : case SIOCSIFADDR:
631 0 : ifp->if_flags |= IFF_UP;
632 : /* FALLTHROUGH */
633 :
634 : case SIOCSIFFLAGS:
635 0 : if (ISSET(ifp->if_flags, IFF_UP)) {
636 0 : if (!ISSET(ifp->if_flags, IFF_RUNNING))
637 0 : error = vlan_up(ifv);
638 : else
639 : error = ENETRESET;
640 : } else {
641 0 : if (ISSET(ifp->if_flags, IFF_RUNNING))
642 0 : error = vlan_down(ifv);
643 : }
644 : break;
645 :
646 : case SIOCSVNETID:
647 0 : if (ifr->ifr_vnetid < EVL_VLID_MIN ||
648 0 : ifr->ifr_vnetid > EVL_VLID_MAX) {
649 : error = EINVAL;
650 0 : break;
651 : }
652 :
653 0 : tag = ifr->ifr_vnetid;
654 0 : if (tag == ifv->ifv_tag)
655 : break;
656 :
657 0 : error = vlan_set_vnetid(ifv, tag);
658 0 : break;
659 :
660 : case SIOCGVNETID:
661 0 : if (ifv->ifv_tag == EVL_VLID_NULL)
662 0 : error = EADDRNOTAVAIL;
663 : else
664 0 : ifr->ifr_vnetid = (int64_t)ifv->ifv_tag;
665 : break;
666 :
667 : case SIOCDVNETID:
668 0 : error = vlan_set_vnetid(ifv, 0);
669 0 : break;
670 :
671 : case SIOCSIFPARENT:
672 0 : error = vlan_set_parent(ifv, parent->ifp_parent);
673 0 : break;
674 :
675 : case SIOCGIFPARENT:
676 0 : ifp0 = if_get(ifv->ifv_ifp0);
677 0 : if (ifp0 == NULL)
678 0 : error = EADDRNOTAVAIL;
679 : else {
680 0 : memcpy(parent->ifp_parent, ifp0->if_xname,
681 : sizeof(parent->ifp_parent));
682 : }
683 0 : if_put(ifp0);
684 0 : break;
685 :
686 : case SIOCDIFPARENT:
687 0 : error = vlan_del_parent(ifv);
688 0 : break;
689 :
690 : case SIOCADDMULTI:
691 0 : error = vlan_multi_add(ifv, ifr);
692 0 : break;
693 :
694 : case SIOCDELMULTI:
695 0 : error = vlan_multi_del(ifv, ifr);
696 0 : break;
697 :
698 : case SIOCGIFMEDIA:
699 0 : error = vlan_media_get(ifv, ifr);
700 0 : break;
701 :
702 : case SIOCSIFMEDIA:
703 : error = ENOTTY;
704 0 : break;
705 :
706 : case SIOCSIFLLADDR:
707 0 : error = vlan_setlladdr(ifv, ifr);
708 0 : break;
709 :
710 : case SIOCSETVLAN:
711 0 : error = vlan_set_compat(ifp, ifr);
712 0 : break;
713 : case SIOCGETVLAN:
714 0 : error = vlan_get_compat(ifp, ifr);
715 0 : break;
716 :
717 : default:
718 0 : error = ether_ioctl(ifp, &ifv->ifv_ac, cmd, data);
719 0 : break;
720 : }
721 :
722 0 : if (error == ENETRESET) {
723 0 : vlan_iff(ifv);
724 : error = 0;
725 0 : }
726 :
727 0 : return error;
728 : }
729 :
730 : int
731 0 : vlan_iff(struct ifvlan *ifv)
732 : {
733 : struct ifnet *ifp0;
734 : int promisc = 0;
735 : int error = 0;
736 :
737 0 : if (ISSET(ifv->ifv_if.if_flags, IFF_PROMISC) ||
738 0 : ISSET(ifv->ifv_flags, IFVF_LLADDR))
739 0 : promisc = IFVF_PROMISC;
740 :
741 0 : if (ISSET(ifv->ifv_flags, IFVF_PROMISC) == promisc)
742 0 : return (0);
743 :
744 0 : if (ISSET(ifv->ifv_if.if_flags, IFF_RUNNING)) {
745 0 : ifp0 = if_get(ifv->ifv_ifp0);
746 0 : if (ifp0 != NULL)
747 0 : error = ifpromisc(ifp0, promisc);
748 0 : if_put(ifp0);
749 0 : }
750 :
751 0 : if (error == 0) {
752 0 : CLR(ifv->ifv_flags, IFVF_PROMISC);
753 0 : SET(ifv->ifv_flags, promisc);
754 0 : }
755 :
756 0 : return (error);
757 0 : }
758 :
759 : int
760 0 : vlan_setlladdr(struct ifvlan *ifv, struct ifreq *ifr)
761 : {
762 0 : struct ifnet *ifp = &ifv->ifv_if;
763 : struct ifnet *ifp0;
764 0 : uint8_t lladdr[ETHER_ADDR_LEN];
765 : int flag;
766 :
767 0 : memcpy(lladdr, ifr->ifr_addr.sa_data, sizeof(lladdr));
768 :
769 : /* setting the mac addr to 00:00:00:00:00:00 means reset lladdr */
770 0 : if (memcmp(lladdr, etheranyaddr, sizeof(lladdr)) == 0) {
771 0 : ifp0 = if_get(ifv->ifv_ifp0);
772 0 : if (ifp0 != NULL)
773 0 : memcpy(lladdr, LLADDR(ifp0->if_sadl), sizeof(lladdr));
774 0 : if_put(ifp0);
775 :
776 : flag = 0;
777 0 : } else
778 : flag = IFVF_LLADDR;
779 :
780 0 : if (memcmp(lladdr, LLADDR(ifp->if_sadl), sizeof(lladdr)) == 0 &&
781 0 : ISSET(ifv->ifv_flags, IFVF_LLADDR) == flag) {
782 : /* nop */
783 0 : return (0);
784 : }
785 :
786 : /* commit */
787 0 : if_setlladdr(ifp, lladdr);
788 0 : CLR(ifv->ifv_flags, IFVF_LLADDR);
789 0 : SET(ifv->ifv_flags, flag);
790 :
791 0 : return (ENETRESET);
792 0 : }
793 :
794 : int
795 0 : vlan_set_vnetid(struct ifvlan *ifv, uint16_t tag)
796 : {
797 0 : struct ifnet *ifp = &ifv->ifv_if;
798 : SRPL_HEAD(, ifvlan) *tagh, *list;
799 0 : u_char link = ifp->if_link_state;
800 0 : uint64_t baud = ifp->if_baudrate;
801 : int error;
802 :
803 0 : tagh = ifv->ifv_type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
804 :
805 0 : if (ISSET(ifp->if_flags, IFF_RUNNING) && LINK_STATE_IS_UP(link))
806 0 : vlan_link_state(ifv, LINK_STATE_DOWN, 0);
807 :
808 0 : error = rw_enter(&vlan_tagh_lk, RW_WRITE);
809 0 : if (error != 0)
810 0 : return (error);
811 :
812 0 : error = vlan_inuse_locked(ifv->ifv_type, ifv->ifv_ifp0, tag);
813 0 : if (error != 0)
814 : goto unlock;
815 :
816 0 : if (ISSET(ifp->if_flags, IFF_RUNNING)) {
817 0 : list = &tagh[TAG_HASH(ifv->ifv_tag)];
818 0 : SRPL_REMOVE_LOCKED(&vlan_tagh_rc, list, ifv, ifvlan, ifv_list);
819 :
820 0 : ifv->ifv_tag = tag;
821 :
822 0 : list = &tagh[TAG_HASH(ifv->ifv_tag)];
823 0 : SRPL_INSERT_HEAD_LOCKED(&vlan_tagh_rc, list, ifv, ifv_list);
824 0 : } else
825 0 : ifv->ifv_tag = tag;
826 :
827 : unlock:
828 0 : rw_exit(&vlan_tagh_lk);
829 :
830 0 : if (ISSET(ifp->if_flags, IFF_RUNNING) && LINK_STATE_IS_UP(link))
831 0 : vlan_link_state(ifv, link, baud);
832 :
833 0 : return (error);
834 0 : }
835 :
836 : int
837 0 : vlan_set_parent(struct ifvlan *ifv, const char *parent)
838 : {
839 0 : struct ifnet *ifp = &ifv->ifv_if;
840 : struct ifnet *ifp0;
841 : int error = 0;
842 :
843 0 : ifp0 = ifunit(parent); /* doesn't need an if_put */
844 0 : if (ifp0 == NULL)
845 0 : return (EINVAL);
846 :
847 0 : if (ifp0->if_type != IFT_ETHER)
848 0 : return (EPROTONOSUPPORT);
849 :
850 0 : if (ifv->ifv_ifp0 == ifp0->if_index) {
851 : /* nop */
852 0 : return (0);
853 : }
854 :
855 0 : if (ISSET(ifp->if_flags, IFF_RUNNING))
856 0 : return (EBUSY);
857 :
858 0 : error = vlan_inuse(ifv->ifv_type, ifp0->if_index, ifv->ifv_tag);
859 0 : if (error != 0)
860 0 : return (error);
861 :
862 : /* commit */
863 0 : ifv->ifv_ifp0 = ifp0->if_index;
864 0 : if (!ISSET(ifv->ifv_flags, IFVF_LLADDR))
865 0 : if_setlladdr(ifp, LLADDR(ifp0->if_sadl));
866 :
867 0 : return (0);
868 0 : }
869 :
870 : int
871 0 : vlan_del_parent(struct ifvlan *ifv)
872 : {
873 0 : struct ifnet *ifp = &ifv->ifv_if;
874 :
875 0 : if (ISSET(ifp->if_flags, IFF_RUNNING))
876 0 : return (EBUSY);
877 :
878 : /* commit */
879 0 : ifv->ifv_ifp0 = 0;
880 0 : if (!ISSET(ifv->ifv_flags, IFVF_LLADDR))
881 0 : if_setlladdr(ifp, etheranyaddr);
882 :
883 0 : return (0);
884 0 : }
885 :
886 : int
887 0 : vlan_set_compat(struct ifnet *ifp, struct ifreq *ifr)
888 : {
889 0 : struct vlanreq vlr;
890 0 : struct ifreq req;
891 0 : struct if_parent parent;
892 :
893 : int error;
894 :
895 0 : error = suser(curproc);
896 0 : if (error != 0)
897 0 : return (error);
898 :
899 0 : error = copyin(ifr->ifr_data, &vlr, sizeof(vlr));
900 0 : if (error != 0)
901 0 : return (error);
902 :
903 0 : if (vlr.vlr_parent[0] == '\0')
904 0 : return (vlan_ioctl(ifp, SIOCDIFPARENT, (caddr_t)ifr));
905 :
906 0 : memset(&req, 0, sizeof(req));
907 0 : memcpy(req.ifr_name, ifp->if_xname, sizeof(req.ifr_name));
908 0 : req.ifr_vnetid = vlr.vlr_tag;
909 :
910 0 : error = vlan_ioctl(ifp, SIOCSVNETID, (caddr_t)&req);
911 0 : if (error != 0)
912 0 : return (error);
913 :
914 0 : memset(&parent, 0, sizeof(parent));
915 0 : memcpy(parent.ifp_name, ifp->if_xname, sizeof(parent.ifp_name));
916 0 : memcpy(parent.ifp_parent, vlr.vlr_parent, sizeof(parent.ifp_parent));
917 0 : error = vlan_ioctl(ifp, SIOCSIFPARENT, (caddr_t)&parent);
918 0 : if (error != 0)
919 0 : return (error);
920 :
921 0 : memset(&req, 0, sizeof(req));
922 0 : memcpy(req.ifr_name, ifp->if_xname, sizeof(req.ifr_name));
923 0 : SET(ifp->if_flags, IFF_UP);
924 0 : return (vlan_ioctl(ifp, SIOCSIFFLAGS, (caddr_t)&req));
925 0 : }
926 :
927 : int
928 0 : vlan_get_compat(struct ifnet *ifp, struct ifreq *ifr)
929 : {
930 0 : struct ifvlan *ifv = ifp->if_softc;
931 0 : struct vlanreq vlr;
932 : struct ifnet *p;
933 :
934 0 : memset(&vlr, 0, sizeof(vlr));
935 0 : p = if_get(ifv->ifv_ifp0);
936 0 : if (p != NULL)
937 0 : memcpy(vlr.vlr_parent, p->if_xname, sizeof(vlr.vlr_parent));
938 0 : if_put(p);
939 :
940 0 : vlr.vlr_tag = ifv->ifv_tag;
941 :
942 0 : return (copyout(&vlr, ifr->ifr_data, sizeof(vlr)));
943 0 : }
944 :
945 : /*
946 : * do a quick check of up and running vlans for existing configurations.
947 : *
948 : * NOTE: this does allow the same config on down vlans, but vlan_up()
949 : * will catch them.
950 : */
951 : int
952 0 : vlan_inuse(uint16_t type, unsigned int ifidx, uint16_t tag)
953 : {
954 : int error = 0;
955 :
956 0 : error = rw_enter(&vlan_tagh_lk, RW_READ | RW_INTR);
957 0 : if (error != 0)
958 0 : return (error);
959 :
960 0 : error = vlan_inuse_locked(type, ifidx, tag);
961 :
962 0 : rw_exit(&vlan_tagh_lk);
963 :
964 0 : return (error);
965 0 : }
966 :
967 : int
968 0 : vlan_inuse_locked(uint16_t type, unsigned int ifidx, uint16_t tag)
969 : {
970 : SRPL_HEAD(, ifvlan) *tagh, *list;
971 : struct ifvlan *ifv;
972 :
973 0 : tagh = type == ETHERTYPE_QINQ ? svlan_tagh : vlan_tagh;
974 0 : list = &tagh[TAG_HASH(tag)];
975 :
976 0 : SRPL_FOREACH_LOCKED(ifv, list, ifv_list) {
977 0 : if (ifv->ifv_tag == tag &&
978 0 : ifv->ifv_type == type && /* wat */
979 0 : ifv->ifv_ifp0 == ifidx)
980 0 : return (EADDRINUSE);
981 : }
982 :
983 0 : return (0);
984 0 : }
985 :
986 : int
987 0 : vlan_multi_add(struct ifvlan *ifv, struct ifreq *ifr)
988 : {
989 : struct ifnet *ifp0;
990 : struct vlan_mc_entry *mc;
991 0 : u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
992 : int error;
993 :
994 0 : error = ether_addmulti(ifr, &ifv->ifv_ac);
995 0 : if (error != ENETRESET)
996 0 : return (error);
997 :
998 : /*
999 : * This is new multicast address. We have to tell parent
1000 : * about it. Also, remember this multicast address so that
1001 : * we can delete them on unconfigure.
1002 : */
1003 0 : if ((mc = malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT)) == NULL) {
1004 : error = ENOMEM;
1005 0 : goto alloc_failed;
1006 : }
1007 :
1008 : /*
1009 : * As ether_addmulti() returns ENETRESET, following two
1010 : * statement shouldn't fail.
1011 : */
1012 0 : (void)ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
1013 0 : ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ac, mc->mc_enm);
1014 0 : memcpy(&mc->mc_addr, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
1015 0 : LIST_INSERT_HEAD(&ifv->vlan_mc_listhead, mc, mc_entries);
1016 :
1017 0 : ifp0 = if_get(ifv->ifv_ifp0);
1018 0 : error = (ifp0 == NULL) ? 0 :
1019 0 : (*ifp0->if_ioctl)(ifp0, SIOCADDMULTI, (caddr_t)ifr);
1020 0 : if_put(ifp0);
1021 :
1022 0 : if (error != 0)
1023 : goto ioctl_failed;
1024 :
1025 0 : return (error);
1026 :
1027 : ioctl_failed:
1028 0 : LIST_REMOVE(mc, mc_entries);
1029 0 : free(mc, M_DEVBUF, sizeof(*mc));
1030 : alloc_failed:
1031 0 : (void)ether_delmulti(ifr, &ifv->ifv_ac);
1032 :
1033 0 : return (error);
1034 0 : }
1035 :
1036 : int
1037 0 : vlan_multi_del(struct ifvlan *ifv, struct ifreq *ifr)
1038 : {
1039 : struct ifnet *ifp0;
1040 : struct ether_multi *enm;
1041 : struct vlan_mc_entry *mc;
1042 0 : u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
1043 : int error;
1044 :
1045 : /*
1046 : * Find a key to lookup vlan_mc_entry. We have to do this
1047 : * before calling ether_delmulti for obvious reason.
1048 : */
1049 0 : if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
1050 0 : return (error);
1051 0 : ETHER_LOOKUP_MULTI(addrlo, addrhi, &ifv->ifv_ac, enm);
1052 0 : if (enm == NULL)
1053 0 : return (EINVAL);
1054 :
1055 0 : LIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries) {
1056 0 : if (mc->mc_enm == enm)
1057 : break;
1058 : }
1059 :
1060 : /* We won't delete entries we didn't add */
1061 0 : if (mc == NULL)
1062 0 : return (EINVAL);
1063 :
1064 0 : error = ether_delmulti(ifr, &ifv->ifv_ac);
1065 0 : if (error != ENETRESET)
1066 0 : return (error);
1067 :
1068 0 : if (!ISSET(ifv->ifv_if.if_flags, IFF_RUNNING))
1069 : goto forget;
1070 :
1071 0 : ifp0 = if_get(ifv->ifv_ifp0);
1072 0 : error = (ifp0 == NULL) ? 0 :
1073 0 : (*ifp0->if_ioctl)(ifp0, SIOCDELMULTI, (caddr_t)ifr);
1074 0 : if_put(ifp0);
1075 :
1076 0 : if (error != 0) {
1077 0 : (void)ether_addmulti(ifr, &ifv->ifv_ac);
1078 0 : return (error);
1079 : }
1080 :
1081 : forget:
1082 : /* forget about this address */
1083 0 : LIST_REMOVE(mc, mc_entries);
1084 0 : free(mc, M_DEVBUF, sizeof(*mc));
1085 :
1086 0 : return (0);
1087 0 : }
1088 :
1089 : int
1090 0 : vlan_media_get(struct ifvlan *ifv, struct ifreq *ifr)
1091 : {
1092 : struct ifnet *ifp0;
1093 : int error;
1094 :
1095 0 : ifp0 = if_get(ifv->ifv_ifp0);
1096 0 : error = (ifp0 == NULL) ? ENOTTY :
1097 0 : (*ifp0->if_ioctl)(ifp0, SIOCGIFMEDIA, (caddr_t)ifr);
1098 0 : if_put(ifp0);
1099 :
1100 0 : return (error);
1101 : }
1102 :
1103 : void
1104 0 : vlan_multi_apply(struct ifvlan *ifv, struct ifnet *ifp0, u_long cmd)
1105 : {
1106 : struct vlan_mc_entry *mc;
1107 0 : union {
1108 : struct ifreq ifreq;
1109 : struct {
1110 : char ifr_name[IFNAMSIZ];
1111 : struct sockaddr_storage ifr_ss;
1112 : } ifreq_storage;
1113 : } ifreq;
1114 0 : struct ifreq *ifr = &ifreq.ifreq;
1115 :
1116 0 : memcpy(ifr->ifr_name, ifp0->if_xname, IFNAMSIZ);
1117 0 : LIST_FOREACH(mc, &ifv->vlan_mc_listhead, mc_entries) {
1118 0 : memcpy(&ifr->ifr_addr, &mc->mc_addr, mc->mc_addr.ss_len);
1119 :
1120 0 : (void)(*ifp0->if_ioctl)(ifp0, cmd, (caddr_t)ifr);
1121 : }
1122 0 : }
1123 :
1124 : void
1125 0 : vlan_multi_free(struct ifvlan *ifv)
1126 : {
1127 : struct vlan_mc_entry *mc;
1128 :
1129 0 : while ((mc = LIST_FIRST(&ifv->vlan_mc_listhead)) != NULL) {
1130 0 : LIST_REMOVE(mc, mc_entries);
1131 0 : free(mc, M_DEVBUF, sizeof(*mc));
1132 : }
1133 0 : }
|