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

Bug#1106602: unblock: iproute2/6.15.0-1



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

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};

Reply to: