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