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