1f595a68aSyz147064 /* 2f595a68aSyz147064 * CDDL HEADER START 3f595a68aSyz147064 * 4f595a68aSyz147064 * The contents of this file are subject to the terms of the 5f595a68aSyz147064 * Common Development and Distribution License (the "License"). 6f595a68aSyz147064 * You may not use this file except in compliance with the License. 7f595a68aSyz147064 * 8f595a68aSyz147064 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9f595a68aSyz147064 * or http://www.opensolaris.org/os/licensing. 10f595a68aSyz147064 * See the License for the specific language governing permissions 11f595a68aSyz147064 * and limitations under the License. 12f595a68aSyz147064 * 13f595a68aSyz147064 * When distributing Covered Code, include this CDDL HEADER in each 14f595a68aSyz147064 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15f595a68aSyz147064 * If applicable, add the following below this CDDL HEADER, with the 16f595a68aSyz147064 * fields enclosed by brackets "[]" replaced with your own identifying 17f595a68aSyz147064 * information: Portions Copyright [yyyy] [name of copyright owner] 18f595a68aSyz147064 * 19f595a68aSyz147064 * CDDL HEADER END 20f595a68aSyz147064 */ 21f595a68aSyz147064 /* 22*0dc2366fSVenugopal Iyer * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23f595a68aSyz147064 * Use is subject to license terms. 24f595a68aSyz147064 */ 25f595a68aSyz147064 26f595a68aSyz147064 #include <stdio.h> 27f595a68aSyz147064 #include <sys/types.h> 28f595a68aSyz147064 #include <sys/stat.h> 29f595a68aSyz147064 #include <string.h> 30f595a68aSyz147064 #include <fcntl.h> 31f595a68aSyz147064 #include <unistd.h> 32f595a68aSyz147064 #include <stropts.h> 33f595a68aSyz147064 #include <stdlib.h> 34f595a68aSyz147064 #include <errno.h> 35d62bc4baSyz147064 #include <assert.h> 36f595a68aSyz147064 #include <strings.h> 37f595a68aSyz147064 #include <libintl.h> 38f595a68aSyz147064 #include <net/if_types.h> 39f595a68aSyz147064 #include <net/if_dl.h> 40da14cebeSEric Cheng #include <sys/dld.h> 41d62bc4baSyz147064 #include <libdllink.h> 42d62bc4baSyz147064 #include <libdlvlan.h> 43f595a68aSyz147064 #include <libdlaggr.h> 44f595a68aSyz147064 #include <libdladm_impl.h> 45f595a68aSyz147064 46f595a68aSyz147064 /* 47f595a68aSyz147064 * Link Aggregation Administration Library. 48f595a68aSyz147064 * 49f595a68aSyz147064 * This library is used by administration tools such as dladm(1M) to 50f595a68aSyz147064 * configure link aggregations. 51f595a68aSyz147064 */ 52f595a68aSyz147064 53f595a68aSyz147064 /* Limits on buffer size for LAIOC_INFO request */ 54f595a68aSyz147064 #define MIN_INFO_SIZE (4*1024) 55f595a68aSyz147064 #define MAX_INFO_SIZE (128*1024) 56f595a68aSyz147064 57f595a68aSyz147064 static uchar_t zero_mac[] = {0, 0, 0, 0, 0, 0}; 58d62bc4baSyz147064 #define VALID_PORT_MAC(mac) \ 59d62bc4baSyz147064 (((mac) != NULL) && (bcmp(zero_mac, (mac), ETHERADDRL) != 0) && \ 60*0dc2366fSVenugopal Iyer (!((mac)[0] & 0x01))) 61f595a68aSyz147064 622b24ab6bSSebastien Roy #define PORT_DELIMITER ":" 63f595a68aSyz147064 64f595a68aSyz147064 typedef struct dladm_aggr_modify_attr { 65f595a68aSyz147064 uint32_t ld_policy; 66f595a68aSyz147064 boolean_t ld_mac_fixed; 67f595a68aSyz147064 uchar_t ld_mac[ETHERADDRL]; 68f595a68aSyz147064 aggr_lacp_mode_t ld_lacp_mode; 69f595a68aSyz147064 aggr_lacp_timer_t ld_lacp_timer; 70f595a68aSyz147064 } dladm_aggr_modify_attr_t; 71f595a68aSyz147064 72f595a68aSyz147064 typedef struct policy_s { 73f595a68aSyz147064 char *pol_name; 74f595a68aSyz147064 uint32_t policy; 75f595a68aSyz147064 } policy_t; 76f595a68aSyz147064 77f595a68aSyz147064 static policy_t policies[] = { 78f595a68aSyz147064 {"L2", AGGR_POLICY_L2}, 79f595a68aSyz147064 {"L3", AGGR_POLICY_L3}, 80f595a68aSyz147064 {"L4", AGGR_POLICY_L4}}; 81f595a68aSyz147064 82f595a68aSyz147064 #define NPOLICIES (sizeof (policies) / sizeof (policy_t)) 83f595a68aSyz147064 84f595a68aSyz147064 typedef struct dladm_aggr_lacpmode_s { 85f595a68aSyz147064 char *mode_str; 86f595a68aSyz147064 aggr_lacp_mode_t mode_id; 87f595a68aSyz147064 } dladm_aggr_lacpmode_t; 88f595a68aSyz147064 89f595a68aSyz147064 static dladm_aggr_lacpmode_t lacp_modes[] = { 90f595a68aSyz147064 {"off", AGGR_LACP_OFF}, 91f595a68aSyz147064 {"active", AGGR_LACP_ACTIVE}, 92f595a68aSyz147064 {"passive", AGGR_LACP_PASSIVE}}; 93f595a68aSyz147064 94f595a68aSyz147064 #define NLACP_MODES (sizeof (lacp_modes) / sizeof (dladm_aggr_lacpmode_t)) 95f595a68aSyz147064 96f595a68aSyz147064 typedef struct dladm_aggr_lacptimer_s { 97f595a68aSyz147064 char *lt_str; 98f595a68aSyz147064 aggr_lacp_timer_t lt_id; 99f595a68aSyz147064 } dladm_aggr_lacptimer_t; 100f595a68aSyz147064 101f595a68aSyz147064 static dladm_aggr_lacptimer_t lacp_timers[] = { 102f595a68aSyz147064 {"short", AGGR_LACP_TIMER_SHORT}, 103f595a68aSyz147064 {"long", AGGR_LACP_TIMER_LONG}}; 104f595a68aSyz147064 105f595a68aSyz147064 #define NLACP_TIMERS (sizeof (lacp_timers) / sizeof (dladm_aggr_lacptimer_t)) 106f595a68aSyz147064 107f595a68aSyz147064 typedef struct dladm_aggr_port_state { 108f595a68aSyz147064 char *state_str; 109f595a68aSyz147064 aggr_port_state_t state_id; 110f595a68aSyz147064 } dladm_aggr_port_state_t; 111f595a68aSyz147064 112f595a68aSyz147064 static dladm_aggr_port_state_t port_states[] = { 113f595a68aSyz147064 {"standby", AGGR_PORT_STATE_STANDBY }, 114f595a68aSyz147064 {"attached", AGGR_PORT_STATE_ATTACHED } 115f595a68aSyz147064 }; 116f595a68aSyz147064 117f595a68aSyz147064 #define NPORT_STATES \ 118f595a68aSyz147064 (sizeof (port_states) / sizeof (dladm_aggr_port_state_t)) 119f595a68aSyz147064 1202b24ab6bSSebastien Roy static dladm_status_t 1212b24ab6bSSebastien Roy write_port(dladm_handle_t handle, char *portstr, datalink_id_t portid, 1222b24ab6bSSebastien Roy size_t portstrsize) 1232b24ab6bSSebastien Roy { 1242b24ab6bSSebastien Roy char pname[MAXLINKNAMELEN + 1]; 1252b24ab6bSSebastien Roy dladm_status_t status; 1262b24ab6bSSebastien Roy 1272b24ab6bSSebastien Roy if ((status = dladm_datalink_id2info(handle, portid, NULL, NULL, NULL, 1282b24ab6bSSebastien Roy pname, sizeof (pname))) != DLADM_STATUS_OK) 1292b24ab6bSSebastien Roy return (status); 1302b24ab6bSSebastien Roy (void) strlcat(pname, PORT_DELIMITER, sizeof (pname)); 1312b24ab6bSSebastien Roy if (strlcat(portstr, pname, portstrsize) >= portstrsize) 1322b24ab6bSSebastien Roy status = DLADM_STATUS_TOOSMALL; 1332b24ab6bSSebastien Roy return (status); 1342b24ab6bSSebastien Roy } 1352b24ab6bSSebastien Roy 1362b24ab6bSSebastien Roy static dladm_status_t 1372b24ab6bSSebastien Roy read_port(dladm_handle_t handle, char **portstr, datalink_id_t *portid) 1382b24ab6bSSebastien Roy { 1392b24ab6bSSebastien Roy dladm_status_t status; 1402b24ab6bSSebastien Roy char *pname; 1412b24ab6bSSebastien Roy 1422b24ab6bSSebastien Roy if ((pname = strtok(*portstr, PORT_DELIMITER)) == NULL) 1432b24ab6bSSebastien Roy return (DLADM_STATUS_REPOSITORYINVAL); 1442b24ab6bSSebastien Roy *portstr += (strlen(pname) + 1); 1452b24ab6bSSebastien Roy status = dladm_name2info(handle, pname, portid, NULL, NULL, NULL); 1462b24ab6bSSebastien Roy return (status); 1472b24ab6bSSebastien Roy } 1482b24ab6bSSebastien Roy 149d62bc4baSyz147064 static int 1504ac67f02SAnurag S. Maskey i_dladm_aggr_ioctl(dladm_handle_t handle, int cmd, void *ptr) 151d62bc4baSyz147064 { 1524ac67f02SAnurag S. Maskey return (ioctl(dladm_dld_fd(handle), cmd, ptr)); 153d62bc4baSyz147064 } 154f595a68aSyz147064 155f595a68aSyz147064 /* 156d62bc4baSyz147064 * Caller must free attr.lg_ports. The ptr pointer is advanced while convert 157d62bc4baSyz147064 * the laioc_info_t to the dladm_aggr_grp_attr_t structure. 158f595a68aSyz147064 */ 159f595a68aSyz147064 static int 160d62bc4baSyz147064 i_dladm_aggr_iocp2grpattr(void **ptr, dladm_aggr_grp_attr_t *attrp) 161f595a68aSyz147064 { 162f595a68aSyz147064 laioc_info_group_t *grp; 163f595a68aSyz147064 laioc_info_port_t *port; 164d62bc4baSyz147064 int i; 165d62bc4baSyz147064 void *where = (*ptr); 166f595a68aSyz147064 167d62bc4baSyz147064 grp = (laioc_info_group_t *)where; 168d62bc4baSyz147064 169d62bc4baSyz147064 attrp->lg_linkid = grp->lg_linkid; 170d62bc4baSyz147064 attrp->lg_key = grp->lg_key; 171d62bc4baSyz147064 attrp->lg_nports = grp->lg_nports; 172d62bc4baSyz147064 attrp->lg_policy = grp->lg_policy; 173d62bc4baSyz147064 attrp->lg_lacp_mode = grp->lg_lacp_mode; 174d62bc4baSyz147064 attrp->lg_lacp_timer = grp->lg_lacp_timer; 175d62bc4baSyz147064 attrp->lg_force = grp->lg_force; 176d62bc4baSyz147064 177d62bc4baSyz147064 bcopy(grp->lg_mac, attrp->lg_mac, ETHERADDRL); 178d62bc4baSyz147064 attrp->lg_mac_fixed = grp->lg_mac_fixed; 179d62bc4baSyz147064 180d62bc4baSyz147064 if ((attrp->lg_ports = malloc(grp->lg_nports * 181d62bc4baSyz147064 sizeof (dladm_aggr_port_attr_t))) == NULL) { 182d62bc4baSyz147064 errno = ENOMEM; 183d62bc4baSyz147064 goto fail; 184d62bc4baSyz147064 } 185d62bc4baSyz147064 186d62bc4baSyz147064 where = (grp + 1); 187d62bc4baSyz147064 188d62bc4baSyz147064 /* 189d62bc4baSyz147064 * Go through each port that is part of the group. 190d62bc4baSyz147064 */ 191d62bc4baSyz147064 for (i = 0; i < grp->lg_nports; i++) { 192d62bc4baSyz147064 port = (laioc_info_port_t *)where; 193d62bc4baSyz147064 194d62bc4baSyz147064 attrp->lg_ports[i].lp_linkid = port->lp_linkid; 195d62bc4baSyz147064 bcopy(port->lp_mac, attrp->lg_ports[i].lp_mac, ETHERADDRL); 196d62bc4baSyz147064 attrp->lg_ports[i].lp_state = port->lp_state; 197d62bc4baSyz147064 attrp->lg_ports[i].lp_lacp_state = port->lp_lacp_state; 198d62bc4baSyz147064 199d62bc4baSyz147064 where = (port + 1); 200d62bc4baSyz147064 } 201d62bc4baSyz147064 *ptr = where; 202d62bc4baSyz147064 return (0); 203d62bc4baSyz147064 fail: 204f595a68aSyz147064 return (-1); 205d62bc4baSyz147064 } 206d62bc4baSyz147064 207d62bc4baSyz147064 /* 208d62bc4baSyz147064 * Get active configuration of a specific aggregation. 209d62bc4baSyz147064 * Caller must free attrp->la_ports. 210d62bc4baSyz147064 */ 211d62bc4baSyz147064 static dladm_status_t 2124ac67f02SAnurag S. Maskey i_dladm_aggr_info_active(dladm_handle_t handle, datalink_id_t linkid, 2134ac67f02SAnurag S. Maskey dladm_aggr_grp_attr_t *attrp) 214d62bc4baSyz147064 { 215d62bc4baSyz147064 laioc_info_t *ioc; 216eae72b5bSSebastien Roy int bufsize; 217d62bc4baSyz147064 void *where; 218d62bc4baSyz147064 dladm_status_t status = DLADM_STATUS_OK; 219f595a68aSyz147064 220f595a68aSyz147064 bufsize = MIN_INFO_SIZE; 221f595a68aSyz147064 ioc = (laioc_info_t *)calloc(1, bufsize); 222d62bc4baSyz147064 if (ioc == NULL) 223d62bc4baSyz147064 return (DLADM_STATUS_NOMEM); 224d62bc4baSyz147064 225d62bc4baSyz147064 ioc->li_group_linkid = linkid; 226f595a68aSyz147064 227f595a68aSyz147064 tryagain: 228eae72b5bSSebastien Roy ioc->li_bufsize = bufsize; 2294ac67f02SAnurag S. Maskey if (i_dladm_aggr_ioctl(handle, LAIOC_INFO, ioc) != 0) { 230f595a68aSyz147064 if (errno == ENOSPC) { 231f595a68aSyz147064 /* 232f595a68aSyz147064 * The LAIOC_INFO call failed due to a short 233f595a68aSyz147064 * buffer. Reallocate the buffer and try again. 234f595a68aSyz147064 */ 235f595a68aSyz147064 bufsize *= 2; 236f595a68aSyz147064 if (bufsize <= MAX_INFO_SIZE) { 237f595a68aSyz147064 ioc = (laioc_info_t *)realloc(ioc, bufsize); 238f595a68aSyz147064 if (ioc != NULL) { 239f595a68aSyz147064 bzero(ioc, sizeof (bufsize)); 240f595a68aSyz147064 goto tryagain; 241f595a68aSyz147064 } 242f595a68aSyz147064 } 243f595a68aSyz147064 } 244d62bc4baSyz147064 status = dladm_errno2status(errno); 245f595a68aSyz147064 goto bail; 246f595a68aSyz147064 } 247f595a68aSyz147064 248f595a68aSyz147064 /* 249f595a68aSyz147064 * Go through each group returned by the aggregation driver. 250f595a68aSyz147064 */ 251f595a68aSyz147064 where = (char *)(ioc + 1); 252d62bc4baSyz147064 if (i_dladm_aggr_iocp2grpattr(&where, attrp) != 0) { 253d62bc4baSyz147064 status = dladm_errno2status(errno); 254f595a68aSyz147064 goto bail; 255f595a68aSyz147064 } 256f595a68aSyz147064 257f595a68aSyz147064 bail: 258f595a68aSyz147064 free(ioc); 259f595a68aSyz147064 return (status); 260f595a68aSyz147064 } 261f595a68aSyz147064 262d62bc4baSyz147064 /* 263d62bc4baSyz147064 * Get configuration information of a specific aggregation. 264d62bc4baSyz147064 * Caller must free attrp->la_ports. 265d62bc4baSyz147064 */ 266d62bc4baSyz147064 static dladm_status_t 2674ac67f02SAnurag S. Maskey i_dladm_aggr_info_persist(dladm_handle_t handle, datalink_id_t linkid, 2684ac67f02SAnurag S. Maskey dladm_aggr_grp_attr_t *attrp) 269d62bc4baSyz147064 { 270d62bc4baSyz147064 dladm_conf_t conf; 271d62bc4baSyz147064 uint32_t nports, i; 2722b24ab6bSSebastien Roy char *portstr = NULL, *next; 273d62bc4baSyz147064 dladm_status_t status; 274d62bc4baSyz147064 uint64_t u64; 275d62bc4baSyz147064 int size; 276d62bc4baSyz147064 char macstr[ETHERADDRL * 3]; 277f595a68aSyz147064 278d62bc4baSyz147064 attrp->lg_linkid = linkid; 2794ac67f02SAnurag S. Maskey if ((status = dladm_read_conf(handle, linkid, &conf)) != 2804ac67f02SAnurag S. Maskey DLADM_STATUS_OK) 281d62bc4baSyz147064 return (status); 282f595a68aSyz147064 2834ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FKEY, &u64, sizeof (u64)); 284d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 285d62bc4baSyz147064 goto done; 286d62bc4baSyz147064 attrp->lg_key = (uint16_t)u64; 287d62bc4baSyz147064 2884ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FPOLICY, &u64, 2894ac67f02SAnurag S. Maskey sizeof (u64)); 290d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 291d62bc4baSyz147064 goto done; 292d62bc4baSyz147064 attrp->lg_policy = (uint32_t)u64; 293d62bc4baSyz147064 2944ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FFIXMACADDR, 2954ac67f02SAnurag S. Maskey &attrp->lg_mac_fixed, sizeof (boolean_t)); 296d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 297d62bc4baSyz147064 goto done; 298d62bc4baSyz147064 299d62bc4baSyz147064 if (attrp->lg_mac_fixed) { 300d62bc4baSyz147064 boolean_t fixed; 301d62bc4baSyz147064 3024ac67f02SAnurag S. Maskey if ((status = dladm_get_conf_field(handle, conf, FMACADDR, 3034ac67f02SAnurag S. Maskey macstr, sizeof (macstr))) != DLADM_STATUS_OK) { 304d62bc4baSyz147064 goto done; 305d62bc4baSyz147064 } 306d62bc4baSyz147064 if (!dladm_aggr_str2macaddr(macstr, &fixed, attrp->lg_mac)) { 307f595a68aSyz147064 status = DLADM_STATUS_REPOSITORYINVAL; 308f595a68aSyz147064 goto done; 309f595a68aSyz147064 } 310d62bc4baSyz147064 } 311f595a68aSyz147064 3124ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FFORCE, &attrp->lg_force, 313d62bc4baSyz147064 sizeof (boolean_t)); 314d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 315f595a68aSyz147064 goto done; 316f595a68aSyz147064 3174ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FLACPMODE, &u64, 3184ac67f02SAnurag S. Maskey sizeof (u64)); 319d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 320d62bc4baSyz147064 goto done; 321d62bc4baSyz147064 attrp->lg_lacp_mode = (aggr_lacp_mode_t)u64; 322f595a68aSyz147064 3234ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FLACPTIMER, &u64, 3244ac67f02SAnurag S. Maskey sizeof (u64)); 325d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 326d62bc4baSyz147064 goto done; 327d62bc4baSyz147064 attrp->lg_lacp_timer = (aggr_lacp_timer_t)u64; 328f595a68aSyz147064 3294ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FNPORTS, &u64, 3304ac67f02SAnurag S. Maskey sizeof (u64)); 331d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 332d62bc4baSyz147064 goto done; 333d62bc4baSyz147064 nports = (uint32_t)u64; 334d62bc4baSyz147064 attrp->lg_nports = nports; 335f595a68aSyz147064 3362b24ab6bSSebastien Roy size = nports * (MAXLINKNAMELEN + 1) + 1; 337d62bc4baSyz147064 if ((portstr = calloc(1, size)) == NULL) { 338f595a68aSyz147064 status = DLADM_STATUS_NOMEM; 339f595a68aSyz147064 goto done; 340f595a68aSyz147064 } 341f595a68aSyz147064 3424ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FPORTS, portstr, size); 3432b24ab6bSSebastien Roy if (status != DLADM_STATUS_OK) 344f595a68aSyz147064 goto done; 345f595a68aSyz147064 346d62bc4baSyz147064 if ((attrp->lg_ports = malloc(nports * 347d62bc4baSyz147064 sizeof (dladm_aggr_port_attr_t))) == NULL) { 348d62bc4baSyz147064 status = DLADM_STATUS_NOMEM; 349d62bc4baSyz147064 goto done; 350d62bc4baSyz147064 } 351d62bc4baSyz147064 352d62bc4baSyz147064 for (next = portstr, i = 0; i < nports; i++) { 3532b24ab6bSSebastien Roy if ((status = read_port(handle, &next, 3542b24ab6bSSebastien Roy &attrp->lg_ports[i].lp_linkid)) != DLADM_STATUS_OK) 355d62bc4baSyz147064 free(attrp->lg_ports); 356d62bc4baSyz147064 } 357d62bc4baSyz147064 358d62bc4baSyz147064 done: 3592b24ab6bSSebastien Roy free(portstr); 3604ac67f02SAnurag S. Maskey dladm_destroy_conf(handle, conf); 361d62bc4baSyz147064 return (status); 362d62bc4baSyz147064 } 363d62bc4baSyz147064 364d62bc4baSyz147064 dladm_status_t 3654ac67f02SAnurag S. Maskey dladm_aggr_info(dladm_handle_t handle, datalink_id_t linkid, 3664ac67f02SAnurag S. Maskey dladm_aggr_grp_attr_t *attrp, uint32_t flags) 367d62bc4baSyz147064 { 368d62bc4baSyz147064 assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST); 369d62bc4baSyz147064 if (flags == DLADM_OPT_ACTIVE) 3704ac67f02SAnurag S. Maskey return (i_dladm_aggr_info_active(handle, linkid, attrp)); 371f595a68aSyz147064 else 3724ac67f02SAnurag S. Maskey return (i_dladm_aggr_info_persist(handle, linkid, attrp)); 373f595a68aSyz147064 } 374f595a68aSyz147064 375d62bc4baSyz147064 /* 376d62bc4baSyz147064 * Add or remove one or more ports to/from an existing link aggregation. 377d62bc4baSyz147064 */ 378d62bc4baSyz147064 static dladm_status_t 3794ac67f02SAnurag S. Maskey i_dladm_aggr_add_rmv(dladm_handle_t handle, datalink_id_t linkid, 3804ac67f02SAnurag S. Maskey uint32_t nports, dladm_aggr_port_attr_db_t *ports, uint32_t flags, int cmd) 381d62bc4baSyz147064 { 382d62bc4baSyz147064 char *orig_portstr = NULL, *portstr = NULL; 3838de9d095Syz147064 laioc_add_rem_t *iocp = NULL; 384d62bc4baSyz147064 laioc_port_t *ioc_ports; 385d62bc4baSyz147064 uint32_t orig_nports, result_nports, len, i, j; 386d62bc4baSyz147064 dladm_conf_t conf; 387d62bc4baSyz147064 datalink_class_t class; 388d62bc4baSyz147064 dladm_status_t status = DLADM_STATUS_OK; 389d62bc4baSyz147064 int size; 390d62bc4baSyz147064 uint64_t u64; 391d62bc4baSyz147064 uint32_t media; 392d62bc4baSyz147064 393d62bc4baSyz147064 if (nports == 0) 394d62bc4baSyz147064 return (DLADM_STATUS_BADARG); 395d62bc4baSyz147064 396d62bc4baSyz147064 /* 397d62bc4baSyz147064 * Sanity check - aggregations can only be created over Ethernet 398b509e89bSRishi Srivatsavai * physical links and simnets. 399d62bc4baSyz147064 */ 400d62bc4baSyz147064 for (i = 0; i < nports; i++) { 4014ac67f02SAnurag S. Maskey if ((dladm_datalink_id2info(handle, ports[i].lp_linkid, NULL, 402d62bc4baSyz147064 &class, &media, NULL, 0) != DLADM_STATUS_OK) || 403b509e89bSRishi Srivatsavai !((class == DATALINK_CLASS_PHYS) || 404b509e89bSRishi Srivatsavai (class == DATALINK_CLASS_SIMNET)) || (media != DL_ETHER)) { 405d62bc4baSyz147064 return (DLADM_STATUS_BADARG); 406d62bc4baSyz147064 } 407d62bc4baSyz147064 } 408d62bc4baSyz147064 409d62bc4baSyz147064 /* 410d62bc4baSyz147064 * First, update the persistent configuration if requested. We only 411d62bc4baSyz147064 * need to update the FPORTS and FNPORTS fields of this aggregation. 412d62bc4baSyz147064 * Note that FPORTS is a list of port linkids separated by 4132b24ab6bSSebastien Roy * PORT_DELIMITER (':'). 414d62bc4baSyz147064 */ 415d62bc4baSyz147064 if (flags & DLADM_OPT_PERSIST) { 4164ac67f02SAnurag S. Maskey status = dladm_read_conf(handle, linkid, &conf); 417d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 418d62bc4baSyz147064 return (status); 419d62bc4baSyz147064 420d62bc4baSyz147064 /* 421d62bc4baSyz147064 * Get the original configuration of FNPORTS and FPORTS. 422d62bc4baSyz147064 */ 4234ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FNPORTS, &u64, 424d62bc4baSyz147064 sizeof (u64)); 425d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 426d62bc4baSyz147064 goto destroyconf; 427d62bc4baSyz147064 orig_nports = (uint32_t)u64; 428d62bc4baSyz147064 429d62bc4baSyz147064 /* 430d62bc4baSyz147064 * At least one port needs to be in the aggregation. 431d62bc4baSyz147064 */ 432d62bc4baSyz147064 if ((cmd == LAIOC_REMOVE) && (orig_nports <= nports)) { 433d62bc4baSyz147064 status = DLADM_STATUS_BADARG; 434d62bc4baSyz147064 goto destroyconf; 435d62bc4baSyz147064 } 436d62bc4baSyz147064 4372b24ab6bSSebastien Roy size = orig_nports * (MAXLINKNAMELEN + 1) + 1; 438d62bc4baSyz147064 if ((orig_portstr = calloc(1, size)) == NULL) { 439d62bc4baSyz147064 status = dladm_errno2status(errno); 440d62bc4baSyz147064 goto destroyconf; 441d62bc4baSyz147064 } 442d62bc4baSyz147064 4434ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FPORTS, 4444ac67f02SAnurag S. Maskey orig_portstr, size); 445d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 446d62bc4baSyz147064 goto destroyconf; 447d62bc4baSyz147064 448d62bc4baSyz147064 result_nports = (cmd == LAIOC_ADD) ? orig_nports + nports : 449d62bc4baSyz147064 orig_nports; 450d62bc4baSyz147064 4512b24ab6bSSebastien Roy size = result_nports * (MAXLINKNAMELEN + 1) + 1; 452d62bc4baSyz147064 if ((portstr = calloc(1, size)) == NULL) { 453d62bc4baSyz147064 status = dladm_errno2status(errno); 454d62bc4baSyz147064 goto destroyconf; 455d62bc4baSyz147064 } 456d62bc4baSyz147064 457d62bc4baSyz147064 /* 458d62bc4baSyz147064 * get the new configuration and set to result_nports and 459d62bc4baSyz147064 * portstr. 460d62bc4baSyz147064 */ 461d62bc4baSyz147064 if (cmd == LAIOC_ADD) { 462d62bc4baSyz147064 (void) strlcpy(portstr, orig_portstr, size); 4632b24ab6bSSebastien Roy for (i = 0; i < nports; i++) { 4642b24ab6bSSebastien Roy status = write_port(handle, portstr, 4652b24ab6bSSebastien Roy ports[i].lp_linkid, size); 4662b24ab6bSSebastien Roy if (status != DLADM_STATUS_OK) { 4672b24ab6bSSebastien Roy free(portstr); 4682b24ab6bSSebastien Roy goto destroyconf; 4692b24ab6bSSebastien Roy } 4702b24ab6bSSebastien Roy } 471d62bc4baSyz147064 } else { 472d62bc4baSyz147064 char *next; 473d62bc4baSyz147064 datalink_id_t portid; 474d62bc4baSyz147064 uint32_t remove = 0; 475d62bc4baSyz147064 476d62bc4baSyz147064 for (next = orig_portstr, j = 0; j < orig_nports; j++) { 477d62bc4baSyz147064 /* 478d62bc4baSyz147064 * Read the portids from the old configuration 479d62bc4baSyz147064 * one by one. 480d62bc4baSyz147064 */ 4812b24ab6bSSebastien Roy status = read_port(handle, &next, &portid); 482d62bc4baSyz147064 if (status != DLADM_STATUS_OK) { 483d62bc4baSyz147064 free(portstr); 484d62bc4baSyz147064 goto destroyconf; 485d62bc4baSyz147064 } 486d62bc4baSyz147064 487d62bc4baSyz147064 /* 488d62bc4baSyz147064 * See whether this port is in the removal 489d62bc4baSyz147064 * list. If not, copy to the new config. 490d62bc4baSyz147064 */ 491d62bc4baSyz147064 for (i = 0; i < nports; i++) { 492d62bc4baSyz147064 if (ports[i].lp_linkid == portid) 493d62bc4baSyz147064 break; 494d62bc4baSyz147064 } 495d62bc4baSyz147064 if (i == nports) { 4962b24ab6bSSebastien Roy status = write_port(handle, portstr, 4972b24ab6bSSebastien Roy portid, size); 4982b24ab6bSSebastien Roy if (status != DLADM_STATUS_OK) { 4992b24ab6bSSebastien Roy free(portstr); 5002b24ab6bSSebastien Roy goto destroyconf; 5012b24ab6bSSebastien Roy } 502d62bc4baSyz147064 } else { 503d62bc4baSyz147064 remove++; 504d62bc4baSyz147064 } 505d62bc4baSyz147064 } 506d62bc4baSyz147064 if (remove != nports) { 507d62bc4baSyz147064 status = DLADM_STATUS_LINKINVAL; 508d62bc4baSyz147064 free(portstr); 509d62bc4baSyz147064 goto destroyconf; 510d62bc4baSyz147064 } 511d62bc4baSyz147064 result_nports -= nports; 512d62bc4baSyz147064 } 513d62bc4baSyz147064 514d62bc4baSyz147064 u64 = result_nports; 5154ac67f02SAnurag S. Maskey if ((status = dladm_set_conf_field(handle, conf, FNPORTS, 516d62bc4baSyz147064 DLADM_TYPE_UINT64, &u64)) != DLADM_STATUS_OK) { 517d62bc4baSyz147064 free(portstr); 518d62bc4baSyz147064 goto destroyconf; 519d62bc4baSyz147064 } 520d62bc4baSyz147064 5214ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FPORTS, 5224ac67f02SAnurag S. Maskey DLADM_TYPE_STR, portstr); 523d62bc4baSyz147064 free(portstr); 524d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 525d62bc4baSyz147064 goto destroyconf; 526d62bc4baSyz147064 527d62bc4baSyz147064 /* 528d62bc4baSyz147064 * Write the new configuration to the persistent repository. 529d62bc4baSyz147064 */ 5304ac67f02SAnurag S. Maskey status = dladm_write_conf(handle, conf); 531d62bc4baSyz147064 532d62bc4baSyz147064 destroyconf: 5334ac67f02SAnurag S. Maskey dladm_destroy_conf(handle, conf); 534d62bc4baSyz147064 if (status != DLADM_STATUS_OK) { 535d62bc4baSyz147064 free(orig_portstr); 536d62bc4baSyz147064 return (status); 537d62bc4baSyz147064 } 538d62bc4baSyz147064 } 539d62bc4baSyz147064 540d62bc4baSyz147064 /* 541d62bc4baSyz147064 * If the caller only requested to update the persistent 542d62bc4baSyz147064 * configuration, we are done. 543d62bc4baSyz147064 */ 544d62bc4baSyz147064 if (!(flags & DLADM_OPT_ACTIVE)) 545d62bc4baSyz147064 goto done; 546d62bc4baSyz147064 547d62bc4baSyz147064 /* 548d62bc4baSyz147064 * Update the active configuration. 549d62bc4baSyz147064 */ 550d62bc4baSyz147064 len = sizeof (*iocp) + nports * sizeof (laioc_port_t); 551d62bc4baSyz147064 if ((iocp = malloc(len)) == NULL) { 552d62bc4baSyz147064 status = DLADM_STATUS_NOMEM; 553d62bc4baSyz147064 goto done; 554d62bc4baSyz147064 } 555d62bc4baSyz147064 556d62bc4baSyz147064 iocp->la_linkid = linkid; 557d62bc4baSyz147064 iocp->la_nports = nports; 558d62bc4baSyz147064 if (cmd == LAIOC_ADD) 559d62bc4baSyz147064 iocp->la_force = (flags & DLADM_OPT_FORCE); 560d62bc4baSyz147064 561d62bc4baSyz147064 ioc_ports = (laioc_port_t *)(iocp + 1); 562d62bc4baSyz147064 for (i = 0; i < nports; i++) 563d62bc4baSyz147064 ioc_ports[i].lp_linkid = ports[i].lp_linkid; 564d62bc4baSyz147064 5654ac67f02SAnurag S. Maskey if (i_dladm_aggr_ioctl(handle, cmd, iocp) < 0) 566d62bc4baSyz147064 status = dladm_errno2status(errno); 567f595a68aSyz147064 568f595a68aSyz147064 done: 569f595a68aSyz147064 free(iocp); 570d62bc4baSyz147064 571d62bc4baSyz147064 /* 572d62bc4baSyz147064 * If the active configuration update fails, restore the old 573d62bc4baSyz147064 * persistent configuration if we've changed that. 574d62bc4baSyz147064 */ 575d62bc4baSyz147064 if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) { 5764ac67f02SAnurag S. Maskey if (dladm_read_conf(handle, linkid, &conf) == DLADM_STATUS_OK) { 577d62bc4baSyz147064 u64 = orig_nports; 5784ac67f02SAnurag S. Maskey if ((dladm_set_conf_field(handle, conf, FNPORTS, 579d62bc4baSyz147064 DLADM_TYPE_UINT64, &u64) == DLADM_STATUS_OK) && 5804ac67f02SAnurag S. Maskey (dladm_set_conf_field(handle, conf, FPORTS, 5814ac67f02SAnurag S. Maskey DLADM_TYPE_STR, orig_portstr) == DLADM_STATUS_OK)) { 5824ac67f02SAnurag S. Maskey (void) dladm_write_conf(handle, conf); 583d62bc4baSyz147064 } 5844ac67f02SAnurag S. Maskey (void) dladm_destroy_conf(handle, conf); 585d62bc4baSyz147064 } 586d62bc4baSyz147064 } 587d62bc4baSyz147064 free(orig_portstr); 588f595a68aSyz147064 return (status); 589f595a68aSyz147064 } 590f595a68aSyz147064 591f595a68aSyz147064 /* 592f595a68aSyz147064 * Send a modify command to the link aggregation driver. 593f595a68aSyz147064 */ 594f595a68aSyz147064 static dladm_status_t 5954ac67f02SAnurag S. Maskey i_dladm_aggr_modify_sys(dladm_handle_t handle, datalink_id_t linkid, 5964ac67f02SAnurag S. Maskey uint32_t mask, dladm_aggr_modify_attr_t *attr) 597f595a68aSyz147064 { 598f595a68aSyz147064 laioc_modify_t ioc; 599f595a68aSyz147064 600d62bc4baSyz147064 ioc.lu_linkid = linkid; 601f595a68aSyz147064 602f595a68aSyz147064 ioc.lu_modify_mask = 0; 603f595a68aSyz147064 if (mask & DLADM_AGGR_MODIFY_POLICY) 604f595a68aSyz147064 ioc.lu_modify_mask |= LAIOC_MODIFY_POLICY; 605f595a68aSyz147064 if (mask & DLADM_AGGR_MODIFY_MAC) 606f595a68aSyz147064 ioc.lu_modify_mask |= LAIOC_MODIFY_MAC; 607f595a68aSyz147064 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) 608f595a68aSyz147064 ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_MODE; 609f595a68aSyz147064 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) 610f595a68aSyz147064 ioc.lu_modify_mask |= LAIOC_MODIFY_LACP_TIMER; 611f595a68aSyz147064 612f595a68aSyz147064 ioc.lu_policy = attr->ld_policy; 613f595a68aSyz147064 ioc.lu_mac_fixed = attr->ld_mac_fixed; 614f595a68aSyz147064 bcopy(attr->ld_mac, ioc.lu_mac, ETHERADDRL); 615f595a68aSyz147064 ioc.lu_lacp_mode = attr->ld_lacp_mode; 616f595a68aSyz147064 ioc.lu_lacp_timer = attr->ld_lacp_timer; 617f595a68aSyz147064 6184ac67f02SAnurag S. Maskey if (i_dladm_aggr_ioctl(handle, LAIOC_MODIFY, &ioc) < 0) { 619f595a68aSyz147064 if (errno == EINVAL) 620d62bc4baSyz147064 return (DLADM_STATUS_MACADDRINVAL); 621f595a68aSyz147064 else 622d62bc4baSyz147064 return (dladm_errno2status(errno)); 623d62bc4baSyz147064 } else { 624d62bc4baSyz147064 return (DLADM_STATUS_OK); 625f595a68aSyz147064 } 626f595a68aSyz147064 } 627f595a68aSyz147064 628f595a68aSyz147064 /* 629f595a68aSyz147064 * Send a create command to the link aggregation driver. 630f595a68aSyz147064 */ 631f595a68aSyz147064 static dladm_status_t 6324ac67f02SAnurag S. Maskey i_dladm_aggr_create_sys(dladm_handle_t handle, datalink_id_t linkid, 6334ac67f02SAnurag S. Maskey uint16_t key, uint32_t nports, dladm_aggr_port_attr_db_t *ports, 6344ac67f02SAnurag S. Maskey uint32_t policy, boolean_t mac_addr_fixed, const uchar_t *mac_addr, 635d62bc4baSyz147064 aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, boolean_t force) 636f595a68aSyz147064 { 637eae72b5bSSebastien Roy int i, len; 638d62bc4baSyz147064 laioc_create_t *iocp = NULL; 639d62bc4baSyz147064 laioc_port_t *ioc_ports; 640f595a68aSyz147064 dladm_status_t status = DLADM_STATUS_OK; 641f595a68aSyz147064 642d62bc4baSyz147064 len = sizeof (*iocp) + nports * sizeof (laioc_port_t); 643f595a68aSyz147064 iocp = malloc(len); 644f595a68aSyz147064 if (iocp == NULL) 645f595a68aSyz147064 return (DLADM_STATUS_NOMEM); 646f595a68aSyz147064 647d62bc4baSyz147064 iocp->lc_key = key; 648d62bc4baSyz147064 iocp->lc_linkid = linkid; 649d62bc4baSyz147064 iocp->lc_nports = nports; 650d62bc4baSyz147064 iocp->lc_policy = policy; 651d62bc4baSyz147064 iocp->lc_lacp_mode = lacp_mode; 652d62bc4baSyz147064 iocp->lc_lacp_timer = lacp_timer; 653d62bc4baSyz147064 ioc_ports = (laioc_port_t *)(iocp + 1); 654d62bc4baSyz147064 iocp->lc_force = force; 655f595a68aSyz147064 656d62bc4baSyz147064 for (i = 0; i < nports; i++) 657d62bc4baSyz147064 ioc_ports[i].lp_linkid = ports[i].lp_linkid; 658f595a68aSyz147064 659d62bc4baSyz147064 if (mac_addr_fixed && !VALID_PORT_MAC(mac_addr)) { 660d62bc4baSyz147064 status = DLADM_STATUS_MACADDRINVAL; 661d62bc4baSyz147064 goto done; 662f595a68aSyz147064 } 663f595a68aSyz147064 664d62bc4baSyz147064 bcopy(mac_addr, iocp->lc_mac, ETHERADDRL); 665d62bc4baSyz147064 iocp->lc_mac_fixed = mac_addr_fixed; 666f595a68aSyz147064 6674ac67f02SAnurag S. Maskey if (i_dladm_aggr_ioctl(handle, LAIOC_CREATE, iocp) < 0) 668d62bc4baSyz147064 status = dladm_errno2status(errno); 669f595a68aSyz147064 670d62bc4baSyz147064 done: 671f595a68aSyz147064 free(iocp); 672f595a68aSyz147064 return (status); 673f595a68aSyz147064 } 674f595a68aSyz147064 675f595a68aSyz147064 /* 676f595a68aSyz147064 * Invoked to bring up a link aggregation group. 677f595a68aSyz147064 */ 678f595a68aSyz147064 static int 6794ac67f02SAnurag S. Maskey i_dladm_aggr_up(dladm_handle_t handle, datalink_id_t linkid, void *arg) 680f595a68aSyz147064 { 681d62bc4baSyz147064 dladm_status_t *statusp = (dladm_status_t *)arg; 682d62bc4baSyz147064 dladm_aggr_grp_attr_t attr; 683d62bc4baSyz147064 dladm_aggr_port_attr_db_t *ports = NULL; 684d62bc4baSyz147064 uint16_t key = 0; 685f595a68aSyz147064 int i, j; 686f595a68aSyz147064 dladm_status_t status; 687f595a68aSyz147064 6884ac67f02SAnurag S. Maskey status = dladm_aggr_info(handle, linkid, &attr, DLADM_OPT_PERSIST); 689d62bc4baSyz147064 if (status != DLADM_STATUS_OK) { 690d62bc4baSyz147064 *statusp = status; 691d62bc4baSyz147064 return (DLADM_WALK_CONTINUE); 692d62bc4baSyz147064 } 693f595a68aSyz147064 694d62bc4baSyz147064 if (attr.lg_key <= AGGR_MAX_KEY) 695d62bc4baSyz147064 key = attr.lg_key; 696f595a68aSyz147064 697d62bc4baSyz147064 ports = malloc(attr.lg_nports * sizeof (dladm_aggr_port_attr_db_t)); 698d62bc4baSyz147064 if (ports == NULL) { 699d62bc4baSyz147064 status = DLADM_STATUS_NOMEM; 700d62bc4baSyz147064 goto done; 701f595a68aSyz147064 } 702f595a68aSyz147064 703f595a68aSyz147064 /* 704d62bc4baSyz147064 * Validate (and purge) each physical link associated with this 705d62bc4baSyz147064 * aggregation, if the specific hardware has been removed during 706d62bc4baSyz147064 * the system shutdown. 707f595a68aSyz147064 */ 708d62bc4baSyz147064 for (i = 0, j = 0; i < attr.lg_nports; i++) { 709d62bc4baSyz147064 datalink_id_t portid = attr.lg_ports[i].lp_linkid; 710d62bc4baSyz147064 uint32_t flags; 711d62bc4baSyz147064 dladm_status_t s; 712f595a68aSyz147064 7134ac67f02SAnurag S. Maskey s = dladm_datalink_id2info(handle, portid, &flags, NULL, NULL, 7144ac67f02SAnurag S. Maskey NULL, 0); 715d62bc4baSyz147064 if (s != DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE)) 716d62bc4baSyz147064 continue; 717f595a68aSyz147064 718d62bc4baSyz147064 ports[j++].lp_linkid = portid; 719d62bc4baSyz147064 } 720d62bc4baSyz147064 721d62bc4baSyz147064 if (j == 0) { 722d62bc4baSyz147064 /* 723d62bc4baSyz147064 * All of the physical links are removed. 724d62bc4baSyz147064 */ 725d62bc4baSyz147064 status = DLADM_STATUS_BADARG; 726d62bc4baSyz147064 goto done; 727d62bc4baSyz147064 } 728d62bc4baSyz147064 729d62bc4baSyz147064 /* 730d62bc4baSyz147064 * Create active aggregation. 731d62bc4baSyz147064 */ 7324ac67f02SAnurag S. Maskey if ((status = i_dladm_aggr_create_sys(handle, linkid, 733d62bc4baSyz147064 key, j, ports, attr.lg_policy, attr.lg_mac_fixed, 734d62bc4baSyz147064 (const uchar_t *)attr.lg_mac, attr.lg_lacp_mode, 735d62bc4baSyz147064 attr.lg_lacp_timer, attr.lg_force)) != DLADM_STATUS_OK) { 736d62bc4baSyz147064 goto done; 737d62bc4baSyz147064 } 738d62bc4baSyz147064 7394ac67f02SAnurag S. Maskey if ((status = dladm_up_datalink_id(handle, linkid)) != 7404ac67f02SAnurag S. Maskey DLADM_STATUS_OK) { 741d62bc4baSyz147064 laioc_delete_t ioc; 7422b24ab6bSSebastien Roy 743d62bc4baSyz147064 ioc.ld_linkid = linkid; 7444ac67f02SAnurag S. Maskey (void) i_dladm_aggr_ioctl(handle, LAIOC_DELETE, &ioc); 745d62bc4baSyz147064 } 746d62bc4baSyz147064 done: 747d62bc4baSyz147064 free(attr.lg_ports); 748d62bc4baSyz147064 free(ports); 749d62bc4baSyz147064 750d62bc4baSyz147064 *statusp = status; 751d62bc4baSyz147064 return (DLADM_WALK_CONTINUE); 752d62bc4baSyz147064 } 753d62bc4baSyz147064 754d62bc4baSyz147064 /* 755d62bc4baSyz147064 * Bring up one aggregation, or all persistent aggregations. In the latter 756d62bc4baSyz147064 * case, the walk may terminate early if bringup of an aggregation fails. 757d62bc4baSyz147064 */ 758d62bc4baSyz147064 dladm_status_t 7594ac67f02SAnurag S. Maskey dladm_aggr_up(dladm_handle_t handle, datalink_id_t linkid) 760f595a68aSyz147064 { 761f595a68aSyz147064 dladm_status_t status; 762f595a68aSyz147064 763d62bc4baSyz147064 if (linkid == DATALINK_ALL_LINKID) { 7644ac67f02SAnurag S. Maskey (void) dladm_walk_datalink_id(i_dladm_aggr_up, handle, &status, 765d62bc4baSyz147064 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, 766d62bc4baSyz147064 DLADM_OPT_PERSIST); 767f595a68aSyz147064 return (DLADM_STATUS_OK); 768d62bc4baSyz147064 } else { 7694ac67f02SAnurag S. Maskey (void) i_dladm_aggr_up(handle, linkid, &status); 770d62bc4baSyz147064 return (status); 771d62bc4baSyz147064 } 772f595a68aSyz147064 } 773f595a68aSyz147064 774f595a68aSyz147064 /* 775f595a68aSyz147064 * Given a policy string, return a policy mask. Returns B_TRUE on 776d62bc4baSyz147064 * success, or B_FALSE if an error occurred during parsing. 777f595a68aSyz147064 */ 778f595a68aSyz147064 boolean_t 779f595a68aSyz147064 dladm_aggr_str2policy(const char *str, uint32_t *policy) 780f595a68aSyz147064 { 781f595a68aSyz147064 int i; 782f595a68aSyz147064 policy_t *pol; 783f595a68aSyz147064 char *token = NULL; 784f595a68aSyz147064 char *lasts; 785f595a68aSyz147064 786f595a68aSyz147064 *policy = 0; 787f595a68aSyz147064 788f595a68aSyz147064 while ((token = strtok_r((token == NULL) ? (char *)str : NULL, ",", 789f595a68aSyz147064 &lasts)) != NULL) { 790f595a68aSyz147064 for (i = 0; i < NPOLICIES; i++) { 791f595a68aSyz147064 pol = &policies[i]; 792f595a68aSyz147064 if (strcasecmp(token, pol->pol_name) == 0) { 793f595a68aSyz147064 *policy |= pol->policy; 794f595a68aSyz147064 break; 795f595a68aSyz147064 } 796f595a68aSyz147064 } 797f595a68aSyz147064 if (i == NPOLICIES) 798f595a68aSyz147064 return (B_FALSE); 799f595a68aSyz147064 } 800f595a68aSyz147064 801f595a68aSyz147064 return (B_TRUE); 802f595a68aSyz147064 } 803f595a68aSyz147064 804f595a68aSyz147064 /* 805f595a68aSyz147064 * Given a policy mask, returns a printable string, or NULL if the 806f595a68aSyz147064 * policy mask is invalid. It is the responsibility of the caller to 807f595a68aSyz147064 * free the returned string after use. 808f595a68aSyz147064 */ 809f595a68aSyz147064 char * 810f595a68aSyz147064 dladm_aggr_policy2str(uint32_t policy, char *str) 811f595a68aSyz147064 { 812f595a68aSyz147064 int i, npolicies = 0; 813f595a68aSyz147064 policy_t *pol; 814f595a68aSyz147064 815d62bc4baSyz147064 if (str == NULL) 816d62bc4baSyz147064 return (NULL); 817d62bc4baSyz147064 818f595a68aSyz147064 str[0] = '\0'; 819f595a68aSyz147064 820f595a68aSyz147064 for (i = 0; i < NPOLICIES; i++) { 821f595a68aSyz147064 pol = &policies[i]; 822f595a68aSyz147064 if ((policy & pol->policy) != 0) { 823f595a68aSyz147064 npolicies++; 824f595a68aSyz147064 if (npolicies > 1) 825d62bc4baSyz147064 (void) strlcat(str, ",", DLADM_STRSIZE); 826d62bc4baSyz147064 (void) strlcat(str, pol->pol_name, DLADM_STRSIZE); 827f595a68aSyz147064 } 828f595a68aSyz147064 } 829f595a68aSyz147064 830f595a68aSyz147064 return (str); 831f595a68aSyz147064 } 832f595a68aSyz147064 833f595a68aSyz147064 /* 834f595a68aSyz147064 * Given a MAC address string, return the MAC address in the mac_addr 835f595a68aSyz147064 * array. If the MAC address was not explicitly specified, i.e. is 836f595a68aSyz147064 * equal to 'auto', zero out mac-addr and set mac_fixed to B_TRUE. 837f595a68aSyz147064 * Return B_FALSE if a syntax error was encountered, B_FALSE otherwise. 838f595a68aSyz147064 */ 839f595a68aSyz147064 boolean_t 840f595a68aSyz147064 dladm_aggr_str2macaddr(const char *str, boolean_t *mac_fixed, uchar_t *mac_addr) 841f595a68aSyz147064 { 842f595a68aSyz147064 uchar_t *conv_str; 843f595a68aSyz147064 int mac_len; 844f595a68aSyz147064 845f595a68aSyz147064 *mac_fixed = (strcmp(str, "auto") != 0); 846f595a68aSyz147064 if (!*mac_fixed) { 847f595a68aSyz147064 bzero(mac_addr, ETHERADDRL); 848f595a68aSyz147064 return (B_TRUE); 849f595a68aSyz147064 } 850f595a68aSyz147064 851f595a68aSyz147064 conv_str = _link_aton(str, &mac_len); 852f595a68aSyz147064 if (conv_str == NULL) 853f595a68aSyz147064 return (B_FALSE); 854f595a68aSyz147064 855f595a68aSyz147064 if (mac_len != ETHERADDRL) { 856f595a68aSyz147064 free(conv_str); 857f595a68aSyz147064 return (B_FALSE); 858f595a68aSyz147064 } 859f595a68aSyz147064 860f595a68aSyz147064 if ((bcmp(zero_mac, conv_str, ETHERADDRL) == 0) || 861f595a68aSyz147064 (conv_str[0] & 0x01)) { 862f595a68aSyz147064 free(conv_str); 863f595a68aSyz147064 return (B_FALSE); 864f595a68aSyz147064 } 865f595a68aSyz147064 866f595a68aSyz147064 bcopy(conv_str, mac_addr, ETHERADDRL); 867f595a68aSyz147064 free(conv_str); 868f595a68aSyz147064 869f595a68aSyz147064 return (B_TRUE); 870f595a68aSyz147064 } 871f595a68aSyz147064 872f595a68aSyz147064 /* 873f595a68aSyz147064 * Returns a string containing a printable representation of a MAC address. 874f595a68aSyz147064 */ 875f595a68aSyz147064 const char * 876d62bc4baSyz147064 dladm_aggr_macaddr2str(const unsigned char *mac, char *buf) 877f595a68aSyz147064 { 878f595a68aSyz147064 static char unknown_mac[] = {0, 0, 0, 0, 0, 0}; 879f595a68aSyz147064 880f595a68aSyz147064 if (buf == NULL) 881f595a68aSyz147064 return (NULL); 882f595a68aSyz147064 883f595a68aSyz147064 if (bcmp(unknown_mac, mac, ETHERADDRL) == 0) 884d62bc4baSyz147064 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 885f595a68aSyz147064 else 886f595a68aSyz147064 return (_link_ntoa(mac, buf, ETHERADDRL, IFT_OTHER)); 887d62bc4baSyz147064 888d62bc4baSyz147064 return (buf); 889f595a68aSyz147064 } 890f595a68aSyz147064 891f595a68aSyz147064 /* 892f595a68aSyz147064 * Given a LACP mode string, find the corresponding LACP mode number. Returns 893f595a68aSyz147064 * B_TRUE if a match was found, B_FALSE otherwise. 894f595a68aSyz147064 */ 895f595a68aSyz147064 boolean_t 896f595a68aSyz147064 dladm_aggr_str2lacpmode(const char *str, aggr_lacp_mode_t *lacp_mode) 897f595a68aSyz147064 { 898f595a68aSyz147064 int i; 899f595a68aSyz147064 dladm_aggr_lacpmode_t *mode; 900f595a68aSyz147064 901f595a68aSyz147064 for (i = 0; i < NLACP_MODES; i++) { 902f595a68aSyz147064 mode = &lacp_modes[i]; 903f595a68aSyz147064 if (strncasecmp(str, mode->mode_str, 904f595a68aSyz147064 strlen(mode->mode_str)) == 0) { 905f595a68aSyz147064 *lacp_mode = mode->mode_id; 906f595a68aSyz147064 return (B_TRUE); 907f595a68aSyz147064 } 908f595a68aSyz147064 } 909f595a68aSyz147064 910f595a68aSyz147064 return (B_FALSE); 911f595a68aSyz147064 } 912f595a68aSyz147064 913f595a68aSyz147064 /* 914f595a68aSyz147064 * Given a LACP mode number, returns a printable string, or NULL if the 915f595a68aSyz147064 * LACP mode number is invalid. 916f595a68aSyz147064 */ 917f595a68aSyz147064 const char * 918f595a68aSyz147064 dladm_aggr_lacpmode2str(aggr_lacp_mode_t mode_id, char *buf) 919f595a68aSyz147064 { 920f595a68aSyz147064 int i; 921f595a68aSyz147064 dladm_aggr_lacpmode_t *mode; 922f595a68aSyz147064 923d62bc4baSyz147064 if (buf == NULL) 924d62bc4baSyz147064 return (NULL); 925d62bc4baSyz147064 926f595a68aSyz147064 for (i = 0; i < NLACP_MODES; i++) { 927f595a68aSyz147064 mode = &lacp_modes[i]; 928f595a68aSyz147064 if (mode->mode_id == mode_id) { 929f595a68aSyz147064 (void) snprintf(buf, DLADM_STRSIZE, "%s", 930f595a68aSyz147064 mode->mode_str); 931f595a68aSyz147064 return (buf); 932f595a68aSyz147064 } 933f595a68aSyz147064 } 934f595a68aSyz147064 935f595a68aSyz147064 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 936f595a68aSyz147064 return (buf); 937f595a68aSyz147064 } 938f595a68aSyz147064 939f595a68aSyz147064 /* 940f595a68aSyz147064 * Given a LACP timer string, find the corresponding LACP timer number. Returns 941f595a68aSyz147064 * B_TRUE if a match was found, B_FALSE otherwise. 942f595a68aSyz147064 */ 943f595a68aSyz147064 boolean_t 944f595a68aSyz147064 dladm_aggr_str2lacptimer(const char *str, aggr_lacp_timer_t *lacp_timer) 945f595a68aSyz147064 { 946f595a68aSyz147064 int i; 947f595a68aSyz147064 dladm_aggr_lacptimer_t *timer; 948f595a68aSyz147064 949f595a68aSyz147064 for (i = 0; i < NLACP_TIMERS; i++) { 950f595a68aSyz147064 timer = &lacp_timers[i]; 951f595a68aSyz147064 if (strncasecmp(str, timer->lt_str, 952f595a68aSyz147064 strlen(timer->lt_str)) == 0) { 953f595a68aSyz147064 *lacp_timer = timer->lt_id; 954f595a68aSyz147064 return (B_TRUE); 955f595a68aSyz147064 } 956f595a68aSyz147064 } 957f595a68aSyz147064 958f595a68aSyz147064 return (B_FALSE); 959f595a68aSyz147064 } 960f595a68aSyz147064 961f595a68aSyz147064 /* 962f595a68aSyz147064 * Given a LACP timer, returns a printable string, or NULL if the 963f595a68aSyz147064 * LACP timer number is invalid. 964f595a68aSyz147064 */ 965f595a68aSyz147064 const char * 966f595a68aSyz147064 dladm_aggr_lacptimer2str(aggr_lacp_timer_t timer_id, char *buf) 967f595a68aSyz147064 { 968f595a68aSyz147064 int i; 969f595a68aSyz147064 dladm_aggr_lacptimer_t *timer; 970f595a68aSyz147064 971d62bc4baSyz147064 if (buf == NULL) 972d62bc4baSyz147064 return (NULL); 973d62bc4baSyz147064 974f595a68aSyz147064 for (i = 0; i < NLACP_TIMERS; i++) { 975f595a68aSyz147064 timer = &lacp_timers[i]; 976f595a68aSyz147064 if (timer->lt_id == timer_id) { 977f595a68aSyz147064 (void) snprintf(buf, DLADM_STRSIZE, "%s", 978f595a68aSyz147064 timer->lt_str); 979f595a68aSyz147064 return (buf); 980f595a68aSyz147064 } 981f595a68aSyz147064 } 982f595a68aSyz147064 983f595a68aSyz147064 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 984f595a68aSyz147064 return (buf); 985f595a68aSyz147064 } 986f595a68aSyz147064 987f595a68aSyz147064 const char * 988f595a68aSyz147064 dladm_aggr_portstate2str(aggr_port_state_t state_id, char *buf) 989f595a68aSyz147064 { 990f595a68aSyz147064 int i; 991f595a68aSyz147064 dladm_aggr_port_state_t *state; 992f595a68aSyz147064 993d62bc4baSyz147064 if (buf == NULL) 994d62bc4baSyz147064 return (NULL); 995d62bc4baSyz147064 996f595a68aSyz147064 for (i = 0; i < NPORT_STATES; i++) { 997f595a68aSyz147064 state = &port_states[i]; 998f595a68aSyz147064 if (state->state_id == state_id) { 999f595a68aSyz147064 (void) snprintf(buf, DLADM_STRSIZE, "%s", 1000f595a68aSyz147064 state->state_str); 1001f595a68aSyz147064 return (buf); 1002f595a68aSyz147064 } 1003f595a68aSyz147064 } 1004f595a68aSyz147064 1005f595a68aSyz147064 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 1006f595a68aSyz147064 return (buf); 1007f595a68aSyz147064 } 1008f595a68aSyz147064 1009f595a68aSyz147064 static dladm_status_t 10104ac67f02SAnurag S. Maskey dladm_aggr_persist_aggr_conf(dladm_handle_t handle, const char *link, 10114ac67f02SAnurag S. Maskey datalink_id_t linkid, uint16_t key, uint32_t nports, 10124ac67f02SAnurag S. Maskey dladm_aggr_port_attr_db_t *ports, uint32_t policy, boolean_t mac_addr_fixed, 10134ac67f02SAnurag S. Maskey const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 10144ac67f02SAnurag S. Maskey aggr_lacp_timer_t lacp_timer, boolean_t force) 1015f595a68aSyz147064 { 1016d62bc4baSyz147064 dladm_conf_t conf = DLADM_INVALID_CONF; 1017d62bc4baSyz147064 char *portstr = NULL; 1018d62bc4baSyz147064 char macstr[ETHERADDRL * 3]; 1019f595a68aSyz147064 dladm_status_t status; 1020d62bc4baSyz147064 int i, size; 1021d62bc4baSyz147064 uint64_t u64; 1022f595a68aSyz147064 10234ac67f02SAnurag S. Maskey if ((status = dladm_create_conf(handle, link, linkid, 10244ac67f02SAnurag S. Maskey DATALINK_CLASS_AGGR, DL_ETHER, &conf)) != DLADM_STATUS_OK) { 1025f595a68aSyz147064 return (status); 1026f595a68aSyz147064 } 1027f595a68aSyz147064 1028d62bc4baSyz147064 u64 = key; 10294ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FKEY, DLADM_TYPE_UINT64, 10304ac67f02SAnurag S. Maskey &u64); 1031d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1032d62bc4baSyz147064 goto done; 1033f595a68aSyz147064 1034d62bc4baSyz147064 u64 = nports; 10354ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FNPORTS, DLADM_TYPE_UINT64, 10364ac67f02SAnurag S. Maskey &u64); 1037d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1038d62bc4baSyz147064 goto done; 1039f595a68aSyz147064 10402b24ab6bSSebastien Roy size = nports * MAXLINKNAMELEN + 1; 1041d62bc4baSyz147064 if ((portstr = calloc(1, size)) == NULL) { 1042d62bc4baSyz147064 status = DLADM_STATUS_NOMEM; 1043f595a68aSyz147064 goto done; 1044f595a68aSyz147064 } 1045f595a68aSyz147064 10462b24ab6bSSebastien Roy for (i = 0; i < nports; i++) { 10472b24ab6bSSebastien Roy status = write_port(handle, portstr, ports[i].lp_linkid, size); 10482b24ab6bSSebastien Roy if (status != DLADM_STATUS_OK) { 10492b24ab6bSSebastien Roy free(portstr); 10502b24ab6bSSebastien Roy goto done; 10512b24ab6bSSebastien Roy } 10522b24ab6bSSebastien Roy } 10534ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FPORTS, DLADM_TYPE_STR, 10544ac67f02SAnurag S. Maskey portstr); 1055d62bc4baSyz147064 free(portstr); 1056d62bc4baSyz147064 1057d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1058d62bc4baSyz147064 goto done; 1059d62bc4baSyz147064 1060d62bc4baSyz147064 u64 = policy; 10614ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FPOLICY, DLADM_TYPE_UINT64, 10624ac67f02SAnurag S. Maskey &u64); 1063d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1064d62bc4baSyz147064 goto done; 1065d62bc4baSyz147064 10664ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FFIXMACADDR, 10674ac67f02SAnurag S. Maskey DLADM_TYPE_BOOLEAN, &mac_addr_fixed); 1068d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1069d62bc4baSyz147064 goto done; 1070d62bc4baSyz147064 1071d62bc4baSyz147064 if (mac_addr_fixed) { 1072d62bc4baSyz147064 if (!VALID_PORT_MAC(mac_addr)) { 1073d62bc4baSyz147064 status = DLADM_STATUS_MACADDRINVAL; 1074f595a68aSyz147064 goto done; 1075f595a68aSyz147064 } 1076d62bc4baSyz147064 1077d62bc4baSyz147064 (void) dladm_aggr_macaddr2str(mac_addr, macstr); 10784ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FMACADDR, 10794ac67f02SAnurag S. Maskey DLADM_TYPE_STR, macstr); 1080d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1081d62bc4baSyz147064 goto done; 1082f595a68aSyz147064 } 1083f595a68aSyz147064 10844ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FFORCE, DLADM_TYPE_BOOLEAN, 10854ac67f02SAnurag S. Maskey &force); 1086d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1087d62bc4baSyz147064 goto done; 1088d62bc4baSyz147064 1089d62bc4baSyz147064 u64 = lacp_mode; 10904ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FLACPMODE, 10914ac67f02SAnurag S. Maskey DLADM_TYPE_UINT64, &u64); 1092d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1093d62bc4baSyz147064 goto done; 1094d62bc4baSyz147064 1095d62bc4baSyz147064 u64 = lacp_timer; 10964ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FLACPTIMER, 10974ac67f02SAnurag S. Maskey DLADM_TYPE_UINT64, &u64); 1098d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1099d62bc4baSyz147064 goto done; 1100d62bc4baSyz147064 1101f595a68aSyz147064 /* 1102d62bc4baSyz147064 * Commit the link aggregation configuration. 1103f595a68aSyz147064 */ 11044ac67f02SAnurag S. Maskey status = dladm_write_conf(handle, conf); 1105f595a68aSyz147064 1106f595a68aSyz147064 done: 11074ac67f02SAnurag S. Maskey dladm_destroy_conf(handle, conf); 1108f595a68aSyz147064 return (status); 1109f595a68aSyz147064 } 1110f595a68aSyz147064 1111f595a68aSyz147064 /* 1112f595a68aSyz147064 * Create a new link aggregation group. Update the configuration 1113f595a68aSyz147064 * file and bring it up. 1114f595a68aSyz147064 */ 1115f595a68aSyz147064 dladm_status_t 11164ac67f02SAnurag S. Maskey dladm_aggr_create(dladm_handle_t handle, const char *name, uint16_t key, 11174ac67f02SAnurag S. Maskey uint32_t nports, dladm_aggr_port_attr_db_t *ports, uint32_t policy, 11184ac67f02SAnurag S. Maskey boolean_t mac_addr_fixed, const uchar_t *mac_addr, 11194ac67f02SAnurag S. Maskey aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer, uint32_t flags) 1120f595a68aSyz147064 { 1121d62bc4baSyz147064 datalink_id_t linkid = DATALINK_INVALID_LINKID; 1122d62bc4baSyz147064 uint32_t media; 1123d62bc4baSyz147064 uint32_t i; 1124d62bc4baSyz147064 datalink_class_t class; 1125f595a68aSyz147064 dladm_status_t status; 1126d62bc4baSyz147064 boolean_t force = (flags & DLADM_OPT_FORCE) ? B_TRUE : B_FALSE; 1127f595a68aSyz147064 1128d62bc4baSyz147064 if (key != 0 && key > AGGR_MAX_KEY) 1129f595a68aSyz147064 return (DLADM_STATUS_KEYINVAL); 1130f595a68aSyz147064 1131d62bc4baSyz147064 if (nports == 0) 1132d62bc4baSyz147064 return (DLADM_STATUS_BADARG); 1133f595a68aSyz147064 1134d62bc4baSyz147064 for (i = 0; i < nports; i++) { 11354ac67f02SAnurag S. Maskey if ((dladm_datalink_id2info(handle, ports[i].lp_linkid, NULL, 1136d62bc4baSyz147064 &class, &media, NULL, 0) != DLADM_STATUS_OK) || 1137b509e89bSRishi Srivatsavai !((class == DATALINK_CLASS_PHYS || class == 1138b509e89bSRishi Srivatsavai DATALINK_CLASS_SIMNET) && (media == DL_ETHER))) { 1139d62bc4baSyz147064 return (DLADM_STATUS_BADARG); 1140d62bc4baSyz147064 } 1141d62bc4baSyz147064 } 1142f595a68aSyz147064 1143d62bc4baSyz147064 flags &= (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); 11444ac67f02SAnurag S. Maskey if ((status = dladm_create_datalink_id(handle, name, 11454ac67f02SAnurag S. Maskey DATALINK_CLASS_AGGR, DL_ETHER, flags, &linkid)) != 11464ac67f02SAnurag S. Maskey DLADM_STATUS_OK) { 1147d62bc4baSyz147064 goto fail; 1148d62bc4baSyz147064 } 1149f595a68aSyz147064 1150d62bc4baSyz147064 if ((flags & DLADM_OPT_PERSIST) && 11514ac67f02SAnurag S. Maskey (status = dladm_aggr_persist_aggr_conf(handle, name, linkid, key, 11524ac67f02SAnurag S. Maskey nports, ports, policy, mac_addr_fixed, mac_addr, lacp_mode, 11534ac67f02SAnurag S. Maskey lacp_timer, force)) != DLADM_STATUS_OK) { 1154d62bc4baSyz147064 goto fail; 1155d62bc4baSyz147064 } 1156d62bc4baSyz147064 1157d62bc4baSyz147064 if (!(flags & DLADM_OPT_ACTIVE)) 1158d62bc4baSyz147064 return (DLADM_STATUS_OK); 1159d62bc4baSyz147064 11604ac67f02SAnurag S. Maskey status = i_dladm_aggr_create_sys(handle, linkid, key, nports, ports, 11614ac67f02SAnurag S. Maskey policy, mac_addr_fixed, mac_addr, lacp_mode, lacp_timer, force); 1162d62bc4baSyz147064 1163d62bc4baSyz147064 if (status != DLADM_STATUS_OK) { 1164d62bc4baSyz147064 if (flags & DLADM_OPT_PERSIST) 11654ac67f02SAnurag S. Maskey (void) dladm_remove_conf(handle, linkid); 1166d62bc4baSyz147064 goto fail; 1167d62bc4baSyz147064 } 1168d62bc4baSyz147064 1169d62bc4baSyz147064 return (DLADM_STATUS_OK); 1170d62bc4baSyz147064 1171d62bc4baSyz147064 fail: 1172d62bc4baSyz147064 if (linkid != DATALINK_INVALID_LINKID) 11734ac67f02SAnurag S. Maskey (void) dladm_destroy_datalink_id(handle, linkid, flags); 1174d62bc4baSyz147064 1175f595a68aSyz147064 return (status); 1176f595a68aSyz147064 } 1177f595a68aSyz147064 1178d62bc4baSyz147064 static dladm_status_t 11794ac67f02SAnurag S. Maskey i_dladm_aggr_get_aggr_attr(dladm_handle_t handle, dladm_conf_t conf, 11804ac67f02SAnurag S. Maskey uint32_t mask, dladm_aggr_modify_attr_t *attrp) 1181d62bc4baSyz147064 { 1182d62bc4baSyz147064 dladm_status_t status = DLADM_STATUS_OK; 1183d62bc4baSyz147064 char macstr[ETHERADDRL * 3]; 1184d62bc4baSyz147064 uint64_t u64; 1185f595a68aSyz147064 1186d62bc4baSyz147064 if (mask & DLADM_AGGR_MODIFY_POLICY) { 11874ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FPOLICY, &u64, 1188d62bc4baSyz147064 sizeof (u64)); 1189d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1190d62bc4baSyz147064 return (status); 1191d62bc4baSyz147064 attrp->ld_policy = (uint32_t)u64; 1192d62bc4baSyz147064 } 1193d62bc4baSyz147064 1194d62bc4baSyz147064 if (mask & DLADM_AGGR_MODIFY_MAC) { 11954ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FFIXMACADDR, 1196d62bc4baSyz147064 &attrp->ld_mac_fixed, sizeof (boolean_t)); 1197d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1198d62bc4baSyz147064 return (status); 1199d62bc4baSyz147064 1200d62bc4baSyz147064 if (attrp->ld_mac_fixed) { 1201d62bc4baSyz147064 boolean_t fixed; 1202d62bc4baSyz147064 12034ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FMACADDR, 1204d62bc4baSyz147064 macstr, sizeof (macstr)); 1205d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1206d62bc4baSyz147064 return (status); 1207d62bc4baSyz147064 1208d62bc4baSyz147064 if (!dladm_aggr_str2macaddr(macstr, &fixed, 1209d62bc4baSyz147064 attrp->ld_mac)) { 1210d62bc4baSyz147064 return (DLADM_STATUS_REPOSITORYINVAL); 1211d62bc4baSyz147064 } 1212d62bc4baSyz147064 } 1213d62bc4baSyz147064 } 1214d62bc4baSyz147064 1215d62bc4baSyz147064 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) { 12164ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FLACPMODE, &u64, 1217d62bc4baSyz147064 sizeof (u64)); 1218d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1219d62bc4baSyz147064 return (status); 1220d62bc4baSyz147064 attrp->ld_lacp_mode = (aggr_lacp_mode_t)u64; 1221d62bc4baSyz147064 } 1222d62bc4baSyz147064 1223d62bc4baSyz147064 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) { 12244ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FLACPTIMER, &u64, 1225d62bc4baSyz147064 sizeof (u64)); 1226d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1227d62bc4baSyz147064 return (status); 1228d62bc4baSyz147064 attrp->ld_lacp_timer = (aggr_lacp_timer_t)u64; 1229d62bc4baSyz147064 } 1230d62bc4baSyz147064 1231d62bc4baSyz147064 return (status); 1232d62bc4baSyz147064 } 1233d62bc4baSyz147064 1234d62bc4baSyz147064 static dladm_status_t 12354ac67f02SAnurag S. Maskey i_dladm_aggr_set_aggr_attr(dladm_handle_t handle, dladm_conf_t conf, 12364ac67f02SAnurag S. Maskey uint32_t mask, dladm_aggr_modify_attr_t *attrp) 1237d62bc4baSyz147064 { 1238d62bc4baSyz147064 dladm_status_t status = DLADM_STATUS_OK; 1239d62bc4baSyz147064 char macstr[ETHERADDRL * 3]; 1240d62bc4baSyz147064 uint64_t u64; 1241d62bc4baSyz147064 1242d62bc4baSyz147064 if (mask & DLADM_AGGR_MODIFY_POLICY) { 1243d62bc4baSyz147064 u64 = attrp->ld_policy; 12444ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FPOLICY, 12454ac67f02SAnurag S. Maskey DLADM_TYPE_UINT64, &u64); 1246d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1247d62bc4baSyz147064 return (status); 1248d62bc4baSyz147064 } 1249d62bc4baSyz147064 1250d62bc4baSyz147064 if (mask & DLADM_AGGR_MODIFY_MAC) { 12514ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FFIXMACADDR, 1252d62bc4baSyz147064 DLADM_TYPE_BOOLEAN, &attrp->ld_mac_fixed); 1253d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1254d62bc4baSyz147064 return (status); 1255d62bc4baSyz147064 1256d62bc4baSyz147064 if (attrp->ld_mac_fixed) { 1257d62bc4baSyz147064 (void) dladm_aggr_macaddr2str(attrp->ld_mac, macstr); 12584ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FMACADDR, 1259d62bc4baSyz147064 DLADM_TYPE_STR, macstr); 1260d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1261d62bc4baSyz147064 return (status); 1262d62bc4baSyz147064 } 1263d62bc4baSyz147064 } 1264d62bc4baSyz147064 1265d62bc4baSyz147064 if (mask & DLADM_AGGR_MODIFY_LACP_MODE) { 1266d62bc4baSyz147064 u64 = attrp->ld_lacp_mode; 12674ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FLACPMODE, 1268d62bc4baSyz147064 DLADM_TYPE_UINT64, &u64); 1269d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1270d62bc4baSyz147064 return (status); 1271d62bc4baSyz147064 } 1272d62bc4baSyz147064 1273d62bc4baSyz147064 if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) { 1274d62bc4baSyz147064 u64 = attrp->ld_lacp_timer; 12754ac67f02SAnurag S. Maskey status = dladm_set_conf_field(handle, conf, FLACPTIMER, 1276d62bc4baSyz147064 DLADM_TYPE_UINT64, &u64); 1277d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1278d62bc4baSyz147064 return (status); 1279d62bc4baSyz147064 } 1280f595a68aSyz147064 1281f595a68aSyz147064 return (status); 1282f595a68aSyz147064 } 1283f595a68aSyz147064 1284f595a68aSyz147064 /* 1285f595a68aSyz147064 * Modify the parameters of an existing link aggregation group. Update 1286f595a68aSyz147064 * the configuration file and pass the changes to the kernel. 1287f595a68aSyz147064 */ 1288f595a68aSyz147064 dladm_status_t 12894ac67f02SAnurag S. Maskey dladm_aggr_modify(dladm_handle_t handle, datalink_id_t linkid, 12904ac67f02SAnurag S. Maskey uint32_t modify_mask, uint32_t policy, boolean_t mac_fixed, 12914ac67f02SAnurag S. Maskey const uchar_t *mac_addr, aggr_lacp_mode_t lacp_mode, 1292d62bc4baSyz147064 aggr_lacp_timer_t lacp_timer, uint32_t flags) 1293f595a68aSyz147064 { 1294f595a68aSyz147064 dladm_aggr_modify_attr_t new_attr, old_attr; 1295d62bc4baSyz147064 dladm_conf_t conf; 1296f595a68aSyz147064 dladm_status_t status; 1297f595a68aSyz147064 1298f595a68aSyz147064 new_attr.ld_policy = policy; 1299f595a68aSyz147064 new_attr.ld_mac_fixed = mac_fixed; 1300f595a68aSyz147064 new_attr.ld_lacp_mode = lacp_mode; 1301f595a68aSyz147064 new_attr.ld_lacp_timer = lacp_timer; 1302d62bc4baSyz147064 bcopy(mac_addr, new_attr.ld_mac, ETHERADDRL); 1303f595a68aSyz147064 1304d62bc4baSyz147064 if (flags & DLADM_OPT_PERSIST) { 13054ac67f02SAnurag S. Maskey status = dladm_read_conf(handle, linkid, &conf); 1306d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1307d62bc4baSyz147064 return (status); 1308d62bc4baSyz147064 13094ac67f02SAnurag S. Maskey if ((status = i_dladm_aggr_get_aggr_attr(handle, conf, 13104ac67f02SAnurag S. Maskey modify_mask, &old_attr)) != DLADM_STATUS_OK) { 1311d62bc4baSyz147064 goto done; 1312d62bc4baSyz147064 } 1313d62bc4baSyz147064 13144ac67f02SAnurag S. Maskey if ((status = i_dladm_aggr_set_aggr_attr(handle, conf, 13154ac67f02SAnurag S. Maskey modify_mask, &new_attr)) != DLADM_STATUS_OK) { 1316d62bc4baSyz147064 goto done; 1317d62bc4baSyz147064 } 1318d62bc4baSyz147064 13194ac67f02SAnurag S. Maskey status = dladm_write_conf(handle, conf); 1320d62bc4baSyz147064 1321d62bc4baSyz147064 done: 13224ac67f02SAnurag S. Maskey dladm_destroy_conf(handle, conf); 1323d62bc4baSyz147064 if (status != DLADM_STATUS_OK) 1324f595a68aSyz147064 return (status); 1325f595a68aSyz147064 } 1326f595a68aSyz147064 1327d62bc4baSyz147064 if (!(flags & DLADM_OPT_ACTIVE)) 1328d62bc4baSyz147064 return (DLADM_STATUS_OK); 1329d62bc4baSyz147064 13304ac67f02SAnurag S. Maskey status = i_dladm_aggr_modify_sys(handle, linkid, modify_mask, 13314ac67f02SAnurag S. Maskey &new_attr); 1332d62bc4baSyz147064 if ((status != DLADM_STATUS_OK) && (flags & DLADM_OPT_PERSIST)) { 13334ac67f02SAnurag S. Maskey if (dladm_read_conf(handle, linkid, &conf) == DLADM_STATUS_OK) { 13344ac67f02SAnurag S. Maskey if (i_dladm_aggr_set_aggr_attr(handle, conf, 13354ac67f02SAnurag S. Maskey modify_mask, &old_attr) == DLADM_STATUS_OK) { 13364ac67f02SAnurag S. Maskey (void) dladm_write_conf(handle, conf); 1337d62bc4baSyz147064 } 13384ac67f02SAnurag S. Maskey dladm_destroy_conf(handle, conf); 1339d62bc4baSyz147064 } 1340f595a68aSyz147064 } 1341f595a68aSyz147064 1342f595a68aSyz147064 return (status); 1343f595a68aSyz147064 } 1344f595a68aSyz147064 1345d62bc4baSyz147064 typedef struct aggr_held_arg_s { 1346d62bc4baSyz147064 datalink_id_t aggrid; 1347d62bc4baSyz147064 boolean_t isheld; 1348d62bc4baSyz147064 } aggr_held_arg_t; 1349d62bc4baSyz147064 1350d62bc4baSyz147064 static int 13514ac67f02SAnurag S. Maskey i_dladm_aggr_is_held(dladm_handle_t handle, datalink_id_t linkid, void *arg) 1352d62bc4baSyz147064 { 1353d62bc4baSyz147064 aggr_held_arg_t *aggr_held_arg = arg; 1354d62bc4baSyz147064 dladm_vlan_attr_t dva; 1355d62bc4baSyz147064 13564ac67f02SAnurag S. Maskey if (dladm_vlan_info(handle, linkid, &dva, DLADM_OPT_PERSIST) != 13574ac67f02SAnurag S. Maskey DLADM_STATUS_OK) 1358d62bc4baSyz147064 return (DLADM_WALK_CONTINUE); 1359d62bc4baSyz147064 1360d62bc4baSyz147064 if (dva.dv_linkid == aggr_held_arg->aggrid) { 1361d62bc4baSyz147064 /* 1362d62bc4baSyz147064 * This VLAN is created over the given aggregation. 1363d62bc4baSyz147064 */ 1364d62bc4baSyz147064 aggr_held_arg->isheld = B_TRUE; 1365d62bc4baSyz147064 return (DLADM_WALK_TERMINATE); 1366d62bc4baSyz147064 } 1367d62bc4baSyz147064 return (DLADM_WALK_CONTINUE); 1368d62bc4baSyz147064 } 1369d62bc4baSyz147064 1370f595a68aSyz147064 /* 1371d62bc4baSyz147064 * Delete a previously created link aggregation group. Either the name "aggr" 1372d62bc4baSyz147064 * or the "key" is specified. 1373f595a68aSyz147064 */ 1374f595a68aSyz147064 dladm_status_t 13754ac67f02SAnurag S. Maskey dladm_aggr_delete(dladm_handle_t handle, datalink_id_t linkid, uint32_t flags) 1376f595a68aSyz147064 { 1377d62bc4baSyz147064 laioc_delete_t ioc; 1378d62bc4baSyz147064 datalink_class_t class; 1379f595a68aSyz147064 dladm_status_t status; 1380f595a68aSyz147064 13814ac67f02SAnurag S. Maskey if ((dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, NULL, 13824ac67f02SAnurag S. Maskey 0) != DLADM_STATUS_OK) || (class != DATALINK_CLASS_AGGR)) { 1383d62bc4baSyz147064 return (DLADM_STATUS_BADARG); 1384d62bc4baSyz147064 } 1385f595a68aSyz147064 1386d62bc4baSyz147064 if (flags & DLADM_OPT_ACTIVE) { 1387d62bc4baSyz147064 ioc.ld_linkid = linkid; 13884ac67f02SAnurag S. Maskey if ((i_dladm_aggr_ioctl(handle, LAIOC_DELETE, &ioc) < 0) && 1389d62bc4baSyz147064 ((errno != ENOENT) || !(flags & DLADM_OPT_PERSIST))) { 1390d62bc4baSyz147064 status = dladm_errno2status(errno); 1391f595a68aSyz147064 return (status); 1392f595a68aSyz147064 } 1393d62bc4baSyz147064 1394d62bc4baSyz147064 /* 1395d62bc4baSyz147064 * Delete ACTIVE linkprop first. 1396d62bc4baSyz147064 */ 13974ac67f02SAnurag S. Maskey (void) dladm_set_linkprop(handle, linkid, NULL, NULL, 0, 1398d62bc4baSyz147064 DLADM_OPT_ACTIVE); 13994ac67f02SAnurag S. Maskey (void) dladm_destroy_datalink_id(handle, linkid, 14004ac67f02SAnurag S. Maskey DLADM_OPT_ACTIVE); 1401f595a68aSyz147064 } 1402f595a68aSyz147064 1403d62bc4baSyz147064 /* 1404d62bc4baSyz147064 * If we reach here, it means that the active aggregation has already 1405d62bc4baSyz147064 * been deleted, and there is no active VLANs holding this aggregation. 1406d62bc4baSyz147064 * Now we see whether there is any persistent VLANs holding this 1407d62bc4baSyz147064 * aggregation. If so, we fail the operation. 1408d62bc4baSyz147064 */ 1409d62bc4baSyz147064 if (flags & DLADM_OPT_PERSIST) { 1410d62bc4baSyz147064 aggr_held_arg_t arg; 1411f595a68aSyz147064 1412d62bc4baSyz147064 arg.aggrid = linkid; 1413d62bc4baSyz147064 arg.isheld = B_FALSE; 1414d62bc4baSyz147064 14154ac67f02SAnurag S. Maskey (void) dladm_walk_datalink_id(i_dladm_aggr_is_held, handle, 1416d62bc4baSyz147064 &arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, 1417d62bc4baSyz147064 DLADM_OPT_PERSIST); 1418d62bc4baSyz147064 if (arg.isheld) 1419d62bc4baSyz147064 return (DLADM_STATUS_LINKBUSY); 1420d62bc4baSyz147064 14212b24ab6bSSebastien Roy (void) dladm_remove_conf(handle, linkid); 14224ac67f02SAnurag S. Maskey (void) dladm_destroy_datalink_id(handle, linkid, 14234ac67f02SAnurag S. Maskey DLADM_OPT_PERSIST); 1424d62bc4baSyz147064 } 1425d62bc4baSyz147064 1426d62bc4baSyz147064 return (DLADM_STATUS_OK); 1427f595a68aSyz147064 } 1428f595a68aSyz147064 1429f595a68aSyz147064 /* 1430f595a68aSyz147064 * Add one or more ports to an existing link aggregation. 1431f595a68aSyz147064 */ 1432f595a68aSyz147064 dladm_status_t 14334ac67f02SAnurag S. Maskey dladm_aggr_add(dladm_handle_t handle, datalink_id_t linkid, uint32_t nports, 1434d62bc4baSyz147064 dladm_aggr_port_attr_db_t *ports, uint32_t flags) 1435f595a68aSyz147064 { 14364ac67f02SAnurag S. Maskey return (i_dladm_aggr_add_rmv(handle, linkid, nports, ports, flags, 14374ac67f02SAnurag S. Maskey LAIOC_ADD)); 1438f595a68aSyz147064 } 1439f595a68aSyz147064 1440f595a68aSyz147064 /* 1441f595a68aSyz147064 * Remove one or more ports from an existing link aggregation. 1442f595a68aSyz147064 */ 1443f595a68aSyz147064 dladm_status_t 14444ac67f02SAnurag S. Maskey dladm_aggr_remove(dladm_handle_t handle, datalink_id_t linkid, uint32_t nports, 1445d62bc4baSyz147064 dladm_aggr_port_attr_db_t *ports, uint32_t flags) 1446f595a68aSyz147064 { 14474ac67f02SAnurag S. Maskey return (i_dladm_aggr_add_rmv(handle, linkid, nports, ports, flags, 1448d62bc4baSyz147064 LAIOC_REMOVE)); 1449f595a68aSyz147064 } 1450f595a68aSyz147064 1451d62bc4baSyz147064 typedef struct i_walk_key_state_s { 1452d62bc4baSyz147064 uint16_t key; 1453d62bc4baSyz147064 datalink_id_t linkid; 1454d62bc4baSyz147064 boolean_t found; 1455d62bc4baSyz147064 } i_walk_key_state_t; 1456f595a68aSyz147064 1457d62bc4baSyz147064 static int 14584ac67f02SAnurag S. Maskey i_dladm_walk_key2linkid(dladm_handle_t handle, datalink_id_t linkid, void *arg) 1459d62bc4baSyz147064 { 1460d62bc4baSyz147064 dladm_conf_t conf; 1461d62bc4baSyz147064 uint16_t key; 1462d62bc4baSyz147064 dladm_status_t status; 1463d62bc4baSyz147064 i_walk_key_state_t *statep = (i_walk_key_state_t *)arg; 1464d62bc4baSyz147064 uint64_t u64; 1465d62bc4baSyz147064 14664ac67f02SAnurag S. Maskey if (dladm_read_conf(handle, linkid, &conf) != 0) 1467d62bc4baSyz147064 return (DLADM_WALK_CONTINUE); 1468d62bc4baSyz147064 14694ac67f02SAnurag S. Maskey status = dladm_get_conf_field(handle, conf, FKEY, &u64, sizeof (u64)); 1470d62bc4baSyz147064 key = (uint16_t)u64; 14714ac67f02SAnurag S. Maskey dladm_destroy_conf(handle, conf); 1472d62bc4baSyz147064 1473d62bc4baSyz147064 if ((status == DLADM_STATUS_OK) && (key == statep->key)) { 1474d62bc4baSyz147064 statep->found = B_TRUE; 1475d62bc4baSyz147064 statep->linkid = linkid; 1476d62bc4baSyz147064 return (DLADM_WALK_TERMINATE); 1477d62bc4baSyz147064 } 1478d62bc4baSyz147064 1479d62bc4baSyz147064 return (DLADM_WALK_CONTINUE); 1480d62bc4baSyz147064 } 1481d62bc4baSyz147064 1482d62bc4baSyz147064 dladm_status_t 14834ac67f02SAnurag S. Maskey dladm_key2linkid(dladm_handle_t handle, uint16_t key, datalink_id_t *linkidp, 14844ac67f02SAnurag S. Maskey uint32_t flags) 1485d62bc4baSyz147064 { 1486d62bc4baSyz147064 i_walk_key_state_t state; 1487d62bc4baSyz147064 1488d62bc4baSyz147064 if (key > AGGR_MAX_KEY) 1489d62bc4baSyz147064 return (DLADM_STATUS_NOTFOUND); 1490d62bc4baSyz147064 1491d62bc4baSyz147064 state.found = B_FALSE; 1492d62bc4baSyz147064 state.key = key; 1493d62bc4baSyz147064 14944ac67f02SAnurag S. Maskey (void) dladm_walk_datalink_id(i_dladm_walk_key2linkid, handle, &state, 1495d62bc4baSyz147064 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags); 1496d62bc4baSyz147064 if (state.found == B_TRUE) { 1497d62bc4baSyz147064 *linkidp = state.linkid; 1498d62bc4baSyz147064 return (DLADM_STATUS_OK); 1499d62bc4baSyz147064 } else { 1500d62bc4baSyz147064 return (DLADM_STATUS_NOTFOUND); 1501d62bc4baSyz147064 } 1502f595a68aSyz147064 } 1503