xref: /illumos-gate/usr/src/lib/libdladm/common/libdlaggr.c (revision fb2a9bae0030340ad72b9c26ba1ffee2ee3cafec)
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 2010 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