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