| /* |
| * 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 2007 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <locale.h> |
| #include <signal.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <fcntl.h> |
| #include <stdarg.h> |
| #include <string.h> |
| #include <stropts.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 <libdlpi.h> |
| #include <libdladm.h> |
| #include <liblaadm.h> |
| #include <libmacadm.h> |
| #include <libwladm.h> |
| #include <libinetutil.h> |
| #include <bsm/adt.h> |
| #include <bsm/adt_event.h> |
| |
| #define AGGR_DRV "aggr" |
| #define MAXPORT 256 |
| #define DUMP_LACP_FORMAT " %-9s %-8s %-7s %-12s " \ |
| "%-5s %-4s %-4s %-9s %-7s\n" |
| |
| 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_link_state { |
| boolean_t ls_firstonly; |
| boolean_t ls_donefirst; |
| boolean_t ls_stats; |
| pktsum_t ls_prevstats; |
| boolean_t ls_parseable; |
| } show_link_state_t; |
| |
| typedef struct show_grp_state { |
| uint32_t gs_key; |
| boolean_t gs_lacp; |
| boolean_t gs_found; |
| boolean_t gs_stats; |
| boolean_t gs_firstonly; |
| pktsum_t gs_prevstats[MAXPORT]; |
| boolean_t gs_parseable; |
| } show_grp_state_t; |
| |
| typedef struct show_mac_state { |
| boolean_t ms_firstonly; |
| boolean_t ms_donefirst; |
| pktsum_t ms_prevstats; |
| boolean_t ms_parseable; |
| } show_mac_state_t; |
| |
| typedef struct port_state { |
| char *state_name; |
| aggr_port_state_t state_num; |
| } port_state_t; |
| |
| static port_state_t port_states[] = { |
| {"standby", AGGR_PORT_STATE_STANDBY }, |
| {"attached", AGGR_PORT_STATE_ATTACHED } |
| }; |
| |
| #define NPORTSTATES (sizeof (port_states) / sizeof (port_state_t)) |
| |
| typedef void cmdfunc_t(int, char **); |
| |
| static cmdfunc_t do_show_link, do_show_dev, do_show_wifi; |
| 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, do_down_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 void show_linkprop_onelink(void *, const char *); |
| |
| static void link_stats(const char *, uint_t); |
| static void aggr_stats(uint32_t, uint_t); |
| static void dev_stats(const char *dev, uint32_t); |
| |
| static void get_mac_stats(const char *, pktsum_t *); |
| static void get_link_stats(const char *, pktsum_t *); |
| static uint64_t mac_ifspeed(const char *); |
| static char *mac_link_state(const char *); |
| static char *mac_link_duplex(const char *); |
| static void stats_total(pktsum_t *, pktsum_t *, pktsum_t *); |
| static void stats_diff(pktsum_t *, pktsum_t *, pktsum_t *); |
| |
| static boolean_t str2int(const char *, int *); |
| static void die(const char *, ...); |
| static void die_optdup(int); |
| static void die_opterr(int, int); |
| static void die_laerr(laadm_diag_t, const char *, ...); |
| static void die_wlerr(wladm_status_t, const char *, ...); |
| static void die_dlerr(dladm_status_t, const char *, ...); |
| static void warn(const char *, ...); |
| static void warn_wlerr(wladm_status_t, const char *, ...); |
| static void warn_dlerr(dladm_status_t, const char *, ...); |
| |
| typedef struct cmd { |
| char *c_name; |
| cmdfunc_t *c_fn; |
| } cmd_t; |
| |
| static cmd_t cmds[] = { |
| { "show-link", do_show_link }, |
| { "show-dev", do_show_dev }, |
| { "create-aggr", do_create_aggr }, |
| { "delete-aggr", do_delete_aggr }, |
| { "add-aggr", do_add_aggr }, |
| { "remove-aggr", do_remove_aggr }, |
| { "modify-aggr", do_modify_aggr }, |
| { "show-aggr", do_show_aggr }, |
| { "up-aggr", do_up_aggr }, |
| { "down-aggr", do_down_aggr }, |
| { "scan-wifi", do_scan_wifi }, |
| { "connect-wifi", do_connect_wifi }, |
| { "disconnect-wifi", do_disconnect_wifi }, |
| { "show-wifi", do_show_wifi }, |
| { "show-linkprop", do_show_linkprop }, |
| { "set-linkprop", do_set_linkprop }, |
| { "reset-linkprop", do_reset_linkprop }, |
| { "create-secobj", do_create_secobj }, |
| { "delete-secobj", do_delete_secobj }, |
| { "show-secobj", do_show_secobj }, |
| { "init-linkprop", do_init_linkprop }, |
| { "init-secobj", do_init_secobj } |
| }; |
| |
| static const struct option longopts[] = { |
| {"vlan-id", required_argument, 0, 'v' }, |
| {"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' }, |
| {"statistics", no_argument, 0, 's' }, |
| {"interval", required_argument, 0, 'i' }, |
| {"lacp", no_argument, 0, 'L' }, |
| {"temporary", no_argument, 0, 't' }, |
| {"root-dir", required_argument, 0, 'r' }, |
| {"parseable", no_argument, 0, 'p' }, |
| { 0, 0, 0, 0 } |
| }; |
| |
| static const struct option prop_longopts[] = { |
| {"temporary", no_argument, 0, 't' }, |
| {"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 char *progname; |
| static sig_atomic_t signalled; |
| |
| static void |
| usage(void) |
| { |
| (void) fprintf(stderr, gettext("usage: dladm <subcommand> <args> ...\n" |
| "\tshow-link [-p] [-s [-i <interval>]] [<name>]\n" |
| "\tshow-dev [-p] [-s [-i <interval>]] [<dev>]\n" |
| "\n" |
| "\tcreate-aggr [-t] [-R <root-dir>] [-P <policy>] [-l <mode>]\n" |
| "\t [-T <time>] [-u <address>] -d <dev> ... <key>\n" |
| "\tmodify-aggr [-t] [-R <root-dir>] [-P <policy>] [-l <mode>]\n" |
| "\t [-T <time>] [-u <address>] <key>\n" |
| "\tdelete-aggr [-t] [-R <root-dir>] <key>\n" |
| "\tadd-aggr [-t] [-R <root-dir>] -d <dev> ... <key>\n" |
| "\tremove-aggr [-t] [-R <root-dir>] -d <dev> ... <key>\n" |
| "\tshow-aggr [-pL][-s [-i <interval>]] [<key>]\n" |
| "\n" |
| "\tscan-wifi [-p] [-o <field>,...] [<name>]\n" |
| "\tconnect-wifi [-e <essid>] [-i <bssid>] [-k <key>,...]" |
| " [-s wep]\n" |
| "\t [-a open|shared] [-b bss|ibss] [-c] [-m a|b|g]\n" |
| "\t [-T <time>] [<name>]\n" |
| "\tdisconnect-wifi [-a] [<name>]\n" |
| "\tshow-wifi [-p] [-o <field>,...] [<name>]\n" |
| "\n" |
| "\tset-linkprop [-t] [-R <root-dir>] -p <prop>=<value>[,...]" |
| " <name>\n" |
| "\treset-linkprop [-t] [-R <root-dir>] [-p <prop>,...] <name>\n" |
| "\tshow-linkprop [-cP][-p <prop>,...] <name>\n" |
| "\n" |
| "\tcreate-secobj [-t] [-R <root-dir>] [-f <file>] -c <class>" |
| " <secobj>\n" |
| "\tdelete-secobj [-t] [-R <root-dir>] <secobj>[,...]\n" |
| "\tshow-secobj [-pP][<secobj>,...]\n")); |
| 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(); |
| |
| if (!priv_ineffect(PRIV_SYS_NET_CONFIG) || |
| !priv_ineffect(PRIV_NET_RAWACCESS)) |
| die("insufficient privileges"); |
| |
| 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]); |
| 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[]) |
| { |
| char option; |
| int key; |
| 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; |
| laadm_port_attr_db_t port[MAXPORT]; |
| uint_t nport = 0; |
| 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 t_arg = B_FALSE; |
| boolean_t u_arg = B_FALSE; |
| boolean_t T_arg = B_FALSE; |
| char *altroot = NULL; |
| laadm_diag_t diag = 0; |
| |
| opterr = 0; |
| while ((option = getopt_long(argc, argv, ":d:l:P:R:tu:T:", |
| longopts, NULL)) != -1) { |
| switch (option) { |
| case 'd': |
| if (nport >= MAXPORT) |
| die("too many <dev> arguments"); |
| |
| if (strlcpy(port[nport].lp_devname, optarg, |
| MAXNAMELEN) >= MAXNAMELEN) |
| die("device name too long"); |
| |
| nport++; |
| break; |
| case 'P': |
| if (P_arg) |
| die_optdup(option); |
| |
| P_arg = B_TRUE; |
| if (!laadm_str_to_policy(optarg, &policy)) |
| die("invalid policy '%s'", optarg); |
| break; |
| case 'u': |
| if (u_arg) |
| die_optdup(option); |
| |
| u_arg = B_TRUE; |
| if (!laadm_str_to_mac_addr(optarg, &mac_addr_fixed, |
| mac_addr)) |
| die("invalid MAC address '%s'", optarg); |
| break; |
| case 'l': |
| if (l_arg) |
| die_optdup(option); |
| |
| l_arg = B_TRUE; |
| if (!laadm_str_to_lacp_mode(optarg, &lacp_mode)) |
| die("invalid LACP mode '%s'", optarg); |
| break; |
| case 'T': |
| if (T_arg) |
| die_optdup(option); |
| |
| T_arg = B_TRUE; |
| if (!laadm_str_to_lacp_timer(optarg, &lacp_timer)) |
| die("invalid LACP timer value '%s'", optarg); |
| break; |
| case 't': |
| t_arg = B_TRUE; |
| break; |
| case 'R': |
| altroot = optarg; |
| break; |
| default: |
| die_opterr(optopt, option); |
| break; |
| } |
| } |
| |
| if (nport == 0) |
| usage(); |
| |
| /* get key value (required last argument) */ |
| if (optind != (argc-1)) |
| usage(); |
| |
| if (!str2int(argv[optind], &key) || key < 1) |
| die("invalid key value '%s'", argv[optind]); |
| |
| if (laadm_create(key, nport, port, policy, mac_addr_fixed, |
| mac_addr, lacp_mode, lacp_timer, t_arg, altroot, &diag) < 0) |
| die_laerr(diag, "create operation failed"); |
| } |
| |
| static void |
| do_delete_aggr(int argc, char *argv[]) |
| { |
| int key; |
| char option; |
| boolean_t t_arg = B_FALSE; |
| char *altroot = NULL; |
| laadm_diag_t diag = 0; |
| |
| opterr = 0; |
| while ((option = getopt_long(argc, argv, ":R:t", longopts, |
| NULL)) != -1) { |
| switch (option) { |
| case 't': |
| t_arg = B_TRUE; |
| break; |
| case 'R': |
| altroot = optarg; |
| break; |
| default: |
| die_opterr(optopt, option); |
| break; |
| } |
| } |
| |
| /* get key value (required last argument) */ |
| if (optind != (argc-1)) |
| usage(); |
| |
| if (!str2int(argv[optind], &key) || key < 1) |
| die("invalid key value '%s'", argv[optind]); |
| |
| if (laadm_delete(key, t_arg, altroot, &diag) < 0) |
| die_laerr(diag, "delete operation failed"); |
| } |
| |
| static void |
| do_add_aggr(int argc, char *argv[]) |
| { |
| char option; |
| int key; |
| laadm_port_attr_db_t port[MAXPORT]; |
| uint_t nport = 0; |
| boolean_t t_arg = B_FALSE; |
| char *altroot = NULL; |
| laadm_diag_t diag = 0; |
| |
| opterr = 0; |
| while ((option = getopt_long(argc, argv, ":d:R:t", longopts, |
| NULL)) != -1) { |
| switch (option) { |
| case 'd': |
| if (nport >= MAXPORT) |
| die("too many <dev> arguments"); |
| |
| if (strlcpy(port[nport].lp_devname, optarg, |
| MAXNAMELEN) >= MAXNAMELEN) |
| die("device name too long"); |
| |
| nport++; |
| break; |
| case 't': |
| t_arg = B_TRUE; |
| break; |
| case 'R': |
| altroot = optarg; |
| break; |
| default: |
| die_opterr(optopt, option); |
| break; |
| } |
| } |
| |
| if (nport == 0) |
| usage(); |
| |
| /* get key value (required last argument) */ |
| if (optind != (argc-1)) |
| usage(); |
| |
| if (!str2int(argv[optind], &key) || key < 1) |
| die("invalid key value '%s'", argv[optind]); |
| |
| if (laadm_add(key, nport, port, t_arg, altroot, &diag) < 0) { |
| /* |
| * checking ENOTSUP is a temporary workaround |
| * and should be removed once 6399681 is fixed. |
| */ |
| if (errno == ENOTSUP) { |
| (void) fprintf(stderr, |
| gettext("%s: add operation failed: %s\n"), |
| progname, |
| gettext("device capabilities don't match")); |
| exit(ENOTSUP); |
| } |
| die_laerr(diag, "add operation failed"); |
| } |
| } |
| |
| static void |
| do_remove_aggr(int argc, char *argv[]) |
| { |
| char option; |
| int key; |
| laadm_port_attr_db_t port[MAXPORT]; |
| uint_t nport = 0; |
| boolean_t t_arg = B_FALSE; |
| char *altroot = NULL; |
| laadm_diag_t diag = 0; |
| |
| opterr = 0; |
| while ((option = getopt_long(argc, argv, ":d:R:t", |
| longopts, NULL)) != -1) { |
| switch (option) { |
| case 'd': |
| if (nport >= MAXPORT) |
| die("too many <dev> arguments"); |
| |
| if (strlcpy(port[nport].lp_devname, optarg, |
| MAXNAMELEN) >= MAXNAMELEN) |
| die("device name too long"); |
| |
| nport++; |
| break; |
| case 't': |
| t_arg = B_TRUE; |
| break; |
| case 'R': |
| altroot = optarg; |
| break; |
| default: |
| die_opterr(optopt, option); |
| break; |
| } |
| } |
| |
| if (nport == 0) |
| usage(); |
| |
| /* get key value (required last argument) */ |
| if (optind != (argc-1)) |
| usage(); |
| |
| if (!str2int(argv[optind], &key) || key < 1) |
| die("invalid key value '%s'", argv[optind]); |
| |
| if (laadm_remove(key, nport, port, t_arg, altroot, &diag) < 0) |
| die_laerr(diag, "remove operation failed"); |
| } |
| |
| static void |
| do_modify_aggr(int argc, char *argv[]) |
| { |
| char option; |
| int key; |
| 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; |
| boolean_t t_arg = B_FALSE; |
| char *altroot = NULL; |
| laadm_diag_t diag = 0; |
| |
| opterr = 0; |
| while ((option = getopt_long(argc, argv, ":l:P:R:tu:T:", longopts, |
| NULL)) != -1) { |
| switch (option) { |
| case 'P': |
| if (modify_mask & LAADM_MODIFY_POLICY) |
| die_optdup(option); |
| |
| modify_mask |= LAADM_MODIFY_POLICY; |
| |
| if (!laadm_str_to_policy(optarg, &policy)) |
| die("invalid policy '%s'", optarg); |
| break; |
| case 'u': |
| if (modify_mask & LAADM_MODIFY_MAC) |
| die_optdup(option); |
| |
| modify_mask |= LAADM_MODIFY_MAC; |
| |
| if (!laadm_str_to_mac_addr(optarg, &mac_addr_fixed, |
| mac_addr)) |
| die("invalid MAC address '%s'", optarg); |
| break; |
| case 'l': |
| if (modify_mask & LAADM_MODIFY_LACP_MODE) |
| die_optdup(option); |
| |
| modify_mask |= LAADM_MODIFY_LACP_MODE; |
| |
| if (!laadm_str_to_lacp_mode(optarg, &lacp_mode)) |
| die("invalid LACP mode '%s'", optarg); |
| break; |
| case 'T': |
| if (modify_mask & LAADM_MODIFY_LACP_TIMER) |
| die_optdup(option); |
| |
| modify_mask |= LAADM_MODIFY_LACP_TIMER; |
| |
| if (!laadm_str_to_lacp_timer(optarg, &lacp_timer)) |
| die("invalid LACP timer value '%s'", optarg); |
| break; |
| case 't': |
| t_arg = B_TRUE; |
| break; |
| case 'R': |
| altroot = optarg; |
| break; |
| default: |
| die_opterr(optopt, option); |
| break; |
| } |
| } |
| |
| if (modify_mask == 0) |
| die("at least one of the -PulT options must be specified"); |
| |
| /* get key value (required last argument) */ |
| if (optind != (argc-1)) |
| usage(); |
| |
| if (!str2int(argv[optind], &key) || key < 1) |
| die("invalid key value '%s'", argv[optind]); |
| |
| if (laadm_modify(key, modify_mask, policy, mac_addr_fixed, mac_addr, |
| lacp_mode, lacp_timer, t_arg, altroot, &diag) < 0) |
| die_laerr(diag, "modify operation failed"); |
| } |
| |
| static void |
| do_up_aggr(int argc, char *argv[]) |
| { |
| int key = 0; |
| laadm_diag_t diag = 0; |
| |
| /* get aggregation key (optional last argument) */ |
| if (argc == 2) { |
| if (!str2int(argv[1], &key) || key < 1) |
| die("invalid key value '%s'", argv[1]); |
| } else if (argc > 2) { |
| usage(); |
| } |
| |
| if (laadm_up(key, NULL, &diag) < 0) { |
| if (key != 0) { |
| die_laerr(diag, "could not bring up aggregation '%u'", |
| key); |
| } else { |
| die_laerr(diag, "could not bring aggregations up"); |
| } |
| } |
| } |
| |
| static void |
| do_down_aggr(int argc, char *argv[]) |
| { |
| int key = 0; |
| |
| /* get aggregation key (optional last argument) */ |
| if (argc == 2) { |
| if (!str2int(argv[1], &key) || key < 1) |
| die("invalid key value '%s'", argv[1]); |
| } else if (argc > 2) { |
| usage(); |
| } |
| |
| if (laadm_down(key) < 0) { |
| if (key != 0) { |
| die("could not bring down aggregation '%u': %s", |
| key, strerror(errno)); |
| } else { |
| die("could not bring down aggregations: %s", |
| strerror(errno)); |
| } |
| } |
| } |
| |
| #define TYPE_WIDTH 10 |
| |
| static void |
| print_link_parseable(const char *name, dladm_attr_t *dap, boolean_t legacy) |
| { |
| char type[TYPE_WIDTH]; |
| |
| if (!legacy) { |
| char drv[LIFNAMSIZ]; |
| int instance; |
| |
| if (dap->da_vid != 0) { |
| (void) snprintf(type, TYPE_WIDTH, "vlan %u", |
| dap->da_vid); |
| } else { |
| (void) snprintf(type, TYPE_WIDTH, "non-vlan"); |
| } |
| if (dlpi_if_parse(dap->da_dev, drv, &instance) != 0) |
| return; |
| if (strncmp(drv, AGGR_DRV, sizeof (AGGR_DRV)) == 0) { |
| (void) printf("%s type=%s mtu=%d key=%u\n", |
| name, type, dap->da_max_sdu, instance); |
| } else { |
| (void) printf("%s type=%s mtu=%d device=%s\n", |
| name, type, dap->da_max_sdu, dap->da_dev); |
| } |
| } else { |
| (void) printf("%s type=legacy mtu=%d device=%s\n", |
| name, dap->da_max_sdu, name); |
| } |
| } |
| |
| static void |
| print_link(const char *name, dladm_attr_t *dap, boolean_t legacy) |
| { |
| char type[TYPE_WIDTH]; |
| |
| if (!legacy) { |
| char drv[LIFNAMSIZ]; |
| int instance; |
| |
| if (dap->da_vid != 0) { |
| (void) snprintf(type, TYPE_WIDTH, gettext("vlan %u"), |
| dap->da_vid); |
| } else { |
| (void) snprintf(type, TYPE_WIDTH, gettext("non-vlan")); |
| } |
| if (dlpi_if_parse(dap->da_dev, drv, &instance) != 0) |
| return; |
| if (strncmp(drv, AGGR_DRV, sizeof (AGGR_DRV)) == 0) { |
| (void) printf(gettext("%-9s\ttype: %s\tmtu: %d" |
| "\taggregation: key %u\n"), name, type, |
| dap->da_max_sdu, instance); |
| } else { |
| (void) printf(gettext("%-9s\ttype: %s\tmtu: " |
| "%d\tdevice: %s\n"), name, type, dap->da_max_sdu, |
| dap->da_dev); |
| } |
| } else { |
| (void) printf(gettext("%-9s\ttype: legacy\tmtu: " |
| "%d\tdevice: %s\n"), name, dap->da_max_sdu, name); |
| } |
| } |
| |
| static int |
| get_if_info(const char *name, dladm_attr_t *dlattrp, boolean_t *legacy) |
| { |
| int err; |
| |
| if ((err = dladm_info(name, dlattrp)) == 0) { |
| *legacy = B_FALSE; |
| } else if (err < 0 && errno == ENODEV) { |
| int fd; |
| dlpi_if_attr_t dia; |
| dl_info_ack_t dlia; |
| |
| /* |
| * A return value of ENODEV means that the specified |
| * device is not gldv3. |
| */ |
| if ((fd = dlpi_if_open(name, &dia, B_FALSE)) != -1 && |
| dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, |
| NULL, NULL) != -1) { |
| (void) dlpi_close(fd); |
| |
| *legacy = B_TRUE; |
| bzero(dlattrp, sizeof (*dlattrp)); |
| dlattrp->da_max_sdu = (uint_t)dlia.dl_max_sdu; |
| } else { |
| errno = ENOENT; |
| return (-1); |
| } |
| } else { |
| /* |
| * If the return value is not ENODEV, this means that |
| * user is either passing in a bogus interface name |
| * or a vlan interface name that doesn't exist yet. |
| */ |
| errno = ENOENT; |
| return (-1); |
| } |
| return (0); |
| } |
| |
| /* ARGSUSED */ |
| static void |
| show_link(void *arg, const char *name) |
| { |
| dladm_attr_t dlattr; |
| boolean_t legacy = B_TRUE; |
| show_link_state_t *state = (show_link_state_t *)arg; |
| |
| if (get_if_info(name, &dlattr, &legacy) < 0) |
| die("invalid link '%s'", name); |
| |
| if (state->ls_parseable) { |
| print_link_parseable(name, &dlattr, legacy); |
| } else { |
| print_link(name, &dlattr, legacy); |
| } |
| } |
| |
| static void |
| show_link_stats(void *arg, const char *name) |
| { |
| show_link_state_t *state = (show_link_state_t *)arg; |
| pktsum_t stats, diff_stats; |
| |
| if (state->ls_firstonly) { |
| if (state->ls_donefirst) |
| return; |
| state->ls_donefirst = B_TRUE; |
| } else { |
| bzero(&state->ls_prevstats, sizeof (state->ls_prevstats)); |
| } |
| |
| get_link_stats(name, &stats); |
| stats_diff(&diff_stats, &stats, &state->ls_prevstats); |
| |
| (void) printf("%s", name); |
| (void) printf("\t\t%-10llu", diff_stats.ipackets); |
| (void) printf("%-12llu", diff_stats.rbytes); |
| (void) printf("%-8u", diff_stats.ierrors); |
| (void) printf("%-10llu", diff_stats.opackets); |
| (void) printf("%-12llu", diff_stats.obytes); |
| (void) printf("%-8u\n", diff_stats.oerrors); |
| |
| state->ls_prevstats = stats; |
| } |
| |
| static void |
| dump_grp(laadm_grp_attr_sys_t *grp, boolean_t parseable) |
| { |
| char policy_str[LAADM_POLICY_STR_LEN]; |
| char addr_str[ETHERADDRL * 3]; |
| |
| if (!parseable) { |
| (void) printf(gettext("key: %d (0x%04x)"), |
| grp->lg_key, grp->lg_key); |
| |
| (void) printf(gettext("\tpolicy: %s"), |
| laadm_policy_to_str(grp->lg_policy, policy_str)); |
| |
| (void) printf(gettext("\taddress: %s (%s)\n"), |
| laadm_mac_addr_to_str(grp->lg_mac, addr_str), |
| (grp->lg_mac_fixed) ? gettext("fixed") : gettext("auto")); |
| } else { |
| (void) printf("aggr key=%d", grp->lg_key); |
| |
| (void) printf(" policy=%s", |
| laadm_policy_to_str(grp->lg_policy, policy_str)); |
| |
| (void) printf(" address=%s", |
| laadm_mac_addr_to_str(grp->lg_mac, addr_str)); |
| |
| (void) printf(" address-type=%s\n", |
| (grp->lg_mac_fixed) ? "fixed" : "auto"); |
| } |
| } |
| |
| static void |
| dump_grp_lacp(laadm_grp_attr_sys_t *grp, boolean_t parseable) |
| { |
| const char *lacp_mode_str = laadm_lacp_mode_to_str(grp->lg_lacp_mode); |
| const char *lacp_timer_str = |
| laadm_lacp_timer_to_str(grp->lg_lacp_timer); |
| |
| if (!parseable) { |
| (void) printf(gettext("\t\tLACP mode: %s"), lacp_mode_str); |
| (void) printf(gettext("\tLACP timer: %s\n"), lacp_timer_str); |
| } else { |
| (void) printf(" lacp-mode=%s", lacp_mode_str); |
| (void) printf(" lacp-timer=%s\n", lacp_timer_str); |
| } |
| } |
| |
| static void |
| dump_grp_stats(laadm_grp_attr_sys_t *grp) |
| { |
| (void) printf("key: %d", grp->lg_key); |
| (void) printf("\tipackets rbytes opackets obytes "); |
| (void) printf("%%ipkts %%opkts\n"); |
| } |
| |
| static void |
| dump_ports_lacp_head(void) |
| { |
| (void) printf(DUMP_LACP_FORMAT, gettext("device"), gettext("activity"), |
| gettext("timeout"), gettext("aggregatable"), gettext("sync"), |
| gettext("coll"), gettext("dist"), gettext("defaulted"), |
| gettext("expired")); |
| } |
| |
| static void |
| dump_ports_head(void) |
| { |
| (void) printf(gettext(" device\taddress\t\t speed\t\tduplex\tlink\t" |
| "state\n")); |
| } |
| |
| static char * |
| port_state_to_str(aggr_port_state_t state_num) |
| { |
| int i; |
| port_state_t *state; |
| |
| for (i = 0; i < NPORTSTATES; i++) { |
| state = &port_states[i]; |
| if (state->state_num == state_num) |
| return (state->state_name); |
| } |
| |
| return ("unknown"); |
| } |
| |
| static void |
| dump_port(laadm_port_attr_sys_t *port, boolean_t parseable) |
| { |
| char *dev = port->lp_devname; |
| char buf[ETHERADDRL * 3]; |
| |
| if (!parseable) { |
| (void) printf(" %-9s\t%s", dev, laadm_mac_addr_to_str( |
| port->lp_mac, buf)); |
| (void) printf("\t %5uMb", (int)(mac_ifspeed(dev) / |
| 1000000ull)); |
| (void) printf("\t%s", mac_link_duplex(dev)); |
| (void) printf("\t%s", mac_link_state(dev)); |
| (void) printf("\t%s\n", port_state_to_str(port->lp_state)); |
| |
| } else { |
| (void) printf(" device=%s address=%s", dev, |
| laadm_mac_addr_to_str(port->lp_mac, buf)); |
| (void) printf(" speed=%u", (int)(mac_ifspeed(dev) / |
| 1000000ull)); |
| (void) printf(" duplex=%s", mac_link_duplex(dev)); |
| (void) printf(" link=%s", mac_link_state(dev)); |
| (void) printf(" port=%s", port_state_to_str(port->lp_state)); |
| } |
| } |
| |
| static void |
| dump_port_lacp(laadm_port_attr_sys_t *port) |
| { |
| aggr_lacp_state_t *state = &port->lp_lacp_state; |
| |
| (void) printf(DUMP_LACP_FORMAT, |
| port->lp_devname, state->bit.activity ? "active" : "passive", |
| state->bit.timeout ? "short" : "long", |
| state->bit.aggregation ? "yes" : "no", |
| state->bit.sync ? "yes" : "no", |
| state->bit.collecting ? "yes" : "no", |
| state->bit.distributing ? "yes" : "no", |
| state->bit.defaulted ? "yes" : "no", |
| state->bit.expired ? "yes" : "no"); |
| } |
| |
| static void |
| dump_port_stat(int index, show_grp_state_t *state, pktsum_t *port_stats, |
| pktsum_t *tot_stats) |
| { |
| pktsum_t diff_stats; |
| pktsum_t *old_stats = &state->gs_prevstats[index]; |
| |
| stats_diff(&diff_stats, port_stats, old_stats); |
| |
| (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->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 = *port_stats; |
| } |
| |
| static int |
| show_key(void *arg, laadm_grp_attr_sys_t *grp) |
| { |
| show_grp_state_t *state = (show_grp_state_t *)arg; |
| int i; |
| pktsum_t pktsumtot, port_stat; |
| |
| if (state->gs_key != 0 && state->gs_key != grp->lg_key) |
| return (0); |
| if (state->gs_firstonly) { |
| if (state->gs_found) |
| return (0); |
| } else { |
| bzero(&state->gs_prevstats, sizeof (state->gs_prevstats)); |
| } |
| |
| state->gs_found = B_TRUE; |
| |
| if (state->gs_stats) { |
| /* show statistics */ |
| dump_grp_stats(grp); |
| |
| /* sum the ports statistics */ |
| bzero(&pktsumtot, sizeof (pktsumtot)); |
| for (i = 0; i < grp->lg_nports; i++) { |
| get_mac_stats(grp->lg_ports[i].lp_devname, &port_stat); |
| stats_total(&pktsumtot, &port_stat, |
| &state->gs_prevstats[i]); |
| } |
| |
| (void) printf(" Total"); |
| (void) printf("\t%-10llu", pktsumtot.ipackets); |
| (void) printf("%-12llu", pktsumtot.rbytes); |
| (void) printf("%-10llu", pktsumtot.opackets); |
| (void) printf("%-12llu\n", pktsumtot.obytes); |
| |
| for (i = 0; i < grp->lg_nports; i++) { |
| get_mac_stats(grp->lg_ports[i].lp_devname, &port_stat); |
| (void) printf(" %s", grp->lg_ports[i].lp_devname); |
| dump_port_stat(i, state, &port_stat, &pktsumtot); |
| } |
| } else if (state->gs_lacp) { |
| /* show LACP info */ |
| dump_grp(grp, state->gs_parseable); |
| dump_grp_lacp(grp, state->gs_parseable); |
| dump_ports_lacp_head(); |
| for (i = 0; i < grp->lg_nports; i++) |
| dump_port_lacp(&grp->lg_ports[i]); |
| } else { |
| dump_grp(grp, state->gs_parseable); |
| if (!state->gs_parseable) |
| dump_ports_head(); |
| for (i = 0; i < grp->lg_nports; i++) { |
| if (state->gs_parseable) |
| (void) printf("dev key=%d", grp->lg_key); |
| dump_port(&grp->lg_ports[i], state->gs_parseable); |
| if (state->gs_parseable) |
| (void) printf("\n"); |
| } |
| } |
| |
| return (0); |
| } |
| |
| static int |
| kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf) |
| { |
| kstat_named_t *knp; |
| |
| if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL) |
| return (-1); |
| |
| if (knp->data_type != type) |
| return (-1); |
| |
| switch (type) { |
| case KSTAT_DATA_UINT64: |
| *(uint64_t *)buf = knp->value.ui64; |
| break; |
| case KSTAT_DATA_UINT32: |
| *(uint32_t *)buf = knp->value.ui32; |
| break; |
| default: |
| return (-1); |
| } |
| |
| return (0); |
| } |
| |
| static void |
| show_dev(void *arg, const char *dev) |
| { |
| show_mac_state_t *state = (show_mac_state_t *)arg; |
| |
| (void) printf("%s", dev); |
| |
| if (!state->ms_parseable) { |
| (void) printf(gettext("\t\tlink: %s"), |
| mac_link_state(dev)); |
| (void) printf(gettext("\tspeed: %5uMb"), |
| (unsigned int)(mac_ifspeed(dev) / 1000000ull)); |
| (void) printf(gettext("\tduplex: %s\n"), |
| mac_link_duplex(dev)); |
| } else { |
| (void) printf(" link=%s", mac_link_state(dev)); |
| (void) printf(" speed=%u", |
| (unsigned int)(mac_ifspeed(dev) / 1000000ull)); |
| (void) printf(" duplex=%s\n", mac_link_duplex(dev)); |
| } |
| } |
| |
| /*ARGSUSED*/ |
| static void |
| show_dev_stats(void *arg, const char *dev) |
| { |
| show_mac_state_t *state = (show_mac_state_t *)arg; |
| pktsum_t stats, diff_stats; |
| |
| if (state->ms_firstonly) { |
| if (state->ms_donefirst) |
| return; |
| state->ms_donefirst = B_TRUE; |
| } else { |
| bzero(&state->ms_prevstats, sizeof (state->ms_prevstats)); |
| } |
| |
| get_mac_stats(dev, &stats); |
| stats_diff(&diff_stats, &stats, &state->ms_prevstats); |
| |
| (void) printf("%s", dev); |
| (void) printf("\t\t%-10llu", diff_stats.ipackets); |
| (void) printf("%-12llu", diff_stats.rbytes); |
| (void) printf("%-8u", diff_stats.ierrors); |
| (void) printf("%-10llu", diff_stats.opackets); |
| (void) printf("%-12llu", diff_stats.obytes); |
| (void) printf("%-8u\n", diff_stats.oerrors); |
| |
| state->ms_prevstats = stats; |
| } |
| |
| static void |
| do_show_link(int argc, char *argv[]) |
| { |
| char *name = NULL; |
| int option; |
| boolean_t s_arg = B_FALSE; |
| boolean_t i_arg = B_FALSE; |
| int interval = 0; |
| show_link_state_t state; |
| |
| state.ls_stats = B_FALSE; |
| state.ls_parseable = B_FALSE; |
| |
| opterr = 0; |
| while ((option = getopt_long(argc, argv, ":psi:", |
| longopts, NULL)) != -1) { |
| switch (option) { |
| case 'p': |
| state.ls_parseable = B_TRUE; |
| break; |
| case 's': |
| if (s_arg) |
| die_optdup(option); |
| |
| s_arg = B_TRUE; |
| 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); |
| break; |
| } |
| } |
| |
| if (i_arg && !s_arg) |
| die("the option -i can be used only with -s"); |
| |
| /* get link name (optional last argument) */ |
| if (optind == (argc-1)) |
| name = argv[optind]; |
| else if (optind != argc) |
| usage(); |
| |
| if (s_arg) { |
| link_stats(name, interval); |
| return; |
| } |
| |
| if (name == NULL) { |
| (void) dladm_walk(show_link, &state); |
| } else { |
| show_link(&state, name); |
| } |
| } |
| |
| static void |
| do_show_aggr(int argc, char *argv[]) |
| { |
| int option; |
| int key = 0; |
| boolean_t L_arg = B_FALSE; |
| boolean_t s_arg = B_FALSE; |
| boolean_t i_arg = B_FALSE; |
| show_grp_state_t state; |
| int interval = 0; |
| |
| state.gs_stats = B_FALSE; |
| state.gs_lacp = B_FALSE; |
| state.gs_parseable = B_FALSE; |
| |
| opterr = 0; |
| while ((option = getopt_long(argc, argv, ":Lpsi:", |
| longopts, NULL)) != -1) { |
| switch (option) { |
| case 'L': |
| if (L_arg) |
| die_optdup(option); |
| |
| if (s_arg || i_arg) { |
| die("the option -L cannot be used with -i " |
| "or -s"); |
| } |
| |
| L_arg = B_TRUE; |
| state.gs_lacp = B_TRUE; |
| break; |
| case 'p': |
| state.gs_parseable = B_TRUE; |
| break; |
| case 's': |
| if (s_arg) |
| die_optdup(option); |
| |
| if (L_arg) |
| die("the option -s cannot be used with -L"); |
| |
| s_arg = B_TRUE; |
| break; |
| case 'i': |
| if (i_arg) |
| die_optdup(option); |
| |
| if (L_arg) |
| die("the option -i cannot be used with -L"); |
| |
| i_arg = B_TRUE; |
| if (!str2int(optarg, &interval) || interval == 0) |
| die("invalid interval value '%s'", optarg); |
| break; |
| default: |
| die_opterr(optopt, option); |
| break; |
| } |
| } |
| |
| if (i_arg && !s_arg) |
| die("the option -i can be used only with -s"); |
| |
| /* get aggregation key (optional last argument) */ |
| if (optind == (argc-1)) { |
| if (!str2int(argv[optind], &key) || key < 1) |
| die("invalid key value '%s'", argv[optind]); |
| } else if (optind != argc) { |
| usage(); |
| } |
| |
| if (s_arg) { |
| aggr_stats(key, interval); |
| return; |
| } |
| |
| state.gs_key = key; |
| state.gs_found = B_FALSE; |
| |
| (void) laadm_walk_sys(show_key, &state); |
| |
| if (key != 0 && !state.gs_found) |
| die("non-existent aggregation key '%u'", key); |
| } |
| |
| static void |
| do_show_dev(int argc, char *argv[]) |
| { |
| int option; |
| char *dev = NULL; |
| boolean_t s_arg = B_FALSE; |
| boolean_t i_arg = B_FALSE; |
| int interval = 0; |
| show_mac_state_t state; |
| |
| state.ms_parseable = B_FALSE; |
| |
| opterr = 0; |
| while ((option = getopt_long(argc, argv, ":psi:", |
| longopts, NULL)) != -1) { |
| switch (option) { |
| case 'p': |
| state.ms_parseable = B_TRUE; |
| break; |
| case 's': |
| if (s_arg) |
| die_optdup(option); |
| |
| s_arg = B_TRUE; |
| 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); |
| break; |
| } |
| } |
| |
| if (i_arg && !s_arg) |
| die("the option -i can be used only with -s"); |
| |
| /* get dev name (optional last argument) */ |
| if (optind == (argc-1)) |
| dev = argv[optind]; |
| else if (optind != argc) |
| usage(); |
| |
| if (dev != NULL) { |
| int index; |
| char drv[LIFNAMSIZ]; |
| dladm_attr_t dlattr; |
| boolean_t legacy; |
| |
| /* |
| * Check for invalid devices. |
| * aggregations and vlans are not considered devices. |
| */ |
| if (strncmp(dev, "aggr", 4) == 0 || |
| dlpi_if_parse(dev, drv, &index) < 0 || |
| index >= 1000 || get_if_info(dev, &dlattr, &legacy) < 0) |
| die("invalid device '%s'", dev); |
| } |
| |
| if (s_arg) { |
| dev_stats(dev, interval); |
| return; |
| } |
| |
| if (dev == NULL) |
| (void) macadm_walk(show_dev, &state, B_TRUE); |
| else |
| show_dev(&state, dev); |
| } |
| |
| /* ARGSUSED */ |
| static void |
| link_stats(const char *link, uint_t interval) |
| { |
| dladm_attr_t dlattr; |
| boolean_t legacy; |
| show_link_state_t state; |
| |
| if (link != NULL && get_if_info(link, &dlattr, &legacy) < 0) |
| die("invalid link '%s'", link); |
| |
| bzero(&state, sizeof (state)); |
| |
| /* |
| * If an interval is specified, continuously show the stats |
| * only for the first MAC port. |
| */ |
| state.ls_firstonly = (interval != 0); |
| |
| for (;;) { |
| (void) printf("\t\tipackets rbytes ierrors "); |
| (void) printf("opackets obytes oerrors\n"); |
| |
| state.ls_donefirst = B_FALSE; |
| if (link == NULL) |
| (void) dladm_walk(show_link_stats, &state); |
| else |
| show_link_stats(&state, link); |
| |
| if (interval == 0) |
| break; |
| |
| (void) sleep(interval); |
| } |
| } |
| |
| /* ARGSUSED */ |
| static void |
| aggr_stats(uint32_t key, uint_t interval) |
| { |
| show_grp_state_t state; |
| |
| bzero(&state, sizeof (state)); |
| state.gs_stats = B_TRUE; |
| state.gs_key = key; |
| |
| /* |
| * If an interval is specified, continuously show the stats |
| * only for the first group. |
| */ |
| state.gs_firstonly = (interval != 0); |
| |
| for (;;) { |
| state.gs_found = B_FALSE; |
| (void) laadm_walk_sys(show_key, &state); |
| if (state.gs_key != 0 && !state.gs_found) |
| die("non-existent aggregation key '%u'", key); |
| |
| if (interval == 0) |
| break; |
| |
| (void) sleep(interval); |
| } |
| } |
| |
| /* ARGSUSED */ |
| static void |
| dev_stats(const char *dev, uint32_t interval) |
| { |
| show_mac_state_t state; |
| |
| bzero(&state, sizeof (state)); |
| |
| /* |
| * If an interval is specified, continuously show the stats |
| * only for the first MAC port. |
| */ |
| state.ms_firstonly = (interval != 0); |
| |
| for (;;) { |
| |
| (void) printf("\t\tipackets rbytes ierrors "); |
| (void) printf("opackets obytes oerrors\n"); |
| |
| state.ms_donefirst = B_FALSE; |
| if (dev == NULL) |
| (void) macadm_walk(show_dev_stats, &state, B_TRUE); |
| else |
| show_dev_stats(&state, dev); |
| |
| if (interval == 0) |
| break; |
| |
| (void) sleep(interval); |
| } |
| } |
| |
| /* 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; |
| } |
| |
| /* |
| * In the following routines, we do the first kstat_lookup() assuming that |
| * the device is gldv3-based and that the kstat name is the one passed in |
| * as the "name" argument. If the lookup fails, we redo the kstat_lookup() |
| * omitting the kstat name. This second lookup is needed for getting kstats |
| * from legacy devices. This can fail too if the device is not attached or |
| * the device is legacy and doesn't export the kstats we need. |
| */ |
| static void |
| get_stats(char *module, int instance, 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, name)) == NULL && |
| (ksp = kstat_lookup(kcp, module, instance, NULL)) == 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 (kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64, |
| &stats->ipackets) < 0) |
| goto bail; |
| |
| if (kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64, |
| &stats->opackets) < 0) |
| goto bail; |
| |
| if (kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64, |
| &stats->rbytes) < 0) |
| goto bail; |
| |
| if (kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64, |
| &stats->obytes) < 0) |
| goto bail; |
| |
| if (kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32, |
| &stats->ierrors) < 0) |
| goto bail; |
| |
| if (kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32, |
| &stats->oerrors) < 0) |
| goto bail; |
| |
| (void) kstat_close(kcp); |
| return; |
| |
| bail: |
| (void) kstat_close(kcp); |
| } |
| |
| static void |
| get_mac_stats(const char *dev, pktsum_t *stats) |
| { |
| char module[LIFNAMSIZ]; |
| int instance; |
| |
| if (dlpi_if_parse(dev, module, &instance) != 0) |
| return; |
| bzero(stats, sizeof (*stats)); |
| get_stats(module, instance, "mac", stats); |
| } |
| |
| static void |
| get_link_stats(const char *link, pktsum_t *stats) |
| { |
| char module[LIFNAMSIZ]; |
| int instance; |
| |
| if (dlpi_if_parse(link, module, &instance) != 0) |
| return; |
| bzero(stats, sizeof (*stats)); |
| get_stats(module, instance, (char *)link, stats); |
| } |
| |
| static int |
| get_single_mac_stat(const char *dev, const char *name, uint8_t type, |
| void *val) |
| { |
| char module[LIFNAMSIZ]; |
| int instance; |
| kstat_ctl_t *kcp; |
| kstat_t *ksp; |
| |
| if ((kcp = kstat_open()) == NULL) { |
| warn("kstat open operation failed"); |
| return (-1); |
| } |
| |
| if (dlpi_if_parse(dev, module, &instance) != 0) |
| return (-1); |
| if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL && |
| (ksp = kstat_lookup(kcp, module, instance, NULL)) == 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 (kstat_value(ksp, name, type, val) < 0) |
| goto bail; |
| |
| (void) kstat_close(kcp); |
| return (0); |
| |
| bail: |
| (void) kstat_close(kcp); |
| return (-1); |
| } |
| |
| static uint64_t |
| mac_ifspeed(const char *dev) |
| { |
| uint64_t ifspeed = 0; |
| |
| (void) get_single_mac_stat(dev, "ifspeed", KSTAT_DATA_UINT64, &ifspeed); |
| return (ifspeed); |
| } |
| |
| static char * |
| mac_link_state(const char *dev) |
| { |
| link_state_t link_state; |
| char *state_str = "unknown"; |
| |
| if (get_single_mac_stat(dev, "link_state", KSTAT_DATA_UINT32, |
| &link_state) != 0) { |
| return (state_str); |
| } |
| |
| switch (link_state) { |
| case LINK_STATE_UP: |
| state_str = "up"; |
| break; |
| case LINK_STATE_DOWN: |
| state_str = "down"; |
| break; |
| default: |
| break; |
| } |
| |
| return (state_str); |
| } |
| |
| |
| static char * |
| mac_link_duplex(const char *dev) |
| { |
| link_duplex_t link_duplex; |
| char *duplex_str = "unknown"; |
| |
| if (get_single_mac_stat(dev, "link_duplex", KSTAT_DATA_UINT32, |
| &link_duplex) != 0) { |
| return (duplex_str); |
| } |
| |
| switch (link_duplex) { |
| case LINK_DUPLEX_FULL: |
| duplex_str = "full"; |
| break; |
| case LINK_DUPLEX_HALF: |
| duplex_str = "half"; |
| break; |
| default: |
| break; |
| } |
| |
| return (duplex_str); |
| } |
| |
| #define WIFI_CMD_SCAN 0x00000001 |
| #define WIFI_CMD_SHOW 0x00000002 |
| #define WIFI_CMD_ALL (WIFI_CMD_SCAN | WIFI_CMD_SHOW) |
| typedef struct wifi_field { |
| const char *wf_name; |
| const char *wf_header; |
| uint_t wf_width; |
| uint_t wf_mask; |
| uint_t wf_cmdtype; |
| } wifi_field_t; |
| |
| static wifi_field_t wifi_fields[] = { |
| { "link", "LINK", 10, 0, WIFI_CMD_ALL}, |
| { "essid", "ESSID", 19, WLADM_WLAN_ATTR_ESSID, WIFI_CMD_ALL}, |
| { "bssid", "BSSID/IBSSID", 17, WLADM_WLAN_ATTR_BSSID, WIFI_CMD_ALL}, |
| { "ibssid", "BSSID/IBSSID", 17, WLADM_WLAN_ATTR_BSSID, WIFI_CMD_ALL}, |
| { "mode", "MODE", 6, WLADM_WLAN_ATTR_MODE, WIFI_CMD_ALL}, |
| { "speed", "SPEED", 6, WLADM_WLAN_ATTR_SPEED, WIFI_CMD_ALL}, |
| { "auth", "AUTH", 8, WLADM_WLAN_ATTR_AUTH, WIFI_CMD_SHOW}, |
| { "bsstype", "BSSTYPE", 8, WLADM_WLAN_ATTR_BSSTYPE, WIFI_CMD_ALL}, |
| { "sec", "SEC", 6, WLADM_WLAN_ATTR_SECMODE, WIFI_CMD_ALL}, |
| { "status", "STATUS", 17, WLADM_LINK_ATTR_STATUS, WIFI_CMD_SHOW}, |
| { "strength", "STRENGTH", 10, WLADM_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 (wifi_field_t)) |
| #define WIFI_MAX_FIELD_LEN 32 |
| |
| 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, wifi_field_t ***fields, uint_t *countp, |
| uint_t cmdtype) |
| { |
| uint_t i, j; |
| wifi_field_t **wf = NULL; |
| split_t *sp; |
| boolean_t good_match = B_FALSE; |
| |
| 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); |
| } |
| |
| sp = split(str, WIFI_MAX_FIELDS, WIFI_MAX_FIELD_LEN); |
| if (sp == NULL) |
| return (-1); |
| |
| wf = malloc(sp->s_nfields * sizeof (wifi_field_t *)); |
| if (wf == NULL) |
| goto fail; |
| |
| for (i = 0; i < sp->s_nfields; i++) { |
| for (j = 0; j < WIFI_MAX_FIELDS; j++) { |
| if (strcasecmp(sp->s_fields[i], |
| wifi_fields[j].wf_name) == 0) { |
| good_match = wifi_fields[j]. |
| wf_cmdtype & cmdtype; |
| break; |
| } |
| } |
| if (!good_match) |
| goto fail; |
| |
| good_match = B_FALSE; |
| wf[i] = &wifi_fields[j]; |
| } |
| *countp = i; |
| *fields = wf; |
| splitfree(sp); |
| return (0); |
| fail: |
| free(wf); |
| splitfree(sp); |
| return (-1); |
| } |
| |
| typedef struct print_wifi_state { |
| const char *ws_link; |
| boolean_t ws_parseable; |
| boolean_t ws_header; |
| wifi_field_t **ws_fields; |
| uint_t ws_nfields; |
| boolean_t ws_lastfield; |
| uint_t ws_overflow; |
| } print_wifi_state_t; |
| |
| static void |
| print_wifi_head(print_wifi_state_t *statep) |
| { |
| int i; |
| wifi_field_t *wfp; |
| |
| for (i = 0; i < statep->ws_nfields; i++) { |
| wfp = statep->ws_fields[i]; |
| if (i + 1 < statep->ws_nfields) |
| (void) printf("%-*s ", wfp->wf_width, wfp->wf_header); |
| else |
| (void) printf("%s", wfp->wf_header); |
| } |
| (void) printf("\n"); |
| } |
| |
| static void |
| print_wifi_field(print_wifi_state_t *statep, wifi_field_t *wfp, |
| const char *value) |
| { |
| uint_t width = wfp->wf_width; |
| uint_t valwidth = strlen(value); |
| uint_t compress; |
| |
| if (statep->ws_parseable) { |
| (void) printf("%s=\"%s\"", wfp->wf_header, value); |
| } else { |
| if (value[0] == '\0') |
| value = "--"; |
| if (statep->ws_lastfield) { |
| (void) printf("%s", value); |
| return; |
| } |
| |
| if (valwidth > width) { |
| statep->ws_overflow += valwidth - width; |
| } else if (valwidth < width && statep->ws_overflow > 0) { |
| compress = min(statep->ws_overflow, width - valwidth); |
| statep->ws_overflow -= compress; |
| width -= compress; |
| } |
| (void) printf("%-*s", width, value); |
| } |
| |
| if (!statep->ws_lastfield) |
| (void) putchar(' '); |
| } |
| |
| static void |
| print_wlan_attr(print_wifi_state_t *statep, wifi_field_t *wfp, |
| wladm_wlan_attr_t *attrp) |
| { |
| char buf[WLADM_STRSIZE]; |
| const char *str = ""; |
| |
| if (wfp->wf_mask == 0) { |
| print_wifi_field(statep, wfp, statep->ws_link); |
| return; |
| } |
| |
| if ((wfp->wf_mask & attrp->wa_valid) == 0) { |
| print_wifi_field(statep, wfp, ""); |
| return; |
| } |
| |
| switch (wfp->wf_mask) { |
| case WLADM_WLAN_ATTR_ESSID: |
| str = wladm_essid2str(&attrp->wa_essid, buf); |
| break; |
| case WLADM_WLAN_ATTR_BSSID: |
| str = wladm_bssid2str(&attrp->wa_bssid, buf); |
| break; |
| case WLADM_WLAN_ATTR_SECMODE: |
| str = wladm_secmode2str(&attrp->wa_secmode, buf); |
| break; |
| case WLADM_WLAN_ATTR_STRENGTH: |
| str = wladm_strength2str(&attrp->wa_strength, buf); |
| break; |
| case WLADM_WLAN_ATTR_MODE: |
| str = wladm_mode2str(&attrp->wa_mode, buf); |
| break; |
| case WLADM_WLAN_ATTR_SPEED: |
| str = wladm_speed2str(&attrp->wa_speed, buf); |
| (void) strlcat(buf, "Mb", sizeof (buf)); |
| break; |
| case WLADM_WLAN_ATTR_AUTH: |
| str = wladm_auth2str(&attrp->wa_auth, buf); |
| break; |
| case WLADM_WLAN_ATTR_BSSTYPE: |
| str = wladm_bsstype2str(&attrp->wa_bsstype, buf); |
| break; |
| } |
| |
| print_wifi_field(statep, wfp, str); |
| } |
| |
| static boolean_t |
| print_scan_results(void *arg, wladm_wlan_attr_t *attrp) |
| { |
| print_wifi_state_t *statep = arg; |
| int i; |
| |
| if (statep->ws_header) { |
| statep->ws_header = B_FALSE; |
| if (!statep->ws_parseable) |
| print_wifi_head(statep); |
| } |
| |
| statep->ws_overflow = 0; |
| for (i = 0; i < statep->ws_nfields; i++) { |
| statep->ws_lastfield = (i + 1 == statep->ws_nfields); |
| print_wlan_attr(statep, statep->ws_fields[i], attrp); |
| } |
| (void) putchar('\n'); |
| return (B_TRUE); |
| } |
| |
| static boolean_t |
| scan_wifi(void *arg, const char *link) |
| { |
| print_wifi_state_t *statep = arg; |
| wladm_status_t status; |
| |
| statep->ws_link = link; |
| status = wladm_scan(link, statep, print_scan_results); |
| if (status != WLADM_STATUS_OK) |
| die_wlerr(status, "cannot scan link '%s'", link); |
| |
| return (B_TRUE); |
| } |
| |
| static void |
| print_link_attr(print_wifi_state_t *statep, wifi_field_t *wfp, |
| wladm_link_attr_t *attrp) |
| { |
| char buf[WLADM_STRSIZE]; |
| const char *str = ""; |
| |
| if (strcmp(wfp->wf_name, "status") == 0) { |
| if ((wfp->wf_mask & attrp->la_valid) != 0) |
| str = wladm_linkstatus2str(&attrp->la_status, buf); |
| print_wifi_field(statep, wfp, str); |
| return; |
| } |
| print_wlan_attr(statep, wfp, &attrp->la_wlan_attr); |
| } |
| |
| static boolean_t |
| show_wifi(void *arg, const char *link) |
| { |
| int i; |
| print_wifi_state_t *statep = arg; |
| wladm_link_attr_t attr; |
| wladm_status_t status; |
| |
| status = wladm_get_link_attr(link, &attr); |
| if (status != WLADM_STATUS_OK) |
| die_wlerr(status, "cannot get link attributes for '%s'", link); |
| |
| if (statep->ws_header) { |
| statep->ws_header = B_FALSE; |
| if (!statep->ws_parseable) |
| print_wifi_head(statep); |
| } |
| |
| statep->ws_link = link; |
| statep->ws_overflow = 0; |
| for (i = 0; i < statep->ws_nfields; i++) { |
| statep->ws_lastfield = (i + 1 == statep->ws_nfields); |
| print_link_attr(statep, statep->ws_fields[i], &attr); |
| } |
| (void) putchar('\n'); |
| return (B_TRUE); |
| } |
| |
| static void |
| do_display_wifi(int argc, char **argv, int cmd) |
| { |
| int option; |
| char *fields_str = NULL; |
| wifi_field_t **fields; |
| boolean_t (*callback)(void *, const char *); |
| uint_t nfields; |
| print_wifi_state_t state; |
| wladm_status_t status; |
| |
| if (cmd == WIFI_CMD_SCAN) |
| callback = scan_wifi; |
| else if (cmd == WIFI_CMD_SHOW) |
| callback = show_wifi; |
| else |
| return; |
| |
| state.ws_link = NULL; |
| 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; |
| if (fields_str == NULL) |
| fields_str = "all"; |
| break; |
| default: |
| die_opterr(optopt, option); |
| break; |
| } |
| } |
| |
| if (optind == (argc - 1)) |
| state.ws_link = argv[optind]; |
| else if (optind != argc) |
| usage(); |
| |
| if (parse_wifi_fields(fields_str, &fields, &nfields, cmd) < 0) |
| die("invalid field(s) specified"); |
| |
| state.ws_fields = fields; |
| state.ws_nfields = nfields; |
| |
| if (state.ws_link == NULL) { |
| status = wladm_walk(&state, callback); |
| if (status != WLADM_STATUS_OK) |
| die_wlerr(status, "cannot walk wifi links"); |
| } else { |
| (void) (*callback)(&state, state.ws_link); |
| } |
| free(fields); |
| } |
| |
| static void |
| do_scan_wifi(int argc, char **argv) |
| { |
| do_display_wifi(argc, argv, WIFI_CMD_SCAN); |
| } |
| |
| static void |
| do_show_wifi(int argc, char **argv) |
| { |
| do_display_wifi(argc, argv, WIFI_CMD_SHOW); |
| } |
| |
| typedef struct wlan_count_attr { |
| uint_t wc_count; |
| const char *wc_link; |
| } wlan_count_attr_t; |
| |
| static boolean_t |
| do_count_wlan(void *arg, const char *link) |
| { |
| wlan_count_attr_t *cp = arg; |
| |
| if (cp->wc_count == 0) |
| cp->wc_link = strdup(link); |
| cp->wc_count++; |
| return (B_TRUE); |
| } |
| |
| static int |
| parse_wep_keys(char *str, wladm_wep_key_t **keys, uint_t *key_countp) |
| { |
| uint_t i; |
| split_t *sp; |
| wladm_wep_key_t *wk; |
| |
| sp = split(str, WLADM_MAX_WEPKEYS, WLADM_MAX_WEPKEYNAME_LEN); |
| if (sp == NULL) |
| return (-1); |
| |
| wk = malloc(sp->s_nfields * sizeof (wladm_wep_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], |
| WLADM_MAX_WEPKEYNAME_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 = WLADM_MAX_WEPKEY_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; |
| } |
| } |
| *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) |
| { |
| int option; |
| wladm_wlan_attr_t attr, *attrp; |
| wladm_status_t status = WLADM_STATUS_OK; |
| int timeout = WLADM_CONNECT_TIMEOUT_DEFAULT; |
| const char *link = NULL; |
| wladm_wep_key_t *keys = NULL; |
| uint_t key_count = 0; |
| uint_t flags = 0; |
| wladm_secmode_t keysecmode = WLADM_SECMODE_NONE; |
| |
| 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 = wladm_str2essid(optarg, &attr.wa_essid); |
| if (status != WLADM_STATUS_OK) |
| die("invalid ESSID '%s'", optarg); |
| |
| attr.wa_valid |= WLADM_WLAN_ATTR_ESSID; |
| /* |
| * Try to connect without doing a scan. |
| */ |
| flags |= WLADM_OPT_NOSCAN; |
| break; |
| case 'i': |
| status = wladm_str2bssid(optarg, &attr.wa_bssid); |
| if (status != WLADM_STATUS_OK) |
| die("invalid BSSID %s", optarg); |
| |
| attr.wa_valid |= WLADM_WLAN_ATTR_BSSID; |
| break; |
| case 'a': |
| status = wladm_str2auth(optarg, &attr.wa_auth); |
| if (status != WLADM_STATUS_OK) |
| die("invalid authentication mode '%s'", optarg); |
| |
| attr.wa_valid |= WLADM_WLAN_ATTR_AUTH; |
| break; |
| case 'm': |
| status = wladm_str2mode(optarg, &attr.wa_mode); |
| if (status != WLADM_STATUS_OK) |
| die("invalid mode '%s'", optarg); |
| |
| attr.wa_valid |= WLADM_WLAN_ATTR_MODE; |
| break; |
| case 'b': |
| status = wladm_str2bsstype(optarg, &attr.wa_bsstype); |
| if (status != WLADM_STATUS_OK) |
| die("invalid bsstype '%s'", optarg); |
| |
| attr.wa_valid |= WLADM_WLAN_ATTR_BSSTYPE; |
| break; |
| case 's': |
| status = wladm_str2secmode(optarg, &attr.wa_secmode); |
| if (status != WLADM_STATUS_OK) |
| die("invalid security mode '%s'", optarg); |
| |
| attr.wa_valid |= WLADM_WLAN_ATTR_SECMODE; |
| break; |
| case 'k': |
| if (parse_wep_keys(optarg, &keys, &key_count) < 0) |
| die("invalid key(s) '%s'", optarg); |
| |
| keysecmode = WLADM_SECMODE_WEP; |
| 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 |= WLADM_OPT_CREATEIBSS; |
| break; |
| default: |
| die_opterr(optopt, option); |
| break; |
| } |
| } |
| |
| if (keysecmode == WLADM_SECMODE_NONE) { |
| if ((attr.wa_valid & WLADM_WLAN_ATTR_SECMODE) != 0 && |
| attr.wa_secmode == WLADM_SECMODE_WEP) |
| die("key required for security mode 'wep'"); |
| } else { |
| if ((attr.wa_valid & WLADM_WLAN_ATTR_SECMODE) != 0 && |
| attr.wa_secmode != keysecmode) |
| die("incompatible -s and -k options"); |
| } |
| attr.wa_secmode = keysecmode; |
| attr.wa_valid |= WLADM_WLAN_ATTR_SECMODE; |
| |
| if (optind == (argc - 1)) |
| link = argv[optind]; |
| else if (optind != argc) |
| usage(); |
| |
| if (link == NULL) { |
| wlan_count_attr_t wcattr; |
| |
| wcattr.wc_link = NULL; |
| wcattr.wc_count = 0; |
| (void) wladm_walk(&wcattr, do_count_wlan); |
| 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"); |
| } |
| link = wcattr.wc_link; |
| } |
| attrp = (attr.wa_valid == 0) ? NULL : &attr; |
| again: |
| status = wladm_connect(link, attrp, timeout, keys, key_count, flags); |
| if (status != WLADM_STATUS_OK) { |
| if ((flags & WLADM_OPT_NOSCAN) != 0) { |
| /* |
| * Try again with scanning and filtering. |
| */ |
| flags &= ~WLADM_OPT_NOSCAN; |
| goto again; |
| } |
| |
| if (status == WLADM_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_wlerr(status, "cannot connect link '%s'", link); |
| } |
| free(keys); |
| } |
| |
| /* ARGSUSED */ |
| static boolean_t |
| do_all_disconnect_wifi(void *arg, const char *link) |
| { |
| wladm_status_t status; |
| |
| status = wladm_disconnect(link); |
| if (status != WLADM_STATUS_OK) |
| warn_wlerr(status, "cannot disconnect link '%s'", link); |
| |
| return (B_TRUE); |
| } |
| |
| static void |
| do_disconnect_wifi(int argc, char **argv) |
| { |
| int option; |
| const char *link = NULL; |
| boolean_t all_links = B_FALSE; |
| wladm_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); |
| break; |
| } |
| } |
| |
| if (optind == (argc - 1)) |
| link = argv[optind]; |
| else if (optind != argc) |
| usage(); |
| |
| if (link == NULL) { |
| if (!all_links) { |
| wcattr.wc_link = NULL; |
| wcattr.wc_count = 0; |
| (void) wladm_walk(&wcattr, do_count_wlan); |
| 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"); |
| } |
| link = wcattr.wc_link; |
| } else { |
| (void) wladm_walk(&all_links, do_all_disconnect_wifi); |
| return; |
| } |
| } |
| status = wladm_disconnect(link); |
| if (status != WLADM_STATUS_OK) |
| die_wlerr(status, "cannot disconnect link '%s'", link); |
| } |
| |
| #define MAX_PROPS 32 |
| #define MAX_PROP_VALS 32 |
| #define MAX_PROP_LINE 512 |
| |
| typedef struct prop_info { |
| char *pi_name; |
| char *pi_val[MAX_PROP_VALS]; |
| 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 { |
| const char *ls_link; |
| char *ls_line; |
| char **ls_propvals; |
| prop_list_t *ls_proplist; |
| boolean_t ls_parseable; |
| boolean_t ls_persist; |
| boolean_t ls_header; |
| } show_linkprop_state_t; |
| |
| 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; |
| |
| 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 > MAX_PROP_VALS) |
| 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_head(void) |
| { |
| (void) printf("%-12s %-15s %-14s %-14s %-20s \n", |
| "LINK", "PROPERTY", "VALUE", "DEFAULT", "POSSIBLE"); |
| } |
| |
| static void |
| print_linkprop(show_linkprop_state_t *statep, const char *propname, |
| dladm_prop_type_t type, const char *typename, 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 = MAX_PROP_VALS; |
| dladm_status_t status; |
| |
| status = dladm_get_prop(statep->ls_link, type, propname, |
| propvals, &valcnt); |
| if (status != DLADM_STATUS_OK) { |
| if (status == DLADM_STATUS_NOTSUP || statep->ls_persist) { |
| valcnt = 1; |
| if (type == DLADM_PROP_VAL_CURRENT) |
| propvals = &unknown; |
| else |
| propvals = ¬sup; |
| } else { |
| die_dlerr(status, "cannot get link property '%s'", |
| propname); |
| } |
| } |
| |
| 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, "--,"); |
| 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=\"%s\" ", typename, buf); |
| } else { |
| *pptr += snprintf(*pptr, lim - *pptr, format, buf); |
| } |
| } |
| |
| static boolean_t |
| show_linkprop(void *arg, const char *propname) |
| { |
| show_linkprop_state_t *statep = arg; |
| char *ptr = statep->ls_line; |
| char *lim = ptr + MAX_PROP_LINE; |
| |
| if (statep->ls_persist && dladm_is_prop_temponly(propname, NULL)) |
| return (B_TRUE); |
| |
| if (statep->ls_parseable) |
| ptr += snprintf(ptr, lim - ptr, "LINK=\"%s\" ", |
| statep->ls_link); |
| else |
| ptr += snprintf(ptr, lim - ptr, "%-12s ", statep->ls_link); |
| |
| if (statep->ls_parseable) |
| ptr += snprintf(ptr, lim - ptr, "PROPERTY=\"%s\" ", propname); |
| else |
| ptr += snprintf(ptr, lim - ptr, "%-15s ", propname); |
| |
| print_linkprop(statep, propname, |
| statep->ls_persist ? DLADM_PROP_VAL_PERSISTENT : |
| DLADM_PROP_VAL_CURRENT, "VALUE", "%-14s ", &ptr); |
| print_linkprop(statep, propname, DLADM_PROP_VAL_DEFAULT, |
| "DEFAULT", "%-14s ", &ptr); |
| print_linkprop(statep, propname, DLADM_PROP_VAL_MODIFIABLE, |
| "POSSIBLE", "%-20s ", &ptr); |
| |
| if (statep->ls_header) { |
| statep->ls_header = B_FALSE; |
| if (!statep->ls_parseable) |
| print_linkprop_head(); |
| } |
| (void) printf("%s\n", statep->ls_line); |
| return (B_TRUE); |
| } |
| |
| static void |
| do_show_linkprop(int argc, char **argv) |
| { |
| int option; |
| prop_list_t *proplist = NULL; |
| show_linkprop_state_t state; |
| |
| opterr = 0; |
| state.ls_link = NULL; |
| state.ls_propvals = NULL; |
| state.ls_line = NULL; |
| state.ls_parseable = B_FALSE; |
| state.ls_persist = B_FALSE; |
| state.ls_header = B_TRUE; |
| while ((option = getopt_long(argc, argv, ":p:cP", |
| 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; |
| break; |
| default: |
| die_opterr(optopt, option); |
| break; |
| } |
| } |
| |
| if (optind == (argc - 1)) |
| state.ls_link = argv[optind]; |
| else if (optind != argc) |
| usage(); |
| |
| state.ls_proplist = proplist; |
| |
| if (state.ls_link == NULL) { |
| (void) dladm_walk(show_linkprop_onelink, &state); |
| } else { |
| show_linkprop_onelink(&state, state.ls_link); |
| } |
| free_props(proplist); |
| } |
| |
| static void |
| show_linkprop_onelink(void *arg, const char *link) |
| { |
| int i, fd; |
| char linkname[MAXPATHLEN]; |
| char *buf; |
| dladm_status_t status; |
| prop_list_t *proplist = NULL; |
| show_linkprop_state_t *statep; |
| const char *savep; |
| |
| statep = (show_linkprop_state_t *)arg; |
| savep = statep->ls_link; |
| statep->ls_link = link; |
| 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. |
| */ |
| (void) snprintf(linkname, MAXPATHLEN, "/dev/%s", link); |
| if ((fd = open(linkname, O_RDWR)) < 0) |
| die("cannot open %s: %s", link, strerror(errno)); |
| |
| buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * MAX_PROP_VALS + |
| MAX_PROP_LINE); |
| if (buf == NULL) |
| die("insufficient memory"); |
| |
| statep->ls_propvals = (char **)(void *)buf; |
| for (i = 0; i < MAX_PROP_VALS; i++) { |
| statep->ls_propvals[i] = buf + sizeof (char *) * MAX_PROP_VALS + |
| i * DLADM_PROP_VAL_MAX; |
| } |
| statep->ls_line = buf + |
| (sizeof (char *) + DLADM_PROP_VAL_MAX) * MAX_PROP_VALS; |
| |
| if (proplist != NULL) { |
| for (i = 0; i < proplist->pl_count; i++) { |
| if (!show_linkprop(statep, |
| proplist->pl_info[i].pi_name)) |
| break; |
| } |
| } else { |
| status = dladm_walk_prop(link, statep, show_linkprop); |
| if (status != DLADM_STATUS_OK) |
| die_dlerr(status, "show-linkprop"); |
| } |
| (void) close(fd); |
| free(buf); |
| statep->ls_link = savep; |
| } |
| |
| static dladm_status_t |
| set_linkprop_persist(const char *link, const char *prop_name, char **prop_val, |
| uint_t val_cnt, boolean_t reset) |
| { |
| dladm_status_t status; |
| char *errprop; |
| |
| status = dladm_set_prop(link, prop_name, prop_val, val_cnt, |
| DLADM_OPT_PERSIST, &errprop); |
| |
|
|