]> git.proxmox.com Git - mirror_iproute2.git/commitdiff
Merge branch 'master' into next
authorDavid Ahern <dsahern@gmail.com>
Sun, 18 Aug 2019 18:40:30 +0000 (11:40 -0700)
committerDavid Ahern <dsahern@gmail.com>
Sun, 18 Aug 2019 18:40:30 +0000 (11:40 -0700)
Signed-off-by: David Ahern <dsahern@gmail.com>
17 files changed:
include/uapi/linux/tc_act/tc_ct.h [new file with mode: 0644]
ip/ip6tunnel.c
ip/iptunnel.c
ip/tunnel.c
ip/tunnel.h
man/man8/tc-etf.8
man/man8/tc-flower.8
man/man8/tc-taprio.8
rdma/res.c
tc/Makefile
tc/f_flower.c
tc/m_action.c
tc/m_ct.c [new file with mode: 0644]
tc/q_etf.c
tc/q_taprio.c
tc/tc_util.c
tc/tc_util.h

diff --git a/include/uapi/linux/tc_act/tc_ct.h b/include/uapi/linux/tc_act/tc_ct.h
new file mode 100644 (file)
index 0000000..5fb1d7a
--- /dev/null
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __UAPI_TC_CT_H
+#define __UAPI_TC_CT_H
+
+#include <linux/types.h>
+#include <linux/pkt_cls.h>
+
+enum {
+       TCA_CT_UNSPEC,
+       TCA_CT_PARMS,
+       TCA_CT_TM,
+       TCA_CT_ACTION,          /* u16 */
+       TCA_CT_ZONE,            /* u16 */
+       TCA_CT_MARK,            /* u32 */
+       TCA_CT_MARK_MASK,       /* u32 */
+       TCA_CT_LABELS,          /* u128 */
+       TCA_CT_LABELS_MASK,     /* u128 */
+       TCA_CT_NAT_IPV4_MIN,    /* be32 */
+       TCA_CT_NAT_IPV4_MAX,    /* be32 */
+       TCA_CT_NAT_IPV6_MIN,    /* struct in6_addr */
+       TCA_CT_NAT_IPV6_MAX,    /* struct in6_addr */
+       TCA_CT_NAT_PORT_MIN,    /* be16 */
+       TCA_CT_NAT_PORT_MAX,    /* be16 */
+       TCA_CT_PAD,
+       __TCA_CT_MAX
+};
+
+#define TCA_CT_MAX (__TCA_CT_MAX - 1)
+
+#define TCA_CT_ACT_COMMIT      (1 << 0)
+#define TCA_CT_ACT_FORCE       (1 << 1)
+#define TCA_CT_ACT_CLEAR       (1 << 2)
+#define TCA_CT_ACT_NAT         (1 << 3)
+#define TCA_CT_ACT_NAT_SRC     (1 << 4)
+#define TCA_CT_ACT_NAT_DST     (1 << 5)
+
+struct tc_ct {
+       tc_gen;
+};
+
+#endif /* __UAPI_TC_CT_H */
index 42535fcfd35fa3be88f4d02d308dc08437fe8edd..5399f91d3923a3356b058fd06d911166ac1974f7 100644 (file)
@@ -71,75 +71,76 @@ static void usage(void)
 static void print_tunnel(const void *t)
 {
        const struct ip6_tnl_parm2 *p = t;
-       char s1[1024];
-       char s2[1024];
+       SPRINT_BUF(b1);
 
        /* Do not use format_host() for local addr,
         * symbolic name will not be useful.
         */
-       printf("%s: %s/ipv6 remote %s local %s",
-              p->name,
-              tnl_strproto(p->proto),
-              format_host_r(AF_INET6, 16, &p->raddr, s1, sizeof(s1)),
-              rt_addr_n2a_r(AF_INET6, 16, &p->laddr, s2, sizeof(s2)));
+       open_json_object(NULL);
+       print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", "%s: ", p->name);
+       snprintf(b1, sizeof(b1), "%s/ipv6", tnl_strproto(p->proto));
+       print_string(PRINT_ANY, "mode", "%s ", b1);
+       print_string(PRINT_FP, NULL, "%s", "remote ");
+       print_color_string(PRINT_ANY, COLOR_INET6, "remote", "%s ",
+                          format_host_r(AF_INET6, 16, &p->raddr, b1, sizeof(b1)));
+       print_string(PRINT_FP, NULL, "%s", "local ");
+       print_color_string(PRINT_ANY, COLOR_INET6, "local", "%s",
+                          rt_addr_n2a_r(AF_INET6, 16, &p->laddr, b1, sizeof(b1)));
+
        if (p->link) {
                const char *n = ll_index_to_name(p->link);
 
                if (n)
-                       printf(" dev %s", n);
+                       print_string(PRINT_ANY, "link", " dev %s", n);
        }
 
        if (p->flags & IP6_TNL_F_IGN_ENCAP_LIMIT)
-               printf(" encaplimit none");
+               print_null(PRINT_ANY, "ip6_tnl_f_ign_encap_limit",
+                          " encaplimit none", NULL);
        else
-               printf(" encaplimit %u", p->encap_limit);
+               print_uint(PRINT_ANY, "encap_limit", " encaplimit %u",
+                          p->encap_limit);
 
        if (p->hop_limit)
-               printf(" hoplimit %u", p->hop_limit);
+               print_uint(PRINT_ANY, "hoplimit", " hoplimit %u", p->hop_limit);
        else
-               printf(" hoplimit inherit");
+               print_string(PRINT_FP, "hoplimit", " hoplimit %s", "inherit");
 
-       if (p->flags & IP6_TNL_F_USE_ORIG_TCLASS)
-               printf(" tclass inherit");
-       else {
+       if (p->flags & IP6_TNL_F_USE_ORIG_TCLASS) {
+               print_null(PRINT_ANY, "ip6_tnl_f_use_orig_tclass",
+                          " tclass inherit", NULL);
+       } else {
                __u32 val = ntohl(p->flowinfo & IP6_FLOWINFO_TCLASS);
 
-               printf(" tclass 0x%02x", (__u8)(val >> 20));
+               snprintf(b1, sizeof(b1), "0x%02x", (__u8)(val >> 20));
+               print_string(PRINT_ANY, "tclass", " tclass %s", b1);
        }
 
-       if (p->flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
-               printf(" flowlabel inherit");
-       else
-               printf(" flowlabel 0x%05x", ntohl(p->flowinfo & IP6_FLOWINFO_FLOWLABEL));
+       if (p->flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) {
+               print_null(PRINT_ANY, "ip6_tnl_f_use_orig_flowlabel",
+                          " flowlabel inherit", NULL);
+       } else {
+               __u32 val = ntohl(p->flowinfo & IP6_FLOWINFO_FLOWLABEL);
+
+               snprintf(b1, sizeof(b1), "0x%05x", val);
+               print_string(PRINT_ANY, "flowlabel", " flowlabel %s", b1);
+       }
 
-       printf(" (flowinfo 0x%08x)", ntohl(p->flowinfo));
+       snprintf(b1, sizeof(b1), "0x%08x", ntohl(p->flowinfo));
+       print_string(PRINT_ANY, "flowinfo", " (flowinfo %s)", b1);
 
        if (p->flags & IP6_TNL_F_RCV_DSCP_COPY)
-               printf(" dscp inherit");
+               print_null(PRINT_ANY, "ip6_tnl_f_rcv_dscp_copy",
+                          " dscp inherit", NULL);
 
        if (p->flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE)
-               printf(" allow-localremote");
-
-       if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) &&
-           p->o_key == p->i_key)
-               printf(" key %u", ntohl(p->i_key));
-       else {
-               if (p->i_flags & GRE_KEY)
-                       printf(" ikey %u", ntohl(p->i_key));
-               if (p->o_flags & GRE_KEY)
-                       printf(" okey %u", ntohl(p->o_key));
-       }
+               print_null(PRINT_ANY, "ip6_tnl_f_allow_local_remote",
+                          " allow-localremote", NULL);
 
-       if (p->proto == IPPROTO_GRE) {
-               if (p->i_flags & GRE_SEQ)
-                       printf("%s  Drop packets out of sequence.", _SL_);
-               if (p->i_flags & GRE_CSUM)
-                       printf("%s  Checksum in received packet is required.", _SL_);
-               if (p->o_flags & GRE_SEQ)
-                       printf("%s  Sequence packets on output.", _SL_);
-               if (p->o_flags & GRE_CSUM)
-                       printf("%s  Checksum output packets.", _SL_);
-       }
+       tnl_print_gre_flags(p->proto, p->i_flags, p->o_flags,
+                           p->i_key, p->o_key);
+
+       close_json_object();
 }
 
 static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm2 *p)
@@ -373,7 +374,6 @@ static int do_show(int argc, char **argv)
                return -1;
 
        print_tunnel(&p);
-       fputc('\n', stdout);
        return 0;
 }
 
index 92a5cb923b6b02a47766f22af5cfbdbf3401c569..696f3b92bff55d691162e3222f16ef8695714df2 100644 (file)
@@ -289,17 +289,25 @@ static void print_tunnel(const void *t)
 {
        const struct ip_tunnel_parm *p = t;
        struct ip_tunnel_6rd ip6rd = {};
-       char s1[1024];
-       char s2[1024];
+       SPRINT_BUF(b1);
 
        /* Do not use format_host() for local addr,
         * symbolic name will not be useful.
         */
-       printf("%s: %s/ip remote %s local %s",
-              p->name,
-              tnl_strproto(p->iph.protocol),
-              p->iph.daddr ? format_host_r(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)) : "any",
-              p->iph.saddr ? rt_addr_n2a_r(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)) : "any");
+       open_json_object(NULL);
+       print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", "%s: ", p->name);
+       snprintf(b1, sizeof(b1), "%s/ip", tnl_strproto(p->iph.protocol));
+       print_string(PRINT_ANY, "mode", "%s ", b1);
+       print_null(PRINT_FP, NULL, "remote ", NULL);
+       print_color_string(PRINT_ANY, COLOR_INET, "remote", "%s ",
+                          p->iph.daddr || is_json_context()
+                               ? format_host_r(AF_INET, 4, &p->iph.daddr, b1, sizeof(b1))
+                               : "any");
+       print_null(PRINT_FP, NULL, "local ", NULL);
+       print_color_string(PRINT_ANY, COLOR_INET, "local", "%s",
+                          p->iph.saddr || is_json_context()
+                               ? rt_addr_n2a_r(AF_INET, 4, &p->iph.saddr, b1, sizeof(b1))
+                               : "any");
 
        if (p->iph.protocol == IPPROTO_IPV6 && (p->i_flags & SIT_ISATAP)) {
                struct ip_tunnel_prl prl[16] = {};
@@ -308,69 +316,70 @@ static void print_tunnel(const void *t)
                prl[0].datalen = sizeof(prl) - sizeof(prl[0]);
                prl[0].addr = htonl(INADDR_ANY);
 
-               if (!tnl_prl_ioctl(SIOCGETPRL, p->name, prl))
+               if (!tnl_prl_ioctl(SIOCGETPRL, p->name, prl)) {
                        for (i = 1; i < ARRAY_SIZE(prl); i++) {
-                               if (prl[i].addr != htonl(INADDR_ANY)) {
-                                       printf(" %s %s ",
-                                              (prl[i].flags & PRL_DEFAULT) ? "pdr" : "pr",
-                                              format_host(AF_INET, 4, &prl[i].addr));
-                               }
+                               if (prl[i].addr == htonl(INADDR_ANY))
+                                       continue;
+                               if (prl[i].flags & PRL_DEFAULT)
+                                       print_string(PRINT_ANY, "pdr",
+                                                    " pdr %s",
+                                                    format_host(AF_INET, 4, &prl[i].addr));
+                               else
+                                       print_string(PRINT_ANY, "pr", " pr %s",
+                                                    format_host(AF_INET, 4, &prl[i].addr));
                        }
+               }
        }
 
        if (p->link) {
                const char *n = ll_index_to_name(p->link);
 
                if (n)
-                       printf(" dev %s", n);
+                       print_string(PRINT_ANY, "dev", " dev %s", n);
        }
 
        if (p->iph.ttl)
-               printf(" ttl %u", p->iph.ttl);
+               print_uint(PRINT_ANY, "ttl", " ttl %u", p->iph.ttl);
        else
-               printf(" ttl inherit");
+               print_string(PRINT_FP, "ttl", " ttl %s", "inherit");
 
        if (p->iph.tos) {
-               SPRINT_BUF(b1);
-               printf(" tos");
-               if (p->iph.tos & 1)
-                       printf(" inherit");
-               if (p->iph.tos & ~1)
-                       printf("%c%s ", p->iph.tos & 1 ? '/' : ' ',
-                              rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1)));
+               SPRINT_BUF(b2);
+
+               if (p->iph.tos != 1) {
+                       if (!is_json_context() && p->iph.tos & 1)
+                               snprintf(b2, sizeof(b2), "%s%s",
+                                        p->iph.tos & 1 ? "inherit/" : "",
+                                        rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1)));
+                       else
+                               snprintf(b2, sizeof(b2), "%s",
+                                        rtnl_dsfield_n2a(p->iph.tos, b1, sizeof(b1)));
+                       print_string(PRINT_ANY, "tos", " tos %s", b2);
+               } else {
+                       print_string(PRINT_FP, NULL, " tos %s", "inherit");
+               }
        }
 
        if (!(p->iph.frag_off & htons(IP_DF)))
-               printf(" nopmtudisc");
+               print_null(PRINT_ANY, "nopmtudisc", " nopmtudisc", NULL);
 
        if (p->iph.protocol == IPPROTO_IPV6 && !tnl_ioctl_get_6rd(p->name, &ip6rd) && ip6rd.prefixlen) {
-               printf(" 6rd-prefix %s/%u",
-                      inet_ntop(AF_INET6, &ip6rd.prefix, s1, sizeof(s1)),
-                      ip6rd.prefixlen);
+               print_string(PRINT_ANY, "6rd-prefix", " 6rd-prefix %s",
+                            inet_ntop(AF_INET6, &ip6rd.prefix, b1, sizeof(b1)));
+               print_uint(PRINT_ANY, "6rd-prefixlen", "/%u", ip6rd.prefixlen);
                if (ip6rd.relay_prefix) {
-                       printf(" 6rd-relay_prefix %s/%u",
-                              format_host(AF_INET, 4, &ip6rd.relay_prefix),
-                              ip6rd.relay_prefixlen);
+                       print_string(PRINT_ANY, "6rd-relay_prefix",
+                                    " 6rd-relay_prefix %s",
+                                    format_host(AF_INET, 4, &ip6rd.relay_prefix));
+                       print_uint(PRINT_ANY, "6rd-relay_prefixlen", "/%u",
+                                  ip6rd.relay_prefixlen);
                }
        }
 
-       if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key)
-               printf(" key %u", ntohl(p->i_key));
-       else if ((p->i_flags | p->o_flags) & GRE_KEY) {
-               if (p->i_flags & GRE_KEY)
-                       printf(" ikey %u", ntohl(p->i_key));
-               if (p->o_flags & GRE_KEY)
-                       printf(" okey %u", ntohl(p->o_key));
-       }
+       tnl_print_gre_flags(p->iph.protocol, p->i_flags, p->o_flags,
+                           p->i_key, p->o_key);
 
-       if (p->i_flags & GRE_SEQ)
-               printf("%s  Drop packets out of sequence.", _SL_);
-       if (p->i_flags & GRE_CSUM)
-               printf("%s  Checksum in received packet is required.", _SL_);
-       if (p->o_flags & GRE_SEQ)
-               printf("%s  Sequence packets on output.", _SL_);
-       if (p->o_flags & GRE_CSUM)
-               printf("%s  Checksum output packets.", _SL_);
+       close_json_object();
 }
 
 
index d0d55f37169e9f522e39453b894b61f9bbf0200e..88585cf3177b598dd11fd1bf2d712f004eb068af 100644 (file)
@@ -308,6 +308,51 @@ void tnl_print_endpoint(const char *name, const struct rtattr *rta, int family)
        }
 }
 
+void tnl_print_gre_flags(__u8 proto,
+                        __be16 i_flags, __be16 o_flags,
+                        __be32 i_key, __be32 o_key)
+{
+       if ((i_flags & GRE_KEY) && (o_flags & GRE_KEY) &&
+           o_key == i_key) {
+               print_uint(PRINT_ANY, "key", " key %u", ntohl(i_key));
+       } else {
+               if (i_flags & GRE_KEY)
+                       print_uint(PRINT_ANY, "ikey", " ikey %u", ntohl(i_key));
+               if (o_flags & GRE_KEY)
+                       print_uint(PRINT_ANY, "okey", " okey %u", ntohl(o_key));
+       }
+
+       if (proto != IPPROTO_GRE)
+               return;
+
+       open_json_array(PRINT_JSON, "flags");
+       if (i_flags & GRE_SEQ) {
+               if (is_json_context())
+                       print_string(PRINT_JSON, NULL, "%s", "rx_drop_ooseq");
+               else
+                       printf("%s  Drop packets out of sequence.", _SL_);
+       }
+       if (i_flags & GRE_CSUM) {
+               if (is_json_context())
+                       print_string(PRINT_JSON, NULL, "%s", "rx_csum");
+               else
+                       printf("%s  Checksum in received packet is required.", _SL_);
+       }
+       if (o_flags & GRE_SEQ) {
+               if (is_json_context())
+                       print_string(PRINT_JSON, NULL, "%s", "tx_seq");
+               else
+                       printf("%s  Sequence packets on output.", _SL_);
+       }
+       if (o_flags & GRE_CSUM) {
+               if (is_json_context())
+                       print_string(PRINT_JSON, NULL, "%s", "tx_csum");
+               else
+                       printf("%s  Checksum output packets.", _SL_);
+       }
+       close_json_array(PRINT_JSON, NULL);
+}
+
 static void tnl_print_stats(const struct rtnl_link_stats64 *s)
 {
        printf("%s", _SL_);
@@ -391,6 +436,7 @@ static int print_nlmsg_tunnel(struct nlmsghdr *n, void *arg)
 
 int do_tunnels_list(struct tnl_print_nlmsg_info *info)
 {
+       new_json_obj(json);
        if (rtnl_linkdump_req(&rth, preferred_family) < 0) {
                perror("Cannot send dump request\n");
                return -1;
@@ -400,6 +446,7 @@ int do_tunnels_list(struct tnl_print_nlmsg_info *info)
                fprintf(stderr, "Dump terminated\n");
                return -1;
        }
+       delete_json_obj();
 
        return 0;
 }
index e530d07cbf6a2f4a7acfef1727f3b441c95f7d85..604f8cbfd6db2164e2bacf5d897937d324138b4a 100644 (file)
@@ -55,5 +55,8 @@ void tnl_print_encap(struct rtattr *tb[],
                     int encap_sport, int encap_dport);
 void tnl_print_endpoint(const char *name,
                        const struct rtattr *rta, int family);
+void tnl_print_gre_flags(__u8 proto,
+                        __be16 i_flags, __be16 o_flags,
+                        __be32 i_key, __be32 o_key);
 
 #endif
index 30a12de7d2c79709c9b711d26d647fc022e832c6..4cb3b9e02d6ee5dc8249d2c388db581827ab32f5 100644 (file)
@@ -106,6 +106,16 @@ referred to as "Launch Time" or "Time-Based Scheduling" by the
 documentation of network interface controllers.
 The default is for this option to be disabled.
 
+.TP
+skip_sock_check
+.br
+.BR etf(8)
+currently drops any packet which does not have a socket associated with it or
+if the socket does not have SO_TXTIME socket option set. But, this will not
+work if the launchtime is set by another entity inside the kernel (e.g. some
+other Qdisc). Setting the skip_sock_check will skip checking for a socket
+associated with the packet.
+
 .SH EXAMPLES
 
 ETF is used to enforce a Quality of Service. It controls when each
index adff41e39b006c367331808cc7b6fbba28529d60..04ee194764e1c1e6c6d00cf1050ee8ae5b5b106c 100644 (file)
@@ -289,6 +289,41 @@ bits is assumed.
 .TQ
 .BI enc_ttl " NUMBER"
 .TQ
+.BR
+.TP
+.BI ct_state " CT_STATE"
+.TQ
+.BI ct_zone " CT_MASKED_ZONE"
+.TQ
+.BI ct_mark " CT_MASKED_MARK"
+.TQ
+.BI ct_label " CT_MASKED_LABEL"
+Matches on connection tracking info
+.RS
+.TP
+.I CT_STATE
+Match the connection state, and can ne combination of [{+|-}flag] flags, where flag can be one of
+.RS
+.TP
+trk - Tracked connection.
+.TP
+new - New connection.
+.TP
+est - Established connection.
+.TP
+Example: +trk+est
+.RE
+.TP
+.I CT_MASKED_ZONE
+Match the connection zone, and can be masked.
+.TP
+.I CT_MASKED_MARK
+32bit match on the connection mark, and can be masked.
+.TP
+.I CT_MASKED_LABEL
+128bit match on the connection label, and can be masked.
+.RE
+.TP
 .BI geneve_opts " OPTIONS"
 Match on IP tunnel metadata. Key id
 .I NUMBER
index 850be9b03649112994a722a9b9109cf85a51f8b2..e1d19ba190896e488411f25cb17ab4c4c5960062 100644 (file)
@@ -112,6 +112,26 @@ means that traffic class 0 is "active" for that schedule entry.
 long that state defined by <command> and <gate mask> should be held
 before moving to the next entry.
 
+.TP
+flags
+.br
+Specifies different modes for taprio. Currently, only txtime-assist is
+supported which can be enabled by setting it to 0x1. In this mode, taprio will
+set the transmit timestamp depending on the interval in which the packet needs
+to be transmitted. It will then utililize the
+.BR etf(8)
+qdisc to sort and transmit the packets at the right time. The second example
+can be used as a reference to configure this mode.
+
+.TP
+txtime-delay
+.br
+This parameter is specific to the txtime offload mode. It specifies the maximum
+time a packet might take to reach the network card from the taprio qdisc. The
+value should always be greater than the delta specified in the
+.BR etf(8)
+qdisc.
+
 .SH EXAMPLES
 
 The following example shows how an traffic schedule with three traffic
@@ -137,6 +157,26 @@ reference CLOCK_TAI. The schedule is composed of three entries each of
               clockid CLOCK_TAI
 .EE
 
+Following is an example to enable the txtime offload mode in taprio. See
+.BR etf(8)
+for more information about configuring the ETF qdisc.
+
+.EX
+# tc qdisc replace dev eth0 parent root handle 100 taprio \\
+              num_tc 3 \\
+              map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 \\
+              queues 1@0 1@0 1@0 \\
+              base-time 1528743495910289987 \\
+              sched-entry S 01 300000 \\
+              sched-entry S 02 300000 \\
+              sched-entry S 04 400000 \\
+              flags 0x1 \\
+              txtime-delay 200000 \\
+              clockid CLOCK_TAI
+
+# tc qdisc replace dev $IFACE parent 100:1 etf skip_skb_check \\
+              offload delta 200000 clockid CLOCK_TAI
+.EE
 
 .SH AUTHORS
 Vinicius Costa Gomes <vinicius.gomes@intel.com>
index ef863f142eca1c8da9f63eacf1e4973ba0d60f73..97a7b9640185e23505a93fbd695921e20f0ce9cb 100644 (file)
@@ -148,9 +148,11 @@ const char *qp_types_to_str(uint8_t idx)
                                                     "UC", "UD", "RAW_IPV6",
                                                     "RAW_ETHERTYPE",
                                                     "UNKNOWN", "RAW_PACKET",
-                                                    "XRC_INI", "XRC_TGT" };
+                                                    "XRC_INI", "XRC_TGT",
+                                                    [0xFF] = "DRIVER",
+       };
 
-       if (idx < ARRAY_SIZE(qp_types_str))
+       if (idx < ARRAY_SIZE(qp_types_str) && qp_types_str[idx])
                return qp_types_str[idx];
        return "UNKNOWN";
 }
index 09ff3692b16636735753cb015b156a7661de8c55..14171a28cba5d2bfeb5b1af4b713279c668859d2 100644 (file)
@@ -53,6 +53,7 @@ TCMODULES += m_ctinfo.o
 TCMODULES += m_bpf.o
 TCMODULES += m_tunnel_key.o
 TCMODULES += m_sample.o
+TCMODULES += m_ct.o
 TCMODULES += p_ip.o
 TCMODULES += p_ip6.o
 TCMODULES += p_icmp.o
index 70d40d3b2f2bf6a00d658671d07d3f2995424447..a2a230162f7850ec78992a38347c1ce8469226b8 100644 (file)
@@ -82,9 +82,14 @@ static void explain(void)
                "                       enc_ttl MASKED-IP_TTL |\n"
                "                       geneve_opts MASKED-OPTIONS |\n"
                "                       ip_flags IP-FLAGS | \n"
-               "                       enc_dst_port [ port_number ] }\n"
+               "                       enc_dst_port [ port_number ] |\n"
+               "                       ct_state MASKED_CT_STATE |\n"
+               "                       ct_label MASKED_CT_LABEL |\n"
+               "                       ct_mark MASKED_CT_MARK |\n"
+               "                       ct_zone MASKED_CT_ZONE }\n"
                "       FILTERID := X:Y:Z\n"
                "       MASKED_LLADDR := { LLADDR | LLADDR/MASK | LLADDR/BITS }\n"
+               "       MASKED_CT_STATE := combination of {+|-} and flags trk,est,new\n"
                "       ACTION-SPEC := ... look at individual actions\n"
                "\n"
                "NOTE:  CLASSID, IP-PROTO are parsed as hexadecimal input.\n"
@@ -214,6 +219,159 @@ static int flower_parse_matching_flags(char *str,
        return 0;
 }
 
+static int flower_parse_u16(char *str, int value_type, int mask_type,
+                           struct nlmsghdr *n)
+{
+       __u16 value, mask;
+       char *slash;
+
+       slash = strchr(str, '/');
+       if (slash)
+               *slash = '\0';
+
+       if (get_u16(&value, str, 0))
+               return -1;
+
+       if (slash) {
+               if (get_u16(&mask, slash + 1, 0))
+                       return -1;
+       } else {
+               mask = UINT16_MAX;
+       }
+
+       addattr16(n, MAX_MSG, value_type, value);
+       addattr16(n, MAX_MSG, mask_type, mask);
+
+       return 0;
+}
+
+static int flower_parse_u32(char *str, int value_type, int mask_type,
+                           struct nlmsghdr *n)
+{
+       __u32 value, mask;
+       char *slash;
+
+       slash = strchr(str, '/');
+       if (slash)
+               *slash = '\0';
+
+       if (get_u32(&value, str, 0))
+               return -1;
+
+       if (slash) {
+               if (get_u32(&mask, slash + 1, 0))
+                       return -1;
+       } else {
+               mask = UINT32_MAX;
+       }
+
+       addattr32(n, MAX_MSG, value_type, value);
+       addattr32(n, MAX_MSG, mask_type, mask);
+
+       return 0;
+}
+
+static int flower_parse_ct_mark(char *str, struct nlmsghdr *n)
+{
+       return flower_parse_u32(str,
+                               TCA_FLOWER_KEY_CT_MARK,
+                               TCA_FLOWER_KEY_CT_MARK_MASK,
+                               n);
+}
+
+static int flower_parse_ct_zone(char *str, struct nlmsghdr *n)
+{
+       return flower_parse_u16(str,
+                               TCA_FLOWER_KEY_CT_ZONE,
+                               TCA_FLOWER_KEY_CT_ZONE_MASK,
+                               n);
+}
+
+static int flower_parse_ct_labels(char *str, struct nlmsghdr *n)
+{
+#define LABELS_SIZE    16
+       uint8_t labels[LABELS_SIZE], lmask[LABELS_SIZE];
+       char *slash, *mask = NULL;
+       size_t slen, slen_mask = 0;
+
+       slash = index(str, '/');
+       if (slash) {
+               *slash = 0;
+               mask = slash + 1;
+               slen_mask = strlen(mask);
+       }
+
+       slen = strlen(str);
+       if (slen > LABELS_SIZE * 2 || slen_mask > LABELS_SIZE * 2) {
+               char errmsg[128];
+
+               snprintf(errmsg, sizeof(errmsg),
+                               "%zd Max allowed size %d",
+                               slen, LABELS_SIZE*2);
+               invarg(errmsg, str);
+       }
+
+       if (hex2mem(str, labels, slen / 2) < 0)
+               invarg("labels must be a hex string\n", str);
+       addattr_l(n, MAX_MSG, TCA_FLOWER_KEY_CT_LABELS, labels, slen / 2);
+
+       if (mask) {
+               if (hex2mem(mask, lmask, slen_mask / 2) < 0)
+                       invarg("labels mask must be a hex string\n", mask);
+       } else {
+               memset(lmask, 0xff, sizeof(lmask));
+               slen_mask = sizeof(lmask) * 2;
+       }
+       addattr_l(n, MAX_MSG, TCA_FLOWER_KEY_CT_LABELS_MASK, lmask,
+                 slen_mask / 2);
+
+       return 0;
+}
+
+static struct flower_ct_states {
+       char *str;
+       int flag;
+} flower_ct_states[] = {
+       { "trk", TCA_FLOWER_KEY_CT_FLAGS_TRACKED },
+       { "new", TCA_FLOWER_KEY_CT_FLAGS_NEW },
+       { "est", TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED },
+};
+
+static int flower_parse_ct_state(char *str, struct nlmsghdr *n)
+{
+       int flags = 0, mask = 0,  len, i;
+       bool p;
+
+       while (*str != '\0') {
+               if (*str == '+')
+                       p = true;
+               else if (*str == '-')
+                       p = false;
+               else
+                       return -1;
+
+               for (i = 0; i < ARRAY_SIZE(flower_ct_states); i++) {
+                       len = strlen(flower_ct_states[i].str);
+                       if (strncmp(str + 1, flower_ct_states[i].str, len))
+                               continue;
+
+                       if (p)
+                               flags |= flower_ct_states[i].flag;
+                       mask |= flower_ct_states[i].flag;
+                       break;
+               }
+
+               if (i == ARRAY_SIZE(flower_ct_states))
+                       return -1;
+
+               str += len + 1;
+       }
+
+       addattr16(n, MAX_MSG, TCA_FLOWER_KEY_CT_STATE, flags);
+       addattr16(n, MAX_MSG, TCA_FLOWER_KEY_CT_STATE_MASK, mask);
+       return 0;
+}
+
 static int flower_parse_ip_proto(char *str, __be16 eth_type, int type,
                                 __u8 *p_ip_proto, struct nlmsghdr *n)
 {
@@ -898,6 +1056,34 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
                        flags |= TCA_CLS_FLAGS_SKIP_HW;
                } else if (matches(*argv, "skip_sw") == 0) {
                        flags |= TCA_CLS_FLAGS_SKIP_SW;
+               } else if (matches(*argv, "ct_state") == 0) {
+                       NEXT_ARG();
+                       ret = flower_parse_ct_state(*argv, n);
+                       if (ret < 0) {
+                               fprintf(stderr, "Illegal \"ct_state\"\n");
+                               return -1;
+                       }
+               } else if (matches(*argv, "ct_zone") == 0) {
+                       NEXT_ARG();
+                       ret = flower_parse_ct_zone(*argv, n);
+                       if (ret < 0) {
+                               fprintf(stderr, "Illegal \"ct_zone\"\n");
+                               return -1;
+                       }
+               } else if (matches(*argv, "ct_mark") == 0) {
+                       NEXT_ARG();
+                       ret = flower_parse_ct_mark(*argv, n);
+                       if (ret < 0) {
+                               fprintf(stderr, "Illegal \"ct_mark\"\n");
+                               return -1;
+                       }
+               } else if (matches(*argv, "ct_label") == 0) {
+                       NEXT_ARG();
+                       ret = flower_parse_ct_labels(*argv, n);
+                       if (ret < 0) {
+                               fprintf(stderr, "Illegal \"ct_label\"\n");
+                               return -1;
+                       }
                } else if (matches(*argv, "indev") == 0) {
                        NEXT_ARG();
                        if (check_ifname(*argv))
@@ -1590,6 +1776,85 @@ static void flower_print_tcp_flags(const char *name, struct rtattr *flags_attr,
        print_string(PRINT_ANY, name, namefrm, out);
 }
 
+static void flower_print_ct_state(struct rtattr *flags_attr,
+                                 struct rtattr *mask_attr)
+{
+       SPRINT_BUF(out);
+       uint16_t state;
+       uint16_t state_mask;
+       size_t done = 0;
+       int i;
+
+       if (!flags_attr)
+               return;
+
+       state = rta_getattr_u16(flags_attr);
+       if (mask_attr)
+               state_mask = rta_getattr_u16(mask_attr);
+       else
+               state_mask = UINT16_MAX;
+
+       for (i = 0; i < ARRAY_SIZE(flower_ct_states); i++) {
+               if (!(state_mask & flower_ct_states[i].flag))
+                       continue;
+
+               if (state & flower_ct_states[i].flag)
+                       done += sprintf(out + done, "+%s",
+                                       flower_ct_states[i].str);
+               else
+                       done += sprintf(out + done, "-%s",
+                                       flower_ct_states[i].str);
+       }
+
+       print_string(PRINT_ANY, "ct_state", "\n  ct_state %s", out);
+}
+
+static void flower_print_ct_label(struct rtattr *attr,
+                                 struct rtattr *mask_attr)
+{
+       const unsigned char *str;
+       bool print_mask = false;
+       int data_len, i;
+       SPRINT_BUF(out);
+       char *p;
+
+       if (!attr)
+               return;
+
+       data_len = RTA_PAYLOAD(attr);
+       hexstring_n2a(RTA_DATA(attr), data_len, out, sizeof(out));
+       p = out + data_len*2;
+
+       data_len = RTA_PAYLOAD(attr);
+       str = RTA_DATA(mask_attr);
+       if (data_len != 16)
+               print_mask = true;
+       for (i = 0; !print_mask && i < data_len; i++) {
+               if (str[i] != 0xff)
+                       print_mask = true;
+       }
+       if (print_mask) {
+               *p++ = '/';
+               hexstring_n2a(RTA_DATA(mask_attr), data_len, p,
+                             sizeof(out)-(p-out));
+               p += data_len*2;
+       }
+       *p = '\0';
+
+       print_string(PRINT_ANY, "ct_label", "\n  ct_label %s", out);
+}
+
+static void flower_print_ct_zone(struct rtattr *attr,
+                                struct rtattr *mask_attr)
+{
+       print_masked_u16("ct_zone", attr, mask_attr);
+}
+
+static void flower_print_ct_mark(struct rtattr *attr,
+                                struct rtattr *mask_attr)
+{
+       print_masked_u32("ct_mark", attr, mask_attr);
+}
 
 static void flower_print_key_id(const char *name, struct rtattr *attr)
 {
@@ -1949,6 +2214,15 @@ static int flower_print_opt(struct filter_util *qu, FILE *f,
                                    tb[TCA_FLOWER_KEY_FLAGS],
                                    tb[TCA_FLOWER_KEY_FLAGS_MASK]);
 
+       flower_print_ct_state(tb[TCA_FLOWER_KEY_CT_STATE],
+                             tb[TCA_FLOWER_KEY_CT_STATE_MASK]);
+       flower_print_ct_zone(tb[TCA_FLOWER_KEY_CT_ZONE],
+                            tb[TCA_FLOWER_KEY_CT_ZONE_MASK]);
+       flower_print_ct_mark(tb[TCA_FLOWER_KEY_CT_MARK],
+                            tb[TCA_FLOWER_KEY_CT_MARK_MASK]);
+       flower_print_ct_label(tb[TCA_FLOWER_KEY_CT_LABELS],
+                             tb[TCA_FLOWER_KEY_CT_LABELS_MASK]);
+
        close_json_object();
 
        if (tb[TCA_FLOWER_FLAGS]) {
index bdc62720879c13ed0990af8bbfd668933285290b..36c744bbe374515dcca04706f46a3de7d3e89e9a 100644 (file)
@@ -214,7 +214,8 @@ done0:
                        tail = addattr_nest(n, MAX_MSG, ++prio);
                        addattr_l(n, MAX_MSG, TCA_ACT_KIND, k, strlen(k) + 1);
 
-                       ret = a->parse_aopt(a, &argc, &argv, TCA_ACT_OPTIONS,
+                       ret = a->parse_aopt(a, &argc, &argv,
+                                           TCA_ACT_OPTIONS | NLA_F_NESTED,
                                            n);
 
                        if (ret < 0) {
diff --git a/tc/m_ct.c b/tc/m_ct.c
new file mode 100644 (file)
index 0000000..8589cb9
--- /dev/null
+++ b/tc/m_ct.c
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* -
+ * m_ct.c     Connection tracking action
+ *
+ * Authors:   Paul Blakey <paulb@mellanox.com>
+ *            Yossi Kuperman <yossiku@mellanox.com>
+ *            Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "utils.h"
+#include "tc_util.h"
+#include <linux/tc_act/tc_ct.h>
+
+static void
+usage(void)
+{
+       fprintf(stderr,
+               "Usage: ct clear\n"
+               "       ct commit [force] [zone ZONE] [mark MASKED_MARK] [label MASKED_LABEL] [nat NAT_SPEC]\n"
+               "       ct [nat] [zone ZONE]\n"
+               "Where: ZONE is the conntrack zone table number\n"
+               "       NAT_SPEC is {src|dst} addr addr1[-addr2] [port port1[-port2]]\n"
+               "\n");
+       exit(-1);
+}
+
+static int ct_parse_nat_addr_range(const char *str, struct nlmsghdr *n)
+{
+       inet_prefix addr = { .family = AF_UNSPEC, };
+       char *addr1, *addr2 = 0;
+       SPRINT_BUF(buffer);
+       int attr;
+       int ret;
+
+       strncpy(buffer, str, sizeof(buffer) - 1);
+
+       addr1 = buffer;
+       addr2 = strchr(addr1, '-');
+       if (addr2) {
+               *addr2 = '\0';
+               addr2++;
+       }
+
+       ret = get_addr(&addr, addr1, AF_UNSPEC);
+       if (ret)
+               return ret;
+       attr = addr.family == AF_INET ? TCA_CT_NAT_IPV4_MIN :
+                                       TCA_CT_NAT_IPV6_MIN;
+       addattr_l(n, MAX_MSG, attr, addr.data, addr.bytelen);
+
+       if (addr2) {
+               ret = get_addr(&addr, addr2, addr.family);
+               if (ret)
+                       return ret;
+       }
+       attr = addr.family == AF_INET ? TCA_CT_NAT_IPV4_MAX :
+                                       TCA_CT_NAT_IPV6_MAX;
+       addattr_l(n, MAX_MSG, attr, addr.data, addr.bytelen);
+
+       return 0;
+}
+
+static int ct_parse_nat_port_range(const char *str, struct nlmsghdr *n)
+{
+       char *port1, *port2 = 0;
+       SPRINT_BUF(buffer);
+       __be16 port;
+       int ret;
+
+       strncpy(buffer, str, sizeof(buffer) - 1);
+
+       port1 = buffer;
+       port2 = strchr(port1, '-');
+       if (port2) {
+               *port2 = '\0';
+               port2++;
+       }
+
+       ret = get_be16(&port, port1, 10);
+       if (ret)
+               return -1;
+       addattr16(n, MAX_MSG, TCA_CT_NAT_PORT_MIN, port);
+
+       if (port2) {
+               ret = get_be16(&port, port2, 10);
+               if (ret)
+                       return -1;
+       }
+       addattr16(n, MAX_MSG, TCA_CT_NAT_PORT_MAX, port);
+
+       return 0;
+}
+
+
+static int ct_parse_u16(char *str, int value_type, int mask_type,
+                       struct nlmsghdr *n)
+{
+       __u16 value, mask;
+       char *slash = 0;
+
+       if (mask_type != TCA_CT_UNSPEC) {
+               slash = strchr(str, '/');
+               if (slash)
+                       *slash = '\0';
+       }
+
+       if (get_u16(&value, str, 0))
+               return -1;
+
+       if (slash) {
+               if (get_u16(&mask, slash + 1, 0))
+                       return -1;
+       } else {
+               mask = UINT16_MAX;
+       }
+
+       addattr16(n, MAX_MSG, value_type, value);
+       if (mask_type != TCA_CT_UNSPEC)
+               addattr16(n, MAX_MSG, mask_type, mask);
+
+       return 0;
+}
+
+static int ct_parse_u32(char *str, int value_type, int mask_type,
+                       struct nlmsghdr *n)
+{
+       __u32 value, mask;
+       char *slash;
+
+       slash = strchr(str, '/');
+       if (slash)
+               *slash = '\0';
+
+       if (get_u32(&value, str, 0))
+               return -1;
+
+       if (slash) {
+               if (get_u32(&mask, slash + 1, 0))
+                       return -1;
+       } else {
+               mask = UINT32_MAX;
+       }
+
+       addattr32(n, MAX_MSG, value_type, value);
+       addattr32(n, MAX_MSG, mask_type, mask);
+
+       return 0;
+}
+
+static int ct_parse_mark(char *str, struct nlmsghdr *n)
+{
+       return ct_parse_u32(str, TCA_CT_MARK, TCA_CT_MARK_MASK, n);
+}
+
+static int ct_parse_labels(char *str, struct nlmsghdr *n)
+{
+#define LABELS_SIZE    16
+       uint8_t labels[LABELS_SIZE], lmask[LABELS_SIZE];
+       char *slash, *mask = NULL;
+       size_t slen, slen_mask = 0;
+
+       slash = index(str, '/');
+       if (slash) {
+               *slash = 0;
+               mask = slash+1;
+               slen_mask = strlen(mask);
+       }
+
+       slen = strlen(str);
+       if (slen > LABELS_SIZE*2 || slen_mask > LABELS_SIZE*2) {
+               char errmsg[128];
+
+               snprintf(errmsg, sizeof(errmsg),
+                               "%zd Max allowed size %d",
+                               slen, LABELS_SIZE*2);
+               invarg(errmsg, str);
+       }
+
+       if (hex2mem(str, labels, slen/2) < 0)
+               invarg("ct: labels must be a hex string\n", str);
+       addattr_l(n, MAX_MSG, TCA_CT_LABELS, labels, slen/2);
+
+       if (mask) {
+               if (hex2mem(mask, lmask, slen_mask/2) < 0)
+                       invarg("ct: labels mask must be a hex string\n", mask);
+       } else {
+               memset(lmask, 0xff, sizeof(lmask));
+               slen_mask = sizeof(lmask)*2;
+       }
+       addattr_l(n, MAX_MSG, TCA_CT_LABELS_MASK, lmask, slen_mask/2);
+
+       return 0;
+}
+
+static int
+parse_ct(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
+               struct nlmsghdr *n)
+{
+       struct tc_ct sel = {};
+       char **argv = *argv_p;
+       struct rtattr *tail;
+       int argc = *argc_p;
+       int ct_action = 0;
+       int ret;
+
+       tail = addattr_nest(n, MAX_MSG, tca_id);
+
+       if (argc && matches(*argv, "ct") == 0)
+               NEXT_ARG_FWD();
+
+       while (argc > 0) {
+               if (matches(*argv, "zone") == 0) {
+                       NEXT_ARG();
+
+                       if (ct_parse_u16(*argv,
+                                        TCA_CT_ZONE, TCA_CT_UNSPEC, n)) {
+                               fprintf(stderr, "ct: Illegal \"zone\"\n");
+                               return -1;
+                       }
+               } else if (matches(*argv, "nat") == 0) {
+                       ct_action |= TCA_CT_ACT_NAT;
+
+                       NEXT_ARG();
+                       if (matches(*argv, "src") == 0)
+                               ct_action |= TCA_CT_ACT_NAT_SRC;
+                       else if (matches(*argv, "dst") == 0)
+                               ct_action |= TCA_CT_ACT_NAT_DST;
+                       else
+                               continue;
+
+                       NEXT_ARG();
+                       if (matches(*argv, "addr") != 0)
+                               usage();
+
+                       NEXT_ARG();
+                       ret = ct_parse_nat_addr_range(*argv, n);
+                       if (ret) {
+                               fprintf(stderr, "ct: Illegal nat address range\n");
+                               return -1;
+                       }
+
+                       NEXT_ARG_FWD();
+                       if (matches(*argv, "port") != 0)
+                               continue;
+
+                       NEXT_ARG();
+                       ret = ct_parse_nat_port_range(*argv, n);
+                       if (ret) {
+                               fprintf(stderr, "ct: Illegal nat port range\n");
+                               return -1;
+                       }
+               } else if (matches(*argv, "clear") == 0) {
+                       ct_action |= TCA_CT_ACT_CLEAR;
+               } else if (matches(*argv, "commit") == 0) {
+                       ct_action |= TCA_CT_ACT_COMMIT;
+               } else if (matches(*argv, "force") == 0) {
+                       ct_action |= TCA_CT_ACT_FORCE;
+               } else if (matches(*argv, "index") == 0) {
+                       NEXT_ARG();
+                       if (get_u32(&sel.index, *argv, 10)) {
+                               fprintf(stderr, "ct: Illegal \"index\"\n");
+                               return -1;
+                       }
+               } else if (matches(*argv, "mark") == 0) {
+                       NEXT_ARG();
+
+                       ret = ct_parse_mark(*argv, n);
+                       if (ret) {
+                               fprintf(stderr, "ct: Illegal \"mark\"\n");
+                               return -1;
+                       }
+               } else if (matches(*argv, "label") == 0) {
+                       NEXT_ARG();
+
+                       ret = ct_parse_labels(*argv, n);
+                       if (ret) {
+                               fprintf(stderr, "ct: Illegal \"label\"\n");
+                               return -1;
+                       }
+               } else if (matches(*argv, "help") == 0) {
+                       usage();
+               } else {
+                       break;
+               }
+               NEXT_ARG_FWD();
+       }
+
+       if (ct_action & TCA_CT_ACT_CLEAR &&
+           ct_action & ~TCA_CT_ACT_CLEAR) {
+               fprintf(stderr, "ct: clear can only be used alone\n");
+               return -1;
+       }
+
+       if (ct_action & TCA_CT_ACT_NAT_SRC &&
+           ct_action & TCA_CT_ACT_NAT_DST) {
+               fprintf(stderr, "ct: src and dst nat can't be used together\n");
+               return -1;
+       }
+
+       if ((ct_action & TCA_CT_ACT_COMMIT) &&
+           (ct_action & TCA_CT_ACT_NAT) &&
+           !(ct_action & (TCA_CT_ACT_NAT_SRC | TCA_CT_ACT_NAT_DST))) {
+               fprintf(stderr, "ct: commit and nat must set src or dst\n");
+               return -1;
+       }
+
+       if (!(ct_action & TCA_CT_ACT_COMMIT) &&
+           (ct_action & (TCA_CT_ACT_NAT_SRC | TCA_CT_ACT_NAT_DST))) {
+               fprintf(stderr, "ct: src or dst is only valid if commit is set\n");
+               return -1;
+       }
+
+       parse_action_control_dflt(&argc, &argv, &sel.action, false,
+                                 TC_ACT_PIPE);
+       NEXT_ARG_FWD();
+
+       addattr16(n, MAX_MSG, TCA_CT_ACTION, ct_action);
+       addattr_l(n, MAX_MSG, TCA_CT_PARMS, &sel, sizeof(sel));
+       addattr_nest_end(n, tail);
+
+       *argc_p = argc;
+       *argv_p = argv;
+       return 0;
+}
+
+static int ct_sprint_port(char *buf, const char *prefix, struct rtattr *attr)
+{
+       if (!attr)
+               return 0;
+
+       return sprintf(buf, "%s%d", prefix, rta_getattr_be16(attr));
+}
+
+static int ct_sprint_ip_addr(char *buf, const char *prefix,
+                            struct rtattr *attr)
+{
+       int family;
+       size_t len;
+
+       if (!attr)
+               return 0;
+
+       len = RTA_PAYLOAD(attr);
+
+       if (len == 4)
+               family = AF_INET;
+       else if (len == 16)
+               family = AF_INET6;
+       else
+               return 0;
+
+       return sprintf(buf, "%s%s", prefix, rt_addr_n2a_rta(family, attr));
+}
+
+static void ct_print_nat(int ct_action, struct rtattr **tb)
+{
+       size_t done = 0;
+       char out[256] = "";
+       bool nat;
+
+       if (!(ct_action & TCA_CT_ACT_NAT))
+               return;
+
+       if (ct_action & TCA_CT_ACT_NAT_SRC) {
+               nat = true;
+               done += sprintf(out + done, "src");
+       } else if (ct_action & TCA_CT_ACT_NAT_DST) {
+               nat = true;
+               done += sprintf(out + done, "dst");
+       }
+
+       if (nat) {
+               done += ct_sprint_ip_addr(out + done, " addr ",
+                                         tb[TCA_CT_NAT_IPV4_MIN]);
+               done += ct_sprint_ip_addr(out + done, " addr ",
+                                         tb[TCA_CT_NAT_IPV6_MIN]);
+               if (tb[TCA_CT_NAT_IPV4_MAX] &&
+                   memcmp(RTA_DATA(tb[TCA_CT_NAT_IPV4_MIN]),
+                          RTA_DATA(tb[TCA_CT_NAT_IPV4_MAX]), 4))
+                       done += ct_sprint_ip_addr(out + done, "-",
+                                                 tb[TCA_CT_NAT_IPV4_MAX]);
+               else if (tb[TCA_CT_NAT_IPV6_MAX] &&
+                           memcmp(RTA_DATA(tb[TCA_CT_NAT_IPV6_MIN]),
+                                  RTA_DATA(tb[TCA_CT_NAT_IPV6_MAX]), 16))
+                       done += ct_sprint_ip_addr(out + done, "-",
+                                                 tb[TCA_CT_NAT_IPV6_MAX]);
+               done += ct_sprint_port(out + done, " port ",
+                                      tb[TCA_CT_NAT_PORT_MIN]);
+               if (tb[TCA_CT_NAT_PORT_MAX] &&
+                   memcmp(RTA_DATA(tb[TCA_CT_NAT_PORT_MIN]),
+                          RTA_DATA(tb[TCA_CT_NAT_PORT_MAX]), 2))
+                       done += ct_sprint_port(out + done, "-",
+                                              tb[TCA_CT_NAT_PORT_MAX]);
+       }
+
+       if (done)
+               print_string(PRINT_ANY, "nat", " nat %s", out);
+       else
+               print_string(PRINT_ANY, "nat", " nat", "");
+}
+
+static void ct_print_labels(struct rtattr *attr,
+                           struct rtattr *mask_attr)
+{
+       const unsigned char *str;
+       bool print_mask = false;
+       char out[256], *p;
+       int data_len, i;
+
+       if (!attr)
+               return;
+
+       data_len = RTA_PAYLOAD(attr);
+       hexstring_n2a(RTA_DATA(attr), data_len, out, sizeof(out));
+       p = out + data_len*2;
+
+       data_len = RTA_PAYLOAD(attr);
+       str = RTA_DATA(mask_attr);
+       if (data_len != 16)
+               print_mask = true;
+       for (i = 0; !print_mask && i < data_len; i++) {
+               if (str[i] != 0xff)
+                       print_mask = true;
+       }
+       if (print_mask) {
+               *p++ = '/';
+               hexstring_n2a(RTA_DATA(mask_attr), data_len, p,
+                             sizeof(out)-(p-out));
+               p += data_len*2;
+       }
+       *p = '\0';
+
+       print_string(PRINT_ANY, "label", " label %s", out);
+}
+
+static int print_ct(struct action_util *au, FILE *f, struct rtattr *arg)
+{
+       struct rtattr *tb[TCA_CT_MAX + 1];
+       const char *commit;
+       struct tc_ct *p;
+       int ct_action = 0;
+
+       if (arg == NULL)
+               return -1;
+
+       parse_rtattr_nested(tb, TCA_CT_MAX, arg);
+       if (tb[TCA_CT_PARMS] == NULL) {
+               print_string(PRINT_FP, NULL, "%s", "[NULL ct parameters]");
+               return -1;
+       }
+
+       p = RTA_DATA(tb[TCA_CT_PARMS]);
+
+       print_string(PRINT_ANY, "kind", "%s", "ct");
+
+       if (tb[TCA_CT_ACTION])
+               ct_action = rta_getattr_u16(tb[TCA_CT_ACTION]);
+       if (ct_action & TCA_CT_ACT_COMMIT) {
+               commit = ct_action & TCA_CT_ACT_FORCE ?
+                        "commit force" : "commit";
+               print_string(PRINT_ANY, "action", " %s", commit);
+       } else if (ct_action & TCA_CT_ACT_CLEAR) {
+               print_string(PRINT_ANY, "action", " %s", "clear");
+       }
+
+       print_masked_u32("mark", tb[TCA_CT_MARK], tb[TCA_CT_MARK_MASK]);
+       print_masked_u16("zone", tb[TCA_CT_ZONE], NULL);
+       ct_print_labels(tb[TCA_CT_LABELS], tb[TCA_CT_LABELS_MASK]);
+       ct_print_nat(ct_action, tb);
+
+       print_action_control(f, " ", p->action, "");
+
+       print_uint(PRINT_ANY, "index", "\n\t index %u", p->index);
+       print_int(PRINT_ANY, "ref", " ref %d", p->refcnt);
+       print_int(PRINT_ANY, "bind", " bind %d", p->bindcnt);
+
+       if (show_stats) {
+               if (tb[TCA_CT_TM]) {
+                       struct tcf_t *tm = RTA_DATA(tb[TCA_CT_TM]);
+
+                       print_tm(f, tm);
+               }
+       }
+       print_string(PRINT_FP, NULL, "%s", "\n ");
+
+       return 0;
+}
+
+struct action_util ct_action_util = {
+       .id = "ct",
+       .parse_aopt = parse_ct,
+       .print_aopt = print_ct,
+};
index 76aca476c61d490bb017246ba3987bd8ae7ee0e1..c2090589bc64595c61843279fada9bea04d38c2d 100644 (file)
@@ -130,6 +130,13 @@ static int etf_parse_opt(struct qdisc_util *qu, int argc,
                                explain_clockid(*argv);
                                return -1;
                        }
+               } else if (strcmp(*argv, "skip_sock_check") == 0) {
+                       if (opt.flags & TC_ETF_SKIP_SOCK_CHECK) {
+                               fprintf(stderr, "etf: duplicate \"skip_sock_check\" specification\n");
+                               return -1;
+                       }
+
+                       opt.flags |= TC_ETF_SKIP_SOCK_CHECK;
                } else if (strcmp(*argv, "help") == 0) {
                        explain();
                        return -1;
@@ -171,8 +178,10 @@ static int etf_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
        print_uint(PRINT_ANY, "delta", "delta %d ", qopt->delta);
        print_string(PRINT_ANY, "offload", "offload %s ",
                                (qopt->flags & TC_ETF_OFFLOAD_ON) ? "on" : "off");
-       print_string(PRINT_ANY, "deadline_mode", "deadline_mode %s",
+       print_string(PRINT_ANY, "deadline_mode", "deadline_mode %s ",
                                (qopt->flags & TC_ETF_DEADLINE_MODE_ON) ? "on" : "off");
+       print_string(PRINT_ANY, "skip_sock_check", "skip_sock_check %s",
+                               (qopt->flags & TC_ETF_SKIP_SOCK_CHECK) ? "on" : "off");
 
        return 0;
 }
index 62c8c591da994167c4d1b32f90b316818d69e151..b9954436b0f9fd66fbf788559dd10b42673da5d9 100644 (file)
@@ -52,7 +52,7 @@ static void explain(void)
                "               [num_tc NUMBER] [map P0 P1 ...] "
                "               [queues COUNT@OFFSET COUNT@OFFSET COUNT@OFFSET ...] "
                "               [ [sched-entry index cmd gate-mask interval] ... ] "
-               "               [base-time time] "
+               "               [base-time time] [txtime-delay delay]"
                "\n"
                "CLOCKID must be a valid SYS-V id (i.e. CLOCK_TAI)\n");
 }
@@ -159,6 +159,8 @@ static int taprio_parse_opt(struct qdisc_util *qu, int argc,
        __s64 cycle_time_extension = 0;
        struct list_head sched_entries;
        struct rtattr *tail, *l;
+       __u32 taprio_flags = 0;
+       __u32 txtime_delay = 0;
        __s64 cycle_time = 0;
        __s64 base_time = 0;
        int err, idx;
@@ -281,6 +283,28 @@ static int taprio_parse_opt(struct qdisc_util *qu, int argc,
                                explain_clockid(*argv);
                                return -1;
                        }
+               } else if (strcmp(*argv, "flags") == 0) {
+                       NEXT_ARG();
+                       if (taprio_flags) {
+                               fprintf(stderr, "taprio: duplicate \"flags\" specification\n");
+                               return -1;
+                       }
+                       if (get_u32(&taprio_flags, *argv, 0)) {
+                               PREV_ARG();
+                               return -1;
+                       }
+
+               } else if (strcmp(*argv, "txtime-delay") == 0) {
+                       NEXT_ARG();
+                       if (txtime_delay != 0) {
+                               fprintf(stderr, "taprio: duplicate \"txtime-delay\" specification\n");
+                               return -1;
+                       }
+                       if (get_u32(&txtime_delay, *argv, 0)) {
+                               PREV_ARG();
+                               return -1;
+                       }
+
                } else if (strcmp(*argv, "help") == 0) {
                        explain();
                        return -1;
@@ -297,9 +321,15 @@ static int taprio_parse_opt(struct qdisc_util *qu, int argc,
        if (clockid != CLOCKID_INVALID)
                addattr_l(n, 1024, TCA_TAPRIO_ATTR_SCHED_CLOCKID, &clockid, sizeof(clockid));
 
+       if (taprio_flags)
+               addattr_l(n, 1024, TCA_TAPRIO_ATTR_FLAGS, &taprio_flags, sizeof(taprio_flags));
+
        if (opt.num_tc > 0)
                addattr_l(n, 1024, TCA_TAPRIO_ATTR_PRIOMAP, &opt, sizeof(opt));
 
+       if (txtime_delay)
+               addattr_l(n, 1024, TCA_TAPRIO_ATTR_TXTIME_DELAY, &txtime_delay, sizeof(txtime_delay));
+
        if (base_time)
                addattr_l(n, 1024, TCA_TAPRIO_ATTR_SCHED_BASE_TIME, &base_time, sizeof(base_time));
 
@@ -442,6 +472,20 @@ static int taprio_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
 
        print_string(PRINT_ANY, "clockid", "clockid %s", get_clock_name(clockid));
 
+       if (tb[TCA_TAPRIO_ATTR_FLAGS]) {
+               __u32 flags;
+
+               flags = rta_getattr_u32(tb[TCA_TAPRIO_ATTR_FLAGS]);
+               print_0xhex(PRINT_ANY, "flags", " flags %#x", flags);
+       }
+
+       if (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]) {
+               __u32 txtime_delay;
+
+               txtime_delay = rta_getattr_s32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]);
+               print_uint(PRINT_ANY, "txtime_delay", " txtime delay %d", txtime_delay);
+       }
+
        print_schedule(f, tb);
 
        if (tb[TCA_TAPRIO_ATTR_ADMIN_SCHED]) {
index b90d256c33a4ab4ca432d396f635497597b42195..0eb530408d056eab82748c4195f49079959e7692 100644 (file)
@@ -914,3 +914,47 @@ compat_xstats:
        if (tb[TCA_XSTATS] && xstats)
                *xstats = tb[TCA_XSTATS];
 }
+
+void print_masked_u32(const char *name, struct rtattr *attr,
+                     struct rtattr *mask_attr)
+{
+       __u32 value, mask;
+       SPRINT_BUF(namefrm);
+       SPRINT_BUF(out);
+       size_t done;
+
+       if (!attr)
+               return;
+
+       value = rta_getattr_u32(attr);
+       mask = mask_attr ? rta_getattr_u32(mask_attr) : UINT32_MAX;
+
+       done = sprintf(out, "%u", value);
+       if (mask != UINT32_MAX)
+               sprintf(out + done, "/0x%x", mask);
+
+       sprintf(namefrm, " %s %%s", name);
+       print_string(PRINT_ANY, name, namefrm, out);
+}
+
+void print_masked_u16(const char *name, struct rtattr *attr,
+                     struct rtattr *mask_attr)
+{
+       __u16 value, mask;
+       SPRINT_BUF(namefrm);
+       SPRINT_BUF(out);
+       size_t done;
+
+       if (!attr)
+               return;
+
+       value = rta_getattr_u16(attr);
+       mask = mask_attr ? rta_getattr_u16(mask_attr) : UINT16_MAX;
+
+       done = sprintf(out, "%u", value);
+       if (mask != UINT16_MAX)
+               sprintf(out + done, "/0x%x", mask);
+
+       sprintf(namefrm, " %s %%s", name);
+       print_string(PRINT_ANY, name, namefrm, out);
+}
index eb4b60db3fdd7bc26181400abf088a81673b06bb..0c3425abc62fad85a6b6b8310031e9968cccc7f7 100644 (file)
@@ -127,4 +127,8 @@ int action_a2n(char *arg, int *result, bool allow_num);
 
 bool tc_qdisc_block_exists(__u32 block_index);
 
+void print_masked_u32(const char *name, struct rtattr *attr,
+                     struct rtattr *mask_attr);
+void print_masked_u16(const char *name, struct rtattr *attr,
+                     struct rtattr *mask_attr);
 #endif