xref: /titanic_51/usr/src/lib/libdladm/common/linkprop.c (revision 53a7b6b6763f5865522a76e5e887390a8f4777d7)
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 2008 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 <sys/mac_flow.h>
55 #include <inttypes.h>
56 #include <sys/ethernet.h>
57 #include <net/wpa.h>
58 #include <sys/sysmacros.h>
59 
60 /*
61  * The linkprop get() callback.
62  * - pd: 	pointer to the prop_desc_t
63  * - propstrp:	a property string array to keep the returned property.
64  *		Caller allocated.
65  * - cntp:	number of returned properties.
66  *		Caller also uses it to indicate how many it expects.
67  */
68 struct prop_desc;
69 typedef struct prop_desc prop_desc_t;
70 
71 typedef dladm_status_t	pd_getf_t(prop_desc_t *pdp,
72 			datalink_id_t, char **propstp, uint_t *cntp,
73 			datalink_media_t, uint_t, uint_t *);
74 
75 /*
76  * The linkprop set() callback.
77  * - propval:	a val_desc_t array which keeps the property values to be set.
78  * - cnt:	number of properties to be set.
79  * - flags: 	additional flags passed down the system call.
80  *
81  * pd_set takes val_desc_t given by pd_check(), translates it into
82  * a format suitable for kernel consumption. This may require allocation
83  * of ioctl buffers etc. pd_set() may call another common routine (used
84  * by all other pd_sets) which invokes the ioctl.
85  */
86 typedef dladm_status_t	pd_setf_t(prop_desc_t *, datalink_id_t,
87 			    val_desc_t *propval, uint_t cnt, uint_t flags,
88 			    datalink_media_t);
89 
90 /*
91  * The linkprop check() callback.
92  * - propstrp:	property string array which keeps the property to be checked.
93  * - cnt:	number of properties.
94  * - propval:	return value; the property values of the given property strings.
95  *
96  * pd_check checks that the input values are valid. It does so by
97  * iteraring through the pd_modval list for the property. If
98  * the modifiable values cannot be expressed as a list, a pd_check
99  * specific to this property can be used. If the input values are
100  * verified to be valid, pd_check allocates a val_desc_t and fills it
101  * with either a val_desc_t found on the pd_modval list or something
102  * generated on the fly.
103  */
104 typedef dladm_status_t	pd_checkf_t(prop_desc_t *pdp, datalink_id_t,
105 			    char **propstrp, uint_t cnt, val_desc_t *propval,
106 			    datalink_media_t);
107 
108 typedef struct link_attr_s {
109 	mac_prop_id_t	pp_id;
110 	size_t		pp_valsize;
111 	char		*pp_name;
112 } link_attr_t;
113 
114 static dld_ioc_macprop_t *i_dladm_buf_alloc_by_name(size_t, datalink_id_t,
115 			    const char *, uint_t, dladm_status_t *);
116 static dld_ioc_macprop_t *i_dladm_buf_alloc_by_id(size_t, datalink_id_t,
117 			    mac_prop_id_t, uint_t, dladm_status_t *);
118 static dld_ioc_macprop_t *i_dladm_get_public_prop(datalink_id_t, char *, uint_t,
119 			    dladm_status_t *, uint_t *);
120 
121 static dladm_status_t i_dladm_set_prop(datalink_id_t, const char *, char **,
122 					uint_t, uint_t);
123 static dladm_status_t i_dladm_get_prop(datalink_id_t, const char *, char **,
124 					uint_t *, dladm_prop_type_t, uint_t);
125 static link_attr_t *dladm_name2prop(const char *);
126 static link_attr_t *dladm_id2prop(mac_prop_id_t);
127 
128 static pd_getf_t	do_get_zone, do_get_autopush, do_get_rate_mod,
129 			do_get_rate_prop, do_get_channel_prop,
130 			do_get_powermode_prop, do_get_radio_prop,
131 			i_dladm_duplex_get, i_dladm_status_get,
132 			i_dladm_binary_get, i_dladm_uint32_get,
133 			i_dladm_flowctl_get, dld_maxbw_get, dld_cpus_get,
134 			dld_priority_get;
135 
136 static pd_setf_t	do_set_zone, do_set_rate_prop,
137 			do_set_powermode_prop, do_set_radio_prop,
138 			i_dladm_set_public_prop, do_set_res, do_set_cpus;
139 
140 static pd_checkf_t	do_check_zone, do_check_autopush, do_check_rate,
141 			i_dladm_defmtu_check, do_check_maxbw, do_check_cpus,
142 			do_check_priority;
143 
144 static dladm_status_t	i_dladm_speed_get(prop_desc_t *, datalink_id_t,
145 			char **, uint_t *, uint_t, uint_t *);
146 static dladm_status_t	i_dladm_wlan_get_legacy_ioctl(datalink_id_t, void *,
147 			    uint_t, uint_t);
148 static dladm_status_t	i_dladm_wlan_set_legacy_ioctl(datalink_id_t, void *,
149 			    uint_t, uint_t);
150 static dladm_status_t	i_dladm_macprop(void *, boolean_t);
151 static const char	*dladm_perm2str(uint_t, char *);
152 
153 struct prop_desc {
154 	/*
155 	 * link property name
156 	 */
157 	char			*pd_name;
158 
159 	/*
160 	 * default property value, can be set to { "", NULL }
161 	 */
162 	val_desc_t		pd_defval;
163 
164 	/*
165 	 * list of optional property values, can be NULL.
166 	 *
167 	 * This is set to non-NULL if there is a list of possible property
168 	 * values.  pd_optval would point to the array of possible values.
169 	 */
170 	val_desc_t		*pd_optval;
171 
172 	/*
173 	 * count of the above optional property values. 0 if pd_optval is NULL.
174 	 */
175 	uint_t			pd_noptval;
176 
177 	/*
178 	 * callback to set link property;
179 	 * set to NULL if this property is read-only
180 	 */
181 	pd_setf_t		*pd_set;
182 
183 	/*
184 	 * callback to get modifiable link property
185 	 */
186 	pd_getf_t		*pd_getmod;
187 
188 	/*
189 	 * callback to get current link property
190 	 */
191 	pd_getf_t		*pd_get;
192 
193 	/*
194 	 * callback to validate link property value, set to NULL if pd_optval
195 	 * is not NULL. In that case, validate the value by comparing it with
196 	 * the pd_optval. Return a val_desc_t array pointer if the value is
197 	 * valid.
198 	 */
199 	pd_checkf_t		*pd_check;
200 
201 	uint_t			pd_flags;
202 #define	PD_TEMPONLY	0x1	/* property is temporary only */
203 #define	PD_CHECK_ALLOC	0x2	/* alloc vd_val as part of pd_check */
204 	/*
205 	 * indicate link classes this property applies to.
206 	 */
207 	datalink_class_t	pd_class;
208 
209 	/*
210 	 * indicate link media type this property applies to.
211 	 */
212 	datalink_media_t	pd_dmedia;
213 };
214 
215 #define	MAC_PROP_BUFSIZE(v)	sizeof (dld_ioc_macprop_t) + (v) - 1
216 
217 /*
218  * Supported link properties enumerated in the prop_table[] array are
219  * computed using the callback functions in that array. To compute the
220  * property value, multiple distinct system calls may be needed (e.g.,
221  * for wifi speed, we need to issue system calls to get desired/supported
222  * rates). The link_attr[] table enumerates the interfaces to the kernel,
223  * and the type/size of the data passed in the user-kernel interface.
224  */
225 static link_attr_t link_attr[] = {
226 	{ MAC_PROP_DUPLEX,	sizeof (link_duplex_t),	"duplex"},
227 
228 	{ MAC_PROP_SPEED,	sizeof (uint64_t),	"speed"},
229 
230 	{ MAC_PROP_STATUS,	sizeof (link_state_t),	"state"},
231 
232 	{ MAC_PROP_AUTONEG,	sizeof (uint8_t),	"adv_autoneg_cap"},
233 
234 	{ MAC_PROP_MTU,		sizeof (uint32_t),	"mtu"},
235 
236 	{ MAC_PROP_FLOWCTRL,	sizeof (link_flowctrl_t), "flowctrl"},
237 
238 	{ MAC_PROP_ZONE,	sizeof (dld_ioc_zid_t),	"zone"},
239 
240 	{ MAC_PROP_AUTOPUSH,	sizeof (struct dlautopush), "autopush"},
241 
242 	{ MAC_PROP_ADV_1000FDX_CAP, sizeof (uint8_t),	"adv_1000fdx_cap"},
243 
244 	{ MAC_PROP_EN_1000FDX_CAP, sizeof (uint8_t),	"en_1000fdx_cap"},
245 
246 	{ MAC_PROP_ADV_1000HDX_CAP, sizeof (uint8_t),	"adv_1000hdx_cap"},
247 
248 	{ MAC_PROP_EN_1000HDX_CAP, sizeof (uint8_t),	"en_1000hdx_cap"},
249 
250 	{ MAC_PROP_ADV_100FDX_CAP, sizeof (uint8_t),	"adv_100fdx_cap"},
251 
252 	{ MAC_PROP_EN_100FDX_CAP, sizeof (uint8_t),	"en_100fdx_cap"},
253 
254 	{ MAC_PROP_ADV_100HDX_CAP, sizeof (uint8_t),	"adv_100hdx_cap"},
255 
256 	{ MAC_PROP_EN_100HDX_CAP, sizeof (uint8_t),	"en_100hdx_cap"},
257 
258 	{ MAC_PROP_ADV_10FDX_CAP, sizeof (uint8_t),	"adv_10fdx_cap"},
259 
260 	{ MAC_PROP_EN_10FDX_CAP, sizeof (uint8_t),	"en_10fdx_cap"},
261 
262 	{ MAC_PROP_ADV_10HDX_CAP, sizeof (uint8_t),	"adv_10hdx_cap"},
263 
264 	{ MAC_PROP_EN_10HDX_CAP, sizeof (uint8_t),	"en_10hdx_cap"},
265 
266 	{ MAC_PROP_WL_ESSID,	sizeof (wl_linkstatus_t), "essid"},
267 
268 	{ MAC_PROP_WL_BSSID,	sizeof (wl_bssid_t),	"bssid"},
269 
270 	{ MAC_PROP_WL_BSSTYPE,	sizeof (wl_bss_type_t),	"bsstype"},
271 
272 	{ MAC_PROP_WL_LINKSTATUS, sizeof (wl_linkstatus_t), "wl_linkstatus"},
273 
274 	/* wl_rates_t has variable length */
275 	{ MAC_PROP_WL_DESIRED_RATES, sizeof (wl_rates_t), "desired_rates"},
276 
277 	/* wl_rates_t has variable length */
278 	{ MAC_PROP_WL_SUPPORTED_RATES, sizeof (wl_rates_t), "supported_rates"},
279 
280 	{ MAC_PROP_WL_AUTH_MODE, sizeof (wl_authmode_t), "authmode"},
281 
282 	{ MAC_PROP_WL_ENCRYPTION, sizeof (wl_encryption_t), "encryption"},
283 
284 	{ MAC_PROP_WL_RSSI,	sizeof (wl_rssi_t),	"signal"},
285 
286 	{ MAC_PROP_WL_PHY_CONFIG, sizeof (wl_phy_conf_t), "phy_conf"},
287 
288 	{ MAC_PROP_WL_CAPABILITY, sizeof (wl_capability_t), "capability"},
289 
290 	{ MAC_PROP_WL_WPA,	sizeof (wl_wpa_t),	"wpa"},
291 
292 	/*  wl_wpa_ess_t has variable length */
293 	{ MAC_PROP_WL_SCANRESULTS, sizeof (wl_wpa_ess_t), "scan_results"},
294 
295 	{ MAC_PROP_WL_POWER_MODE, sizeof (wl_ps_mode_t), "powermode"},
296 
297 	{ MAC_PROP_WL_RADIO,	sizeof (dladm_wlan_radio_t), "wl_radio"},
298 
299 	{ MAC_PROP_WL_ESS_LIST, sizeof (wl_ess_list_t),	"wl_ess_list"},
300 
301 	{ MAC_PROP_WL_KEY_TAB,	sizeof (wl_wep_key_tab_t), "wl_wep_key"},
302 
303 	{ MAC_PROP_WL_CREATE_IBSS, sizeof (wl_create_ibss_t), "createibss"},
304 
305 	/* wl_wpa_ie_t has variable length */
306 	{ MAC_PROP_WL_SETOPTIE,	sizeof (wl_wpa_ie_t),	"set_ie"},
307 
308 	{ MAC_PROP_WL_DELKEY,	sizeof (wl_del_key_t),	"wpa_del_key"},
309 
310 	{ MAC_PROP_WL_KEY,	sizeof (wl_key_t),	"wl_key"},
311 
312 	{ MAC_PROP_WL_MLME,	sizeof (wl_mlme_t),	"mlme"},
313 
314 	{ MAC_PROP_MAXBW,	sizeof (mac_resource_props_t),	"maxbw"},
315 
316 	{ MAC_PROP_PRIO,	sizeof (mac_resource_props_t),	"priority"},
317 
318 	{ MAC_PROP_BIND_CPU,	sizeof (mac_resource_props_t),	"cpus"},
319 
320 	{ MAC_PROP_PRIVATE,	0,			"driver-private"}
321 
322 };
323 
324 static  val_desc_t	link_duplex_vals[] = {
325 	{ "half", 	LINK_DUPLEX_HALF	},
326 	{ "full", 	LINK_DUPLEX_HALF	}
327 };
328 static  val_desc_t	link_status_vals[] = {
329 	{ "up",		LINK_STATE_UP		},
330 	{ "down",	LINK_STATE_DOWN		}
331 };
332 static  val_desc_t	link_01_vals[] = {
333 	{ "1",		1			},
334 	{ "0",		0			}
335 };
336 static  val_desc_t	link_flow_vals[] = {
337 	{ "no",		LINK_FLOWCTRL_NONE	},
338 	{ "tx",		LINK_FLOWCTRL_TX	},
339 	{ "rx",		LINK_FLOWCTRL_RX	},
340 	{ "bi",		LINK_FLOWCTRL_BI	}
341 };
342 static  val_desc_t	link_priority_vals[] = {
343 	{ "low",	MPL_LOW	},
344 	{ "medium",	MPL_MEDIUM	},
345 	{ "high",	MPL_HIGH	}
346 };
347 
348 static val_desc_t	dladm_wlan_radio_vals[] = {
349 	{ "on",		DLADM_WLAN_RADIO_ON	},
350 	{ "off",	DLADM_WLAN_RADIO_OFF	}
351 };
352 
353 static val_desc_t	dladm_wlan_powermode_vals[] = {
354 	{ "off",	DLADM_WLAN_PM_OFF	},
355 	{ "fast",	DLADM_WLAN_PM_FAST	},
356 	{ "max",	DLADM_WLAN_PM_MAX	}
357 };
358 
359 #define	VALCNT(vals)    (sizeof ((vals)) / sizeof (val_desc_t))
360 #define	RESET_VAL	((uintptr_t)-1)
361 
362 static prop_desc_t	prop_table[] = {
363 	{ "channel",	{ NULL, 0 },
364 	    NULL, 0, NULL, NULL,
365 	    do_get_channel_prop, NULL, 0,
366 	    DATALINK_CLASS_PHYS, DL_WIFI },
367 
368 	{ "powermode",	{ "off", DLADM_WLAN_PM_OFF },
369 	    dladm_wlan_powermode_vals, VALCNT(dladm_wlan_powermode_vals),
370 	    do_set_powermode_prop, NULL,
371 	    do_get_powermode_prop, NULL, 0,
372 	    DATALINK_CLASS_PHYS, DL_WIFI },
373 
374 	{ "radio",	{ "on", DLADM_WLAN_RADIO_ON },
375 	    dladm_wlan_radio_vals, VALCNT(dladm_wlan_radio_vals),
376 	    do_set_radio_prop, NULL,
377 	    do_get_radio_prop, NULL, 0,
378 	    DATALINK_CLASS_PHYS, DL_WIFI },
379 
380 	{ "speed",	{ "", 0 }, NULL, 0,
381 	    do_set_rate_prop, do_get_rate_mod,
382 	    do_get_rate_prop, do_check_rate, 0,
383 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE },
384 
385 	{ "autopush",	{ "", 0 }, NULL, 0,
386 	    i_dladm_set_public_prop, NULL,
387 	    do_get_autopush, do_check_autopush, PD_CHECK_ALLOC,
388 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
389 
390 	{ "zone",	{ "", 0 }, NULL, 0,
391 	    do_set_zone, NULL,
392 	    do_get_zone, do_check_zone, PD_TEMPONLY|PD_CHECK_ALLOC,
393 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
394 
395 	{ "duplex",	{ "", 0 },
396 	    link_duplex_vals, VALCNT(link_duplex_vals),
397 	    NULL, NULL, i_dladm_duplex_get, NULL,
398 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
399 
400 	{ "state",	{ "up", LINK_STATE_UP },
401 	    link_status_vals, VALCNT(link_status_vals),
402 	    NULL, NULL, i_dladm_status_get, NULL,
403 	    0, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
404 
405 	{ "adv_autoneg_cap", { "1", 1 },
406 	    link_01_vals, VALCNT(link_01_vals),
407 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
408 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
409 
410 	{ "mtu", { "", 0 }, NULL, 0,
411 	    i_dladm_set_public_prop, NULL, i_dladm_uint32_get,
412 	    i_dladm_defmtu_check, 0, DATALINK_CLASS_ALL,
413 	    DATALINK_ANY_MEDIATYPE },
414 
415 	{ "flowctrl", { "", 0 },
416 	    link_flow_vals, VALCNT(link_flow_vals),
417 	    i_dladm_set_public_prop, NULL, i_dladm_flowctl_get, NULL,
418 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
419 
420 	{ "adv_1000fdx_cap", { "", 0 },
421 	    link_01_vals, VALCNT(link_01_vals),
422 	    NULL, NULL, i_dladm_binary_get, NULL,
423 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
424 
425 	{ "en_1000fdx_cap", { "", 0 },
426 	    link_01_vals, VALCNT(link_01_vals),
427 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
428 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
429 
430 	{ "adv_1000hdx_cap", { "", 0 },
431 	    link_01_vals, VALCNT(link_01_vals),
432 	    NULL, NULL, i_dladm_binary_get, NULL,
433 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
434 
435 	{ "en_1000hdx_cap", { "", 0 },
436 	    link_01_vals, VALCNT(link_01_vals),
437 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
438 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
439 
440 	{ "adv_100fdx_cap", { "", 0 },
441 	    link_01_vals, VALCNT(link_01_vals),
442 	    NULL, NULL, i_dladm_binary_get, NULL,
443 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
444 
445 	{ "en_100fdx_cap", { "", 0 },
446 	    link_01_vals, VALCNT(link_01_vals),
447 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
448 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
449 
450 	{ "adv_100hdx_cap", { "", 0 },
451 	    link_01_vals, VALCNT(link_01_vals),
452 	    NULL, NULL, i_dladm_binary_get, NULL,
453 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
454 
455 	{ "en_100hdx_cap", { "", 0 },
456 	    link_01_vals, VALCNT(link_01_vals),
457 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
458 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
459 
460 	{ "adv_10fdx_cap", { "", 0 },
461 	    link_01_vals, VALCNT(link_01_vals),
462 	    NULL, NULL, i_dladm_binary_get, NULL,
463 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
464 
465 	{ "en_10fdx_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 	{ "adv_10hdx_cap", { "", 0 },
471 	    link_01_vals, VALCNT(link_01_vals),
472 	    NULL, NULL, i_dladm_binary_get, NULL,
473 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
474 
475 	{ "en_10hdx_cap", { "", 0 },
476 	    link_01_vals, VALCNT(link_01_vals),
477 	    i_dladm_set_public_prop, NULL, i_dladm_binary_get, NULL,
478 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
479 
480 	{ "maxbw", { "--", RESET_VAL }, NULL, 0,
481 	    do_set_res, NULL,
482 	    dld_maxbw_get, do_check_maxbw, PD_CHECK_ALLOC,
483 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
484 
485 	{ "cpus", { "--", RESET_VAL }, NULL, 0,
486 	    do_set_cpus, NULL,
487 	    dld_cpus_get, do_check_cpus, 0,
488 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
489 
490 	{ "priority", { "high", RESET_VAL },
491 	    link_priority_vals, VALCNT(link_priority_vals), do_set_res, NULL,
492 	    dld_priority_get, do_check_priority, PD_CHECK_ALLOC,
493 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
494 };
495 
496 #define	DLADM_MAX_PROPS	(sizeof (prop_table) / sizeof (prop_desc_t))
497 
498 static resource_prop_t rsrc_prop_table[] = {
499 	{"maxbw",	do_extract_maxbw},
500 	{"priority",	do_extract_priority},
501 	{"cpus",	do_extract_cpus}
502 };
503 #define	DLADM_MAX_RSRC_PROP (sizeof (rsrc_prop_table) / \
504 	sizeof (resource_prop_t))
505 
506 /*
507  * when retrieving  private properties, we pass down a buffer with
508  * DLADM_PROP_BUF_CHUNK of space for the driver to return the property value.
509  */
510 #define	DLADM_PROP_BUF_CHUNK	1024
511 
512 static dladm_status_t	i_dladm_set_linkprop_db(datalink_id_t, const char *,
513 			    char **, uint_t);
514 static dladm_status_t	i_dladm_get_linkprop_db(datalink_id_t, const char *,
515 			    char **, uint_t *);
516 static dladm_status_t	i_dladm_set_single_prop(datalink_id_t, datalink_class_t,
517 			    uint32_t, prop_desc_t *, char **, uint_t, uint_t);
518 static dladm_status_t	i_dladm_set_linkprop(datalink_id_t, const char *,
519 			    char **, uint_t, uint_t);
520 static dladm_status_t	i_dladm_getset_defval(prop_desc_t *, datalink_id_t,
521 			    datalink_media_t, uint_t);
522 
523 static dladm_status_t	link_proplist_check(dladm_arg_list_t *);
524 
525 /*
526  * Unfortunately, MAX_SCAN_SUPPORT_RATES is too small to allow all
527  * rates to be retrieved. However, we cannot increase it at this
528  * time because it will break binary compatibility with unbundled
529  * WiFi drivers and utilities. So for now we define an additional
530  * constant, MAX_SUPPORT_RATES, to allow all rates to be retrieved.
531  */
532 #define	MAX_SUPPORT_RATES	64
533 
534 #define	AP_ANCHOR	"[anchor]"
535 #define	AP_DELIMITER	'.'
536 
537 static dladm_status_t
538 do_check_prop(prop_desc_t *pdp, char **prop_val, uint_t val_cnt,
539     val_desc_t *vdp)
540 {
541 	int		i, j;
542 	dladm_status_t	status = DLADM_STATUS_OK;
543 
544 	for (j = 0; j < val_cnt; j++) {
545 		for (i = 0; i < pdp->pd_noptval; i++) {
546 			if (strcasecmp(*prop_val,
547 			    pdp->pd_optval[i].vd_name) == 0) {
548 				break;
549 			}
550 		}
551 		if (i == pdp->pd_noptval) {
552 			status = DLADM_STATUS_BADVAL;
553 			goto done;
554 		}
555 		(void) memcpy(vdp + j, &pdp->pd_optval[i], sizeof (val_desc_t));
556 	}
557 
558 done:
559 	return (status);
560 }
561 
562 static dladm_status_t
563 i_dladm_set_single_prop(datalink_id_t linkid, datalink_class_t class,
564     uint32_t media, prop_desc_t *pdp, char **prop_val, uint_t val_cnt,
565     uint_t flags)
566 {
567 	dladm_status_t	status = DLADM_STATUS_OK;
568 	val_desc_t	*vdp = NULL;
569 	boolean_t	needfree = B_FALSE;
570 	uint_t		cnt, i;
571 
572 	if (!(pdp->pd_class & class))
573 		return (DLADM_STATUS_BADARG);
574 
575 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
576 		return (DLADM_STATUS_BADARG);
577 
578 	if ((flags & DLADM_OPT_PERSIST) && (pdp->pd_flags & PD_TEMPONLY))
579 		return (DLADM_STATUS_TEMPONLY);
580 
581 	if (!(flags & DLADM_OPT_ACTIVE))
582 		return (DLADM_STATUS_OK);
583 
584 	if (pdp->pd_set == NULL)
585 		return (DLADM_STATUS_PROPRDONLY);
586 
587 	if (prop_val != NULL) {
588 		vdp = malloc(sizeof (val_desc_t) * val_cnt);
589 		if (vdp == NULL)
590 			return (DLADM_STATUS_NOMEM);
591 
592 		if (pdp->pd_check != NULL) {
593 			needfree = ((pdp->pd_flags & PD_CHECK_ALLOC) != 0);
594 			status = pdp->pd_check(pdp, linkid, prop_val, val_cnt,
595 			    vdp, media);
596 		} else if (pdp->pd_optval != NULL) {
597 			status = do_check_prop(pdp, prop_val, val_cnt, vdp);
598 		} else {
599 			status = DLADM_STATUS_BADARG;
600 		}
601 
602 		if (status != DLADM_STATUS_OK)
603 			goto done;
604 
605 		cnt = val_cnt;
606 	} else {
607 		boolean_t	defval = B_FALSE;
608 
609 		if (pdp->pd_defval.vd_name == NULL)
610 			return (DLADM_STATUS_NOTSUP);
611 
612 		cnt = 1;
613 		defval = (strlen(pdp->pd_defval.vd_name) > 0);
614 		if ((pdp->pd_flags & PD_CHECK_ALLOC) != 0 || defval) {
615 			if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
616 				return (DLADM_STATUS_NOMEM);
617 
618 			if (defval) {
619 				(void) memcpy(vdp, &pdp->pd_defval,
620 				    sizeof (val_desc_t));
621 			} else if (pdp->pd_check != NULL) {
622 				status = pdp->pd_check(pdp, linkid, prop_val,
623 				    cnt, vdp, media);
624 				if (status != DLADM_STATUS_OK)
625 					goto done;
626 			}
627 		} else {
628 			status = i_dladm_getset_defval(pdp, linkid,
629 			    media, flags);
630 			return (status);
631 		}
632 	}
633 	status = pdp->pd_set(pdp, linkid, vdp, cnt, flags, media);
634 	if (needfree) {
635 		for (i = 0; i < cnt; i++)
636 			free((void *)((val_desc_t *)vdp + i)->vd_val);
637 	}
638 done:
639 	free(vdp);
640 	return (status);
641 }
642 
643 static dladm_status_t
644 i_dladm_set_linkprop(datalink_id_t linkid, const char *prop_name,
645     char **prop_val, uint_t val_cnt, uint_t flags)
646 {
647 	int			i;
648 	boolean_t		found = B_FALSE;
649 	datalink_class_t	class;
650 	uint32_t		media;
651 	dladm_status_t		status = DLADM_STATUS_OK;
652 
653 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
654 	if (status != DLADM_STATUS_OK)
655 		return (status);
656 
657 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
658 		prop_desc_t	*pdp = &prop_table[i];
659 		dladm_status_t	s;
660 
661 		if (prop_name != NULL &&
662 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
663 			continue;
664 		found = B_TRUE;
665 		s = i_dladm_set_single_prop(linkid, class, media, pdp, prop_val,
666 		    val_cnt, flags);
667 
668 		if (prop_name != NULL) {
669 			status = s;
670 			break;
671 		} else {
672 			if (s != DLADM_STATUS_OK &&
673 			    s != DLADM_STATUS_NOTSUP)
674 				status = s;
675 		}
676 	}
677 	if (!found) {
678 		if (prop_name[0] == '_') {
679 			/* other private properties */
680 			status = i_dladm_set_prop(linkid, prop_name, prop_val,
681 			    val_cnt, flags);
682 		} else  {
683 			status = DLADM_STATUS_NOTFOUND;
684 		}
685 	}
686 
687 	return (status);
688 }
689 
690 /*
691  * Set/reset link property for specific link
692  */
693 dladm_status_t
694 dladm_set_linkprop(datalink_id_t linkid, const char *prop_name, char **prop_val,
695     uint_t val_cnt, uint_t flags)
696 {
697 	dladm_status_t	status = DLADM_STATUS_OK;
698 
699 	if ((linkid == DATALINK_INVALID_LINKID) || (flags == 0) ||
700 	    (prop_val == NULL && val_cnt > 0) ||
701 	    (prop_val != NULL && val_cnt == 0) ||
702 	    (prop_name == NULL && prop_val != NULL)) {
703 		return (DLADM_STATUS_BADARG);
704 	}
705 
706 	status = i_dladm_set_linkprop(linkid, prop_name, prop_val,
707 	    val_cnt, flags);
708 	if (status != DLADM_STATUS_OK)
709 		return (status);
710 
711 	if (flags & DLADM_OPT_PERSIST) {
712 		status = i_dladm_set_linkprop_db(linkid, prop_name,
713 		    prop_val, val_cnt);
714 	}
715 	return (status);
716 }
717 
718 /*
719  * Walk link properties of the given specific link.
720  */
721 dladm_status_t
722 dladm_walk_linkprop(datalink_id_t linkid, void *arg,
723     int (*func)(datalink_id_t, const char *, void *))
724 {
725 	dladm_status_t		status;
726 	datalink_class_t	class;
727 	uint_t			media;
728 	int			i;
729 
730 	if (linkid == DATALINK_INVALID_LINKID || func == NULL)
731 		return (DLADM_STATUS_BADARG);
732 
733 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
734 	if (status != DLADM_STATUS_OK)
735 		return (status);
736 
737 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
738 		if (!(prop_table[i].pd_class & class))
739 			continue;
740 
741 		if (!DATALINK_MEDIA_ACCEPTED(prop_table[i].pd_dmedia, media))
742 			continue;
743 
744 		if (func(linkid, prop_table[i].pd_name, arg) ==
745 		    DLADM_WALK_TERMINATE) {
746 			break;
747 		}
748 	}
749 
750 	return (DLADM_STATUS_OK);
751 }
752 
753 /*
754  * Get linkprop of the given specific link.
755  */
756 dladm_status_t
757 dladm_get_linkprop(datalink_id_t linkid, dladm_prop_type_t type,
758     const char *prop_name, char **prop_val, uint_t *val_cntp)
759 {
760 	dladm_status_t		status = DLADM_STATUS_OK;
761 	datalink_class_t	class;
762 	uint_t			media;
763 	prop_desc_t		*pdp;
764 	uint_t			cnt, dld_flags = 0;
765 	int			i;
766 	uint_t			perm_flags;
767 
768 	if (type == DLADM_PROP_VAL_DEFAULT)
769 		dld_flags = MAC_PROP_DEFAULT;
770 
771 	if (linkid == DATALINK_INVALID_LINKID || prop_name == NULL ||
772 	    prop_val == NULL || val_cntp == NULL || *val_cntp == 0)
773 		return (DLADM_STATUS_BADARG);
774 
775 	for (i = 0; i < DLADM_MAX_PROPS; i++)
776 		if (strcasecmp(prop_name, prop_table[i].pd_name) == 0)
777 			break;
778 
779 	if (i == DLADM_MAX_PROPS) {
780 		if (prop_name[0] == '_') {
781 			/*
782 			 * private property.
783 			 */
784 			return (i_dladm_get_prop(linkid, prop_name,
785 			    prop_val, val_cntp, type, dld_flags));
786 		} else {
787 			return (DLADM_STATUS_NOTFOUND);
788 		}
789 	}
790 
791 	pdp = &prop_table[i];
792 
793 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
794 	if (status != DLADM_STATUS_OK)
795 		return (status);
796 
797 	if (!(pdp->pd_class & class))
798 		return (DLADM_STATUS_BADARG);
799 
800 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
801 		return (DLADM_STATUS_BADARG);
802 
803 	switch (type) {
804 	case DLADM_PROP_VAL_CURRENT:
805 		status = pdp->pd_get(pdp, linkid, prop_val, val_cntp, media,
806 		    dld_flags, &perm_flags);
807 		break;
808 
809 	case DLADM_PROP_VAL_PERM:
810 		if (pdp->pd_set == NULL) {
811 			perm_flags = MAC_PROP_PERM_READ;
812 			*val_cntp = 1;
813 		} else {
814 			status = pdp->pd_get(pdp, linkid, prop_val, val_cntp,
815 			    media, dld_flags, &perm_flags);
816 		}
817 
818 		*prop_val[0] = '\0';
819 		if (status == DLADM_STATUS_OK)
820 			(void) dladm_perm2str(perm_flags, *prop_val);
821 		break;
822 
823 	case DLADM_PROP_VAL_DEFAULT:
824 		/*
825 		 * If defaults are not defined for the property,
826 		 * pd_defval.vd_name should be null. If the driver
827 		 * has to be contacted for the value, vd_name should
828 		 * be the empty string (""). Otherwise, dladm will
829 		 * just print whatever is in the table.
830 		 */
831 		if (pdp->pd_defval.vd_name == NULL) {
832 			status = DLADM_STATUS_NOTSUP;
833 			break;
834 		}
835 
836 		if (strlen(pdp->pd_defval.vd_name) == 0) {
837 			status = pdp->pd_get(pdp, linkid, prop_val, val_cntp,
838 			    media, dld_flags, &perm_flags);
839 		} else {
840 			(void) strcpy(*prop_val, pdp->pd_defval.vd_name);
841 		}
842 		*val_cntp = 1;
843 		break;
844 
845 	case DLADM_PROP_VAL_MODIFIABLE:
846 		if (pdp->pd_getmod != NULL) {
847 			status = pdp->pd_getmod(pdp, linkid, prop_val,
848 			    val_cntp, media, dld_flags, &perm_flags);
849 			break;
850 		}
851 		cnt = pdp->pd_noptval;
852 		if (cnt == 0) {
853 			status = DLADM_STATUS_NOTSUP;
854 		} else if (cnt > *val_cntp) {
855 			status = DLADM_STATUS_TOOSMALL;
856 		} else {
857 			for (i = 0; i < cnt; i++) {
858 				(void) strcpy(prop_val[i],
859 				    pdp->pd_optval[i].vd_name);
860 			}
861 			*val_cntp = cnt;
862 		}
863 		break;
864 	case DLADM_PROP_VAL_PERSISTENT:
865 		if (pdp->pd_flags & PD_TEMPONLY)
866 			return (DLADM_STATUS_TEMPONLY);
867 		status = i_dladm_get_linkprop_db(linkid, prop_name,
868 		    prop_val, val_cntp);
869 		break;
870 	default:
871 		status = DLADM_STATUS_BADARG;
872 		break;
873 	}
874 
875 	return (status);
876 }
877 
878 /*ARGSUSED*/
879 static int
880 i_dladm_init_one_prop(datalink_id_t linkid, const char *prop_name, void *arg)
881 {
882 	char	*buf, **propvals;
883 	uint_t	i, valcnt = DLADM_MAX_PROP_VALCNT;
884 
885 	if ((buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
886 	    DLADM_MAX_PROP_VALCNT)) == NULL) {
887 		return (DLADM_WALK_CONTINUE);
888 	}
889 
890 	propvals = (char **)(void *)buf;
891 	for (i = 0; i < valcnt; i++) {
892 		propvals[i] = buf +
893 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
894 		    i * DLADM_PROP_VAL_MAX;
895 	}
896 
897 	if (dladm_get_linkprop(linkid, DLADM_PROP_VAL_PERSISTENT, prop_name,
898 	    propvals, &valcnt) != DLADM_STATUS_OK) {
899 		goto done;
900 	}
901 
902 	(void) dladm_set_linkprop(linkid, prop_name, propvals, valcnt,
903 	    DLADM_OPT_ACTIVE);
904 
905 done:
906 	if (buf != NULL)
907 		free(buf);
908 
909 	return (DLADM_WALK_CONTINUE);
910 }
911 
912 /*ARGSUSED*/
913 static int
914 i_dladm_init_linkprop(datalink_id_t linkid, void *arg)
915 {
916 	datalink_class_t	class;
917 	dladm_status_t		status;
918 
919 	status = dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0);
920 	if (status != DLADM_STATUS_OK)
921 		return (DLADM_WALK_TERMINATE);
922 
923 	if ((class & (DATALINK_CLASS_VNIC | DATALINK_CLASS_VLAN)) == 0)
924 		(void) dladm_init_linkprop(linkid, B_TRUE);
925 
926 	return (DLADM_WALK_CONTINUE);
927 }
928 
929 dladm_status_t
930 dladm_init_linkprop(datalink_id_t linkid, boolean_t any_media)
931 {
932 	datalink_media_t	dmedia;
933 	uint32_t		media;
934 
935 	dmedia = any_media ? DATALINK_ANY_MEDIATYPE : DL_WIFI;
936 
937 	if (linkid == DATALINK_ALL_LINKID) {
938 		(void) dladm_walk_datalink_id(i_dladm_init_linkprop, NULL,
939 		    DATALINK_CLASS_ALL, dmedia, DLADM_OPT_PERSIST);
940 	} else if (any_media || ((dladm_datalink_id2info(linkid, NULL, NULL,
941 	    &media, NULL, 0) == DLADM_STATUS_OK) &&
942 	    DATALINK_MEDIA_ACCEPTED(dmedia, media))) {
943 		(void) dladm_walk_linkprop(linkid, NULL, i_dladm_init_one_prop);
944 	}
945 	return (DLADM_STATUS_OK);
946 }
947 
948 /* ARGSUSED */
949 static dladm_status_t
950 do_get_zone(prop_desc_t *pdp, datalink_id_t linkid,
951     char **prop_val, uint_t *val_cnt, datalink_media_t media,
952     uint_t flags, uint_t *perm_flags)
953 {
954 	char			zone_name[ZONENAME_MAX];
955 	zoneid_t		zid;
956 	dladm_status_t		status;
957 	char			*cp;
958 	dld_ioc_macprop_t	*dip;
959 
960 	if (flags != 0)
961 		return (DLADM_STATUS_NOTSUP);
962 
963 	dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
964 	    &status, perm_flags);
965 	if (status != DLADM_STATUS_OK)
966 		return (status);
967 
968 	cp = dip->pr_val;
969 	(void) memcpy(&zid, cp, sizeof (zid));
970 	free(dip);
971 
972 	*val_cnt = 1;
973 	if (zid != GLOBAL_ZONEID) {
974 		if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0) {
975 			return (dladm_errno2status(errno));
976 		}
977 
978 		(void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX);
979 	} else {
980 		*prop_val[0] = '\0';
981 	}
982 
983 	return (DLADM_STATUS_OK);
984 }
985 
986 typedef int (*zone_get_devroot_t)(char *, char *, size_t);
987 
988 static int
989 i_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen)
990 {
991 	char			root[MAXPATHLEN];
992 	zone_get_devroot_t	real_zone_get_devroot;
993 	void			*dlhandle;
994 	void			*sym;
995 	int			ret;
996 
997 	if ((dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY)) == NULL)
998 		return (-1);
999 
1000 	if ((sym = dlsym(dlhandle, "zone_get_devroot")) == NULL) {
1001 		(void) dlclose(dlhandle);
1002 		return (-1);
1003 	}
1004 
1005 	real_zone_get_devroot = (zone_get_devroot_t)sym;
1006 
1007 	if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0)
1008 		(void) snprintf(dev, devlen, "%s%s", root, "/dev");
1009 	(void) dlclose(dlhandle);
1010 	return (ret);
1011 }
1012 
1013 static dladm_status_t
1014 i_dladm_update_deventry(zoneid_t zid, datalink_id_t linkid, boolean_t add)
1015 {
1016 	char		path[MAXPATHLEN];
1017 	char		name[MAXLINKNAMELEN];
1018 	di_prof_t	prof = NULL;
1019 	char		zone_name[ZONENAME_MAX];
1020 	dladm_status_t	status;
1021 	int		ret;
1022 
1023 	if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
1024 		return (dladm_errno2status(errno));
1025 	if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
1026 		return (dladm_errno2status(errno));
1027 	if (di_prof_init(path, &prof) != 0)
1028 		return (dladm_errno2status(errno));
1029 
1030 	status = dladm_linkid2legacyname(linkid, name, MAXLINKNAMELEN);
1031 	if (status != DLADM_STATUS_OK)
1032 		goto cleanup;
1033 
1034 	if (add)
1035 		ret = di_prof_add_dev(prof, name);
1036 	else
1037 		ret = di_prof_add_exclude(prof, name);
1038 
1039 	if (ret != 0) {
1040 		status = dladm_errno2status(errno);
1041 		goto cleanup;
1042 	}
1043 
1044 	if (di_prof_commit(prof) != 0)
1045 		status = dladm_errno2status(errno);
1046 cleanup:
1047 	if (prof)
1048 		di_prof_fini(prof);
1049 
1050 	return (status);
1051 }
1052 
1053 /* ARGSUSED */
1054 static dladm_status_t
1055 do_set_zone(prop_desc_t *pdp, datalink_id_t linkid, val_desc_t *vdp,
1056     uint_t val_cnt, uint_t flags, datalink_media_t media)
1057 {
1058 	dladm_status_t		status = DLADM_STATUS_OK;
1059 	zoneid_t		zid_old, zid_new;
1060 	char			link[MAXLINKNAMELEN];
1061 	char			*cp;
1062 	dld_ioc_macprop_t	*dip;
1063 	dld_ioc_zid_t		*dzp;
1064 
1065 	if (val_cnt != 1)
1066 		return (DLADM_STATUS_BADVALCNT);
1067 
1068 	dzp = (dld_ioc_zid_t *)vdp->vd_val;
1069 
1070 	dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
1071 	    &status, NULL);
1072 	if (status != DLADM_STATUS_OK)
1073 		return (status);
1074 
1075 	cp = dip->pr_val;
1076 	(void) memcpy(&zid_old, cp, sizeof (zid_old));
1077 	free(dip);
1078 
1079 	zid_new = dzp->diz_zid;
1080 	(void) strlcpy(link, dzp->diz_link, MAXLINKNAMELEN);
1081 
1082 	/* Do nothing if setting to current value */
1083 	if (zid_new == zid_old)
1084 		return (status);
1085 
1086 	if (zid_new != GLOBAL_ZONEID) {
1087 		/*
1088 		 * If the new zoneid is the global zone, we could destroy
1089 		 * the link (in the case of an implicitly-created VLAN) as a
1090 		 * result of setting the zoneid. In that case, we defer the
1091 		 * operation to the end of this function to avoid recreating
1092 		 * the VLAN and getting a different linkid during the rollback
1093 		 * if other operation fails.
1094 		 *
1095 		 * Otherwise, this operation will hold a reference to the
1096 		 * link and prevent a link renaming, so we need to do it
1097 		 * before other operations.
1098 		 */
1099 		status = i_dladm_set_public_prop(pdp, linkid, vdp, val_cnt,
1100 		    flags, media);
1101 		if (status != DLADM_STATUS_OK)
1102 			return (status);
1103 	}
1104 
1105 	if (zid_old != GLOBAL_ZONEID) {
1106 		if (zone_remove_datalink(zid_old, link) != 0 &&
1107 		    errno != ENXIO) {
1108 			status = dladm_errno2status(errno);
1109 			goto rollback1;
1110 		}
1111 
1112 		/*
1113 		 * It is okay to fail to update the /dev entry (some
1114 		 * vanity-named links do not have a /dev entry).
1115 		 */
1116 		(void) i_dladm_update_deventry(zid_old, linkid, B_FALSE);
1117 	}
1118 
1119 	if (zid_new != GLOBAL_ZONEID) {
1120 		if (zone_add_datalink(zid_new, link) != 0) {
1121 			status = dladm_errno2status(errno);
1122 			goto rollback2;
1123 		}
1124 
1125 		(void) i_dladm_update_deventry(zid_new, linkid, B_TRUE);
1126 	} else {
1127 		status = i_dladm_set_public_prop(pdp, linkid, vdp, val_cnt,
1128 		    flags, media);
1129 		if (status != DLADM_STATUS_OK)
1130 			goto rollback2;
1131 	}
1132 
1133 	return (DLADM_STATUS_OK);
1134 
1135 rollback2:
1136 	if (zid_old != GLOBAL_ZONEID)
1137 		(void) i_dladm_update_deventry(zid_old, linkid, B_TRUE);
1138 	if (zid_old != GLOBAL_ZONEID)
1139 		(void) zone_add_datalink(zid_old, link);
1140 rollback1:
1141 	if (zid_new != GLOBAL_ZONEID) {
1142 		dzp->diz_zid = zid_old;
1143 		(void) i_dladm_set_public_prop(pdp, linkid, vdp, val_cnt,
1144 		    flags, media);
1145 	}
1146 
1147 	return (status);
1148 }
1149 
1150 /* ARGSUSED */
1151 static dladm_status_t
1152 do_check_zone(prop_desc_t *pdp, datalink_id_t linkid, char **prop_val,
1153     uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
1154 {
1155 	char		*zone_name;
1156 	char		linkname[MAXLINKNAMELEN];
1157 	zoneid_t	zoneid;
1158 	dladm_status_t	status = DLADM_STATUS_OK;
1159 	dld_ioc_zid_t	*dzp;
1160 
1161 	if (val_cnt != 1)
1162 		return (DLADM_STATUS_BADVALCNT);
1163 
1164 	dzp = malloc(sizeof (dld_ioc_zid_t));
1165 	if (dzp == NULL)
1166 		return (DLADM_STATUS_NOMEM);
1167 
1168 	if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL,
1169 	    linkname, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
1170 		goto done;
1171 	}
1172 
1173 	zone_name = (prop_val != NULL) ? *prop_val : GLOBAL_ZONENAME;
1174 	if (strlen(linkname) > MAXLINKNAMELEN) {
1175 		status = DLADM_STATUS_BADVAL;
1176 		goto done;
1177 	}
1178 
1179 	if ((zoneid = getzoneidbyname(zone_name)) == -1) {
1180 		status = DLADM_STATUS_BADVAL;
1181 		goto done;
1182 	}
1183 
1184 	if (zoneid != GLOBAL_ZONEID) {
1185 		ushort_t	flags;
1186 
1187 		if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &flags,
1188 		    sizeof (flags)) < 0) {
1189 			status = dladm_errno2status(errno);
1190 			goto done;
1191 		}
1192 
1193 		if (!(flags & ZF_NET_EXCL)) {
1194 			status = DLADM_STATUS_BADVAL;
1195 			goto done;
1196 		}
1197 	}
1198 
1199 	(void) memset(dzp, 0, sizeof (dld_ioc_zid_t));
1200 
1201 	dzp->diz_zid = zoneid;
1202 	(void) strlcpy(dzp->diz_link, linkname, MAXLINKNAMELEN);
1203 
1204 	vdp->vd_val = (uintptr_t)dzp;
1205 	return (DLADM_STATUS_OK);
1206 done:
1207 	free(dzp);
1208 	return (status);
1209 }
1210 
1211 /* ARGSUSED */
1212 static dladm_status_t
1213 dld_maxbw_get(prop_desc_t *pdp, datalink_id_t linkid,
1214     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1215     uint_t flags, uint_t *perm_flags)
1216 {
1217 	dld_ioc_macprop_t	*dip;
1218 	mac_resource_props_t	mrp;
1219 	dladm_status_t		status;
1220 
1221 	dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
1222 	    &status, perm_flags);
1223 	if (dip == NULL)
1224 		return (status);
1225 
1226 	bcopy(dip->pr_val, &mrp, sizeof (mac_resource_props_t));
1227 	free(dip);
1228 
1229 	if ((mrp.mrp_mask & MRP_MAXBW) == 0) {
1230 		(*prop_val)[0] = '\0';
1231 	} else {
1232 		(void) dladm_bw2str(mrp.mrp_maxbw, prop_val[0]);
1233 	}
1234 	*val_cnt = 1;
1235 	return (DLADM_STATUS_OK);
1236 }
1237 
1238 /* ARGSUSED */
1239 static dladm_status_t
1240 do_check_maxbw(prop_desc_t *pdp, datalink_id_t linkid, char **prop_val,
1241     uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
1242 {
1243 	uint64_t	*maxbw;
1244 	dladm_status_t	status = DLADM_STATUS_OK;
1245 
1246 	if (val_cnt != 1)
1247 		return (DLADM_STATUS_BADVALCNT);
1248 
1249 	maxbw = malloc(sizeof (uint64_t));
1250 	if (maxbw == NULL)
1251 		return (DLADM_STATUS_NOMEM);
1252 
1253 	status = dladm_str2bw(*prop_val, maxbw);
1254 	if (status != DLADM_STATUS_OK) {
1255 		free(maxbw);
1256 		return (status);
1257 	}
1258 
1259 	if ((*maxbw < MRP_MAXBW_MINVAL) && (*maxbw != 0)) {
1260 		free(maxbw);
1261 		return (DLADM_STATUS_MINMAXBW);
1262 	}
1263 
1264 	vdp->vd_val = (uintptr_t)maxbw;
1265 	return (DLADM_STATUS_OK);
1266 }
1267 
1268 /* ARGSUSED */
1269 dladm_status_t
1270 do_extract_maxbw(val_desc_t *vdp, void *arg, uint_t cnt)
1271 {
1272 	mac_resource_props_t *mrp = (mac_resource_props_t *)arg;
1273 
1274 	bcopy((char *)vdp->vd_val, &mrp->mrp_maxbw, sizeof (uint64_t));
1275 	mrp->mrp_mask |= MRP_MAXBW;
1276 
1277 	return (DLADM_STATUS_OK);
1278 }
1279 
1280 /* ARGSUSED */
1281 static dladm_status_t
1282 dld_cpus_get(prop_desc_t *pdp, datalink_id_t linkid,
1283     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1284     uint_t flags, uint_t *perm_flags)
1285 {
1286 	dld_ioc_macprop_t	*dip;
1287 	mac_resource_props_t	mrp;
1288 	int			i;
1289 	uint32_t		ncpus;
1290 	uchar_t			*cp;
1291 	dladm_status_t		status;
1292 
1293 	dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
1294 	    &status, perm_flags);
1295 	if (dip == NULL)
1296 		return (status);
1297 
1298 	cp = (uchar_t *)dip->pr_val;
1299 	(void) memcpy(&mrp, cp, sizeof (mac_resource_props_t));
1300 	free(dip);
1301 
1302 	ncpus = mrp.mrp_ncpus;
1303 
1304 	if (ncpus > *val_cnt)
1305 		return (DLADM_STATUS_TOOSMALL);
1306 
1307 	if (ncpus == 0) {
1308 		(*prop_val)[0] = '\0';
1309 		*val_cnt = 1;
1310 		return (DLADM_STATUS_OK);
1311 	}
1312 
1313 	*val_cnt = ncpus;
1314 	for (i = 0; i < ncpus; i++) {
1315 		(void) snprintf(prop_val[i], DLADM_PROP_VAL_MAX,
1316 		    "%u", mrp.mrp_cpu[i]);
1317 	}
1318 	return (DLADM_STATUS_OK);
1319 }
1320 
1321 /* ARGSUSED */
1322 static dladm_status_t
1323 do_set_res(prop_desc_t *pdp, datalink_id_t linkid, val_desc_t *vdp,
1324     uint_t val_cnt, uint_t flags, datalink_media_t media)
1325 {
1326 	mac_resource_props_t	mrp;
1327 	dladm_status_t		status = DLADM_STATUS_OK;
1328 	dld_ioc_macprop_t	*dip;
1329 
1330 	bzero(&mrp, sizeof (mac_resource_props_t));
1331 	dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name,
1332 	    flags, &status);
1333 
1334 	if (dip == NULL)
1335 		return (status);
1336 
1337 	if (vdp->vd_val == RESET_VAL) {
1338 		switch (dip->pr_num) {
1339 		case MAC_PROP_MAXBW:
1340 			mrp.mrp_maxbw = MRP_MAXBW_RESETVAL;
1341 			mrp.mrp_mask = MRP_MAXBW;
1342 			break;
1343 		case MAC_PROP_PRIO:
1344 			mrp.mrp_priority = MPL_RESET;
1345 			mrp.mrp_mask = MRP_PRIORITY;
1346 			break;
1347 		default:
1348 			free(dip);
1349 			return (DLADM_STATUS_BADARG);
1350 		}
1351 	} else {
1352 		switch (dip->pr_num) {
1353 		case MAC_PROP_MAXBW:
1354 			bcopy((void *)vdp->vd_val, &mrp.mrp_maxbw,
1355 			    sizeof (uint64_t));
1356 			mrp.mrp_mask = MRP_MAXBW;
1357 			break;
1358 		case MAC_PROP_PRIO:
1359 			bcopy((void *)vdp->vd_val, &mrp.mrp_priority,
1360 			    sizeof (mac_priority_level_t));
1361 			mrp.mrp_mask = MRP_PRIORITY;
1362 			break;
1363 		default:
1364 			free(dip);
1365 			return (DLADM_STATUS_BADARG);
1366 		}
1367 	}
1368 
1369 	(void) memcpy(dip->pr_val, &mrp, dip->pr_valsize);
1370 	status = i_dladm_macprop(dip, B_TRUE);
1371 	free(dip);
1372 	return (status);
1373 }
1374 
1375 /* ARGSUSED */
1376 static dladm_status_t
1377 do_set_cpus(prop_desc_t *pdp, datalink_id_t linkid, val_desc_t *vdp,
1378     uint_t val_cnt, uint_t flags, datalink_media_t media)
1379 {
1380 	mac_resource_props_t	mrp;
1381 	dladm_status_t		status;
1382 	dld_ioc_macprop_t	*dip;
1383 	datalink_class_t	class;
1384 
1385 	/*
1386 	 * CPU bindings can be set on VNIC and regular physical links.
1387 	 * However VNICs fails the dladm_phys_info test(). So apply
1388 	 * the phys_info test only on physical links.
1389 	 */
1390 	if ((status = dladm_datalink_id2info(linkid, NULL, &class,
1391 	    NULL, NULL, 0)) != DLADM_STATUS_OK) {
1392 		return (status);
1393 	}
1394 
1395 	/*
1396 	 * We set intr_cpu to -1. The interrupt will be retargetted,
1397 	 * if possible when the setup is complete in MAC.
1398 	 */
1399 	bzero(&mrp, sizeof (mac_resource_props_t));
1400 	mrp.mrp_mask = MRP_CPUS;
1401 	if (vdp != NULL && vdp->vd_val != RESET_VAL) {
1402 		mac_resource_props_t	*vmrp;
1403 
1404 		vmrp = (mac_resource_props_t *)vdp->vd_val;
1405 		if (vmrp->mrp_ncpus > 0) {
1406 			bcopy(vmrp, &mrp, sizeof (mac_resource_props_t));
1407 			mrp.mrp_mask = MRP_CPUS;
1408 		}
1409 		mrp.mrp_mask |= MRP_CPUS_USERSPEC;
1410 		mrp.mrp_fanout_mode = MCM_CPUS;
1411 		mrp.mrp_intr_cpu = -1;
1412 	}
1413 
1414 	dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name,
1415 	    flags, &status);
1416 	if (dip == NULL)
1417 		return (status);
1418 
1419 	(void) memcpy(dip->pr_val, &mrp, dip->pr_valsize);
1420 	status = i_dladm_macprop(dip, B_TRUE);
1421 	free(dip);
1422 	return (status);
1423 }
1424 
1425 /* ARGSUSED */
1426 static dladm_status_t
1427 do_check_cpus(prop_desc_t *pdp, datalink_id_t linkid, char **prop_val,
1428     uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
1429 {
1430 	uint32_t		cpuid;
1431 	int			i, j, rc;
1432 	long			nproc = sysconf(_SC_NPROCESSORS_CONF);
1433 	mac_resource_props_t	*mrp;
1434 
1435 	mrp = malloc(sizeof (mac_resource_props_t));
1436 	if (mrp == NULL)
1437 		return (DLADM_STATUS_NOMEM);
1438 
1439 	for (i = 0; i < val_cnt; i++) {
1440 		errno = 0;
1441 		cpuid = strtol(prop_val[i], (char **)NULL, 10);
1442 		if (errno != 0 || cpuid >= nproc) {
1443 			free(mrp);
1444 			return (DLADM_STATUS_CPUMAX);
1445 		}
1446 		rc = p_online(cpuid, P_STATUS);
1447 		if (rc < 1) {
1448 			free(mrp);
1449 			return (DLADM_STATUS_CPUERR);
1450 		}
1451 		if (rc != P_ONLINE) {
1452 			free(mrp);
1453 			return (DLADM_STATUS_CPUNOTONLINE);
1454 		}
1455 		mrp->mrp_cpu[i] = cpuid;
1456 	}
1457 	mrp->mrp_ncpus = (uint32_t)val_cnt;
1458 
1459 	/* Check for duplicates */
1460 	for (i = 0; i < val_cnt; i++) {
1461 		for (j = 0; j < val_cnt; j++) {
1462 			if (i != j && mrp->mrp_cpu[i] == mrp->mrp_cpu[j]) {
1463 				free(mrp);
1464 				return (DLADM_STATUS_BADARG);
1465 			}
1466 		}
1467 	}
1468 	vdp->vd_val = (uintptr_t)mrp;
1469 
1470 	return (DLADM_STATUS_OK);
1471 }
1472 
1473 /* ARGSUSED */
1474 dladm_status_t
1475 do_extract_cpus(val_desc_t *vdp, void *arg, uint_t cnt)
1476 {
1477 	mac_resource_props_t	*mrp = (mac_resource_props_t *)arg;
1478 	mac_resource_props_t	*vmrp = (mac_resource_props_t *)vdp->vd_val;
1479 	int			i;
1480 
1481 	for (i = 0; i < vmrp->mrp_ncpus; i++) {
1482 		mrp->mrp_cpu[i] = vmrp->mrp_cpu[i];
1483 	}
1484 	mrp->mrp_ncpus = vmrp->mrp_ncpus;
1485 	mrp->mrp_mask |= (MRP_CPUS|MRP_CPUS_USERSPEC);
1486 	mrp->mrp_fanout_mode = MCM_CPUS;
1487 
1488 	return (DLADM_STATUS_OK);
1489 }
1490 
1491 /* ARGSUSED */
1492 static dladm_status_t
1493 dld_priority_get(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 	mac_priority_level_t	pri;
1500 	dladm_status_t		status;
1501 
1502 	dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
1503 	    &status, perm_flags);
1504 	if (dip == NULL)
1505 		return (status);
1506 
1507 	bcopy(dip->pr_val, &mrp, sizeof (mac_resource_props_t));
1508 	free(dip);
1509 
1510 	pri = ((mrp.mrp_mask & MRP_PRIORITY) == 0) ? MPL_HIGH :
1511 	    mrp.mrp_priority;
1512 
1513 	(void) dladm_pri2str(pri, prop_val[0]);
1514 	*val_cnt = 1;
1515 	return (DLADM_STATUS_OK);
1516 }
1517 
1518 /* ARGSUSED */
1519 static dladm_status_t
1520 do_check_priority(prop_desc_t *pdp, datalink_id_t linkid, char **prop_val,
1521     uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
1522 {
1523 	mac_priority_level_t	*pri;
1524 	dladm_status_t	status = DLADM_STATUS_OK;
1525 
1526 	if (val_cnt != 1)
1527 		return (DLADM_STATUS_BADVALCNT);
1528 
1529 	pri = malloc(sizeof (mac_priority_level_t));
1530 	if (pri == NULL)
1531 		return (DLADM_STATUS_NOMEM);
1532 
1533 	status = dladm_str2pri(*prop_val, pri);
1534 	if (status != DLADM_STATUS_OK) {
1535 		free(pri);
1536 		return (status);
1537 	}
1538 
1539 	if (*pri < MPL_LOW || *pri > MPL_HIGH) {
1540 		free(pri);
1541 		return (DLADM_STATUS_BADVAL);
1542 	}
1543 
1544 	vdp->vd_val = (uintptr_t)pri;
1545 	return (DLADM_STATUS_OK);
1546 }
1547 
1548 /* ARGSUSED */
1549 dladm_status_t
1550 do_extract_priority(val_desc_t *vdp, void *arg, uint_t cnt)
1551 {
1552 	mac_resource_props_t *mrp = (mac_resource_props_t *)arg;
1553 
1554 	bcopy((char *)vdp->vd_val, &mrp->mrp_priority,
1555 	    sizeof (mac_priority_level_t));
1556 	mrp->mrp_mask |= MRP_PRIORITY;
1557 
1558 	return (DLADM_STATUS_OK);
1559 }
1560 
1561 /* ARGSUSED */
1562 static dladm_status_t
1563 do_get_autopush(prop_desc_t *pdp, datalink_id_t linkid,
1564     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1565     uint_t flags, uint_t *perm_flags)
1566 {
1567 	struct		dlautopush dlap;
1568 	int		i, len;
1569 	dladm_status_t	status;
1570 	dld_ioc_macprop_t	*dip;
1571 
1572 	if (flags & MAC_PROP_DEFAULT)
1573 		return (DLADM_STATUS_NOTDEFINED);
1574 
1575 	*val_cnt = 1;
1576 	dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
1577 	    &status, perm_flags);
1578 	if (dip == NULL) {
1579 		(*prop_val)[0] = '\0';
1580 		return (DLADM_STATUS_OK);
1581 	}
1582 	(void) memcpy(&dlap, dip->pr_val, sizeof (dlap));
1583 
1584 	for (i = 0, len = 0; i < dlap.dap_npush; i++) {
1585 		if (i != 0) {
1586 			(void) snprintf(*prop_val + len,
1587 			    DLADM_PROP_VAL_MAX - len, "%c", AP_DELIMITER);
1588 			len += 1;
1589 		}
1590 		(void) snprintf(*prop_val + len, DLADM_PROP_VAL_MAX - len,
1591 		    "%s", dlap.dap_aplist[i]);
1592 		len += strlen(dlap.dap_aplist[i]);
1593 		if (dlap.dap_anchor - 1 == i) {
1594 			(void) snprintf(*prop_val + len,
1595 			    DLADM_PROP_VAL_MAX - len, "%c%s", AP_DELIMITER,
1596 			    AP_ANCHOR);
1597 			len += (strlen(AP_ANCHOR) + 1);
1598 		}
1599 	}
1600 	free(dip);
1601 done:
1602 	return (DLADM_STATUS_OK);
1603 }
1604 
1605 /*
1606  * Add the specified module to the dlautopush structure; returns a
1607  * DLADM_STATUS_* code.
1608  */
1609 dladm_status_t
1610 i_dladm_add_ap_module(const char *module, struct dlautopush *dlap)
1611 {
1612 	if ((strlen(module) == 0) || (strlen(module) > FMNAMESZ))
1613 		return (DLADM_STATUS_BADVAL);
1614 
1615 	if (strncasecmp(module, AP_ANCHOR, strlen(AP_ANCHOR)) == 0) {
1616 		/*
1617 		 * We don't allow multiple anchors, and the anchor must
1618 		 * be after at least one module.
1619 		 */
1620 		if (dlap->dap_anchor != 0)
1621 			return (DLADM_STATUS_BADVAL);
1622 		if (dlap->dap_npush == 0)
1623 			return (DLADM_STATUS_BADVAL);
1624 
1625 		dlap->dap_anchor = dlap->dap_npush;
1626 		return (DLADM_STATUS_OK);
1627 	}
1628 	if (dlap->dap_npush > MAXAPUSH)
1629 		return (DLADM_STATUS_BADVALCNT);
1630 
1631 	(void) strlcpy(dlap->dap_aplist[dlap->dap_npush++], module,
1632 	    FMNAMESZ + 1);
1633 
1634 	return (DLADM_STATUS_OK);
1635 }
1636 
1637 /*
1638  * Currently, both '.' and ' '(space) can be used as the delimiters between
1639  * autopush modules. The former is used in dladm set-linkprop, and the
1640  * latter is used in the autopush(1M) file.
1641  */
1642 /* ARGSUSED */
1643 static dladm_status_t
1644 do_check_autopush(prop_desc_t *pdp, datalink_id_t linkid, char **prop_val,
1645     uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
1646 {
1647 	char			*module;
1648 	struct dlautopush	*dlap;
1649 	dladm_status_t		status;
1650 	char			val[DLADM_PROP_VAL_MAX];
1651 	char			delimiters[4];
1652 
1653 	if (val_cnt != 1)
1654 		return (DLADM_STATUS_BADVALCNT);
1655 
1656 	if (prop_val != NULL) {
1657 		dlap = malloc(sizeof (struct dlautopush));
1658 		if (dlap == NULL)
1659 			return (DLADM_STATUS_NOMEM);
1660 
1661 		(void) memset(dlap, 0, sizeof (struct dlautopush));
1662 		(void) snprintf(delimiters, 4, " %c\n", AP_DELIMITER);
1663 		bcopy(*prop_val, val, DLADM_PROP_VAL_MAX);
1664 		module = strtok(val, delimiters);
1665 		while (module != NULL) {
1666 			status = i_dladm_add_ap_module(module, dlap);
1667 			if (status != DLADM_STATUS_OK)
1668 				return (status);
1669 			module = strtok(NULL, delimiters);
1670 		}
1671 
1672 		vdp->vd_val = (uintptr_t)dlap;
1673 	} else {
1674 		vdp->vd_val = 0;
1675 	}
1676 	return (DLADM_STATUS_OK);
1677 }
1678 
1679 #define	WLDP_BUFSIZE (MAX_BUF_LEN - WIFI_BUF_OFFSET)
1680 
1681 /* ARGSUSED */
1682 static dladm_status_t
1683 do_get_rate_common(prop_desc_t *pdp, datalink_id_t linkid,
1684     char **prop_val, uint_t *val_cnt, uint_t id, uint_t *perm_flags)
1685 {
1686 	wl_rates_t	*wrp;
1687 	uint_t		i;
1688 	dladm_status_t	status = DLADM_STATUS_OK;
1689 
1690 	wrp = malloc(WLDP_BUFSIZE);
1691 	if (wrp == NULL)
1692 		return (DLADM_STATUS_NOMEM);
1693 
1694 	status = i_dladm_wlan_param(linkid, wrp, id, WLDP_BUFSIZE, B_FALSE);
1695 	if (status != DLADM_STATUS_OK)
1696 		goto done;
1697 
1698 	if (wrp->wl_rates_num > *val_cnt) {
1699 		status = DLADM_STATUS_TOOSMALL;
1700 		goto done;
1701 	}
1702 
1703 	if (wrp->wl_rates_rates[0] == 0) {
1704 		prop_val[0][0] = '\0';
1705 		*val_cnt = 1;
1706 		goto done;
1707 	}
1708 
1709 	for (i = 0; i < wrp->wl_rates_num; i++) {
1710 		(void) snprintf(prop_val[i], DLADM_STRSIZE, "%.*f",
1711 		    wrp->wl_rates_rates[i] % 2,
1712 		    (float)wrp->wl_rates_rates[i] / 2);
1713 	}
1714 	*val_cnt = wrp->wl_rates_num;
1715 	*perm_flags = MAC_PROP_PERM_RW;
1716 
1717 done:
1718 	free(wrp);
1719 	return (status);
1720 }
1721 
1722 static dladm_status_t
1723 do_get_rate_prop(prop_desc_t *pdp, datalink_id_t linkid,
1724     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1725     uint_t flags, uint_t *perm_flags)
1726 {
1727 	if (media != DL_WIFI) {
1728 		return (i_dladm_speed_get(pdp, linkid, prop_val,
1729 		    val_cnt, flags, perm_flags));
1730 	}
1731 
1732 	return (do_get_rate_common(pdp, linkid, prop_val, val_cnt,
1733 	    MAC_PROP_WL_DESIRED_RATES, perm_flags));
1734 }
1735 
1736 /* ARGSUSED */
1737 static dladm_status_t
1738 do_get_rate_mod(prop_desc_t *pdp, datalink_id_t linkid,
1739     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1740     uint_t flags, uint_t *perm_flags)
1741 {
1742 	switch (media) {
1743 	case DL_ETHER:
1744 		/*
1745 		 * Speed for ethernet links is unbounded. E.g., 802.11b
1746 		 * links can have a speed of 5.5 Gbps.
1747 		 */
1748 		return (DLADM_STATUS_NOTSUP);
1749 
1750 	case DL_WIFI:
1751 		return (do_get_rate_common(pdp, linkid, prop_val, val_cnt,
1752 		    MAC_PROP_WL_SUPPORTED_RATES, perm_flags));
1753 	default:
1754 		return (DLADM_STATUS_BADARG);
1755 	}
1756 }
1757 
1758 static dladm_status_t
1759 do_set_rate(datalink_id_t linkid, dladm_wlan_rates_t *rates)
1760 {
1761 	int		i;
1762 	uint_t		len;
1763 	wl_rates_t	*wrp;
1764 	dladm_status_t	status = DLADM_STATUS_OK;
1765 
1766 	wrp = malloc(WLDP_BUFSIZE);
1767 	if (wrp == NULL)
1768 		return (DLADM_STATUS_NOMEM);
1769 
1770 	bzero(wrp, WLDP_BUFSIZE);
1771 	for (i = 0; i < rates->wr_cnt; i++)
1772 		wrp->wl_rates_rates[i] = rates->wr_rates[i];
1773 	wrp->wl_rates_num = rates->wr_cnt;
1774 
1775 	len = offsetof(wl_rates_t, wl_rates_rates) +
1776 	    (rates->wr_cnt * sizeof (char)) + WIFI_BUF_OFFSET;
1777 	status = i_dladm_wlan_param(linkid, wrp, MAC_PROP_WL_DESIRED_RATES,
1778 	    len, B_TRUE);
1779 
1780 	free(wrp);
1781 	return (status);
1782 }
1783 
1784 /* ARGSUSED */
1785 static dladm_status_t
1786 do_set_rate_prop(prop_desc_t *pdp, datalink_id_t linkid,
1787     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
1788 {
1789 	dladm_wlan_rates_t	rates;
1790 	dladm_status_t		status;
1791 
1792 	/*
1793 	 * can currently set rate on WIFI links only.
1794 	 */
1795 	if (media != DL_WIFI)
1796 		return (DLADM_STATUS_PROPRDONLY);
1797 
1798 	if (val_cnt != 1)
1799 		return (DLADM_STATUS_BADVALCNT);
1800 
1801 	rates.wr_cnt = 1;
1802 	rates.wr_rates[0] = vdp[0].vd_val;
1803 
1804 	status = do_set_rate(linkid, &rates);
1805 
1806 done:
1807 	return (status);
1808 }
1809 
1810 /* ARGSUSED */
1811 static dladm_status_t
1812 do_check_rate(prop_desc_t *pdp, datalink_id_t linkid, char **prop_val,
1813     uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
1814 {
1815 	int		i;
1816 	uint_t		modval_cnt = MAX_SUPPORT_RATES;
1817 	char		*buf, **modval;
1818 	dladm_status_t	status;
1819 	uint_t 		perm_flags;
1820 
1821 	if (val_cnt != 1)
1822 		return (DLADM_STATUS_BADVALCNT);
1823 
1824 	buf = malloc((sizeof (char *) + DLADM_STRSIZE) *
1825 	    MAX_SUPPORT_RATES);
1826 	if (buf == NULL) {
1827 		status = DLADM_STATUS_NOMEM;
1828 		goto done;
1829 	}
1830 
1831 	modval = (char **)(void *)buf;
1832 	for (i = 0; i < MAX_SUPPORT_RATES; i++) {
1833 		modval[i] = buf + sizeof (char *) * MAX_SUPPORT_RATES +
1834 		    i * DLADM_STRSIZE;
1835 	}
1836 
1837 	status = do_get_rate_mod(NULL, linkid, modval, &modval_cnt, media,
1838 	    0, &perm_flags);
1839 	if (status != DLADM_STATUS_OK)
1840 		goto done;
1841 
1842 	for (i = 0; i < modval_cnt; i++) {
1843 		if (strcasecmp(*prop_val, modval[i]) == 0) {
1844 			vdp->vd_val = (uintptr_t)(uint_t)
1845 			    (atof(*prop_val) * 2);
1846 			status = DLADM_STATUS_OK;
1847 			break;
1848 		}
1849 	}
1850 	if (i == modval_cnt)
1851 		status = DLADM_STATUS_BADVAL;
1852 done:
1853 	free(buf);
1854 	return (status);
1855 }
1856 
1857 static dladm_status_t
1858 do_get_phyconf(datalink_id_t linkid, void *buf, int buflen)
1859 {
1860 	return (i_dladm_wlan_param(linkid, buf, MAC_PROP_WL_PHY_CONFIG,
1861 	    buflen, B_FALSE));
1862 }
1863 
1864 /* ARGSUSED */
1865 static dladm_status_t
1866 do_get_channel_prop(prop_desc_t *pdp, datalink_id_t linkid,
1867     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1868     uint_t flags, uint_t *perm_flags)
1869 {
1870 	uint32_t	channel;
1871 	char		buf[WLDP_BUFSIZE];
1872 	dladm_status_t	status = DLADM_STATUS_OK;
1873 	wl_phy_conf_t	wl_phy_conf;
1874 
1875 	if ((status = do_get_phyconf(linkid, buf, sizeof (buf)))
1876 	    != DLADM_STATUS_OK)
1877 		goto done;
1878 
1879 	(void) memcpy(&wl_phy_conf, buf, sizeof (wl_phy_conf));
1880 	if (!i_dladm_wlan_convert_chan(&wl_phy_conf, &channel)) {
1881 		status = DLADM_STATUS_NOTFOUND;
1882 		goto done;
1883 	}
1884 
1885 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%u", channel);
1886 	*val_cnt = 1;
1887 	*perm_flags = MAC_PROP_PERM_READ;
1888 done:
1889 	return (status);
1890 }
1891 
1892 static dladm_status_t
1893 do_get_powermode(datalink_id_t linkid, void *buf, int buflen)
1894 {
1895 	return (i_dladm_wlan_param(linkid, buf, MAC_PROP_WL_POWER_MODE,
1896 	    buflen, B_FALSE));
1897 }
1898 
1899 /* ARGSUSED */
1900 static dladm_status_t
1901 do_get_powermode_prop(prop_desc_t *pdp, datalink_id_t linkid,
1902     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1903     uint_t flags, uint_t *perm_flags)
1904 {
1905 	wl_ps_mode_t	mode;
1906 	const char	*s;
1907 	char		buf[WLDP_BUFSIZE];
1908 	dladm_status_t	status = DLADM_STATUS_OK;
1909 
1910 	if ((status = do_get_powermode(linkid, buf, sizeof (buf)))
1911 	    != DLADM_STATUS_OK)
1912 		goto done;
1913 
1914 	(void) memcpy(&mode, buf, sizeof (mode));
1915 	switch (mode.wl_ps_mode) {
1916 	case WL_PM_AM:
1917 		s = "off";
1918 		break;
1919 	case WL_PM_MPS:
1920 		s = "max";
1921 		break;
1922 	case WL_PM_FAST:
1923 		s = "fast";
1924 		break;
1925 	default:
1926 		status = DLADM_STATUS_NOTFOUND;
1927 		goto done;
1928 	}
1929 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
1930 	*val_cnt = 1;
1931 	*perm_flags = MAC_PROP_PERM_RW;
1932 done:
1933 	return (status);
1934 }
1935 
1936 static dladm_status_t
1937 do_set_powermode(datalink_id_t linkid, dladm_wlan_powermode_t *pm)
1938 {
1939 	wl_ps_mode_t    ps_mode;
1940 
1941 	(void) memset(&ps_mode, 0xff, sizeof (ps_mode));
1942 
1943 	switch (*pm) {
1944 	case DLADM_WLAN_PM_OFF:
1945 		ps_mode.wl_ps_mode = WL_PM_AM;
1946 		break;
1947 	case DLADM_WLAN_PM_MAX:
1948 		ps_mode.wl_ps_mode = WL_PM_MPS;
1949 		break;
1950 	case DLADM_WLAN_PM_FAST:
1951 		ps_mode.wl_ps_mode = WL_PM_FAST;
1952 		break;
1953 	default:
1954 		return (DLADM_STATUS_NOTSUP);
1955 	}
1956 	return (i_dladm_wlan_param(linkid, &ps_mode, MAC_PROP_WL_POWER_MODE,
1957 	    sizeof (ps_mode), B_TRUE));
1958 }
1959 
1960 /* ARGSUSED */
1961 static dladm_status_t
1962 do_set_powermode_prop(prop_desc_t *pdp, datalink_id_t linkid,
1963     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
1964 {
1965 	dladm_wlan_powermode_t powermode = (dladm_wlan_powermode_t)vdp->vd_val;
1966 	dladm_status_t status;
1967 
1968 	if (val_cnt != 1)
1969 		return (DLADM_STATUS_BADVALCNT);
1970 
1971 	status = do_set_powermode(linkid, &powermode);
1972 
1973 	return (status);
1974 }
1975 
1976 static dladm_status_t
1977 do_get_radio(datalink_id_t linkid, void *buf, int buflen)
1978 {
1979 	return (i_dladm_wlan_param(linkid, buf, MAC_PROP_WL_RADIO, buflen,
1980 	    B_FALSE));
1981 }
1982 
1983 /* ARGSUSED */
1984 static dladm_status_t
1985 do_get_radio_prop(prop_desc_t *pdp, datalink_id_t linkid,
1986     char **prop_val, uint_t *val_cnt, datalink_media_t media,
1987     uint_t flags, uint_t *perm_flags)
1988 {
1989 	wl_radio_t	radio;
1990 	const char	*s;
1991 	char		buf[WLDP_BUFSIZE];
1992 	dladm_status_t	status = DLADM_STATUS_OK;
1993 
1994 	if ((status = do_get_radio(linkid, buf, sizeof (buf)))
1995 	    != DLADM_STATUS_OK)
1996 		goto done;
1997 
1998 	(void) memcpy(&radio, buf, sizeof (radio));
1999 	switch (radio) {
2000 	case B_TRUE:
2001 		s = "on";
2002 		break;
2003 	case B_FALSE:
2004 		s = "off";
2005 		break;
2006 	default:
2007 		status = DLADM_STATUS_NOTFOUND;
2008 		goto done;
2009 	}
2010 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
2011 	*val_cnt = 1;
2012 	*perm_flags = MAC_PROP_PERM_RW;
2013 done:
2014 	return (status);
2015 }
2016 
2017 static dladm_status_t
2018 do_set_radio(datalink_id_t linkid, dladm_wlan_radio_t *radio)
2019 {
2020 	wl_radio_t r;
2021 
2022 	switch (*radio) {
2023 	case DLADM_WLAN_RADIO_ON:
2024 		r = B_TRUE;
2025 		break;
2026 	case DLADM_WLAN_RADIO_OFF:
2027 		r = B_FALSE;
2028 		break;
2029 	default:
2030 		return (DLADM_STATUS_NOTSUP);
2031 	}
2032 	return (i_dladm_wlan_param(linkid, &r, MAC_PROP_WL_RADIO,
2033 	    sizeof (r), B_TRUE));
2034 }
2035 
2036 /* ARGSUSED */
2037 static dladm_status_t
2038 do_set_radio_prop(prop_desc_t *pdp, datalink_id_t linkid,
2039     val_desc_t *vdp, uint_t val_cnt, uint_t fags, datalink_media_t media)
2040 {
2041 	dladm_wlan_radio_t radio = (dladm_wlan_radio_t)vdp->vd_val;
2042 	dladm_status_t status;
2043 
2044 	if (val_cnt != 1)
2045 		return (DLADM_STATUS_BADVALCNT);
2046 
2047 	status = do_set_radio(linkid, &radio);
2048 
2049 	return (status);
2050 }
2051 
2052 static dladm_status_t
2053 i_dladm_set_linkprop_db(datalink_id_t linkid, const char *prop_name,
2054     char **prop_val, uint_t val_cnt)
2055 {
2056 	char		buf[MAXLINELEN];
2057 	int		i;
2058 	dladm_conf_t	conf;
2059 	dladm_status_t	status;
2060 
2061 	status = dladm_read_conf(linkid, &conf);
2062 	if (status != DLADM_STATUS_OK)
2063 		return (status);
2064 
2065 	/*
2066 	 * reset case.
2067 	 */
2068 	if (val_cnt == 0) {
2069 		status = dladm_unset_conf_field(conf, prop_name);
2070 		if (status == DLADM_STATUS_OK)
2071 			status = dladm_write_conf(conf);
2072 		goto done;
2073 	}
2074 
2075 	buf[0] = '\0';
2076 	for (i = 0; i < val_cnt; i++) {
2077 		(void) strlcat(buf, prop_val[i], MAXLINELEN);
2078 		if (i != val_cnt - 1)
2079 			(void) strlcat(buf, ",", MAXLINELEN);
2080 	}
2081 
2082 	status = dladm_set_conf_field(conf, prop_name, DLADM_TYPE_STR, buf);
2083 	if (status == DLADM_STATUS_OK)
2084 		status = dladm_write_conf(conf);
2085 
2086 done:
2087 	dladm_destroy_conf(conf);
2088 	return (status);
2089 }
2090 
2091 static dladm_status_t
2092 i_dladm_get_linkprop_db(datalink_id_t linkid, const char *prop_name,
2093     char **prop_val, uint_t *val_cntp)
2094 {
2095 	char		buf[MAXLINELEN], *str;
2096 	uint_t		cnt = 0;
2097 	dladm_conf_t	conf;
2098 	dladm_status_t	status;
2099 
2100 	status = dladm_read_conf(linkid, &conf);
2101 	if (status != DLADM_STATUS_OK)
2102 		return (status);
2103 
2104 	status = dladm_get_conf_field(conf, prop_name, buf, MAXLINELEN);
2105 	if (status != DLADM_STATUS_OK)
2106 		goto done;
2107 
2108 	str = strtok(buf, ",");
2109 	while (str != NULL) {
2110 		if (cnt == *val_cntp) {
2111 			status = DLADM_STATUS_TOOSMALL;
2112 			goto done;
2113 		}
2114 		(void) strlcpy(prop_val[cnt++], str, DLADM_PROP_VAL_MAX);
2115 		str = strtok(NULL, ",");
2116 	}
2117 
2118 	*val_cntp = cnt;
2119 
2120 done:
2121 	dladm_destroy_conf(conf);
2122 	return (status);
2123 }
2124 
2125 static link_attr_t *
2126 dladm_name2prop(const char *prop_name)
2127 {
2128 	link_attr_t *p;
2129 
2130 	for (p = link_attr; p->pp_id != MAC_PROP_PRIVATE; p++) {
2131 		if (strcmp(p->pp_name, prop_name) == 0)
2132 			break;
2133 	}
2134 	return (p);
2135 }
2136 
2137 static link_attr_t *
2138 dladm_id2prop(mac_prop_id_t propid)
2139 {
2140 	link_attr_t *p;
2141 
2142 	for (p = link_attr; p->pp_id != MAC_PROP_PRIVATE; p++) {
2143 		if (p->pp_id == propid)
2144 			break;
2145 	}
2146 	return (p);
2147 }
2148 
2149 static dld_ioc_macprop_t *
2150 i_dladm_buf_alloc_impl(size_t valsize, datalink_id_t linkid,
2151     const char *prop_name, mac_prop_id_t propid, uint_t flags,
2152     dladm_status_t *status)
2153 {
2154 	int dsize;
2155 	dld_ioc_macprop_t *dip;
2156 
2157 	*status = DLADM_STATUS_OK;
2158 	dsize = MAC_PROP_BUFSIZE(valsize);
2159 	dip = malloc(dsize);
2160 	if (dip == NULL) {
2161 		*status = DLADM_STATUS_NOMEM;
2162 		return (NULL);
2163 	}
2164 	bzero(dip, dsize);
2165 	dip->pr_valsize = valsize;
2166 	(void) strlcpy(dip->pr_name, prop_name, sizeof (dip->pr_name));
2167 	dip->pr_version = MAC_PROP_VERSION;
2168 	dip->pr_linkid = linkid;
2169 	dip->pr_num = propid;
2170 	dip->pr_flags = flags;
2171 	return (dip);
2172 }
2173 
2174 static dld_ioc_macprop_t *
2175 i_dladm_buf_alloc_by_name(size_t valsize, datalink_id_t linkid,
2176     const char *prop_name, uint_t flags, dladm_status_t *status)
2177 {
2178 	link_attr_t *p;
2179 
2180 	p = dladm_name2prop(prop_name);
2181 	valsize = MAX(p->pp_valsize, valsize);
2182 	return (i_dladm_buf_alloc_impl(valsize, linkid, prop_name, p->pp_id,
2183 	    flags, status));
2184 }
2185 
2186 static dld_ioc_macprop_t *
2187 i_dladm_buf_alloc_by_id(size_t valsize, datalink_id_t linkid,
2188     mac_prop_id_t propid, uint_t flags, dladm_status_t *status)
2189 {
2190 	link_attr_t *p;
2191 
2192 	p = dladm_id2prop(propid);
2193 	valsize = MAX(p->pp_valsize, valsize);
2194 	return (i_dladm_buf_alloc_impl(valsize, linkid, p->pp_name, propid,
2195 	    flags, status));
2196 }
2197 
2198 /* ARGSUSED */
2199 static dladm_status_t
2200 i_dladm_set_public_prop(prop_desc_t *pdp, datalink_id_t linkid,
2201     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
2202 {
2203 	dld_ioc_macprop_t	*dip;
2204 	dladm_status_t	status = DLADM_STATUS_OK;
2205 	uint8_t		u8;
2206 	uint16_t	u16;
2207 	uint32_t	u32;
2208 	void		*val;
2209 
2210 	dip = i_dladm_buf_alloc_by_name(0, linkid, pdp->pd_name, 0, &status);
2211 	if (dip == NULL)
2212 		return (status);
2213 
2214 	if (pdp->pd_flags & PD_CHECK_ALLOC)
2215 		val = (void *)vdp->vd_val;
2216 	else {
2217 		/*
2218 		 * Currently all 1/2/4-byte size properties are byte/word/int.
2219 		 * No need (yet) to distinguish these from arrays of same size.
2220 		 */
2221 		switch (dip->pr_valsize) {
2222 		case 1:
2223 			u8 = vdp->vd_val;
2224 			val = &u8;
2225 			break;
2226 		case 2:
2227 			u16 = vdp->vd_val;
2228 			val = &u16;
2229 			break;
2230 		case 4:
2231 			u32 = vdp->vd_val;
2232 			val = &u32;
2233 			break;
2234 		default:
2235 			val = &vdp->vd_val;
2236 			break;
2237 		}
2238 	}
2239 
2240 	if (val != NULL)
2241 		(void) memcpy(dip->pr_val, val, dip->pr_valsize);
2242 	else
2243 		dip->pr_valsize = 0;
2244 
2245 	status = i_dladm_macprop(dip, B_TRUE);
2246 
2247 done:
2248 	free(dip);
2249 	return (status);
2250 }
2251 
2252 dladm_status_t
2253 i_dladm_macprop(void *dip, boolean_t set)
2254 {
2255 	int fd;
2256 	dladm_status_t status = DLADM_STATUS_OK;
2257 
2258 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) {
2259 		status = dladm_errno2status(errno);
2260 		return (status);
2261 	}
2262 	if (ioctl(fd, (set ? DLDIOC_SETMACPROP : DLDIOC_GETMACPROP), dip))
2263 		status = dladm_errno2status(errno);
2264 
2265 	(void) close(fd);
2266 	return (status);
2267 }
2268 
2269 static dld_ioc_macprop_t *
2270 i_dladm_get_public_prop(datalink_id_t linkid, char *prop_name, uint_t flags,
2271     dladm_status_t *status, uint_t *perm_flags)
2272 {
2273 	dld_ioc_macprop_t *dip = NULL;
2274 
2275 	dip = i_dladm_buf_alloc_by_name(0, linkid, prop_name, flags, status);
2276 	if (dip == NULL)
2277 		return (NULL);
2278 
2279 	*status = i_dladm_macprop(dip, B_FALSE);
2280 	if (*status != DLADM_STATUS_OK) {
2281 		free(dip);
2282 		return (NULL);
2283 	}
2284 	if (perm_flags != NULL)
2285 		*perm_flags = dip->pr_perm_flags;
2286 
2287 	return (dip);
2288 }
2289 
2290 /* ARGSUSED */
2291 static dladm_status_t
2292 i_dladm_defmtu_check(prop_desc_t *pdp, datalink_id_t linkid,
2293     char **prop_val, uint_t val_cnt, val_desc_t *v, datalink_media_t media)
2294 {
2295 	if (val_cnt != 1)
2296 		return (DLADM_STATUS_BADVAL);
2297 	v->vd_val = atoi(prop_val[0]);
2298 	return (DLADM_STATUS_OK);
2299 }
2300 
2301 /* ARGSUSED */
2302 static dladm_status_t
2303 i_dladm_duplex_get(prop_desc_t *pdp, datalink_id_t linkid,
2304     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2305     uint_t flags, uint_t *perm_flags)
2306 {
2307 	link_duplex_t   link_duplex;
2308 	dladm_status_t  status;
2309 
2310 	if ((status = dladm_get_single_mac_stat(linkid, "link_duplex",
2311 	    KSTAT_DATA_UINT32, &link_duplex)) != 0)
2312 		return (status);
2313 
2314 	switch (link_duplex) {
2315 	case LINK_DUPLEX_FULL:
2316 		(void) strcpy(*prop_val, "full");
2317 		break;
2318 	case LINK_DUPLEX_HALF:
2319 		(void) strcpy(*prop_val, "half");
2320 		break;
2321 	default:
2322 		(void) strcpy(*prop_val, "unknown");
2323 		break;
2324 	}
2325 	*val_cnt = 1;
2326 	return (DLADM_STATUS_OK);
2327 }
2328 
2329 /* ARGSUSED */
2330 static dladm_status_t
2331 i_dladm_speed_get(prop_desc_t *pdp, datalink_id_t linkid,
2332     char **prop_val, uint_t *val_cnt, uint_t flags, uint_t *perm_flags)
2333 {
2334 	uint64_t	ifspeed = 0;
2335 	dladm_status_t status;
2336 
2337 	if ((status = dladm_get_single_mac_stat(linkid, "ifspeed",
2338 	    KSTAT_DATA_UINT64, &ifspeed)) != 0)
2339 		return (status);
2340 
2341 	if ((ifspeed % 1000000) != 0) {
2342 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
2343 		    "%llf", ifspeed / (float)1000000); /* Mbps */
2344 	} else {
2345 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
2346 		    "%llu", ifspeed / 1000000); /* Mbps */
2347 	}
2348 	*val_cnt = 1;
2349 	*perm_flags = MAC_PROP_PERM_READ;
2350 	return (DLADM_STATUS_OK);
2351 }
2352 
2353 /* ARGSUSED */
2354 static dladm_status_t
2355 i_dladm_status_get(prop_desc_t *pdp, datalink_id_t linkid,
2356     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2357     uint_t flags, uint_t *perm_flags)
2358 {
2359 	link_state_t		link_state;
2360 	dladm_status_t		status;
2361 
2362 	status = i_dladm_get_state(linkid, &link_state);
2363 	if (status != DLADM_STATUS_OK)
2364 		return (status);
2365 
2366 	switch (link_state) {
2367 	case LINK_STATE_UP:
2368 		(void) strcpy(*prop_val, "up");
2369 		break;
2370 	case LINK_STATE_DOWN:
2371 		(void) strcpy(*prop_val, "down");
2372 		break;
2373 	default:
2374 		(void) strcpy(*prop_val, "unknown");
2375 		break;
2376 	}
2377 	*val_cnt = 1;
2378 	*perm_flags = MAC_PROP_PERM_READ;
2379 	return (DLADM_STATUS_OK);
2380 }
2381 
2382 /* ARGSUSED */
2383 static dladm_status_t
2384 i_dladm_binary_get(prop_desc_t *pdp, datalink_id_t linkid,
2385     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2386     uint_t flags, uint_t *perm_flags)
2387 {
2388 	dld_ioc_macprop_t *dip;
2389 	dladm_status_t status;
2390 
2391 	dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
2392 	    &status, perm_flags);
2393 	if (dip == NULL)
2394 		return (status);
2395 
2396 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%x", dip->pr_val[0]);
2397 	free(dip);
2398 	*val_cnt = 1;
2399 	return (DLADM_STATUS_OK);
2400 }
2401 
2402 /* ARGSUSED */
2403 static dladm_status_t
2404 i_dladm_uint32_get(prop_desc_t *pdp, datalink_id_t linkid,
2405     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2406     uint_t flags, uint_t *perm_flags)
2407 {
2408 	dld_ioc_macprop_t *dip;
2409 	uint32_t v = 0;
2410 	uchar_t *cp;
2411 	dladm_status_t status;
2412 
2413 	dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
2414 	    &status, perm_flags);
2415 	if (dip == NULL)
2416 		return (status);
2417 
2418 	cp = (uchar_t *)dip->pr_val;
2419 	(void) memcpy(&v, cp, sizeof (v));
2420 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%ld", v);
2421 	free(dip);
2422 	*val_cnt = 1;
2423 	return (DLADM_STATUS_OK);
2424 }
2425 
2426 /* ARGSUSED */
2427 static dladm_status_t
2428 i_dladm_flowctl_get(prop_desc_t *pdp, datalink_id_t linkid,
2429     char **prop_val, uint_t *val_cnt, datalink_media_t media,
2430     uint_t flags, uint_t *perm_flags)
2431 {
2432 	dld_ioc_macprop_t *dip;
2433 	link_flowctrl_t v;
2434 	dladm_status_t status;
2435 	uchar_t *cp;
2436 
2437 	dip = i_dladm_get_public_prop(linkid, pdp->pd_name, flags,
2438 	    &status, perm_flags);
2439 	if (dip == NULL)
2440 		return (status);
2441 
2442 	cp = (uchar_t *)dip->pr_val;
2443 	(void) memcpy(&v, cp, sizeof (v));
2444 	switch (v) {
2445 	case LINK_FLOWCTRL_NONE:
2446 		(void) sprintf(*prop_val, "no");
2447 		break;
2448 	case LINK_FLOWCTRL_RX:
2449 		(void) sprintf(*prop_val, "rx");
2450 		break;
2451 	case LINK_FLOWCTRL_TX:
2452 		(void) sprintf(*prop_val, "tx");
2453 		break;
2454 	case LINK_FLOWCTRL_BI:
2455 		(void) sprintf(*prop_val, "bi");
2456 		break;
2457 	}
2458 	free(dip);
2459 	*val_cnt = 1;
2460 	return (DLADM_STATUS_OK);
2461 }
2462 
2463 
2464 /* ARGSUSED */
2465 static dladm_status_t
2466 i_dladm_set_prop(datalink_id_t linkid, const char *prop_name,
2467     char **prop_val, uint_t val_cnt, uint_t flags)
2468 {
2469 	int		i, slen;
2470 	int 		bufsize = 0;
2471 	dld_ioc_macprop_t *dip = NULL;
2472 	uchar_t 	*dp;
2473 	link_attr_t *p;
2474 	dladm_status_t	status = DLADM_STATUS_OK;
2475 
2476 	if ((prop_name == NULL && prop_val != NULL) ||
2477 	    (prop_val != NULL && val_cnt == 0))
2478 		return (DLADM_STATUS_BADARG);
2479 	p = dladm_name2prop(prop_name);
2480 	if (p->pp_id != MAC_PROP_PRIVATE)
2481 		return (DLADM_STATUS_BADARG);
2482 
2483 	/*
2484 	 * private properties: all parsing is done in the kernel.
2485 	 * allocate a enough space for each property + its separator (',').
2486 	 */
2487 	for (i = 0; i < val_cnt; i++) {
2488 		bufsize += strlen(prop_val[i]) + 1;
2489 	}
2490 
2491 	if (prop_val == NULL) {
2492 		/*
2493 		 * getting default value. so use more buffer space.
2494 		 */
2495 		bufsize += DLADM_PROP_BUF_CHUNK;
2496 	}
2497 
2498 	dip = i_dladm_buf_alloc_by_name(bufsize + 1, linkid, prop_name,
2499 	    (prop_val != NULL ? 0 : MAC_PROP_DEFAULT), &status);
2500 	if (dip == NULL)
2501 		return (status);
2502 
2503 	dp = (uchar_t *)dip->pr_val;
2504 	slen = 0;
2505 
2506 	if (prop_val == NULL) {
2507 		status = i_dladm_macprop(dip, B_FALSE);
2508 	} else {
2509 		for (i = 0; i < val_cnt; i++) {
2510 			int plen = 0;
2511 
2512 			plen = strlen(prop_val[i]);
2513 			bcopy(prop_val[i], dp, plen);
2514 			slen += plen;
2515 			/*
2516 			 * add a "," separator and update dp.
2517 			 */
2518 			if (i != (val_cnt -1))
2519 				dp[slen++] = ',';
2520 			dp += (plen + 1);
2521 		}
2522 		status = i_dladm_macprop(dip, B_TRUE);
2523 	}
2524 
2525 	free(dip);
2526 	return (status);
2527 }
2528 
2529 static dladm_status_t
2530 i_dladm_get_prop(datalink_id_t linkid, const char *prop_name,
2531     char **prop_val, uint_t *val_cnt, dladm_prop_type_t type, uint_t dld_flags)
2532 {
2533 	dladm_status_t	status = DLADM_STATUS_OK;
2534 	dld_ioc_macprop_t *dip = NULL;
2535 	link_attr_t *p;
2536 	char tmp = '\0';
2537 
2538 	if ((prop_name == NULL && prop_val != NULL) ||
2539 	    (prop_val != NULL && val_cnt == 0))
2540 		return (DLADM_STATUS_BADARG);
2541 
2542 	p = dladm_name2prop(prop_name);
2543 	if (p->pp_id != MAC_PROP_PRIVATE)
2544 		return (DLADM_STATUS_BADARG);
2545 
2546 	if (type == DLADM_PROP_VAL_MODIFIABLE) {
2547 		*prop_val = &tmp;
2548 		*val_cnt = 1;
2549 		return (DLADM_STATUS_OK);
2550 	}
2551 
2552 	/*
2553 	 * private properties: all parsing is done in the kernel.
2554 	 */
2555 	dip = i_dladm_buf_alloc_by_name(DLADM_PROP_BUF_CHUNK, linkid, prop_name,
2556 	    dld_flags, &status);
2557 	if (dip == NULL)
2558 		return (status);
2559 
2560 	if ((status = i_dladm_macprop(dip, B_FALSE)) == DLADM_STATUS_OK) {
2561 		if (type == DLADM_PROP_VAL_PERM) {
2562 			(void) dladm_perm2str(dip->pr_perm_flags, *prop_val);
2563 		} else {
2564 			(void) strncpy(*prop_val, dip->pr_val,
2565 			    DLADM_PROP_VAL_MAX);
2566 		}
2567 		*val_cnt = 1;
2568 	}
2569 	free(dip);
2570 	return (status);
2571 }
2572 
2573 
2574 static dladm_status_t
2575 i_dladm_getset_defval(prop_desc_t *pdp, datalink_id_t linkid,
2576     datalink_media_t media, uint_t flags)
2577 {
2578 	dladm_status_t status;
2579 	char **prop_vals = NULL, *buf;
2580 	size_t bufsize;
2581 	uint_t cnt;
2582 	int i;
2583 	uint_t perm_flags;
2584 
2585 	/*
2586 	 * Allocate buffer needed for prop_vals array. We can have at most
2587 	 * DLADM_MAX_PROP_VALCNT char *prop_vals[] entries, where
2588 	 * each entry has max size DLADM_PROP_VAL_MAX
2589 	 */
2590 	bufsize =
2591 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
2592 	buf = malloc(bufsize);
2593 	prop_vals = (char **)(void *)buf;
2594 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
2595 		prop_vals[i] = buf +
2596 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
2597 		    i * DLADM_PROP_VAL_MAX;
2598 	}
2599 
2600 	/*
2601 	 * For properties which have pdp->pd_defval.vd_name as a non-empty
2602 	 * string, the "" itself is used to reset the property (exceptions
2603 	 * are zone and autopush, which populate vdp->vd_val). So
2604 	 * libdladm can copy pdp->pd_defval over to the val_desc_t passed
2605 	 * down on the setprop using the global values in the table. For
2606 	 * other cases (vd_name is ""), doing reset-linkprop will cause
2607 	 * libdladm to do a getprop to find the default value and then do
2608 	 * a setprop to reset the value to default.
2609 	 */
2610 	status = pdp->pd_get(pdp, linkid, prop_vals, &cnt, media,
2611 	    MAC_PROP_DEFAULT, &perm_flags);
2612 	if (status == DLADM_STATUS_OK) {
2613 		if (perm_flags == MAC_PROP_PERM_RW) {
2614 			status = i_dladm_set_single_prop(linkid, pdp->pd_class,
2615 			    media, pdp, prop_vals, cnt, flags);
2616 		}
2617 		else
2618 			status = DLADM_STATUS_NOTSUP;
2619 	}
2620 	free(buf);
2621 	return (status);
2622 }
2623 
2624 int
2625 macprop_to_wifi(mac_prop_id_t wl_prop)
2626 {
2627 	switch (wl_prop) {
2628 	case MAC_PROP_WL_ESSID:
2629 		return (WL_ESSID);
2630 	case MAC_PROP_WL_BSSID:
2631 		return (WL_BSSID);
2632 	case MAC_PROP_WL_BSSTYPE:
2633 		return (WL_BSS_TYPE);
2634 	case MAC_PROP_WL_LINKSTATUS:
2635 		return (WL_LINKSTATUS);
2636 	case MAC_PROP_WL_DESIRED_RATES:
2637 		return (WL_DESIRED_RATES);
2638 	case MAC_PROP_WL_SUPPORTED_RATES:
2639 		return (WL_SUPPORTED_RATES);
2640 	case MAC_PROP_WL_AUTH_MODE:
2641 		return (WL_AUTH_MODE);
2642 	case MAC_PROP_WL_ENCRYPTION:
2643 		return (WL_ENCRYPTION);
2644 	case MAC_PROP_WL_RSSI:
2645 		return (WL_RSSI);
2646 	case MAC_PROP_WL_PHY_CONFIG:
2647 		return (WL_PHY_CONFIG);
2648 	case MAC_PROP_WL_CAPABILITY:
2649 		return (WL_CAPABILITY);
2650 	case MAC_PROP_WL_WPA:
2651 		return (WL_WPA);
2652 	case MAC_PROP_WL_SCANRESULTS:
2653 		return (WL_SCANRESULTS);
2654 	case MAC_PROP_WL_POWER_MODE:
2655 		return (WL_POWER_MODE);
2656 	case MAC_PROP_WL_RADIO:
2657 		return (WL_RADIO);
2658 	case MAC_PROP_WL_ESS_LIST:
2659 		return (WL_ESS_LIST);
2660 	case MAC_PROP_WL_KEY_TAB:
2661 		return (WL_WEP_KEY_TAB);
2662 	case MAC_PROP_WL_CREATE_IBSS:
2663 		return (WL_CREATE_IBSS);
2664 	case MAC_PROP_WL_SETOPTIE:
2665 		return (WL_SETOPTIE);
2666 	case MAC_PROP_WL_DELKEY:
2667 		return (WL_DELKEY);
2668 	case MAC_PROP_WL_KEY:
2669 		return (WL_KEY);
2670 	case MAC_PROP_WL_MLME:
2671 		return (WL_MLME);
2672 	default:
2673 		return (-1);
2674 	}
2675 }
2676 
2677 dladm_status_t
2678 i_dladm_wlan_param(datalink_id_t linkid, void *buf, mac_prop_id_t cmd,
2679     size_t len, boolean_t set)
2680 {
2681 	uint32_t		flags;
2682 	dladm_status_t		status;
2683 	uint32_t		media;
2684 	dld_ioc_macprop_t	*dip;
2685 	void			*dp;
2686 
2687 	if ((status = dladm_datalink_id2info(linkid, &flags, NULL, &media,
2688 	    NULL, 0)) != DLADM_STATUS_OK) {
2689 		return (status);
2690 	}
2691 
2692 	if (media != DL_WIFI)
2693 		return (DLADM_STATUS_BADARG);
2694 
2695 	if (!(flags & DLADM_OPT_ACTIVE))
2696 		return (DLADM_STATUS_TEMPONLY);
2697 
2698 	if (len == (MAX_BUF_LEN - WIFI_BUF_OFFSET))
2699 		len = MAX_BUF_LEN - sizeof (dld_ioc_macprop_t) - 1;
2700 
2701 	dip = i_dladm_buf_alloc_by_id(len, linkid, cmd, 0, &status);
2702 	if (dip == NULL)
2703 		return (DLADM_STATUS_NOMEM);
2704 
2705 	dp = (uchar_t *)dip->pr_val;
2706 	if (set)
2707 		(void) memcpy(dp, buf, len);
2708 
2709 	status = i_dladm_macprop(dip, set);
2710 	if (status == DLADM_STATUS_NOTSUP) {
2711 		if (set) {
2712 			status = i_dladm_wlan_set_legacy_ioctl(linkid,
2713 			    buf, len, macprop_to_wifi(cmd));
2714 		} else {
2715 			status = i_dladm_wlan_get_legacy_ioctl(linkid,
2716 			    buf, len, macprop_to_wifi(cmd));
2717 		}
2718 	} else if (status == DLADM_STATUS_OK) {
2719 		if (!set)
2720 			(void) memcpy(buf, dp, len);
2721 	}
2722 
2723 	free(dip);
2724 	return (status);
2725 }
2726 
2727 static dladm_status_t
2728 i_dladm_wlan_get_legacy_ioctl(datalink_id_t linkid, void *buf, uint_t buflen,
2729     uint_t id)
2730 {
2731 	wldp_t *gbuf;
2732 	dladm_status_t status;
2733 
2734 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
2735 		return (DLADM_STATUS_NOMEM);
2736 
2737 	(void) memset(gbuf, 0, MAX_BUF_LEN);
2738 	status = i_dladm_wlan_legacy_ioctl(linkid, gbuf, id, MAX_BUF_LEN,
2739 	    WLAN_GET_PARAM, sizeof (wldp_t));
2740 	if (status == DLADM_STATUS_OK)
2741 		(void) memcpy(buf, gbuf->wldp_buf, buflen);
2742 
2743 	free(gbuf);
2744 	return (status);
2745 }
2746 
2747 static dladm_status_t
2748 i_dladm_wlan_set_legacy_ioctl(datalink_id_t linkid,  void *buf, uint_t buflen,
2749     uint_t id)
2750 {
2751 	wldp_t *gbuf;
2752 	dladm_status_t status = DLADM_STATUS_OK;
2753 
2754 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
2755 		return (DLADM_STATUS_NOMEM);
2756 
2757 	(void) memset(gbuf, 0, MAX_BUF_LEN);
2758 	(void) memcpy(gbuf->wldp_buf, buf, buflen);
2759 	buflen += WIFI_BUF_OFFSET;
2760 	status = i_dladm_wlan_legacy_ioctl(linkid, gbuf, id, buflen,
2761 	    WLAN_SET_PARAM, buflen);
2762 
2763 	free(gbuf);
2764 	return (status);
2765 }
2766 
2767 static dladm_status_t
2768 link_proplist_check(dladm_arg_list_t *proplist)
2769 {
2770 	int		i, j;
2771 	boolean_t	matched;
2772 
2773 	for (i = 0; i < proplist->al_count; i++) {
2774 		matched = B_FALSE;
2775 		for (j = 0; j < DLADM_MAX_PROPS; j++) {
2776 			if (strcmp(proplist->al_info[i].ai_name,
2777 			    prop_table[j].pd_name) == 0)
2778 				matched = B_TRUE;
2779 		}
2780 		if (!matched)
2781 			return (DLADM_STATUS_BADPROP);
2782 	}
2783 	return (DLADM_STATUS_OK);
2784 }
2785 
2786 dladm_status_t
2787 dladm_parse_link_props(char *str, dladm_arg_list_t **listp, boolean_t novalues)
2788 {
2789 	dladm_status_t	status;
2790 
2791 	status = dladm_parse_args(str, listp, novalues);
2792 	if (status != DLADM_STATUS_OK)
2793 		return (status);
2794 
2795 	status = link_proplist_check(*listp);
2796 	if (status != DLADM_STATUS_OK) {
2797 		dladm_free_props(*listp);
2798 		return (status);
2799 	}
2800 
2801 	return (DLADM_STATUS_OK);
2802 }
2803 
2804 /*
2805  * Retrieve the one link property from the database
2806  */
2807 /*ARGSUSED*/
2808 static int
2809 i_dladm_get_one_prop(datalink_id_t linkid, const char *prop_name, void *arg)
2810 {
2811 	dladm_arg_list_t	*proplist = arg;
2812 	dladm_arg_info_t	*aip = NULL;
2813 
2814 	aip = &proplist->al_info[proplist->al_count];
2815 	/*
2816 	 * it is fine to point to prop_name since prop_name points to the
2817 	 * prop_table[n].pd_name.
2818 	 */
2819 	aip->ai_name = prop_name;
2820 
2821 	(void) dladm_get_linkprop(linkid, DLADM_PROP_VAL_PERSISTENT, prop_name,
2822 	    aip->ai_val, &aip->ai_count);
2823 
2824 	if (aip->ai_count != 0)
2825 		proplist->al_count++;
2826 
2827 	return (DLADM_WALK_CONTINUE);
2828 }
2829 
2830 
2831 /*
2832  * Retrieve all link properties for a link from the database and
2833  * return a property list.
2834  */
2835 dladm_status_t
2836 dladm_link_get_proplist(datalink_id_t linkid, dladm_arg_list_t **listp)
2837 {
2838 	dladm_arg_list_t	*list;
2839 	dladm_status_t		status = DLADM_STATUS_OK;
2840 
2841 	list = calloc(1, sizeof (dladm_arg_list_t));
2842 	if (list == NULL)
2843 		return (dladm_errno2status(errno));
2844 
2845 	status = dladm_walk_linkprop(linkid, list, i_dladm_get_one_prop);
2846 
2847 	*listp = list;
2848 	return (status);
2849 }
2850 
2851 /*
2852  * Retrieve the named property from a proplist, check the value and
2853  * convert to a kernel structure.
2854  */
2855 static dladm_status_t
2856 i_dladm_link_proplist_extract_one(dladm_arg_list_t *proplist,
2857     const char *name, void *val)
2858 {
2859 	dladm_status_t		status;
2860 	dladm_arg_info_t	*aip = NULL;
2861 	int			i, j;
2862 
2863 	/* Find named property in proplist */
2864 	for (i = 0; i < proplist->al_count; i++) {
2865 		aip = &proplist->al_info[i];
2866 		if (strcasecmp(aip->ai_name, name) == 0)
2867 			break;
2868 	}
2869 
2870 	/* Property not in list */
2871 	if (i == proplist->al_count)
2872 		return (DLADM_STATUS_OK);
2873 
2874 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
2875 		prop_desc_t	*pdp = &prop_table[i];
2876 		val_desc_t	*vdp;
2877 
2878 		vdp = malloc(sizeof (val_desc_t) * aip->ai_count);
2879 		if (vdp == NULL)
2880 			return (DLADM_STATUS_NOMEM);
2881 
2882 		if (strcasecmp(aip->ai_name, pdp->pd_name) != 0)
2883 			continue;
2884 
2885 		if (aip->ai_val == NULL)
2886 			return (DLADM_STATUS_BADARG);
2887 
2888 		/* Check property value */
2889 		if (pdp->pd_check != NULL) {
2890 			status = pdp->pd_check(pdp, 0, aip->ai_val,
2891 			    aip->ai_count, vdp, 0);
2892 		} else {
2893 			status = DLADM_STATUS_BADARG;
2894 		}
2895 
2896 		if (status != DLADM_STATUS_OK)
2897 			return (status);
2898 
2899 		for (j = 0; j < DLADM_MAX_RSRC_PROP; j++) {
2900 			resource_prop_t	*rpp = &rsrc_prop_table[j];
2901 
2902 			if (strcasecmp(aip->ai_name, rpp->rp_name) != 0)
2903 				continue;
2904 
2905 			/* Extract kernel structure */
2906 			if (rpp->rp_extract != NULL) {
2907 				status = rpp->rp_extract(vdp, val,
2908 				    aip->ai_count);
2909 			} else {
2910 				status = DLADM_STATUS_BADARG;
2911 			}
2912 			break;
2913 		}
2914 
2915 		if (status != DLADM_STATUS_OK)
2916 			return (status);
2917 
2918 		break;
2919 	}
2920 	return (status);
2921 }
2922 
2923 /*
2924  * Extract properties from a proplist and convert to mac_resource_props_t.
2925  */
2926 dladm_status_t
2927 dladm_link_proplist_extract(dladm_arg_list_t *proplist,
2928     mac_resource_props_t *mrp)
2929 {
2930 	dladm_status_t	status = DLADM_STATUS_OK;
2931 
2932 	status = i_dladm_link_proplist_extract_one(proplist, "maxbw", mrp);
2933 	if (status != DLADM_STATUS_OK)
2934 		return (status);
2935 	status = i_dladm_link_proplist_extract_one(proplist, "priority", mrp);
2936 	if (status != DLADM_STATUS_OK)
2937 		return (status);
2938 	status = i_dladm_link_proplist_extract_one(proplist, "cpus", mrp);
2939 	if (status != DLADM_STATUS_OK)
2940 		return (status);
2941 	return (status);
2942 }
2943 
2944 static const char *
2945 dladm_perm2str(uint_t perm, char *buf)
2946 {
2947 	(void) snprintf(buf, DLADM_STRSIZE, "%c%c",
2948 	    ((perm & MAC_PROP_PERM_READ) != 0) ? 'r' : '-',
2949 	    ((perm & MAC_PROP_PERM_WRITE) != 0) ? 'w' : '-');
2950 	return (buf);
2951 }
2952 
2953 dladm_status_t
2954 i_dladm_get_state(datalink_id_t linkid, link_state_t *state)
2955 {
2956 	dld_ioc_macprop_t	*dip;
2957 	dladm_status_t		status;
2958 	uint_t			perms;
2959 
2960 	dip = i_dladm_get_public_prop(linkid, "state", 0, &status, &perms);
2961 	if (status != DLADM_STATUS_OK)
2962 		return (status);
2963 	(void) memcpy(state, dip->pr_val, sizeof (*state));
2964 	free(dip);
2965 	return (status);
2966 }
2967