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 50 /* 51 * The linkprop get() callback. 52 * - propstrp: a property string array to keep the returned property. 53 * Caller allocated. 54 * - cntp: number of returned properties. 55 * Caller also uses it to indicate how many it expects. 56 */ 57 typedef dladm_status_t pd_getf_t(datalink_id_t, char **propstp, uint_t *cntp); 58 59 /* 60 * The linkprop set() callback. 61 * - propval: a val_desc_t array which keeps the property values to be set. 62 * - cnt: number of properties to be set. 63 */ 64 typedef dladm_status_t pd_setf_t(datalink_id_t, val_desc_t *propval, 65 uint_t cnt); 66 67 #define PD_TEMPONLY 0x1 68 69 /* 70 * The linkprop check() callback. 71 * - propstrp: property string array which keeps the property to be checked. 72 * - cnt: number of properties. 73 * - propval: return value; the property values of the given property strings. 74 * - dofree: indicates whether the caller needs to free propvalp->vd_val. 75 */ 76 typedef dladm_status_t pd_checkf_t(datalink_id_t, char **propstrp, 77 uint_t cnt, val_desc_t *propval, boolean_t *dofree); 78 79 static pd_getf_t do_get_zone, do_get_autopush, do_get_rate_mod, 80 do_get_rate_prop, do_get_channel_prop, 81 do_get_powermode_prop, do_get_radio_prop; 82 static pd_setf_t do_set_zone, do_set_autopush, do_set_rate_prop, 83 do_set_powermode_prop, do_set_radio_prop; 84 static pd_checkf_t do_check_zone, do_check_autopush, do_check_rate; 85 86 typedef struct prop_desc { 87 /* 88 * link property name 89 */ 90 char *pd_name; 91 92 /* 93 * default property value, can be set to { "", NULL } 94 */ 95 val_desc_t pd_defval; 96 97 /* 98 * list of optional property values, can be NULL. 99 * 100 * This is set to non-NULL if there is a list of possible property 101 * values. pd_optval would point to the array of possible values. 102 */ 103 val_desc_t *pd_optval; 104 105 /* 106 * count of the above optional property values. 0 if pd_optval is NULL. 107 */ 108 uint_t pd_noptval; 109 110 /* 111 * callback to set link property; 112 * set to NULL if this property is read-only 113 */ 114 pd_setf_t *pd_set; 115 116 /* 117 * callback to get modifiable link property 118 */ 119 pd_getf_t *pd_getmod; 120 121 /* 122 * callback to get current link property 123 */ 124 pd_getf_t *pd_get; 125 126 /* 127 * callback to validate link property value, set to NULL if pd_optval 128 * is not NULL. In that case, validate the value by comparing it with 129 * the pd_optval. Return a val_desc_t array pointer if the value is 130 * valid. 131 */ 132 pd_checkf_t *pd_check; 133 134 /* 135 * currently only PD_TEMPONLY is valid, which indicates the property 136 * is temporary only. 137 */ 138 uint_t pd_flags; 139 140 /* 141 * indicate link classes this property applies to. 142 */ 143 datalink_class_t pd_class; 144 145 /* 146 * indicate link media type this property applies to. 147 */ 148 datalink_media_t pd_dmedia; 149 } prop_desc_t; 150 151 static val_desc_t dladm_wlan_radio_vals[] = { 152 { "on", DLADM_WLAN_RADIO_ON }, 153 { "off", DLADM_WLAN_RADIO_OFF } 154 }; 155 156 static val_desc_t dladm_wlan_powermode_vals[] = { 157 { "off", DLADM_WLAN_PM_OFF }, 158 { "fast", DLADM_WLAN_PM_FAST }, 159 { "max", DLADM_WLAN_PM_MAX } 160 }; 161 162 static prop_desc_t prop_table[] = { 163 164 { "channel", { NULL, 0 }, NULL, 0, NULL, NULL, 165 do_get_channel_prop, NULL, 0, 166 DATALINK_CLASS_PHYS, DL_WIFI}, 167 168 { "powermode", { "off", DLADM_WLAN_PM_OFF }, 169 dladm_wlan_powermode_vals, VALCNT(dladm_wlan_powermode_vals), 170 do_set_powermode_prop, NULL, 171 do_get_powermode_prop, NULL, 0, 172 DATALINK_CLASS_PHYS, DL_WIFI}, 173 174 { "radio", { "on", DLADM_WLAN_RADIO_ON }, 175 dladm_wlan_radio_vals, VALCNT(dladm_wlan_radio_vals), 176 do_set_radio_prop, NULL, 177 do_get_radio_prop, NULL, 0, 178 DATALINK_CLASS_PHYS, DL_WIFI}, 179 180 { "speed", { "", 0 }, NULL, 0, 181 do_set_rate_prop, do_get_rate_mod, 182 do_get_rate_prop, do_check_rate, 0, 183 DATALINK_CLASS_PHYS, DL_WIFI}, 184 185 { "autopush", { "", NULL }, NULL, 0, 186 do_set_autopush, NULL, 187 do_get_autopush, do_check_autopush, 0, 188 DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE}, 189 190 { "zone", { "", NULL }, NULL, 0, 191 do_set_zone, NULL, 192 do_get_zone, do_check_zone, PD_TEMPONLY, 193 DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE} 194 }; 195 196 #define DLADM_MAX_PROPS (sizeof (prop_table) / sizeof (prop_desc_t)) 197 198 static dladm_status_t i_dladm_set_linkprop_db(datalink_id_t, const char *, 199 char **, uint_t); 200 static dladm_status_t i_dladm_get_linkprop_db(datalink_id_t, const char *, 201 char **, uint_t *); 202 static dladm_status_t i_dladm_set_single_prop(datalink_id_t, datalink_class_t, 203 uint32_t, prop_desc_t *, char **, uint_t, uint_t); 204 static dladm_status_t i_dladm_set_linkprop(datalink_id_t, const char *, 205 char **, uint_t, uint_t); 206 207 /* 208 * Unfortunately, MAX_SCAN_SUPPORT_RATES is too small to allow all 209 * rates to be retrieved. However, we cannot increase it at this 210 * time because it will break binary compatibility with unbundled 211 * WiFi drivers and utilities. So for now we define an additional 212 * constant, MAX_SUPPORT_RATES, to allow all rates to be retrieved. 213 */ 214 #define MAX_SUPPORT_RATES 64 215 216 #define AP_ANCHOR "[anchor]" 217 #define AP_DELIMITER '.' 218 219 static dladm_status_t 220 do_check_prop(prop_desc_t *pdp, char **prop_val, uint_t val_cnt, 221 val_desc_t *vdp) 222 { 223 int i, j; 224 dladm_status_t status = DLADM_STATUS_OK; 225 226 for (j = 0; j < val_cnt; j++) { 227 for (i = 0; i < pdp->pd_noptval; i++) { 228 if (strcasecmp(*prop_val, 229 pdp->pd_optval[i].vd_name) == 0) { 230 break; 231 } 232 } 233 if (i == pdp->pd_noptval) { 234 status = DLADM_STATUS_BADVAL; 235 goto done; 236 } 237 (void) memcpy(vdp + j, &pdp->pd_optval[i], sizeof (val_desc_t)); 238 } 239 240 done: 241 return (status); 242 } 243 244 static dladm_status_t 245 i_dladm_set_single_prop(datalink_id_t linkid, datalink_class_t class, 246 uint32_t media, prop_desc_t *pdp, char **prop_val, uint_t val_cnt, 247 uint_t flags) 248 { 249 dladm_status_t status = DLADM_STATUS_OK; 250 val_desc_t *vdp = NULL; 251 boolean_t needfree = B_FALSE; 252 uint_t cnt, i; 253 254 if (!(pdp->pd_class & class)) 255 return (DLADM_STATUS_BADARG); 256 257 if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media)) 258 return (DLADM_STATUS_BADARG); 259 260 if ((flags & DLADM_OPT_PERSIST) && (pdp->pd_flags & PD_TEMPONLY)) 261 return (DLADM_STATUS_TEMPONLY); 262 263 if (!(flags & DLADM_OPT_ACTIVE)) 264 return (DLADM_STATUS_OK); 265 266 if (pdp->pd_set == NULL) 267 return (DLADM_STATUS_PROPRDONLY); 268 269 if (prop_val != NULL) { 270 vdp = malloc(sizeof (val_desc_t) * val_cnt); 271 if (vdp == NULL) 272 return (DLADM_STATUS_NOMEM); 273 274 if (pdp->pd_check != NULL) { 275 status = pdp->pd_check(linkid, prop_val, val_cnt, vdp, 276 &needfree); 277 } else if (pdp->pd_optval != NULL) { 278 status = do_check_prop(pdp, prop_val, val_cnt, vdp); 279 } else { 280 status = DLADM_STATUS_BADARG; 281 } 282 283 if (status != DLADM_STATUS_OK) 284 goto done; 285 286 cnt = val_cnt; 287 } else { 288 if (pdp->pd_defval.vd_name == NULL) 289 return (DLADM_STATUS_NOTSUP); 290 291 if ((vdp = malloc(sizeof (val_desc_t))) == NULL) 292 return (DLADM_STATUS_NOMEM); 293 294 (void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t)); 295 cnt = 1; 296 } 297 status = pdp->pd_set(linkid, vdp, cnt); 298 if (needfree) { 299 for (i = 0; i < cnt; i++) 300 free((void *)(((val_desc_t *)vdp + i)->vd_val)); 301 } 302 done: 303 free(vdp); 304 return (status); 305 } 306 307 static dladm_status_t 308 i_dladm_set_linkprop(datalink_id_t linkid, const char *prop_name, 309 char **prop_val, uint_t val_cnt, uint_t flags) 310 { 311 int i; 312 boolean_t found = B_FALSE; 313 datalink_class_t class; 314 uint32_t media; 315 dladm_status_t status = DLADM_STATUS_OK; 316 317 status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0); 318 if (status != DLADM_STATUS_OK) 319 return (status); 320 321 for (i = 0; i < DLADM_MAX_PROPS; i++) { 322 prop_desc_t *pdp = &prop_table[i]; 323 dladm_status_t s; 324 325 if (prop_name != NULL && 326 (strcasecmp(prop_name, pdp->pd_name) != 0)) 327 continue; 328 329 found = B_TRUE; 330 s = i_dladm_set_single_prop(linkid, class, media, pdp, prop_val, 331 val_cnt, flags); 332 333 if (prop_name != NULL) { 334 status = s; 335 break; 336 } else { 337 if (s != DLADM_STATUS_OK && 338 s != DLADM_STATUS_NOTSUP) 339 status = s; 340 } 341 } 342 if (!found) 343 status = DLADM_STATUS_NOTFOUND; 344 345 return (status); 346 } 347 348 /* 349 * Set/reset link property for specific link 350 */ 351 dladm_status_t 352 dladm_set_linkprop(datalink_id_t linkid, const char *prop_name, char **prop_val, 353 uint_t val_cnt, uint_t flags) 354 { 355 dladm_status_t status = DLADM_STATUS_OK; 356 357 if ((linkid == DATALINK_INVALID_LINKID) || (flags == 0) || 358 (prop_val == NULL && val_cnt > 0) || 359 (prop_val != NULL && val_cnt == 0) || 360 (prop_name == NULL && prop_val != NULL)) { 361 return (DLADM_STATUS_BADARG); 362 } 363 364 status = i_dladm_set_linkprop(linkid, prop_name, prop_val, 365 val_cnt, flags); 366 if (status != DLADM_STATUS_OK) 367 return (status); 368 369 if (flags & DLADM_OPT_PERSIST) { 370 status = i_dladm_set_linkprop_db(linkid, prop_name, 371 prop_val, val_cnt); 372 } 373 return (status); 374 } 375 376 /* 377 * Walk link properties of the given specific link. 378 */ 379 dladm_status_t 380 dladm_walk_linkprop(datalink_id_t linkid, void *arg, 381 int (*func)(datalink_id_t, const char *, void *)) 382 { 383 dladm_status_t status; 384 datalink_class_t class; 385 uint_t media; 386 int i; 387 388 if (linkid == DATALINK_INVALID_LINKID || func == NULL) 389 return (DLADM_STATUS_BADARG); 390 391 status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0); 392 if (status != DLADM_STATUS_OK) 393 return (status); 394 395 for (i = 0; i < DLADM_MAX_PROPS; i++) { 396 if (!(prop_table[i].pd_class & class)) 397 continue; 398 399 if (!DATALINK_MEDIA_ACCEPTED(prop_table[i].pd_dmedia, media)) 400 continue; 401 402 if (func(linkid, prop_table[i].pd_name, arg) == 403 DLADM_WALK_TERMINATE) { 404 break; 405 } 406 } 407 408 return (DLADM_STATUS_OK); 409 } 410 411 /* 412 * Get linkprop of the given specific link. 413 */ 414 dladm_status_t 415 dladm_get_linkprop(datalink_id_t linkid, dladm_prop_type_t type, 416 const char *prop_name, char **prop_val, uint_t *val_cntp) 417 { 418 dladm_status_t status = DLADM_STATUS_OK; 419 datalink_class_t class; 420 uint_t media; 421 prop_desc_t *pdp; 422 uint_t cnt; 423 int i; 424 425 if (linkid == DATALINK_INVALID_LINKID || prop_name == NULL || 426 prop_val == NULL || val_cntp == NULL || *val_cntp == 0) 427 return (DLADM_STATUS_BADARG); 428 429 for (i = 0; i < DLADM_MAX_PROPS; i++) 430 if (strcasecmp(prop_name, prop_table[i].pd_name) == 0) 431 break; 432 433 if (i == DLADM_MAX_PROPS) 434 return (DLADM_STATUS_NOTFOUND); 435 436 pdp = &prop_table[i]; 437 438 status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0); 439 if (status != DLADM_STATUS_OK) 440 return (status); 441 442 if (!(pdp->pd_class & class)) 443 return (DLADM_STATUS_BADARG); 444 445 if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media)) 446 return (DLADM_STATUS_BADARG); 447 448 switch (type) { 449 case DLADM_PROP_VAL_CURRENT: 450 status = pdp->pd_get(linkid, prop_val, val_cntp); 451 break; 452 453 case DLADM_PROP_VAL_DEFAULT: 454 if (pdp->pd_defval.vd_name == NULL) { 455 status = DLADM_STATUS_NOTSUP; 456 break; 457 } 458 (void) strcpy(*prop_val, pdp->pd_defval.vd_name); 459 *val_cntp = 1; 460 break; 461 462 case DLADM_PROP_VAL_MODIFIABLE: 463 if (pdp->pd_getmod != NULL) { 464 status = pdp->pd_getmod(linkid, prop_val, val_cntp); 465 break; 466 } 467 cnt = pdp->pd_noptval; 468 if (cnt == 0) { 469 status = DLADM_STATUS_NOTSUP; 470 } else if (cnt > *val_cntp) { 471 status = DLADM_STATUS_TOOSMALL; 472 } else { 473 for (i = 0; i < cnt; i++) { 474 (void) strcpy(prop_val[i], 475 pdp->pd_optval[i].vd_name); 476 } 477 *val_cntp = cnt; 478 } 479 break; 480 case DLADM_PROP_VAL_PERSISTENT: 481 if (pdp->pd_flags & PD_TEMPONLY) 482 return (DLADM_STATUS_TEMPONLY); 483 status = i_dladm_get_linkprop_db(linkid, prop_name, 484 prop_val, val_cntp); 485 break; 486 default: 487 status = DLADM_STATUS_BADARG; 488 break; 489 } 490 491 return (status); 492 } 493 494 /*ARGSUSED*/ 495 static int 496 i_dladm_init_one_prop(datalink_id_t linkid, const char *prop_name, void *arg) 497 { 498 char *buf, **propvals; 499 uint_t i, valcnt = DLADM_MAX_PROP_VALCNT; 500 501 if ((buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * 502 DLADM_MAX_PROP_VALCNT)) == NULL) { 503 return (DLADM_WALK_CONTINUE); 504 } 505 506 propvals = (char **)(void *)buf; 507 for (i = 0; i < valcnt; i++) { 508 propvals[i] = buf + 509 sizeof (char *) * DLADM_MAX_PROP_VALCNT + 510 i * DLADM_PROP_VAL_MAX; 511 } 512 513 if (dladm_get_linkprop(linkid, DLADM_PROP_VAL_PERSISTENT, prop_name, 514 propvals, &valcnt) != DLADM_STATUS_OK) { 515 goto done; 516 } 517 518 (void) dladm_set_linkprop(linkid, prop_name, propvals, valcnt, 519 DLADM_OPT_ACTIVE); 520 521 done: 522 if (buf != NULL) 523 free(buf); 524 525 return (DLADM_WALK_CONTINUE); 526 } 527 528 /*ARGSUSED*/ 529 static int 530 i_dladm_init_linkprop(datalink_id_t linkid, void *arg) 531 { 532 (void) dladm_init_linkprop(linkid); 533 return (DLADM_WALK_CONTINUE); 534 } 535 536 dladm_status_t 537 dladm_init_linkprop(datalink_id_t linkid) 538 { 539 if (linkid == DATALINK_ALL_LINKID) { 540 (void) dladm_walk_datalink_id(i_dladm_init_linkprop, NULL, 541 DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 542 DLADM_OPT_PERSIST); 543 } else { 544 (void) dladm_walk_linkprop(linkid, NULL, i_dladm_init_one_prop); 545 } 546 return (DLADM_STATUS_OK); 547 } 548 549 static dladm_status_t 550 do_get_zone(datalink_id_t linkid, char **prop_val, uint_t *val_cnt) 551 { 552 char zone_name[ZONENAME_MAX]; 553 zoneid_t zid; 554 dladm_status_t status; 555 556 status = dladm_getzid(linkid, &zid); 557 if (status != DLADM_STATUS_OK) 558 return (status); 559 560 *val_cnt = 1; 561 if (zid != GLOBAL_ZONEID) { 562 if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0) 563 return (dladm_errno2status(errno)); 564 565 (void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX); 566 } else { 567 *prop_val[0] = '\0'; 568 } 569 570 return (DLADM_STATUS_OK); 571 } 572 573 typedef int (*zone_get_devroot_t)(char *, char *, size_t); 574 575 static int 576 i_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen) 577 { 578 char root[MAXPATHLEN]; 579 zone_get_devroot_t real_zone_get_devroot; 580 void *dlhandle; 581 void *sym; 582 int ret; 583 584 if ((dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY)) == NULL) 585 return (-1); 586 587 if ((sym = dlsym(dlhandle, "zone_get_devroot")) == NULL) { 588 (void) dlclose(dlhandle); 589 return (-1); 590 } 591 592 real_zone_get_devroot = (zone_get_devroot_t)sym; 593 594 if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0) 595 (void) snprintf(dev, devlen, "%s%s", root, "/dev"); 596 (void) dlclose(dlhandle); 597 return (ret); 598 } 599 600 static dladm_status_t 601 i_dladm_update_deventry(zoneid_t zid, datalink_id_t linkid, boolean_t add) 602 { 603 char path[MAXPATHLEN]; 604 char name[MAXLINKNAMELEN]; 605 di_prof_t prof = NULL; 606 char zone_name[ZONENAME_MAX]; 607 dladm_status_t status; 608 int ret; 609 610 if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0) 611 return (dladm_errno2status(errno)); 612 if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0) 613 return (dladm_errno2status(errno)); 614 if (di_prof_init(path, &prof) != 0) 615 return (dladm_errno2status(errno)); 616 617 status = dladm_linkid2legacyname(linkid, name, MAXLINKNAMELEN); 618 if (status != DLADM_STATUS_OK) 619 goto cleanup; 620 621 if (add) 622 ret = di_prof_add_dev(prof, name); 623 else 624 ret = di_prof_add_exclude(prof, name); 625 626 if (ret != 0) { 627 status = dladm_errno2status(errno); 628 goto cleanup; 629 } 630 631 if (di_prof_commit(prof) != 0) 632 status = dladm_errno2status(errno); 633 cleanup: 634 if (prof) 635 di_prof_fini(prof); 636 637 return (status); 638 } 639 640 static dladm_status_t 641 do_set_zone(datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt) 642 { 643 dladm_status_t status; 644 zoneid_t zid_old, zid_new; 645 char link[MAXLINKNAMELEN]; 646 647 if (val_cnt != 1) 648 return (DLADM_STATUS_BADVALCNT); 649 650 status = dladm_getzid(linkid, &zid_old); 651 if (status != DLADM_STATUS_OK) 652 return (status); 653 654 /* Do nothing if setting to current value */ 655 zid_new = vdp->vd_val; 656 if (zid_new == zid_old) 657 return (DLADM_STATUS_OK); 658 659 if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL, 660 link, MAXLINKNAMELEN)) != DLADM_STATUS_OK) { 661 return (status); 662 } 663 664 if (zid_new != GLOBAL_ZONEID) { 665 /* 666 * If the new zoneid is the global zone, we could destroy 667 * the link (in the case of an implicitly-created VLAN) as a 668 * result of the dladm_setzid() operation. In that case, 669 * we defer the operation to the end of this function to avoid 670 * recreating the VLAN and getting a different linkid during 671 * the rollback if other operation fails. 672 * 673 * Otherwise, dladm_setzid() will hold a reference to the 674 * link and prevent a link renaming, so we need to do it 675 * before other operations. 676 */ 677 status = dladm_setzid(link, zid_new); 678 if (status != DLADM_STATUS_OK) 679 return (status); 680 } 681 682 if (zid_old != GLOBAL_ZONEID) { 683 if (zone_remove_datalink(zid_old, link) != 0 && 684 errno != ENXIO) { 685 status = dladm_errno2status(errno); 686 goto rollback1; 687 } 688 689 /* 690 * It is okay to fail to update the /dev entry (some 691 * vanity-named links do not have a /dev entry). 692 */ 693 (void) i_dladm_update_deventry(zid_old, linkid, B_FALSE); 694 } 695 696 if (zid_new != GLOBAL_ZONEID) { 697 if (zone_add_datalink(zid_new, link) != 0) { 698 status = dladm_errno2status(errno); 699 goto rollback2; 700 } 701 702 (void) i_dladm_update_deventry(zid_new, linkid, B_TRUE); 703 } else { 704 status = dladm_setzid(link, zid_new); 705 if (status != DLADM_STATUS_OK) 706 goto rollback2; 707 } 708 709 return (DLADM_STATUS_OK); 710 711 rollback2: 712 if (zid_old != GLOBAL_ZONEID) 713 (void) i_dladm_update_deventry(zid_old, linkid, B_TRUE); 714 if (zid_old != GLOBAL_ZONEID) 715 (void) zone_add_datalink(zid_old, link); 716 rollback1: 717 if (zid_new != GLOBAL_ZONEID) 718 (void) dladm_setzid(link, zid_old); 719 return (status); 720 } 721 722 /* ARGSUSED */ 723 static dladm_status_t 724 do_check_zone(datalink_id_t linkid, char **prop_val, uint_t val_cnt, 725 val_desc_t *vdp, boolean_t *needfreep) 726 { 727 zoneid_t zid; 728 729 if (val_cnt != 1) 730 return (DLADM_STATUS_BADVALCNT); 731 732 if ((zid = getzoneidbyname(*prop_val)) == -1) 733 return (DLADM_STATUS_BADVAL); 734 735 if (zid != GLOBAL_ZONEID) { 736 ushort_t flags; 737 738 if (zone_getattr(zid, ZONE_ATTR_FLAGS, &flags, 739 sizeof (flags)) < 0) { 740 return (dladm_errno2status(errno)); 741 } 742 743 if (!(flags & ZF_NET_EXCL)) { 744 return (DLADM_STATUS_BADVAL); 745 } 746 } 747 748 vdp->vd_val = zid; 749 *needfreep = B_FALSE; 750 return (DLADM_STATUS_OK); 751 } 752 753 static dladm_status_t 754 do_get_autopush(datalink_id_t linkid, char **prop_val, uint_t *val_cnt) 755 { 756 dld_ioc_ap_t dia; 757 int fd, i, len; 758 759 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 760 return (dladm_errno2status(errno)); 761 762 *val_cnt = 1; 763 dia.dia_linkid = linkid; 764 if (i_dladm_ioctl(fd, DLDIOC_GETAUTOPUSH, &dia, sizeof (dia)) < 0) { 765 (*prop_val)[0] = '\0'; 766 goto done; 767 } 768 769 for (i = 0, len = 0; i < dia.dia_npush; i++) { 770 if (i != 0) { 771 (void) snprintf(*prop_val + len, 772 DLADM_PROP_VAL_MAX - len, "%c", AP_DELIMITER); 773 len += 1; 774 } 775 (void) snprintf(*prop_val + len, DLADM_PROP_VAL_MAX - len, 776 "%s", dia.dia_aplist[i]); 777 len += strlen(dia.dia_aplist[i]); 778 if (dia.dia_anchor - 1 == i) { 779 (void) snprintf(*prop_val + len, 780 DLADM_PROP_VAL_MAX - len, "%c%s", AP_DELIMITER, 781 AP_ANCHOR); 782 len += (strlen(AP_ANCHOR) + 1); 783 } 784 } 785 786 done: 787 (void) close(fd); 788 return (DLADM_STATUS_OK); 789 } 790 791 static dladm_status_t 792 do_set_autopush(datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt) 793 { 794 dld_ioc_ap_t dia; 795 struct dlautopush *dlap = (struct dlautopush *)vdp->vd_val; 796 dladm_status_t status = DLADM_STATUS_OK; 797 int fd, i; 798 int ic_cmd; 799 800 if (val_cnt != 1) 801 return (DLADM_STATUS_BADVALCNT); 802 803 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 804 return (dladm_errno2status(errno)); 805 806 dia.dia_linkid = linkid; 807 if (dlap != NULL) { 808 dia.dia_anchor = dlap->dap_anchor; 809 dia.dia_npush = dlap->dap_npush; 810 for (i = 0; i < dia.dia_npush; i++) { 811 (void) strlcpy(dia.dia_aplist[i], dlap->dap_aplist[i], 812 FMNAMESZ+1); 813 } 814 ic_cmd = DLDIOC_SETAUTOPUSH; 815 } else { 816 ic_cmd = DLDIOC_CLRAUTOPUSH; 817 } 818 819 if (i_dladm_ioctl(fd, ic_cmd, &dia, sizeof (dia)) < 0) 820 status = dladm_errno2status(errno); 821 822 (void) close(fd); 823 return (status); 824 } 825 826 /* 827 * Add the specified module to the dlautopush structure; returns a 828 * DLADM_STATUS_* code. 829 */ 830 dladm_status_t 831 i_dladm_add_ap_module(const char *module, struct dlautopush *dlap) 832 { 833 if ((strlen(module) == 0) || (strlen(module) > FMNAMESZ)) 834 return (DLADM_STATUS_BADVAL); 835 836 if (strncasecmp(module, AP_ANCHOR, strlen(AP_ANCHOR)) == 0) { 837 /* 838 * We don't allow multiple anchors, and the anchor must 839 * be after at least one module. 840 */ 841 if (dlap->dap_anchor != 0) 842 return (DLADM_STATUS_BADVAL); 843 if (dlap->dap_npush == 0) 844 return (DLADM_STATUS_BADVAL); 845 846 dlap->dap_anchor = dlap->dap_npush; 847 return (DLADM_STATUS_OK); 848 } 849 if (dlap->dap_npush > MAXAPUSH) 850 return (DLADM_STATUS_BADVALCNT); 851 852 (void) strlcpy(dlap->dap_aplist[dlap->dap_npush++], module, 853 FMNAMESZ + 1); 854 855 return (DLADM_STATUS_OK); 856 } 857 858 /* 859 * Currently, both '.' and ' '(space) can be used as the delimiters between 860 * autopush modules. The former is used in dladm set-linkprop, and the 861 * latter is used in the autopush(1M) file. 862 */ 863 /* ARGSUSED */ 864 static dladm_status_t 865 do_check_autopush(datalink_id_t linkid, char **prop_val, uint_t val_cnt, 866 val_desc_t *vdp, boolean_t *needfreep) 867 { 868 char *module; 869 struct dlautopush *dlap; 870 dladm_status_t status; 871 char val[DLADM_PROP_VAL_MAX]; 872 char delimiters[4]; 873 874 if (val_cnt != 1) 875 return (DLADM_STATUS_BADVALCNT); 876 877 dlap = malloc(sizeof (struct dlautopush)); 878 if (dlap == NULL) 879 return (DLADM_STATUS_NOMEM); 880 881 (void) memset(dlap, 0, sizeof (struct dlautopush)); 882 (void) snprintf(delimiters, 4, " %c\n", AP_DELIMITER); 883 bcopy(*prop_val, val, DLADM_PROP_VAL_MAX); 884 module = strtok(val, delimiters); 885 while (module != NULL) { 886 status = i_dladm_add_ap_module(module, dlap); 887 if (status != DLADM_STATUS_OK) 888 return (status); 889 module = strtok(NULL, delimiters); 890 } 891 892 vdp->vd_val = (uintptr_t)dlap; 893 *needfreep = B_TRUE; 894 return (DLADM_STATUS_OK); 895 } 896 897 static dladm_status_t 898 do_get_rate_common(datalink_id_t linkid, char **prop_val, uint_t *val_cnt, 899 uint_t id) 900 { 901 wl_rates_t *wrp; 902 uint_t i; 903 wldp_t *gbuf = NULL; 904 dladm_status_t status = DLADM_STATUS_OK; 905 906 if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) { 907 status = DLADM_STATUS_NOMEM; 908 goto done; 909 } 910 911 status = i_dladm_wlan_get_ioctl(linkid, gbuf, id); 912 if (status != DLADM_STATUS_OK) 913 goto done; 914 915 wrp = (wl_rates_t *)gbuf->wldp_buf; 916 if (wrp->wl_rates_num > *val_cnt) { 917 status = DLADM_STATUS_TOOSMALL; 918 goto done; 919 } 920 921 if (wrp->wl_rates_rates[0] == 0) { 922 prop_val[0][0] = '\0'; 923 *val_cnt = 1; 924 goto done; 925 } 926 927 for (i = 0; i < wrp->wl_rates_num; i++) { 928 (void) snprintf(prop_val[i], DLADM_STRSIZE, "%.*f", 929 wrp->wl_rates_rates[i] % 2, 930 (float)wrp->wl_rates_rates[i] / 2); 931 } 932 *val_cnt = wrp->wl_rates_num; 933 934 done: 935 free(gbuf); 936 return (status); 937 } 938 939 static dladm_status_t 940 do_get_rate_prop(datalink_id_t linkid, char **prop_val, uint_t *val_cnt) 941 { 942 return (do_get_rate_common(linkid, prop_val, val_cnt, 943 WL_DESIRED_RATES)); 944 } 945 946 static dladm_status_t 947 do_get_rate_mod(datalink_id_t linkid, char **prop_val, uint_t *val_cnt) 948 { 949 return (do_get_rate_common(linkid, prop_val, val_cnt, 950 WL_SUPPORTED_RATES)); 951 } 952 953 static dladm_status_t 954 do_set_rate(datalink_id_t linkid, dladm_wlan_rates_t *rates) 955 { 956 int i; 957 uint_t len; 958 wldp_t *gbuf; 959 wl_rates_t *wrp; 960 dladm_status_t status = DLADM_STATUS_OK; 961 962 if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) 963 return (DLADM_STATUS_NOMEM); 964 965 (void) memset(gbuf, 0, MAX_BUF_LEN); 966 967 wrp = (wl_rates_t *)gbuf->wldp_buf; 968 for (i = 0; i < rates->wr_cnt; i++) 969 wrp->wl_rates_rates[i] = rates->wr_rates[i]; 970 wrp->wl_rates_num = rates->wr_cnt; 971 972 len = offsetof(wl_rates_t, wl_rates_rates) + 973 (rates->wr_cnt * sizeof (char)) + WIFI_BUF_OFFSET; 974 status = i_dladm_wlan_ioctl(linkid, gbuf, WL_DESIRED_RATES, len, 975 WLAN_SET_PARAM, len); 976 977 free(gbuf); 978 return (status); 979 } 980 981 static dladm_status_t 982 do_set_rate_prop(datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt) 983 { 984 dladm_wlan_rates_t rates; 985 dladm_status_t status; 986 987 if (val_cnt != 1) 988 return (DLADM_STATUS_BADVALCNT); 989 990 rates.wr_cnt = 1; 991 rates.wr_rates[0] = vdp[0].vd_val; 992 993 status = do_set_rate(linkid, &rates); 994 995 done: 996 return (status); 997 } 998 999 /* ARGSUSED */ 1000 static dladm_status_t 1001 do_check_rate(datalink_id_t linkid, char **prop_val, uint_t val_cnt, 1002 val_desc_t *vdp, boolean_t *needfreep) 1003 { 1004 int i; 1005 uint_t modval_cnt = MAX_SUPPORT_RATES; 1006 char *buf, **modval; 1007 dladm_status_t status; 1008 1009 if (val_cnt != 1) 1010 return (DLADM_STATUS_BADVALCNT); 1011 1012 buf = malloc((sizeof (char *) + DLADM_STRSIZE) * 1013 MAX_SUPPORT_RATES); 1014 if (buf == NULL) { 1015 status = DLADM_STATUS_NOMEM; 1016 goto done; 1017 } 1018 1019 modval = (char **)(void *)buf; 1020 for (i = 0; i < MAX_SUPPORT_RATES; i++) { 1021 modval[i] = buf + sizeof (char *) * MAX_SUPPORT_RATES + 1022 i * DLADM_STRSIZE; 1023 } 1024 1025 status = do_get_rate_mod(linkid, modval, &modval_cnt); 1026 if (status != DLADM_STATUS_OK) 1027 goto done; 1028 1029 for (i = 0; i < modval_cnt; i++) { 1030 if (strcasecmp(*prop_val, modval[i]) == 0) { 1031 vdp->vd_val = (uint_t)(atof(*prop_val) * 2); 1032 status = DLADM_STATUS_OK; 1033 1034 /* 1035 * Does not need the caller to free the vdp->vd_val 1036 */ 1037 *needfreep = B_FALSE; 1038 break; 1039 } 1040 } 1041 if (i == modval_cnt) 1042 status = DLADM_STATUS_BADVAL; 1043 done: 1044 free(buf); 1045 return (status); 1046 } 1047 1048 static dladm_status_t 1049 do_get_phyconf(datalink_id_t linkid, wldp_t *gbuf) 1050 { 1051 return (i_dladm_wlan_get_ioctl(linkid, gbuf, WL_PHY_CONFIG)); 1052 } 1053 1054 static dladm_status_t 1055 do_get_channel_prop(datalink_id_t linkid, char **prop_val, uint_t *val_cnt) 1056 { 1057 uint32_t channel; 1058 wldp_t *gbuf; 1059 dladm_status_t status = DLADM_STATUS_OK; 1060 1061 if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) 1062 return (DLADM_STATUS_NOMEM); 1063 1064 if ((status = do_get_phyconf(linkid, gbuf)) != DLADM_STATUS_OK) 1065 goto done; 1066 1067 if (!i_dladm_wlan_convert_chan((wl_phy_conf_t *)gbuf->wldp_buf, 1068 &channel)) { 1069 status = DLADM_STATUS_NOTFOUND; 1070 goto done; 1071 } 1072 1073 (void) snprintf(*prop_val, DLADM_STRSIZE, "%u", channel); 1074 *val_cnt = 1; 1075 1076 done: 1077 free(gbuf); 1078 return (status); 1079 } 1080 1081 static dladm_status_t 1082 do_get_powermode(datalink_id_t linkid, wldp_t *gbuf) 1083 { 1084 return (i_dladm_wlan_get_ioctl(linkid, gbuf, WL_POWER_MODE)); 1085 } 1086 1087 static dladm_status_t 1088 do_get_powermode_prop(datalink_id_t linkid, char **prop_val, uint_t *val_cnt) 1089 { 1090 wl_ps_mode_t *mode; 1091 const char *s; 1092 wldp_t *gbuf; 1093 dladm_status_t status = DLADM_STATUS_OK; 1094 1095 if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) 1096 return (DLADM_STATUS_NOMEM); 1097 1098 if ((status = do_get_powermode(linkid, gbuf)) != DLADM_STATUS_OK) 1099 goto done; 1100 1101 mode = (wl_ps_mode_t *)(gbuf->wldp_buf); 1102 switch (mode->wl_ps_mode) { 1103 case WL_PM_AM: 1104 s = "off"; 1105 break; 1106 case WL_PM_MPS: 1107 s = "max"; 1108 break; 1109 case WL_PM_FAST: 1110 s = "fast"; 1111 break; 1112 default: 1113 status = DLADM_STATUS_NOTFOUND; 1114 goto done; 1115 } 1116 (void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s); 1117 *val_cnt = 1; 1118 1119 done: 1120 free(gbuf); 1121 return (status); 1122 } 1123 1124 static dladm_status_t 1125 do_set_powermode(datalink_id_t linkid, dladm_wlan_powermode_t *pm) 1126 { 1127 wl_ps_mode_t ps_mode; 1128 1129 (void) memset(&ps_mode, 0xff, sizeof (ps_mode)); 1130 1131 switch (*pm) { 1132 case DLADM_WLAN_PM_OFF: 1133 ps_mode.wl_ps_mode = WL_PM_AM; 1134 break; 1135 case DLADM_WLAN_PM_MAX: 1136 ps_mode.wl_ps_mode = WL_PM_MPS; 1137 break; 1138 case DLADM_WLAN_PM_FAST: 1139 ps_mode.wl_ps_mode = WL_PM_FAST; 1140 break; 1141 default: 1142 return (DLADM_STATUS_NOTSUP); 1143 } 1144 return (i_dladm_wlan_set_ioctl(linkid, WL_POWER_MODE, &ps_mode, 1145 sizeof (ps_mode))); 1146 } 1147 1148 /* ARGSUSED */ 1149 static dladm_status_t 1150 do_set_powermode_prop(datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt) 1151 { 1152 dladm_wlan_powermode_t powermode = (dladm_wlan_powermode_t)vdp->vd_val; 1153 dladm_status_t status; 1154 1155 if (val_cnt != 1) 1156 return (DLADM_STATUS_BADVALCNT); 1157 1158 status = do_set_powermode(linkid, &powermode); 1159 1160 return (status); 1161 } 1162 1163 static dladm_status_t 1164 do_get_radio(datalink_id_t linkid, wldp_t *gbuf) 1165 { 1166 return (i_dladm_wlan_get_ioctl(linkid, gbuf, WL_RADIO)); 1167 } 1168 1169 static dladm_status_t 1170 do_get_radio_prop(datalink_id_t linkid, char **prop_val, uint_t *val_cnt) 1171 { 1172 wl_radio_t radio; 1173 const char *s; 1174 wldp_t *gbuf; 1175 dladm_status_t status = DLADM_STATUS_OK; 1176 1177 if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) 1178 return (DLADM_STATUS_NOMEM); 1179 1180 if ((status = do_get_radio(linkid, gbuf)) != DLADM_STATUS_OK) 1181 goto done; 1182 1183 radio = *(wl_radio_t *)(gbuf->wldp_buf); 1184 switch (radio) { 1185 case B_TRUE: 1186 s = "on"; 1187 break; 1188 case B_FALSE: 1189 s = "off"; 1190 break; 1191 default: 1192 status = DLADM_STATUS_NOTFOUND; 1193 goto done; 1194 } 1195 (void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s); 1196 *val_cnt = 1; 1197 1198 done: 1199 free(gbuf); 1200 return (status); 1201 } 1202 1203 static dladm_status_t 1204 do_set_radio(datalink_id_t linkid, dladm_wlan_radio_t *radio) 1205 { 1206 wl_radio_t r; 1207 1208 switch (*radio) { 1209 case DLADM_WLAN_RADIO_ON: 1210 r = B_TRUE; 1211 break; 1212 case DLADM_WLAN_RADIO_OFF: 1213 r = B_FALSE; 1214 break; 1215 default: 1216 return (DLADM_STATUS_NOTSUP); 1217 } 1218 return (i_dladm_wlan_set_ioctl(linkid, WL_RADIO, &r, sizeof (r))); 1219 } 1220 1221 /* ARGSUSED */ 1222 static dladm_status_t 1223 do_set_radio_prop(datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt) 1224 { 1225 dladm_wlan_radio_t radio = (dladm_wlan_radio_t)vdp->vd_val; 1226 dladm_status_t status; 1227 1228 if (val_cnt != 1) 1229 return (DLADM_STATUS_BADVALCNT); 1230 1231 status = do_set_radio(linkid, &radio); 1232 1233 return (status); 1234 } 1235 1236 static dladm_status_t 1237 i_dladm_set_linkprop_db(datalink_id_t linkid, const char *prop_name, 1238 char **prop_val, uint_t val_cnt) 1239 { 1240 char buf[MAXLINELEN]; 1241 int i; 1242 dladm_conf_t conf; 1243 dladm_status_t status; 1244 1245 status = dladm_read_conf(linkid, &conf); 1246 if (status != DLADM_STATUS_OK) 1247 return (status); 1248 1249 /* 1250 * reset case. 1251 */ 1252 if (val_cnt == 0) { 1253 status = dladm_unset_conf_field(conf, prop_name); 1254 if (status == DLADM_STATUS_OK) 1255 status = dladm_write_conf(conf); 1256 goto done; 1257 } 1258 1259 buf[0] = '\0'; 1260 for (i = 0; i < val_cnt; i++) { 1261 (void) strlcat(buf, prop_val[i], MAXLINELEN); 1262 if (i != val_cnt - 1) 1263 (void) strlcat(buf, ",", MAXLINELEN); 1264 } 1265 1266 status = dladm_set_conf_field(conf, prop_name, DLADM_TYPE_STR, buf); 1267 if (status == DLADM_STATUS_OK) 1268 status = dladm_write_conf(conf); 1269 1270 done: 1271 dladm_destroy_conf(conf); 1272 return (status); 1273 } 1274 1275 static dladm_status_t 1276 i_dladm_get_linkprop_db(datalink_id_t linkid, const char *prop_name, 1277 char **prop_val, uint_t *val_cntp) 1278 { 1279 char buf[MAXLINELEN], *str; 1280 uint_t cnt = 0; 1281 dladm_conf_t conf; 1282 dladm_status_t status; 1283 1284 status = dladm_read_conf(linkid, &conf); 1285 if (status != DLADM_STATUS_OK) 1286 return (status); 1287 1288 status = dladm_get_conf_field(conf, prop_name, buf, MAXLINELEN); 1289 if (status != DLADM_STATUS_OK) 1290 goto done; 1291 1292 str = strtok(buf, ","); 1293 while (str != NULL) { 1294 if (cnt == *val_cntp) { 1295 status = DLADM_STATUS_TOOSMALL; 1296 goto done; 1297 } 1298 (void) strlcpy(prop_val[cnt++], str, DLADM_PROP_VAL_MAX); 1299 str = strtok(NULL, ","); 1300 } 1301 1302 *val_cntp = cnt; 1303 1304 done: 1305 dladm_destroy_conf(conf); 1306 return (status); 1307 } 1308