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", "IPADDR", 30, 230 offsetof(flow_fields_buf_t, flow_ipaddr), CMD_TYPE_ANY}, 231 { "proto", "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 { "dsfld", "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(EXIT_SUCCESS); 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,proto,port,dsfld"; 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 (state.fs_parseable && !o_arg) 1177 die("-p requires -o"); 1178 1179 if (state.fs_parseable && strcasecmp(fields_str, "all") == 0) 1180 die("\"-o all\" is invalid with -p"); 1181 1182 if (i_arg && !(s_arg || S_arg)) 1183 die("the -i option can be used only with -s or -S"); 1184 1185 if (s_arg && S_arg) 1186 die("the -s option cannot be used with -S"); 1187 1188 /* get flow name (optional last argument */ 1189 if (optind == (argc-1)) { 1190 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) 1191 >= MAXFLOWNAMELEN) 1192 die("flow name too long"); 1193 state.fs_flow = flowname; 1194 } 1195 1196 if (s_arg) { 1197 flow_stats(state.fs_flow, linkid, interval); 1198 return; 1199 } 1200 1201 if (S_arg) { 1202 dladm_continuous(handle, linkid, state.fs_flow, interval, 1203 FLOW_REPORT); 1204 return; 1205 } 1206 1207 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 1208 fields_str = all_fields; 1209 1210 fields = parse_output_fields(fields_str, flow_fields, FLOW_MAX_FIELDS, 1211 CMD_TYPE_ANY, &nfields); 1212 1213 if (fields == NULL) { 1214 die("invalid fields(s) specified"); 1215 return; 1216 } 1217 1218 state.fs_print.ps_fields = fields; 1219 state.fs_print.ps_nfields = nfields; 1220 1221 /* Show attributes of one flow */ 1222 if (state.fs_flow != NULL) { 1223 show_one_flow(&state, state.fs_flow); 1224 1225 /* Show attributes of flows on one link */ 1226 } else if (l_arg) { 1227 (void) show_flows_onelink(handle, linkid, &state); 1228 1229 /* Show attributes of all flows on all links */ 1230 } else { 1231 (void) dladm_walk_datalink_id(show_flows_onelink, handle, 1232 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1233 DLADM_OPT_ACTIVE); 1234 } 1235 } 1236 1237 static dladm_status_t 1238 set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val, 1239 uint_t val_cnt, boolean_t reset) 1240 { 1241 dladm_status_t status; 1242 char *errprop; 1243 1244 status = dladm_set_flowprop(handle, flow, prop_name, prop_val, val_cnt, 1245 DLADM_OPT_PERSIST, &errprop); 1246 1247 if (status != DLADM_STATUS_OK) { 1248 warn_dlerr(status, "cannot persistently %s flow " 1249 "property '%s' on '%s'", reset? "reset": "set", 1250 errprop, flow); 1251 } 1252 return (status); 1253 } 1254 1255 static void 1256 set_flowprop(int argc, char **argv, boolean_t reset) 1257 { 1258 int i, option; 1259 char errmsg[DLADM_STRSIZE]; 1260 const char *flow = NULL; 1261 dladm_arg_list_t *proplist = NULL; 1262 boolean_t temp = B_FALSE; 1263 dladm_status_t status = DLADM_STATUS_OK; 1264 1265 opterr = 0; 1266 while ((option = getopt_long(argc, argv, ":p:R:t", 1267 prop_longopts, NULL)) != -1) { 1268 switch (option) { 1269 case 'p': 1270 if (dladm_parse_flow_props(optarg, &proplist, reset) 1271 != DLADM_STATUS_OK) 1272 die("invalid flow property specified"); 1273 break; 1274 case 't': 1275 temp = B_TRUE; 1276 break; 1277 case 'R': 1278 status = dladm_set_rootdir(optarg); 1279 if (status != DLADM_STATUS_OK) { 1280 die_dlerr(status, "invalid directory " 1281 "specified"); 1282 } 1283 break; 1284 default: 1285 die_opterr(optopt, option); 1286 break; 1287 } 1288 } 1289 1290 if (optind == (argc - 1)) { 1291 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 1292 die("flow name too long"); 1293 flow = argv[optind]; 1294 } else if (optind != argc) { 1295 usage(); 1296 } 1297 if (flow == NULL) 1298 die("flow name must be specified"); 1299 1300 if (proplist == NULL) { 1301 char *errprop; 1302 1303 if (!reset) 1304 die("flow property must be specified"); 1305 1306 status = dladm_set_flowprop(handle, flow, NULL, NULL, 0, 1307 DLADM_OPT_ACTIVE, &errprop); 1308 if (status != DLADM_STATUS_OK) { 1309 warn_dlerr(status, "cannot reset flow property '%s' " 1310 "on '%s'", errprop, flow); 1311 } 1312 if (!temp) { 1313 dladm_status_t s; 1314 1315 s = set_flowprop_persist(flow, NULL, NULL, 0, reset); 1316 if (s != DLADM_STATUS_OK) 1317 status = s; 1318 } 1319 goto done; 1320 } 1321 1322 for (i = 0; i < proplist->al_count; i++) { 1323 dladm_arg_info_t *aip = &proplist->al_info[i]; 1324 char **val; 1325 uint_t count; 1326 dladm_status_t s; 1327 1328 if (reset) { 1329 val = NULL; 1330 count = 0; 1331 } else { 1332 val = aip->ai_val; 1333 count = aip->ai_count; 1334 if (count == 0) { 1335 warn("no value specified for '%s'", 1336 aip->ai_name); 1337 status = DLADM_STATUS_BADARG; 1338 continue; 1339 } 1340 } 1341 s = dladm_set_flowprop(handle, flow, aip->ai_name, val, count, 1342 DLADM_OPT_ACTIVE, NULL); 1343 if (s == DLADM_STATUS_OK) { 1344 if (!temp) { 1345 s = set_flowprop_persist(flow, 1346 aip->ai_name, val, count, reset); 1347 if (s != DLADM_STATUS_OK) 1348 status = s; 1349 } 1350 continue; 1351 } 1352 status = s; 1353 switch (s) { 1354 case DLADM_STATUS_NOTFOUND: 1355 warn("invalid flow property '%s'", aip->ai_name); 1356 break; 1357 case DLADM_STATUS_BADVAL: { 1358 int j; 1359 char *ptr, *lim; 1360 char **propvals = NULL; 1361 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 1362 1363 ptr = malloc((sizeof (char *) + 1364 DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT + 1365 MAX_PROP_LINE); 1366 1367 if (ptr == NULL) 1368 die("insufficient memory"); 1369 propvals = (char **)(void *)ptr; 1370 1371 for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) { 1372 propvals[j] = ptr + sizeof (char *) * 1373 DLADM_MAX_PROP_VALCNT + 1374 j * DLADM_PROP_VAL_MAX; 1375 } 1376 s = dladm_get_flowprop(handle, flow, 1377 DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals, 1378 &valcnt); 1379 1380 ptr = errmsg; 1381 lim = ptr + DLADM_STRSIZE; 1382 *ptr = '\0'; 1383 for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) { 1384 ptr += snprintf(ptr, lim - ptr, "%s,", 1385 propvals[j]); 1386 if (ptr >= lim) 1387 break; 1388 } 1389 if (ptr > errmsg) { 1390 *(ptr - 1) = '\0'; 1391 warn("flow property '%s' must be one of: %s", 1392 aip->ai_name, errmsg); 1393 } else 1394 warn("%s is an invalid value for " 1395 "flow property %s", *val, aip->ai_name); 1396 free(propvals); 1397 break; 1398 } 1399 default: 1400 if (reset) { 1401 warn_dlerr(status, "cannot reset flow property " 1402 "'%s' on '%s'", aip->ai_name, flow); 1403 } else { 1404 warn_dlerr(status, "cannot set flow property " 1405 "'%s' on '%s'", aip->ai_name, flow); 1406 } 1407 break; 1408 } 1409 } 1410 done: 1411 dladm_free_props(proplist); 1412 if (status != DLADM_STATUS_OK) { 1413 dladm_close(handle); 1414 exit(EXIT_FAILURE); 1415 } 1416 } 1417 1418 static void 1419 do_set_flowprop(int argc, char **argv) 1420 { 1421 set_flowprop(argc, argv, B_FALSE); 1422 } 1423 1424 static void 1425 do_reset_flowprop(int argc, char **argv) 1426 { 1427 set_flowprop(argc, argv, B_TRUE); 1428 } 1429 1430 static void 1431 warn(const char *format, ...) 1432 { 1433 va_list alist; 1434 1435 format = gettext(format); 1436 (void) fprintf(stderr, "%s: warning: ", progname); 1437 1438 va_start(alist, format); 1439 (void) vfprintf(stderr, format, alist); 1440 va_end(alist); 1441 1442 (void) putchar('\n'); 1443 } 1444 1445 /* PRINTFLIKE2 */ 1446 static void 1447 warn_dlerr(dladm_status_t err, const char *format, ...) 1448 { 1449 va_list alist; 1450 char errmsg[DLADM_STRSIZE]; 1451 1452 format = gettext(format); 1453 (void) fprintf(stderr, gettext("%s: warning: "), progname); 1454 1455 va_start(alist, format); 1456 (void) vfprintf(stderr, format, alist); 1457 va_end(alist); 1458 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1459 } 1460 1461 /* PRINTFLIKE1 */ 1462 static void 1463 die(const char *format, ...) 1464 { 1465 va_list alist; 1466 1467 format = gettext(format); 1468 (void) fprintf(stderr, "%s: ", progname); 1469 1470 va_start(alist, format); 1471 (void) vfprintf(stderr, format, alist); 1472 va_end(alist); 1473 1474 (void) putchar('\n'); 1475 1476 /* close dladm handle if it was opened */ 1477 if (handle != NULL) 1478 dladm_close(handle); 1479 1480 exit(EXIT_FAILURE); 1481 } 1482 1483 static void 1484 die_optdup(int opt) 1485 { 1486 die("the option -%c cannot be specified more than once", opt); 1487 } 1488 1489 static void 1490 die_opterr(int opt, int opterr) 1491 { 1492 switch (opterr) { 1493 case ':': 1494 die("option '-%c' requires a value", opt); 1495 break; 1496 case '?': 1497 default: 1498 die("unrecognized option '-%c'", opt); 1499 break; 1500 } 1501 } 1502 1503 /* PRINTFLIKE2 */ 1504 static void 1505 die_dlerr(dladm_status_t err, const char *format, ...) 1506 { 1507 va_list alist; 1508 char errmsg[DLADM_STRSIZE]; 1509 1510 format = gettext(format); 1511 (void) fprintf(stderr, "%s: ", progname); 1512 1513 va_start(alist, format); 1514 (void) vfprintf(stderr, format, alist); 1515 va_end(alist); 1516 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1517 1518 /* close dladm handle if it was opened */ 1519 if (handle != NULL) 1520 dladm_close(handle); 1521 1522 exit(EXIT_FAILURE); 1523 } 1524 1525 static void 1526 print_flowprop(const char *flowname, show_flowprop_state_t *statep, 1527 const char *propname, dladm_prop_type_t type, 1528 const char *format, char **pptr) 1529 { 1530 int i; 1531 char *ptr, *lim; 1532 char buf[DLADM_STRSIZE]; 1533 char *unknown = "--", *notsup = ""; 1534 char **propvals = statep->fs_propvals; 1535 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 1536 dladm_status_t status; 1537 1538 status = dladm_get_flowprop(handle, flowname, type, propname, propvals, 1539 &valcnt); 1540 if (status != DLADM_STATUS_OK) { 1541 if (status == DLADM_STATUS_TEMPONLY) { 1542 if (type == DLADM_PROP_VAL_MODIFIABLE && 1543 statep->fs_persist) { 1544 valcnt = 1; 1545 propvals = &unknown; 1546 } else { 1547 statep->fs_status = status; 1548 statep->fs_retstatus = status; 1549 return; 1550 } 1551 } else if (status == DLADM_STATUS_NOTSUP || 1552 statep->fs_persist) { 1553 valcnt = 1; 1554 if (type == DLADM_PROP_VAL_CURRENT) 1555 propvals = &unknown; 1556 else 1557 propvals = ¬sup; 1558 } else { 1559 if ((statep->fs_proplist != NULL) && 1560 statep->fs_status == DLADM_STATUS_OK) { 1561 warn("invalid flow property '%s'", propname); 1562 } 1563 statep->fs_status = status; 1564 statep->fs_retstatus = status; 1565 return; 1566 } 1567 } 1568 1569 statep->fs_status = DLADM_STATUS_OK; 1570 1571 ptr = buf; 1572 lim = buf + DLADM_STRSIZE; 1573 for (i = 0; i < valcnt; i++) { 1574 if (propvals[i][0] == '\0' && !statep->fs_parseable) 1575 ptr += snprintf(ptr, lim - ptr, STR_UNDEF_VAL","); 1576 else 1577 ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]); 1578 if (ptr >= lim) 1579 break; 1580 } 1581 if (valcnt > 0) 1582 buf[strlen(buf) - 1] = '\0'; 1583 1584 lim = statep->fs_line + MAX_PROP_LINE; 1585 if (statep->fs_parseable) { 1586 *pptr += snprintf(*pptr, lim - *pptr, 1587 "%s", buf); 1588 } else { 1589 *pptr += snprintf(*pptr, lim - *pptr, format, buf); 1590 } 1591 } 1592 1593 static char * 1594 flowprop_callback(print_field_t *pf, void *fs_arg) 1595 { 1596 flowprop_args_t *arg = fs_arg; 1597 char *propname = arg->fs_propname; 1598 show_flowprop_state_t *statep = arg->fs_state; 1599 char *ptr = statep->fs_line; 1600 char *lim = ptr + MAX_PROP_LINE; 1601 char *flowname = arg->fs_flowname; 1602 1603 switch (pf->pf_index) { 1604 case FLOWPROP_FLOW: 1605 (void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow); 1606 break; 1607 case FLOWPROP_PROPERTY: 1608 (void) snprintf(ptr, lim - ptr, "%s", propname); 1609 break; 1610 case FLOWPROP_VALUE: 1611 print_flowprop(flowname, statep, propname, 1612 statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT : 1613 DLADM_PROP_VAL_CURRENT, "%s", &ptr); 1614 /* 1615 * If we failed to query the flow property, for example, query 1616 * the persistent value of a non-persistable flow property, 1617 * simply skip the output. 1618 */ 1619 if (statep->fs_status != DLADM_STATUS_OK) 1620 goto skip; 1621 ptr = statep->fs_line; 1622 break; 1623 case FLOWPROP_DEFAULT: 1624 print_flowprop(flowname, statep, propname, 1625 DLADM_PROP_VAL_DEFAULT, "%s", &ptr); 1626 if (statep->fs_status != DLADM_STATUS_OK) 1627 goto skip; 1628 ptr = statep->fs_line; 1629 break; 1630 case FLOWPROP_POSSIBLE: 1631 print_flowprop(flowname, statep, propname, 1632 DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr); 1633 if (statep->fs_status != DLADM_STATUS_OK) 1634 goto skip; 1635 ptr = statep->fs_line; 1636 break; 1637 default: 1638 die("invalid input"); 1639 break; 1640 } 1641 return (ptr); 1642 skip: 1643 if (statep->fs_status != DLADM_STATUS_OK) 1644 return (NULL); 1645 else 1646 return (""); 1647 } 1648 1649 static int 1650 show_one_flowprop(void *arg, const char *propname) 1651 { 1652 show_flowprop_state_t *statep = arg; 1653 flowprop_args_t fs_arg; 1654 1655 bzero(&fs_arg, sizeof (fs_arg)); 1656 fs_arg.fs_state = statep; 1657 fs_arg.fs_propname = (char *)propname; 1658 fs_arg.fs_flowname = (char *)statep->fs_flow; 1659 1660 if (statep->fs_header) { 1661 statep->fs_header = B_FALSE; 1662 if (!statep ->fs_parseable) 1663 print_header(&statep->fs_print); 1664 } 1665 flowadm_print_output(&statep->fs_print, statep->fs_parseable, 1666 flowprop_callback, (void *)&fs_arg); 1667 1668 return (DLADM_WALK_CONTINUE); 1669 } 1670 1671 /* Walker function called by dladm_walk_flow to display flow properties */ 1672 static int 1673 show_flowprop(dladm_flow_attr_t *attr, void *arg) 1674 { 1675 show_flowprop_one_flow(arg, attr->fa_flowname); 1676 return (DLADM_WALK_CONTINUE); 1677 } 1678 1679 /* 1680 * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it 1681 * usable to dladm_walk_datalink_id() 1682 */ 1683 static int 1684 show_flowprop_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg) 1685 { 1686 char name[MAXLINKNAMELEN]; 1687 1688 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, name, 1689 sizeof (name)) != DLADM_STATUS_OK) 1690 return (DLADM_WALK_TERMINATE); 1691 1692 (void) dladm_walk_flow(show_flowprop, dh, linkid, arg, B_FALSE); 1693 1694 return (DLADM_WALK_CONTINUE); 1695 } 1696 1697 static void 1698 do_show_flowprop(int argc, char **argv) 1699 { 1700 int option; 1701 boolean_t o_arg = B_FALSE; 1702 dladm_arg_list_t *proplist = NULL; 1703 show_flowprop_state_t state; 1704 char *fields_str = NULL; 1705 print_field_t **fields; 1706 uint_t nfields; 1707 char *all_fields = 1708 "flow,property,value,default,possible"; 1709 1710 fields_str = all_fields; 1711 opterr = 0; 1712 state.fs_propvals = NULL; 1713 state.fs_line = NULL; 1714 state.fs_parseable = B_FALSE; 1715 state.fs_persist = B_FALSE; 1716 state.fs_header = B_TRUE; 1717 state.fs_retstatus = DLADM_STATUS_OK; 1718 state.fs_linkid = DATALINK_INVALID_LINKID; 1719 state.fs_flow = NULL; 1720 1721 while ((option = getopt_long(argc, argv, ":p:cPl:o:", 1722 prop_longopts, NULL)) != -1) { 1723 switch (option) { 1724 case 'p': 1725 if (dladm_parse_flow_props(optarg, &proplist, B_TRUE) 1726 != DLADM_STATUS_OK) 1727 die("invalid flow properties specified"); 1728 break; 1729 case 'c': 1730 state.fs_parseable = B_TRUE; 1731 break; 1732 case 'P': 1733 state.fs_persist = B_TRUE; 1734 break; 1735 case 'l': 1736 if (dladm_name2info(handle, optarg, &state.fs_linkid, 1737 NULL, NULL, NULL) != DLADM_STATUS_OK) 1738 die("invalid link '%s'", optarg); 1739 break; 1740 case 'o': 1741 o_arg = B_TRUE; 1742 if (strcasecmp(optarg, "all") == 0) 1743 fields_str = all_fields; 1744 else 1745 fields_str = optarg; 1746 break; 1747 default: 1748 die_opterr(optopt, option); 1749 break; 1750 } 1751 } 1752 1753 if (state.fs_parseable && !o_arg) 1754 die("-p requires -o"); 1755 1756 if (state.fs_parseable && strcasecmp(fields_str, "all") == 0) 1757 die("\"-o all\" is invalid with -p"); 1758 1759 if (optind == (argc - 1)) { 1760 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 1761 die("flow name too long"); 1762 state.fs_flow = argv[optind]; 1763 } else if (optind != argc) { 1764 usage(); 1765 } 1766 bzero(&state.fs_print, sizeof (print_state_t)); 1767 state.fs_proplist = proplist; 1768 state.fs_status = DLADM_STATUS_OK; 1769 1770 fields = parse_output_fields(fields_str, flowprop_fields, 1771 FLOWPROP_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 1772 1773 if (fields == NULL) { 1774 die("invalid field(s) specified"); 1775 return; 1776 } 1777 1778 state.fs_print.ps_fields = fields; 1779 state.fs_print.ps_nfields = nfields; 1780 1781 /* Show properties for one flow */ 1782 if (state.fs_flow != NULL) { 1783 show_flowprop_one_flow(&state, state.fs_flow); 1784 1785 /* Show properties for all flows on one link */ 1786 } else if (state.fs_linkid != DATALINK_INVALID_LINKID) { 1787 (void) show_flowprop_onelink(handle, state.fs_linkid, &state); 1788 1789 /* Show properties for all flows on all links */ 1790 } else { 1791 (void) dladm_walk_datalink_id(show_flowprop_onelink, handle, 1792 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1793 DLADM_OPT_ACTIVE); 1794 } 1795 1796 dladm_free_props(proplist); 1797 } 1798 1799 static void 1800 show_flowprop_one_flow(void *arg, const char *flow) 1801 { 1802 int i; 1803 char *buf; 1804 dladm_status_t status; 1805 dladm_arg_list_t *proplist = NULL; 1806 show_flowprop_state_t *statep = arg; 1807 dladm_flow_attr_t attr; 1808 const char *savep; 1809 1810 /* 1811 * Do not print flow props for invalid flows. 1812 */ 1813 if ((status = dladm_flow_info(handle, flow, &attr)) != 1814 DLADM_STATUS_OK) { 1815 die("invalid flow: '%s'", flow); 1816 } 1817 1818 savep = statep->fs_flow; 1819 statep->fs_flow = flow; 1820 1821 proplist = statep->fs_proplist; 1822 1823 buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) 1824 * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE); 1825 if (buf == NULL) 1826 die("insufficient memory"); 1827 1828 statep->fs_propvals = (char **)(void *)buf; 1829 for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) { 1830 statep->fs_propvals[i] = buf + 1831 sizeof (char *) * DLADM_MAX_PROP_VALCNT + 1832 i * DLADM_PROP_VAL_MAX; 1833 } 1834 statep->fs_line = buf + 1835 (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT; 1836 1837 /* show only specified flow properties */ 1838 if (proplist != NULL) { 1839 for (i = 0; i < proplist->al_count; i++) { 1840 if (show_one_flowprop(statep, 1841 proplist->al_info[i].ai_name) != DLADM_STATUS_OK) 1842 break; 1843 } 1844 1845 /* show all flow properties */ 1846 } else { 1847 status = dladm_walk_flowprop(show_one_flowprop, flow, statep); 1848 if (status != DLADM_STATUS_OK) 1849 die_dlerr(status, "show-flowprop"); 1850 } 1851 free(buf); 1852 statep->fs_flow = savep; 1853 } 1854 1855 typedef struct { 1856 char *s_buf; 1857 char **s_fields; /* array of pointer to the fields in s_buf */ 1858 uint_t s_nfields; /* the number of fields in s_buf */ 1859 } split_t; 1860 1861 /* 1862 * Free the split_t structure pointed to by `sp'. 1863 */ 1864 static void 1865 splitfree(split_t *sp) 1866 { 1867 free(sp->s_buf); 1868 free(sp->s_fields); 1869 free(sp); 1870 } 1871 1872 /* 1873 * Split `str' into at most `maxfields' fields, each field at most `maxlen' in 1874 * length. Return a pointer to a split_t containing the split fields, or NULL 1875 * on failure. 1876 */ 1877 static split_t * 1878 split(const char *str, uint_t maxfields, uint_t maxlen) 1879 { 1880 char *field, *token, *lasts = NULL; 1881 split_t *sp; 1882 1883 if (*str == '\0' || maxfields == 0 || maxlen == 0) 1884 return (NULL); 1885 1886 sp = calloc(sizeof (split_t), 1); 1887 if (sp == NULL) 1888 return (NULL); 1889 1890 sp->s_buf = strdup(str); 1891 sp->s_fields = malloc(sizeof (char *) * maxfields); 1892 if (sp->s_buf == NULL || sp->s_fields == NULL) 1893 goto fail; 1894 1895 token = sp->s_buf; 1896 while ((field = strtok_r(token, ",", &lasts)) != NULL) { 1897 if (sp->s_nfields == maxfields || strlen(field) > maxlen) 1898 goto fail; 1899 token = NULL; 1900 sp->s_fields[sp->s_nfields++] = field; 1901 } 1902 return (sp); 1903 fail: 1904 splitfree(sp); 1905 return (NULL); 1906 } 1907 1908 static print_field_t ** 1909 parse_output_fields(char *str, print_field_t *template, int max_fields, 1910 uint_t cmdtype, uint_t *countp) 1911 { 1912 split_t *sp; 1913 boolean_t good_match = B_FALSE; 1914 uint_t i, j; 1915 print_field_t **pf = NULL; 1916 1917 sp = split(str, max_fields, MAX_FIELD_LEN); 1918 1919 if (sp == NULL) 1920 return (NULL); 1921 1922 pf = malloc(sp->s_nfields * sizeof (print_field_t *)); 1923 if (pf == NULL) 1924 goto fail; 1925 1926 for (i = 0; i < sp->s_nfields; i++) { 1927 for (j = 0; j < max_fields; j++) { 1928 if (strcasecmp(sp->s_fields[i], 1929 template[j].pf_name) == 0) { 1930 good_match = template[j]. pf_cmdtype & cmdtype; 1931 break; 1932 } 1933 } 1934 if (!good_match) 1935 goto fail; 1936 1937 good_match = B_FALSE; 1938 pf[i] = &template[j]; 1939 } 1940 *countp = i; 1941 splitfree(sp); 1942 return (pf); 1943 fail: 1944 free(pf); 1945 splitfree(sp); 1946 return (NULL); 1947 } 1948 1949 static void 1950 flowadm_print_output(print_state_t *statep, boolean_t parseable, 1951 print_callback_t fn, void *arg) 1952 { 1953 int i; 1954 char *value; 1955 print_field_t **pf; 1956 1957 pf = statep->ps_fields; 1958 for (i = 0; i < statep->ps_nfields; i++) { 1959 statep->ps_lastfield = (i + 1 == statep->ps_nfields); 1960 value = (*fn)(pf[i], arg); 1961 if (value != NULL) 1962 print_field(statep, pf[i], value, parseable); 1963 } 1964 (void) putchar('\n'); 1965 } 1966 1967 static void 1968 print_header(print_state_t *ps) 1969 { 1970 int i; 1971 print_field_t **pf; 1972 1973 pf = ps->ps_fields; 1974 for (i = 0; i < ps->ps_nfields; i++) { 1975 ps->ps_lastfield = (i + 1 == ps->ps_nfields); 1976 print_field(ps, pf[i], pf[i]->pf_header, B_FALSE); 1977 } 1978 (void) putchar('\n'); 1979 } 1980 1981 static void 1982 print_field(print_state_t *statep, print_field_t *pfp, const char *value, 1983 boolean_t parseable) 1984 { 1985 uint_t width = pfp->pf_width; 1986 uint_t valwidth; 1987 uint_t compress; 1988 1989 /* 1990 * Parsable fields are separated by ':'. If such a field contains 1991 * a ':' or '\', this character is prefixed by a '\'. 1992 */ 1993 if (parseable) { 1994 char c; 1995 1996 if (statep->ps_nfields == 1) { 1997 (void) printf("%s", value); 1998 return; 1999 } 2000 while ((c = *value++) != '\0') { 2001 if (c == ':' || c == '\\') 2002 (void) putchar('\\'); 2003 (void) putchar(c); 2004 } 2005 if (!statep->ps_lastfield) 2006 (void) putchar(':'); 2007 return; 2008 } else { 2009 if (value[0] == '\0') 2010 value = STR_UNDEF_VAL; 2011 if (statep->ps_lastfield) { 2012 (void) printf("%s", value); 2013 statep->ps_overflow = 0; 2014 return; 2015 } 2016 2017 valwidth = strlen(value); 2018 if (valwidth > width) { 2019 statep->ps_overflow += valwidth - width; 2020 } else if (valwidth < width && statep->ps_overflow > 0) { 2021 compress = min(statep->ps_overflow, width - valwidth); 2022 statep->ps_overflow -= compress; 2023 width -= compress; 2024 } 2025 (void) printf("%-*s", width, value); 2026 } 2027 2028 if (!statep->ps_lastfield) 2029 (void) putchar(' '); 2030 } 2031