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