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