diff options
Diffstat (limited to 'usr/src/cmd/dladm/dladm.c')
-rw-r--r-- | usr/src/cmd/dladm/dladm.c | 2090 |
1 files changed, 2043 insertions, 47 deletions
diff --git a/usr/src/cmd/dladm/dladm.c b/usr/src/cmd/dladm/dladm.c index 1167cb1603..d344b3e59b 100644 --- a/usr/src/cmd/dladm/dladm.c +++ b/usr/src/cmd/dladm/dladm.c @@ -26,7 +26,9 @@ #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> @@ -38,11 +40,19 @@ #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 @@ -95,16 +105,15 @@ static port_state_t port_states[] = { #define NPORTSTATES (sizeof (port_states) / sizeof (port_state_t)) -static void do_show_link(int, char **); -static void do_create_aggr(int, char **); -static void do_delete_aggr(int, char **); -static void do_add_aggr(int, char **); -static void do_remove_aggr(int, char **); -static void do_modify_aggr(int, char **); -static void do_show_aggr(int, char **); -static void do_up_aggr(int, char **); -static void do_down_aggr(int, char **); -static void do_show_dev(int, char **); +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 link_stats(const char *, uint32_t); static void aggr_stats(uint16_t, uint32_t); @@ -119,41 +128,81 @@ static void stats_total(pktsum_t *, pktsum_t *, pktsum_t *); static void stats_diff(pktsum_t *, pktsum_t *, pktsum_t *); typedef struct cmd { - char *c_name; - void (*c_fn)(int, char **); + 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 } + { "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'}, + {"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; #define PRINT_ERR_DIAG(s, diag, func) { \ (void) fprintf(stderr, gettext(s), progname, strerror(errno)); \ @@ -165,18 +214,36 @@ static char *progname; static void usage(void) { - (void) fprintf(stderr, gettext( - "usage: dladm create-aggr [-t] [-R <root-dir>] [-P <policy>]\n" - " [-l <mode>] [-T <time>]\n" - " [-u <address>] -d <dev> ... <key>\n" - " delete-aggr [-t] [-R <root-dir>] <key>\n" - " add-aggr [-t] [-R <root-dir>] -d <dev> ... <key>\n" - " remove-aggr [-t] [-R <root-dir>] -d <dev> ... <key>\n" - " modify-aggr [-t] [-R <root-dir>] [-P <policy>]\n" - " [-l <mode>] [-T <time>] [-u <address>] <key>\n" - " show-aggr [-L] [-s] [-i <interval>] [-p] [<key>]\n" - " show-dev [-s] [-i <interval>] [-p] [<dev>]\n" - " show-link [-s] [-i <interval>] [-p] [<name>]\n")); + (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); } @@ -1930,3 +1997,1932 @@ mac_link_duplex(const char *dev) 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_ALL}, +{ "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,auth,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[i]. + 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) +{ + char errmsg[WLADM_STRSIZE]; + 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) { + (void) fprintf(stderr, gettext( + "%s: cannot scan link '%s': %s\n"), + progname, link, wladm_status2str(status, errmsg)); + exit(1); + } + 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; + char buf[WLADM_STRSIZE]; + 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) { + (void) fprintf(stderr, gettext("%s: cannot get link " + "attributes for '%s': %s\n"), progname, link, + wladm_status2str(status, buf)); + exit(1); + } + + 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 errmsg[WLADM_STRSIZE]; + 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; + case ':': + (void) fprintf(stderr, + gettext("%s: option requires a value '-%c'\n"), + progname, optopt); + exit(1); + break; + case '?': + default: + (void) fprintf(stderr, + gettext("%s: unrecognized option '-%c'\n"), + progname, optopt); + exit(1); + 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) { + (void) fprintf(stderr, + gettext("%s: invalid field(s) specified\n"), + progname); + exit(1); + } + state.ws_fields = fields; + state.ws_nfields = nfields; + + if (state.ws_link == NULL) { + status = wladm_walk(&state, callback); + if (status != WLADM_STATUS_OK) { + (void) fprintf(stderr, gettext("%s: %s\n"), + progname, wladm_status2str(status, errmsg)); + exit(1); + } + } 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 = (wlan_count_attr_t *)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; + char errmsg[WLADM_STRSIZE]; + const char *link = NULL; + char *endp = 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) { + (void) fprintf(stderr, + gettext("%s: invalid ESSID '%s'\n"), + progname, optarg); + exit(1); + } + 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) { + (void) fprintf(stderr, + gettext("%s: invalid BSSID %s\n"), + progname, optarg); + exit(1); + } + attr.wa_valid |= WLADM_WLAN_ATTR_BSSID; + break; + case 'a': + status = wladm_str2auth(optarg, &attr.wa_auth); + if (status != WLADM_STATUS_OK) { + (void) fprintf(stderr, + gettext("%s: invalid authentication " + "mode '%s'\n"), progname, optarg); + exit(1); + } + attr.wa_valid |= WLADM_WLAN_ATTR_AUTH; + break; + case 'm': + status = wladm_str2mode(optarg, &attr.wa_mode); + if (status != WLADM_STATUS_OK) { + (void) fprintf(stderr, + gettext("%s: invalid mode '%s'\n"), + progname, optarg); + exit(1); + } + attr.wa_valid |= WLADM_WLAN_ATTR_MODE; + break; + case 'b': + status = wladm_str2bsstype(optarg, &attr.wa_bsstype); + if (status != WLADM_STATUS_OK) { + (void) fprintf(stderr, + gettext("%s: invalid bsstype '%s'\n"), + progname, optarg); + exit(1); + } + attr.wa_valid |= WLADM_WLAN_ATTR_BSSTYPE; + break; + case 's': + status = wladm_str2secmode(optarg, &attr.wa_secmode); + if (status != WLADM_STATUS_OK) { + (void) fprintf(stderr, + gettext("%s: invalid security mode '%s'\n"), + progname, optarg); + exit(1); + } + attr.wa_valid |= WLADM_WLAN_ATTR_SECMODE; + break; + case 'k': + if (parse_wep_keys(optarg, &keys, &key_count) < 0) { + (void) fprintf(stderr, + gettext("%s: invalid key(s) '%s'\n"), + progname, optarg); + exit(1); + } + keysecmode = WLADM_SECMODE_WEP; + break; + case 'T': + if (strcasecmp(optarg, "forever") == 0) { + timeout = -1; + break; + } + errno = 0; + timeout = (int)strtol(optarg, &endp, 10); + if (timeout < 0 || errno != 0 || *endp != '\0') { + (void) fprintf(stderr, + gettext("%s: invalid timeout value '%s'\n"), + progname, optarg); + exit(1); + } + break; + case 'c': + flags |= WLADM_OPT_CREATEIBSS; + break; + case ':': + (void) fprintf(stderr, + gettext("%s: option requires a value '-%c'\n"), + progname, optopt); + exit(1); + break; + case '?': + default: + (void) fprintf(stderr, + gettext("%s: unrecognized option '-%c'\n"), + progname, optopt); + exit(1); + break; + } + } + + if (keysecmode == WLADM_SECMODE_NONE) { + if ((attr.wa_valid & WLADM_WLAN_ATTR_SECMODE) != 0 && + attr.wa_secmode == WLADM_SECMODE_WEP) { + (void) fprintf(stderr, + gettext("%s: key required for security mode " + "'wep'\n"), progname); + exit(1); + } + } else { + if ((attr.wa_valid & WLADM_WLAN_ATTR_SECMODE) != 0 && + attr.wa_secmode != keysecmode) { + (void) fprintf(stderr, + gettext("%s: incompatible -s and -k options\n"), + progname); + exit(1); + } + } + 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) { + (void) fprintf(stderr, gettext( + "%s: no wifi links are available\n"), progname); + exit(1); + } else if (wcattr.wc_count > 1) { + (void) fprintf(stderr, gettext( + "%s: link name is required when more than " + "one link is available\n"), progname); + exit(1); + } + link = wcattr.wc_link; + } + attrp = (attr.wa_valid == 0) ? NULL : &attr; + + status = wladm_connect(link, attrp, timeout, keys, key_count, flags); + if (status != WLADM_STATUS_OK) { + if ((flags & WLADM_OPT_NOSCAN) != 0) { + /* + * Redo the connect. This time with scanning + * and filtering. + */ + flags &= ~WLADM_OPT_NOSCAN; + status = wladm_connect(link, attrp, timeout, keys, + key_count, flags); + if (status == WLADM_STATUS_OK) { + free(keys); + return; + } + } + if (status == WLADM_STATUS_NOTFOUND) { + if (attr.wa_valid == 0) { + (void) fprintf(stderr, gettext( + "%s: no wifi networks are available\n"), + progname); + } else { + (void) fprintf(stderr, gettext("%s: no wifi " + "networks with the specified criteria " + "are available\n"), progname); + } + } else { + (void) fprintf(stderr, gettext("%s: cannot connect: %s" + "\n"), progname, wladm_status2str(status, errmsg)); + } + exit(1); + } + free(keys); +} + +/* ARGSUSED */ +static boolean_t +do_all_disconnect_wifi(void *arg, const char *link) +{ + wladm_status_t status; + char errmsg[WLADM_STRSIZE]; + + status = wladm_disconnect(link); + if (status != WLADM_STATUS_OK) { + (void) fprintf(stderr, + gettext("%s: cannot disconnect link '%s': %s\n"), + progname, link, wladm_status2str(status, errmsg)); + } + return (B_TRUE); +} + +static void +do_disconnect_wifi(int argc, char **argv) +{ + int option; + const char *link = NULL; + char errmsg[WLADM_STRSIZE]; + 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; + case ':': + (void) fprintf(stderr, + gettext("%s: option requires a value '-%c'\n"), + progname, optopt); + exit(1); + break; + case '?': + default: + (void) fprintf(stderr, + gettext("%s: unrecognized option '-%c'\n"), + progname, optopt); + exit(1); + 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) { + (void) fprintf(stderr, gettext( + "%s: no wifi links are available\n"), + progname); + exit(1); + } else if (wcattr.wc_count > 1) { + (void) fprintf(stderr, gettext( + "%s: link name is required when more than " + "one link is available\n"), progname); + exit(1); + } + link = wcattr.wc_link; + } else { + (void) wladm_walk(&all_links, do_all_disconnect_wifi); + return; + } + } + status = wladm_disconnect(link); + if (status != WLADM_STATUS_OK) { + (void) fprintf(stderr, gettext("%s: cannot disconnect: %s\n"), + progname, wladm_status2str(status, errmsg)); + exit(1); + } +} + +#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; + 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("%-15s %-14s %-14s %-30s \n", + "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 { + (void) fprintf(stderr, gettext( + "%s: cannot get link property '%s': %s\n"), + progname, propname, dladm_status2str(status, buf)); + exit(1); + } + } + + 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_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", "%-30s ", &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 i, option, fd; + char errmsg[DLADM_STRSIZE]; + char linkname[MAXPATHLEN]; + prop_list_t *proplist = NULL; + char *buf; + dladm_status_t status; + 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) { + (void) fprintf(stderr, + gettext("%s: invalid field(s) specified\n"), + progname); + exit(1); + } + break; + case 'c': + state.ls_parseable = B_TRUE; + break; + case 'P': + state.ls_persist = B_TRUE; + break; + case ':': + (void) fprintf(stderr, + gettext("%s: option requires a value '-%c'\n"), + progname, optopt); + exit(1); + break; + case '?': + default: + (void) fprintf(stderr, + gettext("%s: unrecognized option '-%c'\n"), + progname, optopt); + exit(1); + break; + } + } + + if (optind == (argc - 1)) + state.ls_link = argv[optind]; + else if (optind != argc) + usage(); + + if (state.ls_link == NULL) { + (void) fprintf(stderr, + gettext("%s: link name must be specified\n"), progname); + exit(1); + } + + /* + * 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", state.ls_link); + if ((fd = open(linkname, O_RDWR)) < 0) { + (void) fprintf(stderr, + gettext("%s: cannot open %s\n"), progname, state.ls_link); + exit(1); + } + + buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * MAX_PROP_VALS + + MAX_PROP_LINE); + if (buf == NULL) { + (void) fprintf(stderr, + gettext("%s: insufficient memory\n"), progname); + exit(1); + } + state.ls_propvals = (char **)(void *)buf; + for (i = 0; i < MAX_PROP_VALS; i++) { + state.ls_propvals[i] = buf + sizeof (char *) * MAX_PROP_VALS + + i * DLADM_PROP_VAL_MAX; + } + state.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(&state, + proplist->pl_info[i].pi_name)) + break; + } + } else { + status = dladm_walk_prop(state.ls_link, &state, show_linkprop); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, + gettext("%s: show-linkprop: %s\n"), progname, + dladm_status2str(status, errmsg)); + exit(1); + } + } + (void) close(fd); + free(buf); + free_props(proplist); +} + +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 errmsg[DLADM_STRSIZE]; + + status = dladm_set_prop(link, prop_name, prop_val, val_cnt, + DLADM_OPT_PERSIST); + + if (status != DLADM_STATUS_OK) { + if (reset) { + (void) fprintf(stderr, gettext("%s: warning: cannot " + "persistently reset link property '%s' on '%s': " + "%s\n"), progname, prop_name, link, + dladm_status2str(status, errmsg)); + } else { + (void) fprintf(stderr, gettext("%s: warning: cannot " + "persistently set link property '%s' on '%s': " + "%s\n"), progname, prop_name, link, + dladm_status2str(status, errmsg)); + } + } + return (status); +} + +static void +set_linkprop(int argc, char **argv, boolean_t reset) +{ + int i, option; + char errmsg[DLADM_STRSIZE]; + const char *link = NULL; + prop_list_t *proplist = NULL; + boolean_t temp = B_FALSE; + dladm_status_t status = DLADM_STATUS_OK; + + opterr = 0; + while ((option = getopt_long(argc, argv, ":p:R:t", + prop_longopts, NULL)) != -1) { + switch (option) { + case 'p': + if (parse_props(optarg, &proplist, reset) < 0) { + (void) fprintf(stderr, gettext( + "%s: invalid link properties specified\n"), + progname); + exit(1); + } + break; + case 't': + temp = B_TRUE; + break; + case 'R': + status = dladm_set_rootdir(optarg); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, gettext( + "%s: invalid directory specified: %s\n"), + progname, dladm_status2str(status, errmsg)); + exit(1); + } + break; + case ':': + (void) fprintf(stderr, + gettext("%s: option requires a value '-%c'\n"), + progname, optopt); + exit(1); + break; + case '?': + default: + (void) fprintf(stderr, + gettext("%s: unrecognized option '-%c'\n"), + progname, optopt); + exit(1); + break; + } + } + + if (optind == (argc - 1)) + link = argv[optind]; + else if (optind != argc) + usage(); + + if (link == NULL) { + (void) fprintf(stderr, + gettext("%s: link name must be specified\n"), + progname); + exit(1); + } + + if (proplist == NULL) { + if (!reset) { + (void) fprintf(stderr, + gettext("%s: link property must be specified\n"), + progname); + exit(1); + } + status = dladm_set_prop(link, NULL, NULL, 0, DLADM_OPT_TEMP); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, gettext( + "%s: warning: cannot reset link " + "properties on '%s': %s\n"), + progname, link, dladm_status2str(status, errmsg)); + } + if (!temp) { + status = set_linkprop_persist(link, NULL, NULL, 0, + reset); + } + goto done; + } + + for (i = 0; i < proplist->pl_count; i++) { + prop_info_t *pip = &proplist->pl_info[i]; + char **val; + uint_t count; + dladm_status_t s; + + if (reset) { + val = NULL; + count = 0; + } else { + val = pip->pi_val; + count = pip->pi_count; + if (count == 0) { + (void) fprintf(stderr, gettext( + "%s: value(s) for '%s' not specified\n"), + progname, pip->pi_name); + status = DLADM_STATUS_BADARG; + continue; + } + } + s = dladm_set_prop(link, pip->pi_name, val, count, + DLADM_OPT_TEMP); + if (s == DLADM_STATUS_OK) { + if (!temp) { + s = set_linkprop_persist(link, + pip->pi_name, val, count, reset); + if (s != DLADM_STATUS_OK) + status = s; + } + continue; + } + status = s; + switch (s) { + case DLADM_STATUS_NOTFOUND: + (void) fprintf(stderr, + gettext("%s: invalid link property '%s'\n"), + progname, pip->pi_name); + break; + case DLADM_STATUS_BADVAL: { + int j; + char *ptr, *lim; + char **propvals = NULL; + uint_t valcnt = MAX_PROP_VALS; + + ptr = malloc((sizeof (char *) + + DLADM_PROP_VAL_MAX) * MAX_PROP_VALS + + MAX_PROP_LINE); + + propvals = (char **)(void *)ptr; + if (propvals == NULL) { + (void) fprintf(stderr, gettext( + "%s: insufficient memory\n"), progname); + exit(1); + } + for (j = 0; j < MAX_PROP_VALS; j++) { + propvals[j] = ptr + sizeof (char *) * + MAX_PROP_VALS + + j * DLADM_PROP_VAL_MAX; + } + s = dladm_get_prop(link, DLADM_PROP_VAL_MODIFIABLE, + pip->pi_name, propvals, &valcnt); + + ptr = errmsg; + lim = ptr + DLADM_STRSIZE; + *ptr = '\0'; + for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) { + ptr += snprintf(ptr, lim - ptr, "%s,", + propvals[j]); + if (ptr >= lim) + break; + } + if (ptr > errmsg) + *(ptr - 1) = '\0'; + (void) fprintf(stderr, gettext( + "%s: link property '%s' must be one of: %s\n"), + progname, pip->pi_name, errmsg); + free(propvals); + break; + } + default: + if (reset) { + (void) fprintf(stderr, gettext( + "%s: cannot reset link property '%s' on " + "'%s': %s\n"), progname, pip->pi_name, link, + dladm_status2str(s, errmsg)); + } else { + (void) fprintf(stderr, gettext( + "%s: cannot set link property '%s' on " + "'%s': %s\n"), progname, pip->pi_name, link, + dladm_status2str(s, errmsg)); + } + break; + } + } +done: + free_props(proplist); + if (status != DLADM_STATUS_OK) + exit(1); +} + +static void +do_set_linkprop(int argc, char **argv) +{ + set_linkprop(argc, argv, B_FALSE); +} + +static void +do_reset_linkprop(int argc, char **argv) +{ + set_linkprop(argc, argv, B_TRUE); +} + +static int +convert_secobj(char *buf, uint_t len, uint8_t *obj_val, uint_t *obj_lenp, + dladm_secobj_class_t class) +{ + int error = 0; + + if (class != DLADM_SECOBJ_CLASS_WEP) + return (ENOENT); + + switch (len) { + case 5: /* ASCII key sizes */ + case 13: + (void) memcpy(obj_val, buf, len); + *obj_lenp = len; + break; + case 10: /* Hex key sizes, not preceded by 0x */ + case 26: + error = hexascii_to_octet(buf, len, obj_val, obj_lenp); + break; + case 12: /* Hex key sizes, preceded by 0x */ + case 28: + if (strncmp(buf, "0x", 2) != 0) + return (EINVAL); + error = hexascii_to_octet(buf + 2, len - 2, obj_val, obj_lenp); + break; + default: + return (EINVAL); + } + return (error); +} + +/* ARGSUSED */ +static void +defersig(int sig) +{ + signalled = sig; +} + +static int +get_secobj_from_tty(uint_t try, const char *objname, char *buf) +{ + uint_t len = 0; + int c; + struct termios stored, current; + void (*sigfunc)(int); + + /* + * Turn off echo -- but before we do so, defer SIGINT handling + * so that a ^C doesn't leave the terminal corrupted. + */ + sigfunc = signal(SIGINT, defersig); + (void) fflush(stdin); + (void) tcgetattr(0, &stored); + current = stored; + current.c_lflag &= ~(ICANON|ECHO); + current.c_cc[VTIME] = 0; + current.c_cc[VMIN] = 1; + (void) tcsetattr(0, TCSANOW, ¤t); +again: + if (try == 1) + (void) printf(gettext("provide value for '%s': "), objname); + else + (void) printf(gettext("confirm value for '%s': "), objname); + + (void) fflush(stdout); + while (signalled == 0) { + c = getchar(); + if (c == '\n' || c == '\r') { + if (len != 0) + break; + (void) putchar('\n'); + goto again; + } + + buf[len++] = c; + if (len >= DLADM_SECOBJ_VAL_MAX - 1) + break; + (void) putchar('*'); + } + + (void) putchar('\n'); + (void) fflush(stdin); + + /* + * Restore terminal setting and handle deferred signals. + */ + (void) tcsetattr(0, TCSANOW, &stored); + + (void) signal(SIGINT, sigfunc); + if (signalled != 0) + (void) kill(getpid(), signalled); + + return (len); +} + +static int +get_secobj_val(char *obj_name, uint8_t *obj_val, uint_t *obj_lenp, + dladm_secobj_class_t class, FILE *filep) +{ + int rval; + uint_t len, len2; + char buf[DLADM_SECOBJ_VAL_MAX], buf2[DLADM_SECOBJ_VAL_MAX]; + + if (filep == NULL) { + len = get_secobj_from_tty(1, obj_name, buf); + rval = convert_secobj(buf, len, obj_val, obj_lenp, class); + if (rval == 0) { + len2 = get_secobj_from_tty(2, obj_name, buf2); + if (len != len2 || memcmp(buf, buf2, len) != 0) + rval = ENOTSUP; + } + return (rval); + } else { + for (;;) { + if (fgets(buf, sizeof (buf), filep) == NULL) + break; + if (isspace(buf[0])) + continue; + + len = strlen(buf); + if (buf[len - 1] == '\n') { + buf[len - 1] = '\0'; + len--; + } + break; + } + (void) fclose(filep); + } + return (convert_secobj(buf, len, obj_val, obj_lenp, class)); +} + +static boolean_t +check_auth(const char *auth) +{ + struct passwd *pw; + + if ((pw = getpwuid(getuid())) == NULL) + return (B_FALSE); + + return (chkauthattr(auth, pw->pw_name) != 0); +} + +static void +audit_secobj(char *auth, char *class, char *obj, + boolean_t success, boolean_t create) +{ + adt_session_data_t *ah; + adt_event_data_t *event; + au_event_t flag; + char *errstr; + + if (create) { + flag = ADT_dladm_create_secobj; + errstr = "ADT_dladm_create_secobj"; + } else { + flag = ADT_dladm_delete_secobj; + errstr = "ADT_dladm_delete_secobj"; + } + + if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) { + (void) fprintf(stderr, "%s: adt_start_session: %s\n", + progname, strerror(errno)); + exit(1); + } + + if ((event = adt_alloc_event(ah, flag)) == NULL) { + (void) fprintf(stderr, "%s: adt_alloc_event" + "(%s): %s\n", progname, errstr, + strerror(errno)); + exit(1); + } + + /* fill in audit info */ + if (create) { + event->adt_dladm_create_secobj.auth_used = auth; + event->adt_dladm_create_secobj.obj_class = class; + event->adt_dladm_create_secobj.obj_name = obj; + } else { + event->adt_dladm_delete_secobj.auth_used = auth; + event->adt_dladm_delete_secobj.obj_class = class; + event->adt_dladm_delete_secobj.obj_name = obj; + } + + if (success) { + if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) { + (void) fprintf(stderr, "%s: adt_put_event" + "(%s, success): %s\n", + progname, errstr, strerror(errno)); + exit(1); + } + } else { + if (adt_put_event(event, ADT_FAILURE, + ADT_FAIL_VALUE_AUTH) != 0) { + (void) fprintf(stderr, "%s: adt_put_event" + "(%s, failure): %s\n", + progname, errstr, strerror(errno)); + exit(1); + } + } + + adt_free_event(event); + (void) adt_end_session(ah); +} + +#define MAX_SECOBJS 32 +#define MAX_SECOBJ_NAMELEN 32 +static void +do_create_secobj(int argc, char **argv) +{ + int option, rval; + char errmsg[DLADM_STRSIZE]; + FILE *filep = NULL; + char *obj_name = NULL; + char *class_name = NULL; + uint8_t obj_val[DLADM_SECOBJ_VAL_MAX]; + uint_t obj_len; + boolean_t success, temp = B_FALSE; + dladm_status_t status; + dladm_secobj_class_t class = -1; + uid_t euid; + + opterr = 0; + (void) memset(obj_val, 0, DLADM_SECOBJ_VAL_MAX); + while ((option = getopt_long(argc, argv, ":f:c:R:t", + wifi_longopts, NULL)) != -1) { + switch (option) { + case 'f': + euid = geteuid(); + (void) seteuid(getuid()); + filep = fopen(optarg, "r"); + if (filep == NULL) { + (void) fprintf(stderr, + gettext("%s: cannot open %s: %s\n"), + progname, optarg, strerror(errno)); + exit(1); + } + (void) seteuid(euid); + break; + case 'c': + class_name = optarg; + status = dladm_str2secobjclass(optarg, &class); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, gettext( + "%s: invalid secure object class '%s', " + "valid values are: wep\n"), + progname, optarg); + exit(1); + } + break; + case 't': + temp = B_TRUE; + break; + case 'R': + status = dladm_set_rootdir(optarg); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, gettext( + "%s: invalid directory specified: %s\n"), + progname, dladm_status2str(status, errmsg)); + exit(1); + } + break; + case ':': + (void) fprintf(stderr, + gettext("%s: option requires a value '-%c'\n"), + progname, optopt); + exit(1); + break; + case '?': + default: + (void) fprintf(stderr, + gettext("%s: unrecognized option '-%c'\n"), + progname, optopt); + exit(1); + break; + } + } + + if (optind == (argc - 1)) + obj_name = argv[optind]; + else if (optind != argc) + usage(); + + if (class == -1) { + (void) fprintf(stderr, + gettext("%s: secure object class required\n"), + progname); + exit(1); + } + + if (obj_name == NULL) { + (void) fprintf(stderr, + gettext("%s: secure object name required\n"), + progname); + exit(1); + } + + success = check_auth(LINK_SEC_AUTH); + audit_secobj(LINK_SEC_AUTH, class_name, obj_name, success, B_TRUE); + if (!success) { + (void) fprintf(stderr, + gettext("%s: authorization '%s' is required\n"), + progname, LINK_SEC_AUTH); + exit(1); + } + + if ((rval = get_secobj_val(obj_name, obj_val, &obj_len, + class, filep)) != 0) { + switch (rval) { + case ENOENT: + (void) fprintf(stderr, + gettext("%s: invalid secure object class\n"), + progname); + break; + case EINVAL: + (void) fprintf(stderr, + gettext("%s: invalid secure object value\n"), + progname); + break; + case ENOTSUP: + (void) fprintf(stderr, gettext( + "%s: verification failed\n"), progname); + break; + default: + (void) fprintf(stderr, gettext( + "%s: invalid secure object: %s\n"), + progname, strerror(rval)); + break; + } + exit(1); + } + + status = dladm_set_secobj(obj_name, class, obj_val, obj_len, + DLADM_OPT_CREATE | DLADM_OPT_TEMP); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, + gettext("%s: could not create secure object '%s': %s\n"), + progname, obj_name, dladm_status2str(status, errmsg)); + exit(1); + } + if (temp) + return; + + status = dladm_set_secobj(obj_name, class, obj_val, obj_len, + DLADM_OPT_PERSIST); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, + gettext("%s: warning: could not persistently create " + "secure object '%s': %s\n"), progname, obj_name, + dladm_status2str(status, errmsg)); + exit(1); + } +} + +static void +do_delete_secobj(int argc, char **argv) +{ + int i, option; + char errmsg[DLADM_STRSIZE]; + boolean_t temp = B_FALSE; + split_t *sp = NULL; + boolean_t success; + dladm_status_t status, pstatus; + + opterr = 0; + status = pstatus = DLADM_STATUS_OK; + while ((option = getopt_long(argc, argv, "R:t", + wifi_longopts, NULL)) != -1) { + switch (option) { + case 't': + temp = B_TRUE; + break; + case 'R': + status = dladm_set_rootdir(optarg); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, gettext( + "%s: invalid directory specified: %s\n"), + progname, dladm_status2str(status, errmsg)); + exit(1); + } + break; + case ':': + (void) fprintf(stderr, + gettext("%s: option requires a value '-%c'\n"), + progname, optopt); + exit(1); + break; + case '?': + default: + (void) fprintf(stderr, + gettext("%s: unrecognized option '-%c'\n"), + progname, optopt); + exit(1); + break; + } + } + + if (optind == (argc - 1)) { + sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN); + if (sp == NULL) { + (void) fprintf(stderr, gettext( + "%s: invalid secure object name(s): '%s'\n"), + progname, argv[optind]); + exit(1); + } + } else if (optind != argc) + usage(); + + if (sp == NULL || sp->s_nfields < 1) { + (void) fprintf(stderr, + gettext("%s: secure object name required\n"), + progname); + exit(1); + } + + success = check_auth(LINK_SEC_AUTH); + audit_secobj(LINK_SEC_AUTH, "wep", argv[optind], success, B_FALSE); + if (!success) { + (void) fprintf(stderr, + gettext("%s: authorization '%s' is required\n"), + progname, LINK_SEC_AUTH); + exit(1); + } + + for (i = 0; i < sp->s_nfields; i++) { + status = dladm_unset_secobj(sp->s_fields[i], DLADM_OPT_TEMP); + if (!temp) { + pstatus = dladm_unset_secobj(sp->s_fields[i], + DLADM_OPT_PERSIST); + } else { + pstatus = DLADM_STATUS_OK; + } + + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, gettext( + "%s: could not delete secure object '%s': %s\n"), + progname, sp->s_fields[i], + dladm_status2str(status, errmsg)); + } + if (pstatus != DLADM_STATUS_OK) { + (void) fprintf(stderr, gettext("%s: warning: could not " + "persistently delete secure object '%s': %s\n"), + progname, sp->s_fields[i], + dladm_status2str(pstatus, errmsg)); + } + } + if (status != DLADM_STATUS_OK || pstatus != DLADM_STATUS_OK) + exit(1); +} + +typedef struct show_secobj_state { + boolean_t ss_persist; + boolean_t ss_parseable; + boolean_t ss_debug; + boolean_t ss_header; +} show_secobj_state_t; + +static void +print_secobj_head(show_secobj_state_t *statep) +{ + (void) printf("%-20s %-20s ", "OBJECT", "CLASS"); + if (statep->ss_debug) + (void) printf("%-30s", "VALUE"); + (void) putchar('\n'); +} + +static boolean_t +show_secobj(void *arg, const char *obj_name) +{ + uint_t obj_len = DLADM_SECOBJ_VAL_MAX; + uint8_t obj_val[DLADM_SECOBJ_VAL_MAX]; + char buf[DLADM_STRSIZE]; + uint_t flags = 0; + dladm_secobj_class_t class; + show_secobj_state_t *statep = arg; + dladm_status_t status; + + if (statep->ss_persist) + flags |= DLADM_OPT_PERSIST; + + status = dladm_get_secobj(obj_name, &class, obj_val, &obj_len, flags); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, gettext( + "%s: cannot get secure object '%s': %s\n"), progname, + obj_name, dladm_status2str(status, buf)); + exit(1); + } + + if (statep->ss_header) { + statep->ss_header = B_FALSE; + if (!statep->ss_parseable) + print_secobj_head(statep); + } + + if (statep->ss_parseable) { + (void) printf("OBJECT=\"%s\" CLASS=\"%s\" ", obj_name, + dladm_secobjclass2str(class, buf)); + } else { + (void) printf("%-20s %-20s ", obj_name, + dladm_secobjclass2str(class, buf)); + } + + if (statep->ss_debug) { + char val[DLADM_SECOBJ_VAL_MAX * 2]; + uint_t len = sizeof (val); + + if (octet_to_hexascii(obj_val, obj_len, val, &len) == 0) { + if (statep->ss_parseable) + (void) printf("VALUE=\"0x%s\"", val); + else + (void) printf("0x%-30s", val); + } + } + (void) putchar('\n'); + return (B_TRUE); +} + +static void +do_show_secobj(int argc, char **argv) +{ + int option; + show_secobj_state_t state; + dladm_status_t status; + uint_t i; + char errmsg[DLADM_STRSIZE]; + split_t *sp; + uint_t flags; + + opterr = 0; + state.ss_persist = B_FALSE; + state.ss_parseable = B_FALSE; + state.ss_debug = B_FALSE; + state.ss_header = B_TRUE; + while ((option = getopt_long(argc, argv, ":pPd", + wifi_longopts, NULL)) != -1) { + switch (option) { + case 'p': + state.ss_parseable = B_TRUE; + break; + case 'P': + state.ss_persist = B_TRUE; + break; + case 'd': + if (getuid() != 0) { + (void) fprintf(stderr, + gettext("%s: insufficient privileges\n"), + progname); + exit(1); + } + state.ss_debug = B_TRUE; + break; + case ':': + (void) fprintf(stderr, + gettext("%s: option requires a value '-%c'\n"), + progname, optopt); + exit(1); + break; + case '?': + default: + (void) fprintf(stderr, + gettext("%s: unrecognized option '-%c'\n"), + progname, optopt); + exit(1); + break; + } + } + + if (optind == (argc - 1)) { + sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN); + if (sp == NULL) { + (void) fprintf(stderr, gettext( + "%s: invalid secure object name(s): '%s'\n"), + progname, argv[optind]); + exit(1); + } + for (i = 0; i < sp->s_nfields; i++) { + if (!show_secobj(&state, sp->s_fields[i])) + break; + } + splitfree(sp); + return; + } else if (optind != argc) + usage(); + + flags = state.ss_persist ? DLADM_OPT_PERSIST : 0; + status = dladm_walk_secobj(&state, show_secobj, flags); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, gettext("%s: show-secobj: %s\n"), + progname, dladm_status2str(status, errmsg)); + exit(1); + } +} + +/* ARGSUSED */ +static void +do_init_linkprop(int argc, char **argv) +{ + char errmsg[DLADM_STRSIZE]; + dladm_status_t status; + + status = dladm_init_linkprop(); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, + gettext("%s: link property initialization failed: %s\n"), + progname, dladm_status2str(status, errmsg)); + exit(1); + } +} + +/* ARGSUSED */ +static void +do_init_secobj(int argc, char **argv) +{ + char errmsg[DLADM_STRSIZE]; + dladm_status_t status; + + status = dladm_init_secobj(); + if (status != DLADM_STATUS_OK) { + (void) fprintf(stderr, + gettext("%s: secure object initialization failed: %s\n"), + progname, dladm_status2str(status, errmsg)); + exit(1); + } +} |