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