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  * Copyright 2011 Joyent, Inc.  All rights reserved.
  25  */
  26 
  27 #include <stdio.h>
  28 #include <locale.h>
  29 #include <stdarg.h>
  30 #include <stdlib.h>
  31 #include <fcntl.h>
  32 #include <string.h>
  33 #include <stropts.h>
  34 #include <errno.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 flow_chain_s {
  55         char                    fc_flowname[MAXFLOWNAMELEN];
  56         boolean_t               fc_visited;
  57         flow_stat_t             *fc_stat;
  58         struct flow_chain_s     *fc_next;
  59 } flow_chain_t;
  60 
  61 typedef struct show_flow_state {
  62         flow_chain_t    *fs_flowchain;
  63         ofmt_handle_t   fs_ofmt;
  64         char            fs_unit;
  65         boolean_t       fs_parsable;
  66 } show_flow_state_t;
  67 
  68 typedef struct show_history_state_s {
  69         boolean_t       us_plot;
  70         boolean_t       us_parsable;
  71         boolean_t       us_printheader;
  72         boolean_t       us_first;
  73         boolean_t       us_showall;
  74         ofmt_handle_t   us_ofmt;
  75 } show_history_state_t;
  76 
  77 static void     do_show_history(int, char **);
  78 
  79 static int      query_flow_stats(dladm_handle_t, dladm_flow_attr_t *, void *);
  80 static int      query_link_flow_stats(dladm_handle_t, datalink_id_t, void *);
  81 
  82 static void     die(const char *, ...);
  83 static void     die_optdup(int);
  84 static void     die_opterr(int, int, const char *);
  85 static void     die_dlerr(dladm_status_t, const char *, ...);
  86 static void     warn(const char *, ...);
  87 
  88 /* callback functions for printing output */
  89 static ofmt_cb_t print_default_cb, print_flow_stats_cb;
  90 static void flowstat_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
  91 
  92 #define NULL_OFMT               {NULL, 0, 0, NULL}
  93 
  94 /*
  95  * structures for flowstat (printing live statistics)
  96  */
  97 typedef enum {
  98         FLOW_S_FLOW,
  99         FLOW_S_IPKTS,
 100         FLOW_S_RBYTES,
 101         FLOW_S_IERRORS,
 102         FLOW_S_OPKTS,
 103         FLOW_S_OBYTES,
 104         FLOW_S_OERRORS
 105 } flow_s_field_index_t;
 106 
 107 static ofmt_field_t flow_s_fields[] = {
 108 /* name,        field width,    index,          callback */
 109 { "FLOW",       15,     FLOW_S_FLOW,    print_flow_stats_cb},
 110 { "IPKTS",      8,      FLOW_S_IPKTS,   print_flow_stats_cb},
 111 { "RBYTES",     8,      FLOW_S_RBYTES,  print_flow_stats_cb},
 112 { "IERRS",      8,      FLOW_S_IERRORS, print_flow_stats_cb},
 113 { "OPKTS",      8,      FLOW_S_OPKTS,   print_flow_stats_cb},
 114 { "OBYTES",     8,      FLOW_S_OBYTES,  print_flow_stats_cb},
 115 { "OERRS",      8,      FLOW_S_OERRORS, print_flow_stats_cb},
 116 NULL_OFMT}
 117 ;
 118 
 119 typedef struct flow_args_s {
 120         char            *flow_s_flow;
 121         flow_stat_t     *flow_s_stat;
 122         char            flow_s_unit;
 123         boolean_t       flow_s_parsable;
 124 } flow_args_t;
 125 
 126 /*
 127  * structures for 'flowstat -h'
 128  */
 129 typedef struct  history_fields_buf_s {
 130         char    history_flow[12];
 131         char    history_duration[10];
 132         char    history_ipackets[9];
 133         char    history_rbytes[10];
 134         char    history_opackets[9];
 135         char    history_obytes[10];
 136         char    history_bandwidth[14];
 137 } history_fields_buf_t;
 138 
 139 static ofmt_field_t history_fields[] = {
 140 /* name,        field width,    offset */
 141 { "FLOW",       13,
 142         offsetof(history_fields_buf_t, history_flow), print_default_cb},
 143 { "DURATION",   11,
 144         offsetof(history_fields_buf_t, history_duration), print_default_cb},
 145 { "IPACKETS",   10,
 146         offsetof(history_fields_buf_t, history_ipackets), print_default_cb},
 147 { "RBYTES",     11,
 148         offsetof(history_fields_buf_t, history_rbytes), print_default_cb},
 149 { "OPACKETS",   10,
 150         offsetof(history_fields_buf_t, history_opackets), print_default_cb},
 151 { "OBYTES",     11,
 152         offsetof(history_fields_buf_t, history_obytes), print_default_cb},
 153 { "BANDWIDTH",  15,
 154         offsetof(history_fields_buf_t, history_bandwidth), print_default_cb},
 155 NULL_OFMT}
 156 ;
 157 
 158 typedef struct  history_l_fields_buf_s {
 159         char    history_l_flow[12];
 160         char    history_l_stime[13];
 161         char    history_l_etime[13];
 162         char    history_l_rbytes[8];
 163         char    history_l_obytes[8];
 164         char    history_l_bandwidth[14];
 165 } history_l_fields_buf_t;
 166 
 167 static ofmt_field_t history_l_fields[] = {
 168 /* name,        field width,    offset */
 169 { "FLOW",       13,
 170         offsetof(history_l_fields_buf_t, history_l_flow), print_default_cb},
 171 { "START",      14,
 172         offsetof(history_l_fields_buf_t, history_l_stime), print_default_cb},
 173 { "END",        14,
 174         offsetof(history_l_fields_buf_t, history_l_etime), print_default_cb},
 175 { "RBYTES",     9,
 176         offsetof(history_l_fields_buf_t, history_l_rbytes), print_default_cb},
 177 { "OBYTES",     9,
 178         offsetof(history_l_fields_buf_t, history_l_obytes), print_default_cb},
 179 { "BANDWIDTH",  15,
 180         offsetof(history_l_fields_buf_t, history_l_bandwidth),
 181             print_default_cb},
 182 NULL_OFMT}
 183 ;
 184 
 185 static char *progname;
 186 
 187 /*
 188  * Handle to libdladm.  Opened in main() before the sub-command
 189  * specific function is called.
 190  */
 191 static dladm_handle_t handle = NULL;
 192 
 193 const char *usage_ermsg = "flowstat [-r | -t] [-i interval] "
 194             "[-l link] [-z zonename] [flow]\n"
 195             "       flowstat [-S] [-A] [-i interval] [-p] [ -o field[,...]]\n"
 196             "                [-u R|K|M|G|T|P] [-l link] [-z zonename] [flow]\n"
 197             "       flowstat -h [-a] [-d] [-F format]"
 198             " [-s <DD/MM/YYYY,HH:MM:SS>]\n"
 199             "                [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> "
 200             "[<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         char                    *zonename = NULL;
 561 
 562         char                    *total_stat_fields =
 563             "flow,ipkts,rbytes,ierrs,opkts,obytes,oerrs";
 564         char                    *rx_stat_fields =
 565             "flow,ipkts,rbytes,ierrs";
 566         char                    *tx_stat_fields =
 567             "flow,opkts,obytes,oerrs";
 568 
 569         ofmt_handle_t           ofmt;
 570         ofmt_status_t           oferr;
 571         uint_t                  ofmtflags = OFMT_RIGHTJUST;
 572 
 573         dladm_flow_attr_t       attr;
 574 
 575         (void) setlocale(LC_ALL, "");
 576 #if !defined(TEXT_DOMAIN)
 577 #define TEXT_DOMAIN "SYS_TEST"
 578 #endif
 579         (void) textdomain(TEXT_DOMAIN);
 580 
 581         progname = argv[0];
 582 
 583         /* Open the libdladm handle */
 584         if ((status = dladm_open(&handle)) != DLADM_STATUS_OK)
 585                 die_dlerr(status, "could not open /dev/dld");
 586 
 587         linkname[0] = '\0';
 588         bzero(&state, sizeof (state));
 589 
 590         opterr = 0;
 591         while ((option = getopt_long(argc, argv, ":rtApSi:o:u:l:hz:",
 592             NULL, NULL)) != -1) {
 593                 switch (option) {
 594                 case 'r':
 595                         if (r_arg)
 596                                 die_optdup(option);
 597 
 598                         r_arg = B_TRUE;
 599                         break;
 600                 case 't':
 601                         if (t_arg)
 602                                 die_optdup(option);
 603 
 604                         t_arg = B_TRUE;
 605                         break;
 606                 case 'A':
 607                         if (A_arg)
 608                                 die_optdup(option);
 609 
 610                         A_arg = B_TRUE;
 611                         break;
 612                 case 'p':
 613                         if (p_arg)
 614                                 die_optdup(option);
 615 
 616                         p_arg = B_TRUE;
 617                         break;
 618                 case 'S':
 619                         if (S_arg)
 620                                 die_optdup(option);
 621                         S_arg = B_TRUE;
 622                         break;
 623                 case 'i':
 624                         if (i_arg)
 625                                 die_optdup(option);
 626 
 627                         i_arg = B_TRUE;
 628                         if (!dladm_str2interval(optarg, &interval))
 629                                 die("invalid interval value '%s'", optarg);
 630                         break;
 631                 case 'o':
 632                         o_arg = B_TRUE;
 633                         o_fields_str = optarg;
 634                         break;
 635                 case 'u':
 636                         if (u_arg)
 637                                 die_optdup(option);
 638 
 639                         u_arg = B_TRUE;
 640                         if (!flowstat_unit(optarg, &unit))
 641                                 die("invalid unit value '%s',"
 642                                     "unit must be R|K|M|G|T|P", optarg);
 643                         break;
 644                 case 'l':
 645                         if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
 646                             >= MAXLINKNAMELEN)
 647                                 die("link name too long\n");
 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                 case 'z':
 659                         zonename = optarg;
 660                         break;
 661                 default:
 662                         die_opterr(optopt, option, usage_ermsg);
 663                         break;
 664                 }
 665         }
 666 
 667         if (r_arg && t_arg)
 668                 die("the option -t and -r are not compatible");
 669 
 670         if (u_arg && p_arg)
 671                 die("the option -u and -p are not compatible");
 672 
 673         if (p_arg && !o_arg)
 674                 die("-p requires -o");
 675 
 676         if (p_arg && strcasecmp(o_fields_str, "all") == 0)
 677                 die("\"-o all\" is invalid with -p");
 678 
 679         if (S_arg &&
 680             (r_arg || t_arg || p_arg || o_arg || u_arg))
 681                 die("the option -S is not compatible with "
 682                     "-r, -t, -p, -o, -u");
 683 
 684         if (A_arg &&
 685             (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg))
 686                 die("the option -A is not compatible with "
 687                     "-r, -t, -p, -o, -u, -i");
 688 
 689         if (linkname[0] != '\0') {
 690                 if (dladm_zname2info(handle, zonename, linkname, &linkid, NULL,
 691                     NULL, NULL) != DLADM_STATUS_OK)
 692                         die("invalid link '%s'", linkname);
 693         }
 694 
 695         /* get flow name (optional last argument) */
 696         if (optind == (argc-1)) {
 697                 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN)
 698                     >= MAXFLOWNAMELEN)
 699                         die("flow name too long");
 700                 flow_arg = B_TRUE;
 701         } else if (optind != argc) {
 702                 usage();
 703         }
 704 
 705         if (S_arg) {
 706                 dladm_continuous(handle, linkid, (flow_arg ? flowname : NULL),
 707                     interval, FLOW_REPORT);
 708                 return (0);
 709         }
 710 
 711         if (flow_arg &&
 712             dladm_flow_info(handle, flowname, &attr) != DLADM_STATUS_OK)
 713                 die("invalid flow %s", flowname);
 714 
 715         if (A_arg) {
 716                 dump_all_flow_stats(&attr, &state, linkid, flow_arg);
 717                 return (0);
 718         }
 719 
 720         state.fs_unit = unit;
 721         state.fs_parsable = p_arg;
 722 
 723         if (state.fs_parsable)
 724                 ofmtflags |= OFMT_PARSABLE;
 725 
 726         if (r_arg)
 727                 fields_str = rx_stat_fields;
 728         else if (t_arg)
 729                 fields_str = tx_stat_fields;
 730         else
 731                 fields_str = total_stat_fields;
 732 
 733         if (o_arg) {
 734                 fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
 735                     fields_str : o_fields_str;
 736         }
 737 
 738         oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt);
 739         flowstat_ofmt_check(oferr, state.fs_parsable, ofmt);
 740         state.fs_ofmt = ofmt;
 741 
 742         for (;;) {
 743                 /* Show stats for named flow */
 744                 if (flow_arg)  {
 745                         (void) query_flow_stats(handle, &attr, &state);
 746 
 747                 /* Show stats for flows on one link */
 748                 } else if (linkid != DATALINK_INVALID_LINKID) {
 749                         (void) dladm_walk_flow(query_flow_stats, handle, linkid,
 750                             &state, B_FALSE);
 751 
 752                 /* Show stats for all flows on all links */
 753                 } else {
 754                         (void) dladm_walk_datalink_id(query_link_flow_stats,
 755                             handle, &state, DATALINK_CLASS_ALL,
 756                             DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
 757                 }
 758 
 759                 if (interval == 0)
 760                         break;
 761 
 762                 (void) fflush(stdout);
 763                 cleanup_removed_flows(&state);
 764                 (void) sleep(interval);
 765         }
 766         ofmt_close(ofmt);
 767 
 768         dladm_close(handle);
 769         return (0);
 770 }
 771 
 772 /* ARGSUSED */
 773 static int
 774 show_history_date(dladm_usage_t *history, void *arg)
 775 {
 776         show_history_state_t    *state = (show_history_state_t *)arg;
 777         time_t                  stime;
 778         char                    timebuf[20];
 779         dladm_flow_attr_t       attr;
 780         dladm_status_t          status;
 781 
 782         /*
 783          * Only show historical information for existing flows unless '-a'
 784          * is specified.
 785          */
 786         if (!state->us_showall && ((status = dladm_flow_info(handle,
 787             history->du_name, &attr)) != DLADM_STATUS_OK)) {
 788                 return (status);
 789         }
 790 
 791         stime = history->du_stime;
 792         (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
 793             localtime(&stime));
 794         (void) printf("%s\n", timebuf);
 795 
 796         return (DLADM_STATUS_OK);
 797 }
 798 
 799 static int
 800 show_history_time(dladm_usage_t *history, void *arg)
 801 {
 802         show_history_state_t    *state = (show_history_state_t *)arg;
 803         char                    buf[DLADM_STRSIZE];
 804         history_l_fields_buf_t  ubuf;
 805         time_t                  time;
 806         double                  bw;
 807         dladm_flow_attr_t       attr;
 808         dladm_status_t          status;
 809 
 810         /*
 811          * Only show historical information for existing flows unless '-a'
 812          * is specified.
 813          */
 814         if (!state->us_showall && ((status = dladm_flow_info(handle,
 815             history->du_name, &attr)) != DLADM_STATUS_OK)) {
 816                 return (status);
 817         }
 818 
 819         if (state->us_plot) {
 820                 if (!state->us_printheader) {
 821                         if (state->us_first) {
 822                                 (void) printf("# Time");
 823                                 state->us_first = B_FALSE;
 824                         }
 825                         (void) printf(" %s", history->du_name);
 826                         if (history->du_last) {
 827                                 (void) printf("\n");
 828                                 state->us_first = B_TRUE;
 829                                 state->us_printheader = B_TRUE;
 830                         }
 831                 } else {
 832                         if (state->us_first) {
 833                                 time = history->du_etime;
 834                                 (void) strftime(buf, sizeof (buf), "%T",
 835                                     localtime(&time));
 836                                 state->us_first = B_FALSE;
 837                                 (void) printf("%s", buf);
 838                         }
 839                         bw = (double)history->du_bandwidth/1000;
 840                         (void) printf(" %.2f", bw);
 841                         if (history->du_last) {
 842                                 (void) printf("\n");
 843                                 state->us_first = B_TRUE;
 844                         }
 845                 }
 846                 return (DLADM_STATUS_OK);
 847         }
 848 
 849         bzero(&ubuf, sizeof (ubuf));
 850 
 851         (void) snprintf(ubuf.history_l_flow, sizeof (ubuf.history_l_flow), "%s",
 852             history->du_name);
 853         time = history->du_stime;
 854         (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
 855         (void) snprintf(ubuf.history_l_stime, sizeof (ubuf.history_l_stime),
 856             "%s", buf);
 857         time = history->du_etime;
 858         (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
 859         (void) snprintf(ubuf.history_l_etime, sizeof (ubuf.history_l_etime),
 860             "%s", buf);
 861         (void) snprintf(ubuf.history_l_rbytes, sizeof (ubuf.history_l_rbytes),
 862             "%llu", history->du_rbytes);
 863         (void) snprintf(ubuf.history_l_obytes, sizeof (ubuf.history_l_obytes),
 864             "%llu", history->du_obytes);
 865         (void) snprintf(ubuf.history_l_bandwidth,
 866             sizeof (ubuf.history_l_bandwidth), "%s Mbps",
 867             dladm_bw2str(history->du_bandwidth, buf));
 868 
 869         ofmt_print(state->us_ofmt, (void *)&ubuf);
 870         return (DLADM_STATUS_OK);
 871 }
 872 
 873 static int
 874 show_history_res(dladm_usage_t *history, void *arg)
 875 {
 876         show_history_state_t    *state = (show_history_state_t *)arg;
 877         char                    buf[DLADM_STRSIZE];
 878         history_fields_buf_t    ubuf;
 879         dladm_flow_attr_t       attr;
 880         dladm_status_t          status;
 881 
 882         /*
 883          * Only show historical information for existing flows unless '-a'
 884          * is specified.
 885          */
 886         if (!state->us_showall && ((status = dladm_flow_info(handle,
 887             history->du_name, &attr)) != DLADM_STATUS_OK)) {
 888                 return (status);
 889         }
 890 
 891         bzero(&ubuf, sizeof (ubuf));
 892 
 893         (void) snprintf(ubuf.history_flow, sizeof (ubuf.history_flow), "%s",
 894             history->du_name);
 895         (void) snprintf(ubuf.history_duration, sizeof (ubuf.history_duration),
 896             "%llu", history->du_duration);
 897         (void) snprintf(ubuf.history_ipackets, sizeof (ubuf.history_ipackets),
 898             "%llu", history->du_ipackets);
 899         (void) snprintf(ubuf.history_rbytes, sizeof (ubuf.history_rbytes),
 900             "%llu", history->du_rbytes);
 901         (void) snprintf(ubuf.history_opackets, sizeof (ubuf.history_opackets),
 902             "%llu", history->du_opackets);
 903         (void) snprintf(ubuf.history_obytes, sizeof (ubuf.history_obytes),
 904             "%llu", history->du_obytes);
 905         (void) snprintf(ubuf.history_bandwidth, sizeof (ubuf.history_bandwidth),
 906             "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
 907 
 908         ofmt_print(state->us_ofmt, (void *)&ubuf);
 909 
 910         return (DLADM_STATUS_OK);
 911 }
 912 
 913 static boolean_t
 914 valid_formatspec(char *formatspec_str)
 915 {
 916         return (strcmp(formatspec_str, "gnuplot") == 0);
 917 }
 918 
 919 /* ARGSUSED */
 920 static void
 921 do_show_history(int argc, char *argv[])
 922 {
 923         char                    *file = NULL;
 924         int                     opt;
 925         dladm_status_t          status;
 926         boolean_t               d_arg = B_FALSE;
 927         char                    *stime = NULL;
 928         char                    *etime = NULL;
 929         char                    *resource = NULL;
 930         show_history_state_t    state;
 931         boolean_t               o_arg = B_FALSE;
 932         boolean_t               F_arg = B_FALSE;
 933         char                    *fields_str = NULL;
 934         char                    *formatspec_str = NULL;
 935         char                    *all_fields =
 936             "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth";
 937         char                    *all_l_fields =
 938             "flow,start,end,rbytes,obytes,bandwidth";
 939         ofmt_handle_t           ofmt;
 940         ofmt_status_t           oferr;
 941         uint_t                  ofmtflags = 0;
 942 
 943         bzero(&state, sizeof (show_history_state_t));
 944         state.us_parsable = B_FALSE;
 945         state.us_printheader = B_FALSE;
 946         state.us_plot = B_FALSE;
 947         state.us_first = B_TRUE;
 948 
 949         while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) {
 950                 switch (opt) {
 951                 case 'd':
 952                         d_arg = B_TRUE;
 953                         break;
 954                 case 'a':
 955                         state.us_showall = B_TRUE;
 956                         break;
 957                 case 'f':
 958                         file = optarg;
 959                         break;
 960                 case 's':
 961                         stime = optarg;
 962                         break;
 963                 case 'e':
 964                         etime = optarg;
 965                         break;
 966                 case 'o':
 967                         o_arg = B_TRUE;
 968                         fields_str = optarg;
 969                         break;
 970                 case 'F':
 971                         state.us_plot = F_arg = B_TRUE;
 972                         formatspec_str = optarg;
 973                         break;
 974                 default:
 975                         die_opterr(optopt, opt, usage_ermsg);
 976                 }
 977         }
 978 
 979         if (file == NULL)
 980                 die("-h requires a file");
 981 
 982         if (optind == (argc-1)) {
 983                 dladm_flow_attr_t       attr;
 984 
 985                 resource = argv[optind];
 986                 if (!state.us_showall &&
 987                     dladm_flow_info(handle, resource, &attr) !=
 988                     DLADM_STATUS_OK) {
 989                         die("invalid flow: '%s'", resource);
 990                 }
 991         }
 992 
 993         if (state.us_parsable)
 994                 ofmtflags |= OFMT_PARSABLE;
 995         if (resource == NULL && stime == NULL && etime == NULL) {
 996                 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
 997                         fields_str = all_fields;
 998                 oferr = ofmt_open(fields_str, history_fields, ofmtflags,
 999                     0, &ofmt);
1000         } else {
1001                 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
1002                         fields_str = all_l_fields;
1003                 oferr = ofmt_open(fields_str, history_l_fields, ofmtflags,
1004                     0, &ofmt);
1005         }
1006 
1007         flowstat_ofmt_check(oferr, state.us_parsable, ofmt);
1008         state.us_ofmt = ofmt;
1009 
1010         if (F_arg && d_arg)
1011                 die("incompatible -d and -F options");
1012 
1013         if (F_arg && !valid_formatspec(formatspec_str))
1014                 die("Format specifier %s not supported", formatspec_str);
1015 
1016         if (d_arg) {
1017                 /* Print log dates */
1018                 status = dladm_usage_dates(show_history_date,
1019                     DLADM_LOGTYPE_FLOW, file, resource, &state);
1020         } else if (resource == NULL && stime == NULL && etime == NULL &&
1021             !F_arg) {
1022                 /* Print summary */
1023                 status = dladm_usage_summary(show_history_res,
1024                     DLADM_LOGTYPE_FLOW, file, &state);
1025         } else if (resource != NULL) {
1026                 /* Print log entries for named resource */
1027                 status = dladm_walk_usage_res(show_history_time,
1028                     DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state);
1029         } else {
1030                 /* Print time and information for each flow */
1031                 status = dladm_walk_usage_time(show_history_time,
1032                     DLADM_LOGTYPE_FLOW, file, stime, etime, &state);
1033         }
1034 
1035         ofmt_close(ofmt);
1036         if (status != DLADM_STATUS_OK)
1037                 die_dlerr(status, "-h");
1038         dladm_close(handle);
1039 }
1040 
1041 static void
1042 warn(const char *format, ...)
1043 {
1044         va_list alist;
1045 
1046         format = gettext(format);
1047         (void) fprintf(stderr, "%s: warning: ", progname);
1048 
1049         va_start(alist, format);
1050         (void) vfprintf(stderr, format, alist);
1051         va_end(alist);
1052 
1053         (void) putc('\n', stderr);
1054 }
1055 
1056 /* PRINTFLIKE1 */
1057 static void
1058 die(const char *format, ...)
1059 {
1060         va_list alist;
1061 
1062         format = gettext(format);
1063         (void) fprintf(stderr, "%s: ", progname);
1064 
1065         va_start(alist, format);
1066         (void) vfprintf(stderr, format, alist);
1067         va_end(alist);
1068 
1069         (void) putc('\n', stderr);
1070 
1071         /* close dladm handle if it was opened */
1072         if (handle != NULL)
1073                 dladm_close(handle);
1074 
1075         exit(EXIT_FAILURE);
1076 }
1077 
1078 static void
1079 die_optdup(int opt)
1080 {
1081         die("the option -%c cannot be specified more than once", opt);
1082 }
1083 
1084 static void
1085 die_opterr(int opt, int opterr, const char *usage)
1086 {
1087         switch (opterr) {
1088         case ':':
1089                 die("option '-%c' requires a value\nusage: %s", opt,
1090                     gettext(usage));
1091                 break;
1092         case '?':
1093         default:
1094                 die("unrecognized option '-%c'\nusage: %s", opt,
1095                     gettext(usage));
1096                 break;
1097         }
1098 }
1099 
1100 /* PRINTFLIKE2 */
1101 static void
1102 die_dlerr(dladm_status_t err, const char *format, ...)
1103 {
1104         va_list alist;
1105         char    errmsg[DLADM_STRSIZE];
1106 
1107         format = gettext(format);
1108         (void) fprintf(stderr, "%s: ", progname);
1109 
1110         va_start(alist, format);
1111         (void) vfprintf(stderr, format, alist);
1112         va_end(alist);
1113         (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1114 
1115         /* close dladm handle if it was opened */
1116         if (handle != NULL)
1117                 dladm_close(handle);
1118 
1119         exit(EXIT_FAILURE);
1120 }
1121 
1122 
1123 /*
1124  * default output callback function that, when invoked from dladm_print_output,
1125  * prints string which is offset by of_arg->ofmt_id within buf.
1126  */
1127 static boolean_t
1128 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1129 {
1130         char *value;
1131 
1132         value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id;
1133         (void) strlcpy(buf, value, bufsize);
1134         return (B_TRUE);
1135 }
1136 
1137 static void
1138 flowstat_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
1139     ofmt_handle_t ofmt)
1140 {
1141         char buf[OFMT_BUFSIZE];
1142 
1143         if (oferr == OFMT_SUCCESS)
1144                 return;
1145         (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
1146         /*
1147          * All errors are considered fatal in parsable mode.
1148          * NOMEM errors are always fatal, regardless of mode.
1149          * For other errors, we print diagnostics in human-readable
1150          * mode and processs what we can.
1151          */
1152         if (parsable || oferr == OFMT_ENOFIELDS) {
1153                 ofmt_close(ofmt);
1154                 die(buf);
1155         } else {
1156                 warn(buf);
1157         }
1158 }