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