13147Sxc151355 /*
23147Sxc151355  * CDDL HEADER START
33147Sxc151355  *
43147Sxc151355  * The contents of this file are subject to the terms of the
53147Sxc151355  * Common Development and Distribution License (the "License").
63147Sxc151355  * You may not use this file except in compliance with the License.
73147Sxc151355  *
83147Sxc151355  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93147Sxc151355  * or http://www.opensolaris.org/os/licensing.
103147Sxc151355  * See the License for the specific language governing permissions
113147Sxc151355  * and limitations under the License.
123147Sxc151355  *
133147Sxc151355  * When distributing Covered Code, include this CDDL HEADER in each
143147Sxc151355  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153147Sxc151355  * If applicable, add the following below this CDDL HEADER, with the
163147Sxc151355  * fields enclosed by brackets "[]" replaced with your own identifying
173147Sxc151355  * information: Portions Copyright [yyyy] [name of copyright owner]
183147Sxc151355  *
193147Sxc151355  * CDDL HEADER END
203147Sxc151355  */
213147Sxc151355 /*
225895Syz147064  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
233147Sxc151355  * Use is subject to license terms.
243147Sxc151355  */
253147Sxc151355 
263147Sxc151355 #pragma ident	"%Z%%M%	%I%	%E% SMI"
273147Sxc151355 
283147Sxc151355 #include <stdlib.h>
293147Sxc151355 #include <strings.h>
303147Sxc151355 #include <errno.h>
313147Sxc151355 #include <ctype.h>
325895Syz147064 #include <stddef.h>
333448Sdh155122 #include <sys/types.h>
343147Sxc151355 #include <sys/stat.h>
353448Sdh155122 #include <sys/dld.h>
363448Sdh155122 #include <sys/zone.h>
373448Sdh155122 #include <fcntl.h>
383448Sdh155122 #include <unistd.h>
393448Sdh155122 #include <libdevinfo.h>
403448Sdh155122 #include <zone.h>
413871Syz147064 #include <libdllink.h>
423147Sxc151355 #include <libdladm_impl.h>
435895Syz147064 #include <libdlwlan_impl.h>
443871Syz147064 #include <libdlwlan.h>
455895Syz147064 #include <libdlvlan.h>
463448Sdh155122 #include <dlfcn.h>
473448Sdh155122 #include <link.h>
485895Syz147064 #include <inet/wifi_ioctl.h>
495903Ssowmini #include <libdladm.h>
505903Ssowmini #include <sys/param.h>
515903Ssowmini #include <sys/dld.h>
525903Ssowmini #include <inttypes.h>
535903Ssowmini #include <sys/ethernet.h>
543448Sdh155122 
555895Syz147064 /*
565895Syz147064  * The linkprop get() callback.
575903Ssowmini  * - pd: 	pointer to the struct prop_desc
585895Syz147064  * - propstrp:	a property string array to keep the returned property.
595895Syz147064  *		Caller allocated.
605895Syz147064  * - cntp:	number of returned properties.
615895Syz147064  *		Caller also uses it to indicate how many it expects.
625895Syz147064  */
635903Ssowmini struct prop_desc;
645903Ssowmini 
655903Ssowmini typedef dladm_status_t	pd_getf_t(struct prop_desc *pd,
665960Ssowmini 			datalink_id_t, char **propstp, uint_t *cntp,
676512Ssowmini 			datalink_media_t, uint_t);
685895Syz147064 
695895Syz147064 /*
705895Syz147064  * The linkprop set() callback.
715895Syz147064  * - propval:	a val_desc_t array which keeps the property values to be set.
725895Syz147064  * - cnt:	number of properties to be set.
735903Ssowmini  * - flags: 	additional flags passed down the system call.
745903Ssowmini  *
755903Ssowmini  * pd_set takes val_desc_t given by pd_check(), translates it into
765903Ssowmini  * a format suitable for kernel consumption. This may require allocation
775903Ssowmini  * of ioctl buffers etc. pd_set() may call another common routine (used
785903Ssowmini  * by all other pd_sets) which invokes the ioctl.
795895Syz147064  */
805903Ssowmini typedef dladm_status_t	pd_setf_t(struct prop_desc *, datalink_id_t,
815960Ssowmini 			val_desc_t *propval, uint_t cnt, uint_t flags,
825960Ssowmini 			datalink_media_t);
833448Sdh155122 
843448Sdh155122 
855895Syz147064 /*
865895Syz147064  * The linkprop check() callback.
875895Syz147064  * - propstrp:	property string array which keeps the property to be checked.
885895Syz147064  * - cnt:	number of properties.
895895Syz147064  * - propval:	return value; the property values of the given property strings.
905903Ssowmini  *
915903Ssowmini  * pd_check checks that the input values are valid. It does so by
925903Ssowmini  * iteraring through the pd_modval list for the property. If
935903Ssowmini  * the modifiable values cannot be expressed as a list, a pd_check
945903Ssowmini  * specific to this property can be used. If the input values are
955903Ssowmini  * verified to be valid, pd_check allocates a val_desc_t and fills it
965903Ssowmini  * with either a val_desc_t found on the pd_modval list or something
975903Ssowmini  * generated on the fly.
985895Syz147064  */
995903Ssowmini typedef dladm_status_t	pd_checkf_t(struct prop_desc *pd,
1005903Ssowmini 			    datalink_id_t, char **propstrp,
1015960Ssowmini 			    uint_t cnt, val_desc_t *propval,
1025960Ssowmini 			    datalink_media_t);
1033448Sdh155122 
1045903Ssowmini typedef struct dld_public_prop_s {
1055903Ssowmini 	dld_prop_id_t	pp_id;
1065903Ssowmini 	size_t		pp_valsize;
1075903Ssowmini 	char		*pp_name;
1085903Ssowmini 	char		*pp_desc;
1095903Ssowmini } dld_public_prop_t;
1105903Ssowmini 
1115903Ssowmini static dld_ioc_prop_t *dld_buf_alloc(size_t, datalink_id_t, const char *,
1126512Ssowmini 					uint_t, dladm_status_t *);
1135903Ssowmini static dladm_status_t dld_set_prop(datalink_id_t, const char *, char **,
1145903Ssowmini 					uint_t, uint_t);
1155903Ssowmini static dladm_status_t dld_get_prop(datalink_id_t, const char *, char **,
1166512Ssowmini 					uint_t *, dladm_prop_type_t, uint_t);
1175895Syz147064 static pd_getf_t	do_get_zone, do_get_autopush, do_get_rate_mod,
1185895Syz147064 			do_get_rate_prop, do_get_channel_prop,
1195903Ssowmini 			do_get_powermode_prop, do_get_radio_prop,
1205960Ssowmini 			dld_duplex_get, dld_status_get,
1216512Ssowmini 			dld_binary_get, dld_uint32_get, dld_flowctl_get;
1225895Syz147064 static pd_setf_t	do_set_zone, do_set_autopush, do_set_rate_prop,
1235903Ssowmini 			do_set_powermode_prop, do_set_radio_prop,
1245903Ssowmini 			dld_set_public_prop;
1255903Ssowmini static pd_checkf_t	do_check_zone, do_check_autopush, do_check_rate,
1265903Ssowmini 			dld_defmtu_check;
1273448Sdh155122 
1285960Ssowmini static dladm_status_t	dld_speed_get(struct prop_desc *, datalink_id_t,
1296512Ssowmini 			char **, uint_t *, uint_t);
1305960Ssowmini 
1313448Sdh155122 typedef struct prop_desc {
1325895Syz147064 	/*
1335895Syz147064 	 * link property name
1345895Syz147064 	 */
1355895Syz147064 	char			*pd_name;
1365895Syz147064 
1375895Syz147064 	/*
1385895Syz147064 	 * default property value, can be set to { "", NULL }
1395895Syz147064 	 */
1405895Syz147064 	val_desc_t		pd_defval;
1415895Syz147064 
1425895Syz147064 	/*
1435895Syz147064 	 * list of optional property values, can be NULL.
1445895Syz147064 	 *
1455895Syz147064 	 * This is set to non-NULL if there is a list of possible property
1465895Syz147064 	 * values.  pd_optval would point to the array of possible values.
1475895Syz147064 	 */
1485895Syz147064 	val_desc_t		*pd_optval;
1495895Syz147064 
1505895Syz147064 	/*
1515895Syz147064 	 * count of the above optional property values. 0 if pd_optval is NULL.
1525895Syz147064 	 */
1535895Syz147064 	uint_t			pd_noptval;
1545895Syz147064 
1555895Syz147064 	/*
1565895Syz147064 	 * callback to set link property;
1575895Syz147064 	 * set to NULL if this property is read-only
1585895Syz147064 	 */
1595895Syz147064 	pd_setf_t		*pd_set;
1605895Syz147064 
1615895Syz147064 	/*
1625895Syz147064 	 * callback to get modifiable link property
1635895Syz147064 	 */
1645895Syz147064 	pd_getf_t		*pd_getmod;
1655895Syz147064 
1665895Syz147064 	/*
1675895Syz147064 	 * callback to get current link property
1685895Syz147064 	 */
1695895Syz147064 	pd_getf_t		*pd_get;
1705895Syz147064 
1715895Syz147064 	/*
1725895Syz147064 	 * callback to validate link property value, set to NULL if pd_optval
1735895Syz147064 	 * is not NULL. In that case, validate the value by comparing it with
1745895Syz147064 	 * the pd_optval. Return a val_desc_t array pointer if the value is
1755895Syz147064 	 * valid.
1765895Syz147064 	 */
1775895Syz147064 	pd_checkf_t		*pd_check;
1785895Syz147064 
1795895Syz147064 	uint_t			pd_flags;
1805903Ssowmini #define	PD_TEMPONLY	0x1	/* property is temporary only */
1815903Ssowmini #define	PD_CHECK_ALLOC	0x2	/* alloc vd_val as part of pd_check */
1826512Ssowmini #define	PD_EMPTY_RESET	0x4	/* Use "" to reset the link property */
1835895Syz147064 	/*
1845895Syz147064 	 * indicate link classes this property applies to.
1855895Syz147064 	 */
1865895Syz147064 	datalink_class_t	pd_class;
1875895Syz147064 
1885895Syz147064 	/*
1895895Syz147064 	 * indicate link media type this property applies to.
1905895Syz147064 	 */
1915895Syz147064 	datalink_media_t	pd_dmedia;
1923448Sdh155122 } prop_desc_t;
1933448Sdh155122 
1945903Ssowmini #define	DLD_PROPBUF_SIZE(v)	sizeof (dld_ioc_prop_t) + (v) - 1
1955903Ssowmini 
1965903Ssowmini 
1975903Ssowmini static dld_public_prop_t dld_prop[] = {
1986512Ssowmini 	{ DLD_PROP_DUPLEX,	sizeof (link_duplex_t),
1995960Ssowmini 	    "duplex",		"link duplex mode" },
2005903Ssowmini 
2015960Ssowmini 	{DLD_PROP_SPEED,	sizeof (uint64_t),
2025960Ssowmini 	    "speed",		"link speed (bps)" },
2035903Ssowmini 
2046512Ssowmini 	{ DLD_PROP_STATUS,	sizeof (link_state_t),
2055960Ssowmini 	    "state",		"link up/down" },
2065903Ssowmini 
2075903Ssowmini 	{ DLD_PROP_AUTONEG,	sizeof (uint8_t),
2085903Ssowmini 	    "adv_autoneg_cap",	"Advertised auto-negotiation" },
2095903Ssowmini 
2106512Ssowmini 	{ DLD_PROP_MTU,		sizeof (uint32_t),
2115960Ssowmini 	    "mtu",		"current link mtu" },
2125903Ssowmini 
2135903Ssowmini 	{ DLD_PROP_FLOWCTRL,	sizeof (link_flowctrl_t),
2145903Ssowmini 	    "flowctrl",		"flowcontrol" },
2155903Ssowmini 
2165903Ssowmini 	{ DLD_PROP_ADV_1000FDX_CAP, sizeof (uint8_t),
2175903Ssowmini 	    "adv_1000fdx_cap",	"Adv 1000 Mbps fdx" },
2185903Ssowmini 
2195903Ssowmini 	{ DLD_PROP_EN_1000FDX_CAP, sizeof (uint8_t),
2205903Ssowmini 	    "en_1000fdx_cap",	"Enable 1000 Mbps fdx" },
2215903Ssowmini 
2225903Ssowmini 	{ DLD_PROP_ADV_1000HDX_CAP, sizeof (uint8_t),
2235903Ssowmini 	    "adv_1000hdx_cap", "Adv 1000 Mbps hdx" },
2245903Ssowmini 
2255903Ssowmini 	{ DLD_PROP_EN_1000HDX_CAP, sizeof (uint8_t),
2265903Ssowmini 	    "en_1000hdx_cap",	"Enable 1000 Mbps hdx" },
2275903Ssowmini 
2285903Ssowmini 	{ DLD_PROP_ADV_100FDX_CAP, sizeof (uint8_t),
2295903Ssowmini 	    "adv_100fdx_cap",	"Adv 100 Mbps fdx" },
2305903Ssowmini 
2315903Ssowmini 	{ DLD_PROP_EN_100FDX_CAP, sizeof (uint8_t),
2325903Ssowmini 	    "en_100fdx_cap",	"Enable 100 Mbps fdx" },
2335903Ssowmini 
2345903Ssowmini 	{ DLD_PROP_ADV_100HDX_CAP, sizeof (uint8_t),
2355903Ssowmini 	    "adv_100hdx_cap",	"Adv 100 Mbps hdx" },
2365903Ssowmini 
2375903Ssowmini 	{ DLD_PROP_EN_100HDX_CAP, sizeof (uint8_t),
2385903Ssowmini 	    "en_100hdx_cap",	"Enable 100 Mbps hdx" },
2395903Ssowmini 
2405903Ssowmini 	{ DLD_PROP_ADV_10FDX_CAP, sizeof (uint8_t),
2415903Ssowmini 	    "adv_10fdx_cap",	"Adv 10 Mbps fdx" },
2425903Ssowmini 
2435903Ssowmini 	{ DLD_PROP_EN_10FDX_CAP, sizeof (uint8_t),
2445903Ssowmini 	    "en_10fdx_cap",	"Enable 10 Mbps fdx" },
2455903Ssowmini 
2465903Ssowmini 	{ DLD_PROP_ADV_10HDX_CAP, sizeof (uint8_t),
2475903Ssowmini 	    "adv_10hdx_cap",	"Adv 10 Mbps hdx" },
2485903Ssowmini 
2495903Ssowmini 	{ DLD_PROP_EN_10HDX_CAP, sizeof (uint8_t),
2505903Ssowmini 	    "en_10hdx_cap",	"Enable 10 Mbps hdx" },
2515903Ssowmini 
2525903Ssowmini 	{ DLD_PROP_PRIVATE, 0,
2535903Ssowmini 	    "driver-private",	"" }
2545903Ssowmini };
2555903Ssowmini 
2565903Ssowmini static  val_desc_t	link_duplex_vals[] = {
2575903Ssowmini 	{ "half", 	LINK_DUPLEX_HALF	},
2585903Ssowmini 	{ "full", 	LINK_DUPLEX_HALF	}
2595903Ssowmini };
2605903Ssowmini static  val_desc_t	link_status_vals[] = {
2615903Ssowmini 	{ "up",		LINK_STATE_UP		},
2625903Ssowmini 	{ "down",	LINK_STATE_DOWN		}
2635903Ssowmini };
2645903Ssowmini static  val_desc_t	link_01_vals[] = {
2655903Ssowmini 	{ "1",		1			},
2665903Ssowmini 	{ "0",		0			}
2675903Ssowmini };
2685903Ssowmini static  val_desc_t	link_flow_vals[] = {
2695903Ssowmini 	{ "no",		LINK_FLOWCTRL_NONE	},
2705903Ssowmini 	{ "tx",		LINK_FLOWCTRL_TX	},
2715903Ssowmini 	{ "rx",		LINK_FLOWCTRL_RX	},
2725903Ssowmini 	{ "bi",		LINK_FLOWCTRL_BI	}
2735903Ssowmini };
2745903Ssowmini 
2755903Ssowmini #define	VALCNT(vals)    (sizeof ((vals)) / sizeof (val_desc_t))
2765903Ssowmini 
2775895Syz147064 static val_desc_t	dladm_wlan_radio_vals[] = {
2785895Syz147064 	{ "on",		DLADM_WLAN_RADIO_ON	},
2795895Syz147064 	{ "off",	DLADM_WLAN_RADIO_OFF	}
2805895Syz147064 };
2815895Syz147064 
2825895Syz147064 static val_desc_t	dladm_wlan_powermode_vals[] = {
2835895Syz147064 	{ "off",	DLADM_WLAN_PM_OFF	},
2845895Syz147064 	{ "fast",	DLADM_WLAN_PM_FAST	},
2855895Syz147064 	{ "max",	DLADM_WLAN_PM_MAX	}
2865895Syz147064 };
2875895Syz147064 
2883448Sdh155122 static prop_desc_t	prop_table[] = {
2895895Syz147064 
2905903Ssowmini 	{ "channel",	{ NULL, 0 },
2915903Ssowmini 	    NULL, 0, NULL, NULL,
2925895Syz147064 	    do_get_channel_prop, NULL, 0,
2935903Ssowmini 	    DATALINK_CLASS_PHYS, DL_WIFI },
2945895Syz147064 
2955895Syz147064 	{ "powermode",	{ "off", DLADM_WLAN_PM_OFF },
2965895Syz147064 	    dladm_wlan_powermode_vals, VALCNT(dladm_wlan_powermode_vals),
2975895Syz147064 	    do_set_powermode_prop, NULL,
2985895Syz147064 	    do_get_powermode_prop, NULL, 0,
2995903Ssowmini 	    DATALINK_CLASS_PHYS, DL_WIFI },
3005895Syz147064 
3015895Syz147064 	{ "radio",	{ "on", DLADM_WLAN_RADIO_ON },
3025895Syz147064 	    dladm_wlan_radio_vals, VALCNT(dladm_wlan_radio_vals),
3035895Syz147064 	    do_set_radio_prop, NULL,
3045895Syz147064 	    do_get_radio_prop, NULL, 0,
3055903Ssowmini 	    DATALINK_CLASS_PHYS, DL_WIFI },
3065895Syz147064 
3075895Syz147064 	{ "speed",	{ "", 0 }, NULL, 0,
3085895Syz147064 	    do_set_rate_prop, do_get_rate_mod,
3095895Syz147064 	    do_get_rate_prop, do_check_rate, 0,
3105960Ssowmini 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE },
3115895Syz147064 
3126512Ssowmini 	{ "autopush",	{ "", 0 }, NULL, 0,
3135895Syz147064 	    do_set_autopush, NULL,
3146512Ssowmini 	    do_get_autopush, do_check_autopush, PD_CHECK_ALLOC|PD_EMPTY_RESET,
3155903Ssowmini 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
3165895Syz147064 
3176512Ssowmini 	{ "zone",	{ "", 0 }, NULL, 0,
3183448Sdh155122 	    do_set_zone, NULL,
3196512Ssowmini 	    do_get_zone, do_check_zone, PD_TEMPONLY|PD_EMPTY_RESET,
3205903Ssowmini 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
3215903Ssowmini 
3226512Ssowmini 	{ "duplex", { "", 0 },
3235903Ssowmini 	    link_duplex_vals, VALCNT(link_duplex_vals),
3245903Ssowmini 	    NULL, NULL, dld_duplex_get, NULL,
3255903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3265903Ssowmini 
3275960Ssowmini 	{ "state", { "up", LINK_STATE_UP },
3285903Ssowmini 	    link_status_vals, VALCNT(link_status_vals),
3295903Ssowmini 	    NULL, NULL, dld_status_get, NULL,
3306512Ssowmini 	    0, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
3315903Ssowmini 
3325903Ssowmini 	{ "adv_autoneg_cap", { "1", 1 },
3335903Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
3345903Ssowmini 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
3355903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3365903Ssowmini 
3376512Ssowmini 	{ "mtu", { "", 0 }, NULL, 0,
3386512Ssowmini 	    dld_set_public_prop, NULL, dld_uint32_get,
3396512Ssowmini 	    dld_defmtu_check, 0, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
3405903Ssowmini 
3416512Ssowmini 	{ "flowctrl", { "", 0 },
3425903Ssowmini 	    link_flow_vals, VALCNT(link_flow_vals),
3435903Ssowmini 	    dld_set_public_prop, NULL, dld_flowctl_get, NULL,
3445903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3455903Ssowmini 
3466512Ssowmini 	{ "adv_1000fdx_cap", { "", 0 },
3476512Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
3485903Ssowmini 	    NULL, NULL, dld_binary_get, NULL,
3495903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3505903Ssowmini 
3516512Ssowmini 	{ "en_1000fdx_cap", { "", 0 },
3525903Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
3535903Ssowmini 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
3545903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3555903Ssowmini 
3566512Ssowmini 	{ "adv_1000hdx_cap", { "", 0 },
3575903Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
3585903Ssowmini 	    NULL, NULL, dld_binary_get, NULL,
3595903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3605903Ssowmini 
3616512Ssowmini 	{ "en_1000hdx_cap", { "", 0 },
3625903Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
3635903Ssowmini 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
3645903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3655903Ssowmini 
3666512Ssowmini 	{ "adv_100fdx_cap", { "", 0 },
3675903Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
3685903Ssowmini 	    NULL, NULL, dld_binary_get, NULL,
3695903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3705903Ssowmini 
3716512Ssowmini 	{ "en_100fdx_cap", { "", 0 },
3725903Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
3735903Ssowmini 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
3745903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3755903Ssowmini 
3766512Ssowmini 	{ "adv_100hdx_cap", { "", 0 },
3775903Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
3785903Ssowmini 	    NULL, NULL, dld_binary_get, NULL,
3795903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3805903Ssowmini 
3816512Ssowmini 	{ "en_100hdx_cap", { "", 0 },
3825903Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
3835903Ssowmini 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
3845903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3855903Ssowmini 
3866512Ssowmini 	{ "adv_10fdx_cap", { "", 0 },
3875903Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
3885903Ssowmini 	    NULL, NULL, dld_binary_get, NULL,
3895903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3905903Ssowmini 
3916512Ssowmini 	{ "en_10fdx_cap", { "", 0 },
3925903Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
3935903Ssowmini 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
3945903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
3955903Ssowmini 
3966512Ssowmini 	{ "adv_10hdx_cap", { "", 0 },
3975903Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
3985903Ssowmini 	    NULL, NULL, dld_binary_get, NULL,
3995903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
4005903Ssowmini 
4016512Ssowmini 	{ "en_10hdx_cap", { "", 0 },
4025903Ssowmini 	    link_01_vals, VALCNT(link_01_vals),
4035903Ssowmini 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
4045903Ssowmini 	    0, DATALINK_CLASS_PHYS, DL_ETHER }
4055903Ssowmini 
4063448Sdh155122 };
4073448Sdh155122 
4085895Syz147064 #define	DLADM_MAX_PROPS	(sizeof (prop_table) / sizeof (prop_desc_t))
4095895Syz147064 
4105895Syz147064 static dladm_status_t	i_dladm_set_linkprop_db(datalink_id_t, const char *,
4115895Syz147064 			    char **, uint_t);
4125895Syz147064 static dladm_status_t	i_dladm_get_linkprop_db(datalink_id_t, const char *,
4135895Syz147064 			    char **, uint_t *);
4145895Syz147064 static dladm_status_t	i_dladm_set_single_prop(datalink_id_t, datalink_class_t,
4155895Syz147064 			    uint32_t, prop_desc_t *, char **, uint_t, uint_t);
4165895Syz147064 static dladm_status_t	i_dladm_set_linkprop(datalink_id_t, const char *,
4175895Syz147064 			    char **, uint_t, uint_t);
4186512Ssowmini static dladm_status_t	i_dladm_getset_defval(prop_desc_t *, datalink_id_t,
4196512Ssowmini 			    datalink_media_t, uint_t);
4205895Syz147064 /*
4215895Syz147064  * Unfortunately, MAX_SCAN_SUPPORT_RATES is too small to allow all
4225895Syz147064  * rates to be retrieved. However, we cannot increase it at this
4235895Syz147064  * time because it will break binary compatibility with unbundled
4245895Syz147064  * WiFi drivers and utilities. So for now we define an additional
4255895Syz147064  * constant, MAX_SUPPORT_RATES, to allow all rates to be retrieved.
4265895Syz147064  */
4275895Syz147064 #define	MAX_SUPPORT_RATES	64
4285895Syz147064 
4295895Syz147064 #define	AP_ANCHOR	"[anchor]"
4305895Syz147064 #define	AP_DELIMITER	'.'
4315895Syz147064 
4325895Syz147064 static dladm_status_t
4335895Syz147064 do_check_prop(prop_desc_t *pdp, char **prop_val, uint_t val_cnt,
4345895Syz147064     val_desc_t *vdp)
4355895Syz147064 {
4365895Syz147064 	int		i, j;
4375895Syz147064 	dladm_status_t	status = DLADM_STATUS_OK;
4383147Sxc151355 
4395895Syz147064 	for (j = 0; j < val_cnt; j++) {
4405895Syz147064 		for (i = 0; i < pdp->pd_noptval; i++) {
4415895Syz147064 			if (strcasecmp(*prop_val,
4425895Syz147064 			    pdp->pd_optval[i].vd_name) == 0) {
4435895Syz147064 				break;
4445895Syz147064 			}
4455895Syz147064 		}
4465895Syz147064 		if (i == pdp->pd_noptval) {
4475895Syz147064 			status = DLADM_STATUS_BADVAL;
4485895Syz147064 			goto done;
4495895Syz147064 		}
4505895Syz147064 		(void) memcpy(vdp + j, &pdp->pd_optval[i], sizeof (val_desc_t));
4515895Syz147064 	}
4525895Syz147064 
4535895Syz147064 done:
4545895Syz147064 	return (status);
4555895Syz147064 }
4565895Syz147064 
4575895Syz147064 static dladm_status_t
4585895Syz147064 i_dladm_set_single_prop(datalink_id_t linkid, datalink_class_t class,
4595895Syz147064     uint32_t media, prop_desc_t *pdp, char **prop_val, uint_t val_cnt,
4605895Syz147064     uint_t flags)
4613147Sxc151355 {
4625895Syz147064 	dladm_status_t	status = DLADM_STATUS_OK;
4635895Syz147064 	val_desc_t	*vdp = NULL;
4645895Syz147064 	boolean_t	needfree = B_FALSE;
4655895Syz147064 	uint_t		cnt, i;
4663147Sxc151355 
4675895Syz147064 	if (!(pdp->pd_class & class))
4685895Syz147064 		return (DLADM_STATUS_BADARG);
4695895Syz147064 
4705895Syz147064 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
4713147Sxc151355 		return (DLADM_STATUS_BADARG);
4723147Sxc151355 
4735895Syz147064 	if ((flags & DLADM_OPT_PERSIST) && (pdp->pd_flags & PD_TEMPONLY))
4745895Syz147064 		return (DLADM_STATUS_TEMPONLY);
4755895Syz147064 
4765895Syz147064 	if (!(flags & DLADM_OPT_ACTIVE))
4775895Syz147064 		return (DLADM_STATUS_OK);
4785895Syz147064 
4795895Syz147064 	if (pdp->pd_set == NULL)
4805895Syz147064 		return (DLADM_STATUS_PROPRDONLY);
4813448Sdh155122 
4825903Ssowmini 	if (pdp->pd_flags & PD_CHECK_ALLOC)
4835903Ssowmini 		needfree = B_TRUE;
4845903Ssowmini 	else
4855903Ssowmini 		needfree = B_FALSE;
4865895Syz147064 	if (prop_val != NULL) {
4875895Syz147064 		vdp = malloc(sizeof (val_desc_t) * val_cnt);
4885895Syz147064 		if (vdp == NULL)
4895895Syz147064 			return (DLADM_STATUS_NOMEM);
4905895Syz147064 
4915903Ssowmini 
4925895Syz147064 		if (pdp->pd_check != NULL) {
4935903Ssowmini 			status = pdp->pd_check(pdp, linkid, prop_val, val_cnt,
4945960Ssowmini 			    vdp, media);
4955895Syz147064 		} else if (pdp->pd_optval != NULL) {
4965895Syz147064 			status = do_check_prop(pdp, prop_val, val_cnt, vdp);
4975895Syz147064 		} else {
4983448Sdh155122 			status = DLADM_STATUS_BADARG;
4993147Sxc151355 		}
5005895Syz147064 
5013147Sxc151355 		if (status != DLADM_STATUS_OK)
5025895Syz147064 			goto done;
5035895Syz147064 
5045895Syz147064 		cnt = val_cnt;
5055895Syz147064 	} else {
5065895Syz147064 		if (pdp->pd_defval.vd_name == NULL)
5075895Syz147064 			return (DLADM_STATUS_NOTSUP);
5085895Syz147064 
5096512Ssowmini 		if ((pdp->pd_flags & PD_EMPTY_RESET) != 0 ||
5106512Ssowmini 		    strlen(pdp->pd_defval.vd_name) > 0) {
5116512Ssowmini 			if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
5126512Ssowmini 				return (DLADM_STATUS_NOMEM);
5136512Ssowmini 			(void) memcpy(vdp, &pdp->pd_defval,
5146512Ssowmini 			    sizeof (val_desc_t));
5156512Ssowmini 		} else {
5166512Ssowmini 			status = i_dladm_getset_defval(pdp, linkid,
5176512Ssowmini 			    media, flags);
5186512Ssowmini 			return (status);
5196512Ssowmini 		}
5205895Syz147064 		cnt = 1;
5215895Syz147064 	}
5225960Ssowmini 	status = pdp->pd_set(pdp, linkid, vdp, cnt, flags, media);
5235895Syz147064 	if (needfree) {
5245895Syz147064 		for (i = 0; i < cnt; i++)
5255903Ssowmini 			free((void *)((val_desc_t *)vdp + i)->vd_val);
5263147Sxc151355 	}
5275895Syz147064 done:
5285895Syz147064 	free(vdp);
5295895Syz147064 	return (status);
5305895Syz147064 }
5315895Syz147064 
5325895Syz147064 static dladm_status_t
5335895Syz147064 i_dladm_set_linkprop(datalink_id_t linkid, const char *prop_name,
5345895Syz147064     char **prop_val, uint_t val_cnt, uint_t flags)
5355895Syz147064 {
5365895Syz147064 	int			i;
5375895Syz147064 	boolean_t		found = B_FALSE;
5385895Syz147064 	datalink_class_t	class;
5395895Syz147064 	uint32_t		media;
5405895Syz147064 	dladm_status_t		status = DLADM_STATUS_OK;
5415895Syz147064 
5425895Syz147064 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
5435895Syz147064 	if (status != DLADM_STATUS_OK)
5445895Syz147064 		return (status);
5455895Syz147064 
5465895Syz147064 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
5475895Syz147064 		prop_desc_t	*pdp = &prop_table[i];
5485895Syz147064 		dladm_status_t	s;
5495895Syz147064 
5505895Syz147064 		if (prop_name != NULL &&
5515895Syz147064 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
5525895Syz147064 			continue;
5535895Syz147064 
5545895Syz147064 		found = B_TRUE;
5555895Syz147064 		s = i_dladm_set_single_prop(linkid, class, media, pdp, prop_val,
5565895Syz147064 		    val_cnt, flags);
5573448Sdh155122 
5585895Syz147064 		if (prop_name != NULL) {
5595895Syz147064 			status = s;
5605895Syz147064 			break;
5615895Syz147064 		} else {
5625895Syz147064 			if (s != DLADM_STATUS_OK &&
5635895Syz147064 			    s != DLADM_STATUS_NOTSUP)
5645895Syz147064 				status = s;
5655895Syz147064 		}
5665895Syz147064 	}
5675903Ssowmini 	if (!found) {
5685903Ssowmini 		if (prop_name[0] == '_') {
5695903Ssowmini 			/* other private properties */
5705903Ssowmini 			status = dld_set_prop(linkid, prop_name, prop_val,
5715903Ssowmini 			    val_cnt, flags);
5725903Ssowmini 		} else  {
5735903Ssowmini 			status = DLADM_STATUS_NOTFOUND;
5745903Ssowmini 		}
5755903Ssowmini 	}
5765895Syz147064 
5775895Syz147064 	return (status);
5785895Syz147064 }
5795895Syz147064 
5805895Syz147064 /*
5815895Syz147064  * Set/reset link property for specific link
5825895Syz147064  */
5835895Syz147064 dladm_status_t
5845895Syz147064 dladm_set_linkprop(datalink_id_t linkid, const char *prop_name, char **prop_val,
5855895Syz147064     uint_t val_cnt, uint_t flags)
5865895Syz147064 {
5875895Syz147064 	dladm_status_t	status = DLADM_STATUS_OK;
5885895Syz147064 
5895895Syz147064 	if ((linkid == DATALINK_INVALID_LINKID) || (flags == 0) ||
5905895Syz147064 	    (prop_val == NULL && val_cnt > 0) ||
5915895Syz147064 	    (prop_val != NULL && val_cnt == 0) ||
5925895Syz147064 	    (prop_name == NULL && prop_val != NULL)) {
5935895Syz147064 		return (DLADM_STATUS_BADARG);
5945895Syz147064 	}
5955895Syz147064 
5965895Syz147064 	status = i_dladm_set_linkprop(linkid, prop_name, prop_val,
5975895Syz147064 	    val_cnt, flags);
5985895Syz147064 	if (status != DLADM_STATUS_OK)
5995895Syz147064 		return (status);
6005895Syz147064 
6015895Syz147064 	if (flags & DLADM_OPT_PERSIST) {
6025895Syz147064 		status = i_dladm_set_linkprop_db(linkid, prop_name,
6033147Sxc151355 		    prop_val, val_cnt);
6043147Sxc151355 	}
6053147Sxc151355 	return (status);
6063147Sxc151355 }
6073147Sxc151355 
6085895Syz147064 /*
6095895Syz147064  * Walk link properties of the given specific link.
6105895Syz147064  */
6113147Sxc151355 dladm_status_t
6125895Syz147064 dladm_walk_linkprop(datalink_id_t linkid, void *arg,
6135895Syz147064     int (*func)(datalink_id_t, const char *, void *))
6143147Sxc151355 {
6155895Syz147064 	dladm_status_t		status;
6165895Syz147064 	datalink_class_t	class;
6175895Syz147064 	uint_t			media;
6185895Syz147064 	int			i;
6195895Syz147064 
6205895Syz147064 	if (linkid == DATALINK_INVALID_LINKID || func == NULL)
6215895Syz147064 		return (DLADM_STATUS_BADARG);
6225895Syz147064 
6235895Syz147064 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
6245895Syz147064 	if (status != DLADM_STATUS_OK)
6255895Syz147064 		return (status);
6265895Syz147064 
6275895Syz147064 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
6285895Syz147064 		if (!(prop_table[i].pd_class & class))
6295895Syz147064 			continue;
6305895Syz147064 
6315895Syz147064 		if (!DATALINK_MEDIA_ACCEPTED(prop_table[i].pd_dmedia, media))
6325895Syz147064 			continue;
6335895Syz147064 
6345895Syz147064 		if (func(linkid, prop_table[i].pd_name, arg) ==
6355895Syz147064 		    DLADM_WALK_TERMINATE) {
6365895Syz147064 			break;
6375895Syz147064 		}
6385895Syz147064 	}
6395895Syz147064 
6405895Syz147064 	return (DLADM_STATUS_OK);
6415895Syz147064 }
6423448Sdh155122 
6435895Syz147064 /*
6445895Syz147064  * Get linkprop of the given specific link.
6455895Syz147064  */
6465895Syz147064 dladm_status_t
6475895Syz147064 dladm_get_linkprop(datalink_id_t linkid, dladm_prop_type_t type,
6485895Syz147064     const char *prop_name, char **prop_val, uint_t *val_cntp)
6495895Syz147064 {
6505895Syz147064 	dladm_status_t		status = DLADM_STATUS_OK;
6515895Syz147064 	datalink_class_t	class;
6525895Syz147064 	uint_t			media;
6535895Syz147064 	prop_desc_t		*pdp;
6546512Ssowmini 	uint_t			cnt, dld_flags = 0;
6555895Syz147064 	int			i;
6565895Syz147064 
6576512Ssowmini 	if (type == DLADM_PROP_VAL_DEFAULT)
6586512Ssowmini 		dld_flags = DLD_DEFAULT;
6596512Ssowmini 
6605895Syz147064 	if (linkid == DATALINK_INVALID_LINKID || prop_name == NULL ||
6615895Syz147064 	    prop_val == NULL || val_cntp == NULL || *val_cntp == 0)
6625895Syz147064 		return (DLADM_STATUS_BADARG);
6635895Syz147064 
6645895Syz147064 	for (i = 0; i < DLADM_MAX_PROPS; i++)
6655895Syz147064 		if (strcasecmp(prop_name, prop_table[i].pd_name) == 0)
6665895Syz147064 			break;
6675895Syz147064 
6685903Ssowmini 	if (i == DLADM_MAX_PROPS) {
6695903Ssowmini 		if (prop_name[0] == '_') {
6705903Ssowmini 			/*
6715903Ssowmini 			 * private property.
6725903Ssowmini 			 */
6735903Ssowmini 			return (dld_get_prop(linkid, prop_name,
6746512Ssowmini 			    prop_val, val_cntp, type, dld_flags));
6755903Ssowmini 		} else {
6765903Ssowmini 			return (DLADM_STATUS_NOTFOUND);
6775903Ssowmini 		}
6785903Ssowmini 	}
6795895Syz147064 
6805895Syz147064 	pdp = &prop_table[i];
6815895Syz147064 
6825895Syz147064 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
6835895Syz147064 	if (status != DLADM_STATUS_OK)
6845895Syz147064 		return (status);
6855895Syz147064 
6865895Syz147064 	if (!(pdp->pd_class & class))
6875895Syz147064 		return (DLADM_STATUS_BADARG);
6885895Syz147064 
6895895Syz147064 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
6903147Sxc151355 		return (DLADM_STATUS_BADARG);
6913147Sxc151355 
6925895Syz147064 	switch (type) {
6935895Syz147064 	case DLADM_PROP_VAL_CURRENT:
6946512Ssowmini 		status = pdp->pd_get(pdp, linkid, prop_val, val_cntp, media,
6956512Ssowmini 		    dld_flags);
6965895Syz147064 		break;
6975895Syz147064 
6985895Syz147064 	case DLADM_PROP_VAL_DEFAULT:
699*6768Sar224390 		/*
700*6768Sar224390 		 * If defaults are not defined for the property,
701*6768Sar224390 		 * pd_defval.vd_name should be null. If the driver
702*6768Sar224390 		 * has to be contacted for the value, vd_name should
703*6768Sar224390 		 * be the empty string (""). Otherwise, dladm will
704*6768Sar224390 		 * just print whatever is in the table.
705*6768Sar224390 		 */
7065895Syz147064 		if (pdp->pd_defval.vd_name == NULL) {
7075895Syz147064 			status = DLADM_STATUS_NOTSUP;
7085895Syz147064 			break;
7095895Syz147064 		}
7106512Ssowmini 
7116512Ssowmini 		if (strlen(pdp->pd_defval.vd_name) == 0) {
7126512Ssowmini 			status = pdp->pd_get(pdp, linkid, prop_val, val_cntp,
7136512Ssowmini 			    media, dld_flags);
7146512Ssowmini 		} else {
7156512Ssowmini 			(void) strcpy(*prop_val, pdp->pd_defval.vd_name);
7166512Ssowmini 		}
7175895Syz147064 		*val_cntp = 1;
7185895Syz147064 		break;
7193448Sdh155122 
7205895Syz147064 	case DLADM_PROP_VAL_MODIFIABLE:
7215895Syz147064 		if (pdp->pd_getmod != NULL) {
7225903Ssowmini 			status = pdp->pd_getmod(pdp, linkid, prop_val,
7236512Ssowmini 			    val_cntp, media, dld_flags);
7245895Syz147064 			break;
7255895Syz147064 		}
7265895Syz147064 		cnt = pdp->pd_noptval;
7275895Syz147064 		if (cnt == 0) {
7285895Syz147064 			status = DLADM_STATUS_NOTSUP;
7295895Syz147064 		} else if (cnt > *val_cntp) {
7305895Syz147064 			status = DLADM_STATUS_TOOSMALL;
7315895Syz147064 		} else {
7325895Syz147064 			for (i = 0; i < cnt; i++) {
7335895Syz147064 				(void) strcpy(prop_val[i],
7345895Syz147064 				    pdp->pd_optval[i].vd_name);
7355895Syz147064 			}
7365895Syz147064 			*val_cntp = cnt;
7375895Syz147064 		}
7385895Syz147064 		break;
7395895Syz147064 	case DLADM_PROP_VAL_PERSISTENT:
7405895Syz147064 		if (pdp->pd_flags & PD_TEMPONLY)
7415895Syz147064 			return (DLADM_STATUS_TEMPONLY);
7425895Syz147064 		status = i_dladm_get_linkprop_db(linkid, prop_name,
7435895Syz147064 		    prop_val, val_cntp);
7445895Syz147064 		break;
7455895Syz147064 	default:
7465895Syz147064 		status = DLADM_STATUS_BADARG;
7475895Syz147064 		break;
7483147Sxc151355 	}
7493448Sdh155122 
7505895Syz147064 	return (status);
7515895Syz147064 }
7525895Syz147064 
7535895Syz147064 /*ARGSUSED*/
7545895Syz147064 static int
7555895Syz147064 i_dladm_init_one_prop(datalink_id_t linkid, const char *prop_name, void *arg)
7565895Syz147064 {
7575895Syz147064 	char	*buf, **propvals;
7585895Syz147064 	uint_t	i, valcnt = DLADM_MAX_PROP_VALCNT;
7595895Syz147064 
7605895Syz147064 	if ((buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
7615895Syz147064 	    DLADM_MAX_PROP_VALCNT)) == NULL) {
7625895Syz147064 		return (DLADM_WALK_CONTINUE);
7635895Syz147064 	}
7645895Syz147064 
7655895Syz147064 	propvals = (char **)(void *)buf;
7665895Syz147064 	for (i = 0; i < valcnt; i++) {
7675895Syz147064 		propvals[i] = buf +
7685895Syz147064 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
7695895Syz147064 		    i * DLADM_PROP_VAL_MAX;
7705895Syz147064 	}
7715895Syz147064 
7725895Syz147064 	if (dladm_get_linkprop(linkid, DLADM_PROP_VAL_PERSISTENT, prop_name,
7735895Syz147064 	    propvals, &valcnt) != DLADM_STATUS_OK) {
7745895Syz147064 		goto done;
7755895Syz147064 	}
7765895Syz147064 
7775895Syz147064 	(void) dladm_set_linkprop(linkid, prop_name, propvals, valcnt,
7785895Syz147064 	    DLADM_OPT_ACTIVE);
7795895Syz147064 
7805895Syz147064 done:
7815895Syz147064 	if (buf != NULL)
7825895Syz147064 		free(buf);
7835895Syz147064 
7845895Syz147064 	return (DLADM_WALK_CONTINUE);
7855895Syz147064 }
7865895Syz147064 
7875895Syz147064 /*ARGSUSED*/
7885895Syz147064 static int
7895895Syz147064 i_dladm_init_linkprop(datalink_id_t linkid, void *arg)
7905895Syz147064 {
7915895Syz147064 	(void) dladm_init_linkprop(linkid);
7925895Syz147064 	return (DLADM_WALK_CONTINUE);
7935895Syz147064 }
7945895Syz147064 
7955895Syz147064 dladm_status_t
7965895Syz147064 dladm_init_linkprop(datalink_id_t linkid)
7975895Syz147064 {
7985895Syz147064 	if (linkid == DATALINK_ALL_LINKID) {
7995895Syz147064 		(void) dladm_walk_datalink_id(i_dladm_init_linkprop, NULL,
8005895Syz147064 		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
8015895Syz147064 		    DLADM_OPT_PERSIST);
8025895Syz147064 	} else {
8035895Syz147064 		(void) dladm_walk_linkprop(linkid, NULL, i_dladm_init_one_prop);
8043448Sdh155122 	}
8053448Sdh155122 	return (DLADM_STATUS_OK);
8063147Sxc151355 }
8073147Sxc151355 
8085903Ssowmini /* ARGSUSED */
8095895Syz147064 static dladm_status_t
8105903Ssowmini do_get_zone(struct prop_desc *pd, datalink_id_t linkid,
8116512Ssowmini     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags)
8123147Sxc151355 {
8135895Syz147064 	char		zone_name[ZONENAME_MAX];
8145895Syz147064 	zoneid_t	zid;
8155895Syz147064 	dladm_status_t	status;
8163147Sxc151355 
8176512Ssowmini 	if (flags != 0)
8186512Ssowmini 		return (DLADM_STATUS_NOTSUP);
8196512Ssowmini 
8205895Syz147064 	status = dladm_getzid(linkid, &zid);
8215895Syz147064 	if (status != DLADM_STATUS_OK)
8223448Sdh155122 		return (status);
8233448Sdh155122 
8245895Syz147064 	*val_cnt = 1;
8255895Syz147064 	if (zid != GLOBAL_ZONEID) {
8265895Syz147064 		if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
8275895Syz147064 			return (dladm_errno2status(errno));
8283147Sxc151355 
8295895Syz147064 		(void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX);
8303147Sxc151355 	} else {
8315895Syz147064 		*prop_val[0] = '\0';
8323147Sxc151355 	}
8333147Sxc151355 
8343448Sdh155122 	return (DLADM_STATUS_OK);
8353448Sdh155122 }
8363448Sdh155122 
8373448Sdh155122 typedef int (*zone_get_devroot_t)(char *, char *, size_t);
8383448Sdh155122 
8393448Sdh155122 static int
8403448Sdh155122 i_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen)
8413448Sdh155122 {
8423448Sdh155122 	char			root[MAXPATHLEN];
8433448Sdh155122 	zone_get_devroot_t	real_zone_get_devroot;
8443448Sdh155122 	void			*dlhandle;
8453448Sdh155122 	void			*sym;
8463448Sdh155122 	int			ret;
8473448Sdh155122 
8483448Sdh155122 	if ((dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY)) == NULL)
8493448Sdh155122 		return (-1);
8503448Sdh155122 
8513448Sdh155122 	if ((sym = dlsym(dlhandle, "zone_get_devroot")) == NULL) {
8523448Sdh155122 		(void) dlclose(dlhandle);
8533448Sdh155122 		return (-1);
8543448Sdh155122 	}
8553448Sdh155122 
8563448Sdh155122 	real_zone_get_devroot = (zone_get_devroot_t)sym;
8573448Sdh155122 
8583448Sdh155122 	if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0)
8593448Sdh155122 		(void) snprintf(dev, devlen, "%s%s", root, "/dev");
8603448Sdh155122 	(void) dlclose(dlhandle);
8613448Sdh155122 	return (ret);
8623448Sdh155122 }
8633448Sdh155122 
8643448Sdh155122 static dladm_status_t
8655895Syz147064 i_dladm_update_deventry(zoneid_t zid, datalink_id_t linkid, boolean_t add)
8663448Sdh155122 {
8673448Sdh155122 	char		path[MAXPATHLEN];
8685895Syz147064 	char		name[MAXLINKNAMELEN];
8693448Sdh155122 	di_prof_t	prof = NULL;
8703448Sdh155122 	char		zone_name[ZONENAME_MAX];
8713448Sdh155122 	dladm_status_t	status;
8725895Syz147064 	int		ret;
8733448Sdh155122 
8743448Sdh155122 	if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
8753448Sdh155122 		return (dladm_errno2status(errno));
8763448Sdh155122 	if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
8773448Sdh155122 		return (dladm_errno2status(errno));
8783448Sdh155122 	if (di_prof_init(path, &prof) != 0)
8793448Sdh155122 		return (dladm_errno2status(errno));
8803448Sdh155122 
8815895Syz147064 	status = dladm_linkid2legacyname(linkid, name, MAXLINKNAMELEN);
8825895Syz147064 	if (status != DLADM_STATUS_OK)
8835895Syz147064 		goto cleanup;
8845895Syz147064 
8855895Syz147064 	if (add)
8865895Syz147064 		ret = di_prof_add_dev(prof, name);
8875895Syz147064 	else
8885895Syz147064 		ret = di_prof_add_exclude(prof, name);
8895895Syz147064 
8905895Syz147064 	if (ret != 0) {
8913448Sdh155122 		status = dladm_errno2status(errno);
8923448Sdh155122 		goto cleanup;
8933448Sdh155122 	}
8943448Sdh155122 
8953448Sdh155122 	if (di_prof_commit(prof) != 0)
8963448Sdh155122 		status = dladm_errno2status(errno);
8973448Sdh155122 cleanup:
8983448Sdh155122 	if (prof)
8993448Sdh155122 		di_prof_fini(prof);
9003448Sdh155122 
9013448Sdh155122 	return (status);
9023448Sdh155122 }
9033448Sdh155122 
9045903Ssowmini /* ARGSUSED */
9053448Sdh155122 static dladm_status_t
9065960Ssowmini do_set_zone(prop_desc_t *pd, datalink_id_t linkid, val_desc_t *vdp,
9075960Ssowmini     uint_t val_cnt, uint_t flags, datalink_media_t media)
9083448Sdh155122 {
9093448Sdh155122 	dladm_status_t	status;
9103448Sdh155122 	zoneid_t	zid_old, zid_new;
9115895Syz147064 	char		link[MAXLINKNAMELEN];
9123448Sdh155122 
9133448Sdh155122 	if (val_cnt != 1)
9143448Sdh155122 		return (DLADM_STATUS_BADVALCNT);
9153448Sdh155122 
9165895Syz147064 	status = dladm_getzid(linkid, &zid_old);
9173448Sdh155122 	if (status != DLADM_STATUS_OK)
9183448Sdh155122 		return (status);
9193448Sdh155122 
9203448Sdh155122 	/* Do nothing if setting to current value */
9215895Syz147064 	zid_new = vdp->vd_val;
9223448Sdh155122 	if (zid_new == zid_old)
9233448Sdh155122 		return (DLADM_STATUS_OK);
9243448Sdh155122 
9255895Syz147064 	if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL,
9265895Syz147064 	    link, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
9275895Syz147064 		return (status);
9285895Syz147064 	}
9295895Syz147064 
9305895Syz147064 	if (zid_new != GLOBAL_ZONEID) {
9315895Syz147064 		/*
9325895Syz147064 		 * If the new zoneid is the global zone, we could destroy
9335895Syz147064 		 * the link (in the case of an implicitly-created VLAN) as a
9345895Syz147064 		 * result of the dladm_setzid() operation. In that case,
9355895Syz147064 		 * we defer the operation to the end of this function to avoid
9365895Syz147064 		 * recreating the VLAN and getting a different linkid during
9375895Syz147064 		 * the rollback if other operation fails.
9385895Syz147064 		 *
9395895Syz147064 		 * Otherwise, dladm_setzid() will hold a reference to the
9405895Syz147064 		 * link and prevent a link renaming, so we need to do it
9415895Syz147064 		 * before other operations.
9425895Syz147064 		 */
9435895Syz147064 		status = dladm_setzid(link, zid_new);
9445895Syz147064 		if (status != DLADM_STATUS_OK)
9455895Syz147064 			return (status);
9465895Syz147064 	}
9475895Syz147064 
9483448Sdh155122 	if (zid_old != GLOBAL_ZONEID) {
9495895Syz147064 		if (zone_remove_datalink(zid_old, link) != 0 &&
9503448Sdh155122 		    errno != ENXIO) {
9513448Sdh155122 			status = dladm_errno2status(errno);
9523448Sdh155122 			goto rollback1;
9533448Sdh155122 		}
9543448Sdh155122 
9555895Syz147064 		/*
9565895Syz147064 		 * It is okay to fail to update the /dev entry (some
9575895Syz147064 		 * vanity-named links do not have a /dev entry).
9585895Syz147064 		 */
9595895Syz147064 		(void) i_dladm_update_deventry(zid_old, linkid, B_FALSE);
9605895Syz147064 	}
9615895Syz147064 
9625895Syz147064 	if (zid_new != GLOBAL_ZONEID) {
9635895Syz147064 		if (zone_add_datalink(zid_new, link) != 0) {
9645895Syz147064 			status = dladm_errno2status(errno);
9655895Syz147064 			goto rollback2;
9665895Syz147064 		}
9675895Syz147064 
9685895Syz147064 		(void) i_dladm_update_deventry(zid_new, linkid, B_TRUE);
9695895Syz147064 	} else {
9705895Syz147064 		status = dladm_setzid(link, zid_new);
9713448Sdh155122 		if (status != DLADM_STATUS_OK)
9723448Sdh155122 			goto rollback2;
9733448Sdh155122 	}
9743448Sdh155122 
9753448Sdh155122 	return (DLADM_STATUS_OK);
9763448Sdh155122 
9773448Sdh155122 rollback2:
9783448Sdh155122 	if (zid_old != GLOBAL_ZONEID)
9795895Syz147064 		(void) i_dladm_update_deventry(zid_old, linkid, B_TRUE);
9805895Syz147064 	if (zid_old != GLOBAL_ZONEID)
9815895Syz147064 		(void) zone_add_datalink(zid_old, link);
9823448Sdh155122 rollback1:
9835895Syz147064 	if (zid_new != GLOBAL_ZONEID)
9845895Syz147064 		(void) dladm_setzid(link, zid_old);
9853448Sdh155122 	return (status);
9863448Sdh155122 }
9873448Sdh155122 
9883448Sdh155122 /* ARGSUSED */
9893448Sdh155122 static dladm_status_t
9905903Ssowmini do_check_zone(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
9915960Ssowmini     uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
9923448Sdh155122 {
9935895Syz147064 	zoneid_t	zid;
9943448Sdh155122 
9953448Sdh155122 	if (val_cnt != 1)
9963448Sdh155122 		return (DLADM_STATUS_BADVALCNT);
9973448Sdh155122 
9983448Sdh155122 	if ((zid = getzoneidbyname(*prop_val)) == -1)
9993448Sdh155122 		return (DLADM_STATUS_BADVAL);
10003448Sdh155122 
10013448Sdh155122 	if (zid != GLOBAL_ZONEID) {
10023448Sdh155122 		ushort_t	flags;
10033448Sdh155122 
10043448Sdh155122 		if (zone_getattr(zid, ZONE_ATTR_FLAGS, &flags,
10053448Sdh155122 		    sizeof (flags)) < 0) {
10063448Sdh155122 			return (dladm_errno2status(errno));
10073448Sdh155122 		}
10083448Sdh155122 
10093448Sdh155122 		if (!(flags & ZF_NET_EXCL)) {
10103448Sdh155122 			return (DLADM_STATUS_BADVAL);
10113448Sdh155122 		}
10123448Sdh155122 	}
10133448Sdh155122 
10145895Syz147064 	vdp->vd_val = zid;
10155895Syz147064 	return (DLADM_STATUS_OK);
10165895Syz147064 }
10175895Syz147064 
10185903Ssowmini /* ARGSUSED */
10195895Syz147064 static dladm_status_t
10205903Ssowmini do_get_autopush(struct prop_desc *pd, datalink_id_t linkid,
10216512Ssowmini     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags)
10225895Syz147064 {
10235895Syz147064 	dld_ioc_ap_t	dia;
10245895Syz147064 	int		fd, i, len;
10255895Syz147064 
10266512Ssowmini 	if (flags & DLD_DEFAULT)
10276512Ssowmini 		return (DLADM_STATUS_NOTSUP);
10286512Ssowmini 
10295895Syz147064 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
10305895Syz147064 		return (dladm_errno2status(errno));
10315895Syz147064 
10325895Syz147064 	*val_cnt = 1;
10335895Syz147064 	dia.dia_linkid = linkid;
10345895Syz147064 	if (i_dladm_ioctl(fd, DLDIOC_GETAUTOPUSH, &dia, sizeof (dia)) < 0) {
10355895Syz147064 		(*prop_val)[0] = '\0';
10365895Syz147064 		goto done;
10375895Syz147064 	}
10385895Syz147064 
10395895Syz147064 	for (i = 0, len = 0; i < dia.dia_npush; i++) {
10405895Syz147064 		if (i != 0) {
10415895Syz147064 			(void) snprintf(*prop_val + len,
10425895Syz147064 			    DLADM_PROP_VAL_MAX - len, "%c", AP_DELIMITER);
10435895Syz147064 			len += 1;
10445895Syz147064 		}
10455895Syz147064 		(void) snprintf(*prop_val + len, DLADM_PROP_VAL_MAX - len,
10465895Syz147064 		    "%s", dia.dia_aplist[i]);
10475895Syz147064 		len += strlen(dia.dia_aplist[i]);
10485895Syz147064 		if (dia.dia_anchor - 1 == i) {
10495895Syz147064 			(void) snprintf(*prop_val + len,
10505895Syz147064 			    DLADM_PROP_VAL_MAX - len, "%c%s", AP_DELIMITER,
10515895Syz147064 			    AP_ANCHOR);
10525895Syz147064 			len += (strlen(AP_ANCHOR) + 1);
10535895Syz147064 		}
10545895Syz147064 	}
10555895Syz147064 
10565895Syz147064 done:
10575895Syz147064 	(void) close(fd);
10585895Syz147064 	return (DLADM_STATUS_OK);
10595895Syz147064 }
10605895Syz147064 
10615903Ssowmini /* ARGSUSED */
10625895Syz147064 static dladm_status_t
10635960Ssowmini do_set_autopush(prop_desc_t *pd, datalink_id_t linkid, val_desc_t *vdp,
10645960Ssowmini     uint_t val_cnt, uint_t flags, datalink_media_t media)
10655895Syz147064 {
10665895Syz147064 	dld_ioc_ap_t		dia;
10675895Syz147064 	struct dlautopush	*dlap = (struct dlautopush *)vdp->vd_val;
10685895Syz147064 	dladm_status_t		status = DLADM_STATUS_OK;
10695895Syz147064 	int			fd, i;
10705895Syz147064 	int			ic_cmd;
10715895Syz147064 
10725895Syz147064 	if (val_cnt != 1)
10735895Syz147064 		return (DLADM_STATUS_BADVALCNT);
10745895Syz147064 
10755895Syz147064 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
10765895Syz147064 		return (dladm_errno2status(errno));
10775895Syz147064 
10785895Syz147064 	dia.dia_linkid = linkid;
10795895Syz147064 	if (dlap != NULL) {
10805895Syz147064 		dia.dia_anchor = dlap->dap_anchor;
10815895Syz147064 		dia.dia_npush = dlap->dap_npush;
10825895Syz147064 		for (i = 0; i < dia.dia_npush; i++) {
10835895Syz147064 			(void) strlcpy(dia.dia_aplist[i], dlap->dap_aplist[i],
10845895Syz147064 			    FMNAMESZ+1);
10855895Syz147064 		}
10865895Syz147064 		ic_cmd = DLDIOC_SETAUTOPUSH;
10875895Syz147064 	} else {
10885895Syz147064 		ic_cmd = DLDIOC_CLRAUTOPUSH;
10895895Syz147064 	}
10905895Syz147064 
10915895Syz147064 	if (i_dladm_ioctl(fd, ic_cmd, &dia, sizeof (dia)) < 0)
10925895Syz147064 		status = dladm_errno2status(errno);
10935895Syz147064 
10945895Syz147064 	(void) close(fd);
10955895Syz147064 	return (status);
10965895Syz147064 }
10975895Syz147064 
10985895Syz147064 /*
10995895Syz147064  * Add the specified module to the dlautopush structure; returns a
11005895Syz147064  * DLADM_STATUS_* code.
11015895Syz147064  */
11025895Syz147064 dladm_status_t
11035895Syz147064 i_dladm_add_ap_module(const char *module, struct dlautopush *dlap)
11045895Syz147064 {
11055895Syz147064 	if ((strlen(module) == 0) || (strlen(module) > FMNAMESZ))
11065895Syz147064 		return (DLADM_STATUS_BADVAL);
11075895Syz147064 
11085895Syz147064 	if (strncasecmp(module, AP_ANCHOR, strlen(AP_ANCHOR)) == 0) {
11095895Syz147064 		/*
11105895Syz147064 		 * We don't allow multiple anchors, and the anchor must
11115895Syz147064 		 * be after at least one module.
11125895Syz147064 		 */
11135895Syz147064 		if (dlap->dap_anchor != 0)
11145895Syz147064 			return (DLADM_STATUS_BADVAL);
11155895Syz147064 		if (dlap->dap_npush == 0)
11165895Syz147064 			return (DLADM_STATUS_BADVAL);
11175895Syz147064 
11185895Syz147064 		dlap->dap_anchor = dlap->dap_npush;
11195895Syz147064 		return (DLADM_STATUS_OK);
11205895Syz147064 	}
11215895Syz147064 	if (dlap->dap_npush > MAXAPUSH)
11225895Syz147064 		return (DLADM_STATUS_BADVALCNT);
11235895Syz147064 
11245895Syz147064 	(void) strlcpy(dlap->dap_aplist[dlap->dap_npush++], module,
11255895Syz147064 	    FMNAMESZ + 1);
11265895Syz147064 
11275895Syz147064 	return (DLADM_STATUS_OK);
11285895Syz147064 }
11295895Syz147064 
11305895Syz147064 /*
11315895Syz147064  * Currently, both '.' and ' '(space) can be used as the delimiters between
11325895Syz147064  * autopush modules. The former is used in dladm set-linkprop, and the
11335895Syz147064  * latter is used in the autopush(1M) file.
11345895Syz147064  */
11355895Syz147064 /* ARGSUSED */
11365895Syz147064 static dladm_status_t
11375903Ssowmini do_check_autopush(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
11385960Ssowmini     uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
11395895Syz147064 {
11405895Syz147064 	char			*module;
11415895Syz147064 	struct dlautopush	*dlap;
11425895Syz147064 	dladm_status_t		status;
11435895Syz147064 	char			val[DLADM_PROP_VAL_MAX];
11445895Syz147064 	char			delimiters[4];
11455895Syz147064 
11465895Syz147064 	if (val_cnt != 1)
11475895Syz147064 		return (DLADM_STATUS_BADVALCNT);
11485895Syz147064 
11495895Syz147064 	dlap = malloc(sizeof (struct dlautopush));
11505895Syz147064 	if (dlap == NULL)
11513448Sdh155122 		return (DLADM_STATUS_NOMEM);
11523448Sdh155122 
11535895Syz147064 	(void) memset(dlap, 0, sizeof (struct dlautopush));
11545895Syz147064 	(void) snprintf(delimiters, 4, " %c\n", AP_DELIMITER);
11555895Syz147064 	bcopy(*prop_val, val, DLADM_PROP_VAL_MAX);
11565895Syz147064 	module = strtok(val, delimiters);
11575895Syz147064 	while (module != NULL) {
11585895Syz147064 		status = i_dladm_add_ap_module(module, dlap);
11595895Syz147064 		if (status != DLADM_STATUS_OK)
11605895Syz147064 			return (status);
11615895Syz147064 		module = strtok(NULL, delimiters);
11625895Syz147064 	}
11635895Syz147064 
11645895Syz147064 	vdp->vd_val = (uintptr_t)dlap;
11653448Sdh155122 	return (DLADM_STATUS_OK);
11663448Sdh155122 }
11673448Sdh155122 
11685903Ssowmini /* ARGSUSED */
11693448Sdh155122 static dladm_status_t
11705903Ssowmini do_get_rate_common(struct prop_desc *pd, datalink_id_t linkid,
11715903Ssowmini     char **prop_val, uint_t *val_cnt, uint_t id)
11723448Sdh155122 {
11735895Syz147064 	wl_rates_t	*wrp;
11745895Syz147064 	uint_t		i;
11755895Syz147064 	wldp_t		*gbuf = NULL;
11765895Syz147064 	dladm_status_t	status = DLADM_STATUS_OK;
11775895Syz147064 
11785895Syz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
11795895Syz147064 		status = DLADM_STATUS_NOMEM;
11805895Syz147064 		goto done;
11815895Syz147064 	}
11825895Syz147064 
11835895Syz147064 	status = i_dladm_wlan_get_ioctl(linkid, gbuf, id);
11845895Syz147064 	if (status != DLADM_STATUS_OK)
11855895Syz147064 		goto done;
11865895Syz147064 
11875895Syz147064 	wrp = (wl_rates_t *)gbuf->wldp_buf;
11885895Syz147064 	if (wrp->wl_rates_num > *val_cnt) {
11895895Syz147064 		status = DLADM_STATUS_TOOSMALL;
11905895Syz147064 		goto done;
11915895Syz147064 	}
11925895Syz147064 
11935895Syz147064 	if (wrp->wl_rates_rates[0] == 0) {
11945895Syz147064 		prop_val[0][0] = '\0';
11955895Syz147064 		*val_cnt = 1;
11965895Syz147064 		goto done;
11975895Syz147064 	}
11985895Syz147064 
11995895Syz147064 	for (i = 0; i < wrp->wl_rates_num; i++) {
12005895Syz147064 		(void) snprintf(prop_val[i], DLADM_STRSIZE, "%.*f",
12015895Syz147064 		    wrp->wl_rates_rates[i] % 2,
12025895Syz147064 		    (float)wrp->wl_rates_rates[i] / 2);
12035895Syz147064 	}
12045895Syz147064 	*val_cnt = wrp->wl_rates_num;
12053448Sdh155122 
12065895Syz147064 done:
12075895Syz147064 	free(gbuf);
12085895Syz147064 	return (status);
12095895Syz147064 }
12105895Syz147064 
12115895Syz147064 static dladm_status_t
12125903Ssowmini do_get_rate_prop(struct prop_desc *pd, datalink_id_t linkid,
12136512Ssowmini     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags)
12145895Syz147064 {
12155960Ssowmini 	if (media != DL_WIFI)
12166512Ssowmini 		return (dld_speed_get(pd, linkid, prop_val, val_cnt, flags));
12175960Ssowmini 
12185903Ssowmini 	return (do_get_rate_common(pd, linkid, prop_val, val_cnt,
12195895Syz147064 	    WL_DESIRED_RATES));
12205895Syz147064 }
12215895Syz147064 
12226512Ssowmini /* ARGSUSED */
12235895Syz147064 static dladm_status_t
12245903Ssowmini do_get_rate_mod(struct prop_desc *pd, datalink_id_t linkid,
12256512Ssowmini     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags)
12265895Syz147064 {
12275960Ssowmini 	switch (media) {
12285960Ssowmini 	case DL_ETHER:
12296512Ssowmini 		/*
12306512Ssowmini 		 * Speed for ethernet links is unbounded. E.g., 802.11b
12316512Ssowmini 		 * links can have a speed of 5.5 Gbps.
12326512Ssowmini 		 */
12336512Ssowmini 		return (DLADM_STATUS_NOTSUP);
12345960Ssowmini 
12355960Ssowmini 	case DL_WIFI:
12365960Ssowmini 		return (do_get_rate_common(pd, linkid, prop_val, val_cnt,
12375960Ssowmini 		    WL_SUPPORTED_RATES));
12385960Ssowmini 	default:
12395960Ssowmini 		return (DLADM_STATUS_BADARG);
12405960Ssowmini 	}
12415895Syz147064 }
12425895Syz147064 
12435895Syz147064 static dladm_status_t
12445895Syz147064 do_set_rate(datalink_id_t linkid, dladm_wlan_rates_t *rates)
12455895Syz147064 {
12465895Syz147064 	int		i;
12475895Syz147064 	uint_t		len;
12485895Syz147064 	wldp_t		*gbuf;
12495895Syz147064 	wl_rates_t	*wrp;
12505895Syz147064 	dladm_status_t	status = DLADM_STATUS_OK;
12515895Syz147064 
12525895Syz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
12535895Syz147064 		return (DLADM_STATUS_NOMEM);
12545895Syz147064 
12555895Syz147064 	(void) memset(gbuf, 0, MAX_BUF_LEN);
12563448Sdh155122 
12575895Syz147064 	wrp = (wl_rates_t *)gbuf->wldp_buf;
12585895Syz147064 	for (i = 0; i < rates->wr_cnt; i++)
12595895Syz147064 		wrp->wl_rates_rates[i] = rates->wr_rates[i];
12605895Syz147064 	wrp->wl_rates_num = rates->wr_cnt;
12615895Syz147064 
12625895Syz147064 	len = offsetof(wl_rates_t, wl_rates_rates) +
12635895Syz147064 	    (rates->wr_cnt * sizeof (char)) + WIFI_BUF_OFFSET;
12645895Syz147064 	status = i_dladm_wlan_ioctl(linkid, gbuf, WL_DESIRED_RATES, len,
12655895Syz147064 	    WLAN_SET_PARAM, len);
12665895Syz147064 
12675895Syz147064 	free(gbuf);
12685895Syz147064 	return (status);
12695895Syz147064 }
12703448Sdh155122 
12715903Ssowmini /* ARGSUSED */
12725895Syz147064 static dladm_status_t
12735903Ssowmini do_set_rate_prop(prop_desc_t *pd, datalink_id_t linkid,
12745960Ssowmini     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
12755895Syz147064 {
12765895Syz147064 	dladm_wlan_rates_t	rates;
12775895Syz147064 	dladm_status_t		status;
12785895Syz147064 
12795960Ssowmini 	/*
12805960Ssowmini 	 * can currently set rate on WIFI links only.
12815960Ssowmini 	 */
12825960Ssowmini 	if (media != DL_WIFI)
12835960Ssowmini 		return (DLADM_STATUS_PROPRDONLY);
12845960Ssowmini 
12855895Syz147064 	if (val_cnt != 1)
12865895Syz147064 		return (DLADM_STATUS_BADVALCNT);
12875895Syz147064 
12885895Syz147064 	rates.wr_cnt = 1;
12895895Syz147064 	rates.wr_rates[0] = vdp[0].vd_val;
12905895Syz147064 
12915895Syz147064 	status = do_set_rate(linkid, &rates);
12925895Syz147064 
12935895Syz147064 done:
12945895Syz147064 	return (status);
12955895Syz147064 }
12963448Sdh155122 
12975895Syz147064 /* ARGSUSED */
12985895Syz147064 static dladm_status_t
12995903Ssowmini do_check_rate(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
13005960Ssowmini     uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
13015895Syz147064 {
13025895Syz147064 	int		i;
13035895Syz147064 	uint_t		modval_cnt = MAX_SUPPORT_RATES;
13045895Syz147064 	char		*buf, **modval;
13055895Syz147064 	dladm_status_t	status;
13065895Syz147064 
13075895Syz147064 	if (val_cnt != 1)
13085895Syz147064 		return (DLADM_STATUS_BADVALCNT);
13095895Syz147064 
13105895Syz147064 	buf = malloc((sizeof (char *) + DLADM_STRSIZE) *
13115895Syz147064 	    MAX_SUPPORT_RATES);
13125895Syz147064 	if (buf == NULL) {
13135895Syz147064 		status = DLADM_STATUS_NOMEM;
13145895Syz147064 		goto done;
13155895Syz147064 	}
13163448Sdh155122 
13175895Syz147064 	modval = (char **)(void *)buf;
13185895Syz147064 	for (i = 0; i < MAX_SUPPORT_RATES; i++) {
13195895Syz147064 		modval[i] = buf + sizeof (char *) * MAX_SUPPORT_RATES +
13205895Syz147064 		    i * DLADM_STRSIZE;
13215895Syz147064 	}
13225895Syz147064 
13236512Ssowmini 	status = do_get_rate_mod(NULL, linkid, modval, &modval_cnt, media, 0);
13245895Syz147064 	if (status != DLADM_STATUS_OK)
13255895Syz147064 		goto done;
13265895Syz147064 
13275895Syz147064 	for (i = 0; i < modval_cnt; i++) {
13285895Syz147064 		if (strcasecmp(*prop_val, modval[i]) == 0) {
13295903Ssowmini 			vdp->vd_val = (uintptr_t)(uint_t)
13305903Ssowmini 			    (atof(*prop_val) * 2);
13315895Syz147064 			status = DLADM_STATUS_OK;
13323448Sdh155122 			break;
13333448Sdh155122 		}
13345895Syz147064 	}
13355895Syz147064 	if (i == modval_cnt)
13365895Syz147064 		status = DLADM_STATUS_BADVAL;
13375895Syz147064 done:
13385895Syz147064 	free(buf);
13395895Syz147064 	return (status);
13405895Syz147064 }
13415895Syz147064 
13425895Syz147064 static dladm_status_t
13435895Syz147064 do_get_phyconf(datalink_id_t linkid, wldp_t *gbuf)
13445895Syz147064 {
13455895Syz147064 	return (i_dladm_wlan_get_ioctl(linkid, gbuf, WL_PHY_CONFIG));
13465895Syz147064 }
13475895Syz147064 
13485903Ssowmini /* ARGSUSED */
13495895Syz147064 static dladm_status_t
13505903Ssowmini do_get_channel_prop(struct prop_desc *pd, datalink_id_t linkid,
13516512Ssowmini     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags)
13525895Syz147064 {
13535895Syz147064 	uint32_t	channel;
13545895Syz147064 	wldp_t		*gbuf;
13555895Syz147064 	dladm_status_t	status = DLADM_STATUS_OK;
13565895Syz147064 
13575895Syz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
13585895Syz147064 		return (DLADM_STATUS_NOMEM);
13595895Syz147064 
13605895Syz147064 	if ((status = do_get_phyconf(linkid, gbuf)) != DLADM_STATUS_OK)
13615895Syz147064 		goto done;
13625895Syz147064 
13635895Syz147064 	if (!i_dladm_wlan_convert_chan((wl_phy_conf_t *)gbuf->wldp_buf,
13645895Syz147064 	    &channel)) {
13655895Syz147064 		status = DLADM_STATUS_NOTFOUND;
13665895Syz147064 		goto done;
13675895Syz147064 	}
13685895Syz147064 
13695895Syz147064 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%u", channel);
13705895Syz147064 	*val_cnt = 1;
13713448Sdh155122 
13725895Syz147064 done:
13735895Syz147064 	free(gbuf);
13745895Syz147064 	return (status);
13755895Syz147064 }
13765895Syz147064 
13775895Syz147064 static dladm_status_t
13785895Syz147064 do_get_powermode(datalink_id_t linkid, wldp_t *gbuf)
13795895Syz147064 {
13805895Syz147064 	return (i_dladm_wlan_get_ioctl(linkid, gbuf, WL_POWER_MODE));
13815895Syz147064 }
13825895Syz147064 
13835903Ssowmini /* ARGSUSED */
13845895Syz147064 static dladm_status_t
13855903Ssowmini do_get_powermode_prop(struct prop_desc *pd, datalink_id_t linkid,
13866512Ssowmini     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags)
13875895Syz147064 {
13885895Syz147064 	wl_ps_mode_t	*mode;
13895895Syz147064 	const char	*s;
13905895Syz147064 	wldp_t		*gbuf;
13915895Syz147064 	dladm_status_t	status = DLADM_STATUS_OK;
13925895Syz147064 
13935895Syz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
13945895Syz147064 		return (DLADM_STATUS_NOMEM);
13955895Syz147064 
13965895Syz147064 	if ((status = do_get_powermode(linkid, gbuf)) != DLADM_STATUS_OK)
13975895Syz147064 		goto done;
13985895Syz147064 
13995895Syz147064 	mode = (wl_ps_mode_t *)(gbuf->wldp_buf);
14005895Syz147064 	switch (mode->wl_ps_mode) {
14015895Syz147064 	case WL_PM_AM:
14025895Syz147064 		s = "off";
14035895Syz147064 		break;
14045895Syz147064 	case WL_PM_MPS:
14055895Syz147064 		s = "max";
14065895Syz147064 		break;
14075895Syz147064 	case WL_PM_FAST:
14085895Syz147064 		s = "fast";
14093448Sdh155122 		break;
14103448Sdh155122 	default:
14115895Syz147064 		status = DLADM_STATUS_NOTFOUND;
14125895Syz147064 		goto done;
14135895Syz147064 	}
14145895Syz147064 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
14155895Syz147064 	*val_cnt = 1;
14165895Syz147064 
14175895Syz147064 done:
14185895Syz147064 	free(gbuf);
14195895Syz147064 	return (status);
14205895Syz147064 }
14215895Syz147064 
14225895Syz147064 static dladm_status_t
14235895Syz147064 do_set_powermode(datalink_id_t linkid, dladm_wlan_powermode_t *pm)
14245895Syz147064 {
14255895Syz147064 	wl_ps_mode_t    ps_mode;
14265895Syz147064 
14275895Syz147064 	(void) memset(&ps_mode, 0xff, sizeof (ps_mode));
14285895Syz147064 
14295895Syz147064 	switch (*pm) {
14305895Syz147064 	case DLADM_WLAN_PM_OFF:
14315895Syz147064 		ps_mode.wl_ps_mode = WL_PM_AM;
14323448Sdh155122 		break;
14335895Syz147064 	case DLADM_WLAN_PM_MAX:
14345895Syz147064 		ps_mode.wl_ps_mode = WL_PM_MPS;
14355895Syz147064 		break;
14365895Syz147064 	case DLADM_WLAN_PM_FAST:
14375895Syz147064 		ps_mode.wl_ps_mode = WL_PM_FAST;
14385895Syz147064 		break;
14395895Syz147064 	default:
14405895Syz147064 		return (DLADM_STATUS_NOTSUP);
14413448Sdh155122 	}
14425895Syz147064 	return (i_dladm_wlan_set_ioctl(linkid, WL_POWER_MODE, &ps_mode,
14435895Syz147064 	    sizeof (ps_mode)));
14445895Syz147064 }
14455895Syz147064 
14465895Syz147064 /* ARGSUSED */
14475895Syz147064 static dladm_status_t
14485903Ssowmini do_set_powermode_prop(prop_desc_t *pd, datalink_id_t linkid,
14495960Ssowmini     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
14505895Syz147064 {
14515895Syz147064 	dladm_wlan_powermode_t powermode = (dladm_wlan_powermode_t)vdp->vd_val;
14525895Syz147064 	dladm_status_t status;
14535895Syz147064 
14545895Syz147064 	if (val_cnt != 1)
14555895Syz147064 		return (DLADM_STATUS_BADVALCNT);
14565895Syz147064 
14575895Syz147064 	status = do_set_powermode(linkid, &powermode);
14583448Sdh155122 
14593448Sdh155122 	return (status);
14603448Sdh155122 }
14613448Sdh155122 
14623448Sdh155122 static dladm_status_t
14635895Syz147064 do_get_radio(datalink_id_t linkid, wldp_t *gbuf)
14643448Sdh155122 {
14655895Syz147064 	return (i_dladm_wlan_get_ioctl(linkid, gbuf, WL_RADIO));
14665895Syz147064 }
14673448Sdh155122 
14685903Ssowmini /* ARGSUSED */
14695895Syz147064 static dladm_status_t
14705903Ssowmini do_get_radio_prop(struct prop_desc *pd, datalink_id_t linkid,
14716512Ssowmini     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags)
14725895Syz147064 {
14735895Syz147064 	wl_radio_t	radio;
14745895Syz147064 	const char	*s;
14755895Syz147064 	wldp_t		*gbuf;
14765895Syz147064 	dladm_status_t	status = DLADM_STATUS_OK;
14773448Sdh155122 
14785895Syz147064 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
14795895Syz147064 		return (DLADM_STATUS_NOMEM);
14803448Sdh155122 
14815895Syz147064 	if ((status = do_get_radio(linkid, gbuf)) != DLADM_STATUS_OK)
14825895Syz147064 		goto done;
14833448Sdh155122 
14845895Syz147064 	radio = *(wl_radio_t *)(gbuf->wldp_buf);
14855895Syz147064 	switch (radio) {
14865895Syz147064 	case B_TRUE:
14875895Syz147064 		s = "on";
14885895Syz147064 		break;
14895895Syz147064 	case B_FALSE:
14905895Syz147064 		s = "off";
14915895Syz147064 		break;
14925895Syz147064 	default:
14935895Syz147064 		status = DLADM_STATUS_NOTFOUND;
14945895Syz147064 		goto done;
14955895Syz147064 	}
14965895Syz147064 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
14975895Syz147064 	*val_cnt = 1;
14983448Sdh155122 
14995895Syz147064 done:
15005895Syz147064 	free(gbuf);
15013448Sdh155122 	return (status);
15023448Sdh155122 }
15033448Sdh155122 
15043448Sdh155122 static dladm_status_t
15055895Syz147064 do_set_radio(datalink_id_t linkid, dladm_wlan_radio_t *radio)
15063448Sdh155122 {
15075895Syz147064 	wl_radio_t r;
15083448Sdh155122 
15095895Syz147064 	switch (*radio) {
15105895Syz147064 	case DLADM_WLAN_RADIO_ON:
15115895Syz147064 		r = B_TRUE;
15125895Syz147064 		break;
15135895Syz147064 	case DLADM_WLAN_RADIO_OFF:
15145895Syz147064 		r = B_FALSE;
15155895Syz147064 		break;
15165895Syz147064 	default:
15175895Syz147064 		return (DLADM_STATUS_NOTSUP);
15185895Syz147064 	}
15195895Syz147064 	return (i_dladm_wlan_set_ioctl(linkid, WL_RADIO, &r, sizeof (r)));
15205895Syz147064 }
15213448Sdh155122 
15225895Syz147064 /* ARGSUSED */
15235895Syz147064 static dladm_status_t
15245903Ssowmini do_set_radio_prop(prop_desc_t *pd, datalink_id_t linkid,
15255960Ssowmini     val_desc_t *vdp, uint_t val_cnt, uint_t fags, datalink_media_t media)
15265895Syz147064 {
15275895Syz147064 	dladm_wlan_radio_t radio = (dladm_wlan_radio_t)vdp->vd_val;
15285895Syz147064 	dladm_status_t status;
15293448Sdh155122 
15305895Syz147064 	if (val_cnt != 1)
15315895Syz147064 		return (DLADM_STATUS_BADVALCNT);
15325895Syz147064 
15335895Syz147064 	status = do_set_radio(linkid, &radio);
15343448Sdh155122 
15353448Sdh155122 	return (status);
15363448Sdh155122 }
15373448Sdh155122 
15385895Syz147064 static dladm_status_t
15395895Syz147064 i_dladm_set_linkprop_db(datalink_id_t linkid, const char *prop_name,
15405895Syz147064     char **prop_val, uint_t val_cnt)
15413448Sdh155122 {
15425895Syz147064 	char		buf[MAXLINELEN];
15435895Syz147064 	int		i;
15445895Syz147064 	dladm_conf_t	conf;
15455895Syz147064 	dladm_status_t	status;
15463448Sdh155122 
15475895Syz147064 	status = dladm_read_conf(linkid, &conf);
15485895Syz147064 	if (status != DLADM_STATUS_OK)
15495895Syz147064 		return (status);
15503448Sdh155122 
15515895Syz147064 	/*
15525895Syz147064 	 * reset case.
15535895Syz147064 	 */
15545895Syz147064 	if (val_cnt == 0) {
15555895Syz147064 		status = dladm_unset_conf_field(conf, prop_name);
15565895Syz147064 		if (status == DLADM_STATUS_OK)
15575895Syz147064 			status = dladm_write_conf(conf);
15585895Syz147064 		goto done;
15595895Syz147064 	}
15603448Sdh155122 
15615895Syz147064 	buf[0] = '\0';
15625895Syz147064 	for (i = 0; i < val_cnt; i++) {
15635895Syz147064 		(void) strlcat(buf, prop_val[i], MAXLINELEN);
15645895Syz147064 		if (i != val_cnt - 1)
15655895Syz147064 			(void) strlcat(buf, ",", MAXLINELEN);
15663448Sdh155122 	}
15673448Sdh155122 
15685895Syz147064 	status = dladm_set_conf_field(conf, prop_name, DLADM_TYPE_STR, buf);
15695895Syz147064 	if (status == DLADM_STATUS_OK)
15705895Syz147064 		status = dladm_write_conf(conf);
15715895Syz147064 
15725895Syz147064 done:
15735895Syz147064 	dladm_destroy_conf(conf);
15745895Syz147064 	return (status);
15753448Sdh155122 }
15765895Syz147064 
15775895Syz147064 static dladm_status_t
15785895Syz147064 i_dladm_get_linkprop_db(datalink_id_t linkid, const char *prop_name,
15795895Syz147064     char **prop_val, uint_t *val_cntp)
15805895Syz147064 {
15815895Syz147064 	char		buf[MAXLINELEN], *str;
15825895Syz147064 	uint_t		cnt = 0;
15835895Syz147064 	dladm_conf_t	conf;
15845895Syz147064 	dladm_status_t	status;
15855895Syz147064 
15865895Syz147064 	status = dladm_read_conf(linkid, &conf);
15875895Syz147064 	if (status != DLADM_STATUS_OK)
15885895Syz147064 		return (status);
15895895Syz147064 
15905895Syz147064 	status = dladm_get_conf_field(conf, prop_name, buf, MAXLINELEN);
15915895Syz147064 	if (status != DLADM_STATUS_OK)
15925895Syz147064 		goto done;
15935895Syz147064 
15945895Syz147064 	str = strtok(buf, ",");
15955895Syz147064 	while (str != NULL) {
15965895Syz147064 		if (cnt == *val_cntp) {
15975895Syz147064 			status = DLADM_STATUS_TOOSMALL;
15985895Syz147064 			goto done;
15995895Syz147064 		}
16005895Syz147064 		(void) strlcpy(prop_val[cnt++], str, DLADM_PROP_VAL_MAX);
16015895Syz147064 		str = strtok(NULL, ",");
16025895Syz147064 	}
16035895Syz147064 
16045895Syz147064 	*val_cntp = cnt;
16055895Syz147064 
16065895Syz147064 done:
16075895Syz147064 	dladm_destroy_conf(conf);
16085895Syz147064 	return (status);
16095895Syz147064 }
16105903Ssowmini 
16115903Ssowmini static dld_public_prop_t *
16125903Ssowmini dladm_name2prop(const char *prop_name)
16135903Ssowmini {
16145903Ssowmini 	dld_public_prop_t *p;
16155903Ssowmini 
16165903Ssowmini 	for (p = dld_prop; p->pp_id != DLD_PROP_PRIVATE; p++) {
16175903Ssowmini 		if (strcmp(p->pp_name, prop_name) == 0)
16185903Ssowmini 			break;
16195903Ssowmini 	}
16205903Ssowmini 	return (p);
16215903Ssowmini }
16225903Ssowmini 
16235903Ssowmini 
16245903Ssowmini static dld_ioc_prop_t *
16255903Ssowmini dld_buf_alloc(size_t valsize, datalink_id_t linkid, const char *prop_name,
16266512Ssowmini     uint_t flags, dladm_status_t *status)
16275903Ssowmini {
16285903Ssowmini 	int dsize;
16295903Ssowmini 	dld_ioc_prop_t *dip;
16305903Ssowmini 	dld_public_prop_t *p;
16315903Ssowmini 
16325903Ssowmini 	*status = DLADM_STATUS_OK;
16335903Ssowmini 	p = dladm_name2prop(prop_name);
16345903Ssowmini 	if (p->pp_id != DLD_PROP_PRIVATE)
16355903Ssowmini 		valsize = p->pp_valsize;
16365903Ssowmini 
16375903Ssowmini 	dsize = DLD_PROPBUF_SIZE(valsize);
16385903Ssowmini 	dip = malloc(dsize);
16395903Ssowmini 	if (dip == NULL) {
16405903Ssowmini 		*status = DLADM_STATUS_NOMEM;
16415903Ssowmini 		return (NULL);
16425903Ssowmini 	}
16435903Ssowmini 	bzero(dip, dsize);
16445903Ssowmini 	dip->pr_valsize = valsize;
16456512Ssowmini 	(void) strlcpy(dip->pr_name, prop_name, sizeof (dip->pr_name));
16465903Ssowmini 	dip->pr_version = DLD_PROP_VERSION;
16475960Ssowmini 	dip->pr_linkid = linkid;
16485903Ssowmini 	dip->pr_num = p->pp_id;
16496512Ssowmini 	dip->pr_flags = flags;
16505903Ssowmini 	return (dip);
16515903Ssowmini }
16525903Ssowmini 
16535903Ssowmini /* ARGSUSED */
16545903Ssowmini static dladm_status_t
16555903Ssowmini dld_set_public_prop(prop_desc_t *pd, datalink_id_t linkid,
16565960Ssowmini     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
16575903Ssowmini {
16585903Ssowmini 	dld_ioc_prop_t	*dip;
16595903Ssowmini 	int		fd, dsize;
16605903Ssowmini 	dladm_status_t	status = DLADM_STATUS_OK;
16615903Ssowmini 	uint8_t		u8;
16625903Ssowmini 	uint16_t	u16;
16635903Ssowmini 	uint32_t	u32;
16645903Ssowmini 	void		*val;
16655903Ssowmini 
16666512Ssowmini 	dip = dld_buf_alloc(0, linkid, pd->pd_name, 0, &status);
16675903Ssowmini 	if (dip == NULL)
16685903Ssowmini 		return (status);
16695903Ssowmini 
16705903Ssowmini 	if (pd->pd_flags & PD_CHECK_ALLOC)
16715903Ssowmini 		val = (void *)vdp->vd_val;
16725903Ssowmini 	else {
16735903Ssowmini 		/*
16745903Ssowmini 		 * Currently all 1/2/4-byte size properties are byte/word/int.
16755903Ssowmini 		 * No need (yet) to distinguish these from arrays of same size.
16765903Ssowmini 		 */
16775903Ssowmini 		switch (dip->pr_valsize) {
16785903Ssowmini 		case 1:
16795903Ssowmini 			u8 = vdp->vd_val;
16805903Ssowmini 			val = &u8;
16815903Ssowmini 			break;
16825903Ssowmini 		case 2:
16835903Ssowmini 			u16 = vdp->vd_val;
16845903Ssowmini 			val = &u16;
16855903Ssowmini 			break;
16865903Ssowmini 		case 4:
16875903Ssowmini 			u32 = vdp->vd_val;
16885903Ssowmini 			val = &u32;
16895903Ssowmini 			break;
16905903Ssowmini 		default:
16915903Ssowmini 			val = &vdp->vd_val;
16925903Ssowmini 			break;
16935903Ssowmini 		}
16945903Ssowmini 	}
16955903Ssowmini 
16965903Ssowmini 	(void) memcpy(dip->pr_val, val, dip->pr_valsize);
16975903Ssowmini 	dsize = DLD_PROPBUF_SIZE(dip->pr_valsize);
16985903Ssowmini 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) {
16995903Ssowmini 		status = dladm_errno2status(errno);
17005903Ssowmini 		goto done;
17015903Ssowmini 	}
17025903Ssowmini 	if (i_dladm_ioctl(fd, DLDIOCSETPROP, dip, dsize) < 0)
17035903Ssowmini 		status = dladm_errno2status(errno);
17045903Ssowmini 
17055903Ssowmini 	(void) close(fd);
17065903Ssowmini done:
17076512Ssowmini 	free(dip);
17085903Ssowmini 	return (status);
17095903Ssowmini }
17105903Ssowmini 
17116512Ssowmini static dld_ioc_prop_t *
17126512Ssowmini dld_get_public_prop(datalink_id_t linkid, char *prop_name, uint_t flags,
17136512Ssowmini     dladm_status_t *status)
17145903Ssowmini {
17155903Ssowmini 	int fd, dsize;
17166512Ssowmini 	dld_ioc_prop_t *dip = NULL;
17176512Ssowmini 
17186512Ssowmini 	*status = DLADM_STATUS_OK;
17196512Ssowmini 
17206512Ssowmini 	dip = dld_buf_alloc(0, linkid, prop_name, flags, status);
17216512Ssowmini 	if (dip == NULL)
17226512Ssowmini 		return (NULL);
17235903Ssowmini 
17245903Ssowmini 	dsize = DLD_PROPBUF_SIZE(dip->pr_valsize);
17255903Ssowmini 
17265903Ssowmini 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) {
17276512Ssowmini 		*status = dladm_errno2status(errno);
17285903Ssowmini 		goto done;
17295903Ssowmini 	}
17305903Ssowmini 	if (i_dladm_ioctl(fd, DLDIOCGETPROP, dip, dsize) < 0) {
17316512Ssowmini 		*status = dladm_errno2status(errno);
17325903Ssowmini 	}
17336512Ssowmini 
17346512Ssowmini 	(void) close(fd);
17355903Ssowmini done:
17366512Ssowmini 	if (*status != DLADM_STATUS_OK) {
17376512Ssowmini 		free(dip);
17386512Ssowmini 		return (NULL);
17396512Ssowmini 	}
17406512Ssowmini 	return (dip);
17415903Ssowmini }
17425903Ssowmini 
17435903Ssowmini /* ARGSUSED */
17445903Ssowmini static dladm_status_t
17455903Ssowmini dld_defmtu_check(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
17465960Ssowmini     uint_t val_cnt, val_desc_t *v, datalink_media_t media)
17475903Ssowmini {
17485903Ssowmini 	if (val_cnt != 1)
17495903Ssowmini 		return (DLADM_STATUS_BADVAL);
17506512Ssowmini 	v->vd_val = atoi(prop_val[0]);
17515903Ssowmini 	return (DLADM_STATUS_OK);
17525903Ssowmini }
17535903Ssowmini 
17545903Ssowmini /* ARGSUSED */
17555903Ssowmini static dladm_status_t
17565903Ssowmini dld_duplex_get(struct prop_desc *pd, datalink_id_t linkid,
17576512Ssowmini     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags)
17585903Ssowmini {
17595903Ssowmini 	link_duplex_t   link_duplex;
17605903Ssowmini 	dladm_status_t  status;
17615903Ssowmini 
17626512Ssowmini 	if (flags & DLD_DEFAULT)
17636512Ssowmini 		return (DLADM_STATUS_NOTSUP);
17646512Ssowmini 
17655903Ssowmini 	if ((status = dladm_get_single_mac_stat(linkid, "link_duplex",
17665903Ssowmini 	    KSTAT_DATA_UINT32, &link_duplex)) != 0)
17675903Ssowmini 		return (status);
17685903Ssowmini 
17695903Ssowmini 	switch (link_duplex) {
17705903Ssowmini 	case LINK_DUPLEX_FULL:
17715903Ssowmini 		(void) strcpy(*prop_val, "full");
17725903Ssowmini 		break;
17735903Ssowmini 	case LINK_DUPLEX_HALF:
17745903Ssowmini 		(void) strcpy(*prop_val, "half");
17755903Ssowmini 		break;
17765903Ssowmini 	default:
17775903Ssowmini 		(void) strcpy(*prop_val, "unknown");
17785903Ssowmini 		break;
17795903Ssowmini 	}
17805903Ssowmini 	*val_cnt = 1;
17815903Ssowmini 	return (DLADM_STATUS_OK);
17825903Ssowmini }
17835903Ssowmini 
17845903Ssowmini /* ARGSUSED */
17855903Ssowmini static dladm_status_t
17865903Ssowmini dld_speed_get(struct prop_desc *pd, datalink_id_t linkid,
17876512Ssowmini     char **prop_val, uint_t *val_cnt, uint_t flags)
17885903Ssowmini {
17895903Ssowmini 	uint64_t	ifspeed = 0;
17905903Ssowmini 	dladm_status_t status;
17915903Ssowmini 
17926512Ssowmini 	if (flags & DLD_DEFAULT)
17936512Ssowmini 		return (DLADM_STATUS_NOTSUP);
17946512Ssowmini 
17955903Ssowmini 	if ((status = dladm_get_single_mac_stat(linkid, "ifspeed",
17965903Ssowmini 	    KSTAT_DATA_UINT64, &ifspeed)) != 0)
17975903Ssowmini 		return (status);
17986512Ssowmini 
17995960Ssowmini 	if ((ifspeed % 1000000) != 0) {
18005960Ssowmini 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
18015960Ssowmini 		    "%llf", ifspeed / (float)1000000); /* Mbps */
18025960Ssowmini 	} else {
18035960Ssowmini 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
18045960Ssowmini 		    "%llu", ifspeed / 1000000); /* Mbps */
18055960Ssowmini 	}
18065903Ssowmini 	*val_cnt = 1;
18075903Ssowmini 	return (DLADM_STATUS_OK);
18085903Ssowmini }
18095903Ssowmini 
18105903Ssowmini /* ARGSUSED */
18115903Ssowmini static dladm_status_t
18125903Ssowmini dld_status_get(struct prop_desc *pd, datalink_id_t linkid,
18136512Ssowmini     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags)
18145903Ssowmini {
18156512Ssowmini 	link_state_t	link_state;
18166512Ssowmini 	dladm_status_t	status;
18176512Ssowmini 	uchar_t 	*cp;
18186512Ssowmini 	dld_ioc_prop_t  *dip;
18195903Ssowmini 
18206512Ssowmini 	if (flags & DLD_DEFAULT)
18216512Ssowmini 		return (DLADM_STATUS_NOTSUP);
18226512Ssowmini 	dip = dld_get_public_prop(linkid, pd->pd_name, flags, &status);
18236512Ssowmini 	if (status != DLADM_STATUS_OK)
18245903Ssowmini 		return (status);
18256512Ssowmini 	cp = (uchar_t *)dip->pr_val;
18266512Ssowmini 	(void) memcpy(&link_state, cp, sizeof (link_state));
18275903Ssowmini 
18285903Ssowmini 	switch (link_state) {
18295903Ssowmini 	case LINK_STATE_UP:
18305903Ssowmini 		(void) strcpy(*prop_val, "up");
18315903Ssowmini 		break;
18325903Ssowmini 	case LINK_STATE_DOWN:
18335903Ssowmini 		(void) strcpy(*prop_val, "down");
18345903Ssowmini 		break;
18355903Ssowmini 	default:
18365903Ssowmini 		(void) strcpy(*prop_val, "unknown");
18375903Ssowmini 		break;
18385903Ssowmini 	}
18395903Ssowmini 	*val_cnt = 1;
18406512Ssowmini 	free(dip);
18415903Ssowmini 	return (DLADM_STATUS_OK);
18425903Ssowmini }
18435903Ssowmini 
18445903Ssowmini /* ARGSUSED */
18455903Ssowmini static dladm_status_t
18465903Ssowmini dld_binary_get(struct prop_desc *pd, datalink_id_t linkid,
18476512Ssowmini     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags)
18485903Ssowmini {
18495903Ssowmini 	dld_ioc_prop_t *dip;
18505903Ssowmini 	dladm_status_t status;
18515903Ssowmini 
18526512Ssowmini 	dip = dld_get_public_prop(linkid, pd->pd_name, flags, &status);
18536512Ssowmini 	if (dip == NULL)
18545903Ssowmini 		return (status);
18555903Ssowmini 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%x", dip->pr_val[0]);
18565903Ssowmini 	free(dip);
18575903Ssowmini 	*val_cnt = 1;
18585903Ssowmini 	return (DLADM_STATUS_OK);
18595903Ssowmini }
18605903Ssowmini 
18615960Ssowmini /* ARGSUSED */
18625903Ssowmini static dladm_status_t
18636512Ssowmini dld_uint32_get(struct prop_desc *pd, datalink_id_t linkid,
18646512Ssowmini     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags)
18655903Ssowmini {
18665903Ssowmini 	dld_ioc_prop_t *dip;
18676512Ssowmini 	uint32_t v  = 0;
18685903Ssowmini 	uchar_t *cp;
18695903Ssowmini 	dladm_status_t status;
18705903Ssowmini 
18716512Ssowmini 	dip = dld_get_public_prop(linkid, pd->pd_name, flags, &status);
18726512Ssowmini 	if (dip == NULL)
18735903Ssowmini 		return (status);
18745903Ssowmini 	cp = (uchar_t *)dip->pr_val;
18755903Ssowmini 	(void) memcpy(&v, cp, sizeof (v));
18766512Ssowmini 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%ld", v);
18775903Ssowmini 	free(dip);
18785903Ssowmini 	*val_cnt = 1;
18795903Ssowmini 	return (DLADM_STATUS_OK);
18805903Ssowmini }
18815903Ssowmini 
18825960Ssowmini /* ARGSUSED */
18835903Ssowmini static dladm_status_t
18845903Ssowmini dld_flowctl_get(struct prop_desc *pd, datalink_id_t linkid,
18856512Ssowmini     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags)
18865903Ssowmini {
18875903Ssowmini 	dld_ioc_prop_t *dip;
18885903Ssowmini 	link_flowctrl_t v;
18895903Ssowmini 	dladm_status_t status;
18905903Ssowmini 	uchar_t *cp;
18915903Ssowmini 
18926512Ssowmini 	dip = dld_get_public_prop(linkid, pd->pd_name, flags, &status);
18936512Ssowmini 	if (dip == NULL)
18945903Ssowmini 		return (status);
18955903Ssowmini 	cp = (uchar_t *)dip->pr_val;
18965903Ssowmini 	(void) memcpy(&v, cp, sizeof (v));
18975903Ssowmini 	switch (v) {
18985903Ssowmini 	case LINK_FLOWCTRL_NONE:
18995903Ssowmini 		(void) sprintf(*prop_val, "no");
19005903Ssowmini 		break;
19015903Ssowmini 	case LINK_FLOWCTRL_RX:
19025903Ssowmini 		(void) sprintf(*prop_val, "rx");
19035903Ssowmini 		break;
19045903Ssowmini 	case LINK_FLOWCTRL_TX:
19055903Ssowmini 		(void) sprintf(*prop_val, "tx");
19065903Ssowmini 		break;
19075903Ssowmini 	case LINK_FLOWCTRL_BI:
19085903Ssowmini 		(void) sprintf(*prop_val, "bi");
19095903Ssowmini 		break;
19105903Ssowmini 	}
19115903Ssowmini 	free(dip);
19125903Ssowmini 	*val_cnt = 1;
19135903Ssowmini 	return (DLADM_STATUS_OK);
19145903Ssowmini }
19155903Ssowmini 
19165903Ssowmini 
19175903Ssowmini /* ARGSUSED */
19185903Ssowmini static dladm_status_t
19195903Ssowmini dld_set_prop(datalink_id_t linkid, const char *prop_name,
19205903Ssowmini     char **prop_val, uint_t val_cnt, uint_t flags)
19215903Ssowmini {
19225903Ssowmini 	int		fd, i, slen;
19235903Ssowmini 	int 		bufsize = 0, dsize;
19245903Ssowmini 	dld_ioc_prop_t *dip = NULL;
19255903Ssowmini 	uchar_t 	*dp;
19265903Ssowmini 	dld_public_prop_t *p;
19276512Ssowmini 	dladm_status_t	status = DLADM_STATUS_OK;
19285903Ssowmini 
19295903Ssowmini 	if ((prop_name == NULL && prop_val != NULL) ||
19305903Ssowmini 	    (prop_val != NULL && val_cnt == 0))
19315903Ssowmini 		return (DLADM_STATUS_BADARG);
19325903Ssowmini 	p = dladm_name2prop(prop_name);
19335903Ssowmini 	if (p->pp_id != DLD_PROP_PRIVATE)
19345903Ssowmini 		return (DLADM_STATUS_BADARG);
19355903Ssowmini 
19365903Ssowmini 	/*
19375903Ssowmini 	 * private properties: all parsing is done in the kernel.
19385903Ssowmini 	 * allocate a enough space for each property + its separator (',').
19395903Ssowmini 	 */
19405903Ssowmini 	for (i = 0; i < val_cnt; i++) {
19415903Ssowmini 		bufsize += strlen(prop_val[i]) + 1;
19425903Ssowmini 	}
19436512Ssowmini 
19446512Ssowmini 	if (prop_val == NULL) {
19456512Ssowmini 		/*
19466512Ssowmini 		 * getting default value. so use more buffer space.
19476512Ssowmini 		 */
19486512Ssowmini 		bufsize += 1024;
19496512Ssowmini 	}
19506512Ssowmini 
19516512Ssowmini 	dip = dld_buf_alloc(bufsize + 1, linkid, prop_name,
19526512Ssowmini 	    (prop_val != NULL ? 0 : DLD_DEFAULT), &status);
19535903Ssowmini 	if (dip == NULL)
19545903Ssowmini 		return (status);
19555903Ssowmini 
19565903Ssowmini 	dp = (uchar_t *)dip->pr_val;
19575903Ssowmini 	dsize = sizeof (dld_ioc_prop_t) + bufsize;
19585903Ssowmini 	slen = 0;
19596512Ssowmini 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) {
19606512Ssowmini 		status = dladm_errno2status(errno);
19616512Ssowmini 		goto done;
19626512Ssowmini 	}
19636512Ssowmini 	if (prop_val == NULL) {
19646512Ssowmini 		if (i_dladm_ioctl(fd, DLDIOCGETPROP, dip, dsize) < 0) {
19656512Ssowmini 			status = dladm_errno2status(errno);
19666512Ssowmini 			goto done;
19676512Ssowmini 		}
19686512Ssowmini 	} else {
19696512Ssowmini 		for (i = 0; i < val_cnt; i++) {
19706512Ssowmini 			int plen = 0;
19715903Ssowmini 
19726512Ssowmini 			plen = strlen(prop_val[i]);
19736512Ssowmini 			bcopy(prop_val[i], dp, plen);
19746512Ssowmini 			slen += plen;
19756512Ssowmini 			/*
19766512Ssowmini 			 * add a "," separator and update dp.
19776512Ssowmini 			 */
19786512Ssowmini 			if (i != (val_cnt -1))
19796512Ssowmini 				dp[slen++] = ',';
19806512Ssowmini 			dp += (plen + 1);
19816512Ssowmini 		}
19825903Ssowmini 	}
19836512Ssowmini 	if (i_dladm_ioctl(fd, DLDIOCSETPROP, dip, dsize) < 0) {
19846512Ssowmini 		status = dladm_errno2status(errno);
19855903Ssowmini 	}
19866512Ssowmini 
19876512Ssowmini done:
19886512Ssowmini 	if (fd > 0)
19896512Ssowmini 		(void) close(fd);
19905903Ssowmini 	free(dip);
19916512Ssowmini 	return (status);
19925903Ssowmini }
19935903Ssowmini 
19945903Ssowmini static dladm_status_t
19955903Ssowmini dld_get_prop(datalink_id_t linkid, const char *prop_name,
19966512Ssowmini     char **prop_val, uint_t *val_cnt, dladm_prop_type_t type, uint_t dld_flags)
19975903Ssowmini {
19985903Ssowmini 	int		fd;
19995903Ssowmini 	dladm_status_t  status = DLADM_STATUS_OK;
20005903Ssowmini 	uint_t 		dsize;
20015903Ssowmini 	dld_ioc_prop_t *dip = NULL;
20025903Ssowmini 	dld_public_prop_t *p;
20035903Ssowmini 	char tmp = '\0';
20045903Ssowmini 
20055903Ssowmini 	if ((prop_name == NULL && prop_val != NULL) ||
20065903Ssowmini 	    (prop_val != NULL && val_cnt == 0))
20075903Ssowmini 		return (DLADM_STATUS_BADARG);
20085903Ssowmini 
20095903Ssowmini 	p = dladm_name2prop(prop_name);
20105903Ssowmini 	if (p->pp_id != DLD_PROP_PRIVATE)
20115903Ssowmini 		return (DLADM_STATUS_BADARG);
20125903Ssowmini 
20136512Ssowmini 	if (type == DLADM_PROP_VAL_MODIFIABLE) {
20145903Ssowmini 		*prop_val = &tmp;
20155903Ssowmini 		*val_cnt = 1;
20165903Ssowmini 		return (DLADM_STATUS_OK);
20175903Ssowmini 	}
20185903Ssowmini 
20195903Ssowmini 	/*
20205903Ssowmini 	 * private properties: all parsing is done in the kernel.
20215903Ssowmini 	 */
20226512Ssowmini 	dip = dld_buf_alloc(1024, linkid, prop_name, dld_flags, &status);
20235903Ssowmini 	if (dip == NULL)
20245903Ssowmini 		return (status);
20255903Ssowmini 	dsize = DLD_PROPBUF_SIZE(dip->pr_valsize);
20265903Ssowmini 
20276512Ssowmini 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) {
20286512Ssowmini 		free(dip);
20295903Ssowmini 		return (DLADM_STATUS_BADARG);
20306512Ssowmini 	}
20315903Ssowmini 
20325903Ssowmini 	if ((status = i_dladm_ioctl(fd, DLDIOCGETPROP, dip, dsize)) < 0) {
20335903Ssowmini 		status = dladm_errno2status(errno);
20345903Ssowmini 	} else {
20355903Ssowmini 		(void) strncpy(*prop_val, dip->pr_val, DLADM_PROP_VAL_MAX);
20365903Ssowmini 		*val_cnt = 1;
20375903Ssowmini 	}
20386512Ssowmini 
20396512Ssowmini 	(void) close(fd);
20406512Ssowmini 	free(dip);
20415903Ssowmini 	return (status);
20425903Ssowmini }
20436512Ssowmini 
20446512Ssowmini 
20456512Ssowmini static dladm_status_t
20466512Ssowmini i_dladm_getset_defval(prop_desc_t *pdp, datalink_id_t linkid,
20476512Ssowmini     datalink_media_t media, uint_t flags)
20486512Ssowmini {
20496512Ssowmini 	dladm_status_t status;
20506512Ssowmini 	char **prop_vals = NULL, *buf;
20516512Ssowmini 	size_t bufsize;
20526512Ssowmini 	uint_t cnt;
20536512Ssowmini 	int i;
20546512Ssowmini 
20556512Ssowmini 	/*
20566512Ssowmini 	 * Allocate buffer needed for prop_vals array. We can have at most
20576512Ssowmini 	 * DLADM_MAX_PROP_VALCNT char *prop_vals[] entries, where
20586512Ssowmini 	 * each entry has max size DLADM_PROP_VAL_MAX
20596512Ssowmini 	 */
20606512Ssowmini 	bufsize =
20616512Ssowmini 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
20626512Ssowmini 	buf = malloc(bufsize);
20636512Ssowmini 	prop_vals = (char **)(void *)buf;
20646512Ssowmini 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
20656512Ssowmini 		prop_vals[i] = buf +
20666512Ssowmini 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
20676512Ssowmini 		    i * DLADM_PROP_VAL_MAX;
20686512Ssowmini 	}
2069*6768Sar224390 
2070*6768Sar224390 	/*
2071*6768Sar224390 	 * PD_EMPTY_RESET is used for properties like zone where the
2072*6768Sar224390 	 * "" itself is used to reset the property. So libdladm can
2073*6768Sar224390 	 * copy pdp->pd_defval over to the val_desc_t passed down on
2074*6768Sar224390 	 * the setprop using the global values in the table. For other
2075*6768Sar224390 	 * cases (PD_EMPTY_RESET is not set, vd_name is ""), doing
2076*6768Sar224390 	 * reset-linkprop will cause libdladm to do a getprop to find
2077*6768Sar224390 	 * the default value and then do a setprop to reset the value
2078*6768Sar224390 	 * to default.
2079*6768Sar224390 	 */
20806512Ssowmini 	status = pdp->pd_get(pdp, linkid, prop_vals, &cnt, media, DLD_DEFAULT);
20816512Ssowmini 	if (status == DLADM_STATUS_OK) {
20826512Ssowmini 		status = i_dladm_set_single_prop(linkid, pdp->pd_class,
20836512Ssowmini 		    media, pdp, prop_vals, cnt, flags);
20846512Ssowmini 	}
20856512Ssowmini 	free(buf);
20866512Ssowmini 	return (status);
20876512Ssowmini }
2088