dladm.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 <locale.h>
#include <stdarg.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <stropts.h>
#include <errno.h>
#include <kstat.h>
#include <strings.h>
#include <getopt.h>
#include <unistd.h>
#include <libintl.h>
#include <libdlpi.h>
#include <libdladm.h>
#include <liblaadm.h>
#include <libmacadm.h>
#define AGGR_DRIVER "aggr"
#define AGGR_DEV "aggr0"
#define MAXPORT 256
#define DUMP_LACP_FORMAT " %-9s %-8s %-7s %-12s " \
"%-5s %-4s %-4s %-9s %-7s\n"
typedef struct pktsum_s {
} pktsum_t;
typedef struct show_link_state {
typedef struct show_grp_state {
typedef struct show_mac_state {
typedef struct port_state {
char *state_name;
} port_state_t;
static port_state_t port_states[] = {
{"standby", AGGR_PORT_STATE_STANDBY },
{"attached", AGGR_PORT_STATE_ATTACHED }
};
static void do_create_vlan(int, char **);
static void do_delete_vlan(int, char **);
static void do_show_link(int, char **);
static void do_up_link(int, char **);
static void do_init_link(int, char **);
static void do_create_aggr(int, char **);
static void do_delete_aggr(int, char **);
static void do_add_aggr(int, char **);
static void do_remove_aggr(int, char **);
static void do_modify_aggr(int, char **);
static void do_show_aggr(int, char **);
static void do_up_aggr(int, char **);
static void do_down_aggr(int, char **);
static void do_show_dev(int, char **);
static void link_stats(const char *, uint32_t);
static void get_link_stats(const char *, pktsum_t *);
static char *mac_link_state(const char *, uint_t);
static char *mac_link_duplex(const char *, uint_t);
typedef struct cmd {
char *c_name;
void (*c_fn)(int, char **);
} cmd_t;
{ "create-vlan", do_create_vlan },
{ "delete-vlan", do_delete_vlan },
{ "show-link", do_show_link },
{ "up-link", do_up_link },
{ "init-link", do_init_link },
{ "create-aggr", do_create_aggr },
{ "delete-aggr", do_delete_aggr },
{ "add-aggr", do_add_aggr },
{ "remove-aggr", do_remove_aggr },
{ "modify-aggr", do_modify_aggr },
{ "show-aggr", do_show_aggr },
{ "up-aggr", do_up_aggr },
{ "down-aggr", do_down_aggr },
{ "show-dev", do_show_dev }
};
{ 0, 0, 0, 0 }
};
static char *progname;
if (diag != 0) \
}
static void
usage(void)
{
"usage: dladm create-vlan [-t] [-R <root-dir>] -v <vlan> -d <dev>\n"
" delete-vlan [-t] [-R <root-dir>] -v <vlan> -d <dev>\n"
" create-aggr [-t] [-R <root-dir>] [-P <policy>]\n"
" [-l <mode>] [-T <time>]\n"
" [-u <address>] -d <dev> ... <key>\n"
" delete-aggr [-t] [-R <root-dir>] <key>\n"
" add-aggr [-t] [-R <root-dir>] -d <dev> ... <key>\n"
" remove-aggr [-t] [-R <root-dir>] -d <dev> ... <key>\n"
" modify-aggr [-t] [-R <root-dir>] [-P <policy>]\n"
" [-l <mode>] [-T <time>] [-u <address>] <key>\n"
" show-aggr [-L] [-s] [-i <interval>] [-p] [<key>]\n"
" show-dev [-s] [-i <interval>] [-p] [<dev>]\n"
" show-link [-s] [-i <interval>] [-p] [<name>]\n"));
exit(1);
}
int
{
int i;
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
if (argc < 2)
usage();
exit(0);
}
}
usage();
return (0);
}
static void
{
char name[MAXNAMELEN];
char driver[MAXNAMELEN];
int instance;
int value;
char option;
dladm_diag_t diag = 0;
opterr = 0;
NULL)) != -1) {
switch (option) {
case 'v':
if (v_arg) {
"-v cannot be specified more than once\n"),
progname);
usage();
}
errno = 0;
*endp != '\0') {
gettext("%s: illegal VLAN identifier"
exit(1);
}
break;
case 'd':
if (d_arg) {
"%s: the option -d cannot be specified "
"more than once\n"), progname);
usage();
}
MAXNAMELEN) {
gettext("%s: device name too long\n"),
progname);
exit(1);
}
break;
case 't':
break;
case 'R':
break;
case ':':
gettext("%s: option requires a value '-%c'\n"),
exit(1);
break;
case '?':
default:
gettext("%s: unrecognized option '-%c'\n"),
exit(1);
break;
}
}
usage();
}
gettext("%s: no device specified\n"),
progname);
exit(1);
}
gettext("%s: no VLAN ID specified\n"),
progname);
exit(1);
}
instance < 0) {
gettext("%s: badly formatted device '%s'\n"),
exit(1);
}
/*
* For aggregations, the da_dev is always AGGR_DEV, and the
* aggregation key (the instance integer extracted above by
* dlpi_if_parse) is da_port.
*/
}
exit(1);
}
if (dladm_sync() < 0) {
gettext("%s: sync operation failed"),
progname);
perror(" ");
exit(1);
}
}
static void
{
char option;
char device[MAXNAMELEN];
char name[MAXNAMELEN];
char driver[MAXNAMELEN];
int instance;
int vid;
dladm_diag_t diag = 0;
opterr = 0;
NULL)) != -1) {
switch (option) {
case 'v':
if (v_arg) {
"-v cannot be specified more than once\n"),
progname);
usage();
}
errno = 0;
*endp != '\0') {
gettext("%s: illegal VLAN identifier"
exit(1);
}
break;
case 'd':
if (d_arg) {
"%s: the option -d cannot be specified "
"more than once\n"), progname);
usage();
}
MAXNAMELEN) {
gettext("%s: device name too long\n"),
progname);
exit(1);
}
break;
case 't':
break;
case 'R':
break;
case ':':
gettext("%s: option requires a value '-%c'\n"),
exit(1);
break;
case '?':
default:
gettext("%s: unrecognized option '-%c'\n"),
exit(1);
break;
}
}
usage();
gettext("%s: no device specified\n"),
progname);
exit(1);
}
gettext("%s: no VLAN ID specified\n"),
progname);
exit(1);
}
instance < 0) {
gettext("%s: badly formatted device '%s'\n"),
exit(1);
}
exit(1);
}
if (dladm_sync() < 0) {
gettext("%s: sync operation failed"),
progname);
perror(" ");
exit(1);
}
}
static void
{
char option;
laadm_diag_t diag = 0;
opterr = 0;
switch (option) {
case 'd':
gettext("%s: too many <dev> arguments\n"),
progname);
exit(1);
}
MAXNAMELEN) >= MAXNAMELEN) {
gettext("%s: device name too long\n"),
progname);
exit(1);
}
nport++;
break;
case 'P':
if (P_arg) {
"%s: the option -P cannot be specified "
"more than once\n"), progname);
usage();
}
gettext("%s: invalid policy '%s'\n"),
exit(1);
}
break;
case 'u':
if (u_arg) {
"%s: the option -u cannot be specified "
"more than once\n"), progname);
usage();
}
mac_addr)) {
gettext("%s: invalid MAC address '%s'\n"),
exit(1);
}
break;
case 'l':
if (l_arg) {
"%s: the option -l cannot be specified "
"more than once\n"), progname);
usage();
}
gettext("%s: invalid LACP mode '%s'\n"),
exit(1);
}
break;
case 'T':
if (T_arg) {
"%s: the option -T cannot be specified "
"more than once\n"), progname);
usage();
}
gettext("%s: invalid LACP timer value"
" '%s'\n"),
exit(1);
}
break;
case 't':
break;
case 'R':
break;
case ':':
gettext("%s: option requires a value '-%c'\n"),
exit(1);
/*NOTREACHED*/
case '?':
default:
gettext("%s: unrecognized option '-%c'\n"),
exit(1);
}
}
if (nport == 0)
usage();
/* get key value (required last argument) */
usage();
errno = 0;
gettext("%s: illegal key value '%d'\n"),
exit(1);
}
exit(1);
}
}
static void
{
char option;
laadm_diag_t diag = 0;
opterr = 0;
NULL)) != -1) {
switch (option) {
case 't':
break;
case 'R':
break;
case ':':
gettext("%s: option requires a value '-%c'\n"),
exit(1);
break;
case '?':
default:
gettext("%s: unrecognized option '-%c'\n"),
exit(1);
break;
}
}
/* get key value (required last argument) */
usage();
errno = 0;
gettext("%s: illegal key value '%d'\n"),
exit(1);
}
exit(1);
}
}
static void
{
char option;
laadm_diag_t diag = 0;
opterr = 0;
NULL)) != -1) {
switch (option) {
case 'd':
gettext("%s: too many <dev> arguments\n"),
progname);
exit(1);
}
MAXNAMELEN) >= MAXNAMELEN) {
gettext("%s: device name too long\n"),
progname);
exit(1);
}
nport++;
break;
case 't':
break;
case 'R':
break;
case ':':
gettext("%s: option requires a value '-%c'\n"),
exit(1);
/*NOTREACHED*/
case '?':
default:
gettext("%s: unrecognized option '-%c'\n"),
exit(1);
}
}
if (nport == 0)
usage();
/* get key value (required last argument) */
usage();
errno = 0;
gettext("%s: illegal key value '%d'\n"),
exit(1);
}
exit(1);
}
}
static void
{
char option;
laadm_diag_t diag = 0;
opterr = 0;
switch (option) {
case 'd':
gettext("%s: too many <dev> arguments\n"),
progname);
exit(1);
}
MAXNAMELEN) >= MAXNAMELEN) {
gettext("%s: device name too long\n"),
progname);
exit(1);
}
nport++;
break;
case 't':
break;
case 'R':
break;
case ':':
gettext("%s: option requires a value '-%c'\n"),
exit(1);
/*NOTREACHED*/
case '?':
default:
gettext("%s: unrecognized option '-%c'\n"),
exit(1);
}
}
if (nport == 0)
usage();
/* get key value (required last argument) */
usage();
errno = 0;
gettext("%s: illegal key value '%d'\n"),
exit(1);
}
exit(1);
}
}
static void
{
char option;
uint8_t modify_mask = 0;
laadm_diag_t diag = 0;
opterr = 0;
NULL)) != -1) {
switch (option) {
case 'P':
if (modify_mask & LAADM_MODIFY_POLICY) {
"%s: the option -P cannot be specified "
"more than once\n"), progname);
usage();
}
gettext("%s: invalid policy '%s'\n"),
exit(1);
}
break;
case 'u':
if (modify_mask & LAADM_MODIFY_MAC) {
"%s: the option -u cannot be specified "
"more than once\n"), progname);
usage();
}
mac_addr)) {
gettext("%s: invalid MAC address '%s'\n"),
exit(1);
}
break;
case 'l':
if (modify_mask & LAADM_MODIFY_LACP_MODE) {
"%s: the option -l cannot be specified "
"more than once\n"), progname);
usage();
}
gettext("%s: invalid LACP mode '%s'\n"),
exit(1);
}
break;
case 'T':
if (modify_mask & LAADM_MODIFY_LACP_TIMER) {
"%s: the option -T cannot be specified "
"more than once\n"), progname);
usage();
}
gettext("%s: invalid LACP timer value"
" '%s'\n"),
exit(1);
}
break;
case 't':
break;
case 'R':
break;
case ':':
gettext("%s: option requires a value '-%c'\n"),
exit(1);
/*NOTREACHED*/
case '?':
default:
gettext("%s: unrecognized option '-%c'\n"),
exit(1);
}
}
if (modify_mask == 0) {
"-PulT options must be specified\n"), progname);
usage();
}
/* get key value (required last argument) */
usage();
errno = 0;
gettext("%s: illegal key value '%d'\n"),
exit(1);
}
exit(1);
}
}
static void
{
dladm_diag_t diag = 0;
/* get link name (optional last argument) */
if (argc == 2)
else if (argc > 2)
usage();
gettext("%s: could not bring up link '%s' : %s"),
if (diag != 0)
dladm_diag(diag));
} else {
PRINT_ERR_DIAG("%s: could not bring links up: %s",
diag, dladm_diag);
}
exit(1);
}
}
static void
{
laadm_diag_t diag = 0;
/* get aggregation key (optional last argument) */
if (argc == 2) {
errno = 0;
gettext("%s: illegal key value '%d'\n"),
exit(1);
}
} else if (argc > 2) {
usage();
}
if (key != 0) {
gettext("%s: could not bring up aggregation"
if (diag != 0)
laadm_diag(diag));
} else {
"%s: could not bring aggregations up: %s",
diag, laadm_diag);
}
exit(1);
}
}
static void
{
/* get aggregation key (optional last argument) */
if (argc == 2) {
errno = 0;
gettext("%s: illegal key value '%d'\n"),
exit(1);
}
} else if (argc > 2) {
usage();
}
if (laadm_down(key) < 0) {
if (key != 0) {
gettext("%s: could not bring aggregation"
" down '%u' : %s"),
} else {
gettext("%s: could not bring aggregations"
}
exit(1);
}
}
static void
{
int flags;
if (port != 0)
return;
gettext("%s: device name too long\n"),
progname);
exit(1);
}
flags |= DLADM_LINK_TEMP;
}
/* ARGSUSED */
static void
{
usage();
}
#define TYPE_WIDTH 10
#define MAC_WIDTH 23
#define MTU_WIDTH 11
#define STATE_WIDTH 15
/* ARGSUSED */
static void
{
char type[TYPE_WIDTH];
int fd;
(void) dlpi_close(fd);
} else {
gettext("%s: invalid device '%s'\n"),
exit(1);
}
if (!state->ls_parseable) {
"\tmtu: %d\tdevice: %s\n"),
} else {
(void) printf("%s type=legacy mtu=%d device=%s\n",
}
} else {
} else {
gettext("non-vlan"));
}
if (!state->ls_parseable) {
"\tmtu: %d\taggregation: key %u\n"),
} else {
(void) printf("%s type=%s mtu=%d key=%u\n",
}
} else {
if (!state->ls_parseable) {
"\tmtu: %d\tdevice: %s\n"),
} else {
(void) printf("%s type=%s mtu=%d device=%s\n",
}
}
}
}
static void
{
if (state->ls_firstonly) {
if (state->ls_donefirst)
return;
} else {
}
}
static void
{
char policy_str[LAADM_POLICY_STR_LEN];
if (!parseable) {
} else {
(void) printf(" policy=%s",
(void) printf(" address=%s",
(void) printf(" address-type=%s\n",
}
}
static void
{
const char *lacp_timer_str =
if (!parseable) {
} else {
}
}
static void
{
(void) printf("\tipackets rbytes opackets obytes ");
(void) printf("%%ipkts %%opkts\n");
}
static void
dump_ports_lacp_head(void)
{
gettext("expired"));
}
static void
dump_ports_head(void)
{
"state\n"));
}
static char *
{
int i;
for (i = 0; i < NPORTSTATES; i++) {
state = &port_states[i];
return (state->state_name);
}
return ("unknown");
}
static void
{
if (!parseable) {
1000000ull));
} else {
1000000ull));
}
}
static void
{
(void) printf(DUMP_LACP_FORMAT,
}
static void
{
(void) printf("\t-");
else
(void) printf("\t-");
else
(void) printf("\n");
*old_stats = *port_stats;
}
static int
{
int i;
return (0);
if (state->gs_firstonly) {
return (0);
} else {
}
/* show statistics */
/* sum the ports statistics */
&state->gs_prevstats[i]);
}
(void) printf(" Total");
}
/* show LACP info */
} else {
if (!state->gs_parseable)
if (state->gs_parseable)
if (state->gs_parseable)
(void) printf("\n");
}
}
return (0);
}
static int
{
return (-1);
return (-1);
switch (type) {
case KSTAT_DATA_UINT64:
break;
case KSTAT_DATA_UINT32:
break;
default:
return (-1);
}
return (0);
}
static void
{
/* aggregations are already managed by a set of subcommands */
return;
if (port != 0)
if (!state->ms_parseable) {
} else {
(void) printf(" speed=%u",
}
}
/*ARGSUSED*/
static void
{
/* aggregations are already managed by a set of subcommands */
return;
if (state->ms_firstonly) {
if (state->ms_donefirst)
return;
} else {
}
if (port != 0)
}
static void
{
int option;
opterr = 0;
switch (option) {
case 'p':
break;
case 's':
if (s_arg) {
"%s: the option -s cannot be specified "
"more than once\n"), progname);
usage();
}
break;
case 'i':
if (i_arg) {
"%s: the option -i cannot be specified "
"more than once\n"), progname);
usage();
}
errno = 0;
gettext("%s: invalid interval value"
" '%d'\n"),
exit(1);
}
break;
case ':':
gettext("%s: option requires a value '-%c'\n"),
exit(1);
/*NOTREACHED*/
case '?':
default:
gettext("%s: unrecognized option '-%c'\n"),
exit(1);
}
}
"can be used only with -s\n"), progname);
usage();
}
/* get link name (optional last argument) */
usage();
if (s_arg) {
return;
}
else
}
static void
{
int option;
opterr = 0;
switch (option) {
case 'L':
if (L_arg) {
"%s: the option -L cannot be specified "
"more than once\n"), progname);
usage();
}
"%s: the option -L cannot be used with "
"any of -is\n"), progname);
usage();
}
break;
case 'p':
break;
case 's':
if (s_arg) {
"%s: the option -s cannot be specified "
"more than once\n"), progname);
usage();
}
if (L_arg) {
"%s: the option -L cannot be used "
"with -k\n"), progname);
usage();
}
break;
case 'i':
if (i_arg) {
"%s: the option -i cannot be specified "
"more than once\n"), progname);
usage();
}
if (L_arg) {
"%s: the option -i cannot be used "
"with -L\n"), progname);
usage();
}
errno = 0;
gettext("%s: invalid interval value"
" '%d'\n"),
exit(1);
}
break;
case ':':
gettext("%s: option requires a value '-%c'\n"),
exit(1);
/*NOTREACHED*/
case '?':
default:
gettext("%s: unrecognized option '-%c'\n"),
exit(1);
}
}
"can be used only with -s\n"), progname);
usage();
}
/* get aggregation key (optional last argument) */
errno = 0;
gettext("%s: illegal key value '%d'\n"),
exit(1);
}
usage();
}
if (s_arg) {
return;
}
gettext("%s: non-existent aggregation key '%u'\n"),
exit(1);
}
}
static void
{
int option;
opterr = 0;
switch (option) {
case 'p':
break;
case 's':
if (s_arg) {
"%s: the option -s cannot be specified "
"more than once\n"), progname);
usage();
}
break;
case 'i':
if (i_arg) {
"%s: the option -i cannot be specified "
"more than once\n"), progname);
usage();
}
errno = 0;
gettext("%s: invalid interval value"
" '%d'\n"),
exit(1);
}
break;
case ':':
gettext("%s: option requires a value '-%c'\n"),
exit(1);
/*NOTREACHED*/
case '?':
default:
gettext("%s: unrecognized option '-%c'\n"),
exit(1);
}
}
"can be used only with -s\n"), progname);
usage();
}
/* get dev name (optional last argument) */
usage();
/* aggregations are already managed by a set of subcommands */
gettext("%s: non-existant device '%s'\n"),
exit(1);
}
if (s_arg) {
return;
}
else
}
/* ARGSUSED */
static void
{
int fd;
(void) dlpi_close(fd);
} else {
gettext("%s: invalid device '%s'\n"),
exit(1);
}
}
/*
* If an interval is specified, continuously show the stats
* only for the first MAC port.
*/
for (;;) {
(void) printf("\t\tipackets rbytes ierrors ");
(void) printf("opackets obytes oerrors\n");
else
if (interval == 0)
break;
}
}
/* ARGSUSED */
static void
{
/*
* If an interval is specified, continuously show the stats
* only for the first group.
*/
for (;;) {
gettext("%s: non-existent aggregation key '%u'\n"),
exit(1);
}
if (interval == 0)
break;
}
}
/* ARGSUSED */
static void
{
/*
* If an interval is specified, continuously show the stats
* only for the first MAC port.
*/
for (;;) {
(void) printf("\t\tipackets rbytes ierrors ");
(void) printf("opackets obytes oerrors\n");
} else {
}
if (interval == 0)
break;
}
}
/* accumulate stats (s1 += (s2 - s3)) */
static void
{
}
/* compute stats differences (s1 = s2 - s3) */
static void
{
}
static void
{
gettext("%s: kstat open operation failed\n"),
progname);
return;
}
/*
* The kstat query could fail if the underlying MAC
* driver was already detached.
*/
(void) kstat_close(kcp);
return;
}
goto bail;
goto bail;
goto bail;
goto bail;
goto bail;
goto bail;
goto bail;
(void) kstat_close(kcp);
return;
bail:
gettext("%s: kstat operation failed\n"),
progname);
(void) kstat_close(kcp);
}
static void
{
char name[MAXNAMELEN];
}
static void
{
}
static uint64_t
{
char name[MAXNAMELEN];
gettext("%s: kstat open operation failed\n"),
progname);
return (0);
}
/*
* The kstat query could fail if the underlying MAC
* driver was already detached.
*/
goto bail;
}
gettext("%s: kstat read failed\n"),
progname);
goto bail;
}
gettext("%s: kstat value failed\n"),
progname);
goto bail;
}
bail:
(void) kstat_close(kcp);
return (ifspeed);
}
static char *
{
char name[MAXNAMELEN];
char *state_str = "unknown";
gettext("%s: kstat open operation failed\n"),
progname);
return (state_str);
}
/*
* The kstat query could fail if the underlying MAC
* driver was already detached.
*/
goto bail;
}
gettext("%s: kstat read failed\n"),
progname);
goto bail;
}
&link_state) < 0) {
goto bail;
}
switch (link_state) {
case LINK_STATE_UP:
state_str = "up";
break;
case LINK_STATE_DOWN:
state_str = "down";
break;
default:
break;
}
bail:
(void) kstat_close(kcp);
return (state_str);
}
static char *
{
char name[MAXNAMELEN];
char *duplex_str = "unknown";
gettext("%s: kstat open operation failed\n"),
progname);
return (duplex_str);
}
/*
* The kstat query could fail if the underlying MAC
* driver was already detached.
*/
goto bail;
}
gettext("%s: kstat read failed\n"),
progname);
goto bail;
}
&link_duplex) < 0) {
goto bail;
}
switch (link_duplex) {
case LINK_DUPLEX_FULL:
duplex_str = "full";
break;
case LINK_DUPLEX_HALF:
duplex_str = "half";
break;
default:
break;
}
bail:
(void) kstat_close(kcp);
return (duplex_str);
}