13871Syz147064 /*
23871Syz147064  * CDDL HEADER START
33871Syz147064  *
43871Syz147064  * The contents of this file are subject to the terms of the
53871Syz147064  * Common Development and Distribution License (the "License").
63871Syz147064  * You may not use this file except in compliance with the License.
73871Syz147064  *
83871Syz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93871Syz147064  * or http://www.opensolaris.org/os/licensing.
103871Syz147064  * See the License for the specific language governing permissions
113871Syz147064  * and limitations under the License.
123871Syz147064  *
133871Syz147064  * When distributing Covered Code, include this CDDL HEADER in each
143871Syz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153871Syz147064  * If applicable, add the following below this CDDL HEADER, with the
163871Syz147064  * fields enclosed by brackets "[]" replaced with your own identifying
173871Syz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
183871Syz147064  *
193871Syz147064  * CDDL HEADER END
203871Syz147064  */
213871Syz147064 /*
229815SRishi.Srivatsavai@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
233871Syz147064  * Use is subject to license terms.
243871Syz147064  */
253871Syz147064 
263871Syz147064 #include <stdio.h>
273871Syz147064 #include <sys/types.h>
283871Syz147064 #include <sys/stat.h>
293871Syz147064 #include <string.h>
303871Syz147064 #include <fcntl.h>
313871Syz147064 #include <unistd.h>
323871Syz147064 #include <stropts.h>
333871Syz147064 #include <stdlib.h>
343871Syz147064 #include <errno.h>
355895Syz147064 #include <assert.h>
363871Syz147064 #include <strings.h>
373871Syz147064 #include <libintl.h>
383871Syz147064 #include <net/if_types.h>
393871Syz147064 #include <net/if_dl.h>
408275SEric Cheng #include <sys/dld.h>
415895Syz147064 #include <libdllink.h>
425895Syz147064 #include <libdlvlan.h>
433871Syz147064 #include <libdlaggr.h>
443871Syz147064 #include <libdladm_impl.h>
453871Syz147064 
463871Syz147064 /*
473871Syz147064  * Link Aggregation Administration Library.
483871Syz147064  *
493871Syz147064  * This library is used by administration tools such as dladm(1M) to
503871Syz147064  * configure link aggregations.
513871Syz147064  */
523871Syz147064 
533871Syz147064 /* Limits on buffer size for LAIOC_INFO request */
543871Syz147064 #define	MIN_INFO_SIZE (4*1024)
553871Syz147064 #define	MAX_INFO_SIZE (128*1024)
563871Syz147064 
575895Syz147064 static uchar_t	zero_mac[] = {0, 0, 0, 0, 0, 0};
585895Syz147064 #define	VALID_PORT_MAC(mac)						\
595895Syz147064 	(((mac) != NULL) && (bcmp(zero_mac, (mac), ETHERADDRL) != 0) &&	\
605895Syz147064 	(!(mac)[0] & 0x01))
613871Syz147064 
62*10616SSebastien.Roy@Sun.COM #define	PORT_DELIMITER	":"
633871Syz147064 
643871Syz147064 typedef struct dladm_aggr_modify_attr {
653871Syz147064 	uint32_t	ld_policy;
663871Syz147064 	boolean_t	ld_mac_fixed;
673871Syz147064 	uchar_t		ld_mac[ETHERADDRL];
683871Syz147064 	aggr_lacp_mode_t ld_lacp_mode;
693871Syz147064 	aggr_lacp_timer_t ld_lacp_timer;
703871Syz147064 } dladm_aggr_modify_attr_t;
713871Syz147064 
723871Syz147064 typedef struct policy_s {
733871Syz147064 	char		*pol_name;
743871Syz147064 	uint32_t	policy;
753871Syz147064 } policy_t;
763871Syz147064 
773871Syz147064 static policy_t policies[] = {
783871Syz147064 	{"L2",		AGGR_POLICY_L2},
793871Syz147064 	{"L3",		AGGR_POLICY_L3},
803871Syz147064 	{"L4",		AGGR_POLICY_L4}};
813871Syz147064 
823871Syz147064 #define	NPOLICIES	(sizeof (policies) / sizeof (policy_t))
833871Syz147064 
843871Syz147064 typedef struct dladm_aggr_lacpmode_s {
853871Syz147064 	char		*mode_str;
863871Syz147064 	aggr_lacp_mode_t mode_id;
873871Syz147064 } dladm_aggr_lacpmode_t;
883871Syz147064 
893871Syz147064 static dladm_aggr_lacpmode_t lacp_modes[] = {
903871Syz147064 	{"off", AGGR_LACP_OFF},
913871Syz147064 	{"active", AGGR_LACP_ACTIVE},
923871Syz147064 	{"passive", AGGR_LACP_PASSIVE}};
933871Syz147064 
943871Syz147064 #define	NLACP_MODES	(sizeof (lacp_modes) / sizeof (dladm_aggr_lacpmode_t))
953871Syz147064 
963871Syz147064 typedef struct dladm_aggr_lacptimer_s {
973871Syz147064 	char		*lt_str;
983871Syz147064 	aggr_lacp_timer_t lt_id;
993871Syz147064 } dladm_aggr_lacptimer_t;
1003871Syz147064 
1013871Syz147064 static dladm_aggr_lacptimer_t lacp_timers[] = {
1023871Syz147064 	{"short", AGGR_LACP_TIMER_SHORT},
1033871Syz147064 	{"long", AGGR_LACP_TIMER_LONG}};
1043871Syz147064 
1053871Syz147064 #define	NLACP_TIMERS	(sizeof (lacp_timers) / sizeof (dladm_aggr_lacptimer_t))
1063871Syz147064 
1073871Syz147064 typedef struct dladm_aggr_port_state {
1083871Syz147064 	char			*state_str;
1093871Syz147064 	aggr_port_state_t	state_id;
1103871Syz147064 } dladm_aggr_port_state_t;
1113871Syz147064 
1123871Syz147064 static dladm_aggr_port_state_t port_states[] = {
1133871Syz147064 	{"standby", AGGR_PORT_STATE_STANDBY },
1143871Syz147064 	{"attached", AGGR_PORT_STATE_ATTACHED }
1153871Syz147064 };
1163871Syz147064 
1173871Syz147064 #define	NPORT_STATES	\
1183871Syz147064 	(sizeof (port_states) / sizeof (dladm_aggr_port_state_t))
1193871Syz147064 
120*10616SSebastien.Roy@Sun.COM static dladm_status_t
121*10616SSebastien.Roy@Sun.COM write_port(dladm_handle_t handle, char *portstr, datalink_id_t portid,
122*10616SSebastien.Roy@Sun.COM     size_t portstrsize)
123*10616SSebastien.Roy@Sun.COM {
124*10616SSebastien.Roy@Sun.COM 	char		pname[MAXLINKNAMELEN + 1];
125*10616SSebastien.Roy@Sun.COM 	dladm_status_t	status;
126*10616SSebastien.Roy@Sun.COM 
127*10616SSebastien.Roy@Sun.COM 	if ((status = dladm_datalink_id2info(handle, portid, NULL, NULL, NULL,
128*10616SSebastien.Roy@Sun.COM 	    pname, sizeof (pname))) != DLADM_STATUS_OK)
129*10616SSebastien.Roy@Sun.COM 		return (status);
130*10616SSebastien.Roy@Sun.COM 	(void) strlcat(pname, PORT_DELIMITER, sizeof (pname));
131*10616SSebastien.Roy@Sun.COM 	if (strlcat(portstr, pname, portstrsize) >= portstrsize)
132*10616SSebastien.Roy@Sun.COM 		status = DLADM_STATUS_TOOSMALL;
133*10616SSebastien.Roy@Sun.COM 	return (status);
134*10616SSebastien.Roy@Sun.COM }
135*10616SSebastien.Roy@Sun.COM 
136*10616SSebastien.Roy@Sun.COM static dladm_status_t
137*10616SSebastien.Roy@Sun.COM read_port(dladm_handle_t handle, char **portstr, datalink_id_t *portid)
138*10616SSebastien.Roy@Sun.COM {
139*10616SSebastien.Roy@Sun.COM 	dladm_status_t	status;
140*10616SSebastien.Roy@Sun.COM 	char		*pname;
141*10616SSebastien.Roy@Sun.COM 
142*10616SSebastien.Roy@Sun.COM 	if ((pname = strtok(*portstr, PORT_DELIMITER)) == NULL)
143*10616SSebastien.Roy@Sun.COM 		return (DLADM_STATUS_REPOSITORYINVAL);
144*10616SSebastien.Roy@Sun.COM 	*portstr += (strlen(pname) + 1);
145*10616SSebastien.Roy@Sun.COM 	status = dladm_name2info(handle, pname, portid, NULL, NULL, NULL);
146*10616SSebastien.Roy@Sun.COM 	return (status);
147*10616SSebastien.Roy@Sun.COM }
148*10616SSebastien.Roy@Sun.COM 
1495895Syz147064 static int
1508453SAnurag.Maskey@Sun.COM i_dladm_aggr_ioctl(dladm_handle_t handle, int cmd, void *ptr)
1515895Syz147064 {
1528453SAnurag.Maskey@Sun.COM 	return (ioctl(dladm_dld_fd(handle), cmd, ptr));
1533871Syz147064 }
1543871Syz147064 
1553871Syz147064 /*
1565895Syz147064  * Caller must free attr.lg_ports. The ptr pointer is advanced while convert
1575895Syz147064  * the laioc_info_t to the dladm_aggr_grp_attr_t structure.
1583871Syz147064  */
1595895Syz147064 static int
1605895Syz147064 i_dladm_aggr_iocp2grpattr(void **ptr, dladm_aggr_grp_attr_t *attrp)
1613871Syz147064 {
1625895Syz147064 	laioc_info_group_t	*grp;
1635895Syz147064 	laioc_info_port_t	*port;
1645895Syz147064 	int			i;
1655895Syz147064 	void			*where = (*ptr);
1665895Syz147064 
1675895Syz147064 	grp = (laioc_info_group_t *)where;
1685895Syz147064 
1695895Syz147064 	attrp->lg_linkid = grp->lg_linkid;
1705895Syz147064 	attrp->lg_key = grp->lg_key;
1715895Syz147064 	attrp->lg_nports = grp->lg_nports;
1725895Syz147064 	attrp->lg_policy = grp->lg_policy;
1735895Syz147064 	attrp->lg_lacp_mode = grp->lg_lacp_mode;
1745895Syz147064 	attrp->lg_lacp_timer = grp->lg_lacp_timer;
1755895Syz147064 	attrp->lg_force = grp->lg_force;
1765895Syz147064 
1775895Syz147064 	bcopy(grp->lg_mac, attrp->lg_mac, ETHERADDRL);
1785895Syz147064 	attrp->lg_mac_fixed = grp->lg_mac_fixed;
1793871Syz147064 
1805895Syz147064 	if ((attrp->lg_ports = malloc(grp->lg_nports *
1815895Syz147064 	    sizeof (dladm_aggr_port_attr_t))) == NULL) {
1825895Syz147064 		errno = ENOMEM;
1835895Syz147064 		goto fail;
1845895Syz147064 	}
1855895Syz147064 
1865895Syz147064 	where = (grp + 1);
1873871Syz147064 
1885895Syz147064 	/*
1895895Syz147064 	 * Go through each port that is part of the group.
1905895Syz147064 	 */
1915895Syz147064 	for (i = 0; i < grp->lg_nports; i++) {
1925895Syz147064 		port = (laioc_info_port_t *)where;
1933871Syz147064 
1945895Syz147064 		attrp->lg_ports[i].lp_linkid = port->lp_linkid;
1955895Syz147064 		bcopy(port->lp_mac, attrp->lg_ports[i].lp_mac, ETHERADDRL);
1965895Syz147064 		attrp->lg_ports[i].lp_state = port->lp_state;
1975895Syz147064 		attrp->lg_ports[i].lp_lacp_state = port->lp_lacp_state;
1985895Syz147064 
1995895Syz147064 		where = (port + 1);
2005895Syz147064 	}
2015895Syz147064 	*ptr = where;
2025895Syz147064 	return (0);
2035895Syz147064 fail:
2045895Syz147064 	return (-1);
2053871Syz147064 }
2063871Syz147064 
2073871Syz147064 /*
2085895Syz147064  * Get active configuration of a specific aggregation.
2095895Syz147064  * Caller must free attrp->la_ports.
2103871Syz147064  */
2115895Syz147064 static dladm_status_t
2128453SAnurag.Maskey@Sun.COM i_dladm_aggr_info_active(dladm_handle_t handle, datalink_id_t linkid,
2138453SAnurag.Maskey@Sun.COM     dladm_aggr_grp_attr_t *attrp)
2143871Syz147064 {
2153871Syz147064 	laioc_info_t *ioc;
2167408SSebastien.Roy@Sun.COM 	int bufsize;
2175895Syz147064 	void *where;
2185895Syz147064 	dladm_status_t status = DLADM_STATUS_OK;
2193871Syz147064 
2203871Syz147064 	bufsize = MIN_INFO_SIZE;
2213871Syz147064 	ioc = (laioc_info_t *)calloc(1, bufsize);
2225895Syz147064 	if (ioc == NULL)
2235895Syz147064 		return (DLADM_STATUS_NOMEM);
2245895Syz147064 
2255895Syz147064 	ioc->li_group_linkid = linkid;
2263871Syz147064 
2273871Syz147064 tryagain:
2287408SSebastien.Roy@Sun.COM 	ioc->li_bufsize = bufsize;
2298453SAnurag.Maskey@Sun.COM 	if (i_dladm_aggr_ioctl(handle, LAIOC_INFO, ioc) != 0) {
2303871Syz147064 		if (errno == ENOSPC) {
2313871Syz147064 			/*
2323871Syz147064 			 * The LAIOC_INFO call failed due to a short
2333871Syz147064 			 * buffer. Reallocate the buffer and try again.
2343871Syz147064 			 */
2353871Syz147064 			bufsize *= 2;
2363871Syz147064 			if (bufsize <= MAX_INFO_SIZE) {
2373871Syz147064 				ioc = (laioc_info_t *)realloc(ioc, bufsize);
2383871Syz147064 				if (ioc != NULL) {
2393871Syz147064 					bzero(ioc, sizeof (bufsize));
2403871Syz147064 					goto tryagain;
2413871Syz147064 				}
2423871Syz147064 			}
2433871Syz147064 		}
2445895Syz147064 		status = dladm_errno2status(errno);
2453871Syz147064 		goto bail;
2463871Syz147064 	}
2473871Syz147064 
2483871Syz147064 	/*
2493871Syz147064 	 * Go through each group returned by the aggregation driver.
2503871Syz147064 	 */
2513871Syz147064 	where = (char *)(ioc + 1);
2525895Syz147064 	if (i_dladm_aggr_iocp2grpattr(&where, attrp) != 0) {
2535895Syz147064 		status = dladm_errno2status(errno);
2545895Syz147064 		goto bail;
2553871Syz147064 	}
2563871Syz147064 
2573871Syz147064 bail:
2583871Syz147064 	free(ioc);
2593871Syz147064 	return (status);
2603871Syz147064 }
2613871Syz147064 
2623871Syz147064 /*
2635895Syz147064  * Get configuration information of a specific aggregation.
2645895Syz147064  * Caller must free attrp->la_ports.
2653871Syz147064  */
2663871Syz147064 static dladm_status_t
2678453SAnurag.Maskey@Sun.COM i_dladm_aggr_info_persist(dladm_handle_t handle, datalink_id_t linkid,
2688453SAnurag.Maskey@Sun.COM     dladm_aggr_grp_attr_t *attrp)
2693871Syz147064 {
2705895Syz147064 	dladm_conf_t	conf;
2715895Syz147064 	uint32_t	nports, i;
272*10616SSebastien.Roy@Sun.COM 	char		*portstr = NULL, *next;
2735895Syz147064 	dladm_status_t	status;
2745895Syz147064 	uint64_t	u64;
2755895Syz147064 	int		size;
2765895Syz147064 	char		macstr[ETHERADDRL * 3];
2775895Syz147064 
2785895Syz147064 	attrp->lg_linkid = linkid;
2798453SAnurag.Maskey@Sun.COM 	if ((status = dladm_read_conf(handle, linkid, &conf)) !=
2808453SAnurag.Maskey@Sun.COM 	    DLADM_STATUS_OK)
2815895Syz147064 		return (status);
2825895Syz147064 
2838453SAnurag.Maskey@Sun.COM 	status = dladm_get_conf_field(handle, conf, FKEY, &u64, sizeof (u64));
2845895Syz147064 	if (status != DLADM_STATUS_OK)
2855895Syz147064 		goto done;
2865895Syz147064 	attrp->lg_key = (uint16_t)u64;
2875895Syz147064 
2888453SAnurag.Maskey@Sun.COM 	status = dladm_get_conf_field(handle, conf, FPOLICY, &u64,
2898453SAnurag.Maskey@Sun.COM 	    sizeof (u64));
2905895Syz147064 	if (status != DLADM_STATUS_OK)
2915895Syz147064 		goto done;
2925895Syz147064 	attrp->lg_policy = (uint32_t)u64;
2935895Syz147064 
2948453SAnurag.Maskey@Sun.COM 	status = dladm_get_conf_field(handle, conf, FFIXMACADDR,
2958453SAnurag.Maskey@Sun.COM 	    &attrp->lg_mac_fixed, sizeof (boolean_t));
2965895Syz147064 	if (status != DLADM_STATUS_OK)
2975895Syz147064 		goto done;
2985895Syz147064 
2995895Syz147064 	if (attrp->lg_mac_fixed) {
3005895Syz147064 		boolean_t fixed;
3013871Syz147064 
3028453SAnurag.Maskey@Sun.COM 		if ((status = dladm_get_conf_field(handle, conf, FMACADDR,
3038453SAnurag.Maskey@Sun.COM 		    macstr, sizeof (macstr))) != DLADM_STATUS_OK) {
3045895Syz147064 			goto done;
3055895Syz147064 		}
3065895Syz147064 		if (!dladm_aggr_str2macaddr(macstr, &fixed, attrp->lg_mac)) {
3075895Syz147064 			status = DLADM_STATUS_REPOSITORYINVAL;
3085895Syz147064 			goto done;
3095895Syz147064 		}
3105895Syz147064 	}
3115895Syz147064 
3128453SAnurag.Maskey@Sun.COM 	status = dladm_get_conf_field(handle, conf, FFORCE, &attrp->lg_force,
3135895Syz147064 	    sizeof (boolean_t));
3145895Syz147064 	if (status != DLADM_STATUS_OK)
3155895Syz147064 		goto done;
3165895Syz147064 
3178453SAnurag.Maskey@Sun.COM 	status = dladm_get_conf_field(handle, conf, FLACPMODE, &u64,
3188453SAnurag.Maskey@Sun.COM 	    sizeof (u64));
3195895Syz147064 	if (status != DLADM_STATUS_OK)
3205895Syz147064 		goto done;
3215895Syz147064 	attrp->lg_lacp_mode = (aggr_lacp_mode_t)u64;
3225895Syz147064 
3238453SAnurag.Maskey@Sun.COM 	status = dladm_get_conf_field(handle, conf, FLACPTIMER, &u64,
3248453SAnurag.Maskey@Sun.COM 	    sizeof (u64));
3255895Syz147064 	if (status != DLADM_STATUS_OK)
3265895Syz147064 		goto done;
3275895Syz147064 	attrp->lg_lacp_timer = (aggr_lacp_timer_t)u64;
3285895Syz147064 
3298453SAnurag.Maskey@Sun.COM 	status = dladm_get_conf_field(handle, conf, FNPORTS, &u64,
3308453SAnurag.Maskey@Sun.COM 	    sizeof (u64));
3315895Syz147064 	if (status != DLADM_STATUS_OK)
3325895Syz147064 		goto done;
3335895Syz147064 	nports = (uint32_t)u64;
3345895Syz147064 	attrp->lg_nports = nports;
3355895Syz147064 
336*10616SSebastien.Roy@Sun.COM 	size = nports * (MAXLINKNAMELEN + 1) + 1;
3375895Syz147064 	if ((portstr = calloc(1, size)) == NULL) {
3385895Syz147064 		status = DLADM_STATUS_NOMEM;
3395895Syz147064 		goto done;
3405895Syz147064 	}
3415895Syz147064 
3428453SAnurag.Maskey@Sun.COM 	status = dladm_get_conf_field(handle, conf, FPORTS, portstr, size);
343*10616SSebastien.Roy@Sun.COM 	if (status != DLADM_STATUS_OK)
3445895Syz147064 		goto done;
3455895Syz147064 
3465895Syz147064 	if ((attrp->lg_ports = malloc(nports *
3475895Syz147064 	    sizeof (dladm_aggr_port_attr_t))) == NULL) {
3483871Syz147064 		status = DLADM_STATUS_NOMEM;
3493871Syz147064 		goto done;
3503871Syz147064 	}
3513871Syz147064 
3525895Syz147064 	for (next = portstr, i = 0; i < nports; i++) {
353*10616SSebastien.Roy@Sun.COM 		if ((status = read_port(handle, &next,
354*10616SSebastien.Roy@Sun.COM 		    &attrp->lg_ports[i].lp_linkid)) != DLADM_STATUS_OK)
3555895Syz147064 			free(attrp->lg_ports);
3565895Syz147064 	}
3575895Syz147064 
3585895Syz147064 done:
359*10616SSebastien.Roy@Sun.COM 	free(portstr);
3608453SAnurag.Maskey@Sun.COM 	dladm_destroy_conf(handle, conf);
3615895Syz147064 	return (status);
3625895Syz147064 }
3635895Syz147064 
3645895Syz147064 dladm_status_t
3658453SAnurag.Maskey@Sun.COM dladm_aggr_info(dladm_handle_t handle, datalink_id_t linkid,
3668453SAnurag.Maskey@Sun.COM     dladm_aggr_grp_attr_t *attrp, uint32_t flags)
3675895Syz147064 {
3685895Syz147064 	assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST);
3695895Syz147064 	if (flags == DLADM_OPT_ACTIVE)
3708453SAnurag.Maskey@Sun.COM 		return (i_dladm_aggr_info_active(handle, linkid, attrp));
3715895Syz147064 	else
3728453SAnurag.Maskey@Sun.COM 		return (i_dladm_aggr_info_persist(handle, linkid, attrp));
3735895Syz147064 }
3743871Syz147064 
3755895Syz147064 /*
3765895Syz147064  * Add or remove one or more ports to/from an existing link aggregation.
3775895Syz147064  */
3785895Syz147064 static dladm_status_t
3798453SAnurag.Maskey@Sun.COM i_dladm_aggr_add_rmv(dladm_handle_t handle, datalink_id_t linkid,
3808453SAnurag.Maskey@Sun.COM     uint32_t nports, dladm_aggr_port_attr_db_t *ports, uint32_t flags, int cmd)
3815895Syz147064 {
3825895Syz147064 	char *orig_portstr = NULL, *portstr = NULL;
3836077Syz147064 	laioc_add_rem_t *iocp = NULL;
3845895Syz147064 	laioc_port_t *ioc_ports;
3855895Syz147064 	uint32_t orig_nports, result_nports, len, i, j;
3865895Syz147064 	dladm_conf_t conf;
3875895Syz147064 	datalink_class_t class;
3885895Syz147064 	dladm_status_t status = DLADM_STATUS_OK;
3895895Syz147064 	int size;
3905895Syz147064 	uint64_t u64;
3915895Syz147064 	uint32_t media;
3925895Syz147064 
3935895Syz147064 	if (nports == 0)
3945895Syz147064 		return (DLADM_STATUS_BADARG);
3955895Syz147064 
3965895Syz147064 	/*
3975895Syz147064 	 * Sanity check - aggregations can only be created over Ethernet
3989815SRishi.Srivatsavai@Sun.COM 	 * physical links and simnets.
3995895Syz147064 	 */
4005895Syz147064 	for (i = 0; i < nports; i++) {
4018453SAnurag.Maskey@Sun.COM 		if ((dladm_datalink_id2info(handle, ports[i].lp_linkid, NULL,
4025895Syz147064 		    &class, &media, NULL, 0) != DLADM_STATUS_OK) ||
4039815SRishi.Srivatsavai@Sun.COM 		    !((class == DATALINK_CLASS_PHYS) ||
4049815SRishi.Srivatsavai@Sun.COM 		    (class == DATALINK_CLASS_SIMNET)) || (media != DL_ETHER)) {
4055895Syz147064 			return (DLADM_STATUS_BADARG);
4063871Syz147064 		}
4073871Syz147064 	}
4083871Syz147064 
4095895Syz147064 	/*
4105895Syz147064 	 * First, update the persistent configuration if requested.  We only
4115895Syz147064 	 * need to update the FPORTS and FNPORTS fields of this aggregation.
4125895Syz147064 	 * Note that FPORTS is a list of port linkids separated by
413*10616SSebastien.Roy@Sun.COM 	 * PORT_DELIMITER (':').
4145895Syz147064 	 */
4155895Syz147064 	if (flags & DLADM_OPT_PERSIST) {
4168453SAnurag.Maskey@Sun.COM 		status = dladm_read_conf(handle, linkid, &conf);
4175895Syz147064 		if (status != DLADM_STATUS_OK)
4185895Syz147064 			return (status);
4195895Syz147064 
4205895Syz147064 		/*
4215895Syz147064 		 * Get the original configuration of FNPORTS and FPORTS.
4225895Syz147064 		 */
4238453SAnurag.Maskey@Sun.COM 		status = dladm_get_conf_field(handle, conf, FNPORTS, &u64,
4245895Syz147064 		    sizeof (u64));
4255895Syz147064 		if (status != DLADM_STATUS_OK)
4265895Syz147064 			goto destroyconf;
4275895Syz147064 		orig_nports = (uint32_t)u64;
4285895Syz147064 
4295895Syz147064 		/*
4305895Syz147064 		 * At least one port needs to be in the aggregation.
4315895Syz147064 		 */
4325895Syz147064 		if ((cmd == LAIOC_REMOVE) && (orig_nports <= nports)) {
4335895Syz147064 			status = DLADM_STATUS_BADARG;
4345895Syz147064 			goto destroyconf;
4355895Syz147064 		}
4365895Syz147064 
437*10616SSebastien.Roy@Sun.COM 		size = orig_nports * (MAXLINKNAMELEN + 1) + 1;
4385895Syz147064 		if ((orig_portstr = calloc(1, size)) == NULL) {
4395895Syz147064 			status = dladm_errno2status(errno);
4405895Syz147064 			goto destroyconf;
4415895Syz147064 		}
4425895Syz147064 
4438453SAnurag.Maskey@Sun.COM 		status = dladm_get_conf_field(handle, conf, FPORTS,
4448453SAnurag.Maskey@Sun.COM 		    orig_portstr, size);
4455895Syz147064 		if (status != DLADM_STATUS_OK)
4465895Syz147064 			goto destroyconf;
4475895Syz147064 
4485895Syz147064 		result_nports = (cmd == LAIOC_ADD) ? orig_nports + nports :
4495895Syz147064 		    orig_nports;
4505895Syz147064 
451*10616SSebastien.Roy@Sun.COM 		size = result_nports * (MAXLINKNAMELEN + 1) + 1;
4525895Syz147064 		if ((portstr = calloc(1, size)) == NULL) {
4535895Syz147064 			status = dladm_errno2status(errno);
4545895Syz147064 			goto destroyconf;
4555895Syz147064 		}
4565895Syz147064 
4575895Syz147064 		/*
4585895Syz147064 		 * get the new configuration and set to result_nports and
4595895Syz147064 		 * portstr.
4605895Syz147064 		 */
4615895Syz147064 		if (cmd == LAIOC_ADD) {
4625895Syz147064 			(void) strlcpy(portstr, orig_portstr, size);
463*10616SSebastien.Roy@Sun.COM 			for (i = 0; i < nports; i++) {
464*10616SSebastien.Roy@Sun.COM 				status = write_port(handle, portstr,
465*10616SSebastien.Roy@Sun.COM 				    ports[i].lp_linkid, size);
466*10616SSebastien.Roy@Sun.COM 				if (status != DLADM_STATUS_OK) {
467*10616SSebastien.Roy@Sun.COM 					free(portstr);
468*10616SSebastien.Roy@Sun.COM 					goto destroyconf;
469*10616SSebastien.Roy@Sun.COM 				}
470*10616SSebastien.Roy@Sun.COM 			}
4715895Syz147064 		} else {
4725895Syz147064 			char *next;
4735895Syz147064 			datalink_id_t portid;
4745895Syz147064 			uint32_t remove = 0;
4755895Syz147064 
4765895Syz147064 			for (next = orig_portstr, j = 0; j < orig_nports; j++) {
4775895Syz147064 				/*
4785895Syz147064 				 * Read the portids from the old configuration
4795895Syz147064 				 * one by one.
4805895Syz147064 				 */
481*10616SSebastien.Roy@Sun.COM 				status = read_port(handle, &next, &portid);
4825895Syz147064 				if (status != DLADM_STATUS_OK) {
4835895Syz147064 					free(portstr);
4845895Syz147064 					goto destroyconf;
4855895Syz147064 				}
4865895Syz147064 
4875895Syz147064 				/*
4885895Syz147064 				 * See whether this port is in the removal
4895895Syz147064 				 * list.  If not, copy to the new config.
4905895Syz147064 				 */
4915895Syz147064 				for (i = 0; i < nports; i++) {
4925895Syz147064 					if (ports[i].lp_linkid == portid)
4935895Syz147064 						break;
4945895Syz147064 				}
4955895Syz147064 				if (i == nports) {
496*10616SSebastien.Roy@Sun.COM 					status = write_port(handle, portstr,
497*10616SSebastien.Roy@Sun.COM 					    portid, size);
498*10616SSebastien.Roy@Sun.COM 					if (status != DLADM_STATUS_OK) {
499*10616SSebastien.Roy@Sun.COM 						free(portstr);
500*10616SSebastien.Roy@Sun.COM 						goto destroyconf;
501*10616SSebastien.Roy@Sun.COM 					}
5025895Syz147064 				} else {
5035895Syz147064 					remove++;
5045895Syz147064 				}
5055895Syz147064 			}
5065895Syz147064 			if (remove != nports) {
5075895Syz147064 				status = DLADM_STATUS_LINKINVAL;
5085895Syz147064 				free(portstr);
5095895Syz147064 				goto destroyconf;
5105895Syz147064 			}
5115895Syz147064 			result_nports -= nports;
5125895Syz147064 		}
5135895Syz147064 
5145895Syz147064 		u64 = result_nports;
5158453SAnurag.Maskey@Sun.COM 		if ((status = dladm_set_conf_field(handle, conf, FNPORTS,
5165895Syz147064 		    DLADM_TYPE_UINT64, &u64)) != DLADM_STATUS_OK) {
5175895Syz147064 			free(portstr);
5185895Syz147064 			goto destroyconf;
5195895Syz147064 		}
5205895Syz147064 
5218453SAnurag.Maskey@Sun.COM 		status = dladm_set_conf_field(handle, conf, FPORTS,
5228453SAnurag.Maskey@Sun.COM 		    DLADM_TYPE_STR, portstr);
5235895Syz147064 		free(portstr);
5245895Syz147064 		if (status != DLADM_STATUS_OK)
5255895Syz147064 			goto destroyconf;
5265895Syz147064 
5275895Syz147064 		/*
5285895Syz147064 		 * Write the new configuration to the persistent repository.
5295895Syz147064 		 */
5308453SAnurag.Maskey@Sun.COM 		status = dladm_write_conf(handle, conf);
5315895Syz147064 
5325895Syz147064 destroyconf:
5338453SAnurag.Maskey@Sun.COM 		dladm_destroy_conf(handle, conf);
5345895Syz147064 		if (status != DLADM_STATUS_OK) {
5355895Syz147064 			free(orig_portstr);
5365895Syz147064 			return (status);
5375895Syz147064 		}
5385895Syz147064 	}
5395895Syz147064 
5405895Syz147064 	/*
5415895Syz147064 	 * If the caller only requested to update the persistent
5425895Syz147064 	 * configuration, we are done.
5435895Syz147064 	 */
5445895Syz147064 	if (!(flags & DLADM_OPT_ACTIVE))
5455895Syz147064 		goto done;
5465895Syz147064 
5475895Syz147064 	/*
5485895Syz147064 	 * Update the active configuration.
5495895Syz147064 	 */
5505895Syz147064 	len = sizeof (*iocp) + nports * sizeof (laioc_port_t);
5515895Syz147064 	if ((iocp = malloc(len)) == NULL) {
5525895Syz147064 		status = DLADM_STATUS_NOMEM;
5533871Syz147064 		goto done;
5543871Syz147064 	}
5553871Syz147064 
5565895Syz147064 	iocp->la_linkid = linkid;
5575895Syz147064 	iocp->la_nports = nports;
5585895Syz147064 	if (cmd == LAIOC_ADD)
5595895Syz147064 		iocp->la_force = (flags & DLADM_OPT_FORCE);
5603871Syz147064 
5615895Syz147064 	ioc_ports = (laioc_port_t *)(iocp + 1);
5625895Syz147064 	for (i = 0; i < nports; i++)
5635895Syz147064 		ioc_ports[i].lp_linkid = ports[i].lp_linkid;
5645895Syz147064 
5658453SAnurag.Maskey@Sun.COM 	if (i_dladm_aggr_ioctl(handle, cmd, iocp) < 0)
5665895Syz147064 		status = dladm_errno2status(errno);
5673871Syz147064 
5683871Syz147064 done:
5693871Syz147064 	free(iocp);
5705895Syz147064 
5715895Syz147064 	/*
5725895Syz147064 	 * If the active configuration update fails, restore the old
5735895Syz147064 	 * persistent configuration if we've changed that.
5745895Syz147064 	 */
5755895Syz147064 	if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) {
5768453SAnurag.Maskey@Sun.COM 		if (dladm_read_conf(handle, linkid, &conf) == DLADM_STATUS_OK) {
5775895Syz147064 			u64 = orig_nports;
5788453SAnurag.Maskey@Sun.COM 			if ((dladm_set_conf_field(handle, conf, FNPORTS,
5795895Syz147064 			    DLADM_TYPE_UINT64, &u64) == DLADM_STATUS_OK) &&
5808453SAnurag.Maskey@Sun.COM 			    (dladm_set_conf_field(handle, conf, FPORTS,
5818453SAnurag.Maskey@Sun.COM 			    DLADM_TYPE_STR, orig_portstr) == DLADM_STATUS_OK)) {
5828453SAnurag.Maskey@Sun.COM 				(void) dladm_write_conf(handle, conf);
5835895Syz147064 			}
5848453SAnurag.Maskey@Sun.COM 			(void) dladm_destroy_conf(handle, conf);
5855895Syz147064 		}
5865895Syz147064 	}
5875895Syz147064 	free(orig_portstr);
5883871Syz147064 	return (status);
5893871Syz147064 }
5903871Syz147064 
5913871Syz147064 /*
5923871Syz147064  * Send a modify command to the link aggregation driver.
5933871Syz147064  */
5943871Syz147064 static dladm_status_t
5958453SAnurag.Maskey@Sun.COM i_dladm_aggr_modify_sys(dladm_handle_t handle, datalink_id_t linkid,
5968453SAnurag.Maskey@Sun.COM     uint32_t mask, dladm_aggr_modify_attr_t *attr)
5973871Syz147064 {
5983871Syz147064 	laioc_modify_t ioc;
5993871Syz147064 
6005895Syz147064 	ioc.lu_linkid = linkid;
6013871Syz147064 
6023871Syz147064 	ioc.lu_modify_mask = 0;
6033871Syz147064 	if (mask & DLADM_AGGR_MODIFY_POLICY)
6043871Syz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_POLICY;
6053871Syz147064 	if (mask & DLADM_AGGR_MODIFY_MAC)
6063871Syz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_MAC;
6073871Syz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE)
6083871Syz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_MODE;
6093871Syz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER)
6103871Syz147064 		ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_TIMER;
6113871Syz147064 
6123871Syz147064 	ioc.lu_policy = attr->ld_policy;
6133871Syz147064 	ioc.lu_mac_fixed = attr->ld_mac_fixed;
6143871Syz147064 	bcopy(attr->ld_mac, ioc.lu_mac, ETHERADDRL);
6153871Syz147064 	ioc.lu_lacp_mode = attr->ld_lacp_mode;
6163871Syz147064 	ioc.lu_lacp_timer = attr->ld_lacp_timer;
6173871Syz147064 
6188453SAnurag.Maskey@Sun.COM 	if (i_dladm_aggr_ioctl(handle, LAIOC_MODIFY, &ioc) < 0) {
6193871Syz147064 		if (errno == EINVAL)
6205895Syz147064 			return (DLADM_STATUS_MACADDRINVAL);
6213871Syz147064 		else
6225895Syz147064 			return (dladm_errno2status(errno));
6235895Syz147064 	} else {
6245895Syz147064 		return (DLADM_STATUS_OK);
6253871Syz147064 	}
6263871Syz147064 }
6273871Syz147064 
6283871Syz147064 /*
6293871Syz147064  * Send a create command to the link aggregation driver.
6303871Syz147064  */
6313871Syz147064 static dladm_status_t
6328453SAnurag.Maskey@Sun.COM i_dladm_aggr_create_sys(dladm_handle_t handle, datalink_id_t linkid,
6338453SAnurag.Maskey@Sun.COM     uint16_t key, uint32_t nports, dladm_aggr_port_attr_db_t *ports,
6348453SAnurag.Maskey@Sun.COM     uint32_t policy, boolean_t mac_addr_fixed, const uchar_t *mac_addr,
6355895Syz147064     aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, boolean_t force)
6363871Syz147064 {
6377408SSebastien.Roy@Sun.COM 	int i, len;
6385895Syz147064 	laioc_create_t *iocp = NULL;
6395895Syz147064 	laioc_port_t *ioc_ports;
6403871Syz147064 	dladm_status_t status = DLADM_STATUS_OK;
6413871Syz147064 
6425895Syz147064 	len = sizeof (*iocp) + nports * sizeof (laioc_port_t);
6433871Syz147064 	iocp = malloc(len);
6443871Syz147064 	if (iocp == NULL)
6453871Syz147064 		return (DLADM_STATUS_NOMEM);
6463871Syz147064 
6475895Syz147064 	iocp->lc_key = key;
6485895Syz147064 	iocp->lc_linkid = linkid;
6495895Syz147064 	iocp->lc_nports = nports;
6505895Syz147064 	iocp->lc_policy = policy;
6515895Syz147064 	iocp->lc_lacp_mode = lacp_mode;
6525895Syz147064 	iocp->lc_lacp_timer = lacp_timer;
6535895Syz147064 	ioc_ports = (laioc_port_t *)(iocp + 1);
6545895Syz147064 	iocp->lc_force = force;
6553871Syz147064 
6565895Syz147064 	for (i = 0; i < nports; i++)
6575895Syz147064 		ioc_ports[i].lp_linkid = ports[i].lp_linkid;
6585895Syz147064 
6595895Syz147064 	if (mac_addr_fixed && !VALID_PORT_MAC(mac_addr)) {
6605895Syz147064 		status = DLADM_STATUS_MACADDRINVAL;
6615895Syz147064 		goto done;
6623871Syz147064 	}
6633871Syz147064 
6645895Syz147064 	bcopy(mac_addr, iocp->lc_mac, ETHERADDRL);
6655895Syz147064 	iocp->lc_mac_fixed = mac_addr_fixed;
6663871Syz147064 
6678453SAnurag.Maskey@Sun.COM 	if (i_dladm_aggr_ioctl(handle, LAIOC_CREATE, iocp) < 0)
6685895Syz147064 		status = dladm_errno2status(errno);
6693871Syz147064 
6705895Syz147064 done:
6713871Syz147064 	free(iocp);
6723871Syz147064 	return (status);
6733871Syz147064 }
6743871Syz147064 
6753871Syz147064 /*
6763871Syz147064  * Invoked to bring up a link aggregation group.
6773871Syz147064  */
6785895Syz147064 static int
6798453SAnurag.Maskey@Sun.COM i_dladm_aggr_up(dladm_handle_t handle, datalink_id_t linkid, void *arg)
6803871Syz147064 {
6815895Syz147064 	dladm_status_t *statusp = (dladm_status_t *)arg;
6825895Syz147064 	dladm_aggr_grp_attr_t attr;
6835895Syz147064 	dladm_aggr_port_attr_db_t *ports = NULL;
6845895Syz147064 	uint16_t key = 0;
6855895Syz147064 	int i, j;
6863871Syz147064 	dladm_status_t status;
6873871Syz147064 
6888453SAnurag.Maskey@Sun.COM 	status = dladm_aggr_info(handle, linkid, &attr, DLADM_OPT_PERSIST);
6895895Syz147064 	if (status != DLADM_STATUS_OK) {
6905895Syz147064 		*statusp = status;
6915895Syz147064 		return (DLADM_WALK_CONTINUE);
6925895Syz147064 	}
6933871Syz147064 
6945895Syz147064 	if (attr.lg_key <= AGGR_MAX_KEY)
6955895Syz147064 		key = attr.lg_key;
6965895Syz147064 
6975895Syz147064 	ports = malloc(attr.lg_nports * sizeof (dladm_aggr_port_attr_db_t));
6985895Syz147064 	if (ports == NULL) {
6995895Syz147064 		status = DLADM_STATUS_NOMEM;
7005895Syz147064 		goto done;
7013871Syz147064 	}
7023871Syz147064 
7033871Syz147064 	/*
7045895Syz147064 	 * Validate (and purge) each physical link associated with this
7055895Syz147064 	 * aggregation, if the specific hardware has been removed during
7065895Syz147064 	 * the system shutdown.
7073871Syz147064 	 */
7085895Syz147064 	for (i = 0, j = 0; i < attr.lg_nports; i++) {
7095895Syz147064 		datalink_id_t	portid = attr.lg_ports[i].lp_linkid;
7105895Syz147064 		uint32_t	flags;
7115895Syz147064 		dladm_status_t	s;
7123871Syz147064 
7138453SAnurag.Maskey@Sun.COM 		s = dladm_datalink_id2info(handle, portid, &flags, NULL, NULL,
7148453SAnurag.Maskey@Sun.COM 		    NULL, 0);
7155895Syz147064 		if (s != DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE))
7165895Syz147064 			continue;
7175895Syz147064 
7185895Syz147064 		ports[j++].lp_linkid = portid;
7195895Syz147064 	}
7203871Syz147064 
7215895Syz147064 	if (j == 0) {
7225895Syz147064 		/*
7235895Syz147064 		 * All of the physical links are removed.
7245895Syz147064 		 */
7255895Syz147064 		status = DLADM_STATUS_BADARG;
7265895Syz147064 		goto done;
7273871Syz147064 	}
7283871Syz147064 
7295895Syz147064 	/*
7305895Syz147064 	 * Create active aggregation.
7315895Syz147064 	 */
7328453SAnurag.Maskey@Sun.COM 	if ((status = i_dladm_aggr_create_sys(handle, linkid,
7335895Syz147064 	    key, j, ports, attr.lg_policy, attr.lg_mac_fixed,
7345895Syz147064 	    (const uchar_t *)attr.lg_mac, attr.lg_lacp_mode,
7355895Syz147064 	    attr.lg_lacp_timer, attr.lg_force)) != DLADM_STATUS_OK) {
7365895Syz147064 		goto done;
7375895Syz147064 	}
7383871Syz147064 
7398453SAnurag.Maskey@Sun.COM 	if ((status = dladm_up_datalink_id(handle, linkid)) !=
7408453SAnurag.Maskey@Sun.COM 	    DLADM_STATUS_OK) {
7415895Syz147064 		laioc_delete_t ioc;
742*10616SSebastien.Roy@Sun.COM 
7435895Syz147064 		ioc.ld_linkid = linkid;
7448453SAnurag.Maskey@Sun.COM 		(void) i_dladm_aggr_ioctl(handle, LAIOC_DELETE, &ioc);
7455895Syz147064 	}
7465895Syz147064 done:
7475895Syz147064 	free(attr.lg_ports);
7485895Syz147064 	free(ports);
7495895Syz147064 
7505895Syz147064 	*statusp = status;
7515895Syz147064 	return (DLADM_WALK_CONTINUE);
7523871Syz147064 }
7533871Syz147064 
7543871Syz147064 /*
7555895Syz147064  * Bring up one aggregation, or all persistent aggregations.  In the latter
7565895Syz147064  * case, the walk may terminate early if bringup of an aggregation fails.
7573871Syz147064  */
7585895Syz147064 dladm_status_t
7598453SAnurag.Maskey@Sun.COM dladm_aggr_up(dladm_handle_t handle, datalink_id_t linkid)
7603871Syz147064 {
7613871Syz147064 	dladm_status_t status;
7623871Syz147064 
7635895Syz147064 	if (linkid == DATALINK_ALL_LINKID) {
7648453SAnurag.Maskey@Sun.COM 		(void) dladm_walk_datalink_id(i_dladm_aggr_up, handle, &status,
7655895Syz147064 		    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
7665895Syz147064 		    DLADM_OPT_PERSIST);
7675895Syz147064 		return (DLADM_STATUS_OK);
7685895Syz147064 	} else {
7698453SAnurag.Maskey@Sun.COM 		(void) i_dladm_aggr_up(handle, linkid, &status);
7703871Syz147064 		return (status);
7713871Syz147064 	}
7723871Syz147064 }
7733871Syz147064 
7743871Syz147064 /*
7753871Syz147064  * Given a policy string, return a policy mask. Returns B_TRUE on
7765895Syz147064  * success, or B_FALSE if an error occurred during parsing.
7773871Syz147064  */
7783871Syz147064 boolean_t
7793871Syz147064 dladm_aggr_str2policy(const char *str, uint32_t *policy)
7803871Syz147064 {
7813871Syz147064 	int i;
7823871Syz147064 	policy_t *pol;
7833871Syz147064 	char *token = NULL;
7843871Syz147064 	char *lasts;
7853871Syz147064 
7863871Syz147064 	*policy = 0;
7873871Syz147064 
7883871Syz147064 	while ((token = strtok_r((token == NULL) ? (char *)str : NULL, ",",
7893871Syz147064 	    &lasts)) != NULL) {
7903871Syz147064 		for (i = 0; i < NPOLICIES; i++) {
7913871Syz147064 			pol = &policies[i];
7923871Syz147064 			if (strcasecmp(token, pol->pol_name) == 0) {
7933871Syz147064 				*policy |= pol->policy;
7943871Syz147064 				break;
7953871Syz147064 			}
7963871Syz147064 		}
7973871Syz147064 		if (i == NPOLICIES)
7983871Syz147064 			return (B_FALSE);
7993871Syz147064 	}
8003871Syz147064 
8013871Syz147064 	return (B_TRUE);
8023871Syz147064 }
8033871Syz147064 
8043871Syz147064 /*
8053871Syz147064  * Given a policy mask, returns a printable string, or NULL if the
8063871Syz147064  * policy mask is invalid. It is the responsibility of the caller to
8073871Syz147064  * free the returned string after use.
8083871Syz147064  */
8093871Syz147064 char *
8103871Syz147064 dladm_aggr_policy2str(uint32_t policy, char *str)
8113871Syz147064 {
8123871Syz147064 	int i, npolicies = 0;
8133871Syz147064 	policy_t *pol;
8143871Syz147064 
8155895Syz147064 	if (str == NULL)
8165895Syz147064 		return (NULL);
8175895Syz147064 
8183871Syz147064 	str[0] = '\0';
8193871Syz147064 
8203871Syz147064 	for (i = 0; i < NPOLICIES; i++) {
8213871Syz147064 		pol = &policies[i];
8223871Syz147064 		if ((policy & pol->policy) != 0) {
8233871Syz147064 			npolicies++;
8243871Syz147064 			if (npolicies > 1)
8255895Syz147064 				(void) strlcat(str, ",", DLADM_STRSIZE);
8265895Syz147064 			(void) strlcat(str, pol->pol_name, DLADM_STRSIZE);
8273871Syz147064 		}
8283871Syz147064 	}
8293871Syz147064 
8303871Syz147064 	return (str);
8313871Syz147064 }
8323871Syz147064 
8333871Syz147064 /*
8343871Syz147064  * Given a MAC address string, return the MAC address in the mac_addr
8353871Syz147064  * array. If the MAC address was not explicitly specified, i.e. is
8363871Syz147064  * equal to 'auto', zero out mac-addr and set mac_fixed to B_TRUE.
8373871Syz147064  * Return B_FALSE if a syntax error was encountered, B_FALSE otherwise.
8383871Syz147064  */
8393871Syz147064 boolean_t
8403871Syz147064 dladm_aggr_str2macaddr(const char *str, boolean_t *mac_fixed, uchar_t *mac_addr)
8413871Syz147064 {
8423871Syz147064 	uchar_t *conv_str;
8433871Syz147064 	int mac_len;
8443871Syz147064 
8453871Syz147064 	*mac_fixed = (strcmp(str, "auto") != 0);
8463871Syz147064 	if (!*mac_fixed) {
8473871Syz147064 		bzero(mac_addr, ETHERADDRL);
8483871Syz147064 		return (B_TRUE);
8493871Syz147064 	}
8503871Syz147064 
8513871Syz147064 	conv_str = _link_aton(str, &mac_len);
8523871Syz147064 	if (conv_str == NULL)
8533871Syz147064 		return (B_FALSE);
8543871Syz147064 
8553871Syz147064 	if (mac_len != ETHERADDRL) {
8563871Syz147064 		free(conv_str);
8573871Syz147064 		return (B_FALSE);
8583871Syz147064 	}
8593871Syz147064 
8603871Syz147064 	if ((bcmp(zero_mac, conv_str, ETHERADDRL) == 0) ||
8613871Syz147064 	    (conv_str[0] & 0x01)) {
8623871Syz147064 		free(conv_str);
8633871Syz147064 		return (B_FALSE);
8643871Syz147064 	}
8653871Syz147064 
8663871Syz147064 	bcopy(conv_str, mac_addr, ETHERADDRL);
8673871Syz147064 	free(conv_str);
8683871Syz147064 
8693871Syz147064 	return (B_TRUE);
8703871Syz147064 }
8713871Syz147064 
8723871Syz147064 /*
8733871Syz147064  * Returns a string containing a printable representation of a MAC address.
8743871Syz147064  */
8753871Syz147064 const char *
8765895Syz147064 dladm_aggr_macaddr2str(const unsigned char *mac, char *buf)
8773871Syz147064 {
8783871Syz147064 	static char unknown_mac[] = {0, 0, 0, 0, 0, 0};
8793871Syz147064 
8803871Syz147064 	if (buf == NULL)
8813871Syz147064 		return (NULL);
8823871Syz147064 
8833871Syz147064 	if (bcmp(unknown_mac, mac, ETHERADDRL) == 0)
8845895Syz147064 		(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
8853871Syz147064 	else
8863871Syz147064 		return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER));
8875895Syz147064 
8885895Syz147064 	return (buf);
8893871Syz147064 }
8903871Syz147064 
8913871Syz147064 /*
8923871Syz147064  * Given a LACP mode string, find the corresponding LACP mode number. Returns
8933871Syz147064  * B_TRUE if a match was found, B_FALSE otherwise.
8943871Syz147064  */
8953871Syz147064 boolean_t
8963871Syz147064 dladm_aggr_str2lacpmode(const char *str, aggr_lacp_mode_t *lacp_mode)
8973871Syz147064 {
8983871Syz147064 	int i;
8993871Syz147064 	dladm_aggr_lacpmode_t *mode;
9003871Syz147064 
9013871Syz147064 	for (i = 0; i < NLACP_MODES; i++) {
9023871Syz147064 		mode = &lacp_modes[i];
9033871Syz147064 		if (strncasecmp(str, mode->mode_str,
9043871Syz147064 		    strlen(mode->mode_str)) == 0) {
9053871Syz147064 			*lacp_mode = mode->mode_id;
9063871Syz147064 			return (B_TRUE);
9073871Syz147064 		}
9083871Syz147064 	}
9093871Syz147064 
9103871Syz147064 	return (B_FALSE);
9113871Syz147064 }
9123871Syz147064 
9133871Syz147064 /*
9143871Syz147064  * Given a LACP mode number, returns a printable string, or NULL if the
9153871Syz147064  * LACP mode number is invalid.
9163871Syz147064  */
9173871Syz147064 const char *
9183871Syz147064 dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id, char *buf)
9193871Syz147064 {
9203871Syz147064 	int i;
9213871Syz147064 	dladm_aggr_lacpmode_t *mode;
9223871Syz147064 
9235895Syz147064 	if (buf == NULL)
9245895Syz147064 		return (NULL);
9255895Syz147064 
9263871Syz147064 	for (i = 0; i < NLACP_MODES; i++) {
9273871Syz147064 		mode = &lacp_modes[i];
9283871Syz147064 		if (mode->mode_id == mode_id) {
9293871Syz147064 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
9303871Syz147064 			    mode->mode_str);
9313871Syz147064 			return (buf);
9323871Syz147064 		}
9333871Syz147064 	}
9343871Syz147064 
9353871Syz147064 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
9363871Syz147064 	return (buf);
9373871Syz147064 }
9383871Syz147064 
9393871Syz147064 /*
9403871Syz147064  * Given a LACP timer string, find the corresponding LACP timer number. Returns
9413871Syz147064  * B_TRUE if a match was found, B_FALSE otherwise.
9423871Syz147064  */
9433871Syz147064 boolean_t
9443871Syz147064 dladm_aggr_str2lacptimer(const char *str, aggr_lacp_timer_t *lacp_timer)
9453871Syz147064 {
9463871Syz147064 	int i;
9473871Syz147064 	dladm_aggr_lacptimer_t *timer;
9483871Syz147064 
9493871Syz147064 	for (i = 0; i < NLACP_TIMERS; i++) {
9503871Syz147064 		timer = &lacp_timers[i];
9513871Syz147064 		if (strncasecmp(str, timer->lt_str,
9523871Syz147064 		    strlen(timer->lt_str)) == 0) {
9533871Syz147064 			*lacp_timer = timer->lt_id;
9543871Syz147064 			return (B_TRUE);
9553871Syz147064 		}
9563871Syz147064 	}
9573871Syz147064 
9583871Syz147064 	return (B_FALSE);
9593871Syz147064 }
9603871Syz147064 
9613871Syz147064 /*
9623871Syz147064  * Given a LACP timer, returns a printable string, or NULL if the
9633871Syz147064  * LACP timer number is invalid.
9643871Syz147064  */
9653871Syz147064 const char *
9663871Syz147064 dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id, char *buf)
9673871Syz147064 {
9683871Syz147064 	int i;
9693871Syz147064 	dladm_aggr_lacptimer_t *timer;
9703871Syz147064 
9715895Syz147064 	if (buf == NULL)
9725895Syz147064 		return (NULL);
9735895Syz147064 
9743871Syz147064 	for (i = 0; i < NLACP_TIMERS; i++) {
9753871Syz147064 		timer = &lacp_timers[i];
9763871Syz147064 		if (timer->lt_id == timer_id) {
9773871Syz147064 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
9783871Syz147064 			    timer->lt_str);
9793871Syz147064 			return (buf);
9803871Syz147064 		}
9813871Syz147064 	}
9823871Syz147064 
9833871Syz147064 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
9843871Syz147064 	return (buf);
9853871Syz147064 }
9863871Syz147064 
9873871Syz147064 const char *
9883871Syz147064 dladm_aggr_portstate2str(aggr_port_state_t state_id, char *buf)
9893871Syz147064 {
9903871Syz147064 	int			i;
9915895Syz147064 	dladm_aggr_port_state_t *state;
9925895Syz147064 
9935895Syz147064 	if (buf == NULL)
9945895Syz147064 		return (NULL);
9953871Syz147064 
9963871Syz147064 	for (i = 0; i < NPORT_STATES; i++) {
9973871Syz147064 		state = &port_states[i];
9983871Syz147064 		if (state->state_id == state_id) {
9993871Syz147064 			(void) snprintf(buf, DLADM_STRSIZE, "%s",
10003871Syz147064 			    state->state_str);
10013871Syz147064 			return (buf);
10023871Syz147064 		}
10033871Syz147064 	}
10043871Syz147064 
10053871Syz147064 	(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
10063871Syz147064 	return (buf);
10073871Syz147064 }
10083871Syz147064 
10095895Syz147064 static dladm_status_t
10108453SAnurag.Maskey@Sun.COM dladm_aggr_persist_aggr_conf(dladm_handle_t handle, const char *link,
10118453SAnurag.Maskey@Sun.COM     datalink_id_t linkid, uint16_t key, uint32_t nports,
10128453SAnurag.Maskey@Sun.COM     dladm_aggr_port_attr_db_t *ports, uint32_t policy, boolean_t mac_addr_fixed,
10138453SAnurag.Maskey@Sun.COM     const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode,
10148453SAnurag.Maskey@Sun.COM     aggr_lacp_timer_t lacp_timer, boolean_t force)
10153871Syz147064 {
10165895Syz147064 	dladm_conf_t conf = DLADM_INVALID_CONF;
10175895Syz147064 	char *portstr = NULL;
10185895Syz147064 	char macstr[ETHERADDRL * 3];
10195895Syz147064 	dladm_status_t status;
10205895Syz147064 	int i, size;
10215895Syz147064 	uint64_t u64;
10223871Syz147064 
10238453SAnurag.Maskey@Sun.COM 	if ((status = dladm_create_conf(handle, link, linkid,
10248453SAnurag.Maskey@Sun.COM 	    DATALINK_CLASS_AGGR, DL_ETHER, &conf)) != DLADM_STATUS_OK) {
10253871Syz147064 		return (status);
10263871Syz147064 	}
10273871Syz147064 
10285895Syz147064 	u64 = key;
10298453SAnurag.Maskey@Sun.COM 	status = dladm_set_conf_field(handle, conf, FKEY, DLADM_TYPE_UINT64,
10308453SAnurag.Maskey@Sun.COM 	    &u64);
10315895Syz147064 	if (status != DLADM_STATUS_OK)
10325895Syz147064 		goto done;
10333871Syz147064 
10345895Syz147064 	u64 = nports;
10358453SAnurag.Maskey@Sun.COM 	status = dladm_set_conf_field(handle, conf, FNPORTS, DLADM_TYPE_UINT64,
10368453SAnurag.Maskey@Sun.COM 	    &u64);
10375895Syz147064 	if (status != DLADM_STATUS_OK)
10385895Syz147064 		goto done;
10395895Syz147064 
1040*10616SSebastien.Roy@Sun.COM 	size = nports * MAXLINKNAMELEN + 1;
10415895Syz147064 	if ((portstr = calloc(1, size)) == NULL) {
10425895Syz147064 		status = DLADM_STATUS_NOMEM;
10435895Syz147064 		goto done;
10445895Syz147064 	}
10453871Syz147064 
1046*10616SSebastien.Roy@Sun.COM 	for (i = 0; i < nports; i++) {
1047*10616SSebastien.Roy@Sun.COM 		status = write_port(handle, portstr, ports[i].lp_linkid, size);
1048*10616SSebastien.Roy@Sun.COM 		if (status != DLADM_STATUS_OK) {
1049*10616SSebastien.Roy@Sun.COM 			free(portstr);
1050*10616SSebastien.Roy@Sun.COM 			goto done;
1051*10616SSebastien.Roy@Sun.COM 		}
1052*10616SSebastien.Roy@Sun.COM 	}
10538453SAnurag.Maskey@Sun.COM 	status = dladm_set_conf_field(handle, conf, FPORTS, DLADM_TYPE_STR,
10548453SAnurag.Maskey@Sun.COM 	    portstr);
10555895Syz147064 	free(portstr);
10565895Syz147064 
10575895Syz147064 	if (status != DLADM_STATUS_OK)
10585895Syz147064 		goto done;
10593871Syz147064 
10605895Syz147064 	u64 = policy;
10618453SAnurag.Maskey@Sun.COM 	status = dladm_set_conf_field(handle, conf, FPOLICY, DLADM_TYPE_UINT64,
10628453SAnurag.Maskey@Sun.COM 	    &u64);
10635895Syz147064 	if (status != DLADM_STATUS_OK)
10645895Syz147064 		goto done;
10655895Syz147064 
10668453SAnurag.Maskey@Sun.COM 	status = dladm_set_conf_field(handle, conf, FFIXMACADDR,
10678453SAnurag.Maskey@Sun.COM 	    DLADM_TYPE_BOOLEAN, &mac_addr_fixed);
10685895Syz147064 	if (status != DLADM_STATUS_OK)
10695895Syz147064 		goto done;
10705895Syz147064 
10715895Syz147064 	if (mac_addr_fixed) {
10725895Syz147064 		if (!VALID_PORT_MAC(mac_addr)) {
10735895Syz147064 			status = DLADM_STATUS_MACADDRINVAL;
10743871Syz147064 			goto done;
10753871Syz147064 		}
10763871Syz147064 
10775895Syz147064 		(void) dladm_aggr_macaddr2str(mac_addr, macstr);
10788453SAnurag.Maskey@Sun.COM 		status = dladm_set_conf_field(handle, conf, FMACADDR,
10798453SAnurag.Maskey@Sun.COM 		    DLADM_TYPE_STR, macstr);
10805895Syz147064 		if (status != DLADM_STATUS_OK)
10813871Syz147064 			goto done;
10823871Syz147064 	}
10833871Syz147064 
10848453SAnurag.Maskey@Sun.COM 	status = dladm_set_conf_field(handle, conf, FFORCE, DLADM_TYPE_BOOLEAN,
10858453SAnurag.Maskey@Sun.COM 	    &force);
10865895Syz147064 	if (status != DLADM_STATUS_OK)
10875895Syz147064 		goto done;
10885895Syz147064 
10895895Syz147064 	u64 = lacp_mode;
10908453SAnurag.Maskey@Sun.COM 	status = dladm_set_conf_field(handle, conf, FLACPMODE,
10918453SAnurag.Maskey@Sun.COM 	    DLADM_TYPE_UINT64, &u64);
10925895Syz147064 	if (status != DLADM_STATUS_OK)
10935895Syz147064 		goto done;
10945895Syz147064 
10955895Syz147064 	u64 = lacp_timer;
10968453SAnurag.Maskey@Sun.COM 	status = dladm_set_conf_field(handle, conf, FLACPTIMER,
10978453SAnurag.Maskey@Sun.COM 	    DLADM_TYPE_UINT64, &u64);
10985895Syz147064 	if (status != DLADM_STATUS_OK)
10995895Syz147064 		goto done;
11005895Syz147064 
11013871Syz147064 	/*
11025895Syz147064 	 * Commit the link aggregation configuration.
11033871Syz147064 	 */
11048453SAnurag.Maskey@Sun.COM 	status = dladm_write_conf(handle, conf);
11053871Syz147064 
11063871Syz147064 done:
11078453SAnurag.Maskey@Sun.COM 	dladm_destroy_conf(handle, conf);
11083871Syz147064 	return (status);
11093871Syz147064 }
11103871Syz147064 
11113871Syz147064 /*
11123871Syz147064  * Create a new link aggregation group. Update the configuration
11133871Syz147064  * file and bring it up.
11143871Syz147064  */
11153871Syz147064 dladm_status_t
11168453SAnurag.Maskey@Sun.COM dladm_aggr_create(dladm_handle_t handle, const char *name, uint16_t key,
11178453SAnurag.Maskey@Sun.COM     uint32_t nports, dladm_aggr_port_attr_db_t *ports, uint32_t policy,
11188453SAnurag.Maskey@Sun.COM     boolean_t mac_addr_fixed, const uchar_t *mac_addr,
11198453SAnurag.Maskey@Sun.COM     aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, uint32_t flags)
11203871Syz147064 {
11215895Syz147064 	datalink_id_t linkid = DATALINK_INVALID_LINKID;
11225895Syz147064 	uint32_t media;
11235895Syz147064 	uint32_t i;
11245895Syz147064 	datalink_class_t class;
11253871Syz147064 	dladm_status_t status;
11265895Syz147064 	boolean_t force = (flags & DLADM_OPT_FORCE) ? B_TRUE : B_FALSE;
11273871Syz147064 
11285895Syz147064 	if (key != 0 && key > AGGR_MAX_KEY)
11293871Syz147064 		return (DLADM_STATUS_KEYINVAL);
11303871Syz147064 
11315895Syz147064 	if (nports == 0)
11325895Syz147064 		return (DLADM_STATUS_BADARG);
11335895Syz147064 
11345895Syz147064 	for (i = 0; i < nports; i++) {
11358453SAnurag.Maskey@Sun.COM 		if ((dladm_datalink_id2info(handle, ports[i].lp_linkid, NULL,
11365895Syz147064 		    &class, &media, NULL, 0) != DLADM_STATUS_OK) ||
11379815SRishi.Srivatsavai@Sun.COM 		    !((class == DATALINK_CLASS_PHYS || class ==
11389815SRishi.Srivatsavai@Sun.COM 		    DATALINK_CLASS_SIMNET) && (media == DL_ETHER))) {
11395895Syz147064 			return (DLADM_STATUS_BADARG);
11405895Syz147064 		}
11415895Syz147064 	}
11425895Syz147064 
11435895Syz147064 	flags &= (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
11448453SAnurag.Maskey@Sun.COM 	if ((status = dladm_create_datalink_id(handle, name,
11458453SAnurag.Maskey@Sun.COM 	    DATALINK_CLASS_AGGR, DL_ETHER, flags, &linkid)) !=
11468453SAnurag.Maskey@Sun.COM 	    DLADM_STATUS_OK) {
11475895Syz147064 		goto fail;
11485895Syz147064 	}
11495895Syz147064 
11505895Syz147064 	if ((flags & DLADM_OPT_PERSIST) &&
11518453SAnurag.Maskey@Sun.COM 	    (status = dladm_aggr_persist_aggr_conf(handle, name, linkid, key,
11528453SAnurag.Maskey@Sun.COM 	    nports, ports, policy, mac_addr_fixed, mac_addr, lacp_mode,
11538453SAnurag.Maskey@Sun.COM 	    lacp_timer, force)) != DLADM_STATUS_OK) {
11545895Syz147064 		goto fail;
11555895Syz147064 	}
11565895Syz147064 
11575895Syz147064 	if (!(flags & DLADM_OPT_ACTIVE))
11585895Syz147064 		return (DLADM_STATUS_OK);
11595895Syz147064 
11608453SAnurag.Maskey@Sun.COM 	status = i_dladm_aggr_create_sys(handle, linkid, key, nports, ports,
11618453SAnurag.Maskey@Sun.COM 	    policy, mac_addr_fixed, mac_addr, lacp_mode, lacp_timer, force);
11623871Syz147064 
11635895Syz147064 	if (status != DLADM_STATUS_OK) {
11645895Syz147064 		if (flags & DLADM_OPT_PERSIST)
11658453SAnurag.Maskey@Sun.COM 			(void) dladm_remove_conf(handle, linkid);
11665895Syz147064 		goto fail;
11675895Syz147064 	}
11685895Syz147064 
11695895Syz147064 	return (DLADM_STATUS_OK);
11705895Syz147064 
11715895Syz147064 fail:
11725895Syz147064 	if (linkid != DATALINK_INVALID_LINKID)
11738453SAnurag.Maskey@Sun.COM 		(void) dladm_destroy_datalink_id(handle, linkid, flags);
11745895Syz147064 
11755895Syz147064 	return (status);
11765895Syz147064 }
11775895Syz147064 
11785895Syz147064 static dladm_status_t
11798453SAnurag.Maskey@Sun.COM i_dladm_aggr_get_aggr_attr(dladm_handle_t handle, dladm_conf_t conf,
11808453SAnurag.Maskey@Sun.COM     uint32_t mask, dladm_aggr_modify_attr_t *attrp)
11815895Syz147064 {
11825895Syz147064 	dladm_status_t status = DLADM_STATUS_OK;
11835895Syz147064 	char macstr[ETHERADDRL * 3];
11845895Syz147064 	uint64_t u64;
11855895Syz147064 
11865895Syz147064 	if (mask & DLADM_AGGR_MODIFY_POLICY) {
11878453SAnurag.Maskey@Sun.COM 		status = dladm_get_conf_field(handle, conf, FPOLICY, &u64,
11885895Syz147064 		    sizeof (u64));
11895895Syz147064 		if (status != DLADM_STATUS_OK)
11905895Syz147064 			return (status);
11915895Syz147064 		attrp->ld_policy = (uint32_t)u64;
11925895Syz147064 	}
11935895Syz147064 
11945895Syz147064 	if (mask & DLADM_AGGR_MODIFY_MAC) {
11958453SAnurag.Maskey@Sun.COM 		status = dladm_get_conf_field(handle, conf, FFIXMACADDR,
11965895Syz147064 		    &attrp->ld_mac_fixed, sizeof (boolean_t));
11973871Syz147064 		if (status != DLADM_STATUS_OK)
11983871Syz147064 			return (status);
11995895Syz147064 
12005895Syz147064 		if (attrp->ld_mac_fixed) {
12015895Syz147064 			boolean_t fixed;
12025895Syz147064 
12038453SAnurag.Maskey@Sun.COM 			status = dladm_get_conf_field(handle, conf, FMACADDR,
12045895Syz147064 			    macstr, sizeof (macstr));
12055895Syz147064 			if (status != DLADM_STATUS_OK)
12065895Syz147064 				return (status);
12073871Syz147064 
12085895Syz147064 			if (!dladm_aggr_str2macaddr(macstr, &fixed,
12095895Syz147064 			    attrp->ld_mac)) {
12105895Syz147064 				return (DLADM_STATUS_REPOSITORYINVAL);
12115895Syz147064 			}
12125895Syz147064 		}
12135895Syz147064 	}
12143871Syz147064 
12155895Syz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE) {
12168453SAnurag.Maskey@Sun.COM 		status = dladm_get_conf_field(handle, conf, FLACPMODE, &u64,
12175895Syz147064 		    sizeof (u64));
12185895Syz147064 		if (status != DLADM_STATUS_OK)
12195895Syz147064 			return (status);
12205895Syz147064 		attrp->ld_lacp_mode = (aggr_lacp_mode_t)u64;
12215895Syz147064 	}
12225895Syz147064 
12235895Syz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) {
12248453SAnurag.Maskey@Sun.COM 		status = dladm_get_conf_field(handle, conf, FLACPTIMER, &u64,
12255895Syz147064 		    sizeof (u64));
12265895Syz147064 		if (status != DLADM_STATUS_OK)
12275895Syz147064 			return (status);
12285895Syz147064 		attrp->ld_lacp_timer = (aggr_lacp_timer_t)u64;
12293871Syz147064 	}
12303871Syz147064 
12315895Syz147064 	return (status);
12325895Syz147064 }
12335895Syz147064 
12345895Syz147064 static dladm_status_t
12358453SAnurag.Maskey@Sun.COM i_dladm_aggr_set_aggr_attr(dladm_handle_t handle, dladm_conf_t conf,
12368453SAnurag.Maskey@Sun.COM     uint32_t mask, dladm_aggr_modify_attr_t *attrp)
12375895Syz147064 {
12385895Syz147064 	dladm_status_t status = DLADM_STATUS_OK;
12395895Syz147064 	char macstr[ETHERADDRL * 3];
12405895Syz147064 	uint64_t u64;
12415895Syz147064 
12425895Syz147064 	if (mask & DLADM_AGGR_MODIFY_POLICY) {
12435895Syz147064 		u64 = attrp->ld_policy;
12448453SAnurag.Maskey@Sun.COM 		status = dladm_set_conf_field(handle, conf, FPOLICY,
12458453SAnurag.Maskey@Sun.COM 		    DLADM_TYPE_UINT64, &u64);
12465895Syz147064 		if (status != DLADM_STATUS_OK)
12475895Syz147064 			return (status);
12485895Syz147064 	}
12495895Syz147064 
12505895Syz147064 	if (mask & DLADM_AGGR_MODIFY_MAC) {
12518453SAnurag.Maskey@Sun.COM 		status = dladm_set_conf_field(handle, conf, FFIXMACADDR,
12525895Syz147064 		    DLADM_TYPE_BOOLEAN, &attrp->ld_mac_fixed);
12535895Syz147064 		if (status != DLADM_STATUS_OK)
12545895Syz147064 			return (status);
12553871Syz147064 
12565895Syz147064 		if (attrp->ld_mac_fixed) {
12575895Syz147064 			(void) dladm_aggr_macaddr2str(attrp->ld_mac, macstr);
12588453SAnurag.Maskey@Sun.COM 			status = dladm_set_conf_field(handle, conf, FMACADDR,
12595895Syz147064 			    DLADM_TYPE_STR, macstr);
12605895Syz147064 			if (status != DLADM_STATUS_OK)
12615895Syz147064 				return (status);
12625895Syz147064 		}
12635895Syz147064 	}
12645895Syz147064 
12655895Syz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_MODE) {
12665895Syz147064 		u64 = attrp->ld_lacp_mode;
12678453SAnurag.Maskey@Sun.COM 		status = dladm_set_conf_field(handle, conf, FLACPMODE,
12685895Syz147064 		    DLADM_TYPE_UINT64, &u64);
12695895Syz147064 		if (status != DLADM_STATUS_OK)
12705895Syz147064 			return (status);
12715895Syz147064 	}
12725895Syz147064 
12735895Syz147064 	if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) {
12745895Syz147064 		u64 = attrp->ld_lacp_timer;
12758453SAnurag.Maskey@Sun.COM 		status = dladm_set_conf_field(handle, conf, FLACPTIMER,
12765895Syz147064 		    DLADM_TYPE_UINT64, &u64);
12775895Syz147064 		if (status != DLADM_STATUS_OK)
12785895Syz147064 			return (status);
12795895Syz147064 	}
12803871Syz147064 
12813871Syz147064 	return (status);
12823871Syz147064 }
12833871Syz147064 
12843871Syz147064 /*
12853871Syz147064  * Modify the parameters of an existing link aggregation group. Update
12863871Syz147064  * the configuration file and pass the changes to the kernel.
12873871Syz147064  */
12883871Syz147064 dladm_status_t
12898453SAnurag.Maskey@Sun.COM dladm_aggr_modify(dladm_handle_t handle, datalink_id_t linkid,
12908453SAnurag.Maskey@Sun.COM     uint32_t modify_mask, uint32_t policy, boolean_t mac_fixed,
12918453SAnurag.Maskey@Sun.COM     const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode,
12925895Syz147064     aggr_lacp_timer_t lacp_timer, uint32_t flags)
12933871Syz147064 {
12943871Syz147064 	dladm_aggr_modify_attr_t new_attr, old_attr;
12955895Syz147064 	dladm_conf_t conf;
12963871Syz147064 	dladm_status_t status;
12973871Syz147064 
12985895Syz147064 	new_attr.ld_policy = policy;
12995895Syz147064 	new_attr.ld_mac_fixed = mac_fixed;
13005895Syz147064 	new_attr.ld_lacp_mode = lacp_mode;
13015895Syz147064 	new_attr.ld_lacp_timer = lacp_timer;
13025895Syz147064 	bcopy(mac_addr, new_attr.ld_mac, ETHERADDRL);
13035895Syz147064 
13045895Syz147064 	if (flags & DLADM_OPT_PERSIST) {
13058453SAnurag.Maskey@Sun.COM 		status = dladm_read_conf(handle, linkid, &conf);
13065895Syz147064 		if (status != DLADM_STATUS_OK)
13075895Syz147064 			return (status);
13083871Syz147064 
13098453SAnurag.Maskey@Sun.COM 		if ((status = i_dladm_aggr_get_aggr_attr(handle, conf,
13108453SAnurag.Maskey@Sun.COM 		    modify_mask, &old_attr)) != DLADM_STATUS_OK) {
13115895Syz147064 			goto done;
13125895Syz147064 		}
13133871Syz147064 
13148453SAnurag.Maskey@Sun.COM 		if ((status = i_dladm_aggr_set_aggr_attr(handle, conf,
13158453SAnurag.Maskey@Sun.COM 		    modify_mask, &new_attr)) != DLADM_STATUS_OK) {
13165895Syz147064 			goto done;
13175895Syz147064 		}
13185895Syz147064 
13198453SAnurag.Maskey@Sun.COM 		status = dladm_write_conf(handle, conf);
13205895Syz147064 
13215895Syz147064 done:
13228453SAnurag.Maskey@Sun.COM 		dladm_destroy_conf(handle, conf);
13235895Syz147064 		if (status != DLADM_STATUS_OK)
13245895Syz147064 			return (status);
13253871Syz147064 	}
13263871Syz147064 
13275895Syz147064 	if (!(flags & DLADM_OPT_ACTIVE))
13285895Syz147064 		return (DLADM_STATUS_OK);
13293871Syz147064 
13308453SAnurag.Maskey@Sun.COM 	status = i_dladm_aggr_modify_sys(handle, linkid, modify_mask,
13318453SAnurag.Maskey@Sun.COM 	    &new_attr);
13325895Syz147064 	if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) {
13338453SAnurag.Maskey@Sun.COM 		if (dladm_read_conf(handle, linkid, &conf) == DLADM_STATUS_OK) {
13348453SAnurag.Maskey@Sun.COM 			if (i_dladm_aggr_set_aggr_attr(handle, conf,
13358453SAnurag.Maskey@Sun.COM 			    modify_mask, &old_attr) == DLADM_STATUS_OK) {
13368453SAnurag.Maskey@Sun.COM 				(void) dladm_write_conf(handle, conf);
13375895Syz147064 			}
13388453SAnurag.Maskey@Sun.COM 			dladm_destroy_conf(handle, conf);
13395895Syz147064 		}
13403871Syz147064 	}
13413871Syz147064 
13423871Syz147064 	return (status);
13433871Syz147064 }
13443871Syz147064 
13455895Syz147064 typedef struct aggr_held_arg_s {
13465895Syz147064 	datalink_id_t	aggrid;
13475895Syz147064 	boolean_t	isheld;
13485895Syz147064 } aggr_held_arg_t;
13495895Syz147064 
13505895Syz147064 static int
13518453SAnurag.Maskey@Sun.COM i_dladm_aggr_is_held(dladm_handle_t handle, datalink_id_t linkid, void *arg)
13525895Syz147064 {
13535895Syz147064 	aggr_held_arg_t		*aggr_held_arg = arg;
13545895Syz147064 	dladm_vlan_attr_t	dva;
13555895Syz147064 
13568453SAnurag.Maskey@Sun.COM 	if (dladm_vlan_info(handle, linkid, &dva, DLADM_OPT_PERSIST) !=
13578453SAnurag.Maskey@Sun.COM 	    DLADM_STATUS_OK)
13585895Syz147064 		return (DLADM_WALK_CONTINUE);
13595895Syz147064 
13605895Syz147064 	if (dva.dv_linkid == aggr_held_arg->aggrid) {
13615895Syz147064 		/*
13625895Syz147064 		 * This VLAN is created over the given aggregation.
13635895Syz147064 		 */
13645895Syz147064 		aggr_held_arg->isheld = B_TRUE;
13655895Syz147064 		return (DLADM_WALK_TERMINATE);
13665895Syz147064 	}
13675895Syz147064 	return (DLADM_WALK_CONTINUE);
13685895Syz147064 }
13695895Syz147064 
13703871Syz147064 /*
13715895Syz147064  * Delete a previously created link aggregation group. Either the name "aggr"
13725895Syz147064  * or the "key" is specified.
13733871Syz147064  */
13743871Syz147064 dladm_status_t
13758453SAnurag.Maskey@Sun.COM dladm_aggr_delete(dladm_handle_t handle, datalink_id_t linkid, uint32_t flags)
13763871Syz147064 {
13775895Syz147064 	laioc_delete_t ioc;
13785895Syz147064 	datalink_class_t class;
13793871Syz147064 	dladm_status_t status;
13803871Syz147064 
13818453SAnurag.Maskey@Sun.COM 	if ((dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, NULL,
13828453SAnurag.Maskey@Sun.COM 	    0) != DLADM_STATUS_OK) || (class != DATALINK_CLASS_AGGR)) {
13835895Syz147064 		return (DLADM_STATUS_BADARG);
13845895Syz147064 	}
13853871Syz147064 
13865895Syz147064 	if (flags & DLADM_OPT_ACTIVE) {
13875895Syz147064 		ioc.ld_linkid = linkid;
13888453SAnurag.Maskey@Sun.COM 		if ((i_dladm_aggr_ioctl(handle, LAIOC_DELETE, &ioc) < 0) &&
13895895Syz147064 		    ((errno != ENOENT) || !(flags & DLADM_OPT_PERSIST))) {
13905895Syz147064 			status = dladm_errno2status(errno);
13915895Syz147064 			return (status);
13925895Syz147064 		}
13933871Syz147064 
13943871Syz147064 		/*
13955895Syz147064 		 * Delete ACTIVE linkprop first.
13963871Syz147064 		 */
13978453SAnurag.Maskey@Sun.COM 		(void) dladm_set_linkprop(handle, linkid, NULL, NULL, 0,
13985895Syz147064 		    DLADM_OPT_ACTIVE);
13998453SAnurag.Maskey@Sun.COM 		(void) dladm_destroy_datalink_id(handle, linkid,
14008453SAnurag.Maskey@Sun.COM 		    DLADM_OPT_ACTIVE);
14013871Syz147064 	}
14023871Syz147064 
14035895Syz147064 	/*
14045895Syz147064 	 * If we reach here, it means that the active aggregation has already
14055895Syz147064 	 * been deleted, and there is no active VLANs holding this aggregation.
14065895Syz147064 	 * Now we see whether there is any persistent VLANs holding this
14075895Syz147064 	 * aggregation. If so, we fail the operation.
14085895Syz147064 	 */
14095895Syz147064 	if (flags & DLADM_OPT_PERSIST) {
14105895Syz147064 		aggr_held_arg_t arg;
14115895Syz147064 
14125895Syz147064 		arg.aggrid = linkid;
14135895Syz147064 		arg.isheld = B_FALSE;
14143871Syz147064 
14158453SAnurag.Maskey@Sun.COM 		(void) dladm_walk_datalink_id(i_dladm_aggr_is_held, handle,
14165895Syz147064 		    &arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
14175895Syz147064 		    DLADM_OPT_PERSIST);
14185895Syz147064 		if (arg.isheld)
14195895Syz147064 			return (DLADM_STATUS_LINKBUSY);
14205895Syz147064 
1421*10616SSebastien.Roy@Sun.COM 		(void) dladm_remove_conf(handle, linkid);
14228453SAnurag.Maskey@Sun.COM 		(void) dladm_destroy_datalink_id(handle, linkid,
14238453SAnurag.Maskey@Sun.COM 		    DLADM_OPT_PERSIST);
14245895Syz147064 	}
14255895Syz147064 
14265895Syz147064 	return (DLADM_STATUS_OK);
14273871Syz147064 }
14283871Syz147064 
14293871Syz147064 /*
14303871Syz147064  * Add one or more ports to an existing link aggregation.
14313871Syz147064  */
14323871Syz147064 dladm_status_t
14338453SAnurag.Maskey@Sun.COM dladm_aggr_add(dladm_handle_t handle, datalink_id_t linkid, uint32_t nports,
14345895Syz147064     dladm_aggr_port_attr_db_t *ports, uint32_t flags)
14353871Syz147064 {
14368453SAnurag.Maskey@Sun.COM 	return (i_dladm_aggr_add_rmv(handle, linkid, nports, ports, flags,
14378453SAnurag.Maskey@Sun.COM 	    LAIOC_ADD));
14383871Syz147064 }
14393871Syz147064 
14403871Syz147064 /*
14413871Syz147064  * Remove one or more ports from an existing link aggregation.
14423871Syz147064  */
14433871Syz147064 dladm_status_t
14448453SAnurag.Maskey@Sun.COM dladm_aggr_remove(dladm_handle_t handle, datalink_id_t linkid, uint32_t nports,
14455895Syz147064     dladm_aggr_port_attr_db_t *ports, uint32_t flags)
14463871Syz147064 {
14478453SAnurag.Maskey@Sun.COM 	return (i_dladm_aggr_add_rmv(handle, linkid, nports, ports, flags,
14485895Syz147064 	    LAIOC_REMOVE));
14495895Syz147064 }
14503871Syz147064 
14515895Syz147064 typedef struct i_walk_key_state_s {
14525895Syz147064 	uint16_t key;
14535895Syz147064 	datalink_id_t linkid;
14545895Syz147064 	boolean_t found;
14555895Syz147064 } i_walk_key_state_t;
14563871Syz147064 
14575895Syz147064 static int
14588453SAnurag.Maskey@Sun.COM i_dladm_walk_key2linkid(dladm_handle_t handle, datalink_id_t linkid, void *arg)
14595895Syz147064 {
14605895Syz147064 	dladm_conf_t conf;
14615895Syz147064 	uint16_t key;
14625895Syz147064 	dladm_status_t status;
14635895Syz147064 	i_walk_key_state_t *statep = (i_walk_key_state_t *)arg;
14645895Syz147064 	uint64_t u64;
14653871Syz147064 
14668453SAnurag.Maskey@Sun.COM 	if (dladm_read_conf(handle, linkid, &conf) != 0)
14675895Syz147064 		return (DLADM_WALK_CONTINUE);
14685895Syz147064 
14698453SAnurag.Maskey@Sun.COM 	status = dladm_get_conf_field(handle, conf, FKEY, &u64, sizeof (u64));
14705895Syz147064 	key = (uint16_t)u64;
14718453SAnurag.Maskey@Sun.COM 	dladm_destroy_conf(handle, conf);
14725895Syz147064 
14735895Syz147064 	if ((status == DLADM_STATUS_OK) && (key == statep->key)) {
14745895Syz147064 		statep->found = B_TRUE;
14755895Syz147064 		statep->linkid = linkid;
14765895Syz147064 		return (DLADM_WALK_TERMINATE);
14773871Syz147064 	}
14783871Syz147064 
14795895Syz147064 	return (DLADM_WALK_CONTINUE);
14805895Syz147064 }
14815895Syz147064 
14825895Syz147064 dladm_status_t
14838453SAnurag.Maskey@Sun.COM dladm_key2linkid(dladm_handle_t handle, uint16_t key, datalink_id_t *linkidp,
14848453SAnurag.Maskey@Sun.COM     uint32_t flags)
14855895Syz147064 {
14865895Syz147064 	i_walk_key_state_t state;
14875895Syz147064 
14885895Syz147064 	if (key > AGGR_MAX_KEY)
14895895Syz147064 		return (DLADM_STATUS_NOTFOUND);
14903871Syz147064 
14915895Syz147064 	state.found = B_FALSE;
14925895Syz147064 	state.key = key;
14935895Syz147064 
14948453SAnurag.Maskey@Sun.COM 	(void) dladm_walk_datalink_id(i_dladm_walk_key2linkid, handle, &state,
14955895Syz147064 	    DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags);
14965895Syz147064 	if (state.found == B_TRUE) {
14975895Syz147064 		*linkidp = state.linkid;
14985895Syz147064 		return (DLADM_STATUS_OK);
14995895Syz147064 	} else {
15005895Syz147064 		return (DLADM_STATUS_NOTFOUND);
15015895Syz147064 	}
15023871Syz147064 }
1503