PSARC 2006/357 Crossbow - Network Virtualization and Resource Partitioning

   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   "@(#)libdlaggr.c        1.12    08/06/17 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 = 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.
 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, B_FALSE);
 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 }
--- EOF ---