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