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 2010 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 <strings.h>
  35 #include <getopt.h>
  36 #include <unistd.h>
  37 #include <priv.h>
  38 #include <netdb.h>
  39 #include <libintl.h>
  40 #include <libdlflow.h>
  41 #include <libdllink.h>
  42 #include <libdlstat.h>
  43 #include <sys/types.h>
  44 #include <sys/socket.h>
  45 #include <netinet/in.h>
  46 #include <arpa/inet.h>
  47 #include <sys/ethernet.h>
  48 #include <inet/ip.h>
  49 #include <inet/ip6.h>
  50 #include <stddef.h>
  51 #include <ofmt.h>
  52 
  53 typedef struct flow_chain_s {
  54         char                    fc_flowname[MAXFLOWNAMELEN];
  55         boolean_t               fc_visited;
  56         flow_stat_t             *fc_stat;
  57         struct flow_chain_s     *fc_next;
  58 } flow_chain_t;
  59 
  60 typedef struct show_flow_state {
  61         flow_chain_t    *fs_flowchain;
  62         ofmt_handle_t   fs_ofmt;
  63         char            fs_unit;
  64         boolean_t       fs_parsable;
  65 } show_flow_state_t;
  66 
  67 typedef struct show_history_state_s {
  68         boolean_t       us_plot;
  69         boolean_t       us_parsable;
  70         boolean_t       us_printheader;
  71         boolean_t       us_first;
  72         boolean_t       us_showall;
  73         ofmt_handle_t   us_ofmt;
  74 } show_history_state_t;
  75 
  76 static void     do_show_history(int, char **);
  77 
  78 static int      query_flow_stats(dladm_handle_t, dladm_flow_attr_t *, void *);
  79 static int      query_link_flow_stats(dladm_handle_t, datalink_id_t, void *);
  80 
  81 static void     die(const char *, ...);
  82 static void     die_optdup(int);
  83 static void     die_opterr(int, int, const char *);
  84 static void     die_dlerr(dladm_status_t, const char *, ...);
  85 static void     warn(const char *, ...);
  86 
  87 /* callback functions for printing output */
  88 static ofmt_cb_t print_default_cb, print_flow_stats_cb;
  89 static void flowstat_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
  90 
  91 #define NULL_OFMT               {NULL, 0, 0, NULL}
  92 
  93 /*
  94  * structures for flowstat (printing live statistics)
  95  */
  96 typedef enum {
  97         FLOW_S_FLOW,
  98         FLOW_S_IPKTS,
  99         FLOW_S_RBYTES,
 100         FLOW_S_IERRORS,
 101         FLOW_S_OPKTS,
 102         FLOW_S_OBYTES,
 103         FLOW_S_OERRORS
 104 } flow_s_field_index_t;
 105 
 106 static ofmt_field_t flow_s_fields[] = {
 107 /* name,        field width,    index,          callback */
 108 { "FLOW",       15,     FLOW_S_FLOW,    print_flow_stats_cb},
 109 { "IPKTS",      8,      FLOW_S_IPKTS,   print_flow_stats_cb},
 110 { "RBYTES",     8,      FLOW_S_RBYTES,  print_flow_stats_cb},
 111 { "IERRS",      8,      FLOW_S_IERRORS, print_flow_stats_cb},
 112 { "OPKTS",      8,      FLOW_S_OPKTS,   print_flow_stats_cb},
 113 { "OBYTES",     8,      FLOW_S_OBYTES,  print_flow_stats_cb},
 114 { "OERRS",      8,      FLOW_S_OERRORS, print_flow_stats_cb},
 115 NULL_OFMT}
 116 ;
 117 
 118 typedef struct flow_args_s {
 119         char            *flow_s_flow;
 120         flow_stat_t     *flow_s_stat;
 121         char            flow_s_unit;
 122         boolean_t       flow_s_parsable;
 123 } flow_args_t;
 124 
 125 /*
 126  * structures for 'flowstat -h'
 127  */
 128 typedef struct  history_fields_buf_s {
 129         char    history_flow[12];
 130         char    history_duration[10];
 131         char    history_ipackets[9];
 132         char    history_rbytes[10];
 133         char    history_opackets[9];
 134         char    history_obytes[10];
 135         char    history_bandwidth[14];
 136 } history_fields_buf_t;
 137 
 138 static ofmt_field_t history_fields[] = {
 139 /* name,        field width,    offset */
 140 { "FLOW",       13,
 141         offsetof(history_fields_buf_t, history_flow), print_default_cb},
 142 { "DURATION",   11,
 143         offsetof(history_fields_buf_t, history_duration), print_default_cb},
 144 { "IPACKETS",   10,
 145         offsetof(history_fields_buf_t, history_ipackets), print_default_cb},
 146 { "RBYTES",     11,
 147         offsetof(history_fields_buf_t, history_rbytes), print_default_cb},
 148 { "OPACKETS",   10,
 149         offsetof(history_fields_buf_t, history_opackets), print_default_cb},
 150 { "OBYTES",     11,
 151         offsetof(history_fields_buf_t, history_obytes), print_default_cb},
 152 { "BANDWIDTH",  15,
 153         offsetof(history_fields_buf_t, history_bandwidth), print_default_cb},
 154 NULL_OFMT}
 155 ;
 156 
 157 typedef struct  history_l_fields_buf_s {
 158         char    history_l_flow[12];
 159         char    history_l_stime[13];
 160         char    history_l_etime[13];
 161         char    history_l_rbytes[8];
 162         char    history_l_obytes[8];
 163         char    history_l_bandwidth[14];
 164 } history_l_fields_buf_t;
 165 
 166 static ofmt_field_t history_l_fields[] = {
 167 /* name,        field width,    offset */
 168 { "FLOW",       13,
 169         offsetof(history_l_fields_buf_t, history_l_flow), print_default_cb},
 170 { "START",      14,
 171         offsetof(history_l_fields_buf_t, history_l_stime), print_default_cb},
 172 { "END",        14,
 173         offsetof(history_l_fields_buf_t, history_l_etime), print_default_cb},
 174 { "RBYTES",     9,
 175         offsetof(history_l_fields_buf_t, history_l_rbytes), print_default_cb},
 176 { "OBYTES",     9,
 177         offsetof(history_l_fields_buf_t, history_l_obytes), print_default_cb},
 178 { "BANDWIDTH",  15,
 179         offsetof(history_l_fields_buf_t, history_l_bandwidth),
 180             print_default_cb},
 181 NULL_OFMT}
 182 ;
 183 
 184 static char *progname;
 185 
 186 /*
 187  * Handle to libdladm.  Opened in main() before the sub-command
 188  * specific function is called.
 189  */
 190 static dladm_handle_t handle = NULL;
 191 
 192 const char *usage_ermsg = "flowstat [-r | -t] [-i interval] "
 193             "[-l link] [flow]\n"
 194             "       flowstat [-S] [-A] [-i interval] [-p] [ -o field[,...]]\n"
 195             "                [-u R|K|M|G|T|P] [-l link] [flow]\n"
 196             "       flowstat -h [-a] [-d] [-F format]"
 197             " [-s <DD/MM/YYYY,HH:MM:SS>]\n"
 198             "                [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> "
 199             "[<flow>]";
 200 
 201 static void
 202 usage(void)
 203 {
 204         (void) fprintf(stderr, "%s\n", gettext(usage_ermsg));
 205 
 206         /* close dladm handle if it was opened */
 207         if (handle != NULL)
 208                 dladm_close(handle);
 209 
 210         exit(1);
 211 }
 212 
 213 boolean_t
 214 flowstat_unit(char *oarg, char *unit)
 215 {
 216         if ((strcmp(oarg, "R") == 0) || (strcmp(oarg, "K") == 0) ||
 217             (strcmp(oarg, "M") == 0) || (strcmp(oarg, "G") == 0) ||
 218             (strcmp(oarg, "T") == 0) || (strcmp(oarg, "P") == 0)) {
 219                 *unit = oarg[0];
 220                 return (B_TRUE);
 221         }
 222 
 223         return (B_FALSE);
 224 }
 225 
 226 void
 227 map_to_units(char *buf, uint_t bufsize, double num, char unit,
 228     boolean_t parsable)
 229 {
 230         if (parsable) {
 231                 (void) snprintf(buf, bufsize, "%.0lf", num);
 232                 return;
 233         }
 234 
 235         if (unit == '\0') {
 236                 int index;
 237 
 238                 for (index = 0; (int)(num/1000) != 0; index++, num /= 1000)
 239                         ;
 240 
 241                 switch (index) {
 242                         case 0:
 243                                 unit = '\0';
 244                                 break;
 245                         case 1:
 246                                 unit = 'K';
 247                                 break;
 248                         case 2:
 249                                 unit = 'M';
 250                                 break;
 251                         case 3:
 252                                 unit = 'G';
 253                                 break;
 254                         case 4:
 255                                 unit = 'T';
 256                                 break;
 257                         case 5:
 258                                 /* Largest unit supported */
 259                         default:
 260                                 unit = 'P';
 261                                 break;
 262                 }
 263         } else  {
 264                 switch (unit) {
 265                         case 'R':
 266                                 /* Already raw numbers */
 267                                 unit = '\0';
 268                                 break;
 269                         case 'K':
 270                                 num /= 1000;
 271                                 break;
 272                         case 'M':
 273                                 num /= (1000*1000);
 274                                 break;
 275                         case 'G':
 276                                 num /= (1000*1000*1000);
 277                                 break;
 278                         case 'T':
 279                                 num /= (1000.0*1000.0*1000.0*1000.0);
 280                                 break;
 281                         case 'P':
 282                                 /* Largest unit supported */
 283                         default:
 284                                 num /= (1000.0*1000.0*1000.0*1000.0*1000.0);
 285                                 break;
 286                 }
 287         }
 288 
 289         if (unit == '\0')
 290                 (void) snprintf(buf, bufsize, " %7.0lf%c", num, unit);
 291         else
 292                 (void) snprintf(buf, bufsize, " %6.2lf%c", num, unit);
 293 }
 294 
 295 flow_chain_t *
 296 get_flow_prev_stat(const char *flowname, void *arg)
 297 {
 298         show_flow_state_t       *state = arg;
 299         flow_chain_t            *flow_curr = NULL;
 300 
 301         /* Scan prev flowname list and look for entry matching this entry */
 302         for (flow_curr = state->fs_flowchain; flow_curr;
 303             flow_curr = flow_curr->fc_next) {
 304                 if (strcmp(flow_curr->fc_flowname, flowname) == 0)
 305                         break;
 306         }
 307 
 308         /* New flow, add it */
 309         if (flow_curr == NULL) {
 310                 flow_curr = (flow_chain_t *)malloc(sizeof (flow_chain_t));
 311                 if (flow_curr == NULL)
 312                         goto done;
 313                 (void) strncpy(flow_curr->fc_flowname, flowname,
 314                     MAXFLOWNAMELEN);
 315                 flow_curr->fc_stat = NULL;
 316                 flow_curr->fc_next = state->fs_flowchain;
 317                 state->fs_flowchain = flow_curr;
 318         }
 319 done:
 320         return (flow_curr);
 321 }
 322 
 323 /*
 324  * Number of flows may change while flowstat -i is executing.
 325  * Free memory allocated for flows that are no longer there.
 326  * Prepare for next iteration by marking visited = false for
 327  * existing stat entries.
 328  */
 329 static void
 330 cleanup_removed_flows(show_flow_state_t *state)
 331 {
 332         flow_chain_t    *fcurr;
 333         flow_chain_t    *fprev;
 334         flow_chain_t    *tofree;
 335 
 336         /* Delete all nodes from the list that have fc_visited marked false */
 337         fcurr = state->fs_flowchain;
 338         while (fcurr != NULL) {
 339                 if (fcurr->fc_visited) {
 340                         fcurr->fc_visited = B_FALSE;
 341                         fprev = fcurr;
 342                         fcurr = fcurr->fc_next;
 343                         continue;
 344                 }
 345 
 346                 /* Is it head of the list? */
 347                 if (fcurr == state->fs_flowchain)
 348                         state->fs_flowchain = fcurr->fc_next;
 349                 else
 350                         fprev->fc_next = fcurr->fc_next;
 351 
 352                 /* fprev remains the same */
 353                 tofree = fcurr;
 354                 fcurr = fcurr->fc_next;
 355 
 356                 /* Free stats memory for the removed flow */
 357                 dladm_flow_stat_free(tofree->fc_stat);
 358                 free(tofree);
 359         }
 360 }
 361 
 362 static boolean_t
 363 print_flow_stats_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
 364 {
 365         flow_args_t     *fargs = of_arg->ofmt_cbarg;
 366         flow_stat_t     *diff_stats = fargs->flow_s_stat;
 367         char            unit = fargs->flow_s_unit;
 368         boolean_t       parsable = fargs->flow_s_parsable;
 369 
 370         switch (of_arg->ofmt_id) {
 371         case FLOW_S_FLOW:
 372                 (void) snprintf(buf, bufsize, "%s", fargs->flow_s_flow);
 373                 break;
 374         case FLOW_S_IPKTS:
 375                 map_to_units(buf, bufsize, diff_stats->fl_ipackets, unit,
 376                     parsable);
 377                 break;
 378         case FLOW_S_RBYTES:
 379                 map_to_units(buf, bufsize, diff_stats->fl_rbytes, unit,
 380                     parsable);
 381                 break;
 382         case FLOW_S_IERRORS:
 383                 map_to_units(buf, bufsize, diff_stats->fl_ierrors, unit,
 384                     parsable);
 385                 break;
 386         case FLOW_S_OPKTS:
 387                 map_to_units(buf, bufsize, diff_stats->fl_opackets, unit,
 388                     parsable);
 389                 break;
 390         case FLOW_S_OBYTES:
 391                 map_to_units(buf, bufsize, diff_stats->fl_obytes, unit,
 392                     parsable);
 393                 break;
 394         case FLOW_S_OERRORS:
 395                 map_to_units(buf, bufsize, diff_stats->fl_oerrors, unit,
 396                     parsable);
 397                 break;
 398         default:
 399                 die("invalid input");
 400                 break;
 401         }
 402         return (B_TRUE);
 403 }
 404 
 405 /* ARGSUSED */
 406 static int
 407 query_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
 408 {
 409         show_flow_state_t       *state = arg;
 410         flow_chain_t            *flow_node;
 411         flow_stat_t             *curr_stat;
 412         flow_stat_t             *prev_stat;
 413         flow_stat_t             *diff_stat;
 414         char                    *flowname = attr->fa_flowname;
 415         flow_args_t             fargs;
 416 
 417         /* Get previous stats for the flow */
 418         flow_node = get_flow_prev_stat(flowname, arg);
 419         if (flow_node == NULL)
 420                 goto done;
 421 
 422         flow_node->fc_visited = B_TRUE;
 423         prev_stat = flow_node->fc_stat;
 424 
 425         /* Query library for current stats */
 426         curr_stat = dladm_flow_stat_query(flowname);
 427         if (curr_stat == NULL)
 428                 goto done;
 429 
 430         /* current stats - prev iteration stats */
 431         diff_stat = dladm_flow_stat_diff(curr_stat, prev_stat);
 432 
 433         /* Free prev stats */
 434         dladm_flow_stat_free(prev_stat);
 435 
 436         /* Prev <- curr stats */
 437         flow_node->fc_stat = curr_stat;
 438 
 439         if (diff_stat == NULL)
 440                 goto done;
 441 
 442         /* Print stats */
 443         fargs.flow_s_flow = flowname;
 444         fargs.flow_s_stat = diff_stat;
 445         fargs.flow_s_unit = state->fs_unit;
 446         fargs.flow_s_parsable = state->fs_parsable;
 447         ofmt_print(state->fs_ofmt, &fargs);
 448 
 449         /* Free diff stats */
 450         dladm_flow_stat_free(diff_stat);
 451 done:
 452         return (DLADM_WALK_CONTINUE);
 453 }
 454 
 455 /*
 456  * Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for
 457  * dladm_walk_datalink_id(). Used for showing flow stats for
 458  * all flows on all links.
 459  */
 460 static int
 461 query_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg)
 462 {
 463         if (dladm_walk_flow(query_flow_stats, dh, linkid, arg, B_FALSE)
 464             == DLADM_STATUS_OK)
 465                 return (DLADM_WALK_CONTINUE);
 466         else
 467                 return (DLADM_WALK_TERMINATE);
 468 }
 469 
 470 void
 471 print_all_stats(name_value_stat_entry_t *stat_entry)
 472 {
 473         name_value_stat_t       *curr_stat;
 474 
 475         printf("%s\n", stat_entry->nve_header);
 476 
 477         for (curr_stat = stat_entry->nve_stats; curr_stat != NULL;
 478             curr_stat = curr_stat->nv_nextstat) {
 479                 printf("\t%15s", curr_stat->nv_statname);
 480                 printf("\t%15llu\n", curr_stat->nv_statval);
 481         }
 482 }
 483 
 484 /* ARGSUSED */
 485 static int
 486 dump_one_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
 487 {
 488         char    *flowname = attr->fa_flowname;
 489         void    *stat;
 490 
 491         stat = dladm_flow_stat_query_all(flowname);
 492         if (stat == NULL)
 493                 goto done;
 494         print_all_stats(stat);
 495         dladm_flow_stat_query_all_free(stat);
 496 
 497 done:
 498         return (DLADM_WALK_CONTINUE);
 499 }
 500 
 501 /*
 502  * Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for
 503  * dladm_walk_datalink_id(). Used for showing flow stats for
 504  * all flows on all links.
 505  */
 506 static int
 507 dump_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg)
 508 {
 509         if (dladm_walk_flow(dump_one_flow_stats, dh, linkid, arg, B_FALSE)
 510             == DLADM_STATUS_OK)
 511                 return (DLADM_WALK_CONTINUE);
 512         else
 513                 return (DLADM_WALK_TERMINATE);
 514 }
 515 
 516 static void
 517 dump_all_flow_stats(dladm_flow_attr_t *attrp, void *arg, datalink_id_t linkid,
 518     boolean_t flow_arg)
 519 {
 520         /* Show stats for named flow */
 521         if (flow_arg)  {
 522                 (void) dump_one_flow_stats(handle, attrp, arg);
 523 
 524         /* Show stats for flows on one link */
 525         } else if (linkid != DATALINK_INVALID_LINKID) {
 526                 (void) dladm_walk_flow(dump_one_flow_stats, handle, linkid,
 527                     arg, B_FALSE);
 528 
 529         /* Show stats for all flows on all links */
 530         } else {
 531                 (void) dladm_walk_datalink_id(dump_link_flow_stats,
 532                     handle, arg, DATALINK_CLASS_ALL,
 533                     DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
 534         }
 535 }
 536 
 537 int
 538 main(int argc, char *argv[])
 539 {
 540         dladm_status_t          status;
 541         int                     option;
 542         boolean_t               r_arg = B_FALSE;
 543         boolean_t               t_arg = B_FALSE;
 544         boolean_t               p_arg = B_FALSE;
 545         boolean_t               i_arg = B_FALSE;
 546         boolean_t               o_arg = B_FALSE;
 547         boolean_t               u_arg = B_FALSE;
 548         boolean_t               A_arg = B_FALSE;
 549         boolean_t               S_arg = B_FALSE;
 550         boolean_t               flow_arg = B_FALSE;
 551         datalink_id_t           linkid = DATALINK_ALL_LINKID;
 552         char                    linkname[MAXLINKNAMELEN];
 553         char                    flowname[MAXFLOWNAMELEN];
 554         uint32_t                interval = 0;
 555         char                    unit = '\0';
 556         show_flow_state_t       state;
 557         char                    *fields_str = NULL;
 558         char                    *o_fields_str = NULL;
 559 
 560         char                    *total_stat_fields =
 561             "flow,ipkts,rbytes,ierrs,opkts,obytes,oerrs";
 562         char                    *rx_stat_fields =
 563             "flow,ipkts,rbytes,ierrs";
 564         char                    *tx_stat_fields =
 565             "flow,opkts,obytes,oerrs";
 566 
 567         ofmt_handle_t           ofmt;
 568         ofmt_status_t           oferr;
 569         uint_t                  ofmtflags = OFMT_RIGHTJUST;
 570 
 571         dladm_flow_attr_t       attr;
 572 
 573         (void) setlocale(LC_ALL, "");
 574 #if !defined(TEXT_DOMAIN)
 575 #define TEXT_DOMAIN "SYS_TEST"
 576 #endif
 577         (void) textdomain(TEXT_DOMAIN);
 578 
 579         progname = argv[0];
 580 
 581         /* Open the libdladm handle */
 582         if ((status = dladm_open(&handle)) != DLADM_STATUS_OK)
 583                 die_dlerr(status, "could not open /dev/dld");
 584 
 585         bzero(&state, sizeof (state));
 586 
 587         opterr = 0;
 588         while ((option = getopt_long(argc, argv, ":rtApSi:o:u:l:h",
 589             NULL, NULL)) != -1) {
 590                 switch (option) {
 591                 case 'r':
 592                         if (r_arg)
 593                                 die_optdup(option);
 594 
 595                         r_arg = B_TRUE;
 596                         break;
 597                 case 't':
 598                         if (t_arg)
 599                                 die_optdup(option);
 600 
 601                         t_arg = B_TRUE;
 602                         break;
 603                 case 'A':
 604                         if (A_arg)
 605                                 die_optdup(option);
 606 
 607                         A_arg = B_TRUE;
 608                         break;
 609                 case 'p':
 610                         if (p_arg)
 611                                 die_optdup(option);
 612 
 613                         p_arg = B_TRUE;
 614                         break;
 615                 case 'S':
 616                         if (S_arg)
 617                                 die_optdup(option);
 618                         S_arg = B_TRUE;
 619                         break;
 620                 case 'i':
 621                         if (i_arg)
 622                                 die_optdup(option);
 623 
 624                         i_arg = B_TRUE;
 625                         if (!dladm_str2interval(optarg, &interval))
 626                                 die("invalid interval value '%s'", optarg);
 627                         break;
 628                 case 'o':
 629                         o_arg = B_TRUE;
 630                         o_fields_str = optarg;
 631                         break;
 632                 case 'u':
 633                         if (u_arg)
 634                                 die_optdup(option);
 635 
 636                         u_arg = B_TRUE;
 637                         if (!flowstat_unit(optarg, &unit))
 638                                 die("invalid unit value '%s',"
 639                                     "unit must be R|K|M|G|T|P", optarg);
 640                         break;
 641                 case 'l':
 642                         if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
 643                             >= MAXLINKNAMELEN)
 644                                 die("link name too long\n");
 645                         if (dladm_name2info(handle, linkname, &linkid, NULL,
 646                             NULL, NULL) != DLADM_STATUS_OK)
 647                                 die("invalid link '%s'", linkname);
 648                         break;
 649                 case 'h':
 650                         if (r_arg || t_arg || p_arg || o_arg || u_arg ||
 651                             i_arg || S_arg || A_arg) {
 652                                 die("the option -h is not compatible with "
 653                                     "-r, -t, -p, -o, -u, -i, -S, -A");
 654                         }
 655                         do_show_history(argc, argv);
 656                         return (0);
 657                         break;
 658                 default:
 659                         die_opterr(optopt, option, usage_ermsg);
 660                         break;
 661                 }
 662         }
 663 
 664         if (r_arg && t_arg)
 665                 die("the option -t and -r are not compatible");
 666 
 667         if (u_arg && p_arg)
 668                 die("the option -u and -p are not compatible");
 669 
 670         if (p_arg && !o_arg)
 671                 die("-p requires -o");
 672 
 673         if (p_arg && strcasecmp(o_fields_str, "all") == 0)
 674                 die("\"-o all\" is invalid with -p");
 675 
 676         if (S_arg &&
 677             (r_arg || t_arg || p_arg || o_arg || u_arg))
 678                 die("the option -S is not compatible with "
 679                     "-r, -t, -p, -o, -u");
 680 
 681         if (A_arg &&
 682             (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg))
 683                 die("the option -A is not compatible with "
 684                     "-r, -t, -p, -o, -u, -i");
 685 
 686         /* get flow name (optional last argument) */
 687         if (optind == (argc-1)) {
 688                 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN)
 689                     >= MAXFLOWNAMELEN)
 690                         die("flow name too long");
 691                 flow_arg = B_TRUE;
 692         } else if (optind != argc) {
 693                 usage();
 694         }
 695 
 696         if (S_arg) {
 697                 dladm_continuous(handle, linkid, (flow_arg ? flowname : NULL),
 698                     interval, FLOW_REPORT);
 699                 return (0);
 700         }
 701 
 702         if (flow_arg &&
 703             dladm_flow_info(handle, flowname, &attr) != DLADM_STATUS_OK)
 704                 die("invalid flow %s", flowname);
 705 
 706         if (A_arg) {
 707                 dump_all_flow_stats(&attr, &state, linkid, flow_arg);
 708                 return (0);
 709         }
 710 
 711         state.fs_unit = unit;
 712         state.fs_parsable = p_arg;
 713 
 714         if (state.fs_parsable)
 715                 ofmtflags |= OFMT_PARSABLE;
 716 
 717         if (r_arg)
 718                 fields_str = rx_stat_fields;
 719         else if (t_arg)
 720                 fields_str = tx_stat_fields;
 721         else
 722                 fields_str = total_stat_fields;
 723 
 724         if (o_arg) {
 725                 fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
 726                     fields_str : o_fields_str;
 727         }
 728 
 729         oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt);
 730         flowstat_ofmt_check(oferr, state.fs_parsable, ofmt);
 731         state.fs_ofmt = ofmt;
 732 
 733         for (;;) {
 734                 /* Show stats for named flow */
 735                 if (flow_arg)  {
 736                         (void) query_flow_stats(handle, &attr, &state);
 737 
 738                 /* Show stats for flows on one link */
 739                 } else if (linkid != DATALINK_INVALID_LINKID) {
 740                         (void) dladm_walk_flow(query_flow_stats, handle, linkid,
 741                             &state, B_FALSE);
 742 
 743                 /* Show stats for all flows on all links */
 744                 } else {
 745                         (void) dladm_walk_datalink_id(query_link_flow_stats,
 746                             handle, &state, DATALINK_CLASS_ALL,
 747                             DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
 748                 }
 749 
 750                 if (interval == 0)
 751                         break;
 752 
 753                 (void) fflush(stdout);
 754                 cleanup_removed_flows(&state);
 755                 (void) sleep(interval);
 756         }
 757         ofmt_close(ofmt);
 758 
 759         dladm_close(handle);
 760         return (0);
 761 }
 762 
 763 /* ARGSUSED */
 764 static int
 765 show_history_date(dladm_usage_t *history, void *arg)
 766 {
 767         show_history_state_t    *state = (show_history_state_t *)arg;
 768         time_t                  stime;
 769         char                    timebuf[20];
 770         dladm_flow_attr_t       attr;
 771         dladm_status_t          status;
 772 
 773         /*
 774          * Only show historical information for existing flows unless '-a'
 775          * is specified.
 776          */
 777         if (!state->us_showall && ((status = dladm_flow_info(handle,
 778             history->du_name, &attr)) != DLADM_STATUS_OK)) {
 779                 return (status);
 780         }
 781 
 782         stime = history->du_stime;
 783         (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
 784             localtime(&stime));
 785         (void) printf("%s\n", timebuf);
 786 
 787         return (DLADM_STATUS_OK);
 788 }
 789 
 790 static int
 791 show_history_time(dladm_usage_t *history, void *arg)
 792 {
 793         show_history_state_t    *state = (show_history_state_t *)arg;
 794         char                    buf[DLADM_STRSIZE];
 795         history_l_fields_buf_t  ubuf;
 796         time_t                  time;
 797         double                  bw;
 798         dladm_flow_attr_t       attr;
 799         dladm_status_t          status;
 800 
 801         /*
 802          * Only show historical information for existing flows unless '-a'
 803          * is specified.
 804          */
 805         if (!state->us_showall && ((status = dladm_flow_info(handle,
 806             history->du_name, &attr)) != DLADM_STATUS_OK)) {
 807                 return (status);
 808         }
 809 
 810         if (state->us_plot) {
 811                 if (!state->us_printheader) {
 812                         if (state->us_first) {
 813                                 (void) printf("# Time");
 814                                 state->us_first = B_FALSE;
 815                         }
 816                         (void) printf(" %s", history->du_name);
 817                         if (history->du_last) {
 818                                 (void) printf("\n");
 819                                 state->us_first = B_TRUE;
 820                                 state->us_printheader = B_TRUE;
 821                         }
 822                 } else {
 823                         if (state->us_first) {
 824                                 time = history->du_etime;
 825                                 (void) strftime(buf, sizeof (buf), "%T",
 826                                     localtime(&time));
 827                                 state->us_first = B_FALSE;
 828                                 (void) printf("%s", buf);
 829                         }
 830                         bw = (double)history->du_bandwidth/1000;
 831                         (void) printf(" %.2f", bw);
 832                         if (history->du_last) {
 833                                 (void) printf("\n");
 834                                 state->us_first = B_TRUE;
 835                         }
 836                 }
 837                 return (DLADM_STATUS_OK);
 838         }
 839 
 840         bzero(&ubuf, sizeof (ubuf));
 841 
 842         (void) snprintf(ubuf.history_l_flow, sizeof (ubuf.history_l_flow), "%s",
 843             history->du_name);
 844         time = history->du_stime;
 845         (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
 846         (void) snprintf(ubuf.history_l_stime, sizeof (ubuf.history_l_stime),
 847             "%s", buf);
 848         time = history->du_etime;
 849         (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
 850         (void) snprintf(ubuf.history_l_etime, sizeof (ubuf.history_l_etime),
 851             "%s", buf);
 852         (void) snprintf(ubuf.history_l_rbytes, sizeof (ubuf.history_l_rbytes),
 853             "%llu", history->du_rbytes);
 854         (void) snprintf(ubuf.history_l_obytes, sizeof (ubuf.history_l_obytes),
 855             "%llu", history->du_obytes);
 856         (void) snprintf(ubuf.history_l_bandwidth,
 857             sizeof (ubuf.history_l_bandwidth), "%s Mbps",
 858             dladm_bw2str(history->du_bandwidth, buf));
 859 
 860         ofmt_print(state->us_ofmt, (void *)&ubuf);
 861         return (DLADM_STATUS_OK);
 862 }
 863 
 864 static int
 865 show_history_res(dladm_usage_t *history, void *arg)
 866 {
 867         show_history_state_t    *state = (show_history_state_t *)arg;
 868         char                    buf[DLADM_STRSIZE];
 869         history_fields_buf_t    ubuf;
 870         dladm_flow_attr_t       attr;
 871         dladm_status_t          status;
 872 
 873         /*
 874          * Only show historical information for existing flows unless '-a'
 875          * is specified.
 876          */
 877         if (!state->us_showall && ((status = dladm_flow_info(handle,
 878             history->du_name, &attr)) != DLADM_STATUS_OK)) {
 879                 return (status);
 880         }
 881 
 882         bzero(&ubuf, sizeof (ubuf));
 883 
 884         (void) snprintf(ubuf.history_flow, sizeof (ubuf.history_flow), "%s",
 885             history->du_name);
 886         (void) snprintf(ubuf.history_duration, sizeof (ubuf.history_duration),
 887             "%llu", history->du_duration);
 888         (void) snprintf(ubuf.history_ipackets, sizeof (ubuf.history_ipackets),
 889             "%llu", history->du_ipackets);
 890         (void) snprintf(ubuf.history_rbytes, sizeof (ubuf.history_rbytes),
 891             "%llu", history->du_rbytes);
 892         (void) snprintf(ubuf.history_opackets, sizeof (ubuf.history_opackets),
 893             "%llu", history->du_opackets);
 894         (void) snprintf(ubuf.history_obytes, sizeof (ubuf.history_obytes),
 895             "%llu", history->du_obytes);
 896         (void) snprintf(ubuf.history_bandwidth, sizeof (ubuf.history_bandwidth),
 897             "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
 898 
 899         ofmt_print(state->us_ofmt, (void *)&ubuf);
 900 
 901         return (DLADM_STATUS_OK);
 902 }
 903 
 904 static boolean_t
 905 valid_formatspec(char *formatspec_str)
 906 {
 907         return (strcmp(formatspec_str, "gnuplot") == 0);
 908 }
 909 
 910 /* ARGSUSED */
 911 static void
 912 do_show_history(int argc, char *argv[])
 913 {
 914         char                    *file = NULL;
 915         int                     opt;
 916         dladm_status_t          status;
 917         boolean_t               d_arg = B_FALSE;
 918         char                    *stime = NULL;
 919         char                    *etime = NULL;
 920         char                    *resource = NULL;
 921         show_history_state_t    state;
 922         boolean_t               o_arg = B_FALSE;
 923         boolean_t               F_arg = B_FALSE;
 924         char                    *fields_str = NULL;
 925         char                    *formatspec_str = NULL;
 926         char                    *all_fields =
 927             "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth";
 928         char                    *all_l_fields =
 929             "flow,start,end,rbytes,obytes,bandwidth";
 930         ofmt_handle_t           ofmt;
 931         ofmt_status_t           oferr;
 932         uint_t                  ofmtflags = 0;
 933 
 934         bzero(&state, sizeof (show_history_state_t));
 935         state.us_parsable = B_FALSE;
 936         state.us_printheader = B_FALSE;
 937         state.us_plot = B_FALSE;
 938         state.us_first = B_TRUE;
 939 
 940         while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) {
 941                 switch (opt) {
 942                 case 'd':
 943                         d_arg = B_TRUE;
 944                         break;
 945                 case 'a':
 946                         state.us_showall = B_TRUE;
 947                         break;
 948                 case 'f':
 949                         file = optarg;
 950                         break;
 951                 case 's':
 952                         stime = optarg;
 953                         break;
 954                 case 'e':
 955                         etime = optarg;
 956                         break;
 957                 case 'o':
 958                         o_arg = B_TRUE;
 959                         fields_str = optarg;
 960                         break;
 961                 case 'F':
 962                         state.us_plot = F_arg = B_TRUE;
 963                         formatspec_str = optarg;
 964                         break;
 965                 default:
 966                         die_opterr(optopt, opt, usage_ermsg);
 967                 }
 968         }
 969 
 970         if (file == NULL)
 971                 die("-h requires a file");
 972 
 973         if (optind == (argc-1)) {
 974                 dladm_flow_attr_t       attr;
 975 
 976                 resource = argv[optind];
 977                 if (!state.us_showall &&
 978                     dladm_flow_info(handle, resource, &attr) !=
 979                     DLADM_STATUS_OK) {
 980                         die("invalid flow: '%s'", resource);
 981                 }
 982         }
 983 
 984         if (state.us_parsable)
 985                 ofmtflags |= OFMT_PARSABLE;
 986         if (resource == NULL && stime == NULL && etime == NULL) {
 987                 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
 988                         fields_str = all_fields;
 989                 oferr = ofmt_open(fields_str, history_fields, ofmtflags,
 990                     0, &ofmt);
 991         } else {
 992                 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
 993                         fields_str = all_l_fields;
 994                 oferr = ofmt_open(fields_str, history_l_fields, ofmtflags,
 995                     0, &ofmt);
 996         }
 997 
 998         flowstat_ofmt_check(oferr, state.us_parsable, ofmt);
 999         state.us_ofmt = ofmt;
1000 
1001         if (F_arg && d_arg)
1002                 die("incompatible -d and -F options");
1003 
1004         if (F_arg && !valid_formatspec(formatspec_str))
1005                 die("Format specifier %s not supported", formatspec_str);
1006 
1007         if (d_arg) {
1008                 /* Print log dates */
1009                 status = dladm_usage_dates(show_history_date,
1010                     DLADM_LOGTYPE_FLOW, file, resource, &state);
1011         } else if (resource == NULL && stime == NULL && etime == NULL &&
1012             !F_arg) {
1013                 /* Print summary */
1014                 status = dladm_usage_summary(show_history_res,
1015                     DLADM_LOGTYPE_FLOW, file, &state);
1016         } else if (resource != NULL) {
1017                 /* Print log entries for named resource */
1018                 status = dladm_walk_usage_res(show_history_time,
1019                     DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state);
1020         } else {
1021                 /* Print time and information for each flow */
1022                 status = dladm_walk_usage_time(show_history_time,
1023                     DLADM_LOGTYPE_FLOW, file, stime, etime, &state);
1024         }
1025 
1026         ofmt_close(ofmt);
1027         if (status != DLADM_STATUS_OK)
1028                 die_dlerr(status, "-h");
1029         dladm_close(handle);
1030 }
1031 
1032 static void
1033 warn(const char *format, ...)
1034 {
1035         va_list alist;
1036 
1037         format = gettext(format);
1038         (void) fprintf(stderr, "%s: warning: ", progname);
1039 
1040         va_start(alist, format);
1041         (void) vfprintf(stderr, format, alist);
1042         va_end(alist);
1043 
1044         (void) putc('\n', stderr);
1045 }
1046 
1047 /* PRINTFLIKE1 */
1048 static void
1049 die(const char *format, ...)
1050 {
1051         va_list alist;
1052 
1053         format = gettext(format);
1054         (void) fprintf(stderr, "%s: ", progname);
1055 
1056         va_start(alist, format);
1057         (void) vfprintf(stderr, format, alist);
1058         va_end(alist);
1059 
1060         (void) putc('\n', stderr);
1061 
1062         /* close dladm handle if it was opened */
1063         if (handle != NULL)
1064                 dladm_close(handle);
1065 
1066         exit(EXIT_FAILURE);
1067 }
1068 
1069 static void
1070 die_optdup(int opt)
1071 {
1072         die("the option -%c cannot be specified more than once", opt);
1073 }
1074 
1075 static void
1076 die_opterr(int opt, int opterr, const char *usage)
1077 {
1078         switch (opterr) {
1079         case ':':
1080                 die("option '-%c' requires a value\nusage: %s", opt,
1081                     gettext(usage));
1082                 break;
1083         case '?':
1084         default:
1085                 die("unrecognized option '-%c'\nusage: %s", opt,
1086                     gettext(usage));
1087                 break;
1088         }
1089 }
1090 
1091 /* PRINTFLIKE2 */
1092 static void
1093 die_dlerr(dladm_status_t err, const char *format, ...)
1094 {
1095         va_list alist;
1096         char    errmsg[DLADM_STRSIZE];
1097 
1098         format = gettext(format);
1099         (void) fprintf(stderr, "%s: ", progname);
1100 
1101         va_start(alist, format);
1102         (void) vfprintf(stderr, format, alist);
1103         va_end(alist);
1104         (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1105 
1106         /* close dladm handle if it was opened */
1107         if (handle != NULL)
1108                 dladm_close(handle);
1109 
1110         exit(EXIT_FAILURE);
1111 }
1112 
1113 
1114 /*
1115  * default output callback function that, when invoked from dladm_print_output,
1116  * prints string which is offset by of_arg->ofmt_id within buf.
1117  */
1118 static boolean_t
1119 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1120 {
1121         char *value;
1122 
1123         value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id;
1124         (void) strlcpy(buf, value, bufsize);
1125         return (B_TRUE);
1126 }
1127 
1128 static void
1129 flowstat_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
1130     ofmt_handle_t ofmt)
1131 {
1132         char buf[OFMT_BUFSIZE];
1133 
1134         if (oferr == OFMT_SUCCESS)
1135                 return;
1136         (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
1137         /*
1138          * All errors are considered fatal in parsable mode.
1139          * NOMEM errors are always fatal, regardless of mode.
1140          * For other errors, we print diagnostics in human-readable
1141          * mode and processs what we can.
1142          */
1143         if (parsable || oferr == OFMT_ENOFIELDS) {
1144                 ofmt_close(ofmt);
1145                 die(buf);
1146         } else {
1147                 warn(buf);
1148         }
1149 }