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 }