1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <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(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(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(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[MAXNAMELEN]; 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 static const char *attr_table[] = 371 {"local_ip", "remote_ip", "transport", "local_port", "dsfield"}; 372 373 #define NATTR (sizeof (attr_table)/sizeof (char *)) 374 375 static void 376 usage(void) 377 { 378 (void) fprintf(stderr, gettext("usage: flowadm <subcommand>" 379 " <args>...\n" 380 "\tadd-flow [-t] [-R <root-dir>] -l <link>\n" 381 "\t\t-a attr=value[,...] [-p prop=value,...]\n" 382 "\t\tflow-name\n" 383 "\tremove-flow [-t] [-R <root-dir>] {-l <link> | flow-name}\n" 384 "\tset-flowprop [-t] [-R <root-dir>] \n" 385 "\t\t-p prop=value[,...] flowname\n" 386 "\treset-flowprop [-t] [-R <root-dir>] \n" 387 "\t\t[-p prop,...] flowname\n" 388 "\tshow-flowprop [-cP] [-l <link>] [-p prop,...] [flow-name]\n" 389 "\tshow-flow [-p] [-s [-i <interval>]] [-l <link>] [flow-name]\n" 390 "\tshow-usage [-d|-p -F <format>] [-s <DD/MM/YYYY,HH:MM:SS>]\n" 391 "\t\t[-e <DD/MM/YYYY,HH:MM:SS>]] -f <logfile> [<name>]\n")); 392 exit(1); 393 } 394 395 int 396 main(int argc, char *argv[]) 397 { 398 int i, arglen, cmdlen; 399 cmd_t *cmdp; 400 401 (void) setlocale(LC_ALL, ""); 402 #if !defined(TEXT_DOMAIN) 403 #define TEXT_DOMAIN "SYS_TEST" 404 #endif 405 (void) textdomain(TEXT_DOMAIN); 406 407 progname = argv[0]; 408 409 if (argc < 2) 410 usage(); 411 412 for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) { 413 cmdp = &cmds[i]; 414 arglen = strlen(argv[1]); 415 cmdlen = strlen(cmdp->c_name); 416 if ((arglen == cmdlen) && (strncmp(argv[1], cmdp->c_name, 417 cmdlen) == 0)) { 418 cmdp->c_fn(argc - 1, &argv[1]); 419 exit(0); 420 } 421 } 422 423 (void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"), 424 progname, argv[1]); 425 usage(); 426 427 return (0); 428 } 429 430 static const char * 431 match_attr(char *attr) 432 { 433 int i; 434 435 for (i = 0; i < NATTR; i++) { 436 if (strlen(attr) == strlen(attr_table[i]) && 437 strncmp(attr, attr_table[i], strlen(attr_table[i])) == 0) { 438 return (attr); 439 } 440 } 441 return (NULL); 442 } 443 444 /* ARGSUSED */ 445 static void 446 do_init_flow(int argc, char *argv[]) 447 { 448 dladm_status_t status; 449 450 status = dladm_flow_init(); 451 if (status != DLADM_STATUS_OK) 452 die_dlerr(status, "flows initialization failed"); 453 } 454 455 /* ARGSUSED */ 456 static int 457 show_usage_date(dladm_usage_t *usage, void *arg) 458 { 459 460 time_t stime; 461 char timebuf[20]; 462 463 stime = usage->du_stime; 464 (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y", 465 localtime(&stime)); 466 (void) printf("%s\n", timebuf); 467 468 return (DLADM_STATUS_OK); 469 } 470 471 static int 472 show_usage_time(dladm_usage_t *usage, void *arg) 473 { 474 show_usage_state_t *state = (show_usage_state_t *)arg; 475 char buf[DLADM_STRSIZE]; 476 usage_l_fields_buf_t ubuf; 477 time_t time; 478 double bw; 479 480 if (state->us_plot) { 481 if (!state->us_printheader) { 482 if (state->us_first) { 483 (void) printf("# Time"); 484 state->us_first = B_FALSE; 485 } 486 (void) printf(" %s", usage->du_name); 487 if (usage->du_last) { 488 (void) printf("\n"); 489 state->us_first = B_TRUE; 490 state->us_printheader = B_TRUE; 491 } 492 } else { 493 if (state->us_first) { 494 time = usage->du_etime; 495 (void) strftime(buf, sizeof (buf), "%T", 496 localtime(&time)); 497 state->us_first = B_FALSE; 498 (void) printf("%s", buf); 499 } 500 bw = (double)usage->du_bandwidth/1000; 501 (void) printf(" %.2f", bw); 502 if (usage->du_last) { 503 (void) printf("\n"); 504 state->us_first = B_TRUE; 505 } 506 } 507 return (DLADM_STATUS_OK); 508 } 509 510 bzero(&ubuf, sizeof (ubuf)); 511 512 (void) snprintf(ubuf.usage_l_flow, sizeof (ubuf.usage_l_flow), "%s", 513 usage->du_name); 514 time = usage->du_stime; 515 (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); 516 (void) snprintf(ubuf.usage_l_stime, sizeof (ubuf.usage_l_stime), "%s", 517 buf); 518 time = usage->du_etime; 519 (void) strftime(buf, sizeof (buf), "%T", localtime(&time)); 520 (void) snprintf(ubuf.usage_l_etime, sizeof (ubuf.usage_l_etime), "%s", 521 buf); 522 (void) snprintf(ubuf.usage_l_rbytes, sizeof (ubuf.usage_l_rbytes), 523 "%llu", usage->du_rbytes); 524 (void) snprintf(ubuf.usage_l_obytes, sizeof (ubuf.usage_l_obytes), 525 "%llu", usage->du_obytes); 526 (void) snprintf(ubuf.usage_l_bandwidth, sizeof (ubuf.usage_l_bandwidth), 527 "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf)); 528 529 if (!state->us_parseable && !state->us_printheader) { 530 print_header(&state->us_print); 531 state->us_printheader = B_TRUE; 532 } 533 534 flowadm_print_output(&state->us_print, state->us_parseable, 535 flowadm_print_field, (void *)&ubuf); 536 537 return (DLADM_STATUS_OK); 538 } 539 540 static int 541 show_usage_res(dladm_usage_t *usage, void *arg) 542 { 543 show_usage_state_t *state = (show_usage_state_t *)arg; 544 char buf[DLADM_STRSIZE]; 545 usage_fields_buf_t ubuf; 546 547 bzero(&ubuf, sizeof (ubuf)); 548 549 (void) snprintf(ubuf.usage_flow, sizeof (ubuf.usage_flow), "%s", 550 usage->du_name); 551 (void) snprintf(ubuf.usage_duration, sizeof (ubuf.usage_duration), 552 "%llu", usage->du_duration); 553 (void) snprintf(ubuf.usage_ipackets, sizeof (ubuf.usage_ipackets), 554 "%llu", usage->du_ipackets); 555 (void) snprintf(ubuf.usage_rbytes, sizeof (ubuf.usage_rbytes), 556 "%llu", usage->du_rbytes); 557 (void) snprintf(ubuf.usage_opackets, sizeof (ubuf.usage_opackets), 558 "%llu", usage->du_opackets); 559 (void) snprintf(ubuf.usage_obytes, sizeof (ubuf.usage_obytes), 560 "%llu", usage->du_obytes); 561 (void) snprintf(ubuf.usage_bandwidth, sizeof (ubuf.usage_bandwidth), 562 "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf)); 563 564 if (!state->us_parseable && !state->us_printheader) { 565 print_header(&state->us_print); 566 state->us_printheader = B_TRUE; 567 } 568 569 flowadm_print_output(&state->us_print, state->us_parseable, 570 flowadm_print_field, (void *)&ubuf); 571 572 return (DLADM_STATUS_OK); 573 } 574 575 static boolean_t 576 valid_formatspec(char *formatspec_str) 577 { 578 if (strcmp(formatspec_str, "gnuplot") == 0) 579 return (B_TRUE); 580 return (B_FALSE); 581 } 582 583 /* ARGSUSED */ 584 static void 585 do_show_usage(int argc, char *argv[]) 586 { 587 char *file = NULL; 588 int opt; 589 dladm_status_t status; 590 boolean_t d_arg = B_FALSE; 591 boolean_t p_arg = B_FALSE; 592 char *stime = NULL; 593 char *etime = NULL; 594 char *resource = NULL; 595 show_usage_state_t state; 596 boolean_t o_arg = B_FALSE; 597 boolean_t F_arg = B_FALSE; 598 char *fields_str = NULL; 599 char *formatspec_str = NULL; 600 print_field_t **fields; 601 uint_t nfields; 602 char *all_fields = 603 "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth"; 604 char *all_l_fields = 605 "flow,start,end,rbytes,obytes,bandwidth"; 606 607 bzero(&state, sizeof (show_usage_state_t)); 608 state.us_parseable = B_FALSE; 609 state.us_printheader = B_FALSE; 610 state.us_plot = B_FALSE; 611 state.us_first = B_TRUE; 612 613 while ((opt = getopt(argc, argv, "dps:e:o:f:F:")) != -1) { 614 switch (opt) { 615 case 'd': 616 d_arg = B_TRUE; 617 break; 618 case 'p': 619 state.us_plot = p_arg = B_TRUE; 620 break; 621 case 'f': 622 file = optarg; 623 break; 624 case 's': 625 stime = optarg; 626 break; 627 case 'e': 628 etime = optarg; 629 break; 630 case 'o': 631 o_arg = B_TRUE; 632 fields_str = optarg; 633 break; 634 case 'F': 635 F_arg = B_TRUE; 636 formatspec_str = optarg; 637 break; 638 default: 639 die_opterr(optopt, opt); 640 } 641 } 642 643 if (file == NULL) 644 die("show-usage requires a file"); 645 646 if (optind == (argc-1)) { 647 resource = argv[optind]; 648 } 649 650 if (resource == NULL && stime == NULL && etime == NULL) { 651 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 652 fields_str = all_fields; 653 fields = parse_output_fields(fields_str, usage_fields, 654 USAGE_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 655 } else { 656 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 657 fields_str = all_l_fields; 658 fields = parse_output_fields(fields_str, usage_l_fields, 659 USAGE_L_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 660 } 661 662 if (fields == NULL) { 663 die("invalid fields(s) specified"); 664 return; 665 } 666 state.us_print.ps_fields = fields; 667 state.us_print.ps_nfields = nfields; 668 669 if (p_arg && d_arg) 670 die("plot and date options are incompatible"); 671 672 if (p_arg && !F_arg) 673 die("specify format speicifier: -F <format>"); 674 675 if (F_arg && valid_formatspec(formatspec_str) == B_FALSE) 676 die("Format specifier %s not supported", formatspec_str); 677 678 if (d_arg) { 679 /* Print log dates */ 680 status = dladm_usage_dates(show_usage_date, 681 DLADM_LOGTYPE_FLOW, file, resource, &state); 682 } else if (resource == NULL && stime == NULL && etime == NULL && 683 !p_arg) { 684 /* Print summary */ 685 status = dladm_usage_summary(show_usage_res, 686 DLADM_LOGTYPE_FLOW, file, &state); 687 } else if (resource != NULL) { 688 /* Print log entries for named resource */ 689 status = dladm_walk_usage_res(show_usage_time, 690 DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state); 691 } else { 692 /* Print time and information for each link */ 693 status = dladm_walk_usage_time(show_usage_time, 694 DLADM_LOGTYPE_FLOW, file, stime, etime, &state); 695 } 696 697 if (status != DLADM_STATUS_OK) 698 die_dlerr(status, "show-usage"); 699 } 700 701 static void 702 do_add_flow(int argc, char *argv[]) 703 { 704 char devname[MAXNAMELEN]; 705 char *name = NULL; 706 uint_t index; 707 datalink_id_t linkid; 708 709 char option; 710 boolean_t l_arg = B_FALSE; 711 dladm_arg_list_t *proplist = NULL; 712 dladm_arg_list_t *attrlist = NULL; 713 dladm_status_t status; 714 715 while ((option = getopt_long(argc, argv, "tR:l:a:p:", 716 prop_longopts, NULL)) != -1) { 717 switch (option) { 718 case 't': 719 t_arg = B_TRUE; 720 break; 721 case 'R': 722 altroot = optarg; 723 break; 724 case 'l': 725 if (strlcpy(devname, optarg, 726 MAXNAMELEN) >= MAXNAMELEN) { 727 die("link name too long"); 728 } 729 if (dladm_name2info(devname, &linkid, NULL, 730 NULL, NULL) != DLADM_STATUS_OK) 731 die("invalid link '%s'", devname); 732 l_arg = B_TRUE; 733 break; 734 case 'a': 735 if (dladm_parse_flow_attrs(optarg, &attrlist, B_FALSE) 736 != DLADM_STATUS_OK) 737 die("invalid flow attribute specified"); 738 break; 739 case 'p': 740 if (dladm_parse_flow_props(optarg, &proplist, B_FALSE) 741 != DLADM_STATUS_OK) 742 die("invalid flow property specified"); 743 break; 744 default: 745 die_opterr(optopt, option); 746 } 747 } 748 if (!l_arg) { 749 die("link is required"); 750 } 751 752 opterr = 0; 753 index = optind; 754 755 if ((index != (argc - 1)) || match_attr(argv[index]) != NULL) { 756 die("flow name is required"); 757 } else { 758 /* get flow name; required last argument */ 759 if (strlen(argv[index]) >= MAXFLOWNAME) 760 die("flow name too long"); 761 name = argv[index]; 762 } 763 764 status = dladm_flow_add(linkid, attrlist, proplist, name, 765 t_arg, altroot); 766 if (status != DLADM_STATUS_OK) 767 die_dlerr(status, "add flow failed"); 768 769 dladm_free_attrs(attrlist); 770 dladm_free_props(proplist); 771 } 772 773 static void 774 do_remove_flow(int argc, char *argv[]) 775 { 776 char option; 777 char *flowname = NULL; 778 char linkname[MAXNAMELEN]; 779 datalink_id_t linkid = DATALINK_ALL_LINKID; 780 boolean_t l_arg = B_FALSE; 781 remove_flow_state_t state; 782 dladm_status_t status; 783 784 bzero(&state, sizeof (state)); 785 786 opterr = 0; 787 while ((option = getopt_long(argc, argv, ":tR:l:", 788 longopts, NULL)) != -1) { 789 switch (option) { 790 case 't': 791 t_arg = B_TRUE; 792 break; 793 case 'R': 794 altroot = optarg; 795 break; 796 case 'l': 797 if (strlcpy(linkname, optarg, 798 MAXLINKNAMELEN) >= MAXLINKNAMELEN) { 799 die("link name too long"); 800 } 801 if (dladm_name2info(linkname, &linkid, NULL, 802 NULL, NULL) != DLADM_STATUS_OK) { 803 die("invalid link '%s'", linkname); 804 } 805 l_arg = B_TRUE; 806 break; 807 default: 808 die_opterr(optopt, option); 809 break; 810 } 811 } 812 813 /* when link not specified get flow name */ 814 if (!l_arg) { 815 if (optind != (argc-1)) { 816 usage(); 817 } else { 818 if (strlen(argv[optind]) >= MAXFLOWNAME) 819 die("flow name too long"); 820 flowname = argv[optind]; 821 } 822 status = dladm_flow_remove(flowname, t_arg, altroot); 823 } else { 824 /* if link is specified then flow name should not be there */ 825 if (optind == argc-1) 826 usage(); 827 /* walk the link to find flows and remove them */ 828 state.fs_tempop = t_arg; 829 state.fs_altroot = altroot; 830 state.fs_status = DLADM_STATUS_OK; 831 status = dladm_walk_flow(remove_flow, linkid, &state, B_FALSE); 832 /* 833 * check if dladm_walk_flow terminated early and see if the 834 * walker function as any status for us 835 */ 836 if (status == DLADM_STATUS_OK) 837 status = state.fs_status; 838 } 839 840 if (status != DLADM_STATUS_OK) 841 die_dlerr(status, "remove flow failed"); 842 } 843 844 /* 845 * Walker function for removing a flow through dladm_walk_flow(); 846 */ 847 static int 848 remove_flow(dladm_flow_attr_t *attr, void *arg) 849 { 850 remove_flow_state_t *state = (remove_flow_state_t *)arg; 851 852 state->fs_status = dladm_flow_remove(attr->fa_flowname, 853 state->fs_tempop, state->fs_altroot); 854 855 if (state->fs_status == DLADM_STATUS_OK) 856 return (DLADM_WALK_CONTINUE); 857 else 858 return (DLADM_WALK_TERMINATE); 859 } 860 861 static char * 862 flowadm_print_field(print_field_t *pf, void *arg) 863 { 864 char *value; 865 866 value = (char *)arg + pf->pf_offset; 867 return (value); 868 } 869 870 /*ARGSUSED*/ 871 static dladm_status_t 872 print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr, 873 flow_fields_buf_t *fbuf) 874 { 875 char link[MAXLINKNAMELEN]; 876 dladm_status_t status; 877 878 if ((status = dladm_datalink_id2info(attr->fa_linkid, NULL, NULL, 879 NULL, link, sizeof (link))) != DLADM_STATUS_OK) { 880 return (status); 881 } 882 883 (void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name), 884 "%s", attr->fa_flowname); 885 (void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link), 886 "%s", link); 887 888 (void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr, 889 sizeof (fbuf->flow_ipaddr)); 890 (void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto, 891 sizeof (fbuf->flow_proto)); 892 (void) dladm_flow_attr_port2str(attr, fbuf->flow_port, 893 sizeof (fbuf->flow_port)); 894 (void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield, 895 sizeof (fbuf->flow_dsfield)); 896 897 return (DLADM_STATUS_OK); 898 } 899 900 /* 901 * Walker function for showing flow attributes through dladm_walk_flow(). 902 */ 903 static int 904 show_flow(dladm_flow_attr_t *attr, void *arg) 905 { 906 show_flow_state_t *statep = arg; 907 dladm_status_t status; 908 flow_fields_buf_t fbuf; 909 910 /* 911 * first get all the flow attributes into fbuf; 912 */ 913 bzero(&fbuf, sizeof (fbuf)); 914 status = print_flow(statep, attr, &fbuf); 915 916 if (status != DLADM_STATUS_OK) 917 goto done; 918 919 if (!statep->fs_parseable && !statep->fs_printheader) { 920 print_header(&statep->fs_print); 921 statep->fs_printheader = B_TRUE; 922 } 923 924 flowadm_print_output(&statep->fs_print, statep->fs_parseable, 925 flowadm_print_field, (void *)&fbuf); 926 927 done: 928 statep->fs_status = status; 929 return (DLADM_WALK_CONTINUE); 930 } 931 932 static void 933 show_one_flow(void *arg, const char *name) 934 { 935 dladm_flow_attr_t attr; 936 dladm_status_t status; 937 938 if (dladm_flow_info(name, &attr) != DLADM_STATUS_OK) 939 die("invalid flow: '%s'", name); 940 else 941 show_flow(&attr, arg); 942 } 943 944 /* 945 * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to 946 * dladm_walk_datalink_id(). Used for showing flow attributes for 947 * all flows on all links. 948 */ 949 static int 950 show_flows_onelink(datalink_id_t linkid, void *arg) 951 { 952 show_flow_state_t *state = arg; 953 954 (void) dladm_walk_flow(show_flow, linkid, arg, state->fs_persist); 955 956 return (DLADM_WALK_CONTINUE); 957 } 958 959 static void 960 get_flow_stats(const char *flowname, pktsum_t *stats) 961 { 962 kstat_ctl_t *kcp; 963 kstat_t *ksp; 964 965 bzero(stats, sizeof (*stats)); 966 967 if ((kcp = kstat_open()) == NULL) { 968 warn("kstat open operation failed"); 969 return; 970 } 971 972 ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow"); 973 974 if (ksp != NULL) 975 dladm_get_stats(kcp, ksp, stats); 976 977 (void) kstat_close(kcp); 978 } 979 980 /* ARGSUSED */ 981 static int 982 show_flow_stats(dladm_flow_attr_t *attr, void *arg) 983 { 984 show_flow_state_t *state = (show_flow_state_t *)arg; 985 const char *name = attr->fa_flowname; 986 pktsum_t stats, diff_stats; 987 988 if (state->fs_firstonly) { 989 if (state->fs_donefirst) 990 return (DLADM_WALK_TERMINATE); 991 state->fs_donefirst = B_TRUE; 992 } else { 993 bzero(&state->fs_prevstats, sizeof (state->fs_prevstats)); 994 } 995 996 get_flow_stats(name, &stats); 997 dladm_stats_diff(&diff_stats, &stats, &state->fs_prevstats); 998 999 (void) printf("%-12s", name); 1000 (void) printf("%-10llu", diff_stats.ipackets); 1001 (void) printf("%-12llu", diff_stats.rbytes); 1002 (void) printf("%-8llu", diff_stats.ierrors); 1003 (void) printf("%-10llu", diff_stats.opackets); 1004 (void) printf("%-12llu", diff_stats.obytes); 1005 (void) printf("%-8llu\n", diff_stats.oerrors); 1006 1007 state->fs_prevstats = stats; 1008 1009 return (DLADM_WALK_CONTINUE); 1010 } 1011 1012 /* 1013 * Wrapper of dladm_walk_flow(show_flow,...) to make it usable for 1014 * dladm_walk_datalink_id(). Used for showing flow stats for 1015 * all flows on all links. 1016 */ 1017 static int 1018 show_link_flow_stats(datalink_id_t linkid, void * arg) 1019 { 1020 if (dladm_walk_flow(show_flow_stats, linkid, arg, B_FALSE) 1021 == DLADM_STATUS_OK) 1022 return (DLADM_WALK_CONTINUE); 1023 else 1024 return (DLADM_WALK_TERMINATE); 1025 } 1026 1027 /* ARGSUSED */ 1028 static void 1029 flow_stats(const char *flow, datalink_id_t linkid, uint_t interval) 1030 { 1031 show_flow_state_t state; 1032 dladm_flow_attr_t attr; 1033 1034 if (flow != NULL && dladm_flow_info(flow, &attr) != DLADM_STATUS_OK) 1035 die("invalid flow %s", flow); 1036 1037 bzero(&state, sizeof (state)); 1038 1039 /* 1040 * If an interval is specified, continuously show the stats 1041 * for only the first flow. 1042 */ 1043 state.fs_firstonly = (interval != 0); 1044 1045 for (;;) { 1046 if (!state.fs_donefirst) 1047 (void) printf("%-12s%-10s%-12s%-8s%-10s%-12s%-8s\n", 1048 "FLOW", "IPACKETS", "RBYTES", "IERRORS", 1049 "OPACKETS", "OBYTES", "OERRORS"); 1050 1051 state.fs_donefirst = B_FALSE; 1052 1053 /* Show stats for named flow */ 1054 if (flow != NULL) { 1055 state.fs_flow = flow; 1056 (void) show_flow_stats(&attr, &state); 1057 1058 /* Show all stats on a link */ 1059 } else if (linkid != DATALINK_INVALID_LINKID) { 1060 (void) dladm_walk_flow(show_flow_stats, linkid, &state, 1061 B_FALSE); 1062 1063 /* Show all stats by datalink */ 1064 } else { 1065 (void) dladm_walk_datalink_id(show_link_flow_stats, 1066 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1067 DLADM_OPT_ACTIVE); 1068 } 1069 1070 if (interval == 0) 1071 break; 1072 1073 (void) sleep(interval); 1074 } 1075 } 1076 1077 static void 1078 do_show_flow(int argc, char *argv[]) 1079 { 1080 char flowname[MAXFLOWNAME]; 1081 char linkname[MAXNAMELEN]; 1082 datalink_id_t linkid = DATALINK_ALL_LINKID; 1083 int option; 1084 boolean_t s_arg = B_FALSE; 1085 boolean_t S_arg = B_FALSE; 1086 boolean_t i_arg = B_FALSE; 1087 boolean_t l_arg = B_FALSE; 1088 boolean_t o_arg = B_FALSE; 1089 uint32_t interval = 0; 1090 char *endp = NULL; 1091 show_flow_state_t state; 1092 char *fields_str = NULL; 1093 print_field_t **fields; 1094 uint_t nfields; 1095 char *all_fields = 1096 "flow,link,ipaddr,transport,port,dsfield"; 1097 dladm_status_t status; 1098 1099 bzero(&state, sizeof (state)); 1100 1101 opterr = 0; 1102 while ((option = getopt_long(argc, argv, ":pPsSi:l:o:", 1103 longopts, NULL)) != -1) { 1104 switch (option) { 1105 case 'p': 1106 state.fs_parseable = B_TRUE; 1107 break; 1108 case 'P': 1109 state.fs_persist = B_TRUE; 1110 break; 1111 case 's': 1112 if (s_arg) 1113 die_optdup(option); 1114 1115 s_arg = B_TRUE; 1116 break; 1117 case 'S': 1118 if (S_arg) 1119 die_optdup(option); 1120 1121 S_arg = B_TRUE; 1122 break; 1123 case 'o': 1124 if (o_arg) 1125 die_optdup(option); 1126 1127 o_arg = B_TRUE; 1128 fields_str = optarg; 1129 break; 1130 case 'i': 1131 if (i_arg) 1132 die_optdup(option); 1133 1134 i_arg = B_TRUE; 1135 1136 errno = 0; 1137 interval = (int)strtol(optarg, &endp, 10); 1138 if (errno != 0 || interval == 0 || *endp != '\0') 1139 die("invalid interval value" " '%d'\n", 1140 interval); 1141 break; 1142 case 'l': 1143 if (strlcpy(linkname, optarg, MAXLINKNAMELEN) 1144 >= MAXLINKNAMELEN) 1145 die("link name too long\n"); 1146 if (dladm_name2info(linkname, &linkid, NULL, 1147 NULL, NULL) != DLADM_STATUS_OK) 1148 die("invalid link '%s'", linkname); 1149 l_arg = B_TRUE; 1150 break; 1151 default: 1152 die_opterr(optopt, option); 1153 break; 1154 } 1155 } 1156 if (i_arg && !(s_arg || S_arg)) 1157 die("the -i option can be used only with -s or -S"); 1158 1159 if (s_arg && S_arg) 1160 die("the -s option cannot be used with -S"); 1161 1162 /* get flow name (optional last argument */ 1163 if (optind == (argc-1)) { 1164 if (strlcpy(flowname, argv[optind], MAXFLOWNAME) 1165 >= MAXFLOWNAME) 1166 die("flow name too long"); 1167 state.fs_flow = flowname; 1168 } 1169 1170 if (s_arg) { 1171 flow_stats(state.fs_flow, linkid, interval); 1172 return; 1173 } 1174 1175 if (S_arg) { 1176 dladm_continuous(linkid, state.fs_flow, interval, FLOW_REPORT); 1177 return; 1178 } 1179 1180 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) 1181 fields_str = all_fields; 1182 1183 fields = parse_output_fields(fields_str, flow_fields, FLOW_MAX_FIELDS, 1184 CMD_TYPE_ANY, &nfields); 1185 1186 if (fields == NULL) { 1187 die("invalid fields(s) specified"); 1188 return; 1189 } 1190 1191 state.fs_print.ps_fields = fields; 1192 state.fs_print.ps_nfields = nfields; 1193 1194 /* Show attributes of one flow */ 1195 if (state.fs_flow != NULL) { 1196 show_one_flow(&state, state.fs_flow); 1197 1198 /* Show attributes of flows on one link */ 1199 } else if (l_arg) { 1200 (void) show_flows_onelink(linkid, &state); 1201 1202 /* Show attributes of all flows on all links */ 1203 } else { 1204 (void) dladm_walk_datalink_id(show_flows_onelink, &state, 1205 DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1206 DLADM_OPT_ACTIVE); 1207 } 1208 } 1209 1210 static dladm_status_t 1211 set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val, 1212 uint_t val_cnt, boolean_t reset) 1213 { 1214 dladm_status_t status; 1215 char *errprop; 1216 1217 status = dladm_set_flowprop(flow, prop_name, prop_val, val_cnt, 1218 DLADM_OPT_PERSIST, &errprop); 1219 1220 if (status != DLADM_STATUS_OK) { 1221 warn_dlerr(status, "cannot persistently %s flow " 1222 "property '%s' on '%s'", reset? "reset": "set", 1223 errprop, flow); 1224 } 1225 return (status); 1226 } 1227 1228 static void 1229 set_flowprop(int argc, char **argv, boolean_t reset) 1230 { 1231 int i, option; 1232 char errmsg[DLADM_STRSIZE]; 1233 const char *flow = NULL; 1234 dladm_arg_list_t *proplist = NULL; 1235 boolean_t temp = B_FALSE; 1236 dladm_status_t status = DLADM_STATUS_OK; 1237 1238 opterr = 0; 1239 while ((option = getopt_long(argc, argv, ":p:R:t", 1240 prop_longopts, NULL)) != -1) { 1241 switch (option) { 1242 case 'p': 1243 if (dladm_parse_flow_props(optarg, &proplist, reset) 1244 != DLADM_STATUS_OK) 1245 die("invalid flow property specified"); 1246 break; 1247 case 't': 1248 temp = B_TRUE; 1249 break; 1250 case 'R': 1251 status = dladm_set_rootdir(optarg); 1252 if (status != DLADM_STATUS_OK) { 1253 die_dlerr(status, "invalid directory " 1254 "specified"); 1255 } 1256 break; 1257 default: 1258 die_opterr(optopt, option); 1259 break; 1260 } 1261 } 1262 1263 if (optind == (argc - 1)) { 1264 if (strlen(argv[optind]) >= MAXFLOWNAME) 1265 die("flow name too long"); 1266 flow = argv[optind]; 1267 } else if (optind != argc) { 1268 usage(); 1269 } 1270 if (flow == NULL) 1271 die("flow name must be specified"); 1272 1273 if (proplist == NULL) { 1274 char *errprop; 1275 1276 if (!reset) 1277 die("flow property must be specified"); 1278 1279 status = dladm_set_flowprop(flow, NULL, NULL, 0, 1280 DLADM_OPT_ACTIVE, &errprop); 1281 if (status != DLADM_STATUS_OK) { 1282 warn_dlerr(status, "cannot reset flow property '%s' " 1283 "on '%s'", errprop, flow); 1284 } 1285 if (!temp) { 1286 dladm_status_t s; 1287 1288 s = set_flowprop_persist(flow, NULL, NULL, 0, reset); 1289 if (s != DLADM_STATUS_OK) 1290 status = s; 1291 } 1292 goto done; 1293 } 1294 1295 for (i = 0; i < proplist->al_count; i++) { 1296 dladm_arg_info_t *aip = &proplist->al_info[i]; 1297 char **val; 1298 uint_t count; 1299 dladm_status_t s; 1300 1301 if (reset) { 1302 val = NULL; 1303 count = 0; 1304 } else { 1305 val = aip->ai_val; 1306 count = aip->ai_count; 1307 if (count == 0) { 1308 warn("no value specified for '%s'", 1309 aip->ai_name); 1310 status = DLADM_STATUS_BADARG; 1311 continue; 1312 } 1313 } 1314 s = dladm_set_flowprop(flow, aip->ai_name, val, count, 1315 DLADM_OPT_ACTIVE, NULL); 1316 if (s == DLADM_STATUS_OK) { 1317 if (!temp) { 1318 s = set_flowprop_persist(flow, 1319 aip->ai_name, val, count, reset); 1320 if (s != DLADM_STATUS_OK) 1321 status = s; 1322 } 1323 continue; 1324 } 1325 status = s; 1326 switch (s) { 1327 case DLADM_STATUS_NOTFOUND: 1328 warn("invalid flow property '%s'", aip->ai_name); 1329 break; 1330 case DLADM_STATUS_BADVAL: { 1331 int j; 1332 char *ptr, *lim; 1333 char **propvals = NULL; 1334 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 1335 1336 ptr = malloc((sizeof (char *) + 1337 DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT + 1338 MAX_PROP_LINE); 1339 1340 if (ptr == NULL) 1341 die("insufficient memory"); 1342 propvals = (char **)(void *)ptr; 1343 1344 for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) { 1345 propvals[j] = ptr + sizeof (char *) * 1346 DLADM_MAX_PROP_VALCNT + 1347 j * DLADM_PROP_VAL_MAX; 1348 } 1349 s = dladm_get_flowprop(flow, DLADM_PROP_VAL_MODIFIABLE, 1350 aip->ai_name, propvals, &valcnt); 1351 1352 ptr = errmsg; 1353 lim = ptr + DLADM_STRSIZE; 1354 *ptr = '\0'; 1355 for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) { 1356 ptr += snprintf(ptr, lim - ptr, "%s,", 1357 propvals[j]); 1358 if (ptr >= lim) 1359 break; 1360 } 1361 if (ptr > errmsg) { 1362 *(ptr - 1) = '\0'; 1363 warn("flow property '%s' must be one of: %s", 1364 aip->ai_name, errmsg); 1365 } else 1366 warn("%s is an invalid value for " 1367 "flow property %s", *val, aip->ai_name); 1368 free(propvals); 1369 break; 1370 } 1371 default: 1372 if (reset) { 1373 warn_dlerr(status, "cannot reset flow property " 1374 "'%s' on '%s'", aip->ai_name, flow); 1375 } else { 1376 warn_dlerr(status, "cannot set flow property " 1377 "'%s' on '%s'", aip->ai_name, flow); 1378 } 1379 break; 1380 } 1381 } 1382 done: 1383 dladm_free_props(proplist); 1384 if (status != DLADM_STATUS_OK) 1385 exit(1); 1386 } 1387 1388 static void 1389 do_set_flowprop(int argc, char **argv) 1390 { 1391 set_flowprop(argc, argv, B_FALSE); 1392 } 1393 1394 static void 1395 do_reset_flowprop(int argc, char **argv) 1396 { 1397 set_flowprop(argc, argv, B_TRUE); 1398 } 1399 1400 static void 1401 warn(const char *format, ...) 1402 { 1403 va_list alist; 1404 1405 format = gettext(format); 1406 (void) fprintf(stderr, "%s: warning: ", progname); 1407 1408 va_start(alist, format); 1409 (void) vfprintf(stderr, format, alist); 1410 va_end(alist); 1411 1412 (void) putchar('\n'); 1413 } 1414 1415 /* PRINTFLIKE2 */ 1416 static void 1417 warn_dlerr(dladm_status_t err, const char *format, ...) 1418 { 1419 va_list alist; 1420 char errmsg[DLADM_STRSIZE]; 1421 1422 format = gettext(format); 1423 (void) fprintf(stderr, gettext("%s: warning: "), progname); 1424 1425 va_start(alist, format); 1426 (void) vfprintf(stderr, format, alist); 1427 va_end(alist); 1428 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1429 } 1430 1431 /* PRINTFLIKE1 */ 1432 static void 1433 die(const char *format, ...) 1434 { 1435 va_list alist; 1436 1437 format = gettext(format); 1438 (void) fprintf(stderr, "%s: ", progname); 1439 1440 va_start(alist, format); 1441 (void) vfprintf(stderr, format, alist); 1442 va_end(alist); 1443 1444 (void) putchar('\n'); 1445 exit(EXIT_FAILURE); 1446 } 1447 1448 static void 1449 die_optdup(int opt) 1450 { 1451 die("the option -%c cannot be specified more than once", opt); 1452 } 1453 1454 static void 1455 die_opterr(int opt, int opterr) 1456 { 1457 switch (opterr) { 1458 case ':': 1459 die("option '-%c' requires a value", opt); 1460 break; 1461 case '?': 1462 default: 1463 die("unrecognized option '-%c'", opt); 1464 break; 1465 } 1466 } 1467 1468 /* PRINTFLIKE2 */ 1469 static void 1470 die_dlerr(dladm_status_t err, const char *format, ...) 1471 { 1472 va_list alist; 1473 char errmsg[DLADM_STRSIZE]; 1474 1475 format = gettext(format); 1476 (void) fprintf(stderr, "%s: ", progname); 1477 1478 va_start(alist, format); 1479 (void) vfprintf(stderr, format, alist); 1480 va_end(alist); 1481 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1482 1483 exit(EXIT_FAILURE); 1484 } 1485 1486 static void 1487 print_flowprop(const char *flowname, show_flowprop_state_t *statep, 1488 const char *propname, dladm_prop_type_t type, 1489 const char *format, char **pptr) 1490 { 1491 int i; 1492 char *ptr, *lim; 1493 char buf[DLADM_STRSIZE]; 1494 char *unknown = "--", *notsup = ""; 1495 char **propvals = statep->fs_propvals; 1496 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 1497 dladm_status_t status; 1498 1499 status = dladm_get_flowprop(flowname, type, propname, propvals, 1500 &valcnt); 1501 if (status != DLADM_STATUS_OK) { 1502 if (status == DLADM_STATUS_TEMPONLY) { 1503 if (type == DLADM_PROP_VAL_MODIFIABLE && 1504 statep->fs_persist) { 1505 valcnt = 1; 1506 propvals = &unknown; 1507 } else { 1508 statep->fs_status = status; 1509 statep->fs_retstatus = status; 1510 return; 1511 } 1512 } else if (status == DLADM_STATUS_NOTSUP || 1513 statep->fs_persist) { 1514 valcnt = 1; 1515 if (type == DLADM_PROP_VAL_CURRENT) 1516 propvals = &unknown; 1517 else 1518 propvals = ¬sup; 1519 } else { 1520 if ((statep->fs_proplist != NULL) && 1521 statep->fs_status == DLADM_STATUS_OK) { 1522 warn("invalid flow property '%s'", propname); 1523 } 1524 statep->fs_status = status; 1525 statep->fs_retstatus = status; 1526 return; 1527 } 1528 } 1529 1530 statep->fs_status = DLADM_STATUS_OK; 1531 1532 ptr = buf; 1533 lim = buf + DLADM_STRSIZE; 1534 for (i = 0; i < valcnt; i++) { 1535 if (propvals[i][0] == '\0' && !statep->fs_parseable) 1536 ptr += snprintf(ptr, lim - ptr, STR_UNDEF_VAL","); 1537 else 1538 ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]); 1539 if (ptr >= lim) 1540 break; 1541 } 1542 if (valcnt > 0) 1543 buf[strlen(buf) - 1] = '\0'; 1544 1545 lim = statep->fs_line + MAX_PROP_LINE; 1546 if (statep->fs_parseable) { 1547 *pptr += snprintf(*pptr, lim - *pptr, 1548 "%s", buf); 1549 } else { 1550 *pptr += snprintf(*pptr, lim - *pptr, format, buf); 1551 } 1552 } 1553 1554 static char * 1555 flowprop_callback(print_field_t *pf, void *fs_arg) 1556 { 1557 flowprop_args_t *arg = fs_arg; 1558 char *propname = arg->fs_propname; 1559 show_flowprop_state_t *statep = arg->fs_state; 1560 char *ptr = statep->fs_line; 1561 char *lim = ptr + MAX_PROP_LINE; 1562 char *flowname = arg->fs_flowname; 1563 1564 switch (pf->pf_index) { 1565 case FLOWPROP_FLOW: 1566 (void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow); 1567 break; 1568 case FLOWPROP_PROPERTY: 1569 (void) snprintf(ptr, lim - ptr, "%s", propname); 1570 break; 1571 case FLOWPROP_VALUE: 1572 print_flowprop(flowname, statep, propname, 1573 statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT : 1574 DLADM_PROP_VAL_CURRENT, "%s", &ptr); 1575 /* 1576 * If we failed to query the flow property, for example, query 1577 * the persistent value of a non-persistable flow property, 1578 * simply skip the output. 1579 */ 1580 if (statep->fs_status != DLADM_STATUS_OK) 1581 goto skip; 1582 ptr = statep->fs_line; 1583 break; 1584 case FLOWPROP_DEFAULT: 1585 print_flowprop(flowname, statep, propname, 1586 DLADM_PROP_VAL_DEFAULT, "%s", &ptr); 1587 if (statep->fs_status != DLADM_STATUS_OK) 1588 goto skip; 1589 ptr = statep->fs_line; 1590 break; 1591 case FLOWPROP_POSSIBLE: 1592 print_flowprop(flowname, statep, propname, 1593 DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr); 1594 if (statep->fs_status != DLADM_STATUS_OK) 1595 goto skip; 1596 ptr = statep->fs_line; 1597 break; 1598 default: 1599 die("invalid input"); 1600 break; 1601 } 1602 return (ptr); 1603 skip: 1604 if (statep->fs_status != DLADM_STATUS_OK) 1605 return (NULL); 1606 else 1607 return (""); 1608 } 1609 1610 static int 1611 show_one_flowprop(void *arg, const char *propname) 1612 { 1613 show_flowprop_state_t *statep = arg; 1614 flowprop_args_t fs_arg; 1615 1616 bzero(&fs_arg, sizeof (fs_arg)); 1617 fs_arg.fs_state = statep; 1618 fs_arg.fs_propname = (char *)propname; 1619 fs_arg.fs_flowname = (char *)statep->fs_flow; 1620 1621 if (statep->fs_header) { 1622 statep->fs_header = B_FALSE; 1623 if (!statep ->fs_parseable) 1624 print_header(&statep->fs_print); 1625 } 1626 flowadm_print_output(&statep->fs_print, statep->fs_parseable, 1627 flowprop_callback, (void *)&fs_arg); 1628 1629 return (DLADM_WALK_CONTINUE); 1630 } 1631 1632 /* Walker function called by dladm_walk_flow to display flow properties */ 1633 static int 1634 show_flowprop(dladm_flow_attr_t *attr, void *arg) 1635 { 1636 show_flowprop_one_flow(arg, attr->fa_flowname); 1637 return (DLADM_WALK_CONTINUE); 1638 } 1639 1640 /* 1641 * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it 1642 * usable to dladm_walk_datalink_id() 1643 */ 1644 static int 1645 show_flowprop_onelink(datalink_id_t linkid, void *arg) 1646 { 1647 char name[MAXLINKNAMELEN]; 1648 1649 if (dladm_datalink_id2info(linkid, NULL, NULL, NULL, 1650 name, sizeof (name)) != DLADM_STATUS_OK) 1651 return (DLADM_WALK_TERMINATE); 1652 1653 (void) dladm_walk_flow(show_flowprop, linkid, arg, B_FALSE); 1654 1655 return (DLADM_WALK_CONTINUE); 1656 } 1657 1658 static void 1659 do_show_flowprop(int argc, char **argv) 1660 { 1661 int option; 1662 dladm_arg_list_t *proplist = NULL; 1663 show_flowprop_state_t state; 1664 char *fields_str = NULL; 1665 print_field_t **fields; 1666 uint_t nfields; 1667 char *all_fields = 1668 "flow,property,value,default,possible"; 1669 1670 fields_str = all_fields; 1671 opterr = 0; 1672 state.fs_propvals = NULL; 1673 state.fs_line = NULL; 1674 state.fs_parseable = B_FALSE; 1675 state.fs_persist = B_FALSE; 1676 state.fs_header = B_TRUE; 1677 state.fs_retstatus = DLADM_STATUS_OK; 1678 state.fs_linkid = DATALINK_INVALID_LINKID; 1679 state.fs_flow = NULL; 1680 1681 while ((option = getopt_long(argc, argv, ":p:cPl:o:", 1682 prop_longopts, NULL)) != -1) { 1683 switch (option) { 1684 case 'p': 1685 if (dladm_parse_flow_props(optarg, &proplist, B_TRUE) 1686 != DLADM_STATUS_OK) 1687 die("invalid flow properties specified"); 1688 break; 1689 case 'c': 1690 state.fs_parseable = B_TRUE; 1691 break; 1692 case 'P': 1693 state.fs_persist = B_TRUE; 1694 break; 1695 case 'l': 1696 if (dladm_name2info(optarg, &state.fs_linkid, 1697 NULL, NULL, NULL) != DLADM_STATUS_OK) 1698 die("invalid link '%s'", optarg); 1699 break; 1700 case 'o': 1701 if (strcasecmp(optarg, "all") == 0) 1702 fields_str = all_fields; 1703 else 1704 fields_str = optarg; 1705 break; 1706 default: 1707 die_opterr(optopt, option); 1708 break; 1709 } 1710 } 1711 1712 if (optind == (argc - 1)) { 1713 if (strlen(argv[optind]) >= MAXFLOWNAME) 1714 die("flow name too long"); 1715 state.fs_flow = argv[optind]; 1716 } else if (optind != argc) { 1717 usage(); 1718 } 1719 bzero(&state.fs_print, sizeof (print_state_t)); 1720 state.fs_proplist = proplist; 1721 state.fs_status = DLADM_STATUS_OK; 1722 1723 fields = parse_output_fields(fields_str, flowprop_fields, 1724 FLOWPROP_MAX_FIELDS, CMD_TYPE_ANY, &nfields); 1725 1726 if (fields == NULL) { 1727 die("invalid field(s) specified"); 1728 return; 1729 } 1730 1731 state.fs_print.ps_fields = fields; 1732 state.fs_print.ps_nfields = nfields; 1733 1734 /* Show properties for one flow */ 1735 if (state.fs_flow != NULL) { 1736 show_flowprop_one_flow(&state, state.fs_flow); 1737 1738 /* Show properties for all flows on one link */ 1739 } else if (state.fs_linkid != DATALINK_INVALID_LINKID) { 1740 (void) show_flowprop_onelink(state.fs_linkid, &state); 1741 1742 /* Show properties for all flows on all links */ 1743 } else { 1744 (void) dladm_walk_datalink_id(show_flowprop_onelink, &state, 1745 DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1746 DLADM_OPT_ACTIVE); 1747 } 1748 1749 dladm_free_props(proplist); 1750 } 1751 1752 static void 1753 show_flowprop_one_flow(void *arg, const char *flow) 1754 { 1755 int i; 1756 char *buf; 1757 dladm_status_t status; 1758 dladm_arg_list_t *proplist = NULL; 1759 show_flowprop_state_t *statep = arg; 1760 dladm_flow_attr_t attr; 1761 const char *savep; 1762 1763 /* 1764 * Do not print flow props for invalid flows. 1765 */ 1766 if ((status = dladm_flow_info(flow, &attr)) != DLADM_STATUS_OK) { 1767 die("invalid flow: '%s'", flow); 1768 } 1769 1770 savep = statep->fs_flow; 1771 statep->fs_flow = flow; 1772 1773 proplist = statep->fs_proplist; 1774 1775 buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) 1776 * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE); 1777 if (buf == NULL) 1778 die("insufficient memory"); 1779 1780 statep->fs_propvals = (char **)(void *)buf; 1781 for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) { 1782 statep->fs_propvals[i] = buf + 1783 sizeof (char *) * DLADM_MAX_PROP_VALCNT + 1784 i * DLADM_PROP_VAL_MAX; 1785 } 1786 statep->fs_line = buf + 1787 (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT; 1788 1789 /* show only specified flow properties */ 1790 if (proplist != NULL) { 1791 for (i = 0; i < proplist->al_count; i++) { 1792 if (show_one_flowprop(statep, 1793 proplist->al_info[i].ai_name) != DLADM_STATUS_OK) 1794 break; 1795 } 1796 1797 /* show all flow properties */ 1798 } else { 1799 status = dladm_walk_flowprop(show_one_flowprop, flow, statep); 1800 if (status != DLADM_STATUS_OK) 1801 die_dlerr(status, "show-flowprop"); 1802 } 1803 free(buf); 1804 statep->fs_flow = savep; 1805 } 1806 1807 typedef struct { 1808 char *s_buf; 1809 char **s_fields; /* array of pointer to the fields in s_buf */ 1810 uint_t s_nfields; /* the number of fields in s_buf */ 1811 } split_t; 1812 1813 /* 1814 * Free the split_t structure pointed to by `sp'. 1815 */ 1816 static void 1817 splitfree(split_t *sp) 1818 { 1819 free(sp->s_buf); 1820 free(sp->s_fields); 1821 free(sp); 1822 } 1823 1824 /* 1825 * Split `str' into at most `maxfields' fields, each field at most `maxlen' in 1826 * length. Return a pointer to a split_t containing the split fields, or NULL 1827 * on failure. 1828 */ 1829 static split_t * 1830 split(const char *str, uint_t maxfields, uint_t maxlen) 1831 { 1832 char *field, *token, *lasts = NULL; 1833 split_t *sp; 1834 1835 if (*str == '\0' || maxfields == 0 || maxlen == 0) 1836 return (NULL); 1837 1838 sp = calloc(sizeof (split_t), 1); 1839 if (sp == NULL) 1840 return (NULL); 1841 1842 sp->s_buf = strdup(str); 1843 sp->s_fields = malloc(sizeof (char *) * maxfields); 1844 if (sp->s_buf == NULL || sp->s_fields == NULL) 1845 goto fail; 1846 1847 token = sp->s_buf; 1848 while ((field = strtok_r(token, ",", &lasts)) != NULL) { 1849 if (sp->s_nfields == maxfields || strlen(field) > maxlen) 1850 goto fail; 1851 token = NULL; 1852 sp->s_fields[sp->s_nfields++] = field; 1853 } 1854 return (sp); 1855 fail: 1856 splitfree(sp); 1857 return (NULL); 1858 } 1859 1860 static print_field_t ** 1861 parse_output_fields(char *str, print_field_t *template, int max_fields, 1862 uint_t cmdtype, uint_t *countp) 1863 { 1864 split_t *sp; 1865 boolean_t good_match = B_FALSE; 1866 uint_t i, j; 1867 print_field_t **pf = NULL; 1868 1869 sp = split(str, max_fields, MAX_FIELD_LEN); 1870 1871 if (sp == NULL) 1872 return (NULL); 1873 1874 pf = malloc(sp->s_nfields * sizeof (print_field_t *)); 1875 if (pf == NULL) 1876 goto fail; 1877 1878 for (i = 0; i < sp->s_nfields; i++) { 1879 for (j = 0; j < max_fields; j++) { 1880 if (strcasecmp(sp->s_fields[i], 1881 template[j].pf_name) == 0) { 1882 good_match = template[j]. pf_cmdtype & cmdtype; 1883 break; 1884 } 1885 } 1886 if (!good_match) 1887 goto fail; 1888 1889 good_match = B_FALSE; 1890 pf[i] = &template[j]; 1891 } 1892 *countp = i; 1893 splitfree(sp); 1894 return (pf); 1895 fail: 1896 free(pf); 1897 splitfree(sp); 1898 return (NULL); 1899 } 1900 1901 static void 1902 flowadm_print_output(print_state_t *statep, boolean_t parseable, 1903 print_callback_t fn, void *arg) 1904 { 1905 int i; 1906 char *value; 1907 print_field_t **pf; 1908 1909 pf = statep->ps_fields; 1910 for (i = 0; i < statep->ps_nfields; i++) { 1911 statep->ps_lastfield = (i + 1 == statep->ps_nfields); 1912 value = (*fn)(pf[i], arg); 1913 if (value != NULL) 1914 print_field(statep, pf[i], value, parseable); 1915 } 1916 (void) putchar('\n'); 1917 } 1918 1919 static void 1920 print_header(print_state_t *ps) 1921 { 1922 int i; 1923 print_field_t **pf; 1924 1925 pf = ps->ps_fields; 1926 for (i = 0; i < ps->ps_nfields; i++) { 1927 ps->ps_lastfield = (i + 1 == ps->ps_nfields); 1928 print_field(ps, pf[i], pf[i]->pf_header, B_FALSE); 1929 } 1930 (void) putchar('\n'); 1931 } 1932 1933 static void 1934 print_field(print_state_t *statep, print_field_t *pfp, const char *value, 1935 boolean_t parseable) 1936 { 1937 uint_t width = pfp->pf_width; 1938 uint_t valwidth = strlen(value); 1939 uint_t compress; 1940 1941 if (parseable) { 1942 (void) printf("%s=\"%s\"", pfp->pf_header, value); 1943 } else { 1944 if (value[0] == '\0') 1945 value = STR_UNDEF_VAL; 1946 if (statep->ps_lastfield) { 1947 (void) printf("%s", value); 1948 return; 1949 } 1950 1951 if (valwidth > width) { 1952 statep->ps_overflow += valwidth - width; 1953 } else if (valwidth < width && statep->ps_overflow > 0) { 1954 compress = min(statep->ps_overflow, width - valwidth); 1955 statep->ps_overflow -= compress; 1956 width -= compress; 1957 } 1958 (void) printf("%-*s", width, value); 1959 } 1960 1961 if (!statep->ps_lastfield) 1962 (void) putchar(' '); 1963 } 1964