summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSubash Abhinov Kasiviswanathan <subashab@codeaurora.org>2015-09-02 14:44:08 -0600
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-22 11:05:44 -0700
commit3ed49aeb34e04891cc83cd8d6eee48f03fc023a2 (patch)
tree601bf5d2633f19c3f01352d79a05afca8791ccde
parentece90739ece2746a30a5eb4998c5949cd1c4f68d (diff)
net: rmnet_data: Add support for MAPv4 data format
Add the MAPv4 ingress and data format handlers. MAPv4 requires the checksum for uplink TCP and UDP packets to be 1's complemented before passing the packet onto the physical netdevice. This workaround is needed due to failures seen in hardware while processing translated packets. Change-Id: Ib79382fa7e8b2bd0c1adbe68b8de75f1602df10b Signed-off-by: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
-rw-r--r--include/uapi/linux/rmnet_data.h4
-rw-r--r--net/rmnet_data/rmnet_data_handlers.c12
-rw-r--r--net/rmnet_data/rmnet_map.h4
-rw-r--r--net/rmnet_data/rmnet_map_data.c44
4 files changed, 55 insertions, 9 deletions
diff --git a/include/uapi/linux/rmnet_data.h b/include/uapi/linux/rmnet_data.h
index aa47160f8fdf..8cfe0270ef4f 100644
--- a/include/uapi/linux/rmnet_data.h
+++ b/include/uapi/linux/rmnet_data.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015, 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
@@ -24,6 +24,7 @@
#define RMNET_EGRESS_FORMAT_AGGREGATION (1<<2)
#define RMNET_EGRESS_FORMAT_MUXING (1<<3)
#define RMNET_EGRESS_FORMAT_MAP_CKSUMV3 (1<<4)
+#define RMNET_EGRESS_FORMAT_MAP_CKSUMV4 (1<<5)
#define RMNET_INGRESS_FIX_ETHERNET (1<<0)
#define RMNET_INGRESS_FORMAT_MAP (1<<1)
@@ -31,6 +32,7 @@
#define RMNET_INGRESS_FORMAT_DEMUXING (1<<3)
#define RMNET_INGRESS_FORMAT_MAP_COMMANDS (1<<4)
#define RMNET_INGRESS_FORMAT_MAP_CKSUMV3 (1<<5)
+#define RMNET_INGRESS_FORMAT_MAP_CKSUMV4 (1<<6)
/* ***************** Netlink API ******************************************** */
#define RMNET_NETLINK_PROTO 31
diff --git a/net/rmnet_data/rmnet_data_handlers.c b/net/rmnet_data/rmnet_data_handlers.c
index c80090bee963..0a34465a4133 100644
--- a/net/rmnet_data/rmnet_data_handlers.c
+++ b/net/rmnet_data/rmnet_data_handlers.c
@@ -364,7 +364,8 @@ static rx_handler_result_t _rmnet_map_ingress_handler(struct sk_buff *skb,
if (config->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING)
skb->dev = ep->egress_dev;
- if (config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV3) {
+ if ((config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV3) ||
+ (config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4)) {
ckresult = rmnet_map_checksum_downlink_packet(skb);
trace_rmnet_map_checksum_downlink_packet(skb, ckresult);
rmnet_stats_dl_checksum(ckresult);
@@ -453,7 +454,8 @@ static int rmnet_map_egress_handler(struct sk_buff *skb,
additional_header_length = 0;
required_headroom = sizeof(struct rmnet_map_header_s);
- if (config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV3) {
+ if ((config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV3) ||
+ (config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV4)) {
required_headroom +=
sizeof(struct rmnet_map_ul_checksum_header_s);
additional_header_length +=
@@ -470,8 +472,10 @@ static int rmnet_map_egress_handler(struct sk_buff *skb,
}
}
- if (config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV3) {
- ckresult = rmnet_map_checksum_uplink_packet(skb, orig_dev);
+ if ((config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV3) ||
+ (config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV4)) {
+ ckresult = rmnet_map_checksum_uplink_packet
+ (skb, orig_dev, config->egress_data_format);
trace_rmnet_map_checksum_uplink_packet(orig_dev, ckresult);
rmnet_stats_ul_checksum(ckresult);
}
diff --git a/net/rmnet_data/rmnet_map.h b/net/rmnet_data/rmnet_map.h
index e4bc3aaf5c6a..99e62e912805 100644
--- a/net/rmnet_data/rmnet_map.h
+++ b/net/rmnet_data/rmnet_map.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015, 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
@@ -141,6 +141,6 @@ void rmnet_map_aggregate(struct sk_buff *skb,
int rmnet_map_checksum_downlink_packet(struct sk_buff *skb);
int rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
- struct net_device *orig_dev);
+ struct net_device *orig_dev, uint32_t egress_data_format);
#endif /* _RMNET_MAP_H_ */
diff --git a/net/rmnet_data/rmnet_map_data.c b/net/rmnet_data/rmnet_map_data.c
index cb8057f5a3ec..de3a6e3bd073 100644
--- a/net/rmnet_data/rmnet_map_data.c
+++ b/net/rmnet_data/rmnet_map_data.c
@@ -132,7 +132,8 @@ struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb,
maph = (struct rmnet_map_header_s *) skb->data;
packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header_s);
- if (config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV3)
+ if ((config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV3) ||
+ (config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4))
packet_len += sizeof(struct rmnet_map_dl_checksum_trailer_s);
if ((((int)skb->len) - ((int)packet_len)) < 0) {
@@ -645,6 +646,37 @@ static void rmnet_map_fill_ipv6_packet_ul_checksum_header(void *iphdr,
skb->ip_summed = CHECKSUM_NONE;
}
+static void rmnet_map_complement_ipv4_txporthdr_csum_field(void *iphdr)
+{
+ struct iphdr *ip4h = (struct iphdr *)iphdr;
+ void *txporthdr;
+ uint16_t *csum;
+
+ txporthdr = iphdr + ip4h->ihl*4;
+
+ if ((ip4h->protocol == IPPROTO_TCP) ||
+ (ip4h->protocol == IPPROTO_UDP)) {
+ csum = (uint16_t *)rmnet_map_get_checksum_field(ip4h->protocol,
+ txporthdr);
+ *csum = ~(*csum);
+ }
+}
+
+static void rmnet_map_complement_ipv6_txporthdr_csum_field(void *ip6hdr)
+{
+ struct ipv6hdr *ip6h = (struct ipv6hdr *)ip6hdr;
+ void *txporthdr;
+ uint16_t *csum;
+
+ txporthdr = ip6hdr + sizeof(struct ipv6hdr);
+
+ if ((ip6h->nexthdr == IPPROTO_TCP) || (ip6h->nexthdr == IPPROTO_UDP)) {
+ csum = (uint16_t *)rmnet_map_get_checksum_field(ip6h->nexthdr,
+ txporthdr);
+ *csum = ~(*csum);
+ }
+}
+
/**
* rmnet_map_checksum_uplink_packet() - Generates UL checksum
* meta info header
@@ -659,7 +691,7 @@ static void rmnet_map_fill_ipv6_packet_ul_checksum_header(void *iphdr,
* - RMNET_MAP_CHECKSUM_SW: Unsupported packet for UL checksum offload.
*/
int rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
- struct net_device *orig_dev)
+ struct net_device *orig_dev, uint32_t egress_data_format)
{
unsigned char ip_version;
struct rmnet_map_ul_checksum_header_s *ul_header;
@@ -682,10 +714,18 @@ int rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
if (ip_version == 0x04) {
rmnet_map_fill_ipv4_packet_ul_checksum_header(iphdr,
ul_header, skb);
+ if (egress_data_format &
+ RMNET_EGRESS_FORMAT_MAP_CKSUMV4)
+ rmnet_map_complement_ipv4_txporthdr_csum_field(
+ iphdr);
return RMNET_MAP_CHECKSUM_OK;
} else if (ip_version == 0x06) {
rmnet_map_fill_ipv6_packet_ul_checksum_header(iphdr,
ul_header, skb);
+ if (egress_data_format &
+ RMNET_EGRESS_FORMAT_MAP_CKSUMV4)
+ rmnet_map_complement_ipv6_txporthdr_csum_field(
+ iphdr);
return RMNET_MAP_CHECKSUM_OK;
} else {
ret = RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION;