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