dladm.c revision bcb5c89da22515e2ccf139578bad3caebcd716ad
/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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.
*/
#include <stdio.h>
#include <ctype.h>
#include <locale.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <stropts.h>
#include <sys/stat.h>
#include <errno.h>
#include <kstat.h>
#include <strings.h>
#include <getopt.h>
#include <unistd.h>
#include <priv.h>
#include <termios.h>
#include <pwd.h>
#include <auth_attr.h>
#include <auth_list.h>
#include <libintl.h>
#include <libdevinfo.h>
#include <libdlpi.h>
#include <libdllink.h>
#include <libdlaggr.h>
#include <libdlwlan.h>
#include <libdlvlan.h>
#include <libdlvnic.h>
#include <libinetutil.h>
#include <bsm/adt.h>
#include <bsm/adt_event.h>
#include <stddef.h>
#define AGGR_DRV "aggr"
#define STR_UNDEF_VAL "--"
#define MAXPORT 256
#define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
#define MAXLINELEN 1024
#define SMF_UPGRADE_FILE "/var/svc/profile/upgrade"
#define SMF_UPGRADEDATALINK_FILE "/var/svc/profile/upgrade_datalink"
#define SMF_DLADM_UPGRADE_MSG " # added by dladm(1M)"
#define CMD_TYPE_ANY 0xffffffff
#define WIFI_CMD_SCAN 0x00000001
#define WIFI_CMD_SHOW 0x00000002
#define WIFI_CMD_ALL (WIFI_CMD_SCAN | WIFI_CMD_SHOW)
/*
* Data structures and routines for printing output.
* All non-parseable output is assumed to be in a columnar format.
* Multiple fields in parsable output are separated by ':'; single
* field output is printed as-is.
*
* Each sub-command is associated with a global array of pointers,
* print_field_t *fields[], where the print_field_t contains information
* about the format in which the output is to be printed.
*
* Sub-commands may be implemented in one of two ways:
* (i) the implementation could get all field values into a character
* buffer, with pf_offset containing the offset (for pf_name) within
* the buffer. The sub-command would make the needed system calls
* to obtain all possible column values and then invoke the
* dladm_print_field() function to print the specific fields
* requested in the command line. See the comments for dladm_print_field
* for further details.
* (ii) Alternatively, each fields[i] entry could store a pf_index value
* that uniquely identifies the column to be printed. The implementation
* of the sub-command would then invoke dladm_print_output() with a
* callback function whose semantics are described below (see comments
* for dladm_print_output())
*
* Thus, an implementation of a sub-command must provide the following:
*
* static print_field_t sub_command_fields[] = {
* {<name>, <header>,<field width>, <offset_or_index>, cmdtype},
* :
* {<name>, <header>,<field width>, <offset_or_index>, cmdtype}
* };
*
* #define SUB_COMMAND_MAX_FIELDS sizeof \
* (sub_comand_fields) / sizeof (print_field_t))
*
* print_state_t sub_command_print_state;
*
* The function that parses command line arguments (typically
* do_sub_command()) should then contain an invocation like:
*
* fields = parse_output_fields(fields_str, sub_command_fields,
* SUB_COMMAND_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
*
* and store the resulting fields and nfields value in a print_state_t
* structure tracked for the command.
*
* sub_command_print_state.ps_fields = fields;
* sub_command_print_state.ps_nfields = nfields;
*
* To print the column header for the output, the print_header()
* function must then be invoked by do_sub_command().
*
* Then if method (i) is used for the sub_command, the do_sub_command()
* function should make the necessary system calls to fill up the buffer
* and then invoke dladm_print_field(). An example of this method is
* the implementation of do_show_link() and show_link();
*
* If method (ii) is used, do_sub_command should invoke dladm_print_output()
* with a callback function that will be called for each field to be printed.
* The callback function will be passed a pointer to the print_field_t
* for the field, and the pf_index may then be used to identify the
* system call required to find the value to be printed. An example of
* this implementation may be found in the do_show_dev() and print_dev()
* invocation.
*/
typedef struct print_field_s {
const char *pf_name; /* name of column to be printed */
const char *pf_header; /* header for this column */
uint_t pf_width;
union {
uint_t _pf_index; /* private index for sub-command */
size_t _pf_offset;
}_pf_un;
#define pf_index _pf_un._pf_index
#define pf_offset _pf_un._pf_offset;
uint_t pf_cmdtype;
} print_field_t;
/*
* The state of the output is tracked in a print_state_t structure.
* Each ps_fields[i] entry points at the global print_field_t array for
* the sub-command, where ps_nfields is the number of requested fields.
*/
typedef struct print_state_s {
print_field_t **ps_fields;
uint_t ps_nfields;
boolean_t ps_lastfield;
uint_t ps_overflow;
} print_state_t;
typedef char *(*print_callback_t)(print_field_t *, void *);
static print_field_t **parse_output_fields(char *, print_field_t *, int,
uint_t, uint_t *);
/*
* print the header for the output
*/
static void print_header(print_state_t *);
static void print_field(print_state_t *, print_field_t *, const char *,
boolean_t);
/*
* to print output values, call dladm_print_output with a callback
* function (*func)() that should parse the args and return an
* unformatted character buffer with the value to be printed.
*
* dladm_print_output() prints the character buffer using the formatting
* information provided in the print_field_t for that column.
*/
static void dladm_print_output(print_state_t *, boolean_t,
print_callback_t, void *);
/*
* helper function that, when invoked as dladm_print_field(pf, buf)
* prints string which is offset by pf->pf_offset within buf
*/
static char *dladm_print_field(print_field_t *, void *);
#define MAX_FIELD_LEN 32
typedef struct pktsum_s {
uint64_t ipackets;
uint64_t opackets;
uint64_t rbytes;
uint64_t obytes;
uint32_t ierrors;
uint32_t oerrors;
} pktsum_t;
typedef struct show_state {
boolean_t ls_firstonly;
boolean_t ls_donefirst;
pktsum_t ls_prevstats;
uint32_t ls_flags;
dladm_status_t ls_status;
print_state_t ls_print;
boolean_t ls_parseable;
boolean_t ls_printheader;
} show_state_t;
typedef struct show_grp_state {
pktsum_t gs_prevstats[MAXPORT];
uint32_t gs_flags;
dladm_status_t gs_status;
boolean_t gs_parseable;
boolean_t gs_lacp;
boolean_t gs_extended;
boolean_t gs_stats;
boolean_t gs_firstonly;
boolean_t gs_donefirst;
boolean_t gs_printheader;
print_state_t gs_print;
} show_grp_state_t;
typedef void cmdfunc_t(int, char **, const char *);
static cmdfunc_t do_show_link, do_show_dev, do_show_wifi, do_show_phys;
static cmdfunc_t do_create_aggr, do_delete_aggr, do_add_aggr, do_remove_aggr;
static cmdfunc_t do_modify_aggr, do_show_aggr, do_up_aggr;
static cmdfunc_t do_scan_wifi, do_connect_wifi, do_disconnect_wifi;
static cmdfunc_t do_show_linkprop, do_set_linkprop, do_reset_linkprop;
static cmdfunc_t do_create_secobj, do_delete_secobj, do_show_secobj;
static cmdfunc_t do_init_linkprop, do_init_secobj;
static cmdfunc_t do_create_vlan, do_delete_vlan, do_up_vlan, do_show_vlan;
static cmdfunc_t do_rename_link, do_delete_phys, do_init_phys;
static cmdfunc_t do_show_linkmap;
static cmdfunc_t do_show_ether;
static void altroot_cmd(char *, int, char **);
static int show_linkprop_onelink(datalink_id_t, void *);
static void link_stats(datalink_id_t, uint_t, char *, show_state_t *);
static void aggr_stats(datalink_id_t, show_grp_state_t *, uint_t);
static void dev_stats(const char *dev, uint32_t, char *, show_state_t *);
static int get_one_kstat(const char *, const char *, uint8_t,
void *, boolean_t);
static void get_mac_stats(const char *, pktsum_t *);
static void get_link_stats(const char *, pktsum_t *);
static uint64_t get_ifspeed(const char *, boolean_t);
static void stats_total(pktsum_t *, pktsum_t *, pktsum_t *);
static void stats_diff(pktsum_t *, pktsum_t *, pktsum_t *);
static const char *get_linkstate(const char *, boolean_t, char *);
static const char *get_linkduplex(const char *, boolean_t, char *);
static int show_etherprop(datalink_id_t, void *);
static void show_ether_xprop(datalink_id_t, void *);
static boolean_t get_speed_duplex(datalink_id_t, const char *, char *,
char *, boolean_t);
static char *pause_str(int, int);
static boolean_t link_is_ether(const char *, datalink_id_t *);
#define IS_FDX 0x10
#define IS_HDX 0x01
static boolean_t str2int(const char *, int *);
static void die(const char *, ...);
static void die_optdup(int);
static void die_opterr(int, int, const char *);
static void die_dlerr(dladm_status_t, const char *, ...);
static void warn(const char *, ...);
static void warn_dlerr(dladm_status_t, const char *, ...);
typedef struct cmd {
char *c_name;
cmdfunc_t *c_fn;
const char *c_usage;
} cmd_t;
static cmd_t cmds[] = {
{ "show-link", do_show_link,
"\tshow-link\t[-pP] [-o <field>,..] [-s [-i <interval>]] [<link>]"},
{ "rename-link", do_rename_link,
"\trename-link\t[-R <root-dir>] <oldlink> <newlink>\n" },
{ "show-dev", do_show_dev,
"\tshow-dev\t[-p] [-o <field>,..] [-s [-i <interval>]] [<dev>]\n" },
{ "create-aggr", do_create_aggr,
"\tcreate-aggr\t[-t] [-R <root-dir>] [-P <policy>] [-L <mode>]\n"
"\t\t\t[-T <time>] [-u <address>] [-l <link>] ... <link>" },
{ "delete-aggr", do_delete_aggr,
"\tdelete-aggr\t[-t] [-R <root-dir>] <link>" },
{ "add-aggr", do_add_aggr,
"\tadd-aggr\t[-t] [-R <root-dir>] [-l <link>] ... <link>" },
{ "remove-aggr", do_remove_aggr,
"\tremove-aggr\t[-t] [-R <root-dir>] [-l <link>] ... <link>"},
{ "modify-aggr", do_modify_aggr,
"\tmodify-aggr\t[-t] [-R <root-dir>] [-P <policy>] [-L <mode>]\n"
"\t\t\t[-T <time>] [-u <address>] <link>" },
{ "show-aggr", do_show_aggr,
"\tshow-aggr\t[-pPLx] [-o <field>,..] [-s [-i <interval>]] "
"[<link>]\n" },
{ "up-aggr", do_up_aggr, NULL },
{ "scan-wifi", do_scan_wifi,
"\tscan-wifi\t[-p] [-o <field>,...] [<link>]" },
{ "connect-wifi", do_connect_wifi,
"\tconnect-wifi\t[-e <essid>] [-i <bssid>] [-k <key>,...] "
"[-s wep|wpa]\n"
"\t\t\t[-a open|shared] [-b bss|ibss] [-c] [-m a|b|g]\n"
"\t\t\t[-T <time>] [<link>]" },
{ "disconnect-wifi", do_disconnect_wifi,
"\tdisconnect-wifi\t[-a] [<link>]" },
{ "show-wifi", do_show_wifi,
"\tshow-wifi\t[-p] [-o <field>,...] [<link>]\n" },
{ "show-linkprop", do_show_linkprop,
"\tshow-linkprop\t[-cP] [-o <field>,...] [-p <prop>,...] <name>"},
{ "set-linkprop", do_set_linkprop,
"\tset-linkprop\t[-t] [-R <root-dir>] -p <prop>=<value>[,...] "
"<name>" },
{ "reset-linkprop", do_reset_linkprop,
"\treset-linkprop\t[-t] [-R <root-dir>] [-p <prop>,...] <name>\n" },
{ "show-ether", do_show_ether,
"\tshow-ether\t[-px][-o <field>,...] <link>\n" },
{ "create-secobj", do_create_secobj,
"\tcreate-secobj\t[-t] [-R <root-dir>] [-f <file>] -c <class> "
"<secobj>" },
{ "delete-secobj", do_delete_secobj,
"\tdelete-secobj\t[-t] [-R <root-dir>] <secobj>[,...]" },
{ "show-secobj", do_show_secobj,
"\tshow-secobj\t[-pP] [-o <field>,...] [<secobj>,...]\n" },
{ "init-linkprop", do_init_linkprop, NULL },
{ "init-secobj", do_init_secobj, NULL },
{ "create-vlan", do_create_vlan,
"\tcreate-vlan\t[-ft] [-R <root-dir>] -l <link> -v <vid> [link]" },
{ "delete-vlan", do_delete_vlan,
"\tdelete-vlan\t[-t] [-R <root-dir>] <link>" },
{ "show-vlan", do_show_vlan,
"\tshow-vlan\t[-pP] [-o <field>,..] [<link>]\n" },
{ "up-vlan", do_up_vlan, NULL },
{ "delete-phys", do_delete_phys,
"\tdelete-phys\t<link>" },
{ "show-phys", do_show_phys,
"\tshow-phys\t[-pP] [-o <field>,..] [<link>]" },
{ "init-phys", do_init_phys, NULL },
{ "show-linkmap", do_show_linkmap, NULL }
};
static const struct option lopts[] = {
{"vlan-id", required_argument, 0, 'v'},
{"output", required_argument, 0, 'o'},
{"dev", required_argument, 0, 'd'},
{"policy", required_argument, 0, 'P'},
{"lacp-mode", required_argument, 0, 'L'},
{"lacp-timer", required_argument, 0, 'T'},
{"unicast", required_argument, 0, 'u'},
{"temporary", no_argument, 0, 't'},
{"root-dir", required_argument, 0, 'R'},
{"link", required_argument, 0, 'l'},
{"forcible", no_argument, 0, 'f'},
{ 0, 0, 0, 0 }
};
static const struct option show_lopts[] = {
{"statistics", no_argument, 0, 's'},
{"interval", required_argument, 0, 'i'},
{"parseable", no_argument, 0, 'p'},
{"extended", no_argument, 0, 'x'},
{"output", required_argument, 0, 'o'},
{"persistent", no_argument, 0, 'P'},
{"lacp", no_argument, 0, 'L'},
{ 0, 0, 0, 0 }
};
static const struct option prop_longopts[] = {
{"temporary", no_argument, 0, 't' },
{"output", required_argument, 0, 'o' },
{"root-dir", required_argument, 0, 'R' },
{"prop", required_argument, 0, 'p' },
{"parseable", no_argument, 0, 'c' },
{"persistent", no_argument, 0, 'P' },
{ 0, 0, 0, 0 }
};
static const struct option wifi_longopts[] = {
{"parseable", no_argument, 0, 'p' },
{"output", required_argument, 0, 'o' },
{"essid", required_argument, 0, 'e' },
{"bsstype", required_argument, 0, 'b' },
{"mode", required_argument, 0, 'm' },
{"key", required_argument, 0, 'k' },
{"sec", required_argument, 0, 's' },
{"auth", required_argument, 0, 'a' },
{"create-ibss", required_argument, 0, 'c' },
{"timeout", required_argument, 0, 'T' },
{"all-links", no_argument, 0, 'a' },
{"temporary", no_argument, 0, 't' },
{"root-dir", required_argument, 0, 'R' },
{"persistent", no_argument, 0, 'P' },
{"file", required_argument, 0, 'f' },
{ 0, 0, 0, 0 }
};
static const struct option showeth_lopts[] = {
{"parseable", no_argument, 0, 'p' },
{"extended", no_argument, 0, 'x' },
{"output", required_argument, 0, 'o' },
{ 0, 0, 0, 0 }
};
/*
* structures for 'dladm show-ether'
*/
typedef struct ether_fields_buf_s
{
char eth_link[15];
char eth_ptype[8];
char eth_state[8];
char eth_autoneg[5];
char eth_spdx[31];
char eth_pause[6];
char eth_rem_fault[16];
} ether_fields_buf_t;
static print_field_t ether_fields[] = {
/* name, header, field width, offset, cmdtype */
{ "link", "LINK", 15,
offsetof(ether_fields_buf_t, eth_link), CMD_TYPE_ANY},
{ "ptype", "PTYPE", 8,
offsetof(ether_fields_buf_t, eth_ptype), CMD_TYPE_ANY},
{ "state", "STATE", 8,
offsetof(ether_fields_buf_t, eth_state), CMD_TYPE_ANY},
{ "auto", "AUTO", 5,
offsetof(ether_fields_buf_t, eth_autoneg), CMD_TYPE_ANY},
{ "speed-duplex", "SPEED-DUPLEX", 31,
offsetof(ether_fields_buf_t, eth_spdx), CMD_TYPE_ANY},
{ "pause", "PAUSE", 6,
offsetof(ether_fields_buf_t, eth_pause), CMD_TYPE_ANY},
{ "rem_fault", "REM_FAULT", 16,
offsetof(ether_fields_buf_t, eth_rem_fault), CMD_TYPE_ANY}}
;
#define ETHER_MAX_FIELDS (sizeof (ether_fields) / sizeof (print_field_t))
typedef struct print_ether_state {
const char *es_link;
boolean_t es_parseable;
boolean_t es_header;
boolean_t es_extended;
print_state_t es_print;
} print_ether_state_t;
/*
* structures for 'dladm show-dev'.
*/
typedef enum {
DEV_LINK,
DEV_STATE,
DEV_SPEED,
DEV_DUPLEX
} dev_field_index_t;
static print_field_t dev_fields[] = {
/* name, header, field width, index, cmdtype */
{ "link", "LINK", 15, DEV_LINK, CMD_TYPE_ANY},
{ "state", "STATE", 6, DEV_STATE, CMD_TYPE_ANY},
{ "speed", "SPEED", 8, DEV_SPEED, CMD_TYPE_ANY},
{ "duplex", "DUPLEX", 8, DEV_DUPLEX, CMD_TYPE_ANY}}
;
#define DEV_MAX_FIELDS (sizeof (dev_fields) / sizeof (print_field_t))
/*
* structures for 'dladm show-dev -s' (print statistics)
*/
typedef enum {
DEVS_LINK,
DEVS_IPKTS,
DEVS_RBYTES,
DEVS_IERRORS,
DEVS_OPKTS,
DEVS_OBYTES,
DEVS_OERRORS
} devs_field_index_t;
static print_field_t devs_fields[] = {
/* name, header, field width, index, cmdtype */
{ "link", "LINK", 15, DEVS_LINK, CMD_TYPE_ANY},
{ "ipackets", "IPACKETS", 10, DEVS_IPKTS, CMD_TYPE_ANY},
{ "rbytes", "RBYTES", 8, DEVS_RBYTES, CMD_TYPE_ANY},
{ "ierrors", "IERRORS", 10, DEVS_IERRORS, CMD_TYPE_ANY},
{ "opackets", "OPACKETS", 12, DEVS_OPKTS, CMD_TYPE_ANY},
{ "obytes", "OBYTES", 12, DEVS_OBYTES, CMD_TYPE_ANY},
{ "oerrors", "OERRORS", 8, DEVS_OERRORS, CMD_TYPE_ANY}}
;
#define DEVS_MAX_FIELDS (sizeof (devs_fields) / sizeof (print_field_t))
typedef struct dev_args_s {
char *devs_link;
pktsum_t *devs_psum;
} dev_args_t;
static char *print_dev_stats(print_field_t *, void *);
static char *print_dev(print_field_t *, void *);
/*
* buffer used by print functions for show-{link,phys,vlan} commands.
*/
typedef struct link_fields_buf_s {
char link_name[MAXLINKNAMELEN];
char link_class[DLADM_STRSIZE];
char link_mtu[11];
char link_state[DLADM_STRSIZE];
char link_over[MAXLINKNAMELEN];
char link_phys_state[DLADM_STRSIZE];
char link_phys_media[DLADM_STRSIZE];
char link_phys_speed[DLADM_STRSIZE];
char link_phys_duplex[DLPI_LINKNAME_MAX];
char link_phys_device[DLPI_LINKNAME_MAX];
char link_flags[6];
char link_vlan_vid[6];
} link_fields_buf_t;
/*
* structures for 'dladm show-link'
*/
static print_field_t link_fields[] = {
/* name, header, field width, offset, cmdtype */
{ "link", "LINK", 11,
offsetof(link_fields_buf_t, link_name), CMD_TYPE_ANY},
{ "class", "CLASS", 8,
offsetof(link_fields_buf_t, link_class), CMD_TYPE_ANY},
{ "mtu", "MTU", 6,
offsetof(link_fields_buf_t, link_mtu), CMD_TYPE_ANY},
{ "state", "STATE", 8,
offsetof(link_fields_buf_t, link_state), CMD_TYPE_ANY},
{ "over", "OVER", DLPI_LINKNAME_MAX,
offsetof(link_fields_buf_t, link_over), CMD_TYPE_ANY}}
;
#define DEV_LINK_FIELDS (sizeof (link_fields) / sizeof (print_field_t))
/*
* structures for 'dladm show-aggr'
*/
typedef struct laggr_fields_buf_s {
char laggr_name[DLPI_LINKNAME_MAX];
char laggr_policy[9];
char laggr_addrpolicy[ETHERADDRL * 3 + 3];
char laggr_lacpactivity[14];
char laggr_lacptimer[DLADM_STRSIZE];
char laggr_flags[7];
} laggr_fields_buf_t;
typedef struct laggr_args_s {
int laggr_lport; /* -1 indicates the aggr itself */
const char *laggr_link;
dladm_aggr_grp_attr_t *laggr_ginfop;
dladm_status_t *laggr_status;
pktsum_t *laggr_pktsumtot; /* -s only */
pktsum_t *laggr_prevstats; /* -s only */
boolean_t laggr_parseable;
} laggr_args_t;
static print_field_t laggr_fields[] = {
/* name, header, field width, offset, cmdtype */
{ "link", "LINK", 15,
offsetof(laggr_fields_buf_t, laggr_name), CMD_TYPE_ANY},
{ "policy", "POLICY", 8,
offsetof(laggr_fields_buf_t, laggr_policy), CMD_TYPE_ANY},
{ "addrpolicy", "ADDRPOLICY", ETHERADDRL * 3 + 2,
offsetof(laggr_fields_buf_t, laggr_addrpolicy), CMD_TYPE_ANY},
{ "lacpactivity", "LACPACTIVITY", 13,
offsetof(laggr_fields_buf_t, laggr_lacpactivity), CMD_TYPE_ANY},
{ "lacptimer", "LACPTIMER", 11,
offsetof(laggr_fields_buf_t, laggr_lacptimer), CMD_TYPE_ANY},
{ "flags", "FLAGS", 7,
offsetof(laggr_fields_buf_t, laggr_flags), CMD_TYPE_ANY}}
;
#define LAGGR_MAX_FIELDS (sizeof (laggr_fields) / sizeof (print_field_t))
/*
* structures for 'dladm show-aggr -x'.
*/
typedef enum {
AGGR_X_LINK,
AGGR_X_PORT,
AGGR_X_SPEED,
AGGR_X_DUPLEX,
AGGR_X_STATE,
AGGR_X_ADDRESS,
AGGR_X_PORTSTATE
} aggr_x_field_index_t;
static print_field_t aggr_x_fields[] = {
/* name, header, field width, index, cmdtype */
{ "link", "LINK", 11, AGGR_X_LINK, CMD_TYPE_ANY},
{ "port", "PORT", 14, AGGR_X_PORT, CMD_TYPE_ANY},
{ "speed", "SPEED", 4, AGGR_X_SPEED, CMD_TYPE_ANY},
{ "duplex", "DUPLEX", 9, AGGR_X_DUPLEX, CMD_TYPE_ANY},
{ "state", "STATE", 9, AGGR_X_STATE, CMD_TYPE_ANY},
{ "address", "ADDRESS", 18, AGGR_X_ADDRESS, CMD_TYPE_ANY},
{ "portstate", "PORTSTATE", 15, AGGR_X_PORTSTATE, CMD_TYPE_ANY}}
;
#define AGGR_X_MAX_FIELDS \
(sizeof (aggr_x_fields) / sizeof (print_field_t))
/*
* structures for 'dladm show-aggr -s'.
*/
typedef enum {
AGGR_S_LINK,
AGGR_S_PORT,
AGGR_S_IPKTS,
AGGR_S_RBYTES,
AGGR_S_OPKTS,
AGGR_S_OBYTES,
AGGR_S_IPKTDIST,
AGGR_S_OPKTDIST
} aggr_s_field_index_t;
static print_field_t aggr_s_fields[] = {
/* name, header, field width, index, cmdtype */
{ "link", "LINK", 11, AGGR_S_LINK,
CMD_TYPE_ANY},
{ "port", "PORT", 9, AGGR_S_PORT,
CMD_TYPE_ANY},
{ "ipackets", "IPACKETS", 7, AGGR_S_IPKTS,
CMD_TYPE_ANY},
{ "rbytes", "RBYTES", 7, AGGR_S_RBYTES,
CMD_TYPE_ANY},
{ "opackets", "OPACKETS", 7, AGGR_S_OPKTS,
CMD_TYPE_ANY},
{ "obytes", "OBYTES", 7, AGGR_S_OBYTES,
CMD_TYPE_ANY},
{ "ipktdist", "IPKTDIST", 8, AGGR_S_IPKTDIST,
CMD_TYPE_ANY},
{ "opktdist", "OPKTDIST", 14, AGGR_S_OPKTDIST,
CMD_TYPE_ANY}}
;
#define AGGR_S_MAX_FIELDS \
(sizeof (aggr_l_fields) / sizeof (print_field_t))
/*
* structures for 'dladm show-dev -L'.
*/
typedef enum {
AGGR_L_LINK,
AGGR_L_PORT,
AGGR_L_AGGREGATABLE,
AGGR_L_SYNC,
AGGR_L_COLL,
AGGR_L_DIST,
AGGR_L_DEFAULTED,
AGGR_L_EXPIRED
} aggr_l_field_index_t;
static print_field_t aggr_l_fields[] = {
/* name, header, field width, index, cmdtype */
{ "link", "LINK", 11, AGGR_L_LINK,
CMD_TYPE_ANY},
{ "port", "PORT", 12, AGGR_L_PORT,
CMD_TYPE_ANY},
{ "aggregatable", "AGGREGATABLE", 12, AGGR_L_AGGREGATABLE,
CMD_TYPE_ANY},
{ "sync", "SYNC", 4, AGGR_L_SYNC,
CMD_TYPE_ANY},
{ "coll", "COLL", 4, AGGR_L_COLL,
CMD_TYPE_ANY},
{ "dist", "DIST", 4, AGGR_L_DIST,
CMD_TYPE_ANY},
{ "defaulted", "DEFAULTED", 9, AGGR_L_DEFAULTED,
CMD_TYPE_ANY},
{ "expired", "EXPIRED", 14, AGGR_L_EXPIRED,
CMD_TYPE_ANY}}
;
#define AGGR_L_MAX_FIELDS \
(sizeof (aggr_l_fields) / sizeof (print_field_t))
/*
* structures for 'dladm show-phys'
*/
static print_field_t phys_fields[] = {
/* name, header, field width, offset, cmdtype */
{ "link", "LINK", 12,
offsetof(link_fields_buf_t, link_name), CMD_TYPE_ANY},
{ "media", "MEDIA", 20,
offsetof(link_fields_buf_t, link_phys_media), CMD_TYPE_ANY},
{ "state", "STATE", 10,
offsetof(link_fields_buf_t, link_phys_state), CMD_TYPE_ANY},
{ "speed", "SPEED", 6,
offsetof(link_fields_buf_t, link_phys_speed), CMD_TYPE_ANY},
{ "duplex", "DUPLEX", 9,
offsetof(link_fields_buf_t, link_phys_duplex), CMD_TYPE_ANY},
{ "device", "DEVICE", 12,
offsetof(link_fields_buf_t, link_phys_device), CMD_TYPE_ANY},
{ "flags", "FLAGS", 6,
offsetof(link_fields_buf_t, link_flags), CMD_TYPE_ANY}}
;
#define PHYS_MAX_FIELDS (sizeof (phys_fields) / sizeof (print_field_t))
/*
* structures for 'dladm show-vlan'
*/
static print_field_t vlan_fields[] = {
/* name, header, field width, offset, cmdtype */
{ "link", "LINK", 15,
offsetof(link_fields_buf_t, link_name), CMD_TYPE_ANY},
{ "vid", "VID", 8,
offsetof(link_fields_buf_t, link_vlan_vid), CMD_TYPE_ANY},
{ "over", "OVER", 12,
offsetof(link_fields_buf_t, link_over), CMD_TYPE_ANY},
{ "flags", "FLAGS", 6,
offsetof(link_fields_buf_t, link_flags), CMD_TYPE_ANY}}
;
#define VLAN_MAX_FIELDS (sizeof (vlan_fields) / sizeof (print_field_t))
/*
* structures for 'dladm show-wifi'
*/
static print_field_t wifi_fields[] = {
{ "link", "LINK", 10, 0, WIFI_CMD_ALL},
{ "essid", "ESSID", 19, DLADM_WLAN_ATTR_ESSID, WIFI_CMD_ALL},
{ "bssid", "BSSID/IBSSID", 17, DLADM_WLAN_ATTR_BSSID, WIFI_CMD_ALL},
{ "ibssid", "BSSID/IBSSID", 17, DLADM_WLAN_ATTR_BSSID, WIFI_CMD_ALL},
{ "mode", "MODE", 6, DLADM_WLAN_ATTR_MODE, WIFI_CMD_ALL},
{ "speed", "SPEED", 6, DLADM_WLAN_ATTR_SPEED, WIFI_CMD_ALL},
{ "auth", "AUTH", 8, DLADM_WLAN_ATTR_AUTH, WIFI_CMD_SHOW},
{ "bsstype", "BSSTYPE", 8, DLADM_WLAN_ATTR_BSSTYPE, WIFI_CMD_ALL},
{ "sec", "SEC", 6, DLADM_WLAN_ATTR_SECMODE, WIFI_CMD_ALL},
{ "status", "STATUS", 17, DLADM_WLAN_LINKATTR_STATUS, WIFI_CMD_SHOW},
{ "strength", "STRENGTH", 10, DLADM_WLAN_ATTR_STRENGTH, WIFI_CMD_ALL}}
;
static char *all_scan_wifi_fields =
"link,essid,bssid,sec,strength,mode,speed,bsstype";
static char *all_show_wifi_fields =
"link,status,essid,sec,strength,mode,speed,auth,bssid,bsstype";
static char *def_scan_wifi_fields =
"link,essid,bssid,sec,strength,mode,speed";
static char *def_show_wifi_fields =
"link,status,essid,sec,strength,mode,speed";
#define WIFI_MAX_FIELDS (sizeof (wifi_fields) / sizeof (print_field_t))
/*
* structures for 'dladm show-linkprop'
*/
typedef enum {
LINKPROP_LINK,
LINKPROP_PROPERTY,
LINKPROP_VALUE,
LINKPROP_DEFAULT,
LINKPROP_POSSIBLE
} linkprop_field_index_t;
static print_field_t linkprop_fields[] = {
/* name, header, field width, index, cmdtype */
{ "link", "LINK", 12, LINKPROP_LINK, CMD_TYPE_ANY},
{ "property", "PROPERTY", 15, LINKPROP_PROPERTY, CMD_TYPE_ANY},
{ "value", "VALUE", 14, LINKPROP_VALUE, CMD_TYPE_ANY},
{ "default", "DEFAULT", 14, LINKPROP_DEFAULT, CMD_TYPE_ANY},
{ "possible", "POSSIBLE", 20, LINKPROP_POSSIBLE, CMD_TYPE_ANY}}
;
#define LINKPROP_MAX_FIELDS \
(sizeof (linkprop_fields) / sizeof (print_field_t))
#define MAX_PROPS 32
#define MAX_PROP_LINE 512
typedef struct prop_info {
char *pi_name;
char *pi_val[DLADM_MAX_PROP_VALCNT];
uint_t pi_count;
} prop_info_t;
typedef struct prop_list {
prop_info_t pl_info[MAX_PROPS];
uint_t pl_count;
char *pl_buf;
} prop_list_t;
typedef struct show_linkprop_state {
char ls_link[MAXLINKNAMELEN];
char *ls_line;
char **ls_propvals;
prop_list_t *ls_proplist;
boolean_t ls_parseable;
boolean_t ls_persist;
boolean_t ls_header;
dladm_status_t ls_status;
dladm_status_t ls_retstatus;
print_state_t ls_print;
} show_linkprop_state_t;
typedef struct linkprop_args_s {
show_linkprop_state_t *ls_state;
char *ls_propname;
datalink_id_t ls_linkid;
} linkprop_args_t;
/*
* structures for 'dladm show-secobj'
*/
typedef struct secobj_fields_buf_s {
char ss_obj_name[DLADM_SECOBJ_VAL_MAX];
char ss_class[20];
char ss_val[30];
} secobj_fields_buf_t;
static print_field_t secobj_fields[] = {
/* name, header, field width, offset, cmdtype */
{ "object", "OBJECT", 20,
offsetof(secobj_fields_buf_t, ss_obj_name), CMD_TYPE_ANY},
{ "class", "CLASS", 20,
offsetof(secobj_fields_buf_t, ss_class), CMD_TYPE_ANY},
{ "value", "VALUE", 30,
offsetof(secobj_fields_buf_t, ss_val), CMD_TYPE_ANY}}
;
#define DEV_SOBJ_FIELDS (sizeof (secobj_fields) / sizeof (print_field_t))
static char *progname;
static sig_atomic_t signalled;
static void
usage(void)
{
int i;
cmd_t *cmdp;
(void) fprintf(stderr, gettext("usage: dladm <subcommand> <args> ..."
"\n"));
for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
cmdp = &cmds[i];
if (cmdp->c_usage != NULL)
(void) fprintf(stderr, "%s\n", gettext(cmdp->c_usage));
}
exit(1);
}
int
main(int argc, char *argv[])
{
int i;
cmd_t *cmdp;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
progname = argv[0];
if (argc < 2)
usage();
for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
cmdp = &cmds[i];
if (strcmp(argv[1], cmdp->c_name) == 0) {
cmdp->c_fn(argc - 1, &argv[1], cmdp->c_usage);
exit(0);
}
}
(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
progname, argv[1]);
usage();
return (0);
}
static void
do_create_aggr(int argc, char *argv[], const char *use)
{
char option;
int key = 0;
uint32_t policy = AGGR_POLICY_L4;
aggr_lacp_mode_t lacp_mode = AGGR_LACP_OFF;
aggr_lacp_timer_t lacp_timer = AGGR_LACP_TIMER_SHORT;
dladm_aggr_port_attr_db_t port[MAXPORT];
uint_t n, ndev, nlink;
uint8_t mac_addr[ETHERADDRL];
boolean_t mac_addr_fixed = B_FALSE;
boolean_t P_arg = B_FALSE;
boolean_t l_arg = B_FALSE;
boolean_t u_arg = B_FALSE;
boolean_t T_arg = B_FALSE;
uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
char *altroot = NULL;
char name[MAXLINKNAMELEN];
char *devs[MAXPORT];
char *links[MAXPORT];
dladm_status_t status;
ndev = nlink = opterr = 0;
while ((option = getopt_long(argc, argv, ":d:l:L:P:R:tfu:T:",
lopts, NULL)) != -1) {
switch (option) {
case 'd':
if (ndev + nlink >= MAXPORT)
die("too many ports specified");
devs[ndev++] = optarg;
break;
case 'P':
if (P_arg)
die_optdup(option);
P_arg = B_TRUE;
if (!dladm_aggr_str2policy(optarg, &policy))
die("invalid policy '%s'", optarg);
break;
case 'u':
if (u_arg)
die_optdup(option);
u_arg = B_TRUE;
if (!dladm_aggr_str2macaddr(optarg, &mac_addr_fixed,
mac_addr))
die("invalid MAC address '%s'", optarg);
break;
case 'l':
if (isdigit(optarg[strlen(optarg) - 1])) {
/*
* Ended with digit, possibly a link name.
*/
if (ndev + nlink >= MAXPORT)
die("too many ports specified");
links[nlink++] = optarg;
break;
}
/* FALLTHROUGH */
case 'L':
if (l_arg)
die_optdup(option);
l_arg = B_TRUE;
if (!dladm_aggr_str2lacpmode(optarg, &lacp_mode))
die("invalid LACP mode '%s'", optarg);
break;
case 'T':
if (T_arg)
die_optdup(option);
T_arg = B_TRUE;
if (!dladm_aggr_str2lacptimer(optarg, &lacp_timer))
die("invalid LACP timer value '%s'", optarg);
break;
case 't':
flags &= ~DLADM_OPT_PERSIST;
break;
case 'f':
flags |= DLADM_OPT_FORCE;
break;
case 'R':
altroot = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (ndev + nlink == 0)
usage();
/* get key value or the aggregation name (required last argument) */
if (optind != (argc-1))
usage();
if (!str2int(argv[optind], &key)) {
if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >=
MAXLINKNAMELEN) {
die("link name too long '%s'", argv[optind]);
}
if (!dladm_valid_linkname(name))
die("invalid link name '%s'", argv[optind]);
} else {
(void) snprintf(name, MAXLINKNAMELEN, "aggr%d", key);
}
if (altroot != NULL)
altroot_cmd(altroot, argc, argv);
for (n = 0; n < ndev; n++) {
if (dladm_dev2linkid(devs[n], &port[n].lp_linkid) !=
DLADM_STATUS_OK) {
die("invalid dev name '%s'", devs[n]);
}
}
for (n = 0; n < nlink; n++) {
if (dladm_name2info(links[n], &port[ndev + n].lp_linkid,
NULL, NULL, NULL) != DLADM_STATUS_OK) {
die("invalid link name '%s'", links[n]);
}
}
status = dladm_aggr_create(name, key, ndev + nlink, port, policy,
mac_addr_fixed, (const uchar_t *)mac_addr, lacp_mode,
lacp_timer, flags);
done:
if (status != DLADM_STATUS_OK) {
if (status == DLADM_STATUS_NONOTIF) {
die_dlerr(status, "not all links have link up/down "
"detection; must use -f (see dladm(1M))\n");
} else {
die_dlerr(status, "create operation failed");
}
}
}
/*
* arg is either the key or the aggr name. Validate it and convert it to
* the linkid if altroot is NULL.
*/
static dladm_status_t
i_dladm_aggr_get_linkid(const char *altroot, const char *arg,
datalink_id_t *linkidp, uint32_t flags)
{
int key = 0;
char *aggr = NULL;
dladm_status_t status;
if (!str2int(arg, &key))
aggr = (char *)arg;
if (aggr == NULL && key == 0)
return (DLADM_STATUS_LINKINVAL);
if (altroot != NULL)
return (DLADM_STATUS_OK);
if (aggr != NULL) {
status = dladm_name2info(aggr, linkidp, NULL, NULL, NULL);
} else {
status = dladm_key2linkid(key, linkidp, flags);
}
return (status);
}
static void
do_delete_aggr(int argc, char *argv[], const char *use)
{
char option;
char *altroot = NULL;
uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
dladm_status_t status;
datalink_id_t linkid;
opterr = 0;
while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) {
switch (option) {
case 't':
flags &= ~DLADM_OPT_PERSIST;
break;
case 'R':
altroot = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
/* get key value or the aggregation name (required last argument) */
if (optind != (argc-1))
usage();
status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
if (status != DLADM_STATUS_OK)
goto done;
if (altroot != NULL)
altroot_cmd(altroot, argc, argv);
status = dladm_aggr_delete(linkid, flags);
done:
if (status != DLADM_STATUS_OK)
die_dlerr(status, "delete operation failed");
}
static void
do_add_aggr(int argc, char *argv[], const char *use)
{
char option;
uint_t n, ndev, nlink;
char *altroot = NULL;
uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
datalink_id_t linkid;
dladm_status_t status;
dladm_aggr_port_attr_db_t port[MAXPORT];
char *devs[MAXPORT];
char *links[MAXPORT];
ndev = nlink = opterr = 0;
while ((option = getopt_long(argc, argv, ":d:l:R:tf", lopts,
NULL)) != -1) {
switch (option) {
case 'd':
if (ndev + nlink >= MAXPORT)
die("too many ports specified");
devs[ndev++] = optarg;
break;
case 'l':
if (ndev + nlink >= MAXPORT)
die("too many ports specified");
links[nlink++] = optarg;
break;
case 't':
flags &= ~DLADM_OPT_PERSIST;
break;
case 'f':
flags |= DLADM_OPT_FORCE;
break;
case 'R':
altroot = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (ndev + nlink == 0)
usage();
/* get key value or the aggregation name (required last argument) */
if (optind != (argc-1))
usage();
if ((status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid,
flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST))) !=
DLADM_STATUS_OK) {
goto done;
}
if (altroot != NULL)
altroot_cmd(altroot, argc, argv);
for (n = 0; n < ndev; n++) {
if (dladm_dev2linkid(devs[n], &(port[n].lp_linkid)) !=
DLADM_STATUS_OK) {
die("invalid <dev> '%s'", devs[n]);
}
}
for (n = 0; n < nlink; n++) {
if (dladm_name2info(links[n], &port[n + ndev].lp_linkid,
NULL, NULL, NULL) != DLADM_STATUS_OK) {
die("invalid <link> '%s'", links[n]);
}
}
status = dladm_aggr_add(linkid, ndev + nlink, port, flags);
done:
if (status != DLADM_STATUS_OK) {
/*
* checking DLADM_STATUS_NOTSUP is a temporary workaround
* and should be removed once 6399681 is fixed.
*/
if (status == DLADM_STATUS_NOTSUP) {
(void) fprintf(stderr,
gettext("%s: add operation failed: %s\n"),
progname,
gettext("link capabilities don't match"));
exit(ENOTSUP);
} else if (status == DLADM_STATUS_NONOTIF) {
die_dlerr(status, "not all links have link up/down "
"detection; must use -f (see dladm(1M))\n");
} else {
die_dlerr(status, "add operation failed");
}
}
}
static void
do_remove_aggr(int argc, char *argv[], const char *use)
{
char option;
dladm_aggr_port_attr_db_t port[MAXPORT];
uint_t n, ndev, nlink;
char *devs[MAXPORT];
char *links[MAXPORT];
char *altroot = NULL;
uint32_t flags;
datalink_id_t linkid;
dladm_status_t status;
flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
ndev = nlink = opterr = 0;
while ((option = getopt_long(argc, argv, ":d:l:R:t",
lopts, NULL)) != -1) {
switch (option) {
case 'd':
if (ndev + nlink >= MAXPORT)
die("too many ports specified");
devs[ndev++] = optarg;
break;
case 'l':
if (ndev + nlink >= MAXPORT)
die("too many ports specified");
links[nlink++] = optarg;
break;
case 't':
flags &= ~DLADM_OPT_PERSIST;
break;
case 'R':
altroot = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (ndev + nlink == 0)
usage();
/* get key value or the aggregation name (required last argument) */
if (optind != (argc-1))
usage();
status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
if (status != DLADM_STATUS_OK)
goto done;
if (altroot != NULL)
altroot_cmd(altroot, argc, argv);
for (n = 0; n < ndev; n++) {
if (dladm_dev2linkid(devs[n], &(port[n].lp_linkid)) !=
DLADM_STATUS_OK) {
die("invalid <dev> '%s'", devs[n]);
}
}
for (n = 0; n < nlink; n++) {
if (dladm_name2info(links[n], &port[n + ndev].lp_linkid,
NULL, NULL, NULL) != DLADM_STATUS_OK) {
die("invalid <link> '%s'", links[n]);
}
}
status = dladm_aggr_remove(linkid, ndev + nlink, port, flags);
done:
if (status != DLADM_STATUS_OK)
die_dlerr(status, "remove operation failed");
}
static void
do_modify_aggr(int argc, char *argv[], const char *use)
{
char option;
uint32_t policy = AGGR_POLICY_L4;
aggr_lacp_mode_t lacp_mode = AGGR_LACP_OFF;
aggr_lacp_timer_t lacp_timer = AGGR_LACP_TIMER_SHORT;
uint8_t mac_addr[ETHERADDRL];
boolean_t mac_addr_fixed = B_FALSE;
uint8_t modify_mask = 0;
char *altroot = NULL;
uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
datalink_id_t linkid;
dladm_status_t status;
opterr = 0;
while ((option = getopt_long(argc, argv, ":L:l:P:R:tu:T:", lopts,
NULL)) != -1) {
switch (option) {
case 'P':
if (modify_mask & DLADM_AGGR_MODIFY_POLICY)
die_optdup(option);
modify_mask |= DLADM_AGGR_MODIFY_POLICY;
if (!dladm_aggr_str2policy(optarg, &policy))
die("invalid policy '%s'", optarg);
break;
case 'u':
if (modify_mask & DLADM_AGGR_MODIFY_MAC)
die_optdup(option);
modify_mask |= DLADM_AGGR_MODIFY_MAC;
if (!dladm_aggr_str2macaddr(optarg, &mac_addr_fixed,
mac_addr))
die("invalid MAC address '%s'", optarg);
break;
case 'l':
case 'L':
if (modify_mask & DLADM_AGGR_MODIFY_LACP_MODE)
die_optdup(option);
modify_mask |= DLADM_AGGR_MODIFY_LACP_MODE;
if (!dladm_aggr_str2lacpmode(optarg, &lacp_mode))
die("invalid LACP mode '%s'", optarg);
break;
case 'T':
if (modify_mask & DLADM_AGGR_MODIFY_LACP_TIMER)
die_optdup(option);
modify_mask |= DLADM_AGGR_MODIFY_LACP_TIMER;
if (!dladm_aggr_str2lacptimer(optarg, &lacp_timer))
die("invalid LACP timer value '%s'", optarg);
break;
case 't':
flags &= ~DLADM_OPT_PERSIST;
break;
case 'R':
altroot = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (modify_mask == 0)
die("at least one of the -PulT options must be specified");
/* get key value or the aggregation name (required last argument) */
if (optind != (argc-1))
usage();
status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags);
if (status != DLADM_STATUS_OK)
goto done;
if (altroot != NULL)
altroot_cmd(altroot, argc, argv);
status = dladm_aggr_modify(linkid, modify_mask, policy, mac_addr_fixed,
(const uchar_t *)mac_addr, lacp_mode, lacp_timer, flags);
done:
if (status != DLADM_STATUS_OK)
die_dlerr(status, "modify operation failed");
}
/*ARGSUSED*/
static void
do_up_aggr(int argc, char *argv[], const char *use)
{
datalink_id_t linkid = DATALINK_ALL_LINKID;
dladm_status_t status;
/*
* get the key or the name of the aggregation (optional last argument)
*/
if (argc == 2) {
if ((status = i_dladm_aggr_get_linkid(NULL, argv[1], &linkid,
DLADM_OPT_PERSIST)) != DLADM_STATUS_OK) {
goto done;
}
} else if (argc > 2) {
usage();
}
status = dladm_aggr_up(linkid);
done:
if (status != DLADM_STATUS_OK) {
if (argc == 2) {
die_dlerr(status,
"could not bring up aggregation '%s'", argv[1]);
} else {
die_dlerr(status, "could not bring aggregations up");
}
}
}
static void
do_create_vlan(int argc, char *argv[], const char *use)
{
char *link = NULL;
char drv[DLPI_LINKNAME_MAX];
uint_t ppa;
datalink_id_t linkid;
int vid = 0;
char option;
uint32_t flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
char *altroot = NULL;
char vlan[MAXLINKNAMELEN];
dladm_status_t status;
opterr = 0;
while ((option = getopt_long(argc, argv, ":tfl:v:",
lopts, NULL)) != -1) {
switch (option) {
case 'v':
if (vid != 0)
die_optdup(option);
if (!str2int(optarg, &vid) || vid < 1 || vid > 4094)
die("invalid VLAN identifier '%s'", optarg);
break;
case 'l':
if (link != NULL)
die_optdup(option);
link = optarg;
break;
case 'f':
flags |= DLADM_OPT_FORCE;
break;
case 't':
flags &= ~DLADM_OPT_PERSIST;
break;
case 'R':
altroot = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
/* get vlan name if there is any */
if ((vid == 0) || (link == NULL) || (argc - optind > 1))
usage();
if (optind == (argc - 1)) {
if (strlcpy(vlan, argv[optind], MAXLINKNAMELEN) >=
MAXLINKNAMELEN) {
die("vlan name too long '%s'", argv[optind]);
}
} else {
if ((dlpi_parselink(link, drv, &ppa) != DLPI_SUCCESS) ||
(ppa >= 1000) ||
(dlpi_makelink(vlan, drv, vid * 1000 + ppa) !=
DLPI_SUCCESS)) {
die("invalid link name '%s'", link);
}
}
if (altroot != NULL)
altroot_cmd(altroot, argc, argv);
if (dladm_name2info(link, &linkid, NULL, NULL, NULL) !=
DLADM_STATUS_OK) {
die("invalid link name '%s'", link);
}
if ((status = dladm_vlan_create(vlan, linkid, vid, flags)) !=
DLADM_STATUS_OK) {
if (status == DLADM_STATUS_NOTSUP) {
die_dlerr(status, "VLAN over '%s' may require lowered "
"MTU; must use -f (see dladm(1M))\n", link);
} else {
die_dlerr(status, "create operation failed");
}
}
}
static void
do_delete_vlan(int argc, char *argv[], const char *use)
{
char option;
uint32_t flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
char *altroot = NULL;
datalink_id_t linkid;
dladm_status_t status;
opterr = 0;
while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) {
switch (option) {
case 't':
flags &= ~DLADM_OPT_PERSIST;
break;
case 'R':
altroot = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
/* get VLAN link name (required last argument) */
if (optind != (argc - 1))
usage();
if (altroot != NULL)
altroot_cmd(altroot, argc, argv);
status = dladm_name2info(argv[optind], &linkid, NULL, NULL, NULL);
if (status != DLADM_STATUS_OK)
goto done;
status = dladm_vlan_delete(linkid, flags);
done:
if (status != DLADM_STATUS_OK)
die_dlerr(status, "delete operation failed");
}
/*ARGSUSED*/
static void
do_up_vlan(int argc, char *argv[], const char *use)
{
datalink_id_t linkid = DATALINK_ALL_LINKID;
dladm_status_t status;
/*
* get the name of the VLAN (optional last argument)
*/
if (argc > 2)
usage();
if (argc == 2) {
status = dladm_name2info(argv[1], &linkid, NULL, NULL, NULL);
if (status != DLADM_STATUS_OK)
goto done;
}
status = dladm_vlan_up(linkid);
done:
if (status != DLADM_STATUS_OK) {
if (argc == 2) {
die_dlerr(status,
"could not bring up VLAN '%s'", argv[1]);
} else {
die_dlerr(status, "could not bring VLANs up");
}
}
}
static void
do_rename_link(int argc, char *argv[], const char *use)
{
char option;
char *link1, *link2;
char *altroot = NULL;
dladm_status_t status;
opterr = 0;
while ((option = getopt_long(argc, argv, ":R:", lopts, NULL)) != -1) {
switch (option) {
case 'R':
altroot = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
/* get link1 and link2 name (required the last 2 arguments) */
if (optind != (argc - 2))
usage();
if (altroot != NULL)
altroot_cmd(altroot, argc, argv);
link1 = argv[optind++];
link2 = argv[optind];
if ((status = dladm_rename_link(link1, link2)) != DLADM_STATUS_OK)
die_dlerr(status, "rename operation failed");
}
/*ARGSUSED*/
static void
do_delete_phys(int argc, char *argv[], const char *use)
{
datalink_id_t linkid = DATALINK_ALL_LINKID;
dladm_status_t status;
/* get link name (required the last argument) */
if (argc > 2)
usage();
if (argc == 2) {
status = dladm_name2info(argv[1], &linkid, NULL, NULL, NULL);
if (status != DLADM_STATUS_OK)
die_dlerr(status, "cannot delete '%s'", argv[1]);
}
if ((status = dladm_phys_delete(linkid)) != DLADM_STATUS_OK) {
if (argc == 2)
die_dlerr(status, "cannot delete '%s'", argv[1]);
else
die_dlerr(status, "delete operation failed");
}
}
/*ARGSUSED*/
static int
i_dladm_walk_linkmap(datalink_id_t linkid, void *arg)
{
char name[MAXLINKNAMELEN];
char mediabuf[DLADM_STRSIZE];
char classbuf[DLADM_STRSIZE];
datalink_class_t class;
uint32_t media;
uint32_t flags;
if (dladm_datalink_id2info(linkid, &flags, &class, &media, name,
MAXLINKNAMELEN) == DLADM_STATUS_OK) {
(void) dladm_class2str(class, classbuf);
(void) dladm_media2str(media, mediabuf);
(void) printf("%-12s%8d %-12s%-20s %6d\n", name,
linkid, classbuf, mediabuf, flags);
}
return (DLADM_WALK_CONTINUE);
}
/*ARGSUSED*/
static void
do_show_linkmap(int argc, char *argv[], const char *use)
{
if (argc != 1)
die("invalid arguments");
(void) printf("%-12s%8s %-12s%-20s %6s\n", "NAME", "LINKID",
"CLASS", "MEDIA", "FLAGS");
(void) dladm_walk_datalink_id(i_dladm_walk_linkmap, NULL,
DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
}
/*
* Delete inactive physical links.
*/
/*ARGSUSED*/
static int
purge_phys(datalink_id_t linkid, void *arg)
{
datalink_class_t class;
uint32_t flags;
if (dladm_datalink_id2info(linkid, &flags, &class, NULL,
NULL, 0) != DLADM_STATUS_OK) {
return (DLADM_WALK_CONTINUE);
}
if (class == DATALINK_CLASS_PHYS && !(flags & DLADM_OPT_ACTIVE))
(void) dladm_phys_delete(linkid);
return (DLADM_WALK_CONTINUE);
}
/*ARGSUSED*/
static void
do_init_phys(int argc, char *argv[], const char *use)
{
di_node_t devtree;
if (argc > 1)
usage();
/*
* Force all the devices to attach, therefore all the network physical
* devices can be known to the dlmgmtd daemon.
*/
if ((devtree = di_init("/", DINFOFORCE | DINFOSUBTREE)) != DI_NODE_NIL)
di_fini(devtree);
(void) dladm_walk_datalink_id(purge_phys, NULL,
DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
}
/*
* Print the active topology information.
*/
static dladm_status_t
print_link_topology(show_state_t *state, datalink_id_t linkid,
datalink_class_t class, link_fields_buf_t *lbuf)
{
uint32_t flags = state->ls_flags;
dladm_status_t status = DLADM_STATUS_OK;
char tmpbuf[MAXLINKNAMELEN];
if (!state->ls_parseable)
(void) sprintf(lbuf->link_over, STR_UNDEF_VAL);
else
(void) sprintf(lbuf->link_over, "");
if (class == DATALINK_CLASS_VLAN) {
dladm_vlan_attr_t vinfo;
status = dladm_vlan_info(linkid, &vinfo, flags);
if (status != DLADM_STATUS_OK)
goto done;
status = dladm_datalink_id2info(vinfo.dv_linkid, NULL, NULL,
NULL, lbuf->link_over, sizeof (lbuf->link_over));
if (status != DLADM_STATUS_OK)
goto done;
} else if (class == DATALINK_CLASS_AGGR) {
dladm_aggr_grp_attr_t ginfo;
int i;
(void) sprintf(lbuf->link_over, "");
status = dladm_aggr_info(linkid, &ginfo, flags);
if (status != DLADM_STATUS_OK)
goto done;
if (ginfo.lg_nports == 0) {
status = DLADM_STATUS_BADVAL;
goto done;
}
for (i = 0; i < ginfo.lg_nports; i++) {
status = dladm_datalink_id2info(
ginfo.lg_ports[i].lp_linkid, NULL, NULL, NULL,
tmpbuf, sizeof (tmpbuf));
if (status != DLADM_STATUS_OK) {
free(ginfo.lg_ports);
goto done;
}
(void) strlcat(lbuf->link_over, tmpbuf,
sizeof (lbuf->link_over));
if (i != (ginfo.lg_nports - 1)) {
(void) strlcat(lbuf->link_over, " ",
sizeof (lbuf->link_over));
}
}
free(ginfo.lg_ports);
} else if (class == DATALINK_CLASS_VNIC) {
dladm_vnic_attr_sys_t vinfo;
if ((status = dladm_vnic_info(linkid, &vinfo, flags)) !=
DLADM_STATUS_OK || (status = dladm_datalink_id2info(
vinfo.va_link_id, NULL, NULL, NULL, lbuf->link_over,
sizeof (lbuf->link_over)) != DLADM_STATUS_OK)) {
goto done;
}
}
done:
return (status);
}
static dladm_status_t
print_link(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *lbuf)
{
char link[MAXLINKNAMELEN];
datalink_class_t class;
uint_t mtu;
uint32_t flags;
dladm_status_t status;
if ((status = dladm_datalink_id2info(linkid, &flags, &class, NULL,
link, sizeof (link))) != DLADM_STATUS_OK) {
goto done;
}
if (!(state->ls_flags & flags)) {
status = DLADM_STATUS_NOTFOUND;
goto done;
}
if (state->ls_flags == DLADM_OPT_ACTIVE) {
dladm_attr_t dlattr;
if (class == DATALINK_CLASS_PHYS) {
dladm_phys_attr_t dpa;
dlpi_handle_t dh;
dlpi_info_t dlinfo;
if ((status = dladm_phys_info(linkid, &dpa,
DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
goto done;
}
if (!dpa.dp_novanity)
goto link_mtu;
/*
* This is a physical link that does not have
* vanity naming support.
*/
if (dlpi_open(dpa.dp_dev, &dh, DLPI_DEVONLY) !=
DLPI_SUCCESS) {
status = DLADM_STATUS_NOTFOUND;
goto done;
}
if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) {
dlpi_close(dh);
status = DLADM_STATUS_BADARG;
goto done;
}
dlpi_close(dh);
mtu = dlinfo.di_max_sdu;
} else {
link_mtu:
status = dladm_info(linkid, &dlattr);
if (status != DLADM_STATUS_OK)
goto done;
mtu = dlattr.da_max_sdu;
}
}
(void) snprintf(lbuf->link_name, sizeof (lbuf->link_name),
"%s", link);
(void) dladm_class2str(class, lbuf->link_class);
if (state->ls_flags == DLADM_OPT_ACTIVE) {
(void) snprintf(lbuf->link_mtu, sizeof (lbuf->link_mtu),
"%u", mtu);
(void) get_linkstate(link, B_TRUE, lbuf->link_state);
}
status = print_link_topology(state, linkid, class, lbuf);
if (status != DLADM_STATUS_OK)
goto done;
done:
return (status);
}
static int
show_link(datalink_id_t linkid, void *arg)
{
show_state_t *state = (show_state_t *)arg;
dladm_status_t status;
link_fields_buf_t lbuf;
/*
* first get all the link attributes into lbuf;
*/
bzero(&lbuf, sizeof (link_fields_buf_t));
status = print_link(state, linkid, &lbuf);
if (status != DLADM_STATUS_OK)
goto done;
if (!state->ls_parseable && !state->ls_printheader) {
print_header(&state->ls_print);
state->ls_printheader = B_TRUE;
}
dladm_print_output(&state->ls_print, state->ls_parseable,
dladm_print_field, (void *)&lbuf);
done:
state->ls_status = status;
return (DLADM_WALK_CONTINUE);
}
static int
show_link_stats(datalink_id_t linkid, void *arg)
{
char link[DLPI_LINKNAME_MAX];
datalink_class_t class;
show_state_t *state = (show_state_t *)arg;
pktsum_t stats, diff_stats;
dladm_phys_attr_t dpa;
dev_args_t largs;
if (state->ls_firstonly) {
if (state->ls_donefirst)
return (DLADM_WALK_CONTINUE);
state->ls_donefirst = B_TRUE;
} else {
bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
}
if (dladm_datalink_id2info(linkid, NULL, &class, NULL, link,
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
return (DLADM_WALK_CONTINUE);
}
if (class == DATALINK_CLASS_PHYS) {
if (dladm_phys_info(linkid, &dpa, DLADM_OPT_ACTIVE) !=
DLADM_STATUS_OK) {
return (DLADM_WALK_CONTINUE);
}
if (dpa.dp_novanity)
get_mac_stats(dpa.dp_dev, &stats);
else
get_link_stats(link, &stats);
} else {
get_link_stats(link, &stats);
}
stats_diff(&diff_stats, &stats, &state->ls_prevstats);
largs.devs_link = link;
largs.devs_psum = &diff_stats;
dladm_print_output(&state->ls_print, state->ls_parseable,
print_dev_stats, &largs);
state->ls_prevstats = stats;
return (DLADM_WALK_CONTINUE);
}
static dladm_status_t
print_aggr_info(show_grp_state_t *state, const char *link,
dladm_aggr_grp_attr_t *ginfop)
{
char addr_str[ETHERADDRL * 3];
laggr_fields_buf_t lbuf;
(void) snprintf(lbuf.laggr_name, sizeof (lbuf.laggr_name),
"%s", link);
(void) dladm_aggr_policy2str(ginfop->lg_policy,
lbuf.laggr_policy);
if (ginfop->lg_mac_fixed) {
(void) dladm_aggr_macaddr2str(ginfop->lg_mac, addr_str);
(void) snprintf(lbuf.laggr_addrpolicy,
sizeof (lbuf.laggr_addrpolicy), "fixed (%s)", addr_str);
} else {
(void) snprintf(lbuf.laggr_addrpolicy,
sizeof (lbuf.laggr_addrpolicy), "auto");
}
(void) dladm_aggr_lacpmode2str(ginfop->lg_lacp_mode,
lbuf.laggr_lacpactivity);
(void) dladm_aggr_lacptimer2str(ginfop->lg_lacp_timer,
lbuf.laggr_lacptimer);
(void) snprintf(lbuf.laggr_flags, sizeof (lbuf.laggr_flags), "%c----",
ginfop->lg_force ? 'f' : '-');
if (!state->gs_parseable && !state->gs_printheader) {
print_header(&state->gs_print);
state->gs_printheader = B_TRUE;
}
dladm_print_output(&state->gs_print, state->gs_parseable,
dladm_print_field, (void *)&lbuf);
return (DLADM_STATUS_OK);
}
static char *
print_xaggr_callback(print_field_t *pf, void *arg)
{
const laggr_args_t *l = arg;
int portnum;
static char buf[DLADM_STRSIZE];
boolean_t is_port = (l->laggr_lport >= 0);
dladm_aggr_port_attr_t *portp;
dladm_phys_attr_t dpa;
dladm_status_t *stat, status;
stat = l->laggr_status;
*stat = DLADM_STATUS_OK;
if (is_port) {
portnum = l->laggr_lport;
portp = &(l->laggr_ginfop->lg_ports[portnum]);
if ((status = dladm_datalink_id2info(portp->lp_linkid,
NULL, NULL, NULL, buf, sizeof (buf))) !=
DLADM_STATUS_OK) {
goto err;
}
if ((status = dladm_phys_info(portp->lp_linkid, &dpa,
DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
goto err;
}
}
switch (pf->pf_index) {
case AGGR_X_LINK:
(void) snprintf(buf, sizeof (buf), "%s",
(is_port && !l->laggr_parseable ? " " : l->laggr_link));
break;
case AGGR_X_PORT:
if (is_port)
break;
return ("");
break;
case AGGR_X_SPEED:
if (is_port) {
(void) snprintf(buf, sizeof (buf), "%uMb",
(uint_t)((get_ifspeed(dpa.dp_dev,
B_FALSE)) / 1000000ull));
} else {
(void) snprintf(buf, sizeof (buf), "%uMb",
(uint_t)((get_ifspeed(l->laggr_link,
B_TRUE)) / 1000000ull));
}
break;
case AGGR_X_DUPLEX:
if (is_port)
(void) get_linkduplex(dpa.dp_dev, B_FALSE, buf);
else
(void) get_linkduplex(l->laggr_link, B_TRUE, buf);
break;
case AGGR_X_STATE:
if (is_port)
(void) get_linkstate(dpa.dp_dev, B_FALSE, buf);
else
(void) get_linkstate(l->laggr_link, B_TRUE, buf);
break;
case AGGR_X_ADDRESS:
(void) dladm_aggr_macaddr2str(
(is_port ? portp->lp_mac : l->laggr_ginfop->lg_mac),
buf);
break;
case AGGR_X_PORTSTATE:
if (is_port)
(void) dladm_aggr_portstate2str(
portp->lp_state, buf);
else
return ("");
break;
}
return (buf);
err:
*stat = status;
buf[0] = '\0';
return (buf);
}
static dladm_status_t
print_aggr_extended(show_grp_state_t *state, const char *link,
dladm_aggr_grp_attr_t *ginfop)
{
int i;
dladm_status_t status;
laggr_args_t largs;
if (!state->gs_parseable && !state->gs_printheader) {
print_header(&state->gs_print);
state->gs_printheader = B_TRUE;
}
largs.laggr_lport = -1;
largs.laggr_link = link;
largs.laggr_ginfop = ginfop;
largs.laggr_status = &status;
largs.laggr_parseable = state->gs_parseable;
dladm_print_output(&state->gs_print, state->gs_parseable,
print_xaggr_callback, &largs);
if (status != DLADM_STATUS_OK)
goto done;
for (i = 0; i < ginfop->lg_nports; i++) {
largs.laggr_lport = i;
dladm_print_output(&state->gs_print, state->gs_parseable,
print_xaggr_callback, &largs);
if (status != DLADM_STATUS_OK)
goto done;
}
status = DLADM_STATUS_OK;
done:
return (status);
}
static char *
print_lacp_callback(print_field_t *pf, void *arg)
{
const laggr_args_t *l = arg;
int portnum;
static char buf[DLADM_STRSIZE];
boolean_t is_port = (l->laggr_lport >= 0);
dladm_aggr_port_attr_t *portp;
dladm_status_t *stat, status;
aggr_lacp_state_t *lstate;
if (!is_port) {
return (NULL); /* cannot happen! */
}
stat = l->laggr_status;
portnum = l->laggr_lport;
portp = &(l->laggr_ginfop->lg_ports[portnum]);
if ((status = dladm_datalink_id2info(portp->lp_linkid,
NULL, NULL, NULL, buf, sizeof (buf))) != DLADM_STATUS_OK) {
goto err;
}
lstate = &(portp->lp_lacp_state);
switch (pf->pf_index) {
case AGGR_L_LINK:
(void) snprintf(buf, sizeof (buf), "%s",
(portnum > 0 ? "" : l->laggr_link));
break;
case AGGR_L_PORT:
break;
case AGGR_L_AGGREGATABLE:
(void) snprintf(buf, sizeof (buf), "%s",
(lstate->bit.aggregation ? "yes" : "no"));
break;
case AGGR_L_SYNC:
(void) snprintf(buf, sizeof (buf), "%s",
(lstate->bit.sync ? "yes" : "no"));
break;
case AGGR_L_COLL:
(void) snprintf(buf, sizeof (buf), "%s",
(lstate->bit.collecting ? "yes" : "no"));
break;
case AGGR_L_DIST:
(void) snprintf(buf, sizeof (buf), "%s",
(lstate->bit.distributing ? "yes" : "no"));
break;
case AGGR_L_DEFAULTED:
(void) snprintf(buf, sizeof (buf), "%s",
(lstate->bit.defaulted ? "yes" : "no"));
break;
case AGGR_L_EXPIRED:
(void) snprintf(buf, sizeof (buf), "%s",
(lstate->bit.expired ? "yes" : "no"));
break;
}
*stat = DLADM_STATUS_OK;
return (buf);
err:
*stat = status;
buf[0] = '\0';
return (buf);
}
static dladm_status_t
print_aggr_lacp(show_grp_state_t *state, const char *link,
dladm_aggr_grp_attr_t *ginfop)
{
int i;
dladm_status_t status;
laggr_args_t largs;
if (!state->gs_parseable && !state->gs_printheader) {
print_header(&state->gs_print);
state->gs_printheader = B_TRUE;
}
largs.laggr_link = link;
largs.laggr_ginfop = ginfop;
largs.laggr_status = &status;
for (i = 0; i < ginfop->lg_nports; i++) {
largs.laggr_lport = i;
dladm_print_output(&state->gs_print, state->gs_parseable,
print_lacp_callback, &largs);
if (status != DLADM_STATUS_OK)
goto done;
}
status = DLADM_STATUS_OK;
done:
return (status);
}
static char *
print_aggr_stats_callback(print_field_t *pf, void *arg)
{
const laggr_args_t *l = arg;
int portnum;
static char buf[DLADM_STRSIZE];
boolean_t is_port = (l->laggr_lport >= 0);
dladm_aggr_port_attr_t *portp;
dladm_phys_attr_t dpa;
dladm_status_t *stat, status;
pktsum_t port_stat, diff_stats;
stat = l->laggr_status;
*stat = DLADM_STATUS_OK;
if (is_port) {
portnum = l->laggr_lport;
portp = &(l->laggr_ginfop->lg_ports[portnum]);
if ((status = dladm_phys_info(portp->lp_linkid, &dpa,
DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
goto err;
}
get_mac_stats(dpa.dp_dev, &port_stat);
if ((status = dladm_datalink_id2info(portp->lp_linkid, NULL,
NULL, NULL, buf, sizeof (buf))) != DLADM_STATUS_OK) {
goto err;
}
stats_diff(&diff_stats, &port_stat, l->laggr_prevstats);
}
switch (pf->pf_index) {
case AGGR_S_LINK:
(void) snprintf(buf, sizeof (buf), "%s",
(is_port ? "" : l->laggr_link));
break;
case AGGR_S_PORT:
if (is_port)
break;
return ("");
break;
case AGGR_S_IPKTS:
if (is_port) {
(void) snprintf(buf, sizeof (buf), "%llu",
diff_stats.ipackets);
} else {
(void) snprintf(buf, sizeof (buf), "%llu",
l->laggr_pktsumtot->ipackets);
}
break;
case AGGR_S_RBYTES:
if (is_port) {
(void) snprintf(buf, sizeof (buf), "%llu",
diff_stats.rbytes);
} else {
(void) snprintf(buf, sizeof (buf), "%llu",
l->laggr_pktsumtot->rbytes);
}
break;
case AGGR_S_OPKTS:
if (is_port) {
(void) snprintf(buf, sizeof (buf), "%llu",
diff_stats.opackets);
} else {
(void) snprintf(buf, sizeof (buf), "%llu",
l->laggr_pktsumtot->opackets);
}
break;
case AGGR_S_OBYTES:
if (is_port) {
(void) snprintf(buf, sizeof (buf), "%llu",
diff_stats.obytes);
} else {
(void) snprintf(buf, sizeof (buf), "%llu",
l->laggr_pktsumtot->obytes);
}
break;
case AGGR_S_IPKTDIST:
if (is_port) {
(void) snprintf(buf, sizeof (buf), "%-6.1f",
(double)diff_stats.opackets/
(double)l->laggr_pktsumtot->ipackets * 100);
} else {
return ("");
}
break;
case AGGR_S_OPKTDIST:
if (is_port) {
(void) snprintf(buf, sizeof (buf), "%-6.1f",
(double)diff_stats.opackets/
(double)l->laggr_pktsumtot->opackets * 100);
} else {
return ("");
}
break;
}
return (buf);
err:
*stat = status;
buf[0] = '\0';
return (buf);
}
static dladm_status_t
print_aggr_stats(show_grp_state_t *state, const char *link,
dladm_aggr_grp_attr_t *ginfop)
{
dladm_phys_attr_t dpa;
dladm_aggr_port_attr_t *portp;
pktsum_t pktsumtot, port_stat;
dladm_status_t status;
int i;
laggr_args_t largs;
/* sum the ports statistics */
bzero(&pktsumtot, sizeof (pktsumtot));
for (i = 0; i < ginfop->lg_nports; i++) {
portp = &(ginfop->lg_ports[i]);
if ((status = dladm_phys_info(portp->lp_linkid, &dpa,
DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) {
goto done;
}
get_mac_stats(dpa.dp_dev, &port_stat);
stats_total(&pktsumtot, &port_stat, &state->gs_prevstats[i]);
}
if (!state->gs_parseable && !state->gs_printheader) {
print_header(&state->gs_print);
state->gs_printheader = B_TRUE;
}
largs.laggr_lport = -1;
largs.laggr_link = link;
largs.laggr_ginfop = ginfop;
largs.laggr_status = &status;
largs.laggr_pktsumtot = &pktsumtot;
dladm_print_output(&state->gs_print, state->gs_parseable,
print_aggr_stats_callback, &largs);
if (status != DLADM_STATUS_OK)
goto done;
for (i = 0; i < ginfop->lg_nports; i++) {
largs.laggr_lport = i;
largs.laggr_prevstats = &state->gs_prevstats[i];
dladm_print_output(&state->gs_print, state->gs_parseable,
print_aggr_stats_callback, &largs);
if (status != DLADM_STATUS_OK)
goto done;
}
status = DLADM_STATUS_OK;
done:
return (status);
}
static dladm_status_t
print_aggr(show_grp_state_t *state, datalink_id_t linkid)
{
char link[MAXLINKNAMELEN];
dladm_aggr_grp_attr_t ginfo;
uint32_t flags;
dladm_status_t status;
bzero(&ginfo, sizeof (dladm_aggr_grp_attr_t));
if ((status = dladm_datalink_id2info(linkid, &flags, NULL, NULL, link,
MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
return (status);
}
if (!(state->gs_flags & flags))
return (DLADM_STATUS_NOTFOUND);
status = dladm_aggr_info(linkid, &ginfo, state->gs_flags);
if (status != DLADM_STATUS_OK)
return (status);
if (state->gs_lacp)
status = print_aggr_lacp(state, link, &ginfo);
else if (state->gs_extended)
status = print_aggr_extended(state, link, &ginfo);
else if (state->gs_stats)
status = print_aggr_stats(state, link, &ginfo);
else {
status = print_aggr_info(state, link, &ginfo);
}
done:
free(ginfo.lg_ports);
return (status);
}
static int
show_aggr(datalink_id_t linkid, void *arg)
{
show_grp_state_t *state = arg;
dladm_status_t status;
status = print_aggr(state, linkid);
if (status != DLADM_STATUS_OK)
goto done;
done:
state->gs_status = status;
return (DLADM_WALK_CONTINUE);
}
static char *
print_dev(print_field_t *pf, void *arg)
{
const char *dev = arg;
static char buf[DLADM_STRSIZE];
switch (pf->pf_index) {
case DEV_LINK:
(void) snprintf(buf, sizeof (buf), "%s", dev);
break;
case DEV_STATE:
(void) get_linkstate(dev, B_FALSE, buf);
break;
case DEV_SPEED:
(void) snprintf(buf, sizeof (buf), "%uMb",
(unsigned int)(get_ifspeed(dev, B_FALSE) / 1000000ull));
break;
case DEV_DUPLEX:
(void) get_linkduplex(dev, B_FALSE, buf);
break;
default:
die("invalid index '%d'", pf->pf_index);
break;
}
return (buf);
}
static int
show_dev(const char *dev, void *arg)
{
show_state_t *state = arg;
if (!state->ls_parseable && !state->ls_printheader) {
print_header(&state->ls_print);
state->ls_printheader = B_TRUE;
}
dladm_print_output(&state->ls_print, state->ls_parseable,
print_dev, (void *)dev);
return (DLADM_WALK_CONTINUE);
}
static char *
print_dev_stats(print_field_t *pf, void *arg)
{
dev_args_t *dargs = arg;
pktsum_t *diff_stats = dargs->devs_psum;
static char buf[DLADM_STRSIZE];
switch (pf->pf_index) {
case DEVS_LINK:
(void) snprintf(buf, sizeof (buf), "%s", dargs->devs_link);
break;
case DEVS_IPKTS:
(void) snprintf(buf, sizeof (buf), "%llu",
diff_stats->ipackets);
break;
case DEVS_RBYTES:
(void) snprintf(buf, sizeof (buf), "%llu",
diff_stats->rbytes);
break;
case DEVS_IERRORS:
(void) snprintf(buf, sizeof (buf), "%u",
diff_stats->ierrors);
break;
case DEVS_OPKTS:
(void) snprintf(buf, sizeof (buf), "%llu",
diff_stats->opackets);
break;
case DEVS_OBYTES:
(void) snprintf(buf, sizeof (buf), "%llu",
diff_stats->obytes);
break;
case DEVS_OERRORS:
(void) snprintf(buf, sizeof (buf), "%u",
diff_stats->oerrors);
break;
default:
die("invalid input");
break;
}
return (buf);
}
static int
show_dev_stats(const char *dev, void *arg)
{
show_state_t *state = arg;
pktsum_t stats, diff_stats;
dev_args_t dargs;
if (state->ls_firstonly) {
if (state->ls_donefirst)
return (DLADM_WALK_CONTINUE);
state->ls_donefirst = B_TRUE;
} else {
bzero(&state->ls_prevstats, sizeof (state->ls_prevstats));
}
get_mac_stats(dev, &stats);
stats_diff(&diff_stats, &stats, &state->ls_prevstats);
dargs.devs_link = (char *)dev;
dargs.devs_psum = &diff_stats;
dladm_print_output(&state->ls_print, state->ls_parseable,
print_dev_stats, &dargs);
state->ls_prevstats = stats;
return (DLADM_WALK_CONTINUE);
}
static void
do_show_link(int argc, char *argv[], const char *use)
{
int option;
boolean_t s_arg = B_FALSE;
boolean_t i_arg = B_FALSE;
uint32_t flags = DLADM_OPT_ACTIVE;
boolean_t p_arg = B_FALSE;
datalink_id_t linkid = DATALINK_ALL_LINKID;
int interval = 0;
show_state_t state;
dladm_status_t status;
boolean_t o_arg = B_FALSE;
char *fields_str = NULL;
print_field_t **fields;
uint_t nfields;
char *all_active_fields = "link,class,mtu,state,over";
char *all_inactive_fields = "link,class,over";
char *allstat_fields =
"link,ipackets,rbytes,ierrors,opackets,obytes,oerrors";
bzero(&state, sizeof (state));
opterr = 0;
while ((option = getopt_long(argc, argv, ":pPsi:o:",
show_lopts, NULL)) != -1) {
switch (option) {
case 'p':
if (p_arg)
die_optdup(option);
p_arg = B_TRUE;
break;
case 's':
if (s_arg)
die_optdup(option);
s_arg = B_TRUE;
break;
case 'P':
if (flags != DLADM_OPT_ACTIVE)
die_optdup(option);
flags = DLADM_OPT_PERSIST;
break;
case 'o':
o_arg = B_TRUE;
fields_str = optarg;
break;
case 'i':
if (i_arg)
die_optdup(option);
i_arg = B_TRUE;
if (!str2int(optarg, &interval) || interval == 0)
die("invalid interval value '%s'", optarg);
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (i_arg && !s_arg)
die("the option -i can be used only with -s");
if (s_arg && flags != DLADM_OPT_ACTIVE)
die("the option -P cannot be used with -s");
/* get link name (optional last argument) */
if (optind == (argc-1)) {
uint32_t f;
if ((status = dladm_name2info(argv[optind], &linkid, &f,
NULL, NULL)) != DLADM_STATUS_OK) {
die_dlerr(status, "link %s is not valid", argv[optind]);
}
if (!(f & flags)) {
die_dlerr(DLADM_STATUS_BADARG, "link %s is %s",
argv[optind], flags == DLADM_OPT_PERSIST ?
"a temporary link" : "temporarily removed");
}
} else if (optind != argc) {
usage();
}
if (p_arg && !o_arg)
die("-p requires -o");
if (p_arg && strcasecmp(fields_str, "all") == 0)
die("\"-o all\" is invalid with -p");
if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
if (s_arg)
fields_str = allstat_fields;
else if (flags & DLADM_OPT_ACTIVE)
fields_str = all_active_fields;
else
fields_str = all_inactive_fields;
}
state.ls_parseable = p_arg;
state.ls_flags = flags;
state.ls_donefirst = B_FALSE;
if (s_arg) {
link_stats(linkid, interval, fields_str, &state);
return;
}
fields = parse_output_fields(fields_str, link_fields, DEV_LINK_FIELDS,
CMD_TYPE_ANY, &nfields);
if (fields == NULL)
die("invalid field(s) specified");
state.ls_print.ps_fields = fields;
state.ls_print.ps_nfields = nfields;
if (linkid == DATALINK_ALL_LINKID) {
(void) dladm_walk_datalink_id(show_link, &state,
DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags);
} else {
(void) show_link(linkid, &state);
if (state.ls_status != DLADM_STATUS_OK) {
die_dlerr(state.ls_status, "failed to show link %s",
argv[optind]);
}
}
}
static void
do_show_aggr(int argc, char *argv[], const char *use)
{
boolean_t L_arg = B_FALSE;
boolean_t s_arg = B_FALSE;
boolean_t i_arg = B_FALSE;
boolean_t p_arg = B_FALSE;
boolean_t x_arg = B_FALSE;
show_grp_state_t state;
uint32_t flags = DLADM_OPT_ACTIVE;
datalink_id_t linkid = DATALINK_ALL_LINKID;
int option;
int interval = 0;
int key;
dladm_status_t status;
boolean_t o_arg = B_FALSE;
char *fields_str = NULL;
print_field_t **fields;
uint_t nfields;
char *all_fields =
"link,policy,addrpolicy,lacpactivity,lacptimer,flags";
char *all_lacp_fields =
"link,port,aggregatable,sync,coll,dist,defaulted,expired";
char *all_stats_fields =
"link,port,ipackets,rbytes,opackets,obytes,ipktdist,opktdist";
char *all_extended_fields =
"link,port,speed,duplex,state,address,portstate";
print_field_t *pf;
int pfmax;
bzero(&state, sizeof (state));
opterr = 0;
while ((option = getopt_long(argc, argv, ":LpPxsi:o:",
show_lopts, NULL)) != -1) {
switch (option) {
case 'L':
if (L_arg)
die_optdup(option);
L_arg = B_TRUE;
break;
case 'p':
if (p_arg)
die_optdup(option);
p_arg = B_TRUE;
break;
case 'x':
if (x_arg)
die_optdup(option);
x_arg = B_TRUE;
break;
case 'P':
if (flags != DLADM_OPT_ACTIVE)
die_optdup(option);
flags = DLADM_OPT_PERSIST;
break;
case 's':
if (s_arg)
die_optdup(option);
s_arg = B_TRUE;
break;
case 'o':
o_arg = B_TRUE;
fields_str = optarg;
break;
case 'i':
if (i_arg)
die_optdup(option);
i_arg = B_TRUE;
if (!str2int(optarg, &interval) || interval == 0)
die("invalid interval value '%s'", optarg);
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (p_arg && !o_arg)
die("-p requires -o");
if (p_arg && strcasecmp(fields_str, "all") == 0)
die("\"-o all\" is invalid with -p");
if (i_arg && !s_arg)
die("the option -i can be used only with -s");
if (s_arg && (L_arg || p_arg || x_arg || flags != DLADM_OPT_ACTIVE)) {
die("the option -%c cannot be used with -s",
L_arg ? 'L' : (p_arg ? 'p' : (x_arg ? 'x' : 'P')));
}
if (L_arg && flags != DLADM_OPT_ACTIVE)
die("the option -P cannot be used with -L");
if (x_arg && (L_arg || flags != DLADM_OPT_ACTIVE))
die("the option -%c cannot be used with -x", L_arg ? 'L' : 'P');
/* get aggregation key or aggrname (optional last argument) */
if (optind == (argc-1)) {
if (!str2int(argv[optind], &key)) {
status = dladm_name2info(argv[optind], &linkid, NULL,
NULL, NULL);
} else {
status = dladm_key2linkid((uint16_t)key,
&linkid, DLADM_OPT_ACTIVE);
}
if (status != DLADM_STATUS_OK)
die("non-existent aggregation '%s'", argv[optind]);
} else if (optind != argc) {
usage();
}
bzero(&state, sizeof (state));
state.gs_lacp = L_arg;
state.gs_stats = s_arg;
state.gs_flags = flags;
state.gs_parseable = p_arg;
state.gs_extended = x_arg;
if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
if (state.gs_lacp)
fields_str = all_lacp_fields;
else if (state.gs_stats)
fields_str = all_stats_fields;
else if (state.gs_extended)
fields_str = all_extended_fields;
else
fields_str = all_fields;
}
if (state.gs_lacp) {
pf = aggr_l_fields;
pfmax = AGGR_L_MAX_FIELDS;
} else if (state.gs_stats) {
pf = aggr_s_fields;
pfmax = AGGR_S_MAX_FIELDS;
} else if (state.gs_extended) {
pf = aggr_x_fields;
pfmax = AGGR_X_MAX_FIELDS;
} else {
pf = laggr_fields;
pfmax = LAGGR_MAX_FIELDS;
}
fields = parse_output_fields(fields_str, pf, pfmax, CMD_TYPE_ANY,
&nfields);
if (fields == NULL) {
die("invalid field(s) specified");
return;
}
state.gs_print.ps_fields = fields;
state.gs_print.ps_nfields = nfields;
if (s_arg) {
aggr_stats(linkid, &state, interval);
return;
}
if (linkid == DATALINK_ALL_LINKID) {
(void) dladm_walk_datalink_id(show_aggr, &state,
DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags);
} else {
(void) show_aggr(linkid, &state);
if (state.gs_status != DLADM_STATUS_OK) {
die_dlerr(state.gs_status, "failed to show aggr %s",
argv[optind]);
}
}
}
static void
do_show_dev(int argc, char *argv[], const char *use)
{
int option;
char *dev = NULL;
boolean_t s_arg = B_FALSE;
boolean_t i_arg = B_FALSE;
boolean_t o_arg = B_FALSE;
boolean_t p_arg = B_FALSE;
datalink_id_t linkid;
int interval = 0;
show_state_t state;
char *fields_str = NULL;
print_field_t **fields;
uint_t nfields;
char *all_fields = "link,state,speed,duplex";
static char *allstat_fields =
"link,ipackets,rbytes,ierrors,opackets,obytes,oerrors";
bzero(&state, sizeof (state));
fields_str = all_fields;
opterr = 0;
while ((option = getopt_long(argc, argv, ":psi:o:",
show_lopts, NULL)) != -1) {
switch (option) {
case 'p':
if (p_arg)
die_optdup(option);
p_arg = B_TRUE;
break;
case 's':
if (s_arg)
die_optdup(option);
s_arg = B_TRUE;
break;
case 'o':
o_arg = B_TRUE;
fields_str = optarg;
break;
case 'i':
if (i_arg)
die_optdup(option);
i_arg = B_TRUE;
if (!str2int(optarg, &interval) || interval == 0)
die("invalid interval value '%s'", optarg);
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (p_arg && !o_arg)
die("-p requires -o");
if (p_arg && strcasecmp(fields_str, "all") == 0)
die("\"-o all\" is invalid with -p");
if (i_arg && !s_arg)
die("the option -i can be used only with -s");
if (o_arg && strcasecmp(fields_str, "all") == 0) {
if (!s_arg)
fields_str = all_fields;
else
fields_str = allstat_fields;
}
if (!o_arg && s_arg)
fields_str = allstat_fields;
if (s_arg && p_arg)
die("the option -s cannot be used with -p");
/* get dev name (optional last argument) */
if (optind == (argc-1)) {
uint32_t flags;
dev = argv[optind];
if (dladm_dev2linkid(dev, &linkid) != DLADM_STATUS_OK)
die("invalid device %s", dev);
if ((dladm_datalink_id2info(linkid, &flags, NULL, NULL,
NULL, 0) != DLADM_STATUS_OK) ||
!(flags & DLADM_OPT_ACTIVE)) {
die("device %s has been removed", dev);
}
} else if (optind != argc) {
usage();
}
state.ls_parseable = p_arg;
state.ls_donefirst = B_FALSE;
if (s_arg) {
dev_stats(dev, interval, fields_str, &state);
return;
}
fields = parse_output_fields(fields_str, dev_fields, DEV_MAX_FIELDS,
CMD_TYPE_ANY, &nfields);
if (fields == NULL) {
die("invalid field(s) specified");
return;
}
state.ls_print.ps_fields = fields;
state.ls_print.ps_nfields = nfields;
if (dev == NULL) {
(void) dladm_mac_walk(show_dev, &state);
} else {
(void) show_dev(dev, &state);
}
}
static dladm_status_t
print_phys(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *pattr)
{
char link[MAXLINKNAMELEN];
dladm_phys_attr_t dpa;
uint32_t flags;
datalink_class_t class;
uint32_t media;
dladm_status_t status;
if ((status = dladm_datalink_id2info(linkid, &flags, &class, &media,
link, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
goto done;
}
if (class != DATALINK_CLASS_PHYS) {
status = DLADM_STATUS_BADARG;
goto done;
}
if (!(state->ls_flags & flags)) {
status = DLADM_STATUS_NOTFOUND;
goto done;
}
status = dladm_phys_info(linkid, &dpa, state->ls_flags);
if (status != DLADM_STATUS_OK)
goto done;
(void) snprintf(pattr->link_phys_device,
sizeof (pattr->link_phys_device), "%s", dpa.dp_dev);
(void) dladm_media2str(media, pattr->link_phys_media);
if (state->ls_flags == DLADM_OPT_ACTIVE) {
boolean_t islink;
if (!dpa.dp_novanity) {
(void) strlcpy(pattr->link_name, link,
sizeof (pattr->link_name));
islink = B_TRUE;
} else {
/*
* This is a physical link that does not have
* vanity naming support.
*/
(void) strlcpy(pattr->link_name, dpa.dp_dev,
sizeof (pattr->link_name));
islink = B_FALSE;
}
(void) get_linkstate(pattr->link_name, islink,
pattr->link_phys_state);
(void) snprintf(pattr->link_phys_speed,
sizeof (pattr->link_phys_speed), "%u",
(uint_t)((get_ifspeed(pattr->link_name,
islink)) / 1000000ull));
(void) get_linkduplex(pattr->link_name, islink,
pattr->link_phys_duplex);
} else {
(void) snprintf(pattr->link_name, sizeof (pattr->link_name),
"%s", link);
(void) snprintf(pattr->link_flags, sizeof (pattr->link_flags),
"%c----", flags & DLADM_OPT_ACTIVE ? '-' : 'r');
}
done:
return (status);
}
static int
show_phys(datalink_id_t linkid, void *arg)
{
show_state_t *state = arg;
dladm_status_t status;
link_fields_buf_t pattr;
bzero(&pattr, sizeof (link_fields_buf_t));
status = print_phys(state, linkid, &pattr);
if (status != DLADM_STATUS_OK)
goto done;
if (!state->ls_parseable && !state->ls_printheader) {
print_header(&state->ls_print);
state->ls_printheader = B_TRUE;
}
dladm_print_output(&state->ls_print, state->ls_parseable,
dladm_print_field, (void *)&pattr);
done:
state->ls_status = status;
return (DLADM_WALK_CONTINUE);
}
/*
* Print the active topology information.
*/
static dladm_status_t
print_vlan(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *l)
{
dladm_vlan_attr_t vinfo;
uint32_t flags;
dladm_status_t status;
if ((status = dladm_datalink_id2info(linkid, &flags, NULL, NULL,
l->link_name, sizeof (l->link_name))) != DLADM_STATUS_OK) {
goto done;
}
if (!(state->ls_flags & flags)) {
status = DLADM_STATUS_NOTFOUND;
goto done;
}
if ((status = dladm_vlan_info(linkid, &vinfo, state->ls_flags)) !=
DLADM_STATUS_OK || (status = dladm_datalink_id2info(
vinfo.dv_linkid, NULL, NULL, NULL, l->link_over,
sizeof (l->link_over))) != DLADM_STATUS_OK) {
goto done;
}
(void) snprintf(l->link_vlan_vid, sizeof (l->link_vlan_vid), "%d",
vinfo.dv_vid);
(void) snprintf(l->link_flags, sizeof (l->link_flags), "%c%c---",
vinfo.dv_force ? 'f' : '-', vinfo.dv_implicit ? 'i' : '-');
done:
return (status);
}
static int
show_vlan(datalink_id_t linkid, void *arg)
{
show_state_t *state = arg;
dladm_status_t status;
link_fields_buf_t lbuf;
bzero(&lbuf, sizeof (link_fields_buf_t));
status = print_vlan(state, linkid, &lbuf);
if (status != DLADM_STATUS_OK)
goto done;
if (!state->ls_parseable && !state->ls_printheader) {
print_header(&state->ls_print);
state->ls_printheader = B_TRUE;
}
dladm_print_output(&state->ls_print, state->ls_parseable,
dladm_print_field, (void *)&lbuf);
done:
state->ls_status = status;
return (DLADM_WALK_CONTINUE);
}
static void
do_show_phys(int argc, char *argv[], const char *use)
{
int option;
uint32_t flags = DLADM_OPT_ACTIVE;
boolean_t p_arg = B_FALSE;
boolean_t o_arg = B_FALSE;
datalink_id_t linkid = DATALINK_ALL_LINKID;
show_state_t state;
dladm_status_t status;
char *fields_str = NULL;
print_field_t **fields;
uint_t nfields;
char *all_active_fields =
"link,media,state,speed,duplex,device";
char *all_inactive_fields = "link,device,media,flags";
bzero(&state, sizeof (state));
opterr = 0;
while ((option = getopt_long(argc, argv, ":pPo:",
show_lopts, NULL)) != -1) {
switch (option) {
case 'p':
if (p_arg)
die_optdup(option);
p_arg = B_TRUE;
break;
case 'P':
if (flags != DLADM_OPT_ACTIVE)
die_optdup(option);
flags = DLADM_OPT_PERSIST;
break;
case 'o':
o_arg = B_TRUE;
fields_str = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (p_arg && !o_arg)
die("-p requires -o");
if (p_arg && strcasecmp(fields_str, "all") == 0)
die("\"-o all\" is invalid with -p");
/* get link name (optional last argument) */
if (optind == (argc-1)) {
if ((status = dladm_name2info(argv[optind], &linkid, NULL,
NULL, NULL)) != DLADM_STATUS_OK) {
die_dlerr(status, "link %s is not valid", argv[optind]);
}
} else if (optind != argc) {
usage();
}
state.ls_parseable = p_arg;
state.ls_flags = flags;
state.ls_donefirst = B_FALSE;
if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
if (state.ls_flags & DLADM_OPT_ACTIVE)
fields_str = all_active_fields;
else
fields_str = all_inactive_fields;
}
fields = parse_output_fields(fields_str, phys_fields,
PHYS_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
if (fields == NULL) {
die("invalid field(s) specified");
return;
}
state.ls_print.ps_fields = fields;
state.ls_print.ps_nfields = nfields;
if (linkid == DATALINK_ALL_LINKID) {
(void) dladm_walk_datalink_id(show_phys, &state,
DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, flags);
} else {
(void) show_phys(linkid, &state);
if (state.ls_status != DLADM_STATUS_OK) {
die_dlerr(state.ls_status,
"failed to show physical link %s", argv[optind]);
}
}
}
static void
do_show_vlan(int argc, char *argv[], const char *use)
{
int option;
uint32_t flags = DLADM_OPT_ACTIVE;
boolean_t p_arg = B_FALSE;
datalink_id_t linkid = DATALINK_ALL_LINKID;
show_state_t state;
dladm_status_t status;
boolean_t o_arg = B_FALSE;
char *fields_str = NULL;
print_field_t **fields;
uint_t nfields;
char *all_fields = "link,vid,over,flags";
bzero(&state, sizeof (state));
opterr = 0;
while ((option = getopt_long(argc, argv, ":pPo:",
show_lopts, NULL)) != -1) {
switch (option) {
case 'p':
if (p_arg)
die_optdup(option);
p_arg = B_TRUE;
break;
case 'P':
if (flags != DLADM_OPT_ACTIVE)
die_optdup(option);
flags = DLADM_OPT_PERSIST;
break;
case 'o':
o_arg = B_TRUE;
fields_str = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (p_arg && !o_arg)
die("-p requires -o");
if (p_arg && strcasecmp(fields_str, "all") == 0)
die("\"-o all\" is invalid with -p");
/* get link name (optional last argument) */
if (optind == (argc-1)) {
if ((status = dladm_name2info(argv[optind], &linkid, NULL,
NULL, NULL)) != DLADM_STATUS_OK) {
die_dlerr(status, "link %s is not valid", argv[optind]);
}
} else if (optind != argc) {
usage();
}
state.ls_parseable = p_arg;
state.ls_flags = flags;
state.ls_donefirst = B_FALSE;
if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
fields_str = all_fields;
fields = parse_output_fields(fields_str, vlan_fields, VLAN_MAX_FIELDS,
CMD_TYPE_ANY, &nfields);
if (fields == NULL) {
die("invalid field(s) specified");
return;
}
state.ls_print.ps_fields = fields;
state.ls_print.ps_nfields = nfields;
if (linkid == DATALINK_ALL_LINKID) {
(void) dladm_walk_datalink_id(show_vlan, &state,
DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, flags);
} else {
(void) show_vlan(linkid, &state);
if (state.ls_status != DLADM_STATUS_OK) {
die_dlerr(state.ls_status, "failed to show vlan %s",
argv[optind]);
}
}
}
static void
link_stats(datalink_id_t linkid, uint_t interval, char *fields_str,
show_state_t *state)
{
print_field_t **fields;
uint_t nfields;
fields = parse_output_fields(fields_str, devs_fields, DEVS_MAX_FIELDS,
CMD_TYPE_ANY, &nfields);
if (fields == NULL) {
die("invalid field(s) specified");
return;
}
state->ls_print.ps_fields = fields;
state->ls_print.ps_nfields = nfields;
/*
* If an interval is specified, continuously show the stats
* only for the first MAC port.
*/
state->ls_firstonly = (interval != 0);
if (!state->ls_parseable)
print_header(&state->ls_print);
for (;;) {
state->ls_donefirst = B_FALSE;
if (linkid == DATALINK_ALL_LINKID) {
(void) dladm_walk_datalink_id(show_link_stats, state,
DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
DLADM_OPT_ACTIVE);
} else {
(void) show_link_stats(linkid, state);
}
if (interval == 0)
break;
(void) sleep(interval);
}
}
static void
aggr_stats(datalink_id_t linkid, show_grp_state_t *state, uint_t interval)
{
/*
* If an interval is specified, continuously show the stats
* only for the first group.
*/
state->gs_firstonly = (interval != 0);
for (;;) {
state->gs_donefirst = B_FALSE;
if (linkid == DATALINK_ALL_LINKID)
(void) dladm_walk_datalink_id(show_aggr, state,
DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
DLADM_OPT_ACTIVE);
else
(void) show_aggr(linkid, state);
if (interval == 0)
break;
(void) sleep(interval);
}
}
static void
dev_stats(const char *dev, uint32_t interval, char *fields_str,
show_state_t *state)
{
print_field_t **fields;
uint_t nfields;
fields = parse_output_fields(fields_str, devs_fields, DEVS_MAX_FIELDS,
CMD_TYPE_ANY, &nfields);
if (fields == NULL) {
die("invalid field(s) specified");
return;
}
state->ls_print.ps_fields = fields;
state->ls_print.ps_nfields = nfields;
/*
* If an interval is specified, continuously show the stats
* only for the first MAC port.
*/
state->ls_firstonly = (interval != 0);
for (;;) {
if (!state->ls_parseable)
print_header(&state->ls_print);
state->ls_donefirst = B_FALSE;
if (dev == NULL)
(void) dladm_mac_walk(show_dev_stats, state);
else
(void) show_dev_stats(dev, state);
if (interval == 0)
break;
(void) sleep(interval);
}
if (dev != NULL && state->ls_status != DLADM_STATUS_OK)
die_dlerr(state->ls_status, "cannot show device '%s'", dev);
}
/* accumulate stats (s1 += (s2 - s3)) */
static void
stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
{
s1->ipackets += (s2->ipackets - s3->ipackets);
s1->opackets += (s2->opackets - s3->opackets);
s1->rbytes += (s2->rbytes - s3->rbytes);
s1->obytes += (s2->obytes - s3->obytes);
s1->ierrors += (s2->ierrors - s3->ierrors);
s1->oerrors += (s2->oerrors - s3->oerrors);
}
/* compute stats differences (s1 = s2 - s3) */
static void
stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
{
s1->ipackets = s2->ipackets - s3->ipackets;
s1->opackets = s2->opackets - s3->opackets;
s1->rbytes = s2->rbytes - s3->rbytes;
s1->obytes = s2->obytes - s3->obytes;
s1->ierrors = s2->ierrors - s3->ierrors;
s1->oerrors = s2->oerrors - s3->oerrors;
}
static void
get_stats(char *module, int instance, const char *name, pktsum_t *stats)
{
kstat_ctl_t *kcp;
kstat_t *ksp;
if ((kcp = kstat_open()) == NULL) {
warn("kstat open operation failed");
return;
}
if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) {
/*
* The kstat query could fail if the underlying MAC
* driver was already detached.
*/
(void) kstat_close(kcp);
return;
}
if (kstat_read(kcp, ksp, NULL) == -1)
goto bail;
if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
&stats->ipackets) < 0)
goto bail;
if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
&stats->opackets) < 0)
goto bail;
if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
&stats->rbytes) < 0)
goto bail;
if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
&stats->obytes) < 0)
goto bail;
if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
&stats->ierrors) < 0)
goto bail;
if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
&stats->oerrors) < 0)
goto bail;
bail:
(void) kstat_close(kcp);
return;
}
static void
get_mac_stats(const char *dev, pktsum_t *stats)
{
char module[DLPI_LINKNAME_MAX];
uint_t instance;
bzero(stats, sizeof (*stats));
if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS)
return;
get_stats(module, instance, "mac", stats);
}
static void
get_link_stats(const char *link, pktsum_t *stats)
{
bzero(stats, sizeof (*stats));
get_stats("link", 0, link, stats);
}
static int
query_kstat(char *module, int instance, const char *name, const char *stat,
uint8_t type, void *val)
{
kstat_ctl_t *kcp;
kstat_t *ksp;
if ((kcp = kstat_open()) == NULL) {
warn("kstat open operation failed");
return (-1);
}
if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) {
/*
* The kstat query could fail if the underlying MAC
* driver was already detached.
*/
goto bail;
}
if (kstat_read(kcp, ksp, NULL) == -1) {
warn("kstat read failed");
goto bail;
}
if (dladm_kstat_value(ksp, stat, type, val) < 0)
goto bail;
(void) kstat_close(kcp);
return (0);
bail:
(void) kstat_close(kcp);
return (-1);
}
static int
get_one_kstat(const char *name, const char *stat, uint8_t type,
void *val, boolean_t islink)
{
char module[DLPI_LINKNAME_MAX];
uint_t instance;
if (islink) {
return (query_kstat("link", 0, name, stat, type, val));
} else {
if (dlpi_parselink(name, module, &instance) != DLPI_SUCCESS)
return (-1);
return (query_kstat(module, instance, "mac", stat, type, val));
}
}
static uint64_t
get_ifspeed(const char *name, boolean_t islink)
{
uint64_t ifspeed = 0;
(void) get_one_kstat(name, "ifspeed", KSTAT_DATA_UINT64,
&ifspeed, islink);
return (ifspeed);
}
static const char *
get_linkstate(const char *name, boolean_t islink, char *buf)
{
link_state_t linkstate;
if (get_one_kstat(name, "link_state", KSTAT_DATA_UINT32,
&linkstate, islink) != 0) {
(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
return (buf);
}
return (dladm_linkstate2str(linkstate, buf));
}
static const char *
get_linkduplex(const char *name, boolean_t islink, char *buf)
{
link_duplex_t linkduplex;
if (get_one_kstat(name, "link_duplex", KSTAT_DATA_UINT32,
&linkduplex, islink) != 0) {
(void) strlcpy(buf, "unknown", DLADM_STRSIZE);
return (buf);
}
return (dladm_linkduplex2str(linkduplex, buf));
}
typedef struct {
char *s_buf;
char **s_fields; /* array of pointer to the fields in s_buf */
uint_t s_nfields; /* the number of fields in s_buf */
} split_t;
/*
* Free the split_t structure pointed to by `sp'.
*/
static void
splitfree(split_t *sp)
{
free(sp->s_buf);
free(sp->s_fields);
free(sp);
}
/*
* Split `str' into at most `maxfields' fields, each field at most `maxlen' in
* length. Return a pointer to a split_t containing the split fields, or NULL
* on failure.
*/
static split_t *
split(const char *str, uint_t maxfields, uint_t maxlen)
{
char *field, *token, *lasts = NULL;
split_t *sp;
if (*str == '\0' || maxfields == 0 || maxlen == 0)
return (NULL);
sp = calloc(sizeof (split_t), 1);
if (sp == NULL)
return (NULL);
sp->s_buf = strdup(str);
sp->s_fields = malloc(sizeof (char *) * maxfields);
if (sp->s_buf == NULL || sp->s_fields == NULL)
goto fail;
token = sp->s_buf;
while ((field = strtok_r(token, ",", &lasts)) != NULL) {
if (sp->s_nfields == maxfields || strlen(field) > maxlen)
goto fail;
token = NULL;
sp->s_fields[sp->s_nfields++] = field;
}
return (sp);
fail:
splitfree(sp);
return (NULL);
}
static int
parse_wifi_fields(char *str, print_field_t ***fields, uint_t *countp,
uint_t cmdtype)
{
if (cmdtype == WIFI_CMD_SCAN) {
if (str == NULL)
str = def_scan_wifi_fields;
if (strcasecmp(str, "all") == 0)
str = all_scan_wifi_fields;
} else if (cmdtype == WIFI_CMD_SHOW) {
if (str == NULL)
str = def_show_wifi_fields;
if (strcasecmp(str, "all") == 0)
str = all_show_wifi_fields;
} else {
return (-1);
}
*fields = parse_output_fields(str, wifi_fields, WIFI_MAX_FIELDS,
cmdtype, countp);
if (*fields != NULL)
return (0);
return (-1);
}
static print_field_t **
parse_output_fields(char *str, print_field_t *template, int max_fields,
uint_t cmdtype, uint_t *countp)
{
split_t *sp;
boolean_t good_match = B_FALSE;
uint_t i, j;
print_field_t **pf = NULL;
sp = split(str, max_fields, MAX_FIELD_LEN);
if (sp == NULL)
return (NULL);
pf = malloc(sp->s_nfields * sizeof (print_field_t *));
if (pf == NULL)
goto fail;
for (i = 0; i < sp->s_nfields; i++) {
for (j = 0; j < max_fields; j++) {
if (strcasecmp(sp->s_fields[i],
template[j].pf_name) == 0) {
good_match = template[j]. pf_cmdtype & cmdtype;
break;
}
}
if (!good_match)
goto fail;
good_match = B_FALSE;
pf[i] = &template[j];
}
*countp = i;
splitfree(sp);
return (pf);
fail:
free(pf);
splitfree(sp);
return (NULL);
}
typedef struct print_wifi_state {
char *ws_link;
boolean_t ws_parseable;
boolean_t ws_header;
print_state_t ws_print_state;
} print_wifi_state_t;
typedef struct wlan_scan_args_s {
print_wifi_state_t *ws_state;
void *ws_attr;
} wlan_scan_args_t;
static void
print_field(print_state_t *statep, print_field_t *pfp, const char *value,
boolean_t parseable)
{
uint_t width = pfp->pf_width;
uint_t valwidth;
uint_t compress;
/*
* Parsable fields are separated by ':'. If such a field contains
* a ':' or '\', this character is prefixed by a '\'.
*/
if (parseable) {
char c;
if (statep->ps_nfields == 1) {
(void) printf("%s", value);
return;
}
while ((c = *value++) != '\0') {
if (c == ':' || c == '\\')
(void) putchar('\\');
(void) putchar(c);
}
if (!statep->ps_lastfield)
(void) putchar(':');
return;
} else {
if (value[0] == '\0')
value = STR_UNDEF_VAL;
if (statep->ps_lastfield) {
(void) printf("%s", value);
statep->ps_overflow = 0;
return;
}
valwidth = strlen(value);
if (valwidth > width) {
statep->ps_overflow += valwidth - width;
} else if (valwidth < width && statep->ps_overflow > 0) {
compress = min(statep->ps_overflow, width - valwidth);
statep->ps_overflow -= compress;
width -= compress;
}
(void) printf("%-*s", width, value);
}
if (!statep->ps_lastfield)
(void) putchar(' ');
}
static char *
print_wlan_attr(print_field_t *wfp, void *warg)
{
static char buf[DLADM_STRSIZE];
wlan_scan_args_t *w = warg;
print_wifi_state_t *statep = w->ws_state;
dladm_wlan_attr_t *attrp = w->ws_attr;
if (wfp->pf_index == 0) {
return ((char *)statep->ws_link);
}
if ((wfp->pf_index & attrp->wa_valid) == 0) {
return ("");
}
switch (wfp->pf_index) {
case DLADM_WLAN_ATTR_ESSID:
(void) dladm_wlan_essid2str(&attrp->wa_essid, buf);
break;
case DLADM_WLAN_ATTR_BSSID:
(void) dladm_wlan_bssid2str(&attrp->wa_bssid, buf);
break;
case DLADM_WLAN_ATTR_SECMODE:
(void) dladm_wlan_secmode2str(&attrp->wa_secmode, buf);
break;
case DLADM_WLAN_ATTR_STRENGTH:
(void) dladm_wlan_strength2str(&attrp->wa_strength, buf);
break;
case DLADM_WLAN_ATTR_MODE:
(void) dladm_wlan_mode2str(&attrp->wa_mode, buf);
break;
case DLADM_WLAN_ATTR_SPEED:
(void) dladm_wlan_speed2str(&attrp->wa_speed, buf);
(void) strlcat(buf, "Mb", sizeof (buf));
break;
case DLADM_WLAN_ATTR_AUTH:
(void) dladm_wlan_auth2str(&attrp->wa_auth, buf);
break;
case DLADM_WLAN_ATTR_BSSTYPE:
(void) dladm_wlan_bsstype2str(&attrp->wa_bsstype, buf);
break;
}
return (buf);
}
static boolean_t
print_scan_results(void *arg, dladm_wlan_attr_t *attrp)
{
print_wifi_state_t *statep = arg;
wlan_scan_args_t warg;
if (statep->ws_header) {
statep->ws_header = B_FALSE;
if (!statep->ws_parseable)
print_header(&statep->ws_print_state);
}
statep->ws_print_state.ps_overflow = 0;
bzero(&warg, sizeof (warg));
warg.ws_state = statep;
warg.ws_attr = attrp;
dladm_print_output(&statep->ws_print_state, statep->ws_parseable,
print_wlan_attr, &warg);
return (B_TRUE);
}
static int
scan_wifi(datalink_id_t linkid, void *arg)
{
print_wifi_state_t *statep = arg;
dladm_status_t status;
char link[MAXLINKNAMELEN];
if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL, link,
sizeof (link))) != DLADM_STATUS_OK) {
return (DLADM_WALK_CONTINUE);
}
statep->ws_link = link;
status = dladm_wlan_scan(linkid, statep, print_scan_results);
if (status != DLADM_STATUS_OK)
die_dlerr(status, "cannot scan link '%s'", statep->ws_link);
return (DLADM_WALK_CONTINUE);
}
static char *
print_link_attr(print_field_t *wfp, void *warg)
{
static char buf[DLADM_STRSIZE];
char *ptr;
wlan_scan_args_t *w = warg, w1;
print_wifi_state_t *statep = w->ws_state;
dladm_wlan_linkattr_t *attrp = w->ws_attr;
if (strcmp(wfp->pf_name, "status") == 0) {
if ((wfp->pf_index & attrp->la_valid) != 0)
(void) dladm_wlan_linkstatus2str(
&attrp->la_status, buf);
return (buf);
}
statep->ws_print_state.ps_overflow = 0;
bzero(&w1, sizeof (w1));
w1.ws_state = statep;
w1.ws_attr = &attrp->la_wlan_attr;
ptr = print_wlan_attr(wfp, &w1);
return (ptr);
}
static int
show_wifi(datalink_id_t linkid, void *arg)
{
print_wifi_state_t *statep = arg;
dladm_wlan_linkattr_t attr;
dladm_status_t status;
char link[MAXLINKNAMELEN];
wlan_scan_args_t warg;
if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL, link,
sizeof (link))) != DLADM_STATUS_OK) {
return (DLADM_WALK_CONTINUE);
}
/* dladm_wlan_get_linkattr() memsets attr with 0 */
status = dladm_wlan_get_linkattr(linkid, &attr);
if (status != DLADM_STATUS_OK)
die_dlerr(status, "cannot get link attributes for %s", link);
statep->ws_link = link;
if (statep->ws_header) {
statep->ws_header = B_FALSE;
if (!statep->ws_parseable)
print_header(&statep->ws_print_state);
}
statep->ws_print_state.ps_overflow = 0;
bzero(&warg, sizeof (warg));
warg.ws_state = statep;
warg.ws_attr = &attr;
dladm_print_output(&statep->ws_print_state, statep->ws_parseable,
print_link_attr, &warg);
return (DLADM_WALK_CONTINUE);
}
static void
do_display_wifi(int argc, char **argv, int cmd, const char *use)
{
int option;
char *fields_str = NULL;
print_field_t **fields;
int (*callback)(datalink_id_t, void *);
uint_t nfields;
print_wifi_state_t state;
datalink_id_t linkid = DATALINK_ALL_LINKID;
dladm_status_t status;
if (cmd == WIFI_CMD_SCAN)
callback = scan_wifi;
else if (cmd == WIFI_CMD_SHOW)
callback = show_wifi;
else
return;
state.ws_parseable = B_FALSE;
state.ws_header = B_TRUE;
opterr = 0;
while ((option = getopt_long(argc, argv, ":o:p",
wifi_longopts, NULL)) != -1) {
switch (option) {
case 'o':
fields_str = optarg;
break;
case 'p':
state.ws_parseable = B_TRUE;
break;
default:
die_opterr(optopt, option, use);
}
}
if (state.ws_parseable && fields_str == NULL)
die("-p requires -o");
if (state.ws_parseable && strcasecmp(fields_str, "all") == 0)
die("\"-o all\" is invalid with -p");
if (optind == (argc - 1)) {
if ((status = dladm_name2info(argv[optind], &linkid, NULL,
NULL, NULL)) != DLADM_STATUS_OK) {
die_dlerr(status, "link %s is not valid", argv[optind]);
}
} else if (optind != argc) {
usage();
}
if (parse_wifi_fields(fields_str, &fields, &nfields, cmd) < 0)
die("invalid field(s) specified");
bzero(&state.ws_print_state, sizeof (state.ws_print_state));
state.ws_print_state.ps_fields = fields;
state.ws_print_state.ps_nfields = nfields;
if (linkid == DATALINK_ALL_LINKID) {
(void) dladm_walk_datalink_id(callback, &state,
DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE);
} else {
(void) (*callback)(linkid, &state);
}
free(fields);
}
static void
do_scan_wifi(int argc, char **argv, const char *use)
{
do_display_wifi(argc, argv, WIFI_CMD_SCAN, use);
}
static void
do_show_wifi(int argc, char **argv, const char *use)
{
do_display_wifi(argc, argv, WIFI_CMD_SHOW, use);
}
typedef struct wlan_count_attr {
uint_t wc_count;
datalink_id_t wc_linkid;
} wlan_count_attr_t;
static int
do_count_wlan(datalink_id_t linkid, void *arg)
{
wlan_count_attr_t *cp = arg;
if (cp->wc_count == 0)
cp->wc_linkid = linkid;
cp->wc_count++;
return (DLADM_WALK_CONTINUE);
}
static int
parse_wlan_keys(char *str, dladm_wlan_key_t **keys, uint_t *key_countp)
{
uint_t i;
split_t *sp;
dladm_wlan_key_t *wk;
sp = split(str, DLADM_WLAN_MAX_WEPKEYS, DLADM_WLAN_MAX_KEYNAME_LEN);
if (sp == NULL)
return (-1);
wk = malloc(sp->s_nfields * sizeof (dladm_wlan_key_t));
if (wk == NULL)
goto fail;
for (i = 0; i < sp->s_nfields; i++) {
char *s;
dladm_secobj_class_t class;
dladm_status_t status;
(void) strlcpy(wk[i].wk_name, sp->s_fields[i],
DLADM_WLAN_MAX_KEYNAME_LEN);
wk[i].wk_idx = 1;
if ((s = strrchr(wk[i].wk_name, ':')) != NULL) {
if (s[1] == '\0' || s[2] != '\0' || !isdigit(s[1]))
goto fail;
wk[i].wk_idx = (uint_t)(s[1] - '0');
*s = '\0';
}
wk[i].wk_len = DLADM_WLAN_MAX_KEY_LEN;
status = dladm_get_secobj(wk[i].wk_name, &class,
wk[i].wk_val, &wk[i].wk_len, 0);
if (status != DLADM_STATUS_OK) {
if (status == DLADM_STATUS_NOTFOUND) {
status = dladm_get_secobj(wk[i].wk_name,
&class, wk[i].wk_val, &wk[i].wk_len,
DLADM_OPT_PERSIST);
}
if (status != DLADM_STATUS_OK)
goto fail;
}
wk[i].wk_class = class;
}
*keys = wk;
*key_countp = i;
splitfree(sp);
return (0);
fail:
free(wk);
splitfree(sp);
return (-1);
}
static void
do_connect_wifi(int argc, char **argv, const char *use)
{
int option;
dladm_wlan_attr_t attr, *attrp;
dladm_status_t status = DLADM_STATUS_OK;
int timeout = DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT;
datalink_id_t linkid = DATALINK_ALL_LINKID;
dladm_wlan_key_t *keys = NULL;
uint_t key_count = 0;
uint_t flags = 0;
dladm_wlan_secmode_t keysecmode = DLADM_WLAN_SECMODE_NONE;
char buf[DLADM_STRSIZE];
opterr = 0;
(void) memset(&attr, 0, sizeof (attr));
while ((option = getopt_long(argc, argv, ":e:i:a:m:b:s:k:T:c",
wifi_longopts, NULL)) != -1) {
switch (option) {
case 'e':
status = dladm_wlan_str2essid(optarg, &attr.wa_essid);
if (status != DLADM_STATUS_OK)
die("invalid ESSID '%s'", optarg);
attr.wa_valid |= DLADM_WLAN_ATTR_ESSID;
/*
* Try to connect without doing a scan.
*/
flags |= DLADM_WLAN_CONNECT_NOSCAN;
break;
case 'i':
status = dladm_wlan_str2bssid(optarg, &attr.wa_bssid);
if (status != DLADM_STATUS_OK)
die("invalid BSSID %s", optarg);
attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
break;
case 'a':
status = dladm_wlan_str2auth(optarg, &attr.wa_auth);
if (status != DLADM_STATUS_OK)
die("invalid authentication mode '%s'", optarg);
attr.wa_valid |= DLADM_WLAN_ATTR_AUTH;
break;
case 'm':
status = dladm_wlan_str2mode(optarg, &attr.wa_mode);
if (status != DLADM_STATUS_OK)
die("invalid mode '%s'", optarg);
attr.wa_valid |= DLADM_WLAN_ATTR_MODE;
break;
case 'b':
if ((status = dladm_wlan_str2bsstype(optarg,
&attr.wa_bsstype)) != DLADM_STATUS_OK) {
die("invalid bsstype '%s'", optarg);
}
attr.wa_valid |= DLADM_WLAN_ATTR_BSSTYPE;
break;
case 's':
if ((status = dladm_wlan_str2secmode(optarg,
&attr.wa_secmode)) != DLADM_STATUS_OK) {
die("invalid security mode '%s'", optarg);
}
attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
break;
case 'k':
if (parse_wlan_keys(optarg, &keys, &key_count) < 0)
die("invalid key(s) '%s'", optarg);
if (keys[0].wk_class == DLADM_SECOBJ_CLASS_WEP)
keysecmode = DLADM_WLAN_SECMODE_WEP;
else
keysecmode = DLADM_WLAN_SECMODE_WPA;
break;
case 'T':
if (strcasecmp(optarg, "forever") == 0) {
timeout = -1;
break;
}
if (!str2int(optarg, &timeout) || timeout < 0)
die("invalid timeout value '%s'", optarg);
break;
case 'c':
flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
flags |= DLADM_WLAN_CONNECT_CREATEIBSS;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (keysecmode == DLADM_WLAN_SECMODE_NONE) {
if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0) {
die("key required for security mode '%s'",
dladm_wlan_secmode2str(&attr.wa_secmode, buf));
}
} else {
if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0 &&
attr.wa_secmode != keysecmode)
die("incompatible -s and -k options");
attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
attr.wa_secmode = keysecmode;
}
if (optind == (argc - 1)) {
if ((status = dladm_name2info(argv[optind], &linkid, NULL,
NULL, NULL)) != DLADM_STATUS_OK) {
die_dlerr(status, "link %s is not valid", argv[optind]);
}
} else if (optind != argc) {
usage();
}
if (linkid == DATALINK_ALL_LINKID) {
wlan_count_attr_t wcattr;
wcattr.wc_linkid = DATALINK_INVALID_LINKID;
wcattr.wc_count = 0;
(void) dladm_walk_datalink_id(do_count_wlan, &wcattr,
DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE);
if (wcattr.wc_count == 0) {
die("no wifi links are available");
} else if (wcattr.wc_count > 1) {
die("link name is required when more than one wifi "
"link is available");
}
linkid = wcattr.wc_linkid;
}
attrp = (attr.wa_valid == 0) ? NULL : &attr;
again:
if ((status = dladm_wlan_connect(linkid, attrp, timeout, keys,
key_count, flags)) != DLADM_STATUS_OK) {
if ((flags & DLADM_WLAN_CONNECT_NOSCAN) != 0) {
/*
* Try again with scanning and filtering.
*/
flags &= ~DLADM_WLAN_CONNECT_NOSCAN;
goto again;
}
if (status == DLADM_STATUS_NOTFOUND) {
if (attr.wa_valid == 0) {
die("no wifi networks are available");
} else {
die("no wifi networks with the specified "
"criteria are available");
}
}
die_dlerr(status, "cannot connect");
}
free(keys);
}
/* ARGSUSED */
static int
do_all_disconnect_wifi(datalink_id_t linkid, void *arg)
{
dladm_status_t status;
status = dladm_wlan_disconnect(linkid);
if (status != DLADM_STATUS_OK)
warn_dlerr(status, "cannot disconnect link");
return (DLADM_WALK_CONTINUE);
}
static void
do_disconnect_wifi(int argc, char **argv, const char *use)
{
int option;
datalink_id_t linkid = DATALINK_ALL_LINKID;
boolean_t all_links = B_FALSE;
dladm_status_t status;
wlan_count_attr_t wcattr;
opterr = 0;
while ((option = getopt_long(argc, argv, ":a",
wifi_longopts, NULL)) != -1) {
switch (option) {
case 'a':
all_links = B_TRUE;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (optind == (argc - 1)) {
if ((status = dladm_name2info(argv[optind], &linkid, NULL,
NULL, NULL)) != DLADM_STATUS_OK) {
die_dlerr(status, "link %s is not valid", argv[optind]);
}
} else if (optind != argc) {
usage();
}
if (linkid == DATALINK_ALL_LINKID) {
if (!all_links) {
wcattr.wc_linkid = linkid;
wcattr.wc_count = 0;
(void) dladm_walk_datalink_id(do_count_wlan, &wcattr,
DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE);
if (wcattr.wc_count == 0) {
die("no wifi links are available");
} else if (wcattr.wc_count > 1) {
die("link name is required when more than "
"one wifi link is available");
}
linkid = wcattr.wc_linkid;
} else {
(void) dladm_walk_datalink_id(do_all_disconnect_wifi,
NULL, DATALINK_CLASS_PHYS, DL_WIFI,
DLADM_OPT_ACTIVE);
return;
}
}
status = dladm_wlan_disconnect(linkid);
if (status != DLADM_STATUS_OK)
die_dlerr(status, "cannot disconnect");
}
static void
free_props(prop_list_t *list)
{
if (list != NULL) {
free(list->pl_buf);
free(list);
}
}
static int
parse_props(char *str, prop_list_t **listp, boolean_t novalues)
{
prop_list_t *list;
prop_info_t *pip;
char *buf, *curr;
int len, i;
list = malloc(sizeof (prop_list_t));
if (list == NULL)
return (-1);
list->pl_count = 0;
list->pl_buf = buf = strdup(str);
if (buf == NULL)
goto fail;
/*
* buf is a string of form [<propname>=<value>][,<propname>=<value>]+
* where each <value> string itself could be a comma-separated array.
* The loop below will count the number of propname assignments
* in pl_count; for each property, there is a pip entry with
* pi_name == <propname>, pi_count == # of elements in <value> array.
* pi_val[] contains the actual values.
*
* This could really be a combination of calls to
* strtok (token delimiter is ",") and strchr (chr '=')
* with appropriate null/string-bound-checks.
*/
curr = buf;
len = strlen(buf);
pip = NULL;
for (i = 0; i < len; i++) {
char c = buf[i];
boolean_t match = (c == '=' || c == ',');
if (!match && i != len - 1)
continue;
if (match) {
buf[i] = '\0';
if (*curr == '\0')
goto fail;
}
if (pip != NULL && c != '=') {
if (pip->pi_count > DLADM_MAX_PROP_VALCNT)
goto fail;
if (novalues)
goto fail;
pip->pi_val[pip->pi_count] = curr;
pip->pi_count++;
} else {
if (list->pl_count > MAX_PROPS)
goto fail;
pip = &list->pl_info[list->pl_count];
pip->pi_name = curr;
pip->pi_count = 0;
list->pl_count++;
if (c == ',')
pip = NULL;
}
curr = buf + i + 1;
}
*listp = list;
return (0);
fail:
free_props(list);
return (-1);
}
static void
print_linkprop(datalink_id_t linkid, show_linkprop_state_t *statep,
const char *propname, dladm_prop_type_t type,
const char *format, char **pptr)
{
int i;
char *ptr, *lim;
char buf[DLADM_STRSIZE];
char *unknown = "?", *notsup = "";
char **propvals = statep->ls_propvals;
uint_t valcnt = DLADM_MAX_PROP_VALCNT;
dladm_status_t status;
status = dladm_get_linkprop(linkid, type, propname, propvals, &valcnt);
if (status != DLADM_STATUS_OK) {
if (status == DLADM_STATUS_TEMPONLY) {
if (type == DLADM_PROP_VAL_MODIFIABLE &&
statep->ls_persist) {
valcnt = 1;
propvals = &unknown;
} else {
statep->ls_status = status;
statep->ls_retstatus = status;
return;
}
} else if (status == DLADM_STATUS_NOTSUP ||
statep->ls_persist) {
valcnt = 1;
if (type == DLADM_PROP_VAL_CURRENT)
propvals = &unknown;
else
propvals = &notsup;
} else {
if (statep->ls_proplist &&
statep->ls_status == DLADM_STATUS_OK) {
warn_dlerr(status,
"cannot get link property '%s' for %s",
propname, statep->ls_link);
}
statep->ls_status = status;
statep->ls_retstatus = status;
return;
}
}
statep->ls_status = DLADM_STATUS_OK;
ptr = buf;
lim = buf + DLADM_STRSIZE;
for (i = 0; i < valcnt; i++) {
if (propvals[i][0] == '\0' && !statep->ls_parseable)
ptr += snprintf(ptr, lim - ptr, STR_UNDEF_VAL",");
else
ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
if (ptr >= lim)
break;
}
if (valcnt > 0)
buf[strlen(buf) - 1] = '\0';
lim = statep->ls_line + MAX_PROP_LINE;
if (statep->ls_parseable) {
*pptr += snprintf(*pptr, lim - *pptr,
"%s", buf);
} else {
*pptr += snprintf(*pptr, lim - *pptr, format, buf);
}
}
static char *
linkprop_callback(print_field_t *pf, void *ls_arg)
{
linkprop_args_t *arg = ls_arg;
char *propname = arg->ls_propname;
show_linkprop_state_t *statep = arg->ls_state;
char *ptr = statep->ls_line;
char *lim = ptr + MAX_PROP_LINE;
datalink_id_t linkid = arg->ls_linkid;
switch (pf->pf_index) {
case LINKPROP_LINK:
(void) snprintf(ptr, lim - ptr, "%s", statep->ls_link);
break;
case LINKPROP_PROPERTY:
(void) snprintf(ptr, lim - ptr, "%s", propname);
break;
case LINKPROP_VALUE:
print_linkprop(linkid, statep, propname,
statep->ls_persist ? DLADM_PROP_VAL_PERSISTENT :
DLADM_PROP_VAL_CURRENT, "%s", &ptr);
/*
* If we failed to query the link property, for example, query
* the persistent value of a non-persistable link property,
* simply skip the output.
*/
if (statep->ls_status != DLADM_STATUS_OK)
goto skip;
ptr = statep->ls_line;
break;
case LINKPROP_DEFAULT:
print_linkprop(linkid, statep, propname,
DLADM_PROP_VAL_DEFAULT, "%s", &ptr);
if (statep->ls_status != DLADM_STATUS_OK)
goto skip;
ptr = statep->ls_line;
break;
case LINKPROP_POSSIBLE:
print_linkprop(linkid, statep, propname,
DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr);
if (statep->ls_status != DLADM_STATUS_OK)
goto skip;
ptr = statep->ls_line;
break;
default:
die("invalid input");
break;
}
return (ptr);
skip:
if (statep->ls_status != DLADM_STATUS_OK)
return (NULL);
else
return ("");
}
static boolean_t
linkprop_is_supported(datalink_id_t linkid, const char *propname,
show_linkprop_state_t *statep)
{
dladm_status_t status;
uint_t valcnt = DLADM_MAX_PROP_VALCNT;
status = dladm_get_linkprop(linkid, DLADM_PROP_VAL_DEFAULT,
propname, statep->ls_propvals, &valcnt);
return (status != DLADM_STATUS_NOTSUP);
}
static int
show_linkprop(datalink_id_t linkid, const char *propname, void *arg)
{
show_linkprop_state_t *statep = arg;
linkprop_args_t ls_arg;
bzero(&ls_arg, sizeof (ls_arg));
ls_arg.ls_state = statep;
ls_arg.ls_propname = (char *)propname;
ls_arg.ls_linkid = linkid;
if (statep->ls_header) {
statep->ls_header = B_FALSE;
if (!statep->ls_parseable)
print_header(&statep->ls_print);
}
if (!linkprop_is_supported(linkid, propname, statep))
return (DLADM_WALK_CONTINUE);
dladm_print_output(&statep->ls_print, statep->ls_parseable,
linkprop_callback, (void *)&ls_arg);
return (DLADM_WALK_CONTINUE);
}
static void
do_show_linkprop(int argc, char **argv, const char *use)
{
int option;
prop_list_t *proplist = NULL;
datalink_id_t linkid = DATALINK_ALL_LINKID;
show_linkprop_state_t state;
uint32_t flags = DLADM_OPT_ACTIVE;
dladm_status_t status;
char *fields_str = NULL;
print_field_t **fields;
uint_t nfields;
boolean_t o_arg = B_FALSE;
char *all_fields =
"link,property,value,default,possible";
fields_str = all_fields;
opterr = 0;
state.ls_propvals = NULL;
state.ls_line = NULL;
state.ls_parseable = B_FALSE;
state.ls_persist = B_FALSE;
state.ls_header = B_TRUE;
state.ls_retstatus = DLADM_STATUS_OK;
while ((option = getopt_long(argc, argv, ":p:cPo:",
prop_longopts, NULL)) != -1) {
switch (option) {
case 'p':
if (parse_props(optarg, &proplist, B_TRUE) < 0)
die("invalid link properties specified");
break;
case 'c':
state.ls_parseable = B_TRUE;
break;
case 'P':
state.ls_persist = B_TRUE;
flags = DLADM_OPT_PERSIST;
break;
case 'o':
o_arg = B_TRUE;
if (strcasecmp(optarg, "all") == 0)
fields_str = all_fields;
else
fields_str = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (state.ls_parseable && !o_arg)
die("-c requires -o");
if (state.ls_parseable && fields_str == all_fields)
die("\"-o all\" is invalid with -c");
if (optind == (argc - 1)) {
if ((status = dladm_name2info(argv[optind], &linkid, NULL,
NULL, NULL)) != DLADM_STATUS_OK) {
die_dlerr(status, "link %s is not valid", argv[optind]);
}
} else if (optind != argc) {
usage();
}
bzero(&state.ls_print, sizeof (print_state_t));
state.ls_proplist = proplist;
state.ls_status = DLADM_STATUS_OK;
fields = parse_output_fields(fields_str, linkprop_fields,
LINKPROP_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
if (fields == NULL) {
die("invalid field(s) specified");
return;
}
state.ls_print.ps_fields = fields;
state.ls_print.ps_nfields = nfields;
if (linkid == DATALINK_ALL_LINKID) {
(void) dladm_walk_datalink_id(show_linkprop_onelink, &state,
DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags);
} else {
(void) show_linkprop_onelink(linkid, &state);
}
free_props(proplist);
if (state.ls_retstatus != DLADM_STATUS_OK)
exit(EXIT_FAILURE);
}
static int
show_linkprop_onelink(datalink_id_t linkid, void *arg)
{
int i;
char *buf;
uint32_t flags;
prop_list_t *proplist = NULL;
show_linkprop_state_t *statep = arg;
dlpi_handle_t dh = NULL;
statep->ls_status = DLADM_STATUS_OK;
if (dladm_datalink_id2info(linkid, &flags, NULL, NULL, statep->ls_link,
MAXLINKNAMELEN) != DLADM_STATUS_OK) {
statep->ls_status = DLADM_STATUS_NOTFOUND;
return (DLADM_WALK_CONTINUE);
}
if ((statep->ls_persist && !(flags & DLADM_OPT_PERSIST)) ||
(!statep->ls_persist && !(flags & DLADM_OPT_ACTIVE))) {
statep->ls_status = DLADM_STATUS_BADARG;
return (DLADM_WALK_CONTINUE);
}
proplist = statep->ls_proplist;
/*
* When some WiFi links are opened for the first time, their hardware
* automatically scans for APs and does other slow operations. Thus,
* if there are no open links, the retrieval of link properties
* (below) will proceed slowly unless we hold the link open.
*
* Note that failure of dlpi_open() does not necessarily mean invalid
* link properties, because dlpi_open() may fail because of incorrect
* autopush configuration. Therefore, we ingore the return value of
* dlpi_open().
*/
if (!statep->ls_persist)
(void) dlpi_open(statep->ls_link, &dh, 0);
buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE);
if (buf == NULL)
die("insufficient memory");
statep->ls_propvals = (char **)(void *)buf;
for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
statep->ls_propvals[i] = buf +
sizeof (char *) * DLADM_MAX_PROP_VALCNT +
i * DLADM_PROP_VAL_MAX;
}
statep->ls_line = buf +
(sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
if (proplist != NULL) {
for (i = 0; i < proplist->pl_count; i++) {
(void) show_linkprop(linkid,
proplist->pl_info[i].pi_name, statep);
}
} else {
(void) dladm_walk_linkprop(linkid, statep, show_linkprop);
}
if (dh != NULL)
dlpi_close(dh);
free(buf);
return (DLADM_WALK_CONTINUE);
}
static dladm_status_t
set_linkprop_persist(datalink_id_t linkid, const char *prop_name,
char **prop_val, uint_t val_cnt, boolean_t reset)
{
dladm_status_t status;
status = dladm_set_linkprop(linkid, prop_name, prop_val, val_cnt,
DLADM_OPT_PERSIST);
if (status != DLADM_STATUS_OK) {
warn_dlerr(status, "cannot persistently %s link property",
reset ? "reset" : "set");
}
return (status);
}
static void
set_linkprop(int argc, char **argv, boolean_t reset, const char *use)
{
int i, option;
char errmsg[DLADM_STRSIZE];
char *altroot = NULL;
datalink_id_t linkid;
prop_list_t *proplist = NULL;
boolean_t temp = B_FALSE;
dladm_status_t status = DLADM_STATUS_OK;
opterr = 0;
while ((option = getopt_long(argc, argv, ":p:R:t",
prop_longopts, NULL)) != -1) {
switch (option) {
case 'p':
if (parse_props(optarg, &proplist, reset) < 0)
die("invalid link properties specified");
break;
case 't':
temp = B_TRUE;
break;
case 'R':
altroot = optarg;
break;
default:
die_opterr(optopt, option, use);
}
}
/* get link name (required last argument) */
if (optind != (argc - 1))
usage();
if (proplist == NULL && !reset)
die("link property must be specified");
if (altroot != NULL) {
free_props(proplist);
altroot_cmd(altroot, argc, argv);
}
status = dladm_name2info(argv[optind], &linkid, NULL, NULL, NULL);
if (status != DLADM_STATUS_OK)
die_dlerr(status, "link %s is not valid", argv[optind]);
if (proplist == NULL) {
status = dladm_set_linkprop(linkid, NULL, NULL, 0,
DLADM_OPT_ACTIVE);
if (status != DLADM_STATUS_OK) {
warn_dlerr(status, "cannot reset link property "
"on '%s'", argv[optind]);
}
if (!temp) {
dladm_status_t s;
s = set_linkprop_persist(linkid, NULL, NULL, 0, reset);
if (s != DLADM_STATUS_OK)
status = s;
}
goto done;
}
for (i = 0; i < proplist->pl_count; i++) {
prop_info_t *pip = &proplist->pl_info[i];
char **val;
uint_t count;
dladm_status_t s;
if (reset) {
val = NULL;
count = 0;
} else {
val = pip->pi_val;
count = pip->pi_count;
if (count == 0) {
warn("no value specified for '%s'",
pip->pi_name);
status = DLADM_STATUS_BADARG;
continue;
}
}
s = dladm_set_linkprop(linkid, pip->pi_name, val, count,
DLADM_OPT_ACTIVE);
if (s == DLADM_STATUS_OK) {
if (!temp) {
s = set_linkprop_persist(linkid,
pip->pi_name, val, count, reset);
if (s != DLADM_STATUS_OK)
status = s;
}
continue;
}
status = s;
switch (s) {
case DLADM_STATUS_NOTFOUND:
warn("invalid link property '%s'", pip->pi_name);
break;
case DLADM_STATUS_BADVAL: {
int j;
char *ptr, *lim;
char **propvals = NULL;
uint_t valcnt = DLADM_MAX_PROP_VALCNT;
ptr = malloc((sizeof (char *) +
DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT +
MAX_PROP_LINE);
propvals = (char **)(void *)ptr;
if (propvals == NULL)
die("insufficient memory");
for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
propvals[j] = ptr + sizeof (char *) *
DLADM_MAX_PROP_VALCNT +
j * DLADM_PROP_VAL_MAX;
}
s = dladm_get_linkprop(linkid,
DLADM_PROP_VAL_MODIFIABLE, pip->pi_name, propvals,
&valcnt);
if (s != DLADM_STATUS_OK) {
warn_dlerr(status, "cannot set link property "
"'%s' on '%s'", pip->pi_name, argv[optind]);
free(propvals);
break;
}
ptr = errmsg;
lim = ptr + DLADM_STRSIZE;
*ptr = '\0';
for (j = 0; j < valcnt; j++) {
ptr += snprintf(ptr, lim - ptr, "%s,",
propvals[j]);
if (ptr >= lim)
break;
}
if (ptr > errmsg) {
*(ptr - 1) = '\0';
warn("link property '%s' must be one of: %s",
pip->pi_name, errmsg);
} else
warn("invalid link property '%s'", *val);
free(propvals);
break;
}
default:
if (reset) {
warn_dlerr(status, "cannot reset link property "
"'%s' on '%s'", pip->pi_name, argv[optind]);
} else {
warn_dlerr(status, "cannot set link property "
"'%s' on '%s'", pip->pi_name, argv[optind]);
}
break;
}
}
done:
free_props(proplist);
if (status != DLADM_STATUS_OK)
exit(1);
}
static void
do_set_linkprop(int argc, char **argv, const char *use)
{
set_linkprop(argc, argv, B_FALSE, use);
}
static void
do_reset_linkprop(int argc, char **argv, const char *use)
{
set_linkprop(argc, argv, B_TRUE, use);
}
static int
convert_secobj(char *buf, uint_t len, uint8_t *obj_val, uint_t *obj_lenp,
dladm_secobj_class_t class)
{
int error = 0;
if (class == DLADM_SECOBJ_CLASS_WPA) {
if (len < 8 || len > 63)
return (EINVAL);
(void) memcpy(obj_val, buf, len);
*obj_lenp = len;
return (error);
}
if (class == DLADM_SECOBJ_CLASS_WEP) {
switch (len) {
case 5: /* ASCII key sizes */
case 13:
(void) memcpy(obj_val, buf, len);
*obj_lenp = len;
break;
case 10: /* Hex key sizes, not preceded by 0x */
case 26:
error = hexascii_to_octet(buf, len, obj_val, obj_lenp);
break;
case 12: /* Hex key sizes, preceded by 0x */
case 28:
if (strncmp(buf, "0x", 2) != 0)
return (EINVAL);
error = hexascii_to_octet(buf + 2, len - 2,
obj_val, obj_lenp);
break;
default:
return (EINVAL);
}
return (error);
}
return (ENOENT);
}
/* ARGSUSED */
static void
defersig(int sig)
{
signalled = sig;
}
static int
get_secobj_from_tty(uint_t try, const char *objname, char *buf)
{
uint_t len = 0;
int c;
struct termios stored, current;
void (*sigfunc)(int);
/*
* Turn off echo -- but before we do so, defer SIGINT handling
* so that a ^C doesn't leave the terminal corrupted.
*/
sigfunc = signal(SIGINT, defersig);
(void) fflush(stdin);
(void) tcgetattr(0, &stored);
current = stored;
current.c_lflag &= ~(ICANON|ECHO);
current.c_cc[VTIME] = 0;
current.c_cc[VMIN] = 1;
(void) tcsetattr(0, TCSANOW, &current);
again:
if (try == 1)
(void) printf(gettext("provide value for '%s': "), objname);
else
(void) printf(gettext("confirm value for '%s': "), objname);
(void) fflush(stdout);
while (signalled == 0) {
c = getchar();
if (c == '\n' || c == '\r') {
if (len != 0)
break;
(void) putchar('\n');
goto again;
}
buf[len++] = c;
if (len >= DLADM_SECOBJ_VAL_MAX - 1)
break;
(void) putchar('*');
}
(void) putchar('\n');
(void) fflush(stdin);
/*
* Restore terminal setting and handle deferred signals.
*/
(void) tcsetattr(0, TCSANOW, &stored);
(void) signal(SIGINT, sigfunc);
if (signalled != 0)
(void) kill(getpid(), signalled);
return (len);
}
static int
get_secobj_val(char *obj_name, uint8_t *obj_val, uint_t *obj_lenp,
dladm_secobj_class_t class, FILE *filep)
{
int rval;
uint_t len, len2;
char buf[DLADM_SECOBJ_VAL_MAX], buf2[DLADM_SECOBJ_VAL_MAX];
if (filep == NULL) {
len = get_secobj_from_tty(1, obj_name, buf);
rval = convert_secobj(buf, len, obj_val, obj_lenp, class);
if (rval == 0) {
len2 = get_secobj_from_tty(2, obj_name, buf2);
if (len != len2 || memcmp(buf, buf2, len) != 0)
rval = ENOTSUP;
}
return (rval);
} else {
for (;;) {
if (fgets(buf, sizeof (buf), filep) == NULL)
break;
if (isspace(buf[0]))
continue;
len = strlen(buf);
if (buf[len - 1] == '\n') {
buf[len - 1] = '\0';
len--;
}
break;
}
(void) fclose(filep);
}
return (convert_secobj(buf, len, obj_val, obj_lenp, class));
}
static boolean_t
check_auth(const char *auth)
{
struct passwd *pw;
if ((pw = getpwuid(getuid())) == NULL)
return (B_FALSE);
return (chkauthattr(auth, pw->pw_name) != 0);
}
static void
audit_secobj(char *auth, char *class, char *obj,
boolean_t success, boolean_t create)
{
adt_session_data_t *ah;
adt_event_data_t *event;
au_event_t flag;
char *errstr;
if (create) {
flag = ADT_dladm_create_secobj;
errstr = "ADT_dladm_create_secobj";
} else {
flag = ADT_dladm_delete_secobj;
errstr = "ADT_dladm_delete_secobj";
}
if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0)
die("adt_start_session: %s", strerror(errno));
if ((event = adt_alloc_event(ah, flag)) == NULL)
die("adt_alloc_event (%s): %s", errstr, strerror(errno));
/* fill in audit info */
if (create) {
event->adt_dladm_create_secobj.auth_used = auth;
event->adt_dladm_create_secobj.obj_class = class;
event->adt_dladm_create_secobj.obj_name = obj;
} else {
event->adt_dladm_delete_secobj.auth_used = auth;
event->adt_dladm_delete_secobj.obj_class = class;
event->adt_dladm_delete_secobj.obj_name = obj;
}
if (success) {
if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
die("adt_put_event (%s, success): %s", errstr,
strerror(errno));
}
} else {
if (adt_put_event(event, ADT_FAILURE,
ADT_FAIL_VALUE_AUTH) != 0) {
die("adt_put_event: (%s, failure): %s", errstr,
strerror(errno));
}
}
adt_free_event(event);
(void) adt_end_session(ah);
}
#define MAX_SECOBJS 32
#define MAX_SECOBJ_NAMELEN 32
static void
do_create_secobj(int argc, char **argv, const char *use)
{
int option, rval;
FILE *filep = NULL;
char *obj_name = NULL;
char *class_name = NULL;
uint8_t obj_val[DLADM_SECOBJ_VAL_MAX];
uint_t obj_len;
boolean_t success, temp = B_FALSE;
dladm_status_t status;
dladm_secobj_class_t class = -1;
uid_t euid;
opterr = 0;
(void) memset(obj_val, 0, DLADM_SECOBJ_VAL_MAX);
while ((option = getopt_long(argc, argv, ":f:c:R:t",
wifi_longopts, NULL)) != -1) {
switch (option) {
case 'f':
euid = geteuid();
(void) seteuid(getuid());
filep = fopen(optarg, "r");
if (filep == NULL) {
die("cannot open %s: %s", optarg,
strerror(errno));
}
(void) seteuid(euid);
break;
case 'c':
class_name = optarg;
status = dladm_str2secobjclass(optarg, &class);
if (status != DLADM_STATUS_OK) {
die("invalid secure object class '%s', "
"valid values are: wep, wpa", optarg);
}
break;
case 't':
temp = B_TRUE;
break;
case 'R':
status = dladm_set_rootdir(optarg);
if (status != DLADM_STATUS_OK) {
die_dlerr(status, "invalid directory "
"specified");
}
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (optind == (argc - 1))
obj_name = argv[optind];
else if (optind != argc)
usage();
if (class == -1)
die("secure object class required");
if (obj_name == NULL)
die("secure object name required");
success = check_auth(LINK_SEC_AUTH);
audit_secobj(LINK_SEC_AUTH, class_name, obj_name, success, B_TRUE);
if (!success)
die("authorization '%s' is required", LINK_SEC_AUTH);
rval = get_secobj_val(obj_name, obj_val, &obj_len, class, filep);
if (rval != 0) {
switch (rval) {
case ENOENT:
die("invalid secure object class");
break;
case EINVAL:
die("invalid secure object value");
break;
case ENOTSUP:
die("verification failed");
break;
default:
die("invalid secure object: %s", strerror(rval));
break;
}
}
status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
DLADM_OPT_CREATE | DLADM_OPT_ACTIVE);
if (status != DLADM_STATUS_OK) {
die_dlerr(status, "could not create secure object '%s'",
obj_name);
}
if (temp)
return;
status = dladm_set_secobj(obj_name, class, obj_val, obj_len,
DLADM_OPT_PERSIST);
if (status != DLADM_STATUS_OK) {
warn_dlerr(status, "could not persistently create secure "
"object '%s'", obj_name);
}
}
static void
do_delete_secobj(int argc, char **argv, const char *use)
{
int i, option;
boolean_t temp = B_FALSE;
split_t *sp = NULL;
boolean_t success;
dladm_status_t status, pstatus;
opterr = 0;
status = pstatus = DLADM_STATUS_OK;
while ((option = getopt_long(argc, argv, ":R:t",
wifi_longopts, NULL)) != -1) {
switch (option) {
case 't':
temp = B_TRUE;
break;
case 'R':
status = dladm_set_rootdir(optarg);
if (status != DLADM_STATUS_OK) {
die_dlerr(status, "invalid directory "
"specified");
}
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (optind == (argc - 1)) {
sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
if (sp == NULL) {
die("invalid secure object name(s): '%s'",
argv[optind]);
}
} else if (optind != argc)
usage();
if (sp == NULL || sp->s_nfields < 1)
die("secure object name required");
success = check_auth(LINK_SEC_AUTH);
audit_secobj(LINK_SEC_AUTH, "unknown", argv[optind], success, B_FALSE);
if (!success)
die("authorization '%s' is required", LINK_SEC_AUTH);
for (i = 0; i < sp->s_nfields; i++) {
status = dladm_unset_secobj(sp->s_fields[i], DLADM_OPT_ACTIVE);
if (!temp) {
pstatus = dladm_unset_secobj(sp->s_fields[i],
DLADM_OPT_PERSIST);
} else {
pstatus = DLADM_STATUS_OK;
}
if (status != DLADM_STATUS_OK) {
warn_dlerr(status, "could not delete secure object "
"'%s'", sp->s_fields[i]);
}
if (pstatus != DLADM_STATUS_OK) {
warn_dlerr(pstatus, "could not persistently delete "
"secure object '%s'", sp->s_fields[i]);
}
}
if (status != DLADM_STATUS_OK || pstatus != DLADM_STATUS_OK)
exit(1);
}
typedef struct show_secobj_state {
boolean_t ss_persist;
boolean_t ss_parseable;
boolean_t ss_header;
print_state_t ss_print;
} show_secobj_state_t;
static boolean_t
show_secobj(void *arg, const char *obj_name)
{
uint_t obj_len = DLADM_SECOBJ_VAL_MAX;
uint8_t obj_val[DLADM_SECOBJ_VAL_MAX];
char buf[DLADM_STRSIZE];
uint_t flags = 0;
dladm_secobj_class_t class;
show_secobj_state_t *statep = arg;
dladm_status_t status;
secobj_fields_buf_t sbuf;
bzero(&sbuf, sizeof (secobj_fields_buf_t));
if (statep->ss_persist)
flags |= DLADM_OPT_PERSIST;
status = dladm_get_secobj(obj_name, &class, obj_val, &obj_len, flags);
if (status != DLADM_STATUS_OK)
die_dlerr(status, "cannot get secure object '%s'", obj_name);
if (statep->ss_header) {
statep->ss_header = B_FALSE;
if (!statep->ss_parseable)
print_header(&statep->ss_print);
}
(void) snprintf(sbuf.ss_obj_name, sizeof (sbuf.ss_obj_name),
obj_name);
(void) dladm_secobjclass2str(class, buf);
(void) snprintf(sbuf.ss_class, sizeof (sbuf.ss_class), "%s", buf);
if (getuid() == 0) {
char val[DLADM_SECOBJ_VAL_MAX * 2];
uint_t len = sizeof (val);
if (octet_to_hexascii(obj_val, obj_len, val, &len) == 0)
(void) snprintf(sbuf.ss_val,
sizeof (sbuf.ss_val), "%s", val);
}
dladm_print_output(&statep->ss_print, statep->ss_parseable,
dladm_print_field, (void *)&sbuf);
return (B_TRUE);
}
static void
do_show_secobj(int argc, char **argv, const char *use)
{
int option;
show_secobj_state_t state;
dladm_status_t status;
boolean_t o_arg = B_FALSE;
uint_t i;
split_t *sp;
uint_t flags;
char *fields_str = NULL;
print_field_t **fields;
uint_t nfields;
char *def_fields = "object,class";
char *all_fields = "object,class,value";
opterr = 0;
bzero(&state, sizeof (state));
state.ss_parseable = B_FALSE;
fields_str = def_fields;
state.ss_persist = B_FALSE;
state.ss_parseable = B_FALSE;
state.ss_header = B_TRUE;
while ((option = getopt_long(argc, argv, ":pPo:",
wifi_longopts, NULL)) != -1) {
switch (option) {
case 'p':
state.ss_parseable = B_TRUE;
break;
case 'P':
state.ss_persist = B_TRUE;
break;
case 'o':
o_arg = B_TRUE;
if (strcasecmp(optarg, "all") == 0)
fields_str = all_fields;
else
fields_str = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (state.ss_parseable && !o_arg)
die("option -c requires -o");
if (state.ss_parseable && fields_str == all_fields)
die("\"-o all\" is invalid with -p");
fields = parse_output_fields(fields_str, secobj_fields,
DEV_SOBJ_FIELDS, CMD_TYPE_ANY, &nfields);
if (fields == NULL) {
die("invalid field(s) specified");
return;
}
state.ss_print.ps_fields = fields;
state.ss_print.ps_nfields = nfields;
flags = state.ss_persist ? DLADM_OPT_PERSIST : 0;
if (optind == (argc - 1)) {
sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN);
if (sp == NULL) {
die("invalid secure object name(s): '%s'",
argv[optind]);
}
for (i = 0; i < sp->s_nfields; i++) {
if (!show_secobj(&state, sp->s_fields[i]))
break;
}
splitfree(sp);
return;
} else if (optind != argc)
usage();
status = dladm_walk_secobj(&state, show_secobj, flags);
if (status != DLADM_STATUS_OK)
die_dlerr(status, "show-secobj");
}
/*ARGSUSED*/
static int
i_dladm_init_linkprop(datalink_id_t linkid, void *arg)
{
(void) dladm_init_linkprop(linkid, B_TRUE);
return (DLADM_WALK_CONTINUE);
}
/*ARGSUSED*/
static void
do_init_linkprop(int argc, char **argv, const char *use)
{
int option;
dladm_status_t status;
datalink_id_t linkid = DATALINK_ALL_LINKID;
datalink_media_t media = DATALINK_ANY_MEDIATYPE;
uint_t any_media = B_TRUE;
opterr = 0;
while ((option = getopt(argc, argv, ":w")) != -1) {
switch (option) {
case 'w':
media = DL_WIFI;
any_media = B_FALSE;
break;
default:
/*
* Because init-linkprop is not a public command,
* print the usage instead.
*/
usage();
break;
}
}
if (optind == (argc - 1)) {
if ((status = dladm_name2info(argv[optind], &linkid, NULL, NULL,
NULL)) != DLADM_STATUS_OK)
die_dlerr(status, "link %s is not valid", argv[optind]);
} else if (optind != argc) {
usage();
}
if (linkid == DATALINK_ALL_LINKID) {
/*
* linkprops of links of other classes have been initialized as
* part of the dladm up-xxx operation.
*/
(void) dladm_walk_datalink_id(i_dladm_init_linkprop, NULL,
DATALINK_CLASS_PHYS, media, DLADM_OPT_PERSIST);
} else {
(void) dladm_init_linkprop(linkid, any_media);
}
}
/* ARGSUSED */
static void
do_show_ether(int argc, char **argv, const char *use)
{
int option;
datalink_id_t linkid;
print_ether_state_t state;
print_field_t **fields;
boolean_t o_arg = B_FALSE;
char *fields_str;
uint_t nfields;
char *all_fields =
"link,ptype,state,auto,speed-duplex,pause,rem_fault";
char *default_fields =
"link,ptype,state,auto,speed-duplex,pause";
fields_str = default_fields;
bzero(&state, sizeof (state));
state.es_link = NULL;
state.es_parseable = B_FALSE;
while ((option = getopt_long(argc, argv, "o:px",
showeth_lopts, NULL)) != -1) {
switch (option) {
case 'x':
state.es_extended = B_TRUE;
break;
case 'p':
state.es_parseable = B_TRUE;
break;
case 'o':
o_arg = B_TRUE;
if (strcasecmp(optarg, "all") == 0)
fields_str = all_fields;
else
fields_str = optarg;
break;
default:
die_opterr(optopt, option, use);
break;
}
}
if (state.es_parseable && !o_arg)
die("-p requires -o");
if (state.es_parseable && fields_str == all_fields)
die("\"-o all\" is invalid with -p");
if (optind == (argc - 1))
state.es_link = argv[optind];
fields = parse_output_fields(fields_str, ether_fields,
ETHER_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
if (fields == NULL) {
die("invalid field(s) specified");
exit(EXIT_FAILURE);
}
state.es_print.ps_fields = fields;
state.es_print.ps_nfields = nfields;
if (state.es_link == NULL) {
(void) dladm_walk_datalink_id(show_etherprop, &state,
DATALINK_CLASS_PHYS, DL_ETHER,
DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
} else {
if (!link_is_ether(state.es_link, &linkid)) {
die("invalid link specified");
}
(void) show_etherprop(linkid, &state);
}
exit(DLADM_STATUS_OK);
}
static char *
dladm_print_field(print_field_t *pf, void *arg)
{
char *value;
value = (char *)arg + pf->pf_offset;
return (value);
}
static int
show_etherprop(datalink_id_t linkid, void *arg)
{
print_ether_state_t *statep = arg;
char buf[DLADM_STRSIZE];
int speed;
uint64_t s;
uint32_t autoneg, pause, asmpause, adv_rf, cap_rf, lp_rf;
ether_fields_buf_t ebuf;
char speed_unit = 'M';
bzero(&ebuf, sizeof (ether_fields_buf_t));
if (dladm_datalink_id2info(linkid, NULL, NULL, NULL,
ebuf.eth_link, sizeof (ebuf.eth_link)) != DLADM_STATUS_OK) {
return (DLADM_WALK_CONTINUE);
}
if (!statep->es_header && !statep->es_parseable) {
print_header(&statep->es_print);
statep->es_header = B_TRUE;
}
(void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype),
"%s", "current");
(void) dladm_get_single_mac_stat(linkid, "link_autoneg",
KSTAT_DATA_UINT32, &autoneg);
(void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg),
"%s", (autoneg ? "yes" : "no"));
(void) dladm_get_single_mac_stat(linkid, "link_pause",
KSTAT_DATA_UINT32, &pause);
(void) dladm_get_single_mac_stat(linkid, "link_asmpause",
KSTAT_DATA_UINT32, &asmpause);
(void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause),
"%s", pause_str(pause, asmpause));
(void) dladm_get_single_mac_stat(linkid, "ifspeed",
KSTAT_DATA_UINT64, &s);
speed = (int)(s/1000000ull);
if (speed >= 1000) {
speed = speed/1000;
speed_unit = 'G';
}
(void) get_linkduplex(ebuf.eth_link, B_TRUE, buf);
(void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%d%c-%c",
speed, speed_unit, buf[0]);
(void) get_linkstate(ebuf.eth_link, B_TRUE, buf);
(void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state),
"%s", buf);
(void) dladm_get_single_mac_stat(linkid, "adv_rem_fault",
KSTAT_DATA_UINT32, &adv_rf);
(void) dladm_get_single_mac_stat(linkid, "cap_rem_fault",
KSTAT_DATA_UINT32, &cap_rf);
(void) dladm_get_single_mac_stat(linkid, "lp_rem_fault",
KSTAT_DATA_UINT32, &lp_rf);
(void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault),
"%s", (adv_rf == 0 && lp_rf == 0 ? "none" : "fault"));
dladm_print_output(&statep->es_print, statep->es_parseable,
dladm_print_field, &ebuf);
if (statep->es_extended)
show_ether_xprop(linkid, arg);
return (DLADM_WALK_CONTINUE);
}
/* ARGSUSED */
static void
do_init_secobj(int argc, char **argv, const char *use)
{
dladm_status_t status;
status = dladm_init_secobj();
if (status != DLADM_STATUS_OK)
die_dlerr(status, "secure object initialization failed");
}
/*
* "-R" option support. It is used for live upgrading. Append dladm commands
* to a upgrade script which will be run when the alternative root boots up:
*
* - If the /etc/dladm/datalink.conf file exists on the alternative root,
* append dladm commands to the <altroot>/var/svc/profile/upgrade_datalink
* script. This script will be run as part of the network/physical service.
* We cannot defer this to /var/svc/profile/upgrade because then the
* configuration will not be able to take effect before network/physical
* plumbs various interfaces.
*
* - If the /etc/dladm/datalink.conf file does not exist on the alternative
* root, append dladm commands to the <altroot>/var/svc/profile/upgrade script,
* which will be run in the manifest-import service.
*
* Note that the SMF team is considering to move the manifest-import service
* to be run at the very begining of boot. Once that is done, the need for
* the /var/svc/profile/upgrade_datalink script will not exist any more.
*/
static void
altroot_cmd(char *altroot, int argc, char *argv[])
{
char path[MAXPATHLEN];
struct stat stbuf;
FILE *fp;
int i;
/*
* Check for the existence of the /etc/dladm/datalink.conf
* configuration file, and determine the name of script file.
*/
(void) snprintf(path, MAXPATHLEN, "/%s/etc/dladm/datalink.conf",
altroot);
if (stat(path, &stbuf) < 0) {
(void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot,
SMF_UPGRADE_FILE);
} else {
(void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot,
SMF_UPGRADEDATALINK_FILE);
}
if ((fp = fopen(path, "a+")) == NULL)
die("operation not supported on %s", altroot);
(void) fprintf(fp, "/sbin/dladm ");
for (i = 0; i < argc; i++) {
/*
* Directly write to the file if it is not the "-R <altroot>"
* option. In which case, skip it.
*/
if (strcmp(argv[i], "-R") != 0)
(void) fprintf(fp, "%s ", argv[i]);
else
i ++;
}
(void) fprintf(fp, "%s\n", SMF_DLADM_UPGRADE_MSG);
(void) fclose(fp);
exit(0);
}
/*
* Convert the string to an integer. Note that the string must not have any
* trailing non-integer characters.
*/
static boolean_t
str2int(const char *str, int *valp)
{
int val;
char *endp = NULL;
errno = 0;
val = strtol(str, &endp, 10);
if (errno != 0 || *endp != '\0')
return (B_FALSE);
*valp = val;
return (B_TRUE);
}
/* PRINTFLIKE1 */
static void
warn(const char *format, ...)
{
va_list alist;
format = gettext(format);
(void) fprintf(stderr, "%s: warning: ", progname);
va_start(alist, format);
(void) vfprintf(stderr, format, alist);
va_end(alist);
(void) putchar('\n');
}
/* PRINTFLIKE2 */
static void
warn_dlerr(dladm_status_t err, const char *format, ...)
{
va_list alist;
char errmsg[DLADM_STRSIZE];
format = gettext(format);
(void) fprintf(stderr, gettext("%s: warning: "), progname);
va_start(alist, format);
(void) vfprintf(stderr, format, alist);
va_end(alist);
(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
}
/* PRINTFLIKE2 */
static void
die_dlerr(dladm_status_t err, const char *format, ...)
{
va_list alist;
char errmsg[DLADM_STRSIZE];
format = gettext(format);
(void) fprintf(stderr, "%s: ", progname);
va_start(alist, format);
(void) vfprintf(stderr, format, alist);
va_end(alist);
(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
exit(EXIT_FAILURE);
}
/* PRINTFLIKE1 */
static void
die(const char *format, ...)
{
va_list alist;
format = gettext(format);
(void) fprintf(stderr, "%s: ", progname);
va_start(alist, format);
(void) vfprintf(stderr, format, alist);
va_end(alist);
(void) putchar('\n');
exit(EXIT_FAILURE);
}
static void
die_optdup(int opt)
{
die("the option -%c cannot be specified more than once", opt);
}
static void
die_opterr(int opt, int opterr, const char *usage)
{
switch (opterr) {
case ':':
die("option '-%c' requires a value\nusage: %s", opt,
gettext(usage));
break;
case '?':
default:
die("unrecognized option '-%c'\nusage: %s", opt,
gettext(usage));
break;
}
}
static void
show_ether_xprop(datalink_id_t linkid, void *arg)
{
print_ether_state_t *statep = arg;
char buf[DLADM_STRSIZE];
uint32_t autoneg, pause, asmpause, adv_rf, cap_rf, lp_rf;
boolean_t add_comma, r1;
ether_fields_buf_t ebuf;
/* capable */
bzero(&ebuf, sizeof (ebuf));
(void) snprintf(ebuf.eth_link, sizeof (ebuf.eth_link), "");
(void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype),
"%s", "capable");
(void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), "");
(void) dladm_get_single_mac_stat(linkid, "cap_autoneg",
KSTAT_DATA_UINT32, &autoneg);
(void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg),
"%s", (autoneg ? "yes" : "no"));
add_comma = B_FALSE;
bzero(buf, sizeof (buf));
r1 = get_speed_duplex(linkid, "cap_1000", buf, "1G", B_FALSE);
if (r1)
add_comma = B_TRUE;
r1 = get_speed_duplex(linkid, "cap_100", buf, "100M", add_comma);
if (r1)
add_comma = B_TRUE;
r1 = get_speed_duplex(linkid, "cap_10", buf, "10M", add_comma);
add_comma = B_FALSE;
(void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%s", buf);
(void) dladm_get_single_mac_stat(linkid, "cap_pause",
KSTAT_DATA_UINT32, &pause);
(void) dladm_get_single_mac_stat(linkid, "cap_asmpause",
KSTAT_DATA_UINT32, &asmpause);
(void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause),
"%s", pause_str(pause, asmpause));
(void) dladm_get_single_mac_stat(linkid, "adv_rem_fault",
KSTAT_DATA_UINT32, &adv_rf);
(void) dladm_get_single_mac_stat(linkid, "cap_rem_fault",
KSTAT_DATA_UINT32, &cap_rf);
(void) dladm_get_single_mac_stat(linkid, "lp_rem_fault",
KSTAT_DATA_UINT32, &lp_rf);
(void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault),
"%s", (cap_rf ? "yes" : "no"));
dladm_print_output(&statep->es_print, statep->es_parseable,
dladm_print_field, &ebuf);
/* advertised */
bzero(&ebuf, sizeof (ebuf));
(void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype),
"%s", "adv");
(void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), "");
(void) dladm_get_single_mac_stat(linkid, "adv_cap_autoneg",
KSTAT_DATA_UINT32, &autoneg);
(void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg),
"%s", (autoneg ? "yes" : "no"));
add_comma = B_FALSE;
bzero(buf, sizeof (buf));
r1 = get_speed_duplex(linkid, "adv_cap_1000", buf, "1G", add_comma);
if (r1)
add_comma = B_TRUE;
r1 = get_speed_duplex(linkid, "adv_cap_100", buf, "100M", add_comma);
if (r1)
add_comma = B_TRUE;
r1 = get_speed_duplex(linkid, "adv_cap_10", buf, "10M", add_comma);
add_comma = B_FALSE;
(void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%s", buf);
(void) dladm_get_single_mac_stat(linkid, "adv_cap_pause",
KSTAT_DATA_UINT32, &pause);
(void) dladm_get_single_mac_stat(linkid, "adv_cap_asmpause",
KSTAT_DATA_UINT32, &asmpause);
(void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause),
"%s", pause_str(pause, asmpause));
(void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault),
"%s", (adv_rf ? "fault" : "none"));
dladm_print_output(&statep->es_print, statep->es_parseable,
dladm_print_field, &ebuf);
/* peeradv */
bzero(&ebuf, sizeof (ebuf));
(void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype),
"%s", "peeradv");
(void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), "");
(void) dladm_get_single_mac_stat(linkid, "lp_cap_autoneg",
KSTAT_DATA_UINT32, &autoneg);
(void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg),
"%s", (autoneg ? "yes" : "no"));
add_comma = B_FALSE;
bzero(buf, sizeof (buf));
r1 = get_speed_duplex(linkid, "lp_cap_1000", buf, "1G", add_comma);
if (r1)
add_comma = B_TRUE;
r1 = get_speed_duplex(linkid, "lp_cap_100", buf, "100M", add_comma);
if (r1)
add_comma = B_TRUE;
r1 = get_speed_duplex(linkid, "lp_cap_10", buf, "10M", add_comma);
(void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%s", buf);
(void) dladm_get_single_mac_stat(linkid, "lp_cap_pause",
KSTAT_DATA_UINT32, &pause);
(void) dladm_get_single_mac_stat(linkid, "lp_cap_asmpause",
KSTAT_DATA_UINT32, &asmpause);
(void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause),
"%s", pause_str(pause, asmpause));
(void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault),
"%s", (lp_rf ? "fault" : "none"));
dladm_print_output(&statep->es_print, statep->es_parseable,
dladm_print_field, &ebuf);
}
static boolean_t
get_speed_duplex(datalink_id_t linkid, const char *mii_prop_prefix,
char *spbuf, char *sp, boolean_t add_comma)
{
int speed, duplex = 0;
boolean_t ret = B_FALSE;
char mii_prop[DLADM_STRSIZE];
(void) snprintf(mii_prop, DLADM_STRSIZE, "%sfdx", mii_prop_prefix);
(void) dladm_get_single_mac_stat(linkid, mii_prop, KSTAT_DATA_UINT32,
&speed);
if (speed) {
ret = B_TRUE;
duplex |= IS_FDX;
}
(void) snprintf(mii_prop, DLADM_STRSIZE, "%shdx", mii_prop_prefix);
(void) dladm_get_single_mac_stat(linkid, mii_prop,
KSTAT_DATA_UINT32, &speed);
if (speed) {
ret = B_TRUE;
duplex |= IS_HDX;
}
if (ret) {
if (add_comma)
(void) strncat(spbuf, ",", DLADM_STRSIZE);
(void) strncat(spbuf, sp, DLADM_STRSIZE);
if ((duplex & (IS_FDX|IS_HDX)) == (IS_FDX|IS_HDX))
(void) strncat(spbuf, "-fh", DLADM_STRSIZE);
else if (duplex & IS_FDX)
(void) strncat(spbuf, "-f", DLADM_STRSIZE);
else if (duplex & IS_HDX)
(void) strncat(spbuf, "-h", DLADM_STRSIZE);
}
return (ret);
}
static void
dladm_print_output(print_state_t *statep, boolean_t parseable,
print_callback_t fn, void *arg)
{
int i;
char *value;
print_field_t **pf;
pf = statep->ps_fields;
for (i = 0; i < statep->ps_nfields; i++) {
statep->ps_lastfield = (i + 1 == statep->ps_nfields);
value = (*fn)(pf[i], arg);
if (value != NULL)
print_field(statep, pf[i], value, parseable);
}
(void) putchar('\n');
}
static void
print_header(print_state_t *ps)
{
int i;
print_field_t **pf;
pf = ps->ps_fields;
for (i = 0; i < ps->ps_nfields; i++) {
ps->ps_lastfield = (i + 1 == ps->ps_nfields);
print_field(ps, pf[i], pf[i]->pf_header, B_FALSE);
}
(void) putchar('\n');
}
static char *
pause_str(int pause, int asmpause)
{
if (pause == 1)
return ("bi");
if (asmpause == 1)
return ("tx");
return ("none");
}
static boolean_t
link_is_ether(const char *link, datalink_id_t *linkid)
{
uint32_t media;
datalink_class_t class;
if (dladm_name2info(link, linkid, NULL, &class, &media) ==
DLADM_STATUS_OK) {
if (class == DATALINK_CLASS_PHYS && media == DL_ETHER)
return (B_TRUE);
}
return (B_FALSE);
}