]> git.proxmox.com Git - mirror_iproute2.git/blame - misc/ifstat.c
libnetlink: Rename rtnl_wilddump_stats_req_filter to rtnl_statsdump_req_filter
[mirror_iproute2.git] / misc / ifstat.c
CommitLineData
aba5acdf
SH
1/*
2 * ifstat.c handy utility to read net interface statistics
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <fcntl.h>
16#include <string.h>
17#include <errno.h>
18#include <time.h>
19#include <sys/time.h>
20#include <fnmatch.h>
21#include <sys/file.h>
22#include <sys/socket.h>
23#include <sys/un.h>
24#include <sys/poll.h>
25#include <sys/wait.h>
26#include <sys/stat.h>
27#include <signal.h>
28#include <math.h>
9f3ea250 29#include <getopt.h>
aba5acdf 30
daf7bd5c
SH
31#include <linux/if.h>
32#include <linux/if_link.h>
aba5acdf 33
3d8048dc
NF
34#include "libnetlink.h"
35#include "json_writer.h"
36#include "SNAPSHOT.h"
5a52102b 37#include "utils.h"
aba5acdf 38
acd1e437
SH
39int dump_zeros;
40int reset_history;
41int ignore_history;
42int no_output;
43int json_output;
44int no_update;
45int scan_interval;
46int time_constant;
47int show_errors;
aba5acdf
SH
48double W;
49char **patterns;
50int npatterns;
5a52102b
NF
51bool is_extended;
52int filter_type;
53int sub_type;
aba5acdf
SH
54
55char info_source[128];
56int source_mismatch;
57
a571587d 58#define MAXS (sizeof(struct rtnl_link_stats)/sizeof(__u32))
5a52102b 59#define NO_SUB_TYPE 0xffff
aba5acdf 60
acd1e437 61struct ifstat_ent {
aba5acdf
SH
62 struct ifstat_ent *next;
63 char *name;
64 int ifindex;
5a52102b 65 __u64 val[MAXS];
aba5acdf 66 double rate[MAXS];
a571587d 67 __u32 ival[MAXS];
aba5acdf
SH
68};
69
ec3e625c
SH
70static const char *stats[MAXS] = {
71 "rx_packets",
72 "tx_packets",
73 "rx_bytes",
74 "tx_bytes",
75 "rx_errors",
76 "tx_errors",
77 "rx_dropped",
78 "tx_dropped",
79 "multicast",
80 "collisions",
81 "rx_length_errors",
82 "rx_over_errors",
83 "rx_crc_errors",
84 "rx_frame_errors",
85 "rx_fifo_errors",
86 "rx_missed_errors",
87 "tx_aborted_errors",
88 "tx_carrier_errors",
89 "tx_fifo_errors",
90 "tx_heartbeat_errors",
91 "tx_window_errors",
92 "rx_compressed",
93 "tx_compressed"
94};
95
aba5acdf
SH
96struct ifstat_ent *kern_db;
97struct ifstat_ent *hist_db;
98
50772dc5 99static int match(const char *id)
aba5acdf
SH
100{
101 int i;
102
103 if (npatterns == 0)
104 return 1;
105
acd1e437 106 for (i = 0; i < npatterns; i++) {
aba5acdf
SH
107 if (!fnmatch(patterns[i], id, 0))
108 return 1;
109 }
110 return 0;
111}
112
5a52102b
NF
113static int get_nlmsg_extended(const struct sockaddr_nl *who,
114 struct nlmsghdr *m, void *arg)
115{
116 struct if_stats_msg *ifsm = NLMSG_DATA(m);
117 struct rtattr *tb[IFLA_STATS_MAX+1];
118 int len = m->nlmsg_len;
119 struct ifstat_ent *n;
120
121 if (m->nlmsg_type != RTM_NEWSTATS)
122 return 0;
123
124 len -= NLMSG_LENGTH(sizeof(*ifsm));
125 if (len < 0)
126 return -1;
127
128 parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
129 if (tb[filter_type] == NULL)
130 return 0;
131
132 n = malloc(sizeof(*n));
133 if (!n)
134 abort();
135
136 n->ifindex = ifsm->ifindex;
137 n->name = strdup(ll_index_to_name(ifsm->ifindex));
138
139 if (sub_type == NO_SUB_TYPE) {
140 memcpy(&n->val, RTA_DATA(tb[filter_type]), sizeof(n->val));
141 } else {
142 struct rtattr *attr;
143
144 attr = parse_rtattr_one_nested(sub_type, tb[filter_type]);
35f6adef
PS
145 if (attr == NULL) {
146 free(n);
5a52102b 147 return 0;
35f6adef 148 }
5a52102b
NF
149 memcpy(&n->val, RTA_DATA(attr), sizeof(n->val));
150 }
151 memset(&n->rate, 0, sizeof(n->rate));
152 n->next = kern_db;
153 kern_db = n;
154 return 0;
155}
156
ae665a52 157static int get_nlmsg(const struct sockaddr_nl *who,
50772dc5 158 struct nlmsghdr *m, void *arg)
aba5acdf
SH
159{
160 struct ifinfomsg *ifi = NLMSG_DATA(m);
acd1e437 161 struct rtattr *tb[IFLA_MAX+1];
aba5acdf
SH
162 int len = m->nlmsg_len;
163 struct ifstat_ent *n;
164 int i;
165
166 if (m->nlmsg_type != RTM_NEWLINK)
167 return 0;
168
169 len -= NLMSG_LENGTH(sizeof(*ifi));
170 if (len < 0)
171 return -1;
172
173 if (!(ifi->ifi_flags&IFF_UP))
174 return 0;
175
aba5acdf
SH
176 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
177 if (tb[IFLA_IFNAME] == NULL || tb[IFLA_STATS] == NULL)
178 return 0;
179
180 n = malloc(sizeof(*n));
181 if (!n)
182 abort();
183 n->ifindex = ifi->ifi_index;
184 n->name = strdup(RTA_DATA(tb[IFLA_IFNAME]));
185 memcpy(&n->ival, RTA_DATA(tb[IFLA_STATS]), sizeof(n->ival));
186 memset(&n->rate, 0, sizeof(n->rate));
acd1e437 187 for (i = 0; i < MAXS; i++)
aba5acdf
SH
188 n->val[i] = n->ival[i];
189 n->next = kern_db;
190 kern_db = n;
191 return 0;
192}
193
d1f28cf1 194static void load_info(void)
aba5acdf
SH
195{
196 struct ifstat_ent *db, *n;
197 struct rtnl_handle rth;
5a52102b 198 __u32 filter_mask;
aba5acdf
SH
199
200 if (rtnl_open(&rth, 0) < 0)
201 exit(1);
202
5a52102b
NF
203 if (is_extended) {
204 ll_init_map(&rth);
205 filter_mask = IFLA_STATS_FILTER_BIT(filter_type);
56eeeda9
DA
206 if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC,
207 filter_mask) < 0) {
5a52102b
NF
208 perror("Cannot send dump request");
209 exit(1);
210 }
aba5acdf 211
5a52102b
NF
212 if (rtnl_dump_filter(&rth, get_nlmsg_extended, NULL) < 0) {
213 fprintf(stderr, "Dump terminated\n");
214 exit(1);
215 }
216 } else {
31ae2912 217 if (rtnl_linkdump_req(&rth, AF_INET) < 0) {
5a52102b
NF
218 perror("Cannot send dump request");
219 exit(1);
220 }
221
222 if (rtnl_dump_filter(&rth, get_nlmsg, NULL) < 0) {
223 fprintf(stderr, "Dump terminated\n");
224 exit(1);
225 }
aba5acdf
SH
226 }
227
228 rtnl_close(&rth);
229
230 db = kern_db;
231 kern_db = NULL;
232
233 while (db) {
234 n = db;
235 db = db->next;
236 n->next = kern_db;
237 kern_db = n;
238 }
239}
240
d1f28cf1 241static void load_raw_table(FILE *fp)
aba5acdf
SH
242{
243 char buf[4096];
244 struct ifstat_ent *db = NULL;
245 struct ifstat_ent *n;
246
247 while (fgets(buf, sizeof(buf), fp) != NULL) {
248 char *p;
249 char *next;
250 int i;
251
252 if (buf[0] == '#') {
253 buf[strlen(buf)-1] = 0;
254 if (info_source[0] && strcmp(info_source, buf+1))
255 source_mismatch = 1;
256 strncpy(info_source, buf+1, sizeof(info_source)-1);
257 continue;
258 }
259 if ((n = malloc(sizeof(*n))) == NULL)
260 abort();
261
262 if (!(p = strchr(buf, ' ')))
263 abort();
264 *p++ = 0;
265
266 if (sscanf(buf, "%d", &n->ifindex) != 1)
267 abort();
268 if (!(next = strchr(p, ' ')))
269 abort();
270 *next++ = 0;
271
272 n->name = strdup(p);
273 p = next;
274
acd1e437
SH
275 for (i = 0; i < MAXS; i++) {
276 unsigned int rate;
277
aba5acdf
SH
278 if (!(next = strchr(p, ' ')))
279 abort();
280 *next++ = 0;
281 if (sscanf(p, "%llu", n->val+i) != 1)
282 abort();
a571587d 283 n->ival[i] = (__u32)n->val[i];
aba5acdf
SH
284 p = next;
285 if (!(next = strchr(p, ' ')))
286 abort();
287 *next++ = 0;
288 if (sscanf(p, "%u", &rate) != 1)
289 abort();
290 n->rate[i] = rate;
291 p = next;
292 }
293 n->next = db;
294 db = n;
295 }
296
297 while (db) {
298 n = db;
299 db = db->next;
300 n->next = kern_db;
301 kern_db = n;
302 }
303}
304
d1f28cf1 305static void dump_raw_db(FILE *fp, int to_hist)
aba5acdf 306{
fcc16c22 307 json_writer_t *jw = json_output ? jsonw_new(fp) : NULL;
aba5acdf 308 struct ifstat_ent *n, *h;
ec3e625c 309
aba5acdf 310 h = hist_db;
fcc16c22 311 if (jw) {
d721a145 312 jsonw_start_object(jw);
fcc16c22
SH
313 jsonw_pretty(jw, pretty);
314 jsonw_name(jw, info_source);
315 jsonw_start_object(jw);
316 } else
ec3e625c 317 fprintf(fp, "#%s\n", info_source);
aba5acdf 318
acd1e437 319 for (n = kern_db; n; n = n->next) {
aba5acdf
SH
320 int i;
321 unsigned long long *vals = n->val;
322 double *rates = n->rate;
acd1e437 323
aba5acdf
SH
324 if (!match(n->name)) {
325 struct ifstat_ent *h1;
acd1e437 326
aba5acdf
SH
327 if (!to_hist)
328 continue;
329 for (h1 = h; h1; h1 = h1->next) {
330 if (h1->ifindex == n->ifindex) {
331 vals = h1->val;
332 rates = h1->rate;
333 h = h1->next;
334 break;
335 }
336 }
337 }
ec3e625c 338
fcc16c22
SH
339 if (jw) {
340 jsonw_name(jw, n->name);
341 jsonw_start_object(jw);
1473bda9 342
acd1e437 343 for (i = 0; i < MAXS && stats[i]; i++)
fcc16c22
SH
344 jsonw_uint_field(jw, stats[i], vals[i]);
345 jsonw_end_object(jw);
ec3e625c
SH
346 } else {
347 fprintf(fp, "%d %s ", n->ifindex, n->name);
acd1e437 348 for (i = 0; i < MAXS; i++)
3d0b7439 349 fprintf(fp, "%llu %u ", vals[i],
acd1e437 350 (unsigned int)rates[i]);
ec3e625c
SH
351 fprintf(fp, "\n");
352 }
aba5acdf 353 }
fcc16c22 354 if (jw) {
d721a145
AK
355 jsonw_end_object(jw);
356
fcc16c22
SH
357 jsonw_end_object(jw);
358 jsonw_destroy(&jw);
359 }
aba5acdf
SH
360}
361
9f3ea250
SH
362/* use communication definitions of meg/kilo etc */
363static const unsigned long long giga = 1000000000ull;
bb6a21a4
SH
364static const unsigned long long mega = 1000000;
365static const unsigned long long kilo = 1000;
aba5acdf 366
ec3e625c
SH
367static void format_rate(FILE *fp, const unsigned long long *vals,
368 const double *rates, int i)
aba5acdf
SH
369{
370 char temp[64];
ec3e625c 371
9f3ea250
SH
372 if (vals[i] > giga)
373 fprintf(fp, "%7lluM ", vals[i]/mega);
374 else if (vals[i] > mega)
375 fprintf(fp, "%7lluK ", vals[i]/kilo);
aba5acdf
SH
376 else
377 fprintf(fp, "%8llu ", vals[i]);
378
9f3ea250 379 if (rates[i] > mega) {
acd1e437 380 sprintf(temp, "%uM", (unsigned int)(rates[i]/mega));
aba5acdf 381 fprintf(fp, "%-6s ", temp);
9f3ea250 382 } else if (rates[i] > kilo) {
acd1e437 383 sprintf(temp, "%uK", (unsigned int)(rates[i]/kilo));
aba5acdf
SH
384 fprintf(fp, "%-6s ", temp);
385 } else
acd1e437 386 fprintf(fp, "%-6u ", (unsigned int)rates[i]);
aba5acdf
SH
387}
388
ec3e625c 389static void format_pair(FILE *fp, const unsigned long long *vals, int i, int k)
aba5acdf
SH
390{
391 char temp[64];
acd1e437 392
9f3ea250
SH
393 if (vals[i] > giga)
394 fprintf(fp, "%7lluM ", vals[i]/mega);
395 else if (vals[i] > mega)
396 fprintf(fp, "%7lluK ", vals[i]/kilo);
aba5acdf
SH
397 else
398 fprintf(fp, "%8llu ", vals[i]);
399
9f3ea250 400 if (vals[k] > giga) {
acd1e437 401 sprintf(temp, "%uM", (unsigned int)(vals[k]/mega));
aba5acdf 402 fprintf(fp, "%-6s ", temp);
9f3ea250 403 } else if (vals[k] > mega) {
acd1e437 404 sprintf(temp, "%uK", (unsigned int)(vals[k]/kilo));
aba5acdf
SH
405 fprintf(fp, "%-6s ", temp);
406 } else
acd1e437 407 fprintf(fp, "%-6u ", (unsigned int)vals[k]);
aba5acdf
SH
408}
409
d1f28cf1 410static void print_head(FILE *fp)
aba5acdf
SH
411{
412 fprintf(fp, "#%s\n", info_source);
413 fprintf(fp, "%-15s ", "Interface");
414
415 fprintf(fp, "%8s/%-6s ", "RX Pkts", "Rate");
416 fprintf(fp, "%8s/%-6s ", "TX Pkts", "Rate");
417 fprintf(fp, "%8s/%-6s ", "RX Data", "Rate");
acd1e437 418 fprintf(fp, "%8s/%-6s\n", "TX Data", "Rate");
aba5acdf
SH
419
420 if (!show_errors) {
421 fprintf(fp, "%-15s ", "");
422 fprintf(fp, "%8s/%-6s ", "RX Errs", "Drop");
423 fprintf(fp, "%8s/%-6s ", "TX Errs", "Drop");
424 fprintf(fp, "%8s/%-6s ", "RX Over", "Rate");
acd1e437 425 fprintf(fp, "%8s/%-6s\n", "TX Coll", "Rate");
aba5acdf
SH
426 } else {
427 fprintf(fp, "%-15s ", "");
428 fprintf(fp, "%8s/%-6s ", "RX Errs", "Rate");
429 fprintf(fp, "%8s/%-6s ", "RX Drop", "Rate");
430 fprintf(fp, "%8s/%-6s ", "RX Over", "Rate");
acd1e437 431 fprintf(fp, "%8s/%-6s\n", "RX Leng", "Rate");
aba5acdf
SH
432
433 fprintf(fp, "%-15s ", "");
434 fprintf(fp, "%8s/%-6s ", "RX Crc", "Rate");
435 fprintf(fp, "%8s/%-6s ", "RX Frm", "Rate");
436 fprintf(fp, "%8s/%-6s ", "RX Fifo", "Rate");
acd1e437 437 fprintf(fp, "%8s/%-6s\n", "RX Miss", "Rate");
aba5acdf
SH
438
439 fprintf(fp, "%-15s ", "");
440 fprintf(fp, "%8s/%-6s ", "TX Errs", "Rate");
441 fprintf(fp, "%8s/%-6s ", "TX Drop", "Rate");
442 fprintf(fp, "%8s/%-6s ", "TX Coll", "Rate");
acd1e437 443 fprintf(fp, "%8s/%-6s\n", "TX Carr", "Rate");
aba5acdf
SH
444
445 fprintf(fp, "%-15s ", "");
446 fprintf(fp, "%8s/%-6s ", "TX Abrt", "Rate");
447 fprintf(fp, "%8s/%-6s ", "TX Fifo", "Rate");
448 fprintf(fp, "%8s/%-6s ", "TX Hear", "Rate");
acd1e437 449 fprintf(fp, "%8s/%-6s\n", "TX Wind", "Rate");
aba5acdf
SH
450 }
451}
452
fcc16c22 453static void print_one_json(json_writer_t *jw, const struct ifstat_ent *n,
ec3e625c
SH
454 const unsigned long long *vals)
455{
fcc16c22
SH
456 int i, m = show_errors ? 20 : 10;
457
458 jsonw_name(jw, n->name);
459 jsonw_start_object(jw);
460
acd1e437 461 for (i = 0; i < m && stats[i]; i++)
fcc16c22
SH
462 jsonw_uint_field(jw, stats[i], vals[i]);
463
464 jsonw_end_object(jw);
ec3e625c
SH
465}
466
467static void print_one_if(FILE *fp, const struct ifstat_ent *n,
468 const unsigned long long *vals)
aba5acdf
SH
469{
470 int i;
ec3e625c 471
aba5acdf 472 fprintf(fp, "%-15s ", n->name);
acd1e437 473 for (i = 0; i < 4; i++)
aba5acdf
SH
474 format_rate(fp, vals, n->rate, i);
475 fprintf(fp, "\n");
476
477 if (!show_errors) {
478 fprintf(fp, "%-15s ", "");
479 format_pair(fp, vals, 4, 6);
480 format_pair(fp, vals, 5, 7);
481 format_rate(fp, vals, n->rate, 11);
482 format_rate(fp, vals, n->rate, 9);
483 fprintf(fp, "\n");
484 } else {
485 fprintf(fp, "%-15s ", "");
486 format_rate(fp, vals, n->rate, 4);
487 format_rate(fp, vals, n->rate, 6);
488 format_rate(fp, vals, n->rate, 11);
489 format_rate(fp, vals, n->rate, 10);
490 fprintf(fp, "\n");
491
492 fprintf(fp, "%-15s ", "");
493 format_rate(fp, vals, n->rate, 12);
494 format_rate(fp, vals, n->rate, 13);
495 format_rate(fp, vals, n->rate, 14);
496 format_rate(fp, vals, n->rate, 15);
497 fprintf(fp, "\n");
498
499 fprintf(fp, "%-15s ", "");
500 format_rate(fp, vals, n->rate, 5);
501 format_rate(fp, vals, n->rate, 7);
502 format_rate(fp, vals, n->rate, 9);
503 format_rate(fp, vals, n->rate, 17);
504 fprintf(fp, "\n");
505
506 fprintf(fp, "%-15s ", "");
507 format_rate(fp, vals, n->rate, 16);
508 format_rate(fp, vals, n->rate, 18);
509 format_rate(fp, vals, n->rate, 19);
510 format_rate(fp, vals, n->rate, 20);
511 fprintf(fp, "\n");
512 }
513}
514
d1f28cf1 515static void dump_kern_db(FILE *fp)
aba5acdf 516{
fcc16c22 517 json_writer_t *jw = json_output ? jsonw_new(fp) : NULL;
dc484542 518 struct ifstat_ent *n;
aba5acdf 519
fcc16c22 520 if (jw) {
d721a145 521 jsonw_start_object(jw);
fcc16c22
SH
522 jsonw_pretty(jw, pretty);
523 jsonw_name(jw, info_source);
524 jsonw_start_object(jw);
525 } else
ec3e625c 526 print_head(fp);
aba5acdf 527
acd1e437 528 for (n = kern_db; n; n = n->next) {
aba5acdf
SH
529 if (!match(n->name))
530 continue;
ec3e625c 531
fcc16c22
SH
532 if (jw)
533 print_one_json(jw, n, n->val);
534 else
ec3e625c 535 print_one_if(fp, n, n->val);
aba5acdf 536 }
b530cef0
PS
537 if (jw) {
538 jsonw_end_object(jw);
539
540 jsonw_end_object(jw);
541 jsonw_destroy(&jw);
542 }
aba5acdf
SH
543}
544
d1f28cf1 545static void dump_incr_db(FILE *fp)
aba5acdf
SH
546{
547 struct ifstat_ent *n, *h;
fcc16c22 548 json_writer_t *jw = json_output ? jsonw_new(fp) : NULL;
aba5acdf 549
ec3e625c 550 h = hist_db;
fcc16c22 551 if (jw) {
d721a145 552 jsonw_start_object(jw);
fcc16c22
SH
553 jsonw_pretty(jw, pretty);
554 jsonw_name(jw, info_source);
555 jsonw_start_object(jw);
556 } else
ec3e625c 557 print_head(fp);
aba5acdf 558
acd1e437 559 for (n = kern_db; n; n = n->next) {
aba5acdf
SH
560 int i;
561 unsigned long long vals[MAXS];
562 struct ifstat_ent *h1;
563
564 memcpy(vals, n->val, sizeof(vals));
565
566 for (h1 = h; h1; h1 = h1->next) {
567 if (h1->ifindex == n->ifindex) {
568 for (i = 0; i < MAXS; i++)
569 vals[i] -= h1->val[i];
570 h = h1->next;
571 break;
572 }
573 }
574 if (!match(n->name))
575 continue;
ec3e625c 576
fcc16c22
SH
577 if (jw)
578 print_one_json(jw, n, n->val);
579 else
ec3e625c 580 print_one_if(fp, n, vals);
aba5acdf 581 }
aba5acdf 582
fcc16c22 583 if (jw) {
d721a145
AK
584 jsonw_end_object(jw);
585
fcc16c22
SH
586 jsonw_end_object(jw);
587 jsonw_destroy(&jw);
588 }
589}
aba5acdf
SH
590
591static int children;
592
d1f28cf1 593static void sigchild(int signo)
aba5acdf
SH
594{
595}
596
d1f28cf1 597static void update_db(int interval)
aba5acdf
SH
598{
599 struct ifstat_ent *n, *h;
600
601 n = kern_db;
602 kern_db = NULL;
603
604 load_info();
605
606 h = kern_db;
607 kern_db = n;
608
609 for (n = kern_db; n; n = n->next) {
610 struct ifstat_ent *h1;
acd1e437 611
aba5acdf
SH
612 for (h1 = h; h1; h1 = h1->next) {
613 if (h1->ifindex == n->ifindex) {
614 int i;
acd1e437 615
aba5acdf
SH
616 for (i = 0; i < MAXS; i++) {
617 if ((long)(h1->ival[i] - n->ival[i]) < 0) {
ae665a52 618 memset(n->ival, 0, sizeof(n->ival));
aba5acdf
SH
619 break;
620 }
621 }
ae665a52 622 for (i = 0; i < MAXS; i++) {
aba5acdf 623 double sample;
5a52102b
NF
624 __u64 incr;
625
626 if (is_extended) {
627 incr = h1->val[i] - n->val[i];
628 n->val[i] = h1->val[i];
629 } else {
630 incr = (__u32) (h1->ival[i] - n->ival[i]);
631 n->val[i] += incr;
632 n->ival[i] = h1->ival[i];
633 }
acd1e437 634
aba5acdf
SH
635 sample = (double)(incr*1000)/interval;
636 if (interval >= scan_interval) {
637 n->rate[i] += W*(sample-n->rate[i]);
638 } else if (interval >= 1000) {
639 if (interval >= time_constant) {
640 n->rate[i] = sample;
641 } else {
642 double w = W*(double)interval/scan_interval;
acd1e437 643
aba5acdf
SH
644 n->rate[i] += w*(sample-n->rate[i]);
645 }
646 }
647 }
648
649 while (h != h1) {
650 struct ifstat_ent *tmp = h;
acd1e437 651
aba5acdf
SH
652 h = h->next;
653 free(tmp->name);
654 free(tmp);
655 };
656 h = h1->next;
657 free(h1->name);
658 free(h1);
659 break;
660 }
661 }
662 }
663}
664
acd1e437 665#define T_DIFF(a, b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000)
aba5acdf
SH
666
667
d1f28cf1 668static void server_loop(int fd)
aba5acdf 669{
737f15f6 670 struct timeval snaptime = { 0 };
aba5acdf 671 struct pollfd p;
acd1e437 672
aba5acdf
SH
673 p.fd = fd;
674 p.events = p.revents = POLLIN;
675
676 sprintf(info_source, "%d.%lu sampling_interval=%d time_const=%d",
677 getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000);
678
679 load_info();
680
681 for (;;) {
682 int status;
dd81ee04 683 time_t tdiff;
aba5acdf 684 struct timeval now;
737f15f6 685
aba5acdf
SH
686 gettimeofday(&now, NULL);
687 tdiff = T_DIFF(now, snaptime);
688 if (tdiff >= scan_interval) {
689 update_db(tdiff);
690 snaptime = now;
691 tdiff = 0;
692 }
737f15f6 693
dd81ee04 694 if (poll(&p, 1, scan_interval - tdiff) > 0
aba5acdf
SH
695 && (p.revents&POLLIN)) {
696 int clnt = accept(fd, NULL, NULL);
acd1e437 697
aba5acdf
SH
698 if (clnt >= 0) {
699 pid_t pid;
acd1e437 700
aba5acdf
SH
701 if (children >= 5) {
702 close(clnt);
703 } else if ((pid = fork()) != 0) {
acd1e437 704 if (pid > 0)
aba5acdf
SH
705 children++;
706 close(clnt);
707 } else {
708 FILE *fp = fdopen(clnt, "w");
acd1e437 709
dd81ee04 710 if (fp)
aba5acdf 711 dump_raw_db(fp, 0);
aba5acdf
SH
712 exit(0);
713 }
714 }
715 }
716 while (children && waitpid(-1, &status, WNOHANG) > 0)
717 children--;
718 }
719}
720
d1f28cf1 721static int verify_forging(int fd)
aba5acdf
SH
722{
723 struct ucred cred;
737f15f6
SH
724 socklen_t olen = sizeof(cred);
725
acd1e437 726 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void *)&cred, &olen) ||
aba5acdf
SH
727 olen < sizeof(cred))
728 return -1;
729 if (cred.uid == getuid() || cred.uid == 0)
730 return 0;
731 return -1;
732}
733
5a52102b
NF
734static void xstat_usage(void)
735{
736 fprintf(stderr,
1c2df613
NF
737"Usage: ifstat supported xstats:\n"
738" cpu_hits Counts only packets that went via the CPU.\n");
5a52102b
NF
739}
740
741struct extended_stats_options_t {
742 char *name;
743 int id;
744 int sub_type;
745};
746
747/* Note: if one xstat name is subset of another, it should be before it in this
748 * list.
749 * Name length must be under 64 chars.
750 */
751static const struct extended_stats_options_t extended_stats_options[] = {
1c2df613 752 {"cpu_hits", IFLA_STATS_LINK_OFFLOAD_XSTATS, IFLA_OFFLOAD_XSTATS_CPU_HIT},
5a52102b
NF
753};
754
755static const char *get_filter_type(const char *name)
756{
757 int name_len;
758 int i;
759
760 name_len = strlen(name);
761 for (i = 0; i < ARRAY_SIZE(extended_stats_options); i++) {
762 const struct extended_stats_options_t *xstat;
763
764 xstat = &extended_stats_options[i];
765 if (strncmp(name, xstat->name, name_len) == 0) {
766 filter_type = xstat->id;
767 sub_type = xstat->sub_type;
768 return xstat->name;
769 }
770 }
771
772 fprintf(stderr, "invalid ifstat extension %s\n", name);
773 xstat_usage();
774 return NULL;
775}
776
d7e0809e
SH
777static void usage(void) __attribute__((noreturn));
778
aba5acdf
SH
779static void usage(void)
780{
d7e0809e
SH
781 fprintf(stderr,
782"Usage: ifstat [OPTION] [ PATTERN [ PATTERN ] ]\n"
eca7a742
MF
783" -h, --help this message\n"
784" -a, --ignore ignore history\n"
785" -d, --scan=SECS sample every statistics every SECS\n"
786" -e, --errors show errors\n"
787" -j, --json format output in JSON\n"
788" -n, --nooutput do history only\n"
789" -p, --pretty pretty print\n"
790" -r, --reset reset history\n"
791" -s, --noupdate don't update history\n"
792" -t, --interval=SECS report average over the last SECS\n"
793" -V, --version output version information\n"
5a52102b
NF
794" -z, --zeros show entries with zero activity\n"
795" -x, --extended=TYPE show extended stats of TYPE\n");
d7e0809e
SH
796
797 exit(-1);
aba5acdf
SH
798}
799
9f3ea250
SH
800static const struct option longopts[] = {
801 { "help", 0, 0, 'h' },
802 { "ignore", 0, 0, 'a' },
803 { "scan", 1, 0, 'd'},
804 { "errors", 0, 0, 'e' },
805 { "nooutput", 0, 0, 'n' },
ec3e625c 806 { "json", 0, 0, 'j' },
9f3ea250 807 { "reset", 0, 0, 'r' },
fcc16c22 808 { "pretty", 0, 0, 'p' },
9f3ea250
SH
809 { "noupdate", 0, 0, 's' },
810 { "interval", 1, 0, 't' },
811 { "version", 0, 0, 'V' },
812 { "zeros", 0, 0, 'z' },
5a52102b 813 { "extended", 1, 0, 'x'},
9f3ea250
SH
814 { 0 }
815};
aba5acdf
SH
816
817int main(int argc, char *argv[])
818{
819 char hist_name[128];
820 struct sockaddr_un sun;
821 FILE *hist_fp = NULL;
5a52102b 822 const char *stats_type = NULL;
aba5acdf
SH
823 int ch;
824 int fd;
825
5a52102b
NF
826 is_extended = false;
827 while ((ch = getopt_long(argc, argv, "hjpvVzrnasd:t:ex:",
9f3ea250 828 longopts, NULL)) != EOF) {
acd1e437 829 switch (ch) {
aba5acdf
SH
830 case 'z':
831 dump_zeros = 1;
832 break;
833 case 'r':
834 reset_history = 1;
835 break;
836 case 'a':
837 ignore_history = 1;
838 break;
839 case 's':
840 no_update = 1;
841 break;
842 case 'n':
843 no_output = 1;
844 break;
845 case 'e':
846 show_errors = 1;
847 break;
ec3e625c
SH
848 case 'j':
849 json_output = 1;
850 break;
fcc16c22
SH
851 case 'p':
852 pretty = 1;
853 break;
aba5acdf 854 case 'd':
9f3ea250
SH
855 scan_interval = atoi(optarg) * 1000;
856 if (scan_interval <= 0) {
857 fprintf(stderr, "ifstat: invalid scan interval\n");
858 exit(-1);
859 }
aba5acdf
SH
860 break;
861 case 't':
9f3ea250
SH
862 time_constant = atoi(optarg);
863 if (time_constant <= 0) {
aba5acdf
SH
864 fprintf(stderr, "ifstat: invalid time constant divisor\n");
865 exit(-1);
866 }
867 break;
5a52102b
NF
868 case 'x':
869 stats_type = optarg;
870 is_extended = true;
871 break;
aba5acdf
SH
872 case 'v':
873 case 'V':
874 printf("ifstat utility, iproute2-ss%s\n", SNAPSHOT);
875 exit(0);
876 case 'h':
877 case '?':
878 default:
879 usage();
880 }
881 }
882
883 argc -= optind;
884 argv += optind;
885
5a52102b
NF
886 if (stats_type) {
887 stats_type = get_filter_type(stats_type);
888 if (!stats_type)
889 exit(-1);
890 }
891
aba5acdf
SH
892 sun.sun_family = AF_UNIX;
893 sun.sun_path[0] = 0;
894 sprintf(sun.sun_path+1, "ifstat%d", getuid());
895
896 if (scan_interval > 0) {
897 if (time_constant == 0)
898 time_constant = 60;
899 time_constant *= 1000;
900 W = 1 - 1/exp(log(10)*(double)scan_interval/time_constant);
901 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
902 perror("ifstat: socket");
903 exit(-1);
904 }
acd1e437 905 if (bind(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) < 0) {
aba5acdf
SH
906 perror("ifstat: bind");
907 exit(-1);
908 }
909 if (listen(fd, 5) < 0) {
910 perror("ifstat: listen");
911 exit(-1);
912 }
a7a9ddbb
MF
913 if (daemon(0, 0)) {
914 perror("ifstat: daemon");
915 exit(-1);
916 }
aba5acdf
SH
917 signal(SIGPIPE, SIG_IGN);
918 signal(SIGCHLD, sigchild);
919 server_loop(fd);
920 exit(0);
921 }
922
923 patterns = argv;
924 npatterns = argc;
925
926 if (getenv("IFSTAT_HISTORY"))
daf7bd5c
SH
927 snprintf(hist_name, sizeof(hist_name),
928 "%s", getenv("IFSTAT_HISTORY"));
aba5acdf 929 else
5a52102b
NF
930 if (!stats_type)
931 snprintf(hist_name, sizeof(hist_name),
932 "%s/.ifstat.u%d", P_tmpdir, getuid());
933 else
934 snprintf(hist_name, sizeof(hist_name),
935 "%s/.%s_ifstat.u%d", P_tmpdir, stats_type,
936 getuid());
aba5acdf
SH
937
938 if (reset_history)
939 unlink(hist_name);
940
941 if (!ignore_history || !no_update) {
942 struct stat stb;
943
944 fd = open(hist_name, O_RDWR|O_CREAT|O_NOFOLLOW, 0600);
945 if (fd < 0) {
946 perror("ifstat: open history file");
947 exit(-1);
948 }
949 if ((hist_fp = fdopen(fd, "r+")) == NULL) {
950 perror("ifstat: fdopen history file");
951 exit(-1);
952 }
953 if (flock(fileno(hist_fp), LOCK_EX)) {
954 perror("ifstat: flock history file");
955 exit(-1);
956 }
957 if (fstat(fileno(hist_fp), &stb) != 0) {
958 perror("ifstat: fstat history file");
959 exit(-1);
960 }
961 if (stb.st_nlink != 1 || stb.st_uid != getuid()) {
962 fprintf(stderr, "ifstat: something is so wrong with history file, that I prefer not to proceed.\n");
963 exit(-1);
964 }
965 if (!ignore_history) {
966 FILE *tfp;
9a230771 967 long uptime = -1;
acd1e437 968
aba5acdf
SH
969 if ((tfp = fopen("/proc/uptime", "r")) != NULL) {
970 if (fscanf(tfp, "%ld", &uptime) != 1)
971 uptime = -1;
972 fclose(tfp);
973 }
974 if (uptime >= 0 && time(NULL) >= stb.st_mtime+uptime) {
975 fprintf(stderr, "ifstat: history is aged out, resetting\n");
d572ed4d
PS
976 if (ftruncate(fileno(hist_fp), 0))
977 perror("ifstat: ftruncate");
aba5acdf
SH
978 }
979 }
980
981 load_raw_table(hist_fp);
982
983 hist_db = kern_db;
984 kern_db = NULL;
985 }
986
987 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 &&
acd1e437 988 (connect(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) == 0
aba5acdf 989 || (strcpy(sun.sun_path+1, "ifstat0"),
acd1e437 990 connect(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) == 0))
aba5acdf
SH
991 && verify_forging(fd) == 0) {
992 FILE *sfp = fdopen(fd, "r");
acd1e437 993
6d02518f
PS
994 if (!sfp) {
995 fprintf(stderr, "ifstat: fdopen failed: %s\n",
996 strerror(errno));
997 close(fd);
998 } else {
999 load_raw_table(sfp);
1000 if (hist_db && source_mismatch) {
1001 fprintf(stderr, "ifstat: history is stale, ignoring it.\n");
1002 hist_db = NULL;
1003 }
1004 fclose(sfp);
aba5acdf 1005 }
aba5acdf
SH
1006 } else {
1007 if (fd >= 0)
1008 close(fd);
1009 if (hist_db && info_source[0] && strcmp(info_source, "kernel")) {
1010 fprintf(stderr, "ifstat: history is stale, ignoring it.\n");
1011 hist_db = NULL;
1012 info_source[0] = 0;
1013 }
1014 load_info();
1015 if (info_source[0] == 0)
1016 strcpy(info_source, "kernel");
1017 }
1018
1019 if (!no_output) {
1020 if (ignore_history || hist_db == NULL)
1021 dump_kern_db(stdout);
1022 else
1023 dump_incr_db(stdout);
1024 }
ec3e625c 1025
aba5acdf 1026 if (!no_update) {
d572ed4d
PS
1027 if (ftruncate(fileno(hist_fp), 0))
1028 perror("ifstat: ftruncate");
aba5acdf 1029 rewind(hist_fp);
ec3e625c
SH
1030
1031 json_output = 0;
aba5acdf 1032 dump_raw_db(hist_fp, 1);
ec3e625c 1033 fclose(hist_fp);
aba5acdf
SH
1034 }
1035 exit(0);
1036}