xref: /netbsd-src/sys/net/lagg/if_lagg.c (revision 481d3881954fd794ca5f2d880b68c53a5db8620e)
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