diff --git a/SOURCES/0031-devlink-Add-health-error-recovery-status-monitoring.patch b/SOURCES/0031-devlink-Add-health-error-recovery-status-monitoring.patch new file mode 100644 index 0000000..813691b --- /dev/null +++ b/SOURCES/0031-devlink-Add-health-error-recovery-status-monitoring.patch @@ -0,0 +1,116 @@ +From 81a035c029c2ef108da0f854c5a670aa3f06bb94 Mon Sep 17 00:00:00 2001 +From: Andrea Claudi +Date: Wed, 17 Jun 2020 12:10:11 +0200 +Subject: [PATCH] devlink: Add health error recovery status monitoring + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1821039 +Upstream Status: iproute2.git commit 5023df6a21c73 +Conflicts: context change due to missing commit + - ef12d6dafaeb9 ("devlink: Add devlink trap set and show commands") + - 4ede9e9d56206 ("devlink: Add devlink trap group set and show commands") + - a66af5569337b ("devlink: Add devlink trap policer set and show commands") + +commit 5023df6a21c73560b514d7fde5381d140373afe9 +Author: Moshe Shemesh +Date: Tue Feb 4 23:37:02 2020 +0200 + + devlink: Add health error recovery status monitoring + + Add support for devlink health error recovery status monitoring. + Update devlink-monitor man page accordingly. + + Signed-off-by: Moshe Shemesh + Acked-by: Jiri Pirko + Signed-off-by: David Ahern +--- + devlink/devlink.c | 17 +++++++++++++++-- + man/man8/devlink-monitor.8 | 3 ++- + 2 files changed, 17 insertions(+), 3 deletions(-) + +diff --git a/devlink/devlink.c b/devlink/devlink.c +index 0293373928f50..f7e859c266394 100644 +--- a/devlink/devlink.c ++++ b/devlink/devlink.c +@@ -3764,6 +3764,7 @@ static const char *cmd_name(uint8_t cmd) + case DEVLINK_CMD_REGION_SET: return "set"; + case DEVLINK_CMD_REGION_NEW: return "new"; + case DEVLINK_CMD_REGION_DEL: return "del"; ++ case DEVLINK_CMD_HEALTH_REPORTER_RECOVER: return "status"; + default: return ""; + } + } +@@ -3792,6 +3793,8 @@ static const char *cmd_obj(uint8_t cmd) + case DEVLINK_CMD_REGION_NEW: + case DEVLINK_CMD_REGION_DEL: + return "region"; ++ case DEVLINK_CMD_HEALTH_REPORTER_RECOVER: ++ return "health"; + default: return ""; + } + } +@@ -3817,6 +3820,7 @@ static bool cmd_filter_check(struct dl *dl, uint8_t cmd) + } + + static void pr_out_region(struct dl *dl, struct nlattr **tb); ++static void pr_out_health(struct dl *dl, struct nlattr **tb_health); + + static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) + { +@@ -3872,6 +3876,14 @@ static int cmd_mon_show_cb(const struct nlmsghdr *nlh, void *data) + pr_out_mon_header(genl->cmd); + pr_out_region(dl, tb); + break; ++ case DEVLINK_CMD_HEALTH_REPORTER_RECOVER: ++ mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb); ++ if (!tb[DEVLINK_ATTR_BUS_NAME] || !tb[DEVLINK_ATTR_DEV_NAME] || ++ !tb[DEVLINK_ATTR_HEALTH_REPORTER]) ++ return MNL_CB_ERROR; ++ pr_out_mon_header(genl->cmd); ++ pr_out_health(dl, tb); ++ break; + } + return MNL_CB_OK; + } +@@ -3885,7 +3897,8 @@ static int cmd_mon_show(struct dl *dl) + while ((cur_obj = dl_argv_index(dl, index++))) { + if (strcmp(cur_obj, "all") != 0 && + strcmp(cur_obj, "dev") != 0 && +- strcmp(cur_obj, "port") != 0) { ++ strcmp(cur_obj, "port") != 0 && ++ strcmp(cur_obj, "health") != 0) { + pr_err("Unknown object \"%s\"\n", cur_obj); + return -EINVAL; + } +@@ -3902,7 +3915,7 @@ static int cmd_mon_show(struct dl *dl) + static void cmd_mon_help(void) + { + pr_err("Usage: devlink monitor [ all | OBJECT-LIST ]\n" +- "where OBJECT-LIST := { dev | port }\n"); ++ "where OBJECT-LIST := { dev | port | health }\n"); + } + + static int cmd_mon(struct dl *dl) +diff --git a/man/man8/devlink-monitor.8 b/man/man8/devlink-monitor.8 +index 13fe641dc8f54..9bc579673ebf4 100644 +--- a/man/man8/devlink-monitor.8 ++++ b/man/man8/devlink-monitor.8 +@@ -21,7 +21,7 @@ command is the first in the command line and then the object list. + .I OBJECT-LIST + is the list of object types that we want to monitor. + It may contain +-.BR dev ", " port ". ++.BR dev ", " port ", " health ". + + .B devlink + opens Devlink Netlink socket, listens on it and dumps state changes. +@@ -31,6 +31,7 @@ opens Devlink Netlink socket, listens on it and dumps state changes. + .BR devlink-dev (8), + .BR devlink-sb (8), + .BR devlink-port (8), ++.BR devlink-health (8), + .br + + .SH AUTHOR +-- +2.26.2 + diff --git a/SOURCES/0032-ss-allow-dumping-kTLS-info.patch b/SOURCES/0032-ss-allow-dumping-kTLS-info.patch new file mode 100644 index 0000000..9fe02ae --- /dev/null +++ b/SOURCES/0032-ss-allow-dumping-kTLS-info.patch @@ -0,0 +1,139 @@ +From 06bd12bd8e48182f7f3293bbec187b4e90da796f Mon Sep 17 00:00:00 2001 +From: Andrea Claudi +Date: Mon, 29 Jun 2020 14:36:51 +0200 +Subject: [PATCH] ss: allow dumping kTLS info + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1812207 +Upstream Status: iproute2.git commit 14cadc707b919 +Conflicts: due to out-of-order cherry-pick of commit + 712fdd98c0839 ("ss: allow dumping MPTCP subflow information") + +commit 14cadc707b919914e9a2d5dffad9232c3ae97c5f +Author: Davide Caratti +Date: Mon Oct 7 12:16:44 2019 +0200 + + ss: allow dumping kTLS info + + now that INET_DIAG_INFO requests can dump TCP ULP information, extend 'ss' + to allow diagnosing kTLS when it is attached to a TCP socket. While at it, + import kTLS uAPI definitions from the latest net-next tree. + + CC: Andrea Claudi + Co-developed-by: Jakub Kicinski + Signed-off-by: Jakub Kicinski + Signed-off-by: Davide Caratti + Signed-off-by: David Ahern +--- + misc/ss.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 82 insertions(+) + +diff --git a/misc/ss.c b/misc/ss.c +index 3d565af86087c..8285382bd6c4a 100644 +--- a/misc/ss.c ++++ b/misc/ss.c +@@ -51,6 +51,7 @@ + #include + #include + #include ++#include + #include + + /* AF_VSOCK/PF_VSOCK is only provided since glibc 2.18 */ +@@ -2752,6 +2753,72 @@ static void print_md5sig(struct tcp_diag_md5sig *sig) + print_escape_buf(sig->tcpm_key, sig->tcpm_keylen, " ,"); + } + ++static void tcp_tls_version(struct rtattr *attr) ++{ ++ u_int16_t val; ++ ++ if (!attr) ++ return; ++ val = rta_getattr_u16(attr); ++ ++ switch (val) { ++ case TLS_1_2_VERSION: ++ out(" version: 1.2"); ++ break; ++ case TLS_1_3_VERSION: ++ out(" version: 1.3"); ++ break; ++ default: ++ out(" version: unknown(%hu)", val); ++ break; ++ } ++} ++ ++static void tcp_tls_cipher(struct rtattr *attr) ++{ ++ u_int16_t val; ++ ++ if (!attr) ++ return; ++ val = rta_getattr_u16(attr); ++ ++ switch (val) { ++ case TLS_CIPHER_AES_GCM_128: ++ out(" cipher: aes-gcm-128"); ++ break; ++ case TLS_CIPHER_AES_GCM_256: ++ out(" cipher: aes-gcm-256"); ++ break; ++ } ++} ++ ++static void tcp_tls_conf(const char *name, struct rtattr *attr) ++{ ++ u_int16_t val; ++ ++ if (!attr) ++ return; ++ val = rta_getattr_u16(attr); ++ ++ switch (val) { ++ case TLS_CONF_BASE: ++ out(" %s: none", name); ++ break; ++ case TLS_CONF_SW: ++ out(" %s: sw", name); ++ break; ++ case TLS_CONF_HW: ++ out(" %s: hw", name); ++ break; ++ case TLS_CONF_HW_RECORD: ++ out(" %s: hw-record", name); ++ break; ++ default: ++ out(" %s: unknown(%hu)", name, val); ++ break; ++ } ++} ++ + static void mptcp_subflow_info(struct rtattr *tb[]) + { + u_int32_t flags = 0; +@@ -2966,6 +3033,21 @@ static void tcp_show_info(const struct nlmsghdr *nlh, struct inet_diag_msg *r, + parse_rtattr_nested(ulpinfo, INET_ULP_INFO_MAX, + tb[INET_DIAG_ULP_INFO]); + ++ if (ulpinfo[INET_ULP_INFO_NAME]) ++ out(" tcp-ulp-%s", ++ rta_getattr_str(ulpinfo[INET_ULP_INFO_NAME])); ++ ++ if (ulpinfo[INET_ULP_INFO_TLS]) { ++ struct rtattr *tlsinfo[TLS_INFO_MAX + 1] = { 0 }; ++ ++ parse_rtattr_nested(tlsinfo, TLS_INFO_MAX, ++ ulpinfo[INET_ULP_INFO_TLS]); ++ ++ tcp_tls_version(tlsinfo[TLS_INFO_VERSION]); ++ tcp_tls_cipher(tlsinfo[TLS_INFO_CIPHER]); ++ tcp_tls_conf("rxconf", tlsinfo[TLS_INFO_RXCONF]); ++ tcp_tls_conf("txconf", tlsinfo[TLS_INFO_TXCONF]); ++ } + if (ulpinfo[INET_ULP_INFO_MPTCP]) { + struct rtattr *sfinfo[MPTCP_SUBFLOW_ATTR_MAX + 1] = + { 0 }; +-- +2.26.2 + diff --git a/SOURCES/0033-Import-tc_act-tc_ct.h-uapi-file.patch b/SOURCES/0033-Import-tc_act-tc_ct.h-uapi-file.patch new file mode 100644 index 0000000..f3c473d --- /dev/null +++ b/SOURCES/0033-Import-tc_act-tc_ct.h-uapi-file.patch @@ -0,0 +1,73 @@ +From 4c330f41bce887a4a06d6c84c62a3a8c1b0b5160 Mon Sep 17 00:00:00 2001 +From: Andrea Claudi +Date: Tue, 9 Jun 2020 15:45:55 +0200 +Subject: [PATCH] Import tc_act/tc_ct.h uapi file + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1844637 +Upstream Status: iproute2.git commit f47081befffc5 + +commit f47081befffc50a5eef734d0a6654b59047e7808 +Author: David Ahern +Date: Thu Jul 18 15:40:07 2019 -0700 + + Import tc_act/tc_ct.h uapi file + + Import include/uapi/linux/tc_act/tc_ct.h header from commit of last + kernel headers sync. + + Signed-off-by: David Ahern +--- + include/uapi/linux/tc_act/tc_ct.h | 41 +++++++++++++++++++++++++++++++ + 1 file changed, 41 insertions(+) + create mode 100644 include/uapi/linux/tc_act/tc_ct.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 +index 0000000000000..5fb1d7ac10272 +--- /dev/null ++++ b/include/uapi/linux/tc_act/tc_ct.h +@@ -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 ++#include ++ ++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 */ +-- +2.26.2 + diff --git a/SOURCES/0034-tc-add-NLA_F_NESTED-flag-to-all-actions-options-nest.patch b/SOURCES/0034-tc-add-NLA_F_NESTED-flag-to-all-actions-options-nest.patch new file mode 100644 index 0000000..5832ac4 --- /dev/null +++ b/SOURCES/0034-tc-add-NLA_F_NESTED-flag-to-all-actions-options-nest.patch @@ -0,0 +1,40 @@ +From bf795d2418ee298ddc73171829d6dc4914c22a46 Mon Sep 17 00:00:00 2001 +From: Andrea Claudi +Date: Tue, 9 Jun 2020 15:45:56 +0200 +Subject: [PATCH] tc: add NLA_F_NESTED flag to all actions options nested block + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1844637 +Upstream Status: iproute2.git commit 18aa9f5583e94 + +commit 18aa9f5583e94abc7204b2376b819ede1180da97 +Author: Paul Blakey +Date: Thu Jul 11 11:14:25 2019 +0300 + + tc: add NLA_F_NESTED flag to all actions options nested block + + Strict netlink validation now requires this flag on all nested + attributes, add it for action options. + + Signed-off-by: Paul Blakey + Signed-off-by: David Ahern +--- + tc/m_action.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/tc/m_action.c b/tc/m_action.c +index c46aeaafa8ebf..4da810c8c0aa7 100644 +--- a/tc/m_action.c ++++ b/tc/m_action.c +@@ -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) { +-- +2.26.2 + diff --git a/SOURCES/0035-tc-Introduce-tc-ct-action.patch b/SOURCES/0035-tc-Introduce-tc-ct-action.patch new file mode 100644 index 0000000..3971adf --- /dev/null +++ b/SOURCES/0035-tc-Introduce-tc-ct-action.patch @@ -0,0 +1,621 @@ +From ef66b6a546f3f1fd517cfa306cc347ad096bd932 Mon Sep 17 00:00:00 2001 +From: Andrea Claudi +Date: Tue, 9 Jun 2020 15:45:56 +0200 +Subject: [PATCH] tc: Introduce tc ct action + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1844637 +Upstream Status: iproute2.git commit c8a494314c400 + +commit c8a494314c400eb023d7555933ba8ab40345519b +Author: Paul Blakey +Date: Thu Jul 11 11:14:26 2019 +0300 + + tc: Introduce tc ct action + + New tc action to send packets to conntrack module, commit + them, and set a zone, labels, mark, and nat on the connection. + + It can also clear the packet's conntrack state by using clear. + + Usage: + ct clear + ct commit [force] [zone] [mark] [label] [nat] + ct [nat] [zone] + + Signed-off-by: Paul Blakey + Signed-off-by: Marcelo Ricardo Leitner + Signed-off-by: Yossi Kuperman + Acked-by: Jiri Pirko + Acked-by: Roi Dayan + Signed-off-by: David Ahern +--- + tc/Makefile | 1 + + tc/m_ct.c | 497 +++++++++++++++++++++++++++++++++++++++++++++++++++ + tc/tc_util.c | 44 +++++ + tc/tc_util.h | 4 + + 4 files changed, 546 insertions(+) + create mode 100644 tc/m_ct.c + +diff --git a/tc/Makefile b/tc/Makefile +index 09ff3692b1663..14171a28cba5d 100644 +--- a/tc/Makefile ++++ b/tc/Makefile +@@ -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 +diff --git a/tc/m_ct.c b/tc/m_ct.c +new file mode 100644 +index 0000000000000..8589cb9a3c515 +--- /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 ++ * Yossi Kuperman ++ * Marcelo Ricardo Leitner ++ */ ++ ++#include ++#include ++#include ++#include ++#include "utils.h" ++#include "tc_util.h" ++#include ++ ++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, ++}; +diff --git a/tc/tc_util.c b/tc/tc_util.c +index b90d256c33a4a..0eb530408d056 100644 +--- a/tc/tc_util.c ++++ b/tc/tc_util.c +@@ -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); ++} +diff --git a/tc/tc_util.h b/tc/tc_util.h +index eb4b60db3fdd7..0c3425abc62fa 100644 +--- a/tc/tc_util.h ++++ b/tc/tc_util.h +@@ -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 +-- +2.26.2 + diff --git a/SOURCES/0036-tc-flower-Add-matching-on-conntrack-info.patch b/SOURCES/0036-tc-flower-Add-matching-on-conntrack-info.patch new file mode 100644 index 0000000..9de9989 --- /dev/null +++ b/SOURCES/0036-tc-flower-Add-matching-on-conntrack-info.patch @@ -0,0 +1,393 @@ +From b60d960c65c6aa5695ecf1c71b18790ca0cb475c Mon Sep 17 00:00:00 2001 +From: Andrea Claudi +Date: Tue, 9 Jun 2020 15:45:56 +0200 +Subject: [PATCH] tc: flower: Add matching on conntrack info + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1844637 +Upstream Status: iproute2.git commit 2fffb1c03056e + +commit 2fffb1c03056e71d49d623f7ca460883fa6110a6 +Author: Paul Blakey +Date: Thu Jul 11 11:14:27 2019 +0300 + + tc: flower: Add matching on conntrack info + + Matches on conntrack state, zone, mark, and label. + + Signed-off-by: Paul Blakey + Signed-off-by: Marcelo Ricardo Leitner + Signed-off-by: Yossi Kuperman + Acked-by: Jiri Pirko + Acked-by: Roi Dayan + Signed-off-by: David Ahern +--- + man/man8/tc-flower.8 | 35 ++++++ + tc/f_flower.c | 276 ++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 310 insertions(+), 1 deletion(-) + +diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8 +index f41b0f7f1ef13..0f95f303f23b7 100644 +--- a/man/man8/tc-flower.8 ++++ b/man/man8/tc-flower.8 +@@ -295,6 +295,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" + .TQ + .BI vxlan_opts " OPTIONS" +diff --git a/tc/f_flower.c b/tc/f_flower.c +index 691541ec59d4c..0a6eaa0df94ce 100644 +--- a/tc/f_flower.c ++++ b/tc/f_flower.c +@@ -84,9 +84,14 @@ static void explain(void) + " vxlan_opts MASKED-OPTIONS |\n" + " erspan_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" +@@ -216,6 +221,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) + { +@@ -1077,6 +1235,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)) +@@ -1783,6 +1969,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) + { +@@ -2218,6 +2483,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]) { +-- +2.26.2 + diff --git a/SOURCES/0037-man-tc-ct.8-Add-manual-page-for-ct-tc-action.patch b/SOURCES/0037-man-tc-ct.8-Add-manual-page-for-ct-tc-action.patch new file mode 100644 index 0000000..5888574 --- /dev/null +++ b/SOURCES/0037-man-tc-ct.8-Add-manual-page-for-ct-tc-action.patch @@ -0,0 +1,154 @@ +From 7c371119412595ad2d063b91fdea616dcacb4eed Mon Sep 17 00:00:00 2001 +From: Andrea Claudi +Date: Tue, 9 Jun 2020 15:45:56 +0200 +Subject: [PATCH] man: tc-ct.8: Add manual page for ct tc action + +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1844637 +Upstream Status: iproute2.git commit 924c43778a845 + +commit 924c43778a8453e2cd0fd1440b9224bed9c87c0d +Author: Paul Blakey +Date: Thu May 14 17:10:20 2020 +0300 + + man: tc-ct.8: Add manual page for ct tc action + + Signed-off-by: Paul Blakey + Signed-off-by: Stephen Hemminger +--- + man/man8/tc-ct.8 | 107 +++++++++++++++++++++++++++++++++++++++++++ + man/man8/tc-flower.8 | 6 +++ + 2 files changed, 113 insertions(+) + create mode 100644 man/man8/tc-ct.8 + +diff --git a/man/man8/tc-ct.8 b/man/man8/tc-ct.8 +new file mode 100644 +index 0000000000000..45d29320f1d01 +--- /dev/null ++++ b/man/man8/tc-ct.8 +@@ -0,0 +1,107 @@ ++.TH "ct action in tc" 8 "14 May 2020" "iproute2" "Linux" ++.SH NAME ++ct \- tc connection tracking action ++.SH SYNOPSIS ++.in +8 ++.ti -8 ++.BR "tc ... action ct commit [ force ] [ zone " ++.IR ZONE ++.BR "] [ mark " ++.IR MASKED_MARK ++.BR "] [ label " ++.IR MASKED_LABEL ++.BR "] [ nat " ++.IR NAT_SPEC ++.BR "]" ++ ++.ti -8 ++.BR "tc ... action ct [ nat ] [ zone " ++.IR ZONE ++.BR "]" ++ ++.ti -8 ++.BR "tc ... action ct clear" ++ ++.SH DESCRIPTION ++The ct action is a tc action for sending packets and interacting with the netfilter conntrack module. ++ ++It can (as shown in the synopsis, in order): ++ ++Send the packet to conntrack, and commit the connection, while configuring ++a 32bit mark, 128bit label, and src/dst nat. ++ ++Send the packet to conntrack, which will mark the packet with the connection's state and ++configured metadata (mark/label), and execute previous configured nat. ++ ++Clear the packet's of previous connection tracking state. ++ ++.SH OPTIONS ++.TP ++.BI zone " ZONE" ++Specify a conntrack zone number on which to send the packet to conntrack. ++.TP ++.BI mark " MASKED_MARK" ++Specify a masked 32bit mark to set for the connection (only valid with commit). ++.TP ++.BI label " MASKED_LABEL" ++Specify a masked 128bit label to set for the connection (only valid with commit). ++.TP ++.BI nat " NAT_SPEC" ++.BI Where " NAT_SPEC " ":= {src|dst} addr" " addr1" "[-" "addr2" "] [port " "port1" "[-" "port2" "]]" ++ ++Specify src/dst and range of nat to configure for the connection (only valid with commit). ++.RS ++.TP ++src/dst - configure src or dst nat ++.TP ++.BI "" "addr1" "/" "addr2" " - IPv4/IPv6 addresses" ++.TP ++.BI "" "port1" "/" "port2" " - Port numbers" ++.RE ++.TP ++.BI nat ++Restore any previous configured nat. ++.TP ++.BI clear ++Remove any conntrack state and metadata (mark/label) from the packet (must only option specified). ++.TP ++.BI force ++Forces conntrack direction for a previously commited connections, so that current direction will become the original direction (only valid with commit). ++ ++.SH EXAMPLES ++Example showing natted firewall in conntrack zone 2, and conntrack mark usage: ++.EX ++ ++#Add ingress qdisc on eth0 and eth1 interfaces ++.nf ++$ tc qdisc add dev eth0 handle ingress ++$ tc qdisc add dev eth1 handle ingress ++ ++#Setup filters on eth0, allowing opening new connections in zone 2, and doing src nat + mark for each new connection ++$ tc filter add dev eth0 ingress prio 1 chain 0 proto ip flower ip_proto tcp ct_state -trk \\ ++action ct zone 2 pipe action goto chain 2 ++$ tc filter add dev eth0 ingress prio 1 chain 2 proto ip flower ct_state +trk+new \\ ++action ct zone 2 commit mark 0xbb nat src addr 5.5.5.7 pipe action mirred egress redirect dev eth1 ++$ tc filter add dev eth0 ingress prio 1 chain 2 proto ip flower ct_zone 2 ct_mark 0xbb ct_state +trk+est \\ ++action ct nat pipe action mirred egress redirect dev eth1 ++ ++#Setup filters on eth1, allowing only established connections of zone 2 through, and reverse nat (dst nat in this case) ++$ tc filter add dev eth1 ingress prio 1 chain 0 proto ip flower ip_proto tcp ct_state -trk \\ ++action ct zone 2 pipe action goto chain 1 ++$ tc filter add dev eth1 ingress prio 1 chain 1 proto ip flower ct_zone 2 ct_mark 0xbb ct_state +trk+est \\ ++action ct nat pipe action mirred egress redirect dev eth0 ++.fi ++ ++.EE ++ ++.RE ++.SH SEE ALSO ++.BR tc (8), ++.BR tc-flower (8) ++.BR tc-mirred (8) ++.SH AUTHORS ++Paul Blakey ++ ++Marcelo Ricardo Leitner ++ ++Yossi Kuperman +diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8 +index 0f95f303f23b7..02a7256f36ebb 100644 +--- a/man/man8/tc-flower.8 ++++ b/man/man8/tc-flower.8 +@@ -1,5 +1,11 @@ + .TH "Flower filter in tc" 8 "22 Oct 2015" "iproute2" "Linux" + ++ "Usage: ct clear\n" ++ " ct commit [force] [zone ZONE] [mark MASKED_MARK] [label MASKED_LABEL] [nat NAT_SPEC] [OFFLOAD_POLICY]\n" ++ " ct [nat] [zone ZONE] [OFFLOAD_POLICY]\n" ++ "Where: ZONE is the conntrack zone table number\n" ++ " NAT_SPEC is {src|dst} addr addr1[-addr2] [port port1[-port2]]\n" ++ " OFFLOAD_POLICY is [policy_pkts PACKETS] [policy_timeout TIMEOUT]\n" + .SH NAME + flower \- flow based traffic control filter + .SH SYNOPSIS +-- +2.26.2 + diff --git a/SPECS/iproute.spec b/SPECS/iproute.spec index 393d372..9c2fe0a 100644 --- a/SPECS/iproute.spec +++ b/SPECS/iproute.spec @@ -1,5 +1,5 @@ %define rpmversion 5.3.0 -%define specrelease 4%{?dist} +%define specrelease 5%{?dist} %define pkg_release %{specrelease}%{?buildid} Summary: Advanced IP routing and network device configuration tools @@ -42,6 +42,13 @@ Patch26: 0027-tc-m_tunnel_key-add-options-support-for-vxlan.patch Patch27: 0028-tc-m_tunnel_key-add-options-support-for-erpsan.patch Patch28: 0029-tc-f_flower-add-options-support-for-vxlan.patch Patch29: 0030-tc-f_flower-add-options-support-for-erspan.patch +Patch30: 0031-devlink-Add-health-error-recovery-status-monitoring.patch +Patch31: 0032-ss-allow-dumping-kTLS-info.patch +Patch32: 0033-Import-tc_act-tc_ct.h-uapi-file.patch +Patch33: 0034-tc-add-NLA_F_NESTED-flag-to-all-actions-options-nest.patch +Patch34: 0035-tc-Introduce-tc-ct-action.patch +Patch35: 0036-tc-flower-Add-matching-on-conntrack-info.patch +Patch36: 0037-man-tc-ct.8-Add-manual-page-for-ct-tc-action.patch License: GPLv2+ and Public Domain BuildRequires: bison BuildRequires: elfutils-libelf-devel @@ -186,6 +193,15 @@ cat %{SOURCE3} >>%{buildroot}%{_sysconfdir}/iproute2/rt_dsfield %{_includedir}/iproute2/bpf_elf.h %changelog +* Mon Jun 29 2020 Andrea Claudi [5.3.0-5.el8] +- man: tc-ct.8: Add manual page for ct tc action (Andrea Claudi) [1844637] +- tc: flower: Add matching on conntrack info (Andrea Claudi) [1844637] +- tc: Introduce tc ct action (Andrea Claudi) [1844637] +- tc: add NLA_F_NESTED flag to all actions options nested block (Andrea Claudi) [1844637] +- Import tc_act/tc_ct.h uapi file (Andrea Claudi) [1844637] +- ss: allow dumping kTLS info (Andrea Claudi) [1812207] +- devlink: Add health error recovery status monitoring (Andrea Claudi) [1821039] + * Fri Jun 05 2020 Andrea Claudi [5.3.0-4.el8] - tc: f_flower: add options support for erspan (Andrea Claudi) [1830485] - tc: f_flower: add options support for vxlan (Andrea Claudi) [1830485]