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