Commit ffbbc5e5 authored by David S. Miller's avatar David S. Miller

Merge branch 'rmnet-checksums-part-2'

Alex Elder says:

====================
net: qualcomm: rmnet: MAPv4 download checksum cleanup, part 2

This is part 2 of a large series that reworks some code that handles
downloaded packets when MAPv4 checksum offload is enabled.  The
first part, which includes an overview, is here:
  https://lore.kernel.org/netdev/20210611190529.3085813-1-elder@linaro.org/

This second part of the series completes the simplification of this
handling code, removing unnecessary byte swaps and bitwise inversions
of checksum values, and along the way avoids the need for almost all
of the forced type casts.  The checksum field in an RMNet download
trailer is given __sum16_t type to accurately reflect the meaning of
that field.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 73a37860 185a108f
......@@ -35,10 +35,8 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
{
struct iphdr *ip4h = (struct iphdr *)skb->data;
void *txporthdr = skb->data + ip4h->ihl * 4;
__sum16 *csum_field, csum_temp, pseudo_csum;
__sum16 *csum_field, pseudo_csum;
__sum16 ip_payload_csum;
u16 csum_value_final;
__be16 addend;
/* Computing the checksum over just the IPv4 header--including its
* checksum field--should yield 0. If it doesn't, the IP header
......@@ -77,41 +75,32 @@ rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb,
* We verified above that the IP header contributes zero to the
* trailer checksum. Therefore the checksum in the trailer is
* just the checksum computed over the IP payload.
* If the IP payload arrives intact, adding the pseudo header
* checksum to the IP payload checksum will yield 0xffff (negative
* zero). This means the trailer checksum and the pseudo checksum
* are additive inverses of each other. Put another way, the
* message passes the checksum test if the trailer checksum value
* is the negated pseudo header checksum.
*
* Knowing this, we don't even need to examine the transport
* header checksum value; it is already accounted for in the
* checksum value found in the trailer.
*/
ip_payload_csum = (__force __sum16)~csum_trailer->csum_value;
pseudo_csum = ~csum_tcpudp_magic(ip4h->saddr, ip4h->daddr,
ntohs(ip4h->tot_len) - ip4h->ihl * 4,
ip4h->protocol, 0);
addend = (__force __be16)pseudo_csum;
pseudo_csum = csum16_add(ip_payload_csum, addend);
addend = (__force __be16)*csum_field;
csum_temp = ~csum16_sub(pseudo_csum, addend);
csum_value_final = (__force u16)csum_temp;
if (unlikely(csum_value_final == 0)) {
switch (ip4h->protocol) {
case IPPROTO_UDP:
/* RFC 768 - DL4 1's complement rule for UDP csum 0 */
csum_value_final = ~csum_value_final;
break;
case IPPROTO_TCP:
/* DL4 Non-RFC compliant TCP checksum found */
if (*csum_field == (__force __sum16)0xFFFF)
csum_value_final = ~csum_value_final;
break;
}
}
ip_payload_csum = csum_trailer->csum_value;
if (csum_value_final == (__force u16)*csum_field) {
priv->stats.csum_ok++;
return 0;
} else {
pseudo_csum = csum_tcpudp_magic(ip4h->saddr, ip4h->daddr,
ntohs(ip4h->tot_len) - ip4h->ihl * 4,
ip4h->protocol, 0);
/* The cast is required to ensure only the low 16 bits are examined */
if (ip_payload_csum != (__sum16)~pseudo_csum) {
priv->stats.csum_validation_failed++;
return -EINVAL;
}
priv->stats.csum_ok++;
return 0;
}
#if IS_ENABLED(CONFIG_IPV6)
......@@ -122,13 +111,9 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb,
{
struct ipv6hdr *ip6h = (struct ipv6hdr *)skb->data;
void *txporthdr = skb->data + sizeof(*ip6h);
__sum16 *csum_field, pseudo_csum, csum_temp;
__be16 ip6_hdr_csum, addend;
__sum16 *csum_field, pseudo_csum;
__sum16 ip6_payload_csum;
__be16 ip_header_csum;
u16 csum_value_final;
__be16 csum_value;
u32 length;
/* Checksum offload is only supported for UDP and TCP protocols;
* the packet cannot include any IPv6 extension headers
......@@ -145,48 +130,28 @@ rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb,
* of the IP header from the trailer checksum. We then add the
* checksum computed over the pseudo header.
*/
csum_value = ~csum_trailer->csum_value;
ip_header_csum = (__force __be16)ip_fast_csum(ip6h, sizeof(*ip6h) / 4);
ip6_hdr_csum = (__force __be16)~ip_header_csum;
ip6_payload_csum = csum16_sub((__force __sum16)csum_value,
ip6_hdr_csum);
length = (ip6h->nexthdr == IPPROTO_UDP) ?
ntohs(((struct udphdr *)txporthdr)->len) :
ntohs(ip6h->payload_len);
pseudo_csum = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
length, ip6h->nexthdr, 0);
addend = (__force __be16)pseudo_csum;
pseudo_csum = csum16_add(ip6_payload_csum, addend);
addend = (__force __be16)*csum_field;
csum_temp = ~csum16_sub(pseudo_csum, addend);
csum_value_final = (__force u16)csum_temp;
if (unlikely(csum_value_final == 0)) {
switch (ip6h->nexthdr) {
case IPPROTO_UDP:
/* RFC 2460 section 8.1
* DL6 One's complement rule for UDP checksum 0
*/
csum_value_final = ~csum_value_final;
break;
case IPPROTO_TCP:
/* DL6 Non-RFC compliant TCP checksum found */
if (*csum_field == (__force __sum16)0xFFFF)
csum_value_final = ~csum_value_final;
break;
}
}
ip6_payload_csum = csum16_sub(csum_trailer->csum_value, ip_header_csum);
if (csum_value_final == (__force u16)*csum_field) {
priv->stats.csum_ok++;
return 0;
} else {
pseudo_csum = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
ntohs(ip6h->payload_len),
ip6h->nexthdr, 0);
/* It's sufficient to compare the IP payload checksum with the
* negated pseudo checksum to determine whether the packet
* checksum was good. (See further explanation in comments
* in rmnet_map_ipv4_dl_csum_trailer()).
*
* The cast is required to ensure only the low 16 bits are
* examined.
*/
if (ip6_payload_csum != (__sum16)~pseudo_csum) {
priv->stats.csum_validation_failed++;
return -EINVAL;
}
priv->stats.csum_ok++;
return 0;
}
#endif
......
......@@ -25,7 +25,7 @@ struct rmnet_map_dl_csum_trailer {
u8 flags; /* MAP_CSUM_DL_VALID_FLAG */
__be16 csum_start_offset;
__be16 csum_length;
__be16 csum_value;
__sum16 csum_value;
} __aligned(1);
/* rmnet_map_dl_csum_trailer flags field:
......
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