Netdev List
 help / color / mirror / Atom feed
From: Lorenzo Bianconi <lorenzo@kernel.org>
To: Andrew Lunn <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Felix Fietkau <nbd@nbd.name>,
	Matthias Brugger <matthias.bgg@gmail.com>,
	AngeloGioacchino Del Regno
	<angelogioacchino.delregno@collabora.com>,
	Simon Horman <horms@kernel.org>, David Ahern <dsahern@kernel.org>,
	Ido Schimmel <idosch@nvidia.com>,
	Pablo Neira Ayuso <pablo@netfilter.org>,
	Florian Westphal <fw@strlen.de>, Phil Sutter <phil@nwl.cc>,
	Shuah Khan <shuah@kernel.org>
Cc: linux-arm-kernel@lists.infradead.org,
	linux-mediatek@lists.infradead.org, netdev@vger.kernel.org,
	netfilter-devel@vger.kernel.org, coreteam@netfilter.org,
	linux-kselftest@vger.kernel.org
Subject: Re: [PATCH nf-next v4 3/6] net: netfilter: Add IPv4 over IPv6 tunnel flowtable acceleration
Date: Wed, 8 Jul 2026 12:11:05 +0200	[thread overview]
Message-ID: <ak4iOYYR65kdTDAm@lore-desk> (raw)
In-Reply-To: <20260703-b4-flowtable-sw-accel-ip6ip-v4-3-00398cd12382@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 18997 bytes --]

commenting on sashiko's report:
https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260703-b4-flowtable-sw-accel-ip6ip-v4-0-00398cd12382%40kernel.org

> Introduce sw flowtable acceleration for the TX/RX paths of
> IPv4 over IPv6 tunnels, relying on the netfilter flowtable
> infrastructure.
> The feature can be tested with a forwarding scenario between two
> NICs (eth0 and eth1), where an IPv4 over IPv6 tunnel is used to
> reach a remote site via eth1 as the underlay device:
> 
>     ETH0 -- TUN0 <==> ETH1 -- [IP network] -- TUN1 (2001:db8:2::2)
> 
> [IP configuration]
> 
> 6: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
>     link/ether 00:00:22:33:11:55 brd ff:ff:ff:ff:ff:ff
>     inet 192.168.0.2/24 scope global eth0
>        valid_lft forever preferred_lft forever
> 7: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
>     link/ether 00:11:22:33:11:55 brd ff:ff:ff:ff:ff:ff
>     inet6 2001:db8:2::1/64 scope global nodad
>        valid_lft forever preferred_lft forever
> 8: tun0@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
>     link/tunnel6 2001:db8:2::1 peer 2001:db8:2::2 permaddr ce9c:2940:7dcc::
>     inet 192.168.100.1/24 scope global tun0
>        valid_lft forever preferred_lft forever
> 
> $ ip route show
> default via 192.168.100.2 dev tun0
> 192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.2
> 192.168.100.0/24 dev tun0 proto kernel scope link src 192.168.100.1
> 
> $ ip -6 route show
> 2001:db8:2::/64 dev eth1 proto kernel metric 256 pref medium
> 
> $ nft list ruleset
> table inet filter {
>     flowtable ft {
>         hook ingress priority filter
>         devices = { eth0, eth1 }
>     }
> 
>     chain forward {
>         type filter hook forward priority filter; policy accept;
>         meta l4proto { tcp, udp } flow add @ft
>     }
> }
> 
> When reproducing this scenario using veth interfaces, the following
> results were observed:
> 
> - TCP stream received from IPv4 over IPv6 tunnel:
>   - net-next (baseline):                ~126 Gbps
>   - net-next + IP6IP flowtable support: ~138 Gbps
> 
> - TCP stream transmitted to IPv4 over IPv6 tunnel:
>   - net-next (baseline):                ~127 Gbps
>   - net-next + IP6IP flowtable support: ~140 Gbps
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
>  net/netfilter/nf_flow_table_core.c |  16 +++-
>  net/netfilter/nf_flow_table_ip.c   | 161 ++++++++++++++++++++++++++-----------
>  net/netfilter/nf_flow_table_path.c |  10 ++-
>  3 files changed, 133 insertions(+), 54 deletions(-)
> 
> diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
> index 99c5b9d671a0..18f89e6fb435 100644
> --- a/net/netfilter/nf_flow_table_core.c
> +++ b/net/netfilter/nf_flow_table_core.c
> @@ -76,9 +76,14 @@ struct flow_offload *flow_offload_alloc(struct nf_conn *ct)
>  }
>  EXPORT_SYMBOL_GPL(flow_offload_alloc);
>  
> -static u32 flow_offload_dst_cookie(struct flow_offload_tuple *flow_tuple)
> +static u32 flow_offload_dst_cookie(struct flow_offload_tuple *flow_tuple,
> +				   u8 tun_encap_proto)
>  {
> -	if (flow_tuple->l3proto == NFPROTO_IPV6)
> +	bool dst_v6;
> +
> +	dst_v6 = tun_encap_proto ? tun_encap_proto == NFPROTO_IPV6
> +				 : flow_tuple->l3proto == NFPROTO_IPV6;
> +	if (dst_v6)
>  		return rt6_get_cookie(dst_rt6_info(flow_tuple->dst_cache));
>  
>  	return 0;
> @@ -99,10 +104,12 @@ static int flow_offload_fill_route(struct flow_offload *flow,
>  				   enum flow_offload_tuple_dir dir)
>  {
>  	struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple;
> +	u8 l3proto, encap_proto = route->tuple[!dir].in.tun.encap_proto;
>  	struct dst_entry *dst = nft_route_dst_fetch(route, dir);
>  	int i, j = 0;
>  
> -	switch (flow_tuple->l3proto) {
> +	l3proto = encap_proto ? encap_proto : flow_tuple->l3proto;
> +	switch (l3proto) {
>  	case NFPROTO_IPV4:
>  		flow_tuple->mtu = ip_dst_mtu_maybe_forward(dst, true);
>  		break;
> @@ -138,7 +145,8 @@ static int flow_offload_fill_route(struct flow_offload *flow,
>  	case FLOW_OFFLOAD_XMIT_NEIGH:
>  		flow_tuple->ifidx = route->tuple[dir].out.ifindex;
>  		flow_tuple->dst_cache = dst;
> -		flow_tuple->dst_cookie = flow_offload_dst_cookie(flow_tuple);
> +		flow_tuple->dst_cookie = flow_offload_dst_cookie(flow_tuple,
> +								 encap_proto);
>  		break;
>  	default:
>  		WARN_ON_ONCE(1);
> diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
> index cf2c74e3fd56..4b6de16bd4f3 100644
> --- a/net/netfilter/nf_flow_table_ip.c
> +++ b/net/netfilter/nf_flow_table_ip.c
> @@ -191,27 +191,27 @@ static void nf_flow_tuple_encap(struct nf_flowtable_ctx *ctx,
>  		break;
>  	}
>  
> -	switch (inner_proto) {
> -	case htons(ETH_P_IP):
> -		iph = (struct iphdr *)(skb_network_header(skb) + offset);
> -		if (ctx->tun.proto == IPPROTO_IPIP) {
> +	if (ctx->tun.proto == IPPROTO_IPIP || ctx->tun.proto == IPPROTO_IPV6) {
> +		switch (inner_proto) {
> +		case htons(ETH_P_IP):
> +			iph = (struct iphdr *)(skb_network_header(skb) +
> +					       offset);
>  			tuple->tun.dst_v4.s_addr = iph->daddr;
>  			tuple->tun.src_v4.s_addr = iph->saddr;
> -			tuple->tun.l3_proto = IPPROTO_IPIP;
> +			tuple->tun.l3_proto = ctx->tun.proto;
>  			tuple->tun.encap_proto = AF_INET;
> -		}
> -		break;
> -	case htons(ETH_P_IPV6):
> -		ip6h = (struct ipv6hdr *)(skb_network_header(skb) + offset);
> -		if (ctx->tun.proto == IPPROTO_IPV6) {
> +			break;
> +		case htons(ETH_P_IPV6):
> +			ip6h = (struct ipv6hdr *)(skb_network_header(skb) +
> +						  offset);
>  			tuple->tun.dst_v6 = ip6h->daddr;
>  			tuple->tun.src_v6 = ip6h->saddr;
> -			tuple->tun.l3_proto = IPPROTO_IPV6;
> +			tuple->tun.l3_proto = ctx->tun.proto;
>  			tuple->tun.encap_proto = AF_INET6;
> +			break;
> +		default:
> +			break;
>  		}
> -		break;
> -	default:
> -		break;
>  	}
>  }
>  
> @@ -363,7 +363,7 @@ static bool nf_flow_ip6_tunnel_proto(struct nf_flowtable_ctx *ctx,
>  	if (ipv6_ext_hdr(ip6h->nexthdr))
>  		return false;
>  
> -	if (ip6h->nexthdr == IPPROTO_IPV6) {
> +	if (ip6h->nexthdr == IPPROTO_IPIP || ip6h->nexthdr == IPPROTO_IPV6) {
>  		ctx->tun.proto = ip6h->nexthdr;
>  		ctx->tun.hdr_size = sizeof(*ip6h);
>  		ctx->offset += ctx->tun.hdr_size;
> @@ -384,6 +384,10 @@ static void nf_flow_ip_tunnel_pop(struct nf_flowtable_ctx *ctx,
>  
>  	skb_pull(skb, ctx->tun.hdr_size);
>  	skb_reset_network_header(skb);
> +	if (ctx->tun.proto == IPPROTO_IPIP)
> +		skb->protocol = htons(ETH_P_IP);
> +	else
> +		skb->protocol = htons(ETH_P_IPV6);
>  }
>  
>  static bool nf_flow_skb_encap_protocol(struct nf_flowtable_ctx *ctx,
> @@ -489,8 +493,16 @@ static int nf_flow_offload_forward(struct nf_flowtable_ctx *ctx,
>  	flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
>  
>  	mtu = flow->tuplehash[dir].tuple.mtu + ctx->offset;
> -	if (flow->tuplehash[!dir].tuple.tun_num)
> +	switch (flow->tuplehash[!dir].tuple.tun.encap_proto) {
> +	case AF_INET:
>  		mtu -= sizeof(*iph);
> +		break;
> +	case AF_INET6:
> +		mtu -= sizeof(struct ipv6hdr);
> +		break;
> +	default:
> +		break;
> +	}
>  
>  	if (unlikely(nf_flow_exceeds_mtu(skb, mtu)))
>  		return 0;
> @@ -636,56 +648,57 @@ static int nf_flow_tunnel_ipip_push(struct net *net, struct sk_buff *skb,
>  	return 0;
>  }
>  
> -static int nf_flow_tunnel_v4_push(struct net *net, struct sk_buff *skb,
> -				  struct flow_offload_tuple *tuple,
> -				  __be32 *ip_daddr)
> -{
> -	if (tuple->tun_num)
> -		return nf_flow_tunnel_ipip_push(net, skb, tuple, ip_daddr);
> -
> -	return 0;
> -}
> -
>  static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,
>  				      struct flow_offload_tuple *tuple,
>  				      struct in6_addr **ip6_daddr)

- This helper is still named nf_flow_tunnel_ip6ip6_push() but now also
  encapsulates IPv4 inner packets inside an IPv6 outer header, branching on
  tuple->tun.l3_proto and writing that value into ip6h->nexthdr rather than
  hard-coding IPPROTO_IPV6.
  Would a name like nf_flow_tunnel_ip6_push() (or splitting the inner-family
  preamble into a small helper) better reflect that this now covers both
  6-in-6 and 4-in-6 encapsulation?
  - I will fix it in v5.

>  {
> -	struct ipv6hdr *ip6h = (struct ipv6hdr *)skb_network_header(skb);
> -	struct rtable *rt = dst_rtable(tuple->dst_cache);
> -	__u8 dsfield = ipv6_get_dsfield(ip6h);
> +	struct dst_entry *dst = tuple->dst_cache;
>  	struct flowi6 fl6 = {
>  		.daddr = tuple->tun.src_v6,
>  		.saddr = tuple->tun.dst_v6,
>  		.flowi6_proto = IPPROTO_IPV6,
>  	};
> -	u8 hop_limit = ip6h->hop_limit;
> +	u8 hop_limit, dsfield;
> +	struct ipv6hdr *ip6h;
>  	int err, mtu;
>  	u32 headroom;
>  
> +	if (tuple->tun.l3_proto == IPPROTO_IPIP) {
> +		struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
> +
> +		dsfield = ipv4_get_dsfield(iph);
> +		hop_limit = iph->ttl;
> +	} else {
> +		ip6h = (struct ipv6hdr *)skb_network_header(skb);
> +		dsfield = ipv6_get_dsfield(ip6h);
> +		hop_limit = ip6h->hop_limit;
> +	}
> +
>  	err = iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6);
>  	if (err)
>  		return err;
>  
> -	skb_set_inner_ipproto(skb, IPPROTO_IPV6);
> -	headroom = sizeof(*ip6h) + LL_RESERVED_SPACE(rt->dst.dev) +
> -		   rt->dst.header_len;
> +	skb_set_inner_ipproto(skb, tuple->tun.l3_proto);
> +	headroom = sizeof(*ip6h) + LL_RESERVED_SPACE(dst->dev) +
> +		   dst->header_len;
>  	err = skb_cow_head(skb, headroom);
>  	if (err)
>  		return err;
>  
>  	skb_scrub_packet(skb, true);
> -	mtu = dst_mtu(&rt->dst) - sizeof(*ip6h);
> +	mtu = dst_mtu(dst) - sizeof(*ip6h);
>  	mtu = max(mtu, IPV6_MIN_MTU);
>  	skb_dst_update_pmtu_no_confirm(skb, mtu);
>  
>  	skb_push(skb, sizeof(*ip6h));
>  	skb_reset_network_header(skb);
> +	skb->protocol = htons(ETH_P_IPV6);
>  
>  	ip6h = ipv6_hdr(skb);
>  	ip6_flow_hdr(ip6h, dsfield,
>  		     ip6_make_flowlabel(net, skb, fl6.flowlabel, true, &fl6));
>  	ip6h->hop_limit = hop_limit;
> -	ip6h->nexthdr = IPPROTO_IPV6;
> +	ip6h->nexthdr = tuple->tun.l3_proto;
>  	ip6h->daddr = tuple->tun.src_v6;
>  	ip6h->saddr = tuple->tun.dst_v6;
>  	ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(*ip6h));
> @@ -696,6 +709,20 @@ static int nf_flow_tunnel_ip6ip6_push(struct net *net, struct sk_buff *skb,
>  	return 0;
>  }
>  
> +static int nf_flow_tunnel_push(struct net *net, struct sk_buff *skb,
> +			       struct flow_offload_tuple *tuple,
> +			       __be32 *ip_daddr, struct in6_addr **ip6_daddr)
> +{
> +	switch (tuple->tun.encap_proto) {
> +	case AF_INET:
> +		return nf_flow_tunnel_ipip_push(net, skb, tuple, ip_daddr);
> +	case AF_INET6:
> +		return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr);
> +	default:
> +		return 0;
> +	}
> +}
> +
>  static int nf_flow_tunnel_v6_push(struct net *net, struct sk_buff *skb,
>  				  struct flow_offload_tuple *tuple,
>  				  struct in6_addr **ip6_daddr)

- The IPv4 hook now goes through the family-aware nf_flow_tunnel_push()
  dispatcher while the IPv6 hook still calls the narrow
  nf_flow_tunnel_v6_push() wrapper below.
  Would it be preferable to route both hooks through nf_flow_tunnel_push()
  so that the two entry points share the same abstraction?
  - I will fix it in v5.

> @@ -815,6 +842,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
>  		.in	= state->in,
>  	};
>  	struct nf_flow_xmit xmit = {};
> +	struct in6_addr *ip6_daddr;
>  	struct flow_offload *flow;
>  	struct neighbour *neigh;
>  	struct rtable *rt;
> @@ -844,25 +872,41 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
>  	other_tuple = &flow->tuplehash[!dir].tuple;
>  	ip_daddr = other_tuple->src_v4.s_addr;
>  
> -	if (nf_flow_tunnel_v4_push(state->net, skb, other_tuple, &ip_daddr) < 0)
> +	if (nf_flow_tunnel_push(state->net, skb, other_tuple, &ip_daddr,
> +				&ip6_daddr) < 0)

- ip6_daddr is left uninitialized here, then its address is passed to
  nf_flow_tunnel_push() below. nf_flow_tunnel_push() only writes through
  ip6_daddr in the AF_INET6 case; the AF_INET and default cases leave the
  pointer untouched:
      switch (tuple->tun.encap_proto) {
      case AF_INET:
              return nf_flow_tunnel_ipip_push(net, skb, tuple, ip_daddr);
      case AF_INET6:
              return nf_flow_tunnel_ip6ip6_push(net, skb, tuple, ip6_daddr);
      default:
              return 0;
      }
  The symmetric IPv6 hook explicitly pre-initialises the corresponding
  pointer with ip6_daddr = &other_tuple->src_v6 before the push call. Should
  the IPv4 hook mirror that pattern here?
  - I will fix it in v5.

>  		return NF_DROP;
>  
>  	switch (tuplehash->tuple.xmit_type) {
> -	case FLOW_OFFLOAD_XMIT_NEIGH:
> -		rt = dst_rtable(tuplehash->tuple.dst_cache);
> +	case FLOW_OFFLOAD_XMIT_NEIGH: {
> +		struct dst_entry *dst;
> +
>  		xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.ifidx);
>  		if (!xmit.outdev) {
>  			flow_offload_teardown(flow);
>  			return NF_DROP;
>  		}
> -		neigh = ip_neigh_gw4(rt->dst.dev, rt_nexthop(rt, ip_daddr));
> +		if (other_tuple->tun.encap_proto == AF_INET6 ||
> +		    ctx.tun.proto == IPPROTO_IPV6) {
> +			struct rt6_info *rt6;
> +
> +			rt6 = dst_rt6_info(tuplehash->tuple.dst_cache);
> +			neigh = ip_neigh_gw6(rt6->dst.dev,
> +					     rt6_nexthop(rt6, ip6_daddr));
> +			dst = &rt6->dst;
> +		} else {
> +			rt = dst_rtable(tuplehash->tuple.dst_cache);
> +			neigh = ip_neigh_gw4(rt->dst.dev,
> +					     rt_nexthop(rt, ip_daddr));
> +			dst = &rt->dst;
> +		}
>  		if (IS_ERR(neigh)) {
>  			flow_offload_teardown(flow);
>  			return NF_DROP;
>  		}
>  		xmit.dest = neigh->ha;
> -		skb_dst_set_noref(skb, &rt->dst);
> +		skb_dst_set_noref(skb, dst);
>  		break;
> +	}
>  	case FLOW_OFFLOAD_XMIT_DIRECT:
>  		xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.out.ifidx);
>  		if (!xmit.outdev) {
> @@ -1112,8 +1156,12 @@ nf_flow_offload_ipv6_lookup(struct nf_flowtable_ctx *ctx,
>  	if (!nf_flow_skb_encap_protocol(ctx, skb, htons(ETH_P_IPV6)))
>  		return NULL;
>  
> -	if (nf_flow_tuple_ipv6(ctx, skb, &tuple) < 0)
> +	if (ctx->tun.proto == IPPROTO_IPIP) {
> +		if (nf_flow_tuple_ip(ctx, skb, &tuple) < 0)
> +			return NULL;
> +	} else if (nf_flow_tuple_ipv6(ctx, skb, &tuple) < 0) {
>  		return NULL;
> +	}
>  
>  	return flow_offload_lookup(flow_table, &tuple);
>  }
> @@ -1140,7 +1188,11 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
>  	if (tuplehash == NULL)
>  		return NF_ACCEPT;
>  
> -	ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash, skb);
> +	if (ctx.tun.proto == IPPROTO_IPIP)
> +		ret = nf_flow_offload_forward(&ctx, flow_table, tuplehash, skb);
> +	else
> +		ret = nf_flow_offload_ipv6_forward(&ctx, flow_table, tuplehash,
> +						   skb);
>  	if (ret < 0)
>  		return NF_DROP;
>  	else if (ret == 0)
> @@ -1164,21 +1216,38 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
>  		return NF_DROP;
>  
>  	switch (tuplehash->tuple.xmit_type) {
> -	case FLOW_OFFLOAD_XMIT_NEIGH:
> -		rt = dst_rt6_info(tuplehash->tuple.dst_cache);
> +	case FLOW_OFFLOAD_XMIT_NEIGH: {
> +		struct dst_entry *dst;
> +
>  		xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.ifidx);
>  		if (!xmit.outdev) {
>  			flow_offload_teardown(flow);
>  			return NF_DROP;
>  		}
> -		neigh = ip_neigh_gw6(rt->dst.dev, rt6_nexthop(rt, ip6_daddr));
> +		if (other_tuple->tun.encap_proto == AF_INET ||
> +		    ctx.tun.proto == IPPROTO_IPIP) {
> +			__be32 ip_daddr = other_tuple->src_v4.s_addr;
> +			struct rtable *rt4;
> +
> +			skb->protocol = htons(ETH_P_IP);
> +			rt4 = dst_rtable(tuplehash->tuple.dst_cache);
> +			neigh = ip_neigh_gw4(rt4->dst.dev,
> +					     rt_nexthop(rt4, ip_daddr));
> +			dst = &rt4->dst;
> +		} else {
> +			rt = dst_rt6_info(tuplehash->tuple.dst_cache);
> +			neigh = ip_neigh_gw6(rt->dst.dev,
> +					     rt6_nexthop(rt, ip6_daddr));
> +			dst = &rt->dst;
> +		}
>  		if (IS_ERR(neigh)) {
>  			flow_offload_teardown(flow);
>  			return NF_DROP;
>  		}
>  		xmit.dest = neigh->ha;
> -		skb_dst_set_noref(skb, &rt->dst);
> +		skb_dst_set_noref(skb, dst);
>  		break;
> +	}
>  	case FLOW_OFFLOAD_XMIT_DIRECT:
>  		xmit.outdev = dev_get_by_index_rcu(state->net, tuplehash->tuple.out.ifidx);
>  		if (!xmit.outdev) {
> diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
> index caaf48c5fd2a..5e84b7f18a26 100644
> --- a/net/netfilter/nf_flow_table_path.c
> +++ b/net/netfilter/nf_flow_table_path.c
> @@ -216,12 +216,13 @@ static int nft_flow_tunnel_update_route(const struct nft_pktinfo *pkt,
>  	struct dst_entry *tun_dst = NULL;
>  	struct flowi fl = {};
>  
> -	switch (nft_pf(pkt)) {
> +	switch (tun->encap_proto) {
>  	case NFPROTO_IPV4:
>  		fl.u.ip4.daddr = tun->dst_v4.s_addr;
>  		fl.u.ip4.saddr = tun->src_v4.s_addr;
>  		fl.u.ip4.flowi4_iif = nft_in(pkt)->ifindex;
> -		fl.u.ip4.flowi4_dscp = ip4h_dscp(ip_hdr(pkt->skb));
> +		if (nft_pf(pkt) == NFPROTO_IPV4)
> +			fl.u.ip4.flowi4_dscp = ip4h_dscp(ip_hdr(pkt->skb));
>  		fl.u.ip4.flowi4_mark = pkt->skb->mark;
>  		fl.u.ip4.flowi4_flags = FLOWI_FLAG_ANYSRC;
>  		break;
> @@ -229,13 +230,14 @@ static int nft_flow_tunnel_update_route(const struct nft_pktinfo *pkt,
>  		fl.u.ip6.daddr = tun->dst_v6;
>  		fl.u.ip6.saddr = tun->src_v6;
>  		fl.u.ip6.flowi6_iif = nft_in(pkt)->ifindex;
> -		fl.u.ip6.flowlabel = ip6_flowinfo(ipv6_hdr(pkt->skb));
> +		if (nft_pf(pkt) == NFPROTO_IPV6)
> +			fl.u.ip6.flowlabel = ip6_flowinfo(ipv6_hdr(pkt->skb));
>  		fl.u.ip6.flowi6_mark = pkt->skb->mark;
>  		fl.u.ip6.flowi6_flags = FLOWI_FLAG_ANYSRC;
>  		break;

- This changes route-family selection from nft_pf(pkt) to tun->encap_proto
  and gates the DSCP and flowlabel inputs on nft_pf(pkt) matching the outer
  family, so cross-family flows now pass zero DSCP or flowlabel into the
  flowi used for the FIB lookup.
  Setups that use policy routing keyed on TOS/DSCP or flow label may see
  different route selection than before. The changelog describes the
  IPv4-over-IPv6 feature and its benchmarks but does not mention this
  behavioural change on tunnel route selection.
  Could this behavioural change be called out in the commit message so it
  is discoverable during bisection and stable-tree evaluation?
  - I will fix commit logs in v5.

Regards,
Lorenzo

>  	}
>  
> -	nf_route(nft_net(pkt), &tun_dst, &fl, false, nft_pf(pkt));
> +	nf_route(nft_net(pkt), &tun_dst, &fl, false, tun->encap_proto);
>  	if (!tun_dst)
>  		return -ENOENT;
>  
> 
> -- 
> 2.55.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

  parent reply	other threads:[~2026-07-08 10:11 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-03 14:10 [PATCH nf-next v4 0/6] Add IPv4 over IPv6 and SIT flowtable SW acceleration Lorenzo Bianconi
2026-07-03 14:10 ` [PATCH nf-next v4 1/6] net: netfilter: Add ether_type to net_device_path_ctx Lorenzo Bianconi
2026-07-03 14:10 ` [PATCH nf-next v4 2/6] net: netfilter: Add encap_proto to flow_offload_tunnel Lorenzo Bianconi
2026-07-03 14:10 ` [PATCH nf-next v4 3/6] net: netfilter: Add IPv4 over IPv6 tunnel flowtable acceleration Lorenzo Bianconi
2026-07-07 16:25   ` Lorenzo Bianconi
2026-07-08 10:11   ` Lorenzo Bianconi [this message]
2026-07-03 14:10 ` [PATCH nf-next v4 4/6] selftests: netfilter: nft_flowtable.sh: Add IPv4 over IPv6 flowtable selftest Lorenzo Bianconi
2026-07-03 14:10 ` [PATCH nf-next v4 5/6] net: netfilter: Add SIT tunnel flowtable acceleration Lorenzo Bianconi
2026-07-08  8:17   ` Lorenzo Bianconi
2026-07-08 10:33   ` Lorenzo Bianconi
2026-07-03 14:10 ` [PATCH nf-next v4 6/6] selftests: netfilter: nft_flowtable.sh: Add SIT flowtable selftest Lorenzo Bianconi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ak4iOYYR65kdTDAm@lore-desk \
    --to=lorenzo@kernel.org \
    --cc=andrew+netdev@lunn.ch \
    --cc=angelogioacchino.delregno@collabora.com \
    --cc=coreteam@netfilter.org \
    --cc=davem@davemloft.net \
    --cc=dsahern@kernel.org \
    --cc=edumazet@google.com \
    --cc=fw@strlen.de \
    --cc=horms@kernel.org \
    --cc=idosch@nvidia.com \
    --cc=kuba@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=matthias.bgg@gmail.com \
    --cc=nbd@nbd.name \
    --cc=netdev@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=pablo@netfilter.org \
    --cc=phil@nwl.cc \
    --cc=shuah@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox