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