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