liblaadm.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 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 <strings.h>
#include <libintl.h>
#include <net/if_types.h>
#include <liblaadm.h>
/*
* Link Aggregation Administration Library.
*
* This library is used by administration tools such as dladm(1M) to
* configure link aggregations.
*
* Link aggregation configuration information is saved in a text
* file of the following format:
*
* <db-file> ::= <groups>*
* <group> ::= <key> <sep> <policy> <sep> <nports> <sep> <ports> <sep>
* <mac> <sep> <lacp-mode> <sep> <lacp-timer>
* <sep> ::= ' ' | '\t'
* <key> ::= <number>
* <nports> ::= <number>
* <ports> ::= <port> <m-port>*
* <m-port> ::= ',' <port>
* <port> ::= <devname> '/' <port-num>
* <devname> ::= <string>
* <port-num> ::= <number>
* <policy> ::= <pol-level> <m-pol>*
* <m-pol> ::= ',' <pol-level>
* <pol-level> ::= 'L2' | 'L3' | 'L4'
* <mac> ::= 'auto' | <mac-addr>
* <mac-addr> ::= <hex> ':' <hex> ':' <hex> ':' <hex> ':' <hex> ':' <hex>
* <lacp-mode> ::= 'off' | 'active' | 'passive'
* <lacp-timer> ::= 'short' | 'long'
*/
#define LAADM_DB "/etc/aggregation.conf"
#define LAADM_DB_TMP "/etc/aggregation.conf.new"
#define LAADM_DB_LOCK "/tmp/aggregation.conf.lock"
#define LAADM_DB_OWNER 0
#define LAADM_DB_GROUP 1
/*
* The largest configurable aggregation key. Because by default the key is
* used as the DLPI device PPA and default VLAN PPA's are calculated as
* ((1000 * vid) + PPA), the largest key can't be > 999.
*/
#define LAADM_MAX_KEY 999
#define MAXLINELEN 1024
/* Limits on buffer size for LAIOC_INFO request */
#define MAXPATHLEN 1024
/* configuration database entry */
typedef struct laadm_grp_attr_db {
typedef struct laadm_up {
int lu_fd;
} laadm_up_t;
typedef struct laadm_down {
} laadm_down_t;
typedef struct laadm_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 laadm_lacp_mode_s {
char *mode_str;
static laadm_lacp_mode_t lacp_modes[] = {
{"off", AGGR_LACP_OFF},
{"active", AGGR_LACP_ACTIVE},
{"passive", AGGR_LACP_PASSIVE}};
typedef struct laadm_lacp_timer_s {
char *lt_str;
static laadm_lacp_timer_t lacp_timers[] = {
{"short", AGGR_LACP_TIMER_SHORT},
{"long", AGGR_LACP_TIMER_LONG}};
typedef struct delete_db_state {
typedef struct modify_db_state {
typedef struct add_db_state {
/*
* Open and lock the aggregation configuration file lock. The lock is
* acquired as a reader (F_RDLCK) or writer (F_WRLCK).
*/
static int
i_laadm_lock_db(short type)
{
int lock_fd;
LAADM_DB_PERMS)) < 0)
return (-1);
(void) unlink(LAADM_DB_LOCK);
return (-1);
}
return (lock_fd);
}
/*
* Unlock and close the specified file.
*/
static void
i_laadm_unlock_db(int fd)
{
if (fd < 0)
return;
(void) unlink(LAADM_DB_LOCK);
}
/*
* Walk through the groups defined on the system and for each group <grp>,
* invoke <fn>(<arg>, <grp>);
* Terminate the walk if at any time <fn> returns non-NULL value
*/
int
{
char *where;
return (-1);
return (-1);
}
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.
*/
for (i = 0; i < ioc->li_ngroups; i++) {
/* LINTED E_BAD_PTR_CAST_ALIGN */
sizeof (laadm_port_attr_sys_t));
goto bail;
}
/*
* Go through each port that is part of the group.
*/
/* LINTED E_BAD_PTR_CAST_ALIGN */
MAXNAMELEN + 1);
}
if (rc != 0)
goto bail;
}
bail:
return (rc);
}
/*
* Parse one line of the link aggregation DB, and return the corresponding
* group. Memory for the ports associated with the aggregation may be
* allocated. It is the responsibility of the caller to free the lt_ports
* aggregation group attribute.
*
* Returns -1 on parsing failure, or 0 on success.
*/
static int
{
char *token;
int i;
int value;
/* key */
goto failed;
errno = 0;
goto failed;
/* policy */
goto failed;
/* number of ports */
return (-1);
errno = 0;
goto failed;
/* ports */
sizeof (laadm_port_attr_db_t))) == NULL)
goto failed;
/* port */
goto failed;
/* device name */
goto failed;
MAXNAMELEN) >= MAXNAMELEN)
goto failed;
/* port number */
errno = 0;
goto failed;
}
/* unicast MAC address */
goto failed;
/* LACP mode */
/* LACP timer */
return (0);
return (-1);
}
/*
* Walk through the groups defined in the DB and for each group <grp>,
* invoke <fn>(<arg>, <grp>);
*/
static int
{
char line[MAXLINELEN];
char *db_file;
char db_file_buf[MAXPATHLEN];
} else {
LAADM_DB);
}
return (-1);
}
/* skip comments */
if (BLANK_LINE(line))
continue;
goto failed;
}
goto failed;
}
retval = 0;
return (retval);
}
/*
* Send an add or remove command to the link aggregation driver.
*/
static int
{
goto failed;
/* LINTED E_BAD_PTR_CAST_ALIGN */
MAXNAMELEN) >= MAXNAMELEN)
goto failed;
}
goto failed;
}
return (rc);
return (-1);
}
/*
* Send a modify command to the link aggregation driver.
*/
static int
{
ioc.lu_modify_mask = 0;
if (mask & LAADM_MODIFY_POLICY)
if (mask & LAADM_MODIFY_MAC)
if (mask & LAADM_MODIFY_LACP_MODE)
if (mask & LAADM_MODIFY_LACP_TIMER)
return (-1);
}
return (rc);
}
/*
* Send a create command to the link aggregation driver.
*/
static int
{
int i, rc;
return (-1);
/* LINTED E_BAD_PTR_CAST_ALIGN */
MAXNAMELEN) >= MAXNAMELEN) {
return (-1);
}
}
if (attr->lt_mac_fixed &&
return (-1);
}
if (rc < 0)
return (rc);
}
/*
* Invoked to bring up a link aggregation group.
*/
static int
{
return (0);
return (-1);
}
return (0);
}
/*
* Bring up a link aggregation group or all of them if the key is zero.
* If key is 0, walk may terminate early if any of the links fail
*/
int
{
return (-1);
return (-1);
}
/*
* only return error if user specified key and key was
* not found
*/
return (-1);
}
return (0);
}
/*
* Send a delete command to the link aggregation driver.
*/
static int
{
}
/*
* Invoked to bring down a link aggregation group.
*/
static int
{
int fd;
return (0);
return (-1);
return (-1);
}
return (0);
}
/*
* Bring down a link aggregation group or all of them if the key is zero.
* If key is 0, walk may terminate early if any of the links fail
*/
int
{
return (-1);
/*
* only return error if user specified key and key was
* not found
*/
return (-1);
}
return (0);
}
/*
* For each group <grp> found in the DB, invokes <fn>(<grp>, <arg>).
*
* The following values can be returned by <fn>():
*
* -1: an error occured. This will cause the walk to be terminated,
* and the original DB file to be preserved.
*
* 0: success and write. The walker will write the contents of
* the attribute passed as argument to <fn>(), and continue walking
* the entries found in the DB.
*
* 1: skip. The walker should not write the contents of the current
* group attributes to the new DB, but should continue walking
* the entries found in the DB.
*/
static int
void *arg,
const char *root,
{
char line[MAXLINELEN];
char *db_file, *tmp_db_file;
char db_file_buf[MAXPATHLEN];
char tmp_db_file_buf[MAXPATHLEN];
} else {
LAADM_DB);
}
return (-1);
return (-1);
}
LAADM_DB_PERMS)) == -1) {
return (-1);
}
(void) unlink(tmp_db_file);
return (-1);
}
/* skip comments */
if (BLANK_LINE(line)) {
goto failed;
continue;
}
goto failed;
}
switch (fn_rc) {
case -1:
/* failure, stop walking */
goto failed;
case 0:
/*
* Success, write group attributes, which could
* have been modified by fn().
*/
goto failed;
break;
case 1:
/* skip current group */
break;
}
}
goto failed;
goto failed;
goto failed;
(void) unlink(tmp_db_file);
return (-1);
}
return (0);
(void) unlink(tmp_db_file);
return (-1);
}
/*
* Remove an entry from the DB.
*/
static int
{
return (0);
/* don't save matching group */
return (1);
}
static int
{
diag) != 0)
return (-1);
return (-1);
}
return (0);
}
/*
* Modify the properties of an existing group in the DB.
*/
static int
{
return (0);
}
}
}
}
}
/* save modified group */
return (0);
}
static int
{
diag) != 0)
return (-1);
return (-1);
}
return (0);
}
/*
* Add ports to an existing group in the DB.
*/
static int
{
void *ports;
int i, j;
return (0);
/* are any of the ports to be added already members of the group? */
return (-1);
}
}
}
/* add groups specified by attr to grp */
return (-1);
MAXNAMELEN + 1)
return (-1);
}
/* save modified group */
return (0);
}
static int
{
diag) != 0)
return (-1);
return (-1);
}
return (0);
}
/*
* Remove ports from an existing group in the DB.
*/
typedef struct remove_db_state {
static int
{
int i, j, k, nremoved;
return (0);
/* remove the ports specified by attr from the group */
nremoved = 0;
k = 0;
}
if (match)
nremoved++;
else
}
return (-1);
}
/* save modified group */
return (0);
}
static int
{
diag) != 0)
return (-1);
return (-1);
}
return (0);
}
/*
* Given a policy string, return a policy mask. Returns B_TRUE on
* success, or B_FALSE if an error occured 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;
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);
}
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);
return (gettext("<unknown>"));
else
}
/*
* 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;
for (i = 0; i < NLACP_MODES; i++) {
mode = &lacp_modes[i];
}
return (NULL);
}
/*
* 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;
for (i = 0; i < NLACP_TIMERS; i++) {
timer = &lacp_timers[i];
}
return (NULL);
}
/*
* Write the attribute of a group to the specified file. Returns 0 on
* success, -1 on failure.
*/
static int
{
int i;
char policy_str[LAADM_POLICY_STR_LEN];
/* key, policy */
/* number of ports, ports */
if (i > 0)
}
/* MAC address */
if (!attr->lt_mac_fixed) {
} else {
}
return (0);
}
static int
{
char line[MAXLINELEN];
char *db_file;
char db_file_buf[MAXPATHLEN];
} else {
LAADM_DB);
}
return (-1);
return (-1);
}
/* look for existing group with same key */
/* skip comments */
if (BLANK_LINE(line))
continue;
/* ignore corrupted lines */
continue;
/* port number */
errno = 0;
goto failed;
}
/* group with key already exists */
goto failed;
}
}
/*
* If we get here, we've verified that no existing group with
* the same key already exists. It's now time to add the
* new group to the DB.
*/
goto failed;
retval = 0;
return (retval);
}
/*
* Create a new link aggregation group. Update the configuration
* file and bring it up.
*/
int
{
int errno_sav;
return (-1);
}
if (attr.lt_mac_fixed)
else
/* add the link aggregation group to the DB */
if (!tempop) {
return (-1);
} else {
}
/* bring up the link aggregation group */
if (!tempop) {
diag);
}
}
return (-1);
}
return (0);
}
/*
* Modify the parameters of an existing link aggregation group. Update
* the configuration file and pass the changes to the kernel.
*/
int
{
int errno_save;
if (key == 0) {
return (-1);
}
if (modify_mask & LAADM_MODIFY_POLICY)
if (modify_mask & LAADM_MODIFY_MAC) {
}
/* update the DB */
if (!tempop) {
return (-1);
}
diag) < 0) {
if (!tempop) {
errno_save = errno;
errno = errno_save;
}
return (-1);
}
return (0);
}
/*
* Delete a previously created link aggregation group.
*/
int
{
if (key == 0) {
return (-1);
}
if (tempop) {
return (-1);
}
if (tempop)
return (0);
}
/*
* Add one or more ports to an existing link aggregation.
*/
int
{
int errno_save;
if (key == 0) {
return (-1);
}
if (!tempop) {
return (-1);
}
if (!tempop) {
errno_save = errno;
errno = errno_save;
}
return (-1);
}
return (0);
}
/*
* Remove one or more ports from an existing link aggregation.
*/
int
{
int errno_save;
if (key == 0) {
return (-1);
}
if (!tempop) {
return (-1);
}
if (!tempop) {
errno_save = errno;
errno = errno_save;
}
return (-1);
}
return (0);
}
const char *
switch (diag) {
return (gettext("configuration repository open failed"));
return (gettext("parsing of configuration repository failed"));
return (gettext("configuration repository close failed"));
return (gettext("invalid interface name"));
return (gettext("invalid MAC address"));
case LAADM_DIAG_INVALID_KEY:
return (gettext("invalid key"));
default:
return (gettext("unknown diagnostic"));
}
}