Commit 7be680f7 authored by Davide Caratti's avatar Davide Caratti Committed by Greg Kroah-Hartman

net/mlx4_en: don't set CHECKSUM_COMPLETE on SCTP packets


[ Upstream commit e718fe45 ]

if the NIC fails to validate the checksum on TCP/UDP, and validation of IP
checksum is successful, the driver subtracts the pseudo-header checksum
from the value obtained by the hardware and sets CHECKSUM_COMPLETE. Don't
do that if protocol is IPPROTO_SCTP, otherwise CRC32c validation fails.

V2: don't test MLX4_CQE_STATUS_IPV6 if MLX4_CQE_STATUS_IPV4 is set
Reported-by: default avatarShuang Li <shuali@redhat.com>
Fixes: f8c6455b ("net/mlx4_en: Extend checksum offloading by CHECKSUM COMPLETE")
Signed-off-by: default avatarDavide Caratti <dcaratti@redhat.com>
Acked-by: default avatarSaeed Mahameed <saeedm@mellanox.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c531692a
...@@ -572,16 +572,21 @@ static inline __wsum get_fixed_vlan_csum(__wsum hw_checksum, ...@@ -572,16 +572,21 @@ static inline __wsum get_fixed_vlan_csum(__wsum hw_checksum,
* header, the HW adds it. To address that, we are subtracting the pseudo * header, the HW adds it. To address that, we are subtracting the pseudo
* header checksum from the checksum value provided by the HW. * header checksum from the checksum value provided by the HW.
*/ */
static void get_fixed_ipv4_csum(__wsum hw_checksum, struct sk_buff *skb, static int get_fixed_ipv4_csum(__wsum hw_checksum, struct sk_buff *skb,
struct iphdr *iph) struct iphdr *iph)
{ {
__u16 length_for_csum = 0; __u16 length_for_csum = 0;
__wsum csum_pseudo_header = 0; __wsum csum_pseudo_header = 0;
__u8 ipproto = iph->protocol;
if (unlikely(ipproto == IPPROTO_SCTP))
return -1;
length_for_csum = (be16_to_cpu(iph->tot_len) - (iph->ihl << 2)); length_for_csum = (be16_to_cpu(iph->tot_len) - (iph->ihl << 2));
csum_pseudo_header = csum_tcpudp_nofold(iph->saddr, iph->daddr, csum_pseudo_header = csum_tcpudp_nofold(iph->saddr, iph->daddr,
length_for_csum, iph->protocol, 0); length_for_csum, ipproto, 0);
skb->csum = csum_sub(hw_checksum, csum_pseudo_header); skb->csum = csum_sub(hw_checksum, csum_pseudo_header);
return 0;
} }
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
...@@ -592,17 +597,20 @@ static void get_fixed_ipv4_csum(__wsum hw_checksum, struct sk_buff *skb, ...@@ -592,17 +597,20 @@ static void get_fixed_ipv4_csum(__wsum hw_checksum, struct sk_buff *skb,
static int get_fixed_ipv6_csum(__wsum hw_checksum, struct sk_buff *skb, static int get_fixed_ipv6_csum(__wsum hw_checksum, struct sk_buff *skb,
struct ipv6hdr *ipv6h) struct ipv6hdr *ipv6h)
{ {
__u8 nexthdr = ipv6h->nexthdr;
__wsum csum_pseudo_hdr = 0; __wsum csum_pseudo_hdr = 0;
if (unlikely(ipv6h->nexthdr == IPPROTO_FRAGMENT || if (unlikely(nexthdr == IPPROTO_FRAGMENT ||
ipv6h->nexthdr == IPPROTO_HOPOPTS)) nexthdr == IPPROTO_HOPOPTS ||
nexthdr == IPPROTO_SCTP))
return -1; return -1;
hw_checksum = csum_add(hw_checksum, (__force __wsum)htons(ipv6h->nexthdr)); hw_checksum = csum_add(hw_checksum, (__force __wsum)htons(nexthdr));
csum_pseudo_hdr = csum_partial(&ipv6h->saddr, csum_pseudo_hdr = csum_partial(&ipv6h->saddr,
sizeof(ipv6h->saddr) + sizeof(ipv6h->daddr), 0); sizeof(ipv6h->saddr) + sizeof(ipv6h->daddr), 0);
csum_pseudo_hdr = csum_add(csum_pseudo_hdr, (__force __wsum)ipv6h->payload_len); csum_pseudo_hdr = csum_add(csum_pseudo_hdr, (__force __wsum)ipv6h->payload_len);
csum_pseudo_hdr = csum_add(csum_pseudo_hdr, (__force __wsum)ntohs(ipv6h->nexthdr)); csum_pseudo_hdr = csum_add(csum_pseudo_hdr,
(__force __wsum)htons(nexthdr));
skb->csum = csum_sub(hw_checksum, csum_pseudo_hdr); skb->csum = csum_sub(hw_checksum, csum_pseudo_hdr);
skb->csum = csum_add(skb->csum, csum_partial(ipv6h, sizeof(struct ipv6hdr), 0)); skb->csum = csum_add(skb->csum, csum_partial(ipv6h, sizeof(struct ipv6hdr), 0));
...@@ -625,11 +633,10 @@ static int check_csum(struct mlx4_cqe *cqe, struct sk_buff *skb, void *va, ...@@ -625,11 +633,10 @@ static int check_csum(struct mlx4_cqe *cqe, struct sk_buff *skb, void *va,
} }
if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV4)) if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV4))
get_fixed_ipv4_csum(hw_checksum, skb, hdr); return get_fixed_ipv4_csum(hw_checksum, skb, hdr);
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
else if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV6)) if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV6))
if (unlikely(get_fixed_ipv6_csum(hw_checksum, skb, hdr))) return get_fixed_ipv6_csum(hw_checksum, skb, hdr);
return -1;
#endif #endif
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment