Skip to content

Commit

Permalink
xfrm: fix tunnel mode TX datapath in packet offload mode
Browse files Browse the repository at this point in the history
Packets that match the output xfrm policy are delivered to the netstack.
In IPsec packet mode for tunnel mode, the HW is responsible for building
the hard header and outer IP header. In such a situation, the inner
header may refer to a network that is not directly reachable by the host,
resulting in a failed neighbor resolution. The packet is then dropped.
xfrm policy defines the netdevice to use for xmit so we can send packets
directly to it.

Makes direct xmit exclusive to tunnel mode, since some rules may apply
in transport mode.

Fixes: f8a70af ("xfrm: add TX datapath support for IPsec packet offload mode")
Signed-off-by: Alexandre Cassen <acassen@corp.free.fr>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
  • Loading branch information
Alexandre Cassen authored and Steffen Klassert committed Feb 21, 2025
1 parent a130069 commit 5eddd76
Showing 1 changed file with 41 additions and 0 deletions.
41 changes: 41 additions & 0 deletions net/xfrm/xfrm_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,40 @@ int xfrm_output_resume(struct sock *sk, struct sk_buff *skb, int err)
}
EXPORT_SYMBOL_GPL(xfrm_output_resume);

static int xfrm_dev_direct_output(struct sock *sk, struct xfrm_state *x,
struct sk_buff *skb)
{
struct dst_entry *dst = skb_dst(skb);
struct net *net = xs_net(x);
int err;

dst = skb_dst_pop(skb);
if (!dst) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
kfree_skb(skb);
return -EHOSTUNREACH;
}
skb_dst_set(skb, dst);
nf_reset_ct(skb);

err = skb_dst(skb)->ops->local_out(net, sk, skb);
if (unlikely(err != 1)) {
kfree_skb(skb);
return err;
}

/* In transport mode, network destination is
* directly reachable, while in tunnel mode,
* inner packet network may not be. In packet
* offload type, HW is responsible for hard
* header packet mangling so directly xmit skb
* to netdevice.
*/
skb->dev = x->xso.dev;
__skb_push(skb, skb->dev->hard_header_len);
return dev_queue_xmit(skb);
}

static int xfrm_output2(struct net *net, struct sock *sk, struct sk_buff *skb)
{
return xfrm_output_resume(sk, skb, 1);
Expand Down Expand Up @@ -735,6 +769,13 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb)
return -EHOSTUNREACH;
}

/* Exclusive direct xmit for tunnel mode, as
* some filtering or matching rules may apply
* in transport mode.
*/
if (x->props.mode == XFRM_MODE_TUNNEL)
return xfrm_dev_direct_output(sk, x, skb);

return xfrm_output_resume(sk, skb, 0);
}

Expand Down

0 comments on commit 5eddd76

Please sign in to comment.