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