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