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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdlib.h>
27 #include <strings.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <stddef.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/dld.h>
34 #include <sys/zone.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <libdevinfo.h>
38 #include <zone.h>
39 #include <libdllink.h>
40 #include <libdladm_impl.h>
41 #include <libdlwlan_impl.h>
42 #include <libdlwlan.h>
43 #include <libdlvlan.h>
44 #include <libdlvnic.h>
45 #include <libintl.h>
46 #include <dlfcn.h>
47 #include <link.h>
48 #include <inet/wifi_ioctl.h>
49 #include <libdladm.h>
50 #include <libdlstat.h>
51 #include <sys/param.h>
52 #include <sys/debug.h>
53 #include <sys/dld.h>
54 #include <inttypes.h>
55 #include <sys/ethernet.h>
56 #include <inet/iptun.h>
57 #include <net/wpa.h>
58 #include <sys/sysmacros.h>
59 #include <sys/vlan.h>
60 #include <libdlbridge.h>
61 #include <stp_in.h>
62 
63 /*
64  * The linkprop get() callback.
65  * - pd: 	pointer to the prop_desc_t
66  * - propstrp:	a property string array to keep the returned property.
67  *		Caller allocated.
68  * - cntp:	number of returned properties.
69  *		Caller also uses it to indicate how many it expects.
70  */
71 struct prop_desc;
72 typedef struct prop_desc prop_desc_t;
73 
74 typedef dladm_status_t	pd_getf_t(dladm_handle_t, prop_desc_t *pdp,
75 			datalink_id_t, char **propstp, uint_t *cntp,
76 			datalink_media_t, uint_t, uint_t *);
77 
78 /*
79  * The linkprop set() callback.
80  * - propval:	a val_desc_t array which keeps the property values to be set.
81  * - cnt:	number of properties to be set.
82  * - flags: 	additional flags passed down the system call.
83  *
84  * pd_set takes val_desc_t given by pd_check(), translates it into
85  * a format suitable for kernel consumption. This may require allocation
86  * of ioctl buffers etc. pd_set() may call another common routine (used
87  * by all other pd_sets) which invokes the ioctl.
88  */
89 typedef dladm_status_t	pd_setf_t(dladm_handle_t, prop_desc_t *, datalink_id_t,
90 			    val_desc_t *propval, uint_t cnt, uint_t flags,
91 			    datalink_media_t);
92 
93 /*
94  * The linkprop check() callback.
95  * - propstrp:	property string array which keeps the property to be checked.
96  * - cnt:	number of properties.
97  * - propval:	return value; the property values of the given property strings.
98  *
99  * pd_check checks that the input values are valid. It does so by
100  * iteraring through the pd_modval list for the property. If
101  * the modifiable values cannot be expressed as a list, a pd_check
102  * specific to this property can be used. If the input values are
103  * verified to be valid, pd_check allocates a val_desc_t and fills it
104  * with either a val_desc_t found on the pd_modval list or something
105  * generated on the fly.
106  */
107 typedef dladm_status_t	pd_checkf_t(dladm_handle_t, prop_desc_t *pdp,
108 			    datalink_id_t, char **propstrp, uint_t cnt,
109 			    val_desc_t *propval, datalink_media_t);
110 
111 typedef struct link_attr_s {
112 	mac_prop_id_t	pp_id;
113 	size_t		pp_valsize;
114 	char		*pp_name;
115 } link_attr_t;
116 
117 static dld_ioc_macprop_t *i_dladm_buf_alloc_by_name(size_t, datalink_id_t,
118 			    const char *, uint_t, dladm_status_t *);
119 static dld_ioc_macprop_t *i_dladm_buf_alloc_by_id(size_t, datalink_id_t,
120 			    mac_prop_id_t, uint_t, dladm_status_t *);
121 static dld_ioc_macprop_t *i_dladm_get_public_prop(dladm_handle_t, datalink_id_t,
122 			    char *, uint_t, dladm_status_t *, uint_t *);
123 
124 static dladm_status_t i_dladm_set_private_prop(dladm_handle_t, datalink_id_t,
125 			    const char *, char **, uint_t, uint_t);
126 static dladm_status_t i_dladm_get_priv_prop(dladm_handle_t, datalink_id_t,
127 			    const char *, char **, uint_t *, dladm_prop_type_t,
128 			    uint_t);
129 static link_attr_t *dladm_name2prop(const char *);
130 static link_attr_t *dladm_id2prop(mac_prop_id_t);
131 
132 static pd_getf_t	do_get_zone, do_get_autopush, do_get_rate_mod,
133 			do_get_rate_prop, do_get_channel_prop,
134 			do_get_powermode_prop, do_get_radio_prop,
135 			i_dladm_duplex_get, i_dladm_status_get,
136 			i_dladm_binary_get, i_dladm_uint32_get,
137 			i_dladm_flowctl_get, i_dladm_maxbw_get,
138 			i_dladm_cpus_get, i_dladm_priority_get,
139 			i_dladm_tagmode_get, i_dladm_range_get,
140 			get_stp_prop, get_bridge_forward,
141 			get_bridge_pvid,
142 			/* the above need to be renamed to "do_get_xxx" */
143 			do_get_protection;
144 
145 static pd_setf_t	do_set_zone, do_set_rate_prop,
146 			do_set_powermode_prop, do_set_radio_prop,
147 			i_dladm_set_public_prop, do_set_res, do_set_cpus,
148 			set_stp_prop, set_bridge_forward, set_bridge_pvid,
149 			do_set_protection;
150 
151 static pd_checkf_t	do_check_zone, do_check_autopush, do_check_rate,
152 			do_check_hoplimit, do_check_encaplim,
153 			i_dladm_uint32_check, do_check_maxbw, do_check_cpus,
154 			do_check_priority, check_stp_prop, check_bridge_pvid,
155 			do_check_allowedips, do_check_prop;
156 
157 static dladm_status_t	i_dladm_speed_get(dladm_handle_t, prop_desc_t *,
158 			    datalink_id_t, char **, uint_t *, uint_t, uint_t *);
159 static dladm_status_t	i_dladm_macprop(dladm_handle_t, void *, boolean_t);
160 static const char	*dladm_perm2str(uint_t, char *);
161 
162 struct prop_desc {
163 	/*
164 	 * link property name
165 	 */
166 	char			*pd_name;
167 
168 	/*
169 	 * default property value, can be set to { "", NULL }
170 	 */
171 	val_desc_t		pd_defval;
172 
173 	/*
174 	 * list of optional property values, can be NULL.
175 	 *
176 	 * This is set to non-NULL if there is a list of possible property
177 	 * values.  pd_optval would point to the array of possible values.
178 	 */
179 	val_desc_t		*pd_optval;
180 
181 	/*
182 	 * count of the above optional property values. 0 if pd_optval is NULL.
183 	 */
184 	uint_t			pd_noptval;
185 
186 	/*
187 	 * callback to set link property; set to NULL if this property is
188 	 * read-only and may be called before or after permanent update; see
189 	 * flags.
190 	 */
191 	pd_setf_t		*pd_set;
192 
193 	/*
194 	 * callback to get modifiable link property
195 	 */
196 	pd_getf_t		*pd_getmod;
197 
198 	/*
199 	 * callback to get current link property
200 	 */
201 	pd_getf_t		*pd_get;
202 
203 	/*
204 	 * callback to validate link property value, set to NULL if pd_optval
205 	 * is not NULL. In that case, validate the value by comparing it with
206 	 * the pd_optval. Return a val_desc_t array pointer if the value is
207 	 * valid.
208 	 */
209 	pd_checkf_t		*pd_check;
210 
211 	uint_t			pd_flags;
212 #define	PD_TEMPONLY	0x1	/* property is temporary only */
213 #define	PD_CHECK_ALLOC	0x2	/* alloc vd_val as part of pd_check */
214 #define	PD_AFTER_PERM	0x4	/* pd_set after db update; no temporary */
215 	/*
216 	 * indicate link classes this property applies to.
217 	 */
218 	datalink_class_t	pd_class;
219 
220 	/*
221 	 * indicate link media type this property applies to.
222 	 */
223 	datalink_media_t	pd_dmedia;
224 };
225 
226 #define	MAC_PROP_BUFSIZE(v)	sizeof (dld_ioc_macprop_t) + (v) - 1
227 
228 /*
229  * Supported link properties enumerated in the prop_table[] array are
230  * computed using the callback functions in that array. To compute the
231  * property value, multiple distinct system calls may be needed (e.g.,
232  * for wifi speed, we need to issue system calls to get desired/supported
233  * rates). The link_attr[] table enumerates the interfaces to the kernel,
234  * and the type/size of the data passed in the user-kernel interface.
235  */
236 static link_attr_t link_attr[] = {
237 	{ MAC_PROP_DUPLEX,	sizeof (link_duplex_t),	"duplex"},
238 
239 	{ MAC_PROP_SPEED,	sizeof (uint64_t),	"speed"},
240 
241 	{ MAC_PROP_STATUS,	sizeof (link_state_t),	"state"},
242 
243 	{ MAC_PROP_AUTONEG,	sizeof (uint8_t),	"adv_autoneg_cap"},
244 
245 	{ MAC_PROP_MTU,		sizeof (uint32_t),	"mtu"},
246 
247 	{ MAC_PROP_FLOWCTRL,	sizeof (link_flowctrl_t), "flowctrl"},
248 
249 	{ MAC_PROP_ZONE,	sizeof (dld_ioc_zid_t),	"zone"},
250 
251 	{ MAC_PROP_AUTOPUSH,	sizeof (struct dlautopush), "autopush"},
252 
253 	{ MAC_PROP_ADV_10GFDX_CAP, sizeof (uint8_t),	"adv_10gfdx_cap"},
254 
255 	{ MAC_PROP_EN_10GFDX_CAP, sizeof (uint8_t),	"en_10gfdx_cap"},
256 
257 	{ MAC_PROP_ADV_1000FDX_CAP, sizeof (uint8_t),	"adv_1000fdx_cap"},
258 
259 	{ MAC_PROP_EN_1000FDX_CAP, sizeof (uint8_t),	"en_1000fdx_cap"},
260 
261 	{ MAC_PROP_ADV_1000HDX_CAP, sizeof (uint8_t),	"adv_1000hdx_cap"},
262 
263 	{ MAC_PROP_EN_1000HDX_CAP, sizeof (uint8_t),	"en_1000hdx_cap"},
264 
265 	{ MAC_PROP_ADV_100FDX_CAP, sizeof (uint8_t),	"adv_100fdx_cap"},
266 
267 	{ MAC_PROP_EN_100FDX_CAP, sizeof (uint8_t),	"en_100fdx_cap"},
268 
269 	{ MAC_PROP_ADV_100HDX_CAP, sizeof (uint8_t),	"adv_100hdx_cap"},
270 
271 	{ MAC_PROP_EN_100HDX_CAP, sizeof (uint8_t),	"en_100hdx_cap"},
272 
273 	{ MAC_PROP_ADV_10FDX_CAP, sizeof (uint8_t),	"adv_10fdx_cap"},
274 
275 	{ MAC_PROP_EN_10FDX_CAP, sizeof (uint8_t),	"en_10fdx_cap"},
276 
277 	{ MAC_PROP_ADV_10HDX_CAP, sizeof (uint8_t),	"adv_10hdx_cap"},
278 
279 	{ MAC_PROP_EN_10HDX_CAP, sizeof (uint8_t),	"en_10hdx_cap"},
280 
281 	{ MAC_PROP_WL_ESSID,	sizeof (wl_linkstatus_t), "essid"},
282 
283 	{ MAC_PROP_WL_BSSID,	sizeof (wl_bssid_t),	"bssid"},
284 
285 	{ MAC_PROP_WL_BSSTYPE,	sizeof (wl_bss_type_t),	"bsstype"},
286 
287 	{ MAC_PROP_WL_LINKSTATUS, sizeof (wl_linkstatus_t), "wl_linkstatus"},
288 
289 	/* wl_rates_t has variable length */
290 	{ MAC_PROP_WL_DESIRED_RATES, sizeof (wl_rates_t), "desired_rates"},
291 
292 	/* wl_rates_t has variable length */
293 	{ MAC_PROP_WL_SUPPORTED_RATES, sizeof (wl_rates_t), "supported_rates"},
294 
295 	{ MAC_PROP_WL_AUTH_MODE, sizeof (wl_authmode_t), "authmode"},
296 
297 	{ MAC_PROP_WL_ENCRYPTION, sizeof (wl_encryption_t), "encryption"},
298 
299 	{ MAC_PROP_WL_RSSI,	sizeof (wl_rssi_t),	"signal"},
300 
301 	{ MAC_PROP_WL_PHY_CONFIG, sizeof (wl_phy_conf_t), "phy_conf"},
302 
303 	{ MAC_PROP_WL_CAPABILITY, sizeof (wl_capability_t), "capability"},
304 
305 	{ MAC_PROP_WL_WPA,	sizeof (wl_wpa_t),	"wpa"},
306 
307 	/*  wl_wpa_ess_t has variable length */
308 	{ MAC_PROP_WL_SCANRESULTS, sizeof (wl_wpa_ess_t), "scan_results"},
309 
310 	{ MAC_PROP_WL_POWER_MODE, sizeof (wl_ps_mode_t), "powermode"},
311 
312 	{ MAC_PROP_WL_RADIO,	sizeof (dladm_wlan_radio_t), "wl_radio"},
313 
314 	{ MAC_PROP_WL_ESS_LIST, sizeof (wl_ess_list_t),	"wl_ess_list"},
315 
316 	{ MAC_PROP_WL_KEY_TAB,	sizeof (wl_wep_key_tab_t), "wl_wep_key"},
317 
318 	{ MAC_PROP_WL_CREATE_IBSS, sizeof (wl_create_ibss_t), "createibss"},
319 
320 	/* wl_wpa_ie_t has variable length */
321 	{ MAC_PROP_WL_SETOPTIE,	sizeof (wl_wpa_ie_t),	"set_ie"},
322 
323 	{ MAC_PROP_WL_DELKEY,	sizeof (wl_del_key_t),	"wpa_del_key"},
324 
325 	{ MAC_PROP_WL_KEY,	sizeof (wl_key_t),	"wl_key"},
326 
327 	{ MAC_PROP_WL_MLME,	sizeof (wl_mlme_t),	"mlme"},
328 
329 	{ MAC_PROP_MAXBW,	sizeof (mac_resource_props_t),	"maxbw"},
330 
331 	{ MAC_PROP_PRIO,	sizeof (mac_resource_props_t),	"priority"},
332 
333 	{ MAC_PROP_BIND_CPU,	sizeof (mac_resource_props_t),	"cpus"},
334 
335 	{ MAC_PROP_TAGMODE,	sizeof (link_tagmode_t),	"tagmode"},
336 
337 	{ MAC_PROP_IPTUN_HOPLIMIT, sizeof (uint32_t),	"hoplimit"},
338 
339 	{ MAC_PROP_IPTUN_ENCAPLIMIT, sizeof (uint32_t),	"encaplimit"},
340 
341 	{ MAC_PROP_PVID,	sizeof (uint16_t),	"default_tag"},
342 
343 	{ MAC_PROP_LLIMIT,	sizeof (uint32_t),	"learn_limit"},
344 
345 	{ MAC_PROP_LDECAY,	sizeof (uint32_t),	"learn_decay"},
346 
347 	{ MAC_PROP_PROTECT,	sizeof (mac_resource_props_t),	"protection"},
348 
349 	{ MAC_PROP_PRIVATE,	0,			"driver-private"}
350 };
351 
352 typedef struct bridge_public_prop_s {
353 	const char	*bpp_name;
354 	int		bpp_code;
355 } bridge_public_prop_t;
356 
357 static const bridge_public_prop_t bridge_prop[] = {
358 	{ "stp", PT_CFG_NON_STP },
359 	{ "stp_priority", PT_CFG_PRIO },
360 	{ "stp_cost", PT_CFG_COST },
361 	{ "stp_edge", PT_CFG_EDGE },
362 	{ "stp_p2p", PT_CFG_P2P },
363 	{ "stp_mcheck", PT_CFG_MCHECK },
364 	{ NULL, 0 }
365 };
366 
367 static  val_desc_t	link_duplex_vals[] = {
368 	{ "half", 	LINK_DUPLEX_HALF	},
369 	{ "full", 	LINK_DUPLEX_HALF	}
370 };
371 static  val_desc_t	link_status_vals[] = {
372 	{ "up",		LINK_STATE_UP		},
373 	{ "down",	LINK_STATE_DOWN		}
374 };
375 static  val_desc_t	link_01_vals[] = {
376 	{ "1",		1			},
377 	{ "0",		0			}
378 };
379 static  val_desc_t	link_flow_vals[] = {
380 	{ "no",		LINK_FLOWCTRL_NONE	},
381 	{ "tx",		LINK_FLOWCTRL_TX	},
382 	{ "rx",		LINK_FLOWCTRL_RX	},
383 	{ "bi",		LINK_FLOWCTRL_BI	}
384 };
385 static  val_desc_t	link_priority_vals[] = {
386 	{ "low",	MPL_LOW	},
387 	{ "medium",	MPL_MEDIUM	},
388 	{ "high",	MPL_HIGH	}
389 };
390 
391 static val_desc_t	link_tagmode_vals[] = {
392 	{ "normal",	LINK_TAGMODE_NORMAL	},
393 	{ "vlanonly",	LINK_TAGMODE_VLANONLY	}
394 };
395 
396 static  val_desc_t	link_protect_vals[] = {
397 	{ "mac-nospoof",	MPT_MACNOSPOOF	},
398 	{ "ip-nospoof",		MPT_IPNOSPOOF	},
399 	{ "restricted",		MPT_RESTRICTED	}
400 };
401 
402 static val_desc_t	dladm_wlan_radio_vals[] = {
403 	{ "on",		DLADM_WLAN_RADIO_ON	},
404 	{ "off",	DLADM_WLAN_RADIO_OFF	}
405 };
406 
407 static val_desc_t	dladm_wlan_powermode_vals[] = {
408 	{ "off",	DLADM_WLAN_PM_OFF	},
409 	{ "fast",	DLADM_WLAN_PM_FAST	},
410 	{ "max",	DLADM_WLAN_PM_MAX	}
411 };
412 
413 static  val_desc_t	stp_p2p_vals[] = {
414 	{ "true",	P2P_FORCE_TRUE		},
415 	{ "false",	P2P_FORCE_FALSE		},
416 	{ "auto",	P2P_AUTO		}
417 };
418 
419 #define	VALCNT(vals)    (sizeof ((vals)) / sizeof (val_desc_t))
420 #define	RESET_VAL	((uintptr_t)-1)
421 
422 static prop_desc_t	prop_table[] = {
423 	{ "channel",	{ NULL, 0 },
424 	    NULL, 0, NULL, NULL,
425 	    do_get_channel_prop, NULL, 0,
426 	    DATALINK_CLASS_PHYS, DL_WIFI },
427 
428 	{ "powermode",	{ "off", DLADM_WLAN_PM_OFF },
429 	    dladm_wlan_powermode_vals, VALCNT(dladm_wlan_powermode_vals),
430 	    do_set_powermode_prop, NULL,
431 	    do_get_powermode_prop, NULL, 0,
432 	    DATALINK_CLASS_PHYS, DL_WIFI },
433 
434 	{ "radio",	{ "on", DLADM_WLAN_RADIO_ON },
435 	    dladm_wlan_radio_vals, VALCNT(dladm_wlan_radio_vals),
436 	    do_set_radio_prop, NULL,
437 	    do_get_radio_prop, NULL, 0,
438 	    DATALINK_CLASS_PHYS, DL_WIFI },
439 
440 	{ "speed",	{ "", 0 }, NULL, 0,
441 	    do_set_rate_prop, do_get_rate_mod,
442 	    do_get_rate_prop, do_check_rate, 0,
443 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE },
444 
445 	{ "autopush",	{ "", 0 }, NULL, 0,
446 	    i_dladm_set_public_prop, NULL,
447 	    do_get_autopush, do_check_autopush, PD_CHECK_ALLOC,
448 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
449 
450 	{ "zone",	{ "", 0 }, NULL, 0,
451 	    do_set_zone, NULL,
452 	    do_get_zone, do_check_zone, PD_TEMPONLY|PD_CHECK_ALLOC,
453 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
454 
455 	{ "duplex",	{ "", 0 },
456 	    link_duplex_vals, VALCNT(link_duplex_vals),
457 	    NULL, NULL, i_dladm_duplex_get, NULL,
458 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
459 
460 	{ "state",	{ "up", LINK_STATE_UP },
461 	    link_status_vals, VALCNT(link_status_vals),
462 	    NULL, NULL, i_dladm_status_get, NULL,
463 	    0, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
464 
465 	{ "adv_autoneg_cap", { "", 0 },
466 	    link_01_vals, VALCNT(link_01_vals),
467 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
468 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
469 
470 	{ "mtu", { "", 0 }, NULL, 0,
471 	    i_dladm_set_public_prop, i_dladm_range_get,
472 	    i_dladm_uint32_get, i_dladm_uint32_check, 0, DATALINK_CLASS_ALL,
473 	    DATALINK_ANY_MEDIATYPE },
474 
475 	{ "flowctrl", { "", 0 },
476 	    link_flow_vals, VALCNT(link_flow_vals),
477 	    i_dladm_set_public_prop, NULL, i_dladm_flowctl_get, NULL,
478 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
479 
480 	{ "adv_10gfdx_cap", { "", 0 },
481 	    link_01_vals, VALCNT(link_01_vals),
482 	    NULL, NULL, i_dladm_binary_get, NULL,
483 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
484 
485 	{ "en_10gfdx_cap", { "", 0 },
486 	    link_01_vals, VALCNT(link_01_vals),
487 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
488 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
489 
490 	{ "adv_1000fdx_cap", { "", 0 },
491 	    link_01_vals, VALCNT(link_01_vals),
492 	    NULL, NULL, i_dladm_binary_get, NULL,
493 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
494 
495 	{ "en_1000fdx_cap", { "", 0 },
496 	    link_01_vals, VALCNT(link_01_vals),
497 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
498 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
499 
500 	{ "adv_1000hdx_cap", { "", 0 },
501 	    link_01_vals, VALCNT(link_01_vals),
502 	    NULL, NULL, i_dladm_binary_get, NULL,
503 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
504 
505 	{ "en_1000hdx_cap", { "", 0 },
506 	    link_01_vals, VALCNT(link_01_vals),
507 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
508 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
509 
510 	{ "adv_100fdx_cap", { "", 0 },
511 	    link_01_vals, VALCNT(link_01_vals),
512 	    NULL, NULL, i_dladm_binary_get, NULL,
513 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
514 
515 	{ "en_100fdx_cap", { "", 0 },
516 	    link_01_vals, VALCNT(link_01_vals),
517 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
518 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
519 
520 	{ "adv_100hdx_cap", { "", 0 },
521 	    link_01_vals, VALCNT(link_01_vals),
522 	    NULL, NULL, i_dladm_binary_get, NULL,
523 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
524 
525 	{ "en_100hdx_cap", { "", 0 },
526 	    link_01_vals, VALCNT(link_01_vals),
527 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
528 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
529 
530 	{ "adv_10fdx_cap", { "", 0 },
531 	    link_01_vals, VALCNT(link_01_vals),
532 	    NULL, NULL, i_dladm_binary_get, NULL,
533 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
534 
535 	{ "en_10fdx_cap", { "", 0 },
536 	    link_01_vals, VALCNT(link_01_vals),
537 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
538 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
539 
540 	{ "adv_10hdx_cap", { "", 0 },
541 	    link_01_vals, VALCNT(link_01_vals),
542 	    NULL, NULL, i_dladm_binary_get, NULL,
543 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
544 
545 	{ "en_10hdx_cap", { "", 0 },
546 	    link_01_vals, VALCNT(link_01_vals),
547 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
548 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
549 
550 	{ "maxbw", { "--", RESET_VAL }, NULL, 0,
551 	    do_set_res, NULL,
552 	    i_dladm_maxbw_get, do_check_maxbw, PD_CHECK_ALLOC,
553 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
554 
555 	{ "cpus", { "--", RESET_VAL }, NULL, 0,
556 	    do_set_cpus, NULL,
557 	    i_dladm_cpus_get, do_check_cpus, 0,
558 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
559 
560 	{ "priority", { "high", RESET_VAL },
561 	    link_priority_vals, VALCNT(link_priority_vals), do_set_res, NULL,
562 	    i_dladm_priority_get, do_check_priority, PD_CHECK_ALLOC,
563 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
564 
565 	{ "tagmode", { "vlanonly", LINK_TAGMODE_VLANONLY },
566 	    link_tagmode_vals, VALCNT(link_tagmode_vals),
567 	    i_dladm_set_public_prop, NULL, i_dladm_tagmode_get,
568 	    NULL, 0,
569 	    DATALINK_CLASS_PHYS | DATALINK_CLASS_AGGR | DATALINK_CLASS_VNIC,
570 	    DL_ETHER },
571 
572 	{ "hoplimit", { "", 0 }, NULL, 0,
573 	    i_dladm_set_public_prop, i_dladm_range_get, i_dladm_uint32_get,
574 	    do_check_hoplimit, 0, DATALINK_CLASS_IPTUN, DATALINK_ANY_MEDIATYPE},
575 
576 	{ "encaplimit", { "", 0 }, NULL, 0,
577 	    i_dladm_set_public_prop, i_dladm_range_get, i_dladm_uint32_get,
578 	    do_check_encaplim, 0, DATALINK_CLASS_IPTUN, DL_IPV6},
579 
580 	{ "forward", { "1", 1 },
581 	    link_01_vals, VALCNT(link_01_vals),
582 	    set_bridge_forward, NULL, get_bridge_forward, NULL, PD_AFTER_PERM,
583 	    DATALINK_CLASS_ALL & ~DATALINK_CLASS_VNIC, DL_ETHER },
584 
585 	{ "default_tag", { "1", 1 }, NULL, 0,
586 	    set_bridge_pvid, NULL, get_bridge_pvid, check_bridge_pvid,
587 	    0, DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
588 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
589 
590 	{ "learn_limit", { "1000", 1000 }, NULL, 0,
591 	    i_dladm_set_public_prop, NULL, i_dladm_uint32_get,
592 	    i_dladm_uint32_check, 0,
593 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
594 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
595 
596 	{ "learn_decay", { "200", 200 }, NULL, 0,
597 	    i_dladm_set_public_prop, NULL, i_dladm_uint32_get,
598 	    i_dladm_uint32_check, 0,
599 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
600 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
601 
602 	{ "stp", { "1", 1 },
603 	    link_01_vals, VALCNT(link_01_vals),
604 	    set_stp_prop, NULL, get_stp_prop, NULL, PD_AFTER_PERM,
605 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
606 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
607 
608 	{ "stp_priority", { "128", 128 }, NULL, 0,
609 	    set_stp_prop, NULL, get_stp_prop, check_stp_prop, PD_AFTER_PERM,
610 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
611 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
612 
613 	{ "stp_cost", { "auto", 0 }, NULL, 0,
614 	    set_stp_prop, NULL, get_stp_prop, check_stp_prop, PD_AFTER_PERM,
615 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
616 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
617 
618 	{ "stp_edge", { "1", 1 },
619 	    link_01_vals, VALCNT(link_01_vals),
620 	    set_stp_prop, NULL, get_stp_prop, NULL, PD_AFTER_PERM,
621 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
622 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
623 
624 	{ "stp_p2p", { "auto", P2P_AUTO },
625 	    stp_p2p_vals, VALCNT(stp_p2p_vals),
626 	    set_stp_prop, NULL, get_stp_prop, NULL, PD_AFTER_PERM,
627 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
628 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
629 
630 	{ "stp_mcheck", { "0", 0 },
631 	    link_01_vals, VALCNT(link_01_vals),
632 	    set_stp_prop, NULL, get_stp_prop, check_stp_prop, PD_AFTER_PERM,
633 	    DATALINK_CLASS_PHYS|DATALINK_CLASS_AGGR|
634 	    DATALINK_CLASS_ETHERSTUB|DATALINK_CLASS_SIMNET, DL_ETHER },
635 
636 	{ "protection", { "--", RESET_VAL },
637 	    link_protect_vals, VALCNT(link_protect_vals),
638 	    do_set_protection, NULL, do_get_protection, do_check_prop, 0,
639 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
640 
641 	{ "allowed-ips", { "--", 0 },
642 	    NULL, 0, do_set_protection, NULL,
643 	    do_get_protection, do_check_allowedips, 0,
644 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
645 };
646 
647 #define	DLADM_MAX_PROPS	(sizeof (prop_table) / sizeof (prop_desc_t))
648 
649 static resource_prop_t rsrc_prop_table[] = {
650 	{"maxbw",	do_extract_maxbw},
651 	{"priority",	do_extract_priority},
652 	{"cpus",	do_extract_cpus},
653 	{"protection",	do_extract_protection},
654 	{"allowed-ips",	do_extract_allowedips}
655 };
656 #define	DLADM_MAX_RSRC_PROP (sizeof (rsrc_prop_table) / \
657 	sizeof (resource_prop_t))
658 
659 /*
660  * when retrieving  private properties, we pass down a buffer with
661  * DLADM_PROP_BUF_CHUNK of space for the driver to return the property value.
662  */
663 #define	DLADM_PROP_BUF_CHUNK	1024
664 
665 static dladm_status_t	i_dladm_set_linkprop_db(dladm_handle_t, datalink_id_t,
666 			    const char *, char **, uint_t);
667 static dladm_status_t	i_dladm_get_linkprop_db(dladm_handle_t, datalink_id_t,
668 			    const char *, char **, uint_t *);
669 static dladm_status_t	i_dladm_walk_linkprop_priv_db(dladm_handle_t,
670 			    datalink_id_t, void *, int (*)(dladm_handle_t,
671 			    datalink_id_t, const char *, void *));
672 static dladm_status_t	i_dladm_set_single_prop(dladm_handle_t, datalink_id_t,
673 			    datalink_class_t, uint32_t, prop_desc_t *, char **,
674 			    uint_t, uint_t);
675 static dladm_status_t	i_dladm_set_linkprop(dladm_handle_t, datalink_id_t,
676 			    const char *, char **, uint_t, uint_t);
677 static dladm_status_t	i_dladm_getset_defval(dladm_handle_t, prop_desc_t *,
678 			    datalink_id_t, datalink_media_t, uint_t);
679 
680 /*
681  * Unfortunately, MAX_SCAN_SUPPORT_RATES is too small to allow all
682  * rates to be retrieved. However, we cannot increase it at this
683  * time because it will break binary compatibility with unbundled
684  * WiFi drivers and utilities. So for now we define an additional
685  * constant, MAX_SUPPORT_RATES, to allow all rates to be retrieved.
686  */
687 #define	MAX_SUPPORT_RATES	64
688 
689 #define	AP_ANCHOR	"[anchor]"
690 #define	AP_DELIMITER	'.'
691 
692 /* ARGSUSED */
693 static dladm_status_t
694 do_check_prop(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
695     char **prop_val, uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
696 {
697 	int		i, j;
698 
699 	for (j = 0; j < val_cnt; j++) {
700 		for (i = 0; i < pdp->pd_noptval; i++) {
701 			if (strcasecmp(prop_val[j],
702 			    pdp->pd_optval[i].vd_name) == 0) {
703 				break;
704 			}
705 		}
706 		if (i == pdp->pd_noptval)
707 			return (DLADM_STATUS_BADVAL);
708 
709 		(void) memcpy(&vdp[j], &pdp->pd_optval[i], sizeof (val_desc_t));
710 	}
711 	return (DLADM_STATUS_OK);
712 }
713 
714 static dladm_status_t
715 i_dladm_set_single_prop(dladm_handle_t handle, datalink_id_t linkid,
716     datalink_class_t class, uint32_t media, prop_desc_t *pdp, char **prop_val,
717     uint_t val_cnt, uint_t flags)
718 {
719 	dladm_status_t	status = DLADM_STATUS_OK;
720 	val_desc_t	*vdp = NULL;
721 	boolean_t	needfree = B_FALSE;
722 	uint_t		cnt, i;
723 
724 	if (!(pdp->pd_class & class))
725 		return (DLADM_STATUS_BADARG);
726 
727 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
728 		return (DLADM_STATUS_BADARG);
729 
730 	if ((flags & DLADM_OPT_PERSIST) && (pdp->pd_flags & PD_TEMPONLY))
731 		return (DLADM_STATUS_TEMPONLY);
732 
733 	if (!(flags & DLADM_OPT_ACTIVE))
734 		return (DLADM_STATUS_OK);
735 
736 	if (pdp->pd_set == NULL)
737 		return (DLADM_STATUS_PROPRDONLY);
738 
739 	if (prop_val != NULL) {
740 		vdp = malloc(sizeof (val_desc_t) * val_cnt);
741 		if (vdp == NULL)
742 			return (DLADM_STATUS_NOMEM);
743 
744 		if (pdp->pd_check != NULL) {
745 			needfree = ((pdp->pd_flags & PD_CHECK_ALLOC) != 0);
746 			status = pdp->pd_check(handle, pdp, linkid, prop_val,
747 			    val_cnt, vdp, media);
748 		} else if (pdp->pd_optval != NULL) {
749 			status = do_check_prop(handle, pdp, linkid, prop_val,
750 			    val_cnt, vdp, media);
751 		} else {
752 			status = DLADM_STATUS_BADARG;
753 		}
754 
755 		if (status != DLADM_STATUS_OK)
756 			goto done;
757 
758 		cnt = val_cnt;
759 	} else {
760 		boolean_t	defval = B_FALSE;
761 
762 		if (pdp->pd_defval.vd_name == NULL)
763 			return (DLADM_STATUS_NOTSUP);
764 
765 		cnt = 1;
766 		defval = (strlen(pdp->pd_defval.vd_name) > 0);
767 		if ((pdp->pd_flags & PD_CHECK_ALLOC) != 0 || defval) {
768 			if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
769 				return (DLADM_STATUS_NOMEM);
770 
771 			if (defval) {
772 				(void) memcpy(vdp, &pdp->pd_defval,
773 				    sizeof (val_desc_t));
774 			} else if (pdp->pd_check != NULL) {
775 				status = pdp->pd_check(handle, pdp, linkid,
776 				    prop_val, cnt, vdp, media);
777 				if (status != DLADM_STATUS_OK)
778 					goto done;
779 			}
780 		} else {
781 			status = i_dladm_getset_defval(handle, pdp, linkid,
782 			    media, flags);
783 			return (status);
784 		}
785 	}
786 	if (pdp->pd_flags & PD_AFTER_PERM)
787 		status = (flags & DLADM_OPT_PERSIST) ? DLADM_STATUS_OK :
788 		    DLADM_STATUS_PERMONLY;
789 	else
790 		status = pdp->pd_set(handle, pdp, linkid, vdp, cnt, flags,
791 		    media);
792 	if (needfree) {
793 		for (i = 0; i < cnt; i++)
794 			free((void *)((val_desc_t *)vdp + i)->vd_val);
795 	}
796 done:
797 	free(vdp);
798 	return (status);
799 }
800 
801 static dladm_status_t
802 i_dladm_set_linkprop(dladm_handle_t handle, datalink_id_t linkid,
803     const char *prop_name, char **prop_val, uint_t val_cnt, uint_t flags)
804 {
805 	int			i;
806 	boolean_t		found = B_FALSE;
807 	datalink_class_t	class;
808 	uint32_t		media;
809 	dladm_status_t		status = DLADM_STATUS_OK;
810 
811 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, &media,
812 	    NULL, 0);
813 	if (status != DLADM_STATUS_OK)
814 		return (status);
815 
816 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
817 		prop_desc_t	*pdp = &prop_table[i];
818 		dladm_status_t	s;
819 
820 		if (prop_name != NULL &&
821 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
822 			continue;
823 		found = B_TRUE;
824 		s = i_dladm_set_single_prop(handle, linkid, class, media, pdp,
825 		    prop_val, val_cnt, flags);
826 
827 		if (prop_name != NULL) {
828 			status = s;
829 			break;
830 		} else {
831 			if (s != DLADM_STATUS_OK &&
832 			    s != DLADM_STATUS_NOTSUP)
833 				status = s;
834 		}
835 	}
836 	if (!found) {
837 		if (prop_name[0] == '_') {
838 			/* other private properties */
839 			status = i_dladm_set_private_prop(handle, linkid,
840 			    prop_name, prop_val, val_cnt, flags);
841 		} else  {
842 			status = DLADM_STATUS_NOTFOUND;
843 		}
844 	}
845 
846 	return (status);
847 }
848 
849 /*
850  * Set/reset link property for specific link
851  */
852 dladm_status_t
853 dladm_set_linkprop(dladm_handle_t handle, datalink_id_t linkid,
854     const char *prop_name, char **prop_val, uint_t val_cnt, uint_t flags)
855 {
856 	dladm_status_t	status = DLADM_STATUS_OK;
857 
858 	if ((linkid == DATALINK_INVALID_LINKID) || (flags == 0) ||
859 	    (prop_val == NULL && val_cnt > 0) ||
860 	    (prop_val != NULL && val_cnt == 0) ||
861 	    (prop_name == NULL && prop_val != NULL)) {
862 		return (DLADM_STATUS_BADARG);
863 	}
864 
865 	/*
866 	 * Check for valid link property against the flags passed
867 	 * and set the link property when active flag is passed.
868 	 */
869 	status = i_dladm_set_linkprop(handle, linkid, prop_name, prop_val,
870 	    val_cnt, flags);
871 	if (status != DLADM_STATUS_OK)
872 		return (status);
873 
874 	if (flags & DLADM_OPT_PERSIST) {
875 		status = i_dladm_set_linkprop_db(handle, linkid, prop_name,
876 		    prop_val, val_cnt);
877 
878 		if (status == DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) {
879 			prop_desc_t *pdp = prop_table;
880 			int i;
881 
882 			for (i = 0; i < DLADM_MAX_PROPS; i++, pdp++) {
883 				if (!(pdp->pd_flags & PD_AFTER_PERM))
884 					continue;
885 				if (prop_name != NULL &&
886 				    strcasecmp(prop_name, pdp->pd_name) != 0)
887 					continue;
888 				status = pdp->pd_set(handle, pdp, linkid, NULL,
889 				    0, flags, 0);
890 			}
891 		}
892 	}
893 	return (status);
894 }
895 
896 /*
897  * Walk all link properties of the given specific link.
898  *
899  * Note: this function currently lacks the ability to walk _all_ private
900  * properties if the link, because there is no kernel interface to
901  * retrieve all known private property names. Once such an interface
902  * is added, this function should be fixed accordingly.
903  */
904 dladm_status_t
905 dladm_walk_linkprop(dladm_handle_t handle, datalink_id_t linkid, void *arg,
906     int (*func)(dladm_handle_t, datalink_id_t, const char *, void *))
907 {
908 	dladm_status_t		status;
909 	datalink_class_t	class;
910 	uint_t			media;
911 	int			i;
912 
913 	if (linkid == DATALINK_INVALID_LINKID || func == NULL)
914 		return (DLADM_STATUS_BADARG);
915 
916 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, &media,
917 	    NULL, 0);
918 	if (status != DLADM_STATUS_OK)
919 		return (status);
920 
921 	/* public */
922 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
923 		if (!(prop_table[i].pd_class & class))
924 			continue;
925 
926 		if (!DATALINK_MEDIA_ACCEPTED(prop_table[i].pd_dmedia, media))
927 			continue;
928 
929 		if (func(handle, linkid, prop_table[i].pd_name, arg) ==
930 		    DLADM_WALK_TERMINATE) {
931 			break;
932 		}
933 	}
934 
935 	/* private */
936 	status = i_dladm_walk_linkprop_priv_db(handle, linkid, arg, func);
937 
938 	return (status);
939 }
940 
941 /*
942  * Get linkprop of the given specific link.
943  */
944 dladm_status_t
945 dladm_get_linkprop(dladm_handle_t handle, datalink_id_t linkid,
946     dladm_prop_type_t type, const char *prop_name, char **prop_val,
947     uint_t *val_cntp)
948 {
949 	dladm_status_t		status = DLADM_STATUS_OK;
950 	datalink_class_t	class;
951 	uint_t			media;
952 	prop_desc_t		*pdp;
953 	uint_t			cnt, dld_flags = 0;
954 	int			i;
955 	uint_t			perm_flags;
956 
957 	if (type == DLADM_PROP_VAL_DEFAULT)
958 		dld_flags |= MAC_PROP_DEFAULT;
959 	else if (type == DLADM_PROP_VAL_MODIFIABLE)
960 		dld_flags |= MAC_PROP_POSSIBLE;
961 
962 	if (linkid == DATALINK_INVALID_LINKID || prop_name == NULL ||
963 	    prop_val == NULL || val_cntp == NULL || *val_cntp == 0)
964 		return (DLADM_STATUS_BADARG);
965 
966 	for (i = 0; i < DLADM_MAX_PROPS; i++)
967 		if (strcasecmp(prop_name, prop_table[i].pd_name) == 0)
968 			break;
969 
970 	if (i == DLADM_MAX_PROPS) {
971 		if (prop_name[0] == '_') {
972 			/*
973 			 * private property.
974 			 */
975 			if (type == DLADM_PROP_VAL_PERSISTENT)
976 				return (i_dladm_get_linkprop_db(handle, linkid,
977 				    prop_name, prop_val, val_cntp));
978 			else
979 				return (i_dladm_get_priv_prop(handle, linkid,
980 				    prop_name, prop_val, val_cntp, type,
981 				    dld_flags));
982 		} else {
983 			return (DLADM_STATUS_NOTFOUND);
984 		}
985 	}
986 
987 	pdp = &prop_table[i];
988 
989 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, &media,
990 	    NULL, 0);
991 	if (status != DLADM_STATUS_OK)
992 		return (status);
993 
994 	if (!(pdp->pd_class & class))
995 		return (DLADM_STATUS_BADARG);
996 
997 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
998 		return (DLADM_STATUS_BADARG);
999 
1000 	switch (type) {
1001 	case DLADM_PROP_VAL_CURRENT:
1002 		status = pdp->pd_get(handle, pdp, linkid, prop_val, val_cntp,
1003 		    media, dld_flags, &perm_flags);
1004 		break;
1005 
1006 	case DLADM_PROP_VAL_PERM:
1007 		if (pdp->pd_set == NULL) {
1008 			perm_flags = MAC_PROP_PERM_READ;
1009 		} else {
1010 			status = pdp->pd_get(handle, pdp, linkid, prop_val,
1011 			    val_cntp, media, dld_flags, &perm_flags);
1012 		}
1013 
1014 		*prop_val[0] = '\0';
1015 		*val_cntp = 1;
1016 		if (status == DLADM_STATUS_OK)
1017 			(void) dladm_perm2str(perm_flags, *prop_val);
1018 		break;
1019 
1020 	case DLADM_PROP_VAL_DEFAULT:
1021 		/*
1022 		 * If defaults are not defined for the property,
1023 		 * pd_defval.vd_name should be null. If the driver
1024 		 * has to be contacted for the value, vd_name should
1025 		 * be the empty string (""). Otherwise, dladm will
1026 		 * just print whatever is in the table.
1027 		 */
1028 		if (pdp->pd_defval.vd_name == NULL) {
1029 			status = DLADM_STATUS_NOTSUP;
1030 			break;
1031 		}
1032 
1033 		if (strlen(pdp->pd_defval.vd_name) == 0) {
1034 			status = pdp->pd_get(handle, pdp, linkid, prop_val,
1035 			    val_cntp, media, dld_flags, &perm_flags);
1036 		} else {
1037 			(void) strcpy(*prop_val, pdp->pd_defval.vd_name);
1038 		}
1039 		*val_cntp = 1;
1040 		break;
1041 
1042 	case DLADM_PROP_VAL_MODIFIABLE:
1043 		if (pdp->pd_getmod != NULL) {
1044 			status = pdp->pd_getmod(handle, pdp, linkid, prop_val,
1045 			    val_cntp, media, dld_flags, &perm_flags);
1046 			break;
1047 		}
1048 		cnt = pdp->pd_noptval;
1049 		if (cnt == 0) {
1050 			status = DLADM_STATUS_NOTSUP;
1051 		} else if (cnt > *val_cntp) {
1052 			status = DLADM_STATUS_TOOSMALL;
1053 		} else {
1054 			for (i = 0; i < cnt; i++) {
1055 				(void) strcpy(prop_val[i],
1056 				    pdp->pd_optval[i].vd_name);
1057 			}
1058 			*val_cntp = cnt;
1059 		}
1060 		break;
1061 	case DLADM_PROP_VAL_PERSISTENT:
1062 		if (pdp->pd_flags & PD_TEMPONLY)
1063 			return (DLADM_STATUS_TEMPONLY);
1064 		status = i_dladm_get_linkprop_db(handle, linkid, prop_name,
1065 		    prop_val, val_cntp);
1066 		break;
1067 	default:
1068 		status = DLADM_STATUS_BADARG;
1069 		break;
1070 	}
1071 
1072 	return (status);
1073 }
1074 
1075 /*
1076  * Get linkprop of the given specific link and run any possible conversion
1077  * of the values using the check function for the property. Fails if the
1078  * check function doesn't succeed for the property value.
1079  */
1080 dladm_status_t
1081 dladm_get_linkprop_values(dladm_handle_t handle, datalink_id_t linkid,
1082     dladm_prop_type_t type, const char *prop_name, uint_t *ret_val,
1083     uint_t *val_cntp)
1084 {
1085 	dladm_status_t		status;
1086 	datalink_class_t	class;
1087 	uint_t			media;
1088 	prop_desc_t		*pdp;
1089 	uint_t			dld_flags;
1090 	int			valc, i;
1091 	char			**prop_val;
1092 	uint_t			perm_flags;
1093 
1094 	if (linkid == DATALINK_INVALID_LINKID || prop_name == NULL ||
1095 	    ret_val == NULL || val_cntp == NULL || *val_cntp == 0)
1096 		return (DLADM_STATUS_BADARG);
1097 
1098 	for (pdp = prop_table; pdp < prop_table + DLADM_MAX_PROPS; pdp++)
1099 		if (strcasecmp(prop_name, pdp->pd_name) == 0)
1100 			break;
1101 
1102 	if (pdp == prop_table + DLADM_MAX_PROPS)
1103 		return (DLADM_STATUS_NOTFOUND);
1104 
1105 	if (pdp->pd_flags & PD_CHECK_ALLOC)
1106 		return (DLADM_STATUS_BADARG);
1107 
1108 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, &media,
1109 	    NULL, 0);
1110 	if (status != DLADM_STATUS_OK)
1111 		return (status);
1112 
1113 	if (!(pdp->pd_class & class))
1114 		return (DLADM_STATUS_BADARG);
1115 
1116 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
1117 		return (DLADM_STATUS_BADARG);
1118 
1119 	prop_val = malloc(*val_cntp * sizeof (*prop_val) +
1120 	    *val_cntp * DLADM_PROP_VAL_MAX);
1121 	if (prop_val == NULL)
1122 		return (DLADM_STATUS_NOMEM);
1123 	for (valc = 0; valc < *val_cntp; valc++)
1124 		prop_val[valc] = (char *)(prop_val + *val_cntp) +
1125 		    valc * DLADM_PROP_VAL_MAX;
1126 
1127 	dld_flags = (type == DLADM_PROP_VAL_DEFAULT) ? MAC_PROP_DEFAULT : 0;
1128 
1129 	switch (type) {
1130 	case DLADM_PROP_VAL_CURRENT:
1131 		status = pdp->pd_get(handle, pdp, linkid, prop_val, val_cntp,
1132 		    media, dld_flags, &perm_flags);
1133 		break;
1134 
1135 	case DLADM_PROP_VAL_DEFAULT:
1136 		/*
1137 		 * If defaults are not defined for the property,
1138 		 * pd_defval.vd_name should be null. If the driver
1139 		 * has to be contacted for the value, vd_name should
1140 		 * be the empty string (""). Otherwise, dladm will
1141 		 * just print whatever is in the table.
1142 		 */
1143 		if (pdp->pd_defval.vd_name == NULL) {
1144 			status = DLADM_STATUS_NOTSUP;
1145 			break;
1146 		}
1147 
1148 		if (pdp->pd_defval.vd_name[0] != '\0') {
1149 			*val_cntp = 1;
1150 			*ret_val = pdp->pd_defval.vd_val;
1151 			free(prop_val);
1152 			return (DLADM_STATUS_OK);
1153 		}
1154 		status = pdp->pd_get(handle, pdp, linkid, prop_val, val_cntp,
1155 		    media, dld_flags, &perm_flags);
1156 		break;
1157 
1158 	case DLADM_PROP_VAL_PERSISTENT:
1159 		if (pdp->pd_flags & PD_TEMPONLY)
1160 			status = DLADM_STATUS_TEMPONLY;
1161 		else
1162 			status = i_dladm_get_linkprop_db(handle, linkid,
1163 			    prop_name, prop_val, val_cntp);
1164 		break;
1165 
1166 	default:
1167 		status = DLADM_STATUS_BADARG;
1168 		break;
1169 	}
1170 
1171 	if (status == DLADM_STATUS_OK) {
1172 		if (pdp->pd_check != NULL) {
1173 			val_desc_t *vdp;
1174 
1175 			vdp = malloc(sizeof (val_desc_t) * *val_cntp);
1176 			if (vdp == NULL)
1177 				status = DLADM_STATUS_NOMEM;
1178 			else
1179 				status = pdp->pd_check(handle, pdp, linkid,
1180 				    prop_val, *val_cntp, vdp, media);
1181 			if (status == DLADM_STATUS_OK) {
1182 				for (valc = 0; valc < *val_cntp; valc++)
1183 					ret_val[valc] = vdp[valc].vd_val;
1184 			}
1185 			free(vdp);
1186 		} else {
1187 			for (valc = 0; valc < *val_cntp; valc++) {
1188 				for (i = 0; i < pdp->pd_noptval; i++) {
1189 					if (strcmp(pdp->pd_optval[i].vd_name,
1190 					    prop_val[valc]) == 0) {
1191 						ret_val[valc] =
1192 						    pdp->pd_optval[i].vd_val;
1193 						break;
1194 					}
1195 				}
1196 				if (i == pdp->pd_noptval) {
1197 					status = DLADM_STATUS_FAILED;
1198 					break;
1199 				}
1200 			}
1201 		}
1202 	}
1203 
1204 	free(prop_val);
1205 
1206 	return (status);
1207 }
1208 
1209 /*ARGSUSED*/
1210 static int
1211 i_dladm_init_one_prop(dladm_handle_t handle, datalink_id_t linkid,
1212     const char *prop_name, void *arg)
1213 {
1214 	char		*buf, **propvals;
1215 	uint_t		i, valcnt = DLADM_MAX_PROP_VALCNT;
1216 	dladm_status_t	status, *retval = arg;
1217 
1218 	if ((buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
1219 	    DLADM_MAX_PROP_VALCNT)) == NULL) {
1220 		return (DLADM_WALK_CONTINUE);
1221 	}
1222 
1223 	propvals = (char **)(void *)buf;
1224 	for (i = 0; i < valcnt; i++) {
1225 		propvals[i] = buf +
1226 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
1227 		    i * DLADM_PROP_VAL_MAX;
1228 	}
1229 
1230 	if (dladm_get_linkprop(handle, linkid, DLADM_PROP_VAL_PERSISTENT,
1231 	    prop_name, propvals, &valcnt) != DLADM_STATUS_OK) {
1232 		goto done;
1233 	}
1234 
1235 	status = dladm_set_linkprop(handle, linkid, prop_name, propvals,
1236 	    valcnt, DLADM_OPT_ACTIVE);
1237 	if (status != DLADM_STATUS_OK)
1238 		*retval = status;
1239 
1240 done:
1241 	if (buf != NULL)
1242 		free(buf);
1243 
1244 	return (DLADM_WALK_CONTINUE);
1245 }
1246 
1247 /*ARGSUSED*/
1248 static int
1249 i_dladm_init_linkprop(dladm_handle_t handle, datalink_id_t linkid, void *arg)
1250 {
1251 	datalink_class_t	class;
1252 	dladm_status_t		status;
1253 
1254 	status = dladm_datalink_id2info(handle, linkid, NULL, &class, NULL,
1255 	    NULL, 0);
1256 	if (status != DLADM_STATUS_OK)
1257 		return (DLADM_WALK_TERMINATE);
1258 
1259 	if ((class & (DATALINK_CLASS_VNIC | DATALINK_CLASS_VLAN)) == 0)
1260 		(void) dladm_init_linkprop(handle, linkid, B_TRUE);
1261 
1262 	return (DLADM_WALK_CONTINUE);
1263 }
1264 
1265 dladm_status_t
1266 dladm_init_linkprop(dladm_handle_t handle, datalink_id_t linkid,
1267     boolean_t any_media)
1268 {
1269 	dladm_status_t		status = DLADM_STATUS_OK;
1270 	datalink_media_t	dmedia;
1271 	uint32_t		media;
1272 
1273 	dmedia = any_media ? DATALINK_ANY_MEDIATYPE : DL_WIFI;
1274 
1275 	if (linkid == DATALINK_ALL_LINKID) {
1276 		(void) dladm_walk_datalink_id(i_dladm_init_linkprop, handle,
1277 		    NULL, DATALINK_CLASS_ALL, dmedia, DLADM_OPT_PERSIST);
1278 	} else if (any_media ||
1279 	    ((dladm_datalink_id2info(handle, linkid, NULL, NULL, &media, NULL,
1280 	    0) == DLADM_STATUS_OK) &&
1281 	    DATALINK_MEDIA_ACCEPTED(dmedia, media))) {
1282 		(void) dladm_walk_linkprop(handle, linkid, &status,
1283 		    i_dladm_init_one_prop);
1284 	}
1285 	return (status);
1286 }
1287 
1288 /* ARGSUSED */
1289 static dladm_status_t
1290 do_get_zone(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1291     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1292     uint_t flags, uint_t *perm_flags)
1293 {
1294 	char			zone_name[ZONENAME_MAX];
1295 	zoneid_t		zid;
1296 	dladm_status_t		status;
1297 	char			*cp;
1298 	dld_ioc_macprop_t	*dip;
1299 
1300 	if (flags != 0)
1301 		return (DLADM_STATUS_NOTSUP);
1302 
1303 	dip = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
1304 	    &status, perm_flags);
1305 	if (status != DLADM_STATUS_OK)
1306 		return (status);
1307 
1308 	cp = dip->pr_val;
1309 	(void) memcpy(&zid, cp, sizeof (zid));
1310 	free(dip);
1311 
1312 	*val_cnt = 1;
1313 	if (zid != GLOBAL_ZONEID) {
1314 		if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0) {
1315 			return (dladm_errno2status(errno));
1316 		}
1317 
1318 		(void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX);
1319 	} else {
1320 		*prop_val[0] = '\0';
1321 	}
1322 
1323 	return (DLADM_STATUS_OK);
1324 }
1325 
1326 typedef int (*zone_get_devroot_t)(char *, char *, size_t);
1327 
1328 static int
1329 i_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen)
1330 {
1331 	char			root[MAXPATHLEN];
1332 	zone_get_devroot_t	real_zone_get_devroot;
1333 	void			*dlhandle;
1334 	void			*sym;
1335 	int			ret;
1336 
1337 	if ((dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY)) == NULL)
1338 		return (-1);
1339 
1340 	if ((sym = dlsym(dlhandle, "zone_get_devroot")) == NULL) {
1341 		(void) dlclose(dlhandle);
1342 		return (-1);
1343 	}
1344 
1345 	real_zone_get_devroot = (zone_get_devroot_t)sym;
1346 
1347 	if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0)
1348 		(void) snprintf(dev, devlen, "%s%s", root, "/dev");
1349 	(void) dlclose(dlhandle);
1350 	return (ret);
1351 }
1352 
1353 static dladm_status_t
1354 i_dladm_update_deventry(dladm_handle_t handle, zoneid_t zid,
1355     datalink_id_t linkid, boolean_t add)
1356 {
1357 	char		path[MAXPATHLEN];
1358 	char		name[MAXLINKNAMELEN];
1359 	di_prof_t	prof = NULL;
1360 	char		zone_name[ZONENAME_MAX];
1361 	dladm_status_t	status;
1362 	int		ret;
1363 
1364 	if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
1365 		return (dladm_errno2status(errno));
1366 	if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
1367 		return (dladm_errno2status(errno));
1368 	if (di_prof_init(path, &prof) != 0)
1369 		return (dladm_errno2status(errno));
1370 
1371 	status = dladm_linkid2legacyname(handle, linkid, name, MAXLINKNAMELEN);
1372 	if (status != DLADM_STATUS_OK)
1373 		goto cleanup;
1374 
1375 	if (add)
1376 		ret = di_prof_add_dev(prof, name);
1377 	else
1378 		ret = di_prof_add_exclude(prof, name);
1379 
1380 	if (ret != 0) {
1381 		status = dladm_errno2status(errno);
1382 		goto cleanup;
1383 	}
1384 
1385 	if (di_prof_commit(prof) != 0)
1386 		status = dladm_errno2status(errno);
1387 cleanup:
1388 	if (prof)
1389 		di_prof_fini(prof);
1390 
1391 	return (status);
1392 }
1393 
1394 /* ARGSUSED */
1395 static dladm_status_t
1396 do_set_zone(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1397     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
1398 {
1399 	dladm_status_t		status = DLADM_STATUS_OK;
1400 	zoneid_t		zid_old, zid_new;
1401 	char			*cp;
1402 	dld_ioc_macprop_t	*dip;
1403 	dld_ioc_zid_t		*dzp;
1404 
1405 	if (val_cnt != 1)
1406 		return (DLADM_STATUS_BADVALCNT);
1407 
1408 	dzp = (dld_ioc_zid_t *)vdp->vd_val;
1409 
1410 	dip = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
1411 	    &status, NULL);
1412 	if (status != DLADM_STATUS_OK)
1413 		return (status);
1414 
1415 	cp = dip->pr_val;
1416 	(void) memcpy(&zid_old, cp, sizeof (zid_old));
1417 	free(dip);
1418 
1419 	zid_new = dzp->diz_zid;
1420 	if (zid_new == zid_old)
1421 		return (DLADM_STATUS_OK);
1422 
1423 	if ((status = i_dladm_set_public_prop(handle, pdp, linkid, vdp, val_cnt,
1424 	    flags, media)) != DLADM_STATUS_OK)
1425 		return (status);
1426 
1427 	/*
1428 	 * It is okay to fail to update the /dev entry (some vanity-named
1429 	 * links do not have a /dev entry).
1430 	 */
1431 	if (zid_old != GLOBAL_ZONEID) {
1432 		(void) i_dladm_update_deventry(handle, zid_old, linkid,
1433 		    B_FALSE);
1434 	}
1435 	if (zid_new != GLOBAL_ZONEID)
1436 		(void) i_dladm_update_deventry(handle, zid_new, linkid, B_TRUE);
1437 
1438 	return (DLADM_STATUS_OK);
1439 }
1440 
1441 /* ARGSUSED */
1442 static dladm_status_t
1443 do_check_zone(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1444     char **prop_val, uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
1445 {
1446 	char		*zone_name;
1447 	zoneid_t	zoneid;
1448 	dladm_status_t	status = DLADM_STATUS_OK;
1449 	dld_ioc_zid_t	*dzp;
1450 
1451 	if (val_cnt != 1)
1452 		return (DLADM_STATUS_BADVALCNT);
1453 
1454 	dzp = malloc(sizeof (dld_ioc_zid_t));
1455 	if (dzp == NULL)
1456 		return (DLADM_STATUS_NOMEM);
1457 
1458 	zone_name = (prop_val != NULL) ? *prop_val : GLOBAL_ZONENAME;
1459 	if ((zoneid = getzoneidbyname(zone_name)) == -1) {
1460 		status = DLADM_STATUS_BADVAL;
1461 		goto done;
1462 	}
1463 
1464 	if (zoneid != GLOBAL_ZONEID) {
1465 		ushort_t	flags;
1466 
1467 		if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &flags,
1468 		    sizeof (flags)) < 0) {
1469 			status = dladm_errno2status(errno);
1470 			goto done;
1471 		}
1472 
1473 		if (!(flags & ZF_NET_EXCL)) {
1474 			status = DLADM_STATUS_BADVAL;
1475 			goto done;
1476 		}
1477 	}
1478 
1479 	(void) memset(dzp, 0, sizeof (dld_ioc_zid_t));
1480 
1481 	dzp->diz_zid = zoneid;
1482 	dzp->diz_linkid = linkid;
1483 
1484 	vdp->vd_val = (uintptr_t)dzp;
1485 	return (DLADM_STATUS_OK);
1486 done:
1487 	free(dzp);
1488 	return (status);
1489 }
1490 
1491 /* ARGSUSED */
1492 static dladm_status_t
1493 i_dladm_maxbw_get(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1494     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1495     uint_t flags, uint_t *perm_flags)
1496 {
1497 	dld_ioc_macprop_t	*dip;
1498 	mac_resource_props_t	mrp;
1499 	dladm_status_t		status;
1500 
1501 	dip = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
1502 	    &status, perm_flags);
1503 	if (dip == NULL)
1504 		return (status);
1505 
1506 	bcopy(dip->pr_val, &mrp, sizeof (mac_resource_props_t));
1507 	free(dip);
1508 
1509 	if ((mrp.mrp_mask & MRP_MAXBW) == 0) {
1510 		(*prop_val)[0] = '\0';
1511 	} else {
1512 		(void) dladm_bw2str(mrp.mrp_maxbw, prop_val[0]);
1513 	}
1514 	*val_cnt = 1;
1515 	return (DLADM_STATUS_OK);
1516 }
1517 
1518 /* ARGSUSED */
1519 static dladm_status_t
1520 do_check_maxbw(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1521     char **prop_val, uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
1522 {
1523 	uint64_t	*maxbw;
1524 	dladm_status_t	status = DLADM_STATUS_OK;
1525 
1526 	if (val_cnt != 1)
1527 		return (DLADM_STATUS_BADVALCNT);
1528 
1529 	maxbw = malloc(sizeof (uint64_t));
1530 	if (maxbw == NULL)
1531 		return (DLADM_STATUS_NOMEM);
1532 
1533 	status = dladm_str2bw(*prop_val, maxbw);
1534 	if (status != DLADM_STATUS_OK) {
1535 		free(maxbw);
1536 		return (status);
1537 	}
1538 
1539 	if ((*maxbw < MRP_MAXBW_MINVAL) && (*maxbw != 0)) {
1540 		free(maxbw);
1541 		return (DLADM_STATUS_MINMAXBW);
1542 	}
1543 
1544 	vdp->vd_val = (uintptr_t)maxbw;
1545 	return (DLADM_STATUS_OK);
1546 }
1547 
1548 /* ARGSUSED */
1549 dladm_status_t
1550 do_extract_maxbw(val_desc_t *vdp, uint_t cnt, void *arg)
1551 {
1552 	mac_resource_props_t *mrp = arg;
1553 
1554 	bcopy((char *)vdp->vd_val, &mrp->mrp_maxbw, sizeof (uint64_t));
1555 	mrp->mrp_mask |= MRP_MAXBW;
1556 
1557 	return (DLADM_STATUS_OK);
1558 }
1559 
1560 /* ARGSUSED */
1561 static dladm_status_t
1562 i_dladm_cpus_get(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1563     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1564     uint_t flags, uint_t *perm_flags)
1565 {
1566 	dld_ioc_macprop_t	*dip;
1567 	mac_resource_props_t	mrp;
1568 	int			i;
1569 	uint32_t		ncpus;
1570 	uchar_t			*cp;
1571 	dladm_status_t		status;
1572 
1573 	dip = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
1574 	    &status, perm_flags);
1575 	if (dip == NULL)
1576 		return (status);
1577 
1578 	cp = (uchar_t *)dip->pr_val;
1579 	(void) memcpy(&mrp, cp, sizeof (mac_resource_props_t));
1580 	free(dip);
1581 
1582 	ncpus = mrp.mrp_ncpus;
1583 
1584 	if (ncpus > *val_cnt)
1585 		return (DLADM_STATUS_TOOSMALL);
1586 
1587 	if (ncpus == 0) {
1588 		(*prop_val)[0] = '\0';
1589 		*val_cnt = 1;
1590 		return (DLADM_STATUS_OK);
1591 	}
1592 
1593 	*val_cnt = ncpus;
1594 	for (i = 0; i < ncpus; i++) {
1595 		(void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
1596 		    "%u", mrp.mrp_cpu[i]);
1597 	}
1598 	return (DLADM_STATUS_OK);
1599 }
1600 
1601 /* ARGSUSED */
1602 static dladm_status_t
1603 do_set_res(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1604     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
1605 {
1606 	mac_resource_props_t	mrp;
1607 	dladm_status_t		status = DLADM_STATUS_OK;
1608 	dld_ioc_macprop_t	*dip;
1609 
1610 	bzero(&mrp, sizeof (mac_resource_props_t));
1611 	dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name,
1612 	    flags, &status);
1613 
1614 	if (dip == NULL)
1615 		return (status);
1616 
1617 	if (vdp->vd_val == RESET_VAL) {
1618 		switch (dip->pr_num) {
1619 		case MAC_PROP_MAXBW:
1620 			mrp.mrp_maxbw = MRP_MAXBW_RESETVAL;
1621 			mrp.mrp_mask = MRP_MAXBW;
1622 			break;
1623 		case MAC_PROP_PRIO:
1624 			mrp.mrp_priority = MPL_RESET;
1625 			mrp.mrp_mask = MRP_PRIORITY;
1626 			break;
1627 		default:
1628 			free(dip);
1629 			return (DLADM_STATUS_BADARG);
1630 		}
1631 	} else {
1632 		switch (dip->pr_num) {
1633 		case MAC_PROP_MAXBW:
1634 			bcopy((void *)vdp->vd_val, &mrp.mrp_maxbw,
1635 			    sizeof (uint64_t));
1636 			mrp.mrp_mask = MRP_MAXBW;
1637 			break;
1638 		case MAC_PROP_PRIO:
1639 			bcopy((void *)vdp->vd_val, &mrp.mrp_priority,
1640 			    sizeof (mac_priority_level_t));
1641 			mrp.mrp_mask = MRP_PRIORITY;
1642 			break;
1643 		default:
1644 			free(dip);
1645 			return (DLADM_STATUS_BADARG);
1646 		}
1647 	}
1648 
1649 	(void) memcpy(dip->pr_val, &mrp, dip->pr_valsize);
1650 	status = i_dladm_macprop(handle, dip, B_TRUE);
1651 	free(dip);
1652 	return (status);
1653 }
1654 
1655 /* ARGSUSED */
1656 static dladm_status_t
1657 do_set_cpus(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1658     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
1659 {
1660 	mac_resource_props_t	mrp;
1661 	dladm_status_t		status;
1662 	dld_ioc_macprop_t	*dip;
1663 	datalink_class_t	class;
1664 
1665 	/*
1666 	 * CPU bindings can be set on VNIC and regular physical links.
1667 	 * However VNICs fails the dladm_phys_info test(). So apply
1668 	 * the phys_info test only on physical links.
1669 	 */
1670 	if ((status = dladm_datalink_id2info(handle, linkid, NULL, &class,
1671 	    NULL, NULL, 0)) != DLADM_STATUS_OK) {
1672 		return (status);
1673 	}
1674 
1675 	/*
1676 	 * We set intr_cpu to -1. The interrupt will be retargetted,
1677 	 * if possible when the setup is complete in MAC.
1678 	 */
1679 	bzero(&mrp, sizeof (mac_resource_props_t));
1680 	mrp.mrp_mask = MRP_CPUS;
1681 	if (vdp != NULL && vdp->vd_val != RESET_VAL) {
1682 		mac_resource_props_t	*vmrp;
1683 
1684 		vmrp = (mac_resource_props_t *)vdp->vd_val;
1685 		if (vmrp->mrp_ncpus > 0) {
1686 			bcopy(vmrp, &mrp, sizeof (mac_resource_props_t));
1687 			mrp.mrp_mask = MRP_CPUS;
1688 		}
1689 		mrp.mrp_mask |= MRP_CPUS_USERSPEC;
1690 		mrp.mrp_fanout_mode = MCM_CPUS;
1691 		mrp.mrp_intr_cpu = -1;
1692 	}
1693 
1694 	dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name,
1695 	    flags, &status);
1696 	if (dip == NULL)
1697 		return (status);
1698 
1699 	(void) memcpy(dip->pr_val, &mrp, dip->pr_valsize);
1700 	status = i_dladm_macprop(handle, dip, B_TRUE);
1701 	free(dip);
1702 	return (status);
1703 }
1704 
1705 /* ARGSUSED */
1706 static dladm_status_t
1707 do_check_cpus(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1708     char **prop_val, uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
1709 {
1710 	uint32_t		cpuid;
1711 	int			i, j, rc;
1712 	long			nproc = sysconf(_SC_NPROCESSORS_CONF);
1713 	mac_resource_props_t	*mrp;
1714 
1715 	mrp = malloc(sizeof (mac_resource_props_t));
1716 	if (mrp == NULL)
1717 		return (DLADM_STATUS_NOMEM);
1718 
1719 	for (i = 0; i < val_cnt; i++) {
1720 		errno = 0;
1721 		cpuid = strtol(prop_val[i], (char **)NULL, 10);
1722 		if (errno != 0 || cpuid >= nproc) {
1723 			free(mrp);
1724 			return (DLADM_STATUS_CPUMAX);
1725 		}
1726 		rc = p_online(cpuid, P_STATUS);
1727 		if (rc < 1) {
1728 			free(mrp);
1729 			return (DLADM_STATUS_CPUERR);
1730 		}
1731 		if (rc != P_ONLINE) {
1732 			free(mrp);
1733 			return (DLADM_STATUS_CPUNOTONLINE);
1734 		}
1735 		mrp->mrp_cpu[i] = cpuid;
1736 	}
1737 	mrp->mrp_ncpus = (uint32_t)val_cnt;
1738 
1739 	/* Check for duplicates */
1740 	for (i = 0; i < val_cnt; i++) {
1741 		for (j = 0; j < val_cnt; j++) {
1742 			if (i != j && mrp->mrp_cpu[i] == mrp->mrp_cpu[j]) {
1743 				free(mrp);
1744 				return (DLADM_STATUS_BADARG);
1745 			}
1746 		}
1747 	}
1748 	vdp->vd_val = (uintptr_t)mrp;
1749 
1750 	return (DLADM_STATUS_OK);
1751 }
1752 
1753 /* ARGSUSED */
1754 dladm_status_t
1755 do_extract_cpus(val_desc_t *vdp, uint_t cnt, void *arg)
1756 {
1757 	mac_resource_props_t	*mrp = arg;
1758 	mac_resource_props_t	*vmrp = (mac_resource_props_t *)vdp->vd_val;
1759 	int			i;
1760 
1761 	for (i = 0; i < vmrp->mrp_ncpus; i++) {
1762 		mrp->mrp_cpu[i] = vmrp->mrp_cpu[i];
1763 	}
1764 	mrp->mrp_ncpus = vmrp->mrp_ncpus;
1765 	mrp->mrp_mask |= (MRP_CPUS|MRP_CPUS_USERSPEC);
1766 	mrp->mrp_fanout_mode = MCM_CPUS;
1767 	mrp->mrp_intr_cpu = -1;
1768 
1769 	return (DLADM_STATUS_OK);
1770 }
1771 
1772 /* ARGSUSED */
1773 static dladm_status_t
1774 i_dladm_priority_get(dladm_handle_t handle, prop_desc_t *pdp,
1775     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
1776     datalink_media_t media, uint_t flags, uint_t *perm_flags)
1777 {
1778 	dld_ioc_macprop_t	*dip;
1779 	mac_resource_props_t	mrp;
1780 	mac_priority_level_t	pri;
1781 	dladm_status_t		status;
1782 
1783 	dip = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
1784 	    &status, perm_flags);
1785 	if (dip == NULL)
1786 		return (status);
1787 
1788 	bcopy(dip->pr_val, &mrp, sizeof (mac_resource_props_t));
1789 	free(dip);
1790 
1791 	pri = ((mrp.mrp_mask & MRP_PRIORITY) == 0) ? MPL_HIGH :
1792 	    mrp.mrp_priority;
1793 
1794 	(void) dladm_pri2str(pri, prop_val[0]);
1795 	*val_cnt = 1;
1796 	return (DLADM_STATUS_OK);
1797 }
1798 
1799 /* ARGSUSED */
1800 static dladm_status_t
1801 do_check_priority(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1802     char **prop_val, uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
1803 {
1804 	mac_priority_level_t	*pri;
1805 	dladm_status_t	status = DLADM_STATUS_OK;
1806 
1807 	if (val_cnt != 1)
1808 		return (DLADM_STATUS_BADVALCNT);
1809 
1810 	pri = malloc(sizeof (mac_priority_level_t));
1811 	if (pri == NULL)
1812 		return (DLADM_STATUS_NOMEM);
1813 
1814 	status = dladm_str2pri(*prop_val, pri);
1815 	if (status != DLADM_STATUS_OK) {
1816 		free(pri);
1817 		return (status);
1818 	}
1819 
1820 	if (*pri < MPL_LOW || *pri > MPL_HIGH) {
1821 		free(pri);
1822 		return (DLADM_STATUS_BADVAL);
1823 	}
1824 
1825 	vdp->vd_val = (uintptr_t)pri;
1826 	return (DLADM_STATUS_OK);
1827 }
1828 
1829 /* ARGSUSED */
1830 dladm_status_t
1831 do_extract_priority(val_desc_t *vdp, uint_t cnt, void *arg)
1832 {
1833 	mac_resource_props_t *mrp = arg;
1834 
1835 	bcopy((char *)vdp->vd_val, &mrp->mrp_priority,
1836 	    sizeof (mac_priority_level_t));
1837 	mrp->mrp_mask |= MRP_PRIORITY;
1838 
1839 	return (DLADM_STATUS_OK);
1840 }
1841 
1842 /* ARGSUSED */
1843 static dladm_status_t
1844 do_set_protection(dladm_handle_t handle, prop_desc_t *pdp,
1845     datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt,
1846     uint_t flags, datalink_media_t media)
1847 {
1848 	mac_resource_props_t	mrp;
1849 	dladm_status_t		status = DLADM_STATUS_OK;
1850 	dld_ioc_macprop_t	*dip;
1851 
1852 	bzero(&mrp, sizeof (mac_resource_props_t));
1853 	dip = i_dladm_buf_alloc_by_name(0, linkid, "protection",
1854 	    flags, &status);
1855 
1856 	if (dip == NULL)
1857 		return (status);
1858 
1859 	if (strcmp(pdp->pd_name, "protection") == 0) {
1860 		status = do_extract_protection(vdp, val_cnt, &mrp);
1861 		if (status != DLADM_STATUS_OK)
1862 			goto done;
1863 
1864 	} else if (strcmp(pdp->pd_name, "allowed-ips") == 0) {
1865 		status = do_extract_allowedips(vdp, val_cnt, &mrp);
1866 		if (status != DLADM_STATUS_OK)
1867 			goto done;
1868 	} else {
1869 		status = DLADM_STATUS_BADARG;
1870 		goto done;
1871 	}
1872 
1873 	(void) memcpy(dip->pr_val, &mrp, dip->pr_valsize);
1874 	status = i_dladm_macprop(handle, dip, B_TRUE);
1875 
1876 done:
1877 	free(dip);
1878 	return (status);
1879 }
1880 
1881 /* ARGSUSED */
1882 static dladm_status_t
1883 do_get_protection(dladm_handle_t handle, prop_desc_t *pdp,
1884     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
1885     datalink_media_t media, uint_t flags, uint_t *perm_flags)
1886 {
1887 	dld_ioc_macprop_t	*dip;
1888 	mac_resource_props_t	mrp;
1889 	mac_protect_t		*p;
1890 	dladm_status_t		status;
1891 	int			i;
1892 
1893 	dip = i_dladm_get_public_prop(handle, linkid, "protection", flags,
1894 	    &status, perm_flags);
1895 	if (dip == NULL)
1896 		return (status);
1897 
1898 	bcopy(dip->pr_val, &mrp, sizeof (mac_resource_props_t));
1899 	free(dip);
1900 
1901 	p = &mrp.mrp_protect;
1902 	if ((mrp.mrp_mask & MRP_PROTECT) != 0 &&
1903 	    strcmp(pdp->pd_name, "protection") == 0) {
1904 		uint32_t	cnt = 0, setbits[32];
1905 
1906 		dladm_find_setbits32(p->mp_types, setbits, &cnt);
1907 		if (cnt > *val_cnt)
1908 			return (DLADM_STATUS_BADVALCNT);
1909 
1910 		for (i = 0; i < cnt; i++)
1911 			(void) dladm_protect2str(setbits[i], prop_val[i]);
1912 
1913 		*val_cnt = cnt;
1914 		return (DLADM_STATUS_OK);
1915 	}
1916 
1917 	if (p->mp_ipaddrcnt > 0 &&
1918 	    strcmp(pdp->pd_name, "allowed-ips") == 0) {
1919 		if (p->mp_ipaddrcnt > *val_cnt)
1920 			return (DLADM_STATUS_BADVALCNT);
1921 
1922 		for (i = 0; i < p->mp_ipaddrcnt; i++) {
1923 			(void) dladm_ipv4addr2str(&p->mp_ipaddrs[i],
1924 			    prop_val[i]);
1925 		}
1926 		*val_cnt = p->mp_ipaddrcnt;
1927 		return (DLADM_STATUS_OK);
1928 	}
1929 
1930 	*val_cnt = 0;
1931 	return (DLADM_STATUS_OK);
1932 }
1933 
1934 dladm_status_t
1935 do_extract_protection(val_desc_t *vdp, uint_t cnt, void *arg)
1936 {
1937 	mac_resource_props_t	*mrp = arg;
1938 	uint32_t		types = 0;
1939 	int			i;
1940 
1941 	for (i = 0; i < cnt; i++)
1942 		types |= (uint32_t)vdp[i].vd_val;
1943 
1944 	mrp->mrp_protect.mp_types = types;
1945 	mrp->mrp_mask |= MRP_PROTECT;
1946 	return (DLADM_STATUS_OK);
1947 }
1948 
1949 dladm_status_t
1950 do_extract_allowedips(val_desc_t *vdp, uint_t cnt, void *arg)
1951 {
1952 	mac_resource_props_t	*mrp = arg;
1953 	mac_protect_t		*p = &mrp->mrp_protect;
1954 	int			i;
1955 
1956 	if (vdp->vd_val == 0) {
1957 		cnt = (uint_t)-1;
1958 	} else {
1959 		for (i = 0; i < cnt; i++)
1960 			p->mp_ipaddrs[i] = (ipaddr_t)vdp[i].vd_val;
1961 	}
1962 	p->mp_ipaddrcnt = cnt;
1963 	mrp->mrp_mask |= MRP_PROTECT;
1964 	return (DLADM_STATUS_OK);
1965 }
1966 
1967 /* ARGSUSED */
1968 static dladm_status_t
1969 do_check_allowedips(dladm_handle_t handle, prop_desc_t *pdp,
1970     datalink_id_t linkid, char **prop_val, uint_t val_cnt,
1971     val_desc_t *vdp, datalink_media_t media)
1972 {
1973 	dladm_status_t	status;
1974 	ipaddr_t	addr;
1975 	int		i;
1976 
1977 	if (val_cnt > MPT_MAXIPADDR)
1978 		return (DLADM_STATUS_BADVALCNT);
1979 
1980 	for (i = 0; i < val_cnt; i++) {
1981 		status = dladm_str2ipv4addr(prop_val[i], &addr);
1982 		if (status != DLADM_STATUS_OK)
1983 			return (status);
1984 
1985 		if (addr == 0)
1986 			return (DLADM_STATUS_BADVAL);
1987 
1988 		vdp[i].vd_val = (uintptr_t)addr;
1989 	}
1990 	return (DLADM_STATUS_OK);
1991 }
1992 
1993 /* ARGSUSED */
1994 static dladm_status_t
1995 do_get_autopush(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
1996     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1997     uint_t flags, uint_t *perm_flags)
1998 {
1999 	struct		dlautopush dlap;
2000 	int		i, len;
2001 	dladm_status_t	status;
2002 	dld_ioc_macprop_t	*dip;
2003 
2004 	if (flags & MAC_PROP_DEFAULT)
2005 		return (DLADM_STATUS_NOTDEFINED);
2006 
2007 	*val_cnt = 1;
2008 	dip = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
2009 	    &status, perm_flags);
2010 	if (dip == NULL) {
2011 		(*prop_val)[0] = '\0';
2012 		return (DLADM_STATUS_OK);
2013 	}
2014 	(void) memcpy(&dlap, dip->pr_val, sizeof (dlap));
2015 
2016 	for (i = 0, len = 0; i < dlap.dap_npush; i++) {
2017 		if (i != 0) {
2018 			(void) snprintf(*prop_val + len,
2019 			    DLADM_PROP_VAL_MAX - len, "%c", AP_DELIMITER);
2020 			len += 1;
2021 		}
2022 		(void) snprintf(*prop_val + len, DLADM_PROP_VAL_MAX - len,
2023 		    "%s", dlap.dap_aplist[i]);
2024 		len += strlen(dlap.dap_aplist[i]);
2025 		if (dlap.dap_anchor - 1 == i) {
2026 			(void) snprintf(*prop_val + len,
2027 			    DLADM_PROP_VAL_MAX - len, "%c%s", AP_DELIMITER,
2028 			    AP_ANCHOR);
2029 			len += (strlen(AP_ANCHOR) + 1);
2030 		}
2031 	}
2032 	free(dip);
2033 done:
2034 	return (DLADM_STATUS_OK);
2035 }
2036 
2037 /*
2038  * Add the specified module to the dlautopush structure; returns a
2039  * DLADM_STATUS_* code.
2040  */
2041 dladm_status_t
2042 i_dladm_add_ap_module(const char *module, struct dlautopush *dlap)
2043 {
2044 	if ((strlen(module) == 0) || (strlen(module) > FMNAMESZ))
2045 		return (DLADM_STATUS_BADVAL);
2046 
2047 	if (strncasecmp(module, AP_ANCHOR, strlen(AP_ANCHOR)) == 0) {
2048 		/*
2049 		 * We don't allow multiple anchors, and the anchor must
2050 		 * be after at least one module.
2051 		 */
2052 		if (dlap->dap_anchor != 0)
2053 			return (DLADM_STATUS_BADVAL);
2054 		if (dlap->dap_npush == 0)
2055 			return (DLADM_STATUS_BADVAL);
2056 
2057 		dlap->dap_anchor = dlap->dap_npush;
2058 		return (DLADM_STATUS_OK);
2059 	}
2060 	if (dlap->dap_npush >= MAXAPUSH)
2061 		return (DLADM_STATUS_BADVALCNT);
2062 
2063 	(void) strlcpy(dlap->dap_aplist[dlap->dap_npush++], module,
2064 	    FMNAMESZ + 1);
2065 
2066 	return (DLADM_STATUS_OK);
2067 }
2068 
2069 /*
2070  * Currently, both '.' and ' '(space) can be used as the delimiters between
2071  * autopush modules. The former is used in dladm set-linkprop, and the
2072  * latter is used in the autopush(1M) file.
2073  */
2074 /* ARGSUSED */
2075 static dladm_status_t
2076 do_check_autopush(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2077     char **prop_val, uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
2078 {
2079 	char			*module;
2080 	struct dlautopush	*dlap;
2081 	dladm_status_t		status;
2082 	char			val[DLADM_PROP_VAL_MAX];
2083 	char			delimiters[4];
2084 
2085 	if (val_cnt != 1)
2086 		return (DLADM_STATUS_BADVALCNT);
2087 
2088 	if (prop_val != NULL) {
2089 		dlap = malloc(sizeof (struct dlautopush));
2090 		if (dlap == NULL)
2091 			return (DLADM_STATUS_NOMEM);
2092 
2093 		(void) memset(dlap, 0, sizeof (struct dlautopush));
2094 		(void) snprintf(delimiters, 4, " %c\n", AP_DELIMITER);
2095 		bcopy(*prop_val, val, DLADM_PROP_VAL_MAX);
2096 		module = strtok(val, delimiters);
2097 		while (module != NULL) {
2098 			status = i_dladm_add_ap_module(module, dlap);
2099 			if (status != DLADM_STATUS_OK)
2100 				return (status);
2101 			module = strtok(NULL, delimiters);
2102 		}
2103 
2104 		vdp->vd_val = (uintptr_t)dlap;
2105 	} else {
2106 		vdp->vd_val = 0;
2107 	}
2108 	return (DLADM_STATUS_OK);
2109 }
2110 
2111 #define	WLDP_BUFSIZE (MAX_BUF_LEN - WIFI_BUF_OFFSET)
2112 
2113 /* ARGSUSED */
2114 static dladm_status_t
2115 do_get_rate_common(dladm_handle_t handle, prop_desc_t *pdp,
2116     datalink_id_t linkid, char **prop_val, uint_t *val_cnt, uint_t id,
2117     uint_t *perm_flags)
2118 {
2119 	wl_rates_t	*wrp;
2120 	uint_t		i;
2121 	dladm_status_t	status = DLADM_STATUS_OK;
2122 
2123 	wrp = malloc(WLDP_BUFSIZE);
2124 	if (wrp == NULL)
2125 		return (DLADM_STATUS_NOMEM);
2126 
2127 	status = i_dladm_wlan_param(handle, linkid, wrp, id, WLDP_BUFSIZE,
2128 	    B_FALSE);
2129 	if (status != DLADM_STATUS_OK)
2130 		goto done;
2131 
2132 	if (wrp->wl_rates_num > *val_cnt) {
2133 		status = DLADM_STATUS_TOOSMALL;
2134 		goto done;
2135 	}
2136 
2137 	if (wrp->wl_rates_rates[0] == 0) {
2138 		prop_val[0][0] = '\0';
2139 		*val_cnt = 1;
2140 		goto done;
2141 	}
2142 
2143 	for (i = 0; i < wrp->wl_rates_num; i++) {
2144 		(void) snprintf(prop_val[i], DLADM_STRSIZE, "%.*f",
2145 		    wrp->wl_rates_rates[i] % 2,
2146 		    (float)wrp->wl_rates_rates[i] / 2);
2147 	}
2148 	*val_cnt = wrp->wl_rates_num;
2149 	*perm_flags = MAC_PROP_PERM_RW;
2150 
2151 done:
2152 	free(wrp);
2153 	return (status);
2154 }
2155 
2156 static dladm_status_t
2157 do_get_rate_prop(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2158     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2159     uint_t flags, uint_t *perm_flags)
2160 {
2161 	if (media != DL_WIFI) {
2162 		return (i_dladm_speed_get(handle, pdp, linkid, prop_val,
2163 		    val_cnt, flags, perm_flags));
2164 	}
2165 
2166 	return (do_get_rate_common(handle, pdp, linkid, prop_val, val_cnt,
2167 	    MAC_PROP_WL_DESIRED_RATES, perm_flags));
2168 }
2169 
2170 /* ARGSUSED */
2171 static dladm_status_t
2172 do_get_rate_mod(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2173     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2174     uint_t flags, uint_t *perm_flags)
2175 {
2176 	switch (media) {
2177 	case DL_ETHER:
2178 		/*
2179 		 * Speed for ethernet links is unbounded. E.g., 802.11b
2180 		 * links can have a speed of 5.5 Gbps.
2181 		 */
2182 		return (DLADM_STATUS_NOTSUP);
2183 
2184 	case DL_WIFI:
2185 		return (do_get_rate_common(handle, pdp, linkid, prop_val,
2186 		    val_cnt, MAC_PROP_WL_SUPPORTED_RATES, perm_flags));
2187 	default:
2188 		return (DLADM_STATUS_BADARG);
2189 	}
2190 }
2191 
2192 static dladm_status_t
2193 do_set_rate(dladm_handle_t handle, datalink_id_t linkid,
2194     dladm_wlan_rates_t *rates)
2195 {
2196 	int		i;
2197 	uint_t		len;
2198 	wl_rates_t	*wrp;
2199 	dladm_status_t	status = DLADM_STATUS_OK;
2200 
2201 	wrp = malloc(WLDP_BUFSIZE);
2202 	if (wrp == NULL)
2203 		return (DLADM_STATUS_NOMEM);
2204 
2205 	bzero(wrp, WLDP_BUFSIZE);
2206 	for (i = 0; i < rates->wr_cnt; i++)
2207 		wrp->wl_rates_rates[i] = rates->wr_rates[i];
2208 	wrp->wl_rates_num = rates->wr_cnt;
2209 
2210 	len = offsetof(wl_rates_t, wl_rates_rates) +
2211 	    (rates->wr_cnt * sizeof (char)) + WIFI_BUF_OFFSET;
2212 	status = i_dladm_wlan_param(handle, linkid, wrp,
2213 	    MAC_PROP_WL_DESIRED_RATES, len, B_TRUE);
2214 
2215 	free(wrp);
2216 	return (status);
2217 }
2218 
2219 /* ARGSUSED */
2220 static dladm_status_t
2221 do_set_rate_prop(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2222     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
2223 {
2224 	dladm_wlan_rates_t	rates;
2225 	dladm_status_t		status;
2226 
2227 	/*
2228 	 * can currently set rate on WIFI links only.
2229 	 */
2230 	if (media != DL_WIFI)
2231 		return (DLADM_STATUS_PROPRDONLY);
2232 
2233 	if (val_cnt != 1)
2234 		return (DLADM_STATUS_BADVALCNT);
2235 
2236 	rates.wr_cnt = 1;
2237 	rates.wr_rates[0] = vdp[0].vd_val;
2238 
2239 	status = do_set_rate(handle, linkid, &rates);
2240 
2241 done:
2242 	return (status);
2243 }
2244 
2245 /* ARGSUSED */
2246 static dladm_status_t
2247 do_check_rate(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2248     char **prop_val, uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
2249 {
2250 	int		i;
2251 	uint_t		modval_cnt = MAX_SUPPORT_RATES;
2252 	char		*buf, **modval;
2253 	dladm_status_t	status;
2254 	uint_t 		perm_flags;
2255 
2256 	if (val_cnt != 1)
2257 		return (DLADM_STATUS_BADVALCNT);
2258 
2259 	buf = malloc((sizeof (char *) + DLADM_STRSIZE) *
2260 	    MAX_SUPPORT_RATES);
2261 	if (buf == NULL) {
2262 		status = DLADM_STATUS_NOMEM;
2263 		goto done;
2264 	}
2265 
2266 	modval = (char **)(void *)buf;
2267 	for (i = 0; i < MAX_SUPPORT_RATES; i++) {
2268 		modval[i] = buf + sizeof (char *) * MAX_SUPPORT_RATES +
2269 		    i * DLADM_STRSIZE;
2270 	}
2271 
2272 	status = do_get_rate_mod(handle, NULL, linkid, modval, &modval_cnt,
2273 	    media, 0, &perm_flags);
2274 	if (status != DLADM_STATUS_OK)
2275 		goto done;
2276 
2277 	for (i = 0; i < modval_cnt; i++) {
2278 		if (strcasecmp(*prop_val, modval[i]) == 0) {
2279 			vdp->vd_val = (uintptr_t)(uint_t)
2280 			    (atof(*prop_val) * 2);
2281 			status = DLADM_STATUS_OK;
2282 			break;
2283 		}
2284 	}
2285 	if (i == modval_cnt)
2286 		status = DLADM_STATUS_BADVAL;
2287 done:
2288 	free(buf);
2289 	return (status);
2290 }
2291 
2292 static dladm_status_t
2293 do_get_phyconf(dladm_handle_t handle, datalink_id_t linkid, void *buf,
2294     int buflen)
2295 {
2296 	return (i_dladm_wlan_param(handle, linkid, buf, MAC_PROP_WL_PHY_CONFIG,
2297 	    buflen, B_FALSE));
2298 }
2299 
2300 /* ARGSUSED */
2301 static dladm_status_t
2302 do_get_channel_prop(dladm_handle_t handle, prop_desc_t *pdp,
2303     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2304     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2305 {
2306 	uint32_t	channel;
2307 	char		buf[WLDP_BUFSIZE];
2308 	dladm_status_t	status = DLADM_STATUS_OK;
2309 	wl_phy_conf_t	wl_phy_conf;
2310 
2311 	if ((status = do_get_phyconf(handle, linkid, buf, sizeof (buf)))
2312 	    != DLADM_STATUS_OK)
2313 		goto done;
2314 
2315 	(void) memcpy(&wl_phy_conf, buf, sizeof (wl_phy_conf));
2316 	if (!i_dladm_wlan_convert_chan(&wl_phy_conf, &channel)) {
2317 		status = DLADM_STATUS_NOTFOUND;
2318 		goto done;
2319 	}
2320 
2321 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%u", channel);
2322 	*val_cnt = 1;
2323 	*perm_flags = MAC_PROP_PERM_READ;
2324 done:
2325 	return (status);
2326 }
2327 
2328 static dladm_status_t
2329 do_get_powermode(dladm_handle_t handle, datalink_id_t linkid, void *buf,
2330     int buflen)
2331 {
2332 	return (i_dladm_wlan_param(handle, linkid, buf, MAC_PROP_WL_POWER_MODE,
2333 	    buflen, B_FALSE));
2334 }
2335 
2336 /* ARGSUSED */
2337 static dladm_status_t
2338 do_get_powermode_prop(dladm_handle_t handle, prop_desc_t *pdp,
2339     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2340     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2341 {
2342 	wl_ps_mode_t	mode;
2343 	const char	*s;
2344 	char		buf[WLDP_BUFSIZE];
2345 	dladm_status_t	status = DLADM_STATUS_OK;
2346 
2347 	if ((status = do_get_powermode(handle, linkid, buf, sizeof (buf)))
2348 	    != DLADM_STATUS_OK)
2349 		goto done;
2350 
2351 	(void) memcpy(&mode, buf, sizeof (mode));
2352 	switch (mode.wl_ps_mode) {
2353 	case WL_PM_AM:
2354 		s = "off";
2355 		break;
2356 	case WL_PM_MPS:
2357 		s = "max";
2358 		break;
2359 	case WL_PM_FAST:
2360 		s = "fast";
2361 		break;
2362 	default:
2363 		status = DLADM_STATUS_NOTFOUND;
2364 		goto done;
2365 	}
2366 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
2367 	*val_cnt = 1;
2368 	*perm_flags = MAC_PROP_PERM_RW;
2369 done:
2370 	return (status);
2371 }
2372 
2373 static dladm_status_t
2374 do_set_powermode(dladm_handle_t handle, datalink_id_t linkid,
2375     dladm_wlan_powermode_t *pm)
2376 {
2377 	wl_ps_mode_t    ps_mode;
2378 
2379 	(void) memset(&ps_mode, 0xff, sizeof (ps_mode));
2380 
2381 	switch (*pm) {
2382 	case DLADM_WLAN_PM_OFF:
2383 		ps_mode.wl_ps_mode = WL_PM_AM;
2384 		break;
2385 	case DLADM_WLAN_PM_MAX:
2386 		ps_mode.wl_ps_mode = WL_PM_MPS;
2387 		break;
2388 	case DLADM_WLAN_PM_FAST:
2389 		ps_mode.wl_ps_mode = WL_PM_FAST;
2390 		break;
2391 	default:
2392 		return (DLADM_STATUS_NOTSUP);
2393 	}
2394 	return (i_dladm_wlan_param(handle, linkid, &ps_mode,
2395 	    MAC_PROP_WL_POWER_MODE, sizeof (ps_mode), B_TRUE));
2396 }
2397 
2398 /* ARGSUSED */
2399 static dladm_status_t
2400 do_set_powermode_prop(dladm_handle_t handle, prop_desc_t *pdp,
2401     datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt, uint_t flags,
2402     datalink_media_t media)
2403 {
2404 	dladm_wlan_powermode_t powermode = (dladm_wlan_powermode_t)vdp->vd_val;
2405 	dladm_status_t status;
2406 
2407 	if (val_cnt != 1)
2408 		return (DLADM_STATUS_BADVALCNT);
2409 
2410 	status = do_set_powermode(handle, linkid, &powermode);
2411 
2412 	return (status);
2413 }
2414 
2415 static dladm_status_t
2416 do_get_radio(dladm_handle_t handle, datalink_id_t linkid, void *buf, int buflen)
2417 {
2418 	return (i_dladm_wlan_param(handle, linkid, buf, MAC_PROP_WL_RADIO,
2419 	    buflen, B_FALSE));
2420 }
2421 
2422 /* ARGSUSED */
2423 static dladm_status_t
2424 do_get_radio_prop(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2425     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2426     uint_t flags, uint_t *perm_flags)
2427 {
2428 	wl_radio_t	radio;
2429 	const char	*s;
2430 	char		buf[WLDP_BUFSIZE];
2431 	dladm_status_t	status = DLADM_STATUS_OK;
2432 
2433 	if ((status = do_get_radio(handle, linkid, buf, sizeof (buf)))
2434 	    != DLADM_STATUS_OK)
2435 		goto done;
2436 
2437 	(void) memcpy(&radio, buf, sizeof (radio));
2438 	switch (radio) {
2439 	case B_TRUE:
2440 		s = "on";
2441 		break;
2442 	case B_FALSE:
2443 		s = "off";
2444 		break;
2445 	default:
2446 		status = DLADM_STATUS_NOTFOUND;
2447 		goto done;
2448 	}
2449 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
2450 	*val_cnt = 1;
2451 	*perm_flags = MAC_PROP_PERM_RW;
2452 done:
2453 	return (status);
2454 }
2455 
2456 static dladm_status_t
2457 do_set_radio(dladm_handle_t handle, datalink_id_t linkid,
2458     dladm_wlan_radio_t *radio)
2459 {
2460 	wl_radio_t r;
2461 
2462 	switch (*radio) {
2463 	case DLADM_WLAN_RADIO_ON:
2464 		r = B_TRUE;
2465 		break;
2466 	case DLADM_WLAN_RADIO_OFF:
2467 		r = B_FALSE;
2468 		break;
2469 	default:
2470 		return (DLADM_STATUS_NOTSUP);
2471 	}
2472 	return (i_dladm_wlan_param(handle, linkid, &r, MAC_PROP_WL_RADIO,
2473 	    sizeof (r), B_TRUE));
2474 }
2475 
2476 /* ARGSUSED */
2477 static dladm_status_t
2478 do_set_radio_prop(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2479     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
2480 {
2481 	dladm_wlan_radio_t radio = (dladm_wlan_radio_t)vdp->vd_val;
2482 	dladm_status_t status;
2483 
2484 	if (val_cnt != 1)
2485 		return (DLADM_STATUS_BADVALCNT);
2486 
2487 	status = do_set_radio(handle, linkid, &radio);
2488 
2489 	return (status);
2490 }
2491 
2492 /* ARGSUSED */
2493 static dladm_status_t
2494 do_check_hoplimit(dladm_handle_t handle, prop_desc_t *pdp,
2495     datalink_id_t linkid, char **prop_val, uint_t val_cnt, val_desc_t *vdp,
2496     datalink_media_t media)
2497 {
2498 	int32_t	hlim;
2499 	char	*ep;
2500 
2501 	if (val_cnt != 1)
2502 		return (DLADM_STATUS_BADVALCNT);
2503 
2504 	errno = 0;
2505 	hlim = strtol(*prop_val, &ep, 10);
2506 	if (errno != 0 || ep == *prop_val || hlim < 1 ||
2507 	    hlim > (int32_t)UINT8_MAX)
2508 		return (DLADM_STATUS_BADVAL);
2509 	vdp->vd_val = hlim;
2510 	return (DLADM_STATUS_OK);
2511 }
2512 
2513 /* ARGSUSED */
2514 static dladm_status_t
2515 do_check_encaplim(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2516     char **prop_val, uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
2517 {
2518 	int32_t	elim;
2519 	char	*ep;
2520 
2521 	if (media != DL_IPV6)
2522 		return (DLADM_STATUS_BADARG);
2523 
2524 	if (val_cnt != 1)
2525 		return (DLADM_STATUS_BADVALCNT);
2526 
2527 	errno = 0;
2528 	elim = strtol(*prop_val, &ep, 10);
2529 	if (errno != 0 || ep == *prop_val || elim < 0 ||
2530 	    elim > (int32_t)UINT8_MAX)
2531 		return (DLADM_STATUS_BADVAL);
2532 	vdp->vd_val = elim;
2533 	return (DLADM_STATUS_OK);
2534 }
2535 
2536 static dladm_status_t
2537 i_dladm_set_linkprop_db(dladm_handle_t handle, datalink_id_t linkid,
2538     const char *prop_name, char **prop_val, uint_t val_cnt)
2539 {
2540 	char		buf[MAXLINELEN];
2541 	int		i;
2542 	dladm_conf_t	conf;
2543 	dladm_status_t	status;
2544 
2545 	status = dladm_read_conf(handle, linkid, &conf);
2546 	if (status != DLADM_STATUS_OK)
2547 		return (status);
2548 
2549 	/*
2550 	 * reset case.
2551 	 */
2552 	if (val_cnt == 0) {
2553 		status = dladm_unset_conf_field(handle, conf, prop_name);
2554 		if (status == DLADM_STATUS_OK)
2555 			status = dladm_write_conf(handle, conf);
2556 		goto done;
2557 	}
2558 
2559 	buf[0] = '\0';
2560 	for (i = 0; i < val_cnt; i++) {
2561 		(void) strlcat(buf, prop_val[i], MAXLINELEN);
2562 		if (i != val_cnt - 1)
2563 			(void) strlcat(buf, ",", MAXLINELEN);
2564 	}
2565 
2566 	status = dladm_set_conf_field(handle, conf, prop_name, DLADM_TYPE_STR,
2567 	    buf);
2568 	if (status == DLADM_STATUS_OK)
2569 		status = dladm_write_conf(handle, conf);
2570 
2571 done:
2572 	dladm_destroy_conf(handle, conf);
2573 	return (status);
2574 }
2575 
2576 static dladm_status_t
2577 i_dladm_get_linkprop_db(dladm_handle_t handle, datalink_id_t linkid,
2578     const char *prop_name, char **prop_val, uint_t *val_cntp)
2579 {
2580 	char		buf[MAXLINELEN], *str;
2581 	uint_t		cnt = 0;
2582 	dladm_conf_t	conf;
2583 	dladm_status_t	status;
2584 
2585 	status = dladm_read_conf(handle, linkid, &conf);
2586 	if (status != DLADM_STATUS_OK)
2587 		return (status);
2588 
2589 	status = dladm_get_conf_field(handle, conf, prop_name, buf, MAXLINELEN);
2590 	if (status != DLADM_STATUS_OK)
2591 		goto done;
2592 
2593 	str = strtok(buf, ",");
2594 	while (str != NULL) {
2595 		if (cnt == *val_cntp) {
2596 			status = DLADM_STATUS_TOOSMALL;
2597 			goto done;
2598 		}
2599 		(void) strlcpy(prop_val[cnt++], str, DLADM_PROP_VAL_MAX);
2600 		str = strtok(NULL, ",");
2601 	}
2602 
2603 	*val_cntp = cnt;
2604 
2605 done:
2606 	dladm_destroy_conf(handle, conf);
2607 	return (status);
2608 }
2609 
2610 /*
2611  * Walk persistent private link properties of a link.
2612  */
2613 static dladm_status_t
2614 i_dladm_walk_linkprop_priv_db(dladm_handle_t handle, datalink_id_t linkid,
2615     void *arg, int (*func)(dladm_handle_t, datalink_id_t, const char *, void *))
2616 {
2617 	dladm_status_t		status;
2618 	dladm_conf_t		conf;
2619 	char			last_attr[MAXLINKATTRLEN];
2620 	char			attr[MAXLINKATTRLEN];
2621 	char			attrval[MAXLINKATTRVALLEN];
2622 	size_t			attrsz;
2623 
2624 	if (linkid == DATALINK_INVALID_LINKID || func == NULL)
2625 		return (DLADM_STATUS_BADARG);
2626 
2627 	status = dladm_read_conf(handle, linkid, &conf);
2628 	if (status != DLADM_STATUS_OK)
2629 		return (status);
2630 
2631 	last_attr[0] = '\0';
2632 	while ((status = dladm_getnext_conf_linkprop(handle, conf, last_attr,
2633 	    attr, attrval, MAXLINKATTRVALLEN, &attrsz)) == DLADM_STATUS_OK) {
2634 		if (attr[0] == '_') {
2635 			if (func(handle, linkid, attr, arg) ==
2636 			    DLADM_WALK_TERMINATE)
2637 				break;
2638 		}
2639 		(void) strlcpy(last_attr, attr, MAXLINKATTRLEN);
2640 	}
2641 
2642 	dladm_destroy_conf(handle, conf);
2643 	return (DLADM_STATUS_OK);
2644 }
2645 
2646 static link_attr_t *
2647 dladm_name2prop(const char *prop_name)
2648 {
2649 	link_attr_t *p;
2650 
2651 	for (p = link_attr; p->pp_id != MAC_PROP_PRIVATE; p++) {
2652 		if (strcmp(p->pp_name, prop_name) == 0)
2653 			break;
2654 	}
2655 	return (p);
2656 }
2657 
2658 static link_attr_t *
2659 dladm_id2prop(mac_prop_id_t propid)
2660 {
2661 	link_attr_t *p;
2662 
2663 	for (p = link_attr; p->pp_id != MAC_PROP_PRIVATE; p++) {
2664 		if (p->pp_id == propid)
2665 			break;
2666 	}
2667 	return (p);
2668 }
2669 
2670 static dld_ioc_macprop_t *
2671 i_dladm_buf_alloc_impl(size_t valsize, datalink_id_t linkid,
2672     const char *prop_name, mac_prop_id_t propid, uint_t flags,
2673     dladm_status_t *status)
2674 {
2675 	int dsize;
2676 	dld_ioc_macprop_t *dip;
2677 
2678 	*status = DLADM_STATUS_OK;
2679 	dsize = MAC_PROP_BUFSIZE(valsize);
2680 	dip = malloc(dsize);
2681 	if (dip == NULL) {
2682 		*status = DLADM_STATUS_NOMEM;
2683 		return (NULL);
2684 	}
2685 	bzero(dip, dsize);
2686 	dip->pr_valsize = valsize;
2687 	(void) strlcpy(dip->pr_name, prop_name, sizeof (dip->pr_name));
2688 	dip->pr_version = MAC_PROP_VERSION;
2689 	dip->pr_linkid = linkid;
2690 	dip->pr_num = propid;
2691 	dip->pr_flags = flags;
2692 	return (dip);
2693 }
2694 
2695 static dld_ioc_macprop_t *
2696 i_dladm_buf_alloc_by_name(size_t valsize, datalink_id_t linkid,
2697     const char *prop_name, uint_t flags, dladm_status_t *status)
2698 {
2699 	link_attr_t *p;
2700 
2701 	p = dladm_name2prop(prop_name);
2702 	valsize = MAX(p->pp_valsize, valsize);
2703 	return (i_dladm_buf_alloc_impl(valsize, linkid, prop_name, p->pp_id,
2704 	    flags, status));
2705 }
2706 
2707 static dld_ioc_macprop_t *
2708 i_dladm_buf_alloc_by_id(size_t valsize, datalink_id_t linkid,
2709     mac_prop_id_t propid, uint_t flags, dladm_status_t *status)
2710 {
2711 	link_attr_t *p;
2712 
2713 	p = dladm_id2prop(propid);
2714 	valsize = MAX(p->pp_valsize, valsize);
2715 	return (i_dladm_buf_alloc_impl(valsize, linkid, p->pp_name, propid,
2716 	    flags, status));
2717 }
2718 
2719 /* ARGSUSED */
2720 static dladm_status_t
2721 i_dladm_set_public_prop(dladm_handle_t handle, prop_desc_t *pdp,
2722     datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt, uint_t flags,
2723     datalink_media_t media)
2724 {
2725 	dld_ioc_macprop_t	*dip;
2726 	dladm_status_t	status = DLADM_STATUS_OK;
2727 	uint8_t		u8;
2728 	uint16_t	u16;
2729 	uint32_t	u32;
2730 	void		*val;
2731 
2732 	dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name, 0, &status);
2733 	if (dip == NULL)
2734 		return (status);
2735 
2736 	if (pdp->pd_flags & PD_CHECK_ALLOC)
2737 		val = (void *)vdp->vd_val;
2738 	else {
2739 		/*
2740 		 * Currently all 1/2/4-byte size properties are byte/word/int.
2741 		 * No need (yet) to distinguish these from arrays of same size.
2742 		 */
2743 		switch (dip->pr_valsize) {
2744 		case 1:
2745 			u8 = vdp->vd_val;
2746 			val = &u8;
2747 			break;
2748 		case 2:
2749 			u16 = vdp->vd_val;
2750 			val = &u16;
2751 			break;
2752 		case 4:
2753 			u32 = vdp->vd_val;
2754 			val = &u32;
2755 			break;
2756 		default:
2757 			val = &vdp->vd_val;
2758 			break;
2759 		}
2760 	}
2761 
2762 	if (val != NULL)
2763 		(void) memcpy(dip->pr_val, val, dip->pr_valsize);
2764 	else
2765 		dip->pr_valsize = 0;
2766 
2767 	status = i_dladm_macprop(handle, dip, B_TRUE);
2768 
2769 done:
2770 	free(dip);
2771 	return (status);
2772 }
2773 
2774 dladm_status_t
2775 i_dladm_macprop(dladm_handle_t handle, void *dip, boolean_t set)
2776 {
2777 	dladm_status_t status = DLADM_STATUS_OK;
2778 
2779 	if (ioctl(dladm_dld_fd(handle),
2780 	    (set ? DLDIOC_SETMACPROP : DLDIOC_GETMACPROP), dip))
2781 		status = dladm_errno2status(errno);
2782 
2783 	return (status);
2784 }
2785 
2786 static dld_ioc_macprop_t *
2787 i_dladm_get_public_prop(dladm_handle_t handle, datalink_id_t linkid,
2788     char *prop_name, uint_t flags, dladm_status_t *status, uint_t *perm_flags)
2789 {
2790 	dld_ioc_macprop_t *dip = NULL;
2791 
2792 	dip = i_dladm_buf_alloc_by_name(0, linkid, prop_name, flags, status);
2793 	if (dip == NULL)
2794 		return (NULL);
2795 
2796 	*status = i_dladm_macprop(handle, dip, B_FALSE);
2797 	if (*status != DLADM_STATUS_OK) {
2798 		free(dip);
2799 		return (NULL);
2800 	}
2801 	if (perm_flags != NULL)
2802 		*perm_flags = dip->pr_perm_flags;
2803 
2804 	return (dip);
2805 }
2806 
2807 /* ARGSUSED */
2808 static dladm_status_t
2809 i_dladm_uint32_check(dladm_handle_t handle, prop_desc_t *pdp,
2810     datalink_id_t linkid, char **prop_val, uint_t val_cnt, val_desc_t *v,
2811     datalink_media_t media)
2812 {
2813 	if (val_cnt != 1)
2814 		return (DLADM_STATUS_BADVAL);
2815 	v->vd_val = strtoul(prop_val[0], NULL, 0);
2816 	return (DLADM_STATUS_OK);
2817 }
2818 
2819 /* ARGSUSED */
2820 static dladm_status_t
2821 i_dladm_duplex_get(dladm_handle_t handle, prop_desc_t *pdp,
2822     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2823     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2824 {
2825 	link_duplex_t   link_duplex;
2826 	dladm_status_t  status;
2827 
2828 	if ((status = dladm_get_single_mac_stat(handle, linkid, "link_duplex",
2829 	    KSTAT_DATA_UINT32, &link_duplex)) != 0)
2830 		return (status);
2831 
2832 	switch (link_duplex) {
2833 	case LINK_DUPLEX_FULL:
2834 		(void) strcpy(*prop_val, "full");
2835 		break;
2836 	case LINK_DUPLEX_HALF:
2837 		(void) strcpy(*prop_val, "half");
2838 		break;
2839 	default:
2840 		(void) strcpy(*prop_val, "unknown");
2841 		break;
2842 	}
2843 	*val_cnt = 1;
2844 	return (DLADM_STATUS_OK);
2845 }
2846 
2847 /* ARGSUSED */
2848 static dladm_status_t
2849 i_dladm_speed_get(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
2850     char **prop_val, uint_t *val_cnt, uint_t flags, uint_t *perm_flags)
2851 {
2852 	uint64_t	ifspeed = 0;
2853 	dladm_status_t status;
2854 
2855 	if ((status = dladm_get_single_mac_stat(handle, linkid, "ifspeed",
2856 	    KSTAT_DATA_UINT64, &ifspeed)) != 0)
2857 		return (status);
2858 
2859 	if ((ifspeed % 1000000) != 0) {
2860 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
2861 		    "%llf", ifspeed / (float)1000000); /* Mbps */
2862 	} else {
2863 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
2864 		    "%llu", ifspeed / 1000000); /* Mbps */
2865 	}
2866 	*val_cnt = 1;
2867 	*perm_flags = MAC_PROP_PERM_READ;
2868 	return (DLADM_STATUS_OK);
2869 }
2870 
2871 /* ARGSUSED */
2872 static dladm_status_t
2873 i_dladm_status_get(dladm_handle_t handle, prop_desc_t *pdp,
2874     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2875     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2876 {
2877 	link_state_t		link_state;
2878 	dladm_status_t		status;
2879 
2880 	status = i_dladm_get_state(handle, linkid, &link_state);
2881 	if (status != DLADM_STATUS_OK)
2882 		return (status);
2883 
2884 	switch (link_state) {
2885 	case LINK_STATE_UP:
2886 		(void) strcpy(*prop_val, "up");
2887 		break;
2888 	case LINK_STATE_DOWN:
2889 		(void) strcpy(*prop_val, "down");
2890 		break;
2891 	default:
2892 		(void) strcpy(*prop_val, "unknown");
2893 		break;
2894 	}
2895 	*val_cnt = 1;
2896 	*perm_flags = MAC_PROP_PERM_READ;
2897 	return (DLADM_STATUS_OK);
2898 }
2899 
2900 /* ARGSUSED */
2901 static dladm_status_t
2902 i_dladm_binary_get(dladm_handle_t handle, prop_desc_t *pdp,
2903     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2904     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2905 {
2906 	dld_ioc_macprop_t *dip;
2907 	dladm_status_t status;
2908 
2909 	dip = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
2910 	    &status, perm_flags);
2911 	if (dip == NULL)
2912 		return (status);
2913 
2914 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%x", dip->pr_val[0]);
2915 	free(dip);
2916 	*val_cnt = 1;
2917 	return (DLADM_STATUS_OK);
2918 }
2919 
2920 /* ARGSUSED */
2921 static dladm_status_t
2922 i_dladm_uint32_get(dladm_handle_t handle, prop_desc_t *pdp,
2923     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2924     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2925 {
2926 	dld_ioc_macprop_t *dip;
2927 	uint32_t v = 0;
2928 	uchar_t *cp;
2929 	dladm_status_t status;
2930 
2931 	dip = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
2932 	    &status, perm_flags);
2933 	if (dip == NULL)
2934 		return (status);
2935 
2936 	cp = (uchar_t *)dip->pr_val;
2937 	(void) memcpy(&v, cp, sizeof (v));
2938 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%ld", v);
2939 	free(dip);
2940 	*val_cnt = 1;
2941 	return (DLADM_STATUS_OK);
2942 }
2943 
2944 /*
2945  * Determines the size of the structure that needs to be sent to drivers
2946  * for retrieving the property range values.
2947  */
2948 static int
2949 i_dladm_range_size(mac_propval_range_t *r, size_t *sz)
2950 {
2951 	uint_t count = r->mpr_count;
2952 
2953 	*sz = sizeof (mac_propval_range_t);
2954 	--count;
2955 
2956 	switch (r->mpr_type) {
2957 	case MAC_PROPVAL_UINT32:
2958 		*sz += (count * sizeof (mac_propval_uint32_range_t));
2959 		return (0);
2960 	default:
2961 		break;
2962 	}
2963 	*sz = 0;
2964 	return (EINVAL);
2965 }
2966 
2967 /* ARGSUSED */
2968 static dladm_status_t
2969 i_dladm_range_get(dladm_handle_t handle, prop_desc_t *pdp,
2970     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
2971     datalink_media_t media, uint_t flags, uint_t *perm_flags)
2972 {
2973 	dld_ioc_macprop_t *dip;
2974 	dladm_status_t status = DLADM_STATUS_OK;
2975 	size_t	sz;
2976 	mac_propval_range_t *rangep;
2977 
2978 	sz = sizeof (mac_propval_range_t);
2979 
2980 	/*
2981 	 * As caller we don't know number of value ranges, the driver
2982 	 * supports. To begin with we assume that number to be 1. If the
2983 	 * buffer size is insufficient, driver returns back with the
2984 	 * actual count of value ranges. See mac.h for more details.
2985 	 */
2986 retry:
2987 	if ((dip = i_dladm_buf_alloc_by_name(sz, linkid, pdp->pd_name, flags,
2988 	    &status)) == NULL)
2989 		return (status);
2990 
2991 	status = i_dladm_macprop(handle, dip, B_FALSE);
2992 	if (status != DLADM_STATUS_OK) {
2993 		if (status == DLADM_STATUS_TOOSMALL) {
2994 			int err;
2995 
2996 			rangep = (mac_propval_range_t *)(void *)&dip->pr_val;
2997 			if ((err = i_dladm_range_size(rangep, &sz)) == 0) {
2998 				free(dip);
2999 				goto retry;
3000 			} else {
3001 				status = dladm_errno2status(err);
3002 			}
3003 		}
3004 		free(dip);
3005 		return (status);
3006 	}
3007 	rangep = (mac_propval_range_t *)(void *)&dip->pr_val;
3008 
3009 	switch (rangep->mpr_type) {
3010 	case MAC_PROPVAL_UINT32: {
3011 		mac_propval_uint32_range_t *ur;
3012 		uint_t	count = rangep->mpr_count, i;
3013 
3014 		ur = &rangep->range_uint32[0];
3015 
3016 		for (i = 0; i < count; i++, ur++) {
3017 			if (ur->mpur_min == ur->mpur_max) {
3018 				(void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
3019 				    "%ld", ur->mpur_min);
3020 			} else {
3021 				(void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
3022 				    "%ld-%ld", ur->mpur_min, ur->mpur_max);
3023 			}
3024 		}
3025 		*val_cnt = count;
3026 		break;
3027 	}
3028 	default:
3029 		status = DLADM_STATUS_BADARG;
3030 		break;
3031 	}
3032 	free(dip);
3033 	return (status);
3034 }
3035 
3036 /* ARGSUSED */
3037 static dladm_status_t
3038 i_dladm_tagmode_get(dladm_handle_t handle, prop_desc_t *pdp,
3039     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3040     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3041 {
3042 	dld_ioc_macprop_t	*dip;
3043 	link_tagmode_t		mode;
3044 	dladm_status_t		status;
3045 
3046 	dip = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
3047 	    &status, perm_flags);
3048 	if (dip == NULL)
3049 		return (status);
3050 	(void) memcpy(&mode, dip->pr_val, sizeof (mode));
3051 	free(dip);
3052 
3053 	switch (mode) {
3054 	case LINK_TAGMODE_NORMAL:
3055 		(void) strlcpy(*prop_val, "normal", DLADM_PROP_VAL_MAX);
3056 		break;
3057 	case LINK_TAGMODE_VLANONLY:
3058 		(void) strlcpy(*prop_val, "vlanonly", DLADM_PROP_VAL_MAX);
3059 		break;
3060 	default:
3061 		(void) strlcpy(*prop_val, "unknown", DLADM_PROP_VAL_MAX);
3062 	}
3063 	*val_cnt = 1;
3064 	return (DLADM_STATUS_OK);
3065 }
3066 
3067 /* ARGSUSED */
3068 static dladm_status_t
3069 i_dladm_flowctl_get(dladm_handle_t handle, prop_desc_t *pdp,
3070     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3071     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3072 {
3073 	dld_ioc_macprop_t *dip;
3074 	link_flowctrl_t v;
3075 	dladm_status_t status;
3076 	uchar_t *cp;
3077 
3078 	dip = i_dladm_get_public_prop(handle, linkid, pdp->pd_name, flags,
3079 	    &status, perm_flags);
3080 	if (dip == NULL)
3081 		return (status);
3082 
3083 	cp = (uchar_t *)dip->pr_val;
3084 	(void) memcpy(&v, cp, sizeof (v));
3085 	switch (v) {
3086 	case LINK_FLOWCTRL_NONE:
3087 		(void) sprintf(*prop_val, "no");
3088 		break;
3089 	case LINK_FLOWCTRL_RX:
3090 		(void) sprintf(*prop_val, "rx");
3091 		break;
3092 	case LINK_FLOWCTRL_TX:
3093 		(void) sprintf(*prop_val, "tx");
3094 		break;
3095 	case LINK_FLOWCTRL_BI:
3096 		(void) sprintf(*prop_val, "bi");
3097 		break;
3098 	}
3099 	free(dip);
3100 	*val_cnt = 1;
3101 	return (DLADM_STATUS_OK);
3102 }
3103 
3104 
3105 /* ARGSUSED */
3106 static dladm_status_t
3107 i_dladm_set_private_prop(dladm_handle_t handle, datalink_id_t linkid,
3108     const char *prop_name, char **prop_val, uint_t val_cnt, uint_t flags)
3109 
3110 {
3111 	int		i, slen;
3112 	int 		bufsize = 0;
3113 	dld_ioc_macprop_t *dip = NULL;
3114 	uchar_t 	*dp;
3115 	link_attr_t *p;
3116 	dladm_status_t	status = DLADM_STATUS_OK;
3117 
3118 	if ((prop_name == NULL && prop_val != NULL) ||
3119 	    (prop_val != NULL && val_cnt == 0))
3120 		return (DLADM_STATUS_BADARG);
3121 	p = dladm_name2prop(prop_name);
3122 	if (p->pp_id != MAC_PROP_PRIVATE)
3123 		return (DLADM_STATUS_BADARG);
3124 
3125 	if (!(flags & DLADM_OPT_ACTIVE))
3126 		return (DLADM_STATUS_OK);
3127 
3128 	/*
3129 	 * private properties: all parsing is done in the kernel.
3130 	 * allocate a enough space for each property + its separator (',').
3131 	 */
3132 	for (i = 0; i < val_cnt; i++) {
3133 		bufsize += strlen(prop_val[i]) + 1;
3134 	}
3135 
3136 	if (prop_val == NULL) {
3137 		/*
3138 		 * getting default value. so use more buffer space.
3139 		 */
3140 		bufsize += DLADM_PROP_BUF_CHUNK;
3141 	}
3142 
3143 	dip = i_dladm_buf_alloc_by_name(bufsize + 1, linkid, prop_name,
3144 	    (prop_val != NULL ? 0 : MAC_PROP_DEFAULT), &status);
3145 	if (dip == NULL)
3146 		return (status);
3147 
3148 	dp = (uchar_t *)dip->pr_val;
3149 	slen = 0;
3150 
3151 	if (prop_val == NULL) {
3152 		status = i_dladm_macprop(handle, dip, B_FALSE);
3153 		dip->pr_flags = 0;
3154 	} else {
3155 		for (i = 0; i < val_cnt; i++) {
3156 			int plen = 0;
3157 
3158 			plen = strlen(prop_val[i]);
3159 			bcopy(prop_val[i], dp, plen);
3160 			slen += plen;
3161 			/*
3162 			 * add a "," separator and update dp.
3163 			 */
3164 			if (i != (val_cnt -1))
3165 				dp[slen++] = ',';
3166 			dp += (plen + 1);
3167 		}
3168 	}
3169 	if (status == DLADM_STATUS_OK)
3170 		status = i_dladm_macprop(handle, dip, B_TRUE);
3171 
3172 	free(dip);
3173 	return (status);
3174 }
3175 
3176 static dladm_status_t
3177 i_dladm_get_priv_prop(dladm_handle_t handle, datalink_id_t linkid,
3178     const char *prop_name, char **prop_val, uint_t *val_cnt,
3179     dladm_prop_type_t type, uint_t dld_flags)
3180 {
3181 	dladm_status_t	status = DLADM_STATUS_OK;
3182 	dld_ioc_macprop_t *dip = NULL;
3183 	link_attr_t *p;
3184 
3185 	if ((prop_name == NULL && prop_val != NULL) ||
3186 	    (prop_val != NULL && val_cnt == 0))
3187 		return (DLADM_STATUS_BADARG);
3188 
3189 	p = dladm_name2prop(prop_name);
3190 	if (p->pp_id != MAC_PROP_PRIVATE)
3191 		return (DLADM_STATUS_BADARG);
3192 
3193 	/*
3194 	 * private properties: all parsing is done in the kernel.
3195 	 */
3196 	dip = i_dladm_buf_alloc_by_name(DLADM_PROP_BUF_CHUNK, linkid, prop_name,
3197 	    dld_flags, &status);
3198 	if (dip == NULL)
3199 		return (status);
3200 
3201 	if ((status = i_dladm_macprop(handle, dip, B_FALSE)) ==
3202 	    DLADM_STATUS_OK) {
3203 		if (type == DLADM_PROP_VAL_PERM) {
3204 			(void) dladm_perm2str(dip->pr_perm_flags, *prop_val);
3205 		} else if (type == DLADM_PROP_VAL_MODIFIABLE) {
3206 			*prop_val[0] = '\0';
3207 		} else {
3208 			(void) strncpy(*prop_val, dip->pr_val,
3209 			    DLADM_PROP_VAL_MAX);
3210 		}
3211 		*val_cnt = 1;
3212 	} else if ((status == DLADM_STATUS_NOTSUP) &&
3213 	    (type == DLADM_PROP_VAL_CURRENT)) {
3214 		status = DLADM_STATUS_NOTFOUND;
3215 	}
3216 	free(dip);
3217 	return (status);
3218 }
3219 
3220 
3221 static dladm_status_t
3222 i_dladm_getset_defval(dladm_handle_t handle, prop_desc_t *pdp,
3223     datalink_id_t linkid, datalink_media_t media, uint_t flags)
3224 {
3225 	dladm_status_t status;
3226 	char **prop_vals = NULL, *buf;
3227 	size_t bufsize;
3228 	uint_t cnt;
3229 	int i;
3230 	uint_t perm_flags;
3231 
3232 	/*
3233 	 * Allocate buffer needed for prop_vals array. We can have at most
3234 	 * DLADM_MAX_PROP_VALCNT char *prop_vals[] entries, where
3235 	 * each entry has max size DLADM_PROP_VAL_MAX
3236 	 */
3237 	bufsize =
3238 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
3239 	buf = malloc(bufsize);
3240 	prop_vals = (char **)(void *)buf;
3241 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
3242 		prop_vals[i] = buf +
3243 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
3244 		    i * DLADM_PROP_VAL_MAX;
3245 	}
3246 
3247 	/*
3248 	 * For properties which have pdp->pd_defval.vd_name as a non-empty
3249 	 * string, the "" itself is used to reset the property (exceptions
3250 	 * are zone and autopush, which populate vdp->vd_val). So
3251 	 * libdladm can copy pdp->pd_defval over to the val_desc_t passed
3252 	 * down on the setprop using the global values in the table. For
3253 	 * other cases (vd_name is ""), doing reset-linkprop will cause
3254 	 * libdladm to do a getprop to find the default value and then do
3255 	 * a setprop to reset the value to default.
3256 	 */
3257 	status = pdp->pd_get(handle, pdp, linkid, prop_vals, &cnt, media,
3258 	    MAC_PROP_DEFAULT, &perm_flags);
3259 	if (status == DLADM_STATUS_OK) {
3260 		if (perm_flags == MAC_PROP_PERM_RW) {
3261 			status = i_dladm_set_single_prop(handle, linkid,
3262 			    pdp->pd_class, media, pdp, prop_vals, cnt, flags);
3263 		}
3264 		else
3265 			status = DLADM_STATUS_NOTSUP;
3266 	}
3267 	free(buf);
3268 	return (status);
3269 }
3270 
3271 /* ARGSUSED */
3272 static dladm_status_t
3273 get_stp_prop(dladm_handle_t handle, struct prop_desc *pd, datalink_id_t linkid,
3274     char **prop_val, uint_t *val_cnt, datalink_media_t media, uint_t flags,
3275     uint_t *perm_flags)
3276 {
3277 	const bridge_public_prop_t *bpp;
3278 	dladm_status_t retv;
3279 	int val, i;
3280 
3281 	if (flags != 0)
3282 		return (DLADM_STATUS_NOTSUP);
3283 	*perm_flags = MAC_PROP_PERM_RW;
3284 	*val_cnt = 1;
3285 	for (bpp = bridge_prop; bpp->bpp_name != NULL; bpp++)
3286 		if (strcmp(bpp->bpp_name, pd->pd_name) == 0)
3287 			break;
3288 	retv = dladm_bridge_get_port_cfg(handle, linkid, bpp->bpp_code, &val);
3289 	/* If the daemon isn't running, then return the persistent value */
3290 	if (retv == DLADM_STATUS_NOTFOUND) {
3291 		if (i_dladm_get_linkprop_db(handle, linkid, pd->pd_name,
3292 		    prop_val, val_cnt) != DLADM_STATUS_OK)
3293 			(void) strlcpy(*prop_val, pd->pd_defval.vd_name,
3294 			    DLADM_PROP_VAL_MAX);
3295 		return (DLADM_STATUS_OK);
3296 	}
3297 	if (retv != DLADM_STATUS_OK) {
3298 		(void) strlcpy(*prop_val, "?", DLADM_PROP_VAL_MAX);
3299 		return (retv);
3300 	}
3301 	if (val == pd->pd_defval.vd_val && pd->pd_defval.vd_name[0] != '\0') {
3302 		(void) strlcpy(*prop_val, pd->pd_defval.vd_name,
3303 		    DLADM_PROP_VAL_MAX);
3304 		return (DLADM_STATUS_OK);
3305 	}
3306 	for (i = 0; i < pd->pd_noptval; i++) {
3307 		if (val == pd->pd_optval[i].vd_val) {
3308 			(void) strlcpy(*prop_val, pd->pd_optval[i].vd_name,
3309 			    DLADM_PROP_VAL_MAX);
3310 			return (DLADM_STATUS_OK);
3311 		}
3312 	}
3313 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%u", (unsigned)val);
3314 	return (DLADM_STATUS_OK);
3315 }
3316 
3317 /* ARGSUSED1 */
3318 static dladm_status_t
3319 set_stp_prop(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid,
3320     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
3321 {
3322 	/*
3323 	 * Special case for mcheck: the daemon resets the value to zero, and we
3324 	 * don't want the daemon to refresh itself; it leads to deadlock.
3325 	 */
3326 	if (flags & DLADM_OPT_NOREFRESH)
3327 		return (DLADM_STATUS_OK);
3328 
3329 	/* Tell the running daemon, if any */
3330 	return (dladm_bridge_refresh(handle, linkid));
3331 }
3332 
3333 /*
3334  * This is used only for stp_priority, stp_cost, and stp_mcheck.
3335  */
3336 /* ARGSUSED */
3337 static dladm_status_t
3338 check_stp_prop(dladm_handle_t handle, struct prop_desc *pd,
3339     datalink_id_t linkid, char **prop_val, uint_t val_cnt, val_desc_t *vdp,
3340     datalink_media_t media)
3341 {
3342 	char *cp;
3343 	boolean_t iscost;
3344 
3345 	if (val_cnt != 1)
3346 		return (DLADM_STATUS_BADVALCNT);
3347 
3348 	if (prop_val == NULL) {
3349 		vdp->vd_val = 0;
3350 	} else {
3351 		/* Only stp_priority and stp_cost use this function */
3352 		iscost = strcmp(pd->pd_name, "stp_cost") == 0;
3353 
3354 		if (iscost && strcmp(prop_val[0], "auto") == 0) {
3355 			/* Illegal value 0 is allowed to mean "automatic" */
3356 			vdp->vd_val = 0;
3357 		} else {
3358 			errno = 0;
3359 			vdp->vd_val = strtoul(prop_val[0], &cp, 0);
3360 			if (errno != 0 || *cp != '\0')
3361 				return (DLADM_STATUS_BADVAL);
3362 		}
3363 	}
3364 
3365 	if (iscost) {
3366 		return (vdp->vd_val > 65535 ? DLADM_STATUS_BADVAL :
3367 		    DLADM_STATUS_OK);
3368 	} else {
3369 		if (vdp->vd_val > 255)
3370 			return (DLADM_STATUS_BADVAL);
3371 		/*
3372 		 * If the user is setting stp_mcheck non-zero, then (per the
3373 		 * IEEE management standards and UNH testing) we need to check
3374 		 * whether this link is part of a bridge that is running RSTP.
3375 		 * If it's not, then setting the flag is an error.  Note that
3376 		 * errors are intentionally discarded here; it's the value
3377 		 * that's the problem -- it's not a bad value, merely one that
3378 		 * can't be used now.
3379 		 */
3380 		if (strcmp(pd->pd_name, "stp_mcheck") == 0 &&
3381 		    vdp->vd_val != 0) {
3382 			char bridge[MAXLINKNAMELEN];
3383 			UID_STP_CFG_T cfg;
3384 			dladm_bridge_prot_t brprot;
3385 
3386 			if (dladm_bridge_getlink(handle, linkid, bridge,
3387 			    sizeof (bridge)) != DLADM_STATUS_OK ||
3388 			    dladm_bridge_get_properties(bridge, &cfg,
3389 			    &brprot) != DLADM_STATUS_OK)
3390 				return (DLADM_STATUS_FAILED);
3391 			if (cfg.force_version <= 1)
3392 				return (DLADM_STATUS_FAILED);
3393 		}
3394 		return (DLADM_STATUS_OK);
3395 	}
3396 }
3397 
3398 /* ARGSUSED */
3399 static dladm_status_t
3400 get_bridge_forward(dladm_handle_t handle, struct prop_desc *pd,
3401     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3402     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3403 {
3404 	dladm_status_t retv;
3405 	uint_t val;
3406 
3407 	if (flags != 0)
3408 		return (DLADM_STATUS_NOTSUP);
3409 	*perm_flags = MAC_PROP_PERM_RW;
3410 	*val_cnt = 1;
3411 	retv = dladm_bridge_get_forwarding(handle, linkid, &val);
3412 	if (retv == DLADM_STATUS_NOTFOUND) {
3413 		if (i_dladm_get_linkprop_db(handle, linkid, pd->pd_name,
3414 		    prop_val, val_cnt) != DLADM_STATUS_OK)
3415 			(void) strlcpy(*prop_val, pd->pd_defval.vd_name,
3416 			    DLADM_PROP_VAL_MAX);
3417 		return (DLADM_STATUS_OK);
3418 	}
3419 	if (retv == DLADM_STATUS_OK)
3420 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%u", val);
3421 	else
3422 		(void) strlcpy(*prop_val, "?", DLADM_PROP_VAL_MAX);
3423 	return (retv);
3424 }
3425 
3426 /* ARGSUSED */
3427 static dladm_status_t
3428 set_bridge_forward(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid,
3429     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
3430 {
3431 	/* Tell the running daemon, if any */
3432 	return (dladm_bridge_refresh(handle, linkid));
3433 }
3434 
3435 /* ARGSUSED */
3436 static dladm_status_t
3437 get_bridge_pvid(dladm_handle_t handle, struct prop_desc *pd,
3438     datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
3439     datalink_media_t media, uint_t flags, uint_t *perm_flags)
3440 {
3441 	dladm_status_t status;
3442 	dld_ioc_macprop_t *dip;
3443 	uint16_t pvid;
3444 
3445 	if (flags != 0)
3446 		return (DLADM_STATUS_NOTSUP);
3447 	*perm_flags = MAC_PROP_PERM_RW;
3448 	*val_cnt = 1;
3449 	dip = i_dladm_buf_alloc_by_id(sizeof (uint16_t), linkid, MAC_PROP_PVID,
3450 	    0, &status);
3451 	if (dip == NULL)
3452 		return (status);
3453 	status = i_dladm_macprop(handle, dip, B_FALSE);
3454 	if (status == DLADM_STATUS_OK) {
3455 		(void) memcpy(&pvid, dip->pr_val, sizeof (pvid));
3456 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%u", pvid);
3457 	} else {
3458 		(void) strlcpy(*prop_val, "?", DLADM_PROP_VAL_MAX);
3459 	}
3460 	free(dip);
3461 	return (status);
3462 }
3463 
3464 /* ARGSUSED */
3465 static dladm_status_t
3466 set_bridge_pvid(dladm_handle_t handle, prop_desc_t *pd, datalink_id_t linkid,
3467     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
3468 {
3469 	dladm_status_t status;
3470 	dld_ioc_macprop_t *dip;
3471 	uint16_t pvid;
3472 
3473 	dip = i_dladm_buf_alloc_by_id(sizeof (uint16_t), linkid, MAC_PROP_PVID,
3474 	    0, &status);
3475 	if (dip == NULL)
3476 		return (status);
3477 	pvid = vdp->vd_val;
3478 	(void) memcpy(dip->pr_val, &pvid, sizeof (pvid));
3479 	status = i_dladm_macprop(handle, dip, B_TRUE);
3480 	free(dip);
3481 	if (status != DLADM_STATUS_OK)
3482 		return (status);
3483 
3484 	/* Tell the running daemon, if any */
3485 	return (dladm_bridge_refresh(handle, linkid));
3486 }
3487 
3488 /* ARGSUSED */
3489 static dladm_status_t
3490 check_bridge_pvid(dladm_handle_t handle, struct prop_desc *pd,
3491     datalink_id_t linkid, char **prop_val, uint_t val_cnt, val_desc_t *vdp,
3492     datalink_media_t media)
3493 {
3494 	char *cp;
3495 
3496 	if (val_cnt != 1)
3497 		return (DLADM_STATUS_BADVALCNT);
3498 
3499 	if (prop_val == NULL) {
3500 		vdp->vd_val = 1;
3501 	} else {
3502 		errno = 0;
3503 		vdp->vd_val = strtoul(prop_val[0], &cp, 0);
3504 		if (errno != 0 || *cp != '\0')
3505 			return (DLADM_STATUS_BADVAL);
3506 	}
3507 
3508 	return (vdp->vd_val > VLAN_ID_MAX ? DLADM_STATUS_BADVAL :
3509 	    DLADM_STATUS_OK);
3510 }
3511 
3512 dladm_status_t
3513 i_dladm_wlan_param(dladm_handle_t handle, datalink_id_t linkid, void *buf,
3514     mac_prop_id_t cmd, size_t len, boolean_t set)
3515 {
3516 	uint32_t		flags;
3517 	dladm_status_t		status;
3518 	uint32_t		media;
3519 	dld_ioc_macprop_t	*dip;
3520 	void			*dp;
3521 
3522 	if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL,
3523 	    &media, NULL, 0)) != DLADM_STATUS_OK) {
3524 		return (status);
3525 	}
3526 
3527 	if (media != DL_WIFI)
3528 		return (DLADM_STATUS_BADARG);
3529 
3530 	if (!(flags & DLADM_OPT_ACTIVE))
3531 		return (DLADM_STATUS_TEMPONLY);
3532 
3533 	if (len == (MAX_BUF_LEN - WIFI_BUF_OFFSET))
3534 		len = MAX_BUF_LEN - sizeof (dld_ioc_macprop_t) - 1;
3535 
3536 	dip = i_dladm_buf_alloc_by_id(len, linkid, cmd, 0, &status);
3537 	if (dip == NULL)
3538 		return (DLADM_STATUS_NOMEM);
3539 
3540 	dp = (uchar_t *)dip->pr_val;
3541 	if (set)
3542 		(void) memcpy(dp, buf, len);
3543 
3544 	status = i_dladm_macprop(handle, dip, set);
3545 	if (status == DLADM_STATUS_OK) {
3546 		if (!set)
3547 			(void) memcpy(buf, dp, len);
3548 	}
3549 
3550 	free(dip);
3551 	return (status);
3552 }
3553 
3554 dladm_status_t
3555 dladm_parse_link_props(char *str, dladm_arg_list_t **listp, boolean_t novalues)
3556 {
3557 	return (dladm_parse_args(str, listp, novalues));
3558 }
3559 
3560 /*
3561  * Retrieve the one link property from the database
3562  */
3563 /*ARGSUSED*/
3564 static int
3565 i_dladm_get_one_prop(dladm_handle_t handle, datalink_id_t linkid,
3566     const char *prop_name, void *arg)
3567 {
3568 	dladm_arg_list_t	*proplist = arg;
3569 	dladm_arg_info_t	*aip = NULL;
3570 
3571 	aip = &proplist->al_info[proplist->al_count];
3572 	/*
3573 	 * it is fine to point to prop_name since prop_name points to the
3574 	 * prop_table[n].pd_name.
3575 	 */
3576 	aip->ai_name = prop_name;
3577 
3578 	(void) dladm_get_linkprop(handle, linkid, DLADM_PROP_VAL_PERSISTENT,
3579 	    prop_name, aip->ai_val, &aip->ai_count);
3580 
3581 	if (aip->ai_count != 0)
3582 		proplist->al_count++;
3583 
3584 	return (DLADM_WALK_CONTINUE);
3585 }
3586 
3587 
3588 /*
3589  * Retrieve all link properties for a link from the database and
3590  * return a property list.
3591  */
3592 dladm_status_t
3593 dladm_link_get_proplist(dladm_handle_t handle, datalink_id_t linkid,
3594     dladm_arg_list_t **listp)
3595 {
3596 	dladm_arg_list_t	*list;
3597 	dladm_status_t		status = DLADM_STATUS_OK;
3598 
3599 	list = calloc(1, sizeof (dladm_arg_list_t));
3600 	if (list == NULL)
3601 		return (dladm_errno2status(errno));
3602 
3603 	status = dladm_walk_linkprop(handle, linkid, list,
3604 	    i_dladm_get_one_prop);
3605 
3606 	*listp = list;
3607 	return (status);
3608 }
3609 
3610 /*
3611  * Retrieve the named property from a proplist, check the value and
3612  * convert to a kernel structure.
3613  */
3614 static dladm_status_t
3615 i_dladm_link_proplist_extract_one(dladm_handle_t handle,
3616     dladm_arg_list_t *proplist, const char *name, void *arg)
3617 {
3618 	dladm_status_t		status;
3619 	dladm_arg_info_t	*aip = NULL;
3620 	int			i, j;
3621 
3622 	/* Find named property in proplist */
3623 	for (i = 0; i < proplist->al_count; i++) {
3624 		aip = &proplist->al_info[i];
3625 		if (strcasecmp(aip->ai_name, name) == 0)
3626 			break;
3627 	}
3628 
3629 	/* Property not in list */
3630 	if (i == proplist->al_count)
3631 		return (DLADM_STATUS_OK);
3632 
3633 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
3634 		prop_desc_t	*pdp = &prop_table[i];
3635 		val_desc_t	*vdp;
3636 
3637 		vdp = malloc(sizeof (val_desc_t) * aip->ai_count);
3638 		if (vdp == NULL)
3639 			return (DLADM_STATUS_NOMEM);
3640 
3641 		if (strcasecmp(aip->ai_name, pdp->pd_name) != 0)
3642 			continue;
3643 
3644 		if (aip->ai_val == NULL)
3645 			return (DLADM_STATUS_BADARG);
3646 
3647 		/* Check property value */
3648 		if (pdp->pd_check != NULL) {
3649 			status = pdp->pd_check(handle, pdp, 0, aip->ai_val,
3650 			    aip->ai_count, vdp, 0);
3651 		} else {
3652 			status = DLADM_STATUS_BADARG;
3653 		}
3654 
3655 		if (status != DLADM_STATUS_OK)
3656 			return (status);
3657 
3658 		for (j = 0; j < DLADM_MAX_RSRC_PROP; j++) {
3659 			resource_prop_t	*rpp = &rsrc_prop_table[j];
3660 
3661 			if (strcasecmp(aip->ai_name, rpp->rp_name) != 0)
3662 				continue;
3663 
3664 			/* Extract kernel structure */
3665 			if (rpp->rp_extract != NULL) {
3666 				status = rpp->rp_extract(vdp,
3667 				    aip->ai_count, arg);
3668 			} else {
3669 				status = DLADM_STATUS_BADARG;
3670 			}
3671 			break;
3672 		}
3673 
3674 		if (status != DLADM_STATUS_OK)
3675 			return (status);
3676 
3677 		break;
3678 	}
3679 	return (status);
3680 }
3681 
3682 /*
3683  * Extract properties from a proplist and convert to mac_resource_props_t.
3684  */
3685 dladm_status_t
3686 dladm_link_proplist_extract(dladm_handle_t handle, dladm_arg_list_t *proplist,
3687     mac_resource_props_t *mrp)
3688 {
3689 	dladm_status_t	status;
3690 	int		i;
3691 
3692 	for (i = 0; i < DLADM_MAX_RSRC_PROP; i++) {
3693 		status = i_dladm_link_proplist_extract_one(handle,
3694 		    proplist, rsrc_prop_table[i].rp_name, mrp);
3695 		if (status != DLADM_STATUS_OK)
3696 			return (status);
3697 	}
3698 	return (status);
3699 }
3700 
3701 static const char *
3702 dladm_perm2str(uint_t perm, char *buf)
3703 {
3704 	(void) snprintf(buf, DLADM_STRSIZE, "%c%c",
3705 	    ((perm & MAC_PROP_PERM_READ) != 0) ? 'r' : '-',
3706 	    ((perm & MAC_PROP_PERM_WRITE) != 0) ? 'w' : '-');
3707 	return (buf);
3708 }
3709 
3710 dladm_status_t
3711 i_dladm_get_state(dladm_handle_t handle, datalink_id_t linkid,
3712     link_state_t *state)
3713 {
3714 	dld_ioc_macprop_t	*dip;
3715 	dladm_status_t		status;
3716 	uint_t			perms;
3717 
3718 	dip = i_dladm_get_public_prop(handle, linkid, "state", 0, &status,
3719 	    &perms);
3720 	if (status != DLADM_STATUS_OK)
3721 		return (status);
3722 	(void) memcpy(state, dip->pr_val, sizeof (*state));
3723 	free(dip);
3724 	return (status);
3725 }
3726 
3727 boolean_t
3728 dladm_attr_is_linkprop(const char *name)
3729 {
3730 	/* non-property attribute names */
3731 	const char *nonprop[] = {
3732 		/* dlmgmtd core attributes */
3733 		"name",
3734 		"class",
3735 		"media",
3736 		FPHYMAJ,
3737 		FPHYINST,
3738 		FDEVNAME,
3739 
3740 		/* other attributes for vlan, aggr, etc */
3741 		DLADM_ATTR_NAMES
3742 	};
3743 	boolean_t	is_nonprop = B_FALSE;
3744 	int		i;
3745 
3746 	for (i = 0; i < sizeof (nonprop) / sizeof (nonprop[0]); i++) {
3747 		if (strcmp(name, nonprop[i]) == 0) {
3748 			is_nonprop = B_TRUE;
3749 			break;
3750 		}
3751 	}
3752 
3753 	return (!is_nonprop);
3754 }
3755