]> git.proxmox.com Git - mirror_iproute2.git/commitdiff
Merge branch 'master' into next
authorDavid Ahern <dsahern@gmail.com>
Tue, 9 Jul 2019 21:26:44 +0000 (14:26 -0700)
committerDavid Ahern <dsahern@gmail.com>
Tue, 9 Jul 2019 21:26:44 +0000 (14:26 -0700)
Signed-off-by: David Ahern <dsahern@gmail.com>
1  2 
ip/ipaddress.c
ip/iproute.c
misc/ss.c
tc/q_netem.c

diff --combined ip/ipaddress.c
index 3d8caf0e9b46ebcb5ee2798a4cf578a861db15b4,733f7d9d55f4883654d32cec465eeb0b50cf246a..bc8f5ba13c33a2b26469b8dd6b374e6101dd3e56
@@@ -26,7 -26,6 +26,7 @@@
  
  #include <linux/netdevice.h>
  #include <linux/if_arp.h>
 +#include <linux/if_infiniband.h>
  #include <linux/sockios.h>
  #include <linux/net_namespace.h>
  
@@@ -53,33 -52,32 +53,33 @@@ static void usage(void
        if (do_link)
                iplink_usage();
  
 -      fprintf(stderr, "Usage: ip address {add|change|replace} IFADDR dev IFNAME [ LIFETIME ]\n");
 -      fprintf(stderr, "                                                      [ CONFFLAG-LIST ]\n");
 -      fprintf(stderr, "       ip address del IFADDR dev IFNAME [mngtmpaddr]\n");
 -      fprintf(stderr, "       ip address {save|flush} [ dev IFNAME ] [ scope SCOPE-ID ]\n");
 -      fprintf(stderr, "                            [ to PREFIX ] [ FLAG-LIST ] [ label LABEL ] [up]\n");
 -      fprintf(stderr, "       ip address [ show [ dev IFNAME ] [ scope SCOPE-ID ] [ master DEVICE ]\n");
 -      fprintf(stderr, "                         [ type TYPE ] [ to PREFIX ] [ FLAG-LIST ]\n");
 -      fprintf(stderr, "                         [ label LABEL ] [up] [ vrf NAME ] ]\n");
 -      fprintf(stderr, "       ip address {showdump|restore}\n");
 -      fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n");
 -      fprintf(stderr, "          [ broadcast ADDR ] [ anycast ADDR ]\n");
 -      fprintf(stderr, "          [ label IFNAME ] [ scope SCOPE-ID ] [ metric METRIC ]\n");
 -      fprintf(stderr, "SCOPE-ID := [ host | link | global | NUMBER ]\n");
 -      fprintf(stderr, "FLAG-LIST := [ FLAG-LIST ] FLAG\n");
 -      fprintf(stderr, "FLAG  := [ permanent | dynamic | secondary | primary |\n");
 -      fprintf(stderr, "           [-]tentative | [-]deprecated | [-]dadfailed | temporary |\n");
 -      fprintf(stderr, "           CONFFLAG-LIST ]\n");
 -      fprintf(stderr, "CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n");
 -      fprintf(stderr, "CONFFLAG  := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ]\n");
 -      fprintf(stderr, "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n");
 -      fprintf(stderr, "LFT := forever | SECONDS\n");
 -      fprintf(stderr, "TYPE := { vlan | veth | vcan | vxcan | dummy | ifb | macvlan | macvtap |\n");
 -      fprintf(stderr, "          bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | lowpan |\n");
 -      fprintf(stderr, "          gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan | vti |\n");
 -      fprintf(stderr, "          nlmon | can | bond_slave | ipvlan | geneve | bridge_slave |\n");
 -      fprintf(stderr, "          hsr | macsec | netdevsim }\n");
 +      fprintf(stderr,
 +              "Usage: ip address {add|change|replace} IFADDR dev IFNAME [ LIFETIME ]\n"
 +              "                                                      [ CONFFLAG-LIST ]\n"
 +              "       ip address del IFADDR dev IFNAME [mngtmpaddr]\n"
 +              "       ip address {save|flush} [ dev IFNAME ] [ scope SCOPE-ID ]\n"
 +              "                            [ to PREFIX ] [ FLAG-LIST ] [ label LABEL ] [up]\n"
 +              "       ip address [ show [ dev IFNAME ] [ scope SCOPE-ID ] [ master DEVICE ]\n"
 +              "                         [ type TYPE ] [ to PREFIX ] [ FLAG-LIST ]\n"
 +              "                         [ label LABEL ] [up] [ vrf NAME ] ]\n"
 +              "       ip address {showdump|restore}\n"
 +              "IFADDR := PREFIX | ADDR peer PREFIX\n"
 +              "          [ broadcast ADDR ] [ anycast ADDR ]\n"
 +              "          [ label IFNAME ] [ scope SCOPE-ID ] [ metric METRIC ]\n"
 +              "SCOPE-ID := [ host | link | global | NUMBER ]\n"
 +              "FLAG-LIST := [ FLAG-LIST ] FLAG\n"
 +              "FLAG  := [ permanent | dynamic | secondary | primary |\n"
 +              "           [-]tentative | [-]deprecated | [-]dadfailed | temporary |\n"
 +              "           CONFFLAG-LIST ]\n"
 +              "CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n"
 +              "CONFFLAG  := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ]\n"
 +              "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n"
 +              "LFT := forever | SECONDS\n"
 +              "TYPE := { vlan | veth | vcan | vxcan | dummy | ifb | macvlan | macvtap |\n"
 +              "          bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan | lowpan |\n"
 +              "          gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan | vti |\n"
 +              "          nlmon | can | bond_slave | ipvlan | geneve | bridge_slave |\n"
 +              "          hsr | macsec | netdevsim }\n");
  
        exit(-1);
  }
@@@ -351,10 -349,9 +351,10 @@@ static void print_af_spec(FILE *fp, str
  
  static void print_vf_stats64(FILE *fp, struct rtattr *vfstats);
  
 -static void print_vfinfo(FILE *fp, struct rtattr *vfinfo)
 +static void print_vfinfo(FILE *fp, struct ifinfomsg *ifi, struct rtattr *vfinfo)
  {
        struct ifla_vf_mac *vf_mac;
 +      struct ifla_vf_broadcast *vf_broadcast;
        struct ifla_vf_tx_rate *vf_tx_rate;
        struct rtattr *vf[IFLA_VF_MAX + 1] = {};
  
        parse_rtattr_nested(vf, IFLA_VF_MAX, vfinfo);
  
        vf_mac = RTA_DATA(vf[IFLA_VF_MAC]);
 +      vf_broadcast = RTA_DATA(vf[IFLA_VF_BROADCAST]);
        vf_tx_rate = RTA_DATA(vf[IFLA_VF_TX_RATE]);
  
        print_string(PRINT_FP, NULL, "%s    ", _SL_);
        print_int(PRINT_ANY, "vf", "vf %d ", vf_mac->vf);
 -      print_string(PRINT_ANY, "mac", "MAC %s",
 -                   ll_addr_n2a((unsigned char *) &vf_mac->mac,
 -                               ETH_ALEN, 0, b1, sizeof(b1)));
 +
 +      print_string(PRINT_ANY,
 +                   "link_type",
 +                   "    link/%s ",
 +                   ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
 +
 +      print_color_string(PRINT_ANY, COLOR_MAC,
 +                         "address", "%s",
 +                         ll_addr_n2a((unsigned char *) &vf_mac->mac,
 +                                     ifi->ifi_type == ARPHRD_ETHER ?
 +                                     ETH_ALEN : INFINIBAND_ALEN,
 +                                     ifi->ifi_type,
 +                                     b1, sizeof(b1)));
 +
 +      if (vf[IFLA_VF_BROADCAST]) {
 +              if (ifi->ifi_flags&IFF_POINTOPOINT) {
 +                      print_string(PRINT_FP, NULL, " peer ", NULL);
 +                      print_bool(PRINT_JSON,
 +                                 "link_pointtopoint", NULL, true);
 +              } else
 +                      print_string(PRINT_FP, NULL, " brd ", NULL);
 +
 +              print_color_string(PRINT_ANY, COLOR_MAC,
 +                                 "broadcast", "%s",
 +                                 ll_addr_n2a((unsigned char *) &vf_broadcast->broadcast,
 +                                             ifi->ifi_type == ARPHRD_ETHER ?
 +                                             ETH_ALEN : INFINIBAND_ALEN,
 +                                             ifi->ifi_type,
 +                                             b1, sizeof(b1)));
 +      }
  
        if (vf[IFLA_VF_VLAN_LIST]) {
                struct rtattr *i, *vfvlanlist = vf[IFLA_VF_VLAN_LIST];
@@@ -1133,7 -1102,7 +1133,7 @@@ int print_linkinfo(struct nlmsghdr *n, 
                open_json_array(PRINT_JSON, "vfinfo_list");
                for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
                        open_json_object(NULL);
 -                      print_vfinfo(fp, i);
 +                      print_vfinfo(fp, ifi, i);
                        close_json_object();
                }
                close_json_array(PRINT_JSON, NULL);
@@@ -2278,11 -2247,20 +2278,20 @@@ static int ipaddr_modify(int cmd, int f
                        if (set_lifetime(&preferred_lft, *argv))
                                invarg("preferred_lft value", *argv);
                } else if (strcmp(*argv, "home") == 0) {
-                       ifa_flags |= IFA_F_HOMEADDRESS;
+                       if (req.ifa.ifa_family == AF_INET6)
+                               ifa_flags |= IFA_F_HOMEADDRESS;
+                       else
+                               fprintf(stderr, "Warning: home option can be set only for IPv6 addresses\n");
                } else if (strcmp(*argv, "nodad") == 0) {
-                       ifa_flags |= IFA_F_NODAD;
+                       if (req.ifa.ifa_family == AF_INET6)
+                               ifa_flags |= IFA_F_NODAD;
+                       else
+                               fprintf(stderr, "Warning: nodad option can be set only for IPv6 addresses\n");
                } else if (strcmp(*argv, "mngtmpaddr") == 0) {
-                       ifa_flags |= IFA_F_MANAGETEMPADDR;
+                       if (req.ifa.ifa_family == AF_INET6)
+                               ifa_flags |= IFA_F_MANAGETEMPADDR;
+                       else
+                               fprintf(stderr, "Warning: mngtmpaddr option can be set only for IPv6 addresses\n");
                } else if (strcmp(*argv, "noprefixroute") == 0) {
                        ifa_flags |= IFA_F_NOPREFIXROUTE;
                } else if (strcmp(*argv, "autojoin") == 0) {
diff --combined ip/iproute.c
index 6b8142250349f71764a83612ec1d29ee88ef5b05,1669e0138259e7afd4c2b142aadc0c572f018f3f..83e99fa2b3f9c29104453f1cf933183b77024b93
@@@ -80,7 -80,7 +80,7 @@@ static void usage(void
                "             [ table TABLE_ID ] [ proto RTPROTO ]\n"
                "             [ scope SCOPE ] [ metric METRIC ]\n"
                "             [ ttl-propagate { enabled | disabled } ]\n"
 -              "INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n"
 +              "INFO_SPEC := { NH | nhid ID } OPTIONS FLAGS [ nexthop NH ]...\n"
                "NH := [ encap ENCAPTYPE ENCAPHDR ] [ via [ FAMILY ] ADDRESS ]\n"
                "           [ dev STRING ] [ weight NUMBER ] NHFLAGS\n"
                "FAMILY := [ inet | inet6 | mpls | bridge | link ]\n"
@@@ -349,7 -349,7 +349,7 @@@ static void print_rtax_features(FILE *f
                            "features", "%#llx ", of);
  }
  
 -static void print_rt_flags(FILE *fp, unsigned int flags)
 +void print_rt_flags(FILE *fp, unsigned int flags)
  {
        open_json_array(PRINT_JSON,
                        is_json_context() ?  "flags" : "");
@@@ -394,7 -394,8 +394,7 @@@ static void print_rt_pref(FILE *fp, uns
        }
  }
  
 -static void print_rta_if(FILE *fp, const struct rtattr *rta,
 -                      const char *prefix)
 +void print_rta_if(FILE *fp, const struct rtattr *rta, const char *prefix)
  {
        const char *ifname = ll_index_to_name(rta_getattr_u32(rta));
  
@@@ -531,16 -532,17 +531,16 @@@ static void print_rta_newdst(FILE *fp, 
        }
  }
  
 -static void print_rta_gateway(FILE *fp, const struct rtmsg *r,
 -                            const struct rtattr *rta)
 +void print_rta_gateway(FILE *fp, unsigned char family, const struct rtattr *rta)
  {
 -      const char *gateway = format_host_rta(r->rtm_family, rta);
 +      const char *gateway = format_host_rta(family, rta);
  
        if (is_json_context())
                print_string(PRINT_JSON, "gateway", NULL, gateway);
        else {
                fprintf(fp, "via ");
                print_color_string(PRINT_FP,
 -                                 ifa_family_color(r->rtm_family),
 +                                 ifa_family_color(family),
                                   NULL, "%s ", gateway);
        }
  }
@@@ -677,8 -679,7 +677,8 @@@ static void print_rta_multipath(FILE *f
                        if (tb[RTA_NEWDST])
                                print_rta_newdst(fp, r, tb[RTA_NEWDST]);
                        if (tb[RTA_GATEWAY])
 -                              print_rta_gateway(fp, r, tb[RTA_GATEWAY]);
 +                              print_rta_gateway(fp, r->rtm_family,
 +                                                tb[RTA_GATEWAY]);
                        if (tb[RTA_VIA])
                                print_rta_via(fp, tb[RTA_VIA]);
                        if (tb[RTA_FLOW])
@@@ -809,10 -810,6 +809,10 @@@ int print_route(struct nlmsghdr *n, voi
                print_string(PRINT_ANY, "src", "from %s ", b1);
        }
  
 +      if (tb[RTA_NH_ID])
 +              print_uint(PRINT_ANY, "nhid", "nhid %u ",
 +                         rta_getattr_u32(tb[RTA_NH_ID]));
 +
        if (tb[RTA_NEWDST])
                print_rta_newdst(fp, r, tb[RTA_NEWDST]);
  
        }
  
        if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len)
 -              print_rta_gateway(fp, r, tb[RTA_GATEWAY]);
 +              print_rta_gateway(fp, r->rtm_family, tb[RTA_GATEWAY]);
  
        if (tb[RTA_VIA])
                print_rta_via(fp, tb[RTA_VIA]);
@@@ -1000,8 -997,7 +1000,8 @@@ static int parse_one_nh(struct nlmsghd
                } else if (strcmp(*argv, "encap") == 0) {
                        int old_len = rta->rta_len;
  
 -                      if (lwt_parse_encap(rta, len, &argc, &argv))
 +                      if (lwt_parse_encap(rta, len, &argc, &argv,
 +                                          RTA_ENCAP, RTA_ENCAP_TYPE))
                                return -1;
                        rtnh->rtnh_len += rta->rta_len - old_len;
                } else if (strcmp(*argv, "as") == 0) {
@@@ -1084,7 -1080,6 +1084,7 @@@ static int iproute_modify(int cmd, unsi
        int table_ok = 0;
        int raw = 0;
        int type_ok = 0;
 +      __u32 nhid = 0;
  
        if (cmd != RTM_DELROUTE) {
                req.r.rtm_protocol = RTPROT_BOOT;
                } else if (strcmp(*argv, "nexthop") == 0) {
                        nhs_ok = 1;
                        break;
 +              } else if (!strcmp(*argv, "nhid")) {
 +                      NEXT_ARG();
 +                      if (get_u32(&nhid, *argv, 0))
 +                              invarg("\"id\" value is invalid\n", *argv);
 +                      addattr32(&req.n, sizeof(req), RTA_NH_ID, nhid);
                } else if (matches(*argv, "protocol") == 0) {
                        __u32 prot;
  
                        rta->rta_type = RTA_ENCAP;
                        rta->rta_len = RTA_LENGTH(0);
  
 -                      lwt_parse_encap(rta, sizeof(buf), &argc, &argv);
 +                      lwt_parse_encap(rta, sizeof(buf), &argc, &argv,
 +                                      RTA_ENCAP, RTA_ENCAP_TYPE);
  
                        if (rta->rta_len > RTA_LENGTH(0))
                                addraw_l(&req.n, 1024
                         req.r.rtm_type == RTN_UNSPEC) {
                        if (cmd == RTM_DELROUTE)
                                req.r.rtm_scope = RT_SCOPE_NOWHERE;
 -                      else if (!gw_ok && !nhs_ok)
 +                      else if (!gw_ok && !nhs_ok && !nhid)
                                req.r.rtm_scope = RT_SCOPE_LINK;
                }
        }
@@@ -1613,6 -1602,30 +1613,30 @@@ static int save_route_prep(void
        return 0;
  }
  
+ static int iproute_dump_filter(struct nlmsghdr *nlh, int reqlen)
+ {
+       struct rtmsg *rtm = NLMSG_DATA(nlh);
+       int err;
+       rtm->rtm_protocol = filter.protocol;
+       if (filter.cloned)
+               rtm->rtm_flags |= RTM_F_CLONED;
+       if (filter.tb) {
+               err = addattr32(nlh, reqlen, RTA_TABLE, filter.tb);
+               if (err)
+                       return err;
+       }
+       if (filter.oif) {
+               err = addattr32(nlh, reqlen, RTA_OIF, filter.oif);
+               if (err)
+                       return err;
+       }
+       return 0;
+ }
  static int iproute_flush(int family, rtnl_filter_t filter_fn)
  {
        time_t start = time(0);
        filter.flushe = sizeof(flushb);
  
        for (;;) {
-               if (rtnl_routedump_req(&rth, family, NULL) < 0) {
+               if (rtnl_routedump_req(&rth, family, iproute_dump_filter) < 0) {
                        perror("Cannot send dump request");
                        return -2;
                }
        }
  }
  
- static int iproute_dump_filter(struct nlmsghdr *nlh, int reqlen)
- {
-       struct rtmsg *rtm = NLMSG_DATA(nlh);
-       int err;
-       rtm->rtm_protocol = filter.protocol;
-       if (filter.cloned)
-               rtm->rtm_flags |= RTM_F_CLONED;
-       if (filter.tb) {
-               err = addattr32(nlh, reqlen, RTA_TABLE, filter.tb);
-               if (err)
-                       return err;
-       }
-       if (filter.oif) {
-               err = addattr32(nlh, reqlen, RTA_OIF, filter.oif);
-               if (err)
-                       return err;
-       }
-       return 0;
- }
  static int iproute_list_flush_or_save(int argc, char **argv, int action)
  {
        int dump_family = preferred_family;
diff --combined misc/ss.c
index e01ebf4d89a62fb62d642de2bbc4050518316754,3d9d1d8f7da0daebab56032cee38864cd1fb92ba..e1c665a51daaef3c6d0e6ad7b641dcb1d64add1e
+++ b/misc/ss.c
@@@ -106,6 -106,7 +106,6 @@@ static int security_get_initial_context
  }
  #endif
  
 -static int resolve_services = 1;
  int preferred_family = AF_UNSPEC;
  static int show_options;
  int show_details;
@@@ -120,7 -121,6 +120,7 @@@ static int follow_events
  static int sctp_ino;
  static int show_tipcinfo;
  static int show_tos;
 +int numeric;
  int oneline;
  
  enum col_id {
@@@ -1553,7 -1553,7 +1553,7 @@@ static const char *resolve_service(int 
                return buf;
        }
  
 -      if (!resolve_services)
 +      if (numeric)
                goto do_numeric;
  
        if (dg_proto == RAW_PROTO)
@@@ -2361,7 -2361,9 +2361,9 @@@ static int proc_inet_split_line(char *l
  
  static char *sprint_bw(char *buf, double bw)
  {
-       if (bw > 1000000.)
+       if (!resolve_services)
+               sprintf(buf, "%.0f", bw);
+       else if (bw > 1000000.)
                sprintf(buf, "%.1fM", bw / 1000000.);
        else if (bw > 1000.)
                sprintf(buf, "%.1fK", bw / 1000.);
@@@ -4296,11 -4298,14 +4298,11 @@@ static int netlink_show_one(struct filt
  
        sock_state_print(&st);
  
 -      if (resolve_services)
 -              prot_name = nl_proto_n2a(prot, prot_buf, sizeof(prot_buf));
 -      else
 -              prot_name = int_to_str(prot, prot_buf);
 +      prot_name = nl_proto_n2a(prot, prot_buf, sizeof(prot_buf));
  
        if (pid == -1) {
                procname[0] = '*';
 -      } else if (resolve_services) {
 +      } else if (!numeric) {
                int done = 0;
  
                if (!pid) {
@@@ -5047,7 -5052,7 +5049,7 @@@ int main(int argc, char *argv[]
                                 long_opts, NULL)) != EOF) {
                switch (ch) {
                case 'n':
 -                      resolve_services = 0;
 +                      numeric = 1;
                        break;
                case 'r':
                        resolve_hosts = 1;
        filter_states_set(&current_filter, state_filter);
        filter_merge_defaults(&current_filter);
  
 -      if (resolve_services && resolve_hosts &&
 +      if (!numeric && resolve_hosts &&
            (current_filter.dbs & (UNIX_DBM|INET_L4_DBM)))
                init_service_resolver();
  
diff --combined tc/q_netem.c
index b85938f4941b61d443158735b112d57ab549eac9,d1cd17f8a8a7e16de2edc77a05c483a845fda4ab..6d748f68031e79782d38febb36708a16703d8dd5
  static void explain(void)
  {
        fprintf(stderr,
 -"Usage: ... netem [ limit PACKETS ]\n" \
 -"                 [ delay TIME [ JITTER [CORRELATION]]]\n" \
 -"                 [ distribution {uniform|normal|pareto|paretonormal} ]\n" \
 -"                 [ corrupt PERCENT [CORRELATION]]\n" \
 -"                 [ duplicate PERCENT [CORRELATION]]\n" \
 -"                 [ loss random PERCENT [CORRELATION]]\n" \
 -"                 [ loss state P13 [P31 [P32 [P23 P14]]]\n" \
 -"                 [ loss gemodel PERCENT [R [1-H [1-K]]]\n" \
 -"                 [ ecn ]\n" \
 -"                 [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n" \
 -"                 [ rate RATE [PACKETOVERHEAD] [CELLSIZE] [CELLOVERHEAD]]\n" \
 -"                 [ slot MIN_DELAY [MAX_DELAY] [packets MAX_PACKETS]" \
 -" [bytes MAX_BYTES]]\n" \
 -"                 [ slot distribution" \
 -" {uniform|normal|pareto|paretonormal|custom} DELAY JITTER" \
 -" [packets MAX_PACKETS] [bytes MAX_BYTES]]\n");
 +              "Usage: ... netem       [ limit PACKETS ]\n" \
 +              "                       [ delay TIME [ JITTER [CORRELATION]]]\n" \
 +              "                       [ distribution {uniform|normal|pareto|paretonormal} ]\n" \
 +              "                       [ corrupt PERCENT [CORRELATION]]\n" \
 +              "                       [ duplicate PERCENT [CORRELATION]]\n" \
 +              "                       [ loss random PERCENT [CORRELATION]]\n" \
 +              "                       [ loss state P13 [P31 [P32 [P23 P14]]]\n" \
 +              "                       [ loss gemodel PERCENT [R [1-H [1-K]]]\n" \
 +              "                       [ ecn ]\n" \
 +              "                       [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n" \
 +              "                       [ rate RATE [PACKETOVERHEAD] [CELLSIZE] [CELLOVERHEAD]]\n" \
 +              "                       [ slot MIN_DELAY [MAX_DELAY] [packets MAX_PACKETS]" \
 +              " [bytes MAX_BYTES]]\n" \
 +              "               [ slot distribution" \
 +              " {uniform|normal|pareto|paretonormal|custom} DELAY JITTER" \
 +              " [packets MAX_PACKETS] [bytes MAX_BYTES]]\n");
  }
  
  static void explain1(const char *arg)
   */
  #define MAX_DIST      (16*1024)
  
 +/* Print values only if they are non-zero */
 +static void __print_int_opt(const char *label_json, const char *label_fp,
 +                          int val)
 +{
 +      print_int(PRINT_ANY, label_json, val ? label_fp : "", val);
 +}
 +#define PRINT_INT_OPT(label, val)                     \
 +      __print_int_opt(label, " " label " %d", (val))
 +
 +/* Time print prints normally with varying units, but for JSON prints
 + * in seconds (1ms vs 0.001).
 + */
 +static void __print_time64(const char *label_json, const char *label_fp,
 +                         __u64 val)
 +{
 +      SPRINT_BUF(b1);
 +
 +      print_string(PRINT_FP, NULL, label_fp, sprint_time64(val, b1));
 +      print_float(PRINT_JSON, label_json, NULL, val / 1000000000.);
 +}
 +#define __PRINT_TIME64(label_json, label_fp, val)     \
 +      __print_time64(label_json, label_fp " %s", (val))
 +#define PRINT_TIME64(label, val) __PRINT_TIME64(label, " " label, (val))
 +
 +/* Percent print prints normally in percentage points, but for JSON prints
 + * an absolute value (1% vs 0.01).
 + */
 +static void __print_percent(const char *label_json, const char *label_fp,
 +                          __u32 per)
 +{
 +      print_float(PRINT_FP, NULL, label_fp, (100. * per) / UINT32_MAX);
 +      print_float(PRINT_JSON, label_json, NULL, (1. * per) / UINT32_MAX);
 +}
 +#define __PRINT_PERCENT(label_json, label_fp, per)            \
 +      __print_percent(label_json, label_fp " %g%%", (per))
 +#define PRINT_PERCENT(label, per) __PRINT_PERCENT(label, " " label, (per))
 +
  /* scaled value used to percent of maximum. */
  static void set_percent(__u32 *percent, double per)
  {
@@@ -112,14 -75,15 +112,14 @@@ static int get_percent(__u32 *percent, 
        return 0;
  }
  
 -static void print_percent(char *buf, int len, __u32 per)
 -{
 -      snprintf(buf, len, "%g%%", (100. * per) / UINT32_MAX);
 -}
 -
 -static char *sprint_percent(__u32 per, char *buf)
 +static void print_corr(bool present, __u32 value)
  {
 -      print_percent(buf, SPRINT_BSIZE-1, per);
 -      return buf;
 +      if (!is_json_context()) {
 +              if (present)
 +                      __PRINT_PERCENT("", "", value);
 +      } else {
 +              PRINT_PERCENT("correlation", value);
 +      }
  }
  
  /*
@@@ -320,14 -284,17 +320,17 @@@ static int netem_parse_opt(struct qdisc
                                }
  
                        } else if (!strcmp(*argv, "gemodel")) {
+                               double p;
                                NEXT_ARG();
-                               if (get_percent(&gemodel.p, *argv)) {
+                               if (parse_percent(&p, *argv)) {
                                        explain1("loss gemodel p");
                                        return -1;
                                }
+                               set_percent(&gemodel.p, p);
  
                                /* set defaults */
-                               set_percent(&gemodel.r, 1.);
+                               set_percent(&gemodel.r, 1. - p);
                                set_percent(&gemodel.h, 0);
                                set_percent(&gemodel.k1, 0);
                                loss_type = NETEM_LOSS_GE;
@@@ -723,109 -690,97 +726,109 @@@ static int netem_print_opt(struct qdisc
                }
        }
  
 -      fprintf(f, "limit %d", qopt.limit);
 +      print_uint(PRINT_ANY, "limit", "limit %d", qopt.limit);
  
        if (qopt.latency) {
 -              fprintf(f, " delay %s", sprint_ticks(qopt.latency, b1));
 -
 -              if (qopt.jitter) {
 -                      fprintf(f, "  %s", sprint_ticks(qopt.jitter, b1));
 -                      if (cor && cor->delay_corr)
 -                              fprintf(f, " %s", sprint_percent(cor->delay_corr, b1));
 +              open_json_object("delay");
 +              if (!is_json_context()) {
 +                      print_string(PRINT_FP, NULL, " delay %s",
 +                                   sprint_ticks(qopt.latency, b1));
 +
 +                      if (qopt.jitter)
 +                              print_string(PRINT_FP, NULL, "  %s",
 +                                           sprint_ticks(qopt.jitter, b1));
 +              } else {
 +                      print_float(PRINT_JSON, "delay", NULL,
 +                                  tc_core_tick2time(qopt.latency) /
 +                                  1000000.);
 +                      print_float(PRINT_JSON, "jitter", NULL,
 +                                  tc_core_tick2time(qopt.jitter) /
 +                                  1000000.);
                }
 +              print_corr(qopt.jitter && cor && cor->delay_corr,
 +                         cor ? cor->delay_corr : 0);
 +              close_json_object();
        }
  
        if (qopt.loss) {
 -              fprintf(f, " loss %s", sprint_percent(qopt.loss, b1));
 -              if (cor && cor->loss_corr)
 -                      fprintf(f, " %s", sprint_percent(cor->loss_corr, b1));
 +              open_json_object("loss-random");
 +              PRINT_PERCENT("loss", qopt.loss);
 +              print_corr(cor && cor->loss_corr, cor ? cor->loss_corr : 0);
 +              close_json_object();
        }
  
        if (gimodel) {
 -              fprintf(f, " loss state p13 %s", sprint_percent(gimodel->p13, b1));
 -              fprintf(f, " p31 %s", sprint_percent(gimodel->p31, b1));
 -              fprintf(f, " p32 %s", sprint_percent(gimodel->p32, b1));
 -              fprintf(f, " p23 %s", sprint_percent(gimodel->p23, b1));
 -              fprintf(f, " p14 %s", sprint_percent(gimodel->p14, b1));
 +              open_json_object("loss-state");
 +              __PRINT_PERCENT("p13", " loss state p13", gimodel->p13);
 +              PRINT_PERCENT("p31", gimodel->p31);
 +              PRINT_PERCENT("p32", gimodel->p32);
 +              PRINT_PERCENT("p23", gimodel->p23);
 +              PRINT_PERCENT("p14", gimodel->p14);
 +              close_json_object();
        }
  
        if (gemodel) {
 -              fprintf(f, " loss gemodel p %s",
 -                      sprint_percent(gemodel->p, b1));
 -              fprintf(f, " r %s", sprint_percent(gemodel->r, b1));
 -              fprintf(f, " 1-h %s", sprint_percent(UINT32_MAX -
 -                                                   gemodel->h, b1));
 -              fprintf(f, " 1-k %s", sprint_percent(gemodel->k1, b1));
 +              open_json_object("loss-gemodel");
 +              __PRINT_PERCENT("p", " loss gemodel p", gemodel->p);
 +              PRINT_PERCENT("r", gemodel->r);
 +              PRINT_PERCENT("1-h", UINT32_MAX - gemodel->h);
 +              PRINT_PERCENT("1-k", gemodel->k1);
 +              close_json_object();
        }
  
        if (qopt.duplicate) {
 -              fprintf(f, " duplicate %s",
 -                      sprint_percent(qopt.duplicate, b1));
 -              if (cor && cor->dup_corr)
 -                      fprintf(f, " %s", sprint_percent(cor->dup_corr, b1));
 +              open_json_object("duplicate");
 +              PRINT_PERCENT("duplicate", qopt.duplicate);
 +              print_corr(cor && cor->dup_corr, cor ? cor->dup_corr : 0);
 +              close_json_object();
        }
  
        if (reorder && reorder->probability) {
 -              fprintf(f, " reorder %s",
 -                      sprint_percent(reorder->probability, b1));
 -              if (reorder->correlation)
 -                      fprintf(f, " %s",
 -                              sprint_percent(reorder->correlation, b1));
 +              open_json_object("reorder");
 +              PRINT_PERCENT("reorder", reorder->probability);
 +              print_corr(reorder->correlation, reorder->correlation);
 +              close_json_object();
        }
  
        if (corrupt && corrupt->probability) {
 -              fprintf(f, " corrupt %s",
 -                      sprint_percent(corrupt->probability, b1));
 -              if (corrupt->correlation)
 -                      fprintf(f, " %s",
 -                              sprint_percent(corrupt->correlation, b1));
 +              open_json_object("corrupt");
 +              PRINT_PERCENT("corrupt", corrupt->probability);
 +              print_corr(corrupt->correlation, corrupt->correlation);
 +              close_json_object();
        }
  
        if (rate && rate->rate) {
 -              if (rate64)
 -                      fprintf(f, " rate %s", sprint_rate(rate64, b1));
 -              else
 -                      fprintf(f, " rate %s", sprint_rate(rate->rate, b1));
 -              if (rate->packet_overhead)
 -                      fprintf(f, " packetoverhead %d", rate->packet_overhead);
 -              if (rate->cell_size)
 -                      fprintf(f, " cellsize %u", rate->cell_size);
 -              if (rate->cell_overhead)
 -                      fprintf(f, " celloverhead %d", rate->cell_overhead);
 +              open_json_object("rate");
 +              rate64 = rate64 ? : rate->rate;
 +              print_string(PRINT_FP, NULL, " rate %s",
 +                           sprint_rate(rate64, b1));
 +              print_lluint(PRINT_JSON, "rate", NULL, rate64);
 +              PRINT_INT_OPT("packetoverhead", rate->packet_overhead);
 +              print_uint(PRINT_ANY, "cellsize",
 +                         rate->cell_size ? " cellsize %u" : "",
 +                         rate->cell_size);
 +              PRINT_INT_OPT("celloverhead", rate->cell_overhead);
 +              close_json_object();
        }
  
        if (slot) {
 +              open_json_object("slot");
                if (slot->dist_jitter > 0) {
 -                  fprintf(f, " slot distribution %s", sprint_time64(slot->dist_delay, b1));
 -                  fprintf(f, " %s", sprint_time64(slot->dist_jitter, b1));
 +                      __PRINT_TIME64("distribution", " slot distribution",
 +                                     slot->dist_delay);
 +                      __PRINT_TIME64("jitter", "", slot->dist_jitter);
                } else {
 -                  fprintf(f, " slot %s", sprint_time64(slot->min_delay, b1));
 -                  fprintf(f, " %s", sprint_time64(slot->max_delay, b1));
 +                      __PRINT_TIME64("min-delay", " slot", slot->min_delay);
 +                      __PRINT_TIME64("max-delay", "", slot->max_delay);
                }
 -              if (slot->max_packets)
 -                      fprintf(f, " packets %d", slot->max_packets);
 -              if (slot->max_bytes)
 -                      fprintf(f, " bytes %d", slot->max_bytes);
 +              PRINT_INT_OPT("packets", slot->max_packets);
 +              PRINT_INT_OPT("bytes", slot->max_bytes);
 +              close_json_object();
        }
  
 -      if (ecn)
 -              fprintf(f, " ecn ");
 -
 -      if (qopt.gap)
 -              fprintf(f, " gap %lu", (unsigned long)qopt.gap);
 -
 +      print_bool(PRINT_ANY, "ecn", ecn ? " ecn " : "", ecn);
 +      print_luint(PRINT_ANY, "gap", qopt.gap ? " gap %lu" : "",
 +                  (unsigned long)qopt.gap);
  
        return 0;
  }