libdlaggr.c revision 8de9d09562aa1c2ecd5453f23d9eb21db41d9ee7
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stropts.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <strings.h>
#include <libintl.h>
#include <net/if_types.h>
#include <libdllink.h>
#include <libdlvlan.h>
#include <libdlaggr.h>
#include <libdladm_impl.h>
/*
* Link Aggregation Administration Library.
*
* This library is used by administration tools such as dladm(1M) to
* configure link aggregations.
*/
/* Limits on buffer size for LAIOC_INFO request */
#define VALID_PORT_MAC(mac) \
(!(mac)[0] & 0x01))
#define PORT_DELIMITER '.'
(portid), PORT_DELIMITER); \
}
errno = 0; \
(status) = DLADM_STATUS_OK; \
} else { \
/* Skip the delimiter. */ \
(portstr)++; \
} \
}
typedef struct dladm_aggr_modify_attr {
typedef struct policy_s {
char *pol_name;
} policy_t;
{"L2", AGGR_POLICY_L2},
{"L3", AGGR_POLICY_L3},
{"L4", AGGR_POLICY_L4}};
typedef struct dladm_aggr_lacpmode_s {
char *mode_str;
static dladm_aggr_lacpmode_t lacp_modes[] = {
{"off", AGGR_LACP_OFF},
{"active", AGGR_LACP_ACTIVE},
{"passive", AGGR_LACP_PASSIVE}};
typedef struct dladm_aggr_lacptimer_s {
char *lt_str;
static dladm_aggr_lacptimer_t lacp_timers[] = {
{"short", AGGR_LACP_TIMER_SHORT},
{"long", AGGR_LACP_TIMER_LONG}};
typedef struct dladm_aggr_port_state {
char *state_str;
static dladm_aggr_port_state_t port_states[] = {
{"standby", AGGR_PORT_STATE_STANDBY },
{"attached", AGGR_PORT_STATE_ATTACHED }
};
#define NPORT_STATES \
(sizeof (port_states) / sizeof (dladm_aggr_port_state_t))
static int
{
return (-1);
return (err);
}
/*
* Caller must free attr.lg_ports. The ptr pointer is advanced while convert
* the laioc_info_t to the dladm_aggr_grp_attr_t structure.
*/
static int
{
int i;
sizeof (dladm_aggr_port_attr_t))) == NULL) {
goto fail;
}
/*
* Go through each port that is part of the group.
*/
}
return (0);
fail:
return (-1);
}
/*
* Get active configuration of a specific aggregation.
* Caller must free attrp->la_ports.
*/
static dladm_status_t
{
void *where;
return (DLADM_STATUS_NOMEM);
if (rc != 0) {
/*
* The LAIOC_INFO call failed due to a short
* buffer. Reallocate the buffer and try again.
*/
bufsize *= 2;
if (bufsize <= MAX_INFO_SIZE) {
goto tryagain;
}
}
}
goto bail;
}
/*
* Go through each group returned by the aggregation driver.
*/
goto bail;
}
bail:
return (status);
}
/*
* Get configuration information of a specific aggregation.
* Caller must free attrp->la_ports.
*/
static dladm_status_t
{
int size;
return (status);
if (status != DLADM_STATUS_OK)
goto done;
if (status != DLADM_STATUS_OK)
goto done;
sizeof (boolean_t));
if (status != DLADM_STATUS_OK)
goto done;
if (attrp->lg_mac_fixed) {
sizeof (macstr))) != DLADM_STATUS_OK) {
goto done;
}
goto done;
}
}
sizeof (boolean_t));
if (status != DLADM_STATUS_OK)
goto done;
if (status != DLADM_STATUS_OK)
goto done;
if (status != DLADM_STATUS_OK)
goto done;
if (status != DLADM_STATUS_OK)
goto done;
goto done;
}
if (status != DLADM_STATUS_OK) {
goto done;
}
sizeof (dladm_aggr_port_attr_t))) == NULL) {
goto done;
}
if (status != DLADM_STATUS_OK) {
goto done;
}
}
done:
return (status);
}
{
if (flags == DLADM_OPT_ACTIVE)
else
}
/*
*/
static dladm_status_t
{
int size;
if (nports == 0)
return (DLADM_STATUS_BADARG);
/*
* Sanity check - aggregations can only be created over Ethernet
* physical links.
*/
for (i = 0; i < nports; i++) {
return (DLADM_STATUS_BADARG);
}
}
/*
* First, update the persistent configuration if requested. We only
* need to update the FPORTS and FNPORTS fields of this aggregation.
* Note that FPORTS is a list of port linkids separated by
* PORT_DELIMITER ('.').
*/
if (flags & DLADM_OPT_PERSIST) {
if (status != DLADM_STATUS_OK)
return (status);
/*
* Get the original configuration of FNPORTS and FPORTS.
*/
sizeof (u64));
if (status != DLADM_STATUS_OK)
goto destroyconf;
/*
* At least one port needs to be in the aggregation.
*/
goto destroyconf;
}
goto destroyconf;
}
if (status != DLADM_STATUS_OK)
goto destroyconf;
goto destroyconf;
}
/*
* get the new configuration and set to result_nports and
* portstr.
*/
for (i = 0; i < nports; i++)
} else {
char *next;
/*
* Read the portids from the old configuration
* one by one.
*/
if (status != DLADM_STATUS_OK) {
goto destroyconf;
}
/*
* See whether this port is in the removal
* list. If not, copy to the new config.
*/
for (i = 0; i < nports; i++) {
break;
}
if (i == nports) {
} else {
remove++;
}
}
goto destroyconf;
}
result_nports -= nports;
}
u64 = result_nports;
goto destroyconf;
}
portstr);
if (status != DLADM_STATUS_OK)
goto destroyconf;
/*
* Write the new configuration to the persistent repository.
*/
if (status != DLADM_STATUS_OK) {
return (status);
}
}
/*
* If the caller only requested to update the persistent
* configuration, we are done.
*/
if (!(flags & DLADM_OPT_ACTIVE))
goto done;
/*
* Update the active configuration.
*/
goto done;
}
for (i = 0; i < nports; i++)
done:
/*
* If the active configuration update fails, restore the old
* persistent configuration if we've changed that.
*/
u64 = orig_nports;
orig_portstr) == DLADM_STATUS_OK)) {
(void) dladm_write_conf(conf);
}
(void) dladm_destroy_conf(conf);
}
}
return (status);
}
/*
* Send a modify command to the link aggregation driver.
*/
static dladm_status_t
{
ioc.lu_modify_mask = 0;
if (mask & DLADM_AGGR_MODIFY_POLICY)
if (mask & DLADM_AGGR_MODIFY_MAC)
if (mask & DLADM_AGGR_MODIFY_LACP_MODE)
if (mask & DLADM_AGGR_MODIFY_LACP_TIMER)
return (DLADM_STATUS_MACADDRINVAL);
else
return (dladm_errno2status(errno));
} else {
return (DLADM_STATUS_OK);
}
}
/*
* Send a create command to the link aggregation driver.
*/
static dladm_status_t
{
return (DLADM_STATUS_NOMEM);
for (i = 0; i < nports; i++)
goto done;
}
if (rc < 0)
done:
return (status);
}
/*
* Invoked to bring up a link aggregation group.
*/
static int
{
int i, j;
if (status != DLADM_STATUS_OK) {
return (DLADM_WALK_CONTINUE);
}
goto done;
}
/*
* Validate (and purge) each physical link associated with this
* aggregation, if the specific hardware has been removed during
* the system shutdown.
*/
continue;
}
if (j == 0) {
/*
* All of the physical links are removed.
*/
goto done;
}
/*
* Create active aggregation.
*/
goto done;
}
goto done;
}
/*
* Reset the active linkprop of this specific link.
*/
(void) dladm_init_linkprop(linkid);
done:
return (DLADM_WALK_CONTINUE);
}
/*
* Bring up one aggregation, or all persistent aggregations. In the latter
* case, the walk may terminate early if bringup of an aggregation fails.
*/
{
if (linkid == DATALINK_ALL_LINKID) {
return (DLADM_STATUS_OK);
} else {
return (status);
}
}
/*
* Given a policy string, return a policy mask. Returns B_TRUE on
* success, or B_FALSE if an error occurred during parsing.
*/
{
int i;
char *lasts;
*policy = 0;
for (i = 0; i < NPOLICIES; i++) {
break;
}
}
if (i == NPOLICIES)
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Given a policy mask, returns a printable string, or NULL if the
* policy mask is invalid. It is the responsibility of the caller to
* free the returned string after use.
*/
char *
{
int i, npolicies = 0;
return (NULL);
str[0] = '\0';
for (i = 0; i < NPOLICIES; i++) {
npolicies++;
if (npolicies > 1)
}
}
return (str);
}
/*
* Given a MAC address string, return the MAC address in the mac_addr
* array. If the MAC address was not explicitly specified, i.e. is
* equal to 'auto', zero out mac-addr and set mac_fixed to B_TRUE.
* Return B_FALSE if a syntax error was encountered, B_FALSE otherwise.
*/
{
int mac_len;
if (!*mac_fixed) {
return (B_TRUE);
}
return (B_FALSE);
if (mac_len != ETHERADDRL) {
return (B_FALSE);
}
(conv_str[0] & 0x01)) {
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Returns a string containing a printable representation of a MAC address.
*/
const char *
{
static char unknown_mac[] = {0, 0, 0, 0, 0, 0};
return (NULL);
else
return (buf);
}
/*
* Given a LACP mode string, find the corresponding LACP mode number. Returns
* B_TRUE if a match was found, B_FALSE otherwise.
*/
{
int i;
for (i = 0; i < NLACP_MODES; i++) {
mode = &lacp_modes[i];
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* Given a LACP mode number, returns a printable string, or NULL if the
* LACP mode number is invalid.
*/
const char *
{
int i;
return (NULL);
for (i = 0; i < NLACP_MODES; i++) {
mode = &lacp_modes[i];
return (buf);
}
}
return (buf);
}
/*
* Given a LACP timer string, find the corresponding LACP timer number. Returns
* B_TRUE if a match was found, B_FALSE otherwise.
*/
{
int i;
for (i = 0; i < NLACP_TIMERS; i++) {
timer = &lacp_timers[i];
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* Given a LACP timer, returns a printable string, or NULL if the
* LACP timer number is invalid.
*/
const char *
{
int i;
return (NULL);
for (i = 0; i < NLACP_TIMERS; i++) {
timer = &lacp_timers[i];
return (buf);
}
}
return (buf);
}
const char *
{
int i;
return (NULL);
for (i = 0; i < NPORT_STATES; i++) {
state = &port_states[i];
return (buf);
}
}
return (buf);
}
static dladm_status_t
{
int i, size;
return (status);
}
if (status != DLADM_STATUS_OK)
goto done;
if (status != DLADM_STATUS_OK)
goto done;
goto done;
}
for (i = 0; i < nports; i++)
if (status != DLADM_STATUS_OK)
goto done;
if (status != DLADM_STATUS_OK)
goto done;
if (status != DLADM_STATUS_OK)
goto done;
if (mac_addr_fixed) {
if (!VALID_PORT_MAC(mac_addr)) {
goto done;
}
macstr);
if (status != DLADM_STATUS_OK)
goto done;
}
if (status != DLADM_STATUS_OK)
goto done;
if (status != DLADM_STATUS_OK)
goto done;
u64 = lacp_timer;
&u64);
if (status != DLADM_STATUS_OK)
goto done;
/*
* Commit the link aggregation configuration.
*/
done:
return (status);
}
/*
* Create a new link aggregation group. Update the configuration
* file and bring it up.
*/
{
uint32_t i;
return (DLADM_STATUS_KEYINVAL);
if (nports == 0)
return (DLADM_STATUS_BADARG);
for (i = 0; i < nports; i++) {
return (DLADM_STATUS_BADARG);
}
}
goto fail;
}
if ((flags & DLADM_OPT_PERSIST) &&
force)) != DLADM_STATUS_OK) {
goto fail;
}
if (!(flags & DLADM_OPT_ACTIVE))
return (DLADM_STATUS_OK);
if (status != DLADM_STATUS_OK) {
if (flags & DLADM_OPT_PERSIST)
(void) dladm_remove_conf(linkid);
goto fail;
}
return (DLADM_STATUS_OK);
fail:
if (linkid != DATALINK_INVALID_LINKID)
return (status);
}
static dladm_status_t
{
if (mask & DLADM_AGGR_MODIFY_POLICY) {
sizeof (u64));
if (status != DLADM_STATUS_OK)
return (status);
}
if (mask & DLADM_AGGR_MODIFY_MAC) {
if (status != DLADM_STATUS_OK)
return (status);
if (attrp->ld_mac_fixed) {
if (status != DLADM_STATUS_OK)
return (status);
return (DLADM_STATUS_REPOSITORYINVAL);
}
}
}
if (mask & DLADM_AGGR_MODIFY_LACP_MODE) {
sizeof (u64));
if (status != DLADM_STATUS_OK)
return (status);
}
if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) {
sizeof (u64));
if (status != DLADM_STATUS_OK)
return (status);
}
return (status);
}
static dladm_status_t
{
if (mask & DLADM_AGGR_MODIFY_POLICY) {
&u64);
if (status != DLADM_STATUS_OK)
return (status);
}
if (mask & DLADM_AGGR_MODIFY_MAC) {
if (status != DLADM_STATUS_OK)
return (status);
if (attrp->ld_mac_fixed) {
if (status != DLADM_STATUS_OK)
return (status);
}
}
if (mask & DLADM_AGGR_MODIFY_LACP_MODE) {
DLADM_TYPE_UINT64, &u64);
if (status != DLADM_STATUS_OK)
return (status);
}
if (mask & DLADM_AGGR_MODIFY_LACP_TIMER) {
DLADM_TYPE_UINT64, &u64);
if (status != DLADM_STATUS_OK)
return (status);
}
return (status);
}
/*
* Modify the parameters of an existing link aggregation group. Update
* the configuration file and pass the changes to the kernel.
*/
{
if (flags & DLADM_OPT_PERSIST) {
if (status != DLADM_STATUS_OK)
return (status);
&old_attr)) != DLADM_STATUS_OK) {
goto done;
}
&new_attr)) != DLADM_STATUS_OK) {
goto done;
}
done:
if (status != DLADM_STATUS_OK)
return (status);
}
if (!(flags & DLADM_OPT_ACTIVE))
return (DLADM_STATUS_OK);
&old_attr) == DLADM_STATUS_OK) {
(void) dladm_write_conf(conf);
}
}
}
return (status);
}
typedef struct aggr_held_arg_s {
static int
{
return (DLADM_WALK_CONTINUE);
/*
* This VLAN is created over the given aggregation.
*/
return (DLADM_WALK_TERMINATE);
}
return (DLADM_WALK_CONTINUE);
}
/*
* Delete a previously created link aggregation group. Either the name "aggr"
* or the "key" is specified.
*/
{
return (DLADM_STATUS_BADARG);
}
if (flags & DLADM_OPT_ACTIVE) {
sizeof (ioc)) < 0) &&
return (status);
}
/*
* Delete ACTIVE linkprop first.
*/
}
/*
* If we reach here, it means that the active aggregation has already
* been deleted, and there is no active VLANs holding this aggregation.
* Now we see whether there is any persistent VLANs holding this
* aggregation. If so, we fail the operation.
*/
if (flags & DLADM_OPT_PERSIST) {
return (DLADM_STATUS_LINKBUSY);
(void) dladm_remove_conf(linkid);
}
return (DLADM_STATUS_OK);
}
/*
* Add one or more ports to an existing link aggregation.
*/
{
}
/*
* Remove one or more ports from an existing link aggregation.
*/
{
LAIOC_REMOVE));
}
typedef struct i_walk_key_state_s {
static int
{
return (DLADM_WALK_CONTINUE);
return (DLADM_WALK_TERMINATE);
}
return (DLADM_WALK_CONTINUE);
}
{
if (key > AGGR_MAX_KEY)
return (DLADM_STATUS_NOTFOUND);
return (DLADM_STATUS_OK);
} else {
return (DLADM_STATUS_NOTFOUND);
}
}