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