13871Syz147064 /*
23871Syz147064  * CDDL HEADER START
33871Syz147064  *
43871Syz147064  * The contents of this file are subject to the terms of the
53871Syz147064  * Common Development and Distribution License (the "License").
63871Syz147064  * You may not use this file except in compliance with the License.
73871Syz147064  *
83871Syz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93871Syz147064  * or http://www.opensolaris.org/os/licensing.
103871Syz147064  * See the License for the specific language governing permissions
113871Syz147064  * and limitations under the License.
123871Syz147064  *
133871Syz147064  * When distributing Covered Code, include this CDDL HEADER in each
143871Syz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153871Syz147064  * If applicable, add the following below this CDDL HEADER, with the
163871Syz147064  * fields enclosed by brackets "[]" replaced with your own identifying
173871Syz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
183871Syz147064  *
193871Syz147064  * CDDL HEADER END
203871Syz147064  */
213871Syz147064 /*
22*5895Syz147064  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
233871Syz147064  * Use is subject to license terms.
243871Syz147064  */
253871Syz147064 
263871Syz147064 #pragma ident	"%Z%%M%	%I%	%E% SMI"
273871Syz147064 
283871Syz147064 #include <stdio.h>
293871Syz147064 #include <sys/types.h>
303871Syz147064 #include <sys/stat.h>
313871Syz147064 #include <string.h>
323871Syz147064 #include <fcntl.h>
333871Syz147064 #include <unistd.h>
343871Syz147064 #include <stropts.h>
353871Syz147064 #include <stdlib.h>
363871Syz147064 #include <errno.h>
37*5895Syz147064 #include <assert.h>
383871Syz147064 #include <strings.h>
393871Syz147064 #include <libintl.h>
403871Syz147064 #include <net/if_types.h>
413871Syz147064 #include <net/if_dl.h>
42*5895Syz147064 #include <libdllink.h>
43*5895Syz147064 #include <libdlvlan.h>
443871Syz147064 #include <libdlaggr.h>
453871Syz147064 #include <libdladm_impl.h>
463871Syz147064 
473871Syz147064 /*
483871Syz147064  * Link Aggregation Administration Library.
493871Syz147064  *
503871Syz147064  * This library is used by administration tools such as dladm(1M) to
513871Syz147064  * configure link aggregations.
523871Syz147064  */
533871Syz147064 
54*5895Syz147064 #define	DLADM_AGGR_DEV	"/devices/pseudo/aggr@0:" AGGR_DEVNAME_CTL
553871Syz147064 
563871Syz147064 /* Limits on buffer size for LAIOC_INFO request */
573871Syz147064 #define	MIN_INFO_SIZE (4*1024)
583871Syz147064 #define	MAX_INFO_SIZE (128*1024)
593871Syz147064 
60*5895Syz147064 static uchar_t	zero_mac[] = {0, 0, 0, 0, 0, 0};
61*5895Syz147064 #define	VALID_PORT_MAC(mac)						\
62*5895Syz147064 	(((mac) != NULL) && (bcmp(zero_mac, (mac), ETHERADDRL) != 0) &&	\
63*5895Syz147064 	(!(mac)[0] & 0x01))
643871Syz147064 
65*5895Syz147064 #define	PORT_DELIMITER	'.'
663871Syz147064 
67*5895Syz147064 #define	WRITE_PORT(portstr, portid, size) {			\
68*5895Syz147064 	char pstr[LINKID_STR_WIDTH + 2];			\
69*5895Syz147064 	(void) snprintf(pstr, LINKID_STR_WIDTH + 2, "%d%c",	\
70*5895Syz147064 	    (portid), PORT_DELIMITER);				\
71*5895Syz147064 	(void) strlcat((portstr), pstr, (size));		\
72*5895Syz147064 }
733871Syz147064 
74*5895Syz147064 #define	READ_PORT(portstr, portid, status) {			\
75*5895Syz147064 	errno = 0;						\
76*5895Syz147064 	(status) = DLADM_STATUS_OK;				\
77*5895Syz147064 	(portid) = (int)strtol((portstr), &(portstr), 10);	\
78*5895Syz147064 	if (errno != 0 || *(portstr) != PORT_DELIMITER) {	\
79*5895Syz147064 		(status) = DLADM_STATUS_REPOSITORYINVAL;	\
80*5895Syz147064 	} else {						\
81*5895Syz147064 		/* Skip the delimiter. */			\
82*5895Syz147064 		(portstr)++;					\
83*5895Syz147064 	}							\
84*5895Syz147064 }
853871Syz147064 
863871Syz147064 typedef struct dladm_aggr_modify_attr {
873871Syz147064 	uint32_t	ld_policy;
883871Syz147064 	boolean_t	ld_mac_fixed;
893871Syz147064 	uchar_t		ld_mac[ETHERADDRL];
903871Syz147064 	aggr_lacp_mode_t ld_lacp_mode;
913871Syz147064 	aggr_lacp_timer_t ld_lacp_timer;
923871Syz147064 } dladm_aggr_modify_attr_t;
933871Syz147064 
943871Syz147064 typedef struct policy_s {
953871Syz147064 	char		*pol_name;
963871Syz147064 	uint32_t	policy;
973871Syz147064 } policy_t;
983871Syz147064 
993871Syz147064 static policy_t policies[] = {
1003871Syz147064 	{"L2",		AGGR_POLICY_L2},
1013871Syz147064 	{"L3",		AGGR_POLICY_L3},
1023871Syz147064 	{"L4",		AGGR_POLICY_L4}};
1033871Syz147064 
1043871Syz147064 #define	NPOLICIES	(sizeof (policies) / sizeof (policy_t))
1053871Syz147064 
1063871Syz147064 typedef struct dladm_aggr_lacpmode_s {
1073871Syz147064 	char		*mode_str;
1083871Syz147064 	aggr_lacp_mode_t mode_id;
1093871Syz147064 } dladm_aggr_lacpmode_t;
1103871Syz147064 
1113871Syz147064 static dladm_aggr_lacpmode_t lacp_modes[] = {
1123871Syz147064 	{"off", AGGR_LACP_OFF},
1133871Syz147064 	{"active", AGGR_LACP_ACTIVE},
1143871Syz147064 	{"passive", AGGR_LACP_PASSIVE}};
1153871Syz147064 
1163871Syz147064 #define	NLACP_MODES	(sizeof (lacp_modes) / sizeof (dladm_aggr_lacpmode_t))
1173871Syz147064 
1183871Syz147064 typedef struct dladm_aggr_lacptimer_s {
1193871Syz147064 	char		*lt_str;
1203871Syz147064 	aggr_lacp_timer_t lt_id;
1213871Syz147064 } dladm_aggr_lacptimer_t;
1223871Syz147064 
1233871Syz147064 static dladm_aggr_lacptimer_t lacp_timers[] = {
1243871Syz147064 	{"short", AGGR_LACP_TIMER_SHORT},
1253871Syz147064 	{"long", AGGR_LACP_TIMER_LONG}};
1263871Syz147064 
1273871Syz147064 #define	NLACP_TIMERS	(sizeof (lacp_timers) / sizeof (dladm_aggr_lacptimer_t))
1283871Syz147064 
1293871Syz147064 typedef struct dladm_aggr_port_state {
1303871Syz147064 	char			*state_str;
1313871Syz147064 	aggr_port_state_t	state_id;
1323871Syz147064 } dladm_aggr_port_state_t;
1333871Syz147064 
1343871Syz147064 static dladm_aggr_port_state_t port_states[] = {
1353871Syz147064 	{"standby", AGGR_PORT_STATE_STANDBY },
1363871Syz147064 	{"attached", AGGR_PORT_STATE_ATTACHED }
1373871Syz147064 };
1383871Syz147064 
1393871Syz147064 #define	NPORT_STATES	\
1403871Syz147064 	(sizeof (port_states) / sizeof (dladm_aggr_port_state_t))
1413871Syz147064 
142*5895Syz147064 static int
143*5895Syz147064 i_dladm_aggr_strioctl(int cmd, void *ptr, int ilen)
144*5895Syz147064 {
145*5895Syz147064 	int err, fd;
1463871Syz147064 
147*5895Syz147064 	if ((fd = open(DLADM_AGGR_DEV, O_RDWR)) < 0)
1483871Syz147064 		return (-1);
1493871Syz147064 
150*5895Syz147064 	err = i_dladm_ioctl(fd, cmd, ptr, ilen);
151*5895Syz147064 	(void) close(fd);
1523871Syz147064 
153*5895Syz147064 	return (err);
1543871Syz147064 }
1553871Syz147064 
1563871Syz147064 /*
157*5895Syz147064  * Caller must free attr.lg_ports. The ptr pointer is advanced while convert
158*5895Syz147064  * the laioc_info_t to the dladm_aggr_grp_attr_t structure.
1593871Syz147064  */
160*5895Syz147064 static int
161*5895Syz147064 i_dladm_aggr_iocp2grpattr(void **ptr, dladm_aggr_grp_attr_t *attrp)
1623871Syz147064 {
163*5895Syz147064 	laioc_info_group_t	*grp;
164*5895Syz147064 	laioc_info_port_t	*port;
165*5895Syz147064 	int			i;
166*5895Syz147064 	void			*where = (*ptr);
167*5895Syz147064 
168*5895Syz147064 	grp = (laioc_info_group_t *)where;
169*5895Syz147064 
170*5895Syz147064 	attrp->lg_linkid = grp->lg_linkid;
171*5895Syz147064 	attrp->lg_key = grp->lg_key;
172*5895Syz147064 	attrp->lg_nports = grp->lg_nports;
173*5895Syz147064 	attrp->lg_policy = grp->lg_policy;
174*5895Syz147064 	attrp->lg_lacp_mode = grp->lg_lacp_mode;
175*5895Syz147064 	attrp->lg_lacp_timer = grp->lg_lacp_timer;
176*5895Syz147064 	attrp->lg_force = grp->lg_force;
177*5895Syz147064 
178*5895Syz147064 	bcopy(grp->lg_mac, attrp->lg_mac, ETHERADDRL);
179*5895Syz147064 	attrp->lg_mac_fixed = grp->lg_mac_fixed;
1803871Syz147064 
181*5895Syz147064 	if ((attrp->lg_ports = malloc(grp->lg_nports *
182*5895Syz147064 	    sizeof (dladm_aggr_port_attr_t))) == NULL) {
183*5895Syz147064 		errno = ENOMEM;
184*5895Syz147064 		goto fail;
185*5895Syz147064 	}
186*5895Syz147064 
187*5895Syz147064 	where = (grp + 1);
1883871Syz147064 
189*5895Syz147064 	/*
190*5895Syz147064 	 * Go through each port that is part of the group.
191*5895Syz147064 	 */
192*5895Syz147064 	for (i = 0; i < grp->lg_nports; i++) {
193*5895Syz147064 		port = (laioc_info_port_t *)where;
1943871Syz147064 
195*5895Syz147064 		attrp->lg_ports[i].lp_linkid = port->lp_linkid;
196*5895Syz147064 		bcopy(port->lp_mac, attrp->lg_ports[i].lp_mac, ETHERADDRL);
197*5895Syz147064 		attrp->lg_ports[i].lp_state = port->lp_state;
198*5895Syz147064 		attrp->lg_ports[i].lp_lacp_state = port->lp_lacp_state;
199*5895Syz147064 
200*5895Syz147064 		where = (port + 1);
201*5895Syz147064 	}
202*5895Syz147064 	*ptr = where;
203*5895Syz147064 	return (0);
204*5895Syz147064 fail:
205*5895Syz147064 	return (-1);
2063871Syz147064 }
2073871Syz147064 
2083871Syz147064 /*
209*5895Syz147064  * Get active configuration of a specific aggregation.
210*5895Syz147064  * Caller must free attrp->la_ports.
2113871Syz147064  */
212*5895Syz147064 static dladm_status_t
213*5895Syz147064 i_dladm_aggr_info_active(datalink_id_t linkid, dladm_aggr_grp_attr_t *attrp)
2143871Syz147064 {
2153871Syz147064 	laioc_info_t *ioc;
216*5895Syz147064 	int rc, bufsize;
217*5895Syz147064 	void *where;
218*5895Syz147064 	dladm_status_t status = DLADM_STATUS_OK;
2193871Syz147064 
2203871Syz147064 	bufsize = MIN_INFO_SIZE;
2213871Syz147064 	ioc = (laioc_info_t *)calloc(1, bufsize);
222*5895Syz147064 	if (ioc == NULL)
223*5895Syz147064 		return (DLADM_STATUS_NOMEM);
224*5895Syz147064 
225*5895Syz147064 	ioc->li_group_linkid = linkid;
2263871Syz147064 
2273871Syz147064 tryagain:
228*5895Syz147064 	rc = i_dladm_aggr_strioctl(LAIOC_INFO, ioc, bufsize);
2293871Syz147064 	if (rc != 0) {
2303871Syz147064 		if (errno == ENOSPC) {
2313871Syz147064 			/*
2323871Syz147064 			 * The LAIOC_INFO call failed due to a short
2333871Syz147064 			 * buffer. Reallocate the buffer and try again.
2343871Syz147064 			 */
2353871Syz147064 			bufsize *= 2;
2363871Syz147064 			if (bufsize <= MAX_INFO_SIZE) {
2373871Syz147064 				ioc = (laioc_info_t *)realloc(ioc, bufsize);
2383871Syz147064 				if (ioc != NULL) {
2393871Syz147064 					bzero(ioc, sizeof (bufsize));
2403871Syz147064 					goto tryagain;
2413871Syz147064 				}
2423871Syz147064 			}
2433871Syz147064 		}
244*5895Syz147064 		status = dladm_errno2status(errno);
2453871Syz147064 		goto bail;
2463871Syz147064 	}
2473871Syz147064 
2483871Syz147064 	/*
2493871Syz147064 	 * Go through each group returned by the aggregation driver.
2503871Syz147064 	 */
2513871Syz147064 	where = (char *)(ioc + 1);
252*5895Syz147064 	if (i_dladm_aggr_iocp2grpattr(&where, attrp) != 0) {
253*5895Syz147064 		status = dladm_errno2status(errno);
254*5895Syz147064 		goto bail;
2553871Syz147064 	}
2563871Syz147064 
2573871Syz147064 bail:
2583871Syz147064 	free(ioc);
2593871Syz147064 	return (status);
2603871Syz147064 }
2613871Syz147064 
2623871Syz147064 /*
263*5895Syz147064  * Get configuration information of a specific aggregation.
264*5895Syz147064  * Caller must free attrp->la_ports.
2653871Syz147064  */
2663871Syz147064 static dladm_status_t
267*5895Syz147064 i_dladm_aggr_info_persist(datalink_id_t linkid, dladm_aggr_grp_attr_t *attrp)
2683871Syz147064 {
269*5895Syz147064 	dladm_conf_t	conf;
270*5895Syz147064 	uint32_t	nports, i;
271*5895Syz147064 	char		*portstr, *next;
272*5895Syz147064 	dladm_status_t	status;
273*5895Syz147064 	uint64_t	u64;
274*5895Syz147064 	int		size;
275*5895Syz147064 	char		macstr[ETHERADDRL * 3];
276*5895Syz147064 
277*5895Syz147064 	attrp->lg_linkid = linkid;
278*5895Syz147064 	if ((status = dladm_read_conf(linkid, &conf)) != DLADM_STATUS_OK)
279*5895Syz147064 		return (status);
280*5895Syz147064 
281*5895Syz147064 	status = dladm_get_conf_field(conf, FKEY, &u64, sizeof (u64));
282*5895Syz147064 	if (status != DLADM_STATUS_OK)
283*5895Syz147064 		goto done;
284*5895Syz147064 	attrp->lg_key = (uint16_t)u64;
285*5895Syz147064 
286*5895Syz147064 	status = dladm_get_conf_field(conf, FPOLICY, &u64, sizeof (u64));
287*5895Syz147064 	if (status != DLADM_STATUS_OK)
288*5895Syz147064 		goto done;
289*5895Syz147064 	attrp->lg_policy = (uint32_t)u64;
290*5895Syz147064 
291*5895Syz147064 	status = dladm_get_conf_field(conf, FFIXMACADDR, &attrp->lg_mac_fixed,
292*5895Syz147064 	    sizeof (boolean_t));
293*5895Syz147064 	if (status != DLADM_STATUS_OK)
294*5895Syz147064 		goto done;
295*5895Syz147064 
296*5895Syz147064 	if (attrp->lg_mac_fixed) {
297*5895Syz147064 		boolean_t fixed;
2983871Syz147064 
299*5895Syz147064 		if ((status = dladm_get_conf_field(conf, FMACADDR, macstr,
300*5895Syz147064 		    sizeof (macstr))) != DLADM_STATUS_OK) {
301*5895Syz147064 			goto done;
302*5895Syz147064 		}
303*5895Syz147064 		if (!dladm_aggr_str2macaddr(macstr, &fixed, attrp->lg_mac)) {
304*5895Syz147064 			status = DLADM_STATUS_REPOSITORYINVAL;
305*5895Syz147064 			goto done;
306*5895Syz147064 		}
307*5895Syz147064 	}
308*5895Syz147064 
309*5895Syz147064 	status = dladm_get_conf_field(conf, FFORCE, &attrp->lg_force,
310*5895Syz147064 	    sizeof (boolean_t));
311*5895Syz147064 	if (status != DLADM_STATUS_OK)
312*5895Syz147064 		goto done;
313*5895Syz147064 
314*5895Syz147064 	status = dladm_get_conf_field(conf, FLACPMODE, &u64, sizeof (u64));
315*5895Syz147064 	if (status != DLADM_STATUS_OK)
316*5895Syz147064 		goto done;
317*5895Syz147064 	attrp->lg_lacp_mode = (aggr_lacp_mode_t)u64;
318*5895Syz147064 
319*5895Syz147064 	status = dladm_get_conf_field(conf, FLACPTIMER, &u64, sizeof (u64));
320*5895Syz147064 	if (status != DLADM_STATUS_OK)
321*5895Syz147064 		goto done;
322*5895Syz147064 	attrp->lg_lacp_timer = (aggr_lacp_timer_t)u64;
323*5895Syz147064 
324*5895Syz147064 	status = dladm_get_conf_field(conf, FNPORTS, &u64, sizeof (u64));
325*5895Syz147064 	if (status != DLADM_STATUS_OK)
326*5895Syz147064 		goto done;
327*5895Syz147064 	nports = (uint32_t)u64;
328*5895Syz147064 	attrp->lg_nports = nports;
329*5895Syz147064 
330*5895Syz147064 	size = nports * (LINKID_STR_WIDTH + 1) + 1;
331*5895Syz147064 	if ((portstr = calloc(1, size)) == NULL) {
332*5895Syz147064 		status = DLADM_STATUS_NOMEM;
333*5895Syz147064 		goto done;
334*5895Syz147064 	}
335*5895Syz147064 
336*5895Syz147064 	status = dladm_get_conf_field(conf, FPORTS, portstr, size);
337*5895Syz147064 	if (status != DLADM_STATUS_OK) {
338*5895Syz147064 		free(portstr);
339*5895Syz147064 		goto done;
340*5895Syz147064 	}
341*5895Syz147064 
342*5895Syz147064 	if ((attrp->lg_ports = malloc(nports *
343*5895Syz147064 	    sizeof (dladm_aggr_port_attr_t))) == NULL) {
344*5895Syz147064 		free(portstr);
3453871Syz147064 		status = DLADM_STATUS_NOMEM;
3463871Syz147064 		goto done;
3473871Syz147064 	}
3483871Syz147064 
349*5895Syz147064 	for (next = portstr, i = 0; i < nports; i++) {
350*5895Syz147064 		READ_PORT(next, attrp->lg_ports[i].lp_linkid, status);
351*5895Syz147064 		if (status != DLADM_STATUS_OK) {
352*5895Syz147064 			free(portstr);
353*5895Syz147064 			free(attrp->lg_ports);
354*5895Syz147064 			goto done;
355*5895Syz147064 		}
356*5895Syz147064 	}
357*5895Syz147064 	free(portstr);
358*5895Syz147064 
359*5895Syz147064 done:
360*5895Syz147064 	dladm_destroy_conf(conf);
361*5895Syz147064 	return (status);
362*5895Syz147064 }
363*5895Syz147064 
364*5895Syz147064 dladm_status_t
365*5895Syz147064 dladm_aggr_info(datalink_id_t linkid, dladm_aggr_grp_attr_t *attrp,
366*5895Syz147064     uint32_t flags)
367*5895Syz147064 {
368*5895Syz147064 	assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST);
369*5895Syz147064 	if (flags == DLADM_OPT_ACTIVE)
370*5895Syz147064 		return (i_dladm_aggr_info_active(linkid, attrp));
371*5895Syz147064 	else
372*5895Syz147064 		return (i_dladm_aggr_info_persist(linkid, attrp));
373*5895Syz147064 }
3743871Syz147064 
375*5895Syz147064 /*
376*5895Syz147064  * Add or remove one or more ports to/from an existing link aggregation.
377*5895Syz147064  */
378*5895Syz147064 static dladm_status_t
379*5895Syz147064 i_dladm_aggr_add_rmv(datalink_id_t linkid, uint32_t nports,
380*5895Syz147064     dladm_aggr_port_attr_db_t *ports, uint32_t flags, int cmd)
381*5895Syz147064 {
382*5895Syz147064 	char *orig_portstr = NULL, *portstr = NULL;
383*5895Syz147064 	laioc_add_rem_t *iocp;
384*5895Syz147064 	laioc_port_t *ioc_ports;
385*5895Syz147064 	uint32_t orig_nports, result_nports, len, i, j;
386*5895Syz147064 	dladm_conf_t conf;
387*5895Syz147064 	datalink_class_t class;
388*5895Syz147064 	dladm_status_t status = DLADM_STATUS_OK;
389*5895Syz147064 	int size;
390*5895Syz147064 	uint64_t u64;
391*5895Syz147064 	uint32_t media;
392*5895Syz147064 
393*5895Syz147064 	if (nports == 0)
394*5895Syz147064 		return (DLADM_STATUS_BADARG);
395*5895Syz147064 
396*5895Syz147064 	/*
397*5895Syz147064 	 * Sanity check - aggregations can only be created over Ethernet
398*5895Syz147064 	 * physical links.
399*5895Syz147064 	 */
400*5895Syz147064 	for (i = 0; i < nports; i++) {
401*5895Syz147064 		if ((dladm_datalink_id2info(ports[i].lp_linkid, NULL,
402*5895Syz147064 		    &class, &media, NULL, 0) != DLADM_STATUS_OK) ||
403*5895Syz147064 		    (class != DATALINK_CLASS_PHYS) || (media != DL_ETHER)) {
404*5895Syz147064 			return (DLADM_STATUS_BADARG);
4053871Syz147064 		}
4063871Syz147064 	}
4073871Syz147064 
408*5895Syz147064 	/*
409*5895Syz147064 	 * First, update the persistent configuration if requested.  We only
410*5895Syz147064 	 * need to update the FPORTS and FNPORTS fields of this aggregation.
411*5895Syz147064 	 * Note that FPORTS is a list of port linkids separated by
412*5895Syz147064 	 * PORT_DELIMITER ('.').
413*5895Syz147064 	 */
414*5895Syz147064 	if (flags & DLADM_OPT_PERSIST) {
415*5895Syz147064 		status = dladm_read_conf(linkid, &conf);
416*5895Syz147064 		if (status != DLADM_STATUS_OK)
417*5895Syz147064 			return (status);
418*5895Syz147064 
419*5895Syz147064 		/*
420*5895Syz147064 		 * Get the original configuration of FNPORTS and FPORTS.
421*5895Syz147064 		 */
422*5895Syz147064 		status = dladm_get_conf_field(conf, FNPORTS, &u64,
423*5895Syz147064 		    sizeof (u64));
424*5895Syz147064 		if (status != DLADM_STATUS_OK)
425*5895Syz147064 			goto destroyconf;
426*5895Syz147064 		orig_nports = (uint32_t)u64;
427*5895Syz147064 
428*5895Syz147064 		/*
429*5895Syz147064 		 * At least one port needs to be in the aggregation.
430*5895Syz147064 		 */
431*5895Syz147064 		if ((cmd == LAIOC_REMOVE) && (orig_nports <= nports)) {
432*5895Syz147064 			status = DLADM_STATUS_BADARG;
433*5895Syz147064 			goto destroyconf;
434*5895Syz147064 		}
435*5895Syz147064 
436*5895Syz147064 		size = orig_nports * (LINKID_STR_WIDTH + 1) + 1;
437*5895Syz147064 		if ((orig_portstr = calloc(1, size)) == NULL) {
438*5895Syz147064 			status = dladm_errno2status(errno);
439*5895Syz147064 			goto destroyconf;
440*5895Syz147064 		}
441*5895Syz147064 
442*5895Syz147064 		status = dladm_get_conf_field(conf, FPORTS, orig_portstr, size);
443*5895Syz147064 		if (status != DLADM_STATUS_OK)
444*5895Syz147064 			goto destroyconf;
445*5895Syz147064 
446*5895Syz147064 		result_nports = (cmd == LAIOC_ADD) ? orig_nports + nports :
447*5895Syz147064 		    orig_nports;
448*5895Syz147064 
449*5895Syz147064 		size = result_nports * (LINKID_STR_WIDTH + 1) + 1;
450*5895Syz147064 		if ((portstr = calloc(1, size)) == NULL) {
451*5895Syz147064 			status = dladm_errno2status(errno);
452*5895Syz147064 			goto destroyconf;
453*5895Syz147064 		}
454*5895Syz147064 
455*5895Syz147064 		/*
456*5895Syz147064 		 * get the new configuration and set to result_nports and
457*5895Syz147064 		 * portstr.
458*5895Syz147064 		 */
459*5895Syz147064 		if (cmd == LAIOC_ADD) {
460*5895Syz147064 			(void) strlcpy(portstr, orig_portstr, size);
461*5895Syz147064 			for (i = 0; i < nports; i++)
462*5895Syz147064 				WRITE_PORT(portstr, ports[i].lp_linkid, size);
463*5895Syz147064 		} else {
464*5895Syz147064 			char *next;
465*5895Syz147064 			datalink_id_t portid;
466*5895Syz147064 			uint32_t remove = 0;
467*5895Syz147064 
468*5895Syz147064 			for (next = orig_portstr, j = 0; j < orig_nports; j++) {
469*5895Syz147064 				/*
470*5895Syz147064 				 * Read the portids from the old configuration
471*5895Syz147064 				 * one by one.
472*5895Syz147064 				 */
473*5895Syz147064 				READ_PORT(next, portid, status);
474*5895Syz147064 				if (status != DLADM_STATUS_OK) {
475*5895Syz147064 					free(portstr);
476*5895Syz147064 					goto destroyconf;
477*5895Syz147064 				}
478*5895Syz147064 
479*5895Syz147064 				/*
480*5895Syz147064 				 * See whether this port is in the removal
481*5895Syz147064 				 * list.  If not, copy to the new config.
482*5895Syz147064 				 */
483*5895Syz147064 				for (i = 0; i < nports; i++) {
484*5895Syz147064 					if (ports[i].lp_linkid == portid)
485*5895Syz147064 						break;
486*5895Syz147064 				}
487*5895Syz147064 				if (i == nports) {
488*5895Syz147064 					WRITE_PORT(portstr, portid, size);
489*5895Syz147064 				} else {
490*5895Syz147064 					remove++;
491*5895Syz147064 				}
492*5895Syz147064 			}
493*5895Syz147064 			if (remove != nports) {
494*5895Syz147064 				status = DLADM_STATUS_LINKINVAL;
495*5895Syz147064 				free(portstr);
496*5895Syz147064 				goto destroyconf;
497*5895Syz147064 			}
498*5895Syz147064 			result_nports -= nports;
499*5895Syz147064 		}
500*5895Syz147064 
501*5895Syz147064 		u64 = result_nports;
502*5895Syz147064 		if ((status = dladm_set_conf_field(conf, FNPORTS,
503*5895Syz147064 		    DLADM_TYPE_UINT64, &u64)) != DLADM_STATUS_OK) {
504*5895Syz147064 			free(portstr);
505*5895Syz147064 			goto destroyconf;
506*5895Syz147064 		}
507*5895Syz147064 
508*5895Syz147064 		status = dladm_set_conf_field(conf, FPORTS, DLADM_TYPE_STR,
509*5895Syz147064 		    portstr);
510*5895Syz147064 		free(portstr);
511*5895Syz147064 		if (status != DLADM_STATUS_OK)
512*5895Syz147064 			goto destroyconf;
513*5895Syz147064 
514*5895Syz147064 		/*
515*5895Syz147064 		 * Write the new configuration to the persistent repository.
516*5895Syz147064 		 */
517*5895Syz147064 		status = dladm_write_conf(conf);
518*5895Syz147064 
519*5895Syz147064 destroyconf:
520*5895Syz147064 		dladm_destroy_conf(conf);
521*5895Syz147064 		if (status != DLADM_STATUS_OK) {
522*5895Syz147064 			free(orig_portstr);
523*5895Syz147064 			return (status);
524*5895Syz147064 		}
525*5895Syz147064 	}
526*5895Syz147064 
527*5895Syz147064 	/*
528*5895Syz147064 	 * If the caller only requested to update the persistent
529*5895Syz147064 	 * configuration, we are done.
530*5895Syz147064 	 */
531*5895Syz147064 	if (!(flags & DLADM_OPT_ACTIVE))
532*5895Syz147064 		goto done;
533*5895Syz147064 
534*5895Syz147064 	/*
535*5895Syz147064 	 * Update the active configuration.
536*5895Syz147064 	 */
537*5895Syz147064 	len = sizeof (*iocp) + nports * sizeof (laioc_port_t);
538*5895Syz147064 	if ((iocp = malloc(len)) == NULL) {
539*5895Syz147064 		status = DLADM_STATUS_NOMEM;
5403871Syz147064 		goto done;
5413871Syz147064 	}
5423871Syz147064 
543*5895Syz147064 	iocp->la_linkid = linkid;
544*5895Syz147064 	iocp->la_nports = nports;
545*5895Syz147064 	if (cmd == LAIOC_ADD)
546*5895Syz147064 		iocp->la_force = (flags & DLADM_OPT_FORCE);
5473871Syz147064 
548*5895Syz147064 	ioc_ports = (laioc_port_t *)(iocp + 1);
549*5895Syz147064 	for (i = 0; i < nports; i++)
550*5895Syz147064 		ioc_ports[i].lp_linkid = ports[i].lp_linkid;
551*5895Syz147064 
552*5895Syz147064 	if (i_dladm_aggr_strioctl(cmd, iocp, len) < 0)
553*5895Syz147064 		status = dladm_errno2status(errno);
5543871Syz147064 
5553871Syz147064 done:
5563871Syz147064 	free(iocp);
557*5895Syz147064 
558*5895Syz147064 	/*
559*5895Syz147064 	 * If the active configuration update fails, restore the old
560*5895Syz147064 	 * persistent configuration if we've changed that.
561*5895Syz147064 	 */
562*5895Syz147064 	if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) {
563*5895Syz147064 		if (dladm_read_conf(linkid, &conf) == DLADM_STATUS_OK) {
564*5895Syz147064 			u64 = orig_nports;
565*5895Syz147064 			if ((dladm_set_conf_field(conf, FNPORTS,
566*5895Syz147064 			    DLADM_TYPE_UINT64, &u64) == DLADM_STATUS_OK) &&
567*5895Syz147064 			    (dladm_set_conf_field(conf, FPORTS, DLADM_TYPE_STR,
568*5895Syz147064 			    orig_portstr) == DLADM_STATUS_OK)) {
569*5895Syz147064 				(void) dladm_write_conf(conf);
570*5895Syz147064 			}
571*5895Syz147064 			(void) dladm_destroy_conf(conf);
572*5895Syz147064 		}
573*5895Syz147064 	}
574*5895Syz147064 	free(orig_portstr);
5753871Syz147064 	return (status);
5763871Syz147064 }
5773871Syz147064 
5783871Syz147064 /*
5793871Syz147064  * Send a modify command to the link aggregation driver.
5803871Syz147064  */
5813871Syz147064 static dladm_status_t
582*5895Syz147064 i_dladm_aggr_modify_sys(datalink_id_t linkid, uint32_t mask,
5833871Syz147064     dladm_aggr_modify_attr_t *attr)
5843871Syz147064 {
5853871Syz147064 	laioc_modify_t ioc;
5863871Syz147064 
587*5895Syz147064 	ioc.lu_linkid = linkid;
5883871Syz147064 
5893871Syz147064 	ioc.lu_modify_mask = 0;
5903871Syz147064 	if (mask & DLADM_AGGR_MODIFY_POLICY)
5913871Syz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_POLICY;
5923871Syz147064 	if (mask & DLADM_AGGR_MODIFY_MAC)
5933871Syz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_MAC;
5943871Syz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE)
5953871Syz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_MODE;
5963871Syz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER)
5973871Syz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_TIMER;
5983871Syz147064 
5993871Syz147064 	ioc.lu_policy = attr->ld_policy;
6003871Syz147064 	ioc.lu_mac_fixed = attr->ld_mac_fixed;
6013871Syz147064 	bcopy(attr->ld_mac, ioc.lu_mac, ETHERADDRL);
6023871Syz147064 	ioc.lu_lacp_mode = attr->ld_lacp_mode;
6033871Syz147064 	ioc.lu_lacp_timer = attr->ld_lacp_timer;
6043871Syz147064 
605*5895Syz147064 	if (i_dladm_aggr_strioctl(LAIOC_MODIFY, &ioc, sizeof (ioc)) < 0) {
6063871Syz147064 		if (errno == EINVAL)
607*5895Syz147064 			return (DLADM_STATUS_MACADDRINVAL);
6083871Syz147064 		else
609*5895Syz147064 			return (dladm_errno2status(errno));
610*5895Syz147064 	} else {
611*5895Syz147064 		return (DLADM_STATUS_OK);
6123871Syz147064 	}
6133871Syz147064 }
6143871Syz147064 
6153871Syz147064 /*
6163871Syz147064  * Send a create command to the link aggregation driver.
6173871Syz147064  */
6183871Syz147064 static dladm_status_t
619*5895Syz147064 i_dladm_aggr_create_sys(datalink_id_t linkid, uint16_t key, uint32_t nports,
620*5895Syz147064     dladm_aggr_port_attr_db_t *ports, uint32_t policy,
621*5895Syz147064     boolean_t mac_addr_fixed, const uchar_t *mac_addr,
622*5895Syz147064     aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, boolean_t force)
6233871Syz147064 {
6243871Syz147064 	int i, rc, len;
625*5895Syz147064 	laioc_create_t *iocp = NULL;
626*5895Syz147064 	laioc_port_t *ioc_ports;
6273871Syz147064 	dladm_status_t status = DLADM_STATUS_OK;
6283871Syz147064 
629*5895Syz147064 	len = sizeof (*iocp) + nports * sizeof (laioc_port_t);
6303871Syz147064 	iocp = malloc(len);
6313871Syz147064 	if (iocp == NULL)
6323871Syz147064 		return (DLADM_STATUS_NOMEM);
6333871Syz147064 
634*5895Syz147064 	iocp->lc_key = key;
635*5895Syz147064 	iocp->lc_linkid = linkid;
636*5895Syz147064 	iocp->lc_nports = nports;
637*5895Syz147064 	iocp->lc_policy = policy;
638*5895Syz147064 	iocp->lc_lacp_mode = lacp_mode;
639*5895Syz147064 	iocp->lc_lacp_timer = lacp_timer;
640*5895Syz147064 	ioc_ports = (laioc_port_t *)(iocp + 1);
641*5895Syz147064 	iocp->lc_force = force;
6423871Syz147064 
643*5895Syz147064 	for (i = 0; i < nports; i++)
644*5895Syz147064 		ioc_ports[i].lp_linkid = ports[i].lp_linkid;
645*5895Syz147064 
646*5895Syz147064 	if (mac_addr_fixed && !VALID_PORT_MAC(mac_addr)) {
647*5895Syz147064 		status = DLADM_STATUS_MACADDRINVAL;
648*5895Syz147064 		goto done;
6493871Syz147064 	}
6503871Syz147064 
651*5895Syz147064 	bcopy(mac_addr, iocp->lc_mac, ETHERADDRL);
652*5895Syz147064 	iocp->lc_mac_fixed = mac_addr_fixed;
6533871Syz147064 
654*5895Syz147064 	rc = i_dladm_aggr_strioctl(LAIOC_CREATE, iocp, len);
655*5895Syz147064 	if (rc < 0)
656*5895Syz147064 		status = dladm_errno2status(errno);
6573871Syz147064 
658*5895Syz147064 done:
6593871Syz147064 	free(iocp);
6603871Syz147064 	return (status);
6613871Syz147064 }
6623871Syz147064 
6633871Syz147064 /*
6643871Syz147064  * Invoked to bring up a link aggregation group.
6653871Syz147064  */
666*5895Syz147064 static int
667*5895Syz147064 i_dladm_aggr_up(datalink_id_t linkid, void *arg)
6683871Syz147064 {
669*5895Syz147064 	dladm_status_t *statusp = (dladm_status_t *)arg;
670*5895Syz147064 	dladm_aggr_grp_attr_t attr;
671*5895Syz147064 	dladm_aggr_port_attr_db_t *ports = NULL;
672*5895Syz147064 	uint16_t key = 0;
673*5895Syz147064 	int i, j;
6743871Syz147064 	dladm_status_t status;
6753871Syz147064 
676*5895Syz147064 	status = dladm_aggr_info(linkid, &attr, DLADM_OPT_PERSIST);
677*5895Syz147064 	if (status != DLADM_STATUS_OK) {
678*5895Syz147064 		*statusp = status;
679*5895Syz147064 		return (DLADM_WALK_CONTINUE);
680*5895Syz147064 	}
6813871Syz147064 
682*5895Syz147064 	if (attr.lg_key <= AGGR_MAX_KEY)
683*5895Syz147064 		key = attr.lg_key;
684*5895Syz147064 
685*5895Syz147064 	ports = malloc(attr.lg_nports * sizeof (dladm_aggr_port_attr_db_t));
686*5895Syz147064 	if (ports == NULL) {
687*5895Syz147064 		status = DLADM_STATUS_NOMEM;
688*5895Syz147064 		goto done;
6893871Syz147064 	}
6903871Syz147064 
6913871Syz147064 	/*
692*5895Syz147064 	 * Validate (and purge) each physical link associated with this
693*5895Syz147064 	 * aggregation, if the specific hardware has been removed during
694*5895Syz147064 	 * the system shutdown.
6953871Syz147064 	 */
696*5895Syz147064 	for (i = 0, j = 0; i < attr.lg_nports; i++) {
697*5895Syz147064 		datalink_id_t	portid = attr.lg_ports[i].lp_linkid;
698*5895Syz147064 		uint32_t	flags;
699*5895Syz147064 		dladm_status_t	s;
7003871Syz147064 
701*5895Syz147064 		s = dladm_datalink_id2info(portid, &flags, NULL, NULL, NULL, 0);
702*5895Syz147064 		if (s != DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE))
703*5895Syz147064 			continue;
704*5895Syz147064 
705*5895Syz147064 		ports[j++].lp_linkid = portid;
706*5895Syz147064 	}
7073871Syz147064 
708*5895Syz147064 	if (j == 0) {
709*5895Syz147064 		/*
710*5895Syz147064 		 * All of the physical links are removed.
711*5895Syz147064 		 */
712*5895Syz147064 		status = DLADM_STATUS_BADARG;
713*5895Syz147064 		goto done;
7143871Syz147064 	}
7153871Syz147064 
716*5895Syz147064 	/*
717*5895Syz147064 	 * Create active aggregation.
718*5895Syz147064 	 */
719*5895Syz147064 	if ((status = i_dladm_aggr_create_sys(linkid,
720*5895Syz147064 	    key, j, ports, attr.lg_policy, attr.lg_mac_fixed,
721*5895Syz147064 	    (const uchar_t *)attr.lg_mac, attr.lg_lacp_mode,
722*5895Syz147064 	    attr.lg_lacp_timer, attr.lg_force)) != DLADM_STATUS_OK) {
723*5895Syz147064 		goto done;
724*5895Syz147064 	}
7253871Syz147064 
726*5895Syz147064 	if ((status = dladm_up_datalink_id(linkid)) != DLADM_STATUS_OK) {
727*5895Syz147064 		laioc_delete_t ioc;
728*5895Syz147064 		ioc.ld_linkid = linkid;
729*5895Syz147064 		(void) i_dladm_aggr_strioctl(LAIOC_DELETE, &ioc, sizeof (ioc));
730*5895Syz147064 		goto done;
731*5895Syz147064 	}
7323871Syz147064 
7333871Syz147064 	/*
734*5895Syz147064 	 * Reset the active linkprop of this specific link.
7353871Syz147064 	 */
736*5895Syz147064 	(void) dladm_init_linkprop(linkid);
7373871Syz147064 
738*5895Syz147064 done:
739*5895Syz147064 	free(attr.lg_ports);
740*5895Syz147064 	free(ports);
741*5895Syz147064 
742*5895Syz147064 	*statusp = status;
743*5895Syz147064 	return (DLADM_WALK_CONTINUE);
7443871Syz147064 }
7453871Syz147064 
7463871Syz147064 /*
747*5895Syz147064  * Bring up one aggregation, or all persistent aggregations.  In the latter
748*5895Syz147064  * case, the walk may terminate early if bringup of an aggregation fails.
7493871Syz147064  */
750*5895Syz147064 dladm_status_t
751*5895Syz147064 dladm_aggr_up(datalink_id_t linkid)
7523871Syz147064 {
7533871Syz147064 	dladm_status_t status;
7543871Syz147064 
755*5895Syz147064 	if (linkid == DATALINK_ALL_LINKID) {
756*5895Syz147064 		(void) dladm_walk_datalink_id(i_dladm_aggr_up, &status,
757*5895Syz147064 		    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
758*5895Syz147064 		    DLADM_OPT_PERSIST);
759*5895Syz147064 		return (DLADM_STATUS_OK);
760*5895Syz147064 	} else {
761*5895Syz147064 		(void) i_dladm_aggr_up(linkid, &status);
7623871Syz147064 		return (status);
7633871Syz147064 	}
7643871Syz147064 }
7653871Syz147064 
7663871Syz147064 /*
7673871Syz147064  * Given a policy string, return a policy mask. Returns B_TRUE on
768*5895Syz147064  * success, or B_FALSE if an error occurred during parsing.
7693871Syz147064  */
7703871Syz147064 boolean_t
7713871Syz147064 dladm_aggr_str2policy(const char *str, uint32_t *policy)
7723871Syz147064 {
7733871Syz147064 	int i;
7743871Syz147064 	policy_t *pol;
7753871Syz147064 	char *token = NULL;
7763871Syz147064 	char *lasts;
7773871Syz147064 
7783871Syz147064 	*policy = 0;
7793871Syz147064 
7803871Syz147064 	while ((token = strtok_r((token == NULL) ? (char *)str : NULL, ",",
7813871Syz147064 	    &lasts)) != NULL) {
7823871Syz147064 		for (i = 0; i < NPOLICIES; i++) {
7833871Syz147064 			pol = &policies[i];
7843871Syz147064 			if (strcasecmp(token, pol->pol_name) == 0) {
7853871Syz147064 				*policy |= pol->policy;
7863871Syz147064 				break;
7873871Syz147064 			}
7883871Syz147064 		}
7893871Syz147064 		if (i == NPOLICIES)
7903871Syz147064 			return (B_FALSE);
7913871Syz147064 	}
7923871Syz147064 
7933871Syz147064 	return (B_TRUE);
7943871Syz147064 }
7953871Syz147064 
7963871Syz147064 /*
7973871Syz147064  * Given a policy mask, returns a printable string, or NULL if the
7983871Syz147064  * policy mask is invalid. It is the responsibility of the caller to
7993871Syz147064  * free the returned string after use.
8003871Syz147064  */
8013871Syz147064 char *
8023871Syz147064 dladm_aggr_policy2str(uint32_t policy, char *str)
8033871Syz147064 {
8043871Syz147064 	int i, npolicies = 0;
8053871Syz147064 	policy_t *pol;
8063871Syz147064 
807*5895Syz147064 	if (str == NULL)
808*5895Syz147064 		return (NULL);
809*5895Syz147064 
8103871Syz147064 	str[0] = '\0';
8113871Syz147064 
8123871Syz147064 	for (i = 0; i < NPOLICIES; i++) {
8133871Syz147064 		pol = &policies[i];
8143871Syz147064 		if ((policy & pol->policy) != 0) {
8153871Syz147064 			npolicies++;
8163871Syz147064 			if (npolicies > 1)
817*5895Syz147064 				(void) strlcat(str, ",", DLADM_STRSIZE);
818*5895Syz147064 			(void) strlcat(str, pol->pol_name, DLADM_STRSIZE);
8193871Syz147064 		}
8203871Syz147064 	}
8213871Syz147064 
8223871Syz147064 	return (str);
8233871Syz147064 }
8243871Syz147064 
8253871Syz147064 /*
8263871Syz147064  * Given a MAC address string, return the MAC address in the mac_addr
8273871Syz147064  * array. If the MAC address was not explicitly specified, i.e. is
8283871Syz147064  * equal to 'auto', zero out mac-addr and set mac_fixed to B_TRUE.
8293871Syz147064  * Return B_FALSE if a syntax error was encountered, B_FALSE otherwise.
8303871Syz147064  */
8313871Syz147064 boolean_t
8323871Syz147064 dladm_aggr_str2macaddr(const char *str, boolean_t *mac_fixed, uchar_t *mac_addr)
8333871Syz147064 {
8343871Syz147064 	uchar_t *conv_str;
8353871Syz147064 	int mac_len;
8363871Syz147064 
8373871Syz147064 	*mac_fixed = (strcmp(str, "auto") != 0);
8383871Syz147064 	if (!*mac_fixed) {
8393871Syz147064 		bzero(mac_addr, ETHERADDRL);
8403871Syz147064 		return (B_TRUE);
8413871Syz147064 	}
8423871Syz147064 
8433871Syz147064 	conv_str = _link_aton(str, &mac_len);
8443871Syz147064 	if (conv_str == NULL)
8453871Syz147064 		return (B_FALSE);
8463871Syz147064 
8473871Syz147064 	if (mac_len != ETHERADDRL) {
8483871Syz147064 		free(conv_str);
8493871Syz147064 		return (B_FALSE);
8503871Syz147064 	}
8513871Syz147064 
8523871Syz147064 	if ((bcmp(zero_mac, conv_str, ETHERADDRL) == 0) ||
8533871Syz147064 	    (conv_str[0] & 0x01)) {
8543871Syz147064 		free(conv_str);
8553871Syz147064 		return (B_FALSE);
8563871Syz147064 	}
8573871Syz147064 
8583871Syz147064 	bcopy(conv_str, mac_addr, ETHERADDRL);
8593871Syz147064 	free(conv_str);
8603871Syz147064 
8613871Syz147064 	return (B_TRUE);
8623871Syz147064 }
8633871Syz147064 
8643871Syz147064 /*
8653871Syz147064  * Returns a string containing a printable representation of a MAC address.
8663871Syz147064  */
8673871Syz147064 const char *
868*5895Syz147064 dladm_aggr_macaddr2str(const unsigned char *mac, char *buf)
8693871Syz147064 {
8703871Syz147064 	static char unknown_mac[] = {0, 0, 0, 0, 0, 0};
8713871Syz147064 
8723871Syz147064 	if (buf == NULL)
8733871Syz147064 		return (NULL);
8743871Syz147064 
8753871Syz147064 	if (bcmp(unknown_mac, mac, ETHERADDRL) == 0)
876*5895Syz147064 		(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
8773871Syz147064 	else
8783871Syz147064 		return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER));
879*5895Syz147064 
880*5895Syz147064 	return (buf);
8813871Syz147064 }
8823871Syz147064 
8833871Syz147064 /*
8843871Syz147064  * Given a LACP mode string, find the corresponding LACP mode number. Returns
8853871Syz147064  * B_TRUE if a match was found, B_FALSE otherwise.
8863871Syz147064  */
8873871Syz147064 boolean_t
8883871Syz147064 dladm_aggr_str2lacpmode(const char *str, aggr_lacp_mode_t *lacp_mode)
8893871Syz147064 {
8903871Syz147064 	int i;
8913871Syz147064 	dladm_aggr_lacpmode_t *mode;
8923871Syz147064 
8933871Syz147064 	for (i = 0; i < NLACP_MODES; i++) {
8943871Syz147064 		mode = &lacp_modes[i];
8953871Syz147064 		if (strncasecmp(str, mode->mode_str,
8963871Syz147064 		    strlen(mode->mode_str)) == 0) {
8973871Syz147064 			*lacp_mode = mode->mode_id;
8983871Syz147064 			return (B_TRUE);
8993871Syz147064 		}
9003871Syz147064 	}
9013871Syz147064 
9023871Syz147064 	return (B_FALSE);
9033871Syz147064 }
9043871Syz147064 
9053871Syz147064 /*
9063871Syz147064  * Given a LACP mode number, returns a printable string, or NULL if the
9073871Syz147064  * LACP mode number is invalid.
9083871Syz147064  */
9093871Syz147064 const char *
9103871Syz147064 dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id, char *buf)
9113871Syz147064 {
9123871Syz147064 	int i;
9133871Syz147064 	dladm_aggr_lacpmode_t *mode;
9143871Syz147064 
915*5895Syz147064 	if (buf == NULL)
916*5895Syz147064 		return (NULL);
917*5895Syz147064 
9183871Syz147064 	for (i = 0; i < NLACP_MODES; i++) {
9193871Syz147064 		mode = &lacp_modes[i];
9203871Syz147064 		if (mode->mode_id == mode_id) {
9213871Syz147064 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
9223871Syz147064 			    mode->mode_str);
9233871Syz147064 			return (buf);
9243871Syz147064 		}
9253871Syz147064 	}
9263871Syz147064 
9273871Syz147064 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
9283871Syz147064 	return (buf);
9293871Syz147064 }
9303871Syz147064 
9313871Syz147064 /*
9323871Syz147064  * Given a LACP timer string, find the corresponding LACP timer number. Returns
9333871Syz147064  * B_TRUE if a match was found, B_FALSE otherwise.
9343871Syz147064  */
9353871Syz147064 boolean_t
9363871Syz147064 dladm_aggr_str2lacptimer(const char *str, aggr_lacp_timer_t *lacp_timer)
9373871Syz147064 {
9383871Syz147064 	int i;
9393871Syz147064 	dladm_aggr_lacptimer_t *timer;
9403871Syz147064 
9413871Syz147064 	for (i = 0; i < NLACP_TIMERS; i++) {
9423871Syz147064 		timer = &lacp_timers[i];
9433871Syz147064 		if (strncasecmp(str, timer->lt_str,
9443871Syz147064 		    strlen(timer->lt_str)) == 0) {
9453871Syz147064 			*lacp_timer = timer->lt_id;
9463871Syz147064 			return (B_TRUE);
9473871Syz147064 		}
9483871Syz147064 	}
9493871Syz147064 
9503871Syz147064 	return (B_FALSE);
9513871Syz147064 }
9523871Syz147064 
9533871Syz147064 /*
9543871Syz147064  * Given a LACP timer, returns a printable string, or NULL if the
9553871Syz147064  * LACP timer number is invalid.
9563871Syz147064  */
9573871Syz147064 const char *
9583871Syz147064 dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id, char *buf)
9593871Syz147064 {
9603871Syz147064 	int i;
9613871Syz147064 	dladm_aggr_lacptimer_t *timer;
9623871Syz147064 
963*5895Syz147064 	if (buf == NULL)
964*5895Syz147064 		return (NULL);
965*5895Syz147064 
9663871Syz147064 	for (i = 0; i < NLACP_TIMERS; i++) {
9673871Syz147064 		timer = &lacp_timers[i];
9683871Syz147064 		if (timer->lt_id == timer_id) {
9693871Syz147064 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
9703871Syz147064 			    timer->lt_str);
9713871Syz147064 			return (buf);
9723871Syz147064 		}
9733871Syz147064 	}
9743871Syz147064 
9753871Syz147064 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
9763871Syz147064 	return (buf);
9773871Syz147064 }
9783871Syz147064 
9793871Syz147064 const char *
9803871Syz147064 dladm_aggr_portstate2str(aggr_port_state_t state_id, char *buf)
9813871Syz147064 {
9823871Syz147064 	int			i;
983*5895Syz147064 	dladm_aggr_port_state_t *state;
984*5895Syz147064 
985*5895Syz147064 	if (buf == NULL)
986*5895Syz147064 		return (NULL);
9873871Syz147064 
9883871Syz147064 	for (i = 0; i < NPORT_STATES; i++) {
9893871Syz147064 		state = &port_states[i];
9903871Syz147064 		if (state->state_id == state_id) {
9913871Syz147064 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
9923871Syz147064 			    state->state_str);
9933871Syz147064 			return (buf);
9943871Syz147064 		}
9953871Syz147064 	}
9963871Syz147064 
9973871Syz147064 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
9983871Syz147064 	return (buf);
9993871Syz147064 }
10003871Syz147064 
1001*5895Syz147064 static dladm_status_t
1002*5895Syz147064 dladm_aggr_persist_aggr_conf(const char *link, datalink_id_t linkid,
1003*5895Syz147064     uint16_t key, uint32_t nports, dladm_aggr_port_attr_db_t *ports,
1004*5895Syz147064     uint32_t policy, boolean_t mac_addr_fixed, const uchar_t *mac_addr,
1005*5895Syz147064     aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer,
1006*5895Syz147064     boolean_t force)
10073871Syz147064 {
1008*5895Syz147064 	dladm_conf_t conf = DLADM_INVALID_CONF;
1009*5895Syz147064 	char *portstr = NULL;
1010*5895Syz147064 	char macstr[ETHERADDRL * 3];
1011*5895Syz147064 	dladm_status_t status;
1012*5895Syz147064 	int i, size;
1013*5895Syz147064 	uint64_t u64;
10143871Syz147064 
1015*5895Syz147064 	if ((status = dladm_create_conf(link, linkid, DATALINK_CLASS_AGGR,
1016*5895Syz147064 	    DL_ETHER, &conf)) != DLADM_STATUS_OK) {
10173871Syz147064 		return (status);
10183871Syz147064 	}
10193871Syz147064 
1020*5895Syz147064 	u64 = key;
1021*5895Syz147064 	status = dladm_set_conf_field(conf, FKEY, DLADM_TYPE_UINT64, &u64);
1022*5895Syz147064 	if (status != DLADM_STATUS_OK)
1023*5895Syz147064 		goto done;
10243871Syz147064 
1025*5895Syz147064 	u64 = nports;
1026*5895Syz147064 	status = dladm_set_conf_field(conf, FNPORTS, DLADM_TYPE_UINT64, &u64);
1027*5895Syz147064 	if (status != DLADM_STATUS_OK)
1028*5895Syz147064 		goto done;
1029*5895Syz147064 
1030*5895Syz147064 	size = nports * (LINKID_STR_WIDTH + 1) + 1;
1031*5895Syz147064 	if ((portstr = calloc(1, size)) == NULL) {
1032*5895Syz147064 		status = DLADM_STATUS_NOMEM;
1033*5895Syz147064 		goto done;
1034*5895Syz147064 	}
10353871Syz147064 
1036*5895Syz147064 	for (i = 0; i < nports; i++)
1037*5895Syz147064 		WRITE_PORT(portstr, ports[i].lp_linkid, size);
1038*5895Syz147064 	status = dladm_set_conf_field(conf, FPORTS, DLADM_TYPE_STR, portstr);
1039*5895Syz147064 	free(portstr);
1040*5895Syz147064 
1041*5895Syz147064 	if (status != DLADM_STATUS_OK)
1042*5895Syz147064 		goto done;
10433871Syz147064 
1044*5895Syz147064 	u64 = policy;
1045*5895Syz147064 	status = dladm_set_conf_field(conf, FPOLICY, DLADM_TYPE_UINT64, &u64);
1046*5895Syz147064 	if (status != DLADM_STATUS_OK)
1047*5895Syz147064 		goto done;
1048*5895Syz147064 
1049*5895Syz147064 	status = dladm_set_conf_field(conf, FFIXMACADDR, DLADM_TYPE_BOOLEAN,
1050*5895Syz147064 	    &mac_addr_fixed);
1051*5895Syz147064 	if (status != DLADM_STATUS_OK)
1052*5895Syz147064 		goto done;
1053*5895Syz147064 
1054*5895Syz147064 	if (mac_addr_fixed) {
1055*5895Syz147064 		if (!VALID_PORT_MAC(mac_addr)) {
1056*5895Syz147064 			status = DLADM_STATUS_MACADDRINVAL;
10573871Syz147064 			goto done;
10583871Syz147064 		}
10593871Syz147064 
1060*5895Syz147064 		(void) dladm_aggr_macaddr2str(mac_addr, macstr);
1061*5895Syz147064 		status = dladm_set_conf_field(conf, FMACADDR, DLADM_TYPE_STR,
1062*5895Syz147064 		    macstr);
1063*5895Syz147064 		if (status != DLADM_STATUS_OK)
10643871Syz147064 			goto done;
10653871Syz147064 	}
10663871Syz147064 
1067*5895Syz147064 	status = dladm_set_conf_field(conf, FFORCE, DLADM_TYPE_BOOLEAN, &force);
1068*5895Syz147064 	if (status != DLADM_STATUS_OK)
1069*5895Syz147064 		goto done;
1070*5895Syz147064 
1071*5895Syz147064 	u64 = lacp_mode;
1072*5895Syz147064 	status = dladm_set_conf_field(conf, FLACPMODE, DLADM_TYPE_UINT64, &u64);
1073*5895Syz147064 	if (status != DLADM_STATUS_OK)
1074*5895Syz147064 		goto done;
1075*5895Syz147064 
1076*5895Syz147064 	u64 = lacp_timer;
1077*5895Syz147064 	status = dladm_set_conf_field(conf, FLACPTIMER, DLADM_TYPE_UINT64,
1078*5895Syz147064 	    &u64);
1079*5895Syz147064 	if (status != DLADM_STATUS_OK)
1080*5895Syz147064 		goto done;
1081*5895Syz147064 
10823871Syz147064 	/*
1083*5895Syz147064 	 * Commit the link aggregation configuration.
10843871Syz147064 	 */
1085*5895Syz147064 	status = dladm_write_conf(conf);
10863871Syz147064 
10873871Syz147064 done:
1088*5895Syz147064 	dladm_destroy_conf(conf);
10893871Syz147064 	return (status);
10903871Syz147064 }
10913871Syz147064 
10923871Syz147064 /*
10933871Syz147064  * Create a new link aggregation group. Update the configuration
10943871Syz147064  * file and bring it up.
10953871Syz147064  */
10963871Syz147064 dladm_status_t
1097*5895Syz147064 dladm_aggr_create(const char *name, uint16_t key, uint32_t nports,
10983871Syz147064     dladm_aggr_port_attr_db_t *ports, uint32_t policy, boolean_t mac_addr_fixed,
1099*5895Syz147064     const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode,
1100*5895Syz147064     aggr_lacp_timer_t lacp_timer, uint32_t flags)
11013871Syz147064 {
1102*5895Syz147064 	datalink_id_t linkid = DATALINK_INVALID_LINKID;
1103*5895Syz147064 	uint32_t media;
1104*5895Syz147064 	uint32_t i;
1105*5895Syz147064 	datalink_class_t class;
11063871Syz147064 	dladm_status_t status;
1107*5895Syz147064 	boolean_t force = (flags & DLADM_OPT_FORCE) ? B_TRUE : B_FALSE;
11083871Syz147064 
1109*5895Syz147064 	if (key != 0 && key > AGGR_MAX_KEY)
11103871Syz147064 		return (DLADM_STATUS_KEYINVAL);
11113871Syz147064 
1112*5895Syz147064 	if (nports == 0)
1113*5895Syz147064 		return (DLADM_STATUS_BADARG);
1114*5895Syz147064 
1115*5895Syz147064 	for (i = 0; i < nports; i++) {
1116*5895Syz147064 		if ((dladm_datalink_id2info(ports[i].lp_linkid, NULL,
1117*5895Syz147064 		    &class, &media, NULL, 0) != DLADM_STATUS_OK) ||
1118*5895Syz147064 		    (class != DATALINK_CLASS_PHYS) && (media != DL_ETHER)) {
1119*5895Syz147064 			return (DLADM_STATUS_BADARG);
1120*5895Syz147064 		}
1121*5895Syz147064 	}
1122*5895Syz147064 
1123*5895Syz147064 	flags &= (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
1124*5895Syz147064 	if ((status = dladm_create_datalink_id(name, DATALINK_CLASS_AGGR,
1125*5895Syz147064 	    DL_ETHER, flags, &linkid)) != DLADM_STATUS_OK) {
1126*5895Syz147064 		goto fail;
1127*5895Syz147064 	}
1128*5895Syz147064 
1129*5895Syz147064 	if ((flags & DLADM_OPT_PERSIST) &&
1130*5895Syz147064 	    (status = dladm_aggr_persist_aggr_conf(name, linkid, key, nports,
1131*5895Syz147064 	    ports, policy, mac_addr_fixed, mac_addr, lacp_mode, lacp_timer,
1132*5895Syz147064 	    force)) != DLADM_STATUS_OK) {
1133*5895Syz147064 		goto fail;
1134*5895Syz147064 	}
1135*5895Syz147064 
1136*5895Syz147064 	if (!(flags & DLADM_OPT_ACTIVE))
1137*5895Syz147064 		return (DLADM_STATUS_OK);
1138*5895Syz147064 
1139*5895Syz147064 	status = i_dladm_aggr_create_sys(linkid, key, nports, ports, policy,
1140*5895Syz147064 	    mac_addr_fixed, mac_addr, lacp_mode, lacp_timer, force);
11413871Syz147064 
1142*5895Syz147064 	if (status != DLADM_STATUS_OK) {
1143*5895Syz147064 		if (flags & DLADM_OPT_PERSIST)
1144*5895Syz147064 			(void) dladm_remove_conf(linkid);
1145*5895Syz147064 		goto fail;
1146*5895Syz147064 	}
1147*5895Syz147064 
1148*5895Syz147064 	return (DLADM_STATUS_OK);
1149*5895Syz147064 
1150*5895Syz147064 fail:
1151*5895Syz147064 	if (linkid != DATALINK_INVALID_LINKID)
1152*5895Syz147064 		(void) dladm_destroy_datalink_id(linkid, flags);
1153*5895Syz147064 
1154*5895Syz147064 	return (status);
1155*5895Syz147064 }
1156*5895Syz147064 
1157*5895Syz147064 static dladm_status_t
1158*5895Syz147064 i_dladm_aggr_get_aggr_attr(dladm_conf_t conf, uint32_t mask,
1159*5895Syz147064     dladm_aggr_modify_attr_t *attrp)
1160*5895Syz147064 {
1161*5895Syz147064 	dladm_status_t status = DLADM_STATUS_OK;
1162*5895Syz147064 	char macstr[ETHERADDRL * 3];
1163*5895Syz147064 	uint64_t u64;
1164*5895Syz147064 
1165*5895Syz147064 	if (mask & DLADM_AGGR_MODIFY_POLICY) {
1166*5895Syz147064 		status = dladm_get_conf_field(conf, FPOLICY, &u64,
1167*5895Syz147064 		    sizeof (u64));
1168*5895Syz147064 		if (status != DLADM_STATUS_OK)
1169*5895Syz147064 			return (status);
1170*5895Syz147064 		attrp->ld_policy = (uint32_t)u64;
1171*5895Syz147064 	}
1172*5895Syz147064 
1173*5895Syz147064 	if (mask & DLADM_AGGR_MODIFY_MAC) {
1174*5895Syz147064 		status = dladm_get_conf_field(conf, FFIXMACADDR,
1175*5895Syz147064 		    &attrp->ld_mac_fixed, sizeof (boolean_t));
11763871Syz147064 		if (status != DLADM_STATUS_OK)
11773871Syz147064 			return (status);
1178*5895Syz147064 
1179*5895Syz147064 		if (attrp->ld_mac_fixed) {
1180*5895Syz147064 			boolean_t fixed;
1181*5895Syz147064 
1182*5895Syz147064 			status = dladm_get_conf_field(conf, FMACADDR,
1183*5895Syz147064 			    macstr, sizeof (macstr));
1184*5895Syz147064 			if (status != DLADM_STATUS_OK)
1185*5895Syz147064 				return (status);
11863871Syz147064 
1187*5895Syz147064 			if (!dladm_aggr_str2macaddr(macstr, &fixed,
1188*5895Syz147064 			    attrp->ld_mac)) {
1189*5895Syz147064 				return (DLADM_STATUS_REPOSITORYINVAL);
1190*5895Syz147064 			}
1191*5895Syz147064 		}
1192*5895Syz147064 	}
11933871Syz147064 
1194*5895Syz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE) {
1195*5895Syz147064 		status = dladm_get_conf_field(conf, FLACPMODE, &u64,
1196*5895Syz147064 		    sizeof (u64));
1197*5895Syz147064 		if (status != DLADM_STATUS_OK)
1198*5895Syz147064 			return (status);
1199*5895Syz147064 		attrp->ld_lacp_mode = (aggr_lacp_mode_t)u64;
1200*5895Syz147064 	}
1201*5895Syz147064 
1202*5895Syz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) {
1203*5895Syz147064 		status = dladm_get_conf_field(conf, FLACPTIMER, &u64,
1204*5895Syz147064 		    sizeof (u64));
1205*5895Syz147064 		if (status != DLADM_STATUS_OK)
1206*5895Syz147064 			return (status);
1207*5895Syz147064 		attrp->ld_lacp_timer = (aggr_lacp_timer_t)u64;
12083871Syz147064 	}
12093871Syz147064 
1210*5895Syz147064 	return (status);
1211*5895Syz147064 }
1212*5895Syz147064 
1213*5895Syz147064 static dladm_status_t
1214*5895Syz147064 i_dladm_aggr_set_aggr_attr(dladm_conf_t conf, uint32_t mask,
1215*5895Syz147064     dladm_aggr_modify_attr_t *attrp)
1216*5895Syz147064 {
1217*5895Syz147064 	dladm_status_t status = DLADM_STATUS_OK;
1218*5895Syz147064 	char macstr[ETHERADDRL * 3];
1219*5895Syz147064 	uint64_t u64;
1220*5895Syz147064 
1221*5895Syz147064 	if (mask & DLADM_AGGR_MODIFY_POLICY) {
1222*5895Syz147064 		u64 = attrp->ld_policy;
1223*5895Syz147064 		status = dladm_set_conf_field(conf, FPOLICY, DLADM_TYPE_UINT64,
1224*5895Syz147064 		    &u64);
1225*5895Syz147064 		if (status != DLADM_STATUS_OK)
1226*5895Syz147064 			return (status);
1227*5895Syz147064 	}
1228*5895Syz147064 
1229*5895Syz147064 	if (mask & DLADM_AGGR_MODIFY_MAC) {
1230*5895Syz147064 		status = dladm_set_conf_field(conf, FFIXMACADDR,
1231*5895Syz147064 		    DLADM_TYPE_BOOLEAN, &attrp->ld_mac_fixed);
1232*5895Syz147064 		if (status != DLADM_STATUS_OK)
1233*5895Syz147064 			return (status);
12343871Syz147064 
1235*5895Syz147064 		if (attrp->ld_mac_fixed) {
1236*5895Syz147064 			(void) dladm_aggr_macaddr2str(attrp->ld_mac, macstr);
1237*5895Syz147064 			status = dladm_set_conf_field(conf, FMACADDR,
1238*5895Syz147064 			    DLADM_TYPE_STR, macstr);
1239*5895Syz147064 			if (status != DLADM_STATUS_OK)
1240*5895Syz147064 				return (status);
1241*5895Syz147064 		}
1242*5895Syz147064 	}
1243*5895Syz147064 
1244*5895Syz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE) {
1245*5895Syz147064 		u64 = attrp->ld_lacp_mode;
1246*5895Syz147064 		status = dladm_set_conf_field(conf, FLACPMODE,
1247*5895Syz147064 		    DLADM_TYPE_UINT64, &u64);
1248*5895Syz147064 		if (status != DLADM_STATUS_OK)
1249*5895Syz147064 			return (status);
1250*5895Syz147064 	}
1251*5895Syz147064 
1252*5895Syz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) {
1253*5895Syz147064 		u64 = attrp->ld_lacp_timer;
1254*5895Syz147064 		status = dladm_set_conf_field(conf, FLACPTIMER,
1255*5895Syz147064 		    DLADM_TYPE_UINT64, &u64);
1256*5895Syz147064 		if (status != DLADM_STATUS_OK)
1257*5895Syz147064 			return (status);
1258*5895Syz147064 	}
12593871Syz147064 
12603871Syz147064 	return (status);
12613871Syz147064 }
12623871Syz147064 
12633871Syz147064 /*
12643871Syz147064  * Modify the parameters of an existing link aggregation group. Update
12653871Syz147064  * the configuration file and pass the changes to the kernel.
12663871Syz147064  */
12673871Syz147064 dladm_status_t
1268*5895Syz147064 dladm_aggr_modify(datalink_id_t linkid, uint32_t modify_mask, uint32_t policy,
1269*5895Syz147064     boolean_t mac_fixed, const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode,
1270*5895Syz147064     aggr_lacp_timer_t lacp_timer, uint32_t flags)
12713871Syz147064 {
12723871Syz147064 	dladm_aggr_modify_attr_t new_attr, old_attr;
1273*5895Syz147064 	dladm_conf_t conf;
12743871Syz147064 	dladm_status_t status;
12753871Syz147064 
1276*5895Syz147064 	new_attr.ld_policy = policy;
1277*5895Syz147064 	new_attr.ld_mac_fixed = mac_fixed;
1278*5895Syz147064 	new_attr.ld_lacp_mode = lacp_mode;
1279*5895Syz147064 	new_attr.ld_lacp_timer = lacp_timer;
1280*5895Syz147064 	bcopy(mac_addr, new_attr.ld_mac, ETHERADDRL);
1281*5895Syz147064 
1282*5895Syz147064 	if (flags & DLADM_OPT_PERSIST) {
1283*5895Syz147064 		status = dladm_read_conf(linkid, &conf);
1284*5895Syz147064 		if (status != DLADM_STATUS_OK)
1285*5895Syz147064 			return (status);
12863871Syz147064 
1287*5895Syz147064 		if ((status = i_dladm_aggr_get_aggr_attr(conf, modify_mask,
1288*5895Syz147064 		    &old_attr)) != DLADM_STATUS_OK) {
1289*5895Syz147064 			goto done;
1290*5895Syz147064 		}
12913871Syz147064 
1292*5895Syz147064 		if ((status = i_dladm_aggr_set_aggr_attr(conf, modify_mask,
1293*5895Syz147064 		    &new_attr)) != DLADM_STATUS_OK) {
1294*5895Syz147064 			goto done;
1295*5895Syz147064 		}
1296*5895Syz147064 
1297*5895Syz147064 		status = dladm_write_conf(conf);
1298*5895Syz147064 
1299*5895Syz147064 done:
1300*5895Syz147064 		dladm_destroy_conf(conf);
1301*5895Syz147064 		if (status != DLADM_STATUS_OK)
1302*5895Syz147064 			return (status);
13033871Syz147064 	}
13043871Syz147064 
1305*5895Syz147064 	if (!(flags & DLADM_OPT_ACTIVE))
1306*5895Syz147064 		return (DLADM_STATUS_OK);
13073871Syz147064 
1308*5895Syz147064 	status = i_dladm_aggr_modify_sys(linkid, modify_mask, &new_attr);
1309*5895Syz147064 	if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) {
1310*5895Syz147064 		if (dladm_read_conf(linkid, &conf) == DLADM_STATUS_OK) {
1311*5895Syz147064 			if (i_dladm_aggr_set_aggr_attr(conf, modify_mask,
1312*5895Syz147064 			    &old_attr) == DLADM_STATUS_OK) {
1313*5895Syz147064 				(void) dladm_write_conf(conf);
1314*5895Syz147064 			}
1315*5895Syz147064 			dladm_destroy_conf(conf);
1316*5895Syz147064 		}
13173871Syz147064 	}
13183871Syz147064 
13193871Syz147064 	return (status);
13203871Syz147064 }
13213871Syz147064 
1322*5895Syz147064 typedef struct aggr_held_arg_s {
1323*5895Syz147064 	datalink_id_t	aggrid;
1324*5895Syz147064 	boolean_t	isheld;
1325*5895Syz147064 } aggr_held_arg_t;
1326*5895Syz147064 
1327*5895Syz147064 static int
1328*5895Syz147064 i_dladm_aggr_is_held(datalink_id_t linkid, void *arg)
1329*5895Syz147064 {
1330*5895Syz147064 	aggr_held_arg_t		*aggr_held_arg = arg;
1331*5895Syz147064 	dladm_vlan_attr_t	dva;
1332*5895Syz147064 
1333*5895Syz147064 	if (dladm_vlan_info(linkid, &dva, DLADM_OPT_PERSIST) != DLADM_STATUS_OK)
1334*5895Syz147064 		return (DLADM_WALK_CONTINUE);
1335*5895Syz147064 
1336*5895Syz147064 	if (dva.dv_linkid == aggr_held_arg->aggrid) {
1337*5895Syz147064 		/*
1338*5895Syz147064 		 * This VLAN is created over the given aggregation.
1339*5895Syz147064 		 */
1340*5895Syz147064 		aggr_held_arg->isheld = B_TRUE;
1341*5895Syz147064 		return (DLADM_WALK_TERMINATE);
1342*5895Syz147064 	}
1343*5895Syz147064 	return (DLADM_WALK_CONTINUE);
1344*5895Syz147064 }
1345*5895Syz147064 
13463871Syz147064 /*
1347*5895Syz147064  * Delete a previously created link aggregation group. Either the name "aggr"
1348*5895Syz147064  * or the "key" is specified.
13493871Syz147064  */
13503871Syz147064 dladm_status_t
1351*5895Syz147064 dladm_aggr_delete(datalink_id_t linkid, uint32_t flags)
13523871Syz147064 {
1353*5895Syz147064 	laioc_delete_t ioc;
1354*5895Syz147064 	datalink_class_t class;
13553871Syz147064 	dladm_status_t status;
13563871Syz147064 
1357*5895Syz147064 	if ((dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0) !=
1358*5895Syz147064 	    DLADM_STATUS_OK) || (class != DATALINK_CLASS_AGGR)) {
1359*5895Syz147064 		return (DLADM_STATUS_BADARG);
1360*5895Syz147064 	}
13613871Syz147064 
1362*5895Syz147064 	if (flags & DLADM_OPT_ACTIVE) {
1363*5895Syz147064 		ioc.ld_linkid = linkid;
1364*5895Syz147064 		if ((i_dladm_aggr_strioctl(LAIOC_DELETE, &ioc,
1365*5895Syz147064 		    sizeof (ioc)) < 0) &&
1366*5895Syz147064 		    ((errno != ENOENT) || !(flags & DLADM_OPT_PERSIST))) {
1367*5895Syz147064 			status = dladm_errno2status(errno);
1368*5895Syz147064 			return (status);
1369*5895Syz147064 		}
13703871Syz147064 
13713871Syz147064 		/*
1372*5895Syz147064 		 * Delete ACTIVE linkprop first.
13733871Syz147064 		 */
1374*5895Syz147064 		(void) dladm_set_linkprop(linkid, NULL, NULL, 0,
1375*5895Syz147064 		    DLADM_OPT_ACTIVE);
1376*5895Syz147064 		(void) dladm_destroy_datalink_id(linkid, DLADM_OPT_ACTIVE);
13773871Syz147064 	}
13783871Syz147064 
1379*5895Syz147064 	/*
1380*5895Syz147064 	 * If we reach here, it means that the active aggregation has already
1381*5895Syz147064 	 * been deleted, and there is no active VLANs holding this aggregation.
1382*5895Syz147064 	 * Now we see whether there is any persistent VLANs holding this
1383*5895Syz147064 	 * aggregation. If so, we fail the operation.
1384*5895Syz147064 	 */
1385*5895Syz147064 	if (flags & DLADM_OPT_PERSIST) {
1386*5895Syz147064 		aggr_held_arg_t arg;
1387*5895Syz147064 
1388*5895Syz147064 		arg.aggrid = linkid;
1389*5895Syz147064 		arg.isheld = B_FALSE;
13903871Syz147064 
1391*5895Syz147064 		(void) dladm_walk_datalink_id(i_dladm_aggr_is_held,
1392*5895Syz147064 		    &arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
1393*5895Syz147064 		    DLADM_OPT_PERSIST);
1394*5895Syz147064 		if (arg.isheld)
1395*5895Syz147064 			return (DLADM_STATUS_LINKBUSY);
1396*5895Syz147064 
1397*5895Syz147064 		(void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST);
1398*5895Syz147064 		(void) dladm_remove_conf(linkid);
1399*5895Syz147064 	}
1400*5895Syz147064 
1401*5895Syz147064 	return (DLADM_STATUS_OK);
14023871Syz147064 }
14033871Syz147064 
14043871Syz147064 /*
14053871Syz147064  * Add one or more ports to an existing link aggregation.
14063871Syz147064  */
14073871Syz147064 dladm_status_t
1408*5895Syz147064 dladm_aggr_add(datalink_id_t linkid, uint32_t nports,
1409*5895Syz147064     dladm_aggr_port_attr_db_t *ports, uint32_t flags)
14103871Syz147064 {
1411*5895Syz147064 	return (i_dladm_aggr_add_rmv(linkid, nports, ports, flags, LAIOC_ADD));
14123871Syz147064 }
14133871Syz147064 
14143871Syz147064 /*
14153871Syz147064  * Remove one or more ports from an existing link aggregation.
14163871Syz147064  */
14173871Syz147064 dladm_status_t
1418*5895Syz147064 dladm_aggr_remove(datalink_id_t linkid, uint32_t nports,
1419*5895Syz147064     dladm_aggr_port_attr_db_t *ports, uint32_t flags)
14203871Syz147064 {
1421*5895Syz147064 	return (i_dladm_aggr_add_rmv(linkid, nports, ports, flags,
1422*5895Syz147064 	    LAIOC_REMOVE));
1423*5895Syz147064 }
14243871Syz147064 
1425*5895Syz147064 typedef struct i_walk_key_state_s {
1426*5895Syz147064 	uint16_t key;
1427*5895Syz147064 	datalink_id_t linkid;
1428*5895Syz147064 	boolean_t found;
1429*5895Syz147064 } i_walk_key_state_t;
14303871Syz147064 
1431*5895Syz147064 static int
1432*5895Syz147064 i_dladm_walk_key2linkid(datalink_id_t linkid, void *arg)
1433*5895Syz147064 {
1434*5895Syz147064 	dladm_conf_t conf;
1435*5895Syz147064 	uint16_t key;
1436*5895Syz147064 	dladm_status_t status;
1437*5895Syz147064 	i_walk_key_state_t *statep = (i_walk_key_state_t *)arg;
1438*5895Syz147064 	uint64_t u64;
14393871Syz147064 
1440*5895Syz147064 	if (dladm_read_conf(linkid, &conf) != 0)
1441*5895Syz147064 		return (DLADM_WALK_CONTINUE);
1442*5895Syz147064 
1443*5895Syz147064 	status = dladm_get_conf_field(conf, FKEY, &u64, sizeof (u64));
1444*5895Syz147064 	key = (uint16_t)u64;
1445*5895Syz147064 	dladm_destroy_conf(conf);
1446*5895Syz147064 
1447*5895Syz147064 	if ((status == DLADM_STATUS_OK) && (key == statep->key)) {
1448*5895Syz147064 		statep->found = B_TRUE;
1449*5895Syz147064 		statep->linkid = linkid;
1450*5895Syz147064 		return (DLADM_WALK_TERMINATE);
14513871Syz147064 	}
14523871Syz147064 
1453*5895Syz147064 	return (DLADM_WALK_CONTINUE);
1454*5895Syz147064 }
1455*5895Syz147064 
1456*5895Syz147064 dladm_status_t
1457*5895Syz147064 dladm_key2linkid(uint16_t key, datalink_id_t *linkidp, uint32_t flags)
1458*5895Syz147064 {
1459*5895Syz147064 	i_walk_key_state_t state;
1460*5895Syz147064 
1461*5895Syz147064 	if (key > AGGR_MAX_KEY)
1462*5895Syz147064 		return (DLADM_STATUS_NOTFOUND);
14633871Syz147064 
1464*5895Syz147064 	state.found = B_FALSE;
1465*5895Syz147064 	state.key = key;
1466*5895Syz147064 
1467*5895Syz147064 	(void) dladm_walk_datalink_id(i_dladm_walk_key2linkid, &state,
1468*5895Syz147064 	    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags);
1469*5895Syz147064 	if (state.found == B_TRUE) {
1470*5895Syz147064 		*linkidp = state.linkid;
1471*5895Syz147064 		return (DLADM_STATUS_OK);
1472*5895Syz147064 	} else {
1473*5895Syz147064 		return (DLADM_STATUS_NOTFOUND);
1474*5895Syz147064 	}
14753871Syz147064 }
1476