xref: /illumos-gate/usr/src/lib/libdladm/common/linkprop.c (revision a92282e44f968185a6bba094d1e5fece2da819cf)
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 	 * For well-known property names, normalize the case.  We can also
1111 	 * save the property name itself, so that we can just do a pointer
1112 	 * equality test later and avoid an extra strcmp.
1113 	 */
1114 	if (prop_name != NULL) {
1115 		int i;
1116 		for (i = 0; i < DLADM_MAX_PROPS; i++) {
1117 			if (strcasecmp(prop_name, prop_table[i].pd_name) == 0) {
1118 				prop_name = prop_table[i].pd_name;
1119 				break;
1120 			}
1121 		}
1122 	}
1123 
1124 	/*
1125 	 * Check for valid link property against the flags passed
1126 	 * and set the link property when active flag is passed.
1127 	 */
1128 	status = dladm_datalink_id2info(handle, linkid, &link_flags, &class,
1129 	    &media, NULL, 0);
1130 	if (status != DLADM_STATUS_OK)
1131 		return (status);
1132 	status = i_dladm_set_linkprop(handle, linkid, prop_name, prop_val,
1133 	    val_cnt, flags, class, media);
1134 	if (status != DLADM_STATUS_OK)
1135 		return (status);
1136 
1137 	/*
1138 	 * Write an entry to the persistent configuration database if
1139 	 * and only if the user has requested the property to be
1140 	 * persistent and the link is a persistent link.
1141 	 */
1142 	if ((flags & DLADM_OPT_PERSIST) && (link_flags & DLMGMT_PERSIST)) {
1143 		status = i_dladm_set_linkprop_db(handle, linkid, prop_name,
1144 		    prop_val, val_cnt);
1145 
1146 		if (status == DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) {
1147 			prop_desc_t *pdp = prop_table;
1148 			int i;
1149 
1150 			for (i = 0; i < DLADM_MAX_PROPS; i++, pdp++) {
1151 				if (!(pdp->pd_flags & PD_AFTER_PERM))
1152 					continue;
1153 				if (prop_name != NULL &&
1154 				    prop_name != pdp->pd_name)
1155 					continue;
1156 				status = pdp->pd_set(handle, pdp, linkid, NULL,
1157 				    0, flags, 0);
1158 			}
1159 		}
1160 	}
1161 	return (status);
1162 }
1163 
1164 /*
1165  * Walk all link properties of the given specific link.
1166  *
1167  * Note: this function currently lacks the ability to walk _all_ private
1168  * properties if the link, because there is no kernel interface to
1169  * retrieve all known private property names. Once such an interface
1170  * is added, this function should be fixed accordingly.
1171  */
1172 dladm_status_t
1173 dladm_walk_linkprop(dladm_handle_t handle, datalink_id_t linkid, void *arg,
1174     int (*func)(dladm_handle_t, datalink_id_t, const char *, void *))
1175 {
1176 	dladm_status_t		status;
1177 	datalink_class_t	class;
1178 	uint_t			media;
1179 	int			i;
1180 
1181 	if (linkid == DATALINK_INVALID_LINKID || func == NULL)
1182 		return (DLADM_STATUS_BADARG);
1183 
1184 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, &media,
1185 	    NULL, 0);
1186 	if (status != DLADM_STATUS_OK)
1187 		return (status);
1188 
1189 	/* public */
1190 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
1191 		if (!(prop_table[i].pd_class & class))
1192 			continue;
1193 
1194 		if (!DATALINK_MEDIA_ACCEPTED(prop_table[i].pd_dmedia, media))
1195 			continue;
1196 
1197 		if (func(handle, linkid, prop_table[i].pd_name, arg) ==
1198 		    DLADM_WALK_TERMINATE) {
1199 			break;
1200 		}
1201 	}
1202 
1203 	/* private */
1204 	status = i_dladm_walk_linkprop_priv_db(handle, linkid, arg, func);
1205 
1206 	return (status);
1207 }
1208 
1209 /*
1210  * Get linkprop of the given specific link.
1211  */
1212 dladm_status_t
1213 dladm_get_linkprop(dladm_handle_t handle, datalink_id_t linkid,
1214     dladm_prop_type_t type, const char *prop_name, char **prop_val,
1215     uint_t *val_cntp)
1216 {
1217 	dladm_status_t		status = DLADM_STATUS_OK;
1218 	datalink_class_t	class;
1219 	uint_t			media;
1220 	prop_desc_t		*pdp;
1221 	uint_t			cnt, dld_flags = 0;
1222 	int			i;
1223 	uint_t			perm_flags;
1224 
1225 	if (type == DLADM_PROP_VAL_DEFAULT)
1226 		dld_flags |= DLD_PROP_DEFAULT;
1227 	else if (type == DLADM_PROP_VAL_MODIFIABLE)
1228 		dld_flags |= DLD_PROP_POSSIBLE;
1229 
1230 	if (linkid == DATALINK_INVALID_LINKID || prop_name == NULL ||
1231 	    prop_val == NULL || val_cntp == NULL || *val_cntp == 0)
1232 		return (DLADM_STATUS_BADARG);
1233 
1234 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
1235 		if (strcasecmp(prop_name, prop_table[i].pd_name) == 0) {
1236 			prop_name = prop_table[i].pd_name;
1237 			break;
1238 		}
1239 	}
1240 
1241 	if (i == DLADM_MAX_PROPS) {
1242 		if (prop_name[0] == '_') {
1243 			/*
1244 			 * private property.
1245 			 */
1246 			if (type == DLADM_PROP_VAL_PERSISTENT)
1247 				return (i_dladm_get_linkprop_db(handle, linkid,
1248 				    prop_name, prop_val, val_cntp));
1249 			else
1250 				return (i_dladm_get_priv_prop(handle, linkid,
1251 				    prop_name, prop_val, val_cntp, type,
1252 				    dld_flags));
1253 		} else {
1254 			return (DLADM_STATUS_NOTFOUND);
1255 		}
1256 	}
1257 
1258 	pdp = &prop_table[i];
1259 
1260 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, &media,
1261 	    NULL, 0);
1262 	if (status != DLADM_STATUS_OK)
1263 		return (status);
1264 
1265 	if (!(pdp->pd_class & class))
1266 		return (DLADM_STATUS_BADARG);
1267 
1268 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
1269 		return (DLADM_STATUS_BADARG);
1270 
1271 	switch (type) {
1272 	case DLADM_PROP_VAL_CURRENT:
1273 		status = pdp->pd_get(handle, pdp, linkid, prop_val, val_cntp,
1274 		    media, dld_flags, &perm_flags);
1275 		break;
1276 
1277 	case DLADM_PROP_VAL_PERM:
1278 		if (pdp->pd_set == NULL) {
1279 			perm_flags = MAC_PROP_PERM_READ;
1280 		} else {
1281 			status = pdp->pd_get(handle, pdp, linkid, prop_val,
1282 			    val_cntp, media, dld_flags, &perm_flags);
1283 		}
1284 
1285 		*prop_val[0] = '\0';
1286 		*val_cntp = 1;
1287 		if (status == DLADM_STATUS_OK)
1288 			(void) dladm_perm2str(perm_flags, *prop_val);
1289 		break;
1290 
1291 	case DLADM_PROP_VAL_DEFAULT:
1292 		/*
1293 		 * If defaults are not defined for the property,
1294 		 * pd_defval.vd_name should be null. If the driver
1295 		 * has to be contacted for the value, vd_name should
1296 		 * be the empty string (""). Otherwise, dladm will
1297 		 * just print whatever is in the table.
1298 		 */
1299 		if (pdp->pd_defval.vd_name == NULL) {
1300 			status = DLADM_STATUS_NOTSUP;
1301 			break;
1302 		}
1303 
1304 		if (strlen(pdp->pd_defval.vd_name) == 0) {
1305 			status = pdp->pd_get(handle, pdp, linkid, prop_val,
1306 			    val_cntp, media, dld_flags, &perm_flags);
1307 		} else {
1308 			(void) strcpy(*prop_val, pdp->pd_defval.vd_name);
1309 		}
1310 		*val_cntp = 1;
1311 		break;
1312 
1313 	case DLADM_PROP_VAL_MODIFIABLE:
1314 		if (pdp->pd_getmod != NULL) {
1315 			status = pdp->pd_getmod(handle, pdp, linkid, prop_val,
1316 			    val_cntp, media, dld_flags, &perm_flags);
1317 			break;
1318 		}
1319 		cnt = pdp->pd_noptval;
1320 		if (cnt == 0) {
1321 			status = DLADM_STATUS_NOTSUP;
1322 		} else if (cnt > *val_cntp) {
1323 			status = DLADM_STATUS_TOOSMALL;
1324 		} else {
1325 			for (i = 0; i < cnt; i++) {
1326 				(void) strcpy(prop_val[i],
1327 				    pdp->pd_optval[i].vd_name);
1328 			}
1329 			*val_cntp = cnt;
1330 		}
1331 		break;
1332 	case DLADM_PROP_VAL_PERSISTENT:
1333 		if (pdp->pd_flags & PD_TEMPONLY)
1334 			return (DLADM_STATUS_TEMPONLY);
1335 		status = i_dladm_get_linkprop_db(handle, linkid, prop_name,
1336 		    prop_val, val_cntp);
1337 		break;
1338 	default:
1339 		status = DLADM_STATUS_BADARG;
1340 		break;
1341 	}
1342 
1343 	return (status);
1344 }
1345 
1346 /*
1347  * Get linkprop of the given specific link and run any possible conversion
1348  * of the values using the check function for the property. Fails if the
1349  * check function doesn't succeed for the property value.
1350  */
1351 dladm_status_t
1352 dladm_get_linkprop_values(dladm_handle_t handle, datalink_id_t linkid,
1353     dladm_prop_type_t type, const char *prop_name, uint_t *ret_val,
1354     uint_t *val_cntp)
1355 {
1356 	dladm_status_t		status;
1357 	datalink_class_t	class;
1358 	uint_t			media;
1359 	prop_desc_t		*pdp;
1360 	uint_t			dld_flags;
1361 	int			valc, i;
1362 	char			**prop_val;
1363 	uint_t			perm_flags;
1364 
1365 	if (linkid == DATALINK_INVALID_LINKID || prop_name == NULL ||
1366 	    ret_val == NULL || val_cntp == NULL || *val_cntp == 0)
1367 		return (DLADM_STATUS_BADARG);
1368 
1369 	for (pdp = prop_table; pdp < prop_table + DLADM_MAX_PROPS; pdp++) {
1370 		if (strcasecmp(prop_name, pdp->pd_name) == 0) {
1371 			prop_name = pdp->pd_name;
1372 			break;
1373 		}
1374 	}
1375 
1376 	if (pdp == prop_table + DLADM_MAX_PROPS)
1377 		return (DLADM_STATUS_NOTFOUND);
1378 
1379 	if (pdp->pd_flags & PD_CHECK_ALLOC)
1380 		return (DLADM_STATUS_BADARG);
1381 
1382 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, &media,
1383 	    NULL, 0);
1384 	if (status != DLADM_STATUS_OK)
1385 		return (status);
1386 
1387 	if (!(pdp->pd_class & class))
1388 		return (DLADM_STATUS_BADARG);
1389 
1390 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
1391 		return (DLADM_STATUS_BADARG);
1392 
1393 	prop_val = malloc(*val_cntp * sizeof (*prop_val) +
1394 	    *val_cntp * DLADM_PROP_VAL_MAX);
1395 	if (prop_val == NULL)
1396 		return (DLADM_STATUS_NOMEM);
1397 	for (valc = 0; valc < *val_cntp; valc++)
1398 		prop_val[valc] = (char *)(prop_val + *val_cntp) +
1399 		    valc * DLADM_PROP_VAL_MAX;
1400 
1401 	dld_flags = (type == DLADM_PROP_VAL_DEFAULT) ? DLD_PROP_DEFAULT : 0;
1402 
1403 	switch (type) {
1404 	case DLADM_PROP_VAL_CURRENT:
1405 		status = pdp->pd_get(handle, pdp, linkid, prop_val, val_cntp,
1406 		    media, dld_flags, &perm_flags);
1407 		break;
1408 
1409 	case DLADM_PROP_VAL_DEFAULT:
1410 		/*
1411 		 * If defaults are not defined for the property,
1412 		 * pd_defval.vd_name should be null. If the driver
1413 		 * has to be contacted for the value, vd_name should
1414 		 * be the empty string (""). Otherwise, dladm will
1415 		 * just print whatever is in the table.
1416 		 */
1417 		if (pdp->pd_defval.vd_name == NULL) {
1418 			status = DLADM_STATUS_NOTSUP;
1419 			break;
1420 		}
1421 
1422 		if (pdp->pd_defval.vd_name[0] != '\0') {
1423 			*val_cntp = 1;
1424 			*ret_val = pdp->pd_defval.vd_val;
1425 			free(prop_val);
1426 			return (DLADM_STATUS_OK);
1427 		}
1428 		status = pdp->pd_get(handle, pdp, linkid, prop_val, val_cntp,
1429 		    media, dld_flags, &perm_flags);
1430 		break;
1431 
1432 	case DLADM_PROP_VAL_PERSISTENT:
1433 		if (pdp->pd_flags & PD_TEMPONLY)
1434 			status = DLADM_STATUS_TEMPONLY;
1435 		else
1436 			status = i_dladm_get_linkprop_db(handle, linkid,
1437 			    prop_name, prop_val, val_cntp);
1438 		break;
1439 
1440 	default:
1441 		status = DLADM_STATUS_BADARG;
1442 		break;
1443 	}
1444 
1445 	if (status == DLADM_STATUS_OK) {
1446 		if (pdp->pd_check != NULL) {
1447 			val_desc_t *vdp;
1448 
1449 			vdp = malloc(sizeof (val_desc_t) * *val_cntp);
1450 			if (vdp == NULL)
1451 				status = DLADM_STATUS_NOMEM;
1452 			else
1453 				status = pdp->pd_check(handle, pdp, linkid,
1454 				    prop_val, val_cntp, 0, &vdp, media);
1455 			if (status == DLADM_STATUS_OK) {
1456 				for (valc = 0; valc < *val_cntp; valc++)
1457 					ret_val[valc] = vdp[valc].vd_val;
1458 			}
1459 			free(vdp);
1460 		} else {
1461 			for (valc = 0; valc < *val_cntp; valc++) {
1462 				for (i = 0; i < pdp->pd_noptval; i++) {
1463 					if (strcmp(pdp->pd_optval[i].vd_name,
1464 					    prop_val[valc]) == 0) {
1465 						ret_val[valc] =
1466 						    pdp->pd_optval[i].vd_val;
1467 						break;
1468 					}
1469 				}
1470 				if (i == pdp->pd_noptval) {
1471 					status = DLADM_STATUS_FAILED;
1472 					break;
1473 				}
1474 			}
1475 		}
1476 	}
1477 
1478 	free(prop_val);
1479 
1480 	return (status);
1481 }
1482 
1483 /*ARGSUSED*/
1484 static int
1485 i_dladm_init_one_prop(dladm_handle_t handle, datalink_id_t linkid,
1486     const char *prop_name, void *arg)
1487 {
1488 	char			*buf, **propvals;
1489 	uint_t			i, valcnt = DLADM_MAX_PROP_VALCNT;
1490 	dladm_status_t		status;
1491 	dladm_linkprop_args_t	*dla = arg;
1492 
1493 	if ((buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
1494 	    DLADM_MAX_PROP_VALCNT)) == NULL) {
1495 		return (DLADM_WALK_CONTINUE);
1496 	}
1497 
1498 	propvals = (char **)(void *)buf;
1499 	for (i = 0; i < valcnt; i++) {
1500 		propvals[i] = buf +
1501 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
1502 		    i * DLADM_PROP_VAL_MAX;
1503 	}
1504 
1505 	if (dladm_get_linkprop(handle, linkid, DLADM_PROP_VAL_PERSISTENT,
1506 	    prop_name, propvals, &valcnt) != DLADM_STATUS_OK) {
1507 		goto done;
1508 	}
1509 
1510 	status = dladm_set_linkprop(handle, linkid, prop_name, propvals,
1511 	    valcnt, dla->dla_flags | DLADM_OPT_ACTIVE);
1512 
1513 	if (status != DLADM_STATUS_OK)
1514 		dla->dla_status = status;
1515 
1516 done:
1517 	if (buf != NULL)
1518 		free(buf);
1519 
1520 	return (DLADM_WALK_CONTINUE);
1521 }
1522 
1523 /*ARGSUSED*/
1524 static int
1525 i_dladm_init_linkprop(dladm_handle_t handle, datalink_id_t linkid, void *arg)
1526 {
1527 	datalink_class_t	class;
1528 	dladm_status_t		status;
1529 
1530 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, NULL,
1531 	    NULL, 0);
1532 	if (status != DLADM_STATUS_OK)
1533 		return (DLADM_WALK_TERMINATE);
1534 
1535 	if ((class & (DATALINK_CLASS_VNIC | DATALINK_CLASS_VLAN)) == 0)
1536 		(void) dladm_init_linkprop(handle, linkid, B_TRUE);
1537 
1538 	return (DLADM_WALK_CONTINUE);
1539 }
1540 
1541 dladm_status_t
1542 dladm_init_linkprop(dladm_handle_t handle, datalink_id_t linkid,
1543     boolean_t any_media)
1544 {
1545 	dladm_status_t		status = DLADM_STATUS_OK;
1546 	datalink_media_t	dmedia;
1547 	uint32_t		media;
1548 	dladm_linkprop_args_t	*dla;
1549 
1550 	dmedia = any_media ? DATALINK_ANY_MEDIATYPE : DL_WIFI;
1551 
1552 	dla = malloc(sizeof (dladm_linkprop_args_t));
1553 	if (dla == NULL)
1554 		return (DLADM_STATUS_NOMEM);
1555 	dla->dla_flags = DLADM_OPT_BOOT;
1556 	dla->dla_status = DLADM_STATUS_OK;
1557 
1558 	if (linkid == DATALINK_ALL_LINKID) {
1559 		(void) dladm_walk_datalink_id(i_dladm_init_linkprop, handle,
1560 		    NULL, DATALINK_CLASS_ALL, dmedia, DLADM_OPT_PERSIST);
1561 	} else if (any_media ||
1562 	    ((dladm_datalink_id2info(handle, linkid, NULL, NULL, &media, NULL,
1563 	    0) == DLADM_STATUS_OK) &&
1564 	    DATALINK_MEDIA_ACCEPTED(dmedia, media))) {
1565 		(void) dladm_walk_linkprop(handle, linkid, (void *)dla,
1566 		    i_dladm_init_one_prop);
1567 		status = dla->dla_status;
1568 	}
1569 	free(dla);
1570 	return (status);
1571 }
1572 
1573 /* ARGSUSED */
1574 static dladm_status_t
1575 get_zone(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1576     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1577     uint_t flags, uint_t *perm_flags)
1578 {
1579 	char			zone_name[ZONENAME_MAX];
1580 	zoneid_t		zid;
1581 	dladm_status_t		status;
1582 
1583 	if (flags != 0)
1584 		return (DLADM_STATUS_NOTSUP);
1585 
1586 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
1587 	    perm_flags, &zid, sizeof (zid));
1588 	if (status != DLADM_STATUS_OK)
1589 		return (status);
1590 
1591 	*val_cnt = 1;
1592 	if (zid != GLOBAL_ZONEID) {
1593 		if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0) {
1594 			return (dladm_errno2status(errno));
1595 		}
1596 
1597 		(void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX);
1598 	} else {
1599 		*prop_val[0] = '\0';
1600 	}
1601 
1602 	return (DLADM_STATUS_OK);
1603 }
1604 
1605 typedef int (*zone_get_devroot_t)(char *, char *, size_t);
1606 
1607 static int
1608 i_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen)
1609 {
1610 	char			root[MAXPATHLEN];
1611 	zone_get_devroot_t	real_zone_get_devroot;
1612 	void			*dlhandle;
1613 	void			*sym;
1614 	int			ret;
1615 
1616 	if ((dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY)) == NULL)
1617 		return (-1);
1618 
1619 	if ((sym = dlsym(dlhandle, "zone_get_devroot")) == NULL) {
1620 		(void) dlclose(dlhandle);
1621 		return (-1);
1622 	}
1623 
1624 	real_zone_get_devroot = (zone_get_devroot_t)sym;
1625 
1626 	if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0)
1627 		(void) snprintf(dev, devlen, "%s%s", root, "/dev");
1628 	(void) dlclose(dlhandle);
1629 	return (ret);
1630 }
1631 
1632 static dladm_status_t
1633 i_dladm_update_deventry(dladm_handle_t handle, zoneid_t zid,
1634     datalink_id_t linkid, boolean_t add)
1635 {
1636 	char		path[MAXPATHLEN];
1637 	char		name[MAXLINKNAMELEN];
1638 	di_prof_t	prof = NULL;
1639 	char		zone_name[ZONENAME_MAX];
1640 	dladm_status_t	status;
1641 	int		ret;
1642 
1643 	if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
1644 		return (dladm_errno2status(errno));
1645 	if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
1646 		return (dladm_errno2status(errno));
1647 	if (di_prof_init(path, &prof) != 0)
1648 		return (dladm_errno2status(errno));
1649 
1650 	status = dladm_linkid2legacyname(handle, linkid, name, MAXLINKNAMELEN);
1651 	if (status != DLADM_STATUS_OK)
1652 		goto cleanup;
1653 
1654 	if (add)
1655 		ret = di_prof_add_dev(prof, name);
1656 	else
1657 		ret = di_prof_add_exclude(prof, name);
1658 
1659 	if (ret != 0) {
1660 		status = dladm_errno2status(errno);
1661 		goto cleanup;
1662 	}
1663 
1664 	if (di_prof_commit(prof) != 0)
1665 		status = dladm_errno2status(errno);
1666 cleanup:
1667 	if (prof)
1668 		di_prof_fini(prof);
1669 
1670 	return (status);
1671 }
1672 
1673 /* ARGSUSED */
1674 static dladm_status_t
1675 set_zone(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1676     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
1677 {
1678 	dladm_status_t		status = DLADM_STATUS_OK;
1679 	zoneid_t		zid_old, zid_new;
1680 	dld_ioc_zid_t		*dzp;
1681 
1682 	if (val_cnt != 1)
1683 		return (DLADM_STATUS_BADVALCNT);
1684 
1685 	dzp = (dld_ioc_zid_t *)vdp->vd_val;
1686 
1687 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
1688 	    NULL, &zid_old, sizeof (zid_old));
1689 	if (status != DLADM_STATUS_OK)
1690 		return (status);
1691 
1692 	zid_new = dzp->diz_zid;
1693 	if (zid_new == zid_old)
1694 		return (DLADM_STATUS_OK);
1695 
1696 	if ((status = set_public_prop(handle, pdp, linkid, vdp, val_cnt,
1697 	    flags, media)) != DLADM_STATUS_OK)
1698 		return (status);
1699 
1700 	/*
1701 	 * It is okay to fail to update the /dev entry (some vanity-named
1702 	 * links do not have a /dev entry).
1703 	 */
1704 	if (zid_old != GLOBAL_ZONEID) {
1705 		(void) i_dladm_update_deventry(handle, zid_old, linkid,
1706 		    B_FALSE);
1707 	}
1708 	if (zid_new != GLOBAL_ZONEID)
1709 		(void) i_dladm_update_deventry(handle, zid_new, linkid, B_TRUE);
1710 
1711 	return (DLADM_STATUS_OK);
1712 }
1713 
1714 /* ARGSUSED */
1715 static dladm_status_t
1716 check_zone(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1717     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
1718     datalink_media_t media)
1719 {
1720 	char		*zone_name;
1721 	zoneid_t	zoneid;
1722 	dladm_status_t	status = DLADM_STATUS_OK;
1723 	dld_ioc_zid_t	*dzp;
1724 	uint_t		val_cnt = *val_cntp;
1725 	val_desc_t	*vdp = *vdpp;
1726 
1727 	if (val_cnt != 1)
1728 		return (DLADM_STATUS_BADVALCNT);
1729 
1730 	dzp = malloc(sizeof (dld_ioc_zid_t));
1731 	if (dzp == NULL)
1732 		return (DLADM_STATUS_NOMEM);
1733 
1734 	zone_name = (prop_val != NULL) ? *prop_val : GLOBAL_ZONENAME;
1735 	if ((zoneid = getzoneidbyname(zone_name)) == -1) {
1736 		status = DLADM_STATUS_BADVAL;
1737 		goto done;
1738 	}
1739 
1740 	if (zoneid != GLOBAL_ZONEID) {
1741 		ushort_t	flags;
1742 
1743 		if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &flags,
1744 		    sizeof (flags)) < 0) {
1745 			status = dladm_errno2status(errno);
1746 			goto done;
1747 		}
1748 
1749 		if (!(flags & ZF_NET_EXCL)) {
1750 			status = DLADM_STATUS_BADVAL;
1751 			goto done;
1752 		}
1753 	}
1754 
1755 	(void) memset(dzp, 0, sizeof (dld_ioc_zid_t));
1756 
1757 	dzp->diz_zid = zoneid;
1758 	dzp->diz_linkid = linkid;
1759 
1760 	vdp->vd_val = (uintptr_t)dzp;
1761 	return (DLADM_STATUS_OK);
1762 done:
1763 	free(dzp);
1764 	return (status);
1765 }
1766 
1767 /* ARGSUSED */
1768 static dladm_status_t
1769 get_maxbw(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1770     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1771     uint_t flags, uint_t *perm_flags)
1772 {
1773 	mac_resource_props_t	mrp;
1774 	dladm_status_t		status;
1775 
1776 	status = i_dladm_get_public_prop(handle, linkid, "resource", flags,
1777 	    perm_flags, &mrp, sizeof (mrp));
1778 	if (status != DLADM_STATUS_OK)
1779 		return (status);
1780 
1781 	if ((mrp.mrp_mask & MRP_MAXBW) == 0) {
1782 		*val_cnt = 0;
1783 		return (DLADM_STATUS_OK);
1784 	}
1785 
1786 	(void) dladm_bw2str(mrp.mrp_maxbw, prop_val[0]);
1787 	*val_cnt = 1;
1788 	return (DLADM_STATUS_OK);
1789 }
1790 
1791 /* ARGSUSED */
1792 static dladm_status_t
1793 check_maxbw(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1794     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
1795     datalink_media_t media)
1796 {
1797 	uint64_t	*maxbw;
1798 	dladm_status_t	status = DLADM_STATUS_OK;
1799 	uint_t		val_cnt = *val_cntp;
1800 	val_desc_t	*vdp = *vdpp;
1801 
1802 	if (val_cnt != 1)
1803 		return (DLADM_STATUS_BADVALCNT);
1804 
1805 	maxbw = malloc(sizeof (uint64_t));
1806 	if (maxbw == NULL)
1807 		return (DLADM_STATUS_NOMEM);
1808 
1809 	status = dladm_str2bw(*prop_val, maxbw);
1810 	if (status != DLADM_STATUS_OK) {
1811 		free(maxbw);
1812 		return (status);
1813 	}
1814 
1815 	if ((*maxbw < MRP_MAXBW_MINVAL) && (*maxbw != 0)) {
1816 		free(maxbw);
1817 		return (DLADM_STATUS_MINMAXBW);
1818 	}
1819 
1820 	vdp->vd_val = (uintptr_t)maxbw;
1821 	return (DLADM_STATUS_OK);
1822 }
1823 
1824 /* ARGSUSED */
1825 dladm_status_t
1826 extract_maxbw(val_desc_t *vdp, uint_t cnt, void *arg)
1827 {
1828 	mac_resource_props_t *mrp = arg;
1829 
1830 	if (vdp->vd_val == RESET_VAL) {
1831 		mrp->mrp_maxbw = MRP_MAXBW_RESETVAL;
1832 	} else {
1833 		bcopy((char *)vdp->vd_val, &mrp->mrp_maxbw, sizeof (uint64_t));
1834 	}
1835 	mrp->mrp_mask |= MRP_MAXBW;
1836 
1837 	return (DLADM_STATUS_OK);
1838 }
1839 
1840 /* ARGSUSED */
1841 static dladm_status_t
1842 get_cpus(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1843     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1844     uint_t flags, uint_t *perm_flags)
1845 {
1846 	dladm_status_t		status;
1847 	mac_resource_props_t	mrp;
1848 	mac_propval_range_t	*pv_range;
1849 	int			err;
1850 
1851 	if (strcmp(pdp->pd_name, "cpus-effective") == 0) {
1852 		status = i_dladm_get_public_prop(handle, linkid,
1853 		    "resource-effective", flags, perm_flags, &mrp,
1854 		    sizeof (mrp));
1855 	} else {
1856 		status = i_dladm_get_public_prop(handle, linkid,
1857 		    "resource", flags, perm_flags, &mrp, sizeof (mrp));
1858 	}
1859 
1860 	if (status != DLADM_STATUS_OK)
1861 		return (status);
1862 
1863 	if (mrp.mrp_ncpus > *val_cnt)
1864 		return (DLADM_STATUS_TOOSMALL);
1865 
1866 	if (mrp.mrp_ncpus == 0) {
1867 		*val_cnt = 0;
1868 		return (DLADM_STATUS_OK);
1869 	}
1870 
1871 	/* Sort CPU list and convert it to a mac_propval_range */
1872 	status = dladm_list2range(mrp.mrp_cpu, mrp.mrp_ncpus,
1873 	    MAC_PROPVAL_UINT32, &pv_range);
1874 	if (status != DLADM_STATUS_OK)
1875 		return (status);
1876 
1877 	/* Write CPU ranges and individual CPUs */
1878 	err = dladm_range2strs(pv_range, prop_val);
1879 	if (err != 0) {
1880 		free(pv_range);
1881 		return (dladm_errno2status(err));
1882 	}
1883 
1884 	*val_cnt = pv_range->mpr_count;
1885 	free(pv_range);
1886 
1887 	return (DLADM_STATUS_OK);
1888 }
1889 
1890 /* ARGSUSED */
1891 static dladm_status_t
1892 check_cpus(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1893     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
1894     datalink_media_t media)
1895 {
1896 	int			i, j, rc;
1897 	long			nproc = sysconf(_SC_NPROCESSORS_CONF);
1898 	mac_resource_props_t	mrp;
1899 	mac_propval_range_t	*pv_range;
1900 	uint_t			perm_flags;
1901 	uint32_t		ncpus;
1902 	uint32_t		*cpus = mrp.mrp_cpu;
1903 	val_desc_t		*vdp = *vdpp;
1904 	val_desc_t		*newvdp;
1905 	uint_t			val_cnt = *val_cntp;
1906 	dladm_status_t		status = DLADM_STATUS_OK;
1907 
1908 	/* Get the current pool property */
1909 	status = i_dladm_get_public_prop(handle, linkid, "resource", 0,
1910 	    &perm_flags, &mrp, sizeof (mrp));
1911 
1912 	if (status == DLADM_STATUS_OK) {
1913 		/* Can't set cpus if a pool is set */
1914 		if (strlen(mrp.mrp_pool) != 0)
1915 			return (DLADM_STATUS_POOLCPU);
1916 	}
1917 
1918 	/* Read ranges and convert to mac_propval_range */
1919 	status = dladm_strs2range(prop_val, val_cnt, MAC_PROPVAL_UINT32,
1920 	    &pv_range);
1921 	if (status != DLADM_STATUS_OK)
1922 		goto done1;
1923 
1924 	/* Convert mac_propval_range to a single CPU list */
1925 	ncpus = MRP_NCPUS;
1926 	status = dladm_range2list(pv_range, cpus, &ncpus);
1927 	if (status != DLADM_STATUS_OK)
1928 		goto done1;
1929 
1930 	/*
1931 	 * If a range of CPUs was entered, update value count and reallocate
1932 	 * the array of val_desc_t's.  The array allocated was sized for
1933 	 * indvidual elements, but needs to be reallocated to accomodate the
1934 	 * expanded list of CPUs.
1935 	 */
1936 	if (val_cnt < ncpus) {
1937 		newvdp = calloc(*val_cntp, sizeof (val_desc_t));
1938 		if (newvdp == NULL) {
1939 			status = DLADM_STATUS_NOMEM;
1940 			goto done1;
1941 		}
1942 		vdp = newvdp;
1943 	}
1944 
1945 	/* Check if all CPUs in the list are online */
1946 	for (i = 0; i < ncpus; i++) {
1947 		if (cpus[i] >= nproc) {
1948 			status = DLADM_STATUS_BADCPUID;
1949 			goto done2;
1950 		}
1951 
1952 		rc = p_online(cpus[i], P_STATUS);
1953 		if (rc < 1) {
1954 			status = DLADM_STATUS_CPUERR;
1955 			goto done2;
1956 		}
1957 
1958 		if (rc != P_ONLINE) {
1959 			status = DLADM_STATUS_CPUNOTONLINE;
1960 			goto done2;
1961 		}
1962 
1963 		vdp[i].vd_val = (uintptr_t)cpus[i];
1964 	}
1965 
1966 	/* Check for duplicate CPUs */
1967 	for (i = 0; i < *val_cntp; i++) {
1968 		for (j = 0; j < *val_cntp; j++) {
1969 			if (i != j && vdp[i].vd_val == vdp[j].vd_val) {
1970 				status = DLADM_STATUS_BADVAL;
1971 				goto done2;
1972 			}
1973 		}
1974 	}
1975 
1976 	/* Update *val_cntp and *vdpp if everything was OK */
1977 	if (val_cnt < ncpus) {
1978 		*val_cntp = ncpus;
1979 		free(*vdpp);
1980 		*vdpp = newvdp;
1981 	}
1982 
1983 	status = DLADM_STATUS_OK;
1984 	goto done1;
1985 
1986 done2:
1987 	free(newvdp);
1988 done1:
1989 	free(pv_range);
1990 	return (status);
1991 }
1992 
1993 /* ARGSUSED */
1994 dladm_status_t
1995 extract_cpus(val_desc_t *vdp, uint_t cnt, void *arg)
1996 {
1997 	mac_resource_props_t	*mrp = arg;
1998 	int			i;
1999 
2000 	if (vdp[0].vd_val == RESET_VAL) {
2001 		bzero(&mrp->mrp_cpus, sizeof (mac_cpus_t));
2002 		mrp->mrp_mask |= MRP_CPUS;
2003 		return (DLADM_STATUS_OK);
2004 	}
2005 
2006 	for (i = 0; i < cnt; i++)
2007 		mrp->mrp_cpu[i] = (uint32_t)vdp[i].vd_val;
2008 
2009 	mrp->mrp_ncpus = cnt;
2010 	mrp->mrp_mask |= (MRP_CPUS|MRP_CPUS_USERSPEC);
2011 	mrp->mrp_fanout_mode = MCM_CPUS;
2012 	mrp->mrp_rx_intr_cpu = -1;
2013 
2014 	return (DLADM_STATUS_OK);
2015 }
2016 
2017 /*
2018  * Get the pool datalink property from the kernel.  This is used
2019  * for both the user specified pool and effective pool properties.
2020  */
2021 /* ARGSUSED */
2022 static dladm_status_t
2023 get_pool(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2024     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2025     uint_t flags, uint_t *perm_flags)
2026 {
2027 	mac_resource_props_t	mrp;
2028 	dladm_status_t		status;
2029 
2030 	if (strcmp(pdp->pd_name, "pool-effective") == 0) {
2031 		status = i_dladm_get_public_prop(handle, linkid,
2032 		    "resource-effective", flags, perm_flags, &mrp,
2033 		    sizeof (mrp));
2034 	} else {
2035 		status = i_dladm_get_public_prop(handle, linkid,
2036 		    "resource", flags, perm_flags, &mrp, sizeof (mrp));
2037 	}
2038 
2039 	if (status != DLADM_STATUS_OK)
2040 		return (status);
2041 
2042 	if (strlen(mrp.mrp_pool) == 0) {
2043 		(*prop_val)[0] = '\0';
2044 	} else {
2045 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
2046 		    "%s", mrp.mrp_pool);
2047 	}
2048 	*val_cnt = 1;
2049 
2050 	return (DLADM_STATUS_OK);
2051 }
2052 
2053 /* ARGSUSED */
2054 static dladm_status_t
2055 check_pool(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2056     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
2057     datalink_media_t media)
2058 {
2059 	pool_conf_t		*poolconf;
2060 	pool_t			*pool;
2061 	mac_resource_props_t	mrp;
2062 	dladm_status_t		status;
2063 	uint_t			perm_flags;
2064 	char			*poolname;
2065 	val_desc_t		*vdp = *vdpp;
2066 
2067 	/* Get the current cpus property */
2068 	status = i_dladm_get_public_prop(handle, linkid, "resource", 0,
2069 	    &perm_flags, &mrp, sizeof (mrp));
2070 
2071 	if (status == DLADM_STATUS_OK) {
2072 		/* Can't set pool if cpus are set */
2073 		if (mrp.mrp_ncpus != 0)
2074 			return (DLADM_STATUS_POOLCPU);
2075 	}
2076 
2077 	poolname = malloc(sizeof (mrp.mrp_pool));
2078 	if (poolname == NULL)
2079 		return (DLADM_STATUS_NOMEM);
2080 
2081 	/* Check for pool's availability if not booting */
2082 	if ((flags & DLADM_OPT_BOOT) == 0) {
2083 
2084 		/* Allocate and open pool configuration */
2085 		if ((poolconf = pool_conf_alloc()) == NULL)
2086 			return (DLADM_STATUS_BADVAL);
2087 
2088 		if (pool_conf_open(poolconf, pool_dynamic_location(), PO_RDONLY)
2089 		    != PO_SUCCESS) {
2090 			pool_conf_free(poolconf);
2091 			return (DLADM_STATUS_BADVAL);
2092 		}
2093 
2094 		/* Look for pool name */
2095 		if ((pool = pool_get_pool(poolconf, *prop_val)) == NULL) {
2096 			pool_conf_free(poolconf);
2097 			return (DLADM_STATUS_BADVAL);
2098 		}
2099 
2100 		pool_conf_free(poolconf);
2101 		free(pool);
2102 	}
2103 
2104 	(void) strlcpy(poolname, *prop_val, sizeof (mrp.mrp_pool));
2105 	vdp->vd_val = (uintptr_t)poolname;
2106 
2107 	return (DLADM_STATUS_OK);
2108 }
2109 
2110 /* ARGSUSED */
2111 dladm_status_t
2112 extract_pool(val_desc_t *vdp, uint_t cnt, void *arg)
2113 {
2114 	mac_resource_props_t	*mrp = (mac_resource_props_t *)arg;
2115 
2116 	if (vdp->vd_val == RESET_VAL) {
2117 		bzero(&mrp->mrp_pool, sizeof (mrp->mrp_pool));
2118 		mrp->mrp_mask |= MRP_POOL;
2119 		return (DLADM_STATUS_OK);
2120 	}
2121 
2122 	(void) strlcpy(mrp->mrp_pool, (char *)vdp->vd_val,
2123 	    sizeof (mrp->mrp_pool));
2124 	mrp->mrp_mask |= MRP_POOL;
2125 	/*
2126 	 * Use MCM_CPUS since the fanout count is not user specified
2127 	 * and will be determined by the cpu list generated from the
2128 	 * pool.
2129 	 */
2130 	mrp->mrp_fanout_mode = MCM_CPUS;
2131 
2132 	return (DLADM_STATUS_OK);
2133 }
2134 
2135 /* ARGSUSED */
2136 static dladm_status_t
2137 get_priority(dladm_handle_t handle, prop_desc_t *pdp,
2138     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2139     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2140 {
2141 	mac_resource_props_t	mrp;
2142 	mac_priority_level_t	pri;
2143 	dladm_status_t		status;
2144 
2145 	status = i_dladm_get_public_prop(handle, linkid, "resource", flags,
2146 	    perm_flags, &mrp, sizeof (mrp));
2147 	if (status != DLADM_STATUS_OK)
2148 		return (status);
2149 
2150 	pri = ((mrp.mrp_mask & MRP_PRIORITY) == 0) ? MPL_HIGH :
2151 	    mrp.mrp_priority;
2152 
2153 	(void) dladm_pri2str(pri, prop_val[0]);
2154 	*val_cnt = 1;
2155 	return (DLADM_STATUS_OK);
2156 }
2157 
2158 /* ARGSUSED */
2159 dladm_status_t
2160 extract_priority(val_desc_t *vdp, uint_t cnt, void *arg)
2161 {
2162 	mac_resource_props_t *mrp = arg;
2163 
2164 	if (cnt != 1)
2165 		return (DLADM_STATUS_BADVAL);
2166 
2167 	mrp->mrp_priority = (mac_priority_level_t)vdp->vd_val;
2168 	mrp->mrp_mask |= MRP_PRIORITY;
2169 
2170 	return (DLADM_STATUS_OK);
2171 }
2172 
2173 /*
2174  * Determines the size of the structure that needs to be sent to drivers
2175  * for retrieving the property range values.
2176  */
2177 static int
2178 i_dladm_range_size(mac_propval_range_t *r, size_t *sz, uint_t *rcount)
2179 {
2180 	uint_t count = r->mpr_count;
2181 
2182 	*sz = sizeof (mac_propval_range_t);
2183 	*rcount = count;
2184 	--count;
2185 
2186 	switch (r->mpr_type) {
2187 	case MAC_PROPVAL_UINT32:
2188 		*sz += (count * sizeof (mac_propval_uint32_range_t));
2189 		return (0);
2190 	default:
2191 		break;
2192 	}
2193 	*sz = 0;
2194 	*rcount = 0;
2195 	return (EINVAL);
2196 }
2197 
2198 
2199 /* ARGSUSED */
2200 static dladm_status_t
2201 check_rings(dladm_handle_t handle, prop_desc_t *pdp,
2202     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
2203     val_desc_t **vp, datalink_media_t media)
2204 {
2205 	uint_t		val_cnt = *val_cntp;
2206 	val_desc_t	*v = *vp;
2207 
2208 	if (val_cnt != 1)
2209 		return (DLADM_STATUS_BADVAL);
2210 	if (strncasecmp(prop_val[0], "hw", strlen("hw")) == 0) {
2211 		v->vd_val = UNSPEC_VAL;
2212 	} else if (strncasecmp(prop_val[0], "sw", strlen("sw")) == 0) {
2213 		v->vd_val = 0;
2214 	} else {
2215 		v->vd_val = strtoul(prop_val[0], NULL, 0);
2216 		if (v->vd_val == 0)
2217 			return (DLADM_STATUS_BADVAL);
2218 	}
2219 	return (DLADM_STATUS_OK);
2220 }
2221 
2222 /* ARGSUSED */
2223 static dladm_status_t
2224 get_rings_range(dladm_handle_t handle, prop_desc_t *pdp,
2225     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2226     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2227 {
2228 	dld_ioc_macprop_t *dip;
2229 	dladm_status_t status = DLADM_STATUS_OK;
2230 	mac_propval_range_t *rangep;
2231 	size_t	sz;
2232 	mac_propval_uint32_range_t *ur;
2233 
2234 	sz = sizeof (mac_propval_range_t);
2235 
2236 	if ((dip = i_dladm_buf_alloc_by_name(sz, linkid, pdp->pd_name, flags,
2237 	    &status)) == NULL)
2238 		return (status);
2239 
2240 	status = i_dladm_macprop(handle, dip, B_FALSE);
2241 	if (status != DLADM_STATUS_OK)
2242 		return (status);
2243 
2244 	rangep = (mac_propval_range_t *)(void *)&dip->pr_val;
2245 	*val_cnt = 1;
2246 	ur = &rangep->mpr_range_uint32[0];
2247 	/* This is the case where the dev doesn't have any rings/groups */
2248 	if (rangep->mpr_count == 0) {
2249 		(*prop_val)[0] = '\0';
2250 	/*
2251 	 * This is the case where the dev supports rings, but static
2252 	 * grouping.
2253 	 */
2254 	} else if (ur->mpur_min == ur->mpur_max &&
2255 	    ur->mpur_max == 0) {
2256 		(void) snprintf(prop_val[0], DLADM_PROP_VAL_MAX, "sw,hw");
2257 	/*
2258 	 * This is the case where the dev supports rings and dynamic
2259 	 * grouping, but has only one value (say 2 rings and 2 groups).
2260 	 */
2261 	} else if (ur->mpur_min == ur->mpur_max) {
2262 		(void) snprintf(prop_val[0], DLADM_PROP_VAL_MAX, "sw,hw,%d",
2263 		    ur->mpur_min);
2264 	/*
2265 	 * This is the case where the dev supports rings and dynamic
2266 	 * grouping and has a range of rings.
2267 	 */
2268 	} else {
2269 		(void) snprintf(prop_val[0], DLADM_PROP_VAL_MAX,
2270 		    "sw,hw,<%ld-%ld>", ur->mpur_min, ur->mpur_max);
2271 	}
2272 	free(dip);
2273 	return (status);
2274 }
2275 
2276 
2277 /* ARGSUSED */
2278 static dladm_status_t
2279 get_rxrings(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2280     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2281     uint_t flags, uint_t *perm_flags)
2282 {
2283 	mac_resource_props_t	mrp;
2284 	dladm_status_t		status;
2285 	uint32_t		nrings = 0;
2286 
2287 	/*
2288 	 * Get the number of (effective-)rings from the resource property.
2289 	 */
2290 	if (strcmp(pdp->pd_name, "rxrings-effective") == 0) {
2291 		status = i_dladm_get_public_prop(handle, linkid,
2292 		    "resource-effective", flags, perm_flags, &mrp,
2293 		    sizeof (mrp));
2294 	} else {
2295 		/*
2296 		 * Get the permissions from the "rxrings" property.
2297 		 */
2298 		status = i_dladm_get_public_prop(handle, linkid, "rxrings",
2299 		    flags, perm_flags, NULL, 0);
2300 		if (status != DLADM_STATUS_OK)
2301 			return (status);
2302 
2303 		status = i_dladm_get_public_prop(handle, linkid,
2304 		    "resource", flags, NULL, &mrp, sizeof (mrp));
2305 	}
2306 
2307 	if (status != DLADM_STATUS_OK)
2308 		return (status);
2309 
2310 	if ((mrp.mrp_mask & MRP_RX_RINGS) == 0) {
2311 		*val_cnt = 0;
2312 		return (DLADM_STATUS_OK);
2313 	}
2314 	nrings = mrp.mrp_nrxrings;
2315 	*val_cnt = 1;
2316 	if (mrp.mrp_mask & MRP_RXRINGS_UNSPEC)
2317 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "hw");
2318 	else if (nrings == 0)
2319 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "sw");
2320 	else
2321 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%ld", nrings);
2322 	return (DLADM_STATUS_OK);
2323 }
2324 
2325 /* ARGSUSED */
2326 dladm_status_t
2327 extract_rxrings(val_desc_t *vdp, uint_t cnt, void *arg)
2328 {
2329 	mac_resource_props_t	*mrp = (mac_resource_props_t *)arg;
2330 
2331 	mrp->mrp_nrxrings = 0;
2332 	if (vdp->vd_val == RESET_VAL)
2333 		mrp->mrp_mask = MRP_RINGS_RESET;
2334 	else if (vdp->vd_val == UNSPEC_VAL)
2335 		mrp->mrp_mask = MRP_RXRINGS_UNSPEC;
2336 	else
2337 		mrp->mrp_nrxrings = vdp->vd_val;
2338 	mrp->mrp_mask |= MRP_RX_RINGS;
2339 
2340 	return (DLADM_STATUS_OK);
2341 }
2342 
2343 /* ARGSUSED */
2344 static dladm_status_t
2345 get_txrings(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2346     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2347     uint_t flags, uint_t *perm_flags)
2348 {
2349 	mac_resource_props_t	mrp;
2350 	dladm_status_t		status;
2351 	uint32_t		nrings = 0;
2352 
2353 
2354 	/*
2355 	 * Get the number of (effective-)rings from the resource property.
2356 	 */
2357 	if (strcmp(pdp->pd_name, "txrings-effective") == 0) {
2358 		status = i_dladm_get_public_prop(handle, linkid,
2359 		    "resource-effective", flags, perm_flags, &mrp,
2360 		    sizeof (mrp));
2361 	} else {
2362 		/*
2363 		 * Get the permissions from the "txrings" property.
2364 		 */
2365 		status = i_dladm_get_public_prop(handle, linkid, "txrings",
2366 		    flags, perm_flags, NULL, 0);
2367 		if (status != DLADM_STATUS_OK)
2368 			return (status);
2369 
2370 		/*
2371 		 * Get the number of rings from the "resource" property.
2372 		 */
2373 		status = i_dladm_get_public_prop(handle, linkid, "resource",
2374 		    flags, NULL, &mrp, sizeof (mrp));
2375 	}
2376 
2377 	if (status != DLADM_STATUS_OK)
2378 		return (status);
2379 
2380 	if ((mrp.mrp_mask & MRP_TX_RINGS) == 0) {
2381 		*val_cnt = 0;
2382 		return (DLADM_STATUS_OK);
2383 	}
2384 	nrings = mrp.mrp_ntxrings;
2385 	*val_cnt = 1;
2386 	if (mrp.mrp_mask & MRP_TXRINGS_UNSPEC)
2387 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "hw");
2388 	else if (nrings == 0)
2389 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "sw");
2390 	else
2391 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%ld", nrings);
2392 	return (DLADM_STATUS_OK);
2393 }
2394 
2395 /* ARGSUSED */
2396 dladm_status_t
2397 extract_txrings(val_desc_t *vdp, uint_t cnt, void *arg)
2398 {
2399 	mac_resource_props_t	*mrp = (mac_resource_props_t *)arg;
2400 
2401 	mrp->mrp_ntxrings = 0;
2402 	if (vdp->vd_val == RESET_VAL)
2403 		mrp->mrp_mask = MRP_RINGS_RESET;
2404 	else if (vdp->vd_val == UNSPEC_VAL)
2405 		mrp->mrp_mask = MRP_TXRINGS_UNSPEC;
2406 	else
2407 		mrp->mrp_ntxrings = vdp->vd_val;
2408 	mrp->mrp_mask |= MRP_TX_RINGS;
2409 
2410 	return (DLADM_STATUS_OK);
2411 }
2412 
2413 /* ARGSUSED */
2414 static dladm_status_t
2415 get_cntavail(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2416     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
2417     uint_t *perm_flags)
2418 {
2419 	if (flags & DLD_PROP_DEFAULT)
2420 		return (DLADM_STATUS_NOTDEFINED);
2421 
2422 	return (get_uint32(handle, pdp, linkid, prop_val, val_cnt, media,
2423 	    flags, perm_flags));
2424 }
2425 
2426 /* ARGSUSED */
2427 static dladm_status_t
2428 set_resource(dladm_handle_t handle, prop_desc_t *pdp,
2429     datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt,
2430     uint_t flags, datalink_media_t media)
2431 {
2432 	mac_resource_props_t	mrp;
2433 	dladm_status_t		status = DLADM_STATUS_OK;
2434 	dld_ioc_macprop_t	*dip;
2435 	int			i;
2436 
2437 	bzero(&mrp, sizeof (mac_resource_props_t));
2438 	dip = i_dladm_buf_alloc_by_name(0, linkid, "resource",
2439 	    flags, &status);
2440 
2441 	if (dip == NULL)
2442 		return (status);
2443 
2444 	for (i = 0; i < DLADM_MAX_RSRC_PROP; i++) {
2445 		resource_prop_t	*rp = &rsrc_prop_table[i];
2446 
2447 		if (strcmp(pdp->pd_name, rp->rp_name) != 0)
2448 			continue;
2449 
2450 		status = rp->rp_extract(vdp, val_cnt, &mrp);
2451 		if (status != DLADM_STATUS_OK)
2452 			goto done;
2453 
2454 		break;
2455 	}
2456 
2457 	(void) memcpy(dip->pr_val, &mrp, dip->pr_valsize);
2458 	status = i_dladm_macprop(handle, dip, B_TRUE);
2459 
2460 done:
2461 	free(dip);
2462 	return (status);
2463 }
2464 
2465 /* ARGSUSED */
2466 static dladm_status_t
2467 get_protection(dladm_handle_t handle, prop_desc_t *pdp,
2468     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2469     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2470 {
2471 	mac_resource_props_t	mrp;
2472 	mac_protect_t		*p;
2473 	dladm_status_t		status;
2474 	uint32_t		i, cnt = 0, setbits[32];
2475 
2476 	status = i_dladm_get_public_prop(handle, linkid, "resource", flags,
2477 	    perm_flags, &mrp, sizeof (mrp));
2478 	if (status != DLADM_STATUS_OK)
2479 		return (status);
2480 
2481 	p = &mrp.mrp_protect;
2482 	if ((mrp.mrp_mask & MRP_PROTECT) == 0) {
2483 		*val_cnt = 0;
2484 		return (DLADM_STATUS_OK);
2485 	}
2486 	dladm_find_setbits32(p->mp_types, setbits, &cnt);
2487 	if (cnt > *val_cnt)
2488 		return (DLADM_STATUS_BADVALCNT);
2489 
2490 	for (i = 0; i < cnt; i++)
2491 		(void) dladm_protect2str(setbits[i], prop_val[i]);
2492 
2493 	*val_cnt = cnt;
2494 	return (DLADM_STATUS_OK);
2495 }
2496 
2497 /* ARGSUSED */
2498 static dladm_status_t
2499 get_allowedips(dladm_handle_t handle, prop_desc_t *pdp,
2500     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2501     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2502 {
2503 	mac_resource_props_t	mrp;
2504 	mac_protect_t		*p;
2505 	dladm_status_t		status;
2506 	int			i;
2507 
2508 	status = i_dladm_get_public_prop(handle, linkid, "resource", flags,
2509 	    perm_flags, &mrp, sizeof (mrp));
2510 	if (status != DLADM_STATUS_OK)
2511 		return (status);
2512 
2513 	p = &mrp.mrp_protect;
2514 	if (p->mp_ipaddrcnt == 0) {
2515 		*val_cnt = 0;
2516 		return (DLADM_STATUS_OK);
2517 	}
2518 	if (p->mp_ipaddrcnt > *val_cnt)
2519 		return (DLADM_STATUS_BADVALCNT);
2520 
2521 	for (i = 0; i < p->mp_ipaddrcnt; i++) {
2522 		int len;
2523 		if (p->mp_ipaddrs[i].ip_version == IPV4_VERSION) {
2524 			ipaddr_t	v4addr;
2525 
2526 			v4addr = V4_PART_OF_V6(p->mp_ipaddrs[i].ip_addr);
2527 			(void) dladm_ipv4addr2str(&v4addr, prop_val[i]);
2528 		} else {
2529 			(void) dladm_ipv6addr2str(&p->mp_ipaddrs[i].ip_addr,
2530 			    prop_val[i]);
2531 		}
2532 		len = strlen(prop_val[i]);
2533 		(void) sprintf(prop_val[i] + len, "/%d",
2534 		    p->mp_ipaddrs[i].ip_netmask);
2535 	}
2536 	*val_cnt = p->mp_ipaddrcnt;
2537 	return (DLADM_STATUS_OK);
2538 }
2539 
2540 dladm_status_t
2541 extract_protection(val_desc_t *vdp, uint_t cnt, void *arg)
2542 {
2543 	mac_resource_props_t	*mrp = arg;
2544 	uint32_t		types = 0;
2545 	int			i;
2546 
2547 	for (i = 0; i < cnt; i++)
2548 		types |= (uint32_t)vdp[i].vd_val;
2549 
2550 	mrp->mrp_protect.mp_types = types;
2551 	mrp->mrp_mask |= MRP_PROTECT;
2552 	return (DLADM_STATUS_OK);
2553 }
2554 
2555 dladm_status_t
2556 extract_allowedips(val_desc_t *vdp, uint_t cnt, void *arg)
2557 {
2558 	mac_resource_props_t	*mrp = arg;
2559 	mac_protect_t		*p = &mrp->mrp_protect;
2560 	int			i;
2561 
2562 	if (vdp->vd_val == 0) {
2563 		cnt = (uint_t)-1;
2564 	} else {
2565 		for (i = 0; i < cnt; i++) {
2566 			bcopy((void *)vdp[i].vd_val, &p->mp_ipaddrs[i],
2567 			    sizeof (mac_ipaddr_t));
2568 		}
2569 	}
2570 	p->mp_ipaddrcnt = cnt;
2571 	mrp->mrp_mask |= MRP_PROTECT;
2572 	return (DLADM_STATUS_OK);
2573 }
2574 
2575 static dladm_status_t
2576 check_single_ip(char *buf, mac_ipaddr_t *addr)
2577 {
2578 	dladm_status_t	status;
2579 	ipaddr_t	v4addr;
2580 	in6_addr_t	v6addr;
2581 	boolean_t	isv4 = B_TRUE;
2582 	char		*p;
2583 	uint32_t	mask = 0;
2584 
2585 	/*
2586 	 * If the IP address is in CIDR format, parse the bits component
2587 	 * seperately. An address in this style will be used to indicate an
2588 	 * entire subnet, so it must be a network number with no host address.
2589 	 */
2590 	if ((p = strchr(buf, '/')) != NULL) {
2591 		char *end = NULL;
2592 
2593 		*p++ = '\0';
2594 		if (!isdigit(*p))
2595 			return (DLADM_STATUS_INVALID_IP);
2596 		mask = strtol(p, &end, 10);
2597 		if (end != NULL && *end != '\0')
2598 			return (DLADM_STATUS_INVALID_IP);
2599 		if (mask > 128|| mask < 1)
2600 			return (DLADM_STATUS_INVALID_IP);
2601 	}
2602 
2603 	status = dladm_str2ipv4addr(buf, &v4addr);
2604 	if (status == DLADM_STATUS_INVALID_IP) {
2605 		status = dladm_str2ipv6addr(buf, &v6addr);
2606 		if (status == DLADM_STATUS_OK)
2607 			isv4 = B_FALSE;
2608 	}
2609 	if (status != DLADM_STATUS_OK)
2610 		return (status);
2611 
2612 	if (isv4) {
2613 		if (v4addr == INADDR_ANY)
2614 			return (DLADM_STATUS_INVALID_IP);
2615 
2616 		IN6_IPADDR_TO_V4MAPPED(v4addr, &addr->ip_addr);
2617 		addr->ip_version = IPV4_VERSION;
2618 		if (p != NULL) {
2619 			uint32_t smask;
2620 
2621 			/*
2622 			 * Validate the netmask is in the proper range for v4
2623 			 */
2624 			if (mask > 32 || mask < 1)
2625 				return (DLADM_STATUS_INVALID_IP);
2626 
2627 			/*
2628 			 * We have a CIDR style address, confirm that only the
2629 			 * network number is set.
2630 			 */
2631 			smask = 0xFFFFFFFFu << (32 - mask);
2632 			if (htonl(v4addr) & ~smask)
2633 				return (DLADM_STATUS_INVALID_IP);
2634 		} else {
2635 			mask = 32;
2636 		}
2637 		addr->ip_netmask = mask;
2638 	} else {
2639 		if (IN6_IS_ADDR_UNSPECIFIED(&v6addr))
2640 			return (DLADM_STATUS_INVALID_IP);
2641 
2642 		if (IN6_IS_ADDR_V4MAPPED_ANY(&v6addr))
2643 			return (DLADM_STATUS_INVALID_IP);
2644 
2645 		if (p != NULL) {
2646 			int i, off, high;
2647 
2648 			/*
2649 			 * Note that the address in our buffer is stored in
2650 			 * network byte order.
2651 			 */
2652 			off = 0;
2653 			for (i = 3; i >= 0; i--) {
2654 				high = ffsl(ntohl(v6addr._S6_un._S6_u32[i]));
2655 				if (high != 0)
2656 					break;
2657 				off += 32;
2658 			}
2659 			off += high;
2660 			if (128 - off >= mask)
2661 				return (DLADM_STATUS_INVALID_IP);
2662 		} else {
2663 			mask = 128;
2664 		}
2665 
2666 		addr->ip_addr = v6addr;
2667 		addr->ip_version = IPV6_VERSION;
2668 		addr->ip_netmask = mask;
2669 	}
2670 	return (DLADM_STATUS_OK);
2671 }
2672 
2673 /* ARGSUSED */
2674 static dladm_status_t
2675 check_allowedips(dladm_handle_t handle, prop_desc_t *pdp,
2676     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
2677     val_desc_t **vdpp, datalink_media_t media)
2678 {
2679 	dladm_status_t	status;
2680 	mac_ipaddr_t	*addr;
2681 	int		i;
2682 	uint_t		val_cnt = *val_cntp;
2683 	val_desc_t	*vdp = *vdpp;
2684 
2685 	if (val_cnt > MPT_MAXIPADDR)
2686 		return (DLADM_STATUS_BADVALCNT);
2687 
2688 	for (i = 0; i < val_cnt; i++) {
2689 		if ((addr = calloc(1, sizeof (mac_ipaddr_t))) == NULL) {
2690 			status = DLADM_STATUS_NOMEM;
2691 			goto fail;
2692 		}
2693 		vdp[i].vd_val = (uintptr_t)addr;
2694 
2695 		status = check_single_ip(prop_val[i], addr);
2696 		if (status != DLADM_STATUS_OK)
2697 			goto fail;
2698 	}
2699 	return (DLADM_STATUS_OK);
2700 
2701 fail:
2702 	for (i = 0; i < val_cnt; i++) {
2703 		free((void *)vdp[i].vd_val);
2704 		vdp[i].vd_val = 0;
2705 	}
2706 	return (status);
2707 }
2708 
2709 static void
2710 dladm_cid2str(mac_dhcpcid_t *cid, char *buf)
2711 {
2712 	char	tmp_buf[DLADM_STRSIZE];
2713 	uint_t	hexlen;
2714 
2715 	switch (cid->dc_form) {
2716 	case CIDFORM_TYPED: {
2717 		uint16_t	duidtype, hwtype;
2718 		uint32_t	timestamp, ennum;
2719 		char		*lladdr;
2720 
2721 		if (cid->dc_len < sizeof (duidtype))
2722 			goto fail;
2723 
2724 		bcopy(cid->dc_id, &duidtype, sizeof (duidtype));
2725 		duidtype = ntohs(duidtype);
2726 		switch (duidtype) {
2727 		case DHCPV6_DUID_LLT: {
2728 			duid_llt_t	llt;
2729 
2730 			if (cid->dc_len < sizeof (llt))
2731 				goto fail;
2732 
2733 			bcopy(cid->dc_id, &llt, sizeof (llt));
2734 			hwtype = ntohs(llt.dllt_hwtype);
2735 			timestamp = ntohl(llt.dllt_time);
2736 			lladdr = _link_ntoa(cid->dc_id + sizeof (llt),
2737 			    NULL, cid->dc_len - sizeof (llt), IFT_OTHER);
2738 			if (lladdr == NULL)
2739 				goto fail;
2740 
2741 			(void) snprintf(buf, DLADM_STRSIZE, "%d.%d.%d.%s",
2742 			    duidtype, hwtype, timestamp, lladdr);
2743 			free(lladdr);
2744 			break;
2745 		}
2746 		case DHCPV6_DUID_EN: {
2747 			duid_en_t	en;
2748 
2749 			if (cid->dc_len < sizeof (en))
2750 				goto fail;
2751 
2752 			bcopy(cid->dc_id, &en, sizeof (en));
2753 			ennum = DHCPV6_GET_ENTNUM(&en);
2754 			hexlen = sizeof (tmp_buf);
2755 			if (octet_to_hexascii(cid->dc_id + sizeof (en),
2756 			    cid->dc_len - sizeof (en), tmp_buf, &hexlen) != 0)
2757 				goto fail;
2758 
2759 			(void) snprintf(buf, DLADM_STRSIZE, "%d.%d.%s",
2760 			    duidtype, ennum, tmp_buf);
2761 			break;
2762 		}
2763 		case DHCPV6_DUID_LL: {
2764 			duid_ll_t	ll;
2765 
2766 			if (cid->dc_len < sizeof (ll))
2767 				goto fail;
2768 
2769 			bcopy(cid->dc_id, &ll, sizeof (ll));
2770 			hwtype = ntohs(ll.dll_hwtype);
2771 			lladdr = _link_ntoa(cid->dc_id + sizeof (ll),
2772 			    NULL, cid->dc_len - sizeof (ll), IFT_OTHER);
2773 			if (lladdr == NULL)
2774 				goto fail;
2775 
2776 			(void) snprintf(buf, DLADM_STRSIZE, "%d.%d.%s",
2777 			    duidtype, hwtype, lladdr);
2778 			free(lladdr);
2779 			break;
2780 		}
2781 		default: {
2782 			hexlen = sizeof (tmp_buf);
2783 			if (octet_to_hexascii(cid->dc_id + sizeof (duidtype),
2784 			    cid->dc_len - sizeof (duidtype),
2785 			    tmp_buf, &hexlen) != 0)
2786 				goto fail;
2787 
2788 			(void) snprintf(buf, DLADM_STRSIZE, "%d.%s",
2789 			    duidtype, tmp_buf);
2790 		}
2791 		}
2792 		break;
2793 	}
2794 	case CIDFORM_HEX: {
2795 		hexlen = sizeof (tmp_buf);
2796 		if (octet_to_hexascii(cid->dc_id, cid->dc_len,
2797 		    tmp_buf, &hexlen) != 0)
2798 			goto fail;
2799 
2800 		(void) snprintf(buf, DLADM_STRSIZE, "0x%s", tmp_buf);
2801 		break;
2802 	}
2803 	case CIDFORM_STR: {
2804 		int	i;
2805 
2806 		for (i = 0; i < cid->dc_len; i++) {
2807 			if (!isprint(cid->dc_id[i]))
2808 				goto fail;
2809 		}
2810 		(void) snprintf(buf, DLADM_STRSIZE, "%s", cid->dc_id);
2811 		break;
2812 	}
2813 	default:
2814 		goto fail;
2815 	}
2816 	return;
2817 
2818 fail:
2819 	(void) snprintf(buf, DLADM_STRSIZE, "<unknown>");
2820 }
2821 
2822 static dladm_status_t
2823 dladm_str2cid(char *buf, mac_dhcpcid_t *cid)
2824 {
2825 	char	*ptr = buf;
2826 	char	tmp_buf[DLADM_STRSIZE];
2827 	uint_t	hexlen, cidlen;
2828 
2829 	bzero(cid, sizeof (*cid));
2830 	if (isdigit(*ptr) &&
2831 	    ptr[strspn(ptr, "0123456789")] == '.') {
2832 		char	*cp;
2833 		ulong_t	duidtype;
2834 		ulong_t	subtype;
2835 		ulong_t	timestamp;
2836 		uchar_t	*lladdr;
2837 		int	addrlen;
2838 
2839 		errno = 0;
2840 		duidtype = strtoul(ptr, &cp, 0);
2841 		if (ptr == cp || errno != 0 || *cp != '.' ||
2842 		    duidtype > USHRT_MAX)
2843 			return (DLADM_STATUS_BADARG);
2844 		ptr = cp + 1;
2845 
2846 		if (duidtype != 0 && duidtype <= DHCPV6_DUID_LL) {
2847 			errno = 0;
2848 			subtype = strtoul(ptr, &cp, 0);
2849 			if (ptr == cp || errno != 0 || *cp != '.')
2850 				return (DLADM_STATUS_BADARG);
2851 			ptr = cp + 1;
2852 		}
2853 		switch (duidtype) {
2854 		case DHCPV6_DUID_LLT: {
2855 			duid_llt_t	llt;
2856 
2857 			errno = 0;
2858 			timestamp = strtoul(ptr, &cp, 0);
2859 			if (ptr == cp || errno != 0 || *cp != '.')
2860 				return (DLADM_STATUS_BADARG);
2861 
2862 			ptr = cp + 1;
2863 			lladdr = _link_aton(ptr, &addrlen);
2864 			if (lladdr == NULL)
2865 				return (DLADM_STATUS_BADARG);
2866 
2867 			cidlen = sizeof (llt) + addrlen;
2868 			if (cidlen > sizeof (cid->dc_id)) {
2869 				free(lladdr);
2870 				return (DLADM_STATUS_TOOSMALL);
2871 			}
2872 			llt.dllt_dutype = htons(duidtype);
2873 			llt.dllt_hwtype = htons(subtype);
2874 			llt.dllt_time = htonl(timestamp);
2875 			bcopy(&llt, cid->dc_id, sizeof (llt));
2876 			bcopy(lladdr, cid->dc_id + sizeof (llt), addrlen);
2877 			free(lladdr);
2878 			break;
2879 		}
2880 		case DHCPV6_DUID_LL: {
2881 			duid_ll_t	ll;
2882 
2883 			lladdr = _link_aton(ptr, &addrlen);
2884 			if (lladdr == NULL)
2885 				return (DLADM_STATUS_BADARG);
2886 
2887 			cidlen = sizeof (ll) + addrlen;
2888 			if (cidlen > sizeof (cid->dc_id)) {
2889 				free(lladdr);
2890 				return (DLADM_STATUS_TOOSMALL);
2891 			}
2892 			ll.dll_dutype = htons(duidtype);
2893 			ll.dll_hwtype = htons(subtype);
2894 			bcopy(&ll, cid->dc_id, sizeof (ll));
2895 			bcopy(lladdr, cid->dc_id + sizeof (ll), addrlen);
2896 			free(lladdr);
2897 			break;
2898 		}
2899 		default: {
2900 			hexlen = sizeof (tmp_buf);
2901 			if (hexascii_to_octet(ptr, strlen(ptr),
2902 			    tmp_buf, &hexlen) != 0)
2903 				return (DLADM_STATUS_BADARG);
2904 
2905 			if (duidtype == DHCPV6_DUID_EN) {
2906 				duid_en_t	en;
2907 
2908 				en.den_dutype = htons(duidtype);
2909 				DHCPV6_SET_ENTNUM(&en, subtype);
2910 
2911 				cidlen = sizeof (en) + hexlen;
2912 				if (cidlen > sizeof (cid->dc_id))
2913 					return (DLADM_STATUS_TOOSMALL);
2914 
2915 				bcopy(&en, cid->dc_id, sizeof (en));
2916 				bcopy(tmp_buf, cid->dc_id + sizeof (en),
2917 				    hexlen);
2918 			} else {
2919 				uint16_t	dutype = htons(duidtype);
2920 
2921 				cidlen = sizeof (dutype) + hexlen;
2922 				if (cidlen > sizeof (cid->dc_id))
2923 					return (DLADM_STATUS_TOOSMALL);
2924 
2925 				bcopy(&dutype, cid->dc_id, sizeof (dutype));
2926 				bcopy(tmp_buf, cid->dc_id + sizeof (dutype),
2927 				    hexlen);
2928 			}
2929 			break;
2930 		}
2931 		}
2932 		cid->dc_form = CIDFORM_TYPED;
2933 	} else if (strncasecmp("0x", ptr, 2) == 0 && ptr[2] != '\0') {
2934 		ptr += 2;
2935 		hexlen = sizeof (tmp_buf);
2936 		if (hexascii_to_octet(ptr, strlen(ptr), tmp_buf,
2937 		    &hexlen) != 0) {
2938 			return (DLADM_STATUS_BADARG);
2939 		}
2940 		cidlen = hexlen;
2941 		if (cidlen > sizeof (cid->dc_id))
2942 			return (DLADM_STATUS_TOOSMALL);
2943 
2944 		bcopy(tmp_buf, cid->dc_id, cidlen);
2945 		cid->dc_form = CIDFORM_HEX;
2946 	} else {
2947 		cidlen = strlen(ptr);
2948 		if (cidlen > sizeof (cid->dc_id))
2949 			return (DLADM_STATUS_TOOSMALL);
2950 
2951 		bcopy(ptr, cid->dc_id, cidlen);
2952 		cid->dc_form = CIDFORM_STR;
2953 	}
2954 	cid->dc_len = cidlen;
2955 	return (DLADM_STATUS_OK);
2956 }
2957 
2958 /* ARGSUSED */
2959 static dladm_status_t
2960 get_allowedcids(dladm_handle_t handle, prop_desc_t *pdp,
2961     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2962     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2963 {
2964 	mac_resource_props_t	mrp;
2965 	mac_protect_t		*p;
2966 	dladm_status_t		status;
2967 	int			i;
2968 
2969 	status = i_dladm_get_public_prop(handle, linkid, "resource", flags,
2970 	    perm_flags, &mrp, sizeof (mrp));
2971 	if (status != DLADM_STATUS_OK)
2972 		return (status);
2973 
2974 	p = &mrp.mrp_protect;
2975 	if (p->mp_cidcnt == 0) {
2976 		*val_cnt = 0;
2977 		return (DLADM_STATUS_OK);
2978 	}
2979 	if (p->mp_cidcnt > *val_cnt)
2980 		return (DLADM_STATUS_BADVALCNT);
2981 
2982 	for (i = 0; i < p->mp_cidcnt; i++) {
2983 		mac_dhcpcid_t	*cid = &p->mp_cids[i];
2984 
2985 		dladm_cid2str(cid, prop_val[i]);
2986 	}
2987 	*val_cnt = p->mp_cidcnt;
2988 	return (DLADM_STATUS_OK);
2989 }
2990 
2991 dladm_status_t
2992 extract_allowedcids(val_desc_t *vdp, uint_t cnt, void *arg)
2993 {
2994 	mac_resource_props_t	*mrp = arg;
2995 	mac_protect_t		*p = &mrp->mrp_protect;
2996 	int			i;
2997 
2998 	if (vdp->vd_val == 0) {
2999 		cnt = (uint_t)-1;
3000 	} else {
3001 		for (i = 0; i < cnt; i++) {
3002 			bcopy((void *)vdp[i].vd_val, &p->mp_cids[i],
3003 			    sizeof (mac_dhcpcid_t));
3004 		}
3005 	}
3006 	p->mp_cidcnt = cnt;
3007 	mrp->mrp_mask |= MRP_PROTECT;
3008 	return (DLADM_STATUS_OK);
3009 }
3010 
3011 /* ARGSUSED */
3012 static dladm_status_t
3013 check_allowedcids(dladm_handle_t handle, prop_desc_t *pdp,
3014     datalink_id_t linkid, char **prop_val, uint_t *val_cntp,
3015     uint_t flags, val_desc_t **vdpp, datalink_media_t media)
3016 {
3017 	dladm_status_t	status;
3018 	mac_dhcpcid_t	*cid;
3019 	int		i;
3020 	uint_t		val_cnt = *val_cntp;
3021 	val_desc_t	*vdp = *vdpp;
3022 
3023 	if (val_cnt > MPT_MAXCID)
3024 		return (DLADM_STATUS_BADVALCNT);
3025 
3026 	for (i = 0; i < val_cnt; i++) {
3027 		if ((cid = calloc(1, sizeof (mac_dhcpcid_t))) == NULL) {
3028 			status = DLADM_STATUS_NOMEM;
3029 			goto fail;
3030 		}
3031 		vdp[i].vd_val = (uintptr_t)cid;
3032 
3033 		status = dladm_str2cid(prop_val[i], cid);
3034 		if (status != DLADM_STATUS_OK)
3035 			goto fail;
3036 	}
3037 	return (DLADM_STATUS_OK);
3038 
3039 fail:
3040 	for (i = 0; i < val_cnt; i++) {
3041 		free((void *)vdp[i].vd_val);
3042 		vdp[i].vd_val = 0;
3043 	}
3044 	return (status);
3045 }
3046 
3047 /* ARGSUSED */
3048 static dladm_status_t
3049 get_secondary_macs(dladm_handle_t handle, prop_desc_t *pdp,
3050     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3051     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3052 {
3053 	mac_secondary_addr_t	sa;
3054 	dladm_status_t		status;
3055 	int			i;
3056 
3057 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
3058 	    perm_flags, &sa, sizeof (sa));
3059 	if (status != DLADM_STATUS_OK)
3060 		return (status);
3061 
3062 	if (sa.ms_addrcnt > *val_cnt)
3063 		return (DLADM_STATUS_BADVALCNT);
3064 
3065 	for (i = 0; i < sa.ms_addrcnt; i++) {
3066 		if (dladm_aggr_macaddr2str(
3067 		    (const unsigned char *)&sa.ms_addrs[i], prop_val[i]) ==
3068 		    NULL) {
3069 			*val_cnt = i;
3070 			return (DLADM_STATUS_NOMEM);
3071 		}
3072 	}
3073 	*val_cnt = sa.ms_addrcnt;
3074 	return (DLADM_STATUS_OK);
3075 }
3076 
3077 /* ARGSUSED */
3078 static dladm_status_t
3079 check_secondary_macs(dladm_handle_t handle, prop_desc_t *pdp,
3080     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
3081     val_desc_t **vdpp, datalink_media_t media)
3082 {
3083 	dladm_status_t	status;
3084 	uchar_t		*addr;
3085 	uint_t		len = 0;
3086 	int		i;
3087 	uint_t		val_cnt = *val_cntp;
3088 	val_desc_t	*vdp = *vdpp;
3089 
3090 	if (val_cnt >= MPT_MAXMACADDR)
3091 		return (DLADM_STATUS_BADVALCNT);
3092 
3093 	for (i = 0; i < val_cnt; i++) {
3094 		addr = _link_aton(prop_val[i], (int *)&len);
3095 		if (addr == NULL) {
3096 			if (len == (uint_t)-1)
3097 				status = DLADM_STATUS_MACADDRINVAL;
3098 			else
3099 				status = DLADM_STATUS_NOMEM;
3100 			goto fail;
3101 		}
3102 
3103 		vdp[i].vd_val = (uintptr_t)addr;
3104 	}
3105 	return (DLADM_STATUS_OK);
3106 
3107 fail:
3108 	for (i = 0; i < val_cnt; i++) {
3109 		free((void *)vdp[i].vd_val);
3110 		vdp[i].vd_val = 0;
3111 	}
3112 	return (status);
3113 }
3114 
3115 /* ARGSUSED */
3116 static dladm_status_t
3117 set_secondary_macs(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid,
3118     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
3119 {
3120 	dladm_status_t status;
3121 	dld_ioc_macprop_t *dip;
3122 	int i;
3123 	mac_secondary_addr_t msa;
3124 
3125 	dip = i_dladm_buf_alloc_by_name(0, linkid, "secondary-macs", 0,
3126 	    &status);
3127 	if (dip == NULL)
3128 		return (status);
3129 
3130 	if (vdp->vd_val == 0) {
3131 		val_cnt = (uint_t)-1;
3132 	} else {
3133 		for (i = 0; i < val_cnt; i++) {
3134 			bcopy((void *)vdp[i].vd_val, msa.ms_addrs[i],
3135 			    MAXMACADDRLEN);
3136 		}
3137 	}
3138 	msa.ms_addrcnt = val_cnt;
3139 	bcopy(&msa, dip->pr_val, dip->pr_valsize);
3140 
3141 	status = i_dladm_macprop(handle, dip, B_TRUE);
3142 
3143 	free(dip);
3144 	return (status);
3145 }
3146 
3147 /* ARGSUSED */
3148 static dladm_status_t
3149 get_autopush(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3150     char **prop_val, uint_t *val_cnt, datalink_media_t media,
3151     uint_t flags, uint_t *perm_flags)
3152 {
3153 	struct		dlautopush dlap;
3154 	int		i, len;
3155 	dladm_status_t	status;
3156 
3157 	if (flags & DLD_PROP_DEFAULT)
3158 		return (DLADM_STATUS_NOTDEFINED);
3159 
3160 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
3161 	    perm_flags, &dlap, sizeof (dlap));
3162 	if (status != DLADM_STATUS_OK)
3163 		return (status);
3164 
3165 	if (dlap.dap_npush == 0) {
3166 		*val_cnt = 0;
3167 		return (DLADM_STATUS_OK);
3168 	}
3169 	for (i = 0, len = 0; i < dlap.dap_npush; i++) {
3170 		if (i != 0) {
3171 			(void) snprintf(*prop_val + len,
3172 			    DLADM_PROP_VAL_MAX - len, "%c", AP_DELIMITER);
3173 			len += 1;
3174 		}
3175 		(void) snprintf(*prop_val + len, DLADM_PROP_VAL_MAX - len,
3176 		    "%s", dlap.dap_aplist[i]);
3177 		len += strlen(dlap.dap_aplist[i]);
3178 		if (dlap.dap_anchor - 1 == i) {
3179 			(void) snprintf(*prop_val + len,
3180 			    DLADM_PROP_VAL_MAX - len, "%c%s", AP_DELIMITER,
3181 			    AP_ANCHOR);
3182 			len += (strlen(AP_ANCHOR) + 1);
3183 		}
3184 	}
3185 	*val_cnt = 1;
3186 	return (DLADM_STATUS_OK);
3187 }
3188 
3189 /*
3190  * Add the specified module to the dlautopush structure; returns a
3191  * DLADM_STATUS_* code.
3192  */
3193 dladm_status_t
3194 i_dladm_add_ap_module(const char *module, struct dlautopush *dlap)
3195 {
3196 	if ((strlen(module) == 0) || (strlen(module) > FMNAMESZ))
3197 		return (DLADM_STATUS_BADVAL);
3198 
3199 	if (strncasecmp(module, AP_ANCHOR, strlen(AP_ANCHOR)) == 0) {
3200 		/*
3201 		 * We don't allow multiple anchors, and the anchor must
3202 		 * be after at least one module.
3203 		 */
3204 		if (dlap->dap_anchor != 0)
3205 			return (DLADM_STATUS_BADVAL);
3206 		if (dlap->dap_npush == 0)
3207 			return (DLADM_STATUS_BADVAL);
3208 
3209 		dlap->dap_anchor = dlap->dap_npush;
3210 		return (DLADM_STATUS_OK);
3211 	}
3212 	if (dlap->dap_npush >= MAXAPUSH)
3213 		return (DLADM_STATUS_BADVALCNT);
3214 
3215 	(void) strlcpy(dlap->dap_aplist[dlap->dap_npush++], module,
3216 	    FMNAMESZ + 1);
3217 
3218 	return (DLADM_STATUS_OK);
3219 }
3220 
3221 /*
3222  * Currently, both '.' and ' '(space) can be used as the delimiters between
3223  * autopush modules. The former is used in dladm set-linkprop, and the
3224  * latter is used in the autopush(1M) file.
3225  */
3226 /* ARGSUSED */
3227 static dladm_status_t
3228 check_autopush(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3229     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
3230     datalink_media_t media)
3231 {
3232 	char			*module;
3233 	struct dlautopush	*dlap;
3234 	dladm_status_t		status;
3235 	char			val[DLADM_PROP_VAL_MAX];
3236 	char			delimiters[4];
3237 	uint_t			val_cnt = *val_cntp;
3238 	val_desc_t		*vdp = *vdpp;
3239 
3240 	if (val_cnt != 1)
3241 		return (DLADM_STATUS_BADVALCNT);
3242 
3243 	if (prop_val != NULL) {
3244 		dlap = malloc(sizeof (struct dlautopush));
3245 		if (dlap == NULL)
3246 			return (DLADM_STATUS_NOMEM);
3247 
3248 		(void) memset(dlap, 0, sizeof (struct dlautopush));
3249 		(void) snprintf(delimiters, 4, " %c\n", AP_DELIMITER);
3250 		bcopy(*prop_val, val, DLADM_PROP_VAL_MAX);
3251 		module = strtok(val, delimiters);
3252 		while (module != NULL) {
3253 			status = i_dladm_add_ap_module(module, dlap);
3254 			if (status != DLADM_STATUS_OK)
3255 				return (status);
3256 			module = strtok(NULL, delimiters);
3257 		}
3258 
3259 		vdp->vd_val = (uintptr_t)dlap;
3260 	} else {
3261 		vdp->vd_val = 0;
3262 	}
3263 	return (DLADM_STATUS_OK);
3264 }
3265 
3266 #define	WLDP_BUFSIZE (MAX_BUF_LEN - WIFI_BUF_OFFSET)
3267 
3268 /* ARGSUSED */
3269 static dladm_status_t
3270 get_rate_common(dladm_handle_t handle, prop_desc_t *pdp,
3271     datalink_id_t linkid, char **prop_val, uint_t *val_cnt, uint_t id,
3272     uint_t *perm_flags)
3273 {
3274 	wl_rates_t	*wrp;
3275 	uint_t		i;
3276 	dladm_status_t	status = DLADM_STATUS_OK;
3277 
3278 	wrp = malloc(WLDP_BUFSIZE);
3279 	if (wrp == NULL)
3280 		return (DLADM_STATUS_NOMEM);
3281 
3282 	status = i_dladm_wlan_param(handle, linkid, wrp, id, WLDP_BUFSIZE,
3283 	    B_FALSE);
3284 	if (status != DLADM_STATUS_OK)
3285 		goto done;
3286 
3287 	if (wrp->wl_rates_num > *val_cnt) {
3288 		status = DLADM_STATUS_TOOSMALL;
3289 		goto done;
3290 	}
3291 
3292 	if (wrp->wl_rates_rates[0] == 0) {
3293 		prop_val[0][0] = '\0';
3294 		*val_cnt = 1;
3295 		goto done;
3296 	}
3297 
3298 	for (i = 0; i < wrp->wl_rates_num; i++) {
3299 		(void) snprintf(prop_val[i], DLADM_STRSIZE, "%.*f",
3300 		    wrp->wl_rates_rates[i] % 2,
3301 		    (float)wrp->wl_rates_rates[i] / 2);
3302 	}
3303 	*val_cnt = wrp->wl_rates_num;
3304 	*perm_flags = MAC_PROP_PERM_RW;
3305 
3306 done:
3307 	free(wrp);
3308 	return (status);
3309 }
3310 
3311 static dladm_status_t
3312 get_rate(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3313     char **prop_val, uint_t *val_cnt, datalink_media_t media,
3314     uint_t flags, uint_t *perm_flags)
3315 {
3316 	if (media != DL_WIFI) {
3317 		return (get_speed(handle, pdp, linkid, prop_val,
3318 		    val_cnt, media, flags, perm_flags));
3319 	}
3320 
3321 	return (get_rate_common(handle, pdp, linkid, prop_val, val_cnt,
3322 	    MAC_PROP_WL_DESIRED_RATES, perm_flags));
3323 }
3324 
3325 /* ARGSUSED */
3326 static dladm_status_t
3327 get_rate_mod(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3328     char **prop_val, uint_t *val_cnt, datalink_media_t media,
3329     uint_t flags, uint_t *perm_flags)
3330 {
3331 	switch (media) {
3332 	case DL_ETHER:
3333 		/*
3334 		 * Speed for ethernet links is unbounded. E.g., 802.11b
3335 		 * links can have a speed of 5.5 Gbps.
3336 		 */
3337 		return (DLADM_STATUS_NOTSUP);
3338 
3339 	case DL_WIFI:
3340 		return (get_rate_common(handle, pdp, linkid, prop_val,
3341 		    val_cnt, MAC_PROP_WL_SUPPORTED_RATES, perm_flags));
3342 	default:
3343 		return (DLADM_STATUS_BADARG);
3344 	}
3345 }
3346 
3347 static dladm_status_t
3348 set_wlan_rate(dladm_handle_t handle, datalink_id_t linkid,
3349     dladm_wlan_rates_t *rates)
3350 {
3351 	int		i;
3352 	uint_t		len;
3353 	wl_rates_t	*wrp;
3354 	dladm_status_t	status = DLADM_STATUS_OK;
3355 
3356 	wrp = malloc(WLDP_BUFSIZE);
3357 	if (wrp == NULL)
3358 		return (DLADM_STATUS_NOMEM);
3359 
3360 	bzero(wrp, WLDP_BUFSIZE);
3361 	for (i = 0; i < rates->wr_cnt; i++)
3362 		wrp->wl_rates_rates[i] = rates->wr_rates[i];
3363 	wrp->wl_rates_num = rates->wr_cnt;
3364 
3365 	len = offsetof(wl_rates_t, wl_rates_rates) +
3366 	    (rates->wr_cnt * sizeof (char)) + WIFI_BUF_OFFSET;
3367 	status = i_dladm_wlan_param(handle, linkid, wrp,
3368 	    MAC_PROP_WL_DESIRED_RATES, len, B_TRUE);
3369 
3370 	free(wrp);
3371 	return (status);
3372 }
3373 
3374 /* ARGSUSED */
3375 static dladm_status_t
3376 set_rate(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3377     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
3378 {
3379 	dladm_wlan_rates_t	rates;
3380 	dladm_status_t		status;
3381 
3382 	/*
3383 	 * can currently set rate on WIFI links only.
3384 	 */
3385 	if (media != DL_WIFI)
3386 		return (DLADM_STATUS_PROPRDONLY);
3387 
3388 	if (val_cnt != 1)
3389 		return (DLADM_STATUS_BADVALCNT);
3390 
3391 	rates.wr_cnt = 1;
3392 	rates.wr_rates[0] = vdp[0].vd_val;
3393 
3394 	status = set_wlan_rate(handle, linkid, &rates);
3395 
3396 	return (status);
3397 }
3398 
3399 /* ARGSUSED */
3400 static dladm_status_t
3401 check_rate(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3402     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
3403     datalink_media_t media)
3404 {
3405 	int		i;
3406 	uint_t		modval_cnt = MAX_SUPPORT_RATES;
3407 	char		*buf, **modval;
3408 	dladm_status_t	status;
3409 	uint_t		perm_flags;
3410 	uint_t		val_cnt = *val_cntp;
3411 	val_desc_t	*vdp = *vdpp;
3412 
3413 	if (val_cnt != 1)
3414 		return (DLADM_STATUS_BADVALCNT);
3415 
3416 	buf = malloc((sizeof (char *) + DLADM_STRSIZE) *
3417 	    MAX_SUPPORT_RATES);
3418 	if (buf == NULL) {
3419 		status = DLADM_STATUS_NOMEM;
3420 		goto done;
3421 	}
3422 
3423 	modval = (char **)(void *)buf;
3424 	for (i = 0; i < MAX_SUPPORT_RATES; i++) {
3425 		modval[i] = buf + sizeof (char *) * MAX_SUPPORT_RATES +
3426 		    i * DLADM_STRSIZE;
3427 	}
3428 
3429 	status = get_rate_mod(handle, NULL, linkid, modval, &modval_cnt,
3430 	    media, 0, &perm_flags);
3431 	if (status != DLADM_STATUS_OK)
3432 		goto done;
3433 
3434 	for (i = 0; i < modval_cnt; i++) {
3435 		if (strcasecmp(*prop_val, modval[i]) == 0) {
3436 			vdp->vd_val = (uintptr_t)(uint_t)
3437 			    (atof(*prop_val) * 2);
3438 			status = DLADM_STATUS_OK;
3439 			break;
3440 		}
3441 	}
3442 	if (i == modval_cnt)
3443 		status = DLADM_STATUS_BADVAL;
3444 done:
3445 	free(buf);
3446 	return (status);
3447 }
3448 
3449 static dladm_status_t
3450 get_phyconf(dladm_handle_t handle, datalink_id_t linkid, void *buf,
3451     int buflen)
3452 {
3453 	return (i_dladm_wlan_param(handle, linkid, buf, MAC_PROP_WL_PHY_CONFIG,
3454 	    buflen, B_FALSE));
3455 }
3456 
3457 /* ARGSUSED */
3458 static dladm_status_t
3459 get_channel(dladm_handle_t handle, prop_desc_t *pdp,
3460     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3461     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3462 {
3463 	uint32_t	channel;
3464 	char		buf[WLDP_BUFSIZE];
3465 	dladm_status_t	status;
3466 	wl_phy_conf_t	wl_phy_conf;
3467 
3468 	if ((status = get_phyconf(handle, linkid, buf, sizeof (buf)))
3469 	    != DLADM_STATUS_OK)
3470 		return (status);
3471 
3472 	(void) memcpy(&wl_phy_conf, buf, sizeof (wl_phy_conf));
3473 	if (!i_dladm_wlan_convert_chan(&wl_phy_conf, &channel))
3474 		return (DLADM_STATUS_NOTFOUND);
3475 
3476 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%u", channel);
3477 	*val_cnt = 1;
3478 	*perm_flags = MAC_PROP_PERM_READ;
3479 	return (DLADM_STATUS_OK);
3480 }
3481 
3482 /* ARGSUSED */
3483 static dladm_status_t
3484 get_powermode(dladm_handle_t handle, prop_desc_t *pdp,
3485     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3486     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3487 {
3488 	wl_ps_mode_t	mode;
3489 	const char	*s;
3490 	char		buf[WLDP_BUFSIZE];
3491 	dladm_status_t	status;
3492 
3493 	if ((status = i_dladm_wlan_param(handle, linkid, buf,
3494 	    MAC_PROP_WL_POWER_MODE, sizeof (buf), B_FALSE)) != DLADM_STATUS_OK)
3495 		return (status);
3496 
3497 	(void) memcpy(&mode, buf, sizeof (mode));
3498 	switch (mode.wl_ps_mode) {
3499 	case WL_PM_AM:
3500 		s = "off";
3501 		break;
3502 	case WL_PM_MPS:
3503 		s = "max";
3504 		break;
3505 	case WL_PM_FAST:
3506 		s = "fast";
3507 		break;
3508 	default:
3509 		return (DLADM_STATUS_NOTFOUND);
3510 	}
3511 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
3512 	*val_cnt = 1;
3513 	*perm_flags = MAC_PROP_PERM_RW;
3514 	return (DLADM_STATUS_OK);
3515 }
3516 
3517 /* ARGSUSED */
3518 static dladm_status_t
3519 set_powermode(dladm_handle_t handle, prop_desc_t *pdp,
3520     datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt, uint_t flags,
3521     datalink_media_t media)
3522 {
3523 	dladm_wlan_powermode_t	powermode = vdp->vd_val;
3524 	wl_ps_mode_t		ps_mode;
3525 
3526 	if (val_cnt != 1)
3527 		return (DLADM_STATUS_BADVALCNT);
3528 
3529 	(void) memset(&ps_mode, 0xff, sizeof (ps_mode));
3530 
3531 	switch (powermode) {
3532 	case DLADM_WLAN_PM_OFF:
3533 		ps_mode.wl_ps_mode = WL_PM_AM;
3534 		break;
3535 	case DLADM_WLAN_PM_MAX:
3536 		ps_mode.wl_ps_mode = WL_PM_MPS;
3537 		break;
3538 	case DLADM_WLAN_PM_FAST:
3539 		ps_mode.wl_ps_mode = WL_PM_FAST;
3540 		break;
3541 	default:
3542 		return (DLADM_STATUS_NOTSUP);
3543 	}
3544 	return (i_dladm_wlan_param(handle, linkid, &ps_mode,
3545 	    MAC_PROP_WL_POWER_MODE, sizeof (ps_mode), B_TRUE));
3546 }
3547 
3548 /* ARGSUSED */
3549 static dladm_status_t
3550 get_radio(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3551     char **prop_val, uint_t *val_cnt, datalink_media_t media,
3552     uint_t flags, uint_t *perm_flags)
3553 {
3554 	wl_radio_t	radio;
3555 	const char	*s;
3556 	char		buf[WLDP_BUFSIZE];
3557 	dladm_status_t	status;
3558 
3559 	if ((status = i_dladm_wlan_param(handle, linkid, buf,
3560 	    MAC_PROP_WL_RADIO, sizeof (buf), B_FALSE)) != DLADM_STATUS_OK)
3561 		return (status);
3562 
3563 	(void) memcpy(&radio, buf, sizeof (radio));
3564 	switch (radio) {
3565 	case B_TRUE:
3566 		s = "on";
3567 		break;
3568 	case B_FALSE:
3569 		s = "off";
3570 		break;
3571 	default:
3572 		return (DLADM_STATUS_NOTFOUND);
3573 	}
3574 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
3575 	*val_cnt = 1;
3576 	*perm_flags = MAC_PROP_PERM_RW;
3577 	return (DLADM_STATUS_OK);
3578 }
3579 
3580 /* ARGSUSED */
3581 static dladm_status_t
3582 set_radio(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3583     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
3584 {
3585 	dladm_wlan_radio_t	radio = vdp->vd_val;
3586 	wl_radio_t		r;
3587 
3588 	if (val_cnt != 1)
3589 		return (DLADM_STATUS_BADVALCNT);
3590 
3591 	switch (radio) {
3592 	case DLADM_WLAN_RADIO_ON:
3593 		r = B_TRUE;
3594 		break;
3595 	case DLADM_WLAN_RADIO_OFF:
3596 		r = B_FALSE;
3597 		break;
3598 	default:
3599 		return (DLADM_STATUS_NOTSUP);
3600 	}
3601 	return (i_dladm_wlan_param(handle, linkid, &r, MAC_PROP_WL_RADIO,
3602 	    sizeof (r), B_TRUE));
3603 }
3604 
3605 /* ARGSUSED */
3606 static dladm_status_t
3607 check_hoplimit(dladm_handle_t handle, prop_desc_t *pdp,
3608     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
3609     val_desc_t **vdpp, datalink_media_t media)
3610 {
3611 	int32_t		hlim;
3612 	char		*ep;
3613 	uint_t		val_cnt = *val_cntp;
3614 	val_desc_t	*vdp = *vdpp;
3615 
3616 	if (val_cnt != 1)
3617 		return (DLADM_STATUS_BADVALCNT);
3618 
3619 	errno = 0;
3620 	hlim = strtol(*prop_val, &ep, 10);
3621 	if (errno != 0 || ep == *prop_val || hlim < 1 ||
3622 	    hlim > (int32_t)UINT8_MAX)
3623 		return (DLADM_STATUS_BADVAL);
3624 	vdp->vd_val = hlim;
3625 	return (DLADM_STATUS_OK);
3626 }
3627 
3628 /* ARGSUSED */
3629 static dladm_status_t
3630 check_encaplim(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
3631     char **prop_val, uint_t *val_cntp, uint_t flags, val_desc_t **vdpp,
3632     datalink_media_t media)
3633 {
3634 	int32_t		elim;
3635 	char		*ep;
3636 	uint_t		val_cnt = *val_cntp;
3637 	val_desc_t	*vdp = *vdpp;
3638 
3639 	if (media != DL_IPV6)
3640 		return (DLADM_STATUS_BADARG);
3641 
3642 	if (val_cnt != 1)
3643 		return (DLADM_STATUS_BADVALCNT);
3644 
3645 	errno = 0;
3646 	elim = strtol(*prop_val, &ep, 10);
3647 	if (errno != 0 || ep == *prop_val || elim < 0 ||
3648 	    elim > (int32_t)UINT8_MAX)
3649 		return (DLADM_STATUS_BADVAL);
3650 	vdp->vd_val = elim;
3651 	return (DLADM_STATUS_OK);
3652 }
3653 
3654 static dladm_status_t
3655 i_dladm_set_linkprop_db(dladm_handle_t handle, datalink_id_t linkid,
3656     const char *prop_name, char **prop_val, uint_t val_cnt)
3657 {
3658 	char		buf[MAXLINELEN];
3659 	int		i;
3660 	dladm_conf_t	conf;
3661 	dladm_status_t	status;
3662 
3663 	status = dladm_open_conf(handle, linkid, &conf);
3664 	if (status != DLADM_STATUS_OK)
3665 		return (status);
3666 
3667 	/*
3668 	 * reset case.
3669 	 */
3670 	if (val_cnt == 0) {
3671 		status = dladm_unset_conf_field(handle, conf, prop_name);
3672 		if (status == DLADM_STATUS_OK)
3673 			status = dladm_write_conf(handle, conf);
3674 		goto done;
3675 	}
3676 
3677 	buf[0] = '\0';
3678 	for (i = 0; i < val_cnt; i++) {
3679 		(void) strlcat(buf, prop_val[i], MAXLINELEN);
3680 		if (i != val_cnt - 1)
3681 			(void) strlcat(buf, ",", MAXLINELEN);
3682 	}
3683 
3684 	status = dladm_set_conf_field(handle, conf, prop_name, DLADM_TYPE_STR,
3685 	    buf);
3686 	if (status == DLADM_STATUS_OK)
3687 		status = dladm_write_conf(handle, conf);
3688 
3689 done:
3690 	dladm_destroy_conf(handle, conf);
3691 	return (status);
3692 }
3693 
3694 static dladm_status_t
3695 i_dladm_get_linkprop_db(dladm_handle_t handle, datalink_id_t linkid,
3696     const char *prop_name, char **prop_val, uint_t *val_cntp)
3697 {
3698 	char		buf[MAXLINELEN], *str;
3699 	uint_t		cnt = 0;
3700 	dladm_conf_t	conf;
3701 	dladm_status_t	status;
3702 
3703 	status = dladm_getsnap_conf(handle, linkid, &conf);
3704 	if (status != DLADM_STATUS_OK)
3705 		return (status);
3706 
3707 	status = dladm_get_conf_field(handle, conf, prop_name, buf, MAXLINELEN);
3708 	if (status != DLADM_STATUS_OK)
3709 		goto done;
3710 
3711 	str = strtok(buf, ",");
3712 	while (str != NULL) {
3713 		if (cnt == *val_cntp) {
3714 			status = DLADM_STATUS_TOOSMALL;
3715 			goto done;
3716 		}
3717 		(void) strlcpy(prop_val[cnt++], str, DLADM_PROP_VAL_MAX);
3718 		str = strtok(NULL, ",");
3719 	}
3720 
3721 	*val_cntp = cnt;
3722 
3723 done:
3724 	dladm_destroy_conf(handle, conf);
3725 	return (status);
3726 }
3727 
3728 /*
3729  * Walk persistent private link properties of a link.
3730  */
3731 static dladm_status_t
3732 i_dladm_walk_linkprop_priv_db(dladm_handle_t handle, datalink_id_t linkid,
3733     void *arg, int (*func)(dladm_handle_t, datalink_id_t, const char *, void *))
3734 {
3735 	dladm_status_t		status;
3736 	dladm_conf_t		conf;
3737 	char			last_attr[MAXLINKATTRLEN];
3738 	char			attr[MAXLINKATTRLEN];
3739 	char			attrval[MAXLINKATTRVALLEN];
3740 	size_t			attrsz;
3741 
3742 	if (linkid == DATALINK_INVALID_LINKID || func == NULL)
3743 		return (DLADM_STATUS_BADARG);
3744 
3745 	status = dladm_getsnap_conf(handle, linkid, &conf);
3746 	if (status != DLADM_STATUS_OK)
3747 		return (status);
3748 
3749 	last_attr[0] = '\0';
3750 	while ((status = dladm_getnext_conf_linkprop(handle, conf, last_attr,
3751 	    attr, attrval, MAXLINKATTRVALLEN, &attrsz)) == DLADM_STATUS_OK) {
3752 		if (attr[0] == '_') {
3753 			if (func(handle, linkid, attr, arg) ==
3754 			    DLADM_WALK_TERMINATE)
3755 				break;
3756 		}
3757 		(void) strlcpy(last_attr, attr, MAXLINKATTRLEN);
3758 	}
3759 
3760 	dladm_destroy_conf(handle, conf);
3761 	return (DLADM_STATUS_OK);
3762 }
3763 
3764 static link_attr_t *
3765 dladm_name2prop(const char *prop_name)
3766 {
3767 	link_attr_t *p;
3768 
3769 	for (p = link_attr; p->pp_id != MAC_PROP_PRIVATE; p++) {
3770 		if (strcmp(p->pp_name, prop_name) == 0)
3771 			break;
3772 	}
3773 	return (p);
3774 }
3775 
3776 static link_attr_t *
3777 dladm_id2prop(mac_prop_id_t propid)
3778 {
3779 	link_attr_t *p;
3780 
3781 	for (p = link_attr; p->pp_id != MAC_PROP_PRIVATE; p++) {
3782 		if (p->pp_id == propid)
3783 			break;
3784 	}
3785 	return (p);
3786 }
3787 
3788 static dld_ioc_macprop_t *
3789 i_dladm_buf_alloc_impl(size_t valsize, datalink_id_t linkid,
3790     const char *prop_name, mac_prop_id_t propid, uint_t flags,
3791     dladm_status_t *status)
3792 {
3793 	int dsize;
3794 	dld_ioc_macprop_t *dip;
3795 
3796 	*status = DLADM_STATUS_OK;
3797 	dsize = MAC_PROP_BUFSIZE(valsize);
3798 	dip = malloc(dsize);
3799 	if (dip == NULL) {
3800 		*status = DLADM_STATUS_NOMEM;
3801 		return (NULL);
3802 	}
3803 	bzero(dip, dsize);
3804 	dip->pr_valsize = valsize;
3805 	(void) strlcpy(dip->pr_name, prop_name, sizeof (dip->pr_name));
3806 	dip->pr_linkid = linkid;
3807 	dip->pr_num = propid;
3808 	dip->pr_flags = flags;
3809 	return (dip);
3810 }
3811 
3812 static dld_ioc_macprop_t *
3813 i_dladm_buf_alloc_by_name(size_t valsize, datalink_id_t linkid,
3814     const char *prop_name, uint_t flags, dladm_status_t *status)
3815 {
3816 	link_attr_t *p;
3817 
3818 	p = dladm_name2prop(prop_name);
3819 	valsize = MAX(p->pp_valsize, valsize);
3820 	return (i_dladm_buf_alloc_impl(valsize, linkid, prop_name, p->pp_id,
3821 	    flags, status));
3822 }
3823 
3824 static dld_ioc_macprop_t *
3825 i_dladm_buf_alloc_by_id(size_t valsize, datalink_id_t linkid,
3826     mac_prop_id_t propid, uint_t flags, dladm_status_t *status)
3827 {
3828 	link_attr_t *p;
3829 
3830 	p = dladm_id2prop(propid);
3831 	valsize = MAX(p->pp_valsize, valsize);
3832 	return (i_dladm_buf_alloc_impl(valsize, linkid, p->pp_name, propid,
3833 	    flags, status));
3834 }
3835 
3836 /* ARGSUSED */
3837 static dladm_status_t
3838 set_public_prop(dladm_handle_t handle, prop_desc_t *pdp,
3839     datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt, uint_t flags,
3840     datalink_media_t media)
3841 {
3842 	dld_ioc_macprop_t	*dip;
3843 	dladm_status_t	status = DLADM_STATUS_OK;
3844 	uint8_t		u8;
3845 	uint16_t	u16;
3846 	uint32_t	u32;
3847 	void		*val;
3848 
3849 	dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name, 0, &status);
3850 	if (dip == NULL)
3851 		return (status);
3852 
3853 	if (pdp->pd_flags & PD_CHECK_ALLOC)
3854 		val = (void *)vdp->vd_val;
3855 	else {
3856 		/*
3857 		 * Currently all 1/2/4-byte size properties are byte/word/int.
3858 		 * No need (yet) to distinguish these from arrays of same size.
3859 		 */
3860 		switch (dip->pr_valsize) {
3861 		case 1:
3862 			u8 = vdp->vd_val;
3863 			val = &u8;
3864 			break;
3865 		case 2:
3866 			u16 = vdp->vd_val;
3867 			val = &u16;
3868 			break;
3869 		case 4:
3870 			u32 = vdp->vd_val;
3871 			val = &u32;
3872 			break;
3873 		default:
3874 			val = &vdp->vd_val;
3875 			break;
3876 		}
3877 	}
3878 
3879 	if (val != NULL)
3880 		(void) memcpy(dip->pr_val, val, dip->pr_valsize);
3881 	else
3882 		dip->pr_valsize = 0;
3883 
3884 	status = i_dladm_macprop(handle, dip, B_TRUE);
3885 
3886 done:
3887 	free(dip);
3888 	return (status);
3889 }
3890 
3891 static dladm_status_t
3892 set_public_bitprop(dladm_handle_t handle, prop_desc_t *pdp,
3893     datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt, uint_t flags,
3894     datalink_media_t media)
3895 {
3896 	uint_t		i, j;
3897 	val_desc_t	vd = { 0 };
3898 
3899 	if ((pdp->pd_flags & PD_CHECK_ALLOC) != 0)
3900 		return (DLADM_STATUS_BADARG);
3901 
3902 	for (i = 0; i < val_cnt; i++) {
3903 		for (j = 0; j < pdp->pd_noptval; j++) {
3904 			if (strcasecmp(vdp[i].vd_name,
3905 			    pdp->pd_optval[j].vd_name) == 0) {
3906 				vd.vd_val |= pdp->pd_optval[j].vd_val;
3907 				break;
3908 			}
3909 		}
3910 	}
3911 
3912 	if (vd.vd_val == 0)
3913 		return (DLADM_STATUS_BADARG);
3914 
3915 	return (set_public_prop(handle, pdp, linkid, &vd, 1, flags, media));
3916 }
3917 
3918 dladm_status_t
3919 i_dladm_macprop(dladm_handle_t handle, void *dip, boolean_t set)
3920 {
3921 	dladm_status_t status = DLADM_STATUS_OK;
3922 
3923 	if (ioctl(dladm_dld_fd(handle),
3924 	    (set ? DLDIOC_SETMACPROP : DLDIOC_GETMACPROP), dip))
3925 		status = dladm_errno2status(errno);
3926 
3927 	return (status);
3928 }
3929 
3930 static dladm_status_t
3931 i_dladm_get_public_prop(dladm_handle_t handle, datalink_id_t linkid,
3932     char *prop_name, uint_t flags, uint_t *perm_flags, void *arg, size_t size)
3933 {
3934 	dld_ioc_macprop_t	*dip;
3935 	dladm_status_t		status;
3936 
3937 	dip = i_dladm_buf_alloc_by_name(0, linkid, prop_name, flags, &status);
3938 	if (dip == NULL)
3939 		return (DLADM_STATUS_NOMEM);
3940 
3941 	status = i_dladm_macprop(handle, dip, B_FALSE);
3942 	if (status != DLADM_STATUS_OK) {
3943 		free(dip);
3944 		return (status);
3945 	}
3946 
3947 	if (perm_flags != NULL)
3948 		*perm_flags = dip->pr_perm_flags;
3949 
3950 	if (arg != NULL)
3951 		(void) memcpy(arg, dip->pr_val, size);
3952 	free(dip);
3953 	return (DLADM_STATUS_OK);
3954 }
3955 
3956 /* ARGSUSED */
3957 static dladm_status_t
3958 check_uint32(dladm_handle_t handle, prop_desc_t *pdp,
3959     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
3960     val_desc_t **vp, datalink_media_t media)
3961 {
3962 	uint_t		val_cnt = *val_cntp;
3963 	val_desc_t	*v = *vp;
3964 
3965 	if (val_cnt != 1)
3966 		return (DLADM_STATUS_BADVAL);
3967 	v->vd_val = strtoul(prop_val[0], NULL, 0);
3968 	return (DLADM_STATUS_OK);
3969 }
3970 
3971 /* ARGSUSED */
3972 static dladm_status_t
3973 get_duplex(dladm_handle_t handle, prop_desc_t *pdp,
3974     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3975     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3976 {
3977 	link_duplex_t   link_duplex;
3978 	dladm_status_t  status;
3979 
3980 	if ((status = dladm_get_single_mac_stat(handle, linkid, "link_duplex",
3981 	    KSTAT_DATA_UINT32, &link_duplex)) != 0)
3982 		return (status);
3983 
3984 	switch (link_duplex) {
3985 	case LINK_DUPLEX_FULL:
3986 		(void) strcpy(*prop_val, "full");
3987 		break;
3988 	case LINK_DUPLEX_HALF:
3989 		(void) strcpy(*prop_val, "half");
3990 		break;
3991 	default:
3992 		(void) strcpy(*prop_val, "unknown");
3993 		break;
3994 	}
3995 	*val_cnt = 1;
3996 	return (DLADM_STATUS_OK);
3997 }
3998 
3999 /* ARGSUSED */
4000 static dladm_status_t
4001 get_speed(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
4002     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
4003     uint_t *perm_flags)
4004 {
4005 	uint64_t	ifspeed = 0;
4006 	dladm_status_t status;
4007 
4008 	if ((status = dladm_get_single_mac_stat(handle, linkid, "ifspeed",
4009 	    KSTAT_DATA_UINT64, &ifspeed)) != 0)
4010 		return (status);
4011 
4012 	if ((ifspeed % 1000000) != 0) {
4013 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
4014 		    "%llf", ifspeed / (float)1000000); /* Mbps */
4015 	} else {
4016 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
4017 		    "%llu", ifspeed / 1000000); /* Mbps */
4018 	}
4019 	*val_cnt = 1;
4020 	*perm_flags = MAC_PROP_PERM_READ;
4021 	return (DLADM_STATUS_OK);
4022 }
4023 
4024 /* ARGSUSED */
4025 static dladm_status_t
4026 get_link_state(dladm_handle_t handle, prop_desc_t *pdp,
4027     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4028     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4029 {
4030 	link_state_t		link_state;
4031 	dladm_status_t		status;
4032 
4033 	status = dladm_get_state(handle, linkid, &link_state);
4034 	if (status != DLADM_STATUS_OK)
4035 		return (status);
4036 
4037 	switch (link_state) {
4038 	case LINK_STATE_UP:
4039 		(void) strcpy(*prop_val, "up");
4040 		break;
4041 	case LINK_STATE_DOWN:
4042 		(void) strcpy(*prop_val, "down");
4043 		break;
4044 	default:
4045 		(void) strcpy(*prop_val, "unknown");
4046 		break;
4047 	}
4048 	*val_cnt = 1;
4049 	*perm_flags = MAC_PROP_PERM_READ;
4050 	return (DLADM_STATUS_OK);
4051 }
4052 
4053 /* ARGSUSED */
4054 static dladm_status_t
4055 get_binary(dladm_handle_t handle, prop_desc_t *pdp,
4056     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4057     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4058 {
4059 	dladm_status_t	status;
4060 	uint_t		v = 0;
4061 
4062 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
4063 	    perm_flags, &v, sizeof (v));
4064 	if (status != DLADM_STATUS_OK)
4065 		return (status);
4066 
4067 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%d", (uint_t)(v > 0));
4068 	*val_cnt = 1;
4069 	return (DLADM_STATUS_OK);
4070 }
4071 
4072 /* ARGSUSED */
4073 static dladm_status_t
4074 get_uint32(dladm_handle_t handle, prop_desc_t *pdp,
4075     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4076     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4077 {
4078 	dladm_status_t	status;
4079 	uint32_t	v = 0;
4080 
4081 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
4082 	    perm_flags, &v, sizeof (v));
4083 	if (status != DLADM_STATUS_OK)
4084 		return (status);
4085 
4086 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%ld", v);
4087 	*val_cnt = 1;
4088 	return (DLADM_STATUS_OK);
4089 }
4090 
4091 /* ARGSUSED */
4092 static dladm_status_t
4093 get_range(dladm_handle_t handle, prop_desc_t *pdp,
4094     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4095     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4096 {
4097 	dld_ioc_macprop_t *dip;
4098 	dladm_status_t status = DLADM_STATUS_OK;
4099 	size_t	sz;
4100 	uint_t	rcount;
4101 	mac_propval_range_t *rangep;
4102 
4103 	/*
4104 	 * As caller we don't know number of value ranges, the driver
4105 	 * supports. To begin with we assume that number to be 1. If the
4106 	 * buffer size is insufficient, driver returns back with the
4107 	 * actual count of value ranges. See mac.h for more details.
4108 	 */
4109 	sz = sizeof (mac_propval_range_t);
4110 	rcount = 1;
4111 retry:
4112 	if ((dip = i_dladm_buf_alloc_by_name(sz, linkid, pdp->pd_name, flags,
4113 	    &status)) == NULL)
4114 		return (status);
4115 
4116 	rangep = (mac_propval_range_t *)(void *)&dip->pr_val;
4117 	rangep->mpr_count = rcount;
4118 
4119 	status = i_dladm_macprop(handle, dip, B_FALSE);
4120 	if (status != DLADM_STATUS_OK) {
4121 		if (status == DLADM_STATUS_TOOSMALL) {
4122 			int err;
4123 
4124 			if ((err = i_dladm_range_size(rangep, &sz, &rcount))
4125 			    == 0) {
4126 				free(dip);
4127 				goto retry;
4128 			} else {
4129 				status = dladm_errno2status(err);
4130 			}
4131 		}
4132 		free(dip);
4133 		return (status);
4134 	}
4135 
4136 	if (rangep->mpr_count == 0) {
4137 		*val_cnt = 1;
4138 		(void) snprintf(prop_val[0], DLADM_PROP_VAL_MAX, "--");
4139 		goto done;
4140 	}
4141 
4142 	switch (rangep->mpr_type) {
4143 	case MAC_PROPVAL_UINT32: {
4144 		mac_propval_uint32_range_t *ur;
4145 		uint_t	count = rangep->mpr_count, i;
4146 
4147 		ur = &rangep->mpr_range_uint32[0];
4148 
4149 		for (i = 0; i < count; i++, ur++) {
4150 			if (ur->mpur_min == ur->mpur_max) {
4151 				(void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
4152 				    "%ld", ur->mpur_min);
4153 			} else {
4154 				(void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
4155 				    "%ld-%ld", ur->mpur_min, ur->mpur_max);
4156 			}
4157 		}
4158 		*val_cnt = count;
4159 		break;
4160 	}
4161 	default:
4162 		status = DLADM_STATUS_BADARG;
4163 		break;
4164 	}
4165 done:
4166 	free(dip);
4167 	return (status);
4168 }
4169 
4170 /* ARGSUSED */
4171 static dladm_status_t
4172 get_tagmode(dladm_handle_t handle, prop_desc_t *pdp,
4173     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4174     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4175 {
4176 	link_tagmode_t		mode;
4177 	dladm_status_t		status;
4178 
4179 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
4180 	    perm_flags, &mode, sizeof (mode));
4181 	if (status != DLADM_STATUS_OK)
4182 		return (status);
4183 
4184 	switch (mode) {
4185 	case LINK_TAGMODE_NORMAL:
4186 		(void) strlcpy(*prop_val, "normal", DLADM_PROP_VAL_MAX);
4187 		break;
4188 	case LINK_TAGMODE_VLANONLY:
4189 		(void) strlcpy(*prop_val, "vlanonly", DLADM_PROP_VAL_MAX);
4190 		break;
4191 	default:
4192 		(void) strlcpy(*prop_val, "unknown", DLADM_PROP_VAL_MAX);
4193 	}
4194 	*val_cnt = 1;
4195 	return (DLADM_STATUS_OK);
4196 }
4197 
4198 /* ARGSUSED */
4199 static dladm_status_t
4200 get_flowctl(dladm_handle_t handle, prop_desc_t *pdp,
4201     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4202     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4203 {
4204 	link_flowctrl_t	v;
4205 	dladm_status_t	status;
4206 
4207 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
4208 	    perm_flags, &v, sizeof (v));
4209 	if (status != DLADM_STATUS_OK)
4210 		return (status);
4211 
4212 	switch (v) {
4213 	case LINK_FLOWCTRL_NONE:
4214 		(void) sprintf(*prop_val, "no");
4215 		break;
4216 	case LINK_FLOWCTRL_RX:
4217 		(void) sprintf(*prop_val, "rx");
4218 		break;
4219 	case LINK_FLOWCTRL_TX:
4220 		(void) sprintf(*prop_val, "tx");
4221 		break;
4222 	case LINK_FLOWCTRL_BI:
4223 		(void) sprintf(*prop_val, "bi");
4224 		break;
4225 	}
4226 	*val_cnt = 1;
4227 	return (DLADM_STATUS_OK);
4228 }
4229 
4230 static dladm_status_t
4231 get_bits(dladm_handle_t handle, prop_desc_t *pdp,
4232     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4233     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4234 {
4235 	uint32_t	v;
4236 	dladm_status_t	status;
4237 	uint_t		i, cnt;
4238 
4239 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
4240 	    perm_flags, &v, sizeof (v));
4241 	if (status != DLADM_STATUS_OK)
4242 		return (status);
4243 
4244 	cnt = 0;
4245 	for (i = 0; cnt < *val_cnt && i < pdp->pd_noptval; i++) {
4246 		if ((v & pdp->pd_optval[i].vd_val) != 0) {
4247 			(void) snprintf(prop_val[cnt++], DLADM_STRSIZE,
4248 			    pdp->pd_optval[i].vd_name);
4249 		}
4250 	}
4251 
4252 	if (i < pdp->pd_noptval)
4253 		return (DLADM_STATUS_BADVALCNT);
4254 
4255 	*val_cnt = cnt;
4256 	return (DLADM_STATUS_OK);
4257 }
4258 
4259 /* ARGSUSED */
4260 static dladm_status_t
4261 i_dladm_set_private_prop(dladm_handle_t handle, datalink_id_t linkid,
4262     const char *prop_name, char **prop_val, uint_t val_cnt, uint_t flags)
4263 {
4264 	int		i, slen;
4265 	int		bufsize = 0;
4266 	dld_ioc_macprop_t *dip = NULL;
4267 	uchar_t		*dp;
4268 	link_attr_t *p;
4269 	dladm_status_t	status = DLADM_STATUS_OK;
4270 
4271 	if ((prop_name == NULL && prop_val != NULL) ||
4272 	    (prop_val != NULL && val_cnt == 0))
4273 		return (DLADM_STATUS_BADARG);
4274 	p = dladm_name2prop(prop_name);
4275 	if (p->pp_id != MAC_PROP_PRIVATE)
4276 		return (DLADM_STATUS_BADARG);
4277 
4278 	if (!(flags & DLADM_OPT_ACTIVE))
4279 		return (DLADM_STATUS_OK);
4280 
4281 	/*
4282 	 * private properties: all parsing is done in the kernel.
4283 	 * allocate a enough space for each property + its separator (',').
4284 	 */
4285 	for (i = 0; i < val_cnt; i++) {
4286 		bufsize += strlen(prop_val[i]) + 1;
4287 	}
4288 
4289 	if (prop_val == NULL) {
4290 		/*
4291 		 * getting default value. so use more buffer space.
4292 		 */
4293 		bufsize += DLADM_PROP_BUF_CHUNK;
4294 	}
4295 
4296 	dip = i_dladm_buf_alloc_by_name(bufsize + 1, linkid, prop_name,
4297 	    (prop_val != NULL ? 0 : DLD_PROP_DEFAULT), &status);
4298 	if (dip == NULL)
4299 		return (status);
4300 
4301 	dp = (uchar_t *)dip->pr_val;
4302 	slen = 0;
4303 
4304 	if (prop_val == NULL) {
4305 		status = i_dladm_macprop(handle, dip, B_FALSE);
4306 		dip->pr_flags = 0;
4307 	} else {
4308 		for (i = 0; i < val_cnt; i++) {
4309 			int plen = 0;
4310 
4311 			plen = strlen(prop_val[i]);
4312 			bcopy(prop_val[i], dp, plen);
4313 			slen += plen;
4314 			/*
4315 			 * add a "," separator and update dp.
4316 			 */
4317 			if (i != (val_cnt -1))
4318 				dp[slen++] = ',';
4319 			dp += (plen + 1);
4320 		}
4321 	}
4322 	if (status == DLADM_STATUS_OK)
4323 		status = i_dladm_macprop(handle, dip, B_TRUE);
4324 
4325 	free(dip);
4326 	return (status);
4327 }
4328 
4329 static dladm_status_t
4330 i_dladm_get_priv_prop(dladm_handle_t handle, datalink_id_t linkid,
4331     const char *prop_name, char **prop_val, uint_t *val_cnt,
4332     dladm_prop_type_t type, uint_t dld_flags)
4333 {
4334 	dladm_status_t	status = DLADM_STATUS_OK;
4335 	dld_ioc_macprop_t *dip = NULL;
4336 	link_attr_t *p;
4337 
4338 	if ((prop_name == NULL && prop_val != NULL) ||
4339 	    (prop_val != NULL && val_cnt == 0))
4340 		return (DLADM_STATUS_BADARG);
4341 
4342 	p = dladm_name2prop(prop_name);
4343 	if (p->pp_id != MAC_PROP_PRIVATE)
4344 		return (DLADM_STATUS_BADARG);
4345 
4346 	/*
4347 	 * private properties: all parsing is done in the kernel.
4348 	 */
4349 	dip = i_dladm_buf_alloc_by_name(DLADM_PROP_BUF_CHUNK, linkid, prop_name,
4350 	    dld_flags, &status);
4351 	if (dip == NULL)
4352 		return (status);
4353 
4354 	if ((status = i_dladm_macprop(handle, dip, B_FALSE)) ==
4355 	    DLADM_STATUS_OK) {
4356 		if (type == DLADM_PROP_VAL_PERM) {
4357 			(void) dladm_perm2str(dip->pr_perm_flags, *prop_val);
4358 		} else if (type == DLADM_PROP_VAL_MODIFIABLE) {
4359 			*prop_val[0] = '\0';
4360 		} else {
4361 			(void) strncpy(*prop_val, dip->pr_val,
4362 			    DLADM_PROP_VAL_MAX);
4363 		}
4364 		*val_cnt = 1;
4365 	} else if ((status == DLADM_STATUS_NOTSUP) &&
4366 	    (type == DLADM_PROP_VAL_CURRENT)) {
4367 		status = DLADM_STATUS_NOTFOUND;
4368 	}
4369 	free(dip);
4370 	return (status);
4371 }
4372 
4373 
4374 static dladm_status_t
4375 i_dladm_getset_defval(dladm_handle_t handle, prop_desc_t *pdp,
4376     datalink_id_t linkid, datalink_media_t media, uint_t flags)
4377 {
4378 	dladm_status_t status;
4379 	char **prop_vals = NULL, *buf;
4380 	size_t bufsize;
4381 	uint_t cnt;
4382 	int i;
4383 	uint_t perm_flags;
4384 
4385 	/*
4386 	 * Allocate buffer needed for prop_vals array. We can have at most
4387 	 * DLADM_MAX_PROP_VALCNT char *prop_vals[] entries, where
4388 	 * each entry has max size DLADM_PROP_VAL_MAX
4389 	 */
4390 	bufsize =
4391 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
4392 	buf = malloc(bufsize);
4393 	prop_vals = (char **)(void *)buf;
4394 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
4395 		prop_vals[i] = buf +
4396 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
4397 		    i * DLADM_PROP_VAL_MAX;
4398 	}
4399 
4400 	/*
4401 	 * For properties which have pdp->pd_defval.vd_name as a non-empty
4402 	 * string, the "" itself is used to reset the property (exceptions
4403 	 * are zone and autopush, which populate vdp->vd_val). So
4404 	 * libdladm can copy pdp->pd_defval over to the val_desc_t passed
4405 	 * down on the setprop using the global values in the table. For
4406 	 * other cases (vd_name is ""), doing reset-linkprop will cause
4407 	 * libdladm to do a getprop to find the default value and then do
4408 	 * a setprop to reset the value to default.
4409 	 */
4410 	status = pdp->pd_get(handle, pdp, linkid, prop_vals, &cnt, media,
4411 	    DLD_PROP_DEFAULT, &perm_flags);
4412 	if (status == DLADM_STATUS_OK) {
4413 		if (perm_flags == MAC_PROP_PERM_RW) {
4414 			status = i_dladm_set_single_prop(handle, linkid,
4415 			    pdp->pd_class, media, pdp, prop_vals, cnt, flags);
4416 		}
4417 		else
4418 			status = DLADM_STATUS_NOTSUP;
4419 	}
4420 	free(buf);
4421 	return (status);
4422 }
4423 
4424 /* ARGSUSED */
4425 static dladm_status_t
4426 get_stp(dladm_handle_t handle, struct prop_desc *pd, datalink_id_t linkid,
4427     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
4428     uint_t *perm_flags)
4429 {
4430 	const bridge_public_prop_t *bpp;
4431 	dladm_status_t retv;
4432 	int val, i;
4433 
4434 	if (flags != 0)
4435 		return (DLADM_STATUS_NOTSUP);
4436 	*perm_flags = MAC_PROP_PERM_RW;
4437 	*val_cnt = 1;
4438 	for (bpp = bridge_prop; bpp->bpp_name != NULL; bpp++)
4439 		if (strcmp(bpp->bpp_name, pd->pd_name) == 0)
4440 			break;
4441 	retv = dladm_bridge_get_port_cfg(handle, linkid, bpp->bpp_code, &val);
4442 	/* If the daemon isn't running, then return the persistent value */
4443 	if (retv == DLADM_STATUS_NOTFOUND) {
4444 		if (i_dladm_get_linkprop_db(handle, linkid, pd->pd_name,
4445 		    prop_val, val_cnt) != DLADM_STATUS_OK)
4446 			(void) strlcpy(*prop_val, pd->pd_defval.vd_name,
4447 			    DLADM_PROP_VAL_MAX);
4448 		return (DLADM_STATUS_OK);
4449 	}
4450 	if (retv != DLADM_STATUS_OK) {
4451 		(void) strlcpy(*prop_val, "?", DLADM_PROP_VAL_MAX);
4452 		return (retv);
4453 	}
4454 	if (val == pd->pd_defval.vd_val && pd->pd_defval.vd_name[0] != '\0') {
4455 		(void) strlcpy(*prop_val, pd->pd_defval.vd_name,
4456 		    DLADM_PROP_VAL_MAX);
4457 		return (DLADM_STATUS_OK);
4458 	}
4459 	for (i = 0; i < pd->pd_noptval; i++) {
4460 		if (val == pd->pd_optval[i].vd_val) {
4461 			(void) strlcpy(*prop_val, pd->pd_optval[i].vd_name,
4462 			    DLADM_PROP_VAL_MAX);
4463 			return (DLADM_STATUS_OK);
4464 		}
4465 	}
4466 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%u", (unsigned)val);
4467 	return (DLADM_STATUS_OK);
4468 }
4469 
4470 /* ARGSUSED1 */
4471 static dladm_status_t
4472 set_stp_prop(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid,
4473     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
4474 {
4475 	/*
4476 	 * Special case for mcheck: the daemon resets the value to zero, and we
4477 	 * don't want the daemon to refresh itself; it leads to deadlock.
4478 	 */
4479 	if (flags & DLADM_OPT_NOREFRESH)
4480 		return (DLADM_STATUS_OK);
4481 
4482 	/* Tell the running daemon, if any */
4483 	return (dladm_bridge_refresh(handle, linkid));
4484 }
4485 
4486 /*
4487  * This is used only for stp_priority, stp_cost, and stp_mcheck.
4488  */
4489 /* ARGSUSED */
4490 static dladm_status_t
4491 check_stp_prop(dladm_handle_t handle, struct prop_desc *pd,
4492     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
4493     val_desc_t **vdpp, datalink_media_t media)
4494 {
4495 	char		*cp;
4496 	boolean_t	iscost;
4497 	uint_t		val_cnt = *val_cntp;
4498 	val_desc_t	*vdp = *vdpp;
4499 
4500 	if (val_cnt != 1)
4501 		return (DLADM_STATUS_BADVALCNT);
4502 
4503 	if (prop_val == NULL) {
4504 		vdp->vd_val = 0;
4505 	} else {
4506 		/* Only stp_priority and stp_cost use this function */
4507 		iscost = strcmp(pd->pd_name, "stp_cost") == 0;
4508 
4509 		if (iscost && strcmp(prop_val[0], "auto") == 0) {
4510 			/* Illegal value 0 is allowed to mean "automatic" */
4511 			vdp->vd_val = 0;
4512 		} else {
4513 			errno = 0;
4514 			vdp->vd_val = strtoul(prop_val[0], &cp, 0);
4515 			if (errno != 0 || *cp != '\0')
4516 				return (DLADM_STATUS_BADVAL);
4517 		}
4518 	}
4519 
4520 	if (iscost) {
4521 		return (vdp->vd_val > 65535 ? DLADM_STATUS_BADVAL :
4522 		    DLADM_STATUS_OK);
4523 	} else {
4524 		if (vdp->vd_val > 255)
4525 			return (DLADM_STATUS_BADVAL);
4526 		/*
4527 		 * If the user is setting stp_mcheck non-zero, then (per the
4528 		 * IEEE management standards and UNH testing) we need to check
4529 		 * whether this link is part of a bridge that is running RSTP.
4530 		 * If it's not, then setting the flag is an error.  Note that
4531 		 * errors are intentionally discarded here; it's the value
4532 		 * that's the problem -- it's not a bad value, merely one that
4533 		 * can't be used now.
4534 		 */
4535 		if (strcmp(pd->pd_name, "stp_mcheck") == 0 &&
4536 		    vdp->vd_val != 0) {
4537 			char bridge[MAXLINKNAMELEN];
4538 			UID_STP_CFG_T cfg;
4539 			dladm_bridge_prot_t brprot;
4540 
4541 			if (dladm_bridge_getlink(handle, linkid, bridge,
4542 			    sizeof (bridge)) != DLADM_STATUS_OK ||
4543 			    dladm_bridge_get_properties(bridge, &cfg,
4544 			    &brprot) != DLADM_STATUS_OK)
4545 				return (DLADM_STATUS_FAILED);
4546 			if (cfg.force_version <= 1)
4547 				return (DLADM_STATUS_FAILED);
4548 		}
4549 		return (DLADM_STATUS_OK);
4550 	}
4551 }
4552 
4553 /* ARGSUSED */
4554 static dladm_status_t
4555 get_bridge_forward(dladm_handle_t handle, struct prop_desc *pd,
4556     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4557     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4558 {
4559 	dladm_status_t retv;
4560 	uint_t val;
4561 
4562 	if (flags != 0)
4563 		return (DLADM_STATUS_NOTSUP);
4564 	*perm_flags = MAC_PROP_PERM_RW;
4565 	*val_cnt = 1;
4566 	retv = dladm_bridge_get_forwarding(handle, linkid, &val);
4567 	if (retv == DLADM_STATUS_NOTFOUND) {
4568 		if (i_dladm_get_linkprop_db(handle, linkid, pd->pd_name,
4569 		    prop_val, val_cnt) != DLADM_STATUS_OK)
4570 			(void) strlcpy(*prop_val, pd->pd_defval.vd_name,
4571 			    DLADM_PROP_VAL_MAX);
4572 		return (DLADM_STATUS_OK);
4573 	}
4574 	if (retv == DLADM_STATUS_OK)
4575 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%u", val);
4576 	else
4577 		(void) strlcpy(*prop_val, "?", DLADM_PROP_VAL_MAX);
4578 	return (retv);
4579 }
4580 
4581 /* ARGSUSED */
4582 static dladm_status_t
4583 set_bridge_forward(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid,
4584     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
4585 {
4586 	/* Tell the running daemon, if any */
4587 	return (dladm_bridge_refresh(handle, linkid));
4588 }
4589 
4590 /* ARGSUSED */
4591 static dladm_status_t
4592 get_bridge_pvid(dladm_handle_t handle, struct prop_desc *pd,
4593     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4594     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4595 {
4596 	dladm_status_t status;
4597 	dld_ioc_macprop_t *dip;
4598 	uint16_t pvid;
4599 
4600 	if (flags != 0)
4601 		return (DLADM_STATUS_NOTSUP);
4602 	*perm_flags = MAC_PROP_PERM_RW;
4603 	*val_cnt = 1;
4604 	dip = i_dladm_buf_alloc_by_id(sizeof (uint16_t), linkid, MAC_PROP_PVID,
4605 	    0, &status);
4606 	if (dip == NULL)
4607 		return (status);
4608 	status = i_dladm_macprop(handle, dip, B_FALSE);
4609 	if (status == DLADM_STATUS_OK) {
4610 		(void) memcpy(&pvid, dip->pr_val, sizeof (pvid));
4611 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%u", pvid);
4612 	} else {
4613 		(void) strlcpy(*prop_val, "?", DLADM_PROP_VAL_MAX);
4614 	}
4615 	free(dip);
4616 	return (status);
4617 }
4618 
4619 /* ARGSUSED */
4620 static dladm_status_t
4621 set_bridge_pvid(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid,
4622     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
4623 {
4624 	dladm_status_t status;
4625 	dld_ioc_macprop_t *dip;
4626 	uint16_t pvid;
4627 
4628 	dip = i_dladm_buf_alloc_by_id(sizeof (uint16_t), linkid, MAC_PROP_PVID,
4629 	    0, &status);
4630 	if (dip == NULL)
4631 		return (status);
4632 	pvid = vdp->vd_val;
4633 	(void) memcpy(dip->pr_val, &pvid, sizeof (pvid));
4634 	status = i_dladm_macprop(handle, dip, B_TRUE);
4635 	free(dip);
4636 	if (status != DLADM_STATUS_OK)
4637 		return (status);
4638 
4639 	/* Tell the running daemon, if any */
4640 	return (dladm_bridge_refresh(handle, linkid));
4641 }
4642 
4643 /* ARGSUSED */
4644 static dladm_status_t
4645 check_bridge_pvid(dladm_handle_t handle, struct prop_desc *pd,
4646     datalink_id_t linkid, char **prop_val, uint_t *val_cntp, uint_t flags,
4647     val_desc_t **vdpp, datalink_media_t media)
4648 {
4649 	char		*cp;
4650 	uint_t		val_cnt = *val_cntp;
4651 	val_desc_t	*vdp = *vdpp;
4652 
4653 	if (val_cnt != 1)
4654 		return (DLADM_STATUS_BADVALCNT);
4655 
4656 	if (prop_val == NULL) {
4657 		vdp->vd_val = 1;
4658 	} else {
4659 		errno = 0;
4660 		vdp->vd_val = strtoul(prop_val[0], &cp, 0);
4661 		if (errno != 0 || *cp != '\0')
4662 			return (DLADM_STATUS_BADVAL);
4663 	}
4664 
4665 	return (vdp->vd_val > VLAN_ID_MAX ? DLADM_STATUS_BADVAL :
4666 	    DLADM_STATUS_OK);
4667 }
4668 
4669 dladm_status_t
4670 i_dladm_wlan_param(dladm_handle_t handle, datalink_id_t linkid, void *buf,
4671     mac_prop_id_t cmd, size_t len, boolean_t set)
4672 {
4673 	uint32_t		flags;
4674 	dladm_status_t		status;
4675 	uint32_t		media;
4676 	dld_ioc_macprop_t	*dip;
4677 	void			*dp;
4678 
4679 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL,
4680 	    &media, NULL, 0)) != DLADM_STATUS_OK) {
4681 		return (status);
4682 	}
4683 
4684 	if (media != DL_WIFI)
4685 		return (DLADM_STATUS_BADARG);
4686 
4687 	if (!(flags & DLADM_OPT_ACTIVE))
4688 		return (DLADM_STATUS_TEMPONLY);
4689 
4690 	if (len == (MAX_BUF_LEN - WIFI_BUF_OFFSET))
4691 		len = MAX_BUF_LEN - sizeof (dld_ioc_macprop_t) - 1;
4692 
4693 	dip = i_dladm_buf_alloc_by_id(len, linkid, cmd, 0, &status);
4694 	if (dip == NULL)
4695 		return (DLADM_STATUS_NOMEM);
4696 
4697 	dp = (uchar_t *)dip->pr_val;
4698 	if (set)
4699 		(void) memcpy(dp, buf, len);
4700 
4701 	status = i_dladm_macprop(handle, dip, set);
4702 	if (status == DLADM_STATUS_OK) {
4703 		if (!set)
4704 			(void) memcpy(buf, dp, len);
4705 	}
4706 
4707 	free(dip);
4708 	return (status);
4709 }
4710 
4711 dladm_status_t
4712 dladm_parse_link_props(char *str, dladm_arg_list_t **listp, boolean_t novalues)
4713 {
4714 	return (dladm_parse_args(str, listp, novalues));
4715 }
4716 
4717 /*
4718  * Retrieve the one link property from the database
4719  */
4720 /*ARGSUSED*/
4721 static int
4722 i_dladm_get_one_prop(dladm_handle_t handle, datalink_id_t linkid,
4723     const char *prop_name, void *arg)
4724 {
4725 	dladm_arg_list_t	*proplist = arg;
4726 	dladm_arg_info_t	*aip = NULL;
4727 
4728 	aip = &proplist->al_info[proplist->al_count];
4729 	/*
4730 	 * it is fine to point to prop_name since prop_name points to the
4731 	 * prop_table[n].pd_name.
4732 	 */
4733 	aip->ai_name = prop_name;
4734 
4735 	(void) dladm_get_linkprop(handle, linkid, DLADM_PROP_VAL_PERSISTENT,
4736 	    prop_name, aip->ai_val, &aip->ai_count);
4737 
4738 	if (aip->ai_count != 0)
4739 		proplist->al_count++;
4740 
4741 	return (DLADM_WALK_CONTINUE);
4742 }
4743 
4744 
4745 /*
4746  * Retrieve all link properties for a link from the database and
4747  * return a property list.
4748  */
4749 dladm_status_t
4750 dladm_link_get_proplist(dladm_handle_t handle, datalink_id_t linkid,
4751     dladm_arg_list_t **listp)
4752 {
4753 	dladm_arg_list_t	*list;
4754 	dladm_status_t		status = DLADM_STATUS_OK;
4755 
4756 	list = calloc(1, sizeof (dladm_arg_list_t));
4757 	if (list == NULL)
4758 		return (dladm_errno2status(errno));
4759 
4760 	status = dladm_walk_linkprop(handle, linkid, list,
4761 	    i_dladm_get_one_prop);
4762 
4763 	*listp = list;
4764 	return (status);
4765 }
4766 
4767 /*
4768  * Retrieve the named property from a proplist, check the value and
4769  * convert to a kernel structure.
4770  */
4771 static dladm_status_t
4772 i_dladm_link_proplist_extract_one(dladm_handle_t handle,
4773     dladm_arg_list_t *proplist, const char *name, uint_t flags, void *arg)
4774 {
4775 	dladm_status_t		status;
4776 	dladm_arg_info_t	*aip = NULL;
4777 	int			i, j;
4778 
4779 	/* Find named property in proplist */
4780 	for (i = 0; i < proplist->al_count; i++) {
4781 		aip = &proplist->al_info[i];
4782 		if (strcasecmp(aip->ai_name, name) == 0)
4783 			break;
4784 	}
4785 
4786 	/* Property not in list */
4787 	if (i == proplist->al_count)
4788 		return (DLADM_STATUS_OK);
4789 
4790 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
4791 		prop_desc_t	*pdp = &prop_table[i];
4792 		val_desc_t	*vdp;
4793 
4794 		vdp = malloc(sizeof (val_desc_t) * aip->ai_count);
4795 		if (vdp == NULL)
4796 			return (DLADM_STATUS_NOMEM);
4797 
4798 		if (strcasecmp(aip->ai_name, pdp->pd_name) != 0)
4799 			continue;
4800 
4801 		if (aip->ai_val == NULL)
4802 			return (DLADM_STATUS_BADARG);
4803 
4804 		/* Check property value */
4805 		if (pdp->pd_check != NULL) {
4806 			status = pdp->pd_check(handle, pdp, 0, aip->ai_val,
4807 			    &(aip->ai_count), flags, &vdp, 0);
4808 		} else {
4809 			status = DLADM_STATUS_BADARG;
4810 		}
4811 
4812 		if (status != DLADM_STATUS_OK)
4813 			return (status);
4814 
4815 		for (j = 0; j < DLADM_MAX_RSRC_PROP; j++) {
4816 			resource_prop_t	*rpp = &rsrc_prop_table[j];
4817 
4818 			if (strcasecmp(aip->ai_name, rpp->rp_name) != 0)
4819 				continue;
4820 
4821 			/* Extract kernel structure */
4822 			if (rpp->rp_extract != NULL) {
4823 				status = rpp->rp_extract(vdp,
4824 				    aip->ai_count, arg);
4825 			} else {
4826 				status = DLADM_STATUS_BADARG;
4827 			}
4828 			break;
4829 		}
4830 
4831 		if (status != DLADM_STATUS_OK)
4832 			return (status);
4833 
4834 		break;
4835 	}
4836 	return (status);
4837 }
4838 
4839 /*
4840  * Extract properties from a proplist and convert to mac_resource_props_t.
4841  */
4842 dladm_status_t
4843 dladm_link_proplist_extract(dladm_handle_t handle, dladm_arg_list_t *proplist,
4844     mac_resource_props_t *mrp, uint_t flags)
4845 {
4846 	dladm_status_t	status;
4847 	int		i;
4848 
4849 	for (i = 0; i < DLADM_MAX_RSRC_PROP; i++) {
4850 		status = i_dladm_link_proplist_extract_one(handle,
4851 		    proplist, rsrc_prop_table[i].rp_name, flags, mrp);
4852 		if (status != DLADM_STATUS_OK)
4853 			return (status);
4854 	}
4855 	return (status);
4856 }
4857 
4858 static const char *
4859 dladm_perm2str(uint_t perm, char *buf)
4860 {
4861 	(void) snprintf(buf, DLADM_STRSIZE, "%c%c",
4862 	    ((perm & MAC_PROP_PERM_READ) != 0) ? 'r' : '-',
4863 	    ((perm & MAC_PROP_PERM_WRITE) != 0) ? 'w' : '-');
4864 	return (buf);
4865 }
4866 
4867 dladm_status_t
4868 dladm_get_state(dladm_handle_t handle, datalink_id_t linkid,
4869     link_state_t *state)
4870 {
4871 	uint_t			perms;
4872 
4873 	return (i_dladm_get_public_prop(handle, linkid, "state", 0,
4874 	    &perms, state, sizeof (*state)));
4875 }
4876 
4877 boolean_t
4878 dladm_attr_is_linkprop(const char *name)
4879 {
4880 	/* non-property attribute names */
4881 	const char *nonprop[] = {
4882 		/* dlmgmtd core attributes */
4883 		"name",
4884 		"class",
4885 		"media",
4886 		FPHYMAJ,
4887 		FPHYINST,
4888 		FDEVNAME,
4889 
4890 		/* other attributes for vlan, aggr, etc */
4891 		DLADM_ATTR_NAMES
4892 	};
4893 	boolean_t	is_nonprop = B_FALSE;
4894 	int		i;
4895 
4896 	for (i = 0; i < sizeof (nonprop) / sizeof (nonprop[0]); i++) {
4897 		if (strcmp(name, nonprop[i]) == 0) {
4898 			is_nonprop = B_TRUE;
4899 			break;
4900 		}
4901 	}
4902 
4903 	return (!is_nonprop);
4904 }
4905 
4906 dladm_status_t
4907 dladm_linkprop_is_set(dladm_handle_t handle, datalink_id_t linkid,
4908     dladm_prop_type_t type, const char *prop_name, boolean_t *is_set)
4909 {
4910 	char		*buf, **propvals;
4911 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
4912 	int		i;
4913 	dladm_status_t	status = DLADM_STATUS_OK;
4914 	size_t		bufsize;
4915 
4916 	*is_set = B_FALSE;
4917 
4918 	bufsize = (sizeof (char *) + DLADM_PROP_VAL_MAX) *
4919 	    DLADM_MAX_PROP_VALCNT;
4920 	if ((buf = calloc(1, bufsize)) == NULL)
4921 		return (DLADM_STATUS_NOMEM);
4922 
4923 	propvals = (char **)(void *)buf;
4924 	for (i = 0; i < valcnt; i++) {
4925 		propvals[i] = buf +
4926 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
4927 		    i * DLADM_PROP_VAL_MAX;
4928 	}
4929 
4930 	if (dladm_get_linkprop(handle, linkid, type, prop_name, propvals,
4931 	    &valcnt) != DLADM_STATUS_OK) {
4932 		goto done;
4933 	}
4934 
4935 	/*
4936 	 * valcnt is always set to 1 by get_pool(), hence we need to check
4937 	 * for a non-null string to see if it is set. For protection,
4938 	 * secondary-macs and allowed-ips, we can check either the *propval
4939 	 * or the valcnt.
4940 	 */
4941 	if ((strcmp(prop_name, "pool") == 0 ||
4942 	    strcmp(prop_name, "protection") == 0 ||
4943 	    strcmp(prop_name, "secondary-macs") == 0 ||
4944 	    strcmp(prop_name, "allowed-ips") == 0) &&
4945 	    (strlen(*propvals) != 0)) {
4946 		*is_set = B_TRUE;
4947 	} else if ((strcmp(prop_name, "cpus") == 0) && (valcnt != 0)) {
4948 		*is_set = B_TRUE;
4949 	} else if ((strcmp(prop_name, "_softmac") == 0) && (valcnt != 0) &&
4950 	    (strcmp(propvals[0], "true") == 0)) {
4951 		*is_set = B_TRUE;
4952 	}
4953 
4954 done:
4955 	if (buf != NULL)
4956 		free(buf);
4957 	return (status);
4958 }
4959 
4960 /* ARGSUSED */
4961 static dladm_status_t
4962 get_linkmode_prop(dladm_handle_t handle, prop_desc_t *pdp,
4963     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4964     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4965 {
4966 	char			*s;
4967 	uint32_t		v;
4968 	dladm_status_t		status;
4969 
4970 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
4971 	    perm_flags, &v, sizeof (v));
4972 	if (status != DLADM_STATUS_OK)
4973 		return (status);
4974 
4975 	switch (v) {
4976 	case DLADM_PART_CM_MODE:
4977 		s = "cm";
4978 		break;
4979 	case DLADM_PART_UD_MODE:
4980 		s = "ud";
4981 		break;
4982 	default:
4983 		s = "";
4984 		break;
4985 	}
4986 	(void) snprintf(prop_val[0], DLADM_STRSIZE, "%s", s);
4987 
4988 	*val_cnt = 1;
4989 	return (DLADM_STATUS_OK);
4990 }
4991 
4992 /*ARGSUSED*/
4993 static dladm_status_t
4994 get_promisc_filtered(dladm_handle_t handle, prop_desc_t *pdp,
4995     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
4996     datalink_media_t media, uint_t flags, uint_t *perm_flags)
4997 {
4998 	char			*s;
4999 	dladm_status_t		status;
5000 	boolean_t		filt;
5001 
5002 	status = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
5003 	    perm_flags, &filt, sizeof (filt));
5004 	if (status != DLADM_STATUS_OK)
5005 		return (status);
5006 
5007 	if (filt != 0)
5008 		s = link_promisc_filtered_vals[1].vd_name;
5009 	else
5010 		s = link_promisc_filtered_vals[0].vd_name;
5011 	(void) snprintf(prop_val[0], DLADM_STRSIZE, "%s", s);
5012 
5013 	*val_cnt = 1;
5014 	return (DLADM_STATUS_OK);
5015 }
5016 
5017 /* ARGSUSED */
5018 static dladm_status_t
5019 set_promisc_filtered(dladm_handle_t handle, prop_desc_t *pdp,
5020     datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt, uint_t flags,
5021     datalink_media_t media)
5022 {
5023 	dld_ioc_macprop_t	*dip;
5024 	dladm_status_t		status = DLADM_STATUS_OK;
5025 
5026 	dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name,
5027 	    0, &status);
5028 
5029 	if (dip == NULL)
5030 		return (status);
5031 
5032 	(void) memcpy(dip->pr_val, &vdp->vd_val, dip->pr_valsize);
5033 	status = i_dladm_macprop(handle, dip, B_TRUE);
5034 
5035 	free(dip);
5036 	return (status);
5037 }
5038