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_handle_t, 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_handle_t, 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_handle_t, dladm_flow_attr_t *, void *); 94 95 static int show_flowprop(dladm_handle_t, 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 /*ARGSUSED*/ 876 static int 877 remove_flow(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) 878 { 879 remove_flow_state_t *state = (remove_flow_state_t *)arg; 880 881 state->fs_status = dladm_flow_remove(handle, attr->fa_flowname, 882 state->fs_tempop, state->fs_altroot); 883 884 if (state->fs_status == DLADM_STATUS_OK) 885 return (DLADM_WALK_CONTINUE); 886 else 887 return (DLADM_WALK_TERMINATE); 888 } 889 890 /*ARGSUSED*/ 891 static dladm_status_t 892 print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr, 893 flow_fields_buf_t *fbuf) 894 { 895 char link[MAXLINKNAMELEN]; 896 dladm_status_t status; 897 898 if ((status = dladm_datalink_id2info(handle, attr->fa_linkid, NULL, 899 NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) { 900 return (status); 901 } 902 903 (void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name), 904 "%s", attr->fa_flowname); 905 (void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link), 906 "%s", link); 907 908 (void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr, 909 sizeof (fbuf->flow_ipaddr)); 910 (void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto, 911 sizeof (fbuf->flow_proto)); 912 (void) dladm_flow_attr_port2str(attr, fbuf->flow_port, 913 sizeof (fbuf->flow_port)); 914 (void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield, 915 sizeof (fbuf->flow_dsfield)); 916 917 return (DLADM_STATUS_OK); 918 } 919 920 /* 921 * Walker function for showing flow attributes through dladm_walk_flow(). 922 */ 923 /*ARGSUSED*/ 924 static int 925 show_flow(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) 926 { 927 show_flow_state_t *statep = arg; 928 dladm_status_t status; 929 flow_fields_buf_t fbuf; 930 931 /* 932 * first get all the flow attributes into fbuf; 933 */ 934 bzero(&fbuf, sizeof (fbuf)); 935 status = print_flow(statep, attr, &fbuf); 936 937 if (status != DLADM_STATUS_OK) 938 goto done; 939 940 ofmt_print(statep->fs_ofmt, (void *)&fbuf); 941 942 done: 943 statep->fs_status = status; 944 return (DLADM_WALK_CONTINUE); 945 } 946 947 static void 948 show_one_flow(void *arg, const char *name) 949 { 950 dladm_flow_attr_t attr; 951 952 if (dladm_flow_info(handle, name, &attr) != DLADM_STATUS_OK) 953 die("invalid flow: '%s'", name); 954 else 955 (void) show_flow(handle, &attr, arg); 956 } 957 958 /* 959 * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to 960 * dladm_walk_datalink_id(). Used for showing flow attributes for 961 * all flows on all links. 962 */ 963 static int 964 show_flows_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg) 965 { 966 show_flow_state_t *state = arg; 967 968 (void) dladm_walk_flow(show_flow, dh, linkid, arg, state->fs_persist); 969 970 return (DLADM_WALK_CONTINUE); 971 } 972 973 static void 974 get_flow_stats(const char *flowname, pktsum_t *stats) 975 { 976 kstat_ctl_t *kcp; 977 kstat_t *ksp; 978 979 bzero(stats, sizeof (*stats)); 980 981 if ((kcp = kstat_open()) == NULL) { 982 warn("kstat open operation failed"); 983 return; 984 } 985 986 ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow"); 987 988 if (ksp != NULL) 989 dladm_get_stats(kcp, ksp, stats); 990 991 (void) kstat_close(kcp); 992 } 993 994 static boolean_t 995 print_flow_stats_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) 996 { 997 flow_args_t *fargs = of_arg->ofmt_cbarg; 998 pktsum_t *diff_stats = fargs->flow_s_psum; 999 1000 switch (of_arg->ofmt_id) { 1001 case FLOW_S_FLOW: 1002 (void) snprintf(buf, bufsize, "%s", fargs->flow_s_flow); 1003 break; 1004 case FLOW_S_IPKTS: 1005 (void) snprintf(buf, bufsize, "%llu", 1006 diff_stats->ipackets); 1007 break; 1008 case FLOW_S_RBYTES: 1009 (void) snprintf(buf, bufsize, "%llu", 1010 diff_stats->rbytes); 1011 break; 1012 case FLOW_S_IERRORS: 1013 (void) snprintf(buf, bufsize, "%u", 1014 diff_stats->ierrors); 1015 break; 1016 case FLOW_S_OPKTS: 1017 (void) snprintf(buf, bufsize, "%llu", 1018 diff_stats->opackets); 1019 break; 1020 case FLOW_S_OBYTES: 1021 (void) snprintf(buf, bufsize, "%llu", 1022 diff_stats->obytes); 1023 break; 1024 case FLOW_S_OERRORS: 1025 (void) snprintf(buf, bufsize, "%u", 1026 diff_stats->oerrors); 1027 break; 1028 default: 1029 die("invalid input"); 1030 break; 1031 } 1032 return (B_TRUE); 1033 } 1034 1035 /* ARGSUSED */ 1036 static int 1037 show_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) 1038 { 1039 show_flow_state_t *state = (show_flow_state_t *)arg; 1040 char *name = attr->fa_flowname; 1041 pktsum_t stats, diff_stats; 1042 flow_args_t fargs; 1043 1044 if (state->fs_firstonly) { 1045 if (state->fs_donefirst) 1046 return (DLADM_WALK_TERMINATE); 1047 state->fs_donefirst = B_TRUE; 1048 } else { 1049 bzero(&state->fs_prevstats, sizeof (state->fs_prevstats)); 1050 } 1051 1052 get_flow_stats(name, &stats); 1053 dladm_stats_diff(&diff_stats, &stats, &state->fs_prevstats); 1054 1055 fargs.flow_s_flow = name; 1056 fargs.flow_s_psum = &diff_stats; 1057 ofmt_print(state->fs_ofmt, (void *)&fargs); 1058 state->fs_prevstats = stats; 1059 1060 return (DLADM_WALK_CONTINUE); 1061 } 1062 1063 /* 1064 * Wrapper of dladm_walk_flow(show_flow,...) to make it usable for 1065 * dladm_walk_datalink_id(). Used for showing flow stats for 1066 * all flows on all links. 1067 */ 1068 static int 1069 show_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg) 1070 { 1071 if (dladm_walk_flow(show_flow_stats, dh, linkid, arg, B_FALSE) 1072 == DLADM_STATUS_OK) 1073 return (DLADM_WALK_CONTINUE); 1074 else 1075 return (DLADM_WALK_TERMINATE); 1076 } 1077 1078 /* ARGSUSED */ 1079 static void 1080 flow_stats(const char *flow, datalink_id_t linkid, uint_t interval, 1081 char *fields_str, show_flow_state_t *state) 1082 { 1083 dladm_flow_attr_t attr; 1084 ofmt_handle_t ofmt; 1085 ofmt_status_t oferr; 1086 uint_t ofmtflags = 0; 1087 1088 oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt); 1089 flowadm_ofmt_check(oferr, state->fs_parsable, ofmt); 1090 state->fs_ofmt = ofmt; 1091 1092 if (flow != NULL && 1093 dladm_flow_info(handle, flow, &attr) != DLADM_STATUS_OK) 1094 die("invalid flow %s", flow); 1095 1096 /* 1097 * If an interval is specified, continuously show the stats 1098 * for only the first flow. 1099 */ 1100 state->fs_firstonly = (interval != 0); 1101 1102 for (;;) { 1103 state->fs_donefirst = B_FALSE; 1104 1105 /* Show stats for named flow */ 1106 if (flow != NULL) { 1107 state->fs_flow = flow; 1108 (void) show_flow_stats(handle, &attr, state); 1109 1110 /* Show all stats on a link */ 1111 } else if (linkid != DATALINK_INVALID_LINKID) { 1112 (void) dladm_walk_flow(show_flow_stats, handle, linkid, 1113 state, B_FALSE); 1114 1115 /* Show all stats by datalink */ 1116 } else { 1117 (void) dladm_walk_datalink_id(show_link_flow_stats, 1118 handle, state, DATALINK_CLASS_ALL, 1119 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); 1120 } 1121 1122 if (interval == 0) 1123 break; 1124 1125 (void) fflush(stdout); 1126 (void) sleep(interval); 1127 } 1128 ofmt_close(ofmt); 1129 } 1130 1131 static void 1132 do_show_flow(int argc, char *argv[]) 1133 { 1134 char flowname[MAXFLOWNAMELEN]; 1135 char linkname[MAXLINKNAMELEN]; 1136 datalink_id_t linkid = DATALINK_ALL_LINKID; 1137 int option; 1138 boolean_t s_arg = B_FALSE; 1139 boolean_t S_arg = B_FALSE; 1140 boolean_t i_arg = B_FALSE; 1141 boolean_t l_arg = B_FALSE; 1142 boolean_t o_arg = B_FALSE; 1143 uint32_t interval = 0; 1144 show_flow_state_t state; 1145 char *fields_str = NULL; 1146 ofmt_handle_t ofmt; 1147 ofmt_status_t oferr; 1148 uint_t ofmtflags = 0; 1149 1150 bzero(&state, sizeof (state)); 1151 1152 opterr = 0; 1153 while ((option = getopt_long(argc, argv, ":pPsSi:l:o:", 1154 longopts, NULL)) != -1) { 1155 switch (option) { 1156 case 'p': 1157 state.fs_parsable = B_TRUE; 1158 ofmtflags |= OFMT_PARSABLE; 1159 break; 1160 case 'P': 1161 state.fs_persist = B_TRUE; 1162 break; 1163 case 's': 1164 if (s_arg) 1165 die_optdup(option); 1166 1167 s_arg = B_TRUE; 1168 break; 1169 case 'S': 1170 if (S_arg) 1171 die_optdup(option); 1172 1173 S_arg = B_TRUE; 1174 break; 1175 case 'o': 1176 if (o_arg) 1177 die_optdup(option); 1178 1179 o_arg = B_TRUE; 1180 fields_str = optarg; 1181 break; 1182 case 'i': 1183 if (i_arg) 1184 die_optdup(option); 1185 1186 i_arg = B_TRUE; 1187 1188 if (!dladm_str2interval(optarg, &interval)) 1189 die("invalid interval value '%s'", optarg); 1190 break; 1191 case 'l': 1192 if (strlcpy(linkname, optarg, MAXLINKNAMELEN) 1193 >= MAXLINKNAMELEN) 1194 die("link name too long\n"); 1195 if (dladm_name2info(handle, linkname, &linkid, NULL, 1196 NULL, NULL) != DLADM_STATUS_OK) 1197 die("invalid link '%s'", linkname); 1198 l_arg = B_TRUE; 1199 break; 1200 default: 1201 die_opterr(optopt, option); 1202 break; 1203 } 1204 } 1205 if (i_arg && !(s_arg || S_arg)) 1206 die("the -i option can be used only with -s or -S"); 1207 1208 if (s_arg && S_arg) 1209 die("the -s option cannot be used with -S"); 1210 1211 /* get flow name (optional last argument */ 1212 if (optind == (argc-1)) { 1213 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) 1214 >= MAXFLOWNAMELEN) 1215 die("flow name too long"); 1216 state.fs_flow = flowname; 1217 } 1218 1219 if (S_arg) { 1220 dladm_continuous(handle, linkid, state.fs_flow, interval, 1221 FLOW_REPORT); 1222 return; 1223 } 1224 1225 if (s_arg) { 1226 flow_stats(state.fs_flow, linkid, interval, fields_str, &state); 1227 return; 1228 } 1229 1230 oferr = ofmt_open(fields_str, flow_fields, ofmtflags, 0, &ofmt); 1231 flowadm_ofmt_check(oferr, state.fs_parsable, ofmt); 1232 state.fs_ofmt = ofmt; 1233 1234 /* Show attributes of one flow */ 1235 if (state.fs_flow != NULL) { 1236 show_one_flow(&state, state.fs_flow); 1237 1238 /* Show attributes of flows on one link */ 1239 } else if (l_arg) { 1240 (void) show_flows_onelink(handle, linkid, &state); 1241 1242 /* Show attributes of all flows on all links */ 1243 } else { 1244 (void) dladm_walk_datalink_id(show_flows_onelink, handle, 1245 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1246 DLADM_OPT_ACTIVE); 1247 } 1248 ofmt_close(ofmt); 1249 } 1250 1251 static dladm_status_t 1252 set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val, 1253 uint_t val_cnt, boolean_t reset) 1254 { 1255 dladm_status_t status; 1256 char *errprop; 1257 1258 status = dladm_set_flowprop(handle, flow, prop_name, prop_val, val_cnt, 1259 DLADM_OPT_PERSIST, &errprop); 1260 1261 if (status != DLADM_STATUS_OK) { 1262 warn_dlerr(status, "cannot persistently %s flow " 1263 "property '%s' on '%s'", reset? "reset": "set", 1264 errprop, flow); 1265 } 1266 return (status); 1267 } 1268 1269 static void 1270 set_flowprop(int argc, char **argv, boolean_t reset) 1271 { 1272 int i, option; 1273 char errmsg[DLADM_STRSIZE]; 1274 const char *flow = NULL; 1275 char propstr[DLADM_STRSIZE]; 1276 dladm_arg_list_t *proplist = NULL; 1277 boolean_t temp = B_FALSE; 1278 dladm_status_t status = DLADM_STATUS_OK; 1279 1280 opterr = 0; 1281 bzero(propstr, DLADM_STRSIZE); 1282 1283 while ((option = getopt_long(argc, argv, ":p:R:t", 1284 prop_longopts, NULL)) != -1) { 1285 switch (option) { 1286 case 'p': 1287 (void) strlcat(propstr, optarg, DLADM_STRSIZE); 1288 if (strlcat(propstr, ",", DLADM_STRSIZE) >= 1289 DLADM_STRSIZE) 1290 die("property list too long '%s'", propstr); 1291 break; 1292 case 't': 1293 temp = B_TRUE; 1294 break; 1295 case 'R': 1296 status = dladm_set_rootdir(optarg); 1297 if (status != DLADM_STATUS_OK) { 1298 die_dlerr(status, "invalid directory " 1299 "specified"); 1300 } 1301 break; 1302 default: 1303 die_opterr(optopt, option); 1304 break; 1305 } 1306 } 1307 1308 if (optind == (argc - 1)) { 1309 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 1310 die("flow name too long"); 1311 flow = argv[optind]; 1312 } else if (optind != argc) { 1313 usage(); 1314 } 1315 if (flow == NULL) 1316 die("flow name must be specified"); 1317 1318 if (dladm_parse_flow_props(propstr, &proplist, reset) 1319 != DLADM_STATUS_OK) 1320 die("invalid flow property specified"); 1321 1322 if (proplist == NULL) { 1323 char *errprop; 1324 1325 if (!reset) 1326 die("flow property must be specified"); 1327 1328 status = dladm_set_flowprop(handle, flow, NULL, NULL, 0, 1329 DLADM_OPT_ACTIVE, &errprop); 1330 if (status != DLADM_STATUS_OK) { 1331 warn_dlerr(status, "cannot reset flow property '%s' " 1332 "on '%s'", errprop, flow); 1333 } 1334 if (!temp) { 1335 dladm_status_t s; 1336 1337 s = set_flowprop_persist(flow, NULL, NULL, 0, reset); 1338 if (s != DLADM_STATUS_OK) 1339 status = s; 1340 } 1341 goto done; 1342 } 1343 1344 for (i = 0; i < proplist->al_count; i++) { 1345 dladm_arg_info_t *aip = &proplist->al_info[i]; 1346 char **val; 1347 uint_t count; 1348 dladm_status_t s; 1349 1350 if (reset) { 1351 val = NULL; 1352 count = 0; 1353 } else { 1354 val = aip->ai_val; 1355 count = aip->ai_count; 1356 if (count == 0) { 1357 warn("no value specified for '%s'", 1358 aip->ai_name); 1359 status = DLADM_STATUS_BADARG; 1360 continue; 1361 } 1362 } 1363 s = dladm_set_flowprop(handle, flow, aip->ai_name, val, count, 1364 DLADM_OPT_ACTIVE, NULL); 1365 if (s == DLADM_STATUS_OK) { 1366 if (!temp) { 1367 s = set_flowprop_persist(flow, 1368 aip->ai_name, val, count, reset); 1369 if (s != DLADM_STATUS_OK) 1370 status = s; 1371 } 1372 continue; 1373 } 1374 status = s; 1375 switch (s) { 1376 case DLADM_STATUS_NOTFOUND: 1377 warn("invalid flow property '%s'", aip->ai_name); 1378 break; 1379 case DLADM_STATUS_BADVAL: { 1380 int j; 1381 char *ptr, *lim; 1382 char **propvals = NULL; 1383 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 1384 1385 ptr = malloc((sizeof (char *) + 1386 DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT + 1387 MAX_PROP_LINE); 1388 1389 if (ptr == NULL) 1390 die("insufficient memory"); 1391 propvals = (char **)(void *)ptr; 1392 1393 for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) { 1394 propvals[j] = ptr + sizeof (char *) * 1395 DLADM_MAX_PROP_VALCNT + 1396 j * DLADM_PROP_VAL_MAX; 1397 } 1398 s = dladm_get_flowprop(handle, flow, 1399 DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals, 1400 &valcnt); 1401 1402 ptr = errmsg; 1403 lim = ptr + DLADM_STRSIZE; 1404 *ptr = '\0'; 1405 for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) { 1406 ptr += snprintf(ptr, lim - ptr, "%s,", 1407 propvals[j]); 1408 if (ptr >= lim) 1409 break; 1410 } 1411 if (ptr > errmsg) { 1412 *(ptr - 1) = '\0'; 1413 warn("flow property '%s' must be one of: %s", 1414 aip->ai_name, errmsg); 1415 } else 1416 warn("%s is an invalid value for " 1417 "flow property %s", *val, aip->ai_name); 1418 free(propvals); 1419 break; 1420 } 1421 default: 1422 if (reset) { 1423 warn_dlerr(status, "cannot reset flow property " 1424 "'%s' on '%s'", aip->ai_name, flow); 1425 } else { 1426 warn_dlerr(status, "cannot set flow property " 1427 "'%s' on '%s'", aip->ai_name, flow); 1428 } 1429 break; 1430 } 1431 } 1432 done: 1433 dladm_free_props(proplist); 1434 if (status != DLADM_STATUS_OK) { 1435 dladm_close(handle); 1436 exit(EXIT_FAILURE); 1437 } 1438 } 1439 1440 static void 1441 do_set_flowprop(int argc, char **argv) 1442 { 1443 set_flowprop(argc, argv, B_FALSE); 1444 } 1445 1446 static void 1447 do_reset_flowprop(int argc, char **argv) 1448 { 1449 set_flowprop(argc, argv, B_TRUE); 1450 } 1451 1452 static void 1453 warn(const char *format, ...) 1454 { 1455 va_list alist; 1456 1457 format = gettext(format); 1458 (void) fprintf(stderr, "%s: warning: ", progname); 1459 1460 va_start(alist, format); 1461 (void) vfprintf(stderr, format, alist); 1462 va_end(alist); 1463 1464 (void) putchar('\n'); 1465 } 1466 1467 /* PRINTFLIKE2 */ 1468 static void 1469 warn_dlerr(dladm_status_t err, const char *format, ...) 1470 { 1471 va_list alist; 1472 char errmsg[DLADM_STRSIZE]; 1473 1474 format = gettext(format); 1475 (void) fprintf(stderr, gettext("%s: warning: "), progname); 1476 1477 va_start(alist, format); 1478 (void) vfprintf(stderr, format, alist); 1479 va_end(alist); 1480 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1481 } 1482 1483 /* PRINTFLIKE1 */ 1484 static void 1485 die(const char *format, ...) 1486 { 1487 va_list alist; 1488 1489 format = gettext(format); 1490 (void) fprintf(stderr, "%s: ", progname); 1491 1492 va_start(alist, format); 1493 (void) vfprintf(stderr, format, alist); 1494 va_end(alist); 1495 1496 (void) putchar('\n'); 1497 1498 /* close dladm handle if it was opened */ 1499 if (handle != NULL) 1500 dladm_close(handle); 1501 1502 exit(EXIT_FAILURE); 1503 } 1504 1505 static void 1506 die_optdup(int opt) 1507 { 1508 die("the option -%c cannot be specified more than once", opt); 1509 } 1510 1511 static void 1512 die_opterr(int opt, int opterr) 1513 { 1514 switch (opterr) { 1515 case ':': 1516 die("option '-%c' requires a value", opt); 1517 break; 1518 case '?': 1519 default: 1520 die("unrecognized option '-%c'", opt); 1521 break; 1522 } 1523 } 1524 1525 /* PRINTFLIKE2 */ 1526 static void 1527 die_dlerr(dladm_status_t err, const char *format, ...) 1528 { 1529 va_list alist; 1530 char errmsg[DLADM_STRSIZE]; 1531 1532 format = gettext(format); 1533 (void) fprintf(stderr, "%s: ", progname); 1534 1535 va_start(alist, format); 1536 (void) vfprintf(stderr, format, alist); 1537 va_end(alist); 1538 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); 1539 1540 /* close dladm handle if it was opened */ 1541 if (handle != NULL) 1542 dladm_close(handle); 1543 1544 exit(EXIT_FAILURE); 1545 } 1546 1547 static void 1548 print_flowprop(const char *flowname, show_flowprop_state_t *statep, 1549 const char *propname, dladm_prop_type_t type, 1550 const char *format, char **pptr) 1551 { 1552 int i; 1553 char *ptr, *lim; 1554 char buf[DLADM_STRSIZE]; 1555 char *unknown = "--", *notsup = ""; 1556 char **propvals = statep->fs_propvals; 1557 uint_t valcnt = DLADM_MAX_PROP_VALCNT; 1558 dladm_status_t status; 1559 1560 status = dladm_get_flowprop(handle, flowname, type, propname, propvals, 1561 &valcnt); 1562 if (status != DLADM_STATUS_OK) { 1563 if (status == DLADM_STATUS_TEMPONLY) { 1564 if (type == DLADM_PROP_VAL_MODIFIABLE && 1565 statep->fs_persist) { 1566 valcnt = 1; 1567 propvals = &unknown; 1568 } else { 1569 statep->fs_status = status; 1570 statep->fs_retstatus = status; 1571 return; 1572 } 1573 } else if (status == DLADM_STATUS_NOTSUP || 1574 statep->fs_persist) { 1575 valcnt = 1; 1576 if (type == DLADM_PROP_VAL_CURRENT) 1577 propvals = &unknown; 1578 else 1579 propvals = ¬sup; 1580 } else { 1581 if ((statep->fs_proplist != NULL) && 1582 statep->fs_status == DLADM_STATUS_OK) { 1583 warn("invalid flow property '%s'", propname); 1584 } 1585 statep->fs_status = status; 1586 statep->fs_retstatus = status; 1587 return; 1588 } 1589 } 1590 1591 statep->fs_status = DLADM_STATUS_OK; 1592 1593 ptr = buf; 1594 lim = buf + DLADM_STRSIZE; 1595 for (i = 0; i < valcnt; i++) { 1596 if (propvals[i][0] == '\0' && !statep->fs_parsable) 1597 ptr += snprintf(ptr, lim - ptr, "--,"); 1598 else 1599 ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]); 1600 if (ptr >= lim) 1601 break; 1602 } 1603 if (valcnt > 0) 1604 buf[strlen(buf) - 1] = '\0'; 1605 1606 lim = statep->fs_line + MAX_PROP_LINE; 1607 if (statep->fs_parsable) { 1608 *pptr += snprintf(*pptr, lim - *pptr, 1609 "%s", buf); 1610 } else { 1611 *pptr += snprintf(*pptr, lim - *pptr, format, buf); 1612 } 1613 } 1614 1615 static boolean_t 1616 print_flowprop_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) 1617 { 1618 flowprop_args_t *arg = of_arg->ofmt_cbarg; 1619 char *propname = arg->fs_propname; 1620 show_flowprop_state_t *statep = arg->fs_state; 1621 char *ptr = statep->fs_line; 1622 char *lim = ptr + MAX_PROP_LINE; 1623 char *flowname = arg->fs_flowname; 1624 1625 switch (of_arg->ofmt_id) { 1626 case FLOWPROP_FLOW: 1627 (void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow); 1628 break; 1629 case FLOWPROP_PROPERTY: 1630 (void) snprintf(ptr, lim - ptr, "%s", propname); 1631 break; 1632 case FLOWPROP_VALUE: 1633 print_flowprop(flowname, statep, propname, 1634 statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT : 1635 DLADM_PROP_VAL_CURRENT, "%s", &ptr); 1636 /* 1637 * If we failed to query the flow property, for example, query 1638 * the persistent value of a non-persistable flow property, 1639 * simply skip the output. 1640 */ 1641 if (statep->fs_status != DLADM_STATUS_OK) 1642 goto skip; 1643 ptr = statep->fs_line; 1644 break; 1645 case FLOWPROP_DEFAULT: 1646 print_flowprop(flowname, statep, propname, 1647 DLADM_PROP_VAL_DEFAULT, "%s", &ptr); 1648 if (statep->fs_status != DLADM_STATUS_OK) 1649 goto skip; 1650 ptr = statep->fs_line; 1651 break; 1652 case FLOWPROP_POSSIBLE: 1653 print_flowprop(flowname, statep, propname, 1654 DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr); 1655 if (statep->fs_status != DLADM_STATUS_OK) 1656 goto skip; 1657 ptr = statep->fs_line; 1658 break; 1659 default: 1660 die("invalid input"); 1661 break; 1662 } 1663 (void) strlcpy(buf, ptr, bufsize); 1664 return (B_TRUE); 1665 skip: 1666 buf[0] = '\0'; 1667 return ((statep->fs_status == DLADM_STATUS_OK) ? 1668 B_TRUE : B_FALSE); 1669 } 1670 1671 static int 1672 show_one_flowprop(void *arg, const char *propname) 1673 { 1674 show_flowprop_state_t *statep = arg; 1675 flowprop_args_t fs_arg; 1676 1677 bzero(&fs_arg, sizeof (fs_arg)); 1678 fs_arg.fs_state = statep; 1679 fs_arg.fs_propname = (char *)propname; 1680 fs_arg.fs_flowname = (char *)statep->fs_flow; 1681 1682 ofmt_print(statep->fs_ofmt, (void *)&fs_arg); 1683 1684 return (DLADM_WALK_CONTINUE); 1685 } 1686 1687 /*ARGSUSED*/ 1688 /* Walker function called by dladm_walk_flow to display flow properties */ 1689 static int 1690 show_flowprop(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) 1691 { 1692 show_flowprop_one_flow(arg, attr->fa_flowname); 1693 return (DLADM_WALK_CONTINUE); 1694 } 1695 1696 /* 1697 * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it 1698 * usable to dladm_walk_datalink_id() 1699 */ 1700 static int 1701 show_flowprop_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg) 1702 { 1703 char name[MAXLINKNAMELEN]; 1704 1705 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, name, 1706 sizeof (name)) != DLADM_STATUS_OK) 1707 return (DLADM_WALK_TERMINATE); 1708 1709 (void) dladm_walk_flow(show_flowprop, dh, linkid, arg, B_FALSE); 1710 1711 return (DLADM_WALK_CONTINUE); 1712 } 1713 1714 static void 1715 do_show_flowprop(int argc, char **argv) 1716 { 1717 int option; 1718 dladm_arg_list_t *proplist = NULL; 1719 show_flowprop_state_t state; 1720 char *fields_str = NULL; 1721 ofmt_handle_t ofmt; 1722 ofmt_status_t oferr; 1723 uint_t ofmtflags = 0; 1724 1725 opterr = 0; 1726 state.fs_propvals = NULL; 1727 state.fs_line = NULL; 1728 state.fs_parsable = B_FALSE; 1729 state.fs_persist = B_FALSE; 1730 state.fs_header = B_TRUE; 1731 state.fs_retstatus = DLADM_STATUS_OK; 1732 state.fs_linkid = DATALINK_INVALID_LINKID; 1733 state.fs_flow = NULL; 1734 1735 while ((option = getopt_long(argc, argv, ":p:cPl:o:", 1736 prop_longopts, NULL)) != -1) { 1737 switch (option) { 1738 case 'p': 1739 if (dladm_parse_flow_props(optarg, &proplist, B_TRUE) 1740 != DLADM_STATUS_OK) 1741 die("invalid flow properties specified"); 1742 break; 1743 case 'c': 1744 state.fs_parsable = B_TRUE; 1745 ofmtflags |= OFMT_PARSABLE; 1746 break; 1747 case 'P': 1748 state.fs_persist = B_TRUE; 1749 break; 1750 case 'l': 1751 if (dladm_name2info(handle, optarg, &state.fs_linkid, 1752 NULL, NULL, NULL) != DLADM_STATUS_OK) 1753 die("invalid link '%s'", optarg); 1754 break; 1755 case 'o': 1756 fields_str = optarg; 1757 break; 1758 default: 1759 die_opterr(optopt, option); 1760 break; 1761 } 1762 } 1763 1764 if (optind == (argc - 1)) { 1765 if (strlen(argv[optind]) >= MAXFLOWNAMELEN) 1766 die("flow name too long"); 1767 state.fs_flow = argv[optind]; 1768 } else if (optind != argc) { 1769 usage(); 1770 } 1771 state.fs_proplist = proplist; 1772 state.fs_status = DLADM_STATUS_OK; 1773 1774 oferr = ofmt_open(fields_str, flowprop_fields, ofmtflags, 0, &ofmt); 1775 flowadm_ofmt_check(oferr, state.fs_parsable, ofmt); 1776 state.fs_ofmt = ofmt; 1777 1778 /* Show properties for one flow */ 1779 if (state.fs_flow != NULL) { 1780 show_flowprop_one_flow(&state, state.fs_flow); 1781 1782 /* Show properties for all flows on one link */ 1783 } else if (state.fs_linkid != DATALINK_INVALID_LINKID) { 1784 (void) show_flowprop_onelink(handle, state.fs_linkid, &state); 1785 1786 /* Show properties for all flows on all links */ 1787 } else { 1788 (void) dladm_walk_datalink_id(show_flowprop_onelink, handle, 1789 &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, 1790 DLADM_OPT_ACTIVE); 1791 } 1792 1793 dladm_free_props(proplist); 1794 ofmt_close(ofmt); 1795 } 1796 1797 static void 1798 show_flowprop_one_flow(void *arg, const char *flow) 1799 { 1800 int i; 1801 char *buf; 1802 dladm_status_t status; 1803 dladm_arg_list_t *proplist = NULL; 1804 show_flowprop_state_t *statep = arg; 1805 dladm_flow_attr_t attr; 1806 const char *savep; 1807 1808 /* 1809 * Do not print flow props for invalid flows. 1810 */ 1811 if ((status = dladm_flow_info(handle, flow, &attr)) != 1812 DLADM_STATUS_OK) { 1813 die("invalid flow: '%s'", flow); 1814 } 1815 1816 savep = statep->fs_flow; 1817 statep->fs_flow = flow; 1818 1819 proplist = statep->fs_proplist; 1820 1821 buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) 1822 * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE); 1823 if (buf == NULL) 1824 die("insufficient memory"); 1825 1826 statep->fs_propvals = (char **)(void *)buf; 1827 for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) { 1828 statep->fs_propvals[i] = buf + 1829 sizeof (char *) * DLADM_MAX_PROP_VALCNT + 1830 i * DLADM_PROP_VAL_MAX; 1831 } 1832 statep->fs_line = buf + 1833 (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT; 1834 1835 /* show only specified flow properties */ 1836 if (proplist != NULL) { 1837 for (i = 0; i < proplist->al_count; i++) { 1838 if (show_one_flowprop(statep, 1839 proplist->al_info[i].ai_name) != DLADM_STATUS_OK) 1840 break; 1841 } 1842 1843 /* show all flow properties */ 1844 } else { 1845 status = dladm_walk_flowprop(show_one_flowprop, flow, statep); 1846 if (status != DLADM_STATUS_OK) 1847 die_dlerr(status, "show-flowprop"); 1848 } 1849 free(buf); 1850 statep->fs_flow = savep; 1851 } 1852 1853 /* 1854 * default output callback function that, when invoked from dladm_print_output, 1855 * prints string which is offset by of_arg->ofmt_id within buf. 1856 */ 1857 static boolean_t 1858 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) 1859 { 1860 char *value; 1861 1862 value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id; 1863 (void) strlcpy(buf, value, bufsize); 1864 return (B_TRUE); 1865 } 1866 1867 static void 1868 flowadm_ofmt_check(ofmt_status_t oferr, boolean_t parsable, 1869 ofmt_handle_t ofmt) 1870 { 1871 char buf[OFMT_BUFSIZE]; 1872 1873 if (oferr == OFMT_SUCCESS) 1874 return; 1875 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf)); 1876 /* 1877 * All errors are considered fatal in parsable mode. 1878 * NOMEM errors are always fatal, regardless of mode. 1879 * For other errors, we print diagnostics in human-readable 1880 * mode and processs what we can. 1881 */ 1882 if (parsable || oferr == OFMT_ENOFIELDS) { 1883 ofmt_close(ofmt); 1884 die(buf); 1885 } else { 1886 warn(buf); 1887 } 1888 } 1889