xref: /titanic_50/usr/src/lib/libdladm/common/libdlaggr.c (revision da14cebe459d3275048785f25bd869cb09b5307f)
1f595a68aSyz147064 /*
2f595a68aSyz147064  * CDDL HEADER START
3f595a68aSyz147064  *
4f595a68aSyz147064  * The contents of this file are subject to the terms of the
5f595a68aSyz147064  * Common Development and Distribution License (the "License").
6f595a68aSyz147064  * You may not use this file except in compliance with the License.
7f595a68aSyz147064  *
8f595a68aSyz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9f595a68aSyz147064  * or http://www.opensolaris.org/os/licensing.
10f595a68aSyz147064  * See the License for the specific language governing permissions
11f595a68aSyz147064  * and limitations under the License.
12f595a68aSyz147064  *
13f595a68aSyz147064  * When distributing Covered Code, include this CDDL HEADER in each
14f595a68aSyz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15f595a68aSyz147064  * If applicable, add the following below this CDDL HEADER, with the
16f595a68aSyz147064  * fields enclosed by brackets "[]" replaced with your own identifying
17f595a68aSyz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
18f595a68aSyz147064  *
19f595a68aSyz147064  * CDDL HEADER END
20f595a68aSyz147064  */
21f595a68aSyz147064 /*
22d62bc4baSyz147064  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23f595a68aSyz147064  * Use is subject to license terms.
24f595a68aSyz147064  */
25f595a68aSyz147064 
26f595a68aSyz147064 #include <stdio.h>
27f595a68aSyz147064 #include <sys/types.h>
28f595a68aSyz147064 #include <sys/stat.h>
29f595a68aSyz147064 #include <string.h>
30f595a68aSyz147064 #include <fcntl.h>
31f595a68aSyz147064 #include <unistd.h>
32f595a68aSyz147064 #include <stropts.h>
33f595a68aSyz147064 #include <stdlib.h>
34f595a68aSyz147064 #include <errno.h>
35d62bc4baSyz147064 #include <assert.h>
36f595a68aSyz147064 #include <strings.h>
37f595a68aSyz147064 #include <libintl.h>
38f595a68aSyz147064 #include <net/if_types.h>
39f595a68aSyz147064 #include <net/if_dl.h>
40*da14cebeSEric Cheng #include <sys/dld.h>
41d62bc4baSyz147064 #include <libdllink.h>
42d62bc4baSyz147064 #include <libdlvlan.h>
43f595a68aSyz147064 #include <libdlaggr.h>
44f595a68aSyz147064 #include <libdladm_impl.h>
45f595a68aSyz147064 
46f595a68aSyz147064 /*
47f595a68aSyz147064  * Link Aggregation Administration Library.
48f595a68aSyz147064  *
49f595a68aSyz147064  * This library is used by administration tools such as dladm(1M) to
50f595a68aSyz147064  * configure link aggregations.
51f595a68aSyz147064  */
52f595a68aSyz147064 
53f595a68aSyz147064 /* Limits on buffer size for LAIOC_INFO request */
54f595a68aSyz147064 #define	MIN_INFO_SIZE (4*1024)
55f595a68aSyz147064 #define	MAX_INFO_SIZE (128*1024)
56f595a68aSyz147064 
57f595a68aSyz147064 static uchar_t	zero_mac[] = {0, 0, 0, 0, 0, 0};
58d62bc4baSyz147064 #define	VALID_PORT_MAC(mac)						\
59d62bc4baSyz147064 	(((mac) != NULL) && (bcmp(zero_mac, (mac), ETHERADDRL) != 0) &&	\
60d62bc4baSyz147064 	(!(mac)[0] & 0x01))
61f595a68aSyz147064 
62d62bc4baSyz147064 #define	PORT_DELIMITER	'.'
63f595a68aSyz147064 
64d62bc4baSyz147064 #define	WRITE_PORT(portstr, portid, size) {			\
65d62bc4baSyz147064 	char pstr[LINKID_STR_WIDTH + 2];			\
66d62bc4baSyz147064 	(void) snprintf(pstr, LINKID_STR_WIDTH + 2, "%d%c",	\
67d62bc4baSyz147064 	    (portid), PORT_DELIMITER);				\
68d62bc4baSyz147064 	(void) strlcat((portstr), pstr, (size));		\
69d62bc4baSyz147064 }
70f595a68aSyz147064 
71d62bc4baSyz147064 #define	READ_PORT(portstr, portid, status) {			\
72d62bc4baSyz147064 	errno = 0;						\
73d62bc4baSyz147064 	(status) = DLADM_STATUS_OK;				\
74d62bc4baSyz147064 	(portid) = (int)strtol((portstr), &(portstr), 10);	\
75d62bc4baSyz147064 	if (errno != 0 || *(portstr) != PORT_DELIMITER) {	\
76d62bc4baSyz147064 		(status) = DLADM_STATUS_REPOSITORYINVAL;	\
77d62bc4baSyz147064 	} else {						\
78d62bc4baSyz147064 		/* Skip the delimiter. */			\
79d62bc4baSyz147064 		(portstr)++;					\
80d62bc4baSyz147064 	}							\
81d62bc4baSyz147064 }
82f595a68aSyz147064 
83f595a68aSyz147064 typedef struct dladm_aggr_modify_attr {
84f595a68aSyz147064 	uint32_t	ld_policy;
85f595a68aSyz147064 	boolean_t	ld_mac_fixed;
86f595a68aSyz147064 	uchar_t		ld_mac[ETHERADDRL];
87f595a68aSyz147064 	aggr_lacp_mode_t ld_lacp_mode;
88f595a68aSyz147064 	aggr_lacp_timer_t ld_lacp_timer;
89f595a68aSyz147064 } dladm_aggr_modify_attr_t;
90f595a68aSyz147064 
91f595a68aSyz147064 typedef struct policy_s {
92f595a68aSyz147064 	char		*pol_name;
93f595a68aSyz147064 	uint32_t	policy;
94f595a68aSyz147064 } policy_t;
95f595a68aSyz147064 
96f595a68aSyz147064 static policy_t policies[] = {
97f595a68aSyz147064 	{"L2",		AGGR_POLICY_L2},
98f595a68aSyz147064 	{"L3",		AGGR_POLICY_L3},
99f595a68aSyz147064 	{"L4",		AGGR_POLICY_L4}};
100f595a68aSyz147064 
101f595a68aSyz147064 #define	NPOLICIES	(sizeof (policies) / sizeof (policy_t))
102f595a68aSyz147064 
103f595a68aSyz147064 typedef struct dladm_aggr_lacpmode_s {
104f595a68aSyz147064 	char		*mode_str;
105f595a68aSyz147064 	aggr_lacp_mode_t mode_id;
106f595a68aSyz147064 } dladm_aggr_lacpmode_t;
107f595a68aSyz147064 
108f595a68aSyz147064 static dladm_aggr_lacpmode_t lacp_modes[] = {
109f595a68aSyz147064 	{"off", AGGR_LACP_OFF},
110f595a68aSyz147064 	{"active", AGGR_LACP_ACTIVE},
111f595a68aSyz147064 	{"passive", AGGR_LACP_PASSIVE}};
112f595a68aSyz147064 
113f595a68aSyz147064 #define	NLACP_MODES	(sizeof (lacp_modes) / sizeof (dladm_aggr_lacpmode_t))
114f595a68aSyz147064 
115f595a68aSyz147064 typedef struct dladm_aggr_lacptimer_s {
116f595a68aSyz147064 	char		*lt_str;
117f595a68aSyz147064 	aggr_lacp_timer_t lt_id;
118f595a68aSyz147064 } dladm_aggr_lacptimer_t;
119f595a68aSyz147064 
120f595a68aSyz147064 static dladm_aggr_lacptimer_t lacp_timers[] = {
121f595a68aSyz147064 	{"short", AGGR_LACP_TIMER_SHORT},
122f595a68aSyz147064 	{"long", AGGR_LACP_TIMER_LONG}};
123f595a68aSyz147064 
124f595a68aSyz147064 #define	NLACP_TIMERS	(sizeof (lacp_timers) / sizeof (dladm_aggr_lacptimer_t))
125f595a68aSyz147064 
126f595a68aSyz147064 typedef struct dladm_aggr_port_state {
127f595a68aSyz147064 	char			*state_str;
128f595a68aSyz147064 	aggr_port_state_t	state_id;
129f595a68aSyz147064 } dladm_aggr_port_state_t;
130f595a68aSyz147064 
131f595a68aSyz147064 static dladm_aggr_port_state_t port_states[] = {
132f595a68aSyz147064 	{"standby", AGGR_PORT_STATE_STANDBY },
133f595a68aSyz147064 	{"attached", AGGR_PORT_STATE_ATTACHED }
134f595a68aSyz147064 };
135f595a68aSyz147064 
136f595a68aSyz147064 #define	NPORT_STATES	\
137f595a68aSyz147064 	(sizeof (port_states) / sizeof (dladm_aggr_port_state_t))
138f595a68aSyz147064 
139d62bc4baSyz147064 static int
140eae72b5bSSebastien Roy i_dladm_aggr_ioctl(int cmd, void *ptr)
141d62bc4baSyz147064 {
142d62bc4baSyz147064 	int err, fd;
143f595a68aSyz147064 
144eae72b5bSSebastien Roy 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
145d62bc4baSyz147064 		return (-1);
146f595a68aSyz147064 
147eae72b5bSSebastien Roy 	err = ioctl(fd, cmd, ptr);
148d62bc4baSyz147064 	(void) close(fd);
149f595a68aSyz147064 
150d62bc4baSyz147064 	return (err);
151d62bc4baSyz147064 }
152f595a68aSyz147064 
153f595a68aSyz147064 /*
154d62bc4baSyz147064  * Caller must free attr.lg_ports. The ptr pointer is advanced while convert
155d62bc4baSyz147064  * the laioc_info_t to the dladm_aggr_grp_attr_t structure.
156f595a68aSyz147064  */
157f595a68aSyz147064 static int
158d62bc4baSyz147064 i_dladm_aggr_iocp2grpattr(void **ptr, dladm_aggr_grp_attr_t *attrp)
159f595a68aSyz147064 {
160f595a68aSyz147064 	laioc_info_group_t	*grp;
161f595a68aSyz147064 	laioc_info_port_t	*port;
162d62bc4baSyz147064 	int			i;
163d62bc4baSyz147064 	void			*where = (*ptr);
164f595a68aSyz147064 
165d62bc4baSyz147064 	grp = (laioc_info_group_t *)where;
166d62bc4baSyz147064 
167d62bc4baSyz147064 	attrp->lg_linkid = grp->lg_linkid;
168d62bc4baSyz147064 	attrp->lg_key = grp->lg_key;
169d62bc4baSyz147064 	attrp->lg_nports = grp->lg_nports;
170d62bc4baSyz147064 	attrp->lg_policy = grp->lg_policy;
171d62bc4baSyz147064 	attrp->lg_lacp_mode = grp->lg_lacp_mode;
172d62bc4baSyz147064 	attrp->lg_lacp_timer = grp->lg_lacp_timer;
173d62bc4baSyz147064 	attrp->lg_force = grp->lg_force;
174d62bc4baSyz147064 
175d62bc4baSyz147064 	bcopy(grp->lg_mac, attrp->lg_mac, ETHERADDRL);
176d62bc4baSyz147064 	attrp->lg_mac_fixed = grp->lg_mac_fixed;
177d62bc4baSyz147064 
178d62bc4baSyz147064 	if ((attrp->lg_ports = malloc(grp->lg_nports *
179d62bc4baSyz147064 	    sizeof (dladm_aggr_port_attr_t))) == NULL) {
180d62bc4baSyz147064 		errno = ENOMEM;
181d62bc4baSyz147064 		goto fail;
182d62bc4baSyz147064 	}
183d62bc4baSyz147064 
184d62bc4baSyz147064 	where = (grp + 1);
185d62bc4baSyz147064 
186d62bc4baSyz147064 	/*
187d62bc4baSyz147064 	 * Go through each port that is part of the group.
188d62bc4baSyz147064 	 */
189d62bc4baSyz147064 	for (i = 0; i < grp->lg_nports; i++) {
190d62bc4baSyz147064 		port = (laioc_info_port_t *)where;
191d62bc4baSyz147064 
192d62bc4baSyz147064 		attrp->lg_ports[i].lp_linkid = port->lp_linkid;
193d62bc4baSyz147064 		bcopy(port->lp_mac, attrp->lg_ports[i].lp_mac, ETHERADDRL);
194d62bc4baSyz147064 		attrp->lg_ports[i].lp_state = port->lp_state;
195d62bc4baSyz147064 		attrp->lg_ports[i].lp_lacp_state = port->lp_lacp_state;
196d62bc4baSyz147064 
197d62bc4baSyz147064 		where = (port + 1);
198d62bc4baSyz147064 	}
199d62bc4baSyz147064 	*ptr = where;
200d62bc4baSyz147064 	return (0);
201d62bc4baSyz147064 fail:
202f595a68aSyz147064 	return (-1);
203d62bc4baSyz147064 }
204d62bc4baSyz147064 
205d62bc4baSyz147064 /*
206d62bc4baSyz147064  * Get active configuration of a specific aggregation.
207d62bc4baSyz147064  * Caller must free attrp->la_ports.
208d62bc4baSyz147064  */
209d62bc4baSyz147064 static dladm_status_t
210d62bc4baSyz147064 i_dladm_aggr_info_active(datalink_id_t linkid, dladm_aggr_grp_attr_t *attrp)
211d62bc4baSyz147064 {
212d62bc4baSyz147064 	laioc_info_t *ioc;
213eae72b5bSSebastien Roy 	int bufsize;
214d62bc4baSyz147064 	void *where;
215d62bc4baSyz147064 	dladm_status_t status = DLADM_STATUS_OK;
216f595a68aSyz147064 
217f595a68aSyz147064 	bufsize = MIN_INFO_SIZE;
218f595a68aSyz147064 	ioc = (laioc_info_t *)calloc(1, bufsize);
219d62bc4baSyz147064 	if (ioc == NULL)
220d62bc4baSyz147064 		return (DLADM_STATUS_NOMEM);
221d62bc4baSyz147064 
222d62bc4baSyz147064 	ioc->li_group_linkid = linkid;
223f595a68aSyz147064 
224f595a68aSyz147064 tryagain:
225eae72b5bSSebastien Roy 	ioc->li_bufsize = bufsize;
226eae72b5bSSebastien Roy 	if (i_dladm_aggr_ioctl(LAIOC_INFO, ioc) != 0) {
227f595a68aSyz147064 		if (errno == ENOSPC) {
228f595a68aSyz147064 			/*
229f595a68aSyz147064 			 * The LAIOC_INFO call failed due to a short
230f595a68aSyz147064 			 * buffer. Reallocate the buffer and try again.
231f595a68aSyz147064 			 */
232f595a68aSyz147064 			bufsize *= 2;
233f595a68aSyz147064 			if (bufsize <= MAX_INFO_SIZE) {
234f595a68aSyz147064 				ioc = (laioc_info_t *)realloc(ioc, bufsize);
235f595a68aSyz147064 				if (ioc != NULL) {
236f595a68aSyz147064 					bzero(ioc, sizeof (bufsize));
237f595a68aSyz147064 					goto tryagain;
238f595a68aSyz147064 				}
239f595a68aSyz147064 			}
240f595a68aSyz147064 		}
241d62bc4baSyz147064 		status = dladm_errno2status(errno);
242f595a68aSyz147064 		goto bail;
243f595a68aSyz147064 	}
244f595a68aSyz147064 
245f595a68aSyz147064 	/*
246f595a68aSyz147064 	 * Go through each group returned by the aggregation driver.
247f595a68aSyz147064 	 */
248f595a68aSyz147064 	where = (char *)(ioc + 1);
249d62bc4baSyz147064 	if (i_dladm_aggr_iocp2grpattr(&where, attrp) != 0) {
250d62bc4baSyz147064 		status = dladm_errno2status(errno);
251f595a68aSyz147064 		goto bail;
252f595a68aSyz147064 	}
253f595a68aSyz147064 
254f595a68aSyz147064 bail:
255f595a68aSyz147064 	free(ioc);
256f595a68aSyz147064 	return (status);
257f595a68aSyz147064 }
258f595a68aSyz147064 
259d62bc4baSyz147064 /*
260d62bc4baSyz147064  * Get configuration information of a specific aggregation.
261d62bc4baSyz147064  * Caller must free attrp->la_ports.
262d62bc4baSyz147064  */
263d62bc4baSyz147064 static dladm_status_t
264d62bc4baSyz147064 i_dladm_aggr_info_persist(datalink_id_t linkid, dladm_aggr_grp_attr_t *attrp)
265d62bc4baSyz147064 {
266d62bc4baSyz147064 	dladm_conf_t	conf;
267d62bc4baSyz147064 	uint32_t	nports, i;
268d62bc4baSyz147064 	char		*portstr, *next;
269d62bc4baSyz147064 	dladm_status_t	status;
270d62bc4baSyz147064 	uint64_t	u64;
271d62bc4baSyz147064 	int		size;
272d62bc4baSyz147064 	char		macstr[ETHERADDRL * 3];
273f595a68aSyz147064 
274d62bc4baSyz147064 	attrp->lg_linkid = linkid;
275d62bc4baSyz147064 	if ((status = dladm_read_conf(linkid, &conf)) != DLADM_STATUS_OK)
276d62bc4baSyz147064 		return (status);
277f595a68aSyz147064 
278d62bc4baSyz147064 	status = dladm_get_conf_field(conf, FKEY, &u64, sizeof (u64));
279d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
280d62bc4baSyz147064 		goto done;
281d62bc4baSyz147064 	attrp->lg_key = (uint16_t)u64;
282d62bc4baSyz147064 
283d62bc4baSyz147064 	status = dladm_get_conf_field(conf, FPOLICY, &u64, sizeof (u64));
284d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
285d62bc4baSyz147064 		goto done;
286d62bc4baSyz147064 	attrp->lg_policy = (uint32_t)u64;
287d62bc4baSyz147064 
288d62bc4baSyz147064 	status = dladm_get_conf_field(conf, FFIXMACADDR, &attrp->lg_mac_fixed,
289d62bc4baSyz147064 	    sizeof (boolean_t));
290d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
291d62bc4baSyz147064 		goto done;
292d62bc4baSyz147064 
293d62bc4baSyz147064 	if (attrp->lg_mac_fixed) {
294d62bc4baSyz147064 		boolean_t fixed;
295d62bc4baSyz147064 
296d62bc4baSyz147064 		if ((status = dladm_get_conf_field(conf, FMACADDR, macstr,
297d62bc4baSyz147064 		    sizeof (macstr))) != DLADM_STATUS_OK) {
298d62bc4baSyz147064 			goto done;
299d62bc4baSyz147064 		}
300d62bc4baSyz147064 		if (!dladm_aggr_str2macaddr(macstr, &fixed, attrp->lg_mac)) {
301f595a68aSyz147064 			status = DLADM_STATUS_REPOSITORYINVAL;
302f595a68aSyz147064 			goto done;
303f595a68aSyz147064 		}
304d62bc4baSyz147064 	}
305f595a68aSyz147064 
306d62bc4baSyz147064 	status = dladm_get_conf_field(conf, FFORCE, &attrp->lg_force,
307d62bc4baSyz147064 	    sizeof (boolean_t));
308d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
309f595a68aSyz147064 		goto done;
310f595a68aSyz147064 
311d62bc4baSyz147064 	status = dladm_get_conf_field(conf, FLACPMODE, &u64, sizeof (u64));
312d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
313d62bc4baSyz147064 		goto done;
314d62bc4baSyz147064 	attrp->lg_lacp_mode = (aggr_lacp_mode_t)u64;
315f595a68aSyz147064 
316d62bc4baSyz147064 	status = dladm_get_conf_field(conf, FLACPTIMER, &u64, sizeof (u64));
317d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
318d62bc4baSyz147064 		goto done;
319d62bc4baSyz147064 	attrp->lg_lacp_timer = (aggr_lacp_timer_t)u64;
320f595a68aSyz147064 
321d62bc4baSyz147064 	status = dladm_get_conf_field(conf, FNPORTS, &u64, sizeof (u64));
322d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
323d62bc4baSyz147064 		goto done;
324d62bc4baSyz147064 	nports = (uint32_t)u64;
325d62bc4baSyz147064 	attrp->lg_nports = nports;
326f595a68aSyz147064 
327d62bc4baSyz147064 	size = nports * (LINKID_STR_WIDTH + 1) + 1;
328d62bc4baSyz147064 	if ((portstr = calloc(1, size)) == NULL) {
329f595a68aSyz147064 		status = DLADM_STATUS_NOMEM;
330f595a68aSyz147064 		goto done;
331f595a68aSyz147064 	}
332f595a68aSyz147064 
333d62bc4baSyz147064 	status = dladm_get_conf_field(conf, FPORTS, portstr, size);
334d62bc4baSyz147064 	if (status != DLADM_STATUS_OK) {
335d62bc4baSyz147064 		free(portstr);
336f595a68aSyz147064 		goto done;
337f595a68aSyz147064 	}
338f595a68aSyz147064 
339d62bc4baSyz147064 	if ((attrp->lg_ports = malloc(nports *
340d62bc4baSyz147064 	    sizeof (dladm_aggr_port_attr_t))) == NULL) {
341d62bc4baSyz147064 		free(portstr);
342d62bc4baSyz147064 		status = DLADM_STATUS_NOMEM;
343d62bc4baSyz147064 		goto done;
344d62bc4baSyz147064 	}
345d62bc4baSyz147064 
346d62bc4baSyz147064 	for (next = portstr, i = 0; i < nports; i++) {
347d62bc4baSyz147064 		READ_PORT(next, attrp->lg_ports[i].lp_linkid, status);
348d62bc4baSyz147064 		if (status != DLADM_STATUS_OK) {
349d62bc4baSyz147064 			free(portstr);
350d62bc4baSyz147064 			free(attrp->lg_ports);
351d62bc4baSyz147064 			goto done;
352d62bc4baSyz147064 		}
353d62bc4baSyz147064 	}
354d62bc4baSyz147064 	free(portstr);
355d62bc4baSyz147064 
356d62bc4baSyz147064 done:
357d62bc4baSyz147064 	dladm_destroy_conf(conf);
358d62bc4baSyz147064 	return (status);
359d62bc4baSyz147064 }
360d62bc4baSyz147064 
361d62bc4baSyz147064 dladm_status_t
362d62bc4baSyz147064 dladm_aggr_info(datalink_id_t linkid, dladm_aggr_grp_attr_t *attrp,
363d62bc4baSyz147064     uint32_t flags)
364d62bc4baSyz147064 {
365d62bc4baSyz147064 	assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST);
366d62bc4baSyz147064 	if (flags == DLADM_OPT_ACTIVE)
367d62bc4baSyz147064 		return (i_dladm_aggr_info_active(linkid, attrp));
368f595a68aSyz147064 	else
369d62bc4baSyz147064 		return (i_dladm_aggr_info_persist(linkid, attrp));
370f595a68aSyz147064 }
371f595a68aSyz147064 
372d62bc4baSyz147064 /*
373d62bc4baSyz147064  * Add or remove one or more ports to/from an existing link aggregation.
374d62bc4baSyz147064  */
375d62bc4baSyz147064 static dladm_status_t
376d62bc4baSyz147064 i_dladm_aggr_add_rmv(datalink_id_t linkid, uint32_t nports,
377d62bc4baSyz147064     dladm_aggr_port_attr_db_t *ports, uint32_t flags, int cmd)
378d62bc4baSyz147064 {
379d62bc4baSyz147064 	char *orig_portstr = NULL, *portstr = NULL;
3808de9d095Syz147064 	laioc_add_rem_t *iocp = NULL;
381d62bc4baSyz147064 	laioc_port_t *ioc_ports;
382d62bc4baSyz147064 	uint32_t orig_nports, result_nports, len, i, j;
383d62bc4baSyz147064 	dladm_conf_t conf;
384d62bc4baSyz147064 	datalink_class_t class;
385d62bc4baSyz147064 	dladm_status_t status = DLADM_STATUS_OK;
386d62bc4baSyz147064 	int size;
387d62bc4baSyz147064 	uint64_t u64;
388d62bc4baSyz147064 	uint32_t media;
389d62bc4baSyz147064 
390d62bc4baSyz147064 	if (nports == 0)
391d62bc4baSyz147064 		return (DLADM_STATUS_BADARG);
392d62bc4baSyz147064 
393d62bc4baSyz147064 	/*
394d62bc4baSyz147064 	 * Sanity check - aggregations can only be created over Ethernet
395d62bc4baSyz147064 	 * physical links.
396d62bc4baSyz147064 	 */
397d62bc4baSyz147064 	for (i = 0; i < nports; i++) {
398d62bc4baSyz147064 		if ((dladm_datalink_id2info(ports[i].lp_linkid, NULL,
399d62bc4baSyz147064 		    &class, &media, NULL, 0) != DLADM_STATUS_OK) ||
400d62bc4baSyz147064 		    (class != DATALINK_CLASS_PHYS) || (media != DL_ETHER)) {
401d62bc4baSyz147064 			return (DLADM_STATUS_BADARG);
402d62bc4baSyz147064 		}
403d62bc4baSyz147064 	}
404d62bc4baSyz147064 
405d62bc4baSyz147064 	/*
406d62bc4baSyz147064 	 * First, update the persistent configuration if requested.  We only
407d62bc4baSyz147064 	 * need to update the FPORTS and FNPORTS fields of this aggregation.
408d62bc4baSyz147064 	 * Note that FPORTS is a list of port linkids separated by
409d62bc4baSyz147064 	 * PORT_DELIMITER ('.').
410d62bc4baSyz147064 	 */
411d62bc4baSyz147064 	if (flags & DLADM_OPT_PERSIST) {
412d62bc4baSyz147064 		status = dladm_read_conf(linkid, &conf);
413d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
414d62bc4baSyz147064 			return (status);
415d62bc4baSyz147064 
416d62bc4baSyz147064 		/*
417d62bc4baSyz147064 		 * Get the original configuration of FNPORTS and FPORTS.
418d62bc4baSyz147064 		 */
419d62bc4baSyz147064 		status = dladm_get_conf_field(conf, FNPORTS, &u64,
420d62bc4baSyz147064 		    sizeof (u64));
421d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
422d62bc4baSyz147064 			goto destroyconf;
423d62bc4baSyz147064 		orig_nports = (uint32_t)u64;
424d62bc4baSyz147064 
425d62bc4baSyz147064 		/*
426d62bc4baSyz147064 		 * At least one port needs to be in the aggregation.
427d62bc4baSyz147064 		 */
428d62bc4baSyz147064 		if ((cmd == LAIOC_REMOVE) && (orig_nports <= nports)) {
429d62bc4baSyz147064 			status = DLADM_STATUS_BADARG;
430d62bc4baSyz147064 			goto destroyconf;
431d62bc4baSyz147064 		}
432d62bc4baSyz147064 
433d62bc4baSyz147064 		size = orig_nports * (LINKID_STR_WIDTH + 1) + 1;
434d62bc4baSyz147064 		if ((orig_portstr = calloc(1, size)) == NULL) {
435d62bc4baSyz147064 			status = dladm_errno2status(errno);
436d62bc4baSyz147064 			goto destroyconf;
437d62bc4baSyz147064 		}
438d62bc4baSyz147064 
439d62bc4baSyz147064 		status = dladm_get_conf_field(conf, FPORTS, orig_portstr, size);
440d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
441d62bc4baSyz147064 			goto destroyconf;
442d62bc4baSyz147064 
443d62bc4baSyz147064 		result_nports = (cmd == LAIOC_ADD) ? orig_nports + nports :
444d62bc4baSyz147064 		    orig_nports;
445d62bc4baSyz147064 
446d62bc4baSyz147064 		size = result_nports * (LINKID_STR_WIDTH + 1) + 1;
447d62bc4baSyz147064 		if ((portstr = calloc(1, size)) == NULL) {
448d62bc4baSyz147064 			status = dladm_errno2status(errno);
449d62bc4baSyz147064 			goto destroyconf;
450d62bc4baSyz147064 		}
451d62bc4baSyz147064 
452d62bc4baSyz147064 		/*
453d62bc4baSyz147064 		 * get the new configuration and set to result_nports and
454d62bc4baSyz147064 		 * portstr.
455d62bc4baSyz147064 		 */
456d62bc4baSyz147064 		if (cmd == LAIOC_ADD) {
457d62bc4baSyz147064 			(void) strlcpy(portstr, orig_portstr, size);
458d62bc4baSyz147064 			for (i = 0; i < nports; i++)
459d62bc4baSyz147064 				WRITE_PORT(portstr, ports[i].lp_linkid, size);
460d62bc4baSyz147064 		} else {
461d62bc4baSyz147064 			char *next;
462d62bc4baSyz147064 			datalink_id_t portid;
463d62bc4baSyz147064 			uint32_t remove = 0;
464d62bc4baSyz147064 
465d62bc4baSyz147064 			for (next = orig_portstr, j = 0; j < orig_nports; j++) {
466d62bc4baSyz147064 				/*
467d62bc4baSyz147064 				 * Read the portids from the old configuration
468d62bc4baSyz147064 				 * one by one.
469d62bc4baSyz147064 				 */
470d62bc4baSyz147064 				READ_PORT(next, portid, status);
471d62bc4baSyz147064 				if (status != DLADM_STATUS_OK) {
472d62bc4baSyz147064 					free(portstr);
473d62bc4baSyz147064 					goto destroyconf;
474d62bc4baSyz147064 				}
475d62bc4baSyz147064 
476d62bc4baSyz147064 				/*
477d62bc4baSyz147064 				 * See whether this port is in the removal
478d62bc4baSyz147064 				 * list.  If not, copy to the new config.
479d62bc4baSyz147064 				 */
480d62bc4baSyz147064 				for (i = 0; i < nports; i++) {
481d62bc4baSyz147064 					if (ports[i].lp_linkid == portid)
482d62bc4baSyz147064 						break;
483d62bc4baSyz147064 				}
484d62bc4baSyz147064 				if (i == nports) {
485d62bc4baSyz147064 					WRITE_PORT(portstr, portid, size);
486d62bc4baSyz147064 				} else {
487d62bc4baSyz147064 					remove++;
488d62bc4baSyz147064 				}
489d62bc4baSyz147064 			}
490d62bc4baSyz147064 			if (remove != nports) {
491d62bc4baSyz147064 				status = DLADM_STATUS_LINKINVAL;
492d62bc4baSyz147064 				free(portstr);
493d62bc4baSyz147064 				goto destroyconf;
494d62bc4baSyz147064 			}
495d62bc4baSyz147064 			result_nports -= nports;
496d62bc4baSyz147064 		}
497d62bc4baSyz147064 
498d62bc4baSyz147064 		u64 = result_nports;
499d62bc4baSyz147064 		if ((status = dladm_set_conf_field(conf, FNPORTS,
500d62bc4baSyz147064 		    DLADM_TYPE_UINT64, &u64)) != DLADM_STATUS_OK) {
501d62bc4baSyz147064 			free(portstr);
502d62bc4baSyz147064 			goto destroyconf;
503d62bc4baSyz147064 		}
504d62bc4baSyz147064 
505d62bc4baSyz147064 		status = dladm_set_conf_field(conf, FPORTS, DLADM_TYPE_STR,
506d62bc4baSyz147064 		    portstr);
507d62bc4baSyz147064 		free(portstr);
508d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
509d62bc4baSyz147064 			goto destroyconf;
510d62bc4baSyz147064 
511d62bc4baSyz147064 		/*
512d62bc4baSyz147064 		 * Write the new configuration to the persistent repository.
513d62bc4baSyz147064 		 */
514d62bc4baSyz147064 		status = dladm_write_conf(conf);
515d62bc4baSyz147064 
516d62bc4baSyz147064 destroyconf:
517d62bc4baSyz147064 		dladm_destroy_conf(conf);
518d62bc4baSyz147064 		if (status != DLADM_STATUS_OK) {
519d62bc4baSyz147064 			free(orig_portstr);
520d62bc4baSyz147064 			return (status);
521d62bc4baSyz147064 		}
522d62bc4baSyz147064 	}
523d62bc4baSyz147064 
524d62bc4baSyz147064 	/*
525d62bc4baSyz147064 	 * If the caller only requested to update the persistent
526d62bc4baSyz147064 	 * configuration, we are done.
527d62bc4baSyz147064 	 */
528d62bc4baSyz147064 	if (!(flags & DLADM_OPT_ACTIVE))
529d62bc4baSyz147064 		goto done;
530d62bc4baSyz147064 
531d62bc4baSyz147064 	/*
532d62bc4baSyz147064 	 * Update the active configuration.
533d62bc4baSyz147064 	 */
534d62bc4baSyz147064 	len = sizeof (*iocp) + nports * sizeof (laioc_port_t);
535d62bc4baSyz147064 	if ((iocp = malloc(len)) == NULL) {
536d62bc4baSyz147064 		status = DLADM_STATUS_NOMEM;
537d62bc4baSyz147064 		goto done;
538d62bc4baSyz147064 	}
539d62bc4baSyz147064 
540d62bc4baSyz147064 	iocp->la_linkid = linkid;
541d62bc4baSyz147064 	iocp->la_nports = nports;
542d62bc4baSyz147064 	if (cmd == LAIOC_ADD)
543d62bc4baSyz147064 		iocp->la_force = (flags & DLADM_OPT_FORCE);
544d62bc4baSyz147064 
545d62bc4baSyz147064 	ioc_ports = (laioc_port_t *)(iocp + 1);
546d62bc4baSyz147064 	for (i = 0; i < nports; i++)
547d62bc4baSyz147064 		ioc_ports[i].lp_linkid = ports[i].lp_linkid;
548d62bc4baSyz147064 
549eae72b5bSSebastien Roy 	if (i_dladm_aggr_ioctl(cmd, iocp) < 0)
550d62bc4baSyz147064 		status = dladm_errno2status(errno);
551f595a68aSyz147064 
552f595a68aSyz147064 done:
553f595a68aSyz147064 	free(iocp);
554d62bc4baSyz147064 
555d62bc4baSyz147064 	/*
556d62bc4baSyz147064 	 * If the active configuration update fails, restore the old
557d62bc4baSyz147064 	 * persistent configuration if we've changed that.
558d62bc4baSyz147064 	 */
559d62bc4baSyz147064 	if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) {
560d62bc4baSyz147064 		if (dladm_read_conf(linkid, &conf) == DLADM_STATUS_OK) {
561d62bc4baSyz147064 			u64 = orig_nports;
562d62bc4baSyz147064 			if ((dladm_set_conf_field(conf, FNPORTS,
563d62bc4baSyz147064 			    DLADM_TYPE_UINT64, &u64) == DLADM_STATUS_OK) &&
564d62bc4baSyz147064 			    (dladm_set_conf_field(conf, FPORTS, DLADM_TYPE_STR,
565d62bc4baSyz147064 			    orig_portstr) == DLADM_STATUS_OK)) {
566d62bc4baSyz147064 				(void) dladm_write_conf(conf);
567d62bc4baSyz147064 			}
568d62bc4baSyz147064 			(void) dladm_destroy_conf(conf);
569d62bc4baSyz147064 		}
570d62bc4baSyz147064 	}
571d62bc4baSyz147064 	free(orig_portstr);
572f595a68aSyz147064 	return (status);
573f595a68aSyz147064 }
574f595a68aSyz147064 
575f595a68aSyz147064 /*
576f595a68aSyz147064  * Send a modify command to the link aggregation driver.
577f595a68aSyz147064  */
578f595a68aSyz147064 static dladm_status_t
579d62bc4baSyz147064 i_dladm_aggr_modify_sys(datalink_id_t linkid, uint32_t mask,
580f595a68aSyz147064     dladm_aggr_modify_attr_t *attr)
581f595a68aSyz147064 {
582f595a68aSyz147064 	laioc_modify_t ioc;
583f595a68aSyz147064 
584d62bc4baSyz147064 	ioc.lu_linkid = linkid;
585f595a68aSyz147064 
586f595a68aSyz147064 	ioc.lu_modify_mask = 0;
587f595a68aSyz147064 	if (mask & DLADM_AGGR_MODIFY_POLICY)
588f595a68aSyz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_POLICY;
589f595a68aSyz147064 	if (mask & DLADM_AGGR_MODIFY_MAC)
590f595a68aSyz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_MAC;
591f595a68aSyz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE)
592f595a68aSyz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_MODE;
593f595a68aSyz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER)
594f595a68aSyz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_TIMER;
595f595a68aSyz147064 
596f595a68aSyz147064 	ioc.lu_policy = attr->ld_policy;
597f595a68aSyz147064 	ioc.lu_mac_fixed = attr->ld_mac_fixed;
598f595a68aSyz147064 	bcopy(attr->ld_mac, ioc.lu_mac, ETHERADDRL);
599f595a68aSyz147064 	ioc.lu_lacp_mode = attr->ld_lacp_mode;
600f595a68aSyz147064 	ioc.lu_lacp_timer = attr->ld_lacp_timer;
601f595a68aSyz147064 
602eae72b5bSSebastien Roy 	if (i_dladm_aggr_ioctl(LAIOC_MODIFY, &ioc) < 0) {
603f595a68aSyz147064 		if (errno == EINVAL)
604d62bc4baSyz147064 			return (DLADM_STATUS_MACADDRINVAL);
605f595a68aSyz147064 		else
606d62bc4baSyz147064 			return (dladm_errno2status(errno));
607d62bc4baSyz147064 	} else {
608d62bc4baSyz147064 		return (DLADM_STATUS_OK);
609f595a68aSyz147064 	}
610f595a68aSyz147064 }
611f595a68aSyz147064 
612f595a68aSyz147064 /*
613f595a68aSyz147064  * Send a create command to the link aggregation driver.
614f595a68aSyz147064  */
615f595a68aSyz147064 static dladm_status_t
616d62bc4baSyz147064 i_dladm_aggr_create_sys(datalink_id_t linkid, uint16_t key, uint32_t nports,
617d62bc4baSyz147064     dladm_aggr_port_attr_db_t *ports, uint32_t policy,
618d62bc4baSyz147064     boolean_t mac_addr_fixed, const uchar_t *mac_addr,
619d62bc4baSyz147064     aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, boolean_t force)
620f595a68aSyz147064 {
621eae72b5bSSebastien Roy 	int i, len;
622d62bc4baSyz147064 	laioc_create_t *iocp = NULL;
623d62bc4baSyz147064 	laioc_port_t *ioc_ports;
624f595a68aSyz147064 	dladm_status_t status = DLADM_STATUS_OK;
625f595a68aSyz147064 
626d62bc4baSyz147064 	len = sizeof (*iocp) + nports * sizeof (laioc_port_t);
627f595a68aSyz147064 	iocp = malloc(len);
628f595a68aSyz147064 	if (iocp == NULL)
629f595a68aSyz147064 		return (DLADM_STATUS_NOMEM);
630f595a68aSyz147064 
631d62bc4baSyz147064 	iocp->lc_key = key;
632d62bc4baSyz147064 	iocp->lc_linkid = linkid;
633d62bc4baSyz147064 	iocp->lc_nports = nports;
634d62bc4baSyz147064 	iocp->lc_policy = policy;
635d62bc4baSyz147064 	iocp->lc_lacp_mode = lacp_mode;
636d62bc4baSyz147064 	iocp->lc_lacp_timer = lacp_timer;
637d62bc4baSyz147064 	ioc_ports = (laioc_port_t *)(iocp + 1);
638d62bc4baSyz147064 	iocp->lc_force = force;
639f595a68aSyz147064 
640d62bc4baSyz147064 	for (i = 0; i < nports; i++)
641d62bc4baSyz147064 		ioc_ports[i].lp_linkid = ports[i].lp_linkid;
642f595a68aSyz147064 
643d62bc4baSyz147064 	if (mac_addr_fixed && !VALID_PORT_MAC(mac_addr)) {
644d62bc4baSyz147064 		status = DLADM_STATUS_MACADDRINVAL;
645d62bc4baSyz147064 		goto done;
646f595a68aSyz147064 	}
647f595a68aSyz147064 
648d62bc4baSyz147064 	bcopy(mac_addr, iocp->lc_mac, ETHERADDRL);
649d62bc4baSyz147064 	iocp->lc_mac_fixed = mac_addr_fixed;
650f595a68aSyz147064 
651eae72b5bSSebastien Roy 	if (i_dladm_aggr_ioctl(LAIOC_CREATE, iocp) < 0)
652d62bc4baSyz147064 		status = dladm_errno2status(errno);
653f595a68aSyz147064 
654d62bc4baSyz147064 done:
655f595a68aSyz147064 	free(iocp);
656f595a68aSyz147064 	return (status);
657f595a68aSyz147064 }
658f595a68aSyz147064 
659f595a68aSyz147064 /*
660f595a68aSyz147064  * Invoked to bring up a link aggregation group.
661f595a68aSyz147064  */
662f595a68aSyz147064 static int
663d62bc4baSyz147064 i_dladm_aggr_up(datalink_id_t linkid, void *arg)
664f595a68aSyz147064 {
665d62bc4baSyz147064 	dladm_status_t *statusp = (dladm_status_t *)arg;
666d62bc4baSyz147064 	dladm_aggr_grp_attr_t attr;
667d62bc4baSyz147064 	dladm_aggr_port_attr_db_t *ports = NULL;
668d62bc4baSyz147064 	uint16_t key = 0;
669f595a68aSyz147064 	int i, j;
670f595a68aSyz147064 	dladm_status_t status;
671f595a68aSyz147064 
672d62bc4baSyz147064 	status = dladm_aggr_info(linkid, &attr, DLADM_OPT_PERSIST);
673d62bc4baSyz147064 	if (status != DLADM_STATUS_OK) {
674d62bc4baSyz147064 		*statusp = status;
675d62bc4baSyz147064 		return (DLADM_WALK_CONTINUE);
676d62bc4baSyz147064 	}
677f595a68aSyz147064 
678d62bc4baSyz147064 	if (attr.lg_key <= AGGR_MAX_KEY)
679d62bc4baSyz147064 		key = attr.lg_key;
680f595a68aSyz147064 
681d62bc4baSyz147064 	ports = malloc(attr.lg_nports * sizeof (dladm_aggr_port_attr_db_t));
682d62bc4baSyz147064 	if (ports == NULL) {
683d62bc4baSyz147064 		status = DLADM_STATUS_NOMEM;
684d62bc4baSyz147064 		goto done;
685f595a68aSyz147064 	}
686f595a68aSyz147064 
687f595a68aSyz147064 	/*
688d62bc4baSyz147064 	 * Validate (and purge) each physical link associated with this
689d62bc4baSyz147064 	 * aggregation, if the specific hardware has been removed during
690d62bc4baSyz147064 	 * the system shutdown.
691f595a68aSyz147064 	 */
692d62bc4baSyz147064 	for (i = 0, j = 0; i < attr.lg_nports; i++) {
693d62bc4baSyz147064 		datalink_id_t	portid = attr.lg_ports[i].lp_linkid;
694d62bc4baSyz147064 		uint32_t	flags;
695d62bc4baSyz147064 		dladm_status_t	s;
696f595a68aSyz147064 
697d62bc4baSyz147064 		s = dladm_datalink_id2info(portid, &flags, NULL, NULL, NULL, 0);
698d62bc4baSyz147064 		if (s != DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE))
699d62bc4baSyz147064 			continue;
700f595a68aSyz147064 
701d62bc4baSyz147064 		ports[j++].lp_linkid = portid;
702d62bc4baSyz147064 	}
703d62bc4baSyz147064 
704d62bc4baSyz147064 	if (j == 0) {
705d62bc4baSyz147064 		/*
706d62bc4baSyz147064 		 * All of the physical links are removed.
707d62bc4baSyz147064 		 */
708d62bc4baSyz147064 		status = DLADM_STATUS_BADARG;
709d62bc4baSyz147064 		goto done;
710d62bc4baSyz147064 	}
711d62bc4baSyz147064 
712d62bc4baSyz147064 	/*
713d62bc4baSyz147064 	 * Create active aggregation.
714d62bc4baSyz147064 	 */
715d62bc4baSyz147064 	if ((status = i_dladm_aggr_create_sys(linkid,
716d62bc4baSyz147064 	    key, j, ports, attr.lg_policy, attr.lg_mac_fixed,
717d62bc4baSyz147064 	    (const uchar_t *)attr.lg_mac, attr.lg_lacp_mode,
718d62bc4baSyz147064 	    attr.lg_lacp_timer, attr.lg_force)) != DLADM_STATUS_OK) {
719d62bc4baSyz147064 		goto done;
720d62bc4baSyz147064 	}
721d62bc4baSyz147064 
722d62bc4baSyz147064 	if ((status = dladm_up_datalink_id(linkid)) != DLADM_STATUS_OK) {
723d62bc4baSyz147064 		laioc_delete_t ioc;
724d62bc4baSyz147064 		ioc.ld_linkid = linkid;
725eae72b5bSSebastien Roy 		(void) i_dladm_aggr_ioctl(LAIOC_DELETE, &ioc);
726d62bc4baSyz147064 		goto done;
727d62bc4baSyz147064 	}
728d62bc4baSyz147064 
729d62bc4baSyz147064 	/*
730d62bc4baSyz147064 	 * Reset the active linkprop of this specific link.
731d62bc4baSyz147064 	 */
73230890389Sartem 	(void) dladm_init_linkprop(linkid, B_FALSE);
733d62bc4baSyz147064 
734d62bc4baSyz147064 done:
735d62bc4baSyz147064 	free(attr.lg_ports);
736d62bc4baSyz147064 	free(ports);
737d62bc4baSyz147064 
738d62bc4baSyz147064 	*statusp = status;
739d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
740d62bc4baSyz147064 }
741d62bc4baSyz147064 
742d62bc4baSyz147064 /*
743d62bc4baSyz147064  * Bring up one aggregation, or all persistent aggregations.  In the latter
744d62bc4baSyz147064  * case, the walk may terminate early if bringup of an aggregation fails.
745d62bc4baSyz147064  */
746d62bc4baSyz147064 dladm_status_t
747d62bc4baSyz147064 dladm_aggr_up(datalink_id_t linkid)
748f595a68aSyz147064 {
749f595a68aSyz147064 	dladm_status_t status;
750f595a68aSyz147064 
751d62bc4baSyz147064 	if (linkid == DATALINK_ALL_LINKID) {
752d62bc4baSyz147064 		(void) dladm_walk_datalink_id(i_dladm_aggr_up, &status,
753d62bc4baSyz147064 		    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
754d62bc4baSyz147064 		    DLADM_OPT_PERSIST);
755f595a68aSyz147064 		return (DLADM_STATUS_OK);
756d62bc4baSyz147064 	} else {
757d62bc4baSyz147064 		(void) i_dladm_aggr_up(linkid, &status);
758d62bc4baSyz147064 		return (status);
759d62bc4baSyz147064 	}
760f595a68aSyz147064 }
761f595a68aSyz147064 
762f595a68aSyz147064 /*
763f595a68aSyz147064  * Given a policy string, return a policy mask. Returns B_TRUE on
764d62bc4baSyz147064  * success, or B_FALSE if an error occurred during parsing.
765f595a68aSyz147064  */
766f595a68aSyz147064 boolean_t
767f595a68aSyz147064 dladm_aggr_str2policy(const char *str, uint32_t *policy)
768f595a68aSyz147064 {
769f595a68aSyz147064 	int i;
770f595a68aSyz147064 	policy_t *pol;
771f595a68aSyz147064 	char *token = NULL;
772f595a68aSyz147064 	char *lasts;
773f595a68aSyz147064 
774f595a68aSyz147064 	*policy = 0;
775f595a68aSyz147064 
776f595a68aSyz147064 	while ((token = strtok_r((token == NULL) ? (char *)str : NULL, ",",
777f595a68aSyz147064 	    &lasts)) != NULL) {
778f595a68aSyz147064 		for (i = 0; i < NPOLICIES; i++) {
779f595a68aSyz147064 			pol = &policies[i];
780f595a68aSyz147064 			if (strcasecmp(token, pol->pol_name) == 0) {
781f595a68aSyz147064 				*policy |= pol->policy;
782f595a68aSyz147064 				break;
783f595a68aSyz147064 			}
784f595a68aSyz147064 		}
785f595a68aSyz147064 		if (i == NPOLICIES)
786f595a68aSyz147064 			return (B_FALSE);
787f595a68aSyz147064 	}
788f595a68aSyz147064 
789f595a68aSyz147064 	return (B_TRUE);
790f595a68aSyz147064 }
791f595a68aSyz147064 
792f595a68aSyz147064 /*
793f595a68aSyz147064  * Given a policy mask, returns a printable string, or NULL if the
794f595a68aSyz147064  * policy mask is invalid. It is the responsibility of the caller to
795f595a68aSyz147064  * free the returned string after use.
796f595a68aSyz147064  */
797f595a68aSyz147064 char *
798f595a68aSyz147064 dladm_aggr_policy2str(uint32_t policy, char *str)
799f595a68aSyz147064 {
800f595a68aSyz147064 	int i, npolicies = 0;
801f595a68aSyz147064 	policy_t *pol;
802f595a68aSyz147064 
803d62bc4baSyz147064 	if (str == NULL)
804d62bc4baSyz147064 		return (NULL);
805d62bc4baSyz147064 
806f595a68aSyz147064 	str[0] = '\0';
807f595a68aSyz147064 
808f595a68aSyz147064 	for (i = 0; i < NPOLICIES; i++) {
809f595a68aSyz147064 		pol = &policies[i];
810f595a68aSyz147064 		if ((policy & pol->policy) != 0) {
811f595a68aSyz147064 			npolicies++;
812f595a68aSyz147064 			if (npolicies > 1)
813d62bc4baSyz147064 				(void) strlcat(str, ",", DLADM_STRSIZE);
814d62bc4baSyz147064 			(void) strlcat(str, pol->pol_name, DLADM_STRSIZE);
815f595a68aSyz147064 		}
816f595a68aSyz147064 	}
817f595a68aSyz147064 
818f595a68aSyz147064 	return (str);
819f595a68aSyz147064 }
820f595a68aSyz147064 
821f595a68aSyz147064 /*
822f595a68aSyz147064  * Given a MAC address string, return the MAC address in the mac_addr
823f595a68aSyz147064  * array. If the MAC address was not explicitly specified, i.e. is
824f595a68aSyz147064  * equal to 'auto', zero out mac-addr and set mac_fixed to B_TRUE.
825f595a68aSyz147064  * Return B_FALSE if a syntax error was encountered, B_FALSE otherwise.
826f595a68aSyz147064  */
827f595a68aSyz147064 boolean_t
828f595a68aSyz147064 dladm_aggr_str2macaddr(const char *str, boolean_t *mac_fixed, uchar_t *mac_addr)
829f595a68aSyz147064 {
830f595a68aSyz147064 	uchar_t *conv_str;
831f595a68aSyz147064 	int mac_len;
832f595a68aSyz147064 
833f595a68aSyz147064 	*mac_fixed = (strcmp(str, "auto") != 0);
834f595a68aSyz147064 	if (!*mac_fixed) {
835f595a68aSyz147064 		bzero(mac_addr, ETHERADDRL);
836f595a68aSyz147064 		return (B_TRUE);
837f595a68aSyz147064 	}
838f595a68aSyz147064 
839f595a68aSyz147064 	conv_str = _link_aton(str, &mac_len);
840f595a68aSyz147064 	if (conv_str == NULL)
841f595a68aSyz147064 		return (B_FALSE);
842f595a68aSyz147064 
843f595a68aSyz147064 	if (mac_len != ETHERADDRL) {
844f595a68aSyz147064 		free(conv_str);
845f595a68aSyz147064 		return (B_FALSE);
846f595a68aSyz147064 	}
847f595a68aSyz147064 
848f595a68aSyz147064 	if ((bcmp(zero_mac, conv_str, ETHERADDRL) == 0) ||
849f595a68aSyz147064 	    (conv_str[0] & 0x01)) {
850f595a68aSyz147064 		free(conv_str);
851f595a68aSyz147064 		return (B_FALSE);
852f595a68aSyz147064 	}
853f595a68aSyz147064 
854f595a68aSyz147064 	bcopy(conv_str, mac_addr, ETHERADDRL);
855f595a68aSyz147064 	free(conv_str);
856f595a68aSyz147064 
857f595a68aSyz147064 	return (B_TRUE);
858f595a68aSyz147064 }
859f595a68aSyz147064 
860f595a68aSyz147064 /*
861f595a68aSyz147064  * Returns a string containing a printable representation of a MAC address.
862f595a68aSyz147064  */
863f595a68aSyz147064 const char *
864d62bc4baSyz147064 dladm_aggr_macaddr2str(const unsigned char *mac, char *buf)
865f595a68aSyz147064 {
866f595a68aSyz147064 	static char unknown_mac[] = {0, 0, 0, 0, 0, 0};
867f595a68aSyz147064 
868f595a68aSyz147064 	if (buf == NULL)
869f595a68aSyz147064 		return (NULL);
870f595a68aSyz147064 
871f595a68aSyz147064 	if (bcmp(unknown_mac, mac, ETHERADDRL) == 0)
872d62bc4baSyz147064 		(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
873f595a68aSyz147064 	else
874f595a68aSyz147064 		return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER));
875d62bc4baSyz147064 
876d62bc4baSyz147064 	return (buf);
877f595a68aSyz147064 }
878f595a68aSyz147064 
879f595a68aSyz147064 /*
880f595a68aSyz147064  * Given a LACP mode string, find the corresponding LACP mode number. Returns
881f595a68aSyz147064  * B_TRUE if a match was found, B_FALSE otherwise.
882f595a68aSyz147064  */
883f595a68aSyz147064 boolean_t
884f595a68aSyz147064 dladm_aggr_str2lacpmode(const char *str, aggr_lacp_mode_t *lacp_mode)
885f595a68aSyz147064 {
886f595a68aSyz147064 	int i;
887f595a68aSyz147064 	dladm_aggr_lacpmode_t *mode;
888f595a68aSyz147064 
889f595a68aSyz147064 	for (i = 0; i < NLACP_MODES; i++) {
890f595a68aSyz147064 		mode = &lacp_modes[i];
891f595a68aSyz147064 		if (strncasecmp(str, mode->mode_str,
892f595a68aSyz147064 		    strlen(mode->mode_str)) == 0) {
893f595a68aSyz147064 			*lacp_mode = mode->mode_id;
894f595a68aSyz147064 			return (B_TRUE);
895f595a68aSyz147064 		}
896f595a68aSyz147064 	}
897f595a68aSyz147064 
898f595a68aSyz147064 	return (B_FALSE);
899f595a68aSyz147064 }
900f595a68aSyz147064 
901f595a68aSyz147064 /*
902f595a68aSyz147064  * Given a LACP mode number, returns a printable string, or NULL if the
903f595a68aSyz147064  * LACP mode number is invalid.
904f595a68aSyz147064  */
905f595a68aSyz147064 const char *
906f595a68aSyz147064 dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id, char *buf)
907f595a68aSyz147064 {
908f595a68aSyz147064 	int i;
909f595a68aSyz147064 	dladm_aggr_lacpmode_t *mode;
910f595a68aSyz147064 
911d62bc4baSyz147064 	if (buf == NULL)
912d62bc4baSyz147064 		return (NULL);
913d62bc4baSyz147064 
914f595a68aSyz147064 	for (i = 0; i < NLACP_MODES; i++) {
915f595a68aSyz147064 		mode = &lacp_modes[i];
916f595a68aSyz147064 		if (mode->mode_id == mode_id) {
917f595a68aSyz147064 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
918f595a68aSyz147064 			    mode->mode_str);
919f595a68aSyz147064 			return (buf);
920f595a68aSyz147064 		}
921f595a68aSyz147064 	}
922f595a68aSyz147064 
923f595a68aSyz147064 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
924f595a68aSyz147064 	return (buf);
925f595a68aSyz147064 }
926f595a68aSyz147064 
927f595a68aSyz147064 /*
928f595a68aSyz147064  * Given a LACP timer string, find the corresponding LACP timer number. Returns
929f595a68aSyz147064  * B_TRUE if a match was found, B_FALSE otherwise.
930f595a68aSyz147064  */
931f595a68aSyz147064 boolean_t
932f595a68aSyz147064 dladm_aggr_str2lacptimer(const char *str, aggr_lacp_timer_t *lacp_timer)
933f595a68aSyz147064 {
934f595a68aSyz147064 	int i;
935f595a68aSyz147064 	dladm_aggr_lacptimer_t *timer;
936f595a68aSyz147064 
937f595a68aSyz147064 	for (i = 0; i < NLACP_TIMERS; i++) {
938f595a68aSyz147064 		timer = &lacp_timers[i];
939f595a68aSyz147064 		if (strncasecmp(str, timer->lt_str,
940f595a68aSyz147064 		    strlen(timer->lt_str)) == 0) {
941f595a68aSyz147064 			*lacp_timer = timer->lt_id;
942f595a68aSyz147064 			return (B_TRUE);
943f595a68aSyz147064 		}
944f595a68aSyz147064 	}
945f595a68aSyz147064 
946f595a68aSyz147064 	return (B_FALSE);
947f595a68aSyz147064 }
948f595a68aSyz147064 
949f595a68aSyz147064 /*
950f595a68aSyz147064  * Given a LACP timer, returns a printable string, or NULL if the
951f595a68aSyz147064  * LACP timer number is invalid.
952f595a68aSyz147064  */
953f595a68aSyz147064 const char *
954f595a68aSyz147064 dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id, char *buf)
955f595a68aSyz147064 {
956f595a68aSyz147064 	int i;
957f595a68aSyz147064 	dladm_aggr_lacptimer_t *timer;
958f595a68aSyz147064 
959d62bc4baSyz147064 	if (buf == NULL)
960d62bc4baSyz147064 		return (NULL);
961d62bc4baSyz147064 
962f595a68aSyz147064 	for (i = 0; i < NLACP_TIMERS; i++) {
963f595a68aSyz147064 		timer = &lacp_timers[i];
964f595a68aSyz147064 		if (timer->lt_id == timer_id) {
965f595a68aSyz147064 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
966f595a68aSyz147064 			    timer->lt_str);
967f595a68aSyz147064 			return (buf);
968f595a68aSyz147064 		}
969f595a68aSyz147064 	}
970f595a68aSyz147064 
971f595a68aSyz147064 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
972f595a68aSyz147064 	return (buf);
973f595a68aSyz147064 }
974f595a68aSyz147064 
975f595a68aSyz147064 const char *
976f595a68aSyz147064 dladm_aggr_portstate2str(aggr_port_state_t state_id, char *buf)
977f595a68aSyz147064 {
978f595a68aSyz147064 	int			i;
979f595a68aSyz147064 	dladm_aggr_port_state_t *state;
980f595a68aSyz147064 
981d62bc4baSyz147064 	if (buf == NULL)
982d62bc4baSyz147064 		return (NULL);
983d62bc4baSyz147064 
984f595a68aSyz147064 	for (i = 0; i < NPORT_STATES; i++) {
985f595a68aSyz147064 		state = &port_states[i];
986f595a68aSyz147064 		if (state->state_id == state_id) {
987f595a68aSyz147064 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
988f595a68aSyz147064 			    state->state_str);
989f595a68aSyz147064 			return (buf);
990f595a68aSyz147064 		}
991f595a68aSyz147064 	}
992f595a68aSyz147064 
993f595a68aSyz147064 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
994f595a68aSyz147064 	return (buf);
995f595a68aSyz147064 }
996f595a68aSyz147064 
997f595a68aSyz147064 static dladm_status_t
998d62bc4baSyz147064 dladm_aggr_persist_aggr_conf(const char *link, datalink_id_t linkid,
999d62bc4baSyz147064     uint16_t key, uint32_t nports, dladm_aggr_port_attr_db_t *ports,
1000d62bc4baSyz147064     uint32_t policy, boolean_t mac_addr_fixed, const uchar_t *mac_addr,
1001d62bc4baSyz147064     aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer,
1002d62bc4baSyz147064     boolean_t force)
1003f595a68aSyz147064 {
1004d62bc4baSyz147064 	dladm_conf_t conf = DLADM_INVALID_CONF;
1005d62bc4baSyz147064 	char *portstr = NULL;
1006d62bc4baSyz147064 	char macstr[ETHERADDRL * 3];
1007f595a68aSyz147064 	dladm_status_t status;
1008d62bc4baSyz147064 	int i, size;
1009d62bc4baSyz147064 	uint64_t u64;
1010f595a68aSyz147064 
1011d62bc4baSyz147064 	if ((status = dladm_create_conf(link, linkid, DATALINK_CLASS_AGGR,
1012d62bc4baSyz147064 	    DL_ETHER, &conf)) != DLADM_STATUS_OK) {
1013f595a68aSyz147064 		return (status);
1014f595a68aSyz147064 	}
1015f595a68aSyz147064 
1016d62bc4baSyz147064 	u64 = key;
1017d62bc4baSyz147064 	status = dladm_set_conf_field(conf, FKEY, DLADM_TYPE_UINT64, &u64);
1018d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1019d62bc4baSyz147064 		goto done;
1020f595a68aSyz147064 
1021d62bc4baSyz147064 	u64 = nports;
1022d62bc4baSyz147064 	status = dladm_set_conf_field(conf, FNPORTS, DLADM_TYPE_UINT64, &u64);
1023d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1024d62bc4baSyz147064 		goto done;
1025f595a68aSyz147064 
1026d62bc4baSyz147064 	size = nports * (LINKID_STR_WIDTH + 1) + 1;
1027d62bc4baSyz147064 	if ((portstr = calloc(1, size)) == NULL) {
1028d62bc4baSyz147064 		status = DLADM_STATUS_NOMEM;
1029f595a68aSyz147064 		goto done;
1030f595a68aSyz147064 	}
1031f595a68aSyz147064 
1032d62bc4baSyz147064 	for (i = 0; i < nports; i++)
1033d62bc4baSyz147064 		WRITE_PORT(portstr, ports[i].lp_linkid, size);
1034d62bc4baSyz147064 	status = dladm_set_conf_field(conf, FPORTS, DLADM_TYPE_STR, portstr);
1035d62bc4baSyz147064 	free(portstr);
1036d62bc4baSyz147064 
1037d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1038d62bc4baSyz147064 		goto done;
1039d62bc4baSyz147064 
1040d62bc4baSyz147064 	u64 = policy;
1041d62bc4baSyz147064 	status = dladm_set_conf_field(conf, FPOLICY, DLADM_TYPE_UINT64, &u64);
1042d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1043d62bc4baSyz147064 		goto done;
1044d62bc4baSyz147064 
1045d62bc4baSyz147064 	status = dladm_set_conf_field(conf, FFIXMACADDR, DLADM_TYPE_BOOLEAN,
1046d62bc4baSyz147064 	    &mac_addr_fixed);
1047d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1048d62bc4baSyz147064 		goto done;
1049d62bc4baSyz147064 
1050d62bc4baSyz147064 	if (mac_addr_fixed) {
1051d62bc4baSyz147064 		if (!VALID_PORT_MAC(mac_addr)) {
1052d62bc4baSyz147064 			status = DLADM_STATUS_MACADDRINVAL;
1053f595a68aSyz147064 			goto done;
1054f595a68aSyz147064 		}
1055d62bc4baSyz147064 
1056d62bc4baSyz147064 		(void) dladm_aggr_macaddr2str(mac_addr, macstr);
1057d62bc4baSyz147064 		status = dladm_set_conf_field(conf, FMACADDR, DLADM_TYPE_STR,
1058d62bc4baSyz147064 		    macstr);
1059d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1060d62bc4baSyz147064 			goto done;
1061f595a68aSyz147064 	}
1062f595a68aSyz147064 
1063d62bc4baSyz147064 	status = dladm_set_conf_field(conf, FFORCE, DLADM_TYPE_BOOLEAN, &force);
1064d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1065d62bc4baSyz147064 		goto done;
1066d62bc4baSyz147064 
1067d62bc4baSyz147064 	u64 = lacp_mode;
1068d62bc4baSyz147064 	status = dladm_set_conf_field(conf, FLACPMODE, DLADM_TYPE_UINT64, &u64);
1069d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1070d62bc4baSyz147064 		goto done;
1071d62bc4baSyz147064 
1072d62bc4baSyz147064 	u64 = lacp_timer;
1073d62bc4baSyz147064 	status = dladm_set_conf_field(conf, FLACPTIMER, DLADM_TYPE_UINT64,
1074d62bc4baSyz147064 	    &u64);
1075d62bc4baSyz147064 	if (status != DLADM_STATUS_OK)
1076d62bc4baSyz147064 		goto done;
1077d62bc4baSyz147064 
1078f595a68aSyz147064 	/*
1079d62bc4baSyz147064 	 * Commit the link aggregation configuration.
1080f595a68aSyz147064 	 */
1081d62bc4baSyz147064 	status = dladm_write_conf(conf);
1082f595a68aSyz147064 
1083f595a68aSyz147064 done:
1084d62bc4baSyz147064 	dladm_destroy_conf(conf);
1085f595a68aSyz147064 	return (status);
1086f595a68aSyz147064 }
1087f595a68aSyz147064 
1088f595a68aSyz147064 /*
1089f595a68aSyz147064  * Create a new link aggregation group. Update the configuration
1090f595a68aSyz147064  * file and bring it up.
1091f595a68aSyz147064  */
1092f595a68aSyz147064 dladm_status_t
1093d62bc4baSyz147064 dladm_aggr_create(const char *name, uint16_t key, uint32_t nports,
1094f595a68aSyz147064     dladm_aggr_port_attr_db_t *ports, uint32_t policy, boolean_t mac_addr_fixed,
1095d62bc4baSyz147064     const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode,
1096d62bc4baSyz147064     aggr_lacp_timer_t lacp_timer, uint32_t flags)
1097f595a68aSyz147064 {
1098d62bc4baSyz147064 	datalink_id_t linkid = DATALINK_INVALID_LINKID;
1099d62bc4baSyz147064 	uint32_t media;
1100d62bc4baSyz147064 	uint32_t i;
1101d62bc4baSyz147064 	datalink_class_t class;
1102f595a68aSyz147064 	dladm_status_t status;
1103d62bc4baSyz147064 	boolean_t force = (flags & DLADM_OPT_FORCE) ? B_TRUE : B_FALSE;
1104f595a68aSyz147064 
1105d62bc4baSyz147064 	if (key != 0 && key > AGGR_MAX_KEY)
1106f595a68aSyz147064 		return (DLADM_STATUS_KEYINVAL);
1107f595a68aSyz147064 
1108d62bc4baSyz147064 	if (nports == 0)
1109d62bc4baSyz147064 		return (DLADM_STATUS_BADARG);
1110f595a68aSyz147064 
1111d62bc4baSyz147064 	for (i = 0; i < nports; i++) {
1112d62bc4baSyz147064 		if ((dladm_datalink_id2info(ports[i].lp_linkid, NULL,
1113d62bc4baSyz147064 		    &class, &media, NULL, 0) != DLADM_STATUS_OK) ||
1114*da14cebeSEric Cheng 		    !((class == DATALINK_CLASS_PHYS) && (media == DL_ETHER))) {
1115d62bc4baSyz147064 			return (DLADM_STATUS_BADARG);
1116d62bc4baSyz147064 		}
1117d62bc4baSyz147064 	}
1118f595a68aSyz147064 
1119d62bc4baSyz147064 	flags &= (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
1120d62bc4baSyz147064 	if ((status = dladm_create_datalink_id(name, DATALINK_CLASS_AGGR,
1121d62bc4baSyz147064 	    DL_ETHER, flags, &linkid)) != DLADM_STATUS_OK) {
1122d62bc4baSyz147064 		goto fail;
1123d62bc4baSyz147064 	}
1124f595a68aSyz147064 
1125d62bc4baSyz147064 	if ((flags & DLADM_OPT_PERSIST) &&
1126d62bc4baSyz147064 	    (status = dladm_aggr_persist_aggr_conf(name, linkid, key, nports,
1127d62bc4baSyz147064 	    ports, policy, mac_addr_fixed, mac_addr, lacp_mode, lacp_timer,
1128d62bc4baSyz147064 	    force)) != DLADM_STATUS_OK) {
1129d62bc4baSyz147064 		goto fail;
1130d62bc4baSyz147064 	}
1131d62bc4baSyz147064 
1132d62bc4baSyz147064 	if (!(flags & DLADM_OPT_ACTIVE))
1133d62bc4baSyz147064 		return (DLADM_STATUS_OK);
1134d62bc4baSyz147064 
1135d62bc4baSyz147064 	status = i_dladm_aggr_create_sys(linkid, key, nports, ports, policy,
1136d62bc4baSyz147064 	    mac_addr_fixed, mac_addr, lacp_mode, lacp_timer, force);
1137d62bc4baSyz147064 
1138d62bc4baSyz147064 	if (status != DLADM_STATUS_OK) {
1139d62bc4baSyz147064 		if (flags & DLADM_OPT_PERSIST)
1140d62bc4baSyz147064 			(void) dladm_remove_conf(linkid);
1141d62bc4baSyz147064 		goto fail;
1142d62bc4baSyz147064 	}
1143d62bc4baSyz147064 
1144d62bc4baSyz147064 	return (DLADM_STATUS_OK);
1145d62bc4baSyz147064 
1146d62bc4baSyz147064 fail:
1147d62bc4baSyz147064 	if (linkid != DATALINK_INVALID_LINKID)
1148d62bc4baSyz147064 		(void) dladm_destroy_datalink_id(linkid, flags);
1149d62bc4baSyz147064 
1150f595a68aSyz147064 	return (status);
1151f595a68aSyz147064 }
1152f595a68aSyz147064 
1153d62bc4baSyz147064 static dladm_status_t
1154d62bc4baSyz147064 i_dladm_aggr_get_aggr_attr(dladm_conf_t conf, uint32_t mask,
1155d62bc4baSyz147064     dladm_aggr_modify_attr_t *attrp)
1156d62bc4baSyz147064 {
1157d62bc4baSyz147064 	dladm_status_t status = DLADM_STATUS_OK;
1158d62bc4baSyz147064 	char macstr[ETHERADDRL * 3];
1159d62bc4baSyz147064 	uint64_t u64;
1160f595a68aSyz147064 
1161d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_POLICY) {
1162d62bc4baSyz147064 		status = dladm_get_conf_field(conf, FPOLICY, &u64,
1163d62bc4baSyz147064 		    sizeof (u64));
1164d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1165d62bc4baSyz147064 			return (status);
1166d62bc4baSyz147064 		attrp->ld_policy = (uint32_t)u64;
1167d62bc4baSyz147064 	}
1168d62bc4baSyz147064 
1169d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_MAC) {
1170d62bc4baSyz147064 		status = dladm_get_conf_field(conf, FFIXMACADDR,
1171d62bc4baSyz147064 		    &attrp->ld_mac_fixed, sizeof (boolean_t));
1172d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1173d62bc4baSyz147064 			return (status);
1174d62bc4baSyz147064 
1175d62bc4baSyz147064 		if (attrp->ld_mac_fixed) {
1176d62bc4baSyz147064 			boolean_t fixed;
1177d62bc4baSyz147064 
1178d62bc4baSyz147064 			status = dladm_get_conf_field(conf, FMACADDR,
1179d62bc4baSyz147064 			    macstr, sizeof (macstr));
1180d62bc4baSyz147064 			if (status != DLADM_STATUS_OK)
1181d62bc4baSyz147064 				return (status);
1182d62bc4baSyz147064 
1183d62bc4baSyz147064 			if (!dladm_aggr_str2macaddr(macstr, &fixed,
1184d62bc4baSyz147064 			    attrp->ld_mac)) {
1185d62bc4baSyz147064 				return (DLADM_STATUS_REPOSITORYINVAL);
1186d62bc4baSyz147064 			}
1187d62bc4baSyz147064 		}
1188d62bc4baSyz147064 	}
1189d62bc4baSyz147064 
1190d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE) {
1191d62bc4baSyz147064 		status = dladm_get_conf_field(conf, FLACPMODE, &u64,
1192d62bc4baSyz147064 		    sizeof (u64));
1193d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1194d62bc4baSyz147064 			return (status);
1195d62bc4baSyz147064 		attrp->ld_lacp_mode = (aggr_lacp_mode_t)u64;
1196d62bc4baSyz147064 	}
1197d62bc4baSyz147064 
1198d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) {
1199d62bc4baSyz147064 		status = dladm_get_conf_field(conf, FLACPTIMER, &u64,
1200d62bc4baSyz147064 		    sizeof (u64));
1201d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1202d62bc4baSyz147064 			return (status);
1203d62bc4baSyz147064 		attrp->ld_lacp_timer = (aggr_lacp_timer_t)u64;
1204d62bc4baSyz147064 	}
1205d62bc4baSyz147064 
1206d62bc4baSyz147064 	return (status);
1207d62bc4baSyz147064 }
1208d62bc4baSyz147064 
1209d62bc4baSyz147064 static dladm_status_t
1210d62bc4baSyz147064 i_dladm_aggr_set_aggr_attr(dladm_conf_t conf, uint32_t mask,
1211d62bc4baSyz147064     dladm_aggr_modify_attr_t *attrp)
1212d62bc4baSyz147064 {
1213d62bc4baSyz147064 	dladm_status_t status = DLADM_STATUS_OK;
1214d62bc4baSyz147064 	char macstr[ETHERADDRL * 3];
1215d62bc4baSyz147064 	uint64_t u64;
1216d62bc4baSyz147064 
1217d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_POLICY) {
1218d62bc4baSyz147064 		u64 = attrp->ld_policy;
1219d62bc4baSyz147064 		status = dladm_set_conf_field(conf, FPOLICY, DLADM_TYPE_UINT64,
1220d62bc4baSyz147064 		    &u64);
1221d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1222d62bc4baSyz147064 			return (status);
1223d62bc4baSyz147064 	}
1224d62bc4baSyz147064 
1225d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_MAC) {
1226d62bc4baSyz147064 		status = dladm_set_conf_field(conf, FFIXMACADDR,
1227d62bc4baSyz147064 		    DLADM_TYPE_BOOLEAN, &attrp->ld_mac_fixed);
1228d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1229d62bc4baSyz147064 			return (status);
1230d62bc4baSyz147064 
1231d62bc4baSyz147064 		if (attrp->ld_mac_fixed) {
1232d62bc4baSyz147064 			(void) dladm_aggr_macaddr2str(attrp->ld_mac, macstr);
1233d62bc4baSyz147064 			status = dladm_set_conf_field(conf, FMACADDR,
1234d62bc4baSyz147064 			    DLADM_TYPE_STR, macstr);
1235d62bc4baSyz147064 			if (status != DLADM_STATUS_OK)
1236d62bc4baSyz147064 				return (status);
1237d62bc4baSyz147064 		}
1238d62bc4baSyz147064 	}
1239d62bc4baSyz147064 
1240d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE) {
1241d62bc4baSyz147064 		u64 = attrp->ld_lacp_mode;
1242d62bc4baSyz147064 		status = dladm_set_conf_field(conf, FLACPMODE,
1243d62bc4baSyz147064 		    DLADM_TYPE_UINT64, &u64);
1244d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1245d62bc4baSyz147064 			return (status);
1246d62bc4baSyz147064 	}
1247d62bc4baSyz147064 
1248d62bc4baSyz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) {
1249d62bc4baSyz147064 		u64 = attrp->ld_lacp_timer;
1250d62bc4baSyz147064 		status = dladm_set_conf_field(conf, FLACPTIMER,
1251d62bc4baSyz147064 		    DLADM_TYPE_UINT64, &u64);
1252d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1253d62bc4baSyz147064 			return (status);
1254d62bc4baSyz147064 	}
1255f595a68aSyz147064 
1256f595a68aSyz147064 	return (status);
1257f595a68aSyz147064 }
1258f595a68aSyz147064 
1259f595a68aSyz147064 /*
1260f595a68aSyz147064  * Modify the parameters of an existing link aggregation group. Update
1261f595a68aSyz147064  * the configuration file and pass the changes to the kernel.
1262f595a68aSyz147064  */
1263f595a68aSyz147064 dladm_status_t
1264d62bc4baSyz147064 dladm_aggr_modify(datalink_id_t linkid, uint32_t modify_mask, uint32_t policy,
1265d62bc4baSyz147064     boolean_t mac_fixed, const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode,
1266d62bc4baSyz147064     aggr_lacp_timer_t lacp_timer, uint32_t flags)
1267f595a68aSyz147064 {
1268f595a68aSyz147064 	dladm_aggr_modify_attr_t new_attr, old_attr;
1269d62bc4baSyz147064 	dladm_conf_t conf;
1270f595a68aSyz147064 	dladm_status_t status;
1271f595a68aSyz147064 
1272f595a68aSyz147064 	new_attr.ld_policy = policy;
1273f595a68aSyz147064 	new_attr.ld_mac_fixed = mac_fixed;
1274f595a68aSyz147064 	new_attr.ld_lacp_mode = lacp_mode;
1275f595a68aSyz147064 	new_attr.ld_lacp_timer = lacp_timer;
1276d62bc4baSyz147064 	bcopy(mac_addr, new_attr.ld_mac, ETHERADDRL);
1277f595a68aSyz147064 
1278d62bc4baSyz147064 	if (flags & DLADM_OPT_PERSIST) {
1279d62bc4baSyz147064 		status = dladm_read_conf(linkid, &conf);
1280d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1281d62bc4baSyz147064 			return (status);
1282d62bc4baSyz147064 
1283d62bc4baSyz147064 		if ((status = i_dladm_aggr_get_aggr_attr(conf, modify_mask,
1284d62bc4baSyz147064 		    &old_attr)) != DLADM_STATUS_OK) {
1285d62bc4baSyz147064 			goto done;
1286d62bc4baSyz147064 		}
1287d62bc4baSyz147064 
1288d62bc4baSyz147064 		if ((status = i_dladm_aggr_set_aggr_attr(conf, modify_mask,
1289d62bc4baSyz147064 		    &new_attr)) != DLADM_STATUS_OK) {
1290d62bc4baSyz147064 			goto done;
1291d62bc4baSyz147064 		}
1292d62bc4baSyz147064 
1293d62bc4baSyz147064 		status = dladm_write_conf(conf);
1294d62bc4baSyz147064 
1295d62bc4baSyz147064 done:
1296d62bc4baSyz147064 		dladm_destroy_conf(conf);
1297d62bc4baSyz147064 		if (status != DLADM_STATUS_OK)
1298f595a68aSyz147064 			return (status);
1299f595a68aSyz147064 	}
1300f595a68aSyz147064 
1301d62bc4baSyz147064 	if (!(flags & DLADM_OPT_ACTIVE))
1302d62bc4baSyz147064 		return (DLADM_STATUS_OK);
1303d62bc4baSyz147064 
1304d62bc4baSyz147064 	status = i_dladm_aggr_modify_sys(linkid, modify_mask, &new_attr);
1305d62bc4baSyz147064 	if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) {
1306d62bc4baSyz147064 		if (dladm_read_conf(linkid, &conf) == DLADM_STATUS_OK) {
1307d62bc4baSyz147064 			if (i_dladm_aggr_set_aggr_attr(conf, modify_mask,
1308d62bc4baSyz147064 			    &old_attr) == DLADM_STATUS_OK) {
1309d62bc4baSyz147064 				(void) dladm_write_conf(conf);
1310d62bc4baSyz147064 			}
1311d62bc4baSyz147064 			dladm_destroy_conf(conf);
1312d62bc4baSyz147064 		}
1313f595a68aSyz147064 	}
1314f595a68aSyz147064 
1315f595a68aSyz147064 	return (status);
1316f595a68aSyz147064 }
1317f595a68aSyz147064 
1318d62bc4baSyz147064 typedef struct aggr_held_arg_s {
1319d62bc4baSyz147064 	datalink_id_t	aggrid;
1320d62bc4baSyz147064 	boolean_t	isheld;
1321d62bc4baSyz147064 } aggr_held_arg_t;
1322d62bc4baSyz147064 
1323d62bc4baSyz147064 static int
1324d62bc4baSyz147064 i_dladm_aggr_is_held(datalink_id_t linkid, void *arg)
1325d62bc4baSyz147064 {
1326d62bc4baSyz147064 	aggr_held_arg_t		*aggr_held_arg = arg;
1327d62bc4baSyz147064 	dladm_vlan_attr_t	dva;
1328d62bc4baSyz147064 
1329d62bc4baSyz147064 	if (dladm_vlan_info(linkid, &dva, DLADM_OPT_PERSIST) != DLADM_STATUS_OK)
1330d62bc4baSyz147064 		return (DLADM_WALK_CONTINUE);
1331d62bc4baSyz147064 
1332d62bc4baSyz147064 	if (dva.dv_linkid == aggr_held_arg->aggrid) {
1333d62bc4baSyz147064 		/*
1334d62bc4baSyz147064 		 * This VLAN is created over the given aggregation.
1335d62bc4baSyz147064 		 */
1336d62bc4baSyz147064 		aggr_held_arg->isheld = B_TRUE;
1337d62bc4baSyz147064 		return (DLADM_WALK_TERMINATE);
1338d62bc4baSyz147064 	}
1339d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
1340d62bc4baSyz147064 }
1341d62bc4baSyz147064 
1342f595a68aSyz147064 /*
1343d62bc4baSyz147064  * Delete a previously created link aggregation group. Either the name "aggr"
1344d62bc4baSyz147064  * or the "key" is specified.
1345f595a68aSyz147064  */
1346f595a68aSyz147064 dladm_status_t
1347d62bc4baSyz147064 dladm_aggr_delete(datalink_id_t linkid, uint32_t flags)
1348f595a68aSyz147064 {
1349d62bc4baSyz147064 	laioc_delete_t ioc;
1350d62bc4baSyz147064 	datalink_class_t class;
1351f595a68aSyz147064 	dladm_status_t status;
1352f595a68aSyz147064 
1353d62bc4baSyz147064 	if ((dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0) !=
1354d62bc4baSyz147064 	    DLADM_STATUS_OK) || (class != DATALINK_CLASS_AGGR)) {
1355d62bc4baSyz147064 		return (DLADM_STATUS_BADARG);
1356d62bc4baSyz147064 	}
1357f595a68aSyz147064 
1358d62bc4baSyz147064 	if (flags & DLADM_OPT_ACTIVE) {
1359d62bc4baSyz147064 		ioc.ld_linkid = linkid;
1360eae72b5bSSebastien Roy 		if ((i_dladm_aggr_ioctl(LAIOC_DELETE, &ioc) < 0) &&
1361d62bc4baSyz147064 		    ((errno != ENOENT) || !(flags & DLADM_OPT_PERSIST))) {
1362d62bc4baSyz147064 			status = dladm_errno2status(errno);
1363f595a68aSyz147064 			return (status);
1364f595a68aSyz147064 		}
1365d62bc4baSyz147064 
1366d62bc4baSyz147064 		/*
1367d62bc4baSyz147064 		 * Delete ACTIVE linkprop first.
1368d62bc4baSyz147064 		 */
1369d62bc4baSyz147064 		(void) dladm_set_linkprop(linkid, NULL, NULL, 0,
1370d62bc4baSyz147064 		    DLADM_OPT_ACTIVE);
1371d62bc4baSyz147064 		(void) dladm_destroy_datalink_id(linkid, DLADM_OPT_ACTIVE);
1372f595a68aSyz147064 	}
1373f595a68aSyz147064 
1374d62bc4baSyz147064 	/*
1375d62bc4baSyz147064 	 * If we reach here, it means that the active aggregation has already
1376d62bc4baSyz147064 	 * been deleted, and there is no active VLANs holding this aggregation.
1377d62bc4baSyz147064 	 * Now we see whether there is any persistent VLANs holding this
1378d62bc4baSyz147064 	 * aggregation. If so, we fail the operation.
1379d62bc4baSyz147064 	 */
1380d62bc4baSyz147064 	if (flags & DLADM_OPT_PERSIST) {
1381d62bc4baSyz147064 		aggr_held_arg_t arg;
1382f595a68aSyz147064 
1383d62bc4baSyz147064 		arg.aggrid = linkid;
1384d62bc4baSyz147064 		arg.isheld = B_FALSE;
1385d62bc4baSyz147064 
1386d62bc4baSyz147064 		(void) dladm_walk_datalink_id(i_dladm_aggr_is_held,
1387d62bc4baSyz147064 		    &arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
1388d62bc4baSyz147064 		    DLADM_OPT_PERSIST);
1389d62bc4baSyz147064 		if (arg.isheld)
1390d62bc4baSyz147064 			return (DLADM_STATUS_LINKBUSY);
1391d62bc4baSyz147064 
1392d62bc4baSyz147064 		(void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST);
1393d62bc4baSyz147064 		(void) dladm_remove_conf(linkid);
1394d62bc4baSyz147064 	}
1395d62bc4baSyz147064 
1396d62bc4baSyz147064 	return (DLADM_STATUS_OK);
1397f595a68aSyz147064 }
1398f595a68aSyz147064 
1399f595a68aSyz147064 /*
1400f595a68aSyz147064  * Add one or more ports to an existing link aggregation.
1401f595a68aSyz147064  */
1402f595a68aSyz147064 dladm_status_t
1403d62bc4baSyz147064 dladm_aggr_add(datalink_id_t linkid, uint32_t nports,
1404d62bc4baSyz147064     dladm_aggr_port_attr_db_t *ports, uint32_t flags)
1405f595a68aSyz147064 {
1406d62bc4baSyz147064 	return (i_dladm_aggr_add_rmv(linkid, nports, ports, flags, LAIOC_ADD));
1407f595a68aSyz147064 }
1408f595a68aSyz147064 
1409f595a68aSyz147064 /*
1410f595a68aSyz147064  * Remove one or more ports from an existing link aggregation.
1411f595a68aSyz147064  */
1412f595a68aSyz147064 dladm_status_t
1413d62bc4baSyz147064 dladm_aggr_remove(datalink_id_t linkid, uint32_t nports,
1414d62bc4baSyz147064     dladm_aggr_port_attr_db_t *ports, uint32_t flags)
1415f595a68aSyz147064 {
1416d62bc4baSyz147064 	return (i_dladm_aggr_add_rmv(linkid, nports, ports, flags,
1417d62bc4baSyz147064 	    LAIOC_REMOVE));
1418f595a68aSyz147064 }
1419f595a68aSyz147064 
1420d62bc4baSyz147064 typedef struct i_walk_key_state_s {
1421d62bc4baSyz147064 	uint16_t key;
1422d62bc4baSyz147064 	datalink_id_t linkid;
1423d62bc4baSyz147064 	boolean_t found;
1424d62bc4baSyz147064 } i_walk_key_state_t;
1425f595a68aSyz147064 
1426d62bc4baSyz147064 static int
1427d62bc4baSyz147064 i_dladm_walk_key2linkid(datalink_id_t linkid, void *arg)
1428d62bc4baSyz147064 {
1429d62bc4baSyz147064 	dladm_conf_t conf;
1430d62bc4baSyz147064 	uint16_t key;
1431d62bc4baSyz147064 	dladm_status_t status;
1432d62bc4baSyz147064 	i_walk_key_state_t *statep = (i_walk_key_state_t *)arg;
1433d62bc4baSyz147064 	uint64_t u64;
1434d62bc4baSyz147064 
1435d62bc4baSyz147064 	if (dladm_read_conf(linkid, &conf) != 0)
1436d62bc4baSyz147064 		return (DLADM_WALK_CONTINUE);
1437d62bc4baSyz147064 
1438d62bc4baSyz147064 	status = dladm_get_conf_field(conf, FKEY, &u64, sizeof (u64));
1439d62bc4baSyz147064 	key = (uint16_t)u64;
1440d62bc4baSyz147064 	dladm_destroy_conf(conf);
1441d62bc4baSyz147064 
1442d62bc4baSyz147064 	if ((status == DLADM_STATUS_OK) && (key == statep->key)) {
1443d62bc4baSyz147064 		statep->found = B_TRUE;
1444d62bc4baSyz147064 		statep->linkid = linkid;
1445d62bc4baSyz147064 		return (DLADM_WALK_TERMINATE);
1446d62bc4baSyz147064 	}
1447d62bc4baSyz147064 
1448d62bc4baSyz147064 	return (DLADM_WALK_CONTINUE);
1449d62bc4baSyz147064 }
1450d62bc4baSyz147064 
1451d62bc4baSyz147064 dladm_status_t
1452d62bc4baSyz147064 dladm_key2linkid(uint16_t key, datalink_id_t *linkidp, uint32_t flags)
1453d62bc4baSyz147064 {
1454d62bc4baSyz147064 	i_walk_key_state_t state;
1455d62bc4baSyz147064 
1456d62bc4baSyz147064 	if (key > AGGR_MAX_KEY)
1457d62bc4baSyz147064 		return (DLADM_STATUS_NOTFOUND);
1458d62bc4baSyz147064 
1459d62bc4baSyz147064 	state.found = B_FALSE;
1460d62bc4baSyz147064 	state.key = key;
1461d62bc4baSyz147064 
1462d62bc4baSyz147064 	(void) dladm_walk_datalink_id(i_dladm_walk_key2linkid, &state,
1463d62bc4baSyz147064 	    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags);
1464d62bc4baSyz147064 	if (state.found == B_TRUE) {
1465d62bc4baSyz147064 		*linkidp = state.linkid;
1466d62bc4baSyz147064 		return (DLADM_STATUS_OK);
1467d62bc4baSyz147064 	} else {
1468d62bc4baSyz147064 		return (DLADM_STATUS_NOTFOUND);
1469d62bc4baSyz147064 	}
1470f595a68aSyz147064 }
1471