diff -pruN 5.15.0-1/bash-completion/tc 5.19.0-1/bash-completion/tc
--- 5.15.0-1/bash-completion/tc	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/bash-completion/tc	2022-08-02 18:36:33.000000000 +0000
@@ -127,10 +127,10 @@ _tc_direct_complete()
         protocol) # list comes from lib/ll_proto.c
             COMPREPLY+=( $( compgen -W ' 802.1Q 802.1ad 802_2 802_3 LLDP aarp \
                 all aoe arp atalk atmfate atmmpoa ax25 bpq can control cust \
-                ddcmp dec diag dna_dl dna_rc dna_rt econet ieeepup ieeepupat \
-                ip ipv4 ipv6 ipx irda lat localtalk loop mobitex ppp_disc \
-                ppp_mp ppp_ses ppptalk pup pupat rarp sca snap tipc tr_802_2 \
-                wan_ppp x25' -- "$cur" ) )
+                ddcmp dec diag dna_dl dna_rc dna_rt econet ethercat ieeepup \
+                ieeepupat ip ipv4 ipv6 ipx irda lat localtalk loop mobitex \
+                ppp_disc ppp_mp ppp_ses ppptalk profinet pup pupat rarp sca \
+                snap tipc tr_802_2 wan_ppp x25' -- "$cur" ) )
             return 0
             ;;
         prio)
diff -pruN 5.15.0-1/bridge/br_common.h 5.19.0-1/bridge/br_common.h
--- 5.15.0-1/bridge/br_common.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/bridge/br_common.h	2022-08-02 18:36:33.000000000 +0000
@@ -12,13 +12,17 @@ int print_mdb_mon(struct nlmsghdr *n, vo
 int print_fdb(struct nlmsghdr *n, void *arg);
 void print_stp_state(__u8 state);
 int parse_stp_state(const char *arg);
-int print_vlan_rtm(struct nlmsghdr *n, void *arg, bool monitor);
+int print_vlan_rtm(struct nlmsghdr *n, void *arg, bool monitor,
+		   bool global_only);
+int print_vnifilter_rtm(struct nlmsghdr *n, void *arg, bool monitor);
+void br_print_router_port_stats(struct rtattr *pattr);
 
 int do_fdb(int argc, char **argv);
 int do_mdb(int argc, char **argv);
 int do_monitor(int argc, char **argv);
 int do_vlan(int argc, char **argv);
 int do_link(int argc, char **argv);
+int do_vni(int argc, char **argv);
 
 extern int preferred_family;
 extern int show_stats;
diff -pruN 5.15.0-1/bridge/bridge.c 5.19.0-1/bridge/bridge.c
--- 5.15.0-1/bridge/bridge.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/bridge/bridge.c	2022-08-02 18:36:33.000000000 +0000
@@ -58,6 +58,7 @@ static const struct cmd {
 	{ "fdb",	do_fdb },
 	{ "mdb",	do_mdb },
 	{ "vlan",	do_vlan },
+	{ "vni",	do_vni },
 	{ "monitor",	do_monitor },
 	{ "help",	do_help },
 	{ 0 }
diff -pruN 5.15.0-1/bridge/fdb.c 5.19.0-1/bridge/fdb.c
--- 5.15.0-1/bridge/fdb.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/bridge/fdb.c	2022-08-02 18:36:33.000000000 +0000
@@ -571,6 +571,7 @@ static int fdb_get(int argc, char **argv
 	char *addr = NULL;
 	short vlan = -1;
 	char *endptr;
+	int ret;
 
 	while (argc > 0) {
 		if ((strcmp(*argv, "brport") == 0) || strcmp(*argv, "dev") == 0) {
@@ -657,13 +658,15 @@ static int fdb_get(int argc, char **argv
 	 * if -json was specified.
 	 */
 	new_json_obj(json);
+	ret = 0;
 	if (print_fdb(answer, stdout) < 0) {
 		fprintf(stderr, "An error :-)\n");
-		return -1;
+		ret = -1;
 	}
 	delete_json_obj();
+	free(answer);
 
-	return 0;
+	return ret;
 }
 
 int do_fdb(int argc, char **argv)
diff -pruN 5.15.0-1/bridge/link.c 5.19.0-1/bridge/link.c
--- 5.15.0-1/bridge/link.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/bridge/link.c	2022-08-02 18:36:33.000000000 +0000
@@ -153,6 +153,12 @@ static void print_protinfo(FILE *fp, str
 		if (prtb[IFLA_BRPORT_MCAST_FLOOD])
 			print_on_off(PRINT_ANY, "mcast_flood", "mcast_flood %s ",
 				     rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_FLOOD]));
+		if (prtb[IFLA_BRPORT_BCAST_FLOOD])
+			print_on_off(PRINT_ANY, "bcast_flood", "bcast_flood %s ",
+				     rta_getattr_u8(prtb[IFLA_BRPORT_BCAST_FLOOD]));
+		if (prtb[IFLA_BRPORT_MULTICAST_ROUTER])
+			print_uint(PRINT_ANY, "mcast_router", "mcast_router %u ",
+				   rta_getattr_u8(prtb[IFLA_BRPORT_MULTICAST_ROUTER]));
 		if (prtb[IFLA_BRPORT_MCAST_TO_UCAST])
 			print_on_off(PRINT_ANY, "mcast_to_unicast", "mcast_to_unicast %s ",
 				     rta_getattr_u8(prtb[IFLA_BRPORT_MCAST_TO_UCAST]));
@@ -175,6 +181,9 @@ static void print_protinfo(FILE *fp, str
 		if (prtb[IFLA_BRPORT_ISOLATED])
 			print_on_off(PRINT_ANY, "isolated", "isolated %s ",
 				     rta_getattr_u8(prtb[IFLA_BRPORT_ISOLATED]));
+		if (prtb[IFLA_BRPORT_LOCKED])
+			print_on_off(PRINT_ANY, "locked", "locked %s ",
+				     rta_getattr_u8(prtb[IFLA_BRPORT_LOCKED]));
 	} else
 		print_stp_state(rta_getattr_u8(attr));
 }
@@ -192,12 +201,6 @@ static void print_af_spec(struct rtattr
 
 	if (aftb[IFLA_BRIDGE_MODE])
 		print_hwmode(rta_getattr_u16(aftb[IFLA_BRIDGE_MODE]));
-
-	if (!show_details)
-		return;
-
-	if (aftb[IFLA_BRIDGE_VLAN_INFO])
-		print_vlan_info(aftb[IFLA_BRIDGE_VLAN_INFO], ifindex);
 }
 
 int print_linkinfo(struct nlmsghdr *n, void *arg)
@@ -270,11 +273,14 @@ static void usage(void)
 		"                               [ learning {on | off} ]\n"
 		"                               [ learning_sync {on | off} ]\n"
 		"                               [ flood {on | off} ]\n"
+		"                               [ mcast_router MULTICAST_ROUTER ]\n"
 		"                               [ mcast_flood {on | off} ]\n"
+		"                               [ bcast_flood {on | off} ]\n"
 		"                               [ mcast_to_unicast {on | off} ]\n"
 		"                               [ neigh_suppress {on | off} ]\n"
 		"                               [ vlan_tunnel {on | off} ]\n"
 		"                               [ isolated {on | off} ]\n"
+		"                               [ locked {on | off} ]\n"
 		"                               [ hwmode {vepa | veb} ]\n"
 		"                               [ backup_port DEVICE ] [ nobackup_port ]\n"
 		"                               [ self ] [ master ]\n"
@@ -301,8 +307,11 @@ static int brlink_modify(int argc, char
 	__s8 learning_sync = -1;
 	__s8 flood = -1;
 	__s8 vlan_tunnel = -1;
+	__s8 mcast_router = -1;
 	__s8 mcast_flood = -1;
+	__s8 bcast_flood = -1;
 	__s8 mcast_to_unicast = -1;
+	__s8 locked = -1;
 	__s8 isolated = -1;
 	__s8 hairpin = -1;
 	__s8 bpdu_guard = -1;
@@ -355,11 +364,19 @@ static int brlink_modify(int argc, char
 			flood = parse_on_off("flood", *argv, &ret);
 			if (ret)
 				return ret;
+		} else if (strcmp(*argv, "mcast_router") == 0) {
+			NEXT_ARG();
+			mcast_router = atoi(*argv);
 		} else if (strcmp(*argv, "mcast_flood") == 0) {
 			NEXT_ARG();
 			mcast_flood = parse_on_off("mcast_flood", *argv, &ret);
 			if (ret)
 				return ret;
+		} else if (strcmp(*argv, "bcast_flood") == 0) {
+			NEXT_ARG();
+			bcast_flood = parse_on_off("bcast_flood", *argv, &ret);
+			if (ret)
+				return ret;
 		} else if (strcmp(*argv, "mcast_to_unicast") == 0) {
 			NEXT_ARG();
 			mcast_to_unicast = parse_on_off("mcast_to_unicast", *argv, &ret);
@@ -415,6 +432,11 @@ static int brlink_modify(int argc, char
 			isolated = parse_on_off("isolated", *argv, &ret);
 			if (ret)
 				return ret;
+		} else if (strcmp(*argv, "locked") == 0) {
+			NEXT_ARG();
+			locked = parse_on_off("locked", *argv, &ret);
+			if (ret)
+				return ret;
 		} else if (strcmp(*argv, "backup_port") == 0) {
 			NEXT_ARG();
 			backup_port_idx = ll_name_to_index(*argv);
@@ -459,9 +481,15 @@ static int brlink_modify(int argc, char
 		addattr8(&req.n, sizeof(req), IFLA_BRPORT_PROTECT, root_block);
 	if (flood >= 0)
 		addattr8(&req.n, sizeof(req), IFLA_BRPORT_UNICAST_FLOOD, flood);
+	if (mcast_router >= 0)
+		addattr8(&req.n, sizeof(req), IFLA_BRPORT_MULTICAST_ROUTER,
+			 mcast_router);
 	if (mcast_flood >= 0)
 		addattr8(&req.n, sizeof(req), IFLA_BRPORT_MCAST_FLOOD,
 			 mcast_flood);
+	if (bcast_flood >= 0)
+		addattr8(&req.n, sizeof(req), IFLA_BRPORT_BCAST_FLOOD,
+			 bcast_flood);
 	if (mcast_to_unicast >= 0)
 		addattr8(&req.n, sizeof(req), IFLA_BRPORT_MCAST_TO_UCAST,
 			 mcast_to_unicast);
@@ -489,6 +517,9 @@ static int brlink_modify(int argc, char
 	if (isolated != -1)
 		addattr8(&req.n, sizeof(req), IFLA_BRPORT_ISOLATED, isolated);
 
+	if (locked >= 0)
+		addattr8(&req.n, sizeof(req), IFLA_BRPORT_LOCKED, locked);
+
 	if (backup_port_idx != -1)
 		addattr32(&req.n, sizeof(req), IFLA_BRPORT_BACKUP_PORT,
 			  backup_port_idx);
@@ -538,19 +569,9 @@ static int brlink_show(int argc, char **
 			return nodev(filter_dev);
 	}
 
-	if (show_details) {
-		if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE,
-					     (compress_vlans ?
-					      RTEXT_FILTER_BRVLAN_COMPRESSED :
-					      RTEXT_FILTER_BRVLAN)) < 0) {
-			perror("Cannon send dump request");
-			exit(1);
-		}
-	} else {
-		if (rtnl_linkdump_req(&rth, PF_BRIDGE) < 0) {
-			perror("Cannon send dump request");
-			exit(1);
-		}
+	if (rtnl_linkdump_req(&rth, PF_BRIDGE) < 0) {
+		perror("Cannot send dump request");
+		exit(1);
 	}
 
 	new_json_obj(json);
diff -pruN 5.15.0-1/bridge/Makefile 5.19.0-1/bridge/Makefile
--- 5.15.0-1/bridge/Makefile	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/bridge/Makefile	2022-08-02 18:36:33.000000000 +0000
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-BROBJ = bridge.o fdb.o monitor.o link.o mdb.o vlan.o
+BROBJ = bridge.o fdb.o monitor.o link.o mdb.o vlan.o vni.o
 
 include ../config.mk
 
diff -pruN 5.15.0-1/bridge/mdb.c 5.19.0-1/bridge/mdb.c
--- 5.15.0-1/bridge/mdb.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/bridge/mdb.c	2022-08-02 18:36:33.000000000 +0000
@@ -59,7 +59,7 @@ static const char *format_timer(__u32 ti
 	return tbuf;
 }
 
-static void __print_router_port_stats(FILE *f, struct rtattr *pattr)
+void br_print_router_port_stats(struct rtattr *pattr)
 {
 	struct rtattr *tb[MDBA_ROUTER_PATTR_MAX + 1];
 
@@ -101,13 +101,13 @@ static void br_print_router_ports(FILE *
 			print_string(PRINT_JSON, "port", NULL, port_ifname);
 
 			if (show_stats)
-				__print_router_port_stats(f, i);
+				br_print_router_port_stats(i);
 			close_json_object();
 		} else if (show_stats) {
 			fprintf(f, "router ports on %s: %s",
 				brifname, port_ifname);
 
-			__print_router_port_stats(f, i);
+			br_print_router_port_stats(i);
 			fprintf(f, "\n");
 		} else {
 			fprintf(f, "%s ", port_ifname);
diff -pruN 5.15.0-1/bridge/monitor.c 5.19.0-1/bridge/monitor.c
--- 5.15.0-1/bridge/monitor.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/bridge/monitor.c	2022-08-02 18:36:33.000000000 +0000
@@ -31,10 +31,20 @@ static int prefix_banner;
 
 static void usage(void)
 {
-	fprintf(stderr, "Usage: bridge monitor [file | link | fdb | mdb | vlan | all]\n");
+	fprintf(stderr, "Usage: bridge monitor [file | link | fdb | mdb | vlan | vni | all]\n");
 	exit(-1);
 }
 
+static int print_tunnel_rtm(struct nlmsghdr *n, void *arg, bool monitor)
+{
+	struct tunnel_msg *tmsg = NLMSG_DATA(n);
+
+	if (tmsg->family == PF_BRIDGE)
+		return print_vnifilter_rtm(n, arg, monitor);
+
+	return 0;
+}
+
 static int accept_msg(struct rtnl_ctrl_data *ctrl,
 		      struct nlmsghdr *n, void *arg)
 {
@@ -71,7 +81,13 @@ static int accept_msg(struct rtnl_ctrl_d
 	case RTM_DELVLAN:
 		if (prefix_banner)
 			fprintf(fp, "[VLAN]");
-		return print_vlan_rtm(n, arg, true);
+		return print_vlan_rtm(n, arg, true, false);
+
+	case RTM_NEWTUNNEL:
+	case RTM_DELTUNNEL:
+		if (prefix_banner)
+			fprintf(fp, "[TUNNEL]");
+		return print_tunnel_rtm(n, arg, true);
 
 	default:
 		return 0;
@@ -86,6 +102,7 @@ int do_monitor(int argc, char **argv)
 	int lneigh = 0;
 	int lmdb = 0;
 	int lvlan = 0;
+	int lvni = 0;
 
 	rtnl_close(&rth);
 
@@ -105,9 +122,13 @@ int do_monitor(int argc, char **argv)
 		} else if (matches(*argv, "vlan") == 0) {
 			lvlan = 1;
 			groups = 0;
+		} else if (strcmp(*argv, "vni") == 0) {
+			lvni = 1;
+			groups = 0;
 		} else if (strcmp(*argv, "all") == 0) {
 			groups = ~RTMGRP_TC;
 			lvlan = 1;
+			lvni = 1;
 			prefix_banner = 1;
 		} else if (matches(*argv, "help") == 0) {
 			usage();
@@ -151,6 +172,11 @@ int do_monitor(int argc, char **argv)
 		exit(1);
 	}
 
+	if (lvni && rtnl_add_nl_group(&rth, RTNLGRP_TUNNEL) < 0) {
+		fprintf(stderr, "Failed to add bridge vni group to list\n");
+		exit(1);
+	}
+
 	ll_init_map(&rth);
 
 	if (rtnl_listen(&rth, accept_msg, stdout) < 0)
diff -pruN 5.15.0-1/bridge/vlan.c 5.19.0-1/bridge/vlan.c
--- 5.15.0-1/bridge/vlan.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/bridge/vlan.c	2022-08-02 18:36:33.000000000 +0000
@@ -9,6 +9,7 @@
 #include <linux/if_bridge.h>
 #include <linux/if_ether.h>
 #include <string.h>
+#include <errno.h>
 
 #include "json_print.h"
 #include "libnetlink.h"
@@ -35,8 +36,23 @@ static void usage(void)
 		"                                                     [ pvid ] [ untagged ]\n"
 		"                                                     [ self ] [ master ]\n"
 		"       bridge vlan { set } vid VLAN_ID dev DEV [ state STP_STATE ]\n"
+		"                                               [ mcast_router MULTICAST_ROUTER ]\n"
 		"       bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n"
-		"       bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n");
+		"       bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n"
+		"       bridge vlan global { set } vid VLAN_ID dev DEV\n"
+		"                      [ mcast_snooping MULTICAST_SNOOPING ]\n"
+		"                      [ mcast_querier MULTICAST_QUERIER ]\n"
+		"                      [ mcast_igmp_version IGMP_VERSION ]\n"
+		"                      [ mcast_mld_version MLD_VERSION ]\n"
+		"                      [ mcast_last_member_count LAST_MEMBER_COUNT ]\n"
+		"                      [ mcast_last_member_interval LAST_MEMBER_INTERVAL ]\n"
+		"                      [ mcast_startup_query_count STARTUP_QUERY_COUNT ]\n"
+		"                      [ mcast_startup_query_interval STARTUP_QUERY_INTERVAL ]\n"
+		"                      [ mcast_membership_interval MEMBERSHIP_INTERVAL ]\n"
+		"                      [ mcast_querier_interval QUERIER_INTERVAL ]\n"
+		"                      [ mcast_query_interval QUERY_INTERVAL ]\n"
+		"                      [ mcast_query_response_interval QUERY_RESPONSE_INTERVAL ]\n"
+		"       bridge vlan global { show } [ dev DEV ] [ vid VLAN_ID ]\n");
 	exit(-1);
 }
 
@@ -257,16 +273,24 @@ static int vlan_option_set(int argc, cha
 	};
 	struct bridge_vlan_info vinfo = {};
 	struct rtattr *afspec;
-	short vid_end = -1;
 	char *d = NULL;
 	short vid = -1;
-	int state = -1;
 
+	afspec = addattr_nest(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY);
+	afspec->rta_type |= NLA_F_NESTED;
 	while (argc > 0) {
 		if (strcmp(*argv, "dev") == 0) {
 			NEXT_ARG();
 			d = *argv;
+			req.bvm.ifindex = ll_name_to_index(d);
+			if (req.bvm.ifindex == 0) {
+				fprintf(stderr,
+					"Cannot find network device \"%s\"\n",
+					d);
+				return -1;
+			}
 		} else if (strcmp(*argv, "vid") == 0) {
+			short vid_end = -1;
 			char *p;
 
 			NEXT_ARG();
@@ -284,8 +308,22 @@ static int vlan_option_set(int argc, cha
 			} else {
 				vid = atoi(*argv);
 			}
+			if (vid >= 4096) {
+				fprintf(stderr, "Invalid VLAN ID \"%hu\"\n",
+					vid);
+				return -1;
+			}
+
+			vinfo.flags = BRIDGE_VLAN_INFO_ONLY_OPTS;
+			vinfo.vid = vid;
+			addattr_l(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_INFO,
+				  &vinfo, sizeof(vinfo));
+			if (vid_end != -1)
+				addattr16(&req.n, sizeof(req),
+					  BRIDGE_VLANDB_ENTRY_RANGE, vid_end);
 		} else if (strcmp(*argv, "state") == 0) {
 			char *endptr;
+			int state;
 
 			NEXT_ARG();
 			state = strtol(*argv, &endptr, 10);
@@ -295,42 +333,197 @@ static int vlan_option_set(int argc, cha
 				fprintf(stderr, "Error: invalid STP state\n");
 				return -1;
 			}
+			addattr8(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_STATE,
+				 state);
+		} else if (strcmp(*argv, "mcast_router") == 0) {
+			__u8 mcast_router;
+
+			NEXT_ARG();
+			if (get_u8(&mcast_router, *argv, 0))
+				invarg("invalid mcast_router", *argv);
+			addattr8(&req.n, sizeof(req),
+				 BRIDGE_VLANDB_ENTRY_MCAST_ROUTER,
+				 mcast_router);
 		} else {
 			if (matches(*argv, "help") == 0)
 				NEXT_ARG();
 		}
 		argc--; argv++;
 	}
+	addattr_nest_end(&req.n, afspec);
 
 	if (d == NULL || vid == -1) {
 		fprintf(stderr, "Device and VLAN ID are required arguments.\n");
 		return -1;
 	}
 
-	req.bvm.ifindex = ll_name_to_index(d);
-	if (req.bvm.ifindex == 0) {
-		fprintf(stderr, "Cannot find network device \"%s\"\n", d);
+	if (rtnl_talk(&rth, &req.n, NULL) < 0)
 		return -1;
-	}
 
-	if (vid >= 4096) {
-		fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid);
-		return -1;
-	}
-	afspec = addattr_nest(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY);
+	return 0;
+}
+
+static int vlan_global_option_set(int argc, char **argv)
+{
+	struct {
+		struct nlmsghdr	n;
+		struct br_vlan_msg	bvm;
+		char			buf[1024];
+	} req = {
+		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_vlan_msg)),
+		.n.nlmsg_flags = NLM_F_REQUEST,
+		.n.nlmsg_type = RTM_NEWVLAN,
+		.bvm.family = PF_BRIDGE,
+	};
+	struct rtattr *afspec;
+	short vid_end = -1;
+	char *d = NULL;
+	short vid = -1;
+	__u64 val64;
+	__u32 val32;
+	__u8 val8;
+
+	afspec = addattr_nest(&req.n, sizeof(req),
+			      BRIDGE_VLANDB_GLOBAL_OPTIONS);
 	afspec->rta_type |= NLA_F_NESTED;
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			d = *argv;
+			req.bvm.ifindex = ll_name_to_index(d);
+			if (req.bvm.ifindex == 0) {
+				fprintf(stderr, "Cannot find network device \"%s\"\n",
+					d);
+				return -1;
+			}
+		} else if (strcmp(*argv, "vid") == 0) {
+			char *p;
 
-	vinfo.flags = BRIDGE_VLAN_INFO_ONLY_OPTS;
-	vinfo.vid = vid;
-	addattr_l(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_INFO, &vinfo,
-		  sizeof(vinfo));
-	if (vid_end != -1)
-		addattr16(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_RANGE,
-			  vid_end);
-	if (state >= 0)
-		addattr8(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_STATE, state);
+			NEXT_ARG();
+			p = strchr(*argv, '-');
+			if (p) {
+				*p = '\0';
+				p++;
+				vid = atoi(*argv);
+				vid_end = atoi(p);
+				if (vid >= vid_end || vid_end >= 4096) {
+					fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n",
+						vid, vid_end);
+					return -1;
+				}
+			} else {
+				vid = atoi(*argv);
+			}
+			if (vid >= 4096) {
+				fprintf(stderr, "Invalid VLAN ID \"%hu\"\n",
+					vid);
+				return -1;
+			}
+			addattr16(&req.n, sizeof(req), BRIDGE_VLANDB_GOPTS_ID,
+				  vid);
+			if (vid_end != -1)
+				addattr16(&req.n, sizeof(req),
+					  BRIDGE_VLANDB_GOPTS_RANGE, vid_end);
+		} else if (strcmp(*argv, "mcast_snooping") == 0) {
+			NEXT_ARG();
+			if (get_u8(&val8, *argv, 0))
+				invarg("invalid mcast_snooping", *argv);
+			addattr8(&req.n, 1024,
+				 BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING, val8);
+		} else if (strcmp(*argv, "mcast_querier") == 0) {
+			NEXT_ARG();
+			if (get_u8(&val8, *argv, 0))
+				invarg("invalid mcast_querier", *argv);
+			addattr8(&req.n, 1024,
+				 BRIDGE_VLANDB_GOPTS_MCAST_QUERIER, val8);
+		} else if (strcmp(*argv, "mcast_igmp_version") == 0) {
+			NEXT_ARG();
+			if (get_u8(&val8, *argv, 0))
+				invarg("invalid mcast_igmp_version", *argv);
+			addattr8(&req.n, 1024,
+				 BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION, val8);
+		} else if (strcmp(*argv, "mcast_mld_version") == 0) {
+			NEXT_ARG();
+			if (get_u8(&val8, *argv, 0))
+				invarg("invalid mcast_mld_version", *argv);
+			addattr8(&req.n, 1024,
+				 BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION, val8);
+		} else if (strcmp(*argv, "mcast_last_member_count") == 0) {
+			NEXT_ARG();
+			if (get_u32(&val32, *argv, 0))
+				invarg("invalid mcast_last_member_count", *argv);
+			addattr32(&req.n, 1024,
+				  BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT,
+				  val32);
+		} else if (strcmp(*argv, "mcast_startup_query_count") == 0) {
+			NEXT_ARG();
+			if (get_u32(&val32, *argv, 0))
+				invarg("invalid mcast_startup_query_count",
+				       *argv);
+			addattr32(&req.n, 1024,
+				  BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT,
+				  val32);
+		} else if (strcmp(*argv, "mcast_last_member_interval") == 0) {
+			NEXT_ARG();
+			if (get_u64(&val64, *argv, 0))
+				invarg("invalid mcast_last_member_interval",
+				       *argv);
+			addattr64(&req.n, 1024,
+				  BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL,
+				  val64);
+		} else if (strcmp(*argv, "mcast_membership_interval") == 0) {
+			NEXT_ARG();
+			if (get_u64(&val64, *argv, 0))
+				invarg("invalid mcast_membership_interval",
+				       *argv);
+			addattr64(&req.n, 1024,
+				  BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL,
+				  val64);
+		} else if (strcmp(*argv, "mcast_querier_interval") == 0) {
+			NEXT_ARG();
+			if (get_u64(&val64, *argv, 0))
+				invarg("invalid mcast_querier_interval",
+				       *argv);
+			addattr64(&req.n, 1024,
+				  BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL,
+				  val64);
+		} else if (strcmp(*argv, "mcast_query_interval") == 0) {
+			NEXT_ARG();
+			if (get_u64(&val64, *argv, 0))
+				invarg("invalid mcast_query_interval",
+				       *argv);
+			addattr64(&req.n, 1024,
+				  BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL,
+				  val64);
+		} else if (strcmp(*argv, "mcast_query_response_interval") == 0) {
+			NEXT_ARG();
+			if (get_u64(&val64, *argv, 0))
+				invarg("invalid mcast_query_response_interval",
+				       *argv);
+			addattr64(&req.n, 1024,
+				  BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL,
+				  val64);
+		} else if (strcmp(*argv, "mcast_startup_query_interval") == 0) {
+			NEXT_ARG();
+			if (get_u64(&val64, *argv, 0))
+				invarg("invalid mcast_startup_query_interval",
+				       *argv);
+			addattr64(&req.n, 1024,
+				  BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL,
+				  val64);
+		} else {
+			if (strcmp(*argv, "help") == 0)
+				NEXT_ARG();
+		}
+		argc--; argv++;
+	}
 	addattr_nest_end(&req.n, afspec);
 
+	if (d == NULL || vid == -1) {
+		fprintf(stderr, "Device and VLAN ID are required arguments.\n");
+		return -1;
+	}
+
 	if (rtnl_talk(&rth, &req.n, NULL) < 0)
 		return -1;
 
@@ -621,11 +814,224 @@ static int print_vlan_stats(struct nlmsg
 	return 0;
 }
 
-int print_vlan_rtm(struct nlmsghdr *n, void *arg, bool monitor)
+static void print_vlan_router_ports(struct rtattr *rattr)
+{
+	int rem = RTA_PAYLOAD(rattr);
+	struct rtattr *i;
+
+	print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s    ", "");
+	open_json_array(PRINT_ANY, is_json_context() ? "router_ports" :
+						       "router ports: ");
+	for (i = RTA_DATA(rattr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+		uint32_t *port_ifindex = RTA_DATA(i);
+		const char *port_ifname = ll_index_to_name(*port_ifindex);
+
+		open_json_object(NULL);
+		if (show_stats && i != RTA_DATA(rattr)) {
+			print_nl();
+			/* start: IFNAMSIZ + 4 + strlen("router ports: ") */
+			print_string(PRINT_FP, NULL,
+				     "%-" __stringify(IFNAMSIZ) "s    "
+				     "              ",
+				     "");
+		}
+		print_string(PRINT_ANY, "port", "%s ", port_ifname);
+		if (show_stats)
+			br_print_router_port_stats(i);
+		close_json_object();
+	}
+	close_json_array(PRINT_JSON, NULL);
+	print_nl();
+}
+
+static void print_vlan_global_opts(struct rtattr *a, int ifindex)
+{
+	struct rtattr *vtb[BRIDGE_VLANDB_GOPTS_MAX + 1], *vattr;
+	__u16 vid, vrange = 0;
+
+	if ((a->rta_type & NLA_TYPE_MASK) != BRIDGE_VLANDB_GLOBAL_OPTIONS)
+		return;
+
+	parse_rtattr_flags(vtb, BRIDGE_VLANDB_GOPTS_MAX, RTA_DATA(a),
+			   RTA_PAYLOAD(a), NLA_F_NESTED);
+	vid = rta_getattr_u16(vtb[BRIDGE_VLANDB_GOPTS_ID]);
+	if (vtb[BRIDGE_VLANDB_GOPTS_RANGE])
+		vrange = rta_getattr_u16(vtb[BRIDGE_VLANDB_GOPTS_RANGE]);
+	else
+		vrange = vid;
+
+	if (filter_vlan && (filter_vlan < vid || filter_vlan > vrange))
+		return;
+
+	if (vlan_rtm_cur_ifidx != ifindex) {
+		open_vlan_port(ifindex, VLAN_SHOW_VLAN);
+		open_json_object(NULL);
+		vlan_rtm_cur_ifidx = ifindex;
+	} else {
+		open_json_object(NULL);
+		print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s  ", "");
+	}
+	print_range("vlan", vid, vrange);
+	print_nl();
+	print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s    ", "");
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING]) {
+		vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING];
+		print_uint(PRINT_ANY, "mcast_snooping", "mcast_snooping %u ",
+			   rta_getattr_u8(vattr));
+	}
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER]) {
+		vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER];
+		print_uint(PRINT_ANY, "mcast_querier", "mcast_querier %u ",
+			   rta_getattr_u8(vattr));
+	}
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION]) {
+		vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION];
+		print_uint(PRINT_ANY, "mcast_igmp_version",
+			   "mcast_igmp_version %u ", rta_getattr_u8(vattr));
+	}
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION]) {
+		vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION];
+		print_uint(PRINT_ANY, "mcast_mld_version",
+			   "mcast_mld_version %u ", rta_getattr_u8(vattr));
+	}
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION]) {
+		vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT];
+		print_uint(PRINT_ANY, "mcast_last_member_count",
+			   "mcast_last_member_count %u ",
+			   rta_getattr_u32(vattr));
+	}
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL]) {
+		vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL];
+		print_lluint(PRINT_ANY, "mcast_last_member_interval",
+			     "mcast_last_member_interval %llu ",
+			     rta_getattr_u64(vattr));
+	}
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT]) {
+		vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT];
+		print_uint(PRINT_ANY, "mcast_startup_query_count",
+			   "mcast_startup_query_count %u ",
+			   rta_getattr_u32(vattr));
+	}
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL]) {
+		vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL];
+		print_lluint(PRINT_ANY, "mcast_startup_query_interval",
+			     "mcast_startup_query_interval %llu ",
+			     rta_getattr_u64(vattr));
+	}
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL]) {
+		vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL];
+		print_lluint(PRINT_ANY, "mcast_membership_interval",
+			     "mcast_membership_interval %llu ",
+			     rta_getattr_u64(vattr));
+	}
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL]) {
+		vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL];
+		print_lluint(PRINT_ANY, "mcast_querier_interval",
+			     "mcast_querier_interval %llu ",
+			     rta_getattr_u64(vattr));
+	}
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL]) {
+		vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL];
+		print_lluint(PRINT_ANY, "mcast_query_interval",
+			     "mcast_query_interval %llu ",
+			     rta_getattr_u64(vattr));
+	}
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL]) {
+		vattr = vtb[BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL];
+		print_lluint(PRINT_ANY, "mcast_query_response_interval",
+			     "mcast_query_response_interval %llu ",
+			     rta_getattr_u64(vattr));
+	}
+	print_nl();
+	if (vtb[BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS]) {
+		vattr = RTA_DATA(vtb[BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS]);
+		print_vlan_router_ports(vattr);
+	}
+	close_json_object();
+}
+
+static void print_vlan_opts(struct rtattr *a, int ifindex)
+{
+	struct rtattr *vtb[BRIDGE_VLANDB_ENTRY_MAX + 1], *vattr;
+	struct bridge_vlan_xstats vstats;
+	struct bridge_vlan_info *vinfo;
+	__u16 vrange = 0;
+	__u8 state = 0;
+
+	if ((a->rta_type & NLA_TYPE_MASK) != BRIDGE_VLANDB_ENTRY)
+		return;
+
+	parse_rtattr_flags(vtb, BRIDGE_VLANDB_ENTRY_MAX, RTA_DATA(a),
+			   RTA_PAYLOAD(a), NLA_F_NESTED);
+	vinfo = RTA_DATA(vtb[BRIDGE_VLANDB_ENTRY_INFO]);
+
+	memset(&vstats, 0, sizeof(vstats));
+	if (vtb[BRIDGE_VLANDB_ENTRY_RANGE])
+		vrange = rta_getattr_u16(vtb[BRIDGE_VLANDB_ENTRY_RANGE]);
+	else
+		vrange = vinfo->vid;
+
+	if (filter_vlan && (filter_vlan < vinfo->vid || filter_vlan > vrange))
+		return;
+
+	if (vtb[BRIDGE_VLANDB_ENTRY_STATE])
+		state = rta_getattr_u8(vtb[BRIDGE_VLANDB_ENTRY_STATE]);
+
+	if (vtb[BRIDGE_VLANDB_ENTRY_STATS]) {
+		struct rtattr *stb[BRIDGE_VLANDB_STATS_MAX+1];
+		struct rtattr *attr;
+
+		attr = vtb[BRIDGE_VLANDB_ENTRY_STATS];
+		parse_rtattr(stb, BRIDGE_VLANDB_STATS_MAX, RTA_DATA(attr),
+			     RTA_PAYLOAD(attr));
+
+		if (stb[BRIDGE_VLANDB_STATS_RX_BYTES]) {
+			attr = stb[BRIDGE_VLANDB_STATS_RX_BYTES];
+			vstats.rx_bytes = rta_getattr_u64(attr);
+		}
+		if (stb[BRIDGE_VLANDB_STATS_RX_PACKETS]) {
+			attr = stb[BRIDGE_VLANDB_STATS_RX_PACKETS];
+			vstats.rx_packets = rta_getattr_u64(attr);
+		}
+		if (stb[BRIDGE_VLANDB_STATS_TX_PACKETS]) {
+			attr = stb[BRIDGE_VLANDB_STATS_TX_PACKETS];
+			vstats.tx_packets = rta_getattr_u64(attr);
+		}
+		if (stb[BRIDGE_VLANDB_STATS_TX_BYTES]) {
+			attr = stb[BRIDGE_VLANDB_STATS_TX_BYTES];
+			vstats.tx_bytes = rta_getattr_u64(attr);
+		}
+	}
+
+	if (vlan_rtm_cur_ifidx != ifindex) {
+		open_vlan_port(ifindex, VLAN_SHOW_VLAN);
+		open_json_object(NULL);
+		vlan_rtm_cur_ifidx = ifindex;
+	} else {
+		open_json_object(NULL);
+		print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s  ", "");
+	}
+	print_range("vlan", vinfo->vid, vrange);
+	print_vlan_flags(vinfo->flags);
+	print_nl();
+	print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s    ", "");
+	print_stp_state(state);
+	if (vtb[BRIDGE_VLANDB_ENTRY_MCAST_ROUTER]) {
+		vattr = vtb[BRIDGE_VLANDB_ENTRY_MCAST_ROUTER];
+		print_uint(PRINT_ANY, "mcast_router", "mcast_router %u ",
+			   rta_getattr_u8(vattr));
+	}
+	print_nl();
+	if (show_stats)
+		__print_one_vlan_stats(&vstats);
+	close_json_object();
+}
+
+int print_vlan_rtm(struct nlmsghdr *n, void *arg, bool monitor, bool global_only)
 {
-	struct rtattr *vtb[BRIDGE_VLANDB_ENTRY_MAX + 1], *a;
 	struct br_vlan_msg *bvm = NLMSG_DATA(n);
 	int len = n->nlmsg_len;
+	struct rtattr *a;
 	int rem;
 
 	if (n->nlmsg_type != RTM_NEWVLAN && n->nlmsg_type != RTM_DELVLAN &&
@@ -660,66 +1066,21 @@ int print_vlan_rtm(struct nlmsghdr *n, v
 
 	rem = len;
 	for (a = BRVLAN_RTA(bvm); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) {
-		struct bridge_vlan_xstats vstats;
-		struct bridge_vlan_info *vinfo;
-		__u32 vrange = 0;
-		__u8 state = 0;
-
-		parse_rtattr_flags(vtb, BRIDGE_VLANDB_ENTRY_MAX, RTA_DATA(a),
-				   RTA_PAYLOAD(a), NLA_F_NESTED);
-		vinfo = RTA_DATA(vtb[BRIDGE_VLANDB_ENTRY_INFO]);
-
-		memset(&vstats, 0, sizeof(vstats));
-		if (vtb[BRIDGE_VLANDB_ENTRY_RANGE])
-			vrange = rta_getattr_u16(vtb[BRIDGE_VLANDB_ENTRY_RANGE]);
-		else
-			vrange = vinfo->vid;
+		unsigned short rta_type = a->rta_type & NLA_TYPE_MASK;
 
-		if (vtb[BRIDGE_VLANDB_ENTRY_STATE])
-			state = rta_getattr_u8(vtb[BRIDGE_VLANDB_ENTRY_STATE]);
+		/* skip unknown attributes */
+		if (rta_type > BRIDGE_VLANDB_MAX ||
+		    (global_only && rta_type != BRIDGE_VLANDB_GLOBAL_OPTIONS))
+			continue;
 
-		if (vtb[BRIDGE_VLANDB_ENTRY_STATS]) {
-			struct rtattr *stb[BRIDGE_VLANDB_STATS_MAX+1];
-			struct rtattr *attr;
-
-			attr = vtb[BRIDGE_VLANDB_ENTRY_STATS];
-			parse_rtattr(stb, BRIDGE_VLANDB_STATS_MAX, RTA_DATA(attr),
-				     RTA_PAYLOAD(attr));
-
-			if (stb[BRIDGE_VLANDB_STATS_RX_BYTES]) {
-				attr = stb[BRIDGE_VLANDB_STATS_RX_BYTES];
-				vstats.rx_bytes = rta_getattr_u64(attr);
-			}
-			if (stb[BRIDGE_VLANDB_STATS_RX_PACKETS]) {
-				attr = stb[BRIDGE_VLANDB_STATS_RX_PACKETS];
-				vstats.rx_packets = rta_getattr_u64(attr);
-			}
-			if (stb[BRIDGE_VLANDB_STATS_TX_PACKETS]) {
-				attr = stb[BRIDGE_VLANDB_STATS_TX_PACKETS];
-				vstats.tx_packets = rta_getattr_u64(attr);
-			}
-			if (stb[BRIDGE_VLANDB_STATS_TX_BYTES]) {
-				attr = stb[BRIDGE_VLANDB_STATS_TX_BYTES];
-				vstats.tx_bytes = rta_getattr_u64(attr);
-			}
-		}
-		if (vlan_rtm_cur_ifidx != bvm->ifindex) {
-			open_vlan_port(bvm->ifindex, VLAN_SHOW_VLAN);
-			open_json_object(NULL);
-			vlan_rtm_cur_ifidx = bvm->ifindex;
-		} else {
-			open_json_object(NULL);
-			print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s  ", "");
+		switch (rta_type) {
+		case BRIDGE_VLANDB_ENTRY:
+			print_vlan_opts(a, bvm->ifindex);
+			break;
+		case BRIDGE_VLANDB_GLOBAL_OPTIONS:
+			print_vlan_global_opts(a, bvm->ifindex);
+			break;
 		}
-		print_range("vlan", vinfo->vid, vrange);
-		print_vlan_flags(vinfo->flags);
-		print_nl();
-		print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s    ", "");
-		print_stp_state(state);
-		print_nl();
-		if (show_stats)
-			__print_one_vlan_stats(&vstats);
-		close_json_object();
 	}
 
 	return 0;
@@ -727,7 +1088,12 @@ int print_vlan_rtm(struct nlmsghdr *n, v
 
 static int print_vlan_rtm_filter(struct nlmsghdr *n, void *arg)
 {
-	return print_vlan_rtm(n, arg, false);
+	return print_vlan_rtm(n, arg, false, false);
+}
+
+static int print_vlan_rtm_global_filter(struct nlmsghdr *n, void *arg)
+{
+	return print_vlan_rtm(n, arg, false, true);
 }
 
 static int vlan_show(int argc, char **argv, int subject)
@@ -813,7 +1179,8 @@ static int vlan_show(int argc, char **ar
 		__u32 filt_mask;
 
 		filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS);
-		if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
+		if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask,
+					      NULL, NULL) < 0) {
 			perror("Cannot send dump request");
 			exit(1);
 		}
@@ -828,7 +1195,8 @@ static int vlan_show(int argc, char **ar
 		}
 
 		filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS_SLAVE);
-		if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
+		if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask,
+					      NULL, NULL) < 0) {
 			perror("Cannot send slave dump request");
 			exit(1);
 		}
@@ -845,6 +1213,61 @@ out:
 	return 0;
 }
 
+static int vlan_global_show(int argc, char **argv)
+{
+	__u32 dump_flags = BRIDGE_VLANDB_DUMPF_GLOBAL;
+	int ret = 0, subject = VLAN_SHOW_VLAN;
+	char *filter_dev = NULL;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			if (filter_dev)
+				duparg("dev", *argv);
+			filter_dev = *argv;
+		} else if (strcmp(*argv, "vid") == 0) {
+			NEXT_ARG();
+			if (filter_vlan)
+				duparg("vid", *argv);
+			filter_vlan = atoi(*argv);
+		}
+		argc--; argv++;
+	}
+
+	if (filter_dev) {
+		filter_index = ll_name_to_index(filter_dev);
+		if (!filter_index)
+			return nodev(filter_dev);
+	}
+
+	new_json_obj(json);
+
+	if (rtnl_brvlandump_req(&rth, PF_BRIDGE, dump_flags) < 0) {
+		perror("Cannot send dump request");
+		exit(1);
+	}
+
+	if (!is_json_context()) {
+		printf("%-" __stringify(IFNAMSIZ) "s  %-"
+		       __stringify(VLAN_ID_LEN) "s", "port",
+		       "vlan-id");
+		printf("\n");
+	}
+
+	ret = rtnl_dump_filter(&rth, print_vlan_rtm_global_filter, &subject);
+	if (ret < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		exit(1);
+	}
+
+	if (vlan_rtm_cur_ifidx != -1)
+		close_vlan_port();
+
+	delete_json_obj();
+	fflush(stdout);
+	return 0;
+}
+
 void print_vlan_info(struct rtattr *tb, int ifindex)
 {
 	struct rtattr *i, *list = tb;
@@ -889,6 +1312,24 @@ void print_vlan_info(struct rtattr *tb,
 		close_vlan_port();
 }
 
+static int vlan_global(int argc, char **argv)
+{
+	if (argc > 0) {
+		if (strcmp(*argv, "show") == 0 ||
+		    strcmp(*argv, "lst") == 0 ||
+		    strcmp(*argv, "list") == 0)
+			return vlan_global_show(argc-1, argv+1);
+		else if (strcmp(*argv, "set") == 0)
+			return vlan_global_option_set(argc-1, argv+1);
+		else
+			usage();
+	} else {
+		return vlan_global_show(0, NULL);
+	}
+
+	return 0;
+}
+
 int do_vlan(int argc, char **argv)
 {
 	ll_init_map(&rth);
@@ -907,6 +1348,8 @@ int do_vlan(int argc, char **argv)
 		}
 		if (matches(*argv, "set") == 0)
 			return vlan_option_set(argc-1, argv+1);
+		if (strcmp(*argv, "global") == 0)
+			return vlan_global(argc-1, argv+1);
 		if (matches(*argv, "help") == 0)
 			usage();
 	} else {
diff -pruN 5.15.0-1/bridge/vni.c 5.19.0-1/bridge/vni.c
--- 5.15.0-1/bridge/vni.c	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/bridge/vni.c	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,439 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Command to manage vnifiltering on a vxlan device
+ *
+ * Authors:     Roopa Prabhu <roopa@nvidia.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <linux/if_link.h>
+#include <linux/if_bridge.h>
+#include <linux/if_ether.h>
+
+#include "json_print.h"
+#include "libnetlink.h"
+#include "br_common.h"
+#include "utils.h"
+
+static unsigned int filter_index;
+
+#define VXLAN_ID_LEN 15
+
+#define __stringify_1(x...) #x
+#define __stringify(x...) __stringify_1(x)
+
+static void usage(void)
+{
+	fprintf(stderr,
+		"Usage: bridge vni { add | del } vni VNI\n"
+		"		[ { group | remote } IP_ADDRESS ]\n"
+	        "		[ dev DEV ]\n"
+		"       bridge vni { show }\n"
+		"\n"
+		"Where:	VNI	:= 0-16777215\n"
+	       );
+	exit(-1);
+}
+
+static int parse_vni_filter(const char *argv, struct nlmsghdr *n, int reqsize,
+			    inet_prefix *group)
+{
+	char *vnilist = strdupa(argv);
+	char *vni = strtok(vnilist, ",");
+	int group_type = AF_UNSPEC;
+	struct rtattr *nlvlist_e;
+	char *v;
+	int i;
+
+	if (group && is_addrtype_inet(group))
+		group_type = (group->family == AF_INET) ?  VXLAN_VNIFILTER_ENTRY_GROUP :
+						     VXLAN_VNIFILTER_ENTRY_GROUP6;
+
+	for (i = 0; vni; i++) {
+		__u32 vni_start = 0, vni_end = 0;
+
+		v = strchr(vni, '-');
+		if (v) {
+			*v = '\0';
+			v++;
+			vni_start = atoi(vni);
+			vni_end = atoi(v);
+		} else {
+			vni_start = atoi(vni);
+		}
+		nlvlist_e = addattr_nest(n, reqsize, VXLAN_VNIFILTER_ENTRY |
+					 NLA_F_NESTED);
+		addattr32(n, 1024, VXLAN_VNIFILTER_ENTRY_START, vni_start);
+		if (vni_end)
+			addattr32(n, 1024, VXLAN_VNIFILTER_ENTRY_END, vni_end);
+		if (group)
+			addattr_l(n, 1024, group_type, group->data, group->bytelen);
+		addattr_nest_end(n, nlvlist_e);
+		vni = strtok(NULL, ",");
+	}
+
+	return 0;
+}
+
+static int vni_modify(int cmd, int argc, char **argv)
+{
+	struct {
+		struct nlmsghdr	n;
+		struct tunnel_msg	tmsg;
+		char			buf[1024];
+	} req = {
+		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tunnel_msg)),
+		.n.nlmsg_flags = NLM_F_REQUEST,
+		.n.nlmsg_type = cmd,
+		.tmsg.family = PF_BRIDGE,
+	};
+	bool group_present = false;
+	inet_prefix daddr;
+	char *vni = NULL;
+	char *d = NULL;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			d = *argv;
+		} else if (strcmp(*argv, "vni") == 0) {
+			NEXT_ARG();
+			if (vni)
+				invarg("duplicate vni", *argv);
+			vni = *argv;
+		} else if (strcmp(*argv, "group") == 0) {
+			if (group_present)
+				invarg("duplicate group", *argv);
+			if (is_addrtype_inet_not_multi(&daddr)) {
+				fprintf(stderr, "vxlan: both group and remote");
+				fprintf(stderr, " cannot be specified\n");
+				return -1;
+			}
+			NEXT_ARG();
+			get_addr(&daddr, *argv, AF_UNSPEC);
+			if (!is_addrtype_inet_multi(&daddr))
+				invarg("invalid group address", *argv);
+			group_present = true;
+		} else if (strcmp(*argv, "remote") == 0) {
+			if (group_present)
+				invarg("duplicate group", *argv);
+			NEXT_ARG();
+			get_addr(&daddr, *argv, AF_UNSPEC);
+			group_present = true;
+		} else {
+			if (strcmp(*argv, "help") == 0)
+				usage();
+		}
+		argc--; argv++;
+	}
+
+	if (d == NULL || vni == NULL) {
+		fprintf(stderr, "Device and VNI ID are required arguments.\n");
+		return -1;
+	}
+
+	if (!vni && group_present) {
+		fprintf(stderr, "Group can only be specified with a vni\n");
+		return -1;
+	}
+
+	if (vni)
+		parse_vni_filter(vni, &req.n, sizeof(req),
+				 (group_present ? &daddr : NULL));
+
+	req.tmsg.ifindex = ll_name_to_index(d);
+	if (req.tmsg.ifindex == 0) {
+		fprintf(stderr, "Cannot find vxlan device \"%s\"\n", d);
+		return -1;
+	}
+
+	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+		return -1;
+
+	return 0;
+}
+
+static void open_vni_port(int ifi_index, const char *fmt)
+{
+	open_json_object(NULL);
+	print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname",
+			   "%-" __stringify(IFNAMSIZ) "s  ",
+			   ll_index_to_name(ifi_index));
+	open_json_array(PRINT_JSON, "vnis");
+}
+
+static void close_vni_port(void)
+{
+	close_json_array(PRINT_JSON, NULL);
+	close_json_object();
+}
+
+static void print_range(const char *name, __u32 start, __u32 id)
+{
+	char end[64];
+
+	snprintf(end, sizeof(end), "%sEnd", name);
+
+	print_uint(PRINT_ANY, name, " %u", start);
+	if (start != id)
+		print_uint(PRINT_ANY, end, "-%-14u ", id);
+
+}
+
+static void print_vnifilter_entry_stats(struct rtattr *stats_attr)
+{
+	struct rtattr *stb[VNIFILTER_ENTRY_STATS_MAX+1];
+	__u64 stat;
+
+	open_json_object("stats");
+	parse_rtattr_flags(stb, VNIFILTER_ENTRY_STATS_MAX, RTA_DATA(stats_attr),
+			   RTA_PAYLOAD(stats_attr), NLA_F_NESTED);
+
+	print_nl();
+	print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s   ", "");
+	print_string(PRINT_FP, NULL, "RX: ", "");
+
+	if (stb[VNIFILTER_ENTRY_STATS_RX_BYTES]) {
+		stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_BYTES]);
+		print_lluint(PRINT_ANY, "rx_bytes", "bytes %llu ", stat);
+	}
+	if (stb[VNIFILTER_ENTRY_STATS_RX_PKTS]) {
+		stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_PKTS]);
+		print_lluint(PRINT_ANY, "rx_pkts", "pkts %llu ", stat);
+	}
+	if (stb[VNIFILTER_ENTRY_STATS_RX_DROPS]) {
+		stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_DROPS]);
+		print_lluint(PRINT_ANY, "rx_drops", "drops %llu ", stat);
+	}
+	if (stb[VNIFILTER_ENTRY_STATS_RX_ERRORS]) {
+		stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_RX_ERRORS]);
+		print_lluint(PRINT_ANY, "rx_errors", "errors %llu ", stat);
+	}
+
+	print_nl();
+	print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s   ", "");
+	print_string(PRINT_FP, NULL, "TX: ", "");
+
+	if (stb[VNIFILTER_ENTRY_STATS_TX_BYTES]) {
+		stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_BYTES]);
+		print_lluint(PRINT_ANY, "tx_bytes", "bytes %llu ", stat);
+	}
+	if (stb[VNIFILTER_ENTRY_STATS_TX_PKTS]) {
+		stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_PKTS]);
+		print_lluint(PRINT_ANY, "tx_pkts", "pkts %llu ", stat);
+	}
+	if (stb[VNIFILTER_ENTRY_STATS_TX_DROPS]) {
+		stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_DROPS]);
+		print_lluint(PRINT_ANY, "tx_drops", "drops %llu ", stat);
+	}
+	if (stb[VNIFILTER_ENTRY_STATS_TX_ERRORS]) {
+		stat = rta_getattr_u64(stb[VNIFILTER_ENTRY_STATS_TX_ERRORS]);
+		print_lluint(PRINT_ANY, "tx_errors", "errors %llu ", stat);
+	}
+	close_json_object();
+}
+
+static void print_vni(struct rtattr *t, int ifindex)
+{
+	struct rtattr *ttb[VXLAN_VNIFILTER_ENTRY_MAX+1];
+	__u32 vni_start = 0;
+	__u32 vni_end = 0;
+
+	parse_rtattr_flags(ttb, VXLAN_VNIFILTER_ENTRY_MAX, RTA_DATA(t),
+			   RTA_PAYLOAD(t), NLA_F_NESTED);
+
+	if (ttb[VXLAN_VNIFILTER_ENTRY_START])
+		vni_start = rta_getattr_u32(ttb[VXLAN_VNIFILTER_ENTRY_START]);
+
+	if (ttb[VXLAN_VNIFILTER_ENTRY_END])
+		vni_end = rta_getattr_u32(ttb[VXLAN_VNIFILTER_ENTRY_END]);
+
+	if (vni_end)
+		print_range("vni", vni_start, vni_end);
+	else
+		print_uint(PRINT_ANY, "vni", " %-14u", vni_start);
+
+	if (ttb[VXLAN_VNIFILTER_ENTRY_GROUP]) {
+		__be32 addr = rta_getattr_u32(ttb[VXLAN_VNIFILTER_ENTRY_GROUP]);
+
+		if (addr) {
+			if (IN_MULTICAST(ntohl(addr)))
+				print_string(PRINT_ANY,
+					     "group",
+					     " %s",
+					     format_host(AF_INET, 4, &addr));
+			else
+				print_string(PRINT_ANY,
+					     "remote",
+					     " %s",
+					     format_host(AF_INET, 4, &addr));
+		}
+	} else if (ttb[VXLAN_VNIFILTER_ENTRY_GROUP6]) {
+		struct in6_addr addr;
+
+		memcpy(&addr, RTA_DATA(ttb[VXLAN_VNIFILTER_ENTRY_GROUP6]), sizeof(struct in6_addr));
+		if (!IN6_IS_ADDR_UNSPECIFIED(&addr)) {
+			if (IN6_IS_ADDR_MULTICAST(&addr))
+				print_string(PRINT_ANY,
+					     "group",
+					     " %s",
+					     format_host(AF_INET6,
+							 sizeof(struct in6_addr),
+							 &addr));
+			else
+				print_string(PRINT_ANY,
+					     "remote",
+					     " %s",
+					     format_host(AF_INET6,
+							 sizeof(struct in6_addr),
+							 &addr));
+		}
+	}
+
+	if (ttb[VXLAN_VNIFILTER_ENTRY_STATS])
+		print_vnifilter_entry_stats(ttb[VXLAN_VNIFILTER_ENTRY_STATS]);
+
+	close_json_object();
+	print_string(PRINT_FP, NULL, "%s", _SL_);
+}
+
+int print_vnifilter_rtm(struct nlmsghdr *n, void *arg, bool monitor)
+{
+	struct tunnel_msg *tmsg = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	bool first = true;
+	struct rtattr *t;
+	int rem;
+
+	if (n->nlmsg_type != RTM_NEWTUNNEL &&
+	    n->nlmsg_type != RTM_DELTUNNEL &&
+	    n->nlmsg_type != RTM_GETTUNNEL) {
+		fprintf(stderr, "Unknown vni tunnel rtm msg: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+		return 0;
+	}
+
+	len -= NLMSG_LENGTH(sizeof(*tmsg));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	if (tmsg->family != AF_BRIDGE)
+		return 0;
+
+	if (filter_index && filter_index != tmsg->ifindex)
+		return 0;
+
+	if (n->nlmsg_type == RTM_DELTUNNEL)
+		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
+
+	rem = len;
+	for (t = TUNNEL_RTA(tmsg); RTA_OK(t, rem); t = RTA_NEXT(t, rem)) {
+		unsigned short rta_type = t->rta_type & NLA_TYPE_MASK;
+
+		if (rta_type != VXLAN_VNIFILTER_ENTRY)
+			continue;
+		if (first) {
+			open_vni_port(tmsg->ifindex, "%s");
+			open_json_object(NULL);
+			first = false;
+		} else {
+			open_json_object(NULL);
+			print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s  ", "");
+		}
+
+		print_vni(t, tmsg->ifindex);
+	}
+	close_vni_port();
+
+	print_string(PRINT_FP, NULL, "%s", _SL_);
+
+	fflush(stdout);
+	return 0;
+}
+
+static int print_vnifilter_rtm_filter(struct nlmsghdr *n, void *arg)
+{
+	return print_vnifilter_rtm(n, arg, false);
+}
+
+static int vni_show(int argc, char **argv)
+{
+	char *filter_dev = NULL;
+	__u8 flags = 0;
+	int ret = 0;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			if (filter_dev)
+				duparg("dev", *argv);
+			filter_dev = *argv;
+		}
+		argc--; argv++;
+	}
+
+	if (filter_dev) {
+		filter_index = ll_name_to_index(filter_dev);
+		if (!filter_index)
+			return nodev(filter_dev);
+	}
+
+	new_json_obj(json);
+
+	if (show_stats)
+		flags = TUNNEL_MSG_FLAG_STATS;
+
+	if (rtnl_tunneldump_req(&rth, PF_BRIDGE, filter_index, flags) < 0) {
+		perror("Cannot send dump request");
+		exit(1);
+	}
+
+	if (!is_json_context()) {
+		printf("%-" __stringify(IFNAMSIZ) "s  %-"
+		       __stringify(VXLAN_ID_LEN) "s  %-"
+		       __stringify(15) "s",
+		       "dev", "vni", "group/remote");
+		printf("\n");
+	}
+
+	ret = rtnl_dump_filter(&rth, print_vnifilter_rtm_filter, NULL);
+	if (ret < 0) {
+		fprintf(stderr, "Dump ternminated\n");
+		exit(1);
+	}
+
+	delete_json_obj();
+	fflush(stdout);
+	return 0;
+}
+
+int do_vni(int argc, char **argv)
+{
+	ll_init_map(&rth);
+
+	if (argc > 0) {
+		if (strcmp(*argv, "add") == 0)
+			return vni_modify(RTM_NEWTUNNEL, argc-1, argv+1);
+		if (strcmp(*argv, "delete") == 0)
+			return vni_modify(RTM_DELTUNNEL, argc-1, argv+1);
+		if (strcmp(*argv, "show") == 0 ||
+		    strcmp(*argv, "lst") == 0 ||
+		    strcmp(*argv, "list") == 0)
+			return vni_show(argc-1, argv+1);
+		if (strcmp(*argv, "help") == 0)
+			usage();
+	} else {
+		return vni_show(0, NULL);
+	}
+
+	fprintf(stderr, "Command \"%s\" is unknown, try \"bridge vni help\".\n", *argv);
+	exit(-1);
+}
diff -pruN 5.15.0-1/configure 5.19.0-1/configure
--- 5.15.0-1/configure	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/configure	2022-08-02 18:36:33.000000000 +0000
@@ -3,6 +3,8 @@
 # This is not an autoconf generated configure
 
 INCLUDE="$PWD/include"
+PREFIX="/usr"
+LIBDIR="\${prefix}/lib"
 
 # Output file which is input to Makefile
 CONFIG=config.mk
@@ -148,6 +150,15 @@ EOF
 	rm -f $TMPDIR/ipttest.c $TMPDIR/ipttest
 }
 
+check_lib_dir()
+{
+	LIBDIR=$(echo $LIBDIR | sed "s|\${prefix}|$PREFIX|")
+
+	echo -n "lib directory: "
+	echo "$LIBDIR"
+	echo "LIBDIR:=$LIBDIR" >> $CONFIG
+}
+
 check_ipt()
 {
 	if ! grep TC_CONFIG_XT $CONFIG > /dev/null; then
@@ -384,6 +395,19 @@ check_selinux()
 	fi
 }
 
+check_tirpc()
+{
+	if ${PKG_CONFIG} libtirpc --exists; then
+		echo "HAVE_RPC:=y" >>$CONFIG
+		echo "yes"
+
+		echo 'LDLIBS +=' `${PKG_CONFIG} --libs libtirpc` >>$CONFIG
+		echo 'CFLAGS += -DHAVE_RPC' `${PKG_CONFIG} --cflags libtirpc` >>$CONFIG
+	else
+		echo "no"
+	fi
+}
+
 check_mnl()
 {
 	if ${PKG_CONFIG} libmnl --exists; then
@@ -485,12 +509,14 @@ usage()
 {
 	cat <<EOF
 Usage: $0 [OPTIONS]
-	--include_dir		Path to iproute2 include dir
-	--libbpf_dir		Path to libbpf DESTDIR
-	--libbpf_force		Enable/disable libbpf by force. Available options:
-				  on: require link against libbpf, quit config if no libbpf support
-				  off: disable libbpf probing
-	-h | --help		Show this usage info
+	--include_dir <dir>		Path to iproute2 include dir
+	--libdir <dir>			Path to iproute2 lib dir
+	--libbpf_dir <dir>		Path to libbpf DESTDIR
+	--libbpf_force <on|off>		Enable/disable libbpf by force. Available options:
+					  on: require link against libbpf, quit config if no libbpf support
+					  off: disable libbpf probing
+	--prefix <dir>			Path prefix of the lib files to install
+	-h | --help			Show this usage info
 EOF
 	exit $1
 }
@@ -499,30 +525,56 @@ EOF
 if [ $# -eq 1 ] && [ "$(echo $1 | cut -c 1)" != '-' ]; then
 	INCLUDE="$1"
 else
-	while true; do
+	while [ "$#" -gt 0 ]; do
 		case "$1" in
 			--include_dir)
-				INCLUDE=$2
-				shift 2 ;;
+				shift
+				INCLUDE="$1" ;;
+			--include_dir=*)
+				INCLUDE="${1#*=}" ;;
+			--libdir)
+				shift
+				LIBDIR="$1" ;;
+			--libdir=*)
+				LIBDIR="${1#*=}" ;;
 			--libbpf_dir)
-				LIBBPF_DIR="$2"
-				shift 2 ;;
+				shift
+				LIBBPF_DIR="$1" ;;
+			--libbpf_dir=*)
+				LIBBPF_DIR="${1#*=}" ;;
 			--libbpf_force)
-				if [ "$2" != 'on' ] && [ "$2" != 'off' ]; then
-					usage 1
-				fi
-				LIBBPF_FORCE=$2
-				shift 2 ;;
+				shift
+				LIBBPF_FORCE="$1" ;;
+			--libbpf_force=*)
+				LIBBPF_FORCE="${1#*=}" ;;
+			--prefix)
+				shift
+				PREFIX="$1" ;;
+			--prefix=*)
+				PREFIX="${1#*=}" ;;
 			-h | --help)
 				usage 0 ;;
-			"")
-				break ;;
+			--*)
+				;;
 			*)
-				shift 1 ;;
+				usage 1 ;;
 		esac
+		[ "$#" -gt 0 ] && shift
 	done
 fi
 
+[ -d "$INCLUDE" ] || usage 1
+if [ "${LIBBPF_DIR-unused}" != "unused" ]; then
+	[ -d "$LIBBPF_DIR" ] || usage 1
+fi
+if [ "${LIBBPF_FORCE-unused}" != "unused" ]; then
+	if [ "$LIBBPF_FORCE" != 'on' ] && [ "$LIBBPF_FORCE" != 'off' ]; then
+		usage 1
+	fi
+fi
+[ -z "$PREFIX" ] && usage 1
+[ -z "$LIBDIR" ] && usage 1
+
 echo "# Generated config based on" $INCLUDE >$CONFIG
 quiet_config >> $CONFIG
 
@@ -546,6 +598,7 @@ if ! grep -q TC_CONFIG_NO_XT $CONFIG; th
 fi
 
 echo
+check_lib_dir
 if ! grep -q TC_CONFIG_NO_XT $CONFIG; then
 	echo -n "iptables modules directory: "
 	check_ipt_lib_dir
@@ -560,6 +613,9 @@ check_name_to_handle_at
 echo -n "SELinux support: "
 check_selinux
 
+echo -n "libtirpc support: "
+check_tirpc
+
 echo -n "libbpf support: "
 check_libbpf
 
diff -pruN 5.15.0-1/dcb/dcb_app.c 5.19.0-1/dcb/dcb_app.c
--- 5.15.0-1/dcb/dcb_app.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/dcb/dcb_app.c	2022-08-02 18:36:33.000000000 +0000
@@ -654,6 +654,8 @@ static int dcb_cmd_app_show(struct dcb *
 			dcb_app_print_dgram_port_prio(&tab);
 		} else if (matches(*argv, "port-prio") == 0) {
 			dcb_app_print_port_prio(&tab);
+		} else if (matches(*argv, "default-prio") == 0) {
+			dcb_app_print_default_prio(&tab);
 		} else {
 			fprintf(stderr, "What is \"%s\"?\n", *argv);
 			dcb_app_help_show_flush();
diff -pruN 5.15.0-1/dcb/dcb.c 5.19.0-1/dcb/dcb.c
--- 5.15.0-1/dcb/dcb.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/dcb/dcb.c	2022-08-02 18:36:33.000000000 +0000
@@ -106,7 +106,7 @@ static int dcb_set_attribute_attr_cb(con
 {
 	struct dcb_set_attribute_response *resp = data;
 	uint16_t len;
-	uint8_t err;
+	int8_t err;
 
 	if (mnl_attr_get_type(attr) != resp->response_attr)
 		return MNL_CB_OK;
@@ -117,10 +117,12 @@ static int dcb_set_attribute_attr_cb(con
 		return MNL_CB_ERROR;
 	}
 
+	/* The attribute is formally u8, but actually an i8 containing a
+	 * negative errno value.
+	 */
 	err = mnl_attr_get_u8(attr);
 	if (err) {
-		fprintf(stderr, "Error when attempting to set attribute: %s\n",
-			strerror(err));
+		errno = -err;
 		return MNL_CB_ERROR;
 	}
 
@@ -242,9 +244,11 @@ static int __dcb_set_attribute(struct dc
 	if (ret)
 		return ret;
 
+	errno = 0;
 	ret = dcb_talk(dcb, nlh, dcb_set_attribute_cb, &resp);
 	if (ret) {
-		perror("Attribute write");
+		if (errno)
+			perror("Attribute write");
 		return ret;
 	}
 	return 0;
@@ -326,51 +330,51 @@ int dcb_set_attribute_bare(struct dcb *d
 
 void dcb_print_array_u8(const __u8 *array, size_t size)
 {
-	SPRINT_BUF(b);
 	size_t i;
 
 	for (i = 0; i < size; i++) {
-		snprintf(b, sizeof(b), "%zd:%%d ", i);
-		print_uint(PRINT_ANY, NULL, b, array[i]);
+		print_uint(PRINT_JSON, NULL, NULL, array[i]);
+		print_uint(PRINT_FP, NULL, "%zd:", i);
+		print_uint(PRINT_FP, NULL, "%d ", array[i]);
 	}
 }
 
 void dcb_print_array_u64(const __u64 *array, size_t size)
 {
-	SPRINT_BUF(b);
 	size_t i;
 
 	for (i = 0; i < size; i++) {
-		snprintf(b, sizeof(b), "%zd:%%" PRIu64 " ", i);
-		print_u64(PRINT_ANY, NULL, b, array[i]);
+		print_u64(PRINT_JSON, NULL, NULL, array[i]);
+		print_uint(PRINT_FP, NULL, "%zd:", i);
+		print_u64(PRINT_FP, NULL, "%" PRIu64 " ", array[i]);
 	}
 }
 
 void dcb_print_array_on_off(const __u8 *array, size_t size)
 {
-	SPRINT_BUF(b);
 	size_t i;
 
 	for (i = 0; i < size; i++) {
-		snprintf(b, sizeof(b), "%zd:%%s ", i);
-		print_on_off(PRINT_ANY, NULL, b, array[i]);
+		print_on_off(PRINT_JSON, NULL, NULL, array[i]);
+		print_uint(PRINT_FP, NULL, "%zd:", i);
+		print_on_off(PRINT_FP, NULL, "%s ", array[i]);
 	}
 }
 
 void dcb_print_array_kw(const __u8 *array, size_t array_size,
 			const char *const kw[], size_t kw_size)
 {
-	SPRINT_BUF(b);
 	size_t i;
 
 	for (i = 0; i < array_size; i++) {
+		const char *str = "???";
 		__u8 emt = array[i];
 
-		snprintf(b, sizeof(b), "%zd:%%s ", i);
 		if (emt < kw_size && kw[emt])
-			print_string(PRINT_ANY, NULL, b, kw[emt]);
-		else
-			print_string(PRINT_ANY, NULL, b, "???");
+			str = kw[emt];
+		print_string(PRINT_JSON, NULL, NULL, str);
+		print_uint(PRINT_FP, NULL, "%zd:", i);
+		print_string(PRINT_FP, NULL, "%s ", str);
 	}
 }
 
diff -pruN 5.15.0-1/dcb/Makefile 5.19.0-1/dcb/Makefile
--- 5.15.0-1/dcb/Makefile	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/dcb/Makefile	2022-08-02 18:36:33.000000000 +0000
@@ -1,10 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 include ../config.mk
 
-TARGETS :=
-
-ifeq ($(HAVE_MNL),y)
-
 DCBOBJ = dcb.o \
          dcb_app.o \
          dcb_buffer.o \
@@ -15,8 +11,6 @@ DCBOBJ = dcb.o \
 TARGETS += dcb
 LDLIBS += -lm
 
-endif
-
 all: $(TARGETS) $(LIBS)
 
 dcb: $(DCBOBJ) $(LIBNETLINK)
diff -pruN 5.15.0-1/debian/changelog 5.19.0-1/debian/changelog
--- 5.15.0-1/debian/changelog	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/changelog	2022-08-03 12:26:05.000000000 +0000
@@ -1,3 +1,55 @@
+iproute2 (5.19.0-1) unstable; urgency=medium
+
+  * Update upstream source from tag 'upstream/5.19.0'
+  * Refresh patch for 5.19
+  * Update Lintian overrides for new syntax
+
+ -- Luca Boccassi <bluca@debian.org>  Wed, 03 Aug 2022 13:26:05 +0100
+
+iproute2 (5.18.0-1) unstable; urgency=medium
+
+  * Update upstream source from tag 'upstream/5.18.0'
+  * Bump Standards-Version to 4.6.1, no changes
+  * Update Lintian overrides for new version
+
+ -- Luca Boccassi <bluca@debian.org>  Wed, 08 Jun 2022 14:47:01 +0100
+
+iproute2 (5.17.0-2) unstable; urgency=medium
+
+  * d/rules: use execute_after instead of override
+  * Switch main branch to debian/sid
+  * Remove outdated README.source
+  * Move maintainership to Debian Kernel Team
+  * Drop unneeded Lintian override
+  * Override Lintian warning about missing upstream bug tracking metadata
+  * iproute2-doc: install examples
+
+ -- Luca Boccassi <bluca@debian.org>  Mon, 18 Apr 2022 22:10:16 +0200
+
+iproute2 (5.17.0-1) unstable; urgency=medium
+
+  * Fix Lintian warnings regarding d/u/metadata.
+  * Update upstream source from tag 'upstream/5.17.0'
+
+ -- Luca Boccassi <bluca@debian.org>  Fri, 25 Mar 2022 10:48:29 +0000
+
+iproute2 (5.16.0-2) unstable; urgency=medium
+
+  * Drop manual /usr/lib/tc/m_ipt.so -> m_xt.so symlink (Closes: #1004059)
+
+ -- Luca Boccassi <bluca@debian.org>  Thu, 20 Jan 2022 10:29:38 +0000
+
+iproute2 (5.16.0-1) unstable; urgency=medium
+
+  * Drop 0004-sync-iptables-header.patch, not necessary anymore
+  * Update upstream source from tag 'upstream/5.16.0'
+  * Update iproute2.install: routef has been dropped
+  * Bump d/copyright year range
+  * Update Lintian overrides
+  * Suggest python3:any for /usr/bin/routel
+
+ -- Luca Boccassi <bluca@debian.org>  Thu, 13 Jan 2022 22:57:57 +0000
+
 iproute2 (5.15.0-1) unstable; urgency=medium
 
   * Update upstream source from tag 'upstream/5.15.0'
@@ -1570,7 +1622,7 @@ iproute (961225-2) unstable frozen; urge
   * Added routing.txt from /usr/src/linux/Documentation/networking/routing.txt
   * Newer version of debmake.
 
- -- Tom Lees <tom@lpsg.demon.co.uk>  Mon, 17 Apr 1997 17:00:36 +0100
+ -- Tom Lees <tom@lpsg.demon.co.uk>  Thu, 17 Apr 1997 17:00:36 +0100
 
 iproute (961225-1) unstable; urgency=low
 
diff -pruN 5.15.0-1/debian/control 5.19.0-1/debian/control
--- 5.15.0-1/debian/control	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/control	2022-08-03 10:37:45.000000000 +0000
@@ -1,12 +1,12 @@
 Source: iproute2
 Section: net
 Priority: optional
-Maintainer: Alexander Wirt <formorer@debian.org>
+Maintainer: Debian Kernel Team <debian-kernel@lists.debian.org>
 Uploaders: Luca Boccassi <bluca@debian.org>
 Homepage: https://wiki.linuxfoundation.org/networking/iproute2
-Vcs-Browser: https://salsa.debian.org/debian/iproute2
-Vcs-Git: https://salsa.debian.org/debian/iproute2.git
-Standards-Version: 4.6.0
+Vcs-Browser: https://salsa.debian.org/kernel-team/iproute2
+Vcs-Git: https://salsa.debian.org/kernel-team/iproute2.git
+Standards-Version: 4.6.1
 Rules-Requires-Root: no
 Build-Depends: bison,
                debhelper-compat (= 13),
@@ -33,7 +33,7 @@ Conflicts: arpd, iproute (<< 20130000-1)
 Replaces: iproute
 Depends: ${misc:Depends}, ${shlibs:Depends}, libcap2-bin,
 Recommends: ${ipmods:Recommends}
-Suggests: iproute2-doc
+Suggests: iproute2-doc, python3:any
 Multi-Arch: foreign
 Description: networking and traffic control tools
  The iproute2 suite is a collection of utilities for networking and
diff -pruN 5.15.0-1/debian/copyright 5.19.0-1/debian/copyright
--- 5.15.0-1/debian/copyright	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/copyright	2022-06-14 10:05:10.000000000 +0000
@@ -6,6 +6,7 @@ Comment:
  http://devresources.linuxfoundation.org/dev/iproute2/download/
  http://developer.osdl.org/dev/iproute2
  ftp://ftp.inr.ac.ru/ip-routing/iproute2-2.4.7-now-ss010824.tar.gz
+Upstream-Contact: netdev@vger.kernel.org
 
 Files: *
 Copyright: 1996-2001 Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
@@ -37,7 +38,7 @@ Copyright: 1996 Tom Lees <tom@lpsg.demon
            1999-2003 Juan Cespedes <cespedes@debian.org>
            2005-2017 Alexander Wirt <formorer@debian.org>
            2008-2015 Andreas Henriksson <andreas@fatal.se>
-           2017 Luca Boccassi <bluca@debian.org>
+           2017-2022 Luca Boccassi <bluca@debian.org>
 License: GPL-2
 
 License: GPL-2
diff -pruN 5.15.0-1/debian/gbp.conf 5.19.0-1/debian/gbp.conf
--- 5.15.0-1/debian/gbp.conf	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/gbp.conf	2022-06-14 10:20:42.000000000 +0000
@@ -1,5 +1,5 @@
 [DEFAULT]
-debian-branch = master
+debian-branch = debian/sid
 upstream-branch = upstream
 pristine-tar = True
 compression = xz
diff -pruN 5.15.0-1/debian/.gitlab-ci.yml 5.19.0-1/debian/.gitlab-ci.yml
--- 5.15.0-1/debian/.gitlab-ci.yml	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/.gitlab-ci.yml	1970-01-01 00:00:00.000000000 +0000
@@ -1,4 +0,0 @@
----
-include:
-  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
-  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
diff -pruN 5.15.0-1/debian/iproute2-doc.docs 5.19.0-1/debian/iproute2-doc.docs
--- 5.15.0-1/debian/iproute2-doc.docs	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/iproute2-doc.docs	2022-06-14 10:05:21.000000000 +0000
@@ -1,2 +1,3 @@
 README*
 doc/*
+examples/
diff -pruN 5.15.0-1/debian/iproute2.install 5.19.0-1/debian/iproute2.install
--- 5.15.0-1/debian/iproute2.install	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/iproute2.install	2022-08-03 10:37:45.000000000 +0000
@@ -9,7 +9,6 @@ sbin/ip /bin
 sbin/lnstat /usr/bin/
 sbin/nstat /usr/bin/
 sbin/rdma /usr/bin
-sbin/routef /usr/bin
 sbin/routel /usr/bin
 sbin/rtacct
 sbin/rtmon
@@ -17,6 +16,6 @@ sbin/ss /bin
 sbin/tc
 sbin/tipc
 sbin/vdpa
-usr/lib/tc/
+usr/lib/*/tc
 usr/share/bash-completion/
 usr/share/man/
diff -pruN 5.15.0-1/debian/iproute2.links 5.19.0-1/debian/iproute2.links
--- 5.15.0-1/debian/iproute2.links	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/iproute2.links	2022-06-08 13:34:21.000000000 +0000
@@ -1,4 +1,3 @@
 /usr/bin/lnstat usr/bin/ctstat
 /usr/bin/lnstat usr/bin/rtstat
-/usr/lib/tc/m_xt.so usr/lib/tc/m_ipt.so
 bin/ip sbin/ip
diff -pruN 5.15.0-1/debian/iproute2.lintian-overrides 5.19.0-1/debian/iproute2.lintian-overrides
--- 5.15.0-1/debian/iproute2.lintian-overrides	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/iproute2.lintian-overrides	2022-08-03 12:26:05.000000000 +0000
@@ -1,17 +1,15 @@
-# False positives, eg: ip link -> ip-link
-iproute2: spare-manual-page
 # False positives
-iproute2: spelling-error-in-binary bin/ip iif if
-iproute2: spelling-error-in-binary sbin/tc iif if
+iproute2: spelling-error-in-binary iif if [bin/ip]
+iproute2: spelling-error-in-binary iif if [sbin/tc]
 iproute2: spelling-error-in-copyright Taht That
-iproute2: typo-in-manual-page usr/share/man/man8/ip-mroute.8.gz line 14 iif if
-iproute2: typo-in-manual-page usr/share/man/man8/ip-mroute.8.gz line 41 iif if
-iproute2: typo-in-manual-page usr/share/man/man8/ip-route.8.gz line 1137 iif if
-iproute2: typo-in-manual-page usr/share/man/man8/ip-route.8.gz line 1183 iif if
-iproute2: typo-in-manual-page usr/share/man/man8/ip-route.8.gz line 1190 iif if
-iproute2: typo-in-manual-page usr/share/man/man8/ip-route.8.gz line 33 iif if
-iproute2: typo-in-manual-page usr/share/man/man8/ip-rule.8.gz line 219 iif if
-iproute2: typo-in-manual-page usr/share/man/man8/ip-rule.8.gz line 41 iif if
-iproute2: typo-in-manual-page usr/share/man/man8/tc-codel.8.gz line 120 Taht That
-iproute2: typo-in-manual-page usr/share/man/man8/tc-flow.8.gz line 191 iif if
-iproute2: typo-in-manual-page usr/share/man/man8/tc-flow.8.gz line 55 iif if
+iproute2: typo-in-manual-page Taht That [usr/share/man/man8/tc-codel.8.gz:120]
+iproute2: typo-in-manual-page iif if [usr/share/man/man8/ip-mroute.8.gz:14]
+iproute2: typo-in-manual-page iif if [usr/share/man/man8/ip-mroute.8.gz:41]
+iproute2: typo-in-manual-page iif if [usr/share/man/man8/ip-route.8.gz:1167]
+iproute2: typo-in-manual-page iif if [usr/share/man/man8/ip-route.8.gz:1213]
+iproute2: typo-in-manual-page iif if [usr/share/man/man8/ip-route.8.gz:1220]
+iproute2: typo-in-manual-page iif if [usr/share/man/man8/ip-route.8.gz:33]
+iproute2: typo-in-manual-page iif if [usr/share/man/man8/ip-rule.8.gz:219]
+iproute2: typo-in-manual-page iif if [usr/share/man/man8/ip-rule.8.gz:41]
+iproute2: typo-in-manual-page iif if [usr/share/man/man8/tc-flow.8.gz:191]
+iproute2: typo-in-manual-page iif if [usr/share/man/man8/tc-flow.8.gz:55]
diff -pruN 5.15.0-1/debian/patches/0001-Add-moo-feature.patch 5.19.0-1/debian/patches/0001-Add-moo-feature.patch
--- 5.15.0-1/debian/patches/0001-Add-moo-feature.patch	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/patches/0001-Add-moo-feature.patch	2022-08-03 12:25:25.000000000 +0000
@@ -1,21 +1,15 @@
-From: Alexander Wirt <formorer@debian.org>
-Date: Mon, 10 Jun 2013 11:47:00 +0200
-Subject: Add moo feature
-
----
- ip/ip.c |   15 +++++++++++++++
- 1 file changed, 15 insertions(+)
-
+Author: Alexander Wirt <formorer@debian.org>
+Description: Add moo feature
+Forwarded: no, critical value-add business feature for Debian
 --- a/ip/ip.c
 +++ b/ip/ip.c
-@@ -86,6 +86,20 @@
+@@ -86,6 +86,19 @@
  	return 0;
  }
  
 +static int do_moo(int argc, char **argv)
 +{
-+	
-+fprintf(stderr,
++	fprintf(stderr,
 +"\n"
 +" _ __ ___   ___   ___\n"
 +"| '_ ` _ \\ / _ \\ / _ \\\n"
@@ -25,15 +19,15 @@ Subject: Add moo feature
 +"P.S. no real cows were harmed for this moo\n");
 +	exit(1);
 +}
-+                       
++
  static const struct cmd {
  	const char *cmd;
  	int (*func)(int argc, char **argv);
-@@ -123,6 +137,7 @@
- 	{ "mptcp",	do_mptcp },
- 	{ "ioam",	do_ioam6 },
- 	{ "help",	do_help },
-+	{ "moo",	do_moo }, 
- 	{ 0 }
- };
- 
+@@ -104,6 +117,7 @@
+ 	{ "fou",	do_ipfou },
+ 	{ "ila",	do_ipila },
+ 	{ "macsec",	do_ipmacsec },
++	{ "moo",	do_moo },
+ 	{ "tunnel",	do_iptunnel },
+ 	{ "tunl",	do_iptunnel },
+ 	{ "tuntap",	do_iptuntap },
diff -pruN 5.15.0-1/debian/patches/0004-sync-iptables-header.patch 5.19.0-1/debian/patches/0004-sync-iptables-header.patch
--- 5.15.0-1/debian/patches/0004-sync-iptables-header.patch	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/patches/0004-sync-iptables-header.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,43 +0,0 @@
-Description: Sync header from iptables
- The current versions in several suites have the same content:
-  - 1.6.0+snapshot20161117-6 (stretch)
-  - 1.6.1-2 (unstable)
-Bug: https://bugs.debian.og/868059
-Forwarded: not-needed
-Author: Cyril Brulebois <cyril@debamax.com>
-Last-Update: 2017-11-22
---- a/include/xtables.h
-+++ b/include/xtables.h
-@@ -437,6 +437,17 @@ struct xtables_globals
- 
- #define XT_GETOPT_TABLEEND {.name = NULL, .has_arg = false}
- 
-+/*
-+ * enum op-
-+ *
-+ * For writing clean nftables translations code
-+ */
-+enum xt_op {
-+	XT_OP_EQ,
-+	XT_OP_NEQ,
-+	XT_OP_MAX,
-+};
-+
- #ifdef __cplusplus
- extern "C" {
- #endif
-@@ -548,6 +581,14 @@ extern void xtables_lmap_free(struct xta
- extern int xtables_lmap_name2id(const struct xtables_lmap *, const char *);
- extern const char *xtables_lmap_id2name(const struct xtables_lmap *, int);
- 
-+/* xlate infrastructure */
-+struct xt_xlate *xt_xlate_alloc(int size);
-+void xt_xlate_free(struct xt_xlate *xl);
-+void xt_xlate_add(struct xt_xlate *xl, const char *fmt, ...);
-+void xt_xlate_add_comment(struct xt_xlate *xl, const char *comment);
-+const char *xt_xlate_get_comment(struct xt_xlate *xl);
-+const char *xt_xlate_get(struct xt_xlate *xl);
-+
- #ifdef XTABLES_INTERNAL
- 
- /* Shipped modules rely on this... */
diff -pruN 5.15.0-1/debian/patches/series 5.19.0-1/debian/patches/series
--- 5.15.0-1/debian/patches/series	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/patches/series	2022-06-08 13:34:21.000000000 +0000
@@ -1,2 +1 @@
 0001-Add-moo-feature.patch
-0004-sync-iptables-header.patch
diff -pruN 5.15.0-1/debian/README.source 5.19.0-1/debian/README.source
--- 5.15.0-1/debian/README.source	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/README.source	1970-01-01 00:00:00.000000000 +0000
@@ -1,58 +0,0 @@
-This package uses quilt to manage all modifications to the upstream
-source.  Changes are stored in the source package as diffs in
-debian/patches and applied during the build.
-
-To configure quilt to use debian/patches instead of patches, you want
-either to export QUILT_PATCHES=debian/patches in your environment
-or use this snippet in your ~/.quiltrc:
-
-    for where in ./ ../ ../../ ../../../ ../../../../ ../../../../../; do
-        if [ -e ${where}debian/rules -a -d ${where}debian/patches ]; then
-                export QUILT_PATCHES=debian/patches
-                break
-        fi
-    done
-
-To get the fully patched source after unpacking the source package, cd to
-the root level of the source package and run:
-
-    quilt push -a
-
-The last patch listed in debian/patches/series will become the current
-patch.
-
-To add a new set of changes, first run quilt push -a, and then run:
-
-    quilt new <patch>
-
-where <patch> is a descriptive name for the patch, used as the filename in
-debian/patches.  Then, for every file that will be modified by this patch,
-run:
-
-    quilt add <file>
-
-before editing those files.  You must tell quilt with quilt add what files
-will be part of the patch before making changes or quilt will not work
-properly.  After editing the files, run:
-
-    quilt refresh
-
-to save the results as a patch.
-
-Alternately, if you already have an external patch and you just want to
-add it to the build system, run quilt push -a and then:
-
-    quilt import -P <patch> /path/to/patch
-    quilt push -a
-
-(add -p 0 to quilt import if needed). <patch> as above is the filename to
-use in debian/patches.  The last quilt push -a will apply the patch to
-make sure it works properly.
-
-To remove an existing patch from the list of patches that will be applied,
-run:
-
-    quilt delete <patch>
-
-You may need to run quilt pop -a to unapply patches first before running
-this command.
diff -pruN 5.15.0-1/debian/rules 5.19.0-1/debian/rules
--- 5.15.0-1/debian/rules	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/rules	2022-08-03 10:37:45.000000000 +0000
@@ -28,13 +28,12 @@ export NETNS_RUN_DIR=/run/netns
 %:
 	dh $@
 
-override_dh_clean:
-	dh_clean
+execute_after_dh_clean:
 	make distclean
 
 override_dh_shlibdeps:
 	dh_shlibdeps -a -Xq_atm.so -Xm_xt.so -Xm_ipt.so
-	dh_shlibdeps -a -- -pipmods -dRecommends -e debian/iproute2/usr/lib/tc/m_xt.so -e debian/iproute2/usr/lib/tc/q_atm.so -xlibc6
+	dh_shlibdeps -a -- -pipmods -dRecommends -e debian/iproute2/usr/lib/$(DEB_HOST_MULTIARCH)/tc/m_xt.so -e debian/iproute2/usr/lib/$(DEB_HOST_MULTIARCH)/tc/q_atm.so -xlibc6
 
 override_dh_auto_test:
 	# upstream test suite needs root and leaves machine unclean, skip it
diff -pruN 5.15.0-1/debian/salsa-ci.yml 5.19.0-1/debian/salsa-ci.yml
--- 5.15.0-1/debian/salsa-ci.yml	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/debian/salsa-ci.yml	2022-06-14 10:20:42.000000000 +0000
@@ -0,0 +1,9 @@
+---
+include:
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
+  - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
+
+variables:
+  # Many false positives due to issue in binutils:
+  # https://bugs.debian.org/1000977
+  SALSA_CI_LINTIAN_SUPPRESS_TAGS: "elf-error"
diff -pruN 5.15.0-1/debian/source/lintian-overrides 5.19.0-1/debian/source/lintian-overrides
--- 5.15.0-1/debian/source/lintian-overrides	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/debian/source/lintian-overrides	2022-08-03 12:25:25.000000000 +0000
@@ -0,0 +1,2 @@
+# Bug reporting happens on the LKML
+iproute2 source: upstream-metadata-missing-bug-tracking
diff -pruN 5.15.0-1/debian/upstream/metadata 5.19.0-1/debian/upstream/metadata
--- 5.15.0-1/debian/upstream/metadata	2021-11-05 23:08:14.000000000 +0000
+++ 5.19.0-1/debian/upstream/metadata	2022-06-14 10:05:10.000000000 +0000
@@ -1,4 +1,2 @@
-Name: iproute2
 Repository: https://git.kernel.org/pub/scm/network/iproute2/iproute2.git
 Repository-Browse: https://git.kernel.org/pub/scm/network/iproute2/iproute2.git
-Contact: stephen@networkplumber.org
diff -pruN 5.15.0-1/devlink/devlink.c 5.19.0-1/devlink/devlink.c
--- 5.15.0-1/devlink/devlink.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/devlink/devlink.c	2022-08-02 18:36:33.000000000 +0000
@@ -315,7 +315,7 @@ struct dl_opts {
 	bool dpipe_counters_enabled;
 	enum devlink_eswitch_encap_mode eswitch_encap_mode;
 	const char *resource_path;
-	uint64_t resource_size;
+	__u64 resource_size;
 	uint32_t resource_id;
 	bool resource_id_valid;
 	const char *param_name;
@@ -323,12 +323,12 @@ struct dl_opts {
 	enum devlink_param_cmode cmode;
 	char *region_name;
 	uint32_t region_snapshot_id;
-	uint64_t region_address;
-	uint64_t region_length;
+	__u64 region_address;
+	__u64 region_length;
 	const char *flash_file_name;
 	const char *flash_component;
 	const char *reporter_name;
-	uint64_t reporter_graceful_period;
+	__u64 reporter_graceful_period;
 	bool reporter_auto_recover;
 	bool reporter_auto_dump;
 	const char *trap_name;
@@ -337,8 +337,8 @@ struct dl_opts {
 	bool netns_is_pid;
 	uint32_t netns;
 	uint32_t trap_policer_id;
-	uint64_t trap_policer_rate;
-	uint64_t trap_policer_burst;
+	__u64 trap_policer_rate;
+	__u64 trap_policer_burst;
 	char port_function_hw_addr[MAX_ADDR_LEN];
 	uint32_t port_function_hw_addr_len;
 	uint32_t overwrite_mask;
@@ -367,6 +367,7 @@ struct dl {
 	bool pretty_output;
 	bool verbose;
 	bool stats;
+	bool hex;
 	struct {
 		bool present;
 		char *bus_name;
@@ -857,62 +858,6 @@ static int ifname_map_rev_lookup(struct
 	return -ENOENT;
 }
 
-static int strtouint64_t(const char *str, uint64_t *p_val)
-{
-	char *endptr;
-	unsigned long long int val;
-
-	val = strtoull(str, &endptr, 10);
-	if (endptr == str || *endptr != '\0')
-		return -EINVAL;
-	if (val > ULONG_MAX)
-		return -ERANGE;
-	*p_val = val;
-	return 0;
-}
-
-static int strtouint32_t(const char *str, uint32_t *p_val)
-{
-	char *endptr;
-	unsigned long int val;
-
-	val = strtoul(str, &endptr, 10);
-	if (endptr == str || *endptr != '\0')
-		return -EINVAL;
-	if (val > UINT_MAX)
-		return -ERANGE;
-	*p_val = val;
-	return 0;
-}
-
-static int strtouint16_t(const char *str, uint16_t *p_val)
-{
-	char *endptr;
-	unsigned long int val;
-
-	val = strtoul(str, &endptr, 10);
-	if (endptr == str || *endptr != '\0')
-		return -EINVAL;
-	if (val > USHRT_MAX)
-		return -ERANGE;
-	*p_val = val;
-	return 0;
-}
-
-static int strtouint8_t(const char *str, uint8_t *p_val)
-{
-	char *endptr;
-	unsigned long int val;
-
-	val = strtoul(str, &endptr, 10);
-	if (endptr == str || *endptr != '\0')
-		return -EINVAL;
-	if (val > UCHAR_MAX)
-		return -ERANGE;
-	*p_val = val;
-	return 0;
-}
-
 static int strtobool(const char *str, bool *p_val)
 {
 	bool val;
@@ -980,7 +925,7 @@ static int __dl_argv_handle_port(char *s
 		pr_err("Port identification \"%s\" is invalid\n", str);
 		return err;
 	}
-	err = strtouint32_t(portstr, p_port_index);
+	err = get_u32(p_port_index, portstr, 10);
 	if (err) {
 		pr_err("Port index \"%s\" is not a number or not within range\n",
 		       portstr);
@@ -1159,7 +1104,7 @@ static int dl_argv_handle_rate(struct dl
 	}
 
 	if (strspn(identifier, "0123456789") == strlen(identifier)) {
-		err = strtouint32_t(identifier, p_port_index);
+		err = get_u32(p_port_index, identifier, 10);
 		if (err) {
 			pr_err("Port index \"%s\" is not a number"
 			       " or not within range\n", identifier);
@@ -1173,7 +1118,7 @@ static int dl_argv_handle_rate(struct dl
 	return 0;
 }
 
-static int dl_argv_uint64_t(struct dl *dl, uint64_t *p_val)
+static int dl_argv_uint64_t(struct dl *dl, __u64 *p_val)
 {
 	char *str = dl_argv_next(dl);
 	int err;
@@ -1183,7 +1128,7 @@ static int dl_argv_uint64_t(struct dl *d
 		return -EINVAL;
 	}
 
-	err = strtouint64_t(str, p_val);
+	err = get_u64(p_val, str, 10);
 	if (err) {
 		pr_err("\"%s\" is not a number or not within range\n", str);
 		return err;
@@ -1201,7 +1146,7 @@ static int dl_argv_uint32_t(struct dl *d
 		return -EINVAL;
 	}
 
-	err = strtouint32_t(str, p_val);
+	err = get_u32(p_val, str, 10);
 	if (err) {
 		pr_err("\"%s\" is not a number or not within range\n", str);
 		return err;
@@ -1219,7 +1164,7 @@ static int dl_argv_uint16_t(struct dl *d
 		return -EINVAL;
 	}
 
-	err = strtouint16_t(str, p_val);
+	err = get_u16(p_val, str, 10);
 	if (err) {
 		pr_err("\"%s\" is not a number or not within range\n", str);
 		return err;
@@ -3036,6 +2981,7 @@ static int cmd_dev_param_show_cb(const s
 struct param_ctx {
 	struct dl *dl;
 	int nla_type;
+	bool cmode_found;
 	union {
 		uint8_t vu8;
 		uint16_t vu16;
@@ -3088,6 +3034,7 @@ static int cmd_dev_param_set_cb(const st
 
 		cmode = mnl_attr_get_u8(nla_value[DEVLINK_ATTR_PARAM_VALUE_CMODE]);
 		if (cmode == dl->opts.cmode) {
+			ctx->cmode_found = true;
 			val_attr = nla_value[DEVLINK_ATTR_PARAM_VALUE_DATA];
 			switch (nla_type) {
 			case MNL_TYPE_U8:
@@ -3140,6 +3087,10 @@ static int cmd_dev_param_set(struct dl *
 	err = mnlu_gen_socket_sndrcv(&dl->nlg, nlh, cmd_dev_param_set_cb, &ctx);
 	if (err)
 		return err;
+	if (!ctx.cmode_found) {
+		pr_err("Configuration mode not supported\n");
+		return -ENOTSUP;
+	}
 
 	nlh = mnlu_gen_socket_cmd_prepare(&dl->nlg, DEVLINK_CMD_PARAM_SET,
 			       NLM_F_REQUEST | NLM_F_ACK);
@@ -3159,7 +3110,7 @@ static int cmd_dev_param_set(struct dl *
 						      &val_u32);
 			val_u8 = val_u32;
 		} else {
-			err = strtouint8_t(dl->opts.param_value, &val_u8);
+			err = get_u8(&val_u8, dl->opts.param_value, 10);
 		}
 		if (err)
 			goto err_param_value_parse;
@@ -3176,7 +3127,7 @@ static int cmd_dev_param_set(struct dl *
 						      &val_u32);
 			val_u16 = val_u32;
 		} else {
-			err = strtouint16_t(dl->opts.param_value, &val_u16);
+			err = get_u16(&val_u16, dl->opts.param_value, 10);
 		}
 		if (err)
 			goto err_param_value_parse;
@@ -3192,7 +3143,7 @@ static int cmd_dev_param_set(struct dl *
 						      dl->opts.param_value,
 						      &val_u32);
 		else
-			err = strtouint32_t(dl->opts.param_value, &val_u32);
+			err = get_u32(&val_u32, dl->opts.param_value, 10);
 		if (err)
 			goto err_param_value_parse;
 		if (val_u32 == ctx.value.vu32)
@@ -4421,7 +4372,7 @@ static int cmd_port_param_set(struct dl
 						      &val_u32);
 			val_u8 = val_u32;
 		} else {
-			err = strtouint8_t(dl->opts.param_value, &val_u8);
+			err = get_u8(&val_u8, dl->opts.param_value, 10);
 		}
 		if (err)
 			goto err_param_value_parse;
@@ -4438,7 +4389,7 @@ static int cmd_port_param_set(struct dl
 						      &val_u32);
 			val_u16 = val_u32;
 		} else {
-			err = strtouint16_t(dl->opts.param_value, &val_u16);
+			err = get_u16(&val_u16, dl->opts.param_value, 10);
 		}
 		if (err)
 			goto err_param_value_parse;
@@ -4454,7 +4405,7 @@ static int cmd_port_param_set(struct dl
 						      dl->opts.param_value,
 						      &val_u32);
 		else
-			err = strtouint32_t(dl->opts.param_value, &val_u32);
+			err = get_u32(&val_u32, dl->opts.param_value, 10);
 		if (err)
 			goto err_param_value_parse;
 		if (val_u32 == ctx.value.vu32)
@@ -7845,6 +7796,10 @@ static void pr_out_region(struct dl *dl,
 	if (tb[DEVLINK_ATTR_REGION_SNAPSHOT_ID])
 		pr_out_snapshot(dl, tb);
 
+	if (tb[DEVLINK_ATTR_REGION_MAX_SNAPSHOTS])
+		pr_out_u64(dl, "max",
+			   mnl_attr_get_u32(tb[DEVLINK_ATTR_REGION_MAX_SNAPSHOTS]));
+
 	pr_out_region_handle_end(dl);
 }
 
@@ -8090,6 +8045,8 @@ static int cmd_health_dump_clear(struct
 
 static int fmsg_value_show(struct dl *dl, int type, struct nlattr *nl_data)
 {
+	const char *num_fmt = dl->hex ? "%#x" : "%u";
+	const char *num64_fmt = dl->hex ? "%#"PRIx64 : "%"PRIu64;
 	uint8_t *data;
 	uint32_t len;
 
@@ -8099,16 +8056,16 @@ static int fmsg_value_show(struct dl *dl
 		print_bool(PRINT_ANY, NULL, "%s", mnl_attr_get_u8(nl_data));
 		break;
 	case MNL_TYPE_U8:
-		print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u8(nl_data));
+		print_uint(PRINT_ANY, NULL, num_fmt, mnl_attr_get_u8(nl_data));
 		break;
 	case MNL_TYPE_U16:
-		print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u16(nl_data));
+		print_uint(PRINT_ANY, NULL, num_fmt, mnl_attr_get_u16(nl_data));
 		break;
 	case MNL_TYPE_U32:
-		print_uint(PRINT_ANY, NULL, "%u", mnl_attr_get_u32(nl_data));
+		print_uint(PRINT_ANY, NULL, num_fmt, mnl_attr_get_u32(nl_data));
 		break;
 	case MNL_TYPE_U64:
-		print_u64(PRINT_ANY, NULL, "%"PRIu64, mnl_attr_get_u64(nl_data));
+		print_u64(PRINT_ANY, NULL, num64_fmt, mnl_attr_get_u64(nl_data));
 		break;
 	case MNL_TYPE_NUL_STRING:
 		print_string(PRINT_ANY, NULL, "%s", mnl_attr_get_str(nl_data));
@@ -8572,6 +8529,23 @@ static void cmd_health_help(void)
 	pr_err("                          [ auto_dump    { true | false } ]\n");
 }
 
+static int cmd_health_dump(struct dl *dl)
+{
+	if (dl_argv_match(dl, "help")) {
+		cmd_health_help();
+		return 0;
+	} else if (dl_argv_match(dl, "show") ||
+		   dl_argv_match(dl, "list") || dl_no_arg(dl)) {
+		dl_arg_inc(dl);
+		return cmd_health_dump_show(dl);
+	} else if (dl_argv_match(dl, "clear")) {
+		dl_arg_inc(dl);
+		return cmd_health_dump_clear(dl);
+	}
+	pr_err("Command \"%s\" not found\n", dl_argv(dl));
+	return -ENOENT;
+}
+
 static int cmd_health(struct dl *dl)
 {
 	if (dl_argv_match(dl, "help")) {
@@ -8592,13 +8566,7 @@ static int cmd_health(struct dl *dl)
 		return cmd_health_test(dl);
 	} else if (dl_argv_match(dl, "dump")) {
 		dl_arg_inc(dl);
-		if (dl_argv_match(dl, "show")) {
-			dl_arg_inc(dl);
-			return cmd_health_dump_show(dl);
-		} else if (dl_argv_match(dl, "clear")) {
-			dl_arg_inc(dl);
-			return cmd_health_dump_clear(dl);
-		}
+		return cmd_health_dump(dl);
 	} else if (dl_argv_match(dl, "set")) {
 		dl_arg_inc(dl);
 		return cmd_health_set_params(dl);
@@ -8974,7 +8942,7 @@ static void help(void)
 	pr_err("Usage: devlink [ OPTIONS ] OBJECT { COMMAND | help }\n"
 	       "       devlink [ -f[orce] ] -b[atch] filename -N[etns] netnsname\n"
 	       "where  OBJECT := { dev | port | sb | monitor | dpipe | resource | region | health | trap }\n"
-	       "       OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] -s[tatistics] }\n");
+	       "       OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] -s[tatistics] -[he]x }\n");
 }
 
 static int dl_cmd(struct dl *dl, int argc, char **argv)
@@ -9088,6 +9056,7 @@ int main(int argc, char **argv)
 		{ "statistics",		no_argument,		NULL, 's' },
 		{ "Netns",		required_argument,	NULL, 'N' },
 		{ "iec",		no_argument,		NULL, 'i' },
+		{ "hex",		no_argument,		NULL, 'x' },
 		{ NULL, 0, NULL, 0 }
 	};
 	const char *batch_file = NULL;
@@ -9103,7 +9072,7 @@ int main(int argc, char **argv)
 		return EXIT_FAILURE;
 	}
 
-	while ((opt = getopt_long(argc, argv, "Vfb:njpvsN:i",
+	while ((opt = getopt_long(argc, argv, "Vfb:njpvsN:ix",
 				  long_options, NULL)) >= 0) {
 
 		switch (opt) {
@@ -9141,6 +9110,9 @@ int main(int argc, char **argv)
 		case 'i':
 			use_iec = true;
 			break;
+		case 'x':
+			dl->hex = true;
+			break;
 		default:
 			pr_err("Unknown option.\n");
 			help();
diff -pruN 5.15.0-1/devlink/Makefile 5.19.0-1/devlink/Makefile
--- 5.15.0-1/devlink/Makefile	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/devlink/Makefile	2022-08-02 18:36:33.000000000 +0000
@@ -1,16 +1,10 @@
 # SPDX-License-Identifier: GPL-2.0
 include ../config.mk
 
-TARGETS :=
-
-ifeq ($(HAVE_MNL),y)
-
 DEVLINKOBJ = devlink.o mnlg.o
 TARGETS += devlink
 LDLIBS += -lm
 
-endif
-
 all: $(TARGETS) $(LIBS)
 
 devlink: $(DEVLINKOBJ) $(LIBNETLINK)
diff -pruN 5.15.0-1/doc/actions/actions-general 5.19.0-1/doc/actions/actions-general
--- 5.15.0-1/doc/actions/actions-general	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/doc/actions/actions-general	2022-08-02 18:36:33.000000000 +0000
@@ -72,7 +72,7 @@ action police mtu 4000 rate 1500kbit bur
 2)In order to take advantage of some of the targets written by the
 iptables people, a classifier can have a packet being massaged by an
 iptable target. I have only tested with mangler targets up to now.
-(infact anything that is not in the mangling table is disabled right now)
+(in fact anything that is not in the mangling table is disabled right now)
 
 In terms of hooks:
 *ingress is mapped to pre-routing hook
diff -pruN 5.15.0-1/doc/actions/gact-usage 5.19.0-1/doc/actions/gact-usage
--- 5.15.0-1/doc/actions/gact-usage	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/doc/actions/gact-usage	2022-08-02 18:36:33.000000000 +0000
@@ -10,7 +10,7 @@ Where:
 
 ACTION semantics
 - pass and ok are equivalent to accept
-- continue allows to restart classification lookup
+- continue allows one to restart classification lookup
 - drop drops packets
 - reclassify implies continue classification where we left off
 
diff -pruN 5.15.0-1/genl/genl_utils.h 5.19.0-1/genl/genl_utils.h
--- 5.15.0-1/genl/genl_utils.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/genl/genl_utils.h	2022-08-02 18:36:33.000000000 +0000
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _TC_UTIL_H_
-#define _TC_UTIL_H_ 1
+#ifndef _GENL_UTILS_H_
+#define _GENL_UTILS_H_ 1
 
 #include <linux/genetlink.h>
 #include "utils.h"
diff -pruN 5.15.0-1/include/bpf_util.h 5.19.0-1/include/bpf_util.h
--- 5.15.0-1/include/bpf_util.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/bpf_util.h	2022-08-02 18:36:33.000000000 +0000
@@ -287,6 +287,8 @@ int bpf_program_attach(int prog_fd, int
 
 int bpf_dump_prog_info(FILE *f, uint32_t id);
 
+int bpf(int cmd, union bpf_attr *attr, unsigned int size);
+
 #ifdef HAVE_ELF
 int bpf_send_map_fds(const char *path, const char *obj);
 int bpf_recv_map_fds(const char *path, int *fds, struct bpf_map_aux *aux,
diff -pruN 5.15.0-1/include/libnetlink.h 5.19.0-1/include/libnetlink.h
--- 5.15.0-1/include/libnetlink.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/libnetlink.h	2022-08-02 18:36:33.000000000 +0000
@@ -37,6 +37,12 @@ struct nlmsg_chain {
 	struct nlmsg_list *tail;
 };
 
+struct ipstats_req {
+	struct nlmsghdr nlh;
+	struct if_stats_msg ifsm;
+	char buf[128];
+};
+
 extern int rcvbuf;
 
 int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions)
@@ -88,7 +94,10 @@ int rtnl_fdb_linkdump_req_filter_fn(stru
 int rtnl_nsiddump_req_filter_fn(struct rtnl_handle *rth, int family,
 				req_filter_fn_t filter_fn)
 	__attribute__((warn_unused_result));
-int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask)
+int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask,
+			      int (*filter_fn)(struct ipstats_req *req,
+					       void *data),
+			      void *filter_data)
 	__attribute__((warn_unused_result));
 int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req,
 			     int len)
@@ -103,6 +112,10 @@ int rtnl_nexthop_bucket_dump_req(struct
 				 req_filter_fn_t filter_fn)
 	__attribute__((warn_unused_result));
 
+int rtnl_tunneldump_req(struct rtnl_handle *rth, int family, int ifindex,
+			__u8 flags)
+	__attribute__((warn_unused_result));
+
 struct rtnl_ctrl_data {
 	int	nsid;
 };
@@ -322,6 +335,11 @@ int rtnl_from_file(FILE *, rtnl_listen_f
 	((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_vlan_msg))))
 #endif
 
+#ifndef TUNNEL_RTA
+#define TUNNEL_RTA(r) \
+	((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct tunnel_msg))))
+#endif
+
 /* User defined nlmsg_type which is used mostly for logging netlink
  * messages from dump file */
 #define NLMSG_TSTAMP	15
diff -pruN 5.15.0-1/include/uapi/linux/amt.h 5.19.0-1/include/uapi/linux/amt.h
--- 5.15.0-1/include/uapi/linux/amt.h	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/amt.h	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2021 Taehee Yoo <ap420073@gmail.com>
+ */
+#ifndef _AMT_H_
+#define _AMT_H_
+
+enum ifla_amt_mode {
+	/* AMT interface works as Gateway mode.
+	 * The Gateway mode encapsulates IGMP/MLD traffic and decapsulates
+	 * multicast traffic.
+	 */
+	AMT_MODE_GATEWAY = 0,
+	/* AMT interface works as Relay mode.
+	 * The Relay mode encapsulates multicast traffic and decapsulates
+	 * IGMP/MLD traffic.
+	 */
+	AMT_MODE_RELAY,
+	__AMT_MODE_MAX,
+};
+
+#define AMT_MODE_MAX (__AMT_MODE_MAX - 1)
+
+enum {
+	IFLA_AMT_UNSPEC,
+	/* This attribute specify mode etier Gateway or Relay. */
+	IFLA_AMT_MODE,
+	/* This attribute specify Relay port.
+	 * AMT interface is created as Gateway mode, this attribute is used
+	 * to specify relay(remote) port.
+	 * AMT interface is created as Relay mode, this attribute is used
+	 * as local port.
+	 */
+	IFLA_AMT_RELAY_PORT,
+	/* This attribute specify Gateway port.
+	 * AMT interface is created as Gateway mode, this attribute is used
+	 * as local port.
+	 * AMT interface is created as Relay mode, this attribute is not used.
+	 */
+	IFLA_AMT_GATEWAY_PORT,
+	/* This attribute specify physical device */
+	IFLA_AMT_LINK,
+	/* This attribute specify local ip address */
+	IFLA_AMT_LOCAL_IP,
+	/* This attribute specify Relay ip address.
+	 * So, this is not used by Relay.
+	 */
+	IFLA_AMT_REMOTE_IP,
+	/* This attribute specify Discovery ip address.
+	 * When Gateway get started, it send discovery message to find the
+	 * Relay's ip address.
+	 * So, this is not used by Relay.
+	 */
+	IFLA_AMT_DISCOVERY_IP,
+	/* This attribute specify number of maximum tunnel. */
+	IFLA_AMT_MAX_TUNNELS,
+	__IFLA_AMT_MAX,
+};
+
+#define IFLA_AMT_MAX (__IFLA_AMT_MAX - 1)
+
+#endif /* _AMT_H_ */
diff -pruN 5.15.0-1/include/uapi/linux/ax25.h 5.19.0-1/include/uapi/linux/ax25.h
--- 5.15.0-1/include/uapi/linux/ax25.h	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/ax25.h	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * These are the public elements of the Linux kernel AX.25 code. A similar
+ * file netrom.h exists for the NET/ROM protocol.
+ */
+
+#ifndef	AX25_KERNEL_H
+#define	AX25_KERNEL_H
+
+#include <linux/socket.h>
+
+#define AX25_MTU	256
+#define AX25_MAX_DIGIS  8
+
+#define AX25_WINDOW	1
+#define AX25_T1		2
+#define AX25_N2		3
+#define AX25_T3		4
+#define AX25_T2		5
+#define	AX25_BACKOFF	6
+#define	AX25_EXTSEQ	7
+#define	AX25_PIDINCL	8
+#define AX25_IDLE	9
+#define AX25_PACLEN	10
+#define AX25_IAMDIGI	12
+
+#define AX25_KILL	99
+
+#define SIOCAX25GETUID		(SIOCPROTOPRIVATE+0)
+#define SIOCAX25ADDUID		(SIOCPROTOPRIVATE+1)
+#define SIOCAX25DELUID		(SIOCPROTOPRIVATE+2)
+#define SIOCAX25NOUID		(SIOCPROTOPRIVATE+3)
+#define SIOCAX25OPTRT		(SIOCPROTOPRIVATE+7)
+#define SIOCAX25CTLCON		(SIOCPROTOPRIVATE+8)
+#define SIOCAX25GETINFOOLD	(SIOCPROTOPRIVATE+9)
+#define SIOCAX25ADDFWD		(SIOCPROTOPRIVATE+10)
+#define SIOCAX25DELFWD		(SIOCPROTOPRIVATE+11)
+#define SIOCAX25DEVCTL          (SIOCPROTOPRIVATE+12)
+#define SIOCAX25GETINFO         (SIOCPROTOPRIVATE+13)
+
+#define AX25_SET_RT_IPMODE	2
+
+#define AX25_NOUID_DEFAULT	0
+#define AX25_NOUID_BLOCK	1
+
+typedef struct {
+	char		ax25_call[7];	/* 6 call + SSID (shifted ascii!) */
+} ax25_address;
+
+struct sockaddr_ax25 {
+	__kernel_sa_family_t sax25_family;
+	ax25_address	sax25_call;
+	int		sax25_ndigis;
+	/* Digipeater ax25_address sets follow */
+};
+
+#define sax25_uid	sax25_ndigis
+
+struct full_sockaddr_ax25 {
+	struct sockaddr_ax25 fsa_ax25;
+	ax25_address	fsa_digipeater[AX25_MAX_DIGIS];
+};
+
+struct ax25_routes_struct {
+	ax25_address	port_addr;
+	ax25_address	dest_addr;
+	unsigned char	digi_count;
+	ax25_address	digi_addr[AX25_MAX_DIGIS];
+};
+
+struct ax25_route_opt_struct {
+	ax25_address	port_addr;
+	ax25_address	dest_addr;
+	int		cmd;
+	int		arg;
+};
+
+struct ax25_ctl_struct {
+        ax25_address            port_addr;
+        ax25_address            source_addr;
+        ax25_address            dest_addr;
+        unsigned int            cmd;
+        unsigned long           arg;
+        unsigned char           digi_count;
+        ax25_address            digi_addr[AX25_MAX_DIGIS];
+};
+
+/* this will go away. Please do not export to user land */
+struct ax25_info_struct_deprecated {
+	unsigned int	n2, n2count;
+	unsigned int	t1, t1timer;
+	unsigned int	t2, t2timer;
+	unsigned int	t3, t3timer;
+	unsigned int	idle, idletimer;
+	unsigned int	state;
+	unsigned int	rcv_q, snd_q;
+};
+
+struct ax25_info_struct {
+	unsigned int	n2, n2count;
+	unsigned int	t1, t1timer;
+	unsigned int	t2, t2timer;
+	unsigned int	t3, t3timer;
+	unsigned int	idle, idletimer;
+	unsigned int	state;
+	unsigned int	rcv_q, snd_q;
+	unsigned int	vs, vr, va, vs_max;
+	unsigned int	paclen;
+	unsigned int	window;
+};
+
+struct ax25_fwd_struct {
+	ax25_address	port_from;
+	ax25_address	port_to;
+};
+
+#endif
diff -pruN 5.15.0-1/include/uapi/linux/batman_adv.h 5.19.0-1/include/uapi/linux/batman_adv.h
--- 5.15.0-1/include/uapi/linux/batman_adv.h	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/batman_adv.h	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,704 @@
+/* SPDX-License-Identifier: MIT */
+/* Copyright (C) B.A.T.M.A.N. contributors:
+ *
+ * Matthias Schiffer
+ */
+
+#ifndef _LINUX_BATMAN_ADV_H_
+#define _LINUX_BATMAN_ADV_H_
+
+#define BATADV_NL_NAME "batadv"
+
+#define BATADV_NL_MCAST_GROUP_CONFIG	"config"
+#define BATADV_NL_MCAST_GROUP_TPMETER	"tpmeter"
+
+/**
+ * enum batadv_tt_client_flags - TT client specific flags
+ *
+ * Bits from 0 to 7 are called _remote flags_ because they are sent on the wire.
+ * Bits from 8 to 15 are called _local flags_ because they are used for local
+ * computations only.
+ *
+ * Bits from 4 to 7 - a subset of remote flags - are ensured to be in sync with
+ * the other nodes in the network. To achieve this goal these flags are included
+ * in the TT CRC computation.
+ */
+enum batadv_tt_client_flags {
+	/**
+	 * @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table
+	 */
+	BATADV_TT_CLIENT_DEL     = (1 << 0),
+
+	/**
+	 * @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and
+	 * the new update telling its new real location has not been
+	 * received/sent yet
+	 */
+	BATADV_TT_CLIENT_ROAM    = (1 << 1),
+
+	/**
+	 * @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi
+	 * interface. This information is used by the "AP Isolation" feature
+	 */
+	BATADV_TT_CLIENT_WIFI    = (1 << 4),
+
+	/**
+	 * @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This
+	 * information is used by the Extended Isolation feature
+	 */
+	BATADV_TT_CLIENT_ISOLA	 = (1 << 5),
+
+	/**
+	 * @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from
+	 * the table
+	 */
+	BATADV_TT_CLIENT_NOPURGE = (1 << 8),
+
+	/**
+	 * @BATADV_TT_CLIENT_NEW: this client has been added to the local table
+	 * but has not been announced yet
+	 */
+	BATADV_TT_CLIENT_NEW     = (1 << 9),
+
+	/**
+	 * @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it
+	 * is kept in the table for one more originator interval for consistency
+	 * purposes
+	 */
+	BATADV_TT_CLIENT_PENDING = (1 << 10),
+
+	/**
+	 * @BATADV_TT_CLIENT_TEMP: this global client has been detected to be
+	 * part of the network but no node has already announced it
+	 */
+	BATADV_TT_CLIENT_TEMP	 = (1 << 11),
+};
+
+/**
+ * enum batadv_mcast_flags_priv - Private, own multicast flags
+ *
+ * These are internal, multicast related flags. Currently they describe certain
+ * multicast related attributes of the segment this originator bridges into the
+ * mesh.
+ *
+ * Those attributes are used to determine the public multicast flags this
+ * originator is going to announce via TT.
+ *
+ * For netlink, if BATADV_MCAST_FLAGS_BRIDGED is unset then all querier
+ * related flags are undefined.
+ */
+enum batadv_mcast_flags_priv {
+	/**
+	 * @BATADV_MCAST_FLAGS_BRIDGED: There is a bridge on top of the mesh
+	 * interface.
+	 */
+	BATADV_MCAST_FLAGS_BRIDGED			= (1 << 0),
+
+	/**
+	 * @BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS: Whether an IGMP querier
+	 * exists in the mesh
+	 */
+	BATADV_MCAST_FLAGS_QUERIER_IPV4_EXISTS		= (1 << 1),
+
+	/**
+	 * @BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS: Whether an MLD querier
+	 * exists in the mesh
+	 */
+	BATADV_MCAST_FLAGS_QUERIER_IPV6_EXISTS		= (1 << 2),
+
+	/**
+	 * @BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING: If an IGMP querier
+	 * exists, whether it is potentially shadowing multicast listeners
+	 * (i.e. querier is behind our own bridge segment)
+	 */
+	BATADV_MCAST_FLAGS_QUERIER_IPV4_SHADOWING	= (1 << 3),
+
+	/**
+	 * @BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING: If an MLD querier
+	 * exists, whether it is potentially shadowing multicast listeners
+	 * (i.e. querier is behind our own bridge segment)
+	 */
+	BATADV_MCAST_FLAGS_QUERIER_IPV6_SHADOWING	= (1 << 4),
+};
+
+/**
+ * enum batadv_gw_modes - gateway mode of node
+ */
+enum batadv_gw_modes {
+	/** @BATADV_GW_MODE_OFF: gw mode disabled */
+	BATADV_GW_MODE_OFF,
+
+	/** @BATADV_GW_MODE_CLIENT: send DHCP requests to gw servers */
+	BATADV_GW_MODE_CLIENT,
+
+	/** @BATADV_GW_MODE_SERVER: announce itself as gateway server */
+	BATADV_GW_MODE_SERVER,
+};
+
+/**
+ * enum batadv_nl_attrs - batman-adv netlink attributes
+ */
+enum batadv_nl_attrs {
+	/**
+	 * @BATADV_ATTR_UNSPEC: unspecified attribute to catch errors
+	 */
+	BATADV_ATTR_UNSPEC,
+
+	/**
+	 * @BATADV_ATTR_VERSION: batman-adv version string
+	 */
+	BATADV_ATTR_VERSION,
+
+	/**
+	 * @BATADV_ATTR_ALGO_NAME: name of routing algorithm
+	 */
+	BATADV_ATTR_ALGO_NAME,
+
+	/**
+	 * @BATADV_ATTR_MESH_IFINDEX: index of the batman-adv interface
+	 */
+	BATADV_ATTR_MESH_IFINDEX,
+
+	/**
+	 * @BATADV_ATTR_MESH_IFNAME: name of the batman-adv interface
+	 */
+	BATADV_ATTR_MESH_IFNAME,
+
+	/**
+	 * @BATADV_ATTR_MESH_ADDRESS: mac address of the batman-adv interface
+	 */
+	BATADV_ATTR_MESH_ADDRESS,
+
+	/**
+	 * @BATADV_ATTR_HARD_IFINDEX: index of the non-batman-adv interface
+	 */
+	BATADV_ATTR_HARD_IFINDEX,
+
+	/**
+	 * @BATADV_ATTR_HARD_IFNAME: name of the non-batman-adv interface
+	 */
+	BATADV_ATTR_HARD_IFNAME,
+
+	/**
+	 * @BATADV_ATTR_HARD_ADDRESS: mac address of the non-batman-adv
+	 * interface
+	 */
+	BATADV_ATTR_HARD_ADDRESS,
+
+	/**
+	 * @BATADV_ATTR_ORIG_ADDRESS: originator mac address
+	 */
+	BATADV_ATTR_ORIG_ADDRESS,
+
+	/**
+	 * @BATADV_ATTR_TPMETER_RESULT: result of run (see
+	 * batadv_tp_meter_status)
+	 */
+	BATADV_ATTR_TPMETER_RESULT,
+
+	/**
+	 * @BATADV_ATTR_TPMETER_TEST_TIME: time (msec) the run took
+	 */
+	BATADV_ATTR_TPMETER_TEST_TIME,
+
+	/**
+	 * @BATADV_ATTR_TPMETER_BYTES: amount of acked bytes during run
+	 */
+	BATADV_ATTR_TPMETER_BYTES,
+
+	/**
+	 * @BATADV_ATTR_TPMETER_COOKIE: session cookie to match tp_meter session
+	 */
+	BATADV_ATTR_TPMETER_COOKIE,
+
+	/**
+	 * @BATADV_ATTR_PAD: attribute used for padding for 64-bit alignment
+	 */
+	BATADV_ATTR_PAD,
+
+	/**
+	 * @BATADV_ATTR_ACTIVE: Flag indicating if the hard interface is active
+	 */
+	BATADV_ATTR_ACTIVE,
+
+	/**
+	 * @BATADV_ATTR_TT_ADDRESS: Client MAC address
+	 */
+	BATADV_ATTR_TT_ADDRESS,
+
+	/**
+	 * @BATADV_ATTR_TT_TTVN: Translation table version
+	 */
+	BATADV_ATTR_TT_TTVN,
+
+	/**
+	 * @BATADV_ATTR_TT_LAST_TTVN: Previous translation table version
+	 */
+	BATADV_ATTR_TT_LAST_TTVN,
+
+	/**
+	 * @BATADV_ATTR_TT_CRC32: CRC32 over translation table
+	 */
+	BATADV_ATTR_TT_CRC32,
+
+	/**
+	 * @BATADV_ATTR_TT_VID: VLAN ID
+	 */
+	BATADV_ATTR_TT_VID,
+
+	/**
+	 * @BATADV_ATTR_TT_FLAGS: Translation table client flags
+	 */
+	BATADV_ATTR_TT_FLAGS,
+
+	/**
+	 * @BATADV_ATTR_FLAG_BEST: Flags indicating entry is the best
+	 */
+	BATADV_ATTR_FLAG_BEST,
+
+	/**
+	 * @BATADV_ATTR_LAST_SEEN_MSECS: Time in milliseconds since last seen
+	 */
+	BATADV_ATTR_LAST_SEEN_MSECS,
+
+	/**
+	 * @BATADV_ATTR_NEIGH_ADDRESS: Neighbour MAC address
+	 */
+	BATADV_ATTR_NEIGH_ADDRESS,
+
+	/**
+	 * @BATADV_ATTR_TQ: TQ to neighbour
+	 */
+	BATADV_ATTR_TQ,
+
+	/**
+	 * @BATADV_ATTR_THROUGHPUT: Estimated throughput to Neighbour
+	 */
+	BATADV_ATTR_THROUGHPUT,
+
+	/**
+	 * @BATADV_ATTR_BANDWIDTH_UP: Reported uplink bandwidth
+	 */
+	BATADV_ATTR_BANDWIDTH_UP,
+
+	/**
+	 * @BATADV_ATTR_BANDWIDTH_DOWN: Reported downlink bandwidth
+	 */
+	BATADV_ATTR_BANDWIDTH_DOWN,
+
+	/**
+	 * @BATADV_ATTR_ROUTER: Gateway router MAC address
+	 */
+	BATADV_ATTR_ROUTER,
+
+	/**
+	 * @BATADV_ATTR_BLA_OWN: Flag indicating own originator
+	 */
+	BATADV_ATTR_BLA_OWN,
+
+	/**
+	 * @BATADV_ATTR_BLA_ADDRESS: Bridge loop avoidance claim MAC address
+	 */
+	BATADV_ATTR_BLA_ADDRESS,
+
+	/**
+	 * @BATADV_ATTR_BLA_VID: BLA VLAN ID
+	 */
+	BATADV_ATTR_BLA_VID,
+
+	/**
+	 * @BATADV_ATTR_BLA_BACKBONE: BLA gateway originator MAC address
+	 */
+	BATADV_ATTR_BLA_BACKBONE,
+
+	/**
+	 * @BATADV_ATTR_BLA_CRC: BLA CRC
+	 */
+	BATADV_ATTR_BLA_CRC,
+
+	/**
+	 * @BATADV_ATTR_DAT_CACHE_IP4ADDRESS: Client IPv4 address
+	 */
+	BATADV_ATTR_DAT_CACHE_IP4ADDRESS,
+
+	/**
+	 * @BATADV_ATTR_DAT_CACHE_HWADDRESS: Client MAC address
+	 */
+	BATADV_ATTR_DAT_CACHE_HWADDRESS,
+
+	/**
+	 * @BATADV_ATTR_DAT_CACHE_VID: VLAN ID
+	 */
+	BATADV_ATTR_DAT_CACHE_VID,
+
+	/**
+	 * @BATADV_ATTR_MCAST_FLAGS: Per originator multicast flags
+	 */
+	BATADV_ATTR_MCAST_FLAGS,
+
+	/**
+	 * @BATADV_ATTR_MCAST_FLAGS_PRIV: Private, own multicast flags
+	 */
+	BATADV_ATTR_MCAST_FLAGS_PRIV,
+
+	/**
+	 * @BATADV_ATTR_VLANID: VLAN id on top of soft interface
+	 */
+	BATADV_ATTR_VLANID,
+
+	/**
+	 * @BATADV_ATTR_AGGREGATED_OGMS_ENABLED: whether the batman protocol
+	 *  messages of the mesh interface shall be aggregated or not.
+	 */
+	BATADV_ATTR_AGGREGATED_OGMS_ENABLED,
+
+	/**
+	 * @BATADV_ATTR_AP_ISOLATION_ENABLED: whether the data traffic going
+	 *  from a wireless client to another wireless client will be silently
+	 *  dropped.
+	 */
+	BATADV_ATTR_AP_ISOLATION_ENABLED,
+
+	/**
+	 * @BATADV_ATTR_ISOLATION_MARK: the isolation mark which is used to
+	 *  classify clients as "isolated" by the Extended Isolation feature.
+	 */
+	BATADV_ATTR_ISOLATION_MARK,
+
+	/**
+	 * @BATADV_ATTR_ISOLATION_MASK: the isolation (bit)mask which is used to
+	 *  classify clients as "isolated" by the Extended Isolation feature.
+	 */
+	BATADV_ATTR_ISOLATION_MASK,
+
+	/**
+	 * @BATADV_ATTR_BONDING_ENABLED: whether the data traffic going through
+	 *  the mesh will be sent using multiple interfaces at the same time.
+	 */
+	BATADV_ATTR_BONDING_ENABLED,
+
+	/**
+	 * @BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED: whether the bridge loop
+	 *  avoidance feature is enabled. This feature detects and avoids loops
+	 *  between the mesh and devices bridged with the soft interface
+	 */
+	BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED,
+
+	/**
+	 * @BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED: whether the distributed
+	 *  arp table feature is enabled. This feature uses a distributed hash
+	 *  table to answer ARP requests without flooding the request through
+	 *  the whole mesh.
+	 */
+	BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED,
+
+	/**
+	 * @BATADV_ATTR_FRAGMENTATION_ENABLED: whether the data traffic going
+	 *  through the mesh will be fragmented or silently discarded if the
+	 *  packet size exceeds the outgoing interface MTU.
+	 */
+	BATADV_ATTR_FRAGMENTATION_ENABLED,
+
+	/**
+	 * @BATADV_ATTR_GW_BANDWIDTH_DOWN: defines the download bandwidth which
+	 *  is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set
+	 *  to 'server'.
+	 */
+	BATADV_ATTR_GW_BANDWIDTH_DOWN,
+
+	/**
+	 * @BATADV_ATTR_GW_BANDWIDTH_UP: defines the upload bandwidth which
+	 *  is propagated by this node if %BATADV_ATTR_GW_BANDWIDTH_MODE was set
+	 *  to 'server'.
+	 */
+	BATADV_ATTR_GW_BANDWIDTH_UP,
+
+	/**
+	 * @BATADV_ATTR_GW_MODE: defines the state of the gateway features.
+	 * Possible values are specified in enum batadv_gw_modes
+	 */
+	BATADV_ATTR_GW_MODE,
+
+	/**
+	 * @BATADV_ATTR_GW_SEL_CLASS: defines the selection criteria this node
+	 *  will use to choose a gateway if gw_mode was set to 'client'.
+	 */
+	BATADV_ATTR_GW_SEL_CLASS,
+
+	/**
+	 * @BATADV_ATTR_HOP_PENALTY: defines the penalty which will be applied
+	 *  to an originator message's tq-field on every hop and/or per
+	 *  hard interface
+	 */
+	BATADV_ATTR_HOP_PENALTY,
+
+	/**
+	 * @BATADV_ATTR_LOG_LEVEL: bitmask with to define which debug messages
+	 *  should be send to the debug log/trace ring buffer
+	 */
+	BATADV_ATTR_LOG_LEVEL,
+
+	/**
+	 * @BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED: whether multicast
+	 *  optimizations should be replaced by simple broadcast-like flooding
+	 *  of multicast packets. If set to non-zero then all nodes in the mesh
+	 *  are going to use classic flooding for any multicast packet with no
+	 *  optimizations.
+	 */
+	BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED,
+
+	/**
+	 * @BATADV_ATTR_NETWORK_CODING_ENABLED: whether Network Coding (using
+	 *  some magic to send fewer wifi packets but still the same content) is
+	 *  enabled or not.
+	 */
+	BATADV_ATTR_NETWORK_CODING_ENABLED,
+
+	/**
+	 * @BATADV_ATTR_ORIG_INTERVAL: defines the interval in milliseconds in
+	 *  which batman sends its protocol messages.
+	 */
+	BATADV_ATTR_ORIG_INTERVAL,
+
+	/**
+	 * @BATADV_ATTR_ELP_INTERVAL: defines the interval in milliseconds in
+	 *  which batman emits probing packets for neighbor sensing (ELP).
+	 */
+	BATADV_ATTR_ELP_INTERVAL,
+
+	/**
+	 * @BATADV_ATTR_THROUGHPUT_OVERRIDE: defines the throughput value to be
+	 *  used by B.A.T.M.A.N. V when estimating the link throughput using
+	 *  this interface. If the value is set to 0 then batman-adv will try to
+	 *  estimate the throughput by itself.
+	 */
+	BATADV_ATTR_THROUGHPUT_OVERRIDE,
+
+	/**
+	 * @BATADV_ATTR_MULTICAST_FANOUT: defines the maximum number of packet
+	 * copies that may be generated for a multicast-to-unicast conversion.
+	 * Once this limit is exceeded distribution will fall back to broadcast.
+	 */
+	BATADV_ATTR_MULTICAST_FANOUT,
+
+	/* add attributes above here, update the policy in netlink.c */
+
+	/**
+	 * @__BATADV_ATTR_AFTER_LAST: internal use
+	 */
+	__BATADV_ATTR_AFTER_LAST,
+
+	/**
+	 * @NUM_BATADV_ATTR: total number of batadv_nl_attrs available
+	 */
+	NUM_BATADV_ATTR = __BATADV_ATTR_AFTER_LAST,
+
+	/**
+	 * @BATADV_ATTR_MAX: highest attribute number currently defined
+	 */
+	BATADV_ATTR_MAX = __BATADV_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum batadv_nl_commands - supported batman-adv netlink commands
+ */
+enum batadv_nl_commands {
+	/**
+	 * @BATADV_CMD_UNSPEC: unspecified command to catch errors
+	 */
+	BATADV_CMD_UNSPEC,
+
+	/**
+	 * @BATADV_CMD_GET_MESH: Get attributes from softif/mesh
+	 */
+	BATADV_CMD_GET_MESH,
+
+	/**
+	 * @BATADV_CMD_GET_MESH_INFO: Alias for @BATADV_CMD_GET_MESH
+	 */
+	BATADV_CMD_GET_MESH_INFO = BATADV_CMD_GET_MESH,
+
+	/**
+	 * @BATADV_CMD_TP_METER: Start a tp meter session
+	 */
+	BATADV_CMD_TP_METER,
+
+	/**
+	 * @BATADV_CMD_TP_METER_CANCEL: Cancel a tp meter session
+	 */
+	BATADV_CMD_TP_METER_CANCEL,
+
+	/**
+	 * @BATADV_CMD_GET_ROUTING_ALGOS: Query the list of routing algorithms.
+	 */
+	BATADV_CMD_GET_ROUTING_ALGOS,
+
+	/**
+	 * @BATADV_CMD_GET_HARDIF: Get attributes from a hardif of the
+	 *  current softif
+	 */
+	BATADV_CMD_GET_HARDIF,
+
+	/**
+	 * @BATADV_CMD_GET_HARDIFS: Alias for @BATADV_CMD_GET_HARDIF
+	 */
+	BATADV_CMD_GET_HARDIFS = BATADV_CMD_GET_HARDIF,
+
+	/**
+	 * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations
+	 */
+	BATADV_CMD_GET_TRANSTABLE_LOCAL,
+
+	/**
+	 * @BATADV_CMD_GET_TRANSTABLE_GLOBAL: Query list of global translations
+	 */
+	BATADV_CMD_GET_TRANSTABLE_GLOBAL,
+
+	/**
+	 * @BATADV_CMD_GET_ORIGINATORS: Query list of originators
+	 */
+	BATADV_CMD_GET_ORIGINATORS,
+
+	/**
+	 * @BATADV_CMD_GET_NEIGHBORS: Query list of neighbours
+	 */
+	BATADV_CMD_GET_NEIGHBORS,
+
+	/**
+	 * @BATADV_CMD_GET_GATEWAYS: Query list of gateways
+	 */
+	BATADV_CMD_GET_GATEWAYS,
+
+	/**
+	 * @BATADV_CMD_GET_BLA_CLAIM: Query list of bridge loop avoidance claims
+	 */
+	BATADV_CMD_GET_BLA_CLAIM,
+
+	/**
+	 * @BATADV_CMD_GET_BLA_BACKBONE: Query list of bridge loop avoidance
+	 * backbones
+	 */
+	BATADV_CMD_GET_BLA_BACKBONE,
+
+	/**
+	 * @BATADV_CMD_GET_DAT_CACHE: Query list of DAT cache entries
+	 */
+	BATADV_CMD_GET_DAT_CACHE,
+
+	/**
+	 * @BATADV_CMD_GET_MCAST_FLAGS: Query list of multicast flags
+	 */
+	BATADV_CMD_GET_MCAST_FLAGS,
+
+	/**
+	 * @BATADV_CMD_SET_MESH: Set attributes for softif/mesh
+	 */
+	BATADV_CMD_SET_MESH,
+
+	/**
+	 * @BATADV_CMD_SET_HARDIF: Set attributes for hardif of the
+	 *  current softif
+	 */
+	BATADV_CMD_SET_HARDIF,
+
+	/**
+	 * @BATADV_CMD_GET_VLAN: Get attributes from a VLAN of the
+	 *  current softif
+	 */
+	BATADV_CMD_GET_VLAN,
+
+	/**
+	 * @BATADV_CMD_SET_VLAN: Set attributes for VLAN of the
+	 *  current softif
+	 */
+	BATADV_CMD_SET_VLAN,
+
+	/* add new commands above here */
+
+	/**
+	 * @__BATADV_CMD_AFTER_LAST: internal use
+	 */
+	__BATADV_CMD_AFTER_LAST,
+
+	/**
+	 * @BATADV_CMD_MAX: highest used command number
+	 */
+	BATADV_CMD_MAX = __BATADV_CMD_AFTER_LAST - 1
+};
+
+/**
+ * enum batadv_tp_meter_reason - reason of a tp meter test run stop
+ */
+enum batadv_tp_meter_reason {
+	/**
+	 * @BATADV_TP_REASON_COMPLETE: sender finished tp run
+	 */
+	BATADV_TP_REASON_COMPLETE		= 3,
+
+	/**
+	 * @BATADV_TP_REASON_CANCEL: sender was stopped during run
+	 */
+	BATADV_TP_REASON_CANCEL			= 4,
+
+	/* error status >= 128 */
+
+	/**
+	 * @BATADV_TP_REASON_DST_UNREACHABLE: receiver could not be reached or
+	 * didn't answer
+	 */
+	BATADV_TP_REASON_DST_UNREACHABLE	= 128,
+
+	/**
+	 * @BATADV_TP_REASON_RESEND_LIMIT: (unused) sender retry reached limit
+	 */
+	BATADV_TP_REASON_RESEND_LIMIT		= 129,
+
+	/**
+	 * @BATADV_TP_REASON_ALREADY_ONGOING: test to or from the same node
+	 * already ongoing
+	 */
+	BATADV_TP_REASON_ALREADY_ONGOING	= 130,
+
+	/**
+	 * @BATADV_TP_REASON_MEMORY_ERROR: test was stopped due to low memory
+	 */
+	BATADV_TP_REASON_MEMORY_ERROR		= 131,
+
+	/**
+	 * @BATADV_TP_REASON_CANT_SEND: failed to send via outgoing interface
+	 */
+	BATADV_TP_REASON_CANT_SEND		= 132,
+
+	/**
+	 * @BATADV_TP_REASON_TOO_MANY: too many ongoing sessions
+	 */
+	BATADV_TP_REASON_TOO_MANY		= 133,
+};
+
+/**
+ * enum batadv_ifla_attrs - batman-adv ifla nested attributes
+ */
+enum batadv_ifla_attrs {
+	/**
+	 * @IFLA_BATADV_UNSPEC: unspecified attribute which is not parsed by
+	 *  rtnetlink
+	 */
+	IFLA_BATADV_UNSPEC,
+
+	/**
+	 * @IFLA_BATADV_ALGO_NAME: routing algorithm (name) which should be
+	 *  used by the newly registered batadv net_device.
+	 */
+	IFLA_BATADV_ALGO_NAME,
+
+	/* add attributes above here, update the policy in soft-interface.c */
+
+	/**
+	 * @__IFLA_BATADV_MAX: internal use
+	 */
+	__IFLA_BATADV_MAX,
+};
+
+#define IFLA_BATADV_MAX (__IFLA_BATADV_MAX - 1)
+
+#endif /* _LINUX_BATMAN_ADV_H_ */
diff -pruN 5.15.0-1/include/uapi/linux/bpf.h 5.19.0-1/include/uapi/linux/bpf.h
--- 5.15.0-1/include/uapi/linux/bpf.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/bpf.h	2022-08-02 18:36:33.000000000 +0000
@@ -330,6 +330,8 @@ union bpf_iter_link_info {
  *			*ctx_out*, *data_in* and *data_out* must be NULL.
  *			*repeat* must be zero.
  *
+ *		BPF_PROG_RUN is an alias for BPF_PROG_TEST_RUN.
+ *
  *	Return
  *		Returns zero on success. On error, -1 is returned and *errno*
  *		is set appropriately.
@@ -906,6 +908,7 @@ enum bpf_map_type {
 	BPF_MAP_TYPE_RINGBUF,
 	BPF_MAP_TYPE_INODE_STORAGE,
 	BPF_MAP_TYPE_TASK_STORAGE,
+	BPF_MAP_TYPE_BLOOM_FILTER,
 };
 
 /* Note that tracing related programs such as
@@ -994,6 +997,7 @@ enum bpf_attach_type {
 	BPF_SK_REUSEPORT_SELECT,
 	BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
 	BPF_PERF_EVENT,
+	BPF_TRACE_KPROBE_MULTI,
 	__MAX_BPF_ATTACH_TYPE
 };
 
@@ -1008,6 +1012,8 @@ enum bpf_link_type {
 	BPF_LINK_TYPE_NETNS = 5,
 	BPF_LINK_TYPE_XDP = 6,
 	BPF_LINK_TYPE_PERF_EVENT = 7,
+	BPF_LINK_TYPE_KPROBE_MULTI = 8,
+	BPF_LINK_TYPE_STRUCT_OPS = 9,
 
 	MAX_BPF_LINK_TYPE,
 };
@@ -1110,6 +1116,16 @@ enum bpf_link_type {
  */
 #define BPF_F_SLEEPABLE		(1U << 4)
 
+/* If BPF_F_XDP_HAS_FRAGS is used in BPF_PROG_LOAD command, the loaded program
+ * fully support xdp frags.
+ */
+#define BPF_F_XDP_HAS_FRAGS	(1U << 5)
+
+/* link_create.kprobe_multi.flags used in LINK_CREATE command for
+ * BPF_TRACE_KPROBE_MULTI attach type to create return probe.
+ */
+#define BPF_F_KPROBE_MULTI_RETURN	(1U << 0)
+
 /* When BPF ldimm64's insn[0].src_reg != 0 then this can have
  * the following extensions:
  *
@@ -1224,6 +1240,8 @@ enum {
 
 /* If set, run the test on the cpu specified by bpf_attr.test.cpu */
 #define BPF_F_TEST_RUN_ON_CPU	(1U << 0)
+/* If set, XDP frames will be transmitted after processing */
+#define BPF_F_TEST_XDP_LIVE_FRAMES	(1U << 1)
 
 /* type for BPF_ENABLE_STATS */
 enum bpf_stats_type {
@@ -1274,6 +1292,13 @@ union bpf_attr {
 						   * struct stored as the
 						   * map value
 						   */
+		/* Any per-map-type extra fields
+		 *
+		 * BPF_MAP_TYPE_BLOOM_FILTER - the lowest 4 bits indicate the
+		 * number of hash functions (if 0, the bloom filter will default
+		 * to using 5 hash functions).
+		 */
+		__u64	map_extra;
 	};
 
 	struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
@@ -1334,8 +1359,10 @@ union bpf_attr {
 			/* or valid module BTF object fd or 0 to attach to vmlinux */
 			__u32		attach_btf_obj_fd;
 		};
-		__u32		:32;		/* pad */
+		__u32		core_relo_cnt;	/* number of bpf_core_relo */
 		__aligned_u64	fd_array;	/* array of FDs */
+		__aligned_u64	core_relos;
+		__u32		core_relo_rec_size; /* sizeof(struct bpf_core_relo) */
 	};
 
 	struct { /* anonymous struct used by BPF_OBJ_* commands */
@@ -1376,6 +1403,7 @@ union bpf_attr {
 		__aligned_u64	ctx_out;
 		__u32		flags;
 		__u32		cpu;
+		__u32		batch_size;
 	} test;
 
 	struct { /* anonymous struct used by BPF_*_GET_*_ID */
@@ -1455,6 +1483,22 @@ union bpf_attr {
 				 */
 				__u64		bpf_cookie;
 			} perf_event;
+			struct {
+				__u32		flags;
+				__u32		cnt;
+				__aligned_u64	syms;
+				__aligned_u64	addrs;
+				__aligned_u64	cookies;
+			} kprobe_multi;
+			struct {
+				/* this is overlaid with the target_btf_id above. */
+				__u32		target_btf_id;
+				/* black box user-provided value passed through
+				 * to BPF program at the execution time and
+				 * accessible through bpf_get_attach_cookie() BPF helper
+				 */
+				__u64		cookie;
+			} tracing;
 		};
 	} link_create;
 
@@ -1629,7 +1673,7 @@ union bpf_attr {
  * u32 bpf_get_smp_processor_id(void)
  * 	Description
  * 		Get the SMP (symmetric multiprocessing) processor id. Note that
- * 		all programs run with preemption disabled, which means that the
+ * 		all programs run with migration disabled, which means that the
  * 		SMP processor id is stable during all the execution of the
  * 		program.
  * 	Return
@@ -1736,7 +1780,7 @@ union bpf_attr {
  * 		if the maximum number of tail calls has been reached for this
  * 		chain of programs. This limit is defined in the kernel by the
  * 		macro **MAX_TAIL_CALL_CNT** (not accessible to user space),
- * 		which is currently set to 32.
+ *		which is currently set to 33.
  * 	Return
  * 		0 on success, or a negative error in case of failure.
  *
@@ -1765,6 +1809,8 @@ union bpf_attr {
  * 		0 on success, or a negative error in case of failure.
  *
  * u64 bpf_get_current_pid_tgid(void)
+ * 	Description
+ * 		Get the current pid and tgid.
  * 	Return
  * 		A 64-bit integer containing the current tgid and pid, and
  * 		created as such:
@@ -1772,6 +1818,8 @@ union bpf_attr {
  * 		*current_task*\ **->pid**.
  *
  * u64 bpf_get_current_uid_gid(void)
+ * 	Description
+ * 		Get the current uid and gid.
  * 	Return
  * 		A 64-bit integer containing the current GID and UID, and
  * 		created as such: *current_gid* **<< 32 \|** *current_uid*.
@@ -2246,6 +2294,8 @@ union bpf_attr {
  * 		The 32-bit hash.
  *
  * u64 bpf_get_current_task(void)
+ * 	Description
+ * 		Get the current task.
  * 	Return
  * 		A pointer to the current task struct.
  *
@@ -2276,8 +2326,8 @@ union bpf_attr {
  * 	Return
  * 		The return value depends on the result of the test, and can be:
  *
- *		* 0, if current task belongs to the cgroup2.
- *		* 1, if current task does not belong to the cgroup2.
+ *		* 1, if current task belongs to the cgroup2.
+ *		* 0, if current task does not belong to the cgroup2.
  * 		* A negative error code, if an error occurred.
  *
  * long bpf_skb_change_tail(struct sk_buff *skb, u32 len, u64 flags)
@@ -2359,6 +2409,8 @@ union bpf_attr {
  * 		indicate that the hash is outdated and to trigger a
  * 		recalculation the next time the kernel tries to access this
  * 		hash or when the **bpf_get_hash_recalc**\ () helper is called.
+ * 	Return
+ * 		void.
  *
  * long bpf_get_numa_node_id(void)
  * 	Description
@@ -2456,6 +2508,8 @@ union bpf_attr {
  * 		A 8-byte long unique number or 0 if *sk* is NULL.
  *
  * u32 bpf_get_socket_uid(struct sk_buff *skb)
+ * 	Description
+ * 		Get the owner UID of the socked associated to *skb*.
  * 	Return
  * 		The owner UID of the socket associated to *skb*. If the socket
  * 		is **NULL**, or if it is not a full socket (i.e. if it is a
@@ -2965,8 +3019,8 @@ union bpf_attr {
  *
  * 			# sysctl kernel.perf_event_max_stack=<new value>
  * 	Return
- * 		A non-negative value equal to or less than *size* on success,
- * 		or a negative error in case of failure.
+ * 		The non-negative copied *buf* length equal to or less than
+ * 		*size* on success, or a negative error in case of failure.
  *
  * long bpf_skb_load_bytes_relative(const void *skb, u32 offset, void *to, u32 len, u32 start_header)
  * 	Description
@@ -3230,6 +3284,9 @@ union bpf_attr {
  * 		The id is returned or 0 in case the id could not be retrieved.
  *
  * u64 bpf_get_current_cgroup_id(void)
+ * 	Description
+ * 		Get the current cgroup id based on the cgroup within which
+ * 		the current task is running.
  * 	Return
  * 		A 64-bit integer containing the current cgroup id based
  * 		on the cgroup within which the current task is running.
@@ -4046,7 +4103,7 @@ union bpf_attr {
  * 		arguments. The *data* are a **u64** array and corresponding format string
  * 		values are stored in the array. For strings and pointers where pointees
  * 		are accessed, only the pointer values are stored in the *data* array.
- * 		The *data_len* is the size of *data* in bytes.
+ * 		The *data_len* is the size of *data* in bytes - must be a multiple of 8.
  *
  *		Formats **%s**, **%p{i,I}{4,6}** requires to read kernel memory.
  *		Reading kernel memory may fail due to either invalid address or
@@ -4269,8 +4326,8 @@ union bpf_attr {
  *
  *			# sysctl kernel.perf_event_max_stack=<new value>
  *	Return
- *		A non-negative value equal to or less than *size* on success,
- *		or a negative error in case of failure.
+ * 		The non-negative copied *buf* length equal to or less than
+ * 		*size* on success, or a negative error in case of failure.
  *
  * long bpf_load_hdr_opt(struct bpf_sock_ops *skops, void *searchby_res, u32 len, u64 flags)
  *	Description
@@ -4751,7 +4808,8 @@ union bpf_attr {
  *		Each format specifier in **fmt** corresponds to one u64 element
  *		in the **data** array. For strings and pointers where pointees
  *		are accessed, only the pointer values are stored in the *data*
- *		array. The *data_len* is the size of *data* in bytes.
+ *		array. The *data_len* is the size of *data* in bytes - must be
+ *		a multiple of 8.
  *
  *		Formats **%s** and **%p{i,I}{4,6}** require to read kernel
  *		memory. Reading kernel memory may fail due to either invalid
@@ -4877,6 +4935,323 @@ union bpf_attr {
  *		Get the struct pt_regs associated with **task**.
  *	Return
  *		A pointer to struct pt_regs.
+ *
+ * long bpf_get_branch_snapshot(void *entries, u32 size, u64 flags)
+ *	Description
+ *		Get branch trace from hardware engines like Intel LBR. The
+ *		hardware engine is stopped shortly after the helper is
+ *		called. Therefore, the user need to filter branch entries
+ *		based on the actual use case. To capture branch trace
+ *		before the trigger point of the BPF program, the helper
+ *		should be called at the beginning of the BPF program.
+ *
+ *		The data is stored as struct perf_branch_entry into output
+ *		buffer *entries*. *size* is the size of *entries* in bytes.
+ *		*flags* is reserved for now and must be zero.
+ *
+ *	Return
+ *		On success, number of bytes written to *buf*. On error, a
+ *		negative value.
+ *
+ *		**-EINVAL** if *flags* is not zero.
+ *
+ *		**-ENOENT** if architecture does not support branch records.
+ *
+ * long bpf_trace_vprintk(const char *fmt, u32 fmt_size, const void *data, u32 data_len)
+ *	Description
+ *		Behaves like **bpf_trace_printk**\ () helper, but takes an array of u64
+ *		to format and can handle more format args as a result.
+ *
+ *		Arguments are to be used as in **bpf_seq_printf**\ () helper.
+ *	Return
+ *		The number of bytes written to the buffer, or a negative error
+ *		in case of failure.
+ *
+ * struct unix_sock *bpf_skc_to_unix_sock(void *sk)
+ * 	Description
+ *		Dynamically cast a *sk* pointer to a *unix_sock* pointer.
+ *	Return
+ *		*sk* if casting is valid, or **NULL** otherwise.
+ *
+ * long bpf_kallsyms_lookup_name(const char *name, int name_sz, int flags, u64 *res)
+ *	Description
+ *		Get the address of a kernel symbol, returned in *res*. *res* is
+ *		set to 0 if the symbol is not found.
+ *	Return
+ *		On success, zero. On error, a negative value.
+ *
+ *		**-EINVAL** if *flags* is not zero.
+ *
+ *		**-EINVAL** if string *name* is not the same size as *name_sz*.
+ *
+ *		**-ENOENT** if symbol is not found.
+ *
+ *		**-EPERM** if caller does not have permission to obtain kernel address.
+ *
+ * long bpf_find_vma(struct task_struct *task, u64 addr, void *callback_fn, void *callback_ctx, u64 flags)
+ *	Description
+ *		Find vma of *task* that contains *addr*, call *callback_fn*
+ *		function with *task*, *vma*, and *callback_ctx*.
+ *		The *callback_fn* should be a static function and
+ *		the *callback_ctx* should be a pointer to the stack.
+ *		The *flags* is used to control certain aspects of the helper.
+ *		Currently, the *flags* must be 0.
+ *
+ *		The expected callback signature is
+ *
+ *		long (\*callback_fn)(struct task_struct \*task, struct vm_area_struct \*vma, void \*callback_ctx);
+ *
+ *	Return
+ *		0 on success.
+ *		**-ENOENT** if *task->mm* is NULL, or no vma contains *addr*.
+ *		**-EBUSY** if failed to try lock mmap_lock.
+ *		**-EINVAL** for invalid **flags**.
+ *
+ * long bpf_loop(u32 nr_loops, void *callback_fn, void *callback_ctx, u64 flags)
+ *	Description
+ *		For **nr_loops**, call **callback_fn** function
+ *		with **callback_ctx** as the context parameter.
+ *		The **callback_fn** should be a static function and
+ *		the **callback_ctx** should be a pointer to the stack.
+ *		The **flags** is used to control certain aspects of the helper.
+ *		Currently, the **flags** must be 0. Currently, nr_loops is
+ *		limited to 1 << 23 (~8 million) loops.
+ *
+ *		long (\*callback_fn)(u32 index, void \*ctx);
+ *
+ *		where **index** is the current index in the loop. The index
+ *		is zero-indexed.
+ *
+ *		If **callback_fn** returns 0, the helper will continue to the next
+ *		loop. If return value is 1, the helper will skip the rest of
+ *		the loops and return. Other return values are not used now,
+ *		and will be rejected by the verifier.
+ *
+ *	Return
+ *		The number of loops performed, **-EINVAL** for invalid **flags**,
+ *		**-E2BIG** if **nr_loops** exceeds the maximum number of loops.
+ *
+ * long bpf_strncmp(const char *s1, u32 s1_sz, const char *s2)
+ *	Description
+ *		Do strncmp() between **s1** and **s2**. **s1** doesn't need
+ *		to be null-terminated and **s1_sz** is the maximum storage
+ *		size of **s1**. **s2** must be a read-only string.
+ *	Return
+ *		An integer less than, equal to, or greater than zero
+ *		if the first **s1_sz** bytes of **s1** is found to be
+ *		less than, to match, or be greater than **s2**.
+ *
+ * long bpf_get_func_arg(void *ctx, u32 n, u64 *value)
+ *	Description
+ *		Get **n**-th argument (zero based) of the traced function (for tracing programs)
+ *		returned in **value**.
+ *
+ *	Return
+ *		0 on success.
+ *		**-EINVAL** if n >= arguments count of traced function.
+ *
+ * long bpf_get_func_ret(void *ctx, u64 *value)
+ *	Description
+ *		Get return value of the traced function (for tracing programs)
+ *		in **value**.
+ *
+ *	Return
+ *		0 on success.
+ *		**-EOPNOTSUPP** for tracing programs other than BPF_TRACE_FEXIT or BPF_MODIFY_RETURN.
+ *
+ * long bpf_get_func_arg_cnt(void *ctx)
+ *	Description
+ *		Get number of arguments of the traced function (for tracing programs).
+ *
+ *	Return
+ *		The number of arguments of the traced function.
+ *
+ * int bpf_get_retval(void)
+ *	Description
+ *		Get the syscall's return value that will be returned to userspace.
+ *
+ *		This helper is currently supported by cgroup programs only.
+ *	Return
+ *		The syscall's return value.
+ *
+ * int bpf_set_retval(int retval)
+ *	Description
+ *		Set the syscall's return value that will be returned to userspace.
+ *
+ *		This helper is currently supported by cgroup programs only.
+ *	Return
+ *		0 on success, or a negative error in case of failure.
+ *
+ * u64 bpf_xdp_get_buff_len(struct xdp_buff *xdp_md)
+ *	Description
+ *		Get the total size of a given xdp buff (linear and paged area)
+ *	Return
+ *		The total size of a given xdp buffer.
+ *
+ * long bpf_xdp_load_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len)
+ *	Description
+ *		This helper is provided as an easy way to load data from a
+ *		xdp buffer. It can be used to load *len* bytes from *offset* from
+ *		the frame associated to *xdp_md*, into the buffer pointed by
+ *		*buf*.
+ *	Return
+ *		0 on success, or a negative error in case of failure.
+ *
+ * long bpf_xdp_store_bytes(struct xdp_buff *xdp_md, u32 offset, void *buf, u32 len)
+ *	Description
+ *		Store *len* bytes from buffer *buf* into the frame
+ *		associated to *xdp_md*, at *offset*.
+ *	Return
+ *		0 on success, or a negative error in case of failure.
+ *
+ * long bpf_copy_from_user_task(void *dst, u32 size, const void *user_ptr, struct task_struct *tsk, u64 flags)
+ *	Description
+ *		Read *size* bytes from user space address *user_ptr* in *tsk*'s
+ *		address space, and stores the data in *dst*. *flags* is not
+ *		used yet and is provided for future extensibility. This helper
+ *		can only be used by sleepable programs.
+ *	Return
+ *		0 on success, or a negative error in case of failure. On error
+ *		*dst* buffer is zeroed out.
+ *
+ * long bpf_skb_set_tstamp(struct sk_buff *skb, u64 tstamp, u32 tstamp_type)
+ *	Description
+ *		Change the __sk_buff->tstamp_type to *tstamp_type*
+ *		and set *tstamp* to the __sk_buff->tstamp together.
+ *
+ *		If there is no need to change the __sk_buff->tstamp_type,
+ *		the tstamp value can be directly written to __sk_buff->tstamp
+ *		instead.
+ *
+ *		BPF_SKB_TSTAMP_DELIVERY_MONO is the only tstamp that
+ *		will be kept during bpf_redirect_*().  A non zero
+ *		*tstamp* must be used with the BPF_SKB_TSTAMP_DELIVERY_MONO
+ *		*tstamp_type*.
+ *
+ *		A BPF_SKB_TSTAMP_UNSPEC *tstamp_type* can only be used
+ *		with a zero *tstamp*.
+ *
+ *		Only IPv4 and IPv6 skb->protocol are supported.
+ *
+ *		This function is most useful when it needs to set a
+ *		mono delivery time to __sk_buff->tstamp and then
+ *		bpf_redirect_*() to the egress of an iface.  For example,
+ *		changing the (rcv) timestamp in __sk_buff->tstamp at
+ *		ingress to a mono delivery time and then bpf_redirect_*()
+ *		to sch_fq@phy-dev.
+ *	Return
+ *		0 on success.
+ *		**-EINVAL** for invalid input
+ *		**-EOPNOTSUPP** for unsupported protocol
+ *
+ * long bpf_ima_file_hash(struct file *file, void *dst, u32 size)
+ *	Description
+ *		Returns a calculated IMA hash of the *file*.
+ *		If the hash is larger than *size*, then only *size*
+ *		bytes will be copied to *dst*
+ *	Return
+ *		The **hash_algo** is returned on success,
+ *		**-EOPNOTSUP** if the hash calculation failed or **-EINVAL** if
+ *		invalid arguments are passed.
+ *
+ * void *bpf_kptr_xchg(void *map_value, void *ptr)
+ *	Description
+ *		Exchange kptr at pointer *map_value* with *ptr*, and return the
+ *		old value. *ptr* can be NULL, otherwise it must be a referenced
+ *		pointer which will be released when this helper is called.
+ *	Return
+ *		The old value of kptr (which can be NULL). The returned pointer
+ *		if not NULL, is a reference which must be released using its
+ *		corresponding release function, or moved into a BPF map before
+ *		program exit.
+ *
+ * void *bpf_map_lookup_percpu_elem(struct bpf_map *map, const void *key, u32 cpu)
+ * 	Description
+ * 		Perform a lookup in *percpu map* for an entry associated to
+ * 		*key* on *cpu*.
+ * 	Return
+ * 		Map value associated to *key* on *cpu*, or **NULL** if no entry
+ * 		was found or *cpu* is invalid.
+ *
+ * struct mptcp_sock *bpf_skc_to_mptcp_sock(void *sk)
+ *	Description
+ *		Dynamically cast a *sk* pointer to a *mptcp_sock* pointer.
+ *	Return
+ *		*sk* if casting is valid, or **NULL** otherwise.
+ *
+ * long bpf_dynptr_from_mem(void *data, u32 size, u64 flags, struct bpf_dynptr *ptr)
+ *	Description
+ *		Get a dynptr to local memory *data*.
+ *
+ *		*data* must be a ptr to a map value.
+ *		The maximum *size* supported is DYNPTR_MAX_SIZE.
+ *		*flags* is currently unused.
+ *	Return
+ *		0 on success, -E2BIG if the size exceeds DYNPTR_MAX_SIZE,
+ *		-EINVAL if flags is not 0.
+ *
+ * long bpf_ringbuf_reserve_dynptr(void *ringbuf, u32 size, u64 flags, struct bpf_dynptr *ptr)
+ *	Description
+ *		Reserve *size* bytes of payload in a ring buffer *ringbuf*
+ *		through the dynptr interface. *flags* must be 0.
+ *
+ *		Please note that a corresponding bpf_ringbuf_submit_dynptr or
+ *		bpf_ringbuf_discard_dynptr must be called on *ptr*, even if the
+ *		reservation fails. This is enforced by the verifier.
+ *	Return
+ *		0 on success, or a negative error in case of failure.
+ *
+ * void bpf_ringbuf_submit_dynptr(struct bpf_dynptr *ptr, u64 flags)
+ *	Description
+ *		Submit reserved ring buffer sample, pointed to by *data*,
+ *		through the dynptr interface. This is a no-op if the dynptr is
+ *		invalid/null.
+ *
+ *		For more information on *flags*, please see
+ *		'bpf_ringbuf_submit'.
+ *	Return
+ *		Nothing. Always succeeds.
+ *
+ * void bpf_ringbuf_discard_dynptr(struct bpf_dynptr *ptr, u64 flags)
+ *	Description
+ *		Discard reserved ring buffer sample through the dynptr
+ *		interface. This is a no-op if the dynptr is invalid/null.
+ *
+ *		For more information on *flags*, please see
+ *		'bpf_ringbuf_discard'.
+ *	Return
+ *		Nothing. Always succeeds.
+ *
+ * long bpf_dynptr_read(void *dst, u32 len, struct bpf_dynptr *src, u32 offset, u64 flags)
+ *	Description
+ *		Read *len* bytes from *src* into *dst*, starting from *offset*
+ *		into *src*.
+ *		*flags* is currently unused.
+ *	Return
+ *		0 on success, -E2BIG if *offset* + *len* exceeds the length
+ *		of *src*'s data, -EINVAL if *src* is an invalid dynptr or if
+ *		*flags* is not 0.
+ *
+ * long bpf_dynptr_write(struct bpf_dynptr *dst, u32 offset, void *src, u32 len, u64 flags)
+ *	Description
+ *		Write *len* bytes from *src* into *dst*, starting from *offset*
+ *		into *dst*.
+ *		*flags* is currently unused.
+ *	Return
+ *		0 on success, -E2BIG if *offset* + *len* exceeds the length
+ *		of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst*
+ *		is a read-only dynptr or if *flags* is not 0.
+ *
+ * void *bpf_dynptr_data(struct bpf_dynptr *ptr, u32 offset, u32 len)
+ *	Description
+ *		Get a pointer to the underlying dynptr data.
+ *
+ *		*len* must be a statically known value. The returned data slice
+ *		is invalidated whenever the dynptr is invalidated.
+ *	Return
+ *		Pointer to the underlying dynptr data, NULL if the dynptr is
+ *		read-only, if the dynptr is invalid, or if the offset and length
+ *		is out of bounds.
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5055,6 +5430,34 @@ union bpf_attr {
 	FN(get_func_ip),		\
 	FN(get_attach_cookie),		\
 	FN(task_pt_regs),		\
+	FN(get_branch_snapshot),	\
+	FN(trace_vprintk),		\
+	FN(skc_to_unix_sock),		\
+	FN(kallsyms_lookup_name),	\
+	FN(find_vma),			\
+	FN(loop),			\
+	FN(strncmp),			\
+	FN(get_func_arg),		\
+	FN(get_func_ret),		\
+	FN(get_func_arg_cnt),		\
+	FN(get_retval),			\
+	FN(set_retval),			\
+	FN(xdp_get_buff_len),		\
+	FN(xdp_load_bytes),		\
+	FN(xdp_store_bytes),		\
+	FN(copy_from_user_task),	\
+	FN(skb_set_tstamp),		\
+	FN(ima_file_hash),		\
+	FN(kptr_xchg),			\
+	FN(map_lookup_percpu_elem),     \
+	FN(skc_to_mptcp_sock),		\
+	FN(dynptr_from_mem),		\
+	FN(ringbuf_reserve_dynptr),	\
+	FN(ringbuf_submit_dynptr),	\
+	FN(ringbuf_discard_dynptr),	\
+	FN(dynptr_read),		\
+	FN(dynptr_write),		\
+	FN(dynptr_data),		\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
@@ -5244,6 +5647,15 @@ union {					\
 	__u64 :64;			\
 } __attribute__((aligned(8)))
 
+enum {
+	BPF_SKB_TSTAMP_UNSPEC,
+	BPF_SKB_TSTAMP_DELIVERY_MONO,	/* tstamp has mono delivery time */
+	/* For any BPF_SKB_TSTAMP_* that the bpf prog cannot handle,
+	 * the bpf prog should handle it like BPF_SKB_TSTAMP_UNSPEC
+	 * and try to deduce it by ingress, egress or skb->sk->sk_clockid.
+	 */
+};
+
 /* user accessible mirror of in-kernel sk_buff.
  * new fields can only be added to the end of this structure
  */
@@ -5284,6 +5696,9 @@ struct __sk_buff {
 	__u32 gso_segs;
 	__bpf_md_ptr(struct bpf_sock *, sk);
 	__u32 gso_size;
+	__u8  tstamp_type;
+	__u32 :24;		/* Padding, future use. */
+	__u64 hwtstamp;
 };
 
 struct bpf_tunnel_key {
@@ -5296,6 +5711,10 @@ struct bpf_tunnel_key {
 	__u8 tunnel_ttl;
 	__u16 tunnel_ext;	/* Padding, future use. */
 	__u32 tunnel_label;
+	union {
+		__u32 local_ipv4;
+		__u32 local_ipv6[4];
+	};
 };
 
 /* user accessible mirror of in-kernel xfrm_state.
@@ -5347,7 +5766,8 @@ struct bpf_sock {
 	__u32 src_ip4;
 	__u32 src_ip6[4];
 	__u32 src_port;		/* host byte order */
-	__u32 dst_port;		/* network byte order */
+	__be16 dst_port;	/* network byte order */
+	__u16 :16;		/* zero padding */
 	__u32 dst_ip4;
 	__u32 dst_ip6[4];
 	__u32 state;
@@ -5577,6 +5997,7 @@ struct bpf_prog_info {
 	__u64 run_time_ns;
 	__u64 run_cnt;
 	__u64 recursion_misses;
+	__u32 verified_insns;
 } __attribute__((aligned(8)));
 
 struct bpf_map_info {
@@ -5594,6 +6015,8 @@ struct bpf_map_info {
 	__u32 btf_id;
 	__u32 btf_key_type_id;
 	__u32 btf_value_type_id;
+	__u32 :32;	/* alignment pad */
+	__u64 map_extra;
 } __attribute__((aligned(8)));
 
 struct bpf_btf_info {
@@ -6186,6 +6609,11 @@ struct bpf_timer {
 	__u64 :64;
 } __attribute__((aligned(8)));
 
+struct bpf_dynptr {
+	__u64 :64;
+	__u64 :64;
+} __attribute__((aligned(8)));
+
 struct bpf_sysctl {
 	__u32	write;		/* Sysctl is being read (= 0) or written (= 1).
 				 * Allows 1,2,4-byte read, but no write.
@@ -6222,10 +6650,12 @@ struct bpf_sk_lookup {
 	__u32 protocol;		/* IP protocol (IPPROTO_TCP, IPPROTO_UDP) */
 	__u32 remote_ip4;	/* Network byte order */
 	__u32 remote_ip6[4];	/* Network byte order */
-	__u32 remote_port;	/* Network byte order */
+	__be16 remote_port;	/* Network byte order */
+	__u16 :16;		/* Zero padding */
 	__u32 local_ip4;	/* Network byte order */
 	__u32 local_ip6[4];	/* Network byte order */
 	__u32 local_port;	/* Host byte order */
+	__u32 ingress_ifindex;		/* The arriving interface. Determined by inet_iif. */
 };
 
 /*
@@ -6258,4 +6688,78 @@ enum {
 	BTF_F_ZERO	=	(1ULL << 3),
 };
 
+/* bpf_core_relo_kind encodes which aspect of captured field/type/enum value
+ * has to be adjusted by relocations. It is emitted by llvm and passed to
+ * libbpf and later to the kernel.
+ */
+enum bpf_core_relo_kind {
+	BPF_CORE_FIELD_BYTE_OFFSET = 0,      /* field byte offset */
+	BPF_CORE_FIELD_BYTE_SIZE = 1,        /* field size in bytes */
+	BPF_CORE_FIELD_EXISTS = 2,           /* field existence in target kernel */
+	BPF_CORE_FIELD_SIGNED = 3,           /* field signedness (0 - unsigned, 1 - signed) */
+	BPF_CORE_FIELD_LSHIFT_U64 = 4,       /* bitfield-specific left bitshift */
+	BPF_CORE_FIELD_RSHIFT_U64 = 5,       /* bitfield-specific right bitshift */
+	BPF_CORE_TYPE_ID_LOCAL = 6,          /* type ID in local BPF object */
+	BPF_CORE_TYPE_ID_TARGET = 7,         /* type ID in target kernel */
+	BPF_CORE_TYPE_EXISTS = 8,            /* type existence in target kernel */
+	BPF_CORE_TYPE_SIZE = 9,              /* type size in bytes */
+	BPF_CORE_ENUMVAL_EXISTS = 10,        /* enum value existence in target kernel */
+	BPF_CORE_ENUMVAL_VALUE = 11,         /* enum value integer value */
+};
+
+/*
+ * "struct bpf_core_relo" is used to pass relocation data form LLVM to libbpf
+ * and from libbpf to the kernel.
+ *
+ * CO-RE relocation captures the following data:
+ * - insn_off - instruction offset (in bytes) within a BPF program that needs
+ *   its insn->imm field to be relocated with actual field info;
+ * - type_id - BTF type ID of the "root" (containing) entity of a relocatable
+ *   type or field;
+ * - access_str_off - offset into corresponding .BTF string section. String
+ *   interpretation depends on specific relocation kind:
+ *     - for field-based relocations, string encodes an accessed field using
+ *       a sequence of field and array indices, separated by colon (:). It's
+ *       conceptually very close to LLVM's getelementptr ([0]) instruction's
+ *       arguments for identifying offset to a field.
+ *     - for type-based relocations, strings is expected to be just "0";
+ *     - for enum value-based relocations, string contains an index of enum
+ *       value within its enum type;
+ * - kind - one of enum bpf_core_relo_kind;
+ *
+ * Example:
+ *   struct sample {
+ *       int a;
+ *       struct {
+ *           int b[10];
+ *       };
+ *   };
+ *
+ *   struct sample *s = ...;
+ *   int *x = &s->a;     // encoded as "0:0" (a is field #0)
+ *   int *y = &s->b[5];  // encoded as "0:1:0:5" (anon struct is field #1,
+ *                       // b is field #0 inside anon struct, accessing elem #5)
+ *   int *z = &s[10]->b; // encoded as "10:1" (ptr is used as an array)
+ *
+ * type_id for all relocs in this example will capture BTF type id of
+ * `struct sample`.
+ *
+ * Such relocation is emitted when using __builtin_preserve_access_index()
+ * Clang built-in, passing expression that captures field address, e.g.:
+ *
+ * bpf_probe_read(&dst, sizeof(dst),
+ *		  __builtin_preserve_access_index(&src->a.b.c));
+ *
+ * In this case Clang will emit field relocation recording necessary data to
+ * be able to find offset of embedded `a.b.c` field within `src` struct.
+ *
+ * [0] https://llvm.org/docs/LangRef.html#getelementptr-instruction
+ */
+struct bpf_core_relo {
+	__u32 insn_off;
+	__u32 type_id;
+	__u32 access_str_off;
+	enum bpf_core_relo_kind kind;
+};
+
 #endif /* __LINUX_BPF_H__ */
diff -pruN 5.15.0-1/include/uapi/linux/btf.h 5.19.0-1/include/uapi/linux/btf.h
--- 5.15.0-1/include/uapi/linux/btf.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/btf.h	2022-08-02 18:36:33.000000000 +0000
@@ -33,8 +33,8 @@ struct btf_type {
 	/* "info" bits arrangement
 	 * bits  0-15: vlen (e.g. # of struct's members)
 	 * bits 16-23: unused
-	 * bits 24-27: kind (e.g. int, ptr, array...etc)
-	 * bits 28-30: unused
+	 * bits 24-28: kind (e.g. int, ptr, array...etc)
+	 * bits 29-30: unused
 	 * bit     31: kind_flag, currently used by
 	 *             struct, union and fwd
 	 */
@@ -43,7 +43,7 @@ struct btf_type {
 	 * "size" tells the size of the type it is describing.
 	 *
 	 * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
-	 * FUNC, FUNC_PROTO and VAR.
+	 * FUNC, FUNC_PROTO, VAR, DECL_TAG and TYPE_TAG.
 	 * "type" is a type_id referring to another type.
 	 */
 	union {
@@ -56,25 +56,30 @@ struct btf_type {
 #define BTF_INFO_VLEN(info)	((info) & 0xffff)
 #define BTF_INFO_KFLAG(info)	((info) >> 31)
 
-#define BTF_KIND_UNKN		0	/* Unknown	*/
-#define BTF_KIND_INT		1	/* Integer	*/
-#define BTF_KIND_PTR		2	/* Pointer	*/
-#define BTF_KIND_ARRAY		3	/* Array	*/
-#define BTF_KIND_STRUCT		4	/* Struct	*/
-#define BTF_KIND_UNION		5	/* Union	*/
-#define BTF_KIND_ENUM		6	/* Enumeration	*/
-#define BTF_KIND_FWD		7	/* Forward	*/
-#define BTF_KIND_TYPEDEF	8	/* Typedef	*/
-#define BTF_KIND_VOLATILE	9	/* Volatile	*/
-#define BTF_KIND_CONST		10	/* Const	*/
-#define BTF_KIND_RESTRICT	11	/* Restrict	*/
-#define BTF_KIND_FUNC		12	/* Function	*/
-#define BTF_KIND_FUNC_PROTO	13	/* Function Proto	*/
-#define BTF_KIND_VAR		14	/* Variable	*/
-#define BTF_KIND_DATASEC	15	/* Section	*/
-#define BTF_KIND_FLOAT		16	/* Floating point	*/
-#define BTF_KIND_MAX		BTF_KIND_FLOAT
-#define NR_BTF_KINDS		(BTF_KIND_MAX + 1)
+enum {
+	BTF_KIND_UNKN		= 0,	/* Unknown	*/
+	BTF_KIND_INT		= 1,	/* Integer	*/
+	BTF_KIND_PTR		= 2,	/* Pointer	*/
+	BTF_KIND_ARRAY		= 3,	/* Array	*/
+	BTF_KIND_STRUCT		= 4,	/* Struct	*/
+	BTF_KIND_UNION		= 5,	/* Union	*/
+	BTF_KIND_ENUM		= 6,	/* Enumeration	*/
+	BTF_KIND_FWD		= 7,	/* Forward	*/
+	BTF_KIND_TYPEDEF	= 8,	/* Typedef	*/
+	BTF_KIND_VOLATILE	= 9,	/* Volatile	*/
+	BTF_KIND_CONST		= 10,	/* Const	*/
+	BTF_KIND_RESTRICT	= 11,	/* Restrict	*/
+	BTF_KIND_FUNC		= 12,	/* Function	*/
+	BTF_KIND_FUNC_PROTO	= 13,	/* Function Proto	*/
+	BTF_KIND_VAR		= 14,	/* Variable	*/
+	BTF_KIND_DATASEC	= 15,	/* Section	*/
+	BTF_KIND_FLOAT		= 16,	/* Floating point	*/
+	BTF_KIND_DECL_TAG	= 17,	/* Decl Tag */
+	BTF_KIND_TYPE_TAG	= 18,	/* Type Tag */
+
+	NR_BTF_KINDS,
+	BTF_KIND_MAX		= NR_BTF_KINDS - 1,
+};
 
 /* For some specific BTF_KIND, "struct btf_type" is immediately
  * followed by extra data.
@@ -170,4 +175,15 @@ struct btf_var_secinfo {
 	__u32	size;
 };
 
+/* BTF_KIND_DECL_TAG is followed by a single "struct btf_decl_tag" to describe
+ * additional information related to the tag applied location.
+ * If component_idx == -1, the tag is applied to a struct, union,
+ * variable or function. Otherwise, it is applied to a struct/union
+ * member or a func argument, and component_idx indicates which member
+ * or argument (0 ... vlen-1).
+ */
+struct btf_decl_tag {
+       __s32   component_idx;
+};
+
 #endif /* __LINUX_BTF_H__ */
diff -pruN 5.15.0-1/include/uapi/linux/can/netlink.h 5.19.0-1/include/uapi/linux/can/netlink.h
--- 5.15.0-1/include/uapi/linux/can/netlink.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/can/netlink.h	2022-08-02 18:36:33.000000000 +0000
@@ -101,6 +101,8 @@ struct can_ctrlmode {
 #define CAN_CTRLMODE_PRESUME_ACK	0x40	/* Ignore missing CAN ACKs */
 #define CAN_CTRLMODE_FD_NON_ISO		0x80	/* CAN FD in non-ISO mode */
 #define CAN_CTRLMODE_CC_LEN8_DLC	0x100	/* Classic CAN DLC option */
+#define CAN_CTRLMODE_TDC_AUTO		0x200	/* CAN transiver automatically calculates TDCV */
+#define CAN_CTRLMODE_TDC_MANUAL		0x400	/* TDCV is manually set up by user */
 
 /*
  * CAN device statistics
@@ -134,10 +136,48 @@ enum {
 	IFLA_CAN_BITRATE_CONST,
 	IFLA_CAN_DATA_BITRATE_CONST,
 	IFLA_CAN_BITRATE_MAX,
-	__IFLA_CAN_MAX
+	IFLA_CAN_TDC,
+	IFLA_CAN_CTRLMODE_EXT,
+
+	/* add new constants above here */
+	__IFLA_CAN_MAX,
+	IFLA_CAN_MAX = __IFLA_CAN_MAX - 1
+};
+
+/*
+ * CAN FD Transmitter Delay Compensation (TDC)
+ *
+ * Please refer to struct can_tdc_const and can_tdc in
+ * include/linux/can/bittiming.h for further details.
+ */
+enum {
+	IFLA_CAN_TDC_UNSPEC,
+	IFLA_CAN_TDC_TDCV_MIN,	/* u32 */
+	IFLA_CAN_TDC_TDCV_MAX,	/* u32 */
+	IFLA_CAN_TDC_TDCO_MIN,	/* u32 */
+	IFLA_CAN_TDC_TDCO_MAX,	/* u32 */
+	IFLA_CAN_TDC_TDCF_MIN,	/* u32 */
+	IFLA_CAN_TDC_TDCF_MAX,	/* u32 */
+	IFLA_CAN_TDC_TDCV,	/* u32 */
+	IFLA_CAN_TDC_TDCO,	/* u32 */
+	IFLA_CAN_TDC_TDCF,	/* u32 */
+
+	/* add new constants above here */
+	__IFLA_CAN_TDC,
+	IFLA_CAN_TDC_MAX = __IFLA_CAN_TDC - 1
 };
 
-#define IFLA_CAN_MAX	(__IFLA_CAN_MAX - 1)
+/*
+ * IFLA_CAN_CTRLMODE_EXT nest: controller mode extended parameters
+ */
+enum {
+	IFLA_CAN_CTRLMODE_UNSPEC,
+	IFLA_CAN_CTRLMODE_SUPPORTED,	/* u32 */
+
+	/* add new constants above here */
+	__IFLA_CAN_CTRLMODE,
+	IFLA_CAN_CTRLMODE_MAX = __IFLA_CAN_CTRLMODE - 1
+};
 
 /* u16 termination range: 1..65535 Ohms */
 #define CAN_TERMINATION_DISABLED 0
diff -pruN 5.15.0-1/include/uapi/linux/devlink.h 5.19.0-1/include/uapi/linux/devlink.h
--- 5.15.0-1/include/uapi/linux/devlink.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/devlink.h	2022-08-02 18:36:33.000000000 +0000
@@ -131,6 +131,11 @@ enum devlink_command {
 	DEVLINK_CMD_RATE_NEW,
 	DEVLINK_CMD_RATE_DEL,
 
+	DEVLINK_CMD_LINECARD_GET,		/* can dump */
+	DEVLINK_CMD_LINECARD_SET,
+	DEVLINK_CMD_LINECARD_NEW,
+	DEVLINK_CMD_LINECARD_DEL,
+
 	/* add new commands above here */
 	__DEVLINK_CMD_MAX,
 	DEVLINK_CMD_MAX = __DEVLINK_CMD_MAX - 1
@@ -338,6 +343,19 @@ enum devlink_reload_limit {
 
 #define DEVLINK_RELOAD_LIMITS_VALID_MASK (_BITUL(__DEVLINK_RELOAD_LIMIT_MAX) - 1)
 
+enum devlink_linecard_state {
+	DEVLINK_LINECARD_STATE_UNSPEC,
+	DEVLINK_LINECARD_STATE_UNPROVISIONED,
+	DEVLINK_LINECARD_STATE_UNPROVISIONING,
+	DEVLINK_LINECARD_STATE_PROVISIONING,
+	DEVLINK_LINECARD_STATE_PROVISIONING_FAILED,
+	DEVLINK_LINECARD_STATE_PROVISIONED,
+	DEVLINK_LINECARD_STATE_ACTIVE,
+
+	__DEVLINK_LINECARD_STATE_MAX,
+	DEVLINK_LINECARD_STATE_MAX = __DEVLINK_LINECARD_STATE_MAX - 1
+};
+
 enum devlink_attr {
 	/* don't change the order or add anything between, this is ABI! */
 	DEVLINK_ATTR_UNSPEC,
@@ -551,6 +569,13 @@ enum devlink_attr {
 	DEVLINK_ATTR_RATE_NODE_NAME,		/* string */
 	DEVLINK_ATTR_RATE_PARENT_NODE_NAME,	/* string */
 
+	DEVLINK_ATTR_REGION_MAX_SNAPSHOTS,	/* u32 */
+
+	DEVLINK_ATTR_LINECARD_INDEX,		/* u32 */
+	DEVLINK_ATTR_LINECARD_STATE,		/* u8 */
+	DEVLINK_ATTR_LINECARD_TYPE,		/* string */
+	DEVLINK_ATTR_LINECARD_SUPPORTED_TYPES,	/* nested */
+
 	/* add new attributes above here, update the policy in devlink.c */
 
 	__DEVLINK_ATTR_MAX,
diff -pruN 5.15.0-1/include/uapi/linux/elf-em.h 5.19.0-1/include/uapi/linux/elf-em.h
--- 5.15.0-1/include/uapi/linux/elf-em.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/elf-em.h	2022-08-02 18:36:33.000000000 +0000
@@ -51,6 +51,7 @@
 #define EM_RISCV	243	/* RISC-V */
 #define EM_BPF		247	/* Linux BPF - in-kernel virtual machine */
 #define EM_CSKY		252	/* C-SKY */
+#define EM_LOONGARCH	258	/* LoongArch */
 #define EM_FRV		0x5441	/* Fujitsu FR-V */
 
 /*
diff -pruN 5.15.0-1/include/uapi/linux/if_addr.h 5.19.0-1/include/uapi/linux/if_addr.h
--- 5.15.0-1/include/uapi/linux/if_addr.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/if_addr.h	2022-08-02 18:36:33.000000000 +0000
@@ -33,8 +33,9 @@ enum {
 	IFA_CACHEINFO,
 	IFA_MULTICAST,
 	IFA_FLAGS,
-	IFA_RT_PRIORITY,  /* u32, priority/metric for prefix route */
+	IFA_RT_PRIORITY,	/* u32, priority/metric for prefix route */
 	IFA_TARGET_NETNSID,
+	IFA_PROTO,		/* u8, address protocol */
 	__IFA_MAX,
 };
 
@@ -67,4 +68,10 @@ struct ifa_cacheinfo {
 #define IFA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
 #define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
 
+/* ifa_proto */
+#define IFAPROT_UNSPEC		0
+#define IFAPROT_KERNEL_LO	1	/* loopback */
+#define IFAPROT_KERNEL_RA	2	/* set by kernel from router announcement */
+#define IFAPROT_KERNEL_LL	3	/* link-local set by kernel */
+
 #endif
diff -pruN 5.15.0-1/include/uapi/linux/if_bridge.h 5.19.0-1/include/uapi/linux/if_bridge.h
--- 5.15.0-1/include/uapi/linux/if_bridge.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/if_bridge.h	2022-08-02 18:36:33.000000000 +0000
@@ -122,6 +122,7 @@ enum {
 	IFLA_BRIDGE_VLAN_TUNNEL_INFO,
 	IFLA_BRIDGE_MRP,
 	IFLA_BRIDGE_CFM,
+	IFLA_BRIDGE_MST,
 	__IFLA_BRIDGE_MAX,
 };
 #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
@@ -453,6 +454,21 @@ enum {
 
 #define IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX (__IFLA_BRIDGE_CFM_CC_PEER_STATUS_MAX - 1)
 
+enum {
+	IFLA_BRIDGE_MST_UNSPEC,
+	IFLA_BRIDGE_MST_ENTRY,
+	__IFLA_BRIDGE_MST_MAX,
+};
+#define IFLA_BRIDGE_MST_MAX (__IFLA_BRIDGE_MST_MAX - 1)
+
+enum {
+	IFLA_BRIDGE_MST_ENTRY_UNSPEC,
+	IFLA_BRIDGE_MST_ENTRY_MSTI,
+	IFLA_BRIDGE_MST_ENTRY_STATE,
+	__IFLA_BRIDGE_MST_ENTRY_MAX,
+};
+#define IFLA_BRIDGE_MST_ENTRY_MAX (__IFLA_BRIDGE_MST_ENTRY_MAX - 1)
+
 struct bridge_stp_xstats {
 	__u64 transition_blk;
 	__u64 transition_fwd;
@@ -564,6 +580,7 @@ enum {
 	BRIDGE_VLANDB_GOPTS_MCAST_QUERIER,
 	BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS,
 	BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE,
+	BRIDGE_VLANDB_GOPTS_MSTI,
 	__BRIDGE_VLANDB_GOPTS_MAX
 };
 #define BRIDGE_VLANDB_GOPTS_MAX (__BRIDGE_VLANDB_GOPTS_MAX - 1)
@@ -759,6 +776,7 @@ struct br_mcast_stats {
 enum br_boolopt_id {
 	BR_BOOLOPT_NO_LL_LEARN,
 	BR_BOOLOPT_MCAST_VLAN_SNOOPING,
+	BR_BOOLOPT_MST_ENABLE,
 	BR_BOOLOPT_MAX
 };
 
diff -pruN 5.15.0-1/include/uapi/linux/if_ether.h 5.19.0-1/include/uapi/linux/if_ether.h
--- 5.15.0-1/include/uapi/linux/if_ether.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/if_ether.h	2022-08-02 18:36:33.000000000 +0000
@@ -86,7 +86,10 @@
 					 * over Ethernet
 					 */
 #define ETH_P_PAE	0x888E		/* Port Access Entity (IEEE 802.1X) */
+#define ETH_P_PROFINET	0x8892		/* PROFINET			*/
+#define ETH_P_REALTEK	0x8899          /* Multiple proprietary protocols */
 #define ETH_P_AOE	0x88A2		/* ATA over Ethernet		*/
+#define ETH_P_ETHERCAT	0x88A4		/* EtherCAT			*/
 #define ETH_P_8021AD	0x88A8          /* 802.1ad Service VLAN		*/
 #define ETH_P_802_EX1	0x88B5		/* 802.1 Local Experimental 1.  */
 #define ETH_P_PREAUTH	0x88C7		/* 802.11 Preauthentication */
@@ -116,7 +119,7 @@
 #define ETH_P_IFE	0xED3E		/* ForCES inter-FE LFB type */
 #define ETH_P_AF_IUCV   0xFBFB		/* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */
 
-#define ETH_P_802_3_MIN	0x0600		/* If the value in the ethernet type is less than this value
+#define ETH_P_802_3_MIN	0x0600		/* If the value in the ethernet type is more than this value
 					 * then the frame is Ethernet II. Else it is 802.3 */
 
 /*
diff -pruN 5.15.0-1/include/uapi/linux/if_link.h 5.19.0-1/include/uapi/linux/if_link.h
--- 5.15.0-1/include/uapi/linux/if_link.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/if_link.h	2022-08-02 18:36:33.000000000 +0000
@@ -211,6 +211,9 @@ struct rtnl_link_stats {
  * @rx_nohandler: Number of packets received on the interface
  *   but dropped by the networking stack because the device is
  *   not designated to receive packets (e.g. backup link in a bond).
+ *
+ * @rx_otherhost_dropped: Number of packets dropped due to mismatch
+ *   in destination MAC address.
  */
 struct rtnl_link_stats64 {
 	__u64	rx_packets;
@@ -243,6 +246,23 @@ struct rtnl_link_stats64 {
 	__u64	rx_compressed;
 	__u64	tx_compressed;
 	__u64	rx_nohandler;
+
+	__u64	rx_otherhost_dropped;
+};
+
+/* Subset of link stats useful for in-HW collection. Meaning of the fields is as
+ * for struct rtnl_link_stats64.
+ */
+struct rtnl_hw_stats64 {
+	__u64	rx_packets;
+	__u64	tx_packets;
+	__u64	rx_bytes;
+	__u64	tx_bytes;
+	__u64	rx_errors;
+	__u64	tx_errors;
+	__u64	rx_dropped;
+	__u64	tx_dropped;
+	__u64	multicast;
 };
 
 /* The struct should be in sync with struct ifmap */
@@ -347,6 +367,9 @@ enum {
 	 */
 	IFLA_PARENT_DEV_NAME,
 	IFLA_PARENT_DEV_BUS_NAME,
+	IFLA_GRO_MAX_SIZE,
+	IFLA_TSO_MAX_SIZE,
+	IFLA_TSO_MAX_SEGS,
 
 	__IFLA_MAX
 };
@@ -534,6 +557,7 @@ enum {
 	IFLA_BRPORT_MRP_IN_OPEN,
 	IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
 	IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
+	IFLA_BRPORT_LOCKED,
 	__IFLA_BRPORT_MAX
 };
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
@@ -709,7 +733,55 @@ enum ipvlan_mode {
 #define IPVLAN_F_PRIVATE	0x01
 #define IPVLAN_F_VEPA		0x02
 
+/* Tunnel RTM header */
+struct tunnel_msg {
+	__u8 family;
+	__u8 flags;
+	__u16 reserved2;
+	__u32 ifindex;
+};
+
 /* VXLAN section */
+
+/* include statistics in the dump */
+#define TUNNEL_MSG_FLAG_STATS	0x01
+
+#define TUNNEL_MSG_VALID_USER_FLAGS TUNNEL_MSG_FLAG_STATS
+
+/* Embedded inside VXLAN_VNIFILTER_ENTRY_STATS */
+enum {
+	VNIFILTER_ENTRY_STATS_UNSPEC,
+	VNIFILTER_ENTRY_STATS_RX_BYTES,
+	VNIFILTER_ENTRY_STATS_RX_PKTS,
+	VNIFILTER_ENTRY_STATS_RX_DROPS,
+	VNIFILTER_ENTRY_STATS_RX_ERRORS,
+	VNIFILTER_ENTRY_STATS_TX_BYTES,
+	VNIFILTER_ENTRY_STATS_TX_PKTS,
+	VNIFILTER_ENTRY_STATS_TX_DROPS,
+	VNIFILTER_ENTRY_STATS_TX_ERRORS,
+	VNIFILTER_ENTRY_STATS_PAD,
+	__VNIFILTER_ENTRY_STATS_MAX
+};
+#define VNIFILTER_ENTRY_STATS_MAX (__VNIFILTER_ENTRY_STATS_MAX - 1)
+
+enum {
+	VXLAN_VNIFILTER_ENTRY_UNSPEC,
+	VXLAN_VNIFILTER_ENTRY_START,
+	VXLAN_VNIFILTER_ENTRY_END,
+	VXLAN_VNIFILTER_ENTRY_GROUP,
+	VXLAN_VNIFILTER_ENTRY_GROUP6,
+	VXLAN_VNIFILTER_ENTRY_STATS,
+	__VXLAN_VNIFILTER_ENTRY_MAX
+};
+#define VXLAN_VNIFILTER_ENTRY_MAX	(__VXLAN_VNIFILTER_ENTRY_MAX - 1)
+
+enum {
+	VXLAN_VNIFILTER_UNSPEC,
+	VXLAN_VNIFILTER_ENTRY,
+	__VXLAN_VNIFILTER_MAX
+};
+#define VXLAN_VNIFILTER_MAX	(__VXLAN_VNIFILTER_MAX - 1)
+
 enum {
 	IFLA_VXLAN_UNSPEC,
 	IFLA_VXLAN_ID,
@@ -741,6 +813,7 @@ enum {
 	IFLA_VXLAN_GPE,
 	IFLA_VXLAN_TTL_INHERIT,
 	IFLA_VXLAN_DF,
+	IFLA_VXLAN_VNIFILTER, /* only applicable with COLLECT_METADATA mode */
 	__IFLA_VXLAN_MAX
 };
 #define IFLA_VXLAN_MAX	(__IFLA_VXLAN_MAX - 1)
@@ -774,6 +847,7 @@ enum {
 	IFLA_GENEVE_LABEL,
 	IFLA_GENEVE_TTL_INHERIT,
 	IFLA_GENEVE_DF,
+	IFLA_GENEVE_INNER_PROTO_INHERIT,
 	__IFLA_GENEVE_MAX
 };
 #define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 1)
@@ -819,6 +893,8 @@ enum {
 	IFLA_GTP_FD1,
 	IFLA_GTP_PDP_HASHSIZE,
 	IFLA_GTP_ROLE,
+	IFLA_GTP_CREATE_SOCKETS,
+	IFLA_GTP_RESTART_COUNT,
 	__IFLA_GTP_MAX,
 };
 #define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
@@ -856,6 +932,8 @@ enum {
 	IFLA_BOND_TLB_DYNAMIC_LB,
 	IFLA_BOND_PEER_NOTIF_DELAY,
 	IFLA_BOND_AD_LACP_ACTIVE,
+	IFLA_BOND_MISSED_MAX,
+	IFLA_BOND_NS_IP6_TARGET,
 	__IFLA_BOND_MAX,
 };
 
@@ -1152,6 +1230,17 @@ enum {
 
 #define IFLA_STATS_FILTER_BIT(ATTR)	(1 << (ATTR - 1))
 
+enum {
+	IFLA_STATS_GETSET_UNSPEC,
+	IFLA_STATS_GET_FILTERS, /* Nest of IFLA_STATS_LINK_xxx, each a u32 with
+				 * a filter mask for the corresponding group.
+				 */
+	IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS, /* 0 or 1 as u8 */
+	__IFLA_STATS_GETSET_MAX,
+};
+
+#define IFLA_STATS_GETSET_MAX (__IFLA_STATS_GETSET_MAX - 1)
+
 /* These are embedded into IFLA_STATS_LINK_XSTATS:
  * [IFLA_STATS_LINK_XSTATS]
  * -> [LINK_XSTATS_TYPE_xxx]
@@ -1169,10 +1258,21 @@ enum {
 enum {
 	IFLA_OFFLOAD_XSTATS_UNSPEC,
 	IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */
+	IFLA_OFFLOAD_XSTATS_HW_S_INFO,	/* HW stats info. A nest */
+	IFLA_OFFLOAD_XSTATS_L3_STATS,	/* struct rtnl_hw_stats64 */
 	__IFLA_OFFLOAD_XSTATS_MAX
 };
 #define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1)
 
+enum {
+	IFLA_OFFLOAD_XSTATS_HW_S_INFO_UNSPEC,
+	IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST,		/* u8 */
+	IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED,		/* u8 */
+	__IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX,
+};
+#define IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX \
+	(__IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX - 1)
+
 /* XDP section */
 
 #define XDP_FLAGS_UPDATE_IF_NOEXIST	(1U << 0)
diff -pruN 5.15.0-1/include/uapi/linux/if_tunnel.h 5.19.0-1/include/uapi/linux/if_tunnel.h
--- 5.15.0-1/include/uapi/linux/if_tunnel.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/if_tunnel.h	2022-08-02 18:36:33.000000000 +0000
@@ -176,8 +176,10 @@ enum {
 #define TUNNEL_VXLAN_OPT	__cpu_to_be16(0x1000)
 #define TUNNEL_NOCACHE		__cpu_to_be16(0x2000)
 #define TUNNEL_ERSPAN_OPT	__cpu_to_be16(0x4000)
+#define TUNNEL_GTP_OPT		__cpu_to_be16(0x8000)
 
 #define TUNNEL_OPTIONS_PRESENT \
-		(TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT)
+		(TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT | \
+		TUNNEL_GTP_OPT)
 
 #endif /* _IF_TUNNEL_H_ */
diff -pruN 5.15.0-1/include/uapi/linux/ioam6_iptunnel.h 5.19.0-1/include/uapi/linux/ioam6_iptunnel.h
--- 5.15.0-1/include/uapi/linux/ioam6_iptunnel.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/ioam6_iptunnel.h	2022-08-02 18:36:33.000000000 +0000
@@ -9,9 +9,47 @@
 #ifndef _LINUX_IOAM6_IPTUNNEL_H
 #define _LINUX_IOAM6_IPTUNNEL_H
 
+/* Encap modes:
+ *  - inline: direct insertion
+ *  - encap: ip6ip6 encapsulation
+ *  - auto: __inline__ for local packets, encap for in-transit packets
+ */
+enum {
+	__IOAM6_IPTUNNEL_MODE_MIN,
+
+	IOAM6_IPTUNNEL_MODE_INLINE,
+	IOAM6_IPTUNNEL_MODE_ENCAP,
+	IOAM6_IPTUNNEL_MODE_AUTO,
+
+	__IOAM6_IPTUNNEL_MODE_MAX,
+};
+
+#define IOAM6_IPTUNNEL_MODE_MIN (__IOAM6_IPTUNNEL_MODE_MIN + 1)
+#define IOAM6_IPTUNNEL_MODE_MAX (__IOAM6_IPTUNNEL_MODE_MAX - 1)
+
 enum {
 	IOAM6_IPTUNNEL_UNSPEC,
+
+	/* Encap mode */
+	IOAM6_IPTUNNEL_MODE,		/* u8 */
+
+	/* Tunnel dst address.
+	 * For encap,auto modes.
+	 */
+	IOAM6_IPTUNNEL_DST,		/* struct in6_addr */
+
+	/* IOAM Trace Header */
 	IOAM6_IPTUNNEL_TRACE,		/* struct ioam6_trace_hdr */
+
+	/* Insertion frequency:
+	 * "k over n" packets (0 < k <= n)
+	 * [0.0001% ... 100%]
+	 */
+#define IOAM6_IPTUNNEL_FREQ_MIN 1
+#define IOAM6_IPTUNNEL_FREQ_MAX 1000000
+	IOAM6_IPTUNNEL_FREQ_K,		/* u32 */
+	IOAM6_IPTUNNEL_FREQ_N,		/* u32 */
+
 	__IOAM6_IPTUNNEL_MAX,
 };
 
diff -pruN 5.15.0-1/include/uapi/linux/ip.h 5.19.0-1/include/uapi/linux/ip.h
--- 5.15.0-1/include/uapi/linux/ip.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/ip.h	2022-08-02 18:36:33.000000000 +0000
@@ -169,6 +169,7 @@ enum
 	IPV4_DEVCONF_DROP_UNICAST_IN_L2_MULTICAST,
 	IPV4_DEVCONF_DROP_GRATUITOUS_ARP,
 	IPV4_DEVCONF_BC_FORWARDING,
+	IPV4_DEVCONF_ARP_EVICT_NOCARRIER,
 	__IPV4_DEVCONF_MAX
 };
 
diff -pruN 5.15.0-1/include/uapi/linux/magic.h 5.19.0-1/include/uapi/linux/magic.h
--- 5.15.0-1/include/uapi/linux/magic.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/magic.h	2022-08-02 18:36:33.000000000 +0000
@@ -6,6 +6,7 @@
 #define AFFS_SUPER_MAGIC	0xadff
 #define AFS_SUPER_MAGIC                0x5346414F
 #define AUTOFS_SUPER_MAGIC	0x0187
+#define CEPH_SUPER_MAGIC	0x00c36400
 #define CODA_SUPER_MAGIC	0x73757245
 #define CRAMFS_MAGIC		0x28cd3d45	/* some random number */
 #define CRAMFS_MAGIC_WEND	0x453dcd28	/* magic number with the wrong endianess */
@@ -35,6 +36,7 @@
 #define EFIVARFS_MAGIC		0xde5e81e4
 #define HOSTFS_SUPER_MAGIC	0x00c0ffee
 #define OVERLAYFS_SUPER_MAGIC	0x794c7630
+#define FUSE_SUPER_MAGIC	0x65735546
 
 #define MINIX_SUPER_MAGIC	0x137F		/* minix v1 fs, 14 char names */
 #define MINIX_SUPER_MAGIC2	0x138F		/* minix v1 fs, 30 char names */
@@ -43,6 +45,7 @@
 #define MINIX3_SUPER_MAGIC	0x4d5a		/* minix v3 fs, 60 char names */
 
 #define MSDOS_SUPER_MAGIC	0x4d44		/* MD */
+#define EXFAT_SUPER_MAGIC	0x2011BAB0
 #define NCP_SUPER_MAGIC		0x564c		/* Guess, what 0x564c is :-) */
 #define NFS_SUPER_MAGIC		0x6969
 #define OCFS2_SUPER_MAGIC	0x7461636f
@@ -51,6 +54,7 @@
 #define QNX6_SUPER_MAGIC	0x68191122	/* qnx6 fs detection */
 #define AFS_FS_MAGIC		0x6B414653
 
+
 #define REISERFS_SUPER_MAGIC	0x52654973	/* used by gcc */
 					/* used by file system utilities that
 	                                   look at the superblock, etc.  */
@@ -59,6 +63,9 @@
 #define REISER2FS_JR_SUPER_MAGIC_STRING	"ReIsEr3Fs"
 
 #define SMB_SUPER_MAGIC		0x517B
+#define CIFS_SUPER_MAGIC	0xFF534D42      /* the first four bytes of SMB PDUs */
+#define SMB2_SUPER_MAGIC	0xFE534D42
+
 #define CGROUP_SUPER_MAGIC	0x27e0eb
 #define CGROUP2_SUPER_MAGIC	0x63677270
 
diff -pruN 5.15.0-1/include/uapi/linux/mptcp.h 5.19.0-1/include/uapi/linux/mptcp.h
--- 5.15.0-1/include/uapi/linux/mptcp.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/mptcp.h	2022-08-02 18:36:33.000000000 +0000
@@ -2,8 +2,14 @@
 #ifndef _MPTCP_H
 #define _MPTCP_H
 
+#include <netinet/in.h>		/* for sockaddr_in and sockaddr_in6	*/
+#include <sys/socket.h>		/* for struct sockaddr			*/
+
 #include <linux/const.h>
 #include <linux/types.h>
+#include <linux/in.h>		/* for sockaddr_in			*/
+#include <linux/in6.h>		/* for sockaddr_in6			*/
+#include <linux/socket.h>	/* for sockaddr_storage and sa_family	*/
 
 #define MPTCP_SUBFLOW_FLAG_MCAP_REM		_BITUL(0)
 #define MPTCP_SUBFLOW_FLAG_MCAP_LOC		_BITUL(1)
@@ -48,6 +54,9 @@ enum {
 	MPTCP_PM_ATTR_ADDR,				/* nested address */
 	MPTCP_PM_ATTR_RCV_ADD_ADDRS,			/* u32 */
 	MPTCP_PM_ATTR_SUBFLOWS,				/* u32 */
+	MPTCP_PM_ATTR_TOKEN,				/* u32 */
+	MPTCP_PM_ATTR_LOC_ID,				/* u8 */
+	MPTCP_PM_ATTR_ADDR_REMOTE,			/* nested address */
 
 	__MPTCP_PM_ATTR_MAX
 };
@@ -74,6 +83,7 @@ enum {
 #define MPTCP_PM_ADDR_FLAG_SUBFLOW			(1 << 1)
 #define MPTCP_PM_ADDR_FLAG_BACKUP			(1 << 2)
 #define MPTCP_PM_ADDR_FLAG_FULLMESH			(1 << 3)
+#define MPTCP_PM_ADDR_FLAG_IMPLICIT			(1 << 4)
 
 enum {
 	MPTCP_PM_CMD_UNSPEC,
@@ -85,6 +95,10 @@ enum {
 	MPTCP_PM_CMD_SET_LIMITS,
 	MPTCP_PM_CMD_GET_LIMITS,
 	MPTCP_PM_CMD_SET_FLAGS,
+	MPTCP_PM_CMD_ANNOUNCE,
+	MPTCP_PM_CMD_REMOVE,
+	MPTCP_PM_CMD_SUBFLOW_CREATE,
+	MPTCP_PM_CMD_SUBFLOW_DESTROY,
 
 	__MPTCP_PM_CMD_AFTER_LAST
 };
@@ -129,19 +143,21 @@ struct mptcp_info {
  * MPTCP_EVENT_REMOVED: token, rem_id
  * An address has been lost by the peer.
  *
- * MPTCP_EVENT_SUB_ESTABLISHED: token, family, saddr4 | saddr6,
- *                              daddr4 | daddr6, sport, dport, backup,
- *                              if_idx [, error]
+ * MPTCP_EVENT_SUB_ESTABLISHED: token, family, loc_id, rem_id,
+ *                              saddr4 | saddr6, daddr4 | daddr6, sport,
+ *                              dport, backup, if_idx [, error]
  * A new subflow has been established. 'error' should not be set.
  *
- * MPTCP_EVENT_SUB_CLOSED: token, family, saddr4 | saddr6, daddr4 | daddr6,
- *                         sport, dport, backup, if_idx [, error]
+ * MPTCP_EVENT_SUB_CLOSED: token, family, loc_id, rem_id, saddr4 | saddr6,
+ *                         daddr4 | daddr6, sport, dport, backup, if_idx
+ *                         [, error]
  * A subflow has been closed. An error (copy of sk_err) could be set if an
  * error has been detected for this subflow.
  *
- * MPTCP_EVENT_SUB_PRIORITY: token, family, saddr4 | saddr6, daddr4 | daddr6,
- *                           sport, dport, backup, if_idx [, error]
- *       The priority of a subflow has changed. 'error' should not be set.
+ * MPTCP_EVENT_SUB_PRIORITY: token, family, loc_id, rem_id, saddr4 | saddr6,
+ *                           daddr4 | daddr6, sport, dport, backup, if_idx
+ *                           [, error]
+ * The priority of a subflow has changed. 'error' should not be set.
  */
 enum mptcp_event_type {
 	MPTCP_EVENT_UNSPEC = 0,
@@ -178,6 +194,7 @@ enum mptcp_event_attr {
 	MPTCP_ATTR_IF_IDX,	/* s32 */
 	MPTCP_ATTR_RESET_REASON,/* u32 */
 	MPTCP_ATTR_RESET_FLAGS, /* u32 */
+	MPTCP_ATTR_SERVER_SIDE,	/* u8 */
 
 	__MPTCP_ATTR_AFTER_LAST
 };
@@ -193,4 +210,32 @@ enum mptcp_event_attr {
 #define MPTCP_RST_EBADPERF	5
 #define MPTCP_RST_EMIDDLEBOX	6
 
+struct mptcp_subflow_data {
+	__u32		size_subflow_data;		/* size of this structure in userspace */
+	__u32		num_subflows;			/* must be 0, set by kernel */
+	__u32		size_kernel;			/* must be 0, set by kernel */
+	__u32		size_user;			/* size of one element in data[] */
+} __attribute__((aligned(8)));
+
+struct mptcp_subflow_addrs {
+	union {
+		__kernel_sa_family_t sa_family;
+		struct sockaddr sa_local;
+		struct sockaddr_in sin_local;
+		struct sockaddr_in6 sin6_local;
+		struct __kernel_sockaddr_storage ss_local;
+	};
+	union {
+		struct sockaddr sa_remote;
+		struct sockaddr_in sin_remote;
+		struct sockaddr_in6 sin6_remote;
+		struct __kernel_sockaddr_storage ss_remote;
+	};
+};
+
+/* MPTCP socket options */
+#define MPTCP_INFO		1
+#define MPTCP_TCPINFO		2
+#define MPTCP_SUBFLOW_ADDRS	3
+
 #endif /* _MPTCP_H */
diff -pruN 5.15.0-1/include/uapi/linux/neighbour.h 5.19.0-1/include/uapi/linux/neighbour.h
--- 5.15.0-1/include/uapi/linux/neighbour.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/neighbour.h	2022-08-02 18:36:33.000000000 +0000
@@ -31,6 +31,9 @@ enum {
 	NDA_PROTOCOL,  /* Originator of entry */
 	NDA_NH_ID,
 	NDA_FDB_EXT_ATTRS,
+	NDA_FLAGS_EXT,
+	NDA_NDM_STATE_MASK,
+	NDA_NDM_FLAGS_MASK,
 	__NDA_MAX
 };
 
@@ -40,14 +43,16 @@ enum {
  *	Neighbor Cache Entry Flags
  */
 
-#define NTF_USE		0x01
-#define NTF_SELF	0x02
-#define NTF_MASTER	0x04
-#define NTF_PROXY	0x08	/* == ATF_PUBL */
-#define NTF_EXT_LEARNED	0x10
-#define NTF_OFFLOADED   0x20
-#define NTF_STICKY	0x40
-#define NTF_ROUTER	0x80
+#define NTF_USE		(1 << 0)
+#define NTF_SELF	(1 << 1)
+#define NTF_MASTER	(1 << 2)
+#define NTF_PROXY	(1 << 3)	/* == ATF_PUBL */
+#define NTF_EXT_LEARNED	(1 << 4)
+#define NTF_OFFLOADED   (1 << 5)
+#define NTF_STICKY	(1 << 6)
+#define NTF_ROUTER	(1 << 7)
+/* Extended flags under NDA_FLAGS_EXT: */
+#define NTF_EXT_MANAGED	(1 << 0)
 
 /*
  *	Neighbor Cache Entry States.
@@ -65,12 +70,22 @@ enum {
 #define NUD_PERMANENT	0x80
 #define NUD_NONE	0x00
 
-/* NUD_NOARP & NUD_PERMANENT are pseudostates, they never change
- * and make no address resolution or NUD.
- * NUD_PERMANENT also cannot be deleted by garbage collectors.
+/* NUD_NOARP & NUD_PERMANENT are pseudostates, they never change and make no
+ * address resolution or NUD.
+ *
+ * NUD_PERMANENT also cannot be deleted by garbage collectors. This holds true
+ * for dynamic entries with NTF_EXT_LEARNED flag as well. However, upon carrier
+ * down event, NUD_PERMANENT entries are not flushed whereas NTF_EXT_LEARNED
+ * flagged entries explicitly are (which is also consistent with the routing
+ * subsystem).
+ *
  * When NTF_EXT_LEARNED is set for a bridge fdb entry the different cache entry
  * states don't make sense and thus are ignored. Such entries don't age and
  * can roam.
+ *
+ * NTF_EXT_MANAGED flagged neigbor entries are managed by the kernel on behalf
+ * of a user space control plane, and automatically refreshed so that (if
+ * possible) they remain in NUD_REACHABLE state.
  */
 
 struct nda_cacheinfo {
diff -pruN 5.15.0-1/include/uapi/linux/netfilter.h 5.19.0-1/include/uapi/linux/netfilter.h
--- 5.15.0-1/include/uapi/linux/netfilter.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/netfilter.h	2022-08-02 18:36:33.000000000 +0000
@@ -49,6 +49,7 @@ enum nf_inet_hooks {
 
 enum nf_dev_hooks {
 	NF_NETDEV_INGRESS,
+	NF_NETDEV_EGRESS,
 	NF_NETDEV_NUMHOOKS
 };
 
diff -pruN 5.15.0-1/include/uapi/linux/netlink.h 5.19.0-1/include/uapi/linux/netlink.h
--- 5.15.0-1/include/uapi/linux/netlink.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/netlink.h	2022-08-02 18:36:33.000000000 +0000
@@ -72,6 +72,7 @@ struct nlmsghdr {
 
 /* Modifiers to DELETE request */
 #define NLM_F_NONREC	0x100	/* Do not delete recursively	*/
+#define NLM_F_BULK	0x200	/* Delete multiple objects	*/
 
 /* Flags for ACK message */
 #define NLM_F_CAPPED	0x100	/* request was capped */
diff -pruN 5.15.0-1/include/uapi/linux/pfkeyv2.h 5.19.0-1/include/uapi/linux/pfkeyv2.h
--- 5.15.0-1/include/uapi/linux/pfkeyv2.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/pfkeyv2.h	2022-08-02 18:36:33.000000000 +0000
@@ -309,6 +309,7 @@ struct sadb_x_filter {
 #define SADB_X_AALG_SHA2_512HMAC	7
 #define SADB_X_AALG_RIPEMD160HMAC	8
 #define SADB_X_AALG_AES_XCBC_MAC	9
+#define SADB_X_AALG_SM3_256HMAC		10
 #define SADB_X_AALG_NULL		251	/* kame */
 #define SADB_AALG_MAX			251
 
@@ -329,6 +330,7 @@ struct sadb_x_filter {
 #define SADB_X_EALG_AES_GCM_ICV16	20
 #define SADB_X_EALG_CAMELLIACBC		22
 #define SADB_X_EALG_NULL_AES_GMAC	23
+#define SADB_X_EALG_SM4CBC		24
 #define SADB_EALG_MAX                   253 /* last EALG */
 /* private allocations should use 249-255 (RFC2407) */
 #define SADB_X_EALG_SERPENTCBC  252     /* draft-ietf-ipsec-ciph-aes-cbc-00 */
diff -pruN 5.15.0-1/include/uapi/linux/pkt_cls.h 5.19.0-1/include/uapi/linux/pkt_cls.h
--- 5.15.0-1/include/uapi/linux/pkt_cls.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/pkt_cls.h	2022-08-02 18:36:33.000000000 +0000
@@ -19,13 +19,16 @@ enum {
 	TCA_ACT_FLAGS,
 	TCA_ACT_HW_STATS,
 	TCA_ACT_USED_HW_STATS,
+	TCA_ACT_IN_HW_COUNT,
 	__TCA_ACT_MAX
 };
 
 /* See other TCA_ACT_FLAGS_ * flags in include/net/act_api.h. */
-#define TCA_ACT_FLAGS_NO_PERCPU_STATS 1 /* Don't use percpu allocator for
-					 * actions stats.
-					 */
+#define TCA_ACT_FLAGS_NO_PERCPU_STATS (1 << 0) /* Don't use percpu allocator for
+						* actions stats.
+						*/
+#define TCA_ACT_FLAGS_SKIP_HW	(1 << 1) /* don't offload action to HW */
+#define TCA_ACT_FLAGS_SKIP_SW	(1 << 2) /* don't use action in SW */
 
 /* tca HW stats type
  * When user does not pass the attribute, he does not care.
@@ -584,6 +587,8 @@ enum {
 	TCA_FLOWER_KEY_HASH,		/* u32 */
 	TCA_FLOWER_KEY_HASH_MASK,	/* u32 */
 
+	TCA_FLOWER_KEY_NUM_OF_VLANS,    /* u8 */
+
 	__TCA_FLOWER_MAX,
 };
 
@@ -613,6 +618,10 @@ enum {
 					 * TCA_FLOWER_KEY_ENC_OPT_ERSPAN_
 					 * attributes
 					 */
+	TCA_FLOWER_KEY_ENC_OPTS_GTP,	/* Nested
+					 * TCA_FLOWER_KEY_ENC_OPT_GTP_
+					 * attributes
+					 */
 	__TCA_FLOWER_KEY_ENC_OPTS_MAX,
 };
 
@@ -652,6 +661,17 @@ enum {
 		(__TCA_FLOWER_KEY_ENC_OPT_ERSPAN_MAX - 1)
 
 enum {
+	TCA_FLOWER_KEY_ENC_OPT_GTP_UNSPEC,
+	TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE,		/* u8 */
+	TCA_FLOWER_KEY_ENC_OPT_GTP_QFI,			/* u8 */
+
+	__TCA_FLOWER_KEY_ENC_OPT_GTP_MAX,
+};
+
+#define TCA_FLOWER_KEY_ENC_OPT_GTP_MAX \
+		(__TCA_FLOWER_KEY_ENC_OPT_GTP_MAX - 1)
+
+enum {
 	TCA_FLOWER_KEY_MPLS_OPTS_UNSPEC,
 	TCA_FLOWER_KEY_MPLS_OPTS_LSE,
 	__TCA_FLOWER_KEY_MPLS_OPTS_MAX,
diff -pruN 5.15.0-1/include/uapi/linux/pkt_sched.h 5.19.0-1/include/uapi/linux/pkt_sched.h
--- 5.15.0-1/include/uapi/linux/pkt_sched.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/pkt_sched.h	2022-08-02 18:36:33.000000000 +0000
@@ -840,6 +840,8 @@ enum {
 	TCA_FQ_CODEL_CE_THRESHOLD,
 	TCA_FQ_CODEL_DROP_BATCH_SIZE,
 	TCA_FQ_CODEL_MEMORY_LIMIT,
+	TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR,
+	TCA_FQ_CODEL_CE_THRESHOLD_MASK,
 	__TCA_FQ_CODEL_MAX
 };
 
diff -pruN 5.15.0-1/include/uapi/linux/rose.h 5.19.0-1/include/uapi/linux/rose.h
--- 5.15.0-1/include/uapi/linux/rose.h	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/rose.h	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * These are the public elements of the Linux kernel Rose implementation.
+ * For kernel AX.25 see the file ax25.h. This file requires ax25.h for the
+ * definition of the ax25_address structure.
+ */
+
+#ifndef	ROSE_KERNEL_H
+#define	ROSE_KERNEL_H
+
+#include <linux/socket.h>
+#include <linux/ax25.h>
+
+#define ROSE_MTU	251
+
+#define ROSE_MAX_DIGIS 6
+
+#define	ROSE_DEFER	1
+#define ROSE_T1		2
+#define	ROSE_T2		3
+#define	ROSE_T3		4
+#define	ROSE_IDLE	5
+#define	ROSE_QBITINCL	6
+#define	ROSE_HOLDBACK	7
+
+#define	SIOCRSGCAUSE		(SIOCPROTOPRIVATE+0)
+#define	SIOCRSSCAUSE		(SIOCPROTOPRIVATE+1)
+#define	SIOCRSL2CALL		(SIOCPROTOPRIVATE+2)
+#define	SIOCRSSL2CALL		(SIOCPROTOPRIVATE+2)
+#define	SIOCRSACCEPT		(SIOCPROTOPRIVATE+3)
+#define	SIOCRSCLRRT		(SIOCPROTOPRIVATE+4)
+#define	SIOCRSGL2CALL		(SIOCPROTOPRIVATE+5)
+#define	SIOCRSGFACILITIES	(SIOCPROTOPRIVATE+6)
+
+#define	ROSE_DTE_ORIGINATED	0x00
+#define	ROSE_NUMBER_BUSY	0x01
+#define	ROSE_INVALID_FACILITY	0x03
+#define	ROSE_NETWORK_CONGESTION	0x05
+#define	ROSE_OUT_OF_ORDER	0x09
+#define	ROSE_ACCESS_BARRED	0x0B
+#define	ROSE_NOT_OBTAINABLE	0x0D
+#define	ROSE_REMOTE_PROCEDURE	0x11
+#define	ROSE_LOCAL_PROCEDURE	0x13
+#define	ROSE_SHIP_ABSENT	0x39
+
+typedef struct {
+	char		rose_addr[5];
+} rose_address;
+
+struct sockaddr_rose {
+	__kernel_sa_family_t srose_family;
+	rose_address	srose_addr;
+	ax25_address	srose_call;
+	int		srose_ndigis;
+	ax25_address	srose_digi;
+};
+
+struct full_sockaddr_rose {
+	__kernel_sa_family_t srose_family;
+	rose_address	srose_addr;
+	ax25_address	srose_call;
+	unsigned int	srose_ndigis;
+	ax25_address	srose_digis[ROSE_MAX_DIGIS];
+};
+
+struct rose_route_struct {
+	rose_address	address;
+	unsigned short	mask;
+	ax25_address	neighbour;
+	char		device[16];
+	unsigned char	ndigis;
+	ax25_address	digipeaters[AX25_MAX_DIGIS];
+};
+
+struct rose_cause_struct {
+	unsigned char	cause;
+	unsigned char	diagnostic;
+};
+
+struct rose_facilities_struct {
+	rose_address	source_addr,   dest_addr;
+	ax25_address	source_call,   dest_call;
+	unsigned char	source_ndigis, dest_ndigis;
+	ax25_address	source_digis[ROSE_MAX_DIGIS];
+	ax25_address	dest_digis[ROSE_MAX_DIGIS];
+	unsigned int	rand;
+	rose_address	fail_addr;
+	ax25_address	fail_call;
+};
+
+#endif
diff -pruN 5.15.0-1/include/uapi/linux/rtnetlink.h 5.19.0-1/include/uapi/linux/rtnetlink.h
--- 5.15.0-1/include/uapi/linux/rtnetlink.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/rtnetlink.h	2022-08-02 18:36:33.000000000 +0000
@@ -146,6 +146,8 @@ enum {
 #define RTM_NEWSTATS RTM_NEWSTATS
 	RTM_GETSTATS = 94,
 #define RTM_GETSTATS RTM_GETSTATS
+	RTM_SETSTATS,
+#define RTM_SETSTATS RTM_SETSTATS
 
 	RTM_NEWCACHEREPORT = 96,
 #define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT
@@ -185,6 +187,13 @@ enum {
 	RTM_GETNEXTHOPBUCKET,
 #define RTM_GETNEXTHOPBUCKET	RTM_GETNEXTHOPBUCKET
 
+	RTM_NEWTUNNEL = 120,
+#define RTM_NEWTUNNEL	RTM_NEWTUNNEL
+	RTM_DELTUNNEL,
+#define RTM_DELTUNNEL	RTM_DELTUNNEL
+	RTM_GETTUNNEL,
+#define RTM_GETTUNNEL	RTM_GETTUNNEL
+
 	__RTM_MAX,
 #define RTM_MAX		(((__RTM_MAX + 3) & ~3) - 1)
 };
@@ -752,6 +761,12 @@ enum rtnetlink_groups {
 #define RTNLGRP_NEXTHOP		RTNLGRP_NEXTHOP
 	RTNLGRP_BRVLAN,
 #define RTNLGRP_BRVLAN		RTNLGRP_BRVLAN
+	RTNLGRP_MCTP_IFADDR,
+#define RTNLGRP_MCTP_IFADDR	RTNLGRP_MCTP_IFADDR
+	RTNLGRP_TUNNEL,
+#define RTNLGRP_TUNNEL		RTNLGRP_TUNNEL
+	RTNLGRP_STATS,
+#define RTNLGRP_STATS		RTNLGRP_STATS
 	__RTNLGRP_MAX
 };
 #define RTNLGRP_MAX	(__RTNLGRP_MAX - 1)
@@ -800,6 +815,7 @@ enum {
 #define RTEXT_FILTER_MRP	(1 << 4)
 #define RTEXT_FILTER_CFM_CONFIG	(1 << 5)
 #define RTEXT_FILTER_CFM_STATUS	(1 << 6)
+#define RTEXT_FILTER_MST	(1 << 7)
 
 /* End of information exported to user level */
 
diff -pruN 5.15.0-1/include/uapi/linux/socket.h 5.19.0-1/include/uapi/linux/socket.h
--- 5.15.0-1/include/uapi/linux/socket.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/socket.h	2022-08-02 18:36:33.000000000 +0000
@@ -31,4 +31,8 @@ struct __kernel_sockaddr_storage {
 
 #define SOCK_BUF_LOCK_MASK (SOCK_SNDBUF_LOCK | SOCK_RCVBUF_LOCK)
 
+#define SOCK_TXREHASH_DEFAULT	255
+#define SOCK_TXREHASH_DISABLED	0
+#define SOCK_TXREHASH_ENABLED	1
+
 #endif /* _LINUX_SOCKET_H */
diff -pruN 5.15.0-1/include/uapi/linux/stddef.h 5.19.0-1/include/uapi/linux/stddef.h
--- 5.15.0-1/include/uapi/linux/stddef.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/stddef.h	2022-08-02 18:36:33.000000000 +0000
@@ -1,6 +1,47 @@
 /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _LINUX_STDDEF_H
+#define _LINUX_STDDEF_H
+
 
 
 #ifndef __always_inline
 #define __always_inline __inline__
 #endif
+
+/**
+ * __struct_group() - Create a mirrored named and anonyomous struct
+ *
+ * @TAG: The tag name for the named sub-struct (usually empty)
+ * @NAME: The identifier name of the mirrored sub-struct
+ * @ATTRS: Any struct attributes (usually empty)
+ * @MEMBERS: The member declarations for the mirrored structs
+ *
+ * Used to create an anonymous union of two structs with identical layout
+ * and size: one anonymous and one named. The former's members can be used
+ * normally without sub-struct naming, and the latter can be used to
+ * reason about the start, end, and size of the group of struct members.
+ * The named struct can also be explicitly tagged for layer reuse, as well
+ * as both having struct attributes appended.
+ */
+#define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \
+	union { \
+		struct { MEMBERS } ATTRS; \
+		struct TAG { MEMBERS } ATTRS NAME; \
+	}
+
+/**
+ * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union
+ *
+ * @TYPE: The type of each flexible array element
+ * @NAME: The name of the flexible array member
+ *
+ * In order to have a flexible array member in a union or alone in a
+ * struct, it needs to be wrapped in an anonymous struct with at least 1
+ * named member, but that member can be empty.
+ */
+#define __DECLARE_FLEX_ARRAY(TYPE, NAME)	\
+	struct { \
+		struct { } __empty_ ## NAME; \
+		TYPE NAME[]; \
+	}
+#endif
diff -pruN 5.15.0-1/include/uapi/linux/tc_act/tc_skbedit.h 5.19.0-1/include/uapi/linux/tc_act/tc_skbedit.h
--- 5.15.0-1/include/uapi/linux/tc_act/tc_skbedit.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/tc_act/tc_skbedit.h	2022-08-02 18:36:33.000000000 +0000
@@ -29,6 +29,7 @@
 #define SKBEDIT_F_PTYPE			0x8
 #define SKBEDIT_F_MASK			0x10
 #define SKBEDIT_F_INHERITDSFIELD	0x20
+#define SKBEDIT_F_TXQ_SKBHASH		0x40
 
 struct tc_skbedit {
 	tc_gen;
@@ -45,6 +46,7 @@ enum {
 	TCA_SKBEDIT_PTYPE,
 	TCA_SKBEDIT_MASK,
 	TCA_SKBEDIT_FLAGS,
+	TCA_SKBEDIT_QUEUE_MAPPING_MAX,
 	__TCA_SKBEDIT_MAX
 };
 #define TCA_SKBEDIT_MAX (__TCA_SKBEDIT_MAX - 1)
diff -pruN 5.15.0-1/include/uapi/linux/tls.h 5.19.0-1/include/uapi/linux/tls.h
--- 5.15.0-1/include/uapi/linux/tls.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/tls.h	2022-08-02 18:36:33.000000000 +0000
@@ -39,6 +39,7 @@
 /* TLS socket options */
 #define TLS_TX			1	/* Set transmit parameters */
 #define TLS_RX			2	/* Set receive parameters */
+#define TLS_TX_ZEROCOPY_RO	3	/* TX zerocopy (only sendfile now) */
 
 /* Supported versions */
 #define TLS_VERSION_MINOR(ver)	((ver) & 0xFF)
@@ -84,6 +85,20 @@
 #define TLS_CIPHER_CHACHA20_POLY1305_TAG_SIZE	16
 #define TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE	8
 
+#define TLS_CIPHER_SM4_GCM				55
+#define TLS_CIPHER_SM4_GCM_IV_SIZE			8
+#define TLS_CIPHER_SM4_GCM_KEY_SIZE		16
+#define TLS_CIPHER_SM4_GCM_SALT_SIZE		4
+#define TLS_CIPHER_SM4_GCM_TAG_SIZE		16
+#define TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE		8
+
+#define TLS_CIPHER_SM4_CCM				56
+#define TLS_CIPHER_SM4_CCM_IV_SIZE			8
+#define TLS_CIPHER_SM4_CCM_KEY_SIZE		16
+#define TLS_CIPHER_SM4_CCM_SALT_SIZE		4
+#define TLS_CIPHER_SM4_CCM_TAG_SIZE		16
+#define TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE		8
+
 #define TLS_SET_RECORD_TYPE	1
 #define TLS_GET_RECORD_TYPE	2
 
@@ -124,12 +139,29 @@ struct tls12_crypto_info_chacha20_poly13
 	unsigned char rec_seq[TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE];
 };
 
+struct tls12_crypto_info_sm4_gcm {
+	struct tls_crypto_info info;
+	unsigned char iv[TLS_CIPHER_SM4_GCM_IV_SIZE];
+	unsigned char key[TLS_CIPHER_SM4_GCM_KEY_SIZE];
+	unsigned char salt[TLS_CIPHER_SM4_GCM_SALT_SIZE];
+	unsigned char rec_seq[TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE];
+};
+
+struct tls12_crypto_info_sm4_ccm {
+	struct tls_crypto_info info;
+	unsigned char iv[TLS_CIPHER_SM4_CCM_IV_SIZE];
+	unsigned char key[TLS_CIPHER_SM4_CCM_KEY_SIZE];
+	unsigned char salt[TLS_CIPHER_SM4_CCM_SALT_SIZE];
+	unsigned char rec_seq[TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE];
+};
+
 enum {
 	TLS_INFO_UNSPEC,
 	TLS_INFO_VERSION,
 	TLS_INFO_CIPHER,
 	TLS_INFO_TXCONF,
 	TLS_INFO_RXCONF,
+	TLS_INFO_ZC_RO_TX,
 	__TLS_INFO_MAX,
 };
 #define TLS_INFO_MAX (__TLS_INFO_MAX - 1)
diff -pruN 5.15.0-1/include/uapi/linux/types.h 5.19.0-1/include/uapi/linux/types.h
--- 5.15.0-1/include/uapi/linux/types.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/types.h	2022-08-02 18:36:33.000000000 +0000
@@ -14,12 +14,15 @@
  * any application/library that wants linux/types.h.
  */
 
+/* sparse defines __CHECKER__; see Documentation/dev-tools/sparse.rst */
 #ifdef __CHECKER__
-#define __bitwise__ __attribute__((bitwise))
+#define __bitwise	__attribute__((bitwise))
 #else
-#define __bitwise__
+#define __bitwise
 #endif
-#define __bitwise __bitwise__
+
+/* The kernel doesn't use this legacy form, but user space does */
+#define __bitwise__ __bitwise
 
 typedef __u16 __bitwise __le16;
 typedef __u16 __bitwise __be16;
diff -pruN 5.15.0-1/include/uapi/linux/vdpa.h 5.19.0-1/include/uapi/linux/vdpa.h
--- 5.15.0-1/include/uapi/linux/vdpa.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/vdpa.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,40 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
-/*
- * vdpa device management interface
- * Copyright (c) 2020 Mellanox Technologies Ltd. All rights reserved.
- */
-
-#ifndef _LINUX_VDPA_H_
-#define _LINUX_VDPA_H_
-
-#define VDPA_GENL_NAME "vdpa"
-#define VDPA_GENL_VERSION 0x1
-
-enum vdpa_command {
-	VDPA_CMD_UNSPEC,
-	VDPA_CMD_MGMTDEV_NEW,
-	VDPA_CMD_MGMTDEV_GET,		/* can dump */
-	VDPA_CMD_DEV_NEW,
-	VDPA_CMD_DEV_DEL,
-	VDPA_CMD_DEV_GET,		/* can dump */
-};
-
-enum vdpa_attr {
-	VDPA_ATTR_UNSPEC,
-
-	/* bus name (optional) + dev name together make the parent device handle */
-	VDPA_ATTR_MGMTDEV_BUS_NAME,		/* string */
-	VDPA_ATTR_MGMTDEV_DEV_NAME,		/* string */
-	VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES,	/* u64 */
-
-	VDPA_ATTR_DEV_NAME,			/* string */
-	VDPA_ATTR_DEV_ID,			/* u32 */
-	VDPA_ATTR_DEV_VENDOR_ID,		/* u32 */
-	VDPA_ATTR_DEV_MAX_VQS,			/* u32 */
-	VDPA_ATTR_DEV_MAX_VQ_SIZE,		/* u16 */
-
-	/* new attributes must be added above here */
-	VDPA_ATTR_MAX,
-};
-
-#endif
diff -pruN 5.15.0-1/include/uapi/linux/virtio_config.h 5.19.0-1/include/uapi/linux/virtio_config.h
--- 5.15.0-1/include/uapi/linux/virtio_config.h	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/virtio_config.h	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,99 @@
+#ifndef _LINUX_VIRTIO_CONFIG_H
+#define _LINUX_VIRTIO_CONFIG_H
+/* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so
+ * anyone can use the definitions to implement compatible drivers/servers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+
+/* Virtio devices use a standardized configuration space to define their
+ * features and pass configuration information, but each implementation can
+ * store and access that space differently. */
+#include <linux/types.h>
+
+/* Status byte for guest to report progress, and synchronize features. */
+/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */
+#define VIRTIO_CONFIG_S_ACKNOWLEDGE	1
+/* We have found a driver for the device. */
+#define VIRTIO_CONFIG_S_DRIVER		2
+/* Driver has used its parts of the config, and is happy */
+#define VIRTIO_CONFIG_S_DRIVER_OK	4
+/* Driver has finished configuring features */
+#define VIRTIO_CONFIG_S_FEATURES_OK	8
+/* Device entered invalid state, driver must reset it */
+#define VIRTIO_CONFIG_S_NEEDS_RESET	0x40
+/* We've given up on this device. */
+#define VIRTIO_CONFIG_S_FAILED		0x80
+
+/*
+ * Virtio feature bits VIRTIO_TRANSPORT_F_START through
+ * VIRTIO_TRANSPORT_F_END are reserved for the transport
+ * being used (e.g. virtio_ring, virtio_pci etc.), the
+ * rest are per-device feature bits.
+ */
+#define VIRTIO_TRANSPORT_F_START	28
+#define VIRTIO_TRANSPORT_F_END		38
+
+#ifndef VIRTIO_CONFIG_NO_LEGACY
+/* Do we get callbacks when the ring is completely used, even if we've
+ * suppressed them? */
+#define VIRTIO_F_NOTIFY_ON_EMPTY	24
+
+/* Can the device handle any descriptor layout? */
+#define VIRTIO_F_ANY_LAYOUT		27
+#endif /* VIRTIO_CONFIG_NO_LEGACY */
+
+/* v1.0 compliant. */
+#define VIRTIO_F_VERSION_1		32
+
+/*
+ * If clear - device has the platform DMA (e.g. IOMMU) bypass quirk feature.
+ * If set - use platform DMA tools to access the memory.
+ *
+ * Note the reverse polarity (compared to most other features),
+ * this is for compatibility with legacy systems.
+ */
+#define VIRTIO_F_ACCESS_PLATFORM	33
+/* Legacy name for VIRTIO_F_ACCESS_PLATFORM (for compatibility with old userspace) */
+#define VIRTIO_F_IOMMU_PLATFORM		VIRTIO_F_ACCESS_PLATFORM
+
+/* This feature indicates support for the packed virtqueue layout. */
+#define VIRTIO_F_RING_PACKED		34
+
+/*
+ * Inorder feature indicates that all buffers are used by the device
+ * in the same order in which they have been made available.
+ */
+#define VIRTIO_F_IN_ORDER		35
+
+/*
+ * This feature indicates that memory accesses by the driver and the
+ * device are ordered in a way described by the platform.
+ */
+#define VIRTIO_F_ORDER_PLATFORM		36
+
+/*
+ * Does the device support Single Root I/O Virtualization?
+ */
+#define VIRTIO_F_SR_IOV			37
+#endif /* _LINUX_VIRTIO_CONFIG_H */
diff -pruN 5.15.0-1/include/uapi/linux/virtio_ids.h 5.19.0-1/include/uapi/linux/virtio_ids.h
--- 5.15.0-1/include/uapi/linux/virtio_ids.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/virtio_ids.h	2022-08-02 18:36:33.000000000 +0000
@@ -73,12 +73,12 @@
  * Virtio Transitional IDs
  */
 
-#define VIRTIO_TRANS_ID_NET		1000 /* transitional virtio net */
-#define VIRTIO_TRANS_ID_BLOCK		1001 /* transitional virtio block */
-#define VIRTIO_TRANS_ID_BALLOON		1002 /* transitional virtio balloon */
-#define VIRTIO_TRANS_ID_CONSOLE		1003 /* transitional virtio console */
-#define VIRTIO_TRANS_ID_SCSI		1004 /* transitional virtio SCSI */
-#define VIRTIO_TRANS_ID_RNG		1005 /* transitional virtio rng */
-#define VIRTIO_TRANS_ID_9P		1009 /* transitional virtio 9p console */
+#define VIRTIO_TRANS_ID_NET		0x1000 /* transitional virtio net */
+#define VIRTIO_TRANS_ID_BLOCK		0x1001 /* transitional virtio block */
+#define VIRTIO_TRANS_ID_BALLOON		0x1002 /* transitional virtio balloon */
+#define VIRTIO_TRANS_ID_CONSOLE		0x1003 /* transitional virtio console */
+#define VIRTIO_TRANS_ID_SCSI		0x1004 /* transitional virtio SCSI */
+#define VIRTIO_TRANS_ID_RNG		0x1005 /* transitional virtio rng */
+#define VIRTIO_TRANS_ID_9P		0x1009 /* transitional virtio 9p console */
 
 #endif /* _LINUX_VIRTIO_IDS_H */
diff -pruN 5.15.0-1/include/uapi/linux/virtio_net.h 5.19.0-1/include/uapi/linux/virtio_net.h
--- 5.15.0-1/include/uapi/linux/virtio_net.h	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/virtio_net.h	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,358 @@
+#ifndef _LINUX_VIRTIO_NET_H
+#define _LINUX_VIRTIO_NET_H
+/* This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+#include <linux/types.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_types.h>
+#include <linux/if_ether.h>
+
+/* The feature bitmap for virtio net */
+#define VIRTIO_NET_F_CSUM	0	/* Host handles pkts w/ partial csum */
+#define VIRTIO_NET_F_GUEST_CSUM	1	/* Guest handles pkts w/ partial csum */
+#define VIRTIO_NET_F_CTRL_GUEST_OFFLOADS 2 /* Dynamic offload configuration. */
+#define VIRTIO_NET_F_MTU	3	/* Initial MTU advice */
+#define VIRTIO_NET_F_MAC	5	/* Host has given MAC address. */
+#define VIRTIO_NET_F_GUEST_TSO4	7	/* Guest can handle TSOv4 in. */
+#define VIRTIO_NET_F_GUEST_TSO6	8	/* Guest can handle TSOv6 in. */
+#define VIRTIO_NET_F_GUEST_ECN	9	/* Guest can handle TSO[6] w/ ECN in. */
+#define VIRTIO_NET_F_GUEST_UFO	10	/* Guest can handle UFO in. */
+#define VIRTIO_NET_F_HOST_TSO4	11	/* Host can handle TSOv4 in. */
+#define VIRTIO_NET_F_HOST_TSO6	12	/* Host can handle TSOv6 in. */
+#define VIRTIO_NET_F_HOST_ECN	13	/* Host can handle TSO[6] w/ ECN in. */
+#define VIRTIO_NET_F_HOST_UFO	14	/* Host can handle UFO in. */
+#define VIRTIO_NET_F_MRG_RXBUF	15	/* Host can merge receive buffers. */
+#define VIRTIO_NET_F_STATUS	16	/* virtio_net_config.status available */
+#define VIRTIO_NET_F_CTRL_VQ	17	/* Control channel available */
+#define VIRTIO_NET_F_CTRL_RX	18	/* Control channel RX mode support */
+#define VIRTIO_NET_F_CTRL_VLAN	19	/* Control channel VLAN filtering */
+#define VIRTIO_NET_F_CTRL_RX_EXTRA 20	/* Extra RX mode control support */
+#define VIRTIO_NET_F_GUEST_ANNOUNCE 21	/* Guest can announce device on the
+					 * network */
+#define VIRTIO_NET_F_MQ	22	/* Device supports Receive Flow
+					 * Steering */
+#define VIRTIO_NET_F_CTRL_MAC_ADDR 23	/* Set MAC address */
+
+#define VIRTIO_NET_F_HASH_REPORT  57	/* Supports hash report */
+#define VIRTIO_NET_F_RSS	  60	/* Supports RSS RX steering */
+#define VIRTIO_NET_F_RSC_EXT	  61	/* extended coalescing info */
+#define VIRTIO_NET_F_STANDBY	  62	/* Act as standby for another device
+					 * with the same MAC.
+					 */
+#define VIRTIO_NET_F_SPEED_DUPLEX 63	/* Device set linkspeed and duplex */
+
+#ifndef VIRTIO_NET_NO_LEGACY
+#define VIRTIO_NET_F_GSO	6	/* Host handles pkts w/ any GSO type */
+#endif /* VIRTIO_NET_NO_LEGACY */
+
+#define VIRTIO_NET_S_LINK_UP	1	/* Link is up */
+#define VIRTIO_NET_S_ANNOUNCE	2	/* Announcement is needed */
+
+/* supported/enabled hash types */
+#define VIRTIO_NET_RSS_HASH_TYPE_IPv4          (1 << 0)
+#define VIRTIO_NET_RSS_HASH_TYPE_TCPv4         (1 << 1)
+#define VIRTIO_NET_RSS_HASH_TYPE_UDPv4         (1 << 2)
+#define VIRTIO_NET_RSS_HASH_TYPE_IPv6          (1 << 3)
+#define VIRTIO_NET_RSS_HASH_TYPE_TCPv6         (1 << 4)
+#define VIRTIO_NET_RSS_HASH_TYPE_UDPv6         (1 << 5)
+#define VIRTIO_NET_RSS_HASH_TYPE_IP_EX         (1 << 6)
+#define VIRTIO_NET_RSS_HASH_TYPE_TCP_EX        (1 << 7)
+#define VIRTIO_NET_RSS_HASH_TYPE_UDP_EX        (1 << 8)
+
+struct virtio_net_config {
+	/* The config defining mac address (if VIRTIO_NET_F_MAC) */
+	__u8 mac[ETH_ALEN];
+	/* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */
+	__virtio16 status;
+	/* Maximum number of each of transmit and receive queues;
+	 * see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ.
+	 * Legal values are between 1 and 0x8000
+	 */
+	__virtio16 max_virtqueue_pairs;
+	/* Default maximum transmit unit advice */
+	__virtio16 mtu;
+	/*
+	 * speed, in units of 1Mb. All values 0 to INT_MAX are legal.
+	 * Any other value stands for unknown.
+	 */
+	__le32 speed;
+	/*
+	 * 0x00 - half duplex
+	 * 0x01 - full duplex
+	 * Any other value stands for unknown.
+	 */
+	__u8 duplex;
+	/* maximum size of RSS key */
+	__u8 rss_max_key_size;
+	/* maximum number of indirection table entries */
+	__le16 rss_max_indirection_table_length;
+	/* bitmask of supported VIRTIO_NET_RSS_HASH_ types */
+	__le32 supported_hash_types;
+} __attribute__((packed));
+
+/*
+ * This header comes first in the scatter-gather list.  If you don't
+ * specify GSO or CSUM features, you can simply ignore the header.
+ *
+ * This is bitwise-equivalent to the legacy struct virtio_net_hdr_mrg_rxbuf,
+ * only flattened.
+ */
+struct virtio_net_hdr_v1 {
+#define VIRTIO_NET_HDR_F_NEEDS_CSUM	1	/* Use csum_start, csum_offset */
+#define VIRTIO_NET_HDR_F_DATA_VALID	2	/* Csum is valid */
+#define VIRTIO_NET_HDR_F_RSC_INFO	4	/* rsc info in csum_ fields */
+	__u8 flags;
+#define VIRTIO_NET_HDR_GSO_NONE		0	/* Not a GSO frame */
+#define VIRTIO_NET_HDR_GSO_TCPV4	1	/* GSO frame, IPv4 TCP (TSO) */
+#define VIRTIO_NET_HDR_GSO_UDP		3	/* GSO frame, IPv4 UDP (UFO) */
+#define VIRTIO_NET_HDR_GSO_TCPV6	4	/* GSO frame, IPv6 TCP */
+#define VIRTIO_NET_HDR_GSO_ECN		0x80	/* TCP has ECN set */
+	__u8 gso_type;
+	__virtio16 hdr_len;	/* Ethernet + IP + tcp/udp hdrs */
+	__virtio16 gso_size;	/* Bytes to append to hdr_len per frame */
+	union {
+		struct {
+			__virtio16 csum_start;
+			__virtio16 csum_offset;
+		};
+		/* Checksum calculation */
+		struct {
+			/* Position to start checksumming from */
+			__virtio16 start;
+			/* Offset after that to place checksum */
+			__virtio16 offset;
+		} csum;
+		/* Receive Segment Coalescing */
+		struct {
+			/* Number of coalesced segments */
+			__le16 segments;
+			/* Number of duplicated acks */
+			__le16 dup_acks;
+		} rsc;
+	};
+	__virtio16 num_buffers;	/* Number of merged rx buffers */
+};
+
+struct virtio_net_hdr_v1_hash {
+	struct virtio_net_hdr_v1 hdr;
+	__le32 hash_value;
+#define VIRTIO_NET_HASH_REPORT_NONE            0
+#define VIRTIO_NET_HASH_REPORT_IPv4            1
+#define VIRTIO_NET_HASH_REPORT_TCPv4           2
+#define VIRTIO_NET_HASH_REPORT_UDPv4           3
+#define VIRTIO_NET_HASH_REPORT_IPv6            4
+#define VIRTIO_NET_HASH_REPORT_TCPv6           5
+#define VIRTIO_NET_HASH_REPORT_UDPv6           6
+#define VIRTIO_NET_HASH_REPORT_IPv6_EX         7
+#define VIRTIO_NET_HASH_REPORT_TCPv6_EX        8
+#define VIRTIO_NET_HASH_REPORT_UDPv6_EX        9
+	__le16 hash_report;
+	__le16 padding;
+};
+
+#ifndef VIRTIO_NET_NO_LEGACY
+/* This header comes first in the scatter-gather list.
+ * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must
+ * be the first element of the scatter-gather list.  If you don't
+ * specify GSO or CSUM features, you can simply ignore the header. */
+struct virtio_net_hdr {
+	/* See VIRTIO_NET_HDR_F_* */
+	__u8 flags;
+	/* See VIRTIO_NET_HDR_GSO_* */
+	__u8 gso_type;
+	__virtio16 hdr_len;		/* Ethernet + IP + tcp/udp hdrs */
+	__virtio16 gso_size;		/* Bytes to append to hdr_len per frame */
+	__virtio16 csum_start;	/* Position to start checksumming from */
+	__virtio16 csum_offset;	/* Offset after that to place checksum */
+};
+
+/* This is the version of the header to use when the MRG_RXBUF
+ * feature has been negotiated. */
+struct virtio_net_hdr_mrg_rxbuf {
+	struct virtio_net_hdr hdr;
+	__virtio16 num_buffers;	/* Number of merged rx buffers */
+};
+#endif /* ...VIRTIO_NET_NO_LEGACY */
+
+/*
+ * Control virtqueue data structures
+ *
+ * The control virtqueue expects a header in the first sg entry
+ * and an ack/status response in the last entry.  Data for the
+ * command goes in between.
+ */
+struct virtio_net_ctrl_hdr {
+	__u8 class;
+	__u8 cmd;
+} __attribute__((packed));
+
+typedef __u8 virtio_net_ctrl_ack;
+
+#define VIRTIO_NET_OK     0
+#define VIRTIO_NET_ERR    1
+
+/*
+ * Control the RX mode, ie. promisucous, allmulti, etc...
+ * All commands require an "out" sg entry containing a 1 byte
+ * state value, zero = disable, non-zero = enable.  Commands
+ * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature.
+ * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA.
+ */
+#define VIRTIO_NET_CTRL_RX    0
+ #define VIRTIO_NET_CTRL_RX_PROMISC      0
+ #define VIRTIO_NET_CTRL_RX_ALLMULTI     1
+ #define VIRTIO_NET_CTRL_RX_ALLUNI       2
+ #define VIRTIO_NET_CTRL_RX_NOMULTI      3
+ #define VIRTIO_NET_CTRL_RX_NOUNI        4
+ #define VIRTIO_NET_CTRL_RX_NOBCAST      5
+
+/*
+ * Control the MAC
+ *
+ * The MAC filter table is managed by the hypervisor, the guest should
+ * assume the size is infinite.  Filtering should be considered
+ * non-perfect, ie. based on hypervisor resources, the guest may
+ * received packets from sources not specified in the filter list.
+ *
+ * In addition to the class/cmd header, the TABLE_SET command requires
+ * two out scatterlists.  Each contains a 4 byte count of entries followed
+ * by a concatenated byte stream of the ETH_ALEN MAC addresses.  The
+ * first sg list contains unicast addresses, the second is for multicast.
+ * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature
+ * is available.
+ *
+ * The ADDR_SET command requests one out scatterlist, it contains a
+ * 6 bytes MAC address. This functionality is present if the
+ * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available.
+ */
+struct virtio_net_ctrl_mac {
+	__virtio32 entries;
+	__u8 macs[][ETH_ALEN];
+} __attribute__((packed));
+
+#define VIRTIO_NET_CTRL_MAC    1
+ #define VIRTIO_NET_CTRL_MAC_TABLE_SET        0
+ #define VIRTIO_NET_CTRL_MAC_ADDR_SET         1
+
+/*
+ * Control VLAN filtering
+ *
+ * The VLAN filter table is controlled via a simple ADD/DEL interface.
+ * VLAN IDs not added may be filterd by the hypervisor.  Del is the
+ * opposite of add.  Both commands expect an out entry containing a 2
+ * byte VLAN ID.  VLAN filterting is available with the
+ * VIRTIO_NET_F_CTRL_VLAN feature bit.
+ */
+#define VIRTIO_NET_CTRL_VLAN       2
+ #define VIRTIO_NET_CTRL_VLAN_ADD             0
+ #define VIRTIO_NET_CTRL_VLAN_DEL             1
+
+/*
+ * Control link announce acknowledgement
+ *
+ * The command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that
+ * driver has recevied the notification; device would clear the
+ * VIRTIO_NET_S_ANNOUNCE bit in the status field after it receives
+ * this command.
+ */
+#define VIRTIO_NET_CTRL_ANNOUNCE       3
+ #define VIRTIO_NET_CTRL_ANNOUNCE_ACK         0
+
+/*
+ * Control Receive Flow Steering
+ */
+#define VIRTIO_NET_CTRL_MQ   4
+/*
+ * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET
+ * enables Receive Flow Steering, specifying the number of the transmit and
+ * receive queues that will be used. After the command is consumed and acked by
+ * the device, the device will not steer new packets on receive virtqueues
+ * other than specified nor read from transmit virtqueues other than specified.
+ * Accordingly, driver should not transmit new packets  on virtqueues other than
+ * specified.
+ */
+struct virtio_net_ctrl_mq {
+	__virtio16 virtqueue_pairs;
+};
+
+ #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET        0
+ #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN        1
+ #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX        0x8000
+
+/*
+ * The command VIRTIO_NET_CTRL_MQ_RSS_CONFIG has the same effect as
+ * VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET does and additionally configures
+ * the receive steering to use a hash calculated for incoming packet
+ * to decide on receive virtqueue to place the packet. The command
+ * also provides parameters to calculate a hash and receive virtqueue.
+ */
+struct virtio_net_rss_config {
+	__le32 hash_types;
+	__le16 indirection_table_mask;
+	__le16 unclassified_queue;
+	__le16 indirection_table[1/* + indirection_table_mask */];
+	__le16 max_tx_vq;
+	__u8 hash_key_length;
+	__u8 hash_key_data[/* hash_key_length */];
+};
+
+ #define VIRTIO_NET_CTRL_MQ_RSS_CONFIG          1
+
+/*
+ * The command VIRTIO_NET_CTRL_MQ_HASH_CONFIG requests the device
+ * to include in the virtio header of the packet the value of the
+ * calculated hash and the report type of hash. It also provides
+ * parameters for hash calculation. The command requires feature
+ * VIRTIO_NET_F_HASH_REPORT to be negotiated to extend the
+ * layout of virtio header as defined in virtio_net_hdr_v1_hash.
+ */
+struct virtio_net_hash_config {
+	__le32 hash_types;
+	/* for compatibility with virtio_net_rss_config */
+	__le16 reserved[4];
+	__u8 hash_key_length;
+	__u8 hash_key_data[/* hash_key_length */];
+};
+
+ #define VIRTIO_NET_CTRL_MQ_HASH_CONFIG         2
+
+/*
+ * Control network offloads
+ *
+ * Reconfigures the network offloads that Guest can handle.
+ *
+ * Available with the VIRTIO_NET_F_CTRL_GUEST_OFFLOADS feature bit.
+ *
+ * Command data format matches the feature bit mask exactly.
+ *
+ * See VIRTIO_NET_F_GUEST_* for the list of offloads
+ * that can be enabled/disabled.
+ */
+#define VIRTIO_NET_CTRL_GUEST_OFFLOADS   5
+#define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET        0
+
+#endif /* _LINUX_VIRTIO_NET_H */
diff -pruN 5.15.0-1/include/uapi/linux/virtio_types.h 5.19.0-1/include/uapi/linux/virtio_types.h
--- 5.15.0-1/include/uapi/linux/virtio_types.h	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/virtio_types.h	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,46 @@
+#ifndef _LINUX_VIRTIO_TYPES_H
+#define _LINUX_VIRTIO_TYPES_H
+/* Type definitions for virtio implementations.
+ *
+ * This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ */
+#include <linux/types.h>
+
+/*
+ * __virtio{16,32,64} have the following meaning:
+ * - __u{16,32,64} for virtio devices in legacy mode, accessed in native endian
+ * - __le{16,32,64} for standard-compliant virtio devices
+ */
+
+typedef __u16 __bitwise __virtio16;
+typedef __u32 __bitwise __virtio32;
+typedef __u64 __bitwise __virtio64;
+
+#endif /* _LINUX_VIRTIO_TYPES_H */
diff -pruN 5.15.0-1/include/uapi/linux/xfrm.h 5.19.0-1/include/uapi/linux/xfrm.h
--- 5.15.0-1/include/uapi/linux/xfrm.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/uapi/linux/xfrm.h	2022-08-02 18:36:33.000000000 +0000
@@ -313,6 +313,7 @@ enum xfrm_attr_type_t {
 	XFRMA_SET_MARK,		/* __u32 */
 	XFRMA_SET_MARK_MASK,	/* __u32 */
 	XFRMA_IF_ID,		/* __u32 */
+	XFRMA_MTIMER_THRESH,	/* __u32 in seconds for input SA */
 	__XFRMA_MAX
 
 #define XFRMA_OUTPUT_MARK XFRMA_SET_MARK	/* Compatibility */
@@ -510,6 +511,12 @@ struct xfrm_user_offload {
 	int				ifindex;
 	__u8				flags;
 };
+/* This flag was exposed without any kernel code that supporting it.
+ * Unfortunately, strongswan has the code that uses sets this flag,
+ * which makes impossible to reuse this bit.
+ *
+ * So leave it here to make sure that it won't be reused by mistake.
+ */
 #define XFRM_OFFLOAD_IPV6	1
 #define XFRM_OFFLOAD_INBOUND	2
 
diff -pruN 5.15.0-1/include/utils.h 5.19.0-1/include/utils.h
--- 5.15.0-1/include/utils.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/utils.h	2022-08-02 18:36:33.000000000 +0000
@@ -198,9 +198,15 @@ bool matches(const char *prefix, const c
 int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits);
 int inet_addr_match_rta(const inet_prefix *m, const struct rtattr *rta);
 
+const char *ax25_ntop(int af, const void *addr, char *str, socklen_t len);
+
+const char *rose_ntop(int af, const void *addr, char *buf, socklen_t buflen);
+
 const char *mpls_ntop(int af, const void *addr, char *str, size_t len);
 int mpls_pton(int af, const char *src, void *addr, size_t alen);
 
+const char *netrom_ntop(int af, const void *addr, char *str, socklen_t len);
+
 extern int __iproute2_hz_internal;
 int __get_hz(void);
 
@@ -255,7 +261,9 @@ int print_timestamp(FILE *fp);
 void print_nlmsg_timestamp(FILE *fp, const struct nlmsghdr *n);
 
 unsigned int print_name_and_link(const char *fmt,
-				 const char *name, struct rtattr *tb[]);
+				 const char *name, struct rtattr *tb[])
+	__attribute__((format(printf, 1, 0)));
+
 
 #define BIT(nr)                 (UINT64_C(1) << (nr))
 
@@ -301,7 +309,7 @@ char *find_cgroup2_mount(bool do_mount);
 __u64 get_cgroup2_id(const char *path);
 char *get_cgroup2_path(__u64 id, bool full);
 int get_command_name(const char *pid, char *comm, size_t len);
-char *get_task_name(pid_t pid);
+int get_task_name(pid_t pid, char *name, size_t len);
 
 int get_rtnl_link_stats_rta(struct rtnl_link_stats64 *stats64,
 			    struct rtattr *tb[]);
diff -pruN 5.15.0-1/include/version.h 5.19.0-1/include/version.h
--- 5.15.0-1/include/version.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/include/version.h	2022-08-02 18:36:33.000000000 +0000
@@ -1 +1 @@
-static const char version[] = "5.15.0";
+static const char version[] = "5.19.0";
diff -pruN 5.15.0-1/ip/ifcfg 5.19.0-1/ip/ifcfg
--- 5.15.0-1/ip/ifcfg	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/ifcfg	1970-01-01 00:00:00.000000000 +0000
@@ -1,150 +0,0 @@
-#! /bin/sh
-# SPDX-License-Identifier: GPL-2.0
-
-CheckForwarding () {
-  local sbase fwd
-  sbase=/proc/sys/net/ipv4/conf
-  fwd=0
-  if [ -d $sbase ]; then
-    for dir in $sbase/*/forwarding; do
-      fwd=$(( fwd + $(cat "$dir") ))
-    done
-  else
-    fwd=2
-  fi
-  return $fwd
-}
-
-RestartRDISC () {
-  killall -HUP rdisc || rdisc -fs
-}
-
-ABCMaskLen () {
-  local class;
-
-  class=${1%%.*}
-  if [ "$1" = "" -o $class -eq 0 -o $class -ge 224 ]; then return 0
-  elif [ $class -ge 224 ]; then return 0
-  elif [ $class -ge 192 ]; then return 24
-  elif [ $class -ge 128 ]; then return 16
-  else return 8; fi
-}
-
-label="label $1"
-ldev="$1"
-dev=${1%:*}
-if [ "$dev" = "" -o "$1" = "help" ]; then
-  echo "Usage: ifcfg DEV [[add|del [ADDR[/LEN]] [PEER] | stop]" 1>&2
-  echo "       add - add new address" 1>&2
-  echo "       del - delete address" 1>&2
-  echo "       stop - completely disable IP" 1>&2
-  exit 1
-fi
-shift
-
-CheckForwarding
-fwd=$?
-if [ $fwd -ne 0 ]; then
-  echo "Forwarding is ON or its state is unknown ($fwd). OK, No RDISC." 1>&2
-fi
-
-
-deleting=0
-case "$1" in
-add) shift ;;
-stop)
-  if [ "$ldev" != "$dev" ]; then
-    echo "Cannot stop alias $ldev" 1>&2
-    exit 1;
-  fi
-  ip -4 addr flush dev $dev $label || exit 1
-  if [ $fwd -eq 0 ]; then RestartRDISC; fi
-  exit 0 ;;
-del*)
-  deleting=1; shift ;;
-*)
-esac
-
-ipaddr=
-pfxlen=
-if [ "$1" != "" ]; then
-  ipaddr=${1%/*}
-  if [ "$1" != "$ipaddr" ]; then
-    pfxlen=${1#*/}
-  fi
-  if [ "$ipaddr" = "" ]; then
-    echo "$1 is bad IP address." 1>&2
-    exit 1
-  fi
-fi
-shift
-
-peer=$1
-if [ "$peer" != "" ]; then
-  if [ "$pfxlen" != "" -a "$pfxlen" != "32" ]; then
-    echo "Peer address with non-trivial netmask." 1>&2
-    exit 1
-  fi
-  pfx="$ipaddr peer $peer"
-else
-  if [ "$ipaddr" = "" ]; then
-    echo "Missing IP address argument." 1>&2
-    exit 1
-  fi
-  if [ "$pfxlen" = "" ]; then
-    ABCMaskLen $ipaddr
-    pfxlen=$?
-  fi
-  pfx="$ipaddr/$pfxlen"
-fi
-
-if [ "$ldev" = "$dev" -a "$ipaddr" != "" ]; then
-  label=
-fi
-
-if [ $deleting -ne 0 ]; then
-  ip addr del $pfx dev $dev $label || exit 1
-  if [ $fwd -eq 0 ]; then RestartRDISC; fi
-  exit 0
-fi
-
-
-if ! ip link set up dev $dev ; then
-  echo "Error: cannot enable interface $dev." 1>&2
-  exit 1
-fi
-if [ "$ipaddr" = "" ]; then exit 0; fi
-
-if ! arping -q -c 2 -w 3 -D -I $dev $ipaddr ; then
-  echo "Error: some host already uses address $ipaddr on $dev." 1>&2
-  exit 1
-fi
-
-if ! ip address add $pfx brd + dev $dev $label; then
-  echo "Error: failed to add $pfx on $dev." 1>&2
-  exit 1
-fi
-
-arping -q -A -c 1 -I $dev $ipaddr
-noarp=$?
-( sleep 2 ;
-  arping -q -U -c 1 -I $dev $ipaddr ) >/dev/null 2>&1 </dev/null &
-
-ip route add unreachable 224.0.0.0/24 >/dev/null 2>&1
-ip route add unreachable 255.255.255.255 >/dev/null 2>&1
-if [ "`ip link ls $dev | grep -c MULTICAST`" -ge 1 ]; then
-  ip route add 224.0.0.0/4 dev $dev scope global >/dev/null 2>&1
-fi
-
-if [ $fwd -eq 0 ]; then
-  if [ $noarp -eq 0 ]; then
-    ip ro append default dev $dev metric 30000 scope global
-  elif [ "$peer" != "" ]; then
-    if ping -q -c 2 -w 4 $peer ; then
-      ip ro append default via $peer dev $dev metric 30001
-    fi
-  fi
-  RestartRDISC
-fi
-
-exit 0
diff -pruN 5.15.0-1/ip/ipaddress.c 5.19.0-1/ip/ipaddress.c
--- 5.15.0-1/ip/ipaddress.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/ipaddress.c	2022-08-02 18:36:33.000000000 +0000
@@ -60,6 +60,7 @@ static void usage(void)
 		"       ip address {save|flush} [ dev IFNAME ] [ scope SCOPE-ID ]\n"
 		"                            [ to PREFIX ] [ FLAG-LIST ] [ label LABEL ] [up]\n"
 		"       ip address [ show [ dev IFNAME ] [ scope SCOPE-ID ] [ master DEVICE ]\n"
+		"                         [ nomaster ]\n"
 		"                         [ type TYPE ] [ to PREFIX ] [ FLAG-LIST ]\n"
 		"                         [ label LABEL ] [up] [ vrf NAME ] ]\n"
 		"       ip address {showdump|restore}\n"
@@ -545,10 +546,10 @@ static void print_vfinfo(FILE *fp, struc
 		print_vf_stats64(fp, vf[IFLA_VF_STATS]);
 }
 
-static void size_columns(unsigned int cols[], unsigned int n, ...)
+void size_columns(unsigned int cols[], unsigned int n, ...)
 {
 	unsigned int i, len;
-	uint64_t val, powi;
+	uint64_t val;
 	va_list args;
 
 	va_start(args, n);
@@ -559,7 +560,7 @@ static void size_columns(unsigned int co
 		if (human_readable)
 			continue;
 
-		for (len = 1, powi = 10; powi < val; len++, powi *= 10)
+		for (len = 1; val > 9; len++, val /= 10)
 			/* nothing */;
 		if (len > cols[i])
 			cols[i] = len;
@@ -679,10 +680,10 @@ static void print_vf_stats64(FILE *fp, s
 	}
 }
 
-static void __print_link_stats(FILE *fp, struct rtattr *tb[])
+void print_stats64(FILE *fp, struct rtnl_link_stats64 *s,
+		   const struct rtattr *carrier_changes,
+		   const char *what)
 {
-	const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES];
-	struct rtnl_link_stats64 _s, *s = &_s;
 	unsigned int cols[] = {
 		strlen("*X errors:"),
 		strlen("packets"),
@@ -692,14 +693,10 @@ static void __print_link_stats(FILE *fp,
 		strlen("overrun"),
 		strlen("compressed"),
 	};
-	int ret;
-
-	ret = get_rtnl_link_stats_rta(s, tb);
-	if (ret < 0)
-		return;
 
 	if (is_json_context()) {
-		open_json_object((ret == sizeof(*s)) ? "stats64" : "stats");
+		if (what)
+			open_json_object(what);
 
 		/* RX stats */
 		open_json_object("rx");
@@ -770,7 +767,8 @@ static void __print_link_stats(FILE *fp,
 		}
 
 		close_json_object();
-		close_json_object();
+		if (what)
+			close_json_object();
 	} else {
 		size_columns(cols, ARRAY_SIZE(cols),
 			     s->rx_bytes, s->rx_packets, s->rx_errors,
@@ -785,13 +783,15 @@ static void __print_link_stats(FILE *fp,
 			     s->tx_bytes, s->tx_packets, s->tx_errors,
 			     s->tx_dropped, s->tx_carrier_errors,
 			     s->collisions, s->tx_compressed);
-		if (show_stats > 1)
+		if (show_stats > 1) {
+			uint64_t cc = carrier_changes ?
+				      rta_getattr_u32(carrier_changes) : 0;
+
 			size_columns(cols, ARRAY_SIZE(cols), 0, 0,
 				     s->tx_aborted_errors, s->tx_fifo_errors,
 				     s->tx_window_errors,
-				     s->tx_heartbeat_errors,
-				     carrier_changes ?
-				     rta_getattr_u32(carrier_changes) : 0);
+				     s->tx_heartbeat_errors, cc);
+		}
 
 		/* RX stats */
 		fprintf(fp, "    RX: %*s %*s %*s %*s %*s %*s %*s%s",
@@ -869,6 +869,20 @@ static void __print_link_stats(FILE *fp,
 	}
 }
 
+static void __print_link_stats(FILE *fp, struct rtattr *tb[])
+{
+	const struct rtattr *carrier_changes = tb[IFLA_CARRIER_CHANGES];
+	struct rtnl_link_stats64 _s, *s = &_s;
+	int ret;
+
+	ret = get_rtnl_link_stats_rta(s, tb);
+	if (ret < 0)
+		return;
+
+	print_stats64(fp, s, carrier_changes,
+		      (ret == sizeof(*s)) ? "stats64" : "stats");
+}
+
 static void print_link_stats(FILE *fp, struct nlmsghdr *n)
 {
 	struct ifinfomsg *ifi = NLMSG_DATA(n);
@@ -1218,6 +1232,12 @@ int print_linkinfo(struct nlmsghdr *n, v
 				   "gso_max_segs %u ",
 				   rta_getattr_u32(tb[IFLA_GSO_MAX_SEGS]));
 
+		if (tb[IFLA_GRO_MAX_SIZE])
+			print_uint(PRINT_ANY,
+				   "gro_max_size",
+				   "gro_max_size %u ",
+				   rta_getattr_u32(tb[IFLA_GRO_MAX_SIZE]));
+
 		if (tb[IFLA_PHYS_PORT_NAME])
 			print_string(PRINT_ANY,
 				     "phys_port_name",
@@ -2010,8 +2030,10 @@ static int ipaddr_link_get(int index, st
 
 	if (store_nlmsg(answer, linfo) < 0) {
 		fprintf(stderr, "Failed to process link information\n");
+		free(answer);
 		return 1;
 	}
+	free(answer);
 
 	return 0;
 }
@@ -2123,6 +2145,8 @@ static int ipaddr_list_flush_or_save(int
 			if (!name_is_vrf(*argv))
 				invarg("Not a valid VRF name\n", *argv);
 			filter.master = ifindex;
+		} else if (strcmp(*argv, "nomaster") == 0) {
+			filter.master = -1;
 		} else if (strcmp(*argv, "type") == 0) {
 			int soff;
 
@@ -2340,16 +2364,6 @@ static bool ipaddr_is_multicast(inet_pre
 		return false;
 }
 
-static bool is_valid_label(const char *dev, const char *label)
-{
-	size_t len = strlen(dev);
-
-	if (strncmp(label, dev, len) != 0)
-		return false;
-
-	return label[len] == '\0' || label[len] == ':';
-}
-
 static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
 {
 	struct {
@@ -2492,12 +2506,6 @@ static int ipaddr_modify(int cmd, int fl
 		fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
 		return -1;
 	}
-	if (l && !is_valid_label(d, l)) {
-		fprintf(stderr,
-			"\"label\" (%s) must match \"dev\" (%s) or be prefixed by \"dev\" with a colon.\n",
-			l, d);
-		return -1;
-	}
 
 	if (peer_len == 0 && local_len) {
 		if (cmd == RTM_DELADDR && lcl.family == AF_INET && !(lcl.flags & PREFIXLEN_SPECIFIED)) {
diff -pruN 5.15.0-1/ip/ip.c 5.19.0-1/ip/ip.c
--- 5.15.0-1/ip/ip.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/ip.c	2022-08-02 18:36:33.000000000 +0000
@@ -64,8 +64,8 @@ static void usage(void)
 	fprintf(stderr,
 		"Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n"
 		"       ip [ -force ] -batch filename\n"
-		"where  OBJECT := { address | addrlabel | fou | help | ila | ioam | l2tp | link |\n"
-		"                   macsec | maddress | monitor | mptcp | mroute | mrule |\n"
+		"where  OBJECT := { address | addrlabel | amt | fou | help | ila | ioam | l2tp |\n"
+		"                   link | macsec | maddress | monitor | mptcp | mroute | mrule |\n"
 		"                   neighbor | neighbour | netconf | netns | nexthop | ntable |\n"
 		"                   ntbl | route | rule | sr | tap | tcpmetrics |\n"
 		"                   token | tunnel | tuntap | vrf | xfrm }\n"
@@ -123,6 +123,7 @@ static const struct cmd {
 	{ "mptcp",	do_mptcp },
 	{ "ioam",	do_ioam6 },
 	{ "help",	do_help },
+	{ "stats",	do_ipstats },
 	{ 0 }
 };
 
diff -pruN 5.15.0-1/ip/ip_common.h 5.19.0-1/ip/ip_common.h
--- 5.15.0-1/ip/ip_common.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/ip_common.h	2022-08-02 18:36:33.000000000 +0000
@@ -3,6 +3,7 @@
 #define _IP_COMMON_H_
 
 #include <stdbool.h>
+#include <linux/mpls.h>
 
 #include "json_print.h"
 
@@ -53,11 +54,11 @@ int print_prefix(struct nlmsghdr *n, voi
 int print_rule(struct nlmsghdr *n, void *arg);
 int print_netconf(struct rtnl_ctrl_data *ctrl,
 		  struct nlmsghdr *n, void *arg);
-int print_nexthop(struct nlmsghdr *n, void *arg);
 int print_nexthop_bucket(struct nlmsghdr *n, void *arg);
 void netns_map_init(void);
 void netns_nsid_socket_init(void);
 int print_nsid(struct nlmsghdr *n, void *arg);
+int ipstats_print(struct nlmsghdr *n, void *arg);
 char *get_name_from_nsid(int nsid);
 int get_netnsid_from_name(const char *name);
 int set_netnsid_from_name(const char *name, int nsid);
@@ -91,6 +92,7 @@ int do_seg6(int argc, char **argv);
 int do_ipnh(int argc, char **argv);
 int do_mptcp(int argc, char **argv);
 int do_ioam6(int argc, char **argv);
+int do_ipstats(int argc, char **argv);
 
 int iplink_get(char *name, __u32 filt_mask);
 int iplink_ifla_xstats(int argc, char **argv);
@@ -140,9 +142,14 @@ int iplink_parse(int argc, char **argv,
 void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len);
 int bridge_parse_xstats(struct link_util *lu, int argc, char **argv);
 int bridge_print_xstats(struct nlmsghdr *n, void *arg);
+extern const struct ipstats_stat_desc ipstats_stat_desc_xstats_bridge_group;
+extern const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_bridge_group;
 
+/* iplink_bond.c */
 int bond_parse_xstats(struct link_util *lu, int argc, char **argv);
 int bond_print_xstats(struct nlmsghdr *n, void *arg);
+extern const struct ipstats_stat_desc ipstats_stat_desc_xstats_bond_group;
+extern const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_bond_group;
 
 /* iproute_lwtunnel.c */
 int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
@@ -158,6 +165,53 @@ void xdp_dump(FILE *fp, struct rtattr *t
 __u32 ipvrf_get_table(const char *name);
 int name_is_vrf(const char *name);
 
+/* ipstats.c */
+enum ipstats_stat_desc_kind {
+	IPSTATS_STAT_DESC_KIND_LEAF,
+	IPSTATS_STAT_DESC_KIND_GROUP,
+};
+
+struct ipstats_stat_dump_filters;
+struct ipstats_stat_show_attrs;
+
+struct ipstats_stat_desc {
+	const char *name;
+	enum ipstats_stat_desc_kind kind;
+	union {
+		struct {
+			const struct ipstats_stat_desc **subs;
+			size_t nsubs;
+		};
+		struct {
+			void (*pack)(struct ipstats_stat_dump_filters *filters,
+				     const struct ipstats_stat_desc *desc);
+			int (*show)(struct ipstats_stat_show_attrs *attrs,
+				    const struct ipstats_stat_desc *desc);
+		};
+	};
+};
+
+struct ipstats_stat_desc_xstats {
+	const struct ipstats_stat_desc desc;
+	int xstats_at;
+	int link_type_at;
+	int inner_max;
+	int inner_at;
+	void (*show_cb)(const struct rtattr *at);
+};
+
+void ipstats_stat_desc_pack_xstats(struct ipstats_stat_dump_filters *filters,
+				   const struct ipstats_stat_desc *desc);
+int ipstats_stat_desc_show_xstats(struct ipstats_stat_show_attrs *attrs,
+				  const struct ipstats_stat_desc *desc);
+
+#define IPSTATS_STAT_DESC_XSTATS_LEAF(NAME) {		\
+		.name = (NAME),				\
+		.kind = IPSTATS_STAT_DESC_KIND_LEAF,	\
+		.show = &ipstats_stat_desc_show_xstats,	\
+		.pack = &ipstats_stat_desc_pack_xstats,	\
+	}
+
 #ifndef	INFINITY_LIFE_TIME
 #define     INFINITY_LIFE_TIME      0xFFFFFFFFU
 #endif
@@ -168,7 +222,13 @@ int name_is_vrf(const char *name);
 
 void print_num(FILE *fp, unsigned int width, uint64_t count);
 void print_rt_flags(FILE *fp, unsigned int flags);
-void print_rta_if(FILE *fp, const struct rtattr *rta, const char *prefix);
+void print_rta_ifidx(FILE *fp, __u32 ifidx, const char *prefix);
+void __print_rta_gateway(FILE *fp, unsigned char family, const char *gateway);
 void print_rta_gateway(FILE *fp, unsigned char family,
 		       const struct rtattr *rta);
+void size_columns(unsigned int cols[], unsigned int n, ...);
+void print_stats64(FILE *fp, struct rtnl_link_stats64 *s,
+		   const struct rtattr *carrier_changes, const char *what);
+void print_mpls_link_stats(FILE *fp, const struct mpls_link_stats *stats,
+			   const char *indent);
 #endif /* _IP_COMMON_H_ */
diff -pruN 5.15.0-1/ip/ipl2tp.c 5.19.0-1/ip/ipl2tp.c
--- 5.15.0-1/ip/ipl2tp.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/ipl2tp.c	2022-08-02 18:36:33.000000000 +0000
@@ -191,8 +191,9 @@ static int delete_session(struct l2tp_pa
 	return 0;
 }
 
-static void print_cookie(const char *name, const char *fmt,
-			 const uint8_t *cookie, int len)
+static void __attribute__((format(printf, 2, 0)))
+print_cookie(const char *name, const char *fmt,
+	     const uint8_t *cookie, int len)
 {
 	char abuf[32];
 	size_t n;
@@ -257,7 +258,7 @@ static void print_tunnel(const struct l2
 					   NULL, p->udp6_csum_tx);
 
 				print_bool(PRINT_JSON, "checksum_rx",
-					   NULL, p->udp6_csum_tx);
+					   NULL, p->udp6_csum_rx);
 			} else {
 				printf("  UDP checksum: %s%s%s%s\n",
 				       p->udp6_csum_tx && p->udp6_csum_rx
diff -pruN 5.15.0-1/ip/iplink_amt.c 5.19.0-1/ip/iplink_amt.c
--- 5.15.0-1/ip/iplink_amt.c	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/ip/iplink_amt.c	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,200 @@
+/*
+ * iplink_amt.c	AMT device support
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Taehee Yoo <ap420073@gmail.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <net/if.h>
+#include <linux/ip.h>
+#include <linux/if_link.h>
+#include <arpa/inet.h>
+#include <linux/amt.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+#define AMT_ATTRSET(attrs, type) (((attrs) & (1L << (type))) != 0)
+
+static void print_usage(FILE *f)
+{
+	fprintf(f,
+		"Usage: ... amt\n"
+		"               [ discovery IP_ADDRESS ]\n"
+		"               [ mode MODE ]\n"
+		"               [ local ADDR ]\n"
+		"               [ dev PHYS_DEV ]\n"
+		"               [ relay_port PORT ]\n"
+		"               [ gateway_port PORT ]\n"
+		"               [ max_tunnels NUMBER ]\n"
+		"\n"
+		"Where: ADDR	:= { IP_ADDRESS }\n"
+		"       MODE	:= { gateway | relay }\n"
+		);
+}
+
+static char *modename[] = {"gateway", "relay"};
+
+static void usage(void)
+{
+	print_usage(stderr);
+}
+
+static void check_duparg(__u64 *attrs, int type, const char *key,
+		const char *argv)
+{
+	if (!AMT_ATTRSET(*attrs, type)) {
+		*attrs |= (1L << type);
+		return;
+	}
+	duparg2(key, argv);
+}
+
+static int amt_parse_opt(struct link_util *lu, int argc, char **argv,
+			 struct nlmsghdr *n)
+{
+	unsigned int mode, max_tunnels;
+	inet_prefix saddr, daddr;
+	__u64 attrs = 0;
+	__u16 port;
+
+	saddr.family = daddr.family = AF_UNSPEC;
+
+	inet_prefix_reset(&saddr);
+	inet_prefix_reset(&daddr);
+
+	while (argc > 0) {
+		if (strcmp(*argv, "mode") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "gateway") == 0) {
+				mode = 0;
+			} else if (strcmp(*argv, "relay") == 0) {
+				mode = 1;
+			} else {
+				usage();
+				return -1;
+			}
+			addattr32(n, 1024, IFLA_AMT_MODE, mode);
+		} else if (strcmp(*argv, "relay_port") == 0) {
+			NEXT_ARG();
+			if (get_u16(&port, *argv, 0))
+				invarg("relay_port", *argv);
+			addattr16(n, 1024, IFLA_AMT_RELAY_PORT, htons(port));
+		} else if (strcmp(*argv, "gateway_port") == 0) {
+			NEXT_ARG();
+			if (get_u16(&port, *argv, 0))
+				invarg("gateway_port", *argv);
+			addattr16(n, 1024, IFLA_AMT_GATEWAY_PORT, htons(port));
+		} else if (strcmp(*argv, "max_tunnels") == 0) {
+			NEXT_ARG();
+			if (get_u32(&max_tunnels, *argv, 0))
+				invarg("max_tunnels", *argv);
+			addattr32(n, 1024, IFLA_AMT_MAX_TUNNELS, max_tunnels);
+		} else if (strcmp(*argv, "dev") == 0) {
+			unsigned int link;
+
+			NEXT_ARG();
+			link = ll_name_to_index(*argv);
+			if (!link)
+				exit(nodev(*argv));
+			addattr32(n, 1024, IFLA_AMT_LINK, link);
+		} else if (strcmp(*argv, "local") == 0) {
+			NEXT_ARG();
+			check_duparg(&attrs, IFLA_AMT_LOCAL_IP, "local", *argv);
+			get_addr(&saddr, *argv, daddr.family);
+
+			if (is_addrtype_inet(&saddr))
+				addattr_l(n, 1024, IFLA_AMT_LOCAL_IP,
+					  saddr.data, saddr.bytelen);
+		} else if (strcmp(*argv, "discovery") == 0) {
+			NEXT_ARG();
+			check_duparg(&attrs, IFLA_AMT_DISCOVERY_IP,
+				     "discovery", *argv);
+			get_addr(&daddr, *argv, daddr.family);
+			if (is_addrtype_inet(&daddr))
+				addattr_l(n, 1024, IFLA_AMT_DISCOVERY_IP,
+					  daddr.data, daddr.bytelen);
+		} else if (strcmp(*argv, "help") == 0) {
+			usage();
+			return -1;
+		} else {
+			fprintf(stderr, "amt: unknown command \"%s\"?\n", *argv);
+			usage();
+			return -1;
+		}
+		argc--, argv++;
+	}
+
+	return 0;
+}
+
+static void amt_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+	if (!tb)
+		return;
+
+	if (tb[IFLA_AMT_MODE])
+		print_string(PRINT_ANY, "mode", "%s ",
+			     modename[rta_getattr_u32(tb[IFLA_AMT_MODE])]);
+
+	if (tb[IFLA_AMT_GATEWAY_PORT])
+		print_uint(PRINT_ANY, "gateway_port", "gateway_port %u ",
+			   rta_getattr_be16(tb[IFLA_AMT_GATEWAY_PORT]));
+
+	if (tb[IFLA_AMT_RELAY_PORT])
+		print_uint(PRINT_ANY, "relay_port", "relay_port %u ",
+			   rta_getattr_be16(tb[IFLA_AMT_RELAY_PORT]));
+
+	if (tb[IFLA_AMT_LOCAL_IP]) {
+		__be32 addr = rta_getattr_u32(tb[IFLA_AMT_LOCAL_IP]);
+
+		print_string(PRINT_ANY, "local", "local %s ",
+			     format_host(AF_INET, 4, &addr));
+	}
+
+	if (tb[IFLA_AMT_REMOTE_IP]) {
+		__be32 addr = rta_getattr_u32(tb[IFLA_AMT_REMOTE_IP]);
+
+		print_string(PRINT_ANY, "remote", "remote %s ",
+			     format_host(AF_INET, 4, &addr));
+	}
+
+	if (tb[IFLA_AMT_DISCOVERY_IP]) {
+		__be32 addr = rta_getattr_u32(tb[IFLA_AMT_DISCOVERY_IP]);
+
+		print_string(PRINT_ANY, "discovery", "discovery %s ",
+			     format_host(AF_INET, 4, &addr));
+	}
+
+	if (tb[IFLA_AMT_LINK]) {
+		unsigned int link = rta_getattr_u32(tb[IFLA_AMT_LINK]);
+
+		print_string(PRINT_ANY, "link", "dev %s ",
+			     ll_index_to_name(link));
+	}
+
+	if (tb[IFLA_AMT_MAX_TUNNELS])
+		print_uint(PRINT_ANY, "max_tunnels", "max_tunnels %u ",
+			   rta_getattr_u32(tb[IFLA_AMT_MAX_TUNNELS]));
+}
+
+static void amt_print_help(struct link_util *lu, int argc, char **argv, FILE *f)
+{
+	print_usage(f);
+}
+
+struct link_util amt_link_util = {
+	.id		= "amt",
+	.maxattr	= IFLA_AMT_MAX,
+	.parse_opt	= amt_parse_opt,
+	.print_opt	= amt_print_opt,
+	.print_help	= amt_print_help,
+};
diff -pruN 5.15.0-1/ip/iplink_batadv.c 5.19.0-1/ip/iplink_batadv.c
--- 5.15.0-1/ip/iplink_batadv.c	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/ip/iplink_batadv.c	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,64 @@
+/*
+ * iplink_batadv.c	Batman-adv support
+ *
+ * Authors:     Nicolas Escande <nico.escande@gmail.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <linux/batman_adv.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+static void print_explain(FILE *f)
+{
+	fprintf(f,
+		"Usage: ... batadv [ ra ROUTING_ALG ]\n"
+		"\n"
+		"Where: ROUTING_ALG := { BATMAN_IV | BATMAN_V }\n"
+	);
+}
+
+static void explain(void)
+{
+	print_explain(stderr);
+}
+
+static int batadv_parse_opt(struct link_util *lu, int argc, char **argv,
+			    struct nlmsghdr *n)
+{
+	while (argc > 0) {
+		if (matches(*argv, "ra") == 0) {
+			NEXT_ARG();
+			addattrstrz(n, 1024, IFLA_BATADV_ALGO_NAME, *argv);
+		} else if (matches(*argv, "help") == 0) {
+			explain();
+			return -1;
+		} else {
+			fprintf(stderr,
+				"batadv: unknown command \"%s\"?\n",
+				*argv);
+			explain();
+			return -1;
+		}
+		argc--, argv++;
+	}
+
+	return 0;
+}
+
+static void batadv_print_help(struct link_util *lu, int argc, char **argv,
+			      FILE *f)
+{
+	print_explain(f);
+}
+
+struct link_util batadv_link_util = {
+	.id		= "batadv",
+	.maxattr	= IFLA_BATADV_MAX,
+	.parse_opt	= batadv_parse_opt,
+	.print_help	= batadv_print_help,
+};
diff -pruN 5.15.0-1/ip/iplink_bond.c 5.19.0-1/ip/iplink_bond.c
--- 5.15.0-1/ip/iplink_bond.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/iplink_bond.c	2022-08-02 18:36:33.000000000 +0000
@@ -15,12 +15,14 @@
 #include <string.h>
 #include <linux/if_bonding.h>
 
+#include "list.h"
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
 #include "json_print.h"
 
 #define BOND_MAX_ARP_TARGETS    16
+#define BOND_MAX_NS_TARGETS     BOND_MAX_ARP_TARGETS
 
 static unsigned int xstats_print_attr;
 static int filter_index;
@@ -136,6 +138,7 @@ static void print_explain(FILE *f)
 		"                [ arp_validate ARP_VALIDATE ]\n"
 		"                [ arp_all_targets ARP_ALL_TARGETS ]\n"
 		"                [ arp_ip_target [ ARP_IP_TARGET, ... ] ]\n"
+		"                [ ns_ip6_target [ NS_IP6_TARGET, ... ] ]\n"
 		"                [ primary SLAVE_DEV ]\n"
 		"                [ primary_reselect PRIMARY_RESELECT ]\n"
 		"                [ fail_over_mac FAIL_OVER_MAC ]\n"
@@ -153,6 +156,7 @@ static void print_explain(FILE *f)
 		"                [ ad_user_port_key PORTKEY ]\n"
 		"                [ ad_actor_sys_prio SYSPRIO ]\n"
 		"                [ ad_actor_system LLADDR ]\n"
+		"                [ arp_missed_max MISSED_MAX ]\n"
 		"\n"
 		"BONDMODE := balance-rr|active-backup|balance-xor|broadcast|802.3ad|balance-tlb|balance-alb\n"
 		"ARP_VALIDATE := none|active|backup|all|filter|filter_active|filter_backup\n"
@@ -181,6 +185,7 @@ static int bond_parse_opt(struct link_ut
 	__u32 miimon, updelay, downdelay, peer_notify_delay, arp_interval, arp_validate;
 	__u32 arp_all_targets, resend_igmp, min_links, lp_interval;
 	__u32 packets_per_slave;
+	__u8 missed_max;
 	unsigned int ifindex;
 
 	while (argc > 0) {
@@ -246,6 +251,25 @@ static int bond_parse_opt(struct link_ut
 				addattr_nest_end(n, nest);
 			}
 			addattr_nest_end(n, nest);
+		} else if (strcmp(*argv, "ns_ip6_target") == 0) {
+			struct rtattr *nest = addattr_nest(n, 1024,
+				IFLA_BOND_NS_IP6_TARGET);
+			if (NEXT_ARG_OK()) {
+				NEXT_ARG();
+				char *targets = strdupa(*argv);
+				char *target = strtok(targets, ",");
+				int i;
+
+				for (i = 0; target && i < BOND_MAX_NS_TARGETS; i++) {
+					inet_prefix ip6_addr;
+
+					get_addr(&ip6_addr, target, AF_INET6);
+					addattr_l(n, 1024, i, ip6_addr.data, sizeof(struct in6_addr));
+					target = strtok(NULL, ",");
+				}
+				addattr_nest_end(n, nest);
+			}
+			addattr_nest_end(n, nest);
 		} else if (matches(*argv, "arp_validate") == 0) {
 			NEXT_ARG();
 			if (get_index(arp_validate_tbl, *argv) < 0)
@@ -258,6 +282,12 @@ static int bond_parse_opt(struct link_ut
 				invarg("invalid arp_all_targets", *argv);
 			arp_all_targets = get_index(arp_all_targets_tbl, *argv);
 			addattr32(n, 1024, IFLA_BOND_ARP_ALL_TARGETS, arp_all_targets);
+		} else if (strcmp(*argv, "arp_missed_max") == 0) {
+			NEXT_ARG();
+			if (get_u8(&missed_max, *argv, 0))
+				invarg("invalid arp_missed_max", *argv);
+
+			addattr8(n, 1024, IFLA_BOND_MISSED_MAX, missed_max);
 		} else if (matches(*argv, "primary") == 0) {
 			NEXT_ARG();
 			ifindex = ll_name_to_index(*argv);
@@ -396,6 +426,8 @@ static int bond_parse_opt(struct link_ut
 
 static void bond_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
+	int i;
+
 	if (!tb)
 		return;
 
@@ -453,9 +485,14 @@ static void bond_print_opt(struct link_u
 			   "arp_interval %u ",
 			   rta_getattr_u32(tb[IFLA_BOND_ARP_INTERVAL]));
 
+	if (tb[IFLA_BOND_MISSED_MAX])
+		print_uint(PRINT_ANY,
+			   "arp_missed_max",
+			   "arp_missed_max %u ",
+			   rta_getattr_u8(tb[IFLA_BOND_MISSED_MAX]));
+
 	if (tb[IFLA_BOND_ARP_IP_TARGET]) {
 		struct rtattr *iptb[BOND_MAX_ARP_TARGETS + 1];
-		int i;
 
 		parse_rtattr_nested(iptb, BOND_MAX_ARP_TARGETS,
 				    tb[IFLA_BOND_ARP_IP_TARGET]);
@@ -483,6 +520,35 @@ static void bond_print_opt(struct link_u
 		}
 	}
 
+	if (tb[IFLA_BOND_NS_IP6_TARGET]) {
+		struct rtattr *ip6tb[BOND_MAX_NS_TARGETS + 1];
+
+		parse_rtattr_nested(ip6tb, BOND_MAX_NS_TARGETS,
+				    tb[IFLA_BOND_NS_IP6_TARGET]);
+
+		if (ip6tb[0]) {
+			open_json_array(PRINT_JSON, "ns_ip6_target");
+			print_string(PRINT_FP, NULL, "ns_ip6_target ", NULL);
+		}
+
+		for (i = 0; i < BOND_MAX_NS_TARGETS; i++) {
+			if (ip6tb[i])
+				print_string(PRINT_ANY,
+					     NULL,
+					     "%s",
+					     rt_addr_n2a_rta(AF_INET6, ip6tb[i]));
+			if (!is_json_context()
+			    && i < BOND_MAX_NS_TARGETS-1
+			    && ip6tb[i+1])
+				fprintf(f, ",");
+		}
+
+		if (ip6tb[0]) {
+			print_string(PRINT_FP, NULL, " ", NULL);
+			close_json_array(PRINT_JSON, NULL);
+		}
+	}
+
 	if (tb[IFLA_BOND_ARP_VALIDATE]) {
 		__u32 arp_v = rta_getattr_u32(tb[IFLA_BOND_ARP_VALIDATE]);
 		const char *arp_validate = get_name(arp_validate_tbl, arp_v);
@@ -696,7 +762,7 @@ static void bond_print_xstats_help(struc
 	fprintf(f, "Usage: ... %s [ 802.3ad ] [ dev DEVICE ]\n", lu->id);
 }
 
-static void bond_print_3ad_stats(struct rtattr *lacpattr)
+static void bond_print_3ad_stats(const struct rtattr *lacpattr)
 {
 	struct rtattr *lacptb[BOND_3AD_STAT_MAX+1];
 	__u64 val;
@@ -847,7 +913,6 @@ int bond_parse_xstats(struct link_util *
 	return 0;
 }
 
-
 struct link_util bond_link_util = {
 	.id		= "bond",
 	.maxattr	= IFLA_BOND_MAX,
@@ -857,3 +922,47 @@ struct link_util bond_link_util = {
 	.parse_ifla_xstats = bond_parse_xstats,
 	.print_ifla_xstats = bond_print_xstats,
 };
+
+static const struct ipstats_stat_desc_xstats
+ipstats_stat_desc_xstats_bond_lacp = {
+	.desc = IPSTATS_STAT_DESC_XSTATS_LEAF("802.3ad"),
+	.xstats_at = IFLA_STATS_LINK_XSTATS,
+	.link_type_at = LINK_XSTATS_TYPE_BOND,
+	.inner_max = BOND_XSTATS_MAX,
+	.inner_at = BOND_XSTATS_3AD,
+	.show_cb = &bond_print_3ad_stats,
+};
+
+static const struct ipstats_stat_desc *
+ipstats_stat_desc_xstats_bond_subs[] = {
+	&ipstats_stat_desc_xstats_bond_lacp.desc,
+};
+
+const struct ipstats_stat_desc ipstats_stat_desc_xstats_bond_group = {
+	.name = "bond",
+	.kind = IPSTATS_STAT_DESC_KIND_GROUP,
+	.subs = ipstats_stat_desc_xstats_bond_subs,
+	.nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_bond_subs),
+};
+
+static const struct ipstats_stat_desc_xstats
+ipstats_stat_desc_xstats_slave_bond_lacp = {
+	.desc = IPSTATS_STAT_DESC_XSTATS_LEAF("802.3ad"),
+	.xstats_at = IFLA_STATS_LINK_XSTATS_SLAVE,
+	.link_type_at = LINK_XSTATS_TYPE_BOND,
+	.inner_max = BOND_XSTATS_MAX,
+	.inner_at = BOND_XSTATS_3AD,
+	.show_cb = &bond_print_3ad_stats,
+};
+
+static const struct ipstats_stat_desc *
+ipstats_stat_desc_xstats_slave_bond_subs[] = {
+	&ipstats_stat_desc_xstats_slave_bond_lacp.desc,
+};
+
+const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_bond_group = {
+	.name = "bond",
+	.kind = IPSTATS_STAT_DESC_KIND_GROUP,
+	.subs = ipstats_stat_desc_xstats_slave_bond_subs,
+	.nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_slave_bond_subs),
+};
diff -pruN 5.15.0-1/ip/iplink_bridge.c 5.19.0-1/ip/iplink_bridge.c
--- 5.15.0-1/ip/iplink_bridge.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/iplink_bridge.c	2022-08-02 18:36:33.000000000 +0000
@@ -43,6 +43,7 @@ static void print_explain(FILE *f)
 		"		  [ vlan_stats_enabled VLAN_STATS_ENABLED ]\n"
 		"		  [ vlan_stats_per_port VLAN_STATS_PER_PORT ]\n"
 		"		  [ mcast_snooping MULTICAST_SNOOPING ]\n"
+		"		  [ mcast_vlan_snooping MULTICAST_VLAN_SNOOPING ]\n"
 		"		  [ mcast_router MULTICAST_ROUTER ]\n"
 		"		  [ mcast_query_use_ifaddr MCAST_QUERY_USE_IFADDR ]\n"
 		"		  [ mcast_querier MULTICAST_QUERIER ]\n"
@@ -83,6 +84,7 @@ void br_dump_bridge_id(const struct ifla
 static int bridge_parse_opt(struct link_util *lu, int argc, char **argv,
 			    struct nlmsghdr *n)
 {
+	struct br_boolopt_multi bm = {};
 	__u32 val;
 
 	while (argc > 0) {
@@ -200,6 +202,18 @@ static int bridge_parse_opt(struct link_
 				invarg("invalid mcast_snooping", *argv);
 
 			addattr8(n, 1024, IFLA_BR_MCAST_SNOOPING, mcast_snoop);
+		} else if (strcmp(*argv, "mcast_vlan_snooping") == 0) {
+			__u32 mcvl_bit = 1 << BR_BOOLOPT_MCAST_VLAN_SNOOPING;
+			__u8 mcast_vlan_snooping;
+
+			NEXT_ARG();
+			if (get_u8(&mcast_vlan_snooping, *argv, 0))
+				invarg("invalid mcast_vlan_snooping", *argv);
+			bm.optmask |= 1 << BR_BOOLOPT_MCAST_VLAN_SNOOPING;
+			if (mcast_vlan_snooping)
+				bm.optval |= mcvl_bit;
+			else
+				bm.optval &= ~mcvl_bit;
 		} else if (matches(*argv, "mcast_query_use_ifaddr") == 0) {
 			__u8 mcast_qui;
 
@@ -379,6 +393,9 @@ static int bridge_parse_opt(struct link_
 		argc--, argv++;
 	}
 
+	if (bm.optmask)
+		addattr_l(n, 1024, IFLA_BR_MULTI_BOOLOPT,
+			  &bm, sizeof(bm));
 	return 0;
 }
 
@@ -559,6 +576,18 @@ static void bridge_print_opt(struct link
 			   "mcast_snooping %u ",
 			   rta_getattr_u8(tb[IFLA_BR_MCAST_SNOOPING]));
 
+	if (tb[IFLA_BR_MULTI_BOOLOPT]) {
+		__u32 mcvl_bit = 1 << BR_BOOLOPT_MCAST_VLAN_SNOOPING;
+		struct br_boolopt_multi *bm;
+
+		bm = RTA_DATA(tb[IFLA_BR_MULTI_BOOLOPT]);
+		if (bm->optmask & mcvl_bit)
+			print_uint(PRINT_ANY,
+				   "mcast_vlan_snooping",
+				   "mcast_vlan_snooping %u ",
+				    !!(bm->optval & mcvl_bit));
+	}
+
 	if (tb[IFLA_BR_MCAST_ROUTER])
 		print_uint(PRINT_ANY,
 			   "mcast_router",
@@ -685,11 +714,140 @@ static void bridge_print_xstats_help(str
 	fprintf(f, "Usage: ... %s [ igmp ] [ dev DEVICE ]\n", lu->id);
 }
 
+static void bridge_print_stats_mcast(const struct rtattr *attr)
+{
+	struct br_mcast_stats *mstats;
+
+	mstats = RTA_DATA(attr);
+	open_json_object("multicast");
+	open_json_object("igmp_queries");
+	print_string(PRINT_FP, NULL,
+		     "%-16s    IGMP queries:\n", "");
+	print_string(PRINT_FP, NULL, "%-16s      ", "");
+	print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
+		  mstats->igmp_v1queries[BR_MCAST_DIR_RX]);
+	print_u64(PRINT_ANY, "rx_v2", "v2 %llu ",
+		  mstats->igmp_v2queries[BR_MCAST_DIR_RX]);
+	print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n",
+		  mstats->igmp_v3queries[BR_MCAST_DIR_RX]);
+	print_string(PRINT_FP, NULL, "%-16s      ", "");
+	print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
+		  mstats->igmp_v1queries[BR_MCAST_DIR_TX]);
+	print_u64(PRINT_ANY, "tx_v2", "v2 %llu ",
+		  mstats->igmp_v2queries[BR_MCAST_DIR_TX]);
+	print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n",
+		  mstats->igmp_v3queries[BR_MCAST_DIR_TX]);
+	close_json_object();
+
+	open_json_object("igmp_reports");
+	print_string(PRINT_FP, NULL,
+		     "%-16s    IGMP reports:\n", "");
+	print_string(PRINT_FP, NULL, "%-16s      ", "");
+	print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
+		  mstats->igmp_v1reports[BR_MCAST_DIR_RX]);
+	print_u64(PRINT_ANY, "rx_v2", "v2 %llu ",
+		  mstats->igmp_v2reports[BR_MCAST_DIR_RX]);
+	print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n",
+		  mstats->igmp_v3reports[BR_MCAST_DIR_RX]);
+	print_string(PRINT_FP, NULL, "%-16s      ", "");
+	print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
+		  mstats->igmp_v1reports[BR_MCAST_DIR_TX]);
+	print_u64(PRINT_ANY, "tx_v2", "v2 %llu ",
+		  mstats->igmp_v2reports[BR_MCAST_DIR_TX]);
+	print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n",
+		  mstats->igmp_v3reports[BR_MCAST_DIR_TX]);
+	close_json_object();
+
+	open_json_object("igmp_leaves");
+	print_string(PRINT_FP, NULL,
+		     "%-16s    IGMP leaves: ", "");
+	print_u64(PRINT_ANY, "rx", "RX: %llu ",
+		  mstats->igmp_leaves[BR_MCAST_DIR_RX]);
+	print_u64(PRINT_ANY, "tx", "TX: %llu\n",
+		  mstats->igmp_leaves[BR_MCAST_DIR_TX]);
+	close_json_object();
+
+	print_string(PRINT_FP, NULL,
+		     "%-16s    IGMP parse errors: ", "");
+	print_u64(PRINT_ANY, "igmp_parse_errors", "%llu\n",
+		  mstats->igmp_parse_errors);
+
+	open_json_object("mld_queries");
+	print_string(PRINT_FP, NULL,
+		     "%-16s    MLD queries:\n", "");
+	print_string(PRINT_FP, NULL, "%-16s      ", "");
+	print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
+		  mstats->mld_v1queries[BR_MCAST_DIR_RX]);
+	print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n",
+		  mstats->mld_v2queries[BR_MCAST_DIR_RX]);
+	print_string(PRINT_FP, NULL, "%-16s      ", "");
+	print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
+		  mstats->mld_v1queries[BR_MCAST_DIR_TX]);
+	print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n",
+		  mstats->mld_v2queries[BR_MCAST_DIR_TX]);
+	close_json_object();
+
+	open_json_object("mld_reports");
+	print_string(PRINT_FP, NULL,
+		     "%-16s    MLD reports:\n", "");
+	print_string(PRINT_FP, NULL, "%-16s      ", "");
+	print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
+		  mstats->mld_v1reports[BR_MCAST_DIR_RX]);
+	print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n",
+		  mstats->mld_v2reports[BR_MCAST_DIR_RX]);
+	print_string(PRINT_FP, NULL, "%-16s      ", "");
+	print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
+		  mstats->mld_v1reports[BR_MCAST_DIR_TX]);
+	print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n",
+		  mstats->mld_v2reports[BR_MCAST_DIR_TX]);
+	close_json_object();
+
+	open_json_object("mld_leaves");
+	print_string(PRINT_FP, NULL,
+		     "%-16s    MLD leaves: ", "");
+	print_u64(PRINT_ANY, "rx", "RX: %llu ",
+		  mstats->mld_leaves[BR_MCAST_DIR_RX]);
+	print_u64(PRINT_ANY, "tx", "TX: %llu\n",
+		  mstats->mld_leaves[BR_MCAST_DIR_TX]);
+	close_json_object();
+
+	print_string(PRINT_FP, NULL,
+		     "%-16s    MLD parse errors: ", "");
+	print_u64(PRINT_ANY, "mld_parse_errors", "%llu\n",
+		  mstats->mld_parse_errors);
+	close_json_object();
+}
+
+static void bridge_print_stats_stp(const struct rtattr *attr)
+{
+	struct bridge_stp_xstats *sstats;
+
+	sstats = RTA_DATA(attr);
+	open_json_object("stp");
+	print_string(PRINT_FP, NULL,
+		     "%-16s    STP BPDU:  ", "");
+	print_u64(PRINT_ANY, "rx_bpdu", "RX: %llu ",
+		  sstats->rx_bpdu);
+	print_u64(PRINT_ANY, "tx_bpdu", "TX: %llu\n",
+		  sstats->tx_bpdu);
+	print_string(PRINT_FP, NULL,
+		     "%-16s    STP TCN:   ", "");
+	print_u64(PRINT_ANY, "rx_tcn", "RX: %llu ",
+		  sstats->rx_tcn);
+	print_u64(PRINT_ANY, "tx_tcn", "TX: %llu\n",
+		  sstats->tx_tcn);
+	print_string(PRINT_FP, NULL,
+		     "%-16s    STP Transitions: ", "");
+	print_u64(PRINT_ANY, "transition_blk", "Blocked: %llu ",
+		  sstats->transition_blk);
+	print_u64(PRINT_ANY, "transition_fwd", "Forwarding: %llu\n",
+		  sstats->transition_fwd);
+	close_json_object();
+}
+
 static void bridge_print_stats_attr(struct rtattr *attr, int ifindex)
 {
 	struct rtattr *brtb[LINK_XSTATS_TYPE_MAX+1];
-	struct bridge_stp_xstats *sstats;
-	struct br_mcast_stats *mstats;
 	struct rtattr *i, *list;
 	const char *ifname = "";
 	int rem;
@@ -709,127 +867,10 @@ static void bridge_print_stats_attr(stru
 			continue;
 		switch (i->rta_type) {
 		case BRIDGE_XSTATS_MCAST:
-			mstats = RTA_DATA(i);
-			open_json_object("multicast");
-			open_json_object("igmp_queries");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    IGMP queries:\n", "");
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
-				  mstats->igmp_v1queries[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "rx_v2", "v2 %llu ",
-				  mstats->igmp_v2queries[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n",
-				  mstats->igmp_v3queries[BR_MCAST_DIR_RX]);
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
-				  mstats->igmp_v1queries[BR_MCAST_DIR_TX]);
-			print_u64(PRINT_ANY, "tx_v2", "v2 %llu ",
-				  mstats->igmp_v2queries[BR_MCAST_DIR_TX]);
-			print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n",
-				  mstats->igmp_v3queries[BR_MCAST_DIR_TX]);
-			close_json_object();
-
-			open_json_object("igmp_reports");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    IGMP reports:\n", "");
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
-				  mstats->igmp_v1reports[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "rx_v2", "v2 %llu ",
-				  mstats->igmp_v2reports[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "rx_v3", "v3 %llu\n",
-				  mstats->igmp_v3reports[BR_MCAST_DIR_RX]);
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
-				  mstats->igmp_v1reports[BR_MCAST_DIR_TX]);
-			print_u64(PRINT_ANY, "tx_v2", "v2 %llu ",
-				  mstats->igmp_v2reports[BR_MCAST_DIR_TX]);
-			print_u64(PRINT_ANY, "tx_v3", "v3 %llu\n",
-				  mstats->igmp_v3reports[BR_MCAST_DIR_TX]);
-			close_json_object();
-
-			open_json_object("igmp_leaves");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    IGMP leaves: ", "");
-			print_u64(PRINT_ANY, "rx", "RX: %llu ",
-				  mstats->igmp_leaves[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "tx", "TX: %llu\n",
-				  mstats->igmp_leaves[BR_MCAST_DIR_TX]);
-			close_json_object();
-
-			print_string(PRINT_FP, NULL,
-				     "%-16s    IGMP parse errors: ", "");
-			print_u64(PRINT_ANY, "igmp_parse_errors", "%llu\n",
-				  mstats->igmp_parse_errors);
-
-			open_json_object("mld_queries");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    MLD queries:\n", "");
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
-				  mstats->mld_v1queries[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n",
-				  mstats->mld_v2queries[BR_MCAST_DIR_RX]);
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
-				  mstats->mld_v1queries[BR_MCAST_DIR_TX]);
-			print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n",
-				  mstats->mld_v2queries[BR_MCAST_DIR_TX]);
-			close_json_object();
-
-			open_json_object("mld_reports");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    MLD reports:\n", "");
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "rx_v1", "RX: v1 %llu ",
-				  mstats->mld_v1reports[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "rx_v2", "v2 %llu\n",
-				  mstats->mld_v2reports[BR_MCAST_DIR_RX]);
-			print_string(PRINT_FP, NULL, "%-16s      ", "");
-			print_u64(PRINT_ANY, "tx_v1", "TX: v1 %llu ",
-				  mstats->mld_v1reports[BR_MCAST_DIR_TX]);
-			print_u64(PRINT_ANY, "tx_v2", "v2 %llu\n",
-				  mstats->mld_v2reports[BR_MCAST_DIR_TX]);
-			close_json_object();
-
-			open_json_object("mld_leaves");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    MLD leaves: ", "");
-			print_u64(PRINT_ANY, "rx", "RX: %llu ",
-				  mstats->mld_leaves[BR_MCAST_DIR_RX]);
-			print_u64(PRINT_ANY, "tx", "TX: %llu\n",
-				  mstats->mld_leaves[BR_MCAST_DIR_TX]);
-			close_json_object();
-
-			print_string(PRINT_FP, NULL,
-				     "%-16s    MLD parse errors: ", "");
-			print_u64(PRINT_ANY, "mld_parse_errors", "%llu\n",
-				  mstats->mld_parse_errors);
-			close_json_object();
+			bridge_print_stats_mcast(i);
 			break;
 		case BRIDGE_XSTATS_STP:
-			sstats = RTA_DATA(i);
-			open_json_object("stp");
-			print_string(PRINT_FP, NULL,
-				     "%-16s    STP BPDU:  ", "");
-			print_u64(PRINT_ANY, "rx_bpdu", "RX: %llu ",
-				  sstats->rx_bpdu);
-			print_u64(PRINT_ANY, "tx_bpdu", "TX: %llu\n",
-				  sstats->tx_bpdu);
-			print_string(PRINT_FP, NULL,
-				     "%-16s    STP TCN:   ", "");
-			print_u64(PRINT_ANY, "rx_tcn", "RX: %llu ",
-				  sstats->rx_tcn);
-			print_u64(PRINT_ANY, "tx_tcn", "TX: %llu\n",
-				  sstats->tx_tcn);
-			print_string(PRINT_FP, NULL,
-				     "%-16s    STP Transitions: ", "");
-			print_u64(PRINT_ANY, "transition_blk", "Blocked: %llu ",
-				  sstats->transition_blk);
-			print_u64(PRINT_ANY, "transition_fwd", "Forwarding: %llu\n",
-				  sstats->transition_fwd);
-			close_json_object();
+			bridge_print_stats_stp(i);
 			break;
 		}
 	}
@@ -895,3 +936,69 @@ struct link_util bridge_link_util = {
 	.parse_ifla_xstats = bridge_parse_xstats,
 	.print_ifla_xstats = bridge_print_xstats,
 };
+
+static const struct ipstats_stat_desc_xstats
+ipstats_stat_desc_xstats_bridge_stp = {
+	.desc = IPSTATS_STAT_DESC_XSTATS_LEAF("stp"),
+	.xstats_at = IFLA_STATS_LINK_XSTATS,
+	.link_type_at = LINK_XSTATS_TYPE_BRIDGE,
+	.inner_max = BRIDGE_XSTATS_MAX,
+	.inner_at = BRIDGE_XSTATS_STP,
+	.show_cb = &bridge_print_stats_stp,
+};
+
+static const struct ipstats_stat_desc_xstats
+ipstats_stat_desc_xstats_bridge_mcast = {
+	.desc = IPSTATS_STAT_DESC_XSTATS_LEAF("mcast"),
+	.xstats_at = IFLA_STATS_LINK_XSTATS,
+	.link_type_at = LINK_XSTATS_TYPE_BRIDGE,
+	.inner_max = BRIDGE_XSTATS_MAX,
+	.inner_at = BRIDGE_XSTATS_MCAST,
+	.show_cb = &bridge_print_stats_mcast,
+};
+
+static const struct ipstats_stat_desc *
+ipstats_stat_desc_xstats_bridge_subs[] = {
+	&ipstats_stat_desc_xstats_bridge_stp.desc,
+	&ipstats_stat_desc_xstats_bridge_mcast.desc,
+};
+
+const struct ipstats_stat_desc ipstats_stat_desc_xstats_bridge_group = {
+	.name = "bridge",
+	.kind = IPSTATS_STAT_DESC_KIND_GROUP,
+	.subs = ipstats_stat_desc_xstats_bridge_subs,
+	.nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_bridge_subs),
+};
+
+static const struct ipstats_stat_desc_xstats
+ipstats_stat_desc_xstats_slave_bridge_stp = {
+	.desc = IPSTATS_STAT_DESC_XSTATS_LEAF("stp"),
+	.xstats_at = IFLA_STATS_LINK_XSTATS_SLAVE,
+	.link_type_at = LINK_XSTATS_TYPE_BRIDGE,
+	.inner_max = BRIDGE_XSTATS_MAX,
+	.inner_at = BRIDGE_XSTATS_STP,
+	.show_cb = &bridge_print_stats_stp,
+};
+
+static const struct ipstats_stat_desc_xstats
+ipstats_stat_desc_xstats_slave_bridge_mcast = {
+	.desc = IPSTATS_STAT_DESC_XSTATS_LEAF("mcast"),
+	.xstats_at = IFLA_STATS_LINK_XSTATS_SLAVE,
+	.link_type_at = LINK_XSTATS_TYPE_BRIDGE,
+	.inner_max = BRIDGE_XSTATS_MAX,
+	.inner_at = BRIDGE_XSTATS_MCAST,
+	.show_cb = &bridge_print_stats_mcast,
+};
+
+static const struct ipstats_stat_desc *
+ipstats_stat_desc_xstats_slave_bridge_subs[] = {
+	&ipstats_stat_desc_xstats_slave_bridge_stp.desc,
+	&ipstats_stat_desc_xstats_slave_bridge_mcast.desc,
+};
+
+const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_bridge_group = {
+	.name = "bridge",
+	.kind = IPSTATS_STAT_DESC_KIND_GROUP,
+	.subs = ipstats_stat_desc_xstats_slave_bridge_subs,
+	.nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_slave_bridge_subs),
+};
diff -pruN 5.15.0-1/ip/iplink_bridge_slave.c 5.19.0-1/ip/iplink_bridge_slave.c
--- 5.15.0-1/ip/iplink_bridge_slave.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/iplink_bridge_slave.c	2022-08-02 18:36:33.000000000 +0000
@@ -37,11 +37,13 @@ static void print_explain(FILE *f)
 		"			[ mcast_router MULTICAST_ROUTER ]\n"
 		"			[ mcast_fast_leave {on | off} ]\n"
 		"			[ mcast_flood {on | off} ]\n"
+		"			[ bcast_flood {on | off} ]\n"
 		"			[ mcast_to_unicast {on | off} ]\n"
 		"			[ group_fwd_mask MASK ]\n"
 		"			[ neigh_suppress {on | off} ]\n"
 		"			[ vlan_tunnel {on | off} ]\n"
 		"			[ isolated {on | off} ]\n"
+		"			[ locked {on | off} ]\n"
 		"			[ backup_port DEVICE ] [ nobackup_port ]\n"
 	);
 }
@@ -250,6 +252,10 @@ static void bridge_slave_print_opt(struc
 		print_on_off(PRINT_ANY, "mcast_flood", "mcast_flood %s ",
 			     rta_getattr_u8(tb[IFLA_BRPORT_MCAST_FLOOD]));
 
+	if (tb[IFLA_BRPORT_BCAST_FLOOD])
+		print_on_off(PRINT_ANY, "bcast_flood", "bcast_flood %s ",
+			     rta_getattr_u8(tb[IFLA_BRPORT_BCAST_FLOOD]));
+
 	if (tb[IFLA_BRPORT_MCAST_TO_UCAST])
 		print_on_off(PRINT_ANY, "mcast_to_unicast", "mcast_to_unicast %s ",
 			     rta_getattr_u8(tb[IFLA_BRPORT_MCAST_TO_UCAST]));
@@ -278,6 +284,10 @@ static void bridge_slave_print_opt(struc
 		print_on_off(PRINT_ANY, "isolated", "isolated %s ",
 			     rta_getattr_u8(tb[IFLA_BRPORT_ISOLATED]));
 
+	if (tb[IFLA_BRPORT_LOCKED])
+		print_on_off(PRINT_ANY, "locked", "locked %s ",
+			     rta_getattr_u8(tb[IFLA_BRPORT_LOCKED]));
+
 	if (tb[IFLA_BRPORT_BACKUP_PORT]) {
 		int backup_p = rta_getattr_u32(tb[IFLA_BRPORT_BACKUP_PORT]);
 
@@ -350,6 +360,10 @@ static int bridge_slave_parse_opt(struct
 			NEXT_ARG();
 			bridge_slave_parse_on_off("mcast_flood", *argv, n,
 						  IFLA_BRPORT_MCAST_FLOOD);
+		} else if (matches(*argv, "bcast_flood") == 0) {
+			NEXT_ARG();
+			bridge_slave_parse_on_off("bcast_flood", *argv, n,
+						  IFLA_BRPORT_BCAST_FLOOD);
 		} else if (matches(*argv, "mcast_to_unicast") == 0) {
 			NEXT_ARG();
 			bridge_slave_parse_on_off("mcast_to_unicast", *argv, n,
@@ -393,6 +407,10 @@ static int bridge_slave_parse_opt(struct
 			NEXT_ARG();
 			bridge_slave_parse_on_off("isolated", *argv, n,
 						  IFLA_BRPORT_ISOLATED);
+		} else if (matches(*argv, "locked") == 0) {
+			NEXT_ARG();
+			bridge_slave_parse_on_off("locked", *argv, n,
+						  IFLA_BRPORT_LOCKED);
 		} else if (matches(*argv, "backup_port") == 0) {
 			int ifindex;
 
diff -pruN 5.15.0-1/ip/iplink.c 5.19.0-1/ip/iplink.c
--- 5.15.0-1/ip/iplink.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/iplink.c	2022-08-02 18:36:33.000000000 +0000
@@ -35,9 +35,6 @@
 
 #define IPLINK_IOCTL_COMPAT	1
 
-#ifndef GSO_MAX_SIZE
-#define GSO_MAX_SIZE		65536
-#endif
 #ifndef GSO_MAX_SEGS
 #define GSO_MAX_SEGS		65535
 #endif
@@ -50,14 +47,14 @@ void iplink_types_usage(void)
 {
 	/* Remember to add new entry here if new type is added. */
 	fprintf(stderr,
-		"TYPE := { bareudp | bond | bond_slave | bridge | bridge_slave |\n"
-		"          dummy | erspan | geneve | gre | gretap | ifb |\n"
+		"TYPE := { amt | bareudp | bond | bond_slave | bridge | bridge_slave |\n"
+		"          dummy | erspan | geneve | gre | gretap | gtp | ifb |\n"
 		"          ip6erspan | ip6gre | ip6gretap | ip6tnl |\n"
 		"          ipip | ipoib | ipvlan | ipvtap |\n"
 		"          macsec | macvlan | macvtap |\n"
 		"          netdevsim | nlmon | rmnet | sit | team | team_slave |\n"
 		"          vcan | veth | vlan | vrf | vti | vxcan | vxlan | wwan |\n"
-		"          xfrm }\n");
+		"          xfrm | virt_wifi }\n");
 }
 
 void iplink_usage(void)
@@ -118,8 +115,10 @@ void iplink_usage(void)
 		"		[ protodown { on | off } ]\n"
 		"		[ protodown_reason PREASON { on | off } ]\n"
 		"		[ gso_max_size BYTES ] | [ gso_max_segs PACKETS ]\n"
+		"		[ gro_max_size BYTES ]\n"
 		"\n"
 		"	ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE]\n"
+		"		[nomaster]\n"
 		"\n"
 		"	ip link xstats type TYPE [ ARGS ]\n"
 		"\n"
@@ -578,6 +577,7 @@ static int iplink_parse_vf(int vf, int *
 
 int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
 {
+	bool move_netns = false;
 	char *name = NULL;
 	char *dev = NULL;
 	char *link = NULL;
@@ -683,6 +683,7 @@ int iplink_parse(int argc, char **argv,
 					  IFLA_NET_NS_PID, &netns, 4);
 			else
 				invarg("Invalid \"netns\" value\n", *argv);
+			move_netns = true;
 		} else if (strcmp(*argv, "multicast") == 0) {
 			NEXT_ARG();
 			req->i.ifi_change |= IFF_MULTICAST;
@@ -923,8 +924,7 @@ int iplink_parse(int argc, char **argv,
 			unsigned int max_size;
 
 			NEXT_ARG();
-			if (get_unsigned(&max_size, *argv, 0) ||
-			    max_size > GSO_MAX_SIZE)
+			if (get_unsigned(&max_size, *argv, 0))
 				invarg("Invalid \"gso_max_size\" value\n",
 				       *argv);
 			addattr32(&req->n, sizeof(*req),
@@ -939,6 +939,15 @@ int iplink_parse(int argc, char **argv,
 				       *argv);
 			addattr32(&req->n, sizeof(*req),
 				  IFLA_GSO_MAX_SEGS, max_segs);
+		}  else if (strcmp(*argv, "gro_max_size") == 0) {
+			unsigned int max_size;
+
+			NEXT_ARG();
+			if (get_unsigned(&max_size, *argv, 0))
+				invarg("Invalid \"gro_max_size\" value\n",
+				       *argv);
+			addattr32(&req->n, sizeof(*req),
+				  IFLA_GRO_MAX_SIZE, max_size);
 		} else if (strcmp(*argv, "parentdev") == 0) {
 			NEXT_ARG();
 			addattr_l(&req->n, sizeof(*req), IFLA_PARENT_DEV_NAME,
@@ -980,9 +989,11 @@ int iplink_parse(int argc, char **argv,
 		}
 	}
 
-	if (!(req->n.nlmsg_flags & NLM_F_CREATE) && index) {
+	if (index &&
+	    (!(req->n.nlmsg_flags & NLM_F_CREATE) &&
+	     !move_netns)) {
 		fprintf(stderr,
-			"index can be used only when creating devices.\n");
+			"index can be used only when creating devices or when moving device to another netns.\n");
 		exit(-1);
 	}
 
@@ -1019,6 +1030,9 @@ int iplink_parse(int argc, char **argv,
 		/* Not renaming to the same name */
 		if (name == dev)
 			name = NULL;
+
+		if (index)
+			addattr32(&req->n, sizeof(*req), IFLA_NEW_IFINDEX, index);
 	} else {
 		if (name != dev) {
 			fprintf(stderr,
@@ -1500,6 +1514,65 @@ static int do_set(int argc, char **argv)
 }
 #endif /* IPLINK_IOCTL_COMPAT */
 
+void print_mpls_link_stats(FILE *fp, const struct mpls_link_stats *stats,
+			   const char *indent)
+{
+	unsigned int cols[] = {
+		strlen("*X: bytes"),
+		strlen("packets"),
+		strlen("errors"),
+		strlen("dropped"),
+		strlen("noroute"),
+	};
+
+	if (is_json_context()) {
+		/* RX stats */
+		open_json_object("rx");
+		print_u64(PRINT_JSON, "bytes", NULL, stats->rx_bytes);
+		print_u64(PRINT_JSON, "packets", NULL, stats->rx_packets);
+		print_u64(PRINT_JSON, "errors", NULL, stats->rx_errors);
+		print_u64(PRINT_JSON, "dropped", NULL, stats->rx_dropped);
+		print_u64(PRINT_JSON, "noroute", NULL, stats->rx_noroute);
+		close_json_object();
+
+		/* TX stats */
+		open_json_object("tx");
+		print_u64(PRINT_JSON, "bytes", NULL, stats->tx_bytes);
+		print_u64(PRINT_JSON, "packets", NULL, stats->tx_packets);
+		print_u64(PRINT_JSON, "errors", NULL, stats->tx_errors);
+		print_u64(PRINT_JSON, "dropped", NULL, stats->tx_dropped);
+		close_json_object();
+	} else {
+		size_columns(cols, ARRAY_SIZE(cols), stats->rx_bytes,
+			     stats->rx_packets, stats->rx_errors,
+			     stats->rx_dropped, stats->rx_noroute);
+		size_columns(cols, ARRAY_SIZE(cols), stats->tx_bytes,
+			     stats->tx_packets, stats->tx_errors,
+			     stats->tx_dropped, 0);
+
+		fprintf(fp, "%sRX: %*s %*s %*s %*s %*s%s", indent,
+			cols[0] - 4, "bytes", cols[1], "packets",
+			cols[2], "errors", cols[3], "dropped",
+			cols[4], "noroute", _SL_);
+		fprintf(fp, "%s", indent);
+		print_num(fp, cols[0], stats->rx_bytes);
+		print_num(fp, cols[1], stats->rx_packets);
+		print_num(fp, cols[2], stats->rx_errors);
+		print_num(fp, cols[3], stats->rx_dropped);
+		print_num(fp, cols[4], stats->rx_noroute);
+		fprintf(fp, "\n");
+
+		fprintf(fp, "%sTX: %*s %*s %*s %*s%s", indent,
+			cols[0] - 4, "bytes", cols[1], "packets",
+			cols[2], "errors", cols[3], "dropped", _SL_);
+		fprintf(fp, "%s", indent);
+		print_num(fp, cols[0], stats->tx_bytes);
+		print_num(fp, cols[1], stats->tx_packets);
+		print_num(fp, cols[2], stats->tx_errors);
+		print_num(fp, cols[3], stats->tx_dropped);
+	}
+}
+
 static void print_mpls_stats(FILE *fp, struct rtattr *attr)
 {
 	struct rtattr *mrtb[MPLS_STATS_MAX+1];
@@ -1511,22 +1584,8 @@ static void print_mpls_stats(FILE *fp, s
 		return;
 
 	stats = RTA_DATA(mrtb[MPLS_STATS_LINK]);
-
 	fprintf(fp, "    mpls:\n");
-	fprintf(fp, "        RX: bytes  packets  errors  dropped  noroute\n");
-	fprintf(fp, "        ");
-	print_num(fp, 10, stats->rx_bytes);
-	print_num(fp, 8, stats->rx_packets);
-	print_num(fp, 7, stats->rx_errors);
-	print_num(fp, 8, stats->rx_dropped);
-	print_num(fp, 7, stats->rx_noroute);
-	fprintf(fp, "\n");
-	fprintf(fp, "        TX: bytes  packets  errors  dropped\n");
-	fprintf(fp, "        ");
-	print_num(fp, 10, stats->tx_bytes);
-	print_num(fp, 8, stats->tx_packets);
-	print_num(fp, 7, stats->tx_errors);
-	print_num(fp, 7, stats->tx_dropped);
+	print_mpls_link_stats(fp, stats, "        ");
 	fprintf(fp, "\n");
 }
 
@@ -1627,7 +1686,8 @@ static int iplink_afstats(int argc, char
 		}
 	}
 
-	if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
+	if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask,
+				      NULL, NULL) < 0) {
 		perror("Cannont send dump request");
 		return 1;
 	}
diff -pruN 5.15.0-1/ip/iplink_can.c 5.19.0-1/ip/iplink_can.c
--- 5.15.0-1/ip/iplink_can.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/iplink_can.c	2022-08-02 18:36:33.000000000 +0000
@@ -28,6 +28,7 @@ static void print_usage(FILE *f)
 		"\n"
 		"\t[ dbitrate BITRATE [ dsample-point SAMPLE-POINT] ] |\n"
 		"\t[ dtq TQ dprop-seg PROP_SEG dphase-seg1 PHASE-SEG1\n \t  dphase-seg2 PHASE-SEG2 [ dsjw SJW ] ]\n"
+		"\t[ tdcv TDCV tdco TDCO tdcf TDCF ]\n"
 		"\n"
 		"\t[ loopback { on | off } ]\n"
 		"\t[ listen-only { on | off } ]\n"
@@ -38,20 +39,24 @@ static void print_usage(FILE *f)
 		"\t[ fd-non-iso { on | off } ]\n"
 		"\t[ presume-ack { on | off } ]\n"
 		"\t[ cc-len8-dlc { on | off } ]\n"
+		"\t[ tdc-mode { auto | manual | off } ]\n"
 		"\n"
 		"\t[ restart-ms TIME-MS ]\n"
 		"\t[ restart ]\n"
 		"\n"
 		"\t[ termination { 0..65535 } ]\n"
 		"\n"
-		"\tWhere: BITRATE	:= { 1..1000000 }\n"
+		"\tWhere: BITRATE	:= { NUMBER in bps }\n"
 		"\t	  SAMPLE-POINT	:= { 0.000..0.999 }\n"
-		"\t	  TQ		:= { NUMBER }\n"
-		"\t	  PROP-SEG	:= { 1..8 }\n"
-		"\t	  PHASE-SEG1	:= { 1..8 }\n"
-		"\t	  PHASE-SEG2	:= { 1..8 }\n"
-		"\t	  SJW		:= { 1..4 }\n"
-		"\t	  RESTART-MS	:= { 0 | NUMBER }\n"
+		"\t	  TQ		:= { NUMBER in ns }\n"
+		"\t	  PROP-SEG	:= { NUMBER in tq }\n"
+		"\t	  PHASE-SEG1	:= { NUMBER in tq }\n"
+		"\t	  PHASE-SEG2	:= { NUMBER in tq }\n"
+		"\t	  SJW		:= { NUMBER in tq }\n"
+		"\t	  TDCV		:= { NUMBER in tc }\n"
+		"\t	  TDCO		:= { NUMBER in tc }\n"
+		"\t	  TDCF		:= { NUMBER in tc }\n"
+		"\t	  RESTART-MS	:= { 0 | NUMBER in ms }\n"
 		);
 }
 
@@ -88,34 +93,47 @@ static void set_ctrlmode(char *name, cha
 	cm->mask |= flags;
 }
 
-static void print_ctrlmode(FILE *f, __u32 cm)
+static void print_flag(enum output_type t, __u32 *flags, __u32 flag,
+		       const char* name)
 {
-	open_json_array(PRINT_ANY, is_json_context() ? "ctrlmode" : "<");
-#define _PF(cmflag, cmname)						\
-	if (cm & cmflag) {						\
-		cm &= ~cmflag;						\
-		print_string(PRINT_ANY, NULL, cm ? "%s," : "%s", cmname); \
-	}
-	_PF(CAN_CTRLMODE_LOOPBACK, "LOOPBACK");
-	_PF(CAN_CTRLMODE_LISTENONLY, "LISTEN-ONLY");
-	_PF(CAN_CTRLMODE_3_SAMPLES, "TRIPLE-SAMPLING");
-	_PF(CAN_CTRLMODE_ONE_SHOT, "ONE-SHOT");
-	_PF(CAN_CTRLMODE_BERR_REPORTING, "BERR-REPORTING");
-	_PF(CAN_CTRLMODE_FD, "FD");
-	_PF(CAN_CTRLMODE_FD_NON_ISO, "FD-NON-ISO");
-	_PF(CAN_CTRLMODE_PRESUME_ACK, "PRESUME-ACK");
-	_PF(CAN_CTRLMODE_CC_LEN8_DLC, "CC-LEN8-DLC");
-#undef _PF
-	if (cm)
-		print_hex(PRINT_ANY, NULL, "%x", cm);
-	close_json_array(PRINT_ANY, "> ");
+	if (*flags & flag) {
+		*flags &= ~flag;
+		print_string(t, NULL, *flags ? "%s," : "%s", name);
+	}
+}
+
+static void print_ctrlmode(enum output_type t, __u32 flags, const char* key)
+{
+	if (!flags)
+		return;
+
+	open_json_array(t, is_json_context() ? key : "<");
+
+	print_flag(t, &flags, CAN_CTRLMODE_LOOPBACK, "LOOPBACK");
+	print_flag(t, &flags, CAN_CTRLMODE_LISTENONLY, "LISTEN-ONLY");
+	print_flag(t, &flags, CAN_CTRLMODE_3_SAMPLES, "TRIPLE-SAMPLING");
+	print_flag(t, &flags, CAN_CTRLMODE_ONE_SHOT, "ONE-SHOT");
+	print_flag(t, &flags, CAN_CTRLMODE_BERR_REPORTING, "BERR-REPORTING");
+	print_flag(t, &flags, CAN_CTRLMODE_FD, "FD");
+	print_flag(t, &flags, CAN_CTRLMODE_FD_NON_ISO, "FD-NON-ISO");
+	print_flag(t, &flags, CAN_CTRLMODE_PRESUME_ACK, "PRESUME-ACK");
+	print_flag(t, &flags, CAN_CTRLMODE_CC_LEN8_DLC, "CC-LEN8-DLC");
+	print_flag(t, &flags, CAN_CTRLMODE_TDC_AUTO, "TDC-AUTO");
+	print_flag(t, &flags, CAN_CTRLMODE_TDC_MANUAL, "TDC-MANUAL");
+
+	if (flags)
+		print_hex(t, NULL, "%x", flags);
+
+	close_json_array(t, "> ");
 }
 
 static int can_parse_opt(struct link_util *lu, int argc, char **argv,
 			 struct nlmsghdr *n)
 {
 	struct can_bittiming bt = {}, dbt = {};
-	struct can_ctrlmode cm = {0, 0};
+	struct can_ctrlmode cm = { 0 };
+	struct rtattr *tdc;
+	__u32 tdcv = -1, tdco = -1, tdcf = -1;
 
 	while (argc > 0) {
 		if (matches(*argv, "bitrate") == 0) {
@@ -181,6 +199,18 @@ static int can_parse_opt(struct link_uti
 			NEXT_ARG();
 			if (get_u32(&dbt.sjw, *argv, 0))
 				invarg("invalid \"dsjw\" value\n", *argv);
+		} else if (matches(*argv, "tdcv") == 0) {
+			NEXT_ARG();
+			if (get_u32(&tdcv, *argv, 0))
+				invarg("invalid \"tdcv\" value\n", *argv);
+		} else if (matches(*argv, "tdco") == 0) {
+			NEXT_ARG();
+			if (get_u32(&tdco, *argv, 0))
+				invarg("invalid \"tdco\" value\n", *argv);
+		} else if (matches(*argv, "tdcf") == 0) {
+			NEXT_ARG();
+			if (get_u32(&tdcf, *argv, 0))
+				invarg("invalid \"tdcf\" value\n", *argv);
 		} else if (matches(*argv, "loopback") == 0) {
 			NEXT_ARG();
 			set_ctrlmode("loopback", *argv, &cm,
@@ -217,6 +247,23 @@ static int can_parse_opt(struct link_uti
 			NEXT_ARG();
 			set_ctrlmode("cc-len8-dlc", *argv, &cm,
 				     CAN_CTRLMODE_CC_LEN8_DLC);
+		} else if (matches(*argv, "tdc-mode") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "auto") == 0) {
+				cm.flags |= CAN_CTRLMODE_TDC_AUTO;
+				cm.mask |= CAN_CTRLMODE_TDC_AUTO;
+			} else if (strcmp(*argv, "manual") == 0) {
+				cm.flags |= CAN_CTRLMODE_TDC_MANUAL;
+				cm.mask |= CAN_CTRLMODE_TDC_MANUAL;
+			} else if (strcmp(*argv, "off") == 0) {
+				cm.mask |= CAN_CTRLMODE_TDC_AUTO |
+					   CAN_CTRLMODE_TDC_MANUAL;
+			} else {
+				fprintf(stderr,
+					"Error: argument of \"tdc-mode\" must be \"auto\", \"manual\" or \"off\", not \"%s\"\n",
+					*argv);
+				exit (-1);
+			}
 		} else if (matches(*argv, "restart") == 0) {
 			__u32 val = 1;
 
@@ -254,6 +301,17 @@ static int can_parse_opt(struct link_uti
 	if (cm.mask)
 		addattr_l(n, 1024, IFLA_CAN_CTRLMODE, &cm, sizeof(cm));
 
+	if (tdcv != -1 || tdco != -1 || tdcf != -1) {
+		tdc = addattr_nest(n, 1024, IFLA_CAN_TDC | NLA_F_NESTED);
+		if (tdcv != -1)
+			addattr32(n, 1024, IFLA_CAN_TDC_TDCV, tdcv);
+		if (tdco != -1)
+			addattr32(n, 1024, IFLA_CAN_TDC_TDCO, tdco);
+		if (tdcf != -1)
+			addattr32(n, 1024, IFLA_CAN_TDC_TDCF, tdcf);
+		addattr_nest_end(n, tdc);
+	}
+
 	return 0;
 }
 
@@ -266,14 +324,93 @@ static const char *can_state_names[CAN_S
 	[CAN_STATE_SLEEPING] = "SLEEPING"
 };
 
-static void can_print_json_timing_min_max(const char *attr, int min, int max)
+static void can_print_nl_indent(void)
 {
-	open_json_object(attr);
-	print_int(PRINT_JSON, "min", NULL, min);
-	print_int(PRINT_JSON, "max", NULL, max);
+	print_nl();
+	print_string(PRINT_FP, NULL, "%s", "\t ");
+}
+
+static void __attribute__((format(printf, 2, 0)))
+can_print_timing_min_max(const char *json_attr, const char *fp_attr,
+			 int min, int max)
+{
+	print_null(PRINT_FP, NULL, fp_attr, NULL);
+	open_json_object(json_attr);
+	print_uint(PRINT_ANY, "min", " %d", min);
+	print_uint(PRINT_ANY, "max", "..%d", max);
 	close_json_object();
 }
 
+static void can_print_tdc_opt(FILE *f, struct rtattr *tdc_attr)
+{
+	struct rtattr *tb[IFLA_CAN_TDC_MAX + 1];
+
+	parse_rtattr_nested(tb, IFLA_CAN_TDC_MAX, tdc_attr);
+	if (tb[IFLA_CAN_TDC_TDCV] || tb[IFLA_CAN_TDC_TDCO] ||
+	    tb[IFLA_CAN_TDC_TDCF]) {
+		open_json_object("tdc");
+		can_print_nl_indent();
+		if (tb[IFLA_CAN_TDC_TDCV]) {
+			__u32 *tdcv = RTA_DATA(tb[IFLA_CAN_TDC_TDCV]);
+
+			print_uint(PRINT_ANY, "tdcv", " tdcv %u", *tdcv);
+		}
+		if (tb[IFLA_CAN_TDC_TDCO]) {
+			__u32 *tdco = RTA_DATA(tb[IFLA_CAN_TDC_TDCO]);
+
+			print_uint(PRINT_ANY, "tdco", " tdco %u", *tdco);
+		}
+		if (tb[IFLA_CAN_TDC_TDCF]) {
+			__u32 *tdcf = RTA_DATA(tb[IFLA_CAN_TDC_TDCF]);
+
+			print_uint(PRINT_ANY, "tdcf", " tdcf %u", *tdcf);
+		}
+		close_json_object();
+	}
+}
+
+static void can_print_tdc_const_opt(FILE *f, struct rtattr *tdc_attr)
+{
+	struct rtattr *tb[IFLA_CAN_TDC_MAX + 1];
+
+	parse_rtattr_nested(tb, IFLA_CAN_TDC_MAX, tdc_attr);
+	open_json_object("tdc");
+	can_print_nl_indent();
+	if (tb[IFLA_CAN_TDC_TDCV_MIN] && tb[IFLA_CAN_TDC_TDCV_MAX]) {
+		__u32 *tdcv_min = RTA_DATA(tb[IFLA_CAN_TDC_TDCV_MIN]);
+		__u32 *tdcv_max = RTA_DATA(tb[IFLA_CAN_TDC_TDCV_MAX]);
+
+		can_print_timing_min_max("tdcv", " tdcv", *tdcv_min, *tdcv_max);
+	}
+	if (tb[IFLA_CAN_TDC_TDCO_MIN] && tb[IFLA_CAN_TDC_TDCO_MAX]) {
+		__u32 *tdco_min = RTA_DATA(tb[IFLA_CAN_TDC_TDCO_MIN]);
+		__u32 *tdco_max = RTA_DATA(tb[IFLA_CAN_TDC_TDCO_MAX]);
+
+		can_print_timing_min_max("tdco", " tdco", *tdco_min, *tdco_max);
+	}
+	if (tb[IFLA_CAN_TDC_TDCF_MIN] && tb[IFLA_CAN_TDC_TDCF_MAX]) {
+		__u32 *tdcf_min = RTA_DATA(tb[IFLA_CAN_TDC_TDCF_MIN]);
+		__u32 *tdcf_max = RTA_DATA(tb[IFLA_CAN_TDC_TDCF_MAX]);
+
+		can_print_timing_min_max("tdcf", " tdcf", *tdcf_min, *tdcf_max);
+	}
+	close_json_object();
+}
+
+static void can_print_ctrlmode_ext(FILE *f, struct rtattr *ctrlmode_ext_attr,
+				   __u32 cm_flags)
+{
+	struct rtattr *tb[IFLA_CAN_CTRLMODE_MAX + 1];
+
+	parse_rtattr_nested(tb, IFLA_CAN_CTRLMODE_MAX, ctrlmode_ext_attr);
+	if (tb[IFLA_CAN_CTRLMODE_SUPPORTED]) {
+		__u32 *supported = RTA_DATA(tb[IFLA_CAN_CTRLMODE_SUPPORTED]);
+
+		print_ctrlmode(PRINT_JSON, *supported, "ctrlmode_supported");
+		print_ctrlmode(PRINT_JSON, cm_flags & ~*supported, "ctrlmode_static");
+	}
+}
+
 static void can_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 {
 	if (!tb)
@@ -282,8 +419,10 @@ static void can_print_opt(struct link_ut
 	if (tb[IFLA_CAN_CTRLMODE]) {
 		struct can_ctrlmode *cm = RTA_DATA(tb[IFLA_CAN_CTRLMODE]);
 
-		if (cm->flags)
-			print_ctrlmode(f, cm->flags);
+		print_ctrlmode(PRINT_ANY, cm->flags, "ctrlmode");
+		if (tb[IFLA_CAN_CTRLMODE_EXT])
+			can_print_ctrlmode_ext(f, tb[IFLA_CAN_CTRLMODE_EXT],
+					       cm->flags);
 	}
 
 	if (tb[IFLA_CAN_STATE]) {
@@ -297,56 +436,39 @@ static void can_print_opt(struct link_ut
 		struct can_berr_counter *bc =
 			RTA_DATA(tb[IFLA_CAN_BERR_COUNTER]);
 
-		if (is_json_context()) {
-			open_json_object("berr_counter");
-			print_int(PRINT_JSON, "tx", NULL, bc->txerr);
-			print_int(PRINT_JSON, "rx", NULL, bc->rxerr);
-			close_json_object();
-		} else {
-			fprintf(f, "(berr-counter tx %d rx %d) ",
-				bc->txerr, bc->rxerr);
-		}
+		open_json_object("berr_counter");
+		print_uint(PRINT_ANY, "tx", "(berr-counter tx %u", bc->txerr);
+		print_uint(PRINT_ANY, "rx", " rx %u) ", bc->rxerr);
+		close_json_object();
 	}
 
 	if (tb[IFLA_CAN_RESTART_MS]) {
 		__u32 *restart_ms = RTA_DATA(tb[IFLA_CAN_RESTART_MS]);
 
-		print_int(PRINT_ANY,
-			  "restart_ms",
-			  "restart-ms %d ",
-			  *restart_ms);
+		print_uint(PRINT_ANY, "restart_ms", "restart-ms %u ",
+			   *restart_ms);
 	}
 
 	/* bittiming is irrelevant if fixed bitrate is defined */
 	if (tb[IFLA_CAN_BITTIMING] && !tb[IFLA_CAN_BITRATE_CONST]) {
 		struct can_bittiming *bt = RTA_DATA(tb[IFLA_CAN_BITTIMING]);
+		char sp[6];
 
-		if (is_json_context()) {
-			json_writer_t *jw;
-
-			open_json_object("bittiming");
-			print_int(PRINT_ANY, "bitrate", NULL, bt->bitrate);
-			jw = get_json_writer();
-			jsonw_name(jw, "sample_point");
-			jsonw_printf(jw, "%.3f",
-				     (float) bt->sample_point / 1000);
-			print_int(PRINT_ANY, "tq", NULL, bt->tq);
-			print_int(PRINT_ANY, "prop_seg", NULL, bt->prop_seg);
-			print_int(PRINT_ANY, "phase_seg1",
-				  NULL, bt->phase_seg1);
-			print_int(PRINT_ANY, "phase_seg2",
-				  NULL, bt->phase_seg2);
-			print_int(PRINT_ANY, "sjw", NULL, bt->sjw);
-			close_json_object();
-		} else {
-			fprintf(f, "\n	  bitrate %d sample-point %.3f ",
-				bt->bitrate, (float) bt->sample_point / 1000.);
-			fprintf(f,
-				"\n	  tq %d prop-seg %d phase-seg1 %d phase-seg2 %d sjw %d",
-				bt->tq, bt->prop_seg,
-				bt->phase_seg1, bt->phase_seg2,
-				bt->sjw);
-		}
+		open_json_object("bittiming");
+		can_print_nl_indent();
+		print_uint(PRINT_ANY, "bitrate", " bitrate %u", bt->bitrate);
+		snprintf(sp, sizeof(sp), "%.3f", bt->sample_point / 1000.);
+		print_string(PRINT_ANY, "sample_point", " sample-point %s", sp);
+		can_print_nl_indent();
+		print_uint(PRINT_ANY, "tq", " tq %u", bt->tq);
+		print_uint(PRINT_ANY, "prop_seg", " prop-seg %u", bt->prop_seg);
+		print_uint(PRINT_ANY, "phase_seg1", " phase-seg1 %u",
+			   bt->phase_seg1);
+		print_uint(PRINT_ANY, "phase_seg2", " phase-seg2 %u",
+			   bt->phase_seg2);
+		print_uint(PRINT_ANY, "sjw", " sjw %u", bt->sjw);
+		print_uint(PRINT_ANY, "brp", " brp %u", bt->brp);
+		close_json_object();
 	}
 
 	/* bittiming const is irrelevant if fixed bitrate is defined */
@@ -354,28 +476,18 @@ static void can_print_opt(struct link_ut
 		struct can_bittiming_const *btc =
 			RTA_DATA(tb[IFLA_CAN_BITTIMING_CONST]);
 
-		if (is_json_context()) {
-			open_json_object("bittiming_const");
-			print_string(PRINT_JSON, "name", NULL, btc->name);
-			can_print_json_timing_min_max("tseg1",
-						      btc->tseg1_min,
-						      btc->tseg1_max);
-			can_print_json_timing_min_max("tseg2",
-						      btc->tseg2_min,
-						      btc->tseg2_max);
-			can_print_json_timing_min_max("sjw", 1, btc->sjw_max);
-			can_print_json_timing_min_max("brp",
-						      btc->brp_min,
-						      btc->brp_max);
-			print_int(PRINT_JSON, "brp_inc", NULL, btc->brp_inc);
-			close_json_object();
-		} else {
-			fprintf(f, "\n	  %s: tseg1 %d..%d tseg2 %d..%d "
-				"sjw 1..%d brp %d..%d brp-inc %d",
-				btc->name, btc->tseg1_min, btc->tseg1_max,
-				btc->tseg2_min, btc->tseg2_max, btc->sjw_max,
-				btc->brp_min, btc->brp_max, btc->brp_inc);
-		}
+		open_json_object("bittiming_const");
+		can_print_nl_indent();
+		print_string(PRINT_ANY, "name", " %s:", btc->name);
+		can_print_timing_min_max("tseg1", " tseg1",
+					 btc->tseg1_min, btc->tseg1_max);
+		can_print_timing_min_max("tseg2", " tseg2",
+					 btc->tseg2_min, btc->tseg2_max);
+		can_print_timing_min_max("sjw", " sjw", 1, btc->sjw_max);
+		can_print_timing_min_max("brp", " brp",
+					 btc->brp_min, btc->brp_max);
+		print_uint(PRINT_ANY, "brp_inc", " brp_inc %u", btc->brp_inc);
+		close_json_object();
 	}
 
 	if (tb[IFLA_CAN_BITRATE_CONST]) {
@@ -391,64 +503,52 @@ static void can_print_opt(struct link_ut
 			bitrate = bt->bitrate;
 		}
 
-		if (is_json_context()) {
-			print_uint(PRINT_JSON,
-				   "bittiming_bitrate",
-				   NULL, bitrate);
-			open_json_array(PRINT_JSON, "bitrate_const");
-			for (i = 0; i < bitrate_cnt; ++i)
-				print_uint(PRINT_JSON, NULL, NULL,
-					   bitrate_const[i]);
-			close_json_array(PRINT_JSON, NULL);
-		} else {
-			fprintf(f, "\n	  bitrate %u", bitrate);
-			fprintf(f, "\n	     [");
-
-			for (i = 0; i < bitrate_cnt - 1; ++i) {
-				/* This will keep lines below 80 signs */
-				if (!(i % 6) && i)
-					fprintf(f, "\n	      ");
-
-				fprintf(f, "%8u, ", bitrate_const[i]);
+		can_print_nl_indent();
+		print_uint(PRINT_ANY, "bittiming_bitrate", " bitrate %u",
+			   bitrate);
+		can_print_nl_indent();
+		open_json_array(PRINT_ANY, is_json_context() ?
+				"bitrate_const" : "    [");
+		for (i = 0; i < bitrate_cnt; ++i) {
+			/* This will keep lines below 80 signs */
+			if (!(i % 6) && i) {
+				can_print_nl_indent();
+				print_string(PRINT_FP, NULL, "%s", "     ");
 			}
-
-			if (!(i % 6) && i)
-				fprintf(f, "\n	      ");
-			fprintf(f, "%8u ]", bitrate_const[i]);
+			print_uint(PRINT_ANY, NULL,
+				   i < bitrate_cnt - 1 ? "%8u, " : "%8u",
+				   bitrate_const[i]);
 		}
+		close_json_array(PRINT_JSON, " ]");
 	}
 
 	/* data bittiming is irrelevant if fixed bitrate is defined */
 	if (tb[IFLA_CAN_DATA_BITTIMING] && !tb[IFLA_CAN_DATA_BITRATE_CONST]) {
 		struct can_bittiming *dbt =
 			RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING]);
+		char dsp[6];
 
-		if (is_json_context()) {
-			json_writer_t *jw;
+		open_json_object("data_bittiming");
+		can_print_nl_indent();
+		print_uint(PRINT_ANY, "bitrate", " dbitrate %u", dbt->bitrate);
+		snprintf(dsp, sizeof(dsp), "%.3f", dbt->sample_point / 1000.);
+		print_string(PRINT_ANY, "sample_point", " dsample-point %s",
+			     dsp);
+		can_print_nl_indent();
+		print_uint(PRINT_ANY, "tq", " dtq %u", dbt->tq);
+		print_uint(PRINT_ANY, "prop_seg", " dprop-seg %u",
+			   dbt->prop_seg);
+		print_uint(PRINT_ANY, "phase_seg1", " dphase-seg1 %u",
+			   dbt->phase_seg1);
+		print_uint(PRINT_ANY, "phase_seg2", " dphase-seg2 %u",
+			   dbt->phase_seg2);
+		print_uint(PRINT_ANY, "sjw", " dsjw %u", dbt->sjw);
+		print_uint(PRINT_ANY, "brp", " dbrp %u", dbt->brp);
 
-			open_json_object("data_bittiming");
-			print_int(PRINT_JSON, "bitrate", NULL, dbt->bitrate);
-			jw = get_json_writer();
-			jsonw_name(jw, "sample_point");
-			jsonw_printf(jw, "%.3f",
-				     (float) dbt->sample_point / 1000.);
-			print_int(PRINT_JSON, "tq", NULL, dbt->tq);
-			print_int(PRINT_JSON, "prop_seg", NULL, dbt->prop_seg);
-			print_int(PRINT_JSON, "phase_seg1",
-				  NULL, dbt->phase_seg1);
-			print_int(PRINT_JSON, "phase_seg2",
-				  NULL, dbt->phase_seg2);
-			print_int(PRINT_JSON, "sjw", NULL, dbt->sjw);
-			close_json_object();
-		} else {
-			fprintf(f, "\n	  dbitrate %d dsample-point %.3f ",
-				dbt->bitrate,
-				(float) dbt->sample_point / 1000.);
-			fprintf(f, "\n	  dtq %d dprop-seg %d dphase-seg1 %d "
-				"dphase-seg2 %d dsjw %d",
-				dbt->tq, dbt->prop_seg, dbt->phase_seg1,
-				dbt->phase_seg2, dbt->sjw);
-		}
+		if (tb[IFLA_CAN_TDC])
+			can_print_tdc_opt(f, tb[IFLA_CAN_TDC]);
+
+		close_json_object();
 	}
 
 	/* data bittiming const is irrelevant if fixed bitrate is defined */
@@ -457,29 +557,22 @@ static void can_print_opt(struct link_ut
 		struct can_bittiming_const *dbtc =
 			RTA_DATA(tb[IFLA_CAN_DATA_BITTIMING_CONST]);
 
-		if (is_json_context()) {
-			open_json_object("data_bittiming_const");
-			print_string(PRINT_JSON, "name", NULL, dbtc->name);
-			can_print_json_timing_min_max("tseg1",
-						      dbtc->tseg1_min,
-						      dbtc->tseg1_max);
-			can_print_json_timing_min_max("tseg2",
-						      dbtc->tseg2_min,
-						      dbtc->tseg2_max);
-			can_print_json_timing_min_max("sjw", 1, dbtc->sjw_max);
-			can_print_json_timing_min_max("brp",
-						      dbtc->brp_min,
-						      dbtc->brp_max);
+		open_json_object("data_bittiming_const");
+		can_print_nl_indent();
+		print_string(PRINT_ANY, "name", " %s:", dbtc->name);
+		can_print_timing_min_max("tseg1", " dtseg1",
+					 dbtc->tseg1_min, dbtc->tseg1_max);
+		can_print_timing_min_max("tseg2", " dtseg2",
+					 dbtc->tseg2_min, dbtc->tseg2_max);
+		can_print_timing_min_max("sjw", " dsjw", 1, dbtc->sjw_max);
+		can_print_timing_min_max("brp", " dbrp",
+					 dbtc->brp_min, dbtc->brp_max);
+		print_uint(PRINT_ANY, "brp_inc", " dbrp_inc %u", dbtc->brp_inc);
 
-			print_int(PRINT_JSON, "brp_inc", NULL, dbtc->brp_inc);
-			close_json_object();
-		} else {
-			fprintf(f, "\n	  %s: dtseg1 %d..%d dtseg2 %d..%d "
-				"dsjw 1..%d dbrp %d..%d dbrp-inc %d",
-				dbtc->name, dbtc->tseg1_min, dbtc->tseg1_max,
-				dbtc->tseg2_min, dbtc->tseg2_max, dbtc->sjw_max,
-				dbtc->brp_min, dbtc->brp_max, dbtc->brp_inc);
-		}
+		if (tb[IFLA_CAN_TDC])
+			can_print_tdc_const_opt(f, tb[IFLA_CAN_TDC]);
+
+		close_json_object();
 	}
 
 	if (tb[IFLA_CAN_DATA_BITRATE_CONST]) {
@@ -497,30 +590,23 @@ static void can_print_opt(struct link_ut
 			dbitrate = dbt->bitrate;
 		}
 
-		if (is_json_context()) {
-			print_uint(PRINT_JSON, "data_bittiming_bitrate",
-				   NULL, dbitrate);
-			open_json_array(PRINT_JSON, "data_bitrate_const");
-			for (i = 0; i < dbitrate_cnt; ++i)
-				print_uint(PRINT_JSON, NULL, NULL,
-					   dbitrate_const[i]);
-			close_json_array(PRINT_JSON, NULL);
-		} else {
-			fprintf(f, "\n	  dbitrate %u", dbitrate);
-			fprintf(f, "\n	     [");
-
-			for (i = 0; i < dbitrate_cnt - 1; ++i) {
-				/* This will keep lines below 80 signs */
-				if (!(i % 6) && i)
-					fprintf(f, "\n	      ");
-
-				fprintf(f, "%8u, ", dbitrate_const[i]);
+		can_print_nl_indent();
+		print_uint(PRINT_ANY, "data_bittiming_bitrate", " dbitrate %u",
+			   dbitrate);
+		can_print_nl_indent();
+		open_json_array(PRINT_ANY, is_json_context() ?
+				"data_bitrate_const" : "    [");
+		for (i = 0; i < dbitrate_cnt; ++i) {
+			/* This will keep lines below 80 signs */
+			if (!(i % 6) && i) {
+				can_print_nl_indent();
+				print_string(PRINT_FP, NULL, "%s", "     ");
 			}
-
-			if (!(i % 6) && i)
-				fprintf(f, "\n	      ");
-			fprintf(f, "%8u ]", dbitrate_const[i]);
+			print_uint(PRINT_ANY, NULL,
+				   i < dbitrate_cnt - 1 ? "%8u, " : "%8u",
+				   dbitrate_const[i]);
 		}
+		close_json_array(PRINT_JSON, " ]");
 	}
 
 	if (tb[IFLA_CAN_TERMINATION_CONST] && tb[IFLA_CAN_TERMINATION]) {
@@ -530,29 +616,21 @@ static void can_print_opt(struct link_ut
 			sizeof(*trm_const);
 		int i;
 
-		if (is_json_context()) {
-			print_hu(PRINT_JSON, "termination", NULL, *trm);
-			open_json_array(PRINT_JSON, "termination_const");
-			for (i = 0; i < trm_cnt; ++i)
-				print_hu(PRINT_JSON, NULL, NULL, trm_const[i]);
-			close_json_array(PRINT_JSON, NULL);
-		} else {
-			fprintf(f, "\n	  termination %hu [ ", *trm);
-
-			for (i = 0; i < trm_cnt - 1; ++i)
-				fprintf(f, "%hu, ", trm_const[i]);
-
-			fprintf(f, "%hu ]", trm_const[i]);
-		}
+		can_print_nl_indent();
+		print_hu(PRINT_ANY, "termination", " termination %hu [ ", *trm);
+		open_json_array(PRINT_JSON, "termination_const");
+		for (i = 0; i < trm_cnt; ++i)
+			print_hu(PRINT_ANY, NULL,
+				 i < trm_cnt - 1 ? "%hu, " : "%hu",
+				 trm_const[i]);
+		close_json_array(PRINT_JSON, " ]");
 	}
 
 	if (tb[IFLA_CAN_CLOCK]) {
 		struct can_clock *clock = RTA_DATA(tb[IFLA_CAN_CLOCK]);
 
-		print_int(PRINT_ANY,
-			  "clock",
-			  "\n	  clock %d ",
-			  clock->freq);
+		can_print_nl_indent();
+		print_uint(PRINT_ANY, "clock", " clock %u ", clock->freq);
 	}
 
 }
@@ -565,31 +643,23 @@ static void can_print_xstats(struct link
 	if (xstats && RTA_PAYLOAD(xstats) == sizeof(*stats)) {
 		stats = RTA_DATA(xstats);
 
-		if (is_json_context()) {
-			print_int(PRINT_JSON, "restarts",
-				  NULL, stats->restarts);
-			print_int(PRINT_JSON, "bus_error",
-				  NULL, stats->bus_error);
-			print_int(PRINT_JSON, "arbitration_lost",
-				  NULL, stats->arbitration_lost);
-			print_int(PRINT_JSON, "error_warning",
-				  NULL, stats->error_warning);
-			print_int(PRINT_JSON, "error_passive",
-				  NULL, stats->error_passive);
-			print_int(PRINT_JSON, "bus_off", NULL, stats->bus_off);
-		} else {
-			fprintf(f, "\n	  re-started bus-errors arbit-lost "
-				"error-warn error-pass bus-off");
-			fprintf(f, "\n	  %-10d %-10d %-10d %-10d %-10d %-10d",
-				stats->restarts, stats->bus_error,
-				stats->arbitration_lost, stats->error_warning,
-				stats->error_passive, stats->bus_off);
-		}
+		can_print_nl_indent();
+		print_string(PRINT_FP, NULL, "%s",
+			     " re-started bus-errors arbit-lost error-warn error-pass bus-off");
+		can_print_nl_indent();
+		print_uint(PRINT_ANY, "restarts", " %-10u", stats->restarts);
+		print_uint(PRINT_ANY, "bus_error", " %-10u", stats->bus_error);
+		print_uint(PRINT_ANY, "arbitration_lost", " %-10u",
+			   stats->arbitration_lost);
+		print_uint(PRINT_ANY, "error_warning", " %-10u",
+			   stats->error_warning);
+		print_uint(PRINT_ANY, "error_passive", " %-10u",
+			   stats->error_passive);
+		print_uint(PRINT_ANY, "bus_off", " %-10u", stats->bus_off);
 	}
 }
 
-static void can_print_help(struct link_util *lu, int argc, char **argv,
-			   FILE *f)
+static void can_print_help(struct link_util *lu, int argc, char **argv, FILE *f)
 {
 	print_usage(f);
 }
diff -pruN 5.15.0-1/ip/iplink_geneve.c 5.19.0-1/ip/iplink_geneve.c
--- 5.15.0-1/ip/iplink_geneve.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/iplink_geneve.c	2022-08-02 18:36:33.000000000 +0000
@@ -31,6 +31,7 @@ static void print_explain(FILE *f)
 		"		[ [no]udpcsum ]\n"
 		"		[ [no]udp6zerocsumtx ]\n"
 		"		[ [no]udp6zerocsumrx ]\n"
+		"		[ innerprotoinherit ]\n"
 		"\n"
 		"Where:	VNI   := 0-16777215\n"
 		"	ADDR  := IP_ADDRESS\n"
@@ -72,6 +73,7 @@ static int geneve_parse_opt(struct link_
 	__u64 attrs = 0;
 	bool set_op = (n->nlmsg_type == RTM_NEWLINK &&
 		       !(n->nlmsg_flags & NLM_F_CREATE));
+	bool inner_proto_inherit = false;
 
 	inet_prefix_reset(&daddr);
 
@@ -182,6 +184,10 @@ static int geneve_parse_opt(struct link_
 			check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
 				     *argv, *argv);
 			udp6zerocsumrx = 0;
+		} else if (!strcmp(*argv, "innerprotoinherit")) {
+			check_duparg(&attrs, IFLA_GENEVE_INNER_PROTO_INHERIT,
+				     *argv, *argv);
+			inner_proto_inherit = true;
 		} else if (matches(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -231,6 +237,8 @@ static int geneve_parse_opt(struct link_
 		addattr16(n, 1024, IFLA_GENEVE_PORT, htons(dstport));
 	if (metadata)
 		addattr(n, 1024, IFLA_GENEVE_COLLECT_METADATA);
+	if (inner_proto_inherit)
+		addattr(n, 1024, IFLA_GENEVE_INNER_PROTO_INHERIT);
 	if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_CSUM))
 		addattr8(n, 1024, IFLA_GENEVE_UDP_CSUM, udpcsum);
 	if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX))
@@ -365,6 +373,11 @@ static void geneve_print_opt(struct link
 			fputs("udp6zerocsumrx ", f);
 		}
 	}
+
+	if (tb[IFLA_GENEVE_INNER_PROTO_INHERIT]) {
+		print_bool(PRINT_ANY, "inner_proto_inherit",
+			   "innerprotoinherit ", true);
+	}
 }
 
 static void geneve_print_help(struct link_util *lu, int argc, char **argv,
diff -pruN 5.15.0-1/ip/iplink_gtp.c 5.19.0-1/ip/iplink_gtp.c
--- 5.15.0-1/ip/iplink_gtp.c	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/ip/iplink_gtp.c	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <stdio.h>
+
+#include "rt_names.h"
+#include "utils.h"
+#include "ip_common.h"
+
+#define GTP_ATTRSET(attrs, type) (((attrs) & (1L << (type))) != 0)
+
+static void print_explain(FILE *f)
+{
+	fprintf(f,
+		"Usage: ... gtp role ROLE\n"
+		"		[ hsize HSIZE ]\n"
+		"		[ restart_count RESTART_COUNT ]\n"
+		"\n"
+		"Where:	ROLE		:= { sgsn | ggsn }\n"
+		"	HSIZE		:= 1-131071\n"
+		"	RESTART_COUNT	:= 0-255\n"
+	);
+}
+
+static void check_duparg(__u32 *attrs, int type, const char *key,
+			 const char *argv)
+{
+	if (!GTP_ATTRSET(*attrs, type)) {
+		*attrs |= (1L << type);
+		return;
+	}
+	duparg2(key, argv);
+}
+
+static int gtp_parse_opt(struct link_util *lu, int argc, char **argv,
+			 struct nlmsghdr *n)
+{
+	__u32 attrs = 0;
+
+	/* When creating GTP device through ip link,
+	 * this flag has to be set.
+	 */
+	addattr8(n, 1024, IFLA_GTP_CREATE_SOCKETS, true);
+
+	while (argc > 0) {
+		if (!strcmp(*argv, "role")) {
+			NEXT_ARG();
+			check_duparg(&attrs, IFLA_GTP_ROLE, "role", *argv);
+			if (!strcmp(*argv, "sgsn"))
+				addattr32(n, 1024, IFLA_GTP_ROLE, GTP_ROLE_SGSN);
+			else if (!strcmp(*argv, "ggsn"))
+				addattr32(n, 1024, IFLA_GTP_ROLE, GTP_ROLE_GGSN);
+			else
+				invarg("invalid role, use sgsn or ggsn", *argv);
+		} else if (!strcmp(*argv, "hsize")) {
+			__u32 hsize;
+
+			NEXT_ARG();
+			check_duparg(&attrs, IFLA_GTP_PDP_HASHSIZE, "hsize", *argv);
+
+			if (get_u32(&hsize, *argv, 0))
+				invarg("invalid PDP hash size", *argv);
+			if (hsize >= 1u << 17)
+				invarg("PDP hash size too big", *argv);
+			addattr32(n, 1024, IFLA_GTP_PDP_HASHSIZE, hsize);
+		} else if (!strcmp(*argv, "restart_count")) {
+			__u8 restart_count;
+
+			NEXT_ARG();
+			check_duparg(&attrs, IFLA_GTP_RESTART_COUNT, "restart_count", *argv);
+
+			if (get_u8(&restart_count, *argv, 10))
+				invarg("invalid restart_count", *argv);
+			addattr8(n, 1024, IFLA_GTP_RESTART_COUNT, restart_count);
+		} else if (!strcmp(*argv, "help")) {
+			print_explain(stderr);
+			return -1;
+		}
+		argc--, argv++;
+	}
+
+	if (!GTP_ATTRSET(attrs, IFLA_GTP_ROLE)) {
+		fprintf(stderr, "gtp: role of the gtp device was not specified\n");
+		return -1;
+	}
+
+	if (!GTP_ATTRSET(attrs, IFLA_GTP_PDP_HASHSIZE))
+		addattr32(n, 1024, IFLA_GTP_PDP_HASHSIZE, 1024);
+
+	return 0;
+}
+
+static const char *gtp_role_to_string(__u32 role)
+{
+	switch (role) {
+	case GTP_ROLE_GGSN:
+		return "ggsn";
+	case GTP_ROLE_SGSN:
+		return "sgsn";
+	default:
+		return "unknown";
+	}
+}
+
+static void gtp_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
+{
+
+	if (tb[IFLA_GTP_ROLE]) {
+		__u32 role = rta_getattr_u32(tb[IFLA_GTP_ROLE]);
+
+		print_string(PRINT_ANY, "role", "role %s ",
+			     gtp_role_to_string(role));
+	}
+
+	if (tb[IFLA_GTP_PDP_HASHSIZE]) {
+		__u32 hsize = rta_getattr_u32(tb[IFLA_GTP_PDP_HASHSIZE]);
+
+		print_uint(PRINT_ANY, "hsize", "hsize %u ", hsize);
+	}
+
+	if (tb[IFLA_GTP_RESTART_COUNT]) {
+		__u8 restart_count = rta_getattr_u8(tb[IFLA_GTP_RESTART_COUNT]);
+
+		print_uint(PRINT_ANY, "restart_count",
+			   "restart_count %u ", restart_count);
+	}
+}
+
+static void gtp_print_help(struct link_util *lu, int argc, char **argv,
+			   FILE *f)
+{
+	print_explain(f);
+}
+
+struct link_util gtp_link_util = {
+	.id		= "gtp",
+	.maxattr	= IFLA_GTP_MAX,
+	.parse_opt	= gtp_parse_opt,
+	.print_opt	= gtp_print_opt,
+	.print_help	= gtp_print_help,
+};
diff -pruN 5.15.0-1/ip/iplink_virt_wifi.c 5.19.0-1/ip/iplink_virt_wifi.c
--- 5.15.0-1/ip/iplink_virt_wifi.c	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/ip/iplink_virt_wifi.c	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * iplink_virt_wifi.c  A fake implementation of cfg80211_ops that can be tacked
+ *                     on to an ethernet net_device to make it appear as a
+ *                     wireless connection.
+ *
+ * Authors:            Baligh Gasmi <gasmibal@gmail.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "utils.h"
+#include "ip_common.h"
+
+static void virt_wifi_print_help(struct link_util *lu,
+		int argc, char **argv, FILE *f)
+{
+	fprintf(f, "Usage: ... virt_wifi \n");
+}
+
+struct link_util virt_wifi_link_util = {
+	.id		= "virt_wifi",
+	.print_help	= virt_wifi_print_help,
+};
diff -pruN 5.15.0-1/ip/iplink_vxlan.c 5.19.0-1/ip/iplink_vxlan.c
--- 5.15.0-1/ip/iplink_vxlan.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/iplink_vxlan.c	2022-08-02 18:36:33.000000000 +0000
@@ -48,6 +48,7 @@ static void print_explain(FILE *f)
 		"		[ [no]udp6zerocsumrx ]\n"
 		"		[ [no]remcsumtx ] [ [no]remcsumrx ]\n"
 		"		[ [no]external ] [ gbp ] [ gpe ]\n"
+		"		[ [no]vnifilter ]\n"
 		"\n"
 		"Where:	VNI	:= 0-16777215\n"
 		"	ADDR	:= { IP_ADDRESS | any }\n"
@@ -81,6 +82,7 @@ static int vxlan_parse_opt(struct link_u
 	__u8 learning = 1;
 	__u16 dstport = 0;
 	__u8 metadata = 0;
+	__u8 vnifilter = 0;
 	__u64 attrs = 0;
 	bool set_op = (n->nlmsg_type == RTM_NEWLINK &&
 		       !(n->nlmsg_flags & NLM_F_CREATE));
@@ -330,6 +332,15 @@ static int vxlan_parse_opt(struct link_u
 		} else if (!matches(*argv, "gpe")) {
 			check_duparg(&attrs, IFLA_VXLAN_GPE, *argv, *argv);
 			addattr_l(n, 1024, IFLA_VXLAN_GPE, NULL, 0);
+		} else if (!strcmp(*argv, "vnifilter")) {
+			check_duparg(&attrs, IFLA_VXLAN_VNIFILTER,
+				     *argv, *argv);
+			addattr8(n, 1024, IFLA_VXLAN_VNIFILTER, 1);
+			vnifilter = 1;
+		} else if (!strcmp(*argv, "novnifilter")) {
+			check_duparg(&attrs, IFLA_VXLAN_VNIFILTER,
+				     *argv, *argv);
+			addattr8(n, 1024, IFLA_VXLAN_VNIFILTER, 0);
 		} else if (matches(*argv, "help") == 0) {
 			explain();
 			return -1;
@@ -341,12 +352,17 @@ static int vxlan_parse_opt(struct link_u
 		argc--, argv++;
 	}
 
+	if (!metadata && vnifilter) {
+		fprintf(stderr, "vxlan: vnifilter is valid only when 'external' is set\n");
+		return -1;
+	}
+
 	if (metadata && VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID)) {
 		fprintf(stderr, "vxlan: both 'external' and vni cannot be specified\n");
 		return -1;
 	}
 
-	if (!metadata && !VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID) && !set_op) {
+	if (!metadata && !vnifilter && !VXLAN_ATTRSET(attrs, IFLA_VXLAN_ID) && !set_op) {
 		fprintf(stderr, "vxlan: missing virtual network identifier\n");
 		return -1;
 	}
@@ -420,6 +436,11 @@ static void vxlan_print_opt(struct link_
 		print_bool(PRINT_ANY, "external", "external ", true);
 	}
 
+	if (tb[IFLA_VXLAN_VNIFILTER] &&
+	    rta_getattr_u8(tb[IFLA_VXLAN_VNIFILTER])) {
+		print_bool(PRINT_ANY, "vnifilter", "vnifilter", true);
+	}
+
 	if (tb[IFLA_VXLAN_ID] &&
 	    RTA_PAYLOAD(tb[IFLA_VXLAN_ID]) >= sizeof(__u32)) {
 		print_uint(PRINT_ANY, "id", "id %u ", rta_getattr_u32(tb[IFLA_VXLAN_ID]));
diff -pruN 5.15.0-1/ip/iplink_xstats.c 5.19.0-1/ip/iplink_xstats.c
--- 5.15.0-1/ip/iplink_xstats.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/iplink_xstats.c	2022-08-02 18:36:33.000000000 +0000
@@ -65,7 +65,8 @@ int iplink_ifla_xstats(int argc, char **
 	else
 		filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_LINK_XSTATS);
 
-	if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
+	if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask,
+				      NULL, NULL) < 0) {
 		perror("Cannont send dump request");
 		return -1;
 	}
diff -pruN 5.15.0-1/ip/ipmonitor.c 5.19.0-1/ip/ipmonitor.c
--- 5.15.0-1/ip/ipmonitor.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/ipmonitor.c	2022-08-02 18:36:33.000000000 +0000
@@ -22,6 +22,7 @@
 
 #include "utils.h"
 #include "ip_common.h"
+#include "nh_common.h"
 
 static void usage(void) __attribute__((noreturn));
 static int prefix_banner;
@@ -33,7 +34,7 @@ static void usage(void)
 		"Usage: ip monitor [ all | OBJECTS ] [ FILE ] [ label ] [ all-nsid ]\n"
 		"                  [ dev DEVICE ]\n"
 		"OBJECTS :=  address | link | mroute | neigh | netconf |\n"
-		"            nexthop | nsid | prefix | route | rule\n"
+		"            nexthop | nsid | prefix | route | rule | stats\n"
 		"FILE := file FILENAME\n");
 	exit(-1);
 }
@@ -88,7 +89,7 @@ static int accept_msg(struct rtnl_ctrl_d
 	case RTM_NEWNEXTHOP:
 	case RTM_DELNEXTHOP:
 		print_headers(fp, "[NEXTHOP]", ctrl);
-		print_nexthop(n, arg);
+		print_cache_nexthop(n, arg, true);
 		return 0;
 
 	case RTM_NEWNEXTHOPBUCKET:
@@ -157,6 +158,11 @@ static int accept_msg(struct rtnl_ctrl_d
 		print_nsid(n, arg);
 		return 0;
 
+	case RTM_NEWSTATS:
+		print_headers(fp, "[STATS]", ctrl);
+		ipstats_print(n, arg);
+		return 0;
+
 	case NLMSG_ERROR:
 	case NLMSG_NOOP:
 	case NLMSG_DONE:
@@ -184,6 +190,7 @@ int do_ipmonitor(int argc, char **argv)
 	int lprefix = 0;
 	int lneigh = 0;
 	int lnetconf = 0;
+	int lstats = 0;
 	int lrule = 0;
 	int lnsid = 0;
 	int ifindex = 0;
@@ -252,6 +259,9 @@ int do_ipmonitor(int argc, char **argv)
 		} else if (matches(*argv, "nexthop") == 0) {
 			lnexthop = 1;
 			groups = 0;
+		} else if (strcmp(*argv, "stats") == 0) {
+			lstats = 1;
+			groups = 0;
 		} else if (strcmp(*argv, "all") == 0) {
 			prefix_banner = 1;
 		} else if (matches(*argv, "all-nsid") == 0) {
@@ -348,6 +358,11 @@ int do_ipmonitor(int argc, char **argv)
 		exit(1);
 	}
 
+	if (lstats && rtnl_add_nl_group(&rth, RTNLGRP_STATS) < 0) {
+		fprintf(stderr, "Failed to add stats group to list\n");
+		exit(1);
+	}
+
 	if (listen_all_nsid && rtnl_listen_all_nsid(&rth) < 0)
 		exit(1);
 
diff -pruN 5.15.0-1/ip/ipmptcp.c 5.19.0-1/ip/ipmptcp.c
--- 5.15.0-1/ip/ipmptcp.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/ipmptcp.c	2022-08-02 18:36:33.000000000 +0000
@@ -1,31 +1,39 @@
 // SPDX-License-Identifier: GPL-2.0
 
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
-#include <rt_names.h>
-#include <errno.h>
 
 #include <linux/genetlink.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
 #include <linux/mptcp.h>
 
 #include "utils.h"
 #include "ip_common.h"
-#include "libgenl.h"
 #include "json_print.h"
+#include "libgenl.h"
+#include "libnetlink.h"
+#include "ll_map.h"
 
 static void usage(void)
 {
 	fprintf(stderr,
 		"Usage:	ip mptcp endpoint add ADDRESS [ dev NAME ] [ id ID ]\n"
 		"				      [ port NR ] [ FLAG-LIST ]\n"
-		"	ip mptcp endpoint delete id ID\n"
+		"	ip mptcp endpoint delete id ID [ ADDRESS ]\n"
+		"	ip mptcp endpoint change [ id ID ] [ ADDRESS ] [ port NR ] CHANGE-OPT\n"
 		"	ip mptcp endpoint show [ id ID ]\n"
 		"	ip mptcp endpoint flush\n"
 		"	ip mptcp limits set [ subflows NR ] [ add_addr_accepted NR ]\n"
 		"	ip mptcp limits show\n"
 		"	ip mptcp monitor\n"
 		"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
-		"FLAG  := [ signal | subflow | backup ]\n");
+		"FLAG  := [ signal | subflow | backup | fullmesh ]\n"
+		"CHANGE-OPT := [ backup | nobackup | fullmesh | nofullmesh ]\n");
 
 	exit(-1);
 }
@@ -39,6 +47,8 @@ static int genl_family = -1;
 	GENL_REQUEST(_req, MPTCP_BUFLEN, genl_family, 0,	\
 		     MPTCP_PM_VER, _cmd, _flags)
 
+#define MPTCP_PM_ADDR_FLAG_NONE 0x0
+
 /* Mapping from argument to address flag mask */
 static const struct {
 	const char *name;
@@ -47,6 +57,9 @@ static const struct {
 	{ "signal",		MPTCP_PM_ADDR_FLAG_SIGNAL },
 	{ "subflow",		MPTCP_PM_ADDR_FLAG_SUBFLOW },
 	{ "backup",		MPTCP_PM_ADDR_FLAG_BACKUP },
+	{ "fullmesh",		MPTCP_PM_ADDR_FLAG_FULLMESH },
+	{ "nobackup",		MPTCP_PM_ADDR_FLAG_NONE },
+	{ "nofullmesh",		MPTCP_PM_ADDR_FLAG_NONE }
 };
 
 static void print_mptcp_addr_flags(unsigned int flags)
@@ -89,9 +102,11 @@ static int get_flags(const char *arg, __
 	return -1;
 }
 
-static int mptcp_parse_opt(int argc, char **argv, struct nlmsghdr *n,
-			 bool adding)
+static int mptcp_parse_opt(int argc, char **argv, struct nlmsghdr *n, int cmd)
 {
+	bool setting = cmd == MPTCP_PM_CMD_SET_FLAGS;
+	bool adding = cmd == MPTCP_PM_CMD_ADD_ADDR;
+	bool deling = cmd == MPTCP_PM_CMD_DEL_ADDR;
 	struct rtattr *attr_addr;
 	bool addr_set = false;
 	inet_prefix address;
@@ -104,6 +119,17 @@ static int mptcp_parse_opt(int argc, cha
 	ll_init_map(&rth);
 	while (argc > 0) {
 		if (get_flags(*argv, &flags) == 0) {
+			if (adding &&
+			    (flags & MPTCP_PM_ADDR_FLAG_SIGNAL) &&
+			    (flags & MPTCP_PM_ADDR_FLAG_FULLMESH))
+				invarg("flags mustn't have both signal and fullmesh", *argv);
+
+			/* allow changing the 'backup' and 'fullmesh' flags only */
+			if (setting &&
+			    (flags & ~(MPTCP_PM_ADDR_FLAG_BACKUP |
+				       MPTCP_PM_ADDR_FLAG_FULLMESH)))
+				invarg("invalid flags, backup and fullmesh only", *argv);
+
 		} else if (matches(*argv, "id") == 0) {
 			NEXT_ARG();
 
@@ -140,12 +166,21 @@ static int mptcp_parse_opt(int argc, cha
 	if (!addr_set && adding)
 		missarg("ADDRESS");
 
-	if (!id_set && !adding)
+	if (!id_set && deling) {
 		missarg("ID");
+	} else if (id_set && deling) {
+		if (id && addr_set)
+			invarg("invalid for non-zero id address\n", "ADDRESS");
+		else if (!id && !addr_set)
+			invarg("address is needed for deleting id 0 address\n", "ID");
+	}
 
-	if (port && !(flags & MPTCP_PM_ADDR_FLAG_SIGNAL))
+	if (adding && port && !(flags & MPTCP_PM_ADDR_FLAG_SIGNAL))
 		invarg("flags must have signal when using port", "port");
 
+	if (setting && id_set && port)
+		invarg("port can't be used with id", "port");
+
 	attr_addr = addattr_nest(n, MPTCP_BUFLEN,
 				 MPTCP_PM_ATTR_ADDR | NLA_F_NESTED);
 	if (id_set)
@@ -176,7 +211,7 @@ static int mptcp_addr_modify(int argc, c
 	MPTCP_REQUEST(req, cmd, NLM_F_REQUEST);
 	int ret;
 
-	ret = mptcp_parse_opt(argc, argv, &req.n, cmd == MPTCP_PM_CMD_ADD_ADDR);
+	ret = mptcp_parse_opt(argc, argv, &req.n, cmd);
 	if (ret)
 		return ret;
 
@@ -292,14 +327,19 @@ static int mptcp_addr_show(int argc, cha
 	if (argc <= 0)
 		return mptcp_addr_dump();
 
-	ret = mptcp_parse_opt(argc, argv, &req.n, false);
+	ret = mptcp_parse_opt(argc, argv, &req.n, MPTCP_PM_CMD_GET_ADDR);
 	if (ret)
 		return ret;
 
 	if (rtnl_talk(&genl_rth, &req.n, &answer) < 0)
 		return -2;
 
-	return print_mptcp_addr(answer, stdout);
+	new_json_obj(json);
+	ret = print_mptcp_addr(answer, stdout);
+	delete_json_obj();
+	free(answer);
+	fflush(stdout);
+	return ret;
 }
 
 static int mptcp_addr_flush(int argc, char **argv)
@@ -396,9 +436,13 @@ static int mptcp_limit_get_set(int argc,
 	if (rtnl_talk(&genl_rth, &req.n, do_get ? &answer : NULL) < 0)
 		return -2;
 
-	if (do_get)
-		return print_mptcp_limit(answer, stdout);
-	return 0;
+	ret = 0;
+	if (do_get) {
+		ret = print_mptcp_limit(answer, stdout);
+		free(answer);
+	}
+
+	return ret;
 }
 
 static const char * const event_to_str[] = {
@@ -524,6 +568,9 @@ int do_mptcp(int argc, char **argv)
 		if (matches(*argv, "add") == 0)
 			return mptcp_addr_modify(argc-1, argv+1,
 						 MPTCP_PM_CMD_ADD_ADDR);
+		if (matches(*argv, "change") == 0)
+			return mptcp_addr_modify(argc-1, argv+1,
+						 MPTCP_PM_CMD_SET_FLAGS);
 		if (matches(*argv, "delete") == 0)
 			return mptcp_addr_modify(argc-1, argv+1,
 						 MPTCP_PM_CMD_DEL_ADDR);
diff -pruN 5.15.0-1/ip/ipneigh.c 5.19.0-1/ip/ipneigh.c
--- 5.15.0-1/ip/ipneigh.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/ipneigh.c	2022-08-02 18:36:33.000000000 +0000
@@ -51,12 +51,12 @@ static void usage(void)
 	fprintf(stderr,
 		"Usage: ip neigh { add | del | change | replace }\n"
 		"                { ADDR [ lladdr LLADDR ] [ nud STATE ] proxy ADDR }\n"
-		"                [ dev DEV ] [ router ] [ extern_learn ] [ protocol PROTO ]\n"
+		"                [ dev DEV ] [ router ] [ use ] [ managed ] [ extern_learn ]\n"
+		"                [ protocol PROTO ]\n"
 		"\n"
-		"       ip neigh { show | flush } [ proxy ] [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n"
-		"                                 [ vrf NAME ]\n"
-		"\n"
-		"       ip neigh get { ADDR | proxy ADDR } dev DEV\n"
+		"	ip neigh { show | flush } [ proxy ] [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n"
+		"				  [ vrf NAME ] [ nomaster ]\n"
+		"	ip neigh get { ADDR | proxy ADDR } dev DEV\n"
 		"\n"
 		"STATE := { delay | failed | incomplete | noarp | none |\n"
 		"           permanent | probe | reachable | stale }\n");
@@ -116,6 +116,7 @@ static int ipneigh_modify(int cmd, int f
 		.ndm.ndm_family = preferred_family,
 		.ndm.ndm_state = NUD_PERMANENT,
 	};
+	__u32 ext_flags = 0;
 	char  *dev = NULL;
 	int dst_ok = 0;
 	int dev_ok = 0;
@@ -149,6 +150,11 @@ static int ipneigh_modify(int cmd, int f
 			req.ndm.ndm_flags |= NTF_PROXY;
 		} else if (strcmp(*argv, "router") == 0) {
 			req.ndm.ndm_flags |= NTF_ROUTER;
+		} else if (strcmp(*argv, "use") == 0) {
+			req.ndm.ndm_flags |= NTF_USE;
+		} else if (strcmp(*argv, "managed") == 0) {
+			ext_flags |= NTF_EXT_MANAGED;
+			req.ndm.ndm_state = NUD_NONE;
 		} else if (matches(*argv, "extern_learn") == 0) {
 			req.ndm.ndm_flags |= NTF_EXT_LEARNED;
 		} else if (strcmp(*argv, "dev") == 0) {
@@ -184,7 +190,10 @@ static int ipneigh_modify(int cmd, int f
 	req.ndm.ndm_family = dst.family;
 	if (addattr_l(&req.n, sizeof(req), NDA_DST, &dst.data, dst.bytelen) < 0)
 		return -1;
-
+	if (ext_flags &&
+	    addattr_l(&req.n, sizeof(req), NDA_FLAGS_EXT, &ext_flags,
+		      sizeof(ext_flags)) < 0)
+		return -1;
 	if (lla && strcmp(lla, "null")) {
 		char llabuf[20];
 		int l;
@@ -236,7 +245,7 @@ static void print_neigh_state(unsigned i
 #define PRINT_FLAG(f)						\
 	if (nud & NUD_##f) {					\
 		nud &= ~NUD_##f;				\
-		print_string(PRINT_ANY, NULL, " %s", #f);	\
+		print_string(PRINT_ANY, NULL, "%s ", #f);	\
 	}
 
 	PRINT_FLAG(INCOMPLETE);
@@ -304,6 +313,7 @@ int print_neigh(struct nlmsghdr *n, void
 	int len = n->nlmsg_len;
 	struct rtattr *tb[NDA_MAX+1];
 	static int logit = 1;
+	__u32 ext_flags = 0;
 	__u8 protocol = 0;
 
 	if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH &&
@@ -347,6 +357,8 @@ int print_neigh(struct nlmsghdr *n, void
 
 	if (tb[NDA_PROTOCOL])
 		protocol = rta_getattr_u8(tb[NDA_PROTOCOL]);
+	if (tb[NDA_FLAGS_EXT])
+		ext_flags = rta_getattr_u32(tb[NDA_FLAGS_EXT]);
 
 	if (filter.protocol && filter.protocol != protocol)
 		return 0;
@@ -424,27 +436,26 @@ int print_neigh(struct nlmsghdr *n, void
 			fprintf(fp, "lladdr ");
 
 		print_color_string(PRINT_ANY, COLOR_MAC,
-				   "lladdr", "%s", lladdr);
+				   "lladdr", "%s ", lladdr);
 	}
 
 	if (r->ndm_flags & NTF_ROUTER)
-		print_null(PRINT_ANY, "router", " %s", "router");
-
+		print_null(PRINT_ANY, "router", "%s ", "router");
 	if (r->ndm_flags & NTF_PROXY)
-		print_null(PRINT_ANY, "proxy", " %s", "proxy");
-
+		print_null(PRINT_ANY, "proxy", "%s ", "proxy");
+	if (ext_flags & NTF_EXT_MANAGED)
+		print_null(PRINT_ANY, "managed", "%s ", "managed");
 	if (r->ndm_flags & NTF_EXT_LEARNED)
-		print_null(PRINT_ANY, "extern_learn", " %s ", "extern_learn");
-
+		print_null(PRINT_ANY, "extern_learn", "%s ", "extern_learn");
 	if (r->ndm_flags & NTF_OFFLOADED)
-		print_null(PRINT_ANY, "offload", " %s", "offload");
+		print_null(PRINT_ANY, "offload", "%s ", "offload");
 
 	if (show_stats) {
 		if (tb[NDA_CACHEINFO])
 			print_cacheinfo(RTA_DATA(tb[NDA_CACHEINFO]));
 
 		if (tb[NDA_PROBES])
-			print_uint(PRINT_ANY, "probes", " probes %u",
+			print_uint(PRINT_ANY, "probes", "probes %u ",
 				   rta_getattr_u32(tb[NDA_PROBES]));
 	}
 
@@ -454,7 +465,7 @@ int print_neigh(struct nlmsghdr *n, void
 	if (protocol) {
 		SPRINT_BUF(b1);
 
-		print_string(PRINT_ANY, "protocol", " proto %s ",
+		print_string(PRINT_ANY, "protocol", "proto %s ",
 			     rtnl_rtprot_n2a(protocol, b1, sizeof(b1)));
 	}
 
@@ -536,6 +547,8 @@ static int do_show_or_flush(int argc, ch
 			if (!name_is_vrf(*argv))
 				invarg("Not a valid VRF name\n", *argv);
 			filter.master = ifindex;
+		} else if (strcmp(*argv, "nomaster") == 0) {
+			filter.master = -1;
 		} else if (strcmp(*argv, "unused") == 0) {
 			filter.unused_only = 1;
 		} else if (strcmp(*argv, "nud") == 0) {
@@ -718,8 +731,10 @@ static int ipneigh_get(int argc, char **
 	ipneigh_reset_filter(0);
 	if (print_neigh(answer, stdout) < 0) {
 		fprintf(stderr, "An error :-)\n");
+		free(answer);
 		return -1;
 	}
+	free(answer);
 
 	return 0;
 }
diff -pruN 5.15.0-1/ip/ipnexthop.c 5.19.0-1/ip/ipnexthop.c
--- 5.15.0-1/ip/ipnexthop.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/ipnexthop.c	2022-08-02 18:36:33.000000000 +0000
@@ -6,6 +6,7 @@
  */
 
 #include <linux/nexthop.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 #include <rt_names.h>
@@ -13,6 +14,7 @@
 
 #include "utils.h"
 #include "ip_common.h"
+#include "nh_common.h"
 
 static struct {
 	unsigned int flushed;
@@ -33,6 +35,9 @@ enum {
 #define RTM_NHA(h)  ((struct rtattr *)(((char *)(h)) + \
 			NLMSG_ALIGN(sizeof(struct nhmsg))))
 
+static struct hlist_head nh_cache[NH_CACHE_SIZE];
+static struct rtnl_handle nh_cache_rth = { .fd = -1 };
+
 static void usage(void) __attribute__((noreturn));
 
 static void usage(void)
@@ -212,28 +217,29 @@ out:
 	return rc;
 }
 
-static void print_nh_group(FILE *fp, const struct rtattr *grps_attr)
+static bool __valid_nh_group_attr(const struct rtattr *g_attr)
 {
-	struct nexthop_grp *nhg = RTA_DATA(grps_attr);
-	int num = RTA_PAYLOAD(grps_attr) / sizeof(*nhg);
-	int i;
+	int num = RTA_PAYLOAD(g_attr) / sizeof(struct nexthop_grp);
 
-	if (!num || num * sizeof(*nhg) != RTA_PAYLOAD(grps_attr)) {
-		fprintf(fp, "<invalid nexthop group>");
-		return;
-	}
+	return num && num * sizeof(struct nexthop_grp) == RTA_PAYLOAD(g_attr);
+}
+
+static void print_nh_group(const struct nh_entry *nhe)
+{
+	int i;
 
 	open_json_array(PRINT_JSON, "group");
 	print_string(PRINT_FP, NULL, "%s", "group ");
-	for (i = 0; i < num; ++i) {
+	for (i = 0; i < nhe->nh_groups_cnt; ++i) {
 		open_json_object(NULL);
 
 		if (i)
 			print_string(PRINT_FP, NULL, "%s", "/");
 
-		print_uint(PRINT_ANY, "id", "%u", nhg[i].id);
-		if (nhg[i].weight)
-			print_uint(PRINT_ANY, "weight", ",%u", nhg[i].weight + 1);
+		print_uint(PRINT_ANY, "id", "%u", nhe->nh_groups[i].id);
+		if (nhe->nh_groups[i].weight)
+			print_uint(PRINT_ANY, "weight", ",%u",
+				   nhe->nh_groups[i].weight + 1);
 
 		close_json_object();
 	}
@@ -253,50 +259,59 @@ static const char *nh_group_type_name(__
 	}
 }
 
-static void print_nh_group_type(FILE *fp, const struct rtattr *grp_type_attr)
+static void print_nh_group_type(__u16 nh_grp_type)
 {
-	__u16 type = rta_getattr_u16(grp_type_attr);
-
-	if (type == NEXTHOP_GRP_TYPE_MPATH)
+	if (nh_grp_type == NEXTHOP_GRP_TYPE_MPATH)
 		/* Do not print type in order not to break existing output. */
 		return;
 
-	print_string(PRINT_ANY, "type", "type %s ", nh_group_type_name(type));
+	print_string(PRINT_ANY, "type", "type %s ", nh_group_type_name(nh_grp_type));
 }
 
-static void print_nh_res_group(FILE *fp, const struct rtattr *res_grp_attr)
+static void parse_nh_res_group_rta(const struct rtattr *res_grp_attr,
+				   struct nha_res_grp *res_grp)
 {
 	struct rtattr *tb[NHA_RES_GROUP_MAX + 1];
 	struct rtattr *rta;
-	struct timeval tv;
 
+	memset(res_grp, 0, sizeof(*res_grp));
 	parse_rtattr_nested(tb, NHA_RES_GROUP_MAX, res_grp_attr);
 
-	open_json_object("resilient_args");
-
 	if (tb[NHA_RES_GROUP_BUCKETS])
-		print_uint(PRINT_ANY, "buckets", "buckets %u ",
-			   rta_getattr_u16(tb[NHA_RES_GROUP_BUCKETS]));
+		res_grp->buckets = rta_getattr_u16(tb[NHA_RES_GROUP_BUCKETS]);
 
 	if (tb[NHA_RES_GROUP_IDLE_TIMER]) {
 		rta = tb[NHA_RES_GROUP_IDLE_TIMER];
-		__jiffies_to_tv(&tv, rta_getattr_u32(rta));
-		print_tv(PRINT_ANY, "idle_timer", "idle_timer %g ", &tv);
+		res_grp->idle_timer = rta_getattr_u32(rta);
 	}
 
 	if (tb[NHA_RES_GROUP_UNBALANCED_TIMER]) {
 		rta = tb[NHA_RES_GROUP_UNBALANCED_TIMER];
-		__jiffies_to_tv(&tv, rta_getattr_u32(rta));
-		print_tv(PRINT_ANY, "unbalanced_timer", "unbalanced_timer %g ",
-			 &tv);
+		res_grp->unbalanced_timer = rta_getattr_u32(rta);
 	}
 
 	if (tb[NHA_RES_GROUP_UNBALANCED_TIME]) {
 		rta = tb[NHA_RES_GROUP_UNBALANCED_TIME];
-		__jiffies_to_tv(&tv, rta_getattr_u32(rta));
-		print_tv(PRINT_ANY, "unbalanced_time", "unbalanced_time %g ",
-			 &tv);
+		res_grp->unbalanced_time = rta_getattr_u64(rta);
 	}
+}
+
+static void print_nh_res_group(const struct nha_res_grp *res_grp)
+{
+	struct timeval tv;
+
+	open_json_object("resilient_args");
+
+	print_uint(PRINT_ANY, "buckets", "buckets %u ", res_grp->buckets);
+
+	 __jiffies_to_tv(&tv, res_grp->idle_timer);
+	print_tv(PRINT_ANY, "idle_timer", "idle_timer %g ", &tv);
+
+	__jiffies_to_tv(&tv, res_grp->unbalanced_timer);
+	print_tv(PRINT_ANY, "unbalanced_timer", "unbalanced_timer %g ", &tv);
+
+	__jiffies_to_tv(&tv, res_grp->unbalanced_time);
+	print_tv(PRINT_ANY, "unbalanced_time", "unbalanced_time %g ", &tv);
 
 	close_json_object();
 }
@@ -328,86 +343,366 @@ static void print_nh_res_bucket(FILE *fp
 	close_json_object();
 }
 
-int print_nexthop(struct nlmsghdr *n, void *arg)
+static void ipnh_destroy_entry(struct nh_entry *nhe)
+{
+	if (nhe->nh_encap)
+		free(nhe->nh_encap);
+	if (nhe->nh_groups)
+		free(nhe->nh_groups);
+}
+
+/* parse nhmsg into nexthop entry struct which must be destroyed by
+ * ipnh_destroy_enty when it's not needed anymore
+ */
+static int ipnh_parse_nhmsg(FILE *fp, const struct nhmsg *nhm, int len,
+			    struct nh_entry *nhe)
 {
-	struct nhmsg *nhm = NLMSG_DATA(n);
 	struct rtattr *tb[NHA_MAX+1];
-	FILE *fp = (FILE *)arg;
-	int len;
+	int err = 0;
 
-	SPRINT_BUF(b1);
+	memset(nhe, 0, sizeof(*nhe));
+	parse_rtattr_flags(tb, NHA_MAX, RTM_NHA(nhm), len, NLA_F_NESTED);
 
-	if (n->nlmsg_type != RTM_DELNEXTHOP &&
-	    n->nlmsg_type != RTM_NEWNEXTHOP) {
-		fprintf(stderr, "Not a nexthop: %08x %08x %08x\n",
-			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
-		return -1;
+	if (tb[NHA_ID])
+		nhe->nh_id = rta_getattr_u32(tb[NHA_ID]);
+
+	if (tb[NHA_OIF])
+		nhe->nh_oif = rta_getattr_u32(tb[NHA_OIF]);
+
+	if (tb[NHA_GROUP_TYPE])
+		nhe->nh_grp_type = rta_getattr_u16(tb[NHA_GROUP_TYPE]);
+
+	if (tb[NHA_GATEWAY]) {
+		if (RTA_PAYLOAD(tb[NHA_GATEWAY]) > sizeof(nhe->nh_gateway)) {
+			fprintf(fp, "<nexthop id %u invalid gateway length %lu>\n",
+				nhe->nh_id, RTA_PAYLOAD(tb[NHA_GATEWAY]));
+			err = -EINVAL;
+			goto out_err;
+		}
+		nhe->nh_gateway_len = RTA_PAYLOAD(tb[NHA_GATEWAY]);
+		memcpy(&nhe->nh_gateway, RTA_DATA(tb[NHA_GATEWAY]),
+		       RTA_PAYLOAD(tb[NHA_GATEWAY]));
 	}
 
-	len = n->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
-	if (len < 0) {
-		close_json_object();
-		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
-		return -1;
+	if (tb[NHA_ENCAP]) {
+		nhe->nh_encap = malloc(RTA_LENGTH(RTA_PAYLOAD(tb[NHA_ENCAP])));
+		if (!nhe->nh_encap) {
+			err = -ENOMEM;
+			goto out_err;
+		}
+		memcpy(nhe->nh_encap, tb[NHA_ENCAP],
+		       RTA_LENGTH(RTA_PAYLOAD(tb[NHA_ENCAP])));
+		memcpy(&nhe->nh_encap_type, tb[NHA_ENCAP_TYPE],
+		       sizeof(nhe->nh_encap_type));
 	}
 
-	if (filter.proto && filter.proto != nhm->nh_protocol)
-		return 0;
+	if (tb[NHA_GROUP]) {
+		if (!__valid_nh_group_attr(tb[NHA_GROUP])) {
+			fprintf(fp, "<nexthop id %u invalid nexthop group>",
+				nhe->nh_id);
+			err = -EINVAL;
+			goto out_err;
+		}
 
-	parse_rtattr_flags(tb, NHA_MAX, RTM_NHA(nhm), len, NLA_F_NESTED);
+		nhe->nh_groups = malloc(RTA_PAYLOAD(tb[NHA_GROUP]));
+		if (!nhe->nh_groups) {
+			err = -ENOMEM;
+			goto out_err;
+		}
+		nhe->nh_groups_cnt = RTA_PAYLOAD(tb[NHA_GROUP]) /
+				     sizeof(struct nexthop_grp);
+		memcpy(nhe->nh_groups, RTA_DATA(tb[NHA_GROUP]),
+		       RTA_PAYLOAD(tb[NHA_GROUP]));
+	}
 
-	open_json_object(NULL);
+	if (tb[NHA_RES_GROUP]) {
+		parse_nh_res_group_rta(tb[NHA_RES_GROUP], &nhe->nh_res_grp);
+		nhe->nh_has_res_grp = true;
+	}
 
-	if (n->nlmsg_type == RTM_DELNEXTHOP)
+	nhe->nh_blackhole = !!tb[NHA_BLACKHOLE];
+	nhe->nh_fdb = !!tb[NHA_FDB];
+
+	nhe->nh_family = nhm->nh_family;
+	nhe->nh_protocol = nhm->nh_protocol;
+	nhe->nh_scope = nhm->nh_scope;
+	nhe->nh_flags = nhm->nh_flags;
+
+	return 0;
+
+out_err:
+	ipnh_destroy_entry(nhe);
+	return err;
+}
+
+static void __print_nexthop_entry(FILE *fp, const char *jsobj,
+				  struct nh_entry *nhe,
+				  bool deleted)
+{
+	SPRINT_BUF(b1);
+
+	open_json_object(jsobj);
+
+	if (deleted)
 		print_bool(PRINT_ANY, "deleted", "Deleted ", true);
 
-	if (tb[NHA_ID])
-		print_uint(PRINT_ANY, "id", "id %u ",
-			   rta_getattr_u32(tb[NHA_ID]));
+	print_uint(PRINT_ANY, "id", "id %u ", nhe->nh_id);
 
-	if (tb[NHA_GROUP])
-		print_nh_group(fp, tb[NHA_GROUP]);
+	if (nhe->nh_groups)
+		print_nh_group(nhe);
 
-	if (tb[NHA_GROUP_TYPE])
-		print_nh_group_type(fp, tb[NHA_GROUP_TYPE]);
+	print_nh_group_type(nhe->nh_grp_type);
 
-	if (tb[NHA_RES_GROUP])
-		print_nh_res_group(fp, tb[NHA_RES_GROUP]);
+	if (nhe->nh_has_res_grp)
+		print_nh_res_group(&nhe->nh_res_grp);
 
-	if (tb[NHA_ENCAP])
-		lwt_print_encap(fp, tb[NHA_ENCAP_TYPE], tb[NHA_ENCAP]);
+	if (nhe->nh_encap)
+		lwt_print_encap(fp, &nhe->nh_encap_type.rta, nhe->nh_encap);
 
-	if (tb[NHA_GATEWAY])
-		print_rta_gateway(fp, nhm->nh_family, tb[NHA_GATEWAY]);
+	if (nhe->nh_gateway_len)
+		__print_rta_gateway(fp, nhe->nh_family,
+				    format_host(nhe->nh_family,
+				    nhe->nh_gateway_len,
+				    &nhe->nh_gateway));
 
-	if (tb[NHA_OIF])
-		print_rta_if(fp, tb[NHA_OIF], "dev");
+	if (nhe->nh_oif)
+		print_rta_ifidx(fp, nhe->nh_oif, "dev");
 
-	if (nhm->nh_scope != RT_SCOPE_UNIVERSE || show_details > 0) {
+	if (nhe->nh_scope != RT_SCOPE_UNIVERSE || show_details > 0) {
 		print_string(PRINT_ANY, "scope", "scope %s ",
-			     rtnl_rtscope_n2a(nhm->nh_scope, b1, sizeof(b1)));
+			     rtnl_rtscope_n2a(nhe->nh_scope, b1, sizeof(b1)));
 	}
 
-	if (tb[NHA_BLACKHOLE])
+	if (nhe->nh_blackhole)
 		print_null(PRINT_ANY, "blackhole", "blackhole ", NULL);
 
-	if (nhm->nh_protocol != RTPROT_UNSPEC || show_details > 0) {
+	if (nhe->nh_protocol != RTPROT_UNSPEC || show_details > 0) {
 		print_string(PRINT_ANY, "protocol", "proto %s ",
-			     rtnl_rtprot_n2a(nhm->nh_protocol, b1, sizeof(b1)));
+			     rtnl_rtprot_n2a(nhe->nh_protocol, b1, sizeof(b1)));
 	}
 
-	print_rt_flags(fp, nhm->nh_flags);
+	print_rt_flags(fp, nhe->nh_flags);
 
-	if (tb[NHA_FDB])
+	if (nhe->nh_fdb)
 		print_null(PRINT_ANY, "fdb", "fdb", NULL);
 
-	print_string(PRINT_FP, NULL, "%s", "\n");
 	close_json_object();
+}
+
+static int  __ipnh_get_id(struct rtnl_handle *rthp, __u32 nh_id,
+			  struct nlmsghdr **answer)
+{
+	struct {
+		struct nlmsghdr n;
+		struct nhmsg	nhm;
+		char		buf[1024];
+	} req = {
+		.n.nlmsg_len	= NLMSG_LENGTH(sizeof(struct nhmsg)),
+		.n.nlmsg_flags	= NLM_F_REQUEST,
+		.n.nlmsg_type	= RTM_GETNEXTHOP,
+		.nhm.nh_family	= preferred_family,
+	};
+
+	addattr32(&req.n, sizeof(req), NHA_ID, nh_id);
+
+	return rtnl_talk(rthp, &req.n, answer);
+}
+
+static struct hlist_head *ipnh_cache_head(__u32 nh_id)
+{
+	nh_id ^= nh_id >> 20;
+	nh_id ^= nh_id >> 10;
+
+	return &nh_cache[nh_id % NH_CACHE_SIZE];
+}
+
+static void ipnh_cache_link_entry(struct nh_entry *nhe)
+{
+	struct hlist_head *head = ipnh_cache_head(nhe->nh_id);
+
+	hlist_add_head(&nhe->nh_hash, head);
+}
+
+static void ipnh_cache_unlink_entry(struct nh_entry *nhe)
+{
+	hlist_del(&nhe->nh_hash);
+}
+
+static struct nh_entry *ipnh_cache_get(__u32 nh_id)
+{
+	struct hlist_head *head = ipnh_cache_head(nh_id);
+	struct nh_entry *nhe;
+	struct hlist_node *n;
+
+	hlist_for_each(n, head) {
+		nhe = container_of(n, struct nh_entry, nh_hash);
+		if (nhe->nh_id == nh_id)
+			return nhe;
+	}
+
+	return NULL;
+}
+
+static int __ipnh_cache_parse_nlmsg(const struct nlmsghdr *n,
+				    struct nh_entry *nhe)
+{
+	int err, len;
+
+	len = n->nlmsg_len - NLMSG_SPACE(sizeof(struct nhmsg));
+	if (len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -EINVAL;
+	}
+
+	err = ipnh_parse_nhmsg(stderr, NLMSG_DATA(n), len, nhe);
+	if (err) {
+		fprintf(stderr, "Error parsing nexthop: %s\n", strerror(-err));
+		return err;
+	}
+
+	return 0;
+}
+
+static struct nh_entry *ipnh_cache_add(__u32 nh_id)
+{
+	struct nlmsghdr *answer = NULL;
+	struct nh_entry *nhe = NULL;
+
+	if (nh_cache_rth.fd < 0 && rtnl_open(&nh_cache_rth, 0) < 0) {
+		nh_cache_rth.fd = -1;
+		goto out;
+	}
+
+	if (__ipnh_get_id(&nh_cache_rth, nh_id, &answer) < 0)
+		goto out;
+
+	nhe = malloc(sizeof(*nhe));
+	if (!nhe)
+		goto out;
+
+	if (__ipnh_cache_parse_nlmsg(answer, nhe))
+		goto out_free_nhe;
+
+	ipnh_cache_link_entry(nhe);
+
+out:
+	if (answer)
+		free(answer);
+
+	return nhe;
+
+out_free_nhe:
+	free(nhe);
+	nhe = NULL;
+	goto out;
+}
+
+static void ipnh_cache_del(struct nh_entry *nhe)
+{
+	ipnh_cache_unlink_entry(nhe);
+	ipnh_destroy_entry(nhe);
+	free(nhe);
+}
+
+/* update, add or delete a nexthop entry based on nlmsghdr */
+static int ipnh_cache_process_nlmsg(const struct nlmsghdr *n,
+				    struct nh_entry *new_nhe)
+{
+	struct nh_entry *nhe;
+
+	nhe = ipnh_cache_get(new_nhe->nh_id);
+	switch (n->nlmsg_type) {
+	case RTM_DELNEXTHOP:
+		if (nhe)
+			ipnh_cache_del(nhe);
+		ipnh_destroy_entry(new_nhe);
+		break;
+	case RTM_NEWNEXTHOP:
+		if (!nhe) {
+			nhe = malloc(sizeof(*nhe));
+			if (!nhe) {
+				ipnh_destroy_entry(new_nhe);
+				return -1;
+			}
+		} else {
+			/* this allows us to save 1 allocation on updates by
+			 * reusing the old nh entry, but we need to cleanup its
+			 * internal storage
+			 */
+			ipnh_cache_unlink_entry(nhe);
+			ipnh_destroy_entry(nhe);
+		}
+		memcpy(nhe, new_nhe, sizeof(*nhe));
+		ipnh_cache_link_entry(nhe);
+		break;
+	}
+
+	return 0;
+}
+
+void print_cache_nexthop_id(FILE *fp, const char *fp_prefix, const char *jsobj,
+			    __u32 nh_id)
+{
+	struct nh_entry *nhe = ipnh_cache_get(nh_id);
+
+	if (!nhe) {
+		nhe = ipnh_cache_add(nh_id);
+		if (!nhe)
+			return;
+	}
+
+	if (fp_prefix)
+		print_string(PRINT_FP, NULL, "%s", fp_prefix);
+	__print_nexthop_entry(fp, jsobj, nhe, false);
+}
+
+int print_cache_nexthop(struct nlmsghdr *n, void *arg, bool process_cache)
+{
+	struct nhmsg *nhm = NLMSG_DATA(n);
+	FILE *fp = (FILE *)arg;
+	struct nh_entry nhe;
+	int len, err;
+
+	if (n->nlmsg_type != RTM_DELNEXTHOP &&
+	    n->nlmsg_type != RTM_NEWNEXTHOP) {
+		fprintf(stderr, "Not a nexthop: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+		return -1;
+	}
+
+	len = n->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
+	if (len < 0) {
+		close_json_object();
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+		return -1;
+	}
+
+	if (filter.proto && filter.proto != nhm->nh_protocol)
+		return 0;
+
+	err = ipnh_parse_nhmsg(fp, nhm, len, &nhe);
+	if (err) {
+		close_json_object();
+		fprintf(stderr, "Error parsing nexthop: %s\n", strerror(-err));
+		return -1;
+	}
+	__print_nexthop_entry(fp, NULL, &nhe, n->nlmsg_type == RTM_DELNEXTHOP);
+	print_string(PRINT_FP, NULL, "%s", "\n");
 	fflush(fp);
 
+	if (process_cache)
+		ipnh_cache_process_nlmsg(n, &nhe);
+	else
+		ipnh_destroy_entry(&nhe);
+
 	return 0;
 }
 
+static int print_nexthop_nocache(struct nlmsghdr *n, void *arg)
+{
+	return print_cache_nexthop(n, arg, false);
+}
+
 int print_nexthop_bucket(struct nlmsghdr *n, void *arg)
 {
 	struct nhmsg *nhm = NLMSG_DATA(n);
@@ -546,7 +841,7 @@ static void parse_nh_group_type_res(stru
 
 			NEXT_ARG();
 			if (get_unsigned(&idle_timer, *argv, 0) ||
-			    idle_timer >= ~0UL / 100)
+			    idle_timer >= UINT32_MAX / 100)
 				invarg("invalid idle timer value", *argv);
 
 			addattr32(n, maxlen, NHA_RES_GROUP_IDLE_TIMER,
@@ -556,7 +851,7 @@ static void parse_nh_group_type_res(stru
 
 			NEXT_ARG();
 			if (get_unsigned(&unbalanced_timer, *argv, 0) ||
-			    unbalanced_timer >= ~0UL / 100)
+			    unbalanced_timer >= UINT32_MAX / 100)
 				invarg("invalid unbalanced timer value", *argv);
 
 			addattr32(n, maxlen, NHA_RES_GROUP_UNBALANCED_TIMER,
@@ -712,26 +1007,14 @@ static int ipnh_modify(int cmd, unsigned
 
 static int ipnh_get_id(__u32 id)
 {
-	struct {
-		struct nlmsghdr	n;
-		struct nhmsg	nhm;
-		char		buf[1024];
-	} req = {
-		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
-		.n.nlmsg_flags = NLM_F_REQUEST,
-		.n.nlmsg_type  = RTM_GETNEXTHOP,
-		.nhm.nh_family = preferred_family,
-	};
 	struct nlmsghdr *answer;
 
-	addattr32(&req.n, sizeof(req), NHA_ID, id);
-
-	if (rtnl_talk(&rth, &req.n, &answer) < 0)
+	if (__ipnh_get_id(&rth, id, &answer) < 0)
 		return -2;
 
 	new_json_obj(json);
 
-	if (print_nexthop(answer, (void *)stdout) < 0) {
+	if (print_nexthop_nocache(answer, (void *)stdout) < 0) {
 		free(answer);
 		return -1;
 	}
@@ -816,7 +1099,7 @@ static int ipnh_list_flush(int argc, cha
 
 	new_json_obj(json);
 
-	if (rtnl_dump_filter(&rth, print_nexthop, stdout) < 0) {
+	if (rtnl_dump_filter(&rth, print_nexthop_nocache, stdout) < 0) {
 		fprintf(stderr, "Dump terminated\n");
 		return -2;
 	}
diff -pruN 5.15.0-1/ip/iproute.c 5.19.0-1/ip/iproute.c
--- 5.15.0-1/ip/iproute.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/iproute.c	2022-08-02 18:36:33.000000000 +0000
@@ -28,6 +28,7 @@
 #include "rt_names.h"
 #include "utils.h"
 #include "ip_common.h"
+#include "nh_common.h"
 
 #ifndef RTAX_RTTVAR
 #define RTAX_RTTVAR RTAX_HOPS
@@ -410,13 +411,13 @@ static void print_rt_pref(FILE *fp, unsi
 	}
 }
 
-void print_rta_if(FILE *fp, const struct rtattr *rta, const char *prefix)
+void print_rta_ifidx(FILE *fp, __u32 ifidx, const char *prefix)
 {
-	const char *ifname = ll_index_to_name(rta_getattr_u32(rta));
+	const char *ifname = ll_index_to_name(ifidx);
 
-	if (is_json_context())
+	if (is_json_context()) {
 		print_string(PRINT_JSON, prefix, NULL, ifname);
-	else {
+	} else {
 		fprintf(fp, "%s ", prefix);
 		color_fprintf(fp, COLOR_IFNAME, "%s ", ifname);
 	}
@@ -547,13 +548,11 @@ static void print_rta_newdst(FILE *fp, c
 	}
 }
 
-void print_rta_gateway(FILE *fp, unsigned char family, const struct rtattr *rta)
+void __print_rta_gateway(FILE *fp, unsigned char family, const char *gateway)
 {
-	const char *gateway = format_host_rta(family, rta);
-
-	if (is_json_context())
+	if (is_json_context()) {
 		print_string(PRINT_JSON, "gateway", NULL, gateway);
-	else {
+	} else {
 		fprintf(fp, "via ");
 		print_color_string(PRINT_FP,
 				   ifa_family_color(family),
@@ -561,6 +560,13 @@ void print_rta_gateway(FILE *fp, unsigne
 	}
 }
 
+void print_rta_gateway(FILE *fp, unsigned char family, const struct rtattr *rta)
+{
+	const char *gateway = format_host_rta(family, rta);
+
+	__print_rta_gateway(fp, family, gateway);
+}
+
 static void print_rta_via(FILE *fp, const struct rtattr *rta)
 {
 	size_t len = RTA_PAYLOAD(rta) - 2;
@@ -862,7 +868,7 @@ int print_route(struct nlmsghdr *n, void
 		print_rta_via(fp, tb[RTA_VIA]);
 
 	if (tb[RTA_OIF] && filter.oifmask != -1)
-		print_rta_if(fp, tb[RTA_OIF], "dev");
+		print_rta_ifidx(fp, rta_getattr_u32(tb[RTA_OIF]), "dev");
 
 	if (table && (table != RT_TABLE_MAIN || show_details > 0) && !filter.tb)
 		print_string(PRINT_ANY,
@@ -946,7 +952,7 @@ int print_route(struct nlmsghdr *n, void
 		print_rta_metrics(fp, tb[RTA_METRICS]);
 
 	if (tb[RTA_IIF] && filter.iifmask != -1)
-		print_rta_if(fp, tb[RTA_IIF], "iif");
+		print_rta_ifidx(fp, rta_getattr_u32(tb[RTA_IIF]), "iif");
 
 	if (tb[RTA_PREF])
 		print_rt_pref(fp, rta_getattr_u8(tb[RTA_PREF]));
@@ -963,6 +969,10 @@ int print_route(struct nlmsghdr *n, void
 				     propagate ? "enabled" : "disabled");
 	}
 
+	if (tb[RTA_NH_ID] && show_details)
+		print_cache_nexthop_id(fp, "\n\tnh_info ", "nh_info",
+				       rta_getattr_u32(tb[RTA_NH_ID]));
+
 	if (tb[RTA_MULTIPATH])
 		print_rta_multipath(fp, r, tb[RTA_MULTIPATH]);
 
diff -pruN 5.15.0-1/ip/iproute_lwtunnel.c 5.19.0-1/ip/iproute_lwtunnel.c
--- 5.15.0-1/ip/iproute_lwtunnel.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/iproute_lwtunnel.c	2022-08-02 18:36:33.000000000 +0000
@@ -210,16 +210,62 @@ static void print_encap_rpl(FILE *fp, st
 	print_rpl_srh(fp, srh);
 }
 
+static const char *ioam6_mode_types[IOAM6_IPTUNNEL_MODE_MAX + 1] = {
+	[IOAM6_IPTUNNEL_MODE_INLINE]	= "inline",
+	[IOAM6_IPTUNNEL_MODE_ENCAP]	= "encap",
+	[IOAM6_IPTUNNEL_MODE_AUTO]	= "auto",
+};
+
+static const char *format_ioam6mode_type(int mode)
+{
+	if (mode < IOAM6_IPTUNNEL_MODE_MIN ||
+	    mode > IOAM6_IPTUNNEL_MODE_MAX ||
+	    !ioam6_mode_types[mode])
+		return "<unknown>";
+
+	return ioam6_mode_types[mode];
+}
+
+static __u8 read_ioam6mode_type(const char *mode)
+{
+	__u8 i;
+
+	for (i = IOAM6_IPTUNNEL_MODE_MIN; i <= IOAM6_IPTUNNEL_MODE_MAX; i++) {
+		if (ioam6_mode_types[i] && !strcmp(mode, ioam6_mode_types[i]))
+			return i;
+	}
+
+	return 0;
+}
+
 static void print_encap_ioam6(FILE *fp, struct rtattr *encap)
 {
 	struct rtattr *tb[IOAM6_IPTUNNEL_MAX + 1];
 	struct ioam6_trace_hdr *trace;
+	__u32 freq_k, freq_n;
+	__u8 mode;
 
 	parse_rtattr_nested(tb, IOAM6_IPTUNNEL_MAX, encap);
+	if (!tb[IOAM6_IPTUNNEL_MODE] || !tb[IOAM6_IPTUNNEL_TRACE] ||
+	    !tb[IOAM6_IPTUNNEL_FREQ_K] || !tb[IOAM6_IPTUNNEL_FREQ_N])
+		return;
+
+	freq_k = rta_getattr_u32(tb[IOAM6_IPTUNNEL_FREQ_K]);
+	freq_n = rta_getattr_u32(tb[IOAM6_IPTUNNEL_FREQ_N]);
 
-	if (!tb[IOAM6_IPTUNNEL_TRACE])
+	print_uint(PRINT_ANY, "freqk", "freq %u", freq_k);
+	print_uint(PRINT_ANY, "freqn", "/%u ", freq_n);
+
+	mode = rta_getattr_u8(tb[IOAM6_IPTUNNEL_MODE]);
+	if (!tb[IOAM6_IPTUNNEL_DST] && mode != IOAM6_IPTUNNEL_MODE_INLINE)
 		return;
 
+	print_string(PRINT_ANY, "mode", "mode %s ", format_ioam6mode_type(mode));
+
+	if (mode != IOAM6_IPTUNNEL_MODE_INLINE)
+		print_string(PRINT_ANY, "tundst", "tundst %s ",
+			     rt_addr_n2a_rta(AF_INET6, tb[IOAM6_IPTUNNEL_DST]));
+
 	trace = RTA_DATA(tb[IOAM6_IPTUNNEL_TRACE]);
 
 	print_null(PRINT_ANY, "trace", "trace ", NULL);
@@ -881,26 +927,106 @@ out:
 	return ret;
 }
 
+static int parse_ioam6_freq(char *buf, __u32 *freq_k, __u32 *freq_n)
+{
+	char *s;
+	int i;
+
+	s = buf;
+	for (i = 0; *s; *s++ == '/' ? i++ : *s);
+	if (i != 1)
+		return 1;
+
+	s = strtok(buf, "/");
+	if (!s || get_u32(freq_k, s, 10))
+		return 1;
+
+	s = strtok(NULL, "/");
+	if (!s || get_u32(freq_n, s, 10))
+		return 1;
+
+	s = strtok(NULL, "/");
+	if (s)
+		return 1;
+
+	return 0;
+}
+
 static int parse_encap_ioam6(struct rtattr *rta, size_t len, int *argcp,
 			     char ***argvp)
 {
+	int ns_found = 0, argc = *argcp;
+	__u16 trace_ns, trace_size = 0;
 	struct ioam6_trace_hdr *trace;
 	char **argv = *argvp;
-	int argc = *argcp;
-	int ns_found = 0;
-	__u16 size = 0;
-	__u32 type = 0;
-	__u16 ns;
+	__u32 trace_type = 0;
+	__u32 freq_k, freq_n;
+	char buf[16] = {0};
+	inet_prefix addr;
+	__u8 mode;
+
+	if (strcmp(*argv, "freq") != 0) {
+		freq_k = IOAM6_IPTUNNEL_FREQ_MIN;
+		freq_n = IOAM6_IPTUNNEL_FREQ_MIN;
+	} else {
+		NEXT_ARG();
 
-	trace = calloc(1, sizeof(*trace));
-	if (!trace)
-		return -1;
+		if (strlen(*argv) > sizeof(buf) - 1)
+			invarg("Invalid frequency (too long)", *argv);
 
-	if (strcmp(*argv, "trace"))
+		strncpy(buf, *argv, sizeof(buf));
+
+		if (parse_ioam6_freq(buf, &freq_k, &freq_n))
+			invarg("Invalid frequency (malformed)", *argv);
+
+		if (freq_k < IOAM6_IPTUNNEL_FREQ_MIN ||
+		    freq_k > IOAM6_IPTUNNEL_FREQ_MAX)
+			invarg("Out of bound \"k\" frequency", *argv);
+
+		if (freq_n < IOAM6_IPTUNNEL_FREQ_MIN ||
+		    freq_n > IOAM6_IPTUNNEL_FREQ_MAX)
+			invarg("Out of bound \"n\" frequency", *argv);
+
+		if (freq_k > freq_n)
+			invarg("Frequency with k > n is forbidden", *argv);
+
+		NEXT_ARG();
+	}
+
+	if (strcmp(*argv, "mode") != 0) {
+		mode = IOAM6_IPTUNNEL_MODE_INLINE;
+	} else {
+		NEXT_ARG();
+
+		mode = read_ioam6mode_type(*argv);
+		if (!mode)
+			invarg("Invalid mode", *argv);
+
+		NEXT_ARG();
+	}
+
+	if (strcmp(*argv, "tundst") != 0) {
+		if (mode != IOAM6_IPTUNNEL_MODE_INLINE)
+			missarg("tundst");
+	} else {
+		if (mode == IOAM6_IPTUNNEL_MODE_INLINE)
+			invarg("Inline mode does not need tundst", *argv);
+
+		NEXT_ARG();
+
+		get_addr(&addr, *argv, AF_INET6);
+		if (addr.family != AF_INET6 || addr.bytelen != 16)
+			invarg("Invalid IPv6 address for tundst", *argv);
+
+		NEXT_ARG();
+	}
+
+	if (strcmp(*argv, "trace") != 0)
 		missarg("trace");
 
 	NEXT_ARG();
-	if (strcmp(*argv, "prealloc"))
+
+	if (strcmp(*argv, "prealloc") != 0)
 		missarg("prealloc");
 
 	while (NEXT_ARG_OK()) {
@@ -909,63 +1035,60 @@ static int parse_encap_ioam6(struct rtat
 		if (strcmp(*argv, "type") == 0) {
 			NEXT_ARG();
 
-			if (type)
+			if (trace_type)
 				duparg2("type", *argv);
 
-			if (get_u32(&type, *argv, 0) || !type)
-				invarg("Invalid type", *argv);
-
-			trace->type_be32 = htonl(type << 8);
-
+			if (get_u32(&trace_type, *argv, 0) || !trace_type)
+				invarg("Invalid trace type", *argv);
 		} else if (strcmp(*argv, "ns") == 0) {
 			NEXT_ARG();
 
 			if (ns_found++)
 				duparg2("ns", *argv);
 
-			if (!type)
-				missarg("type");
-
-			if (get_u16(&ns, *argv, 0))
+			if (get_u16(&trace_ns, *argv, 0))
 				invarg("Invalid namespace ID", *argv);
-
-			trace->namespace_id = htons(ns);
-
 		} else if (strcmp(*argv, "size") == 0) {
 			NEXT_ARG();
 
-			if (size)
+			if (trace_size)
 				duparg2("size", *argv);
 
-			if (!type)
-				missarg("type");
-			if (!ns_found)
-				missarg("ns");
-
-			if (get_u16(&size, *argv, 0) || !size)
-				invarg("Invalid size", *argv);
-
-			if (size % 4)
-				invarg("Size must be a 4-octet multiple", *argv);
-			if (size > IOAM6_TRACE_DATA_SIZE_MAX)
-				invarg("Size too big", *argv);
+			if (get_u16(&trace_size, *argv, 0) || !trace_size)
+				invarg("Invalid trace size", *argv);
 
-			trace->remlen = (__u8)(size / 4);
+			if (trace_size % 4)
+				invarg("Trace size must be a 4-octet multiple",
+				       *argv);
 
+			if (trace_size > IOAM6_TRACE_DATA_SIZE_MAX)
+				invarg("Trace size is too big", *argv);
 		} else {
 			break;
 		}
 	}
 
-	if (!type)
+	if (!trace_type)
 		missarg("type");
 	if (!ns_found)
 		missarg("ns");
-	if (!size)
+	if (!trace_size)
 		missarg("size");
 
-	if (rta_addattr_l(rta, len, IOAM6_IPTUNNEL_TRACE, trace,
-			  sizeof(*trace))) {
+	trace = calloc(1, sizeof(*trace));
+	if (!trace)
+		return -1;
+
+	trace->type_be32 = htonl(trace_type << 8);
+	trace->namespace_id = htons(trace_ns);
+	trace->remlen = (__u8)(trace_size / 4);
+
+	if (rta_addattr32(rta, len, IOAM6_IPTUNNEL_FREQ_K, freq_k) ||
+	    rta_addattr32(rta, len, IOAM6_IPTUNNEL_FREQ_N, freq_n) ||
+	    rta_addattr8(rta, len, IOAM6_IPTUNNEL_MODE, mode) ||
+	    (mode != IOAM6_IPTUNNEL_MODE_INLINE &&
+	     rta_addattr_l(rta, len, IOAM6_IPTUNNEL_DST, &addr.data, addr.bytelen)) ||
+	    rta_addattr_l(rta, len, IOAM6_IPTUNNEL_TRACE, trace, sizeof(*trace))) {
 		free(trace);
 		return -1;
 	}
diff -pruN 5.15.0-1/ip/iprule.c 5.19.0-1/ip/iprule.c
--- 5.15.0-1/ip/iprule.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/iprule.c	2022-08-02 18:36:33.000000000 +0000
@@ -592,7 +592,8 @@ static int iprule_list_flush_or_save(int
 			filter.prefmask = 1;
 		} else if (strcmp(*argv, "not") == 0) {
 			filter.not = 1;
-		} else if (strcmp(*argv, "tos") == 0) {
+		} else if (strcmp(*argv, "tos") == 0 ||
+			   strcmp(*argv, "dsfield") == 0) {
 			__u32 tos;
 
 			NEXT_ARG();
diff -pruN 5.15.0-1/ip/ipstats.c 5.19.0-1/ip/ipstats.c
--- 5.15.0-1/ip/ipstats.c	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/ip/ipstats.c	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,1356 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <assert.h>
+#include <errno.h>
+
+#include "list.h"
+#include "utils.h"
+#include "ip_common.h"
+
+struct ipstats_stat_dump_filters {
+	/* mask[0] filters outer attributes. Then individual nests have their
+	 * filtering mask at the index of the nested attribute.
+	 */
+	__u32 mask[IFLA_STATS_MAX + 1];
+};
+
+static void
+ipstats_stat_desc_enable_bit(struct ipstats_stat_dump_filters *filters,
+			     unsigned int group, unsigned int subgroup)
+{
+	filters->mask[0] |= IFLA_STATS_FILTER_BIT(group);
+	if (subgroup)
+		filters->mask[group] |= IFLA_STATS_FILTER_BIT(subgroup);
+}
+
+struct ipstats_stat_show_attrs {
+	struct if_stats_msg *ifsm;
+	int len;
+
+	/* tbs[0] contains top-level attribute table. Then individual nests have
+	 * their attribute tables at the index of the nested attribute.
+	 */
+	struct rtattr **tbs[IFLA_STATS_MAX + 1];
+};
+
+static const char *const ipstats_levels[] = {
+	"group",
+	"subgroup",
+	"suite",
+};
+
+enum {
+	IPSTATS_LEVELS_COUNT = ARRAY_SIZE(ipstats_levels),
+};
+
+struct ipstats_sel {
+	const char *sel[IPSTATS_LEVELS_COUNT];
+};
+
+struct ipstats_stat_enabled_one {
+	const struct ipstats_stat_desc *desc;
+	struct ipstats_sel sel;
+};
+
+struct ipstats_stat_enabled {
+	struct ipstats_stat_enabled_one *enabled;
+	size_t nenabled;
+};
+
+static const unsigned int ipstats_stat_ifla_max[] = {
+	[0] = IFLA_STATS_MAX,
+	[IFLA_STATS_LINK_XSTATS] = LINK_XSTATS_TYPE_MAX,
+	[IFLA_STATS_LINK_XSTATS_SLAVE] = LINK_XSTATS_TYPE_MAX,
+	[IFLA_STATS_LINK_OFFLOAD_XSTATS] = IFLA_OFFLOAD_XSTATS_MAX,
+	[IFLA_STATS_AF_SPEC] = AF_MAX - 1,
+};
+
+static_assert(ARRAY_SIZE(ipstats_stat_ifla_max) == IFLA_STATS_MAX + 1,
+	      "An IFLA_STATS attribute is missing from the ifla_max table");
+
+static int
+ipstats_stat_show_attrs_alloc_tb(struct ipstats_stat_show_attrs *attrs,
+				 unsigned int group)
+{
+	unsigned int ifla_max;
+	int err;
+
+	assert(group < ARRAY_SIZE(ipstats_stat_ifla_max));
+	assert(group < ARRAY_SIZE(attrs->tbs));
+	ifla_max = ipstats_stat_ifla_max[group];
+	assert(ifla_max != 0);
+
+	if (attrs->tbs[group])
+		return 0;
+
+	attrs->tbs[group] = calloc(ifla_max + 1, sizeof(*attrs->tbs[group]));
+	if (attrs->tbs[group] == NULL)
+		return -ENOMEM;
+
+	if (group == 0)
+		err = parse_rtattr(attrs->tbs[group], ifla_max,
+				   IFLA_STATS_RTA(attrs->ifsm), attrs->len);
+	else
+		err = parse_rtattr_nested(attrs->tbs[group], ifla_max,
+					  attrs->tbs[0][group]);
+
+	if (err != 0) {
+		free(attrs->tbs[group]);
+		attrs->tbs[group] = NULL;
+	}
+	return err;
+}
+
+static const struct rtattr *
+ipstats_stat_show_get_attr(struct ipstats_stat_show_attrs *attrs,
+			   int group, int subgroup, int *err)
+{
+	int tmp_err;
+
+	if (err == NULL)
+		err = &tmp_err;
+
+	*err = 0;
+	if (subgroup == 0)
+		return attrs->tbs[0][group];
+
+	if (attrs->tbs[0][group] == NULL)
+		return NULL;
+
+	*err = ipstats_stat_show_attrs_alloc_tb(attrs, group);
+	if (*err != 0)
+		return NULL;
+
+	return attrs->tbs[group][subgroup];
+}
+
+static void
+ipstats_stat_show_attrs_free(struct ipstats_stat_show_attrs *attrs)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(attrs->tbs); i++)
+		free(attrs->tbs[i]);
+}
+
+#define IPSTATS_RTA_PAYLOAD(VAR, AT)					\
+	do {								\
+		const struct rtattr *__at = (AT);			\
+		size_t __at_sz = __at->rta_len - RTA_LENGTH(0);		\
+		size_t __var_sz = sizeof(VAR);				\
+		typeof(VAR) *__dest = &VAR;				\
+									\
+		memset(__dest, 0, __var_sz);				\
+		memcpy(__dest, RTA_DATA(__at), MIN(__at_sz, __var_sz));	\
+	} while (0)
+
+static int ipstats_show_64(struct ipstats_stat_show_attrs *attrs,
+			   unsigned int group, unsigned int subgroup)
+{
+	struct rtnl_link_stats64 stats;
+	const struct rtattr *at;
+	int err;
+
+	at = ipstats_stat_show_get_attr(attrs, group, subgroup, &err);
+	if (at == NULL)
+		return err;
+
+	IPSTATS_RTA_PAYLOAD(stats, at);
+
+	open_json_object("stats64");
+	print_stats64(stdout, &stats, NULL, NULL);
+	close_json_object();
+	return 0;
+}
+
+static void print_hw_stats64(FILE *fp, struct rtnl_hw_stats64 *s)
+{
+	unsigned int cols[] = {
+		strlen("*X: bytes"),
+		strlen("packets"),
+		strlen("errors"),
+		strlen("dropped"),
+		strlen("overrun"),
+	};
+
+	if (is_json_context()) {
+		/* RX stats */
+		open_json_object("rx");
+		print_u64(PRINT_JSON, "bytes", NULL, s->rx_bytes);
+		print_u64(PRINT_JSON, "packets", NULL, s->rx_packets);
+		print_u64(PRINT_JSON, "errors", NULL, s->rx_errors);
+		print_u64(PRINT_JSON, "dropped", NULL, s->rx_dropped);
+		print_u64(PRINT_JSON, "multicast", NULL, s->multicast);
+		close_json_object();
+
+		/* TX stats */
+		open_json_object("tx");
+		print_u64(PRINT_JSON, "bytes", NULL, s->tx_bytes);
+		print_u64(PRINT_JSON, "packets", NULL, s->tx_packets);
+		print_u64(PRINT_JSON, "errors", NULL, s->tx_errors);
+		print_u64(PRINT_JSON, "dropped", NULL, s->tx_dropped);
+		close_json_object();
+	} else {
+		size_columns(cols, ARRAY_SIZE(cols),
+			     s->rx_bytes, s->rx_packets, s->rx_errors,
+			     s->rx_dropped, s->multicast);
+		size_columns(cols, ARRAY_SIZE(cols),
+			     s->tx_bytes, s->tx_packets, s->tx_errors,
+			     s->tx_dropped, 0);
+
+		/* RX stats */
+		fprintf(fp, "    RX: %*s %*s %*s %*s %*s%s",
+			cols[0] - 4, "bytes", cols[1], "packets",
+			cols[2], "errors", cols[3], "dropped",
+			cols[4], "mcast", _SL_);
+
+		fprintf(fp, "    ");
+		print_num(fp, cols[0], s->rx_bytes);
+		print_num(fp, cols[1], s->rx_packets);
+		print_num(fp, cols[2], s->rx_errors);
+		print_num(fp, cols[3], s->rx_dropped);
+		print_num(fp, cols[4], s->multicast);
+		fprintf(fp, "%s", _SL_);
+
+		/* TX stats */
+		fprintf(fp, "    TX: %*s %*s %*s %*s%s",
+			cols[0] - 4, "bytes", cols[1], "packets",
+			cols[2], "errors", cols[3], "dropped", _SL_);
+
+		fprintf(fp, "    ");
+		print_num(fp, cols[0], s->tx_bytes);
+		print_num(fp, cols[1], s->tx_packets);
+		print_num(fp, cols[2], s->tx_errors);
+		print_num(fp, cols[3], s->tx_dropped);
+	}
+}
+
+static int ipstats_show_hw64(const struct rtattr *at)
+{
+	struct rtnl_hw_stats64 stats;
+
+	IPSTATS_RTA_PAYLOAD(stats, at);
+	print_hw_stats64(stdout, &stats);
+	return 0;
+}
+
+enum ipstats_maybe_on_off {
+	IPSTATS_MOO_OFF = -1,
+	IPSTATS_MOO_INVALID,
+	IPSTATS_MOO_ON,
+};
+
+static bool ipstats_moo_to_bool(enum ipstats_maybe_on_off moo)
+{
+	assert(moo != IPSTATS_MOO_INVALID);
+	return moo + 1;
+}
+
+static int ipstats_print_moo(enum output_type t, const char *key,
+			     const char *fmt, enum ipstats_maybe_on_off moo)
+{
+	if (!moo)
+		return 0;
+	return print_on_off(t, key, fmt, ipstats_moo_to_bool(moo));
+}
+
+struct ipstats_hw_s_info_one {
+	enum ipstats_maybe_on_off request;
+	enum ipstats_maybe_on_off used;
+};
+
+enum ipstats_hw_s_info_idx {
+	IPSTATS_HW_S_INFO_IDX_L3_STATS,
+	IPSTATS_HW_S_INFO_IDX_COUNT
+};
+
+static const char *const ipstats_hw_s_info_name[] = {
+	"l3_stats",
+};
+
+static_assert(ARRAY_SIZE(ipstats_hw_s_info_name) ==
+	      IPSTATS_HW_S_INFO_IDX_COUNT,
+	      "mismatch: enum ipstats_hw_s_info_idx x ipstats_hw_s_info_name");
+
+struct ipstats_hw_s_info {
+	/* Indexed by enum ipstats_hw_s_info_idx. */
+	struct ipstats_hw_s_info_one *infos[IPSTATS_HW_S_INFO_IDX_COUNT];
+};
+
+static enum ipstats_maybe_on_off ipstats_dissect_01(int value, const char *what)
+{
+	switch (value) {
+	case 0:
+		return IPSTATS_MOO_OFF;
+	case 1:
+		return IPSTATS_MOO_ON;
+	default:
+		fprintf(stderr, "Invalid value for %s: expected 0 or 1, got %d.\n",
+			what, value);
+		return IPSTATS_MOO_INVALID;
+	}
+}
+
+static int ipstats_dissect_hw_s_info_one(const struct rtattr *at,
+					 struct ipstats_hw_s_info_one *p_hwsio,
+					 const char *what)
+{
+	int attr_id_request = IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST;
+	struct rtattr *tb[IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX + 1];
+	int attr_id_used = IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED;
+	struct ipstats_hw_s_info_one hwsio = {};
+	int err;
+	int v;
+
+	err = parse_rtattr_nested(tb, IFLA_OFFLOAD_XSTATS_HW_S_INFO_MAX, at);
+	if (err)
+		return err;
+
+	if (tb[attr_id_request]) {
+		v = rta_getattr_u8(tb[attr_id_request]);
+		hwsio.request = ipstats_dissect_01(v, "request");
+
+		/* This has to be present & valid. */
+		if (!hwsio.request)
+			return -EINVAL;
+	}
+
+	if (tb[attr_id_used]) {
+		v = rta_getattr_u8(tb[attr_id_used]);
+		hwsio.used = ipstats_dissect_01(v, "used");
+	}
+
+	*p_hwsio = hwsio;
+	return 0;
+}
+
+static int ipstats_dissect_hw_s_info(const struct rtattr *at,
+				     struct ipstats_hw_s_info *hwsi)
+{
+	struct rtattr *tb[IFLA_OFFLOAD_XSTATS_MAX + 1];
+	int attr_id_l3 = IFLA_OFFLOAD_XSTATS_L3_STATS;
+	struct ipstats_hw_s_info_one *hwsio = NULL;
+	int err;
+
+	err = parse_rtattr_nested(tb, IFLA_OFFLOAD_XSTATS_MAX, at);
+	if (err)
+		return err;
+
+	*hwsi = (struct ipstats_hw_s_info){};
+
+	if (tb[attr_id_l3]) {
+		hwsio = malloc(sizeof(*hwsio));
+		if (!hwsio) {
+			err = -ENOMEM;
+			goto out;
+		}
+
+		err = ipstats_dissect_hw_s_info_one(tb[attr_id_l3], hwsio, "l3");
+		if (err)
+			goto out;
+
+		hwsi->infos[IPSTATS_HW_S_INFO_IDX_L3_STATS] = hwsio;
+		hwsio = NULL;
+	}
+
+	return 0;
+
+out:
+	free(hwsio);
+	return err;
+}
+
+static void ipstats_fini_hw_s_info(struct ipstats_hw_s_info *hwsi)
+{
+	int i;
+
+	for (i = 0; i < IPSTATS_HW_S_INFO_IDX_COUNT; i++)
+		free(hwsi->infos[i]);
+}
+
+static void
+__ipstats_show_hw_s_info_one(const struct ipstats_hw_s_info_one *hwsio)
+{
+	if (hwsio == NULL)
+		return;
+
+	ipstats_print_moo(PRINT_ANY, "request", " %s", hwsio->request);
+	ipstats_print_moo(PRINT_ANY, "used", " used %s", hwsio->used);
+}
+
+static void
+ipstats_show_hw_s_info_one(const struct ipstats_hw_s_info *hwsi,
+			   enum ipstats_hw_s_info_idx idx)
+{
+	const struct ipstats_hw_s_info_one *hwsio = hwsi->infos[idx];
+	const char *name = ipstats_hw_s_info_name[idx];
+
+	if (hwsio == NULL)
+		return;
+
+	print_string(PRINT_FP, NULL, "    %s", name);
+	open_json_object(name);
+	__ipstats_show_hw_s_info_one(hwsio);
+	close_json_object();
+}
+
+static int __ipstats_show_hw_s_info(const struct rtattr *at)
+{
+	struct ipstats_hw_s_info hwsi = {};
+	int err;
+
+	err = ipstats_dissect_hw_s_info(at, &hwsi);
+	if (err)
+		return err;
+
+	open_json_object("info");
+	ipstats_show_hw_s_info_one(&hwsi, IPSTATS_HW_S_INFO_IDX_L3_STATS);
+	close_json_object();
+
+	ipstats_fini_hw_s_info(&hwsi);
+	return 0;
+}
+
+static int ipstats_show_hw_s_info(struct ipstats_stat_show_attrs *attrs,
+				  unsigned int group, unsigned int subgroup)
+{
+	const struct rtattr *at;
+	int err;
+
+	at = ipstats_stat_show_get_attr(attrs, group, subgroup, &err);
+	if (at == NULL)
+		return err;
+
+	print_nl();
+	return __ipstats_show_hw_s_info(at);
+}
+
+static int __ipstats_show_hw_stats(const struct rtattr *at_hwsi,
+				   const struct rtattr *at_stats,
+				   enum ipstats_hw_s_info_idx idx)
+{
+	int err = 0;
+
+	if (at_hwsi != NULL) {
+		struct ipstats_hw_s_info hwsi = {};
+
+		err = ipstats_dissect_hw_s_info(at_hwsi, &hwsi);
+		if (err)
+			return err;
+
+		open_json_object("info");
+		__ipstats_show_hw_s_info_one(hwsi.infos[idx]);
+		close_json_object();
+
+		ipstats_fini_hw_s_info(&hwsi);
+	}
+
+	if (at_stats != NULL) {
+		print_nl();
+		open_json_object("stats64");
+		err = ipstats_show_hw64(at_stats);
+		close_json_object();
+	}
+
+	return err;
+}
+
+static int ipstats_show_hw_stats(struct ipstats_stat_show_attrs *attrs,
+				 unsigned int group,
+				 unsigned int hw_s_info,
+				 unsigned int hw_stats,
+				 enum ipstats_hw_s_info_idx idx)
+{
+	const struct rtattr *at_stats;
+	const struct rtattr *at_hwsi;
+	int err = 0;
+
+	at_hwsi = ipstats_stat_show_get_attr(attrs, group, hw_s_info, &err);
+	if (at_hwsi == NULL)
+		return err;
+
+	at_stats = ipstats_stat_show_get_attr(attrs, group, hw_stats, &err);
+	if (at_stats == NULL && err != 0)
+		return err;
+
+	return __ipstats_show_hw_stats(at_hwsi, at_stats, idx);
+}
+
+static void
+ipstats_stat_desc_pack_cpu_hit(struct ipstats_stat_dump_filters *filters,
+			       const struct ipstats_stat_desc *desc)
+{
+	ipstats_stat_desc_enable_bit(filters,
+				     IFLA_STATS_LINK_OFFLOAD_XSTATS,
+				     IFLA_OFFLOAD_XSTATS_CPU_HIT);
+}
+
+static int ipstats_stat_desc_show_cpu_hit(struct ipstats_stat_show_attrs *attrs,
+					  const struct ipstats_stat_desc *desc)
+{
+	print_nl();
+	return ipstats_show_64(attrs,
+			       IFLA_STATS_LINK_OFFLOAD_XSTATS,
+			       IFLA_OFFLOAD_XSTATS_CPU_HIT);
+}
+
+static const struct ipstats_stat_desc ipstats_stat_desc_offload_cpu_hit = {
+	.name = "cpu_hit",
+	.kind = IPSTATS_STAT_DESC_KIND_LEAF,
+	.pack = &ipstats_stat_desc_pack_cpu_hit,
+	.show = &ipstats_stat_desc_show_cpu_hit,
+};
+
+static void
+ipstats_stat_desc_pack_hw_stats_info(struct ipstats_stat_dump_filters *filters,
+				     const struct ipstats_stat_desc *desc)
+{
+	ipstats_stat_desc_enable_bit(filters,
+				     IFLA_STATS_LINK_OFFLOAD_XSTATS,
+				     IFLA_OFFLOAD_XSTATS_HW_S_INFO);
+}
+
+static int
+ipstats_stat_desc_show_hw_stats_info(struct ipstats_stat_show_attrs *attrs,
+				     const struct ipstats_stat_desc *desc)
+{
+	return ipstats_show_hw_s_info(attrs,
+				      IFLA_STATS_LINK_OFFLOAD_XSTATS,
+				      IFLA_OFFLOAD_XSTATS_HW_S_INFO);
+}
+
+static const struct ipstats_stat_desc ipstats_stat_desc_offload_hw_s_info = {
+	.name = "hw_stats_info",
+	.kind = IPSTATS_STAT_DESC_KIND_LEAF,
+	.pack = &ipstats_stat_desc_pack_hw_stats_info,
+	.show = &ipstats_stat_desc_show_hw_stats_info,
+};
+
+static void
+ipstats_stat_desc_pack_l3_stats(struct ipstats_stat_dump_filters *filters,
+				const struct ipstats_stat_desc *desc)
+{
+	ipstats_stat_desc_enable_bit(filters,
+				     IFLA_STATS_LINK_OFFLOAD_XSTATS,
+				     IFLA_OFFLOAD_XSTATS_L3_STATS);
+	ipstats_stat_desc_enable_bit(filters,
+				     IFLA_STATS_LINK_OFFLOAD_XSTATS,
+				     IFLA_OFFLOAD_XSTATS_HW_S_INFO);
+}
+
+static int
+ipstats_stat_desc_show_l3_stats(struct ipstats_stat_show_attrs *attrs,
+				const struct ipstats_stat_desc *desc)
+{
+	return ipstats_show_hw_stats(attrs,
+				     IFLA_STATS_LINK_OFFLOAD_XSTATS,
+				     IFLA_OFFLOAD_XSTATS_HW_S_INFO,
+				     IFLA_OFFLOAD_XSTATS_L3_STATS,
+				     IPSTATS_HW_S_INFO_IDX_L3_STATS);
+}
+
+static const struct ipstats_stat_desc ipstats_stat_desc_offload_l3_stats = {
+	.name = "l3_stats",
+	.kind = IPSTATS_STAT_DESC_KIND_LEAF,
+	.pack = &ipstats_stat_desc_pack_l3_stats,
+	.show = &ipstats_stat_desc_show_l3_stats,
+};
+
+static const struct ipstats_stat_desc *ipstats_stat_desc_offload_subs[] = {
+	&ipstats_stat_desc_offload_cpu_hit,
+	&ipstats_stat_desc_offload_hw_s_info,
+	&ipstats_stat_desc_offload_l3_stats,
+};
+
+static const struct ipstats_stat_desc ipstats_stat_desc_offload_group = {
+	.name = "offload",
+	.kind = IPSTATS_STAT_DESC_KIND_GROUP,
+	.subs = ipstats_stat_desc_offload_subs,
+	.nsubs = ARRAY_SIZE(ipstats_stat_desc_offload_subs),
+};
+
+void ipstats_stat_desc_pack_xstats(struct ipstats_stat_dump_filters *filters,
+				   const struct ipstats_stat_desc *desc)
+{
+	struct ipstats_stat_desc_xstats *xdesc;
+
+	xdesc = container_of(desc, struct ipstats_stat_desc_xstats, desc);
+	ipstats_stat_desc_enable_bit(filters, xdesc->xstats_at, 0);
+}
+
+int ipstats_stat_desc_show_xstats(struct ipstats_stat_show_attrs *attrs,
+				  const struct ipstats_stat_desc *desc)
+{
+	struct ipstats_stat_desc_xstats *xdesc;
+	const struct rtattr *at;
+	struct rtattr **tb;
+	int err;
+
+	xdesc = container_of(desc, struct ipstats_stat_desc_xstats, desc);
+	at = ipstats_stat_show_get_attr(attrs,
+					xdesc->xstats_at,
+					xdesc->link_type_at, &err);
+	if (at == NULL)
+		return err;
+
+	tb = alloca(sizeof(*tb) * (xdesc->inner_max + 1));
+	err = parse_rtattr_nested(tb, xdesc->inner_max, at);
+	if (err != 0)
+		return err;
+
+	if (tb[xdesc->inner_at] != NULL) {
+		print_nl();
+		xdesc->show_cb(tb[xdesc->inner_at]);
+	}
+	return 0;
+}
+
+static const struct ipstats_stat_desc *ipstats_stat_desc_xstats_subs[] = {
+	&ipstats_stat_desc_xstats_bridge_group,
+	&ipstats_stat_desc_xstats_bond_group,
+};
+
+static const struct ipstats_stat_desc ipstats_stat_desc_xstats_group = {
+	.name = "xstats",
+	.kind = IPSTATS_STAT_DESC_KIND_GROUP,
+	.subs = ipstats_stat_desc_xstats_subs,
+	.nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_subs),
+};
+
+static const struct ipstats_stat_desc *ipstats_stat_desc_xstats_slave_subs[] = {
+	&ipstats_stat_desc_xstats_slave_bridge_group,
+	&ipstats_stat_desc_xstats_slave_bond_group,
+};
+
+static const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_group = {
+	.name = "xstats_slave",
+	.kind = IPSTATS_STAT_DESC_KIND_GROUP,
+	.subs = ipstats_stat_desc_xstats_slave_subs,
+	.nsubs = ARRAY_SIZE(ipstats_stat_desc_xstats_slave_subs),
+};
+
+static void
+ipstats_stat_desc_pack_link(struct ipstats_stat_dump_filters *filters,
+			    const struct ipstats_stat_desc *desc)
+{
+	ipstats_stat_desc_enable_bit(filters,
+				     IFLA_STATS_LINK_64, 0);
+}
+
+static int
+ipstats_stat_desc_show_link(struct ipstats_stat_show_attrs *attrs,
+			    const struct ipstats_stat_desc *desc)
+{
+	print_nl();
+	return ipstats_show_64(attrs, IFLA_STATS_LINK_64, 0);
+}
+
+static const struct ipstats_stat_desc ipstats_stat_desc_toplev_link = {
+	.name = "link",
+	.kind = IPSTATS_STAT_DESC_KIND_LEAF,
+	.pack = &ipstats_stat_desc_pack_link,
+	.show = &ipstats_stat_desc_show_link,
+};
+
+static const struct ipstats_stat_desc ipstats_stat_desc_afstats_group;
+
+static void
+ipstats_stat_desc_pack_afstats(struct ipstats_stat_dump_filters *filters,
+			       const struct ipstats_stat_desc *desc)
+{
+	ipstats_stat_desc_enable_bit(filters, IFLA_STATS_AF_SPEC, 0);
+}
+
+static int
+ipstats_stat_desc_show_afstats_mpls(struct ipstats_stat_show_attrs *attrs,
+				    const struct ipstats_stat_desc *desc)
+{
+	struct rtattr *mrtb[MPLS_STATS_MAX+1];
+	struct mpls_link_stats stats;
+	const struct rtattr *at;
+	int err;
+
+	at = ipstats_stat_show_get_attr(attrs, IFLA_STATS_AF_SPEC,
+					AF_MPLS, &err);
+	if (at == NULL)
+		return err;
+
+	parse_rtattr_nested(mrtb, MPLS_STATS_MAX, at);
+	if (mrtb[MPLS_STATS_LINK] == NULL)
+		return -ENOENT;
+
+	IPSTATS_RTA_PAYLOAD(stats, mrtb[MPLS_STATS_LINK]);
+
+	print_nl();
+	open_json_object("mpls_stats");
+	print_mpls_link_stats(stdout, &stats, "    ");
+	close_json_object();
+	return 0;
+}
+
+static const struct ipstats_stat_desc ipstats_stat_desc_afstats_mpls = {
+	.name = "mpls",
+	.kind = IPSTATS_STAT_DESC_KIND_LEAF,
+	.pack = &ipstats_stat_desc_pack_afstats,
+	.show = &ipstats_stat_desc_show_afstats_mpls,
+};
+
+static const struct ipstats_stat_desc *ipstats_stat_desc_afstats_subs[] = {
+	&ipstats_stat_desc_afstats_mpls,
+};
+
+static const struct ipstats_stat_desc ipstats_stat_desc_afstats_group = {
+	.name = "afstats",
+	.kind = IPSTATS_STAT_DESC_KIND_GROUP,
+	.subs = ipstats_stat_desc_afstats_subs,
+	.nsubs = ARRAY_SIZE(ipstats_stat_desc_afstats_subs),
+};
+static const struct ipstats_stat_desc *ipstats_stat_desc_toplev_subs[] = {
+	&ipstats_stat_desc_toplev_link,
+	&ipstats_stat_desc_xstats_group,
+	&ipstats_stat_desc_xstats_slave_group,
+	&ipstats_stat_desc_offload_group,
+	&ipstats_stat_desc_afstats_group,
+};
+
+static const struct ipstats_stat_desc ipstats_stat_desc_toplev_group = {
+	.name = "top-level",
+	.kind = IPSTATS_STAT_DESC_KIND_GROUP,
+	.subs = ipstats_stat_desc_toplev_subs,
+	.nsubs = ARRAY_SIZE(ipstats_stat_desc_toplev_subs),
+};
+
+static void ipstats_show_group(const struct ipstats_sel *sel)
+{
+	int i;
+
+	for (i = 0; i < IPSTATS_LEVELS_COUNT; i++) {
+		if (sel->sel[i] == NULL)
+			break;
+		print_string(PRINT_JSON, ipstats_levels[i], NULL, sel->sel[i]);
+		print_string(PRINT_FP, NULL, " %s ", ipstats_levels[i]);
+		print_string(PRINT_FP, NULL, "%s", sel->sel[i]);
+	}
+}
+
+static int
+ipstats_process_ifsm(struct nlmsghdr *answer,
+		     struct ipstats_stat_enabled *enabled)
+{
+	struct ipstats_stat_show_attrs show_attrs = {};
+	const char *dev;
+	int err = 0;
+	int i;
+
+	show_attrs.ifsm = NLMSG_DATA(answer);
+	show_attrs.len = (answer->nlmsg_len -
+			  NLMSG_LENGTH(sizeof(*show_attrs.ifsm)));
+	if (show_attrs.len < 0) {
+		fprintf(stderr, "BUG: wrong nlmsg len %d\n", show_attrs.len);
+		return -EINVAL;
+	}
+
+	err = ipstats_stat_show_attrs_alloc_tb(&show_attrs, 0);
+	if (err != 0) {
+		fprintf(stderr, "Error parsing netlink answer: %s\n",
+			strerror(err));
+		return err;
+	}
+
+	dev = ll_index_to_name(show_attrs.ifsm->ifindex);
+
+	for (i = 0; i < enabled->nenabled; i++) {
+		const struct ipstats_stat_desc *desc = enabled->enabled[i].desc;
+
+		open_json_object(NULL);
+		print_int(PRINT_ANY, "ifindex", "%d:",
+			  show_attrs.ifsm->ifindex);
+		print_color_string(PRINT_ANY, COLOR_IFNAME,
+				   "ifname", " %s:", dev);
+		ipstats_show_group(&enabled->enabled[i].sel);
+		err = desc->show(&show_attrs, desc);
+		if (err != 0)
+			goto out;
+		close_json_object();
+		print_nl();
+	}
+
+out:
+	ipstats_stat_show_attrs_free(&show_attrs);
+	return err;
+}
+
+static bool
+ipstats_req_should_filter_at(struct ipstats_stat_dump_filters *filters, int at)
+{
+	return filters->mask[at] != 0 &&
+	       filters->mask[at] != (1 << ipstats_stat_ifla_max[at]) - 1;
+}
+
+static int
+ipstats_req_add_filters(struct ipstats_req *req, void *data)
+{
+	struct ipstats_stat_dump_filters dump_filters = {};
+	struct ipstats_stat_enabled *enabled = data;
+	bool get_filters = false;
+	int i;
+
+	for (i = 0; i < enabled->nenabled; i++)
+		enabled->enabled[i].desc->pack(&dump_filters,
+					       enabled->enabled[i].desc);
+
+	for (i = 1; i < ARRAY_SIZE(dump_filters.mask); i++) {
+		if (ipstats_req_should_filter_at(&dump_filters, i)) {
+			get_filters = true;
+			break;
+		}
+	}
+
+	req->ifsm.filter_mask = dump_filters.mask[0];
+	if (get_filters) {
+		struct rtattr *nest;
+
+		nest = addattr_nest(&req->nlh, sizeof(*req),
+				    IFLA_STATS_GET_FILTERS | NLA_F_NESTED);
+
+		for (i = 1; i < ARRAY_SIZE(dump_filters.mask); i++) {
+			if (ipstats_req_should_filter_at(&dump_filters, i))
+				addattr32(&req->nlh, sizeof(*req), i,
+					  dump_filters.mask[i]);
+		}
+
+		addattr_nest_end(&req->nlh, nest);
+	}
+
+	return 0;
+}
+
+static int
+ipstats_show_one(int ifindex, struct ipstats_stat_enabled *enabled)
+{
+	struct ipstats_req req = {
+		.nlh.nlmsg_flags = NLM_F_REQUEST,
+		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg)),
+		.nlh.nlmsg_type = RTM_GETSTATS,
+		.ifsm.family = PF_UNSPEC,
+		.ifsm.ifindex = ifindex,
+	};
+	struct nlmsghdr *answer;
+	int err = 0;
+
+	ipstats_req_add_filters(&req, enabled);
+	if (rtnl_talk(&rth, &req.nlh, &answer) < 0)
+		return -2;
+	err = ipstats_process_ifsm(answer, enabled);
+	free(answer);
+
+	return err;
+}
+
+static int ipstats_dump_one(struct nlmsghdr *n, void *arg)
+{
+	struct ipstats_stat_enabled *enabled = arg;
+	int rc;
+
+	rc = ipstats_process_ifsm(n, enabled);
+	if (rc)
+		return rc;
+
+	print_nl();
+	return 0;
+}
+
+static int ipstats_dump(struct ipstats_stat_enabled *enabled)
+{
+	int rc = 0;
+
+	if (rtnl_statsdump_req_filter(&rth, PF_UNSPEC, 0,
+				      ipstats_req_add_filters,
+				      enabled) < 0) {
+		perror("Cannot send dump request");
+		return -2;
+	}
+
+	if (rtnl_dump_filter(&rth, ipstats_dump_one, enabled) < 0) {
+		fprintf(stderr, "Dump terminated\n");
+		rc = -2;
+	}
+
+	fflush(stdout);
+	return rc;
+}
+
+static int
+ipstats_show_do(int ifindex, struct ipstats_stat_enabled *enabled)
+{
+	int rc;
+
+	new_json_obj(json);
+	if (ifindex)
+		rc = ipstats_show_one(ifindex, enabled);
+	else
+		rc = ipstats_dump(enabled);
+	delete_json_obj();
+
+	return rc;
+}
+
+static int ipstats_add_enabled(struct ipstats_stat_enabled_one ens[],
+			       size_t nens,
+			       struct ipstats_stat_enabled *enabled)
+{
+	struct ipstats_stat_enabled_one *new_en;
+
+	new_en = realloc(enabled->enabled,
+			 sizeof(*new_en) * (enabled->nenabled + nens));
+	if (new_en == NULL)
+		return -ENOMEM;
+
+	enabled->enabled = new_en;
+	while (nens-- > 0)
+		enabled->enabled[enabled->nenabled++] = *ens++;
+	return 0;
+}
+
+static void ipstats_select_push(struct ipstats_sel *sel, const char *name)
+{
+	int i;
+
+	for (i = 0; i < IPSTATS_LEVELS_COUNT; i++)
+		if (sel->sel[i] == NULL) {
+			sel->sel[i] = name;
+			return;
+		}
+
+	assert(false);
+}
+
+static int
+ipstats_enable_recursively(const struct ipstats_stat_desc *desc,
+			   struct ipstats_stat_enabled *enabled,
+			   const struct ipstats_sel *sel)
+{
+	bool found = false;
+	size_t i;
+	int err;
+
+	if (desc->kind == IPSTATS_STAT_DESC_KIND_LEAF) {
+		struct ipstats_stat_enabled_one en[] = {{
+			.desc = desc,
+			.sel = *sel,
+		}};
+
+		return ipstats_add_enabled(en, ARRAY_SIZE(en), enabled);
+	}
+
+	for (i = 0; i < desc->nsubs; i++) {
+		struct ipstats_sel subsel = *sel;
+
+		ipstats_select_push(&subsel, desc->subs[i]->name);
+		err = ipstats_enable_recursively(desc->subs[i], enabled,
+						 &subsel);
+		if (err == -ENOENT)
+			continue;
+		if (err != 0)
+			return err;
+		found = true;
+	}
+
+	return found ? 0 : -ENOENT;
+}
+
+static int ipstats_comp_enabled(const void *a, const void *b)
+{
+	const struct ipstats_stat_enabled_one *en_a = a;
+	const struct ipstats_stat_enabled_one *en_b = b;
+
+	if (en_a->desc < en_b->desc)
+		return -1;
+	if (en_a->desc > en_b->desc)
+		return 1;
+
+	return 0;
+}
+
+static void ipstats_enabled_free(struct ipstats_stat_enabled *enabled)
+{
+	free(enabled->enabled);
+}
+
+static const struct ipstats_stat_desc *
+ipstats_stat_desc_find(const struct ipstats_stat_desc *desc,
+		       const char *name)
+{
+	size_t i;
+
+	assert(desc->kind == IPSTATS_STAT_DESC_KIND_GROUP);
+	for (i = 0; i < desc->nsubs; i++) {
+		const struct ipstats_stat_desc *sub = desc->subs[i];
+
+		if (strcmp(sub->name, name) == 0)
+			return sub;
+	}
+
+	return NULL;
+}
+
+static const struct ipstats_stat_desc *
+ipstats_enable_find_stat_desc(struct ipstats_sel *sel)
+{
+	const struct ipstats_stat_desc *toplev = &ipstats_stat_desc_toplev_group;
+	const struct ipstats_stat_desc *desc = toplev;
+	int i;
+
+	for (i = 0; i < IPSTATS_LEVELS_COUNT; i++) {
+		const struct ipstats_stat_desc *next_desc;
+
+		if (sel->sel[i] == NULL)
+			break;
+		if (desc->kind == IPSTATS_STAT_DESC_KIND_LEAF) {
+			fprintf(stderr, "Error: %s %s requested inside leaf %s %s\n",
+				ipstats_levels[i], sel->sel[i],
+				ipstats_levels[i - 1], desc->name);
+			return NULL;
+		}
+
+		next_desc = ipstats_stat_desc_find(desc, sel->sel[i]);
+		if (next_desc == NULL) {
+			fprintf(stderr, "Error: no %s named %s found inside %s\n",
+				ipstats_levels[i], sel->sel[i], desc->name);
+			return NULL;
+		}
+
+		desc = next_desc;
+	}
+
+	return desc;
+}
+
+static int ipstats_enable(struct ipstats_sel *sel,
+			  struct ipstats_stat_enabled *enabled)
+{
+	struct ipstats_stat_enabled new_enabled = {};
+	const struct ipstats_stat_desc *desc;
+	size_t i, j;
+	int err = 0;
+
+	desc = ipstats_enable_find_stat_desc(sel);
+	if (desc == NULL)
+		return -EINVAL;
+
+	err = ipstats_enable_recursively(desc, &new_enabled, sel);
+	if (err != 0)
+		return err;
+
+	err = ipstats_add_enabled(new_enabled.enabled, new_enabled.nenabled,
+				  enabled);
+	if (err != 0)
+		goto out;
+
+	qsort(enabled->enabled, enabled->nenabled, sizeof(*enabled->enabled),
+	      ipstats_comp_enabled);
+
+	for (i = 1, j = 1; i < enabled->nenabled; i++) {
+		if (enabled->enabled[i].desc != enabled->enabled[j - 1].desc)
+			enabled->enabled[j++] = enabled->enabled[i];
+	}
+	enabled->nenabled = j;
+
+out:
+	ipstats_enabled_free(&new_enabled);
+	return err;
+}
+
+static int ipstats_enable_check(struct ipstats_sel *sel,
+				struct ipstats_stat_enabled *enabled)
+{
+	int err;
+	int i;
+
+	err = ipstats_enable(sel, enabled);
+	if (err == -ENOENT) {
+		fprintf(stderr, "The request for");
+		for (i = 0; i < IPSTATS_LEVELS_COUNT; i++)
+			if (sel->sel[i] != NULL)
+				fprintf(stderr, " %s %s",
+					ipstats_levels[i], sel->sel[i]);
+			else
+				break;
+		fprintf(stderr, " did not match any known stats.\n");
+	}
+
+	return err;
+}
+
+static int do_help(void)
+{
+	const struct ipstats_stat_desc *toplev = &ipstats_stat_desc_toplev_group;
+	int i;
+
+	fprintf(stderr,
+		"Usage: ip stats help\n"
+		"       ip stats show [ dev DEV ] [ group GROUP [ subgroup SUBGROUP [ suite SUITE ] ... ] ... ] ...\n"
+		"       ip stats set dev DEV l3_stats { on | off }\n"
+		);
+
+	for (i = 0; i < toplev->nsubs; i++) {
+		const struct ipstats_stat_desc *desc = toplev->subs[i];
+
+		if (i == 0)
+			fprintf(stderr, "GROUP := { %s", desc->name);
+		else
+			fprintf(stderr, " | %s", desc->name);
+	}
+	if (i > 0)
+		fprintf(stderr, " }\n");
+
+	for (i = 0; i < toplev->nsubs; i++) {
+		const struct ipstats_stat_desc *desc = toplev->subs[i];
+		bool opened = false;
+		size_t j;
+
+		if (desc->kind != IPSTATS_STAT_DESC_KIND_GROUP)
+			continue;
+
+		for (j = 0; j < desc->nsubs; j++) {
+			size_t k;
+
+			if (j == 0)
+				fprintf(stderr, "%s SUBGROUP := {", desc->name);
+			else
+				fprintf(stderr, " |");
+			fprintf(stderr, " %s", desc->subs[j]->name);
+			opened = true;
+
+			if (desc->subs[j]->kind != IPSTATS_STAT_DESC_KIND_GROUP)
+				continue;
+
+			for (k = 0; k < desc->subs[j]->nsubs; k++)
+				fprintf(stderr, " [ suite %s ]",
+					desc->subs[j]->subs[k]->name);
+		}
+		if (opened)
+			fprintf(stderr, " }\n");
+	}
+
+	return 0;
+}
+
+static int ipstats_select(struct ipstats_sel *old_sel,
+			  const char *new_sel, int level,
+			  struct ipstats_stat_enabled *enabled)
+{
+	int err;
+	int i;
+
+	for (i = 0; i < level; i++) {
+		if (old_sel->sel[i] == NULL) {
+			fprintf(stderr, "Error: %s %s requested without selecting a %s first\n",
+				ipstats_levels[level], new_sel,
+				ipstats_levels[i]);
+			return -EINVAL;
+		}
+	}
+
+	for (i = level; i < IPSTATS_LEVELS_COUNT; i++) {
+		if (old_sel->sel[i] != NULL) {
+			err = ipstats_enable_check(old_sel, enabled);
+			if (err)
+				return err;
+			break;
+		}
+	}
+
+	old_sel->sel[level] = new_sel;
+	for (i = level + 1; i < IPSTATS_LEVELS_COUNT; i++)
+		old_sel->sel[i] = NULL;
+
+	return 0;
+}
+
+static int ipstats_show(int argc, char **argv)
+{
+	struct ipstats_stat_enabled enabled = {};
+	struct ipstats_sel sel = {};
+	const char *dev = NULL;
+	int ifindex;
+	int err;
+	int i;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			if (dev != NULL)
+				duparg2("dev", *argv);
+			if (check_ifname(*argv))
+				invarg("\"dev\" not a valid ifname", *argv);
+			dev = *argv;
+		} else if (strcmp(*argv, "help") == 0) {
+			do_help();
+			return 0;
+		} else {
+			bool found_level = false;
+
+			for (i = 0; i < ARRAY_SIZE(ipstats_levels); i++) {
+				if (strcmp(*argv, ipstats_levels[i]) == 0) {
+					NEXT_ARG();
+					err = ipstats_select(&sel, *argv, i,
+							     &enabled);
+					if (err)
+						goto err;
+
+					found_level = true;
+				}
+			}
+
+			if (!found_level) {
+				fprintf(stderr, "What is \"%s\"?\n", *argv);
+				do_help();
+				err = -EINVAL;
+				goto err;
+			}
+		}
+
+		NEXT_ARG_FWD();
+	}
+
+	/* Push whatever was given. */
+	err = ipstats_enable_check(&sel, &enabled);
+	if (err)
+		goto err;
+
+	if (dev) {
+		ifindex = ll_name_to_index(dev);
+		if (!ifindex) {
+			err = nodev(dev);
+			goto err;
+		}
+	} else {
+		ifindex = 0;
+	}
+
+
+	err = ipstats_show_do(ifindex, &enabled);
+
+err:
+	ipstats_enabled_free(&enabled);
+	return err;
+}
+
+static int ipstats_set_do(int ifindex, int at, bool enable)
+{
+	struct ipstats_req req = {
+		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg)),
+		.nlh.nlmsg_flags = NLM_F_REQUEST,
+		.nlh.nlmsg_type = RTM_SETSTATS,
+		.ifsm.family = PF_UNSPEC,
+		.ifsm.ifindex = ifindex,
+	};
+
+	addattr8(&req.nlh, sizeof(req), at, enable);
+
+	if (rtnl_talk(&rth, &req.nlh, NULL) < 0)
+		return -2;
+	return 0;
+}
+
+static int ipstats_set(int argc, char **argv)
+{
+	const char *dev = NULL;
+	bool enable = false;
+	int ifindex;
+	int at = 0;
+
+	while (argc > 0) {
+		if (strcmp(*argv, "dev") == 0) {
+			NEXT_ARG();
+			if (dev)
+				duparg2("dev", *argv);
+			if (check_ifname(*argv))
+				invarg("\"dev\" not a valid ifname", *argv);
+			dev = *argv;
+		} else if (strcmp(*argv, "l3_stats") == 0) {
+			int err;
+
+			NEXT_ARG();
+			if (at) {
+				fprintf(stderr, "A statistics suite to toggle was already given.\n");
+				return -EINVAL;
+			}
+			at = IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS;
+			enable = parse_on_off("l3_stats", *argv, &err);
+			if (err)
+				return err;
+		} else if (strcmp(*argv, "help") == 0) {
+			do_help();
+			return 0;
+		} else {
+			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			do_help();
+			return -EINVAL;
+		}
+
+		NEXT_ARG_FWD();
+	}
+
+	if (!dev) {
+		fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
+		exit(-1);
+	}
+
+	if (!at) {
+		fprintf(stderr, "Not enough information: stat type to toggle is required.\n");
+		exit(-1);
+	}
+
+	ifindex = ll_name_to_index(dev);
+	if (!ifindex)
+		return nodev(dev);
+
+	return ipstats_set_do(ifindex, at, enable);
+}
+
+int do_ipstats(int argc, char **argv)
+{
+	int rc;
+
+	if (argc == 0) {
+		rc = ipstats_show(0, NULL);
+	} else if (strcmp(*argv, "help") == 0) {
+		do_help();
+		rc = 0;
+	} else if (strcmp(*argv, "show") == 0) {
+		/* Invoking "stats show" implies one -s. Passing -d adds one
+		 * more -s.
+		 */
+		show_stats += show_details + 1;
+		rc = ipstats_show(argc-1, argv+1);
+	} else if (strcmp(*argv, "set") == 0) {
+		rc = ipstats_set(argc-1, argv+1);
+	} else {
+		fprintf(stderr, "Command \"%s\" is unknown, try \"ip stats help\".\n",
+			*argv);
+		rc = -1;
+	}
+
+	return rc;
+}
+
+int ipstats_print(struct nlmsghdr *n, void *arg)
+{
+	struct ipstats_stat_enabled_one one = {
+		.desc = &ipstats_stat_desc_offload_hw_s_info,
+	};
+	struct ipstats_stat_enabled enabled = {
+		.enabled = &one,
+		.nenabled = 1,
+	};
+	FILE *fp = arg;
+	int rc;
+
+	rc = ipstats_process_ifsm(n, &enabled);
+	if (rc)
+		return rc;
+
+	fflush(fp);
+	return 0;
+}
diff -pruN 5.15.0-1/ip/iptuntap.c 5.19.0-1/ip/iptuntap.c
--- 5.15.0-1/ip/iptuntap.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/iptuntap.c	2022-08-02 18:36:33.000000000 +0000
@@ -321,14 +321,16 @@ static void show_processes(const char *n
 			} else if (err == 2 &&
 				   !strcmp("iff", key) &&
 				   !strcmp(name, value)) {
-				char *pname = get_task_name(pid);
+				SPRINT_BUF(pname);
 
-				print_string(PRINT_ANY, "name",
-					     "%s", pname ? : "<NULL>");
+				if (get_task_name(pid, pname, sizeof(pname)))
+					print_string(PRINT_ANY, "name",
+						     "%s", "<NULL>");
+				else
+					print_string(PRINT_ANY, "name",
+						     "%s", pname);
 
-				print_uint(PRINT_ANY, "pid",
-					   "(%d)", pid);
-				free(pname);
+				print_uint(PRINT_ANY, "pid", "(%d)", pid);
 			}
 
 			free(key);
diff -pruN 5.15.0-1/ip/link_xfrm.c 5.19.0-1/ip/link_xfrm.c
--- 5.15.0-1/ip/link_xfrm.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/link_xfrm.c	2022-08-02 18:36:33.000000000 +0000
@@ -19,7 +19,7 @@ static void xfrm_print_help(struct link_
 	fprintf(f,
 		"Usage: ... %-4s dev [ PHYS_DEV ] [ if_id IF-ID ]\n"
 		"\n"
-		"Where: IF-ID := { 0x0..0xffffffff }\n",
+		"Where: IF-ID := { 0x1..0xffffffff }\n",
 		lu->id);
 }
 
@@ -39,6 +39,8 @@ static int xfrm_parse_opt(struct link_ut
 			NEXT_ARG();
 			if (get_u32(&if_id, *argv, 0))
 				invarg("if_id value is invalid", *argv);
+			else if (!if_id)
+				invarg("if_id value is invalid", *argv);
 			else
 				addattr32(n, 1024, IFLA_XFRM_IF_ID, if_id);
 		} else {
@@ -48,6 +50,9 @@ static int xfrm_parse_opt(struct link_ut
 		argc--; argv++;
 	}
 
+	if (!if_id)
+		missarg("IF_ID");
+
 	if (link)
 		addattr32(n, 1024, IFLA_XFRM_LINK, link);
 
diff -pruN 5.15.0-1/ip/Makefile 5.19.0-1/ip/Makefile
--- 5.15.0-1/ip/Makefile	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/Makefile	2022-08-02 18:36:33.000000000 +0000
@@ -11,14 +11,16 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o ipr
     iplink_bridge.o iplink_bridge_slave.o ipfou.o iplink_ipvlan.o \
     iplink_geneve.o iplink_vrf.o iproute_lwtunnel.o ipmacsec.o ipila.o \
     ipvrf.o iplink_xstats.o ipseg6.o iplink_netdevsim.o iplink_rmnet.o \
-    ipnexthop.o ipmptcp.o iplink_bareudp.o iplink_wwan.o ipioam6.o
+    ipnexthop.o ipmptcp.o iplink_bareudp.o iplink_wwan.o ipioam6.o \
+    iplink_amt.o iplink_batadv.o iplink_gtp.o iplink_virt_wifi.o \
+    ipstats.o
 
 RTMONOBJ=rtmon.o
 
 include ../config.mk
 
 ALLOBJ=$(IPOBJ) $(RTMONOBJ)
-SCRIPTS=ifcfg rtpr routel routef
+SCRIPTS=routel
 TARGETS=ip rtmon
 
 all: $(TARGETS) $(SCRIPTS)
diff -pruN 5.15.0-1/ip/nh_common.h 5.19.0-1/ip/nh_common.h
--- 5.15.0-1/ip/nh_common.h	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/ip/nh_common.h	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __NH_COMMON_H__
+#define __NH_COMMON_H__ 1
+
+#include <list.h>
+
+#define NH_CACHE_SIZE		1024
+
+struct nha_res_grp {
+	__u16			buckets;
+	__u32			idle_timer;
+	__u32			unbalanced_timer;
+	__u64			unbalanced_time;
+};
+
+struct nh_entry {
+	struct hlist_node	nh_hash;
+
+	__u32			nh_id;
+	__u32			nh_oif;
+	__u32			nh_flags;
+	__u16			nh_grp_type;
+	__u8			nh_family;
+	__u8			nh_scope;
+	__u8			nh_protocol;
+
+	bool			nh_blackhole;
+	bool			nh_fdb;
+
+	int			nh_gateway_len;
+	union {
+		__be32		ipv4;
+		struct in6_addr	ipv6;
+	}			nh_gateway;
+
+	struct rtattr		*nh_encap;
+	union {
+		struct rtattr   rta;
+		__u8		_buf[RTA_LENGTH(sizeof(__u16))];
+	}			nh_encap_type;
+
+	bool			nh_has_res_grp;
+	struct nha_res_grp	nh_res_grp;
+
+	int			nh_groups_cnt;
+	struct nexthop_grp	*nh_groups;
+};
+
+void print_cache_nexthop_id(FILE *fp, const char *fp_prefix, const char *jsobj,
+			    __u32 nh_id);
+int print_cache_nexthop(struct nlmsghdr *n, void *arg, bool process_cache);
+
+#endif /* __NH_COMMON_H__ */
diff -pruN 5.15.0-1/ip/routef 5.19.0-1/ip/routef
--- 5.15.0-1/ip/routef	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/routef	1970-01-01 00:00:00.000000000 +0000
@@ -1,10 +0,0 @@
-#! /bin/sh
-# SPDX-License-Identifier: GPL-2.0
-
-if [ -z "$*" ] ; then
-	exec ip -4 ro flush  scope global  type unicast
-else
-	echo "Usage: routef"
-	echo
-	echo "This script will flush the IPv4 routing table"
-fi
diff -pruN 5.15.0-1/ip/routel 5.19.0-1/ip/routel
--- 5.15.0-1/ip/routel	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/routel	2022-08-02 18:36:33.000000000 +0000
@@ -1,72 +1,62 @@
-#!/bin/sh
+#! /usr/bin/env python3
 # SPDX-License-Identifier: GPL-2.0
-
-#
-# Script created by: Stephen R. van den Berg <srb@cuci.nl>, 1999/04/18
-# Donated to the public domain.
-#
-# This script transforms the output of "ip" into more readable text.
-# "ip" is the Linux-advanced-routing configuration tool part of the
-# iproute package.
 #
+# This is simple script to process JSON output from ip route
+# command and format it.  Based on earlier shell script version.
+"""Script to parse ip route output into more readable text."""
+
+import sys
+import json
+import getopt
+import subprocess
+
+
+def usage():
+    '''Print usage and exit'''
+    print("Usage: {} [tablenr [raw ip args...]]".format(sys.argv[0]))
+    sys.exit(64)
+
+
+def main():
+    '''Process the arguments'''
+    family = 'inet'
+    try:
+        opts, args = getopt.getopt(sys.argv[1:], "h46f:", ["help", "family="])
+    except getopt.GetoptError as err:
+        print(err)
+        usage()
+
+    for opt, arg in opts:
+        if opt in ["-h", "--help"]:
+            usage()
+        elif opt == '-6':
+            family = 'inet6'
+        elif opt == "-4":
+            family = 'inet'
+        elif opt in ["-f", "--family"]:
+            family = arg
+        else:
+            assert False, "unhandled option"
+
+    if not args:
+        args = ['0']
+
+    cmd = ['ip', '-f', family, '-j', 'route', 'list', 'table'] + args
+    process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+    tbl = json.load(process.stdout)
+    if family == 'inet':
+        fmt = '{:15} {:15} {:15} {:8} {:8}{:<16} {}'
+    else:
+        fmt = '{:32} {:32} {:32} {:8} {:8}{:<16} {}'
+
+    # ip route json keys
+    keys = ['dst', 'gateway', 'prefsrc', 'protocol', 'scope', 'dev', 'table']
+    print(fmt.format(*map(lambda x: x.capitalize(), keys)))
 
-test "X-h" = "X$1" && echo "Usage: $0 [tablenr [raw ip args...]]" && exit 64
+    for record in tbl:
+        fields = [record[k] if k in record else '' for k in keys]
+        print(fmt.format(*fields))
 
-test -z "$*" && set 0
 
-ip route list table "$@" |
- while read network rest
- do set xx $rest
-    shift
-    proto=""
-    via=""
-    dev=""
-    scope=""
-    src=""
-    table=""
-    case $network in
-       broadcast|local|unreachable) via=$network
-          network=$1
-          shift
-          ;;
-    esac
-    while test $# != 0
-    do
-       case "$1" in
-          proto|via|dev|scope|src|table)
-             key=$1
-             val=$2
-             eval "$key='$val'"
-             shift 2
-             ;;
-          dead|onlink|pervasive|offload|notify|linkdown|unresolved)
-             shift
-             ;;
-          *)
-             # avoid infinite loop on unknown keyword without value at line end
-             shift
-             shift
-             ;;
-       esac
-    done
-    echo "$network	$via	$src	$proto	$scope	$dev	$table"
- done | awk -F '	' '
-BEGIN {
-   format="%15s%-3s %15s %15s %8s %8s%7s %s\n";
-   printf(format,"target","","gateway","source","proto","scope","dev","tbl");
- }
- { network=$1;
-   mask="";
-   if(match(network,"/"))
-    { mask=" "substr(network,RSTART+1);
-      network=substr(network,0,RSTART);
-    }
-   via=$2;
-   src=$3;
-   proto=$4;
-   scope=$5;
-   dev=$6;
-   table=$7;
-   printf(format,network,mask,via,src,proto,scope,dev,table);
- }
-'
+if __name__ == "__main__":
+    main()
diff -pruN 5.15.0-1/ip/rtpr 5.19.0-1/ip/rtpr
--- 5.15.0-1/ip/rtpr	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/rtpr	1970-01-01 00:00:00.000000000 +0000
@@ -1,5 +0,0 @@
-#! /bin/sh
-# SPDX-License-Identifier: GPL-2.0
-
-exec tr "[\\\\]" "[
-]"
diff -pruN 5.15.0-1/ip/tunnel.c 5.19.0-1/ip/tunnel.c
--- 5.15.0-1/ip/tunnel.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/tunnel.c	2022-08-02 18:36:33.000000000 +0000
@@ -298,14 +298,8 @@ void tnl_print_endpoint(const char *name
 			value = "unknown";
 	}
 
-	if (is_json_context()) {
-		print_string(PRINT_JSON, name, NULL, value);
-	} else {
-		SPRINT_BUF(b1);
-
-		snprintf(b1, sizeof(b1), "%s %%s ", name);
-		print_string(PRINT_FP, NULL, b1, value);
-	}
+	print_string_name_value(name, value);
+	print_string(PRINT_FP, NULL, " ", NULL);
 }
 
 void tnl_print_gre_flags(__u8 proto,
diff -pruN 5.15.0-1/ip/xfrm.h 5.19.0-1/ip/xfrm.h
--- 5.15.0-1/ip/xfrm.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/xfrm.h	2022-08-02 18:36:33.000000000 +0000
@@ -132,6 +132,7 @@ void xfrm_state_info_print(struct xfrm_u
 void xfrm_policy_info_print(struct xfrm_userpolicy_info *xpinfo,
 			    struct rtattr *tb[], FILE *fp, const char *prefix,
 			    const char *title);
+int xfrm_policy_default_print(struct nlmsghdr *n, FILE *fp);
 int xfrm_id_parse(xfrm_address_t *saddr, struct xfrm_id *id, __u16 *family,
 		  int loose, int *argcp, char ***argvp);
 int xfrm_mode_parse(__u8 *mode, int *argcp, char ***argvp);
diff -pruN 5.15.0-1/ip/xfrm_monitor.c 5.19.0-1/ip/xfrm_monitor.c
--- 5.15.0-1/ip/xfrm_monitor.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/xfrm_monitor.c	2022-08-02 18:36:33.000000000 +0000
@@ -323,6 +323,9 @@ static int xfrm_accept_msg(struct rtnl_c
 	case XFRM_MSG_MAPPING:
 		xfrm_mapping_print(n, arg);
 		return 0;
+	case XFRM_MSG_GETDEFAULT:
+		xfrm_policy_default_print(n, arg);
+		return 0;
 	default:
 		break;
 	}
diff -pruN 5.15.0-1/ip/xfrm_policy.c 5.19.0-1/ip/xfrm_policy.c
--- 5.15.0-1/ip/xfrm_policy.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/ip/xfrm_policy.c	2022-08-02 18:36:33.000000000 +0000
@@ -66,6 +66,8 @@ static void usage(void)
 		"Usage: ip xfrm policy flush [ ptype PTYPE ]\n"
 		"Usage: ip xfrm policy count\n"
 		"Usage: ip xfrm policy set [ hthresh4 LBITS RBITS ] [ hthresh6 LBITS RBITS ]\n"
+		"Usage: ip xfrm policy setdefault DIR ACTION [ DIR ACTION ] [ DIR ACTION ]\n"
+		"Usage: ip xfrm policy getdefault\n"
 		"SELECTOR := [ src ADDR[/PLEN] ] [ dst ADDR[/PLEN] ] [ dev DEV ] [ UPSPEC ]\n"
 		"UPSPEC := proto { { tcp | udp | sctp | dccp } [ sport PORT ] [ dport PORT ] |\n"
 		"                  { icmp | ipv6-icmp | mobility-header } [ type NUMBER ] [ code NUMBER ] |\n"
@@ -1124,6 +1126,133 @@ static int xfrm_spd_getinfo(int argc, ch
 	return 0;
 }
 
+static int xfrm_str_to_policy(char *name, uint8_t *policy)
+{
+	if (strcmp(name, "block") == 0) {
+		*policy = XFRM_USERPOLICY_BLOCK;
+		return 0;
+	} else if (strcmp(name, "accept") == 0) {
+		*policy = XFRM_USERPOLICY_ACCEPT;
+		return 0;
+	}
+
+	return -1;
+}
+
+static char *xfrm_policy_to_str(uint8_t policy)
+{
+	switch (policy) {
+	case XFRM_USERPOLICY_UNSPEC:
+		return "unspec";
+	case XFRM_USERPOLICY_BLOCK:
+		return "block";
+	case XFRM_USERPOLICY_ACCEPT:
+		return "accept";
+	default:
+		return "unknown";
+	}
+}
+
+static int xfrm_spd_setdefault(int argc, char **argv)
+{
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr			n;
+		struct xfrm_userpolicy_default  up;
+	} req = {
+		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_default)),
+		.n.nlmsg_flags = NLM_F_REQUEST,
+		.n.nlmsg_type = XFRM_MSG_SETDEFAULT,
+	};
+
+	while (argc > 0) {
+		if (strcmp(*argv, "in") == 0) {
+			if (req.up.in)
+				duparg("in", *argv);
+
+			NEXT_ARG();
+			if (xfrm_str_to_policy(*argv, &req.up.in) < 0)
+				invarg("in policy value is invalid", *argv);
+		} else if (strcmp(*argv, "fwd") == 0) {
+			if (req.up.fwd)
+				duparg("fwd", *argv);
+
+			NEXT_ARG();
+			if (xfrm_str_to_policy(*argv, &req.up.fwd) < 0)
+				invarg("fwd policy value is invalid", *argv);
+		} else if (strcmp(*argv, "out") == 0) {
+			if (req.up.out)
+				duparg("out", *argv);
+
+			NEXT_ARG();
+			if (xfrm_str_to_policy(*argv, &req.up.out) < 0)
+				invarg("out policy value is invalid", *argv);
+		} else {
+			invarg("unknown direction", *argv);
+		}
+
+		argc--; argv++;
+	}
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (rtnl_talk(&rth, &req.n, NULL) < 0)
+		exit(2);
+
+	rtnl_close(&rth);
+
+	return 0;
+}
+
+int xfrm_policy_default_print(struct nlmsghdr *n, FILE *fp)
+{
+	struct xfrm_userpolicy_default *up = NLMSG_DATA(n);
+	int len = n->nlmsg_len - NLMSG_SPACE(sizeof(*up));
+
+	if (len < 0) {
+		fprintf(stderr,
+			"BUG: short nlmsg len %u (expect %lu) for XFRM_MSG_GETDEFAULT\n",
+			n->nlmsg_len, NLMSG_SPACE(sizeof(*up)));
+		return -1;
+	}
+
+	fprintf(fp, "Default policies:\n");
+	fprintf(fp, " in:  %s\n", xfrm_policy_to_str(up->in));
+	fprintf(fp, " fwd: %s\n", xfrm_policy_to_str(up->fwd));
+	fprintf(fp, " out: %s\n", xfrm_policy_to_str(up->out));
+	fflush(fp);
+
+	return 0;
+}
+
+static int xfrm_spd_getdefault(int argc, char **argv)
+{
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr			n;
+		struct xfrm_userpolicy_default  up;
+	} req = {
+		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_default)),
+		.n.nlmsg_flags = NLM_F_REQUEST,
+		.n.nlmsg_type = XFRM_MSG_GETDEFAULT,
+	};
+	struct nlmsghdr *answer;
+
+	if (rtnl_open_byproto(&rth, 0, NETLINK_XFRM) < 0)
+		exit(1);
+
+	if (rtnl_talk(&rth, &req.n, &answer) < 0)
+		exit(2);
+
+	xfrm_policy_default_print(answer, (FILE *)stdout);
+
+	free(answer);
+	rtnl_close(&rth);
+
+	return 0;
+}
+
 static int xfrm_policy_flush(int argc, char **argv)
 {
 	struct rtnl_handle rth;
@@ -1197,6 +1326,10 @@ int do_xfrm_policy(int argc, char **argv
 		return xfrm_spd_getinfo(argc, argv);
 	if (matches(*argv, "set") == 0)
 		return xfrm_spd_setinfo(argc-1, argv+1);
+	if (matches(*argv, "setdefault") == 0)
+		return xfrm_spd_setdefault(argc-1, argv+1);
+	if (matches(*argv, "getdefault") == 0)
+		return xfrm_spd_getdefault(argc-1, argv+1);
 	if (matches(*argv, "help") == 0)
 		usage();
 	fprintf(stderr, "Command \"%s\" is unknown, try \"ip xfrm policy help\".\n", *argv);
diff -pruN 5.15.0-1/lib/ax25_ntop.c 5.19.0-1/lib/ax25_ntop.c
--- 5.15.0-1/lib/ax25_ntop.c	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/lib/ax25_ntop.c	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <linux/ax25.h>
+
+#include "utils.h"
+
+const char *ax25_ntop1(const ax25_address *src, char *dst, socklen_t size);
+
+/*
+ * AX.25 addresses are based on Amateur radio callsigns followed by an SSID
+ * like XXXXXX-SS where the callsign consists of up to 6 ASCII characters
+ * which are either letters or digits and the SSID is a decimal number in the
+ * range 0..15.
+ * Amateur radio callsigns are assigned by a country's relevant authorities
+ * and are 3..6 characters though a few countries have assigned callsigns
+ * longer than that.  AX.25 is not able to handle such longer callsigns.
+ * There are further restrictions on the format of valid callsigns by
+ * applicable national and international law.  Linux doesn't need to care and
+ * will happily accept anything that consists of 6 ASCII characters in the
+ * range of A-Z and 0-9 for a callsign such as the default AX.25 MAC address
+ * LINUX-1 and the default broadcast address QST-0.
+ * The SSID is just a number and not encoded in ASCII digits.
+ *
+ * Being based on HDLC AX.25 encodes addresses by shifting them one bit left
+ * thus zeroing bit 0, the HDLC extension bit for all but the last bit of
+ * a packet's address field but for our purposes here we're not considering
+ * the HDLC extension bit that is it will always be zero.
+ *
+ * Linux' internal representation of AX.25 addresses in Linux is very similar
+ * to this on the on-air or on-the-wire format.  The callsign is padded to
+ * 6 octets by adding spaces, followed by the SSID octet then all 7 octets
+ * are left-shifted by one bit.
+ *
+ * For example, for the address "LINUX-1" the callsign is LINUX and SSID is 1
+ * the internal format is 98:92:9c:aa:b0:40:02.
+ */
+
+const char *ax25_ntop1(const ax25_address *src, char *dst, socklen_t size)
+{
+	char c, *s;
+	int n;
+
+	for (n = 0, s = dst; n < 6; n++) {
+		c = (src->ax25_call[n] >> 1) & 0x7f;
+		if (c != ' ')
+			*s++ = c;
+	}
+
+	*s++ = '-';
+
+	n = ((src->ax25_call[6] >> 1) & 0x0f);
+	if (n > 9) {
+		*s++ = '1';
+		n -= 10;
+	}
+
+	*s++ = n + '0';
+	*s++ = '\0';
+
+	if (*dst == '\0' || *dst == '-') {
+		dst[0] = '*';
+		dst[1] = '\0';
+	}
+
+	return dst;
+}
+
+const char *ax25_ntop(int af, const void *addr, char *buf, socklen_t buflen)
+{
+	switch (af) {
+	case AF_AX25:
+		errno = 0;
+		return ax25_ntop1((ax25_address *)addr, buf, buflen);
+
+	default:
+		errno = EAFNOSUPPORT;
+	}
+
+	return NULL;
+}
diff -pruN 5.15.0-1/lib/bpf_glue.c 5.19.0-1/lib/bpf_glue.c
--- 5.15.0-1/lib/bpf_glue.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/lib/bpf_glue.c	2022-08-02 18:36:33.000000000 +0000
@@ -4,22 +4,24 @@
  * Authors:	Hangbin Liu <haliu@redhat.com>
  *
  */
+#include <sys/syscall.h>
 #include <limits.h>
+#include <unistd.h>
+#include <errno.h>
 
 #include "bpf_util.h"
 #ifdef HAVE_LIBBPF
 #include <bpf/bpf.h>
 #endif
 
-int bpf_program_load(enum bpf_prog_type type, const struct bpf_insn *insns,
-		     size_t size_insns, const char *license, char *log,
-		     size_t size_log)
+int bpf(int cmd, union bpf_attr *attr, unsigned int size)
 {
-#ifdef HAVE_LIBBPF
-	return bpf_load_program(type, insns, size_insns / sizeof(struct bpf_insn),
-				license, 0, log, size_log);
+#ifdef __NR_bpf
+	return syscall(__NR_bpf, cmd, attr, size);
 #else
-	return bpf_prog_load_dev(type, insns, size_insns, license, 0, log, size_log);
+	fprintf(stderr, "No bpf syscall, kernel headers too old?\n");
+	errno = ENOSYS;
+	return -1;
 #endif
 }
 
diff -pruN 5.15.0-1/lib/bpf_legacy.c 5.19.0-1/lib/bpf_legacy.c
--- 5.15.0-1/lib/bpf_legacy.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/lib/bpf_legacy.c	2022-08-02 18:36:33.000000000 +0000
@@ -33,7 +33,6 @@
 #include <sys/un.h>
 #include <sys/vfs.h>
 #include <sys/mount.h>
-#include <sys/syscall.h>
 #include <sys/sendfile.h>
 #include <sys/resource.h>
 
@@ -134,17 +133,6 @@ static inline __u64 bpf_ptr_to_u64(const
 	return (__u64)(unsigned long)ptr;
 }
 
-static int bpf(int cmd, union bpf_attr *attr, unsigned int size)
-{
-#ifdef __NR_bpf
-	return syscall(__NR_bpf, cmd, attr, size);
-#else
-	fprintf(stderr, "No bpf syscall, kernel headers too old?\n");
-	errno = ENOSYS;
-	return -1;
-#endif
-}
-
 static int bpf_map_update(int fd, const void *key, const void *value,
 			  uint64_t flags)
 {
@@ -203,12 +191,30 @@ int bpf_dump_prog_info(FILE *f, uint32_t
 	if (!ret && len) {
 		int jited = !!info.jited_prog_len;
 
+		print_string(PRINT_ANY, "name", "name %s ", info.name);
 		print_string(PRINT_ANY, "tag", "tag %s ",
 			     hexstring_n2a(info.tag, sizeof(info.tag),
 					   tmp, sizeof(tmp)));
 		print_uint(PRINT_JSON, "jited", NULL, jited);
 		if (jited && !is_json_context())
 			fprintf(f, "jited ");
+
+		if (show_details) {
+			if (info.load_time) {
+				/* ns since boottime */
+				print_lluint(PRINT_ANY, "load_time",
+					     "load_time %llu ", info.load_time);
+
+				print_luint(PRINT_ANY, "created_by_uid",
+					    "created_by_uid %lu ",
+					    info.created_by_uid);
+			}
+
+			if (info.btf_id)
+				print_luint(PRINT_ANY, "btf_id", "btf_id %lu ",
+					    info.btf_id);
+		}
+
 		dump_ok = 1;
 	}
 
@@ -1108,6 +1114,13 @@ int bpf_prog_load_dev(enum bpf_prog_type
 	return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
 }
 
+int bpf_program_load(enum bpf_prog_type type, const struct bpf_insn *insns,
+		     size_t size_insns, const char *license, char *log,
+		     size_t size_log)
+{
+	return bpf_prog_load_dev(type, insns, size_insns, license, 0, log, size_log);
+}
+
 #ifdef HAVE_ELF
 struct bpf_elf_prog {
 	enum bpf_prog_type	type;
diff -pruN 5.15.0-1/lib/bpf_libbpf.c 5.19.0-1/lib/bpf_libbpf.c
--- 5.15.0-1/lib/bpf_libbpf.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/lib/bpf_libbpf.c	2022-08-02 18:36:33.000000000 +0000
@@ -23,12 +23,14 @@
 
 #include "bpf_util.h"
 
-static int verbose_print(enum libbpf_print_level level, const char *format, va_list args)
+static int __attribute__((format(printf, 2, 0)))
+verbose_print(enum libbpf_print_level level, const char *format, va_list args)
 {
 	return vfprintf(stderr, format, args);
 }
 
-static int silent_print(enum libbpf_print_level level, const char *format, va_list args)
+static int __attribute__((format(printf, 2, 0)))
+silent_print(enum libbpf_print_level level, const char *format, va_list args)
 {
 	if (level > LIBBPF_WARN)
 		return 0;
@@ -52,18 +54,18 @@ static const char *get_bpf_program__sect
 static int create_map(const char *name, struct bpf_elf_map *map,
 		      __u32 ifindex, int inner_fd)
 {
-	struct bpf_create_map_attr map_attr = {};
+	union bpf_attr attr = {};
 
-	map_attr.name = name;
-	map_attr.map_type = map->type;
-	map_attr.map_flags = map->flags;
-	map_attr.key_size = map->size_key;
-	map_attr.value_size = map->size_value;
-	map_attr.max_entries = map->max_elem;
-	map_attr.map_ifindex = ifindex;
-	map_attr.inner_map_fd = inner_fd;
+	attr.map_type = map->type;
+	strlcpy(attr.map_name, name, sizeof(attr.map_name));
+	attr.map_flags = map->flags;
+	attr.key_size = map->size_key;
+	attr.value_size = map->size_value;
+	attr.max_entries = map->max_elem;
+	attr.map_ifindex = ifindex;
+	attr.inner_map_fd = inner_fd;
 
-	return bpf_create_map_xattr(&map_attr);
+	return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
 }
 
 static int create_map_in_map(struct bpf_object *obj, struct bpf_map *map,
@@ -149,7 +151,8 @@ handle_legacy_map_in_map(struct bpf_obje
 	return ret;
 }
 
-static int find_legacy_tail_calls(struct bpf_program *prog, struct bpf_object *obj)
+static int find_legacy_tail_calls(struct bpf_program *prog, struct bpf_object *obj,
+				  struct bpf_map **pmap)
 {
 	unsigned int map_id, key_id;
 	const char *sec_name;
@@ -173,8 +176,8 @@ static int find_legacy_tail_calls(struct
 	if (!map)
 		return -1;
 
-	/* Save the map here for later updating */
-	bpf_program__set_priv(prog, map, NULL);
+	if (pmap)
+		*pmap = map;
 
 	return 0;
 }
@@ -188,8 +191,10 @@ static int update_legacy_tail_call_maps(
 	struct bpf_map *map;
 
 	bpf_object__for_each_program(prog, obj) {
-		map = bpf_program__priv(prog);
-		if (!map)
+		/* load_bpf_object has already verified find_legacy_tail_calls
+		 * succeeds when it should
+		 */
+		if (find_legacy_tail_calls(prog, obj, &map) < 0)
 			continue;
 
 		prog_fd = bpf_program__fd(prog);
@@ -244,6 +249,11 @@ static int handle_legacy_maps(struct bpf
 	return ret;
 }
 
+static bool bpf_map_is_offload_neutral(const struct bpf_map *map)
+{
+	return bpf_map__type(map) == BPF_MAP_TYPE_PERF_EVENT_ARRAY;
+}
+
 static int load_bpf_object(struct bpf_cfg_in *cfg)
 {
 	struct bpf_program *p, *prog = NULL;
@@ -273,7 +283,8 @@ static int load_bpf_object(struct bpf_cf
 
 		/* Only load the programs that will either be subsequently
 		 * attached or inserted into a tail call map */
-		if (find_legacy_tail_calls(p, obj) < 0 && !prog_to_attach) {
+		if (find_legacy_tail_calls(p, obj, NULL) < 0 &&
+		    !prog_to_attach) {
 			ret = bpf_program__set_autoload(p, false);
 			if (ret)
 				return -EINVAL;
@@ -288,7 +299,7 @@ static int load_bpf_object(struct bpf_cf
 	}
 
 	bpf_object__for_each_map(map, obj) {
-		if (!bpf_map__is_offload_neutral(map))
+		if (!bpf_map_is_offload_neutral(map))
 			bpf_map__set_ifindex(map, cfg->ifindex);
 	}
 
diff -pruN 5.15.0-1/lib/fs.c 5.19.0-1/lib/fs.c
--- 5.15.0-1/lib/fs.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/lib/fs.c	2022-08-02 18:36:33.000000000 +0000
@@ -342,25 +342,28 @@ int get_command_name(const char *pid, ch
 	return 0;
 }
 
-char *get_task_name(pid_t pid)
+int get_task_name(pid_t pid, char *name, size_t len)
 {
-	char *comm;
+	char path[PATH_MAX];
 	FILE *f;
 
 	if (!pid)
-		return NULL;
+		return -1;
 
-	if (asprintf(&comm, "/proc/%d/comm", pid) < 0)
-		return NULL;
+	if (snprintf(path, sizeof(path), "/proc/%d/comm", pid) >= sizeof(path))
+		return -1;
 
-	f = fopen(comm, "r");
+	f = fopen(path, "r");
 	if (!f)
-		return NULL;
+		return -1;
 
-	if (fscanf(f, "%ms\n", &comm) != 1)
-		comm = NULL;
+	if (!fgets(name, len, f))
+		return -1;
+
+	/* comm ends in \n, get rid of it */
+	name[strcspn(name, "\n")] = '\0';
 
 	fclose(f);
 
-	return comm;
+	return 0;
 }
diff -pruN 5.15.0-1/lib/json_print.c 5.19.0-1/lib/json_print.c
--- 5.15.0-1/lib/json_print.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/lib/json_print.c	2022-08-02 18:36:33.000000000 +0000
@@ -299,6 +299,13 @@ int print_color_null(enum output_type ty
 	return ret;
 }
 
+/*
+ * This function does take printf style argument but applying
+ * format attribute to causes more warnings since the print_XXX
+ * functions are used with NULL for format if unused.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
 int print_color_tv(enum output_type type,
 		   enum color_attr color,
 		   const char *key,
@@ -311,6 +318,7 @@ int print_color_tv(enum output_type type
 
 	return print_color_float(type, color, key, fmt, time);
 }
+#pragma GCC diagnostic pop
 
 /* Print line separator (if not in JSON mode) */
 void print_nl(void)
diff -pruN 5.15.0-1/lib/libnetlink.c 5.19.0-1/lib/libnetlink.c
--- 5.15.0-1/lib/libnetlink.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/lib/libnetlink.c	2022-08-02 18:36:33.000000000 +0000
@@ -210,13 +210,13 @@ int rtnl_open_byproto(struct rtnl_handle
 	if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF,
 		       &sndbuf, sizeof(sndbuf)) < 0) {
 		perror("SO_SNDBUF");
-		return -1;
+		goto err;
 	}
 
 	if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF,
 		       &rcvbuf, sizeof(rcvbuf)) < 0) {
 		perror("SO_RCVBUF");
-		return -1;
+		goto err;
 	}
 
 	/* Older kernels may no support extended ACK reporting */
@@ -230,25 +230,28 @@ int rtnl_open_byproto(struct rtnl_handle
 	if (bind(rth->fd, (struct sockaddr *)&rth->local,
 		 sizeof(rth->local)) < 0) {
 		perror("Cannot bind netlink socket");
-		return -1;
+		goto err;
 	}
 	addr_len = sizeof(rth->local);
 	if (getsockname(rth->fd, (struct sockaddr *)&rth->local,
 			&addr_len) < 0) {
 		perror("Cannot getsockname");
-		return -1;
+		goto err;
 	}
 	if (addr_len != sizeof(rth->local)) {
 		fprintf(stderr, "Wrong address length %d\n", addr_len);
-		return -1;
+		goto err;
 	}
 	if (rth->local.nl_family != AF_NETLINK) {
 		fprintf(stderr, "Wrong address family %d\n",
 			rth->local.nl_family);
-		return -1;
+		goto err;
 	}
 	rth->seq = time(NULL);
 	return 0;
+err:
+	rtnl_close(rth);
+	return -1;
 }
 
 int rtnl_open(struct rtnl_handle *rth, unsigned int subscriptions)
@@ -616,12 +619,13 @@ int rtnl_fdb_linkdump_req_filter_fn(stru
 	return send(rth->fd, &req, sizeof(req), 0);
 }
 
-int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam, __u32 filt_mask)
+int rtnl_statsdump_req_filter(struct rtnl_handle *rth, int fam,
+			      __u32 filt_mask,
+			      int (*filter_fn)(struct ipstats_req *req,
+					       void *data),
+			      void *filter_data)
 {
-	struct {
-		struct nlmsghdr nlh;
-		struct if_stats_msg ifsm;
-	} req;
+	struct ipstats_req req;
 
 	memset(&req, 0, sizeof(req));
 	req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct if_stats_msg));
@@ -632,6 +636,14 @@ int rtnl_statsdump_req_filter(struct rtn
 	req.ifsm.family = fam;
 	req.ifsm.filter_mask = filt_mask;
 
+	if (filter_fn) {
+		int err;
+
+		err = filter_fn(&req, filter_data);
+		if (err)
+			return err;
+	}
+
 	return send(rth->fd, &req, sizeof(req), 0);
 }
 
@@ -1597,3 +1609,23 @@ void nl_print_policy(const struct rtattr
 		}
 	}
 }
+
+int rtnl_tunneldump_req(struct rtnl_handle *rth, int family, int ifindex,
+			__u8 flags)
+{
+	struct {
+		struct nlmsghdr nlh;
+		struct tunnel_msg tmsg;
+		char buf[256];
+	} req = {
+		.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tunnel_msg)),
+		.nlh.nlmsg_type = RTM_GETTUNNEL,
+		.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+		.nlh.nlmsg_seq = rth->dump = ++rth->seq,
+		.tmsg.family = family,
+		.tmsg.flags = flags,
+		.tmsg.ifindex = ifindex,
+	};
+
+	return send(rth->fd, &req, sizeof(req), 0);
+}
diff -pruN 5.15.0-1/lib/ll_addr.c 5.19.0-1/lib/ll_addr.c
--- 5.15.0-1/lib/ll_addr.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/lib/ll_addr.c	2022-08-02 18:36:33.000000000 +0000
@@ -39,6 +39,12 @@ const char *ll_addr_n2a(const unsigned c
 
 	if (alen == 16 && (type == ARPHRD_TUNNEL6 || type == ARPHRD_IP6GRE))
 		return inet_ntop(AF_INET6, addr, buf, blen);
+	if (alen == 7 && type == ARPHRD_AX25)
+		return ax25_ntop(AF_AX25, addr, buf, blen);
+	if (alen == 7 && type == ARPHRD_NETROM)
+		return netrom_ntop(AF_NETROM, addr, buf, blen);
+	if (alen == 5 && type == ARPHRD_ROSE)
+		return rose_ntop(AF_ROSE, addr, buf, blen);
 
 	snprintf(buf, blen, "%02x", addr[0]);
 	for (i = 1, l = 2; i < alen && l < blen; i++, l += 3)
diff -pruN 5.15.0-1/lib/ll_proto.c 5.19.0-1/lib/ll_proto.c
--- 5.15.0-1/lib/ll_proto.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/lib/ll_proto.c	2022-08-02 18:36:33.000000000 +0000
@@ -75,7 +75,9 @@ __PF(CONTROL,control)
 __PF(IRDA,irda)
 __PF(ECONET,econet)
 __PF(TIPC,tipc)
+__PF(PROFINET,profinet)
 __PF(AOE,aoe)
+__PF(ETHERCAT,ethercat)
 __PF(8021Q,802.1Q)
 __PF(8021AD,802.1ad)
 __PF(MPLS_UC,mpls_uc)
diff -pruN 5.15.0-1/lib/netrom_ntop.c 5.19.0-1/lib/netrom_ntop.c
--- 5.15.0-1/lib/netrom_ntop.c	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/lib/netrom_ntop.c	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <sys/socket.h>
+#include <errno.h>
+#include <linux/ax25.h>
+
+#include "utils.h"
+
+const char *ax25_ntop1(const ax25_address *src, char *dst, socklen_t size);
+
+const char *netrom_ntop(int af, const void *addr, char *buf, socklen_t buflen)
+{
+	switch (af) {
+	case AF_NETROM:
+		errno = 0;
+		return ax25_ntop1((ax25_address *)addr, buf, buflen);
+
+	default:
+		errno = EAFNOSUPPORT;
+	}
+
+	return NULL;
+}
diff -pruN 5.15.0-1/lib/rose_ntop.c 5.19.0-1/lib/rose_ntop.c
--- 5.15.0-1/lib/rose_ntop.c	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/lib/rose_ntop.c	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/sockios.h>
+#include <linux/rose.h>
+
+#include "rt_names.h"
+#include "utils.h"
+
+static const char *rose_ntop1(const rose_address *src, char *dst,
+			      socklen_t size)
+{
+	char *p = dst;
+	int i;
+
+	if (size < 10)
+		return NULL;
+
+	for (i = 0; i < 5; i++) {
+		*p++ = '0' + ((src->rose_addr[i] >> 4) & 0xf);
+		*p++ = '0' + ((src->rose_addr[i]     ) & 0xf);
+	}
+
+	if (size == 10)
+		return dst;
+
+	*p = '\0';
+
+	return dst;
+}
+
+const char *rose_ntop(int af, const void *addr, char *buf, socklen_t buflen)
+{
+	switch (af) {
+	case AF_ROSE:
+		errno = 0;
+		return rose_ntop1((rose_address *)addr, buf, buflen);
+
+	default:
+		errno = EAFNOSUPPORT;
+	}
+
+	return NULL;
+}
diff -pruN 5.15.0-1/Makefile 5.19.0-1/Makefile
--- 5.15.0-1/Makefile	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/Makefile	2022-08-02 18:36:33.000000000 +0000
@@ -1,6 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0
 # Top level Makefile for iproute2
 
+-include config.mk
+
 ifeq ("$(origin V)", "command line")
 VERBOSE = $(V)
 endif
@@ -13,7 +15,6 @@ MAKEFLAGS += --no-print-directory
 endif
 
 PREFIX?=/usr
-LIBDIR?=$(PREFIX)/lib
 SBINDIR?=/sbin
 CONFDIR?=/etc/iproute2
 NETNS_RUN_DIR?=/var/run/netns
@@ -40,9 +41,18 @@ DEFINES+=-DCONFDIR=\"$(CONFDIR)\" \
          -DNETNS_RUN_DIR=\"$(NETNS_RUN_DIR)\" \
          -DNETNS_ETC_DIR=\"$(NETNS_ETC_DIR)\"
 
+#options for AX.25
+ADDLIB+=ax25_ntop.o
+
+#options for AX.25
+ADDLIB+=rose_ntop.o
+
 #options for mpls
 ADDLIB+=mpls_ntop.o mpls_pton.o
 
+#options for NETROM
+ADDLIB+=netrom_ntop.o
+
 CC := gcc
 HOSTCC ?= $(CC)
 DEFINES += -D_GNU_SOURCE
@@ -55,12 +65,15 @@ WFLAGS += -Wmissing-declarations -Wold-s
 CFLAGS := $(WFLAGS) $(CCOPTS) -I../include -I../include/uapi $(DEFINES) $(CFLAGS)
 YACCFLAGS = -d -t -v
 
-SUBDIRS=lib ip tc bridge misc netem genl tipc devlink rdma dcb man vdpa
+SUBDIRS=lib ip tc bridge misc netem genl man
+ifeq ($(HAVE_MNL),y)
+SUBDIRS += tipc devlink rdma dcb vdpa
+endif
 
 LIBNETLINK=../lib/libutil.a ../lib/libnetlink.a
 LDLIBS += $(LIBNETLINK)
 
-all: config
+all: config.mk
 	@set -e; \
 	for i in $(SUBDIRS); \
 	do echo; echo $$i; $(MAKE) -C $$i; done
@@ -80,7 +93,7 @@ help:
 	@echo "Make Arguments:"
 	@echo " V=[0|1]             - set build verbosity level"
 
-config:
+config.mk:
 	@if [ ! -f config.mk -o configure -nt config.mk ]; then \
 		sh configure $(KERNEL_INCLUDE); \
 	fi
diff -pruN 5.15.0-1/man/man8/bridge.8 5.19.0-1/man/man8/bridge.8
--- 5.15.0-1/man/man8/bridge.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/bridge.8	2022-08-02 18:36:33.000000000 +0000
@@ -13,7 +13,7 @@ bridge \- show / manipulate bridge addre
 
 .ti -8
 .IR OBJECT " := { "
-.BR link " | " fdb " | " mdb " | " vlan " | " monitor " }"
+.BR link " | " fdb " | " mdb " | " vlan " | " vni " | " monitor " }"
 .sp
 
 .ti -8
@@ -45,11 +45,15 @@ bridge \- show / manipulate bridge addre
 .BR learning_sync " { " on " | " off " } ] [ "
 .BR flood " { " on " | " off " } ] [ "
 .BR hwmode " { " vepa " | " veb " } ] [ "
+.BR bcast_flood " { " on " | " off " } ] [ "
 .BR mcast_flood " { " on " | " off " } ] [ "
+.BR mcast_router
+.IR MULTICAST_ROUTER " ] ["
 .BR mcast_to_unicast " { " on " | " off " } ] [ "
 .BR neigh_suppress " { " on " | " off " } ] [ "
 .BR vlan_tunnel " { " on " | " off " } ] [ "
 .BR isolated " { " on " | " off " } ] [ "
+.BR locked " { " on " | " off " } ] [ "
 .B backup_port
 .IR  DEVICE " ] ["
 .BR nobackup_port " ] [ "
@@ -123,7 +127,7 @@ bridge \- show / manipulate bridge addre
 .IR VID " ] "
 
 .ti -8
-.BR "bridge mdb show " [ "
+.BR "bridge mdb show" " [ "
 .B dev
 .IR DEV " ]"
 
@@ -145,7 +149,9 @@ bridge \- show / manipulate bridge addre
 .B vid
 .IR VID " [ "
 .B state
-.IR STP_STATE " ] "
+.IR STP_STATE " ] [ "
+.B mcast_router
+.IR MULTICAST_ROUTER " ]"
 
 .ti -8
 .BR "bridge vlan" " [ " show " | " tunnelshow " ] [ "
@@ -153,6 +159,63 @@ bridge \- show / manipulate bridge addre
 .IR DEV " ]"
 
 .ti -8
+.BR "bridge vlan global set"
+.B dev
+.I DEV
+.B vid
+.IR VID " [ "
+.B mcast_snooping
+.IR MULTICAST_SNOOPING " ] [ "
+.B mcast_querier
+.IR MULTICAST_QUERIER " ] [ "
+.B mcast_igmp_version
+.IR IGMP_VERSION " ] [ "
+.B mcast_mld_version
+.IR MLD_VERSION " ] [ "
+.B mcast_last_member_count
+.IR LAST_MEMBER_COUNT " ] [ "
+.B mcast_last_member_interval
+.IR LAST_MEMBER_INTERVAL " ] [ "
+.B mcast_startup_query_count
+.IR STARTUP_QUERY_COUNT " ] [ "
+.B mcast_startup_query_interval
+.IR STARTUP_QUERY_INTERVAL " ] [ "
+.B mcast_membership_interval
+.IR MEMBERSHIP_INTERVAL " ] [ "
+.B mcast_querier_interval
+.IR QUERIER_INTERVAL " ] [ "
+.B mcast_query_interval
+.IR QUERY_INTERVAL " ] [ "
+.B mcast_query_response_interval
+.IR QUERY_RESPONSE_INTERVAL " ]"
+
+.ti -8
+.BR "bridge vlan global" " [ " show " ] [ "
+.B dev
+.IR DEV " ] [ "
+.B vid
+.IR VID " ]"
+
+.ti -8
+.BR "bridge vlan" " show " [ "
+.B dev
+.IR DEV " ]"
+
+.ti -8
+.BR "bridge vni" " { " add " | " del " } "
+.B dev
+.I DEV
+.B vni
+.IR VNI " [ { "
+.B group | remote "} "
+.IR IPADDR " ] "
+
+.ti -8
+.BR "bridge vni" " show " [ "
+.B dev
+.IR DEV " ]"
+
+.ti -8
 .BR "bridge monitor" " [ " all " | " neigh " | " link " | " mdb " | " vlan " ]"
 
 .SH OPTIONS
@@ -259,6 +322,10 @@ the output.
 .B vlan
 - VLAN filter list.
 
+.TP
+.B vni
+- VNI filter list.
+
 .SS
 .I COMMAND
 
@@ -422,11 +489,29 @@ switch.
 - bridging happens in hardware.
 
 .TP
+.BR "bcast_flood on " or " bcast_flood off "
+Controls flooding of broadcast traffic on the given port.
+By default this flag is on.
+
+.TP
 .BR "mcast_flood on " or " mcast_flood off "
 Controls whether multicast traffic for which there is no MDB entry will be
 flooded towards this given port. By default this flag is on.
 
 .TP
+.BI mcast_router " MULTICAST_ROUTER "
+This flag is almost the same as the per-VLAN flag, see below, except its
+value can only be set in the range 0-2.  The default is
+.B 1
+where the bridge figures out automatically where an IGMP/MLD querier,
+MRDISC capable device, or PIM router, is located.  Setting this flag to
+.B 2
+is useful in cases where the multicast router does not indicate its
+presence in any meaningful way (e.g. older versions of SMCRoute, or
+mrouted), or when there is a need for forwarding both known and unknown
+IP multicast to a secondary/backup router.
+
+.TP
 .BR "mcast_to_unicast on " or " mcast_to_unicast off "
 Controls whether a given port will replicate packets using unicast
 instead of multicast. By default this flag is off.
@@ -474,6 +559,16 @@ able to communicate with non-isolated po
 flag is off.
 
 .TP
+.BR "locked on " or " locked off "
+Controls whether a port will be locked, meaning that hosts behind the
+port will not be able to communicate through the port unless an FDB
+entry with the units MAC address is in the FDB.
+The common use is that hosts are allowed access through authentication
+with the IEEE 802.1X protocol or based on whitelists or like setups.
+By default this flag is off.
+
+
+.TP
 .BI backup_port " DEVICE"
 If the port loses carrier all traffic will be redirected to the
 configured backup port
@@ -877,6 +972,31 @@ is used during the STP election process.
 STP BPDUs.
 .sp
 
+.TP
+.BI mcast_router " MULTICAST_ROUTER "
+configure this vlan and interface's multicast router mode, note that only modes
+0 - 2 are available for bridge devices.
+A vlan and interface with a multicast router will receive all multicast traffic.
+.I MULTICAST_ROUTER
+may be either
+.sp
+.B 0
+- to disable multicast router.
+.sp
+
+.B 1
+- to let the system detect the presence of routers (default).
+.sp
+
+.B 2
+- to permanently enable multicast traffic forwarding on this vlan and interface.
+.sp
+
+.B 3
+- to temporarily mark this vlan and port as having a multicast router, i.e.
+enable multicast traffic forwarding. This mode is available only for ports.
+.sp
+
 .SS bridge vlan show - list vlan configuration.
 
 This command displays the current VLAN filter table.
@@ -895,6 +1015,150 @@ option, the command displays per-vlan tr
 
 This command displays the current vlan tunnel info mapping.
 
+.SS bridge vlan global set - change vlan filter entry's global options
+
+This command changes vlan filter entry's global options.
+
+.TP
+.BI dev " NAME"
+the interface with which this vlan is associated. Only bridge devices are
+supported for global options.
+
+.TP
+.BI vid " VID"
+the VLAN ID that identifies the vlan.
+
+.TP
+.BI mcast_snooping " MULTICAST_SNOOPING "
+turn multicast snooping for VLAN entry with VLAN ID on
+.RI ( MULTICAST_SNOOPING " > 0) "
+or off
+.RI ( MULTICAST_SNOOPING " == 0). Default is on. "
+
+.TP
+.BI mcast_querier " MULTICAST_QUERIER "
+enable
+.RI ( MULTICAST_QUERIER " > 0) "
+or disable
+.RI ( MULTICAST_QUERIER " == 0) "
+IGMP/MLD querier, ie sending of multicast queries by the bridge. Default is disabled.
+
+.TP
+.BI mcast_igmp_version " IGMP_VERSION "
+set the IGMP version. Default is 2.
+
+.TP
+.BI mcast_mld_version " MLD_VERSION "
+set the MLD version. Default is 1.
+
+.TP
+.BI mcast_last_member_count " LAST_MEMBER_COUNT "
+set multicast last member count, ie the number of queries the bridge
+will send before stopping forwarding a multicast group after a "leave"
+message has been received. Default is 2.
+
+.TP
+.BI mcast_last_member_interval " LAST_MEMBER_INTERVAL "
+interval between queries to find remaining members of a group,
+after a "leave" message is received.
+
+.TP
+.BI mcast_startup_query_count " STARTUP_QUERY_COUNT "
+set the number of queries to send during startup phase. Default is 2.
+
+.TP
+.BI mcast_startup_query_interval " STARTUP_QUERY_INTERVAL "
+interval between queries in the startup phase.
+
+.TP
+.BI mcast_membership_interval " MEMBERSHIP_INTERVAL "
+delay after which the bridge will leave a group,
+if no membership reports for this group are received.
+
+.TP
+.BI mcast_querier_interval " QUERIER_INTERVAL "
+interval between queries sent by other routers. If no queries are seen
+after this delay has passed, the bridge will start to send its own queries
+(as if
+.BI mcast_querier
+was enabled).
+
+.TP
+.BI mcast_query_interval " QUERY_INTERVAL "
+interval between queries sent by the bridge after the end of the
+startup phase.
+
+.TP
+.BI mcast_query_response_interval " QUERY_RESPONSE_INTERVAL "
+set the Max Response Time/Maximum Response Delay for IGMP/MLD
+queries sent by the bridge.
+
+.SS bridge vlan global show - list global vlan options.
+
+This command displays the global VLAN options for each VLAN entry.
+
+.TP
+.BI dev " DEV"
+the interface only whose VLAN global options should be listed. Default is to list
+all bridge interfaces.
+
+.TP
+.BI vid " VID"
+the VLAN ID only whose global options should be listed. Default is to list
+all vlans.
+
+.SH bridge vni - VNI filter list
+
+.B vni
+objects contain known VNI IDs for a dst metadata vxlan link.
+
+.P
+The corresponding commands display vni filter entries, add new entries,
+and delete old ones.
+
+.SS bridge vni add - add a new vni filter entry
+
+This command creates a new vni filter entry.
+
+.TP
+.BI dev " NAME"
+the interface with which this vni is associated.
+
+.TP
+.BI vni " VNI"
+the VNI ID that identifies the vni.
+
+.TP
+.BI remote " IPADDR"
+specifies the unicast destination IP address to use in outgoing packets
+when the destination link layer address is not known in the VXLAN device
+forwarding database. This parameter cannot be specified with the group.
+
+.TP
+.BI group " IPADDR"
+specifies the multicast IP address to join for this VNI
+
+.SS bridge vni del - delete a new vni filter entry
+
+This command removes an existing vni filter entry.
+
+.PP
+The arguments are the same as with
+.BR "bridge vni add".
+
+.SS bridge vni show - list vni filtering configuration.
+
+This command displays the current vni filter table.
+
+.PP
+With the
+.B -statistics
+option, the command displays per-vni traffic statistics.
+
+.TP
+.BI dev " NAME"
+shows vni filtering table associated with the vxlan device
+
 .SH bridge monitor - state monitoring
 
 The
diff -pruN 5.15.0-1/man/man8/dcb-app.8 5.19.0-1/man/man8/dcb-app.8
--- 5.15.0-1/man/man8/dcb-app.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/dcb-app.8	2022-08-02 18:36:33.000000000 +0000
@@ -170,7 +170,7 @@ priorities that should be assigned to ma
 \fIDSCP-MAP\fR uses the array parameter syntax, see
 .BR dcb (8)
 for details. Keys are DSCP points, values are priorities assigned to
-traffic with matching DSCP. DSCP points can be written either direcly as
+traffic with matching DSCP. DSCP points can be written either directly as
 numeric values, or using symbolic names specified in
 .B /etc/iproute2/rt_dsfield
 (however note that that file specifies full 8-bit dsfield values, whereas
diff -pruN 5.15.0-1/man/man8/dcb-dcbx.8 5.19.0-1/man/man8/dcb-dcbx.8
--- 5.15.0-1/man/man8/dcb-dcbx.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/dcb-dcbx.8	2022-08-02 18:36:33.000000000 +0000
@@ -67,7 +67,7 @@ allows setting both and lets the driver
 .B cee
 .TQ
 .B ieee
-The device supports CEE (Converged Enhanced Ethernet) and, respecively, IEEE
+The device supports CEE (Converged Enhanced Ethernet) and, respectively, IEEE
 version of the DCB specification. Typically only one of these will be set, but
 .B dcb dcbx
 does not mandate this.
diff -pruN 5.15.0-1/man/man8/devlink.8 5.19.0-1/man/man8/devlink.8
--- 5.15.0-1/man/man8/devlink.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/devlink.8	2022-08-02 18:36:33.000000000 +0000
@@ -63,6 +63,10 @@ Switches to the specified network namesp
 .BR "\-i", " --iec"
 Print human readable rates in IEC units (e.g. 1Ki = 1024).
 
+.TP
+.BR "\-x", " --hex"
+Print dump numbers in hexadecimal format.
+
 .SS
 .I OBJECT
 
diff -pruN 5.15.0-1/man/man8/devlink-dev.8 5.19.0-1/man/man8/devlink-dev.8
--- 5.15.0-1/man/man8/devlink-dev.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/devlink-dev.8	2022-08-02 18:36:33.000000000 +0000
@@ -199,7 +199,7 @@ pending this action will reload current
 
 .B limit no_reset
 - Specifies limitation on reload action.
-If this argument is omitted limit is unspecificed and the reload action is not
+If this argument is omitted limit is unspecified and the reload action is not
 limited. In such case driver implementation may include reset or downtime as
 needed to perform the actions.
 
diff -pruN 5.15.0-1/man/man8/devlink-rate.8 5.19.0-1/man/man8/devlink-rate.8
--- 5.15.0-1/man/man8/devlink-rate.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/devlink-rate.8	2022-08-02 18:36:33.000000000 +0000
@@ -63,7 +63,7 @@ Command output show rate object identifi
 parent node name. Rate values printed in SI units which are more suitable to
 represent specific value. To print values in IEC units \fB-i\fR switch is
 used. JSON (\fB-j\fR) output always print rate values in bytes per second. Zero
-rate values means "unlimited" rates and ommited in output, as well as parent
+rate values means "unlimited" rates and omitted in output, as well as parent
 node name.
 
 .SS devlink port function rate set - set rate object parameters.
diff -pruN 5.15.0-1/man/man8/devlink-region.8 5.19.0-1/man/man8/devlink-region.8
--- 5.15.0-1/man/man8/devlink-region.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/devlink-region.8	2022-08-02 18:36:33.000000000 +0000
@@ -116,7 +116,7 @@ devlink region dump pci/0000:00:05.0/cr-
 Dump the snapshot taken from cr-space address region with ID 1
 .RE
 .PP
-devlink region read pci/0000:00:05.0/cr-space snapshot 1 address 0x10 legth 16
+devlink region read pci/0000:00:05.0/cr-space snapshot 1 address 0x10 length 16
 .RS 4
 Read from address 0x10, 16 Bytes of snapshot ID 1 taken from cr-space address region
 .RE
diff -pruN 5.15.0-1/man/man8/ifcfg.8 5.19.0-1/man/man8/ifcfg.8
--- 5.15.0-1/man/man8/ifcfg.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/ifcfg.8	1970-01-01 00:00:00.000000000 +0000
@@ -1,48 +0,0 @@
-.TH IFCFG 8 "September 24 2009" "iproute2" "Linux"
-.SH NAME
-ifcfg \- simplistic script which replaces ifconfig IP management
-.SH SYNOPSIS
-.ad l
-.in +8
-.ti -8
-.B ifcfg
-.RI "[ " DEVICE " ] [ " command " ] " ADDRESS " [ " PEER " ] "
-.sp
-
-.SH DESCRIPTION
-This manual page documents briefly the
-.B ifcfg
-command.
-.PP
-This is a simplistic script replacing one option of
-.B ifconfig
-, namely, IP address management. It not only adds
-addresses, but also carries out Duplicate Address Detection RFC-DHCP,
-sends unsolicited ARP to update the caches of other hosts sharing
-the interface, adds some control routes and restarts Router Discovery
-when it is necessary.
-
-.SH IFCONFIG - COMMAND SYNTAX
-
-.SS
-.TP
-.B DEVICE
-- it may have alias, suffix, separated by colon.
-
-.TP
-.B command
-- add, delete or stop.
-
-.TP
-.B ADDRESS
-- optionally followed by prefix length.
-
-.TP
-.B peer
-- optional peer address for pointpoint interfaces.
-
-.SH NOTES
-This script is not suitable for use with IPv6.
-
-.SH SEE ALSO
-.RB "IP Command reference " ip-cref.ps
diff -pruN 5.15.0-1/man/man8/ip.8 5.19.0-1/man/man8/ip.8
--- 5.15.0-1/man/man8/ip.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/ip.8	2022-08-02 18:36:33.000000000 +0000
@@ -22,7 +22,7 @@ ip \- show / manipulate routing, network
 .BR link " | " address " | " addrlabel " | " route " | " rule " | " neigh " | "\
  ntable " | " tunnel " | " tuntap " | " maddress " | "  mroute " | " mrule " | "\
  monitor " | " xfrm " | " netns " | "  l2tp " | "  tcp_metrics " | " token " | "\
- macsec " | " vrf " | " mptcp " | " ioam " }"
+ macsec " | " vrf " | " mptcp " | " ioam " | " stats " }"
 .sp
 
 .ti -8
@@ -303,6 +303,10 @@ readability.
 - rule in routing policy database.
 
 .TP
+.B stats
+- manage and show interface statistics.
+
+.TP
 .B tcp_metrics/tcpmetrics
 - manage TCP Metrics
 
@@ -419,6 +423,7 @@ was written by Alexey N. Kuznetsov and a
 .BR ip-ntable (8),
 .BR ip-route (8),
 .BR ip-rule (8),
+.BR ip-stats (8)
 .BR ip-tcp_metrics (8),
 .BR ip-token (8),
 .BR ip-tunnel (8),
diff -pruN 5.15.0-1/man/man8/ip-address.8.in 5.19.0-1/man/man8/ip-address.8.in
--- 5.15.0-1/man/man8/ip-address.8.in	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/ip-address.8.in	2022-08-02 18:36:33.000000000 +0000
@@ -49,7 +49,8 @@ ip-address \- protocol address managemen
 .IR TYPE " ] [ "
 .B vrf
 .IR NAME " ] [ "
-.BR up " ] ]"
+.BR up " ] ["
+.BR nomaster " ] ]"
 
 .ti -8
 .BR "ip address" " { " showdump " | " restore " }"
@@ -92,7 +93,7 @@ ip-address \- protocol address managemen
 
 .ti -8
 .IR CONFFLAG " := "
-.RB "[ " home " | " mngtmpaddr " | " nodad " | " optimstic " | " noprefixroute " | " autojoin " ]"
+.RB "[ " home " | " mngtmpaddr " | " nodad " | " optimistic " | " noprefixroute " | " autojoin " ]"
 
 .ti -8
 .IR LIFETIME " := [ "
@@ -194,9 +195,6 @@ is derived by setting/resetting the host
 .TP
 .BI label " LABEL"
 Each address may be tagged with a label string.
-In order to preserve compatibility with Linux-2.0 net aliases,
-this string must coincide with the name of the device or must be prefixed
-with the device name followed by colon.
 The maximum allowed total length of label is 15 characters.
 
 .TP
@@ -341,6 +339,10 @@ output.
 only list running interfaces.
 
 .TP
+.B nomaster
+only list interfaces with no master.
+
+.TP
 .BR dynamic " and " permanent
 (IPv6 only) only list addresses installed due to stateless
 address configuration or only list permanent (not dynamic)
diff -pruN 5.15.0-1/man/man8/ip-link.8.in 5.19.0-1/man/man8/ip-link.8.in
--- 5.15.0-1/man/man8/ip-link.8.in	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/ip-link.8.in	2022-08-02 18:36:33.000000000 +0000
@@ -36,11 +36,14 @@ ip-link \- network device configuration
 .RB "[ " numrxqueues
 .IR QUEUE_COUNT " ]"
 .br
-.BR "[ " gso_max_size
+.RB "[ " gso_max_size
 .IR BYTES " ]"
 .RB "[ " gso_max_segs
 .IR SEGMENTS " ]"
 .br
+.RB "[ " gro_max_size
+.IR BYTES " ]"
+.br
 .BI type " TYPE"
 .RI "[ " ARGS " ]"
 
@@ -83,6 +86,13 @@ ip-link \- network device configuration
 .RB "[ " txqueuelen
 .IR PACKETS " ]"
 .br
+.RB "[ " max_gso_size
+.IR BYTES " ]"
+.RB "[ " max_gso_segs
+.IR SEGMENTS " ]"
+.RB "[ " max_gro_size
+.IR BYTES " ]"
+.br
 .RB "[ " name
 .IR NEWNAME " ]"
 .br
@@ -179,7 +189,8 @@ ip-link \- network device configuration
 .B type
 .IR ETYPE " ] ["
 .B vrf
-.IR NAME " ]"
+.IR NAME " ] ["
+.BR nomaster " ]"
 
 .ti -8
 .B ip link xstats
@@ -197,40 +208,43 @@ ip-link \- network device configuration
 
 .ti -8
 .IR TYPE " := [ "
-.BR bridge " | "
+.BR amt " | "
+.BR bareudp " |"
 .BR bond " | "
+.BR bridge " | "
 .BR can " | "
 .BR dummy " | "
-.BR hsr " | "
-.BR ifb " | "
-.BR ipoib " |"
-.BR macvlan  " | "
-.BR macvtap  " | "
-.BR vcan " | "
-.BR vxcan " | "
-.BR veth " | "
-.BR vlan " | "
-.BR vxlan " |"
-.BR ip6tnl " |"
-.BR ipip " |"
-.BR sit " |"
+.BR erspan " |"
+.BR geneve " |"
 .BR gre " |"
 .BR gretap " |"
-.BR erspan " |"
+.BR gtp " |"
+.BR hsr " | "
+.BR ifb " | "
+.BR ip6erspan " |"
 .BR ip6gre " |"
 .BR ip6gretap " |"
-.BR ip6erspan " |"
-.BR vti " |"
-.BR nlmon " |"
+.BR ip6tnl " |"
+.BR ipip " |"
+.BR ipoib " |"
 .BR ipvlan " |"
 .BR ipvtap " |"
 .BR lowpan " |"
-.BR geneve " |"
-.BR bareudp " |"
-.BR vrf " |"
 .BR macsec " |"
+.BR macvlan  " | "
+.BR macvtap  " | "
 .BR netdevsim " |"
+.BR nlmon " |"
 .BR rmnet " |"
+.BR sit " |"
+.BR vcan " | "
+.BR veth " | "
+.BR virt_wifi " |"
+.BR vlan " | "
+.BR vrf " |"
+.BR vti " |"
+.BR vxcan " | "
+.BR vxlan " |"
 .BR xfrm " ]"
 
 .ti -8
@@ -276,44 +290,52 @@ specifies the type of the new device.
 Link types:
 
 .in +8
-.B bridge
-- Ethernet Bridge device
+.BR amt
+- Automatic Multicast Tunneling (AMT)
+.sp
+.BR bareudp
+- Bare UDP L3 encapsulation support
 .sp
 .B bond
 - Bonding device
+.B bridge
+- Ethernet Bridge device
+.sp
+.B can
+- Controller Area Network
 .sp
 .B dummy
 - Dummy network interface
 .sp
-.B hsr
-- High-availability Seamless Redundancy device
+.BR erspan
+- Encapsulated Remote SPAN over GRE and IPv4
 .sp
-.B ifb
-- Intermediate Functional Block device
+.B geneve
+- GEneric NEtwork Virtualization Encapsulation
 .sp
-.B ipoib
-- IP over Infiniband device
+.B gre
+- Virtual tunnel interface GRE over IPv4
 .sp
-.B macvlan
-- Virtual interface base on link layer address (MAC)
+.BR gretap
+- Virtual L2 tunnel interface GRE over IPv4
 .sp
-.B macvtap
-- Virtual interface based on link layer address (MAC) and TAP.
+.BR gtp
+- GPRS Tunneling Protocol
 .sp
-.B vcan
-- Virtual Controller Area Network interface
+.B hsr
+- High-availability Seamless Redundancy device
 .sp
-.B vxcan
-- Virtual Controller Area Network tunnel interface
+.B ifb
+- Intermediate Functional Block device
 .sp
-.B veth
-- Virtual ethernet interface
+.BR ip6erspan
+- Encapsulated Remote SPAN over GRE and IPv6
 .sp
-.BR vlan
-- 802.1q tagged virtual LAN interface
+.BR ip6gre
+- Virtual tunnel interface GRE over IPv6
 .sp
-.BR vxlan
-- Virtual eXtended LAN
+.BR ip6gretap
+- Virtual L2 tunnel interface GRE over IPv6
 .sp
 .BR ip6tnl
 - Virtual tunnel interface IPv4|IPv6 over IPv6
@@ -321,62 +343,66 @@ Link types:
 .BR ipip
 - Virtual tunnel interface IPv4 over IPv4
 .sp
-.BR sit
-- Virtual tunnel interface IPv6 over IPv4
+.B ipoib
+- IP over Infiniband device
 .sp
-.BR gre
-- Virtual tunnel interface GRE over IPv4
+.BR ipvlan
+- Interface for L3 (IPv6/IPv4) based VLANs
 .sp
-.BR gretap
-- Virtual L2 tunnel interface GRE over IPv4
+.BR ipvtap
+- Interface for L3 (IPv6/IPv4) based VLANs and TAP
 .sp
-.BR erspan
-- Encapsulated Remote SPAN over GRE and IPv4
+.BR lowpan
+- Interface for 6LoWPAN (IPv6) over IEEE 802.15.4 / Bluetooth
 .sp
-.BR ip6gre
-- Virtual tunnel interface GRE over IPv6
+.BR macsec
+- Interface for IEEE 802.1AE MAC Security (MACsec)
 .sp
-.BR ip6gretap
-- Virtual L2 tunnel interface GRE over IPv6
+.B macvlan
+- Virtual interface base on link layer address (MAC)
 .sp
-.BR ip6erspan
-- Encapsulated Remote SPAN over GRE and IPv6
+.B macvtap
+- Virtual interface based on link layer address (MAC) and TAP.
 .sp
-.BR vti
-- Virtual tunnel interface
+.BR netdevsim
+- Interface for netdev API tests
 .sp
 .BR nlmon
 - Netlink monitoring device
 .sp
-.BR ipvlan
-- Interface for L3 (IPv6/IPv4) based VLANs
+.BR rmnet
+- Qualcomm rmnet device
 .sp
-.BR ipvtap
-- Interface for L3 (IPv6/IPv4) based VLANs and TAP
+.BR sit
+- Virtual tunnel interface IPv6 over IPv4
 .sp
-.BR lowpan
-- Interface for 6LoWPAN (IPv6) over IEEE 802.15.4 / Bluetooth
+.B vcan
+- Virtual Controller Area Network interface
 .sp
-.BR geneve
-- GEneric NEtwork Virtualization Encapsulation
+.B veth
+- Virtual ethernet interface
 .sp
-.BR bareudp
-- Bare UDP L3 encapsulation support
+.BR virt_wifi
+- rtnetlink wifi simulation device
 .sp
-.BR macsec
-- Interface for IEEE 802.1AE MAC Security (MACsec)
+.BR vlan
+- 802.1q tagged virtual LAN interface
 .sp
 .BR vrf
 - Interface for L3 VRF domains
 .sp
-.BR netdevsim
-- Interface for netdev API tests
+.BR vti
+- Virtual tunnel interface
 .sp
-.BR rmnet
-- Qualcomm rmnet device
+.B vxcan
+- Virtual Controller Area Network tunnel interface
+.sp
+.BR vxlan
+- Virtual eXtended LAN
 .sp
 .BR xfrm
 - Virtual xfrm interface
+.sp
 .in -8
 
 .TP
@@ -398,6 +424,11 @@ specifies the recommended maximum number
 segments the new device should accept.
 
 .TP
+.BI gro_max_size " BYTES "
+specifies the maximum size of a packet built by GRO stack
+on this device.
+
+.TP
 .BI index " IDX "
 specifies the desired index of the new virtual device. The link
 creation fails, if the index is busy.
@@ -457,7 +488,7 @@ then VLAN header will be not inserted im
 passing to the physical device (if this device does not support VLAN
 offloading), the similar on the RX direction - by default the packet
 will be untagged before being received by VLAN device. Reordering
-allows to accelerate tagging on egress and to hide VLAN header on
+allows one to accelerate tagging on egress and to hide VLAN header on
 ingress so the packet looks like regular Ethernet packet, at the same
 time it might be confusing for packet capture as the VLAN header does
 not exist within the packet.
@@ -476,11 +507,11 @@ where <phy_dev> is the physical device t
 
 .BR gvrp " { " on " | " off " } "
 - specifies whether this VLAN should be registered using GARP VLAN
-  Registration Protocol.
+Registration Protocol.
 
 .BR mvrp " { " on " | " off " } "
 - specifies whether this VLAN should be registered using Multiple VLAN
-  Registration Protocol.
+Registration Protocol.
 
 .BR loose_binding " { " on " | " off " } "
 - specifies whether the VLAN device state is bound to the physical device state.
@@ -570,6 +601,8 @@ the following additional arguments are s
 .B gbp
 ] [
 .B gpe
+] [
+.RB [ no ] vnifilter
 ]
 
 .in +8
@@ -682,11 +715,18 @@ are entered into the VXLAN device forwar
 or the internal FDB should be used.
 
 .sp
+.RB [ no ] vnifilter
+- specifies whether the vxlan device is capable of vni filtering. Only works with a vxlan
+device with external flag set. once enabled, bridge vni command is used to manage the
+vni filtering table on the device. The device can only receive packets with vni's configured
+in the vni filtering table.
+
+.sp
 .B gbp
 - enables the Group Policy extension (VXLAN-GBP).
 
 .in +4
-Allows to transport group policy context across VXLAN network peers.
+Allows one to transport group policy context across VXLAN network peers.
 If enabled, includes the mark of a packet in the VXLAN header for outgoing
 packets and fills the packet mark based on the information found in the
 VXLAN header for incoming packets.
@@ -1235,6 +1275,8 @@ the following additional arguments are s
 .RB [ no ] udp6zerocsumtx
 ] [
 .RB [ no ] udp6zerocsumrx
+] [
+.B innerprotoinherit
 ]
 
 .in +8
@@ -1298,6 +1340,10 @@ options.
 .RB [ no ] udp6zerocsumrx
 - allow incoming UDP packets over IPv6 with zero checksum field.
 
+.sp
+.B innerprotoinherit
+- use IPv4/IPv6 as inner protocol instead of Ethernet.
+
 .in -8
 
 .TP
@@ -1344,6 +1390,48 @@ is "ipv4", this allows the tunnel to als
 by default.
 
 .TP
+AMT Type Support
+For a link of type
+.I AMT
+the following additional arguments are supported:
+
+.BI "ip link add " DEVICE
+.BI type " AMT " discovery " IPADDR " mode " { " gateway " | " relay " } "
+.BI local " IPADDR " dev " PHYS_DEV " [
+.BI relay_port " PORT " ]
+[
+.BI gateway_port " PORT " ]
+[
+.BI max_tunnels " NUMBER "
+]
+
+.in +8
+.sp
+.BI discovery " IPADDR"
+- specifies the unicast discovery IP address to use to find remote IP address.
+
+.BR mode " { " gateway " | " relay " } "
+- specifies the role of AMT, Gateway or Relay
+
+.BI local " IPADDR "
+- specifies the source IP address to use in outgoing packets.
+
+.BI dev " PHYS_DEV "
+- specifies the underlying physical interface from which transform traffic
+is sent and received.
+
+.BI relay_port " PORT "
+- specifies the UDP Relay port to communicate to the Relay.
+
+.BI gateway_port " PORT "
+- specifies the UDP Gateway port to communicate to the Gateway.
+
+.BI max_tunnels " NUMBER "
+- specifies the maximum number of tunnels.
+
+.in -8
+
+.TP
 MACVLAN and MACVTAP Type Support
 For a link of type
 .I MACVLAN
@@ -1492,6 +1580,8 @@ the following additional arguments are s
 ] [
 .BI mcast_snooping " MULTICAST_SNOOPING "
 ] [
+.BI mcast_vlan_snooping " MULTICAST_VLAN_SNOOPING "
+] [
 .BI mcast_router " MULTICAST_ROUTER "
 ] [
 .BI mcast_query_use_ifaddr " MCAST_QUERY_USE_IFADDR "
@@ -1614,6 +1704,12 @@ per-VLAN per-port stats accounting. Can
 or off
 .RI ( MULTICAST_SNOOPING " == 0). "
 
+.BI mcast_vlan_snooping " MULTICAST_VLAN_SNOOPING "
+- turn multicast VLAN snooping on
+.RI ( MULTICAST_VLAN_SNOOPING " > 0) "
+or off
+.RI ( MULTICAST_VLAN_SNOOPING " == 0). "
+
 .BI mcast_router " MULTICAST_ROUTER "
 - set bridge's multicast router if IGMP snooping is enabled.
 .I MULTICAST_ROUTER
@@ -1871,6 +1967,29 @@ policies. Policies must be configured wi
 
 .in -8
 
+.TP
+GTP Type Support
+For a link of type
+.I GTP
+the following additional arguments are supported:
+
+.BI "ip link add " DEVICE " type gtp role " ROLE " hsize " HSIZE
+
+.in +8
+.sp
+.BI role " ROLE "
+- specifies the role of the GTP device, either sgsn or ggsn
+
+.sp
+.BI hsize " HSIZE "
+- specifies size of the hashtable which stores PDP contexts
+
+.sp
+.BI restart_count " RESTART_COUNT "
+- GTP instance restart counter
+
+.in -8
+
 .SS ip link delete - delete virtual link
 
 .TP
@@ -2038,7 +2157,7 @@ flag in the output of the
 
 To change network namespace for wireless devices the
 .B iw
-tool can be used. But it allows to change network namespace only for
+tool can be used. But it allows one to change network namespace only for
 physical devices and by process
 .IR PID .
 
@@ -2134,9 +2253,9 @@ parameter must be specified.
 .sp
 .BI query_rss " on|off"
 - toggle the ability of querying the RSS configuration of a specific
-  VF. VF RSS information like RSS hash key may be considered sensitive
-  on some devices where this information is shared between VF and PF
-  and thus its querying may be prohibited by default.
+VF. VF RSS information like RSS hash key may be considered sensitive
+on some devices where this information is shared between VF and PF
+and thus its querying may be prohibited by default.
 .sp
 .BI state " auto|enable|disable"
 - set the virtual link state as seen by the specified VF. Setting to
@@ -2309,6 +2428,8 @@ the following additional arguments are s
 ] [
 .BR mcast_fast_leave " { " on " | " off "}"
 ] [
+.BR bcast_flood " { " on " | " off " }"
+] [
 .BR mcast_flood " { " on " | " off " }"
 ] [
 .BR mcast_to_unicast " { " on " | " off " }"
@@ -2321,6 +2442,7 @@ the following additional arguments are s
 ] [
 .BR isolated " { " on " | " off " }"
 ] [
+.BR locked " { " on " | " off " }"
 .BR backup_port " DEVICE"
 ] [
 .BR nobackup_port " ]"
@@ -2395,13 +2517,17 @@ queries.
 .B fastleave
 option above.
 
+.BR bcast_flood " { " on " | " off " }"
+- controls flooding of broadcast traffic on the given port. By default
+this flag is on.
+
 .BR mcast_flood " { " on " | " off " }"
 - controls whether a given port will flood multicast traffic for which
-  there is no MDB entry.
+there is no MDB entry. By default this flag is on.
 
 .BR mcast_to_unicast " { " on " | " off " }"
 - controls whether a given port will replicate packets using unicast
-  instead of multicast. By default this flag is off.
+instead of multicast. By default this flag is off.
 
 .BI group_fwd_mask " MASK "
 - set the group forward mask. This is the bitmask that is applied to
@@ -2418,6 +2544,11 @@ is enabled on the port. By default this
 - controls whether vlan to tunnel mapping is enabled on the port. By
 default this flag is off.
 
+.BR locked " { " on " | " off " }"
+- sets or unsets a port in locked mode, so that when enabled, hosts
+behind the port cannot communicate through the port unless a FDB entry
+representing the host is in the FDB. By default this flag is off.
+
 .BI backup_port " DEVICE"
 - if the port loses carrier all traffic will be redirected to the
 configured backup port
@@ -2528,6 +2659,10 @@ interface list by comparing it with the
 didn't filter already. Therefore any string is accepted, but may lead to empty
 output.
 
+.TP
+.B nomaster
+only show devices with no master
+
 .SS  ip link xstats - display extended statistics
 
 .TP
diff -pruN 5.15.0-1/man/man8/ip-macsec.8 5.19.0-1/man/man8/ip-macsec.8
--- 5.15.0-1/man/man8/ip-macsec.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/ip-macsec.8	2022-08-02 18:36:33.000000000 +0000
@@ -10,7 +10,7 @@ ip-macsec \- MACsec device configuration
 |
 .BI sci " <u64>"
 ] [
-.BR cipher " { " default " | " gcm-aes-128 " | "gcm-aes-256" } ] ["
+.BR cipher " { " default " | " gcm-aes-128 " | " gcm-aes-256 " } ] ["
 .BI icvlen " ICVLEN"
 ] [
 .BR encrypt " { " on " | " off " } ] ["
diff -pruN 5.15.0-1/man/man8/ip-monitor.8 5.19.0-1/man/man8/ip-monitor.8
--- 5.15.0-1/man/man8/ip-monitor.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/ip-monitor.8	2022-08-02 18:36:33.000000000 +0000
@@ -55,7 +55,7 @@ command is the first in the command line
 is the list of object types that we want to monitor.
 It may contain
 .BR link ", " address ", " route ", " mroute ", " prefix ", "
-.BR neigh ", " netconf ", "  rule ", " nsid " and " nexthop "."
+.BR neigh ", " netconf ", "  rule ", " stats ", " nsid " and " nexthop "."
 If no
 .B file
 argument is given,
diff -pruN 5.15.0-1/man/man8/ip-mptcp.8 5.19.0-1/man/man8/ip-mptcp.8
--- 5.15.0-1/man/man8/ip-mptcp.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/ip-mptcp.8	2022-08-02 18:36:33.000000000 +0000
@@ -31,8 +31,21 @@ ip-mptcp \- MPTCP path manager configura
 .RB "] "
 
 .ti -8
-.BR "ip mptcp endpoint del id "
+.BR "ip mptcp endpoint delete id "
 .I ID
+.RB "[ "
+.I IFADDR
+.RB "] "
+
+.ti -8
+.BR "ip mptcp endpoint change "
+.RB "[ " id
+.I ID
+.RB "] [ "
+.IR IFADDR
+.RB "] [ " port
+.IR PORT " ]"
+.RB "CHANGE-OPT"
 
 .ti -8
 .BR "ip mptcp endpoint show "
@@ -53,6 +66,19 @@ ip-mptcp \- MPTCP path manager configura
 .B subflow
 .RB "|"
 .B backup
+.RB "|"
+.B fullmesh
+.RB  "]"
+
+.ti -8
+.IR CHANGE-OPT " := ["
+.B backup
+.RB "|"
+.B nobackup
+.RB "|"
+.B fullmesh
+.RB "|"
+.B nofullmesh
 .RB  "]"
 
 .ti -8
@@ -92,33 +118,63 @@ ip mptcp endpoint flush	flush all existi
 .TE
 
 .TP
+.IR IFADDR
+An IPv4 or IPv6 address. When used with the
+.B delete id
+operation, an
+.B IFADDR
+is only included when the
+.B ID
+is 0.
+
+.TP
 .IR PORT
 When a port number is specified, incoming MPTCP subflows for already
 established MPTCP sockets will be accepted on the specified port, regardless
 the original listener port accepting the first MPTCP subflow and/or
 this peer being actually on the client side.
 
+.TP
 .IR ID
 is a unique numeric identifier for the given endpoint
 
 .TP
 .BR signal
-the endpoint will be announced/signalled to each peer via an ADD_ADDR MPTCP
-sub-option
+The endpoint will be announced/signaled to each peer via an MPTCP ADD_ADDR
+sub-option. Upon reception of an ADD_ADDR sub-option, the peer can try to
+create additional subflows, see
+.BR ADD_ADDR_ACCEPTED_NR.
 
 .TP
 .BR subflow
-if additional subflow creation is allowed by MPTCP limits, the endpoint will
-be used as the source address to create an additional subflow after that
-the MPTCP connection is established.
+If additional subflow creation is allowed by the MPTCP limits, the MPTCP
+path manager will try to create an additional subflow using this endpoint
+as the source address after the MPTCP connection is established.
 
 .TP
 .BR backup
-the endpoint will be announced as a backup address, if this is a
-.BR signal
-endpoint, or the subflow will be created as a backup one if this is a
+If this is a
+.BR subflow
+endpoint, the subflows created using this endpoint will have the backup
+flag set during the connection process. This flag instructs the peer to
+only send data on a given subflow when all non-backup subflows are
+unavailable. This does not affect outgoing data, where subflow priority
+is determined by the backup/non-backup flag received from the peer
+
+.TP
+.BR fullmesh
+If this is a
+.BR subflow
+endpoint and additional subflow creation is allowed by the MPTCP limits,
+the MPTCP path manager will try to create an additional subflow for each
+known peer address, using this endpoint as the source address. This will
+occur after the MPTCP connection is established. If the peer did not
+announce any additional addresses using the MPTCP ADD_ADDR sub-option,
+this will behave the same as a plain
 .BR subflow
-endpoint
+endpoint. When the peer does announce addresses, each received ADD_ADDR
+sub-option will trigger creation of an additional subflow to generate a
+full mesh topology.
 
 .sp
 .PP
@@ -136,17 +192,29 @@ ip mptcp limits set	change the MPTCP sub
 .IR SUBFLOW_NR
 specifies the maximum number of additional subflows allowed for each MPTCP
 connection. Additional subflows can be created due to: incoming accepted
-ADD_ADDR option, local
+ADD_ADDR sub-option, local
 .BR subflow
 endpoints, additional subflows started by the peer.
 
 .TP
 .IR ADD_ADDR_ACCEPTED_NR
-specifies the maximum number of ADD_ADDR suboptions accepted for each MPTCP
-connection. The MPTCP path manager will try to create a new subflow for
-each accepted ADD_ADDR option, respecting the
+specifies the maximum number of incoming ADD_ADDR sub-options accepted for
+each MPTCP connection. After receiving the specified number of ADD_ADDR
+sub-options, any other incoming one will be ignored for the MPTCP connection
+lifetime. When an ADD_ADDR sub-option is accepted and there are no local
+.IR fullmesh
+endpoints, the MPTCP path manager will try to create a new subflow using the
+address in the ADD_ADDR sub-option as the destination address and a source
+address determined using local routing resolution
+When
+.IR fullmesh
+endpoints are available, the MPTCP path manager will try to create new subflows
+using each
+.IR fullmesh
+endpoint as a source address and the peer's ADD_ADDR address as the destination.
+In both cases the
 .IR SUBFLOW_NR
-limit.
+limit is enforced.
 
 .sp
 .PP
diff -pruN 5.15.0-1/man/man8/ip-neighbour.8 5.19.0-1/man/man8/ip-neighbour.8
--- 5.15.0-1/man/man8/ip-neighbour.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/ip-neighbour.8	2022-08-02 18:36:33.000000000 +0000
@@ -25,6 +25,8 @@ ip-neighbour \- neighbour/arp tables man
 .B  dev
 .IR DEV " ] [ "
 .BR router " ] [ "
+.BR use " ] [ "
+.BR managed " ] [ "
 .BR extern_learn " ]"
 
 .ti -8
@@ -35,7 +37,8 @@ ip-neighbour \- neighbour/arp tables man
 .B  nud
 .IR STATE " ] [ "
 .B  vrf
-.IR NAME " ] "
+.IR NAME " ] ["
+.BR nomaster " ]"
 
 .ti -8
 .B ip neigh get
@@ -92,6 +95,21 @@ indicates whether we are proxying for th
 indicates whether neighbour is a router
 
 .TP
+.BI use
+this neigh entry is in "use". This option can be used to indicate to
+the kernel that a controller is using this dynamic entry. If the entry
+does not exist, the kernel will resolve it. If it exists, an attempt
+to refresh the neighbor entry will be triggered.
+
+.TP
+.BI managed
+this neigh entry is "managed". This option can be used to indicate to
+the kernel that a controller is using this dynamic entry. In contrast
+to "use", if the entry does not exist, the kernel will resolve it and
+periodically attempt to auto-refresh the neighbor entry such that it
+remains in resolved state when possible.
+
+.TP
 .BI extern_learn
 this neigh entry was learned externally. This option can be used to
 indicate to the kernel that this is a controller learnt dynamic entry.
@@ -192,6 +210,10 @@ only list the neighbours attached to thi
 only list the neighbours for given VRF.
 
 .TP
+.BI nomaster
+only list neighbours attached to an interface with no master.
+
+.TP
 .BI proxy
 list neighbour proxies.
 
diff -pruN 5.15.0-1/man/man8/ip-route.8.in 5.19.0-1/man/man8/ip-route.8.in
--- 5.15.0-1/man/man8/ip-route.8.in	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/ip-route.8.in	2022-08-02 18:36:33.000000000 +0000
@@ -245,14 +245,20 @@ throw " | " unreachable " | " prohibit "
 
 .ti -8
 .IR ENCAP_IOAM6 " := "
-.B ioam6
-.BR trace
-.BR prealloc
-.BR type
+.BR ioam6 " ["
+.B freq
+.IR K "/" N " ] "
+.BR mode " [ "
+.BR inline " | " encap " | " auto " ] ["
+.B tundst
+.IR ADDRESS " ] "
+.B trace
+.B prealloc
+.B type
 .IR IOAM6_TRACE_TYPE
-.BR ns
+.B ns
 .IR IOAM6_NAMESPACE
-.BR size
+.B size
 .IR IOAM6_TRACE_SIZE
 
 .ti -8
@@ -915,14 +921,38 @@ address is set as described in \fBip-sr\
 
 .B ioam6
 .in +2
+.B freq K/N
+- Inject IOAM in K packets every N packets (default is 1/1).
+
+.B mode inline
+- Directly insert IOAM after IPv6 header (default mode).
+.sp
+
+.B mode encap
+- Encapsulate packet in an outer IPv6 header with IOAM.
+.sp
+
+.B mode auto
+- Automatically use inline mode for local packets and encap mode for in-transit
+packets.
+.sp
+
+.B tundst
+.I ADDRESS
+- IPv6 address of the tunnel destination (outer header), not used with inline
+mode.
+
+.B type
 .I IOAM6_TRACE_TYPE
 - List of IOAM data required in the trace, represented by a bitfield (24 bits).
 .sp
 
+.B ns
 .I IOAM6_NAMESPACE
 - Numerical value to represent an IOAM namespace. See \fBip-ioam\fR(8).
 .sp
 
+.B size
 .I IOAM6_TRACE_SIZE
 - Size, in octets, of the pre-allocated trace data block.
 .in -4
@@ -1249,9 +1279,9 @@ ip -6 route add 2001:db8:1::/64 encap se
 Adds an IPv6 route with SRv6 decapsulation and forward with lookup in VRF table.
 .RE
 .PP
-ip -6 route add 2001:db8:1::/64 encap ioam6 trace prealloc type 0x800000 ns 1 size 12 dev eth0
+ip -6 route add 2001:db8:1::/64 encap ioam6 freq 2/5 mode encap tundst 2001:db8:42::1 trace prealloc type 0x800000 ns 1 size 12 dev eth0
 .RS 4
-Adds an IPv6 route with an IOAM Pre-allocated Trace encapsulation that only includes the hop limit and the node id, configured for the IOAM namespace 1 and a pre-allocated data block of 12 octets.
+Adds an IPv6 route with an IOAM Pre-allocated Trace encapsulation (ip6ip6) that only includes the hop limit and the node id, configured for the IOAM namespace 1 and a pre-allocated data block of 12 octets (will be injected in 2 packets every 5 packets).
 .RE
 .PP
 ip route add 10.1.1.0/30 nhid 10
diff -pruN 5.15.0-1/man/man8/ip-stats.8 5.19.0-1/man/man8/ip-stats.8
--- 5.15.0-1/man/man8/ip-stats.8	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/man/man8/ip-stats.8	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,208 @@
+.TH IP\-STATS 8 "16 Mar 2022" "iproute2" "Linux"
+.SH NAME
+ip-stats \- manage and show interface statistics
+.SH SYNOPSIS
+.sp
+.ad l
+.in +8
+.ti -8
+.B ip
+.B stats
+.RI  " { " COMMAND " | "
+.BR help " }"
+.sp
+
+.ti -8
+.BR "ip stats show"
+.RB "[ " dev
+.IR DEV " ] "
+.RB "[ " group
+.IR GROUP " [ "
+.BI subgroup " SUBGROUP"
+.RB " [ " suite
+.IR " SUITE" " ] ... ] ... ] ..."
+
+.ti -8
+.BR "ip stats set"
+.BI dev " DEV"
+.BR l3_stats " { "
+.BR on " | " off " }"
+
+.SH DESCRIPTION
+
+.TP
+.B ip stats set
+is used for toggling whether a certain HW statistics suite is collected on
+a given netdevice. The following statistics suites are supported:
+
+.in 21
+
+.ti 14
+.B l3_stats
+L3 stats reflect traffic that takes place in a HW device on an object that
+corresponds to the given software netdevice.
+
+.TP
+.B ip stats show
+is used for showing stats on a given netdevice, or dumping statistics
+across all netdevices. By default, all stats are requested. It is possible
+to filter which stats are requested by using the
+.B group
+and
+.B subgroup
+keywords.
+
+It is possible to specify several groups, or several subgroups for one
+group. When no subgroups are given for a group, all the subgroups are
+requested.
+
+The following groups are recognized:
+.in 21
+
+.ti 14
+.B group link
+- Link statistics. The same suite that "ip -s link show" shows.
+
+.ti 14
+.B group offload
+- A group that contains a number of HW-oriented statistics. See below for
+individual subgroups within this group.
+
+.ti 14
+.B group xstats
+- Extended statistics. A subgroup identifies the type of netdevice to show the
+statistics for.
+
+.ti 14
+.B group xstats_slave
+- Extended statistics for the slave of a netdevice of a given type. A subgroup
+identifies the type of master netdevice.
+
+.ti 14
+.B group afstats
+- A group for address-family specific netdevice statistics.
+
+.TQ
+.BR "group offload " subgroups:
+.in 21
+
+.ti 14
+.B subgroup cpu_hit
+- The
+.B cpu_hit
+statistics suite is useful on hardware netdevices. The
+.B link
+statistics on these devices reflect both the hardware- and
+software-datapath traffic. The
+.B cpu_hit
+statistics then only reflect software-datapath traffic.
+
+.ti 14
+.B subgroup hw_stats_info
+- This suite does not include traffic statistics, but rather communicates
+the state of other statistics. Through this subgroup, it is possible to
+discover whether a given statistic was enabled, and when it was, whether
+any device driver actually configured its device to collect these
+statistics. For example,
+.B l3_stats
+was enabled in the following case, but no driver has installed it:
+
+# ip stats show dev swp1 group offload subgroup hw_stats_info
+.br
+56: swp1: group offload subgroup hw_stats_info
+.br
+    l3_stats on used off
+
+After an L3 address is added to the netdevice, the counter will be
+installed:
+
+# ip addr add dev swp1 192.0.2.1/28
+.br
+# ip stats show dev swp1 group offload subgroup hw_stats_info
+.br
+56: swp1: group offload subgroup hw_stats_info
+.br
+    l3_stats on used on
+
+.ti 14
+.B subgroup l3_stats
+- These statistics reflect L3 traffic that takes place in HW on an object
+that corresponds to the netdevice. Note that this suite is disabled by
+default and needs to be first enabled through
+.B ip stats set\fR.
+
+For example:
+
+# ip stats show dev swp2.200 group offload subgroup l3_stats
+.br
+112: swp2.200: group offload subgroup l3_stats on used on
+.br
+    RX:  bytes packets errors dropped   mcast
+.br
+          8900      72      2       0       3
+.br
+    TX:  bytes packets errors dropped
+.br
+          7176      58      0       0
+
+Note how the l3_stats_info for the selected group is also part of the dump.
+
+.TQ
+.BR "group xstats " and " group xstats_slave " subgroups:
+.in 21
+
+.ti 14
+.B subgroup bridge \fR[\fB suite stp \fR] [\fB suite mcast \fR]
+- Statistics for STP and, respectively, IGMP / MLD (under the keyword
+\fBmcast\fR) traffic on bridges and their slaves.
+
+.ti 14
+.B subgroup bond \fR[\fB suite 802.3ad \fR]
+- Statistics for LACP traffic on bond devices and their slaves.
+
+.TQ
+.BR "group afstats " subgroups:
+.in 21
+
+.ti 14
+.B subgroup mpls
+- Statistics for MPLS traffic seen on the netdevice. For example:
+
+# ip stats show dev veth01 group afstats subgroup mpls
+.br
+3: veth01: group afstats subgroup mpls
+.br
+    RX: bytes packets errors dropped noroute
+.br
+            0       0      0       0       0
+.br
+    TX: bytes packets errors dropped
+.br
+          216       2      0       0
+
+.SH EXAMPLES
+.PP
+# ip stats set dev swp1 l3_stats on
+.RS
+Enables collection of L3 HW statistics on swp1.
+.RE
+
+.PP
+# ip stats show group offload
+.RS
+Shows all offload statistics on all netdevices.
+.RE
+
+.PP
+# ip stats show dev swp1 group link
+.RS
+Shows link statistics on the given netdevice.
+.RE
+
+.SH SEE ALSO
+.br
+.BR ip (8),
+.BR ip-link (8),
+
+.SH AUTHOR
+Manpage by Petr Machata.
diff -pruN 5.15.0-1/man/man8/ip-xfrm.8 5.19.0-1/man/man8/ip-xfrm.8
--- 5.15.0-1/man/man8/ip-xfrm.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/ip-xfrm.8	2022-08-02 18:36:33.000000000 +0000
@@ -299,6 +299,18 @@ ip-xfrm \- transform configuration
 .IR LBITS " " RBITS " ]"
 
 .ti -8
+.B "ip xfrm policy setdefault"
+.IR DIR
+.IR ACTION " [ "
+.IR DIR
+.IR ACTION " ] [ "
+.IR DIR
+.IR ACTION " ]"
+
+.ti -8
+.B "ip xfrm policy getdefault"
+
+.ti -8
 .IR SELECTOR " :="
 .RB "[ " src
 .IR ADDR "[/" PLEN "] ]"
diff -pruN 5.15.0-1/man/man8/rdma-statistic.8 5.19.0-1/man/man8/rdma-statistic.8
--- 5.15.0-1/man/man8/rdma-statistic.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/rdma-statistic.8	2022-08-02 18:36:33.000000000 +0000
@@ -59,6 +59,28 @@ rdma-statistic \- RDMA statistic counter
 .RI "[ " OBJECT-ID " ]"
 
 .ti -8
+.B rdma statistic
+.B mode
+.B "[" supported "]"
+.B link
+.RI "[ " DEV/PORT_INDEX " ]"
+
+.ti -8
+.B rdma statistic
+.B set
+.B link
+.RI "[ " DEV/PORT_INDEX " ]"
+.B optional-counters
+.RI "[ " OPTIONAL-COUNTERS " ]"
+
+.ti -8
+.B rdma statistic
+.B unset
+.B link
+.RI "[ " DEV/PORT_INDEX " ]"
+.B optional-counters
+
+.ti -8
 .IR COUNTER_SCOPE " := "
 .RB "{ " link " | " dev " }"
 
@@ -100,6 +122,17 @@ When unbound the statistics of this obje
 - specifies the id of the counter to be bound.
 If this argument is omitted then a new counter will be allocated.
 
+.SS rdma statistic mode - Display the enabled optional counters for each link.
+
+.SS rdma statistic mode supported - Display the supported optional counters for each link.
+
+.SS rdma statistic set - Enable a set of optional counters for a specific device/port.
+
+.I "OPTIONAL-COUNTERS"
+- specifies the name of the optional counters to enable. Optional counters that are not specified will be disabled. Note that optional counters are driver-specific.
+
+.SS rdma statistic unset - Disable all optional counters for a specific device/port.
+
 .SH "EXAMPLES"
 .PP
 rdma statistic show
@@ -186,6 +219,26 @@ rdma statistic show mr mrn 6
 .RS 4
 Dump a specific MR statistics with mrn 6. Dumps nothing if does not exists.
 .RE
+.PP
+rdma statistic mode link mlx5_2/1
+.RS 4
+Display the optional counters that was enabled on mlx5_2/1.
+.RE
+.PP
+rdma statistic mode supported link mlx5_2/1
+.RS 4
+Display the optional counters that mlx5_2/1 supports.
+.RE
+.PP
+rdma statistic set link mlx5_2/1 optional-counters cc_rx_ce_pkts,cc_rx_cnp_pkts
+.RS 4
+Enable the cc_rx_ce_pkts,cc_rx_cnp_pkts counters on device mlx5_2 port 1.
+.RE
+.PP
+rdma statistic unset link mlx5_2/1 optional-counters
+.RS 4
+Disable all the optional counters on device mlx5_2 port 1.
+.RE
 
 .SH SEE ALSO
 .BR rdma (8),
@@ -198,3 +251,5 @@ Dump a specific MR statistics with mrn 6
 Mark Zhang <markz@mellanox.com>
 .br
 Erez Alfasi <ereza@mellanox.com>
+.br
+Neta Ostrovsky <netao@nvidia.com>
diff -pruN 5.15.0-1/man/man8/routef.8 5.19.0-1/man/man8/routef.8
--- 5.15.0-1/man/man8/routef.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/routef.8	1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
-.so man8/routel.8
diff -pruN 5.15.0-1/man/man8/routel.8 5.19.0-1/man/man8/routel.8
--- 5.15.0-1/man/man8/routel.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/routel.8	2022-08-02 18:36:33.000000000 +0000
@@ -1,25 +1,31 @@
-.TH "ROUTEL" "8" "3 Jan, 2008" "iproute2" "Linux"
+.TH ROUTEL 8 "1 Sept, 2021" "iproute2" "Linux"
 .SH "NAME"
-.LP
 routel \- list routes with pretty output format
-.br
-routef \- flush routes
-.SH "SYNTAX"
-.LP
-routel [\fItablenr\fP [\fIraw ip args...\fP]]
-.br
-routef
+.SH SYNOPSIS
+.B routel
+.RI "[ " OPTIONS " ]"
+.RI "[ " tablenr
+[ \fIip route options...\fR ] ]
+.P
+.ti 8
+.IR OPTIONS " := {"
+\fB-h\fR | \fB--help\fR |
+[{\fB-f\fR | \fB--family\fR }
+{\fBinet\fR | \fBinet6\fR } |
+\fB-4\fR | \fB-6\fR }
+
 .SH "DESCRIPTION"
 .LP
-These programs are a set of helper scripts you can use instead of raw iproute2 commands.
-.br
-The routel script will list routes in a format that some might consider easier to interpret then the ip route list equivalent.
-.br
-The routef script does not take any arguments and will simply flush the routing table down the drain. Beware! This means deleting all routes which will make your network unusable!
+The routel script will list routes in a format that some might consider
+easier to interpret then the
+.B ip
+route list equivalent.
 
 .SH "AUTHORS"
 .LP
-The routel script was written by Stephen R. van den Berg <srb@cuci.nl>, 1999/04/18 and donated to the public domain.
+Rewritten by Stephen Hemminger <stephen@networkplumber.org>.
+.br
+Original script by Stephen R. van den Berg <srb@cuci.nl>.
 .br
 This manual page was written by Andreas Henriksson  <andreas@fatal.se>, for the Debian GNU/Linux system.
 .SH "SEE ALSO"
diff -pruN 5.15.0-1/man/man8/rtpr.8 5.19.0-1/man/man8/rtpr.8
--- 5.15.0-1/man/man8/rtpr.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/rtpr.8	1970-01-01 00:00:00.000000000 +0000
@@ -1,25 +0,0 @@
-.TH RTPR 8 "18 September, 2015"
-
-.SH NAME
-rtpr \- replace backslashes with newlines.
-
-.SH DESCRIPTION
-.B rtpr
-is a trivial shell script which converts backslashes in standard input to newlines. It's sole purpose is to be fed with input from
-.B ip
-when executed with it's
-.B --oneline
-flag.
-
-.SH EXAMPLES
-.TP
-ip --oneline address show | rtpr
-Undo oneline converted
-.B ip-address
-output.
-
-.SH SEE ALSO
-.BR ip (8)
-
-.SH AUTHORS
-Stephen Hemminger <shemming@brocade.com>
diff -pruN 5.15.0-1/man/man8/ss.8 5.19.0-1/man/man8/ss.8
--- 5.15.0-1/man/man8/ss.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/ss.8	2022-08-02 18:36:33.000000000 +0000
@@ -409,7 +409,7 @@ Please take a look at the official docum
 .SH STATE-FILTER
 
 .B STATE-FILTER
-allows to construct arbitrary set of states to match. Its syntax is
+allows one to construct arbitrary set of states to match. Its syntax is
 sequence of keywords state and exclude followed by identifier of
 state.
 .TP
diff -pruN 5.15.0-1/man/man8/tc.8 5.19.0-1/man/man8/tc.8
--- 5.15.0-1/man/man8/tc.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc.8	2022-08-02 18:36:33.000000000 +0000
@@ -343,7 +343,7 @@ band is not stopped prior to dequeuing a
 .TP
 netem
 Network Emulator is an enhancement of the Linux traffic control facilities that
-allow to add delay, packet loss, duplication and more other characteristics to
+allow one to add delay, packet loss, duplication and more other characteristics to
 packets outgoing from a selected network interface.
 .TP
 pfifo_fast
diff -pruN 5.15.0-1/man/man8/tc-actions.8 5.19.0-1/man/man8/tc-actions.8
--- 5.15.0-1/man/man8/tc-actions.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-actions.8	2022-08-02 18:36:33.000000000 +0000
@@ -52,6 +52,8 @@ actions \- independently defined actions
 .I HWSTATSSPEC
 ] [
 .I CONTROL
+] [
+.I SKIPSPEC
 ]
 
 .I ACTISPEC
@@ -99,6 +101,11 @@ Time since last update.
 .IR reclassify " | " pipe " | " drop " | " continue " | " ok
 }
 
+.I SKIPSPEC
+:= {
+.IR skip_sw " | " skip_hw
+}
+
 .I TC_OPTIONS
 These are the options that are specific to
 .B tc
@@ -270,6 +277,23 @@ Return to the calling qdisc for packet p
 this packet.
 .RE
 
+.TP
+.I SKIPSPEC
+The
+.I SKIPSPEC
+indicates how
+.B tc
+should proceed when executing the action. Any of the following are valid:
+.RS
+.TP
+.B skip_sw
+Do not process action by software. If hardware has no offload support for this
+action, operation will fail.
+.TP
+.B skip_hw
+Do not process action by hardware.
+.RE
+
 .SH SEE ALSO
 .BR tc (8),
 .BR tc-bpf (8),
diff -pruN 5.15.0-1/man/man8/tc-basic.8 5.19.0-1/man/man8/tc-basic.8
--- 5.15.0-1/man/man8/tc-basic.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-basic.8	2022-08-02 18:36:33.000000000 +0000
@@ -14,7 +14,7 @@ basic \- basic traffic control filter
 .SH DESCRIPTION
 The
 .B basic
-filter allows to classify packets using the extended match infrastructure.
+filter allows one to classify packets using the extended match infrastructure.
 .SH OPTIONS
 .TP
 .BI action " ACTION_SPEC"
diff -pruN 5.15.0-1/man/man8/tc-bpf.8 5.19.0-1/man/man8/tc-bpf.8
--- 5.15.0-1/man/man8/tc-bpf.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-bpf.8	2022-08-02 18:36:33.000000000 +0000
@@ -217,7 +217,7 @@ identifiers which would need to reparse
 .SS bytecode
 is being used for loading cBPF classifier and actions only. The cBPF bytecode
 is directly passed as a text string in the form of
-.B \'s,c t f k,c t f k,c t f k,...\'
+.B \(aqs,c t f k,c t f k,c t f k,...'
 , where
 .B s
 denotes the number of subsequent 4-tuples. One such 4-tuple consists of
diff -pruN 5.15.0-1/man/man8/tc-ct.8 5.19.0-1/man/man8/tc-ct.8
--- 5.15.0-1/man/man8/tc-ct.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-ct.8	2022-08-02 18:36:33.000000000 +0000
@@ -74,8 +74,8 @@ Example showing natted firewall in connt
 
 #Add ingress qdisc on eth0 and eth1 interfaces
 .nf
-$ tc qdisc add dev eth0 handle ingress
-$ tc qdisc add dev eth1 handle ingress
+$ tc qdisc add dev eth0 ingress
+$ tc qdisc add dev eth1 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 \\
diff -pruN 5.15.0-1/man/man8/tc-ctinfo.8 5.19.0-1/man/man8/tc-ctinfo.8
--- 5.15.0-1/man/man8/tc-ctinfo.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-ctinfo.8	2022-08-02 18:36:33.000000000 +0000
@@ -58,7 +58,7 @@ Specify the conntrack zone when doing co
 zone is a 16bit unsigned decimal value.
 Default is 0.
 .IP CONTROL
-The following keywords allow to control how the tree of qdisc, classes,
+The following keywords allow one to control how the tree of qdisc, classes,
 filters and actions is further traversed after this action.
 .RS
 .TP
diff -pruN 5.15.0-1/man/man8/tc-flower.8 5.19.0-1/man/man8/tc-flower.8
--- 5.15.0-1/man/man8/tc-flower.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-flower.8	2022-08-02 18:36:33.000000000 +0000
@@ -89,6 +89,8 @@ flower \- flow based traffic control fil
 .B vxlan_opts
 |
 .B erspan_opts
+|
+.B gtp_opts
 }
 .IR OPTIONS " | "
 .BR ip_flags
@@ -162,6 +164,11 @@ provided in LLADDR format, in which case
 number of high bits to match. If the mask is missing then a match on all
 bits is assumed.
 .TP
+.BI num_of_vlans " NUM"
+Match on the number of vlan tags in the packet.
+.I NUM
+can be 0 or small positive integer. Typically in 0-4 range.
+.TP
 .BI vlan_id " VID"
 Match on vlan tag id.
 .I VID
@@ -411,6 +418,8 @@ Match the connection zone, and can be ma
 .BI vxlan_opts " OPTIONS"
 .TQ
 .BI erspan_opts " OPTIONS"
+.TQ
+.BI gtp_opts " OPTIONS"
 Match on IP tunnel metadata. Key id
 .I NUMBER
 is a 32 bit tunnel key id (e.g. VNI for VXLAN tunnel).
@@ -446,6 +455,12 @@ VERSION:INDEX:DIR:HWID/VERSION:INDEX_MAS
 represented as a 8bit number, INDEX as an 32bit number, DIR and HWID as a 8bit
 number. Multiple options is not supported. Note INDEX/INDEX_MASK is used when
 VERSION is 1, and DIR/DIR_MASK and HWID/HWID_MASK are used when VERSION is 2.
+gtp_opts
+.I OPTIONS
+doesn't support multiple options, and it consists of a key followed by a slash
+and corresponding mask. If the mask is missing, \fBtc\fR assumes a full-length
+match. The option can be described in the form PDU_TYPE:QFI/PDU_TYPE_MASK:QFI_MASK
+where both PDU_TYPE and QFI are represented as a 8bit hexadecimal values.
 .TP
 .BI ip_flags " IP_FLAGS"
 .I IP_FLAGS
diff -pruN 5.15.0-1/man/man8/tc-fq_codel.8 5.19.0-1/man/man8/tc-fq_codel.8
--- 5.15.0-1/man/man8/tc-fq_codel.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-fq_codel.8	2022-08-02 18:36:33.000000000 +0000
@@ -20,6 +20,8 @@ BYTES ] [
 ] [
 .B ce_threshold
 TIME ] [
+.B ce_threshold_selector
+VALUE/MASK ] [
 .B memory_limit
 BYTES ]
 
@@ -64,7 +66,8 @@ the local minimum queue delay that packe
 has the same semantics as
 .B codel
 and is used to ensure that the measured minimum delay does not become too stale.
-The minimum delay must be experienced in the last epoch of length .B interval.
+The minimum delay must be experienced in the last epoch of length 
+.BR interval .
 It should be set on the order of the worst-case RTT through the bottleneck to
 give endpoints sufficient time to react. Default value is 100ms.
 
@@ -89,6 +92,22 @@ sets a threshold above which all packets
 Experienced. This is useful for DCTCP-style congestion control algorithms that
 require marking at very shallow queueing thresholds.
 
+.SS ce_threshold_selector
+sets a filter so that the
+.B ce_threshold
+feature is applied to only a subset of the traffic seen by the qdisc. If set, the MASK value
+will be applied as a bitwise AND to the diffserv/ECN byte of the IP header, and only if the
+result of this masking equals VALUE, will the
+.B ce_threshold
+logic be applied to the packet.
+
+.SS drop_batch
+sets the maximum number of packets to drop when
+.B limit
+or
+.B memory_limit
+is exceeded. Default value is 64.
+
 .SH EXAMPLES
 #tc qdisc add   dev eth0 root fq_codel
 .br
diff -pruN 5.15.0-1/man/man8/tc-fw.8 5.19.0-1/man/man8/tc-fw.8
--- 5.15.0-1/man/man8/tc-fw.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-fw.8	2022-08-02 18:36:33.000000000 +0000
@@ -12,13 +12,13 @@ fw \- fwmark traffic control filter
 .SH DESCRIPTION
 the
 .B fw
-filter allows to classify packets based on a previously set
+filter allows one to classify packets based on a previously set
 .BR fwmark " by " iptables .
 If it is identical to the filter's
 .BR handle ,
 the filter matches.
 .B iptables
-allows to mark single packets with the
+allows one to mark single packets with the
 .B MARK
 target, or whole connections using
 .BR CONNMARK .
diff -pruN 5.15.0-1/man/man8/tc-matchall.8 5.19.0-1/man/man8/tc-matchall.8
--- 5.15.0-1/man/man8/tc-matchall.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-matchall.8	2022-08-02 18:36:33.000000000 +0000
@@ -15,7 +15,7 @@ matchall \- traffic control filter that
 .SH DESCRIPTION
 The
 .B matchall
-filter allows to classify every packet that flows on the port and run a
+filter allows one to classify every packet that flows on the port and run a
 action on it.
 .SH OPTIONS
 .TP
diff -pruN 5.15.0-1/man/man8/tc-nat.8 5.19.0-1/man/man8/tc-nat.8
--- 5.15.0-1/man/man8/tc-nat.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-nat.8	2022-08-02 18:36:33.000000000 +0000
@@ -25,7 +25,7 @@ nat - stateless native address translati
 .SH DESCRIPTION
 The
 .B nat
-action allows to perform NAT without the overhead of conntrack, which is
+action allows one to perform NAT without the overhead of conntrack, which is
 desirable if the number of flows or addresses to perform NAT on is large. This
 action is best used in combination with the
 .B u32
diff -pruN 5.15.0-1/man/man8/tc-netem.8 5.19.0-1/man/man8/tc-netem.8
--- 5.15.0-1/man/man8/tc-netem.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-netem.8	2022-08-02 18:36:33.000000000 +0000
@@ -67,7 +67,7 @@ NetEm \- Network Emulator
 
 .SH DESCRIPTION
 NetEm is an enhancement of the Linux traffic control facilities
-that allow to add delay, packet loss, duplication and more other
+that allow one to add delay, packet loss, duplication and more other
 characteristics to packets outgoing from a selected network
 interface. NetEm is built using the existing Quality Of Service (QOS)
 and Differentiated Services (diffserv) facilities in the Linux
@@ -82,12 +82,12 @@ maximum number of packets the qdisc may
 
 .SS delay
 adds the chosen delay to the packets outgoing to chosen network interface. The
-optional parameters allows to introduce a delay variation and a correlation.
+optional parameters allows one to introduce a delay variation and a correlation.
 Delay and jitter values are expressed in ms while correlation is percentage.
 
 .SS distribution
 allow the user to choose the delay distribution. If not specified, the default
-distribution is Normal. Additional parameters allow to consider situations in
+distribution is Normal. Additional parameters allow one to consider situations in
 which network has variable delays depending on traffic flows concurring on the
 same path, that causes several delay peaks and a tail.
 
@@ -99,7 +99,7 @@ is now deprecated due to the noticed bad
 .SS loss state
 adds packet losses according to the 4-state Markov using the transition
 probabilities as input parameters. The parameter p13 is mandatory and if used
-alone corresponds to the Bernoulli model. The optional parameters allows to
+alone corresponds to the Bernoulli model. The optional parameters allows one to
 extend the model to 2-state (p31), 3-state (p23 and p32) and 4-state (p14).
 State 1 corresponds to good reception, State 4 to independent losses, State 3
 to burst losses and State 2 to good reception within a burst.
diff -pruN 5.15.0-1/man/man8/tc-pedit.8 5.19.0-1/man/man8/tc-pedit.8
--- 5.15.0-1/man/man8/tc-pedit.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-pedit.8	2022-08-02 18:36:33.000000000 +0000
@@ -118,7 +118,7 @@ or a single byte
 .BI at " AT " offmask " MASK " shift " SHIFT"
 This is an optional part of
 .IR RAW_OP
-which allows to have a variable
+which allows one to have a variable
 .I OFFSET
 depending on packet data at offset
 .IR AT ,
@@ -202,7 +202,7 @@ unexpected things.
 .B icmp_type
 .TQ
 .B icmp_code
-Again, this allows to change data past the actual IP header itself. It assumes
+Again, this allows one to change data past the actual IP header itself. It assumes
 an ICMP header is present immediately following the (minimal sized) IP header.
 If it is not or the latter is bigger than the minimum of 20 bytes, this will do
 unexpected things. These fields are eight-bit values.
@@ -300,11 +300,11 @@ Keep the addressed data as is.
 .BI retain " RVAL"
 This optional extra part of
 .I CMD_SPEC
-allows to exclude bits from being changed. Supported only for 32 bits fields
+allows one to exclude bits from being changed. Supported only for 32 bits fields
 or smaller.
 .TP
 .I CONTROL
-The following keywords allow to control how the tree of qdisc, classes,
+The following keywords allow one to control how the tree of qdisc, classes,
 filters and actions is further traversed after this action.
 .RS
 .TP
diff -pruN 5.15.0-1/man/man8/tc-sample.8 5.19.0-1/man/man8/tc-sample.8
--- 5.15.0-1/man/man8/tc-sample.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-sample.8	2022-08-02 18:36:33.000000000 +0000
@@ -92,8 +92,8 @@ packets that came from a specific rule.
 .TP
 .BI index " INDEX"
 Is a unique ID for an action. When creating new action instance, this parameter
-allows to set the new action index. When using existing action, this parameter
-allows to specify the existing action index.  The index must 32bit unsigned
+allows one to set the new action index. When using existing action, this parameter
+allows one to specify the existing action index.  The index must 32bit unsigned
 integer greater than zero.
 .SH EXAMPLES
 Sample one of every 100 packets flowing into interface eth0 to psample group 12:
diff -pruN 5.15.0-1/man/man8/tc-skbedit.8 5.19.0-1/man/man8/tc-skbedit.8
--- 5.15.0-1/man/man8/tc-skbedit.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-skbedit.8	2022-08-02 18:36:33.000000000 +0000
@@ -16,9 +16,9 @@ skbedit - SKB editing action
 .SH DESCRIPTION
 The
 .B skbedit
-action allows to change a packet's associated meta data. It complements the
+action allows one to change a packet's associated meta data. It complements the
 .B pedit
-action, which in turn allows to change parts of the packet data itself.
+action, which in turn allows one to change parts of the packet data itself.
 
 The most unique feature of
 .B skbedit
diff -pruN 5.15.0-1/man/man8/tc-skbmod.8 5.19.0-1/man/man8/tc-skbmod.8
--- 5.15.0-1/man/man8/tc-skbmod.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-skbmod.8	2022-08-02 18:36:33.000000000 +0000
@@ -63,7 +63,7 @@ Used to mark ECN Capable Transport (ECT)
 Does not affect Non ECN-Capable Transport (Non-ECT) packets.
 .TP
 .I CONTROL
-The following keywords allow to control how the tree of qdisc, classes,
+The following keywords allow one to control how the tree of qdisc, classes,
 filters and actions is further traversed after this action.
 .RS
 .TP
diff -pruN 5.15.0-1/man/man8/tc-tcindex.8 5.19.0-1/man/man8/tc-tcindex.8
--- 5.15.0-1/man/man8/tc-tcindex.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-tcindex.8	2022-08-02 18:36:33.000000000 +0000
@@ -16,7 +16,7 @@ tcindex \- traffic control index filter
 .B action
 .BR ACTION_SPEC " ]"
 .SH DESCRIPTION
-This filter allows to match packets based on their
+This filter allows one to match packets based on their
 .B tcindex
 field value, i.e. the combination of the DSCP and ECN fields as present in IPv4
 and IPv6 headers.
diff -pruN 5.15.0-1/man/man8/tc-tunnel_key.8 5.19.0-1/man/man8/tc-tunnel_key.8
--- 5.15.0-1/man/man8/tc-tunnel_key.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-tunnel_key.8	2022-08-02 18:36:33.000000000 +0000
@@ -23,7 +23,7 @@ tunnel_key - Tunnel metadata manipulatio
 .SH DESCRIPTION
 The
 .B tunnel_key
-action combined with a shared IP tunnel device, allows to perform IP tunnel en-
+action combined with a shared IP tunnel device, allows one to perform IP tunnel en-
 or decapsulation on a packet, reflected by
 the operation modes
 .IR UNSET " and " SET .
diff -pruN 5.15.0-1/man/man8/tc-u32.8 5.19.0-1/man/man8/tc-u32.8
--- 5.15.0-1/man/man8/tc-u32.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-u32.8	2022-08-02 18:36:33.000000000 +0000
@@ -150,7 +150,7 @@ u32 \- universal 32bit traffic control f
 .BR at " [ " nexthdr+ " ] "
 .IR int_value " ]"
 .SH DESCRIPTION
-The Universal/Ugly 32bit filter allows to match arbitrary bitfields in the
+The Universal/Ugly 32bit filter allows one to match arbitrary bitfields in the
 packet. Due to breaking everything down to values, masks and offsets, It is
 equally powerful and hard to use. Luckily many abstracting directives are
 present which allow defining rules on a higher level and therefore free the
@@ -375,7 +375,7 @@ or IPv6 (
 .BR ip6 )
 header.
 .IR IP / IP6
-then allows to match various header fields:
+then allows one to match various header fields:
 .RS
 .TP
 .BI src " ADDR"
@@ -427,7 +427,7 @@ Also minimal header size for IPv4 and la
 IPv4 only, check certain flags and fragment offset values. Match if the packet
 is not a fragment
 .RB ( nofrag ),
-the first fragment
+the first fragment of a fragmented packet
 .RB ( firstfrag ),
 if Don't Fragment
 .RB ( df )
@@ -644,7 +644,7 @@ tc filter add dev eth0 parent 1:0 protoc
 tc filter add dev eth0 parent 1:0 protocol ip \\
         u32 ht 800: \\
         match ip protocol 6 FF \\
-        match ip firstfrag \\
+        match u16 0 1fff at 6 \\
         offset at 0 mask 0f00 shift 6 \\
         link 1:
 .EE
diff -pruN 5.15.0-1/man/man8/tc-vlan.8 5.19.0-1/man/man8/tc-vlan.8
--- 5.15.0-1/man/man8/tc-vlan.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-vlan.8	2022-08-02 18:36:33.000000000 +0000
@@ -35,7 +35,7 @@ vlan - vlan manipulation module
 .SH DESCRIPTION
 The
 .B vlan
-action allows to perform 802.1Q en- or decapsulation on a packet, reflected by
+action allows one to perform 802.1Q en- or decapsulation on a packet, reflected by
 the operation modes
 .IR POP ", " PUSH " and " MODIFY .
 The
@@ -45,7 +45,7 @@ outer-most VLAN encapsulation. The
 .IR PUSH " and " MODIFY
 modes require at least a
 .I VLANID
-and allow to optionally choose the
+and allow one to optionally choose the
 .I VLANPROTO
 to use.
 
diff -pruN 5.15.0-1/man/man8/tc-xt.8 5.19.0-1/man/man8/tc-xt.8
--- 5.15.0-1/man/man8/tc-xt.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/tc-xt.8	2022-08-02 18:36:33.000000000 +0000
@@ -10,7 +10,7 @@ xt - tc iptables action
 .SH DESCRIPTION
 The
 .B xt
-action allows to call arbitrary iptables targets for packets matching the filter
+action allows one to call arbitrary iptables targets for packets matching the filter
 this action is attached to.
 .SH OPTIONS
 .TP
diff -pruN 5.15.0-1/man/man8/vdpa-dev.8 5.19.0-1/man/man8/vdpa-dev.8
--- 5.15.0-1/man/man8/vdpa-dev.8	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/man/man8/vdpa-dev.8	2022-08-02 18:36:33.000000000 +0000
@@ -31,11 +31,18 @@ vdpa-dev \- vdpa device configuration
 .I NAME
 .B mgmtdev
 .I MGMTDEV
+.RI "[ mac " MACADDR " ]"
+.RI "[ mtu " MTU " ]"
+.RI "[ max_vqp " MAX_VQ_PAIRS " ]"
 
 .ti -8
 .B vdpa dev del
 .I DEV
 
+.ti -8
+.B vdpa dev config show
+.RI "[ " DEV " ]"
+
 .SH "DESCRIPTION"
 .SS vdpa dev show - display vdpa device attributes
 
@@ -59,12 +66,33 @@ Name of the new vdpa device to add.
 .BI mgmtdev " MGMTDEV"
 Name of the management device to use for device addition.
 
+.PP
+.BI mac " MACADDR"
+- specifies the mac address for the new vdpa device.
+This is applicable only for the network type of vdpa device. This is optional.
+
+.BI mtu " MTU"
+- specifies the mtu for the new vdpa device.
+This is applicable only for the network type of vdpa device. This is optional.
+
 .SS vdpa dev del - Delete the vdpa device.
 
 .PP
 .I "DEV"
 - specifies the vdpa device to delete.
 
+.SS vdpa dev config show - Show configuration of specific device or all devices.
+
+.PP
+.I "DEV"
+- specifies the vdpa device to show its configuration.
+If this argument is omitted all devices configuration is listed.
+
+.in +4
+Format is:
+.in +2
+VDPA_DEVICE_NAME
+
 .SH "EXAMPLES"
 .PP
 vdpa dev show
@@ -82,10 +110,30 @@ vdpa dev add name foo mgmtdev vdpa_sim_n
 Add the vdpa device named foo on the management device vdpa_sim_net.
 .RE
 .PP
+vdpa dev add name foo mgmtdev vdpa_sim_net mac 00:11:22:33:44:55
+.RS 4
+Add the vdpa device named foo on the management device vdpa_sim_net with mac address of 00:11:22:33:44:55.
+.RE
+.PP
+vdpa dev add name foo mgmtdev vdpa_sim_net mac 00:11:22:33:44:55 mtu 9000
+.RS 4
+Add the vdpa device named foo on the management device vdpa_sim_net with mac address of 00:11:22:33:44:55 and mtu of 9000 bytes.
+.RE
+.PP
+vdpa dev add name foo mgmtdev auxiliary/mlx5_core.sf.1 mac 00:11:22:33:44:55 max_vqp 8
+.RS 4
+Add the vdpa device named foo on the management device auxiliary/mlx5_core.sf.1 with mac address of 00:11:22:33:44:55 and max 8 virtqueue pairs
+.RE
+.PP
 vdpa dev del foo
 .RS 4
 Delete the vdpa device named foo which was previously created.
 .RE
+.PP
+vdpa dev config show foo
+.RS 4
+Shows the vdpa device configuration of device named foo.
+.RE
 
 .SH SEE ALSO
 .BR vdpa (8),
diff -pruN 5.15.0-1/misc/ifstat.c 5.19.0-1/misc/ifstat.c
--- 5.15.0-1/misc/ifstat.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/misc/ifstat.c	2022-08-02 18:36:33.000000000 +0000
@@ -202,7 +202,7 @@ static void load_info(void)
 		ll_init_map(&rth);
 		filter_mask = IFLA_STATS_FILTER_BIT(filter_type);
 		if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC,
-					      filter_mask) < 0) {
+					      filter_mask, NULL, NULL) < 0) {
 			perror("Cannot send dump request");
 			exit(1);
 		}
diff -pruN 5.15.0-1/misc/lnstat.c 5.19.0-1/misc/lnstat.c
--- 5.15.0-1/misc/lnstat.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/misc/lnstat.c	2022-08-02 18:36:33.000000000 +0000
@@ -210,8 +210,9 @@ static struct table_hdr *build_hdr_strin
 		}
 		ofs += width+1;
 	}
+
 	/* fill in spaces */
-	for (h = 1; h <= th.num_lines; h++) {
+	for (h = 1; h < th.num_lines; h++) {
 		for (i = 0; i < ofs; i++) {
 			if (th.hdr[h][i] == '\0')
 				th.hdr[h][i] = ' ';
@@ -330,6 +331,7 @@ int main(int argc, char **argv)
 				for (i = 0; i < MAX_FIELDS; i++)
 					fp.params[i].print.width = len;
 			}
+			free(tmp);
 			break;
 		default:
 			usage(argv[0], 1);
diff -pruN 5.15.0-1/misc/ss.c 5.19.0-1/misc/ss.c
--- 5.15.0-1/misc/ss.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/misc/ss.c	2022-08-02 18:36:33.000000000 +0000
@@ -55,6 +55,11 @@
 #include <linux/tls.h>
 #include <linux/mptcp.h>
 
+#if HAVE_RPC
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#endif
+
 /* AF_VSOCK/PF_VSOCK is only provided since glibc 2.18 */
 #ifndef PF_VSOCK
 #define PF_VSOCK 40
@@ -97,6 +102,11 @@ static int security_get_initial_context(
 	*context = NULL;
 	return -1;
 }
+
+static void freecon(char *context)
+{
+	free(context);
+}
 #endif
 
 int preferred_family = AF_UNSPEC;
@@ -187,8 +197,12 @@ static struct {
 } buffer;
 
 static const char *TCP_PROTO = "tcp";
-static const char *SCTP_PROTO = "sctp";
 static const char *UDP_PROTO = "udp";
+#ifdef HAVE_RPC
+static const char *TCP6_PROTO = "tcp6";
+static const char *UDP6_PROTO = "udp6";
+static const char *SCTP_PROTO = "sctp";
+#endif
 static const char *RAW_PROTO = "raw";
 static const char *dg_proto;
 
@@ -618,7 +632,7 @@ static void user_ent_hash_build(void)
 		snprintf(name + nameoff, sizeof(name) - nameoff, "%d/fd/", pid);
 		pos = strlen(name);
 		if ((dir1 = opendir(name)) == NULL) {
-			free(pid_context);
+			freecon(pid_context);
 			continue;
 		}
 
@@ -667,9 +681,9 @@ static void user_ent_hash_build(void)
 			}
 			user_ent_add(ino, p, pid, fd,
 					pid_context, sock_context);
-			free(sock_context);
+			freecon(sock_context);
 		}
-		free(pid_context);
+		freecon(pid_context);
 		closedir(dir1);
 	}
 	closedir(dir);
@@ -849,6 +863,8 @@ struct tcpstat {
 	unsigned int	    reord_seen;
 	double		    rcv_rtt;
 	double		    min_rtt;
+	unsigned int 	    rcv_ooopack;
+	unsigned int	    snd_wnd;
 	int		    rcv_space;
 	unsigned int        rcv_ssthresh;
 	unsigned long long  busy_time;
@@ -1472,45 +1488,87 @@ struct scache {
 
 static struct scache *rlist;
 
+#ifdef HAVE_RPC
+static CLIENT *rpc_client_create(rpcprog_t prog, rpcvers_t vers)
+{
+	struct netbuf nbuf;
+	struct sockaddr_un saddr;
+	int sock;
+
+	memset(&saddr, 0, sizeof(saddr));
+	sock = socket(AF_LOCAL, SOCK_STREAM, 0);
+	if (sock < 0)
+		return NULL;
+
+	saddr.sun_family = AF_LOCAL;
+	strcpy(saddr.sun_path, _PATH_RPCBINDSOCK);
+	nbuf.len = SUN_LEN(&saddr);
+	nbuf.maxlen = sizeof(struct sockaddr_un);
+	nbuf.buf = &saddr;
+
+	return clnt_vc_create(sock, &nbuf, prog, vers, 0, 0);
+}
+
 static void init_service_resolver(void)
 {
-	char buf[128];
-	FILE *fp = popen("/usr/sbin/rpcinfo -p 2>/dev/null", "r");
+	struct rpcblist *rhead = NULL;
+	struct timeval timeout;
+	struct rpcent *rpc;
+	enum clnt_stat res;
+	CLIENT *client;
+
+	timeout.tv_sec = 5;
+	timeout.tv_usec = 0;
 
-	if (!fp)
+	client = rpc_client_create(PMAPPROG, RPCBVERS4);
+	if (!client)
 		return;
 
-	if (!fgets(buf, sizeof(buf), fp)) {
-		pclose(fp);
+	res = clnt_call(client, RPCBPROC_DUMP, (xdrproc_t)xdr_void, NULL,
+			(xdrproc_t)xdr_rpcblist_ptr, (char *)&rhead,
+			timeout);
+	if (res != RPC_SUCCESS)
 		return;
-	}
-	while (fgets(buf, sizeof(buf), fp) != NULL) {
-		unsigned int progn, port;
-		char proto[128], prog[128] = "rpc.";
+
+	for (; rhead; rhead = rhead->rpcb_next) {
+		char prog[128] = "rpc.";
 		struct scache *c;
+		int hport, lport, ok;
 
-		if (sscanf(buf, "%u %*d %s %u %s",
-			   &progn, proto, &port, prog+4) != 4)
+		c = malloc(sizeof(*c));
+		if (!c)
 			continue;
 
-		if (!(c = malloc(sizeof(*c))))
+		ok = sscanf(rhead->rpcb_map.r_addr, "::.%d.%d", &hport, &lport);
+		if (!ok)
+			ok = sscanf(rhead->rpcb_map.r_addr, "0.0.0.0.%d.%d",
+				    &hport, &lport);
+		if (!ok)
 			continue;
+		c->port = hport << 8 | lport;
 
-		c->port = port;
-		c->name = strdup(prog);
-		if (strcmp(proto, TCP_PROTO) == 0)
+		if (strcmp(rhead->rpcb_map.r_netid, TCP_PROTO) == 0 ||
+		    strcmp(rhead->rpcb_map.r_netid, TCP6_PROTO) == 0)
 			c->proto = TCP_PROTO;
-		else if (strcmp(proto, UDP_PROTO) == 0)
+		else if (strcmp(rhead->rpcb_map.r_netid, UDP_PROTO) == 0 ||
+			 strcmp(rhead->rpcb_map.r_netid, UDP6_PROTO) == 0)
 			c->proto = UDP_PROTO;
-		else if (strcmp(proto, SCTP_PROTO) == 0)
+		else if (strcmp(rhead->rpcb_map.r_netid, SCTP_PROTO) == 0)
 			c->proto = SCTP_PROTO;
 		else
-			c->proto = NULL;
+			continue;
+
+		rpc = getrpcbynumber(rhead->rpcb_map.r_prog);
+		if (rpc) {
+			strncat(prog, rpc->r_name, 128 - strlen(prog));
+			c->name = strdup(prog);
+		}
+
 		c->next = rlist;
 		rlist = c;
 	}
-	pclose(fp);
 }
+#endif
 
 /* Even do not try default linux ephemeral port ranges:
  * default /etc/services contains so much of useless crap
@@ -2649,6 +2707,10 @@ static void tcp_stats_print(struct tcpst
 		out(" notsent:%u", s->not_sent);
 	if (s->min_rtt)
 		out(" minrtt:%g", s->min_rtt);
+	if (s->rcv_ooopack)
+		out(" rcv_ooopack:%u", s->rcv_ooopack);
+	if (s->snd_wnd)
+		out(" snd_wnd:%u", s->snd_wnd);
 }
 
 static void tcp_timer_print(struct tcpstat *s)
@@ -3083,6 +3145,8 @@ static void tcp_show_info(const struct n
 		s.reord_seen = info->tcpi_reord_seen;
 		s.bytes_sent = info->tcpi_bytes_sent;
 		s.bytes_retrans = info->tcpi_bytes_retrans;
+		s.rcv_ooopack = info->tcpi_rcv_ooopack;
+		s.snd_wnd = info->tcpi_snd_wnd;
 		tcp_stats_print(&s);
 		free(s.dctcp);
 		free(s.bbr_info);
@@ -4725,7 +4789,7 @@ static int netlink_show_one(struct filte
 			getpidcon(pid, &pid_context);
 
 		out(" proc_ctx=%s", pid_context ? : "unavailable");
-		free(pid_context);
+		freecon(pid_context);
 	}
 
 	if (show_details) {
@@ -5655,9 +5719,11 @@ int main(int argc, char *argv[])
 	filter_states_set(&current_filter, state_filter);
 	filter_merge_defaults(&current_filter);
 
+#ifdef HAVE_RPC
 	if (!numeric && resolve_hosts &&
 	    (current_filter.dbs & (UNIX_DBM|INET_L4_DBM)))
 		init_service_resolver();
+#endif
 
 	if (current_filter.dbs == 0) {
 		fprintf(stderr, "ss: no socket tables to show with such filter.\n");
diff -pruN 5.15.0-1/rdma/include/uapi/rdma/ib_user_verbs.h 5.19.0-1/rdma/include/uapi/rdma/ib_user_verbs.h
--- 5.15.0-1/rdma/include/uapi/rdma/ib_user_verbs.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/include/uapi/rdma/ib_user_verbs.h	2022-08-02 18:36:33.000000000 +0000
@@ -457,6 +457,17 @@ struct ib_uverbs_poll_cq {
 	__u32 ne;
 };
 
+enum ib_uverbs_wc_opcode {
+	IB_UVERBS_WC_SEND = 0,
+	IB_UVERBS_WC_RDMA_WRITE = 1,
+	IB_UVERBS_WC_RDMA_READ = 2,
+	IB_UVERBS_WC_COMP_SWAP = 3,
+	IB_UVERBS_WC_FETCH_ADD = 4,
+	IB_UVERBS_WC_BIND_MW = 5,
+	IB_UVERBS_WC_LOCAL_INV = 6,
+	IB_UVERBS_WC_TSO = 7,
+};
+
 struct ib_uverbs_wc {
 	__aligned_u64 wr_id;
 	__u32 status;
@@ -585,20 +596,6 @@ enum {
 	IB_UVERBS_CREATE_QP_SUP_COMP_MASK = IB_UVERBS_CREATE_QP_MASK_IND_TABLE,
 };
 
-enum {
-	/*
-	 * This value is equal to IB_QP_DEST_QPN.
-	 */
-	IB_USER_LEGACY_LAST_QP_ATTR_MASK = 1ULL << 20,
-};
-
-enum {
-	/*
-	 * This value is equal to IB_QP_RATE_LIMIT.
-	 */
-	IB_USER_LAST_QP_ATTR_MASK = 1ULL << 25,
-};
-
 struct ib_uverbs_ex_create_qp {
 	__aligned_u64 user_handle;
 	__u32 pd_handle;
@@ -1301,4 +1298,46 @@ struct ib_uverbs_ex_modify_cq {
 
 #define IB_DEVICE_NAME_MAX 64
 
+/*
+ * bits 9, 15, 16, 19, 22, 27, 30, 31, 32, 33, 35 and 37 may be set by old
+ * kernels and should not be used.
+ */
+enum ib_uverbs_device_cap_flags {
+	IB_UVERBS_DEVICE_RESIZE_MAX_WR = 1 << 0,
+	IB_UVERBS_DEVICE_BAD_PKEY_CNTR = 1 << 1,
+	IB_UVERBS_DEVICE_BAD_QKEY_CNTR = 1 << 2,
+	IB_UVERBS_DEVICE_RAW_MULTI = 1 << 3,
+	IB_UVERBS_DEVICE_AUTO_PATH_MIG = 1 << 4,
+	IB_UVERBS_DEVICE_CHANGE_PHY_PORT = 1 << 5,
+	IB_UVERBS_DEVICE_UD_AV_PORT_ENFORCE = 1 << 6,
+	IB_UVERBS_DEVICE_CURR_QP_STATE_MOD = 1 << 7,
+	IB_UVERBS_DEVICE_SHUTDOWN_PORT = 1 << 8,
+	/* IB_UVERBS_DEVICE_INIT_TYPE = 1 << 9, (not in use) */
+	IB_UVERBS_DEVICE_PORT_ACTIVE_EVENT = 1 << 10,
+	IB_UVERBS_DEVICE_SYS_IMAGE_GUID = 1 << 11,
+	IB_UVERBS_DEVICE_RC_RNR_NAK_GEN = 1 << 12,
+	IB_UVERBS_DEVICE_SRQ_RESIZE = 1 << 13,
+	IB_UVERBS_DEVICE_N_NOTIFY_CQ = 1 << 14,
+	IB_UVERBS_DEVICE_MEM_WINDOW = 1 << 17,
+	IB_UVERBS_DEVICE_UD_IP_CSUM = 1 << 18,
+	IB_UVERBS_DEVICE_XRC = 1 << 20,
+	IB_UVERBS_DEVICE_MEM_MGT_EXTENSIONS = 1 << 21,
+	IB_UVERBS_DEVICE_MEM_WINDOW_TYPE_2A = 1 << 23,
+	IB_UVERBS_DEVICE_MEM_WINDOW_TYPE_2B = 1 << 24,
+	IB_UVERBS_DEVICE_RC_IP_CSUM = 1 << 25,
+	/* Deprecated. Please use IB_UVERBS_RAW_PACKET_CAP_IP_CSUM. */
+	IB_UVERBS_DEVICE_RAW_IP_CSUM = 1 << 26,
+	IB_UVERBS_DEVICE_MANAGED_FLOW_STEERING = 1 << 29,
+	/* Deprecated. Please use IB_UVERBS_RAW_PACKET_CAP_SCATTER_FCS. */
+	IB_UVERBS_DEVICE_RAW_SCATTER_FCS = 1ULL << 34,
+	IB_UVERBS_DEVICE_PCI_WRITE_END_PADDING = 1ULL << 36,
+};
+
+enum ib_uverbs_raw_packet_caps {
+	IB_UVERBS_RAW_PACKET_CAP_CVLAN_STRIPPING = 1 << 0,
+	IB_UVERBS_RAW_PACKET_CAP_SCATTER_FCS = 1 << 1,
+	IB_UVERBS_RAW_PACKET_CAP_IP_CSUM = 1 << 2,
+	IB_UVERBS_RAW_PACKET_CAP_DELAY_DROP = 1 << 3,
+};
+
 #endif /* IB_USER_VERBS_H */
diff -pruN 5.15.0-1/rdma/include/uapi/rdma/rdma_netlink.h 5.19.0-1/rdma/include/uapi/rdma/rdma_netlink.h
--- 5.15.0-1/rdma/include/uapi/rdma/rdma_netlink.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/include/uapi/rdma/rdma_netlink.h	2022-08-02 18:36:33.000000000 +0000
@@ -297,6 +297,8 @@ enum rdma_nldev_command {
 
 	RDMA_NLDEV_CMD_RES_SRQ_GET, /* can dump */
 
+	RDMA_NLDEV_CMD_STAT_GET_STATUS,
+
 	RDMA_NLDEV_NUM_OPS
 };
 
@@ -549,6 +551,9 @@ enum rdma_nldev_attr {
 
 	RDMA_NLDEV_SYS_ATTR_COPY_ON_FORK,	/* u8 */
 
+	RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX,	/* u32 */
+	RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC, /* u8 */
+
 	/*
 	 * Always the end
 	 */
diff -pruN 5.15.0-1/rdma/include/uapi/rdma/rdma_user_cm.h 5.19.0-1/rdma/include/uapi/rdma/rdma_user_cm.h
--- 5.15.0-1/rdma/include/uapi/rdma/rdma_user_cm.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/include/uapi/rdma/rdma_user_cm.h	2022-08-02 18:36:33.000000000 +0000
@@ -164,6 +164,8 @@ struct rdma_ucm_query_route_resp {
 	__u32 num_paths;
 	__u8 port_num;
 	__u8 reserved[3];
+	__u32 ibdev_index;
+	__u32 reserved1;
 };
 
 struct rdma_ucm_query_addr_resp {
@@ -175,6 +177,8 @@ struct rdma_ucm_query_addr_resp {
 	__u16 dst_size;
 	struct __kernel_sockaddr_storage src_addr;
 	struct __kernel_sockaddr_storage dst_addr;
+	__u32 ibdev_index;
+	__u32 reserved1;
 };
 
 struct rdma_ucm_query_path_resp {
@@ -206,10 +210,16 @@ struct rdma_ucm_ud_param {
 	__u8  reserved[7];
 };
 
+struct rdma_ucm_ece {
+	__u32 vendor_id;
+	__u32 attr_mod;
+};
+
 struct rdma_ucm_connect {
 	struct rdma_ucm_conn_param conn_param;
 	__u32 id;
 	__u32 reserved;
+	struct rdma_ucm_ece ece;
 };
 
 struct rdma_ucm_listen {
@@ -222,12 +232,14 @@ struct rdma_ucm_accept {
 	struct rdma_ucm_conn_param conn_param;
 	__u32 id;
 	__u32 reserved;
+	struct rdma_ucm_ece ece;
 };
 
 struct rdma_ucm_reject {
 	__u32 id;
 	__u8  private_data_len;
-	__u8  reserved[3];
+	__u8  reason;
+	__u8  reserved[2];
 	__u8  private_data[RDMA_MAX_PRIVATE_DATA];
 };
 
@@ -287,6 +299,7 @@ struct rdma_ucm_event_resp {
 		struct rdma_ucm_ud_param   ud;
 	} param;
 	__u32 reserved;
+	struct rdma_ucm_ece ece;
 };
 
 /* Option levels */
diff -pruN 5.15.0-1/rdma/Makefile 5.19.0-1/rdma/Makefile
--- 5.15.0-1/rdma/Makefile	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/Makefile	2022-08-02 18:36:33.000000000 +0000
@@ -1,16 +1,12 @@
 # SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
 include ../config.mk
 
-TARGETS :=
-
-ifeq ($(HAVE_MNL),y)
 CFLAGS += -I./include/uapi/
 
 RDMA_OBJ = rdma.o utils.o dev.o link.o res.o res-pd.o res-mr.o res-cq.o \
 	   res-cmid.o res-qp.o sys.o stat.o stat-mr.o res-ctx.o res-srq.o
 
 TARGETS += rdma
-endif
 
 all:	$(TARGETS) $(LIBS)
 
diff -pruN 5.15.0-1/rdma/res.c 5.19.0-1/rdma/res.c
--- 5.15.0-1/rdma/res.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/res.c	2022-08-02 18:36:33.000000000 +0000
@@ -51,7 +51,7 @@ static int res_print_summary(struct rd *
 
 		name = mnl_attr_get_str(nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME]);
 		curr = mnl_attr_get_u64(nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]);
-		res_print_uint(
+		res_print_u64(
 			rd, name, curr,
 			nla_line[RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR]);
 	}
@@ -146,12 +146,12 @@ const char *qp_types_to_str(uint8_t idx)
 						     "RAW_ETHERTYPE",
 						     "UNKNOWN", "RAW_PACKET",
 						     "XRC_INI", "XRC_TGT",
-						     [0xFF] = "DRIVER",
 	};
 
-	if (idx < ARRAY_SIZE(qp_types_str) && qp_types_str[idx])
+	if (idx < ARRAY_SIZE(qp_types_str))
 		return qp_types_str[idx];
-	return "UNKNOWN";
+
+	return (idx == 0xFF) ? "DRIVER" : "UNKNOWN";
 }
 
 void print_comm(struct rd *rd, const char *str, struct nlattr **nla_line)
@@ -208,13 +208,22 @@ void print_key(struct rd *rd, const char
 	print_color_hex(PRINT_ANY, COLOR_NONE, name, " 0x%" PRIx64 " ", val);
 }
 
-void res_print_uint(struct rd *rd, const char *name, uint64_t val,
+void res_print_u32(struct rd *rd, const char *name, uint32_t val,
 		    struct nlattr *nlattr)
 {
 	if (!nlattr)
 		return;
 	print_color_uint(PRINT_ANY, COLOR_NONE, name, name, val);
-	print_color_uint(PRINT_FP, COLOR_NONE, NULL, " %d ", val);
+	print_color_uint(PRINT_FP, COLOR_NONE, NULL, " %" PRIu32 " ", val);
+}
+
+void res_print_u64(struct rd *rd, const char *name, uint64_t val,
+		    struct nlattr *nlattr)
+{
+	if (!nlattr)
+		return;
+	print_color_u64(PRINT_ANY, COLOR_NONE, name, name, val);
+	print_color_u64(PRINT_FP, COLOR_NONE, NULL, " %" PRIu64 " ", val);
 }
 
 RES_FUNC(res_no_args,	RDMA_NLDEV_CMD_RES_GET,	NULL, true, 0);
diff -pruN 5.15.0-1/rdma/res-cmid.c 5.19.0-1/rdma/res-cmid.c
--- 5.15.0-1/rdma/res-cmid.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/res-cmid.c	2022-08-02 18:36:33.000000000 +0000
@@ -159,8 +159,15 @@ static int res_cm_id_line(struct rd *rd,
 		goto out;
 
 	if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) {
+		SPRINT_BUF(b);
+
 		pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
-		comm = get_task_name(pid);
+		if (!get_task_name(pid, b, sizeof(b)))
+			comm = b;
+	} else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) {
+		/* discard const from mnl_attr_get_str */
+		comm = (char *)mnl_attr_get_str(
+			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
 	}
 
 	if (rd_is_filtered_attr(rd, "pid", pid,
@@ -173,22 +180,16 @@ static int res_cm_id_line(struct rd *rd,
 				nla_line[RDMA_NLDEV_ATTR_RES_CM_IDN]))
 		goto out;
 
-	if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) {
-		/* discard const from mnl_attr_get_str */
-		comm = (char *)mnl_attr_get_str(
-			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
-	}
-
 	open_json_object(NULL);
 	print_link(rd, idx, name, port, nla_line);
-	res_print_uint(rd, "cm-idn", cm_idn,
+	res_print_u32(rd, "cm-idn", cm_idn,
 		       nla_line[RDMA_NLDEV_ATTR_RES_CM_IDN]);
-	res_print_uint(rd, "lqpn", lqpn, nla_line[RDMA_NLDEV_ATTR_RES_LQPN]);
+	res_print_u32(rd, "lqpn", lqpn, nla_line[RDMA_NLDEV_ATTR_RES_LQPN]);
 	if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE])
 		print_qp_type(rd, type);
 	print_cm_id_state(rd, state);
 	print_ps(rd, ps);
-	res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
+	res_print_u32(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 	print_comm(rd, comm, nla_line);
 
 	if (nla_line[RDMA_NLDEV_ATTR_RES_SRC_ADDR])
@@ -199,8 +200,7 @@ static int res_cm_id_line(struct rd *rd,
 	print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]);
 	newline(rd);
 
-out:	if (nla_line[RDMA_NLDEV_ATTR_RES_PID])
-		free(comm);
+out:
 	return MNL_CB_OK;
 }
 
diff -pruN 5.15.0-1/rdma/res-cq.c 5.19.0-1/rdma/res-cq.c
--- 5.15.0-1/rdma/res-cq.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/res-cq.c	2022-08-02 18:36:33.000000000 +0000
@@ -84,8 +84,15 @@ static int res_cq_line(struct rd *rd, co
 		goto out;
 
 	if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) {
+		SPRINT_BUF(b);
+
 		pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
-		comm = get_task_name(pid);
+		if (!get_task_name(pid, b, sizeof(b)))
+			comm = b;
+	} else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) {
+		/* discard const from mnl_attr_get_str */
+		comm = (char *)mnl_attr_get_str(
+			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
 	}
 
 	if (rd_is_filtered_attr(rd, "pid", pid,
@@ -103,28 +110,22 @@ static int res_cq_line(struct rd *rd, co
 				nla_line[RDMA_NLDEV_ATTR_RES_CTXN]))
 		goto out;
 
-	if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])
-		/* discard const from mnl_attr_get_str */
-		comm = (char *)mnl_attr_get_str(
-			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
-
 	open_json_object(NULL);
 	print_dev(rd, idx, name);
-	res_print_uint(rd, "cqn", cqn, nla_line[RDMA_NLDEV_ATTR_RES_CQN]);
-	res_print_uint(rd, "cqe", cqe, nla_line[RDMA_NLDEV_ATTR_RES_CQE]);
-	res_print_uint(rd, "users", users,
+	res_print_u32(rd, "cqn", cqn, nla_line[RDMA_NLDEV_ATTR_RES_CQN]);
+	res_print_u32(rd, "cqe", cqe, nla_line[RDMA_NLDEV_ATTR_RES_CQE]);
+	res_print_u64(rd, "users", users,
 		       nla_line[RDMA_NLDEV_ATTR_RES_USECNT]);
 	print_poll_ctx(rd, poll_ctx, nla_line[RDMA_NLDEV_ATTR_RES_POLL_CTX]);
 	print_cq_dim_setting(rd, nla_line[RDMA_NLDEV_ATTR_DEV_DIM]);
-	res_print_uint(rd, "ctxn", ctxn, nla_line[RDMA_NLDEV_ATTR_RES_CTXN]);
-	res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
+	res_print_u32(rd, "ctxn", ctxn, nla_line[RDMA_NLDEV_ATTR_RES_CTXN]);
+	res_print_u32(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 	print_comm(rd, comm, nla_line);
 
 	print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]);
 	newline(rd);
 
-out:	if (nla_line[RDMA_NLDEV_ATTR_RES_PID])
-		free(comm);
+out:
 	return MNL_CB_OK;
 }
 
diff -pruN 5.15.0-1/rdma/res-ctx.c 5.19.0-1/rdma/res-ctx.c
--- 5.15.0-1/rdma/res-ctx.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/res-ctx.c	2022-08-02 18:36:33.000000000 +0000
@@ -18,8 +18,15 @@ static int res_ctx_line(struct rd *rd, c
 		return MNL_CB_ERROR;
 
 	if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) {
+		SPRINT_BUF(b);
+
 		pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
-		comm = get_task_name(pid);
+		if (!get_task_name(pid, b, sizeof(b)))
+			comm = b;
+	} else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) {
+		/* discard const from mnl_attr_get_str */
+		comm = (char *)mnl_attr_get_str(
+			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
 	}
 
 	if (rd_is_filtered_attr(rd, "pid", pid,
@@ -33,23 +40,16 @@ static int res_ctx_line(struct rd *rd, c
 				nla_line[RDMA_NLDEV_ATTR_RES_CTXN]))
 		goto out;
 
-	if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])
-		/* discard const from mnl_attr_get_str */
-		comm = (char *)mnl_attr_get_str(
-			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
-
 	open_json_object(NULL);
 	print_dev(rd, idx, name);
-	res_print_uint(rd, "ctxn", ctxn, nla_line[RDMA_NLDEV_ATTR_RES_CTXN]);
-	res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
+	res_print_u32(rd, "ctxn", ctxn, nla_line[RDMA_NLDEV_ATTR_RES_CTXN]);
+	res_print_u32(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 	print_comm(rd, comm, nla_line);
 
 	print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]);
 	newline(rd);
 
 out:
-	if (nla_line[RDMA_NLDEV_ATTR_RES_PID])
-		free(comm);
 	return MNL_CB_OK;
 }
 
diff -pruN 5.15.0-1/rdma/res.h 5.19.0-1/rdma/res.h
--- 5.15.0-1/rdma/res.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/res.h	2022-08-02 18:36:33.000000000 +0000
@@ -188,7 +188,9 @@ void print_link(struct rd *rd, uint32_t
 		struct nlattr **nla_line);
 void print_key(struct rd *rd, const char *name, uint64_t val,
 	       struct nlattr *nlattr);
-void res_print_uint(struct rd *rd, const char *name, uint64_t val,
+void res_print_u32(struct rd *rd, const char *name, uint32_t val,
+		    struct nlattr *nlattr);
+void res_print_u64(struct rd *rd, const char *name, uint64_t val,
 		    struct nlattr *nlattr);
 void print_comm(struct rd *rd, const char *str, struct nlattr **nla_line);
 const char *qp_types_to_str(uint8_t idx);
diff -pruN 5.15.0-1/rdma/res-mr.c 5.19.0-1/rdma/res-mr.c
--- 5.15.0-1/rdma/res-mr.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/res-mr.c	2022-08-02 18:36:33.000000000 +0000
@@ -47,8 +47,15 @@ static int res_mr_line(struct rd *rd, co
 		goto out;
 
 	if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) {
+		SPRINT_BUF(b);
+
 		pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
-		comm = get_task_name(pid);
+		if (!get_task_name(pid, b, sizeof(b)))
+			comm = b;
+	} else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) {
+		/* discard const from mnl_attr_get_str */
+		comm = (char *)mnl_attr_get_str(
+			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
 	}
 
 	if (rd_is_filtered_attr(rd, "pid", pid,
@@ -67,19 +74,15 @@ static int res_mr_line(struct rd *rd, co
 				nla_line[RDMA_NLDEV_ATTR_RES_PDN]))
 		goto out;
 
-	if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])
-		/* discard const from mnl_attr_get_str */
-		comm = (char *)mnl_attr_get_str(
-			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
 	open_json_object(NULL);
 	print_dev(rd, idx, name);
-	res_print_uint(rd, "mrn", mrn, nla_line[RDMA_NLDEV_ATTR_RES_MRN]);
+	res_print_u32(rd, "mrn", mrn, nla_line[RDMA_NLDEV_ATTR_RES_MRN]);
 	print_key(rd, "rkey", rkey, nla_line[RDMA_NLDEV_ATTR_RES_RKEY]);
 	print_key(rd, "lkey", lkey, nla_line[RDMA_NLDEV_ATTR_RES_LKEY]);
 	print_key(rd, "iova", iova, nla_line[RDMA_NLDEV_ATTR_RES_IOVA]);
-	res_print_uint(rd, "mrlen", mrlen, nla_line[RDMA_NLDEV_ATTR_RES_MRLEN]);
-	res_print_uint(rd, "pdn", pdn, nla_line[RDMA_NLDEV_ATTR_RES_PDN]);
-	res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
+	res_print_u64(rd, "mrlen", mrlen, nla_line[RDMA_NLDEV_ATTR_RES_MRLEN]);
+	res_print_u32(rd, "pdn", pdn, nla_line[RDMA_NLDEV_ATTR_RES_PDN]);
+	res_print_u32(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 	print_comm(rd, comm, nla_line);
 
 	print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]);
@@ -87,8 +90,6 @@ static int res_mr_line(struct rd *rd, co
 	newline(rd);
 
 out:
-	if (nla_line[RDMA_NLDEV_ATTR_RES_PID])
-		free(comm);
 	return MNL_CB_OK;
 }
 
diff -pruN 5.15.0-1/rdma/res-pd.c 5.19.0-1/rdma/res-pd.c
--- 5.15.0-1/rdma/res-pd.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/res-pd.c	2022-08-02 18:36:33.000000000 +0000
@@ -34,8 +34,15 @@ static int res_pd_line(struct rd *rd, co
 			nla_line[RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY]);
 
 	if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) {
+		SPRINT_BUF(b);
+
 		pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
-		comm = get_task_name(pid);
+		if (!get_task_name(pid, b, sizeof(b)))
+			comm = b;
+	} else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) {
+		/* discard const from mnl_attr_get_str */
+		comm = (char *)mnl_attr_get_str(
+			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
 	}
 
 	if (rd_is_filtered_attr(rd, "pid", pid,
@@ -55,29 +62,23 @@ static int res_pd_line(struct rd *rd, co
 				nla_line[RDMA_NLDEV_ATTR_RES_PDN]))
 		goto out;
 
-	if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])
-		/* discard const from mnl_attr_get_str */
-		comm = (char *)mnl_attr_get_str(
-			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
-
 	open_json_object(NULL);
 	print_dev(rd, idx, name);
-	res_print_uint(rd, "pdn", pdn, nla_line[RDMA_NLDEV_ATTR_RES_PDN]);
+	res_print_u32(rd, "pdn", pdn, nla_line[RDMA_NLDEV_ATTR_RES_PDN]);
 	print_key(rd, "local_dma_lkey", local_dma_lkey,
 		  nla_line[RDMA_NLDEV_ATTR_RES_LOCAL_DMA_LKEY]);
-	res_print_uint(rd, "users", users,
+	res_print_u64(rd, "users", users,
 		       nla_line[RDMA_NLDEV_ATTR_RES_USECNT]);
 	print_key(rd, "unsafe_global_rkey", unsafe_global_rkey,
 		  nla_line[RDMA_NLDEV_ATTR_RES_UNSAFE_GLOBAL_RKEY]);
-	res_print_uint(rd, "ctxn", ctxn, nla_line[RDMA_NLDEV_ATTR_RES_CTXN]);
-	res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
+	res_print_u32(rd, "ctxn", ctxn, nla_line[RDMA_NLDEV_ATTR_RES_CTXN]);
+	res_print_u32(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 	print_comm(rd, comm, nla_line);
 
 	print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]);
 	newline(rd);
 
-out:	if (nla_line[RDMA_NLDEV_ATTR_RES_PID])
-		free(comm);
+out:
 	return MNL_CB_OK;
 }
 
diff -pruN 5.15.0-1/rdma/res-qp.c 5.19.0-1/rdma/res-qp.c
--- 5.15.0-1/rdma/res-qp.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/res-qp.c	2022-08-02 18:36:33.000000000 +0000
@@ -146,41 +146,41 @@ static int res_qp_line(struct rd *rd, co
 		goto out;
 
 	if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) {
+		SPRINT_BUF(b);
+
 		pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
-		comm = get_task_name(pid);
+		if (!get_task_name(pid, b, sizeof(b)))
+			comm = b;
+	} else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) {
+		/* discard const from mnl_attr_get_str */
+		comm = (char *)mnl_attr_get_str(
+			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
 	}
 
 	if (rd_is_filtered_attr(rd, "pid", pid,
 				nla_line[RDMA_NLDEV_ATTR_RES_PID]))
 		goto out;
 
-	if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])
-		/* discard const from mnl_attr_get_str */
-		comm = (char *)mnl_attr_get_str(
-			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
-
 	open_json_object(NULL);
 	print_link(rd, idx, name, port, nla_line);
-	res_print_uint(rd, "lqpn", lqpn, nla_line[RDMA_NLDEV_ATTR_RES_LQPN]);
+	res_print_u32(rd, "lqpn", lqpn, nla_line[RDMA_NLDEV_ATTR_RES_LQPN]);
 	print_rqpn(rd, rqpn, nla_line);
 
 	print_type(rd, type);
 	print_state(rd, state);
 
 	print_rqpsn(rd, rq_psn, nla_line);
-	res_print_uint(rd, "sq-psn", sq_psn,
+	res_print_u32(rd, "sq-psn", sq_psn,
 		       nla_line[RDMA_NLDEV_ATTR_RES_SQ_PSN]);
 
 	print_pathmig(rd, path_mig_state, nla_line);
-	res_print_uint(rd, "pdn", pdn, nla_line[RDMA_NLDEV_ATTR_RES_PDN]);
-	res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
+	res_print_u32(rd, "pdn", pdn, nla_line[RDMA_NLDEV_ATTR_RES_PDN]);
+	res_print_u32(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 	print_comm(rd, comm, nla_line);
 
 	print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]);
 	newline(rd);
 out:
-	if (nla_line[RDMA_NLDEV_ATTR_RES_PID])
-		free(comm);
 	return MNL_CB_OK;
 }
 
diff -pruN 5.15.0-1/rdma/res-srq.c 5.19.0-1/rdma/res-srq.c
--- 5.15.0-1/rdma/res-srq.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/res-srq.c	2022-08-02 18:36:33.000000000 +0000
@@ -70,9 +70,8 @@ static int filter_srq_range_qps(struct r
 					 *delimiter, tmp_min_range,
 					 tmp_max_range);
 
-			if (strlen(qp_str) + strlen(tmp) >= MAX_QP_STR_LEN)
-				return -EINVAL;
-			strncat(qp_str, tmp, sizeof(tmp) - 1);
+			strncat(qp_str, tmp,
+				MAX_QP_STR_LEN - strlen(qp_str) - 1);
 
 			memset(tmp, 0, strlen(tmp));
 			*delimiter = ",";
@@ -94,9 +93,7 @@ static int filter_srq_range_qps(struct r
 		snprintf(tmp, sizeof(tmp), "%s%d-%d", *delimiter,
 			 tmp_min_range, tmp_max_range);
 
-	if (strlen(qp_str) + strlen(tmp) >= MAX_QP_STR_LEN)
-		return -EINVAL;
-	strncat(qp_str, tmp, sizeof(tmp) - 1);
+	strncat(qp_str, tmp, MAX_QP_STR_LEN - strlen(qp_str) - 1);
 	*delimiter = ",";
 	return 0;
 }
@@ -137,9 +134,8 @@ static int get_srq_qps(struct rd *rd, st
 					qp_line[RDMA_NLDEV_ATTR_RES_LQPN]))
 				continue;
 			snprintf(tmp, sizeof(tmp), "%s%d", delimiter, qpn);
-			if (strlen(qp_str) + strlen(tmp) >= MAX_QP_STR_LEN)
-				goto out;
-			strncat(qp_str, tmp, sizeof(tmp) - 1);
+			strncat(qp_str, tmp,
+				MAX_QP_STR_LEN - strlen(qp_str) - 1);
 			delimiter = ",";
 		} else if (qp_line[RDMA_NLDEV_ATTR_MIN_RANGE] &&
 			   qp_line[RDMA_NLDEV_ATTR_MAX_RANGE]) {
@@ -178,9 +174,17 @@ static int res_srq_line(struct rd *rd, c
 		return MNL_CB_ERROR;
 
 	if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) {
+		SPRINT_BUF(b);
+
 		pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
-		comm = get_task_name(pid);
+		if (!get_task_name(pid, b, sizeof(b)))
+			comm = b;
+	} else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) {
+		/* discard const from mnl_attr_get_str */
+		comm = (char *)mnl_attr_get_str(
+			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
 	}
+
 	if (rd_is_filtered_attr(rd, "pid", pid,
 				nla_line[RDMA_NLDEV_ATTR_RES_PID]))
 		goto out;
@@ -213,27 +217,20 @@ static int res_srq_line(struct rd *rd, c
 			MNL_CB_OK)
 		goto out;
 
-	if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])
-		/* discard const from mnl_attr_get_str */
-		comm = (char *)mnl_attr_get_str(
-			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
-
 	open_json_object(NULL);
 	print_dev(rd, idx, name);
-	res_print_uint(rd, "srqn", srqn, nla_line[RDMA_NLDEV_ATTR_RES_SRQN]);
+	res_print_u32(rd, "srqn", srqn, nla_line[RDMA_NLDEV_ATTR_RES_SRQN]);
 	print_type(rd, type);
 	print_qps(qp_str);
-	res_print_uint(rd, "pdn", pdn, nla_line[RDMA_NLDEV_ATTR_RES_PDN]);
-	res_print_uint(rd, "cqn", cqn, nla_line[RDMA_NLDEV_ATTR_RES_CQN]);
-	res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
+	res_print_u32(rd, "pdn", pdn, nla_line[RDMA_NLDEV_ATTR_RES_PDN]);
+	res_print_u32(rd, "cqn", cqn, nla_line[RDMA_NLDEV_ATTR_RES_CQN]);
+	res_print_u32(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 	print_comm(rd, comm, nla_line);
 
 	print_driver_table(rd, nla_line[RDMA_NLDEV_ATTR_DRIVER]);
 	newline(rd);
 
 out:
-	if (nla_line[RDMA_NLDEV_ATTR_RES_PID])
-		free(comm);
 	return MNL_CB_OK;
 }
 
diff -pruN 5.15.0-1/rdma/stat.c 5.19.0-1/rdma/stat.c
--- 5.15.0-1/rdma/stat.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/stat.c	2022-08-02 18:36:33.000000000 +0000
@@ -20,6 +20,10 @@ static int stat_help(struct rd *rd)
 	pr_out("       %s statistic OBJECT unbind COUNTER_SCOPE [DEV/PORT_INDEX] [COUNTER-ID]\n", rd->filename);
 	pr_out("       %s statistic show\n", rd->filename);
 	pr_out("       %s statistic show link [ DEV/PORT_INDEX ]\n", rd->filename);
+	pr_out("       %s statistic mode [ supported ]\n", rd->filename);
+	pr_out("       %s statistic mode [ supported ] link [ DEV/PORT_INDEX ]\n", rd->filename);
+	pr_out("       %s statistic set link [ DEV/PORT_INDEX ] optional-counters [ OPTIONAL-COUNTERS ]\n", rd->filename);
+	pr_out("       %s statistic unset link [ DEV/PORT_INDEX ] optional-counters\n", rd->filename);
 	pr_out("where  OBJECT: = { qp }\n");
 	pr_out("       CRITERIA : = { type }\n");
 	pr_out("       COUNTER_SCOPE: = { link | dev }\n");
@@ -37,6 +41,12 @@ static int stat_help(struct rd *rd)
 	pr_out("       %s statistic qp unbind link mlx5_2/1 cntn 4 lqpn 178\n", rd->filename);
 	pr_out("       %s statistic show\n", rd->filename);
 	pr_out("       %s statistic show link mlx5_2/1\n", rd->filename);
+	pr_out("       %s statistic mode\n", rd->filename);
+	pr_out("       %s statistic mode link mlx5_2/1\n", rd->filename);
+	pr_out("       %s statistic mode supported\n", rd->filename);
+	pr_out("       %s statistic mode supported link mlx5_2/1\n", rd->filename);
+	pr_out("       %s statistic set link mlx5_2/1 optional-counters cc_rx_ce_pkts,cc_rx_cnp_pkts\n", rd->filename);
+	pr_out("       %s statistic unset link mlx5_2/1 optional-counters\n", rd->filename);
 
 	return 0;
 }
@@ -200,7 +210,7 @@ int res_get_hwcounters(struct rd *rd, st
 		v = mnl_attr_get_u64(hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE]);
 		if (rd->pretty_output && !rd->json_output)
 			newline_indent(rd);
-		res_print_uint(rd, nm, v, hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
+		res_print_u64(rd, nm, v, hw_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
 	}
 
 	return MNL_CB_OK;
@@ -238,17 +248,21 @@ static int res_counter_line(struct rd *r
 		return MNL_CB_OK;
 
 	if (nla_line[RDMA_NLDEV_ATTR_RES_PID]) {
+		SPRINT_BUF(b);
+
 		pid = mnl_attr_get_u32(nla_line[RDMA_NLDEV_ATTR_RES_PID]);
-		comm = get_task_name(pid);
+		if (!get_task_name(pid, b, sizeof(b)))
+			comm = b;
+	} else if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]) {
+		/* discard const from mnl_attr_get_str */
+		comm = (char *)mnl_attr_get_str(
+			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
 	}
+
 	if (rd_is_filtered_attr(rd, "pid", pid,
 				nla_line[RDMA_NLDEV_ATTR_RES_PID]))
 		return MNL_CB_OK;
 
-	if (nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME])
-		comm = (char *)mnl_attr_get_str(
-			nla_line[RDMA_NLDEV_ATTR_RES_KERN_NAME]);
-
 	mnl_attr_for_each_nested(nla_entry, qp_table) {
 		struct nlattr *qp_line[RDMA_NLDEV_ATTR_MAX] = {};
 
@@ -273,7 +287,7 @@ static int res_counter_line(struct rd *r
 	print_color_uint(PRINT_ANY, COLOR_NONE, "cntn", "cntn %u ", cntn);
 	if (nla_line[RDMA_NLDEV_ATTR_RES_TYPE])
 		print_qp_type(rd, qp_type);
-	res_print_uint(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
+	res_print_u64(rd, "pid", pid, nla_line[RDMA_NLDEV_ATTR_RES_PID]);
 	print_comm(rd, comm, nla_line);
 	res_get_hwcounters(rd, hwc_table, true);
 	isfirst = true;
@@ -493,6 +507,30 @@ static int stat_qp_set(struct rd *rd)
 	return rd_exec_cmd(rd, cmds, "parameter");
 }
 
+static int stat_get_arg_str(struct rd *rd, const char *arg, char **value, bool allow_empty)
+{
+	int len = 0;
+
+	if (strcmpx(rd_argv(rd), arg) != 0) {
+		pr_err("Unknown parameter '%s'.\n", rd_argv(rd));
+		return -EINVAL;
+	}
+
+	rd_arg_inc(rd);
+	if (!rd_no_arg(rd)) {
+		*value = strdup(rd_argv(rd));
+		len = strlen(*value);
+		rd_arg_inc(rd);
+	}
+
+	if ((allow_empty && len) || (!allow_empty && !len)) {
+		stat_help(rd);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int stat_get_arg(struct rd *rd, const char *arg)
 {
 	int value = 0;
@@ -715,6 +753,310 @@ static int stat_qp(struct rd *rd)
 	return rd_exec_cmd(rd, cmds, "parameter");
 }
 
+static int do_stat_mode_parse_cb(const struct nlmsghdr *nlh, void *data,
+				 bool supported)
+{
+	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
+	struct nlattr *nla_entry;
+	const char *dev, *name;
+	struct rd *rd = data;
+	int enabled, err = 0;
+	bool isfirst = true;
+	uint32_t port;
+
+	mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
+	if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME] ||
+	    !tb[RDMA_NLDEV_ATTR_PORT_INDEX] ||
+	    !tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
+		return MNL_CB_ERROR;
+
+	dev = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]);
+	port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]);
+
+	mnl_attr_for_each_nested(nla_entry,
+				 tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
+		struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {};
+
+		err  = mnl_attr_parse_nested(nla_entry, rd_attr_cb, cnt);
+		if ((err != MNL_CB_OK) ||
+		    (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]))
+			return -EINVAL;
+
+		if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
+			continue;
+
+		enabled = mnl_attr_get_u8(cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC]);
+		name = mnl_attr_get_str(cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME]);
+		if (supported || enabled) {
+			if (isfirst) {
+				open_json_object(NULL);
+				print_color_string(PRINT_ANY, COLOR_NONE,
+						   "ifname", "link %s/", dev);
+				print_color_uint(PRINT_ANY, COLOR_NONE, "port",
+						 "%u ", port);
+				if (supported)
+					open_json_array(PRINT_ANY,
+						"supported optional-counters");
+				else
+					open_json_array(PRINT_ANY,
+							"optional-counters");
+				print_color_string(PRINT_FP, COLOR_NONE, NULL,
+						   " ", NULL);
+				isfirst = false;
+			} else {
+				print_color_string(PRINT_FP, COLOR_NONE, NULL,
+						   ",", NULL);
+			}
+			if (rd->pretty_output && !rd->json_output)
+				newline_indent(rd);
+
+			print_color_string(PRINT_ANY, COLOR_NONE, NULL, "%s",
+					   name);
+		}
+	}
+
+	if (!isfirst) {
+		close_json_array(PRINT_JSON, NULL);
+		newline(rd);
+	}
+
+	return 0;
+}
+
+static int stat_mode_parse_cb(const struct nlmsghdr *nlh, void *data)
+{
+	return do_stat_mode_parse_cb(nlh, data, false);
+}
+
+static int stat_mode_parse_cb_supported(const struct nlmsghdr *nlh, void *data)
+{
+	return do_stat_mode_parse_cb(nlh, data, true);
+}
+
+static int stat_one_link_get_status_req(struct rd *rd, uint32_t *seq)
+{
+	int flags = NLM_F_REQUEST | NLM_F_ACK;
+
+	rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_GET_STATUS, seq,  flags);
+	mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
+	mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX, rd->port_idx);
+
+	return rd_send_msg(rd);
+}
+
+static int stat_one_link_get_mode(struct rd *rd)
+{
+	uint32_t seq;
+	int err;
+
+	if (!rd->port_idx)
+		return 0;
+
+	err = stat_one_link_get_status_req(rd, &seq);
+	if (err)
+		return err;
+
+	return rd_recv_msg(rd, stat_mode_parse_cb, rd, seq);
+}
+
+static int stat_one_link_get_mode_supported(struct rd *rd)
+{
+	uint32_t seq;
+	int err;
+
+	if (!rd->port_idx)
+		return 0;
+
+	err = stat_one_link_get_status_req(rd, &seq);
+	if (err)
+		return err;
+
+	return rd_recv_msg(rd, stat_mode_parse_cb_supported, rd, seq);
+}
+
+static int stat_link_get_mode(struct rd *rd)
+{
+	return rd_exec_link(rd, stat_one_link_get_mode, false);
+}
+
+static int stat_link_get_mode_supported(struct rd *rd)
+{
+	return rd_exec_link(rd, stat_one_link_get_mode_supported, false);
+}
+
+static int stat_mode_supported(struct rd *rd)
+{
+	const struct rd_cmd cmds[] = {
+		{ NULL,		stat_link_get_mode_supported },
+		{ "link",	stat_link_get_mode_supported },
+		{ "help",	stat_help },
+		{ 0 },
+	};
+	return rd_exec_cmd(rd, cmds, "parameter");
+}
+
+static int stat_mode(struct rd *rd)
+{
+	const struct rd_cmd cmds[] = {
+		{ NULL,		stat_link_get_mode },
+		{ "link",	stat_link_get_mode },
+		{ "show",	stat_link_get_mode },
+		{ "supported",	stat_mode_supported },
+		{ "help",	stat_help },
+		{ 0 },
+	};
+
+	return rd_exec_cmd(rd, cmds, "parameter");
+}
+
+static int stat_one_set_link_opcounters(const struct nlmsghdr *nlh, void *data)
+{
+	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
+	struct nlattr *nla_entry, *tb_set;
+	int ret, flags = NLM_F_REQUEST | NLM_F_ACK;
+	char *opcnt, *opcnts;
+	struct rd *rd = data;
+	uint32_t seq;
+	bool found;
+
+	mnl_attr_parse(nlh, 0, rd_attr_cb, tb);
+	if (!tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS])
+		return MNL_CB_ERROR;
+
+	if (rd_no_arg(rd)) {
+		stat_help(rd);
+		return -EINVAL;
+	}
+
+	ret = stat_get_arg_str(rd, "optional-counters", &opcnts, false);
+	if (ret)
+		return ret;
+
+	rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &seq, flags);
+	mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX,
+			 rd->dev_idx);
+	mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX,
+			 rd->port_idx);
+
+	tb_set = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
+
+	opcnt = strtok(opcnts, ",");
+	while (opcnt) {
+		found = false;
+		mnl_attr_for_each_nested(nla_entry,
+					 tb[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
+			struct nlattr *cnt[RDMA_NLDEV_ATTR_MAX] = {}, *nm, *id;
+
+			if (mnl_attr_parse_nested(nla_entry, rd_attr_cb,
+						  cnt) != MNL_CB_OK)
+				return -EINVAL;
+
+			nm = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME];
+			id = cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX];
+			if (!nm || ! id)
+				return -EINVAL;
+
+			if (!cnt[RDMA_NLDEV_ATTR_STAT_HWCOUNTER_DYNAMIC])
+				continue;
+
+			if (strcmp(opcnt, mnl_attr_get_str(nm)) == 0) {
+				mnl_attr_put_u32(rd->nlh,
+						 RDMA_NLDEV_ATTR_STAT_HWCOUNTER_INDEX,
+						 mnl_attr_get_u32(id));
+				found = true;
+			}
+		}
+
+		if (!found)
+			return -EINVAL;
+
+		opcnt = strtok(NULL, ",");
+	}
+	mnl_attr_nest_end(rd->nlh, tb_set);
+
+	return rd_sendrecv_msg(rd, seq);
+}
+
+static int stat_one_set_link(struct rd *rd)
+{
+	uint32_t seq;
+	int err;
+
+	if (!rd->port_idx)
+		return 0;
+
+	err = stat_one_link_get_status_req(rd, &seq);
+	if (err)
+		return err;
+
+	return rd_recv_msg(rd, stat_one_set_link_opcounters, rd, seq);
+}
+
+static int stat_set_link(struct rd *rd)
+{
+	return rd_exec_link(rd, stat_one_set_link, true);
+}
+
+static int stat_set(struct rd *rd)
+{
+	const struct rd_cmd cmds[] = {
+		{ NULL,		stat_help },
+		{ "link",	stat_set_link },
+		{ "help",	stat_help },
+		{ 0 },
+	};
+	return rd_exec_cmd(rd, cmds, "parameter");
+}
+
+static int stat_one_unset_link_opcounters(struct rd *rd)
+{
+	int ret, flags = NLM_F_REQUEST | NLM_F_ACK;
+	struct nlattr *tbl;
+	uint32_t seq;
+	char *opcnts;
+
+	if (rd_no_arg(rd)) {
+		stat_help(rd);
+		return -EINVAL;
+	}
+
+	ret = stat_get_arg_str(rd, "optional-counters", &opcnts, true);
+	if (ret)
+		return ret;
+
+	rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET, &seq, flags);
+	mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX,
+			 rd->dev_idx);
+	mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_PORT_INDEX,
+			 rd->port_idx);
+
+	tbl = mnl_attr_nest_start(rd->nlh, RDMA_NLDEV_ATTR_STAT_HWCOUNTERS);
+	mnl_attr_nest_end(rd->nlh, tbl);
+
+	return rd_sendrecv_msg(rd, seq);
+}
+
+static int stat_one_unset_link(struct rd *rd)
+{
+	return stat_one_unset_link_opcounters(rd);
+}
+
+static int stat_unset_link(struct rd *rd)
+{
+	return rd_exec_link(rd, stat_one_unset_link, true);
+}
+
+static int stat_unset(struct rd *rd)
+{
+	const struct rd_cmd cmds[] = {
+		{ NULL,		stat_help },
+		{ "link",	stat_unset_link },
+		{ "help",	stat_help },
+		{ 0 },
+	};
+	return rd_exec_cmd(rd, cmds, "parameter");
+}
+
 static int stat_show_parse_cb(const struct nlmsghdr *nlh, void *data)
 {
 	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
@@ -786,6 +1128,9 @@ int cmd_stat(struct rd *rd)
 		{ "help",	stat_help },
 		{ "qp",		stat_qp },
 		{ "mr",		stat_mr },
+		{ "mode",	stat_mode },
+		{ "set",	stat_set },
+		{ "unset",	stat_unset },
 		{ 0 }
 	};
 
diff -pruN 5.15.0-1/rdma/stat-mr.c 5.19.0-1/rdma/stat-mr.c
--- 5.15.0-1/rdma/stat-mr.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/rdma/stat-mr.c	2022-08-02 18:36:33.000000000 +0000
@@ -22,7 +22,7 @@ static int stat_mr_line(struct rd *rd, c
 
 	open_json_object(NULL);
 	print_dev(rd, idx, name);
-	res_print_uint(rd, "mrn", mrn, nla_line[RDMA_NLDEV_ATTR_RES_MRN]);
+	res_print_u32(rd, "mrn", mrn, nla_line[RDMA_NLDEV_ATTR_RES_MRN]);
 
 	if (nla_line[RDMA_NLDEV_ATTR_STAT_HWCOUNTERS]) {
 		ret = res_get_hwcounters(
diff -pruN 5.15.0-1/tc/em_u32.c 5.19.0-1/tc/em_u32.c
--- 5.15.0-1/tc/em_u32.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/em_u32.c	2022-08-02 18:36:33.000000000 +0000
@@ -84,7 +84,7 @@ static int u32_parse_eopt(struct nlmsghd
 		char buf[a->len - nh_len + 1];
 
 		offmask = -1;
-		memcpy(buf, a->data + nh_len, a->len - nh_len);
+		strncpy(buf, a->data + nh_len, a->len - nh_len + 1);
 		offset = strtoul(buf, NULL, 0);
 	} else if (!bstrcmp(a, "nexthdr+")) {
 		a = bstr_next(a);
diff -pruN 5.15.0-1/tc/f_basic.c 5.19.0-1/tc/f_basic.c
--- 5.15.0-1/tc/f_basic.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/f_basic.c	2022-08-02 18:36:33.000000000 +0000
@@ -70,14 +70,14 @@ static int basic_parse_opt(struct filter
 			continue;
 		} else if (matches(*argv, "classid") == 0 ||
 			   strcmp(*argv, "flowid") == 0) {
-			unsigned int handle;
+			unsigned int classid;
 
 			NEXT_ARG();
-			if (get_tc_classid(&handle, *argv)) {
+			if (get_tc_classid(&classid, *argv)) {
 				fprintf(stderr, "Illegal \"classid\"\n");
 				return -1;
 			}
-			addattr_l(n, MAX_MSG, TCA_BASIC_CLASSID, &handle, 4);
+			addattr_l(n, MAX_MSG, TCA_BASIC_CLASSID, &classid, 4);
 		} else if (matches(*argv, "action") == 0) {
 			NEXT_ARG();
 			if (parse_action(&argc, &argv, TCA_BASIC_ACT, n)) {
diff -pruN 5.15.0-1/tc/f_bpf.c 5.19.0-1/tc/f_bpf.c
--- 5.15.0-1/tc/f_bpf.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/f_bpf.c	2022-08-02 18:36:33.000000000 +0000
@@ -126,14 +126,14 @@ opt_bpf:
 			bpf_uds_name = cfg.uds;
 		} else if (matches(*argv, "classid") == 0 ||
 			   matches(*argv, "flowid") == 0) {
-			unsigned int handle;
+			unsigned int classid;
 
 			NEXT_ARG();
-			if (get_tc_classid(&handle, *argv)) {
+			if (get_tc_classid(&classid, *argv)) {
 				fprintf(stderr, "Illegal \"classid\"\n");
 				return -1;
 			}
-			addattr32(n, MAX_MSG, TCA_BPF_CLASSID, handle);
+			addattr32(n, MAX_MSG, TCA_BPF_CLASSID, classid);
 		} else if (matches(*argv, "direct-action") == 0 ||
 			   matches(*argv, "da") == 0) {
 			bpf_flags |= TCA_BPF_FLAG_ACT_DIRECT;
diff -pruN 5.15.0-1/tc/f_flower.c 5.19.0-1/tc/f_flower.c
--- 5.15.0-1/tc/f_flower.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/f_flower.c	2022-08-02 18:36:33.000000000 +0000
@@ -48,6 +48,7 @@ static void explain(void)
 		"\n"
 		"Where: MATCH-LIST := [ MATCH-LIST ] MATCH\n"
 		"       MATCH      := {	indev DEV-NAME |\n"
+		"			num_of_vlans VLANS_COUNT |\n"
 		"			vlan_id VID |\n"
 		"			vlan_prio PRIORITY |\n"
 		"			vlan_ethtype [ ipv4 | ipv6 | ETH-TYPE ] |\n"
@@ -84,7 +85,8 @@ static void explain(void)
 		"			geneve_opts MASKED-OPTIONS |\n"
 		"			vxlan_opts MASKED-OPTIONS |\n"
 		"                       erspan_opts MASKED-OPTIONS |\n"
-		"			ip_flags IP-FLAGS | \n"
+		"			gtp_opts MASKED-OPTIONS |\n"
+		"			ip_flags IP-FLAGS |\n"
 		"			enc_dst_port [ port_number ] |\n"
 		"			ct_state MASKED_CT_STATE |\n"
 		"			ct_label MASKED_CT_LABEL |\n"
@@ -102,6 +104,19 @@ static void explain(void)
 		"	to specify different mask, he has to use different prio.\n");
 }
 
+/* prints newline, two spaces, name/value */
+static void print_indent_name_value(const char *name, const char *value)
+{
+	print_string(PRINT_FP, NULL, "%s  ", _SL_);
+	print_string_name_value(name, value);
+}
+
+static void print_uint_indent_name_value(const char *name, unsigned int value)
+{
+	print_string(PRINT_FP, NULL, "  ", NULL);
+	print_uint_name_value(name, value);
+}
+
 static int flower_parse_eth_addr(char *str, int addr_type, int mask_type,
 				 struct nlmsghdr *n)
 {
@@ -118,7 +133,7 @@ static int flower_parse_eth_addr(char *s
 	addattr_l(n, MAX_MSG, addr_type, addr, sizeof(addr));
 
 	if (slash) {
-		unsigned bits;
+		unsigned int bits;
 
 		if (!get_unsigned(&bits, slash + 1, 10)) {
 			uint64_t mask;
@@ -145,21 +160,23 @@ err:
 	return err;
 }
 
-static bool eth_type_vlan(__be16 ethertype)
+static bool eth_type_vlan(__be16 ethertype, bool good_num_of_vlans)
 {
 	return ethertype == htons(ETH_P_8021Q) ||
-	       ethertype == htons(ETH_P_8021AD);
+	       ethertype == htons(ETH_P_8021AD) ||
+	       good_num_of_vlans;
 }
 
 static int flower_parse_vlan_eth_type(char *str, __be16 eth_type, int type,
 				      __be16 *p_vlan_eth_type,
-				      struct nlmsghdr *n)
+				      struct nlmsghdr *n, bool good_num_of_vlans)
 {
 	__be16 vlan_eth_type;
 
-	if (!eth_type_vlan(eth_type)) {
-		fprintf(stderr, "Can't set \"%s\" if ethertype isn't 802.1Q or 802.1AD\n",
-			type == TCA_FLOWER_KEY_VLAN_ETH_TYPE ? "vlan_ethtype" : "cvlan_ethtype");
+	if (!eth_type_vlan(eth_type, good_num_of_vlans)) {
+		fprintf(stderr, "Can't set \"%s\" if ethertype isn't 802.1Q or 802.1AD and num_of_vlans %s\n",
+			type == TCA_FLOWER_KEY_VLAN_ETH_TYPE ? "vlan_ethtype" : "cvlan_ethtype",
+			type == TCA_FLOWER_KEY_VLAN_ETH_TYPE ? "is 0" : "less than 2");
 		return -1;
 	}
 
@@ -529,8 +546,7 @@ static int flower_parse_u8(char *str, in
 		ret = get_u8(&mask, slash + 1, 10);
 		if (ret)
 			goto err;
-	}
-	else {
+	} else {
 		mask = UINT8_MAX;
 	}
 
@@ -1022,6 +1038,52 @@ static int flower_parse_erspan_opt(char
 	return 0;
 }
 
+static int flower_parse_gtp_opt(char *str, struct nlmsghdr *n)
+{
+	struct rtattr *nest;
+	char *token;
+	int arg, err;
+
+	nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS_GTP | NLA_F_NESTED);
+
+	token = strsep(&str, ":");
+	for (arg = 1; arg <= TCA_FLOWER_KEY_ENC_OPT_GTP_MAX; arg++) {
+		switch (arg) {
+		case TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE:
+		{
+			__u8 pdu_type;
+
+			if (!strlen(token))
+				break;
+			err = get_u8(&pdu_type, token, 16);
+			if (err)
+				return err;
+			addattr8(n, MAX_MSG, arg, pdu_type);
+			break;
+		}
+		case TCA_FLOWER_KEY_ENC_OPT_GTP_QFI:
+		{
+			__u8 qfi;
+
+			if (!strlen(token))
+				break;
+			err = get_u8(&qfi, token, 16);
+			if (err)
+				return err;
+			addattr8(n, MAX_MSG, arg, qfi);
+			break;
+		}
+		default:
+			fprintf(stderr, "Unknown \"gtp_opts\" type\n");
+			return -1;
+		}
+		token = strsep(&str, ":");
+	}
+	addattr_nest_end(n, nest);
+
+	return 0;
+}
+
 static int flower_parse_geneve_opts(char *str, struct nlmsghdr *n)
 {
 	char *token;
@@ -1205,6 +1267,41 @@ static int flower_parse_enc_opts_erspan(
 	return 0;
 }
 
+static int flower_parse_enc_opts_gtp(char *str, struct nlmsghdr *n)
+{
+	char key[XATTR_SIZE_MAX], mask[XATTR_SIZE_MAX];
+	struct rtattr *nest;
+	char *slash;
+	int err;
+
+	slash = strchr(str, '/');
+	if (slash) {
+		*slash++ = '\0';
+		if (strlen(slash) > XATTR_SIZE_MAX)
+			return -1;
+		strcpy(mask, slash);
+	} else
+		strcpy(mask, "ff:ff");
+
+	if (strlen(str) > XATTR_SIZE_MAX)
+		return -1;
+	strcpy(key, str);
+
+	nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS | NLA_F_NESTED);
+	err = flower_parse_gtp_opt(key, n);
+	if (err)
+		return err;
+	addattr_nest_end(n, nest);
+
+	nest = addattr_nest(n, MAX_MSG, TCA_FLOWER_KEY_ENC_OPTS_MASK | NLA_F_NESTED);
+	err = flower_parse_gtp_opt(mask, n);
+	if (err)
+		return err;
+	addattr_nest_end(n, nest);
+
+	return 0;
+}
+
 static int flower_parse_mpls_lse(int *argc_p, char ***argv_p,
 				 struct nlmsghdr *nlh)
 {
@@ -1330,6 +1427,7 @@ static int flower_parse_opt(struct filte
 	__be16 tc_proto = TC_H_MIN(t->tcm_info);
 	__be16 eth_type = tc_proto;
 	__be16 vlan_ethtype = 0;
+	__u8 num_of_vlans = 0;
 	__u8 ip_proto = 0xff;
 	__u32 flags = 0;
 	__u32 mtf = 0;
@@ -1354,17 +1452,17 @@ static int flower_parse_opt(struct filte
 	while (argc > 0) {
 		if (matches(*argv, "classid") == 0 ||
 		    matches(*argv, "flowid") == 0) {
-			unsigned int handle;
+			unsigned int classid;
 
 			NEXT_ARG();
-			ret = get_tc_classid(&handle, *argv);
+			ret = get_tc_classid(&classid, *argv);
 			if (ret) {
 				fprintf(stderr, "Illegal \"classid\"\n");
 				return -1;
 			}
-			addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, 4);
+			addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &classid, 4);
 		} else if (matches(*argv, "hw_tc") == 0) {
-			unsigned int handle;
+			unsigned int classid;
 			__u32 tc;
 			char *end;
 
@@ -1378,10 +1476,10 @@ static int flower_parse_opt(struct filte
 				fprintf(stderr, "TC index exceeds max range\n");
 				return -1;
 			}
-			handle = TC_H_MAKE(TC_H_MAJ(t->tcm_parent),
+			classid = TC_H_MAKE(TC_H_MAJ(t->tcm_parent),
 					   TC_H_MIN(tc + TC_H_MIN_PRIORITY));
-			addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle,
-				  sizeof(handle));
+			addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &classid,
+				  sizeof(classid));
 		} else if (matches(*argv, "ip_flags") == 0) {
 			NEXT_ARG();
 			ret = flower_parse_matching_flags(*argv,
@@ -1431,12 +1529,22 @@ static int flower_parse_opt(struct filte
 			if (check_ifname(*argv))
 				invarg("\"indev\" not a valid ifname", *argv);
 			addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, *argv);
+		} else if (strcmp(*argv, "num_of_vlans") == 0) {
+			NEXT_ARG();
+			ret = get_u8(&num_of_vlans, *argv, 10);
+			if (ret < 0) {
+				fprintf(stderr, "Illegal \"num_of_vlans\"\n");
+				return -1;
+			}
+			addattr8(n, MAX_MSG,
+				 TCA_FLOWER_KEY_NUM_OF_VLANS, num_of_vlans);
 		} else if (matches(*argv, "vlan_id") == 0) {
 			__u16 vid;
 
 			NEXT_ARG();
-			if (!eth_type_vlan(tc_proto)) {
-				fprintf(stderr, "Can't set \"vlan_id\" if ethertype isn't 802.1Q or 802.1AD\n");
+			if (!eth_type_vlan(tc_proto, num_of_vlans > 0)) {
+				fprintf(stderr, "Can't set \"vlan_id\" if ethertype isn't 802.1Q or 802.1AD"
+						" and num_of_vlans is 0\n");
 				return -1;
 			}
 			ret = get_u16(&vid, *argv, 10);
@@ -1449,8 +1557,9 @@ static int flower_parse_opt(struct filte
 			__u8 vlan_prio;
 
 			NEXT_ARG();
-			if (!eth_type_vlan(tc_proto)) {
-				fprintf(stderr, "Can't set \"vlan_prio\" if ethertype isn't 802.1Q or 802.1AD\n");
+			if (!eth_type_vlan(tc_proto, num_of_vlans > 0)) {
+				fprintf(stderr, "Can't set \"vlan_prio\" if ethertype isn't 802.1Q or 802.1AD"
+						" and num_of_vlans is 0\n");
 				return -1;
 			}
 			ret = get_u8(&vlan_prio, *argv, 10);
@@ -1464,7 +1573,7 @@ static int flower_parse_opt(struct filte
 			NEXT_ARG();
 			ret = flower_parse_vlan_eth_type(*argv, eth_type,
 						 TCA_FLOWER_KEY_VLAN_ETH_TYPE,
-						 &vlan_ethtype, n);
+						 &vlan_ethtype, n, num_of_vlans > 0);
 			if (ret < 0)
 				return -1;
 			/* get new ethtype for later parsing  */
@@ -1473,8 +1582,9 @@ static int flower_parse_opt(struct filte
 			__u16 vid;
 
 			NEXT_ARG();
-			if (!eth_type_vlan(vlan_ethtype)) {
-				fprintf(stderr, "Can't set \"cvlan_id\" if inner vlan ethertype isn't 802.1Q or 802.1AD\n");
+			if (!eth_type_vlan(vlan_ethtype, num_of_vlans > 1)) {
+				fprintf(stderr, "Can't set \"cvlan_id\" if inner vlan ethertype isn't 802.1Q or 802.1AD"
+						" and num_of_vlans is less than 2\n");
 				return -1;
 			}
 			ret = get_u16(&vid, *argv, 10);
@@ -1487,8 +1597,9 @@ static int flower_parse_opt(struct filte
 			__u8 cvlan_prio;
 
 			NEXT_ARG();
-			if (!eth_type_vlan(vlan_ethtype)) {
-				fprintf(stderr, "Can't set \"cvlan_prio\" if inner vlan ethertype isn't 802.1Q or 802.1AD\n");
+			if (!eth_type_vlan(vlan_ethtype, num_of_vlans > 1)) {
+				fprintf(stderr, "Can't set \"cvlan_prio\" if inner vlan ethertype isn't 802.1Q or 802.1AD"
+						" and num_of_vlans is less than 2\n");
 				return -1;
 			}
 			ret = get_u8(&cvlan_prio, *argv, 10);
@@ -1503,7 +1614,7 @@ static int flower_parse_opt(struct filte
 			/* get new ethtype for later parsing */
 			ret = flower_parse_vlan_eth_type(*argv, vlan_ethtype,
 						 TCA_FLOWER_KEY_CVLAN_ETH_TYPE,
-						 &eth_type, n);
+						 &eth_type, n, num_of_vlans > 1);
 			if (ret < 0)
 				return -1;
 		} else if (matches(*argv, "mpls") == 0) {
@@ -1857,6 +1968,13 @@ static int flower_parse_opt(struct filte
 				fprintf(stderr, "Illegal \"erspan_opts\"\n");
 				return -1;
 			}
+		} else if (!strcmp(*argv, "gtp_opts")) {
+			NEXT_ARG();
+			ret = flower_parse_enc_opts_gtp(*argv, n);
+			if (ret < 0) {
+				fprintf(stderr, "Illegal \"gtp_opts\"\n");
+				return -1;
+			}
 		} else if (matches(*argv, "action") == 0) {
 			NEXT_ARG();
 			ret = parse_action(&argc, &argv, TCA_FLOWER_ACT, n);
@@ -1865,11 +1983,9 @@ static int flower_parse_opt(struct filte
 				return -1;
 			}
 			continue;
-		} else if (strcmp(*argv, "help") == 0) {
-			explain();
-			return -1;
 		} else {
-			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			if (strcmp(*argv, "help") != 0)
+				fprintf(stderr, "What is \"%s\"?\n", *argv);
 			explain();
 			return -1;
 		}
@@ -1917,7 +2033,7 @@ static int __mask_bits(char *addr, size_
 				bits++;
 			} else if (bits) {
 				hole = true;
-			} else{
+			} else {
 				return -1;
 			}
 		}
@@ -1925,10 +2041,9 @@ static int __mask_bits(char *addr, size_
 	return bits;
 }
 
-static void flower_print_eth_addr(char *name, struct rtattr *addr_attr,
+static void flower_print_eth_addr(const char *name, struct rtattr *addr_attr,
 				  struct rtattr *mask_attr)
 {
-	SPRINT_BUF(namefrm);
 	SPRINT_BUF(out);
 	SPRINT_BUF(b1);
 	size_t done;
@@ -1949,9 +2064,7 @@ static void flower_print_eth_addr(char *
 			sprintf(out + done, "/%d", bits);
 	}
 
-	print_nl();
-	sprintf(namefrm, "  %s %%s", name);
-	print_string(PRINT_ANY, name, namefrm, out);
+	print_indent_name_value(name, out);
 }
 
 static void flower_print_eth_type(__be16 *p_eth_type,
@@ -2064,7 +2177,6 @@ static void flower_print_ip_addr(char *n
 {
 	struct rtattr *addr_attr;
 	struct rtattr *mask_attr;
-	SPRINT_BUF(namefrm);
 	SPRINT_BUF(out);
 	size_t done;
 	int family;
@@ -2095,10 +2207,9 @@ static void flower_print_ip_addr(char *n
 	else if (bits < len * 8)
 		sprintf(out + done, "/%d", bits);
 
-	print_nl();
-	sprintf(namefrm, "  %s %%s", name);
-	print_string(PRINT_ANY, name, namefrm, out);
+	print_indent_name_value(name, out);
 }
+
 static void flower_print_ip4_addr(char *name, struct rtattr *addr_attr,
 				  struct rtattr *mask_attr)
 {
@@ -2124,22 +2235,18 @@ static void flower_print_port_range(char
 		print_hu(PRINT_JSON, "end", NULL, rta_getattr_be16(max_attr));
 		close_json_object();
 	} else {
-		SPRINT_BUF(namefrm);
 		SPRINT_BUF(out);
 		size_t done;
 
 		done = sprintf(out, "%u", rta_getattr_be16(min_attr));
 		sprintf(out + done, "-%u", rta_getattr_be16(max_attr));
-		print_nl();
-		sprintf(namefrm, "  %s %%s", name);
-		print_string(PRINT_ANY, name, namefrm, out);
+		print_indent_name_value(name, out);
 	}
 }
 
 static void flower_print_tcp_flags(const char *name, struct rtattr *flags_attr,
 				   struct rtattr *mask_attr)
 {
-	SPRINT_BUF(namefrm);
 	SPRINT_BUF(out);
 	size_t done;
 
@@ -2150,9 +2257,7 @@ static void flower_print_tcp_flags(const
 	if (mask_attr)
 		sprintf(out + done, "/%x", rta_getattr_be16(mask_attr));
 
-	print_nl();
-	sprintf(namefrm, "  %s %%s", name);
-	print_string(PRINT_ANY, name, namefrm, out);
+	print_indent_name_value(name, out);
 }
 
 static void flower_print_ct_state(struct rtattr *flags_attr,
@@ -2195,7 +2300,7 @@ static void flower_print_ct_label(struct
 	const unsigned char *str;
 	bool print_mask = false;
 	int data_len, i;
-	SPRINT_BUF(out);
+	char out[128];
 	char *p;
 
 	if (!attr)
@@ -2239,14 +2344,11 @@ static void flower_print_ct_mark(struct
 
 static void flower_print_key_id(const char *name, struct rtattr *attr)
 {
-	SPRINT_BUF(namefrm);
-
 	if (!attr)
 		return;
 
 	print_nl();
-	sprintf(namefrm, "  %s %%u", name);
-	print_uint(PRINT_ANY, name, namefrm, rta_getattr_be32(attr));
+	print_uint_indent_name_value(name, rta_getattr_be32(attr));
 }
 
 static void flower_print_geneve_opts(const char *name, struct rtattr *attr,
@@ -2342,8 +2444,24 @@ static void flower_print_erspan_opts(con
 	sprintf(strbuf, "%u:%u:%u:%u", ver, idx, dir, hwid);
 }
 
-static void flower_print_enc_parts(const char *name, const char *namefrm,
-				   struct rtattr *attr, char *key, char *mask)
+static void flower_print_gtp_opts(const char *name, struct rtattr *attr,
+				  char *strbuf, int len)
+{
+	struct rtattr *tb[TCA_FLOWER_KEY_ENC_OPT_GTP_MAX + 1];
+	__u8 pdu_type, qfi;
+
+	parse_rtattr(tb, TCA_FLOWER_KEY_ENC_OPT_GTP_MAX, RTA_DATA(attr),
+		     RTA_PAYLOAD(attr));
+
+	pdu_type = rta_getattr_u8(tb[TCA_FLOWER_KEY_ENC_OPT_GTP_PDU_TYPE]);
+	qfi = rta_getattr_u8(tb[TCA_FLOWER_KEY_ENC_OPT_GTP_QFI]);
+
+	snprintf(strbuf, len, "%02x:%02x", pdu_type, qfi);
+}
+
+static void __attribute__((format(printf, 2, 0)))
+flower_print_enc_parts(const char *name, const char *namefrm,
+		       struct rtattr *attr, char *key, char *mask)
 {
 	char *key_token, *mask_token, *out;
 	int len;
@@ -2373,15 +2491,18 @@ static void flower_print_enc_opts(const
 	struct rtattr *key_tb[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1];
 	struct rtattr *msk_tb[TCA_FLOWER_KEY_ENC_OPTS_MAX + 1];
 	char *key, *msk;
+	int len;
 
 	if (!attr)
 		return;
 
-	key = malloc(RTA_PAYLOAD(attr) * 2 + 1);
+	len = RTA_PAYLOAD(attr) * 2 + 1;
+
+	key = malloc(len);
 	if (!key)
 		return;
 
-	msk = malloc(RTA_PAYLOAD(attr) * 2 + 1);
+	msk = malloc(len);
 	if (!msk)
 		goto err_key_free;
 
@@ -2418,6 +2539,18 @@ static void flower_print_enc_opts(const
 
 		flower_print_enc_parts(name, "  erspan_opts %s", attr, key,
 				       msk);
+	} else if (key_tb[TCA_FLOWER_KEY_ENC_OPTS_GTP]) {
+		flower_print_gtp_opts("gtp_opt_key",
+				      key_tb[TCA_FLOWER_KEY_ENC_OPTS_GTP],
+				      key, len);
+
+		if (msk_tb[TCA_FLOWER_KEY_ENC_OPTS_GTP])
+			flower_print_gtp_opts("gtp_opt_mask",
+					      msk_tb[TCA_FLOWER_KEY_ENC_OPTS_GTP],
+					      msk, len);
+
+		flower_print_enc_parts(name, "  gtp_opts %s", attr, key,
+				       msk);
 	}
 
 	free(msk);
@@ -2431,7 +2564,6 @@ static void flower_print_masked_u8(const
 {
 	const char *value_str = NULL;
 	__u8 value, mask;
-	SPRINT_BUF(namefrm);
 	SPRINT_BUF(out);
 	size_t done;
 
@@ -2451,9 +2583,7 @@ static void flower_print_masked_u8(const
 	if (mask != UINT8_MAX)
 		sprintf(out + done, "/%d", mask);
 
-	print_nl();
-	sprintf(namefrm, "  %s %%s", name);
-	print_string(PRINT_ANY, name, namefrm, out);
+	print_indent_name_value(name, out);
 }
 
 static void flower_print_u8(const char *name, struct rtattr *attr)
@@ -2463,14 +2593,11 @@ static void flower_print_u8(const char *
 
 static void flower_print_u32(const char *name, struct rtattr *attr)
 {
-	SPRINT_BUF(namefrm);
-
 	if (!attr)
 		return;
 
 	print_nl();
-	sprintf(namefrm, "  %s %%u", name);
-	print_uint(PRINT_ANY, name, namefrm, rta_getattr_u32(attr));
+	print_uint_indent_name_value(name, rta_getattr_u32(attr));
 }
 
 static void flower_print_mpls_opt_lse(struct rtattr *lse)
@@ -2584,6 +2711,14 @@ static int flower_print_opt(struct filte
 
 	open_json_object("keys");
 
+	if (tb[TCA_FLOWER_KEY_NUM_OF_VLANS]) {
+		struct rtattr *attr = tb[TCA_FLOWER_KEY_NUM_OF_VLANS];
+
+		print_nl();
+		print_uint(PRINT_ANY, "num_of_vlans", "  num_of_vlans %d",
+			   rta_getattr_u8(attr));
+	}
+
 	if (tb[TCA_FLOWER_KEY_VLAN_ID]) {
 		struct rtattr *attr = tb[TCA_FLOWER_KEY_VLAN_ID];
 
@@ -2780,8 +2915,7 @@ static int flower_print_opt(struct filte
 				print_uint(PRINT_ANY, "in_hw_count",
 					   " in_hw_count %u", count);
 			}
-		}
-		else if (flags & TCA_CLS_FLAGS_NOT_IN_HW) {
+		} else if (flags & TCA_CLS_FLAGS_NOT_IN_HW) {
 			print_nl();
 			print_bool(PRINT_ANY, "not_in_hw", "  not_in_hw", true);
 		}
diff -pruN 5.15.0-1/tc/f_fw.c 5.19.0-1/tc/f_fw.c
--- 5.15.0-1/tc/f_fw.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/f_fw.c	2022-08-02 18:36:33.000000000 +0000
@@ -70,14 +70,14 @@ static int fw_parse_opt(struct filter_ut
 	while (argc > 0) {
 		if (matches(*argv, "classid") == 0 ||
 		    matches(*argv, "flowid") == 0) {
-			unsigned int handle;
+			unsigned int classid;
 
 			NEXT_ARG();
-			if (get_tc_classid(&handle, *argv)) {
+			if (get_tc_classid(&classid, *argv)) {
 				fprintf(stderr, "Illegal \"classid\"\n");
 				return -1;
 			}
-			addattr_l(n, 4096, TCA_FW_CLASSID, &handle, 4);
+			addattr_l(n, 4096, TCA_FW_CLASSID, &classid, 4);
 		} else if (matches(*argv, "police") == 0) {
 			NEXT_ARG();
 			if (parse_police(&argc, &argv, TCA_FW_POLICE, n)) {
diff -pruN 5.15.0-1/tc/f_matchall.c 5.19.0-1/tc/f_matchall.c
--- 5.15.0-1/tc/f_matchall.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/f_matchall.c	2022-08-02 18:36:33.000000000 +0000
@@ -63,14 +63,14 @@ static int matchall_parse_opt(struct fil
 	while (argc > 0) {
 		if (matches(*argv, "classid") == 0 ||
 			   strcmp(*argv, "flowid") == 0) {
-			unsigned int handle;
+			unsigned int classid;
 
 			NEXT_ARG();
-			if (get_tc_classid(&handle, *argv)) {
+			if (get_tc_classid(&classid, *argv)) {
 				fprintf(stderr, "Illegal \"classid\"\n");
 				return -1;
 			}
-			addattr_l(n, MAX_MSG, TCA_MATCHALL_CLASSID, &handle, 4);
+			addattr_l(n, MAX_MSG, TCA_MATCHALL_CLASSID, &classid, 4);
 		} else if (matches(*argv, "action") == 0) {
 			NEXT_ARG();
 			if (parse_action(&argc, &argv, TCA_MATCHALL_ACT, n)) {
diff -pruN 5.15.0-1/tc/f_route.c 5.19.0-1/tc/f_route.c
--- 5.15.0-1/tc/f_route.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/f_route.c	2022-08-02 18:36:33.000000000 +0000
@@ -91,14 +91,14 @@ static int route_parse_opt(struct filter
 			fh |= (0x8000|id)<<16;
 		} else if (matches(*argv, "classid") == 0 ||
 			   strcmp(*argv, "flowid") == 0) {
-			unsigned int handle;
+			unsigned int classid;
 
 			NEXT_ARG();
-			if (get_tc_classid(&handle, *argv)) {
+			if (get_tc_classid(&classid, *argv)) {
 				fprintf(stderr, "Illegal \"classid\"\n");
 				return -1;
 			}
-			addattr_l(n, 4096, TCA_ROUTE4_CLASSID, &handle, 4);
+			addattr_l(n, 4096, TCA_ROUTE4_CLASSID, &classid, 4);
 		} else if (matches(*argv, "police") == 0) {
 			NEXT_ARG();
 			if (parse_police(&argc, &argv, TCA_ROUTE4_POLICE, n)) {
diff -pruN 5.15.0-1/tc/f_rsvp.c 5.19.0-1/tc/f_rsvp.c
--- 5.15.0-1/tc/f_rsvp.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/f_rsvp.c	2022-08-02 18:36:33.000000000 +0000
@@ -230,14 +230,14 @@ static int rsvp_parse_opt(struct filter_
 			pinfo_ok++;
 		} else if (matches(*argv, "classid") == 0 ||
 			   strcmp(*argv, "flowid") == 0) {
-			unsigned int handle;
+			unsigned int classid;
 
 			NEXT_ARG();
-			if (get_tc_classid(&handle, *argv)) {
+			if (get_tc_classid(&classid, *argv)) {
 				fprintf(stderr, "Illegal \"classid\"\n");
 				return -1;
 			}
-			addattr_l(n, 4096, TCA_RSVP_CLASSID, &handle, 4);
+			addattr_l(n, 4096, TCA_RSVP_CLASSID, &classid, 4);
 		} else if (strcmp(*argv, "tunnelid") == 0) {
 			unsigned int tid;
 
diff -pruN 5.15.0-1/tc/f_u32.c 5.19.0-1/tc/f_u32.c
--- 5.15.0-1/tc/f_u32.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/f_u32.c	2022-08-02 18:36:33.000000000 +0000
@@ -109,7 +109,7 @@ static char *sprint_u32_handle(__u32 han
 		}
 	}
 	if (show_raw)
-		snprintf(b, bsize, "[%08x] ", handle);
+		snprintf(b, bsize, "[%08x]", handle);
 	return buf;
 }
 
@@ -824,23 +824,27 @@ static void print_ipv4(FILE *f, const st
 {
 	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 st
 			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 st
 	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 st
 			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 st
 	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 {
@@ -1213,11 +1250,11 @@ static int u32_print_opt(struct filter_u
 
 	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 +1264,15 @@ static int u32_print_opt(struct filter_u
 	}
 
 	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 +1281,27 @@ static int u32_print_opt(struct filter_u
 			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 +1312,10 @@ static int u32_print_opt(struct filter_u
 		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 +1323,10 @@ static int u32_print_opt(struct filter_u
 		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 +1337,45 @@ static int u32_print_opt(struct filter_u
 			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])
diff -pruN 5.15.0-1/tc/m_action.c 5.19.0-1/tc/m_action.c
--- 5.15.0-1/tc/m_action.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/m_action.c	2022-08-02 18:36:33.000000000 +0000
@@ -51,9 +51,10 @@ static void act_usage(void)
 		"	FL := ls | list | flush | <ACTNAMESPEC>\n"
 		"	ACTNAMESPEC :=  action <ACTNAME>\n"
 		"	ACTISPEC := <ACTNAMESPEC> <INDEXSPEC>\n"
-		"	ACTSPEC := action <ACTDETAIL> [INDEXSPEC] [HWSTATSSPEC]\n"
+		"	ACTSPEC := action <ACTDETAIL> [INDEXSPEC] [HWSTATSSPEC] [SKIPSPEC]\n"
 		"	INDEXSPEC := index <32 bit indexvalue>\n"
 		"	HWSTATSSPEC := hw_stats [ immediate | delayed | disabled ]\n"
+		"	SKIPSPEC := [ skip_sw | skip_hw ]\n"
 		"	ACTDETAIL := <ACTNAME> <ACTPARAMS>\n"
 		"		Example ACTNAME is gact, mirred, bpf, etc\n"
 		"		Each action has its own parameters (ACTPARAMS)\n"
@@ -245,6 +246,8 @@ int parse_action(int *argc_p, char ***ar
 			goto done0;
 		} else {
 			struct action_util *a = NULL;
+			int skip_loop = 2;
+			__u32 flag = 0;
 
 			if (!action_a2n(*argv, NULL, false))
 				strncpy(k, "gact", sizeof(k) - 1);
@@ -314,13 +317,27 @@ done0:
 			}
 
 			if (*argv && strcmp(*argv, "no_percpu") == 0) {
+				flag |= TCA_ACT_FLAGS_NO_PERCPU_STATS;
+				NEXT_ARG_FWD();
+			}
+
+			/* we need to parse twice to fix skip flag out of order */
+			while (skip_loop--) {
+				if (*argv && strcmp(*argv, "skip_sw") == 0) {
+					flag |= TCA_ACT_FLAGS_SKIP_SW;
+					NEXT_ARG_FWD();
+				} else if (*argv && strcmp(*argv, "skip_hw") == 0) {
+					flag |= TCA_ACT_FLAGS_SKIP_HW;
+					NEXT_ARG_FWD();
+				}
+			}
+
+			if (flag) {
 				struct nla_bitfield32 flags =
-					{ TCA_ACT_FLAGS_NO_PERCPU_STATS,
-					  TCA_ACT_FLAGS_NO_PERCPU_STATS };
+					{ flag, flag };
 
 				addattr_l(n, MAX_MSG, TCA_ACT_FLAGS, &flags,
 					  sizeof(struct nla_bitfield32));
-				NEXT_ARG_FWD();
 			}
 
 			addattr_nest_end(n, tail);
@@ -347,7 +364,7 @@ bad_val:
 	return -1;
 }
 
-static int tc_print_one_action(FILE *f, struct rtattr *arg)
+static int tc_print_one_action(FILE *f, struct rtattr *arg, bool bind)
 {
 
 	struct rtattr *tb[TCA_ACT_MAX + 1];
@@ -396,14 +413,52 @@ static int tc_print_one_action(FILE *f,
 					   strsz, b1, sizeof(b1)));
 		print_nl();
 	}
-	if (tb[TCA_ACT_FLAGS]) {
-		struct nla_bitfield32 *flags = RTA_DATA(tb[TCA_ACT_FLAGS]);
+	if (tb[TCA_ACT_FLAGS] || tb[TCA_ACT_IN_HW_COUNT]) {
+		bool skip_hw = false;
+		bool newline = false;
+
+		if (tb[TCA_ACT_FLAGS]) {
+			struct nla_bitfield32 *flags = RTA_DATA(tb[TCA_ACT_FLAGS]);
+
+			if (flags->selector & TCA_ACT_FLAGS_NO_PERCPU_STATS) {
+				newline = true;
+				print_bool(PRINT_ANY, "no_percpu", "\tno_percpu",
+					   flags->value &
+					   TCA_ACT_FLAGS_NO_PERCPU_STATS);
+			}
+			if (!bind) {
+				if (flags->selector & TCA_ACT_FLAGS_SKIP_HW) {
+					newline = true;
+					print_bool(PRINT_ANY, "skip_hw", "\tskip_hw",
+						   flags->value &
+						   TCA_ACT_FLAGS_SKIP_HW);
+					skip_hw = !!(flags->value & TCA_ACT_FLAGS_SKIP_HW);
+				}
+				if (flags->selector & TCA_ACT_FLAGS_SKIP_SW) {
+					newline = true;
+					print_bool(PRINT_ANY, "skip_sw", "\tskip_sw",
+						   flags->value &
+						   TCA_ACT_FLAGS_SKIP_SW);
+				}
+			}
+		}
+		if (tb[TCA_ACT_IN_HW_COUNT] && !bind && !skip_hw) {
+			__u32 count = rta_getattr_u32(tb[TCA_ACT_IN_HW_COUNT]);
 
-		if (flags->selector & TCA_ACT_FLAGS_NO_PERCPU_STATS)
-			print_bool(PRINT_ANY, "no_percpu", "\tno_percpu",
-				   flags->value &
-				   TCA_ACT_FLAGS_NO_PERCPU_STATS);
-		print_nl();
+			newline = true;
+			if (count) {
+				print_bool(PRINT_ANY, "in_hw", "\tin_hw",
+					   true);
+				print_uint(PRINT_ANY, "in_hw_count",
+					   " in_hw_count %u", count);
+			} else {
+				print_bool(PRINT_ANY, "not_in_hw",
+					   "\tnot_in_hw", true);
+			}
+		}
+
+		if (newline)
+			print_nl();
 	}
 	if (tb[TCA_ACT_HW_STATS])
 		print_hw_stats(tb[TCA_ACT_HW_STATS], false);
@@ -440,8 +495,9 @@ tc_print_action_flush(FILE *f, const str
 	return 0;
 }
 
-int
-tc_print_action(FILE *f, const struct rtattr *arg, unsigned short tot_acts)
+static int
+tc_dump_action(FILE *f, const struct rtattr *arg, unsigned short tot_acts,
+	       bool bind)
 {
 
 	int i;
@@ -466,10 +522,8 @@ tc_print_action(FILE *f, const struct rt
 			print_nl();
 			print_uint(PRINT_ANY, "order",
 				   "\taction order %u: ", i);
-			if (tc_print_one_action(f, tb[i]) < 0) {
-				print_string(PRINT_FP, NULL,
-					     "Error printing action\n", NULL);
-			}
+			if (tc_print_one_action(f, tb[i], bind) < 0)
+				fprintf(stderr, "Error printing action\n");
 			close_json_object();
 		}
 
@@ -479,6 +533,12 @@ tc_print_action(FILE *f, const struct rt
 	return 0;
 }
 
+int
+tc_print_action(FILE *f, const struct rtattr *arg, unsigned short tot_acts)
+{
+	return tc_dump_action(f, arg, tot_acts, true);
+}
+
 int print_action(struct nlmsghdr *n, void *arg)
 {
 	FILE *fp = (FILE *)arg;
@@ -529,7 +589,7 @@ int print_action(struct nlmsghdr *n, voi
 	}
 
 	open_json_object(NULL);
-	tc_print_action(fp, tb[TCA_ACT_TAB], tot_acts ? *tot_acts:0);
+	tc_dump_action(fp, tb[TCA_ACT_TAB], tot_acts ? *tot_acts:0, false);
 	close_json_object();
 
 	return 0;
diff -pruN 5.15.0-1/tc/m_vlan.c 5.19.0-1/tc/m_vlan.c
--- 5.15.0-1/tc/m_vlan.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/m_vlan.c	2022-08-02 18:36:33.000000000 +0000
@@ -279,8 +279,8 @@ static int print_vlan(struct action_util
 				    ETH_ALEN, 0, b1, sizeof(b1));
 			print_string(PRINT_ANY, "dst_mac", " dst_mac %s", b1);
 		}
-		if (tb[TCA_VLAN_PUSH_ETH_SRC &&
-		       RTA_PAYLOAD(tb[TCA_VLAN_PUSH_ETH_SRC]) == ETH_ALEN]) {
+		if (tb[TCA_VLAN_PUSH_ETH_SRC] &&
+		       RTA_PAYLOAD(tb[TCA_VLAN_PUSH_ETH_SRC]) == ETH_ALEN) {
 			ll_addr_n2a(RTA_DATA(tb[TCA_VLAN_PUSH_ETH_SRC]),
 				    ETH_ALEN, 0, b1, sizeof(b1));
 			print_string(PRINT_ANY, "src_mac", " src_mac %s", b1);
diff -pruN 5.15.0-1/tc/q_cake.c 5.19.0-1/tc/q_cake.c
--- 5.15.0-1/tc/q_cake.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/q_cake.c	2022-08-02 18:36:33.000000000 +0000
@@ -95,7 +95,7 @@ static int cake_parse_opt(struct qdisc_u
 	bool overhead_override = false;
 	bool overhead_set = false;
 	unsigned int interval = 0;
-	unsigned int diffserv = 0;
+	int diffserv = -1;
 	unsigned int memlimit = 0;
 	unsigned int fwmark = 0;
 	unsigned int target = 0;
@@ -356,7 +356,7 @@ static int cake_parse_opt(struct qdisc_u
 	if (bandwidth || unlimited)
 		addattr_l(n, 1024, TCA_CAKE_BASE_RATE64, &bandwidth,
 			  sizeof(bandwidth));
-	if (diffserv)
+	if (diffserv != -1)
 		addattr_l(n, 1024, TCA_CAKE_DIFFSERV_MODE, &diffserv,
 			  sizeof(diffserv));
 	if (atm != -1)
diff -pruN 5.15.0-1/tc/q_fq_codel.c 5.19.0-1/tc/q_fq_codel.c
--- 5.15.0-1/tc/q_fq_codel.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/q_fq_codel.c	2022-08-02 18:36:33.000000000 +0000
@@ -55,6 +55,7 @@ static void explain(void)
 					"[ target TIME ] [ interval TIME ]\n"
 					"[ quantum BYTES ] [ [no]ecn ]\n"
 					"[ ce_threshold TIME ]\n"
+					"[ ce_threshold_selector VALUE/MASK ]\n"
 					"[ drop_batch SIZE ]\n");
 }
 
@@ -69,6 +70,8 @@ static int fq_codel_parse_opt(struct qdi
 	unsigned int quantum = 0;
 	unsigned int ce_threshold = ~0U;
 	unsigned int memory = ~0U;
+	__u8 ce_threshold_mask = 0;
+	__u8 ce_threshold_selector = 0xFF;
 	int ecn = -1;
 	struct rtattr *tail;
 
@@ -109,6 +112,24 @@ static int fq_codel_parse_opt(struct qdi
 				fprintf(stderr, "Illegal \"ce_threshold\"\n");
 				return -1;
 			}
+		} else if (strcmp(*argv, "ce_threshold_selector") == 0) {
+			char *sep;
+
+			NEXT_ARG();
+			sep = strchr(*argv, '/');
+			if (!sep) {
+				fprintf(stderr, "Missing mask for \"ce_threshold_selector\"\n");
+				return -1;
+			}
+			*sep++ = '\0';
+			if (get_u8(&ce_threshold_mask, sep, 0)) {
+				fprintf(stderr, "Illegal mask for \"ce_threshold_selector\"\n");
+				return -1;
+			}
+			if (get_u8(&ce_threshold_selector, *argv, 0)) {
+				fprintf(stderr, "Illegal \"ce_threshold_selector\"\n");
+				return -1;
+			}
 		} else if (strcmp(*argv, "memory_limit") == 0) {
 			NEXT_ARG();
 			if (get_size(&memory, *argv)) {
@@ -152,6 +173,10 @@ static int fq_codel_parse_opt(struct qdi
 	if (ce_threshold != ~0U)
 		addattr_l(n, 1024, TCA_FQ_CODEL_CE_THRESHOLD,
 			  &ce_threshold, sizeof(ce_threshold));
+	if (ce_threshold_selector != 0xFF) {
+		addattr8(n, 1024, TCA_FQ_CODEL_CE_THRESHOLD_MASK, ce_threshold_mask);
+		addattr8(n, 1024, TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR, ce_threshold_selector);
+	}
 	if (memory != ~0U)
 		addattr_l(n, 1024, TCA_FQ_CODEL_MEMORY_LIMIT,
 			  &memory, sizeof(memory));
@@ -172,6 +197,8 @@ static int fq_codel_print_opt(struct qdi
 	unsigned int ecn;
 	unsigned int quantum;
 	unsigned int ce_threshold;
+	__u8 ce_threshold_selector = 0;
+	__u8 ce_threshold_mask = 0;
 	unsigned int memory_limit;
 	unsigned int drop_batch;
 
@@ -211,6 +238,19 @@ static int fq_codel_print_opt(struct qdi
 		print_string(PRINT_FP, NULL, "ce_threshold %s ",
 			     sprint_time(ce_threshold, b1));
 	}
+	if (tb[TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR]) >= sizeof(__u8))
+		ce_threshold_selector = rta_getattr_u8(tb[TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR]);
+	if (tb[TCA_FQ_CODEL_CE_THRESHOLD_MASK] &&
+	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_CE_THRESHOLD_MASK]) >= sizeof(__u8))
+		ce_threshold_mask = rta_getattr_u8(tb[TCA_FQ_CODEL_CE_THRESHOLD_MASK]);
+	if (ce_threshold_mask || ce_threshold_selector) {
+		print_hhu(PRINT_ANY, "ce_threshold_selector", "ce_threshold_selector %#x",
+			  ce_threshold_selector);
+		print_hhu(PRINT_ANY, "ce_threshold_mask", "/%#x ",
+			  ce_threshold_mask);
+	}
+
 	if (tb[TCA_FQ_CODEL_INTERVAL] &&
 	    RTA_PAYLOAD(tb[TCA_FQ_CODEL_INTERVAL]) >= sizeof(__u32)) {
 		interval = rta_getattr_u32(tb[TCA_FQ_CODEL_INTERVAL]);
diff -pruN 5.15.0-1/tc/q_netem.c 5.19.0-1/tc/q_netem.c
--- 5.15.0-1/tc/q_netem.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/q_netem.c	2022-08-02 18:36:33.000000000 +0000
@@ -1,13 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
- * q_netem.c		NETEM.
- *
- *		This program is free software; you can redistribute it and/or
- *		modify it under the terms of the GNU General Public License
- *		as published by the Free Software Foundation; either version
- *		2 of the License, or (at your option) any later version.
- *
- * Authors:	Stephen Hemminger <shemminger@linux-foundation.org>
- *
+ * Author:	Stephen Hemminger <shemminger@linux-foundation.org>
  */
 
 #include <stdio.h>
@@ -30,22 +23,20 @@
 static void explain(void)
 {
 	fprintf(stderr,
-		"Usage: ... netem	[ limit PACKETS ]\n" \
-		"			[ delay TIME [ JITTER [CORRELATION]]]\n" \
-		"			[ distribution {uniform|normal|pareto|paretonormal} ]\n" \
-		"			[ corrupt PERCENT [CORRELATION]]\n" \
-		"			[ duplicate PERCENT [CORRELATION]]\n" \
-		"			[ loss random PERCENT [CORRELATION]]\n" \
-		"			[ loss state P13 [P31 [P32 [P23 P14]]]\n" \
-		"			[ loss gemodel PERCENT [R [1-H [1-K]]]\n" \
-		"			[ ecn ]\n" \
-		"			[ reorder PERCENT [CORRELATION] [ gap DISTANCE ]]\n" \
-		"			[ rate RATE [PACKETOVERHEAD] [CELLSIZE] [CELLOVERHEAD]]\n" \
-		"			[ slot MIN_DELAY [MAX_DELAY] [packets MAX_PACKETS]" \
-		" [bytes MAX_BYTES]]\n" \
-		"		[ slot distribution" \
-		" {uniform|normal|pareto|paretonormal|custom} DELAY JITTER" \
-		" [packets MAX_PACKETS] [bytes MAX_BYTES]]\n");
+		"Usage: ... netem [ limit PACKETS ]\n"
+		"                 [ delay TIME [ JITTER [CORRELATION]]]\n"
+		"                 [ distribution {uniform|normal|pareto|paretonormal} ]\n"
+		"                 [ corrupt PERCENT [CORRELATION]]\n"
+		"                 [ duplicate PERCENT [CORRELATION]]\n"
+		"                 [ loss random PERCENT [CORRELATION]]\n"
+		"                 [ loss state P13 [P31 [P32 [P23 P14]]]\n"
+		"                 [ loss gemodel PERCENT [R [1-H [1-K]]]\n"
+		"                 [ ecn ]\n"
+		"                 [ reorder PERCENT [CORRELATION] [ gap DISTANCE ]]\n"
+		"                 [ rate RATE [PACKETOVERHEAD] [CELLSIZE] [CELLOVERHEAD]]\n"
+		"                 [ slot MIN_DELAY [MAX_DELAY] [packets MAX_PACKETS] [bytes MAX_BYTES]]\n"
+		"                 [ slot distribution {uniform|normal|pareto|paretonormal|custom}\n"
+		"                   DELAY JITTER [packets MAX_PACKETS] [bytes MAX_BYTES]]\n");
 }
 
 static void explain1(const char *arg)
@@ -59,10 +50,12 @@ static void explain1(const char *arg)
 #define MAX_DIST	(16*1024)
 
 /* Print values only if they are non-zero */
-static void __print_int_opt(const char *label_json, const char *label_fp,
-			    int val)
+static void __attribute__((format(printf, 2, 0)))
+__print_int_opt(const char *label_json, const char *label_fp, int val)
 {
-	print_int(PRINT_ANY, label_json, val ? label_fp : "", val);
+	print_int(PRINT_JSON, label_json, NULL, val);
+	if (val != 0)
+		print_int(PRINT_FP, NULL, label_fp, val);
 }
 #define PRINT_INT_OPT(label, val)			\
 	__print_int_opt(label, " " label " %d", (val))
@@ -70,8 +63,8 @@ static void __print_int_opt(const char *
 /* Time print prints normally with varying units, but for JSON prints
  * in seconds (1ms vs 0.001).
  */
-static void __print_time64(const char *label_json, const char *label_fp,
-			   __u64 val)
+static void __attribute__((format(printf, 2, 0)))
+__print_time64(const char *label_json, const char *label_fp, __u64 val)
 {
 	SPRINT_BUF(b1);
 
@@ -85,8 +78,8 @@ static void __print_time64(const char *l
 /* Percent print prints normally in percentage points, but for JSON prints
  * an absolute value (1% vs 0.01).
  */
-static void __print_percent(const char *label_json, const char *label_fp,
-			    __u32 per)
+static void __attribute__((format(printf, 2, 0)))
+__print_percent(const char *label_json, const char *label_fp, __u32 per)
 {
 	print_float(PRINT_FP, NULL, label_fp, (100. * per) / UINT32_MAX);
 	print_float(PRINT_JSON, label_json, NULL, (1. * per) / UINT32_MAX);
@@ -138,7 +131,8 @@ static int get_distribution(const char *
 	char name[128];
 
 	snprintf(name, sizeof(name), "%s/%s.dist", get_tc_lib(), type);
-	if ((f = fopen(name, "r")) == NULL) {
+	f = fopen(name, "r");
+	if (f == NULL) {
 		fprintf(stderr, "No distribution data for %s (%s: %s)\n",
 			type, name, strerror(errno));
 		return -1;
@@ -175,8 +169,10 @@ static int get_distribution(const char *
 #define NEXT_IS_SIGNED_NUMBER() \
 	(NEXT_ARG_OK() && (isdigit(argv[1][0]) || argv[1][0] == '-'))
 
-/* Adjust for the fact that psched_ticks aren't always usecs
-   (based on kernel PSCHED_CLOCK configuration */
+/*
+ * Adjust for the fact that psched_ticks aren't always usecs
+ *  (based on kernel PSCHED_CLOCK configuration
+ */
 static int get_ticks(__u32 *ticks, const char *str)
 {
 	unsigned int t;
@@ -258,7 +254,7 @@ static int netem_parse_opt(struct qdisc_
 
 			if (!strcmp(*argv, "random")) {
 				NEXT_ARG();
-			random_loss_model:
+random_loss_model:
 				if (get_percent(&opt.loss, *argv)) {
 					explain1("loss percent");
 					return -1;
@@ -523,6 +519,7 @@ static int netem_parse_opt(struct qdisc_
 			if (NEXT_ARG_OK() &&
 			    matches(*(argv+1), "bytes") == 0) {
 				unsigned int max_bytes;
+
 				NEXT_ARG();
 				if (!NEXT_ARG_OK() ||
 				    get_size(&max_bytes, *(argv+1))) {
@@ -532,11 +529,9 @@ static int netem_parse_opt(struct qdisc_
 				slot.max_bytes = (int) max_bytes;
 				NEXT_ARG();
 			}
-		} else if (strcmp(*argv, "help") == 0) {
-			explain();
-			return -1;
 		} else {
-			fprintf(stderr, "What is \"%s\"?\n", *argv);
+			if (strcmp(*argv, "help") != 0)
+				fprintf(stderr, "What is \"%s\"?\n", *argv);
 			explain();
 			return -1;
 		}
@@ -802,9 +797,10 @@ static int netem_print_opt(struct qdisc_
 		rate64 = rate64 ? : rate->rate;
 		tc_print_rate(PRINT_ANY, "rate", " rate %s", rate64);
 		PRINT_INT_OPT("packetoverhead", rate->packet_overhead);
-		print_uint(PRINT_ANY, "cellsize",
-			   rate->cell_size ? " cellsize %u" : "",
-			   rate->cell_size);
+
+		print_uint(PRINT_JSON, "cellsize", NULL, rate->cell_size);
+		if (rate->cell_size)
+			print_uint(PRINT_FP, NULL, " cellsize %u", rate->cell_size);
 		PRINT_INT_OPT("celloverhead", rate->cell_overhead);
 		close_json_object();
 	}
@@ -824,9 +820,13 @@ static int netem_print_opt(struct qdisc_
 		close_json_object();
 	}
 
-	print_bool(PRINT_ANY, "ecn", ecn ? " ecn " : "", ecn);
-	print_luint(PRINT_ANY, "gap", qopt.gap ? " gap %lu" : "",
-		    (unsigned long)qopt.gap);
+	print_bool(PRINT_JSON, "ecn", NULL, ecn);
+	if (ecn)
+		print_bool(PRINT_FP, NULL, " ecn ", ecn);
+
+	print_luint(PRINT_JSON, "gap", NULL, qopt.gap);
+	if (qopt.gap)
+		print_luint(PRINT_FP, NULL, " gap %lu", qopt.gap);
 
 	return 0;
 }
diff -pruN 5.15.0-1/tc/tc_util.c 5.19.0-1/tc/tc_util.c
--- 5.15.0-1/tc/tc_util.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tc/tc_util.c	2022-08-02 18:36:33.000000000 +0000
@@ -247,7 +247,8 @@ int get_percent_rate64(__u64 *rate, cons
 	return get_rate64(rate, r_str);
 }
 
-void tc_print_rate(enum output_type t, const char *key, const char *fmt,
+void __attribute__((format(printf, 3, 0)))
+tc_print_rate(enum output_type t, const char *key, const char *fmt,
 		   unsigned long long rate)
 {
 	print_rate(use_iec, t, key, fmt, rate);
@@ -475,8 +476,7 @@ static int parse_action_control_slash_sp
 			result_p = &result2;
 			NEXT_ARG();
 			/* fall-through */
-		case 0: /* fall-through */
-		case 2:
+		case 0: 
 			ret = parse_action_control(&argc, &argv,
 						   result_p, allow_num);
 			if (ret)
@@ -782,10 +782,7 @@ static void print_masked_type(__u32 type
 			      const char *name, struct rtattr *attr,
 			      struct rtattr *mask_attr, bool newline)
 {
-	SPRINT_BUF(namefrm);
 	__u32 value, mask;
-	SPRINT_BUF(out);
-	size_t done;
 
 	if (!attr)
 		return;
@@ -793,27 +790,18 @@ static void print_masked_type(__u32 type
 	value = rta_getattr_type(attr);
 	mask = mask_attr ? rta_getattr_type(mask_attr) : type_max;
 
-	if (is_json_context()) {
-		sprintf(namefrm, "\n  %s %%u", name);
-		print_hu(PRINT_ANY, name, namefrm,
-			 rta_getattr_type(attr));
-		if (mask != type_max) {
-			char mask_name[SPRINT_BSIZE-6];
-
-			sprintf(mask_name, "%s_mask", name);
-			if (newline)
-				print_string(PRINT_FP, NULL, "%s ", _SL_);
-			sprintf(namefrm, " %s %%u", mask_name);
-			print_hu(PRINT_ANY, mask_name, namefrm, mask);
-		}
-	} else {
-		done = sprintf(out, "%u", value);
-		if (mask != type_max)
-			sprintf(out + done, "/0x%x", mask);
-		if (newline)
-			print_string(PRINT_FP, NULL, "%s ", _SL_);
-		sprintf(namefrm, " %s %%s", name);
-		print_string(PRINT_ANY, name, namefrm, out);
+	if (newline)
+		print_string(PRINT_FP, NULL, "%s  ", _SL_);
+	else
+		print_string(PRINT_FP, NULL, " ", _SL_);
+
+	print_uint_name_value(name, value);
+
+	if (mask != type_max) {
+		char mask_name[SPRINT_BSIZE-6];
+
+		snprintf(mask_name, sizeof(mask_name), "%s_mask", name);
+		print_hex(PRINT_ANY, mask_name, "/0x%x", mask);
 	}
 }
 
diff -pruN 5.15.0-1/testsuite/tests/ip/link/add_type_xfrm.t 5.19.0-1/testsuite/tests/ip/link/add_type_xfrm.t
--- 5.15.0-1/testsuite/tests/ip/link/add_type_xfrm.t	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/testsuite/tests/ip/link/add_type_xfrm.t	2022-08-02 18:36:33.000000000 +0000
@@ -15,18 +15,3 @@ test_on "$NEW_DEV"
 test_on "if_id $IF_ID"
 
 ts_ip "$0" "Del $NEW_DEV xfrm interface"   link del dev $NEW_DEV
-
-
-ts_log "[Testing Add XFRM Interface, No IF-ID]"
-
-PHYS_DEV="lo"
-NEW_DEV="$(rand_dev)"
-IF_ID="0xf"
-
-ts_ip "$0" "Add $NEW_DEV xfrm interface"    link add dev $NEW_DEV type xfrm dev $PHYS_DEV
-
-ts_ip "$0" "Show $NEW_DEV xfrm interface"   -d link show dev $NEW_DEV
-test_on "$NEW_DEV"
-test_on_not "if_id $IF_ID"
-
-ts_ip "$0" "Del $NEW_DEV xfrm interface"   link del dev $NEW_DEV
diff -pruN 5.15.0-1/testsuite/tests/ip/rule/dsfield.t 5.19.0-1/testsuite/tests/ip/rule/dsfield.t
--- 5.15.0-1/testsuite/tests/ip/rule/dsfield.t	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/testsuite/tests/ip/rule/dsfield.t	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+. lib/generic.sh
+
+ts_log "[Testing rule with option dsfield/tos]"
+
+ts_ip "$0" "Add IPv4 rule with dsfield 0x10" -4 rule add dsfield 0x10
+ts_ip "$0" "Show IPv4 rule with dsfield 0x10" -4 rule show dsfield 0x10
+test_on "tos 0x10"
+test_lines_count 1
+ts_ip "$0" "Delete IPv4 rule with dsfield 0x10" -4 rule del dsfield 0x10
+
+ts_ip "$0" "Add IPv4 rule with tos 0x10" -4 rule add tos 0x10
+ts_ip "$0" "Show IPv4 rule with tos 0x10" -4 rule show tos 0x10
+test_on "tos 0x10"
+test_lines_count 1
+ts_ip "$0" "Delete IPv4 rule with tos 0x10" -4 rule del tos 0x10
+
+ts_ip "$0" "Add IPv6 rule with dsfield 0x10" -6 rule add dsfield 0x10
+ts_ip "$0" "Show IPv6 rule with dsfield 0x10" -6 rule show dsfield 0x10
+test_on "tos 0x10"
+test_lines_count 1
+ts_ip "$0" "Delete IPv6 rule with dsfield 0x10" -6 rule del dsfield 0x10
+
+ts_ip "$0" "Add IPv6 rule with tos 0x10" -6 rule add tos 0x10
+ts_ip "$0" "Show IPv6 rule with tos 0x10" -6 rule show tos 0x10
+test_on "tos 0x10"
+test_lines_count 1
+ts_ip "$0" "Delete IPv6 rule with tos 0x10" -6 rule del tos 0x10
diff -pruN 5.15.0-1/testsuite/tests/tc/vlan.t 5.19.0-1/testsuite/tests/tc/vlan.t
--- 5.15.0-1/testsuite/tests/tc/vlan.t	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/testsuite/tests/tc/vlan.t	2022-08-02 18:36:33.000000000 +0000
@@ -50,7 +50,6 @@ test_on "vlan"
 test_on "modify"
 test_on "id 5"
 test_on "protocol 802.1Q"
-test_on "priority 0"
 test_on "pipe"
 
 reset_qdisc
diff -pruN 5.15.0-1/tipc/link.c 5.19.0-1/tipc/link.c
--- 5.15.0-1/tipc/link.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tipc/link.c	2022-08-02 18:36:33.000000000 +0000
@@ -332,7 +332,7 @@ static int _show_link_stat(const char *n
 	open_json_object(NULL);
 
 	print_string(PRINT_ANY, "link", "Link <%s>\n", name);
-	print_string(PRINT_JSON, "state", "", NULL);
+	print_string(PRINT_JSON, "state", NULL, NULL);
 	open_json_array(PRINT_JSON, NULL);
 	if (attrs[TIPC_NLA_LINK_ACTIVE])
 		print_string(PRINT_ANY, NULL, "  %s", "ACTIVE");
diff -pruN 5.15.0-1/tipc/Makefile 5.19.0-1/tipc/Makefile
--- 5.15.0-1/tipc/Makefile	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tipc/Makefile	2022-08-02 18:36:33.000000000 +0000
@@ -1,10 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 include ../config.mk
 
-TARGETS :=
-
-ifeq ($(HAVE_MNL),y)
-
 TIPCOBJ=bearer.o \
     cmdl.o link.o \
     media.o misc.o \
@@ -14,8 +10,6 @@ TIPCOBJ=bearer.o \
 
 TARGETS += tipc
 
-endif
-
 all: $(TARGETS) $(LIBS)
 
 tipc: $(TIPCOBJ)
diff -pruN 5.15.0-1/tipc/misc.c 5.19.0-1/tipc/misc.c
--- 5.15.0-1/tipc/misc.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/tipc/misc.c	2022-08-02 18:36:33.000000000 +0000
@@ -113,16 +113,15 @@ int str2key(char *str, struct tipc_aead_
 	    }
 	}
 
-	if (len > TIPC_AEAD_KEYLEN_MAX)
+	key->keylen = ishex ? (len + 1) / 2 : len;
+	if (key->keylen > TIPC_AEAD_KEYLEN_MAX)
 		return -1;
 
 	/* Obtain key: */
 	if (!ishex) {
-		key->keylen = len;
 		memcpy(key->key, str, len);
 	} else {
 		/* Convert hex string to key */
-		key->keylen = (len + 1) / 2;
 		for (i = 0; i < key->keylen; i++) {
 			if (i == 0 && len % 2 != 0) {
 				if (sscanf(str, "%1hhx", &key->key[0]) != 1)
diff -pruN 5.15.0-1/vdpa/include/uapi/linux/vdpa.h 5.19.0-1/vdpa/include/uapi/linux/vdpa.h
--- 5.15.0-1/vdpa/include/uapi/linux/vdpa.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/vdpa/include/uapi/linux/vdpa.h	2022-08-02 18:36:33.000000000 +0000
@@ -17,11 +17,16 @@ enum vdpa_command {
 	VDPA_CMD_DEV_NEW,
 	VDPA_CMD_DEV_DEL,
 	VDPA_CMD_DEV_GET,		/* can dump */
+	VDPA_CMD_DEV_CONFIG_GET,	/* can dump */
+	VDPA_CMD_DEV_VSTATS_GET,
 };
 
 enum vdpa_attr {
 	VDPA_ATTR_UNSPEC,
 
+	/* Pad attribute for 64b alignment */
+	VDPA_ATTR_PAD = VDPA_ATTR_UNSPEC,
+
 	/* bus name (optional) + dev name together make the parent device handle */
 	VDPA_ATTR_MGMTDEV_BUS_NAME,		/* string */
 	VDPA_ATTR_MGMTDEV_DEV_NAME,		/* string */
@@ -32,6 +37,20 @@ enum vdpa_attr {
 	VDPA_ATTR_DEV_VENDOR_ID,		/* u32 */
 	VDPA_ATTR_DEV_MAX_VQS,			/* u32 */
 	VDPA_ATTR_DEV_MAX_VQ_SIZE,		/* u16 */
+	VDPA_ATTR_DEV_MIN_VQ_SIZE,		/* u16 */
+
+	VDPA_ATTR_DEV_NET_CFG_MACADDR,		/* binary */
+	VDPA_ATTR_DEV_NET_STATUS,		/* u8 */
+	VDPA_ATTR_DEV_NET_CFG_MAX_VQP,		/* u16 */
+	VDPA_ATTR_DEV_NET_CFG_MTU,		/* u16 */
+
+	VDPA_ATTR_DEV_NEGOTIATED_FEATURES,	/* u64 */
+	VDPA_ATTR_DEV_MGMTDEV_MAX_VQS,		/* u32 */
+	VDPA_ATTR_DEV_SUPPORTED_FEATURES,	/* u64 */
+
+	VDPA_ATTR_DEV_QUEUE_INDEX,              /* u32 */
+	VDPA_ATTR_DEV_VENDOR_ATTR_NAME,		/* string */
+	VDPA_ATTR_DEV_VENDOR_ATTR_VALUE,        /* u64 */
 
 	/* new attributes must be added above here */
 	VDPA_ATTR_MAX,
diff -pruN 5.15.0-1/vdpa/include/uapi/linux/virtio_ids.h 5.19.0-1/vdpa/include/uapi/linux/virtio_ids.h
--- 5.15.0-1/vdpa/include/uapi/linux/virtio_ids.h	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/vdpa/include/uapi/linux/virtio_ids.h	2022-08-02 18:36:33.000000000 +0000
@@ -51,8 +51,34 @@
 #define VIRTIO_ID_PSTORE		22 /* virtio pstore device */
 #define VIRTIO_ID_IOMMU			23 /* virtio IOMMU */
 #define VIRTIO_ID_MEM			24 /* virtio mem */
+#define VIRTIO_ID_SOUND			25 /* virtio sound */
 #define VIRTIO_ID_FS			26 /* virtio filesystem */
 #define VIRTIO_ID_PMEM			27 /* virtio pmem */
+#define VIRTIO_ID_RPMB			28 /* virtio rpmb */
 #define VIRTIO_ID_MAC80211_HWSIM	29 /* virtio mac80211-hwsim */
+#define VIRTIO_ID_VIDEO_ENCODER		30 /* virtio video encoder */
+#define VIRTIO_ID_VIDEO_DECODER		31 /* virtio video decoder */
+#define VIRTIO_ID_SCMI			32 /* virtio SCMI */
+#define VIRTIO_ID_NITRO_SEC_MOD		33 /* virtio nitro secure module*/
+#define VIRTIO_ID_I2C_ADAPTER		34 /* virtio i2c adapter */
+#define VIRTIO_ID_WATCHDOG		35 /* virtio watchdog */
+#define VIRTIO_ID_CAN			36 /* virtio can */
+#define VIRTIO_ID_DMABUF		37 /* virtio dmabuf */
+#define VIRTIO_ID_PARAM_SERV		38 /* virtio parameter server */
+#define VIRTIO_ID_AUDIO_POLICY		39 /* virtio audio policy */
+#define VIRTIO_ID_BT			40 /* virtio bluetooth */
+#define VIRTIO_ID_GPIO			41 /* virtio gpio */
+
+/*
+ * Virtio Transitional IDs
+ */
+
+#define VIRTIO_TRANS_ID_NET		0x1000 /* transitional virtio net */
+#define VIRTIO_TRANS_ID_BLOCK		0x1001 /* transitional virtio block */
+#define VIRTIO_TRANS_ID_BALLOON		0x1002 /* transitional virtio balloon */
+#define VIRTIO_TRANS_ID_CONSOLE		0x1003 /* transitional virtio console */
+#define VIRTIO_TRANS_ID_SCSI		0x1004 /* transitional virtio SCSI */
+#define VIRTIO_TRANS_ID_RNG		0x1005 /* transitional virtio rng */
+#define VIRTIO_TRANS_ID_9P		0x1009 /* transitional virtio 9p console */
 
 #endif /* _LINUX_VIRTIO_IDS_H */
diff -pruN 5.15.0-1/vdpa/include/uapi/linux/virtio_ring.h 5.19.0-1/vdpa/include/uapi/linux/virtio_ring.h
--- 5.15.0-1/vdpa/include/uapi/linux/virtio_ring.h	1970-01-01 00:00:00.000000000 +0000
+++ 5.19.0-1/vdpa/include/uapi/linux/virtio_ring.h	2022-08-02 18:36:33.000000000 +0000
@@ -0,0 +1,242 @@
+#ifndef _LINUX_VIRTIO_RING_H
+#define _LINUX_VIRTIO_RING_H
+/* An interface for efficient virtio implementation, currently for use by KVM,
+ * but hopefully others soon.  Do NOT change this since it will
+ * break existing servers and clients.
+ *
+ * This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Copyright Rusty Russell IBM Corporation 2007. */
+#include <stdint.h>
+#include <linux/types.h>
+#include <linux/virtio_types.h>
+
+/* This marks a buffer as continuing via the next field. */
+#define VRING_DESC_F_NEXT	1
+/* This marks a buffer as write-only (otherwise read-only). */
+#define VRING_DESC_F_WRITE	2
+/* This means the buffer contains a list of buffer descriptors. */
+#define VRING_DESC_F_INDIRECT	4
+
+/*
+ * Mark a descriptor as available or used in packed ring.
+ * Notice: they are defined as shifts instead of shifted values.
+ */
+#define VRING_PACKED_DESC_F_AVAIL	7
+#define VRING_PACKED_DESC_F_USED	15
+
+/* The Host uses this in used->flags to advise the Guest: don't kick me when
+ * you add a buffer.  It's unreliable, so it's simply an optimization.  Guest
+ * will still kick if it's out of buffers. */
+#define VRING_USED_F_NO_NOTIFY	1
+/* The Guest uses this in avail->flags to advise the Host: don't interrupt me
+ * when you consume a buffer.  It's unreliable, so it's simply an
+ * optimization.  */
+#define VRING_AVAIL_F_NO_INTERRUPT	1
+
+/* Enable events in packed ring. */
+#define VRING_PACKED_EVENT_FLAG_ENABLE	0x0
+/* Disable events in packed ring. */
+#define VRING_PACKED_EVENT_FLAG_DISABLE	0x1
+/*
+ * Enable events for a specific descriptor in packed ring.
+ * (as specified by Descriptor Ring Change Event Offset/Wrap Counter).
+ * Only valid if VIRTIO_RING_F_EVENT_IDX has been negotiated.
+ */
+#define VRING_PACKED_EVENT_FLAG_DESC	0x2
+
+/*
+ * Wrap counter bit shift in event suppression structure
+ * of packed ring.
+ */
+#define VRING_PACKED_EVENT_F_WRAP_CTR	15
+
+/* We support indirect buffer descriptors */
+#define VIRTIO_RING_F_INDIRECT_DESC	28
+
+/* The Guest publishes the used index for which it expects an interrupt
+ * at the end of the avail ring. Host should ignore the avail->flags field. */
+/* The Host publishes the avail index for which it expects a kick
+ * at the end of the used ring. Guest should ignore the used->flags field. */
+#define VIRTIO_RING_F_EVENT_IDX		29
+
+/* Alignment requirements for vring elements.
+ * When using pre-virtio 1.0 layout, these fall out naturally.
+ */
+#define VRING_AVAIL_ALIGN_SIZE 2
+#define VRING_USED_ALIGN_SIZE 4
+#define VRING_DESC_ALIGN_SIZE 16
+
+/* Virtio ring descriptors: 16 bytes.  These can chain together via "next". */
+struct vring_desc {
+	/* Address (guest-physical). */
+	__virtio64 addr;
+	/* Length. */
+	__virtio32 len;
+	/* The flags as indicated above. */
+	__virtio16 flags;
+	/* We chain unused descriptors via this, too */
+	__virtio16 next;
+};
+
+struct vring_avail {
+	__virtio16 flags;
+	__virtio16 idx;
+	__virtio16 ring[];
+};
+
+/* u32 is used here for ids for padding reasons. */
+struct vring_used_elem {
+	/* Index of start of used descriptor chain. */
+	__virtio32 id;
+	/* Total length of the descriptor chain which was used (written to) */
+	__virtio32 len;
+};
+
+typedef struct vring_used_elem __attribute__((aligned(VRING_USED_ALIGN_SIZE)))
+	vring_used_elem_t;
+
+struct vring_used {
+	__virtio16 flags;
+	__virtio16 idx;
+	vring_used_elem_t ring[];
+};
+
+/*
+ * The ring element addresses are passed between components with different
+ * alignments assumptions. Thus, we might need to decrease the compiler-selected
+ * alignment, and so must use a typedef to make sure the aligned attribute
+ * actually takes hold:
+ *
+ * https://gcc.gnu.org/onlinedocs//gcc/Common-Type-Attributes.html#Common-Type-Attributes
+ *
+ * When used on a struct, or struct member, the aligned attribute can only
+ * increase the alignment; in order to decrease it, the packed attribute must
+ * be specified as well. When used as part of a typedef, the aligned attribute
+ * can both increase and decrease alignment, and specifying the packed
+ * attribute generates a warning.
+ */
+typedef struct vring_desc __attribute__((aligned(VRING_DESC_ALIGN_SIZE)))
+	vring_desc_t;
+typedef struct vring_avail __attribute__((aligned(VRING_AVAIL_ALIGN_SIZE)))
+	vring_avail_t;
+typedef struct vring_used __attribute__((aligned(VRING_USED_ALIGN_SIZE)))
+	vring_used_t;
+
+struct vring {
+	unsigned int num;
+
+	vring_desc_t *desc;
+
+	vring_avail_t *avail;
+
+	vring_used_t *used;
+};
+
+#ifndef VIRTIO_RING_NO_LEGACY
+
+/* The standard layout for the ring is a continuous chunk of memory which looks
+ * like this.  We assume num is a power of 2.
+ *
+ * struct vring
+ * {
+ *	// The actual descriptors (16 bytes each)
+ *	struct vring_desc desc[num];
+ *
+ *	// A ring of available descriptor heads with free-running index.
+ *	__virtio16 avail_flags;
+ *	__virtio16 avail_idx;
+ *	__virtio16 available[num];
+ *	__virtio16 used_event_idx;
+ *
+ *	// Padding to the next align boundary.
+ *	char pad[];
+ *
+ *	// A ring of used descriptor heads with free-running index.
+ *	__virtio16 used_flags;
+ *	__virtio16 used_idx;
+ *	struct vring_used_elem used[num];
+ *	__virtio16 avail_event_idx;
+ * };
+ */
+/* We publish the used event index at the end of the available ring, and vice
+ * versa. They are at the end for backwards compatibility. */
+#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num])
+#define vring_avail_event(vr) (*(__virtio16 *)&(vr)->used->ring[(vr)->num])
+
+static __inline__ void vring_init(struct vring *vr, unsigned int num, void *p,
+			      unsigned long align)
+{
+	vr->num = num;
+	vr->desc = p;
+	vr->avail = (struct vring_avail *)((char *)p + num * sizeof(struct vring_desc));
+	vr->used = (void *)(((uintptr_t)&vr->avail->ring[num] + sizeof(__virtio16)
+		+ align-1) & ~(align - 1));
+}
+
+static __inline__ unsigned vring_size(unsigned int num, unsigned long align)
+{
+	return ((sizeof(struct vring_desc) * num + sizeof(__virtio16) * (3 + num)
+		 + align - 1) & ~(align - 1))
+		+ sizeof(__virtio16) * 3 + sizeof(struct vring_used_elem) * num;
+}
+
+#endif /* VIRTIO_RING_NO_LEGACY */
+
+/* The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX */
+/* Assuming a given event_idx value from the other side, if
+ * we have just incremented index from old to new_idx,
+ * should we trigger an event? */
+static __inline__ int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old)
+{
+	/* Note: Xen has similar logic for notification hold-off
+	 * in include/xen/interface/io/ring.h with req_event and req_prod
+	 * corresponding to event_idx + 1 and new_idx respectively.
+	 * Note also that req_event and req_prod in Xen start at 1,
+	 * event indexes in virtio start at 0. */
+	return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old);
+}
+
+struct vring_packed_desc_event {
+	/* Descriptor Ring Change Event Offset/Wrap Counter. */
+	__le16 off_wrap;
+	/* Descriptor Ring Change Event Flags. */
+	__le16 flags;
+};
+
+struct vring_packed_desc {
+	/* Buffer Address. */
+	__le64 addr;
+	/* Buffer Length. */
+	__le32 len;
+	/* Buffer ID. */
+	__le16 id;
+	/* The flags depending on descriptor type. */
+	__le16 flags;
+};
+
+#endif /* _LINUX_VIRTIO_RING_H */
diff -pruN 5.15.0-1/vdpa/Makefile 5.19.0-1/vdpa/Makefile
--- 5.15.0-1/vdpa/Makefile	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/vdpa/Makefile	2022-08-02 18:36:33.000000000 +0000
@@ -1,16 +1,10 @@
 # SPDX-License-Identifier: GPL-2.0
 include ../config.mk
 
-TARGETS :=
-
-ifeq ($(HAVE_MNL),y)
-
 CFLAGS += -I./include/uapi/
 VDPAOBJ = vdpa.o
 TARGETS += vdpa
 
-endif
-
 all: $(TARGETS) $(LIBS)
 
 vdpa: $(VDPAOBJ)
diff -pruN 5.15.0-1/vdpa/vdpa.c 5.19.0-1/vdpa/vdpa.c
--- 5.15.0-1/vdpa/vdpa.c	2021-11-01 23:41:02.000000000 +0000
+++ 5.19.0-1/vdpa/vdpa.c	2022-08-02 18:36:33.000000000 +0000
@@ -4,11 +4,16 @@
 #include <getopt.h>
 #include <errno.h>
 #include <linux/genetlink.h>
+#include <linux/if_ether.h>
 #include <linux/vdpa.h>
 #include <linux/virtio_ids.h>
+#include <linux/virtio_net.h>
 #include <linux/netlink.h>
 #include <libmnl/libmnl.h>
+#include <linux/virtio_ring.h>
+#include <linux/virtio_config.h>
 #include "mnl_utils.h"
+#include <rt_names.h>
 
 #include "version.h"
 #include "json_print.h"
@@ -18,6 +23,9 @@
 #define VDPA_OPT_VDEV_MGMTDEV_HANDLE	BIT(1)
 #define VDPA_OPT_VDEV_NAME		BIT(2)
 #define VDPA_OPT_VDEV_HANDLE		BIT(3)
+#define VDPA_OPT_VDEV_MAC		BIT(4)
+#define VDPA_OPT_VDEV_MTU		BIT(5)
+#define VDPA_OPT_MAX_VQP		BIT(6)
 
 struct vdpa_opts {
 	uint64_t present; /* flags of present items */
@@ -25,6 +33,9 @@ struct vdpa_opts {
 	char *mdev_name;
 	const char *vdev_name;
 	unsigned int device_id;
+	char mac[ETH_ALEN];
+	uint16_t mtu;
+	uint16_t max_vqp;
 };
 
 struct vdpa {
@@ -71,6 +82,9 @@ static const enum mnl_attr_data_type vdp
 	[VDPA_ATTR_DEV_VENDOR_ID] = MNL_TYPE_U32,
 	[VDPA_ATTR_DEV_MAX_VQS] = MNL_TYPE_U32,
 	[VDPA_ATTR_DEV_MAX_VQ_SIZE] = MNL_TYPE_U16,
+	[VDPA_ATTR_DEV_NEGOTIATED_FEATURES] = MNL_TYPE_U64,
+	[VDPA_ATTR_DEV_MGMTDEV_MAX_VQS] = MNL_TYPE_U32,
+	[VDPA_ATTR_DEV_SUPPORTED_FEATURES] = MNL_TYPE_U64,
 };
 
 static int attr_cb(const struct nlattr *attr, void *data)
@@ -134,6 +148,32 @@ static int vdpa_argv_str(struct vdpa *vd
 	return 0;
 }
 
+static int vdpa_argv_mac(struct vdpa *vdpa, int argc, char **argv, char *mac)
+{
+	int alen;
+
+	if (argc <= 0 || *argv == NULL) {
+		fprintf(stderr, "String parameter expected\n");
+		return -EINVAL;
+	}
+
+	alen = ll_addr_a2n(mac, ETH_ALEN, *argv);
+	if (alen < 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int vdpa_argv_u16(struct vdpa *vdpa, int argc, char **argv,
+			 uint16_t *result)
+{
+	if (argc <= 0 || *argv == NULL) {
+		fprintf(stderr, "number expected\n");
+		return -EINVAL;
+	}
+
+	return get_u16(result, *argv, 10);
+}
+
 struct vdpa_args_metadata {
 	uint64_t o_flag;
 	const char *err_msg;
@@ -181,13 +221,20 @@ static void vdpa_opts_put(struct nlmsghd
 	if ((opts->present & VDPA_OPT_VDEV_NAME) ||
 	    (opts->present & VDPA_OPT_VDEV_HANDLE))
 		mnl_attr_put_strz(nlh, VDPA_ATTR_DEV_NAME, opts->vdev_name);
+	if (opts->present & VDPA_OPT_VDEV_MAC)
+		mnl_attr_put(nlh, VDPA_ATTR_DEV_NET_CFG_MACADDR,
+			     sizeof(opts->mac), opts->mac);
+	if (opts->present & VDPA_OPT_VDEV_MTU)
+		mnl_attr_put_u16(nlh, VDPA_ATTR_DEV_NET_CFG_MTU, opts->mtu);
+	if (opts->present & VDPA_OPT_MAX_VQP)
+		mnl_attr_put_u16(nlh, VDPA_ATTR_DEV_NET_CFG_MAX_VQP, opts->max_vqp);
 }
 
 static int vdpa_argv_parse(struct vdpa *vdpa, int argc, char **argv,
-			   uint64_t o_required)
+			   uint64_t o_required, uint64_t o_optional)
 {
+	uint64_t o_all = o_required | o_optional;
 	struct vdpa_opts *opts = &vdpa->opts;
-	uint64_t o_all = o_required;
 	uint64_t o_found = 0;
 	int err;
 
@@ -231,6 +278,32 @@ static int vdpa_argv_parse(struct vdpa *
 
 			NEXT_ARG_FWD();
 			o_found |= VDPA_OPT_VDEV_MGMTDEV_HANDLE;
+		} else if ((strcmp(*argv, "mac") == 0) &&
+			   (o_all & VDPA_OPT_VDEV_MAC)) {
+			NEXT_ARG_FWD();
+			err = vdpa_argv_mac(vdpa, argc, argv, opts->mac);
+			if (err)
+				return err;
+
+			NEXT_ARG_FWD();
+			o_found |= VDPA_OPT_VDEV_MAC;
+		} else if ((strcmp(*argv, "mtu") == 0) &&
+			   (o_all & VDPA_OPT_VDEV_MTU)) {
+			NEXT_ARG_FWD();
+			err = vdpa_argv_u16(vdpa, argc, argv, &opts->mtu);
+			if (err)
+				return err;
+
+			NEXT_ARG_FWD();
+			o_found |= VDPA_OPT_VDEV_MTU;
+		} else if ((matches(*argv, "max_vqp")  == 0) && (o_optional & VDPA_OPT_MAX_VQP)) {
+			NEXT_ARG_FWD();
+			err = vdpa_argv_u16(vdpa, argc, argv, &opts->max_vqp);
+			if (err)
+				return err;
+
+			NEXT_ARG_FWD();
+			o_found |= VDPA_OPT_MAX_VQP;
 		} else {
 			fprintf(stderr, "Unknown option \"%s\"\n", *argv);
 			return -EINVAL;
@@ -244,11 +317,11 @@ static int vdpa_argv_parse(struct vdpa *
 
 static int vdpa_argv_parse_put(struct nlmsghdr *nlh, struct vdpa *vdpa,
 			       int argc, char **argv,
-			       uint64_t o_required)
+			       uint64_t o_required, uint64_t o_optional)
 {
 	int err;
 
-	err = vdpa_argv_parse(vdpa, argc, argv, o_required);
+	err = vdpa_argv_parse(vdpa, argc, argv, o_required, o_optional);
 	if (err)
 		return err;
 	vdpa_opts_put(nlh, vdpa);
@@ -329,17 +402,105 @@ static const char *parse_class(int num)
 	return class ? class : "< unknown class >";
 }
 
+static const char * const net_feature_strs[64] = {
+	[VIRTIO_NET_F_CSUM] = "CSUM",
+	[VIRTIO_NET_F_GUEST_CSUM] = "GUEST_CSUM",
+	[VIRTIO_NET_F_CTRL_GUEST_OFFLOADS] = "CTRL_GUEST_OFFLOADS",
+	[VIRTIO_NET_F_MTU] = "MTU",
+	[VIRTIO_NET_F_MAC] = "MAC",
+	[VIRTIO_NET_F_GUEST_TSO4] = "GUEST_TSO4",
+	[VIRTIO_NET_F_GUEST_TSO6] = "GUEST_TSO6",
+	[VIRTIO_NET_F_GUEST_ECN] = "GUEST_ECN",
+	[VIRTIO_NET_F_GUEST_UFO] = "GUEST_UFO",
+	[VIRTIO_NET_F_HOST_TSO4] = "HOST_TSO4",
+	[VIRTIO_NET_F_HOST_TSO6] = "HOST_TSO6",
+	[VIRTIO_NET_F_HOST_ECN] = "HOST_ECN",
+	[VIRTIO_NET_F_HOST_UFO] = "HOST_UFO",
+	[VIRTIO_NET_F_MRG_RXBUF] = "MRG_RXBUF",
+	[VIRTIO_NET_F_STATUS] = "STATUS",
+	[VIRTIO_NET_F_CTRL_VQ] = "CTRL_VQ",
+	[VIRTIO_NET_F_CTRL_RX] = "CTRL_RX",
+	[VIRTIO_NET_F_CTRL_VLAN] = "CTRL_VLAN",
+	[VIRTIO_NET_F_CTRL_RX_EXTRA] = "CTRL_RX_EXTRA",
+	[VIRTIO_NET_F_GUEST_ANNOUNCE] = "GUEST_ANNOUNCE",
+	[VIRTIO_NET_F_MQ] = "MQ",
+	[VIRTIO_F_NOTIFY_ON_EMPTY] = "NOTIFY_ON_EMPTY",
+	[VIRTIO_NET_F_CTRL_MAC_ADDR] = "CTRL_MAC_ADDR",
+	[VIRTIO_F_ANY_LAYOUT] = "ANY_LAYOUT",
+	[VIRTIO_NET_F_RSC_EXT] = "RSC_EXT",
+	[VIRTIO_NET_F_HASH_REPORT] = "HASH_REPORT",
+	[VIRTIO_NET_F_RSS] = "RSS",
+	[VIRTIO_NET_F_STANDBY] = "STANDBY",
+	[VIRTIO_NET_F_SPEED_DUPLEX] = "SPEED_DUPLEX",
+};
+
+#define VIRTIO_F_IN_ORDER 35
+#define VIRTIO_F_NOTIFICATION_DATA 38
+#define VDPA_EXT_FEATURES_SZ (VIRTIO_TRANSPORT_F_END - \
+			      VIRTIO_TRANSPORT_F_START + 1)
+
+static const char * const ext_feature_strs[VDPA_EXT_FEATURES_SZ] = {
+	[VIRTIO_RING_F_INDIRECT_DESC - VIRTIO_TRANSPORT_F_START] = "RING_INDIRECT_DESC",
+	[VIRTIO_RING_F_EVENT_IDX - VIRTIO_TRANSPORT_F_START] = "RING_EVENT_IDX",
+	[VIRTIO_F_VERSION_1 - VIRTIO_TRANSPORT_F_START] = "VERSION_1",
+	[VIRTIO_F_ACCESS_PLATFORM - VIRTIO_TRANSPORT_F_START] = "ACCESS_PLATFORM",
+	[VIRTIO_F_RING_PACKED - VIRTIO_TRANSPORT_F_START] = "RING_PACKED",
+	[VIRTIO_F_IN_ORDER - VIRTIO_TRANSPORT_F_START] = "IN_ORDER",
+	[VIRTIO_F_ORDER_PLATFORM - VIRTIO_TRANSPORT_F_START] = "ORDER_PLATFORM",
+	[VIRTIO_F_SR_IOV - VIRTIO_TRANSPORT_F_START] = "SR_IOV",
+	[VIRTIO_F_NOTIFICATION_DATA - VIRTIO_TRANSPORT_F_START] = "NOTIFICATION_DATA",
+};
+
+static const char * const *dev_to_feature_str[] = {
+	[VIRTIO_ID_NET] = net_feature_strs,
+};
+
+#define NUM_FEATURE_BITS 64
+
+static void print_features(struct vdpa *vdpa, uint64_t features, bool mgmtdevf,
+			   uint16_t dev_id)
+{
+	const char * const *feature_strs = NULL;
+	const char *s;
+	int i;
+
+	if (dev_id < ARRAY_SIZE(dev_to_feature_str))
+		feature_strs = dev_to_feature_str[dev_id];
+
+	if (mgmtdevf)
+		pr_out_array_start(vdpa, "dev_features");
+	else
+		pr_out_array_start(vdpa, "negotiated_features");
+
+	for (i = 0; i < NUM_FEATURE_BITS; i++) {
+		if (!(features & (1ULL << i)))
+			continue;
+
+		if (i < VIRTIO_TRANSPORT_F_START || i > VIRTIO_TRANSPORT_F_END)
+			s = feature_strs ? feature_strs[i] : NULL;
+		else
+			s = ext_feature_strs[i - VIRTIO_TRANSPORT_F_START];
+
+		if (!s)
+			print_uint(PRINT_ANY, NULL, " bit_%d", i);
+		else
+			print_string(PRINT_ANY, NULL, " %s", s);
+	}
+
+	pr_out_array_end(vdpa);
+}
+
 static void pr_out_mgmtdev_show(struct vdpa *vdpa, const struct nlmsghdr *nlh,
 				struct nlattr **tb)
 {
+	uint64_t classes = 0;
 	const char *class;
 	unsigned int i;
 
 	pr_out_handle_start(vdpa, tb);
 
 	if (tb[VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES]) {
-		uint64_t classes = mnl_attr_get_u64(tb[VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES]);
-
+		classes = mnl_attr_get_u64(tb[VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES]);
 		pr_out_array_start(vdpa, "supported_classes");
 
 		for (i = 1; i < 64; i++) {
@@ -352,6 +513,24 @@ static void pr_out_mgmtdev_show(struct v
 		pr_out_array_end(vdpa);
 	}
 
+	if (tb[VDPA_ATTR_DEV_MGMTDEV_MAX_VQS]) {
+		uint32_t num_vqs;
+
+		print_nl();
+		num_vqs = mnl_attr_get_u32(tb[VDPA_ATTR_DEV_MGMTDEV_MAX_VQS]);
+		print_uint(PRINT_ANY, "max_supported_vqs", "  max_supported_vqs %d", num_vqs);
+	}
+
+	if (tb[VDPA_ATTR_DEV_SUPPORTED_FEATURES]) {
+		uint64_t features;
+
+		features  = mnl_attr_get_u64(tb[VDPA_ATTR_DEV_SUPPORTED_FEATURES]);
+		if (classes & BIT(VIRTIO_ID_NET))
+			print_features(vdpa, features, true, VIRTIO_ID_NET);
+		else
+			print_features(vdpa, features, true, 0);
+	}
+
 	pr_out_handle_end(vdpa);
 }
 
@@ -384,7 +563,7 @@ static int cmd_mgmtdev_show(struct vdpa
 					  flags);
 	if (argc > 0) {
 		err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
-					  VDPA_OPT_MGMTDEV_HANDLE);
+					  VDPA_OPT_MGMTDEV_HANDLE, 0);
 		if (err)
 			return err;
 	}
@@ -411,8 +590,10 @@ static int cmd_mgmtdev(struct vdpa *vdpa
 static void cmd_dev_help(void)
 {
 	fprintf(stderr, "Usage: vdpa dev show [ DEV ]\n");
-	fprintf(stderr, "       vdpa dev add name NAME mgmtdev MANAGEMENTDEV\n");
+	fprintf(stderr, "       vdpa dev add name NAME mgmtdev MANAGEMENTDEV [ mac MACADDR ] [ mtu MTU ]\n");
+	fprintf(stderr, "                                                    [ max_vqp MAX_VQ_PAIRS ]\n");
 	fprintf(stderr, "       vdpa dev del DEV\n");
+	fprintf(stderr, "Usage: vdpa dev config COMMAND [ OPTIONS ]\n");
 }
 
 static const char *device_type_name(uint32_t type)
@@ -480,7 +661,7 @@ static int cmd_dev_show(struct vdpa *vdp
 	nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_GET, flags);
 	if (argc > 0) {
 		err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
-					  VDPA_OPT_VDEV_HANDLE);
+					  VDPA_OPT_VDEV_HANDLE, 0);
 		if (err)
 			return err;
 	}
@@ -499,7 +680,9 @@ static int cmd_dev_add(struct vdpa *vdpa
 	nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_NEW,
 					  NLM_F_REQUEST | NLM_F_ACK);
 	err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
-				  VDPA_OPT_VDEV_MGMTDEV_HANDLE | VDPA_OPT_VDEV_NAME);
+				  VDPA_OPT_VDEV_MGMTDEV_HANDLE | VDPA_OPT_VDEV_NAME,
+				  VDPA_OPT_VDEV_MAC | VDPA_OPT_VDEV_MTU |
+				  VDPA_OPT_MAX_VQP);
 	if (err)
 		return err;
 
@@ -513,13 +696,129 @@ static int cmd_dev_del(struct vdpa *vdpa
 
 	nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_DEL,
 					  NLM_F_REQUEST | NLM_F_ACK);
-	err = vdpa_argv_parse_put(nlh, vdpa, argc, argv, VDPA_OPT_VDEV_HANDLE);
+	err = vdpa_argv_parse_put(nlh, vdpa, argc, argv, VDPA_OPT_VDEV_HANDLE,
+				  0);
 	if (err)
 		return err;
 
 	return mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, NULL, NULL);
 }
 
+static void pr_out_dev_net_config(struct vdpa *vdpa, struct nlattr **tb)
+{
+	SPRINT_BUF(macaddr);
+	uint64_t val_u64;
+	uint16_t val_u16;
+
+	if (tb[VDPA_ATTR_DEV_NET_CFG_MACADDR]) {
+		const unsigned char *data;
+		uint16_t len;
+
+		len = mnl_attr_get_payload_len(tb[VDPA_ATTR_DEV_NET_CFG_MACADDR]);
+		data = mnl_attr_get_payload(tb[VDPA_ATTR_DEV_NET_CFG_MACADDR]);
+
+		print_string(PRINT_ANY, "mac", "mac %s ",
+			     ll_addr_n2a(data, len, 0, macaddr, sizeof(macaddr)));
+	}
+	if (tb[VDPA_ATTR_DEV_NET_STATUS]) {
+		val_u16 = mnl_attr_get_u16(tb[VDPA_ATTR_DEV_NET_STATUS]);
+		print_string(PRINT_ANY, "link ", "link %s ",
+			     (val_u16 & VIRTIO_NET_S_LINK_UP) ? "up" : "down");
+		print_bool(PRINT_ANY, "link_announce ", "link_announce %s ",
+			     (val_u16 & VIRTIO_NET_S_ANNOUNCE) ? true : false);
+	}
+	if (tb[VDPA_ATTR_DEV_NET_CFG_MAX_VQP]) {
+		val_u16 = mnl_attr_get_u16(tb[VDPA_ATTR_DEV_NET_CFG_MAX_VQP]);
+		print_uint(PRINT_ANY, "max_vq_pairs", "max_vq_pairs %d ",
+			     val_u16);
+	}
+	if (tb[VDPA_ATTR_DEV_NET_CFG_MTU]) {
+		val_u16 = mnl_attr_get_u16(tb[VDPA_ATTR_DEV_NET_CFG_MTU]);
+		print_uint(PRINT_ANY, "mtu", "mtu %d ", val_u16);
+	}
+	if (tb[VDPA_ATTR_DEV_NEGOTIATED_FEATURES]) {
+		uint16_t dev_id = 0;
+
+		if (tb[VDPA_ATTR_DEV_ID])
+			dev_id = mnl_attr_get_u32(tb[VDPA_ATTR_DEV_ID]);
+
+		val_u64 = mnl_attr_get_u64(tb[VDPA_ATTR_DEV_NEGOTIATED_FEATURES]);
+		print_features(vdpa, val_u64, false, dev_id);
+	}
+}
+
+static void pr_out_dev_config(struct vdpa *vdpa, struct nlattr **tb)
+{
+	uint32_t device_id = mnl_attr_get_u32(tb[VDPA_ATTR_DEV_ID]);
+
+	pr_out_vdev_handle_start(vdpa, tb);
+	switch (device_id) {
+	case VIRTIO_ID_NET:
+		pr_out_dev_net_config(vdpa, tb);
+		break;
+	default:
+		break;
+	}
+	pr_out_vdev_handle_end(vdpa);
+}
+
+static int cmd_dev_config_show_cb(const struct nlmsghdr *nlh, void *data)
+{
+	struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+	struct nlattr *tb[VDPA_ATTR_MAX + 1] = {};
+	struct vdpa *vdpa = data;
+
+	mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
+	if (!tb[VDPA_ATTR_DEV_NAME] || !tb[VDPA_ATTR_DEV_ID])
+		return MNL_CB_ERROR;
+	pr_out_dev_config(vdpa, tb);
+	return MNL_CB_OK;
+}
+
+static int cmd_dev_config_show(struct vdpa *vdpa, int argc, char **argv)
+{
+	uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
+	struct nlmsghdr *nlh;
+	int err;
+
+	if (argc <= 0)
+		flags |= NLM_F_DUMP;
+
+	nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_CONFIG_GET,
+					  flags);
+	if (argc > 0) {
+		err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
+					  VDPA_OPT_VDEV_HANDLE, 0);
+		if (err)
+			return err;
+	}
+
+	pr_out_section_start(vdpa, "config");
+	err = mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, cmd_dev_config_show_cb, vdpa);
+	pr_out_section_end(vdpa);
+	return err;
+}
+
+static void cmd_dev_config_help(void)
+{
+	fprintf(stderr, "Usage: vdpa dev config show [ DEV ]\n");
+}
+
+static int cmd_dev_config(struct vdpa *vdpa, int argc, char **argv)
+{
+	if (!argc)
+		return cmd_dev_config_show(vdpa, argc - 1, argv + 1);
+
+	if (matches(*argv, "help") == 0) {
+		cmd_dev_config_help();
+		return 0;
+	} else if (matches(*argv, "show") == 0) {
+		return cmd_dev_config_show(vdpa, argc - 1, argv + 1);
+	}
+	fprintf(stderr, "Command \"%s\" not found\n", *argv);
+	return -ENOENT;
+}
+
 static int cmd_dev(struct vdpa *vdpa, int argc, char **argv)
 {
 	if (!argc)
@@ -535,6 +834,8 @@ static int cmd_dev(struct vdpa *vdpa, in
 		return cmd_dev_add(vdpa, argc - 1, argv + 1);
 	} else if (matches(*argv, "del") == 0) {
 		return cmd_dev_del(vdpa, argc - 1, argv + 1);
+	} else if (matches(*argv, "config") == 0) {
+		return cmd_dev_config(vdpa, argc - 1, argv + 1);
 	}
 	fprintf(stderr, "Command \"%s\" not found\n", *argv);
 	return -ENOENT;
@@ -545,7 +846,7 @@ static void help(void)
 	fprintf(stderr,
 		"Usage: vdpa [ OPTIONS ] OBJECT { COMMAND | help }\n"
 		"where  OBJECT := { mgmtdev | dev }\n"
-		"       OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] }\n");
+		"       OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] }\n");
 }
 
 static int vdpa_cmd(struct vdpa *vdpa, int argc, char **argv)
