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