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 <stdio.h> 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <string.h> 30 #include <fcntl.h> 31 #include <unistd.h> 32 #include <stropts.h> 33 #include <stdlib.h> 34 #include <errno.h> 35 #include <assert.h> 36 #include <strings.h> 37 #include <libintl.h> 38 #include <net/if_types.h> 39 #include <net/if_dl.h> 40 #include <sys/dld.h> 41 #include <libdllink.h> 42 #include <libdlvlan.h> 43 #include <libdlaggr.h> 44 #include <libdladm_impl.h> 45 46 /* 47 * Link Aggregation Administration Library. 48 * 49 * This library is used by administration tools such as dladm(1M) to 50 * configure link aggregations. 51 */ 52 53 /* Limits on buffer size for LAIOC_INFO request */ 54 #define MIN_INFO_SIZE (4*1024) 55 #define MAX_INFO_SIZE (128*1024) 56 57 static uchar_t zero_mac[] = {0, 0, 0, 0, 0, 0}; 58 #define VALID_PORT_MAC(mac) \ 59 (((mac) != NULL) && (bcmp(zero_mac, (mac), ETHERADDRL) != 0) && \ 60 (!(mac)[0] & 0x01)) 61 62 #define PORT_DELIMITER '.' 63 64 #define WRITE_PORT(portstr, portid, size) { \ 65 char pstr[LINKID_STR_WIDTH + 2]; \ 66 (void) snprintf(pstr, LINKID_STR_WIDTH + 2, "%d%c", \ 67 (portid), PORT_DELIMITER); \ 68 (void) strlcat((portstr), pstr, (size)); \ 69 } 70 71 #define READ_PORT(portstr, portid, status) { \ 72 errno = 0; \ 73 (status) = DLADM_STATUS_OK; \ 74 (portid) = (int)strtol((portstr), &(portstr), 10); \ 75 if (errno != 0 || *(portstr) != PORT_DELIMITER) { \ 76 (status) = DLADM_STATUS_REPOSITORYINVAL; \ 77 } else { \ 78 /* Skip the delimiter. */ \ 79 (portstr)++; \ 80 } \ 81 } 82 83 typedef struct dladm_aggr_modify_attr { 84 uint32_t ld_policy; 85 boolean_t ld_mac_fixed; 86 uchar_t ld_mac[ETHERADDRL]; 87 aggr_lacp_mode_t ld_lacp_mode; 88 aggr_lacp_timer_t ld_lacp_timer; 89 } dladm_aggr_modify_attr_t; 90 91 typedef struct policy_s { 92 char *pol_name; 93 uint32_t policy; 94 } policy_t; 95 96 static policy_t policies[] = { 97 {"L2", AGGR_POLICY_L2}, 98 {"L3", AGGR_POLICY_L3}, 99 {"L4", AGGR_POLICY_L4}}; 100 101 #define NPOLICIES (sizeof (policies) / sizeof (policy_t)) 102 103 typedef struct dladm_aggr_lacpmode_s { 104 char *mode_str; 105 aggr_lacp_mode_t mode_id; 106 } dladm_aggr_lacpmode_t; 107 108 static dladm_aggr_lacpmode_t lacp_modes[] = { 109 {"off", AGGR_LACP_OFF}, 110 {"active", AGGR_LACP_ACTIVE}, 111 {"passive", AGGR_LACP_PASSIVE}}; 112 113 #define NLACP_MODES (sizeof (lacp_modes) / sizeof (dladm_aggr_lacpmode_t)) 114 115 typedef struct dladm_aggr_lacptimer_s { 116 char *lt_str; 117 aggr_lacp_timer_t lt_id; 118 } dladm_aggr_lacptimer_t; 119 120 static dladm_aggr_lacptimer_t lacp_timers[] = { 121 {"short", AGGR_LACP_TIMER_SHORT}, 122 {"long", AGGR_LACP_TIMER_LONG}}; 123 124 #define NLACP_TIMERS (sizeof (lacp_timers) / sizeof (dladm_aggr_lacptimer_t)) 125 126 typedef struct dladm_aggr_port_state { 127 char *state_str; 128 aggr_port_state_t state_id; 129 } dladm_aggr_port_state_t; 130 131 static dladm_aggr_port_state_t port_states[] = { 132 {"standby", AGGR_PORT_STATE_STANDBY }, 133 {"attached", AGGR_PORT_STATE_ATTACHED } 134 }; 135 136 #define NPORT_STATES \ 137 (sizeof (port_states) / sizeof (dladm_aggr_port_state_t)) 138 139 static int 140 i_dladm_aggr_ioctl(int cmd, void *ptr) 141 { 142 int err, fd; 143 144 if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) 145 return (-1); 146 147 err = ioctl(fd, cmd, ptr); 148 (void) close(fd); 149 150 return (err); 151 } 152 153 /* 154 * Caller must free attr.lg_ports. The ptr pointer is advanced while convert 155 * the laioc_info_t to the dladm_aggr_grp_attr_t structure. 156 */ 157 static int 158 i_dladm_aggr_iocp2grpattr(void **ptr, dladm_aggr_grp_attr_t *attrp) 159 { 160 laioc_info_group_t *grp; 161 laioc_info_port_t *port; 162 int i; 163 void *where = (*ptr); 164 165 grp = (laioc_info_group_t *)where; 166 167 attrp->lg_linkid = grp->lg_linkid; 168 attrp->lg_key = grp->lg_key; 169 attrp->lg_nports = grp->lg_nports; 170 attrp->lg_policy = grp->lg_policy; 171 attrp->lg_lacp_mode = grp->lg_lacp_mode; 172 attrp->lg_lacp_timer = grp->lg_lacp_timer; 173 attrp->lg_force = grp->lg_force; 174 175 bcopy(grp->lg_mac, attrp->lg_mac, ETHERADDRL); 176 attrp->lg_mac_fixed = grp->lg_mac_fixed; 177 178 if ((attrp->lg_ports = malloc(grp->lg_nports * 179 sizeof (dladm_aggr_port_attr_t))) == NULL) { 180 errno = ENOMEM; 181 goto fail; 182 } 183 184 where = (grp + 1); 185 186 /* 187 * Go through each port that is part of the group. 188 */ 189 for (i = 0; i < grp->lg_nports; i++) { 190 port = (laioc_info_port_t *)where; 191 192 attrp->lg_ports[i].lp_linkid = port->lp_linkid; 193 bcopy(port->lp_mac, attrp->lg_ports[i].lp_mac, ETHERADDRL); 194 attrp->lg_ports[i].lp_state = port->lp_state; 195 attrp->lg_ports[i].lp_lacp_state = port->lp_lacp_state; 196 197 where = (port + 1); 198 } 199 *ptr = where; 200 return (0); 201 fail: 202 return (-1); 203 } 204 205 /* 206 * Get active configuration of a specific aggregation. 207 * Caller must free attrp->la_ports. 208 */ 209 static dladm_status_t 210 i_dladm_aggr_info_active(datalink_id_t linkid, dladm_aggr_grp_attr_t *attrp) 211 { 212 laioc_info_t *ioc; 213 int bufsize; 214 void *where; 215 dladm_status_t status = DLADM_STATUS_OK; 216 217 bufsize = MIN_INFO_SIZE; 218 ioc = (laioc_info_t *)calloc(1, bufsize); 219 if (ioc == NULL) 220 return (DLADM_STATUS_NOMEM); 221 222 ioc->li_group_linkid = linkid; 223 224 tryagain: 225 ioc->li_bufsize = bufsize; 226 if (i_dladm_aggr_ioctl(LAIOC_INFO, ioc) != 0) { 227 if (errno == ENOSPC) { 228 /* 229 * The LAIOC_INFO call failed due to a short 230 * buffer. Reallocate the buffer and try again. 231 */ 232 bufsize *= 2; 233 if (bufsize <= MAX_INFO_SIZE) { 234 ioc = (laioc_info_t *)realloc(ioc, bufsize); 235 if (ioc != NULL) { 236 bzero(ioc, sizeof (bufsize)); 237 goto tryagain; 238 } 239 } 240 } 241 status = dladm_errno2status(errno); 242 goto bail; 243 } 244 245 /* 246 * Go through each group returned by the aggregation driver. 247 */ 248 where = (char *)(ioc + 1); 249 if (i_dladm_aggr_iocp2grpattr(&where, attrp) != 0) { 250 status = dladm_errno2status(errno); 251 goto bail; 252 } 253 254 bail: 255 free(ioc); 256 return (status); 257 } 258 259 /* 260 * Get configuration information of a specific aggregation. 261 * Caller must free attrp->la_ports. 262 */ 263 static dladm_status_t 264 i_dladm_aggr_info_persist(datalink_id_t linkid, dladm_aggr_grp_attr_t *attrp) 265 { 266 dladm_conf_t conf; 267 uint32_t nports, i; 268 char *portstr, *next; 269 dladm_status_t status; 270 uint64_t u64; 271 int size; 272 char macstr[ETHERADDRL * 3]; 273 274 attrp->lg_linkid = linkid; 275 if ((status = dladm_read_conf(linkid, &conf)) != DLADM_STATUS_OK) 276 return (status); 277 278 status = dladm_get_conf_field(conf, FKEY, &u64, sizeof (u64)); 279 if (status != DLADM_STATUS_OK) 280 goto done; 281 attrp->lg_key = (uint16_t)u64; 282 283 status = dladm_get_conf_field(conf, FPOLICY, &u64, sizeof (u64)); 284 if (status != DLADM_STATUS_OK) 285 goto done; 286 attrp->lg_policy = (uint32_t)u64; 287 288 status = dladm_get_conf_field(conf, FFIXMACADDR, &attrp->lg_mac_fixed, 289 sizeof (boolean_t)); 290 if (status != DLADM_STATUS_OK) 291 goto done; 292 293 if (attrp->lg_mac_fixed) { 294 boolean_t fixed; 295 296 if ((status = dladm_get_conf_field(conf, FMACADDR, macstr, 297 sizeof (macstr))) != DLADM_STATUS_OK) { 298 goto done; 299 } 300 if (!dladm_aggr_str2macaddr(macstr, &fixed, attrp->lg_mac)) { 301 status = DLADM_STATUS_REPOSITORYINVAL; 302 goto done; 303 } 304 } 305 306 status = dladm_get_conf_field(conf, FFORCE, &attrp->lg_force, 307 sizeof (boolean_t)); 308 if (status != DLADM_STATUS_OK) 309 goto done; 310 311 status = dladm_get_conf_field(conf, FLACPMODE, &u64, sizeof (u64)); 312 if (status != DLADM_STATUS_OK) 313 goto done; 314 attrp->lg_lacp_mode = (aggr_lacp_mode_t)u64; 315 316 status = dladm_get_conf_field(conf, FLACPTIMER, &u64, sizeof (u64)); 317 if (status != DLADM_STATUS_OK) 318 goto done; 319 attrp->lg_lacp_timer = (aggr_lacp_timer_t)u64; 320 321 status = dladm_get_conf_field(conf, FNPORTS, &u64, sizeof (u64)); 322 if (status != DLADM_STATUS_OK) 323 goto done; 324 nports = (uint32_t)u64; 325 attrp->lg_nports = nports; 326 327 size = nports * (LINKID_STR_WIDTH + 1) + 1; 328 if ((portstr = calloc(1, size)) == NULL) { 329 status = DLADM_STATUS_NOMEM; 330 goto done; 331 } 332 333 status = dladm_get_conf_field(conf, FPORTS, portstr, size); 334 if (status != DLADM_STATUS_OK) { 335 free(portstr); 336 goto done; 337 } 338 339 if ((attrp->lg_ports = malloc(nports * 340 sizeof (dladm_aggr_port_attr_t))) == NULL) { 341 free(portstr); 342 status = DLADM_STATUS_NOMEM; 343 goto done; 344 } 345 346 for (next = portstr, i = 0; i < nports; i++) { 347 READ_PORT(next, attrp->lg_ports[i].lp_linkid, status); 348 if (status != DLADM_STATUS_OK) { 349 free(portstr); 350 free(attrp->lg_ports); 351 goto done; 352 } 353 } 354 free(portstr); 355 356 done: 357 dladm_destroy_conf(conf); 358 return (status); 359 } 360 361 dladm_status_t 362 dladm_aggr_info(datalink_id_t linkid, dladm_aggr_grp_attr_t *attrp, 363 uint32_t flags) 364 { 365 assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST); 366 if (flags == DLADM_OPT_ACTIVE) 367 return (i_dladm_aggr_info_active(linkid, attrp)); 368 else 369 return (i_dladm_aggr_info_persist(linkid, attrp)); 370 } 371 372 /* 373 * Add or remove one or more ports to/from an existing link aggregation. 374 */ 375 static dladm_status_t 376 i_dladm_aggr_add_rmv(datalink_id_t linkid, uint32_t nports, 377 dladm_aggr_port_attr_db_t *ports, uint32_t flags, int cmd) 378 { 379 char *orig_portstr = NULL, *portstr = NULL; 380 laioc_add_rem_t *iocp = NULL; 381 laioc_port_t *ioc_ports; 382 uint32_t orig_nports, result_nports, len, i, j; 383 dladm_conf_t conf; 384 datalink_class_t class; 385 dladm_status_t status = DLADM_STATUS_OK; 386 int size; 387 uint64_t u64; 388 uint32_t media; 389 390 if (nports == 0) 391 return (DLADM_STATUS_BADARG); 392 393 /* 394 * Sanity check - aggregations can only be created over Ethernet 395 * physical links. 396 */ 397 for (i = 0; i < nports; i++) { 398 if ((dladm_datalink_id2info(ports[i].lp_linkid, NULL, 399 &class, &media, NULL, 0) != DLADM_STATUS_OK) || 400 (class != DATALINK_CLASS_PHYS) || (media != DL_ETHER)) { 401 return (DLADM_STATUS_BADARG); 402 } 403 } 404 405 /* 406 * First, update the persistent configuration if requested. We only 407 * need to update the FPORTS and FNPORTS fields of this aggregation. 408 * Note that FPORTS is a list of port linkids separated by 409 * PORT_DELIMITER ('.'). 410 */ 411 if (flags & DLADM_OPT_PERSIST) { 412 status = dladm_read_conf(linkid, &conf); 413 if (status != DLADM_STATUS_OK) 414 return (status); 415 416 /* 417 * Get the original configuration of FNPORTS and FPORTS. 418 */ 419 status = dladm_get_conf_field(conf, FNPORTS, &u64, 420 sizeof (u64)); 421 if (status != DLADM_STATUS_OK) 422 goto destroyconf; 423 orig_nports = (uint32_t)u64; 424 425 /* 426 * At least one port needs to be in the aggregation. 427 */ 428 if ((cmd == LAIOC_REMOVE) && (orig_nports <= nports)) { 429 status = DLADM_STATUS_BADARG; 430 goto destroyconf; 431 } 432 433 size = orig_nports * (LINKID_STR_WIDTH + 1) + 1; 434 if ((orig_portstr = calloc(1, size)) == NULL) { 435 status = dladm_errno2status(errno); 436 goto destroyconf; 437 } 438 439 status = dladm_get_conf_field(conf, FPORTS, orig_portstr, size); 440 if (status != DLADM_STATUS_OK) 441 goto destroyconf; 442 443 result_nports = (cmd == LAIOC_ADD) ? orig_nports + nports : 444 orig_nports; 445 446 size = result_nports * (LINKID_STR_WIDTH + 1) + 1; 447 if ((portstr = calloc(1, size)) == NULL) { 448 status = dladm_errno2status(errno); 449 goto destroyconf; 450 } 451 452 /* 453 * get the new configuration and set to result_nports and 454 * portstr. 455 */ 456 if (cmd == LAIOC_ADD) { 457 (void) strlcpy(portstr, orig_portstr, size); 458 for (i = 0; i < nports; i++) 459 WRITE_PORT(portstr, ports[i].lp_linkid, size); 460 } else { 461 char *next; 462 datalink_id_t portid; 463 uint32_t remove = 0; 464 465 for (next = orig_portstr, j = 0; j < orig_nports; j++) { 466 /* 467 * Read the portids from the old configuration 468 * one by one. 469 */ 470 READ_PORT(next, portid, status); 471 if (status != DLADM_STATUS_OK) { 472 free(portstr); 473 goto destroyconf; 474 } 475 476 /* 477 * See whether this port is in the removal 478 * list. If not, copy to the new config. 479 */ 480 for (i = 0; i < nports; i++) { 481 if (ports[i].lp_linkid == portid) 482 break; 483 } 484 if (i == nports) { 485 WRITE_PORT(portstr, portid, size); 486 } else { 487 remove++; 488 } 489 } 490 if (remove != nports) { 491 status = DLADM_STATUS_LINKINVAL; 492 free(portstr); 493 goto destroyconf; 494 } 495 result_nports -= nports; 496 } 497 498 u64 = result_nports; 499 if ((status = dladm_set_conf_field(conf, FNPORTS, 500 DLADM_TYPE_UINT64, &u64)) != DLADM_STATUS_OK) { 501 free(portstr); 502 goto destroyconf; 503 } 504 505 status = dladm_set_conf_field(conf, FPORTS, DLADM_TYPE_STR, 506 portstr); 507 free(portstr); 508 if (status != DLADM_STATUS_OK) 509 goto destroyconf; 510 511 /* 512 * Write the new configuration to the persistent repository. 513 */ 514 status = dladm_write_conf(conf); 515 516 destroyconf: 517 dladm_destroy_conf(conf); 518 if (status != DLADM_STATUS_OK) { 519 free(orig_portstr); 520 return (status); 521 } 522 } 523 524 /* 525 * If the caller only requested to update the persistent 526 * configuration, we are done. 527 */ 528 if (!(flags & DLADM_OPT_ACTIVE)) 529 goto done; 530 531 /* 532 * Update the active configuration. 533 */ 534 len = sizeof (*iocp) + nports * sizeof (laioc_port_t); 535 if ((iocp = malloc(len)) == NULL) { 536 status = DLADM_STATUS_NOMEM; 537 goto done; 538 } 539 540 iocp->la_linkid = linkid; 541 iocp->la_nports = nports; 542 if (cmd == LAIOC_ADD) 543 iocp->la_force = (flags & DLADM_OPT_FORCE); 544 545 ioc_ports = (laioc_port_t *)(iocp + 1); 546 for (i = 0; i < nports; i++) 547 ioc_ports[i].lp_linkid = ports[i].lp_linkid; 548 549 if (i_dladm_aggr_ioctl(cmd, iocp) < 0) 550 status = dladm_errno2status(errno); 551 552 done: 553 free(iocp); 554 555 /* 556 * If the active configuration update fails, restore the old 557 * persistent configuration if we've changed that. 558 */ 559 if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) { 560 if (dladm_read_conf(linkid, &conf) == DLADM_STATUS_OK) { 561 u64 = orig_nports; 562 if ((dladm_set_conf_field(conf, FNPORTS, 563 DLADM_TYPE_UINT64, &u64) == DLADM_STATUS_OK) && 564 (dladm_set_conf_field(conf, FPORTS, DLADM_TYPE_STR, 565 orig_portstr) == DLADM_STATUS_OK)) { 566 (void) dladm_write_conf(conf); 567 } 568 (void) dladm_destroy_conf(conf); 569 } 570 } 571 free(orig_portstr); 572 return (status); 573 } 574 575 /* 576 * Send a modify command to the link aggregation driver. 577 */ 578 static dladm_status_t 579 i_dladm_aggr_modify_sys(datalink_id_t linkid, uint32_t mask, 580 dladm_aggr_modify_attr_t *attr) 581 { 582 laioc_modify_t ioc; 583 584 ioc.lu_linkid = linkid; 585 586 ioc.lu_modify_mask = 0; 587 if (mask & DLADM_AGGR_MODIFY_POLICY) 588 ioc.lu_modify_mask |= LAIOC_MODIFY_POLICY; 589 if (mask & DLADM_AGGR_MODIFY_MAC) 590 ioc.lu_modify_mask |= LAIOC_MODIFY_MAC; 591 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) 592 ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_MODE; 593 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) 594 ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_TIMER; 595 596 ioc.lu_policy = attr->ld_policy; 597 ioc.lu_mac_fixed = attr->ld_mac_fixed; 598 bcopy(attr->ld_mac, ioc.lu_mac, ETHERADDRL); 599 ioc.lu_lacp_mode = attr->ld_lacp_mode; 600 ioc.lu_lacp_timer = attr->ld_lacp_timer; 601 602 if (i_dladm_aggr_ioctl(LAIOC_MODIFY, &ioc) < 0) { 603 if (errno == EINVAL) 604 return (DLADM_STATUS_MACADDRINVAL); 605 else 606 return (dladm_errno2status(errno)); 607 } else { 608 return (DLADM_STATUS_OK); 609 } 610 } 611 612 /* 613 * Send a create command to the link aggregation driver. 614 */ 615 static dladm_status_t 616 i_dladm_aggr_create_sys(datalink_id_t linkid, uint16_t key, uint32_t nports, 617 dladm_aggr_port_attr_db_t *ports, uint32_t policy, 618 boolean_t mac_addr_fixed, const uchar_t *mac_addr, 619 aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, boolean_t force) 620 { 621 int i, len; 622 laioc_create_t *iocp = NULL; 623 laioc_port_t *ioc_ports; 624 dladm_status_t status = DLADM_STATUS_OK; 625 626 len = sizeof (*iocp) + nports * sizeof (laioc_port_t); 627 iocp = malloc(len); 628 if (iocp == NULL) 629 return (DLADM_STATUS_NOMEM); 630 631 iocp->lc_key = key; 632 iocp->lc_linkid = linkid; 633 iocp->lc_nports = nports; 634 iocp->lc_policy = policy; 635 iocp->lc_lacp_mode = lacp_mode; 636 iocp->lc_lacp_timer = lacp_timer; 637 ioc_ports = (laioc_port_t *)(iocp + 1); 638 iocp->lc_force = force; 639 640 for (i = 0; i < nports; i++) 641 ioc_ports[i].lp_linkid = ports[i].lp_linkid; 642 643 if (mac_addr_fixed && !VALID_PORT_MAC(mac_addr)) { 644 status = DLADM_STATUS_MACADDRINVAL; 645 goto done; 646 } 647 648 bcopy(mac_addr, iocp->lc_mac, ETHERADDRL); 649 iocp->lc_mac_fixed = mac_addr_fixed; 650 651 if (i_dladm_aggr_ioctl(LAIOC_CREATE, iocp) < 0) 652 status = dladm_errno2status(errno); 653 654 done: 655 free(iocp); 656 return (status); 657 } 658 659 /* 660 * Invoked to bring up a link aggregation group. 661 */ 662 static int 663 i_dladm_aggr_up(datalink_id_t linkid, void *arg) 664 { 665 dladm_status_t *statusp = (dladm_status_t *)arg; 666 dladm_aggr_grp_attr_t attr; 667 dladm_aggr_port_attr_db_t *ports = NULL; 668 uint16_t key = 0; 669 int i, j; 670 dladm_status_t status; 671 672 status = dladm_aggr_info(linkid, &attr, DLADM_OPT_PERSIST); 673 if (status != DLADM_STATUS_OK) { 674 *statusp = status; 675 return (DLADM_WALK_CONTINUE); 676 } 677 678 if (attr.lg_key <= AGGR_MAX_KEY) 679 key = attr.lg_key; 680 681 ports = malloc(attr.lg_nports * sizeof (dladm_aggr_port_attr_db_t)); 682 if (ports == NULL) { 683 status = DLADM_STATUS_NOMEM; 684 goto done; 685 } 686 687 /* 688 * Validate (and purge) each physical link associated with this 689 * aggregation, if the specific hardware has been removed during 690 * the system shutdown. 691 */ 692 for (i = 0, j = 0; i < attr.lg_nports; i++) { 693 datalink_id_t portid = attr.lg_ports[i].lp_linkid; 694 uint32_t flags; 695 dladm_status_t s; 696 697 s = dladm_datalink_id2info(portid, &flags, NULL, NULL, NULL, 0); 698 if (s != DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE)) 699 continue; 700 701 ports[j++].lp_linkid = portid; 702 } 703 704 if (j == 0) { 705 /* 706 * All of the physical links are removed. 707 */ 708 status = DLADM_STATUS_BADARG; 709 goto done; 710 } 711 712 /* 713 * Create active aggregation. 714 */ 715 if ((status = i_dladm_aggr_create_sys(linkid, 716 key, j, ports, attr.lg_policy, attr.lg_mac_fixed, 717 (const uchar_t *)attr.lg_mac, attr.lg_lacp_mode, 718 attr.lg_lacp_timer, attr.lg_force)) != DLADM_STATUS_OK) { 719 goto done; 720 } 721 722 if ((status = dladm_up_datalink_id(linkid)) != DLADM_STATUS_OK) { 723 laioc_delete_t ioc; 724 ioc.ld_linkid = linkid; 725 (void) i_dladm_aggr_ioctl(LAIOC_DELETE, &ioc); 726 goto done; 727 } 728 729 /* 730 * Reset the active linkprop of this specific link. 731 */ 732 (void) dladm_init_linkprop(linkid, B_FALSE); 733 734 done: 735 free(attr.lg_ports); 736 free(ports); 737 738 *statusp = status; 739 return (DLADM_WALK_CONTINUE); 740 } 741 742 /* 743 * Bring up one aggregation, or all persistent aggregations. In the latter 744 * case, the walk may terminate early if bringup of an aggregation fails. 745 */ 746 dladm_status_t 747 dladm_aggr_up(datalink_id_t linkid) 748 { 749 dladm_status_t status; 750 751 if (linkid == DATALINK_ALL_LINKID) { 752 (void) dladm_walk_datalink_id(i_dladm_aggr_up, &status, 753 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, 754 DLADM_OPT_PERSIST); 755 return (DLADM_STATUS_OK); 756 } else { 757 (void) i_dladm_aggr_up(linkid, &status); 758 return (status); 759 } 760 } 761 762 /* 763 * Given a policy string, return a policy mask. Returns B_TRUE on 764 * success, or B_FALSE if an error occurred during parsing. 765 */ 766 boolean_t 767 dladm_aggr_str2policy(const char *str, uint32_t *policy) 768 { 769 int i; 770 policy_t *pol; 771 char *token = NULL; 772 char *lasts; 773 774 *policy = 0; 775 776 while ((token = strtok_r((token == NULL) ? (char *)str : NULL, ",", 777 &lasts)) != NULL) { 778 for (i = 0; i < NPOLICIES; i++) { 779 pol = &policies[i]; 780 if (strcasecmp(token, pol->pol_name) == 0) { 781 *policy |= pol->policy; 782 break; 783 } 784 } 785 if (i == NPOLICIES) 786 return (B_FALSE); 787 } 788 789 return (B_TRUE); 790 } 791 792 /* 793 * Given a policy mask, returns a printable string, or NULL if the 794 * policy mask is invalid. It is the responsibility of the caller to 795 * free the returned string after use. 796 */ 797 char * 798 dladm_aggr_policy2str(uint32_t policy, char *str) 799 { 800 int i, npolicies = 0; 801 policy_t *pol; 802 803 if (str == NULL) 804 return (NULL); 805 806 str[0] = '\0'; 807 808 for (i = 0; i < NPOLICIES; i++) { 809 pol = &policies[i]; 810 if ((policy & pol->policy) != 0) { 811 npolicies++; 812 if (npolicies > 1) 813 (void) strlcat(str, ",", DLADM_STRSIZE); 814 (void) strlcat(str, pol->pol_name, DLADM_STRSIZE); 815 } 816 } 817 818 return (str); 819 } 820 821 /* 822 * Given a MAC address string, return the MAC address in the mac_addr 823 * array. If the MAC address was not explicitly specified, i.e. is 824 * equal to 'auto', zero out mac-addr and set mac_fixed to B_TRUE. 825 * Return B_FALSE if a syntax error was encountered, B_FALSE otherwise. 826 */ 827 boolean_t 828 dladm_aggr_str2macaddr(const char *str, boolean_t *mac_fixed, uchar_t *mac_addr) 829 { 830 uchar_t *conv_str; 831 int mac_len; 832 833 *mac_fixed = (strcmp(str, "auto") != 0); 834 if (!*mac_fixed) { 835 bzero(mac_addr, ETHERADDRL); 836 return (B_TRUE); 837 } 838 839 conv_str = _link_aton(str, &mac_len); 840 if (conv_str == NULL) 841 return (B_FALSE); 842 843 if (mac_len != ETHERADDRL) { 844 free(conv_str); 845 return (B_FALSE); 846 } 847 848 if ((bcmp(zero_mac, conv_str, ETHERADDRL) == 0) || 849 (conv_str[0] & 0x01)) { 850 free(conv_str); 851 return (B_FALSE); 852 } 853 854 bcopy(conv_str, mac_addr, ETHERADDRL); 855 free(conv_str); 856 857 return (B_TRUE); 858 } 859 860 /* 861 * Returns a string containing a printable representation of a MAC address. 862 */ 863 const char * 864 dladm_aggr_macaddr2str(const unsigned char *mac, char *buf) 865 { 866 static char unknown_mac[] = {0, 0, 0, 0, 0, 0}; 867 868 if (buf == NULL) 869 return (NULL); 870 871 if (bcmp(unknown_mac, mac, ETHERADDRL) == 0) 872 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 873 else 874 return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER)); 875 876 return (buf); 877 } 878 879 /* 880 * Given a LACP mode string, find the corresponding LACP mode number. Returns 881 * B_TRUE if a match was found, B_FALSE otherwise. 882 */ 883 boolean_t 884 dladm_aggr_str2lacpmode(const char *str, aggr_lacp_mode_t *lacp_mode) 885 { 886 int i; 887 dladm_aggr_lacpmode_t *mode; 888 889 for (i = 0; i < NLACP_MODES; i++) { 890 mode = &lacp_modes[i]; 891 if (strncasecmp(str, mode->mode_str, 892 strlen(mode->mode_str)) == 0) { 893 *lacp_mode = mode->mode_id; 894 return (B_TRUE); 895 } 896 } 897 898 return (B_FALSE); 899 } 900 901 /* 902 * Given a LACP mode number, returns a printable string, or NULL if the 903 * LACP mode number is invalid. 904 */ 905 const char * 906 dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id, char *buf) 907 { 908 int i; 909 dladm_aggr_lacpmode_t *mode; 910 911 if (buf == NULL) 912 return (NULL); 913 914 for (i = 0; i < NLACP_MODES; i++) { 915 mode = &lacp_modes[i]; 916 if (mode->mode_id == mode_id) { 917 (void) snprintf(buf, DLADM_STRSIZE, "%s", 918 mode->mode_str); 919 return (buf); 920 } 921 } 922 923 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 924 return (buf); 925 } 926 927 /* 928 * Given a LACP timer string, find the corresponding LACP timer number. Returns 929 * B_TRUE if a match was found, B_FALSE otherwise. 930 */ 931 boolean_t 932 dladm_aggr_str2lacptimer(const char *str, aggr_lacp_timer_t *lacp_timer) 933 { 934 int i; 935 dladm_aggr_lacptimer_t *timer; 936 937 for (i = 0; i < NLACP_TIMERS; i++) { 938 timer = &lacp_timers[i]; 939 if (strncasecmp(str, timer->lt_str, 940 strlen(timer->lt_str)) == 0) { 941 *lacp_timer = timer->lt_id; 942 return (B_TRUE); 943 } 944 } 945 946 return (B_FALSE); 947 } 948 949 /* 950 * Given a LACP timer, returns a printable string, or NULL if the 951 * LACP timer number is invalid. 952 */ 953 const char * 954 dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id, char *buf) 955 { 956 int i; 957 dladm_aggr_lacptimer_t *timer; 958 959 if (buf == NULL) 960 return (NULL); 961 962 for (i = 0; i < NLACP_TIMERS; i++) { 963 timer = &lacp_timers[i]; 964 if (timer->lt_id == timer_id) { 965 (void) snprintf(buf, DLADM_STRSIZE, "%s", 966 timer->lt_str); 967 return (buf); 968 } 969 } 970 971 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 972 return (buf); 973 } 974 975 const char * 976 dladm_aggr_portstate2str(aggr_port_state_t state_id, char *buf) 977 { 978 int i; 979 dladm_aggr_port_state_t *state; 980 981 if (buf == NULL) 982 return (NULL); 983 984 for (i = 0; i < NPORT_STATES; i++) { 985 state = &port_states[i]; 986 if (state->state_id == state_id) { 987 (void) snprintf(buf, DLADM_STRSIZE, "%s", 988 state->state_str); 989 return (buf); 990 } 991 } 992 993 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 994 return (buf); 995 } 996 997 static dladm_status_t 998 dladm_aggr_persist_aggr_conf(const char *link, datalink_id_t linkid, 999 uint16_t key, uint32_t nports, dladm_aggr_port_attr_db_t *ports, 1000 uint32_t policy, boolean_t mac_addr_fixed, const uchar_t *mac_addr, 1001 aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, 1002 boolean_t force) 1003 { 1004 dladm_conf_t conf = DLADM_INVALID_CONF; 1005 char *portstr = NULL; 1006 char macstr[ETHERADDRL * 3]; 1007 dladm_status_t status; 1008 int i, size; 1009 uint64_t u64; 1010 1011 if ((status = dladm_create_conf(link, linkid, DATALINK_CLASS_AGGR, 1012 DL_ETHER, &conf)) != DLADM_STATUS_OK) { 1013 return (status); 1014 } 1015 1016 u64 = key; 1017 status = dladm_set_conf_field(conf, FKEY, DLADM_TYPE_UINT64, &u64); 1018 if (status != DLADM_STATUS_OK) 1019 goto done; 1020 1021 u64 = nports; 1022 status = dladm_set_conf_field(conf, FNPORTS, DLADM_TYPE_UINT64, &u64); 1023 if (status != DLADM_STATUS_OK) 1024 goto done; 1025 1026 size = nports * (LINKID_STR_WIDTH + 1) + 1; 1027 if ((portstr = calloc(1, size)) == NULL) { 1028 status = DLADM_STATUS_NOMEM; 1029 goto done; 1030 } 1031 1032 for (i = 0; i < nports; i++) 1033 WRITE_PORT(portstr, ports[i].lp_linkid, size); 1034 status = dladm_set_conf_field(conf, FPORTS, DLADM_TYPE_STR, portstr); 1035 free(portstr); 1036 1037 if (status != DLADM_STATUS_OK) 1038 goto done; 1039 1040 u64 = policy; 1041 status = dladm_set_conf_field(conf, FPOLICY, DLADM_TYPE_UINT64, &u64); 1042 if (status != DLADM_STATUS_OK) 1043 goto done; 1044 1045 status = dladm_set_conf_field(conf, FFIXMACADDR, DLADM_TYPE_BOOLEAN, 1046 &mac_addr_fixed); 1047 if (status != DLADM_STATUS_OK) 1048 goto done; 1049 1050 if (mac_addr_fixed) { 1051 if (!VALID_PORT_MAC(mac_addr)) { 1052 status = DLADM_STATUS_MACADDRINVAL; 1053 goto done; 1054 } 1055 1056 (void) dladm_aggr_macaddr2str(mac_addr, macstr); 1057 status = dladm_set_conf_field(conf, FMACADDR, DLADM_TYPE_STR, 1058 macstr); 1059 if (status != DLADM_STATUS_OK) 1060 goto done; 1061 } 1062 1063 status = dladm_set_conf_field(conf, FFORCE, DLADM_TYPE_BOOLEAN, &force); 1064 if (status != DLADM_STATUS_OK) 1065 goto done; 1066 1067 u64 = lacp_mode; 1068 status = dladm_set_conf_field(conf, FLACPMODE, DLADM_TYPE_UINT64, &u64); 1069 if (status != DLADM_STATUS_OK) 1070 goto done; 1071 1072 u64 = lacp_timer; 1073 status = dladm_set_conf_field(conf, FLACPTIMER, DLADM_TYPE_UINT64, 1074 &u64); 1075 if (status != DLADM_STATUS_OK) 1076 goto done; 1077 1078 /* 1079 * Commit the link aggregation configuration. 1080 */ 1081 status = dladm_write_conf(conf); 1082 1083 done: 1084 dladm_destroy_conf(conf); 1085 return (status); 1086 } 1087 1088 /* 1089 * Create a new link aggregation group. Update the configuration 1090 * file and bring it up. 1091 */ 1092 dladm_status_t 1093 dladm_aggr_create(const char *name, uint16_t key, uint32_t nports, 1094 dladm_aggr_port_attr_db_t *ports, uint32_t policy, boolean_t mac_addr_fixed, 1095 const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 1096 aggr_lacp_timer_t lacp_timer, uint32_t flags) 1097 { 1098 datalink_id_t linkid = DATALINK_INVALID_LINKID; 1099 uint32_t media; 1100 uint32_t i; 1101 datalink_class_t class; 1102 dladm_status_t status; 1103 boolean_t force = (flags & DLADM_OPT_FORCE) ? B_TRUE : B_FALSE; 1104 1105 if (key != 0 && key > AGGR_MAX_KEY) 1106 return (DLADM_STATUS_KEYINVAL); 1107 1108 if (nports == 0) 1109 return (DLADM_STATUS_BADARG); 1110 1111 for (i = 0; i < nports; i++) { 1112 if ((dladm_datalink_id2info(ports[i].lp_linkid, NULL, 1113 &class, &media, NULL, 0) != DLADM_STATUS_OK) || 1114 !((class == DATALINK_CLASS_PHYS) && (media == DL_ETHER))) { 1115 return (DLADM_STATUS_BADARG); 1116 } 1117 } 1118 1119 flags &= (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); 1120 if ((status = dladm_create_datalink_id(name, DATALINK_CLASS_AGGR, 1121 DL_ETHER, flags, &linkid)) != DLADM_STATUS_OK) { 1122 goto fail; 1123 } 1124 1125 if ((flags & DLADM_OPT_PERSIST) && 1126 (status = dladm_aggr_persist_aggr_conf(name, linkid, key, nports, 1127 ports, policy, mac_addr_fixed, mac_addr, lacp_mode, lacp_timer, 1128 force)) != DLADM_STATUS_OK) { 1129 goto fail; 1130 } 1131 1132 if (!(flags & DLADM_OPT_ACTIVE)) 1133 return (DLADM_STATUS_OK); 1134 1135 status = i_dladm_aggr_create_sys(linkid, key, nports, ports, policy, 1136 mac_addr_fixed, mac_addr, lacp_mode, lacp_timer, force); 1137 1138 if (status != DLADM_STATUS_OK) { 1139 if (flags & DLADM_OPT_PERSIST) 1140 (void) dladm_remove_conf(linkid); 1141 goto fail; 1142 } 1143 1144 return (DLADM_STATUS_OK); 1145 1146 fail: 1147 if (linkid != DATALINK_INVALID_LINKID) 1148 (void) dladm_destroy_datalink_id(linkid, flags); 1149 1150 return (status); 1151 } 1152 1153 static dladm_status_t 1154 i_dladm_aggr_get_aggr_attr(dladm_conf_t conf, uint32_t mask, 1155 dladm_aggr_modify_attr_t *attrp) 1156 { 1157 dladm_status_t status = DLADM_STATUS_OK; 1158 char macstr[ETHERADDRL * 3]; 1159 uint64_t u64; 1160 1161 if (mask & DLADM_AGGR_MODIFY_POLICY) { 1162 status = dladm_get_conf_field(conf, FPOLICY, &u64, 1163 sizeof (u64)); 1164 if (status != DLADM_STATUS_OK) 1165 return (status); 1166 attrp->ld_policy = (uint32_t)u64; 1167 } 1168 1169 if (mask & DLADM_AGGR_MODIFY_MAC) { 1170 status = dladm_get_conf_field(conf, FFIXMACADDR, 1171 &attrp->ld_mac_fixed, sizeof (boolean_t)); 1172 if (status != DLADM_STATUS_OK) 1173 return (status); 1174 1175 if (attrp->ld_mac_fixed) { 1176 boolean_t fixed; 1177 1178 status = dladm_get_conf_field(conf, FMACADDR, 1179 macstr, sizeof (macstr)); 1180 if (status != DLADM_STATUS_OK) 1181 return (status); 1182 1183 if (!dladm_aggr_str2macaddr(macstr, &fixed, 1184 attrp->ld_mac)) { 1185 return (DLADM_STATUS_REPOSITORYINVAL); 1186 } 1187 } 1188 } 1189 1190 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) { 1191 status = dladm_get_conf_field(conf, FLACPMODE, &u64, 1192 sizeof (u64)); 1193 if (status != DLADM_STATUS_OK) 1194 return (status); 1195 attrp->ld_lacp_mode = (aggr_lacp_mode_t)u64; 1196 } 1197 1198 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) { 1199 status = dladm_get_conf_field(conf, FLACPTIMER, &u64, 1200 sizeof (u64)); 1201 if (status != DLADM_STATUS_OK) 1202 return (status); 1203 attrp->ld_lacp_timer = (aggr_lacp_timer_t)u64; 1204 } 1205 1206 return (status); 1207 } 1208 1209 static dladm_status_t 1210 i_dladm_aggr_set_aggr_attr(dladm_conf_t conf, uint32_t mask, 1211 dladm_aggr_modify_attr_t *attrp) 1212 { 1213 dladm_status_t status = DLADM_STATUS_OK; 1214 char macstr[ETHERADDRL * 3]; 1215 uint64_t u64; 1216 1217 if (mask & DLADM_AGGR_MODIFY_POLICY) { 1218 u64 = attrp->ld_policy; 1219 status = dladm_set_conf_field(conf, FPOLICY, DLADM_TYPE_UINT64, 1220 &u64); 1221 if (status != DLADM_STATUS_OK) 1222 return (status); 1223 } 1224 1225 if (mask & DLADM_AGGR_MODIFY_MAC) { 1226 status = dladm_set_conf_field(conf, FFIXMACADDR, 1227 DLADM_TYPE_BOOLEAN, &attrp->ld_mac_fixed); 1228 if (status != DLADM_STATUS_OK) 1229 return (status); 1230 1231 if (attrp->ld_mac_fixed) { 1232 (void) dladm_aggr_macaddr2str(attrp->ld_mac, macstr); 1233 status = dladm_set_conf_field(conf, FMACADDR, 1234 DLADM_TYPE_STR, macstr); 1235 if (status != DLADM_STATUS_OK) 1236 return (status); 1237 } 1238 } 1239 1240 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) { 1241 u64 = attrp->ld_lacp_mode; 1242 status = dladm_set_conf_field(conf, FLACPMODE, 1243 DLADM_TYPE_UINT64, &u64); 1244 if (status != DLADM_STATUS_OK) 1245 return (status); 1246 } 1247 1248 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) { 1249 u64 = attrp->ld_lacp_timer; 1250 status = dladm_set_conf_field(conf, FLACPTIMER, 1251 DLADM_TYPE_UINT64, &u64); 1252 if (status != DLADM_STATUS_OK) 1253 return (status); 1254 } 1255 1256 return (status); 1257 } 1258 1259 /* 1260 * Modify the parameters of an existing link aggregation group. Update 1261 * the configuration file and pass the changes to the kernel. 1262 */ 1263 dladm_status_t 1264 dladm_aggr_modify(datalink_id_t linkid, uint32_t modify_mask, uint32_t policy, 1265 boolean_t mac_fixed, const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 1266 aggr_lacp_timer_t lacp_timer, uint32_t flags) 1267 { 1268 dladm_aggr_modify_attr_t new_attr, old_attr; 1269 dladm_conf_t conf; 1270 dladm_status_t status; 1271 1272 new_attr.ld_policy = policy; 1273 new_attr.ld_mac_fixed = mac_fixed; 1274 new_attr.ld_lacp_mode = lacp_mode; 1275 new_attr.ld_lacp_timer = lacp_timer; 1276 bcopy(mac_addr, new_attr.ld_mac, ETHERADDRL); 1277 1278 if (flags & DLADM_OPT_PERSIST) { 1279 status = dladm_read_conf(linkid, &conf); 1280 if (status != DLADM_STATUS_OK) 1281 return (status); 1282 1283 if ((status = i_dladm_aggr_get_aggr_attr(conf, modify_mask, 1284 &old_attr)) != DLADM_STATUS_OK) { 1285 goto done; 1286 } 1287 1288 if ((status = i_dladm_aggr_set_aggr_attr(conf, modify_mask, 1289 &new_attr)) != DLADM_STATUS_OK) { 1290 goto done; 1291 } 1292 1293 status = dladm_write_conf(conf); 1294 1295 done: 1296 dladm_destroy_conf(conf); 1297 if (status != DLADM_STATUS_OK) 1298 return (status); 1299 } 1300 1301 if (!(flags & DLADM_OPT_ACTIVE)) 1302 return (DLADM_STATUS_OK); 1303 1304 status = i_dladm_aggr_modify_sys(linkid, modify_mask, &new_attr); 1305 if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) { 1306 if (dladm_read_conf(linkid, &conf) == DLADM_STATUS_OK) { 1307 if (i_dladm_aggr_set_aggr_attr(conf, modify_mask, 1308 &old_attr) == DLADM_STATUS_OK) { 1309 (void) dladm_write_conf(conf); 1310 } 1311 dladm_destroy_conf(conf); 1312 } 1313 } 1314 1315 return (status); 1316 } 1317 1318 typedef struct aggr_held_arg_s { 1319 datalink_id_t aggrid; 1320 boolean_t isheld; 1321 } aggr_held_arg_t; 1322 1323 static int 1324 i_dladm_aggr_is_held(datalink_id_t linkid, void *arg) 1325 { 1326 aggr_held_arg_t *aggr_held_arg = arg; 1327 dladm_vlan_attr_t dva; 1328 1329 if (dladm_vlan_info(linkid, &dva, DLADM_OPT_PERSIST) != DLADM_STATUS_OK) 1330 return (DLADM_WALK_CONTINUE); 1331 1332 if (dva.dv_linkid == aggr_held_arg->aggrid) { 1333 /* 1334 * This VLAN is created over the given aggregation. 1335 */ 1336 aggr_held_arg->isheld = B_TRUE; 1337 return (DLADM_WALK_TERMINATE); 1338 } 1339 return (DLADM_WALK_CONTINUE); 1340 } 1341 1342 /* 1343 * Delete a previously created link aggregation group. Either the name "aggr" 1344 * or the "key" is specified. 1345 */ 1346 dladm_status_t 1347 dladm_aggr_delete(datalink_id_t linkid, uint32_t flags) 1348 { 1349 laioc_delete_t ioc; 1350 datalink_class_t class; 1351 dladm_status_t status; 1352 1353 if ((dladm_datalink_id2info(linkid, NULL, &class, NULL, NULL, 0) != 1354 DLADM_STATUS_OK) || (class != DATALINK_CLASS_AGGR)) { 1355 return (DLADM_STATUS_BADARG); 1356 } 1357 1358 if (flags & DLADM_OPT_ACTIVE) { 1359 ioc.ld_linkid = linkid; 1360 if ((i_dladm_aggr_ioctl(LAIOC_DELETE, &ioc) < 0) && 1361 ((errno != ENOENT) || !(flags & DLADM_OPT_PERSIST))) { 1362 status = dladm_errno2status(errno); 1363 return (status); 1364 } 1365 1366 /* 1367 * Delete ACTIVE linkprop first. 1368 */ 1369 (void) dladm_set_linkprop(linkid, NULL, NULL, 0, 1370 DLADM_OPT_ACTIVE); 1371 (void) dladm_destroy_datalink_id(linkid, DLADM_OPT_ACTIVE); 1372 } 1373 1374 /* 1375 * If we reach here, it means that the active aggregation has already 1376 * been deleted, and there is no active VLANs holding this aggregation. 1377 * Now we see whether there is any persistent VLANs holding this 1378 * aggregation. If so, we fail the operation. 1379 */ 1380 if (flags & DLADM_OPT_PERSIST) { 1381 aggr_held_arg_t arg; 1382 1383 arg.aggrid = linkid; 1384 arg.isheld = B_FALSE; 1385 1386 (void) dladm_walk_datalink_id(i_dladm_aggr_is_held, 1387 &arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, 1388 DLADM_OPT_PERSIST); 1389 if (arg.isheld) 1390 return (DLADM_STATUS_LINKBUSY); 1391 1392 (void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST); 1393 (void) dladm_remove_conf(linkid); 1394 } 1395 1396 return (DLADM_STATUS_OK); 1397 } 1398 1399 /* 1400 * Add one or more ports to an existing link aggregation. 1401 */ 1402 dladm_status_t 1403 dladm_aggr_add(datalink_id_t linkid, uint32_t nports, 1404 dladm_aggr_port_attr_db_t *ports, uint32_t flags) 1405 { 1406 return (i_dladm_aggr_add_rmv(linkid, nports, ports, flags, LAIOC_ADD)); 1407 } 1408 1409 /* 1410 * Remove one or more ports from an existing link aggregation. 1411 */ 1412 dladm_status_t 1413 dladm_aggr_remove(datalink_id_t linkid, uint32_t nports, 1414 dladm_aggr_port_attr_db_t *ports, uint32_t flags) 1415 { 1416 return (i_dladm_aggr_add_rmv(linkid, nports, ports, flags, 1417 LAIOC_REMOVE)); 1418 } 1419 1420 typedef struct i_walk_key_state_s { 1421 uint16_t key; 1422 datalink_id_t linkid; 1423 boolean_t found; 1424 } i_walk_key_state_t; 1425 1426 static int 1427 i_dladm_walk_key2linkid(datalink_id_t linkid, void *arg) 1428 { 1429 dladm_conf_t conf; 1430 uint16_t key; 1431 dladm_status_t status; 1432 i_walk_key_state_t *statep = (i_walk_key_state_t *)arg; 1433 uint64_t u64; 1434 1435 if (dladm_read_conf(linkid, &conf) != 0) 1436 return (DLADM_WALK_CONTINUE); 1437 1438 status = dladm_get_conf_field(conf, FKEY, &u64, sizeof (u64)); 1439 key = (uint16_t)u64; 1440 dladm_destroy_conf(conf); 1441 1442 if ((status == DLADM_STATUS_OK) && (key == statep->key)) { 1443 statep->found = B_TRUE; 1444 statep->linkid = linkid; 1445 return (DLADM_WALK_TERMINATE); 1446 } 1447 1448 return (DLADM_WALK_CONTINUE); 1449 } 1450 1451 dladm_status_t 1452 dladm_key2linkid(uint16_t key, datalink_id_t *linkidp, uint32_t flags) 1453 { 1454 i_walk_key_state_t state; 1455 1456 if (key > AGGR_MAX_KEY) 1457 return (DLADM_STATUS_NOTFOUND); 1458 1459 state.found = B_FALSE; 1460 state.key = key; 1461 1462 (void) dladm_walk_datalink_id(i_dladm_walk_key2linkid, &state, 1463 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags); 1464 if (state.found == B_TRUE) { 1465 *linkidp = state.linkid; 1466 return (DLADM_STATUS_OK); 1467 } else { 1468 return (DLADM_STATUS_NOTFOUND); 1469 } 1470 } 1471