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

1370                     ((errno != ENOENT) || !(flags & DLADM_OPT_PERSIST))) {
1371                         status = dladm_errno2status(errno);
1372                         return (status);
1373                 }
1374 
1375                 /*
1376                  * Delete ACTIVE linkprop first.
1377                  */
1378                 (void) dladm_set_linkprop(linkid, NULL, NULL, 0,
1379                     DLADM_OPT_ACTIVE);
1380                 (void) dladm_destroy_datalink_id(linkid, DLADM_OPT_ACTIVE);
1381         }
1382 
1383         /*
1384          * If we reach here, it means that the active aggregation has already
1385          * been deleted, and there is no active VLANs holding this aggregation.
1386          * Now we see whether there is any persistent VLANs holding this
1387          * aggregation. If so, we fail the operation.
1388          */
1389         if (flags & DLADM_OPT_PERSIST) {
1390                 aggr_held_arg_t arg;
1391 
1392                 arg.aggrid = linkid;
1393                 arg.isheld = B_FALSE;
1394 
1395                 (void) dladm_walk_datalink_id(i_dladm_aggr_is_held,
1396                     &arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
1397                     DLADM_OPT_PERSIST);
1398                 if (arg.isheld)
1399                         return (DLADM_STATUS_LINKBUSY);
1400 
1401                 (void) dladm_destroy_datalink_id(linkid, DLADM_OPT_PERSIST);
1402                 (void) dladm_remove_conf(linkid);
1403         }
1404 
1405         return (DLADM_STATUS_OK);
1406 }
1407 
1408 /*
1409  * Add one or more ports to an existing link aggregation.
1410  */
1411 dladm_status_t
1412 dladm_aggr_add(datalink_id_t linkid, uint32_t nports,
1413     dladm_aggr_port_attr_db_t *ports, uint32_t flags)
1414 {
1415         return (i_dladm_aggr_add_rmv(linkid, nports, ports, flags, LAIOC_ADD));
1416 }
1417 
1418 /*
1419  * Remove one or more ports from an existing link aggregation.
1420  */
1421 dladm_status_t
1422 dladm_aggr_remove(datalink_id_t linkid, uint32_t nports,
1423     dladm_aggr_port_attr_db_t *ports, uint32_t flags)
1424 {
1425         return (i_dladm_aggr_add_rmv(linkid, nports, ports, flags,
1426             LAIOC_REMOVE));
1427 }
1428 
1429 typedef struct i_walk_key_state_s {
1430         uint16_t key;
1431         datalink_id_t linkid;
1432         boolean_t found;
1433 } i_walk_key_state_t;
1434 
1435 static int
1436 i_dladm_walk_key2linkid(datalink_id_t linkid, void *arg)
1437 {
1438         dladm_conf_t conf;
1439         uint16_t key;
1440         dladm_status_t status;
1441         i_walk_key_state_t *statep = (i_walk_key_state_t *)arg;
1442         uint64_t u64;
1443 
1444         if (dladm_read_conf(linkid, &conf) != 0)
1445                 return (DLADM_WALK_CONTINUE);
1446 
1447         status = dladm_get_conf_field(conf, FKEY, &u64, sizeof (u64));
1448         key = (uint16_t)u64;
1449         dladm_destroy_conf(conf);
1450 
1451         if ((status == DLADM_STATUS_OK) && (key == statep->key)) {
1452                 statep->found = B_TRUE;
1453                 statep->linkid = linkid;
1454                 return (DLADM_WALK_TERMINATE);
1455         }
1456 
1457         return (DLADM_WALK_CONTINUE);
1458 }
1459 
1460 dladm_status_t
1461 dladm_key2linkid(uint16_t key, datalink_id_t *linkidp, uint32_t flags)
1462 {
1463         i_walk_key_state_t state;
1464 
1465         if (key > AGGR_MAX_KEY)
1466                 return (DLADM_STATUS_NOTFOUND);
1467 
1468         state.found = B_FALSE;
1469         state.key = key;
1470 
1471         (void) dladm_walk_datalink_id(i_dladm_walk_key2linkid, &state,
1472             DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags);
1473         if (state.found == B_TRUE) {
1474                 *linkidp = state.linkid;
1475                 return (DLADM_STATUS_OK);
1476         } else {
1477                 return (DLADM_STATUS_NOTFOUND);
1478         }
1479 }
--- EOF ---