1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <ctype.h> 30 #include <locale.h> 31 #include <signal.h> 32 #include <stdarg.h> 33 #include <stdlib.h> 34 #include <fcntl.h> 35 #include <string.h> 36 #include <stropts.h> 37 #include <sys/stat.h> 38 #include <errno.h> 39 #include <kstat.h> 40 #include <strings.h> 41 #include <getopt.h> 42 #include <unistd.h> 43 #include <priv.h> 44 #include <termios.h> 45 #include <pwd.h> 46 #include <auth_attr.h> 47 #include <auth_list.h> 48 #include <libintl.h> 49 #include <libdevinfo.h> 50 #include <libdlpi.h> 51 #include <libdllink.h> 52 #include <libdlaggr.h> 53 #include <libdlwlan.h> 54 #include <libdlvlan.h> 55 #include <libdlvnic.h> 56 #include <libinetutil.h> 57 #include <bsm/adt.h> 58 #include <bsm/adt_event.h> 59 #include <stddef.h> 60 61 #define AGGR_DRV "aggr" 62 #define STR_UNDEF_VAL "--" 63 #define MAXPORT 256 64 #define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0) 65 #define MAXLINELEN 1024 66 #define SMF_UPGRADE_FILE "/var/svc/profile/upgrade" 67 #define SMF_UPGRADEDATALINK_FILE "/var/svc/profile/upgrade_datalink" 68 #define SMF_DLADM_UPGRADE_MSG " # added by dladm(1M)" 69 70 #define CMD_TYPE_ANY 0xffffffff 71 #define WIFI_CMD_SCAN 0x00000001 72 #define WIFI_CMD_SHOW 0x00000002 73 #define WIFI_CMD_ALL (WIFI_CMD_SCAN | WIFI_CMD_SHOW) 74 75 /* 76 * Data structures and routines for printing output. 77 * All non-parseable output is assumed to be in a columnar format. 78 * Multiple fields in parsable output are separated by ':'; single 79 * field output is printed as-is. 80 * 81 * Each sub-command is associated with a global array of pointers, 82 * print_field_t *fields[], where the print_field_t contains information 83 * about the format in which the output is to be printed. 84 * 85 * Sub-commands may be implemented in one of two ways: 86 * (i) the implementation could get all field values into a character 87 * buffer, with pf_offset containing the offset (for pf_name) within 88 * the buffer. The sub-command would make the needed system calls 89 * to obtain all possible column values and then invoke the 90 * dladm_print_field() function to print the specific fields 91 * requested in the command line. See the comments for dladm_print_field 92 * for further details. 93 * (ii) Alternatively, each fields[i] entry could store a pf_index value 94 * that uniquely identifies the column to be printed. The implementation 95 * of the sub-command would then invoke dladm_print_output() with a 96 * callback function whose semantics are described below (see comments 97 * for dladm_print_output()) 98 * 99 * Thus, an implementation of a sub-command must provide the following: 100 * 101 * static print_field_t sub_command_fields[] = { 102 * {<name>, <header>,<field width>, <offset_or_index>, cmdtype}, 103 * : 104 * {<name>, <header>,<field width>, <offset_or_index>, cmdtype} 105 * }; 106 * 107 * #define SUB_COMMAND_MAX_FIELDS sizeof \ 108 * (sub_comand_fields) / sizeof (print_field_t)) 109 * 110 * print_state_t sub_command_print_state; 111 * 112 * The function that parses command line arguments (typically 113 * do_sub_command()) should then contain an invocation like: 114 * 115 * fields = parse_output_fields(fields_str, sub_command_fields, 116 * SUB_COMMAND_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 117 * 118 * and store the resulting fields and nfields value in a print_state_t 119 * structure tracked for the command. 120 * 121 * sub_command_print_state.ps_fields = fields; 122 * sub_command_print_state.ps_nfields = nfields; 123 * 124 * To print the column header for the output, the print_header() 125 * function must then be invoked by do_sub_command(). 126 * 127 * Then if method (i) is used for the sub_command, the do_sub_command() 128 * function should make the necessary system calls to fill up the buffer 129 * and then invoke dladm_print_field(). An example of this method is 130 * the implementation of do_show_link() and show_link(); 131 * 132 * If method (ii) is used, do_sub_command should invoke dladm_print_output() 133 * with a callback function that will be called for each field to be printed. 134 * The callback function will be passed a pointer to the print_field_t 135 * for the field, and the pf_index may then be used to identify the 136 * system call required to find the value to be printed. An example of 137 * this implementation may be found in the do_show_dev() and print_dev() 138 * invocation. 139 */ 140 141 typedef struct print_field_s { 142 const char *pf_name; /* name of column to be printed */ 143 const char *pf_header; /* header for this column */ 144 uint_t pf_width; 145 union { 146 uint_t _pf_index; /* private index for sub-command */ 147 size_t _pf_offset; 148 }_pf_un; 149 #define pf_index _pf_un._pf_index 150 #define pf_offset _pf_un._pf_offset; 151 uint_t pf_cmdtype; 152 } print_field_t; 153 154 /* 155 * The state of the output is tracked in a print_state_t structure. 156 * Each ps_fields[i] entry points at the global print_field_t array for 157 * the sub-command, where ps_nfields is the number of requested fields. 158 */ 159 typedef struct print_state_s { 160 print_field_t **ps_fields; 161 uint_t ps_nfields; 162 boolean_t ps_lastfield; 163 uint_t ps_overflow; 164 } print_state_t; 165 166 typedef char *(*print_callback_t)(print_field_t *, void *); 167 static print_field_t **parse_output_fields(char *, print_field_t *, int, 168 uint_t, uint_t *); 169 /* 170 * print the header for the output 171 */ 172 static void print_header(print_state_t *); 173 static void print_field(print_state_t *, print_field_t *, const char *, 174 boolean_t); 175 176 /* 177 * to print output values, call dladm_print_output with a callback 178 * function (*func)() that should parse the args and return an 179 * unformatted character buffer with the value to be printed. 180 * 181 * dladm_print_output() prints the character buffer using the formatting 182 * information provided in the print_field_t for that column. 183 */ 184 static void dladm_print_output(print_state_t *, boolean_t, 185 print_callback_t, void *); 186 187 /* 188 * helper function that, when invoked as dladm_print_field(pf, buf) 189 * prints string which is offset by pf->pf_offset within buf 190 */ 191 static char *dladm_print_field(print_field_t *, void *); 192 193 194 #define MAX_FIELD_LEN 32 195 196 197 typedef struct pktsum_s { 198 uint64_t ipackets; 199 uint64_t opackets; 200 uint64_t rbytes; 201 uint64_t obytes; 202 uint32_t ierrors; 203 uint32_t oerrors; 204 } pktsum_t; 205 206 typedef struct show_state { 207 boolean_t ls_firstonly; 208 boolean_t ls_donefirst; 209 pktsum_t ls_prevstats; 210 uint32_t ls_flags; 211 dladm_status_t ls_status; 212 print_state_t ls_print; 213 boolean_t ls_parseable; 214 boolean_t ls_printheader; 215 } show_state_t; 216 217 typedef struct show_grp_state { 218 pktsum_t gs_prevstats[MAXPORT]; 219 uint32_t gs_flags; 220 dladm_status_t gs_status; 221 boolean_t gs_parseable; 222 boolean_t gs_lacp; 223 boolean_t gs_extended; 224 boolean_t gs_stats; 225 boolean_t gs_firstonly; 226 boolean_t gs_donefirst; 227 boolean_t gs_printheader; 228 print_state_t gs_print; 229 } show_grp_state_t; 230 231 typedef void cmdfunc_t(int, char **, const char *); 232 233 static cmdfunc_t do_show_link, do_show_dev, do_show_wifi, do_show_phys; 234 static cmdfunc_t do_create_aggr, do_delete_aggr, do_add_aggr, do_remove_aggr; 235 static cmdfunc_t do_modify_aggr, do_show_aggr, do_up_aggr; 236 static cmdfunc_t do_scan_wifi, do_connect_wifi, do_disconnect_wifi; 237 static cmdfunc_t do_show_linkprop, do_set_linkprop, do_reset_linkprop; 238 static cmdfunc_t do_create_secobj, do_delete_secobj, do_show_secobj; 239 static cmdfunc_t do_init_linkprop, do_init_secobj; 240 static cmdfunc_t do_create_vlan, do_delete_vlan, do_up_vlan, do_show_vlan; 241 static cmdfunc_t do_rename_link, do_delete_phys, do_init_phys; 242 static cmdfunc_t do_show_linkmap; 243 static cmdfunc_t do_show_ether; 244 245 static void altroot_cmd(char *, int, char **); 246 static int show_linkprop_onelink(datalink_id_t, void *); 247 248 static void link_stats(datalink_id_t, uint_t); 249 static void aggr_stats(datalink_id_t, show_grp_state_t *, uint_t); 250 static void dev_stats(const char *dev, uint32_t, char *, show_state_t *); 251 252 static int get_one_kstat(const char *, const char *, uint8_t, 253 void *, boolean_t); 254 static void get_mac_stats(const char *, pktsum_t *); 255 static void get_link_stats(const char *, pktsum_t *); 256 static uint64_t get_ifspeed(const char *, boolean_t); 257 static void stats_total(pktsum_t *, pktsum_t *, pktsum_t *); 258 static void stats_diff(pktsum_t *, pktsum_t *, pktsum_t *); 259 static const char *get_linkstate(const char *, boolean_t, char *); 260 static const char *get_linkduplex(const char *, boolean_t, char *); 261 262 static int show_etherprop(datalink_id_t, void *); 263 static void show_ether_xprop(datalink_id_t, void *); 264 static boolean_t get_speed_duplex(datalink_id_t, const char *, char *, 265 char *, boolean_t); 266 static char *pause_str(int, int); 267 static boolean_t link_is_ether(const char *, datalink_id_t *); 268 269 #define IS_FDX 0x10 270 #define IS_HDX 0x01 271 272 static boolean_t str2int(const char *, int *); 273 static void die(const char *, ...); 274 static void die_optdup(int); 275 static void die_opterr(int, int, const char *); 276 static void die_dlerr(dladm_status_t, const char *, ...); 277 static void warn(const char *, ...); 278 static void warn_dlerr(dladm_status_t, const char *, ...); 279 280 typedef struct cmd { 281 char *c_name; 282 cmdfunc_t *c_fn; 283 const char *c_usage; 284 } cmd_t; 285 286 static cmd_t cmds[] = { 287 { "show-link", do_show_link, 288 "\tshow-link\t[-pP] [-o <field>,..] [-s [-i <interval>]] [<link>]"}, 289 { "rename-link", do_rename_link, 290 "\trename-link\t[-R <root-dir>] <oldlink> <newlink>\n" }, 291 { "show-dev", do_show_dev, 292 "\tshow-dev\t[-p] [-o <field>,..] [-s [-i <interval>]] [<dev>]\n" }, 293 { "create-aggr", do_create_aggr, 294 "\tcreate-aggr\t[-t] [-R <root-dir>] [-P <policy>] [-L <mode>]\n" 295 "\t\t\t[-T <time>] [-u <address>] [-l <link>] ... <link>" }, 296 { "delete-aggr", do_delete_aggr, 297 "\tdelete-aggr\t[-t] [-R <root-dir>] <link>" }, 298 { "add-aggr", do_add_aggr, 299 "\tadd-aggr\t[-t] [-R <root-dir>] [-l <link>] ... <link>" }, 300 { "remove-aggr", do_remove_aggr, 301 "\tremove-aggr\t[-t] [-R <root-dir>] [-l <link>] ... <link>"}, 302 { "modify-aggr", do_modify_aggr, 303 "\tmodify-aggr\t[-t] [-R <root-dir>] [-P <policy>] [-L <mode>]\n" 304 "\t\t\t[-T <time>] [-u <address>] <link>" }, 305 { "show-aggr", do_show_aggr, 306 "\tshow-aggr\t[-pPLx] [-o <field>,..] [-s [-i <interval>]] " 307 "[<link>]\n" }, 308 { "up-aggr", do_up_aggr, NULL }, 309 { "scan-wifi", do_scan_wifi, 310 "\tscan-wifi\t[-p] [-o <field>,...] [<link>]" }, 311 { "connect-wifi", do_connect_wifi, 312 "\tconnect-wifi\t[-e <essid>] [-i <bssid>] [-k <key>,...] " 313 "[-s wep|wpa]\n" 314 "\t\t\t[-a open|shared] [-b bss|ibss] [-c] [-m a|b|g]\n" 315 "\t\t\t[-T <time>] [<link>]" }, 316 { "disconnect-wifi", do_disconnect_wifi, 317 "\tdisconnect-wifi\t[-a] [<link>]" }, 318 { "show-wifi", do_show_wifi, 319 "\tshow-wifi\t[-p] [-o <field>,...] [<link>]\n" }, 320 { "show-linkprop", do_show_linkprop, 321 "\tshow-linkprop\t[-cP] [-o <field>,...] [-p <prop>,...] <name>"}, 322 { "set-linkprop", do_set_linkprop, 323 "\tset-linkprop\t[-t] [-R <root-dir>] -p <prop>=<value>[,...] " 324 "<name>" }, 325 { "reset-linkprop", do_reset_linkprop, 326 "\treset-linkprop\t[-t] [-R <root-dir>] [-p <prop>,...] <name>\n" }, 327 { "show-ether", do_show_ether, 328 "\tshow-ether\t[-px][-o <field>,...] <link>\n" }, 329 { "create-secobj", do_create_secobj, 330 "\tcreate-secobj\t[-t] [-R <root-dir>] [-f <file>] -c <class> " 331 "<secobj>" }, 332 { "delete-secobj", do_delete_secobj, 333 "\tdelete-secobj\t[-t] [-R <root-dir>] <secobj>[,...]" }, 334 { "show-secobj", do_show_secobj, 335 "\tshow-secobj\t[-pP] [-o <field>,...] [<secobj>,...]\n" }, 336 { "init-linkprop", do_init_linkprop, NULL }, 337 { "init-secobj", do_init_secobj, NULL }, 338 { "create-vlan", do_create_vlan, 339 "\tcreate-vlan\t[-ft] [-R <root-dir>] -l <link> -v <vid> [link]" }, 340 { "delete-vlan", do_delete_vlan, 341 "\tdelete-vlan\t[-t] [-R <root-dir>] <link>" }, 342 { "show-vlan", do_show_vlan, 343 "\tshow-vlan\t[-pP] [-o <field>,..] [<link>]\n" }, 344 { "up-vlan", do_up_vlan, NULL }, 345 { "delete-phys", do_delete_phys, 346 "\tdelete-phys\t<link>" }, 347 { "show-phys", do_show_phys, 348 "\tshow-phys\t[-pP] [-o <field>,..] [<link>]" }, 349 { "init-phys", do_init_phys, NULL }, 350 { "show-linkmap", do_show_linkmap, NULL } 351 }; 352 353 static const struct option lopts[] = { 354 {"vlan-id", required_argument, 0, 'v'}, 355 {"output", required_argument, 0, 'o'}, 356 {"dev", required_argument, 0, 'd'}, 357 {"policy", required_argument, 0, 'P'}, 358 {"lacp-mode", required_argument, 0, 'L'}, 359 {"lacp-timer", required_argument, 0, 'T'}, 360 {"unicast", required_argument, 0, 'u'}, 361 {"temporary", no_argument, 0, 't'}, 362 {"root-dir", required_argument, 0, 'R'}, 363 {"link", required_argument, 0, 'l'}, 364 {"forcible", no_argument, 0, 'f'}, 365 { 0, 0, 0, 0 } 366 }; 367 368 static const struct option show_lopts[] = { 369 {"statistics", no_argument, 0, 's'}, 370 {"interval", required_argument, 0, 'i'}, 371 {"parseable", no_argument, 0, 'p'}, 372 {"extended", no_argument, 0, 'x'}, 373 {"output", required_argument, 0, 'o'}, 374 {"persistent", no_argument, 0, 'P'}, 375 {"lacp", no_argument, 0, 'L'}, 376 { 0, 0, 0, 0 } 377 }; 378 379 static const struct option prop_longopts[] = { 380 {"temporary", no_argument, 0, 't' }, 381 {"output", required_argument, 0, 'o' }, 382 {"root-dir", required_argument, 0, 'R' }, 383 {"prop", required_argument, 0, 'p' }, 384 {"parseable", no_argument, 0, 'c' }, 385 {"persistent", no_argument, 0, 'P' }, 386 { 0, 0, 0, 0 } 387 }; 388 389 static const struct option wifi_longopts[] = { 390 {"parseable", no_argument, 0, 'p' }, 391 {"output", required_argument, 0, 'o' }, 392 {"essid", required_argument, 0, 'e' }, 393 {"bsstype", required_argument, 0, 'b' }, 394 {"mode", required_argument, 0, 'm' }, 395 {"key", required_argument, 0, 'k' }, 396 {"sec", required_argument, 0, 's' }, 397 {"auth", required_argument, 0, 'a' }, 398 {"create-ibss", required_argument, 0, 'c' }, 399 {"timeout", required_argument, 0, 'T' }, 400 {"all-links", no_argument, 0, 'a' }, 401 {"temporary", no_argument, 0, 't' }, 402 {"root-dir", required_argument, 0, 'R' }, 403 {"persistent", no_argument, 0, 'P' }, 404 {"file", required_argument, 0, 'f' }, 405 { 0, 0, 0, 0 } 406 }; 407 static const struct option showeth_lopts[] = { 408 {"parseable", no_argument, 0, 'p' }, 409 {"extended", no_argument, 0, 'x' }, 410 {"output", required_argument, 0, 'o' }, 411 { 0, 0, 0, 0 } 412 }; 413 414 /* 415 * structures for 'dladm show-ether' 416 */ 417 typedef struct ether_fields_buf_s 418 { 419 char eth_link[15]; 420 char eth_ptype[8]; 421 char eth_state[8]; 422 char eth_autoneg[5]; 423 char eth_spdx[31]; 424 char eth_pause[6]; 425 char eth_rem_fault[16]; 426 } ether_fields_buf_t; 427 428 static print_field_t ether_fields[] = { 429 /* name, header, field width, offset, cmdtype */ 430 { "link", "LINK", 15, 431 offsetof(ether_fields_buf_t, eth_link), CMD_TYPE_ANY}, 432 { "ptype", "PTYPE", 8, 433 offsetof(ether_fields_buf_t, eth_ptype), CMD_TYPE_ANY}, 434 { "state", "STATE", 8, 435 offsetof(ether_fields_buf_t, eth_state), CMD_TYPE_ANY}, 436 { "auto", "AUTO", 5, 437 offsetof(ether_fields_buf_t, eth_autoneg), CMD_TYPE_ANY}, 438 { "speed-duplex", "SPEED-DUPLEX", 31, 439 offsetof(ether_fields_buf_t, eth_spdx), CMD_TYPE_ANY}, 440 { "pause", "PAUSE", 6, 441 offsetof(ether_fields_buf_t, eth_pause), CMD_TYPE_ANY}, 442 { "rem_fault", "REM_FAULT", 16, 443 offsetof(ether_fields_buf_t, eth_rem_fault), CMD_TYPE_ANY}} 444 ; 445 #define ETHER_MAX_FIELDS (sizeof (ether_fields) / sizeof (print_field_t)) 446 447 typedef struct print_ether_state { 448 const char *es_link; 449 boolean_t es_parseable; 450 boolean_t es_header; 451 boolean_t es_extended; 452 print_state_t es_print; 453 } print_ether_state_t; 454 455 /* 456 * structures for 'dladm show-dev'. 457 */ 458 typedef enum { 459 DEV_LINK, 460 DEV_STATE, 461 DEV_SPEED, 462 DEV_DUPLEX 463 } dev_field_index_t; 464 465 static print_field_t dev_fields[] = { 466 /* name, header, field width, index, cmdtype */ 467 { "link", "LINK", 15, DEV_LINK, CMD_TYPE_ANY}, 468 { "state", "STATE", 6, DEV_STATE, CMD_TYPE_ANY}, 469 { "speed", "SPEED", 8, DEV_SPEED, CMD_TYPE_ANY}, 470 { "duplex", "DUPLEX", 8, DEV_DUPLEX, CMD_TYPE_ANY}} 471 ; 472 #define DEV_MAX_FIELDS (sizeof (dev_fields) / sizeof (print_field_t)) 473 474 /* 475 * structures for 'dladm show-dev -s' (print statistics) 476 */ 477 typedef enum { 478 DEVS_LINK, 479 DEVS_IPKTS, 480 DEVS_RBYTES, 481 DEVS_IERRORS, 482 DEVS_OPKTS, 483 DEVS_OBYTES, 484 DEVS_OERRORS 485 } devs_field_index_t; 486 487 static print_field_t devs_fields[] = { 488 /* name, header, field width, index, cmdtype */ 489 { "link", "LINK", 15, DEVS_LINK, CMD_TYPE_ANY}, 490 { "ipackets", "IPACKETS", 10, DEVS_IPKTS, CMD_TYPE_ANY}, 491 { "rbytes", "RBYTES", 8, DEVS_RBYTES, CMD_TYPE_ANY}, 492 { "ierrors", "IERRORS", 10, DEVS_IERRORS, CMD_TYPE_ANY}, 493 { "opackets", "OPACKETS", 12, DEVS_OPKTS, CMD_TYPE_ANY}, 494 { "obytes", "OBYTES", 12, DEVS_OBYTES, CMD_TYPE_ANY}, 495 { "oerrors", "OERRORS", 8, DEVS_OERRORS, CMD_TYPE_ANY}} 496 ; 497 #define DEVS_MAX_FIELDS (sizeof (devs_fields) / sizeof (print_field_t)) 498 typedef struct dev_args_s { 499 char *devs_link; 500 pktsum_t *devs_psum; 501 } dev_args_t; 502 static char *print_dev_stats(print_field_t *, void *); 503 static char *print_dev(print_field_t *, void *); 504 505 /* 506 * buffer used by print functions for show-{link,phys,vlan} commands. 507 */ 508 typedef struct link_fields_buf_s { 509 char link_name[MAXLINKNAMELEN]; 510 char link_class[DLADM_STRSIZE]; 511 char link_mtu[11]; 512 char link_state[DLADM_STRSIZE]; 513 char link_over[MAXLINKNAMELEN]; 514 char link_phys_state[DLADM_STRSIZE]; 515 char link_phys_media[DLADM_STRSIZE]; 516 char link_phys_speed[DLADM_STRSIZE]; 517 char link_phys_duplex[DLPI_LINKNAME_MAX]; 518 char link_phys_device[DLPI_LINKNAME_MAX]; 519 char link_flags[6]; 520 char link_vlan_vid[6]; 521 } link_fields_buf_t; 522 523 /* 524 * structures for 'dladm show-link' 525 */ 526 static print_field_t link_fields[] = { 527 /* name, header, field width, offset, cmdtype */ 528 { "link", "LINK", 11, 529 offsetof(link_fields_buf_t, link_name), CMD_TYPE_ANY}, 530 { "class", "CLASS", 8, 531 offsetof(link_fields_buf_t, link_class), CMD_TYPE_ANY}, 532 { "mtu", "MTU", 6, 533 offsetof(link_fields_buf_t, link_mtu), CMD_TYPE_ANY}, 534 { "state", "STATE", 8, 535 offsetof(link_fields_buf_t, link_state), CMD_TYPE_ANY}, 536 { "over", "OVER", DLPI_LINKNAME_MAX, 537 offsetof(link_fields_buf_t, link_over), CMD_TYPE_ANY}} 538 ; 539 #define DEV_LINK_FIELDS (sizeof (link_fields) / sizeof (print_field_t)) 540 541 /* 542 * structures for 'dladm show-aggr' 543 */ 544 typedef struct laggr_fields_buf_s { 545 char laggr_name[DLPI_LINKNAME_MAX]; 546 char laggr_policy[9]; 547 char laggr_addrpolicy[ETHERADDRL * 3 + 3]; 548 char laggr_lacpactivity[14]; 549 char laggr_lacptimer[DLADM_STRSIZE]; 550 char laggr_flags[7]; 551 } laggr_fields_buf_t; 552 553 typedef struct laggr_args_s { 554 int laggr_lport; /* -1 indicates the aggr itself */ 555 const char *laggr_link; 556 dladm_aggr_grp_attr_t *laggr_ginfop; 557 dladm_status_t *laggr_status; 558 pktsum_t *laggr_pktsumtot; /* -s only */ 559 pktsum_t *laggr_prevstats; /* -s only */ 560 boolean_t laggr_parseable; 561 } laggr_args_t; 562 563 static print_field_t laggr_fields[] = { 564 /* name, header, field width, offset, cmdtype */ 565 { "link", "LINK", 15, 566 offsetof(laggr_fields_buf_t, laggr_name), CMD_TYPE_ANY}, 567 { "policy", "POLICY", 8, 568 offsetof(laggr_fields_buf_t, laggr_policy), CMD_TYPE_ANY}, 569 { "addrpolicy", "ADDRPOLICY", ETHERADDRL * 3 + 2, 570 offsetof(laggr_fields_buf_t, laggr_addrpolicy), CMD_TYPE_ANY}, 571 { "lacpactivity", "LACPACTIVITY", 13, 572 offsetof(laggr_fields_buf_t, laggr_lacpactivity), CMD_TYPE_ANY}, 573 { "lacptimer", "LACPTIMER", 11, 574 offsetof(laggr_fields_buf_t, laggr_lacptimer), CMD_TYPE_ANY}, 575 { "flags", "FLAGS", 7, 576 offsetof(laggr_fields_buf_t, laggr_flags), CMD_TYPE_ANY}} 577 ; 578 #define LAGGR_MAX_FIELDS (sizeof (laggr_fields) / sizeof (print_field_t)) 579 580 /* 581 * structures for 'dladm show-aggr -x'. 582 */ 583 typedef enum { 584 AGGR_X_LINK, 585 AGGR_X_PORT, 586 AGGR_X_SPEED, 587 AGGR_X_DUPLEX, 588 AGGR_X_STATE, 589 AGGR_X_ADDRESS, 590 AGGR_X_PORTSTATE 591 } aggr_x_field_index_t; 592 593 static print_field_t aggr_x_fields[] = { 594 /* name, header, field width, index, cmdtype */ 595 { "link", "LINK", 11, AGGR_X_LINK, CMD_TYPE_ANY}, 596 { "port", "PORT", 14, AGGR_X_PORT, CMD_TYPE_ANY}, 597 { "speed", "SPEED", 4, AGGR_X_SPEED, CMD_TYPE_ANY}, 598 { "duplex", "DUPLEX", 9, AGGR_X_DUPLEX, CMD_TYPE_ANY}, 599 { "state", "STATE", 9, AGGR_X_STATE, CMD_TYPE_ANY}, 600 { "address", "ADDRESS", 18, AGGR_X_ADDRESS, CMD_TYPE_ANY}, 601 { "portstate", "PORTSTATE", 15, AGGR_X_PORTSTATE, CMD_TYPE_ANY}} 602 ; 603 #define AGGR_X_MAX_FIELDS \ 604 (sizeof (aggr_x_fields) / sizeof (print_field_t)) 605 606 /* 607 * structures for 'dladm show-aggr -s'. 608 */ 609 typedef enum { 610 AGGR_S_LINK, 611 AGGR_S_PORT, 612 AGGR_S_IPKTS, 613 AGGR_S_RBYTES, 614 AGGR_S_OPKTS, 615 AGGR_S_OBYTES, 616 AGGR_S_IPKTDIST, 617 AGGR_S_OPKTDIST 618 } aggr_s_field_index_t; 619 620 static print_field_t aggr_s_fields[] = { 621 /* name, header, field width, index, cmdtype */ 622 { "link", "LINK", 11, AGGR_S_LINK, 623 CMD_TYPE_ANY}, 624 { "port", "PORT", 9, AGGR_S_PORT, 625 CMD_TYPE_ANY}, 626 { "ipackets", "IPACKETS", 7, AGGR_S_IPKTS, 627 CMD_TYPE_ANY}, 628 { "rbytes", "RBYTES", 7, AGGR_S_RBYTES, 629 CMD_TYPE_ANY}, 630 { "opackets", "OPACKETS", 7, AGGR_S_OPKTS, 631 CMD_TYPE_ANY}, 632 { "obytes", "OBYTES", 7, AGGR_S_OBYTES, 633 CMD_TYPE_ANY}, 634 { "ipktdist", "IPKTDIST", 8, AGGR_S_IPKTDIST, 635 CMD_TYPE_ANY}, 636 { "opktdist", "OPKTDIST", 14, AGGR_S_OPKTDIST, 637 CMD_TYPE_ANY}} 638 ; 639 #define AGGR_S_MAX_FIELDS \ 640 (sizeof (aggr_l_fields) / sizeof (print_field_t)) 641 642 /* 643 * structures for 'dladm show-dev -L'. 644 */ 645 typedef enum { 646 AGGR_L_LINK, 647 AGGR_L_PORT, 648 AGGR_L_AGGREGATABLE, 649 AGGR_L_SYNC, 650 AGGR_L_COLL, 651 AGGR_L_DIST, 652 AGGR_L_DEFAULTED, 653 AGGR_L_EXPIRED 654 } aggr_l_field_index_t; 655 656 static print_field_t aggr_l_fields[] = { 657 /* name, header, field width, index, cmdtype */ 658 { "link", "LINK", 11, AGGR_L_LINK, 659 CMD_TYPE_ANY}, 660 { "port", "PORT", 12, AGGR_L_PORT, 661 CMD_TYPE_ANY}, 662 { "aggregatable", "AGGREGATABLE", 12, AGGR_L_AGGREGATABLE, 663 CMD_TYPE_ANY}, 664 { "sync", "SYNC", 4, AGGR_L_SYNC, 665 CMD_TYPE_ANY}, 666 { "coll", "COLL", 4, AGGR_L_COLL, 667 CMD_TYPE_ANY}, 668 { "dist", "DIST", 4, AGGR_L_DIST, 669 CMD_TYPE_ANY}, 670 { "defaulted", "DEFAULTED", 9, AGGR_L_DEFAULTED, 671 CMD_TYPE_ANY}, 672 { "expired", "EXPIRED", 14, AGGR_L_EXPIRED, 673 CMD_TYPE_ANY}} 674 ; 675 #define AGGR_L_MAX_FIELDS \ 676 (sizeof (aggr_l_fields) / sizeof (print_field_t)) 677 678 /* 679 * structures for 'dladm show-phys' 680 */ 681 682 static print_field_t phys_fields[] = { 683 /* name, header, field width, offset, cmdtype */ 684 { "link", "LINK", 12, 685 offsetof(link_fields_buf_t, link_name), CMD_TYPE_ANY}, 686 { "media", "MEDIA", 20, 687 offsetof(link_fields_buf_t, link_phys_media), CMD_TYPE_ANY}, 688 { "state", "STATE", 10, 689 offsetof(link_fields_buf_t, link_phys_state), CMD_TYPE_ANY}, 690 { "speed", "SPEED", 4, 691 offsetof(link_fields_buf_t, link_phys_speed), CMD_TYPE_ANY}, 692 { "duplex", "DUPLEX", 9, 693 offsetof(link_fields_buf_t, link_phys_duplex), CMD_TYPE_ANY}, 694 { "device", "DEVICE", 12, 695 offsetof(link_fields_buf_t, link_phys_device), CMD_TYPE_ANY}, 696 { "flags", "FLAGS", 6, 697 offsetof(link_fields_buf_t, link_flags), CMD_TYPE_ANY}} 698 ; 699 #define PHYS_MAX_FIELDS (sizeof (phys_fields) / sizeof (print_field_t)) 700 701 /* 702 * structures for 'dladm show-vlan' 703 */ 704 static print_field_t vlan_fields[] = { 705 /* name, header, field width, offset, cmdtype */ 706 { "link", "LINK", 15, 707 offsetof(link_fields_buf_t, link_name), CMD_TYPE_ANY}, 708 { "vid", "VID", 8, 709 offsetof(link_fields_buf_t, link_vlan_vid), CMD_TYPE_ANY}, 710 { "over", "OVER", 12, 711 offsetof(link_fields_buf_t, link_over), CMD_TYPE_ANY}, 712 { "flags", "FLAGS", 6, 713 offsetof(link_fields_buf_t, link_flags), CMD_TYPE_ANY}} 714 ; 715 #define VLAN_MAX_FIELDS (sizeof (vlan_fields) / sizeof (print_field_t)) 716 717 /* 718 * structures for 'dladm show-wifi' 719 */ 720 static print_field_t wifi_fields[] = { 721 { "link", "LINK", 10, 0, WIFI_CMD_ALL}, 722 { "essid", "ESSID", 19, DLADM_WLAN_ATTR_ESSID, WIFI_CMD_ALL}, 723 { "bssid", "BSSID/IBSSID", 17, DLADM_WLAN_ATTR_BSSID, WIFI_CMD_ALL}, 724 { "ibssid", "BSSID/IBSSID", 17, DLADM_WLAN_ATTR_BSSID, WIFI_CMD_ALL}, 725 { "mode", "MODE", 6, DLADM_WLAN_ATTR_MODE, WIFI_CMD_ALL}, 726 { "speed", "SPEED", 6, DLADM_WLAN_ATTR_SPEED, WIFI_CMD_ALL}, 727 { "auth", "AUTH", 8, DLADM_WLAN_ATTR_AUTH, WIFI_CMD_SHOW}, 728 { "bsstype", "BSSTYPE", 8, DLADM_WLAN_ATTR_BSSTYPE, WIFI_CMD_ALL}, 729 { "sec", "SEC", 6, DLADM_WLAN_ATTR_SECMODE, WIFI_CMD_ALL}, 730 { "status", "STATUS", 17, DLADM_WLAN_LINKATTR_STATUS, WIFI_CMD_SHOW}, 731 { "strength", "STRENGTH", 10, DLADM_WLAN_ATTR_STRENGTH, WIFI_CMD_ALL}} 732 ; 733 734 static char *all_scan_wifi_fields = 735 "link,essid,bssid,sec,strength,mode,speed,bsstype"; 736 static char *all_show_wifi_fields = 737 "link,status,essid,sec,strength,mode,speed,auth,bssid,bsstype"; 738 static char *def_scan_wifi_fields = 739 "link,essid,bssid,sec,strength,mode,speed"; 740 static char *def_show_wifi_fields = 741 "link,status,essid,sec,strength,mode,speed"; 742 743 #define WIFI_MAX_FIELDS (sizeof (wifi_fields) / sizeof (print_field_t)) 744 745 /* 746 * structures for 'dladm show-linkprop' 747 */ 748 typedef enum { 749 LINKPROP_LINK, 750 LINKPROP_PROPERTY, 751 LINKPROP_VALUE, 752 LINKPROP_DEFAULT, 753 LINKPROP_POSSIBLE 754 } linkprop_field_index_t; 755 756 static print_field_t linkprop_fields[] = { 757 /* name, header, field width, index, cmdtype */ 758 { "link", "LINK", 12, LINKPROP_LINK, CMD_TYPE_ANY}, 759 { "property", "PROPERTY", 15, LINKPROP_PROPERTY, CMD_TYPE_ANY}, 760 { "value", "VALUE", 14, LINKPROP_VALUE, CMD_TYPE_ANY}, 761 { "default", "DEFAULT", 14, LINKPROP_DEFAULT, CMD_TYPE_ANY}, 762 { "possible", "POSSIBLE", 20, LINKPROP_POSSIBLE, CMD_TYPE_ANY}} 763 ; 764 #define LINKPROP_MAX_FIELDS \ 765 (sizeof (linkprop_fields) / sizeof (print_field_t)) 766 767 #define MAX_PROPS 32 768 #define MAX_PROP_LINE 512 769 770 typedef struct prop_info { 771 char *pi_name; 772 char *pi_val[DLADM_MAX_PROP_VALCNT]; 773 uint_t pi_count; 774 } prop_info_t; 775 776 typedef struct prop_list { 777 prop_info_t pl_info[MAX_PROPS]; 778 uint_t pl_count; 779 char *pl_buf; 780 } prop_list_t; 781 782 typedef struct show_linkprop_state { 783 char ls_link[MAXLINKNAMELEN]; 784 char *ls_line; 785 char **ls_propvals; 786 prop_list_t *ls_proplist; 787 boolean_t ls_parseable; 788 boolean_t ls_persist; 789 boolean_t ls_header; 790 dladm_status_t ls_status; 791 dladm_status_t ls_retstatus; 792 print_state_t ls_print; 793 } show_linkprop_state_t; 794 795 typedef struct linkprop_args_s { 796 show_linkprop_state_t *ls_state; 797 char *ls_propname; 798 datalink_id_t ls_linkid; 799 } linkprop_args_t; 800 801 /* 802 * structures for 'dladm show-secobj' 803 */ 804 typedef struct secobj_fields_buf_s { 805 char ss_obj_name[DLADM_SECOBJ_VAL_MAX]; 806 char ss_class[20]; 807 char ss_val[30]; 808 } secobj_fields_buf_t; 809 static print_field_t secobj_fields[] = { 810 /* name, header, field width, offset, cmdtype */ 811 { "object", "OBJECT", 20, 812 offsetof(secobj_fields_buf_t, ss_obj_name), CMD_TYPE_ANY}, 813 { "class", "CLASS", 20, 814 offsetof(secobj_fields_buf_t, ss_class), CMD_TYPE_ANY}, 815 { "value", "VALUE", 30, 816 offsetof(secobj_fields_buf_t, ss_val), CMD_TYPE_ANY}} 817 ; 818 #define DEV_SOBJ_FIELDS (sizeof (secobj_fields) / sizeof (print_field_t)) 819 820 static char *progname; 821 static sig_atomic_t signalled; 822 823 static void 824 usage(void) 825 { 826 int i; 827 cmd_t *cmdp; 828 (void) fprintf(stderr, gettext("usage: dladm <subcommand> <args> ..." 829 "\n")); 830 for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) { 831 cmdp = &cmds[i]; 832 if (cmdp->c_usage != NULL) 833 (void) fprintf(stderr, "%s\n", gettext(cmdp->c_usage)); 834 } 835 exit(1); 836 } 837 838 int 839 main(int argc, char *argv[]) 840 { 841 int i; 842 cmd_t *cmdp; 843 844 (void) setlocale(LC_ALL, ""); 845 #if !defined(TEXT_DOMAIN) 846 #define TEXT_DOMAIN "SYS_TEST" 847 #endif 848 (void) textdomain(TEXT_DOMAIN); 849 850 progname = argv[0]; 851 852 if (argc < 2) 853 usage(); 854 855 if (!priv_ineffect(PRIV_SYS_NET_CONFIG) || 856 !priv_ineffect(PRIV_NET_RAWACCESS)) 857 die("insufficient privileges"); 858 859 for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) { 860 cmdp = &cmds[i]; 861 if (strcmp(argv[1], cmdp->c_name) == 0) { 862 cmdp->c_fn(argc - 1, &argv[1], cmdp->c_usage); 863 exit(0); 864 } 865 } 866 867 (void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"), 868 progname, argv[1]); 869 usage(); 870 871 return (0); 872 } 873 874 static void 875 do_create_aggr(int argc, char *argv[], const char *use) 876 { 877 char option; 878 int key = 0; 879 uint32_t policy = AGGR_POLICY_L4; 880 aggr_lacp_mode_t lacp_mode = AGGR_LACP_OFF; 881 aggr_lacp_timer_t lacp_timer = AGGR_LACP_TIMER_SHORT; 882 dladm_aggr_port_attr_db_t port[MAXPORT]; 883 uint_t n, ndev, nlink; 884 uint8_t mac_addr[ETHERADDRL]; 885 boolean_t mac_addr_fixed = B_FALSE; 886 boolean_t P_arg = B_FALSE; 887 boolean_t l_arg = B_FALSE; 888 boolean_t u_arg = B_FALSE; 889 boolean_t T_arg = B_FALSE; 890 uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; 891 char *altroot = NULL; 892 char name[MAXLINKNAMELEN]; 893 char *devs[MAXPORT]; 894 char *links[MAXPORT]; 895 dladm_status_t status; 896 897 ndev = nlink = opterr = 0; 898 while ((option = getopt_long(argc, argv, ":d:l:L:P:R:tfu:T:", 899 lopts, NULL)) != -1) { 900 switch (option) { 901 case 'd': 902 if (ndev + nlink >= MAXPORT) 903 die("too many ports specified"); 904 905 devs[ndev++] = optarg; 906 break; 907 case 'P': 908 if (P_arg) 909 die_optdup(option); 910 911 P_arg = B_TRUE; 912 if (!dladm_aggr_str2policy(optarg, &policy)) 913 die("invalid policy '%s'", optarg); 914 break; 915 case 'u': 916 if (u_arg) 917 die_optdup(option); 918 919 u_arg = B_TRUE; 920 if (!dladm_aggr_str2macaddr(optarg, &mac_addr_fixed, 921 mac_addr)) 922 die("invalid MAC address '%s'", optarg); 923 break; 924 case 'l': 925 if (isdigit(optarg[strlen(optarg) - 1])) { 926 927 /* 928 * Ended with digit, possibly a link name. 929 */ 930 if (ndev + nlink >= MAXPORT) 931 die("too many ports specified"); 932 933 links[nlink++] = optarg; 934 break; 935 } 936 /* FALLTHROUGH */ 937 case 'L': 938 if (l_arg) 939 die_optdup(option); 940 941 l_arg = B_TRUE; 942 if (!dladm_aggr_str2lacpmode(optarg, &lacp_mode)) 943 die("invalid LACP mode '%s'", optarg); 944 break; 945 case 'T': 946 if (T_arg) 947 die_optdup(option); 948 949 T_arg = B_TRUE; 950 if (!dladm_aggr_str2lacptimer(optarg, &lacp_timer)) 951 die("invalid LACP timer value '%s'", optarg); 952 break; 953 case 't': 954 flags &= ~DLADM_OPT_PERSIST; 955 break; 956 case 'f': 957 flags |= DLADM_OPT_FORCE; 958 break; 959 case 'R': 960 altroot = optarg; 961 break; 962 default: 963 die_opterr(optopt, option, use); 964 break; 965 } 966 } 967 968 if (ndev + nlink == 0) 969 usage(); 970 971 /* get key value or the aggregation name (required last argument) */ 972 if (optind != (argc-1)) 973 usage(); 974 975 if (!str2int(argv[optind], &key)) { 976 if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= 977 MAXLINKNAMELEN) { 978 die("link name too long '%s'", argv[optind]); 979 } 980 981 if (!dladm_valid_linkname(name)) 982 die("invalid link name '%s'", argv[optind]); 983 } else { 984 (void) snprintf(name, MAXLINKNAMELEN, "aggr%d", key); 985 } 986 987 if (altroot != NULL) 988 altroot_cmd(altroot, argc, argv); 989 990 for (n = 0; n < ndev; n++) { 991 if (dladm_dev2linkid(devs[n], &port[n].lp_linkid) != 992 DLADM_STATUS_OK) { 993 die("invalid dev name '%s'", devs[n]); 994 } 995 } 996 997 for (n = 0; n < nlink; n++) { 998 if (dladm_name2info(links[n], &port[ndev + n].lp_linkid, 999 NULL, NULL, NULL) != DLADM_STATUS_OK) { 1000 die("invalid link name '%s'", links[n]); 1001 } 1002 } 1003 1004 status = dladm_aggr_create(name, key, ndev + nlink, port, policy, 1005 mac_addr_fixed, (const uchar_t *)mac_addr, lacp_mode, 1006 lacp_timer, flags); 1007 done: 1008 if (status != DLADM_STATUS_OK) { 1009 if (status == DLADM_STATUS_NONOTIF) { 1010 die_dlerr(status, "not all links have link up/down " 1011 "detection; must use -f (see dladm(1M))\n"); 1012 } else { 1013 die_dlerr(status, "create operation failed"); 1014 } 1015 } 1016 } 1017 1018 /* 1019 * arg is either the key or the aggr name. Validate it and convert it to 1020 * the linkid if altroot is NULL. 1021 */ 1022 static dladm_status_t 1023 i_dladm_aggr_get_linkid(const char *altroot, const char *arg, 1024 datalink_id_t *linkidp, uint32_t flags) 1025 { 1026 int key = 0; 1027 char *aggr = NULL; 1028 dladm_status_t status; 1029 1030 if (!str2int(arg, &key)) 1031 aggr = (char *)arg; 1032 1033 if (aggr == NULL && key == 0) 1034 return (DLADM_STATUS_LINKINVAL); 1035 1036 if (altroot != NULL) 1037 return (DLADM_STATUS_OK); 1038 1039 if (aggr != NULL) { 1040 status = dladm_name2info(aggr, linkidp, NULL, NULL, NULL); 1041 } else { 1042 status = dladm_key2linkid(key, linkidp, flags); 1043 } 1044 1045 return (status); 1046 } 1047 1048 static void 1049 do_delete_aggr(int argc, char *argv[], const char *use) 1050 { 1051 char option; 1052 char *altroot = NULL; 1053 uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; 1054 dladm_status_t status; 1055 datalink_id_t linkid; 1056 1057 opterr = 0; 1058 while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) { 1059 switch (option) { 1060 case 't': 1061 flags &= ~DLADM_OPT_PERSIST; 1062 break; 1063 case 'R': 1064 altroot = optarg; 1065 break; 1066 default: 1067 die_opterr(optopt, option, use); 1068 break; 1069 } 1070 } 1071 1072 /* get key value or the aggregation name (required last argument) */ 1073 if (optind != (argc-1)) 1074 usage(); 1075 1076 status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags); 1077 if (status != DLADM_STATUS_OK) 1078 goto done; 1079 1080 if (altroot != NULL) 1081 altroot_cmd(altroot, argc, argv); 1082 1083 status = dladm_aggr_delete(linkid, flags); 1084 done: 1085 if (status != DLADM_STATUS_OK) 1086 die_dlerr(status, "delete operation failed"); 1087 } 1088 1089 static void 1090 do_add_aggr(int argc, char *argv[], const char *use) 1091 { 1092 char option; 1093 uint_t n, ndev, nlink; 1094 char *altroot = NULL; 1095 uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; 1096 datalink_id_t linkid; 1097 dladm_status_t status; 1098 dladm_aggr_port_attr_db_t port[MAXPORT]; 1099 char *devs[MAXPORT]; 1100 char *links[MAXPORT]; 1101 1102 ndev = nlink = opterr = 0; 1103 while ((option = getopt_long(argc, argv, ":d:l:R:tf", lopts, 1104 NULL)) != -1) { 1105 switch (option) { 1106 case 'd': 1107 if (ndev + nlink >= MAXPORT) 1108 die("too many ports specified"); 1109 1110 devs[ndev++] = optarg; 1111 break; 1112 case 'l': 1113 if (ndev + nlink >= MAXPORT) 1114 die("too many ports specified"); 1115 1116 links[nlink++] = optarg; 1117 break; 1118 case 't': 1119 flags &= ~DLADM_OPT_PERSIST; 1120 break; 1121 case 'f': 1122 flags |= DLADM_OPT_FORCE; 1123 break; 1124 case 'R': 1125 altroot = optarg; 1126 break; 1127 default: 1128 die_opterr(optopt, option, use); 1129 break; 1130 } 1131 } 1132 1133 if (ndev + nlink == 0) 1134 usage(); 1135 1136 /* get key value or the aggregation name (required last argument) */ 1137 if (optind != (argc-1)) 1138 usage(); 1139 1140 if ((status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, 1141 flags & (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST))) != 1142 DLADM_STATUS_OK) { 1143 goto done; 1144 } 1145 1146 if (altroot != NULL) 1147 altroot_cmd(altroot, argc, argv); 1148 1149 for (n = 0; n < ndev; n++) { 1150 if (dladm_dev2linkid(devs[n], &(port[n].lp_linkid)) != 1151 DLADM_STATUS_OK) { 1152 die("invalid <dev> '%s'", devs[n]); 1153 } 1154 } 1155 1156 for (n = 0; n < nlink; n++) { 1157 if (dladm_name2info(links[n], &port[n + ndev].lp_linkid, 1158 NULL, NULL, NULL) != DLADM_STATUS_OK) { 1159 die("invalid <link> '%s'", links[n]); 1160 } 1161 } 1162 1163 status = dladm_aggr_add(linkid, ndev + nlink, port, flags); 1164 done: 1165 if (status != DLADM_STATUS_OK) { 1166 /* 1167 * checking DLADM_STATUS_NOTSUP is a temporary workaround 1168 * and should be removed once 6399681 is fixed. 1169 */ 1170 if (status == DLADM_STATUS_NOTSUP) { 1171 (void) fprintf(stderr, 1172 gettext("%s: add operation failed: %s\n"), 1173 progname, 1174 gettext("link capabilities don't match")); 1175 exit(ENOTSUP); 1176 } else if (status == DLADM_STATUS_NONOTIF) { 1177 die_dlerr(status, "not all links have link up/down " 1178 "detection; must use -f (see dladm(1M))\n"); 1179 } else { 1180 die_dlerr(status, "add operation failed"); 1181 } 1182 } 1183 } 1184 1185 static void 1186 do_remove_aggr(int argc, char *argv[], const char *use) 1187 { 1188 char option; 1189 dladm_aggr_port_attr_db_t port[MAXPORT]; 1190 uint_t n, ndev, nlink; 1191 char *devs[MAXPORT]; 1192 char *links[MAXPORT]; 1193 char *altroot = NULL; 1194 uint32_t flags; 1195 datalink_id_t linkid; 1196 dladm_status_t status; 1197 1198 flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; 1199 ndev = nlink = opterr = 0; 1200 while ((option = getopt_long(argc, argv, ":d:l:R:t", 1201 lopts, NULL)) != -1) { 1202 switch (option) { 1203 case 'd': 1204 if (ndev + nlink >= MAXPORT) 1205 die("too many ports specified"); 1206 1207 devs[ndev++] = optarg; 1208 break; 1209 case 'l': 1210 if (ndev + nlink >= MAXPORT) 1211 die("too many ports specified"); 1212 1213 links[nlink++] = optarg; 1214 break; 1215 case 't': 1216 flags &= ~DLADM_OPT_PERSIST; 1217 break; 1218 case 'R': 1219 altroot = optarg; 1220 break; 1221 default: 1222 die_opterr(optopt, option, use); 1223 break; 1224 } 1225 } 1226 1227 if (ndev + nlink == 0) 1228 usage(); 1229 1230 /* get key value or the aggregation name (required last argument) */ 1231 if (optind != (argc-1)) 1232 usage(); 1233 1234 status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags); 1235 if (status != DLADM_STATUS_OK) 1236 goto done; 1237 1238 if (altroot != NULL) 1239 altroot_cmd(altroot, argc, argv); 1240 1241 for (n = 0; n < ndev; n++) { 1242 if (dladm_dev2linkid(devs[n], &(port[n].lp_linkid)) != 1243 DLADM_STATUS_OK) { 1244 die("invalid <dev> '%s'", devs[n]); 1245 } 1246 } 1247 1248 for (n = 0; n < nlink; n++) { 1249 if (dladm_name2info(links[n], &port[n + ndev].lp_linkid, 1250 NULL, NULL, NULL) != DLADM_STATUS_OK) { 1251 die("invalid <link> '%s'", links[n]); 1252 } 1253 } 1254 1255 status = dladm_aggr_remove(linkid, ndev + nlink, port, flags); 1256 done: 1257 if (status != DLADM_STATUS_OK) 1258 die_dlerr(status, "remove operation failed"); 1259 } 1260 1261 static void 1262 do_modify_aggr(int argc, char *argv[], const char *use) 1263 { 1264 char option; 1265 uint32_t policy = AGGR_POLICY_L4; 1266 aggr_lacp_mode_t lacp_mode = AGGR_LACP_OFF; 1267 aggr_lacp_timer_t lacp_timer = AGGR_LACP_TIMER_SHORT; 1268 uint8_t mac_addr[ETHERADDRL]; 1269 boolean_t mac_addr_fixed = B_FALSE; 1270 uint8_t modify_mask = 0; 1271 char *altroot = NULL; 1272 uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; 1273 datalink_id_t linkid; 1274 dladm_status_t status; 1275 1276 opterr = 0; 1277 while ((option = getopt_long(argc, argv, ":L:l:P:R:tu:T:", lopts, 1278 NULL)) != -1) { 1279 switch (option) { 1280 case 'P': 1281 if (modify_mask & DLADM_AGGR_MODIFY_POLICY) 1282 die_optdup(option); 1283 1284 modify_mask |= DLADM_AGGR_MODIFY_POLICY; 1285 1286 if (!dladm_aggr_str2policy(optarg, &policy)) 1287 die("invalid policy '%s'", optarg); 1288 break; 1289 case 'u': 1290 if (modify_mask & DLADM_AGGR_MODIFY_MAC) 1291 die_optdup(option); 1292 1293 modify_mask |= DLADM_AGGR_MODIFY_MAC; 1294 1295 if (!dladm_aggr_str2macaddr(optarg, &mac_addr_fixed, 1296 mac_addr)) 1297 die("invalid MAC address '%s'", optarg); 1298 break; 1299 case 'l': 1300 case 'L': 1301 if (modify_mask & DLADM_AGGR_MODIFY_LACP_MODE) 1302 die_optdup(option); 1303 1304 modify_mask |= DLADM_AGGR_MODIFY_LACP_MODE; 1305 1306 if (!dladm_aggr_str2lacpmode(optarg, &lacp_mode)) 1307 die("invalid LACP mode '%s'", optarg); 1308 break; 1309 case 'T': 1310 if (modify_mask & DLADM_AGGR_MODIFY_LACP_TIMER) 1311 die_optdup(option); 1312 1313 modify_mask |= DLADM_AGGR_MODIFY_LACP_TIMER; 1314 1315 if (!dladm_aggr_str2lacptimer(optarg, &lacp_timer)) 1316 die("invalid LACP timer value '%s'", optarg); 1317 break; 1318 case 't': 1319 flags &= ~DLADM_OPT_PERSIST; 1320 break; 1321 case 'R': 1322 altroot = optarg; 1323 break; 1324 default: 1325 die_opterr(optopt, option, use); 1326 break; 1327 } 1328 } 1329 1330 if (modify_mask == 0) 1331 die("at least one of the -PulT options must be specified"); 1332 1333 /* get key value or the aggregation name (required last argument) */ 1334 if (optind != (argc-1)) 1335 usage(); 1336 1337 status = i_dladm_aggr_get_linkid(altroot, argv[optind], &linkid, flags); 1338 if (status != DLADM_STATUS_OK) 1339 goto done; 1340 1341 if (altroot != NULL) 1342 altroot_cmd(altroot, argc, argv); 1343 1344 status = dladm_aggr_modify(linkid, modify_mask, policy, mac_addr_fixed, 1345 (const uchar_t *)mac_addr, lacp_mode, lacp_timer, flags); 1346 1347 done: 1348 if (status != DLADM_STATUS_OK) 1349 die_dlerr(status, "modify operation failed"); 1350 } 1351 1352 /*ARGSUSED*/ 1353 static void 1354 do_up_aggr(int argc, char *argv[], const char *use) 1355 { 1356 datalink_id_t linkid = DATALINK_ALL_LINKID; 1357 dladm_status_t status; 1358 1359 /* 1360 * get the key or the name of the aggregation (optional last argument) 1361 */ 1362 if (argc == 2) { 1363 if ((status = i_dladm_aggr_get_linkid(NULL, argv[1], &linkid, 1364 DLADM_OPT_PERSIST)) != DLADM_STATUS_OK) { 1365 goto done; 1366 } 1367 } else if (argc > 2) { 1368 usage(); 1369 } 1370 1371 status = dladm_aggr_up(linkid); 1372 done: 1373 if (status != DLADM_STATUS_OK) { 1374 if (argc == 2) { 1375 die_dlerr(status, 1376 "could not bring up aggregation '%s'", argv[1]); 1377 } else { 1378 die_dlerr(status, "could not bring aggregations up"); 1379 } 1380 } 1381 } 1382 1383 static void 1384 do_create_vlan(int argc, char *argv[], const char *use) 1385 { 1386 char *link = NULL; 1387 char drv[DLPI_LINKNAME_MAX]; 1388 uint_t ppa; 1389 datalink_id_t linkid; 1390 int vid = 0; 1391 char option; 1392 uint32_t flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); 1393 char *altroot = NULL; 1394 char vlan[MAXLINKNAMELEN]; 1395 dladm_status_t status; 1396 1397 opterr = 0; 1398 while ((option = getopt_long(argc, argv, ":tfl:v:", 1399 lopts, NULL)) != -1) { 1400 switch (option) { 1401 case 'v': 1402 if (vid != 0) 1403 die_optdup(option); 1404 1405 if (!str2int(optarg, &vid) || vid < 1 || vid > 4094) 1406 die("invalid VLAN identifier '%s'", optarg); 1407 1408 break; 1409 case 'l': 1410 if (link != NULL) 1411 die_optdup(option); 1412 1413 link = optarg; 1414 break; 1415 case 'f': 1416 flags |= DLADM_OPT_FORCE; 1417 break; 1418 case 't': 1419 flags &= ~DLADM_OPT_PERSIST; 1420 break; 1421 case 'R': 1422 altroot = optarg; 1423 break; 1424 default: 1425 die_opterr(optopt, option, use); 1426 break; 1427 } 1428 } 1429 1430 /* get vlan name if there is any */ 1431 if ((vid == 0) || (link == NULL) || (argc - optind > 1)) 1432 usage(); 1433 1434 if (optind == (argc - 1)) { 1435 if (strlcpy(vlan, argv[optind], MAXLINKNAMELEN) >= 1436 MAXLINKNAMELEN) { 1437 die("vlan name too long '%s'", argv[optind]); 1438 } 1439 } else { 1440 if ((dlpi_parselink(link, drv, &ppa) != DLPI_SUCCESS) || 1441 (ppa >= 1000) || 1442 (dlpi_makelink(vlan, drv, vid * 1000 + ppa) != 1443 DLPI_SUCCESS)) { 1444 die("invalid link name '%s'", link); 1445 } 1446 } 1447 1448 if (altroot != NULL) 1449 altroot_cmd(altroot, argc, argv); 1450 1451 if (dladm_name2info(link, &linkid, NULL, NULL, NULL) != 1452 DLADM_STATUS_OK) { 1453 die("invalid link name '%s'", link); 1454 } 1455 1456 if ((status = dladm_vlan_create(vlan, linkid, vid, flags)) != 1457 DLADM_STATUS_OK) { 1458 if (status == DLADM_STATUS_NOTSUP) { 1459 die_dlerr(status, "VLAN over '%s' may require lowered " 1460 "MTU; must use -f (see dladm(1M))\n", link); 1461 } else { 1462 die_dlerr(status, "create operation failed"); 1463 } 1464 } 1465 } 1466 1467 static void 1468 do_delete_vlan(int argc, char *argv[], const char *use) 1469 { 1470 char option; 1471 uint32_t flags = (DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); 1472 char *altroot = NULL; 1473 datalink_id_t linkid; 1474 dladm_status_t status; 1475 1476 opterr = 0; 1477 while ((option = getopt_long(argc, argv, ":R:t", lopts, NULL)) != -1) { 1478 switch (option) { 1479 case 't': 1480 flags &= ~DLADM_OPT_PERSIST; 1481 break; 1482 case 'R': 1483 altroot = optarg; 1484 break; 1485 default: 1486 die_opterr(optopt, option, use); 1487 break; 1488 } 1489 } 1490 1491 /* get VLAN link name (required last argument) */ 1492 if (optind != (argc - 1)) 1493 usage(); 1494 1495 if (altroot != NULL) 1496 altroot_cmd(altroot, argc, argv); 1497 1498 status = dladm_name2info(argv[optind], &linkid, NULL, NULL, NULL); 1499 if (status != DLADM_STATUS_OK) 1500 goto done; 1501 1502 status = dladm_vlan_delete(linkid, flags); 1503 done: 1504 if (status != DLADM_STATUS_OK) 1505 die_dlerr(status, "delete operation failed"); 1506 } 1507 1508 /*ARGSUSED*/ 1509 static void 1510 do_up_vlan(int argc, char *argv[], const char *use) 1511 { 1512 datalink_id_t linkid = DATALINK_ALL_LINKID; 1513 dladm_status_t status; 1514 1515 /* 1516 * get the name of the VLAN (optional last argument) 1517 */ 1518 if (argc > 2) 1519 usage(); 1520 1521 if (argc == 2) { 1522 status = dladm_name2info(argv[1], &linkid, NULL, NULL, NULL); 1523 if (status != DLADM_STATUS_OK) 1524 goto done; 1525 } 1526 1527 status = dladm_vlan_up(linkid); 1528 done: 1529 if (status != DLADM_STATUS_OK) { 1530 if (argc == 2) { 1531 die_dlerr(status, 1532 "could not bring up VLAN '%s'", argv[1]); 1533 } else { 1534 die_dlerr(status, "could not bring VLANs up"); 1535 } 1536 } 1537 } 1538 1539 static void 1540 do_rename_link(int argc, char *argv[], const char *use) 1541 { 1542 char option; 1543 char *link1, *link2; 1544 char *altroot = NULL; 1545 dladm_status_t status; 1546 1547 opterr = 0; 1548 while ((option = getopt_long(argc, argv, ":R:", lopts, NULL)) != -1) { 1549 switch (option) { 1550 case 'R': 1551 altroot = optarg; 1552 break; 1553 default: 1554 die_opterr(optopt, option, use); 1555 break; 1556 } 1557 } 1558 1559 /* get link1 and link2 name (required the last 2 arguments) */ 1560 if (optind != (argc - 2)) 1561 usage(); 1562 1563 if (altroot != NULL) 1564 altroot_cmd(altroot, argc, argv); 1565 1566 link1 = argv[optind++]; 1567 link2 = argv[optind]; 1568 if ((status = dladm_rename_link(link1, link2)) != DLADM_STATUS_OK) 1569 die_dlerr(status, "rename operation failed"); 1570 } 1571 1572 /*ARGSUSED*/ 1573 static void 1574 do_delete_phys(int argc, char *argv[], const char *use) 1575 { 1576 datalink_id_t linkid = DATALINK_ALL_LINKID; 1577 dladm_status_t status; 1578 1579 /* get link name (required the last argument) */ 1580 if (argc > 2) 1581 usage(); 1582 1583 if (argc == 2) { 1584 status = dladm_name2info(argv[1], &linkid, NULL, NULL, NULL); 1585 if (status != DLADM_STATUS_OK) 1586 die_dlerr(status, "cannot delete '%s'", argv[1]); 1587 } 1588 1589 if ((status = dladm_phys_delete(linkid)) != DLADM_STATUS_OK) { 1590 if (argc == 2) 1591 die_dlerr(status, "cannot delete '%s'", argv[1]); 1592 else 1593 die_dlerr(status, "delete operation failed"); 1594 } 1595 } 1596 1597 /*ARGSUSED*/ 1598 static int 1599 i_dladm_walk_linkmap(datalink_id_t linkid, void *arg) 1600 { 1601 char name[MAXLINKNAMELEN]; 1602 char mediabuf[DLADM_STRSIZE]; 1603 char classbuf[DLADM_STRSIZE]; 1604 datalink_class_t class; 1605 uint32_t media; 1606 uint32_t flags; 1607 1608 if (dladm_datalink_id2info(linkid, &flags, &class, &media, name, 1609 MAXLINKNAMELEN) == DLADM_STATUS_OK) { 1610 (void) dladm_class2str(class, classbuf); 1611 (void) dladm_media2str(media, mediabuf); 1612 (void) printf("%-12s%8d %-12s%-20s %6d\n", name, 1613 linkid, classbuf, mediabuf, flags); 1614 } 1615 return (DLADM_WALK_CONTINUE); 1616 } 1617 1618 /*ARGSUSED*/ 1619 static void 1620 do_show_linkmap(int argc, char *argv[], const char *use) 1621 { 1622 if (argc != 1) 1623 die("invalid arguments"); 1624 1625 (void) printf("%-12s%8s %-12s%-20s %6s\n", "NAME", "LINKID", 1626 "CLASS", "MEDIA", "FLAGS"); 1627 (void) dladm_walk_datalink_id(i_dladm_walk_linkmap, NULL, 1628 DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1629 DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); 1630 } 1631 1632 /* 1633 * Delete inactive physical links. 1634 */ 1635 /*ARGSUSED*/ 1636 static int 1637 purge_phys(datalink_id_t linkid, void *arg) 1638 { 1639 datalink_class_t class; 1640 uint32_t flags; 1641 1642 if (dladm_datalink_id2info(linkid, &flags, &class, NULL, 1643 NULL, 0) != DLADM_STATUS_OK) { 1644 return (DLADM_WALK_CONTINUE); 1645 } 1646 1647 if (class == DATALINK_CLASS_PHYS && !(flags & DLADM_OPT_ACTIVE)) 1648 (void) dladm_phys_delete(linkid); 1649 1650 return (DLADM_WALK_CONTINUE); 1651 } 1652 1653 /*ARGSUSED*/ 1654 static void 1655 do_init_phys(int argc, char *argv[], const char *use) 1656 { 1657 di_node_t devtree; 1658 1659 if (argc > 1) 1660 usage(); 1661 1662 /* 1663 * Force all the devices to attach, therefore all the network physical 1664 * devices can be known to the dlmgmtd daemon. 1665 */ 1666 if ((devtree = di_init("/", DINFOFORCE | DINFOSUBTREE)) != DI_NODE_NIL) 1667 di_fini(devtree); 1668 1669 (void) dladm_walk_datalink_id(purge_phys, NULL, 1670 DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST); 1671 } 1672 1673 1674 /* 1675 * Print the active topology information. 1676 */ 1677 static dladm_status_t 1678 print_link_topology(show_state_t *state, datalink_id_t linkid, 1679 datalink_class_t class, link_fields_buf_t *lbuf) 1680 { 1681 uint32_t flags = state->ls_flags; 1682 dladm_status_t status = DLADM_STATUS_OK; 1683 char tmpbuf[MAXLINKNAMELEN]; 1684 1685 if (!state->ls_parseable) 1686 (void) sprintf(lbuf->link_over, STR_UNDEF_VAL); 1687 else 1688 (void) sprintf(lbuf->link_over, ""); 1689 1690 if (class == DATALINK_CLASS_VLAN) { 1691 dladm_vlan_attr_t vinfo; 1692 1693 status = dladm_vlan_info(linkid, &vinfo, flags); 1694 if (status != DLADM_STATUS_OK) 1695 goto done; 1696 status = dladm_datalink_id2info(vinfo.dv_linkid, NULL, NULL, 1697 NULL, lbuf->link_over, sizeof (lbuf->link_over)); 1698 if (status != DLADM_STATUS_OK) 1699 goto done; 1700 } else if (class == DATALINK_CLASS_AGGR) { 1701 dladm_aggr_grp_attr_t ginfo; 1702 int i; 1703 1704 (void) sprintf(lbuf->link_over, ""); 1705 1706 status = dladm_aggr_info(linkid, &ginfo, flags); 1707 if (status != DLADM_STATUS_OK) 1708 goto done; 1709 1710 if (ginfo.lg_nports == 0) { 1711 status = DLADM_STATUS_BADVAL; 1712 goto done; 1713 } 1714 for (i = 0; i < ginfo.lg_nports; i++) { 1715 status = dladm_datalink_id2info( 1716 ginfo.lg_ports[i].lp_linkid, NULL, NULL, NULL, 1717 tmpbuf, sizeof (tmpbuf)); 1718 if (status != DLADM_STATUS_OK) { 1719 free(ginfo.lg_ports); 1720 goto done; 1721 } 1722 (void) strlcat(lbuf->link_over, tmpbuf, 1723 sizeof (lbuf->link_over)); 1724 if (i != (ginfo.lg_nports - 1)) { 1725 (void) strlcat(lbuf->link_over, " ", 1726 sizeof (lbuf->link_over)); 1727 } 1728 } 1729 free(ginfo.lg_ports); 1730 } else if (class == DATALINK_CLASS_VNIC) { 1731 dladm_vnic_attr_sys_t vinfo; 1732 1733 if ((status = dladm_vnic_info(linkid, &vinfo, flags)) != 1734 DLADM_STATUS_OK || (status = dladm_datalink_id2info( 1735 vinfo.va_link_id, NULL, NULL, NULL, lbuf->link_over, 1736 sizeof (lbuf->link_over)) != DLADM_STATUS_OK)) { 1737 goto done; 1738 } 1739 } 1740 done: 1741 return (status); 1742 } 1743 1744 static dladm_status_t 1745 print_link(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *lbuf) 1746 { 1747 char link[MAXLINKNAMELEN]; 1748 datalink_class_t class; 1749 uint_t mtu; 1750 uint32_t flags; 1751 dladm_status_t status; 1752 1753 if ((status = dladm_datalink_id2info(linkid, &flags, &class, NULL, 1754 link, sizeof (link))) != DLADM_STATUS_OK) { 1755 goto done; 1756 } 1757 1758 if (!(state->ls_flags & flags)) { 1759 status = DLADM_STATUS_NOTFOUND; 1760 goto done; 1761 } 1762 1763 if (state->ls_flags == DLADM_OPT_ACTIVE) { 1764 dladm_attr_t dlattr; 1765 1766 if (class == DATALINK_CLASS_PHYS) { 1767 dladm_phys_attr_t dpa; 1768 dlpi_handle_t dh; 1769 dlpi_info_t dlinfo; 1770 1771 if ((status = dladm_phys_info(linkid, &dpa, 1772 DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) { 1773 goto done; 1774 } 1775 1776 if (!dpa.dp_novanity) 1777 goto link_mtu; 1778 1779 /* 1780 * This is a physical link that does not have 1781 * vanity naming support. 1782 */ 1783 if (dlpi_open(dpa.dp_dev, &dh, DLPI_DEVONLY) != 1784 DLPI_SUCCESS) { 1785 status = DLADM_STATUS_NOTFOUND; 1786 goto done; 1787 } 1788 1789 if (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS) { 1790 dlpi_close(dh); 1791 status = DLADM_STATUS_BADARG; 1792 goto done; 1793 } 1794 1795 dlpi_close(dh); 1796 mtu = dlinfo.di_max_sdu; 1797 } else { 1798 link_mtu: 1799 status = dladm_info(linkid, &dlattr); 1800 if (status != DLADM_STATUS_OK) 1801 goto done; 1802 mtu = dlattr.da_max_sdu; 1803 } 1804 } 1805 1806 (void) snprintf(lbuf->link_name, sizeof (lbuf->link_name), 1807 "%s", link); 1808 (void) dladm_class2str(class, lbuf->link_class); 1809 if (state->ls_flags == DLADM_OPT_ACTIVE) { 1810 (void) snprintf(lbuf->link_mtu, sizeof (lbuf->link_mtu), 1811 "%u", mtu); 1812 (void) get_linkstate(link, B_TRUE, lbuf->link_state); 1813 } 1814 1815 status = print_link_topology(state, linkid, class, lbuf); 1816 if (status != DLADM_STATUS_OK) 1817 goto done; 1818 1819 done: 1820 return (status); 1821 } 1822 1823 1824 static int 1825 show_link(datalink_id_t linkid, void *arg) 1826 { 1827 show_state_t *state = (show_state_t *)arg; 1828 dladm_status_t status; 1829 link_fields_buf_t lbuf; 1830 1831 /* 1832 * first get all the link attributes into lbuf; 1833 */ 1834 status = print_link(state, linkid, &lbuf); 1835 1836 if (status != DLADM_STATUS_OK) 1837 goto done; 1838 1839 if (!state->ls_parseable && !state->ls_printheader) { 1840 print_header(&state->ls_print); 1841 state->ls_printheader = B_TRUE; 1842 } 1843 1844 dladm_print_output(&state->ls_print, state->ls_parseable, 1845 dladm_print_field, (void *)&lbuf); 1846 1847 done: 1848 state->ls_status = status; 1849 return (DLADM_WALK_CONTINUE); 1850 } 1851 1852 static int 1853 show_link_stats(datalink_id_t linkid, void *arg) 1854 { 1855 char link[DLPI_LINKNAME_MAX]; 1856 datalink_class_t class; 1857 show_state_t *state = (show_state_t *)arg; 1858 pktsum_t stats, diff_stats; 1859 dladm_phys_attr_t dpa; 1860 1861 if (state->ls_firstonly) { 1862 if (state->ls_donefirst) 1863 return (DLADM_WALK_CONTINUE); 1864 state->ls_donefirst = B_TRUE; 1865 } else { 1866 bzero(&state->ls_prevstats, sizeof (state->ls_prevstats)); 1867 } 1868 1869 if (dladm_datalink_id2info(linkid, NULL, &class, NULL, link, 1870 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) { 1871 return (DLADM_WALK_CONTINUE); 1872 } 1873 1874 if (class == DATALINK_CLASS_PHYS) { 1875 if (dladm_phys_info(linkid, &dpa, DLADM_OPT_ACTIVE) != 1876 DLADM_STATUS_OK) { 1877 return (DLADM_WALK_CONTINUE); 1878 } 1879 if (dpa.dp_novanity) 1880 get_mac_stats(dpa.dp_dev, &stats); 1881 else 1882 get_link_stats(link, &stats); 1883 } else { 1884 get_link_stats(link, &stats); 1885 } 1886 stats_diff(&diff_stats, &stats, &state->ls_prevstats); 1887 1888 (void) printf("%-12s", link); 1889 (void) printf("%-10llu", diff_stats.ipackets); 1890 (void) printf("%-12llu", diff_stats.rbytes); 1891 (void) printf("%-8u", diff_stats.ierrors); 1892 (void) printf("%-10llu", diff_stats.opackets); 1893 (void) printf("%-12llu", diff_stats.obytes); 1894 (void) printf("%-8u\n", diff_stats.oerrors); 1895 1896 state->ls_prevstats = stats; 1897 return (DLADM_WALK_CONTINUE); 1898 } 1899 1900 1901 static dladm_status_t 1902 print_aggr_info(show_grp_state_t *state, const char *link, 1903 dladm_aggr_grp_attr_t *ginfop) 1904 { 1905 char addr_str[ETHERADDRL * 3]; 1906 laggr_fields_buf_t lbuf; 1907 1908 (void) snprintf(lbuf.laggr_name, sizeof (lbuf.laggr_name), 1909 "%s", link); 1910 1911 (void) dladm_aggr_policy2str(ginfop->lg_policy, 1912 lbuf.laggr_policy); 1913 1914 if (ginfop->lg_mac_fixed) { 1915 (void) dladm_aggr_macaddr2str(ginfop->lg_mac, addr_str); 1916 (void) snprintf(lbuf.laggr_addrpolicy, 1917 sizeof (lbuf.laggr_addrpolicy), "fixed (%s)", addr_str); 1918 } else { 1919 (void) snprintf(lbuf.laggr_addrpolicy, 1920 sizeof (lbuf.laggr_addrpolicy), "auto"); 1921 } 1922 1923 1924 (void) dladm_aggr_lacpmode2str(ginfop->lg_lacp_mode, 1925 lbuf.laggr_lacpactivity); 1926 (void) dladm_aggr_lacptimer2str(ginfop->lg_lacp_timer, 1927 lbuf.laggr_lacptimer); 1928 (void) snprintf(lbuf.laggr_flags, sizeof (lbuf.laggr_flags), "%c----", 1929 ginfop->lg_force ? 'f' : '-'); 1930 1931 if (!state->gs_parseable && !state->gs_printheader) { 1932 print_header(&state->gs_print); 1933 state->gs_printheader = B_TRUE; 1934 } 1935 1936 dladm_print_output(&state->gs_print, state->gs_parseable, 1937 dladm_print_field, (void *)&lbuf); 1938 1939 return (DLADM_STATUS_OK); 1940 } 1941 1942 static char * 1943 print_xaggr_callback(print_field_t *pf, void *arg) 1944 { 1945 const laggr_args_t *l = arg; 1946 int portnum; 1947 static char buf[DLADM_STRSIZE]; 1948 boolean_t is_port = (l->laggr_lport >= 0); 1949 dladm_aggr_port_attr_t *portp; 1950 dladm_phys_attr_t dpa; 1951 dladm_status_t *stat, status; 1952 1953 stat = l->laggr_status; 1954 *stat = DLADM_STATUS_OK; 1955 1956 if (is_port) { 1957 portnum = l->laggr_lport; 1958 portp = &(l->laggr_ginfop->lg_ports[portnum]); 1959 if ((status = dladm_datalink_id2info(portp->lp_linkid, 1960 NULL, NULL, NULL, buf, sizeof (buf))) != 1961 DLADM_STATUS_OK) { 1962 goto err; 1963 } 1964 if ((status = dladm_phys_info(portp->lp_linkid, &dpa, 1965 DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) { 1966 goto err; 1967 } 1968 } 1969 1970 switch (pf->pf_index) { 1971 case AGGR_X_LINK: 1972 (void) snprintf(buf, sizeof (buf), "%s", 1973 (is_port && !l->laggr_parseable ? " " : l->laggr_link)); 1974 break; 1975 case AGGR_X_PORT: 1976 if (is_port) 1977 break; 1978 return (""); 1979 break; 1980 1981 case AGGR_X_SPEED: 1982 if (is_port) { 1983 (void) snprintf(buf, sizeof (buf), "%uMb", 1984 (uint_t)((get_ifspeed(dpa.dp_dev, 1985 B_FALSE)) / 1000000ull)); 1986 } else { 1987 (void) snprintf(buf, sizeof (buf), "%uMb", 1988 (uint_t)((get_ifspeed(l->laggr_link, 1989 B_TRUE)) / 1000000ull)); 1990 } 1991 break; 1992 1993 case AGGR_X_DUPLEX: 1994 if (is_port) 1995 (void) get_linkduplex(dpa.dp_dev, B_FALSE, buf); 1996 else 1997 (void) get_linkduplex(l->laggr_link, B_TRUE, buf); 1998 break; 1999 2000 case AGGR_X_STATE: 2001 if (is_port) { 2002 (void) dladm_aggr_portstate2str( 2003 portp->lp_state, buf); 2004 } else { 2005 return (""); 2006 } 2007 break; 2008 case AGGR_X_ADDRESS: 2009 (void) dladm_aggr_macaddr2str( 2010 (is_port ? portp->lp_mac : l->laggr_ginfop->lg_mac), 2011 buf); 2012 break; 2013 case AGGR_X_PORTSTATE: 2014 if (is_port) 2015 (void) dladm_aggr_portstate2str( 2016 portp->lp_state, buf); 2017 else 2018 return (""); 2019 break; 2020 } 2021 return (buf); 2022 2023 err: 2024 *stat = status; 2025 buf[0] = '\0'; 2026 return (buf); 2027 } 2028 2029 static dladm_status_t 2030 print_aggr_extended(show_grp_state_t *state, const char *link, 2031 dladm_aggr_grp_attr_t *ginfop) 2032 { 2033 int i; 2034 dladm_status_t status; 2035 laggr_args_t largs; 2036 2037 if (!state->gs_parseable && !state->gs_printheader) { 2038 print_header(&state->gs_print); 2039 state->gs_printheader = B_TRUE; 2040 } 2041 2042 largs.laggr_lport = -1; 2043 largs.laggr_link = link; 2044 largs.laggr_ginfop = ginfop; 2045 largs.laggr_status = &status; 2046 largs.laggr_parseable = state->gs_parseable; 2047 2048 dladm_print_output(&state->gs_print, state->gs_parseable, 2049 print_xaggr_callback, &largs); 2050 2051 if (status != DLADM_STATUS_OK) 2052 goto done; 2053 2054 for (i = 0; i < ginfop->lg_nports; i++) { 2055 largs.laggr_lport = i; 2056 dladm_print_output(&state->gs_print, state->gs_parseable, 2057 print_xaggr_callback, &largs); 2058 if (status != DLADM_STATUS_OK) 2059 goto done; 2060 } 2061 2062 status = DLADM_STATUS_OK; 2063 done: 2064 return (status); 2065 } 2066 2067 2068 static char * 2069 print_lacp_callback(print_field_t *pf, void *arg) 2070 { 2071 const laggr_args_t *l = arg; 2072 int portnum; 2073 static char buf[DLADM_STRSIZE]; 2074 boolean_t is_port = (l->laggr_lport >= 0); 2075 dladm_aggr_port_attr_t *portp; 2076 dladm_status_t *stat, status; 2077 aggr_lacp_state_t *lstate; 2078 2079 if (!is_port) { 2080 return (NULL); /* cannot happen! */ 2081 } 2082 2083 stat = l->laggr_status; 2084 2085 portnum = l->laggr_lport; 2086 portp = &(l->laggr_ginfop->lg_ports[portnum]); 2087 if ((status = dladm_datalink_id2info(portp->lp_linkid, 2088 NULL, NULL, NULL, buf, sizeof (buf))) != DLADM_STATUS_OK) { 2089 goto err; 2090 } 2091 lstate = &(portp->lp_lacp_state); 2092 2093 switch (pf->pf_index) { 2094 case AGGR_L_LINK: 2095 (void) snprintf(buf, sizeof (buf), "%s", 2096 (portnum > 0 ? "" : l->laggr_link)); 2097 break; 2098 2099 case AGGR_L_PORT: 2100 break; 2101 2102 case AGGR_L_AGGREGATABLE: 2103 (void) snprintf(buf, sizeof (buf), "%s", 2104 (lstate->bit.aggregation ? "yes" : "no")); 2105 break; 2106 2107 case AGGR_L_SYNC: 2108 (void) snprintf(buf, sizeof (buf), "%s", 2109 (lstate->bit.sync ? "yes" : "no")); 2110 break; 2111 2112 case AGGR_L_COLL: 2113 (void) snprintf(buf, sizeof (buf), "%s", 2114 (lstate->bit.collecting ? "yes" : "no")); 2115 break; 2116 2117 case AGGR_L_DIST: 2118 (void) snprintf(buf, sizeof (buf), "%s", 2119 (lstate->bit.distributing ? "yes" : "no")); 2120 break; 2121 2122 case AGGR_L_DEFAULTED: 2123 (void) snprintf(buf, sizeof (buf), "%s", 2124 (lstate->bit.defaulted ? "yes" : "no")); 2125 break; 2126 2127 case AGGR_L_EXPIRED: 2128 (void) snprintf(buf, sizeof (buf), "%s", 2129 (lstate->bit.expired ? "yes" : "no")); 2130 break; 2131 } 2132 2133 *stat = DLADM_STATUS_OK; 2134 return (buf); 2135 2136 err: 2137 *stat = status; 2138 buf[0] = '\0'; 2139 return (buf); 2140 } 2141 2142 static dladm_status_t 2143 print_aggr_lacp(show_grp_state_t *state, const char *link, 2144 dladm_aggr_grp_attr_t *ginfop) 2145 { 2146 int i; 2147 dladm_status_t status; 2148 laggr_args_t largs; 2149 2150 if (!state->gs_parseable && !state->gs_printheader) { 2151 print_header(&state->gs_print); 2152 state->gs_printheader = B_TRUE; 2153 } 2154 2155 largs.laggr_link = link; 2156 largs.laggr_ginfop = ginfop; 2157 largs.laggr_status = &status; 2158 2159 for (i = 0; i < ginfop->lg_nports; i++) { 2160 largs.laggr_lport = i; 2161 dladm_print_output(&state->gs_print, state->gs_parseable, 2162 print_lacp_callback, &largs); 2163 if (status != DLADM_STATUS_OK) 2164 goto done; 2165 } 2166 2167 status = DLADM_STATUS_OK; 2168 done: 2169 return (status); 2170 } 2171 2172 static char * 2173 print_aggr_stats_callback(print_field_t *pf, void *arg) 2174 { 2175 const laggr_args_t *l = arg; 2176 int portnum; 2177 static char buf[DLADM_STRSIZE]; 2178 boolean_t is_port = (l->laggr_lport >= 0); 2179 dladm_aggr_port_attr_t *portp; 2180 dladm_phys_attr_t dpa; 2181 dladm_status_t *stat, status; 2182 pktsum_t port_stat, diff_stats; 2183 2184 stat = l->laggr_status; 2185 *stat = DLADM_STATUS_OK; 2186 2187 if (is_port) { 2188 portnum = l->laggr_lport; 2189 portp = &(l->laggr_ginfop->lg_ports[portnum]); 2190 if ((status = dladm_phys_info(portp->lp_linkid, &dpa, 2191 DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) { 2192 goto err; 2193 } 2194 2195 get_mac_stats(dpa.dp_dev, &port_stat); 2196 2197 if ((status = dladm_datalink_id2info(portp->lp_linkid, NULL, 2198 NULL, NULL, buf, sizeof (buf))) != DLADM_STATUS_OK) { 2199 goto err; 2200 } 2201 2202 stats_diff(&diff_stats, &port_stat, l->laggr_prevstats); 2203 } 2204 2205 switch (pf->pf_index) { 2206 case AGGR_S_LINK: 2207 (void) snprintf(buf, sizeof (buf), "%s", 2208 (is_port ? "" : l->laggr_link)); 2209 break; 2210 case AGGR_S_PORT: 2211 if (is_port) 2212 break; 2213 return (""); 2214 break; 2215 2216 case AGGR_S_IPKTS: 2217 if (is_port) { 2218 (void) snprintf(buf, sizeof (buf), "%llu", 2219 diff_stats.ipackets); 2220 } else { 2221 (void) snprintf(buf, sizeof (buf), "%llu", 2222 l->laggr_pktsumtot->ipackets); 2223 } 2224 break; 2225 2226 case AGGR_S_RBYTES: 2227 if (is_port) { 2228 (void) snprintf(buf, sizeof (buf), "%llu", 2229 diff_stats.rbytes); 2230 } else { 2231 (void) snprintf(buf, sizeof (buf), "%llu", 2232 l->laggr_pktsumtot->rbytes); 2233 } 2234 break; 2235 2236 case AGGR_S_OPKTS: 2237 if (is_port) { 2238 (void) snprintf(buf, sizeof (buf), "%llu", 2239 diff_stats.opackets); 2240 } else { 2241 (void) snprintf(buf, sizeof (buf), "%llu", 2242 l->laggr_pktsumtot->opackets); 2243 } 2244 break; 2245 case AGGR_S_OBYTES: 2246 if (is_port) { 2247 (void) snprintf(buf, sizeof (buf), "%llu", 2248 diff_stats.obytes); 2249 } else { 2250 (void) snprintf(buf, sizeof (buf), "%llu", 2251 l->laggr_pktsumtot->obytes); 2252 2253 } 2254 break; 2255 2256 case AGGR_S_IPKTDIST: 2257 if (is_port) { 2258 (void) snprintf(buf, sizeof (buf), "%-6.1f", 2259 (double)diff_stats.opackets/ 2260 (double)l->laggr_pktsumtot->ipackets * 100); 2261 } else { 2262 return (""); 2263 } 2264 break; 2265 case AGGR_S_OPKTDIST: 2266 if (is_port) { 2267 (void) snprintf(buf, sizeof (buf), "%-6.1f", 2268 (double)diff_stats.opackets/ 2269 (double)l->laggr_pktsumtot->opackets * 100); 2270 } else { 2271 return (""); 2272 } 2273 break; 2274 } 2275 return (buf); 2276 2277 err: 2278 *stat = status; 2279 buf[0] = '\0'; 2280 return (buf); 2281 } 2282 2283 static dladm_status_t 2284 print_aggr_stats(show_grp_state_t *state, const char *link, 2285 dladm_aggr_grp_attr_t *ginfop) 2286 { 2287 dladm_phys_attr_t dpa; 2288 dladm_aggr_port_attr_t *portp; 2289 pktsum_t pktsumtot, port_stat; 2290 dladm_status_t status; 2291 int i; 2292 laggr_args_t largs; 2293 2294 /* sum the ports statistics */ 2295 bzero(&pktsumtot, sizeof (pktsumtot)); 2296 2297 for (i = 0; i < ginfop->lg_nports; i++) { 2298 2299 portp = &(ginfop->lg_ports[i]); 2300 if ((status = dladm_phys_info(portp->lp_linkid, &dpa, 2301 DLADM_OPT_ACTIVE)) != DLADM_STATUS_OK) { 2302 goto done; 2303 } 2304 2305 get_mac_stats(dpa.dp_dev, &port_stat); 2306 stats_total(&pktsumtot, &port_stat, &state->gs_prevstats[i]); 2307 } 2308 2309 if (!state->gs_parseable && !state->gs_printheader) { 2310 print_header(&state->gs_print); 2311 state->gs_printheader = B_TRUE; 2312 } 2313 2314 largs.laggr_lport = -1; 2315 largs.laggr_link = link; 2316 largs.laggr_ginfop = ginfop; 2317 largs.laggr_status = &status; 2318 largs.laggr_pktsumtot = &pktsumtot; 2319 2320 dladm_print_output(&state->gs_print, state->gs_parseable, 2321 print_aggr_stats_callback, &largs); 2322 2323 if (status != DLADM_STATUS_OK) 2324 goto done; 2325 2326 for (i = 0; i < ginfop->lg_nports; i++) { 2327 largs.laggr_lport = i; 2328 largs.laggr_prevstats = &state->gs_prevstats[i]; 2329 dladm_print_output(&state->gs_print, state->gs_parseable, 2330 print_aggr_stats_callback, &largs); 2331 if (status != DLADM_STATUS_OK) 2332 goto done; 2333 } 2334 2335 status = DLADM_STATUS_OK; 2336 done: 2337 return (status); 2338 } 2339 2340 static dladm_status_t 2341 print_aggr(show_grp_state_t *state, datalink_id_t linkid) 2342 { 2343 char link[MAXLINKNAMELEN]; 2344 dladm_aggr_grp_attr_t ginfo; 2345 uint32_t flags; 2346 dladm_status_t status; 2347 2348 if ((status = dladm_datalink_id2info(linkid, &flags, NULL, NULL, link, 2349 MAXLINKNAMELEN)) != DLADM_STATUS_OK) { 2350 return (status); 2351 } 2352 2353 if (!(state->gs_flags & flags)) 2354 return (DLADM_STATUS_NOTFOUND); 2355 2356 status = dladm_aggr_info(linkid, &ginfo, state->gs_flags); 2357 if (status != DLADM_STATUS_OK) 2358 return (status); 2359 2360 if (state->gs_lacp) 2361 status = print_aggr_lacp(state, link, &ginfo); 2362 else if (state->gs_extended) 2363 status = print_aggr_extended(state, link, &ginfo); 2364 else if (state->gs_stats) 2365 status = print_aggr_stats(state, link, &ginfo); 2366 else { 2367 status = print_aggr_info(state, link, &ginfo); 2368 } 2369 2370 done: 2371 free(ginfo.lg_ports); 2372 return (status); 2373 } 2374 2375 static int 2376 show_aggr(datalink_id_t linkid, void *arg) 2377 { 2378 show_grp_state_t *state = arg; 2379 dladm_status_t status; 2380 2381 status = print_aggr(state, linkid); 2382 if (status != DLADM_STATUS_OK) 2383 goto done; 2384 2385 done: 2386 state->gs_status = status; 2387 return (DLADM_WALK_CONTINUE); 2388 } 2389 2390 static char * 2391 print_dev(print_field_t *pf, void *arg) 2392 { 2393 const char *dev = arg; 2394 static char buf[DLADM_STRSIZE]; 2395 2396 switch (pf->pf_index) { 2397 case DEV_LINK: 2398 (void) snprintf(buf, sizeof (buf), "%s", dev); 2399 break; 2400 case DEV_STATE: 2401 (void) get_linkstate(dev, B_FALSE, buf); 2402 break; 2403 case DEV_SPEED: 2404 (void) snprintf(buf, sizeof (buf), "%uMb", 2405 (unsigned int)(get_ifspeed(dev, B_FALSE) / 1000000ull)); 2406 break; 2407 case DEV_DUPLEX: 2408 (void) get_linkduplex(dev, B_FALSE, buf); 2409 break; 2410 default: 2411 die("invalid index '%d'", pf->pf_index); 2412 break; 2413 } 2414 return (buf); 2415 } 2416 2417 static int 2418 show_dev(const char *dev, void *arg) 2419 { 2420 show_state_t *state = arg; 2421 2422 if (!state->ls_parseable && !state->ls_printheader) { 2423 print_header(&state->ls_print); 2424 state->ls_printheader = B_TRUE; 2425 } 2426 2427 dladm_print_output(&state->ls_print, state->ls_parseable, 2428 print_dev, (void *)dev); 2429 2430 return (DLADM_WALK_CONTINUE); 2431 } 2432 2433 static char * 2434 print_dev_stats(print_field_t *pf, void *arg) 2435 { 2436 dev_args_t *dargs = arg; 2437 pktsum_t *diff_stats = dargs->devs_psum; 2438 static char buf[DLADM_STRSIZE]; 2439 2440 switch (pf->pf_index) { 2441 case DEVS_LINK: 2442 (void) snprintf(buf, sizeof (buf), "%s", dargs->devs_link); 2443 break; 2444 case DEVS_IPKTS: 2445 (void) snprintf(buf, sizeof (buf), "%llu", 2446 diff_stats->ipackets); 2447 break; 2448 case DEVS_RBYTES: 2449 (void) snprintf(buf, sizeof (buf), "%llu", 2450 diff_stats->rbytes); 2451 break; 2452 case DEVS_IERRORS: 2453 (void) snprintf(buf, sizeof (buf), "%u", 2454 diff_stats->ierrors); 2455 break; 2456 case DEVS_OPKTS: 2457 (void) snprintf(buf, sizeof (buf), "%llu", 2458 diff_stats->opackets); 2459 break; 2460 case DEVS_OBYTES: 2461 (void) snprintf(buf, sizeof (buf), "%llu", 2462 diff_stats->obytes); 2463 break; 2464 case DEVS_OERRORS: 2465 (void) snprintf(buf, sizeof (buf), "%u", 2466 diff_stats->oerrors); 2467 break; 2468 default: 2469 die("invalid input"); 2470 break; 2471 } 2472 return (buf); 2473 } 2474 2475 static int 2476 show_dev_stats(const char *dev, void *arg) 2477 { 2478 show_state_t *state = arg; 2479 pktsum_t stats, diff_stats; 2480 dev_args_t dargs; 2481 2482 if (state->ls_firstonly) { 2483 if (state->ls_donefirst) 2484 return (DLADM_WALK_CONTINUE); 2485 state->ls_donefirst = B_TRUE; 2486 } else { 2487 bzero(&state->ls_prevstats, sizeof (state->ls_prevstats)); 2488 } 2489 2490 get_mac_stats(dev, &stats); 2491 stats_diff(&diff_stats, &stats, &state->ls_prevstats); 2492 2493 dargs.devs_link = (char *)dev; 2494 dargs.devs_psum = &diff_stats; 2495 dladm_print_output(&state->ls_print, state->ls_parseable, 2496 print_dev_stats, &dargs); 2497 2498 state->ls_prevstats = stats; 2499 return (DLADM_WALK_CONTINUE); 2500 } 2501 2502 static void 2503 do_show_link(int argc, char *argv[], const char *use) 2504 { 2505 int option; 2506 boolean_t s_arg = B_FALSE; 2507 boolean_t i_arg = B_FALSE; 2508 uint32_t flags = DLADM_OPT_ACTIVE; 2509 boolean_t p_arg = B_FALSE; 2510 datalink_id_t linkid = DATALINK_ALL_LINKID; 2511 int interval = 0; 2512 show_state_t state; 2513 dladm_status_t status; 2514 boolean_t o_arg = B_FALSE; 2515 char *fields_str = NULL; 2516 print_field_t **fields; 2517 uint_t nfields; 2518 char *all_active_fields = "link,class,mtu,state,over"; 2519 char *all_inactive_fields = "link,class,over"; 2520 2521 bzero(&state, sizeof (state)); 2522 2523 opterr = 0; 2524 while ((option = getopt_long(argc, argv, ":pPsi:o:", 2525 show_lopts, NULL)) != -1) { 2526 switch (option) { 2527 case 'p': 2528 if (p_arg) 2529 die_optdup(option); 2530 2531 p_arg = B_TRUE; 2532 break; 2533 case 's': 2534 if (s_arg) 2535 die_optdup(option); 2536 2537 s_arg = B_TRUE; 2538 break; 2539 case 'P': 2540 if (flags != DLADM_OPT_ACTIVE) 2541 die_optdup(option); 2542 2543 flags = DLADM_OPT_PERSIST; 2544 break; 2545 case 'o': 2546 o_arg = B_TRUE; 2547 fields_str = optarg; 2548 break; 2549 case 'i': 2550 if (i_arg) 2551 die_optdup(option); 2552 2553 i_arg = B_TRUE; 2554 if (!str2int(optarg, &interval) || interval == 0) 2555 die("invalid interval value '%s'", optarg); 2556 break; 2557 default: 2558 die_opterr(optopt, option, use); 2559 break; 2560 } 2561 } 2562 2563 if (i_arg && !s_arg) 2564 die("the option -i can be used only with -s"); 2565 2566 if (s_arg && (p_arg || flags != DLADM_OPT_ACTIVE)) 2567 die("the option -%c cannot be used with -s", p_arg ? 'p' : 'P'); 2568 2569 /* get link name (optional last argument) */ 2570 if (optind == (argc-1)) { 2571 uint32_t f; 2572 2573 if ((status = dladm_name2info(argv[optind], &linkid, &f, 2574 NULL, NULL)) != DLADM_STATUS_OK) { 2575 die_dlerr(status, "link %s is not valid", argv[optind]); 2576 } 2577 2578 if (!(f & flags)) { 2579 die_dlerr(DLADM_STATUS_BADARG, "link %s is %s", 2580 argv[optind], flags == DLADM_OPT_PERSIST ? 2581 "a temporary link" : "temporarily removed"); 2582 } 2583 } else if (optind != argc) { 2584 usage(); 2585 } 2586 2587 if (s_arg) { 2588 link_stats(linkid, interval); 2589 return; 2590 } 2591 2592 state.ls_parseable = p_arg; 2593 state.ls_flags = flags; 2594 state.ls_donefirst = B_FALSE; 2595 2596 if (p_arg && !o_arg) 2597 die("-p requires -o"); 2598 2599 if (p_arg && strcasecmp(fields_str, "all") == 0) 2600 die("\"-o all\" is invalid with -p"); 2601 2602 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) { 2603 if (state.ls_flags & DLADM_OPT_ACTIVE) 2604 fields_str = all_active_fields; 2605 else 2606 fields_str = all_inactive_fields; 2607 } 2608 2609 2610 fields = parse_output_fields(fields_str, link_fields, DEV_LINK_FIELDS, 2611 CMD_TYPE_ANY, &nfields); 2612 2613 if (fields == NULL) 2614 die("invalid field(s) specified"); 2615 2616 state.ls_print.ps_fields = fields; 2617 state.ls_print.ps_nfields = nfields; 2618 2619 if (linkid == DATALINK_ALL_LINKID) { 2620 (void) dladm_walk_datalink_id(show_link, &state, 2621 DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags); 2622 } else { 2623 (void) show_link(linkid, &state); 2624 if (state.ls_status != DLADM_STATUS_OK) { 2625 die_dlerr(state.ls_status, "failed to show link %s", 2626 argv[optind]); 2627 } 2628 } 2629 } 2630 2631 static void 2632 do_show_aggr(int argc, char *argv[], const char *use) 2633 { 2634 boolean_t L_arg = B_FALSE; 2635 boolean_t s_arg = B_FALSE; 2636 boolean_t i_arg = B_FALSE; 2637 boolean_t p_arg = B_FALSE; 2638 boolean_t x_arg = B_FALSE; 2639 show_grp_state_t state; 2640 uint32_t flags = DLADM_OPT_ACTIVE; 2641 datalink_id_t linkid = DATALINK_ALL_LINKID; 2642 int option; 2643 int interval = 0; 2644 int key; 2645 dladm_status_t status; 2646 boolean_t o_arg = B_FALSE; 2647 char *fields_str = NULL; 2648 print_field_t **fields; 2649 uint_t nfields; 2650 char *all_fields = 2651 "link,policy,addrpolicy,lacpactivity,lacptimer,flags"; 2652 char *all_lacp_fields = 2653 "link,port,aggregatable,sync,coll,dist,defaulted,expired"; 2654 char *all_stats_fields = 2655 "link,port,ipackets,rbytes,opackets,obytes,ipktdist,opktdist"; 2656 char *all_extended_fields = 2657 "link,port,speed,duplex,state,address,portstate"; 2658 print_field_t *pf; 2659 int pfmax; 2660 2661 bzero(&state, sizeof (state)); 2662 2663 opterr = 0; 2664 while ((option = getopt_long(argc, argv, ":LpPxsi:o:", 2665 show_lopts, NULL)) != -1) { 2666 switch (option) { 2667 case 'L': 2668 if (L_arg) 2669 die_optdup(option); 2670 2671 L_arg = B_TRUE; 2672 break; 2673 case 'p': 2674 if (p_arg) 2675 die_optdup(option); 2676 2677 p_arg = B_TRUE; 2678 break; 2679 case 'x': 2680 if (x_arg) 2681 die_optdup(option); 2682 2683 x_arg = B_TRUE; 2684 break; 2685 case 'P': 2686 if (flags != DLADM_OPT_ACTIVE) 2687 die_optdup(option); 2688 2689 flags = DLADM_OPT_PERSIST; 2690 break; 2691 case 's': 2692 if (s_arg) 2693 die_optdup(option); 2694 2695 s_arg = B_TRUE; 2696 break; 2697 case 'o': 2698 o_arg = B_TRUE; 2699 fields_str = optarg; 2700 break; 2701 case 'i': 2702 if (i_arg) 2703 die_optdup(option); 2704 2705 i_arg = B_TRUE; 2706 if (!str2int(optarg, &interval) || interval == 0) 2707 die("invalid interval value '%s'", optarg); 2708 break; 2709 default: 2710 die_opterr(optopt, option, use); 2711 break; 2712 } 2713 } 2714 2715 if (p_arg && !o_arg) 2716 die("-p requires -o"); 2717 2718 if (p_arg && strcasecmp(fields_str, "all") == 0) 2719 die("\"-o all\" is invalid with -p"); 2720 2721 if (i_arg && !s_arg) 2722 die("the option -i can be used only with -s"); 2723 2724 if (s_arg && (L_arg || p_arg || x_arg || flags != DLADM_OPT_ACTIVE)) { 2725 die("the option -%c cannot be used with -s", 2726 L_arg ? 'L' : (p_arg ? 'p' : (x_arg ? 'x' : 'P'))); 2727 } 2728 2729 if (L_arg && flags != DLADM_OPT_ACTIVE) 2730 die("the option -P cannot be used with -L"); 2731 2732 if (x_arg && (L_arg || flags != DLADM_OPT_ACTIVE)) 2733 die("the option -%c cannot be used with -x", L_arg ? 'L' : 'P'); 2734 2735 /* get aggregation key or aggrname (optional last argument) */ 2736 if (optind == (argc-1)) { 2737 if (!str2int(argv[optind], &key)) { 2738 status = dladm_name2info(argv[optind], &linkid, NULL, 2739 NULL, NULL); 2740 } else { 2741 status = dladm_key2linkid((uint16_t)key, 2742 &linkid, DLADM_OPT_ACTIVE); 2743 } 2744 2745 if (status != DLADM_STATUS_OK) 2746 die("non-existent aggregation '%s'", argv[optind]); 2747 2748 } else if (optind != argc) { 2749 usage(); 2750 } 2751 2752 bzero(&state, sizeof (state)); 2753 state.gs_lacp = L_arg; 2754 state.gs_stats = s_arg; 2755 state.gs_flags = flags; 2756 state.gs_parseable = p_arg; 2757 state.gs_extended = x_arg; 2758 2759 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) { 2760 if (state.gs_lacp) 2761 fields_str = all_lacp_fields; 2762 else if (state.gs_stats) 2763 fields_str = all_stats_fields; 2764 else if (state.gs_extended) 2765 fields_str = all_extended_fields; 2766 else 2767 fields_str = all_fields; 2768 } 2769 2770 if (state.gs_lacp) { 2771 pf = aggr_l_fields; 2772 pfmax = AGGR_L_MAX_FIELDS; 2773 } else if (state.gs_stats) { 2774 pf = aggr_s_fields; 2775 pfmax = AGGR_S_MAX_FIELDS; 2776 } else if (state.gs_extended) { 2777 pf = aggr_x_fields; 2778 pfmax = AGGR_X_MAX_FIELDS; 2779 } else { 2780 pf = laggr_fields; 2781 pfmax = LAGGR_MAX_FIELDS; 2782 } 2783 fields = parse_output_fields(fields_str, pf, pfmax, CMD_TYPE_ANY, 2784 &nfields); 2785 2786 if (fields == NULL) { 2787 die("invalid field(s) specified"); 2788 return; 2789 } 2790 2791 state.gs_print.ps_fields = fields; 2792 state.gs_print.ps_nfields = nfields; 2793 2794 if (s_arg) { 2795 aggr_stats(linkid, &state, interval); 2796 return; 2797 } 2798 2799 if (linkid == DATALINK_ALL_LINKID) { 2800 (void) dladm_walk_datalink_id(show_aggr, &state, 2801 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, flags); 2802 } else { 2803 (void) show_aggr(linkid, &state); 2804 if (state.gs_status != DLADM_STATUS_OK) { 2805 die_dlerr(state.gs_status, "failed to show aggr %s", 2806 argv[optind]); 2807 } 2808 } 2809 } 2810 2811 static void 2812 do_show_dev(int argc, char *argv[], const char *use) 2813 { 2814 int option; 2815 char *dev = NULL; 2816 boolean_t s_arg = B_FALSE; 2817 boolean_t i_arg = B_FALSE; 2818 boolean_t o_arg = B_FALSE; 2819 boolean_t p_arg = B_FALSE; 2820 datalink_id_t linkid; 2821 int interval = 0; 2822 show_state_t state; 2823 char *fields_str = NULL; 2824 print_field_t **fields; 2825 uint_t nfields; 2826 char *all_fields = "link,state,speed,duplex"; 2827 static char *allstat_fields = 2828 "link,ipackets,rbytes,ierrors,opackets,obytes,oerrors"; 2829 2830 bzero(&state, sizeof (state)); 2831 fields_str = all_fields; 2832 2833 opterr = 0; 2834 while ((option = getopt_long(argc, argv, ":psi:o:", 2835 show_lopts, NULL)) != -1) { 2836 switch (option) { 2837 case 'p': 2838 if (p_arg) 2839 die_optdup(option); 2840 2841 p_arg = B_TRUE; 2842 break; 2843 case 's': 2844 if (s_arg) 2845 die_optdup(option); 2846 2847 s_arg = B_TRUE; 2848 break; 2849 case 'o': 2850 o_arg = B_TRUE; 2851 fields_str = optarg; 2852 break; 2853 case 'i': 2854 if (i_arg) 2855 die_optdup(option); 2856 2857 i_arg = B_TRUE; 2858 if (!str2int(optarg, &interval) || interval == 0) 2859 die("invalid interval value '%s'", optarg); 2860 break; 2861 default: 2862 die_opterr(optopt, option, use); 2863 break; 2864 } 2865 } 2866 2867 if (p_arg && !o_arg) 2868 die("-p requires -o"); 2869 2870 if (p_arg && strcasecmp(fields_str, "all") == 0) 2871 die("\"-o all\" is invalid with -p"); 2872 2873 if (i_arg && !s_arg) 2874 die("the option -i can be used only with -s"); 2875 2876 if (o_arg && strcasecmp(fields_str, "all") == 0) { 2877 if (!s_arg) 2878 fields_str = all_fields; 2879 else 2880 fields_str = allstat_fields; 2881 } 2882 2883 if (!o_arg && s_arg) 2884 fields_str = allstat_fields; 2885 2886 if (s_arg && p_arg) 2887 die("the option -s cannot be used with -p"); 2888 2889 /* get dev name (optional last argument) */ 2890 if (optind == (argc-1)) { 2891 uint32_t flags; 2892 2893 dev = argv[optind]; 2894 2895 if (dladm_dev2linkid(dev, &linkid) != DLADM_STATUS_OK) 2896 die("invalid device %s", dev); 2897 2898 if ((dladm_datalink_id2info(linkid, &flags, NULL, NULL, 2899 NULL, 0) != DLADM_STATUS_OK) || 2900 !(flags & DLADM_OPT_ACTIVE)) { 2901 die("device %s has been removed", dev); 2902 } 2903 } else if (optind != argc) { 2904 usage(); 2905 } 2906 2907 state.ls_parseable = p_arg; 2908 state.ls_donefirst = B_FALSE; 2909 2910 if (s_arg) { 2911 dev_stats(dev, interval, fields_str, &state); 2912 return; 2913 } 2914 2915 fields = parse_output_fields(fields_str, dev_fields, DEV_MAX_FIELDS, 2916 CMD_TYPE_ANY, &nfields); 2917 2918 if (fields == NULL) { 2919 die("invalid field(s) specified"); 2920 return; 2921 } 2922 2923 state.ls_print.ps_fields = fields; 2924 state.ls_print.ps_nfields = nfields; 2925 2926 if (dev == NULL) { 2927 (void) dladm_mac_walk(show_dev, &state); 2928 } else { 2929 (void) show_dev(dev, &state); 2930 } 2931 } 2932 2933 2934 static dladm_status_t 2935 print_phys(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *pattr) 2936 { 2937 char link[MAXLINKNAMELEN]; 2938 dladm_phys_attr_t dpa; 2939 uint32_t flags; 2940 datalink_class_t class; 2941 uint32_t media; 2942 dladm_status_t status; 2943 2944 if ((status = dladm_datalink_id2info(linkid, &flags, &class, &media, 2945 link, MAXLINKNAMELEN)) != DLADM_STATUS_OK) { 2946 goto done; 2947 } 2948 2949 if (class != DATALINK_CLASS_PHYS) { 2950 status = DLADM_STATUS_BADARG; 2951 goto done; 2952 } 2953 2954 if (!(state->ls_flags & flags)) { 2955 status = DLADM_STATUS_NOTFOUND; 2956 goto done; 2957 } 2958 2959 status = dladm_phys_info(linkid, &dpa, state->ls_flags); 2960 if (status != DLADM_STATUS_OK) 2961 goto done; 2962 2963 (void) snprintf(pattr->link_phys_device, 2964 sizeof (pattr->link_phys_device), "%s", dpa.dp_dev); 2965 (void) dladm_media2str(media, pattr->link_phys_media); 2966 if (state->ls_flags == DLADM_OPT_ACTIVE) { 2967 boolean_t islink; 2968 2969 if (!dpa.dp_novanity) { 2970 (void) strlcpy(pattr->link_name, link, 2971 sizeof (pattr->link_name)); 2972 islink = B_TRUE; 2973 } else { 2974 /* 2975 * This is a physical link that does not have 2976 * vanity naming support. 2977 */ 2978 (void) strlcpy(pattr->link_name, dpa.dp_dev, 2979 sizeof (pattr->link_name)); 2980 islink = B_FALSE; 2981 } 2982 2983 (void) get_linkstate(pattr->link_name, islink, 2984 pattr->link_phys_state); 2985 (void) snprintf(pattr->link_phys_speed, 2986 sizeof (pattr->link_phys_speed), "%u", 2987 (uint_t)((get_ifspeed(pattr->link_name, 2988 islink)) / 1000000ull)); 2989 (void) get_linkduplex(pattr->link_name, islink, 2990 pattr->link_phys_duplex); 2991 } else { 2992 (void) snprintf(pattr->link_name, sizeof (pattr->link_name), 2993 "%s", link); 2994 (void) snprintf(pattr->link_flags, sizeof (pattr->link_flags), 2995 "%c----", flags & DLADM_OPT_ACTIVE ? '-' : 'r'); 2996 } 2997 2998 done: 2999 return (status); 3000 } 3001 3002 static int 3003 show_phys(datalink_id_t linkid, void *arg) 3004 { 3005 show_state_t *state = arg; 3006 dladm_status_t status; 3007 link_fields_buf_t pattr; 3008 3009 status = print_phys(state, linkid, &pattr); 3010 if (status != DLADM_STATUS_OK) 3011 goto done; 3012 3013 if (!state->ls_parseable && !state->ls_printheader) { 3014 print_header(&state->ls_print); 3015 state->ls_printheader = B_TRUE; 3016 } 3017 3018 dladm_print_output(&state->ls_print, state->ls_parseable, 3019 dladm_print_field, (void *)&pattr); 3020 3021 done: 3022 state->ls_status = status; 3023 return (DLADM_WALK_CONTINUE); 3024 } 3025 3026 3027 /* 3028 * Print the active topology information. 3029 */ 3030 static dladm_status_t 3031 print_vlan(show_state_t *state, datalink_id_t linkid, link_fields_buf_t *l) 3032 { 3033 dladm_vlan_attr_t vinfo; 3034 uint32_t flags; 3035 dladm_status_t status; 3036 3037 if ((status = dladm_datalink_id2info(linkid, &flags, NULL, NULL, 3038 l->link_name, sizeof (l->link_name))) != DLADM_STATUS_OK) { 3039 goto done; 3040 } 3041 3042 if (!(state->ls_flags & flags)) { 3043 status = DLADM_STATUS_NOTFOUND; 3044 goto done; 3045 } 3046 3047 if ((status = dladm_vlan_info(linkid, &vinfo, state->ls_flags)) != 3048 DLADM_STATUS_OK || (status = dladm_datalink_id2info( 3049 vinfo.dv_linkid, NULL, NULL, NULL, l->link_over, 3050 sizeof (l->link_over))) != DLADM_STATUS_OK) { 3051 goto done; 3052 } 3053 3054 (void) snprintf(l->link_vlan_vid, sizeof (l->link_vlan_vid), "%d", 3055 vinfo.dv_vid); 3056 (void) snprintf(l->link_flags, sizeof (l->link_flags), "%c%c---", 3057 vinfo.dv_force ? 'f' : '-', vinfo.dv_implicit ? 'i' : '-'); 3058 3059 done: 3060 return (status); 3061 } 3062 3063 static int 3064 show_vlan(datalink_id_t linkid, void *arg) 3065 { 3066 show_state_t *state = arg; 3067 dladm_status_t status; 3068 link_fields_buf_t lbuf; 3069 3070 status = print_vlan(state, linkid, &lbuf); 3071 if (status != DLADM_STATUS_OK) 3072 goto done; 3073 3074 if (!state->ls_parseable && !state->ls_printheader) { 3075 print_header(&state->ls_print); 3076 state->ls_printheader = B_TRUE; 3077 } 3078 3079 dladm_print_output(&state->ls_print, state->ls_parseable, 3080 dladm_print_field, (void *)&lbuf); 3081 3082 done: 3083 state->ls_status = status; 3084 return (DLADM_WALK_CONTINUE); 3085 } 3086 3087 static void 3088 do_show_phys(int argc, char *argv[], const char *use) 3089 { 3090 int option; 3091 uint32_t flags = DLADM_OPT_ACTIVE; 3092 boolean_t p_arg = B_FALSE; 3093 boolean_t o_arg = B_FALSE; 3094 datalink_id_t linkid = DATALINK_ALL_LINKID; 3095 show_state_t state; 3096 dladm_status_t status; 3097 char *fields_str = NULL; 3098 print_field_t **fields; 3099 uint_t nfields; 3100 char *all_active_fields = 3101 "link,media,state,speed,duplex,device"; 3102 char *all_inactive_fields = 3103 "link,device,media,flags"; 3104 3105 bzero(&state, sizeof (state)); 3106 opterr = 0; 3107 while ((option = getopt_long(argc, argv, ":pPo:", 3108 show_lopts, NULL)) != -1) { 3109 switch (option) { 3110 case 'p': 3111 if (p_arg) 3112 die_optdup(option); 3113 3114 p_arg = B_TRUE; 3115 break; 3116 case 'P': 3117 if (flags != DLADM_OPT_ACTIVE) 3118 die_optdup(option); 3119 3120 flags = DLADM_OPT_PERSIST; 3121 break; 3122 case 'o': 3123 o_arg = B_TRUE; 3124 fields_str = optarg; 3125 break; 3126 default: 3127 die_opterr(optopt, option, use); 3128 break; 3129 } 3130 } 3131 3132 if (p_arg && !o_arg) 3133 die("-p requires -o"); 3134 3135 if (p_arg && strcasecmp(fields_str, "all") == 0) 3136 die("\"-o all\" is invalid with -p"); 3137 3138 /* get link name (optional last argument) */ 3139 if (optind == (argc-1)) { 3140 if ((status = dladm_name2info(argv[optind], &linkid, NULL, 3141 NULL, NULL)) != DLADM_STATUS_OK) { 3142 die_dlerr(status, "link %s is not valid", argv[optind]); 3143 } 3144 } else if (optind != argc) { 3145 usage(); 3146 } 3147 3148 state.ls_parseable = p_arg; 3149 state.ls_flags = flags; 3150 state.ls_donefirst = B_FALSE; 3151 3152 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) { 3153 if (state.ls_flags & DLADM_OPT_ACTIVE) 3154 fields_str = all_active_fields; 3155 else 3156 fields_str = all_inactive_fields; 3157 } 3158 3159 fields = parse_output_fields(fields_str, phys_fields, 3160 PHYS_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 3161 3162 if (fields == NULL) { 3163 die("invalid field(s) specified"); 3164 return; 3165 } 3166 3167 state.ls_print.ps_fields = fields; 3168 state.ls_print.ps_nfields = nfields; 3169 3170 if (linkid == DATALINK_ALL_LINKID) { 3171 (void) dladm_walk_datalink_id(show_phys, &state, 3172 DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, flags); 3173 } else { 3174 (void) show_phys(linkid, &state); 3175 if (state.ls_status != DLADM_STATUS_OK) { 3176 die_dlerr(state.ls_status, 3177 "failed to show physical link %s", argv[optind]); 3178 } 3179 } 3180 } 3181 3182 static void 3183 do_show_vlan(int argc, char *argv[], const char *use) 3184 { 3185 int option; 3186 uint32_t flags = DLADM_OPT_ACTIVE; 3187 boolean_t p_arg = B_FALSE; 3188 datalink_id_t linkid = DATALINK_ALL_LINKID; 3189 show_state_t state; 3190 dladm_status_t status; 3191 boolean_t o_arg = B_FALSE; 3192 char *fields_str = NULL; 3193 print_field_t **fields; 3194 uint_t nfields; 3195 char *all_fields = "link,vid,over,flags"; 3196 3197 bzero(&state, sizeof (state)); 3198 3199 opterr = 0; 3200 while ((option = getopt_long(argc, argv, ":pPo:", 3201 show_lopts, NULL)) != -1) { 3202 switch (option) { 3203 case 'p': 3204 if (p_arg) 3205 die_optdup(option); 3206 3207 p_arg = B_TRUE; 3208 break; 3209 case 'P': 3210 if (flags != DLADM_OPT_ACTIVE) 3211 die_optdup(option); 3212 3213 flags = DLADM_OPT_PERSIST; 3214 break; 3215 case 'o': 3216 o_arg = B_TRUE; 3217 fields_str = optarg; 3218 break; 3219 default: 3220 die_opterr(optopt, option, use); 3221 break; 3222 } 3223 } 3224 3225 if (p_arg && !o_arg) 3226 die("-p requires -o"); 3227 3228 if (p_arg && strcasecmp(fields_str, "all") == 0) 3229 die("\"-o all\" is invalid with -p"); 3230 3231 /* get link name (optional last argument) */ 3232 if (optind == (argc-1)) { 3233 if ((status = dladm_name2info(argv[optind], &linkid, NULL, 3234 NULL, NULL)) != DLADM_STATUS_OK) { 3235 die_dlerr(status, "link %s is not valid", argv[optind]); 3236 } 3237 } else if (optind != argc) { 3238 usage(); 3239 } 3240 3241 state.ls_parseable = p_arg; 3242 state.ls_flags = flags; 3243 state.ls_donefirst = B_FALSE; 3244 3245 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 3246 fields_str = all_fields; 3247 3248 fields = parse_output_fields(fields_str, vlan_fields, VLAN_MAX_FIELDS, 3249 CMD_TYPE_ANY, &nfields); 3250 3251 if (fields == NULL) { 3252 die("invalid field(s) specified"); 3253 return; 3254 } 3255 state.ls_print.ps_fields = fields; 3256 state.ls_print.ps_nfields = nfields; 3257 3258 if (linkid == DATALINK_ALL_LINKID) { 3259 (void) dladm_walk_datalink_id(show_vlan, &state, 3260 DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, flags); 3261 } else { 3262 (void) show_vlan(linkid, &state); 3263 if (state.ls_status != DLADM_STATUS_OK) { 3264 die_dlerr(state.ls_status, "failed to show vlan %s", 3265 argv[optind]); 3266 } 3267 } 3268 } 3269 3270 static void 3271 link_stats(datalink_id_t linkid, uint_t interval) 3272 { 3273 show_state_t state; 3274 3275 bzero(&state, sizeof (state)); 3276 3277 /* 3278 * If an interval is specified, continuously show the stats 3279 * only for the first MAC port. 3280 */ 3281 state.ls_firstonly = (interval != 0); 3282 3283 for (;;) { 3284 (void) printf("%-12s%-10s%-12s%-8s%-10s%-12s%-8s\n", 3285 "LINK", "IPACKETS", "RBYTES", "IERRORS", "OPACKETS", 3286 "OBYTES", "OERRORS"); 3287 3288 state.ls_donefirst = B_FALSE; 3289 if (linkid == DATALINK_ALL_LINKID) { 3290 (void) dladm_walk_datalink_id(show_link_stats, &state, 3291 DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 3292 DLADM_OPT_ACTIVE); 3293 } else { 3294 (void) show_link_stats(linkid, &state); 3295 } 3296 3297 if (interval == 0) 3298 break; 3299 3300 (void) sleep(interval); 3301 } 3302 } 3303 3304 static void 3305 aggr_stats(datalink_id_t linkid, show_grp_state_t *state, uint_t interval) 3306 { 3307 /* 3308 * If an interval is specified, continuously show the stats 3309 * only for the first group. 3310 */ 3311 state->gs_firstonly = (interval != 0); 3312 3313 for (;;) { 3314 state->gs_donefirst = B_FALSE; 3315 if (linkid == DATALINK_ALL_LINKID) 3316 (void) dladm_walk_datalink_id(show_aggr, state, 3317 DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, 3318 DLADM_OPT_ACTIVE); 3319 else 3320 (void) show_aggr(linkid, state); 3321 3322 if (interval == 0) 3323 break; 3324 3325 (void) sleep(interval); 3326 } 3327 } 3328 3329 static void 3330 dev_stats(const char *dev, uint32_t interval, char *fields_str, 3331 show_state_t *state) 3332 { 3333 print_field_t **fields; 3334 uint_t nfields; 3335 3336 fields = parse_output_fields(fields_str, devs_fields, DEVS_MAX_FIELDS, 3337 CMD_TYPE_ANY, &nfields); 3338 3339 if (fields == NULL) { 3340 die("invalid field(s) specified"); 3341 return; 3342 } 3343 3344 state->ls_print.ps_fields = fields; 3345 state->ls_print.ps_nfields = nfields; 3346 3347 3348 /* 3349 * If an interval is specified, continuously show the stats 3350 * only for the first MAC port. 3351 */ 3352 state->ls_firstonly = (interval != 0); 3353 3354 for (;;) { 3355 3356 if (!state->ls_parseable) 3357 print_header(&state->ls_print); 3358 state->ls_donefirst = B_FALSE; 3359 3360 if (dev == NULL) 3361 (void) dladm_mac_walk(show_dev_stats, state); 3362 else 3363 (void) show_dev_stats(dev, state); 3364 3365 if (interval == 0) 3366 break; 3367 3368 (void) sleep(interval); 3369 } 3370 3371 if (dev != NULL && state->ls_status != DLADM_STATUS_OK) 3372 die_dlerr(state->ls_status, "cannot show device '%s'", dev); 3373 } 3374 3375 /* accumulate stats (s1 += (s2 - s3)) */ 3376 static void 3377 stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) 3378 { 3379 s1->ipackets += (s2->ipackets - s3->ipackets); 3380 s1->opackets += (s2->opackets - s3->opackets); 3381 s1->rbytes += (s2->rbytes - s3->rbytes); 3382 s1->obytes += (s2->obytes - s3->obytes); 3383 s1->ierrors += (s2->ierrors - s3->ierrors); 3384 s1->oerrors += (s2->oerrors - s3->oerrors); 3385 } 3386 3387 /* compute stats differences (s1 = s2 - s3) */ 3388 static void 3389 stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) 3390 { 3391 s1->ipackets = s2->ipackets - s3->ipackets; 3392 s1->opackets = s2->opackets - s3->opackets; 3393 s1->rbytes = s2->rbytes - s3->rbytes; 3394 s1->obytes = s2->obytes - s3->obytes; 3395 s1->ierrors = s2->ierrors - s3->ierrors; 3396 s1->oerrors = s2->oerrors - s3->oerrors; 3397 } 3398 3399 static void 3400 get_stats(char *module, int instance, const char *name, pktsum_t *stats) 3401 { 3402 kstat_ctl_t *kcp; 3403 kstat_t *ksp; 3404 3405 if ((kcp = kstat_open()) == NULL) { 3406 warn("kstat open operation failed"); 3407 return; 3408 } 3409 3410 if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) { 3411 /* 3412 * The kstat query could fail if the underlying MAC 3413 * driver was already detached. 3414 */ 3415 (void) kstat_close(kcp); 3416 return; 3417 } 3418 3419 if (kstat_read(kcp, ksp, NULL) == -1) 3420 goto bail; 3421 3422 if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64, 3423 &stats->ipackets) < 0) 3424 goto bail; 3425 3426 if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64, 3427 &stats->opackets) < 0) 3428 goto bail; 3429 3430 if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64, 3431 &stats->rbytes) < 0) 3432 goto bail; 3433 3434 if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64, 3435 &stats->obytes) < 0) 3436 goto bail; 3437 3438 if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32, 3439 &stats->ierrors) < 0) 3440 goto bail; 3441 3442 if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32, 3443 &stats->oerrors) < 0) 3444 goto bail; 3445 3446 bail: 3447 (void) kstat_close(kcp); 3448 return; 3449 3450 } 3451 3452 static void 3453 get_mac_stats(const char *dev, pktsum_t *stats) 3454 { 3455 char module[DLPI_LINKNAME_MAX]; 3456 uint_t instance; 3457 3458 bzero(stats, sizeof (*stats)); 3459 if (dlpi_parselink(dev, module, &instance) != DLPI_SUCCESS) 3460 return; 3461 3462 get_stats(module, instance, "mac", stats); 3463 } 3464 3465 static void 3466 get_link_stats(const char *link, pktsum_t *stats) 3467 { 3468 bzero(stats, sizeof (*stats)); 3469 get_stats("link", 0, link, stats); 3470 } 3471 3472 static int 3473 query_kstat(char *module, int instance, const char *name, const char *stat, 3474 uint8_t type, void *val) 3475 { 3476 kstat_ctl_t *kcp; 3477 kstat_t *ksp; 3478 3479 if ((kcp = kstat_open()) == NULL) { 3480 warn("kstat open operation failed"); 3481 return (-1); 3482 } 3483 3484 if ((ksp = kstat_lookup(kcp, module, instance, (char *)name)) == NULL) { 3485 /* 3486 * The kstat query could fail if the underlying MAC 3487 * driver was already detached. 3488 */ 3489 goto bail; 3490 } 3491 3492 if (kstat_read(kcp, ksp, NULL) == -1) { 3493 warn("kstat read failed"); 3494 goto bail; 3495 } 3496 3497 if (dladm_kstat_value(ksp, stat, type, val) < 0) 3498 goto bail; 3499 3500 (void) kstat_close(kcp); 3501 return (0); 3502 3503 bail: 3504 (void) kstat_close(kcp); 3505 return (-1); 3506 } 3507 3508 static int 3509 get_one_kstat(const char *name, const char *stat, uint8_t type, 3510 void *val, boolean_t islink) 3511 { 3512 char module[DLPI_LINKNAME_MAX]; 3513 uint_t instance; 3514 3515 if (islink) { 3516 return (query_kstat("link", 0, name, stat, type, val)); 3517 } else { 3518 if (dlpi_parselink(name, module, &instance) != DLPI_SUCCESS) 3519 return (-1); 3520 3521 return (query_kstat(module, instance, "mac", stat, type, val)); 3522 } 3523 } 3524 3525 static uint64_t 3526 get_ifspeed(const char *name, boolean_t islink) 3527 { 3528 uint64_t ifspeed = 0; 3529 3530 (void) get_one_kstat(name, "ifspeed", KSTAT_DATA_UINT64, 3531 &ifspeed, islink); 3532 3533 return (ifspeed); 3534 } 3535 3536 static const char * 3537 get_linkstate(const char *name, boolean_t islink, char *buf) 3538 { 3539 link_state_t linkstate; 3540 3541 if (get_one_kstat(name, "link_state", KSTAT_DATA_UINT32, 3542 &linkstate, islink) != 0) { 3543 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 3544 return (buf); 3545 } 3546 return (dladm_linkstate2str(linkstate, buf)); 3547 } 3548 3549 static const char * 3550 get_linkduplex(const char *name, boolean_t islink, char *buf) 3551 { 3552 link_duplex_t linkduplex; 3553 3554 if (get_one_kstat(name, "link_duplex", KSTAT_DATA_UINT32, 3555 &linkduplex, islink) != 0) { 3556 (void) strlcpy(buf, "unknown", DLADM_STRSIZE); 3557 return (buf); 3558 } 3559 3560 return (dladm_linkduplex2str(linkduplex, buf)); 3561 } 3562 3563 typedef struct { 3564 char *s_buf; 3565 char **s_fields; /* array of pointer to the fields in s_buf */ 3566 uint_t s_nfields; /* the number of fields in s_buf */ 3567 } split_t; 3568 3569 /* 3570 * Free the split_t structure pointed to by `sp'. 3571 */ 3572 static void 3573 splitfree(split_t *sp) 3574 { 3575 free(sp->s_buf); 3576 free(sp->s_fields); 3577 free(sp); 3578 } 3579 3580 /* 3581 * Split `str' into at most `maxfields' fields, each field at most `maxlen' in 3582 * length. Return a pointer to a split_t containing the split fields, or NULL 3583 * on failure. 3584 */ 3585 static split_t * 3586 split(const char *str, uint_t maxfields, uint_t maxlen) 3587 { 3588 char *field, *token, *lasts = NULL; 3589 split_t *sp; 3590 3591 if (*str == '\0' || maxfields == 0 || maxlen == 0) 3592 return (NULL); 3593 3594 sp = calloc(sizeof (split_t), 1); 3595 if (sp == NULL) 3596 return (NULL); 3597 3598 sp->s_buf = strdup(str); 3599 sp->s_fields = malloc(sizeof (char *) * maxfields); 3600 if (sp->s_buf == NULL || sp->s_fields == NULL) 3601 goto fail; 3602 3603 token = sp->s_buf; 3604 while ((field = strtok_r(token, ",", &lasts)) != NULL) { 3605 if (sp->s_nfields == maxfields || strlen(field) > maxlen) 3606 goto fail; 3607 token = NULL; 3608 sp->s_fields[sp->s_nfields++] = field; 3609 } 3610 return (sp); 3611 fail: 3612 splitfree(sp); 3613 return (NULL); 3614 } 3615 3616 static int 3617 parse_wifi_fields(char *str, print_field_t ***fields, uint_t *countp, 3618 uint_t cmdtype) 3619 { 3620 3621 if (cmdtype == WIFI_CMD_SCAN) { 3622 if (str == NULL) 3623 str = def_scan_wifi_fields; 3624 if (strcasecmp(str, "all") == 0) 3625 str = all_scan_wifi_fields; 3626 } else if (cmdtype == WIFI_CMD_SHOW) { 3627 if (str == NULL) 3628 str = def_show_wifi_fields; 3629 if (strcasecmp(str, "all") == 0) 3630 str = all_show_wifi_fields; 3631 } else { 3632 return (-1); 3633 } 3634 *fields = parse_output_fields(str, wifi_fields, WIFI_MAX_FIELDS, 3635 cmdtype, countp); 3636 if (*fields != NULL) 3637 return (0); 3638 return (-1); 3639 } 3640 static print_field_t ** 3641 parse_output_fields(char *str, print_field_t *template, int max_fields, 3642 uint_t cmdtype, uint_t *countp) 3643 { 3644 split_t *sp; 3645 boolean_t good_match = B_FALSE; 3646 uint_t i, j; 3647 print_field_t **pf = NULL; 3648 3649 sp = split(str, max_fields, MAX_FIELD_LEN); 3650 3651 if (sp == NULL) 3652 return (NULL); 3653 3654 pf = malloc(sp->s_nfields * sizeof (print_field_t *)); 3655 if (pf == NULL) 3656 goto fail; 3657 3658 for (i = 0; i < sp->s_nfields; i++) { 3659 for (j = 0; j < max_fields; j++) { 3660 if (strcasecmp(sp->s_fields[i], 3661 template[j].pf_name) == 0) { 3662 good_match = template[j]. pf_cmdtype & cmdtype; 3663 break; 3664 } 3665 } 3666 if (!good_match) 3667 goto fail; 3668 3669 good_match = B_FALSE; 3670 pf[i] = &template[j]; 3671 } 3672 *countp = i; 3673 splitfree(sp); 3674 return (pf); 3675 fail: 3676 free(pf); 3677 splitfree(sp); 3678 return (NULL); 3679 } 3680 3681 typedef struct print_wifi_state { 3682 char *ws_link; 3683 boolean_t ws_parseable; 3684 boolean_t ws_header; 3685 print_state_t ws_print_state; 3686 } print_wifi_state_t; 3687 3688 typedef struct wlan_scan_args_s { 3689 print_wifi_state_t *ws_state; 3690 void *ws_attr; 3691 } wlan_scan_args_t; 3692 3693 static void 3694 print_field(print_state_t *statep, print_field_t *pfp, const char *value, 3695 boolean_t parseable) 3696 { 3697 uint_t width = pfp->pf_width; 3698 uint_t valwidth = strlen(value); 3699 uint_t compress; 3700 3701 /* 3702 * Parsable fields are separated by ':'. If such a field contains 3703 * a ':' or '\', this character is prefixed by a '\'. 3704 */ 3705 if (parseable) { 3706 char c; 3707 3708 if (statep->ps_nfields == 1) { 3709 (void) printf("%s", value); 3710 return; 3711 } 3712 while ((c = *value++) != '\0') { 3713 if (c == ':' || c == '\\') 3714 (void) putchar('\\'); 3715 (void) putchar(c); 3716 } 3717 if (!statep->ps_lastfield) 3718 (void) putchar(':'); 3719 return; 3720 } else { 3721 if (value[0] == '\0') 3722 value = STR_UNDEF_VAL; 3723 if (statep->ps_lastfield) { 3724 (void) printf("%s", value); 3725 return; 3726 } 3727 3728 if (valwidth > width) { 3729 statep->ps_overflow += valwidth - width; 3730 } else if (valwidth < width && statep->ps_overflow > 0) { 3731 compress = min(statep->ps_overflow, width - valwidth); 3732 statep->ps_overflow -= compress; 3733 width -= compress; 3734 } 3735 (void) printf("%-*s", width, value); 3736 } 3737 3738 if (!statep->ps_lastfield) 3739 (void) putchar(' '); 3740 } 3741 3742 static char * 3743 print_wlan_attr(print_field_t *wfp, void *warg) 3744 { 3745 static char buf[DLADM_STRSIZE]; 3746 wlan_scan_args_t *w = warg; 3747 print_wifi_state_t *statep = w->ws_state; 3748 dladm_wlan_attr_t *attrp = w->ws_attr; 3749 3750 if (wfp->pf_index == 0) { 3751 return ((char *)statep->ws_link); 3752 } 3753 3754 if ((wfp->pf_index & attrp->wa_valid) == 0) { 3755 return (""); 3756 } 3757 3758 switch (wfp->pf_index) { 3759 case DLADM_WLAN_ATTR_ESSID: 3760 (void) dladm_wlan_essid2str(&attrp->wa_essid, buf); 3761 break; 3762 case DLADM_WLAN_ATTR_BSSID: 3763 (void) dladm_wlan_bssid2str(&attrp->wa_bssid, buf); 3764 break; 3765 case DLADM_WLAN_ATTR_SECMODE: 3766 (void) dladm_wlan_secmode2str(&attrp->wa_secmode, buf); 3767 break; 3768 case DLADM_WLAN_ATTR_STRENGTH: 3769 (void) dladm_wlan_strength2str(&attrp->wa_strength, buf); 3770 break; 3771 case DLADM_WLAN_ATTR_MODE: 3772 (void) dladm_wlan_mode2str(&attrp->wa_mode, buf); 3773 break; 3774 case DLADM_WLAN_ATTR_SPEED: 3775 (void) dladm_wlan_speed2str(&attrp->wa_speed, buf); 3776 (void) strlcat(buf, "Mb", sizeof (buf)); 3777 break; 3778 case DLADM_WLAN_ATTR_AUTH: 3779 (void) dladm_wlan_auth2str(&attrp->wa_auth, buf); 3780 break; 3781 case DLADM_WLAN_ATTR_BSSTYPE: 3782 (void) dladm_wlan_bsstype2str(&attrp->wa_bsstype, buf); 3783 break; 3784 } 3785 3786 return (buf); 3787 } 3788 3789 static boolean_t 3790 print_scan_results(void *arg, dladm_wlan_attr_t *attrp) 3791 { 3792 print_wifi_state_t *statep = arg; 3793 wlan_scan_args_t warg; 3794 3795 if (statep->ws_header) { 3796 statep->ws_header = B_FALSE; 3797 if (!statep->ws_parseable) 3798 print_header(&statep->ws_print_state); 3799 } 3800 3801 statep->ws_print_state.ps_overflow = 0; 3802 bzero(&warg, sizeof (warg)); 3803 warg.ws_state = statep; 3804 warg.ws_attr = attrp; 3805 dladm_print_output(&statep->ws_print_state, statep->ws_parseable, 3806 print_wlan_attr, &warg); 3807 return (B_TRUE); 3808 } 3809 3810 static int 3811 scan_wifi(datalink_id_t linkid, void *arg) 3812 { 3813 print_wifi_state_t *statep = arg; 3814 dladm_status_t status; 3815 char link[MAXLINKNAMELEN]; 3816 3817 if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL, link, 3818 sizeof (link))) != DLADM_STATUS_OK) { 3819 return (DLADM_WALK_CONTINUE); 3820 } 3821 3822 statep->ws_link = link; 3823 status = dladm_wlan_scan(linkid, statep, print_scan_results); 3824 if (status != DLADM_STATUS_OK) 3825 die_dlerr(status, "cannot scan link '%s'", statep->ws_link); 3826 3827 return (DLADM_WALK_CONTINUE); 3828 } 3829 3830 static char * 3831 print_link_attr(print_field_t *wfp, void *warg) 3832 { 3833 static char buf[DLADM_STRSIZE]; 3834 char *ptr; 3835 wlan_scan_args_t *w = warg, w1; 3836 print_wifi_state_t *statep = w->ws_state; 3837 dladm_wlan_linkattr_t *attrp = w->ws_attr; 3838 3839 if (strcmp(wfp->pf_name, "status") == 0) { 3840 if ((wfp->pf_index & attrp->la_valid) != 0) 3841 (void) dladm_wlan_linkstatus2str( 3842 &attrp->la_status, buf); 3843 return (buf); 3844 } 3845 statep->ws_print_state.ps_overflow = 0; 3846 bzero(&w1, sizeof (w1)); 3847 w1.ws_state = statep; 3848 w1.ws_attr = &attrp->la_wlan_attr; 3849 ptr = print_wlan_attr(wfp, &w1); 3850 return (ptr); 3851 } 3852 3853 static int 3854 show_wifi(datalink_id_t linkid, void *arg) 3855 { 3856 print_wifi_state_t *statep = arg; 3857 dladm_wlan_linkattr_t attr; 3858 dladm_status_t status; 3859 char link[MAXLINKNAMELEN]; 3860 wlan_scan_args_t warg; 3861 3862 if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL, link, 3863 sizeof (link))) != DLADM_STATUS_OK) { 3864 return (DLADM_WALK_CONTINUE); 3865 } 3866 3867 status = dladm_wlan_get_linkattr(linkid, &attr); 3868 if (status != DLADM_STATUS_OK) 3869 die_dlerr(status, "cannot get link attributes for %s", link); 3870 3871 statep->ws_link = link; 3872 3873 if (statep->ws_header) { 3874 statep->ws_header = B_FALSE; 3875 if (!statep->ws_parseable) 3876 print_header(&statep->ws_print_state); 3877 } 3878 3879 statep->ws_print_state.ps_overflow = 0; 3880 bzero(&warg, sizeof (warg)); 3881 warg.ws_state = statep; 3882 warg.ws_attr = &attr; 3883 dladm_print_output(&statep->ws_print_state, statep->ws_parseable, 3884 print_link_attr, &warg); 3885 return (DLADM_WALK_CONTINUE); 3886 } 3887 3888 static void 3889 do_display_wifi(int argc, char **argv, int cmd, const char *use) 3890 { 3891 int option; 3892 char *fields_str = NULL; 3893 print_field_t **fields; 3894 int (*callback)(datalink_id_t, void *); 3895 uint_t nfields; 3896 print_wifi_state_t state; 3897 datalink_id_t linkid = DATALINK_ALL_LINKID; 3898 dladm_status_t status; 3899 3900 if (cmd == WIFI_CMD_SCAN) 3901 callback = scan_wifi; 3902 else if (cmd == WIFI_CMD_SHOW) 3903 callback = show_wifi; 3904 else 3905 return; 3906 3907 state.ws_parseable = B_FALSE; 3908 state.ws_header = B_TRUE; 3909 opterr = 0; 3910 while ((option = getopt_long(argc, argv, ":o:p", 3911 wifi_longopts, NULL)) != -1) { 3912 switch (option) { 3913 case 'o': 3914 fields_str = optarg; 3915 break; 3916 case 'p': 3917 state.ws_parseable = B_TRUE; 3918 break; 3919 default: 3920 die_opterr(optopt, option, use); 3921 } 3922 } 3923 3924 if (state.ws_parseable && fields_str == NULL) 3925 die("-p requires -o"); 3926 3927 if (state.ws_parseable && strcasecmp(fields_str, "all") == 0) 3928 die("\"-o all\" is invalid with -p"); 3929 3930 if (optind == (argc - 1)) { 3931 if ((status = dladm_name2info(argv[optind], &linkid, NULL, 3932 NULL, NULL)) != DLADM_STATUS_OK) { 3933 die_dlerr(status, "link %s is not valid", argv[optind]); 3934 } 3935 } else if (optind != argc) { 3936 usage(); 3937 } 3938 3939 if (parse_wifi_fields(fields_str, &fields, &nfields, cmd) < 0) 3940 die("invalid field(s) specified"); 3941 3942 bzero(&state.ws_print_state, sizeof (state.ws_print_state)); 3943 state.ws_print_state.ps_fields = fields; 3944 state.ws_print_state.ps_nfields = nfields; 3945 3946 if (linkid == DATALINK_ALL_LINKID) { 3947 (void) dladm_walk_datalink_id(callback, &state, 3948 DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE); 3949 } else { 3950 (void) (*callback)(linkid, &state); 3951 } 3952 free(fields); 3953 } 3954 3955 static void 3956 do_scan_wifi(int argc, char **argv, const char *use) 3957 { 3958 do_display_wifi(argc, argv, WIFI_CMD_SCAN, use); 3959 } 3960 3961 static void 3962 do_show_wifi(int argc, char **argv, const char *use) 3963 { 3964 do_display_wifi(argc, argv, WIFI_CMD_SHOW, use); 3965 } 3966 3967 typedef struct wlan_count_attr { 3968 uint_t wc_count; 3969 datalink_id_t wc_linkid; 3970 } wlan_count_attr_t; 3971 3972 static int 3973 do_count_wlan(datalink_id_t linkid, void *arg) 3974 { 3975 wlan_count_attr_t *cp = arg; 3976 3977 if (cp->wc_count == 0) 3978 cp->wc_linkid = linkid; 3979 cp->wc_count++; 3980 return (DLADM_WALK_CONTINUE); 3981 } 3982 3983 static int 3984 parse_wlan_keys(char *str, dladm_wlan_key_t **keys, uint_t *key_countp) 3985 { 3986 uint_t i; 3987 split_t *sp; 3988 dladm_wlan_key_t *wk; 3989 3990 sp = split(str, DLADM_WLAN_MAX_WEPKEYS, DLADM_WLAN_MAX_KEYNAME_LEN); 3991 if (sp == NULL) 3992 return (-1); 3993 3994 wk = malloc(sp->s_nfields * sizeof (dladm_wlan_key_t)); 3995 if (wk == NULL) 3996 goto fail; 3997 3998 for (i = 0; i < sp->s_nfields; i++) { 3999 char *s; 4000 dladm_secobj_class_t class; 4001 dladm_status_t status; 4002 4003 (void) strlcpy(wk[i].wk_name, sp->s_fields[i], 4004 DLADM_WLAN_MAX_KEYNAME_LEN); 4005 4006 wk[i].wk_idx = 1; 4007 if ((s = strrchr(wk[i].wk_name, ':')) != NULL) { 4008 if (s[1] == '\0' || s[2] != '\0' || !isdigit(s[1])) 4009 goto fail; 4010 4011 wk[i].wk_idx = (uint_t)(s[1] - '0'); 4012 *s = '\0'; 4013 } 4014 wk[i].wk_len = DLADM_WLAN_MAX_KEY_LEN; 4015 4016 status = dladm_get_secobj(wk[i].wk_name, &class, 4017 wk[i].wk_val, &wk[i].wk_len, 0); 4018 if (status != DLADM_STATUS_OK) { 4019 if (status == DLADM_STATUS_NOTFOUND) { 4020 status = dladm_get_secobj(wk[i].wk_name, 4021 &class, wk[i].wk_val, &wk[i].wk_len, 4022 DLADM_OPT_PERSIST); 4023 } 4024 if (status != DLADM_STATUS_OK) 4025 goto fail; 4026 } 4027 wk[i].wk_class = class; 4028 } 4029 *keys = wk; 4030 *key_countp = i; 4031 splitfree(sp); 4032 return (0); 4033 fail: 4034 free(wk); 4035 splitfree(sp); 4036 return (-1); 4037 } 4038 4039 static void 4040 do_connect_wifi(int argc, char **argv, const char *use) 4041 { 4042 int option; 4043 dladm_wlan_attr_t attr, *attrp; 4044 dladm_status_t status = DLADM_STATUS_OK; 4045 int timeout = DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT; 4046 datalink_id_t linkid = DATALINK_ALL_LINKID; 4047 dladm_wlan_key_t *keys = NULL; 4048 uint_t key_count = 0; 4049 uint_t flags = 0; 4050 dladm_wlan_secmode_t keysecmode = DLADM_WLAN_SECMODE_NONE; 4051 char buf[DLADM_STRSIZE]; 4052 4053 opterr = 0; 4054 (void) memset(&attr, 0, sizeof (attr)); 4055 while ((option = getopt_long(argc, argv, ":e:i:a:m:b:s:k:T:c", 4056 wifi_longopts, NULL)) != -1) { 4057 switch (option) { 4058 case 'e': 4059 status = dladm_wlan_str2essid(optarg, &attr.wa_essid); 4060 if (status != DLADM_STATUS_OK) 4061 die("invalid ESSID '%s'", optarg); 4062 4063 attr.wa_valid |= DLADM_WLAN_ATTR_ESSID; 4064 /* 4065 * Try to connect without doing a scan. 4066 */ 4067 flags |= DLADM_WLAN_CONNECT_NOSCAN; 4068 break; 4069 case 'i': 4070 status = dladm_wlan_str2bssid(optarg, &attr.wa_bssid); 4071 if (status != DLADM_STATUS_OK) 4072 die("invalid BSSID %s", optarg); 4073 4074 attr.wa_valid |= DLADM_WLAN_ATTR_BSSID; 4075 break; 4076 case 'a': 4077 status = dladm_wlan_str2auth(optarg, &attr.wa_auth); 4078 if (status != DLADM_STATUS_OK) 4079 die("invalid authentication mode '%s'", optarg); 4080 4081 attr.wa_valid |= DLADM_WLAN_ATTR_AUTH; 4082 break; 4083 case 'm': 4084 status = dladm_wlan_str2mode(optarg, &attr.wa_mode); 4085 if (status != DLADM_STATUS_OK) 4086 die("invalid mode '%s'", optarg); 4087 4088 attr.wa_valid |= DLADM_WLAN_ATTR_MODE; 4089 break; 4090 case 'b': 4091 if ((status = dladm_wlan_str2bsstype(optarg, 4092 &attr.wa_bsstype)) != DLADM_STATUS_OK) { 4093 die("invalid bsstype '%s'", optarg); 4094 } 4095 4096 attr.wa_valid |= DLADM_WLAN_ATTR_BSSTYPE; 4097 break; 4098 case 's': 4099 if ((status = dladm_wlan_str2secmode(optarg, 4100 &attr.wa_secmode)) != DLADM_STATUS_OK) { 4101 die("invalid security mode '%s'", optarg); 4102 } 4103 4104 attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE; 4105 break; 4106 case 'k': 4107 if (parse_wlan_keys(optarg, &keys, &key_count) < 0) 4108 die("invalid key(s) '%s'", optarg); 4109 4110 if (keys[0].wk_class == DLADM_SECOBJ_CLASS_WEP) 4111 keysecmode = DLADM_WLAN_SECMODE_WEP; 4112 else 4113 keysecmode = DLADM_WLAN_SECMODE_WPA; 4114 break; 4115 case 'T': 4116 if (strcasecmp(optarg, "forever") == 0) { 4117 timeout = -1; 4118 break; 4119 } 4120 if (!str2int(optarg, &timeout) || timeout < 0) 4121 die("invalid timeout value '%s'", optarg); 4122 break; 4123 case 'c': 4124 flags |= DLADM_WLAN_CONNECT_CREATEIBSS; 4125 flags |= DLADM_WLAN_CONNECT_CREATEIBSS; 4126 break; 4127 default: 4128 die_opterr(optopt, option, use); 4129 break; 4130 } 4131 } 4132 4133 if (keysecmode == DLADM_WLAN_SECMODE_NONE) { 4134 if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0) { 4135 die("key required for security mode '%s'", 4136 dladm_wlan_secmode2str(&attr.wa_secmode, buf)); 4137 } 4138 } else { 4139 if ((attr.wa_valid & DLADM_WLAN_ATTR_SECMODE) != 0 && 4140 attr.wa_secmode != keysecmode) 4141 die("incompatible -s and -k options"); 4142 attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE; 4143 attr.wa_secmode = keysecmode; 4144 } 4145 4146 if (optind == (argc - 1)) { 4147 if ((status = dladm_name2info(argv[optind], &linkid, NULL, 4148 NULL, NULL)) != DLADM_STATUS_OK) { 4149 die_dlerr(status, "link %s is not valid", argv[optind]); 4150 } 4151 } else if (optind != argc) { 4152 usage(); 4153 } 4154 4155 if (linkid == DATALINK_ALL_LINKID) { 4156 wlan_count_attr_t wcattr; 4157 4158 wcattr.wc_linkid = DATALINK_INVALID_LINKID; 4159 wcattr.wc_count = 0; 4160 (void) dladm_walk_datalink_id(do_count_wlan, &wcattr, 4161 DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE); 4162 if (wcattr.wc_count == 0) { 4163 die("no wifi links are available"); 4164 } else if (wcattr.wc_count > 1) { 4165 die("link name is required when more than one wifi " 4166 "link is available"); 4167 } 4168 linkid = wcattr.wc_linkid; 4169 } 4170 attrp = (attr.wa_valid == 0) ? NULL : &attr; 4171 again: 4172 if ((status = dladm_wlan_connect(linkid, attrp, timeout, keys, 4173 key_count, flags)) != DLADM_STATUS_OK) { 4174 if ((flags & DLADM_WLAN_CONNECT_NOSCAN) != 0) { 4175 /* 4176 * Try again with scanning and filtering. 4177 */ 4178 flags &= ~DLADM_WLAN_CONNECT_NOSCAN; 4179 goto again; 4180 } 4181 4182 if (status == DLADM_STATUS_NOTFOUND) { 4183 if (attr.wa_valid == 0) { 4184 die("no wifi networks are available"); 4185 } else { 4186 die("no wifi networks with the specified " 4187 "criteria are available"); 4188 } 4189 } 4190 die_dlerr(status, "cannot connect"); 4191 } 4192 free(keys); 4193 } 4194 4195 /* ARGSUSED */ 4196 static int 4197 do_all_disconnect_wifi(datalink_id_t linkid, void *arg) 4198 { 4199 dladm_status_t status; 4200 4201 status = dladm_wlan_disconnect(linkid); 4202 if (status != DLADM_STATUS_OK) 4203 warn_dlerr(status, "cannot disconnect link"); 4204 4205 return (DLADM_WALK_CONTINUE); 4206 } 4207 4208 static void 4209 do_disconnect_wifi(int argc, char **argv, const char *use) 4210 { 4211 int option; 4212 datalink_id_t linkid = DATALINK_ALL_LINKID; 4213 boolean_t all_links = B_FALSE; 4214 dladm_status_t status; 4215 wlan_count_attr_t wcattr; 4216 4217 opterr = 0; 4218 while ((option = getopt_long(argc, argv, ":a", 4219 wifi_longopts, NULL)) != -1) { 4220 switch (option) { 4221 case 'a': 4222 all_links = B_TRUE; 4223 break; 4224 default: 4225 die_opterr(optopt, option, use); 4226 break; 4227 } 4228 } 4229 4230 if (optind == (argc - 1)) { 4231 if ((status = dladm_name2info(argv[optind], &linkid, NULL, 4232 NULL, NULL)) != DLADM_STATUS_OK) { 4233 die_dlerr(status, "link %s is not valid", argv[optind]); 4234 } 4235 } else if (optind != argc) { 4236 usage(); 4237 } 4238 4239 if (linkid == DATALINK_ALL_LINKID) { 4240 if (!all_links) { 4241 wcattr.wc_linkid = linkid; 4242 wcattr.wc_count = 0; 4243 (void) dladm_walk_datalink_id(do_count_wlan, &wcattr, 4244 DATALINK_CLASS_PHYS, DL_WIFI, DLADM_OPT_ACTIVE); 4245 if (wcattr.wc_count == 0) { 4246 die("no wifi links are available"); 4247 } else if (wcattr.wc_count > 1) { 4248 die("link name is required when more than " 4249 "one wifi link is available"); 4250 } 4251 linkid = wcattr.wc_linkid; 4252 } else { 4253 (void) dladm_walk_datalink_id(do_all_disconnect_wifi, 4254 NULL, DATALINK_CLASS_PHYS, DL_WIFI, 4255 DLADM_OPT_ACTIVE); 4256 return; 4257 } 4258 } 4259 status = dladm_wlan_disconnect(linkid); 4260 if (status != DLADM_STATUS_OK) 4261 die_dlerr(status, "cannot disconnect"); 4262 } 4263 4264 4265 static void 4266 free_props(prop_list_t *list) 4267 { 4268 if (list != NULL) { 4269 free(list->pl_buf); 4270 free(list); 4271 } 4272 } 4273 4274 static int 4275 parse_props(char *str, prop_list_t **listp, boolean_t novalues) 4276 { 4277 prop_list_t *list; 4278 prop_info_t *pip; 4279 char *buf, *curr; 4280 int len, i; 4281 4282 list = malloc(sizeof (prop_list_t)); 4283 if (list == NULL) 4284 return (-1); 4285 4286 list->pl_count = 0; 4287 list->pl_buf = buf = strdup(str); 4288 if (buf == NULL) 4289 goto fail; 4290 4291 /* 4292 * buf is a string of form [<propname>=<value>][,<propname>=<value>]+ 4293 * where each <value> string itself could be a comma-separated array. 4294 * The loop below will count the number of propname assignments 4295 * in pl_count; for each property, there is a pip entry with 4296 * pi_name == <propname>, pi_count == # of elements in <value> array. 4297 * pi_val[] contains the actual values. 4298 * 4299 * This could really be a combination of calls to 4300 * strtok (token delimiter is ",") and strchr (chr '=') 4301 * with appropriate null/string-bound-checks. 4302 */ 4303 4304 curr = buf; 4305 len = strlen(buf); 4306 pip = NULL; 4307 for (i = 0; i < len; i++) { 4308 char c = buf[i]; 4309 boolean_t match = (c == '=' || c == ','); 4310 4311 if (!match && i != len - 1) 4312 continue; 4313 4314 if (match) { 4315 buf[i] = '\0'; 4316 if (*curr == '\0') 4317 goto fail; 4318 } 4319 4320 if (pip != NULL && c != '=') { 4321 if (pip->pi_count > DLADM_MAX_PROP_VALCNT) 4322 goto fail; 4323 4324 if (novalues) 4325 goto fail; 4326 4327 pip->pi_val[pip->pi_count] = curr; 4328 pip->pi_count++; 4329 } else { 4330 if (list->pl_count > MAX_PROPS) 4331 goto fail; 4332 4333 pip = &list->pl_info[list->pl_count]; 4334 pip->pi_name = curr; 4335 pip->pi_count = 0; 4336 list->pl_count++; 4337 if (c == ',') 4338 pip = NULL; 4339 } 4340 curr = buf + i + 1; 4341 } 4342 *listp = list; 4343 return (0); 4344 4345 fail: 4346 free_props(list); 4347 return (-1); 4348 } 4349 4350 static void 4351 print_linkprop(datalink_id_t linkid, show_linkprop_state_t *statep, 4352 const char *propname, dladm_prop_type_t type, 4353 const char *format, char **pptr) 4354 { 4355 int i; 4356 char *ptr, *lim; 4357 char buf[DLADM_STRSIZE]; 4358 char *unknown = "?", *notsup = ""; 4359 char **propvals = statep->ls_propvals; 4360 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 4361 dladm_status_t status; 4362 4363 status = dladm_get_linkprop(linkid, type, propname, propvals, &valcnt); 4364 if (status != DLADM_STATUS_OK) { 4365 if (status == DLADM_STATUS_TEMPONLY) { 4366 if (type == DLADM_PROP_VAL_MODIFIABLE && 4367 statep->ls_persist) { 4368 valcnt = 1; 4369 propvals = &unknown; 4370 } else { 4371 statep->ls_status = status; 4372 statep->ls_retstatus = status; 4373 return; 4374 } 4375 } else if (status == DLADM_STATUS_NOTSUP || 4376 statep->ls_persist) { 4377 valcnt = 1; 4378 if (type == DLADM_PROP_VAL_CURRENT) 4379 propvals = &unknown; 4380 else 4381 propvals = ¬sup; 4382 } else { 4383 if (statep->ls_proplist && 4384 statep->ls_status == DLADM_STATUS_OK) { 4385 warn_dlerr(status, 4386 "cannot get link property '%s' for %s", 4387 propname, statep->ls_link); 4388 } 4389 statep->ls_status = status; 4390 statep->ls_retstatus = status; 4391 return; 4392 } 4393 } 4394 4395 statep->ls_status = DLADM_STATUS_OK; 4396 4397 ptr = buf; 4398 lim = buf + DLADM_STRSIZE; 4399 for (i = 0; i < valcnt; i++) { 4400 if (propvals[i][0] == '\0' && !statep->ls_parseable) 4401 ptr += snprintf(ptr, lim - ptr, STR_UNDEF_VAL","); 4402 else 4403 ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]); 4404 if (ptr >= lim) 4405 break; 4406 } 4407 if (valcnt > 0) 4408 buf[strlen(buf) - 1] = '\0'; 4409 4410 lim = statep->ls_line + MAX_PROP_LINE; 4411 if (statep->ls_parseable) { 4412 *pptr += snprintf(*pptr, lim - *pptr, 4413 "%s", buf); 4414 } else { 4415 *pptr += snprintf(*pptr, lim - *pptr, format, buf); 4416 } 4417 } 4418 4419 static char * 4420 linkprop_callback(print_field_t *pf, void *ls_arg) 4421 { 4422 linkprop_args_t *arg = ls_arg; 4423 char *propname = arg->ls_propname; 4424 show_linkprop_state_t *statep = arg->ls_state; 4425 char *ptr = statep->ls_line; 4426 char *lim = ptr + MAX_PROP_LINE; 4427 datalink_id_t linkid = arg->ls_linkid; 4428 4429 switch (pf->pf_index) { 4430 case LINKPROP_LINK: 4431 (void) snprintf(ptr, lim - ptr, "%s", statep->ls_link); 4432 break; 4433 case LINKPROP_PROPERTY: 4434 (void) snprintf(ptr, lim - ptr, "%s", propname); 4435 break; 4436 case LINKPROP_VALUE: 4437 print_linkprop(linkid, statep, propname, 4438 statep->ls_persist ? DLADM_PROP_VAL_PERSISTENT : 4439 DLADM_PROP_VAL_CURRENT, "%s", &ptr); 4440 /* 4441 * If we failed to query the link property, for example, query 4442 * the persistent value of a non-persistable link property, 4443 * simply skip the output. 4444 */ 4445 if (statep->ls_status != DLADM_STATUS_OK) 4446 goto skip; 4447 ptr = statep->ls_line; 4448 break; 4449 case LINKPROP_DEFAULT: 4450 print_linkprop(linkid, statep, propname, 4451 DLADM_PROP_VAL_DEFAULT, "%s", &ptr); 4452 if (statep->ls_status != DLADM_STATUS_OK) 4453 goto skip; 4454 ptr = statep->ls_line; 4455 break; 4456 case LINKPROP_POSSIBLE: 4457 print_linkprop(linkid, statep, propname, 4458 DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr); 4459 if (statep->ls_status != DLADM_STATUS_OK) 4460 goto skip; 4461 ptr = statep->ls_line; 4462 break; 4463 default: 4464 die("invalid input"); 4465 break; 4466 } 4467 return (ptr); 4468 skip: 4469 if (statep->ls_status != DLADM_STATUS_OK) 4470 return (NULL); 4471 else 4472 return (""); 4473 } 4474 4475 static int 4476 show_linkprop(datalink_id_t linkid, const char *propname, void *arg) 4477 { 4478 show_linkprop_state_t *statep = arg; 4479 linkprop_args_t ls_arg; 4480 4481 bzero(&ls_arg, sizeof (ls_arg)); 4482 ls_arg.ls_state = statep; 4483 ls_arg.ls_propname = (char *)propname; 4484 ls_arg.ls_linkid = linkid; 4485 4486 if (statep->ls_header) { 4487 statep->ls_header = B_FALSE; 4488 if (!statep->ls_parseable) 4489 print_header(&statep->ls_print); 4490 } 4491 dladm_print_output(&statep->ls_print, statep->ls_parseable, 4492 linkprop_callback, (void *)&ls_arg); 4493 4494 return (DLADM_WALK_CONTINUE); 4495 } 4496 4497 static void 4498 do_show_linkprop(int argc, char **argv, const char *use) 4499 { 4500 int option; 4501 prop_list_t *proplist = NULL; 4502 datalink_id_t linkid = DATALINK_ALL_LINKID; 4503 show_linkprop_state_t state; 4504 uint32_t flags = DLADM_OPT_ACTIVE; 4505 dladm_status_t status; 4506 char *fields_str = NULL; 4507 print_field_t **fields; 4508 uint_t nfields; 4509 boolean_t o_arg = B_FALSE; 4510 char *all_fields = 4511 "link,property,value,default,possible"; 4512 4513 fields_str = all_fields; 4514 4515 opterr = 0; 4516 state.ls_propvals = NULL; 4517 state.ls_line = NULL; 4518 state.ls_parseable = B_FALSE; 4519 state.ls_persist = B_FALSE; 4520 state.ls_header = B_TRUE; 4521 state.ls_retstatus = DLADM_STATUS_OK; 4522 while ((option = getopt_long(argc, argv, ":p:cPo:", 4523 prop_longopts, NULL)) != -1) { 4524 switch (option) { 4525 case 'p': 4526 if (parse_props(optarg, &proplist, B_TRUE) < 0) 4527 die("invalid link properties specified"); 4528 break; 4529 case 'c': 4530 state.ls_parseable = B_TRUE; 4531 break; 4532 case 'P': 4533 state.ls_persist = B_TRUE; 4534 flags = DLADM_OPT_PERSIST; 4535 break; 4536 case 'o': 4537 o_arg = B_TRUE; 4538 if (strcasecmp(optarg, "all") == 0) 4539 fields_str = all_fields; 4540 else 4541 fields_str = optarg; 4542 break; 4543 default: 4544 die_opterr(optopt, option, use); 4545 break; 4546 } 4547 } 4548 4549 if (state.ls_parseable && !o_arg) 4550 die("-c requires -o"); 4551 4552 if (state.ls_parseable && fields_str == all_fields) 4553 die("\"-o all\" is invalid with -c"); 4554 4555 if (optind == (argc - 1)) { 4556 if ((status = dladm_name2info(argv[optind], &linkid, NULL, 4557 NULL, NULL)) != DLADM_STATUS_OK) { 4558 die_dlerr(status, "link %s is not valid", argv[optind]); 4559 } 4560 } else if (optind != argc) { 4561 usage(); 4562 } 4563 4564 bzero(&state.ls_print, sizeof (print_state_t)); 4565 state.ls_proplist = proplist; 4566 state.ls_status = DLADM_STATUS_OK; 4567 4568 fields = parse_output_fields(fields_str, linkprop_fields, 4569 LINKPROP_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 4570 4571 if (fields == NULL) { 4572 die("invalid field(s) specified"); 4573 return; 4574 } 4575 4576 state.ls_print.ps_fields = fields; 4577 state.ls_print.ps_nfields = nfields; 4578 if (linkid == DATALINK_ALL_LINKID) { 4579 (void) dladm_walk_datalink_id(show_linkprop_onelink, &state, 4580 DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, flags); 4581 } else { 4582 (void) show_linkprop_onelink(linkid, &state); 4583 } 4584 free_props(proplist); 4585 4586 if (state.ls_retstatus != DLADM_STATUS_OK) 4587 exit(EXIT_FAILURE); 4588 } 4589 4590 static int 4591 show_linkprop_onelink(datalink_id_t linkid, void *arg) 4592 { 4593 int i; 4594 char *buf; 4595 uint32_t flags; 4596 prop_list_t *proplist = NULL; 4597 show_linkprop_state_t *statep = arg; 4598 dlpi_handle_t dh = NULL; 4599 4600 statep->ls_status = DLADM_STATUS_OK; 4601 4602 if (dladm_datalink_id2info(linkid, &flags, NULL, NULL, statep->ls_link, 4603 MAXLINKNAMELEN) != DLADM_STATUS_OK) { 4604 statep->ls_status = DLADM_STATUS_NOTFOUND; 4605 return (DLADM_WALK_CONTINUE); 4606 } 4607 4608 if ((statep->ls_persist && !(flags & DLADM_OPT_PERSIST)) || 4609 (!statep->ls_persist && !(flags & DLADM_OPT_ACTIVE))) { 4610 statep->ls_status = DLADM_STATUS_BADARG; 4611 return (DLADM_WALK_CONTINUE); 4612 } 4613 4614 proplist = statep->ls_proplist; 4615 4616 /* 4617 * When some WiFi links are opened for the first time, their hardware 4618 * automatically scans for APs and does other slow operations. Thus, 4619 * if there are no open links, the retrieval of link properties 4620 * (below) will proceed slowly unless we hold the link open. 4621 * 4622 * Note that failure of dlpi_open() does not necessarily mean invalid 4623 * link properties, because dlpi_open() may fail because of incorrect 4624 * autopush configuration. Therefore, we ingore the return value of 4625 * dlpi_open(). 4626 */ 4627 if (!statep->ls_persist) 4628 (void) dlpi_open(statep->ls_link, &dh, 0); 4629 4630 buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * 4631 DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE); 4632 if (buf == NULL) 4633 die("insufficient memory"); 4634 4635 statep->ls_propvals = (char **)(void *)buf; 4636 for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) { 4637 statep->ls_propvals[i] = buf + 4638 sizeof (char *) * DLADM_MAX_PROP_VALCNT + 4639 i * DLADM_PROP_VAL_MAX; 4640 } 4641 statep->ls_line = buf + 4642 (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT; 4643 4644 if (proplist != NULL) { 4645 for (i = 0; i < proplist->pl_count; i++) { 4646 (void) show_linkprop(linkid, 4647 proplist->pl_info[i].pi_name, statep); 4648 } 4649 } else { 4650 (void) dladm_walk_linkprop(linkid, statep, show_linkprop); 4651 } 4652 if (dh != NULL) 4653 dlpi_close(dh); 4654 free(buf); 4655 return (DLADM_WALK_CONTINUE); 4656 } 4657 4658 static dladm_status_t 4659 set_linkprop_persist(datalink_id_t linkid, const char *prop_name, 4660 char **prop_val, uint_t val_cnt, boolean_t reset) 4661 { 4662 dladm_status_t status; 4663 4664 status = dladm_set_linkprop(linkid, prop_name, prop_val, val_cnt, 4665 DLADM_OPT_PERSIST); 4666 4667 if (status != DLADM_STATUS_OK) { 4668 warn_dlerr(status, "cannot persistently %s link property", 4669 reset ? "reset" : "set"); 4670 } 4671 return (status); 4672 } 4673 4674 static void 4675 set_linkprop(int argc, char **argv, boolean_t reset, const char *use) 4676 { 4677 int i, option; 4678 char errmsg[DLADM_STRSIZE]; 4679 char *altroot = NULL; 4680 datalink_id_t linkid; 4681 prop_list_t *proplist = NULL; 4682 boolean_t temp = B_FALSE; 4683 dladm_status_t status = DLADM_STATUS_OK; 4684 4685 opterr = 0; 4686 while ((option = getopt_long(argc, argv, ":p:R:t", 4687 prop_longopts, NULL)) != -1) { 4688 switch (option) { 4689 case 'p': 4690 if (parse_props(optarg, &proplist, reset) < 0) 4691 die("invalid link properties specified"); 4692 break; 4693 case 't': 4694 temp = B_TRUE; 4695 break; 4696 case 'R': 4697 altroot = optarg; 4698 break; 4699 default: 4700 die_opterr(optopt, option, use); 4701 4702 } 4703 } 4704 4705 /* get link name (required last argument) */ 4706 if (optind != (argc - 1)) 4707 usage(); 4708 4709 if (proplist == NULL && !reset) 4710 die("link property must be specified"); 4711 4712 if (altroot != NULL) { 4713 free_props(proplist); 4714 altroot_cmd(altroot, argc, argv); 4715 } 4716 4717 status = dladm_name2info(argv[optind], &linkid, NULL, NULL, NULL); 4718 if (status != DLADM_STATUS_OK) 4719 die_dlerr(status, "link %s is not valid", argv[optind]); 4720 4721 if (proplist == NULL) { 4722 status = dladm_set_linkprop(linkid, NULL, NULL, 0, 4723 DLADM_OPT_ACTIVE); 4724 if (status != DLADM_STATUS_OK) { 4725 warn_dlerr(status, "cannot reset link property " 4726 "on '%s'", argv[optind]); 4727 } 4728 if (!temp) { 4729 dladm_status_t s; 4730 4731 s = set_linkprop_persist(linkid, NULL, NULL, 0, reset); 4732 if (s != DLADM_STATUS_OK) 4733 status = s; 4734 } 4735 goto done; 4736 } 4737 4738 for (i = 0; i < proplist->pl_count; i++) { 4739 prop_info_t *pip = &proplist->pl_info[i]; 4740 char **val; 4741 uint_t count; 4742 dladm_status_t s; 4743 4744 if (reset) { 4745 val = NULL; 4746 count = 0; 4747 } else { 4748 val = pip->pi_val; 4749 count = pip->pi_count; 4750 if (count == 0) { 4751 warn("no value specified for '%s'", 4752 pip->pi_name); 4753 status = DLADM_STATUS_BADARG; 4754 continue; 4755 } 4756 } 4757 s = dladm_set_linkprop(linkid, pip->pi_name, val, count, 4758 DLADM_OPT_ACTIVE); 4759 if (s == DLADM_STATUS_OK) { 4760 if (!temp) { 4761 s = set_linkprop_persist(linkid, 4762 pip->pi_name, val, count, reset); 4763 if (s != DLADM_STATUS_OK) 4764 status = s; 4765 } 4766 continue; 4767 } 4768 status = s; 4769 switch (s) { 4770 case DLADM_STATUS_NOTFOUND: 4771 warn("invalid link property '%s'", pip->pi_name); 4772 break; 4773 case DLADM_STATUS_BADVAL: { 4774 int j; 4775 char *ptr, *lim; 4776 char **propvals = NULL; 4777 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 4778 4779 ptr = malloc((sizeof (char *) + 4780 DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT + 4781 MAX_PROP_LINE); 4782 4783 propvals = (char **)(void *)ptr; 4784 if (propvals == NULL) 4785 die("insufficient memory"); 4786 4787 for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) { 4788 propvals[j] = ptr + sizeof (char *) * 4789 DLADM_MAX_PROP_VALCNT + 4790 j * DLADM_PROP_VAL_MAX; 4791 } 4792 s = dladm_get_linkprop(linkid, 4793 DLADM_PROP_VAL_MODIFIABLE, pip->pi_name, propvals, 4794 &valcnt); 4795 4796 if (s != DLADM_STATUS_OK) { 4797 warn_dlerr(status, "cannot set link property " 4798 "'%s' on '%s'", pip->pi_name, argv[optind]); 4799 free(propvals); 4800 break; 4801 } 4802 4803 ptr = errmsg; 4804 lim = ptr + DLADM_STRSIZE; 4805 *ptr = '\0'; 4806 for (j = 0; j < valcnt; j++) { 4807 ptr += snprintf(ptr, lim - ptr, "%s,", 4808 propvals[j]); 4809 if (ptr >= lim) 4810 break; 4811 } 4812 if (ptr > errmsg) { 4813 *(ptr - 1) = '\0'; 4814 warn("link property '%s' must be one of: %s", 4815 pip->pi_name, errmsg); 4816 } else 4817 warn("invalid link property '%s'", *val); 4818 free(propvals); 4819 break; 4820 } 4821 default: 4822 if (reset) { 4823 warn_dlerr(status, "cannot reset link property " 4824 "'%s' on '%s'", pip->pi_name, argv[optind]); 4825 } else { 4826 warn_dlerr(status, "cannot set link property " 4827 "'%s' on '%s'", pip->pi_name, argv[optind]); 4828 } 4829 break; 4830 } 4831 } 4832 done: 4833 free_props(proplist); 4834 if (status != DLADM_STATUS_OK) 4835 exit(1); 4836 } 4837 4838 static void 4839 do_set_linkprop(int argc, char **argv, const char *use) 4840 { 4841 set_linkprop(argc, argv, B_FALSE, use); 4842 } 4843 4844 static void 4845 do_reset_linkprop(int argc, char **argv, const char *use) 4846 { 4847 set_linkprop(argc, argv, B_TRUE, use); 4848 } 4849 4850 static int 4851 convert_secobj(char *buf, uint_t len, uint8_t *obj_val, uint_t *obj_lenp, 4852 dladm_secobj_class_t class) 4853 { 4854 int error = 0; 4855 4856 if (class == DLADM_SECOBJ_CLASS_WPA) { 4857 if (len < 8 || len > 63) 4858 return (EINVAL); 4859 (void) memcpy(obj_val, buf, len); 4860 *obj_lenp = len; 4861 return (error); 4862 } 4863 4864 if (class == DLADM_SECOBJ_CLASS_WEP) { 4865 switch (len) { 4866 case 5: /* ASCII key sizes */ 4867 case 13: 4868 (void) memcpy(obj_val, buf, len); 4869 *obj_lenp = len; 4870 break; 4871 case 10: /* Hex key sizes, not preceded by 0x */ 4872 case 26: 4873 error = hexascii_to_octet(buf, len, obj_val, obj_lenp); 4874 break; 4875 case 12: /* Hex key sizes, preceded by 0x */ 4876 case 28: 4877 if (strncmp(buf, "0x", 2) != 0) 4878 return (EINVAL); 4879 error = hexascii_to_octet(buf + 2, len - 2, 4880 obj_val, obj_lenp); 4881 break; 4882 default: 4883 return (EINVAL); 4884 } 4885 return (error); 4886 } 4887 4888 return (ENOENT); 4889 } 4890 4891 /* ARGSUSED */ 4892 static void 4893 defersig(int sig) 4894 { 4895 signalled = sig; 4896 } 4897 4898 static int 4899 get_secobj_from_tty(uint_t try, const char *objname, char *buf) 4900 { 4901 uint_t len = 0; 4902 int c; 4903 struct termios stored, current; 4904 void (*sigfunc)(int); 4905 4906 /* 4907 * Turn off echo -- but before we do so, defer SIGINT handling 4908 * so that a ^C doesn't leave the terminal corrupted. 4909 */ 4910 sigfunc = signal(SIGINT, defersig); 4911 (void) fflush(stdin); 4912 (void) tcgetattr(0, &stored); 4913 current = stored; 4914 current.c_lflag &= ~(ICANON|ECHO); 4915 current.c_cc[VTIME] = 0; 4916 current.c_cc[VMIN] = 1; 4917 (void) tcsetattr(0, TCSANOW, ¤t); 4918 again: 4919 if (try == 1) 4920 (void) printf(gettext("provide value for '%s': "), objname); 4921 else 4922 (void) printf(gettext("confirm value for '%s': "), objname); 4923 4924 (void) fflush(stdout); 4925 while (signalled == 0) { 4926 c = getchar(); 4927 if (c == '\n' || c == '\r') { 4928 if (len != 0) 4929 break; 4930 (void) putchar('\n'); 4931 goto again; 4932 } 4933 4934 buf[len++] = c; 4935 if (len >= DLADM_SECOBJ_VAL_MAX - 1) 4936 break; 4937 (void) putchar('*'); 4938 } 4939 4940 (void) putchar('\n'); 4941 (void) fflush(stdin); 4942 4943 /* 4944 * Restore terminal setting and handle deferred signals. 4945 */ 4946 (void) tcsetattr(0, TCSANOW, &stored); 4947 4948 (void) signal(SIGINT, sigfunc); 4949 if (signalled != 0) 4950 (void) kill(getpid(), signalled); 4951 4952 return (len); 4953 } 4954 4955 static int 4956 get_secobj_val(char *obj_name, uint8_t *obj_val, uint_t *obj_lenp, 4957 dladm_secobj_class_t class, FILE *filep) 4958 { 4959 int rval; 4960 uint_t len, len2; 4961 char buf[DLADM_SECOBJ_VAL_MAX], buf2[DLADM_SECOBJ_VAL_MAX]; 4962 4963 if (filep == NULL) { 4964 len = get_secobj_from_tty(1, obj_name, buf); 4965 rval = convert_secobj(buf, len, obj_val, obj_lenp, class); 4966 if (rval == 0) { 4967 len2 = get_secobj_from_tty(2, obj_name, buf2); 4968 if (len != len2 || memcmp(buf, buf2, len) != 0) 4969 rval = ENOTSUP; 4970 } 4971 return (rval); 4972 } else { 4973 for (;;) { 4974 if (fgets(buf, sizeof (buf), filep) == NULL) 4975 break; 4976 if (isspace(buf[0])) 4977 continue; 4978 4979 len = strlen(buf); 4980 if (buf[len - 1] == '\n') { 4981 buf[len - 1] = '\0'; 4982 len--; 4983 } 4984 break; 4985 } 4986 (void) fclose(filep); 4987 } 4988 return (convert_secobj(buf, len, obj_val, obj_lenp, class)); 4989 } 4990 4991 static boolean_t 4992 check_auth(const char *auth) 4993 { 4994 struct passwd *pw; 4995 4996 if ((pw = getpwuid(getuid())) == NULL) 4997 return (B_FALSE); 4998 4999 return (chkauthattr(auth, pw->pw_name) != 0); 5000 } 5001 5002 static void 5003 audit_secobj(char *auth, char *class, char *obj, 5004 boolean_t success, boolean_t create) 5005 { 5006 adt_session_data_t *ah; 5007 adt_event_data_t *event; 5008 au_event_t flag; 5009 char *errstr; 5010 5011 if (create) { 5012 flag = ADT_dladm_create_secobj; 5013 errstr = "ADT_dladm_create_secobj"; 5014 } else { 5015 flag = ADT_dladm_delete_secobj; 5016 errstr = "ADT_dladm_delete_secobj"; 5017 } 5018 5019 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) 5020 die("adt_start_session: %s", strerror(errno)); 5021 5022 if ((event = adt_alloc_event(ah, flag)) == NULL) 5023 die("adt_alloc_event (%s): %s", errstr, strerror(errno)); 5024 5025 /* fill in audit info */ 5026 if (create) { 5027 event->adt_dladm_create_secobj.auth_used = auth; 5028 event->adt_dladm_create_secobj.obj_class = class; 5029 event->adt_dladm_create_secobj.obj_name = obj; 5030 } else { 5031 event->adt_dladm_delete_secobj.auth_used = auth; 5032 event->adt_dladm_delete_secobj.obj_class = class; 5033 event->adt_dladm_delete_secobj.obj_name = obj; 5034 } 5035 5036 if (success) { 5037 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) { 5038 die("adt_put_event (%s, success): %s", errstr, 5039 strerror(errno)); 5040 } 5041 } else { 5042 if (adt_put_event(event, ADT_FAILURE, 5043 ADT_FAIL_VALUE_AUTH) != 0) { 5044 die("adt_put_event: (%s, failure): %s", errstr, 5045 strerror(errno)); 5046 } 5047 } 5048 5049 adt_free_event(event); 5050 (void) adt_end_session(ah); 5051 } 5052 5053 #define MAX_SECOBJS 32 5054 #define MAX_SECOBJ_NAMELEN 32 5055 static void 5056 do_create_secobj(int argc, char **argv, const char *use) 5057 { 5058 int option, rval; 5059 FILE *filep = NULL; 5060 char *obj_name = NULL; 5061 char *class_name = NULL; 5062 uint8_t obj_val[DLADM_SECOBJ_VAL_MAX]; 5063 uint_t obj_len; 5064 boolean_t success, temp = B_FALSE; 5065 dladm_status_t status; 5066 dladm_secobj_class_t class = -1; 5067 uid_t euid; 5068 5069 opterr = 0; 5070 (void) memset(obj_val, 0, DLADM_SECOBJ_VAL_MAX); 5071 while ((option = getopt_long(argc, argv, ":f:c:R:t", 5072 wifi_longopts, NULL)) != -1) { 5073 switch (option) { 5074 case 'f': 5075 euid = geteuid(); 5076 (void) seteuid(getuid()); 5077 filep = fopen(optarg, "r"); 5078 if (filep == NULL) { 5079 die("cannot open %s: %s", optarg, 5080 strerror(errno)); 5081 } 5082 (void) seteuid(euid); 5083 break; 5084 case 'c': 5085 class_name = optarg; 5086 status = dladm_str2secobjclass(optarg, &class); 5087 if (status != DLADM_STATUS_OK) { 5088 die("invalid secure object class '%s', " 5089 "valid values are: wep, wpa", optarg); 5090 } 5091 break; 5092 case 't': 5093 temp = B_TRUE; 5094 break; 5095 case 'R': 5096 status = dladm_set_rootdir(optarg); 5097 if (status != DLADM_STATUS_OK) { 5098 die_dlerr(status, "invalid directory " 5099 "specified"); 5100 } 5101 break; 5102 default: 5103 die_opterr(optopt, option, use); 5104 break; 5105 } 5106 } 5107 5108 if (optind == (argc - 1)) 5109 obj_name = argv[optind]; 5110 else if (optind != argc) 5111 usage(); 5112 5113 if (class == -1) 5114 die("secure object class required"); 5115 5116 if (obj_name == NULL) 5117 die("secure object name required"); 5118 5119 success = check_auth(LINK_SEC_AUTH); 5120 audit_secobj(LINK_SEC_AUTH, class_name, obj_name, success, B_TRUE); 5121 if (!success) 5122 die("authorization '%s' is required", LINK_SEC_AUTH); 5123 5124 rval = get_secobj_val(obj_name, obj_val, &obj_len, class, filep); 5125 if (rval != 0) { 5126 switch (rval) { 5127 case ENOENT: 5128 die("invalid secure object class"); 5129 break; 5130 case EINVAL: 5131 die("invalid secure object value"); 5132 break; 5133 case ENOTSUP: 5134 die("verification failed"); 5135 break; 5136 default: 5137 die("invalid secure object: %s", strerror(rval)); 5138 break; 5139 } 5140 } 5141 5142 status = dladm_set_secobj(obj_name, class, obj_val, obj_len, 5143 DLADM_OPT_CREATE | DLADM_OPT_ACTIVE); 5144 if (status != DLADM_STATUS_OK) { 5145 die_dlerr(status, "could not create secure object '%s'", 5146 obj_name); 5147 } 5148 if (temp) 5149 return; 5150 5151 status = dladm_set_secobj(obj_name, class, obj_val, obj_len, 5152 DLADM_OPT_PERSIST); 5153 if (status != DLADM_STATUS_OK) { 5154 warn_dlerr(status, "could not persistently create secure " 5155 "object '%s'", obj_name); 5156 } 5157 } 5158 5159 static void 5160 do_delete_secobj(int argc, char **argv, const char *use) 5161 { 5162 int i, option; 5163 boolean_t temp = B_FALSE; 5164 split_t *sp = NULL; 5165 boolean_t success; 5166 dladm_status_t status, pstatus; 5167 5168 opterr = 0; 5169 status = pstatus = DLADM_STATUS_OK; 5170 while ((option = getopt_long(argc, argv, ":R:t", 5171 wifi_longopts, NULL)) != -1) { 5172 switch (option) { 5173 case 't': 5174 temp = B_TRUE; 5175 break; 5176 case 'R': 5177 status = dladm_set_rootdir(optarg); 5178 if (status != DLADM_STATUS_OK) { 5179 die_dlerr(status, "invalid directory " 5180 "specified"); 5181 } 5182 break; 5183 default: 5184 die_opterr(optopt, option, use); 5185 break; 5186 } 5187 } 5188 5189 if (optind == (argc - 1)) { 5190 sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN); 5191 if (sp == NULL) { 5192 die("invalid secure object name(s): '%s'", 5193 argv[optind]); 5194 } 5195 } else if (optind != argc) 5196 usage(); 5197 5198 if (sp == NULL || sp->s_nfields < 1) 5199 die("secure object name required"); 5200 5201 success = check_auth(LINK_SEC_AUTH); 5202 audit_secobj(LINK_SEC_AUTH, "unknown", argv[optind], success, B_FALSE); 5203 if (!success) 5204 die("authorization '%s' is required", LINK_SEC_AUTH); 5205 5206 for (i = 0; i < sp->s_nfields; i++) { 5207 status = dladm_unset_secobj(sp->s_fields[i], DLADM_OPT_ACTIVE); 5208 if (!temp) { 5209 pstatus = dladm_unset_secobj(sp->s_fields[i], 5210 DLADM_OPT_PERSIST); 5211 } else { 5212 pstatus = DLADM_STATUS_OK; 5213 } 5214 5215 if (status != DLADM_STATUS_OK) { 5216 warn_dlerr(status, "could not delete secure object " 5217 "'%s'", sp->s_fields[i]); 5218 } 5219 if (pstatus != DLADM_STATUS_OK) { 5220 warn_dlerr(pstatus, "could not persistently delete " 5221 "secure object '%s'", sp->s_fields[i]); 5222 } 5223 } 5224 if (status != DLADM_STATUS_OK || pstatus != DLADM_STATUS_OK) 5225 exit(1); 5226 } 5227 5228 typedef struct show_secobj_state { 5229 boolean_t ss_persist; 5230 boolean_t ss_parseable; 5231 boolean_t ss_header; 5232 print_state_t ss_print; 5233 } show_secobj_state_t; 5234 5235 5236 static boolean_t 5237 show_secobj(void *arg, const char *obj_name) 5238 { 5239 uint_t obj_len = DLADM_SECOBJ_VAL_MAX; 5240 uint8_t obj_val[DLADM_SECOBJ_VAL_MAX]; 5241 char buf[DLADM_STRSIZE]; 5242 uint_t flags = 0; 5243 dladm_secobj_class_t class; 5244 show_secobj_state_t *statep = arg; 5245 dladm_status_t status; 5246 secobj_fields_buf_t sbuf; 5247 5248 if (statep->ss_persist) 5249 flags |= DLADM_OPT_PERSIST; 5250 5251 status = dladm_get_secobj(obj_name, &class, obj_val, &obj_len, flags); 5252 if (status != DLADM_STATUS_OK) 5253 die_dlerr(status, "cannot get secure object '%s'", obj_name); 5254 5255 if (statep->ss_header) { 5256 statep->ss_header = B_FALSE; 5257 if (!statep->ss_parseable) 5258 print_header(&statep->ss_print); 5259 } 5260 5261 (void) snprintf(sbuf.ss_obj_name, sizeof (sbuf.ss_obj_name), 5262 obj_name); 5263 (void) dladm_secobjclass2str(class, buf); 5264 (void) snprintf(sbuf.ss_class, sizeof (sbuf.ss_class), "%s", buf); 5265 if (getuid() == 0) { 5266 char val[DLADM_SECOBJ_VAL_MAX * 2]; 5267 uint_t len = sizeof (val); 5268 5269 if (octet_to_hexascii(obj_val, obj_len, val, &len) == 0) 5270 (void) snprintf(sbuf.ss_val, 5271 sizeof (sbuf.ss_val), "%s", val); 5272 } 5273 dladm_print_output(&statep->ss_print, statep->ss_parseable, 5274 dladm_print_field, (void *)&sbuf); 5275 return (B_TRUE); 5276 } 5277 5278 static void 5279 do_show_secobj(int argc, char **argv, const char *use) 5280 { 5281 int option; 5282 show_secobj_state_t state; 5283 dladm_status_t status; 5284 boolean_t o_arg = B_FALSE; 5285 uint_t i; 5286 split_t *sp; 5287 uint_t flags; 5288 char *fields_str = NULL; 5289 print_field_t **fields; 5290 uint_t nfields; 5291 char *def_fields = "object,class"; 5292 char *all_fields = "object,class,value"; 5293 5294 opterr = 0; 5295 bzero(&state, sizeof (state)); 5296 state.ss_parseable = B_FALSE; 5297 fields_str = def_fields; 5298 state.ss_persist = B_FALSE; 5299 state.ss_parseable = B_FALSE; 5300 state.ss_header = B_TRUE; 5301 while ((option = getopt_long(argc, argv, ":pPo:", 5302 wifi_longopts, NULL)) != -1) { 5303 switch (option) { 5304 case 'p': 5305 state.ss_parseable = B_TRUE; 5306 break; 5307 case 'P': 5308 state.ss_persist = B_TRUE; 5309 break; 5310 case 'o': 5311 o_arg = B_TRUE; 5312 if (strcasecmp(optarg, "all") == 0) 5313 fields_str = all_fields; 5314 else 5315 fields_str = optarg; 5316 break; 5317 default: 5318 die_opterr(optopt, option, use); 5319 break; 5320 } 5321 } 5322 5323 if (state.ss_parseable && !o_arg) 5324 die("option -c requires -o"); 5325 5326 if (state.ss_parseable && fields_str == all_fields) 5327 die("\"-o all\" is invalid with -p"); 5328 5329 fields = parse_output_fields(fields_str, secobj_fields, 5330 DEV_SOBJ_FIELDS, CMD_TYPE_ANY, &nfields); 5331 5332 if (fields == NULL) { 5333 die("invalid field(s) specified"); 5334 return; 5335 } 5336 state.ss_print.ps_fields = fields; 5337 state.ss_print.ps_nfields = nfields; 5338 5339 flags = state.ss_persist ? DLADM_OPT_PERSIST : 0; 5340 if (optind == (argc - 1)) { 5341 sp = split(argv[optind], MAX_SECOBJS, MAX_SECOBJ_NAMELEN); 5342 if (sp == NULL) { 5343 die("invalid secure object name(s): '%s'", 5344 argv[optind]); 5345 } 5346 for (i = 0; i < sp->s_nfields; i++) { 5347 if (!show_secobj(&state, sp->s_fields[i])) 5348 break; 5349 } 5350 splitfree(sp); 5351 return; 5352 } else if (optind != argc) 5353 usage(); 5354 5355 status = dladm_walk_secobj(&state, show_secobj, flags); 5356 if (status != DLADM_STATUS_OK) 5357 die_dlerr(status, "show-secobj"); 5358 } 5359 5360 /*ARGSUSED*/ 5361 static int 5362 i_dladm_init_linkprop(datalink_id_t linkid, void *arg) 5363 { 5364 (void) dladm_init_linkprop(linkid, B_TRUE); 5365 return (DLADM_WALK_CONTINUE); 5366 } 5367 5368 /*ARGSUSED*/ 5369 static void 5370 do_init_linkprop(int argc, char **argv, const char *use) 5371 { 5372 int option; 5373 dladm_status_t status; 5374 datalink_id_t linkid = DATALINK_ALL_LINKID; 5375 datalink_media_t media = DATALINK_ANY_MEDIATYPE; 5376 uint_t any_media = B_TRUE; 5377 5378 opterr = 0; 5379 while ((option = getopt(argc, argv, ":w")) != -1) { 5380 switch (option) { 5381 case 'w': 5382 media = DL_WIFI; 5383 any_media = B_FALSE; 5384 break; 5385 default: 5386 /* 5387 * Because init-linkprop is not a public command, 5388 * print the usage instead. 5389 */ 5390 usage(); 5391 break; 5392 } 5393 } 5394 5395 if (optind == (argc - 1)) { 5396 if ((status = dladm_name2info(argv[optind], &linkid, NULL, NULL, 5397 NULL)) != DLADM_STATUS_OK) 5398 die_dlerr(status, "link %s is not valid", argv[optind]); 5399 } else if (optind != argc) { 5400 usage(); 5401 } 5402 5403 if (linkid == DATALINK_ALL_LINKID) { 5404 /* 5405 * linkprops of links of other classes have been initialized as 5406 * part of the dladm up-xxx operation. 5407 */ 5408 (void) dladm_walk_datalink_id(i_dladm_init_linkprop, NULL, 5409 DATALINK_CLASS_PHYS, media, DLADM_OPT_PERSIST); 5410 } else { 5411 (void) dladm_init_linkprop(linkid, any_media); 5412 } 5413 } 5414 5415 /* ARGSUSED */ 5416 static void 5417 do_show_ether(int argc, char **argv, const char *use) 5418 { 5419 int option; 5420 datalink_id_t linkid; 5421 print_ether_state_t state; 5422 print_field_t **fields; 5423 boolean_t o_arg = B_FALSE; 5424 char *fields_str; 5425 uint_t nfields; 5426 char *all_fields = 5427 "link,ptype,state,auto,speed-duplex,pause,rem_fault"; 5428 char *default_fields = 5429 "link,ptype,state,auto,speed-duplex,pause"; 5430 5431 fields_str = default_fields; 5432 bzero(&state, sizeof (state)); 5433 state.es_link = NULL; 5434 state.es_parseable = B_FALSE; 5435 5436 while ((option = getopt_long(argc, argv, "o:px", 5437 showeth_lopts, NULL)) != -1) { 5438 switch (option) { 5439 case 'x': 5440 state.es_extended = B_TRUE; 5441 break; 5442 case 'p': 5443 state.es_parseable = B_TRUE; 5444 break; 5445 case 'o': 5446 o_arg = B_TRUE; 5447 if (strcasecmp(optarg, "all") == 0) 5448 fields_str = all_fields; 5449 else 5450 fields_str = optarg; 5451 break; 5452 default: 5453 die_opterr(optopt, option, use); 5454 break; 5455 } 5456 } 5457 5458 if (state.es_parseable && !o_arg) 5459 die("-p requires -o"); 5460 5461 if (state.es_parseable && fields_str == all_fields) 5462 die("\"-o all\" is invalid with -p"); 5463 5464 if (optind == (argc - 1)) 5465 state.es_link = argv[optind]; 5466 5467 fields = parse_output_fields(fields_str, ether_fields, 5468 ETHER_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 5469 5470 if (fields == NULL) { 5471 die("invalid field(s) specified"); 5472 exit(EXIT_FAILURE); 5473 } 5474 state.es_print.ps_fields = fields; 5475 state.es_print.ps_nfields = nfields; 5476 5477 if (state.es_link == NULL) { 5478 (void) dladm_walk_datalink_id(show_etherprop, &state, 5479 DATALINK_CLASS_PHYS, DL_ETHER, 5480 DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST); 5481 } else { 5482 if (!link_is_ether(state.es_link, &linkid)) { 5483 die("invalid link specified"); 5484 } 5485 (void) show_etherprop(linkid, &state); 5486 } 5487 5488 exit(DLADM_STATUS_OK); 5489 5490 } 5491 5492 static char * 5493 dladm_print_field(print_field_t *pf, void *arg) 5494 { 5495 char *value; 5496 5497 value = (char *)arg + pf->pf_offset; 5498 return (value); 5499 } 5500 5501 static int 5502 show_etherprop(datalink_id_t linkid, void *arg) 5503 { 5504 print_ether_state_t *statep = arg; 5505 char buf[DLADM_STRSIZE]; 5506 int speed; 5507 uint64_t s; 5508 uint32_t autoneg, pause, asmpause, adv_rf, cap_rf, lp_rf; 5509 ether_fields_buf_t ebuf; 5510 char speed_unit = 'M'; 5511 5512 if (dladm_datalink_id2info(linkid, NULL, NULL, NULL, 5513 ebuf.eth_link, sizeof (ebuf.eth_link)) != DLADM_STATUS_OK) { 5514 return (DLADM_WALK_CONTINUE); 5515 } 5516 5517 if (!statep->es_header && !statep->es_parseable) { 5518 print_header(&statep->es_print); 5519 statep->es_header = B_TRUE; 5520 } 5521 (void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype), 5522 "%s", "current"); 5523 5524 (void) dladm_get_single_mac_stat(linkid, "link_autoneg", 5525 KSTAT_DATA_UINT32, &autoneg); 5526 (void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg), 5527 "%s", (autoneg ? "yes" : "no")); 5528 5529 (void) dladm_get_single_mac_stat(linkid, "link_pause", 5530 KSTAT_DATA_UINT32, &pause); 5531 (void) dladm_get_single_mac_stat(linkid, "link_asmpause", 5532 KSTAT_DATA_UINT32, &asmpause); 5533 (void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause), 5534 "%s", pause_str(pause, asmpause)); 5535 5536 (void) dladm_get_single_mac_stat(linkid, "ifspeed", 5537 KSTAT_DATA_UINT64, &s); 5538 speed = (int)(s/1000000ull); 5539 5540 if (speed >= 1000) { 5541 speed = speed/1000; 5542 speed_unit = 'G'; 5543 } 5544 (void) get_linkduplex(ebuf.eth_link, B_TRUE, buf); 5545 (void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%d%c-%c", 5546 speed, speed_unit, buf[0]); 5547 5548 (void) get_linkstate(ebuf.eth_link, B_TRUE, buf); 5549 (void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), 5550 "%s", buf); 5551 5552 (void) dladm_get_single_mac_stat(linkid, "adv_rem_fault", 5553 KSTAT_DATA_UINT32, &adv_rf); 5554 (void) dladm_get_single_mac_stat(linkid, "cap_rem_fault", 5555 KSTAT_DATA_UINT32, &cap_rf); 5556 (void) dladm_get_single_mac_stat(linkid, "lp_rem_fault", 5557 KSTAT_DATA_UINT32, &lp_rf); 5558 (void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault), 5559 "%s", (adv_rf == 0 && lp_rf == 0 ? "none" : "fault")); 5560 5561 dladm_print_output(&statep->es_print, statep->es_parseable, 5562 dladm_print_field, &ebuf); 5563 5564 if (statep->es_extended) 5565 show_ether_xprop(linkid, arg); 5566 5567 return (DLADM_WALK_CONTINUE); 5568 } 5569 5570 /* ARGSUSED */ 5571 static void 5572 do_init_secobj(int argc, char **argv, const char *use) 5573 { 5574 dladm_status_t status; 5575 5576 status = dladm_init_secobj(); 5577 if (status != DLADM_STATUS_OK) 5578 die_dlerr(status, "secure object initialization failed"); 5579 } 5580 5581 /* 5582 * "-R" option support. It is used for live upgrading. Append dladm commands 5583 * to a upgrade script which will be run when the alternative root boots up: 5584 * 5585 * - If the /etc/dladm/datalink.conf file exists on the alternative root, 5586 * append dladm commands to the <altroot>/var/svc/profile/upgrade_datalink 5587 * script. This script will be run as part of the network/physical service. 5588 * We cannot defer this to /var/svc/profile/upgrade because then the 5589 * configuration will not be able to take effect before network/physical 5590 * plumbs various interfaces. 5591 * 5592 * - If the /etc/dladm/datalink.conf file does not exist on the alternative 5593 * root, append dladm commands to the <altroot>/var/svc/profile/upgrade script, 5594 * which will be run in the manifest-import service. 5595 * 5596 * Note that the SMF team is considering to move the manifest-import service 5597 * to be run at the very begining of boot. Once that is done, the need for 5598 * the /var/svc/profile/upgrade_datalink script will not exist any more. 5599 */ 5600 static void 5601 altroot_cmd(char *altroot, int argc, char *argv[]) 5602 { 5603 char path[MAXPATHLEN]; 5604 struct stat stbuf; 5605 FILE *fp; 5606 int i; 5607 5608 /* 5609 * Check for the existence of the /etc/dladm/datalink.conf 5610 * configuration file, and determine the name of script file. 5611 */ 5612 (void) snprintf(path, MAXPATHLEN, "/%s/etc/dladm/datalink.conf", 5613 altroot); 5614 if (stat(path, &stbuf) < 0) { 5615 (void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot, 5616 SMF_UPGRADE_FILE); 5617 } else { 5618 (void) snprintf(path, MAXPATHLEN, "/%s/%s", altroot, 5619 SMF_UPGRADEDATALINK_FILE); 5620 } 5621 5622 if ((fp = fopen(path, "a+")) == NULL) 5623 die("operation not supported on %s", altroot); 5624 5625 (void) fprintf(fp, "/sbin/dladm "); 5626 for (i = 0; i < argc; i++) { 5627 /* 5628 * Directly write to the file if it is not the "-R <altroot>" 5629 * option. In which case, skip it. 5630 */ 5631 if (strcmp(argv[i], "-R") != 0) 5632 (void) fprintf(fp, "%s ", argv[i]); 5633 else 5634 i ++; 5635 } 5636 (void) fprintf(fp, "%s\n", SMF_DLADM_UPGRADE_MSG); 5637 (void) fclose(fp); 5638 exit(0); 5639 } 5640 5641 /* 5642 * Convert the string to an integer. Note that the string must not have any 5643 * trailing non-integer characters. 5644 */ 5645 static boolean_t 5646 str2int(const char *str, int *valp) 5647 { 5648 int val; 5649 char *endp = NULL; 5650 5651 errno = 0; 5652 val = strtol(str, &endp, 10); 5653 if (errno != 0 || *endp != '\0') 5654 return (B_FALSE); 5655 5656 *valp = val; 5657 return (B_TRUE); 5658 } 5659 5660 /* PRINTFLIKE1 */ 5661 static void 5662 warn(const char *format, ...) 5663 { 5664 va_list alist; 5665 5666 format = gettext(format); 5667 (void) fprintf(stderr, "%s: warning: ", progname); 5668 5669 va_start(alist, format); 5670 (void) vfprintf(stderr, format, alist); 5671 va_end(alist); 5672 5673 (void) putchar('\n'); 5674 } 5675 5676 /* PRINTFLIKE2 */ 5677 static void 5678 warn_dlerr(dladm_status_t err, const char *format, ...) 5679 { 5680 va_list alist; 5681 char errmsg[DLADM_STRSIZE]; 5682 5683 format = gettext(format); 5684 (void) fprintf(stderr, gettext("%s: warning: "), progname); 5685 5686 va_start(alist, format); 5687 (void) vfprintf(stderr, format, alist); 5688 va_end(alist); 5689 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 5690 } 5691 5692 /* PRINTFLIKE2 */ 5693 static void 5694 die_dlerr(dladm_status_t err, const char *format, ...) 5695 { 5696 va_list alist; 5697 char errmsg[DLADM_STRSIZE]; 5698 5699 format = gettext(format); 5700 (void) fprintf(stderr, "%s: ", progname); 5701 5702 va_start(alist, format); 5703 (void) vfprintf(stderr, format, alist); 5704 va_end(alist); 5705 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 5706 5707 exit(EXIT_FAILURE); 5708 } 5709 5710 /* PRINTFLIKE1 */ 5711 static void 5712 die(const char *format, ...) 5713 { 5714 va_list alist; 5715 5716 format = gettext(format); 5717 (void) fprintf(stderr, "%s: ", progname); 5718 5719 va_start(alist, format); 5720 (void) vfprintf(stderr, format, alist); 5721 va_end(alist); 5722 5723 (void) putchar('\n'); 5724 exit(EXIT_FAILURE); 5725 } 5726 5727 static void 5728 die_optdup(int opt) 5729 { 5730 die("the option -%c cannot be specified more than once", opt); 5731 } 5732 5733 static void 5734 die_opterr(int opt, int opterr, const char *usage) 5735 { 5736 switch (opterr) { 5737 case ':': 5738 die("option '-%c' requires a value\nusage: %s", opt, 5739 gettext(usage)); 5740 break; 5741 case '?': 5742 default: 5743 die("unrecognized option '-%c'\nusage: %s", opt, 5744 gettext(usage)); 5745 break; 5746 } 5747 } 5748 5749 static void 5750 show_ether_xprop(datalink_id_t linkid, void *arg) 5751 { 5752 print_ether_state_t *statep = arg; 5753 char buf[DLADM_STRSIZE]; 5754 uint32_t autoneg, pause, asmpause, adv_rf, cap_rf, lp_rf; 5755 boolean_t add_comma, r1; 5756 ether_fields_buf_t ebuf; 5757 5758 /* capable */ 5759 bzero(&ebuf, sizeof (ebuf)); 5760 (void) snprintf(ebuf.eth_link, sizeof (ebuf.eth_link), ""); 5761 5762 (void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype), 5763 "%s", "capable"); 5764 (void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), ""); 5765 5766 (void) dladm_get_single_mac_stat(linkid, "cap_autoneg", 5767 KSTAT_DATA_UINT32, &autoneg); 5768 (void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg), 5769 "%s", (autoneg ? "yes" : "no")); 5770 5771 add_comma = B_FALSE; 5772 bzero(buf, sizeof (buf)); 5773 r1 = get_speed_duplex(linkid, "cap_1000", buf, "1G", B_FALSE); 5774 if (r1) 5775 add_comma = B_TRUE; 5776 r1 = get_speed_duplex(linkid, "cap_100", buf, "100M", add_comma); 5777 if (r1) 5778 add_comma = B_TRUE; 5779 r1 = get_speed_duplex(linkid, "cap_10", buf, "10M", add_comma); 5780 add_comma = B_FALSE; 5781 (void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%s", buf); 5782 5783 (void) dladm_get_single_mac_stat(linkid, "cap_pause", 5784 KSTAT_DATA_UINT32, &pause); 5785 (void) dladm_get_single_mac_stat(linkid, "cap_asmpause", 5786 KSTAT_DATA_UINT32, &asmpause); 5787 (void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause), 5788 "%s", pause_str(pause, asmpause)); 5789 5790 (void) dladm_get_single_mac_stat(linkid, "adv_rem_fault", 5791 KSTAT_DATA_UINT32, &adv_rf); 5792 (void) dladm_get_single_mac_stat(linkid, "cap_rem_fault", 5793 KSTAT_DATA_UINT32, &cap_rf); 5794 (void) dladm_get_single_mac_stat(linkid, "lp_rem_fault", 5795 KSTAT_DATA_UINT32, &lp_rf); 5796 5797 (void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault), 5798 "%s", (cap_rf ? "yes" : "no")); 5799 5800 dladm_print_output(&statep->es_print, statep->es_parseable, 5801 dladm_print_field, &ebuf); 5802 5803 /* advertised */ 5804 bzero(&ebuf, sizeof (ebuf)); 5805 (void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype), 5806 "%s", "adv"); 5807 (void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), ""); 5808 5809 (void) dladm_get_single_mac_stat(linkid, "adv_cap_autoneg", 5810 KSTAT_DATA_UINT32, &autoneg); 5811 (void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg), 5812 "%s", (autoneg ? "yes" : "no")); 5813 5814 add_comma = B_FALSE; 5815 bzero(buf, sizeof (buf)); 5816 r1 = get_speed_duplex(linkid, "adv_cap_1000", buf, "1G", add_comma); 5817 if (r1) 5818 add_comma = B_TRUE; 5819 r1 = get_speed_duplex(linkid, "adv_cap_100", buf, "100M", add_comma); 5820 if (r1) 5821 add_comma = B_TRUE; 5822 r1 = get_speed_duplex(linkid, "adv_cap_10", buf, "10M", add_comma); 5823 add_comma = B_FALSE; 5824 (void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%s", buf); 5825 5826 (void) dladm_get_single_mac_stat(linkid, "adv_cap_pause", 5827 KSTAT_DATA_UINT32, &pause); 5828 (void) dladm_get_single_mac_stat(linkid, "adv_cap_asmpause", 5829 KSTAT_DATA_UINT32, &asmpause); 5830 (void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause), 5831 "%s", pause_str(pause, asmpause)); 5832 5833 (void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault), 5834 "%s", (adv_rf ? "fault" : "none")); 5835 5836 dladm_print_output(&statep->es_print, statep->es_parseable, 5837 dladm_print_field, &ebuf); 5838 5839 /* peeradv */ 5840 bzero(&ebuf, sizeof (ebuf)); 5841 (void) snprintf(ebuf.eth_ptype, sizeof (ebuf.eth_ptype), 5842 "%s", "peeradv"); 5843 (void) snprintf(ebuf.eth_state, sizeof (ebuf.eth_state), ""); 5844 5845 (void) dladm_get_single_mac_stat(linkid, "lp_cap_autoneg", 5846 KSTAT_DATA_UINT32, &autoneg); 5847 (void) snprintf(ebuf.eth_autoneg, sizeof (ebuf.eth_autoneg), 5848 "%s", (autoneg ? "yes" : "no")); 5849 5850 add_comma = B_FALSE; 5851 bzero(buf, sizeof (buf)); 5852 r1 = get_speed_duplex(linkid, "lp_cap_1000", buf, "1G", add_comma); 5853 if (r1) 5854 add_comma = B_TRUE; 5855 r1 = get_speed_duplex(linkid, "lp_cap_100", buf, "100M", add_comma); 5856 if (r1) 5857 add_comma = B_TRUE; 5858 r1 = get_speed_duplex(linkid, "lp_cap_10", buf, "10M", add_comma); 5859 (void) snprintf(ebuf.eth_spdx, sizeof (ebuf.eth_spdx), "%s", buf); 5860 5861 (void) dladm_get_single_mac_stat(linkid, "lp_cap_pause", 5862 KSTAT_DATA_UINT32, &pause); 5863 (void) dladm_get_single_mac_stat(linkid, "lp_cap_asmpause", 5864 KSTAT_DATA_UINT32, &asmpause); 5865 (void) snprintf(ebuf.eth_pause, sizeof (ebuf.eth_pause), 5866 "%s", pause_str(pause, asmpause)); 5867 5868 (void) snprintf(ebuf.eth_rem_fault, sizeof (ebuf.eth_rem_fault), 5869 "%s", (lp_rf ? "fault" : "none")); 5870 5871 dladm_print_output(&statep->es_print, statep->es_parseable, 5872 dladm_print_field, &ebuf); 5873 } 5874 5875 static boolean_t 5876 get_speed_duplex(datalink_id_t linkid, const char *mii_prop_prefix, 5877 char *spbuf, char *sp, boolean_t add_comma) 5878 { 5879 int speed, duplex = 0; 5880 boolean_t ret = B_FALSE; 5881 char mii_prop[DLADM_STRSIZE]; 5882 5883 (void) snprintf(mii_prop, DLADM_STRSIZE, "%sfdx", mii_prop_prefix); 5884 (void) dladm_get_single_mac_stat(linkid, mii_prop, KSTAT_DATA_UINT32, 5885 &speed); 5886 if (speed) { 5887 ret = B_TRUE; 5888 duplex |= IS_FDX; 5889 } 5890 (void) snprintf(mii_prop, DLADM_STRSIZE, "%shdx", mii_prop_prefix); 5891 (void) dladm_get_single_mac_stat(linkid, mii_prop, 5892 KSTAT_DATA_UINT32, &speed); 5893 if (speed) { 5894 ret = B_TRUE; 5895 duplex |= IS_HDX; 5896 } 5897 if (ret) { 5898 if (add_comma) 5899 (void) strncat(spbuf, ",", DLADM_STRSIZE); 5900 (void) strncat(spbuf, sp, DLADM_STRSIZE); 5901 if ((duplex & (IS_FDX|IS_HDX)) == (IS_FDX|IS_HDX)) 5902 (void) strncat(spbuf, "-fh", DLADM_STRSIZE); 5903 else if (duplex & IS_FDX) 5904 (void) strncat(spbuf, "-f", DLADM_STRSIZE); 5905 else if (duplex & IS_HDX) 5906 (void) strncat(spbuf, "-h", DLADM_STRSIZE); 5907 } 5908 return (ret); 5909 } 5910 5911 static void 5912 dladm_print_output(print_state_t *statep, boolean_t parseable, 5913 print_callback_t fn, void *arg) 5914 { 5915 int i; 5916 char *value; 5917 print_field_t **pf; 5918 5919 pf = statep->ps_fields; 5920 for (i = 0; i < statep->ps_nfields; i++) { 5921 statep->ps_lastfield = (i + 1 == statep->ps_nfields); 5922 value = (*fn)(pf[i], arg); 5923 if (value != NULL) 5924 print_field(statep, pf[i], value, parseable); 5925 } 5926 (void) putchar('\n'); 5927 } 5928 5929 static void 5930 print_header(print_state_t *ps) 5931 { 5932 int i; 5933 print_field_t **pf; 5934 5935 pf = ps->ps_fields; 5936 for (i = 0; i < ps->ps_nfields; i++) { 5937 ps->ps_lastfield = (i + 1 == ps->ps_nfields); 5938 print_field(ps, pf[i], pf[i]->pf_header, B_FALSE); 5939 } 5940 (void) putchar('\n'); 5941 } 5942 5943 static char * 5944 pause_str(int pause, int asmpause) 5945 { 5946 if (pause == 1) 5947 return ("bi"); 5948 if (asmpause == 1) 5949 return ("tx"); 5950 return ("none"); 5951 } 5952 5953 static boolean_t 5954 link_is_ether(const char *link, datalink_id_t *linkid) 5955 { 5956 uint32_t media; 5957 datalink_class_t class; 5958 5959 if (dladm_name2info(link, linkid, NULL, &class, &media) == 5960 DLADM_STATUS_OK) { 5961 if (class == DATALINK_CLASS_PHYS && media == DL_ETHER) 5962 return (B_TRUE); 5963 } 5964 return (B_FALSE); 5965 } 5966