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