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