xref: /illumos-gate/usr/src/lib/libdladm/common/linkprop.c (revision c3d26abc9ee97b4f60233556aadeb57e0bd30bb9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright (c) 2014, Joyent, Inc. All rights reserved.
24  * Copyright 2015 Garrett D'Amore <garrett@damore.org>
25  */
26 
27 #include <stdlib.h>
28 #include <string.h>
29 #include <strings.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <stddef.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/dld.h>
36 #include <sys/zone.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <libdevinfo.h>
40 #include <zone.h>
41 #include <libdllink.h>
42 #include <libdladm_impl.h>
43 #include <libdlwlan_impl.h>
44 #include <libdlwlan.h>
45 #include <libdlvlan.h>
46 #include <libdlvnic.h>
47 #include <libdlib.h>
48 #include <libintl.h>
49 #include <dlfcn.h>
50 #include <link.h>
51 #include <inet/wifi_ioctl.h>
52 #include <libdladm.h>
53 #include <libdlstat.h>
54 #include <sys/param.h>
55 #include <sys/debug.h>
56 #include <sys/dld.h>
57 #include <inttypes.h>
58 #include <sys/ethernet.h>
59 #include <inet/iptun.h>
60 #include <net/wpa.h>
61 #include <sys/sysmacros.h>
62 #include <sys/vlan.h>
63 #include <libdlbridge.h>
64 #include <stp_in.h>
65 #include <netinet/dhcp.h>
66 #include <netinet/dhcp6.h>
67 #include <net/if_types.h>
68 #include <libinetutil.h>
69 #include <pool.h>
70 #include <libdlaggr.h>
71 
72 /*
73  * The linkprop get() callback.
74  * - pd: 	pointer to the prop_desc_t
75  * - propstrp:	a property string array to keep the returned property.
76  *		Caller allocated.
77  * - cntp:	number of returned properties.
78  *		Caller also uses it to indicate how many it expects.
79  */
80 struct prop_desc;
81 typedef struct prop_desc prop_desc_t;
82 
83 typedef dladm_status_t	pd_getf_t(dladm_handle_t, prop_desc_t *pdp,
84 			datalink_id_t, char **propstp, uint_t *cntp,
85 			datalink_media_t, uint_t, uint_t *);
86 
87 /*
88  * The linkprop set() callback.
89  * - propval:	a val_desc_t array which keeps the property values to be set.
90  * - cnt:	number of properties to be set.
91  * - flags: 	additional flags passed down the system call.
92  *
93  * pd_set takes val_desc_t given by pd_check(), translates it into
94  * a format suitable for kernel consumption. This may require allocation
95  * of ioctl buffers etc. pd_set() may call another common routine (used
96  * by all other pd_sets) which invokes the ioctl.
97  */
98 typedef dladm_status_t	pd_setf_t(dladm_handle_t, prop_desc_t *, datalink_id_t,
99 			    val_desc_t *propval, uint_t cnt, uint_t flags,
100 			    datalink_media_t);
101 
102 /*
103  * The linkprop check() callback.
104  * - propstrp:	property string array which keeps the property to be checked.
105  * - cnt:	number of properties.
106  * - propval:	return value; the property values of the given property strings.
107  *
108  * pd_check checks that the input values are valid. It does so by
109  * iteraring through the pd_modval list for the property. If
110  * the modifiable values cannot be expressed as a list, a pd_check
111  * specific to this property can be used. If the input values are
112  * verified to be valid, pd_check allocates a val_desc_t and fills it
113  * with either a val_desc_t found on the pd_modval list or something
114  * generated on the fly.
115  */
116 typedef dladm_status_t	pd_checkf_t(dladm_handle_t, prop_desc_t *pdp,
117 			    datalink_id_t, char **propstrp, uint_t *cnt,
118 			    uint_t flags, val_desc_t **propval,
119 			    datalink_media_t);
120 
121 typedef struct link_attr_s {
122 	mac_prop_id_t	pp_id;
123 	size_t		pp_valsize;
124 	char		*pp_name;
125 } link_attr_t;
126 
127 typedef struct dladm_linkprop_args_s {
128 	dladm_status_t	dla_status;
129 	uint_t		dla_flags;
130 } dladm_linkprop_args_t;
131 
132 static dld_ioc_macprop_t *i_dladm_buf_alloc_by_name(size_t, datalink_id_t,
133 			    const char *, uint_t, dladm_status_t *);
134 static dld_ioc_macprop_t *i_dladm_buf_alloc_by_id(size_t, datalink_id_t,
135 			    mac_prop_id_t, uint_t, dladm_status_t *);
136 static dladm_status_t	i_dladm_get_public_prop(dladm_handle_t, datalink_id_t,
137 			    char *, uint_t, uint_t *, void *, size_t);
138 
139 static dladm_status_t	i_dladm_set_private_prop(dladm_handle_t, datalink_id_t,
140 			    const char *, char **, uint_t, uint_t);
141 static dladm_status_t	i_dladm_get_priv_prop(dladm_handle_t, datalink_id_t,
142 			    const char *, char **, uint_t *, dladm_prop_type_t,
143 			    uint_t);
144 static dladm_status_t	i_dladm_macprop(dladm_handle_t, void *, boolean_t);
145 static const char	*dladm_perm2str(uint_t, char *);
146 static link_attr_t	*dladm_name2prop(const char *);
147 static link_attr_t	*dladm_id2prop(mac_prop_id_t);
148 
149 static pd_getf_t	get_zone, get_autopush, get_rate_mod, get_rate,
150 			get_speed, get_channel, get_powermode, get_radio,
151 			get_duplex, get_link_state, get_binary, get_uint32,
152 			get_flowctl, get_maxbw, get_cpus, get_priority,
153 			get_tagmode, get_range, get_stp, get_bridge_forward,
154 			get_bridge_pvid, get_protection, get_rxrings,
155 			get_txrings, get_cntavail, get_secondary_macs,
156 			get_allowedips, get_allowedcids, get_pool,
157 			get_rings_range, get_linkmode_prop;
158 
159 static pd_setf_t	set_zone, set_rate, set_powermode, set_radio,
160 			set_public_prop, set_resource, set_stp_prop,
161 			set_bridge_forward, set_bridge_pvid, set_secondary_macs;
162 
163 static pd_checkf_t	check_zone, check_autopush, check_rate, check_hoplimit,
164 			check_encaplim, check_uint32, check_maxbw, check_cpus,
165 			check_stp_prop, check_bridge_pvid, check_allowedips,
166 			check_allowedcids, check_secondary_macs, check_rings,
167 			check_pool, check_prop;
168 
169 struct prop_desc {
170 	/*
171 	 * link property name
172 	 */
173 	char			*pd_name;
174 
175 	/*
176 	 * default property value, can be set to { "", NULL }
177 	 */
178 	val_desc_t		pd_defval;
179 
180 	/*
181 	 * list of optional property values, can be NULL.
182 	 *
183 	 * This is set to non-NULL if there is a list of possible property
184 	 * values.  pd_optval would point to the array of possible values.
185 	 */
186 	val_desc_t		*pd_optval;
187 
188 	/*
189 	 * count of the above optional property values. 0 if pd_optval is NULL.
190 	 */
191 	uint_t			pd_noptval;
192 
193 	/*
194 	 * callback to set link property; set to NULL if this property is
195 	 * read-only and may be called before or after permanent update; see
196 	 * flags.
197 	 */
198 	pd_setf_t		*pd_set;
199 
200 	/*
201 	 * callback to get modifiable link property
202 	 */
203 	pd_getf_t		*pd_getmod;
204 
205 	/*
206 	 * callback to get current link property
207 	 */
208 	pd_getf_t		*pd_get;
209 
210 	/*
211 	 * callback to validate link property value, set to NULL if pd_optval
212 	 * is not NULL. In that case, validate the value by comparing it with
213 	 * the pd_optval. Return a val_desc_t array pointer if the value is
214 	 * valid.
215 	 */
216 	pd_checkf_t		*pd_check;
217 
218 	uint_t			pd_flags;
219 #define	PD_TEMPONLY	0x1	/* property is temporary only */
220 #define	PD_CHECK_ALLOC	0x2	/* alloc vd_val as part of pd_check */
221 #define	PD_AFTER_PERM	0x4	/* pd_set after db update; no temporary */
222 	/*
223 	 * indicate link classes this property applies to.
224 	 */
225 	datalink_class_t	pd_class;
226 
227 	/*
228 	 * indicate link media type this property applies to.
229 	 */
230 	datalink_media_t	pd_dmedia;
231 };
232 
233 #define	MAC_PROP_BUFSIZE(v)	sizeof (dld_ioc_macprop_t) + (v) - 1
234 
235 /*
236  * Supported link properties enumerated in the prop_table[] array are
237  * computed using the callback functions in that array. To compute the
238  * property value, multiple distinct system calls may be needed (e.g.,
239  * for wifi speed, we need to issue system calls to get desired/supported
240  * rates). The link_attr[] table enumerates the interfaces to the kernel,
241  * and the type/size of the data passed in the user-kernel interface.
242  */
243 static link_attr_t link_attr[] = {
244 	{ MAC_PROP_DUPLEX,	sizeof (link_duplex_t),	"duplex"},
245 
246 	{ MAC_PROP_SPEED,	sizeof (uint64_t),	"speed"},
247 
248 	{ MAC_PROP_STATUS,	sizeof (link_state_t),	"state"},
249 
250 	{ MAC_PROP_AUTONEG,	sizeof (uint8_t),	"adv_autoneg_cap"},
251 
252 	{ MAC_PROP_MTU,		sizeof (uint32_t),	"mtu"},
253 
254 	{ MAC_PROP_FLOWCTRL,	sizeof (link_flowctrl_t), "flowctrl"},
255 
256 	{ MAC_PROP_ZONE,	sizeof (dld_ioc_zid_t),	"zone"},
257 
258 	{ MAC_PROP_AUTOPUSH,	sizeof (struct dlautopush), "autopush"},
259 
260 	{ MAC_PROP_ADV_5000FDX_CAP, sizeof (uint8_t),	"adv_5000fdx_cap"},
261 
262 	{ MAC_PROP_EN_5000FDX_CAP, sizeof (uint8_t),	"en_5000fdx_cap"},
263 
264 	{ MAC_PROP_ADV_2500FDX_CAP, sizeof (uint8_t),	"adv_2500fdx_cap"},
265 
266 	{ MAC_PROP_EN_2500FDX_CAP, sizeof (uint8_t),	"en_2500fdx_cap"},
267 
268 	{ MAC_PROP_ADV_100GFDX_CAP, sizeof (uint8_t),	"adv_100gfdx_cap"},
269 
270 	{ MAC_PROP_EN_100GFDX_CAP, sizeof (uint8_t),	"en_100gfdx_cap"},
271 
272 	{ MAC_PROP_ADV_40GFDX_CAP, sizeof (uint8_t),	"adv_40gfdx_cap"},
273 
274 	{ MAC_PROP_EN_40GFDX_CAP, sizeof (uint8_t),	"en_40gfdx_cap"},
275 
276 	{ MAC_PROP_ADV_10GFDX_CAP, sizeof (uint8_t),	"adv_10gfdx_cap"},
277 
278 	{ MAC_PROP_EN_10GFDX_CAP, sizeof (uint8_t),	"en_10gfdx_cap"},
279 
280 	{ MAC_PROP_ADV_1000FDX_CAP, sizeof (uint8_t),	"adv_1000fdx_cap"},
281 
282 	{ MAC_PROP_EN_1000FDX_CAP, sizeof (uint8_t),	"en_1000fdx_cap"},
283 
284 	{ MAC_PROP_ADV_1000HDX_CAP, sizeof (uint8_t),	"adv_1000hdx_cap"},
285 
286 	{ MAC_PROP_EN_1000HDX_CAP, sizeof (uint8_t),	"en_1000hdx_cap"},
287 
288 	{ MAC_PROP_ADV_100FDX_CAP, sizeof (uint8_t),	"adv_100fdx_cap"},
289 
290 	{ MAC_PROP_EN_100FDX_CAP, sizeof (uint8_t),	"en_100fdx_cap"},
291 
292 	{ MAC_PROP_ADV_100HDX_CAP, sizeof (uint8_t),	"adv_100hdx_cap"},
293 
294 	{ MAC_PROP_EN_100HDX_CAP, sizeof (uint8_t),	"en_100hdx_cap"},
295 
296 	{ MAC_PROP_ADV_10FDX_CAP, sizeof (uint8_t),	"adv_10fdx_cap"},
297 
298 	{ MAC_PROP_EN_10FDX_CAP, sizeof (uint8_t),	"en_10fdx_cap"},
299 
300 	{ MAC_PROP_ADV_10HDX_CAP, sizeof (uint8_t),	"adv_10hdx_cap"},
301 
302 	{ MAC_PROP_EN_10HDX_CAP, sizeof (uint8_t),	"en_10hdx_cap"},
303 
304 	{ MAC_PROP_WL_ESSID,	sizeof (wl_linkstatus_t), "essid"},
305 
306 	{ MAC_PROP_WL_BSSID,	sizeof (wl_bssid_t),	"bssid"},
307 
308 	{ MAC_PROP_WL_BSSTYPE,	sizeof (wl_bss_type_t),	"bsstype"},
309 
310 	{ MAC_PROP_WL_LINKSTATUS, sizeof (wl_linkstatus_t), "wl_linkstatus"},
311 
312 	/* wl_rates_t has variable length */
313 	{ MAC_PROP_WL_DESIRED_RATES, sizeof (wl_rates_t), "desired_rates"},
314 
315 	/* wl_rates_t has variable length */
316 	{ MAC_PROP_WL_SUPPORTED_RATES, sizeof (wl_rates_t), "supported_rates"},
317 
318 	{ MAC_PROP_WL_AUTH_MODE, sizeof (wl_authmode_t), "authmode"},
319 
320 	{ MAC_PROP_WL_ENCRYPTION, sizeof (wl_encryption_t), "encryption"},
321 
322 	{ MAC_PROP_WL_RSSI,	sizeof (wl_rssi_t),	"signal"},
323 
324 	{ MAC_PROP_WL_PHY_CONFIG, sizeof (wl_phy_conf_t), "phy_conf"},
325 
326 	{ MAC_PROP_WL_CAPABILITY, sizeof (wl_capability_t), "capability"},
327 
328 	{ MAC_PROP_WL_WPA,	sizeof (wl_wpa_t),	"wpa"},
329 
330 	/*  wl_wpa_ess_t has variable length */
331 	{ MAC_PROP_WL_SCANRESULTS, sizeof (wl_wpa_ess_t), "scan_results"},
332 
333 	{ MAC_PROP_WL_POWER_MODE, sizeof (wl_ps_mode_t), "powermode"},
334 
335 	{ MAC_PROP_WL_RADIO,	sizeof (dladm_wlan_radio_t), "wl_radio"},
336 
337 	{ MAC_PROP_WL_ESS_LIST, sizeof (wl_ess_list_t),	"wl_ess_list"},
338 
339 	{ MAC_PROP_WL_KEY_TAB,	sizeof (wl_wep_key_tab_t), "wl_wep_key"},
340 
341 	{ MAC_PROP_WL_CREATE_IBSS, sizeof (wl_create_ibss_t), "createibss"},
342 
343 	/* wl_wpa_ie_t has variable length */
344 	{ MAC_PROP_WL_SETOPTIE,	sizeof (wl_wpa_ie_t),	"set_ie"},
345 
346 	{ MAC_PROP_WL_DELKEY,	sizeof (wl_del_key_t),	"wpa_del_key"},
347 
348 	{ MAC_PROP_WL_KEY,	sizeof (wl_key_t),	"wl_key"},
349 
350 	{ MAC_PROP_WL_MLME,	sizeof (wl_mlme_t),	"mlme"},
351 
352 	{ MAC_PROP_TAGMODE,	sizeof (link_tagmode_t),	"tagmode"},
353 
354 	{ MAC_PROP_IPTUN_HOPLIMIT, sizeof (uint32_t),	"hoplimit"},
355 
356 	{ MAC_PROP_IPTUN_ENCAPLIMIT, sizeof (uint32_t),	"encaplimit"},
357 
358 	{ MAC_PROP_PVID,	sizeof (uint16_t),	"default_tag"},
359 
360 	{ MAC_PROP_LLIMIT,	sizeof (uint32_t),	"learn_limit"},
361 
362 	{ MAC_PROP_LDECAY,	sizeof (uint32_t),	"learn_decay"},
363 
364 	{ MAC_PROP_RESOURCE,	sizeof (mac_resource_props_t),	"resource"},
365 
366 	{ MAC_PROP_RESOURCE_EFF, sizeof (mac_resource_props_t),
367 	    "resource-effective"},
368 
369 	{ MAC_PROP_RXRINGSRANGE, sizeof (mac_propval_range_t),	"rxrings"},
370 
371 	{ MAC_PROP_TXRINGSRANGE, sizeof (mac_propval_range_t),	"txrings"},
372 
373 	{ MAC_PROP_MAX_TX_RINGS_AVAIL,	sizeof (uint_t),
374 	    "txrings-available"},
375 
376 	{ MAC_PROP_MAX_RX_RINGS_AVAIL,	sizeof (uint_t),
377 	    "rxrings-available"},
378 
379 	{ MAC_PROP_MAX_RXHWCLNT_AVAIL,	sizeof (uint_t), "rxhwclnt-available"},
380 
381 	{ MAC_PROP_MAX_TXHWCLNT_AVAIL,	sizeof (uint_t), "txhwclnt-available"},
382 
383 	{ MAC_PROP_IB_LINKMODE,	sizeof (uint32_t),	"linkmode"},
384 
385 	{ MAC_PROP_SECONDARY_ADDRS, sizeof (mac_secondary_addr_t),
386 	    "secondary-macs"},
387 
388 	{ MAC_PROP_PRIVATE,	0,			"driver-private"}
389 };
390 
391 typedef struct bridge_public_prop_s {
392 	const char	*bpp_name;
393 	int		bpp_code;
394 } bridge_public_prop_t;
395 
396 static const bridge_public_prop_t bridge_prop[] = {
397 	{ "stp", PT_CFG_NON_STP },
398 	{ "stp_priority", PT_CFG_PRIO },
399 	{ "stp_cost", PT_CFG_COST },
400 	{ "stp_edge", PT_CFG_EDGE },
401 	{ "stp_p2p", PT_CFG_P2P },
402 	{ "stp_mcheck", PT_CFG_MCHECK },
403 	{ NULL, 0 }
404 };
405 
406 static  val_desc_t	link_duplex_vals[] = {
407 	{ "half", 	LINK_DUPLEX_HALF	},
408 	{ "full", 	LINK_DUPLEX_HALF	}
409 };
410 static  val_desc_t	link_status_vals[] = {
411 	{ "up",		LINK_STATE_UP		},
412 	{ "down",	LINK_STATE_DOWN		}
413 };
414 static  val_desc_t	link_01_vals[] = {
415 	{ "1",		1			},
416 	{ "0",		0			}
417 };
418 static  val_desc_t	link_flow_vals[] = {
419 	{ "no",		LINK_FLOWCTRL_NONE	},
420 	{ "tx",		LINK_FLOWCTRL_TX	},
421 	{ "rx",		LINK_FLOWCTRL_RX	},
422 	{ "bi",		LINK_FLOWCTRL_BI	}
423 };
424 static  val_desc_t	link_priority_vals[] = {
425 	{ "low",	MPL_LOW	},
426 	{ "medium",	MPL_MEDIUM	},
427 	{ "high",	MPL_HIGH	}
428 };
429 
430 static val_desc_t	link_tagmode_vals[] = {
431 	{ "normal",	LINK_TAGMODE_NORMAL	},
432 	{ "vlanonly",	LINK_TAGMODE_VLANONLY	}
433 };
434 
435 static  val_desc_t	link_protect_vals[] = {
436 	{ "mac-nospoof",	MPT_MACNOSPOOF	},
437 	{ "restricted",		MPT_RESTRICTED	},
438 	{ "ip-nospoof",		MPT_IPNOSPOOF	},
439 	{ "dhcp-nospoof",	MPT_DHCPNOSPOOF	},
440 };
441 
442 static val_desc_t	dladm_wlan_radio_vals[] = {
443 	{ "on",		DLADM_WLAN_RADIO_ON	},
444 	{ "off",	DLADM_WLAN_RADIO_OFF	}
445 };
446 
447 static val_desc_t	dladm_wlan_powermode_vals[] = {
448 	{ "off",	DLADM_WLAN_PM_OFF	},
449 	{ "fast",	DLADM_WLAN_PM_FAST	},
450 	{ "max",	DLADM_WLAN_PM_MAX	}
451 };
452 
453 static  val_desc_t	stp_p2p_vals[] = {
454 	{ "true",	P2P_FORCE_TRUE		},
455 	{ "false",	P2P_FORCE_FALSE		},
456 	{ "auto",	P2P_AUTO		}
457 };
458 
459 static  val_desc_t	dladm_part_linkmode_vals[] = {
460 	{ "cm",		DLADM_PART_CM_MODE	},
461 	{ "ud",		DLADM_PART_UD_MODE	},
462 };
463 
464 #define	VALCNT(vals)    (sizeof ((vals)) / sizeof (val_desc_t))
465 #define	RESET_VAL	((uintptr_t)-1)
466 #define	UNSPEC_VAL	((uintptr_t)-2)
467 
468 /*
469  * For the default, if defaults are not defined for the property,
470  * pd_defval.vd_name should be null. If the driver has to be contacted for the
471  * value, vd_name should be the empty string (""). Otherwise, dladm will
472  * just print whatever is in the table.
473  */
474 static prop_desc_t	prop_table[] = {
475 	{ "channel",	{ NULL, 0 },
476 	    NULL, 0, NULL, NULL,
477 	    get_channel, NULL, 0,
478 	    DATALINK_CLASS_PHYS, DL_WIFI },
479 
480 	{ "powermode",	{ "off", DLADM_WLAN_PM_OFF },
481 	    dladm_wlan_powermode_vals, VALCNT(dladm_wlan_powermode_vals),
482 	    set_powermode, NULL,
483 	    get_powermode, NULL, 0,
484 	    DATALINK_CLASS_PHYS, DL_WIFI },
485 
486 	{ "radio",	{ "on", DLADM_WLAN_RADIO_ON },
487 	    dladm_wlan_radio_vals, VALCNT(dladm_wlan_radio_vals),
488 	    set_radio, NULL,
489 	    get_radio, NULL, 0,
490 	    DATALINK_CLASS_PHYS, DL_WIFI },
491 
492 	{ "linkmode",	{ "cm", DLADM_PART_CM_MODE },
493 	    dladm_part_linkmode_vals, VALCNT(dladm_part_linkmode_vals),
494 	    set_public_prop, NULL, get_linkmode_prop, NULL, 0,
495 	    DATALINK_CLASS_PART, DL_IB },
496 
497 	{ "speed",	{ "", 0 }, NULL, 0,
498 	    set_rate, get_rate_mod,
499 	    get_rate, check_rate, 0,
500 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE },
501 
502 	{ "autopush",	{ "", 0 }, NULL, 0,
503 	    set_public_prop, NULL,
504 	    get_autopush, check_autopush, PD_CHECK_ALLOC,
505 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
506 
507 	{ "zone",	{ "", 0 }, NULL, 0,
508 	    set_zone, NULL,
509 	    get_zone, check_zone, PD_TEMPONLY|PD_CHECK_ALLOC,
510 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
511 
512 	{ "duplex",	{ "", 0 },
513 	    link_duplex_vals, VALCNT(link_duplex_vals),
514 	    NULL, NULL, get_duplex, NULL,
515 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
516 
517 	{ "state",	{ "up", LINK_STATE_UP },
518 	    link_status_vals, VALCNT(link_status_vals),
519 	    NULL, NULL, get_link_state, NULL,
520 	    0, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
521 
522 	{ "adv_autoneg_cap", { "", 0 },
523 	    link_01_vals, VALCNT(link_01_vals),
524 	    set_public_prop, NULL, get_binary, NULL,
525 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
526 
527 	{ "mtu", { "", 0 }, NULL, 0,
528 	    set_public_prop, get_range,
529 	    get_uint32, check_uint32, 0, DATALINK_CLASS_ALL,
530 	    DATALINK_ANY_MEDIATYPE },
531 
532 	{ "flowctrl", { "", 0 },
533 	    link_flow_vals, VALCNT(link_flow_vals),
534 	    set_public_prop, NULL, get_flowctl, NULL,
535 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
536 
537 	{ "secondary-macs", { "--", 0 }, NULL, 0,
538 	    set_secondary_macs, NULL,
539 	    get_secondary_macs, check_secondary_macs, PD_CHECK_ALLOC,
540 	    DATALINK_CLASS_VNIC, DL_ETHER },
541 
542 	{ "adv_100gfdx_cap", { "", 0 },
543 	    link_01_vals, VALCNT(link_01_vals),
544 	    NULL, NULL, get_binary, NULL,
545 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
546 
547 	{ "en_100gfdx_cap", { "", 0 },
548 	    link_01_vals, VALCNT(link_01_vals),
549 	    set_public_prop, NULL, get_binary, NULL,
550 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
551 
552 	{ "adv_40gfdx_cap", { "", 0 },
553 	    link_01_vals, VALCNT(link_01_vals),
554 	    NULL, NULL, get_binary, NULL,
555 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
556 
557 	{ "en_40gfdx_cap", { "", 0 },
558 	    link_01_vals, VALCNT(link_01_vals),
559 	    set_public_prop, NULL, get_binary, NULL,
560 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
561 
562 	{ "adv_10gfdx_cap", { "", 0 },
563 	    link_01_vals, VALCNT(link_01_vals),
564 	    NULL, NULL, get_binary, NULL,
565 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
566 
567 	{ "en_10gfdx_cap", { "", 0 },
568 	    link_01_vals, VALCNT(link_01_vals),
569 	    set_public_prop, NULL, get_binary, NULL,
570 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
571 
572 	{ "adv_5000fdx_cap", { "", 0 },
573 	    link_01_vals, VALCNT(link_01_vals),
574 	    NULL, NULL, get_binary, NULL,
575 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
576 
577 	{ "en_5000fdx_cap", { "", 0 },
578 	    link_01_vals, VALCNT(link_01_vals),
579 	    set_public_prop, NULL, get_binary, NULL,
580 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
581 
582 	{ "adv_2500fdx_cap", { "", 0 },
583 	    link_01_vals, VALCNT(link_01_vals),
584 	    NULL, NULL, get_binary, NULL,
585 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
586 
587 	{ "en_2500fdx_cap", { "", 0 },
588 	    link_01_vals, VALCNT(link_01_vals),
589 	    set_public_prop, NULL, get_binary, NULL,
590 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
591 
592 	{ "adv_1000fdx_cap", { "", 0 },
593 	    link_01_vals, VALCNT(link_01_vals),
594 	    NULL, NULL, get_binary, NULL,
595 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
596 
597 	{ "en_1000fdx_cap", { "", 0 },
598 	    link_01_vals, VALCNT(link_01_vals),
599 	    set_public_prop, NULL, get_binary, NULL,
600 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
601 
602 	{ "adv_1000hdx_cap", { "", 0 },
603 	    link_01_vals, VALCNT(link_01_vals),
604 	    NULL, NULL, get_binary, NULL,
605 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
606 
607 	{ "en_1000hdx_cap", { "", 0 },
608 	    link_01_vals, VALCNT(link_01_vals),
609 	    set_public_prop, NULL, get_binary, NULL,
610 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
611 
612 	{ "adv_100fdx_cap", { "", 0 },
613 	    link_01_vals, VALCNT(link_01_vals),
614 	    NULL, NULL, get_binary, NULL,
615 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
616 
617 	{ "en_100fdx_cap", { "", 0 },
618 	    link_01_vals, VALCNT(link_01_vals),
619 	    set_public_prop, NULL, get_binary, NULL,
620 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
621 
622 	{ "adv_100hdx_cap", { "", 0 },
623 	    link_01_vals, VALCNT(link_01_vals),
624 	    NULL, NULL, get_binary, NULL,
625 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
626 
627 	{ "en_100hdx_cap", { "", 0 },
628 	    link_01_vals, VALCNT(link_01_vals),
629 	    set_public_prop, NULL, get_binary, NULL,
630 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
631 
632 	{ "adv_10fdx_cap", { "", 0 },
633 	    link_01_vals, VALCNT(link_01_vals),
634 	    NULL, NULL, get_binary, NULL,
635 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
636 
637 	{ "en_10fdx_cap", { "", 0 },
638 	    link_01_vals, VALCNT(link_01_vals),
639 	    set_public_prop, NULL, get_binary, NULL,
640 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
641 
642 	{ "adv_10hdx_cap", { "", 0 },
643 	    link_01_vals, VALCNT(link_01_vals),
644 	    NULL, NULL, get_binary, NULL,
645 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
646 
647 	{ "en_10hdx_cap", { "", 0 },
648 	    link_01_vals, VALCNT(link_01_vals),
649 	    set_public_prop, NULL, get_binary, NULL,
650 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
651 
652 	{ "maxbw", { "--", RESET_VAL }, NULL, 0,
653 	    set_resource, NULL,
654 	    get_maxbw, check_maxbw, PD_CHECK_ALLOC,
655 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
656 
657 	{ "cpus", { "--", RESET_VAL }, NULL, 0,
658 	    set_resource, NULL,
659 	    get_cpus, check_cpus, 0,
660 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
661 
662 	{ "cpus-effective", { "--", 0 },
663 	    NULL, 0, NULL, NULL,
664 	    get_cpus, 0, 0,
665 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
666 
667 	{ "pool", { "--", RESET_VAL }, NULL, 0,
668 	    set_resource, NULL,
669 	    get_pool, check_pool, 0,
670 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
671 
672 	{ "pool-effective", { "--", 0 },
673 	    NULL, 0, NULL, NULL,
674 	    get_pool, 0, 0,
675 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
676 
677 	{ "priority", { "high", MPL_RESET },
678 	    link_priority_vals, VALCNT(link_priority_vals), set_resource,
679 	    NULL, get_priority, check_prop, 0,
680 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
681 
682 	{ "tagmode", { "vlanonly", LINK_TAGMODE_VLANONLY },
683 	    link_tagmode_vals, VALCNT(link_tagmode_vals),
684 	    set_public_prop, NULL, get_tagmode,
685 	    NULL, 0,
686 	    DATALINK_CLASS_PHYS | DATALINK_CLASS_AGGR | DATALINK_CLASS_VNIC,
687 	    DL_ETHER },
688 
689 	{ "hoplimit", { "", 0 }, NULL, 0,
690 	    set_public_prop, get_range, get_uint32,
691 	    check_hoplimit, 0, DATALINK_CLASS_IPTUN, DATALINK_ANY_MEDIATYPE},
692 
693 	{ "encaplimit", { "", 0 }, NULL, 0,
694 	    set_public_prop, get_range, get_uint32,
695 	    check_encaplim, 0, DATALINK_CLASS_IPTUN, DL_IPV6},
696 
697 	{ "forward", { "1", 1 },
698 	    link_01_vals, VALCNT(link_01_vals),
699 	    set_bridge_forward, NULL, get_bridge_forward, NULL, PD_AFTER_PERM,
700 	    DATALINK_CLASS_ALL & ~DATALINK_CLASS_VNIC, DL_ETHER },
701 
702 	{ "default_tag", { "1", 1 }, NULL, 0,
703 	    set_bridge_pvid, NULL, get_bridge_pvid, check_bridge_pvid,
704 	    0, DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
705 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
706 
707 	{ "learn_limit", { "1000", 1000 }, NULL, 0,
708 	    set_public_prop, NULL, get_uint32,
709 	    check_uint32, 0,
710 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
711 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
712 
713 	{ "learn_decay", { "200", 200 }, NULL, 0,
714 	    set_public_prop, NULL, get_uint32,
715 	    check_uint32, 0,
716 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
717 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
718 
719 	{ "stp", { "1", 1 },
720 	    link_01_vals, VALCNT(link_01_vals),
721 	    set_stp_prop, NULL, get_stp, NULL, PD_AFTER_PERM,
722 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
723 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
724 
725 	{ "stp_priority", { "128", 128 }, NULL, 0,
726 	    set_stp_prop, NULL, get_stp, check_stp_prop, PD_AFTER_PERM,
727 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
728 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
729 
730 	{ "stp_cost", { "auto", 0 }, NULL, 0,
731 	    set_stp_prop, NULL, get_stp, check_stp_prop, PD_AFTER_PERM,
732 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
733 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
734 
735 	{ "stp_edge", { "1", 1 },
736 	    link_01_vals, VALCNT(link_01_vals),
737 	    set_stp_prop, NULL, get_stp, NULL, PD_AFTER_PERM,
738 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
739 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
740 
741 	{ "stp_p2p", { "auto", P2P_AUTO },
742 	    stp_p2p_vals, VALCNT(stp_p2p_vals),
743 	    set_stp_prop, NULL, get_stp, NULL, PD_AFTER_PERM,
744 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
745 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
746 
747 	{ "stp_mcheck", { "0", 0 },
748 	    link_01_vals, VALCNT(link_01_vals),
749 	    set_stp_prop, NULL, get_stp, check_stp_prop, PD_AFTER_PERM,
750 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
751 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
752 
753 	{ "protection", { "--", RESET_VAL },
754 	    link_protect_vals, VALCNT(link_protect_vals),
755 	    set_resource, NULL, get_protection, check_prop, 0,
756 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
757 
758 	{ "allowed-ips", { "--", 0 },
759 	    NULL, 0, set_resource, NULL,
760 	    get_allowedips, check_allowedips, PD_CHECK_ALLOC,
761 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
762 
763 	{ "allowed-dhcp-cids", { "--", 0 },
764 	    NULL, 0, set_resource, NULL,
765 	    get_allowedcids, check_allowedcids, PD_CHECK_ALLOC,
766 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
767 
768 	{ "rxrings", { "--", RESET_VAL }, NULL, 0,
769 	    set_resource, get_rings_range, get_rxrings, check_rings, 0,
770 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
771 
772 	{ "rxrings-effective", { "--", 0 },
773 	    NULL, 0, NULL, NULL,
774 	    get_rxrings, NULL, 0,
775 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
776 
777 	{ "txrings", { "--", RESET_VAL }, NULL, 0,
778 	    set_resource, get_rings_range, get_txrings, check_rings, 0,
779 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
780 
781 	{ "txrings-effective", { "--", 0 },
782 	    NULL, 0, NULL, NULL,
783 	    get_txrings, NULL, 0,
784 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
785 
786 	{ "txrings-available", { "", 0 }, NULL, 0,
787 	    NULL, NULL, get_cntavail, NULL, 0,
788 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
789 
790 	{ "rxrings-available", { "", 0 }, NULL, 0,
791 	    NULL, NULL, get_cntavail, NULL, 0,
792 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
793 
794 	{ "rxhwclnt-available", { "", 0 }, NULL, 0,
795 	    NULL, NULL, get_cntavail, NULL, 0,
796 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
797 
798 	{ "txhwclnt-available", { "", 0 }, NULL, 0,
799 	    NULL, NULL, get_cntavail, NULL, 0,
800 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
801 
802 };
803 
804 #define	DLADM_MAX_PROPS	(sizeof (prop_table) / sizeof (prop_desc_t))
805 
806 static resource_prop_t rsrc_prop_table[] = {
807 	{"maxbw",		extract_maxbw},
808 	{"priority",		extract_priority},
809 	{"cpus",		extract_cpus},
810 	{"cpus-effective",	extract_cpus},
811 	{"pool",		extract_pool},
812 	{"pool-effective",	extract_pool},
813 	{"protection",		extract_protection},
814 	{"allowed-ips",		extract_allowedips},
815 	{"allowed-dhcp-cids",	extract_allowedcids},
816 	{"rxrings",		extract_rxrings},
817 	{"rxrings-effective",	extract_rxrings},
818 	{"txrings",		extract_txrings},
819 	{"txrings-effective",	extract_txrings}
820 };
821 #define	DLADM_MAX_RSRC_PROP (sizeof (rsrc_prop_table) / \
822 	sizeof (resource_prop_t))
823 
824 /*
825  * when retrieving  private properties, we pass down a buffer with
826  * DLADM_PROP_BUF_CHUNK of space for the driver to return the property value.
827  */
828 #define	DLADM_PROP_BUF_CHUNK	1024
829 
830 static dladm_status_t	i_dladm_set_linkprop_db(dladm_handle_t, datalink_id_t,
831 			    const char *, char **, uint_t);
832 static dladm_status_t	i_dladm_get_linkprop_db(dladm_handle_t, datalink_id_t,
833 			    const char *, char **, uint_t *);
834 static dladm_status_t	i_dladm_walk_linkprop_priv_db(dladm_handle_t,
835 			    datalink_id_t, void *, int (*)(dladm_handle_t,
836 			    datalink_id_t, const char *, void *));
837 static dladm_status_t	i_dladm_set_single_prop(dladm_handle_t, datalink_id_t,
838 			    datalink_class_t, uint32_t, prop_desc_t *, char **,
839 			    uint_t, uint_t);
840 static dladm_status_t	i_dladm_set_linkprop(dladm_handle_t, datalink_id_t,
841 			    const char *, char **, uint_t, uint_t);
842 static dladm_status_t	i_dladm_getset_defval(dladm_handle_t, prop_desc_t *,
843 			    datalink_id_t, datalink_media_t, uint_t);
844 
845 /*
846  * Unfortunately, MAX_SCAN_SUPPORT_RATES is too small to allow all
847  * rates to be retrieved. However, we cannot increase it at this
848  * time because it will break binary compatibility with unbundled
849  * WiFi drivers and utilities. So for now we define an additional
850  * constant, MAX_SUPPORT_RATES, to allow all rates to be retrieved.
851  */
852 #define	MAX_SUPPORT_RATES	64
853 
854 #define	AP_ANCHOR	"[anchor]"
855 #define	AP_DELIMITER	'.'
856 
857 /* ARGSUSED */
858 static dladm_status_t
859 check_prop(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
860     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
861     datalink_media_t media)
862 {
863 	int		i, j;
864 	uint_t		val_cnt = *val_cntp;
865 	val_desc_t	*vdp = *vdpp;
866 
867 	for (j = 0; j < val_cnt; j++) {
868 		for (i = 0; i < pdp->pd_noptval; i++) {
869 			if (strcasecmp(prop_val[j],
870 			    pdp->pd_optval[i].vd_name) == 0) {
871 				break;
872 			}
873 		}
874 		if (i == pdp->pd_noptval)
875 			return (DLADM_STATUS_BADVAL);
876 
877 		(void) memcpy(&vdp[j], &pdp->pd_optval[i], sizeof (val_desc_t));
878 	}
879 	return (DLADM_STATUS_OK);
880 }
881 
882 static dladm_status_t
883 i_dladm_set_single_prop(dladm_handle_t handle, datalink_id_t linkid,
884     datalink_class_t class, uint32_t media, prop_desc_t *pdp, char **prop_val,
885     uint_t val_cnt, uint_t flags)
886 {
887 	dladm_status_t	status = DLADM_STATUS_OK;
888 	val_desc_t	*vdp = NULL;
889 	boolean_t	needfree = B_FALSE;
890 	uint_t		cnt, i;
891 
892 	if (!(pdp->pd_class & class))
893 		return (DLADM_STATUS_BADARG);
894 
895 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
896 		return (DLADM_STATUS_BADARG);
897 
898 	if ((flags & DLADM_OPT_PERSIST) && (pdp->pd_flags & PD_TEMPONLY))
899 		return (DLADM_STATUS_TEMPONLY);
900 
901 	if (!(flags & DLADM_OPT_ACTIVE))
902 		return (DLADM_STATUS_OK);
903 
904 	if (pdp->pd_set == NULL)
905 		return (DLADM_STATUS_PROPRDONLY);
906 
907 	if (prop_val != NULL) {
908 		vdp = calloc(val_cnt, sizeof (val_desc_t));
909 		if (vdp == NULL)
910 			return (DLADM_STATUS_NOMEM);
911 
912 		if (pdp->pd_check != NULL) {
913 			needfree = ((pdp->pd_flags & PD_CHECK_ALLOC) != 0);
914 			status = pdp->pd_check(handle, pdp, linkid, prop_val,
915 			    &val_cnt, flags, &vdp, media);
916 		} else if (pdp->pd_optval != NULL) {
917 			status = check_prop(handle, pdp, linkid, prop_val,
918 			    &val_cnt, flags, &vdp, media);
919 		} else {
920 			status = DLADM_STATUS_BADARG;
921 		}
922 
923 		if (status != DLADM_STATUS_OK)
924 			goto done;
925 
926 		cnt = val_cnt;
927 	} else {
928 		boolean_t	defval = B_FALSE;
929 
930 		if (pdp->pd_defval.vd_name == NULL)
931 			return (DLADM_STATUS_NOTSUP);
932 
933 		cnt = 1;
934 		defval = (strlen(pdp->pd_defval.vd_name) > 0);
935 		if ((pdp->pd_flags & PD_CHECK_ALLOC) != 0 || defval) {
936 			if ((vdp = calloc(1, sizeof (val_desc_t))) == NULL)
937 				return (DLADM_STATUS_NOMEM);
938 
939 			if (defval) {
940 				(void) memcpy(vdp, &pdp->pd_defval,
941 				    sizeof (val_desc_t));
942 			} else if (pdp->pd_check != NULL) {
943 				status = pdp->pd_check(handle, pdp, linkid,
944 				    prop_val, &cnt, flags, &vdp, media);
945 				if (status != DLADM_STATUS_OK)
946 					goto done;
947 			}
948 		} else {
949 			status = i_dladm_getset_defval(handle, pdp, linkid,
950 			    media, flags);
951 			return (status);
952 		}
953 	}
954 	if (pdp->pd_flags & PD_AFTER_PERM)
955 		status = (flags & DLADM_OPT_PERSIST) ? DLADM_STATUS_OK :
956 		    DLADM_STATUS_PERMONLY;
957 	else
958 		status = pdp->pd_set(handle, pdp, linkid, vdp, cnt, flags,
959 		    media);
960 	if (needfree) {
961 		for (i = 0; i < cnt; i++)
962 			free((void *)((val_desc_t *)vdp + i)->vd_val);
963 	}
964 done:
965 	free(vdp);
966 	return (status);
967 }
968 
969 static dladm_status_t
970 i_dladm_set_linkprop(dladm_handle_t handle, datalink_id_t linkid,
971     const char *prop_name, char **prop_val, uint_t val_cnt, uint_t flags)
972 {
973 	int			i;
974 	boolean_t		found = B_FALSE;
975 	datalink_class_t	class;
976 	uint32_t		media;
977 	dladm_status_t		status = DLADM_STATUS_OK;
978 
979 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, &media,
980 	    NULL, 0);
981 	if (status != DLADM_STATUS_OK)
982 		return (status);
983 
984 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
985 		prop_desc_t	*pdp = &prop_table[i];
986 		dladm_status_t	s;
987 
988 		if (prop_name != NULL &&
989 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
990 			continue;
991 		found = B_TRUE;
992 		s = i_dladm_set_single_prop(handle, linkid, class, media, pdp,
993 		    prop_val, val_cnt, flags);
994 
995 		if (prop_name != NULL) {
996 			status = s;
997 			break;
998 		} else {
999 			if (s != DLADM_STATUS_OK &&
1000 			    s != DLADM_STATUS_NOTSUP)
1001 				status = s;
1002 		}
1003 	}
1004 	if (!found) {
1005 		if (prop_name[0] == '_') {
1006 			/* other private properties */
1007 			status = i_dladm_set_private_prop(handle, linkid,
1008 			    prop_name, prop_val, val_cnt, flags);
1009 		} else  {
1010 			status = DLADM_STATUS_NOTFOUND;
1011 		}
1012 	}
1013 	return (status);
1014 }
1015 
1016 /*
1017  * Set/reset link property for specific link
1018  */
1019 dladm_status_t
1020 dladm_set_linkprop(dladm_handle_t handle, datalink_id_t linkid,
1021     const char *prop_name, char **prop_val, uint_t val_cnt, uint_t flags)
1022 {
1023 	dladm_status_t	status = DLADM_STATUS_OK;
1024 
1025 	if ((linkid == DATALINK_INVALID_LINKID) || (flags == 0) ||
1026 	    (prop_val == NULL && val_cnt > 0) ||
1027 	    (prop_val != NULL && val_cnt == 0) ||
1028 	    (prop_name == NULL && prop_val != NULL)) {
1029 		return (DLADM_STATUS_BADARG);
1030 	}
1031 
1032 	/*
1033 	 * Check for valid link property against the flags passed
1034 	 * and set the link property when active flag is passed.
1035 	 */
1036 	status = i_dladm_set_linkprop(handle, linkid, prop_name, prop_val,
1037 	    val_cnt, flags);
1038 	if (status != DLADM_STATUS_OK)
1039 		return (status);
1040 
1041 	if (flags & DLADM_OPT_PERSIST) {
1042 		status = i_dladm_set_linkprop_db(handle, linkid, prop_name,
1043 		    prop_val, val_cnt);
1044 
1045 		if (status == DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) {
1046 			prop_desc_t *pdp = prop_table;
1047 			int i;
1048 
1049 			for (i = 0; i < DLADM_MAX_PROPS; i++, pdp++) {
1050 				if (!(pdp->pd_flags & PD_AFTER_PERM))
1051 					continue;
1052 				if (prop_name != NULL &&
1053 				    strcasecmp(prop_name, pdp->pd_name) != 0)
1054 					continue;
1055 				status = pdp->pd_set(handle, pdp, linkid, NULL,
1056 				    0, flags, 0);
1057 			}
1058 		}
1059 	}
1060 	return (status);
1061 }
1062 
1063 /*
1064  * Walk all link properties of the given specific link.
1065  *
1066  * Note: this function currently lacks the ability to walk _all_ private
1067  * properties if the link, because there is no kernel interface to
1068  * retrieve all known private property names. Once such an interface
1069  * is added, this function should be fixed accordingly.
1070  */
1071 dladm_status_t
1072 dladm_walk_linkprop(dladm_handle_t handle, datalink_id_t linkid, void *arg,
1073     int (*func)(dladm_handle_t, datalink_id_t, const char *, void *))
1074 {
1075 	dladm_status_t		status;
1076 	datalink_class_t	class;
1077 	uint_t			media;
1078 	int			i;
1079 
1080 	if (linkid == DATALINK_INVALID_LINKID || func == NULL)
1081 		return (DLADM_STATUS_BADARG);
1082 
1083 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, &media,
1084 	    NULL, 0);
1085 	if (status != DLADM_STATUS_OK)
1086 		return (status);
1087 
1088 	/* public */
1089 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
1090 		if (!(prop_table[i].pd_class & class))
1091 			continue;
1092 
1093 		if (!DATALINK_MEDIA_ACCEPTED(prop_table[i].pd_dmedia, media))
1094 			continue;
1095 
1096 		if (func(handle, linkid, prop_table[i].pd_name, arg) ==
1097 		    DLADM_WALK_TERMINATE) {
1098 			break;
1099 		}
1100 	}
1101 
1102 	/* private */
1103 	status = i_dladm_walk_linkprop_priv_db(handle, linkid, arg, func);
1104 
1105 	return (status);
1106 }
1107 
1108 /*
1109  * Get linkprop of the given specific link.
1110  */
1111 dladm_status_t
1112 dladm_get_linkprop(dladm_handle_t handle, datalink_id_t linkid,
1113     dladm_prop_type_t type, const char *prop_name, char **prop_val,
1114     uint_t *val_cntp)
1115 {
1116 	dladm_status_t		status = DLADM_STATUS_OK;
1117 	datalink_class_t	class;
1118 	uint_t			media;
1119 	prop_desc_t		*pdp;
1120 	uint_t			cnt, dld_flags = 0;
1121 	int			i;
1122 	uint_t			perm_flags;
1123 
1124 	if (type == DLADM_PROP_VAL_DEFAULT)
1125 		dld_flags |= DLD_PROP_DEFAULT;
1126 	else if (type == DLADM_PROP_VAL_MODIFIABLE)
1127 		dld_flags |= DLD_PROP_POSSIBLE;
1128 
1129 	if (linkid == DATALINK_INVALID_LINKID || prop_name == NULL ||
1130 	    prop_val == NULL || val_cntp == NULL || *val_cntp == 0)
1131 		return (DLADM_STATUS_BADARG);
1132 
1133 	for (i = 0; i < DLADM_MAX_PROPS; i++)
1134 		if (strcasecmp(prop_name, prop_table[i].pd_name) == 0)
1135 			break;
1136 
1137 	if (i == DLADM_MAX_PROPS) {
1138 		if (prop_name[0] == '_') {
1139 			/*
1140 			 * private property.
1141 			 */
1142 			if (type == DLADM_PROP_VAL_PERSISTENT)
1143 				return (i_dladm_get_linkprop_db(handle, linkid,
1144 				    prop_name, prop_val, val_cntp));
1145 			else
1146 				return (i_dladm_get_priv_prop(handle, linkid,
1147 				    prop_name, prop_val, val_cntp, type,
1148 				    dld_flags));
1149 		} else {
1150 			return (DLADM_STATUS_NOTFOUND);
1151 		}
1152 	}
1153 
1154 	pdp = &prop_table[i];
1155 
1156 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, &media,
1157 	    NULL, 0);
1158 	if (status != DLADM_STATUS_OK)
1159 		return (status);
1160 
1161 	if (!(pdp->pd_class & class))
1162 		return (DLADM_STATUS_BADARG);
1163 
1164 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
1165 		return (DLADM_STATUS_BADARG);
1166 
1167 	switch (type) {
1168 	case DLADM_PROP_VAL_CURRENT:
1169 		status = pdp->pd_get(handle, pdp, linkid, prop_val, val_cntp,
1170 		    media, dld_flags, &perm_flags);
1171 		break;
1172 
1173 	case DLADM_PROP_VAL_PERM:
1174 		if (pdp->pd_set == NULL) {
1175 			perm_flags = MAC_PROP_PERM_READ;
1176 		} else {
1177 			status = pdp->pd_get(handle, pdp, linkid, prop_val,
1178 			    val_cntp, media, dld_flags, &perm_flags);
1179 		}
1180 
1181 		*prop_val[0] = '\0';
1182 		*val_cntp = 1;
1183 		if (status == DLADM_STATUS_OK)
1184 			(void) dladm_perm2str(perm_flags, *prop_val);
1185 		break;
1186 
1187 	case DLADM_PROP_VAL_DEFAULT:
1188 		/*
1189 		 * If defaults are not defined for the property,
1190 		 * pd_defval.vd_name should be null. If the driver
1191 		 * has to be contacted for the value, vd_name should
1192 		 * be the empty string (""). Otherwise, dladm will
1193 		 * just print whatever is in the table.
1194 		 */
1195 		if (pdp->pd_defval.vd_name == NULL) {
1196 			status = DLADM_STATUS_NOTSUP;
1197 			break;
1198 		}
1199 
1200 		if (strlen(pdp->pd_defval.vd_name) == 0) {
1201 			status = pdp->pd_get(handle, pdp, linkid, prop_val,
1202 			    val_cntp, media, dld_flags, &perm_flags);
1203 		} else {
1204 			(void) strcpy(*prop_val, pdp->pd_defval.vd_name);
1205 		}
1206 		*val_cntp = 1;
1207 		break;
1208 
1209 	case DLADM_PROP_VAL_MODIFIABLE:
1210 		if (pdp->pd_getmod != NULL) {
1211 			status = pdp->pd_getmod(handle, pdp, linkid, prop_val,
1212 			    val_cntp, media, dld_flags, &perm_flags);
1213 			break;
1214 		}
1215 		cnt = pdp->pd_noptval;
1216 		if (cnt == 0) {
1217 			status = DLADM_STATUS_NOTSUP;
1218 		} else if (cnt > *val_cntp) {
1219 			status = DLADM_STATUS_TOOSMALL;
1220 		} else {
1221 			for (i = 0; i < cnt; i++) {
1222 				(void) strcpy(prop_val[i],
1223 				    pdp->pd_optval[i].vd_name);
1224 			}
1225 			*val_cntp = cnt;
1226 		}
1227 		break;
1228 	case DLADM_PROP_VAL_PERSISTENT:
1229 		if (pdp->pd_flags & PD_TEMPONLY)
1230 			return (DLADM_STATUS_TEMPONLY);
1231 		status = i_dladm_get_linkprop_db(handle, linkid, prop_name,
1232 		    prop_val, val_cntp);
1233 		break;
1234 	default:
1235 		status = DLADM_STATUS_BADARG;
1236 		break;
1237 	}
1238 
1239 	return (status);
1240 }
1241 
1242 /*
1243  * Get linkprop of the given specific link and run any possible conversion
1244  * of the values using the check function for the property. Fails if the
1245  * check function doesn't succeed for the property value.
1246  */
1247 dladm_status_t
1248 dladm_get_linkprop_values(dladm_handle_t handle, datalink_id_t linkid,
1249     dladm_prop_type_t type, const char *prop_name, uint_t *ret_val,
1250     uint_t *val_cntp)
1251 {
1252 	dladm_status_t		status;
1253 	datalink_class_t	class;
1254 	uint_t			media;
1255 	prop_desc_t		*pdp;
1256 	uint_t			dld_flags;
1257 	int			valc, i;
1258 	char			**prop_val;
1259 	uint_t			perm_flags;
1260 
1261 	if (linkid == DATALINK_INVALID_LINKID || prop_name == NULL ||
1262 	    ret_val == NULL || val_cntp == NULL || *val_cntp == 0)
1263 		return (DLADM_STATUS_BADARG);
1264 
1265 	for (pdp = prop_table; pdp < prop_table + DLADM_MAX_PROPS; pdp++)
1266 		if (strcasecmp(prop_name, pdp->pd_name) == 0)
1267 			break;
1268 
1269 	if (pdp == prop_table + DLADM_MAX_PROPS)
1270 		return (DLADM_STATUS_NOTFOUND);
1271 
1272 	if (pdp->pd_flags & PD_CHECK_ALLOC)
1273 		return (DLADM_STATUS_BADARG);
1274 
1275 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, &media,
1276 	    NULL, 0);
1277 	if (status != DLADM_STATUS_OK)
1278 		return (status);
1279 
1280 	if (!(pdp->pd_class & class))
1281 		return (DLADM_STATUS_BADARG);
1282 
1283 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
1284 		return (DLADM_STATUS_BADARG);
1285 
1286 	prop_val = malloc(*val_cntp * sizeof (*prop_val) +
1287 	    *val_cntp * DLADM_PROP_VAL_MAX);
1288 	if (prop_val == NULL)
1289 		return (DLADM_STATUS_NOMEM);
1290 	for (valc = 0; valc < *val_cntp; valc++)
1291 		prop_val[valc] = (char *)(prop_val + *val_cntp) +
1292 		    valc * DLADM_PROP_VAL_MAX;
1293 
1294 	dld_flags = (type == DLADM_PROP_VAL_DEFAULT) ? DLD_PROP_DEFAULT : 0;
1295 
1296 	switch (type) {
1297 	case DLADM_PROP_VAL_CURRENT:
1298 		status = pdp->pd_get(handle, pdp, linkid, prop_val, val_cntp,
1299 		    media, dld_flags, &perm_flags);
1300 		break;
1301 
1302 	case DLADM_PROP_VAL_DEFAULT:
1303 		/*
1304 		 * If defaults are not defined for the property,
1305 		 * pd_defval.vd_name should be null. If the driver
1306 		 * has to be contacted for the value, vd_name should
1307 		 * be the empty string (""). Otherwise, dladm will
1308 		 * just print whatever is in the table.
1309 		 */
1310 		if (pdp->pd_defval.vd_name == NULL) {
1311 			status = DLADM_STATUS_NOTSUP;
1312 			break;
1313 		}
1314 
1315 		if (pdp->pd_defval.vd_name[0] != '\0') {
1316 			*val_cntp = 1;
1317 			*ret_val = pdp->pd_defval.vd_val;
1318 			free(prop_val);
1319 			return (DLADM_STATUS_OK);
1320 		}
1321 		status = pdp->pd_get(handle, pdp, linkid, prop_val, val_cntp,
1322 		    media, dld_flags, &perm_flags);
1323 		break;
1324 
1325 	case DLADM_PROP_VAL_PERSISTENT:
1326 		if (pdp->pd_flags & PD_TEMPONLY)
1327 			status = DLADM_STATUS_TEMPONLY;
1328 		else
1329 			status = i_dladm_get_linkprop_db(handle, linkid,
1330 			    prop_name, prop_val, val_cntp);
1331 		break;
1332 
1333 	default:
1334 		status = DLADM_STATUS_BADARG;
1335 		break;
1336 	}
1337 
1338 	if (status == DLADM_STATUS_OK) {
1339 		if (pdp->pd_check != NULL) {
1340 			val_desc_t *vdp;
1341 
1342 			vdp = malloc(sizeof (val_desc_t) * *val_cntp);
1343 			if (vdp == NULL)
1344 				status = DLADM_STATUS_NOMEM;
1345 			else
1346 				status = pdp->pd_check(handle, pdp, linkid,
1347 				    prop_val, val_cntp, 0, &vdp, media);
1348 			if (status == DLADM_STATUS_OK) {
1349 				for (valc = 0; valc < *val_cntp; valc++)
1350 					ret_val[valc] = vdp[valc].vd_val;
1351 			}
1352 			free(vdp);
1353 		} else {
1354 			for (valc = 0; valc < *val_cntp; valc++) {
1355 				for (i = 0; i < pdp->pd_noptval; i++) {
1356 					if (strcmp(pdp->pd_optval[i].vd_name,
1357 					    prop_val[valc]) == 0) {
1358 						ret_val[valc] =
1359 						    pdp->pd_optval[i].vd_val;
1360 						break;
1361 					}
1362 				}
1363 				if (i == pdp->pd_noptval) {
1364 					status = DLADM_STATUS_FAILED;
1365 					break;
1366 				}
1367 			}
1368 		}
1369 	}
1370 
1371 	free(prop_val);
1372 
1373 	return (status);
1374 }
1375 
1376 /*ARGSUSED*/
1377 static int
1378 i_dladm_init_one_prop(dladm_handle_t handle, datalink_id_t linkid,
1379     const char *prop_name, void *arg)
1380 {
1381 	char			*buf, **propvals;
1382 	uint_t			i, valcnt = DLADM_MAX_PROP_VALCNT;
1383 	dladm_status_t		status;
1384 	dladm_linkprop_args_t	*dla = arg;
1385 
1386 	if ((buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
1387 	    DLADM_MAX_PROP_VALCNT)) == NULL) {
1388 		return (DLADM_WALK_CONTINUE);
1389 	}
1390 
1391 	propvals = (char **)(void *)buf;
1392 	for (i = 0; i < valcnt; i++) {
1393 		propvals[i] = buf +
1394 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
1395 		    i * DLADM_PROP_VAL_MAX;
1396 	}
1397 
1398 	if (dladm_get_linkprop(handle, linkid, DLADM_PROP_VAL_PERSISTENT,
1399 	    prop_name, propvals, &valcnt) != DLADM_STATUS_OK) {
1400 		goto done;
1401 	}
1402 
1403 	status = dladm_set_linkprop(handle, linkid, prop_name, propvals,
1404 	    valcnt, dla->dla_flags | DLADM_OPT_ACTIVE);
1405 
1406 	if (status != DLADM_STATUS_OK)
1407 		dla->dla_status = status;
1408 
1409 done:
1410 	if (buf != NULL)
1411 		free(buf);
1412 
1413 	return (DLADM_WALK_CONTINUE);
1414 }
1415 
1416 /*ARGSUSED*/
1417 static int
1418 i_dladm_init_linkprop(dladm_handle_t handle, datalink_id_t linkid, void *arg)
1419 {
1420 	datalink_class_t	class;
1421 	dladm_status_t		status;
1422 
1423 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, NULL,
1424 	    NULL, 0);
1425 	if (status != DLADM_STATUS_OK)
1426 		return (DLADM_WALK_TERMINATE);
1427 
1428 	if ((class & (DATALINK_CLASS_VNIC | DATALINK_CLASS_VLAN)) == 0)
1429 		(void) dladm_init_linkprop(handle, linkid, B_TRUE);
1430 
1431 	return (DLADM_WALK_CONTINUE);
1432 }
1433 
1434 dladm_status_t
1435 dladm_init_linkprop(dladm_handle_t handle, datalink_id_t linkid,
1436     boolean_t any_media)
1437 {
1438 	dladm_status_t		status = DLADM_STATUS_OK;
1439 	datalink_media_t	dmedia;
1440 	uint32_t		media;
1441 	dladm_linkprop_args_t	*dla;
1442 
1443 	dmedia = any_media ? DATALINK_ANY_MEDIATYPE : DL_WIFI;
1444 
1445 	dla = malloc(sizeof (dladm_linkprop_args_t));
1446 	if (dla == NULL)
1447 		return (DLADM_STATUS_NOMEM);
1448 	dla->dla_flags = DLADM_OPT_BOOT;
1449 	dla->dla_status = DLADM_STATUS_OK;
1450 
1451 	if (linkid == DATALINK_ALL_LINKID) {
1452 		(void) dladm_walk_datalink_id(i_dladm_init_linkprop, handle,
1453 		    NULL, DATALINK_CLASS_ALL, dmedia, DLADM_OPT_PERSIST);
1454 	} else if (any_media ||
1455 	    ((dladm_datalink_id2info(handle, linkid, NULL, NULL, &media, NULL,
1456 	    0) == DLADM_STATUS_OK) &&
1457 	    DATALINK_MEDIA_ACCEPTED(dmedia, media))) {
1458 		(void) dladm_walk_linkprop(handle, linkid, (void *)dla,
1459 		    i_dladm_init_one_prop);
1460 		status = dla->dla_status;
1461 	}
1462 	free(dla);
1463 	return (status);
1464 }
1465 
1466 /* ARGSUSED */
1467 static dladm_status_t
1468 get_zone(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1469     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1470     uint_t flags, uint_t *perm_flags)
1471 {
1472 	char			zone_name[ZONENAME_MAX];
1473 	zoneid_t		zid;
1474 	dladm_status_t		status;
1475 
1476 	if (flags != 0)
1477 		return (DLADM_STATUS_NOTSUP);
1478 
1479 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
1480 	    perm_flags, &zid, sizeof (zid));
1481 	if (status != DLADM_STATUS_OK)
1482 		return (status);
1483 
1484 	*val_cnt = 1;
1485 	if (zid != GLOBAL_ZONEID) {
1486 		if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0) {
1487 			return (dladm_errno2status(errno));
1488 		}
1489 
1490 		(void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX);
1491 	} else {
1492 		*prop_val[0] = '\0';
1493 	}
1494 
1495 	return (DLADM_STATUS_OK);
1496 }
1497 
1498 typedef int (*zone_get_devroot_t)(char *, char *, size_t);
1499 
1500 static int
1501 i_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen)
1502 {
1503 	char			root[MAXPATHLEN];
1504 	zone_get_devroot_t	real_zone_get_devroot;
1505 	void			*dlhandle;
1506 	void			*sym;
1507 	int			ret;
1508 
1509 	if ((dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY)) == NULL)
1510 		return (-1);
1511 
1512 	if ((sym = dlsym(dlhandle, "zone_get_devroot")) == NULL) {
1513 		(void) dlclose(dlhandle);
1514 		return (-1);
1515 	}
1516 
1517 	real_zone_get_devroot = (zone_get_devroot_t)sym;
1518 
1519 	if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0)
1520 		(void) snprintf(dev, devlen, "%s%s", root, "/dev");
1521 	(void) dlclose(dlhandle);
1522 	return (ret);
1523 }
1524 
1525 static dladm_status_t
1526 i_dladm_update_deventry(dladm_handle_t handle, zoneid_t zid,
1527     datalink_id_t linkid, boolean_t add)
1528 {
1529 	char		path[MAXPATHLEN];
1530 	char		name[MAXLINKNAMELEN];
1531 	di_prof_t	prof = NULL;
1532 	char		zone_name[ZONENAME_MAX];
1533 	dladm_status_t	status;
1534 	int		ret;
1535 
1536 	if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
1537 		return (dladm_errno2status(errno));
1538 	if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
1539 		return (dladm_errno2status(errno));
1540 	if (di_prof_init(path, &prof) != 0)
1541 		return (dladm_errno2status(errno));
1542 
1543 	status = dladm_linkid2legacyname(handle, linkid, name, MAXLINKNAMELEN);
1544 	if (status != DLADM_STATUS_OK)
1545 		goto cleanup;
1546 
1547 	if (add)
1548 		ret = di_prof_add_dev(prof, name);
1549 	else
1550 		ret = di_prof_add_exclude(prof, name);
1551 
1552 	if (ret != 0) {
1553 		status = dladm_errno2status(errno);
1554 		goto cleanup;
1555 	}
1556 
1557 	if (di_prof_commit(prof) != 0)
1558 		status = dladm_errno2status(errno);
1559 cleanup:
1560 	if (prof)
1561 		di_prof_fini(prof);
1562 
1563 	return (status);
1564 }
1565 
1566 /* ARGSUSED */
1567 static dladm_status_t
1568 set_zone(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1569     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
1570 {
1571 	dladm_status_t		status = DLADM_STATUS_OK;
1572 	zoneid_t		zid_old, zid_new;
1573 	dld_ioc_zid_t		*dzp;
1574 
1575 	if (val_cnt != 1)
1576 		return (DLADM_STATUS_BADVALCNT);
1577 
1578 	dzp = (dld_ioc_zid_t *)vdp->vd_val;
1579 
1580 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
1581 	    NULL, &zid_old, sizeof (zid_old));
1582 	if (status != DLADM_STATUS_OK)
1583 		return (status);
1584 
1585 	zid_new = dzp->diz_zid;
1586 	if (zid_new == zid_old)
1587 		return (DLADM_STATUS_OK);
1588 
1589 	if ((status = set_public_prop(handle, pdp, linkid, vdp, val_cnt,
1590 	    flags, media)) != DLADM_STATUS_OK)
1591 		return (status);
1592 
1593 	/*
1594 	 * It is okay to fail to update the /dev entry (some vanity-named
1595 	 * links do not have a /dev entry).
1596 	 */
1597 	if (zid_old != GLOBAL_ZONEID) {
1598 		(void) i_dladm_update_deventry(handle, zid_old, linkid,
1599 		    B_FALSE);
1600 	}
1601 	if (zid_new != GLOBAL_ZONEID)
1602 		(void) i_dladm_update_deventry(handle, zid_new, linkid, B_TRUE);
1603 
1604 	return (DLADM_STATUS_OK);
1605 }
1606 
1607 /* ARGSUSED */
1608 static dladm_status_t
1609 check_zone(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1610     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
1611     datalink_media_t media)
1612 {
1613 	char		*zone_name;
1614 	zoneid_t	zoneid;
1615 	dladm_status_t	status = DLADM_STATUS_OK;
1616 	dld_ioc_zid_t	*dzp;
1617 	uint_t		val_cnt = *val_cntp;
1618 	val_desc_t	*vdp = *vdpp;
1619 
1620 	if (val_cnt != 1)
1621 		return (DLADM_STATUS_BADVALCNT);
1622 
1623 	dzp = malloc(sizeof (dld_ioc_zid_t));
1624 	if (dzp == NULL)
1625 		return (DLADM_STATUS_NOMEM);
1626 
1627 	zone_name = (prop_val != NULL) ? *prop_val : GLOBAL_ZONENAME;
1628 	if ((zoneid = getzoneidbyname(zone_name)) == -1) {
1629 		status = DLADM_STATUS_BADVAL;
1630 		goto done;
1631 	}
1632 
1633 	if (zoneid != GLOBAL_ZONEID) {
1634 		ushort_t	flags;
1635 
1636 		if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &flags,
1637 		    sizeof (flags)) < 0) {
1638 			status = dladm_errno2status(errno);
1639 			goto done;
1640 		}
1641 
1642 		if (!(flags & ZF_NET_EXCL)) {
1643 			status = DLADM_STATUS_BADVAL;
1644 			goto done;
1645 		}
1646 	}
1647 
1648 	(void) memset(dzp, 0, sizeof (dld_ioc_zid_t));
1649 
1650 	dzp->diz_zid = zoneid;
1651 	dzp->diz_linkid = linkid;
1652 
1653 	vdp->vd_val = (uintptr_t)dzp;
1654 	return (DLADM_STATUS_OK);
1655 done:
1656 	free(dzp);
1657 	return (status);
1658 }
1659 
1660 /* ARGSUSED */
1661 static dladm_status_t
1662 get_maxbw(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1663     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1664     uint_t flags, uint_t *perm_flags)
1665 {
1666 	mac_resource_props_t	mrp;
1667 	dladm_status_t		status;
1668 
1669 	status = i_dladm_get_public_prop(handle, linkid, "resource", flags,
1670 	    perm_flags, &mrp, sizeof (mrp));
1671 	if (status != DLADM_STATUS_OK)
1672 		return (status);
1673 
1674 	if ((mrp.mrp_mask & MRP_MAXBW) == 0) {
1675 		*val_cnt = 0;
1676 		return (DLADM_STATUS_OK);
1677 	}
1678 
1679 	(void) dladm_bw2str(mrp.mrp_maxbw, prop_val[0]);
1680 	*val_cnt = 1;
1681 	return (DLADM_STATUS_OK);
1682 }
1683 
1684 /* ARGSUSED */
1685 static dladm_status_t
1686 check_maxbw(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1687     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
1688     datalink_media_t media)
1689 {
1690 	uint64_t	*maxbw;
1691 	dladm_status_t	status = DLADM_STATUS_OK;
1692 	uint_t		val_cnt = *val_cntp;
1693 	val_desc_t	*vdp = *vdpp;
1694 
1695 	if (val_cnt != 1)
1696 		return (DLADM_STATUS_BADVALCNT);
1697 
1698 	maxbw = malloc(sizeof (uint64_t));
1699 	if (maxbw == NULL)
1700 		return (DLADM_STATUS_NOMEM);
1701 
1702 	status = dladm_str2bw(*prop_val, maxbw);
1703 	if (status != DLADM_STATUS_OK) {
1704 		free(maxbw);
1705 		return (status);
1706 	}
1707 
1708 	if ((*maxbw < MRP_MAXBW_MINVAL) && (*maxbw != 0)) {
1709 		free(maxbw);
1710 		return (DLADM_STATUS_MINMAXBW);
1711 	}
1712 
1713 	vdp->vd_val = (uintptr_t)maxbw;
1714 	return (DLADM_STATUS_OK);
1715 }
1716 
1717 /* ARGSUSED */
1718 dladm_status_t
1719 extract_maxbw(val_desc_t *vdp, uint_t cnt, void *arg)
1720 {
1721 	mac_resource_props_t *mrp = arg;
1722 
1723 	if (vdp->vd_val == RESET_VAL) {
1724 		mrp->mrp_maxbw = MRP_MAXBW_RESETVAL;
1725 	} else {
1726 		bcopy((char *)vdp->vd_val, &mrp->mrp_maxbw, sizeof (uint64_t));
1727 	}
1728 	mrp->mrp_mask |= MRP_MAXBW;
1729 
1730 	return (DLADM_STATUS_OK);
1731 }
1732 
1733 /* ARGSUSED */
1734 static dladm_status_t
1735 get_cpus(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1736     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1737     uint_t flags, uint_t *perm_flags)
1738 {
1739 	dladm_status_t		status;
1740 	mac_resource_props_t	mrp;
1741 	mac_propval_range_t	*pv_range;
1742 	int			err;
1743 
1744 	if (strcmp(pdp->pd_name, "cpus-effective") == 0) {
1745 		status = i_dladm_get_public_prop(handle, linkid,
1746 		    "resource-effective", flags, perm_flags, &mrp,
1747 		    sizeof (mrp));
1748 	} else {
1749 		status = i_dladm_get_public_prop(handle, linkid,
1750 		    "resource", flags, perm_flags, &mrp, sizeof (mrp));
1751 	}
1752 
1753 	if (status != DLADM_STATUS_OK)
1754 		return (status);
1755 
1756 	if (mrp.mrp_ncpus > *val_cnt)
1757 		return (DLADM_STATUS_TOOSMALL);
1758 
1759 	if (mrp.mrp_ncpus == 0) {
1760 		*val_cnt = 0;
1761 		return (DLADM_STATUS_OK);
1762 	}
1763 
1764 	/* Sort CPU list and convert it to a mac_propval_range */
1765 	status = dladm_list2range(mrp.mrp_cpu, mrp.mrp_ncpus,
1766 	    MAC_PROPVAL_UINT32, &pv_range);
1767 	if (status != DLADM_STATUS_OK)
1768 		return (status);
1769 
1770 	/* Write CPU ranges and individual CPUs */
1771 	err = dladm_range2strs(pv_range, prop_val);
1772 	if (err != 0) {
1773 		free(pv_range);
1774 		return (dladm_errno2status(err));
1775 	}
1776 
1777 	*val_cnt = pv_range->mpr_count;
1778 	free(pv_range);
1779 
1780 	return (DLADM_STATUS_OK);
1781 }
1782 
1783 /* ARGSUSED */
1784 static dladm_status_t
1785 check_cpus(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1786     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
1787     datalink_media_t media)
1788 {
1789 	int			i, j, rc;
1790 	long			nproc = sysconf(_SC_NPROCESSORS_CONF);
1791 	mac_resource_props_t	mrp;
1792 	mac_propval_range_t	*pv_range;
1793 	uint_t			perm_flags;
1794 	uint32_t		ncpus;
1795 	uint32_t		*cpus = mrp.mrp_cpu;
1796 	val_desc_t		*vdp = *vdpp;
1797 	val_desc_t		*newvdp;
1798 	uint_t			val_cnt = *val_cntp;
1799 	dladm_status_t		status = DLADM_STATUS_OK;
1800 
1801 	/* Get the current pool property */
1802 	status = i_dladm_get_public_prop(handle, linkid, "resource", 0,
1803 	    &perm_flags, &mrp, sizeof (mrp));
1804 
1805 	if (status == DLADM_STATUS_OK) {
1806 		/* Can't set cpus if a pool is set */
1807 		if (strlen(mrp.mrp_pool) != 0)
1808 			return (DLADM_STATUS_POOLCPU);
1809 	}
1810 
1811 	/* Read ranges and convert to mac_propval_range */
1812 	status = dladm_strs2range(prop_val, val_cnt, MAC_PROPVAL_UINT32,
1813 	    &pv_range);
1814 	if (status != DLADM_STATUS_OK)
1815 		goto done1;
1816 
1817 	/* Convert mac_propval_range to a single CPU list */
1818 	ncpus = MRP_NCPUS;
1819 	status = dladm_range2list(pv_range, cpus, &ncpus);
1820 	if (status != DLADM_STATUS_OK)
1821 		goto done1;
1822 
1823 	/*
1824 	 * If a range of CPUs was entered, update value count and reallocate
1825 	 * the array of val_desc_t's.  The array allocated was sized for
1826 	 * indvidual elements, but needs to be reallocated to accomodate the
1827 	 * expanded list of CPUs.
1828 	 */
1829 	if (val_cnt < ncpus) {
1830 		newvdp = calloc(*val_cntp, sizeof (val_desc_t));
1831 		if (newvdp == NULL) {
1832 			status = DLADM_STATUS_NOMEM;
1833 			goto done1;
1834 		}
1835 		vdp = newvdp;
1836 	}
1837 
1838 	/* Check if all CPUs in the list are online */
1839 	for (i = 0; i < ncpus; i++) {
1840 		if (cpus[i] >= nproc) {
1841 			status = DLADM_STATUS_BADCPUID;
1842 			goto done2;
1843 		}
1844 
1845 		rc = p_online(cpus[i], P_STATUS);
1846 		if (rc < 1) {
1847 			status = DLADM_STATUS_CPUERR;
1848 			goto done2;
1849 		}
1850 
1851 		if (rc != P_ONLINE) {
1852 			status = DLADM_STATUS_CPUNOTONLINE;
1853 			goto done2;
1854 		}
1855 
1856 		vdp[i].vd_val = (uintptr_t)cpus[i];
1857 	}
1858 
1859 	/* Check for duplicate CPUs */
1860 	for (i = 0; i < *val_cntp; i++) {
1861 		for (j = 0; j < *val_cntp; j++) {
1862 			if (i != j && vdp[i].vd_val == vdp[j].vd_val) {
1863 				status = DLADM_STATUS_BADVAL;
1864 				goto done2;
1865 			}
1866 		}
1867 	}
1868 
1869 	/* Update *val_cntp and *vdpp if everything was OK */
1870 	if (val_cnt < ncpus) {
1871 		*val_cntp = ncpus;
1872 		free(*vdpp);
1873 		*vdpp = newvdp;
1874 	}
1875 
1876 	status = DLADM_STATUS_OK;
1877 	goto done1;
1878 
1879 done2:
1880 	free(newvdp);
1881 done1:
1882 	free(pv_range);
1883 	return (status);
1884 }
1885 
1886 /* ARGSUSED */
1887 dladm_status_t
1888 extract_cpus(val_desc_t *vdp, uint_t cnt, void *arg)
1889 {
1890 	mac_resource_props_t	*mrp = arg;
1891 	int			i;
1892 
1893 	if (vdp[0].vd_val == RESET_VAL) {
1894 		bzero(&mrp->mrp_cpus, sizeof (mac_cpus_t));
1895 		mrp->mrp_mask |= MRP_CPUS;
1896 		return (DLADM_STATUS_OK);
1897 	}
1898 
1899 	for (i = 0; i < cnt; i++)
1900 		mrp->mrp_cpu[i] = (uint32_t)vdp[i].vd_val;
1901 
1902 	mrp->mrp_ncpus = cnt;
1903 	mrp->mrp_mask |= (MRP_CPUS|MRP_CPUS_USERSPEC);
1904 	mrp->mrp_fanout_mode = MCM_CPUS;
1905 	mrp->mrp_rx_intr_cpu = -1;
1906 
1907 	return (DLADM_STATUS_OK);
1908 }
1909 
1910 /*
1911  * Get the pool datalink property from the kernel.  This is used
1912  * for both the user specified pool and effective pool properties.
1913  */
1914 /* ARGSUSED */
1915 static dladm_status_t
1916 get_pool(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1917     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1918     uint_t flags, uint_t *perm_flags)
1919 {
1920 	mac_resource_props_t	mrp;
1921 	dladm_status_t		status;
1922 
1923 	if (strcmp(pdp->pd_name, "pool-effective") == 0) {
1924 		status = i_dladm_get_public_prop(handle, linkid,
1925 		    "resource-effective", flags, perm_flags, &mrp,
1926 		    sizeof (mrp));
1927 	} else {
1928 		status = i_dladm_get_public_prop(handle, linkid,
1929 		    "resource", flags, perm_flags, &mrp, sizeof (mrp));
1930 	}
1931 
1932 	if (status != DLADM_STATUS_OK)
1933 		return (status);
1934 
1935 	if (strlen(mrp.mrp_pool) == 0) {
1936 		(*prop_val)[0] = '\0';
1937 	} else {
1938 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
1939 		    "%s", mrp.mrp_pool);
1940 	}
1941 	*val_cnt = 1;
1942 
1943 	return (DLADM_STATUS_OK);
1944 }
1945 
1946 /* ARGSUSED */
1947 static dladm_status_t
1948 check_pool(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1949     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
1950     datalink_media_t media)
1951 {
1952 	pool_conf_t		*poolconf;
1953 	pool_t			*pool;
1954 	mac_resource_props_t	mrp;
1955 	dladm_status_t		status;
1956 	uint_t			perm_flags;
1957 	char			*poolname;
1958 	val_desc_t		*vdp = *vdpp;
1959 
1960 	/* Get the current cpus property */
1961 	status = i_dladm_get_public_prop(handle, linkid, "resource", 0,
1962 	    &perm_flags, &mrp, sizeof (mrp));
1963 
1964 	if (status == DLADM_STATUS_OK) {
1965 		/* Can't set pool if cpus are set */
1966 		if (mrp.mrp_ncpus != 0)
1967 			return (DLADM_STATUS_POOLCPU);
1968 	}
1969 
1970 	poolname = malloc(sizeof (mrp.mrp_pool));
1971 	if (poolname == NULL)
1972 		return (DLADM_STATUS_NOMEM);
1973 
1974 	/* Check for pool's availability if not booting */
1975 	if ((flags & DLADM_OPT_BOOT) == 0) {
1976 
1977 		/* Allocate and open pool configuration */
1978 		if ((poolconf = pool_conf_alloc()) == NULL)
1979 			return (DLADM_STATUS_BADVAL);
1980 
1981 		if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY)
1982 		    != PO_SUCCESS) {
1983 			pool_conf_free(poolconf);
1984 			return (DLADM_STATUS_BADVAL);
1985 		}
1986 
1987 		/* Look for pool name */
1988 		if ((pool = pool_get_pool(poolconf, *prop_val)) == NULL) {
1989 			pool_conf_free(poolconf);
1990 			return (DLADM_STATUS_BADVAL);
1991 		}
1992 
1993 		pool_conf_free(poolconf);
1994 		free(pool);
1995 	}
1996 
1997 	(void) strlcpy(poolname, *prop_val, sizeof (mrp.mrp_pool));
1998 	vdp->vd_val = (uintptr_t)poolname;
1999 
2000 	return (DLADM_STATUS_OK);
2001 }
2002 
2003 /* ARGSUSED */
2004 dladm_status_t
2005 extract_pool(val_desc_t *vdp, uint_t cnt, void *arg)
2006 {
2007 	mac_resource_props_t	*mrp = (mac_resource_props_t *)arg;
2008 
2009 	if (vdp->vd_val == RESET_VAL) {
2010 		bzero(&mrp->mrp_pool, sizeof (mrp->mrp_pool));
2011 		mrp->mrp_mask |= MRP_POOL;
2012 		return (DLADM_STATUS_OK);
2013 	}
2014 
2015 	(void) strlcpy(mrp->mrp_pool, (char *)vdp->vd_val,
2016 	    sizeof (mrp->mrp_pool));
2017 	mrp->mrp_mask |= MRP_POOL;
2018 	/*
2019 	 * Use MCM_CPUS since the fanout count is not user specified
2020 	 * and will be determined by the cpu list generated from the
2021 	 * pool.
2022 	 */
2023 	mrp->mrp_fanout_mode = MCM_CPUS;
2024 
2025 	return (DLADM_STATUS_OK);
2026 }
2027 
2028 /* ARGSUSED */
2029 static dladm_status_t
2030 get_priority(dladm_handle_t handle, prop_desc_t *pdp,
2031     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2032     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2033 {
2034 	mac_resource_props_t	mrp;
2035 	mac_priority_level_t	pri;
2036 	dladm_status_t		status;
2037 
2038 	status = i_dladm_get_public_prop(handle, linkid, "resource", flags,
2039 	    perm_flags, &mrp, sizeof (mrp));
2040 	if (status != DLADM_STATUS_OK)
2041 		return (status);
2042 
2043 	pri = ((mrp.mrp_mask & MRP_PRIORITY) == 0) ? MPL_HIGH :
2044 	    mrp.mrp_priority;
2045 
2046 	(void) dladm_pri2str(pri, prop_val[0]);
2047 	*val_cnt = 1;
2048 	return (DLADM_STATUS_OK);
2049 }
2050 
2051 /* ARGSUSED */
2052 dladm_status_t
2053 extract_priority(val_desc_t *vdp, uint_t cnt, void *arg)
2054 {
2055 	mac_resource_props_t *mrp = arg;
2056 
2057 	if (cnt != 1)
2058 		return (DLADM_STATUS_BADVAL);
2059 
2060 	mrp->mrp_priority = (mac_priority_level_t)vdp->vd_val;
2061 	mrp->mrp_mask |= MRP_PRIORITY;
2062 
2063 	return (DLADM_STATUS_OK);
2064 }
2065 
2066 /*
2067  * Determines the size of the structure that needs to be sent to drivers
2068  * for retrieving the property range values.
2069  */
2070 static int
2071 i_dladm_range_size(mac_propval_range_t *r, size_t *sz, uint_t *rcount)
2072 {
2073 	uint_t count = r->mpr_count;
2074 
2075 	*sz = sizeof (mac_propval_range_t);
2076 	*rcount = count;
2077 	--count;
2078 
2079 	switch (r->mpr_type) {
2080 	case MAC_PROPVAL_UINT32:
2081 		*sz += (count * sizeof (mac_propval_uint32_range_t));
2082 		return (0);
2083 	default:
2084 		break;
2085 	}
2086 	*sz = 0;
2087 	*rcount = 0;
2088 	return (EINVAL);
2089 }
2090 
2091 
2092 /* ARGSUSED */
2093 static dladm_status_t
2094 check_rings(dladm_handle_t handle, prop_desc_t *pdp,
2095     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
2096     val_desc_t **vp, datalink_media_t media)
2097 {
2098 	uint_t		val_cnt = *val_cntp;
2099 	val_desc_t	*v = *vp;
2100 
2101 	if (val_cnt != 1)
2102 		return (DLADM_STATUS_BADVAL);
2103 	if (strncasecmp(prop_val[0], "hw", strlen("hw")) == 0) {
2104 		v->vd_val = UNSPEC_VAL;
2105 	} else if (strncasecmp(prop_val[0], "sw", strlen("sw")) == 0) {
2106 		v->vd_val = 0;
2107 	} else {
2108 		v->vd_val = strtoul(prop_val[0], NULL, 0);
2109 		if (v->vd_val == 0)
2110 			return (DLADM_STATUS_BADVAL);
2111 	}
2112 	return (DLADM_STATUS_OK);
2113 }
2114 
2115 /* ARGSUSED */
2116 static dladm_status_t
2117 get_rings_range(dladm_handle_t handle, prop_desc_t *pdp,
2118     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2119     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2120 {
2121 	dld_ioc_macprop_t *dip;
2122 	dladm_status_t status = DLADM_STATUS_OK;
2123 	mac_propval_range_t *rangep;
2124 	size_t	sz;
2125 	mac_propval_uint32_range_t *ur;
2126 
2127 	sz = sizeof (mac_propval_range_t);
2128 
2129 	if ((dip = i_dladm_buf_alloc_by_name(sz, linkid, pdp->pd_name, flags,
2130 	    &status)) == NULL)
2131 		return (status);
2132 
2133 	status = i_dladm_macprop(handle, dip, B_FALSE);
2134 	if (status != DLADM_STATUS_OK)
2135 		return (status);
2136 
2137 	rangep = (mac_propval_range_t *)(void *)&dip->pr_val;
2138 	*val_cnt = 1;
2139 	ur = &rangep->mpr_range_uint32[0];
2140 	/* This is the case where the dev doesn't have any rings/groups */
2141 	if (rangep->mpr_count == 0) {
2142 		(*prop_val)[0] = '\0';
2143 	/*
2144 	 * This is the case where the dev supports rings, but static
2145 	 * grouping.
2146 	 */
2147 	} else if (ur->mpur_min == ur->mpur_max &&
2148 	    ur->mpur_max == 0) {
2149 		(void) snprintf(prop_val[0], DLADM_PROP_VAL_MAX, "sw,hw");
2150 	/*
2151 	 * This is the case where the dev supports rings and dynamic
2152 	 * grouping, but has only one value (say 2 rings and 2 groups).
2153 	 */
2154 	} else if (ur->mpur_min == ur->mpur_max) {
2155 		(void) snprintf(prop_val[0], DLADM_PROP_VAL_MAX, "sw,hw,%d",
2156 		    ur->mpur_min);
2157 	/*
2158 	 * This is the case where the dev supports rings and dynamic
2159 	 * grouping and has a range of rings.
2160 	 */
2161 	} else {
2162 		(void) snprintf(prop_val[0], DLADM_PROP_VAL_MAX,
2163 		    "sw,hw,<%ld-%ld>", ur->mpur_min, ur->mpur_max);
2164 	}
2165 	free(dip);
2166 	return (status);
2167 }
2168 
2169 
2170 /* ARGSUSED */
2171 static dladm_status_t
2172 get_rxrings(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2173     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2174     uint_t flags, uint_t *perm_flags)
2175 {
2176 	mac_resource_props_t	mrp;
2177 	dladm_status_t		status;
2178 	uint32_t		nrings = 0;
2179 
2180 	/*
2181 	 * Get the number of (effective-)rings from the resource property.
2182 	 */
2183 	if (strcmp(pdp->pd_name, "rxrings-effective") == 0) {
2184 		status = i_dladm_get_public_prop(handle, linkid,
2185 		    "resource-effective", flags, perm_flags, &mrp,
2186 		    sizeof (mrp));
2187 	} else {
2188 		/*
2189 		 * Get the permissions from the "rxrings" property.
2190 		 */
2191 		status = i_dladm_get_public_prop(handle, linkid, "rxrings",
2192 		    flags, perm_flags, NULL, 0);
2193 		if (status != DLADM_STATUS_OK)
2194 			return (status);
2195 
2196 		status = i_dladm_get_public_prop(handle, linkid,
2197 		    "resource", flags, NULL, &mrp, sizeof (mrp));
2198 	}
2199 
2200 	if (status != DLADM_STATUS_OK)
2201 		return (status);
2202 
2203 	if ((mrp.mrp_mask & MRP_RX_RINGS) == 0) {
2204 		*val_cnt = 0;
2205 		return (DLADM_STATUS_OK);
2206 	}
2207 	nrings = mrp.mrp_nrxrings;
2208 	*val_cnt = 1;
2209 	if (mrp.mrp_mask & MRP_RXRINGS_UNSPEC)
2210 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "hw");
2211 	else if (nrings == 0)
2212 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "sw");
2213 	else
2214 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%ld", nrings);
2215 	return (DLADM_STATUS_OK);
2216 }
2217 
2218 /* ARGSUSED */
2219 dladm_status_t
2220 extract_rxrings(val_desc_t *vdp, uint_t cnt, void *arg)
2221 {
2222 	mac_resource_props_t	*mrp = (mac_resource_props_t *)arg;
2223 
2224 	mrp->mrp_nrxrings = 0;
2225 	if (vdp->vd_val == RESET_VAL)
2226 		mrp->mrp_mask = MRP_RINGS_RESET;
2227 	else if (vdp->vd_val == UNSPEC_VAL)
2228 		mrp->mrp_mask = MRP_RXRINGS_UNSPEC;
2229 	else
2230 		mrp->mrp_nrxrings = vdp->vd_val;
2231 	mrp->mrp_mask |= MRP_RX_RINGS;
2232 
2233 	return (DLADM_STATUS_OK);
2234 }
2235 
2236 /* ARGSUSED */
2237 static dladm_status_t
2238 get_txrings(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2239     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2240     uint_t flags, uint_t *perm_flags)
2241 {
2242 	mac_resource_props_t	mrp;
2243 	dladm_status_t		status;
2244 	uint32_t		nrings = 0;
2245 
2246 
2247 	/*
2248 	 * Get the number of (effective-)rings from the resource property.
2249 	 */
2250 	if (strcmp(pdp->pd_name, "txrings-effective") == 0) {
2251 		status = i_dladm_get_public_prop(handle, linkid,
2252 		    "resource-effective", flags, perm_flags, &mrp,
2253 		    sizeof (mrp));
2254 	} else {
2255 		/*
2256 		 * Get the permissions from the "txrings" property.
2257 		 */
2258 		status = i_dladm_get_public_prop(handle, linkid, "txrings",
2259 		    flags, perm_flags, NULL, 0);
2260 		if (status != DLADM_STATUS_OK)
2261 			return (status);
2262 
2263 		/*
2264 		 * Get the number of rings from the "resource" property.
2265 		 */
2266 		status = i_dladm_get_public_prop(handle, linkid, "resource",
2267 		    flags, NULL, &mrp, sizeof (mrp));
2268 	}
2269 
2270 	if (status != DLADM_STATUS_OK)
2271 		return (status);
2272 
2273 	if ((mrp.mrp_mask & MRP_TX_RINGS) == 0) {
2274 		*val_cnt = 0;
2275 		return (DLADM_STATUS_OK);
2276 	}
2277 	nrings = mrp.mrp_ntxrings;
2278 	*val_cnt = 1;
2279 	if (mrp.mrp_mask & MRP_TXRINGS_UNSPEC)
2280 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "hw");
2281 	else if (nrings == 0)
2282 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "sw");
2283 	else
2284 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%ld", nrings);
2285 	return (DLADM_STATUS_OK);
2286 }
2287 
2288 /* ARGSUSED */
2289 dladm_status_t
2290 extract_txrings(val_desc_t *vdp, uint_t cnt, void *arg)
2291 {
2292 	mac_resource_props_t	*mrp = (mac_resource_props_t *)arg;
2293 
2294 	mrp->mrp_ntxrings = 0;
2295 	if (vdp->vd_val == RESET_VAL)
2296 		mrp->mrp_mask = MRP_RINGS_RESET;
2297 	else if (vdp->vd_val == UNSPEC_VAL)
2298 		mrp->mrp_mask = MRP_TXRINGS_UNSPEC;
2299 	else
2300 		mrp->mrp_ntxrings = vdp->vd_val;
2301 	mrp->mrp_mask |= MRP_TX_RINGS;
2302 
2303 	return (DLADM_STATUS_OK);
2304 }
2305 
2306 /* ARGSUSED */
2307 static dladm_status_t
2308 get_cntavail(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2309     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
2310     uint_t *perm_flags)
2311 {
2312 	if (flags & DLD_PROP_DEFAULT)
2313 		return (DLADM_STATUS_NOTDEFINED);
2314 
2315 	return (get_uint32(handle, pdp, linkid, prop_val, val_cnt, media,
2316 	    flags, perm_flags));
2317 }
2318 
2319 /* ARGSUSED */
2320 static dladm_status_t
2321 set_resource(dladm_handle_t handle, prop_desc_t *pdp,
2322     datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt,
2323     uint_t flags, datalink_media_t media)
2324 {
2325 	mac_resource_props_t	mrp;
2326 	dladm_status_t		status = DLADM_STATUS_OK;
2327 	dld_ioc_macprop_t	*dip;
2328 	int			i;
2329 
2330 	bzero(&mrp, sizeof (mac_resource_props_t));
2331 	dip = i_dladm_buf_alloc_by_name(0, linkid, "resource",
2332 	    flags, &status);
2333 
2334 	if (dip == NULL)
2335 		return (status);
2336 
2337 	for (i = 0; i < DLADM_MAX_RSRC_PROP; i++) {
2338 		resource_prop_t	*rp = &rsrc_prop_table[i];
2339 
2340 		if (strcmp(pdp->pd_name, rp->rp_name) != 0)
2341 			continue;
2342 
2343 		status = rp->rp_extract(vdp, val_cnt, &mrp);
2344 		if (status != DLADM_STATUS_OK)
2345 			goto done;
2346 
2347 		break;
2348 	}
2349 
2350 	(void) memcpy(dip->pr_val, &mrp, dip->pr_valsize);
2351 	status = i_dladm_macprop(handle, dip, B_TRUE);
2352 
2353 done:
2354 	free(dip);
2355 	return (status);
2356 }
2357 
2358 /* ARGSUSED */
2359 static dladm_status_t
2360 get_protection(dladm_handle_t handle, prop_desc_t *pdp,
2361     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2362     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2363 {
2364 	mac_resource_props_t	mrp;
2365 	mac_protect_t		*p;
2366 	dladm_status_t		status;
2367 	uint32_t		i, cnt = 0, setbits[32];
2368 
2369 	status = i_dladm_get_public_prop(handle, linkid, "resource", flags,
2370 	    perm_flags, &mrp, sizeof (mrp));
2371 	if (status != DLADM_STATUS_OK)
2372 		return (status);
2373 
2374 	p = &mrp.mrp_protect;
2375 	if ((mrp.mrp_mask & MRP_PROTECT) == 0) {
2376 		*val_cnt = 0;
2377 		return (DLADM_STATUS_OK);
2378 	}
2379 	dladm_find_setbits32(p->mp_types, setbits, &cnt);
2380 	if (cnt > *val_cnt)
2381 		return (DLADM_STATUS_BADVALCNT);
2382 
2383 	for (i = 0; i < cnt; i++)
2384 		(void) dladm_protect2str(setbits[i], prop_val[i]);
2385 
2386 	*val_cnt = cnt;
2387 	return (DLADM_STATUS_OK);
2388 }
2389 
2390 /* ARGSUSED */
2391 static dladm_status_t
2392 get_allowedips(dladm_handle_t handle, prop_desc_t *pdp,
2393     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2394     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2395 {
2396 	mac_resource_props_t	mrp;
2397 	mac_protect_t		*p;
2398 	dladm_status_t		status;
2399 	int			i;
2400 
2401 	status = i_dladm_get_public_prop(handle, linkid, "resource", flags,
2402 	    perm_flags, &mrp, sizeof (mrp));
2403 	if (status != DLADM_STATUS_OK)
2404 		return (status);
2405 
2406 	p = &mrp.mrp_protect;
2407 	if (p->mp_ipaddrcnt == 0) {
2408 		*val_cnt = 0;
2409 		return (DLADM_STATUS_OK);
2410 	}
2411 	if (p->mp_ipaddrcnt > *val_cnt)
2412 		return (DLADM_STATUS_BADVALCNT);
2413 
2414 	for (i = 0; i < p->mp_ipaddrcnt; i++) {
2415 		int len;
2416 		if (p->mp_ipaddrs[i].ip_version == IPV4_VERSION) {
2417 			ipaddr_t	v4addr;
2418 
2419 			v4addr = V4_PART_OF_V6(p->mp_ipaddrs[i].ip_addr);
2420 			(void) dladm_ipv4addr2str(&v4addr, prop_val[i]);
2421 		} else {
2422 			(void) dladm_ipv6addr2str(&p->mp_ipaddrs[i].ip_addr,
2423 			    prop_val[i]);
2424 		}
2425 		len = strlen(prop_val[i]);
2426 		(void) sprintf(prop_val[i] + len, "/%d",
2427 		    p->mp_ipaddrs[i].ip_netmask);
2428 	}
2429 	*val_cnt = p->mp_ipaddrcnt;
2430 	return (DLADM_STATUS_OK);
2431 }
2432 
2433 dladm_status_t
2434 extract_protection(val_desc_t *vdp, uint_t cnt, void *arg)
2435 {
2436 	mac_resource_props_t	*mrp = arg;
2437 	uint32_t		types = 0;
2438 	int			i;
2439 
2440 	for (i = 0; i < cnt; i++)
2441 		types |= (uint32_t)vdp[i].vd_val;
2442 
2443 	mrp->mrp_protect.mp_types = types;
2444 	mrp->mrp_mask |= MRP_PROTECT;
2445 	return (DLADM_STATUS_OK);
2446 }
2447 
2448 dladm_status_t
2449 extract_allowedips(val_desc_t *vdp, uint_t cnt, void *arg)
2450 {
2451 	mac_resource_props_t	*mrp = arg;
2452 	mac_protect_t		*p = &mrp->mrp_protect;
2453 	int			i;
2454 
2455 	if (vdp->vd_val == 0) {
2456 		cnt = (uint_t)-1;
2457 	} else {
2458 		for (i = 0; i < cnt; i++) {
2459 			bcopy((void *)vdp[i].vd_val, &p->mp_ipaddrs[i],
2460 			    sizeof (mac_ipaddr_t));
2461 		}
2462 	}
2463 	p->mp_ipaddrcnt = cnt;
2464 	mrp->mrp_mask |= MRP_PROTECT;
2465 	return (DLADM_STATUS_OK);
2466 }
2467 
2468 static dladm_status_t
2469 check_single_ip(char *buf, mac_ipaddr_t *addr)
2470 {
2471 	dladm_status_t	status;
2472 	ipaddr_t	v4addr;
2473 	in6_addr_t	v6addr;
2474 	boolean_t	isv4 = B_TRUE;
2475 	char		*p;
2476 	uint32_t	mask = 0;
2477 
2478 	/*
2479 	 * If the IP address is in CIDR format, parse the bits component
2480 	 * seperately. An address in this style will be used to indicate an
2481 	 * entire subnet, so it must be a network number with no host address.
2482 	 */
2483 	if ((p = strchr(buf, '/')) != NULL) {
2484 		char *end = NULL;
2485 
2486 		*p++ = '\0';
2487 		if (!isdigit(*p))
2488 			return (DLADM_STATUS_INVALID_IP);
2489 		mask = strtol(p, &end, 10);
2490 		if (end != NULL && *end != '\0')
2491 			return (DLADM_STATUS_INVALID_IP);
2492 		if (mask > 128|| mask < 1)
2493 			return (DLADM_STATUS_INVALID_IP);
2494 	}
2495 
2496 	status = dladm_str2ipv4addr(buf, &v4addr);
2497 	if (status == DLADM_STATUS_INVALID_IP) {
2498 		status = dladm_str2ipv6addr(buf, &v6addr);
2499 		if (status == DLADM_STATUS_OK)
2500 			isv4 = B_FALSE;
2501 	}
2502 	if (status != DLADM_STATUS_OK)
2503 		return (status);
2504 
2505 	if (isv4) {
2506 		if (v4addr == INADDR_ANY)
2507 			return (DLADM_STATUS_INVALID_IP);
2508 
2509 		IN6_IPADDR_TO_V4MAPPED(v4addr, &addr->ip_addr);
2510 		addr->ip_version = IPV4_VERSION;
2511 		if (p != NULL) {
2512 			uint32_t smask;
2513 
2514 			/*
2515 			 * Validate the netmask is in the proper range for v4
2516 			 */
2517 			if (mask > 32 || mask < 1)
2518 				return (DLADM_STATUS_INVALID_IP);
2519 
2520 			/*
2521 			 * We have a CIDR style address, confirm that only the
2522 			 * network number is set.
2523 			 */
2524 			smask = 0xFFFFFFFFu << (32 - mask);
2525 			if (htonl(v4addr) & ~smask)
2526 				return (DLADM_STATUS_INVALID_IP);
2527 		} else {
2528 			mask = 32;
2529 		}
2530 		addr->ip_netmask = mask;
2531 	} else {
2532 		if (IN6_IS_ADDR_UNSPECIFIED(&v6addr))
2533 			return (DLADM_STATUS_INVALID_IP);
2534 
2535 		if (IN6_IS_ADDR_V4MAPPED_ANY(&v6addr))
2536 			return (DLADM_STATUS_INVALID_IP);
2537 
2538 		if (p != NULL) {
2539 			int i, off, high;
2540 
2541 			/*
2542 			 * Note that the address in our buffer is stored in
2543 			 * network byte order.
2544 			 */
2545 			off = 0;
2546 			for (i = 3; i >= 0; i--) {
2547 				high = ffsl(ntohl(v6addr._S6_un._S6_u32[i]));
2548 				if (high != 0)
2549 					break;
2550 				off += 32;
2551 			}
2552 			off += high;
2553 			if (128 - off >= mask)
2554 				return (DLADM_STATUS_INVALID_IP);
2555 		} else {
2556 			mask = 128;
2557 		}
2558 
2559 		addr->ip_addr = v6addr;
2560 		addr->ip_version = IPV6_VERSION;
2561 		addr->ip_netmask = mask;
2562 	}
2563 	return (DLADM_STATUS_OK);
2564 }
2565 
2566 /* ARGSUSED */
2567 static dladm_status_t
2568 check_allowedips(dladm_handle_t handle, prop_desc_t *pdp,
2569     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
2570     val_desc_t **vdpp, datalink_media_t media)
2571 {
2572 	dladm_status_t	status;
2573 	mac_ipaddr_t	*addr;
2574 	int		i;
2575 	uint_t		val_cnt = *val_cntp;
2576 	val_desc_t	*vdp = *vdpp;
2577 
2578 	if (val_cnt > MPT_MAXIPADDR)
2579 		return (DLADM_STATUS_BADVALCNT);
2580 
2581 	for (i = 0; i < val_cnt; i++) {
2582 		if ((addr = calloc(1, sizeof (mac_ipaddr_t))) == NULL) {
2583 			status = DLADM_STATUS_NOMEM;
2584 			goto fail;
2585 		}
2586 		vdp[i].vd_val = (uintptr_t)addr;
2587 
2588 		status = check_single_ip(prop_val[i], addr);
2589 		if (status != DLADM_STATUS_OK)
2590 			goto fail;
2591 	}
2592 	return (DLADM_STATUS_OK);
2593 
2594 fail:
2595 	for (i = 0; i < val_cnt; i++) {
2596 		free((void *)vdp[i].vd_val);
2597 		vdp[i].vd_val = NULL;
2598 	}
2599 	return (status);
2600 }
2601 
2602 static void
2603 dladm_cid2str(mac_dhcpcid_t *cid, char *buf)
2604 {
2605 	char	tmp_buf[DLADM_STRSIZE];
2606 	uint_t	hexlen;
2607 
2608 	switch (cid->dc_form) {
2609 	case CIDFORM_TYPED: {
2610 		uint16_t	duidtype, hwtype;
2611 		uint32_t	timestamp, ennum;
2612 		char		*lladdr;
2613 
2614 		if (cid->dc_len < sizeof (duidtype))
2615 			goto fail;
2616 
2617 		bcopy(cid->dc_id, &duidtype, sizeof (duidtype));
2618 		duidtype = ntohs(duidtype);
2619 		switch (duidtype) {
2620 		case DHCPV6_DUID_LLT: {
2621 			duid_llt_t	llt;
2622 
2623 			if (cid->dc_len < sizeof (llt))
2624 				goto fail;
2625 
2626 			bcopy(cid->dc_id, &llt, sizeof (llt));
2627 			hwtype = ntohs(llt.dllt_hwtype);
2628 			timestamp = ntohl(llt.dllt_time);
2629 			lladdr = _link_ntoa(cid->dc_id + sizeof (llt),
2630 			    NULL, cid->dc_len - sizeof (llt), IFT_OTHER);
2631 			if (lladdr == NULL)
2632 				goto fail;
2633 
2634 			(void) snprintf(buf, DLADM_STRSIZE, "%d.%d.%d.%s",
2635 			    duidtype, hwtype, timestamp, lladdr);
2636 			free(lladdr);
2637 			break;
2638 		}
2639 		case DHCPV6_DUID_EN: {
2640 			duid_en_t	en;
2641 
2642 			if (cid->dc_len < sizeof (en))
2643 				goto fail;
2644 
2645 			bcopy(cid->dc_id, &en, sizeof (en));
2646 			ennum = DHCPV6_GET_ENTNUM(&en);
2647 			hexlen = sizeof (tmp_buf);
2648 			if (octet_to_hexascii(cid->dc_id + sizeof (en),
2649 			    cid->dc_len - sizeof (en), tmp_buf, &hexlen) != 0)
2650 				goto fail;
2651 
2652 			(void) snprintf(buf, DLADM_STRSIZE, "%d.%d.%s",
2653 			    duidtype, ennum, tmp_buf);
2654 			break;
2655 		}
2656 		case DHCPV6_DUID_LL: {
2657 			duid_ll_t	ll;
2658 
2659 			if (cid->dc_len < sizeof (ll))
2660 				goto fail;
2661 
2662 			bcopy(cid->dc_id, &ll, sizeof (ll));
2663 			hwtype = ntohs(ll.dll_hwtype);
2664 			lladdr = _link_ntoa(cid->dc_id + sizeof (ll),
2665 			    NULL, cid->dc_len - sizeof (ll), IFT_OTHER);
2666 			if (lladdr == NULL)
2667 				goto fail;
2668 
2669 			(void) snprintf(buf, DLADM_STRSIZE, "%d.%d.%s",
2670 			    duidtype, hwtype, lladdr);
2671 			free(lladdr);
2672 			break;
2673 		}
2674 		default: {
2675 			hexlen = sizeof (tmp_buf);
2676 			if (octet_to_hexascii(cid->dc_id + sizeof (duidtype),
2677 			    cid->dc_len - sizeof (duidtype),
2678 			    tmp_buf, &hexlen) != 0)
2679 				goto fail;
2680 
2681 			(void) snprintf(buf, DLADM_STRSIZE, "%d.%s",
2682 			    duidtype, tmp_buf);
2683 		}
2684 		}
2685 		break;
2686 	}
2687 	case CIDFORM_HEX: {
2688 		hexlen = sizeof (tmp_buf);
2689 		if (octet_to_hexascii(cid->dc_id, cid->dc_len,
2690 		    tmp_buf, &hexlen) != 0)
2691 			goto fail;
2692 
2693 		(void) snprintf(buf, DLADM_STRSIZE, "0x%s", tmp_buf);
2694 		break;
2695 	}
2696 	case CIDFORM_STR: {
2697 		int	i;
2698 
2699 		for (i = 0; i < cid->dc_len; i++) {
2700 			if (!isprint(cid->dc_id[i]))
2701 				goto fail;
2702 		}
2703 		(void) snprintf(buf, DLADM_STRSIZE, "%s", cid->dc_id);
2704 		break;
2705 	}
2706 	default:
2707 		goto fail;
2708 	}
2709 	return;
2710 
2711 fail:
2712 	(void) snprintf(buf, DLADM_STRSIZE, "<unknown>");
2713 }
2714 
2715 static dladm_status_t
2716 dladm_str2cid(char *buf, mac_dhcpcid_t *cid)
2717 {
2718 	char	*ptr = buf;
2719 	char	tmp_buf[DLADM_STRSIZE];
2720 	uint_t	hexlen, cidlen;
2721 
2722 	bzero(cid, sizeof (*cid));
2723 	if (isdigit(*ptr) &&
2724 	    ptr[strspn(ptr, "0123456789")] == '.') {
2725 		char	*cp;
2726 		ulong_t	duidtype;
2727 		ulong_t	subtype;
2728 		ulong_t	timestamp;
2729 		uchar_t	*lladdr;
2730 		int	addrlen;
2731 
2732 		errno = 0;
2733 		duidtype = strtoul(ptr, &cp, 0);
2734 		if (ptr == cp || errno != 0 || *cp != '.' ||
2735 		    duidtype > USHRT_MAX)
2736 			return (DLADM_STATUS_BADARG);
2737 		ptr = cp + 1;
2738 
2739 		if (duidtype != 0 && duidtype <= DHCPV6_DUID_LL) {
2740 			errno = 0;
2741 			subtype = strtoul(ptr, &cp, 0);
2742 			if (ptr == cp || errno != 0 || *cp != '.')
2743 				return (DLADM_STATUS_BADARG);
2744 			ptr = cp + 1;
2745 		}
2746 		switch (duidtype) {
2747 		case DHCPV6_DUID_LLT: {
2748 			duid_llt_t	llt;
2749 
2750 			errno = 0;
2751 			timestamp = strtoul(ptr, &cp, 0);
2752 			if (ptr == cp || errno != 0 || *cp != '.')
2753 				return (DLADM_STATUS_BADARG);
2754 
2755 			ptr = cp + 1;
2756 			lladdr = _link_aton(ptr, &addrlen);
2757 			if (lladdr == NULL)
2758 				return (DLADM_STATUS_BADARG);
2759 
2760 			cidlen = sizeof (llt) + addrlen;
2761 			if (cidlen > sizeof (cid->dc_id)) {
2762 				free(lladdr);
2763 				return (DLADM_STATUS_TOOSMALL);
2764 			}
2765 			llt.dllt_dutype = htons(duidtype);
2766 			llt.dllt_hwtype = htons(subtype);
2767 			llt.dllt_time = htonl(timestamp);
2768 			bcopy(&llt, cid->dc_id, sizeof (llt));
2769 			bcopy(lladdr, cid->dc_id + sizeof (llt), addrlen);
2770 			free(lladdr);
2771 			break;
2772 		}
2773 		case DHCPV6_DUID_LL: {
2774 			duid_ll_t	ll;
2775 
2776 			lladdr = _link_aton(ptr, &addrlen);
2777 			if (lladdr == NULL)
2778 				return (DLADM_STATUS_BADARG);
2779 
2780 			cidlen = sizeof (ll) + addrlen;
2781 			if (cidlen > sizeof (cid->dc_id)) {
2782 				free(lladdr);
2783 				return (DLADM_STATUS_TOOSMALL);
2784 			}
2785 			ll.dll_dutype = htons(duidtype);
2786 			ll.dll_hwtype = htons(subtype);
2787 			bcopy(&ll, cid->dc_id, sizeof (ll));
2788 			bcopy(lladdr, cid->dc_id + sizeof (ll), addrlen);
2789 			free(lladdr);
2790 			break;
2791 		}
2792 		default: {
2793 			hexlen = sizeof (tmp_buf);
2794 			if (hexascii_to_octet(ptr, strlen(ptr),
2795 			    tmp_buf, &hexlen) != 0)
2796 				return (DLADM_STATUS_BADARG);
2797 
2798 			if (duidtype == DHCPV6_DUID_EN) {
2799 				duid_en_t	en;
2800 
2801 				en.den_dutype = htons(duidtype);
2802 				DHCPV6_SET_ENTNUM(&en, subtype);
2803 
2804 				cidlen = sizeof (en) + hexlen;
2805 				if (cidlen > sizeof (cid->dc_id))
2806 					return (DLADM_STATUS_TOOSMALL);
2807 
2808 				bcopy(&en, cid->dc_id, sizeof (en));
2809 				bcopy(tmp_buf, cid->dc_id + sizeof (en),
2810 				    hexlen);
2811 			} else {
2812 				uint16_t	dutype = htons(duidtype);
2813 
2814 				cidlen = sizeof (dutype) + hexlen;
2815 				if (cidlen > sizeof (cid->dc_id))
2816 					return (DLADM_STATUS_TOOSMALL);
2817 
2818 				bcopy(&dutype, cid->dc_id, sizeof (dutype));
2819 				bcopy(tmp_buf, cid->dc_id + sizeof (dutype),
2820 				    hexlen);
2821 			}
2822 			break;
2823 		}
2824 		}
2825 		cid->dc_form = CIDFORM_TYPED;
2826 	} else if (strncasecmp("0x", ptr, 2) == 0 && ptr[2] != '\0') {
2827 		ptr += 2;
2828 		hexlen = sizeof (tmp_buf);
2829 		if (hexascii_to_octet(ptr, strlen(ptr), tmp_buf,
2830 		    &hexlen) != 0) {
2831 			return (DLADM_STATUS_BADARG);
2832 		}
2833 		cidlen = hexlen;
2834 		if (cidlen > sizeof (cid->dc_id))
2835 			return (DLADM_STATUS_TOOSMALL);
2836 
2837 		bcopy(tmp_buf, cid->dc_id, cidlen);
2838 		cid->dc_form = CIDFORM_HEX;
2839 	} else {
2840 		cidlen = strlen(ptr);
2841 		if (cidlen > sizeof (cid->dc_id))
2842 			return (DLADM_STATUS_TOOSMALL);
2843 
2844 		bcopy(ptr, cid->dc_id, cidlen);
2845 		cid->dc_form = CIDFORM_STR;
2846 	}
2847 	cid->dc_len = cidlen;
2848 	return (DLADM_STATUS_OK);
2849 }
2850 
2851 /* ARGSUSED */
2852 static dladm_status_t
2853 get_allowedcids(dladm_handle_t handle, prop_desc_t *pdp,
2854     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2855     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2856 {
2857 	mac_resource_props_t	mrp;
2858 	mac_protect_t		*p;
2859 	dladm_status_t		status;
2860 	int			i;
2861 
2862 	status = i_dladm_get_public_prop(handle, linkid, "resource", flags,
2863 	    perm_flags, &mrp, sizeof (mrp));
2864 	if (status != DLADM_STATUS_OK)
2865 		return (status);
2866 
2867 	p = &mrp.mrp_protect;
2868 	if (p->mp_cidcnt == 0) {
2869 		*val_cnt = 0;
2870 		return (DLADM_STATUS_OK);
2871 	}
2872 	if (p->mp_cidcnt > *val_cnt)
2873 		return (DLADM_STATUS_BADVALCNT);
2874 
2875 	for (i = 0; i < p->mp_cidcnt; i++) {
2876 		mac_dhcpcid_t	*cid = &p->mp_cids[i];
2877 
2878 		dladm_cid2str(cid, prop_val[i]);
2879 	}
2880 	*val_cnt = p->mp_cidcnt;
2881 	return (DLADM_STATUS_OK);
2882 }
2883 
2884 dladm_status_t
2885 extract_allowedcids(val_desc_t *vdp, uint_t cnt, void *arg)
2886 {
2887 	mac_resource_props_t	*mrp = arg;
2888 	mac_protect_t		*p = &mrp->mrp_protect;
2889 	int			i;
2890 
2891 	if (vdp->vd_val == 0) {
2892 		cnt = (uint_t)-1;
2893 	} else {
2894 		for (i = 0; i < cnt; i++) {
2895 			bcopy((void *)vdp[i].vd_val, &p->mp_cids[i],
2896 			    sizeof (mac_dhcpcid_t));
2897 		}
2898 	}
2899 	p->mp_cidcnt = cnt;
2900 	mrp->mrp_mask |= MRP_PROTECT;
2901 	return (DLADM_STATUS_OK);
2902 }
2903 
2904 /* ARGSUSED */
2905 static dladm_status_t
2906 check_allowedcids(dladm_handle_t handle, prop_desc_t *pdp,
2907     datalink_id_t linkid, char **prop_val, uint_t *val_cntp,
2908     uint_t flags, val_desc_t **vdpp, datalink_media_t media)
2909 {
2910 	dladm_status_t	status;
2911 	mac_dhcpcid_t	*cid;
2912 	int		i;
2913 	uint_t		val_cnt = *val_cntp;
2914 	val_desc_t	*vdp = *vdpp;
2915 
2916 	if (val_cnt > MPT_MAXCID)
2917 		return (DLADM_STATUS_BADVALCNT);
2918 
2919 	for (i = 0; i < val_cnt; i++) {
2920 		if ((cid = calloc(1, sizeof (mac_dhcpcid_t))) == NULL) {
2921 			status = DLADM_STATUS_NOMEM;
2922 			goto fail;
2923 		}
2924 		vdp[i].vd_val = (uintptr_t)cid;
2925 
2926 		status = dladm_str2cid(prop_val[i], cid);
2927 		if (status != DLADM_STATUS_OK)
2928 			goto fail;
2929 	}
2930 	return (DLADM_STATUS_OK);
2931 
2932 fail:
2933 	for (i = 0; i < val_cnt; i++) {
2934 		free((void *)vdp[i].vd_val);
2935 		vdp[i].vd_val = NULL;
2936 	}
2937 	return (status);
2938 }
2939 
2940 /* ARGSUSED */
2941 static dladm_status_t
2942 get_secondary_macs(dladm_handle_t handle, prop_desc_t *pdp,
2943     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2944     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2945 {
2946 	mac_secondary_addr_t	sa;
2947 	dladm_status_t		status;
2948 	int			i;
2949 
2950 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
2951 	    perm_flags, &sa, sizeof (sa));
2952 	if (status != DLADM_STATUS_OK)
2953 		return (status);
2954 
2955 	if (sa.ms_addrcnt > *val_cnt)
2956 		return (DLADM_STATUS_BADVALCNT);
2957 
2958 	for (i = 0; i < sa.ms_addrcnt; i++) {
2959 		if (dladm_aggr_macaddr2str(
2960 		    (const unsigned char *)&sa.ms_addrs[i], prop_val[i]) ==
2961 		    NULL) {
2962 			*val_cnt = i;
2963 			return (DLADM_STATUS_NOMEM);
2964 		}
2965 	}
2966 	*val_cnt = sa.ms_addrcnt;
2967 	return (DLADM_STATUS_OK);
2968 }
2969 
2970 /* ARGSUSED */
2971 static dladm_status_t
2972 check_secondary_macs(dladm_handle_t handle, prop_desc_t *pdp,
2973     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
2974     val_desc_t **vdpp, datalink_media_t media)
2975 {
2976 	dladm_status_t	status;
2977 	uchar_t		*addr;
2978 	uint_t		len = 0;
2979 	int		i;
2980 	uint_t		val_cnt = *val_cntp;
2981 	val_desc_t	*vdp = *vdpp;
2982 
2983 	if (val_cnt >= MPT_MAXMACADDR)
2984 		return (DLADM_STATUS_BADVALCNT);
2985 
2986 	for (i = 0; i < val_cnt; i++) {
2987 		addr = _link_aton(prop_val[i], (int *)&len);
2988 		if (addr == NULL) {
2989 			if (len == (uint_t)-1)
2990 				status = DLADM_STATUS_MACADDRINVAL;
2991 			else
2992 				status = DLADM_STATUS_NOMEM;
2993 			goto fail;
2994 		}
2995 
2996 		vdp[i].vd_val = (uintptr_t)addr;
2997 	}
2998 	return (DLADM_STATUS_OK);
2999 
3000 fail:
3001 	for (i = 0; i < val_cnt; i++) {
3002 		free((void *)vdp[i].vd_val);
3003 		vdp[i].vd_val = NULL;
3004 	}
3005 	return (status);
3006 }
3007 
3008 /* ARGSUSED */
3009 static dladm_status_t
3010 set_secondary_macs(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid,
3011     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
3012 {
3013 	dladm_status_t status;
3014 	dld_ioc_macprop_t *dip;
3015 	int i;
3016 	mac_secondary_addr_t msa;
3017 
3018 	dip = i_dladm_buf_alloc_by_name(0, linkid, "secondary-macs", 0,
3019 	    &status);
3020 	if (dip == NULL)
3021 		return (status);
3022 
3023 	if (vdp->vd_val == 0) {
3024 		val_cnt = (uint_t)-1;
3025 	} else {
3026 		for (i = 0; i < val_cnt; i++) {
3027 			bcopy((void *)vdp[i].vd_val, msa.ms_addrs[i],
3028 			    MAXMACADDRLEN);
3029 		}
3030 	}
3031 	msa.ms_addrcnt = val_cnt;
3032 	bcopy(&msa, dip->pr_val, dip->pr_valsize);
3033 
3034 	status = i_dladm_macprop(handle, dip, B_TRUE);
3035 
3036 	free(dip);
3037 	return (status);
3038 }
3039 
3040 /* ARGSUSED */
3041 static dladm_status_t
3042 get_autopush(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3043     char **prop_val, uint_t *val_cnt, datalink_media_t media,
3044     uint_t flags, uint_t *perm_flags)
3045 {
3046 	struct		dlautopush dlap;
3047 	int		i, len;
3048 	dladm_status_t	status;
3049 
3050 	if (flags & DLD_PROP_DEFAULT)
3051 		return (DLADM_STATUS_NOTDEFINED);
3052 
3053 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
3054 	    perm_flags, &dlap, sizeof (dlap));
3055 	if (status != DLADM_STATUS_OK)
3056 		return (status);
3057 
3058 	if (dlap.dap_npush == 0) {
3059 		*val_cnt = 0;
3060 		return (DLADM_STATUS_OK);
3061 	}
3062 	for (i = 0, len = 0; i < dlap.dap_npush; i++) {
3063 		if (i != 0) {
3064 			(void) snprintf(*prop_val + len,
3065 			    DLADM_PROP_VAL_MAX - len, "%c", AP_DELIMITER);
3066 			len += 1;
3067 		}
3068 		(void) snprintf(*prop_val + len, DLADM_PROP_VAL_MAX - len,
3069 		    "%s", dlap.dap_aplist[i]);
3070 		len += strlen(dlap.dap_aplist[i]);
3071 		if (dlap.dap_anchor - 1 == i) {
3072 			(void) snprintf(*prop_val + len,
3073 			    DLADM_PROP_VAL_MAX - len, "%c%s", AP_DELIMITER,
3074 			    AP_ANCHOR);
3075 			len += (strlen(AP_ANCHOR) + 1);
3076 		}
3077 	}
3078 	*val_cnt = 1;
3079 	return (DLADM_STATUS_OK);
3080 }
3081 
3082 /*
3083  * Add the specified module to the dlautopush structure; returns a
3084  * DLADM_STATUS_* code.
3085  */
3086 dladm_status_t
3087 i_dladm_add_ap_module(const char *module, struct dlautopush *dlap)
3088 {
3089 	if ((strlen(module) == 0) || (strlen(module) > FMNAMESZ))
3090 		return (DLADM_STATUS_BADVAL);
3091 
3092 	if (strncasecmp(module, AP_ANCHOR, strlen(AP_ANCHOR)) == 0) {
3093 		/*
3094 		 * We don't allow multiple anchors, and the anchor must
3095 		 * be after at least one module.
3096 		 */
3097 		if (dlap->dap_anchor != 0)
3098 			return (DLADM_STATUS_BADVAL);
3099 		if (dlap->dap_npush == 0)
3100 			return (DLADM_STATUS_BADVAL);
3101 
3102 		dlap->dap_anchor = dlap->dap_npush;
3103 		return (DLADM_STATUS_OK);
3104 	}
3105 	if (dlap->dap_npush >= MAXAPUSH)
3106 		return (DLADM_STATUS_BADVALCNT);
3107 
3108 	(void) strlcpy(dlap->dap_aplist[dlap->dap_npush++], module,
3109 	    FMNAMESZ + 1);
3110 
3111 	return (DLADM_STATUS_OK);
3112 }
3113 
3114 /*
3115  * Currently, both '.' and ' '(space) can be used as the delimiters between
3116  * autopush modules. The former is used in dladm set-linkprop, and the
3117  * latter is used in the autopush(1M) file.
3118  */
3119 /* ARGSUSED */
3120 static dladm_status_t
3121 check_autopush(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3122     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
3123     datalink_media_t media)
3124 {
3125 	char			*module;
3126 	struct dlautopush	*dlap;
3127 	dladm_status_t		status;
3128 	char			val[DLADM_PROP_VAL_MAX];
3129 	char			delimiters[4];
3130 	uint_t			val_cnt = *val_cntp;
3131 	val_desc_t		*vdp = *vdpp;
3132 
3133 	if (val_cnt != 1)
3134 		return (DLADM_STATUS_BADVALCNT);
3135 
3136 	if (prop_val != NULL) {
3137 		dlap = malloc(sizeof (struct dlautopush));
3138 		if (dlap == NULL)
3139 			return (DLADM_STATUS_NOMEM);
3140 
3141 		(void) memset(dlap, 0, sizeof (struct dlautopush));
3142 		(void) snprintf(delimiters, 4, " %c\n", AP_DELIMITER);
3143 		bcopy(*prop_val, val, DLADM_PROP_VAL_MAX);
3144 		module = strtok(val, delimiters);
3145 		while (module != NULL) {
3146 			status = i_dladm_add_ap_module(module, dlap);
3147 			if (status != DLADM_STATUS_OK)
3148 				return (status);
3149 			module = strtok(NULL, delimiters);
3150 		}
3151 
3152 		vdp->vd_val = (uintptr_t)dlap;
3153 	} else {
3154 		vdp->vd_val = 0;
3155 	}
3156 	return (DLADM_STATUS_OK);
3157 }
3158 
3159 #define	WLDP_BUFSIZE (MAX_BUF_LEN - WIFI_BUF_OFFSET)
3160 
3161 /* ARGSUSED */
3162 static dladm_status_t
3163 get_rate_common(dladm_handle_t handle, prop_desc_t *pdp,
3164     datalink_id_t linkid, char **prop_val, uint_t *val_cnt, uint_t id,
3165     uint_t *perm_flags)
3166 {
3167 	wl_rates_t	*wrp;
3168 	uint_t		i;
3169 	dladm_status_t	status = DLADM_STATUS_OK;
3170 
3171 	wrp = malloc(WLDP_BUFSIZE);
3172 	if (wrp == NULL)
3173 		return (DLADM_STATUS_NOMEM);
3174 
3175 	status = i_dladm_wlan_param(handle, linkid, wrp, id, WLDP_BUFSIZE,
3176 	    B_FALSE);
3177 	if (status != DLADM_STATUS_OK)
3178 		goto done;
3179 
3180 	if (wrp->wl_rates_num > *val_cnt) {
3181 		status = DLADM_STATUS_TOOSMALL;
3182 		goto done;
3183 	}
3184 
3185 	if (wrp->wl_rates_rates[0] == 0) {
3186 		prop_val[0][0] = '\0';
3187 		*val_cnt = 1;
3188 		goto done;
3189 	}
3190 
3191 	for (i = 0; i < wrp->wl_rates_num; i++) {
3192 		(void) snprintf(prop_val[i], DLADM_STRSIZE, "%.*f",
3193 		    wrp->wl_rates_rates[i] % 2,
3194 		    (float)wrp->wl_rates_rates[i] / 2);
3195 	}
3196 	*val_cnt = wrp->wl_rates_num;
3197 	*perm_flags = MAC_PROP_PERM_RW;
3198 
3199 done:
3200 	free(wrp);
3201 	return (status);
3202 }
3203 
3204 static dladm_status_t
3205 get_rate(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3206     char **prop_val, uint_t *val_cnt, datalink_media_t media,
3207     uint_t flags, uint_t *perm_flags)
3208 {
3209 	if (media != DL_WIFI) {
3210 		return (get_speed(handle, pdp, linkid, prop_val,
3211 		    val_cnt, media, flags, perm_flags));
3212 	}
3213 
3214 	return (get_rate_common(handle, pdp, linkid, prop_val, val_cnt,
3215 	    MAC_PROP_WL_DESIRED_RATES, perm_flags));
3216 }
3217 
3218 /* ARGSUSED */
3219 static dladm_status_t
3220 get_rate_mod(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3221     char **prop_val, uint_t *val_cnt, datalink_media_t media,
3222     uint_t flags, uint_t *perm_flags)
3223 {
3224 	switch (media) {
3225 	case DL_ETHER:
3226 		/*
3227 		 * Speed for ethernet links is unbounded. E.g., 802.11b
3228 		 * links can have a speed of 5.5 Gbps.
3229 		 */
3230 		return (DLADM_STATUS_NOTSUP);
3231 
3232 	case DL_WIFI:
3233 		return (get_rate_common(handle, pdp, linkid, prop_val,
3234 		    val_cnt, MAC_PROP_WL_SUPPORTED_RATES, perm_flags));
3235 	default:
3236 		return (DLADM_STATUS_BADARG);
3237 	}
3238 }
3239 
3240 static dladm_status_t
3241 set_wlan_rate(dladm_handle_t handle, datalink_id_t linkid,
3242     dladm_wlan_rates_t *rates)
3243 {
3244 	int		i;
3245 	uint_t		len;
3246 	wl_rates_t	*wrp;
3247 	dladm_status_t	status = DLADM_STATUS_OK;
3248 
3249 	wrp = malloc(WLDP_BUFSIZE);
3250 	if (wrp == NULL)
3251 		return (DLADM_STATUS_NOMEM);
3252 
3253 	bzero(wrp, WLDP_BUFSIZE);
3254 	for (i = 0; i < rates->wr_cnt; i++)
3255 		wrp->wl_rates_rates[i] = rates->wr_rates[i];
3256 	wrp->wl_rates_num = rates->wr_cnt;
3257 
3258 	len = offsetof(wl_rates_t, wl_rates_rates) +
3259 	    (rates->wr_cnt * sizeof (char)) + WIFI_BUF_OFFSET;
3260 	status = i_dladm_wlan_param(handle, linkid, wrp,
3261 	    MAC_PROP_WL_DESIRED_RATES, len, B_TRUE);
3262 
3263 	free(wrp);
3264 	return (status);
3265 }
3266 
3267 /* ARGSUSED */
3268 static dladm_status_t
3269 set_rate(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3270     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
3271 {
3272 	dladm_wlan_rates_t	rates;
3273 	dladm_status_t		status;
3274 
3275 	/*
3276 	 * can currently set rate on WIFI links only.
3277 	 */
3278 	if (media != DL_WIFI)
3279 		return (DLADM_STATUS_PROPRDONLY);
3280 
3281 	if (val_cnt != 1)
3282 		return (DLADM_STATUS_BADVALCNT);
3283 
3284 	rates.wr_cnt = 1;
3285 	rates.wr_rates[0] = vdp[0].vd_val;
3286 
3287 	status = set_wlan_rate(handle, linkid, &rates);
3288 
3289 	return (status);
3290 }
3291 
3292 /* ARGSUSED */
3293 static dladm_status_t
3294 check_rate(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3295     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
3296     datalink_media_t media)
3297 {
3298 	int		i;
3299 	uint_t		modval_cnt = MAX_SUPPORT_RATES;
3300 	char		*buf, **modval;
3301 	dladm_status_t	status;
3302 	uint_t 		perm_flags;
3303 	uint_t		val_cnt = *val_cntp;
3304 	val_desc_t	*vdp = *vdpp;
3305 
3306 	if (val_cnt != 1)
3307 		return (DLADM_STATUS_BADVALCNT);
3308 
3309 	buf = malloc((sizeof (char *) + DLADM_STRSIZE) *
3310 	    MAX_SUPPORT_RATES);
3311 	if (buf == NULL) {
3312 		status = DLADM_STATUS_NOMEM;
3313 		goto done;
3314 	}
3315 
3316 	modval = (char **)(void *)buf;
3317 	for (i = 0; i < MAX_SUPPORT_RATES; i++) {
3318 		modval[i] = buf + sizeof (char *) * MAX_SUPPORT_RATES +
3319 		    i * DLADM_STRSIZE;
3320 	}
3321 
3322 	status = get_rate_mod(handle, NULL, linkid, modval, &modval_cnt,
3323 	    media, 0, &perm_flags);
3324 	if (status != DLADM_STATUS_OK)
3325 		goto done;
3326 
3327 	for (i = 0; i < modval_cnt; i++) {
3328 		if (strcasecmp(*prop_val, modval[i]) == 0) {
3329 			vdp->vd_val = (uintptr_t)(uint_t)
3330 			    (atof(*prop_val) * 2);
3331 			status = DLADM_STATUS_OK;
3332 			break;
3333 		}
3334 	}
3335 	if (i == modval_cnt)
3336 		status = DLADM_STATUS_BADVAL;
3337 done:
3338 	free(buf);
3339 	return (status);
3340 }
3341 
3342 static dladm_status_t
3343 get_phyconf(dladm_handle_t handle, datalink_id_t linkid, void *buf,
3344     int buflen)
3345 {
3346 	return (i_dladm_wlan_param(handle, linkid, buf, MAC_PROP_WL_PHY_CONFIG,
3347 	    buflen, B_FALSE));
3348 }
3349 
3350 /* ARGSUSED */
3351 static dladm_status_t
3352 get_channel(dladm_handle_t handle, prop_desc_t *pdp,
3353     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3354     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3355 {
3356 	uint32_t	channel;
3357 	char		buf[WLDP_BUFSIZE];
3358 	dladm_status_t	status;
3359 	wl_phy_conf_t	wl_phy_conf;
3360 
3361 	if ((status = get_phyconf(handle, linkid, buf, sizeof (buf)))
3362 	    != DLADM_STATUS_OK)
3363 		return (status);
3364 
3365 	(void) memcpy(&wl_phy_conf, buf, sizeof (wl_phy_conf));
3366 	if (!i_dladm_wlan_convert_chan(&wl_phy_conf, &channel))
3367 		return (DLADM_STATUS_NOTFOUND);
3368 
3369 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%u", channel);
3370 	*val_cnt = 1;
3371 	*perm_flags = MAC_PROP_PERM_READ;
3372 	return (DLADM_STATUS_OK);
3373 }
3374 
3375 /* ARGSUSED */
3376 static dladm_status_t
3377 get_powermode(dladm_handle_t handle, prop_desc_t *pdp,
3378     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3379     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3380 {
3381 	wl_ps_mode_t	mode;
3382 	const char	*s;
3383 	char		buf[WLDP_BUFSIZE];
3384 	dladm_status_t	status;
3385 
3386 	if ((status = i_dladm_wlan_param(handle, linkid, buf,
3387 	    MAC_PROP_WL_POWER_MODE, sizeof (buf), B_FALSE)) != DLADM_STATUS_OK)
3388 		return (status);
3389 
3390 	(void) memcpy(&mode, buf, sizeof (mode));
3391 	switch (mode.wl_ps_mode) {
3392 	case WL_PM_AM:
3393 		s = "off";
3394 		break;
3395 	case WL_PM_MPS:
3396 		s = "max";
3397 		break;
3398 	case WL_PM_FAST:
3399 		s = "fast";
3400 		break;
3401 	default:
3402 		return (DLADM_STATUS_NOTFOUND);
3403 	}
3404 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
3405 	*val_cnt = 1;
3406 	*perm_flags = MAC_PROP_PERM_RW;
3407 	return (DLADM_STATUS_OK);
3408 }
3409 
3410 /* ARGSUSED */
3411 static dladm_status_t
3412 set_powermode(dladm_handle_t handle, prop_desc_t *pdp,
3413     datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt, uint_t flags,
3414     datalink_media_t media)
3415 {
3416 	dladm_wlan_powermode_t	powermode = vdp->vd_val;
3417 	wl_ps_mode_t		ps_mode;
3418 
3419 	if (val_cnt != 1)
3420 		return (DLADM_STATUS_BADVALCNT);
3421 
3422 	(void) memset(&ps_mode, 0xff, sizeof (ps_mode));
3423 
3424 	switch (powermode) {
3425 	case DLADM_WLAN_PM_OFF:
3426 		ps_mode.wl_ps_mode = WL_PM_AM;
3427 		break;
3428 	case DLADM_WLAN_PM_MAX:
3429 		ps_mode.wl_ps_mode = WL_PM_MPS;
3430 		break;
3431 	case DLADM_WLAN_PM_FAST:
3432 		ps_mode.wl_ps_mode = WL_PM_FAST;
3433 		break;
3434 	default:
3435 		return (DLADM_STATUS_NOTSUP);
3436 	}
3437 	return (i_dladm_wlan_param(handle, linkid, &ps_mode,
3438 	    MAC_PROP_WL_POWER_MODE, sizeof (ps_mode), B_TRUE));
3439 }
3440 
3441 /* ARGSUSED */
3442 static dladm_status_t
3443 get_radio(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3444     char **prop_val, uint_t *val_cnt, datalink_media_t media,
3445     uint_t flags, uint_t *perm_flags)
3446 {
3447 	wl_radio_t	radio;
3448 	const char	*s;
3449 	char		buf[WLDP_BUFSIZE];
3450 	dladm_status_t	status;
3451 
3452 	if ((status = i_dladm_wlan_param(handle, linkid, buf,
3453 	    MAC_PROP_WL_RADIO, sizeof (buf), B_FALSE)) != DLADM_STATUS_OK)
3454 		return (status);
3455 
3456 	(void) memcpy(&radio, buf, sizeof (radio));
3457 	switch (radio) {
3458 	case B_TRUE:
3459 		s = "on";
3460 		break;
3461 	case B_FALSE:
3462 		s = "off";
3463 		break;
3464 	default:
3465 		return (DLADM_STATUS_NOTFOUND);
3466 	}
3467 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
3468 	*val_cnt = 1;
3469 	*perm_flags = MAC_PROP_PERM_RW;
3470 	return (DLADM_STATUS_OK);
3471 }
3472 
3473 /* ARGSUSED */
3474 static dladm_status_t
3475 set_radio(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3476     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
3477 {
3478 	dladm_wlan_radio_t	radio = vdp->vd_val;
3479 	wl_radio_t		r;
3480 
3481 	if (val_cnt != 1)
3482 		return (DLADM_STATUS_BADVALCNT);
3483 
3484 	switch (radio) {
3485 	case DLADM_WLAN_RADIO_ON:
3486 		r = B_TRUE;
3487 		break;
3488 	case DLADM_WLAN_RADIO_OFF:
3489 		r = B_FALSE;
3490 		break;
3491 	default:
3492 		return (DLADM_STATUS_NOTSUP);
3493 	}
3494 	return (i_dladm_wlan_param(handle, linkid, &r, MAC_PROP_WL_RADIO,
3495 	    sizeof (r), B_TRUE));
3496 }
3497 
3498 /* ARGSUSED */
3499 static dladm_status_t
3500 check_hoplimit(dladm_handle_t handle, prop_desc_t *pdp,
3501     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
3502     val_desc_t **vdpp, datalink_media_t media)
3503 {
3504 	int32_t		hlim;
3505 	char		*ep;
3506 	uint_t		val_cnt = *val_cntp;
3507 	val_desc_t	*vdp = *vdpp;
3508 
3509 	if (val_cnt != 1)
3510 		return (DLADM_STATUS_BADVALCNT);
3511 
3512 	errno = 0;
3513 	hlim = strtol(*prop_val, &ep, 10);
3514 	if (errno != 0 || ep == *prop_val || hlim < 1 ||
3515 	    hlim > (int32_t)UINT8_MAX)
3516 		return (DLADM_STATUS_BADVAL);
3517 	vdp->vd_val = hlim;
3518 	return (DLADM_STATUS_OK);
3519 }
3520 
3521 /* ARGSUSED */
3522 static dladm_status_t
3523 check_encaplim(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3524     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
3525     datalink_media_t media)
3526 {
3527 	int32_t		elim;
3528 	char		*ep;
3529 	uint_t		val_cnt = *val_cntp;
3530 	val_desc_t	*vdp = *vdpp;
3531 
3532 	if (media != DL_IPV6)
3533 		return (DLADM_STATUS_BADARG);
3534 
3535 	if (val_cnt != 1)
3536 		return (DLADM_STATUS_BADVALCNT);
3537 
3538 	errno = 0;
3539 	elim = strtol(*prop_val, &ep, 10);
3540 	if (errno != 0 || ep == *prop_val || elim < 0 ||
3541 	    elim > (int32_t)UINT8_MAX)
3542 		return (DLADM_STATUS_BADVAL);
3543 	vdp->vd_val = elim;
3544 	return (DLADM_STATUS_OK);
3545 }
3546 
3547 static dladm_status_t
3548 i_dladm_set_linkprop_db(dladm_handle_t handle, datalink_id_t linkid,
3549     const char *prop_name, char **prop_val, uint_t val_cnt)
3550 {
3551 	char		buf[MAXLINELEN];
3552 	int		i;
3553 	dladm_conf_t	conf;
3554 	dladm_status_t	status;
3555 
3556 	status = dladm_open_conf(handle, linkid, &conf);
3557 	if (status != DLADM_STATUS_OK)
3558 		return (status);
3559 
3560 	/*
3561 	 * reset case.
3562 	 */
3563 	if (val_cnt == 0) {
3564 		status = dladm_unset_conf_field(handle, conf, prop_name);
3565 		if (status == DLADM_STATUS_OK)
3566 			status = dladm_write_conf(handle, conf);
3567 		goto done;
3568 	}
3569 
3570 	buf[0] = '\0';
3571 	for (i = 0; i < val_cnt; i++) {
3572 		(void) strlcat(buf, prop_val[i], MAXLINELEN);
3573 		if (i != val_cnt - 1)
3574 			(void) strlcat(buf, ",", MAXLINELEN);
3575 	}
3576 
3577 	status = dladm_set_conf_field(handle, conf, prop_name, DLADM_TYPE_STR,
3578 	    buf);
3579 	if (status == DLADM_STATUS_OK)
3580 		status = dladm_write_conf(handle, conf);
3581 
3582 done:
3583 	dladm_destroy_conf(handle, conf);
3584 	return (status);
3585 }
3586 
3587 static dladm_status_t
3588 i_dladm_get_linkprop_db(dladm_handle_t handle, datalink_id_t linkid,
3589     const char *prop_name, char **prop_val, uint_t *val_cntp)
3590 {
3591 	char		buf[MAXLINELEN], *str;
3592 	uint_t		cnt = 0;
3593 	dladm_conf_t	conf;
3594 	dladm_status_t	status;
3595 
3596 	status = dladm_getsnap_conf(handle, linkid, &conf);
3597 	if (status != DLADM_STATUS_OK)
3598 		return (status);
3599 
3600 	status = dladm_get_conf_field(handle, conf, prop_name, buf, MAXLINELEN);
3601 	if (status != DLADM_STATUS_OK)
3602 		goto done;
3603 
3604 	str = strtok(buf, ",");
3605 	while (str != NULL) {
3606 		if (cnt == *val_cntp) {
3607 			status = DLADM_STATUS_TOOSMALL;
3608 			goto done;
3609 		}
3610 		(void) strlcpy(prop_val[cnt++], str, DLADM_PROP_VAL_MAX);
3611 		str = strtok(NULL, ",");
3612 	}
3613 
3614 	*val_cntp = cnt;
3615 
3616 done:
3617 	dladm_destroy_conf(handle, conf);
3618 	return (status);
3619 }
3620 
3621 /*
3622  * Walk persistent private link properties of a link.
3623  */
3624 static dladm_status_t
3625 i_dladm_walk_linkprop_priv_db(dladm_handle_t handle, datalink_id_t linkid,
3626     void *arg, int (*func)(dladm_handle_t, datalink_id_t, const char *, void *))
3627 {
3628 	dladm_status_t		status;
3629 	dladm_conf_t		conf;
3630 	char			last_attr[MAXLINKATTRLEN];
3631 	char			attr[MAXLINKATTRLEN];
3632 	char			attrval[MAXLINKATTRVALLEN];
3633 	size_t			attrsz;
3634 
3635 	if (linkid == DATALINK_INVALID_LINKID || func == NULL)
3636 		return (DLADM_STATUS_BADARG);
3637 
3638 	status = dladm_getsnap_conf(handle, linkid, &conf);
3639 	if (status != DLADM_STATUS_OK)
3640 		return (status);
3641 
3642 	last_attr[0] = '\0';
3643 	while ((status = dladm_getnext_conf_linkprop(handle, conf, last_attr,
3644 	    attr, attrval, MAXLINKATTRVALLEN, &attrsz)) == DLADM_STATUS_OK) {
3645 		if (attr[0] == '_') {
3646 			if (func(handle, linkid, attr, arg) ==
3647 			    DLADM_WALK_TERMINATE)
3648 				break;
3649 		}
3650 		(void) strlcpy(last_attr, attr, MAXLINKATTRLEN);
3651 	}
3652 
3653 	dladm_destroy_conf(handle, conf);
3654 	return (DLADM_STATUS_OK);
3655 }
3656 
3657 static link_attr_t *
3658 dladm_name2prop(const char *prop_name)
3659 {
3660 	link_attr_t *p;
3661 
3662 	for (p = link_attr; p->pp_id != MAC_PROP_PRIVATE; p++) {
3663 		if (strcmp(p->pp_name, prop_name) == 0)
3664 			break;
3665 	}
3666 	return (p);
3667 }
3668 
3669 static link_attr_t *
3670 dladm_id2prop(mac_prop_id_t propid)
3671 {
3672 	link_attr_t *p;
3673 
3674 	for (p = link_attr; p->pp_id != MAC_PROP_PRIVATE; p++) {
3675 		if (p->pp_id == propid)
3676 			break;
3677 	}
3678 	return (p);
3679 }
3680 
3681 static dld_ioc_macprop_t *
3682 i_dladm_buf_alloc_impl(size_t valsize, datalink_id_t linkid,
3683     const char *prop_name, mac_prop_id_t propid, uint_t flags,
3684     dladm_status_t *status)
3685 {
3686 	int dsize;
3687 	dld_ioc_macprop_t *dip;
3688 
3689 	*status = DLADM_STATUS_OK;
3690 	dsize = MAC_PROP_BUFSIZE(valsize);
3691 	dip = malloc(dsize);
3692 	if (dip == NULL) {
3693 		*status = DLADM_STATUS_NOMEM;
3694 		return (NULL);
3695 	}
3696 	bzero(dip, dsize);
3697 	dip->pr_valsize = valsize;
3698 	(void) strlcpy(dip->pr_name, prop_name, sizeof (dip->pr_name));
3699 	dip->pr_linkid = linkid;
3700 	dip->pr_num = propid;
3701 	dip->pr_flags = flags;
3702 	return (dip);
3703 }
3704 
3705 static dld_ioc_macprop_t *
3706 i_dladm_buf_alloc_by_name(size_t valsize, datalink_id_t linkid,
3707     const char *prop_name, uint_t flags, dladm_status_t *status)
3708 {
3709 	link_attr_t *p;
3710 
3711 	p = dladm_name2prop(prop_name);
3712 	valsize = MAX(p->pp_valsize, valsize);
3713 	return (i_dladm_buf_alloc_impl(valsize, linkid, prop_name, p->pp_id,
3714 	    flags, status));
3715 }
3716 
3717 static dld_ioc_macprop_t *
3718 i_dladm_buf_alloc_by_id(size_t valsize, datalink_id_t linkid,
3719     mac_prop_id_t propid, uint_t flags, dladm_status_t *status)
3720 {
3721 	link_attr_t *p;
3722 
3723 	p = dladm_id2prop(propid);
3724 	valsize = MAX(p->pp_valsize, valsize);
3725 	return (i_dladm_buf_alloc_impl(valsize, linkid, p->pp_name, propid,
3726 	    flags, status));
3727 }
3728 
3729 /* ARGSUSED */
3730 static dladm_status_t
3731 set_public_prop(dladm_handle_t handle, prop_desc_t *pdp,
3732     datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt, uint_t flags,
3733     datalink_media_t media)
3734 {
3735 	dld_ioc_macprop_t	*dip;
3736 	dladm_status_t	status = DLADM_STATUS_OK;
3737 	uint8_t		u8;
3738 	uint16_t	u16;
3739 	uint32_t	u32;
3740 	void		*val;
3741 
3742 	dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name, 0, &status);
3743 	if (dip == NULL)
3744 		return (status);
3745 
3746 	if (pdp->pd_flags & PD_CHECK_ALLOC)
3747 		val = (void *)vdp->vd_val;
3748 	else {
3749 		/*
3750 		 * Currently all 1/2/4-byte size properties are byte/word/int.
3751 		 * No need (yet) to distinguish these from arrays of same size.
3752 		 */
3753 		switch (dip->pr_valsize) {
3754 		case 1:
3755 			u8 = vdp->vd_val;
3756 			val = &u8;
3757 			break;
3758 		case 2:
3759 			u16 = vdp->vd_val;
3760 			val = &u16;
3761 			break;
3762 		case 4:
3763 			u32 = vdp->vd_val;
3764 			val = &u32;
3765 			break;
3766 		default:
3767 			val = &vdp->vd_val;
3768 			break;
3769 		}
3770 	}
3771 
3772 	if (val != NULL)
3773 		(void) memcpy(dip->pr_val, val, dip->pr_valsize);
3774 	else
3775 		dip->pr_valsize = 0;
3776 
3777 	status = i_dladm_macprop(handle, dip, B_TRUE);
3778 
3779 done:
3780 	free(dip);
3781 	return (status);
3782 }
3783 
3784 dladm_status_t
3785 i_dladm_macprop(dladm_handle_t handle, void *dip, boolean_t set)
3786 {
3787 	dladm_status_t status = DLADM_STATUS_OK;
3788 
3789 	if (ioctl(dladm_dld_fd(handle),
3790 	    (set ? DLDIOC_SETMACPROP : DLDIOC_GETMACPROP), dip))
3791 		status = dladm_errno2status(errno);
3792 
3793 	return (status);
3794 }
3795 
3796 static dladm_status_t
3797 i_dladm_get_public_prop(dladm_handle_t handle, datalink_id_t linkid,
3798     char *prop_name, uint_t flags, uint_t *perm_flags, void *arg, size_t size)
3799 {
3800 	dld_ioc_macprop_t	*dip;
3801 	dladm_status_t		status;
3802 
3803 	dip = i_dladm_buf_alloc_by_name(0, linkid, prop_name, flags, &status);
3804 	if (dip == NULL)
3805 		return (DLADM_STATUS_NOMEM);
3806 
3807 	status = i_dladm_macprop(handle, dip, B_FALSE);
3808 	if (status != DLADM_STATUS_OK) {
3809 		free(dip);
3810 		return (status);
3811 	}
3812 
3813 	if (perm_flags != NULL)
3814 		*perm_flags = dip->pr_perm_flags;
3815 
3816 	if (arg != NULL)
3817 		(void) memcpy(arg, dip->pr_val, size);
3818 	free(dip);
3819 	return (DLADM_STATUS_OK);
3820 }
3821 
3822 /* ARGSUSED */
3823 static dladm_status_t
3824 check_uint32(dladm_handle_t handle, prop_desc_t *pdp,
3825     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
3826     val_desc_t **vp, datalink_media_t media)
3827 {
3828 	uint_t		val_cnt = *val_cntp;
3829 	val_desc_t	*v = *vp;
3830 
3831 	if (val_cnt != 1)
3832 		return (DLADM_STATUS_BADVAL);
3833 	v->vd_val = strtoul(prop_val[0], NULL, 0);
3834 	return (DLADM_STATUS_OK);
3835 }
3836 
3837 /* ARGSUSED */
3838 static dladm_status_t
3839 get_duplex(dladm_handle_t handle, prop_desc_t *pdp,
3840     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3841     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3842 {
3843 	link_duplex_t   link_duplex;
3844 	dladm_status_t  status;
3845 
3846 	if ((status = dladm_get_single_mac_stat(handle, linkid, "link_duplex",
3847 	    KSTAT_DATA_UINT32, &link_duplex)) != 0)
3848 		return (status);
3849 
3850 	switch (link_duplex) {
3851 	case LINK_DUPLEX_FULL:
3852 		(void) strcpy(*prop_val, "full");
3853 		break;
3854 	case LINK_DUPLEX_HALF:
3855 		(void) strcpy(*prop_val, "half");
3856 		break;
3857 	default:
3858 		(void) strcpy(*prop_val, "unknown");
3859 		break;
3860 	}
3861 	*val_cnt = 1;
3862 	return (DLADM_STATUS_OK);
3863 }
3864 
3865 /* ARGSUSED */
3866 static dladm_status_t
3867 get_speed(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3868     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
3869     uint_t *perm_flags)
3870 {
3871 	uint64_t	ifspeed = 0;
3872 	dladm_status_t status;
3873 
3874 	if ((status = dladm_get_single_mac_stat(handle, linkid, "ifspeed",
3875 	    KSTAT_DATA_UINT64, &ifspeed)) != 0)
3876 		return (status);
3877 
3878 	if ((ifspeed % 1000000) != 0) {
3879 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
3880 		    "%llf", ifspeed / (float)1000000); /* Mbps */
3881 	} else {
3882 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
3883 		    "%llu", ifspeed / 1000000); /* Mbps */
3884 	}
3885 	*val_cnt = 1;
3886 	*perm_flags = MAC_PROP_PERM_READ;
3887 	return (DLADM_STATUS_OK);
3888 }
3889 
3890 /* ARGSUSED */
3891 static dladm_status_t
3892 get_link_state(dladm_handle_t handle, prop_desc_t *pdp,
3893     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3894     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3895 {
3896 	link_state_t		link_state;
3897 	dladm_status_t		status;
3898 
3899 	status = dladm_get_state(handle, linkid, &link_state);
3900 	if (status != DLADM_STATUS_OK)
3901 		return (status);
3902 
3903 	switch (link_state) {
3904 	case LINK_STATE_UP:
3905 		(void) strcpy(*prop_val, "up");
3906 		break;
3907 	case LINK_STATE_DOWN:
3908 		(void) strcpy(*prop_val, "down");
3909 		break;
3910 	default:
3911 		(void) strcpy(*prop_val, "unknown");
3912 		break;
3913 	}
3914 	*val_cnt = 1;
3915 	*perm_flags = MAC_PROP_PERM_READ;
3916 	return (DLADM_STATUS_OK);
3917 }
3918 
3919 /* ARGSUSED */
3920 static dladm_status_t
3921 get_binary(dladm_handle_t handle, prop_desc_t *pdp,
3922     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3923     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3924 {
3925 	dladm_status_t	status;
3926 	uint_t		v = 0;
3927 
3928 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
3929 	    perm_flags, &v, sizeof (v));
3930 	if (status != DLADM_STATUS_OK)
3931 		return (status);
3932 
3933 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%d", (uint_t)(v > 0));
3934 	*val_cnt = 1;
3935 	return (DLADM_STATUS_OK);
3936 }
3937 
3938 /* ARGSUSED */
3939 static dladm_status_t
3940 get_uint32(dladm_handle_t handle, prop_desc_t *pdp,
3941     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3942     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3943 {
3944 	dladm_status_t	status;
3945 	uint32_t	v = 0;
3946 
3947 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
3948 	    perm_flags, &v, sizeof (v));
3949 	if (status != DLADM_STATUS_OK)
3950 		return (status);
3951 
3952 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%ld", v);
3953 	*val_cnt = 1;
3954 	return (DLADM_STATUS_OK);
3955 }
3956 
3957 /* ARGSUSED */
3958 static dladm_status_t
3959 get_range(dladm_handle_t handle, prop_desc_t *pdp,
3960     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3961     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3962 {
3963 	dld_ioc_macprop_t *dip;
3964 	dladm_status_t status = DLADM_STATUS_OK;
3965 	size_t	sz;
3966 	uint_t	rcount;
3967 	mac_propval_range_t *rangep;
3968 
3969 	/*
3970 	 * As caller we don't know number of value ranges, the driver
3971 	 * supports. To begin with we assume that number to be 1. If the
3972 	 * buffer size is insufficient, driver returns back with the
3973 	 * actual count of value ranges. See mac.h for more details.
3974 	 */
3975 	sz = sizeof (mac_propval_range_t);
3976 	rcount = 1;
3977 retry:
3978 	if ((dip = i_dladm_buf_alloc_by_name(sz, linkid, pdp->pd_name, flags,
3979 	    &status)) == NULL)
3980 		return (status);
3981 
3982 	rangep = (mac_propval_range_t *)(void *)&dip->pr_val;
3983 	rangep->mpr_count = rcount;
3984 
3985 	status = i_dladm_macprop(handle, dip, B_FALSE);
3986 	if (status != DLADM_STATUS_OK) {
3987 		if (status == DLADM_STATUS_TOOSMALL) {
3988 			int err;
3989 
3990 			if ((err = i_dladm_range_size(rangep, &sz, &rcount))
3991 			    == 0) {
3992 				free(dip);
3993 				goto retry;
3994 			} else {
3995 				status = dladm_errno2status(err);
3996 			}
3997 		}
3998 		free(dip);
3999 		return (status);
4000 	}
4001 
4002 	if (rangep->mpr_count == 0) {
4003 		*val_cnt = 1;
4004 		(void) snprintf(prop_val[0], DLADM_PROP_VAL_MAX, "--");
4005 		goto done;
4006 	}
4007 
4008 	switch (rangep->mpr_type) {
4009 	case MAC_PROPVAL_UINT32: {
4010 		mac_propval_uint32_range_t *ur;
4011 		uint_t	count = rangep->mpr_count, i;
4012 
4013 		ur = &rangep->mpr_range_uint32[0];
4014 
4015 		for (i = 0; i < count; i++, ur++) {
4016 			if (ur->mpur_min == ur->mpur_max) {
4017 				(void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
4018 				    "%ld", ur->mpur_min);
4019 			} else {
4020 				(void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
4021 				    "%ld-%ld", ur->mpur_min, ur->mpur_max);
4022 			}
4023 		}
4024 		*val_cnt = count;
4025 		break;
4026 	}
4027 	default:
4028 		status = DLADM_STATUS_BADARG;
4029 		break;
4030 	}
4031 done:
4032 	free(dip);
4033 	return (status);
4034 }
4035 
4036 /* ARGSUSED */
4037 static dladm_status_t
4038 get_tagmode(dladm_handle_t handle, prop_desc_t *pdp,
4039     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4040     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4041 {
4042 	link_tagmode_t		mode;
4043 	dladm_status_t		status;
4044 
4045 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
4046 	    perm_flags, &mode, sizeof (mode));
4047 	if (status != DLADM_STATUS_OK)
4048 		return (status);
4049 
4050 	switch (mode) {
4051 	case LINK_TAGMODE_NORMAL:
4052 		(void) strlcpy(*prop_val, "normal", DLADM_PROP_VAL_MAX);
4053 		break;
4054 	case LINK_TAGMODE_VLANONLY:
4055 		(void) strlcpy(*prop_val, "vlanonly", DLADM_PROP_VAL_MAX);
4056 		break;
4057 	default:
4058 		(void) strlcpy(*prop_val, "unknown", DLADM_PROP_VAL_MAX);
4059 	}
4060 	*val_cnt = 1;
4061 	return (DLADM_STATUS_OK);
4062 }
4063 
4064 /* ARGSUSED */
4065 static dladm_status_t
4066 get_flowctl(dladm_handle_t handle, prop_desc_t *pdp,
4067     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4068     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4069 {
4070 	link_flowctrl_t	v;
4071 	dladm_status_t	status;
4072 
4073 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
4074 	    perm_flags, &v, sizeof (v));
4075 	if (status != DLADM_STATUS_OK)
4076 		return (status);
4077 
4078 	switch (v) {
4079 	case LINK_FLOWCTRL_NONE:
4080 		(void) sprintf(*prop_val, "no");
4081 		break;
4082 	case LINK_FLOWCTRL_RX:
4083 		(void) sprintf(*prop_val, "rx");
4084 		break;
4085 	case LINK_FLOWCTRL_TX:
4086 		(void) sprintf(*prop_val, "tx");
4087 		break;
4088 	case LINK_FLOWCTRL_BI:
4089 		(void) sprintf(*prop_val, "bi");
4090 		break;
4091 	}
4092 	*val_cnt = 1;
4093 	return (DLADM_STATUS_OK);
4094 }
4095 
4096 
4097 /* ARGSUSED */
4098 static dladm_status_t
4099 i_dladm_set_private_prop(dladm_handle_t handle, datalink_id_t linkid,
4100     const char *prop_name, char **prop_val, uint_t val_cnt, uint_t flags)
4101 
4102 {
4103 	int		i, slen;
4104 	int 		bufsize = 0;
4105 	dld_ioc_macprop_t *dip = NULL;
4106 	uchar_t 	*dp;
4107 	link_attr_t *p;
4108 	dladm_status_t	status = DLADM_STATUS_OK;
4109 
4110 	if ((prop_name == NULL && prop_val != NULL) ||
4111 	    (prop_val != NULL && val_cnt == 0))
4112 		return (DLADM_STATUS_BADARG);
4113 	p = dladm_name2prop(prop_name);
4114 	if (p->pp_id != MAC_PROP_PRIVATE)
4115 		return (DLADM_STATUS_BADARG);
4116 
4117 	if (!(flags & DLADM_OPT_ACTIVE))
4118 		return (DLADM_STATUS_OK);
4119 
4120 	/*
4121 	 * private properties: all parsing is done in the kernel.
4122 	 * allocate a enough space for each property + its separator (',').
4123 	 */
4124 	for (i = 0; i < val_cnt; i++) {
4125 		bufsize += strlen(prop_val[i]) + 1;
4126 	}
4127 
4128 	if (prop_val == NULL) {
4129 		/*
4130 		 * getting default value. so use more buffer space.
4131 		 */
4132 		bufsize += DLADM_PROP_BUF_CHUNK;
4133 	}
4134 
4135 	dip = i_dladm_buf_alloc_by_name(bufsize + 1, linkid, prop_name,
4136 	    (prop_val != NULL ? 0 : DLD_PROP_DEFAULT), &status);
4137 	if (dip == NULL)
4138 		return (status);
4139 
4140 	dp = (uchar_t *)dip->pr_val;
4141 	slen = 0;
4142 
4143 	if (prop_val == NULL) {
4144 		status = i_dladm_macprop(handle, dip, B_FALSE);
4145 		dip->pr_flags = 0;
4146 	} else {
4147 		for (i = 0; i < val_cnt; i++) {
4148 			int plen = 0;
4149 
4150 			plen = strlen(prop_val[i]);
4151 			bcopy(prop_val[i], dp, plen);
4152 			slen += plen;
4153 			/*
4154 			 * add a "," separator and update dp.
4155 			 */
4156 			if (i != (val_cnt -1))
4157 				dp[slen++] = ',';
4158 			dp += (plen + 1);
4159 		}
4160 	}
4161 	if (status == DLADM_STATUS_OK)
4162 		status = i_dladm_macprop(handle, dip, B_TRUE);
4163 
4164 	free(dip);
4165 	return (status);
4166 }
4167 
4168 static dladm_status_t
4169 i_dladm_get_priv_prop(dladm_handle_t handle, datalink_id_t linkid,
4170     const char *prop_name, char **prop_val, uint_t *val_cnt,
4171     dladm_prop_type_t type, uint_t dld_flags)
4172 {
4173 	dladm_status_t	status = DLADM_STATUS_OK;
4174 	dld_ioc_macprop_t *dip = NULL;
4175 	link_attr_t *p;
4176 
4177 	if ((prop_name == NULL && prop_val != NULL) ||
4178 	    (prop_val != NULL && val_cnt == 0))
4179 		return (DLADM_STATUS_BADARG);
4180 
4181 	p = dladm_name2prop(prop_name);
4182 	if (p->pp_id != MAC_PROP_PRIVATE)
4183 		return (DLADM_STATUS_BADARG);
4184 
4185 	/*
4186 	 * private properties: all parsing is done in the kernel.
4187 	 */
4188 	dip = i_dladm_buf_alloc_by_name(DLADM_PROP_BUF_CHUNK, linkid, prop_name,
4189 	    dld_flags, &status);
4190 	if (dip == NULL)
4191 		return (status);
4192 
4193 	if ((status = i_dladm_macprop(handle, dip, B_FALSE)) ==
4194 	    DLADM_STATUS_OK) {
4195 		if (type == DLADM_PROP_VAL_PERM) {
4196 			(void) dladm_perm2str(dip->pr_perm_flags, *prop_val);
4197 		} else if (type == DLADM_PROP_VAL_MODIFIABLE) {
4198 			*prop_val[0] = '\0';
4199 		} else {
4200 			(void) strncpy(*prop_val, dip->pr_val,
4201 			    DLADM_PROP_VAL_MAX);
4202 		}
4203 		*val_cnt = 1;
4204 	} else if ((status == DLADM_STATUS_NOTSUP) &&
4205 	    (type == DLADM_PROP_VAL_CURRENT)) {
4206 		status = DLADM_STATUS_NOTFOUND;
4207 	}
4208 	free(dip);
4209 	return (status);
4210 }
4211 
4212 
4213 static dladm_status_t
4214 i_dladm_getset_defval(dladm_handle_t handle, prop_desc_t *pdp,
4215     datalink_id_t linkid, datalink_media_t media, uint_t flags)
4216 {
4217 	dladm_status_t status;
4218 	char **prop_vals = NULL, *buf;
4219 	size_t bufsize;
4220 	uint_t cnt;
4221 	int i;
4222 	uint_t perm_flags;
4223 
4224 	/*
4225 	 * Allocate buffer needed for prop_vals array. We can have at most
4226 	 * DLADM_MAX_PROP_VALCNT char *prop_vals[] entries, where
4227 	 * each entry has max size DLADM_PROP_VAL_MAX
4228 	 */
4229 	bufsize =
4230 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
4231 	buf = malloc(bufsize);
4232 	prop_vals = (char **)(void *)buf;
4233 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
4234 		prop_vals[i] = buf +
4235 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
4236 		    i * DLADM_PROP_VAL_MAX;
4237 	}
4238 
4239 	/*
4240 	 * For properties which have pdp->pd_defval.vd_name as a non-empty
4241 	 * string, the "" itself is used to reset the property (exceptions
4242 	 * are zone and autopush, which populate vdp->vd_val). So
4243 	 * libdladm can copy pdp->pd_defval over to the val_desc_t passed
4244 	 * down on the setprop using the global values in the table. For
4245 	 * other cases (vd_name is ""), doing reset-linkprop will cause
4246 	 * libdladm to do a getprop to find the default value and then do
4247 	 * a setprop to reset the value to default.
4248 	 */
4249 	status = pdp->pd_get(handle, pdp, linkid, prop_vals, &cnt, media,
4250 	    DLD_PROP_DEFAULT, &perm_flags);
4251 	if (status == DLADM_STATUS_OK) {
4252 		if (perm_flags == MAC_PROP_PERM_RW) {
4253 			status = i_dladm_set_single_prop(handle, linkid,
4254 			    pdp->pd_class, media, pdp, prop_vals, cnt, flags);
4255 		}
4256 		else
4257 			status = DLADM_STATUS_NOTSUP;
4258 	}
4259 	free(buf);
4260 	return (status);
4261 }
4262 
4263 /* ARGSUSED */
4264 static dladm_status_t
4265 get_stp(dladm_handle_t handle, struct prop_desc *pd, datalink_id_t linkid,
4266     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
4267     uint_t *perm_flags)
4268 {
4269 	const bridge_public_prop_t *bpp;
4270 	dladm_status_t retv;
4271 	int val, i;
4272 
4273 	if (flags != 0)
4274 		return (DLADM_STATUS_NOTSUP);
4275 	*perm_flags = MAC_PROP_PERM_RW;
4276 	*val_cnt = 1;
4277 	for (bpp = bridge_prop; bpp->bpp_name != NULL; bpp++)
4278 		if (strcmp(bpp->bpp_name, pd->pd_name) == 0)
4279 			break;
4280 	retv = dladm_bridge_get_port_cfg(handle, linkid, bpp->bpp_code, &val);
4281 	/* If the daemon isn't running, then return the persistent value */
4282 	if (retv == DLADM_STATUS_NOTFOUND) {
4283 		if (i_dladm_get_linkprop_db(handle, linkid, pd->pd_name,
4284 		    prop_val, val_cnt) != DLADM_STATUS_OK)
4285 			(void) strlcpy(*prop_val, pd->pd_defval.vd_name,
4286 			    DLADM_PROP_VAL_MAX);
4287 		return (DLADM_STATUS_OK);
4288 	}
4289 	if (retv != DLADM_STATUS_OK) {
4290 		(void) strlcpy(*prop_val, "?", DLADM_PROP_VAL_MAX);
4291 		return (retv);
4292 	}
4293 	if (val == pd->pd_defval.vd_val && pd->pd_defval.vd_name[0] != '\0') {
4294 		(void) strlcpy(*prop_val, pd->pd_defval.vd_name,
4295 		    DLADM_PROP_VAL_MAX);
4296 		return (DLADM_STATUS_OK);
4297 	}
4298 	for (i = 0; i < pd->pd_noptval; i++) {
4299 		if (val == pd->pd_optval[i].vd_val) {
4300 			(void) strlcpy(*prop_val, pd->pd_optval[i].vd_name,
4301 			    DLADM_PROP_VAL_MAX);
4302 			return (DLADM_STATUS_OK);
4303 		}
4304 	}
4305 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%u", (unsigned)val);
4306 	return (DLADM_STATUS_OK);
4307 }
4308 
4309 /* ARGSUSED1 */
4310 static dladm_status_t
4311 set_stp_prop(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid,
4312     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
4313 {
4314 	/*
4315 	 * Special case for mcheck: the daemon resets the value to zero, and we
4316 	 * don't want the daemon to refresh itself; it leads to deadlock.
4317 	 */
4318 	if (flags & DLADM_OPT_NOREFRESH)
4319 		return (DLADM_STATUS_OK);
4320 
4321 	/* Tell the running daemon, if any */
4322 	return (dladm_bridge_refresh(handle, linkid));
4323 }
4324 
4325 /*
4326  * This is used only for stp_priority, stp_cost, and stp_mcheck.
4327  */
4328 /* ARGSUSED */
4329 static dladm_status_t
4330 check_stp_prop(dladm_handle_t handle, struct prop_desc *pd,
4331     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
4332     val_desc_t **vdpp, datalink_media_t media)
4333 {
4334 	char		*cp;
4335 	boolean_t	iscost;
4336 	uint_t		val_cnt = *val_cntp;
4337 	val_desc_t	*vdp = *vdpp;
4338 
4339 	if (val_cnt != 1)
4340 		return (DLADM_STATUS_BADVALCNT);
4341 
4342 	if (prop_val == NULL) {
4343 		vdp->vd_val = 0;
4344 	} else {
4345 		/* Only stp_priority and stp_cost use this function */
4346 		iscost = strcmp(pd->pd_name, "stp_cost") == 0;
4347 
4348 		if (iscost && strcmp(prop_val[0], "auto") == 0) {
4349 			/* Illegal value 0 is allowed to mean "automatic" */
4350 			vdp->vd_val = 0;
4351 		} else {
4352 			errno = 0;
4353 			vdp->vd_val = strtoul(prop_val[0], &cp, 0);
4354 			if (errno != 0 || *cp != '\0')
4355 				return (DLADM_STATUS_BADVAL);
4356 		}
4357 	}
4358 
4359 	if (iscost) {
4360 		return (vdp->vd_val > 65535 ? DLADM_STATUS_BADVAL :
4361 		    DLADM_STATUS_OK);
4362 	} else {
4363 		if (vdp->vd_val > 255)
4364 			return (DLADM_STATUS_BADVAL);
4365 		/*
4366 		 * If the user is setting stp_mcheck non-zero, then (per the
4367 		 * IEEE management standards and UNH testing) we need to check
4368 		 * whether this link is part of a bridge that is running RSTP.
4369 		 * If it's not, then setting the flag is an error.  Note that
4370 		 * errors are intentionally discarded here; it's the value
4371 		 * that's the problem -- it's not a bad value, merely one that
4372 		 * can't be used now.
4373 		 */
4374 		if (strcmp(pd->pd_name, "stp_mcheck") == 0 &&
4375 		    vdp->vd_val != 0) {
4376 			char bridge[MAXLINKNAMELEN];
4377 			UID_STP_CFG_T cfg;
4378 			dladm_bridge_prot_t brprot;
4379 
4380 			if (dladm_bridge_getlink(handle, linkid, bridge,
4381 			    sizeof (bridge)) != DLADM_STATUS_OK ||
4382 			    dladm_bridge_get_properties(bridge, &cfg,
4383 			    &brprot) != DLADM_STATUS_OK)
4384 				return (DLADM_STATUS_FAILED);
4385 			if (cfg.force_version <= 1)
4386 				return (DLADM_STATUS_FAILED);
4387 		}
4388 		return (DLADM_STATUS_OK);
4389 	}
4390 }
4391 
4392 /* ARGSUSED */
4393 static dladm_status_t
4394 get_bridge_forward(dladm_handle_t handle, struct prop_desc *pd,
4395     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4396     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4397 {
4398 	dladm_status_t retv;
4399 	uint_t val;
4400 
4401 	if (flags != 0)
4402 		return (DLADM_STATUS_NOTSUP);
4403 	*perm_flags = MAC_PROP_PERM_RW;
4404 	*val_cnt = 1;
4405 	retv = dladm_bridge_get_forwarding(handle, linkid, &val);
4406 	if (retv == DLADM_STATUS_NOTFOUND) {
4407 		if (i_dladm_get_linkprop_db(handle, linkid, pd->pd_name,
4408 		    prop_val, val_cnt) != DLADM_STATUS_OK)
4409 			(void) strlcpy(*prop_val, pd->pd_defval.vd_name,
4410 			    DLADM_PROP_VAL_MAX);
4411 		return (DLADM_STATUS_OK);
4412 	}
4413 	if (retv == DLADM_STATUS_OK)
4414 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%u", val);
4415 	else
4416 		(void) strlcpy(*prop_val, "?", DLADM_PROP_VAL_MAX);
4417 	return (retv);
4418 }
4419 
4420 /* ARGSUSED */
4421 static dladm_status_t
4422 set_bridge_forward(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid,
4423     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
4424 {
4425 	/* Tell the running daemon, if any */
4426 	return (dladm_bridge_refresh(handle, linkid));
4427 }
4428 
4429 /* ARGSUSED */
4430 static dladm_status_t
4431 get_bridge_pvid(dladm_handle_t handle, struct prop_desc *pd,
4432     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4433     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4434 {
4435 	dladm_status_t status;
4436 	dld_ioc_macprop_t *dip;
4437 	uint16_t pvid;
4438 
4439 	if (flags != 0)
4440 		return (DLADM_STATUS_NOTSUP);
4441 	*perm_flags = MAC_PROP_PERM_RW;
4442 	*val_cnt = 1;
4443 	dip = i_dladm_buf_alloc_by_id(sizeof (uint16_t), linkid, MAC_PROP_PVID,
4444 	    0, &status);
4445 	if (dip == NULL)
4446 		return (status);
4447 	status = i_dladm_macprop(handle, dip, B_FALSE);
4448 	if (status == DLADM_STATUS_OK) {
4449 		(void) memcpy(&pvid, dip->pr_val, sizeof (pvid));
4450 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%u", pvid);
4451 	} else {
4452 		(void) strlcpy(*prop_val, "?", DLADM_PROP_VAL_MAX);
4453 	}
4454 	free(dip);
4455 	return (status);
4456 }
4457 
4458 /* ARGSUSED */
4459 static dladm_status_t
4460 set_bridge_pvid(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid,
4461     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
4462 {
4463 	dladm_status_t status;
4464 	dld_ioc_macprop_t *dip;
4465 	uint16_t pvid;
4466 
4467 	dip = i_dladm_buf_alloc_by_id(sizeof (uint16_t), linkid, MAC_PROP_PVID,
4468 	    0, &status);
4469 	if (dip == NULL)
4470 		return (status);
4471 	pvid = vdp->vd_val;
4472 	(void) memcpy(dip->pr_val, &pvid, sizeof (pvid));
4473 	status = i_dladm_macprop(handle, dip, B_TRUE);
4474 	free(dip);
4475 	if (status != DLADM_STATUS_OK)
4476 		return (status);
4477 
4478 	/* Tell the running daemon, if any */
4479 	return (dladm_bridge_refresh(handle, linkid));
4480 }
4481 
4482 /* ARGSUSED */
4483 static dladm_status_t
4484 check_bridge_pvid(dladm_handle_t handle, struct prop_desc *pd,
4485     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
4486     val_desc_t **vdpp, datalink_media_t media)
4487 {
4488 	char		*cp;
4489 	uint_t		val_cnt = *val_cntp;
4490 	val_desc_t	*vdp = *vdpp;
4491 
4492 	if (val_cnt != 1)
4493 		return (DLADM_STATUS_BADVALCNT);
4494 
4495 	if (prop_val == NULL) {
4496 		vdp->vd_val = 1;
4497 	} else {
4498 		errno = 0;
4499 		vdp->vd_val = strtoul(prop_val[0], &cp, 0);
4500 		if (errno != 0 || *cp != '\0')
4501 			return (DLADM_STATUS_BADVAL);
4502 	}
4503 
4504 	return (vdp->vd_val > VLAN_ID_MAX ? DLADM_STATUS_BADVAL :
4505 	    DLADM_STATUS_OK);
4506 }
4507 
4508 dladm_status_t
4509 i_dladm_wlan_param(dladm_handle_t handle, datalink_id_t linkid, void *buf,
4510     mac_prop_id_t cmd, size_t len, boolean_t set)
4511 {
4512 	uint32_t		flags;
4513 	dladm_status_t		status;
4514 	uint32_t		media;
4515 	dld_ioc_macprop_t	*dip;
4516 	void			*dp;
4517 
4518 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL,
4519 	    &media, NULL, 0)) != DLADM_STATUS_OK) {
4520 		return (status);
4521 	}
4522 
4523 	if (media != DL_WIFI)
4524 		return (DLADM_STATUS_BADARG);
4525 
4526 	if (!(flags & DLADM_OPT_ACTIVE))
4527 		return (DLADM_STATUS_TEMPONLY);
4528 
4529 	if (len == (MAX_BUF_LEN - WIFI_BUF_OFFSET))
4530 		len = MAX_BUF_LEN - sizeof (dld_ioc_macprop_t) - 1;
4531 
4532 	dip = i_dladm_buf_alloc_by_id(len, linkid, cmd, 0, &status);
4533 	if (dip == NULL)
4534 		return (DLADM_STATUS_NOMEM);
4535 
4536 	dp = (uchar_t *)dip->pr_val;
4537 	if (set)
4538 		(void) memcpy(dp, buf, len);
4539 
4540 	status = i_dladm_macprop(handle, dip, set);
4541 	if (status == DLADM_STATUS_OK) {
4542 		if (!set)
4543 			(void) memcpy(buf, dp, len);
4544 	}
4545 
4546 	free(dip);
4547 	return (status);
4548 }
4549 
4550 dladm_status_t
4551 dladm_parse_link_props(char *str, dladm_arg_list_t **listp, boolean_t novalues)
4552 {
4553 	return (dladm_parse_args(str, listp, novalues));
4554 }
4555 
4556 /*
4557  * Retrieve the one link property from the database
4558  */
4559 /*ARGSUSED*/
4560 static int
4561 i_dladm_get_one_prop(dladm_handle_t handle, datalink_id_t linkid,
4562     const char *prop_name, void *arg)
4563 {
4564 	dladm_arg_list_t	*proplist = arg;
4565 	dladm_arg_info_t	*aip = NULL;
4566 
4567 	aip = &proplist->al_info[proplist->al_count];
4568 	/*
4569 	 * it is fine to point to prop_name since prop_name points to the
4570 	 * prop_table[n].pd_name.
4571 	 */
4572 	aip->ai_name = prop_name;
4573 
4574 	(void) dladm_get_linkprop(handle, linkid, DLADM_PROP_VAL_PERSISTENT,
4575 	    prop_name, aip->ai_val, &aip->ai_count);
4576 
4577 	if (aip->ai_count != 0)
4578 		proplist->al_count++;
4579 
4580 	return (DLADM_WALK_CONTINUE);
4581 }
4582 
4583 
4584 /*
4585  * Retrieve all link properties for a link from the database and
4586  * return a property list.
4587  */
4588 dladm_status_t
4589 dladm_link_get_proplist(dladm_handle_t handle, datalink_id_t linkid,
4590     dladm_arg_list_t **listp)
4591 {
4592 	dladm_arg_list_t	*list;
4593 	dladm_status_t		status = DLADM_STATUS_OK;
4594 
4595 	list = calloc(1, sizeof (dladm_arg_list_t));
4596 	if (list == NULL)
4597 		return (dladm_errno2status(errno));
4598 
4599 	status = dladm_walk_linkprop(handle, linkid, list,
4600 	    i_dladm_get_one_prop);
4601 
4602 	*listp = list;
4603 	return (status);
4604 }
4605 
4606 /*
4607  * Retrieve the named property from a proplist, check the value and
4608  * convert to a kernel structure.
4609  */
4610 static dladm_status_t
4611 i_dladm_link_proplist_extract_one(dladm_handle_t handle,
4612     dladm_arg_list_t *proplist, const char *name, uint_t flags, void *arg)
4613 {
4614 	dladm_status_t		status;
4615 	dladm_arg_info_t	*aip = NULL;
4616 	int			i, j;
4617 
4618 	/* Find named property in proplist */
4619 	for (i = 0; i < proplist->al_count; i++) {
4620 		aip = &proplist->al_info[i];
4621 		if (strcasecmp(aip->ai_name, name) == 0)
4622 			break;
4623 	}
4624 
4625 	/* Property not in list */
4626 	if (i == proplist->al_count)
4627 		return (DLADM_STATUS_OK);
4628 
4629 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
4630 		prop_desc_t	*pdp = &prop_table[i];
4631 		val_desc_t	*vdp;
4632 
4633 		vdp = malloc(sizeof (val_desc_t) * aip->ai_count);
4634 		if (vdp == NULL)
4635 			return (DLADM_STATUS_NOMEM);
4636 
4637 		if (strcasecmp(aip->ai_name, pdp->pd_name) != 0)
4638 			continue;
4639 
4640 		if (aip->ai_val == NULL)
4641 			return (DLADM_STATUS_BADARG);
4642 
4643 		/* Check property value */
4644 		if (pdp->pd_check != NULL) {
4645 			status = pdp->pd_check(handle, pdp, 0, aip->ai_val,
4646 			    &(aip->ai_count), flags, &vdp, 0);
4647 		} else {
4648 			status = DLADM_STATUS_BADARG;
4649 		}
4650 
4651 		if (status != DLADM_STATUS_OK)
4652 			return (status);
4653 
4654 		for (j = 0; j < DLADM_MAX_RSRC_PROP; j++) {
4655 			resource_prop_t	*rpp = &rsrc_prop_table[j];
4656 
4657 			if (strcasecmp(aip->ai_name, rpp->rp_name) != 0)
4658 				continue;
4659 
4660 			/* Extract kernel structure */
4661 			if (rpp->rp_extract != NULL) {
4662 				status = rpp->rp_extract(vdp,
4663 				    aip->ai_count, arg);
4664 			} else {
4665 				status = DLADM_STATUS_BADARG;
4666 			}
4667 			break;
4668 		}
4669 
4670 		if (status != DLADM_STATUS_OK)
4671 			return (status);
4672 
4673 		break;
4674 	}
4675 	return (status);
4676 }
4677 
4678 /*
4679  * Extract properties from a proplist and convert to mac_resource_props_t.
4680  */
4681 dladm_status_t
4682 dladm_link_proplist_extract(dladm_handle_t handle, dladm_arg_list_t *proplist,
4683     mac_resource_props_t *mrp, uint_t flags)
4684 {
4685 	dladm_status_t	status;
4686 	int		i;
4687 
4688 	for (i = 0; i < DLADM_MAX_RSRC_PROP; i++) {
4689 		status = i_dladm_link_proplist_extract_one(handle,
4690 		    proplist, rsrc_prop_table[i].rp_name, flags, mrp);
4691 		if (status != DLADM_STATUS_OK)
4692 			return (status);
4693 	}
4694 	return (status);
4695 }
4696 
4697 static const char *
4698 dladm_perm2str(uint_t perm, char *buf)
4699 {
4700 	(void) snprintf(buf, DLADM_STRSIZE, "%c%c",
4701 	    ((perm & MAC_PROP_PERM_READ) != 0) ? 'r' : '-',
4702 	    ((perm & MAC_PROP_PERM_WRITE) != 0) ? 'w' : '-');
4703 	return (buf);
4704 }
4705 
4706 dladm_status_t
4707 dladm_get_state(dladm_handle_t handle, datalink_id_t linkid,
4708     link_state_t *state)
4709 {
4710 	uint_t			perms;
4711 
4712 	return (i_dladm_get_public_prop(handle, linkid, "state", 0,
4713 	    &perms, state, sizeof (*state)));
4714 }
4715 
4716 boolean_t
4717 dladm_attr_is_linkprop(const char *name)
4718 {
4719 	/* non-property attribute names */
4720 	const char *nonprop[] = {
4721 		/* dlmgmtd core attributes */
4722 		"name",
4723 		"class",
4724 		"media",
4725 		FPHYMAJ,
4726 		FPHYINST,
4727 		FDEVNAME,
4728 
4729 		/* other attributes for vlan, aggr, etc */
4730 		DLADM_ATTR_NAMES
4731 	};
4732 	boolean_t	is_nonprop = B_FALSE;
4733 	int		i;
4734 
4735 	for (i = 0; i < sizeof (nonprop) / sizeof (nonprop[0]); i++) {
4736 		if (strcmp(name, nonprop[i]) == 0) {
4737 			is_nonprop = B_TRUE;
4738 			break;
4739 		}
4740 	}
4741 
4742 	return (!is_nonprop);
4743 }
4744 
4745 dladm_status_t
4746 dladm_linkprop_is_set(dladm_handle_t handle, datalink_id_t linkid,
4747     dladm_prop_type_t type, const char *prop_name, boolean_t *is_set)
4748 {
4749 	char		*buf, **propvals;
4750 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
4751 	int		i;
4752 	dladm_status_t	status = DLADM_STATUS_OK;
4753 	size_t		bufsize;
4754 
4755 	*is_set = B_FALSE;
4756 
4757 	bufsize = (sizeof (char *) + DLADM_PROP_VAL_MAX) *
4758 	    DLADM_MAX_PROP_VALCNT;
4759 	if ((buf = calloc(1, bufsize)) == NULL)
4760 		return (DLADM_STATUS_NOMEM);
4761 
4762 	propvals = (char **)(void *)buf;
4763 	for (i = 0; i < valcnt; i++) {
4764 		propvals[i] = buf +
4765 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
4766 		    i * DLADM_PROP_VAL_MAX;
4767 	}
4768 
4769 	if (dladm_get_linkprop(handle, linkid, type, prop_name, propvals,
4770 	    &valcnt) != DLADM_STATUS_OK) {
4771 		goto done;
4772 	}
4773 
4774 	/*
4775 	 * valcnt is always set to 1 by get_pool(), hence we need to check
4776 	 * for a non-null string to see if it is set. For protection,
4777 	 * secondary-macs and allowed-ips, we can check either the *propval
4778 	 * or the valcnt.
4779 	 */
4780 	if ((strcmp(prop_name, "pool") == 0 ||
4781 	    strcmp(prop_name, "protection") == 0 ||
4782 	    strcmp(prop_name, "secondary-macs") == 0 ||
4783 	    strcmp(prop_name, "allowed-ips") == 0) &&
4784 	    (strlen(*propvals) != 0)) {
4785 		*is_set = B_TRUE;
4786 	} else if ((strcmp(prop_name, "cpus") == 0) && (valcnt != 0)) {
4787 		*is_set = B_TRUE;
4788 	} else if ((strcmp(prop_name, "_softmac") == 0) && (valcnt != 0) &&
4789 	    (strcmp(propvals[0], "true") == 0)) {
4790 		*is_set = B_TRUE;
4791 	}
4792 
4793 done:
4794 	if (buf != NULL)
4795 		free(buf);
4796 	return (status);
4797 }
4798 
4799 /* ARGSUSED */
4800 static dladm_status_t
4801 get_linkmode_prop(dladm_handle_t handle, prop_desc_t *pdp,
4802     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4803     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4804 {
4805 	char			*s;
4806 	uint32_t		v;
4807 	dladm_status_t		status;
4808 
4809 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
4810 	    perm_flags, &v, sizeof (v));
4811 	if (status != DLADM_STATUS_OK)
4812 		return (status);
4813 
4814 	switch (v) {
4815 	case DLADM_PART_CM_MODE:
4816 		s = "cm";
4817 		break;
4818 	case DLADM_PART_UD_MODE:
4819 		s = "ud";
4820 		break;
4821 	default:
4822 		s = "";
4823 		break;
4824 	}
4825 	(void) snprintf(prop_val[0], DLADM_STRSIZE, "%s", s);
4826 
4827 	*val_cnt = 1;
4828 	return (DLADM_STATUS_OK);
4829 }
4830