diff --git a/SOURCES/0012-tc-u32-add-support-for-json-output.patch b/SOURCES/0012-tc-u32-add-support-for-json-output.patch
new file mode 100644
index 0000000..0e6efe0
--- /dev/null
+++ b/SOURCES/0012-tc-u32-add-support-for-json-output.patch
@@ -0,0 +1,206 @@
+From 0a250b280fbaf8e4d6ad173cf6d9e082658954b4 Mon Sep 17 00:00:00 2001
+Message-Id: <0a250b280fbaf8e4d6ad173cf6d9e082658954b4.1644243783.git.aclaudi@redhat.com>
+In-Reply-To: <b30268eda844bdebbb8e5e4f5735e3b1bb666368.1644243783.git.aclaudi@redhat.com>
+References: <b30268eda844bdebbb8e5e4f5735e3b1bb666368.1644243783.git.aclaudi@redhat.com>
+From: Andrea Claudi <aclaudi@redhat.com>
+Date: Mon, 7 Feb 2022 15:16:36 +0100
+Subject: [PATCH] tc: u32: add support for json output
+
+Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1989591
+Upstream Status: unknown commit c733722b
+
+commit c733722b993cb82832722b1490cbc5002035fd20
+Author: Wen Liang <liangwen12year@gmail.com>
+Date:   Wed Jan 26 14:44:47 2022 -0500
+
+    tc: u32: add support for json output
+
+    Currently u32 filter output does not support json. This commit uses
+    proper json functions to add support for it.
+
+    `sprint_u32_handle` adds an extra space after the raw check, remove the
+    extra space.
+
+    Signed-off-by: Wen Liang <liangwen12year@gmail.com>
+    Tested-by: Victor Nogueira <victor@mojatatu.com>
+    Signed-off-by: David Ahern <dsahern@kernel.org>
+---
+ tc/f_u32.c | 83 ++++++++++++++++++++++++++++++------------------------
+ 1 file changed, 46 insertions(+), 37 deletions(-)
+
+diff --git a/tc/f_u32.c b/tc/f_u32.c
+index a5747f67..11da202e 100644
+--- a/tc/f_u32.c
++++ b/tc/f_u32.c
+@@ -109,7 +109,7 @@ static char *sprint_u32_handle(__u32 handle, char *buf)
+ 		}
+ 	}
+ 	if (show_raw)
+-		snprintf(b, bsize, "[%08x] ", handle);
++		snprintf(b, bsize, "[%08x]", handle);
+ 	return buf;
+ }
+ 
+@@ -1213,11 +1213,11 @@ static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt,
+ 
+ 	if (handle) {
+ 		SPRINT_BUF(b1);
+-		fprintf(f, "fh %s ", sprint_u32_handle(handle, b1));
++		print_string(PRINT_ANY, "fh", "fh %s ", sprint_u32_handle(handle, b1));
+ 	}
+ 
+ 	if (TC_U32_NODE(handle))
+-		fprintf(f, "order %d ", TC_U32_NODE(handle));
++		print_int(PRINT_ANY, "order", "order %d ", TC_U32_NODE(handle));
+ 
+ 	if (tb[TCA_U32_SEL]) {
+ 		if (RTA_PAYLOAD(tb[TCA_U32_SEL])  < sizeof(*sel))
+@@ -1227,15 +1227,15 @@ static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt,
+ 	}
+ 
+ 	if (tb[TCA_U32_DIVISOR]) {
+-		fprintf(f, "ht divisor %d ",
+-			rta_getattr_u32(tb[TCA_U32_DIVISOR]));
++		__u32 htdivisor = rta_getattr_u32(tb[TCA_U32_DIVISOR]);
++
++		print_int(PRINT_ANY, "ht_divisor", "ht divisor %d ", htdivisor);
+ 	} else if (tb[TCA_U32_HASH]) {
+ 		__u32 htid = rta_getattr_u32(tb[TCA_U32_HASH]);
+-
+-		fprintf(f, "key ht %x bkt %x ", TC_U32_USERHTID(htid),
+-			TC_U32_HASH(htid));
++		print_hex(PRINT_ANY, "key_ht", "key ht %x ", TC_U32_USERHTID(htid));
++		print_hex(PRINT_ANY, "bkt", "bkt %x ", TC_U32_HASH(htid));
+ 	} else {
+-		fprintf(f, "??? ");
++		fprintf(stderr, "divisor and hash missing ");
+ 	}
+ 	if (tb[TCA_U32_CLASSID]) {
+ 		SPRINT_BUF(b1);
+@@ -1244,27 +1244,27 @@ static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt,
+ 			sprint_tc_classid(rta_getattr_u32(tb[TCA_U32_CLASSID]),
+ 					  b1));
+ 	} else if (sel && sel->flags & TC_U32_TERMINAL) {
+-		fprintf(f, "terminal flowid ??? ");
++		print_string(PRINT_FP, NULL, "terminal flowid ", NULL);
+ 	}
+ 	if (tb[TCA_U32_LINK]) {
+ 		SPRINT_BUF(b1);
+-		fprintf(f, "link %s ",
+-			sprint_u32_handle(rta_getattr_u32(tb[TCA_U32_LINK]),
+-					  b1));
++		char *link = sprint_u32_handle(rta_getattr_u32(tb[TCA_U32_LINK]), b1);
++
++		print_string(PRINT_ANY, "link", "link %s ", link);
+ 	}
+ 
+ 	if (tb[TCA_U32_FLAGS]) {
+ 		__u32 flags = rta_getattr_u32(tb[TCA_U32_FLAGS]);
+ 
+ 		if (flags & TCA_CLS_FLAGS_SKIP_HW)
+-			fprintf(f, "skip_hw ");
++			print_bool(PRINT_ANY, "skip_hw", "skip_hw ", true);
+ 		if (flags & TCA_CLS_FLAGS_SKIP_SW)
+-			fprintf(f, "skip_sw ");
++			print_bool(PRINT_ANY, "skip_sw", "skip_sw ", true);
+ 
+ 		if (flags & TCA_CLS_FLAGS_IN_HW)
+-			fprintf(f, "in_hw ");
++			print_bool(PRINT_ANY, "in_hw", "in_hw ", true);
+ 		else if (flags & TCA_CLS_FLAGS_NOT_IN_HW)
+-			fprintf(f, "not_in_hw ");
++			print_bool(PRINT_ANY, "not_in_hw", "not_in_hw ", true);
+ 	}
+ 
+ 	if (tb[TCA_U32_PCNT]) {
+@@ -1275,10 +1275,10 @@ static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt,
+ 		pf = RTA_DATA(tb[TCA_U32_PCNT]);
+ 	}
+ 
+-	if (sel && show_stats && NULL != pf)
+-		fprintf(f, " (rule hit %llu success %llu)",
+-			(unsigned long long) pf->rcnt,
+-			(unsigned long long) pf->rhit);
++	if (sel && show_stats && NULL != pf) {
++		print_u64(PRINT_ANY, "rule_hit", "(rule hit %llu ", pf->rcnt);
++		print_u64(PRINT_ANY, "success", "success %llu)", pf->rhit);
++	}
+ 
+ 	if (tb[TCA_U32_MARK]) {
+ 		struct tc_u32_mark *mark = RTA_DATA(tb[TCA_U32_MARK]);
+@@ -1286,8 +1286,10 @@ static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt,
+ 		if (RTA_PAYLOAD(tb[TCA_U32_MARK]) < sizeof(*mark)) {
+ 			fprintf(f, "\n  Invalid mark (kernel&iproute2 mismatch)\n");
+ 		} else {
+-			fprintf(f, "\n  mark 0x%04x 0x%04x (success %d)",
+-				mark->val, mark->mask, mark->success);
++			print_nl();
++			print_0xhex(PRINT_ANY, "fwmark_value", "  mark 0x%04x ", mark->val);
++			print_0xhex(PRINT_ANY, "fwmark_mask", "0x%04x ", mark->mask);
++			print_int(PRINT_ANY, "fwmark_success", "(success %d)", mark->success);
+ 		}
+ 	}
+ 
+@@ -1298,38 +1300,45 @@ static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt,
+ 			for (i = 0; i < sel->nkeys; i++) {
+ 				show_keys(f, sel->keys + i);
+ 				if (show_stats && NULL != pf)
+-					fprintf(f, " (success %llu ) ",
+-						(unsigned long long) pf->kcnts[i]);
++					print_u64(PRINT_ANY, "success", " (success %llu ) ",
++						  pf->kcnts[i]);
+ 			}
+ 		}
+ 
+ 		if (sel->flags & (TC_U32_VAROFFSET | TC_U32_OFFSET)) {
+-			fprintf(f, "\n    offset ");
+-			if (sel->flags & TC_U32_VAROFFSET)
+-				fprintf(f, "%04x>>%d at %d ",
+-					ntohs(sel->offmask),
+-					sel->offshift,  sel->offoff);
++			print_nl();
++			print_string(PRINT_ANY, NULL, "%s", "    offset ");
++			if (sel->flags & TC_U32_VAROFFSET) {
++				print_hex(PRINT_ANY, "offset_mask", "%04x", ntohs(sel->offmask));
++				print_int(PRINT_ANY, "offset_shift", ">>%d ", sel->offshift);
++				print_int(PRINT_ANY, "offset_off", "at %d ", sel->offoff);
++			}
+ 			if (sel->off)
+-				fprintf(f, "plus %d ", sel->off);
++				print_int(PRINT_ANY, "plus", "plus %d ", sel->off);
+ 		}
+ 		if (sel->flags & TC_U32_EAT)
+-			fprintf(f, " eat ");
++			print_string(PRINT_ANY, NULL, "%s", " eat ");
+ 
+ 		if (sel->hmask) {
+-			fprintf(f, "\n    hash mask %08x at %d ",
+-				(unsigned int)htonl(sel->hmask), sel->hoff);
++			print_nl();
++			unsigned int hmask = (unsigned int)htonl(sel->hmask);
++
++			print_hex(PRINT_ANY, "hash_mask", "    hash mask %08x ", hmask);
++			print_int(PRINT_ANY, "hash_off", "at %d ", sel->hoff);
+ 		}
+ 	}
+ 
+ 	if (tb[TCA_U32_POLICE]) {
+-		fprintf(f, "\n");
++		print_nl();
+ 		tc_print_police(f, tb[TCA_U32_POLICE]);
+ 	}
+ 
+ 	if (tb[TCA_U32_INDEV]) {
+ 		struct rtattr *idev = tb[TCA_U32_INDEV];
+-
+-		fprintf(f, "\n  input dev %s\n", rta_getattr_str(idev));
++		print_nl();
++		print_string(PRINT_ANY, "input_dev", "  input dev %s",
++			     rta_getattr_str(idev));
++		print_nl();
+ 	}
+ 
+ 	if (tb[TCA_U32_ACT])
+-- 
+2.34.1
+
diff --git a/SOURCES/0013-tc-u32-add-json-support-in-print_raw-print_ipv4-prin.patch b/SOURCES/0013-tc-u32-add-json-support-in-print_raw-print_ipv4-prin.patch
new file mode 100644
index 0000000..c5d48b1
--- /dev/null
+++ b/SOURCES/0013-tc-u32-add-json-support-in-print_raw-print_ipv4-prin.patch
@@ -0,0 +1,240 @@
+From 66efa0a6dc179f814614fbd2f47c37d7e20e4405 Mon Sep 17 00:00:00 2001
+Message-Id: <66efa0a6dc179f814614fbd2f47c37d7e20e4405.1644243783.git.aclaudi@redhat.com>
+In-Reply-To: <b30268eda844bdebbb8e5e4f5735e3b1bb666368.1644243783.git.aclaudi@redhat.com>
+References: <b30268eda844bdebbb8e5e4f5735e3b1bb666368.1644243783.git.aclaudi@redhat.com>
+From: Andrea Claudi <aclaudi@redhat.com>
+Date: Mon, 7 Feb 2022 15:16:36 +0100
+Subject: [PATCH] tc: u32: add json support in `print_raw`, `print_ipv4`,
+ `print_ipv6`
+
+Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1989591
+Upstream Status: unknown commit 721435dc
+
+commit 721435dcfd9274277af2fb6a4cec81d4a9bcc6b4
+Author: Wen Liang <liangwen12year@gmail.com>
+Date:   Wed Jan 26 14:44:48 2022 -0500
+
+    tc: u32: add json support in `print_raw`, `print_ipv4`, `print_ipv6`
+
+    Currently the key struct of u32 filter does not support json. This
+    commit adds json support for showing key.
+
+    Signed-off-by: Wen Liang <liangwen12year@gmail.com>
+    Tested-by: Victor Nogueira <victor@mojatatu.com>
+    Signed-off-by: David Ahern <dsahern@kernel.org>
+---
+ tc/f_u32.c | 121 ++++++++++++++++++++++++++++++++++-------------------
+ 1 file changed, 79 insertions(+), 42 deletions(-)
+
+diff --git a/tc/f_u32.c b/tc/f_u32.c
+index 11da202e..d787eb91 100644
+--- a/tc/f_u32.c
++++ b/tc/f_u32.c
+@@ -824,23 +824,27 @@ static void print_ipv4(FILE *f, const struct tc_u32_key *key)
+ {
+ 	char abuf[256];
+ 
++	open_json_object("match");
+ 	switch (key->off) {
+ 	case 0:
+ 		switch (ntohl(key->mask)) {
+ 		case 0x0f000000:
+-			fprintf(f, "\n  match IP ihl %u",
+-				ntohl(key->val) >> 24);
++			print_nl();
++			print_uint(PRINT_ANY, "ip_ihl", "  match IP ihl %u",
++				   ntohl(key->val) >> 24);
+ 			return;
+ 		case 0x00ff0000:
+-			fprintf(f, "\n  match IP dsfield %#x",
+-				ntohl(key->val) >> 16);
++			print_nl();
++			print_0xhex(PRINT_ANY, "ip_dsfield", "  match IP dsfield %#x",
++				    ntohl(key->val) >> 16);
+ 			return;
+ 		}
+ 		break;
+ 	case 8:
+ 		if (ntohl(key->mask) == 0x00ff0000) {
+-			fprintf(f, "\n  match IP protocol %d",
+-				ntohl(key->val) >> 16);
++			print_nl();
++			print_int(PRINT_ANY, "ip_protocol", "  match IP protocol %d",
++				  ntohl(key->val) >> 16);
+ 			return;
+ 		}
+ 		break;
+@@ -849,11 +853,21 @@ static void print_ipv4(FILE *f, const struct tc_u32_key *key)
+ 			int bits = mask2bits(key->mask);
+ 
+ 			if (bits >= 0) {
+-				fprintf(f, "\n  %s %s/%d",
+-					key->off == 12 ? "match IP src" : "match IP dst",
+-					inet_ntop(AF_INET, &key->val,
+-						  abuf, sizeof(abuf)),
+-					bits);
++				const char *addr;
++
++				if (key->off == 12) {
++					print_nl();
++					print_null(PRINT_FP, NULL, "  match IP src ", NULL);
++					open_json_object("src");
++				} else {
++					print_nl();
++					print_null(PRINT_FP, NULL, "  match IP dst ", NULL);
++					open_json_object("dst");
++				}
++				addr = inet_ntop(AF_INET, &key->val, abuf, sizeof(abuf));
++				print_string(PRINT_ANY, "address", "%s", addr);
++				print_int(PRINT_ANY, "prefixlen", "/%d", bits);
++				close_json_object();
+ 				return;
+ 			}
+ 		}
+@@ -862,45 +876,52 @@ static void print_ipv4(FILE *f, const struct tc_u32_key *key)
+ 	case 20:
+ 		switch (ntohl(key->mask)) {
+ 		case 0x0000ffff:
+-			fprintf(f, "\n  match dport %u",
+-				ntohl(key->val) & 0xffff);
++			print_uint(PRINT_ANY, "dport", "match dport %u",
++				   ntohl(key->val) & 0xffff);
+ 			return;
+ 		case 0xffff0000:
+-			fprintf(f, "\n  match sport %u",
+-				ntohl(key->val) >> 16);
++			print_nl();
++			print_uint(PRINT_ANY, "sport", "  match sport %u",
++				   ntohl(key->val) >> 16);
+ 			return;
+ 		case 0xffffffff:
+-			fprintf(f, "\n  match dport %u, match sport %u",
+-				ntohl(key->val) & 0xffff,
+-				ntohl(key->val) >> 16);
+-
++			print_nl();
++			print_uint(PRINT_ANY, "dport", "  match dport %u, ",
++				   ntohl(key->val) & 0xffff);
++			print_uint(PRINT_ANY, "sport", "match sport %u",
++				   ntohl(key->val) >> 16);
+ 			return;
+ 		}
+ 		/* XXX: Default print_raw */
+ 	}
++	close_json_object();
+ }
+ 
+ static void print_ipv6(FILE *f, const struct tc_u32_key *key)
+ {
+ 	char abuf[256];
+ 
++	open_json_object("match");
+ 	switch (key->off) {
+ 	case 0:
+ 		switch (ntohl(key->mask)) {
+ 		case 0x0f000000:
+-			fprintf(f, "\n  match IP ihl %u",
+-				ntohl(key->val) >> 24);
++			print_nl();
++			print_uint(PRINT_ANY, "ip_ihl", "  match IP ihl %u",
++				   ntohl(key->val) >> 24);
+ 			return;
+ 		case 0x00ff0000:
+-			fprintf(f, "\n  match IP dsfield %#x",
+-				ntohl(key->val) >> 16);
++			print_nl();
++			print_0xhex(PRINT_ANY, "ip_dsfield", "  match IP dsfield %#x",
++				    ntohl(key->val) >> 16);
+ 			return;
+ 		}
+ 		break;
+ 	case 8:
+ 		if (ntohl(key->mask) == 0x00ff0000) {
+-			fprintf(f, "\n  match IP protocol %d",
+-				ntohl(key->val) >> 16);
++			print_nl();
++			print_int(PRINT_ANY, "ip_protocol", "  match IP protocol %d",
++				  ntohl(key->val) >> 16);
+ 			return;
+ 		}
+ 		break;
+@@ -909,11 +930,21 @@ static void print_ipv6(FILE *f, const struct tc_u32_key *key)
+ 			int bits = mask2bits(key->mask);
+ 
+ 			if (bits >= 0) {
+-				fprintf(f, "\n  %s %s/%d",
+-					key->off == 12 ? "match IP src" : "match IP dst",
+-					inet_ntop(AF_INET, &key->val,
+-						  abuf, sizeof(abuf)),
+-					bits);
++				const char *addr;
++
++				if (key->off == 12) {
++					print_nl();
++					print_null(PRINT_FP, NULL, "  match IP src ", NULL);
++					open_json_object("src");
++				} else {
++					print_nl();
++					print_null(PRINT_FP, NULL, "  match IP dst ", NULL);
++					open_json_object("dst");
++				}
++				addr = inet_ntop(AF_INET, &key->val, abuf, sizeof(abuf));
++				print_string(PRINT_ANY, "address", "%s", addr);
++				print_int(PRINT_ANY, "prefixlen", "/%d", bits);
++				close_json_object();
+ 				return;
+ 			}
+ 		}
+@@ -922,31 +953,37 @@ static void print_ipv6(FILE *f, const struct tc_u32_key *key)
+ 	case 20:
+ 		switch (ntohl(key->mask)) {
+ 		case 0x0000ffff:
+-			fprintf(f, "\n  match sport %u",
+-				ntohl(key->val) & 0xffff);
++			print_nl();
++			print_uint(PRINT_ANY, "sport", "  match sport %u",
++				   ntohl(key->val) & 0xffff);
+ 			return;
+ 		case 0xffff0000:
+-			fprintf(f, "\n  match dport %u",
+-				ntohl(key->val) >> 16);
++			print_uint(PRINT_ANY, "dport", "match dport %u",
++				   ntohl(key->val) >> 16);
+ 			return;
+ 		case 0xffffffff:
+-			fprintf(f, "\n  match sport %u, match dport %u",
+-				ntohl(key->val) & 0xffff,
+-				ntohl(key->val) >> 16);
++			print_nl();
++			print_uint(PRINT_ANY, "sport", "  match sport %u, ",
++				   ntohl(key->val) & 0xffff);
++			print_uint(PRINT_ANY, "dport", "match dport %u",
++				   ntohl(key->val) >> 16);
+ 
+ 			return;
+ 		}
+ 		/* XXX: Default print_raw */
+ 	}
++	close_json_object();
+ }
+ 
+ static void print_raw(FILE *f, const struct tc_u32_key *key)
+ {
+-	fprintf(f, "\n  match %08x/%08x at %s%d",
+-		(unsigned int)ntohl(key->val),
+-		(unsigned int)ntohl(key->mask),
+-		key->offmask ? "nexthdr+" : "",
+-		key->off);
++	open_json_object("match");
++	print_nl();
++	print_hex(PRINT_ANY, "value", "  match %08x", (unsigned int)ntohl(key->val));
++	print_hex(PRINT_ANY, "mask", "/%08x ", (unsigned int)ntohl(key->mask));
++	print_string(PRINT_ANY, "offmask", "at %s", key->offmask ? "nexthdr+" : "");
++	print_int(PRINT_ANY, "off", "%d", key->off);
++	close_json_object();
+ }
+ 
+ static const struct {
+-- 
+2.34.1
+
diff --git a/SPECS/iproute.spec b/SPECS/iproute.spec
index c309cd8..51002b0 100644
--- a/SPECS/iproute.spec
+++ b/SPECS/iproute.spec
@@ -1,8 +1,10 @@
 Summary:            Advanced IP routing and network device configuration tools
 Name:               iproute
 Version:            5.15.0
-Release:            2%{?dist}%{?buildid}
+Release:            3%{?dist}%{?buildid}
+%if 0%{?rhel}
 Group:              Applications/System
+%endif
 URL:                https://kernel.org/pub/linux/utils/net/%{name}2/
 Source0:            https://kernel.org/pub/linux/utils/net/%{name}2/%{name}2-%{version}.tar.xz
 Source1:            rt_dsfield.deprecated
@@ -17,6 +19,8 @@ Patch7:             0008-vdpa-align-uapi-headers.patch
 Patch8:             0009-vdpa-Enable-user-to-query-vdpa-device-config-layout.patch
 Patch9:             0010-vdpa-Enable-user-to-set-mac-address-of-vdpa-device.patch
 Patch10:            0011-vdpa-Enable-user-to-set-mtu-of-the-vdpa-device.patch
+Patch11:            0012-tc-u32-add-support-for-json-output.patch
+Patch12:            0013-tc-u32-add-json-support-in-print_raw-print_ipv4-prin.patch
 
 License:            GPLv2+ and Public Domain
 BuildRequires:      bison
@@ -135,6 +139,10 @@ cat %{SOURCE1} >>%{buildroot}%{_sysconfdir}/iproute2/rt_dsfield
 %{_includedir}/iproute2/bpf_elf.h
 
 %changelog
+* Mon Feb 07 2022 Andrea Claudi <aclaudi@redhat.com> - 5.15.0-3.el8
+- tc: u32: add json support in `print_raw`, `print_ipv4`, `print_ipv6` (Andrea Claudi) [1989591]
+- tc: u32: add support for json output (Andrea Claudi) [1989591]
+
 * Wed Jan 26 2022 Andrea Claudi <aclaudi@redhat.com> - 5.15.0-2.el8
 - vdpa: Enable user to set mtu of the vdpa device (Andrea Claudi) [2036880]
 - vdpa: Enable user to set mac address of vdpa device (Andrea Claudi) [2036880]