netfilter pull request 26-07-31

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEjF9xRqF1emXiQiqU1w0aZmrPKyEFAmpsvPAACgkQ1w0aZmrP
 KyFK9Q/+IhNDdeTfwLhAemxCtgCVyqsuKYprtnNU6eH4s+5RMF9uKiWQIzdjfOfP
 G5f9TvPfJbhqPAr6L3t1MpVu+wNSLlABdCJIEhTR1rMZaEEHZPuXH0TGlml+u8yC
 /izI0s7YP0ijGfGZgNznYXToGEzHRiMQ800LE/U8G5+QQE1pIp+ihpMTWz+4lNzS
 XmHqFmc1UoWCmkvMR5OkwqV3m1Q8PsNkeNwt85G6Uq4D4ILeoSo6oiphMTkuN6ZU
 kcWwBC0ko2yvBWMLtygFroa7ZQbM3CIkqjvBhORNyQ1X0/uK1JXvTVWiwxPgnWOM
 1wfKTAxgaqxOH0tVAi6CD6oOjEoCKnEYrnlIJ5vV0bJDTbCg/bofDpqODO7CvaOs
 LahTQB/3MTemCKHWrXgOo7Wf/LR4fs01pO4dLdezb1rPhLG34ACuDbLEMwzKxD3V
 Wl1py7l0CUVAbg/o02la5x/2Gng0JYENPOJCUAesDkHKXTebgSUMI4CSUK47AcrY
 e6skGbkVO86gzwiPWuUrtF8jcxmQl9jrtqsaUfpXM78uRwzSWIIOBpfmF2oovM6z
 my+yN+uLfIdzde1P2so6tyjdCNAwkGGsavycdFYqXU7w7/dGO45hicGy2ZQvxFzF
 qy2EphdePWNJ8FXSvYnsmLfqG0LZLT7biIhvxLWRNjVf10nTd7w=
 =T2rO
 -----END PGP SIGNATURE-----

Merge tag 'nf-next-26-07-31' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next

Pablo Neira Ayuso says:

====================
Netfilter updates for net-next

The following patchset contains Netfilter updates for net-next:

1) Update conncount to use the original tuple after ct lookup to ensure
   consistent counting, from Fernando F. Mancera.

2) Remove redundant net_device field in info structure that helps
   parse the flowtable path discovery.

3) Move net_device to flowtable check to the flowtable discovery
   path parser. This is preparation work to pass the tunnel dst_entry
   via .fill_forward_path.

4) Update DSA .fill_forward_path to break at the user DSA, since
   the conduit DSA is not used in the datapath. This slighly simplifies
   the flowtable path discovery parser.

5) Do not advance index in the path stack prematurely, otherwise
   it points to uninitialized slots on error. Not an issue currently
   but it could be once tunnel dst_entry is passed via .fill_forward_path.

6) Pass the tunnel dst_entry via dev_fill_forward_path().

7) Update ipip and ip6ip6 tunnels to pass the dst_entry through
   dev_fill_forward_path().

8) Call skb_valid_dst() before accessing skb_dst() to ensure dst_entry
   is not a template.

9) Use UNACK timeout when RST packet does not match the expected
   window while in ESTABLISHED state, the existing approach the CLOSE
   state timeout which is only 10 seconds. Adopt a more conservative
   timeout by default for this case.

* tag 'nf-next-26-07-31' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next:
  netfilter: conntrack: tcp: use UNACK timeout for non-closing RST packets
  netfilter: nf_tables: call skb_valid_dst() before skb_dst()
  netfilter: flowtable: release tunnel route on error when building forward path
  net: pass dst via net_device_path in dev_fill_forward_path()
  net: do not advance stack index from dev_fwd_path()
  net: dsa: stop at the user device in .fill_forward_path
  netfilter: flowtable: consolidate flowtable device check
  netfilter: flowtable: consolidate net_device field in nft_forward_info struct
  netfilter: conncount: normalize tuple and zone on successful ct lookup
====================

Link: https://patch.msgid.link/20260731153402.851224-1-pablo@netfilter.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-08-05 16:26:00 -07:00
commit 419ce0a308
15 changed files with 121 additions and 99 deletions

View File

@ -296,14 +296,18 @@ static int airoha_ppe_get_wdma_info(struct net_device *dev, const u8 *addr,
return err;
path = &stack.path[stack.num_paths - 1];
if (path->type != DEV_PATH_MTK_WDMA)
return -EINVAL;
if (path->type != DEV_PATH_MTK_WDMA) {
err = -EINVAL;
goto err_out;
}
info->idx = path->mtk_wdma.wdma_idx;
info->bss = path->mtk_wdma.bss;
info->wcid = path->mtk_wdma.wcid;
err_out:
dev_fill_forward_path_release(&stack);
return 0;
return err;
}
static int airoha_get_dsa_port(struct net_device **dev)

View File

@ -108,16 +108,20 @@ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_i
return err;
path = &stack.path[stack.num_paths - 1];
if (path->type != DEV_PATH_MTK_WDMA)
return -1;
if (path->type != DEV_PATH_MTK_WDMA) {
err = -EINVAL;
goto err_out;
}
info->wdma_idx = path->mtk_wdma.wdma_idx;
info->queue = path->mtk_wdma.queue;
info->bss = path->mtk_wdma.bss;
info->wcid = path->mtk_wdma.wcid;
info->amsdu = path->mtk_wdma.amsdu;
err_out:
dev_fill_forward_path_release(&stack);
return 0;
return err;
}

View File

@ -892,6 +892,7 @@ struct net_device_path {
u8 h_dest[ETH_ALEN];
} encap;
struct {
struct dst_entry *dst;
union {
struct in_addr src_v4;
struct in6_addr src_v6;
@ -3427,6 +3428,7 @@ int dev_get_iflink(const struct net_device *dev);
int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb);
int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
struct net_device_path_stack *stack);
void dev_fill_forward_path_release(struct net_device_path_stack *stack);
struct net_device *dev_get_by_name(struct net *net, const char *name);
struct net_device *dev_get_by_name_rcu(struct net *net, const char *name);
struct net_device *__dev_get_by_name(struct net *net, const char *name);

View File

@ -742,14 +742,33 @@ EXPORT_SYMBOL_GPL(dev_fill_metadata_dst);
static struct net_device_path *dev_fwd_path(struct net_device_path_stack *stack)
{
int k = stack->num_paths++;
if (k >= NET_DEVICE_PATH_STACK_MAX)
if (stack->num_paths + 1 > NET_DEVICE_PATH_STACK_MAX)
return NULL;
return &stack->path[k];
return &stack->path[stack->num_paths];
}
void dev_fill_forward_path_release(struct net_device_path_stack *stack)
{
struct net_device_path *path;
int k;
if (stack->num_paths == 0)
return;
for (k = stack->num_paths - 1; k >= 0; k--) {
path = &stack->path[k];
switch (path->type) {
case DEV_PATH_TUN:
dst_release(path->tun.dst);
break;
default:
break;
}
}
}
EXPORT_SYMBOL_GPL(dev_fill_forward_path_release);
int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
struct net_device_path_stack *stack)
{
@ -766,15 +785,16 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
last_dev = ctx.dev;
path = dev_fwd_path(stack);
if (!path)
return -1;
goto err_out;
memset(path, 0, sizeof(struct net_device_path));
ret = ctx.dev->netdev_ops->ndo_fill_forward_path(&ctx, path);
if (ret < 0)
return -1;
goto err_out;
stack->num_paths++;
if (WARN_ON_ONCE(last_dev == ctx.dev))
return -1;
goto err_out;
}
if (!ctx.dev)
@ -782,11 +802,17 @@ int dev_fill_forward_path(const struct net_device *dev, const u8 *daddr,
path = dev_fwd_path(stack);
if (!path)
return -1;
goto err_out;
path->type = DEV_PATH_ETHERNET;
path->dev = ctx.dev;
stack->num_paths++;
return ret;
return 0;
err_out:
dev_fill_forward_path_release(stack);
return -1;
}
EXPORT_SYMBOL_GPL(dev_fill_forward_path);

View File

@ -2547,14 +2547,13 @@ static int dsa_user_fill_forward_path(struct net_device_path_ctx *ctx,
struct net_device_path *path)
{
struct dsa_port *dp = dsa_user_to_port(ctx->dev);
struct net_device *conduit = dsa_port_to_conduit(dp);
struct dsa_port *cpu_dp = dp->cpu_dp;
path->dev = ctx->dev;
path->type = DEV_PATH_DSA;
path->dsa.proto = cpu_dp->tag_ops->proto;
path->dsa.port = dp->index;
ctx->dev = conduit;
ctx->dev = NULL;
return 0;
}

View File

@ -376,10 +376,10 @@ static int ipip_fill_forward_path(struct net_device_path_ctx *ctx,
path->tun.src_v4.s_addr = tiph->saddr;
path->tun.dst_v4.s_addr = tiph->daddr;
path->tun.l3_proto = IPPROTO_IPIP;
path->tun.dst = &rt->dst;
path->dev = ctx->dev;
ctx->dev = rt->dst.dev;
ip_rt_put(rt);
return 0;
}

View File

@ -8,6 +8,7 @@
#include <net/tcp.h>
#include <net/route.h>
#include <net/dst.h>
#include <net/dst_metadata.h>
#include <net/netfilter/ipv4/nf_reject.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_bridge.h>
@ -263,6 +264,7 @@ static int nf_reject_fill_skb_dst(struct sk_buff *skb_in)
if (!dst)
return -1;
skb_dst_drop(skb_in);
skb_dst_set(skb_in, dst);
return 0;
}
@ -279,7 +281,7 @@ void nf_send_reset(struct net *net, struct sock *sk, struct sk_buff *oldskb,
if (!oth)
return;
if (!skb_dst(oldskb) && nf_reject_fill_skb_dst(oldskb) < 0)
if (!skb_valid_dst(oldskb) && nf_reject_fill_skb_dst(oldskb) < 0)
return;
if (skb_rtable(oldskb)->rt_flags & (RTCF_BROADCAST | RTCF_MULTICAST))
@ -352,7 +354,7 @@ void nf_send_unreach(struct sk_buff *skb_in, int code, int hook)
if (iph->frag_off & htons(IP_OFFSET))
return;
if (!skb_dst(skb_in) && nf_reject_fill_skb_dst(skb_in) < 0)
if (!skb_valid_dst(skb_in) && nf_reject_fill_skb_dst(skb_in) < 0)
return;
if (skb_csum_unnecessary(skb_in) ||

View File

@ -1870,12 +1870,14 @@ static int ip6_tnl_fill_forward_path(struct net_device_path_ctx *ctx,
path->tun.src_v6 = fl6.saddr;
path->tun.dst_v6 = fl6.daddr;
path->tun.l3_proto = IPPROTO_IPV6;
path->tun.dst = dst;
path->dev = ctx->dev;
ctx->dev = dst->dev;
}
err = dst->error;
dst_release(dst);
if (err)
dst_release(dst);
return err;
}

View File

@ -8,6 +8,7 @@
#include <net/ip6_route.h>
#include <net/ip6_fib.h>
#include <net/ip6_checksum.h>
#include <net/dst_metadata.h>
#include <net/netfilter/ipv6/nf_reject.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_bridge.h>
@ -304,6 +305,7 @@ static int nf_reject6_fill_skb_dst(struct sk_buff *skb_in)
if (!dst)
return -1;
skb_dst_drop(skb_in);
skb_dst_set(skb_in, dst);
return 0;
}
@ -336,10 +338,12 @@ void nf_send_reset6(struct net *net, struct sock *sk, struct sk_buff *oldskb,
fl6.fl6_sport = otcph->dest;
fl6.fl6_dport = otcph->source;
if (!skb_dst(oldskb)) {
if (!skb_valid_dst(oldskb)) {
nf_ip6_route(net, &dst, flowi6_to_flowi(&fl6), false);
if (!dst)
return;
skb_dst_drop(oldskb);
skb_dst_set(oldskb, dst);
}
@ -440,7 +444,7 @@ void nf_send_unreach6(struct net *net, struct sk_buff *skb_in,
if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL)
skb_in->dev = net->loopback_dev;
if (!skb_dst(skb_in) && nf_reject6_fill_skb_dst(skb_in) < 0)
if (!skb_valid_dst(skb_in) && nf_reject6_fill_skb_dst(skb_in) < 0)
return;
icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0);

View File

@ -158,6 +158,8 @@ static bool get_ct_or_tuple_from_skb(struct net *net,
return true;
found_ct = nf_ct_tuplehash_to_ctrack(h);
*tuple = found_ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
*zone = nf_ct_zone(found_ct);
*refcounted = true;
*ct = found_ct;

View File

@ -1281,8 +1281,9 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
if (ct->proto.tcp.retrans >= tn->tcp_max_retrans &&
timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS])
timeout = timeouts[TCP_CONNTRACK_RETRANS];
else if (unlikely(index == TCP_RST_SET))
timeout = timeouts[TCP_CONNTRACK_CLOSE];
else if (unlikely(index == TCP_RST_SET &&
new_state == TCP_CONNTRACK_ESTABLISHED))
timeout = timeouts[TCP_CONNTRACK_UNACK];
else if ((ct->proto.tcp.seen[0].flags | ct->proto.tcp.seen[1].flags) &
IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED &&
timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK])

View File

@ -42,8 +42,7 @@ static bool nft_is_valid_ether_device(const struct net_device *dev)
return true;
}
static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
const struct dst_entry *dst_cache,
static int nft_dev_fill_forward_path(const struct dst_entry *dst_cache,
const struct nf_conn *ct,
enum ip_conntrack_dir dir, u8 *ha,
struct net_device_path_stack *stack)
@ -76,14 +75,14 @@ static int nft_dev_fill_forward_path(const struct nf_flow_route *route,
}
struct nft_forward_info {
const struct net_device *indev;
const struct net_device *outdev;
const struct net_device *dev;
struct id {
__u16 id;
__be16 proto;
} encap[NF_FLOW_TABLE_ENCAP_MAX];
u8 num_encaps;
struct flow_offload_tunnel tun;
struct dst_entry *tun_dst;
u8 num_tuns;
u8 ingress_vlans;
u8 h_source[ETH_ALEN];
@ -92,9 +91,12 @@ struct nft_forward_info {
enum flow_offload_xmit_type xmit_type;
};
static int nft_dev_path_info(const struct net_device_path_stack *stack,
static bool nft_flowtable_find_dev(const struct net_device *dev,
struct nft_flowtable *ft);
static int nft_dev_path_info(struct net_device_path_stack *stack,
struct nft_forward_info *info,
unsigned char *ha, struct nf_flowtable *flowtable)
unsigned char *ha, struct nft_flowtable *ft)
{
const struct net_device_path *path;
int i;
@ -109,29 +111,27 @@ static int nft_dev_path_info(const struct net_device_path_stack *stack,
case DEV_PATH_VLAN:
case DEV_PATH_PPPOE:
case DEV_PATH_TUN:
info->indev = path->dev;
info->dev = path->dev;
if (is_zero_ether_addr(info->h_source))
memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
if (path->type == DEV_PATH_ETHERNET)
if (path->type == DEV_PATH_ETHERNET ||
path->type == DEV_PATH_DSA)
break;
if (path->type == DEV_PATH_DSA) {
i = stack->num_paths;
break;
}
/* DEV_PATH_VLAN, DEV_PATH_PPPOE and DEV_PATH_TUN */
if (path->type == DEV_PATH_TUN) {
if (info->num_tuns)
return -1;
goto err_out;
info->tun.src_v6 = path->tun.src_v6;
info->tun.dst_v6 = path->tun.dst_v6;
info->tun.l3_proto = path->tun.l3_proto;
info->tun_dst = path->tun.dst;
info->num_tuns++;
} else {
if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX)
return -1;
goto err_out;
info->encap[info->num_encaps].id =
path->encap.id;
@ -152,13 +152,13 @@ static int nft_dev_path_info(const struct net_device_path_stack *stack,
switch (path->bridge.vlan_mode) {
case DEV_PATH_BR_VLAN_UNTAG_HW:
if (info->num_encaps == 0)
return -1;
goto err_out;
info->ingress_vlans |= BIT(info->num_encaps - 1);
break;
case DEV_PATH_BR_VLAN_TAG:
if (info->num_encaps >= NF_FLOW_TABLE_ENCAP_MAX)
return -1;
goto err_out;
info->encap[info->num_encaps].id = path->bridge.vlan_id;
info->encap[info->num_encaps].proto = path->bridge.vlan_proto;
@ -166,7 +166,7 @@ static int nft_dev_path_info(const struct net_device_path_stack *stack,
break;
case DEV_PATH_BR_VLAN_UNTAG:
if (info->num_encaps == 0)
return -1;
goto err_out;
info->num_encaps--;
break;
@ -176,16 +176,22 @@ static int nft_dev_path_info(const struct net_device_path_stack *stack,
info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
break;
default:
return -1;
goto err_out;
}
}
info->outdev = info->indev;
if (nf_flowtable_hw_offload(flowtable) &&
nft_is_valid_ether_device(info->indev))
if (nf_flowtable_hw_offload(&ft->data) &&
nft_is_valid_ether_device(info->dev))
info->xmit_type = FLOW_OFFLOAD_XMIT_DIRECT;
if (!nft_flowtable_find_dev(info->dev, ft))
goto err_out;
return 0;
err_out:
dev_fill_forward_path_release(stack);
return -1;
}
static bool nft_flowtable_find_dev(const struct net_device *dev,
@ -205,44 +211,6 @@ static bool nft_flowtable_find_dev(const struct net_device *dev,
return found;
}
static int nft_flow_tunnel_update_route(const struct nft_pktinfo *pkt,
struct flow_offload_tunnel *tun,
struct nf_flow_route *route,
enum ip_conntrack_dir dir)
{
struct dst_entry *cur_dst = route->tuple[dir].dst;
struct dst_entry *tun_dst = NULL;
struct flowi fl = {};
switch (nft_pf(pkt)) {
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));
fl.u.ip4.flowi4_mark = pkt->skb->mark;
fl.u.ip4.flowi4_flags = FLOWI_FLAG_ANYSRC;
break;
case NFPROTO_IPV6:
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));
fl.u.ip6.flowi6_mark = pkt->skb->mark;
fl.u.ip6.flowi6_flags = FLOWI_FLAG_ANYSRC;
break;
}
nf_route(nft_net(pkt), &tun_dst, &fl, false, nft_pf(pkt));
if (!tun_dst)
return -ENOENT;
route->tuple[dir].dst = tun_dst;
dst_release(cur_dst);
return 0;
}
static int nft_dev_forward_path(const struct nft_pktinfo *pkt,
struct nf_flow_route *route,
const struct nf_conn *ct,
@ -255,28 +223,25 @@ static int nft_dev_forward_path(const struct nft_pktinfo *pkt,
unsigned char ha[ETH_ALEN];
int i;
if (nft_dev_fill_forward_path(route, dst, ct, dir, ha, &stack) < 0 ||
nft_dev_path_info(&stack, &info, ha, &ft->data) < 0)
if (nft_dev_fill_forward_path(dst, ct, dir, ha, &stack) < 0 ||
nft_dev_path_info(&stack, &info, ha, ft) < 0)
return -ENOENT;
if (!nft_flowtable_find_dev(info.indev, ft))
return -ENOENT;
route->tuple[!dir].in.ifindex = info.dev->ifindex;
route->tuple[dir].out.ifindex = info.dev->ifindex;
if (info.outdev)
route->tuple[dir].out.ifindex = info.outdev->ifindex;
route->tuple[!dir].in.ifindex = info.indev->ifindex;
for (i = 0; i < info.num_encaps; i++) {
route->tuple[!dir].in.encap[i].id = info.encap[i].id;
route->tuple[!dir].in.encap[i].proto = info.encap[i].proto;
}
if (info.num_tuns &&
!nft_flow_tunnel_update_route(pkt, &info.tun, route, dir)) {
if (info.num_tuns) {
route->tuple[!dir].in.tun.src_v6 = info.tun.dst_v6;
route->tuple[!dir].in.tun.dst_v6 = info.tun.src_v6;
route->tuple[!dir].in.tun.l3_proto = info.tun.l3_proto;
route->tuple[!dir].in.num_tuns = info.num_tuns;
dst_release(route->tuple[dir].dst);
route->tuple[dir].dst = info.tun_dst;
}
route->tuple[!dir].in.num_encaps = info.num_encaps;

View File

@ -20,6 +20,7 @@
#include <net/dst.h>
#include <net/ip.h>
#include <net/sock.h>
#include <net/dst_metadata.h>
#include <net/tcp_states.h> /* for TCP_TIME_WAIT */
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
@ -279,11 +280,12 @@ static bool nft_meta_get_eval_ifname(enum nft_meta_keys key, u32 *dest,
static noinline bool
nft_meta_get_eval_rtclassid(const struct sk_buff *skb, u32 *dest)
{
const struct dst_entry *dst = skb_dst(skb);
const struct dst_entry *dst;
if (!dst)
if (!skb_valid_dst(skb))
return false;
dst = skb_dst(skb);
*dest = dst->tclassid;
return true;
}

View File

@ -8,6 +8,7 @@
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/dst.h>
#include <net/dst_metadata.h>
#include <net/ip6_route.h>
#include <net/route.h>
#include <net/netfilter/nf_tables.h>
@ -59,10 +60,11 @@ void nft_rt_get_eval(const struct nft_expr *expr,
u32 *dest = &regs->data[priv->dreg];
const struct dst_entry *dst;
dst = skb_dst(skb);
if (!dst)
if (!skb_valid_dst(skb))
goto err;
dst = skb_dst(skb);
switch (priv->key) {
#ifdef CONFIG_IP_ROUTE_CLASSID
case NFT_RT_CLASSID:

View File

@ -12,6 +12,7 @@
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables_core.h>
#include <net/netfilter/nf_tables.h>
#include <net/dst_metadata.h>
#include <linux/in.h>
#include <net/xfrm.h>
@ -177,9 +178,15 @@ static void nft_xfrm_get_eval_out(const struct nft_xfrm *priv,
struct nft_regs *regs,
const struct nft_pktinfo *pkt)
{
const struct dst_entry *dst = skb_dst(pkt->skb);
const struct dst_entry *dst;
int i;
if (!skb_valid_dst(pkt->skb)) {
regs->verdict.code = NFT_BREAK;
return;
}
dst = skb_dst(pkt->skb);
for (i = 0; dst && dst->xfrm;
dst = ((const struct xfrm_dst *)dst)->child, i++) {
if (i < priv->spnum)