[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1106602: unblock: iproute2/6.15.0-1



Control: tags -1 moreinfo

On 2025-05-27 00:53:10 +0100, Luca Boccassi wrote:
> Package: release.debian.org
> Severity: normal
> Control: affects -1 + src:iproute2
> User: release.debian.org@packages.debian.org
> Usertags: unblock
> 
> Dear RT,
> 
> Please unblock iproute2/6.15.0-1

Issues preventing migration:
∙ ∙ autopkgtest for iproute2/6.15.0-1: amd64: Pass, arm64: No tests, superficial or marked flaky ♻ (reference ♻), armel: No tests, superficial or marked flaky ♻ (reference ♻), armhf: No tests, superficial or marked flaky ♻ (reference ♻), i386: No tests, superficial or marked flaky ♻ (reference ♻), ppc64el: No tests, superficial or marked flaky ♻ (reference ♻), riscv64: No tests, superficial or marked flaky ♻ (reference ♻), s390x: No tests, superficial or marked flaky ♻ (reference ♻)
∙ ∙ autopkgtest for systemd/257.5-2: amd64: Regression or new test ♻ (reference ♻), arm64: Pass, armel: Pass, armhf: Pass, i386: Pass, ppc64el: Pass, riscv64: Pass, s390x: Pass

Could you check what's wrong with systemd?

Cheers

> 
> The new bug fix release only contains small bug fixes and changes, and
> allows to drop all previously backported patches. Crucially, it fixes a
> regression introduced in 6.14 that broke a major and popular feature,
> #1106321.
> 
> Full debdiff attached.
> 
> Thanks.

> diff -Nru iproute2-6.14.0/bridge/bridge.c iproute2-6.15.0/bridge/bridge.c
> --- iproute2-6.14.0/bridge/bridge.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/bridge/bridge.c	2025-05-26 16:19:09.000000000 +0100
> @@ -103,7 +103,7 @@
>  int
>  main(int argc, char **argv)
>  {
> -	int color = CONF_COLOR;
> +	int color = default_color_opt();
>  
>  	while (argc > 1) {
>  		const char *opt = argv[1];
> diff -Nru iproute2-6.14.0/debian/changelog iproute2-6.15.0/debian/changelog
> --- iproute2-6.14.0/debian/changelog	2025-04-12 12:15:56.000000000 +0100
> +++ iproute2-6.15.0/debian/changelog	2025-05-27 00:16:26.000000000 +0100
> @@ -1,3 +1,12 @@
> +iproute2 (6.15.0-1) unstable; urgency=medium
> +
> +  * Update upstream source from tag 'upstream/6.15.0'
> +    (Closes: #1106321)
> +  * Drop all backported patches, merged upstream
> +  * d/copyright: use GPL URL instead of old FSF postal address
> +
> + -- Luca Boccassi <bluca@debian.org>  Tue, 27 May 2025 00:16:26 +0100
> +
>  iproute2 (6.14.0-3) unstable; urgency=medium
>  
>    * autopkgtest: remove build-essential and dpkg-dev deps, add gcc
> diff -Nru iproute2-6.14.0/debian/copyright iproute2-6.15.0/debian/copyright
> --- iproute2-6.14.0/debian/copyright	2024-11-24 11:21:07.000000000 +0000
> +++ iproute2-6.15.0/debian/copyright	2025-05-27 00:16:26.000000000 +0100
> @@ -52,8 +52,7 @@
>   GNU General Public License for more details.
>   .
>   You should have received a copy of the GNU General Public License
> - along with this package; if not, write to the Free Software
> - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
> + along with this package; if not, see <https://www.gnu.org/licenses/>
>   .
>   On Debian systems, the complete text of the GNU General Public License version
>   2 can be found in `/usr/share/common-licenses/GPL-2'.
> diff -Nru iproute2-6.14.0/debian/patches/0002-color-Introduce-and-use-default_color_opt-function.patch iproute2-6.15.0/debian/patches/0002-color-Introduce-and-use-default_color_opt-function.patch
> --- iproute2-6.14.0/debian/patches/0002-color-Introduce-and-use-default_color_opt-function.patch	2025-04-12 12:14:49.000000000 +0100
> +++ iproute2-6.15.0/debian/patches/0002-color-Introduce-and-use-default_color_opt-function.patch	1970-01-01 01:00:00.000000000 +0100
> @@ -1,61 +0,0 @@
> -Author: Ben Hutchings <benh@debian.org>
> -Description: color: Introduce and use default_color_opt() function
> -Origin: commit:446edf9ef8055125a8ef28a9d9218b05971ee465
> -Forwarded: https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=446edf9ef8055125a8ef28a9d9218b05971ee465
> ---- a/bridge/bridge.c
> -+++ b/bridge/bridge.c
> -@@ -103,7 +103,7 @@
> - int
> - main(int argc, char **argv)
> - {
> --	int color = CONF_COLOR;
> -+	int color = default_color_opt();
> - 
> - 	while (argc > 1) {
> - 		const char *opt = argv[1];
> ---- a/include/color.h
> -+++ b/include/color.h
> -@@ -20,6 +20,7 @@
> - 	COLOR_OPT_ALWAYS = 2
> - };
> - 
> -+int default_color_opt(void);
> - bool check_enable_color(int color, int json);
> - bool matches_color(const char *arg, int *val);
> - int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...);
> ---- a/ip/ip.c
> -+++ b/ip/ip.c
> -@@ -180,7 +180,7 @@
> - 	const char *libbpf_version;
> - 	char *batch_file = NULL;
> - 	char *basename;
> --	int color = CONF_COLOR;
> -+	int color = default_color_opt();
> - 
> - 	/* to run vrf exec without root, capabilities might be set, drop them
> - 	 * if not needed as the first thing.
> ---- a/lib/color.c
> -+++ b/lib/color.c
> -@@ -81,6 +81,11 @@
> - 	set_color_palette();
> - }
> - 
> -+int default_color_opt(void)
> -+{
> -+	return CONF_COLOR;
> -+}
> -+
> - bool check_enable_color(int color, int json)
> - {
> - 	if (json || color == COLOR_OPT_NEVER)
> ---- a/tc/tc.c
> -+++ b/tc/tc.c
> -@@ -254,7 +254,7 @@
> - {
> - 	const char *libbpf_version;
> - 	char *batch_file = NULL;
> --	int color = CONF_COLOR;
> -+	int color = default_color_opt();
> - 	int ret;
> - 
> - 	while (argc > 1) {
> diff -Nru iproute2-6.14.0/debian/patches/0003-color-Handle-NO_COLOR-environment-variable-in-defaul.patch iproute2-6.15.0/debian/patches/0003-color-Handle-NO_COLOR-environment-variable-in-defaul.patch
> --- iproute2-6.14.0/debian/patches/0003-color-Handle-NO_COLOR-environment-variable-in-defaul.patch	2025-04-12 12:14:49.000000000 +0100
> +++ iproute2-6.15.0/debian/patches/0003-color-Handle-NO_COLOR-environment-variable-in-defaul.patch	1970-01-01 01:00:00.000000000 +0100
> @@ -1,20 +0,0 @@
> -Author: Ben Hutchings <benh@debian.org>
> -Description: color: Handle NO_COLOR environment variable in default_color_opt()
> -Origin: commit:f0076a016cf7926f39c47b95dae0002249c082dc
> -Forwarded: https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=f0076a016cf7926f39c47b95dae0002249c082dc
> ---- a/lib/color.c
> -+++ b/lib/color.c
> -@@ -83,6 +83,13 @@
> - 
> - int default_color_opt(void)
> - {
> -+	const char *no_color;
> -+
> -+	/* If NO_COLOR has a non-empty value, coloured output is never wanted */
> -+	no_color = getenv("NO_COLOR");
> -+	if (no_color && *no_color)
> -+		return COLOR_OPT_NEVER;
> -+
> - 	return CONF_COLOR;
> - }
> - 
> diff -Nru iproute2-6.14.0/debian/patches/0004-color-Assume-background-is-dark-if-unknown.patch iproute2-6.15.0/debian/patches/0004-color-Assume-background-is-dark-if-unknown.patch
> --- iproute2-6.14.0/debian/patches/0004-color-Assume-background-is-dark-if-unknown.patch	2025-04-12 12:14:49.000000000 +0100
> +++ iproute2-6.15.0/debian/patches/0004-color-Assume-background-is-dark-if-unknown.patch	1970-01-01 01:00:00.000000000 +0100
> @@ -1,36 +0,0 @@
> -Author: Ben Hutchings <benh@debian.org>
> -Description: color: Assume background is dark if unknown
> -Origin: commit:cc0f1109d2864686180ba2ce6fba5fcb3bf437bf
> -Forwarded: https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=cc0f1109d2864686180ba2ce6fba5fcb3bf437bf
> ---- a/lib/color.c
> -+++ b/lib/color.c
> -@@ -72,7 +72,11 @@
> - 	C_CLEAR
> - };
> - 
> --static int is_dark_bg;
> -+/*
> -+ * Assume dark background until we know otherwise. The dark-background
> -+ * colours work better on a light background than vice versa.
> -+ */
> -+static int is_dark_bg = 1;
> - static int color_is_enabled;
> - 
> - static void enable_color(void)
> -@@ -138,12 +142,12 @@
> - 	/*
> - 	 * COLORFGBG environment variable usually contains either two or three
> - 	 * values separated by semicolons; we want the last value in either case.
> --	 * If this value is 0-6 or 8, background is dark.
> -+	 * If this value is 0-6 or 8, background is dark; otherwise it's light.
> - 	 */
> - 	if (p && (p = strrchr(p, ';')) != NULL
> --		&& ((p[1] >= '0' && p[1] <= '6') || p[1] == '8')
> --		&& p[2] == '\0')
> --		is_dark_bg = 1;
> -+		&& !(((p[1] >= '0' && p[1] <= '6') || p[1] == '8')
> -+		     && p[2] == '\0'))
> -+		is_dark_bg = 0;
> - }
> - 
> - __attribute__((format(printf, 3, 4)))
> diff -Nru iproute2-6.14.0/debian/patches/0005-color-Do-not-use-dark-blue-in-dark-background-palett.patch iproute2-6.15.0/debian/patches/0005-color-Do-not-use-dark-blue-in-dark-background-palett.patch
> --- iproute2-6.14.0/debian/patches/0005-color-Do-not-use-dark-blue-in-dark-background-palett.patch	2025-04-12 12:14:49.000000000 +0100
> +++ iproute2-6.15.0/debian/patches/0005-color-Do-not-use-dark-blue-in-dark-background-palett.patch	1970-01-01 01:00:00.000000000 +0100
> @@ -1,33 +0,0 @@
> -Author: Ben Hutchings <benh@debian.org>
> -Description: color: Do not use dark blue in dark-background palette
> -Origin: commit:46a4659313c2610427a088d8f03b731819f2b87a
> -Forwarded: https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=46a4659313c2610427a088d8f03b731819f2b87a
> ---- a/lib/color.c
> -+++ b/lib/color.c
> -@@ -24,7 +24,7 @@
> - 	C_BOLD_RED,
> - 	C_BOLD_GREEN,
> - 	C_BOLD_YELLOW,
> --	C_BOLD_BLUE,
> -+	C_BOLD_LIGHT_BLUE,
> - 	C_BOLD_MAGENTA,
> - 	C_BOLD_CYAN,
> - 	C_BOLD_WHITE,
> -@@ -42,7 +42,7 @@
> - 	"\e[1;31m",
> - 	"\e[1;32m",
> - 	"\e[1;33m",
> --	"\e[1;34m",
> -+	"\e[1;94m",
> - 	"\e[1;35m",
> - 	"\e[1;36m",
> - 	"\e[1;37m",
> -@@ -66,7 +66,7 @@
> - 	C_BOLD_CYAN,
> - 	C_BOLD_YELLOW,
> - 	C_BOLD_MAGENTA,
> --	C_BOLD_BLUE,
> -+	C_BOLD_LIGHT_BLUE,
> - 	C_BOLD_GREEN,
> - 	C_BOLD_RED,
> - 	C_CLEAR
> diff -Nru iproute2-6.14.0/debian/patches/0006-nstat-NULL-Dereference-when-no-entries-specified.patch iproute2-6.15.0/debian/patches/0006-nstat-NULL-Dereference-when-no-entries-specified.patch
> --- iproute2-6.14.0/debian/patches/0006-nstat-NULL-Dereference-when-no-entries-specified.patch	2025-04-12 12:15:36.000000000 +0100
> +++ iproute2-6.15.0/debian/patches/0006-nstat-NULL-Dereference-when-no-entries-specified.patch	1970-01-01 01:00:00.000000000 +0100
> @@ -1,17 +0,0 @@
> -Author: ZiAo Li <23110240084@m.fudan.edu.cn>
> -Description: nstat: NULL Dereference when no entries specified
> -Origin: commit:866e1d107b7de68ca1fcd1d4d5ffecf9d96bff30
> -Forwarded: https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/commit/?id=866e1d107b7de68ca1fcd1d4d5ffecf9d96bff30
> ---- a/misc/nstat.c
> -+++ b/misc/nstat.c
> -@@ -218,6 +218,10 @@
> - 			p = next;
> - 		}
> - 		n = db;
> -+		if (n == NULL) {
> -+			fprintf(stderr, "Error: Invalid input – line has ':' but no entries. Add values after ':'.\n");
> -+			exit(-2);
> -+		}
> - 		nread = getline(&buf, &buflen, fp);
> - 		if (nread == -1) {
> - 			fprintf(stderr, "%s:%d: error parsing history file\n",
> diff -Nru iproute2-6.14.0/debian/patches/series iproute2-6.15.0/debian/patches/series
> --- iproute2-6.14.0/debian/patches/series	2025-04-12 12:13:53.000000000 +0100
> +++ iproute2-6.15.0/debian/patches/series	2025-05-27 00:16:02.000000000 +0100
> @@ -1,6 +1 @@
>  0001-Add-moo-feature.patch
> -0002-color-Introduce-and-use-default_color_opt-function.patch
> -0003-color-Handle-NO_COLOR-environment-variable-in-defaul.patch
> -0004-color-Assume-background-is-dark-if-unknown.patch
> -0005-color-Do-not-use-dark-blue-in-dark-background-palett.patch
> -0006-nstat-NULL-Dereference-when-no-entries-specified.patch
> diff -Nru iproute2-6.14.0/etc/iproute2/rt_protos iproute2-6.15.0/etc/iproute2/rt_protos
> --- iproute2-6.14.0/etc/iproute2/rt_protos	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/etc/iproute2/rt_protos	2025-05-26 16:19:09.000000000 +0100
> @@ -17,6 +17,7 @@
>  16	dhcp
>  18	keepalived
>  42	babel
> +84	ovn
>  99	openr
>  186	bgp
>  187	isis
> diff -Nru iproute2-6.14.0/include/color.h iproute2-6.15.0/include/color.h
> --- iproute2-6.14.0/include/color.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/color.h	2025-05-26 16:19:09.000000000 +0100
> @@ -20,6 +20,7 @@
>  	COLOR_OPT_ALWAYS = 2
>  };
>  
> +int default_color_opt(void);
>  bool check_enable_color(int color, int json);
>  bool matches_color(const char *arg, int *val);
>  int color_fprintf(FILE *fp, enum color_attr attr, const char *fmt, ...);
> diff -Nru iproute2-6.14.0/include/uapi/linux/batman_adv.h iproute2-6.15.0/include/uapi/linux/batman_adv.h
> --- iproute2-6.14.0/include/uapi/linux/batman_adv.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/batman_adv.h	2025-05-26 16:19:09.000000000 +0100
> @@ -342,7 +342,7 @@
>  	BATADV_ATTR_MCAST_FLAGS_PRIV,
>  
>  	/**
> -	 * @BATADV_ATTR_VLANID: VLAN id on top of soft interface
> +	 * @BATADV_ATTR_VLANID: VLAN id on top of mesh interface
>  	 */
>  	BATADV_ATTR_VLANID,
>  
> @@ -380,7 +380,7 @@
>  	/**
>  	 * @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
> +	 *  between the mesh and devices bridged with the mesh interface
>  	 */
>  	BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED,
>  
> @@ -509,7 +509,7 @@
>  	BATADV_CMD_UNSPEC,
>  
>  	/**
> -	 * @BATADV_CMD_GET_MESH: Get attributes from softif/mesh
> +	 * @BATADV_CMD_GET_MESH: Get attributes from mesh(if)
>  	 */
>  	BATADV_CMD_GET_MESH,
>  
> @@ -535,7 +535,7 @@
>  
>  	/**
>  	 * @BATADV_CMD_GET_HARDIF: Get attributes from a hardif of the
> -	 *  current softif
> +	 *  current mesh(if)
>  	 */
>  	BATADV_CMD_GET_HARDIF,
>  
> @@ -591,25 +591,25 @@
>  	BATADV_CMD_GET_MCAST_FLAGS,
>  
>  	/**
> -	 * @BATADV_CMD_SET_MESH: Set attributes for softif/mesh
> +	 * @BATADV_CMD_SET_MESH: Set attributes for mesh(if)
>  	 */
>  	BATADV_CMD_SET_MESH,
>  
>  	/**
>  	 * @BATADV_CMD_SET_HARDIF: Set attributes for hardif of the
> -	 *  current softif
> +	 *  current mesh(if)
>  	 */
>  	BATADV_CMD_SET_HARDIF,
>  
>  	/**
>  	 * @BATADV_CMD_GET_VLAN: Get attributes from a VLAN of the
> -	 *  current softif
> +	 *  current mesh(if)
>  	 */
>  	BATADV_CMD_GET_VLAN,
>  
>  	/**
>  	 * @BATADV_CMD_SET_VLAN: Set attributes for VLAN of the
> -	 *  current softif
> +	 *  current mesh(if)
>  	 */
>  	BATADV_CMD_SET_VLAN,
>  
> @@ -691,7 +691,7 @@
>  	 */
>  	IFLA_BATADV_ALGO_NAME,
>  
> -	/* add attributes above here, update the policy in soft-interface.c */
> +	/* add attributes above here, update the policy in mesh-interface.c */
>  
>  	/**
>  	 * @__IFLA_BATADV_MAX: internal use
> diff -Nru iproute2-6.14.0/include/uapi/linux/bpf.h iproute2-6.15.0/include/uapi/linux/bpf.h
> --- iproute2-6.14.0/include/uapi/linux/bpf.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/bpf.h	2025-05-26 16:19:09.000000000 +0100
> @@ -51,6 +51,9 @@
>  #define BPF_XCHG	(0xe0 | BPF_FETCH)	/* atomic exchange */
>  #define BPF_CMPXCHG	(0xf0 | BPF_FETCH)	/* atomic compare-and-write */
>  
> +#define BPF_LOAD_ACQ	0x100	/* load-acquire */
> +#define BPF_STORE_REL	0x110	/* store-release */
> +
>  enum bpf_cond_pseudo_jmp {
>  	BPF_MAY_GOTO = 0,
>  };
> @@ -1207,6 +1210,7 @@
>  #define BPF_F_BEFORE		(1U << 3)
>  #define BPF_F_AFTER		(1U << 4)
>  #define BPF_F_ID		(1U << 5)
> +#define BPF_F_PREORDER		(1U << 6)
>  #define BPF_F_LINK		BPF_F_LINK /* 1 << 13 */
>  
>  /* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the
> @@ -1648,6 +1652,7 @@
>  		};
>  		__u32		next_id;
>  		__u32		open_flags;
> +		__s32		fd_by_id_token_fd;
>  	};
>  
>  	struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
> @@ -4963,6 +4968,9 @@
>   * 		the netns switch takes place from ingress to ingress without
>   * 		going through the CPU's backlog queue.
>   *
> + * 		*skb*\ **->mark** and *skb*\ **->tstamp** are not cleared during
> + * 		the netns switch.
> + *
>   * 		The *flags* argument is reserved and must be 0. The helper is
>   * 		currently only supported for tc BPF program types at the
>   * 		ingress hook and for veth and netkit target device types. The
> @@ -6019,7 +6027,10 @@
>  	FN(user_ringbuf_drain, 209, ##ctx)		\
>  	FN(cgrp_storage_get, 210, ##ctx)		\
>  	FN(cgrp_storage_delete, 211, ##ctx)		\
> -	/* */
> +	/* This helper list is effectively frozen. If you are trying to	\
> +	 * add a new helper, you should add a kfunc instead which has	\
> +	 * less stability guarantees. See Documentation/bpf/kfuncs.rst	\
> +	 */
>  
>  /* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't
>   * know or care about integer value that is now passed as second argument
> @@ -6913,6 +6924,12 @@
>  	BPF_SOCK_OPS_ALL_CB_FLAGS       = 0x7F,
>  };
>  
> +enum {
> +	SK_BPF_CB_TX_TIMESTAMPING	= 1<<0,
> +	SK_BPF_CB_MASK			= (SK_BPF_CB_TX_TIMESTAMPING - 1) |
> +					   SK_BPF_CB_TX_TIMESTAMPING
> +};
> +
>  /* List of known BPF sock_ops operators.
>   * New entries can only be added at the end
>   */
> @@ -7025,6 +7042,29 @@
>  					 * by the kernel or the
>  					 * earlier bpf-progs.
>  					 */
> +	BPF_SOCK_OPS_TSTAMP_SCHED_CB,	/* Called when skb is passing
> +					 * through dev layer when
> +					 * SK_BPF_CB_TX_TIMESTAMPING
> +					 * feature is on.
> +					 */
> +	BPF_SOCK_OPS_TSTAMP_SND_SW_CB,	/* Called when skb is about to send
> +					 * to the nic when SK_BPF_CB_TX_TIMESTAMPING
> +					 * feature is on.
> +					 */
> +	BPF_SOCK_OPS_TSTAMP_SND_HW_CB,	/* Called in hardware phase when
> +					 * SK_BPF_CB_TX_TIMESTAMPING feature
> +					 * is on.
> +					 */
> +	BPF_SOCK_OPS_TSTAMP_ACK_CB,	/* Called when all the skbs in the
> +					 * same sendmsg call are acked
> +					 * when SK_BPF_CB_TX_TIMESTAMPING
> +					 * feature is on.
> +					 */
> +	BPF_SOCK_OPS_TSTAMP_SENDMSG_CB,	/* Called when every sendmsg syscall
> +					 * is triggered. It's used to correlate
> +					 * sendmsg timestamp with corresponding
> +					 * tskey.
> +					 */
>  };
>  
>  /* List of TCP states. There is a build check in net/ipv4/tcp.c to detect
> @@ -7091,6 +7131,7 @@
>  	TCP_BPF_SYN_IP		= 1006, /* Copy the IP[46] and TCP header */
>  	TCP_BPF_SYN_MAC         = 1007, /* Copy the MAC, IP[46], and TCP header */
>  	TCP_BPF_SOCK_OPS_CB_FLAGS = 1008, /* Get or Set TCP sock ops flags */
> +	SK_BPF_CB_FLAGS		= 1009, /* Get or set sock ops flags in socket */
>  };
>  
>  enum {
> diff -Nru iproute2-6.14.0/include/uapi/linux/btf.h iproute2-6.15.0/include/uapi/linux/btf.h
> --- iproute2-6.14.0/include/uapi/linux/btf.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/btf.h	2025-05-26 16:19:09.000000000 +0100
> @@ -36,7 +36,8 @@
>  	 * bits 24-28: kind (e.g. int, ptr, array...etc)
>  	 * bits 29-30: unused
>  	 * bit     31: kind_flag, currently used by
> -	 *             struct, union, enum, fwd and enum64
> +	 *             struct, union, enum, fwd, enum64,
> +	 *             decl_tag and type_tag
>  	 */
>  	__u32 info;
>  	/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
> diff -Nru iproute2-6.14.0/include/uapi/linux/can.h iproute2-6.15.0/include/uapi/linux/can.h
> --- iproute2-6.14.0/include/uapi/linux/can.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/can.h	2025-05-26 16:19:09.000000000 +0100
> @@ -182,7 +182,7 @@
>  /*
>   * defined bits for canxl_frame.flags
>   *
> - * The canxl_frame.flags element contains two bits CANXL_XLF and CANXL_SEC
> + * The canxl_frame.flags element contains three bits CANXL_[XLF|SEC|RRS]
>   * and shares the relative position of the struct can[fd]_frame.len element.
>   * The CANXL_XLF bit ALWAYS needs to be set to indicate a valid CAN XL frame.
>   * As a side effect setting this bit intentionally breaks the length checks
> @@ -192,6 +192,7 @@
>   */
>  #define CANXL_XLF 0x80 /* mandatory CAN XL frame flag (must always be set!) */
>  #define CANXL_SEC 0x01 /* Simple Extended Content (security/segmentation) */
> +#define CANXL_RRS 0x02 /* Remote Request Substitution */
>  
>  /* the 8-bit VCID is optionally placed in the canxl_frame.prio element */
>  #define CANXL_VCID_OFFSET 16 /* bit offset of VCID in prio element */
> diff -Nru iproute2-6.14.0/include/uapi/linux/capability.h iproute2-6.15.0/include/uapi/linux/capability.h
> --- iproute2-6.14.0/include/uapi/linux/capability.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/capability.h	2025-05-26 16:19:09.000000000 +0100
> @@ -273,6 +273,7 @@
>  /* Allow setting encryption key on loopback filesystem */
>  /* Allow setting zone reclaim policy */
>  /* Allow everything under CAP_BPF and CAP_PERFMON for backward compatibility */
> +/* Allow setting hardware protection emergency action */
>  
>  #define CAP_SYS_ADMIN        21
>  
> diff -Nru iproute2-6.14.0/include/uapi/linux/const.h iproute2-6.15.0/include/uapi/linux/const.h
> --- iproute2-6.14.0/include/uapi/linux/const.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/const.h	2025-05-26 16:19:09.000000000 +0100
> @@ -33,7 +33,7 @@
>   * Missing __asm__ support
>   *
>   * __BIT128() would not work in the __asm__ code, as it shifts an
> - * 'unsigned __init128' data type as direct representation of
> + * 'unsigned __int128' data type as direct representation of
>   * 128 bit constants is not supported in the gcc compiler, as
>   * they get silently truncated.
>   *
> diff -Nru iproute2-6.14.0/include/uapi/linux/fib_rules.h iproute2-6.15.0/include/uapi/linux/fib_rules.h
> --- iproute2-6.14.0/include/uapi/linux/fib_rules.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/fib_rules.h	2025-05-26 16:19:09.000000000 +0100
> @@ -70,6 +70,9 @@
>  	FRA_DSCP,	/* dscp */
>  	FRA_FLOWLABEL,	/* flowlabel */
>  	FRA_FLOWLABEL_MASK,	/* flowlabel mask */
> +	FRA_SPORT_MASK,	/* sport mask */
> +	FRA_DPORT_MASK,	/* dport mask */
> +	FRA_DSCP_MASK,	/* dscp mask */
>  	__FRA_MAX
>  };
>  
> diff -Nru iproute2-6.14.0/include/uapi/linux/if_link.h iproute2-6.15.0/include/uapi/linux/if_link.h
> --- iproute2-6.14.0/include/uapi/linux/if_link.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/if_link.h	2025-05-26 16:19:09.000000000 +0100
> @@ -378,6 +378,7 @@
>  	IFLA_GRO_IPV4_MAX_SIZE,
>  	IFLA_DPLL_PIN,
>  	IFLA_MAX_PACING_OFFLOAD_HORIZON,
> +	IFLA_NETNS_IMMUTABLE,
>  	__IFLA_MAX
>  };
>  
> @@ -1436,6 +1437,7 @@
>  	IFLA_GENEVE_TTL_INHERIT,
>  	IFLA_GENEVE_DF,
>  	IFLA_GENEVE_INNER_PROTO_INHERIT,
> +	IFLA_GENEVE_PORT_RANGE,
>  	__IFLA_GENEVE_MAX
>  };
>  #define IFLA_GENEVE_MAX	(__IFLA_GENEVE_MAX - 1)
> @@ -1448,6 +1450,11 @@
>  	GENEVE_DF_MAX = __GENEVE_DF_END - 1,
>  };
>  
> +struct ifla_geneve_port_range {
> +	__be16 low;
> +	__be16 high;
> +};
> +
>  /* Bareudp section  */
>  enum {
>  	IFLA_BAREUDP_UNSPEC,
> diff -Nru iproute2-6.14.0/include/uapi/linux/rtnetlink.h iproute2-6.15.0/include/uapi/linux/rtnetlink.h
> --- iproute2-6.14.0/include/uapi/linux/rtnetlink.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/rtnetlink.h	2025-05-26 16:19:09.000000000 +0100
> @@ -307,6 +307,7 @@
>  #define RTPROT_MROUTED		17	/* Multicast daemon */
>  #define RTPROT_KEEPALIVED	18	/* Keepalived daemon */
>  #define RTPROT_BABEL		42	/* Babel daemon */
> +#define RTPROT_OVN		84	/* OVN daemon */
>  #define RTPROT_OPENR		99	/* Open Routing (Open/R) Routes */
>  #define RTPROT_BGP		186	/* BGP Routes */
>  #define RTPROT_ISIS		187	/* ISIS Routes */
> diff -Nru iproute2-6.14.0/include/uapi/linux/snmp.h iproute2-6.15.0/include/uapi/linux/snmp.h
> --- iproute2-6.14.0/include/uapi/linux/snmp.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/snmp.h	2025-05-26 16:19:09.000000000 +0100
> @@ -23,9 +23,14 @@
>  	IPSTATS_MIB_INPKTS,			/* InReceives */
>  	IPSTATS_MIB_INOCTETS,			/* InOctets */
>  	IPSTATS_MIB_INDELIVERS,			/* InDelivers */
> -	IPSTATS_MIB_OUTFORWDATAGRAMS,		/* OutForwDatagrams */
> +	IPSTATS_MIB_NOECTPKTS,			/* InNoECTPkts */
> +	IPSTATS_MIB_ECT1PKTS,			/* InECT1Pkts */
> +	IPSTATS_MIB_ECT0PKTS,			/* InECT0Pkts */
> +	IPSTATS_MIB_CEPKTS,			/* InCEPkts */
>  	IPSTATS_MIB_OUTREQUESTS,		/* OutRequests */
> +	IPSTATS_MIB_OUTPKTS,			/* OutTransmits */
>  	IPSTATS_MIB_OUTOCTETS,			/* OutOctets */
> +	IPSTATS_MIB_OUTFORWDATAGRAMS,		/* OutForwDatagrams */
>  /* other fields */
>  	IPSTATS_MIB_INHDRERRORS,		/* InHdrErrors */
>  	IPSTATS_MIB_INTOOBIGERRORS,		/* InTooBigErrors */
> @@ -52,12 +57,7 @@
>  	IPSTATS_MIB_INBCASTOCTETS,		/* InBcastOctets */
>  	IPSTATS_MIB_OUTBCASTOCTETS,		/* OutBcastOctets */
>  	IPSTATS_MIB_CSUMERRORS,			/* InCsumErrors */
> -	IPSTATS_MIB_NOECTPKTS,			/* InNoECTPkts */
> -	IPSTATS_MIB_ECT1PKTS,			/* InECT1Pkts */
> -	IPSTATS_MIB_ECT0PKTS,			/* InECT0Pkts */
> -	IPSTATS_MIB_CEPKTS,			/* InCEPkts */
>  	IPSTATS_MIB_REASM_OVERLAPS,		/* ReasmOverlaps */
> -	IPSTATS_MIB_OUTPKTS,			/* OutTransmits */
>  	__IPSTATS_MIB_MAX
>  };
>  
> @@ -186,6 +186,7 @@
>  	LINUX_MIB_TIMEWAITKILLED,		/* TimeWaitKilled */
>  	LINUX_MIB_PAWSACTIVEREJECTED,		/* PAWSActiveRejected */
>  	LINUX_MIB_PAWSESTABREJECTED,		/* PAWSEstabRejected */
> +	LINUX_MIB_TSECRREJECTED,		/* TSEcrRejected */
>  	LINUX_MIB_PAWS_OLD_ACK,			/* PAWSOldAck */
>  	LINUX_MIB_DELAYEDACKS,			/* DelayedACKs */
>  	LINUX_MIB_DELAYEDACKLOCKED,		/* DelayedACKLocked */
> diff -Nru iproute2-6.14.0/include/uapi/linux/stddef.h iproute2-6.15.0/include/uapi/linux/stddef.h
> --- iproute2-6.14.0/include/uapi/linux/stddef.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/stddef.h	2025-05-26 16:19:09.000000000 +0100
> @@ -70,4 +70,6 @@
>  #define __counted_by_be(m)
>  #endif
>  
> +#define __kernel_nonstring
> +
>  #endif /* _LINUX_STDDEF_H */
> diff -Nru iproute2-6.14.0/include/uapi/linux/tcp.h iproute2-6.15.0/include/uapi/linux/tcp.h
> --- iproute2-6.14.0/include/uapi/linux/tcp.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/tcp.h	2025-05-26 16:19:09.000000000 +0100
> @@ -28,7 +28,8 @@
>  	__be32	seq;
>  	__be32	ack_seq;
>  #if defined(__LITTLE_ENDIAN_BITFIELD)
> -	__u16	res1:4,
> +	__u16	ae:1,
> +		res1:3,
>  		doff:4,
>  		fin:1,
>  		syn:1,
> @@ -40,7 +41,8 @@
>  		cwr:1;
>  #elif defined(__BIG_ENDIAN_BITFIELD)
>  	__u16	doff:4,
> -		res1:4,
> +		res1:3,
> +		ae:1,
>  		cwr:1,
>  		ece:1,
>  		urg:1,
> @@ -70,6 +72,7 @@
>  #define tcp_flag_word(tp) (((union tcp_word_hdr *)(tp))->words[3])
>  
>  enum {
> +	TCP_FLAG_AE  = __constant_cpu_to_be32(0x01000000),
>  	TCP_FLAG_CWR = __constant_cpu_to_be32(0x00800000),
>  	TCP_FLAG_ECE = __constant_cpu_to_be32(0x00400000),
>  	TCP_FLAG_URG = __constant_cpu_to_be32(0x00200000),
> @@ -78,7 +81,7 @@
>  	TCP_FLAG_RST = __constant_cpu_to_be32(0x00040000),
>  	TCP_FLAG_SYN = __constant_cpu_to_be32(0x00020000),
>  	TCP_FLAG_FIN = __constant_cpu_to_be32(0x00010000),
> -	TCP_RESERVED_BITS = __constant_cpu_to_be32(0x0F000000),
> +	TCP_RESERVED_BITS = __constant_cpu_to_be32(0x0E000000),
>  	TCP_DATA_OFFSET = __constant_cpu_to_be32(0xF0000000)
>  };
>  
> @@ -136,6 +139,9 @@
>  #define TCP_AO_REPAIR		42	/* Get/Set SNEs and ISNs */
>  
>  #define TCP_IS_MPTCP		43	/* Is MPTCP being used? */
> +#define TCP_RTO_MAX_MS		44	/* max rto time in ms */
> +#define TCP_RTO_MIN_US		45	/* min rto time in us */
> +#define TCP_DELACK_MAX_US	46	/* max delayed ack time in us */
>  
>  #define TCP_REPAIR_ON		1
>  #define TCP_REPAIR_OFF		0
> diff -Nru iproute2-6.14.0/include/uapi/linux/virtio_net.h iproute2-6.15.0/include/uapi/linux/virtio_net.h
> --- iproute2-6.14.0/include/uapi/linux/virtio_net.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/uapi/linux/virtio_net.h	2025-05-26 16:19:09.000000000 +0100
> @@ -327,6 +327,19 @@
>  	__u8 hash_key_data[/* hash_key_length */];
>  };
>  
> +struct virtio_net_rss_config_hdr {
> +	__le32 hash_types;
> +	__le16 indirection_table_mask;
> +	__le16 unclassified_queue;
> +	__le16 indirection_table[/* 1 + indirection_table_mask */];
> +};
> +
> +struct virtio_net_rss_config_trailer {
> +	__le16 max_tx_vq;
> +	__u8 hash_key_length;
> +	__u8 hash_key_data[/* hash_key_length */];
> +};
> +
>   #define VIRTIO_NET_CTRL_MQ_RSS_CONFIG          1
>  
>  /*
> diff -Nru iproute2-6.14.0/include/version.h iproute2-6.15.0/include/version.h
> --- iproute2-6.14.0/include/version.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/include/version.h	2025-05-26 16:19:09.000000000 +0100
> @@ -1 +1 @@
> -static const char version[] = "6.14.0";
> +static const char version[] = "6.15.0";
> diff -Nru iproute2-6.14.0/ip/ila_common.h iproute2-6.15.0/ip/ila_common.h
> --- iproute2-6.14.0/ip/ila_common.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/ip/ila_common.h	2025-05-26 16:19:09.000000000 +0100
> @@ -31,8 +31,6 @@
>  		return ILA_CSUM_NEUTRAL_MAP_AUTO;
>  	else if (strcmp(name, "no-action") == 0)
>  		return ILA_CSUM_NO_ACTION;
> -	else if (strcmp(name, "neutral-map-auto") == 0)
> -		return ILA_CSUM_NEUTRAL_MAP_AUTO;
>  	else
>  		return -1;
>  }
> diff -Nru iproute2-6.14.0/ip/ip.c iproute2-6.15.0/ip/ip.c
> --- iproute2-6.14.0/ip/ip.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/ip/ip.c	2025-05-26 16:19:09.000000000 +0100
> @@ -166,7 +166,7 @@
>  	const char *libbpf_version;
>  	char *batch_file = NULL;
>  	char *basename;
> -	int color = CONF_COLOR;
> +	int color = default_color_opt();
>  
>  	/* to run vrf exec without root, capabilities might be set, drop them
>  	 * if not needed as the first thing.
> diff -Nru iproute2-6.14.0/ip/iplink_netkit.c iproute2-6.15.0/ip/iplink_netkit.c
> --- iproute2-6.14.0/ip/iplink_netkit.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/ip/iplink_netkit.c	2025-05-26 16:19:09.000000000 +0100
> @@ -24,13 +24,19 @@
>  	[NETKIT_DROP]		= "blackhole",
>  };
>  
> +static const char * const netkit_scrub_strings[] = {
> +	[NETKIT_SCRUB_NONE]	= "none",
> +	[NETKIT_SCRUB_DEFAULT]	= "default",
> +};
> +
>  static void explain(struct link_util *lu, FILE *f)
>  {
>  	fprintf(f,
> -		"Usage: ... %s [ mode MODE ] [ POLICY ] [ peer [ POLICY <options> ] ]\n"
> +		"Usage: ... %s [ mode MODE ] [ POLICY ] [ scrub SCRUB ] [ peer [ POLICY <options> ] ]\n"
>  		"\n"
>  		"MODE: l3 | l2\n"
>  		"POLICY: forward | blackhole\n"
> +		"SCRUB: default | none\n"
>  		"(first values are the defaults if nothing is specified)\n"
>  		"\n"
>  		"To get <options> type 'ip link add help'.\n",
> @@ -91,6 +97,23 @@
>  			if (seen_peer)
>  				duparg("peer", *(argv + 1));
>  			seen_peer = true;
> +		} else if (strcmp(*argv, "scrub") == 0) {
> +			int attr_name = seen_peer ?
> +					IFLA_NETKIT_PEER_SCRUB :
> +					IFLA_NETKIT_SCRUB;
> +			enum netkit_scrub scrub;
> +
> +			NEXT_ARG();
> +
> +			if (strcmp(*argv, "none") == 0) {
> +				scrub = NETKIT_SCRUB_NONE;
> +			} else if (strcmp(*argv, "default") == 0) {
> +				scrub = NETKIT_SCRUB_DEFAULT;
> +			} else {
> +				fprintf(stderr, "Error: scrub must be either \"none\" or \"default\"\n");
> +				return -1;
> +			}
> +			addattr32(n, 1024, attr_name, scrub);
>  		} else {
>  			char *type = NULL;
>  
> @@ -144,6 +167,15 @@
>  	return netkit_mode_strings[mode] ? : inv;
>  }
>  
> +static const char *netkit_print_scrub(enum netkit_scrub scrub)
> +{
> +	const char *inv = "UNKNOWN";
> +
> +	if (scrub >= ARRAY_SIZE(netkit_scrub_strings))
> +		return inv;
> +	return netkit_scrub_strings[scrub] ? : inv;
> +}
> +
>  static void netkit_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
>  {
>  	if (!tb)
> @@ -172,6 +204,18 @@
>  		print_string(PRINT_ANY, "peer_policy", "peer policy %s ",
>  			     netkit_print_policy(policy));
>  	}
> +	if (tb[IFLA_NETKIT_SCRUB]) {
> +		enum netkit_scrub scrub = rta_getattr_u32(tb[IFLA_NETKIT_SCRUB]);
> +
> +		print_string(PRINT_ANY, "scrub", "scrub %s ",
> +			     netkit_print_scrub(scrub));
> +	}
> +	if (tb[IFLA_NETKIT_PEER_SCRUB]) {
> +		enum netkit_scrub scrub = rta_getattr_u32(tb[IFLA_NETKIT_PEER_SCRUB]);
> +
> +		print_string(PRINT_ANY, "peer_scrub", "peer scrub %s ",
> +			     netkit_print_scrub(scrub));
> +	}
>  }
>  
>  static void netkit_print_help(struct link_util *lu,
> diff -Nru iproute2-6.14.0/ip/ipmonitor.c iproute2-6.15.0/ip/ipmonitor.c
> --- iproute2-6.14.0/ip/ipmonitor.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/ip/ipmonitor.c	2025-05-26 16:19:09.000000000 +0100
> @@ -5,6 +5,7 @@
>   * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
>   */
>  
> +#include <errno.h>
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <unistd.h>
> @@ -328,38 +329,46 @@
>  
>  	if (lmask & IPMON_LNEXTHOP &&
>  	    rtnl_add_nl_group(&rth, RTNLGRP_NEXTHOP) < 0) {
> -		fprintf(stderr, "Failed to add nexthop group to list\n");
> -		exit(1);
> +		if (errno != EINVAL) {
> +			fprintf(stderr, "Failed to add nexthop group to list\n");
> +			exit(1);
> +		}
>  	}
>  
>  	if (lmask & IPMON_LSTATS &&
>  	    rtnl_add_nl_group(&rth, RTNLGRP_STATS) < 0 &&
>  	    nmask & IPMON_LSTATS) {
> -		fprintf(stderr, "Failed to add stats group to list\n");
> -		exit(1);
> +		if (errno != EINVAL) {
> +			fprintf(stderr, "Failed to add stats group to list\n");
> +			exit(1);
> +		}
>  	}
>  
>  	if (lmask & IPMON_LMADDR) {
>  		if ((!preferred_family || preferred_family == AF_INET) &&
>  		    rtnl_add_nl_group(&rth, RTNLGRP_IPV4_MCADDR) < 0) {
> -			fprintf(stderr,
> -				"Failed to add ipv4 mcaddr group to list\n");
> -			exit(1);
> +			if (errno != EINVAL) {
> +				fprintf(stderr, "Failed to add ipv4 mcaddr group to list\n");
> +				exit(1);
> +			}
>  		}
>  		if ((!preferred_family || preferred_family == AF_INET6) &&
>  		    rtnl_add_nl_group(&rth, RTNLGRP_IPV6_MCADDR) < 0) {
> -			fprintf(stderr,
> -				"Failed to add ipv6 mcaddr group to list\n");
> -			exit(1);
> +			if (errno != EINVAL) {
> +				fprintf(stderr,
> +					"Failed to add ipv6 mcaddr group to list\n");
> +				exit(1);
> +			}
>  		}
>  	}
>  
>  	if (lmask & IPMON_LACADDR) {
>  		if ((!preferred_family || preferred_family == AF_INET6) &&
>  		    rtnl_add_nl_group(&rth, RTNLGRP_IPV6_ACADDR) < 0) {
> -			fprintf(stderr,
> -				"Failed to add ipv6 acaddr group to list\n");
> -			exit(1);
> +			if (errno != EINVAL) {
> +				fprintf(stderr, "Failed to add ipv6 acaddr group to list\n");
> +				exit(1);
> +			}
>  		}
>  	}
>  
> diff -Nru iproute2-6.14.0/ip/iproute.c iproute2-6.15.0/ip/iproute.c
> --- iproute2-6.14.0/ip/iproute.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/ip/iproute.c	2025-05-26 16:19:09.000000000 +0100
> @@ -1729,7 +1729,10 @@
>  
>  	if (filter.cloned) {
>  		if (family != AF_INET6) {
> -			iproute_flush_cache();
> +			ret = iproute_flush_cache();
> +			if (ret < 0)
> +				return ret;
> +
>  			if (show_stats)
>  				printf("*** IPv4 routing cache is flushed.\n");
>  		}
> diff -Nru iproute2-6.14.0/ip/iprule.c iproute2-6.15.0/ip/iprule.c
> --- iproute2-6.14.0/ip/iprule.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/ip/iprule.c	2025-05-26 16:19:09.000000000 +0100
> @@ -23,6 +23,9 @@
>  #include "ip_common.h"
>  #include "json_print.h"
>  
> +#define PORT_MAX_MASK 0xFFFF
> +#define DSCP_MAX_MASK 0x3F
> +
>  enum list_action {
>  	IPRULE_LIST,
>  	IPRULE_FLUSH,
> @@ -44,9 +47,9 @@
>  		"            [ iif STRING ] [ oif STRING ] [ pref NUMBER ] [ l3mdev ]\n"
>  		"            [ uidrange NUMBER-NUMBER ]\n"
>  		"            [ ipproto PROTOCOL ]\n"
> -		"            [ sport [ NUMBER | NUMBER-NUMBER ]\n"
> -		"            [ dport [ NUMBER | NUMBER-NUMBER ] ]\n"
> -		"            [ dscp DSCP ] [ flowlabel FLOWLABEL[/MASK] ]\n"
> +		"            [ sport [ NUMBER[/MASK] | NUMBER-NUMBER ]\n"
> +		"            [ dport [ NUMBER[/MASK] | NUMBER-NUMBER ] ]\n"
> +		"            [ dscp DSCP[/MASK] ] [ flowlabel FLOWLABEL[/MASK] ]\n"
>  		"ACTION := [ table TABLE_ID ]\n"
>  		"          [ protocol PROTO ]\n"
>  		"          [ nat ADDRESS ]\n"
> @@ -80,6 +83,7 @@
>  	int protocolmask;
>  	struct fib_rule_port_range sport;
>  	struct fib_rule_port_range dport;
> +	__u16 sport_mask, dport_mask;
>  	__u8 ipproto;
>  } filter;
>  
> @@ -186,8 +190,9 @@
>  			return false;
>  	}
>  
> -	if (filter.sport.start) {
> +	if (filter.sport_mask) {
>  		const struct fib_rule_port_range *r;
> +		__u16 sport_mask = PORT_MAX_MASK;
>  
>  		if (!tb[FRA_SPORT_RANGE])
>  			return false;
> @@ -196,10 +201,16 @@
>  		if (r->start != filter.sport.start ||
>  		    r->end != filter.sport.end)
>  			return false;
> +
> +		if (tb[FRA_SPORT_MASK])
> +			sport_mask = rta_getattr_u16(tb[FRA_SPORT_MASK]);
> +		if (filter.sport_mask != sport_mask)
> +			return false;
>  	}
>  
> -	if (filter.dport.start) {
> +	if (filter.dport_mask) {
>  		const struct fib_rule_port_range *r;
> +		__u16 dport_mask = PORT_MAX_MASK;
>  
>  		if (!tb[FRA_DPORT_RANGE])
>  			return false;
> @@ -208,6 +219,11 @@
>  		if (r->start != filter.dport.start ||
>  		    r->end != filter.dport.end)
>  			return false;
> +
> +		if (tb[FRA_DPORT_MASK])
> +			dport_mask = rta_getattr_u16(tb[FRA_DPORT_MASK]);
> +		if (filter.dport_mask != dport_mask)
> +			return false;
>  	}
>  
>  	if (filter.tun_id) {
> @@ -223,14 +239,21 @@
>  	}
>  
>  	if (filter.dscpmask) {
> -		if (tb[FRA_DSCP]) {
> -			__u8 dscp = rta_getattr_u8(tb[FRA_DSCP]);
> +		__u8 dscp_mask = DSCP_MAX_MASK;
> +		__u8 dscp;
>  
> -			if (filter.dscp != dscp)
> -				return false;
> -		} else {
> +		if (!tb[FRA_DSCP])
> +			return false;
> +
> +		dscp = rta_getattr_u8(tb[FRA_DSCP]);
> +		if (filter.dscp != dscp)
> +			return false;
> +
> +		if (tb[FRA_DSCP_MASK])
> +			dscp_mask = rta_getattr_u8(tb[FRA_DSCP_MASK]);
> +
> +		if (filter.dscpmask != dscp_mask)
>  			return false;
> -		}
>  	}
>  
>  	if (filter.flowlabel_mask) {
> @@ -390,7 +413,26 @@
>  		struct fib_rule_port_range *r = RTA_DATA(tb[FRA_SPORT_RANGE]);
>  
>  		if (r->start == r->end) {
> -			print_uint(PRINT_ANY, "sport", " sport %u", r->start);
> +			if (tb[FRA_SPORT_MASK]) {
> +				__u16 mask;
> +
> +				mask = rta_getattr_u16(tb[FRA_SPORT_MASK]);
> +				print_uint(PRINT_JSON, "sport", NULL, r->start);
> +				print_0xhex(PRINT_JSON, "sport_mask", NULL,
> +					    mask);
> +				if (mask == PORT_MAX_MASK) {
> +					print_uint(PRINT_FP, NULL, " sport %u",
> +						   r->start);
> +				} else {
> +					print_0xhex(PRINT_FP, NULL,
> +						    " sport %#x", r->start);
> +					print_0xhex(PRINT_FP, NULL, "/%#x",
> +						    mask);
> +				}
> +			} else {
> +				print_uint(PRINT_ANY, "sport", " sport %u",
> +					   r->start);
> +			}
>  		} else {
>  			print_uint(PRINT_ANY, "sport_start", " sport %u",
>  				   r->start);
> @@ -402,7 +444,26 @@
>  		struct fib_rule_port_range *r = RTA_DATA(tb[FRA_DPORT_RANGE]);
>  
>  		if (r->start == r->end) {
> -			print_uint(PRINT_ANY, "dport", " dport %u", r->start);
> +			if (tb[FRA_DPORT_MASK]) {
> +				__u16 mask;
> +
> +				mask = rta_getattr_u16(tb[FRA_DPORT_MASK]);
> +				print_uint(PRINT_JSON, "dport", NULL, r->start);
> +				print_0xhex(PRINT_JSON, "dport_mask", NULL,
> +					    mask);
> +				if (mask == 0xFFFF) {
> +					print_uint(PRINT_FP, NULL, " dport %u",
> +						   r->start);
> +				} else {
> +					print_0xhex(PRINT_FP, NULL,
> +						    " dport %#x", r->start);
> +					print_0xhex(PRINT_FP, NULL, "/%#x",
> +						    mask);
> +				}
> +			} else {
> +				print_uint(PRINT_ANY, "dport", " dport %u",
> +					   r->start);
> +			}
>  		} else {
>  			print_uint(PRINT_ANY, "dport_start", " dport %u",
>  				   r->start);
> @@ -499,8 +560,24 @@
>  	if (tb[FRA_DSCP]) {
>  		__u8 dscp = rta_getattr_u8(tb[FRA_DSCP]);
>  
> -		print_string(PRINT_ANY, "dscp", " dscp %s",
> -			     rtnl_dscp_n2a(dscp, b1, sizeof(b1)));
> +		if (tb[FRA_DSCP_MASK]) {
> +			__u8 mask = rta_getattr_u8(tb[FRA_DSCP_MASK]);
> +
> +			print_string(PRINT_JSON, "dscp", NULL,
> +				     rtnl_dscp_n2a(dscp, b1, sizeof(b1)));
> +			print_0xhex(PRINT_JSON, "dscp_mask", NULL, mask);
> +			if (mask == DSCP_MAX_MASK) {
> +				print_string(PRINT_FP, NULL, " dscp %s",
> +					     rtnl_dscp_n2a(dscp, b1,
> +							   sizeof(b1)));
> +			} else {
> +				print_0xhex(PRINT_FP, NULL, " dscp %#x", dscp);
> +				print_0xhex(PRINT_FP, NULL, "/%#x", mask);
> +			}
> +		} else {
> +			print_string(PRINT_ANY, "dscp", " dscp %s",
> +				     rtnl_dscp_n2a(dscp, b1, sizeof(b1)));
> +		}
>  	}
>  
>  	/* The kernel will either provide both attributes, or none */
> @@ -600,6 +677,55 @@
>  	return 0;
>  }
>  
> +static void iprule_port_parse(char *arg, struct fib_rule_port_range *r,
> +			      __u16 *mask)
> +{
> +	char *sep;
> +
> +	*mask = PORT_MAX_MASK;
> +
> +	sep = strchr(arg, '-');
> +	if (sep) {
> +		*sep = '\0';
> +
> +		if (get_u16(&r->start, arg, 0))
> +			invarg("invalid port range start", arg);
> +
> +		if (get_u16(&r->end, sep + 1, 0))
> +			invarg("invalid port range end", sep + 1);
> +
> +		return;
> +	}
> +
> +	sep = strchr(arg, '/');
> +	if (sep) {
> +		*sep = '\0';
> +
> +		if (get_u16(mask, sep + 1, 0))
> +			invarg("invalid mask", sep + 1);
> +	}
> +
> +	if (get_u16(&r->start, arg, 0))
> +		invarg("invalid port", arg);
> +
> +	r->end = r->start;
> +}
> +
> +static void iprule_dscp_parse(char *arg, __u32 *dscp, __u32 *mask)
> +{
> +	char *slash;
> +
> +	*mask = DSCP_MAX_MASK;
> +
> +	slash = strchr(arg, '/');
> +	if (slash != NULL)
> +		*slash = '\0';
> +	if (rtnl_dscp_a2n(dscp, arg))
> +		invarg("invalid dscp", arg);
> +	if (slash && get_u32(mask, slash + 1, 0))
> +		invarg("invalid dscp mask", slash + 1);
> +}
> +
>  static void iprule_flowlabel_parse(char *arg, __u32 *flowlabel,
>  				   __u32 *flowlabel_mask)
>  {
> @@ -746,35 +872,17 @@
>  				invarg("Invalid \"ipproto\" value\n", *argv);
>  			filter.ipproto = ipproto;
>  		} else if (strcmp(*argv, "sport") == 0) {
> -			struct fib_rule_port_range r;
> -			int ret;
> -
>  			NEXT_ARG();
> -			ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
> -			if (ret == 1)
> -				r.end = r.start;
> -			else if (ret != 2)
> -				invarg("invalid port range\n", *argv);
> -			filter.sport = r;
> +			iprule_port_parse(*argv, &filter.sport,
> +					  &filter.sport_mask);
>  		} else if (strcmp(*argv, "dport") == 0) {
> -			struct fib_rule_port_range r;
> -			int ret;
> -
>  			NEXT_ARG();
> -			ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
> -			if (ret == 1)
> -				r.end = r.start;
> -			else if (ret != 2)
> -				invarg("invalid dport range\n", *argv);
> -			filter.dport = r;
> +			iprule_port_parse(*argv, &filter.dport,
> +					  &filter.dport_mask);
>  		} else if (strcmp(*argv, "dscp") == 0) {
> -			__u32 dscp;
> -
>  			NEXT_ARG();
> -			if (rtnl_dscp_a2n(&dscp, *argv))
> -				invarg("invalid dscp\n", *argv);
> -			filter.dscp = dscp;
> -			filter.dscpmask = 1;
> +			iprule_dscp_parse(*argv, &filter.dscp,
> +					  &filter.dscpmask);
>  		} else if (strcmp(*argv, "flowlabel") == 0) {
>  			NEXT_ARG();
>  
> @@ -1036,35 +1144,35 @@
>  			addattr8(&req.n, sizeof(req), FRA_IP_PROTO, ipproto);
>  		} else if (strcmp(*argv, "sport") == 0) {
>  			struct fib_rule_port_range r;
> -			int ret = 0;
> +			__u16 sport_mask;
>  
>  			NEXT_ARG();
> -			ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
> -			if (ret == 1)
> -				r.end = r.start;
> -			else if (ret != 2)
> -				invarg("invalid port range\n", *argv);
> +			iprule_port_parse(*argv, &r, &sport_mask);
>  			addattr_l(&req.n, sizeof(req), FRA_SPORT_RANGE, &r,
>  				  sizeof(r));
> +			if (sport_mask != PORT_MAX_MASK)
> +				addattr16(&req.n, sizeof(req), FRA_SPORT_MASK,
> +					  sport_mask);
>  		} else if (strcmp(*argv, "dport") == 0) {
>  			struct fib_rule_port_range r;
> -			int ret = 0;
> +			__u16 dport_mask;
>  
>  			NEXT_ARG();
> -			ret = sscanf(*argv, "%hu-%hu", &r.start, &r.end);
> -			if (ret == 1)
> -				r.end = r.start;
> -			else if (ret != 2)
> -				invarg("invalid dport range\n", *argv);
> +			iprule_port_parse(*argv, &r, &dport_mask);
>  			addattr_l(&req.n, sizeof(req), FRA_DPORT_RANGE, &r,
>  				  sizeof(r));
> +			if (dport_mask != PORT_MAX_MASK)
> +				addattr16(&req.n, sizeof(req), FRA_DPORT_MASK,
> +					  dport_mask);
>  		} else if (strcmp(*argv, "dscp") == 0) {
> -			__u32 dscp;
> +			__u32 dscp, dscp_mask;
>  
>  			NEXT_ARG();
> -			if (rtnl_dscp_a2n(&dscp, *argv))
> -				invarg("invalid dscp\n", *argv);
> +			iprule_dscp_parse(*argv, &dscp, &dscp_mask);
>  			addattr8(&req.n, sizeof(req), FRA_DSCP, dscp);
> +			if (dscp_mask != DSCP_MAX_MASK)
> +				addattr8(&req.n, sizeof(req), FRA_DSCP_MASK,
> +					 dscp_mask);
>  		} else if (strcmp(*argv, "flowlabel") == 0) {
>  			__u32 flowlabel, flowlabel_mask;
>  
> diff -Nru iproute2-6.14.0/ip/ipxfrm.c iproute2-6.15.0/ip/ipxfrm.c
> --- iproute2-6.14.0/ip/ipxfrm.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/ip/ipxfrm.c	2025-05-26 16:19:09.000000000 +0100
> @@ -351,7 +351,10 @@
>  		t = (long)time;
>  		tp = localtime(&t);
>  
> -		strftime(str, sizeof(str), "%Y-%m-%d %T", tp);
> +		if (!tp)
> +			strcpy(str, "invalid-time");
> +		else
> +			strftime(str, sizeof(str), "%Y-%m-%d %T", tp);
>  	}
>  
>  	return str;
> diff -Nru iproute2-6.14.0/lib/color.c iproute2-6.15.0/lib/color.c
> --- iproute2-6.14.0/lib/color.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/lib/color.c	2025-05-26 16:19:09.000000000 +0100
> @@ -81,6 +81,18 @@
>  	set_color_palette();
>  }
>  
> +int default_color_opt(void)
> +{
> +	const char *no_color;
> +
> +	/* If NO_COLOR has a non-empty value, coloured output is never wanted */
> +	no_color = getenv("NO_COLOR");
> +	if (no_color && *no_color)
> +		return COLOR_OPT_NEVER;
> +
> +	return CONF_COLOR;
> +}
> +
>  bool check_enable_color(int color, int json)
>  {
>  	if (json || color == COLOR_OPT_NEVER)
> diff -Nru iproute2-6.14.0/lib/utils.c iproute2-6.15.0/lib/utils.c
> --- iproute2-6.14.0/lib/utils.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/lib/utils.c	2025-05-26 16:19:09.000000000 +0100
> @@ -304,10 +304,6 @@
>  	if (res == ULLONG_MAX && errno == ERANGE)
>  		return -1;
>  
> -	/* in case ULL is 128 bits */
> -	if (res > 0xFFFFFFFFFFFFFFFFULL)
> -		return -1;
> -
>  	*val = res;
>  	return 0;
>  }
> @@ -399,8 +395,6 @@
>  		return -1;
>  	if ((res == LLONG_MIN || res == LLONG_MAX) && errno == ERANGE)
>  		return -1;
> -	if (res > INT64_MAX || res < INT64_MIN)
> -		return -1;
>  
>  	*val = res;
>  	return 0;
> diff -Nru iproute2-6.14.0/MAINTAINERS iproute2-6.15.0/MAINTAINERS
> --- iproute2-6.14.0/MAINTAINERS	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/MAINTAINERS	2025-05-26 16:19:09.000000000 +0100
> @@ -26,7 +26,7 @@
>  L: netdev@vger.kernel.org
>  
>  Ethernet Bridging - bridge
> -M: Roopa Prabhu <roopa@nvidia.com>
> +M: Ido Schimmel <idosch@nvidia.com>
>  M: Nikolay Aleksandrov <razor@blackwall.org>
>  L: bridge@lists.linux-foundation.org (moderated for non-subscribers)
>  F: bridge/*
> diff -Nru iproute2-6.14.0/man/man8/ip-link.8.in iproute2-6.15.0/man/man8/ip-link.8.in
> --- iproute2-6.14.0/man/man8/ip-link.8.in	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/man/man8/ip-link.8.in	2025-05-26 16:19:09.000000000 +0100
> @@ -882,10 +882,14 @@
>  [
>  .BI mode " MODE "
>  ] [
> +.BI scrub " SCRUB "
> +] [
>  .I "POLICY "
>  ] [
>  .BR peer
>  [
> +.BI scrub " SCRUB "
> +] [
>  .I "POLICY "
>  ] [
>  .I "NAME "
> @@ -899,6 +903,17 @@
>  as possible values. Default option is "l3".
>  
>  .sp
> +.BI scrub " SCRUB"
> +- specifies the scrub behavior of the netkit device with "default" and
> +"none" as possible values. With "default" the device zeroes the
> +skb->{mark,priority} fields before invoking the attached BPF program
> +when its peer device resides in a different network namespace. With
> +"none" the device leaves clearing skb->{mark,priority} up to the BPF
> +program. Default option is "default". Specifying scrub before the peer
> +option refers to the primary device, after the peer option refers to
> +the peer device.
> +
> +.sp
>  .I "POLICY"
>  - specifies the default device policy when no BPF programs are attached
>  with "forward" and "blackhole" as possible values. Default option is
> diff -Nru iproute2-6.14.0/man/man8/ip-rule.8.in iproute2-6.15.0/man/man8/ip-rule.8.in
> --- iproute2-6.14.0/man/man8/ip-rule.8.in	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/man/man8/ip-rule.8.in	2025-05-26 16:19:09.000000000 +0100
> @@ -37,7 +37,7 @@
>  .B  tos
>  .IR TOS " ] [ "
>  .B  dscp
> -.IR DSCP " ] [ "
> +.IR DSCP\fR[\fB/\fIMASK "] ] [ "
>  .B  fwmark
>  .IR FWMARK\fR[\fB/\fIMASK "] ] [ "
>  .B  iif
> @@ -52,10 +52,10 @@
>  .B ipproto
>  .IR PROTOCOL " ] [ "
>  .BR sport " [ "
> -.IR NUMBER " | "
> +.IR NUMBER\fR[\fB/\fIMASK "] | "
>  .IR NUMBER "-" NUMBER " ] ] [ "
>  .BR dport " [ "
> -.IR NUMBER " | "
> +.IR NUMBER\fR[\fB/\fIMASK "] | "
>  .IR NUMBER "-" NUMBER " ] ] [ "
>  .B  tun_id
>  .IR TUN_ID " ] [ "
> @@ -239,9 +239,10 @@
>  select the TOS value to match.
>  
>  .TP
> -.BI dscp " DSCP"
> -select the DSCP value to match. DSCP values can be written either directly as
> -numeric values (valid values are 0-63), or using symbolic names specified in
> +.BI dscp " DSCP\fR[\fB/\fIMASK\fR]"
> +select the DSCP value to match with an optional mask. DSCP values can be
> +written either directly as numeric values (valid values are 0-63), or using
> +symbolic names specified in
>  .BR @SYSCONF_USR_DIR@/rt_dsfield " or " @SYSCONF_ETC_DIR@/rt_dsfield
>  (has precedence if exists).
>  However, note that the file specifies full 8-bit dsfield values, whereas
> @@ -270,12 +271,14 @@
>  select the ip protocol value to match.
>  
>  .TP
> -.BI sport " NUMBER | NUMBER-NUMBER"
> -select the source port value to match. supports port range.
> +.BI sport " NUMBER\fR[\fB/\fIMASK\fR] | NUMBER-NUMBER"
> +select the source port value to match with an optional mask. Supports port
> +range.
>  
>  .TP
> -.BI dport " NUMBER | NUMBER-NUMBER"
> -select the destination port value to match. supports port range.
> +.BI dport " NUMBER\fR[\fB/\fIMASK\fR] | NUMBER-NUMBER"
> +select the destination port value to match with an optional mask. Supports port
> +range.
>  
>  .TP
>  .BI priority " PREFERENCE"
> diff -Nru iproute2-6.14.0/man/man8/rdma-statistic.8 iproute2-6.15.0/man/man8/rdma-statistic.8
> --- iproute2-6.14.0/man/man8/rdma-statistic.8	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/man/man8/rdma-statistic.8	2025-05-26 16:19:09.000000000 +0100
> @@ -39,6 +39,7 @@
>  .B auto
>  .RI "{ " CRITERIA " | "
>  .BR off " }"
> +.B [ optional-counters | on/off ]
>  
>  .ti -8
>  .B rdma statistic
> @@ -180,6 +181,11 @@
>  On device mlx5_2 port 1, for each new user QP bind it with a counter automatically. Per counter for QPs with same qp type.
>  .RE
>  .PP
> +rdma statistic qp set link mlx5_2/1 auto type on optional-counters on
> +.RS 4
> +On device mlx5_2 port 1, for each new user QP bind it with a counter automatically. Per counter for QPs with same qp type. Whilst also binding the currently enabled optional-counters.
> +.RE
> +.PP
>  rdma statistic qp set link mlx5_2/1 auto pid on
>  .RS 4
>  On device mlx5_2 port 1, for each new user QP bind it with a counter automatically. Per counter for QPs with same pid.
> diff -Nru iproute2-6.14.0/misc/nstat.c iproute2-6.15.0/misc/nstat.c
> --- iproute2-6.14.0/misc/nstat.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/misc/nstat.c	2025-05-26 16:19:09.000000000 +0100
> @@ -218,6 +218,10 @@
>  			p = next;
>  		}
>  		n = db;
> +		if (n == NULL) {
> +			fprintf(stderr, "Error: Invalid input – line has ':' but no entries. Add values after ':'.\n");
> +			exit(-2);
> +		}
>  		nread = getline(&buf, &buflen, fp);
>  		if (nread == -1) {
>  			fprintf(stderr, "%s:%d: error parsing history file\n",
> diff -Nru iproute2-6.14.0/rdma/include/uapi/rdma/rdma_netlink.h iproute2-6.15.0/rdma/include/uapi/rdma/rdma_netlink.h
> --- iproute2-6.14.0/rdma/include/uapi/rdma/rdma_netlink.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/rdma/include/uapi/rdma/rdma_netlink.h	2025-05-26 16:19:09.000000000 +0100
> @@ -580,6 +580,8 @@
>  	RDMA_NLDEV_ATTR_EVENT_TYPE,		/* u8 */
>  
>  	RDMA_NLDEV_SYS_ATTR_MONITOR_MODE,	/* u8 */
> +
> +	RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED,	/* u8 */
>  	/*
>  	 * Always the end
>  	 */
> diff -Nru iproute2-6.14.0/rdma/stat.c iproute2-6.15.0/rdma/stat.c
> --- iproute2-6.14.0/rdma/stat.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/rdma/stat.c	2025-05-26 16:19:09.000000000 +0100
> @@ -7,6 +7,7 @@
>  #include "rdma.h"
>  #include "res.h"
>  #include "stat.h"
> +#include "utils.h"
>  #include <inttypes.h>
>  
>  static int stat_help(struct rd *rd)
> @@ -62,7 +63,8 @@
>  	{ NULL },
>  };
>  
> -static int prepare_auto_mode_str(uint32_t mask, char *output, int len)
> +static int prepare_auto_mode_str(uint32_t mask, bool opcnt, char *output,
> +				 int len)
>  {
>  	char s[] = "qp auto";
>  	int i, outlen = strlen(s);
> @@ -90,6 +92,10 @@
>  		if (outlen + strlen(" on") >= len)
>  			return -EINVAL;
>  		strcat(output, " on");
> +
> +		strcat(output, " optional-counters ");
> +		strcat(output, (opcnt) ? "on" : "off");
> +
>  	} else {
>  		if (outlen + strlen(" off") >= len)
>  			return -EINVAL;
> @@ -104,6 +110,7 @@
>  	struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {};
>  	uint32_t mode = 0, mask = 0;
>  	char output[128] = {};
> +	bool opcnt = false;
>  	uint32_t idx, port;
>  	const char *name;
>  
> @@ -126,7 +133,10 @@
>  		if (!tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK])
>  			return MNL_CB_ERROR;
>  		mask = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK]);
> -		prepare_auto_mode_str(mask, output, sizeof(output));
> +		if (tb[RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED])
> +			opcnt = mnl_attr_get_u8(
> +				tb[RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED]);
> +		prepare_auto_mode_str(mask, opcnt, output, sizeof(output));
>  	} else {
>  		snprintf(output, sizeof(output), "qp auto off");
>  	}
> @@ -351,6 +361,7 @@
>  	{ .name = "lqpn", .is_number = true },
>  	{ .name = "pid", .is_number = true },
>  	{ .name = "qp-type", .is_number = false },
> +	{ .name = "optional-counters", .is_number = false },
>  };
>  
>  static int stat_qp_show_one_link(struct rd *rd)
> @@ -395,9 +406,37 @@
>  	return rd_exec_cmd(rd, cmds, "parameter");
>  }
>  
> +static bool stat_get_on_off(struct rd *rd, const char *arg, int *ret)
> +{
> +	bool value = false;
> +
> +	if (strcmpx(rd_argv(rd), arg) != 0) {
> +		*ret = -EINVAL;
> +		return false;
> +	}
> +
> +	rd_arg_inc(rd);
> +
> +	if (rd_is_multiarg(rd)) {
> +		pr_err("The parameter %s shouldn't include range\n", arg);
> +		*ret = EINVAL;
> +		return false;
> +	}
> +
> +	value = parse_on_off(arg, rd_argv(rd), ret);
> +	if (*ret)
> +		return false;
> +
> +	rd_arg_inc(rd);
> +
> +	return value;
> +}
> +
>  static int stat_qp_set_link_auto_sendmsg(struct rd *rd, uint32_t mask)
>  {
>  	uint32_t seq;
> +	bool opcnt;
> +	int ret;
>  
>  	rd_prepare_msg(rd, RDMA_NLDEV_CMD_STAT_SET,
>  		       &seq, (NLM_F_REQUEST | NLM_F_ACK));
> @@ -408,6 +447,13 @@
>  	mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_MODE,
>  			 RDMA_COUNTER_MODE_AUTO);
>  	mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK, mask);
> +	if (rd_argc(rd)) {
> +		opcnt = stat_get_on_off(rd, "optional-counters", &ret);
> +		if (ret)
> +			return ret;
> +		mnl_attr_put_u8(rd->nlh, RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED,
> +				opcnt);
> +	}
>  
>  	return rd_sendrecv_msg(rd, seq);
>  }
> diff -Nru iproute2-6.14.0/rdma/utils.c iproute2-6.15.0/rdma/utils.c
> --- iproute2-6.14.0/rdma/utils.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/rdma/utils.c	2025-05-26 16:19:09.000000000 +0100
> @@ -479,6 +479,7 @@
>  	[RDMA_NLDEV_ATTR_PARENT_NAME] = MNL_TYPE_STRING,
>  	[RDMA_NLDEV_ATTR_EVENT_TYPE] = MNL_TYPE_U8,
>  	[RDMA_NLDEV_SYS_ATTR_MONITOR_MODE] = MNL_TYPE_U8,
> +	[RDMA_NLDEV_ATTR_STAT_OPCOUNTER_ENABLED] = MNL_TYPE_U8,
>  };
>  
>  static int rd_attr_check(const struct nlattr *attr, int *typep)
> diff -Nru iproute2-6.14.0/tc/tc.c iproute2-6.15.0/tc/tc.c
> --- iproute2-6.14.0/tc/tc.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/tc/tc.c	2025-05-26 16:19:09.000000000 +0100
> @@ -254,7 +254,7 @@
>  {
>  	const char *libbpf_version;
>  	char *batch_file = NULL;
> -	int color = CONF_COLOR;
> +	int color = default_color_opt();
>  	int ret;
>  
>  	while (argc > 1) {
> diff -Nru iproute2-6.14.0/tc/tc_core.c iproute2-6.15.0/tc/tc_core.c
> --- iproute2-6.14.0/tc/tc_core.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/tc/tc_core.c	2025-05-26 16:19:09.000000000 +0100
> @@ -23,12 +23,12 @@
>  static double tick_in_usec = 1;
>  static double clock_factor = 1;
>  
> -static unsigned int tc_core_time2tick(unsigned int time)
> +static double tc_core_time2tick(double time)
>  {
>  	return time * tick_in_usec;
>  }
>  
> -unsigned int tc_core_tick2time(unsigned int tick)
> +double tc_core_tick2time(double tick)
>  {
>  	return tick / tick_in_usec;
>  }
> @@ -45,7 +45,7 @@
>  
>  unsigned int tc_calc_xmittime(__u64 rate, unsigned int size)
>  {
> -	return tc_core_time2tick(TIME_UNITS_PER_SEC*((double)size/(double)rate));
> +	return ceil(tc_core_time2tick(TIME_UNITS_PER_SEC*((double)size/(double)rate)));
>  }
>  
>  unsigned int tc_calc_xmitsize(__u64 rate, unsigned int ticks)
> diff -Nru iproute2-6.14.0/tc/tc_core.h iproute2-6.15.0/tc/tc_core.h
> --- iproute2-6.14.0/tc/tc_core.h	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/tc/tc_core.h	2025-05-26 16:19:09.000000000 +0100
> @@ -12,7 +12,7 @@
>  };
>  
>  
> -unsigned tc_core_tick2time(unsigned tick);
> +double tc_core_tick2time(double tick);
>  unsigned tc_core_time2ktime(unsigned time);
>  unsigned tc_core_ktime2time(unsigned ktime);
>  unsigned tc_calc_xmittime(__u64 rate, unsigned size);
> diff -Nru iproute2-6.14.0/tc/tc_util.c iproute2-6.15.0/tc/tc_util.c
> --- iproute2-6.14.0/tc/tc_util.c	2025-03-24 16:04:44.000000000 +0000
> +++ iproute2-6.15.0/tc/tc_util.c	2025-05-26 16:19:09.000000000 +0100
> @@ -665,7 +665,8 @@
>  			   tm->expires / hz);
>  }
>  
> -static void print_tcstats_basic_hw(struct rtattr **tbs, const char *prefix)
> +static void print_tcstats_basic_hw(struct rtattr **tbs, const char *prefix,
> +				   __u64 packets64, __u64 packets64_hw)
>  {
>  	struct gnet_stats_basic bs_hw;
>  
> @@ -674,8 +675,9 @@
>  
>  	memcpy(&bs_hw, RTA_DATA(tbs[TCA_STATS_BASIC_HW]),
>  	       MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC_HW]), sizeof(bs_hw)));
> +	packets64_hw = packets64_hw ? : bs_hw.packets;
>  
> -	if (bs_hw.bytes == 0 && bs_hw.packets == 0)
> +	if (bs_hw.bytes == 0 && packets64_hw == 0)
>  		return;
>  
>  	if (tbs[TCA_STATS_BASIC]) {
> @@ -684,15 +686,16 @@
>  		memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]),
>  		       MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]),
>  			   sizeof(bs)));
> +		packets64 = packets64 ? : bs.packets;
>  
> -		if (bs.bytes >= bs_hw.bytes && bs.packets >= bs_hw.packets) {
> +		if (bs.bytes >= bs_hw.bytes && packets64 >= packets64_hw) {
>  			print_nl();
>  			print_string(PRINT_FP, NULL, "%s", prefix);
>  			print_lluint(PRINT_ANY, "sw_bytes",
>  				     "Sent software %llu bytes",
>  				     bs.bytes - bs_hw.bytes);
> -			print_uint(PRINT_ANY, "sw_packets", " %u pkt",
> -				   bs.packets - bs_hw.packets);
> +			print_lluint(PRINT_ANY, "sw_packets", " %llu pkt",
> +				     packets64 - packets64_hw);
>  		}
>  	}
>  
> @@ -700,21 +703,40 @@
>  	print_string(PRINT_FP, NULL, "%s", prefix);
>  	print_lluint(PRINT_ANY, "hw_bytes", "Sent hardware %llu bytes",
>  		     bs_hw.bytes);
> -	print_uint(PRINT_ANY, "hw_packets", " %u pkt", bs_hw.packets);
> +	print_lluint(PRINT_ANY, "hw_packets", " %llu pkt", packets64_hw);
> +}
> +
> +static void parse_packets64(const struct rtattr *nest, __u64 *p_packets64,
> +			    __u64 *p_packets64_hw)
> +{
> +	unsigned short prev_type = __TCA_STATS_MAX;
> +	const struct rtattr *pos;
> +
> +	/* 'TCA_STATS_PKT64' can appear twice in the 'TCA_ACT_STATS' nest.
> +	 * Whether the attribute carries the combined or hardware only
> +	 * statistics depends on the attribute that precedes it in the nest.
> +	 */
> +	rtattr_for_each_nested(pos, nest) {
> +		if (pos->rta_type == TCA_STATS_PKT64 &&
> +		    prev_type == TCA_STATS_BASIC)
> +			*p_packets64 = rta_getattr_u64(pos);
> +		else if (pos->rta_type == TCA_STATS_PKT64 &&
> +			 prev_type == TCA_STATS_BASIC_HW)
> +			*p_packets64_hw = rta_getattr_u64(pos);
> +		prev_type = pos->rta_type;
> +	}
>  }
>  
>  void print_tcstats2_attr(struct rtattr *rta, const char *prefix, struct rtattr **xstats)
>  {
>  	struct rtattr *tbs[TCA_STATS_MAX + 1];
> +	__u64 packets64 = 0, packets64_hw = 0;
>  
>  	parse_rtattr_nested(tbs, TCA_STATS_MAX, rta);
> +	parse_packets64(rta, &packets64, &packets64_hw);
>  
>  	if (tbs[TCA_STATS_BASIC]) {
>  		struct gnet_stats_basic bs = {0};
> -		__u64 packets64 = 0;
> -
> -		if (tbs[TCA_STATS_PKT64])
> -			packets64 = rta_getattr_u64(tbs[TCA_STATS_PKT64]);
>  
>  		memcpy(&bs, RTA_DATA(tbs[TCA_STATS_BASIC]),
>  		       MIN(RTA_PAYLOAD(tbs[TCA_STATS_BASIC]), sizeof(bs)));
> @@ -740,7 +762,7 @@
>  	}
>  
>  	if (tbs[TCA_STATS_BASIC_HW])
> -		print_tcstats_basic_hw(tbs, prefix);
> +		print_tcstats_basic_hw(tbs, prefix, packets64, packets64_hw);
>  
>  	if (tbs[TCA_STATS_RATE_EST64]) {
>  		struct gnet_stats_rate_est64 re = {0};


-- 
Sebastian Ramacher


Reply to: