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