Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 35d90144 authored by Davide Caratti's avatar Davide Caratti Committed by Greg Kroah-Hartman
Browse files

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




[ Upstream commit e718fe450e616227b74d27a233cdf37b4df0c82b ]

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 1b582a2c
Loading
Loading
Loading
Loading
+18 −11
Original line number Original line Diff line number Diff line
@@ -724,16 +724,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)
@@ -744,17 +749,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));
@@ -777,11 +785,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;
}
}