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