1 | /* $NetBSD: if_agr.c,v 1.39 2016/08/07 17:38:34 christos Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c)2005 YAMAMOTO Takashi, |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 | * SUCH DAMAGE. |
27 | */ |
28 | |
29 | #include <sys/cdefs.h> |
30 | __KERNEL_RCSID(0, "$NetBSD: if_agr.c,v 1.39 2016/08/07 17:38:34 christos Exp $" ); |
31 | |
32 | #ifdef _KERNEL_OPT |
33 | #include "opt_inet.h" |
34 | #endif |
35 | |
36 | #include <sys/param.h> |
37 | #include <sys/callout.h> |
38 | #include <sys/malloc.h> |
39 | #include <sys/mbuf.h> |
40 | #include <sys/systm.h> |
41 | #include <sys/types.h> |
42 | #include <sys/queue.h> |
43 | #include <sys/sockio.h> |
44 | #include <sys/proc.h> /* XXX for curproc */ |
45 | #include <sys/kauth.h> |
46 | #include <sys/xcall.h> |
47 | #include <sys/device.h> |
48 | #include <sys/module.h> |
49 | #include <sys/atomic.h> |
50 | |
51 | #include <net/bpf.h> |
52 | #include <net/if.h> |
53 | #include <net/if_dl.h> |
54 | #include <net/if_types.h> |
55 | #include <net/if_ether.h> |
56 | |
57 | #if defined(INET) |
58 | #include <netinet/in.h> |
59 | #include <netinet/if_inarp.h> |
60 | #endif |
61 | |
62 | #include <net/agr/if_agrvar.h> |
63 | #include <net/agr/if_agrvar_impl.h> |
64 | #include <net/agr/if_agrioctl.h> |
65 | #include <net/agr/if_agrsubr.h> |
66 | #include <net/agr/if_agrethervar.h> |
67 | |
68 | #include "ioconf.h" |
69 | |
70 | static int agr_clone_create(struct if_clone *, int); |
71 | static int agr_clone_destroy(struct ifnet *); |
72 | static void agr_start(struct ifnet *); |
73 | static int agr_setconfig(struct agr_softc *, const struct agrreq *); |
74 | static int agr_getconfig(struct agr_softc *, struct agrreq *); |
75 | static int agr_getportlist(struct agr_softc *, struct agrreq *); |
76 | static int agr_addport(struct ifnet *, struct ifnet *); |
77 | static int agr_remport(struct ifnet *, struct ifnet *); |
78 | static int agrreq_copyin(const void *, struct agrreq *); |
79 | static int agrreq_copyout(void *, struct agrreq *); |
80 | static int agr_ioctl(struct ifnet *, u_long, void *); |
81 | static struct agr_port *agr_select_tx_port(struct agr_softc *, struct mbuf *); |
82 | static int agr_ioctl_filter(struct ifnet *, u_long, void *); |
83 | static void agr_reset_iftype(struct ifnet *); |
84 | static int agr_config_promisc(struct agr_softc *); |
85 | static int agrport_config_promisc_callback(struct agr_port *, void *); |
86 | static int agrport_config_promisc(struct agr_port *, bool); |
87 | static int agrport_cleanup(struct agr_softc *, struct agr_port *); |
88 | |
89 | static int agr_enter(struct agr_softc *); |
90 | static void agr_exit(struct agr_softc *); |
91 | static int agr_pause(struct agr_softc *); |
92 | static void agr_evacuate(struct agr_softc *); |
93 | static void agr_sync(void); |
94 | static void agr_ports_lock(struct agr_softc *); |
95 | static void agr_ports_unlock(struct agr_softc *); |
96 | static bool agr_ports_enter(struct agr_softc *); |
97 | static void agr_ports_exit(struct agr_softc *); |
98 | |
99 | static struct if_clone agr_cloner = |
100 | IF_CLONE_INITIALIZER("agr" , agr_clone_create, agr_clone_destroy); |
101 | |
102 | static u_int agr_count; |
103 | |
104 | /* |
105 | * EXPORTED FUNCTIONS |
106 | */ |
107 | |
108 | /* |
109 | * agrattch: device attach routine. |
110 | */ |
111 | |
112 | void |
113 | agrattach(int count) |
114 | { |
115 | |
116 | /* |
117 | * Nothing to do here, initialization is handled by the |
118 | * module initialization code in agrinit() below). |
119 | */ |
120 | } |
121 | |
122 | static void |
123 | agrinit(void) |
124 | { |
125 | if_clone_attach(&agr_cloner); |
126 | } |
127 | |
128 | static int |
129 | agrdetach(void) |
130 | { |
131 | int error = 0; |
132 | |
133 | if (agr_count != 0) |
134 | error = EBUSY; |
135 | |
136 | if (error == 0) |
137 | if_clone_detach(&agr_cloner); |
138 | |
139 | return error; |
140 | } |
141 | |
142 | /* |
143 | * agr_input: frame collector. |
144 | */ |
145 | |
146 | void |
147 | agr_input(struct ifnet *ifp_port, struct mbuf *m) |
148 | { |
149 | struct agr_port *port; |
150 | struct ifnet *ifp; |
151 | #if NVLAN > 0 |
152 | struct m_tag *mtag; |
153 | #endif |
154 | |
155 | port = ifp_port->if_agrprivate; |
156 | KASSERT(port); |
157 | ifp = port->port_agrifp; |
158 | if ((port->port_flags & AGRPORT_COLLECTING) == 0) { |
159 | m_freem(m); |
160 | ifp->if_ierrors++; |
161 | return; |
162 | } |
163 | |
164 | ifp->if_ipackets++; |
165 | m_set_rcvif(m, ifp); |
166 | |
167 | #define DNH_DEBUG |
168 | #if NVLAN > 0 |
169 | /* got a vlan packet? */ |
170 | if ((mtag = m_tag_find(m, PACKET_TAG_VLAN, NULL)) != NULL) { |
171 | #ifdef DNH_DEBUG |
172 | printf("%s: vlan tag %d attached\n" , |
173 | ifp->if_xname, |
174 | htole16((*(u_int *)(mtag + 1)) & 0xffff)); |
175 | printf("%s: vlan input\n" , ifp->if_xname); |
176 | #endif |
177 | vlan_input(ifp, m); |
178 | return; |
179 | #ifdef DNH_DEBUG |
180 | } else { |
181 | struct ethercom *ec = (void *)ifp; |
182 | printf("%s: no vlan tag attached, ec_nvlans=%d\n" , |
183 | ifp->if_xname, ec->ec_nvlans); |
184 | #endif |
185 | } |
186 | #endif |
187 | |
188 | bpf_mtap(ifp, m); |
189 | if_percpuq_enqueue(ifp->if_percpuq, m); |
190 | } |
191 | |
192 | /* |
193 | * EXPORTED AGR-INTERNAL FUNCTIONS |
194 | */ |
195 | |
196 | void |
197 | agr_lock(struct agr_softc *sc) |
198 | { |
199 | |
200 | mutex_enter(&sc->sc_lock); |
201 | } |
202 | |
203 | void |
204 | agr_unlock(struct agr_softc *sc) |
205 | { |
206 | |
207 | mutex_exit(&sc->sc_lock); |
208 | } |
209 | |
210 | /* |
211 | * agr_xmit_frame: transmit a pre-built frame. |
212 | */ |
213 | |
214 | int |
215 | agr_xmit_frame(struct ifnet *ifp_port, struct mbuf *m) |
216 | { |
217 | int error; |
218 | |
219 | struct sockaddr_storage dst0; |
220 | struct sockaddr *dst; |
221 | int hdrlen; |
222 | |
223 | /* |
224 | * trim off link level header and let if_output re-add it. |
225 | * XXX better to introduce an API to transmit pre-built frames. |
226 | */ |
227 | |
228 | hdrlen = ifp_port->if_hdrlen; |
229 | if (m->m_pkthdr.len < hdrlen) { |
230 | m_freem(m); |
231 | return EINVAL; |
232 | } |
233 | memset(&dst0, 0, sizeof(dst0)); |
234 | dst = (struct sockaddr *)&dst0; |
235 | dst->sa_family = pseudo_AF_HDRCMPLT; |
236 | dst->sa_len = hdrlen; |
237 | m_copydata(m, 0, hdrlen, &dst->sa_data); |
238 | m_adj(m, hdrlen); |
239 | |
240 | error = if_output_lock(ifp_port, ifp_port, m, dst, NULL); |
241 | |
242 | return error; |
243 | } |
244 | |
245 | int |
246 | agrport_ioctl(struct agr_port *port, u_long cmd, void *arg) |
247 | { |
248 | struct ifnet *ifp = port->port_ifp; |
249 | |
250 | KASSERT(ifp->if_agrprivate == (void *)port); |
251 | KASSERT(ifp->if_ioctl == agr_ioctl_filter); |
252 | |
253 | return (*port->port_ioctl)(ifp, cmd, arg); |
254 | } |
255 | |
256 | /* |
257 | * INTERNAL FUNCTIONS |
258 | */ |
259 | |
260 | /* |
261 | * Enable vlan hardware assist for the specified port. |
262 | */ |
263 | static int |
264 | agr_vlan_add(struct agr_port *port, void *arg) |
265 | { |
266 | struct ifnet *ifp = port->port_ifp; |
267 | struct ethercom *ec_port = (void *)ifp; |
268 | int error=0; |
269 | |
270 | if (ec_port->ec_nvlans++ == 0 && |
271 | (ec_port->ec_capabilities & ETHERCAP_VLAN_MTU) != 0) { |
272 | struct ifnet *p = port->port_ifp; |
273 | /* |
274 | * Enable Tx/Rx of VLAN-sized frames. |
275 | */ |
276 | ec_port->ec_capenable |= ETHERCAP_VLAN_MTU; |
277 | if (p->if_flags & IFF_UP) { |
278 | error = if_flags_set(p, p->if_flags); |
279 | if (error) { |
280 | if (ec_port->ec_nvlans-- == 1) |
281 | ec_port->ec_capenable &= |
282 | ~ETHERCAP_VLAN_MTU; |
283 | return (error); |
284 | } |
285 | } |
286 | } |
287 | |
288 | return error; |
289 | } |
290 | |
291 | /* |
292 | * Disable vlan hardware assist for the specified port. |
293 | */ |
294 | static int |
295 | agr_vlan_del(struct agr_port *port, void *arg) |
296 | { |
297 | struct ethercom *ec_port = (void *)port->port_ifp; |
298 | |
299 | /* Disable vlan support */ |
300 | if (ec_port->ec_nvlans-- == 1) { |
301 | /* |
302 | * Disable Tx/Rx of VLAN-sized frames. |
303 | */ |
304 | ec_port->ec_capenable &= ~ETHERCAP_VLAN_MTU; |
305 | if (port->port_ifp->if_flags & IFF_UP) { |
306 | (void)if_flags_set(port->port_ifp, |
307 | port->port_ifp->if_flags); |
308 | } |
309 | } |
310 | |
311 | return 0; |
312 | } |
313 | |
314 | |
315 | /* |
316 | * Check for vlan attach/detach. |
317 | * ec->ec_nvlans is directly modified by the vlan driver. |
318 | * We keep a local count in sc (sc->sc_nvlans) to detect |
319 | * when the vlan driver attaches or detaches. |
320 | * Note the agr interface must be up for this to work. |
321 | */ |
322 | static void |
323 | agr_vlan_check(struct ifnet *ifp, struct agr_softc *sc) |
324 | { |
325 | struct ethercom *ec = (void *)ifp; |
326 | |
327 | /* vlans in sync? */ |
328 | if (sc->sc_nvlans == ec->ec_nvlans) { |
329 | return; |
330 | } |
331 | |
332 | if (sc->sc_nvlans == 0) { |
333 | /* vlan added */ |
334 | agr_port_foreach(sc, agr_vlan_add, NULL); |
335 | sc->sc_nvlans = ec->ec_nvlans; |
336 | } else if (ec->ec_nvlans == 0) { |
337 | /* vlan removed */ |
338 | agr_port_foreach(sc, agr_vlan_del, NULL); |
339 | sc->sc_nvlans = 0; |
340 | } |
341 | } |
342 | |
343 | static int |
344 | agr_clone_create(struct if_clone *ifc, int unit) |
345 | { |
346 | struct agr_softc *sc; |
347 | struct ifnet *ifp; |
348 | |
349 | sc = agr_alloc_softc(); |
350 | TAILQ_INIT(&sc->sc_ports); |
351 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NET); |
352 | mutex_init(&sc->sc_entry_mtx, MUTEX_DEFAULT, IPL_NONE); |
353 | cv_init(&sc->sc_insc_cv, "agrsoftc" ); |
354 | cv_init(&sc->sc_ports_cv, "agrports" ); |
355 | agrtimer_init(sc); |
356 | ifp = &sc->sc_if; |
357 | snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d" , |
358 | ifc->ifc_name, unit); |
359 | |
360 | ifp->if_softc = sc; |
361 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; |
362 | ifp->if_start = agr_start; |
363 | ifp->if_ioctl = agr_ioctl; |
364 | IFQ_SET_READY(&ifp->if_snd); |
365 | |
366 | if_attach(ifp); |
367 | |
368 | agr_reset_iftype(ifp); |
369 | atomic_inc_uint(&agr_count); |
370 | return 0; |
371 | } |
372 | |
373 | static void |
374 | agr_reset_iftype(struct ifnet *ifp) |
375 | { |
376 | |
377 | ifp->if_type = IFT_OTHER; |
378 | ifp->if_dlt = DLT_NULL; |
379 | ifp->if_addrlen = 0; |
380 | if_alloc_sadl(ifp); |
381 | } |
382 | |
383 | static int |
384 | agr_clone_destroy(struct ifnet *ifp) |
385 | { |
386 | struct agr_softc *sc = ifp->if_softc; |
387 | int error; |
388 | |
389 | if ((error = agr_pause(sc)) != 0) |
390 | return error; |
391 | |
392 | if_detach(ifp); |
393 | agrtimer_destroy(sc); |
394 | /* Now that the ifnet has been detached, and our |
395 | * component ifnets are disconnected, there can be |
396 | * no new threads in the softc. Wait for every |
397 | * thread to get out of the softc. |
398 | */ |
399 | agr_evacuate(sc); |
400 | mutex_destroy(&sc->sc_lock); |
401 | mutex_destroy(&sc->sc_entry_mtx); |
402 | cv_destroy(&sc->sc_insc_cv); |
403 | cv_destroy(&sc->sc_ports_cv); |
404 | agr_free_softc(sc); |
405 | |
406 | atomic_dec_uint(&agr_count); |
407 | return 0; |
408 | } |
409 | |
410 | static struct agr_port * |
411 | agr_select_tx_port(struct agr_softc *sc, struct mbuf *m) |
412 | { |
413 | |
414 | return (*sc->sc_iftop->iftop_select_tx_port)(sc, m); |
415 | } |
416 | |
417 | #if 0 /* "generic" version */ |
418 | static struct agr_port * |
419 | agr_select_tx_port(struct agr_softc *sc, struct mbuf *m) |
420 | { |
421 | struct agr_port *port; |
422 | uint32_t hash; |
423 | |
424 | hash = (*sc->sc_iftop->iftop_hashmbuf)(sc, m); |
425 | if (sc->sc_nports == 0) |
426 | return NULL; |
427 | hash %= sc->sc_nports; |
428 | port = TAILQ_FIRST(&sc->sc_ports); |
429 | KASSERT(port != NULL); |
430 | while (hash--) { |
431 | port = TAILQ_NEXT(port, port_q); |
432 | KASSERT(port != NULL); |
433 | } |
434 | |
435 | return port; |
436 | } |
437 | #endif /* 0 */ |
438 | |
439 | static void |
440 | agr_start(struct ifnet *ifp) |
441 | { |
442 | struct agr_softc *sc = ifp->if_softc; |
443 | struct mbuf *m; |
444 | |
445 | AGR_LOCK(sc); |
446 | |
447 | while (/* CONSTCOND */ 1) { |
448 | struct agr_port *port; |
449 | |
450 | IFQ_DEQUEUE(&ifp->if_snd, m); |
451 | if (m == NULL) { |
452 | break; |
453 | } |
454 | bpf_mtap(ifp, m); |
455 | port = agr_select_tx_port(sc, m); |
456 | if (port) { |
457 | int error; |
458 | |
459 | error = agr_xmit_frame(port->port_ifp, m); |
460 | if (error) { |
461 | ifp->if_oerrors++; |
462 | } else { |
463 | ifp->if_opackets++; |
464 | } |
465 | } else { |
466 | m_freem(m); |
467 | ifp->if_oerrors++; |
468 | } |
469 | } |
470 | |
471 | AGR_UNLOCK(sc); |
472 | |
473 | ifp->if_flags &= ~IFF_OACTIVE; |
474 | } |
475 | |
476 | static int |
477 | agr_setconfig(struct agr_softc *sc, const struct agrreq *ar) |
478 | { |
479 | struct ifnet *ifp = &sc->sc_if; |
480 | int cmd = ar->ar_cmd; |
481 | struct ifnet *ifp_port; |
482 | int error = 0; |
483 | char ifname[IFNAMSIZ]; |
484 | |
485 | memset(ifname, 0, sizeof(ifname)); |
486 | error = copyin(ar->ar_buf, ifname, |
487 | MIN(ar->ar_buflen, sizeof(ifname) - 1)); |
488 | if (error) { |
489 | return error; |
490 | } |
491 | ifp_port = ifunit(ifname); |
492 | if (ifp_port == NULL) { |
493 | return ENOENT; |
494 | } |
495 | |
496 | agr_ports_lock(sc); |
497 | switch (cmd) { |
498 | case AGRCMD_ADDPORT: |
499 | error = agr_addport(ifp, ifp_port); |
500 | break; |
501 | |
502 | case AGRCMD_REMPORT: |
503 | error = agr_remport(ifp, ifp_port); |
504 | break; |
505 | |
506 | default: |
507 | error = EINVAL; |
508 | break; |
509 | } |
510 | agr_ports_unlock(sc); |
511 | |
512 | return error; |
513 | } |
514 | |
515 | static int |
516 | agr_getportlist(struct agr_softc *sc, struct agrreq *ar) |
517 | { |
518 | struct agr_port *port; |
519 | struct agrportlist apl; |
520 | struct agrportinfo api; |
521 | char *cp = ar->ar_buf; |
522 | size_t bufleft = (cp == NULL) ? 0 : ar->ar_buflen; |
523 | int error; |
524 | |
525 | if (cp != NULL) { |
526 | memset(&apl, 0, sizeof(apl)); |
527 | memset(&api, 0, sizeof(api)); |
528 | |
529 | if (bufleft < sizeof(apl)) { |
530 | return E2BIG; |
531 | } |
532 | apl.apl_nports = sc->sc_nports; |
533 | error = copyout(&apl, cp, sizeof(apl)); |
534 | if (error) { |
535 | return error; |
536 | } |
537 | cp += sizeof(apl); |
538 | } |
539 | bufleft -= sizeof(apl); |
540 | |
541 | TAILQ_FOREACH(port, &sc->sc_ports, port_q) { |
542 | if (cp != NULL) { |
543 | if (bufleft < sizeof(api)) { |
544 | return E2BIG; |
545 | } |
546 | memcpy(api.api_ifname, port->port_ifp->if_xname, |
547 | sizeof(api.api_ifname)); |
548 | api.api_flags = 0; |
549 | if (port->port_flags & AGRPORT_COLLECTING) { |
550 | api.api_flags |= AGRPORTINFO_COLLECTING; |
551 | } |
552 | if (port->port_flags & AGRPORT_DISTRIBUTING) { |
553 | api.api_flags |= AGRPORTINFO_DISTRIBUTING; |
554 | } |
555 | error = copyout(&api, cp, sizeof(api)); |
556 | if (error) { |
557 | return error; |
558 | } |
559 | cp += sizeof(api); |
560 | } |
561 | bufleft -= sizeof(api); |
562 | } |
563 | |
564 | if (cp == NULL) { |
565 | ar->ar_buflen = -bufleft; /* necessary buffer size */ |
566 | } |
567 | |
568 | return 0; |
569 | } |
570 | |
571 | static int |
572 | agr_getconfig(struct agr_softc *sc, struct agrreq *ar) |
573 | { |
574 | int cmd = ar->ar_cmd; |
575 | int error; |
576 | |
577 | (void)agr_ports_enter(sc); |
578 | switch (cmd) { |
579 | case AGRCMD_PORTLIST: |
580 | error = agr_getportlist(sc, ar); |
581 | break; |
582 | |
583 | default: |
584 | error = EINVAL; |
585 | break; |
586 | } |
587 | agr_ports_exit(sc); |
588 | |
589 | return error; |
590 | } |
591 | |
592 | static int |
593 | agr_addport(struct ifnet *ifp, struct ifnet *ifp_port) |
594 | { |
595 | const struct ifaddr *ifa; |
596 | struct agr_softc *sc = ifp->if_softc; |
597 | struct agr_port *port = NULL; |
598 | int error = 0; |
599 | int s; |
600 | |
601 | if (ifp_port->if_ioctl == NULL) { |
602 | error = EOPNOTSUPP; |
603 | goto out; |
604 | } |
605 | |
606 | if (ifp_port->if_agrprivate) { |
607 | error = EBUSY; |
608 | goto out; |
609 | } |
610 | |
611 | if (ifp_port->if_start == agr_start) { |
612 | error = EINVAL; |
613 | goto out; |
614 | } |
615 | |
616 | port = malloc(sizeof(*port) + ifp_port->if_addrlen, M_DEVBUF, |
617 | M_WAITOK | M_ZERO); |
618 | if (port == NULL) { |
619 | error = ENOMEM; |
620 | goto out; |
621 | } |
622 | port->port_flags = AGRPORT_LARVAL; |
623 | |
624 | s = pserialize_read_enter(); |
625 | IFADDR_READER_FOREACH(ifa, ifp_port) { |
626 | if (ifa->ifa_addr->sa_family != AF_LINK) { |
627 | pserialize_read_exit(s); |
628 | error = EBUSY; |
629 | goto out; |
630 | } |
631 | } |
632 | pserialize_read_exit(s); |
633 | |
634 | if (sc->sc_nports == 0) { |
635 | switch (ifp_port->if_type) { |
636 | case IFT_ETHER: |
637 | sc->sc_iftop = &agrether_ops; |
638 | break; |
639 | |
640 | default: |
641 | error = EPROTONOSUPPORT; /* XXX */ |
642 | goto out; |
643 | } |
644 | |
645 | error = (*sc->sc_iftop->iftop_ctor)(sc, ifp_port); |
646 | if (error) |
647 | goto out; |
648 | agrtimer_start(sc); |
649 | } else { |
650 | if (ifp->if_type != ifp_port->if_type) { |
651 | error = EINVAL; |
652 | goto out; |
653 | } |
654 | if (ifp->if_addrlen != ifp_port->if_addrlen) { |
655 | error = EINVAL; |
656 | goto out; |
657 | } |
658 | } |
659 | |
660 | memcpy(port->port_origlladdr, CLLADDR(ifp_port->if_sadl), |
661 | ifp_port->if_addrlen); |
662 | |
663 | /* |
664 | * start to modify ifp_port. |
665 | */ |
666 | |
667 | /* |
668 | * XXX this should probably be SIOCALIFADDR but that doesn't |
669 | * appear to work (ENOTTY). We want to change the mac address |
670 | * of each port to that of the first port. No need for arps |
671 | * since there are no inet addresses assigned to the ports. |
672 | */ |
673 | error = if_addr_init(ifp_port, ifp->if_dl, true); |
674 | |
675 | if (error) { |
676 | printf("%s: if_addr_init error %d\n" , __func__, error); |
677 | goto cleanup; |
678 | } |
679 | port->port_flags |= AGRPORT_LADDRCHANGED; |
680 | |
681 | ifp->if_type = ifp_port->if_type; |
682 | AGR_LOCK(sc); |
683 | |
684 | port->port_ifp = ifp_port; |
685 | ifp_port->if_agrprivate = port; |
686 | port->port_agrifp = ifp; |
687 | TAILQ_INSERT_TAIL(&sc->sc_ports, port, port_q); |
688 | sc->sc_nports++; |
689 | |
690 | port->port_ioctl = ifp_port->if_ioctl; |
691 | ifp_port->if_ioctl = agr_ioctl_filter; |
692 | |
693 | port->port_flags |= AGRPORT_ATTACHED; |
694 | |
695 | AGR_UNLOCK(sc); |
696 | |
697 | error = (*sc->sc_iftop->iftop_portinit)(sc, port); |
698 | if (error) { |
699 | printf("%s: portinit error %d\n" , __func__, error); |
700 | goto cleanup; |
701 | } |
702 | |
703 | ifp->if_flags |= IFF_RUNNING; |
704 | |
705 | agrport_config_promisc(port, (ifp->if_flags & IFF_PROMISC) != 0); |
706 | error = (*sc->sc_iftop->iftop_configmulti_port)(sc, port, true); |
707 | if (error) { |
708 | printf("%s: configmulti error %d\n" , __func__, error); |
709 | goto cleanup; |
710 | } |
711 | |
712 | AGR_LOCK(sc); |
713 | port->port_flags &= ~AGRPORT_LARVAL; |
714 | AGR_UNLOCK(sc); |
715 | out: |
716 | if (error && port) { |
717 | free(port, M_DEVBUF); |
718 | } |
719 | return error; |
720 | |
721 | cleanup: |
722 | if (agrport_cleanup(sc, port)) { |
723 | printf("%s: error on cleanup\n" , __func__); |
724 | |
725 | port = NULL; /* XXX */ |
726 | } |
727 | |
728 | if (sc->sc_nports == 0) { |
729 | KASSERT(TAILQ_EMPTY(&sc->sc_ports)); |
730 | agrtimer_stop(sc); |
731 | (*sc->sc_iftop->iftop_dtor)(sc); |
732 | sc->sc_iftop = NULL; |
733 | agr_reset_iftype(ifp); |
734 | } else { |
735 | KASSERT(!TAILQ_EMPTY(&sc->sc_ports)); |
736 | } |
737 | |
738 | goto out; |
739 | } |
740 | |
741 | static int |
742 | agr_remport(struct ifnet *ifp, struct ifnet *ifp_port) |
743 | { |
744 | struct agr_softc *sc = ifp->if_softc; |
745 | struct agr_port *port; |
746 | int error = 0; |
747 | |
748 | if (ifp_port->if_agrprivate == NULL) { |
749 | error = ENOENT; |
750 | return error; |
751 | } |
752 | |
753 | port = ifp_port->if_agrprivate; |
754 | if (port->port_agrifp != ifp) { |
755 | error = EINVAL; |
756 | return error; |
757 | } |
758 | |
759 | KASSERT(sc->sc_nports > 0); |
760 | |
761 | AGR_LOCK(sc); |
762 | port->port_flags |= AGRPORT_DETACHING; |
763 | AGR_UNLOCK(sc); |
764 | |
765 | error = (*sc->sc_iftop->iftop_portfini)(sc, port); |
766 | if (error) { |
767 | /* XXX XXX */ |
768 | printf("%s: portfini error %d\n" , __func__, error); |
769 | goto out; |
770 | } |
771 | |
772 | error = (*sc->sc_iftop->iftop_configmulti_port)(sc, port, false); |
773 | if (error) { |
774 | /* XXX XXX */ |
775 | printf("%s: configmulti_port error %d\n" , __func__, error); |
776 | goto out; |
777 | } |
778 | |
779 | error = agrport_cleanup(sc, port); |
780 | if (error) { |
781 | /* XXX XXX */ |
782 | printf("%s: agrport_cleanup error %d\n" , __func__, error); |
783 | goto out; |
784 | } |
785 | |
786 | free(port, M_DEVBUF); |
787 | |
788 | out: |
789 | if (sc->sc_nports == 0) { |
790 | KASSERT(TAILQ_EMPTY(&sc->sc_ports)); |
791 | agrtimer_stop(sc); |
792 | (*sc->sc_iftop->iftop_dtor)(sc); |
793 | sc->sc_iftop = NULL; |
794 | /* XXX should purge all addresses? */ |
795 | agr_reset_iftype(ifp); |
796 | } else { |
797 | KASSERT(!TAILQ_EMPTY(&sc->sc_ports)); |
798 | } |
799 | |
800 | return error; |
801 | } |
802 | |
803 | static int |
804 | agrport_cleanup(struct agr_softc *sc, struct agr_port *port) |
805 | { |
806 | struct ifnet *ifp_port = port->port_ifp; |
807 | int error; |
808 | int result = 0; |
809 | |
810 | error = agrport_config_promisc(port, false); |
811 | if (error) { |
812 | printf("%s: config_promisc error %d\n" , __func__, error); |
813 | result = error; |
814 | } |
815 | |
816 | if ((port->port_flags & AGRPORT_LADDRCHANGED)) { |
817 | #if 0 |
818 | memcpy(LLADDR(ifp_port->if_sadl), port->port_origlladdr, |
819 | ifp_port->if_addrlen); |
820 | if (ifp_port->if_init != NULL) { |
821 | error = (*ifp_port->if_init)(ifp_port); |
822 | } |
823 | #else |
824 | union { |
825 | struct sockaddr sa; |
826 | struct sockaddr_dl sdl; |
827 | struct sockaddr_storage ss; |
828 | } u; |
829 | struct ifaddr ifa; |
830 | |
831 | sockaddr_dl_init(&u.sdl, sizeof(u.ss), |
832 | 0, ifp_port->if_type, NULL, 0, |
833 | port->port_origlladdr, ifp_port->if_addrlen); |
834 | memset(&ifa, 0, sizeof(ifa)); |
835 | ifa.ifa_addr = &u.sa; |
836 | error = agrport_ioctl(port, SIOCINITIFADDR, &ifa); |
837 | #endif |
838 | if (error) { |
839 | printf("%s: if_init error %d\n" , __func__, error); |
840 | result = error; |
841 | } else { |
842 | port->port_flags &= ~AGRPORT_LADDRCHANGED; |
843 | } |
844 | } |
845 | |
846 | AGR_LOCK(sc); |
847 | if ((port->port_flags & AGRPORT_ATTACHED)) { |
848 | ifp_port->if_agrprivate = NULL; |
849 | |
850 | TAILQ_REMOVE(&sc->sc_ports, port, port_q); |
851 | sc->sc_nports--; |
852 | |
853 | KASSERT(ifp_port->if_ioctl == agr_ioctl_filter); |
854 | ifp_port->if_ioctl = port->port_ioctl; |
855 | |
856 | port->port_flags &= ~AGRPORT_ATTACHED; |
857 | } |
858 | AGR_UNLOCK(sc); |
859 | |
860 | return result; |
861 | } |
862 | |
863 | static int |
864 | agr_ioctl_multi(struct ifnet *ifp, u_long cmd, struct ifreq *ifr) |
865 | { |
866 | struct agr_softc *sc = ifp->if_softc; |
867 | int error; |
868 | |
869 | error = (*sc->sc_iftop->iftop_configmulti_ifreq)(sc, ifr, |
870 | (cmd == SIOCADDMULTI)); |
871 | |
872 | return error; |
873 | } |
874 | |
875 | /* |
876 | * XXX an incomplete hack; can't filter ioctls handled ifioctl(). |
877 | * |
878 | * the intention here is to prevent operations on underlying interfaces |
879 | * so that their states are not changed in the way that agr(4) doesn't |
880 | * expect. cf. the BUGS section in the agr(4) manual page. |
881 | */ |
882 | static int |
883 | agr_ioctl_filter(struct ifnet *ifp, u_long cmd, void *arg) |
884 | { |
885 | struct agr_port *port = ifp->if_agrprivate; |
886 | int error; |
887 | |
888 | KASSERT(port); |
889 | |
890 | switch (cmd) { |
891 | case SIOCADDMULTI: /* add m'cast addr */ |
892 | case SIOCAIFADDR: /* add/chg IF alias */ |
893 | case SIOCALIFADDR: /* add IF addr */ |
894 | case SIOCDELMULTI: /* del m'cast addr */ |
895 | case SIOCDIFADDR: /* delete IF addr */ |
896 | case SIOCDIFPHYADDR: /* delete gif addrs */ |
897 | case SIOCDLIFADDR: /* delete IF addr */ |
898 | case SIOCINITIFADDR: |
899 | case SIOCSDRVSPEC: /* set driver-specific parameters */ |
900 | case SIOCSIFADDR: /* set ifnet address */ |
901 | case SIOCSIFBRDADDR: /* set broadcast addr */ |
902 | case SIOCSIFDSTADDR: /* set p-p address */ |
903 | case SIOCSIFGENERIC: /* generic IF set op */ |
904 | case SIOCSIFMEDIA: /* set net media */ |
905 | case SIOCSIFMETRIC: /* set IF metric */ |
906 | case SIOCSIFMTU: /* set ifnet mtu */ |
907 | case SIOCSIFNETMASK: /* set net addr mask */ |
908 | case SIOCSIFPHYADDR: /* set gif addres */ |
909 | case SIOCSLIFPHYADDR: /* set gif addrs */ |
910 | case SIOCSVH: /* set carp param */ |
911 | error = EBUSY; |
912 | break; |
913 | case SIOCSIFCAP: /* XXX */ |
914 | case SIOCSIFFLAGS: /* XXX */ |
915 | default: |
916 | error = agrport_ioctl(port, cmd, arg); |
917 | break; |
918 | } |
919 | return error; |
920 | } |
921 | |
922 | static int |
923 | agrreq_copyin(const void *ubuf, struct agrreq *ar) |
924 | { |
925 | int error; |
926 | |
927 | error = copyin(ubuf, ar, sizeof(*ar)); |
928 | if (error) { |
929 | return error; |
930 | } |
931 | |
932 | if (ar->ar_version != AGRREQ_VERSION) { |
933 | return EINVAL; |
934 | } |
935 | |
936 | return 0; |
937 | } |
938 | |
939 | static int |
940 | agrreq_copyout(void *ubuf, struct agrreq *ar) |
941 | { |
942 | int error; |
943 | |
944 | KASSERT(ar->ar_version == AGRREQ_VERSION); |
945 | |
946 | error = copyout(ar, ubuf, sizeof(*ar)); |
947 | if (error) { |
948 | return error; |
949 | } |
950 | |
951 | return 0; |
952 | } |
953 | |
954 | /* Make sure that if any interrupt handlers are out of the softc. */ |
955 | static void |
956 | agr_sync(void) |
957 | { |
958 | uint64_t h; |
959 | |
960 | if (!mp_online) |
961 | return; |
962 | |
963 | h = xc_broadcast(0, (xcfunc_t)nullop, NULL, NULL); |
964 | xc_wait(h); |
965 | } |
966 | |
967 | static int |
968 | agr_pause(struct agr_softc *sc) |
969 | { |
970 | int error; |
971 | |
972 | mutex_enter(&sc->sc_entry_mtx); |
973 | if ((error = sc->sc_noentry) != 0) |
974 | goto out; |
975 | |
976 | sc->sc_noentry = EBUSY; |
977 | |
978 | while (sc->sc_insc != 0) |
979 | cv_wait(&sc->sc_insc_cv, &sc->sc_entry_mtx); |
980 | |
981 | if (sc->sc_nports == 0) { |
982 | sc->sc_noentry = ENXIO; |
983 | } else { |
984 | sc->sc_noentry = 0; |
985 | error = EBUSY; |
986 | } |
987 | cv_broadcast(&sc->sc_insc_cv); |
988 | out: |
989 | mutex_exit(&sc->sc_entry_mtx); |
990 | return error; |
991 | } |
992 | |
993 | static void |
994 | agr_evacuate(struct agr_softc *sc) |
995 | { |
996 | mutex_enter(&sc->sc_entry_mtx); |
997 | cv_broadcast(&sc->sc_insc_cv); |
998 | while (sc->sc_insc != 0 || sc->sc_paused != 0) |
999 | cv_wait(&sc->sc_insc_cv, &sc->sc_entry_mtx); |
1000 | mutex_exit(&sc->sc_entry_mtx); |
1001 | |
1002 | agr_sync(); |
1003 | } |
1004 | |
1005 | static int |
1006 | agr_enter(struct agr_softc *sc) |
1007 | { |
1008 | int error; |
1009 | |
1010 | mutex_enter(&sc->sc_entry_mtx); |
1011 | sc->sc_paused++; |
1012 | while ((error = sc->sc_noentry) == EBUSY) |
1013 | cv_wait(&sc->sc_insc_cv, &sc->sc_entry_mtx); |
1014 | sc->sc_paused--; |
1015 | if (error == 0) |
1016 | sc->sc_insc++; |
1017 | mutex_exit(&sc->sc_entry_mtx); |
1018 | |
1019 | return error; |
1020 | } |
1021 | |
1022 | static void |
1023 | agr_exit(struct agr_softc *sc) |
1024 | { |
1025 | mutex_enter(&sc->sc_entry_mtx); |
1026 | if (--sc->sc_insc == 0) |
1027 | cv_signal(&sc->sc_insc_cv); |
1028 | mutex_exit(&sc->sc_entry_mtx); |
1029 | } |
1030 | |
1031 | static bool |
1032 | agr_ports_enter(struct agr_softc *sc) |
1033 | { |
1034 | mutex_enter(&sc->sc_entry_mtx); |
1035 | while (sc->sc_wrports) |
1036 | cv_wait(&sc->sc_ports_cv, &sc->sc_entry_mtx); |
1037 | sc->sc_rdports++; |
1038 | mutex_exit(&sc->sc_entry_mtx); |
1039 | |
1040 | return true; |
1041 | } |
1042 | |
1043 | static void |
1044 | agr_ports_exit(struct agr_softc *sc) |
1045 | { |
1046 | mutex_enter(&sc->sc_entry_mtx); |
1047 | if (--sc->sc_rdports == 0) |
1048 | cv_signal(&sc->sc_ports_cv); |
1049 | mutex_exit(&sc->sc_entry_mtx); |
1050 | } |
1051 | |
1052 | static void |
1053 | agr_ports_lock(struct agr_softc *sc) |
1054 | { |
1055 | mutex_enter(&sc->sc_entry_mtx); |
1056 | while (sc->sc_rdports != 0) |
1057 | cv_wait(&sc->sc_ports_cv, &sc->sc_entry_mtx); |
1058 | sc->sc_wrports = true; |
1059 | mutex_exit(&sc->sc_entry_mtx); |
1060 | } |
1061 | |
1062 | static void |
1063 | agr_ports_unlock(struct agr_softc *sc) |
1064 | { |
1065 | mutex_enter(&sc->sc_entry_mtx); |
1066 | sc->sc_wrports = false; |
1067 | cv_signal(&sc->sc_ports_cv); |
1068 | mutex_exit(&sc->sc_entry_mtx); |
1069 | } |
1070 | |
1071 | static int |
1072 | agr_ioctl(struct ifnet *ifp, const u_long cmd, void *data) |
1073 | { |
1074 | struct agr_softc *sc = ifp->if_softc; |
1075 | struct ifreq *ifr = (struct ifreq *)data; |
1076 | struct ifaddr *ifa = (struct ifaddr *)data; |
1077 | struct agrreq ar; |
1078 | int error; |
1079 | bool in_ports = false; |
1080 | int s; |
1081 | |
1082 | if ((error = agr_enter(sc)) != 0) |
1083 | return error; |
1084 | |
1085 | s = splnet(); |
1086 | |
1087 | switch (cmd) { |
1088 | case SIOCINITIFADDR: |
1089 | in_ports = agr_ports_enter(sc); |
1090 | if (sc->sc_nports == 0) { |
1091 | error = EINVAL; |
1092 | break; |
1093 | } |
1094 | ifp->if_flags |= IFF_UP; |
1095 | switch (ifa->ifa_addr->sa_family) { |
1096 | #if defined(INET) |
1097 | case AF_INET: |
1098 | arp_ifinit(ifp, ifa); |
1099 | break; |
1100 | #endif |
1101 | default: |
1102 | break; |
1103 | } |
1104 | break; |
1105 | |
1106 | #if 0 /* notyet */ |
1107 | case SIOCSIFMTU: |
1108 | #endif |
1109 | |
1110 | case SIOCSIFFLAGS: |
1111 | /* |
1112 | * Check for a change in vlan status. This ioctl is the |
1113 | * only way we can tell that a vlan has attached or detached. |
1114 | * Note the agr interface must be up. |
1115 | */ |
1116 | agr_vlan_check(ifp, sc); |
1117 | |
1118 | if ((error = ifioctl_common(ifp, cmd, data)) != 0) |
1119 | break; |
1120 | agr_config_promisc(sc); |
1121 | break; |
1122 | |
1123 | case SIOCSETAGR: |
1124 | splx(s); |
1125 | error = kauth_authorize_network(kauth_cred_get(), |
1126 | KAUTH_NETWORK_INTERFACE, |
1127 | KAUTH_REQ_NETWORK_INTERFACE_SETPRIV, ifp, (void *)cmd, |
1128 | NULL); |
1129 | if (!error) { |
1130 | error = agrreq_copyin(ifr->ifr_data, &ar); |
1131 | } |
1132 | if (!error) { |
1133 | error = agr_setconfig(sc, &ar); |
1134 | } |
1135 | s = splnet(); |
1136 | break; |
1137 | |
1138 | case SIOCGETAGR: |
1139 | splx(s); |
1140 | error = agrreq_copyin(ifr->ifr_data, &ar); |
1141 | if (!error) { |
1142 | error = agr_getconfig(sc, &ar); |
1143 | } |
1144 | if (!error) { |
1145 | error = agrreq_copyout(ifr->ifr_data, &ar); |
1146 | } |
1147 | s = splnet(); |
1148 | break; |
1149 | |
1150 | case SIOCADDMULTI: |
1151 | case SIOCDELMULTI: |
1152 | in_ports = agr_ports_enter(sc); |
1153 | if (sc->sc_nports == 0) |
1154 | error = EINVAL; |
1155 | else |
1156 | error = agr_ioctl_multi(ifp, cmd, ifr); |
1157 | break; |
1158 | |
1159 | default: |
1160 | error = ifioctl_common(ifp, cmd, data); |
1161 | break; |
1162 | } |
1163 | |
1164 | if (in_ports) |
1165 | agr_ports_exit(sc); |
1166 | |
1167 | splx(s); |
1168 | |
1169 | agr_exit(sc); |
1170 | |
1171 | return error; |
1172 | } |
1173 | |
1174 | static int |
1175 | agr_config_promisc(struct agr_softc *sc) |
1176 | { |
1177 | int error; |
1178 | |
1179 | agr_port_foreach(sc, agrport_config_promisc_callback, &error); |
1180 | |
1181 | return error; |
1182 | } |
1183 | |
1184 | static int |
1185 | agrport_config_promisc_callback(struct agr_port *port, void *arg) |
1186 | { |
1187 | struct agr_softc *sc = AGR_SC_FROM_PORT(port); |
1188 | int *errorp = arg; |
1189 | int error; |
1190 | bool promisc; |
1191 | |
1192 | promisc = (sc->sc_if.if_flags & IFF_PROMISC) != 0; |
1193 | |
1194 | error = agrport_config_promisc(port, promisc); |
1195 | if (error) { |
1196 | *errorp = error; |
1197 | } |
1198 | |
1199 | return 0; |
1200 | } |
1201 | |
1202 | static int |
1203 | agrport_config_promisc(struct agr_port *port, bool promisc) |
1204 | { |
1205 | int error; |
1206 | |
1207 | if (( promisc && (port->port_flags & AGRPORT_PROMISC) != 0) || |
1208 | (!promisc && (port->port_flags & AGRPORT_PROMISC) == 0)) { |
1209 | return 0; |
1210 | } |
1211 | |
1212 | error = ifpromisc(port->port_ifp, promisc); |
1213 | if (error == 0) { |
1214 | if (promisc) { |
1215 | port->port_flags |= AGRPORT_PROMISC; |
1216 | } else { |
1217 | port->port_flags &= ~AGRPORT_PROMISC; |
1218 | } |
1219 | } |
1220 | |
1221 | return error; |
1222 | } |
1223 | |
1224 | /* |
1225 | * Module infrastructure |
1226 | */ |
1227 | #include <net/if_module.h> |
1228 | |
1229 | IF_MODULE(MODULE_CLASS_DRIVER, agr, "" ) |
1230 | |