]> git.proxmox.com Git - mirror_iproute2.git/blobdiff - ip/tcp_metrics.c
lib: Add parse_one_of(), parse_on_off()
[mirror_iproute2.git] / ip / tcp_metrics.c
index 8972acd05fb28fd91d052c89a8ae9e90eb80c8b0..acbd745ab09ed596f01c0a9f6a11b3e00342b05a 100644 (file)
 
 static void usage(void)
 {
-       fprintf(stderr, "Usage: ip tcp_metrics/tcpmetrics { COMMAND | help }\n");
-       fprintf(stderr, "       ip tcp_metrics { show | flush } SELECTOR\n");
-       fprintf(stderr, "       ip tcp_metrics delete [ address ] ADDRESS\n");
-       fprintf(stderr, "SELECTOR := [ [ address ] PREFIX ]\n");
+       fprintf(stderr,
+               "Usage: ip tcp_metrics/tcpmetrics { COMMAND | help }\n"
+               "       ip tcp_metrics { show | flush } SELECTOR\n"
+               "       ip tcp_metrics delete [ address ] ADDRESS\n"
+               "SELECTOR := [ [ address ] PREFIX ]\n");
        exit(-1);
 }
 
 /* netlink socket */
 static struct rtnl_handle grth = { .fd = -1 };
 static int genl_family = -1;
+static const double usec_per_sec = 1000000.;
 
 #define TCPM_REQUEST(_req, _bufsiz, _cmd, _flags) \
        GENL_REQUEST(_req, _bufsiz, genl_family, 0, \
@@ -47,8 +49,8 @@ static int genl_family = -1;
 #define CMD_DEL                0x0002  /* delete, remove               */
 #define CMD_FLUSH      0x0004  /* flush                        */
 
-static struct {
-       char    *name;
+static const struct {
+       const char *name;
        int     code;
 } cmds[] = {
        {       "list",         CMD_LIST        },
@@ -59,7 +61,7 @@ static struct {
        {       "flush",        CMD_FLUSH       },
 };
 
-static char *metric_name[TCP_METRIC_MAX + 1] = {
+static const char *metric_name[TCP_METRIC_MAX + 1] = {
        [TCP_METRIC_RTT]                = "rtt",
        [TCP_METRIC_RTTVAR]             = "rttvar",
        [TCP_METRIC_SSTHRESH]           = "ssthresh",
@@ -67,8 +69,7 @@ static char *metric_name[TCP_METRIC_MAX + 1] = {
        [TCP_METRIC_REORDERING]         = "reordering",
 };
 
-static struct
-{
+static struct {
        int flushed;
        char *flushb;
        int flushp;
@@ -88,15 +89,83 @@ static int flush_update(void)
        return 0;
 }
 
-static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n,
-                      void *arg)
+static void print_tcp_metrics(struct rtattr *a)
+{
+       struct rtattr *m[TCP_METRIC_MAX + 1 + 1];
+       unsigned long rtt = 0, rttvar = 0;
+       int i;
+
+       parse_rtattr_nested(m, TCP_METRIC_MAX + 1, a);
+
+       for (i = 0; i < TCP_METRIC_MAX + 1; i++) {
+               const char *name;
+               __u32 val;
+               SPRINT_BUF(b1);
+
+               a = m[i + 1];
+               if (!a)
+                       continue;
+
+               val = rta_getattr_u32(a);
+
+               switch (i) {
+               case TCP_METRIC_RTT:
+                       if (!rtt)
+                               rtt = (val * 1000UL) >> 3;
+                       continue;
+               case TCP_METRIC_RTTVAR:
+                       if (!rttvar)
+                               rttvar = (val * 1000UL) >> 2;
+                       continue;
+               case TCP_METRIC_RTT_US:
+                       rtt = val >> 3;
+                       continue;
+
+               case TCP_METRIC_RTTVAR_US:
+                       rttvar = val >> 2;
+                       continue;
+
+               case TCP_METRIC_SSTHRESH:
+               case TCP_METRIC_CWND:
+               case TCP_METRIC_REORDERING:
+                       name = metric_name[i];
+                       break;
+
+               default:
+                       snprintf(b1, sizeof(b1),
+                                " metric_%d ", i);
+                       name = b1;
+               }
+
+
+               print_uint(PRINT_JSON, name, NULL, val);
+               print_string(PRINT_FP, NULL, " %s ", name);
+               print_uint(PRINT_FP, NULL, "%u", val);
+       }
+
+       if (rtt) {
+               print_float(PRINT_JSON, "rtt", NULL,
+                           (double)rtt / usec_per_sec);
+               print_u64(PRINT_FP, NULL,
+                          " rtt %luus", rtt);
+       }
+       if (rttvar) {
+               print_float(PRINT_JSON, "rttvar", NULL,
+                           (double) rttvar / usec_per_sec);
+               print_u64(PRINT_FP, NULL,
+                          " rttvar %luus", rttvar);
+       }
+}
+
+static int process_msg(struct nlmsghdr *n, void *arg)
 {
        FILE *fp = (FILE *) arg;
        struct genlmsghdr *ghdr;
        struct rtattr *attrs[TCP_METRICS_ATTR_MAX + 1], *a;
+       const char *h;
        int len = n->nlmsg_len;
        inet_prefix daddr, saddr;
-       int family, i, atype, stype, dlen = 0, slen = 0;
+       int atype, stype;
 
        if (n->nlmsg_type != genl_family)
                return -1;
@@ -116,61 +185,61 @@ static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n,
                if (f.daddr.family && f.daddr.family != AF_INET)
                        return 0;
                a = attrs[TCP_METRICS_ATTR_ADDR_IPV4];
-               memcpy(&daddr.data, RTA_DATA(a), 4);
-               daddr.bytelen = 4;
-               family = AF_INET;
+               daddr.family = AF_INET;
                atype = TCP_METRICS_ATTR_ADDR_IPV4;
-               dlen = RTA_PAYLOAD(a);
        } else if (attrs[TCP_METRICS_ATTR_ADDR_IPV6]) {
                if (f.daddr.family && f.daddr.family != AF_INET6)
                        return 0;
                a = attrs[TCP_METRICS_ATTR_ADDR_IPV6];
-               memcpy(&daddr.data, RTA_DATA(a), 16);
-               daddr.bytelen = 16;
-               family = AF_INET6;
+               daddr.family = AF_INET6;
                atype = TCP_METRICS_ATTR_ADDR_IPV6;
-               dlen = RTA_PAYLOAD(a);
        } else {
                return 0;
        }
 
+       if (get_addr_rta(&daddr, a, daddr.family))
+               return 0;
+
+       if (f.daddr.family && f.daddr.bitlen >= 0 &&
+           inet_addr_match(&daddr, &f.daddr, f.daddr.bitlen))
+               return 0;
+
        if (attrs[TCP_METRICS_ATTR_SADDR_IPV4]) {
                if (f.saddr.family && f.saddr.family != AF_INET)
                        return 0;
                a = attrs[TCP_METRICS_ATTR_SADDR_IPV4];
-               memcpy(&saddr.data, RTA_DATA(a), 4);
-               saddr.bytelen = 4;
+               saddr.family = AF_INET;
                stype = TCP_METRICS_ATTR_SADDR_IPV4;
-               slen = RTA_PAYLOAD(a);
        } else if (attrs[TCP_METRICS_ATTR_SADDR_IPV6]) {
                if (f.saddr.family && f.saddr.family != AF_INET6)
                        return 0;
                a = attrs[TCP_METRICS_ATTR_SADDR_IPV6];
-               memcpy(&saddr.data, RTA_DATA(a), 16);
-               saddr.bytelen = 16;
+               saddr.family = AF_INET6;
                stype = TCP_METRICS_ATTR_SADDR_IPV6;
-               slen = RTA_PAYLOAD(a);
+       } else {
+               saddr.family = AF_UNSPEC;
+               stype = 0;
        }
 
-       if (f.daddr.family && f.daddr.bitlen >= 0 &&
-           inet_addr_match(&daddr, &f.daddr, f.daddr.bitlen))
-              return 0;
-       /* Only check for the source-address if the kernel supports it,
-        * meaning slen != 0.
-        */
-       if (slen && f.saddr.family && f.saddr.bitlen >= 0 &&
-           inet_addr_match(&saddr, &f.saddr, f.saddr.bitlen))
-               return 0;
+       /* Only get/check for the source-address if the kernel supports it. */
+       if (saddr.family) {
+               if (get_addr_rta(&saddr, a, saddr.family))
+                       return 0;
+
+               if (f.saddr.family && f.saddr.bitlen >= 0 &&
+                   inet_addr_match(&saddr, &f.saddr, f.saddr.bitlen))
+                       return 0;
+       }
 
        if (f.flushb) {
                struct nlmsghdr *fn;
 
                TCPM_REQUEST(req2, 128, TCP_METRICS_CMD_DEL, NLM_F_REQUEST);
 
-               addattr_l(&req2.n, sizeof(req2), atype, &daddr.data,
+               addattr_l(&req2.n, sizeof(req2), atype, daddr.data,
                          daddr.bytelen);
-               if (slen)
-                       addattr_l(&req2.n, sizeof(req2), stype, &saddr.data,
+               if (saddr.family)
+                       addattr_l(&req2.n, sizeof(req2), stype, saddr.data,
                                  saddr.bytelen);
 
                if (NLMSG_ALIGN(f.flushp) + req2.n.nlmsg_len > f.flushe) {
@@ -186,96 +255,60 @@ static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n,
                        return 0;
        }
 
+       open_json_object(NULL);
        if (f.cmd & (CMD_DEL | CMD_FLUSH))
-               fprintf(fp, "Deleted ");
+               print_bool(PRINT_ANY, "deleted", "Deleted ", true);
 
-       fprintf(fp, "%s",
-               format_host(family, dlen, &daddr.data));
+       h = format_host(daddr.family, daddr.bytelen, daddr.data);
+       print_color_string(PRINT_ANY,
+                          ifa_family_color(daddr.family),
+                          "dst", "%s", h);
 
        a = attrs[TCP_METRICS_ATTR_AGE];
        if (a) {
-               unsigned long long val = rta_getattr_u64(a);
+               __u64 val = rta_getattr_u64(a);
+               double age = val / 1000.;
 
-               fprintf(fp, " age %llu.%03llusec",
-                       val / 1000, val % 1000);
+               print_float(PRINT_ANY, "age",
+                            " age %.03fsec", age);
        }
 
        a = attrs[TCP_METRICS_ATTR_TW_TS_STAMP];
        if (a) {
                __s32 val = (__s32) rta_getattr_u32(a);
                __u32 tsval;
+               char tw_ts[64];
 
                a = attrs[TCP_METRICS_ATTR_TW_TSVAL];
                tsval = a ? rta_getattr_u32(a) : 0;
-               fprintf(fp, " tw_ts %u/%dsec ago", tsval, val);
+               snprintf(tw_ts, sizeof(tw_ts),
+                        "%u/%d", tsval, val);
+               print_string(PRINT_ANY, "tw_ts_stamp",
+                    " tw_ts %s ago", tw_ts);
        }
 
-       a = attrs[TCP_METRICS_ATTR_VALS];
-       if (a) {
-               struct rtattr *m[TCP_METRIC_MAX + 1 + 1];
-               unsigned long rtt = 0, rttvar = 0;
-
-               parse_rtattr_nested(m, TCP_METRIC_MAX + 1, a);
-
-               for (i = 0; i < TCP_METRIC_MAX + 1; i++) {
-                       unsigned long val;
-
-                       a = m[i + 1];
-                       if (!a)
-                               continue;
-                       if (i != TCP_METRIC_RTT &&
-                           i != TCP_METRIC_RTT_US &&
-                           i != TCP_METRIC_RTTVAR &&
-                           i != TCP_METRIC_RTTVAR_US) {
-                               if (metric_name[i])
-                                       fprintf(fp, " %s ", metric_name[i]);
-                               else
-                                       fprintf(fp, " metric_%d ", i);
-                       }
-                       val = rta_getattr_u32(a);
-                       switch (i) {
-                       case TCP_METRIC_RTT:
-                               if (!rtt)
-                                       rtt = (val * 1000UL) >> 3;
-                               break;
-                       case TCP_METRIC_RTTVAR:
-                               if (!rttvar)
-                                       rttvar = (val * 1000UL) >> 2;
-                               break;
-                       case TCP_METRIC_RTT_US:
-                               rtt = val >> 3;
-                               break;
-                       case TCP_METRIC_RTTVAR_US:
-                               rttvar = val >> 2;
-                               break;
-                       case TCP_METRIC_SSTHRESH:
-                       case TCP_METRIC_CWND:
-                       case TCP_METRIC_REORDERING:
-                       default:
-                               fprintf(fp, "%lu", val);
-                               break;
-                       }
-               }
-               if (rtt)
-                       fprintf(fp, " rtt %luus", rtt);
-               if (rttvar)
-                       fprintf(fp, " rttvar %luus", rttvar);
-       }
+       if (attrs[TCP_METRICS_ATTR_VALS])
+               print_tcp_metrics(attrs[TCP_METRICS_ATTR_VALS]);
 
        a = attrs[TCP_METRICS_ATTR_FOPEN_MSS];
-       if (a)
-               fprintf(fp, " fo_mss %u", rta_getattr_u16(a));
+       if (a) {
+               print_uint(PRINT_ANY, "fopen_miss", " fo_mss %u",
+                          rta_getattr_u16(a));
+       }
 
        a = attrs[TCP_METRICS_ATTR_FOPEN_SYN_DROPS];
        if (a) {
                __u16 syn_loss = rta_getattr_u16(a);
-               unsigned long long ts;
+               double ts;
 
                a = attrs[TCP_METRICS_ATTR_FOPEN_SYN_DROP_TS];
                ts = a ? rta_getattr_u64(a) : 0;
 
-               fprintf(fp, " fo_syn_drops %u/%llu.%03llusec ago",
-                       syn_loss, ts / 1000, ts % 1000);
+               print_uint(PRINT_ANY, "fopen_syn_drops",
+                          " fo_syn_drops %u", syn_loss);
+               print_float(PRINT_ANY, "fopen_syn_drop_ts",
+                            "/%.03fusec ago",
+                            ts / 1000000.);
        }
 
        a = attrs[TCP_METRICS_ATTR_FOPEN_COOKIE];
@@ -289,16 +322,21 @@ static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n,
                cookie[0] = 0;
                for (i = 0; i < max; i++)
                        sprintf(cookie + i + i, "%02x", ptr[i]);
-               fprintf(fp, " fo_cookie %s", cookie);
-       }
 
-       if (slen) {
-               fprintf(fp, " source %s",
-                       format_host(family, slen, &saddr.data));
+               print_string(PRINT_ANY, "fo_cookie",
+                            " fo_cookie %s", cookie);
        }
 
-       fprintf(fp, "\n");
+       if (saddr.family) {
+               const char *src;
+
+               src = format_host(saddr.family, saddr.bytelen, saddr.data);
+               print_string(PRINT_ANY, "source",
+                            " source %s", src);
+       }
 
+       print_string(PRINT_FP, NULL, "\n", "");
+       close_json_object();
        fflush(fp);
        return 0;
 }
@@ -306,6 +344,7 @@ static int process_msg(const struct sockaddr_nl *who, struct nlmsghdr *n,
 static int tcpm_do_cmd(int cmd, int argc, char **argv)
 {
        TCPM_REQUEST(req, 1024, TCP_METRICS_CMD_GET, NLM_F_REQUEST);
+       struct nlmsghdr *answer;
        int atype = -1, stype = -1;
        int ack;
 
@@ -457,15 +496,16 @@ static int tcpm_do_cmd(int cmd, int argc, char **argv)
        }
 
        if (ack) {
-               if (rtnl_talk(&grth, &req.n, NULL, 0) < 0)
+               if (rtnl_talk(&grth, &req.n, NULL) < 0)
                        return -2;
        } else if (atype >= 0) {
-               if (rtnl_talk(&grth, &req.n, &req.n, sizeof(req)) < 0)
+               if (rtnl_talk(&grth, &req.n, &answer) < 0)
                        return -2;
-               if (process_msg(NULL, &req.n, stdout) < 0) {
+               if (process_msg(answer, stdout) < 0) {
                        fprintf(stderr, "Dump terminated\n");
                        exit(1);
                }
+               free(answer);
        } else {
                req.n.nlmsg_seq = grth.dump = ++grth.seq;
                if (rtnl_send(&grth, &req, req.n.nlmsg_len) < 0) {
@@ -473,10 +513,12 @@ static int tcpm_do_cmd(int cmd, int argc, char **argv)
                        exit(1);
                }
 
+               new_json_obj(json);
                if (rtnl_dump_filter(&grth, process_msg, stdout) < 0) {
                        fprintf(stderr, "Dump terminated\n");
                        exit(1);
                }
+               delete_json_obj();
        }
        return 0;
 }