Line data Source code
1 : /* $OpenBSD: if_trunk.c,v 1.137 2018/08/12 23:50:31 ccardenas Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2005, 2006, 2007 Reyk Floeter <reyk@openbsd.org>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : #include <sys/param.h>
20 : #include <sys/kernel.h>
21 : #include <sys/malloc.h>
22 : #include <sys/mbuf.h>
23 : #include <sys/queue.h>
24 : #include <sys/socket.h>
25 : #include <sys/sockio.h>
26 : #include <sys/systm.h>
27 : #include <sys/task.h>
28 : #include <sys/timeout.h>
29 :
30 : #include <crypto/siphash.h>
31 :
32 : #include <net/if.h>
33 : #include <net/if_dl.h>
34 : #include <net/if_media.h>
35 : #include <net/if_types.h>
36 :
37 : #include <netinet/in.h>
38 : #include <netinet/if_ether.h>
39 : #include <netinet/ip.h>
40 :
41 : #ifdef INET6
42 : #include <netinet/ip6.h>
43 : #endif
44 :
45 : #include <net/if_vlan_var.h>
46 : #include <net/if_trunk.h>
47 : #include <net/trunklacp.h>
48 :
49 : #include "bpfilter.h"
50 : #if NBPFILTER > 0
51 : #include <net/bpf.h>
52 : #endif
53 :
54 : SLIST_HEAD(__trhead, trunk_softc) trunk_list; /* list of trunks */
55 :
56 : void trunkattach(int);
57 : int trunk_clone_create(struct if_clone *, int);
58 : int trunk_clone_destroy(struct ifnet *);
59 : void trunk_lladdr(struct arpcom *, u_int8_t *);
60 : int trunk_capabilities(struct trunk_softc *);
61 : void trunk_port_lladdr(struct trunk_port *, u_int8_t *);
62 : int trunk_port_create(struct trunk_softc *, struct ifnet *);
63 : int trunk_port_destroy(struct trunk_port *);
64 : void trunk_port_state(void *);
65 : void trunk_port_ifdetach(void *);
66 : int trunk_port_ioctl(struct ifnet *, u_long, caddr_t);
67 : int trunk_port_output(struct ifnet *, struct mbuf *, struct sockaddr *,
68 : struct rtentry *);
69 : struct trunk_port *trunk_port_get(struct trunk_softc *, struct ifnet *);
70 : int trunk_port_checkstacking(struct trunk_softc *);
71 : void trunk_port2req(struct trunk_port *, struct trunk_reqport *);
72 : int trunk_ioctl(struct ifnet *, u_long, caddr_t);
73 : int trunk_ether_addmulti(struct trunk_softc *, struct ifreq *);
74 : int trunk_ether_delmulti(struct trunk_softc *, struct ifreq *);
75 : void trunk_ether_purgemulti(struct trunk_softc *);
76 : int trunk_ether_cmdmulti(struct trunk_port *, u_long);
77 : int trunk_ioctl_allports(struct trunk_softc *, u_long, caddr_t);
78 : int trunk_input(struct ifnet *, struct mbuf *, void *);
79 : void trunk_start(struct ifnet *);
80 : void trunk_init(struct ifnet *);
81 : void trunk_stop(struct ifnet *);
82 : int trunk_media_change(struct ifnet *);
83 : void trunk_media_status(struct ifnet *, struct ifmediareq *);
84 : struct trunk_port *trunk_link_active(struct trunk_softc *,
85 : struct trunk_port *);
86 : const void *trunk_gethdr(struct mbuf *, u_int, u_int, void *);
87 :
88 : struct if_clone trunk_cloner =
89 : IF_CLONE_INITIALIZER("trunk", trunk_clone_create, trunk_clone_destroy);
90 :
91 : /* Simple round robin */
92 : int trunk_rr_attach(struct trunk_softc *);
93 : int trunk_rr_detach(struct trunk_softc *);
94 : void trunk_rr_port_destroy(struct trunk_port *);
95 : int trunk_rr_start(struct trunk_softc *, struct mbuf *);
96 : int trunk_rr_input(struct trunk_softc *, struct trunk_port *,
97 : struct mbuf *);
98 :
99 : /* Active failover */
100 : int trunk_fail_attach(struct trunk_softc *);
101 : int trunk_fail_detach(struct trunk_softc *);
102 : int trunk_fail_port_create(struct trunk_port *);
103 : void trunk_fail_port_destroy(struct trunk_port *);
104 : int trunk_fail_start(struct trunk_softc *, struct mbuf *);
105 : int trunk_fail_input(struct trunk_softc *, struct trunk_port *,
106 : struct mbuf *);
107 : void trunk_fail_linkstate(struct trunk_port *);
108 :
109 : /* Loadbalancing */
110 : int trunk_lb_attach(struct trunk_softc *);
111 : int trunk_lb_detach(struct trunk_softc *);
112 : int trunk_lb_port_create(struct trunk_port *);
113 : void trunk_lb_port_destroy(struct trunk_port *);
114 : int trunk_lb_start(struct trunk_softc *, struct mbuf *);
115 : int trunk_lb_input(struct trunk_softc *, struct trunk_port *,
116 : struct mbuf *);
117 : int trunk_lb_porttable(struct trunk_softc *, struct trunk_port *);
118 :
119 : /* Broadcast mode */
120 : int trunk_bcast_attach(struct trunk_softc *);
121 : int trunk_bcast_detach(struct trunk_softc *);
122 : int trunk_bcast_start(struct trunk_softc *, struct mbuf *);
123 : int trunk_bcast_input(struct trunk_softc *, struct trunk_port *,
124 : struct mbuf *);
125 :
126 : /* 802.3ad LACP */
127 : int trunk_lacp_attach(struct trunk_softc *);
128 : int trunk_lacp_detach(struct trunk_softc *);
129 : int trunk_lacp_start(struct trunk_softc *, struct mbuf *);
130 : int trunk_lacp_input(struct trunk_softc *, struct trunk_port *,
131 : struct mbuf *);
132 :
133 : /* Trunk protocol table */
134 : static const struct {
135 : enum trunk_proto ti_proto;
136 : int (*ti_attach)(struct trunk_softc *);
137 : } trunk_protos[] = {
138 : { TRUNK_PROTO_ROUNDROBIN, trunk_rr_attach },
139 : { TRUNK_PROTO_FAILOVER, trunk_fail_attach },
140 : { TRUNK_PROTO_LOADBALANCE, trunk_lb_attach },
141 : { TRUNK_PROTO_BROADCAST, trunk_bcast_attach },
142 : { TRUNK_PROTO_LACP, trunk_lacp_attach },
143 : { TRUNK_PROTO_NONE, NULL }
144 : };
145 :
146 : void
147 0 : trunkattach(int count)
148 : {
149 0 : SLIST_INIT(&trunk_list);
150 0 : if_clone_attach(&trunk_cloner);
151 0 : }
152 :
153 : int
154 0 : trunk_clone_create(struct if_clone *ifc, int unit)
155 : {
156 : struct trunk_softc *tr;
157 : struct ifnet *ifp;
158 : int i, error = 0;
159 :
160 0 : tr = malloc(sizeof(*tr), M_DEVBUF, M_WAITOK|M_ZERO);
161 0 : tr->tr_unit = unit;
162 0 : tr->tr_proto = TRUNK_PROTO_NONE;
163 0 : for (i = 0; trunk_protos[i].ti_proto != TRUNK_PROTO_NONE; i++) {
164 0 : if (trunk_protos[i].ti_proto == TRUNK_PROTO_DEFAULT) {
165 0 : tr->tr_proto = trunk_protos[i].ti_proto;
166 0 : if ((error = trunk_protos[i].ti_attach(tr)) != 0) {
167 0 : free(tr, M_DEVBUF, sizeof *tr);
168 0 : return (error);
169 : }
170 : break;
171 : }
172 : }
173 0 : SLIST_INIT(&tr->tr_ports);
174 :
175 : /* Initialise pseudo media types */
176 0 : ifmedia_init(&tr->tr_media, 0, trunk_media_change,
177 : trunk_media_status);
178 0 : ifmedia_add(&tr->tr_media, IFM_ETHER | IFM_AUTO, 0, NULL);
179 0 : ifmedia_set(&tr->tr_media, IFM_ETHER | IFM_AUTO);
180 :
181 0 : ifp = &tr->tr_ac.ac_if;
182 0 : ifp->if_softc = tr;
183 0 : ifp->if_start = trunk_start;
184 0 : ifp->if_ioctl = trunk_ioctl;
185 0 : ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
186 0 : ifp->if_capabilities = trunk_capabilities(tr);
187 :
188 0 : IFQ_SET_MAXLEN(&ifp->if_snd, 1);
189 :
190 0 : snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
191 0 : ifc->ifc_name, unit);
192 :
193 : /*
194 : * Attach as an ordinary ethernet device, children will be attached
195 : * as special device IFT_IEEE8023ADLAG.
196 : */
197 0 : if_attach(ifp);
198 0 : ether_ifattach(ifp);
199 :
200 : /* Insert into the global list of trunks */
201 0 : SLIST_INSERT_HEAD(&trunk_list, tr, tr_entries);
202 :
203 0 : return (0);
204 0 : }
205 :
206 : int
207 0 : trunk_clone_destroy(struct ifnet *ifp)
208 : {
209 0 : struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
210 : struct trunk_port *tp;
211 : int error;
212 :
213 : /* Remove any multicast groups that we may have joined. */
214 0 : trunk_ether_purgemulti(tr);
215 :
216 : /* Shutdown and remove trunk ports, return on error */
217 0 : NET_LOCK();
218 0 : while ((tp = SLIST_FIRST(&tr->tr_ports)) != NULL) {
219 0 : if ((error = trunk_port_destroy(tp)) != 0) {
220 0 : NET_UNLOCK();
221 0 : return (error);
222 : }
223 : }
224 0 : NET_UNLOCK();
225 :
226 0 : ifmedia_delete_instance(&tr->tr_media, IFM_INST_ANY);
227 0 : ether_ifdetach(ifp);
228 0 : if_detach(ifp);
229 :
230 0 : SLIST_REMOVE(&trunk_list, tr, trunk_softc, tr_entries);
231 0 : free(tr, M_DEVBUF, sizeof *tr);
232 :
233 0 : return (0);
234 0 : }
235 :
236 : void
237 0 : trunk_lladdr(struct arpcom *ac, u_int8_t *lladdr)
238 : {
239 0 : struct ifnet *ifp = &ac->ac_if;
240 : struct sockaddr_dl *sdl;
241 :
242 0 : sdl = ifp->if_sadl;
243 0 : sdl->sdl_type = IFT_ETHER;
244 0 : sdl->sdl_alen = ETHER_ADDR_LEN;
245 0 : bcopy(lladdr, LLADDR(sdl), ETHER_ADDR_LEN);
246 0 : bcopy(lladdr, ac->ac_enaddr, ETHER_ADDR_LEN);
247 0 : }
248 :
249 : int
250 0 : trunk_capabilities(struct trunk_softc *tr)
251 : {
252 : struct trunk_port *tp;
253 : int cap = ~0, priv;
254 :
255 : /* Preserve private capabilities */
256 0 : priv = tr->tr_capabilities & IFCAP_TRUNK_MASK;
257 :
258 : /* Get capabilities from the trunk ports */
259 0 : SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
260 0 : cap &= tp->tp_capabilities;
261 :
262 0 : if (tr->tr_ifflags & IFF_DEBUG) {
263 0 : printf("%s: capabilities 0x%08x\n",
264 0 : tr->tr_ifname, cap == ~0 ? priv : (cap | priv));
265 0 : }
266 :
267 0 : return (cap == ~0 ? priv : (cap | priv));
268 : }
269 :
270 : void
271 0 : trunk_port_lladdr(struct trunk_port *tp, u_int8_t *lladdr)
272 : {
273 0 : struct ifnet *ifp = tp->tp_if;
274 :
275 : /* Set the link layer address */
276 0 : trunk_lladdr((struct arpcom *)ifp, lladdr);
277 :
278 : /* Reset the port to update the lladdr */
279 0 : ifnewlladdr(ifp);
280 0 : }
281 :
282 : int
283 0 : trunk_port_create(struct trunk_softc *tr, struct ifnet *ifp)
284 : {
285 : struct trunk_softc *tr_ptr;
286 : struct trunk_port *tp;
287 : int error = 0;
288 :
289 : /* Limit the maximal number of trunk ports */
290 0 : if (tr->tr_count >= TRUNK_MAX_PORTS)
291 0 : return (ENOSPC);
292 :
293 : /* Check if port has already been associated to a trunk */
294 0 : if (trunk_port_get(NULL, ifp) != NULL)
295 0 : return (EBUSY);
296 :
297 : /* XXX Disallow non-ethernet interfaces (this should be any of 802) */
298 0 : if (ifp->if_type != IFT_ETHER)
299 0 : return (EPROTONOSUPPORT);
300 :
301 : /* Take MTU from the first member port */
302 0 : if (SLIST_EMPTY(&tr->tr_ports)) {
303 0 : if (tr->tr_ifflags & IFF_DEBUG)
304 0 : printf("%s: first port, setting trunk mtu %u\n",
305 0 : tr->tr_ifname, ifp->if_mtu);
306 0 : tr->tr_ac.ac_if.if_mtu = ifp->if_mtu;
307 0 : tr->tr_ac.ac_if.if_hardmtu = ifp->if_mtu;
308 0 : } else if (tr->tr_ac.ac_if.if_mtu != ifp->if_mtu) {
309 0 : printf("%s: adding %s failed, MTU %u != %u\n", tr->tr_ifname,
310 0 : ifp->if_xname, ifp->if_mtu, tr->tr_ac.ac_if.if_mtu);
311 0 : return (EINVAL);
312 : }
313 :
314 0 : if ((error = ifpromisc(ifp, 1)) != 0)
315 0 : return (error);
316 :
317 0 : if ((tp = malloc(sizeof *tp, M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
318 0 : return (ENOMEM);
319 :
320 : /* Check if port is a stacked trunk */
321 0 : SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
322 0 : if (ifp == &tr_ptr->tr_ac.ac_if) {
323 0 : tp->tp_flags |= TRUNK_PORT_STACK;
324 0 : if (trunk_port_checkstacking(tr_ptr) >=
325 : TRUNK_MAX_STACKING) {
326 0 : free(tp, M_DEVBUF, sizeof *tp);
327 0 : return (E2BIG);
328 : }
329 : }
330 : }
331 :
332 : /* Change the interface type */
333 0 : tp->tp_iftype = ifp->if_type;
334 0 : ifp->if_type = IFT_IEEE8023ADLAG;
335 :
336 0 : tp->tp_ioctl = ifp->if_ioctl;
337 0 : ifp->if_ioctl = trunk_port_ioctl;
338 :
339 0 : tp->tp_output = ifp->if_output;
340 0 : ifp->if_output = trunk_port_output;
341 :
342 0 : tp->tp_if = ifp;
343 0 : tp->tp_trunk = tr;
344 :
345 : /* Save port link layer address */
346 0 : bcopy(((struct arpcom *)ifp)->ac_enaddr, tp->tp_lladdr, ETHER_ADDR_LEN);
347 :
348 0 : if (SLIST_EMPTY(&tr->tr_ports)) {
349 0 : tr->tr_primary = tp;
350 0 : tp->tp_flags |= TRUNK_PORT_MASTER;
351 0 : trunk_lladdr(&tr->tr_ac, tp->tp_lladdr);
352 0 : }
353 :
354 : /* Insert into the list of ports */
355 0 : SLIST_INSERT_HEAD(&tr->tr_ports, tp, tp_entries);
356 0 : tr->tr_count++;
357 :
358 : /* Update link layer address for this port */
359 0 : trunk_port_lladdr(tp,
360 0 : ((struct arpcom *)(tr->tr_primary->tp_if))->ac_enaddr);
361 :
362 : /* Update trunk capabilities */
363 0 : tr->tr_capabilities = trunk_capabilities(tr);
364 :
365 : /* Add multicast addresses to this port */
366 0 : trunk_ether_cmdmulti(tp, SIOCADDMULTI);
367 :
368 : /* Register callback for physical link state changes */
369 0 : tp->lh_cookie = hook_establish(ifp->if_linkstatehooks, 1,
370 : trunk_port_state, tp);
371 :
372 : /* Register callback if parent wants to unregister */
373 0 : tp->dh_cookie = hook_establish(ifp->if_detachhooks, 0,
374 : trunk_port_ifdetach, tp);
375 :
376 0 : if (tr->tr_port_create != NULL)
377 0 : error = (*tr->tr_port_create)(tp);
378 :
379 : /* Change input handler of the physical interface. */
380 0 : if_ih_insert(ifp, trunk_input, tp);
381 :
382 0 : return (error);
383 0 : }
384 :
385 : int
386 0 : trunk_port_checkstacking(struct trunk_softc *tr)
387 : {
388 : struct trunk_softc *tr_ptr;
389 : struct trunk_port *tp;
390 : int m = 0;
391 :
392 0 : SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
393 0 : if (tp->tp_flags & TRUNK_PORT_STACK) {
394 0 : tr_ptr = (struct trunk_softc *)tp->tp_if->if_softc;
395 0 : m = MAX(m, trunk_port_checkstacking(tr_ptr));
396 0 : }
397 : }
398 :
399 0 : return (m + 1);
400 : }
401 :
402 : int
403 0 : trunk_port_destroy(struct trunk_port *tp)
404 : {
405 0 : struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
406 : struct trunk_port *tp_ptr;
407 0 : struct ifnet *ifp = tp->tp_if;
408 :
409 : /* Restore previous input handler. */
410 0 : if_ih_remove(ifp, trunk_input, tp);
411 :
412 : /* Remove multicast addresses from this port */
413 0 : trunk_ether_cmdmulti(tp, SIOCDELMULTI);
414 :
415 : /* Port has to be down */
416 0 : if (ifp->if_flags & IFF_UP)
417 0 : if_down(ifp);
418 :
419 0 : ifpromisc(ifp, 0);
420 :
421 0 : if (tr->tr_port_destroy != NULL)
422 0 : (*tr->tr_port_destroy)(tp);
423 :
424 : /* Restore interface type. */
425 0 : ifp->if_type = tp->tp_iftype;
426 :
427 0 : ifp->if_ioctl = tp->tp_ioctl;
428 0 : ifp->if_output = tp->tp_output;
429 :
430 0 : hook_disestablish(ifp->if_linkstatehooks, tp->lh_cookie);
431 0 : hook_disestablish(ifp->if_detachhooks, tp->dh_cookie);
432 :
433 : /* Finally, remove the port from the trunk */
434 0 : SLIST_REMOVE(&tr->tr_ports, tp, trunk_port, tp_entries);
435 0 : tr->tr_count--;
436 :
437 : /* Update the primary interface */
438 0 : if (tp == tr->tr_primary) {
439 0 : u_int8_t lladdr[ETHER_ADDR_LEN];
440 :
441 0 : if ((tp_ptr = SLIST_FIRST(&tr->tr_ports)) == NULL) {
442 0 : bzero(&lladdr, ETHER_ADDR_LEN);
443 0 : } else {
444 0 : bcopy(((struct arpcom *)tp_ptr->tp_if)->ac_enaddr,
445 0 : lladdr, ETHER_ADDR_LEN);
446 0 : tp_ptr->tp_flags = TRUNK_PORT_MASTER;
447 : }
448 0 : trunk_lladdr(&tr->tr_ac, lladdr);
449 0 : tr->tr_primary = tp_ptr;
450 :
451 : /* Update link layer address for each port */
452 0 : SLIST_FOREACH(tp_ptr, &tr->tr_ports, tp_entries)
453 0 : trunk_port_lladdr(tp_ptr, lladdr);
454 0 : }
455 :
456 : /* Reset the port lladdr */
457 0 : trunk_port_lladdr(tp, tp->tp_lladdr);
458 :
459 0 : free(tp, M_DEVBUF, sizeof *tp);
460 :
461 : /* Update trunk capabilities */
462 0 : tr->tr_capabilities = trunk_capabilities(tr);
463 :
464 0 : return (0);
465 : }
466 :
467 : int
468 0 : trunk_port_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
469 : {
470 0 : struct trunk_reqport *rp = (struct trunk_reqport *)data;
471 : struct trunk_softc *tr;
472 : struct trunk_port *tp = NULL;
473 : int error = 0;
474 :
475 : /* Should be checked by the caller */
476 0 : if (ifp->if_type != IFT_IEEE8023ADLAG ||
477 0 : (tp = trunk_port_get(NULL, ifp)) == NULL ||
478 0 : (tr = (struct trunk_softc *)tp->tp_trunk) == NULL) {
479 : error = EINVAL;
480 0 : goto fallback;
481 : }
482 :
483 0 : switch (cmd) {
484 : case SIOCGTRUNKPORT:
485 0 : if (rp->rp_portname[0] == '\0' ||
486 0 : ifunit(rp->rp_portname) != ifp) {
487 : error = EINVAL;
488 0 : break;
489 : }
490 :
491 : /* Search in all trunks if the global flag is set */
492 0 : if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
493 0 : NULL : tr, ifp)) == NULL) {
494 : error = ENOENT;
495 0 : break;
496 : }
497 :
498 0 : trunk_port2req(tp, rp);
499 0 : break;
500 : case SIOCSIFMTU:
501 : /* Do not allow the MTU to be changed once joined */
502 : error = EINVAL;
503 0 : break;
504 : default:
505 : error = ENOTTY;
506 0 : goto fallback;
507 : }
508 :
509 0 : return (error);
510 :
511 : fallback:
512 0 : if (tp != NULL)
513 0 : error = (*tp->tp_ioctl)(ifp, cmd, data);
514 :
515 0 : return (error);
516 0 : }
517 :
518 : int
519 0 : trunk_port_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
520 : struct rtentry *rt)
521 : {
522 : /* restrict transmission on trunk members to bpf only */
523 0 : if (ifp->if_type == IFT_IEEE8023ADLAG &&
524 0 : (m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL)) {
525 0 : m_freem(m);
526 0 : return (EBUSY);
527 : }
528 :
529 0 : return (ether_output(ifp, m, dst, rt));
530 0 : }
531 :
532 : void
533 0 : trunk_port_ifdetach(void *arg)
534 : {
535 0 : struct trunk_port *tp = (struct trunk_port *)arg;
536 :
537 0 : trunk_port_destroy(tp);
538 0 : }
539 :
540 : struct trunk_port *
541 0 : trunk_port_get(struct trunk_softc *tr, struct ifnet *ifp)
542 : {
543 : struct trunk_port *tp;
544 : struct trunk_softc *tr_ptr;
545 :
546 0 : if (tr != NULL) {
547 : /* Search port in specified trunk */
548 0 : SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
549 0 : if (tp->tp_if == ifp)
550 0 : return (tp);
551 : }
552 : } else {
553 : /* Search all trunks for the selected port */
554 0 : SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
555 0 : SLIST_FOREACH(tp, &tr_ptr->tr_ports, tp_entries) {
556 0 : if (tp->tp_if == ifp)
557 0 : return (tp);
558 : }
559 : }
560 : }
561 :
562 0 : return (NULL);
563 0 : }
564 :
565 : void
566 0 : trunk_port2req(struct trunk_port *tp, struct trunk_reqport *rp)
567 : {
568 0 : struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
569 :
570 0 : strlcpy(rp->rp_ifname, tr->tr_ifname, sizeof(rp->rp_ifname));
571 0 : strlcpy(rp->rp_portname, tp->tp_if->if_xname, sizeof(rp->rp_portname));
572 0 : rp->rp_prio = tp->tp_prio;
573 0 : if (tr->tr_portreq != NULL)
574 0 : (*tr->tr_portreq)(tp, (caddr_t)&rp->rp_psc);
575 :
576 : /* Add protocol specific flags */
577 0 : switch (tr->tr_proto) {
578 : case TRUNK_PROTO_FAILOVER:
579 0 : rp->rp_flags = tp->tp_flags;
580 0 : if (tp == trunk_link_active(tr, tr->tr_primary))
581 0 : rp->rp_flags |= TRUNK_PORT_ACTIVE;
582 : break;
583 :
584 : case TRUNK_PROTO_ROUNDROBIN:
585 : case TRUNK_PROTO_LOADBALANCE:
586 : case TRUNK_PROTO_BROADCAST:
587 0 : rp->rp_flags = tp->tp_flags;
588 0 : if (TRUNK_PORTACTIVE(tp))
589 0 : rp->rp_flags |= TRUNK_PORT_ACTIVE;
590 : break;
591 :
592 : case TRUNK_PROTO_LACP:
593 : /* LACP has a different definition of active */
594 0 : rp->rp_flags = lacp_port_status(tp);
595 0 : break;
596 : default:
597 : break;
598 : }
599 0 : }
600 :
601 : int
602 0 : trunk_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
603 : {
604 0 : struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
605 0 : struct trunk_reqall *ra = (struct trunk_reqall *)data;
606 0 : struct trunk_reqport *rp = (struct trunk_reqport *)data, rpbuf;
607 0 : struct trunk_opts *tro = (struct trunk_opts *)data;
608 0 : struct ifreq *ifr = (struct ifreq *)data;
609 : struct lacp_softc *lsc;
610 : struct trunk_port *tp;
611 : struct lacp_port *lp;
612 : struct ifnet *tpif;
613 : int i, error = 0;
614 :
615 0 : bzero(&rpbuf, sizeof(rpbuf));
616 :
617 0 : switch (cmd) {
618 : case SIOCGTRUNK:
619 0 : ra->ra_proto = tr->tr_proto;
620 0 : if (tr->tr_req != NULL)
621 0 : (*tr->tr_req)(tr, (caddr_t)&ra->ra_psc);
622 0 : ra->ra_ports = i = 0;
623 0 : tp = SLIST_FIRST(&tr->tr_ports);
624 0 : while (tp && ra->ra_size >=
625 0 : i + sizeof(struct trunk_reqport)) {
626 0 : trunk_port2req(tp, &rpbuf);
627 0 : error = copyout(&rpbuf, (caddr_t)ra->ra_port + i,
628 : sizeof(struct trunk_reqport));
629 0 : if (error)
630 : break;
631 0 : i += sizeof(struct trunk_reqport);
632 0 : ra->ra_ports++;
633 0 : tp = SLIST_NEXT(tp, tp_entries);
634 : }
635 : break;
636 : case SIOCSTRUNK:
637 0 : if ((error = suser(curproc)) != 0) {
638 : error = EPERM;
639 0 : break;
640 : }
641 0 : if (ra->ra_proto >= TRUNK_PROTO_MAX) {
642 : error = EPROTONOSUPPORT;
643 0 : break;
644 : }
645 : /*
646 : * Serialize modifications to the trunk and trunk
647 : * ports via the ifih SRP: detaching trunk_input
648 : * from the trunk port will require all currently
649 : * running trunk_input's on this port to finish
650 : * granting us an exclusive access to it.
651 : */
652 0 : SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
653 0 : if_ih_remove(tp->tp_if, trunk_input, tp);
654 0 : if (tr->tr_proto != TRUNK_PROTO_NONE)
655 0 : error = tr->tr_detach(tr);
656 0 : if (error != 0)
657 : break;
658 0 : for (i = 0; i < nitems(trunk_protos); i++) {
659 0 : if (trunk_protos[i].ti_proto == ra->ra_proto) {
660 0 : if (tr->tr_ifflags & IFF_DEBUG)
661 0 : printf("%s: using proto %u\n",
662 0 : tr->tr_ifname,
663 : trunk_protos[i].ti_proto);
664 0 : tr->tr_proto = trunk_protos[i].ti_proto;
665 0 : if (tr->tr_proto != TRUNK_PROTO_NONE)
666 0 : error = trunk_protos[i].ti_attach(tr);
667 0 : SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
668 0 : if_ih_insert(tp->tp_if,
669 0 : trunk_input, tp);
670 : /* Update trunk capabilities */
671 0 : tr->tr_capabilities = trunk_capabilities(tr);
672 0 : goto out;
673 : }
674 : }
675 : error = EPROTONOSUPPORT;
676 0 : break;
677 : case SIOCGTRUNKOPTS:
678 : /* Only LACP trunks have options atm */
679 0 : if (tro->to_proto != TRUNK_PROTO_LACP) {
680 : error = EPROTONOSUPPORT;
681 0 : break;
682 : }
683 0 : lsc = LACP_SOFTC(tr);
684 0 : tro->to_lacpopts.lacp_mode = lsc->lsc_mode;
685 0 : tro->to_lacpopts.lacp_timeout = lsc->lsc_timeout;
686 0 : tro->to_lacpopts.lacp_prio = lsc->lsc_sys_prio;
687 0 : tro->to_lacpopts.lacp_portprio = lsc->lsc_port_prio;
688 0 : tro->to_lacpopts.lacp_ifqprio = lsc->lsc_ifq_prio;
689 0 : break;
690 : case SIOCSTRUNKOPTS:
691 0 : if ((error = suser(curproc)) != 0) {
692 : error = EPERM;
693 0 : break;
694 : }
695 : /* Only LACP trunks have options atm */
696 0 : if (tro->to_proto != TRUNK_PROTO_LACP) {
697 : error = EPROTONOSUPPORT;
698 0 : break;
699 : }
700 0 : lsc = LACP_SOFTC(tr);
701 0 : switch(tro->to_opts) {
702 : case TRUNK_OPT_LACP_MODE:
703 : /*
704 : * Ensure mode changes occur immediately
705 : * on all ports
706 : */
707 0 : lsc->lsc_mode = tro->to_lacpopts.lacp_mode;
708 0 : if (lsc->lsc_mode == 0) {
709 0 : LIST_FOREACH(lp, &lsc->lsc_ports,
710 : lp_next)
711 0 : lp->lp_state &=
712 : ~LACP_STATE_ACTIVITY;
713 : } else {
714 0 : LIST_FOREACH(lp, &lsc->lsc_ports,
715 : lp_next)
716 0 : lp->lp_state |=
717 : LACP_STATE_ACTIVITY;
718 : }
719 : break;
720 : case TRUNK_OPT_LACP_TIMEOUT:
721 : /*
722 : * Ensure timeout changes occur immediately
723 : * on all ports
724 : */
725 0 : lsc->lsc_timeout =
726 0 : tro->to_lacpopts.lacp_timeout;
727 0 : if (lsc->lsc_timeout == 0) {
728 0 : LIST_FOREACH(lp, &lsc->lsc_ports,
729 : lp_next)
730 0 : lp->lp_state &=
731 : ~LACP_STATE_TIMEOUT;
732 : } else {
733 0 : LIST_FOREACH(lp, &lsc->lsc_ports,
734 : lp_next)
735 0 : lp->lp_state |=
736 : LACP_STATE_TIMEOUT;
737 : }
738 : break;
739 : case TRUNK_OPT_LACP_SYS_PRIO:
740 0 : if (tro->to_lacpopts.lacp_prio == 0) {
741 : error = EINVAL;
742 0 : break;
743 : }
744 0 : lsc->lsc_sys_prio = tro->to_lacpopts.lacp_prio;
745 0 : break;
746 : case TRUNK_OPT_LACP_PORT_PRIO:
747 0 : if (tro->to_lacpopts.lacp_portprio == 0) {
748 : error = EINVAL;
749 0 : break;
750 : }
751 0 : lsc->lsc_port_prio =
752 : tro->to_lacpopts.lacp_portprio;
753 0 : break;
754 : case TRUNK_OPT_LACP_IFQ_PRIO:
755 0 : if (tro->to_lacpopts.lacp_ifqprio >
756 : IFQ_MAXPRIO) {
757 : error = EINVAL;
758 0 : break;
759 : }
760 0 : lsc->lsc_ifq_prio =
761 : tro->to_lacpopts.lacp_ifqprio;
762 0 : break;
763 : }
764 : break;
765 : case SIOCGTRUNKPORT:
766 0 : if (rp->rp_portname[0] == '\0' ||
767 0 : (tpif = ifunit(rp->rp_portname)) == NULL) {
768 : error = EINVAL;
769 0 : break;
770 : }
771 :
772 : /* Search in all trunks if the global flag is set */
773 0 : if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
774 0 : NULL : tr, tpif)) == NULL) {
775 : error = ENOENT;
776 0 : break;
777 : }
778 :
779 0 : trunk_port2req(tp, rp);
780 0 : break;
781 : case SIOCSTRUNKPORT:
782 0 : if ((error = suser(curproc)) != 0) {
783 : error = EPERM;
784 0 : break;
785 : }
786 0 : if (rp->rp_portname[0] == '\0' ||
787 0 : (tpif = ifunit(rp->rp_portname)) == NULL) {
788 : error = EINVAL;
789 0 : break;
790 : }
791 0 : error = trunk_port_create(tr, tpif);
792 0 : break;
793 : case SIOCSTRUNKDELPORT:
794 0 : if ((error = suser(curproc)) != 0) {
795 : error = EPERM;
796 0 : break;
797 : }
798 0 : if (rp->rp_portname[0] == '\0' ||
799 0 : (tpif = ifunit(rp->rp_portname)) == NULL) {
800 : error = EINVAL;
801 0 : break;
802 : }
803 :
804 : /* Search in all trunks if the global flag is set */
805 0 : if ((tp = trunk_port_get(rp->rp_flags & TRUNK_PORT_GLOBAL ?
806 0 : NULL : tr, tpif)) == NULL) {
807 : error = ENOENT;
808 0 : break;
809 : }
810 :
811 0 : error = trunk_port_destroy(tp);
812 0 : break;
813 : case SIOCSIFADDR:
814 0 : ifp->if_flags |= IFF_UP;
815 : /* FALLTHROUGH */
816 : case SIOCSIFFLAGS:
817 : error = ENETRESET;
818 0 : break;
819 : case SIOCADDMULTI:
820 0 : error = trunk_ether_addmulti(tr, ifr);
821 0 : break;
822 : case SIOCDELMULTI:
823 0 : error = trunk_ether_delmulti(tr, ifr);
824 0 : break;
825 : case SIOCSIFMEDIA:
826 : case SIOCGIFMEDIA:
827 0 : error = ifmedia_ioctl(ifp, ifr, &tr->tr_media, cmd);
828 0 : break;
829 : case SIOCSIFLLADDR:
830 : /* Update the port lladdrs as well */
831 0 : SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
832 0 : trunk_port_lladdr(tp, ifr->ifr_addr.sa_data);
833 : error = ENETRESET;
834 0 : break;
835 : default:
836 0 : error = ether_ioctl(ifp, &tr->tr_ac, cmd, data);
837 0 : }
838 :
839 0 : if (error == ENETRESET) {
840 0 : if (ifp->if_flags & IFF_UP) {
841 0 : if ((ifp->if_flags & IFF_RUNNING) == 0)
842 0 : trunk_init(ifp);
843 : } else {
844 0 : if (ifp->if_flags & IFF_RUNNING)
845 0 : trunk_stop(ifp);
846 : }
847 : error = 0;
848 0 : }
849 :
850 : out:
851 0 : return (error);
852 0 : }
853 :
854 : int
855 0 : trunk_ether_addmulti(struct trunk_softc *tr, struct ifreq *ifr)
856 : {
857 : struct trunk_mc *mc;
858 0 : u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
859 : int error;
860 :
861 : /* Ignore ENETRESET error code */
862 0 : if ((error = ether_addmulti(ifr, &tr->tr_ac)) != ENETRESET)
863 0 : return (error);
864 :
865 0 : if ((mc = malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT)) == NULL) {
866 : error = ENOMEM;
867 : goto failed;
868 : }
869 :
870 0 : ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi);
871 0 : ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, mc->mc_enm);
872 0 : bcopy(&ifr->ifr_addr, &mc->mc_addr, ifr->ifr_addr.sa_len);
873 0 : SLIST_INSERT_HEAD(&tr->tr_mc_head, mc, mc_entries);
874 :
875 0 : if ((error = trunk_ioctl_allports(tr, SIOCADDMULTI,
876 0 : (caddr_t)ifr)) != 0) {
877 0 : trunk_ether_delmulti(tr, ifr);
878 0 : return (error);
879 : }
880 :
881 0 : return (error);
882 :
883 : failed:
884 0 : ether_delmulti(ifr, &tr->tr_ac);
885 :
886 0 : return (error);
887 0 : }
888 :
889 : int
890 0 : trunk_ether_delmulti(struct trunk_softc *tr, struct ifreq *ifr)
891 : {
892 : struct ether_multi *enm;
893 : struct trunk_mc *mc;
894 0 : u_int8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
895 : int error;
896 :
897 0 : if ((error = ether_multiaddr(&ifr->ifr_addr, addrlo, addrhi)) != 0)
898 0 : return (error);
899 0 : ETHER_LOOKUP_MULTI(addrlo, addrhi, &tr->tr_ac, enm);
900 0 : if (enm == NULL)
901 0 : return (EINVAL);
902 :
903 0 : SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries)
904 0 : if (mc->mc_enm == enm)
905 : break;
906 :
907 : /* We won't delete entries we didn't add */
908 0 : if (mc == NULL)
909 0 : return (EINVAL);
910 :
911 0 : if ((error = ether_delmulti(ifr, &tr->tr_ac)) != ENETRESET)
912 0 : return (error);
913 :
914 : /* We no longer use this multicast address. Tell parent so. */
915 0 : error = trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr);
916 0 : if (error == 0) {
917 0 : SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
918 0 : free(mc, M_DEVBUF, sizeof(*mc));
919 0 : } else {
920 : /* XXX At least one port failed to remove the address */
921 0 : if (tr->tr_ifflags & IFF_DEBUG) {
922 0 : printf("%s: failed to remove multicast address "
923 0 : "on all ports (%d)\n", tr->tr_ifname, error);
924 0 : }
925 0 : (void)ether_addmulti(ifr, &tr->tr_ac);
926 : }
927 :
928 0 : return (0);
929 0 : }
930 :
931 : void
932 0 : trunk_ether_purgemulti(struct trunk_softc *tr)
933 : {
934 : struct trunk_mc *mc;
935 0 : struct trunk_ifreq ifs;
936 0 : struct ifreq *ifr = &ifs.ifreq.ifreq;
937 :
938 0 : while ((mc = SLIST_FIRST(&tr->tr_mc_head)) != NULL) {
939 0 : bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
940 :
941 : /* Try to remove multicast address on all ports */
942 0 : trunk_ioctl_allports(tr, SIOCDELMULTI, (caddr_t)ifr);
943 :
944 0 : SLIST_REMOVE(&tr->tr_mc_head, mc, trunk_mc, mc_entries);
945 0 : free(mc, M_DEVBUF, sizeof(*mc));
946 : }
947 0 : }
948 :
949 : int
950 0 : trunk_ether_cmdmulti(struct trunk_port *tp, u_long cmd)
951 : {
952 0 : struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
953 : struct trunk_mc *mc;
954 0 : struct trunk_ifreq ifs;
955 0 : struct ifreq *ifr = &ifs.ifreq.ifreq;
956 : int ret, error = 0;
957 :
958 0 : bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
959 0 : SLIST_FOREACH(mc, &tr->tr_mc_head, mc_entries) {
960 0 : bcopy(&mc->mc_addr, &ifr->ifr_addr, mc->mc_addr.ss_len);
961 :
962 0 : if ((ret = tp->tp_ioctl(tp->tp_if, cmd, (caddr_t)ifr)) != 0) {
963 0 : if (tr->tr_ifflags & IFF_DEBUG) {
964 0 : printf("%s: ioctl %lu failed on %s: %d\n",
965 0 : tr->tr_ifname, cmd, tp->tp_ifname, ret);
966 0 : }
967 : /* Store last known error and continue */
968 : error = ret;
969 0 : }
970 : }
971 :
972 0 : return (error);
973 0 : }
974 :
975 : int
976 0 : trunk_ioctl_allports(struct trunk_softc *tr, u_long cmd, caddr_t data)
977 : {
978 0 : struct ifreq *ifr = (struct ifreq *)data;
979 : struct trunk_port *tp;
980 : int ret, error = 0;
981 :
982 0 : SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
983 0 : bcopy(tp->tp_ifname, ifr->ifr_name, IFNAMSIZ);
984 0 : if ((ret = tp->tp_ioctl(tp->tp_if, cmd, data)) != 0) {
985 0 : if (tr->tr_ifflags & IFF_DEBUG) {
986 0 : printf("%s: ioctl %lu failed on %s: %d\n",
987 0 : tr->tr_ifname, cmd, tp->tp_ifname, ret);
988 0 : }
989 : /* Store last known error and continue */
990 : error = ret;
991 0 : }
992 : }
993 :
994 0 : return (error);
995 : }
996 :
997 : void
998 0 : trunk_start(struct ifnet *ifp)
999 : {
1000 0 : struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
1001 : struct mbuf *m;
1002 : int error;
1003 :
1004 0 : for (;;) {
1005 0 : IFQ_DEQUEUE(&ifp->if_snd, m);
1006 0 : if (m == NULL)
1007 : break;
1008 :
1009 : #if NBPFILTER > 0
1010 0 : if (ifp->if_bpf)
1011 0 : bpf_mtap_ether(ifp->if_bpf, m, BPF_DIRECTION_OUT);
1012 : #endif
1013 :
1014 0 : if (tr->tr_proto != TRUNK_PROTO_NONE && tr->tr_count) {
1015 0 : error = (*tr->tr_start)(tr, m);
1016 0 : if (error != 0)
1017 0 : ifp->if_oerrors++;
1018 : } else {
1019 0 : m_freem(m);
1020 0 : if (tr->tr_proto != TRUNK_PROTO_NONE)
1021 0 : ifp->if_oerrors++;
1022 : }
1023 : }
1024 0 : }
1025 :
1026 : u_int32_t
1027 0 : trunk_hashmbuf(struct mbuf *m, SIPHASH_KEY *key)
1028 : {
1029 0 : u_int16_t etype, ether_vtag;
1030 : u_int32_t p = 0;
1031 0 : u_int16_t *vlan, vlanbuf[2];
1032 : int off;
1033 : struct ether_header *eh;
1034 0 : struct ip *ip, ipbuf;
1035 : #ifdef INET6
1036 0 : u_int32_t flow;
1037 0 : struct ip6_hdr *ip6, ip6buf;
1038 : #endif
1039 0 : SIPHASH_CTX ctx;
1040 :
1041 0 : if (m->m_pkthdr.ph_flowid & M_FLOWID_VALID)
1042 0 : return (m->m_pkthdr.ph_flowid & M_FLOWID_MASK);
1043 :
1044 0 : SipHash24_Init(&ctx, key);
1045 : off = sizeof(*eh);
1046 0 : if (m->m_len < off)
1047 : goto done;
1048 0 : eh = mtod(m, struct ether_header *);
1049 0 : etype = ntohs(eh->ether_type);
1050 0 : SipHash24_Update(&ctx, &eh->ether_shost, ETHER_ADDR_LEN);
1051 0 : SipHash24_Update(&ctx, &eh->ether_dhost, ETHER_ADDR_LEN);
1052 :
1053 : /* Special handling for encapsulating VLAN frames */
1054 0 : if (m->m_flags & M_VLANTAG) {
1055 0 : ether_vtag = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
1056 0 : SipHash24_Update(&ctx, ðer_vtag, sizeof(ether_vtag));
1057 0 : } else if (etype == ETHERTYPE_VLAN) {
1058 0 : if ((vlan = (u_int16_t *)
1059 0 : trunk_gethdr(m, off, EVL_ENCAPLEN, &vlanbuf)) == NULL)
1060 0 : return (p);
1061 0 : ether_vtag = EVL_VLANOFTAG(*vlan);
1062 0 : SipHash24_Update(&ctx, ðer_vtag, sizeof(ether_vtag));
1063 0 : etype = ntohs(vlan[1]);
1064 : off += EVL_ENCAPLEN;
1065 0 : }
1066 :
1067 0 : switch (etype) {
1068 : case ETHERTYPE_IP:
1069 0 : if ((ip = (struct ip *)
1070 0 : trunk_gethdr(m, off, sizeof(*ip), &ipbuf)) == NULL)
1071 0 : return (p);
1072 0 : SipHash24_Update(&ctx, &ip->ip_src, sizeof(struct in_addr));
1073 0 : SipHash24_Update(&ctx, &ip->ip_dst, sizeof(struct in_addr));
1074 0 : break;
1075 : #ifdef INET6
1076 : case ETHERTYPE_IPV6:
1077 0 : if ((ip6 = (struct ip6_hdr *)
1078 0 : trunk_gethdr(m, off, sizeof(*ip6), &ip6buf)) == NULL)
1079 0 : return (p);
1080 0 : SipHash24_Update(&ctx, &ip6->ip6_src, sizeof(struct in6_addr));
1081 0 : SipHash24_Update(&ctx, &ip6->ip6_dst, sizeof(struct in6_addr));
1082 0 : flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
1083 0 : SipHash24_Update(&ctx, &flow, sizeof(flow)); /* IPv6 flow label */
1084 0 : break;
1085 : #endif
1086 : }
1087 :
1088 : done:
1089 0 : return SipHash24_End(&ctx);
1090 0 : }
1091 :
1092 : void
1093 0 : trunk_init(struct ifnet *ifp)
1094 : {
1095 0 : struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
1096 :
1097 0 : ifp->if_flags |= IFF_RUNNING;
1098 :
1099 0 : if (tr->tr_init != NULL)
1100 0 : (*tr->tr_init)(tr);
1101 0 : }
1102 :
1103 : void
1104 0 : trunk_stop(struct ifnet *ifp)
1105 : {
1106 0 : struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
1107 :
1108 0 : ifp->if_flags &= ~IFF_RUNNING;
1109 :
1110 0 : if (tr->tr_stop != NULL)
1111 0 : (*tr->tr_stop)(tr);
1112 0 : }
1113 :
1114 : int
1115 0 : trunk_input(struct ifnet *ifp, struct mbuf *m, void *cookie)
1116 : {
1117 : struct trunk_softc *tr;
1118 : struct trunk_port *tp;
1119 : struct ifnet *trifp = NULL;
1120 : struct ether_header *eh;
1121 0 : struct mbuf_list ml = MBUF_LIST_INITIALIZER();
1122 :
1123 0 : eh = mtod(m, struct ether_header *);
1124 0 : if (ETHER_IS_MULTICAST(eh->ether_dhost))
1125 0 : ifp->if_imcasts++;
1126 :
1127 : /* Should be checked by the caller */
1128 0 : if (ifp->if_type != IFT_IEEE8023ADLAG)
1129 : goto bad;
1130 :
1131 0 : tp = (struct trunk_port *)cookie;
1132 0 : if ((tr = (struct trunk_softc *)tp->tp_trunk) == NULL)
1133 : goto bad;
1134 :
1135 0 : trifp = &tr->tr_ac.ac_if;
1136 0 : if (tr->tr_proto == TRUNK_PROTO_NONE)
1137 : goto bad;
1138 :
1139 0 : if ((*tr->tr_input)(tr, tp, m)) {
1140 : /*
1141 : * We stop here if the packet has been consumed
1142 : * by the protocol routine.
1143 : */
1144 0 : return (1);
1145 : }
1146 :
1147 0 : if ((trifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
1148 : goto bad;
1149 :
1150 : /*
1151 : * Drop promiscuously received packets if we are not in
1152 : * promiscuous mode.
1153 : */
1154 0 : if (!ETHER_IS_MULTICAST(eh->ether_dhost) &&
1155 0 : (ifp->if_flags & IFF_PROMISC) &&
1156 0 : (trifp->if_flags & IFF_PROMISC) == 0) {
1157 0 : if (bcmp(&tr->tr_ac.ac_enaddr, eh->ether_dhost,
1158 : ETHER_ADDR_LEN)) {
1159 0 : m_freem(m);
1160 0 : return (1);
1161 : }
1162 : }
1163 :
1164 :
1165 0 : ml_enqueue(&ml, m);
1166 0 : if_input(trifp, &ml);
1167 0 : return (1);
1168 :
1169 : bad:
1170 0 : if (trifp != NULL)
1171 0 : trifp->if_ierrors++;
1172 0 : m_freem(m);
1173 0 : return (1);
1174 0 : }
1175 :
1176 : int
1177 0 : trunk_media_change(struct ifnet *ifp)
1178 : {
1179 0 : struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
1180 :
1181 0 : if (tr->tr_ifflags & IFF_DEBUG)
1182 0 : printf("%s\n", __func__);
1183 :
1184 : /* Ignore */
1185 0 : return (0);
1186 : }
1187 :
1188 : void
1189 0 : trunk_media_status(struct ifnet *ifp, struct ifmediareq *imr)
1190 : {
1191 0 : struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
1192 : struct trunk_port *tp;
1193 :
1194 0 : imr->ifm_status = IFM_AVALID;
1195 0 : imr->ifm_active = IFM_ETHER | IFM_AUTO;
1196 :
1197 0 : SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
1198 0 : if (TRUNK_PORTACTIVE(tp))
1199 0 : imr->ifm_status |= IFM_ACTIVE;
1200 : }
1201 0 : }
1202 :
1203 : void
1204 0 : trunk_port_state(void *arg)
1205 : {
1206 0 : struct trunk_port *tp = (struct trunk_port *)arg;
1207 : struct trunk_softc *tr = NULL;
1208 :
1209 0 : if (tp != NULL)
1210 0 : tr = (struct trunk_softc *)tp->tp_trunk;
1211 0 : if (tr == NULL)
1212 0 : return;
1213 0 : if (tr->tr_linkstate != NULL)
1214 0 : (*tr->tr_linkstate)(tp);
1215 0 : trunk_link_active(tr, tp);
1216 0 : }
1217 :
1218 : struct trunk_port *
1219 0 : trunk_link_active(struct trunk_softc *tr, struct trunk_port *tp)
1220 : {
1221 : struct trunk_port *tp_next, *rval = NULL;
1222 : int new_link = LINK_STATE_DOWN;
1223 :
1224 : /*
1225 : * Search a port which reports an active link state.
1226 : */
1227 :
1228 0 : if (tp == NULL)
1229 : goto search;
1230 0 : if (TRUNK_PORTACTIVE(tp)) {
1231 : rval = tp;
1232 0 : goto found;
1233 : }
1234 0 : if ((tp_next = SLIST_NEXT(tp, tp_entries)) != NULL &&
1235 0 : TRUNK_PORTACTIVE(tp_next)) {
1236 : rval = tp_next;
1237 0 : goto found;
1238 : }
1239 :
1240 : search:
1241 0 : SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
1242 0 : if (TRUNK_PORTACTIVE(tp_next)) {
1243 : rval = tp_next;
1244 0 : goto found;
1245 : }
1246 : }
1247 :
1248 : found:
1249 0 : if (rval != NULL) {
1250 : /*
1251 : * The IEEE 802.1D standard assumes that a trunk with
1252 : * multiple ports is always full duplex. This is valid
1253 : * for load sharing trunks and if at least two links
1254 : * are active. Unfortunately, checking the latter would
1255 : * be too expensive at this point.
1256 : */
1257 0 : if ((tr->tr_capabilities & IFCAP_TRUNK_FULLDUPLEX) &&
1258 0 : (tr->tr_count > 1))
1259 0 : new_link = LINK_STATE_FULL_DUPLEX;
1260 : else
1261 0 : new_link = rval->tp_link_state;
1262 : }
1263 :
1264 0 : if (tr->tr_ac.ac_if.if_link_state != new_link) {
1265 0 : tr->tr_ac.ac_if.if_link_state = new_link;
1266 0 : if_link_state_change(&tr->tr_ac.ac_if);
1267 0 : }
1268 :
1269 0 : return (rval);
1270 : }
1271 :
1272 : const void *
1273 0 : trunk_gethdr(struct mbuf *m, u_int off, u_int len, void *buf)
1274 : {
1275 0 : if (m->m_pkthdr.len < (off + len))
1276 0 : return (NULL);
1277 0 : else if (m->m_len < (off + len)) {
1278 0 : m_copydata(m, off, len, buf);
1279 0 : return (buf);
1280 : }
1281 0 : return (mtod(m, caddr_t) + off);
1282 0 : }
1283 :
1284 : /*
1285 : * Simple round robin trunking
1286 : */
1287 :
1288 : int
1289 0 : trunk_rr_attach(struct trunk_softc *tr)
1290 : {
1291 : struct trunk_port *tp;
1292 :
1293 0 : tr->tr_detach = trunk_rr_detach;
1294 0 : tr->tr_start = trunk_rr_start;
1295 0 : tr->tr_input = trunk_rr_input;
1296 0 : tr->tr_init = NULL;
1297 0 : tr->tr_stop = NULL;
1298 0 : tr->tr_linkstate = NULL;
1299 0 : tr->tr_port_create = NULL;
1300 0 : tr->tr_port_destroy = trunk_rr_port_destroy;
1301 0 : tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX;
1302 0 : tr->tr_req = NULL;
1303 0 : tr->tr_portreq = NULL;
1304 :
1305 0 : tp = SLIST_FIRST(&tr->tr_ports);
1306 0 : tr->tr_psc = (caddr_t)tp;
1307 :
1308 0 : return (0);
1309 : }
1310 :
1311 : int
1312 0 : trunk_rr_detach(struct trunk_softc *tr)
1313 : {
1314 0 : tr->tr_psc = NULL;
1315 0 : return (0);
1316 : }
1317 :
1318 : void
1319 0 : trunk_rr_port_destroy(struct trunk_port *tp)
1320 : {
1321 0 : struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
1322 :
1323 0 : if (tp == (struct trunk_port *)tr->tr_psc)
1324 0 : tr->tr_psc = NULL;
1325 0 : }
1326 :
1327 : int
1328 0 : trunk_rr_start(struct trunk_softc *tr, struct mbuf *m)
1329 : {
1330 0 : struct trunk_port *tp = (struct trunk_port *)tr->tr_psc, *tp_next;
1331 : int error = 0;
1332 :
1333 0 : if (tp == NULL && (tp = trunk_link_active(tr, NULL)) == NULL) {
1334 0 : m_freem(m);
1335 0 : return (ENOENT);
1336 : }
1337 :
1338 0 : if ((error = if_enqueue(tp->tp_if, m)) != 0)
1339 0 : return (error);
1340 :
1341 : /* Get next active port */
1342 0 : tp_next = trunk_link_active(tr, SLIST_NEXT(tp, tp_entries));
1343 0 : tr->tr_psc = (caddr_t)tp_next;
1344 :
1345 0 : return (0);
1346 0 : }
1347 :
1348 : int
1349 0 : trunk_rr_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
1350 : {
1351 : /* Just pass in the packet to our trunk device */
1352 0 : return (0);
1353 : }
1354 :
1355 : /*
1356 : * Active failover
1357 : */
1358 :
1359 : int
1360 0 : trunk_fail_attach(struct trunk_softc *tr)
1361 : {
1362 0 : tr->tr_detach = trunk_fail_detach;
1363 0 : tr->tr_start = trunk_fail_start;
1364 0 : tr->tr_input = trunk_fail_input;
1365 0 : tr->tr_init = NULL;
1366 0 : tr->tr_stop = NULL;
1367 0 : tr->tr_port_create = trunk_fail_port_create;
1368 0 : tr->tr_port_destroy = trunk_fail_port_destroy;
1369 0 : tr->tr_linkstate = trunk_fail_linkstate;
1370 0 : tr->tr_req = NULL;
1371 0 : tr->tr_portreq = NULL;
1372 :
1373 : /* Get primary or the next active port */
1374 0 : tr->tr_psc = (caddr_t)trunk_link_active(tr, tr->tr_primary);
1375 :
1376 0 : return (0);
1377 : }
1378 :
1379 : int
1380 0 : trunk_fail_detach(struct trunk_softc *tr)
1381 : {
1382 0 : tr->tr_psc = NULL;
1383 0 : return (0);
1384 : }
1385 :
1386 : int
1387 0 : trunk_fail_port_create(struct trunk_port *tp)
1388 : {
1389 0 : struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
1390 :
1391 : /* Get primary or the next active port */
1392 0 : tr->tr_psc = (caddr_t)trunk_link_active(tr, tr->tr_primary);
1393 0 : return (0);
1394 : }
1395 :
1396 : void
1397 0 : trunk_fail_port_destroy(struct trunk_port *tp)
1398 : {
1399 0 : struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
1400 : struct trunk_port *tp_next;
1401 :
1402 0 : if ((caddr_t)tp == tr->tr_psc) {
1403 : /* Get the next active port */
1404 0 : tp_next = trunk_link_active(tr, SLIST_NEXT(tp, tp_entries));
1405 0 : if (tp_next == tp)
1406 0 : tr->tr_psc = NULL;
1407 : else
1408 0 : tr->tr_psc = (caddr_t)tp_next;
1409 : } else {
1410 : /* Get primary or the next active port */
1411 0 : tr->tr_psc = (caddr_t)trunk_link_active(tr, tr->tr_primary);
1412 : }
1413 0 : }
1414 :
1415 : int
1416 0 : trunk_fail_start(struct trunk_softc *tr, struct mbuf *m)
1417 : {
1418 0 : struct trunk_port *tp = (struct trunk_port *)tr->tr_psc;
1419 :
1420 : /* Use the master port if active or the next available port */
1421 0 : if (tp == NULL) {
1422 0 : m_freem(m);
1423 0 : return (ENOENT);
1424 : }
1425 :
1426 0 : return (if_enqueue(tp->tp_if, m));
1427 0 : }
1428 :
1429 : int
1430 0 : trunk_fail_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
1431 : {
1432 0 : if ((caddr_t)tp == tr->tr_psc)
1433 0 : return (0);
1434 0 : m_freem(m);
1435 0 : return (-1);
1436 0 : }
1437 :
1438 : void
1439 0 : trunk_fail_linkstate(struct trunk_port *tp)
1440 : {
1441 0 : struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
1442 :
1443 0 : tr->tr_psc = (caddr_t)trunk_link_active(tr, tr->tr_primary);
1444 0 : }
1445 :
1446 : /*
1447 : * Loadbalancing
1448 : */
1449 :
1450 : int
1451 0 : trunk_lb_attach(struct trunk_softc *tr)
1452 : {
1453 : struct trunk_lb *lb;
1454 :
1455 0 : if ((lb = malloc(sizeof(*lb), M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
1456 0 : return (ENOMEM);
1457 :
1458 0 : tr->tr_detach = trunk_lb_detach;
1459 0 : tr->tr_start = trunk_lb_start;
1460 0 : tr->tr_input = trunk_lb_input;
1461 0 : tr->tr_port_create = trunk_lb_port_create;
1462 0 : tr->tr_port_destroy = trunk_lb_port_destroy;
1463 0 : tr->tr_linkstate = NULL;
1464 0 : tr->tr_capabilities = IFCAP_TRUNK_FULLDUPLEX;
1465 0 : tr->tr_req = NULL;
1466 0 : tr->tr_portreq = NULL;
1467 0 : tr->tr_init = NULL;
1468 0 : tr->tr_stop = NULL;
1469 :
1470 0 : arc4random_buf(&lb->lb_key, sizeof(lb->lb_key));
1471 0 : tr->tr_psc = (caddr_t)lb;
1472 :
1473 0 : return (0);
1474 0 : }
1475 :
1476 : int
1477 0 : trunk_lb_detach(struct trunk_softc *tr)
1478 : {
1479 0 : struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
1480 :
1481 0 : free(lb, M_DEVBUF, sizeof *lb);
1482 0 : return (0);
1483 : }
1484 :
1485 : int
1486 0 : trunk_lb_porttable(struct trunk_softc *tr, struct trunk_port *tp)
1487 : {
1488 0 : struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
1489 : struct trunk_port *tp_next;
1490 : int i = 0;
1491 :
1492 0 : bzero(&lb->lb_ports, sizeof(lb->lb_ports));
1493 0 : SLIST_FOREACH(tp_next, &tr->tr_ports, tp_entries) {
1494 0 : if (tp_next == tp)
1495 : continue;
1496 0 : if (i >= TRUNK_MAX_PORTS)
1497 0 : return (EINVAL);
1498 0 : if (tr->tr_ifflags & IFF_DEBUG)
1499 0 : printf("%s: port %s at index %d\n",
1500 0 : tr->tr_ifname, tp_next->tp_ifname, i);
1501 0 : lb->lb_ports[i++] = tp_next;
1502 0 : }
1503 :
1504 0 : return (0);
1505 0 : }
1506 :
1507 : int
1508 0 : trunk_lb_port_create(struct trunk_port *tp)
1509 : {
1510 0 : struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
1511 0 : return (trunk_lb_porttable(tr, NULL));
1512 : }
1513 :
1514 : void
1515 0 : trunk_lb_port_destroy(struct trunk_port *tp)
1516 : {
1517 0 : struct trunk_softc *tr = (struct trunk_softc *)tp->tp_trunk;
1518 0 : trunk_lb_porttable(tr, tp);
1519 0 : }
1520 :
1521 : int
1522 0 : trunk_lb_start(struct trunk_softc *tr, struct mbuf *m)
1523 : {
1524 0 : struct trunk_lb *lb = (struct trunk_lb *)tr->tr_psc;
1525 : struct trunk_port *tp = NULL;
1526 : u_int32_t p = 0;
1527 :
1528 0 : p = trunk_hashmbuf(m, &lb->lb_key);
1529 0 : p %= tr->tr_count;
1530 0 : tp = lb->lb_ports[p];
1531 :
1532 : /*
1533 : * Check the port's link state. This will return the next active
1534 : * port if the link is down or the port is NULL.
1535 : */
1536 0 : if ((tp = trunk_link_active(tr, tp)) == NULL) {
1537 0 : m_freem(m);
1538 0 : return (ENOENT);
1539 : }
1540 :
1541 0 : return (if_enqueue(tp->tp_if, m));
1542 0 : }
1543 :
1544 : int
1545 0 : trunk_lb_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
1546 : {
1547 : /* Just pass in the packet to our trunk device */
1548 0 : return (0);
1549 : }
1550 :
1551 : /*
1552 : * Broadcast mode
1553 : */
1554 :
1555 : int
1556 0 : trunk_bcast_attach(struct trunk_softc *tr)
1557 : {
1558 0 : tr->tr_detach = trunk_bcast_detach;
1559 0 : tr->tr_start = trunk_bcast_start;
1560 0 : tr->tr_input = trunk_bcast_input;
1561 0 : tr->tr_init = NULL;
1562 0 : tr->tr_stop = NULL;
1563 0 : tr->tr_port_create = NULL;
1564 0 : tr->tr_port_destroy = NULL;
1565 0 : tr->tr_linkstate = NULL;
1566 0 : tr->tr_req = NULL;
1567 0 : tr->tr_portreq = NULL;
1568 :
1569 0 : return (0);
1570 : }
1571 :
1572 : int
1573 0 : trunk_bcast_detach(struct trunk_softc *tr)
1574 : {
1575 0 : return (0);
1576 : }
1577 :
1578 : int
1579 0 : trunk_bcast_start(struct trunk_softc *tr, struct mbuf *m0)
1580 : {
1581 : int active_ports = 0;
1582 : int errors = 0;
1583 : struct trunk_port *tp, *last = NULL;
1584 : struct mbuf *m;
1585 :
1586 0 : SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
1587 0 : if (!TRUNK_PORTACTIVE(tp))
1588 : continue;
1589 :
1590 0 : active_ports++;
1591 :
1592 0 : if (last != NULL) {
1593 0 : m = m_copym(m0, 0, M_COPYALL, M_DONTWAIT);
1594 0 : if (m == NULL) {
1595 0 : errors++;
1596 0 : break;
1597 : }
1598 :
1599 0 : if (if_enqueue(last->tp_if, m) != 0)
1600 0 : errors++;
1601 : }
1602 : last = tp;
1603 0 : }
1604 0 : if (last == NULL) {
1605 0 : m_freem(m0);
1606 0 : return (ENOENT);
1607 : }
1608 :
1609 0 : if (if_enqueue(last->tp_if, m0) != 0)
1610 0 : errors++;
1611 :
1612 0 : if (errors == active_ports)
1613 0 : return (ENOBUFS);
1614 :
1615 0 : return (0);
1616 0 : }
1617 :
1618 : int
1619 0 : trunk_bcast_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
1620 : {
1621 0 : return (0);
1622 : }
1623 :
1624 : /*
1625 : * 802.3ad LACP
1626 : */
1627 :
1628 : int
1629 0 : trunk_lacp_attach(struct trunk_softc *tr)
1630 : {
1631 : struct trunk_port *tp;
1632 : int error;
1633 :
1634 0 : tr->tr_detach = trunk_lacp_detach;
1635 0 : tr->tr_port_create = lacp_port_create;
1636 0 : tr->tr_port_destroy = lacp_port_destroy;
1637 0 : tr->tr_linkstate = lacp_linkstate;
1638 0 : tr->tr_start = trunk_lacp_start;
1639 0 : tr->tr_input = trunk_lacp_input;
1640 0 : tr->tr_init = lacp_init;
1641 0 : tr->tr_stop = lacp_stop;
1642 0 : tr->tr_req = lacp_req;
1643 0 : tr->tr_portreq = lacp_portreq;
1644 :
1645 0 : error = lacp_attach(tr);
1646 0 : if (error)
1647 0 : return (error);
1648 :
1649 0 : SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
1650 0 : lacp_port_create(tp);
1651 :
1652 0 : return (error);
1653 0 : }
1654 :
1655 : int
1656 0 : trunk_lacp_detach(struct trunk_softc *tr)
1657 : {
1658 : struct trunk_port *tp;
1659 : int error;
1660 :
1661 0 : SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
1662 0 : lacp_port_destroy(tp);
1663 :
1664 : /* unlocking is safe here */
1665 0 : error = lacp_detach(tr);
1666 :
1667 0 : return (error);
1668 : }
1669 :
1670 : int
1671 0 : trunk_lacp_start(struct trunk_softc *tr, struct mbuf *m)
1672 : {
1673 : struct trunk_port *tp;
1674 :
1675 0 : tp = lacp_select_tx_port(tr, m);
1676 0 : if (tp == NULL) {
1677 0 : m_freem(m);
1678 0 : return (EBUSY);
1679 : }
1680 :
1681 0 : return (if_enqueue(tp->tp_if, m));
1682 0 : }
1683 :
1684 : int
1685 0 : trunk_lacp_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
1686 : {
1687 0 : return (lacp_input(tp, m));
1688 : }
|