From 6ee188aa7933553da7606c0722cb7cf02895ba11 Mon Sep 17 00:00:00 2001 From: Chinmay Agarwal Date: Fri, 17 Jul 2020 18:09:02 +0530 Subject: [PATCH] net: rmnet_data: Fix incorrect UL checksum offload logic The udp_ip4_ind bit is set only for IPv4 UDP non-fragmented packets so that the hardware can flip the checksum to 0xFFFF if the computed checksum is 0 per RFC768. However, this bit had to be set for IPv6 UDP non fragmented packets as well per hardware requirements. Otherwise, IPv6 UDP packets with computed checksum as 0 were transmitted by hardware and were dropped in the network. In addition to setting this bit for IPv6 UDP, the field is also appropriately renamed to udp_ind as part of this change. Change-Id: Ia00e3f364e9732b182b7aebe843beb443269fd9e Signed-off-by: Chinmay Agarwal --- net/rmnet_data/rmnet_map.h | 6 +++--- net/rmnet_data/rmnet_map_data.c | 15 ++++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/net/rmnet_data/rmnet_map.h b/net/rmnet_data/rmnet_map.h index 718140cf075f..dbe07c6368e3 100644 --- a/net/rmnet_data/rmnet_map.h +++ b/net/rmnet_data/rmnet_map.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, 2020 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -64,11 +64,11 @@ struct rmnet_map_ul_checksum_header_s { unsigned short checksum_start_offset; #if defined(__LITTLE_ENDIAN_BITFIELD) unsigned short checksum_insert_offset:14; - unsigned short udp_ip4_ind:1; + unsigned short udp_ind:1; unsigned short cks_en:1; #elif defined(__BIG_ENDIAN_BITFIELD) unsigned short cks_en:1; - unsigned short udp_ip4_ind:1; + unsigned short udp_ind:1; unsigned short checksum_insert_offset:14; #else #error "Please fix " diff --git a/net/rmnet_data/rmnet_map_data.c b/net/rmnet_data/rmnet_map_data.c index 3ad30cff0fb2..36573b68356c 100644 --- a/net/rmnet_data/rmnet_map_data.c +++ b/net/rmnet_data/rmnet_map_data.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -625,9 +625,9 @@ static void rmnet_map_fill_ipv4_packet_ul_checksum_header ul_header->checksum_insert_offset = skb->csum_offset; ul_header->cks_en = 1; if (ip4h->protocol == IPPROTO_UDP) - ul_header->udp_ip4_ind = 1; + ul_header->udp_ind = 1; else - ul_header->udp_ip4_ind = 0; + ul_header->udp_ind = 0; /* Changing checksum_insert_offset to network order */ hdr++; *hdr = htons(*hdr); @@ -638,13 +638,18 @@ static void rmnet_map_fill_ipv6_packet_ul_checksum_header (void *iphdr, struct rmnet_map_ul_checksum_header_s *ul_header, struct sk_buff *skb) { + struct ipv6hdr *ip6h = (struct ipv6hdr *)iphdr; unsigned short *hdr = (unsigned short *)ul_header; ul_header->checksum_start_offset = htons((unsigned short) (skb_transport_header(skb) - (unsigned char *)iphdr)); ul_header->checksum_insert_offset = skb->csum_offset; ul_header->cks_en = 1; - ul_header->udp_ip4_ind = 0; + + if (ip6h->nexthdr == IPPROTO_UDP) + ul_header->udp_ind = 1; + else + ul_header->udp_ind = 0; /* Changing checksum_insert_offset to network order */ hdr++; *hdr = htons(*hdr); @@ -747,7 +752,7 @@ sw_checksum: ul_header->checksum_start_offset = 0; ul_header->checksum_insert_offset = 0; ul_header->cks_en = 0; - ul_header->udp_ip4_ind = 0; + ul_header->udp_ind = 0; done: return ret; }