Message ID | 20210603160045.11805-3-huyn@nvidia.com |
---|---|
State | New |
Headers | show |
Series | Fix IPsec crypto offloads with vxlan tunnel | expand |
On Thu, Jun 03, 2021 at 07:00:44PM +0300, Huy Nguyen wrote: > > +/* For partial checksum offload, the outer header checksum is calculated > + * by software and the inner header checksum is calculated by hardware. > + * 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. > + */ > +static void xfrm_get_inner_ipproto(struct sk_buff *skb) > +{ > + struct xfrm_offload *xo = xfrm_offload(skb); > + const struct ethhdr *eth; > + > + if (!skb->inner_protocol) > + return; inner_protocol is only valid if skb->encapsulation is set, maybe you should test for that before calling this function. In particular, you should do that before we set it explicitly in xfrm_output. > + > + xo = xfrm_offload(skb); > + if (!xo) > + return; > + > + if (skb->inner_protocol_type == ENCAP_TYPE_IPPROTO) { > + xo->inner_ipproto = skb->inner_ipproto; > + return; > + } > + > + if (skb->inner_protocol_type != ENCAP_TYPE_ETHER) > + return; > + > + eth = (struct ethhdr *)skb_inner_mac_header(skb); > + > + switch (ntohs(eth->h_proto)) { > + case ETH_P_IPV6: > + xo->inner_ipproto = inner_ipv6_hdr(skb)->nexthdr; > + break; > + case ETH_P_IP: > + xo->inner_ipproto = inner_ip_hdr(skb)->protocol; > + break; > + } > +} > + > int xfrm_output(struct sock *sk, struct sk_buff *skb) > { > struct net *net = dev_net(skb_dst(skb)->dev); > @@ -594,12 +634,14 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb) > kfree_skb(skb); > return -ENOMEM; > } > - skb->encapsulation = 1; > > + skb->encapsulation = 1; > sp->olen++; > sp->xvec[sp->len++] = x; > xfrm_state_hold(x); > > + xfrm_get_inner_ipproto(skb); > + > if (skb_is_gso(skb)) { > if (skb->inner_protocol) > return xfrm_output_gso(net, sk, skb); > -- > 2.24.1
diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 6e11db6fa0ab..c51da30d2542 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1025,6 +1025,7 @@ struct xfrm_offload { #define CRYPTO_INVALID_PROTOCOL 128 __u8 proto; + __u8 inner_ipproto; }; struct sec_path { diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index e4cb0ff4dcf4..cd70d2ea5d8b 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -565,6 +565,46 @@ static int xfrm_output_gso(struct net *net, struct sock *sk, struct sk_buff *skb return 0; } +/* For partial checksum offload, the outer header checksum is calculated + * by software and the inner header checksum is calculated by hardware. + * 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. + */ +static void xfrm_get_inner_ipproto(struct sk_buff *skb) +{ + struct xfrm_offload *xo = xfrm_offload(skb); + const struct ethhdr *eth; + + if (!skb->inner_protocol) + return; + + xo = xfrm_offload(skb); + if (!xo) + return; + + if (skb->inner_protocol_type == ENCAP_TYPE_IPPROTO) { + xo->inner_ipproto = skb->inner_ipproto; + return; + } + + if (skb->inner_protocol_type != ENCAP_TYPE_ETHER) + return; + + eth = (struct ethhdr *)skb_inner_mac_header(skb); + + switch (ntohs(eth->h_proto)) { + case ETH_P_IPV6: + xo->inner_ipproto = inner_ipv6_hdr(skb)->nexthdr; + break; + case ETH_P_IP: + xo->inner_ipproto = inner_ip_hdr(skb)->protocol; + break; + } +} + int xfrm_output(struct sock *sk, struct sk_buff *skb) { struct net *net = dev_net(skb_dst(skb)->dev); @@ -594,12 +634,14 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb) kfree_skb(skb); return -ENOMEM; } - skb->encapsulation = 1; + skb->encapsulation = 1; sp->olen++; sp->xvec[sp->len++] = x; xfrm_state_hold(x); + xfrm_get_inner_ipproto(skb); + if (skb_is_gso(skb)) { if (skb->inner_protocol) return xfrm_output_gso(net, sk, skb);