ieee8023ad_lacp.c revision 169569
1168561Sthompsa/*	$NetBSD: ieee8023ad_lacp.c,v 1.3 2005/12/11 12:24:54 christos Exp $	*/
2168561Sthompsa
3168561Sthompsa/*-
4168561Sthompsa * Copyright (c)2005 YAMAMOTO Takashi,
5168561Sthompsa * All rights reserved.
6168561Sthompsa *
7168561Sthompsa * Redistribution and use in source and binary forms, with or without
8168561Sthompsa * modification, are permitted provided that the following conditions
9168561Sthompsa * are met:
10168561Sthompsa * 1. Redistributions of source code must retain the above copyright
11168561Sthompsa *    notice, this list of conditions and the following disclaimer.
12168561Sthompsa * 2. Redistributions in binary form must reproduce the above copyright
13168561Sthompsa *    notice, this list of conditions and the following disclaimer in the
14168561Sthompsa *    documentation and/or other materials provided with the distribution.
15168561Sthompsa *
16168561Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17168561Sthompsa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18168561Sthompsa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19168561Sthompsa * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20168561Sthompsa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21168561Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22168561Sthompsa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23168561Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24168561Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25168561Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26168561Sthompsa * SUCH DAMAGE.
27168561Sthompsa */
28168561Sthompsa
29168561Sthompsa#include <sys/cdefs.h>
30168561Sthompsa__FBSDID("$FreeBSD: head/sys/net/ieee8023ad_lacp.c 169569 2007-05-15 07:41:46Z thompsa $");
31168561Sthompsa
32168561Sthompsa#include <sys/param.h>
33168561Sthompsa#include <sys/callout.h>
34168561Sthompsa#include <sys/mbuf.h>
35168561Sthompsa#include <sys/systm.h>
36168561Sthompsa#include <sys/malloc.h>
37168561Sthompsa#include <sys/kernel.h> /* hz */
38168561Sthompsa#include <sys/socket.h> /* for net/if.h */
39168561Sthompsa#include <sys/sockio.h>
40168561Sthompsa#include <machine/stdarg.h>
41169569Sthompsa#include <sys/lock.h>
42169569Sthompsa#include <sys/rwlock.h>
43169569Sthompsa#include <sys/taskqueue.h>
44168561Sthompsa
45168561Sthompsa#include <net/if.h>
46168561Sthompsa#include <net/if_dl.h>
47168561Sthompsa#include <net/ethernet.h>
48168561Sthompsa#include <net/if_media.h>
49168561Sthompsa#include <net/if_types.h>
50168561Sthompsa
51168793Sthompsa#include <net/if_lagg.h>
52168561Sthompsa#include <net/ieee8023ad_lacp.h>
53168561Sthompsa
54168561Sthompsa/*
55168561Sthompsa * actor system priority and port priority.
56168561Sthompsa * XXX should be configurable.
57168561Sthompsa */
58168561Sthompsa
59168561Sthompsa#define	LACP_SYSTEM_PRIO	0x8000
60168561Sthompsa#define	LACP_PORT_PRIO		0x8000
61168561Sthompsa
62168561Sthompsaconst uint8_t ethermulticastaddr_slowprotocols[ETHER_ADDR_LEN] =
63168561Sthompsa    { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 };
64168561Sthompsa
65168561Sthompsastatic const struct tlv_template lacp_info_tlv_template[] = {
66168561Sthompsa	{ LACP_TYPE_ACTORINFO,
67168561Sthompsa	    sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) },
68168561Sthompsa	{ LACP_TYPE_PARTNERINFO,
69168561Sthompsa	    sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) },
70168561Sthompsa	{ LACP_TYPE_COLLECTORINFO,
71168561Sthompsa	    sizeof(struct tlvhdr) + sizeof(struct lacp_collectorinfo) },
72168561Sthompsa	{ 0, 0 },
73168561Sthompsa};
74168561Sthompsa
75168561Sthompsatypedef void (*lacp_timer_func_t)(struct lacp_port *);
76168561Sthompsa
77168561Sthompsastatic const struct tlv_template marker_info_tlv_template[] = {
78168561Sthompsa	{ MARKER_TYPE_INFO, 16 },
79168561Sthompsa	{ 0, 0 },
80168561Sthompsa};
81168561Sthompsa
82168561Sthompsastatic const struct tlv_template marker_response_tlv_template[] = {
83168561Sthompsa	{ MARKER_TYPE_RESPONSE, 16 },
84168561Sthompsa	{ 0, 0 },
85168561Sthompsa};
86168561Sthompsa
87168561Sthompsastatic void	lacp_fill_actorinfo(struct lacp_port *, struct lacp_peerinfo *);
88168561Sthompsa
89168561Sthompsastatic uint64_t	lacp_aggregator_bandwidth(struct lacp_aggregator *);
90168561Sthompsastatic void	lacp_suppress_distributing(struct lacp_softc *,
91168561Sthompsa		    struct lacp_aggregator *);
92168561Sthompsastatic void	lacp_transit_expire(void *);
93168561Sthompsastatic void	lacp_select_active_aggregator(struct lacp_softc *);
94168561Sthompsastatic uint16_t	lacp_compose_key(struct lacp_port *);
95168561Sthompsastatic int	tlv_check(const void *, size_t, const struct tlvhdr *,
96168561Sthompsa		    const struct tlv_template *, boolean_t);
97168561Sthompsastatic void	lacp_tick(void *);
98168561Sthompsa
99168561Sthompsastatic void	lacp_fill_aggregator_id(struct lacp_aggregator *,
100168561Sthompsa		    const struct lacp_port *);
101168561Sthompsastatic void	lacp_fill_aggregator_id_peer(struct lacp_peerinfo *,
102168561Sthompsa		    const struct lacp_peerinfo *);
103168561Sthompsastatic int	lacp_aggregator_is_compatible(const struct lacp_aggregator *,
104168561Sthompsa		    const struct lacp_port *);
105168561Sthompsastatic int	lacp_peerinfo_is_compatible(const struct lacp_peerinfo *,
106168561Sthompsa		    const struct lacp_peerinfo *);
107168561Sthompsa
108168561Sthompsastatic struct lacp_aggregator *lacp_aggregator_get(struct lacp_softc *,
109168561Sthompsa		    struct lacp_port *);
110168561Sthompsastatic void	lacp_aggregator_addref(struct lacp_softc *,
111168561Sthompsa		    struct lacp_aggregator *);
112168561Sthompsastatic void	lacp_aggregator_delref(struct lacp_softc *,
113168561Sthompsa		    struct lacp_aggregator *);
114168561Sthompsa
115168561Sthompsa/* receive machine */
116168561Sthompsa
117169569Sthompsastatic void	lacp_dequeue(void *, int);
118169569Sthompsastatic int	lacp_pdu_input(struct lagg_port *, struct mbuf *);
119169569Sthompsastatic int	lacp_marker_input(struct lagg_port *, struct mbuf *);
120168561Sthompsastatic void	lacp_sm_rx(struct lacp_port *, const struct lacpdu *);
121168561Sthompsastatic void	lacp_sm_rx_timer(struct lacp_port *);
122168561Sthompsastatic void	lacp_sm_rx_set_expired(struct lacp_port *);
123168561Sthompsastatic void	lacp_sm_rx_update_ntt(struct lacp_port *,
124168561Sthompsa		    const struct lacpdu *);
125168561Sthompsastatic void	lacp_sm_rx_record_pdu(struct lacp_port *,
126168561Sthompsa		    const struct lacpdu *);
127168561Sthompsastatic void	lacp_sm_rx_update_selected(struct lacp_port *,
128168561Sthompsa		    const struct lacpdu *);
129168561Sthompsastatic void	lacp_sm_rx_record_default(struct lacp_port *);
130168561Sthompsastatic void	lacp_sm_rx_update_default_selected(struct lacp_port *);
131168561Sthompsastatic void	lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *,
132168561Sthompsa		    const struct lacp_peerinfo *);
133168561Sthompsa
134168561Sthompsa/* mux machine */
135168561Sthompsa
136168561Sthompsastatic void	lacp_sm_mux(struct lacp_port *);
137168561Sthompsastatic void	lacp_set_mux(struct lacp_port *, enum lacp_mux_state);
138168561Sthompsastatic void	lacp_sm_mux_timer(struct lacp_port *);
139168561Sthompsa
140168561Sthompsa/* periodic transmit machine */
141168561Sthompsa
142168561Sthompsastatic void	lacp_sm_ptx_update_timeout(struct lacp_port *, uint8_t);
143168561Sthompsastatic void	lacp_sm_ptx_tx_schedule(struct lacp_port *);
144168561Sthompsastatic void	lacp_sm_ptx_timer(struct lacp_port *);
145168561Sthompsa
146168561Sthompsa/* transmit machine */
147168561Sthompsa
148168561Sthompsastatic void	lacp_sm_tx(struct lacp_port *);
149168561Sthompsastatic void	lacp_sm_assert_ntt(struct lacp_port *);
150168561Sthompsa
151168561Sthompsastatic void	lacp_run_timers(struct lacp_port *);
152168561Sthompsastatic int	lacp_compare_peerinfo(const struct lacp_peerinfo *,
153168561Sthompsa		    const struct lacp_peerinfo *);
154168561Sthompsastatic int	lacp_compare_systemid(const struct lacp_systemid *,
155168561Sthompsa		    const struct lacp_systemid *);
156168561Sthompsastatic void	lacp_port_enable(struct lacp_port *);
157168561Sthompsastatic void	lacp_port_disable(struct lacp_port *);
158168561Sthompsastatic void	lacp_select(struct lacp_port *);
159168561Sthompsastatic void	lacp_unselect(struct lacp_port *);
160168561Sthompsastatic void	lacp_disable_collecting(struct lacp_port *);
161168561Sthompsastatic void	lacp_enable_collecting(struct lacp_port *);
162168561Sthompsastatic void	lacp_disable_distributing(struct lacp_port *);
163168561Sthompsastatic void	lacp_enable_distributing(struct lacp_port *);
164168561Sthompsastatic int	lacp_xmit_lacpdu(struct lacp_port *);
165168561Sthompsa
166168561Sthompsa#if defined(LACP_DEBUG)
167168561Sthompsastatic void	lacp_dump_lacpdu(const struct lacpdu *);
168168561Sthompsastatic const char *lacp_format_partner(const struct lacp_peerinfo *, char *,
169168561Sthompsa		    size_t);
170168561Sthompsastatic const char *lacp_format_lagid(const struct lacp_peerinfo *,
171168561Sthompsa		    const struct lacp_peerinfo *, char *, size_t);
172168561Sthompsastatic const char *lacp_format_lagid_aggregator(const struct lacp_aggregator *,
173168561Sthompsa		    char *, size_t);
174168561Sthompsastatic const char *lacp_format_state(uint8_t, char *, size_t);
175168561Sthompsastatic const char *lacp_format_mac(const uint8_t *, char *, size_t);
176168561Sthompsastatic const char *lacp_format_systemid(const struct lacp_systemid *, char *,
177168561Sthompsa		    size_t);
178168561Sthompsastatic const char *lacp_format_portid(const struct lacp_portid *, char *,
179168561Sthompsa		    size_t);
180168561Sthompsastatic void	lacp_dprintf(const struct lacp_port *, const char *, ...)
181168561Sthompsa		    __attribute__((__format__(__printf__, 2, 3)));
182168561Sthompsa#define	LACP_DPRINTF(a)	lacp_dprintf a
183168561Sthompsa#else
184168561Sthompsa#define LACP_DPRINTF(a) /* nothing */
185168561Sthompsa#endif
186168561Sthompsa
187168561Sthompsa/*
188168561Sthompsa * partner administration variables.
189168561Sthompsa * XXX should be configurable.
190168561Sthompsa */
191168561Sthompsa
192168561Sthompsastatic const struct lacp_peerinfo lacp_partner_admin = {
193168561Sthompsa	.lip_systemid = { .lsi_prio = 0xffff },
194168561Sthompsa	.lip_portid = { .lpi_prio = 0xffff },
195168561Sthompsa#if 1
196168561Sthompsa	/* optimistic */
197168561Sthompsa	.lip_state = LACP_STATE_SYNC | LACP_STATE_AGGREGATION |
198168561Sthompsa	    LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING,
199168561Sthompsa#else
200168561Sthompsa	/* pessimistic */
201168561Sthompsa	.lip_state = 0,
202168561Sthompsa#endif
203168561Sthompsa};
204168561Sthompsa
205168561Sthompsastatic const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = {
206168561Sthompsa	[LACP_TIMER_CURRENT_WHILE] = lacp_sm_rx_timer,
207168561Sthompsa	[LACP_TIMER_PERIODIC] = lacp_sm_ptx_timer,
208168561Sthompsa	[LACP_TIMER_WAIT_WHILE] = lacp_sm_mux_timer,
209168561Sthompsa};
210168561Sthompsa
211169569Sthompsavoid
212169569Sthompsalacp_input(struct lagg_port *lgp, struct mbuf *m)
213169569Sthompsa{
214169569Sthompsa	struct lagg_softc *lgs = lgp->lp_lagg;
215169569Sthompsa	struct lacp_softc *lsc = LACP_SOFTC(lgs);
216169569Sthompsa	uint8_t subtype;
217169569Sthompsa
218169569Sthompsa	if (m->m_pkthdr.len < sizeof(struct ether_header) + sizeof(subtype)) {
219169569Sthompsa		m_freem(m);
220169569Sthompsa		return;
221169569Sthompsa	}
222169569Sthompsa
223169569Sthompsa	m_copydata(m, sizeof(struct ether_header), sizeof(subtype), &subtype);
224169569Sthompsa	switch (subtype) {
225169569Sthompsa		case SLOWPROTOCOLS_SUBTYPE_LACP:
226169569Sthompsa			IF_HANDOFF(&lsc->lsc_queue, m, NULL);
227169569Sthompsa			taskqueue_enqueue(taskqueue_swi, &lsc->lsc_qtask);
228169569Sthompsa			break;
229169569Sthompsa
230169569Sthompsa		case SLOWPROTOCOLS_SUBTYPE_MARKER:
231169569Sthompsa			lacp_marker_input(lgp, m);
232169569Sthompsa			break;
233169569Sthompsa
234169569Sthompsa		default:
235169569Sthompsa			/* Unknown LACP packet type */
236169569Sthompsa			m_freem(m);
237169569Sthompsa			break;
238169569Sthompsa	}
239169569Sthompsa}
240169569Sthompsa
241169569Sthompsastatic void
242169569Sthompsalacp_dequeue(void *arg, int pending)
243169569Sthompsa{
244169569Sthompsa	struct lacp_softc *lsc = (struct lacp_softc *)arg;
245169569Sthompsa	struct lagg_softc *sc = lsc->lsc_lagg;
246169569Sthompsa	struct lagg_port *lgp;
247169569Sthompsa	struct mbuf *m;
248169569Sthompsa
249169569Sthompsa	LAGG_WLOCK(sc);
250169569Sthompsa	for (;;) {
251169569Sthompsa		IF_DEQUEUE(&lsc->lsc_queue, m);
252169569Sthompsa		if (m == NULL)
253169569Sthompsa			break;
254169569Sthompsa		lgp = m->m_pkthdr.rcvif->if_lagg;
255169569Sthompsa		lacp_pdu_input(lgp, m);
256169569Sthompsa	}
257169569Sthompsa	LAGG_WUNLOCK(sc);
258169569Sthompsa}
259169569Sthompsa
260168561Sthompsa/*
261169569Sthompsa * lacp_pdu_input: process lacpdu
262168561Sthompsa */
263169569Sthompsastatic int
264169569Sthompsalacp_pdu_input(struct lagg_port *lgp, struct mbuf *m)
265168561Sthompsa{
266168793Sthompsa	struct lacp_port *lp = LACP_PORT(lgp);
267168561Sthompsa	struct lacpdu *du;
268168561Sthompsa	int error = 0;
269168561Sthompsa
270169569Sthompsa	LAGG_WLOCK_ASSERT(lgp->lp_lagg);
271168561Sthompsa
272168561Sthompsa	if (__predict_false(lp->lp_flags & LACP_PORT_DETACHING)) {
273168561Sthompsa		goto bad;
274168561Sthompsa	}
275168561Sthompsa
276168561Sthompsa	if (m->m_pkthdr.len != sizeof(*du)) {
277168561Sthompsa		goto bad;
278168561Sthompsa	}
279168561Sthompsa
280168561Sthompsa	if ((m->m_flags & M_MCAST) == 0) {
281168561Sthompsa		goto bad;
282168561Sthompsa	}
283168561Sthompsa
284168561Sthompsa	if (m->m_len < sizeof(*du)) {
285168561Sthompsa		m = m_pullup(m, sizeof(*du));
286168561Sthompsa		if (m == NULL) {
287168561Sthompsa			return (ENOMEM);
288168561Sthompsa		}
289168561Sthompsa	}
290168561Sthompsa
291168561Sthompsa	du = mtod(m, struct lacpdu *);
292168561Sthompsa
293168561Sthompsa	if (memcmp(&du->ldu_eh.ether_dhost,
294168561Sthompsa	    &ethermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) {
295168561Sthompsa		goto bad;
296168561Sthompsa	}
297168561Sthompsa
298168561Sthompsa	/* XXX
299168561Sthompsa	KASSERT(du->ldu_sph.sph_subtype == SLOWPROTOCOLS_SUBTYPE_LACP,
300168561Sthompsa	    ("a very bad kassert!"));
301168561Sthompsa	*/
302168561Sthompsa
303168561Sthompsa	/*
304168561Sthompsa	 * ignore the version for compatibility with
305168561Sthompsa	 * the future protocol revisions.
306168561Sthompsa	 */
307168561Sthompsa
308168561Sthompsa#if 0
309168561Sthompsa	if (du->ldu_sph.sph_version != 1) {
310168561Sthompsa		goto bad;
311168561Sthompsa	}
312168561Sthompsa#endif
313168561Sthompsa
314168561Sthompsa	/*
315168561Sthompsa	 * ignore tlv types for compatibility with
316168561Sthompsa	 * the future protocol revisions.
317168561Sthompsa	 */
318168561Sthompsa
319168561Sthompsa	if (tlv_check(du, sizeof(*du), &du->ldu_tlv_actor,
320168561Sthompsa	    lacp_info_tlv_template, FALSE)) {
321168561Sthompsa		goto bad;
322168561Sthompsa	}
323168561Sthompsa
324168561Sthompsa#if defined(LACP_DEBUG)
325168561Sthompsa	LACP_DPRINTF((lp, "lacpdu receive\n"));
326168561Sthompsa	lacp_dump_lacpdu(du);
327168561Sthompsa#endif /* defined(LACP_DEBUG) */
328168561Sthompsa	lacp_sm_rx(lp, du);
329168561Sthompsa
330168561Sthompsa	m_freem(m);
331168561Sthompsa
332168561Sthompsa	return (error);
333168561Sthompsa
334168561Sthompsabad:
335168561Sthompsa	m_freem(m);
336168561Sthompsa	return (EINVAL);
337168561Sthompsa}
338168561Sthompsa
339168561Sthompsastatic void
340168561Sthompsalacp_fill_actorinfo(struct lacp_port *lp, struct lacp_peerinfo *info)
341168561Sthompsa{
342168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
343168793Sthompsa	struct lagg_softc *lgs = lgp->lp_lagg;
344168561Sthompsa
345168561Sthompsa	info->lip_systemid.lsi_prio = htons(LACP_SYSTEM_PRIO);
346168561Sthompsa	memcpy(&info->lip_systemid.lsi_mac,
347168793Sthompsa	    IF_LLADDR(lgs->sc_ifp), ETHER_ADDR_LEN);
348168561Sthompsa	info->lip_portid.lpi_prio = htons(LACP_PORT_PRIO);
349168561Sthompsa	info->lip_portid.lpi_portno = htons(lp->lp_ifp->if_index);
350168561Sthompsa	info->lip_state = lp->lp_state;
351168561Sthompsa}
352168561Sthompsa
353168561Sthompsastatic int
354168561Sthompsalacp_xmit_lacpdu(struct lacp_port *lp)
355168561Sthompsa{
356168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
357168561Sthompsa	struct mbuf *m;
358168561Sthompsa	struct lacpdu *du;
359168561Sthompsa	int error;
360168561Sthompsa
361169569Sthompsa	LAGG_WLOCK_ASSERT(lgp->lp_lagg);
362168561Sthompsa
363168561Sthompsa	m = m_gethdr(M_DONTWAIT, MT_DATA);
364168561Sthompsa	if (m == NULL) {
365168561Sthompsa		return (ENOMEM);
366168561Sthompsa	}
367168561Sthompsa	m->m_len = m->m_pkthdr.len = sizeof(*du);
368168561Sthompsa
369168561Sthompsa	du = mtod(m, struct lacpdu *);
370168561Sthompsa	memset(du, 0, sizeof(*du));
371168561Sthompsa
372168561Sthompsa	memcpy(&du->ldu_eh.ether_dhost, ethermulticastaddr_slowprotocols,
373168561Sthompsa	    ETHER_ADDR_LEN);
374168793Sthompsa	memcpy(&du->ldu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN);
375168561Sthompsa	du->ldu_eh.ether_type = htons(ETHERTYPE_SLOW);
376168561Sthompsa
377168561Sthompsa	du->ldu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_LACP;
378168561Sthompsa	du->ldu_sph.sph_version = 1;
379168561Sthompsa
380168561Sthompsa	TLV_SET(&du->ldu_tlv_actor, LACP_TYPE_ACTORINFO, sizeof(du->ldu_actor));
381168561Sthompsa	du->ldu_actor = lp->lp_actor;
382168561Sthompsa
383168561Sthompsa	TLV_SET(&du->ldu_tlv_partner, LACP_TYPE_PARTNERINFO,
384168561Sthompsa	    sizeof(du->ldu_partner));
385168561Sthompsa	du->ldu_partner = lp->lp_partner;
386168561Sthompsa
387168561Sthompsa	TLV_SET(&du->ldu_tlv_collector, LACP_TYPE_COLLECTORINFO,
388168561Sthompsa	    sizeof(du->ldu_collector));
389168561Sthompsa	du->ldu_collector.lci_maxdelay = 0;
390168561Sthompsa
391168561Sthompsa#if defined(LACP_DEBUG)
392168561Sthompsa	LACP_DPRINTF((lp, "lacpdu transmit\n"));
393168561Sthompsa	lacp_dump_lacpdu(du);
394168561Sthompsa#endif /* defined(LACP_DEBUG) */
395168561Sthompsa
396168561Sthompsa	m->m_flags |= M_MCAST;
397168561Sthompsa
398168561Sthompsa	/*
399168561Sthompsa	 * XXX should use higher priority queue.
400168561Sthompsa	 * otherwise network congestion can break aggregation.
401168561Sthompsa	 */
402168561Sthompsa
403168793Sthompsa	error = lagg_enqueue(lp->lp_ifp, m);
404168561Sthompsa	return (error);
405168561Sthompsa}
406168561Sthompsa
407168561Sthompsavoid
408168793Sthompsalacp_linkstate(struct lagg_port *lgp)
409168561Sthompsa{
410168793Sthompsa	struct lacp_port *lp = LACP_PORT(lgp);
411168793Sthompsa	struct ifnet *ifp = lgp->lp_ifp;
412168561Sthompsa	struct ifmediareq ifmr;
413168561Sthompsa	int error = 0;
414168561Sthompsa	u_int media;
415168561Sthompsa	uint8_t old_state;
416168561Sthompsa	uint16_t old_key;
417168561Sthompsa
418169569Sthompsa	LAGG_WLOCK_ASSERT(lgp->lp_lagg);
419168561Sthompsa
420168561Sthompsa	bzero((char *)&ifmr, sizeof(ifmr));
421168561Sthompsa	error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr);
422168561Sthompsa	if (error != 0)
423168561Sthompsa		return;
424168561Sthompsa
425168561Sthompsa	media = ifmr.ifm_active;
426169227Sthompsa	LACP_DPRINTF((lp, "media changed 0x%x -> 0x%x, ether = %d, fdx = %d, "
427169227Sthompsa	    "link = %d\n", lp->lp_media, media, IFM_TYPE(media) == IFM_ETHER,
428169227Sthompsa	    (media & IFM_FDX) != 0, ifp->if_link_state == LINK_STATE_UP));
429168561Sthompsa	old_state = lp->lp_state;
430168561Sthompsa	old_key = lp->lp_key;
431168561Sthompsa
432168561Sthompsa	lp->lp_media = media;
433169227Sthompsa	/*
434169227Sthompsa	 * If the port is not an active full duplex Ethernet link then it can
435169227Sthompsa	 * not be aggregated.
436169227Sthompsa	 */
437169227Sthompsa	if (IFM_TYPE(media) != IFM_ETHER || (media & IFM_FDX) == 0 ||
438169227Sthompsa	    ifp->if_link_state != LINK_STATE_UP) {
439168561Sthompsa		lacp_port_disable(lp);
440168561Sthompsa	} else {
441168561Sthompsa		lacp_port_enable(lp);
442168561Sthompsa	}
443168561Sthompsa	lp->lp_key = lacp_compose_key(lp);
444168561Sthompsa
445168561Sthompsa	if (old_state != lp->lp_state || old_key != lp->lp_key) {
446168561Sthompsa		LACP_DPRINTF((lp, "-> UNSELECTED\n"));
447168561Sthompsa		lp->lp_selected = LACP_UNSELECTED;
448168561Sthompsa	}
449168561Sthompsa}
450168561Sthompsa
451168561Sthompsastatic void
452168561Sthompsalacp_tick(void *arg)
453168561Sthompsa{
454168561Sthompsa	struct lacp_softc *lsc = arg;
455169569Sthompsa	struct lagg_softc *sc = lsc->lsc_lagg;
456168561Sthompsa	struct lacp_port *lp;
457168561Sthompsa
458169569Sthompsa	LAGG_WLOCK(sc);
459168561Sthompsa	LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) {
460168561Sthompsa		if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0)
461168561Sthompsa			continue;
462168561Sthompsa
463168561Sthompsa		lacp_run_timers(lp);
464168561Sthompsa
465168561Sthompsa		lacp_select(lp);
466168561Sthompsa		lacp_sm_mux(lp);
467168561Sthompsa		lacp_sm_tx(lp);
468168561Sthompsa		lacp_sm_ptx_tx_schedule(lp);
469168561Sthompsa	}
470169569Sthompsa	LAGG_WUNLOCK(sc);
471168561Sthompsa	callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc);
472168561Sthompsa}
473168561Sthompsa
474168561Sthompsaint
475168793Sthompsalacp_port_create(struct lagg_port *lgp)
476168561Sthompsa{
477168793Sthompsa	struct lagg_softc *lgs = lgp->lp_lagg;
478168793Sthompsa	struct lacp_softc *lsc = LACP_SOFTC(lgs);
479168561Sthompsa	struct lacp_port *lp;
480168793Sthompsa	struct ifnet *ifp = lgp->lp_ifp;
481168561Sthompsa	struct sockaddr_dl sdl;
482168561Sthompsa	struct ifmultiaddr *rifma = NULL;
483168561Sthompsa	int error;
484168561Sthompsa
485168561Sthompsa	boolean_t active = TRUE; /* XXX should be configurable */
486168561Sthompsa	boolean_t fast = FALSE; /* XXX should be configurable */
487168561Sthompsa
488169569Sthompsa	LAGG_WLOCK_ASSERT(lgs);
489168561Sthompsa
490168561Sthompsa	bzero((char *)&sdl, sizeof(sdl));
491168561Sthompsa	sdl.sdl_len = sizeof(sdl);
492168561Sthompsa	sdl.sdl_family = AF_LINK;
493168561Sthompsa	sdl.sdl_index = ifp->if_index;
494168561Sthompsa	sdl.sdl_type = IFT_ETHER;
495168561Sthompsa	sdl.sdl_alen = ETHER_ADDR_LEN;
496168561Sthompsa
497168561Sthompsa	bcopy(&ethermulticastaddr_slowprotocols,
498168561Sthompsa	    LLADDR(&sdl), ETHER_ADDR_LEN);
499168561Sthompsa	error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma);
500168561Sthompsa	if (error) {
501168793Sthompsa		printf("%s: ADDMULTI failed on %s\n", __func__, lgp->lp_ifname);
502168561Sthompsa		return (error);
503168561Sthompsa	}
504168561Sthompsa
505168561Sthompsa	lp = malloc(sizeof(struct lacp_port),
506168561Sthompsa	    M_DEVBUF, M_NOWAIT|M_ZERO);
507168561Sthompsa	if (lp == NULL)
508168561Sthompsa		return (ENOMEM);
509168561Sthompsa
510168793Sthompsa	lgp->lp_psc = (caddr_t)lp;
511168561Sthompsa	lp->lp_ifp = ifp;
512168793Sthompsa	lp->lp_lagg = lgp;
513168561Sthompsa	lp->lp_lsc = lsc;
514169327Sthompsa	lp->lp_ifma = rifma;
515168561Sthompsa
516168561Sthompsa	LIST_INSERT_HEAD(&lsc->lsc_ports, lp, lp_next);
517168561Sthompsa
518168561Sthompsa	lacp_fill_actorinfo(lp, &lp->lp_actor);
519168561Sthompsa	lp->lp_state =
520168561Sthompsa	    (active ? LACP_STATE_ACTIVITY : 0) |
521168561Sthompsa	    (fast ? LACP_STATE_TIMEOUT : 0);
522168561Sthompsa	lp->lp_aggregator = NULL;
523168793Sthompsa	lacp_linkstate(lgp);
524168561Sthompsa	lacp_sm_rx_set_expired(lp);
525168561Sthompsa
526168561Sthompsa	return (0);
527168561Sthompsa}
528168561Sthompsa
529168561Sthompsavoid
530168793Sthompsalacp_port_destroy(struct lagg_port *lgp)
531168561Sthompsa{
532168793Sthompsa	struct lacp_port *lp = LACP_PORT(lgp);
533169327Sthompsa	int i;
534168561Sthompsa
535169569Sthompsa	LAGG_WLOCK_ASSERT(lgp->lp_lagg);
536168561Sthompsa
537168561Sthompsa	for (i = 0; i < LACP_NTIMER; i++) {
538168561Sthompsa		LACP_TIMER_DISARM(lp, i);
539168561Sthompsa	}
540168561Sthompsa
541168561Sthompsa	lacp_disable_collecting(lp);
542168561Sthompsa	lacp_disable_distributing(lp);
543168561Sthompsa	lacp_unselect(lp);
544169227Sthompsa	lgp->lp_flags &= ~LAGG_PORT_DISABLED;
545168561Sthompsa
546169328Sthompsa	/* The address may have already been removed by if_purgemaddrs() */
547169328Sthompsa	if (!lgp->lp_detaching)
548169328Sthompsa		if_delmulti_ifma(lp->lp_ifma);
549168561Sthompsa
550168561Sthompsa	LIST_REMOVE(lp, lp_next);
551168561Sthompsa	free(lp, M_DEVBUF);
552168561Sthompsa}
553168561Sthompsa
554168561Sthompsaint
555168793Sthompsalacp_port_isactive(struct lagg_port *lgp)
556168561Sthompsa{
557168793Sthompsa	struct lacp_port *lp = LACP_PORT(lgp);
558168561Sthompsa	struct lacp_softc *lsc = lp->lp_lsc;
559168561Sthompsa	struct lacp_aggregator *la = lp->lp_aggregator;
560168561Sthompsa
561168561Sthompsa	/* This port is joined to the active aggregator */
562168561Sthompsa	if (la != NULL && la == lsc->lsc_active_aggregator)
563168561Sthompsa		return (1);
564168561Sthompsa
565168561Sthompsa	return (0);
566168561Sthompsa}
567168561Sthompsa
568168561Sthompsastatic void
569168561Sthompsalacp_disable_collecting(struct lacp_port *lp)
570168561Sthompsa{
571168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
572168561Sthompsa
573168561Sthompsa	LACP_DPRINTF((lp, "collecting disabled\n"));
574168561Sthompsa
575168561Sthompsa	lp->lp_state &= ~LACP_STATE_COLLECTING;
576168793Sthompsa	lgp->lp_flags &= ~LAGG_PORT_COLLECTING;
577168561Sthompsa}
578168561Sthompsa
579168561Sthompsastatic void
580168561Sthompsalacp_enable_collecting(struct lacp_port *lp)
581168561Sthompsa{
582168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
583168561Sthompsa
584168561Sthompsa	LACP_DPRINTF((lp, "collecting enabled\n"));
585168561Sthompsa
586168561Sthompsa	lp->lp_state |= LACP_STATE_COLLECTING;
587168793Sthompsa	lgp->lp_flags |= LAGG_PORT_COLLECTING;
588168561Sthompsa}
589168561Sthompsa
590168561Sthompsastatic void
591168561Sthompsalacp_disable_distributing(struct lacp_port *lp)
592168561Sthompsa{
593168561Sthompsa	struct lacp_aggregator *la = lp->lp_aggregator;
594168561Sthompsa	struct lacp_softc *lsc = lp->lp_lsc;
595168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
596168561Sthompsa#if defined(LACP_DEBUG)
597168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
598168561Sthompsa#endif /* defined(LACP_DEBUG) */
599168561Sthompsa
600169569Sthompsa	LAGG_WLOCK_ASSERT(lgp->lp_lagg);
601168561Sthompsa
602168561Sthompsa	if (la == NULL || (lp->lp_state & LACP_STATE_DISTRIBUTING) == 0) {
603168561Sthompsa		return;
604168561Sthompsa	}
605168561Sthompsa
606168561Sthompsa	KASSERT(!TAILQ_EMPTY(&la->la_ports), ("no aggregator ports"));
607168561Sthompsa	KASSERT(la->la_nports > 0, ("nports invalid (%d)", la->la_nports));
608168561Sthompsa	KASSERT(la->la_refcnt >= la->la_nports, ("aggregator refcnt invalid"));
609168561Sthompsa
610168561Sthompsa	LACP_DPRINTF((lp, "disable distributing on aggregator %s, "
611168561Sthompsa	    "nports %d -> %d\n",
612168561Sthompsa	    lacp_format_lagid_aggregator(la, buf, sizeof(buf)),
613168561Sthompsa	    la->la_nports, la->la_nports - 1));
614168561Sthompsa
615168561Sthompsa	TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q);
616168561Sthompsa	la->la_nports--;
617168561Sthompsa
618168561Sthompsa	lacp_suppress_distributing(lsc, la);
619168561Sthompsa
620168561Sthompsa	lp->lp_state &= ~LACP_STATE_DISTRIBUTING;
621168793Sthompsa	lgp->lp_flags &= ~LAGG_PORT_DISTRIBUTING;
622168561Sthompsa
623168561Sthompsa	if (lsc->lsc_active_aggregator == la) {
624168561Sthompsa		lacp_select_active_aggregator(lsc);
625168561Sthompsa	}
626168561Sthompsa}
627168561Sthompsa
628168561Sthompsastatic void
629168561Sthompsalacp_enable_distributing(struct lacp_port *lp)
630168561Sthompsa{
631168561Sthompsa	struct lacp_aggregator *la = lp->lp_aggregator;
632168561Sthompsa	struct lacp_softc *lsc = lp->lp_lsc;
633168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
634168561Sthompsa#if defined(LACP_DEBUG)
635168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
636168561Sthompsa#endif /* defined(LACP_DEBUG) */
637168561Sthompsa
638169569Sthompsa	LAGG_WLOCK_ASSERT(lgp->lp_lagg);
639168561Sthompsa
640168561Sthompsa	if ((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0) {
641168561Sthompsa		return;
642168561Sthompsa	}
643168561Sthompsa
644168561Sthompsa	LACP_DPRINTF((lp, "enable distributing on aggregator %s, "
645168561Sthompsa	    "nports %d -> %d\n",
646168561Sthompsa	    lacp_format_lagid_aggregator(la, buf, sizeof(buf)),
647168561Sthompsa	    la->la_nports, la->la_nports + 1));
648168561Sthompsa
649168561Sthompsa	KASSERT(la->la_refcnt > la->la_nports, ("aggregator refcnt invalid"));
650168561Sthompsa	TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q);
651168561Sthompsa	la->la_nports++;
652168561Sthompsa
653168561Sthompsa	lacp_suppress_distributing(lsc, la);
654168561Sthompsa
655168561Sthompsa	lp->lp_state |= LACP_STATE_DISTRIBUTING;
656168793Sthompsa	lgp->lp_flags |= LAGG_PORT_DISTRIBUTING;
657168561Sthompsa
658168561Sthompsa	if (lsc->lsc_active_aggregator != la) {
659168561Sthompsa		lacp_select_active_aggregator(lsc);
660168561Sthompsa	}
661168561Sthompsa}
662168561Sthompsa
663168561Sthompsastatic void
664168561Sthompsalacp_transit_expire(void *vp)
665168561Sthompsa{
666168561Sthompsa	struct lacp_softc *lsc = vp;
667168561Sthompsa
668168561Sthompsa	LACP_DPRINTF((NULL, "%s\n", __func__));
669168561Sthompsa	lsc->lsc_suppress_distributing = FALSE;
670168561Sthompsa}
671168561Sthompsa
672168561Sthompsaint
673168793Sthompsalacp_attach(struct lagg_softc *lgs)
674168561Sthompsa{
675168561Sthompsa	struct lacp_softc *lsc;
676168561Sthompsa
677169569Sthompsa	LAGG_WLOCK_ASSERT(lgs);
678168561Sthompsa
679168561Sthompsa	lsc = malloc(sizeof(struct lacp_softc),
680168561Sthompsa	    M_DEVBUF, M_NOWAIT|M_ZERO);
681168561Sthompsa	if (lsc == NULL)
682168561Sthompsa		return (ENOMEM);
683168561Sthompsa
684168793Sthompsa	lgs->sc_psc = (caddr_t)lsc;
685168793Sthompsa	lsc->lsc_lagg = lgs;
686168561Sthompsa
687168561Sthompsa	lsc->lsc_hashkey = arc4random();
688168561Sthompsa	lsc->lsc_active_aggregator = NULL;
689168561Sthompsa	TAILQ_INIT(&lsc->lsc_aggregators);
690168561Sthompsa	LIST_INIT(&lsc->lsc_ports);
691168561Sthompsa
692169569Sthompsa	TASK_INIT(&lsc->lsc_qtask, 0, lacp_dequeue, lsc);
693169569Sthompsa	mtx_init(&lsc->lsc_queue.ifq_mtx, "lacp queue", NULL, MTX_DEF);
694169569Sthompsa	lsc->lsc_queue.ifq_maxlen = ifqmaxlen;
695168561Sthompsa
696169569Sthompsa	callout_init(&lsc->lsc_transit_callout, CALLOUT_MPSAFE);
697169569Sthompsa	callout_init(&lsc->lsc_callout, CALLOUT_MPSAFE);
698169569Sthompsa
699168793Sthompsa	/* if the lagg is already up then do the same */
700168793Sthompsa	if (lgs->sc_ifp->if_drv_flags & IFF_DRV_RUNNING)
701168793Sthompsa		lacp_init(lgs);
702168561Sthompsa
703168561Sthompsa	return (0);
704168561Sthompsa}
705168561Sthompsa
706168561Sthompsaint
707168793Sthompsalacp_detach(struct lagg_softc *lgs)
708168561Sthompsa{
709168793Sthompsa	struct lacp_softc *lsc = LACP_SOFTC(lgs);
710168561Sthompsa
711168561Sthompsa	KASSERT(TAILQ_EMPTY(&lsc->lsc_aggregators),
712168561Sthompsa	    ("aggregators still active"));
713168561Sthompsa	KASSERT(lsc->lsc_active_aggregator == NULL,
714168561Sthompsa	    ("aggregator still attached"));
715168561Sthompsa
716168793Sthompsa	lgs->sc_psc = NULL;
717168561Sthompsa	callout_drain(&lsc->lsc_transit_callout);
718168561Sthompsa	callout_drain(&lsc->lsc_callout);
719169569Sthompsa	taskqueue_drain(taskqueue_swi, &lsc->lsc_qtask);
720169569Sthompsa	IF_DRAIN(&lsc->lsc_queue);
721169569Sthompsa	mtx_destroy(&lsc->lsc_queue.ifq_mtx);
722168561Sthompsa
723168561Sthompsa	free(lsc, M_DEVBUF);
724168561Sthompsa	return (0);
725168561Sthompsa}
726168561Sthompsa
727168561Sthompsavoid
728168793Sthompsalacp_init(struct lagg_softc *lgs)
729168561Sthompsa{
730168793Sthompsa	struct lacp_softc *lsc = LACP_SOFTC(lgs);
731168561Sthompsa
732168561Sthompsa	callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc);
733168561Sthompsa}
734168561Sthompsa
735168561Sthompsavoid
736168793Sthompsalacp_stop(struct lagg_softc *lgs)
737168561Sthompsa{
738168793Sthompsa	struct lacp_softc *lsc = LACP_SOFTC(lgs);
739168561Sthompsa
740168561Sthompsa	callout_stop(&lsc->lsc_transit_callout);
741168561Sthompsa	callout_stop(&lsc->lsc_callout);
742168561Sthompsa}
743168561Sthompsa
744168793Sthompsastruct lagg_port *
745168793Sthompsalacp_select_tx_port(struct lagg_softc *lgs, struct mbuf *m)
746168561Sthompsa{
747168793Sthompsa	struct lacp_softc *lsc = LACP_SOFTC(lgs);
748168561Sthompsa	struct lacp_aggregator *la;
749168561Sthompsa	struct lacp_port *lp;
750168561Sthompsa	uint32_t hash;
751168561Sthompsa	int nports;
752168561Sthompsa
753169569Sthompsa	LAGG_WLOCK_ASSERT(lgs);
754168561Sthompsa
755168561Sthompsa	if (__predict_false(lsc->lsc_suppress_distributing)) {
756168561Sthompsa		LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__));
757168561Sthompsa		return (NULL);
758168561Sthompsa	}
759168561Sthompsa
760168561Sthompsa	la = lsc->lsc_active_aggregator;
761168561Sthompsa	if (__predict_false(la == NULL)) {
762168561Sthompsa		LACP_DPRINTF((NULL, "%s: no active aggregator\n", __func__));
763168561Sthompsa		return (NULL);
764168561Sthompsa	}
765168561Sthompsa
766168561Sthompsa	nports = la->la_nports;
767168561Sthompsa	KASSERT(nports > 0, ("no ports available"));
768168561Sthompsa
769168793Sthompsa	hash = lagg_hashmbuf(m, lsc->lsc_hashkey);
770168561Sthompsa	hash %= nports;
771168561Sthompsa	lp = TAILQ_FIRST(&la->la_ports);
772168561Sthompsa	while (hash--) {
773168561Sthompsa		lp = TAILQ_NEXT(lp, lp_dist_q);
774168561Sthompsa	}
775168561Sthompsa
776168561Sthompsa	KASSERT((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0,
777168561Sthompsa	    ("aggregated port is not distributing"));
778168561Sthompsa
779168793Sthompsa	return (lp->lp_lagg);
780168561Sthompsa}
781168561Sthompsa/*
782168561Sthompsa * lacp_suppress_distributing: drop transmit packets for a while
783168561Sthompsa * to preserve packet ordering.
784168561Sthompsa */
785168561Sthompsa
786168561Sthompsastatic void
787168561Sthompsalacp_suppress_distributing(struct lacp_softc *lsc, struct lacp_aggregator *la)
788168561Sthompsa{
789168561Sthompsa	if (lsc->lsc_active_aggregator != la) {
790168561Sthompsa		return;
791168561Sthompsa	}
792168561Sthompsa
793168561Sthompsa	LACP_DPRINTF((NULL, "%s\n", __func__));
794168561Sthompsa	lsc->lsc_suppress_distributing = TRUE;
795168561Sthompsa	/* XXX should consider collector max delay */
796168561Sthompsa	callout_reset(&lsc->lsc_transit_callout,
797168561Sthompsa	    LACP_TRANSIT_DELAY * hz / 1000, lacp_transit_expire, lsc);
798168561Sthompsa}
799168561Sthompsa
800168561Sthompsastatic int
801168561Sthompsalacp_compare_peerinfo(const struct lacp_peerinfo *a,
802168561Sthompsa    const struct lacp_peerinfo *b)
803168561Sthompsa{
804168561Sthompsa	return (memcmp(a, b, offsetof(struct lacp_peerinfo, lip_state)));
805168561Sthompsa}
806168561Sthompsa
807168561Sthompsastatic int
808168561Sthompsalacp_compare_systemid(const struct lacp_systemid *a,
809168561Sthompsa    const struct lacp_systemid *b)
810168561Sthompsa{
811168561Sthompsa	return (memcmp(a, b, sizeof(*a)));
812168561Sthompsa}
813168561Sthompsa
814168561Sthompsa#if 0	/* unused */
815168561Sthompsastatic int
816168561Sthompsalacp_compare_portid(const struct lacp_portid *a,
817168561Sthompsa    const struct lacp_portid *b)
818168561Sthompsa{
819168561Sthompsa	return (memcmp(a, b, sizeof(*a)));
820168561Sthompsa}
821168561Sthompsa#endif
822168561Sthompsa
823168561Sthompsastatic uint64_t
824168561Sthompsalacp_aggregator_bandwidth(struct lacp_aggregator *la)
825168561Sthompsa{
826168561Sthompsa	struct lacp_port *lp;
827168561Sthompsa	uint64_t speed;
828168561Sthompsa
829168561Sthompsa	lp = TAILQ_FIRST(&la->la_ports);
830168561Sthompsa	if (lp == NULL) {
831168561Sthompsa		return (0);
832168561Sthompsa	}
833168561Sthompsa
834168561Sthompsa	speed = ifmedia_baudrate(lp->lp_media);
835168561Sthompsa	speed *= la->la_nports;
836168561Sthompsa	if (speed == 0) {
837168561Sthompsa		LACP_DPRINTF((lp, "speed 0? media=0x%x nports=%d\n",
838168561Sthompsa		    lp->lp_media, la->la_nports));
839168561Sthompsa	}
840168561Sthompsa
841168561Sthompsa	return (speed);
842168561Sthompsa}
843168561Sthompsa
844168561Sthompsa/*
845168561Sthompsa * lacp_select_active_aggregator: select an aggregator to be used to transmit
846168793Sthompsa * packets from lagg(4) interface.
847168561Sthompsa */
848168561Sthompsa
849168561Sthompsastatic void
850168561Sthompsalacp_select_active_aggregator(struct lacp_softc *lsc)
851168561Sthompsa{
852168561Sthompsa	struct lacp_aggregator *la;
853168561Sthompsa	struct lacp_aggregator *best_la = NULL;
854168561Sthompsa	uint64_t best_speed = 0;
855168561Sthompsa#if defined(LACP_DEBUG)
856168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
857168561Sthompsa#endif /* defined(LACP_DEBUG) */
858168561Sthompsa
859168561Sthompsa	LACP_DPRINTF((NULL, "%s:\n", __func__));
860168561Sthompsa
861168561Sthompsa	TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) {
862168561Sthompsa		uint64_t speed;
863168561Sthompsa
864168561Sthompsa		if (la->la_nports == 0) {
865168561Sthompsa			continue;
866168561Sthompsa		}
867168561Sthompsa
868168561Sthompsa		speed = lacp_aggregator_bandwidth(la);
869168561Sthompsa		LACP_DPRINTF((NULL, "%s, speed=%jd, nports=%d\n",
870168561Sthompsa		    lacp_format_lagid_aggregator(la, buf, sizeof(buf)),
871168561Sthompsa		    speed, la->la_nports));
872168561Sthompsa		if (speed > best_speed ||
873168561Sthompsa		    (speed == best_speed &&
874168561Sthompsa		    la == lsc->lsc_active_aggregator)) {
875168561Sthompsa			best_la = la;
876168561Sthompsa			best_speed = speed;
877168561Sthompsa		}
878168561Sthompsa	}
879168561Sthompsa
880168561Sthompsa	KASSERT(best_la == NULL || best_la->la_nports > 0,
881168561Sthompsa	    ("invalid aggregator refcnt"));
882168561Sthompsa	KASSERT(best_la == NULL || !TAILQ_EMPTY(&best_la->la_ports),
883168561Sthompsa	    ("invalid aggregator list"));
884168561Sthompsa
885168561Sthompsa#if defined(LACP_DEBUG)
886168561Sthompsa	if (lsc->lsc_active_aggregator != best_la) {
887168561Sthompsa		LACP_DPRINTF((NULL, "active aggregator changed\n"));
888168561Sthompsa		LACP_DPRINTF((NULL, "old %s\n",
889168561Sthompsa		    lacp_format_lagid_aggregator(lsc->lsc_active_aggregator,
890168561Sthompsa		    buf, sizeof(buf))));
891168561Sthompsa	} else {
892168561Sthompsa		LACP_DPRINTF((NULL, "active aggregator not changed\n"));
893168561Sthompsa	}
894168561Sthompsa	LACP_DPRINTF((NULL, "new %s\n",
895168561Sthompsa	    lacp_format_lagid_aggregator(best_la, buf, sizeof(buf))));
896168561Sthompsa#endif /* defined(LACP_DEBUG) */
897168561Sthompsa
898168561Sthompsa	if (lsc->lsc_active_aggregator != best_la) {
899168561Sthompsa		lsc->lsc_active_aggregator = best_la;
900168561Sthompsa		if (best_la) {
901168561Sthompsa			lacp_suppress_distributing(lsc, best_la);
902168561Sthompsa		}
903168561Sthompsa	}
904168561Sthompsa}
905168561Sthompsa
906168561Sthompsastatic uint16_t
907168561Sthompsalacp_compose_key(struct lacp_port *lp)
908168561Sthompsa{
909168793Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
910168793Sthompsa	struct lagg_softc *lgs = lgp->lp_lagg;
911168561Sthompsa	u_int media = lp->lp_media;
912168561Sthompsa	uint16_t key;
913168561Sthompsa
914168561Sthompsa	if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) {
915168561Sthompsa
916168561Sthompsa		/*
917168561Sthompsa		 * non-aggregatable links should have unique keys.
918168561Sthompsa		 *
919168561Sthompsa		 * XXX this isn't really unique as if_index is 16 bit.
920168561Sthompsa		 */
921168561Sthompsa
922168561Sthompsa		/* bit 0..14:	(some bits of) if_index of this port */
923168561Sthompsa		key = lp->lp_ifp->if_index;
924168561Sthompsa		/* bit 15:	1 */
925168561Sthompsa		key |= 0x8000;
926168561Sthompsa	} else {
927168561Sthompsa		u_int subtype = IFM_SUBTYPE(media);
928168561Sthompsa
929169227Sthompsa		KASSERT(IFM_TYPE(media) == IFM_ETHER, ("invalid media type"));
930169227Sthompsa		KASSERT((media & IFM_FDX) != 0, ("aggregating HDX interface"));
931168561Sthompsa
932168561Sthompsa		/* bit 0..4:	IFM_SUBTYPE */
933168561Sthompsa		key = subtype;
934168793Sthompsa		/* bit 5..14:	(some bits of) if_index of lagg device */
935168793Sthompsa		key |= 0x7fe0 & ((lgs->sc_ifp->if_index) << 5);
936168561Sthompsa		/* bit 15:	0 */
937168561Sthompsa	}
938168561Sthompsa	return (htons(key));
939168561Sthompsa}
940168561Sthompsa
941168561Sthompsastatic void
942168561Sthompsalacp_aggregator_addref(struct lacp_softc *lsc, struct lacp_aggregator *la)
943168561Sthompsa{
944168561Sthompsa#if defined(LACP_DEBUG)
945168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
946168561Sthompsa#endif
947168561Sthompsa
948168561Sthompsa	LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n",
949168561Sthompsa	    __func__,
950168561Sthompsa	    lacp_format_lagid(&la->la_actor, &la->la_partner,
951168561Sthompsa	    buf, sizeof(buf)),
952168561Sthompsa	    la->la_refcnt, la->la_refcnt + 1));
953168561Sthompsa
954168561Sthompsa	KASSERT(la->la_refcnt > 0, ("refcount <= 0"));
955168561Sthompsa	la->la_refcnt++;
956168561Sthompsa	KASSERT(la->la_refcnt > la->la_nports, ("invalid refcount"));
957168561Sthompsa}
958168561Sthompsa
959168561Sthompsastatic void
960168561Sthompsalacp_aggregator_delref(struct lacp_softc *lsc, struct lacp_aggregator *la)
961168561Sthompsa{
962168561Sthompsa#if defined(LACP_DEBUG)
963168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
964168561Sthompsa#endif
965168561Sthompsa
966168561Sthompsa	LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n",
967168561Sthompsa	    __func__,
968168561Sthompsa	    lacp_format_lagid(&la->la_actor, &la->la_partner,
969168561Sthompsa	    buf, sizeof(buf)),
970168561Sthompsa	    la->la_refcnt, la->la_refcnt - 1));
971168561Sthompsa
972168561Sthompsa	KASSERT(la->la_refcnt > la->la_nports, ("invalid refcnt"));
973168561Sthompsa	la->la_refcnt--;
974168561Sthompsa	if (la->la_refcnt > 0) {
975168561Sthompsa		return;
976168561Sthompsa	}
977168561Sthompsa
978168561Sthompsa	KASSERT(la->la_refcnt == 0, ("refcount not zero"));
979168561Sthompsa	KASSERT(lsc->lsc_active_aggregator != la, ("aggregator active"));
980168561Sthompsa
981168561Sthompsa	TAILQ_REMOVE(&lsc->lsc_aggregators, la, la_q);
982168561Sthompsa
983168561Sthompsa	free(la, M_DEVBUF);
984168561Sthompsa}
985168561Sthompsa
986168561Sthompsa/*
987168561Sthompsa * lacp_aggregator_get: allocate an aggregator.
988168561Sthompsa */
989168561Sthompsa
990168561Sthompsastatic struct lacp_aggregator *
991168561Sthompsalacp_aggregator_get(struct lacp_softc *lsc, struct lacp_port *lp)
992168561Sthompsa{
993168561Sthompsa	struct lacp_aggregator *la;
994168561Sthompsa
995168561Sthompsa	la = malloc(sizeof(*la), M_DEVBUF, M_NOWAIT);
996168561Sthompsa	if (la) {
997168561Sthompsa		la->la_refcnt = 1;
998168561Sthompsa		la->la_nports = 0;
999168561Sthompsa		TAILQ_INIT(&la->la_ports);
1000168561Sthompsa		la->la_pending = 0;
1001168561Sthompsa		TAILQ_INSERT_TAIL(&lsc->lsc_aggregators, la, la_q);
1002168561Sthompsa	}
1003168561Sthompsa
1004168561Sthompsa	return (la);
1005168561Sthompsa}
1006168561Sthompsa
1007168561Sthompsa/*
1008168561Sthompsa * lacp_fill_aggregator_id: setup a newly allocated aggregator from a port.
1009168561Sthompsa */
1010168561Sthompsa
1011168561Sthompsastatic void
1012168561Sthompsalacp_fill_aggregator_id(struct lacp_aggregator *la, const struct lacp_port *lp)
1013168561Sthompsa{
1014168561Sthompsa	lacp_fill_aggregator_id_peer(&la->la_partner, &lp->lp_partner);
1015168561Sthompsa	lacp_fill_aggregator_id_peer(&la->la_actor, &lp->lp_actor);
1016168561Sthompsa
1017168561Sthompsa	la->la_actor.lip_state = lp->lp_state & LACP_STATE_AGGREGATION;
1018168561Sthompsa}
1019168561Sthompsa
1020168561Sthompsastatic void
1021168561Sthompsalacp_fill_aggregator_id_peer(struct lacp_peerinfo *lpi_aggr,
1022168561Sthompsa    const struct lacp_peerinfo *lpi_port)
1023168561Sthompsa{
1024168561Sthompsa	memset(lpi_aggr, 0, sizeof(*lpi_aggr));
1025168561Sthompsa	lpi_aggr->lip_systemid = lpi_port->lip_systemid;
1026168561Sthompsa	lpi_aggr->lip_key = lpi_port->lip_key;
1027168561Sthompsa}
1028168561Sthompsa
1029168561Sthompsa/*
1030168561Sthompsa * lacp_aggregator_is_compatible: check if a port can join to an aggregator.
1031168561Sthompsa */
1032168561Sthompsa
1033168561Sthompsastatic int
1034168561Sthompsalacp_aggregator_is_compatible(const struct lacp_aggregator *la,
1035168561Sthompsa    const struct lacp_port *lp)
1036168561Sthompsa{
1037168561Sthompsa	if (!(lp->lp_state & LACP_STATE_AGGREGATION) ||
1038168561Sthompsa	    !(lp->lp_partner.lip_state & LACP_STATE_AGGREGATION)) {
1039168561Sthompsa		return (0);
1040168561Sthompsa	}
1041168561Sthompsa
1042168561Sthompsa	if (!(la->la_actor.lip_state & LACP_STATE_AGGREGATION)) {
1043168561Sthompsa		return (0);
1044168561Sthompsa	}
1045168561Sthompsa
1046168561Sthompsa	if (!lacp_peerinfo_is_compatible(&la->la_partner, &lp->lp_partner)) {
1047168561Sthompsa		return (0);
1048168561Sthompsa	}
1049168561Sthompsa
1050168561Sthompsa	if (!lacp_peerinfo_is_compatible(&la->la_actor, &lp->lp_actor)) {
1051168561Sthompsa		return (0);
1052168561Sthompsa	}
1053168561Sthompsa
1054168561Sthompsa	return (1);
1055168561Sthompsa}
1056168561Sthompsa
1057168561Sthompsastatic int
1058168561Sthompsalacp_peerinfo_is_compatible(const struct lacp_peerinfo *a,
1059168561Sthompsa    const struct lacp_peerinfo *b)
1060168561Sthompsa{
1061168561Sthompsa	if (memcmp(&a->lip_systemid, &b->lip_systemid,
1062168561Sthompsa	    sizeof(a->lip_systemid))) {
1063168561Sthompsa		return (0);
1064168561Sthompsa	}
1065168561Sthompsa
1066168561Sthompsa	if (memcmp(&a->lip_key, &b->lip_key, sizeof(a->lip_key))) {
1067168561Sthompsa		return (0);
1068168561Sthompsa	}
1069168561Sthompsa
1070168561Sthompsa	return (1);
1071168561Sthompsa}
1072168561Sthompsa
1073168561Sthompsastatic void
1074168561Sthompsalacp_port_enable(struct lacp_port *lp)
1075168561Sthompsa{
1076169227Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
1077169227Sthompsa
1078168561Sthompsa	lp->lp_state |= LACP_STATE_AGGREGATION;
1079169227Sthompsa	lgp->lp_flags &= ~LAGG_PORT_DISABLED;
1080168561Sthompsa}
1081168561Sthompsa
1082168561Sthompsastatic void
1083168561Sthompsalacp_port_disable(struct lacp_port *lp)
1084168561Sthompsa{
1085169227Sthompsa	struct lagg_port *lgp = lp->lp_lagg;
1086169227Sthompsa
1087168561Sthompsa	lacp_set_mux(lp, LACP_MUX_DETACHED);
1088168561Sthompsa
1089168561Sthompsa	lp->lp_state &= ~LACP_STATE_AGGREGATION;
1090168561Sthompsa	lp->lp_selected = LACP_UNSELECTED;
1091168561Sthompsa	lacp_sm_rx_record_default(lp);
1092168561Sthompsa	lp->lp_partner.lip_state &= ~LACP_STATE_AGGREGATION;
1093168561Sthompsa	lp->lp_state &= ~LACP_STATE_EXPIRED;
1094169227Sthompsa	lgp->lp_flags |= LAGG_PORT_DISABLED;
1095168561Sthompsa}
1096168561Sthompsa
1097168561Sthompsa/*
1098168561Sthompsa * lacp_select: select an aggregator.  create one if necessary.
1099168561Sthompsa */
1100168561Sthompsastatic void
1101168561Sthompsalacp_select(struct lacp_port *lp)
1102168561Sthompsa{
1103168561Sthompsa	struct lacp_softc *lsc = lp->lp_lsc;
1104168561Sthompsa	struct lacp_aggregator *la;
1105168561Sthompsa#if defined(LACP_DEBUG)
1106168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
1107168561Sthompsa#endif
1108168561Sthompsa
1109168561Sthompsa	if (lp->lp_aggregator) {
1110168561Sthompsa		return;
1111168561Sthompsa	}
1112168561Sthompsa
1113168561Sthompsa	KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE),
1114168561Sthompsa	    ("timer_wait_while still active"));
1115168561Sthompsa
1116168561Sthompsa	LACP_DPRINTF((lp, "port lagid=%s\n",
1117168561Sthompsa	    lacp_format_lagid(&lp->lp_actor, &lp->lp_partner,
1118168561Sthompsa	    buf, sizeof(buf))));
1119168561Sthompsa
1120168561Sthompsa	TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) {
1121168561Sthompsa		if (lacp_aggregator_is_compatible(la, lp)) {
1122168561Sthompsa			break;
1123168561Sthompsa		}
1124168561Sthompsa	}
1125168561Sthompsa
1126168561Sthompsa	if (la == NULL) {
1127168561Sthompsa		la = lacp_aggregator_get(lsc, lp);
1128168561Sthompsa		if (la == NULL) {
1129168561Sthompsa			LACP_DPRINTF((lp, "aggregator creation failed\n"));
1130168561Sthompsa
1131168561Sthompsa			/*
1132168561Sthompsa			 * will retry on the next tick.
1133168561Sthompsa			 */
1134168561Sthompsa
1135168561Sthompsa			return;
1136168561Sthompsa		}
1137168561Sthompsa		lacp_fill_aggregator_id(la, lp);
1138168561Sthompsa		LACP_DPRINTF((lp, "aggregator created\n"));
1139168561Sthompsa	} else {
1140168561Sthompsa		LACP_DPRINTF((lp, "compatible aggregator found\n"));
1141168561Sthompsa		lacp_aggregator_addref(lsc, la);
1142168561Sthompsa	}
1143168561Sthompsa
1144168561Sthompsa	LACP_DPRINTF((lp, "aggregator lagid=%s\n",
1145168561Sthompsa	    lacp_format_lagid(&la->la_actor, &la->la_partner,
1146168561Sthompsa	    buf, sizeof(buf))));
1147168561Sthompsa
1148168561Sthompsa	lp->lp_aggregator = la;
1149168561Sthompsa	lp->lp_selected = LACP_SELECTED;
1150168561Sthompsa}
1151168561Sthompsa
1152168561Sthompsa/*
1153168561Sthompsa * lacp_unselect: finish unselect/detach process.
1154168561Sthompsa */
1155168561Sthompsa
1156168561Sthompsastatic void
1157168561Sthompsalacp_unselect(struct lacp_port *lp)
1158168561Sthompsa{
1159168561Sthompsa	struct lacp_softc *lsc = lp->lp_lsc;
1160168561Sthompsa	struct lacp_aggregator *la = lp->lp_aggregator;
1161168561Sthompsa
1162168561Sthompsa	KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE),
1163168561Sthompsa	    ("timer_wait_while still active"));
1164168561Sthompsa
1165168561Sthompsa	if (la == NULL) {
1166168561Sthompsa		return;
1167168561Sthompsa	}
1168168561Sthompsa
1169168561Sthompsa	lp->lp_aggregator = NULL;
1170168561Sthompsa	lacp_aggregator_delref(lsc, la);
1171168561Sthompsa}
1172168561Sthompsa
1173168561Sthompsa/* mux machine */
1174168561Sthompsa
1175168561Sthompsastatic void
1176168561Sthompsalacp_sm_mux(struct lacp_port *lp)
1177168561Sthompsa{
1178168561Sthompsa	enum lacp_mux_state new_state;
1179168561Sthompsa	boolean_t p_sync =
1180168561Sthompsa		    (lp->lp_partner.lip_state & LACP_STATE_SYNC) != 0;
1181168561Sthompsa	boolean_t p_collecting =
1182168561Sthompsa	    (lp->lp_partner.lip_state & LACP_STATE_COLLECTING) != 0;
1183168561Sthompsa	enum lacp_selected selected = lp->lp_selected;
1184168561Sthompsa	struct lacp_aggregator *la;
1185168561Sthompsa
1186168561Sthompsa	/* LACP_DPRINTF((lp, "%s: state %d\n", __func__, lp->lp_mux_state)); */
1187168561Sthompsa
1188168561Sthompsare_eval:
1189168561Sthompsa	la = lp->lp_aggregator;
1190168561Sthompsa	KASSERT(lp->lp_mux_state == LACP_MUX_DETACHED || la != NULL,
1191168561Sthompsa	    ("MUX not detached"));
1192168561Sthompsa	new_state = lp->lp_mux_state;
1193168561Sthompsa	switch (lp->lp_mux_state) {
1194168561Sthompsa	case LACP_MUX_DETACHED:
1195168561Sthompsa		if (selected != LACP_UNSELECTED) {
1196168561Sthompsa			new_state = LACP_MUX_WAITING;
1197168561Sthompsa		}
1198168561Sthompsa		break;
1199168561Sthompsa	case LACP_MUX_WAITING:
1200168561Sthompsa		KASSERT(la->la_pending > 0 ||
1201168561Sthompsa		    !LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE),
1202168561Sthompsa		    ("timer_wait_while still active"));
1203168561Sthompsa		if (selected == LACP_SELECTED && la->la_pending == 0) {
1204168561Sthompsa			new_state = LACP_MUX_ATTACHED;
1205168561Sthompsa		} else if (selected == LACP_UNSELECTED) {
1206168561Sthompsa			new_state = LACP_MUX_DETACHED;
1207168561Sthompsa		}
1208168561Sthompsa		break;
1209168561Sthompsa	case LACP_MUX_ATTACHED:
1210168561Sthompsa		if (selected == LACP_SELECTED && p_sync) {
1211168561Sthompsa			new_state = LACP_MUX_COLLECTING;
1212168561Sthompsa		} else if (selected != LACP_SELECTED) {
1213168561Sthompsa			new_state = LACP_MUX_DETACHED;
1214168561Sthompsa		}
1215168561Sthompsa		break;
1216168561Sthompsa	case LACP_MUX_COLLECTING:
1217168561Sthompsa		if (selected == LACP_SELECTED && p_sync && p_collecting) {
1218168561Sthompsa			new_state = LACP_MUX_DISTRIBUTING;
1219168561Sthompsa		} else if (selected != LACP_SELECTED || !p_sync) {
1220168561Sthompsa			new_state = LACP_MUX_ATTACHED;
1221168561Sthompsa		}
1222168561Sthompsa		break;
1223168561Sthompsa	case LACP_MUX_DISTRIBUTING:
1224168561Sthompsa		if (selected != LACP_SELECTED || !p_sync || !p_collecting) {
1225168561Sthompsa			new_state = LACP_MUX_COLLECTING;
1226168561Sthompsa		}
1227168561Sthompsa		break;
1228168561Sthompsa	default:
1229168561Sthompsa		panic("%s: unknown state", __func__);
1230168561Sthompsa	}
1231168561Sthompsa
1232168561Sthompsa	if (lp->lp_mux_state == new_state) {
1233168561Sthompsa		return;
1234168561Sthompsa	}
1235168561Sthompsa
1236168561Sthompsa	lacp_set_mux(lp, new_state);
1237168561Sthompsa	goto re_eval;
1238168561Sthompsa}
1239168561Sthompsa
1240168561Sthompsastatic void
1241168561Sthompsalacp_set_mux(struct lacp_port *lp, enum lacp_mux_state new_state)
1242168561Sthompsa{
1243168561Sthompsa	struct lacp_aggregator *la = lp->lp_aggregator;
1244168561Sthompsa
1245168561Sthompsa	if (lp->lp_mux_state == new_state) {
1246168561Sthompsa		return;
1247168561Sthompsa	}
1248168561Sthompsa
1249168561Sthompsa	switch (new_state) {
1250168561Sthompsa	case LACP_MUX_DETACHED:
1251168561Sthompsa		lp->lp_state &= ~LACP_STATE_SYNC;
1252168561Sthompsa		lacp_disable_distributing(lp);
1253168561Sthompsa		lacp_disable_collecting(lp);
1254168561Sthompsa		lacp_sm_assert_ntt(lp);
1255168561Sthompsa		/* cancel timer */
1256168561Sthompsa		if (LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE)) {
1257168561Sthompsa			KASSERT(la->la_pending > 0,
1258168561Sthompsa			    ("timer_wait_while not active"));
1259168561Sthompsa			la->la_pending--;
1260168561Sthompsa		}
1261168561Sthompsa		LACP_TIMER_DISARM(lp, LACP_TIMER_WAIT_WHILE);
1262168561Sthompsa		lacp_unselect(lp);
1263168561Sthompsa		break;
1264168561Sthompsa	case LACP_MUX_WAITING:
1265168561Sthompsa		LACP_TIMER_ARM(lp, LACP_TIMER_WAIT_WHILE,
1266168561Sthompsa		    LACP_AGGREGATE_WAIT_TIME);
1267168561Sthompsa		la->la_pending++;
1268168561Sthompsa		break;
1269168561Sthompsa	case LACP_MUX_ATTACHED:
1270168561Sthompsa		lp->lp_state |= LACP_STATE_SYNC;
1271168561Sthompsa		lacp_disable_collecting(lp);
1272168561Sthompsa		lacp_sm_assert_ntt(lp);
1273168561Sthompsa		break;
1274168561Sthompsa	case LACP_MUX_COLLECTING:
1275168561Sthompsa		lacp_enable_collecting(lp);
1276168561Sthompsa		lacp_disable_distributing(lp);
1277168561Sthompsa		lacp_sm_assert_ntt(lp);
1278168561Sthompsa		break;
1279168561Sthompsa	case LACP_MUX_DISTRIBUTING:
1280168561Sthompsa		lacp_enable_distributing(lp);
1281168561Sthompsa		break;
1282168561Sthompsa	default:
1283168561Sthompsa		panic("%s: unknown state", __func__);
1284168561Sthompsa	}
1285168561Sthompsa
1286168561Sthompsa	LACP_DPRINTF((lp, "mux_state %d -> %d\n", lp->lp_mux_state, new_state));
1287168561Sthompsa
1288168561Sthompsa	lp->lp_mux_state = new_state;
1289168561Sthompsa}
1290168561Sthompsa
1291168561Sthompsastatic void
1292168561Sthompsalacp_sm_mux_timer(struct lacp_port *lp)
1293168561Sthompsa{
1294168561Sthompsa	struct lacp_aggregator *la = lp->lp_aggregator;
1295168561Sthompsa#if defined(LACP_DEBUG)
1296168561Sthompsa	char buf[LACP_LAGIDSTR_MAX+1];
1297168561Sthompsa#endif
1298168561Sthompsa
1299168561Sthompsa	KASSERT(la->la_pending > 0, ("no pending event"));
1300168561Sthompsa
1301168561Sthompsa	LACP_DPRINTF((lp, "%s: aggregator %s, pending %d -> %d\n", __func__,
1302168561Sthompsa	    lacp_format_lagid(&la->la_actor, &la->la_partner,
1303168561Sthompsa	    buf, sizeof(buf)),
1304168561Sthompsa	    la->la_pending, la->la_pending - 1));
1305168561Sthompsa
1306168561Sthompsa	la->la_pending--;
1307168561Sthompsa}
1308168561Sthompsa
1309168561Sthompsa/* periodic transmit machine */
1310168561Sthompsa
1311168561Sthompsastatic void
1312168561Sthompsalacp_sm_ptx_update_timeout(struct lacp_port *lp, uint8_t oldpstate)
1313168561Sthompsa{
1314168561Sthompsa	if (LACP_STATE_EQ(oldpstate, lp->lp_partner.lip_state,
1315168561Sthompsa	    LACP_STATE_TIMEOUT)) {
1316168561Sthompsa		return;
1317168561Sthompsa	}
1318168561Sthompsa
1319168561Sthompsa	LACP_DPRINTF((lp, "partner timeout changed\n"));
1320168561Sthompsa
1321168561Sthompsa	/*
1322168561Sthompsa	 * FAST_PERIODIC -> SLOW_PERIODIC
1323168561Sthompsa	 * or
1324168561Sthompsa	 * SLOW_PERIODIC (-> PERIODIC_TX) -> FAST_PERIODIC
1325168561Sthompsa	 *
1326168561Sthompsa	 * let lacp_sm_ptx_tx_schedule to update timeout.
1327168561Sthompsa	 */
1328168561Sthompsa
1329168561Sthompsa	LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC);
1330168561Sthompsa
1331168561Sthompsa	/*
1332168561Sthompsa	 * if timeout has been shortened, assert NTT.
1333168561Sthompsa	 */
1334168561Sthompsa
1335168561Sthompsa	if ((lp->lp_partner.lip_state & LACP_STATE_TIMEOUT)) {
1336168561Sthompsa		lacp_sm_assert_ntt(lp);
1337168561Sthompsa	}
1338168561Sthompsa}
1339168561Sthompsa
1340168561Sthompsastatic void
1341168561Sthompsalacp_sm_ptx_tx_schedule(struct lacp_port *lp)
1342168561Sthompsa{
1343168561Sthompsa	int timeout;
1344168561Sthompsa
1345168561Sthompsa	if (!(lp->lp_state & LACP_STATE_ACTIVITY) &&
1346168561Sthompsa	    !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) {
1347168561Sthompsa
1348168561Sthompsa		/*
1349168561Sthompsa		 * NO_PERIODIC
1350168561Sthompsa		 */
1351168561Sthompsa
1352168561Sthompsa		LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC);
1353168561Sthompsa		return;
1354168561Sthompsa	}
1355168561Sthompsa
1356168561Sthompsa	if (LACP_TIMER_ISARMED(lp, LACP_TIMER_PERIODIC)) {
1357168561Sthompsa		return;
1358168561Sthompsa	}
1359168561Sthompsa
1360168561Sthompsa	timeout = (lp->lp_partner.lip_state & LACP_STATE_TIMEOUT) ?
1361168561Sthompsa	    LACP_FAST_PERIODIC_TIME : LACP_SLOW_PERIODIC_TIME;
1362168561Sthompsa
1363168561Sthompsa	LACP_TIMER_ARM(lp, LACP_TIMER_PERIODIC, timeout);
1364168561Sthompsa}
1365168561Sthompsa
1366168561Sthompsastatic void
1367168561Sthompsalacp_sm_ptx_timer(struct lacp_port *lp)
1368168561Sthompsa{
1369168561Sthompsa	lacp_sm_assert_ntt(lp);
1370168561Sthompsa}
1371168561Sthompsa
1372168561Sthompsastatic void
1373168561Sthompsalacp_sm_rx(struct lacp_port *lp, const struct lacpdu *du)
1374168561Sthompsa{
1375168561Sthompsa	int timeout;
1376168561Sthompsa
1377168561Sthompsa	/*
1378168561Sthompsa	 * check LACP_DISABLED first
1379168561Sthompsa	 */
1380168561Sthompsa
1381168561Sthompsa	if (!(lp->lp_state & LACP_STATE_AGGREGATION)) {
1382168561Sthompsa		return;
1383168561Sthompsa	}
1384168561Sthompsa
1385168561Sthompsa	/*
1386168561Sthompsa	 * check loopback condition.
1387168561Sthompsa	 */
1388168561Sthompsa
1389168561Sthompsa	if (!lacp_compare_systemid(&du->ldu_actor.lip_systemid,
1390168561Sthompsa	    &lp->lp_actor.lip_systemid)) {
1391168561Sthompsa		return;
1392168561Sthompsa	}
1393168561Sthompsa
1394168561Sthompsa	/*
1395168561Sthompsa	 * EXPIRED, DEFAULTED, CURRENT -> CURRENT
1396168561Sthompsa	 */
1397168561Sthompsa
1398168561Sthompsa	lacp_sm_rx_update_selected(lp, du);
1399168561Sthompsa	lacp_sm_rx_update_ntt(lp, du);
1400168561Sthompsa	lacp_sm_rx_record_pdu(lp, du);
1401168561Sthompsa
1402168561Sthompsa	timeout = (lp->lp_state & LACP_STATE_TIMEOUT) ?
1403168561Sthompsa	    LACP_SHORT_TIMEOUT_TIME : LACP_LONG_TIMEOUT_TIME;
1404168561Sthompsa	LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, timeout);
1405168561Sthompsa
1406168561Sthompsa	lp->lp_state &= ~LACP_STATE_EXPIRED;
1407168561Sthompsa
1408168561Sthompsa	/*
1409168561Sthompsa	 * kick transmit machine without waiting the next tick.
1410168561Sthompsa	 */
1411168561Sthompsa
1412168561Sthompsa	lacp_sm_tx(lp);
1413168561Sthompsa}
1414168561Sthompsa
1415168561Sthompsastatic void
1416168561Sthompsalacp_sm_rx_set_expired(struct lacp_port *lp)
1417168561Sthompsa{
1418168561Sthompsa	lp->lp_partner.lip_state &= ~LACP_STATE_SYNC;
1419168561Sthompsa	lp->lp_partner.lip_state |= LACP_STATE_TIMEOUT;
1420168561Sthompsa	LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, LACP_SHORT_TIMEOUT_TIME);
1421168561Sthompsa	lp->lp_state |= LACP_STATE_EXPIRED;
1422168561Sthompsa}
1423168561Sthompsa
1424168561Sthompsastatic void
1425168561Sthompsalacp_sm_rx_timer(struct lacp_port *lp)
1426168561Sthompsa{
1427168561Sthompsa	if ((lp->lp_state & LACP_STATE_EXPIRED) == 0) {
1428168561Sthompsa		/* CURRENT -> EXPIRED */
1429168561Sthompsa		LACP_DPRINTF((lp, "%s: CURRENT -> EXPIRED\n", __func__));
1430168561Sthompsa		lacp_sm_rx_set_expired(lp);
1431168561Sthompsa	} else {
1432168561Sthompsa		/* EXPIRED -> DEFAULTED */
1433168561Sthompsa		LACP_DPRINTF((lp, "%s: EXPIRED -> DEFAULTED\n", __func__));
1434168561Sthompsa		lacp_sm_rx_update_default_selected(lp);
1435168561Sthompsa		lacp_sm_rx_record_default(lp);
1436168561Sthompsa		lp->lp_state &= ~LACP_STATE_EXPIRED;
1437168561Sthompsa	}
1438168561Sthompsa}
1439168561Sthompsa
1440168561Sthompsastatic void
1441168561Sthompsalacp_sm_rx_record_pdu(struct lacp_port *lp, const struct lacpdu *du)
1442168561Sthompsa{
1443168561Sthompsa	boolean_t active;
1444168561Sthompsa	uint8_t oldpstate;
1445168561Sthompsa#if defined(LACP_DEBUG)
1446168561Sthompsa	char buf[LACP_STATESTR_MAX+1];
1447168561Sthompsa#endif
1448168561Sthompsa
1449168561Sthompsa	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
1450168561Sthompsa
1451168561Sthompsa	oldpstate = lp->lp_partner.lip_state;
1452168561Sthompsa
1453168561Sthompsa	active = (du->ldu_actor.lip_state & LACP_STATE_ACTIVITY)
1454168561Sthompsa	    || ((lp->lp_state & LACP_STATE_ACTIVITY) &&
1455168561Sthompsa	    (du->ldu_partner.lip_state & LACP_STATE_ACTIVITY));
1456168561Sthompsa
1457168561Sthompsa	lp->lp_partner = du->ldu_actor;
1458168561Sthompsa	if (active &&
1459168561Sthompsa	    ((LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state,
1460168561Sthompsa	    LACP_STATE_AGGREGATION) &&
1461168561Sthompsa	    !lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner))
1462168561Sthompsa	    || (du->ldu_partner.lip_state & LACP_STATE_AGGREGATION) == 0)) {
1463168561Sthompsa		/* XXX nothing? */
1464168561Sthompsa	} else {
1465168561Sthompsa		lp->lp_partner.lip_state &= ~LACP_STATE_SYNC;
1466168561Sthompsa	}
1467168561Sthompsa
1468168561Sthompsa	lp->lp_state &= ~LACP_STATE_DEFAULTED;
1469168561Sthompsa
1470168561Sthompsa	if (oldpstate != lp->lp_partner.lip_state) {
1471168561Sthompsa		LACP_DPRINTF((lp, "old pstate %s\n",
1472168561Sthompsa		    lacp_format_state(oldpstate, buf, sizeof(buf))));
1473168561Sthompsa		LACP_DPRINTF((lp, "new pstate %s\n",
1474168561Sthompsa		    lacp_format_state(lp->lp_partner.lip_state, buf,
1475168561Sthompsa		    sizeof(buf))));
1476168561Sthompsa	}
1477168561Sthompsa
1478168561Sthompsa	lacp_sm_ptx_update_timeout(lp, oldpstate);
1479168561Sthompsa}
1480168561Sthompsa
1481168561Sthompsastatic void
1482168561Sthompsalacp_sm_rx_update_ntt(struct lacp_port *lp, const struct lacpdu *du)
1483168561Sthompsa{
1484168561Sthompsa	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
1485168561Sthompsa
1486168561Sthompsa	if (lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner) ||
1487168561Sthompsa	    !LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state,
1488168561Sthompsa	    LACP_STATE_ACTIVITY | LACP_STATE_SYNC | LACP_STATE_AGGREGATION)) {
1489168561Sthompsa		LACP_DPRINTF((lp, "%s: assert ntt\n", __func__));
1490168561Sthompsa		lacp_sm_assert_ntt(lp);
1491168561Sthompsa	}
1492168561Sthompsa}
1493168561Sthompsa
1494168561Sthompsastatic void
1495168561Sthompsalacp_sm_rx_record_default(struct lacp_port *lp)
1496168561Sthompsa{
1497168561Sthompsa	uint8_t oldpstate;
1498168561Sthompsa
1499168561Sthompsa	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
1500168561Sthompsa
1501168561Sthompsa	oldpstate = lp->lp_partner.lip_state;
1502168561Sthompsa	lp->lp_partner = lacp_partner_admin;
1503168561Sthompsa	lp->lp_state |= LACP_STATE_DEFAULTED;
1504168561Sthompsa	lacp_sm_ptx_update_timeout(lp, oldpstate);
1505168561Sthompsa}
1506168561Sthompsa
1507168561Sthompsastatic void
1508168561Sthompsalacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *lp,
1509168561Sthompsa    const struct lacp_peerinfo *info)
1510168561Sthompsa{
1511168561Sthompsa	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
1512168561Sthompsa
1513168561Sthompsa	if (lacp_compare_peerinfo(&lp->lp_partner, info) ||
1514168561Sthompsa	    !LACP_STATE_EQ(lp->lp_partner.lip_state, info->lip_state,
1515168561Sthompsa	    LACP_STATE_AGGREGATION)) {
1516168561Sthompsa		lp->lp_selected = LACP_UNSELECTED;
1517168561Sthompsa		/* mux machine will clean up lp->lp_aggregator */
1518168561Sthompsa	}
1519168561Sthompsa}
1520168561Sthompsa
1521168561Sthompsastatic void
1522168561Sthompsalacp_sm_rx_update_selected(struct lacp_port *lp, const struct lacpdu *du)
1523168561Sthompsa{
1524168561Sthompsa	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
1525168561Sthompsa
1526168561Sthompsa	lacp_sm_rx_update_selected_from_peerinfo(lp, &du->ldu_actor);
1527168561Sthompsa}
1528168561Sthompsa
1529168561Sthompsastatic void
1530168561Sthompsalacp_sm_rx_update_default_selected(struct lacp_port *lp)
1531168561Sthompsa{
1532168561Sthompsa	/* LACP_DPRINTF((lp, "%s\n", __func__)); */
1533168561Sthompsa
1534168561Sthompsa	lacp_sm_rx_update_selected_from_peerinfo(lp, &lacp_partner_admin);
1535168561Sthompsa}
1536168561Sthompsa
1537168561Sthompsa/* transmit machine */
1538168561Sthompsa
1539168561Sthompsastatic void
1540168561Sthompsalacp_sm_tx(struct lacp_port *lp)
1541168561Sthompsa{
1542168561Sthompsa	int error;
1543168561Sthompsa
1544168561Sthompsa	if (!(lp->lp_state & LACP_STATE_AGGREGATION)
1545168561Sthompsa#if 1
1546168561Sthompsa	    || (!(lp->lp_state & LACP_STATE_ACTIVITY)
1547168561Sthompsa	    && !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY))
1548168561Sthompsa#endif
1549168561Sthompsa	    ) {
1550168561Sthompsa		lp->lp_flags &= ~LACP_PORT_NTT;
1551168561Sthompsa	}
1552168561Sthompsa
1553168561Sthompsa	if (!(lp->lp_flags & LACP_PORT_NTT)) {
1554168561Sthompsa		return;
1555168561Sthompsa	}
1556168561Sthompsa
1557168561Sthompsa	/* Rate limit to 3 PDUs per LACP_FAST_PERIODIC_TIME */
1558168561Sthompsa	if (ppsratecheck(&lp->lp_last_lacpdu, &lp->lp_lacpdu_sent,
1559168561Sthompsa		    (3 / LACP_FAST_PERIODIC_TIME)) == 0) {
1560168561Sthompsa		LACP_DPRINTF((lp, "rate limited pdu\n"));
1561168561Sthompsa		return;
1562168561Sthompsa	}
1563168561Sthompsa
1564168561Sthompsa	error = lacp_xmit_lacpdu(lp);
1565168561Sthompsa
1566168561Sthompsa	if (error == 0) {
1567168561Sthompsa		lp->lp_flags &= ~LACP_PORT_NTT;
1568168561Sthompsa	} else {
1569168561Sthompsa		LACP_DPRINTF((lp, "lacpdu transmit failure, error %d\n",
1570168561Sthompsa		    error));
1571168561Sthompsa	}
1572168561Sthompsa}
1573168561Sthompsa
1574168561Sthompsastatic void
1575168561Sthompsalacp_sm_assert_ntt(struct lacp_port *lp)
1576168561Sthompsa{
1577168561Sthompsa
1578168561Sthompsa	lp->lp_flags |= LACP_PORT_NTT;
1579168561Sthompsa}
1580168561Sthompsa
1581168561Sthompsastatic void
1582168561Sthompsalacp_run_timers(struct lacp_port *lp)
1583168561Sthompsa{
1584168561Sthompsa	int i;
1585168561Sthompsa
1586168561Sthompsa	for (i = 0; i < LACP_NTIMER; i++) {
1587168561Sthompsa		KASSERT(lp->lp_timer[i] >= 0,
1588168561Sthompsa		    ("invalid timer value %d", lp->lp_timer[i]));
1589168561Sthompsa		if (lp->lp_timer[i] == 0) {
1590168561Sthompsa			continue;
1591168561Sthompsa		} else if (--lp->lp_timer[i] <= 0) {
1592168561Sthompsa			if (lacp_timer_funcs[i]) {
1593168561Sthompsa				(*lacp_timer_funcs[i])(lp);
1594168561Sthompsa			}
1595168561Sthompsa		}
1596168561Sthompsa	}
1597168561Sthompsa}
1598168561Sthompsa
1599168561Sthompsaint
1600168793Sthompsalacp_marker_input(struct lagg_port *lgp, struct mbuf *m)
1601168561Sthompsa{
1602168793Sthompsa	struct lacp_port *lp = LACP_PORT(lgp);
1603168561Sthompsa	struct markerdu *mdu;
1604168561Sthompsa	int error = 0;
1605168561Sthompsa
1606169569Sthompsa	LAGG_RLOCK_ASSERT(lgp->lp_lagg);
1607168561Sthompsa
1608168561Sthompsa	if (__predict_false(lp->lp_flags & LACP_PORT_DETACHING)) {
1609168561Sthompsa		goto bad;
1610168561Sthompsa	}
1611168561Sthompsa
1612168561Sthompsa	if (m->m_pkthdr.len != sizeof(*mdu)) {
1613168561Sthompsa		goto bad;
1614168561Sthompsa	}
1615168561Sthompsa
1616168561Sthompsa	if ((m->m_flags & M_MCAST) == 0) {
1617168561Sthompsa		goto bad;
1618168561Sthompsa	}
1619168561Sthompsa
1620168561Sthompsa	if (m->m_len < sizeof(*mdu)) {
1621168561Sthompsa		m = m_pullup(m, sizeof(*mdu));
1622168561Sthompsa		if (m == NULL) {
1623168561Sthompsa			return (ENOMEM);
1624168561Sthompsa		}
1625168561Sthompsa	}
1626168561Sthompsa
1627168561Sthompsa	mdu = mtod(m, struct markerdu *);
1628168561Sthompsa
1629168561Sthompsa	if (memcmp(&mdu->mdu_eh.ether_dhost,
1630168561Sthompsa	    &ethermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) {
1631168561Sthompsa		goto bad;
1632168561Sthompsa	}
1633168561Sthompsa
1634168561Sthompsa	/* XXX
1635168561Sthompsa	KASSERT(mdu->mdu_sph.sph_subtype == SLOWPROTOCOLS_SUBTYPE_MARKER,
1636168561Sthompsa	    ("a very bad kassert!"));
1637168561Sthompsa	*/
1638168561Sthompsa
1639168561Sthompsa	if (mdu->mdu_sph.sph_version != 1) {
1640168561Sthompsa		goto bad;
1641168561Sthompsa	}
1642168561Sthompsa
1643168561Sthompsa	switch (mdu->mdu_tlv.tlv_type) {
1644168561Sthompsa	case MARKER_TYPE_INFO:
1645168561Sthompsa		if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv,
1646168561Sthompsa		    marker_info_tlv_template, TRUE)) {
1647168561Sthompsa			goto bad;
1648168561Sthompsa		}
1649168561Sthompsa		mdu->mdu_tlv.tlv_type = MARKER_TYPE_RESPONSE;
1650168561Sthompsa		memcpy(&mdu->mdu_eh.ether_dhost,
1651168561Sthompsa		    &ethermulticastaddr_slowprotocols, ETHER_ADDR_LEN);
1652168561Sthompsa		memcpy(&mdu->mdu_eh.ether_shost,
1653168793Sthompsa		    lgp->lp_lladdr, ETHER_ADDR_LEN);
1654168793Sthompsa		error = lagg_enqueue(lp->lp_ifp, m);
1655168561Sthompsa		break;
1656168561Sthompsa
1657168561Sthompsa	case MARKER_TYPE_RESPONSE:
1658168561Sthompsa		if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv,
1659168561Sthompsa		    marker_response_tlv_template, TRUE)) {
1660168561Sthompsa			goto bad;
1661168561Sthompsa		}
1662168561Sthompsa		/*
1663168561Sthompsa		 * we are not interested in responses as
1664168561Sthompsa		 * we don't have a marker sender.
1665168561Sthompsa		 */
1666168561Sthompsa		/* FALLTHROUGH */
1667168561Sthompsa	default:
1668168561Sthompsa		goto bad;
1669168561Sthompsa	}
1670168561Sthompsa
1671168561Sthompsa	return (error);
1672168561Sthompsa
1673168561Sthompsabad:
1674168561Sthompsa	m_freem(m);
1675168561Sthompsa	return (EINVAL);
1676168561Sthompsa}
1677168561Sthompsa
1678168561Sthompsastatic int
1679168561Sthompsatlv_check(const void *p, size_t size, const struct tlvhdr *tlv,
1680168561Sthompsa    const struct tlv_template *tmpl, boolean_t check_type)
1681168561Sthompsa{
1682168561Sthompsa	while (/* CONSTCOND */ 1) {
1683168561Sthompsa		if ((const char *)tlv - (const char *)p + sizeof(*tlv) > size) {
1684168561Sthompsa			return (EINVAL);
1685168561Sthompsa		}
1686168561Sthompsa		if ((check_type && tlv->tlv_type != tmpl->tmpl_type) ||
1687168561Sthompsa		    tlv->tlv_length != tmpl->tmpl_length) {
1688168561Sthompsa			return (EINVAL);
1689168561Sthompsa		}
1690168561Sthompsa		if (tmpl->tmpl_type == 0) {
1691168561Sthompsa			break;
1692168561Sthompsa		}
1693168561Sthompsa		tlv = (const struct tlvhdr *)
1694168561Sthompsa		    ((const char *)tlv + tlv->tlv_length);
1695168561Sthompsa		tmpl++;
1696168561Sthompsa	}
1697168561Sthompsa
1698168561Sthompsa	return (0);
1699168561Sthompsa}
1700168561Sthompsa
1701168561Sthompsa#if defined(LACP_DEBUG)
1702168561Sthompsaconst char *
1703168561Sthompsalacp_format_mac(const uint8_t *mac, char *buf, size_t buflen)
1704168561Sthompsa{
1705168561Sthompsa	snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X",
1706168561Sthompsa	    (int)mac[0],
1707168561Sthompsa	    (int)mac[1],
1708168561Sthompsa	    (int)mac[2],
1709168561Sthompsa	    (int)mac[3],
1710168561Sthompsa	    (int)mac[4],
1711168561Sthompsa	    (int)mac[5]);
1712168561Sthompsa
1713168561Sthompsa	return (buf);
1714168561Sthompsa}
1715168561Sthompsa
1716168561Sthompsaconst char *
1717168561Sthompsalacp_format_systemid(const struct lacp_systemid *sysid,
1718168561Sthompsa    char *buf, size_t buflen)
1719168561Sthompsa{
1720168561Sthompsa	char macbuf[LACP_MACSTR_MAX+1];
1721168561Sthompsa
1722168561Sthompsa	snprintf(buf, buflen, "%04X,%s",
1723168561Sthompsa	    ntohs(sysid->lsi_prio),
1724168561Sthompsa	    lacp_format_mac(sysid->lsi_mac, macbuf, sizeof(macbuf)));
1725168561Sthompsa
1726168561Sthompsa	return (buf);
1727168561Sthompsa}
1728168561Sthompsa
1729168561Sthompsaconst char *
1730168561Sthompsalacp_format_portid(const struct lacp_portid *portid, char *buf, size_t buflen)
1731168561Sthompsa{
1732168561Sthompsa	snprintf(buf, buflen, "%04X,%04X",
1733168561Sthompsa	    ntohs(portid->lpi_prio),
1734168561Sthompsa	    ntohs(portid->lpi_portno));
1735168561Sthompsa
1736168561Sthompsa	return (buf);
1737168561Sthompsa}
1738168561Sthompsa
1739168561Sthompsaconst char *
1740168561Sthompsalacp_format_partner(const struct lacp_peerinfo *peer, char *buf, size_t buflen)
1741168561Sthompsa{
1742168561Sthompsa	char sysid[LACP_SYSTEMIDSTR_MAX+1];
1743168561Sthompsa	char portid[LACP_PORTIDSTR_MAX+1];
1744168561Sthompsa
1745168561Sthompsa	snprintf(buf, buflen, "(%s,%04X,%s)",
1746168561Sthompsa	    lacp_format_systemid(&peer->lip_systemid, sysid, sizeof(sysid)),
1747168561Sthompsa	    ntohs(peer->lip_key),
1748168561Sthompsa	    lacp_format_portid(&peer->lip_portid, portid, sizeof(portid)));
1749168561Sthompsa
1750168561Sthompsa	return (buf);
1751168561Sthompsa}
1752168561Sthompsa
1753168561Sthompsaconst char *
1754168561Sthompsalacp_format_lagid(const struct lacp_peerinfo *a,
1755168561Sthompsa    const struct lacp_peerinfo *b, char *buf, size_t buflen)
1756168561Sthompsa{
1757168561Sthompsa	char astr[LACP_PARTNERSTR_MAX+1];
1758168561Sthompsa	char bstr[LACP_PARTNERSTR_MAX+1];
1759168561Sthompsa
1760168561Sthompsa#if 0
1761168561Sthompsa	/*
1762168561Sthompsa	 * there's a convention to display small numbered peer
1763168561Sthompsa	 * in the left.
1764168561Sthompsa	 */
1765168561Sthompsa
1766168561Sthompsa	if (lacp_compare_peerinfo(a, b) > 0) {
1767168561Sthompsa		const struct lacp_peerinfo *t;
1768168561Sthompsa
1769168561Sthompsa		t = a;
1770168561Sthompsa		a = b;
1771168561Sthompsa		b = t;
1772168561Sthompsa	}
1773168561Sthompsa#endif
1774168561Sthompsa
1775168561Sthompsa	snprintf(buf, buflen, "[%s,%s]",
1776168561Sthompsa	    lacp_format_partner(a, astr, sizeof(astr)),
1777168561Sthompsa	    lacp_format_partner(b, bstr, sizeof(bstr)));
1778168561Sthompsa
1779168561Sthompsa	return (buf);
1780168561Sthompsa}
1781168561Sthompsa
1782168561Sthompsaconst char *
1783168561Sthompsalacp_format_lagid_aggregator(const struct lacp_aggregator *la,
1784168561Sthompsa    char *buf, size_t buflen)
1785168561Sthompsa{
1786168561Sthompsa	if (la == NULL) {
1787168561Sthompsa		return ("(none)");
1788168561Sthompsa	}
1789168561Sthompsa
1790168561Sthompsa	return (lacp_format_lagid(&la->la_actor, &la->la_partner, buf, buflen));
1791168561Sthompsa}
1792168561Sthompsa
1793168561Sthompsaconst char *
1794168561Sthompsalacp_format_state(uint8_t state, char *buf, size_t buflen)
1795168561Sthompsa{
1796168561Sthompsa	snprintf(buf, buflen, "%b", state, LACP_STATE_BITS);
1797168561Sthompsa	return (buf);
1798168561Sthompsa}
1799168561Sthompsa
1800168561Sthompsastatic void
1801168561Sthompsalacp_dump_lacpdu(const struct lacpdu *du)
1802168561Sthompsa{
1803168561Sthompsa	char buf[LACP_PARTNERSTR_MAX+1];
1804168561Sthompsa	char buf2[LACP_STATESTR_MAX+1];
1805168561Sthompsa
1806168561Sthompsa	printf("actor=%s\n",
1807168561Sthompsa	    lacp_format_partner(&du->ldu_actor, buf, sizeof(buf)));
1808168561Sthompsa	printf("actor.state=%s\n",
1809168561Sthompsa	    lacp_format_state(du->ldu_actor.lip_state, buf2, sizeof(buf2)));
1810168561Sthompsa	printf("partner=%s\n",
1811168561Sthompsa	    lacp_format_partner(&du->ldu_partner, buf, sizeof(buf)));
1812168561Sthompsa	printf("partner.state=%s\n",
1813168561Sthompsa	    lacp_format_state(du->ldu_partner.lip_state, buf2, sizeof(buf2)));
1814168561Sthompsa
1815168561Sthompsa	printf("maxdelay=%d\n", ntohs(du->ldu_collector.lci_maxdelay));
1816168561Sthompsa}
1817168561Sthompsa
1818168561Sthompsastatic void
1819168561Sthompsalacp_dprintf(const struct lacp_port *lp, const char *fmt, ...)
1820168561Sthompsa{
1821168561Sthompsa	va_list va;
1822168561Sthompsa
1823168561Sthompsa	if (lp) {
1824168561Sthompsa		printf("%s: ", lp->lp_ifp->if_xname);
1825168561Sthompsa	}
1826168561Sthompsa
1827168561Sthompsa	va_start(va, fmt);
1828168561Sthompsa	vprintf(fmt, va);
1829168561Sthompsa	va_end(va);
1830168561Sthompsa}
1831168561Sthompsa#endif
1832