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