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 <sys/types.h> 27 #include <unistd.h> 28 #include <errno.h> 29 #include <fcntl.h> 30 #include <assert.h> 31 #include <ctype.h> 32 #include <strings.h> 33 #include <sys/stat.h> 34 #include <sys/dld.h> 35 #include <sys/vlan.h> 36 #include <zone.h> 37 #include <librcm.h> 38 #include <libdlpi.h> 39 #include <libdevinfo.h> 40 #include <libdlaggr.h> 41 #include <libdlvlan.h> 42 #include <libdllink.h> 43 #include <libdlmgmt.h> 44 #include <libdladm_impl.h> 45 #include <libinetutil.h> 46 47 /* 48 * Return the attributes of the specified datalink from the DLD driver. 49 */ 50 static dladm_status_t 51 i_dladm_info(int fd, const datalink_id_t linkid, dladm_attr_t *dap) 52 { 53 dld_ioc_attr_t dia; 54 55 dia.dia_linkid = linkid; 56 57 if (i_dladm_ioctl(fd, DLDIOC_ATTR, &dia, sizeof (dia)) < 0) 58 return (dladm_errno2status(errno)); 59 60 dap->da_max_sdu = dia.dia_max_sdu; 61 62 return (DLADM_STATUS_OK); 63 } 64 65 struct i_dladm_walk_arg { 66 dladm_walkcb_t *fn; 67 void *arg; 68 }; 69 70 static int 71 i_dladm_walk(datalink_id_t linkid, void *arg) 72 { 73 struct i_dladm_walk_arg *walk_arg = arg; 74 char link[MAXLINKNAMELEN]; 75 76 if (dladm_datalink_id2info(linkid, NULL, NULL, NULL, link, 77 sizeof (link)) == DLADM_STATUS_OK) { 78 return (walk_arg->fn(link, walk_arg->arg)); 79 } 80 81 return (DLADM_WALK_CONTINUE); 82 } 83 84 /* 85 * Walk all datalinks. 86 */ 87 dladm_status_t 88 dladm_walk(dladm_walkcb_t *fn, void *arg, datalink_class_t class, 89 datalink_media_t dmedia, uint32_t flags) 90 { 91 struct i_dladm_walk_arg walk_arg; 92 93 walk_arg.fn = fn; 94 walk_arg.arg = arg; 95 return (dladm_walk_datalink_id(i_dladm_walk, &walk_arg, 96 class, dmedia, flags)); 97 } 98 99 /* 100 * These routines are used by administration tools such as dladm(1M) to 101 * iterate through the list of MAC interfaces 102 */ 103 104 typedef struct dladm_mac_dev { 105 char dm_name[MAXNAMELEN]; 106 struct dladm_mac_dev *dm_next; 107 } dladm_mac_dev_t; 108 109 typedef struct macadm_walk { 110 dladm_mac_dev_t *dmd_dev_list; 111 } dladm_mac_walk_t; 112 113 /* 114 * Local callback invoked for each DDI_NT_NET node. 115 */ 116 /* ARGSUSED */ 117 static int 118 i_dladm_mac_walk(di_node_t node, di_minor_t minor, void *arg) 119 { 120 dladm_mac_walk_t *dmwp = arg; 121 dladm_mac_dev_t *dmdp = dmwp->dmd_dev_list; 122 dladm_mac_dev_t **last_dmdp = &dmwp->dmd_dev_list; 123 char mac[MAXNAMELEN]; 124 125 (void) snprintf(mac, MAXNAMELEN, "%s%d", 126 di_driver_name(node), di_instance(node)); 127 128 /* 129 * Skip aggregations. 130 */ 131 if (strcmp("aggr", di_driver_name(node)) == 0) 132 return (DI_WALK_CONTINUE); 133 134 /* 135 * Skip softmacs. 136 */ 137 if (strcmp("softmac", di_driver_name(node)) == 0) 138 return (DI_WALK_CONTINUE); 139 140 while (dmdp) { 141 /* 142 * Skip duplicates. 143 */ 144 if (strcmp(dmdp->dm_name, mac) == 0) 145 return (DI_WALK_CONTINUE); 146 147 last_dmdp = &dmdp->dm_next; 148 dmdp = dmdp->dm_next; 149 } 150 151 if ((dmdp = malloc(sizeof (*dmdp))) == NULL) 152 return (DI_WALK_CONTINUE); 153 154 (void) strlcpy(dmdp->dm_name, mac, MAXNAMELEN); 155 dmdp->dm_next = NULL; 156 *last_dmdp = dmdp; 157 158 return (DI_WALK_CONTINUE); 159 } 160 161 /* 162 * Invoke the specified callback for each DDI_NT_NET node. 163 */ 164 dladm_status_t 165 dladm_mac_walk(int (*fn)(const char *, void *arg), void *arg) 166 { 167 di_node_t root; 168 dladm_mac_walk_t dmw; 169 dladm_mac_dev_t *dmdp, *next; 170 boolean_t done = B_FALSE; 171 172 if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 173 return (dladm_errno2status(errno)); 174 175 dmw.dmd_dev_list = NULL; 176 177 (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dmw, 178 i_dladm_mac_walk); 179 180 di_fini(root); 181 182 dmdp = dmw.dmd_dev_list; 183 for (dmdp = dmw.dmd_dev_list; dmdp != NULL; dmdp = next) { 184 next = dmdp->dm_next; 185 if (!done && 186 ((*fn)(dmdp->dm_name, arg) == DLADM_WALK_TERMINATE)) { 187 done = B_TRUE; 188 } 189 free(dmdp); 190 } 191 192 return (DLADM_STATUS_OK); 193 } 194 195 /* 196 * Get the current attributes of the specified datalink. 197 */ 198 dladm_status_t 199 dladm_info(datalink_id_t linkid, dladm_attr_t *dap) 200 { 201 int fd; 202 dladm_status_t status; 203 204 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 205 return (dladm_errno2status(errno)); 206 207 status = i_dladm_info(fd, linkid, dap); 208 209 (void) close(fd); 210 return (status); 211 } 212 213 const char * 214 dladm_linkstate2str(link_state_t state, char *buf) 215 { 216 const char *s; 217 218 switch (state) { 219 case LINK_STATE_UP: 220 s = "up"; 221 break; 222 case LINK_STATE_DOWN: 223 s = "down"; 224 break; 225 default: 226 s = "unknown"; 227 break; 228 } 229 (void) snprintf(buf, DLADM_STRSIZE, "%s", s); 230 return (buf); 231 } 232 233 const char * 234 dladm_linkduplex2str(link_duplex_t duplex, char *buf) 235 { 236 const char *s; 237 238 switch (duplex) { 239 case LINK_DUPLEX_FULL: 240 s = "full"; 241 break; 242 case LINK_DUPLEX_HALF: 243 s = "half"; 244 break; 245 default: 246 s = "unknown"; 247 break; 248 } 249 (void) snprintf(buf, DLADM_STRSIZE, "%s", s); 250 return (buf); 251 } 252 253 /* 254 * Set zoneid of a given link. Note that this function takes a link name 255 * argument instead of a linkid, because a data-link (and its linkid) could 256 * be created implicitly as the result of this function. For example, a VLAN 257 * could be created if a VLAN PPA hack name is assigned to an exclusive 258 * non-global zone. 259 */ 260 dladm_status_t 261 dladm_setzid(const char *dlname, char *zone_name) 262 { 263 datalink_id_t linkid; 264 char *val; 265 char **prop_val; 266 char link[MAXLINKNAMELEN]; 267 uint_t ppa; 268 char dev[DLPI_LINKNAME_MAX]; 269 int valsize; 270 dladm_status_t status = DLADM_STATUS_OK; 271 char *prop_name = "zone"; 272 boolean_t needfree = B_FALSE; 273 char delim = ':'; 274 275 /* If the link does not exist, it is a ppa-hacked vlan. */ 276 status = dladm_name2info(dlname, &linkid, NULL, NULL, NULL); 277 switch (status) { 278 case DLADM_STATUS_NOTFOUND: 279 if (strlen(dlname) > MAXLINKNAMELEN) 280 return (DLADM_STATUS_BADVAL); 281 282 if (strlen(zone_name) > ZONENAME_MAX) 283 return (DLADM_STATUS_BADVAL); 284 285 status = dladm_parselink(dlname, dev, &ppa); 286 if (status != DLADM_STATUS_OK) 287 return (status); 288 289 ppa = (uint_t)DLS_PPA2INST(ppa); 290 (void) snprintf(link, sizeof (link), "%s%d", dev, ppa); 291 292 status = dladm_name2info(link, &linkid, NULL, NULL, NULL); 293 if (status != DLADM_STATUS_OK) 294 return (status); 295 296 /* 297 * Since the link does not exist as yet, we've to pass the 298 * link name too as part of data, so that the kernel can 299 * create the link. Hence, we're packing the zone_name and 300 * the link name into val. 301 */ 302 valsize = ZONENAME_MAX + MAXLINKNAMELEN + 1; 303 val = malloc(valsize); 304 if (val == NULL) 305 return (DLADM_STATUS_NOMEM); 306 needfree = B_TRUE; 307 308 (void) snprintf(val, valsize, "%s%c%s", zone_name, 309 delim, dlname); 310 311 break; 312 case DLADM_STATUS_OK: 313 /* 314 * The link exists, so only the zone_name is being passed as 315 * val. We could also pass zone_name + linkname like in the 316 * previous case just to maintain consistency, but other calls 317 * like set_linkprop() in dladm.c [which is called when we run 318 * 'dladm set-linkprop -p zone <linkname>' at the command line] 319 * pass in the value entered at the command line [which is zone 320 * name] as val. 321 */ 322 val = zone_name; 323 break; 324 default: 325 return (DLADM_STATUS_FAILED); 326 } 327 328 prop_val = &val; 329 status = dladm_set_linkprop(linkid, prop_name, prop_val, 1, 330 DLADM_OPT_ACTIVE); 331 332 if (needfree) 333 free(val); 334 return (status); 335 } 336 337 /* 338 * Case 1: rename an existing link1 to a link2 that does not exist. 339 * Result: <linkid1, link2> 340 */ 341 static dladm_status_t 342 i_dladm_rename_link_c1(datalink_id_t linkid1, const char *link1, 343 const char *link2, uint32_t flags) 344 { 345 dld_ioc_rename_t dir; 346 dladm_conf_t conf; 347 dladm_status_t status = DLADM_STATUS_OK; 348 int fd; 349 350 /* 351 * Link is currently available. Check to see whether anything is 352 * holding this link to prevent a rename operation. 353 */ 354 if (flags & DLADM_OPT_ACTIVE) { 355 dir.dir_linkid1 = linkid1; 356 dir.dir_linkid2 = DATALINK_INVALID_LINKID; 357 (void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN); 358 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 359 return (dladm_errno2status(errno)); 360 361 if (i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, sizeof (dir)) < 0) { 362 status = dladm_errno2status(errno); 363 (void) close(fd); 364 return (status); 365 } 366 } 367 368 status = dladm_remap_datalink_id(linkid1, link2); 369 if (status != DLADM_STATUS_OK) 370 goto done; 371 372 /* 373 * Flush the current mapping to persistent configuration. 374 */ 375 if ((flags & DLADM_OPT_PERSIST) && 376 (((status = dladm_read_conf(linkid1, &conf)) != DLADM_STATUS_OK) || 377 ((status = dladm_write_conf(conf)) != DLADM_STATUS_OK))) { 378 (void) dladm_remap_datalink_id(linkid1, link1); 379 } 380 done: 381 if (flags & DLADM_OPT_ACTIVE) { 382 if (status != DLADM_STATUS_OK) { 383 (void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN); 384 (void) i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, 385 sizeof (dir)); 386 } 387 (void) close(fd); 388 } 389 return (status); 390 } 391 392 typedef struct link_hold_arg_s { 393 datalink_id_t linkid; 394 datalink_id_t holder; 395 uint32_t flags; 396 } link_hold_arg_t; 397 398 static int 399 i_dladm_aggr_link_hold(datalink_id_t aggrid, void *arg) 400 { 401 link_hold_arg_t *hold_arg = arg; 402 dladm_aggr_grp_attr_t ginfo; 403 dladm_status_t status; 404 int i; 405 406 status = dladm_aggr_info(aggrid, &ginfo, hold_arg->flags); 407 if (status != DLADM_STATUS_OK) 408 return (DLADM_WALK_CONTINUE); 409 410 for (i = 0; i < ginfo.lg_nports; i++) { 411 if (ginfo.lg_ports[i].lp_linkid == hold_arg->linkid) { 412 hold_arg->holder = aggrid; 413 return (DLADM_WALK_TERMINATE); 414 } 415 } 416 return (DLADM_WALK_CONTINUE); 417 } 418 419 static int 420 i_dladm_vlan_link_hold(datalink_id_t vlanid, void *arg) 421 { 422 link_hold_arg_t *hold_arg = arg; 423 dladm_vlan_attr_t vinfo; 424 dladm_status_t status; 425 426 status = dladm_vlan_info(vlanid, &vinfo, hold_arg->flags); 427 if (status != DLADM_STATUS_OK) 428 return (DLADM_WALK_CONTINUE); 429 430 if (vinfo.dv_linkid == hold_arg->linkid) { 431 hold_arg->holder = vlanid; 432 return (DLADM_WALK_TERMINATE); 433 } 434 return (DLADM_WALK_CONTINUE); 435 } 436 437 /* 438 * Case 2: rename an available physical link link1 to a REMOVED physical link 439 * link2. As a result, link1 directly inherits all datalinks configured 440 * over link2 (linkid2). 441 * Result: <linkid2, link2, link1_phymaj, link1_phyinst, link1_devname, 442 * link2_other_attr> 443 */ 444 static dladm_status_t 445 i_dladm_rename_link_c2(datalink_id_t linkid1, datalink_id_t linkid2) 446 { 447 rcm_handle_t *rcm_hdl = NULL; 448 nvlist_t *nvl = NULL; 449 link_hold_arg_t arg; 450 dld_ioc_rename_t dir; 451 int fd; 452 dladm_conf_t conf1, conf2; 453 char devname[MAXLINKNAMELEN]; 454 uint64_t phymaj, phyinst; 455 dladm_status_t status = DLADM_STATUS_OK; 456 457 /* 458 * First check if linkid1 is associated with any persistent 459 * aggregations or VLANs. If yes, return BUSY. 460 */ 461 arg.linkid = linkid1; 462 arg.holder = DATALINK_INVALID_LINKID; 463 arg.flags = DLADM_OPT_PERSIST; 464 (void) dladm_walk_datalink_id(i_dladm_aggr_link_hold, &arg, 465 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 466 if (arg.holder != DATALINK_INVALID_LINKID) 467 return (DLADM_STATUS_LINKBUSY); 468 469 arg.flags = DLADM_OPT_PERSIST; 470 (void) dladm_walk_datalink_id(i_dladm_vlan_link_hold, &arg, 471 DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 472 if (arg.holder != DATALINK_INVALID_LINKID) 473 return (DLADM_STATUS_LINKBUSY); 474 475 /* 476 * Send DLDIOC_RENAME to request to rename link1's linkid to 477 * be linkid2. This will check whether link1 is used by any 478 * aggregations or VLANs, or is held by any application. If yes, 479 * return failure. 480 */ 481 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 482 return (dladm_errno2status(errno)); 483 484 dir.dir_linkid1 = linkid1; 485 dir.dir_linkid2 = linkid2; 486 if (i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, sizeof (dir)) < 0) 487 status = dladm_errno2status(errno); 488 489 if (status != DLADM_STATUS_OK) { 490 (void) close(fd); 491 return (status); 492 } 493 494 /* 495 * Now change the phymaj, phyinst and devname associated with linkid1 496 * to be associated with linkid2. Before doing that, the old active 497 * linkprop of linkid1 should be deleted. 498 */ 499 (void) dladm_set_linkprop(linkid1, NULL, NULL, 0, DLADM_OPT_ACTIVE); 500 501 if (((status = dladm_read_conf(linkid1, &conf1)) != DLADM_STATUS_OK) || 502 ((status = dladm_get_conf_field(conf1, FDEVNAME, devname, 503 MAXLINKNAMELEN)) != DLADM_STATUS_OK) || 504 ((status = dladm_get_conf_field(conf1, FPHYMAJ, &phymaj, 505 sizeof (uint64_t))) != DLADM_STATUS_OK) || 506 ((status = dladm_get_conf_field(conf1, FPHYINST, &phyinst, 507 sizeof (uint64_t))) != DLADM_STATUS_OK) || 508 ((status = dladm_read_conf(linkid2, &conf2)) != DLADM_STATUS_OK)) { 509 dir.dir_linkid1 = linkid2; 510 dir.dir_linkid2 = linkid1; 511 (void) dladm_init_linkprop(linkid1, B_FALSE); 512 (void) i_dladm_ioctl(fd, DLDIOC_RENAME, &dir, sizeof (dir)); 513 (void) close(fd); 514 return (status); 515 } 516 (void) close(fd); 517 518 dladm_destroy_conf(conf1); 519 (void) dladm_set_conf_field(conf2, FDEVNAME, DLADM_TYPE_STR, devname); 520 (void) dladm_set_conf_field(conf2, FPHYMAJ, DLADM_TYPE_UINT64, &phymaj); 521 (void) dladm_set_conf_field(conf2, FPHYINST, 522 DLADM_TYPE_UINT64, &phyinst); 523 (void) dladm_write_conf(conf2); 524 dladm_destroy_conf(conf2); 525 526 /* 527 * Delete link1 and mark link2 up. 528 */ 529 (void) dladm_destroy_datalink_id(linkid1, DLADM_OPT_ACTIVE | 530 DLADM_OPT_PERSIST); 531 (void) dladm_remove_conf(linkid1); 532 (void) dladm_up_datalink_id(linkid2); 533 534 /* 535 * Now generate the RCM_RESOURCE_LINK_NEW sysevent which can be 536 * consumed by the RCM framework to restore all the datalink and 537 * IP configuration. 538 */ 539 status = DLADM_STATUS_FAILED; 540 if ((nvlist_alloc(&nvl, 0, 0) != 0) || 541 (nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid2) != 0)) { 542 goto done; 543 } 544 545 if (rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl) != RCM_SUCCESS) 546 goto done; 547 548 if (rcm_notify_event(rcm_hdl, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) == 549 RCM_SUCCESS) { 550 status = DLADM_STATUS_OK; 551 } 552 553 done: 554 if (rcm_hdl != NULL) 555 (void) rcm_free_handle(rcm_hdl); 556 if (nvl != NULL) 557 nvlist_free(nvl); 558 return (status); 559 } 560 561 /* 562 * case 3: rename a non-existent link to a REMOVED physical link. 563 * Set the removed physical link's device name to link1, so that 564 * when link1 attaches, it inherits all the link configuration of 565 * the removed physical link. 566 */ 567 static dladm_status_t 568 i_dladm_rename_link_c3(const char *link1, datalink_id_t linkid2) 569 { 570 dladm_conf_t conf; 571 dladm_status_t status; 572 573 if (!dladm_valid_linkname(link1)) 574 return (DLADM_STATUS_LINKINVAL); 575 576 status = dladm_read_conf(linkid2, &conf); 577 if (status != DLADM_STATUS_OK) 578 goto done; 579 580 if ((status = dladm_set_conf_field(conf, FDEVNAME, DLADM_TYPE_STR, 581 link1)) == DLADM_STATUS_OK) { 582 status = dladm_write_conf(conf); 583 } 584 585 dladm_destroy_conf(conf); 586 587 done: 588 return (status); 589 } 590 591 dladm_status_t 592 dladm_rename_link(const char *link1, const char *link2) 593 { 594 datalink_id_t linkid1 = DATALINK_INVALID_LINKID; 595 datalink_id_t linkid2 = DATALINK_INVALID_LINKID; 596 uint32_t flags1, flags2; 597 datalink_class_t class1, class2; 598 uint32_t media1, media2; 599 boolean_t remphy2 = B_FALSE; 600 dladm_status_t status; 601 602 (void) dladm_name2info(link1, &linkid1, &flags1, &class1, &media1); 603 if ((dladm_name2info(link2, &linkid2, &flags2, &class2, &media2) == 604 DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) && 605 (flags2 == DLADM_OPT_PERSIST)) { 606 /* 607 * see whether link2 is a removed physical link. 608 */ 609 remphy2 = B_TRUE; 610 } 611 612 if (linkid1 != DATALINK_INVALID_LINKID) { 613 if (linkid2 == DATALINK_INVALID_LINKID) { 614 /* 615 * case 1: rename an existing link to a link that 616 * does not exist. 617 */ 618 status = i_dladm_rename_link_c1(linkid1, link1, link2, 619 flags1); 620 } else if (remphy2) { 621 /* 622 * case 2: rename an available link to a REMOVED 623 * physical link. Return failure if link1 is not 624 * an active physical link. 625 */ 626 if ((class1 != class2) || (media1 != media2) || 627 !(flags1 & DLADM_OPT_ACTIVE)) { 628 status = DLADM_STATUS_BADARG; 629 } else { 630 status = i_dladm_rename_link_c2(linkid1, 631 linkid2); 632 } 633 } else { 634 status = DLADM_STATUS_EXIST; 635 } 636 } else if (remphy2) { 637 status = i_dladm_rename_link_c3(link1, linkid2); 638 } else { 639 status = DLADM_STATUS_NOTFOUND; 640 } 641 return (status); 642 } 643 644 typedef struct consumer_del_phys_arg_s { 645 datalink_id_t linkid; 646 } consumer_del_phys_arg_t; 647 648 static int 649 i_dladm_vlan_link_del(datalink_id_t vlanid, void *arg) 650 { 651 consumer_del_phys_arg_t *del_arg = arg; 652 dladm_vlan_attr_t vinfo; 653 dladm_status_t status; 654 655 status = dladm_vlan_info(vlanid, &vinfo, DLADM_OPT_PERSIST); 656 if (status != DLADM_STATUS_OK) 657 return (DLADM_WALK_CONTINUE); 658 659 if (vinfo.dv_linkid == del_arg->linkid) 660 (void) dladm_vlan_delete(vlanid, DLADM_OPT_PERSIST); 661 return (DLADM_WALK_CONTINUE); 662 } 663 664 static int 665 i_dladm_aggr_link_del(datalink_id_t aggrid, void *arg) 666 { 667 consumer_del_phys_arg_t *del_arg = arg; 668 dladm_aggr_grp_attr_t ginfo; 669 dladm_status_t status; 670 dladm_aggr_port_attr_db_t port[1]; 671 int i; 672 673 status = dladm_aggr_info(aggrid, &ginfo, DLADM_OPT_PERSIST); 674 if (status != DLADM_STATUS_OK) 675 return (DLADM_WALK_CONTINUE); 676 677 for (i = 0; i < ginfo.lg_nports; i++) 678 if (ginfo.lg_ports[i].lp_linkid == del_arg->linkid) 679 break; 680 681 if (i != ginfo.lg_nports) { 682 if (ginfo.lg_nports == 1 && i == 0) { 683 consumer_del_phys_arg_t aggr_del_arg; 684 685 /* 686 * First delete all the VLANs on this aggregation, then 687 * delete the aggregation itself. 688 */ 689 aggr_del_arg.linkid = aggrid; 690 (void) dladm_walk_datalink_id(i_dladm_vlan_link_del, 691 &aggr_del_arg, DATALINK_CLASS_VLAN, 692 DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 693 (void) dladm_aggr_delete(aggrid, DLADM_OPT_PERSIST); 694 } else { 695 port[0].lp_linkid = del_arg->linkid; 696 (void) dladm_aggr_remove(aggrid, 1, port, 697 DLADM_OPT_PERSIST); 698 } 699 } 700 return (DLADM_WALK_CONTINUE); 701 } 702 703 typedef struct del_phys_arg_s { 704 dladm_status_t rval; 705 } del_phys_arg_t; 706 707 static int 708 i_dladm_phys_delete(datalink_id_t linkid, void *arg) 709 { 710 uint32_t flags; 711 datalink_class_t class; 712 uint32_t media; 713 dladm_status_t status = DLADM_STATUS_OK; 714 del_phys_arg_t *del_phys_arg = arg; 715 consumer_del_phys_arg_t del_arg; 716 717 if ((status = dladm_datalink_id2info(linkid, &flags, &class, 718 &media, NULL, 0)) != DLADM_STATUS_OK) { 719 goto done; 720 } 721 722 /* 723 * see whether this link is a removed physical link. 724 */ 725 if ((class != DATALINK_CLASS_PHYS) || !(flags & DLADM_OPT_PERSIST) || 726 (flags & DLADM_OPT_ACTIVE)) { 727 status = DLADM_STATUS_BADARG; 728 goto done; 729 } 730 731 if (media == DL_ETHER) { 732 del_arg.linkid = linkid; 733 (void) dladm_walk_datalink_id(i_dladm_aggr_link_del, &del_arg, 734 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, 735 DLADM_OPT_PERSIST); 736 (void) dladm_walk_datalink_id(i_dladm_vlan_link_del, &del_arg, 737 DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, 738 DLADM_OPT_PERSIST); 739 } 740 741 (void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST); 742 (void) dladm_remove_conf(linkid); 743 744 done: 745 del_phys_arg->rval = status; 746 return (DLADM_WALK_CONTINUE); 747 } 748 749 dladm_status_t 750 dladm_phys_delete(datalink_id_t linkid) 751 { 752 del_phys_arg_t arg = {DLADM_STATUS_OK}; 753 754 if (linkid == DATALINK_ALL_LINKID) { 755 (void) dladm_walk_datalink_id(i_dladm_phys_delete, &arg, 756 DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, 757 DLADM_OPT_PERSIST); 758 return (DLADM_STATUS_OK); 759 } else { 760 (void) i_dladm_phys_delete(linkid, &arg); 761 return (arg.rval); 762 } 763 } 764 765 dladm_status_t 766 dladm_phys_info(datalink_id_t linkid, dladm_phys_attr_t *dpap, uint32_t flags) 767 { 768 dladm_status_t status; 769 770 assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST); 771 772 switch (flags) { 773 case DLADM_OPT_PERSIST: { 774 dladm_conf_t conf; 775 776 status = dladm_read_conf(linkid, &conf); 777 if (status != DLADM_STATUS_OK) 778 return (status); 779 780 status = dladm_get_conf_field(conf, FDEVNAME, dpap->dp_dev, 781 MAXLINKNAMELEN); 782 dladm_destroy_conf(conf); 783 return (status); 784 } 785 case DLADM_OPT_ACTIVE: { 786 dld_ioc_phys_attr_t dip; 787 int fd; 788 789 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 790 return (dladm_errno2status(errno)); 791 792 dip.dip_linkid = linkid; 793 if (i_dladm_ioctl(fd, DLDIOC_PHYS_ATTR, &dip, sizeof (dip)) 794 < 0) { 795 status = dladm_errno2status(errno); 796 (void) close(fd); 797 return (status); 798 } 799 (void) close(fd); 800 dpap->dp_novanity = dip.dip_novanity; 801 (void) strlcpy(dpap->dp_dev, dip.dip_dev, MAXLINKNAMELEN); 802 return (DLADM_STATUS_OK); 803 } 804 default: 805 return (DLADM_STATUS_BADARG); 806 } 807 } 808 809 typedef struct i_walk_dev_state_s { 810 const char *devname; 811 datalink_id_t linkid; 812 boolean_t found; 813 } i_walk_dev_state_t; 814 815 int 816 i_dladm_walk_dev2linkid(datalink_id_t linkid, void *arg) 817 { 818 dladm_phys_attr_t dpa; 819 dladm_status_t status; 820 i_walk_dev_state_t *statep = arg; 821 822 status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST); 823 if ((status == DLADM_STATUS_OK) && 824 (strcmp(statep->devname, dpa.dp_dev) == 0)) { 825 statep->found = B_TRUE; 826 statep->linkid = linkid; 827 return (DLADM_WALK_TERMINATE); 828 } 829 return (DLADM_WALK_CONTINUE); 830 } 831 832 /* 833 * Get the linkid from the physical device name. 834 */ 835 dladm_status_t 836 dladm_dev2linkid(const char *devname, datalink_id_t *linkidp) 837 { 838 i_walk_dev_state_t state; 839 840 state.found = B_FALSE; 841 state.devname = devname; 842 843 (void) dladm_walk_datalink_id(i_dladm_walk_dev2linkid, &state, 844 DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 845 if (state.found == B_TRUE) { 846 *linkidp = state.linkid; 847 return (DLADM_STATUS_OK); 848 } else { 849 return (dladm_errno2status(ENOENT)); 850 } 851 } 852 853 static int 854 parse_devname(const char *devname, char *driver, uint_t *ppa, size_t maxlen) 855 { 856 char *cp, *tp; 857 int len; 858 859 /* 860 * device name length must not be 0, and it must end with digit. 861 */ 862 if (((len = strlen(devname)) == 0) || !isdigit(devname[len - 1])) 863 return (EINVAL); 864 865 (void) strlcpy(driver, devname, maxlen); 866 cp = (char *)&driver[len - 1]; 867 868 for (tp = cp; isdigit(*tp); tp--) { 869 if (tp <= driver) 870 return (EINVAL); 871 } 872 873 *ppa = atoi(tp + 1); 874 *(tp + 1) = '\0'; 875 return (0); 876 } 877 878 dladm_status_t 879 dladm_linkid2legacyname(datalink_id_t linkid, char *dev, size_t len) 880 { 881 char devname[MAXLINKNAMELEN]; 882 uint16_t vid = VLAN_ID_NONE; 883 datalink_class_t class; 884 dladm_status_t status; 885 886 status = dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0); 887 if (status != DLADM_STATUS_OK) 888 goto done; 889 890 /* 891 * If this is a VLAN, we must first determine the class and linkid of 892 * the link the VLAN has been created over. 893 */ 894 if (class == DATALINK_CLASS_VLAN) { 895 dladm_vlan_attr_t dva; 896 897 status = dladm_vlan_info(linkid, &dva, DLADM_OPT_ACTIVE); 898 if (status != DLADM_STATUS_OK) 899 goto done; 900 linkid = dva.dv_linkid; 901 vid = dva.dv_vid; 902 903 if ((status = dladm_datalink_id2info(linkid, NULL, &class, NULL, 904 NULL, 0)) != DLADM_STATUS_OK) { 905 goto done; 906 } 907 } 908 909 switch (class) { 910 case DATALINK_CLASS_AGGR: { 911 dladm_aggr_grp_attr_t dga; 912 913 status = dladm_aggr_info(linkid, &dga, DLADM_OPT_ACTIVE); 914 if (status != DLADM_STATUS_OK) 915 goto done; 916 917 if (dga.lg_key == 0) { 918 /* 919 * If the key was not specified when the aggregation 920 * is created, we cannot guess its /dev node name. 921 */ 922 status = DLADM_STATUS_BADARG; 923 goto done; 924 } 925 (void) snprintf(devname, MAXLINKNAMELEN, "aggr%d", dga.lg_key); 926 break; 927 } 928 case DATALINK_CLASS_PHYS: { 929 dladm_phys_attr_t dpa; 930 931 status = dladm_phys_info(linkid, &dpa, DLADM_OPT_PERSIST); 932 if (status != DLADM_STATUS_OK) 933 goto done; 934 935 (void) strlcpy(devname, dpa.dp_dev, MAXLINKNAMELEN); 936 break; 937 } 938 default: 939 status = DLADM_STATUS_BADARG; 940 goto done; 941 } 942 943 if (vid != VLAN_ID_NONE) { 944 char drv[MAXNAMELEN]; 945 uint_t ppa; 946 947 if (parse_devname(devname, drv, &ppa, MAXNAMELEN) != 0) { 948 status = DLADM_STATUS_BADARG; 949 goto done; 950 } 951 if (snprintf(dev, len, "%s%d", drv, vid * 1000 + ppa) >= len) 952 status = DLADM_STATUS_TOOSMALL; 953 } else { 954 if (strlcpy(dev, devname, len) >= len) 955 status = DLADM_STATUS_TOOSMALL; 956 } 957 958 done: 959 return (status); 960 } 961 962 dladm_status_t 963 dladm_get_single_mac_stat(datalink_id_t linkid, const char *name, uint8_t type, 964 void *val) 965 { 966 char module[DLPI_LINKNAME_MAX]; 967 uint_t instance; 968 char link[DLPI_LINKNAME_MAX]; 969 dladm_status_t status; 970 uint32_t flags, media; 971 kstat_ctl_t *kcp; 972 kstat_t *ksp; 973 dladm_phys_attr_t dpap; 974 975 if ((status = dladm_datalink_id2info(linkid, &flags, NULL, &media, 976 link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK) 977 return (status); 978 979 if (media != DL_ETHER) 980 return (DLADM_STATUS_LINKINVAL); 981 982 status = dladm_phys_info(linkid, &dpap, DLADM_OPT_PERSIST); 983 984 if (status != DLADM_STATUS_OK) 985 return (status); 986 987 status = dladm_parselink(dpap.dp_dev, module, &instance); 988 989 if (status != DLADM_STATUS_OK) 990 return (status); 991 992 if ((kcp = kstat_open()) == NULL) 993 return (dladm_errno2status(errno)); 994 995 /* 996 * The kstat query could fail if the underlying MAC 997 * driver was already detached. 998 */ 999 if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL && 1000 (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) 1001 goto bail; 1002 1003 if (kstat_read(kcp, ksp, NULL) == -1) 1004 goto bail; 1005 1006 if (dladm_kstat_value(ksp, name, type, val) < 0) 1007 goto bail; 1008 1009 (void) kstat_close(kcp); 1010 return (DLADM_STATUS_OK); 1011 bail: 1012 (void) kstat_close(kcp); 1013 return (dladm_errno2status(errno)); 1014 1015 } 1016 1017 int 1018 dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf) 1019 { 1020 kstat_named_t *knp; 1021 1022 if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL) 1023 return (-1); 1024 1025 if (knp->data_type != type) 1026 return (-1); 1027 1028 switch (type) { 1029 case KSTAT_DATA_UINT64: 1030 *(uint64_t *)buf = knp->value.ui64; 1031 break; 1032 case KSTAT_DATA_UINT32: 1033 *(uint32_t *)buf = knp->value.ui32; 1034 break; 1035 default: 1036 return (-1); 1037 } 1038 1039 return (0); 1040 } 1041 1042 dladm_status_t 1043 dladm_parselink(const char *dev, char *provider, uint_t *ppa) 1044 { 1045 ifspec_t ifsp; 1046 1047 if (dev == NULL || !ifparse_ifspec(dev, &ifsp)) 1048 return (DLADM_STATUS_LINKINVAL); 1049 1050 if (provider != NULL) 1051 (void) strlcpy(provider, ifsp.ifsp_devnm, DLPI_LINKNAME_MAX); 1052 1053 if (ppa != NULL) 1054 *ppa = ifsp.ifsp_ppa; 1055 1056 return (DLADM_STATUS_OK); 1057 } 1058