--- /dev/null
+/* 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 */
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)
return -1;
print_tunnel(&p);
- fputc('\n', stdout);
return 0;
}
{
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] = {};
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();
}
}
}
+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_);
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;
fprintf(stderr, "Dump terminated\n");
return -1;
}
+ delete_json_obj();
return 0;
}
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
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
.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
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
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>
"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";
}
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
" 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"
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)
{
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))
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)
{
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]) {
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) {
--- /dev/null
+// 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,
+};
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;
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;
}
" [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");
}
__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;
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;
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));
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]) {
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);
+}
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