diff options
Diffstat (limited to 'usr/src/cmd/dladm/dladm.c')
-rw-r--r-- | usr/src/cmd/dladm/dladm.c | 2318 |
1 files changed, 1669 insertions, 649 deletions
diff --git a/usr/src/cmd/dladm/dladm.c b/usr/src/cmd/dladm/dladm.c index 466adfe6c0..9422a31da3 100644 --- a/usr/src/cmd/dladm/dladm.c +++ b/usr/src/cmd/dladm/dladm.c @@ -46,7 +46,9 @@ #include <libintl.h> #include <libdevinfo.h> #include <libdlpi.h> +#include <libdladm.h> #include <libdllink.h> +#include <libdlstat.h> #include <libdlaggr.h> #include <libdlwlan.h> #include <libdlvlan.h> @@ -54,11 +56,18 @@ #include <libinetutil.h> #include <bsm/adt.h> #include <bsm/adt_event.h> +#include <libdlvnic.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/processor.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if_types.h> #include <stddef.h> -#define AGGR_DRV "aggr" #define STR_UNDEF_VAL "--" #define MAXPORT 256 +#define MAXVNIC 256 #define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0) #define MAXLINELEN 1024 #define SMF_UPGRADE_FILE "/var/svc/profile/upgrade" @@ -131,9 +140,7 @@ * 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. + * system call required to find the value to be printed. */ typedef struct print_field_s { @@ -192,15 +199,6 @@ 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; @@ -210,6 +208,8 @@ typedef struct show_state { print_state_t ls_print; boolean_t ls_parseable; boolean_t ls_printheader; + boolean_t ls_mac; + boolean_t ls_hwgrp; } show_state_t; typedef struct show_grp_state { @@ -226,9 +226,37 @@ typedef struct show_grp_state { print_state_t gs_print; } show_grp_state_t; +typedef struct show_vnic_state { + datalink_id_t vs_vnic_id; + datalink_id_t vs_link_id; + char vs_vnic[MAXLINKNAMELEN]; + char vs_link[MAXLINKNAMELEN]; + boolean_t vs_parseable; + boolean_t vs_printheader; + boolean_t vs_found; + boolean_t vs_firstonly; + boolean_t vs_donefirst; + boolean_t vs_stats; + boolean_t vs_printstats; + pktsum_t vs_totalstats; + pktsum_t vs_prevstats[MAXVNIC]; + boolean_t vs_etherstub; + dladm_status_t vs_status; + uint32_t vs_flags; + print_state_t vs_print; +} show_vnic_state_t; + +typedef struct show_usage_state_s { + boolean_t us_plot; + boolean_t us_parseable; + boolean_t us_printheader; + boolean_t us_first; + print_state_t us_print; +} show_usage_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_show_link, 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; @@ -239,21 +267,25 @@ 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 cmdfunc_t do_create_vnic, do_delete_vnic, do_show_vnic; +static cmdfunc_t do_up_vnic; +static cmdfunc_t do_create_etherstub, do_delete_etherstub, do_show_etherstub; +static cmdfunc_t do_show_usage; + +static void do_up_vnic_common(int, char **, const char *, boolean_t); 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 void vnic_stats(show_vnic_state_t *, uint32_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 *); @@ -286,8 +318,6 @@ static cmd_t cmds[] = { "\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>" }, @@ -343,9 +373,30 @@ static cmd_t cmds[] = { { "delete-phys", do_delete_phys, "\tdelete-phys\t<link>" }, { "show-phys", do_show_phys, - "\tshow-phys\t[-pP] [-o <field>,..] [<link>]" }, + "\tshow-phys\t[-pP] [-o <field>,..] [-H] [<link>]" }, { "init-phys", do_init_phys, NULL }, - { "show-linkmap", do_show_linkmap, NULL } + { "show-linkmap", do_show_linkmap, NULL }, + { "create-vnic", do_create_vnic, + "\tcreate-vnic [-t] [-R <root-dir>] -l <link> [-m <value> |" + " auto |\n" + "\t {factory [-n <slot-identifier>]} |\n" + "\t {random [-r <prefix>]}] [-v vlan-tag [-f]]\n" + "\t -p <prop>=<value>[,...] [-H]" + " <vnic-link>\n" }, + { "delete-vnic", do_delete_vnic, + "\tdelete-vnic [-t] [-R <root-dir>] <vnic-link>\n" }, + { "show-vnic", do_show_vnic, + "\tshow-vnic [-pP] [-l <link>] [-s [-i <interval>]]" }, + { "up-vnic", do_up_vnic, NULL }, + { "create-etherstub", do_create_etherstub, + "\tcreate-etherstub [-t] [-R <root-dir>] <link>\n" }, + { "delete-etherstub", do_delete_etherstub, + "\tdelete-etherstub [-t] [-R <root-dir>] <link>\n" }, + { "show-etherstub", do_show_etherstub, + "\tshow-etherstub [-t] [-R <root-dir>] [<link>]\n" }, + { "show-usage", do_show_usage, + "\tshow-usage [-d|-p -F <format>] [-f <filename>]\n" + "\t [-s <time>] [-e <time>] <link>\n" } }; static const struct option lopts[] = { @@ -360,11 +411,15 @@ static const struct option lopts[] = { {"root-dir", required_argument, 0, 'R'}, {"link", required_argument, 0, 'l'}, {"forcible", no_argument, 0, 'f'}, + {"bw-limit", required_argument, 0, 'b'}, + {"mac-address", required_argument, 0, 'm'}, + {"slot", required_argument, 0, 'n'}, { 0, 0, 0, 0 } }; static const struct option show_lopts[] = { {"statistics", no_argument, 0, 's'}, + {"continuous", no_argument, 0, 'S'}, {"interval", required_argument, 0, 'i'}, {"parseable", no_argument, 0, 'p'}, {"extended", no_argument, 0, 'x'}, @@ -409,6 +464,24 @@ static const struct option showeth_lopts[] = { { 0, 0, 0, 0 } }; +static const struct option vnic_lopts[] = { + {"temporary", no_argument, 0, 't' }, + {"root-dir", required_argument, 0, 'R' }, + {"dev", required_argument, 0, 'd' }, + {"mac-address", required_argument, 0, 'm' }, + {"cpus", required_argument, 0, 'c' }, + {"bw-limit", required_argument, 0, 'b' }, + {"slot", required_argument, 0, 'n' }, + {"mac-prefix", required_argument, 0, 'r' }, + { 0, 0, 0, 0 } +}; + +static const struct option etherstub_lopts[] = { + {"temporary", no_argument, 0, 't' }, + {"root-dir", required_argument, 0, 'R' }, + { 0, 0, 0, 0 } +}; + /* * structures for 'dladm show-ether' */ @@ -451,26 +524,7 @@ typedef struct print_ether_state { } 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) + * structures for 'dladm show-link -s' (print statistics) */ typedef enum { DEVS_LINK, @@ -493,12 +547,6 @@ static print_field_t devs_fields[] = { { "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. @@ -635,10 +683,10 @@ static print_field_t aggr_s_fields[] = { CMD_TYPE_ANY}} ; #define AGGR_S_MAX_FIELDS \ - (sizeof (aggr_l_fields) / sizeof (print_field_t)) + (sizeof (aggr_s_fields) / sizeof (print_field_t)) /* - * structures for 'dladm show-dev -L'. + * structures for 'dladm show-aggr -L'. */ typedef enum { AGGR_L_LINK, @@ -697,6 +745,50 @@ static print_field_t phys_fields[] = { #define PHYS_MAX_FIELDS (sizeof (phys_fields) / sizeof (print_field_t)) /* + * structures for 'dladm show-phys -m' + */ + +typedef enum { + PHYS_M_LINK, + PHYS_M_SLOT, + PHYS_M_ADDRESS, + PHYS_M_INUSE, + PHYS_M_CLIENT +} phys_m_field_index_t; + +static print_field_t phys_m_fields[] = { +/* name, header, field width, offset, cmdtype */ +{ "link", "LINK", 12, PHYS_M_LINK, CMD_TYPE_ANY}, +{ "slot", "SLOT", 8, PHYS_M_SLOT, CMD_TYPE_ANY}, +{ "address", "ADDRESS", 18, PHYS_M_ADDRESS, CMD_TYPE_ANY}, +{ "inuse", "INUSE", 4, PHYS_M_INUSE, CMD_TYPE_ANY}, +{ "client", "CLIENT", 12, PHYS_M_CLIENT, CMD_TYPE_ANY}} +; +#define PHYS_M_MAX_FIELDS (sizeof (phys_m_fields) / sizeof (print_field_t)) + +/* + * structures for 'dladm show-phys -H' + */ + +typedef enum { + PHYS_H_LINK, + PHYS_H_GROUP, + PHYS_H_GRPTYPE, + PHYS_H_RINGS, + PHYS_H_CLIENTS +} phys_h_field_index_t; + +static print_field_t phys_h_fields[] = { +/* name, header, field width, offset, cmdtype */ +{ "link", "LINK", 12, PHYS_H_LINK, CMD_TYPE_ANY}, +{ "group", "GROUP", 8, PHYS_H_GROUP, CMD_TYPE_ANY}, +{ "grouptype", "TYPE", 6, PHYS_H_GRPTYPE, CMD_TYPE_ANY}, +{ "rings", "NUM-RINGS", 16, PHYS_H_RINGS, CMD_TYPE_ANY}, +{ "clients", "CLIENTS", 20, PHYS_H_CLIENTS, CMD_TYPE_ANY}} +; +#define PHYS_H_MAX_FIELDS (sizeof (phys_h_fields) / sizeof (print_field_t)) + +/* * structures for 'dladm show-vlan' */ static print_field_t vlan_fields[] = { @@ -712,6 +804,7 @@ static print_field_t vlan_fields[] = { ; #define VLAN_MAX_FIELDS (sizeof (vlan_fields) / sizeof (print_field_t)) + /* * structures for 'dladm show-wifi' */ @@ -764,34 +857,28 @@ static print_field_t linkprop_fields[] = { #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; + char ls_link[MAXLINKNAMELEN]; + char *ls_line; + char **ls_propvals; + dladm_arg_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 set_linkprop_state { + const char *ls_name; + boolean_t ls_reset; + boolean_t ls_temp; + dladm_status_t ls_status; +} set_linkprop_state_t; + typedef struct linkprop_args_s { show_linkprop_state_t *ls_state; char *ls_propname; @@ -817,9 +904,108 @@ static print_field_t secobj_fields[] = { ; #define DEV_SOBJ_FIELDS (sizeof (secobj_fields) / sizeof (print_field_t)) +/* + * structures for 'dladm show-vnic' + */ +typedef struct vnic_fields_buf_s +{ + char vnic_link[DLPI_LINKNAME_MAX]; + char vnic_over[DLPI_LINKNAME_MAX]; + char vnic_speed[6]; + char vnic_macaddr[19]; + char vnic_macaddrtype[19]; + char vnic_vid[6]; +} vnic_fields_buf_t; + +static print_field_t vnic_fields[] = { +/* name, header, field width, offset, cmdtype */ +{ "link", "LINK", 12, + offsetof(vnic_fields_buf_t, vnic_link), CMD_TYPE_ANY}, +{ "over", "OVER", 12, + offsetof(vnic_fields_buf_t, vnic_over), CMD_TYPE_ANY}, +{ "speed", "SPEED", 6, + offsetof(vnic_fields_buf_t, vnic_speed), CMD_TYPE_ANY}, +{ "macaddr", "MACADDRESS", 20, + offsetof(vnic_fields_buf_t, vnic_macaddr), CMD_TYPE_ANY}, +{ "macaddrtype", "MACADDRTYPE", 19, + offsetof(vnic_fields_buf_t, vnic_macaddrtype), CMD_TYPE_ANY}, +{ "vid", "VID", 6, + offsetof(vnic_fields_buf_t, vnic_vid), CMD_TYPE_ANY}} +; +#define VNIC_MAX_FIELDS (sizeof (vnic_fields) / sizeof (print_field_t)) + +/* + * structures for 'dladm show-usage' + */ + +typedef struct usage_fields_buf_s { + char usage_link[12]; + char usage_duration[10]; + char usage_ipackets[9]; + char usage_rbytes[10]; + char usage_opackets[9]; + char usage_obytes[10]; + char usage_bandwidth[14]; +} usage_fields_buf_t; + +static print_field_t usage_fields[] = { +/* name, header, field width, offset, cmdtype */ +{ "link", "LINK", 12, + offsetof(usage_fields_buf_t, usage_link), CMD_TYPE_ANY}, +{ "duration", "DURATION", 10, + offsetof(usage_fields_buf_t, usage_duration), CMD_TYPE_ANY}, +{ "ipackets", "IPACKETS", 9, + offsetof(usage_fields_buf_t, usage_ipackets), CMD_TYPE_ANY}, +{ "rbytes", "RBYTES", 10, + offsetof(usage_fields_buf_t, usage_rbytes), CMD_TYPE_ANY}, +{ "opackets", "OPACKETS", 9, + offsetof(usage_fields_buf_t, usage_opackets), CMD_TYPE_ANY}, +{ "obytes", "OBYTES", 10, + offsetof(usage_fields_buf_t, usage_obytes), CMD_TYPE_ANY}, +{ "bandwidth", "BANDWIDTH", 14, + offsetof(usage_fields_buf_t, usage_bandwidth), CMD_TYPE_ANY}} +; + +#define USAGE_MAX_FIELDS (sizeof (usage_fields) / sizeof (print_field_t)) + +/* + * structures for 'dladm show-usage link' + */ + +typedef struct usage_l_fields_buf_s { + char usage_l_link[12]; + char usage_l_stime[13]; + char usage_l_etime[13]; + char usage_l_rbytes[8]; + char usage_l_obytes[8]; + char usage_l_bandwidth[14]; +} usage_l_fields_buf_t; + +static print_field_t usage_l_fields[] = { +/* name, header, field width, offset, cmdtype */ +{ "link", "LINK", 12, + offsetof(usage_l_fields_buf_t, usage_l_link), CMD_TYPE_ANY}, +{ "start", "START", 13, + offsetof(usage_l_fields_buf_t, usage_l_stime), CMD_TYPE_ANY}, +{ "end", "END", 13, + offsetof(usage_l_fields_buf_t, usage_l_etime), CMD_TYPE_ANY}, +{ "rbytes", "RBYTES", 8, + offsetof(usage_l_fields_buf_t, usage_l_rbytes), CMD_TYPE_ANY}, +{ "obytes", "OBYTES", 8, + offsetof(usage_l_fields_buf_t, usage_l_obytes), CMD_TYPE_ANY}, +{ "bandwidth", "BANDWIDTH", 14, + offsetof(usage_l_fields_buf_t, usage_l_bandwidth), CMD_TYPE_ANY}} +; + +#define USAGE_L_MAX_FIELDS \ + (sizeof (usage_l_fields) /sizeof (print_field_t)) + static char *progname; static sig_atomic_t signalled; +#define DLADM_ETHERSTUB_NAME "etherstub" +#define DLADM_IS_ETHERSTUB(id) (id == DATALINK_INVALID_LINKID) + static void usage(void) { @@ -867,6 +1053,254 @@ main(int argc, char *argv[]) return (0); } +/*ARGSUSED*/ +static int +show_usage_date(dladm_usage_t *usage, void *arg) +{ + + time_t stime; + char timebuf[20]; + + stime = usage->du_stime; + (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y", + localtime(&stime)); + (void) printf("%s\n", timebuf); + + return (DLADM_STATUS_OK); +} + +static int +show_usage_time(dladm_usage_t *usage, void *arg) +{ + show_usage_state_t *state = (show_usage_state_t *)arg; + char buf[DLADM_STRSIZE]; + usage_l_fields_buf_t ubuf; + time_t time; + double bw; + + if (state->us_plot) { + if (!state->us_printheader) { + if (state->us_first) { + (void) printf("# Time"); + state->us_first = B_FALSE; + } + (void) printf(" %s", usage->du_name); + if (usage->du_last) { + (void) printf("\n"); + state->us_first = B_TRUE; + state->us_printheader = B_TRUE; + } + } else { + if (state->us_first) { + time = usage->du_etime; + (void) strftime(buf, sizeof (buf), "%T", + localtime(&time)); + state->us_first = B_FALSE; + (void) printf("%s", buf); + } + bw = (double)usage->du_bandwidth/1000; + (void) printf(" %.2f", bw); + if (usage->du_last) { + (void) printf("\n"); + state->us_first = B_TRUE; + } + } + return (DLADM_STATUS_OK); + } + + bzero(&ubuf, sizeof (ubuf)); + + (void) snprintf(ubuf.usage_l_link, sizeof (ubuf.usage_l_link), "%s", + usage->du_name); + time = usage->du_stime; + (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); + (void) snprintf(ubuf.usage_l_stime, sizeof (ubuf.usage_l_stime), "%s", + buf); + time = usage->du_etime; + (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); + (void) snprintf(ubuf.usage_l_etime, sizeof (ubuf.usage_l_etime), "%s", + buf); + (void) snprintf(ubuf.usage_l_rbytes, sizeof (ubuf.usage_l_rbytes), + "%llu", usage->du_rbytes); + (void) snprintf(ubuf.usage_l_obytes, sizeof (ubuf.usage_l_obytes), + "%llu", usage->du_obytes); + (void) snprintf(ubuf.usage_l_bandwidth, sizeof (ubuf.usage_l_bandwidth), + "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf)); + + if (!state->us_parseable && !state->us_printheader) { + print_header(&state->us_print); + state->us_printheader = B_TRUE; + } + + dladm_print_output(&state->us_print, state->us_parseable, + dladm_print_field, (void *)&ubuf); + + return (DLADM_STATUS_OK); +} + +static int +show_usage_res(dladm_usage_t *usage, void *arg) +{ + show_usage_state_t *state = (show_usage_state_t *)arg; + char buf[DLADM_STRSIZE]; + usage_fields_buf_t ubuf; + + bzero(&ubuf, sizeof (ubuf)); + + (void) snprintf(ubuf.usage_link, sizeof (ubuf.usage_link), "%s", + usage->du_name); + (void) snprintf(ubuf.usage_duration, sizeof (ubuf.usage_duration), + "%llu", usage->du_duration); + (void) snprintf(ubuf.usage_ipackets, sizeof (ubuf.usage_ipackets), + "%llu", usage->du_ipackets); + (void) snprintf(ubuf.usage_rbytes, sizeof (ubuf.usage_rbytes), + "%llu", usage->du_rbytes); + (void) snprintf(ubuf.usage_opackets, sizeof (ubuf.usage_opackets), + "%llu", usage->du_opackets); + (void) snprintf(ubuf.usage_obytes, sizeof (ubuf.usage_obytes), + "%llu", usage->du_obytes); + (void) snprintf(ubuf.usage_bandwidth, sizeof (ubuf.usage_bandwidth), + "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf)); + + if (!state->us_parseable && !state->us_printheader) { + print_header(&state->us_print); + state->us_printheader = B_TRUE; + } + + dladm_print_output(&state->us_print, state->us_parseable, + dladm_print_field, (void *)&ubuf); + + return (DLADM_STATUS_OK); +} + +static boolean_t +valid_formatspec(char *formatspec_str) +{ + if (strcmp(formatspec_str, "gnuplot") == 0) + return (B_TRUE); + return (B_FALSE); + +} + +/*ARGSUSED*/ +static void +do_show_usage(int argc, char *argv[], const char *use) +{ + char *file = NULL; + int opt; + dladm_status_t status; + boolean_t d_arg = B_FALSE; + boolean_t p_arg = B_FALSE; + char *stime = NULL; + char *etime = NULL; + char *resource = NULL; + show_usage_state_t state; + boolean_t o_arg = B_FALSE; + boolean_t F_arg = B_FALSE; + char *fields_str = NULL; + char *formatspec_str = NULL; + print_field_t **fields; + uint_t nfields; + char *all_fields = + "link,duration,ipackets,rbytes,opackets,obytes,bandwidth"; + char *all_l_fields = + "link,start,end,rbytes,obytes,bandwidth"; + + bzero(&state, sizeof (show_usage_state_t)); + state.us_parseable = B_FALSE; + state.us_printheader = B_FALSE; + state.us_plot = B_FALSE; + state.us_first = B_TRUE; + + while ((opt = getopt(argc, argv, "dps:e:o:f:F:")) != -1) { + switch (opt) { + case 'd': + d_arg = B_TRUE; + break; + case 'p': + state.us_plot = p_arg = B_TRUE; + break; + case 'f': + file = optarg; + break; + case 's': + stime = optarg; + break; + case 'e': + etime = optarg; + break; + case 'o': + o_arg = B_TRUE; + fields_str = optarg; + break; + case 'F': + F_arg = B_TRUE; + formatspec_str = optarg; + break; + default: + die_opterr(optopt, opt, use); + break; + } + } + + if (file == NULL) + die("show-usage requires a file"); + + if (optind == (argc-1)) { + resource = argv[optind]; + } + + if (resource == NULL && stime == NULL && etime == NULL) { + if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) + fields_str = all_fields; + fields = parse_output_fields(fields_str, usage_fields, + USAGE_MAX_FIELDS, CMD_TYPE_ANY, &nfields); + } else { + if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) + fields_str = all_l_fields; + fields = parse_output_fields(fields_str, usage_l_fields, + USAGE_L_MAX_FIELDS, CMD_TYPE_ANY, &nfields); + } + + if (fields == NULL) { + die("invalid fields(s) specified"); + return; + } + state.us_print.ps_fields = fields; + state.us_print.ps_nfields = nfields; + + if (p_arg && d_arg) + die("plot and date options are incompatible"); + + if (p_arg && !F_arg) + die("specify format speicifier: -F <format>"); + + if (F_arg && valid_formatspec(formatspec_str) == B_FALSE) + die("Format specifier %s not supported", formatspec_str); + + if (d_arg) { + /* Print log dates */ + status = dladm_usage_dates(show_usage_date, + DLADM_LOGTYPE_LINK, file, resource, &state); + } else if (resource == NULL && stime == NULL && etime == NULL && + !p_arg) { + /* Print summary */ + status = dladm_usage_summary(show_usage_res, + DLADM_LOGTYPE_LINK, file, &state); + } else if (resource != NULL) { + /* Print log entries for named resource */ + status = dladm_walk_usage_res(show_usage_time, + DLADM_LOGTYPE_LINK, file, resource, stime, etime, &state); + } else { + /* Print time and information for each link */ + status = dladm_walk_usage_time(show_usage_time, + DLADM_LOGTYPE_LINK, file, stime, etime, &state); + } + + if (status != DLADM_STATUS_OK) + die_dlerr(status, "show-usage"); +} + static void do_create_aggr(int argc, char *argv[], const char *use) { @@ -889,9 +1323,13 @@ do_create_aggr(int argc, char *argv[], const char *use) char *devs[MAXPORT]; char *links[MAXPORT]; dladm_status_t status; + dladm_status_t pstatus; + dladm_arg_list_t *proplist = NULL; + int i; + datalink_id_t linkid; ndev = nlink = opterr = 0; - while ((option = getopt_long(argc, argv, ":d:l:L:P:R:tfu:T:", + while ((option = getopt_long(argc, argv, ":d:l:L:P:R:tfu:T:p:", lopts, NULL)) != -1) { switch (option) { case 'd': @@ -955,6 +1393,11 @@ do_create_aggr(int argc, char *argv[], const char *use) case 'R': altroot = optarg; break; + case 'p': + if (dladm_parse_link_props(optarg, &proplist, B_FALSE) + != DLADM_STATUS_OK) + die("invalid aggregation property"); + break; default: die_opterr(optopt, option, use); break; @@ -1000,7 +1443,30 @@ do_create_aggr(int argc, char *argv[], const char *use) status = dladm_aggr_create(name, key, ndev + nlink, port, policy, mac_addr_fixed, (const uchar_t *)mac_addr, lacp_mode, lacp_timer, flags); + if (status != DLADM_STATUS_OK) + goto done; + + if (proplist == NULL) + return; + + status = dladm_name2info(name, &linkid, NULL, NULL, NULL); + if (status != DLADM_STATUS_OK) + goto done; + + for (i = 0; i < proplist->al_count; i++) { + dladm_arg_info_t *aip = &proplist->al_info[i]; + + pstatus = dladm_set_linkprop(linkid, aip->ai_name, + aip->ai_val, aip->ai_count, flags); + + if (pstatus != DLADM_STATUS_OK) { + die_dlerr(pstatus, + "aggr creation succeeded but " + "could not set property '%s'", aip->ai_name); + } + } done: + dladm_free_props(proplist); if (status != DLADM_STATUS_OK) { if (status == DLADM_STATUS_NONOTIF) { die_dlerr(status, "not all links have link up/down " @@ -1379,19 +1845,21 @@ done: 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; + char *link = NULL; + char drv[DLPI_LINKNAME_MAX]; + uint_t ppa; + datalink_id_t linkid; + datalink_id_t dev_linkid; + int vid = 0; + char option; + uint32_t flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); + char *altroot = NULL; + char vlan[MAXLINKNAMELEN]; + dladm_arg_list_t *proplist = NULL; + dladm_status_t status; opterr = 0; - while ((option = getopt_long(argc, argv, ":tfl:v:", + while ((option = getopt_long(argc, argv, ":tfR:l:v:p:", lopts, NULL)) != -1) { switch (option) { case 'v': @@ -1408,15 +1876,21 @@ do_create_vlan(int argc, char *argv[], const char *use) link = optarg; break; - case 'f': - flags |= DLADM_OPT_FORCE; - break; case 't': flags &= ~DLADM_OPT_PERSIST; break; case 'R': altroot = optarg; break; + case 'p': + if (dladm_parse_link_props(optarg, &proplist, B_FALSE) + != DLADM_STATUS_OK) { + die("invalid vlan property"); + } + break; + case 'f': + flags |= DLADM_OPT_FORCE; + break; default: die_opterr(optopt, option, use); break; @@ -1444,19 +1918,14 @@ do_create_vlan(int argc, char *argv[], const char *use) if (altroot != NULL) altroot_cmd(altroot, argc, argv); - if (dladm_name2info(link, &linkid, NULL, NULL, NULL) != + if (dladm_name2info(link, &dev_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"); - } + if ((status = dladm_vlan_create(vlan, dev_linkid, vid, proplist, flags, + &linkid)) != DLADM_STATUS_OK) { + die_dlerr(status, "create operation over %s failed", link); } } @@ -1505,31 +1974,7 @@ done: 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"); - } - } + do_up_vnic_common(argc, argv, use, B_TRUE); } static void @@ -1724,7 +2169,7 @@ print_link_topology(show_state_t *state, datalink_id_t linkid, } free(ginfo.lg_ports); } else if (class == DATALINK_CLASS_VNIC) { - dladm_vnic_attr_sys_t vinfo; + dladm_vnic_attr_t vinfo; if ((status = dladm_vnic_info(linkid, &vinfo, flags)) != DLADM_STATUS_OK || (status = dladm_datalink_id2info( @@ -1816,7 +2261,6 @@ done: return (status); } - static int show_link(datalink_id_t linkid, void *arg) { @@ -1854,7 +2298,6 @@ show_link_stats(datalink_id_t linkid, void *arg) 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) @@ -1881,12 +2324,15 @@ show_link_stats(datalink_id_t linkid, void *arg) } else { get_link_stats(link, &stats); } - stats_diff(&diff_stats, &stats, &state->ls_prevstats); + dladm_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); + (void) printf("%-12s", link); + (void) printf("%-10llu", diff_stats.ipackets); + (void) printf("%-12llu", diff_stats.rbytes); + (void) printf("%-8llu", diff_stats.ierrors); + (void) printf("%-10llu", diff_stats.opackets); + (void) printf("%-12llu", diff_stats.obytes); + (void) printf("%-8llu\n", diff_stats.oerrors); state->ls_prevstats = stats; return (DLADM_WALK_CONTINUE); @@ -2192,7 +2638,7 @@ print_aggr_stats_callback(print_field_t *pf, void *arg) goto err; } - stats_diff(&diff_stats, &port_stat, l->laggr_prevstats); + dladm_stats_diff(&diff_stats, &port_stat, l->laggr_prevstats); } switch (pf->pf_index) { @@ -2296,7 +2742,8 @@ print_aggr_stats(show_grp_state_t *state, const char *link, } get_mac_stats(dpa.dp_dev, &port_stat); - stats_total(&pktsumtot, &port_stat, &state->gs_prevstats[i]); + dladm_stats_total(&pktsumtot, &port_stat, + &state->gs_prevstats[i]); } if (!state->gs_parseable && !state->gs_printheader) { @@ -2381,127 +2828,17 @@ done: 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 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; + char linkname[MAXLINKNAMELEN]; int interval = 0; show_state_t state; dladm_status_t status; @@ -2517,7 +2854,7 @@ do_show_link(int argc, char *argv[], const char *use) bzero(&state, sizeof (state)); opterr = 0; - while ((option = getopt_long(argc, argv, ":pPsi:o:", + while ((option = getopt_long(argc, argv, ":pPsSi:o:", show_lopts, NULL)) != -1) { switch (option) { case 'p': @@ -2538,6 +2875,12 @@ do_show_link(int argc, char *argv[], const char *use) 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; @@ -2556,19 +2899,32 @@ do_show_link(int argc, char *argv[], const char *use) } } - if (i_arg && !s_arg) - die("the option -i can be used only with -s"); + if (i_arg && !(s_arg || S_arg)) + die("the option -i can be used only with -s or -S"); + + if (s_arg && S_arg) + die("the -s option cannot be used with -S"); if (s_arg && flags != DLADM_OPT_ACTIVE) die("the option -P cannot be used with -s"); + if (S_arg && (p_arg || flags != DLADM_OPT_ACTIVE)) + die("the option -%c cannot be used with -S", p_arg ? 'p' : 'P'); + /* get link name (optional last argument) */ if (optind == (argc-1)) { uint32_t f; - if ((status = dladm_name2info(argv[optind], &linkid, &f, + if (strlcpy(linkname, argv[optind], MAXLINKNAMELEN) + >= MAXLINKNAMELEN) { + (void) fprintf(stderr, + gettext("%s: link name too long\n"), + progname); + exit(1); + } + if ((status = dladm_name2info(linkname, &linkid, &f, NULL, NULL)) != DLADM_STATUS_OK) { - die_dlerr(status, "link %s is not valid", argv[optind]); + die_dlerr(status, "link %s is not valid", linkname); } if (!(f & flags)) { @@ -2583,6 +2939,11 @@ do_show_link(int argc, char *argv[], const char *use) if (p_arg && !o_arg) die("-p requires -o"); + if (S_arg) { + dladm_continuous(linkid, NULL, interval, LINK_REPORT); + return; + } + if (p_arg && strcasecmp(fields_str, "all") == 0) die("\"-o all\" is invalid with -p"); @@ -2604,7 +2965,6 @@ do_show_link(int argc, char *argv[], const char *use) return; } - fields = parse_output_fields(fields_str, link_fields, DEV_LINK_FIELDS, CMD_TYPE_ANY, &nfields); @@ -2641,17 +3001,17 @@ do_show_aggr(int argc, char *argv[], const char *use) 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 = + 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 = + char *all_lacp_fields = "link,port,aggregatable,sync,coll,dist,defaulted,expired"; - char *all_stats_fields = + char *all_stats_fields = "link,port,ipackets,rbytes,opackets,obytes,ipktdist,opktdist"; - char *all_extended_fields = + char *all_extended_fields = "link,port,speed,duplex,state,address,portstate"; print_field_t *pf; int pfmax; @@ -2806,138 +3166,222 @@ do_show_aggr(int argc, char *argv[], const char *use) } } -static void -do_show_dev(int argc, char *argv[], const char *use) +static dladm_status_t +print_phys_default(show_state_t *state, datalink_id_t linkid, + const char *link, uint32_t flags, uint32_t media) { - 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"; + dladm_phys_attr_t dpa; + dladm_status_t status; + link_fields_buf_t pattr; - bzero(&state, sizeof (state)); - fields_str = all_fields; + status = dladm_phys_info(linkid, &dpa, state->ls_flags); + if (status != DLADM_STATUS_OK) + goto done; - opterr = 0; - while ((option = getopt_long(argc, argv, ":psi:o:", - show_lopts, NULL)) != -1) { - switch (option) { - case 'p': - if (p_arg) - die_optdup(option); + (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; - p_arg = B_TRUE; - break; - case 's': - if (s_arg) - die_optdup(option); + 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; + } - s_arg = B_TRUE; - break; - case 'o': - o_arg = B_TRUE; - fields_str = optarg; - break; - case 'i': - if (i_arg) - die_optdup(option); + (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'); + } - 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 (!state->ls_parseable && !state->ls_printheader) { + print_header(&state->ls_print); + state->ls_printheader = B_TRUE; } - if (p_arg && !o_arg) - die("-p requires -o"); + dladm_print_output(&state->ls_print, state->ls_parseable, + dladm_print_field, (void *)&pattr); - if (p_arg && strcasecmp(fields_str, "all") == 0) - die("\"-o all\" is invalid with -p"); +done: + return (status); +} - if (i_arg && !s_arg) - die("the option -i can be used only with -s"); +typedef struct { + show_state_t *ms_state; + char *ms_link; + dladm_macaddr_attr_t *ms_mac_attr; +} print_phys_mac_state_t; - if (o_arg && strcasecmp(fields_str, "all") == 0) { - if (!s_arg) - fields_str = all_fields; +/* callback of dladm_print_output() */ +static char * +print_phys_one_mac_callback(print_field_t *pf, void *arg) +{ + print_phys_mac_state_t *mac_state = arg; + dladm_macaddr_attr_t *attr = mac_state->ms_mac_attr; + static char buf[DLADM_STRSIZE]; + boolean_t is_primary = (attr->ma_slot == 0); + boolean_t is_parseable = mac_state->ms_state->ls_parseable; + + switch (pf->pf_index) { + case PHYS_M_LINK: + (void) snprintf(buf, sizeof (buf), "%s", + (is_primary || is_parseable) ? mac_state->ms_link : " "); + break; + case PHYS_M_SLOT: + if (is_primary) + (void) snprintf(buf, sizeof (buf), gettext("primary")); else - fields_str = allstat_fields; + (void) snprintf(buf, sizeof (buf), "%d", attr->ma_slot); + break; + case PHYS_M_ADDRESS: + (void) dladm_aggr_macaddr2str(attr->ma_addr, buf); + break; + case PHYS_M_INUSE: + (void) snprintf(buf, sizeof (buf), "%s", + attr->ma_flags & DLADM_MACADDR_USED ? gettext("yes") : + gettext("no")); + break; + case PHYS_M_CLIENT: + /* + * CR 6678526: resolve link id to actual link name if + * it is valid. + */ + (void) snprintf(buf, sizeof (buf), "%s", attr->ma_client_name); + break; } - 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; + return (buf); +} - dev = argv[optind]; +typedef struct { + show_state_t *hs_state; + char *hs_link; + dladm_hwgrp_attr_t *hs_grp_attr; +} print_phys_hwgrp_state_t; - if (dladm_dev2linkid(dev, &linkid) != DLADM_STATUS_OK) - die("invalid device %s", dev); +static char * +print_phys_one_hwgrp_callback(print_field_t *pf, void *arg) +{ + print_phys_hwgrp_state_t *hg_state = arg; + dladm_hwgrp_attr_t *attr = hg_state->hs_grp_attr; + static char buf[DLADM_STRSIZE]; - if ((dladm_datalink_id2info(linkid, &flags, NULL, NULL, - NULL, 0) != DLADM_STATUS_OK) || - !(flags & DLADM_OPT_ACTIVE)) { - die("device %s has been removed", dev); + switch (pf->pf_index) { + case PHYS_H_LINK: + (void) snprintf(buf, sizeof (buf), "%s", attr->hg_link_name); + break; + case PHYS_H_GROUP: + (void) snprintf(buf, sizeof (buf), "%d", attr->hg_grp_num); + break; + case PHYS_H_GRPTYPE: + (void) snprintf(buf, sizeof (buf), "%s", + attr->hg_grp_type == DLADM_HWGRP_TYPE_RX ? "RX" : "TX"); + break; + case PHYS_H_RINGS: + (void) snprintf(buf, sizeof (buf), "%d", attr->hg_n_rings); + break; + case PHYS_H_CLIENTS: + if (attr->hg_client_names[0] == '\0') { + (void) snprintf(buf, sizeof (buf), "--"); + } else { + (void) snprintf(buf, sizeof (buf), "%s ", + attr->hg_client_names); } - } else if (optind != argc) { - usage(); + break; } - state.ls_parseable = p_arg; - state.ls_donefirst = B_FALSE; + return (buf); +} - if (s_arg) { - dev_stats(dev, interval, fields_str, &state); - return; +/* callback of dladm_walk_macaddr, invoked for each MAC address slot */ +static boolean_t +print_phys_mac_callback(void *arg, dladm_macaddr_attr_t *attr) +{ + print_phys_mac_state_t *mac_state = arg; + show_state_t *state = mac_state->ms_state; + + if (!state->ls_parseable && !state->ls_printheader) { + print_header(&state->ls_print); + state->ls_printheader = B_TRUE; } - fields = parse_output_fields(fields_str, dev_fields, DEV_MAX_FIELDS, - CMD_TYPE_ANY, &nfields); + mac_state->ms_mac_attr = attr; + dladm_print_output(&state->ls_print, state->ls_parseable, + print_phys_one_mac_callback, mac_state); - if (fields == NULL) { - die("invalid field(s) specified"); - return; - } + return (B_TRUE); +} - state.ls_print.ps_fields = fields; - state.ls_print.ps_nfields = nfields; +/* invoked by show-phys -m for each physical data-link */ +static dladm_status_t +print_phys_mac(show_state_t *state, datalink_id_t linkid, char *link) +{ + print_phys_mac_state_t mac_state; - if (dev == NULL) { - (void) dladm_mac_walk(show_dev, &state); - } else { - (void) show_dev(dev, &state); + mac_state.ms_state = state; + mac_state.ms_link = link; + + return (dladm_walk_macaddr(linkid, &mac_state, + print_phys_mac_callback)); +} + +/* callback of dladm_walk_hwgrp, invoked for each MAC hwgrp */ +static boolean_t +print_phys_hwgrp_callback(void *arg, dladm_hwgrp_attr_t *attr) +{ + print_phys_hwgrp_state_t *hwgrp_state = arg; + show_state_t *state = hwgrp_state->hs_state; + + if (!state->ls_parseable && !state->ls_printheader) { + print_header(&state->ls_print); + state->ls_printheader = B_TRUE; } + hwgrp_state->hs_grp_attr = attr; + dladm_print_output(&state->ls_print, state->ls_parseable, + print_phys_one_hwgrp_callback, hwgrp_state); + + return (B_TRUE); } +/* invoked by show-phys -H for each physical data-link */ +static dladm_status_t +print_phys_hwgrp(show_state_t *state, datalink_id_t linkid, char *link) +{ + print_phys_hwgrp_state_t hwgrp_state; + + hwgrp_state.hs_state = state; + hwgrp_state.hs_link = link; + return (dladm_walk_hwgrp(linkid, &hwgrp_state, + print_phys_hwgrp_callback)); +} static dladm_status_t -print_phys(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *pattr) +print_phys(show_state_t *state, datalink_id_t linkid) { char link[MAXLINKNAMELEN]; - dladm_phys_attr_t dpa; uint32_t flags; + dladm_status_t status; 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) { @@ -2954,44 +3398,12 @@ print_phys(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *pattr) 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'); - } + if (state->ls_mac) + status = print_phys_mac(state, linkid, link); + else if (state->ls_hwgrp) + status = print_phys_hwgrp(state, linkid, link); + else + status = print_phys_default(state, linkid, link, flags, media); done: return (status); @@ -3000,29 +3412,12 @@ done: 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); + show_state_t *state = arg; -done: - state->ls_status = status; + state->ls_status = print_phys(state, linkid); return (DLADM_WALK_CONTINUE); } - /* * Print the active topology information. */ @@ -3052,8 +3447,8 @@ print_vlan(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *l) (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' : '-'); + (void) snprintf(l->link_flags, sizeof (l->link_flags), "%c----", + vinfo.dv_force ? 'f' : '-'); done: return (status); @@ -3091,6 +3486,8 @@ do_show_phys(int argc, char *argv[], const char *use) uint32_t flags = DLADM_OPT_ACTIVE; boolean_t p_arg = B_FALSE; boolean_t o_arg = B_FALSE; + boolean_t m_arg = B_FALSE; + boolean_t H_arg = B_FALSE; datalink_id_t linkid = DATALINK_ALL_LINKID; show_state_t state; dladm_status_t status; @@ -3100,10 +3497,15 @@ do_show_phys(int argc, char *argv[], const char *use) char *all_active_fields = "link,media,state,speed,duplex,device"; char *all_inactive_fields = "link,device,media,flags"; + char *all_mac_fields = "link,slot,address,inuse,client"; + char *all_hwgrp_fields = + "link,group,grouptype,rings,clients"; + print_field_t *pf; + int pfmax; bzero(&state, sizeof (state)); opterr = 0; - while ((option = getopt_long(argc, argv, ":pPo:", + while ((option = getopt_long(argc, argv, ":pPo:mH", show_lopts, NULL)) != -1) { switch (option) { case 'p': @@ -3122,6 +3524,12 @@ do_show_phys(int argc, char *argv[], const char *use) o_arg = B_TRUE; fields_str = optarg; break; + case 'm': + m_arg = B_TRUE; + break; + case 'H': + H_arg = B_TRUE; + break; default: die_opterr(optopt, option, use); break; @@ -3131,6 +3539,9 @@ do_show_phys(int argc, char *argv[], const char *use) if (p_arg && !o_arg) die("-p requires -o"); + if (m_arg && H_arg) + die("-m cannot combine with -H"); + if (p_arg && strcasecmp(fields_str, "all") == 0) die("\"-o all\" is invalid with -p"); @@ -3147,16 +3558,42 @@ do_show_phys(int argc, char *argv[], const char *use) state.ls_parseable = p_arg; state.ls_flags = flags; state.ls_donefirst = B_FALSE; + state.ls_mac = m_arg; + state.ls_hwgrp = H_arg; + + if (m_arg && !(flags & DLADM_OPT_ACTIVE)) { + /* + * We can only display the factory MAC addresses of + * active data-links. + */ + die("-m not compatible with -P"); + } if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) { - if (state.ls_flags & DLADM_OPT_ACTIVE) + if (state.ls_mac) + fields_str = all_mac_fields; + else if (state.ls_hwgrp) + fields_str = all_hwgrp_fields; + else if (state.ls_flags & DLADM_OPT_ACTIVE) { fields_str = all_active_fields; - else + } else { fields_str = all_inactive_fields; + } + } + + if (state.ls_mac) { + pf = phys_m_fields; + pfmax = PHYS_M_MAX_FIELDS; + } else if (state.ls_hwgrp) { + pf = phys_h_fields; + pfmax = PHYS_H_MAX_FIELDS; + } else { + pf = phys_fields; + pfmax = PHYS_MAX_FIELDS; } - fields = parse_output_fields(fields_str, phys_fields, - PHYS_MAX_FIELDS, CMD_TYPE_ANY, &nfields); + fields = parse_output_fields(fields_str, pf, + pfmax, CMD_TYPE_ANY, &nfields); if (fields == NULL) { die("invalid field(s) specified"); @@ -3267,6 +3704,661 @@ do_show_vlan(int argc, char *argv[], const char *use) } static void +do_create_vnic(int argc, char *argv[], const char *use) +{ + datalink_id_t linkid, dev_linkid; + char devname[MAXLINKNAMELEN]; + char name[MAXLINKNAMELEN]; + boolean_t l_arg = B_FALSE; + uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; + char *altroot = NULL; + char option; + char *endp = NULL; + dladm_status_t status; + vnic_mac_addr_type_t mac_addr_type = VNIC_MAC_ADDR_TYPE_AUTO; + uchar_t *mac_addr; + int mac_slot = -1, maclen = 0, mac_prefix_len = 0; + dladm_arg_list_t *proplist = NULL; + uint16_t vid = 0; + + opterr = 0; + while ((option = getopt_long(argc, argv, ":tfR:l:m:n:p:r:v:H", + vnic_lopts, NULL)) != -1) { + switch (option) { + case 't': + flags &= ~DLADM_OPT_PERSIST; + break; + case 'R': + altroot = optarg; + break; + case 'l': + if (strlcpy(devname, optarg, MAXLINKNAMELEN) >= + MAXLINKNAMELEN) + die("link name too long"); + l_arg = B_TRUE; + break; + case 'm': + if (strcmp(optarg, "fixed") == 0) { + /* + * A fixed MAC address must be specified + * by its value, not by the keyword 'fixed'. + */ + die("'fixed' is not a valid MAC address"); + } + if (dladm_vnic_str2macaddrtype(optarg, + &mac_addr_type) != DLADM_STATUS_OK) { + mac_addr_type = VNIC_MAC_ADDR_TYPE_FIXED; + /* MAC address specified by value */ + mac_addr = _link_aton(optarg, &maclen); + if (mac_addr == NULL) { + if (maclen == -1) + die("invalid MAC address"); + else + die("out of memory"); + exit(1); + } + } + break; + case 'n': + errno = 0; + mac_slot = (int)strtol(optarg, &endp, 10); + if (errno != 0 || *endp != '\0') + die("invalid slot number"); + break; + case 'p': + if (dladm_parse_link_props(optarg, &proplist, B_FALSE) + != DLADM_STATUS_OK) + die("invalid vnic property"); + break; + case 'r': + mac_addr = _link_aton(optarg, &mac_prefix_len); + if (mac_addr == NULL) { + if (mac_prefix_len == -1) + die("invalid MAC address"); + else + die("out of memory"); + exit(1); + } + break; + case 'v': + vid = (int)strtol(optarg, &endp, 10); + if (errno != 0 || *endp != '\0' || vid == 0) + /* VID of 0 is invalid */ + die("invalid VLAN id"); + break; + case 'f': + flags |= DLADM_OPT_FORCE; + break; + case 'H': + flags |= DLADM_OPT_HWRINGS; + break; + default: + die_opterr(optopt, option, use); + } + } + + /* + * 'f' - force, flag can be specified only with 'v' - vlan. + */ + if ((flags & DLADM_OPT_FORCE) != 0 && vid == 0) + die("-f option can only be used with -v"); + + if (mac_prefix_len != 0 && mac_addr_type != VNIC_MAC_ADDR_TYPE_RANDOM && + mac_addr_type != VNIC_MAC_ADDR_TYPE_FIXED) + usage(); + + /* check required options */ + if (!l_arg) + usage(); + + if (mac_slot != -1 && mac_addr_type != VNIC_MAC_ADDR_TYPE_FACTORY) + usage(); + + /* the VNIC id is the required operand */ + if (optind != (argc - 1)) + usage(); + + 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]); + + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); + + if (dladm_name2info(devname, &dev_linkid, NULL, NULL, NULL) != + DLADM_STATUS_OK) + die("invalid link name '%s'", devname); + + status = dladm_vnic_create(name, dev_linkid, mac_addr_type, mac_addr, + maclen, &mac_slot, mac_prefix_len, vid, &linkid, proplist, flags); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "vnic creation over %s failed", devname); + + dladm_free_props(proplist); +} + +static void +do_etherstub_check(const char *name, datalink_id_t linkid, boolean_t etherstub, + uint32_t flags) +{ + boolean_t is_etherstub; + dladm_vnic_attr_t attr; + + if (dladm_vnic_info(linkid, &attr, flags) != DLADM_STATUS_OK) { + /* + * Let the delete continue anyway. + */ + return; + } + is_etherstub = (attr.va_link_id == DATALINK_INVALID_LINKID); + if (is_etherstub != etherstub) { + die("'%s' is not %s", name, + (is_etherstub ? "a vnic" : "an etherstub")); + } +} + +static void +do_delete_vnic_common(int argc, char *argv[], const char *use, + boolean_t etherstub) +{ + char option; + uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; + datalink_id_t linkid; + char *altroot = NULL; + 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); + } + } + + /* get vnic 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) + die("invalid link name '%s'", argv[optind]); + + if ((flags & DLADM_OPT_ACTIVE) != 0) { + do_etherstub_check(argv[optind], linkid, etherstub, + DLADM_OPT_ACTIVE); + } + if ((flags & DLADM_OPT_PERSIST) != 0) { + do_etherstub_check(argv[optind], linkid, etherstub, + DLADM_OPT_PERSIST); + } + + status = dladm_vnic_delete(linkid, flags); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "vnic deletion failed"); +} + +static void +do_delete_vnic(int argc, char *argv[], const char *use) +{ + do_delete_vnic_common(argc, argv, use, B_FALSE); +} + +/* ARGSUSED */ +static void +do_up_vnic_common(int argc, char *argv[], const char *use, boolean_t vlan) +{ + datalink_id_t linkid = DATALINK_ALL_LINKID; + dladm_status_t status; + char *type; + + type = vlan ? "vlan" : "vnic"; + + /* + * get the id or the name of the vnic/vlan (optional last argument) + */ + if (argc == 2) { + status = dladm_name2info(argv[1], &linkid, NULL, NULL, NULL); + if (status != DLADM_STATUS_OK) + goto done; + + } else if (argc > 2) { + usage(); + } + + if (vlan) + status = dladm_vlan_up(linkid); + else + status = dladm_vnic_up(linkid, 0); + +done: + if (status != DLADM_STATUS_OK) { + if (argc == 2) { + die_dlerr(status, + "could not bring up %s '%s'", type, argv[1]); + } else { + die_dlerr(status, "could not bring %ss up", type); + } + } +} + +static void +do_up_vnic(int argc, char *argv[], const char *use) +{ + do_up_vnic_common(argc, argv, use, B_FALSE); +} + +static void +dump_vnics_head(const char *dev) +{ + if (strlen(dev)) + (void) printf("%s", dev); + + (void) printf("\tipackets rbytes opackets obytes "); + + if (strlen(dev)) + (void) printf("%%ipkts %%opkts\n"); + else + (void) printf("\n"); +} + +static void +dump_vnic_stat(const char *name, datalink_id_t vnic_id, + show_vnic_state_t *state, pktsum_t *vnic_stats, pktsum_t *tot_stats) +{ + pktsum_t diff_stats; + pktsum_t *old_stats = &state->vs_prevstats[vnic_id]; + + dladm_stats_diff(&diff_stats, vnic_stats, old_stats); + + (void) printf("%s", name); + + (void) printf("\t%-10llu", diff_stats.ipackets); + (void) printf("%-12llu", diff_stats.rbytes); + (void) printf("%-10llu", diff_stats.opackets); + (void) printf("%-12llu", diff_stats.obytes); + + if (tot_stats) { + if (tot_stats->ipackets == 0) { + (void) printf("\t-"); + } else { + (void) printf("\t%-6.1f", (double)diff_stats.ipackets/ + (double)tot_stats->ipackets * 100); + } + if (tot_stats->opackets == 0) { + (void) printf("\t-"); + } else { + (void) printf("\t%-6.1f", (double)diff_stats.opackets/ + (double)tot_stats->opackets * 100); + } + } + (void) printf("\n"); + + *old_stats = *vnic_stats; +} + +/* + * Called from the walker dladm_vnic_walk_sys() for each vnic to display + * vnic information or statistics. + */ +static dladm_status_t +print_vnic(show_vnic_state_t *state, datalink_id_t linkid) +{ + dladm_vnic_attr_t attr, *vnic = &attr; + dladm_status_t status; + boolean_t is_etherstub; + char devname[MAXLINKNAMELEN]; + char vnic_name[MAXLINKNAMELEN]; + char mstr[MAXMACADDRLEN * 3]; + vnic_fields_buf_t vbuf; + + if ((status = dladm_vnic_info(linkid, vnic, state->vs_flags)) != + DLADM_STATUS_OK) + return (status); + + is_etherstub = (vnic->va_link_id == DATALINK_INVALID_LINKID); + if (state->vs_etherstub != is_etherstub) { + /* + * Want all etherstub but it's not one, or want + * non-etherstub and it's one. + */ + return (DLADM_STATUS_OK); + } + + if (state->vs_link_id != DATALINK_ALL_LINKID) { + if (state->vs_link_id != vnic->va_link_id) + return (DLADM_STATUS_OK); + } + + if (dladm_datalink_id2info(linkid, NULL, NULL, + NULL, vnic_name, sizeof (vnic_name)) != DLADM_STATUS_OK) + return (DLADM_STATUS_BADARG); + + bzero(devname, sizeof (devname)); + if (!is_etherstub && + dladm_datalink_id2info(vnic->va_link_id, NULL, NULL, + NULL, devname, sizeof (devname)) != DLADM_STATUS_OK) + return (DLADM_STATUS_BADARG); + + state->vs_found = B_TRUE; + if (state->vs_stats) { + /* print vnic statistics */ + pktsum_t vnic_stats; + + if (state->vs_firstonly) { + if (state->vs_donefirst) + return (0); + state->vs_donefirst = B_TRUE; + } + + if (!state->vs_printstats) { + /* + * get vnic statistics and add to the sum for the + * named device. + */ + get_link_stats(vnic_name, &vnic_stats); + dladm_stats_total(&state->vs_totalstats, &vnic_stats, + &state->vs_prevstats[vnic->va_vnic_id]); + } else { + /* get and print vnic statistics */ + get_link_stats(vnic_name, &vnic_stats); + dump_vnic_stat(vnic_name, linkid, state, &vnic_stats, + &state->vs_totalstats); + } + return (DLADM_STATUS_OK); + } else { + (void) snprintf(vbuf.vnic_link, sizeof (vbuf.vnic_link), + "%s", vnic_name); + + if (!is_etherstub) { + + (void) snprintf(vbuf.vnic_over, sizeof (vbuf.vnic_over), + "%s", devname); + (void) snprintf(vbuf.vnic_speed, + sizeof (vbuf.vnic_speed), "%u", + (uint_t)((get_ifspeed(vnic_name, B_TRUE)) + / 1000000ull)); + + switch (vnic->va_mac_addr_type) { + case VNIC_MAC_ADDR_TYPE_FIXED: + case VNIC_MAC_ADDR_TYPE_PRIMARY: + (void) snprintf(vbuf.vnic_macaddrtype, + sizeof (vbuf.vnic_macaddrtype), + gettext("fixed")); + break; + case VNIC_MAC_ADDR_TYPE_RANDOM: + (void) snprintf(vbuf.vnic_macaddrtype, + sizeof (vbuf.vnic_macaddrtype), + gettext("random")); + break; + case VNIC_MAC_ADDR_TYPE_FACTORY: + (void) snprintf(vbuf.vnic_macaddrtype, + sizeof (vbuf.vnic_macaddrtype), + gettext("factory, slot %d"), + vnic->va_mac_slot); + break; + } + + if (strlen(vbuf.vnic_macaddrtype) > 0) { + (void) snprintf(vbuf.vnic_macaddr, + sizeof (vbuf.vnic_macaddr), "%s", + dladm_aggr_macaddr2str(vnic->va_mac_addr, + mstr)); + } + + (void) snprintf(vbuf.vnic_vid, sizeof (vbuf.vnic_vid), + "%d", vnic->va_vid); + } + + if (!state->vs_parseable && !state->vs_printheader) { + print_header(&state->vs_print); + state->vs_printheader = B_TRUE; + } + + dladm_print_output(&state->vs_print, state->vs_parseable, + dladm_print_field, (void *)&vbuf); + + return (DLADM_STATUS_OK); + } +} + +static int +show_vnic(datalink_id_t linkid, void *arg) +{ + show_vnic_state_t *state = arg; + + state->vs_status = print_vnic(state, linkid); + return (DLADM_WALK_CONTINUE); +} + +static void +do_show_vnic_common(int argc, char *argv[], const char *use, + boolean_t etherstub) +{ + int option; + boolean_t s_arg = B_FALSE; + boolean_t i_arg = B_FALSE; + boolean_t l_arg = B_FALSE; + char *endp = NULL; + uint32_t interval = 0, flags = DLADM_OPT_ACTIVE; + datalink_id_t linkid = DATALINK_ALL_LINKID; + datalink_id_t dev_linkid = DATALINK_ALL_LINKID; + show_vnic_state_t state; + dladm_status_t status; + boolean_t o_arg = B_FALSE; + char *fields_str = NULL; + print_field_t **fields; + print_field_t *pf; + int pfmax; + uint_t nfields; + char *all_fields = + "link,over,speed,macaddr,macaddrtype,vid"; + char *all_e_fields = + "link"; + + bzero(&state, sizeof (state)); + opterr = 0; + while ((option = getopt_long(argc, argv, ":pPl:si:o:", lopts, + NULL)) != -1) { + switch (option) { + case 'p': + state.vs_parseable = B_TRUE; + break; + case 'P': + flags = DLADM_OPT_PERSIST; + break; + case 'l': + if (etherstub) + die("option not supported for this command"); + + if (strlcpy(state.vs_link, optarg, MAXLINKNAMELEN) >= + MAXLINKNAMELEN) + die("link name too long"); + + l_arg = B_TRUE; + break; + case 's': + if (s_arg) { + die("the option -s cannot be specified " + "more than once"); + } + s_arg = B_TRUE; + break; + case 'i': + if (i_arg) { + die("the option -i cannot be specified " + "more than once"); + } + i_arg = B_TRUE; + interval = (int)strtol(optarg, &endp, 10); + if (errno != 0 || interval == 0 || *endp != '\0') + die("invalid interval value '%s'", optarg); + break; + case 'o': + o_arg = B_TRUE; + fields_str = optarg; + break; + default: + die_opterr(optopt, option, use); + } + } + + if (i_arg && !s_arg) + die("the option -i can be used only with -s"); + + /* get vnic ID (optional last argument) */ + if (optind == (argc - 1)) { + status = dladm_name2info(argv[optind], &linkid, NULL, + NULL, NULL); + if (status != DLADM_STATUS_OK) { + die_dlerr(status, "invalid vnic name '%s'", + argv[optind]); + } + (void) strlcpy(state.vs_vnic, argv[optind], MAXLINKNAMELEN); + } else if (optind != argc) { + usage(); + } + + if (l_arg) { + status = dladm_name2info(state.vs_link, &dev_linkid, NULL, + NULL, NULL); + if (status != DLADM_STATUS_OK) { + die_dlerr(status, "invalid link name '%s'", + state.vs_link); + } + } + + state.vs_vnic_id = linkid; + state.vs_link_id = dev_linkid; + state.vs_etherstub = etherstub; + state.vs_found = B_FALSE; + state.vs_flags = flags; + + if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) { + if (etherstub) + fields_str = all_e_fields; + else + fields_str = all_fields; + } + + pf = vnic_fields; + pfmax = VNIC_MAX_FIELDS; + + fields = parse_output_fields(fields_str, pf, pfmax, CMD_TYPE_ANY, + &nfields); + + if (fields == NULL) { + die("invalid field(s) specified"); + return; + } + + state.vs_print.ps_fields = fields; + state.vs_print.ps_nfields = nfields; + + if (s_arg) { + /* Display vnic statistics */ + vnic_stats(&state, interval); + return; + } + + /* Display vnic information */ + state.vs_donefirst = B_FALSE; + + if (linkid == DATALINK_ALL_LINKID) { + (void) dladm_walk_datalink_id(show_vnic, &state, + DATALINK_CLASS_VNIC | DATALINK_CLASS_ETHERSTUB, + DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); + } else { + (void) show_vnic(linkid, &state); + if (state.vs_status != DLADM_STATUS_OK) { + die_dlerr(state.vs_status, "failed to show vnic '%s'", + state.vs_vnic); + } + } +} + +static void +do_show_vnic(int argc, char *argv[], const char *use) +{ + do_show_vnic_common(argc, argv, use, B_FALSE); +} + +static void +do_create_etherstub(int argc, char *argv[], const char *use) +{ + uint32_t flags; + char *altroot = NULL; + char option; + dladm_status_t status; + char name[MAXLINKNAMELEN]; + uchar_t mac_addr[ETHERADDRL]; + + name[0] = '\0'; + bzero(mac_addr, sizeof (mac_addr)); + flags = DLADM_OPT_ANCHOR | DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; + + opterr = 0; + while ((option = getopt_long(argc, argv, "tR:", + etherstub_lopts, NULL)) != -1) { + switch (option) { + case 't': + flags &= ~DLADM_OPT_PERSIST; + break; + case 'R': + altroot = optarg; + break; + default: + die_opterr(optopt, option, use); + } + } + + /* the etherstub id is the required operand */ + if (optind != (argc - 1)) + usage(); + + 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]); + + if (altroot != NULL) + altroot_cmd(altroot, argc, argv); + + status = dladm_vnic_create(name, DATALINK_INVALID_LINKID, + VNIC_MAC_ADDR_TYPE_AUTO, mac_addr, ETHERADDRL, NULL, 0, 0, NULL, + NULL, flags); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "etherstub creation failed"); + + +} + +static void +do_delete_etherstub(int argc, char *argv[], const char *use) +{ + do_delete_vnic_common(argc, argv, use, B_TRUE); +} + +/* ARGSUSED */ +static void +do_show_etherstub(int argc, char *argv[], const char *use) +{ + do_show_vnic_common(argc, argv, use, B_TRUE); +} + +static void link_stats(datalink_id_t linkid, uint_t interval, char *fields_str, show_state_t *state) { @@ -3333,147 +4425,134 @@ aggr_stats(datalink_id_t linkid, show_grp_state_t *state, uint_t interval) } } +/* ARGSUSED */ static void -dev_stats(const char *dev, uint32_t interval, char *fields_str, - show_state_t *state) +vnic_stats(show_vnic_state_t *sp, uint32_t interval) { - print_field_t **fields; - uint_t nfields; - - fields = parse_output_fields(fields_str, devs_fields, DEVS_MAX_FIELDS, - CMD_TYPE_ANY, &nfields); + show_vnic_state_t state; + boolean_t specific_link, specific_dev; - if (fields == NULL) { - die("invalid field(s) specified"); - return; - } - - state->ls_print.ps_fields = fields; - state->ls_print.ps_nfields = nfields; + /* Display vnic statistics */ + dump_vnics_head(sp->vs_link); + bzero(&state, sizeof (state)); + state.vs_stats = B_TRUE; + state.vs_vnic_id = sp->vs_vnic_id; + state.vs_link_id = sp->vs_link_id; /* - * If an interval is specified, continuously show the stats - * only for the first MAC port. + * If an interval is specified, and a vnic ID is not specified, + * continuously show the stats only for the first vnic. */ - state->ls_firstonly = (interval != 0); + specific_link = (sp->vs_vnic_id != DATALINK_ALL_LINKID); + specific_dev = (sp->vs_link_id != DATALINK_ALL_LINKID); for (;;) { + /* Get stats for each vnic */ + state.vs_found = B_FALSE; + state.vs_donefirst = B_FALSE; + state.vs_printstats = B_FALSE; + state.vs_flags = DLADM_OPT_ACTIVE; + + if (!specific_link) { + (void) dladm_walk_datalink_id(show_vnic, &state, + DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_ACTIVE); + } else { + (void) show_vnic(sp->vs_vnic_id, &state); + if (state.vs_status != DLADM_STATUS_OK) { + die_dlerr(state.vs_status, + "failed to show vnic '%s'", sp->vs_vnic); + } + } - if (!state->ls_parseable) - print_header(&state->ls_print); - state->ls_donefirst = B_FALSE; + if (specific_link && !state.vs_found) + die("non-existent vnic '%s'", sp->vs_vnic); + if (specific_dev && !state.vs_found) + die("device %s has no vnics", sp->vs_link); + + /* Show totals */ + if ((specific_link | specific_dev) && !interval) { + (void) printf("Total"); + (void) printf("\t%-10llu", + state.vs_totalstats.ipackets); + (void) printf("%-12llu", + state.vs_totalstats.rbytes); + (void) printf("%-10llu", + state.vs_totalstats.opackets); + (void) printf("%-12llu\n", + state.vs_totalstats.obytes); + } - if (dev == NULL) - (void) dladm_mac_walk(show_dev_stats, state); - else - (void) show_dev_stats(dev, state); + /* Show stats for each vnic */ + state.vs_donefirst = B_FALSE; + state.vs_printstats = B_TRUE; + + if (!specific_link) { + (void) dladm_walk_datalink_id(show_vnic, &state, + DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_ACTIVE); + } else { + (void) show_vnic(sp->vs_vnic_id, &state); + if (state.vs_status != DLADM_STATUS_OK) { + die_dlerr(state.vs_status, + "failed to show vnic '%s'", sp->vs_vnic); + } + } 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) +get_mac_stats(const char *dev, pktsum_t *stats) { kstat_ctl_t *kcp; kstat_t *ksp; + char module[DLPI_LINKNAME_MAX]; + uint_t instance; - if ((kcp = kstat_open()) == NULL) { - warn("kstat open operation failed"); + + bzero(stats, sizeof (*stats)); + + if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS) 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); + if ((kcp = kstat_open()) == NULL) { + warn("kstat open operation failed"); 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; + ksp = dladm_kstat_lookup(kcp, module, instance, "mac", NULL); + if (ksp != NULL) + dladm_get_stats(kcp, ksp, stats); -bail: (void) kstat_close(kcp); - return; } static void -get_mac_stats(const char *dev, pktsum_t *stats) +get_link_stats(const char *link, pktsum_t *stats) { - char module[DLPI_LINKNAME_MAX]; - uint_t instance; + kstat_ctl_t *kcp; + kstat_t *ksp; bzero(stats, sizeof (*stats)); - if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS) + + if ((kcp = kstat_open()) == NULL) { + warn("kstat_open operation failed"); return; + } - get_stats(module, instance, "mac", stats); -} + ksp = dladm_kstat_lookup(kcp, "link", 0, link, NULL); -static void -get_link_stats(const char *link, pktsum_t *stats) -{ - bzero(stats, sizeof (*stats)); - get_stats("link", 0, link, stats); + if (ksp != NULL) + dladm_get_stats(kcp, ksp, stats); + + (void) kstat_close(kcp); } static int @@ -3547,7 +4626,7 @@ get_linkstate(const char *name, boolean_t islink, char *buf) if (get_one_kstat(name, "link_state", KSTAT_DATA_UINT32, &linkstate, islink) != 0) { - (void) strlcpy(buf, "unknown", DLADM_STRSIZE); + (void) strlcpy(buf, "?", DLADM_STRSIZE); return (buf); } return (dladm_linkstate2str(linkstate, buf)); @@ -4271,92 +5350,6 @@ do_disconnect_wifi(int argc, char **argv, const char *use) 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, @@ -4365,7 +5358,7 @@ print_linkprop(datalink_id_t linkid, show_linkprop_state_t *statep, int i; char *ptr, *lim; char buf[DLADM_STRSIZE]; - char *unknown = "?", *notsup = ""; + char *unknown = "--", *notsup = ""; char **propvals = statep->ls_propvals; uint_t valcnt = DLADM_MAX_PROP_VALCNT; dladm_status_t status; @@ -4545,7 +5538,7 @@ static void do_show_linkprop(int argc, char **argv, const char *use) { int option; - prop_list_t *proplist = NULL; + dladm_arg_list_t *proplist = NULL; datalink_id_t linkid = DATALINK_ALL_LINKID; show_linkprop_state_t state; uint32_t flags = DLADM_OPT_ACTIVE; @@ -4570,7 +5563,8 @@ do_show_linkprop(int argc, char **argv, const char *use) prop_longopts, NULL)) != -1) { switch (option) { case 'p': - if (parse_props(optarg, &proplist, B_TRUE) < 0) + if (dladm_parse_link_props(optarg, &proplist, B_TRUE) + != DLADM_STATUS_OK) die("invalid link properties specified"); break; case 'c': @@ -4628,7 +5622,7 @@ do_show_linkprop(int argc, char **argv, const char *use) } else { (void) show_linkprop_onelink(linkid, &state); } - free_props(proplist); + dladm_free_props(proplist); if (state.ls_retstatus != DLADM_STATUS_OK) exit(EXIT_FAILURE); @@ -4640,7 +5634,7 @@ show_linkprop_onelink(datalink_id_t linkid, void *arg) int i; char *buf; uint32_t flags; - prop_list_t *proplist = NULL; + dladm_arg_list_t *proplist = NULL; show_linkprop_state_t *statep = arg; dlpi_handle_t dh = NULL; @@ -4689,9 +5683,9 @@ show_linkprop_onelink(datalink_id_t linkid, void *arg) (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT; if (proplist != NULL) { - for (i = 0; i < proplist->pl_count; i++) { + for (i = 0; i < proplist->al_count; i++) { (void) show_linkprop(linkid, - proplist->pl_info[i].pi_name, statep); + proplist->al_info[i].ai_name, statep); } } else { (void) dladm_walk_linkprop(linkid, statep, show_linkprop); @@ -4712,30 +5706,58 @@ set_linkprop_persist(datalink_id_t linkid, const char *prop_name, DLADM_OPT_PERSIST); if (status != DLADM_STATUS_OK) { - warn_dlerr(status, "cannot persistently %s link property", - reset ? "reset" : "set"); + warn_dlerr(status, "cannot persistently %s link property '%s'", + reset ? "reset" : "set", prop_name); } return (status); } +static int +reset_one_linkprop(datalink_id_t linkid, const char *propname, void *arg) +{ + set_linkprop_state_t *statep = arg; + dladm_status_t status; + + status = dladm_set_linkprop(linkid, propname, NULL, 0, + DLADM_OPT_ACTIVE); + if (status != DLADM_STATUS_OK) { + warn_dlerr(status, "cannot reset link property '%s' on '%s'", + propname, statep->ls_name); + } + if (!statep->ls_temp) { + dladm_status_t s; + + s = set_linkprop_persist(linkid, propname, NULL, 0, + statep->ls_reset); + if (s != DLADM_STATUS_OK) + status = s; + } + if (status != DLADM_STATUS_OK) + statep->ls_status = status; + + return (DLADM_WALK_CONTINUE); +} + 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; + int i, option; + char errmsg[DLADM_STRSIZE]; + char *altroot = NULL; + datalink_id_t linkid; + boolean_t temp = B_FALSE; + dladm_status_t status = DLADM_STATUS_OK; + dladm_arg_list_t *proplist = NULL; 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) + if (dladm_parse_link_props(optarg, &proplist, reset) != + DLADM_STATUS_OK) { die("invalid link properties specified"); + } break; case 't': temp = B_TRUE; @@ -4757,7 +5779,7 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) die("link property must be specified"); if (altroot != NULL) { - free_props(proplist); + dladm_free_props(proplist); altroot_cmd(altroot, argc, argv); } @@ -4766,24 +5788,21 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) 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; + set_linkprop_state_t state; - s = set_linkprop_persist(linkid, NULL, NULL, 0, reset); - if (s != DLADM_STATUS_OK) - status = s; - } + state.ls_name = argv[optind]; + state.ls_reset = reset; + state.ls_temp = temp; + state.ls_status = DLADM_STATUS_OK; + + (void) dladm_walk_linkprop(linkid, &state, reset_one_linkprop); + + status = state.ls_status; goto done; } - for (i = 0; i < proplist->pl_count; i++) { - prop_info_t *pip = &proplist->pl_info[i]; + for (i = 0; i < proplist->al_count; i++) { + dladm_arg_info_t *aip = &proplist->al_info[i]; char **val; uint_t count; dladm_status_t s; @@ -4792,21 +5811,21 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) val = NULL; count = 0; } else { - val = pip->pi_val; - count = pip->pi_count; + val = aip->ai_val; + count = aip->ai_count; if (count == 0) { warn("no value specified for '%s'", - pip->pi_name); + aip->ai_name); status = DLADM_STATUS_BADARG; continue; } } - s = dladm_set_linkprop(linkid, pip->pi_name, val, count, + s = dladm_set_linkprop(linkid, aip->ai_name, val, count, DLADM_OPT_ACTIVE); if (s == DLADM_STATUS_OK) { if (!temp) { s = set_linkprop_persist(linkid, - pip->pi_name, val, count, reset); + aip->ai_name, val, count, reset); if (s != DLADM_STATUS_OK) status = s; } @@ -4815,7 +5834,7 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) status = s; switch (s) { case DLADM_STATUS_NOTFOUND: - warn("invalid link property '%s'", pip->pi_name); + warn("invalid link property '%s'", aip->ai_name); break; case DLADM_STATUS_BADVAL: { int j; @@ -4837,12 +5856,12 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) j * DLADM_PROP_VAL_MAX; } s = dladm_get_linkprop(linkid, - DLADM_PROP_VAL_MODIFIABLE, pip->pi_name, propvals, + DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals, &valcnt); if (s != DLADM_STATUS_OK) { warn_dlerr(status, "cannot set link property " - "'%s' on '%s'", pip->pi_name, argv[optind]); + "'%s' on '%s'", aip->ai_name, argv[optind]); free(propvals); break; } @@ -4859,7 +5878,7 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) if (ptr > errmsg) { *(ptr - 1) = '\0'; warn("link property '%s' must be one of: %s", - pip->pi_name, errmsg); + aip->ai_name, errmsg); } else warn("invalid link property '%s'", *val); free(propvals); @@ -4868,16 +5887,16 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) default: if (reset) { warn_dlerr(status, "cannot reset link property " - "'%s' on '%s'", pip->pi_name, argv[optind]); + "'%s' on '%s'", aip->ai_name, argv[optind]); } else { warn_dlerr(status, "cannot set link property " - "'%s' on '%s'", pip->pi_name, argv[optind]); + "'%s' on '%s'", aip->ai_name, argv[optind]); } break; } } done: - free_props(proplist); + dladm_free_props(proplist); if (status != DLADM_STATUS_OK) exit(1); } @@ -5414,7 +6433,7 @@ i_dladm_init_linkprop(datalink_id_t linkid, void *arg) } /*ARGSUSED*/ -static void +void do_init_linkprop(int argc, char **argv, const char *use) { int option; @@ -5890,6 +6909,7 @@ show_ether_xprop(datalink_id_t linkid, void *arg) (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), |