]> git.proxmox.com Git - mirror_iproute2.git/blobdiff - misc/ifstat.c
Merge branch 'master' into next
[mirror_iproute2.git] / misc / ifstat.c
index abbb4e732fcefd9f44ee734129de640e9ed98ee8..60efe6cb60faafc78185b17bdadd9ed77f500595 100644 (file)
 #include <math.h>
 #include <getopt.h>
 
-#include <libnetlink.h>
-#include <json_writer.h>
 #include <linux/if.h>
 #include <linux/if_link.h>
 
-#include <SNAPSHOT.h>
+#include "libnetlink.h"
+#include "json_writer.h"
+#include "SNAPSHOT.h"
+#include "utils.h"
 
 int dump_zeros;
 int reset_history;
@@ -44,21 +45,24 @@ int no_update;
 int scan_interval;
 int time_constant;
 int show_errors;
-int pretty;
 double W;
 char **patterns;
 int npatterns;
+bool is_extended;
+int filter_type;
+int sub_type;
 
 char info_source[128];
 int source_mismatch;
 
 #define MAXS (sizeof(struct rtnl_link_stats)/sizeof(__u32))
+#define NO_SUB_TYPE 0xffff
 
 struct ifstat_ent {
        struct ifstat_ent       *next;
        char                    *name;
        int                     ifindex;
-       unsigned long long      val[MAXS];
+       __u64                   val[MAXS];
        double                  rate[MAXS];
        __u32                   ival[MAXS];
 };
@@ -106,8 +110,50 @@ static int match(const char *id)
        return 0;
 }
 
-static int get_nlmsg(const struct sockaddr_nl *who,
-                    struct nlmsghdr *m, void *arg)
+static int get_nlmsg_extended(struct nlmsghdr *m, void *arg)
+{
+       struct if_stats_msg *ifsm = NLMSG_DATA(m);
+       struct rtattr *tb[IFLA_STATS_MAX+1];
+       int len = m->nlmsg_len;
+       struct ifstat_ent *n;
+
+       if (m->nlmsg_type != RTM_NEWSTATS)
+               return 0;
+
+       len -= NLMSG_LENGTH(sizeof(*ifsm));
+       if (len < 0)
+               return -1;
+
+       parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
+       if (tb[filter_type] == NULL)
+               return 0;
+
+       n = malloc(sizeof(*n));
+       if (!n)
+               abort();
+
+       n->ifindex = ifsm->ifindex;
+       n->name = strdup(ll_index_to_name(ifsm->ifindex));
+
+       if (sub_type == NO_SUB_TYPE) {
+               memcpy(&n->val, RTA_DATA(tb[filter_type]), sizeof(n->val));
+       } else {
+               struct rtattr *attr;
+
+               attr = parse_rtattr_one_nested(sub_type, tb[filter_type]);
+               if (attr == NULL) {
+                       free(n);
+                       return 0;
+               }
+               memcpy(&n->val, RTA_DATA(attr), sizeof(n->val));
+       }
+       memset(&n->rate, 0, sizeof(n->rate));
+       n->next = kern_db;
+       kern_db = n;
+       return 0;
+}
+
+static int get_nlmsg(struct nlmsghdr *m, void *arg)
 {
        struct ifinfomsg *ifi = NLMSG_DATA(m);
        struct rtattr *tb[IFLA_MAX+1];
@@ -147,18 +193,34 @@ static void load_info(void)
 {
        struct ifstat_ent *db, *n;
        struct rtnl_handle rth;
+       __u32 filter_mask;
 
        if (rtnl_open(&rth, 0) < 0)
                exit(1);
 
-       if (rtnl_wilddump_request(&rth, AF_INET, RTM_GETLINK) < 0) {
-               perror("Cannot send dump request");
-               exit(1);
-       }
+       if (is_extended) {
+               ll_init_map(&rth);
+               filter_mask = IFLA_STATS_FILTER_BIT(filter_type);
+               if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC,
+                                             filter_mask) < 0) {
+                       perror("Cannot send dump request");
+                       exit(1);
+               }
 
-       if (rtnl_dump_filter(&rth, get_nlmsg, NULL) < 0) {
-               fprintf(stderr, "Dump terminated\n");
-               exit(1);
+               if (rtnl_dump_filter(&rth, get_nlmsg_extended, NULL) < 0) {
+                       fprintf(stderr, "Dump terminated\n");
+                       exit(1);
+               }
+       } else {
+               if (rtnl_linkdump_req(&rth, AF_INET) < 0) {
+                       perror("Cannot send dump request");
+                       exit(1);
+               }
+
+               if (rtnl_dump_filter(&rth, get_nlmsg, NULL) < 0) {
+                       fprintf(stderr, "Dump terminated\n");
+                       exit(1);
+               }
        }
 
        rtnl_close(&rth);
@@ -245,6 +307,7 @@ static void dump_raw_db(FILE *fp, int to_hist)
 
        h = hist_db;
        if (jw) {
+               jsonw_start_object(jw);
                jsonw_pretty(jw, pretty);
                jsonw_name(jw, info_source);
                jsonw_start_object(jw);
@@ -287,6 +350,8 @@ static void dump_raw_db(FILE *fp, int to_hist)
                }
        }
        if (jw) {
+               jsonw_end_object(jw);
+
                jsonw_end_object(jw);
                jsonw_destroy(&jw);
        }
@@ -451,6 +516,7 @@ static void dump_kern_db(FILE *fp)
        struct ifstat_ent *n;
 
        if (jw) {
+               jsonw_start_object(jw);
                jsonw_pretty(jw, pretty);
                jsonw_name(jw, info_source);
                jsonw_start_object(jw);
@@ -466,8 +532,12 @@ static void dump_kern_db(FILE *fp)
                else
                        print_one_if(fp, n, n->val);
        }
-       if (json_output)
-               fprintf(fp, "\n} }\n");
+       if (jw) {
+               jsonw_end_object(jw);
+
+               jsonw_end_object(jw);
+               jsonw_destroy(&jw);
+       }
 }
 
 static void dump_incr_db(FILE *fp)
@@ -477,6 +547,7 @@ static void dump_incr_db(FILE *fp)
 
        h = hist_db;
        if (jw) {
+               jsonw_start_object(jw);
                jsonw_pretty(jw, pretty);
                jsonw_name(jw, info_source);
                jsonw_start_object(jw);
@@ -508,6 +579,8 @@ static void dump_incr_db(FILE *fp)
        }
 
        if (jw) {
+               jsonw_end_object(jw);
+
                jsonw_end_object(jw);
                jsonw_destroy(&jw);
        }
@@ -546,10 +619,17 @@ static void update_db(int interval)
                                }
                                for (i = 0; i < MAXS; i++) {
                                        double sample;
-                                       unsigned long incr = h1->ival[i] - n->ival[i];
+                                       __u64 incr;
+
+                                       if (is_extended) {
+                                               incr = h1->val[i] - n->val[i];
+                                               n->val[i] = h1->val[i];
+                                       } else {
+                                               incr = (__u32) (h1->ival[i] - n->ival[i]);
+                                               n->val[i] += incr;
+                                               n->ival[i] = h1->ival[i];
+                                       }
 
-                                       n->val[i] += incr;
-                                       n->ival[i] = h1->ival[i];
                                        sample = (double)(incr*1000)/interval;
                                        if (interval >= scan_interval) {
                                                n->rate[i] += W*(sample-n->rate[i]);
@@ -649,24 +729,68 @@ static int verify_forging(int fd)
        return -1;
 }
 
+static void xstat_usage(void)
+{
+       fprintf(stderr,
+"Usage: ifstat supported xstats:\n"
+"       cpu_hits       Counts only packets that went via the CPU.\n");
+}
+
+struct extended_stats_options_t {
+       char *name;
+       int id;
+       int sub_type;
+};
+
+/* Note: if one xstat name is subset of another, it should be before it in this
+ * list.
+ * Name length must be under 64 chars.
+ */
+static const struct extended_stats_options_t extended_stats_options[] = {
+       {"cpu_hits",  IFLA_STATS_LINK_OFFLOAD_XSTATS, IFLA_OFFLOAD_XSTATS_CPU_HIT},
+};
+
+static const char *get_filter_type(const char *name)
+{
+       int name_len;
+       int i;
+
+       name_len = strlen(name);
+       for (i = 0; i < ARRAY_SIZE(extended_stats_options); i++) {
+               const struct extended_stats_options_t *xstat;
+
+               xstat = &extended_stats_options[i];
+               if (strncmp(name, xstat->name, name_len) == 0) {
+                       filter_type = xstat->id;
+                       sub_type = xstat->sub_type;
+                       return xstat->name;
+               }
+       }
+
+       fprintf(stderr, "invalid ifstat extension %s\n", name);
+       xstat_usage();
+       return NULL;
+}
+
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
 {
        fprintf(stderr,
 "Usage: ifstat [OPTION] [ PATTERN [ PATTERN ] ]\n"
-"   -h, --help         this message\n"
-"   -a, --ignore       ignore history\n"
-"   -d, --scan=SECS    sample every statistics every SECS\n"
-"   -e, --errors       show errors\n"
-"   -j, --json          format output in JSON\n"
-"   -n, --nooutput     do history only\n"
-"   -p, --pretty        pretty print\n"
-"   -r, --reset                reset history\n"
-"   -s, --noupdate     don\'t update history\n"
-"   -t, --interval=SECS        report average over the last SECS\n"
-"   -V, --version      output version information\n"
-"   -z, --zeros                show entries with zero activity\n");
+"   -h, --help           this message\n"
+"   -a, --ignore         ignore history\n"
+"   -d, --scan=SECS      sample every statistics every SECS\n"
+"   -e, --errors         show errors\n"
+"   -j, --json           format output in JSON\n"
+"   -n, --nooutput       do history only\n"
+"   -p, --pretty         pretty print\n"
+"   -r, --reset          reset history\n"
+"   -s, --noupdate       don't update history\n"
+"   -t, --interval=SECS  report average over the last SECS\n"
+"   -V, --version        output version information\n"
+"   -z, --zeros          show entries with zero activity\n"
+"   -x, --extended=TYPE  show extended stats of TYPE\n");
 
        exit(-1);
 }
@@ -684,6 +808,7 @@ static const struct option longopts[] = {
        { "interval", 1, 0, 't' },
        { "version", 0, 0, 'V' },
        { "zeros", 0, 0, 'z' },
+       { "extended", 1, 0, 'x'},
        { 0 }
 };
 
@@ -692,10 +817,12 @@ int main(int argc, char *argv[])
        char hist_name[128];
        struct sockaddr_un sun;
        FILE *hist_fp = NULL;
+       const char *stats_type = NULL;
        int ch;
        int fd;
 
-       while ((ch = getopt_long(argc, argv, "hjpvVzrnasd:t:e",
+       is_extended = false;
+       while ((ch = getopt_long(argc, argv, "hjpvVzrnasd:t:ex:",
                        longopts, NULL)) != EOF) {
                switch (ch) {
                case 'z':
@@ -736,6 +863,10 @@ int main(int argc, char *argv[])
                                exit(-1);
                        }
                        break;
+               case 'x':
+                       stats_type = optarg;
+                       is_extended = true;
+                       break;
                case 'v':
                case 'V':
                        printf("ifstat utility, iproute2-ss%s\n", SNAPSHOT);
@@ -750,6 +881,12 @@ int main(int argc, char *argv[])
        argc -= optind;
        argv += optind;
 
+       if (stats_type) {
+               stats_type = get_filter_type(stats_type);
+               if (!stats_type)
+                       exit(-1);
+       }
+
        sun.sun_family = AF_UNIX;
        sun.sun_path[0] = 0;
        sprintf(sun.sun_path+1, "ifstat%d", getuid());
@@ -788,8 +925,13 @@ int main(int argc, char *argv[])
                snprintf(hist_name, sizeof(hist_name),
                         "%s", getenv("IFSTAT_HISTORY"));
        else
-               snprintf(hist_name, sizeof(hist_name),
-                        "%s/.ifstat.u%d", P_tmpdir, getuid());
+               if (!stats_type)
+                       snprintf(hist_name, sizeof(hist_name),
+                                "%s/.ifstat.u%d", P_tmpdir, getuid());
+               else
+                       snprintf(hist_name, sizeof(hist_name),
+                                "%s/.%s_ifstat.u%d", P_tmpdir, stats_type,
+                                getuid());
 
        if (reset_history)
                unlink(hist_name);
@@ -847,12 +989,18 @@ int main(int argc, char *argv[])
            && verify_forging(fd) == 0) {
                FILE *sfp = fdopen(fd, "r");
 
-               load_raw_table(sfp);
-               if (hist_db && source_mismatch) {
-                       fprintf(stderr, "ifstat: history is stale, ignoring it.\n");
-                       hist_db = NULL;
+               if (!sfp) {
+                       fprintf(stderr, "ifstat: fdopen failed: %s\n",
+                               strerror(errno));
+                       close(fd);
+               } else  {
+                       load_raw_table(sfp);
+                       if (hist_db && source_mismatch) {
+                               fprintf(stderr, "ifstat: history is stale, ignoring it.\n");
+                               hist_db = NULL;
+                       }
+                       fclose(sfp);
                }
-               fclose(sfp);
        } else {
                if (fd >= 0)
                        close(fd);