Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net-next v3 3/5] ipv4: igmp: encode multicast exponential fields
From: Ido Schimmel @ 2026-04-07 13:44 UTC (permalink / raw)
  To: Ujjal Roy
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, netdev,
	linux-kernel, linux-kselftest
In-Reply-To: <20260403150050.1235-4-royujjal@gmail.com>

On Fri, Apr 03, 2026 at 03:00:48PM +0000, Ujjal Roy wrote:
> In IGMP, QQIC and MRC fields are not correctly encoded
> when generating query packets. Since the receiver of the
> query interprets these fields using the IGMPv3 floating-
> point decoding logic, any value that exceeds the linear
> threshold is incorrectly parsed as an exponential value,
> leading to an incorrect interval calculation.
> 
> Encode and assign the corresponding protocol fields during
> query generation. Introduce the logic to dynamically
> calculate the exponent and mantissa using bit-scan (fls).
> This ensures QQIC and MRC fields (8-bit) are properly
> encoded when transmitting query packets with intervals
> that exceed their respective linear threshold value of
> 128 (for QQI/MRT).
> 
> RFC 3376: if QQIC/MRC >= 128, the QQIC/MRC field represents
> a floating-point value as follows:
>      0 1 2 3 4 5 6 7
>     +-+-+-+-+-+-+-+-+
>     |1| exp | mant  |
>     +-+-+-+-+-+-+-+-+
> 
> Signed-off-by: Ujjal Roy <royujjal@gmail.com>

Same comments as on the previous patches, but otherwise LGTM

^ permalink raw reply

* Re: [PATCH net-next v3 4/5] ipv6: mld: encode multicast exponential fields
From: Ido Schimmel @ 2026-04-07 13:45 UTC (permalink / raw)
  To: Ujjal Roy
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, netdev,
	linux-kernel, linux-kselftest
In-Reply-To: <20260403150050.1235-5-royujjal@gmail.com>

On Fri, Apr 03, 2026 at 03:00:49PM +0000, Ujjal Roy wrote:
> In MLD, QQIC and MRC fields are not correctly encoded when
> generating query packets. Since the receiver of the query
> interprets these fields using the MLDv2 floating-point
> decoding logic, any value that exceeds the linear threshold
> is incorrectly parsed as an exponential value, leading to
> an incorrect interval calculation.
> 
> Encode and assign the corresponding protocol fields during
> query generation. Introduce the logic to dynamically
> calculate the exponent and mantissa using bit-scan (fls).
> This ensures QQIC (8-bit) and MRC (16-bit) fields are
> properly encoded when transmitting query packets with
> intervals that exceed their respective linear thresholds
> (128 for QQI; 32768 for MRD).
> 
> RFC3810: If QQIC >= 128, the QQIC field represents a
> floating-point value as follows:
>      0 1 2 3 4 5 6 7
>     +-+-+-+-+-+-+-+-+
>     |1| exp | mant  |
>     +-+-+-+-+-+-+-+-+
> 
> RFC3810: If Maximum Response Code >= 32768, the Maximum
> Response Code field represents a floating-point value as
> follows:
>      0 1 2 3 4 5 6 7 8 9 A B C D E F
>     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>     |1| exp |          mant         |
>     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> 
> Signed-off-by: Ujjal Roy <royujjal@gmail.com>

Reviewed-by: Ido Schimmel <idosch@nvidia.com>

^ permalink raw reply

* Re: [PATCH net-next v3 5/5] selftests: net: bridge: add tests for MRC and QQIC validation
From: Ido Schimmel @ 2026-04-07 13:45 UTC (permalink / raw)
  To: Ujjal Roy
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Nikolay Aleksandrov, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata, Ujjal Roy, bridge, netdev,
	linux-kernel, linux-kselftest
In-Reply-To: <20260403150050.1235-6-royujjal@gmail.com>

On Fri, Apr 03, 2026 at 03:00:50PM +0000, Ujjal Roy wrote:
> Update bridge selftests that configure Max Resp Time (MRT) and Querier
> Query Interval (QQI) parameters and validate the resulting Query packet
> fields for Max Resp Code (MRC) and Querier Query Interval Code (QQIC).
> 
> These tests cover the IGMPv3 and MLDv2 for below cases:
> * MRC and QQIC in linear range.
> * MRC and QQIC in non-linear range.
> 
> TEST: Vlan multicast snooping enable                                [ OK ]
> TEST: Vlan mcast_query_interval global option default value         [ OK ]
> Vlan 10 mcast_query_interval (QQIC) test cases:
> TEST: Number of tagged IGMPv2 general query                         [ OK ]
> TEST: IGMPv3 QQIC linear value 60                                   [ OK ]
> TEST: MLDv2 QQIC linear value 60                                    [ OK ]
> TEST: IGMPv3 QQIC non linear value 160                              [ OK ]
> TEST: MLDv2 QQIC non linear value 160                               [ OK ]
> TEST: Vlan mcast_query_response_interval global option default value   [ OK ]
> Vlan 10 mcast_query_response_interval (MRC) test cases:
> TEST: IGMPv3 MRC linear value 60                                    [ OK ]
> TEST: IGMPv3 MRC non linear value 160                               [ OK ]
> TEST: MLDv2 MRC linear value 30000                                  [ OK ]
> TEST: MLDv2 MRC non linear value 60000                              [ OK ]
> 
> Signed-off-by: Ujjal Roy <royujjal@gmail.com>
> ---
>  .../selftests/net/forwarding/.gitignore       |   2 +
>  .../testing/selftests/net/forwarding/Makefile |  10 ++
>  .../net/forwarding/bridge_vlan_mcast.sh       | 157 +++++++++++++++++-
>  .../selftests/net/forwarding/mc_decode.c      |  73 ++++++++
>  .../selftests/net/forwarding/mc_encode.c      |  78 +++++++++
>  5 files changed, 315 insertions(+), 5 deletions(-)
>  create mode 100644 tools/testing/selftests/net/forwarding/mc_decode.c
>  create mode 100644 tools/testing/selftests/net/forwarding/mc_encode.c
> 
> diff --git a/tools/testing/selftests/net/forwarding/.gitignore b/tools/testing/selftests/net/forwarding/.gitignore
> index 418ff96c52ef..aa0c7f1afb4b 100644
> --- a/tools/testing/selftests/net/forwarding/.gitignore
> +++ b/tools/testing/selftests/net/forwarding/.gitignore
> @@ -1,3 +1,5 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  forwarding.config
>  ipmr
> +mc_encode

Do we really need this binary and all the added complexity? You can just
hard code in the test the expected encoded form of the non-linear values

> +mc_decode

This one doesn't seem to be used

> diff --git a/tools/testing/selftests/net/forwarding/Makefile b/tools/testing/selftests/net/forwarding/Makefile
> index bbaf4d937dd8..a26da846632d 100644
> --- a/tools/testing/selftests/net/forwarding/Makefile
> +++ b/tools/testing/selftests/net/forwarding/Makefile
> @@ -1,5 +1,15 @@
>  # SPDX-License-Identifier: GPL-2.0+ OR MIT
>  
> +top_srcdir = ../../../../..
> +
> +CFLAGS += -Wall -Wl,--no-as-needed -O2 -g -I$(top_srcdir)/usr/include $(KHDR_INCLUDES)
> +CFLAGS += -I$(top_srcdir)/tools/include
> +
> +TEST_GEN_FILES := \
> +	mc_encode \
> +	mc_decode \
> +# end of TEST_GEN_FILES
> +
>  TEST_PROGS := \
>  	bridge_activity_notify.sh \
>  	bridge_fdb_learning_limit.sh \
> diff --git a/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh b/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh
> index 72dfbeaf56b9..89598712f869 100755
> --- a/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh
> +++ b/tools/testing/selftests/net/forwarding/bridge_vlan_mcast.sh
> @@ -5,6 +5,7 @@ ALL_TESTS="vlmc_control_test vlmc_querier_test vlmc_igmp_mld_version_test \
>  	   vlmc_last_member_test vlmc_startup_query_test vlmc_membership_test \
>  	   vlmc_querier_intvl_test vlmc_query_intvl_test vlmc_query_response_intvl_test \
>  	   vlmc_router_port_test vlmc_filtering_test"
> +TEST_NAME=""

Why is this needed? Just pass to vlmc_query_get_intvl_match() an
argument that indicates if we need MRC / QQIC

>  NUM_NETIFS=4
>  CHECK_TC="yes"
>  TEST_GROUP="239.10.10.10"
> @@ -96,6 +97,17 @@ cleanup()
>  	vrf_cleanup
>  }
>  
> +check_binary()
> +{
> +	local cmd=$1; shift
> +	local args=$@
> +
> +	if [[ ! -x "$(command -v "$cmd")" ]]; then
> +		log_test_skip "$args $cmd not found"
> +		return $EXIT_STATUS
> +	fi
> +}

This can be removed as well

> +
>  vlmc_v2join_test()
>  {
>  	local expect=$1
> @@ -162,14 +174,27 @@ vlmc_query_cnt_setup()
>  {
>  	local type=$1
>  	local dev=$2
> +	local intvl_match="$3"
>  
>  	if [[ $type == "igmp" ]]; then
> +		# This matches: IP Protocol 2 (IGMP)
>  		tc filter add dev $dev egress pref 10 prot 802.1Q \
>  			flower vlan_id 10 vlan_ethtype ipv4 dst_ip 224.0.0.1 ip_proto 2 \
> +			action continue
> +		# AND Type 0x11 (Query) at offset 24 after IP
> +		# IP (20 byte IP + 4 bytes Option)
> +		tc filter add dev $dev egress pref 20 prot 802.1Q u32 \
> +			match u8 0x11 0xff at 24 $intvl_match \
>  			action pass
>  	else
> +		# This matches: ICMPv6
>  		tc filter add dev $dev egress pref 10 prot 802.1Q \
>  			flower vlan_id 10 vlan_ethtype ipv6 dst_ip ff02::1 ip_proto icmpv6 \
> +			action continue
> +		# AND Type 0x82 (Query) at offset 48 after IPv6
> +		# IPv6 (40 bytes IPv6 + 2 bytes next HDR + 4 bytes Option + 2 byte pad)
> +		tc filter add dev $dev egress pref 20 prot 802.1Q u32 \
> +			match u8 0x82 0xff at 48 $intvl_match \
>  			action pass
>  	fi
>  
> @@ -181,9 +206,46 @@ vlmc_query_cnt_cleanup()
>  	local dev=$1
>  
>  	ip link set dev br0 type bridge mcast_stats_enabled 0
> +	tc filter del dev $dev egress pref 20
>  	tc filter del dev $dev egress pref 10
>  }
>  
> +vlmc_query_get_intvl_match()
> +{
> +	local type=$1
> +	local version=$2
> +	local interval=$3
> +	local encode=""
> +
> +	if [ "$interval" = "" ]; then
> +		return
> +	fi
> +
> +	if [ "$TEST_NAME" = "vlmc_query_intvl_test" ]; then
> +		# QQIC is 8-bit floating point encoding for IGMPv3 and MLDv2
> +		encode="$(./mc_encode 8 $interval)"
> +		if [ "${type}v${version}" = "igmpv3" ]; then
> +			# IP 20 bytes + 4 bytes Option + IGMPv3[9]
> +			echo "match u8 $encode 0xff at 33"
> +		elif [ "${type}v${version}" = "mldv2" ]; then
> +			# IPv6 40 + 2 next HDR + 4 Option + 2 pad + MLDv2[25]
> +			echo "match u8 $encode 0xff at 73"
> +		fi
> +	elif [ "$TEST_NAME" = "vlmc_query_response_intvl_test" ]; then
> +		if [ "${type}v${version}" = "igmpv3" ]; then
> +			# MRC is 8-bit floating point encoding for MLDv2
> +			encode="$(./mc_encode 8 $interval)"
> +			# IP 20 bytes + 4 bytes Option + IGMPv3[1]
> +			echo "match u8 $encode 0xff at 25"
> +		elif [ "${type}v${version}" = "mldv2" ]; then
> +			# MRC is 16-bit floating point encoding for MLDv2
> +			encode="$(./mc_encode 16 $interval)"
> +			# IPv6 40 + 2 next HDR + 4 Option + 2 pad + MLDv2[4]
> +			echo "match u16 $encode 0xffff at 52"
> +		fi
> +	fi
> +}
> +
>  vlmc_check_query()
>  {
>  	local type=$1
> @@ -191,9 +253,12 @@ vlmc_check_query()
>  	local dev=$3
>  	local expect=$4
>  	local time=$5
> +	local interval=$6
> +	local intvl_match=""
>  	local ret=0
>  
> -	vlmc_query_cnt_setup $type $dev
> +	intvl_match="$(vlmc_query_get_intvl_match $type $version $interval)"
> +	vlmc_query_cnt_setup $type $dev "$intvl_match"

Did you run shellcheck? I believe it will issue a warning if you don't
quote "$type" and "$dev". I realize it differs from other places in the
test, but we try to avoid adding new warnings / errors.

>  
>  	local pre_tx_xstats=$(vlmc_query_cnt_xstats $type $version $dev)
>  	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_querier 1
> @@ -201,7 +266,7 @@ vlmc_check_query()
>  	if [[ $ret -eq 0 ]]; then
>  		sleep $time
>  
> -		local tcstats=$(tc_rule_stats_get $dev 10 egress)
> +		local tcstats=$(tc_rule_stats_get $dev 20 egress)
>  		local post_tx_xstats=$(vlmc_query_cnt_xstats $type $version $dev)
>  
>  		if [[ $tcstats != $expect || \
> @@ -428,6 +493,10 @@ vlmc_querier_intvl_test()
>  
>  vlmc_query_intvl_test()
>  {
> +	TEST_NAME="vlmc_query_intvl_test"
> +
> +	check_binary "./mc_encode" "$TEST_NAME: verify" || return 1
> +
>  	RET=0
>  	local goutput=`bridge -j vlan global show`
>  	echo -n $goutput |
> @@ -440,6 +509,7 @@ vlmc_query_intvl_test()
>  	check_err $? "Wrong default mcast_query_interval global vlan option value"
>  	log_test "Vlan mcast_query_interval global option default value"
>  
> +	echo "Vlan 10 mcast_query_interval (QQIC) test cases:"

log_info()

>  	RET=0
>  	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_startup_query_count 0
>  	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_interval 200
> @@ -447,14 +517,52 @@ vlmc_query_intvl_test()
>  	# 1 is sent immediately, then 2 more in the next 5 seconds
>  	vlmc_check_query igmp 2 $swp1 3 5
>  	check_err $? "Wrong number of tagged IGMPv2 general queries sent"
> -	log_test "Vlan 10 mcast_query_interval option changed to 200"
> +	log_test "Number of tagged IGMPv2 general query"
> +
> +	RET=0
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_igmp_version 3
> +	check_err $? "Could not set mcast_igmp_version in vlan 10"
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_mld_version 2
> +	check_err $? "Could not set mcast_mld_version in vlan 10"
> +
> +	RET=0

What's the point in the check_err() above if there is no log_test()
before resetting RET? Let's remove them

> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_interval 6000
> +	check_err $? "Could not set mcast_query_interval in vlan 10"
> +	# 1 is sent immediately, IGMPv3 QQIC should match with linear value 60s
> +	vlmc_check_query igmp 3 $swp1 1 1 60
> +	check_err $? "Wrong QQIC in sent tagged IGMPv3 general queries"
> +	log_test "IGMPv3 QQIC linear value 60"
> +	RET=0
> +	# 1 is sent immediately, MLDv2 QQIC should match with linear value 60s
> +	vlmc_check_query mld 2 $swp1 1 1 60
> +	check_err $? "Wrong QQIC in sent tagged MLDv2 general queries"
> +	log_test "MLDv2 QQIC linear value 60"
>  
> +	RET=0
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_interval 16000
> +	check_err $? "Could not set mcast_query_interval in vlan 10"
> +	# 1 is sent immediately, IGMPv3 QQIC should match with non linear value 160s
> +	vlmc_check_query igmp 3 $swp1 1 1 160
> +	check_err $? "Wrong QQIC in sent tagged IGMPv3 general queries"
> +	log_test "IGMPv3 QQIC non linear value 160"
> +	RET=0
> +	# 1 is sent immediately, MLDv2 QQIC should match with non linear value 160s
> +	vlmc_check_query mld 2 $swp1 1 1 160
> +	check_err $? "Wrong QQIC in sent tagged MLDv2 general queries"
> +	log_test "MLDv2 QQIC non linear value 160"
> +
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_igmp_version 2
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_mld_version 1
>  	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_startup_query_count 2
>  	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_interval 12500
>  }
>  
>  vlmc_query_response_intvl_test()
>  {
> +	TEST_NAME="vlmc_query_response_intvl_test"
> +
> +	check_binary "./mc_encode" "$TEST_NAME: verify" || return 1
> +
>  	RET=0
>  	local goutput=`bridge -j vlan global show`
>  	echo -n $goutput |
> @@ -468,10 +576,49 @@ vlmc_query_response_intvl_test()
>  	log_test "Vlan mcast_query_response_interval global option default value"
>  
>  	RET=0
> -	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_response_interval 200
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_startup_query_count 0
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_igmp_version 3
> +	check_err $? "Could not set mcast_igmp_version in vlan 10"

Can be removed

> +
> +	echo "Vlan 10 mcast_query_response_interval (MRC) test cases:"

log_info()

> +	RET=0
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_response_interval 600
> +	check_err $? "Could not set mcast_query_response_interval in vlan 10"
> +	# 1 is sent immediately, IGMPv3 MRC should match with linear value 60 units of 1/10s
> +	vlmc_check_query igmp 3 $swp1 1 1 60
> +	check_err $? "Wrong MRC in sent tagged IGMPv3 general queries"
> +	log_test "IGMPv3 MRC linear value 60"
> +
> +	RET=0
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_response_interval 1600
> +	check_err $? "Could not set mcast_query_response_interval in vlan 10"
> +	# 1 is sent immediately, IGMPv3 MRC should match with non linear value 160 unit of 1/10s
> +	vlmc_check_query igmp 3 $swp1 1 1 160
> +	check_err $? "Wrong MRC in sent tagged IGMPv3 general queries"
> +	log_test "IGMPv3 MRC non linear value 160"
> +
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_mld_version 2
> +	check_err $? "Could not set mcast_mld_version in vlan 10"
> +
> +	RET=0
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_response_interval 3000
> +	check_err $? "Could not set mcast_query_response_interval in vlan 10"
> +	# 1 is sent immediately, MLDv2 MRC should match with linear value 30000(ms)
> +	vlmc_check_query mld 2 $swp1 1 1 30000
> +	check_err $? "Wrong MRC in sent tagged MLDv2 general queries"
> +	log_test "MLDv2 MRC linear value 30000"
> +
> +	RET=0
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_response_interval 6000
>  	check_err $? "Could not set mcast_query_response_interval in vlan 10"
> -	log_test "Vlan 10 mcast_query_response_interval option changed to 200"
> +	# 1 is sent immediately, MLDv2 MRC should match with non linear value 60000(ms)
> +	vlmc_check_query mld 2 $swp1 1 1 60000
> +	check_err $? "Wrong MRC in sent tagged MLDv2 general queries"
> +	log_test "MLDv2 MRC non linear value 60000"
>  
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_igmp_version 2
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_mld_version 1
> +	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_startup_query_count 2
>  	bridge vlan global set vid 10 dev br0 mcast_snooping 1 mcast_query_response_interval 1000
>  }
>  
> diff --git a/tools/testing/selftests/net/forwarding/mc_decode.c b/tools/testing/selftests/net/forwarding/mc_decode.c
> new file mode 100644
> index 000000000000..2c5e784226e4
> --- /dev/null
> +++ b/tools/testing/selftests/net/forwarding/mc_decode.c
> @@ -0,0 +1,73 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <linux/bitops.h>
> +
> +/* 8-bit floating-point exponential field decode */
> +#define FP_8BIT_EXP(value)		(((value) >> 4) & 0x07)
> +#define FP_8BIT_MAN(value)		((value) & 0x0f)
> +
> +/* 16-bit floating-point exponential field decode */
> +#define FP_16BIT_EXP(value)		(((value) >> 12) & 0x0007)
> +#define FP_16BIT_MAN(value)		((value) & 0x0fff)
> +
> +/* 8-bit floating-point exponential field linear threshold */
> +#define FP_8BIT_MIN_THRESHOLD		128
> +/* 8-bit non linear max representable (mant = 0xF, exp = 7) -> 31744 */
> +#define FP_8BIT_MAX_THRESHOLD		31744
> +
> +/* 16-bit floating-point exponential field linear threshold */
> +#define FP_16BIT_MIN_THRESHOLD		32768UL
> +/* 16-bit non linear max representable (mant = 0xFFF, exp = 7) -> 8387584 */
> +#define FP_16BIT_MAX_THRESHOLD		8387584
> +
> +/* This decodes 8-bit floating-point exponential values */
> +static inline uint32_t decode_8bit_field(const u8 code)
> +{
> +	if (code < FP_8BIT_MIN_THRESHOLD) {
> +		return code;
> +	} else {
> +		uint32_t mc_man, mc_exp;
> +
> +		mc_exp = FP_8BIT_EXP(code);
> +		mc_man = FP_8BIT_MAN(code);
> +		return (mc_man | 0x10) << (mc_exp + 3);
> +	}
> +}
> +
> +/* This decodes 16-bit floating-point exponential values */
> +static inline uint32_t decode_16bit_field(const uint16_t code)
> +{
> +	if (code < FP_16BIT_MIN_THRESHOLD) {
> +		return code;
> +	} else {
> +		uint32_t mc_man, mc_exp;
> +
> +		mc_exp = FP_16BIT_EXP(code);
> +		mc_man = FP_16BIT_MAN(code);
> +
> +		return (mc_man | 0x1000) << (mc_exp + 3);
> +	}
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	uint32_t bits = 8, code = 0, decode = 0;
> +
> +	if (argc != 3)
> +		return 1;
> +
> +	if (bits != 8 && bits != 16)
> +		return 1;
> +
> +	bits = atoi(argv[1]);
> +	code = atoi(argv[2]);
> +
> +	if (bits == 8)
> +		decode = decode_8bit_field(code);
> +	else
> +		decode = decode_16bit_field(code);
> +	printf("%u\n", decode);
> +
> +	return 0;
> +}
> diff --git a/tools/testing/selftests/net/forwarding/mc_encode.c b/tools/testing/selftests/net/forwarding/mc_encode.c
> new file mode 100644
> index 000000000000..24d9bd9299cc
> --- /dev/null
> +++ b/tools/testing/selftests/net/forwarding/mc_encode.c
> @@ -0,0 +1,78 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <linux/bitops.h>
> +
> +/* 8-bit floating-point exponential field linear threshold */
> +#define FP_8BIT_MIN_THRESHOLD		128
> +/* 8-bit non linear max representable (mant = 0xF, exp = 7) -> 31744 */
> +#define FP_8BIT_MAX_THRESHOLD		31744
> +
> +/* 16-bit floating-point exponential field linear threshold */
> +#define FP_16BIT_MIN_THRESHOLD		32768UL
> +/* 16-bit non linear max representable (mant = 0xFFF, exp = 7) -> 8387584 */
> +#define FP_16BIT_MAX_THRESHOLD		8387584
> +
> +/* This encodes value to 8-bit floating-point exponential format */
> +static inline uint8_t encode_8bit_field(unsigned int value)
> +{
> +	uint8_t mc_exp, mc_man;
> +
> +	/* Value < 128 is literal */
> +	if (value < FP_8BIT_MIN_THRESHOLD)
> +		return value;
> +
> +	/* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */
> +	if (value >= FP_8BIT_MAX_THRESHOLD)
> +		return 0xFF;
> +
> +	mc_exp  = fls(value) - 8;
> +	mc_man = (value >> (mc_exp + 3)) & 0x0F;
> +
> +	return 0x80 | (mc_exp << 4) | mc_man;
> +}
> +
> +/* This encodes value to 16-bit floating-point exponential format */
> +static inline uint16_t encode_16bit_field(unsigned int value)
> +{
> +	uint16_t mc_man, mc_exp;
> +
> +	/* Value < 32768 is literal */
> +	if (value < FP_16BIT_MIN_THRESHOLD)
> +		return value;
> +
> +	/* Saturate at max representable (mant = 0xFFF, exp = 7) -> 8387584 */
> +	if (value >= FP_16BIT_MAX_THRESHOLD)
> +		return 0xFFFF;
> +
> +	mc_exp = fls(value) - 16;
> +	mc_man = (value >> (mc_exp + 3)) & 0x0FFF;
> +
> +	return 0x8000 | (mc_exp << 12) | mc_man;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	unsigned int bits = 8, value = 0;
> +	uint8_t encoded8 = 0;
> +	uint16_t encoded16 = 0;
> +
> +	if (argc != 3)
> +		return 1;
> +
> +	bits = atoi(argv[1]);
> +	value = atoi(argv[2]);
> +
> +	if (bits != 8 && bits != 16)
> +		return 1;
> +
> +	if (bits == 8) {
> +		encoded8 = encode_8bit_field(value);
> +		printf("%hhu\n", encoded8);
> +	} else {
> +		encoded16 = encode_16bit_field(value);
> +		printf("%hu\n", encoded16);
> +	}
> +
> +	return 0;
> +}
> -- 
> 2.43.0
> 

^ permalink raw reply

* Re: [PATCH net-next v5 0/2] net: mana: add ethtool private flag for full-page RX buffers
From: Dipayaan Roy @ 2026-04-07 13:54 UTC (permalink / raw)
  To: Alexander Lobakin
  Cc: kys, haiyangz, wei.liu, decui, andrew+netdev, davem, edumazet,
	kuba, pabeni, leon, longli, kotaranov, horms, shradhagupta,
	ssengar, ernis, shirazsaleem, linux-hyperv, netdev, linux-kernel,
	linux-rdma, stephen, jacob.e.keller, dipayanroy, leitao, kees
In-Reply-To: <e80b603d-8be0-4aee-8a31-c9cbb4a8ab00@intel.com>

On Tue, Apr 07, 2026 at 03:10:45PM +0200, Alexander Lobakin wrote:
> From: Dipayaan Roy <dipayanroy@linux.microsoft.com>
> Date: Sat, 4 Apr 2026 20:42:15 -0700
> 
> > On some ARM64 platforms with 4K PAGE_SIZE, utilizing page_pool 
> > fragments for allocation in the RX refill path (~2kB buffer per fragment)
> > causes 15-20% throughput regression under high connection counts
> > (>16 TCP streams at 180+ Gbps). Using full-page buffers on these
> > platforms shows no regression and restores line-rate performance.
> > 
> > This behavior is observed on a single platform; other platforms
> > perform better with page_pool fragments, indicating this is not a
> > page_pool issue but platform-specific.
> > 
> > This series adds an ethtool private flag "full-page-rx" to let the
> > user opt in to one RX buffer per page:
> > 
> >   ethtool --set-priv-flags eth0 full-page-rx on
> 
> Sorry I may've missed the previous threads.
> 
> Has this approach been discussed here? Private flags are generally
> discouraged.
> 
> Alternatively, you can provide Ethtool ops to change the Rx buffer size,
> so that you'd be able to set it to PAGE_SIZE on affected platforms and
> the result would be the same.
>
Hi Alex, 
This was discussed here:
https://lore.kernel.org/all/adHTm2SvjDrezEdv@linuxonhyperv3.guj3yctzbm1etfxqx2vob5hsef.xx.internal.cloudapp.net/ 
> > 
> > There is no behavioral change by default. The flag can be persisted
> > via udev rule for affected platforms.
> > 
> > Changes in v5:
> >   - Split prep refactor into separate patch (patch 1/2)
> > Changes in v4:
> >   - Dropping the smbios string parsing and add ethtool priv flag
> >     to reconfigure the queues with full page rx buffers.
> > Changes in v3:
> >   - changed u8* to char*
> > Changes in v2:
> >   - separate reading string index and the string, remove inline.
> > 
> > Dipayaan Roy (2):
> >   net: mana: refactor mana_get_strings() and mana_get_sset_count() to
> >     use switch
> >   net: mana: force full-page RX buffers via ethtool private flag
> > 
> >  drivers/net/ethernet/microsoft/mana/mana_en.c |  22 ++-
> >  .../ethernet/microsoft/mana/mana_ethtool.c    | 164 ++++++++++++++----
> >  include/net/mana/mana.h                       |   8 +
> >  3 files changed, 163 insertions(+), 31 deletions(-)
> 
> Thanks,
> Olek

^ permalink raw reply

* Re: [PATCH net-next] net: dropreason: add SKB_DROP_REASON_{BROAD,MULTI}CAST_BACKLOG
From: Jakub Kicinski @ 2026-04-07 14:04 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David S . Miller, Paolo Abeni, Simon Horman, Kuniyuki Iwashima,
	Andrew Lunn, netdev, eric.dumazet
In-Reply-To: <CANn89iJeijaiWZUeGfXUBBN-JjWRfB=afrPXP9=PH_vPSU2s6w@mail.gmail.com>

On Tue, 7 Apr 2026 03:09:41 -0700 Eric Dumazet wrote:
> On Mon, Apr 6, 2026 at 6:02 PM Jakub Kicinski <kuba@kernel.org> wrote:
> > On Fri,  3 Apr 2026 02:24:43 +0000 Eric Dumazet wrote:  
> > > ipvlan and macvlan use queues to process broadcast or multicast packets
> > > from a work queue.
> > >
> > > Under attack these queues can drop packets.
> > >
> > > Add BROADCAST_BACKLOG drop_reason for macvlan broadcast queue.
> > >
> > > Add MULTICAST_BACKLOG drop_reason for ipvlan multicast queue.  
> >
> > What distinction are you trying to communicate by using BROADCAST
> > vs MULTICAST? Funny naming of the functions aside I believe that
> > in both cases we're basically dealing with multicast traffic.
> > Reading the code it seems like macvlan has some optimization where
> > it delivers the frame inline if the multicast group is not very
> > popular. And if it is popular it schedules a work (and calls that
> > broadcast). ipvlan doesn't have the inline filter and calls the
> > deliver via work multicast.
> >
> > tl;dr I think the drivers do the same thing just call their functions
> > broadcast vs multicast. Having two drop reasons here is actively
> > misleading?  
> 
> My intent was to differentiate ipvlan and macvlan reasons, some
> deployments use both.
> 
> I can rename the reasons if you think this is better.
> 
> IPVLAN_MULTICAST_BACKLOG
> 
> MACVLAN_BROADCAST_BACKLOG

Sounds better, thanks

^ permalink raw reply

* Re: [PATCH net-next] net: hsr: emit notification for PRP slave2 changed hw addr on port deletion
From: Felix Maurer @ 2026-04-07 14:08 UTC (permalink / raw)
  To: Fernando Fernandez Mancera
  Cc: netdev, acsjakub, liuhangbin, kees, horms, pabeni, kuba, edumazet,
	davem, Luka Gejak
In-Reply-To: <20260403123928.4249-2-fmancera@suse.de>

On Fri, Apr 03, 2026 at 02:39:29PM +0200, Fernando Fernandez Mancera wrote:
> On PRP protocol, when deleting the port the MAC address change
> notification was missing. In addition to that, make sure to only perform
> the MAC address change on slave2 deletion and PRP protocol as the
> operation isn't necessary for HSR nor slave1.
>
> Note that the eth_hw_addr_set() is correct on PRP context as the slaves
> are either in promiscuous mode or forward offload enabled.
>
> Fixes: b65999e7238e ("net: hsr: sync hw addr of slave2 according to slave1 hw addr on PRP")
> Reported-by: Luka Gejak <luka.gejak@linux.dev>
> Closes: https://lore.kernel.org/netdev/DHFCZEM93FTT.1RWFBIE32K7OT@linux.dev/
> Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>

I don't have a preference for the tree, but:
Reviewed-by: Felix Maurer <fmaurer@redhat.com>

> ---
> Note: routed through net-next tree as the next net tree as rc6 batch is
> already out and I do not think this is worth to be included in a last
> call batch. Anyway, if maintainer thinks it is, feel free to apply it to
> net tree.
> ---
>  net/hsr/hsr_slave.c | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c
> index 44f83c8c56a7..d9af9e65f72f 100644
> --- a/net/hsr/hsr_slave.c
> +++ b/net/hsr/hsr_slave.c
> @@ -243,7 +243,11 @@ void hsr_del_port(struct hsr_port *port)
>  		if (!port->hsr->fwd_offloaded)
>  			dev_set_promiscuity(port->dev, -1);
>  		netdev_upper_dev_unlink(port->dev, master->dev);
> -		eth_hw_addr_set(port->dev, port->original_macaddress);
> +		if (hsr->prot_version == PRP_V1 &&
> +		    port->type == HSR_PT_SLAVE_B) {
> +			eth_hw_addr_set(port->dev, port->original_macaddress);
> +			call_netdevice_notifiers(NETDEV_CHANGEADDR, port->dev);
> +		}
>  	}
>
>  	kfree_rcu(port, rcu);
> --
> 2.53.0
>


^ permalink raw reply

* Re: [PATCH net-next v3 4/5] ipv6: mld: encode multicast exponential fields
From: Nikolay Aleksandrov @ 2026-04-07 14:09 UTC (permalink / raw)
  To: Ujjal Roy, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Ido Schimmel, David Ahern, Shuah Khan,
	Andy Roulin, Yong Wang, Petr Machata
  Cc: Ujjal Roy, bridge, netdev, linux-kernel, linux-kselftest
In-Reply-To: <20260403150050.1235-5-royujjal@gmail.com>

On 03/04/2026 18:00, Ujjal Roy wrote:
> In MLD, QQIC and MRC fields are not correctly encoded when
> generating query packets. Since the receiver of the query
> interprets these fields using the MLDv2 floating-point
> decoding logic, any value that exceeds the linear threshold
> is incorrectly parsed as an exponential value, leading to
> an incorrect interval calculation.
> 
> Encode and assign the corresponding protocol fields during
> query generation. Introduce the logic to dynamically
> calculate the exponent and mantissa using bit-scan (fls).
> This ensures QQIC (8-bit) and MRC (16-bit) fields are
> properly encoded when transmitting query packets with
> intervals that exceed their respective linear thresholds
> (128 for QQI; 32768 for MRD).
> 
> RFC3810: If QQIC >= 128, the QQIC field represents a
> floating-point value as follows:
>       0 1 2 3 4 5 6 7
>      +-+-+-+-+-+-+-+-+
>      |1| exp | mant  |
>      +-+-+-+-+-+-+-+-+
> 
> RFC3810: If Maximum Response Code >= 32768, the Maximum
> Response Code field represents a floating-point value as
> follows:
>       0 1 2 3 4 5 6 7 8 9 A B C D E F
>      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>      |1| exp |          mant         |
>      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> 
> Signed-off-by: Ujjal Roy <royujjal@gmail.com>
> ---
>   include/net/mld.h         | 122 ++++++++++++++++++++++++++++++++++++++
>   net/bridge/br_multicast.c |   4 +-
>   2 files changed, 124 insertions(+), 2 deletions(-)
> 
> diff --git a/include/net/mld.h b/include/net/mld.h
> index da3299545ebd..147e8c44eb28 100644
> --- a/include/net/mld.h
> +++ b/include/net/mld.h
> @@ -90,12 +90,134 @@ struct mld2_query {
>   #define MLDV2_QQIC_MAN(value)	((value) & 0x0f)
>   
>   #define MLD_QQIC_MIN_THRESHOLD	128
> +/* Max representable (mant = 0xF, exp = 7) -> 31744 */
> +#define MLD_QQIC_MAX_THRESHOLD	31744
>   #define MLD_MRC_MIN_THRESHOLD	32768UL
> +/* Max representable (mant = 0xFFF, exp = 7) -> 8387584 */
> +#define MLD_MRC_MAX_THRESHOLD	8387584
>   #define MLDV1_MRD_MAX_COMPAT	(MLD_MRC_MIN_THRESHOLD - 1)
>   
>   #define MLD_MAX_QUEUE		8
>   #define MLD_MAX_SKBS		32
>   
> +/* V2 exponential field encoding */
> +
> +/*
> + * Calculate Maximum Response Code from Maximum Response Delay
> + *
> + * MRC represents the 16-bit encoded form of Maximum Response
> + * Delay (MRD); once decoded, the resulting value is in
> + * milliseconds.
> + *
> + * RFC3810 defines only the decoding formula:
> + * Maximum Response Delay = (mant | 0x1000) << (exp + 3)
> + *
> + * but does NOT define the encoding procedure. To derive exponent:
> + *
> + * For the 16-bit MRC, the "hidden bit" (0x1000) is left shifted by 12
> + * to sit above the 12-bit mantissa. The RFC then shifts this entire
> + * block left by (exp + 3) to reconstruct the value.
> + * So, 'hidden bit' is the MSB which is shifted by (12 + exp + 3).
> + *
> + * Total left shift of the hidden bit = 12 + (exp + 3) = exp + 15.
> + * This is the MSB at the 0-based bit position: (exp + 15).
> + * Since fls() is 1-based, fls(value) - 1 = exp + 15.
> + *
> + * Therefore:
> + *     exp  = fls(value) - 16
> + *     mant = (value >> (exp + 3)) & 0x0FFF
> + *
> + * Final encoding formula:
> + *     0x8000 | (exp << 12) | mant
> + *
> + * Example (value = 1311744):
> + *  0               1               2               3
> + *  0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + * |0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0| 1311744
> + * |                      ^-^--------mant---------^ ^...(exp+3)...^| exp=5
> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *
> + * Encoded:
> + *   0x8000 | (5 << 12) | 0x404 = 0xD404
> + */
> +static inline u16 mldv2_mrc(unsigned long mrd)
> +{
> +	u16 mc_man, mc_exp;
> +
> +	/* RFC3810: MRC < 32768 is literal */
> +	if (mrd < MLD_MRC_MIN_THRESHOLD)
> +		return mrd;
> +
> +	/* Saturate at max representable (mant = 0xFFF, exp = 7) -> 8387584 */
> +	if (mrd >= MLD_MRC_MAX_THRESHOLD)
> +		return 0xFFFF;
> +
> +	mc_exp = fls(mrd) - 16;
> +	mc_man = (mrd >> (mc_exp + 3)) & 0x0FFF;
> +
> +	return 0x8000 | (mc_exp << 12) | mc_man;
> +}
> +
> +/*
> + * Calculate Querier's Query Interval Code from Query Interval
> + *
> + * QQIC represents the 8-bit encoded form of Query Interval (QQI);
> + * once decoded, the resulting value is in seconds.
> + *
> + * MLDv2 QQIC 8-bit floating-point encoding (RFC3810).
> + *
> + * RFC3810 defines only the decoding formula:
> + * QQI = (mant | 0x10) << (exp + 3)
> + *
> + * but does NOT define the encoding procedure. To derive exponent:
> + *
> + * For any value of mantissa and exponent, the decoding formula
> + * indicates that the "hidden bit" (0x10) is shifted 4 bits left
> + * to sit above the 4-bit mantissa. The RFC again shifts this
> + * entire block left by (exp + 3) to reconstruct the value.
> + * So, 'hidden bit' is the MSB which is shifted by (4 + exp + 3).
> + *
> + * Total left shift of the 'hidden bit' = 4 + (exp + 3) = exp + 7.
> + * This is the MSB at the 0-based bit position: (exp + 7).
> + * Since fls() is 1-based, fls(value) - 1 = exp + 7.
> + *
> + * Therefore:
> + *     exp  = fls(value) - 8
> + *     mant = (value >> (exp + 3)) & 0x0F
> + *
> + * Final encoding formula:
> + *     0x80 | (exp << 4) | mant
> + *
> + * Example (value = 3200):
> + *  0               1
> + *  0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + * |0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0| (value = 3200)
> + * |        ^-^-mant^ ^..(exp+3)..^| exp = 4, mant = 9
> + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
> + *
> + * Encoded:
> + *   0x80 | (4 << 4) | 9 = 0xC9
> + */
> +static inline u8 mldv2_qqic(unsigned long value)
> +{
> +	u8 mc_man, mc_exp;
> +
> +	/* RFC3810: QQIC < 128 is literal */
> +	if (value < MLD_QQIC_MIN_THRESHOLD)
> +		return value;
> +
> +	/* Saturate at max representable (mant = 0xF, exp = 7) -> 31744 */
> +	if (value >= MLD_QQIC_MAX_THRESHOLD)
> +		return 0xFF;
> +
> +	mc_exp  = fls(value) - 8;
> +	mc_man = (value >> (mc_exp + 3)) & 0x0F;
> +
> +	return 0x80 | (mc_exp << 4) | mc_man;
> +}
> +
>   /* V2 exponential field decoding */
>   
>   /* Calculate Maximum Response Delay from Maximum Response Code
> diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
> index 27010744d7ae..a22e44c4fa48 100644
> --- a/net/bridge/br_multicast.c
> +++ b/net/bridge/br_multicast.c
> @@ -1181,7 +1181,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
>   		break;
>   	case 2:
>   		mld2q = (struct mld2_query *)icmp6_hdr(skb);
> -		mld2q->mld2q_mrc = htons((u16)jiffies_to_msecs(interval));
> +		mld2q->mld2q_mrc = htons((u16)mldv2_mrc(jiffies_to_msecs(interval)));

you've defined mldv2_mrc as u16, no need to cast it again here

>   		mld2q->mld2q_type = ICMPV6_MGM_QUERY;
>   		mld2q->mld2q_code = 0;
>   		mld2q->mld2q_cksum = 0;
> @@ -1190,7 +1190,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge_mcast *brm
>   		mld2q->mld2q_suppress = sflag;
>   		mld2q->mld2q_qrv = 2;
>   		mld2q->mld2q_nsrcs = htons(llqt_srcs);
> -		mld2q->mld2q_qqic = brmctx->multicast_query_interval / HZ;
> +		mld2q->mld2q_qqic = mldv2_qqic(brmctx->multicast_query_interval / HZ);
>   		mld2q->mld2q_mca = *group;
>   		csum = &mld2q->mld2q_cksum;
>   		csum_start = (void *)mld2q;


^ permalink raw reply

* Re: [PATCH net-next v9 1/4] net: stmmac: Add DW25GMAC support in stmmac core driver
From: Russell King (Oracle) @ 2026-04-07 14:09 UTC (permalink / raw)
  To: Jitendra Vegiraju
  Cc: netdev, alexandre.torgue, davem, edumazet, kuba, pabeni,
	mcoquelin.stm32, bcm-kernel-feedback-list, richardcochran, ast,
	daniel, hawk, john.fastabend, rohan.g.thomas, linux-kernel,
	linux-stm32, linux-arm-kernel, bpf, andrew+netdev, horms, sdf, me,
	siyanteng, prabhakar.mahadev-lad.rj, weishangjuan, wens,
	vladimir.oltean, lizhi2, boon.khai.ng, maxime.chevallier,
	chenchuangyu, yangtiezhu, ovidiu.panait.rb, chenhuacai,
	florian.fainelli, quic_abchauha
In-Reply-To: <20260402213629.1996133-2-jitendra.vegiraju@broadcom.com>

Not withstanding my comment about the other Synopsys xlgmac driver that
we have in the kernel...

On Thu, Apr 02, 2026 at 02:36:26PM -0700, Jitendra Vegiraju wrote:
> From: Jitendra Vegiraju <jitendra.vegiraju@broadcom.com>
> 
> The DW25GMAC introduced a new DMA architecture called Hyper-DMA (HDMA) for
> virtualization scalability. This is realized by decoupling physical DMA
> channels(PDMA) from potentially large number of virtual DMA channels(VDMA).
> The VDMAs provide software abstraction to driver that map to PDMAs for
> frame transmission and reception.
> Since 25GMAC is a derivative of XGMAC, majority of IP is common to both.
> 
> To add support for the HDMA in 25GMAC, a new instance of dma_ops,
> dw25gmac400_dma_ops is introduced.
> To support the current needs, a simple one-to-one mapping of dw25gmac's
> logical VDMA (channel) to TC to PDMAs is used. Most of the other dma
> operation functions in existing dwxgamc2_dma.c file are reused whereever

Typo: dwxgmac2_dma.c

> applicable.
> Added setup function for DW25GMAC's stmmac_hwif_entry in stmmac core.

In a previous review, I questioned the use of DWMAC_CORE_25GMAC and
asked about its version numberspace. I believe you indicated that the
version numberspace is the same as the existing XGMAC core.

I'm going to question the value of adding DWMAC_CORE_25GMAC.

1. What is the value of splitting DWMAC_CORE_25GMAC from
   DWMAC_CORE_XGMAC given that it's in the same versioning numberspace
   as XGMAC, and most tests (via dwmac_is_xgmac()) test for XGMAC or
   25GMAC?

2. Have you reviewed all the places that explicitly test for
   DWMAC_CORE_XGMAC, looking at their "false" paths (for non-XGMAC
   cores) to determine whether they are suitable? For example:

        if (priv->plat->core_type == DWMAC_CORE_XGMAC)
                ndev->max_mtu = XGMAC_JUMBO_LEN;
        else if (priv->plat->enh_desc || priv->synopsys_id >= DWMAC_CORE_4_00)
                ndev->max_mtu = JUMBO_LEN;
        else
                ndev->max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN);

   XGMAC can handle a max MTU of 16368, but with your code using
   DWMAC_CORE_25GMAC, we fall back to the next test, which tests the
   IP version against 0x40, and uses a max MTU of 9000. Given that
   DWMAC_CORE_4_00 is a different "version number space" this seems
   wrong.

3. Looking at the MDIO code, this looks very wrong if you're
   introducing DWMAC_CORE_25GMAC. Have you tested MDIO accesses?

   dwxgmac2_setup() is called for DWMAC_CORE_XGMAC core-type. In
   stmmac_mdio_register(), DWMAC_CORE_XGMAC uses different functions
   for MDIO bus access for C22 and C45 from other cores - it uses the
   stmmac_xgmac2_mdio_* functions.

   These use stmmac_xgmac2_c45_format() and stmmac_xgmac2_c22_format()
   to format the register values which do not depend on mii.*_mask, but
   do use mii.address and mii.data for the register offsets. Thus, is
   there any point to setting mii.addr_mask and mii.reg_mask ?

   For non-DWMAC_CORE_XGMAC cores, we fall back to the stmmac_mdio_*()
   functions, which for non-DWMAC_CORE_GMAC4 will only support Clause
   22 access, not Clause 45 - which would be very strange for a 25G
   core.

4. What about the feature printing in
   stmmac_main.c::stmmac_dma_cap_show() ?

5. What about similar tests in stmmac_est.c and stmmac_ethtool.c ?



-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

^ permalink raw reply

* [PATCH net-next 00/13] netfilter: updates for net-next
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo

Hi,

The following patchset contains Netfilter fixes for *net-next*:

1) Fix ancient sparse warnings in nf conntrack nat modules, from
   Sun Jian.
2) Fix typo in enum description, from Jelle van der Waa.
3) remove redundant refetch of netns pointer in nf_conntrack_sip.
4) add a deprecation warning for dccp match.
   We can extend the deadline later if needed, but plan atm is to
   remove the feature.
5) remove nf_conntrack_h323 debug code that can read out-of-bounds
   with malformed messages. This code was commented out, but better
   remove this.
6+7) add more netlink policy validations in netfilter.
   This could theoretically cause issues when a client sends e.g.
   unsupported feature flags that were previously ignored, so we
   may have to relax some changes. For now, try to be stricter and
   reject upfront.
8+9) minor code cleanup in nft_set_pipapo (an nftables set backend).
10) Add nftables matching support fro double-tagged vlan and pppoe
    frames, from Pablo Neira Ayuso.
11) Fix up indentation of debug messages in nf_conntrack_h323 conntrack
    helper, from David Laight.
12) Add a helper to iterate to next flow action and bail out if the
    maximum number of actions is reached, also from Pablo.
13) Impose more retrictions on expectations attached via ctnetlink
    control plane by restricting this based on the helper attached to
    the master conntrack, also from Pablo Neira Ayuso.

Please, pull these changes from:
The following changes since commit 97a8355b6a715c79c090b906894e12dc3934b3fe:

  Merge branch 'net-mlx5e-xdp-add-support-for-multi-packet-per-page' (2026-04-07 13:34:08 +0200)

are available in the Git repository at:

  https://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next.git tags/nf-next-26-04-07

for you to fetch changes up to ead9479042e7349e3deab204add7b7ccebe20429:

  netfilter: ctnetlink: restrict expectfn to helper (2026-04-07 15:48:16 +0200)

----------------------------------------------------------------
netfilter pull request nf-next-26-04-07

----------------------------------------------------------------
David Laight (1):
  netfilter: nf_conntrack_h323: Correct indentation when H323_TRACE
    defined

Florian Westphal (7):
  netfilter: nf_conntrack_sip: remove net variable shadowing
  netfilter: add deprecation warning for dccp support
  netfilter: nf_conntrack_h323: remove unreliable debug code in
    decode_octstr
  netfilter: add more netlink-based policy range checks
  netfilter: nf_tables: add netlink policy based cap on registers
  netfilter: nft_set_pipapo: increment data in one step
  netfilter: nft_set_pipapo_avx2: remove redundant loop in lookup_slow

Jelle van der Waa (1):
  netfilter: nf_tables: Fix typo in enum description

Pablo Neira Ayuso (3):
  netfilter: nft_meta: add double-tagged vlan and pppoe support
  netfilter: nf_tables_offload: add nft_flow_action_entry_next() and use
    it
  netfilter: ctnetlink: restrict expectfn to helper

Sun Jian (1):
  netfilter: use function typedefs for __rcu NAT helper hook pointers

 include/linux/netfilter/nf_conntrack_amanda.h | 15 +++--
 include/linux/netfilter/nf_conntrack_ftp.h    | 17 +++---
 include/linux/netfilter/nf_conntrack_irc.h    | 15 +++--
 include/linux/netfilter/nf_conntrack_snmp.h   | 11 ++--
 include/linux/netfilter/nf_conntrack_tftp.h   |  9 ++-
 include/net/netfilter/nf_conntrack_helper.h   |  3 +-
 include/net/netfilter/nf_tables.h             |  4 ++
 include/net/netfilter/nf_tables_ipv4.h        | 17 ++++--
 include/net/netfilter/nf_tables_ipv6.h        | 16 +++--
 include/net/netfilter/nf_tables_offload.h     | 10 ++++
 include/uapi/linux/netfilter/nf_tables.h      |  6 +-
 net/ipv4/netfilter/nf_nat_h323.c              |  2 +
 net/netfilter/ipset/ip_set_core.c             |  2 +-
 net/netfilter/nf_conntrack_amanda.c           | 10 +---
 net/netfilter/nf_conntrack_ftp.c              | 10 +---
 net/netfilter/nf_conntrack_h323_asn1.c        | 45 ++++++--------
 net/netfilter/nf_conntrack_helper.c           |  5 +-
 net/netfilter/nf_conntrack_irc.c              | 10 +---
 net/netfilter/nf_conntrack_netlink.c          |  2 +-
 net/netfilter/nf_conntrack_sip.c              |  3 +-
 net/netfilter/nf_conntrack_snmp.c             |  7 +--
 net/netfilter/nf_conntrack_tftp.c             |  7 +--
 net/netfilter/nf_dup_netdev.c                 |  5 +-
 net/netfilter/nf_nat_sip.c                    |  1 +
 net/netfilter/nf_tables_api.c                 | 20 +++++--
 net/netfilter/nf_tables_core.c                |  2 +-
 net/netfilter/nfnetlink_acct.c                |  2 +-
 net/netfilter/nfnetlink_cthelper.c            |  2 +-
 net/netfilter/nfnetlink_hook.c                |  2 +-
 net/netfilter/nfnetlink_log.c                 |  4 +-
 net/netfilter/nfnetlink_osf.c                 |  2 +-
 net/netfilter/nfnetlink_queue.c               |  2 +-
 net/netfilter/nft_bitwise.c                   |  6 +-
 net/netfilter/nft_byteorder.c                 |  4 +-
 net/netfilter/nft_cmp.c                       |  2 +-
 net/netfilter/nft_compat.c                    |  2 +-
 net/netfilter/nft_connlimit.c                 |  2 +-
 net/netfilter/nft_ct.c                        |  6 +-
 net/netfilter/nft_dynset.c                    |  3 +-
 net/netfilter/nft_exthdr.c                    |  9 ++-
 net/netfilter/nft_fib.c                       |  2 +-
 net/netfilter/nft_hash.c                      |  4 +-
 net/netfilter/nft_immediate.c                 |  6 +-
 net/netfilter/nft_inner.c                     |  2 +-
 net/netfilter/nft_limit.c                     |  2 +-
 net/netfilter/nft_log.c                       |  2 +-
 net/netfilter/nft_lookup.c                    |  4 +-
 net/netfilter/nft_meta.c                      | 58 ++++++++++++++++++-
 net/netfilter/nft_numgen.c                    |  2 +-
 net/netfilter/nft_objref.c                    |  2 +-
 net/netfilter/nft_osf.c                       |  4 +-
 net/netfilter/nft_payload.c                   |  8 +--
 net/netfilter/nft_queue.c                     |  2 +-
 net/netfilter/nft_quota.c                     |  2 +-
 net/netfilter/nft_range.c                     |  2 +-
 net/netfilter/nft_rt.c                        |  2 +-
 net/netfilter/nft_set_pipapo.c                |  4 +-
 net/netfilter/nft_set_pipapo.h                |  3 -
 net/netfilter/nft_set_pipapo_avx2.c           | 32 +++-------
 net/netfilter/nft_socket.c                    |  2 +-
 net/netfilter/nft_synproxy.c                  |  4 +-
 net/netfilter/nft_tunnel.c                    |  6 +-
 net/netfilter/nft_xfrm.c                      |  6 +-
 net/netfilter/xt_dccp.c                       |  3 +
 64 files changed, 271 insertions(+), 195 deletions(-)
-- 
2.52.0

^ permalink raw reply

* [PATCH net-next 01/13] netfilter: use function typedefs for __rcu NAT helper hook pointers
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

From: Sun Jian <sun.jian.kdev@gmail.com>

After commit 07919126ecfc ("netfilter: annotate NAT helper hook pointers
with __rcu"), sparse can warn about type/address-space mismatches when
RCU-dereferencing NAT helper hook function pointers.

The hooks are __rcu-annotated and accessed via rcu_dereference(), but the
combination of complex function pointer declarators and the WRITE_ONCE()
machinery used by RCU_INIT_POINTER()/rcu_assign_pointer() can confuse
sparse and trigger false positives.

Introduce typedefs for the NAT helper function types, so __rcu applies to
a simple "fn_t __rcu *" pointer form. Also replace local typeof(hook)
variables with "fn_t *" to avoid propagating __rcu address space into
temporaries.

No functional change intended.

Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202603022359.3dGE9fwI-lkp@intel.com/
Signed-off-by: Sun Jian <sun.jian.kdev@gmail.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/linux/netfilter/nf_conntrack_amanda.h | 15 +++++++++------
 include/linux/netfilter/nf_conntrack_ftp.h    | 17 ++++++++++-------
 include/linux/netfilter/nf_conntrack_irc.h    | 15 +++++++++------
 include/linux/netfilter/nf_conntrack_snmp.h   | 11 +++++++----
 include/linux/netfilter/nf_conntrack_tftp.h   |  9 ++++++---
 net/netfilter/nf_conntrack_amanda.c           | 10 ++--------
 net/netfilter/nf_conntrack_ftp.c              | 10 ++--------
 net/netfilter/nf_conntrack_irc.c              | 10 ++--------
 net/netfilter/nf_conntrack_snmp.c             |  7 ++-----
 net/netfilter/nf_conntrack_tftp.c             |  7 ++-----
 10 files changed, 51 insertions(+), 60 deletions(-)

diff --git a/include/linux/netfilter/nf_conntrack_amanda.h b/include/linux/netfilter/nf_conntrack_amanda.h
index dfe89f38d1f7..1719987e8fd8 100644
--- a/include/linux/netfilter/nf_conntrack_amanda.h
+++ b/include/linux/netfilter/nf_conntrack_amanda.h
@@ -7,10 +7,13 @@
 #include <linux/skbuff.h>
 #include <net/netfilter/nf_conntrack_expect.h>
 
-extern unsigned int (__rcu *nf_nat_amanda_hook)(struct sk_buff *skb,
-					  enum ip_conntrack_info ctinfo,
-					  unsigned int protoff,
-					  unsigned int matchoff,
-					  unsigned int matchlen,
-					  struct nf_conntrack_expect *exp);
+typedef unsigned int
+nf_nat_amanda_hook_fn(struct sk_buff *skb,
+		      enum ip_conntrack_info ctinfo,
+		      unsigned int protoff,
+		      unsigned int matchoff,
+		      unsigned int matchlen,
+		      struct nf_conntrack_expect *exp);
+
+extern nf_nat_amanda_hook_fn __rcu *nf_nat_amanda_hook;
 #endif /* _NF_CONNTRACK_AMANDA_H */
diff --git a/include/linux/netfilter/nf_conntrack_ftp.h b/include/linux/netfilter/nf_conntrack_ftp.h
index f31292642035..7b62446ccec4 100644
--- a/include/linux/netfilter/nf_conntrack_ftp.h
+++ b/include/linux/netfilter/nf_conntrack_ftp.h
@@ -26,11 +26,14 @@ struct nf_ct_ftp_master {
 
 /* For NAT to hook in when we find a packet which describes what other
  * connection we should expect. */
-extern unsigned int (__rcu *nf_nat_ftp_hook)(struct sk_buff *skb,
-				       enum ip_conntrack_info ctinfo,
-				       enum nf_ct_ftp_type type,
-				       unsigned int protoff,
-				       unsigned int matchoff,
-				       unsigned int matchlen,
-				       struct nf_conntrack_expect *exp);
+typedef unsigned int
+nf_nat_ftp_hook_fn(struct sk_buff *skb,
+		   enum ip_conntrack_info ctinfo,
+		   enum nf_ct_ftp_type type,
+		   unsigned int protoff,
+		   unsigned int matchoff,
+		   unsigned int matchlen,
+		   struct nf_conntrack_expect *exp);
+
+extern nf_nat_ftp_hook_fn __rcu *nf_nat_ftp_hook;
 #endif /* _NF_CONNTRACK_FTP_H */
diff --git a/include/linux/netfilter/nf_conntrack_irc.h b/include/linux/netfilter/nf_conntrack_irc.h
index 4f3ca5621998..ce07250afb4e 100644
--- a/include/linux/netfilter/nf_conntrack_irc.h
+++ b/include/linux/netfilter/nf_conntrack_irc.h
@@ -8,11 +8,14 @@
 
 #define IRC_PORT	6667
 
-extern unsigned int (__rcu *nf_nat_irc_hook)(struct sk_buff *skb,
-				       enum ip_conntrack_info ctinfo,
-				       unsigned int protoff,
-				       unsigned int matchoff,
-				       unsigned int matchlen,
-				       struct nf_conntrack_expect *exp);
+typedef unsigned int
+nf_nat_irc_hook_fn(struct sk_buff *skb,
+		   enum ip_conntrack_info ctinfo,
+		   unsigned int protoff,
+		   unsigned int matchoff,
+		   unsigned int matchlen,
+		   struct nf_conntrack_expect *exp);
+
+extern nf_nat_irc_hook_fn __rcu *nf_nat_irc_hook;
 
 #endif /* _NF_CONNTRACK_IRC_H */
diff --git a/include/linux/netfilter/nf_conntrack_snmp.h b/include/linux/netfilter/nf_conntrack_snmp.h
index 99107e4f5234..bb39f04a9977 100644
--- a/include/linux/netfilter/nf_conntrack_snmp.h
+++ b/include/linux/netfilter/nf_conntrack_snmp.h
@@ -5,9 +5,12 @@
 #include <linux/netfilter.h>
 #include <linux/skbuff.h>
 
-extern int (__rcu *nf_nat_snmp_hook)(struct sk_buff *skb,
-				unsigned int protoff,
-				struct nf_conn *ct,
-				enum ip_conntrack_info ctinfo);
+typedef int
+nf_nat_snmp_hook_fn(struct sk_buff *skb,
+		    unsigned int protoff,
+		    struct nf_conn *ct,
+		    enum ip_conntrack_info ctinfo);
+
+extern nf_nat_snmp_hook_fn __rcu *nf_nat_snmp_hook;
 
 #endif /* _NF_CONNTRACK_SNMP_H */
diff --git a/include/linux/netfilter/nf_conntrack_tftp.h b/include/linux/netfilter/nf_conntrack_tftp.h
index 1490b68dd7d1..90b334bbce3c 100644
--- a/include/linux/netfilter/nf_conntrack_tftp.h
+++ b/include/linux/netfilter/nf_conntrack_tftp.h
@@ -19,8 +19,11 @@ struct tftphdr {
 #define TFTP_OPCODE_ACK		4
 #define TFTP_OPCODE_ERROR	5
 
-extern unsigned int (__rcu *nf_nat_tftp_hook)(struct sk_buff *skb,
-				        enum ip_conntrack_info ctinfo,
-				        struct nf_conntrack_expect *exp);
+typedef unsigned int
+nf_nat_tftp_hook_fn(struct sk_buff *skb,
+		    enum ip_conntrack_info ctinfo,
+		    struct nf_conntrack_expect *exp);
+
+extern nf_nat_tftp_hook_fn __rcu *nf_nat_tftp_hook;
 
 #endif /* _NF_CONNTRACK_TFTP_H */
diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c
index c0132559f6af..d2c09e8dd872 100644
--- a/net/netfilter/nf_conntrack_amanda.c
+++ b/net/netfilter/nf_conntrack_amanda.c
@@ -37,13 +37,7 @@ MODULE_PARM_DESC(master_timeout, "timeout for the master connection");
 module_param(ts_algo, charp, 0400);
 MODULE_PARM_DESC(ts_algo, "textsearch algorithm to use (default kmp)");
 
-unsigned int (__rcu *nf_nat_amanda_hook)(struct sk_buff *skb,
-					 enum ip_conntrack_info ctinfo,
-					 unsigned int protoff,
-					 unsigned int matchoff,
-					 unsigned int matchlen,
-					 struct nf_conntrack_expect *exp)
-					 __read_mostly;
+nf_nat_amanda_hook_fn __rcu *nf_nat_amanda_hook __read_mostly;
 EXPORT_SYMBOL_GPL(nf_nat_amanda_hook);
 
 enum amanda_strings {
@@ -98,7 +92,7 @@ static int amanda_help(struct sk_buff *skb,
 	u_int16_t len;
 	__be16 port;
 	int ret = NF_ACCEPT;
-	typeof(nf_nat_amanda_hook) nf_nat_amanda;
+	nf_nat_amanda_hook_fn *nf_nat_amanda;
 
 	/* Only look at packets from the Amanda server */
 	if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL)
diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c
index 5e00f9123c38..de83bf9e6c61 100644
--- a/net/netfilter/nf_conntrack_ftp.c
+++ b/net/netfilter/nf_conntrack_ftp.c
@@ -43,13 +43,7 @@ module_param_array(ports, ushort, &ports_c, 0400);
 static bool loose;
 module_param(loose, bool, 0600);
 
-unsigned int (__rcu *nf_nat_ftp_hook)(struct sk_buff *skb,
-				      enum ip_conntrack_info ctinfo,
-				      enum nf_ct_ftp_type type,
-				      unsigned int protoff,
-				      unsigned int matchoff,
-				      unsigned int matchlen,
-				      struct nf_conntrack_expect *exp);
+nf_nat_ftp_hook_fn __rcu *nf_nat_ftp_hook;
 EXPORT_SYMBOL_GPL(nf_nat_ftp_hook);
 
 static int try_rfc959(const char *, size_t, struct nf_conntrack_man *,
@@ -385,7 +379,7 @@ static int help(struct sk_buff *skb,
 	struct nf_conntrack_man cmd = {};
 	unsigned int i;
 	int found = 0, ends_in_nl;
-	typeof(nf_nat_ftp_hook) nf_nat_ftp;
+	nf_nat_ftp_hook_fn *nf_nat_ftp;
 
 	/* Until there's been traffic both ways, don't look in packets. */
 	if (ctinfo != IP_CT_ESTABLISHED &&
diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c
index b8e6d724acd1..522183b9a604 100644
--- a/net/netfilter/nf_conntrack_irc.c
+++ b/net/netfilter/nf_conntrack_irc.c
@@ -30,13 +30,7 @@ static unsigned int dcc_timeout __read_mostly = 300;
 static char *irc_buffer;
 static DEFINE_SPINLOCK(irc_buffer_lock);
 
-unsigned int (__rcu *nf_nat_irc_hook)(struct sk_buff *skb,
-				      enum ip_conntrack_info ctinfo,
-				      unsigned int protoff,
-				      unsigned int matchoff,
-				      unsigned int matchlen,
-				      struct nf_conntrack_expect *exp)
-				      __read_mostly;
+nf_nat_irc_hook_fn __rcu *nf_nat_irc_hook __read_mostly;
 EXPORT_SYMBOL_GPL(nf_nat_irc_hook);
 
 #define HELPER_NAME "irc"
@@ -122,7 +116,7 @@ static int help(struct sk_buff *skb, unsigned int protoff,
 	__be16 port;
 	int i, ret = NF_ACCEPT;
 	char *addr_beg_p, *addr_end_p;
-	typeof(nf_nat_irc_hook) nf_nat_irc;
+	nf_nat_irc_hook_fn *nf_nat_irc;
 	unsigned int datalen;
 
 	/* If packet is coming from IRC server */
diff --git a/net/netfilter/nf_conntrack_snmp.c b/net/netfilter/nf_conntrack_snmp.c
index 387dd6e58f88..7b7eed43c54f 100644
--- a/net/netfilter/nf_conntrack_snmp.c
+++ b/net/netfilter/nf_conntrack_snmp.c
@@ -25,17 +25,14 @@ static unsigned int timeout __read_mostly = 30;
 module_param(timeout, uint, 0400);
 MODULE_PARM_DESC(timeout, "timeout for master connection/replies in seconds");
 
-int (__rcu *nf_nat_snmp_hook)(struct sk_buff *skb,
-			      unsigned int protoff,
-			      struct nf_conn *ct,
-			      enum ip_conntrack_info ctinfo);
+nf_nat_snmp_hook_fn __rcu *nf_nat_snmp_hook;
 EXPORT_SYMBOL_GPL(nf_nat_snmp_hook);
 
 static int snmp_conntrack_help(struct sk_buff *skb, unsigned int protoff,
 			       struct nf_conn *ct,
 			       enum ip_conntrack_info ctinfo)
 {
-	typeof(nf_nat_snmp_hook) nf_nat_snmp;
+	nf_nat_snmp_hook_fn *nf_nat_snmp;
 
 	nf_conntrack_broadcast_help(skb, ct, ctinfo, timeout);
 
diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c
index 89e9914e5d03..a2e6833a0bf7 100644
--- a/net/netfilter/nf_conntrack_tftp.c
+++ b/net/netfilter/nf_conntrack_tftp.c
@@ -32,10 +32,7 @@ static unsigned int ports_c;
 module_param_array(ports, ushort, &ports_c, 0400);
 MODULE_PARM_DESC(ports, "Port numbers of TFTP servers");
 
-unsigned int (__rcu *nf_nat_tftp_hook)(struct sk_buff *skb,
-				       enum ip_conntrack_info ctinfo,
-				       struct nf_conntrack_expect *exp)
-				       __read_mostly;
+nf_nat_tftp_hook_fn __rcu *nf_nat_tftp_hook __read_mostly;
 EXPORT_SYMBOL_GPL(nf_nat_tftp_hook);
 
 static int tftp_help(struct sk_buff *skb,
@@ -48,7 +45,7 @@ static int tftp_help(struct sk_buff *skb,
 	struct nf_conntrack_expect *exp;
 	struct nf_conntrack_tuple *tuple;
 	unsigned int ret = NF_ACCEPT;
-	typeof(nf_nat_tftp_hook) nf_nat_tftp;
+	nf_nat_tftp_hook_fn *nf_nat_tftp;
 
 	tfh = skb_header_pointer(skb, protoff + sizeof(struct udphdr),
 				 sizeof(_tftph), &_tftph);
-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next 02/13] netfilter: nf_tables: Fix typo in enum description
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

From: Jelle van der Waa <jelle@vdwaa.nl>

Fix the spelling of "options".

Signed-off-by: Jelle van der Waa <jelle@vdwaa.nl>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/uapi/linux/netfilter/nf_tables.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 45c71f7d21c2..dca9e72b0558 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -884,7 +884,7 @@ enum nft_exthdr_flags {
  * @NFT_EXTHDR_OP_TCPOPT: match against tcp options
  * @NFT_EXTHDR_OP_IPV4: match against ipv4 options
  * @NFT_EXTHDR_OP_SCTP: match against sctp chunks
- * @NFT_EXTHDR_OP_DCCP: match against dccp otions
+ * @NFT_EXTHDR_OP_DCCP: match against dccp options
  */
 enum nft_exthdr_op {
 	NFT_EXTHDR_OP_IPV6,
-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next 03/13] netfilter: nf_conntrack_sip: remove net variable shadowing
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

net is already set, derived from nf_conn.
I don't see how the device could be living in a different netns
than the conntrack entry.

Remove the extra variable and re-use existing one.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 net/netfilter/nf_conntrack_sip.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 939502ff7c87..182cfb119448 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -869,9 +869,8 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff,
 		saddr = &ct->tuplehash[!dir].tuple.src.u3;
 	} else if (sip_external_media) {
 		struct net_device *dev = skb_dst(skb)->dev;
-		struct net *net = dev_net(dev);
-		struct flowi fl;
 		struct dst_entry *dst = NULL;
+		struct flowi fl;
 
 		memset(&fl, 0, sizeof(fl));
 
-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next 04/13] netfilter: add deprecation warning for dccp support
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

Add a deprecation warning for the xt_dccp match and the
nft exthdr code.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 net/netfilter/nft_exthdr.c | 3 +++
 net/netfilter/xt_dccp.c    | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
index 5f01269a49bd..14d4ad7f518c 100644
--- a/net/netfilter/nft_exthdr.c
+++ b/net/netfilter/nft_exthdr.c
@@ -796,6 +796,9 @@ nft_exthdr_select_ops(const struct nft_ctx *ctx,
 		break;
 #ifdef CONFIG_NFT_EXTHDR_DCCP
 	case NFT_EXTHDR_OP_DCCP:
+		pr_warn_once("The dccp option matching is deprecated and scheduled to be removed in 2027.\n"
+			     "Please contact the netfilter-devel mailing list or update your nftables rules.\n");
+
 		if (tb[NFTA_EXTHDR_DREG])
 			return &nft_exthdr_dccp_ops;
 		break;
diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c
index 037ab93e25d0..3db81e041af9 100644
--- a/net/netfilter/xt_dccp.c
+++ b/net/netfilter/xt_dccp.c
@@ -159,6 +159,9 @@ static int __init dccp_mt_init(void)
 {
 	int ret;
 
+	pr_warn_once("The DCCP match is deprecated and scheduled to be removed in 2027.\n"
+		     "Please contact the netfilter-devel mailing list or update your iptables rules\n");
+
 	/* doff is 8 bits, so the maximum option size is (4*256).  Don't put
 	 * this in BSS since DaveM is worried about locked TLB's for kernel
 	 * BSS. */
-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next 05/13] netfilter: nf_conntrack_h323: remove unreliable debug code in decode_octstr
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

The debug code (not enabled in any build) reads up to 6 octets of
the inpt buffer, but does so without bound checks.  Zap this.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 net/netfilter/nf_conntrack_h323_asn1.c | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/net/netfilter/nf_conntrack_h323_asn1.c b/net/netfilter/nf_conntrack_h323_asn1.c
index 7b1497ed97d2..09e0f724644f 100644
--- a/net/netfilter/nf_conntrack_h323_asn1.c
+++ b/net/netfilter/nf_conntrack_h323_asn1.c
@@ -21,7 +21,6 @@
 
 #if H323_TRACE
 #define TAB_SIZE 4
-#define IFTHEN(cond, act) if(cond){act;}
 #ifdef __KERNEL__
 #define PRINT printk
 #else
@@ -29,7 +28,6 @@
 #endif
 #define FNAME(name) name,
 #else
-#define IFTHEN(cond, act)
 #define PRINT(fmt, args...)
 #define FNAME(name)
 #endif
@@ -445,11 +443,6 @@ static int decode_octstr(struct bitstr *bs, const struct field_t *f,
 			BYTE_ALIGN(bs);
 			if (base && (f->attr & DECODE)) {
 				/* The IP Address */
-				IFTHEN(f->lb == 4,
-				       PRINT(" = %d.%d.%d.%d:%d",
-					     bs->cur[0], bs->cur[1],
-					     bs->cur[2], bs->cur[3],
-					     bs->cur[4] * 256 + bs->cur[5]));
 				*((unsigned int *)(base + f->offset)) =
 				    bs->cur - bs->buf;
 			}
-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next 06/13] netfilter: add more netlink-based policy range checks
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

These spots either already check the attribute range manually
before use or the consuming functions tolerate unexpected values.

Nevertheless, add more range checks via netlink policy so we gain
more users and avoid possible re-use in other places that might
not have the required manual checks.  This also improves error
reporting: netlink core can generate extack errors.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 net/netfilter/ipset/ip_set_core.c  |  2 +-
 net/netfilter/nf_tables_api.c      | 20 +++++++++++++++-----
 net/netfilter/nfnetlink_acct.c     |  2 +-
 net/netfilter/nfnetlink_cthelper.c |  2 +-
 net/netfilter/nfnetlink_hook.c     |  2 +-
 net/netfilter/nfnetlink_log.c      |  4 +++-
 net/netfilter/nfnetlink_osf.c      |  2 +-
 net/netfilter/nfnetlink_queue.c    |  2 +-
 net/netfilter/nft_compat.c         |  2 +-
 net/netfilter/nft_connlimit.c      |  2 +-
 net/netfilter/nft_ct.c             |  2 +-
 net/netfilter/nft_dynset.c         |  3 ++-
 net/netfilter/nft_exthdr.c         |  2 +-
 net/netfilter/nft_inner.c          |  2 +-
 net/netfilter/nft_limit.c          |  2 +-
 net/netfilter/nft_log.c            |  2 +-
 net/netfilter/nft_osf.c            |  2 +-
 net/netfilter/nft_payload.c        |  2 +-
 net/netfilter/nft_queue.c          |  2 +-
 net/netfilter/nft_quota.c          |  2 +-
 net/netfilter/nft_synproxy.c       |  4 ++--
 net/netfilter/nft_tunnel.c         |  4 ++--
 net/netfilter/nft_xfrm.c           |  4 ++--
 23 files changed, 43 insertions(+), 30 deletions(-)

diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index d0c9fe59c67d..c5a26236a0bb 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -985,7 +985,7 @@ static const struct nla_policy ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] = {
 				    .len = IPSET_MAXNAMELEN - 1 },
 	[IPSET_ATTR_TYPENAME]	= { .type = NLA_NUL_STRING,
 				    .len = IPSET_MAXNAMELEN - 1},
-	[IPSET_ATTR_REVISION]	= { .type = NLA_U8 },
+	[IPSET_ATTR_REVISION]	= NLA_POLICY_MAX(NLA_U8, IPSET_REVISION_MAX),
 	[IPSET_ATTR_FAMILY]	= { .type = NLA_U8 },
 	[IPSET_ATTR_DATA]	= { .type = NLA_NESTED },
 };
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 8ed8d5384b97..8537b94653d3 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -1112,7 +1112,7 @@ static __be16 nft_base_seq_be16(const struct net *net)
 static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = {
 	[NFTA_TABLE_NAME]	= { .type = NLA_STRING,
 				    .len = NFT_TABLE_MAXNAMELEN - 1 },
-	[NFTA_TABLE_FLAGS]	= { .type = NLA_U32 },
+	[NFTA_TABLE_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFT_TABLE_F_MASK),
 	[NFTA_TABLE_HANDLE]	= { .type = NLA_U64 },
 	[NFTA_TABLE_USERDATA]	= { .type = NLA_BINARY,
 				    .len = NFT_USERDATA_MAXLEN }
@@ -1878,7 +1878,7 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
 	[NFTA_CHAIN_TYPE]	= { .type = NLA_STRING,
 				    .len = NFT_MODULE_AUTOLOAD_LIMIT },
 	[NFTA_CHAIN_COUNTERS]	= { .type = NLA_NESTED },
-	[NFTA_CHAIN_FLAGS]	= { .type = NLA_U32 },
+	[NFTA_CHAIN_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFT_CHAIN_FLAGS),
 	[NFTA_CHAIN_ID]		= { .type = NLA_U32 },
 	[NFTA_CHAIN_USERDATA]	= { .type = NLA_BINARY,
 				    .len = NFT_USERDATA_MAXLEN },
@@ -4597,7 +4597,16 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
 					    .len = NFT_TABLE_MAXNAMELEN - 1 },
 	[NFTA_SET_NAME]			= { .type = NLA_STRING,
 					    .len = NFT_SET_MAXNAMELEN - 1 },
-	[NFTA_SET_FLAGS]		= { .type = NLA_U32 },
+	[NFTA_SET_FLAGS]		= NLA_POLICY_MASK(NLA_BE32,
+							  NFT_SET_ANONYMOUS |
+							  NFT_SET_CONSTANT |
+							  NFT_SET_INTERVAL |
+							  NFT_SET_MAP |
+							  NFT_SET_TIMEOUT |
+							  NFT_SET_EVAL |
+							  NFT_SET_OBJECT |
+							  NFT_SET_CONCAT |
+							  NFT_SET_EXPR),
 	[NFTA_SET_KEY_TYPE]		= { .type = NLA_U32 },
 	[NFTA_SET_KEY_LEN]		= { .type = NLA_U32 },
 	[NFTA_SET_DATA_TYPE]		= { .type = NLA_U32 },
@@ -5929,7 +5938,8 @@ const struct nft_set_ext_type nft_set_ext_types[] = {
 static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = {
 	[NFTA_SET_ELEM_KEY]		= { .type = NLA_NESTED },
 	[NFTA_SET_ELEM_DATA]		= { .type = NLA_NESTED },
-	[NFTA_SET_ELEM_FLAGS]		= { .type = NLA_U32 },
+	[NFTA_SET_ELEM_FLAGS]		= NLA_POLICY_MASK(NLA_BE32, NFT_SET_ELEM_INTERVAL_END |
+								    NFT_SET_ELEM_CATCHALL),
 	[NFTA_SET_ELEM_TIMEOUT]		= { .type = NLA_U64 },
 	[NFTA_SET_ELEM_EXPIRATION]	= { .type = NLA_U64 },
 	[NFTA_SET_ELEM_USERDATA]	= { .type = NLA_BINARY,
@@ -8649,7 +8659,7 @@ static const struct nla_policy nft_flowtable_policy[NFTA_FLOWTABLE_MAX + 1] = {
 					    .len = NFT_NAME_MAXLEN - 1 },
 	[NFTA_FLOWTABLE_HOOK]		= { .type = NLA_NESTED },
 	[NFTA_FLOWTABLE_HANDLE]		= { .type = NLA_U64 },
-	[NFTA_FLOWTABLE_FLAGS]		= { .type = NLA_U32 },
+	[NFTA_FLOWTABLE_FLAGS]		= NLA_POLICY_MASK(NLA_BE32, NFT_FLOWTABLE_MASK),
 };
 
 struct nft_flowtable *nft_flowtable_lookup(const struct net *net,
diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c
index 2bfaa773d82f..8ff1e0ad5cb0 100644
--- a/net/netfilter/nfnetlink_acct.c
+++ b/net/netfilter/nfnetlink_acct.c
@@ -373,7 +373,7 @@ static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = {
 	[NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 },
 	[NFACCT_BYTES] = { .type = NLA_U64 },
 	[NFACCT_PKTS] = { .type = NLA_U64 },
-	[NFACCT_FLAGS] = { .type = NLA_U32 },
+	[NFACCT_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFACCT_F_QUOTA),
 	[NFACCT_QUOTA] = { .type = NLA_U64 },
 	[NFACCT_FILTER] = {.type = NLA_NESTED },
 };
diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c
index d545fa459455..0d16ad82d70c 100644
--- a/net/netfilter/nfnetlink_cthelper.c
+++ b/net/netfilter/nfnetlink_cthelper.c
@@ -165,7 +165,7 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy,
 
 static const struct nla_policy
 nfnl_cthelper_expect_policy_set[NFCTH_POLICY_SET_MAX+1] = {
-	[NFCTH_POLICY_SET_NUM] = { .type = NLA_U32, },
+	[NFCTH_POLICY_SET_NUM] = NLA_POLICY_MAX(NLA_BE32, NF_CT_MAX_EXPECT_CLASSES),
 };
 
 static int
diff --git a/net/netfilter/nfnetlink_hook.c b/net/netfilter/nfnetlink_hook.c
index 531706982859..5623c18fcd12 100644
--- a/net/netfilter/nfnetlink_hook.c
+++ b/net/netfilter/nfnetlink_hook.c
@@ -24,7 +24,7 @@
 #include <net/sock.h>
 
 static const struct nla_policy nfnl_hook_nla_policy[NFNLA_HOOK_MAX + 1] = {
-	[NFNLA_HOOK_HOOKNUM]	= { .type = NLA_U32 },
+	[NFNLA_HOOK_HOOKNUM]	= NLA_POLICY_MAX(NLA_BE32, 255),
 	[NFNLA_HOOK_PRIORITY]	= { .type = NLA_U32 },
 	[NFNLA_HOOK_DEV]	= { .type = NLA_STRING,
 				    .len = IFNAMSIZ - 1 },
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 91aa210b3e53..9497ebeedd55 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -879,7 +879,9 @@ static const struct nla_policy nfula_cfg_policy[NFULA_CFG_MAX+1] = {
 	[NFULA_CFG_TIMEOUT]	= { .type = NLA_U32 },
 	[NFULA_CFG_QTHRESH]	= { .type = NLA_U32 },
 	[NFULA_CFG_NLBUFSIZ]	= { .type = NLA_U32 },
-	[NFULA_CFG_FLAGS]	= { .type = NLA_U16 },
+	[NFULA_CFG_FLAGS]	= NLA_POLICY_MASK(NLA_BE16, NFULNL_CFG_F_SEQ |
+						  NFULNL_CFG_F_SEQ_GLOBAL |
+						  NFULNL_CFG_F_CONNTRACK),
 };
 
 static int nfulnl_recv_config(struct sk_buff *skb, const struct nfnl_info *info,
diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c
index 45d9ad231a92..d64ce21c7b55 100644
--- a/net/netfilter/nfnetlink_osf.c
+++ b/net/netfilter/nfnetlink_osf.c
@@ -293,7 +293,7 @@ bool nf_osf_find(const struct sk_buff *skb,
 EXPORT_SYMBOL_GPL(nf_osf_find);
 
 static const struct nla_policy nfnl_osf_policy[OSF_ATTR_MAX + 1] = {
-	[OSF_ATTR_FINGER]	= { .len = sizeof(struct nf_osf_user_finger) },
+	[OSF_ATTR_FINGER]	= NLA_POLICY_EXACT_LEN(sizeof(struct nf_osf_user_finger)),
 };
 
 static int nfnl_osf_add_callback(struct sk_buff *skb,
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index 2aa2380d976a..ac0c19233681 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -1599,7 +1599,7 @@ static const struct nla_policy nfqa_cfg_policy[NFQA_CFG_MAX+1] = {
 	[NFQA_CFG_PARAMS]	= { .len = sizeof(struct nfqnl_msg_config_params) },
 	[NFQA_CFG_QUEUE_MAXLEN]	= { .type = NLA_U32 },
 	[NFQA_CFG_MASK]		= { .type = NLA_U32 },
-	[NFQA_CFG_FLAGS]	= { .type = NLA_U32 },
+	[NFQA_CFG_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFQA_CFG_F_MAX - 1),
 };
 
 static const struct nf_queue_handler nfqh = {
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index 53a614a0e3cd..decc725a33c2 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -195,7 +195,7 @@ static void target_compat_from_user(struct xt_target *t, void *in, void *out)
 
 static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1] = {
 	[NFTA_RULE_COMPAT_PROTO]	= { .type = NLA_U32 },
-	[NFTA_RULE_COMPAT_FLAGS]	= { .type = NLA_U32 },
+	[NFTA_RULE_COMPAT_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFT_RULE_COMPAT_F_MASK),
 };
 
 static int nft_parse_compat(const struct nlattr *attr, u16 *proto, bool *inv)
diff --git a/net/netfilter/nft_connlimit.c b/net/netfilter/nft_connlimit.c
index 09ac4f77e389..46b31d78abc6 100644
--- a/net/netfilter/nft_connlimit.c
+++ b/net/netfilter/nft_connlimit.c
@@ -159,7 +159,7 @@ static int nft_connlimit_obj_dump(struct sk_buff *skb,
 
 static const struct nla_policy nft_connlimit_policy[NFTA_CONNLIMIT_MAX + 1] = {
 	[NFTA_CONNLIMIT_COUNT]	= { .type = NLA_U32 },
-	[NFTA_CONNLIMIT_FLAGS]	= { .type = NLA_U32 },
+	[NFTA_CONNLIMIT_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFT_CONNLIMIT_F_INV),
 };
 
 static struct nft_object_type nft_connlimit_obj_type;
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
index a8fcb4b6ea1a..00dabd985883 100644
--- a/net/netfilter/nft_ct.c
+++ b/net/netfilter/nft_ct.c
@@ -338,7 +338,7 @@ static void nft_ct_set_eval(const struct nft_expr *expr,
 static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = {
 	[NFTA_CT_DREG]		= { .type = NLA_U32 },
 	[NFTA_CT_KEY]		= NLA_POLICY_MAX(NLA_BE32, 255),
-	[NFTA_CT_DIRECTION]	= { .type = NLA_U8 },
+	[NFTA_CT_DIRECTION]	= NLA_POLICY_MAX(NLA_U8, IP_CT_DIR_REPLY),
 	[NFTA_CT_SREG]		= { .type = NLA_U32 },
 };
 
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
index 57bf94ae8724..ee9d3e7b1ecf 100644
--- a/net/netfilter/nft_dynset.c
+++ b/net/netfilter/nft_dynset.c
@@ -163,7 +163,8 @@ static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = {
 	[NFTA_DYNSET_SREG_DATA]	= { .type = NLA_U32 },
 	[NFTA_DYNSET_TIMEOUT]	= { .type = NLA_U64 },
 	[NFTA_DYNSET_EXPR]	= { .type = NLA_NESTED },
-	[NFTA_DYNSET_FLAGS]	= { .type = NLA_U32 },
+	[NFTA_DYNSET_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFT_DYNSET_F_INV |
+						  NFT_DYNSET_F_EXPR),
 	[NFTA_DYNSET_EXPRESSIONS] = { .type = NLA_NESTED },
 };
 
diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
index 14d4ad7f518c..b997307d94f9 100644
--- a/net/netfilter/nft_exthdr.c
+++ b/net/netfilter/nft_exthdr.c
@@ -490,7 +490,7 @@ static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
 	[NFTA_EXTHDR_TYPE]		= { .type = NLA_U8 },
 	[NFTA_EXTHDR_OFFSET]		= { .type = NLA_U32 },
 	[NFTA_EXTHDR_LEN]		= NLA_POLICY_MAX(NLA_BE32, 255),
-	[NFTA_EXTHDR_FLAGS]		= { .type = NLA_U32 },
+	[NFTA_EXTHDR_FLAGS]		= NLA_POLICY_MASK(NLA_BE32, NFT_EXTHDR_F_PRESENT),
 	[NFTA_EXTHDR_OP]		= NLA_POLICY_MAX(NLA_BE32, 255),
 	[NFTA_EXTHDR_SREG]		= { .type = NLA_U32 },
 };
diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c
index c4569d4b9228..03ffb1159fc1 100644
--- a/net/netfilter/nft_inner.c
+++ b/net/netfilter/nft_inner.c
@@ -321,7 +321,7 @@ static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs,
 
 static const struct nla_policy nft_inner_policy[NFTA_INNER_MAX + 1] = {
 	[NFTA_INNER_NUM]	= { .type = NLA_U32 },
-	[NFTA_INNER_FLAGS]	= { .type = NLA_U32 },
+	[NFTA_INNER_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFT_INNER_MASK),
 	[NFTA_INNER_HDRSIZE]	= { .type = NLA_U32 },
 	[NFTA_INNER_TYPE]	= { .type = NLA_U32 },
 	[NFTA_INNER_EXPR]	= { .type = NLA_NESTED },
diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c
index f6830621c471..167d99b1447f 100644
--- a/net/netfilter/nft_limit.c
+++ b/net/netfilter/nft_limit.c
@@ -189,7 +189,7 @@ static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = {
 	[NFTA_LIMIT_UNIT]	= { .type = NLA_U64 },
 	[NFTA_LIMIT_BURST]	= { .type = NLA_U32 },
 	[NFTA_LIMIT_TYPE]	= { .type = NLA_U32 },
-	[NFTA_LIMIT_FLAGS]	= { .type = NLA_U32 },
+	[NFTA_LIMIT_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFT_LIMIT_F_INV),
 };
 
 static int nft_limit_pkts_init(const struct nft_ctx *ctx,
diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c
index da0c0d1c9cea..0d868eea6257 100644
--- a/net/netfilter/nft_log.c
+++ b/net/netfilter/nft_log.c
@@ -69,7 +69,7 @@ static const struct nla_policy nft_log_policy[NFTA_LOG_MAX + 1] = {
 	[NFTA_LOG_SNAPLEN]	= { .type = NLA_U32 },
 	[NFTA_LOG_QTHRESHOLD]	= { .type = NLA_U16 },
 	[NFTA_LOG_LEVEL]	= { .type = NLA_U32 },
-	[NFTA_LOG_FLAGS]	= { .type = NLA_U32 },
+	[NFTA_LOG_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NF_LOG_MASK),
 };
 
 static int nft_log_modprobe(struct net *net, enum nf_log_type t)
diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c
index 39ccd67ed265..b2f44bc6bd3f 100644
--- a/net/netfilter/nft_osf.c
+++ b/net/netfilter/nft_osf.c
@@ -14,7 +14,7 @@ struct nft_osf {
 static const struct nla_policy nft_osf_policy[NFTA_OSF_MAX + 1] = {
 	[NFTA_OSF_DREG]		= { .type = NLA_U32 },
 	[NFTA_OSF_TTL]		= { .type = NLA_U8 },
-	[NFTA_OSF_FLAGS]	= { .type = NLA_U32 },
+	[NFTA_OSF_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFT_OSF_F_VERSION),
 };
 
 static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs,
diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
index 973d56af03ff..91b62083d942 100644
--- a/net/netfilter/nft_payload.c
+++ b/net/netfilter/nft_payload.c
@@ -216,7 +216,7 @@ static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = {
 	[NFTA_PAYLOAD_LEN]		= NLA_POLICY_MAX(NLA_BE32, 255),
 	[NFTA_PAYLOAD_CSUM_TYPE]	= { .type = NLA_U32 },
 	[NFTA_PAYLOAD_CSUM_OFFSET]	= NLA_POLICY_MAX(NLA_BE32, 255),
-	[NFTA_PAYLOAD_CSUM_FLAGS]	= { .type = NLA_U32 },
+	[NFTA_PAYLOAD_CSUM_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFT_PAYLOAD_L4CSUM_PSEUDOHDR),
 };
 
 static int nft_payload_init(const struct nft_ctx *ctx,
diff --git a/net/netfilter/nft_queue.c b/net/netfilter/nft_queue.c
index 8eb13a02942e..b83d209db886 100644
--- a/net/netfilter/nft_queue.c
+++ b/net/netfilter/nft_queue.c
@@ -95,7 +95,7 @@ static int nft_queue_validate(const struct nft_ctx *ctx,
 static const struct nla_policy nft_queue_policy[NFTA_QUEUE_MAX + 1] = {
 	[NFTA_QUEUE_NUM]	= { .type = NLA_U16 },
 	[NFTA_QUEUE_TOTAL]	= { .type = NLA_U16 },
-	[NFTA_QUEUE_FLAGS]	= { .type = NLA_U16 },
+	[NFTA_QUEUE_FLAGS]	= NLA_POLICY_MASK(NLA_BE16, NFT_QUEUE_FLAG_MASK),
 	[NFTA_QUEUE_SREG_QNUM]	= { .type = NLA_U32 },
 };
 
diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c
index 3be788e5223c..6ed7c4409706 100644
--- a/net/netfilter/nft_quota.c
+++ b/net/netfilter/nft_quota.c
@@ -46,7 +46,7 @@ static inline void nft_quota_do_eval(struct nft_quota *priv,
 
 static const struct nla_policy nft_quota_policy[NFTA_QUOTA_MAX + 1] = {
 	[NFTA_QUOTA_BYTES]	= { .type = NLA_U64 },
-	[NFTA_QUOTA_FLAGS]	= { .type = NLA_U32 },
+	[NFTA_QUOTA_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFT_QUOTA_F_INV),
 	[NFTA_QUOTA_CONSUMED]	= { .type = NLA_U64 },
 };
 
diff --git a/net/netfilter/nft_synproxy.c b/net/netfilter/nft_synproxy.c
index 8e452a874969..7641f249614c 100644
--- a/net/netfilter/nft_synproxy.c
+++ b/net/netfilter/nft_synproxy.c
@@ -17,8 +17,8 @@ struct nft_synproxy {
 
 static const struct nla_policy nft_synproxy_policy[NFTA_SYNPROXY_MAX + 1] = {
 	[NFTA_SYNPROXY_MSS]		= { .type = NLA_U16 },
-	[NFTA_SYNPROXY_WSCALE]		= { .type = NLA_U8 },
-	[NFTA_SYNPROXY_FLAGS]		= { .type = NLA_U32 },
+	[NFTA_SYNPROXY_WSCALE]		= NLA_POLICY_MAX(NLA_U8, TCP_MAX_WSCALE),
+	[NFTA_SYNPROXY_FLAGS]		= NLA_POLICY_MASK(NLA_BE32, NF_SYNPROXY_OPT_MASK),
 };
 
 static void nft_synproxy_tcp_options(struct synproxy_options *opts,
diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c
index f5cadba91417..65d06300f48a 100644
--- a/net/netfilter/nft_tunnel.c
+++ b/net/netfilter/nft_tunnel.c
@@ -68,7 +68,7 @@ static void nft_tunnel_get_eval(const struct nft_expr *expr,
 static const struct nla_policy nft_tunnel_policy[NFTA_TUNNEL_MAX + 1] = {
 	[NFTA_TUNNEL_KEY]	= NLA_POLICY_MAX(NLA_BE32, 255),
 	[NFTA_TUNNEL_DREG]	= { .type = NLA_U32 },
-	[NFTA_TUNNEL_MODE]	= NLA_POLICY_MAX(NLA_BE32, 255),
+	[NFTA_TUNNEL_MODE]	= NLA_POLICY_MAX(NLA_BE32, NFT_TUNNEL_MODE_MAX),
 };
 
 static int nft_tunnel_get_init(const struct nft_ctx *ctx,
@@ -408,7 +408,7 @@ static const struct nla_policy nft_tunnel_key_policy[NFTA_TUNNEL_KEY_MAX + 1] =
 	[NFTA_TUNNEL_KEY_IP]	= { .type = NLA_NESTED, },
 	[NFTA_TUNNEL_KEY_IP6]	= { .type = NLA_NESTED, },
 	[NFTA_TUNNEL_KEY_ID]	= { .type = NLA_U32, },
-	[NFTA_TUNNEL_KEY_FLAGS]	= { .type = NLA_U32, },
+	[NFTA_TUNNEL_KEY_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFT_TUNNEL_F_MASK),
 	[NFTA_TUNNEL_KEY_TOS]	= { .type = NLA_U8, },
 	[NFTA_TUNNEL_KEY_TTL]	= { .type = NLA_U8, },
 	[NFTA_TUNNEL_KEY_SPORT]	= { .type = NLA_U16, },
diff --git a/net/netfilter/nft_xfrm.c b/net/netfilter/nft_xfrm.c
index 7ffe6a2690d1..6858cd2d16a4 100644
--- a/net/netfilter/nft_xfrm.c
+++ b/net/netfilter/nft_xfrm.c
@@ -17,8 +17,8 @@
 
 static const struct nla_policy nft_xfrm_policy[NFTA_XFRM_MAX + 1] = {
 	[NFTA_XFRM_KEY]		= NLA_POLICY_MAX(NLA_BE32, 255),
-	[NFTA_XFRM_DIR]		= { .type = NLA_U8 },
-	[NFTA_XFRM_SPNUM]	= NLA_POLICY_MAX(NLA_BE32, 255),
+	[NFTA_XFRM_DIR]		= NLA_POLICY_MAX(NLA_U8, XFRM_POLICY_OUT),
+	[NFTA_XFRM_SPNUM]	= NLA_POLICY_MAX(NLA_BE32, XFRM_MAX_DEPTH - 1),
 	[NFTA_XFRM_DREG]	= { .type = NLA_U32 },
 };
 
-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next 07/13] netfilter: nf_tables: add netlink policy based cap on registers
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

Should have no effect in practice; all of these use the
nft_parse_register_load/store apis which is mandatory anyway due
to the need to further validate the register load/store, e.g.
that the size argument doesn't result in out-of-bounds load/store.

OTOH this is a simple method to reject obviously wrong input
at earlier stage.

Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/uapi/linux/netfilter/nf_tables.h | 4 ++++
 net/netfilter/nft_bitwise.c              | 6 +++---
 net/netfilter/nft_byteorder.c            | 4 ++--
 net/netfilter/nft_cmp.c                  | 2 +-
 net/netfilter/nft_ct.c                   | 4 ++--
 net/netfilter/nft_exthdr.c               | 4 ++--
 net/netfilter/nft_fib.c                  | 2 +-
 net/netfilter/nft_hash.c                 | 4 ++--
 net/netfilter/nft_immediate.c            | 2 +-
 net/netfilter/nft_lookup.c               | 4 ++--
 net/netfilter/nft_meta.c                 | 4 ++--
 net/netfilter/nft_numgen.c               | 2 +-
 net/netfilter/nft_objref.c               | 2 +-
 net/netfilter/nft_osf.c                  | 2 +-
 net/netfilter/nft_payload.c              | 4 ++--
 net/netfilter/nft_range.c                | 2 +-
 net/netfilter/nft_rt.c                   | 2 +-
 net/netfilter/nft_socket.c               | 2 +-
 net/netfilter/nft_tunnel.c               | 2 +-
 net/netfilter/nft_xfrm.c                 | 2 +-
 20 files changed, 32 insertions(+), 28 deletions(-)

diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index dca9e72b0558..0b708153469c 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -46,6 +46,10 @@ enum nft_registers {
 };
 #define NFT_REG_MAX	(__NFT_REG_MAX - 1)
 
+#ifdef __KERNEL__
+#define NFT_REG32_MAX	NFT_REG32_15
+#endif
+
 #define NFT_REG_SIZE	16
 #define NFT_REG32_SIZE	4
 #define NFT_REG32_COUNT	(NFT_REG32_15 - NFT_REG32_00 + 1)
diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c
index a4ff781f334d..13808e9cd999 100644
--- a/net/netfilter/nft_bitwise.c
+++ b/net/netfilter/nft_bitwise.c
@@ -125,9 +125,9 @@ void nft_bitwise_eval(const struct nft_expr *expr,
 }
 
 static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = {
-	[NFTA_BITWISE_SREG]	= { .type = NLA_U32 },
-	[NFTA_BITWISE_SREG2]	= { .type = NLA_U32 },
-	[NFTA_BITWISE_DREG]	= { .type = NLA_U32 },
+	[NFTA_BITWISE_SREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
+	[NFTA_BITWISE_SREG2]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
+	[NFTA_BITWISE_DREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_BITWISE_LEN]	= { .type = NLA_U32 },
 	[NFTA_BITWISE_MASK]	= { .type = NLA_NESTED },
 	[NFTA_BITWISE_XOR]	= { .type = NLA_NESTED },
diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c
index 744878773dac..e00dddfa2fc0 100644
--- a/net/netfilter/nft_byteorder.c
+++ b/net/netfilter/nft_byteorder.c
@@ -87,8 +87,8 @@ void nft_byteorder_eval(const struct nft_expr *expr,
 }
 
 static const struct nla_policy nft_byteorder_policy[NFTA_BYTEORDER_MAX + 1] = {
-	[NFTA_BYTEORDER_SREG]	= { .type = NLA_U32 },
-	[NFTA_BYTEORDER_DREG]	= { .type = NLA_U32 },
+	[NFTA_BYTEORDER_SREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
+	[NFTA_BYTEORDER_DREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_BYTEORDER_OP]	= NLA_POLICY_MAX(NLA_BE32, 255),
 	[NFTA_BYTEORDER_LEN]	= NLA_POLICY_MAX(NLA_BE32, 255),
 	[NFTA_BYTEORDER_SIZE]	= NLA_POLICY_MAX(NLA_BE32, 255),
diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c
index b61dc9c3383e..e085c2a00b70 100644
--- a/net/netfilter/nft_cmp.c
+++ b/net/netfilter/nft_cmp.c
@@ -64,7 +64,7 @@ void nft_cmp_eval(const struct nft_expr *expr,
 }
 
 static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = {
-	[NFTA_CMP_SREG]		= { .type = NLA_U32 },
+	[NFTA_CMP_SREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_CMP_OP]		= { .type = NLA_U32 },
 	[NFTA_CMP_DATA]		= { .type = NLA_NESTED },
 };
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
index 00dabd985883..afa7142c529a 100644
--- a/net/netfilter/nft_ct.c
+++ b/net/netfilter/nft_ct.c
@@ -336,10 +336,10 @@ static void nft_ct_set_eval(const struct nft_expr *expr,
 }
 
 static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = {
-	[NFTA_CT_DREG]		= { .type = NLA_U32 },
+	[NFTA_CT_DREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_CT_KEY]		= NLA_POLICY_MAX(NLA_BE32, 255),
 	[NFTA_CT_DIRECTION]	= NLA_POLICY_MAX(NLA_U8, IP_CT_DIR_REPLY),
-	[NFTA_CT_SREG]		= { .type = NLA_U32 },
+	[NFTA_CT_SREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 };
 
 #ifdef CONFIG_NF_CONNTRACK_ZONES
diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
index b997307d94f9..0407d6f708ae 100644
--- a/net/netfilter/nft_exthdr.c
+++ b/net/netfilter/nft_exthdr.c
@@ -486,13 +486,13 @@ static void nft_exthdr_dccp_eval(const struct nft_expr *expr,
 #endif
 
 static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
-	[NFTA_EXTHDR_DREG]		= { .type = NLA_U32 },
+	[NFTA_EXTHDR_DREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_EXTHDR_TYPE]		= { .type = NLA_U8 },
 	[NFTA_EXTHDR_OFFSET]		= { .type = NLA_U32 },
 	[NFTA_EXTHDR_LEN]		= NLA_POLICY_MAX(NLA_BE32, 255),
 	[NFTA_EXTHDR_FLAGS]		= NLA_POLICY_MASK(NLA_BE32, NFT_EXTHDR_F_PRESENT),
 	[NFTA_EXTHDR_OP]		= NLA_POLICY_MAX(NLA_BE32, 255),
-	[NFTA_EXTHDR_SREG]		= { .type = NLA_U32 },
+	[NFTA_EXTHDR_SREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 };
 
 static int nft_exthdr_init(const struct nft_ctx *ctx,
diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c
index f7dc0e54375f..327a5f33659c 100644
--- a/net/netfilter/nft_fib.c
+++ b/net/netfilter/nft_fib.c
@@ -19,7 +19,7 @@
 			NFTA_FIB_F_PRESENT)
 
 const struct nla_policy nft_fib_policy[NFTA_FIB_MAX + 1] = {
-	[NFTA_FIB_DREG]		= { .type = NLA_U32 },
+	[NFTA_FIB_DREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_FIB_RESULT]	= { .type = NLA_U32 },
 	[NFTA_FIB_FLAGS]	=
 		NLA_POLICY_MASK(NLA_BE32, NFTA_FIB_F_ALL),
diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c
index 1cf41e0a0e0c..3bacc9b53789 100644
--- a/net/netfilter/nft_hash.c
+++ b/net/netfilter/nft_hash.c
@@ -58,8 +58,8 @@ static void nft_symhash_eval(const struct nft_expr *expr,
 }
 
 static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = {
-	[NFTA_HASH_SREG]	= { .type = NLA_U32 },
-	[NFTA_HASH_DREG]	= { .type = NLA_U32 },
+	[NFTA_HASH_SREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
+	[NFTA_HASH_DREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_HASH_LEN]		= NLA_POLICY_MAX(NLA_BE32, 255),
 	[NFTA_HASH_MODULUS]	= { .type = NLA_U32 },
 	[NFTA_HASH_SEED]	= { .type = NLA_U32 },
diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c
index 37c29947b380..1b733c7b1b0e 100644
--- a/net/netfilter/nft_immediate.c
+++ b/net/netfilter/nft_immediate.c
@@ -25,7 +25,7 @@ void nft_immediate_eval(const struct nft_expr *expr,
 }
 
 static const struct nla_policy nft_immediate_policy[NFTA_IMMEDIATE_MAX + 1] = {
-	[NFTA_IMMEDIATE_DREG]	= { .type = NLA_U32 },
+	[NFTA_IMMEDIATE_DREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_IMMEDIATE_DATA]	= { .type = NLA_NESTED },
 };
 
diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c
index e4e619027542..9fafe5afc490 100644
--- a/net/netfilter/nft_lookup.c
+++ b/net/netfilter/nft_lookup.c
@@ -125,8 +125,8 @@ static const struct nla_policy nft_lookup_policy[NFTA_LOOKUP_MAX + 1] = {
 	[NFTA_LOOKUP_SET]	= { .type = NLA_STRING,
 				    .len = NFT_SET_MAXNAMELEN - 1 },
 	[NFTA_LOOKUP_SET_ID]	= { .type = NLA_U32 },
-	[NFTA_LOOKUP_SREG]	= { .type = NLA_U32 },
-	[NFTA_LOOKUP_DREG]	= { .type = NLA_U32 },
+	[NFTA_LOOKUP_SREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
+	[NFTA_LOOKUP_DREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_LOOKUP_FLAGS]	=
 		NLA_POLICY_MASK(NLA_BE32, NFT_LOOKUP_F_INV),
 };
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
index d0df6cf374d1..7478063339d4 100644
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -460,9 +460,9 @@ void nft_meta_set_eval(const struct nft_expr *expr,
 EXPORT_SYMBOL_GPL(nft_meta_set_eval);
 
 const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
-	[NFTA_META_DREG]	= { .type = NLA_U32 },
+	[NFTA_META_DREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_META_KEY]		= NLA_POLICY_MAX(NLA_BE32, 255),
-	[NFTA_META_SREG]	= { .type = NLA_U32 },
+	[NFTA_META_SREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 };
 EXPORT_SYMBOL_GPL(nft_meta_policy);
 
diff --git a/net/netfilter/nft_numgen.c b/net/netfilter/nft_numgen.c
index 4d69b3399195..b0c802370159 100644
--- a/net/netfilter/nft_numgen.c
+++ b/net/netfilter/nft_numgen.c
@@ -43,7 +43,7 @@ static void nft_ng_inc_eval(const struct nft_expr *expr,
 }
 
 static const struct nla_policy nft_ng_policy[NFTA_NG_MAX + 1] = {
-	[NFTA_NG_DREG]		= { .type = NLA_U32 },
+	[NFTA_NG_DREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_NG_MODULUS]	= { .type = NLA_U32 },
 	[NFTA_NG_TYPE]		= { .type = NLA_U32 },
 	[NFTA_NG_OFFSET]	= { .type = NLA_U32 },
diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c
index 633cce69568f..249ded517446 100644
--- a/net/netfilter/nft_objref.c
+++ b/net/netfilter/nft_objref.c
@@ -265,7 +265,7 @@ static const struct nla_policy nft_objref_policy[NFTA_OBJREF_MAX + 1] = {
 	[NFTA_OBJREF_IMM_NAME]	= { .type = NLA_STRING,
 				    .len = NFT_OBJ_MAXNAMELEN - 1 },
 	[NFTA_OBJREF_IMM_TYPE]	= { .type = NLA_U32 },
-	[NFTA_OBJREF_SET_SREG]	= { .type = NLA_U32 },
+	[NFTA_OBJREF_SET_SREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_OBJREF_SET_NAME]	= { .type = NLA_STRING,
 				    .len = NFT_SET_MAXNAMELEN - 1 },
 	[NFTA_OBJREF_SET_ID]	= { .type = NLA_U32 },
diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c
index b2f44bc6bd3f..18003433476c 100644
--- a/net/netfilter/nft_osf.c
+++ b/net/netfilter/nft_osf.c
@@ -12,7 +12,7 @@ struct nft_osf {
 };
 
 static const struct nla_policy nft_osf_policy[NFTA_OSF_MAX + 1] = {
-	[NFTA_OSF_DREG]		= { .type = NLA_U32 },
+	[NFTA_OSF_DREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_OSF_TTL]		= { .type = NLA_U8 },
 	[NFTA_OSF_FLAGS]	= NLA_POLICY_MASK(NLA_BE32, NFT_OSF_F_VERSION),
 };
diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
index 91b62083d942..3fa3c6c835be 100644
--- a/net/netfilter/nft_payload.c
+++ b/net/netfilter/nft_payload.c
@@ -209,8 +209,8 @@ void nft_payload_eval(const struct nft_expr *expr,
 }
 
 static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = {
-	[NFTA_PAYLOAD_SREG]		= { .type = NLA_U32 },
-	[NFTA_PAYLOAD_DREG]		= { .type = NLA_U32 },
+	[NFTA_PAYLOAD_SREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
+	[NFTA_PAYLOAD_DREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_PAYLOAD_BASE]		= { .type = NLA_U32 },
 	[NFTA_PAYLOAD_OFFSET]		= { .type = NLA_BE32 },
 	[NFTA_PAYLOAD_LEN]		= NLA_POLICY_MAX(NLA_BE32, 255),
diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c
index cbb02644b836..f8a1641afccf 100644
--- a/net/netfilter/nft_range.c
+++ b/net/netfilter/nft_range.c
@@ -41,7 +41,7 @@ void nft_range_eval(const struct nft_expr *expr,
 }
 
 static const struct nla_policy nft_range_policy[NFTA_RANGE_MAX + 1] = {
-	[NFTA_RANGE_SREG]		= { .type = NLA_U32 },
+	[NFTA_RANGE_SREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_RANGE_OP]			= NLA_POLICY_MAX(NLA_BE32, 255),
 	[NFTA_RANGE_FROM_DATA]		= { .type = NLA_NESTED },
 	[NFTA_RANGE_TO_DATA]		= { .type = NLA_NESTED },
diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c
index ad527f3596c0..e23cd4759851 100644
--- a/net/netfilter/nft_rt.c
+++ b/net/netfilter/nft_rt.c
@@ -103,7 +103,7 @@ void nft_rt_get_eval(const struct nft_expr *expr,
 }
 
 static const struct nla_policy nft_rt_policy[NFTA_RT_MAX + 1] = {
-	[NFTA_RT_DREG]		= { .type = NLA_U32 },
+	[NFTA_RT_DREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_RT_KEY]		= NLA_POLICY_MAX(NLA_BE32, 255),
 };
 
diff --git a/net/netfilter/nft_socket.c b/net/netfilter/nft_socket.c
index c55a1310226a..a146a45d7531 100644
--- a/net/netfilter/nft_socket.c
+++ b/net/netfilter/nft_socket.c
@@ -163,7 +163,7 @@ static void nft_socket_eval(const struct nft_expr *expr,
 
 static const struct nla_policy nft_socket_policy[NFTA_SOCKET_MAX + 1] = {
 	[NFTA_SOCKET_KEY]		= NLA_POLICY_MAX(NLA_BE32, 255),
-	[NFTA_SOCKET_DREG]		= { .type = NLA_U32 },
+	[NFTA_SOCKET_DREG]		= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_SOCKET_LEVEL]		= NLA_POLICY_MAX(NLA_BE32, 255),
 };
 
diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c
index 65d06300f48a..0b987bc2132a 100644
--- a/net/netfilter/nft_tunnel.c
+++ b/net/netfilter/nft_tunnel.c
@@ -67,7 +67,7 @@ static void nft_tunnel_get_eval(const struct nft_expr *expr,
 
 static const struct nla_policy nft_tunnel_policy[NFTA_TUNNEL_MAX + 1] = {
 	[NFTA_TUNNEL_KEY]	= NLA_POLICY_MAX(NLA_BE32, 255),
-	[NFTA_TUNNEL_DREG]	= { .type = NLA_U32 },
+	[NFTA_TUNNEL_DREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 	[NFTA_TUNNEL_MODE]	= NLA_POLICY_MAX(NLA_BE32, NFT_TUNNEL_MODE_MAX),
 };
 
diff --git a/net/netfilter/nft_xfrm.c b/net/netfilter/nft_xfrm.c
index 6858cd2d16a4..65a75d88e5f0 100644
--- a/net/netfilter/nft_xfrm.c
+++ b/net/netfilter/nft_xfrm.c
@@ -19,7 +19,7 @@ static const struct nla_policy nft_xfrm_policy[NFTA_XFRM_MAX + 1] = {
 	[NFTA_XFRM_KEY]		= NLA_POLICY_MAX(NLA_BE32, 255),
 	[NFTA_XFRM_DIR]		= NLA_POLICY_MAX(NLA_U8, XFRM_POLICY_OUT),
 	[NFTA_XFRM_SPNUM]	= NLA_POLICY_MAX(NLA_BE32, XFRM_MAX_DEPTH - 1),
-	[NFTA_XFRM_DREG]	= { .type = NLA_U32 },
+	[NFTA_XFRM_DREG]	= NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
 };
 
 struct nft_xfrm {
-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next 08/13] netfilter: nft_set_pipapo: increment data in one step
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

Since commit e807b13cb3e3 ("nft_set_pipapo: Generalise group size for buckets")
there is no longer a need to increment the data pointer in two steps.
Switch to a single invocation of NFT_PIPAPO_GROUPS_PADDED_SIZE() helper,
like the avx2 implementation.

[ Stefano: Improve commit message ]

Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 net/netfilter/nft_set_pipapo.c | 4 +---
 net/netfilter/nft_set_pipapo.h | 3 ---
 2 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c
index 7fd24e0cc428..50d4a4f04309 100644
--- a/net/netfilter/nft_set_pipapo.c
+++ b/net/netfilter/nft_set_pipapo.c
@@ -452,8 +452,6 @@ static struct nft_pipapo_elem *pipapo_get_slow(const struct nft_pipapo_match *m,
 			pipapo_and_field_buckets_4bit(f, res_map, data);
 		NFT_PIPAPO_GROUP_BITS_ARE_8_OR_4;
 
-		data += f->groups / NFT_PIPAPO_GROUPS_PER_BYTE(f);
-
 		/* Now populate the bitmap for the next field, unless this is
 		 * the last field, in which case return the matched 'ext'
 		 * pointer if any.
@@ -498,7 +496,7 @@ static struct nft_pipapo_elem *pipapo_get_slow(const struct nft_pipapo_match *m,
 		map_index = !map_index;
 		swap(res_map, fill_map);
 
-		data += NFT_PIPAPO_GROUPS_PADDING(f);
+		data += NFT_PIPAPO_GROUPS_PADDED_SIZE(f);
 	}
 
 	__local_unlock_nested_bh(&scratch->bh_lock);
diff --git a/net/netfilter/nft_set_pipapo.h b/net/netfilter/nft_set_pipapo.h
index 9aee9a9eaeb7..b82abb03576e 100644
--- a/net/netfilter/nft_set_pipapo.h
+++ b/net/netfilter/nft_set_pipapo.h
@@ -42,9 +42,6 @@
 /* Fields are padded to 32 bits in input registers */
 #define NFT_PIPAPO_GROUPS_PADDED_SIZE(f)				\
 	(round_up((f)->groups / NFT_PIPAPO_GROUPS_PER_BYTE(f), sizeof(u32)))
-#define NFT_PIPAPO_GROUPS_PADDING(f)					\
-	(NFT_PIPAPO_GROUPS_PADDED_SIZE(f) - (f)->groups /		\
-					    NFT_PIPAPO_GROUPS_PER_BYTE(f))
 
 /* Number of buckets given by 2 ^ n, with n bucket bits */
 #define NFT_PIPAPO_BUCKETS(bb)		(1 << (bb))
-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next 09/13] netfilter: nft_set_pipapo_avx2: remove redundant loop in lookup_slow
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

nft_pipapo_avx2_lookup_slow will never be used in reality, because the
common sizes are handled by avx2 optimized versions.

However, nft_pipapo_avx2_lookup_slow loops over the data just like the
avx2 functions.  However, _slow doesn't need to do that.

As-is, first loop sets all the right result bits and the next iterations
boil down to 'x = x & x'.  Remove the loop.

Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 net/netfilter/nft_set_pipapo_avx2.c | 32 ++++++++---------------------
 1 file changed, 9 insertions(+), 23 deletions(-)

diff --git a/net/netfilter/nft_set_pipapo_avx2.c b/net/netfilter/nft_set_pipapo_avx2.c
index 6395982e4d95..dad265807b8b 100644
--- a/net/netfilter/nft_set_pipapo_avx2.c
+++ b/net/netfilter/nft_set_pipapo_avx2.c
@@ -1041,7 +1041,6 @@ static int nft_pipapo_avx2_lookup_8b_16(unsigned long *map, unsigned long *fill,
  * @map:	Previous match result, used as initial bitmap
  * @fill:	Destination bitmap to be filled with current match result
  * @f:		Field, containing lookup and mapping tables
- * @offset:	Ignore buckets before the given index, no bits are filled there
  * @pkt:	Packet data, pointer to input nftables register
  * @first:	If this is the first field, don't source previous result
  * @last:	Last field: stop at the first match and return bit index
@@ -1056,32 +1055,19 @@ static int nft_pipapo_avx2_lookup_8b_16(unsigned long *map, unsigned long *fill,
 static int nft_pipapo_avx2_lookup_slow(const struct nft_pipapo_match *mdata,
 					unsigned long *map, unsigned long *fill,
 					const struct nft_pipapo_field *f,
-					int offset, const u8 *pkt,
+					const u8 *pkt,
 					bool first, bool last)
 {
-	unsigned long bsize = f->bsize;
-	int i, ret = -1, b;
-
 	if (first)
 		pipapo_resmap_init(mdata, map);
 
-	for (i = offset; i < bsize; i++) {
-		if (f->bb == 8)
-			pipapo_and_field_buckets_8bit(f, map, pkt);
-		else
-			pipapo_and_field_buckets_4bit(f, map, pkt);
-		NFT_PIPAPO_GROUP_BITS_ARE_8_OR_4;
-
-		b = pipapo_refill(map, bsize, f->rules, fill, f->mt, last);
+	if (f->bb == 8)
+		pipapo_and_field_buckets_8bit(f, map, pkt);
+	else
+		pipapo_and_field_buckets_4bit(f, map, pkt);
+	NFT_PIPAPO_GROUP_BITS_ARE_8_OR_4;
 
-		if (last)
-			return b;
-
-		if (ret == -1)
-			ret = b / XSAVE_YMM_SIZE;
-	}
-
-	return ret;
+	return pipapo_refill(map, f->bsize, f->rules, fill, f->mt, last);
 }
 
 /**
@@ -1201,7 +1187,7 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m,
 				NFT_SET_PIPAPO_AVX2_LOOKUP(8, 16);
 			} else {
 				ret = nft_pipapo_avx2_lookup_slow(m, res, fill, f,
-								  ret, data,
+								  data,
 								  first, last);
 			}
 		} else {
@@ -1217,7 +1203,7 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m,
 				NFT_SET_PIPAPO_AVX2_LOOKUP(4, 32);
 			} else {
 				ret = nft_pipapo_avx2_lookup_slow(m, res, fill, f,
-								  ret, data,
+								  data,
 								  first, last);
 			}
 		}
-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next 10/13] netfilter: nft_meta: add double-tagged vlan and pppoe support
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

From: Pablo Neira Ayuso <pablo@netfilter.org>

Currently:

  add rule netdev x y ip saddr 1.1.1.1

does not work with neither double-tagged vlan nor pppoe packets. This is
because the network and transport header offset are not pointing to the
IP and transport protocol headers in the stack.

This patch expands NFT_META_PROTOCOL and NFT_META_L4PROTO to parse
double-tagged vlan and pppoe packets so matching network and transport
header fields becomes possible with the existing userspace generated
bytecode. Note that this parser only supports double-tagged vlan which
is composed of vlan offload + vlan header in the skb payload area for
simplicity.

NFT_META_PROTOCOL is used by bridge and netdev family as an implicit
dependency in the bytecode to match on network header fields.
Similarly, there is also NFT_META_L4PROTO, which is also used as an
implicit dependency when matching on the transport protocol header
fields.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/net/netfilter/nf_tables.h      |  4 ++
 include/net/netfilter/nf_tables_ipv4.h | 17 +++++---
 include/net/netfilter/nf_tables_ipv6.h | 16 +++++---
 net/netfilter/nf_tables_core.c         |  2 +-
 net/netfilter/nft_meta.c               | 54 +++++++++++++++++++++++++-
 net/netfilter/nft_payload.c            |  2 +-
 6 files changed, 82 insertions(+), 13 deletions(-)

diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 66cedcfa338e..2c0173d9309c 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -31,7 +31,9 @@ struct nft_pktinfo {
 	const struct nf_hook_state	*state;
 	u8				flags;
 	u8				tprot;
+	__be16				ethertype;
 	u16				fragoff;
+	u16				nhoff;
 	u16				thoff;
 	u16				inneroff;
 };
@@ -83,6 +85,8 @@ static inline void nft_set_pktinfo_unspec(struct nft_pktinfo *pkt)
 {
 	pkt->flags = 0;
 	pkt->tprot = 0;
+	pkt->ethertype = pkt->skb->protocol;
+	pkt->nhoff = 0;
 	pkt->thoff = 0;
 	pkt->fragoff = 0;
 }
diff --git a/include/net/netfilter/nf_tables_ipv4.h b/include/net/netfilter/nf_tables_ipv4.h
index fcf967286e37..e715405a73cb 100644
--- a/include/net/netfilter/nf_tables_ipv4.h
+++ b/include/net/netfilter/nf_tables_ipv4.h
@@ -12,16 +12,19 @@ static inline void nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt)
 	ip = ip_hdr(pkt->skb);
 	pkt->flags = NFT_PKTINFO_L4PROTO;
 	pkt->tprot = ip->protocol;
+	pkt->ethertype = pkt->skb->protocol;
+	pkt->nhoff = 0;
 	pkt->thoff = ip_hdrlen(pkt->skb);
 	pkt->fragoff = ntohs(ip->frag_off) & IP_OFFSET;
 }
 
-static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
+static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt,
+						  int nhoff)
 {
 	struct iphdr *iph, _iph;
 	u32 len, thoff, skb_len;
 
-	iph = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb),
+	iph = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb) + nhoff,
 				 sizeof(*iph), &_iph);
 	if (!iph)
 		return -1;
@@ -31,7 +34,7 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
 
 	len = iph_totlen(pkt->skb, iph);
 	thoff = iph->ihl * 4;
-	skb_len = pkt->skb->len - skb_network_offset(pkt->skb);
+	skb_len = pkt->skb->len - skb_network_offset(pkt->skb) - nhoff;
 
 	if (skb_len < len)
 		return -1;
@@ -42,7 +45,9 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
 
 	pkt->flags = NFT_PKTINFO_L4PROTO;
 	pkt->tprot = iph->protocol;
-	pkt->thoff = skb_network_offset(pkt->skb) + thoff;
+	pkt->ethertype = pkt->skb->protocol;
+	pkt->nhoff = nhoff;
+	pkt->thoff = skb_network_offset(pkt->skb) + nhoff + thoff;
 	pkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET;
 
 	return 0;
@@ -50,7 +55,7 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
 
 static inline void nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
 {
-	if (__nft_set_pktinfo_ipv4_validate(pkt) < 0)
+	if (__nft_set_pktinfo_ipv4_validate(pkt, 0) < 0)
 		nft_set_pktinfo_unspec(pkt);
 }
 
@@ -78,6 +83,8 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt)
 	}
 
 	pkt->flags = NFT_PKTINFO_L4PROTO;
+	pkt->ethertype = pkt->skb->protocol;
+	pkt->nhoff = 0;
 	pkt->tprot = iph->protocol;
 	pkt->thoff = thoff;
 	pkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET;
diff --git a/include/net/netfilter/nf_tables_ipv6.h b/include/net/netfilter/nf_tables_ipv6.h
index c53ac00bb974..d7b8c559b795 100644
--- a/include/net/netfilter/nf_tables_ipv6.h
+++ b/include/net/netfilter/nf_tables_ipv6.h
@@ -20,21 +20,23 @@ static inline void nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt)
 
 	pkt->flags = NFT_PKTINFO_L4PROTO;
 	pkt->tprot = protohdr;
+	pkt->ethertype = pkt->skb->protocol;
+	pkt->nhoff = 0;
 	pkt->thoff = thoff;
 	pkt->fragoff = frag_off;
 }
 
-static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
+static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt, int nhoff)
 {
 #if IS_ENABLED(CONFIG_IPV6)
 	unsigned int flags = IP6_FH_F_AUTH;
 	struct ipv6hdr *ip6h, _ip6h;
-	unsigned int thoff = 0;
+	unsigned int thoff = nhoff;
 	unsigned short frag_off;
 	u32 pkt_len, skb_len;
 	int protohdr;
 
-	ip6h = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb),
+	ip6h = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb) + nhoff,
 				  sizeof(*ip6h), &_ip6h);
 	if (!ip6h)
 		return -1;
@@ -43,7 +45,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
 		return -1;
 
 	pkt_len = ipv6_payload_len(pkt->skb, ip6h);
-	skb_len = pkt->skb->len - skb_network_offset(pkt->skb);
+	skb_len = pkt->skb->len - skb_network_offset(pkt->skb) - nhoff;
 	if (pkt_len + sizeof(*ip6h) > skb_len)
 		return -1;
 
@@ -53,6 +55,8 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
 
 	pkt->flags = NFT_PKTINFO_L4PROTO;
 	pkt->tprot = protohdr;
+	pkt->ethertype = pkt->skb->protocol;
+	pkt->nhoff = nhoff;
 	pkt->thoff = thoff;
 	pkt->fragoff = frag_off;
 
@@ -64,7 +68,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
 
 static inline void nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
 {
-	if (__nft_set_pktinfo_ipv6_validate(pkt) < 0)
+	if (__nft_set_pktinfo_ipv6_validate(pkt, 0) < 0)
 		nft_set_pktinfo_unspec(pkt);
 }
 
@@ -99,6 +103,8 @@ static inline int nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo *pkt)
 
 	pkt->flags = NFT_PKTINFO_L4PROTO;
 	pkt->tprot = protohdr;
+	pkt->ethertype = pkt->skb->protocol;
+	pkt->nhoff = 0;
 	pkt->thoff = thoff;
 	pkt->fragoff = frag_off;
 
diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c
index 6557a4018c09..5ddd5b6e135f 100644
--- a/net/netfilter/nf_tables_core.c
+++ b/net/netfilter/nf_tables_core.c
@@ -151,7 +151,7 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr,
 	unsigned char *ptr;
 
 	if (priv->base == NFT_PAYLOAD_NETWORK_HEADER)
-		ptr = skb_network_header(skb);
+		ptr = skb_network_header(skb) + pkt->nhoff;
 	else {
 		if (!(pkt->flags & NFT_PKTINFO_L4PROTO))
 			return false;
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
index 7478063339d4..5b25851381e5 100644
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -23,6 +23,8 @@
 #include <net/tcp_states.h> /* for TCP_TIME_WAIT */
 #include <net/netfilter/nf_tables.h>
 #include <net/netfilter/nf_tables_core.h>
+#include <net/netfilter/nf_tables_ipv4.h>
+#include <net/netfilter/nf_tables_ipv6.h>
 #include <net/netfilter/nft_meta.h>
 #include <net/netfilter/nf_tables_offload.h>
 
@@ -309,6 +311,54 @@ nft_meta_get_eval_sdifname(u32 *dest, const struct nft_pktinfo *pkt)
 	nft_meta_store_ifname(dest, dev);
 }
 
+static void nft_meta_pktinfo_may_update(struct nft_pktinfo *pkt)
+{
+	struct sk_buff *skb = pkt->skb;
+	struct vlan_ethhdr *veth;
+	__be16 ethertype;
+	int nhoff;
+
+	/* Is this an IP packet? Then, skip. */
+	if (pkt->flags)
+		return;
+
+	/* ... else maybe an IP packet over PPPoE or Q-in-Q? */
+	switch (skb->protocol) {
+	case htons(ETH_P_8021Q):
+		if (!pskb_may_pull(skb, skb_mac_offset(skb) + sizeof(*veth)))
+			return;
+
+		veth = (struct vlan_ethhdr *)skb_mac_header(skb);
+		nhoff = VLAN_HLEN;
+		ethertype = veth->h_vlan_encapsulated_proto;
+		break;
+	case htons(ETH_P_PPP_SES):
+		if (!nf_flow_pppoe_proto(skb, &ethertype))
+			return;
+
+		nhoff = PPPOE_SES_HLEN;
+		break;
+	default:
+		return;
+	}
+
+	nhoff += skb_network_offset(skb);
+	switch (ethertype) {
+	case htons(ETH_P_IP):
+		if (__nft_set_pktinfo_ipv4_validate(pkt, nhoff))
+			nft_set_pktinfo_unspec(pkt);
+		break;
+	case htons(ETH_P_IPV6):
+		if (__nft_set_pktinfo_ipv6_validate(pkt, nhoff))
+			nft_set_pktinfo_unspec(pkt);
+		break;
+	default:
+		break;
+	}
+
+	pkt->ethertype = ethertype;
+}
+
 void nft_meta_get_eval(const struct nft_expr *expr,
 		       struct nft_regs *regs,
 		       const struct nft_pktinfo *pkt)
@@ -322,12 +372,14 @@ void nft_meta_get_eval(const struct nft_expr *expr,
 		*dest = skb->len;
 		break;
 	case NFT_META_PROTOCOL:
-		nft_reg_store16(dest, (__force u16)skb->protocol);
+		nft_meta_pktinfo_may_update((struct nft_pktinfo *)pkt);
+		nft_reg_store16(dest, (__force u16)pkt->ethertype);
 		break;
 	case NFT_META_NFPROTO:
 		nft_reg_store8(dest, nft_pf(pkt));
 		break;
 	case NFT_META_L4PROTO:
+		nft_meta_pktinfo_may_update((struct nft_pktinfo *)pkt);
 		if (!(pkt->flags & NFT_PKTINFO_L4PROTO))
 			goto err;
 		nft_reg_store8(dest, pkt->tprot);
diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
index 3fa3c6c835be..01e13e5255a9 100644
--- a/net/netfilter/nft_payload.c
+++ b/net/netfilter/nft_payload.c
@@ -183,7 +183,7 @@ void nft_payload_eval(const struct nft_expr *expr,
 		offset = skb_mac_header(skb) - skb->data;
 		break;
 	case NFT_PAYLOAD_NETWORK_HEADER:
-		offset = skb_network_offset(skb);
+		offset = skb_network_offset(skb) + pkt->nhoff;
 		break;
 	case NFT_PAYLOAD_TRANSPORT_HEADER:
 		if (!(pkt->flags & NFT_PKTINFO_L4PROTO) || pkt->fragoff)
-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next 11/13] netfilter: nf_conntrack_h323: Correct indentation when H323_TRACE defined
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

From: David Laight <david.laight.linux@gmail.com>

The trace lines are indented using PRINT("%*.s", xx, " ").
Userspace will treat this as "%*.0s" and will output no characters
when 'xx' is zero, the kernel treats it as "%*s" and will output
a single ' ' - which is probably what is intended.

Change all the formats to "%*s" removing the default precision.
This gives a single space indent when level is zero.

Signed-off-by: David Laight <david.laight.linux@gmail.com>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 net/netfilter/nf_conntrack_h323_asn1.c | 38 +++++++++++++-------------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/net/netfilter/nf_conntrack_h323_asn1.c b/net/netfilter/nf_conntrack_h323_asn1.c
index 09e0f724644f..6830c9da3507 100644
--- a/net/netfilter/nf_conntrack_h323_asn1.c
+++ b/net/netfilter/nf_conntrack_h323_asn1.c
@@ -274,7 +274,7 @@ static unsigned int get_uint(struct bitstr *bs, int b)
 static int decode_nul(struct bitstr *bs, const struct field_t *f,
                       char *base, int level)
 {
-	PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
+	PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name);
 
 	return H323_ERROR_NONE;
 }
@@ -282,7 +282,7 @@ static int decode_nul(struct bitstr *bs, const struct field_t *f,
 static int decode_bool(struct bitstr *bs, const struct field_t *f,
                        char *base, int level)
 {
-	PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
+	PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name);
 
 	INC_BIT(bs);
 	if (nf_h323_error_boundary(bs, 0, 0))
@@ -295,7 +295,7 @@ static int decode_oid(struct bitstr *bs, const struct field_t *f,
 {
 	int len;
 
-	PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
+	PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name);
 
 	BYTE_ALIGN(bs);
 	if (nf_h323_error_boundary(bs, 1, 0))
@@ -314,7 +314,7 @@ static int decode_int(struct bitstr *bs, const struct field_t *f,
 {
 	unsigned int len;
 
-	PRINT("%*.s%s", level * TAB_SIZE, " ", f->name);
+	PRINT("%*s%s", level * TAB_SIZE, " ", f->name);
 
 	switch (f->sz) {
 	case BYTE:		/* Range == 256 */
@@ -361,7 +361,7 @@ static int decode_int(struct bitstr *bs, const struct field_t *f,
 static int decode_enum(struct bitstr *bs, const struct field_t *f,
                        char *base, int level)
 {
-	PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
+	PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name);
 
 	if ((f->attr & EXT) && get_bit(bs)) {
 		INC_BITS(bs, 7);
@@ -379,7 +379,7 @@ static int decode_bitstr(struct bitstr *bs, const struct field_t *f,
 {
 	unsigned int len;
 
-	PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
+	PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name);
 
 	BYTE_ALIGN(bs);
 	switch (f->sz) {
@@ -415,7 +415,7 @@ static int decode_numstr(struct bitstr *bs, const struct field_t *f,
 {
 	unsigned int len;
 
-	PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
+	PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name);
 
 	/* 2 <= Range <= 255 */
 	if (nf_h323_error_boundary(bs, 0, f->sz))
@@ -435,7 +435,7 @@ static int decode_octstr(struct bitstr *bs, const struct field_t *f,
 {
 	unsigned int len;
 
-	PRINT("%*.s%s", level * TAB_SIZE, " ", f->name);
+	PRINT("%*s%s", level * TAB_SIZE, " ", f->name);
 
 	switch (f->sz) {
 	case FIXD:		/* Range == 1 */
@@ -483,7 +483,7 @@ static int decode_bmpstr(struct bitstr *bs, const struct field_t *f,
 {
 	unsigned int len;
 
-	PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
+	PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name);
 
 	switch (f->sz) {
 	case BYTE:		/* Range == 256 */
@@ -515,7 +515,7 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f,
 	const struct field_t *son;
 	unsigned char *beg = NULL;
 
-	PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
+	PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name);
 
 	/* Decode? */
 	base = (base && (f->attr & DECODE)) ? base + f->offset : NULL;
@@ -537,7 +537,7 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f,
 	/* Decode the root components */
 	for (i = opt = 0, son = f->fields; i < f->lb; i++, son++) {
 		if (son->attr & STOP) {
-			PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ",
+			PRINT("%*s%s\n", (level + 1) * TAB_SIZE, " ",
 			      son->name);
 			return H323_ERROR_STOP;
 		}
@@ -555,7 +555,7 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f,
 			if (nf_h323_error_boundary(bs, len, 0))
 				return H323_ERROR_BOUND;
 			if (!base || !(son->attr & DECODE)) {
-				PRINT("%*.s%s\n", (level + 1) * TAB_SIZE,
+				PRINT("%*s%s\n", (level + 1) * TAB_SIZE,
 				      " ", son->name);
 				bs->cur += len;
 				continue;
@@ -608,7 +608,7 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f,
 		}
 
 		if (son->attr & STOP) {
-			PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ",
+			PRINT("%*s%s\n", (level + 1) * TAB_SIZE, " ",
 			      son->name);
 			return H323_ERROR_STOP;
 		}
@@ -622,7 +622,7 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f,
 		if (nf_h323_error_boundary(bs, len, 0))
 			return H323_ERROR_BOUND;
 		if (!base || !(son->attr & DECODE)) {
-			PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ",
+			PRINT("%*s%s\n", (level + 1) * TAB_SIZE, " ",
 			      son->name);
 			bs->cur += len;
 			continue;
@@ -648,7 +648,7 @@ static int decode_seqof(struct bitstr *bs, const struct field_t *f,
 	const struct field_t *son;
 	unsigned char *beg = NULL;
 
-	PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
+	PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name);
 
 	/* Decode? */
 	base = (base && (f->attr & DECODE)) ? base + f->offset : NULL;
@@ -703,7 +703,7 @@ static int decode_seqof(struct bitstr *bs, const struct field_t *f,
 			if (nf_h323_error_boundary(bs, len, 0))
 				return H323_ERROR_BOUND;
 			if (!base || !(son->attr & DECODE)) {
-				PRINT("%*.s%s\n", (level + 1) * TAB_SIZE,
+				PRINT("%*s%s\n", (level + 1) * TAB_SIZE,
 				      " ", son->name);
 				bs->cur += len;
 				continue;
@@ -744,7 +744,7 @@ static int decode_choice(struct bitstr *bs, const struct field_t *f,
 	const struct field_t *son;
 	unsigned char *beg = NULL;
 
-	PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name);
+	PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name);
 
 	/* Decode? */
 	base = (base && (f->attr & DECODE)) ? base + f->offset : NULL;
@@ -785,7 +785,7 @@ static int decode_choice(struct bitstr *bs, const struct field_t *f,
 	/* Transfer to son level */
 	son = &f->fields[type];
 	if (son->attr & STOP) {
-		PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ", son->name);
+		PRINT("%*s%s\n", (level + 1) * TAB_SIZE, " ", son->name);
 		return H323_ERROR_STOP;
 	}
 
@@ -797,7 +797,7 @@ static int decode_choice(struct bitstr *bs, const struct field_t *f,
 		if (nf_h323_error_boundary(bs, len, 0))
 			return H323_ERROR_BOUND;
 		if (!base || !(son->attr & DECODE)) {
-			PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ",
+			PRINT("%*s%s\n", (level + 1) * TAB_SIZE, " ",
 			      son->name);
 			bs->cur += len;
 			return H323_ERROR_NONE;
-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next 12/13] netfilter: nf_tables_offload: add nft_flow_action_entry_next() and use it
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

From: Pablo Neira Ayuso <pablo@netfilter.org>

Add a new helper function to retrieve the next action entry in flow
rule, check if the maximum number of actions is reached, bail out in
such case.

Replace existing opencoded iteration on the action array by this
helper function.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/net/netfilter/nf_tables_offload.h | 10 ++++++++++
 net/netfilter/nf_dup_netdev.c             |  5 ++++-
 net/netfilter/nft_immediate.c             |  4 +++-
 3 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/include/net/netfilter/nf_tables_offload.h b/include/net/netfilter/nf_tables_offload.h
index 3568b6a2f5f0..14c427891ee6 100644
--- a/include/net/netfilter/nf_tables_offload.h
+++ b/include/net/netfilter/nf_tables_offload.h
@@ -67,6 +67,16 @@ struct nft_flow_rule {
 	struct flow_rule	*rule;
 };
 
+static inline struct flow_action_entry *
+nft_flow_action_entry_next(struct nft_offload_ctx *ctx,
+			   struct nft_flow_rule *flow)
+{
+	if (unlikely(ctx->num_actions >= flow->rule->action.num_entries))
+		return NULL;
+
+	return &flow->rule->action.entries[ctx->num_actions++];
+}
+
 void nft_flow_rule_set_addr_type(struct nft_flow_rule *flow,
 				 enum flow_dissector_key_id addr_type);
 
diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c
index fab8b9011098..e348fb90b8dc 100644
--- a/net/netfilter/nf_dup_netdev.c
+++ b/net/netfilter/nf_dup_netdev.c
@@ -95,7 +95,10 @@ int nft_fwd_dup_netdev_offload(struct nft_offload_ctx *ctx,
 	if (!dev)
 		return -EOPNOTSUPP;
 
-	entry = &flow->rule->action.entries[ctx->num_actions++];
+	entry = nft_flow_action_entry_next(ctx, flow);
+	if (!entry)
+		return -E2BIG;
+
 	entry->id = id;
 	entry->dev = dev;
 
diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c
index 1b733c7b1b0e..d00eb2eb30e4 100644
--- a/net/netfilter/nft_immediate.c
+++ b/net/netfilter/nft_immediate.c
@@ -279,7 +279,9 @@ static int nft_immediate_offload_verdict(struct nft_offload_ctx *ctx,
 	struct flow_action_entry *entry;
 	const struct nft_data *data;
 
-	entry = &flow->rule->action.entries[ctx->num_actions++];
+	entry = nft_flow_action_entry_next(ctx, flow);
+	if (!entry)
+		return -E2BIG;
 
 	data = &priv->data;
 	switch (data->verdict.code) {
-- 
2.52.0


^ permalink raw reply related

* [PATCH net-next 13/13] netfilter: ctnetlink: restrict expectfn to helper
From: Florian Westphal @ 2026-04-07 14:15 UTC (permalink / raw)
  To: netdev
  Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
	netfilter-devel, pablo
In-Reply-To: <20260407141540.11549-1-fw@strlen.de>

From: Pablo Neira Ayuso <pablo@netfilter.org>

The expectfn callback determines how nf_nat_setup_info() is invoked for
this expectation.

This patch restricts expectfn to the master conntrack helper, there is
nf_nat_follow_master() that is used by most expectations to deal with
nat. However, sip and h.323 helpers still offer their own variants for
different purpose.

Add a new helper field to struct nf_ct_helper_expectfn to restrict the
expectfn to its helper. If NULL, then this can be used by any
expectation, which is the case nf_nat_follow_master().

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 include/net/netfilter/nf_conntrack_helper.h | 3 ++-
 net/ipv4/netfilter/nf_nat_h323.c            | 2 ++
 net/netfilter/nf_conntrack_helper.c         | 5 +++--
 net/netfilter/nf_conntrack_netlink.c        | 2 +-
 net/netfilter/nf_nat_sip.c                  | 1 +
 5 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h
index de2f956abf34..dc566921cc73 100644
--- a/include/net/netfilter/nf_conntrack_helper.h
+++ b/include/net/netfilter/nf_conntrack_helper.h
@@ -145,6 +145,7 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, struct nf_conn *ct,
 
 struct nf_ct_helper_expectfn {
 	struct list_head head;
+	const char *helper;
 	const char *name;
 	void (*expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp);
 };
@@ -156,7 +157,7 @@ void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
 void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n);
 void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n);
 struct nf_ct_helper_expectfn *
-nf_ct_helper_expectfn_find_by_name(const char *name);
+nf_ct_helper_expectfn_find_by_name(const char *helper, const char *name);
 struct nf_ct_helper_expectfn *
 nf_ct_helper_expectfn_find_by_symbol(const void *symbol);
 
diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c
index faee20af4856..21353623130c 100644
--- a/net/ipv4/netfilter/nf_nat_h323.c
+++ b/net/ipv4/netfilter/nf_nat_h323.c
@@ -518,11 +518,13 @@ static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct,
 }
 
 static struct nf_ct_helper_expectfn q931_nat = {
+	.helper		= "RAS",
 	.name		= "Q.931",
 	.expectfn	= ip_nat_q931_expect,
 };
 
 static struct nf_ct_helper_expectfn callforwarding_nat = {
+	.helper		= "Q.931",
 	.name		= "callforwarding",
 	.expectfn	= ip_nat_callforwarding_expect,
 };
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index a715304a53d8..5e6d2687a558 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -285,13 +285,14 @@ EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_unregister);
 
 /* Caller should hold the rcu lock */
 struct nf_ct_helper_expectfn *
-nf_ct_helper_expectfn_find_by_name(const char *name)
+nf_ct_helper_expectfn_find_by_name(const char *helper, const char *name)
 {
 	struct nf_ct_helper_expectfn *cur;
 	bool found = false;
 
 	list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
-		if (!strcmp(cur->name, name)) {
+		if ((cur->helper && !strcmp(cur->helper, helper)) ||
+		    !strcmp(cur->name, name)) {
 			found = true;
 			break;
 		}
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index eda5fe4a75c8..7744f67a0fbe 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -3552,7 +3552,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct,
 		const char *name = nla_data(cda[CTA_EXPECT_FN]);
 		struct nf_ct_helper_expectfn *expfn;
 
-		expfn = nf_ct_helper_expectfn_find_by_name(name);
+		expfn = nf_ct_helper_expectfn_find_by_name(helper->name, name);
 		if (expfn == NULL) {
 			err = -EINVAL;
 			goto err_out;
diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
index cf4aeb299bde..047733012963 100644
--- a/net/netfilter/nf_nat_sip.c
+++ b/net/netfilter/nf_nat_sip.c
@@ -641,6 +641,7 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,
 }
 
 static struct nf_ct_helper_expectfn sip_nat = {
+	.helper		= "sip",
 	.name		= "sip",
 	.expectfn	= nf_nat_sip_expected,
 };
-- 
2.52.0


^ permalink raw reply related

* Re: [PATCH net-next 13/13] netfilter: ctnetlink: restrict expectfn to helper
From: Florian Westphal @ 2026-04-07 14:27 UTC (permalink / raw)
  To: pablo; +Cc: netdev
In-Reply-To: <20260407141540.11549-14-fw@strlen.de>

Florian Westphal <fw@strlen.de> wrote:
>  	list_for_each_entry_rcu(cur, &nf_ct_helper_expectfn_list, head) {
> -		if (!strcmp(cur->name, name)) {
> +		if ((cur->helper && !strcmp(cur->helper, helper)) ||
> +		    !strcmp(cur->name, name)) {

Sigh, I don't know why I did not see this earlier.  It looks wrong.

Should this be:

if ((cur->helper && strcmp(cur->helper, helper))
	continue; // skip, name doesn't match

if (!strcmp(cur->name, name)) {
 ...

as is, this restriction has no effect in case the requested
name matches?

AI suggests

if ((cur->helper && !strcmp(cur->helper, helper)) &&
   !strcmp(cur->name, name)) {

... but i think thats bogus too.  What to do?

Send v2 or do you want to followup later?

^ permalink raw reply

* [PATCH net-next] codel: annotate data-races in codel_dump_stats()
From: Eric Dumazet @ 2026-04-07 14:30 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Jamal Hadi Salim, Jiri Pirko, netdev, eric.dumazet,
	Eric Dumazet

codel_dump_stats() only runs with RTNL held,
reading fields that can be changed in qdisc fast path.

Add READ_ONCE()/WRITE_ONCE() annotations.

Alternative would be to acquire the qdisc spinlock, but our long-term
goal is to make qdisc dump operations lockless as much as we can.

tc_codel_xstats fields don't need to be latched atomically,
otherwise this bug would have been caught earlier.

No change in kernel size:

$ scripts/bloat-o-meter -t vmlinux.0 vmlinux
add/remove: 0/0 grow/shrink: 1/1 up/down: 3/-1 (2)
Function                                     old     new   delta
codel_qdisc_dequeue                         2462    2465      +3
codel_dump_stats                             250     249      -1
Total: Before=29739919, After=29739921, chg +0.00%

Fixes: 76e3cc126bb2 ("codel: Controlled Delay AQM")
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net-next tree has been chosen to avoid a merge conflict.
I will handle stable submission, this bug is minor.

 include/net/codel_impl.h | 45 +++++++++++++++++++++-------------------
 net/sched/sch_codel.c    | 22 ++++++++++----------
 2 files changed, 35 insertions(+), 32 deletions(-)

diff --git a/include/net/codel_impl.h b/include/net/codel_impl.h
index b2c359c6dd1b84e3aeb77834272641bac73f8e6a..2c1f0ec309e9fe02e79d377129da6c5c0cbce19b 100644
--- a/include/net/codel_impl.h
+++ b/include/net/codel_impl.h
@@ -120,10 +120,10 @@ static bool codel_should_drop(const struct sk_buff *skb,
 	}
 
 	skb_len = skb_len_func(skb);
-	vars->ldelay = now - skb_time_func(skb);
+	WRITE_ONCE(vars->ldelay, now - skb_time_func(skb));
 
 	if (unlikely(skb_len > stats->maxpacket))
-		stats->maxpacket = skb_len;
+		WRITE_ONCE(stats->maxpacket, skb_len);
 
 	if (codel_time_before(vars->ldelay, params->target) ||
 	    *backlog <= params->mtu) {
@@ -159,7 +159,7 @@ static struct sk_buff *codel_dequeue(void *ctx,
 
 	if (!skb) {
 		vars->first_above_time = 0;
-		vars->dropping = false;
+		WRITE_ONCE(vars->dropping, false);
 		return skb;
 	}
 	now = codel_get_time();
@@ -168,7 +168,7 @@ static struct sk_buff *codel_dequeue(void *ctx,
 	if (vars->dropping) {
 		if (!drop) {
 			/* sojourn time below target - leave dropping state */
-			vars->dropping = false;
+			WRITE_ONCE(vars->dropping, false);
 		} else if (codel_time_after_eq(now, vars->drop_next)) {
 			/* It's time for the next drop. Drop the current
 			 * packet and dequeue the next. The dequeue might
@@ -180,16 +180,18 @@ static struct sk_buff *codel_dequeue(void *ctx,
 			 */
 			while (vars->dropping &&
 			       codel_time_after_eq(now, vars->drop_next)) {
-				vars->count++; /* dont care of possible wrap
-						* since there is no more divide
-						*/
+				/* dont care of possible wrap
+				 * since there is no more divide.
+				 */
+				WRITE_ONCE(vars->count, vars->count + 1);
 				codel_Newton_step(vars);
 				if (params->ecn && INET_ECN_set_ce(skb)) {
-					stats->ecn_mark++;
-					vars->drop_next =
+					WRITE_ONCE(stats->ecn_mark,
+						   stats->ecn_mark + 1);
+					WRITE_ONCE(vars->drop_next,
 						codel_control_law(vars->drop_next,
 								  params->interval,
-								  vars->rec_inv_sqrt);
+								  vars->rec_inv_sqrt));
 					goto end;
 				}
 				stats->drop_len += skb_len_func(skb);
@@ -202,13 +204,13 @@ static struct sk_buff *codel_dequeue(void *ctx,
 						       skb_time_func,
 						       backlog, now)) {
 					/* leave dropping state */
-					vars->dropping = false;
+					WRITE_ONCE(vars->dropping, false);
 				} else {
 					/* and schedule the next drop */
-					vars->drop_next =
+					WRITE_ONCE(vars->drop_next,
 						codel_control_law(vars->drop_next,
 								  params->interval,
-								  vars->rec_inv_sqrt);
+								  vars->rec_inv_sqrt));
 				}
 			}
 		}
@@ -216,7 +218,7 @@ static struct sk_buff *codel_dequeue(void *ctx,
 		u32 delta;
 
 		if (params->ecn && INET_ECN_set_ce(skb)) {
-			stats->ecn_mark++;
+			WRITE_ONCE(stats->ecn_mark, stats->ecn_mark + 1);
 		} else {
 			stats->drop_len += skb_len_func(skb);
 			drop_func(skb, ctx);
@@ -227,7 +229,7 @@ static struct sk_buff *codel_dequeue(void *ctx,
 						 stats, skb_len_func,
 						 skb_time_func, backlog, now);
 		}
-		vars->dropping = true;
+		WRITE_ONCE(vars->dropping, true);
 		/* if min went above target close to when we last went below it
 		 * assume that the drop rate that controlled the queue on the
 		 * last cycle is a good starting point to control it now.
@@ -236,19 +238,20 @@ static struct sk_buff *codel_dequeue(void *ctx,
 		if (delta > 1 &&
 		    codel_time_before(now - vars->drop_next,
 				      16 * params->interval)) {
-			vars->count = delta;
+			WRITE_ONCE(vars->count, delta);
 			/* we dont care if rec_inv_sqrt approximation
 			 * is not very precise :
 			 * Next Newton steps will correct it quadratically.
 			 */
 			codel_Newton_step(vars);
 		} else {
-			vars->count = 1;
+			WRITE_ONCE(vars->count, 1);
 			vars->rec_inv_sqrt = ~0U >> REC_INV_SQRT_SHIFT;
 		}
-		vars->lastcount = vars->count;
-		vars->drop_next = codel_control_law(now, params->interval,
-						    vars->rec_inv_sqrt);
+		WRITE_ONCE(vars->lastcount, vars->count);
+		WRITE_ONCE(vars->drop_next,
+			   codel_control_law(now, params->interval,
+					     vars->rec_inv_sqrt));
 	}
 end:
 	if (skb && codel_time_after(vars->ldelay, params->ce_threshold)) {
@@ -262,7 +265,7 @@ static struct sk_buff *codel_dequeue(void *ctx,
 				   params->ce_threshold_selector));
 		}
 		if (set_ce && INET_ECN_set_ce(skb))
-			stats->ce_mark++;
+			WRITE_ONCE(stats->ce_mark, stats->ce_mark + 1);
 	}
 	return skb;
 }
diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c
index dc2be90666ffbd715550800790a0091acd28701d..317aae0ec7bd6aedb4bae09b18423c981fed16e7 100644
--- a/net/sched/sch_codel.c
+++ b/net/sched/sch_codel.c
@@ -85,7 +85,7 @@ static int codel_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 		return qdisc_enqueue_tail(skb, sch);
 	}
 	q = qdisc_priv(sch);
-	q->drop_overlimit++;
+	WRITE_ONCE(q->drop_overlimit, q->drop_overlimit + 1);
 	return qdisc_drop_reason(skb, sch, to_free, QDISC_DROP_OVERLIMIT);
 }
 
@@ -221,18 +221,18 @@ static int codel_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
 {
 	const struct codel_sched_data *q = qdisc_priv(sch);
 	struct tc_codel_xstats st = {
-		.maxpacket	= q->stats.maxpacket,
-		.count		= q->vars.count,
-		.lastcount	= q->vars.lastcount,
-		.drop_overlimit = q->drop_overlimit,
-		.ldelay		= codel_time_to_us(q->vars.ldelay),
-		.dropping	= q->vars.dropping,
-		.ecn_mark	= q->stats.ecn_mark,
-		.ce_mark	= q->stats.ce_mark,
+		.maxpacket	= READ_ONCE(q->stats.maxpacket),
+		.count		= READ_ONCE(q->vars.count),
+		.lastcount	= READ_ONCE(q->vars.lastcount),
+		.drop_overlimit = READ_ONCE(q->drop_overlimit),
+		.ldelay		= codel_time_to_us(READ_ONCE(q->vars.ldelay)),
+		.dropping	= READ_ONCE(q->vars.dropping),
+		.ecn_mark	= READ_ONCE(q->stats.ecn_mark),
+		.ce_mark	= READ_ONCE(q->stats.ce_mark),
 	};
 
-	if (q->vars.dropping) {
-		codel_tdiff_t delta = q->vars.drop_next - codel_get_time();
+	if (st.dropping) {
+		codel_tdiff_t delta = READ_ONCE(q->vars.drop_next) - codel_get_time();
 
 		if (delta >= 0)
 			st.drop_next = codel_time_to_us(delta);
-- 
2.53.0.1213.gd9a14994de-goog


^ permalink raw reply related

* [PATCH net-next] vsock/virtio: remove unnecessary call to `virtio_transport_get_ops`
From: Luigi Leonardi @ 2026-04-07 14:31 UTC (permalink / raw)
  To: Michael S. Tsirkin, Jason Wang, Xuan Zhuo, Eugenio Pérez,
	Stefan Hajnoczi, Stefano Garzarella, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Arseniy Krasnov
  Cc: kvm, virtualization, netdev, linux-kernel, Luigi Leonardi

`virtio_transport_send_pkt_info` gets all the transport information
from the parameter `t_ops`. There is no need to call
`virtio_transport_get_ops()`.

Remove it.

Fixes: 581512a6dc93 ("vsock/virtio: MSG_ZEROCOPY flag support")
Signed-off-by: Luigi Leonardi <leonardi@redhat.com>
---
I marked this as net-next material, but honsetly I'm not sure if I
should have targeted net. It's not a bug after all, it's just a cleanup.
---
 net/vmw_vsock/virtio_transport_common.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index 8a9fb23c6e85..a152a9e208d0 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -60,8 +60,6 @@ static bool virtio_transport_can_zcopy(const struct virtio_transport *t_ops,
 		return false;
 
 	/* Check that transport can send data in zerocopy mode. */
-	t_ops = virtio_transport_get_ops(info->vsk);
-
 	if (t_ops->can_msgzerocopy) {
 		int pages_to_send = iov_iter_npages(iov_iter, MAX_SKB_FRAGS);
 

---
base-commit: bfe62a454542cfad3379f6ef5680b125f41e20f4
change-id: 20260407-remove_parameter-f61a3e40cf90

Best regards,
-- 
Luigi Leonardi <leonardi@redhat.com>


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox