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