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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <locale.h> 28 #include <stdarg.h> 29 #include <stdlib.h> 30 #include <fcntl.h> 31 #include <string.h> 32 #include <stropts.h> 33 #include <errno.h> 34 #include <kstat.h> 35 #include <strings.h> 36 #include <getopt.h> 37 #include <unistd.h> 38 #include <priv.h> 39 #include <netdb.h> 40 #include <libintl.h> 41 #include <libdlflow.h> 42 #include <libdllink.h> 43 #include <libdlstat.h> 44 #include <sys/types.h> 45 #include <sys/socket.h> 46 #include <netinet/in.h> 47 #include <arpa/inet.h> 48 #include <sys/ethernet.h> 49 #include <inet/ip.h> 50 #include <inet/ip6.h> 51 #include <stddef.h> 52 53 #define CMD_TYPE_ANY 0xffffffff 54 #define STR_UNDEF_VAL "--" 55 56 57 /* 58 * data structures and routines for printing output. 59 */ 60 61 typedef struct print_field_s { 62 const char *pf_name; 63 const char *pf_header; 64 uint_t pf_width; 65 union { 66 uint_t _pf_index; 67 size_t _pf_offset; 68 }_pf_un; 69 #define pf_index _pf_un._pf_index 70 #define pf_offset _pf_un._pf_offset; 71 uint_t pf_cmdtype; 72 } print_field_t; 73 74 typedef struct print_state_s { 75 print_field_t **ps_fields; 76 uint_t ps_nfields; 77 boolean_t ps_lastfield; 78 uint_t ps_overflow; 79 } print_state_t; 80 81 typedef struct show_usage_state_s { 82 boolean_t us_plot; 83 boolean_t us_parseable; 84 boolean_t us_printheader; 85 boolean_t us_first; 86 print_state_t us_print; 87 } show_usage_state_t; 88 89 typedef char *(*print_callback_t)(print_field_t *, void *); 90 static print_field_t **parse_output_fields(char *, print_field_t *, int, 91 uint_t, uint_t *); 92 93 static void print_header(print_state_t *); 94 static void print_field(print_state_t *, print_field_t *, const char *, 95 boolean_t); 96 97 static void flowadm_print_output(print_state_t *, boolean_t, 98 print_callback_t, void *); 99 100 /* 101 * helper function that, when invoked as flowadm(print_field(pf, buf) 102 * prints string which is offset by pf->pf_offset within buf. 103 */ 104 static char *flowadm_print_field(print_field_t *, void *); 105 106 #define MAX_FIELD_LEN 32 107 108 typedef void cmdfunc_t(int, char **); 109 110 static cmdfunc_t do_add_flow, do_remove_flow, do_init_flow, do_show_flow; 111 static cmdfunc_t do_show_flowprop, do_set_flowprop, do_reset_flowprop; 112 static cmdfunc_t do_show_usage; 113 114 static int show_flow(dladm_flow_attr_t *, void *); 115 static int show_flows_onelink(dladm_handle_t, datalink_id_t, void *); 116 117 static void flow_stats(const char *, datalink_id_t, uint_t); 118 static void get_flow_stats(const char *, pktsum_t *); 119 static int show_flow_stats(dladm_flow_attr_t *, void *); 120 static int show_link_flow_stats(dladm_handle_t, datalink_id_t, void *); 121 122 static int remove_flow(dladm_flow_attr_t *, void *); 123 124 static int show_flowprop(dladm_flow_attr_t *, void *); 125 static void show_flowprop_one_flow(void *, const char *); 126 static int show_flowprop_onelink(dladm_handle_t, datalink_id_t, void *); 127 128 static void die(const char *, ...); 129 static void die_optdup(int); 130 static void die_opterr(int, int); 131 static void die_dlerr(dladm_status_t, const char *, ...); 132 static void warn(const char *, ...); 133 static void warn_dlerr(dladm_status_t, const char *, ...); 134 135 typedef struct cmd { 136 char *c_name; 137 void (*c_fn)(int, char **); 138 } cmd_t; 139 140 static cmd_t cmds[] = { 141 { "add-flow", do_add_flow }, 142 { "remove-flow", do_remove_flow }, 143 { "show-flowprop", do_show_flowprop }, 144 { "set-flowprop", do_set_flowprop }, 145 { "reset-flowprop", do_reset_flowprop }, 146 { "show-flow", do_show_flow }, 147 { "init-flow", do_init_flow }, 148 { "show-usage", do_show_usage } 149 }; 150 151 static const struct option longopts[] = { 152 {"link", required_argument, 0, 'l'}, 153 {"parseable", no_argument, 0, 'p'}, 154 {"statistics", no_argument, 0, 's'}, 155 {"interval", required_argument, 0, 'i'}, 156 {"temporary", no_argument, 0, 't'}, 157 {"root-dir", required_argument, 0, 'R'}, 158 { 0, 0, 0, 0 } 159 }; 160 161 static const struct option prop_longopts[] = { 162 {"link", required_argument, 0, 'l'}, 163 {"temporary", no_argument, 0, 't'}, 164 {"root-dir", required_argument, 0, 'R'}, 165 {"prop", required_argument, 0, 'p'}, 166 {"attr", required_argument, 0, 'a'}, 167 { 0, 0, 0, 0 } 168 }; 169 170 /* 171 * structures for 'flowadm show-flow' 172 */ 173 174 typedef struct show_flow_state { 175 boolean_t fs_firstonly; 176 boolean_t fs_donefirst; 177 pktsum_t fs_prevstats; 178 uint32_t fs_flags; 179 dladm_status_t fs_status; 180 print_state_t fs_print; 181 const char *fs_flow; 182 const char *fs_link; 183 boolean_t fs_parseable; 184 boolean_t fs_printheader; 185 boolean_t fs_persist; 186 boolean_t fs_stats; 187 uint64_t fs_mask; 188 } show_flow_state_t; 189 190 /* 191 * structures for 'flowadm remove-flow' 192 */ 193 194 typedef struct remove_flow_state { 195 boolean_t fs_tempop; 196 const char *fs_altroot; 197 dladm_status_t fs_status; 198 } remove_flow_state_t; 199 200 typedef struct flow_args_s { 201 const char *fa_link; 202 int fa_attrno; /* -1 indicates flow itself */ 203 uint64_t fa_mask; 204 dladm_flow_attr_t *fa_finfop; 205 dladm_status_t *fa_status; 206 boolean_t fa_parseable; 207 } flow_args_t; 208 209 #define PROTO_MAXSTR_LEN 7 210 #define PORT_MAXSTR_LEN 6 211 #define DSFIELD_MAXSTR_LEN 10 212 213 typedef struct flow_fields_buf_s 214 { 215 char flow_name[MAXFLOWNAMELEN]; 216 char flow_link[MAXLINKNAMELEN]; 217 char flow_ipaddr[INET6_ADDRSTRLEN+4]; 218 char flow_proto[PROTO_MAXSTR_LEN]; 219 char flow_port[PORT_MAXSTR_LEN]; 220 char flow_dsfield[DSFIELD_MAXSTR_LEN]; 221 } flow_fields_buf_t; 222 223 static print_field_t flow_fields[] = { 224 /* name, header, field width, index, cmdtype */ 225 { "flow", "FLOW", 11, 226 offsetof(flow_fields_buf_t, flow_name), CMD_TYPE_ANY}, 227 { "link", "LINK", 11, 228 offsetof(flow_fields_buf_t, flow_link), CMD_TYPE_ANY}, 229 { "ipaddr", "IP ADDR", 30, 230 offsetof(flow_fields_buf_t, flow_ipaddr), CMD_TYPE_ANY}, 231 { "transport", "PROTO", 6, 232 offsetof(flow_fields_buf_t, flow_proto), CMD_TYPE_ANY}, 233 { "port", "PORT", 7, 234 offsetof(flow_fields_buf_t, flow_port), CMD_TYPE_ANY}, 235 { "dsfield", "DSFLD", 9, 236 offsetof(flow_fields_buf_t, flow_dsfield), CMD_TYPE_ANY}} 237 ; 238 239 #define FLOW_MAX_FIELDS (sizeof (flow_fields) / sizeof (print_field_t)) 240 241 /* 242 * structures for 'flowadm show-flowprop' 243 */ 244 typedef enum { 245 FLOWPROP_FLOW, 246 FLOWPROP_PROPERTY, 247 FLOWPROP_VALUE, 248 FLOWPROP_DEFAULT, 249 FLOWPROP_POSSIBLE 250 } flowprop_field_index_t; 251 252 static print_field_t flowprop_fields[] = { 253 /* name, header, fieldwidth, index, cmdtype */ 254 { "flow", "FLOW", 12, FLOWPROP_FLOW, CMD_TYPE_ANY}, 255 { "property", "PROPERTY", 15, FLOWPROP_PROPERTY, CMD_TYPE_ANY}, 256 { "value", "VALUE", 14, FLOWPROP_VALUE, CMD_TYPE_ANY}, 257 { "default", "DEFAULT", 14, FLOWPROP_DEFAULT, CMD_TYPE_ANY}, 258 { "possible", "POSSIBLE", 20, FLOWPROP_POSSIBLE, CMD_TYPE_ANY}} 259 ; 260 #define FLOWPROP_MAX_FIELDS \ 261 (sizeof (flowprop_fields) / sizeof (print_field_t)) 262 263 #define MAX_PROP_LINE 512 264 265 typedef struct show_flowprop_state { 266 const char *fs_flow; 267 datalink_id_t fs_linkid; 268 char *fs_line; 269 char **fs_propvals; 270 dladm_arg_list_t *fs_proplist; 271 boolean_t fs_parseable; 272 boolean_t fs_persist; 273 boolean_t fs_header; 274 dladm_status_t fs_status; 275 dladm_status_t fs_retstatus; 276 print_state_t fs_print; 277 } show_flowprop_state_t; 278 279 typedef struct set_flowprop_state { 280 const char *fs_name; 281 boolean_t fs_reset; 282 boolean_t fs_temp; 283 dladm_status_t fs_status; 284 } set_flowprop_state_t; 285 286 typedef struct flowprop_args_s { 287 show_flowprop_state_t *fs_state; 288 char *fs_propname; 289 char *fs_flowname; 290 } flowprop_args_t; 291 292 /* 293 * structures for 'flow show-usage' 294 */ 295 296 typedef struct usage_fields_buf_s { 297 char usage_flow[12]; 298 char usage_duration[10]; 299 char usage_ipackets[9]; 300 char usage_rbytes[10]; 301 char usage_opackets[9]; 302 char usage_obytes[10]; 303 char usage_bandwidth[14]; 304 } usage_fields_buf_t; 305 306 static print_field_t usage_fields[] = { 307 /* name, header, field width, offset, cmdtype */ 308 { "flow", "FLOW", 12, 309 offsetof(usage_fields_buf_t, usage_flow), CMD_TYPE_ANY}, 310 { "duration", "DURATION", 10, 311 offsetof(usage_fields_buf_t, usage_duration), CMD_TYPE_ANY}, 312 { "ipackets", "IPACKETS", 9, 313 offsetof(usage_fields_buf_t, usage_ipackets), CMD_TYPE_ANY}, 314 { "rbytes", "RBYTES", 10, 315 offsetof(usage_fields_buf_t, usage_rbytes), CMD_TYPE_ANY}, 316 { "opackets", "OPACKETS", 9, 317 offsetof(usage_fields_buf_t, usage_opackets), CMD_TYPE_ANY}, 318 { "obytes", "OBYTES", 10, 319 offsetof(usage_fields_buf_t, usage_obytes), CMD_TYPE_ANY}, 320 { "bandwidth", "BANDWIDTH", 14, 321 offsetof(usage_fields_buf_t, usage_bandwidth), CMD_TYPE_ANY}} 322 ; 323 324 #define USAGE_MAX_FIELDS (sizeof (usage_fields) / sizeof (print_field_t)) 325 326 /* 327 * structures for 'dladm show-usage link' 328 */ 329 330 typedef struct usage_l_fields_buf_s { 331 char usage_l_flow[12]; 332 char usage_l_stime[13]; 333 char usage_l_etime[13]; 334 char usage_l_rbytes[8]; 335 char usage_l_obytes[8]; 336 char usage_l_bandwidth[14]; 337 } usage_l_fields_buf_t; 338 339 static print_field_t usage_l_fields[] = { 340 /* name, header, field width, offset, cmdtype */ 341 { "flow", "FLOW", 12, 342 offsetof(usage_l_fields_buf_t, usage_l_flow), CMD_TYPE_ANY}, 343 { "start", "START", 13, 344 offsetof(usage_l_fields_buf_t, usage_l_stime), CMD_TYPE_ANY}, 345 { "end", "END", 13, 346 offsetof(usage_l_fields_buf_t, usage_l_etime), CMD_TYPE_ANY}, 347 { "rbytes", "RBYTES", 8, 348 offsetof(usage_l_fields_buf_t, usage_l_rbytes), CMD_TYPE_ANY}, 349 { "obytes", "OBYTES", 8, 350 offsetof(usage_l_fields_buf_t, usage_l_obytes), CMD_TYPE_ANY}, 351 { "bandwidth", "BANDWIDTH", 14, 352 offsetof(usage_l_fields_buf_t, usage_l_bandwidth), CMD_TYPE_ANY}} 353 ; 354 355 #define USAGE_L_MAX_FIELDS \ 356 (sizeof (usage_l_fields) /sizeof (print_field_t)) 357 358 #define PRI_HI 100 359 #define PRI_LO 10 360 #define PRI_NORM 50 361 362 #define FLOWADM_CONF "/etc/dladm/flowadm.conf" 363 #define BLANK_LINE(s) ((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n')) 364 365 static char *progname; 366 367 boolean_t t_arg = B_FALSE; /* changes are persistent */ 368 char *altroot = NULL; 369 370 /* 371 * Handle to libdladm. Opened in main() before the sub-command 372 * specific function is called. 373 */ 374 static dladm_handle_t handle = NULL; 375 376 static const char *attr_table[] = 377 {"local_ip", "remote_ip", "transport", "local_port", "dsfield"}; 378 379 #define NATTR (sizeof (attr_table)/sizeof (char *)) 380 381 static void 382 usage(void) 383 { 384 (void) fprintf(stderr, gettext("usage: flowadm <subcommand>" 385 " <args>...\n" 386 " add-flow [-t] -l <link> -a <attr>=<value>[,...]\n" 387 "\t\t [-p <prop>=<value>,...] <flow>\n" 388 " remove-flow [-t] {-l <link> | <flow>}\n" 389 " show-flow [-p] [-s [-i <interval>]] [-l <link>] " 390 "[<flow>]\n\n" 391 " set-flowprop [-t] -p <prop>=<value>[,...] <flow>\n" 392 " reset-flowprop [-t] [-p <prop>,...] <flow>\n" 393 " show-flowprop [-cP] [-l <link>] [-p <prop>,...] " 394 "[<flow>]\n\n" 395 " show-usage [-d|-p -F <format>] " 396 "[-s <DD/MM/YYYY,HH:MM:SS>]\n" 397 "\t\t [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> [<flow>]\n")); 398 399 /* close dladm handle if it was opened */ 400 if (handle != NULL) 401 dladm_close(handle); 402 403 exit(1); 404 } 405 406 int 407 main(int argc, char *argv[]) 408 { 409 int i, arglen, cmdlen; 410 cmd_t *cmdp; 411 dladm_status_t status; 412 413 (void) setlocale(LC_ALL, ""); 414 #if !defined(TEXT_DOMAIN) 415 #define TEXT_DOMAIN "SYS_TEST" 416 #endif 417 (void) textdomain(TEXT_DOMAIN); 418 419 progname = argv[0]; 420 421 if (argc < 2) 422 usage(); 423 424 for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) { 425 cmdp = &cmds[i]; 426 arglen = strlen(argv[1]); 427 cmdlen = strlen(cmdp->c_name); 428 if ((arglen == cmdlen) && (strncmp(argv[1], cmdp->c_name, 429 cmdlen) == 0)) { 430 /* Open the libdladm handle */ 431 if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) { 432 die_dlerr(status, 433 "could not open /dev/dld"); 434 } 435 436 cmdp->c_fn(argc - 1, &argv[1]); 437 438 dladm_close(handle); 439 exit(0); 440 } 441 } 442 443 (void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"), 444 progname, argv[1]); 445 usage(); 446 447 return (0); 448 } 449 450 static const char * 451 match_attr(char *attr) 452 { 453 int i; 454 455 for (i = 0; i < NATTR; i++) { 456 if (strlen(attr) == strlen(attr_table[i]) && 457 strncmp(attr, attr_table[i], strlen(attr_table[i])) == 0) { 458 return (attr); 459 } 460 } 461 return (NULL); 462 } 463 464 /* ARGSUSED */ 465 static void 466 do_init_flow(int argc, char *argv[]) 467 { 468 dladm_status_t status; 469 470 status = dladm_flow_init(handle); 471 if (status != DLADM_STATUS_OK) 472 die_dlerr(status, "flows initialization failed"); 473 } 474 475 /* ARGSUSED */ 476 static int 477 show_usage_date(dladm_usage_t *usage, void *arg) 478 { 479 480 time_t stime; 481 char timebuf[20]; 482 483 stime = usage->du_stime; 484 (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y", 485 localtime(&stime)); 486 (void) printf("%s\n", timebuf); 487 488 return (DLADM_STATUS_OK); 489 } 490 491 static int 492 show_usage_time(dladm_usage_t *usage, void *arg) 493 { 494 show_usage_state_t *state = (show_usage_state_t *)arg; 495 char buf[DLADM_STRSIZE]; 496 usage_l_fields_buf_t ubuf; 497 time_t time; 498 double bw; 499 500 if (state->us_plot) { 501 if (!state->us_printheader) { 502 if (state->us_first) { 503 (void) printf("# Time"); 504 state->us_first = B_FALSE; 505 } 506 (void) printf(" %s", usage->du_name); 507 if (usage->du_last) { 508 (void) printf("\n"); 509 state->us_first = B_TRUE; 510 state->us_printheader = B_TRUE; 511 } 512 } else { 513 if (state->us_first) { 514 time = usage->du_etime; 515 (void) strftime(buf, sizeof (buf), "%T", 516 localtime(&time)); 517 state->us_first = B_FALSE; 518 (void) printf("%s", buf); 519 } 520 bw = (double)usage->du_bandwidth/1000; 521 (void) printf(" %.2f", bw); 522 if (usage->du_last) { 523 (void) printf("\n"); 524 state->us_first = B_TRUE; 525 } 526 } 527 return (DLADM_STATUS_OK); 528 } 529 530 bzero(&ubuf, sizeof (ubuf)); 531 532 (void) snprintf(ubuf.usage_l_flow, sizeof (ubuf.usage_l_flow), "%s", 533 usage->du_name); 534 time = usage->du_stime; 535 (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); 536 (void) snprintf(ubuf.usage_l_stime, sizeof (ubuf.usage_l_stime), "%s", 537 buf); 538 time = usage->du_etime; 539 (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); 540 (void) snprintf(ubuf.usage_l_etime, sizeof (ubuf.usage_l_etime), "%s", 541 buf); 542 (void) snprintf(ubuf.usage_l_rbytes, sizeof (ubuf.usage_l_rbytes), 543 "%llu", usage->du_rbytes); 544 (void) snprintf(ubuf.usage_l_obytes, sizeof (ubuf.usage_l_obytes), 545 "%llu", usage->du_obytes); 546 (void) snprintf(ubuf.usage_l_bandwidth, sizeof (ubuf.usage_l_bandwidth), 547 "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf)); 548 549 if (!state->us_parseable && !state->us_printheader) { 550 print_header(&state->us_print); 551 state->us_printheader = B_TRUE; 552 } 553 554 flowadm_print_output(&state->us_print, state->us_parseable, 555 flowadm_print_field, (void *)&ubuf); 556 557 return (DLADM_STATUS_OK); 558 } 559 560 static int 561 show_usage_res(dladm_usage_t *usage, void *arg) 562 { 563 show_usage_state_t *state = (show_usage_state_t *)arg; 564 char buf[DLADM_STRSIZE]; 565 usage_fields_buf_t ubuf; 566 567 bzero(&ubuf, sizeof (ubuf)); 568 569 (void) snprintf(ubuf.usage_flow, sizeof (ubuf.usage_flow), "%s", 570 usage->du_name); 571 (void) snprintf(ubuf.usage_duration, sizeof (ubuf.usage_duration), 572 "%llu", usage->du_duration); 573 (void) snprintf(ubuf.usage_ipackets, sizeof (ubuf.usage_ipackets), 574 "%llu", usage->du_ipackets); 575 (void) snprintf(ubuf.usage_rbytes, sizeof (ubuf.usage_rbytes), 576 "%llu", usage->du_rbytes); 577 (void) snprintf(ubuf.usage_opackets, sizeof (ubuf.usage_opackets), 578 "%llu", usage->du_opackets); 579 (void) snprintf(ubuf.usage_obytes, sizeof (ubuf.usage_obytes), 580 "%llu", usage->du_obytes); 581 (void) snprintf(ubuf.usage_bandwidth, sizeof (ubuf.usage_bandwidth), 582 "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf)); 583 584 if (!state->us_parseable && !state->us_printheader) { 585 print_header(&state->us_print); 586 state->us_printheader = B_TRUE; 587 } 588 589 flowadm_print_output(&state->us_print, state->us_parseable, 590 flowadm_print_field, (void *)&ubuf); 591 592 return (DLADM_STATUS_OK); 593 } 594 595 static boolean_t 596 valid_formatspec(char *formatspec_str) 597 { 598 if (strcmp(formatspec_str, "gnuplot") == 0) 599 return (B_TRUE); 600 return (B_FALSE); 601 } 602 603 /* ARGSUSED */ 604 static void 605 do_show_usage(int argc, char *argv[]) 606 { 607 char *file = NULL; 608 int opt; 609 dladm_status_t status; 610 boolean_t d_arg = B_FALSE; 611 boolean_t p_arg = B_FALSE; 612 char *stime = NULL; 613 char *etime = NULL; 614 char *resource = NULL; 615 show_usage_state_t state; 616 boolean_t o_arg = B_FALSE; 617 boolean_t F_arg = B_FALSE; 618 char *fields_str = NULL; 619 char *formatspec_str = NULL; 620 print_field_t **fields; 621 uint_t nfields; 622 char *all_fields = 623 "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth"; 624 char *all_l_fields = 625 "flow,start,end,rbytes,obytes,bandwidth"; 626 627 bzero(&state, sizeof (show_usage_state_t)); 628 state.us_parseable = B_FALSE; 629 state.us_printheader = B_FALSE; 630 state.us_plot = B_FALSE; 631 state.us_first = B_TRUE; 632 633 while ((opt = getopt(argc, argv, "dps:e:o:f:F:")) != -1) { 634 switch (opt) { 635 case 'd': 636 d_arg = B_TRUE; 637 break; 638 case 'p': 639 state.us_plot = p_arg = B_TRUE; 640 break; 641 case 'f': 642 file = optarg; 643 break; 644 case 's': 645 stime = optarg; 646 break; 647 case 'e': 648 etime = optarg; 649 break; 650 case 'o': 651 o_arg = B_TRUE; 652 fields_str = optarg; 653 break; 654 case 'F': 655 F_arg = B_TRUE; 656 formatspec_str = optarg; 657 break; 658 default: 659 die_opterr(optopt, opt); 660 } 661 } 662 663 if (file == NULL) 664 die("show-usage requires a file"); 665 666 if (optind == (argc-1)) { 667 resource = argv[optind]; 668 } 669 670 if (resource == NULL && stime == NULL && etime == NULL) { 671 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 672 fields_str = all_fields; 673 fields = parse_output_fields(fields_str, usage_fields, 674 USAGE_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 675 } else { 676 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 677 fields_str = all_l_fields; 678 fields = parse_output_fields(fields_str, usage_l_fields, 679 USAGE_L_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 680 } 681 682 if (fields == NULL) { 683 die("invalid fields(s) specified"); 684 return; 685 } 686 state.us_print.ps_fields = fields; 687 state.us_print.ps_nfields = nfields; 688 689 if (p_arg && d_arg) 690 die("plot and date options are incompatible"); 691 692 if (p_arg && !F_arg) 693 die("specify format speicifier: -F <format>"); 694 695 if (F_arg && valid_formatspec(formatspec_str) == B_FALSE) 696 die("Format specifier %s not supported", formatspec_str); 697 698 if (d_arg) { 699 /* Print log dates */ 700 status = dladm_usage_dates(show_usage_date, 701 DLADM_LOGTYPE_FLOW, file, resource, &state); 702 } else if (resource == NULL && stime == NULL && etime == NULL && 703 !p_arg) { 704 /* Print summary */ 705 status = dladm_usage_summary(show_usage_res, 706 DLADM_LOGTYPE_FLOW, file, &state); 707 } else if (resource != NULL) { 708 /* Print log entries for named resource */ 709 status = dladm_walk_usage_res(show_usage_time, 710 DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state); 711 } else { 712 /* Print time and information for each link */ 713 status = dladm_walk_usage_time(show_usage_time, 714 DLADM_LOGTYPE_FLOW, file, stime, etime, &state); 715 } 716 717 if (status != DLADM_STATUS_OK) 718 die_dlerr(status, "show-usage"); 719 } 720 721 static void 722 do_add_flow(int argc, char *argv[]) 723 { 724 char devname[MAXLINKNAMELEN]; 725 char *name = NULL; 726 uint_t index; 727 datalink_id_t linkid; 728 729 char option; 730 boolean_t l_arg = B_FALSE; 731 dladm_arg_list_t *proplist = NULL; 732 dladm_arg_list_t *attrlist = NULL; 733 dladm_status_t status; 734 735 while ((option = getopt_long(argc, argv, "tR:l:a:p:", 736 prop_longopts, NULL)) != -1) { 737 switch (option) { 738 case 't': 739 t_arg = B_TRUE; 740 break; 741 case 'R': 742 altroot = optarg; 743 break; 744 case 'l': 745 if (strlcpy(devname, optarg, 746 MAXLINKNAMELEN) >= MAXLINKNAMELEN) { 747 die("link name too long"); 748 } 749 if (dladm_name2info(handle, devname, &linkid, NULL, 750 NULL, NULL) != DLADM_STATUS_OK) 751 die("invalid link '%s'", devname); 752 l_arg = B_TRUE; 753 break; 754 case 'a': 755 if (dladm_parse_flow_attrs(optarg, &attrlist, B_FALSE) 756 != DLADM_STATUS_OK) 757 die("invalid flow attribute specified"); 758 break; 759 case 'p': 760 if (dladm_parse_flow_props(optarg, &proplist, B_FALSE) 761 != DLADM_STATUS_OK) 762 die("invalid flow property specified"); 763 break; 764 default: 765 die_opterr(optopt, option); 766 } 767 } 768 if (!l_arg) { 769 die("link is required"); 770 } 771 772 opterr = 0; 773 index = optind; 774 775 if ((index != (argc - 1)) || match_attr(argv[index]) != NULL) { 776 die("flow name is required"); 777 } else { 778 /* get flow name; required last argument */ 779 if (strlen(argv[index]) >= MAXFLOWNAMELEN) 780 die("flow name too long"); 781 name = argv[index]; 782 } 783 784 status = dladm_flow_add(handle, linkid, attrlist, proplist, name, 785 t_arg, altroot); 786 if (status != DLADM_STATUS_OK) 787 die_dlerr(status, "add flow failed"); 788 789 dladm_free_attrs(attrlist); 790 dladm_free_props(proplist); 791 } 792 793 static void 794 do_remove_flow(int argc, char *argv[]) 795 { 796 char option; 797 char *flowname = NULL; 798 char linkname[MAXLINKNAMELEN]; 799 datalink_id_t linkid = DATALINK_ALL_LINKID; 800 boolean_t l_arg = B_FALSE; 801 remove_flow_state_t state; 802 dladm_status_t status; 803 804 bzero(&state, sizeof (state)); 805 806 opterr = 0; 807 while ((option = getopt_long(argc, argv, ":tR:l:", 808 longopts, NULL)) != -1) { 809 switch (option) { 810 case 't': 811 t_arg = B_TRUE; 812 break; 813 case 'R': 814 altroot = optarg; 815 break; 816 case 'l': 817 if (strlcpy(linkname, optarg, 818 MAXLINKNAMELEN) >= MAXLINKNAMELEN) { 819 die("link name too long"); 820 } 821 if (dladm_name2info(handle, linkname, &linkid, NULL, 822 NULL, NULL) != DLADM_STATUS_OK) { 823 die("invalid link '%s'", linkname); 824 } 825 l_arg = B_TRUE; 826 break; 827 default: 828 die_opterr(optopt, option); 829 break; 830 } 831 } 832 833 /* when link not specified get flow name */ 834 if (!l_arg) { 835 if (optind != (argc-1)) { 836 usage(); 837 } else { 838 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 839 die("flow name too long"); 840 flowname = argv[optind]; 841 } 842 status = dladm_flow_remove(handle, flowname, t_arg, altroot); 843 } else { 844 /* if link is specified then flow name should not be there */ 845 if (optind == argc-1) 846 usage(); 847 /* walk the link to find flows and remove them */ 848 state.fs_tempop = t_arg; 849 state.fs_altroot = altroot; 850 state.fs_status = DLADM_STATUS_OK; 851 status = dladm_walk_flow(remove_flow, handle, linkid, &state, 852 B_FALSE); 853 /* 854 * check if dladm_walk_flow terminated early and see if the 855 * walker function as any status for us 856 */ 857 if (status == DLADM_STATUS_OK) 858 status = state.fs_status; 859 } 860 861 if (status != DLADM_STATUS_OK) 862 die_dlerr(status, "remove flow failed"); 863 } 864 865 /* 866 * Walker function for removing a flow through dladm_walk_flow(); 867 */ 868 static int 869 remove_flow(dladm_flow_attr_t *attr, void *arg) 870 { 871 remove_flow_state_t *state = (remove_flow_state_t *)arg; 872 873 state->fs_status = dladm_flow_remove(handle, attr->fa_flowname, 874 state->fs_tempop, state->fs_altroot); 875 876 if (state->fs_status == DLADM_STATUS_OK) 877 return (DLADM_WALK_CONTINUE); 878 else 879 return (DLADM_WALK_TERMINATE); 880 } 881 882 static char * 883 flowadm_print_field(print_field_t *pf, void *arg) 884 { 885 char *value; 886 887 value = (char *)arg + pf->pf_offset; 888 return (value); 889 } 890 891 /*ARGSUSED*/ 892 static dladm_status_t 893 print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr, 894 flow_fields_buf_t *fbuf) 895 { 896 char link[MAXLINKNAMELEN]; 897 dladm_status_t status; 898 899 if ((status = dladm_datalink_id2info(handle, attr->fa_linkid, NULL, 900 NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) { 901 return (status); 902 } 903 904 (void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name), 905 "%s", attr->fa_flowname); 906 (void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link), 907 "%s", link); 908 909 (void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr, 910 sizeof (fbuf->flow_ipaddr)); 911 (void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto, 912 sizeof (fbuf->flow_proto)); 913 (void) dladm_flow_attr_port2str(attr, fbuf->flow_port, 914 sizeof (fbuf->flow_port)); 915 (void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield, 916 sizeof (fbuf->flow_dsfield)); 917 918 return (DLADM_STATUS_OK); 919 } 920 921 /* 922 * Walker function for showing flow attributes through dladm_walk_flow(). 923 */ 924 static int 925 show_flow(dladm_flow_attr_t *attr, void *arg) 926 { 927 show_flow_state_t *statep = arg; 928 dladm_status_t status; 929 flow_fields_buf_t fbuf; 930 931 /* 932 * first get all the flow attributes into fbuf; 933 */ 934 bzero(&fbuf, sizeof (fbuf)); 935 status = print_flow(statep, attr, &fbuf); 936 937 if (status != DLADM_STATUS_OK) 938 goto done; 939 940 if (!statep->fs_parseable && !statep->fs_printheader) { 941 print_header(&statep->fs_print); 942 statep->fs_printheader = B_TRUE; 943 } 944 945 flowadm_print_output(&statep->fs_print, statep->fs_parseable, 946 flowadm_print_field, (void *)&fbuf); 947 948 done: 949 statep->fs_status = status; 950 return (DLADM_WALK_CONTINUE); 951 } 952 953 static void 954 show_one_flow(void *arg, const char *name) 955 { 956 dladm_flow_attr_t attr; 957 958 if (dladm_flow_info(handle, name, &attr) != DLADM_STATUS_OK) 959 die("invalid flow: '%s'", name); 960 else 961 (void) show_flow(&attr, arg); 962 } 963 964 /* 965 * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to 966 * dladm_walk_datalink_id(). Used for showing flow attributes for 967 * all flows on all links. 968 */ 969 static int 970 show_flows_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg) 971 { 972 show_flow_state_t *state = arg; 973 974 (void) dladm_walk_flow(show_flow, dh, linkid, arg, state->fs_persist); 975 976 return (DLADM_WALK_CONTINUE); 977 } 978 979 static void 980 get_flow_stats(const char *flowname, pktsum_t *stats) 981 { 982 kstat_ctl_t *kcp; 983 kstat_t *ksp; 984 985 bzero(stats, sizeof (*stats)); 986 987 if ((kcp = kstat_open()) == NULL) { 988 warn("kstat open operation failed"); 989 return; 990 } 991 992 ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow"); 993 994 if (ksp != NULL) 995 dladm_get_stats(kcp, ksp, stats); 996 997 (void) kstat_close(kcp); 998 } 999 1000 /* ARGSUSED */ 1001 static int 1002 show_flow_stats(dladm_flow_attr_t *attr, void *arg) 1003 { 1004 show_flow_state_t *state = (show_flow_state_t *)arg; 1005 const char *name = attr->fa_flowname; 1006 pktsum_t stats, diff_stats; 1007 1008 if (state->fs_firstonly) { 1009 if (state->fs_donefirst) 1010 return (DLADM_WALK_TERMINATE); 1011 state->fs_donefirst = B_TRUE; 1012 } else { 1013 bzero(&state->fs_prevstats, sizeof (state->fs_prevstats)); 1014 } 1015 1016 get_flow_stats(name, &stats); 1017 dladm_stats_diff(&diff_stats, &stats, &state->fs_prevstats); 1018 1019 (void) printf("%-12s", name); 1020 (void) printf("%-10llu", diff_stats.ipackets); 1021 (void) printf("%-12llu", diff_stats.rbytes); 1022 (void) printf("%-8llu", diff_stats.ierrors); 1023 (void) printf("%-10llu", diff_stats.opackets); 1024 (void) printf("%-12llu", diff_stats.obytes); 1025 (void) printf("%-8llu\n", diff_stats.oerrors); 1026 1027 state->fs_prevstats = stats; 1028 1029 return (DLADM_WALK_CONTINUE); 1030 } 1031 1032 /* 1033 * Wrapper of dladm_walk_flow(show_flow,...) to make it usable for 1034 * dladm_walk_datalink_id(). Used for showing flow stats for 1035 * all flows on all links. 1036 */ 1037 static int 1038 show_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg) 1039 { 1040 if (dladm_walk_flow(show_flow_stats, dh, linkid, arg, B_FALSE) 1041 == DLADM_STATUS_OK) 1042 return (DLADM_WALK_CONTINUE); 1043 else 1044 return (DLADM_WALK_TERMINATE); 1045 } 1046 1047 /* ARGSUSED */ 1048 static void 1049 flow_stats(const char *flow, datalink_id_t linkid, uint_t interval) 1050 { 1051 show_flow_state_t state; 1052 dladm_flow_attr_t attr; 1053 1054 if (flow != NULL && 1055 dladm_flow_info(handle, flow, &attr) != DLADM_STATUS_OK) 1056 die("invalid flow %s", flow); 1057 1058 bzero(&state, sizeof (state)); 1059 1060 /* 1061 * If an interval is specified, continuously show the stats 1062 * for only the first flow. 1063 */ 1064 state.fs_firstonly = (interval != 0); 1065 1066 for (;;) { 1067 if (!state.fs_donefirst) 1068 (void) printf("%-12s%-10s%-12s%-8s%-10s%-12s%-8s\n", 1069 "FLOW", "IPACKETS", "RBYTES", "IERRORS", 1070 "OPACKETS", "OBYTES", "OERRORS"); 1071 1072 state.fs_donefirst = B_FALSE; 1073 1074 /* Show stats for named flow */ 1075 if (flow != NULL) { 1076 state.fs_flow = flow; 1077 (void) show_flow_stats(&attr, &state); 1078 1079 /* Show all stats on a link */ 1080 } else if (linkid != DATALINK_INVALID_LINKID) { 1081 (void) dladm_walk_flow(show_flow_stats, handle, linkid, 1082 &state, B_FALSE); 1083 1084 /* Show all stats by datalink */ 1085 } else { 1086 (void) dladm_walk_datalink_id(show_link_flow_stats, 1087 handle, &state, DATALINK_CLASS_ALL, 1088 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 1089 } 1090 1091 if (interval == 0) 1092 break; 1093 1094 (void) sleep(interval); 1095 } 1096 } 1097 1098 static void 1099 do_show_flow(int argc, char *argv[]) 1100 { 1101 char flowname[MAXFLOWNAMELEN]; 1102 char linkname[MAXLINKNAMELEN]; 1103 datalink_id_t linkid = DATALINK_ALL_LINKID; 1104 int option; 1105 boolean_t s_arg = B_FALSE; 1106 boolean_t S_arg = B_FALSE; 1107 boolean_t i_arg = B_FALSE; 1108 boolean_t l_arg = B_FALSE; 1109 boolean_t o_arg = B_FALSE; 1110 uint32_t interval = 0; 1111 char *endp = NULL; 1112 show_flow_state_t state; 1113 char *fields_str = NULL; 1114 print_field_t **fields; 1115 uint_t nfields; 1116 char *all_fields = 1117 "flow,link,ipaddr,transport,port,dsfield"; 1118 1119 bzero(&state, sizeof (state)); 1120 1121 opterr = 0; 1122 while ((option = getopt_long(argc, argv, ":pPsSi:l:o:", 1123 longopts, NULL)) != -1) { 1124 switch (option) { 1125 case 'p': 1126 state.fs_parseable = B_TRUE; 1127 break; 1128 case 'P': 1129 state.fs_persist = B_TRUE; 1130 break; 1131 case 's': 1132 if (s_arg) 1133 die_optdup(option); 1134 1135 s_arg = B_TRUE; 1136 break; 1137 case 'S': 1138 if (S_arg) 1139 die_optdup(option); 1140 1141 S_arg = B_TRUE; 1142 break; 1143 case 'o': 1144 if (o_arg) 1145 die_optdup(option); 1146 1147 o_arg = B_TRUE; 1148 fields_str = optarg; 1149 break; 1150 case 'i': 1151 if (i_arg) 1152 die_optdup(option); 1153 1154 i_arg = B_TRUE; 1155 1156 errno = 0; 1157 interval = (int)strtol(optarg, &endp, 10); 1158 if (errno != 0 || interval == 0 || *endp != '\0') 1159 die("invalid interval value" " '%d'\n", 1160 interval); 1161 break; 1162 case 'l': 1163 if (strlcpy(linkname, optarg, MAXLINKNAMELEN) 1164 >= MAXLINKNAMELEN) 1165 die("link name too long\n"); 1166 if (dladm_name2info(handle, linkname, &linkid, NULL, 1167 NULL, NULL) != DLADM_STATUS_OK) 1168 die("invalid link '%s'", linkname); 1169 l_arg = B_TRUE; 1170 break; 1171 default: 1172 die_opterr(optopt, option); 1173 break; 1174 } 1175 } 1176 if (i_arg && !(s_arg || S_arg)) 1177 die("the -i option can be used only with -s or -S"); 1178 1179 if (s_arg && S_arg) 1180 die("the -s option cannot be used with -S"); 1181 1182 /* get flow name (optional last argument */ 1183 if (optind == (argc-1)) { 1184 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) 1185 >= MAXFLOWNAMELEN) 1186 die("flow name too long"); 1187 state.fs_flow = flowname; 1188 } 1189 1190 if (s_arg) { 1191 flow_stats(state.fs_flow, linkid, interval); 1192 return; 1193 } 1194 1195 if (S_arg) { 1196 dladm_continuous(handle, linkid, state.fs_flow, interval, 1197 FLOW_REPORT); 1198 return; 1199 } 1200 1201 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 1202 fields_str = all_fields; 1203 1204 fields = parse_output_fields(fields_str, flow_fields, FLOW_MAX_FIELDS, 1205 CMD_TYPE_ANY, &nfields); 1206 1207 if (fields == NULL) { 1208 die("invalid fields(s) specified"); 1209 return; 1210 } 1211 1212 state.fs_print.ps_fields = fields; 1213 state.fs_print.ps_nfields = nfields; 1214 1215 /* Show attributes of one flow */ 1216 if (state.fs_flow != NULL) { 1217 show_one_flow(&state, state.fs_flow); 1218 1219 /* Show attributes of flows on one link */ 1220 } else if (l_arg) { 1221 (void) show_flows_onelink(handle, linkid, &state); 1222 1223 /* Show attributes of all flows on all links */ 1224 } else { 1225 (void) dladm_walk_datalink_id(show_flows_onelink, handle, 1226 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1227 DLADM_OPT_ACTIVE); 1228 } 1229 } 1230 1231 static dladm_status_t 1232 set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val, 1233 uint_t val_cnt, boolean_t reset) 1234 { 1235 dladm_status_t status; 1236 char *errprop; 1237 1238 status = dladm_set_flowprop(handle, flow, prop_name, prop_val, val_cnt, 1239 DLADM_OPT_PERSIST, &errprop); 1240 1241 if (status != DLADM_STATUS_OK) { 1242 warn_dlerr(status, "cannot persistently %s flow " 1243 "property '%s' on '%s'", reset? "reset": "set", 1244 errprop, flow); 1245 } 1246 return (status); 1247 } 1248 1249 static void 1250 set_flowprop(int argc, char **argv, boolean_t reset) 1251 { 1252 int i, option; 1253 char errmsg[DLADM_STRSIZE]; 1254 const char *flow = NULL; 1255 dladm_arg_list_t *proplist = NULL; 1256 boolean_t temp = B_FALSE; 1257 dladm_status_t status = DLADM_STATUS_OK; 1258 1259 opterr = 0; 1260 while ((option = getopt_long(argc, argv, ":p:R:t", 1261 prop_longopts, NULL)) != -1) { 1262 switch (option) { 1263 case 'p': 1264 if (dladm_parse_flow_props(optarg, &proplist, reset) 1265 != DLADM_STATUS_OK) 1266 die("invalid flow property specified"); 1267 break; 1268 case 't': 1269 temp = B_TRUE; 1270 break; 1271 case 'R': 1272 status = dladm_set_rootdir(optarg); 1273 if (status != DLADM_STATUS_OK) { 1274 die_dlerr(status, "invalid directory " 1275 "specified"); 1276 } 1277 break; 1278 default: 1279 die_opterr(optopt, option); 1280 break; 1281 } 1282 } 1283 1284 if (optind == (argc - 1)) { 1285 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 1286 die("flow name too long"); 1287 flow = argv[optind]; 1288 } else if (optind != argc) { 1289 usage(); 1290 } 1291 if (flow == NULL) 1292 die("flow name must be specified"); 1293 1294 if (proplist == NULL) { 1295 char *errprop; 1296 1297 if (!reset) 1298 die("flow property must be specified"); 1299 1300 status = dladm_set_flowprop(handle, flow, NULL, NULL, 0, 1301 DLADM_OPT_ACTIVE, &errprop); 1302 if (status != DLADM_STATUS_OK) { 1303 warn_dlerr(status, "cannot reset flow property '%s' " 1304 "on '%s'", errprop, flow); 1305 } 1306 if (!temp) { 1307 dladm_status_t s; 1308 1309 s = set_flowprop_persist(flow, NULL, NULL, 0, reset); 1310 if (s != DLADM_STATUS_OK) 1311 status = s; 1312 } 1313 goto done; 1314 } 1315 1316 for (i = 0; i < proplist->al_count; i++) { 1317 dladm_arg_info_t *aip = &proplist->al_info[i]; 1318 char **val; 1319 uint_t count; 1320 dladm_status_t s; 1321 1322 if (reset) { 1323 val = NULL; 1324 count = 0; 1325 } else { 1326 val = aip->ai_val; 1327 count = aip->ai_count; 1328 if (count == 0) { 1329 warn("no value specified for '%s'", 1330 aip->ai_name); 1331 status = DLADM_STATUS_BADARG; 1332 continue; 1333 } 1334 } 1335 s = dladm_set_flowprop(handle, flow, aip->ai_name, val, count, 1336 DLADM_OPT_ACTIVE, NULL); 1337 if (s == DLADM_STATUS_OK) { 1338 if (!temp) { 1339 s = set_flowprop_persist(flow, 1340 aip->ai_name, val, count, reset); 1341 if (s != DLADM_STATUS_OK) 1342 status = s; 1343 } 1344 continue; 1345 } 1346 status = s; 1347 switch (s) { 1348 case DLADM_STATUS_NOTFOUND: 1349 warn("invalid flow property '%s'", aip->ai_name); 1350 break; 1351 case DLADM_STATUS_BADVAL: { 1352 int j; 1353 char *ptr, *lim; 1354 char **propvals = NULL; 1355 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 1356 1357 ptr = malloc((sizeof (char *) + 1358 DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT + 1359 MAX_PROP_LINE); 1360 1361 if (ptr == NULL) 1362 die("insufficient memory"); 1363 propvals = (char **)(void *)ptr; 1364 1365 for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) { 1366 propvals[j] = ptr + sizeof (char *) * 1367 DLADM_MAX_PROP_VALCNT + 1368 j * DLADM_PROP_VAL_MAX; 1369 } 1370 s = dladm_get_flowprop(handle, flow, 1371 DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals, 1372 &valcnt); 1373 1374 ptr = errmsg; 1375 lim = ptr + DLADM_STRSIZE; 1376 *ptr = '\0'; 1377 for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) { 1378 ptr += snprintf(ptr, lim - ptr, "%s,", 1379 propvals[j]); 1380 if (ptr >= lim) 1381 break; 1382 } 1383 if (ptr > errmsg) { 1384 *(ptr - 1) = '\0'; 1385 warn("flow property '%s' must be one of: %s", 1386 aip->ai_name, errmsg); 1387 } else 1388 warn("%s is an invalid value for " 1389 "flow property %s", *val, aip->ai_name); 1390 free(propvals); 1391 break; 1392 } 1393 default: 1394 if (reset) { 1395 warn_dlerr(status, "cannot reset flow property " 1396 "'%s' on '%s'", aip->ai_name, flow); 1397 } else { 1398 warn_dlerr(status, "cannot set flow property " 1399 "'%s' on '%s'", aip->ai_name, flow); 1400 } 1401 break; 1402 } 1403 } 1404 done: 1405 dladm_free_props(proplist); 1406 if (status != DLADM_STATUS_OK) { 1407 dladm_close(handle); 1408 exit(1); 1409 } 1410 } 1411 1412 static void 1413 do_set_flowprop(int argc, char **argv) 1414 { 1415 set_flowprop(argc, argv, B_FALSE); 1416 } 1417 1418 static void 1419 do_reset_flowprop(int argc, char **argv) 1420 { 1421 set_flowprop(argc, argv, B_TRUE); 1422 } 1423 1424 static void 1425 warn(const char *format, ...) 1426 { 1427 va_list alist; 1428 1429 format = gettext(format); 1430 (void) fprintf(stderr, "%s: warning: ", progname); 1431 1432 va_start(alist, format); 1433 (void) vfprintf(stderr, format, alist); 1434 va_end(alist); 1435 1436 (void) putchar('\n'); 1437 } 1438 1439 /* PRINTFLIKE2 */ 1440 static void 1441 warn_dlerr(dladm_status_t err, const char *format, ...) 1442 { 1443 va_list alist; 1444 char errmsg[DLADM_STRSIZE]; 1445 1446 format = gettext(format); 1447 (void) fprintf(stderr, gettext("%s: warning: "), progname); 1448 1449 va_start(alist, format); 1450 (void) vfprintf(stderr, format, alist); 1451 va_end(alist); 1452 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1453 } 1454 1455 /* PRINTFLIKE1 */ 1456 static void 1457 die(const char *format, ...) 1458 { 1459 va_list alist; 1460 1461 format = gettext(format); 1462 (void) fprintf(stderr, "%s: ", progname); 1463 1464 va_start(alist, format); 1465 (void) vfprintf(stderr, format, alist); 1466 va_end(alist); 1467 1468 (void) putchar('\n'); 1469 1470 /* close dladm handle if it was opened */ 1471 if (handle != NULL) 1472 dladm_close(handle); 1473 1474 exit(EXIT_FAILURE); 1475 } 1476 1477 static void 1478 die_optdup(int opt) 1479 { 1480 die("the option -%c cannot be specified more than once", opt); 1481 } 1482 1483 static void 1484 die_opterr(int opt, int opterr) 1485 { 1486 switch (opterr) { 1487 case ':': 1488 die("option '-%c' requires a value", opt); 1489 break; 1490 case '?': 1491 default: 1492 die("unrecognized option '-%c'", opt); 1493 break; 1494 } 1495 } 1496 1497 /* PRINTFLIKE2 */ 1498 static void 1499 die_dlerr(dladm_status_t err, const char *format, ...) 1500 { 1501 va_list alist; 1502 char errmsg[DLADM_STRSIZE]; 1503 1504 format = gettext(format); 1505 (void) fprintf(stderr, "%s: ", progname); 1506 1507 va_start(alist, format); 1508 (void) vfprintf(stderr, format, alist); 1509 va_end(alist); 1510 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1511 1512 /* close dladm handle if it was opened */ 1513 if (handle != NULL) 1514 dladm_close(handle); 1515 1516 exit(EXIT_FAILURE); 1517 } 1518 1519 static void 1520 print_flowprop(const char *flowname, show_flowprop_state_t *statep, 1521 const char *propname, dladm_prop_type_t type, 1522 const char *format, char **pptr) 1523 { 1524 int i; 1525 char *ptr, *lim; 1526 char buf[DLADM_STRSIZE]; 1527 char *unknown = "--", *notsup = ""; 1528 char **propvals = statep->fs_propvals; 1529 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 1530 dladm_status_t status; 1531 1532 status = dladm_get_flowprop(handle, flowname, type, propname, propvals, 1533 &valcnt); 1534 if (status != DLADM_STATUS_OK) { 1535 if (status == DLADM_STATUS_TEMPONLY) { 1536 if (type == DLADM_PROP_VAL_MODIFIABLE && 1537 statep->fs_persist) { 1538 valcnt = 1; 1539 propvals = &unknown; 1540 } else { 1541 statep->fs_status = status; 1542 statep->fs_retstatus = status; 1543 return; 1544 } 1545 } else if (status == DLADM_STATUS_NOTSUP || 1546 statep->fs_persist) { 1547 valcnt = 1; 1548 if (type == DLADM_PROP_VAL_CURRENT) 1549 propvals = &unknown; 1550 else 1551 propvals = ¬sup; 1552 } else { 1553 if ((statep->fs_proplist != NULL) && 1554 statep->fs_status == DLADM_STATUS_OK) { 1555 warn("invalid flow property '%s'", propname); 1556 } 1557 statep->fs_status = status; 1558 statep->fs_retstatus = status; 1559 return; 1560 } 1561 } 1562 1563 statep->fs_status = DLADM_STATUS_OK; 1564 1565 ptr = buf; 1566 lim = buf + DLADM_STRSIZE; 1567 for (i = 0; i < valcnt; i++) { 1568 if (propvals[i][0] == '\0' && !statep->fs_parseable) 1569 ptr += snprintf(ptr, lim - ptr, STR_UNDEF_VAL","); 1570 else 1571 ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]); 1572 if (ptr >= lim) 1573 break; 1574 } 1575 if (valcnt > 0) 1576 buf[strlen(buf) - 1] = '\0'; 1577 1578 lim = statep->fs_line + MAX_PROP_LINE; 1579 if (statep->fs_parseable) { 1580 *pptr += snprintf(*pptr, lim - *pptr, 1581 "%s", buf); 1582 } else { 1583 *pptr += snprintf(*pptr, lim - *pptr, format, buf); 1584 } 1585 } 1586 1587 static char * 1588 flowprop_callback(print_field_t *pf, void *fs_arg) 1589 { 1590 flowprop_args_t *arg = fs_arg; 1591 char *propname = arg->fs_propname; 1592 show_flowprop_state_t *statep = arg->fs_state; 1593 char *ptr = statep->fs_line; 1594 char *lim = ptr + MAX_PROP_LINE; 1595 char *flowname = arg->fs_flowname; 1596 1597 switch (pf->pf_index) { 1598 case FLOWPROP_FLOW: 1599 (void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow); 1600 break; 1601 case FLOWPROP_PROPERTY: 1602 (void) snprintf(ptr, lim - ptr, "%s", propname); 1603 break; 1604 case FLOWPROP_VALUE: 1605 print_flowprop(flowname, statep, propname, 1606 statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT : 1607 DLADM_PROP_VAL_CURRENT, "%s", &ptr); 1608 /* 1609 * If we failed to query the flow property, for example, query 1610 * the persistent value of a non-persistable flow property, 1611 * simply skip the output. 1612 */ 1613 if (statep->fs_status != DLADM_STATUS_OK) 1614 goto skip; 1615 ptr = statep->fs_line; 1616 break; 1617 case FLOWPROP_DEFAULT: 1618 print_flowprop(flowname, statep, propname, 1619 DLADM_PROP_VAL_DEFAULT, "%s", &ptr); 1620 if (statep->fs_status != DLADM_STATUS_OK) 1621 goto skip; 1622 ptr = statep->fs_line; 1623 break; 1624 case FLOWPROP_POSSIBLE: 1625 print_flowprop(flowname, statep, propname, 1626 DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr); 1627 if (statep->fs_status != DLADM_STATUS_OK) 1628 goto skip; 1629 ptr = statep->fs_line; 1630 break; 1631 default: 1632 die("invalid input"); 1633 break; 1634 } 1635 return (ptr); 1636 skip: 1637 if (statep->fs_status != DLADM_STATUS_OK) 1638 return (NULL); 1639 else 1640 return (""); 1641 } 1642 1643 static int 1644 show_one_flowprop(void *arg, const char *propname) 1645 { 1646 show_flowprop_state_t *statep = arg; 1647 flowprop_args_t fs_arg; 1648 1649 bzero(&fs_arg, sizeof (fs_arg)); 1650 fs_arg.fs_state = statep; 1651 fs_arg.fs_propname = (char *)propname; 1652 fs_arg.fs_flowname = (char *)statep->fs_flow; 1653 1654 if (statep->fs_header) { 1655 statep->fs_header = B_FALSE; 1656 if (!statep ->fs_parseable) 1657 print_header(&statep->fs_print); 1658 } 1659 flowadm_print_output(&statep->fs_print, statep->fs_parseable, 1660 flowprop_callback, (void *)&fs_arg); 1661 1662 return (DLADM_WALK_CONTINUE); 1663 } 1664 1665 /* Walker function called by dladm_walk_flow to display flow properties */ 1666 static int 1667 show_flowprop(dladm_flow_attr_t *attr, void *arg) 1668 { 1669 show_flowprop_one_flow(arg, attr->fa_flowname); 1670 return (DLADM_WALK_CONTINUE); 1671 } 1672 1673 /* 1674 * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it 1675 * usable to dladm_walk_datalink_id() 1676 */ 1677 static int 1678 show_flowprop_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg) 1679 { 1680 char name[MAXLINKNAMELEN]; 1681 1682 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, name, 1683 sizeof (name)) != DLADM_STATUS_OK) 1684 return (DLADM_WALK_TERMINATE); 1685 1686 (void) dladm_walk_flow(show_flowprop, dh, linkid, arg, B_FALSE); 1687 1688 return (DLADM_WALK_CONTINUE); 1689 } 1690 1691 static void 1692 do_show_flowprop(int argc, char **argv) 1693 { 1694 int option; 1695 dladm_arg_list_t *proplist = NULL; 1696 show_flowprop_state_t state; 1697 char *fields_str = NULL; 1698 print_field_t **fields; 1699 uint_t nfields; 1700 char *all_fields = 1701 "flow,property,value,default,possible"; 1702 1703 fields_str = all_fields; 1704 opterr = 0; 1705 state.fs_propvals = NULL; 1706 state.fs_line = NULL; 1707 state.fs_parseable = B_FALSE; 1708 state.fs_persist = B_FALSE; 1709 state.fs_header = B_TRUE; 1710 state.fs_retstatus = DLADM_STATUS_OK; 1711 state.fs_linkid = DATALINK_INVALID_LINKID; 1712 state.fs_flow = NULL; 1713 1714 while ((option = getopt_long(argc, argv, ":p:cPl:o:", 1715 prop_longopts, NULL)) != -1) { 1716 switch (option) { 1717 case 'p': 1718 if (dladm_parse_flow_props(optarg, &proplist, B_TRUE) 1719 != DLADM_STATUS_OK) 1720 die("invalid flow properties specified"); 1721 break; 1722 case 'c': 1723 state.fs_parseable = B_TRUE; 1724 break; 1725 case 'P': 1726 state.fs_persist = B_TRUE; 1727 break; 1728 case 'l': 1729 if (dladm_name2info(handle, optarg, &state.fs_linkid, 1730 NULL, NULL, NULL) != DLADM_STATUS_OK) 1731 die("invalid link '%s'", optarg); 1732 break; 1733 case 'o': 1734 if (strcasecmp(optarg, "all") == 0) 1735 fields_str = all_fields; 1736 else 1737 fields_str = optarg; 1738 break; 1739 default: 1740 die_opterr(optopt, option); 1741 break; 1742 } 1743 } 1744 1745 if (optind == (argc - 1)) { 1746 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 1747 die("flow name too long"); 1748 state.fs_flow = argv[optind]; 1749 } else if (optind != argc) { 1750 usage(); 1751 } 1752 bzero(&state.fs_print, sizeof (print_state_t)); 1753 state.fs_proplist = proplist; 1754 state.fs_status = DLADM_STATUS_OK; 1755 1756 fields = parse_output_fields(fields_str, flowprop_fields, 1757 FLOWPROP_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 1758 1759 if (fields == NULL) { 1760 die("invalid field(s) specified"); 1761 return; 1762 } 1763 1764 state.fs_print.ps_fields = fields; 1765 state.fs_print.ps_nfields = nfields; 1766 1767 /* Show properties for one flow */ 1768 if (state.fs_flow != NULL) { 1769 show_flowprop_one_flow(&state, state.fs_flow); 1770 1771 /* Show properties for all flows on one link */ 1772 } else if (state.fs_linkid != DATALINK_INVALID_LINKID) { 1773 (void) show_flowprop_onelink(handle, state.fs_linkid, &state); 1774 1775 /* Show properties for all flows on all links */ 1776 } else { 1777 (void) dladm_walk_datalink_id(show_flowprop_onelink, handle, 1778 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1779 DLADM_OPT_ACTIVE); 1780 } 1781 1782 dladm_free_props(proplist); 1783 } 1784 1785 static void 1786 show_flowprop_one_flow(void *arg, const char *flow) 1787 { 1788 int i; 1789 char *buf; 1790 dladm_status_t status; 1791 dladm_arg_list_t *proplist = NULL; 1792 show_flowprop_state_t *statep = arg; 1793 dladm_flow_attr_t attr; 1794 const char *savep; 1795 1796 /* 1797 * Do not print flow props for invalid flows. 1798 */ 1799 if ((status = dladm_flow_info(handle, flow, &attr)) != 1800 DLADM_STATUS_OK) { 1801 die("invalid flow: '%s'", flow); 1802 } 1803 1804 savep = statep->fs_flow; 1805 statep->fs_flow = flow; 1806 1807 proplist = statep->fs_proplist; 1808 1809 buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) 1810 * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE); 1811 if (buf == NULL) 1812 die("insufficient memory"); 1813 1814 statep->fs_propvals = (char **)(void *)buf; 1815 for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) { 1816 statep->fs_propvals[i] = buf + 1817 sizeof (char *) * DLADM_MAX_PROP_VALCNT + 1818 i * DLADM_PROP_VAL_MAX; 1819 } 1820 statep->fs_line = buf + 1821 (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT; 1822 1823 /* show only specified flow properties */ 1824 if (proplist != NULL) { 1825 for (i = 0; i < proplist->al_count; i++) { 1826 if (show_one_flowprop(statep, 1827 proplist->al_info[i].ai_name) != DLADM_STATUS_OK) 1828 break; 1829 } 1830 1831 /* show all flow properties */ 1832 } else { 1833 status = dladm_walk_flowprop(show_one_flowprop, flow, statep); 1834 if (status != DLADM_STATUS_OK) 1835 die_dlerr(status, "show-flowprop"); 1836 } 1837 free(buf); 1838 statep->fs_flow = savep; 1839 } 1840 1841 typedef struct { 1842 char *s_buf; 1843 char **s_fields; /* array of pointer to the fields in s_buf */ 1844 uint_t s_nfields; /* the number of fields in s_buf */ 1845 } split_t; 1846 1847 /* 1848 * Free the split_t structure pointed to by `sp'. 1849 */ 1850 static void 1851 splitfree(split_t *sp) 1852 { 1853 free(sp->s_buf); 1854 free(sp->s_fields); 1855 free(sp); 1856 } 1857 1858 /* 1859 * Split `str' into at most `maxfields' fields, each field at most `maxlen' in 1860 * length. Return a pointer to a split_t containing the split fields, or NULL 1861 * on failure. 1862 */ 1863 static split_t * 1864 split(const char *str, uint_t maxfields, uint_t maxlen) 1865 { 1866 char *field, *token, *lasts = NULL; 1867 split_t *sp; 1868 1869 if (*str == '\0' || maxfields == 0 || maxlen == 0) 1870 return (NULL); 1871 1872 sp = calloc(sizeof (split_t), 1); 1873 if (sp == NULL) 1874 return (NULL); 1875 1876 sp->s_buf = strdup(str); 1877 sp->s_fields = malloc(sizeof (char *) * maxfields); 1878 if (sp->s_buf == NULL || sp->s_fields == NULL) 1879 goto fail; 1880 1881 token = sp->s_buf; 1882 while ((field = strtok_r(token, ",", &lasts)) != NULL) { 1883 if (sp->s_nfields == maxfields || strlen(field) > maxlen) 1884 goto fail; 1885 token = NULL; 1886 sp->s_fields[sp->s_nfields++] = field; 1887 } 1888 return (sp); 1889 fail: 1890 splitfree(sp); 1891 return (NULL); 1892 } 1893 1894 static print_field_t ** 1895 parse_output_fields(char *str, print_field_t *template, int max_fields, 1896 uint_t cmdtype, uint_t *countp) 1897 { 1898 split_t *sp; 1899 boolean_t good_match = B_FALSE; 1900 uint_t i, j; 1901 print_field_t **pf = NULL; 1902 1903 sp = split(str, max_fields, MAX_FIELD_LEN); 1904 1905 if (sp == NULL) 1906 return (NULL); 1907 1908 pf = malloc(sp->s_nfields * sizeof (print_field_t *)); 1909 if (pf == NULL) 1910 goto fail; 1911 1912 for (i = 0; i < sp->s_nfields; i++) { 1913 for (j = 0; j < max_fields; j++) { 1914 if (strcasecmp(sp->s_fields[i], 1915 template[j].pf_name) == 0) { 1916 good_match = template[j]. pf_cmdtype & cmdtype; 1917 break; 1918 } 1919 } 1920 if (!good_match) 1921 goto fail; 1922 1923 good_match = B_FALSE; 1924 pf[i] = &template[j]; 1925 } 1926 *countp = i; 1927 splitfree(sp); 1928 return (pf); 1929 fail: 1930 free(pf); 1931 splitfree(sp); 1932 return (NULL); 1933 } 1934 1935 static void 1936 flowadm_print_output(print_state_t *statep, boolean_t parseable, 1937 print_callback_t fn, void *arg) 1938 { 1939 int i; 1940 char *value; 1941 print_field_t **pf; 1942 1943 pf = statep->ps_fields; 1944 for (i = 0; i < statep->ps_nfields; i++) { 1945 statep->ps_lastfield = (i + 1 == statep->ps_nfields); 1946 value = (*fn)(pf[i], arg); 1947 if (value != NULL) 1948 print_field(statep, pf[i], value, parseable); 1949 } 1950 (void) putchar('\n'); 1951 } 1952 1953 static void 1954 print_header(print_state_t *ps) 1955 { 1956 int i; 1957 print_field_t **pf; 1958 1959 pf = ps->ps_fields; 1960 for (i = 0; i < ps->ps_nfields; i++) { 1961 ps->ps_lastfield = (i + 1 == ps->ps_nfields); 1962 print_field(ps, pf[i], pf[i]->pf_header, B_FALSE); 1963 } 1964 (void) putchar('\n'); 1965 } 1966 1967 static void 1968 print_field(print_state_t *statep, print_field_t *pfp, const char *value, 1969 boolean_t parseable) 1970 { 1971 uint_t width = pfp->pf_width; 1972 uint_t valwidth = strlen(value); 1973 uint_t compress; 1974 1975 if (parseable) { 1976 (void) printf("%s=\"%s\"", pfp->pf_header, value); 1977 } else { 1978 if (value[0] == '\0') 1979 value = STR_UNDEF_VAL; 1980 if (statep->ps_lastfield) { 1981 (void) printf("%s", value); 1982 return; 1983 } 1984 1985 if (valwidth > width) { 1986 statep->ps_overflow += valwidth - width; 1987 } else if (valwidth < width && statep->ps_overflow > 0) { 1988 compress = min(statep->ps_overflow, width - valwidth); 1989 statep->ps_overflow -= compress; 1990 width -= compress; 1991 } 1992 (void) printf("%-*s", width, value); 1993 } 1994 1995 if (!statep->ps_lastfield) 1996 (void) putchar(' '); 1997 } 1998