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