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