1*481d3881Srin /* $NetBSD: if_lagg.c,v 1.72 2024/07/05 04:31:53 rin Exp $ */
215e65644Syamaguchi
315e65644Syamaguchi /*
415e65644Syamaguchi * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
515e65644Syamaguchi * Copyright (c) 2007 Andrew Thompson <thompsa@FreeBSD.org>
615e65644Syamaguchi * Copyright (c) 2014, 2016 Marcelo Araujo <araujo@FreeBSD.org>
715e65644Syamaguchi * Copyright (c) 2021, Internet Initiative Japan Inc.
815e65644Syamaguchi *
915e65644Syamaguchi * Permission to use, copy, modify, and distribute this software for any
1015e65644Syamaguchi * purpose with or without fee is hereby granted, provided that the above
1115e65644Syamaguchi * copyright notice and this permission notice appear in all copies.
1215e65644Syamaguchi *
1315e65644Syamaguchi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1415e65644Syamaguchi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1515e65644Syamaguchi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1615e65644Syamaguchi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1715e65644Syamaguchi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1815e65644Syamaguchi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1915e65644Syamaguchi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2015e65644Syamaguchi */
21f0101d0eSyamaguchi
22026b9470Sthorpej #include <sys/cdefs.h>
23*481d3881Srin __KERNEL_RCSID(0, "$NetBSD: if_lagg.c,v 1.72 2024/07/05 04:31:53 rin Exp $");
24026b9470Sthorpej
25f0101d0eSyamaguchi #ifdef _KERNEL_OPT
26f0101d0eSyamaguchi #include "opt_inet.h"
27f0101d0eSyamaguchi #include "opt_lagg.h"
28f0101d0eSyamaguchi #endif
29f0101d0eSyamaguchi
30f0101d0eSyamaguchi #include <sys/param.h>
31f0101d0eSyamaguchi #include <sys/types.h>
32f0101d0eSyamaguchi
33f0101d0eSyamaguchi #include <sys/cprng.h>
341875049cSyamaguchi #include <sys/cpu.h>
35f0101d0eSyamaguchi #include <sys/device.h>
36f0101d0eSyamaguchi #include <sys/evcnt.h>
37f0101d0eSyamaguchi #include <sys/hash.h>
38f0101d0eSyamaguchi #include <sys/kmem.h>
39f0101d0eSyamaguchi #include <sys/module.h>
40f0101d0eSyamaguchi #include <sys/pserialize.h>
41f0101d0eSyamaguchi #include <sys/pslist.h>
42f0101d0eSyamaguchi #include <sys/psref.h>
43f0101d0eSyamaguchi #include <sys/sysctl.h>
44f0101d0eSyamaguchi #include <sys/syslog.h>
45f0101d0eSyamaguchi #include <sys/workqueue.h>
46f0101d0eSyamaguchi
47f0101d0eSyamaguchi #include <net/bpf.h>
48f0101d0eSyamaguchi #include <net/if.h>
49f0101d0eSyamaguchi #include <net/if_dl.h>
50f0101d0eSyamaguchi #include <net/if_ether.h>
51f0101d0eSyamaguchi #include <net/if_media.h>
52f0101d0eSyamaguchi #include <net/if_types.h>
53f0101d0eSyamaguchi #include <net/if_vlanvar.h>
54f0101d0eSyamaguchi #include <netinet/ip.h>
55f0101d0eSyamaguchi #include <netinet/ip6.h>
56f0101d0eSyamaguchi #include <netinet/tcp.h>
57f0101d0eSyamaguchi #include <netinet/udp.h>
58f0101d0eSyamaguchi
59f0101d0eSyamaguchi #if defined(INET) || defined(INET6)
60f0101d0eSyamaguchi #include <netinet/in.h>
61f0101d0eSyamaguchi #endif
62f0101d0eSyamaguchi
63f0101d0eSyamaguchi #ifdef INET6
64f0101d0eSyamaguchi #include <netinet6/in6_ifattach.h>
65f0101d0eSyamaguchi #include <netinet6/in6_var.h>
66f0101d0eSyamaguchi #endif
67f0101d0eSyamaguchi
68f0101d0eSyamaguchi #include <net/lagg/if_lagg.h>
69f0101d0eSyamaguchi #include <net/lagg/if_laggproto.h>
70f0101d0eSyamaguchi
71f0101d0eSyamaguchi #include "ioconf.h"
72f0101d0eSyamaguchi
73f0101d0eSyamaguchi enum lagg_portctrl {
74f0101d0eSyamaguchi LAGG_PORTCTRL_ALLOC,
75f0101d0eSyamaguchi LAGG_PORTCTRL_FREE,
76f0101d0eSyamaguchi LAGG_PORTCTRL_START,
77f0101d0eSyamaguchi LAGG_PORTCTRL_STOP
78f0101d0eSyamaguchi };
79f0101d0eSyamaguchi
80f0101d0eSyamaguchi enum lagg_iftypes {
81f0101d0eSyamaguchi LAGG_IF_TYPE_ETHERNET,
82f0101d0eSyamaguchi };
83f0101d0eSyamaguchi
84f0101d0eSyamaguchi static const struct lagg_proto lagg_protos[] = {
85f0101d0eSyamaguchi [LAGG_PROTO_NONE] = {
86f0101d0eSyamaguchi .pr_num = LAGG_PROTO_NONE,
87f0101d0eSyamaguchi .pr_attach = lagg_none_attach,
88f0101d0eSyamaguchi },
89f0101d0eSyamaguchi [LAGG_PROTO_LACP] = {
90f0101d0eSyamaguchi .pr_num = LAGG_PROTO_LACP,
91f0101d0eSyamaguchi .pr_attach = lacp_attach,
92f0101d0eSyamaguchi .pr_detach = lacp_detach,
93f0101d0eSyamaguchi .pr_up = lacp_up,
94f0101d0eSyamaguchi .pr_down = lacp_down,
95f0101d0eSyamaguchi .pr_transmit = lacp_transmit,
96f0101d0eSyamaguchi .pr_input = lacp_input,
97f0101d0eSyamaguchi .pr_allocport = lacp_allocport,
98f0101d0eSyamaguchi .pr_freeport = lacp_freeport,
99f0101d0eSyamaguchi .pr_startport = lacp_startport,
100f0101d0eSyamaguchi .pr_stopport = lacp_stopport,
101f0101d0eSyamaguchi .pr_protostat = lacp_protostat,
102f0101d0eSyamaguchi .pr_portstat = lacp_portstat,
1031efd0671Syamaguchi .pr_linkstate = lacp_linkstate_ifnet_locked,
104f0101d0eSyamaguchi .pr_ioctl = lacp_ioctl,
105f0101d0eSyamaguchi },
106f0101d0eSyamaguchi [LAGG_PROTO_FAILOVER] = {
107f0101d0eSyamaguchi .pr_num = LAGG_PROTO_FAILOVER,
108f0101d0eSyamaguchi .pr_attach = lagg_fail_attach,
109f0101d0eSyamaguchi .pr_detach = lagg_common_detach,
110f0101d0eSyamaguchi .pr_transmit = lagg_fail_transmit,
111f0101d0eSyamaguchi .pr_input = lagg_fail_input,
112f0101d0eSyamaguchi .pr_allocport = lagg_common_allocport,
113f0101d0eSyamaguchi .pr_freeport = lagg_common_freeport,
114f0101d0eSyamaguchi .pr_startport = lagg_common_startport,
115f0101d0eSyamaguchi .pr_stopport = lagg_common_stopport,
116f0101d0eSyamaguchi .pr_portstat = lagg_fail_portstat,
117d3cb38e4Syamaguchi .pr_linkstate = lagg_common_linkstate_ifnet_locked,
118f0101d0eSyamaguchi .pr_ioctl = lagg_fail_ioctl,
119f0101d0eSyamaguchi },
120f0101d0eSyamaguchi [LAGG_PROTO_LOADBALANCE] = {
121f0101d0eSyamaguchi .pr_num = LAGG_PROTO_LOADBALANCE,
122f0101d0eSyamaguchi .pr_attach = lagg_lb_attach,
123f0101d0eSyamaguchi .pr_detach = lagg_common_detach,
124f0101d0eSyamaguchi .pr_transmit = lagg_lb_transmit,
125f0101d0eSyamaguchi .pr_input = lagg_lb_input,
126f0101d0eSyamaguchi .pr_allocport = lagg_common_allocport,
127f0101d0eSyamaguchi .pr_freeport = lagg_common_freeport,
128f0101d0eSyamaguchi .pr_startport = lagg_lb_startport,
129f0101d0eSyamaguchi .pr_stopport = lagg_lb_stopport,
130f0101d0eSyamaguchi .pr_portstat = lagg_lb_portstat,
131d3cb38e4Syamaguchi .pr_linkstate = lagg_common_linkstate_ifnet_locked,
132f0101d0eSyamaguchi },
133f0101d0eSyamaguchi };
134f0101d0eSyamaguchi
1355cfb8022Syamaguchi static int lagg_chg_sadl(struct ifnet *, const uint8_t *, size_t);
136d1fb1196Syamaguchi static void lagg_input_ethernet(struct ifnet *, struct mbuf *);
137f0101d0eSyamaguchi static int lagg_clone_create(struct if_clone *, int);
138f0101d0eSyamaguchi static int lagg_clone_destroy(struct ifnet *);
139f0101d0eSyamaguchi static int lagg_init(struct ifnet *);
140f0101d0eSyamaguchi static int lagg_init_locked(struct lagg_softc *);
141f0101d0eSyamaguchi static void lagg_stop(struct ifnet *, int);
142f0101d0eSyamaguchi static void lagg_stop_locked(struct lagg_softc *);
143f0101d0eSyamaguchi static int lagg_ioctl(struct ifnet *, u_long, void *);
144f0101d0eSyamaguchi static int lagg_transmit(struct ifnet *, struct mbuf *);
145f0101d0eSyamaguchi static void lagg_start(struct ifnet *);
146f0101d0eSyamaguchi static int lagg_media_change(struct ifnet *);
147f0101d0eSyamaguchi static void lagg_media_status(struct ifnet *, struct ifmediareq *);
148f0101d0eSyamaguchi static int lagg_vlan_cb(struct ethercom *, uint16_t, bool);
1494a93a140Syamaguchi static void lagg_linkstate_changed(void *);
1501f3caf1eSyamaguchi static void lagg_ifdetach(void *);
151f0101d0eSyamaguchi static struct lagg_softc *
152f0101d0eSyamaguchi lagg_softc_alloc(enum lagg_iftypes);
153f0101d0eSyamaguchi static void lagg_softc_free(struct lagg_softc *);
154f0101d0eSyamaguchi static int lagg_setup_sysctls(struct lagg_softc *);
155f0101d0eSyamaguchi static void lagg_teardown_sysctls(struct lagg_softc *);
156f0101d0eSyamaguchi static int lagg_proto_attach(struct lagg_softc *, lagg_proto,
157f0101d0eSyamaguchi struct lagg_proto_softc **);
158f0101d0eSyamaguchi static void lagg_proto_detach(struct lagg_variant *);
159f0101d0eSyamaguchi static int lagg_proto_up(struct lagg_softc *);
160f0101d0eSyamaguchi static void lagg_proto_down(struct lagg_softc *);
161f0101d0eSyamaguchi static int lagg_proto_allocport(struct lagg_softc *, struct lagg_port *);
162f0101d0eSyamaguchi static void lagg_proto_freeport(struct lagg_softc *, struct lagg_port *);
163f0101d0eSyamaguchi static void lagg_proto_startport(struct lagg_softc *,
164f0101d0eSyamaguchi struct lagg_port *);
165f0101d0eSyamaguchi static void lagg_proto_stopport(struct lagg_softc *,
166f0101d0eSyamaguchi struct lagg_port *);
167f0101d0eSyamaguchi static struct mbuf *
168f0101d0eSyamaguchi lagg_proto_input(struct lagg_softc *, struct lagg_port *,
169f0101d0eSyamaguchi struct mbuf *);
170f0101d0eSyamaguchi static void lagg_proto_linkstate(struct lagg_softc *, struct lagg_port *);
171f0101d0eSyamaguchi static int lagg_proto_ioctl(struct lagg_softc *, struct lagg_req *);
172f0101d0eSyamaguchi static int lagg_get_stats(struct lagg_softc *, struct lagg_req *, size_t);
173f0101d0eSyamaguchi static int lagg_pr_attach(struct lagg_softc *, lagg_proto);
174f0101d0eSyamaguchi static void lagg_pr_detach(struct lagg_softc *);
175f0101d0eSyamaguchi static int lagg_addport(struct lagg_softc *, struct ifnet *);
176f0101d0eSyamaguchi static int lagg_delport(struct lagg_softc *, struct ifnet *);
1771f3caf1eSyamaguchi static int lagg_delport_all(struct lagg_softc *);
178f0101d0eSyamaguchi static int lagg_port_ioctl(struct ifnet *, u_long, void *);
179f0101d0eSyamaguchi static int lagg_port_output(struct ifnet *, struct mbuf *,
180f0101d0eSyamaguchi const struct sockaddr *, const struct rtentry *);
181714bab40Syamaguchi static void lagg_config_promisc(struct lagg_softc *, struct lagg_port *);
182edab87ecSyamaguchi static void lagg_unconfig_promisc(struct lagg_softc *, struct lagg_port *);
183f0101d0eSyamaguchi static struct lagg_variant *
184f0101d0eSyamaguchi lagg_variant_getref(struct lagg_softc *, struct psref *);
185f0101d0eSyamaguchi static void lagg_variant_putref(struct lagg_variant *, struct psref *);
186f0101d0eSyamaguchi static int lagg_ether_addmulti(struct lagg_softc *, struct ifreq *);
187f0101d0eSyamaguchi static int lagg_ether_delmulti(struct lagg_softc *, struct ifreq *);
188f0101d0eSyamaguchi static void lagg_port_syncmulti(struct lagg_softc *, struct lagg_port *);
189f0101d0eSyamaguchi static void lagg_port_purgemulti(struct lagg_softc *, struct lagg_port *);
19074194586Syamaguchi static int lagg_port_setup(struct lagg_softc *, struct lagg_port *,
19174194586Syamaguchi struct ifnet *);
19274194586Syamaguchi static void lagg_port_teardown(struct lagg_softc *, struct lagg_port *,
19374194586Syamaguchi bool);
194f0101d0eSyamaguchi static void lagg_port_syncvlan(struct lagg_softc *, struct lagg_port *);
195f0101d0eSyamaguchi static void lagg_port_purgevlan(struct lagg_softc *, struct lagg_port *);
19680bf8caaSyamaguchi static void lagg_capabilities_update(struct lagg_softc *);
197bcfe44c5Syamaguchi static void lagg_sync_ifcaps(struct lagg_softc *);
198bcfe44c5Syamaguchi static void lagg_sync_ethcaps(struct lagg_softc *);
1995cfb8022Syamaguchi static void lagg_sync_sadl(struct lagg_softc *);
200f0101d0eSyamaguchi
201f0101d0eSyamaguchi static struct if_clone lagg_cloner =
202f0101d0eSyamaguchi IF_CLONE_INITIALIZER("lagg", lagg_clone_create, lagg_clone_destroy);
203f0101d0eSyamaguchi static unsigned int lagg_count;
204f0101d0eSyamaguchi static struct psref_class
205f0101d0eSyamaguchi *lagg_psref_class __read_mostly;
206f0101d0eSyamaguchi static struct psref_class
207f0101d0eSyamaguchi *lagg_port_psref_class __read_mostly;
208f0101d0eSyamaguchi
209f0101d0eSyamaguchi static enum lagg_iftypes
210f0101d0eSyamaguchi lagg_iftype = LAGG_IF_TYPE_ETHERNET;
211f0101d0eSyamaguchi
212f0101d0eSyamaguchi #ifdef LAGG_DEBUG
2135cfb8022Syamaguchi #define __LAGGDEBUGUSED
214f0101d0eSyamaguchi #define LAGG_DPRINTF(_sc, _fmt, _args...) do { \
215f0101d0eSyamaguchi printf("%s: " _fmt, (_sc) != NULL ? \
2162ed90b61Syamaguchi (_sc)->sc_if.if_xname : "lagg", ##_args); \
217f0101d0eSyamaguchi } while (0)
218f0101d0eSyamaguchi #else
2195cfb8022Syamaguchi #define __LAGGDEBUGUSED __unused
220f0101d0eSyamaguchi #define LAGG_DPRINTF(_sc, _fmt, _args...) __nothing
221f0101d0eSyamaguchi #endif
222f0101d0eSyamaguchi
22380bf8caaSyamaguchi #ifndef LAGG_SETCAPS_RETRY
22480bf8caaSyamaguchi #define LAGG_SETCAPS_RETRY (LAGG_MAX_PORTS * 2)
22580bf8caaSyamaguchi #endif
22680bf8caaSyamaguchi
22774194586Syamaguchi static size_t
lagg_sizeof_softc(enum lagg_iftypes ift)228f0101d0eSyamaguchi lagg_sizeof_softc(enum lagg_iftypes ift)
229f0101d0eSyamaguchi {
230f0101d0eSyamaguchi struct lagg_softc *_dummy = NULL;
231f0101d0eSyamaguchi size_t s;
232f0101d0eSyamaguchi
233f0101d0eSyamaguchi s = sizeof(*_dummy) - sizeof(_dummy->sc_if);
234f0101d0eSyamaguchi
235f0101d0eSyamaguchi switch (ift) {
236f0101d0eSyamaguchi case LAGG_IF_TYPE_ETHERNET:
237f0101d0eSyamaguchi s += sizeof(struct ethercom);
238f0101d0eSyamaguchi break;
239f0101d0eSyamaguchi default:
240f0101d0eSyamaguchi s += sizeof(struct ifnet);
241f0101d0eSyamaguchi break;
242f0101d0eSyamaguchi }
243f0101d0eSyamaguchi
244f0101d0eSyamaguchi return s;
245f0101d0eSyamaguchi }
246f0101d0eSyamaguchi
24774194586Syamaguchi static void
lagg_evcnt_attach(struct lagg_softc * sc,struct evcnt * ev,const char * name)248f0101d0eSyamaguchi lagg_evcnt_attach(struct lagg_softc *sc,
249f0101d0eSyamaguchi struct evcnt *ev, const char *name)
250f0101d0eSyamaguchi {
251f0101d0eSyamaguchi
252f0101d0eSyamaguchi evcnt_attach_dynamic(ev, EVCNT_TYPE_MISC, NULL,
253f0101d0eSyamaguchi sc->sc_evgroup, name);
254f0101d0eSyamaguchi }
255f0101d0eSyamaguchi
25674194586Syamaguchi static void
lagg_in6_ifattach(struct ifnet * ifp)257f0101d0eSyamaguchi lagg_in6_ifattach(struct ifnet *ifp)
258f0101d0eSyamaguchi {
259f0101d0eSyamaguchi
260f0101d0eSyamaguchi #ifdef INET6
261f0101d0eSyamaguchi KERNEL_LOCK_UNLESS_NET_MPSAFE();
262f0101d0eSyamaguchi if (in6_present) {
263f0101d0eSyamaguchi if (ISSET(ifp->if_flags, IFF_UP))
264f0101d0eSyamaguchi in6_ifattach(ifp, NULL);
265f0101d0eSyamaguchi }
266f0101d0eSyamaguchi KERNEL_UNLOCK_UNLESS_NET_MPSAFE();
267f0101d0eSyamaguchi #endif
268f0101d0eSyamaguchi }
269f0101d0eSyamaguchi
27074194586Syamaguchi static void
lagg_in6_ifdetach(struct ifnet * ifp)271f0101d0eSyamaguchi lagg_in6_ifdetach(struct ifnet *ifp)
272f0101d0eSyamaguchi {
273f0101d0eSyamaguchi
274f0101d0eSyamaguchi #ifdef INET6
275f0101d0eSyamaguchi KERNEL_LOCK_UNLESS_NET_MPSAFE();
276e43cf638Syamaguchi if (in6_present)
277f0101d0eSyamaguchi in6_ifdetach(ifp);
278f0101d0eSyamaguchi KERNEL_UNLOCK_UNLESS_NET_MPSAFE();
279f0101d0eSyamaguchi #endif
280f0101d0eSyamaguchi }
281f0101d0eSyamaguchi
28274194586Syamaguchi static int
lagg_lp_ioctl(struct lagg_port * lp,u_long cmd,void * data)283f0101d0eSyamaguchi lagg_lp_ioctl(struct lagg_port *lp, u_long cmd, void *data)
284f0101d0eSyamaguchi {
285f0101d0eSyamaguchi struct ifnet *ifp_port;
286f0101d0eSyamaguchi int error;
287f0101d0eSyamaguchi
288f0101d0eSyamaguchi if (lp->lp_ioctl == NULL)
289f0101d0eSyamaguchi return EINVAL;
290f0101d0eSyamaguchi
291f0101d0eSyamaguchi ifp_port = lp->lp_ifp;
292f0101d0eSyamaguchi IFNET_LOCK(ifp_port);
293f0101d0eSyamaguchi error = lp->lp_ioctl(ifp_port, cmd, data);
294f0101d0eSyamaguchi IFNET_UNLOCK(ifp_port);
295f0101d0eSyamaguchi
296f0101d0eSyamaguchi return error;
297f0101d0eSyamaguchi }
298f0101d0eSyamaguchi
29974194586Syamaguchi static bool
lagg_lladdr_equal(const uint8_t * a,const uint8_t * b)30074194586Syamaguchi lagg_lladdr_equal(const uint8_t *a, const uint8_t *b)
30174194586Syamaguchi {
30274194586Syamaguchi
30374194586Syamaguchi if (memcmp(a, b, ETHER_ADDR_LEN) == 0)
30474194586Syamaguchi return true;
30574194586Syamaguchi
30674194586Syamaguchi return false;
30774194586Syamaguchi }
30874194586Syamaguchi
30974194586Syamaguchi static void
lagg_lladdr_cpy(uint8_t * dst,const uint8_t * src)31074194586Syamaguchi lagg_lladdr_cpy(uint8_t *dst, const uint8_t *src)
31174194586Syamaguchi {
31274194586Syamaguchi
31374194586Syamaguchi memcpy(dst, src, ETHER_ADDR_LEN);
31474194586Syamaguchi }
31574194586Syamaguchi
316f0101d0eSyamaguchi void
laggattach(int n)317f0101d0eSyamaguchi laggattach(int n)
318f0101d0eSyamaguchi {
319f0101d0eSyamaguchi
320f0101d0eSyamaguchi /*
321f0101d0eSyamaguchi * Nothing to do here, initialization is handled by the
322f0101d0eSyamaguchi * module initialization code in lagginit() below).
323f0101d0eSyamaguchi */
324f0101d0eSyamaguchi }
325f0101d0eSyamaguchi
326f0101d0eSyamaguchi static void
lagginit(void)327f0101d0eSyamaguchi lagginit(void)
328f0101d0eSyamaguchi {
329f0101d0eSyamaguchi size_t i;
330f0101d0eSyamaguchi
331f0101d0eSyamaguchi lagg_psref_class = psref_class_create("laggvariant", IPL_SOFTNET);
332f0101d0eSyamaguchi lagg_port_psref_class = psref_class_create("laggport", IPL_SOFTNET);
333f0101d0eSyamaguchi
334f0101d0eSyamaguchi for (i = 0; i < LAGG_PROTO_MAX; i++) {
335f0101d0eSyamaguchi if (lagg_protos[i].pr_init != NULL)
336f0101d0eSyamaguchi lagg_protos[i].pr_init();
337f0101d0eSyamaguchi }
338f0101d0eSyamaguchi
339f0101d0eSyamaguchi if_clone_attach(&lagg_cloner);
340f0101d0eSyamaguchi }
341f0101d0eSyamaguchi
342f0101d0eSyamaguchi static int
laggdetach(void)343f0101d0eSyamaguchi laggdetach(void)
344f0101d0eSyamaguchi {
345f0101d0eSyamaguchi size_t i;
346f0101d0eSyamaguchi
347f0101d0eSyamaguchi if (lagg_count > 0)
348f0101d0eSyamaguchi return EBUSY;
349f0101d0eSyamaguchi
350f0101d0eSyamaguchi if_clone_detach(&lagg_cloner);
351f0101d0eSyamaguchi
352f0101d0eSyamaguchi for (i = 0; i < LAGG_PROTO_MAX; i++) {
353f0101d0eSyamaguchi if (lagg_protos[i].pr_fini != NULL)
354f0101d0eSyamaguchi lagg_protos[i].pr_fini();
355f0101d0eSyamaguchi }
356f0101d0eSyamaguchi
357f0101d0eSyamaguchi psref_class_destroy(lagg_port_psref_class);
358f0101d0eSyamaguchi psref_class_destroy(lagg_psref_class);
359f0101d0eSyamaguchi
360f0101d0eSyamaguchi return 0;
361f0101d0eSyamaguchi }
362f0101d0eSyamaguchi
363f0101d0eSyamaguchi static int
lagg_clone_create(struct if_clone * ifc,int unit)364f0101d0eSyamaguchi lagg_clone_create(struct if_clone *ifc, int unit)
365f0101d0eSyamaguchi {
366f0101d0eSyamaguchi struct lagg_softc *sc;
367f0101d0eSyamaguchi struct ifnet *ifp;
3686e31a1b0Syamaguchi struct ethercom *ec;
369f0101d0eSyamaguchi int error;
370f0101d0eSyamaguchi
371f0101d0eSyamaguchi sc = lagg_softc_alloc(lagg_iftype);
372f0101d0eSyamaguchi ifp = &sc->sc_if;
373f0101d0eSyamaguchi
374f0101d0eSyamaguchi mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTNET);
375f0101d0eSyamaguchi sc->sc_psz = pserialize_create();
376f0101d0eSyamaguchi SIMPLEQ_INIT(&sc->sc_ports);
377f0101d0eSyamaguchi LIST_INIT(&sc->sc_mclist);
378f0101d0eSyamaguchi TAILQ_INIT(&sc->sc_vtags);
379f0101d0eSyamaguchi sc->sc_hash_mac = true;
380f0101d0eSyamaguchi sc->sc_hash_ipaddr = true;
381f0101d0eSyamaguchi sc->sc_hash_ip6addr = true;
382f0101d0eSyamaguchi sc->sc_hash_tcp = true;
383f0101d0eSyamaguchi sc->sc_hash_udp = true;
384f0101d0eSyamaguchi
385f0101d0eSyamaguchi if_initname(ifp, ifc->ifc_name, unit);
386f0101d0eSyamaguchi ifp->if_softc = sc;
387f0101d0eSyamaguchi ifp->if_init = lagg_init;
388f0101d0eSyamaguchi ifp->if_stop = lagg_stop;
389f0101d0eSyamaguchi ifp->if_ioctl = lagg_ioctl;
390f0101d0eSyamaguchi ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
391f0101d0eSyamaguchi ifp->if_extflags = IFEF_MPSAFE;
392f0101d0eSyamaguchi ifp->if_transmit = lagg_transmit;
393f0101d0eSyamaguchi ifp->if_start = lagg_start;
39494c10bc8Sozaki-r IFQ_SET_READY(&ifp->if_snd);
395f0101d0eSyamaguchi
396f0101d0eSyamaguchi error = lagg_setup_sysctls(sc);
397f0101d0eSyamaguchi if (error != 0)
398f0101d0eSyamaguchi goto destroy_psz;
399f0101d0eSyamaguchi
400f0101d0eSyamaguchi /*XXX dependent on ethernet */
401f0101d0eSyamaguchi ifmedia_init_with_lock(&sc->sc_media, 0, lagg_media_change,
402f0101d0eSyamaguchi lagg_media_status, &sc->sc_lock);
403f0101d0eSyamaguchi ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
404f0101d0eSyamaguchi ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
405f0101d0eSyamaguchi
406076e3579Sriastradh if_initialize(ifp);
407f0101d0eSyamaguchi
408f0101d0eSyamaguchi switch (lagg_iftype) {
409f0101d0eSyamaguchi case LAGG_IF_TYPE_ETHERNET:
4106e31a1b0Syamaguchi ec = (struct ethercom *)ifp;
41174194586Syamaguchi cprng_fast(sc->sc_lladdr_rand, sizeof(sc->sc_lladdr_rand));
412396733a0Syamaguchi sc->sc_lladdr_rand[0] &= 0xFE; /* clear I/G bit */
413396733a0Syamaguchi sc->sc_lladdr_rand[0] |= 0x02; /* set G/L bit */
41474194586Syamaguchi lagg_lladdr_cpy(sc->sc_lladdr, sc->sc_lladdr_rand);
4156e31a1b0Syamaguchi ether_set_vlan_cb(ec, lagg_vlan_cb);
4166e31a1b0Syamaguchi
4176e31a1b0Syamaguchi /*
4186e31a1b0Syamaguchi * notify ETHERCAP_VLAN_HWTAGGING to ether_ifattach
4196e31a1b0Syamaguchi * to handle VLAN tag, stripped by hardware, in bpf(4)
4206e31a1b0Syamaguchi */
4216e31a1b0Syamaguchi ec->ec_capabilities = ETHERCAP_VLAN_HWTAGGING;
4226e31a1b0Syamaguchi
4235cfb8022Syamaguchi ether_ifattach(ifp, sc->sc_lladdr_rand);
424f0101d0eSyamaguchi break;
425f0101d0eSyamaguchi default:
426f0101d0eSyamaguchi panic("unknown if type");
427f0101d0eSyamaguchi }
428f0101d0eSyamaguchi
429f0101d0eSyamaguchi snprintf(sc->sc_evgroup, sizeof(sc->sc_evgroup),
430f0101d0eSyamaguchi "%s", ifp->if_xname);
431f0101d0eSyamaguchi lagg_evcnt_attach(sc, &sc->sc_novar, "no lagg variant");
432f0101d0eSyamaguchi if_link_state_change(&sc->sc_if, LINK_STATE_DOWN);
433f0101d0eSyamaguchi lagg_setup_sysctls(sc);
434f0101d0eSyamaguchi (void)lagg_pr_attach(sc, LAGG_PROTO_NONE);
435f0101d0eSyamaguchi if_register(ifp);
436f0101d0eSyamaguchi lagg_count++;
437f0101d0eSyamaguchi
438f0101d0eSyamaguchi return 0;
439f0101d0eSyamaguchi
440f0101d0eSyamaguchi destroy_psz:
441f0101d0eSyamaguchi pserialize_destroy(sc->sc_psz);
442f0101d0eSyamaguchi mutex_destroy(&sc->sc_lock);
443f0101d0eSyamaguchi lagg_softc_free(sc);
444f0101d0eSyamaguchi
445f0101d0eSyamaguchi return error;
446f0101d0eSyamaguchi }
447f0101d0eSyamaguchi
448f0101d0eSyamaguchi static int
lagg_clone_destroy(struct ifnet * ifp)449f0101d0eSyamaguchi lagg_clone_destroy(struct ifnet *ifp)
450f0101d0eSyamaguchi {
451f0101d0eSyamaguchi struct lagg_softc *sc = (struct lagg_softc *)ifp->if_softc;
4521f3caf1eSyamaguchi struct lagg_port *lp;
453f0101d0eSyamaguchi
454f0101d0eSyamaguchi lagg_stop(ifp, 1);
455f0101d0eSyamaguchi
45699626c34Syamaguchi IFNET_LOCK(ifp);
4571f3caf1eSyamaguchi LAGG_LOCK(sc);
4581f3caf1eSyamaguchi while ((lp = LAGG_PORTS_FIRST(sc)) != NULL) {
4591f3caf1eSyamaguchi lagg_port_teardown(sc, lp, false);
4601f3caf1eSyamaguchi }
4611f3caf1eSyamaguchi LAGG_UNLOCK(sc);
46299626c34Syamaguchi IFNET_UNLOCK(ifp);
463f0101d0eSyamaguchi
464f0101d0eSyamaguchi switch (ifp->if_type) {
465f0101d0eSyamaguchi case IFT_ETHER:
466f0101d0eSyamaguchi ether_ifdetach(ifp);
467f0101d0eSyamaguchi KASSERT(TAILQ_EMPTY(&sc->sc_vtags));
468f0101d0eSyamaguchi break;
469f0101d0eSyamaguchi }
470f0101d0eSyamaguchi
471f0101d0eSyamaguchi if_detach(ifp);
472f0101d0eSyamaguchi ifmedia_fini(&sc->sc_media);
473f0101d0eSyamaguchi lagg_pr_detach(sc);
474f0101d0eSyamaguchi evcnt_detach(&sc->sc_novar);
475f0101d0eSyamaguchi lagg_teardown_sysctls(sc);
476f0101d0eSyamaguchi
477f0101d0eSyamaguchi pserialize_destroy(sc->sc_psz);
478f0101d0eSyamaguchi mutex_destroy(&sc->sc_lock);
479f0101d0eSyamaguchi lagg_softc_free(sc);
480f0101d0eSyamaguchi
481f0101d0eSyamaguchi if (lagg_count > 0)
482f0101d0eSyamaguchi lagg_count--;
483f0101d0eSyamaguchi
484f0101d0eSyamaguchi return 0;
485f0101d0eSyamaguchi }
486f0101d0eSyamaguchi
487f0101d0eSyamaguchi static int
lagg_init(struct ifnet * ifp)488f0101d0eSyamaguchi lagg_init(struct ifnet *ifp)
489f0101d0eSyamaguchi {
490f0101d0eSyamaguchi struct lagg_softc *sc;
491f0101d0eSyamaguchi int rv;
492f0101d0eSyamaguchi
493f0101d0eSyamaguchi sc = ifp->if_softc;
494f0101d0eSyamaguchi LAGG_LOCK(sc);
495f0101d0eSyamaguchi rv = lagg_init_locked(sc);
496f0101d0eSyamaguchi LAGG_UNLOCK(sc);
497f0101d0eSyamaguchi
498f0101d0eSyamaguchi return rv;
499f0101d0eSyamaguchi }
500f0101d0eSyamaguchi
501f0101d0eSyamaguchi static int
lagg_init_locked(struct lagg_softc * sc)502f0101d0eSyamaguchi lagg_init_locked(struct lagg_softc *sc)
503f0101d0eSyamaguchi {
504f0101d0eSyamaguchi struct ifnet *ifp = &sc->sc_if;
505f0101d0eSyamaguchi int rv;
506f0101d0eSyamaguchi
507f0101d0eSyamaguchi KASSERT(LAGG_LOCKED(sc));
508f0101d0eSyamaguchi
509f0101d0eSyamaguchi if (ISSET(ifp->if_flags, IFF_RUNNING))
510f0101d0eSyamaguchi lagg_stop_locked(sc);
511f0101d0eSyamaguchi
5125cfb8022Syamaguchi lagg_sync_sadl(sc);
51374194586Syamaguchi
514f0101d0eSyamaguchi SET(ifp->if_flags, IFF_RUNNING);
515f0101d0eSyamaguchi
516f0101d0eSyamaguchi rv = lagg_proto_up(sc);
517f0101d0eSyamaguchi if (rv != 0)
518f0101d0eSyamaguchi lagg_stop_locked(sc);
519f0101d0eSyamaguchi
520f0101d0eSyamaguchi return rv;
521f0101d0eSyamaguchi }
522f0101d0eSyamaguchi
523f0101d0eSyamaguchi static void
lagg_stop(struct ifnet * ifp,int disable __unused)524f0101d0eSyamaguchi lagg_stop(struct ifnet *ifp, int disable __unused)
525f0101d0eSyamaguchi {
526f0101d0eSyamaguchi struct lagg_softc *sc;
527f0101d0eSyamaguchi
528f0101d0eSyamaguchi sc = ifp->if_softc;
529f0101d0eSyamaguchi LAGG_LOCK(sc);
530f0101d0eSyamaguchi lagg_stop_locked(sc);
531f0101d0eSyamaguchi LAGG_UNLOCK(sc);
532f0101d0eSyamaguchi }
533f0101d0eSyamaguchi
534f0101d0eSyamaguchi static void
lagg_stop_locked(struct lagg_softc * sc)535f0101d0eSyamaguchi lagg_stop_locked(struct lagg_softc *sc)
536f0101d0eSyamaguchi {
537f0101d0eSyamaguchi struct ifnet *ifp = &sc->sc_if;
538f0101d0eSyamaguchi
539f0101d0eSyamaguchi KASSERT(LAGG_LOCKED(sc));
540f0101d0eSyamaguchi
541f0101d0eSyamaguchi if (!ISSET(ifp->if_flags, IFF_RUNNING))
542f0101d0eSyamaguchi return;
543f0101d0eSyamaguchi
544f0101d0eSyamaguchi CLR(ifp->if_flags, IFF_RUNNING);
545f0101d0eSyamaguchi lagg_proto_down(sc);
546f0101d0eSyamaguchi
547f0101d0eSyamaguchi }
548f0101d0eSyamaguchi
549f0101d0eSyamaguchi static int
lagg_config(struct lagg_softc * sc,struct lagg_req * lrq)550f0101d0eSyamaguchi lagg_config(struct lagg_softc *sc, struct lagg_req *lrq)
551f0101d0eSyamaguchi {
552f0101d0eSyamaguchi struct ifnet *ifp_port;
553f0101d0eSyamaguchi struct laggreqport *rp;
554f0101d0eSyamaguchi struct lagg_port *lp;
555f0101d0eSyamaguchi struct psref psref;
556f0101d0eSyamaguchi size_t i;
557f0101d0eSyamaguchi int error, bound;
558f0101d0eSyamaguchi
559f0101d0eSyamaguchi error = 0;
560f0101d0eSyamaguchi bound = curlwp_bind();
561f0101d0eSyamaguchi
562f0101d0eSyamaguchi switch (lrq->lrq_ioctl) {
563f0101d0eSyamaguchi case LAGGIOC_SETPROTO:
564f0101d0eSyamaguchi if (lrq->lrq_proto >= LAGG_PROTO_MAX) {
565f0101d0eSyamaguchi error = EPROTONOSUPPORT;
566f0101d0eSyamaguchi break;
567f0101d0eSyamaguchi }
568f0101d0eSyamaguchi
5691f3caf1eSyamaguchi error = lagg_delport_all(sc);
5701f3caf1eSyamaguchi if (error != 0)
5711f3caf1eSyamaguchi break;
572f0101d0eSyamaguchi error = lagg_pr_attach(sc, lrq->lrq_proto);
573f0101d0eSyamaguchi if (error != 0)
574f0101d0eSyamaguchi break;
575f0101d0eSyamaguchi
576f0101d0eSyamaguchi for (i = 0; i < lrq->lrq_nports; i++) {
577f0101d0eSyamaguchi rp = &lrq->lrq_reqports[i];
578f0101d0eSyamaguchi ifp_port = if_get(rp->rp_portname, &psref);
579f0101d0eSyamaguchi if (ifp_port == NULL) {
580f0101d0eSyamaguchi error = ENOENT;
581f0101d0eSyamaguchi break; /* break for */
582f0101d0eSyamaguchi }
583f0101d0eSyamaguchi
584f0101d0eSyamaguchi error = lagg_addport(sc, ifp_port);
585f0101d0eSyamaguchi if_put(ifp_port, &psref);
586f0101d0eSyamaguchi
587f0101d0eSyamaguchi if (error != 0)
588f0101d0eSyamaguchi break; /* break for */
589f0101d0eSyamaguchi }
590f0101d0eSyamaguchi break; /* break switch */
591f0101d0eSyamaguchi case LAGGIOC_ADDPORT:
592f0101d0eSyamaguchi rp = &lrq->lrq_reqports[0];
593f0101d0eSyamaguchi ifp_port = if_get(rp->rp_portname, &psref);
594f0101d0eSyamaguchi if (ifp_port == NULL) {
595f0101d0eSyamaguchi error = ENOENT;
596f0101d0eSyamaguchi break;
597f0101d0eSyamaguchi }
598f0101d0eSyamaguchi
599f0101d0eSyamaguchi error = lagg_addport(sc, ifp_port);
600f0101d0eSyamaguchi if_put(ifp_port, &psref);
601f0101d0eSyamaguchi break;
602f0101d0eSyamaguchi case LAGGIOC_DELPORT:
603f0101d0eSyamaguchi rp = &lrq->lrq_reqports[0];
604f0101d0eSyamaguchi ifp_port = if_get(rp->rp_portname, &psref);
605f0101d0eSyamaguchi if (ifp_port == NULL) {
606f0101d0eSyamaguchi error = ENOENT;
607f0101d0eSyamaguchi break;
608f0101d0eSyamaguchi }
609f0101d0eSyamaguchi
610f0101d0eSyamaguchi error = lagg_delport(sc, ifp_port);
611f0101d0eSyamaguchi if_put(ifp_port, &psref);
612f0101d0eSyamaguchi break;
613f0101d0eSyamaguchi case LAGGIOC_SETPORTPRI:
614f0101d0eSyamaguchi rp = &lrq->lrq_reqports[0];
615f0101d0eSyamaguchi ifp_port = if_get(rp->rp_portname, &psref);
616f0101d0eSyamaguchi if (ifp_port == NULL) {
617f0101d0eSyamaguchi error = ENOENT;
618f0101d0eSyamaguchi break;
619f0101d0eSyamaguchi }
620f0101d0eSyamaguchi
621f0101d0eSyamaguchi lp = ifp_port->if_lagg;
622f0101d0eSyamaguchi if (lp == NULL || lp->lp_softc != sc) {
623f0101d0eSyamaguchi if_put(ifp_port, &psref);
624f0101d0eSyamaguchi error = ENOENT;
625f0101d0eSyamaguchi break;
626f0101d0eSyamaguchi }
627f0101d0eSyamaguchi
628f0101d0eSyamaguchi lp->lp_prio = rp->rp_prio;
629f0101d0eSyamaguchi
630f0101d0eSyamaguchi /* restart protocol */
631f0101d0eSyamaguchi LAGG_LOCK(sc);
632f0101d0eSyamaguchi lagg_proto_stopport(sc, lp);
633f0101d0eSyamaguchi lagg_proto_startport(sc, lp);
634f0101d0eSyamaguchi LAGG_UNLOCK(sc);
635f0101d0eSyamaguchi if_put(ifp_port, &psref);
636f0101d0eSyamaguchi break;
637f0101d0eSyamaguchi case LAGGIOC_SETPROTOOPT:
638f0101d0eSyamaguchi error = lagg_proto_ioctl(sc, lrq);
639f0101d0eSyamaguchi break;
640f0101d0eSyamaguchi default:
641f0101d0eSyamaguchi error = ENOTTY;
642f0101d0eSyamaguchi }
643f0101d0eSyamaguchi
644f0101d0eSyamaguchi curlwp_bindx(bound);
645f0101d0eSyamaguchi return error;
646f0101d0eSyamaguchi }
647f0101d0eSyamaguchi
648f0101d0eSyamaguchi static int
lagg_ioctl(struct ifnet * ifp,u_long cmd,void * data)649f0101d0eSyamaguchi lagg_ioctl(struct ifnet *ifp, u_long cmd, void *data)
650f0101d0eSyamaguchi {
651f0101d0eSyamaguchi struct lagg_softc *sc;
652f0101d0eSyamaguchi struct ifreq *ifr = (struct ifreq *)data;
653f0101d0eSyamaguchi struct lagg_req laggreq, *laggresp;
654f0101d0eSyamaguchi struct lagg_port *lp;
655f0101d0eSyamaguchi size_t allocsiz, outlen, nports;
656f0101d0eSyamaguchi char *outbuf;
657f0101d0eSyamaguchi void *buf;
658f0101d0eSyamaguchi int error = 0, rv;
659f0101d0eSyamaguchi
660f0101d0eSyamaguchi sc = ifp->if_softc;
661f0101d0eSyamaguchi
662f0101d0eSyamaguchi switch (cmd) {
663f0101d0eSyamaguchi case SIOCGLAGG:
664f0101d0eSyamaguchi error = copyin(ifr->ifr_data, &laggreq, sizeof(laggreq));
665f0101d0eSyamaguchi if (error != 0)
666f0101d0eSyamaguchi break;
667f0101d0eSyamaguchi
668f0101d0eSyamaguchi nports = sc->sc_nports;
669f0101d0eSyamaguchi nports = MIN(nports, laggreq.lrq_nports);
670f0101d0eSyamaguchi
671f0101d0eSyamaguchi allocsiz = sizeof(*laggresp)
672f0101d0eSyamaguchi + sizeof(laggresp->lrq_reqports[0]) * nports;
673f0101d0eSyamaguchi laggresp = kmem_zalloc(allocsiz, KM_SLEEP);
674f0101d0eSyamaguchi
675f0101d0eSyamaguchi rv = lagg_get_stats(sc, laggresp, nports);
676f0101d0eSyamaguchi
677f0101d0eSyamaguchi outbuf = (char *)laggresp;
678f0101d0eSyamaguchi
679f0101d0eSyamaguchi nports = MIN(laggresp->lrq_nports, nports);
680f0101d0eSyamaguchi outlen = sizeof(*laggresp)
681f0101d0eSyamaguchi + sizeof(laggresp->lrq_reqports[0]) * nports;
682f0101d0eSyamaguchi
683f0101d0eSyamaguchi error = copyout(outbuf, ifr->ifr_data, outlen);
684f0101d0eSyamaguchi kmem_free(outbuf, allocsiz);
685f0101d0eSyamaguchi
686f0101d0eSyamaguchi if (error == 0 && rv != 0)
687f0101d0eSyamaguchi error = rv;
688f0101d0eSyamaguchi
689f0101d0eSyamaguchi break;
690f0101d0eSyamaguchi case SIOCSLAGG:
691f0101d0eSyamaguchi error = copyin(ifr->ifr_data, &laggreq, sizeof(laggreq));
692f0101d0eSyamaguchi if (error != 0)
693f0101d0eSyamaguchi break;
694f0101d0eSyamaguchi
695f0101d0eSyamaguchi nports = laggreq.lrq_nports;
6969fc4f923Syamaguchi if (nports > LAGG_MAX_PORTS) {
6979fc4f923Syamaguchi error = ENOMEM;
6989fc4f923Syamaguchi break;
6999fc4f923Syamaguchi } else if (nports > 0) {
700f0101d0eSyamaguchi allocsiz = sizeof(struct lagg_req)
701f0101d0eSyamaguchi + sizeof(struct laggreqport) * nports;
702f0101d0eSyamaguchi buf = kmem_alloc(allocsiz, KM_SLEEP);
703f0101d0eSyamaguchi
704f0101d0eSyamaguchi error = copyin(ifr->ifr_data, buf, allocsiz);
705f0101d0eSyamaguchi if (error != 0) {
706f0101d0eSyamaguchi kmem_free(buf, allocsiz);
707f0101d0eSyamaguchi break;
708f0101d0eSyamaguchi }
709f0101d0eSyamaguchi } else {
710f0101d0eSyamaguchi buf = (void *)&laggreq;
711f0101d0eSyamaguchi allocsiz = 0;
712f0101d0eSyamaguchi }
713f0101d0eSyamaguchi
714f0101d0eSyamaguchi error = lagg_config(sc, buf);
715f0101d0eSyamaguchi if (allocsiz > 0)
716f0101d0eSyamaguchi kmem_free(buf, allocsiz);
717f0101d0eSyamaguchi break;
718f0101d0eSyamaguchi case SIOCSIFFLAGS:
719f0101d0eSyamaguchi error = ifioctl_common(ifp, cmd, data);
720f0101d0eSyamaguchi if (error != 0)
721f0101d0eSyamaguchi break;
722f0101d0eSyamaguchi
723f0101d0eSyamaguchi switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
724f0101d0eSyamaguchi case IFF_RUNNING:
72566fd63daSriastradh if_stop(ifp, 1);
726f0101d0eSyamaguchi break;
727f0101d0eSyamaguchi case IFF_UP:
728b4d088cbSriastradh error = if_init(ifp);
729f0101d0eSyamaguchi break;
730f0101d0eSyamaguchi }
731f0101d0eSyamaguchi
732f0101d0eSyamaguchi if (error != 0)
733f0101d0eSyamaguchi break;
734f0101d0eSyamaguchi
735f0101d0eSyamaguchi /* Set flags on ports too */
736f0101d0eSyamaguchi LAGG_LOCK(sc);
737f0101d0eSyamaguchi LAGG_PORTS_FOREACH(sc, lp) {
738f0101d0eSyamaguchi (void)lagg_config_promisc(sc, lp);
739f0101d0eSyamaguchi }
740f0101d0eSyamaguchi LAGG_UNLOCK(sc);
741f0101d0eSyamaguchi break;
742f0101d0eSyamaguchi case SIOCSIFMTU:
7431e945cefSyamaguchi /* set the MTU to each port */
744d9d48b03Syamaguchi LAGG_LOCK(sc);
745f0101d0eSyamaguchi LAGG_PORTS_FOREACH(sc, lp) {
746f0101d0eSyamaguchi error = lagg_lp_ioctl(lp, cmd, (void *)ifr);
747f0101d0eSyamaguchi
748f0101d0eSyamaguchi if (error != 0) {
7497fdf5714Syamaguchi LAGG_LOG(sc, LOG_ERR,
750f0101d0eSyamaguchi "failed to change MTU to %d on port %s, "
75131123504Srillig "reverting all ports to original "
75231123504Srillig "MTU(%" PRIu64 ")\n",
753f0101d0eSyamaguchi ifr->ifr_mtu, lp->lp_ifp->if_xname,
754f0101d0eSyamaguchi ifp->if_mtu);
755f0101d0eSyamaguchi break;
756f0101d0eSyamaguchi }
757f0101d0eSyamaguchi }
758d9d48b03Syamaguchi LAGG_UNLOCK(sc);
759f0101d0eSyamaguchi
7601e945cefSyamaguchi /* set the MTU to the lagg interface */
7611e945cefSyamaguchi if (error == 0)
7621e945cefSyamaguchi error = ether_ioctl(ifp, cmd, data);
7631e945cefSyamaguchi
7641e945cefSyamaguchi if (error != 0) {
7651e945cefSyamaguchi /* undo the changed MTU */
766f0101d0eSyamaguchi ifr->ifr_mtu = ifp->if_mtu;
767d9d48b03Syamaguchi
768d9d48b03Syamaguchi LAGG_LOCK(sc);
769f0101d0eSyamaguchi LAGG_PORTS_FOREACH(sc, lp) {
770e43cf638Syamaguchi if (lp->lp_ioctl != NULL)
771f0101d0eSyamaguchi lagg_lp_ioctl(lp, cmd, (void *)ifr);
772f0101d0eSyamaguchi }
773f0101d0eSyamaguchi LAGG_UNLOCK(sc);
774d9d48b03Syamaguchi }
775f0101d0eSyamaguchi break;
776f0101d0eSyamaguchi case SIOCADDMULTI:
777f0101d0eSyamaguchi if (sc->sc_if.if_type == IFT_ETHER) {
778f0101d0eSyamaguchi error = lagg_ether_addmulti(sc, ifr);
779f0101d0eSyamaguchi } else {
780f0101d0eSyamaguchi error = EPROTONOSUPPORT;
781f0101d0eSyamaguchi }
782f0101d0eSyamaguchi break;
783f0101d0eSyamaguchi case SIOCDELMULTI:
784f0101d0eSyamaguchi if (sc->sc_if.if_type == IFT_ETHER) {
785f0101d0eSyamaguchi error = lagg_ether_delmulti(sc, ifr);
786f0101d0eSyamaguchi } else {
787f0101d0eSyamaguchi error = EPROTONOSUPPORT;
788f0101d0eSyamaguchi }
789f0101d0eSyamaguchi break;
790bcfe44c5Syamaguchi case SIOCSIFCAP:
791bcfe44c5Syamaguchi error = ether_ioctl(ifp, cmd, data);
792bcfe44c5Syamaguchi if (error == 0)
793bcfe44c5Syamaguchi lagg_sync_ifcaps(sc);
794bcfe44c5Syamaguchi break;
795bcfe44c5Syamaguchi case SIOCSETHERCAP:
796bcfe44c5Syamaguchi error = ether_ioctl(ifp, cmd, data);
797bcfe44c5Syamaguchi if (error == 0)
798bcfe44c5Syamaguchi lagg_sync_ethcaps(sc);
799bcfe44c5Syamaguchi break;
800f0101d0eSyamaguchi default:
801f0101d0eSyamaguchi error = ether_ioctl(ifp, cmd, data);
802f0101d0eSyamaguchi }
803f0101d0eSyamaguchi return error;
804f0101d0eSyamaguchi }
805f0101d0eSyamaguchi
806f0101d0eSyamaguchi static int
lagg_setup_sysctls(struct lagg_softc * sc)807f0101d0eSyamaguchi lagg_setup_sysctls(struct lagg_softc *sc)
808f0101d0eSyamaguchi {
8097fdf5714Syamaguchi struct sysctllog **slog;
810f0101d0eSyamaguchi const struct sysctlnode **rnode, *hashnode;
811f0101d0eSyamaguchi const char *ifname;
812f0101d0eSyamaguchi int error;
813f0101d0eSyamaguchi
8147fdf5714Syamaguchi slog = &sc->sc_sysctllog;
815f0101d0eSyamaguchi rnode = &sc->sc_sysctlnode;
816f0101d0eSyamaguchi ifname = sc->sc_if.if_xname;
817f0101d0eSyamaguchi
8187fdf5714Syamaguchi error = sysctl_createv(slog, 0, NULL, rnode,
819f0101d0eSyamaguchi CTLFLAG_PERMANENT, CTLTYPE_NODE, ifname,
820f0101d0eSyamaguchi SYSCTL_DESCR("lagg information and settings"),
821f0101d0eSyamaguchi NULL, 0, NULL, 0, CTL_NET, CTL_CREATE, CTL_EOL);
822f0101d0eSyamaguchi if (error != 0)
823f0101d0eSyamaguchi goto done;
824f0101d0eSyamaguchi
8257fdf5714Syamaguchi error = sysctl_createv(slog, 0, rnode, &hashnode,
826f0101d0eSyamaguchi CTLFLAG_PERMANENT, CTLTYPE_NODE, "hash",
827f0101d0eSyamaguchi SYSCTL_DESCR("hash calculation settings"),
828f0101d0eSyamaguchi NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
829f0101d0eSyamaguchi if (error != 0)
830f0101d0eSyamaguchi goto done;
831f0101d0eSyamaguchi
8327fdf5714Syamaguchi error = sysctl_createv(slog, 0, &hashnode, NULL,
833f0101d0eSyamaguchi CTLFLAG_READWRITE, CTLTYPE_BOOL, "macaddr",
834f0101d0eSyamaguchi SYSCTL_DESCR("use src/dst mac addresses"),
835f0101d0eSyamaguchi NULL, 0, &sc->sc_hash_mac, 0, CTL_CREATE, CTL_EOL);
836f0101d0eSyamaguchi if (error != 0)
837f0101d0eSyamaguchi goto done;
838f0101d0eSyamaguchi
8397fdf5714Syamaguchi error = sysctl_createv(slog, 0, &hashnode, NULL,
840f0101d0eSyamaguchi CTLFLAG_READWRITE, CTLTYPE_BOOL, "ipaddr",
841f0101d0eSyamaguchi SYSCTL_DESCR("use src/dst IPv4 addresses"),
842f0101d0eSyamaguchi NULL, 0, &sc->sc_hash_ipaddr, 0, CTL_CREATE, CTL_EOL);
843f0101d0eSyamaguchi if (error != 0)
844f0101d0eSyamaguchi goto done;
845f0101d0eSyamaguchi
8467fdf5714Syamaguchi error = sysctl_createv(slog, 0, &hashnode, NULL,
847f0101d0eSyamaguchi CTLFLAG_READWRITE, CTLTYPE_BOOL, "ip6addr",
848f0101d0eSyamaguchi SYSCTL_DESCR("use src/dst IPv6 addresses"),
849f0101d0eSyamaguchi NULL, 0, &sc->sc_hash_ip6addr, 0, CTL_CREATE, CTL_EOL);
850f0101d0eSyamaguchi if (error != 0)
851f0101d0eSyamaguchi goto done;
852f0101d0eSyamaguchi
8537fdf5714Syamaguchi error = sysctl_createv(slog, 0, &hashnode, NULL,
854f0101d0eSyamaguchi CTLFLAG_READWRITE, CTLTYPE_BOOL, "tcp",
855f0101d0eSyamaguchi SYSCTL_DESCR("use TCP src/dst port"),
856f0101d0eSyamaguchi NULL, 0, &sc->sc_hash_tcp, 0, CTL_CREATE, CTL_EOL);
857f0101d0eSyamaguchi if (error != 0)
858f0101d0eSyamaguchi goto done;
859f0101d0eSyamaguchi
8607fdf5714Syamaguchi error = sysctl_createv(slog, 0, &hashnode, NULL,
861f0101d0eSyamaguchi CTLFLAG_READWRITE, CTLTYPE_BOOL, "udp",
862f0101d0eSyamaguchi SYSCTL_DESCR("use UDP src/dst port"),
863f0101d0eSyamaguchi NULL, 0, &sc->sc_hash_udp, 0, CTL_CREATE, CTL_EOL);
864f0101d0eSyamaguchi done:
865f0101d0eSyamaguchi if (error != 0) {
8667fdf5714Syamaguchi LAGG_LOG(sc, LOG_ERR, "unable to create sysctl node\n");
8677fdf5714Syamaguchi sysctl_teardown(slog);
868f0101d0eSyamaguchi }
869f0101d0eSyamaguchi
870f0101d0eSyamaguchi return error;
871f0101d0eSyamaguchi }
872f0101d0eSyamaguchi
873f0101d0eSyamaguchi static void
lagg_teardown_sysctls(struct lagg_softc * sc)874f0101d0eSyamaguchi lagg_teardown_sysctls(struct lagg_softc *sc)
875f0101d0eSyamaguchi {
876f0101d0eSyamaguchi
877f0101d0eSyamaguchi sc->sc_sysctlnode = NULL;
878f0101d0eSyamaguchi sysctl_teardown(&sc->sc_sysctllog);
879f0101d0eSyamaguchi }
880f0101d0eSyamaguchi
881f0101d0eSyamaguchi uint32_t
lagg_hashmbuf(struct lagg_softc * sc,struct mbuf * m)882f0101d0eSyamaguchi lagg_hashmbuf(struct lagg_softc *sc, struct mbuf *m)
883f0101d0eSyamaguchi {
884f0101d0eSyamaguchi union {
885f0101d0eSyamaguchi struct ether_header _eh;
886f0101d0eSyamaguchi struct ether_vlan_header _evl;
887f0101d0eSyamaguchi struct ip _ip;
888f0101d0eSyamaguchi struct ip6_hdr _ip6;
889f0101d0eSyamaguchi struct tcphdr _th;
890f0101d0eSyamaguchi struct udphdr _uh;
891f0101d0eSyamaguchi } buf;
892f0101d0eSyamaguchi const struct ether_header *eh;
893f0101d0eSyamaguchi const struct ether_vlan_header *evl;
894f0101d0eSyamaguchi const struct ip *ip;
895f0101d0eSyamaguchi const struct ip6_hdr *ip6;
896f0101d0eSyamaguchi const struct tcphdr *th;
897f0101d0eSyamaguchi const struct udphdr *uh;
898e903d516Syamaguchi uint32_t hash, hash_src, hash_dst;
899f0101d0eSyamaguchi uint32_t flowlabel;
900f0101d0eSyamaguchi uint16_t etype, vlantag;
901f0101d0eSyamaguchi uint8_t proto;
902f0101d0eSyamaguchi size_t off;
903f0101d0eSyamaguchi
904f0101d0eSyamaguchi KASSERT(ISSET(m->m_flags, M_PKTHDR));
905f0101d0eSyamaguchi
906e903d516Syamaguchi hash = HASH32_BUF_INIT;
907e903d516Syamaguchi hash_src = HASH32_BUF_INIT;
908e903d516Syamaguchi hash_dst = HASH32_BUF_INIT;
909e903d516Syamaguchi
910e903d516Syamaguchi #define LAGG_HASH_ADD(hp, v) do { \
911e903d516Syamaguchi *(hp) = hash32_buf(&(v), sizeof(v), *(hp)); \
912e903d516Syamaguchi } while(0)
913e903d516Syamaguchi
914a27b16dfSriastradh eh = lagg_m_extract(m, 0, sizeof(*eh), __alignof(*eh), &buf);
915e43cf638Syamaguchi if (eh == NULL)
916e903d516Syamaguchi goto out;
917e43cf638Syamaguchi
918f0101d0eSyamaguchi off = ETHER_HDR_LEN;
919f0101d0eSyamaguchi etype = ntohs(eh->ether_type);
920f0101d0eSyamaguchi
921f0101d0eSyamaguchi if (etype == ETHERTYPE_VLAN) {
922a27b16dfSriastradh evl = lagg_m_extract(m, 0, sizeof(*evl), __alignof(*evl),
923a27b16dfSriastradh &buf);
924e43cf638Syamaguchi if (evl == NULL)
925e903d516Syamaguchi goto out;
926f0101d0eSyamaguchi
927f0101d0eSyamaguchi vlantag = ntohs(evl->evl_tag);
928f0101d0eSyamaguchi etype = ntohs(evl->evl_proto);
929f0101d0eSyamaguchi off += ETHER_VLAN_ENCAP_LEN;
930f0101d0eSyamaguchi } else if (vlan_has_tag(m)) {
931f0101d0eSyamaguchi vlantag = vlan_get_tag(m);
932f0101d0eSyamaguchi } else {
933f0101d0eSyamaguchi vlantag = 0;
934f0101d0eSyamaguchi }
935f0101d0eSyamaguchi
936f0101d0eSyamaguchi if (sc->sc_hash_mac) {
937e903d516Syamaguchi LAGG_HASH_ADD(&hash_dst, eh->ether_dhost);
938e903d516Syamaguchi LAGG_HASH_ADD(&hash_src, eh->ether_shost);
939e903d516Syamaguchi LAGG_HASH_ADD(&hash, vlantag);
940f0101d0eSyamaguchi }
941f0101d0eSyamaguchi
942f0101d0eSyamaguchi switch (etype) {
943f0101d0eSyamaguchi case ETHERTYPE_IP:
944a27b16dfSriastradh ip = lagg_m_extract(m, off, sizeof(*ip), __alignof(*ip), &buf);
945e43cf638Syamaguchi if (ip == NULL)
946e903d516Syamaguchi goto out;
947f0101d0eSyamaguchi
948f0101d0eSyamaguchi if (sc->sc_hash_ipaddr) {
949e903d516Syamaguchi LAGG_HASH_ADD(&hash_src, ip->ip_src);
950e903d516Syamaguchi LAGG_HASH_ADD(&hash_dst, ip->ip_dst);
951e903d516Syamaguchi LAGG_HASH_ADD(&hash, ip->ip_p);
952f0101d0eSyamaguchi }
953f0101d0eSyamaguchi off += ip->ip_hl << 2;
954f0101d0eSyamaguchi proto = ip->ip_p;
955f0101d0eSyamaguchi break;
956f0101d0eSyamaguchi case ETHERTYPE_IPV6:
957a27b16dfSriastradh ip6 = lagg_m_extract(m, off, sizeof(*ip6), __alignof(*ip6),
958a27b16dfSriastradh &buf);
959e43cf638Syamaguchi if (ip6 == NULL)
960e903d516Syamaguchi goto out;
961f0101d0eSyamaguchi
962f0101d0eSyamaguchi if (sc->sc_hash_ip6addr) {
963e903d516Syamaguchi LAGG_HASH_ADD(&hash_src, ip6->ip6_src);
964e903d516Syamaguchi LAGG_HASH_ADD(&hash_dst, ip6->ip6_dst);
965f0101d0eSyamaguchi flowlabel = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
966e903d516Syamaguchi LAGG_HASH_ADD(&hash, flowlabel);
967f0101d0eSyamaguchi }
968f0101d0eSyamaguchi proto = ip6->ip6_nxt;
969f0101d0eSyamaguchi off += sizeof(*ip6);
970e903d516Syamaguchi break;
971f0101d0eSyamaguchi
972f0101d0eSyamaguchi default:
973f0101d0eSyamaguchi return hash;
974f0101d0eSyamaguchi }
975f0101d0eSyamaguchi
976f0101d0eSyamaguchi switch (proto) {
977f0101d0eSyamaguchi case IPPROTO_TCP:
978a27b16dfSriastradh th = lagg_m_extract(m, off, sizeof(*th), __alignof(*th), &buf);
979e43cf638Syamaguchi if (th == NULL)
980e903d516Syamaguchi goto out;
981f0101d0eSyamaguchi
982f0101d0eSyamaguchi if (sc->sc_hash_tcp) {
983e903d516Syamaguchi LAGG_HASH_ADD(&hash_src, th->th_sport);
984e903d516Syamaguchi LAGG_HASH_ADD(&hash_dst, th->th_dport);
985f0101d0eSyamaguchi }
986f0101d0eSyamaguchi break;
987f0101d0eSyamaguchi case IPPROTO_UDP:
988a27b16dfSriastradh uh = lagg_m_extract(m, off, sizeof(*uh), __alignof(*uh), &buf);
989e43cf638Syamaguchi if (uh == NULL)
990e903d516Syamaguchi goto out;
991f0101d0eSyamaguchi
992f0101d0eSyamaguchi if (sc->sc_hash_udp) {
993e903d516Syamaguchi LAGG_HASH_ADD(&hash_src, uh->uh_sport);
994e903d516Syamaguchi LAGG_HASH_ADD(&hash_dst, uh->uh_dport);
995f0101d0eSyamaguchi }
996f0101d0eSyamaguchi break;
997f0101d0eSyamaguchi }
998f0101d0eSyamaguchi
999e903d516Syamaguchi out:
1000e903d516Syamaguchi hash_src ^= hash_dst;
1001e903d516Syamaguchi LAGG_HASH_ADD(&hash, hash_src);
1002e903d516Syamaguchi #undef LAGG_HASH_ADD
1003e903d516Syamaguchi
1004f0101d0eSyamaguchi return hash;
1005f0101d0eSyamaguchi }
1006f0101d0eSyamaguchi
1007f0101d0eSyamaguchi static int
lagg_tx_common(struct ifnet * ifp,struct mbuf * m)1008f0101d0eSyamaguchi lagg_tx_common(struct ifnet *ifp, struct mbuf *m)
1009f0101d0eSyamaguchi {
1010f0101d0eSyamaguchi struct lagg_variant *var;
1011f0101d0eSyamaguchi lagg_proto pr;
1012f0101d0eSyamaguchi struct psref psref;
1013f0101d0eSyamaguchi int error;
1014f0101d0eSyamaguchi
1015f0101d0eSyamaguchi var = lagg_variant_getref(ifp->if_softc, &psref);
1016f0101d0eSyamaguchi
1017f0101d0eSyamaguchi if (__predict_false(var == NULL)) {
1018f0101d0eSyamaguchi m_freem(m);
1019f0101d0eSyamaguchi if_statinc(ifp, if_oerrors);
1020f0101d0eSyamaguchi return ENOENT;
1021f0101d0eSyamaguchi }
1022f0101d0eSyamaguchi
1023f0101d0eSyamaguchi pr = var->lv_proto;
1024f0101d0eSyamaguchi if (__predict_true(lagg_protos[pr].pr_transmit != NULL)) {
1025f0101d0eSyamaguchi error = lagg_protos[pr].pr_transmit(var->lv_psc, m);
1026f0101d0eSyamaguchi /* mbuf is already freed */
1027f0101d0eSyamaguchi } else {
1028f0101d0eSyamaguchi m_freem(m);
1029f0101d0eSyamaguchi if_statinc(ifp, if_oerrors);
1030ca7931b0Syamaguchi error = EIO;
1031f0101d0eSyamaguchi }
1032f0101d0eSyamaguchi
1033f0101d0eSyamaguchi lagg_variant_putref(var, &psref);
1034f0101d0eSyamaguchi
1035f0101d0eSyamaguchi return error;
1036f0101d0eSyamaguchi }
1037f0101d0eSyamaguchi
1038f0101d0eSyamaguchi static int
lagg_transmit(struct ifnet * ifp,struct mbuf * m)1039f0101d0eSyamaguchi lagg_transmit(struct ifnet *ifp, struct mbuf *m)
1040f0101d0eSyamaguchi {
1041f0101d0eSyamaguchi
1042f0101d0eSyamaguchi return lagg_tx_common(ifp, m);
1043f0101d0eSyamaguchi }
1044f0101d0eSyamaguchi
1045f0101d0eSyamaguchi static void
lagg_start(struct ifnet * ifp)1046f0101d0eSyamaguchi lagg_start(struct ifnet *ifp)
1047f0101d0eSyamaguchi {
1048f0101d0eSyamaguchi struct mbuf *m;
1049f0101d0eSyamaguchi
1050f0101d0eSyamaguchi for (;;) {
1051f0101d0eSyamaguchi IFQ_DEQUEUE(&ifp->if_snd, m);
1052f0101d0eSyamaguchi if (m == NULL)
1053f0101d0eSyamaguchi break;
1054f0101d0eSyamaguchi
1055f0101d0eSyamaguchi (void)lagg_tx_common(ifp, m);
1056f0101d0eSyamaguchi }
1057f0101d0eSyamaguchi }
1058f0101d0eSyamaguchi
1059f0101d0eSyamaguchi void
lagg_output(struct lagg_softc * sc,struct lagg_port * lp,struct mbuf * m)10605d4d6e29Syamaguchi lagg_output(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m)
1061f0101d0eSyamaguchi {
1062f0101d0eSyamaguchi struct ifnet *ifp;
1063f0101d0eSyamaguchi int len, error;
1064f0101d0eSyamaguchi short mflags;
1065f0101d0eSyamaguchi
1066f0101d0eSyamaguchi ifp = &sc->sc_if;
1067f0101d0eSyamaguchi len = m->m_pkthdr.len;
1068f0101d0eSyamaguchi mflags = m->m_flags;
1069f0101d0eSyamaguchi
107092525919Syamaguchi error = pfil_run_hooks(ifp->if_pfil, &m, ifp, PFIL_OUT);
10710abd4780Syamaguchi if (error != 0) {
10720abd4780Syamaguchi m_freem(m);
107392525919Syamaguchi return;
10740abd4780Syamaguchi }
107592525919Syamaguchi bpf_mtap(ifp, m, BPF_D_OUT);
107692525919Syamaguchi
1077f0101d0eSyamaguchi error = lagg_port_xmit(lp, m);
1078f0101d0eSyamaguchi if (error) {
1079f0101d0eSyamaguchi /* mbuf is already freed */
1080f0101d0eSyamaguchi if_statinc(ifp, if_oerrors);
1081855e1781Syamaguchi } else {
1082f0101d0eSyamaguchi net_stat_ref_t nsr = IF_STAT_GETREF(ifp);
1083be6f2fceSriastradh if_statinc_ref(ifp, nsr, if_opackets);
1084be6f2fceSriastradh if_statadd_ref(ifp, nsr, if_obytes, len);
1085f0101d0eSyamaguchi if (mflags & M_MCAST)
1086be6f2fceSriastradh if_statinc_ref(ifp, nsr, if_omcasts);
1087f0101d0eSyamaguchi IF_STAT_PUTREF(ifp);
1088f0101d0eSyamaguchi }
1089855e1781Syamaguchi }
1090f0101d0eSyamaguchi
1091f0101d0eSyamaguchi static struct mbuf *
lagg_proto_input(struct lagg_softc * sc,struct lagg_port * lp,struct mbuf * m)1092f0101d0eSyamaguchi lagg_proto_input(struct lagg_softc *sc, struct lagg_port *lp, struct mbuf *m)
1093f0101d0eSyamaguchi {
1094f0101d0eSyamaguchi struct psref psref;
1095f0101d0eSyamaguchi struct lagg_variant *var;
1096f0101d0eSyamaguchi lagg_proto pr;
1097f0101d0eSyamaguchi
1098f0101d0eSyamaguchi var = lagg_variant_getref(sc, &psref);
1099f0101d0eSyamaguchi
1100f0101d0eSyamaguchi if (var == NULL) {
1101f0101d0eSyamaguchi sc->sc_novar.ev_count++;
1102f0101d0eSyamaguchi m_freem(m);
1103f0101d0eSyamaguchi return NULL;
1104f0101d0eSyamaguchi }
1105f0101d0eSyamaguchi
1106f0101d0eSyamaguchi pr = var->lv_proto;
1107f0101d0eSyamaguchi
1108f0101d0eSyamaguchi if (lagg_protos[pr].pr_input != NULL) {
1109f0101d0eSyamaguchi m = lagg_protos[pr].pr_input(var->lv_psc, lp, m);
1110f0101d0eSyamaguchi } else {
1111f0101d0eSyamaguchi m_freem(m);
1112f0101d0eSyamaguchi m = NULL;
1113f0101d0eSyamaguchi }
1114f0101d0eSyamaguchi
1115f0101d0eSyamaguchi lagg_variant_putref(var, &psref);
1116f0101d0eSyamaguchi
1117f0101d0eSyamaguchi return m;
1118f0101d0eSyamaguchi }
1119f0101d0eSyamaguchi
1120d1fb1196Syamaguchi static void
lagg_input_ethernet(struct ifnet * ifp_port,struct mbuf * m)1121f0101d0eSyamaguchi lagg_input_ethernet(struct ifnet *ifp_port, struct mbuf *m)
1122f0101d0eSyamaguchi {
1123f0101d0eSyamaguchi struct ifnet *ifp;
1124f0101d0eSyamaguchi struct psref psref;
1125f0101d0eSyamaguchi struct lagg_port *lp;
1126d1fb1196Syamaguchi struct ether_header *eh;
1127f0101d0eSyamaguchi int s;
1128f0101d0eSyamaguchi
1129f0101d0eSyamaguchi /* sanity check */
1130f0101d0eSyamaguchi s = pserialize_read_enter();
1131f0101d0eSyamaguchi lp = atomic_load_consume(&ifp_port->if_lagg);
1132f0101d0eSyamaguchi if (lp == NULL) {
1133f0101d0eSyamaguchi /* This interface is not a member of lagg */
1134f0101d0eSyamaguchi pserialize_read_exit(s);
1135d1fb1196Syamaguchi m_freem(m);
1136d1fb1196Syamaguchi if_statinc(ifp_port, if_ierrors);
1137d1fb1196Syamaguchi return;
1138f0101d0eSyamaguchi }
1139f0101d0eSyamaguchi lagg_port_getref(lp, &psref);
1140f0101d0eSyamaguchi pserialize_read_exit(s);
1141f0101d0eSyamaguchi
1142cfe7bfbeSyamaguchi ifp = &lp->lp_softc->sc_if;
1143cfe7bfbeSyamaguchi
11446c25ecc7Smartin if (__predict_false(m->m_len < (int)sizeof(*eh))) {
1145d1fb1196Syamaguchi if ((m = m_pullup(m, sizeof(*eh))) == NULL) {
1146cfe7bfbeSyamaguchi if_statinc(ifp, if_ierrors);
1147cfe7bfbeSyamaguchi goto out;
1148cfe7bfbeSyamaguchi }
1149cfe7bfbeSyamaguchi }
1150cfe7bfbeSyamaguchi
1151d1fb1196Syamaguchi eh = mtod(m, struct ether_header *);
1152d1fb1196Syamaguchi
1153d1fb1196Syamaguchi if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
1154d1fb1196Syamaguchi /*
1155d1fb1196Syamaguchi * If this is not a simplex interface, drop the packet
1156d1fb1196Syamaguchi * if it came from us.
1157d1fb1196Syamaguchi */
1158d1fb1196Syamaguchi if ((ifp->if_flags & IFF_SIMPLEX) == 0 &&
1159d1fb1196Syamaguchi memcmp(CLLADDR(ifp->if_sadl), eh->ether_shost,
1160d1fb1196Syamaguchi ETHER_ADDR_LEN) == 0) {
1161d1fb1196Syamaguchi goto drop;
1162d1fb1196Syamaguchi }
1163d1fb1196Syamaguchi
1164d1fb1196Syamaguchi if_statinc(ifp_port, if_imcasts);
1165d1fb1196Syamaguchi } else {
1166187fba3aSyamaguchi /*
1167187fba3aSyamaguchi * Drop promiscuously received packets
1168187fba3aSyamaguchi * if we are not in promiscuous mode.
1169187fba3aSyamaguchi */
1170d1fb1196Syamaguchi if ((ifp->if_flags & IFF_PROMISC) == 0 &&
1171d1fb1196Syamaguchi (ifp_port->if_flags & IFF_PROMISC) != 0 &&
1172d1fb1196Syamaguchi memcmp(CLLADDR(ifp->if_sadl), eh->ether_dhost,
1173d1fb1196Syamaguchi ETHER_ADDR_LEN) != 0)
1174d1fb1196Syamaguchi goto drop;
1175d1fb1196Syamaguchi }
1176d1fb1196Syamaguchi
1177d1fb1196Syamaguchi if_statadd(ifp_port, if_ibytes, m->m_pkthdr.len);
1178d1fb1196Syamaguchi
1179f0101d0eSyamaguchi if (pfil_run_hooks(ifp_port->if_pfil, &m,
11800abd4780Syamaguchi ifp_port, PFIL_IN) != 0) {
11810abd4780Syamaguchi m_freem(m);
11820abd4780Syamaguchi m = NULL;
1183f0101d0eSyamaguchi goto out;
11840abd4780Syamaguchi }
1185f0101d0eSyamaguchi
1186f0101d0eSyamaguchi m = lagg_proto_input(lp->lp_softc, lp, m);
1187f0101d0eSyamaguchi if (m != NULL) {
1188f0101d0eSyamaguchi m_set_rcvif(m, ifp);
1189a5c0a90fSyamaguchi m->m_flags &= ~M_PROMISC;
1190f0101d0eSyamaguchi if_input(ifp, m);
1191f0101d0eSyamaguchi }
1192f0101d0eSyamaguchi
1193f0101d0eSyamaguchi out:
1194f0101d0eSyamaguchi lagg_port_putref(lp, &psref);
1195d1fb1196Syamaguchi return;
1196f0101d0eSyamaguchi
1197d1fb1196Syamaguchi drop:
1198d1fb1196Syamaguchi lagg_port_putref(lp, &psref);
1199d1fb1196Syamaguchi m_freem(m);
1200d1fb1196Syamaguchi if_statinc(ifp_port, if_iqdrops);
1201d1fb1196Syamaguchi return;
1202f0101d0eSyamaguchi }
1203f0101d0eSyamaguchi
1204f0101d0eSyamaguchi static int
lagg_media_change(struct ifnet * ifp)1205f0101d0eSyamaguchi lagg_media_change(struct ifnet *ifp)
1206f0101d0eSyamaguchi {
1207f0101d0eSyamaguchi
1208f0101d0eSyamaguchi if (ISSET(ifp->if_flags, IFF_DEBUG))
1209f0101d0eSyamaguchi printf("%s: ignore media change\n", ifp->if_xname);
1210f0101d0eSyamaguchi
1211f0101d0eSyamaguchi return 0;
1212f0101d0eSyamaguchi }
1213f0101d0eSyamaguchi
1214f0101d0eSyamaguchi static void
lagg_media_status(struct ifnet * ifp,struct ifmediareq * imr)1215f0101d0eSyamaguchi lagg_media_status(struct ifnet *ifp, struct ifmediareq *imr)
1216f0101d0eSyamaguchi {
1217f0101d0eSyamaguchi struct lagg_softc *sc;
1218f0101d0eSyamaguchi struct lagg_port *lp;
1219f0101d0eSyamaguchi
1220f0101d0eSyamaguchi sc = ifp->if_softc;
1221f0101d0eSyamaguchi
1222f0101d0eSyamaguchi imr->ifm_status = IFM_AVALID;
1223f0101d0eSyamaguchi imr->ifm_active = IFM_ETHER | IFM_AUTO;
1224f0101d0eSyamaguchi
1225f0101d0eSyamaguchi LAGG_LOCK(sc);
122671d2a73aSyamaguchi
122771d2a73aSyamaguchi imr->ifm_active |= sc->sc_media_active;
122871d2a73aSyamaguchi
1229f0101d0eSyamaguchi LAGG_PORTS_FOREACH(sc, lp) {
1230f0101d0eSyamaguchi if (lagg_portactive(lp))
1231f0101d0eSyamaguchi imr->ifm_status |= IFM_ACTIVE;
1232f0101d0eSyamaguchi }
1233f0101d0eSyamaguchi LAGG_UNLOCK(sc);
1234f0101d0eSyamaguchi }
1235f0101d0eSyamaguchi
123671d2a73aSyamaguchi static uint64_t
lagg_search_media_type(uint64_t linkspeed)123771d2a73aSyamaguchi lagg_search_media_type(uint64_t linkspeed)
123871d2a73aSyamaguchi {
123971d2a73aSyamaguchi
124071d2a73aSyamaguchi if (linkspeed == IF_Gbps(40))
124171d2a73aSyamaguchi return IFM_40G_T | IFM_FDX;
124271d2a73aSyamaguchi
124371d2a73aSyamaguchi if (linkspeed == IF_Gbps(25))
124471d2a73aSyamaguchi return IFM_25G_T | IFM_FDX;
124571d2a73aSyamaguchi
124671d2a73aSyamaguchi if (linkspeed == IF_Gbps(10))
124771d2a73aSyamaguchi return IFM_10G_T | IFM_FDX;
124871d2a73aSyamaguchi
124971d2a73aSyamaguchi if (linkspeed == IF_Gbps(5))
125071d2a73aSyamaguchi return IFM_5000_T | IFM_FDX;
125171d2a73aSyamaguchi
125271d2a73aSyamaguchi if (linkspeed == IF_Mbps(2500))
125371d2a73aSyamaguchi return IFM_2500_T | IFM_FDX;
125471d2a73aSyamaguchi
125571d2a73aSyamaguchi if (linkspeed == IF_Gbps(1))
125671d2a73aSyamaguchi return IFM_1000_T | IFM_FDX;
125771d2a73aSyamaguchi
125871d2a73aSyamaguchi if (linkspeed == IF_Mbps(100))
125971d2a73aSyamaguchi return IFM_100_TX | IFM_FDX;
126071d2a73aSyamaguchi
126171d2a73aSyamaguchi if (linkspeed == IF_Mbps(10))
126271d2a73aSyamaguchi return IFM_10_T | IFM_FDX;
126371d2a73aSyamaguchi
126471d2a73aSyamaguchi return 0;
126571d2a73aSyamaguchi }
126671d2a73aSyamaguchi
126771d2a73aSyamaguchi void
lagg_set_linkspeed(struct lagg_softc * sc,uint64_t linkspeed)126871d2a73aSyamaguchi lagg_set_linkspeed(struct lagg_softc *sc, uint64_t linkspeed)
126971d2a73aSyamaguchi {
127071d2a73aSyamaguchi struct ifnet *ifp;
127171d2a73aSyamaguchi
127271d2a73aSyamaguchi ifp = &sc->sc_if;
127371d2a73aSyamaguchi
127471d2a73aSyamaguchi KASSERT(LAGG_LOCKED(sc));
127571d2a73aSyamaguchi
127671d2a73aSyamaguchi ifp->if_baudrate = linkspeed;
127771d2a73aSyamaguchi
127871d2a73aSyamaguchi sc->sc_media_active =
127971d2a73aSyamaguchi lagg_search_media_type(linkspeed);
128071d2a73aSyamaguchi }
128171d2a73aSyamaguchi
1282f0101d0eSyamaguchi static int
lagg_port_vlan_cb(struct lagg_port * lp,struct lagg_vlantag * lvt,bool set)1283f0101d0eSyamaguchi lagg_port_vlan_cb(struct lagg_port *lp,
1284f0101d0eSyamaguchi struct lagg_vlantag *lvt, bool set)
1285f0101d0eSyamaguchi {
1286f0101d0eSyamaguchi struct ifnet *ifp_port;
1287f0101d0eSyamaguchi int error;
1288f0101d0eSyamaguchi
1289afc8fc7cSyamaguchi if (lp->lp_iftype != IFT_ETHER)
1290f0101d0eSyamaguchi return 0;
1291f0101d0eSyamaguchi
1292f0101d0eSyamaguchi error = 0;
1293f0101d0eSyamaguchi ifp_port = lp->lp_ifp;
1294f0101d0eSyamaguchi
1295f0101d0eSyamaguchi if (set) {
12964263242bSyamaguchi error = ether_add_vlantag(ifp_port,
12974263242bSyamaguchi lvt->lvt_vtag, NULL);
1298f0101d0eSyamaguchi } else {
12994263242bSyamaguchi error = ether_del_vlantag(ifp_port,
13004263242bSyamaguchi lvt->lvt_vtag);
1301f0101d0eSyamaguchi }
1302f0101d0eSyamaguchi
1303f0101d0eSyamaguchi return error;
1304f0101d0eSyamaguchi }
1305f0101d0eSyamaguchi
1306f0101d0eSyamaguchi static int
lagg_vlan_cb(struct ethercom * ec,uint16_t vtag,bool set)1307f0101d0eSyamaguchi lagg_vlan_cb(struct ethercom *ec, uint16_t vtag, bool set)
1308f0101d0eSyamaguchi {
1309f0101d0eSyamaguchi struct ifnet *ifp;
1310f0101d0eSyamaguchi struct lagg_softc *sc;
1311f0101d0eSyamaguchi struct lagg_vlantag *lvt, *lvt0;
1312f0101d0eSyamaguchi struct lagg_port *lp;
1313f0101d0eSyamaguchi int error;
1314f0101d0eSyamaguchi
1315f0101d0eSyamaguchi ifp = (struct ifnet *)ec;
1316f0101d0eSyamaguchi sc = ifp->if_softc;
1317f0101d0eSyamaguchi
1318f0101d0eSyamaguchi if (set) {
1319f0101d0eSyamaguchi lvt = kmem_zalloc(sizeof(*lvt), KM_SLEEP);
1320f0101d0eSyamaguchi lvt->lvt_vtag = vtag;
1321f0101d0eSyamaguchi TAILQ_INSERT_TAIL(&sc->sc_vtags, lvt, lvt_entry);
1322f0101d0eSyamaguchi } else {
1323f0101d0eSyamaguchi TAILQ_FOREACH_SAFE(lvt, &sc->sc_vtags, lvt_entry, lvt0) {
1324f0101d0eSyamaguchi if (lvt->lvt_vtag == vtag) {
1325f0101d0eSyamaguchi TAILQ_REMOVE(&sc->sc_vtags, lvt, lvt_entry);
1326f0101d0eSyamaguchi break;
1327f0101d0eSyamaguchi }
1328f0101d0eSyamaguchi }
1329f0101d0eSyamaguchi
1330f0101d0eSyamaguchi if (lvt == NULL)
1331f0101d0eSyamaguchi return ENOENT;
1332f0101d0eSyamaguchi }
1333f0101d0eSyamaguchi
1334f0101d0eSyamaguchi KASSERT(lvt != NULL);
1335f0101d0eSyamaguchi LAGG_PORTS_FOREACH(sc, lp) {
1336f0101d0eSyamaguchi error = lagg_port_vlan_cb(lp, lvt, set);
1337f0101d0eSyamaguchi if (error != 0) {
13387fdf5714Syamaguchi LAGG_LOG(sc, LOG_WARNING,
1339f0101d0eSyamaguchi "%s failed to configure vlan on %d\n",
1340f0101d0eSyamaguchi lp->lp_ifp->if_xname, error);
1341f0101d0eSyamaguchi }
1342f0101d0eSyamaguchi }
1343f0101d0eSyamaguchi
1344f0101d0eSyamaguchi return 0;
1345f0101d0eSyamaguchi }
1346f0101d0eSyamaguchi
1347f0101d0eSyamaguchi static struct lagg_softc *
lagg_softc_alloc(enum lagg_iftypes ift)1348f0101d0eSyamaguchi lagg_softc_alloc(enum lagg_iftypes ift)
1349f0101d0eSyamaguchi {
1350f0101d0eSyamaguchi struct lagg_softc *sc;
1351f0101d0eSyamaguchi size_t s;
1352f0101d0eSyamaguchi
1353f0101d0eSyamaguchi s = lagg_sizeof_softc(ift);
1354f0101d0eSyamaguchi KASSERT(s > 0);
1355f0101d0eSyamaguchi
1356f0101d0eSyamaguchi sc = kmem_zalloc(s, KM_SLEEP);
1357f0101d0eSyamaguchi KASSERT(sc != NULL);
1358f0101d0eSyamaguchi
1359f0101d0eSyamaguchi return sc;
1360f0101d0eSyamaguchi }
1361f0101d0eSyamaguchi
1362f0101d0eSyamaguchi static void
lagg_softc_free(struct lagg_softc * sc)1363f0101d0eSyamaguchi lagg_softc_free(struct lagg_softc *sc)
1364f0101d0eSyamaguchi {
1365f0101d0eSyamaguchi
1366f0101d0eSyamaguchi kmem_free(sc,
1367f0101d0eSyamaguchi lagg_sizeof_softc(sc->sc_iftype));
1368f0101d0eSyamaguchi }
1369f0101d0eSyamaguchi
1370f0101d0eSyamaguchi static void
lagg_variant_update(struct lagg_softc * sc,struct lagg_variant * newvar)1371f0101d0eSyamaguchi lagg_variant_update(struct lagg_softc *sc, struct lagg_variant *newvar)
1372f0101d0eSyamaguchi {
1373f0101d0eSyamaguchi struct lagg_variant *oldvar;
1374f0101d0eSyamaguchi
1375f0101d0eSyamaguchi KASSERT(LAGG_LOCKED(sc));
1376f0101d0eSyamaguchi
1377f0101d0eSyamaguchi psref_target_init(&newvar->lv_psref, lagg_psref_class);
1378f0101d0eSyamaguchi
1379f0101d0eSyamaguchi oldvar = sc->sc_var;
1380f0101d0eSyamaguchi atomic_store_release(&sc->sc_var, newvar);
1381f0101d0eSyamaguchi pserialize_perform(sc->sc_psz);
1382f0101d0eSyamaguchi
1383f0101d0eSyamaguchi if (__predict_true(oldvar != NULL))
1384f0101d0eSyamaguchi psref_target_destroy(&oldvar->lv_psref, lagg_psref_class);
1385f0101d0eSyamaguchi }
1386f0101d0eSyamaguchi
1387f0101d0eSyamaguchi static struct lagg_variant *
lagg_variant_getref(struct lagg_softc * sc,struct psref * psref)1388f0101d0eSyamaguchi lagg_variant_getref(struct lagg_softc *sc, struct psref *psref)
1389f0101d0eSyamaguchi {
1390f0101d0eSyamaguchi struct lagg_variant *var;
1391f0101d0eSyamaguchi int s;
1392f0101d0eSyamaguchi
1393f0101d0eSyamaguchi s = pserialize_read_enter();
1394f0101d0eSyamaguchi var = atomic_load_consume(&sc->sc_var);
1395f0101d0eSyamaguchi if (var == NULL) {
1396f0101d0eSyamaguchi pserialize_read_exit(s);
1397f0101d0eSyamaguchi return NULL;
1398f0101d0eSyamaguchi }
1399f0101d0eSyamaguchi
1400f0101d0eSyamaguchi psref_acquire(psref, &var->lv_psref, lagg_psref_class);
1401f0101d0eSyamaguchi pserialize_read_exit(s);
1402f0101d0eSyamaguchi
1403f0101d0eSyamaguchi return var;
1404f0101d0eSyamaguchi }
1405f0101d0eSyamaguchi
1406f0101d0eSyamaguchi static void
lagg_variant_putref(struct lagg_variant * var,struct psref * psref)1407f0101d0eSyamaguchi lagg_variant_putref(struct lagg_variant *var, struct psref *psref)
1408f0101d0eSyamaguchi {
1409f0101d0eSyamaguchi
1410f0101d0eSyamaguchi if (__predict_false(var == NULL))
1411f0101d0eSyamaguchi return;
1412f0101d0eSyamaguchi psref_release(psref, &var->lv_psref, lagg_psref_class);
1413f0101d0eSyamaguchi }
1414f0101d0eSyamaguchi
1415f0101d0eSyamaguchi static int
lagg_proto_attach(struct lagg_softc * sc,lagg_proto pr,struct lagg_proto_softc ** psc)1416f0101d0eSyamaguchi lagg_proto_attach(struct lagg_softc *sc, lagg_proto pr,
1417f0101d0eSyamaguchi struct lagg_proto_softc **psc)
1418f0101d0eSyamaguchi {
1419f0101d0eSyamaguchi
1420f0101d0eSyamaguchi KASSERT(lagg_protos[pr].pr_attach != NULL);
1421f0101d0eSyamaguchi return lagg_protos[pr].pr_attach(sc, psc);
1422f0101d0eSyamaguchi }
1423f0101d0eSyamaguchi
1424f0101d0eSyamaguchi static void
lagg_proto_detach(struct lagg_variant * oldvar)1425f0101d0eSyamaguchi lagg_proto_detach(struct lagg_variant *oldvar)
1426f0101d0eSyamaguchi {
1427f0101d0eSyamaguchi lagg_proto pr;
1428f0101d0eSyamaguchi
1429f0101d0eSyamaguchi pr = oldvar->lv_proto;
1430f0101d0eSyamaguchi
1431f0101d0eSyamaguchi if (lagg_protos[pr].pr_detach == NULL)
1432f0101d0eSyamaguchi return;
1433f0101d0eSyamaguchi
1434f0101d0eSyamaguchi lagg_protos[pr].pr_detach(oldvar->lv_psc);
1435f0101d0eSyamaguchi }
1436f0101d0eSyamaguchi
1437f0101d0eSyamaguchi static int
lagg_proto_updown(struct lagg_softc * sc,bool is_up)1438f0101d0eSyamaguchi lagg_proto_updown(struct lagg_softc *sc, bool is_up)
1439f0101d0eSyamaguchi {
1440f0101d0eSyamaguchi struct lagg_variant *var;
1441f0101d0eSyamaguchi struct psref psref;
1442f0101d0eSyamaguchi lagg_proto pr;
1443f0101d0eSyamaguchi int error, bound;
1444f0101d0eSyamaguchi
1445f0101d0eSyamaguchi error = 0;
1446f0101d0eSyamaguchi bound = curlwp_bind();
1447f0101d0eSyamaguchi
1448f0101d0eSyamaguchi var = lagg_variant_getref(sc, &psref);
1449f0101d0eSyamaguchi if (var == NULL) {
1450f0101d0eSyamaguchi curlwp_bindx(bound);
1451f0101d0eSyamaguchi return ENXIO;
1452f0101d0eSyamaguchi }
1453f0101d0eSyamaguchi
1454f0101d0eSyamaguchi pr = var->lv_proto;
1455f0101d0eSyamaguchi
14563dc652eeSyamaguchi if (is_up && lagg_protos[pr].pr_up != NULL) {
1457f0101d0eSyamaguchi error = lagg_protos[pr].pr_up(var->lv_psc);
14583dc652eeSyamaguchi } else if (!is_up && lagg_protos[pr].pr_down != NULL) {
1459f0101d0eSyamaguchi lagg_protos[pr].pr_down(var->lv_psc);
1460f0101d0eSyamaguchi }
1461f0101d0eSyamaguchi
1462f0101d0eSyamaguchi lagg_variant_putref(var, &psref);
1463f0101d0eSyamaguchi curlwp_bindx(bound);
1464f0101d0eSyamaguchi
1465f0101d0eSyamaguchi return error;
1466f0101d0eSyamaguchi }
1467f0101d0eSyamaguchi
1468f0101d0eSyamaguchi static int
lagg_proto_up(struct lagg_softc * sc)1469f0101d0eSyamaguchi lagg_proto_up(struct lagg_softc *sc)
1470f0101d0eSyamaguchi {
1471f0101d0eSyamaguchi
1472f0101d0eSyamaguchi return lagg_proto_updown(sc, true);
1473f0101d0eSyamaguchi }
1474f0101d0eSyamaguchi
1475f0101d0eSyamaguchi static void
lagg_proto_down(struct lagg_softc * sc)1476f0101d0eSyamaguchi lagg_proto_down(struct lagg_softc *sc)
1477f0101d0eSyamaguchi {
1478f0101d0eSyamaguchi
1479f0101d0eSyamaguchi (void)lagg_proto_updown(sc, false);
1480f0101d0eSyamaguchi }
1481f0101d0eSyamaguchi
1482f0101d0eSyamaguchi static int
lagg_proto_portctrl(struct lagg_softc * sc,struct lagg_port * lp,enum lagg_portctrl ctrl)1483f0101d0eSyamaguchi lagg_proto_portctrl(struct lagg_softc *sc, struct lagg_port *lp,
1484f0101d0eSyamaguchi enum lagg_portctrl ctrl)
1485f0101d0eSyamaguchi {
1486f0101d0eSyamaguchi struct lagg_variant *var;
1487f0101d0eSyamaguchi struct psref psref;
1488f0101d0eSyamaguchi lagg_proto pr;
1489f0101d0eSyamaguchi int error, bound;
1490f0101d0eSyamaguchi
1491f0101d0eSyamaguchi error = 0;
1492f0101d0eSyamaguchi bound = curlwp_bind();
1493f0101d0eSyamaguchi
1494f0101d0eSyamaguchi var = lagg_variant_getref(sc, &psref);
1495f0101d0eSyamaguchi if (var == NULL) {
1496f0101d0eSyamaguchi curlwp_bindx(bound);
1497f0101d0eSyamaguchi return ENXIO;
1498f0101d0eSyamaguchi }
1499f0101d0eSyamaguchi
1500f0101d0eSyamaguchi pr = var->lv_proto;
1501f0101d0eSyamaguchi
1502f0101d0eSyamaguchi switch (ctrl) {
1503f0101d0eSyamaguchi case LAGG_PORTCTRL_ALLOC:
15043dc652eeSyamaguchi if (lagg_protos[pr].pr_allocport == NULL) {
15053dc652eeSyamaguchi goto nosupport;
15063dc652eeSyamaguchi }
1507f0101d0eSyamaguchi error = lagg_protos[pr].pr_allocport(var->lv_psc, lp);
1508f0101d0eSyamaguchi break;
1509f0101d0eSyamaguchi case LAGG_PORTCTRL_FREE:
15103dc652eeSyamaguchi if (lagg_protos[pr].pr_freeport == NULL) {
15113dc652eeSyamaguchi goto nosupport;
1512f0101d0eSyamaguchi }
15133dc652eeSyamaguchi lagg_protos[pr].pr_freeport(var->lv_psc, lp);
1514f0101d0eSyamaguchi break;
1515f0101d0eSyamaguchi case LAGG_PORTCTRL_START:
15163dc652eeSyamaguchi if (lagg_protos[pr].pr_startport == NULL) {
15173dc652eeSyamaguchi goto nosupport;
1518f0101d0eSyamaguchi }
15193dc652eeSyamaguchi lagg_protos[pr].pr_startport(var->lv_psc, lp);
1520f0101d0eSyamaguchi break;
1521f0101d0eSyamaguchi case LAGG_PORTCTRL_STOP:
15223dc652eeSyamaguchi if (lagg_protos[pr].pr_stopport == NULL) {
15233dc652eeSyamaguchi goto nosupport;
1524f0101d0eSyamaguchi }
15253dc652eeSyamaguchi lagg_protos[pr].pr_stopport(var->lv_psc, lp);
1526f0101d0eSyamaguchi break;
15273dc652eeSyamaguchi default:
15283dc652eeSyamaguchi goto nosupport;
1529f0101d0eSyamaguchi }
1530f0101d0eSyamaguchi
1531f0101d0eSyamaguchi lagg_variant_putref(var, &psref);
1532f0101d0eSyamaguchi curlwp_bindx(bound);
1533f0101d0eSyamaguchi return error;
15343dc652eeSyamaguchi
15353dc652eeSyamaguchi nosupport:
15363dc652eeSyamaguchi lagg_variant_putref(var, &psref);
15373dc652eeSyamaguchi curlwp_bindx(bound);
15383dc652eeSyamaguchi return EPROTONOSUPPORT;
1539f0101d0eSyamaguchi }
1540f0101d0eSyamaguchi
1541f0101d0eSyamaguchi static int
lagg_proto_allocport(struct lagg_softc * sc,struct lagg_port * lp)1542f0101d0eSyamaguchi lagg_proto_allocport(struct lagg_softc *sc, struct lagg_port *lp)
1543f0101d0eSyamaguchi {
1544f0101d0eSyamaguchi
1545f0101d0eSyamaguchi return lagg_proto_portctrl(sc, lp, LAGG_PORTCTRL_ALLOC);
1546f0101d0eSyamaguchi }
1547f0101d0eSyamaguchi
1548f0101d0eSyamaguchi static void
lagg_proto_freeport(struct lagg_softc * sc,struct lagg_port * lp)1549f0101d0eSyamaguchi lagg_proto_freeport(struct lagg_softc *sc, struct lagg_port *lp)
1550f0101d0eSyamaguchi {
1551f0101d0eSyamaguchi
1552f0101d0eSyamaguchi lagg_proto_portctrl(sc, lp, LAGG_PORTCTRL_FREE);
1553f0101d0eSyamaguchi }
1554f0101d0eSyamaguchi
1555f0101d0eSyamaguchi static void
lagg_proto_startport(struct lagg_softc * sc,struct lagg_port * lp)1556f0101d0eSyamaguchi lagg_proto_startport(struct lagg_softc *sc, struct lagg_port *lp)
1557f0101d0eSyamaguchi {
1558f0101d0eSyamaguchi
1559f0101d0eSyamaguchi lagg_proto_portctrl(sc, lp, LAGG_PORTCTRL_START);
1560f0101d0eSyamaguchi }
1561f0101d0eSyamaguchi
1562f0101d0eSyamaguchi static void
lagg_proto_stopport(struct lagg_softc * sc,struct lagg_port * lp)1563f0101d0eSyamaguchi lagg_proto_stopport(struct lagg_softc *sc, struct lagg_port *lp)
1564f0101d0eSyamaguchi {
1565f0101d0eSyamaguchi
1566f0101d0eSyamaguchi lagg_proto_portctrl(sc, lp, LAGG_PORTCTRL_STOP);
1567f0101d0eSyamaguchi }
1568f0101d0eSyamaguchi
1569f0101d0eSyamaguchi static void
lagg_proto_linkstate(struct lagg_softc * sc,struct lagg_port * lp)1570f0101d0eSyamaguchi lagg_proto_linkstate(struct lagg_softc *sc, struct lagg_port *lp)
1571f0101d0eSyamaguchi {
1572f0101d0eSyamaguchi struct lagg_variant *var;
1573f0101d0eSyamaguchi struct psref psref;
1574f0101d0eSyamaguchi lagg_proto pr;
1575f0101d0eSyamaguchi int bound;
1576f0101d0eSyamaguchi
15771efd0671Syamaguchi KASSERT(IFNET_LOCKED(lp->lp_ifp));
15781efd0671Syamaguchi
1579f0101d0eSyamaguchi bound = curlwp_bind();
1580f0101d0eSyamaguchi var = lagg_variant_getref(sc, &psref);
1581f0101d0eSyamaguchi
1582f0101d0eSyamaguchi if (var == NULL) {
1583f0101d0eSyamaguchi curlwp_bindx(bound);
1584f0101d0eSyamaguchi return;
1585f0101d0eSyamaguchi }
1586f0101d0eSyamaguchi
1587f0101d0eSyamaguchi pr = var->lv_proto;
1588f0101d0eSyamaguchi
1589f0101d0eSyamaguchi if (lagg_protos[pr].pr_linkstate)
1590f0101d0eSyamaguchi lagg_protos[pr].pr_linkstate(var->lv_psc, lp);
1591f0101d0eSyamaguchi
1592f0101d0eSyamaguchi lagg_variant_putref(var, &psref);
1593f0101d0eSyamaguchi curlwp_bindx(bound);
1594f0101d0eSyamaguchi }
1595f0101d0eSyamaguchi
1596f0101d0eSyamaguchi static void
lagg_proto_stat(struct lagg_variant * var,struct laggreqproto * resp)1597f0101d0eSyamaguchi lagg_proto_stat(struct lagg_variant *var, struct laggreqproto *resp)
1598f0101d0eSyamaguchi {
1599f0101d0eSyamaguchi lagg_proto pr;
1600f0101d0eSyamaguchi
1601f0101d0eSyamaguchi pr = var->lv_proto;
1602f0101d0eSyamaguchi
1603f0101d0eSyamaguchi if (lagg_protos[pr].pr_protostat != NULL)
1604f0101d0eSyamaguchi lagg_protos[pr].pr_protostat(var->lv_psc, resp);
1605f0101d0eSyamaguchi }
1606f0101d0eSyamaguchi
1607f0101d0eSyamaguchi static void
lagg_proto_portstat(struct lagg_variant * var,struct lagg_port * lp,struct laggreqport * resp)1608f0101d0eSyamaguchi lagg_proto_portstat(struct lagg_variant *var, struct lagg_port *lp,
1609f0101d0eSyamaguchi struct laggreqport *resp)
1610f0101d0eSyamaguchi {
1611f0101d0eSyamaguchi lagg_proto pr;
1612f0101d0eSyamaguchi
1613f0101d0eSyamaguchi pr = var->lv_proto;
1614f0101d0eSyamaguchi
1615f0101d0eSyamaguchi if (lagg_protos[pr].pr_portstat != NULL)
1616f0101d0eSyamaguchi lagg_protos[pr].pr_portstat(var->lv_psc, lp, resp);
1617f0101d0eSyamaguchi }
1618f0101d0eSyamaguchi
1619f0101d0eSyamaguchi static int
lagg_proto_ioctl(struct lagg_softc * sc,struct lagg_req * lreq)1620f0101d0eSyamaguchi lagg_proto_ioctl(struct lagg_softc *sc, struct lagg_req *lreq)
1621f0101d0eSyamaguchi {
1622f0101d0eSyamaguchi struct lagg_variant *var;
1623f0101d0eSyamaguchi struct psref psref;
1624f0101d0eSyamaguchi lagg_proto pr;
1625f0101d0eSyamaguchi int bound, error;
1626f0101d0eSyamaguchi
1627f0101d0eSyamaguchi error = ENOTTY;
1628f0101d0eSyamaguchi bound = curlwp_bind();
1629f0101d0eSyamaguchi var = lagg_variant_getref(sc, &psref);
1630f0101d0eSyamaguchi
1631f0101d0eSyamaguchi if (var == NULL) {
1632f0101d0eSyamaguchi error = ENXIO;
1633f0101d0eSyamaguchi goto done;
1634f0101d0eSyamaguchi }
1635f0101d0eSyamaguchi
1636f0101d0eSyamaguchi pr = var->lv_proto;
1637f0101d0eSyamaguchi if (pr != lreq->lrq_proto) {
1638f0101d0eSyamaguchi error = EBUSY;
1639f0101d0eSyamaguchi goto done;
1640f0101d0eSyamaguchi }
1641f0101d0eSyamaguchi
1642f0101d0eSyamaguchi if (lagg_protos[pr].pr_ioctl != NULL) {
1643f0101d0eSyamaguchi error = lagg_protos[pr].pr_ioctl(var->lv_psc,
1644f0101d0eSyamaguchi &lreq->lrq_reqproto);
1645f0101d0eSyamaguchi }
1646f0101d0eSyamaguchi
1647f0101d0eSyamaguchi done:
1648f0101d0eSyamaguchi if (var != NULL)
1649f0101d0eSyamaguchi lagg_variant_putref(var, &psref);
1650f0101d0eSyamaguchi curlwp_bindx(bound);
1651f0101d0eSyamaguchi return error;
1652f0101d0eSyamaguchi }
1653f0101d0eSyamaguchi
1654f0101d0eSyamaguchi static int
lagg_pr_attach(struct lagg_softc * sc,lagg_proto pr)1655f0101d0eSyamaguchi lagg_pr_attach(struct lagg_softc *sc, lagg_proto pr)
1656f0101d0eSyamaguchi {
1657f0101d0eSyamaguchi struct lagg_variant *newvar, *oldvar;
1658f0101d0eSyamaguchi struct lagg_proto_softc *psc;
1659f0101d0eSyamaguchi int error;
1660f0101d0eSyamaguchi
1661f0101d0eSyamaguchi error = 0;
1662f0101d0eSyamaguchi newvar = kmem_alloc(sizeof(*newvar), KM_SLEEP);
1663f0101d0eSyamaguchi
1664f0101d0eSyamaguchi LAGG_LOCK(sc);
1665f0101d0eSyamaguchi oldvar = sc->sc_var;
1666f0101d0eSyamaguchi
1667f0101d0eSyamaguchi if (oldvar != NULL && oldvar->lv_proto == pr) {
1668f0101d0eSyamaguchi error = 0;
16692e03b97eSyamaguchi goto failed;
1670f0101d0eSyamaguchi }
1671f0101d0eSyamaguchi
1672f0101d0eSyamaguchi error = lagg_proto_attach(sc, pr, &psc);
1673f0101d0eSyamaguchi if (error != 0)
16742e03b97eSyamaguchi goto failed;
1675f0101d0eSyamaguchi
1676f0101d0eSyamaguchi newvar->lv_proto = pr;
1677f0101d0eSyamaguchi newvar->lv_psc = psc;
1678f0101d0eSyamaguchi lagg_variant_update(sc, newvar);
16792e03b97eSyamaguchi lagg_set_linkspeed(sc, 0);
16802e03b97eSyamaguchi LAGG_UNLOCK(sc);
1681f0101d0eSyamaguchi
1682f0101d0eSyamaguchi if (oldvar != NULL) {
1683f0101d0eSyamaguchi lagg_proto_detach(oldvar);
16842e03b97eSyamaguchi kmem_free(oldvar, sizeof(*oldvar));
1685f0101d0eSyamaguchi }
168671d2a73aSyamaguchi
16872e03b97eSyamaguchi return 0;
1688f0101d0eSyamaguchi
16892e03b97eSyamaguchi failed:
169048828ff9Syamaguchi LAGG_UNLOCK(sc);
1691f0101d0eSyamaguchi kmem_free(newvar, sizeof(*newvar));
1692f0101d0eSyamaguchi
1693f0101d0eSyamaguchi return error;
1694f0101d0eSyamaguchi }
1695f0101d0eSyamaguchi
1696f0101d0eSyamaguchi static void
lagg_pr_detach(struct lagg_softc * sc)1697f0101d0eSyamaguchi lagg_pr_detach(struct lagg_softc *sc)
1698f0101d0eSyamaguchi {
1699f0101d0eSyamaguchi struct lagg_variant *var;
1700f0101d0eSyamaguchi
1701f0101d0eSyamaguchi LAGG_LOCK(sc);
1702f0101d0eSyamaguchi var = sc->sc_var;
1703f0101d0eSyamaguchi atomic_store_release(&sc->sc_var, NULL);
17042e03b97eSyamaguchi LAGG_UNLOCK(sc);
170548a2a44cSyamaguchi pserialize_perform(sc->sc_psz);
1706f0101d0eSyamaguchi
1707f0101d0eSyamaguchi if (var != NULL)
1708f0101d0eSyamaguchi lagg_proto_detach(var);
1709f0101d0eSyamaguchi
1710f0101d0eSyamaguchi
1711f0101d0eSyamaguchi if (var != NULL)
1712f0101d0eSyamaguchi kmem_free(var, sizeof(*var));
1713f0101d0eSyamaguchi }
1714f0101d0eSyamaguchi
1715f0101d0eSyamaguchi static int
lagg_ether_addmulti(struct lagg_softc * sc,struct ifreq * ifr)1716f0101d0eSyamaguchi lagg_ether_addmulti(struct lagg_softc *sc, struct ifreq *ifr)
1717f0101d0eSyamaguchi {
1718f0101d0eSyamaguchi struct lagg_port *lp;
1719f0101d0eSyamaguchi struct lagg_mc_entry *mc;
1720f0101d0eSyamaguchi struct ethercom *ec;
1721f0101d0eSyamaguchi const struct sockaddr *sa;
1722f0101d0eSyamaguchi uint8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
1723f0101d0eSyamaguchi int error;
1724f0101d0eSyamaguchi
1725f0101d0eSyamaguchi if (sc->sc_if.if_type != IFT_ETHER)
1726f0101d0eSyamaguchi return EPROTONOSUPPORT;
1727f0101d0eSyamaguchi
1728f0101d0eSyamaguchi ec = (struct ethercom *)&sc->sc_if;
1729f0101d0eSyamaguchi sa = ifreq_getaddr(SIOCADDMULTI, ifr);
1730f0101d0eSyamaguchi
1731f0101d0eSyamaguchi error = ether_addmulti(sa, ec);
1732f0101d0eSyamaguchi if (error != ENETRESET)
1733f0101d0eSyamaguchi return error;
1734f0101d0eSyamaguchi
1735f0101d0eSyamaguchi error = ether_multiaddr(sa, addrlo, addrhi);
1736f0101d0eSyamaguchi KASSERT(error == 0);
1737f0101d0eSyamaguchi
1738f0101d0eSyamaguchi mc = kmem_zalloc(sizeof(*mc), KM_SLEEP);
1739f0101d0eSyamaguchi
1740f0101d0eSyamaguchi ETHER_LOCK(ec);
1741f0101d0eSyamaguchi mc->mc_enm = ether_lookup_multi(addrlo, addrhi, ec);
1742f0101d0eSyamaguchi ETHER_UNLOCK(ec);
1743f0101d0eSyamaguchi
1744f0101d0eSyamaguchi KASSERT(mc->mc_enm != NULL);
1745f0101d0eSyamaguchi
1746f0101d0eSyamaguchi LAGG_LOCK(sc);
1747f0101d0eSyamaguchi LAGG_PORTS_FOREACH(sc, lp) {
1748f0101d0eSyamaguchi (void)lagg_lp_ioctl(lp, SIOCADDMULTI, (void *)ifr);
1749f0101d0eSyamaguchi }
1750f0101d0eSyamaguchi LAGG_UNLOCK(sc);
1751f0101d0eSyamaguchi
1752f0101d0eSyamaguchi KASSERT(sa->sa_len <= sizeof(mc->mc_addr));
1753f0101d0eSyamaguchi memcpy(&mc->mc_addr, sa, sa->sa_len);
1754f0101d0eSyamaguchi LIST_INSERT_HEAD(&sc->sc_mclist, mc, mc_entry);
1755f0101d0eSyamaguchi
1756f0101d0eSyamaguchi return 0;
1757f0101d0eSyamaguchi }
1758f0101d0eSyamaguchi
1759f0101d0eSyamaguchi static int
lagg_ether_delmulti(struct lagg_softc * sc,struct ifreq * ifr)1760f0101d0eSyamaguchi lagg_ether_delmulti(struct lagg_softc *sc, struct ifreq *ifr)
1761f0101d0eSyamaguchi {
1762f0101d0eSyamaguchi struct lagg_port *lp;
1763f0101d0eSyamaguchi struct lagg_mc_entry *mc;
1764f0101d0eSyamaguchi const struct sockaddr *sa;
1765f0101d0eSyamaguchi struct ethercom *ec;
1766f0101d0eSyamaguchi struct ether_multi *enm;
1767f0101d0eSyamaguchi uint8_t addrlo[ETHER_ADDR_LEN], addrhi[ETHER_ADDR_LEN];
1768f0101d0eSyamaguchi int error;
1769f0101d0eSyamaguchi
1770f0101d0eSyamaguchi ec = (struct ethercom *)&sc->sc_if;
1771f0101d0eSyamaguchi sa = ifreq_getaddr(SIOCDELMULTI, ifr);
1772f0101d0eSyamaguchi error = ether_multiaddr(sa, addrlo, addrhi);
1773f0101d0eSyamaguchi if (error != 0)
1774f0101d0eSyamaguchi return error;
1775f0101d0eSyamaguchi
1776f0101d0eSyamaguchi ETHER_LOCK(ec);
1777f0101d0eSyamaguchi enm = ether_lookup_multi(addrlo, addrhi, ec);
1778f0101d0eSyamaguchi ETHER_UNLOCK(ec);
1779f0101d0eSyamaguchi
1780f0101d0eSyamaguchi if (enm == NULL)
1781f0101d0eSyamaguchi return ENOENT;
1782f0101d0eSyamaguchi
1783f0101d0eSyamaguchi LIST_FOREACH(mc, &sc->sc_mclist, mc_entry) {
1784f0101d0eSyamaguchi if (mc->mc_enm == enm)
1785f0101d0eSyamaguchi break;
1786f0101d0eSyamaguchi }
1787f0101d0eSyamaguchi
1788f0101d0eSyamaguchi if (mc == NULL)
1789f0101d0eSyamaguchi return ENOENT;
1790f0101d0eSyamaguchi
1791f0101d0eSyamaguchi error = ether_delmulti(sa, ec);
1792f0101d0eSyamaguchi if (error != ENETRESET)
1793f0101d0eSyamaguchi return error;
1794f0101d0eSyamaguchi
1795f0101d0eSyamaguchi LAGG_LOCK(sc);
1796f0101d0eSyamaguchi LAGG_PORTS_FOREACH(sc, lp) {
1797f0101d0eSyamaguchi (void)lagg_lp_ioctl(lp, SIOCDELMULTI, (void *)ifr);
1798f0101d0eSyamaguchi }
1799f0101d0eSyamaguchi LAGG_UNLOCK(sc);
1800f0101d0eSyamaguchi
1801f0101d0eSyamaguchi LIST_REMOVE(mc, mc_entry);
1802f0101d0eSyamaguchi kmem_free(mc, sizeof(*mc));
1803f0101d0eSyamaguchi
1804f0101d0eSyamaguchi return 0;
1805f0101d0eSyamaguchi }
1806f0101d0eSyamaguchi
1807f0101d0eSyamaguchi static void
lagg_port_multi(struct lagg_softc * sc,struct lagg_port * lp,u_long cmd)1808f0101d0eSyamaguchi lagg_port_multi(struct lagg_softc *sc, struct lagg_port *lp,
1809f0101d0eSyamaguchi u_long cmd)
1810f0101d0eSyamaguchi {
1811f0101d0eSyamaguchi struct lagg_mc_entry *mc;
1812f0101d0eSyamaguchi struct ifreq ifr;
1813f0101d0eSyamaguchi struct ifnet *ifp_port;
1814f0101d0eSyamaguchi const struct sockaddr *sa;
1815f0101d0eSyamaguchi
1816f0101d0eSyamaguchi ifp_port = lp->lp_ifp;
1817f0101d0eSyamaguchi
1818f0101d0eSyamaguchi memset(&ifr, 0, sizeof(ifr));
1819f0101d0eSyamaguchi strlcpy(ifr.ifr_name, ifp_port->if_xname, sizeof(ifr.ifr_name));
1820f0101d0eSyamaguchi
1821f0101d0eSyamaguchi LIST_FOREACH(mc, &sc->sc_mclist, mc_entry) {
1822f0101d0eSyamaguchi sa = (struct sockaddr *)&mc->mc_addr;
1823f0101d0eSyamaguchi KASSERT(sizeof(ifr.ifr_space) >= sa->sa_len);
1824f0101d0eSyamaguchi memcpy(&ifr.ifr_addr, sa, sa->sa_len);
1825f0101d0eSyamaguchi (void)lagg_lp_ioctl(lp, cmd, (void *)&ifr);
1826f0101d0eSyamaguchi }
1827f0101d0eSyamaguchi
1828f0101d0eSyamaguchi }
1829f0101d0eSyamaguchi
1830f0101d0eSyamaguchi static void
lagg_port_syncmulti(struct lagg_softc * sc,struct lagg_port * lp)1831f0101d0eSyamaguchi lagg_port_syncmulti(struct lagg_softc *sc, struct lagg_port *lp)
1832f0101d0eSyamaguchi {
1833f0101d0eSyamaguchi
1834f0101d0eSyamaguchi lagg_port_multi(sc, lp, SIOCADDMULTI);
1835f0101d0eSyamaguchi }
1836f0101d0eSyamaguchi
1837f0101d0eSyamaguchi static void
lagg_port_purgemulti(struct lagg_softc * sc,struct lagg_port * lp)1838f0101d0eSyamaguchi lagg_port_purgemulti(struct lagg_softc *sc, struct lagg_port *lp)
1839f0101d0eSyamaguchi {
1840f0101d0eSyamaguchi
1841f0101d0eSyamaguchi lagg_port_multi(sc, lp, SIOCDELMULTI);
1842f0101d0eSyamaguchi }
1843f0101d0eSyamaguchi
1844f0101d0eSyamaguchi static void
lagg_port_vlan(struct lagg_softc * sc,struct lagg_port * lp,bool set)1845f0101d0eSyamaguchi lagg_port_vlan(struct lagg_softc *sc, struct lagg_port *lp,
1846f0101d0eSyamaguchi bool set)
1847f0101d0eSyamaguchi {
1848f0101d0eSyamaguchi struct lagg_vlantag *lvt;
1849f0101d0eSyamaguchi int error;
1850f0101d0eSyamaguchi
1851f0101d0eSyamaguchi TAILQ_FOREACH(lvt, &sc->sc_vtags, lvt_entry) {
1852f0101d0eSyamaguchi error = lagg_port_vlan_cb(lp, lvt, set);
1853f0101d0eSyamaguchi if (error != 0) {
18547fdf5714Syamaguchi LAGG_LOG(sc, LOG_WARNING,
1855f0101d0eSyamaguchi "%s failed to configure vlan on %d\n",
1856f0101d0eSyamaguchi lp->lp_ifp->if_xname, error);
1857f0101d0eSyamaguchi }
1858f0101d0eSyamaguchi }
1859f0101d0eSyamaguchi }
1860f0101d0eSyamaguchi
1861f0101d0eSyamaguchi static void
lagg_port_syncvlan(struct lagg_softc * sc,struct lagg_port * lp)1862f0101d0eSyamaguchi lagg_port_syncvlan(struct lagg_softc *sc, struct lagg_port *lp)
1863f0101d0eSyamaguchi
1864f0101d0eSyamaguchi {
1865f0101d0eSyamaguchi lagg_port_vlan(sc, lp, true);
1866f0101d0eSyamaguchi }
1867f0101d0eSyamaguchi
1868f0101d0eSyamaguchi static void
lagg_port_purgevlan(struct lagg_softc * sc,struct lagg_port * lp)1869f0101d0eSyamaguchi lagg_port_purgevlan(struct lagg_softc *sc, struct lagg_port *lp)
1870f0101d0eSyamaguchi {
1871f0101d0eSyamaguchi
1872f0101d0eSyamaguchi lagg_port_vlan(sc, lp, false);
1873f0101d0eSyamaguchi }
1874f0101d0eSyamaguchi
1875f0101d0eSyamaguchi static int
lagg_setifcaps(struct lagg_port * lp,uint64_t cap)187680bf8caaSyamaguchi lagg_setifcaps(struct lagg_port *lp, uint64_t cap)
187780bf8caaSyamaguchi {
187880bf8caaSyamaguchi struct ifcapreq ifcr;
187980bf8caaSyamaguchi int error;
188080bf8caaSyamaguchi
188180bf8caaSyamaguchi if (lp->lp_ifp->if_capenable == cap)
188280bf8caaSyamaguchi return 0;
188380bf8caaSyamaguchi
188480bf8caaSyamaguchi memset(&ifcr, 0, sizeof(ifcr));
188580bf8caaSyamaguchi ifcr.ifcr_capenable = cap;
188680bf8caaSyamaguchi
188780bf8caaSyamaguchi IFNET_LOCK(lp->lp_ifp);
188880bf8caaSyamaguchi error = LAGG_PORT_IOCTL(lp, SIOCSIFCAP, &ifcr);
188980bf8caaSyamaguchi IFNET_UNLOCK(lp->lp_ifp);
189080bf8caaSyamaguchi
189180bf8caaSyamaguchi return error;
189280bf8caaSyamaguchi }
189380bf8caaSyamaguchi
1894bcfe44c5Syamaguchi static void
lagg_sync_ifcaps(struct lagg_softc * sc)1895bcfe44c5Syamaguchi lagg_sync_ifcaps(struct lagg_softc *sc)
1896bcfe44c5Syamaguchi {
1897bcfe44c5Syamaguchi struct lagg_port *lp;
1898bcfe44c5Syamaguchi struct ifnet *ifp;
1899bcfe44c5Syamaguchi int error = 0;
1900bcfe44c5Syamaguchi
1901bcfe44c5Syamaguchi ifp = (struct ifnet *)&sc->sc_if;
1902bcfe44c5Syamaguchi
1903bcfe44c5Syamaguchi LAGG_LOCK(sc);
1904bcfe44c5Syamaguchi LAGG_PORTS_FOREACH(sc, lp) {
1905bcfe44c5Syamaguchi error = lagg_setifcaps(lp, ifp->if_capenable);
1906bcfe44c5Syamaguchi
1907bcfe44c5Syamaguchi if (error != 0) {
19087fdf5714Syamaguchi LAGG_LOG(sc, LOG_WARNING,
1909bcfe44c5Syamaguchi "failed to update capabilities "
19107fdf5714Syamaguchi "of %s, error=%d\n",
1911bcfe44c5Syamaguchi lp->lp_ifp->if_xname, error);
1912bcfe44c5Syamaguchi }
1913bcfe44c5Syamaguchi }
1914bcfe44c5Syamaguchi LAGG_UNLOCK(sc);
1915bcfe44c5Syamaguchi }
1916bcfe44c5Syamaguchi
191780bf8caaSyamaguchi static int
lagg_setethcaps(struct lagg_port * lp,int cap)191880bf8caaSyamaguchi lagg_setethcaps(struct lagg_port *lp, int cap)
191980bf8caaSyamaguchi {
192080bf8caaSyamaguchi struct ethercom *ec;
192180bf8caaSyamaguchi struct eccapreq eccr;
192280bf8caaSyamaguchi int error;
192380bf8caaSyamaguchi
192480bf8caaSyamaguchi KASSERT(lp->lp_iftype == IFT_ETHER);
192580bf8caaSyamaguchi ec = (struct ethercom *)lp->lp_ifp;
192680bf8caaSyamaguchi
192780bf8caaSyamaguchi if (ec->ec_capenable == cap)
192880bf8caaSyamaguchi return 0;
192980bf8caaSyamaguchi
193080bf8caaSyamaguchi memset(&eccr, 0, sizeof(eccr));
193180bf8caaSyamaguchi eccr.eccr_capenable = cap;
193280bf8caaSyamaguchi
193380bf8caaSyamaguchi IFNET_LOCK(lp->lp_ifp);
193480bf8caaSyamaguchi error = LAGG_PORT_IOCTL(lp, SIOCSETHERCAP, &eccr);
193580bf8caaSyamaguchi IFNET_UNLOCK(lp->lp_ifp);
193680bf8caaSyamaguchi
193780bf8caaSyamaguchi return error;
193880bf8caaSyamaguchi }
193980bf8caaSyamaguchi
194080bf8caaSyamaguchi static void
lagg_sync_ethcaps(struct lagg_softc * sc)1941bcfe44c5Syamaguchi lagg_sync_ethcaps(struct lagg_softc *sc)
1942bcfe44c5Syamaguchi {
1943bcfe44c5Syamaguchi struct ethercom *ec;
1944bcfe44c5Syamaguchi struct lagg_port *lp;
1945bcfe44c5Syamaguchi int error;
1946bcfe44c5Syamaguchi
1947bcfe44c5Syamaguchi ec = (struct ethercom *)&sc->sc_if;
1948bcfe44c5Syamaguchi
1949bcfe44c5Syamaguchi LAGG_LOCK(sc);
1950bcfe44c5Syamaguchi LAGG_PORTS_FOREACH(sc, lp) {
1951bcfe44c5Syamaguchi if (lp->lp_iftype != IFT_ETHER)
1952bcfe44c5Syamaguchi continue;
1953bcfe44c5Syamaguchi
1954bcfe44c5Syamaguchi error = lagg_setethcaps(lp, ec->ec_capenable);
1955bcfe44c5Syamaguchi if (error != 0) {
19567fdf5714Syamaguchi LAGG_LOG(sc, LOG_WARNING,
1957bcfe44c5Syamaguchi "failed to update ether "
19587fdf5714Syamaguchi "capabilities"" of %s, error=%d\n",
1959bcfe44c5Syamaguchi lp->lp_ifp->if_xname, error);
1960bcfe44c5Syamaguchi }
1961bcfe44c5Syamaguchi
1962bcfe44c5Syamaguchi }
1963bcfe44c5Syamaguchi LAGG_UNLOCK(sc);
1964bcfe44c5Syamaguchi }
1965bcfe44c5Syamaguchi
1966bcfe44c5Syamaguchi static void
lagg_ifcap_update(struct lagg_softc * sc)196780bf8caaSyamaguchi lagg_ifcap_update(struct lagg_softc *sc)
196880bf8caaSyamaguchi {
196980bf8caaSyamaguchi struct ifnet *ifp;
197080bf8caaSyamaguchi struct lagg_port *lp;
197180bf8caaSyamaguchi uint64_t cap, ena, pena;
197280bf8caaSyamaguchi size_t i;
197380bf8caaSyamaguchi
197480bf8caaSyamaguchi KASSERT(LAGG_LOCKED(sc));
197580bf8caaSyamaguchi
197680bf8caaSyamaguchi /* Get common capabilities for the lagg ports */
197780bf8caaSyamaguchi ena = ~(uint64_t)0;
197880bf8caaSyamaguchi cap = ~(uint64_t)0;
197980bf8caaSyamaguchi LAGG_PORTS_FOREACH(sc, lp) {
198080bf8caaSyamaguchi ena &= lp->lp_ifp->if_capenable;
198180bf8caaSyamaguchi cap &= lp->lp_ifp->if_capabilities;
198280bf8caaSyamaguchi }
198380bf8caaSyamaguchi
198480bf8caaSyamaguchi if (ena == ~(uint64_t)0)
198580bf8caaSyamaguchi ena = 0;
198680bf8caaSyamaguchi if (cap == ~(uint64_t)0)
198780bf8caaSyamaguchi cap = 0;
198880bf8caaSyamaguchi
198980bf8caaSyamaguchi /*
199080bf8caaSyamaguchi * Apply common enabled capabilities back to the lagg ports.
199180bf8caaSyamaguchi * May require several iterations if they are dependent.
199280bf8caaSyamaguchi */
199380bf8caaSyamaguchi for (i = 0; i < LAGG_SETCAPS_RETRY; i++) {
199480bf8caaSyamaguchi pena = ena;
199580bf8caaSyamaguchi LAGG_PORTS_FOREACH(sc, lp) {
199680bf8caaSyamaguchi lagg_setifcaps(lp, ena);
199780bf8caaSyamaguchi ena &= lp->lp_ifp->if_capenable;
199880bf8caaSyamaguchi }
199980bf8caaSyamaguchi
200080bf8caaSyamaguchi if (pena == ena)
200180bf8caaSyamaguchi break;
200280bf8caaSyamaguchi }
200380bf8caaSyamaguchi
200480bf8caaSyamaguchi if (pena != ena) {
20057fdf5714Syamaguchi LAGG_LOG(sc, LOG_DEBUG, "couldn't set "
20067fdf5714Syamaguchi "capabilities 0x%08"PRIx64"\n", pena);
200780bf8caaSyamaguchi }
200880bf8caaSyamaguchi
200980bf8caaSyamaguchi ifp = &sc->sc_if;
201080bf8caaSyamaguchi
201180bf8caaSyamaguchi if (ifp->if_capabilities != cap ||
201280bf8caaSyamaguchi ifp->if_capenable != ena) {
201380bf8caaSyamaguchi ifp->if_capabilities = cap;
201480bf8caaSyamaguchi ifp->if_capenable = ena;
201580bf8caaSyamaguchi
20167fdf5714Syamaguchi LAGG_LOG(sc, LOG_DEBUG,"capabilities "
20177fdf5714Syamaguchi "0x%08"PRIx64" enabled 0x%08"PRIx64"\n",
201880bf8caaSyamaguchi cap, ena);
201980bf8caaSyamaguchi }
202080bf8caaSyamaguchi }
202180bf8caaSyamaguchi
202280bf8caaSyamaguchi static void
lagg_ethercap_update(struct lagg_softc * sc)202380bf8caaSyamaguchi lagg_ethercap_update(struct lagg_softc *sc)
202480bf8caaSyamaguchi {
202580bf8caaSyamaguchi struct ethercom *ec;
202680bf8caaSyamaguchi struct lagg_port *lp;
202780bf8caaSyamaguchi int cap, ena, pena;
202880bf8caaSyamaguchi size_t i;
202980bf8caaSyamaguchi
203080bf8caaSyamaguchi KASSERT(LAGG_LOCKED(sc));
203180bf8caaSyamaguchi
203280bf8caaSyamaguchi if (sc->sc_if.if_type != IFT_ETHER)
203380bf8caaSyamaguchi return;
203480bf8caaSyamaguchi
20356e31a1b0Syamaguchi if (SIMPLEQ_EMPTY(&sc->sc_ports)) {
20366e31a1b0Syamaguchi ena = 0;
20376e31a1b0Syamaguchi cap = ETHERCAP_VLAN_HWTAGGING;
20386e31a1b0Syamaguchi } else {
203980bf8caaSyamaguchi /* Get common enabled capabilities for the lagg ports */
204080bf8caaSyamaguchi ena = ~0;
204180bf8caaSyamaguchi cap = ~0;
204280bf8caaSyamaguchi LAGG_PORTS_FOREACH(sc, lp) {
2043366fe416Syamaguchi switch (lp->lp_iftype) {
2044366fe416Syamaguchi case IFT_ETHER:
204580bf8caaSyamaguchi ec = (struct ethercom *)lp->lp_ifp;
204680bf8caaSyamaguchi ena &= ec->ec_capenable;
204780bf8caaSyamaguchi cap &= ec->ec_capabilities;
2048366fe416Syamaguchi break;
2049366fe416Syamaguchi case IFT_L2TP:
2050366fe416Syamaguchi ena &= (ETHERCAP_VLAN_MTU | ETHERCAP_JUMBO_MTU);
2051366fe416Syamaguchi cap &= (ETHERCAP_VLAN_MTU | ETHERCAP_JUMBO_MTU);
2052366fe416Syamaguchi break;
2053366fe416Syamaguchi default:
205480bf8caaSyamaguchi ena = 0;
205580bf8caaSyamaguchi cap = 0;
205680bf8caaSyamaguchi }
205780bf8caaSyamaguchi }
20586e31a1b0Syamaguchi }
205980bf8caaSyamaguchi
206080bf8caaSyamaguchi /*
206180bf8caaSyamaguchi * Apply common enabled capabilities back to the lagg ports.
206280bf8caaSyamaguchi * May require several iterations if they are dependent.
206380bf8caaSyamaguchi */
206480bf8caaSyamaguchi for (i = 0; i < LAGG_SETCAPS_RETRY; i++) {
206580bf8caaSyamaguchi pena = ena;
206680bf8caaSyamaguchi LAGG_PORTS_FOREACH(sc, lp) {
206780bf8caaSyamaguchi if (lp->lp_iftype != IFT_ETHER)
206880bf8caaSyamaguchi continue;
206980bf8caaSyamaguchi
207080bf8caaSyamaguchi ec = (struct ethercom *)lp->lp_ifp;
207180bf8caaSyamaguchi lagg_setethcaps(lp, ena);
207280bf8caaSyamaguchi ena &= ec->ec_capenable;
207380bf8caaSyamaguchi }
207480bf8caaSyamaguchi
207580bf8caaSyamaguchi if (pena == ena)
207680bf8caaSyamaguchi break;
207780bf8caaSyamaguchi }
207880bf8caaSyamaguchi
207980bf8caaSyamaguchi if (pena != ena) {
20807fdf5714Syamaguchi LAGG_LOG(sc, LOG_DEBUG, "couldn't set "
20817fdf5714Syamaguchi "ether capabilities 0x%08x\n", pena);
208280bf8caaSyamaguchi }
208380bf8caaSyamaguchi
208480bf8caaSyamaguchi ec = (struct ethercom *)&sc->sc_if;
208580bf8caaSyamaguchi
208680bf8caaSyamaguchi if (ec->ec_capabilities != cap ||
208780bf8caaSyamaguchi ec->ec_capenable != ena) {
208880bf8caaSyamaguchi ec->ec_capabilities = cap;
208980bf8caaSyamaguchi ec->ec_capenable = ena;
209080bf8caaSyamaguchi
20917fdf5714Syamaguchi LAGG_LOG(sc, LOG_DEBUG,
209280bf8caaSyamaguchi "ether capabilities 0x%08x"
20937fdf5714Syamaguchi " enabled 0x%08x\n", cap, ena);
209480bf8caaSyamaguchi }
209580bf8caaSyamaguchi }
209680bf8caaSyamaguchi
209780bf8caaSyamaguchi static void
lagg_capabilities_update(struct lagg_softc * sc)209880bf8caaSyamaguchi lagg_capabilities_update(struct lagg_softc *sc)
209980bf8caaSyamaguchi {
210080bf8caaSyamaguchi
210180bf8caaSyamaguchi lagg_ifcap_update(sc);
210280bf8caaSyamaguchi lagg_ethercap_update(sc);
210380bf8caaSyamaguchi }
210480bf8caaSyamaguchi
210580bf8caaSyamaguchi static int
lagg_setmtu(struct ifnet * ifp,uint64_t mtu)21065cfb8022Syamaguchi lagg_setmtu(struct ifnet *ifp, uint64_t mtu)
2107f0101d0eSyamaguchi {
21085cfb8022Syamaguchi struct lagg_softc *sc __LAGGDEBUGUSED;
21095cfb8022Syamaguchi struct lagg_port *lp;
2110f0101d0eSyamaguchi struct ifreq ifr;
2111f0101d0eSyamaguchi int error;
2112f0101d0eSyamaguchi
21131e945cefSyamaguchi KASSERT(IFNET_LOCKED(ifp));
21145cfb8022Syamaguchi
21155cfb8022Syamaguchi memset(&ifr, 0, sizeof(ifr));
21165cfb8022Syamaguchi ifr.ifr_mtu = mtu;
21175cfb8022Syamaguchi lp = ifp->if_lagg;
21185cfb8022Syamaguchi
21195cfb8022Syamaguchi if (lp != NULL) {
21205cfb8022Syamaguchi /* ioctl for port interface */
21215cfb8022Syamaguchi error = lp->lp_ioctl(ifp, SIOCSIFMTU, &ifr);
21225cfb8022Syamaguchi sc = lp->lp_softc;
212374194586Syamaguchi } else {
21245cfb8022Syamaguchi /* ioctl for lagg interface */
21255cfb8022Syamaguchi error = ether_ioctl(ifp, SIOCSIFMTU, &ifr);
21265cfb8022Syamaguchi sc = ifp->if_softc;
212774194586Syamaguchi }
212874194586Syamaguchi
212974194586Syamaguchi if (error != 0) {
21301e945cefSyamaguchi LAGG_DPRINTF(sc,
21315cfb8022Syamaguchi "couldn't change MTU for %s\n",
21325cfb8022Syamaguchi ifp->if_xname);
213374194586Syamaguchi }
213474194586Syamaguchi
21351e945cefSyamaguchi return error;
213674194586Syamaguchi }
213774194586Syamaguchi
213874194586Syamaguchi static void
lagg_port_setsadl(struct lagg_port * lp,const uint8_t * lladdr)21395cfb8022Syamaguchi lagg_port_setsadl(struct lagg_port *lp, const uint8_t *lladdr)
214074194586Syamaguchi {
214174194586Syamaguchi struct ifnet *ifp_port;
214274194586Syamaguchi int error;
214374194586Syamaguchi
214474194586Syamaguchi ifp_port = lp->lp_ifp;
214574194586Syamaguchi
214674194586Syamaguchi KASSERT(LAGG_LOCKED(lp->lp_softc));
214774194586Syamaguchi KASSERT(IFNET_LOCKED(ifp_port));
214874194586Syamaguchi
2149a5c0a90fSyamaguchi switch (lp->lp_iftype) {
2150a5c0a90fSyamaguchi case IFT_ETHER:
21515cfb8022Syamaguchi if (lladdr == NULL) {
21525cfb8022Syamaguchi lladdr = lp->lp_lladdr;
21535cfb8022Syamaguchi } else {
21545cfb8022Syamaguchi if (lagg_lladdr_equal(lladdr,
21555cfb8022Syamaguchi CLLADDR(ifp_port->if_sadl)))
2156a5c0a90fSyamaguchi break;
215774194586Syamaguchi }
215874194586Syamaguchi
21591875049cSyamaguchi lagg_chg_sadl(ifp_port,
21601875049cSyamaguchi lladdr, ETHER_ADDR_LEN);
2161a5c0a90fSyamaguchi
2162da8dae57Syamaguchi if (ifp_port->if_init != NULL) {
2163da8dae57Syamaguchi error = 0;
21644579aeafSyamaguchi /* Apply updated ifp_port->if_sadl to the device */
2165da8dae57Syamaguchi if (ISSET(ifp_port->if_flags, IFF_RUNNING))
2166b4d088cbSriastradh error = if_init(ifp_port);
2167da8dae57Syamaguchi
216874194586Syamaguchi if (error != 0) {
21697fdf5714Syamaguchi LAGG_LOG(lp->lp_softc, LOG_WARNING,
217074194586Syamaguchi "%s failed to if_init() on %d\n",
217174194586Syamaguchi ifp_port->if_xname, error);
217274194586Syamaguchi }
2173da8dae57Syamaguchi }
2174a5c0a90fSyamaguchi break;
2175a5c0a90fSyamaguchi default:
2176a5c0a90fSyamaguchi if_alloc_sadl(ifp_port);
2177a5c0a90fSyamaguchi break;
2178a5c0a90fSyamaguchi }
2179a5c0a90fSyamaguchi }
2180a5c0a90fSyamaguchi
2181a5c0a90fSyamaguchi static void
lagg_if_setsadl(struct lagg_softc * sc,uint8_t * lladdr)21825cfb8022Syamaguchi lagg_if_setsadl(struct lagg_softc *sc, uint8_t *lladdr)
218374194586Syamaguchi {
218474194586Syamaguchi struct ifnet *ifp;
21855cfb8022Syamaguchi
21865cfb8022Syamaguchi KASSERT(LAGG_LOCKED(sc));
218774194586Syamaguchi
218874194586Syamaguchi ifp = &sc->sc_if;
218974194586Syamaguchi
21905cfb8022Syamaguchi if (lagg_lladdr_equal(CLLADDR(ifp->if_sadl), lladdr))
219174194586Syamaguchi return;
219274194586Syamaguchi
21935cfb8022Syamaguchi lagg_chg_sadl(ifp, lladdr, ETHER_ADDR_LEN);
21942ac78cccSyamaguchi
219574194586Syamaguchi LAGG_UNLOCK(sc);
219674194586Syamaguchi lagg_in6_ifdetach(ifp);
219774194586Syamaguchi lagg_in6_ifattach(ifp);
219874194586Syamaguchi LAGG_LOCK(sc);
21995cfb8022Syamaguchi
22005cfb8022Syamaguchi lagg_sync_sadl(sc);
22015cfb8022Syamaguchi }
22025cfb8022Syamaguchi
22035cfb8022Syamaguchi static void
lagg_sync_sadl(struct lagg_softc * sc)22045cfb8022Syamaguchi lagg_sync_sadl(struct lagg_softc *sc)
22055cfb8022Syamaguchi {
22065cfb8022Syamaguchi struct ifnet *ifp;
22075cfb8022Syamaguchi struct lagg_port *lp;
22085cfb8022Syamaguchi const uint8_t *lla;
22095cfb8022Syamaguchi
22105cfb8022Syamaguchi ifp = &sc->sc_if;
22115cfb8022Syamaguchi KASSERT(IFNET_LOCKED(ifp));
22125cfb8022Syamaguchi
22135cfb8022Syamaguchi lla = CLLADDR(ifp->if_sadl);
22145cfb8022Syamaguchi if (lagg_lladdr_equal(lla, sc->sc_lladdr))
22155cfb8022Syamaguchi return;
22165cfb8022Syamaguchi
22175cfb8022Syamaguchi lagg_lladdr_cpy(sc->sc_lladdr, lla);
22185cfb8022Syamaguchi
22195cfb8022Syamaguchi LAGG_PORTS_FOREACH(sc, lp) {
22205cfb8022Syamaguchi IFNET_LOCK(lp->lp_ifp);
22215cfb8022Syamaguchi lagg_port_setsadl(lp, lla);
22225cfb8022Syamaguchi IFNET_UNLOCK(lp->lp_ifp);
22235cfb8022Syamaguchi }
222474194586Syamaguchi }
222574194586Syamaguchi
222674194586Syamaguchi static int
lagg_port_setup(struct lagg_softc * sc,struct lagg_port * lp,struct ifnet * ifp_port)222774194586Syamaguchi lagg_port_setup(struct lagg_softc *sc,
222874194586Syamaguchi struct lagg_port *lp, struct ifnet *ifp_port)
222974194586Syamaguchi {
22305cfb8022Syamaguchi struct ifnet *ifp;
223174194586Syamaguchi u_char if_type;
223274194586Syamaguchi int error;
2233f4c6b811Syamaguchi bool stopped, use_lagg_sadl;
223474194586Syamaguchi
223574194586Syamaguchi KASSERT(LAGG_LOCKED(sc));
223674194586Syamaguchi IFNET_ASSERT_UNLOCKED(ifp_port);
223774194586Syamaguchi
22385cfb8022Syamaguchi ifp = &sc->sc_if;
2239f4c6b811Syamaguchi
2240f4c6b811Syamaguchi use_lagg_sadl = true;
2241f4c6b811Syamaguchi if (SIMPLEQ_EMPTY(&sc->sc_ports) &&
2242f4c6b811Syamaguchi ifp_port->if_type == IFT_ETHER) {
2243f4c6b811Syamaguchi if (lagg_lladdr_equal(CLLADDR(ifp->if_sadl),
2244f4c6b811Syamaguchi sc->sc_lladdr_rand))
2245f4c6b811Syamaguchi use_lagg_sadl = false;
2246f4c6b811Syamaguchi }
22475cfb8022Syamaguchi
2248f0101d0eSyamaguchi if (&sc->sc_if == ifp_port) {
2249f0101d0eSyamaguchi LAGG_DPRINTF(sc, "cannot add a lagg to itself as a port\n");
2250f0101d0eSyamaguchi return EINVAL;
2251f0101d0eSyamaguchi }
2252f0101d0eSyamaguchi
2253e43cf638Syamaguchi if (sc->sc_nports > LAGG_MAX_PORTS)
2254f0101d0eSyamaguchi return ENOSPC;
2255f0101d0eSyamaguchi
2256f0101d0eSyamaguchi if (ifp_port->if_lagg != NULL) {
2257f0101d0eSyamaguchi lp = (struct lagg_port *)ifp_port->if_lagg;
2258f0101d0eSyamaguchi if (lp->lp_softc == sc)
2259f0101d0eSyamaguchi return EEXIST;
2260f0101d0eSyamaguchi return EBUSY;
2261f0101d0eSyamaguchi }
2262f0101d0eSyamaguchi
2263f0101d0eSyamaguchi switch (ifp_port->if_type) {
2264f0101d0eSyamaguchi case IFT_ETHER:
2265a5c0a90fSyamaguchi case IFT_L2TP:
2266764ee25eSyamaguchi if (VLAN_ATTACHED((struct ethercom *)ifp_port))
2267764ee25eSyamaguchi return EBUSY;
2268764ee25eSyamaguchi
2269f0101d0eSyamaguchi if_type = IFT_IEEE8023ADLAG;
2270f0101d0eSyamaguchi break;
2271f0101d0eSyamaguchi default:
2272f0101d0eSyamaguchi return ENOTSUP;
2273f0101d0eSyamaguchi }
2274f0101d0eSyamaguchi
227574194586Syamaguchi error = 0;
227674194586Syamaguchi stopped = false;
2277f0101d0eSyamaguchi lp->lp_softc = sc;
2278f0101d0eSyamaguchi lp->lp_prio = LAGG_PORT_PRIO;
22794a93a140Syamaguchi lp->lp_linkstate_hook = if_linkstate_change_establish(ifp_port,
22804a93a140Syamaguchi lagg_linkstate_changed, ifp_port);
22811f3caf1eSyamaguchi lp->lp_ifdetach_hook = ether_ifdetachhook_establish(ifp_port,
22821f3caf1eSyamaguchi lagg_ifdetach, ifp_port);
2283f0101d0eSyamaguchi psref_target_init(&lp->lp_psref, lagg_port_psref_class);
2284f0101d0eSyamaguchi
2285f0101d0eSyamaguchi IFNET_LOCK(ifp_port);
22865cfb8022Syamaguchi /* stop packet processing */
22875cfb8022Syamaguchi if (ISSET(ifp_port->if_flags, IFF_RUNNING) &&
22885cfb8022Syamaguchi ifp_port->if_init != NULL) {
22895cfb8022Syamaguchi if_stop(ifp_port, 0);
22905cfb8022Syamaguchi stopped = true;
22915cfb8022Syamaguchi }
22925cfb8022Syamaguchi
22935cfb8022Syamaguchi /* to delete ipv6 link local address */
22945cfb8022Syamaguchi lagg_in6_ifdetach(ifp_port);
22955cfb8022Syamaguchi
22965cfb8022Syamaguchi /* backup members */
229774194586Syamaguchi lp->lp_iftype = ifp_port->if_type;
229874194586Syamaguchi lp->lp_ioctl = ifp_port->if_ioctl;
2299d1fb1196Syamaguchi lp->lp_input = ifp_port->_if_input;
230074194586Syamaguchi lp->lp_output = ifp_port->if_output;
230174194586Syamaguchi lp->lp_ifcapenable = ifp_port->if_capenable;
230274194586Syamaguchi lp->lp_mtu = ifp_port->if_mtu;
230380bf8caaSyamaguchi if (lp->lp_iftype == IFT_ETHER) {
230480bf8caaSyamaguchi struct ethercom *ec;
230580bf8caaSyamaguchi ec = (struct ethercom *)ifp_port;
230680bf8caaSyamaguchi
230774194586Syamaguchi lagg_lladdr_cpy(lp->lp_lladdr, CLLADDR(ifp_port->if_sadl));
230880bf8caaSyamaguchi lp->lp_eccapenable = ec->ec_capenable;
230980bf8caaSyamaguchi }
231074194586Syamaguchi
23115cfb8022Syamaguchi /* change callbacks and others */
23125cfb8022Syamaguchi atomic_store_release(&ifp_port->if_lagg, (void *)lp);
2313f0101d0eSyamaguchi ifp_port->if_type = if_type;
2314f0101d0eSyamaguchi ifp_port->if_ioctl = lagg_port_ioctl;
2315d1fb1196Syamaguchi ifp_port->_if_input = lagg_input_ethernet;
23165cfb8022Syamaguchi ifp_port->if_output = lagg_port_output;
2317f4c6b811Syamaguchi
2318f4c6b811Syamaguchi /* update Link address */
2319f4c6b811Syamaguchi if (use_lagg_sadl) {
2320f4c6b811Syamaguchi lagg_port_setsadl(lp, CLLADDR(ifp->if_sadl));
2321f4c6b811Syamaguchi } else {
2322f4c6b811Syamaguchi /* update if_type in if_sadl */
23235cfb8022Syamaguchi if (lp->lp_iftype != ifp_port->if_type)
23245cfb8022Syamaguchi lagg_port_setsadl(lp, NULL);
23252bbefb42Syamaguchi }
23262bbefb42Syamaguchi
23275cfb8022Syamaguchi error = lagg_setmtu(ifp_port, ifp->if_mtu);
2328e43cf638Syamaguchi if (error != 0)
23295cfb8022Syamaguchi goto restore_sadl;
2330f0101d0eSyamaguchi
2331f0101d0eSyamaguchi error = lagg_proto_allocport(sc, lp);
2332f0101d0eSyamaguchi if (error != 0)
23335cfb8022Syamaguchi goto restore_mtu;
2334f0101d0eSyamaguchi
23355cfb8022Syamaguchi /* restart packet processing */
23365cfb8022Syamaguchi if (stopped) {
23375cfb8022Syamaguchi error = if_init(ifp_port);
23385cfb8022Syamaguchi if (error != 0)
23395cfb8022Syamaguchi goto free_port;
23405cfb8022Syamaguchi }
23415cfb8022Syamaguchi
23425cfb8022Syamaguchi /* setup of ifp_port is complete */
23435cfb8022Syamaguchi IFNET_UNLOCK(ifp_port);
23445cfb8022Syamaguchi
2345f4c6b811Syamaguchi /* copy sadl from added port to lagg */
2346f4c6b811Syamaguchi if (!use_lagg_sadl)
23475cfb8022Syamaguchi lagg_if_setsadl(sc, lp->lp_lladdr);
23485cfb8022Syamaguchi
2349f0101d0eSyamaguchi SIMPLEQ_INSERT_TAIL(&sc->sc_ports, lp, lp_entry);
2350f0101d0eSyamaguchi sc->sc_nports++;
2351f0101d0eSyamaguchi
23525cfb8022Syamaguchi lagg_capabilities_update(sc);
23536ee98b46Syamaguchi lagg_port_syncmulti(sc, lp);
23546ee98b46Syamaguchi lagg_port_syncvlan(sc, lp);
2355edab87ecSyamaguchi lagg_config_promisc(sc, lp);
23565cfb8022Syamaguchi
2357f0101d0eSyamaguchi lagg_proto_startport(sc, lp);
2358f0101d0eSyamaguchi
2359f0101d0eSyamaguchi return 0;
2360f0101d0eSyamaguchi
23615cfb8022Syamaguchi free_port:
236274194586Syamaguchi KASSERT(IFNET_LOCKED(ifp_port));
23635cfb8022Syamaguchi lagg_proto_freeport(sc, lp);
23645cfb8022Syamaguchi restore_mtu:
23655cfb8022Syamaguchi KASSERT(IFNET_LOCKED(ifp_port));
23665cfb8022Syamaguchi if (ifp_port->if_mtu != lp->lp_mtu)
23675cfb8022Syamaguchi lagg_setmtu(ifp_port, lp->lp_mtu);
23685cfb8022Syamaguchi restore_sadl:
23695cfb8022Syamaguchi KASSERT(IFNET_LOCKED(ifp_port));
23705cfb8022Syamaguchi
23715cfb8022Syamaguchi /* restore if_type before changing sadl */
23725cfb8022Syamaguchi if_type = ifp_port->if_type;
23735cfb8022Syamaguchi ifp_port->if_type = lp->lp_iftype;
23745cfb8022Syamaguchi
23755cfb8022Syamaguchi if (!SIMPLEQ_EMPTY(&sc->sc_ports)) {
23765cfb8022Syamaguchi lagg_port_setsadl(lp, lp->lp_lladdr);
23775cfb8022Syamaguchi } else {
23785cfb8022Syamaguchi if (ifp_port->if_type != if_type)
23795cfb8022Syamaguchi lagg_port_setsadl(lp, NULL);
23805cfb8022Syamaguchi }
23815cfb8022Syamaguchi
23825cfb8022Syamaguchi lagg_in6_ifattach(ifp_port);
238374194586Syamaguchi if (stopped) {
2384b4d088cbSriastradh if (if_init(ifp_port) != 0) {
23857fdf5714Syamaguchi LAGG_LOG(sc, LOG_WARNING,
238674194586Syamaguchi "couldn't re-start port %s\n",
238774194586Syamaguchi ifp_port->if_xname);
238874194586Syamaguchi }
238974194586Syamaguchi }
239074194586Syamaguchi
23915cfb8022Syamaguchi ifp_port->if_ioctl = lp->lp_ioctl;
2392d1fb1196Syamaguchi ifp_port->_if_input = lp->lp_input;
23935cfb8022Syamaguchi ifp_port->if_output = lp->lp_output;
23945cfb8022Syamaguchi atomic_store_release(&ifp_port->if_lagg, NULL);
2395f0101d0eSyamaguchi IFNET_UNLOCK(ifp_port);
2396f0101d0eSyamaguchi
239774194586Syamaguchi psref_target_destroy(&lp->lp_psref, lagg_port_psref_class);
239874194586Syamaguchi if_linkstate_change_disestablish(ifp_port,
239974194586Syamaguchi lp->lp_linkstate_hook, NULL);
24001f3caf1eSyamaguchi ether_ifdetachhook_disestablish(ifp_port,
24011f3caf1eSyamaguchi lp->lp_ifdetach_hook, &sc->sc_lock);
2402f0101d0eSyamaguchi
2403f0101d0eSyamaguchi return error;
2404f0101d0eSyamaguchi }
2405f0101d0eSyamaguchi
2406f0101d0eSyamaguchi static void
lagg_port_teardown(struct lagg_softc * sc,struct lagg_port * lp,bool is_ifdetach)240774194586Syamaguchi lagg_port_teardown(struct lagg_softc *sc, struct lagg_port *lp,
2408bbf66dafSyamaguchi bool is_ifdetach)
2409f0101d0eSyamaguchi {
24105cfb8022Syamaguchi struct ifnet *ifp, *ifp_port;
24115cfb8022Syamaguchi bool stopped, is_1st_port, iftype_changed;
2412f0101d0eSyamaguchi
2413f0101d0eSyamaguchi KASSERT(LAGG_LOCKED(sc));
2414f0101d0eSyamaguchi
24155cfb8022Syamaguchi ifp = &sc->sc_if;
2416f0101d0eSyamaguchi ifp_port = lp->lp_ifp;
2417f0101d0eSyamaguchi stopped = false;
24185cfb8022Syamaguchi is_1st_port =
24195cfb8022Syamaguchi SIMPLEQ_FIRST(&sc->sc_ports) == lp ? true : false;
2420f0101d0eSyamaguchi
24211f3caf1eSyamaguchi ether_ifdetachhook_disestablish(ifp_port,
24221f3caf1eSyamaguchi lp->lp_ifdetach_hook, &sc->sc_lock);
24231f3caf1eSyamaguchi
24241f3caf1eSyamaguchi if (ifp_port->if_lagg == NULL) {
24251f3caf1eSyamaguchi /* already done in lagg_ifdetach() */
24261f3caf1eSyamaguchi return;
24271f3caf1eSyamaguchi }
24281f3caf1eSyamaguchi
24294a93a140Syamaguchi if_linkstate_change_disestablish(ifp_port,
24304a93a140Syamaguchi lp->lp_linkstate_hook, NULL);
24314a93a140Syamaguchi
24325cfb8022Syamaguchi lagg_proto_stopport(sc, lp);
2433f0101d0eSyamaguchi
243474194586Syamaguchi lagg_port_purgemulti(sc, lp);
243574194586Syamaguchi lagg_port_purgevlan(sc, lp);
2436bbf66dafSyamaguchi if (is_ifdetach == false) {
2437edab87ecSyamaguchi lagg_unconfig_promisc(sc, lp);
243880bf8caaSyamaguchi lagg_setifcaps(lp, lp->lp_ifcapenable);
243980bf8caaSyamaguchi if (lp->lp_iftype == IFT_ETHER)
244080bf8caaSyamaguchi lagg_setethcaps(lp, lp->lp_eccapenable);
2441f0101d0eSyamaguchi }
244274194586Syamaguchi
24435cfb8022Syamaguchi SIMPLEQ_REMOVE(&sc->sc_ports, lp, lagg_port, lp_entry);
24445cfb8022Syamaguchi sc->sc_nports--;
24455cfb8022Syamaguchi
24465cfb8022Syamaguchi if (is_1st_port) {
24475cfb8022Syamaguchi if (lp->lp_iftype == IFT_ETHER &&
24485cfb8022Syamaguchi lagg_lladdr_equal(lp->lp_lladdr,
24495cfb8022Syamaguchi CLLADDR(ifp->if_sadl))) {
24505cfb8022Syamaguchi struct lagg_port *lp0;
24515cfb8022Syamaguchi uint8_t *lla;
24525cfb8022Syamaguchi
24535cfb8022Syamaguchi lp0 = SIMPLEQ_FIRST(&sc->sc_ports);
24545cfb8022Syamaguchi if (lp0 != NULL &&
24555cfb8022Syamaguchi lp0->lp_iftype == IFT_ETHER) {
24565cfb8022Syamaguchi lla = lp0->lp_lladdr;
24575cfb8022Syamaguchi } else {
24585cfb8022Syamaguchi lla = sc->sc_lladdr_rand;
24595cfb8022Syamaguchi }
24605cfb8022Syamaguchi
24615cfb8022Syamaguchi lagg_if_setsadl(sc, lla);
24625cfb8022Syamaguchi }
24635cfb8022Syamaguchi }
24645cfb8022Syamaguchi
24655cfb8022Syamaguchi IFNET_LOCK(ifp_port);
24665cfb8022Syamaguchi /* stop packet processing */
24675cfb8022Syamaguchi if (ISSET(ifp_port->if_flags, IFF_RUNNING) &&
24685cfb8022Syamaguchi ifp_port->if_init != NULL) {
24695cfb8022Syamaguchi if_stop(ifp_port, 0);
24705cfb8022Syamaguchi stopped = true;
24715cfb8022Syamaguchi }
247280bf8caaSyamaguchi
247380bf8caaSyamaguchi lagg_proto_freeport(sc, lp);
24745cfb8022Syamaguchi
24755cfb8022Syamaguchi /* change if_type before set sadl */
24765cfb8022Syamaguchi iftype_changed = ifp_port->if_type != lp->lp_iftype ?
24775cfb8022Syamaguchi true : false;
24785cfb8022Syamaguchi ifp_port->if_type = lp->lp_iftype;
24795cfb8022Syamaguchi
24805cfb8022Syamaguchi if (is_ifdetach == false) {
24815cfb8022Syamaguchi if (iftype_changed &&
24825cfb8022Syamaguchi lagg_lladdr_equal(CLLADDR(ifp_port->if_sadl),
24835cfb8022Syamaguchi lp->lp_lladdr)) {
24845cfb8022Syamaguchi lagg_port_setsadl(lp, NULL);
24855cfb8022Syamaguchi }
24865cfb8022Syamaguchi lagg_port_setsadl(lp, lp->lp_lladdr);
24875cfb8022Syamaguchi lagg_in6_ifattach(ifp_port);
24885cfb8022Syamaguchi (void)lagg_setmtu(ifp_port, lp->lp_mtu);
24895cfb8022Syamaguchi }
24905cfb8022Syamaguchi
2491d1fb1196Syamaguchi ifp_port->_if_input = lp->lp_input;
24925cfb8022Syamaguchi ifp_port->if_output = lp->lp_output;
24935cfb8022Syamaguchi if (ifp_port->if_ioctl == lagg_port_ioctl)
24945cfb8022Syamaguchi ifp_port->if_ioctl = lp->lp_ioctl;
24955cfb8022Syamaguchi atomic_store_release(&ifp_port->if_lagg, NULL);
24965cfb8022Syamaguchi pserialize_perform(sc->sc_psz);
24975cfb8022Syamaguchi
24985cfb8022Syamaguchi /* to assign ipv6 link local address */
24995cfb8022Syamaguchi if (is_ifdetach == false) {
25005cfb8022Syamaguchi lagg_in6_ifattach(ifp_port);
25015cfb8022Syamaguchi }
25025cfb8022Syamaguchi
25035cfb8022Syamaguchi /* restart packet processing */
25045cfb8022Syamaguchi if (stopped) {
25055cfb8022Syamaguchi int error;
25065cfb8022Syamaguchi error = if_init(ifp_port);
25075cfb8022Syamaguchi if (error != 0) {
25085cfb8022Syamaguchi LAGG_LOG(sc, LOG_WARNING,
25095cfb8022Syamaguchi "%s failed to if_init() on %d\n",
25105cfb8022Syamaguchi ifp_port->if_xname, error);
25115cfb8022Syamaguchi }
25125cfb8022Syamaguchi }
25135cfb8022Syamaguchi IFNET_UNLOCK(ifp_port);
25145cfb8022Syamaguchi
25155cfb8022Syamaguchi psref_target_destroy(&lp->lp_psref, lagg_port_psref_class);
251680bf8caaSyamaguchi kmem_free(lp, sizeof(*lp));
2517f0101d0eSyamaguchi }
2518f0101d0eSyamaguchi
251974194586Syamaguchi static int
lagg_addport(struct lagg_softc * sc,struct ifnet * ifp_port)252074194586Syamaguchi lagg_addport(struct lagg_softc *sc, struct ifnet *ifp_port)
2521f0101d0eSyamaguchi {
252274194586Syamaguchi struct lagg_port *lp;
252374194586Syamaguchi int error;
252474194586Syamaguchi
252574194586Syamaguchi lp = kmem_zalloc(sizeof(*lp), KM_SLEEP);
252674194586Syamaguchi lp->lp_ifp = ifp_port;
2527f0101d0eSyamaguchi
2528f0101d0eSyamaguchi LAGG_LOCK(sc);
252974194586Syamaguchi error = lagg_port_setup(sc, lp, ifp_port);
2530f0101d0eSyamaguchi LAGG_UNLOCK(sc);
253174194586Syamaguchi
253274194586Syamaguchi if (error != 0)
253374194586Syamaguchi kmem_free(lp, sizeof(*lp));
253474194586Syamaguchi
253574194586Syamaguchi return error;
2536f0101d0eSyamaguchi }
2537f0101d0eSyamaguchi
2538f0101d0eSyamaguchi static int
lagg_delport(struct lagg_softc * sc,struct ifnet * ifp_port)2539f0101d0eSyamaguchi lagg_delport(struct lagg_softc *sc, struct ifnet *ifp_port)
2540f0101d0eSyamaguchi {
2541f0101d0eSyamaguchi struct lagg_port *lp;
25421f3caf1eSyamaguchi int error;
2543f0101d0eSyamaguchi
2544f0101d0eSyamaguchi KASSERT(IFNET_LOCKED(&sc->sc_if));
2545f0101d0eSyamaguchi
25461f3caf1eSyamaguchi error = 0;
2547f0101d0eSyamaguchi LAGG_LOCK(sc);
254874194586Syamaguchi lp = ifp_port->if_lagg;
254974194586Syamaguchi if (lp == NULL || lp->lp_softc != sc) {
25501f3caf1eSyamaguchi error = ENOENT;
25511f3caf1eSyamaguchi goto out;
25521f3caf1eSyamaguchi }
25531f3caf1eSyamaguchi
25541f3caf1eSyamaguchi if (lp->lp_ifdetaching) {
25551f3caf1eSyamaguchi error = EBUSY;
25561f3caf1eSyamaguchi goto out;
255774194586Syamaguchi }
255874194586Syamaguchi
255974194586Syamaguchi lagg_port_teardown(sc, lp, false);
25601f3caf1eSyamaguchi
25611f3caf1eSyamaguchi out:
2562f0101d0eSyamaguchi LAGG_UNLOCK(sc);
2563f0101d0eSyamaguchi
25641f3caf1eSyamaguchi return error;
2565f0101d0eSyamaguchi }
2566f0101d0eSyamaguchi
25671f3caf1eSyamaguchi static int
lagg_delport_all(struct lagg_softc * sc)256874194586Syamaguchi lagg_delport_all(struct lagg_softc *sc)
256974194586Syamaguchi {
25701f3caf1eSyamaguchi struct lagg_port *lp;
25711f3caf1eSyamaguchi int error;
25721f3caf1eSyamaguchi
25731f3caf1eSyamaguchi KASSERT(IFNET_LOCKED(&sc->sc_if));
25741f3caf1eSyamaguchi
25751f3caf1eSyamaguchi error = 0;
257674194586Syamaguchi
257774194586Syamaguchi LAGG_LOCK(sc);
25781f3caf1eSyamaguchi while ((lp = LAGG_PORTS_FIRST(sc)) != NULL) {
25791f3caf1eSyamaguchi if (lp->lp_ifdetaching) {
25801f3caf1eSyamaguchi error = EBUSY;
25811f3caf1eSyamaguchi continue;
25821f3caf1eSyamaguchi }
25831f3caf1eSyamaguchi
258474194586Syamaguchi lagg_port_teardown(sc, lp, false);
258574194586Syamaguchi }
258674194586Syamaguchi
258774194586Syamaguchi LAGG_UNLOCK(sc);
25881f3caf1eSyamaguchi
25891f3caf1eSyamaguchi return error;
259074194586Syamaguchi }
259174194586Syamaguchi
2592f0101d0eSyamaguchi static int
lagg_get_stats(struct lagg_softc * sc,struct lagg_req * resp,size_t nports)2593f0101d0eSyamaguchi lagg_get_stats(struct lagg_softc *sc, struct lagg_req *resp,
2594f0101d0eSyamaguchi size_t nports)
2595f0101d0eSyamaguchi {
2596f0101d0eSyamaguchi struct lagg_variant *var;
2597f0101d0eSyamaguchi struct lagg_port *lp;
2598f0101d0eSyamaguchi struct laggreqport *port;
2599f0101d0eSyamaguchi struct psref psref;
2600f0101d0eSyamaguchi struct ifnet *ifp;
2601f0101d0eSyamaguchi int bound;
2602f0101d0eSyamaguchi size_t n;
2603f0101d0eSyamaguchi
2604f0101d0eSyamaguchi bound = curlwp_bind();
2605f0101d0eSyamaguchi var = lagg_variant_getref(sc, &psref);
260632d0c394Syamaguchi if (var == NULL) {
260732d0c394Syamaguchi curlwp_bindx(bound);
260832d0c394Syamaguchi return ENOENT;
260932d0c394Syamaguchi }
2610f0101d0eSyamaguchi
2611f0101d0eSyamaguchi resp->lrq_proto = var->lv_proto;
2612f0101d0eSyamaguchi
2613f0101d0eSyamaguchi lagg_proto_stat(var, &resp->lrq_reqproto);
2614f0101d0eSyamaguchi
2615f0101d0eSyamaguchi n = 0;
2616f0101d0eSyamaguchi LAGG_LOCK(sc);
2617f0101d0eSyamaguchi LAGG_PORTS_FOREACH(sc, lp) {
2618f0101d0eSyamaguchi if (n < nports) {
2619f0101d0eSyamaguchi port = &resp->lrq_reqports[n];
2620f0101d0eSyamaguchi
2621f0101d0eSyamaguchi ifp = lp->lp_ifp;
2622f0101d0eSyamaguchi strlcpy(port->rp_portname, ifp->if_xname,
2623f0101d0eSyamaguchi sizeof(port->rp_portname));
2624f0101d0eSyamaguchi
2625f0101d0eSyamaguchi port->rp_prio = lp->lp_prio;
2626f0101d0eSyamaguchi port->rp_flags = lp->lp_flags;
2627f0101d0eSyamaguchi lagg_proto_portstat(var, lp, port);
2628f0101d0eSyamaguchi }
2629f0101d0eSyamaguchi n++;
2630f0101d0eSyamaguchi }
2631f0101d0eSyamaguchi LAGG_UNLOCK(sc);
2632f0101d0eSyamaguchi
2633f0101d0eSyamaguchi resp->lrq_nports = n;
2634f0101d0eSyamaguchi
2635f0101d0eSyamaguchi lagg_variant_putref(var, &psref);
2636f0101d0eSyamaguchi curlwp_bindx(bound);
2637f0101d0eSyamaguchi
2638f0101d0eSyamaguchi if (resp->lrq_nports > nports) {
2639a5a34c68Syamaguchi return ENOBUFS;
2640f0101d0eSyamaguchi }
2641f0101d0eSyamaguchi return 0;
2642f0101d0eSyamaguchi }
2643f0101d0eSyamaguchi
2644714bab40Syamaguchi static void
lagg_config_promisc(struct lagg_softc * sc,struct lagg_port * lp)2645f0101d0eSyamaguchi lagg_config_promisc(struct lagg_softc *sc, struct lagg_port *lp)
2646f0101d0eSyamaguchi {
2647714bab40Syamaguchi struct ifnet *ifp, *ifp_port;
2648f0101d0eSyamaguchi int error;
2649714bab40Syamaguchi bool promisc;
2650f0101d0eSyamaguchi
2651714bab40Syamaguchi KASSERT(LAGG_LOCKED(sc));
2652714bab40Syamaguchi
2653f0101d0eSyamaguchi ifp = &sc->sc_if;
2654714bab40Syamaguchi ifp_port = lp->lp_ifp;
2655f0101d0eSyamaguchi
2656714bab40Syamaguchi if (lp->lp_iftype == IFT_ETHER) {
2657714bab40Syamaguchi promisc = ISSET(ifp->if_flags, IFF_PROMISC) ?
2658714bab40Syamaguchi true : false;
2659714bab40Syamaguchi } else {
2660714bab40Syamaguchi promisc = true;
2661714bab40Syamaguchi }
2662714bab40Syamaguchi
2663714bab40Syamaguchi if (lp->lp_promisc == promisc)
2664714bab40Syamaguchi return;
2665714bab40Syamaguchi
2666714bab40Syamaguchi error = ifpromisc(ifp_port, promisc ? 1 : 0);
2667714bab40Syamaguchi if (error == ENETRESET) {
2668714bab40Syamaguchi error = ifp_port->if_init(ifp_port);
2669714bab40Syamaguchi }
2670714bab40Syamaguchi
2671edab87ecSyamaguchi if (error == 0) {
2672714bab40Syamaguchi lp->lp_promisc = promisc;
2673714bab40Syamaguchi } else {
26747fdf5714Syamaguchi LAGG_LOG(sc, LOG_WARNING,
2675714bab40Syamaguchi "couldn't %s promisc on %s\n",
2676714bab40Syamaguchi promisc ? "set" : "unset",
2677714bab40Syamaguchi ifp_port->if_xname);
2678edab87ecSyamaguchi }
2679edab87ecSyamaguchi }
2680f0101d0eSyamaguchi
2681edab87ecSyamaguchi static void
lagg_unconfig_promisc(struct lagg_softc * sc,struct lagg_port * lp)2682edab87ecSyamaguchi lagg_unconfig_promisc(struct lagg_softc *sc, struct lagg_port *lp)
2683edab87ecSyamaguchi {
2684714bab40Syamaguchi struct ifnet *ifp_port;
2685edab87ecSyamaguchi int error;
2686edab87ecSyamaguchi
2687714bab40Syamaguchi KASSERT(LAGG_LOCKED(sc));
2688714bab40Syamaguchi
2689714bab40Syamaguchi ifp_port = lp->lp_ifp;
2690714bab40Syamaguchi
2691714bab40Syamaguchi if (lp->lp_promisc == false)
2692714bab40Syamaguchi return;
2693714bab40Syamaguchi
2694714bab40Syamaguchi error = ifpromisc(ifp_port, 0);
2695714bab40Syamaguchi if (error == ENETRESET) {
2696714bab40Syamaguchi error = ifp_port->if_init(ifp_port);
2697edab87ecSyamaguchi }
2698714bab40Syamaguchi
2699714bab40Syamaguchi if (error != 0) {
27007fdf5714Syamaguchi LAGG_LOG(sc, LOG_WARNING,
2701714bab40Syamaguchi "couldn't unset promisc on %s\n",
2702714bab40Syamaguchi ifp_port->if_xname);
2703edab87ecSyamaguchi }
2704edab87ecSyamaguchi }
2705edab87ecSyamaguchi
2706f0101d0eSyamaguchi static int
lagg_port_ioctl(struct ifnet * ifp,u_long cmd,void * data)2707f0101d0eSyamaguchi lagg_port_ioctl(struct ifnet *ifp, u_long cmd, void *data)
2708f0101d0eSyamaguchi {
2709f0101d0eSyamaguchi struct lagg_softc *sc;
2710f0101d0eSyamaguchi struct lagg_port *lp;
2711f0101d0eSyamaguchi int error = 0;
2712f0101d0eSyamaguchi u_int ifflags;
2713f0101d0eSyamaguchi
27148c756923Syamaguchi if ((lp = ifp->if_lagg) == NULL)
2715f0101d0eSyamaguchi goto fallback;
27168c756923Syamaguchi
27178c756923Syamaguchi sc = lp->lp_softc;
27188c756923Syamaguchi KASSERT(sc != NULL);
2719f0101d0eSyamaguchi
27201efd0671Syamaguchi KASSERT(IFNET_LOCKED(lp->lp_ifp));
27211efd0671Syamaguchi
2722f0101d0eSyamaguchi switch (cmd) {
2723f0101d0eSyamaguchi case SIOCSIFCAP:
2724f0101d0eSyamaguchi case SIOCSIFMTU:
272580bf8caaSyamaguchi case SIOCSETHERCAP:
2726f0101d0eSyamaguchi /* Do not allow the setting to be cahanged once joined */
2727f0101d0eSyamaguchi error = EINVAL;
2728f0101d0eSyamaguchi break;
2729f0101d0eSyamaguchi case SIOCSIFFLAGS:
2730f0101d0eSyamaguchi ifflags = ifp->if_flags;
2731f0101d0eSyamaguchi error = LAGG_PORT_IOCTL(lp, cmd, data);
2732f0101d0eSyamaguchi ifflags ^= ifp->if_flags;
2733f0101d0eSyamaguchi
2734e43cf638Syamaguchi if ((ifflags & (IFF_UP | IFF_RUNNING)) != 0)
2735f0101d0eSyamaguchi lagg_proto_linkstate(sc, lp);
2736f0101d0eSyamaguchi break;
2737f0101d0eSyamaguchi default:
2738f0101d0eSyamaguchi goto fallback;
2739f0101d0eSyamaguchi }
2740f0101d0eSyamaguchi
2741f0101d0eSyamaguchi return error;
2742f0101d0eSyamaguchi fallback:
2743f0101d0eSyamaguchi if (lp != NULL) {
2744f0101d0eSyamaguchi error = LAGG_PORT_IOCTL(lp, cmd, data);
2745f0101d0eSyamaguchi } else {
2746f0101d0eSyamaguchi error = ENOTTY;
2747f0101d0eSyamaguchi }
2748f0101d0eSyamaguchi
2749f0101d0eSyamaguchi return error;
2750f0101d0eSyamaguchi }
2751f0101d0eSyamaguchi
2752f0101d0eSyamaguchi static int
lagg_port_output(struct ifnet * ifp,struct mbuf * m,const struct sockaddr * dst,const struct rtentry * rt)2753f0101d0eSyamaguchi lagg_port_output(struct ifnet *ifp, struct mbuf *m,
2754f0101d0eSyamaguchi const struct sockaddr *dst, const struct rtentry *rt)
2755f0101d0eSyamaguchi {
2756f0101d0eSyamaguchi struct lagg_port *lp = ifp->if_lagg;
2757f0101d0eSyamaguchi int error = 0;
2758f0101d0eSyamaguchi
2759f0101d0eSyamaguchi switch (dst->sa_family) {
2760f0101d0eSyamaguchi case pseudo_AF_HDRCMPLT:
2761f0101d0eSyamaguchi case AF_UNSPEC:
2762f0101d0eSyamaguchi if (lp != NULL)
2763f0101d0eSyamaguchi error = lp->lp_output(ifp, m, dst, rt);
2764f0101d0eSyamaguchi else
2765f0101d0eSyamaguchi error = ENETDOWN;
2766f0101d0eSyamaguchi break;
2767f0101d0eSyamaguchi default:
2768f0101d0eSyamaguchi m_freem(m);
2769f0101d0eSyamaguchi error = ENETDOWN;
2770f0101d0eSyamaguchi }
2771f0101d0eSyamaguchi
2772f0101d0eSyamaguchi return error;
2773f0101d0eSyamaguchi }
2774f0101d0eSyamaguchi
2775f0101d0eSyamaguchi void
lagg_ifdetach(void * xifp_port)27761f3caf1eSyamaguchi lagg_ifdetach(void *xifp_port)
2777f0101d0eSyamaguchi {
27781f3caf1eSyamaguchi struct ifnet *ifp_port = xifp_port;
2779f0101d0eSyamaguchi struct lagg_port *lp;
2780f0101d0eSyamaguchi struct lagg_softc *sc;
2781f0101d0eSyamaguchi int s;
2782f0101d0eSyamaguchi
2783f0101d0eSyamaguchi IFNET_ASSERT_UNLOCKED(ifp_port);
2784f0101d0eSyamaguchi
2785f0101d0eSyamaguchi s = pserialize_read_enter();
2786f0101d0eSyamaguchi lp = atomic_load_consume(&ifp_port->if_lagg);
2787f0101d0eSyamaguchi if (lp == NULL) {
2788f0101d0eSyamaguchi pserialize_read_exit(s);
2789f0101d0eSyamaguchi return;
27908c756923Syamaguchi } else {
2791f0101d0eSyamaguchi sc = lp->lp_softc;
27928c756923Syamaguchi KASSERT(sc != NULL);
2793f0101d0eSyamaguchi }
2794bbf66dafSyamaguchi pserialize_read_exit(s);
2795f0101d0eSyamaguchi
27961f3caf1eSyamaguchi LAGG_LOCK(sc);
27971f3caf1eSyamaguchi lp = ifp_port->if_lagg;
27981f3caf1eSyamaguchi if (lp == NULL) {
27991f3caf1eSyamaguchi LAGG_UNLOCK(sc);
28001f3caf1eSyamaguchi return;
28011f3caf1eSyamaguchi }
28021f3caf1eSyamaguchi
28031f3caf1eSyamaguchi /*
28041f3caf1eSyamaguchi * mark as a detaching to prevent other
28051f3caf1eSyamaguchi * lagg_port_teardown() processings with IFNET_LOCK() held
28061f3caf1eSyamaguchi */
28071f3caf1eSyamaguchi lp->lp_ifdetaching = true;
28081f3caf1eSyamaguchi
28091f3caf1eSyamaguchi LAGG_UNLOCK(sc);
28101f3caf1eSyamaguchi
2811bbf66dafSyamaguchi IFNET_LOCK(&sc->sc_if);
2812bbf66dafSyamaguchi LAGG_LOCK(sc);
2813bbf66dafSyamaguchi lp = ifp_port->if_lagg;
28141f3caf1eSyamaguchi if (lp != NULL) {
281574194586Syamaguchi lagg_port_teardown(sc, lp, true);
28161f3caf1eSyamaguchi }
2817f0101d0eSyamaguchi LAGG_UNLOCK(sc);
2818f0101d0eSyamaguchi IFNET_UNLOCK(&sc->sc_if);
2819f0101d0eSyamaguchi }
2820f0101d0eSyamaguchi
2821f0101d0eSyamaguchi void
lagg_linkstate_changed(void * xifp)28224a93a140Syamaguchi lagg_linkstate_changed(void *xifp)
2823f0101d0eSyamaguchi {
28244a93a140Syamaguchi struct ifnet *ifp = xifp;
2825f0101d0eSyamaguchi struct lagg_port *lp;
2826f0101d0eSyamaguchi struct psref psref;
2827f0101d0eSyamaguchi int s, bound;
2828f0101d0eSyamaguchi
2829f0101d0eSyamaguchi s = pserialize_read_enter();
2830f0101d0eSyamaguchi lp = atomic_load_consume(&ifp->if_lagg);
2831f0101d0eSyamaguchi if (lp != NULL) {
2832f0101d0eSyamaguchi bound = curlwp_bind();
2833f0101d0eSyamaguchi lagg_port_getref(lp, &psref);
2834f0101d0eSyamaguchi } else {
2835f0101d0eSyamaguchi pserialize_read_exit(s);
2836f0101d0eSyamaguchi return;
2837f0101d0eSyamaguchi }
2838f0101d0eSyamaguchi pserialize_read_exit(s);
2839f0101d0eSyamaguchi
28401efd0671Syamaguchi IFNET_LOCK(lp->lp_ifp);
2841f0101d0eSyamaguchi lagg_proto_linkstate(lp->lp_softc, lp);
28421efd0671Syamaguchi IFNET_UNLOCK(lp->lp_ifp);
28431efd0671Syamaguchi
2844f0101d0eSyamaguchi lagg_port_putref(lp, &psref);
2845f0101d0eSyamaguchi curlwp_bindx(bound);
2846f0101d0eSyamaguchi }
2847f0101d0eSyamaguchi
2848f0101d0eSyamaguchi void
lagg_port_getref(struct lagg_port * lp,struct psref * psref)2849f0101d0eSyamaguchi lagg_port_getref(struct lagg_port *lp, struct psref *psref)
2850f0101d0eSyamaguchi {
2851f0101d0eSyamaguchi
2852f0101d0eSyamaguchi psref_acquire(psref, &lp->lp_psref, lagg_port_psref_class);
2853f0101d0eSyamaguchi }
2854f0101d0eSyamaguchi
2855f0101d0eSyamaguchi void
lagg_port_putref(struct lagg_port * lp,struct psref * psref)2856f0101d0eSyamaguchi lagg_port_putref(struct lagg_port *lp, struct psref *psref)
2857f0101d0eSyamaguchi {
2858f0101d0eSyamaguchi
2859f0101d0eSyamaguchi psref_release(psref, &lp->lp_psref, lagg_port_psref_class);
2860f0101d0eSyamaguchi }
2861f0101d0eSyamaguchi
2862f0101d0eSyamaguchi static void
lagg_workq_work(struct work * wk,void * context)2863f0101d0eSyamaguchi lagg_workq_work(struct work *wk, void *context)
2864f0101d0eSyamaguchi {
2865f0101d0eSyamaguchi struct lagg_work *lw;
2866f0101d0eSyamaguchi
2867f0101d0eSyamaguchi lw = container_of(wk, struct lagg_work, lw_cookie);
2868f0101d0eSyamaguchi
2869f0101d0eSyamaguchi atomic_cas_uint(&lw->lw_state, LAGG_WORK_ENQUEUED, LAGG_WORK_IDLE);
2870f0101d0eSyamaguchi lw->lw_func(lw, lw->lw_arg);
2871f0101d0eSyamaguchi }
2872f0101d0eSyamaguchi
2873f0101d0eSyamaguchi struct workqueue *
lagg_workq_create(const char * name,pri_t prio,int ipl,int flags)2874f0101d0eSyamaguchi lagg_workq_create(const char *name, pri_t prio, int ipl, int flags)
2875f0101d0eSyamaguchi {
2876f0101d0eSyamaguchi struct workqueue *wq;
2877f0101d0eSyamaguchi int error;
2878f0101d0eSyamaguchi
2879f0101d0eSyamaguchi error = workqueue_create(&wq, name, lagg_workq_work,
2880f0101d0eSyamaguchi NULL, prio, ipl, flags);
2881f0101d0eSyamaguchi
2882f0101d0eSyamaguchi if (error)
2883f0101d0eSyamaguchi return NULL;
2884f0101d0eSyamaguchi
2885f0101d0eSyamaguchi return wq;
2886f0101d0eSyamaguchi }
2887f0101d0eSyamaguchi
2888f0101d0eSyamaguchi void
lagg_workq_destroy(struct workqueue * wq)2889f0101d0eSyamaguchi lagg_workq_destroy(struct workqueue *wq)
2890f0101d0eSyamaguchi {
2891f0101d0eSyamaguchi
2892f0101d0eSyamaguchi workqueue_destroy(wq);
2893f0101d0eSyamaguchi }
2894f0101d0eSyamaguchi
2895f0101d0eSyamaguchi void
lagg_workq_add(struct workqueue * wq,struct lagg_work * lw)2896f0101d0eSyamaguchi lagg_workq_add(struct workqueue *wq, struct lagg_work *lw)
2897f0101d0eSyamaguchi {
2898f0101d0eSyamaguchi
2899f0101d0eSyamaguchi if (atomic_cas_uint(&lw->lw_state, LAGG_WORK_IDLE,
2900f0101d0eSyamaguchi LAGG_WORK_ENQUEUED) != LAGG_WORK_IDLE)
2901f0101d0eSyamaguchi return;
2902f0101d0eSyamaguchi
2903f0101d0eSyamaguchi KASSERT(lw->lw_func != NULL);
2904f0101d0eSyamaguchi kpreempt_disable();
2905f0101d0eSyamaguchi workqueue_enqueue(wq, &lw->lw_cookie, NULL);
2906f0101d0eSyamaguchi kpreempt_enable();
2907f0101d0eSyamaguchi }
2908f0101d0eSyamaguchi
2909f0101d0eSyamaguchi void
lagg_workq_wait(struct workqueue * wq,struct lagg_work * lw)2910f0101d0eSyamaguchi lagg_workq_wait(struct workqueue *wq, struct lagg_work *lw)
2911f0101d0eSyamaguchi {
2912f0101d0eSyamaguchi
2913f0101d0eSyamaguchi atomic_swap_uint(&lw->lw_state, LAGG_WORK_STOPPING);
2914f0101d0eSyamaguchi workqueue_wait(wq, &lw->lw_cookie);
2915f0101d0eSyamaguchi }
2916f0101d0eSyamaguchi
29171875049cSyamaguchi static int
lagg_chg_sadl(struct ifnet * ifp,const uint8_t * lla,size_t lla_len)29185cfb8022Syamaguchi lagg_chg_sadl(struct ifnet *ifp, const uint8_t *lla, size_t lla_len)
29191875049cSyamaguchi {
29201875049cSyamaguchi struct psref psref_cur, psref_next;
29211875049cSyamaguchi struct ifaddr *ifa_cur, *ifa_next, *ifa_lla;
29221875049cSyamaguchi const struct sockaddr_dl *sdl, *nsdl;
29231875049cSyamaguchi int s, error;
29241875049cSyamaguchi
29251875049cSyamaguchi KASSERT(!cpu_intr_p() && !cpu_softintr_p());
29261875049cSyamaguchi KASSERT(IFNET_LOCKED(ifp));
29271875049cSyamaguchi KASSERT(ifp->if_addrlen == lla_len);
29281875049cSyamaguchi
29291875049cSyamaguchi error = 0;
29301875049cSyamaguchi ifa_lla = NULL;
29311875049cSyamaguchi
29324579aeafSyamaguchi /* Renew all AF_LINK address to update sdl_type */
29331875049cSyamaguchi while (1) {
29344579aeafSyamaguchi /* find a Link-Level address that has the previous sdl_type */
29351875049cSyamaguchi s = pserialize_read_enter();
29361875049cSyamaguchi IFADDR_READER_FOREACH(ifa_cur, ifp) {
29371875049cSyamaguchi sdl = satocsdl(ifa_cur->ifa_addr);
29381875049cSyamaguchi if (sdl->sdl_family != AF_LINK)
29391875049cSyamaguchi continue;
29401875049cSyamaguchi
29411875049cSyamaguchi if (sdl->sdl_type != ifp->if_type) {
29421875049cSyamaguchi ifa_acquire(ifa_cur, &psref_cur);
29431875049cSyamaguchi break;
29441875049cSyamaguchi }
29451875049cSyamaguchi }
29461875049cSyamaguchi pserialize_read_exit(s);
29471875049cSyamaguchi
29481875049cSyamaguchi if (ifa_cur == NULL)
29491875049cSyamaguchi break;
29504579aeafSyamaguchi /*
29514579aeafSyamaguchi * create a new address that has new sdl_type,
29524579aeafSyamaguchi * and copy address from the previous.
29534579aeafSyamaguchi */
29541875049cSyamaguchi ifa_next = if_dl_create(ifp, &nsdl);
29551875049cSyamaguchi if (ifa_next == NULL) {
29561875049cSyamaguchi error = ENOMEM;
29571875049cSyamaguchi ifa_release(ifa_cur, &psref_cur);
29581875049cSyamaguchi goto done;
29591875049cSyamaguchi }
29601875049cSyamaguchi ifa_acquire(ifa_next, &psref_next);
29611875049cSyamaguchi (void)sockaddr_dl_setaddr(__UNCONST(nsdl), nsdl->sdl_len,
29621875049cSyamaguchi CLLADDR(sdl), ifp->if_addrlen);
29631875049cSyamaguchi ifa_insert(ifp, ifa_next);
29641875049cSyamaguchi
29654579aeafSyamaguchi /* the next Link-Level address is already set */
29661875049cSyamaguchi if (ifa_lla == NULL &&
29671875049cSyamaguchi memcmp(CLLADDR(sdl), lla, lla_len) == 0) {
29681875049cSyamaguchi ifa_lla = ifa_next;
29691875049cSyamaguchi ifaref(ifa_lla);
29701875049cSyamaguchi }
29711875049cSyamaguchi
29721875049cSyamaguchi if (ifa_cur == ifp->if_dl)
29731875049cSyamaguchi if_activate_sadl(ifp, ifa_next, nsdl);
29741875049cSyamaguchi
29751875049cSyamaguchi if (ifa_cur == ifp->if_hwdl) {
29761875049cSyamaguchi ifp->if_hwdl = ifa_next;
29771875049cSyamaguchi ifaref(ifa_next);
29781875049cSyamaguchi ifafree(ifa_cur);
29791875049cSyamaguchi }
29801875049cSyamaguchi
29814579aeafSyamaguchi /* remove the old address */
29821875049cSyamaguchi ifaref(ifa_cur);
29831875049cSyamaguchi ifa_release(ifa_cur, &psref_cur);
29841875049cSyamaguchi ifa_remove(ifp, ifa_cur);
29851875049cSyamaguchi KASSERTMSG(ifa_cur->ifa_refcnt == 1,
29861875049cSyamaguchi "ifa_refcnt=%d", ifa_cur->ifa_refcnt);
29871875049cSyamaguchi ifafree(ifa_cur);
29881875049cSyamaguchi ifa_release(ifa_next, &psref_next);
29891875049cSyamaguchi }
29901875049cSyamaguchi
29914579aeafSyamaguchi /* acquire or create the next Link-Level address */
29921875049cSyamaguchi if (ifa_lla != NULL) {
29931875049cSyamaguchi ifa_next = ifa_lla;
29941875049cSyamaguchi
29951875049cSyamaguchi ifa_acquire(ifa_next, &psref_next);
29961875049cSyamaguchi ifafree(ifa_lla);
29971875049cSyamaguchi
29981875049cSyamaguchi nsdl = satocsdl(ifa_next->ifa_addr);
29991875049cSyamaguchi } else {
30001875049cSyamaguchi ifa_next = if_dl_create(ifp, &nsdl);
30011875049cSyamaguchi if (ifa_next == NULL) {
30021875049cSyamaguchi error = ENOMEM;
30031875049cSyamaguchi goto done;
30041875049cSyamaguchi }
30051875049cSyamaguchi ifa_acquire(ifa_next, &psref_next);
30061875049cSyamaguchi (void)sockaddr_dl_setaddr(__UNCONST(nsdl),
30071875049cSyamaguchi nsdl->sdl_len, lla, ifp->if_addrlen);
30081875049cSyamaguchi ifa_insert(ifp, ifa_next);
30091875049cSyamaguchi }
30101875049cSyamaguchi
30114579aeafSyamaguchi /* Activate the next Link-Level address */
3012de1c4693Syamaguchi if (__predict_true(ifa_next != ifp->if_dl)) {
30134579aeafSyamaguchi /* save the current address */
30141875049cSyamaguchi ifa_cur = ifp->if_dl;
30151875049cSyamaguchi if (ifa_cur != NULL)
30161875049cSyamaguchi ifa_acquire(ifa_cur, &psref_cur);
30171875049cSyamaguchi
30181875049cSyamaguchi if_activate_sadl(ifp, ifa_next, nsdl);
30191875049cSyamaguchi
30204579aeafSyamaguchi /*
30214579aeafSyamaguchi * free the saved address after switching,
30224579aeafSyamaguchi * if the address is not if_hwdl.
30234579aeafSyamaguchi */
30241875049cSyamaguchi if (ifa_cur != NULL) {
30251875049cSyamaguchi if (ifa_cur != ifp->if_hwdl) {
30261875049cSyamaguchi ifaref(ifa_cur);
30271875049cSyamaguchi ifa_release(ifa_cur, &psref_cur);
30281875049cSyamaguchi ifa_remove(ifp, ifa_cur);
30291875049cSyamaguchi KASSERTMSG(ifa_cur->ifa_refcnt == 1,
30301875049cSyamaguchi "ifa_refcnt=%d",
30311875049cSyamaguchi ifa_cur->ifa_refcnt);
30321875049cSyamaguchi ifafree(ifa_cur);
30331875049cSyamaguchi } else {
30341875049cSyamaguchi ifa_release(ifa_cur, &psref_cur);
30351875049cSyamaguchi }
30361875049cSyamaguchi }
30371875049cSyamaguchi }
30381875049cSyamaguchi
30391875049cSyamaguchi ifa_release(ifa_next, &psref_next);
30401875049cSyamaguchi
30411875049cSyamaguchi done:
30421875049cSyamaguchi return error;
30431875049cSyamaguchi }
30441875049cSyamaguchi
3045f0101d0eSyamaguchi /*
3046f0101d0eSyamaguchi * Module infrastructure
3047f0101d0eSyamaguchi */
3048f0101d0eSyamaguchi #include <net/if_module.h>
3049f0101d0eSyamaguchi
3050f0101d0eSyamaguchi IF_MODULE(MODULE_CLASS_DRIVER, lagg, NULL)
3051