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