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