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