| From c0202f324c0c6020ae4ceee4cbc41e6cba3ac6d4 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 3 Jan 2022 13:19:29 +0200 |
| Subject: net/xfrm: IPsec tunnel mode fix inner_ipproto setting in sec_path |
| |
| From: Raed Salem <raeds@nvidia.com> |
| |
| [ Upstream commit 45a98ef4922def8c679ca7c454403d1957fe70e7 ] |
| |
| The inner_ipproto saves the inner IP protocol of the plain |
| text packet. This allows vendor's IPsec feature making offload |
| decision at skb's features_check and configuring hardware at |
| ndo_start_xmit, current code implenetation did not handle the |
| case where IPsec is used in tunnel mode. |
| |
| Fix by handling the case when IPsec is used in tunnel mode by |
| reading the protocol of the plain text packet IP protocol. |
| |
| Fixes: fa4535238fb5 ("net/xfrm: Add inner_ipproto into sec_path") |
| Signed-off-by: Raed Salem <raeds@nvidia.com> |
| Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/xfrm/xfrm_output.c | 30 +++++++++++++++++++++++++----- |
| 1 file changed, 25 insertions(+), 5 deletions(-) |
| |
| diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c |
| index 229544bc70c21..4dc4a7bbe51cf 100644 |
| --- a/net/xfrm/xfrm_output.c |
| +++ b/net/xfrm/xfrm_output.c |
| @@ -647,10 +647,12 @@ static int xfrm_output_gso(struct net *net, struct sock *sk, struct sk_buff *skb |
| * This requires hardware to know the inner packet type to calculate |
| * the inner header checksum. Save inner ip protocol here to avoid |
| * traversing the packet in the vendor's xmit code. |
| - * If the encap type is IPIP, just save skb->inner_ipproto. Otherwise, |
| - * get the ip protocol from the IP header. |
| + * For IPsec tunnel mode save the ip protocol from the IP header of the |
| + * plain text packet. Otherwise If the encap type is IPIP, just save |
| + * skb->inner_ipproto in any other case get the ip protocol from the IP |
| + * header. |
| */ |
| -static void xfrm_get_inner_ipproto(struct sk_buff *skb) |
| +static void xfrm_get_inner_ipproto(struct sk_buff *skb, struct xfrm_state *x) |
| { |
| struct xfrm_offload *xo = xfrm_offload(skb); |
| const struct ethhdr *eth; |
| @@ -658,6 +660,25 @@ static void xfrm_get_inner_ipproto(struct sk_buff *skb) |
| if (!xo) |
| return; |
| |
| + if (x->outer_mode.encap == XFRM_MODE_TUNNEL) { |
| + switch (x->outer_mode.family) { |
| + case AF_INET: |
| + xo->inner_ipproto = ip_hdr(skb)->protocol; |
| + break; |
| + case AF_INET6: |
| + xo->inner_ipproto = ipv6_hdr(skb)->nexthdr; |
| + break; |
| + default: |
| + break; |
| + } |
| + |
| + return; |
| + } |
| + |
| + /* non-Tunnel Mode */ |
| + if (!skb->encapsulation) |
| + return; |
| + |
| if (skb->inner_protocol_type == ENCAP_TYPE_IPPROTO) { |
| xo->inner_ipproto = skb->inner_ipproto; |
| return; |
| @@ -712,8 +733,7 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb) |
| sp->xvec[sp->len++] = x; |
| xfrm_state_hold(x); |
| |
| - if (skb->encapsulation) |
| - xfrm_get_inner_ipproto(skb); |
| + xfrm_get_inner_ipproto(skb, x); |
| skb->encapsulation = 1; |
| |
| if (skb_is_gso(skb)) { |
| -- |
| 2.34.1 |
| |