diff options
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 10 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c | 36 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h | 23 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c | 17 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c | 309 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | 4 |
8 files changed, 378 insertions, 25 deletions
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c index cedacddf50fb..7e7704daf5f1 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | |||
@@ -143,7 +143,7 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, | |||
143 | struct nlattr *tb[], struct nlattr *data[], | 143 | struct nlattr *tb[], struct nlattr *data[], |
144 | struct netlink_ext_ack *extack) | 144 | struct netlink_ext_ack *extack) |
145 | { | 145 | { |
146 | int ingress_format = RMNET_INGRESS_FORMAT_DEAGGREGATION; | 146 | u32 data_format = RMNET_INGRESS_FORMAT_DEAGGREGATION; |
147 | struct net_device *real_dev; | 147 | struct net_device *real_dev; |
148 | int mode = RMNET_EPMODE_VND; | 148 | int mode = RMNET_EPMODE_VND; |
149 | struct rmnet_endpoint *ep; | 149 | struct rmnet_endpoint *ep; |
@@ -185,11 +185,11 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, | |||
185 | struct ifla_vlan_flags *flags; | 185 | struct ifla_vlan_flags *flags; |
186 | 186 | ||
187 | flags = nla_data(data[IFLA_VLAN_FLAGS]); | 187 | flags = nla_data(data[IFLA_VLAN_FLAGS]); |
188 | ingress_format = flags->flags & flags->mask; | 188 | data_format = flags->flags & flags->mask; |
189 | } | 189 | } |
190 | 190 | ||
191 | netdev_dbg(dev, "data format [ingress 0x%08X]\n", ingress_format); | 191 | netdev_dbg(dev, "data format [0x%08X]\n", data_format); |
192 | port->ingress_data_format = ingress_format; | 192 | port->data_format = data_format; |
193 | 193 | ||
194 | return 0; | 194 | return 0; |
195 | 195 | ||
@@ -353,7 +353,7 @@ static int rmnet_changelink(struct net_device *dev, struct nlattr *tb[], | |||
353 | struct ifla_vlan_flags *flags; | 353 | struct ifla_vlan_flags *flags; |
354 | 354 | ||
355 | flags = nla_data(data[IFLA_VLAN_FLAGS]); | 355 | flags = nla_data(data[IFLA_VLAN_FLAGS]); |
356 | port->ingress_data_format = flags->flags & flags->mask; | 356 | port->data_format = flags->flags & flags->mask; |
357 | } | 357 | } |
358 | 358 | ||
359 | return 0; | 359 | return 0; |
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h index 2ea9fe326571..00e4634100d3 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h | |||
@@ -32,7 +32,7 @@ struct rmnet_endpoint { | |||
32 | */ | 32 | */ |
33 | struct rmnet_port { | 33 | struct rmnet_port { |
34 | struct net_device *dev; | 34 | struct net_device *dev; |
35 | u32 ingress_data_format; | 35 | u32 data_format; |
36 | u8 nr_rmnet_devs; | 36 | u8 nr_rmnet_devs; |
37 | u8 rmnet_mode; | 37 | u8 rmnet_mode; |
38 | struct hlist_head muxed_ep[RMNET_MAX_LOGICAL_EP]; | 38 | struct hlist_head muxed_ep[RMNET_MAX_LOGICAL_EP]; |
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c index 05539321ba3a..601edec28c5f 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/netdevice.h> | 16 | #include <linux/netdevice.h> |
17 | #include <linux/netdev_features.h> | 17 | #include <linux/netdev_features.h> |
18 | #include <linux/if_arp.h> | 18 | #include <linux/if_arp.h> |
19 | #include <net/sock.h> | ||
19 | #include "rmnet_private.h" | 20 | #include "rmnet_private.h" |
20 | #include "rmnet_config.h" | 21 | #include "rmnet_config.h" |
21 | #include "rmnet_vnd.h" | 22 | #include "rmnet_vnd.h" |
@@ -65,19 +66,19 @@ __rmnet_map_ingress_handler(struct sk_buff *skb, | |||
65 | struct rmnet_port *port) | 66 | struct rmnet_port *port) |
66 | { | 67 | { |
67 | struct rmnet_endpoint *ep; | 68 | struct rmnet_endpoint *ep; |
69 | u16 len, pad; | ||
68 | u8 mux_id; | 70 | u8 mux_id; |
69 | u16 len; | ||
70 | 71 | ||
71 | if (RMNET_MAP_GET_CD_BIT(skb)) { | 72 | if (RMNET_MAP_GET_CD_BIT(skb)) { |
72 | if (port->ingress_data_format | 73 | if (port->data_format & RMNET_INGRESS_FORMAT_MAP_COMMANDS) |
73 | & RMNET_INGRESS_FORMAT_MAP_COMMANDS) | ||
74 | return rmnet_map_command(skb, port); | 74 | return rmnet_map_command(skb, port); |
75 | 75 | ||
76 | goto free_skb; | 76 | goto free_skb; |
77 | } | 77 | } |
78 | 78 | ||
79 | mux_id = RMNET_MAP_GET_MUX_ID(skb); | 79 | mux_id = RMNET_MAP_GET_MUX_ID(skb); |
80 | len = RMNET_MAP_GET_LENGTH(skb) - RMNET_MAP_GET_PAD(skb); | 80 | pad = RMNET_MAP_GET_PAD(skb); |
81 | len = RMNET_MAP_GET_LENGTH(skb) - pad; | ||
81 | 82 | ||
82 | if (mux_id >= RMNET_MAX_LOGICAL_EP) | 83 | if (mux_id >= RMNET_MAX_LOGICAL_EP) |
83 | goto free_skb; | 84 | goto free_skb; |
@@ -90,8 +91,14 @@ __rmnet_map_ingress_handler(struct sk_buff *skb, | |||
90 | 91 | ||
91 | /* Subtract MAP header */ | 92 | /* Subtract MAP header */ |
92 | skb_pull(skb, sizeof(struct rmnet_map_header)); | 93 | skb_pull(skb, sizeof(struct rmnet_map_header)); |
93 | skb_trim(skb, len); | ||
94 | rmnet_set_skb_proto(skb); | 94 | rmnet_set_skb_proto(skb); |
95 | |||
96 | if (port->data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4) { | ||
97 | if (!rmnet_map_checksum_downlink_packet(skb, len + pad)) | ||
98 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
99 | } | ||
100 | |||
101 | skb_trim(skb, len); | ||
95 | rmnet_deliver_skb(skb); | 102 | rmnet_deliver_skb(skb); |
96 | return; | 103 | return; |
97 | 104 | ||
@@ -114,8 +121,8 @@ rmnet_map_ingress_handler(struct sk_buff *skb, | |||
114 | skb_push(skb, ETH_HLEN); | 121 | skb_push(skb, ETH_HLEN); |
115 | } | 122 | } |
116 | 123 | ||
117 | if (port->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) { | 124 | if (port->data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) { |
118 | while ((skbn = rmnet_map_deaggregate(skb)) != NULL) | 125 | while ((skbn = rmnet_map_deaggregate(skb, port)) != NULL) |
119 | __rmnet_map_ingress_handler(skbn, port); | 126 | __rmnet_map_ingress_handler(skbn, port); |
120 | 127 | ||
121 | consume_skb(skb); | 128 | consume_skb(skb); |
@@ -134,19 +141,24 @@ static int rmnet_map_egress_handler(struct sk_buff *skb, | |||
134 | additional_header_len = 0; | 141 | additional_header_len = 0; |
135 | required_headroom = sizeof(struct rmnet_map_header); | 142 | required_headroom = sizeof(struct rmnet_map_header); |
136 | 143 | ||
144 | if (port->data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV4) { | ||
145 | additional_header_len = sizeof(struct rmnet_map_ul_csum_header); | ||
146 | required_headroom += additional_header_len; | ||
147 | } | ||
148 | |||
137 | if (skb_headroom(skb) < required_headroom) { | 149 | if (skb_headroom(skb) < required_headroom) { |
138 | if (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL)) | 150 | if (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL)) |
139 | goto fail; | 151 | goto fail; |
140 | } | 152 | } |
141 | 153 | ||
154 | if (port->data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV4) | ||
155 | rmnet_map_checksum_uplink_packet(skb, orig_dev); | ||
156 | |||
142 | map_header = rmnet_map_add_map_header(skb, additional_header_len, 0); | 157 | map_header = rmnet_map_add_map_header(skb, additional_header_len, 0); |
143 | if (!map_header) | 158 | if (!map_header) |
144 | goto fail; | 159 | goto fail; |
145 | 160 | ||
146 | if (mux_id == 0xff) | 161 | map_header->mux_id = mux_id; |
147 | map_header->mux_id = 0; | ||
148 | else | ||
149 | map_header->mux_id = mux_id; | ||
150 | 162 | ||
151 | skb->protocol = htons(ETH_P_MAP); | 163 | skb->protocol = htons(ETH_P_MAP); |
152 | 164 | ||
@@ -208,6 +220,8 @@ void rmnet_egress_handler(struct sk_buff *skb) | |||
208 | struct rmnet_priv *priv; | 220 | struct rmnet_priv *priv; |
209 | u8 mux_id; | 221 | u8 mux_id; |
210 | 222 | ||
223 | sk_pacing_shift_update(skb->sk, 8); | ||
224 | |||
211 | orig_dev = skb->dev; | 225 | orig_dev = skb->dev; |
212 | priv = netdev_priv(orig_dev); | 226 | priv = netdev_priv(orig_dev); |
213 | skb->dev = priv->real_dev; | 227 | skb->dev = priv->real_dev; |
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h index 4df359de28c5..6ce31e29136d 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h | |||
@@ -47,6 +47,22 @@ struct rmnet_map_header { | |||
47 | u16 pkt_len; | 47 | u16 pkt_len; |
48 | } __aligned(1); | 48 | } __aligned(1); |
49 | 49 | ||
50 | struct rmnet_map_dl_csum_trailer { | ||
51 | u8 reserved1; | ||
52 | u8 valid:1; | ||
53 | u8 reserved2:7; | ||
54 | u16 csum_start_offset; | ||
55 | u16 csum_length; | ||
56 | __be16 csum_value; | ||
57 | } __aligned(1); | ||
58 | |||
59 | struct rmnet_map_ul_csum_header { | ||
60 | __be16 csum_start_offset; | ||
61 | u16 csum_insert_offset:14; | ||
62 | u16 udp_ip4_ind:1; | ||
63 | u16 csum_enabled:1; | ||
64 | } __aligned(1); | ||
65 | |||
50 | #define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \ | 66 | #define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \ |
51 | (Y)->data)->mux_id) | 67 | (Y)->data)->mux_id) |
52 | #define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \ | 68 | #define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \ |
@@ -67,10 +83,13 @@ struct rmnet_map_header { | |||
67 | #define RMNET_MAP_NO_PAD_BYTES 0 | 83 | #define RMNET_MAP_NO_PAD_BYTES 0 |
68 | #define RMNET_MAP_ADD_PAD_BYTES 1 | 84 | #define RMNET_MAP_ADD_PAD_BYTES 1 |
69 | 85 | ||
70 | u8 rmnet_map_demultiplex(struct sk_buff *skb); | 86 | struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, |
71 | struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb); | 87 | struct rmnet_port *port); |
72 | struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, | 88 | struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, |
73 | int hdrlen, int pad); | 89 | int hdrlen, int pad); |
74 | void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port); | 90 | void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port); |
91 | int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len); | ||
92 | void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, | ||
93 | struct net_device *orig_dev); | ||
75 | 94 | ||
76 | #endif /* _RMNET_MAP_H_ */ | 95 | #endif /* _RMNET_MAP_H_ */ |
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c index 51e604923ac1..6bc328fb88e1 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c | |||
@@ -58,11 +58,24 @@ static u8 rmnet_map_do_flow_control(struct sk_buff *skb, | |||
58 | } | 58 | } |
59 | 59 | ||
60 | static void rmnet_map_send_ack(struct sk_buff *skb, | 60 | static void rmnet_map_send_ack(struct sk_buff *skb, |
61 | unsigned char type) | 61 | unsigned char type, |
62 | struct rmnet_port *port) | ||
62 | { | 63 | { |
63 | struct rmnet_map_control_command *cmd; | 64 | struct rmnet_map_control_command *cmd; |
64 | int xmit_status; | 65 | int xmit_status; |
65 | 66 | ||
67 | if (port->data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4) { | ||
68 | if (skb->len < sizeof(struct rmnet_map_header) + | ||
69 | RMNET_MAP_GET_LENGTH(skb) + | ||
70 | sizeof(struct rmnet_map_dl_csum_trailer)) { | ||
71 | kfree_skb(skb); | ||
72 | return; | ||
73 | } | ||
74 | |||
75 | skb_trim(skb, skb->len - | ||
76 | sizeof(struct rmnet_map_dl_csum_trailer)); | ||
77 | } | ||
78 | |||
66 | skb->protocol = htons(ETH_P_MAP); | 79 | skb->protocol = htons(ETH_P_MAP); |
67 | 80 | ||
68 | cmd = RMNET_MAP_GET_CMD_START(skb); | 81 | cmd = RMNET_MAP_GET_CMD_START(skb); |
@@ -100,5 +113,5 @@ void rmnet_map_command(struct sk_buff *skb, struct rmnet_port *port) | |||
100 | break; | 113 | break; |
101 | } | 114 | } |
102 | if (rc == RMNET_MAP_COMMAND_ACK) | 115 | if (rc == RMNET_MAP_COMMAND_ACK) |
103 | rmnet_map_send_ack(skb, rc); | 116 | rmnet_map_send_ack(skb, rc, port); |
104 | } | 117 | } |
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c index 86b8c758f94e..c74a6c56d315 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c | |||
@@ -14,6 +14,9 @@ | |||
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include <linux/netdevice.h> | 16 | #include <linux/netdevice.h> |
17 | #include <linux/ip.h> | ||
18 | #include <linux/ipv6.h> | ||
19 | #include <net/ip6_checksum.h> | ||
17 | #include "rmnet_config.h" | 20 | #include "rmnet_config.h" |
18 | #include "rmnet_map.h" | 21 | #include "rmnet_map.h" |
19 | #include "rmnet_private.h" | 22 | #include "rmnet_private.h" |
@@ -21,6 +24,233 @@ | |||
21 | #define RMNET_MAP_DEAGGR_SPACING 64 | 24 | #define RMNET_MAP_DEAGGR_SPACING 64 |
22 | #define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2) | 25 | #define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2) |
23 | 26 | ||
27 | static __sum16 *rmnet_map_get_csum_field(unsigned char protocol, | ||
28 | const void *txporthdr) | ||
29 | { | ||
30 | __sum16 *check = NULL; | ||
31 | |||
32 | switch (protocol) { | ||
33 | case IPPROTO_TCP: | ||
34 | check = &(((struct tcphdr *)txporthdr)->check); | ||
35 | break; | ||
36 | |||
37 | case IPPROTO_UDP: | ||
38 | check = &(((struct udphdr *)txporthdr)->check); | ||
39 | break; | ||
40 | |||
41 | default: | ||
42 | check = NULL; | ||
43 | break; | ||
44 | } | ||
45 | |||
46 | return check; | ||
47 | } | ||
48 | |||
49 | static int | ||
50 | rmnet_map_ipv4_dl_csum_trailer(struct sk_buff *skb, | ||
51 | struct rmnet_map_dl_csum_trailer *csum_trailer) | ||
52 | { | ||
53 | __sum16 *csum_field, csum_temp, pseudo_csum, hdr_csum, ip_payload_csum; | ||
54 | u16 csum_value, csum_value_final; | ||
55 | struct iphdr *ip4h; | ||
56 | void *txporthdr; | ||
57 | __be16 addend; | ||
58 | |||
59 | ip4h = (struct iphdr *)(skb->data); | ||
60 | if ((ntohs(ip4h->frag_off) & IP_MF) || | ||
61 | ((ntohs(ip4h->frag_off) & IP_OFFSET) > 0)) | ||
62 | return -EOPNOTSUPP; | ||
63 | |||
64 | txporthdr = skb->data + ip4h->ihl * 4; | ||
65 | |||
66 | csum_field = rmnet_map_get_csum_field(ip4h->protocol, txporthdr); | ||
67 | |||
68 | if (!csum_field) | ||
69 | return -EPROTONOSUPPORT; | ||
70 | |||
71 | /* RFC 768 - Skip IPv4 UDP packets where sender checksum field is 0 */ | ||
72 | if (*csum_field == 0 && ip4h->protocol == IPPROTO_UDP) | ||
73 | return 0; | ||
74 | |||
75 | csum_value = ~ntohs(csum_trailer->csum_value); | ||
76 | hdr_csum = ~ip_fast_csum(ip4h, (int)ip4h->ihl); | ||
77 | ip_payload_csum = csum16_sub((__force __sum16)csum_value, | ||
78 | (__force __be16)hdr_csum); | ||
79 | |||
80 | pseudo_csum = ~csum_tcpudp_magic(ip4h->saddr, ip4h->daddr, | ||
81 | ntohs(ip4h->tot_len) - ip4h->ihl * 4, | ||
82 | ip4h->protocol, 0); | ||
83 | addend = (__force __be16)ntohs((__force __be16)pseudo_csum); | ||
84 | pseudo_csum = csum16_add(ip_payload_csum, addend); | ||
85 | |||
86 | addend = (__force __be16)ntohs((__force __be16)*csum_field); | ||
87 | csum_temp = ~csum16_sub(pseudo_csum, addend); | ||
88 | csum_value_final = (__force u16)csum_temp; | ||
89 | |||
90 | if (unlikely(csum_value_final == 0)) { | ||
91 | switch (ip4h->protocol) { | ||
92 | case IPPROTO_UDP: | ||
93 | /* RFC 768 - DL4 1's complement rule for UDP csum 0 */ | ||
94 | csum_value_final = ~csum_value_final; | ||
95 | break; | ||
96 | |||
97 | case IPPROTO_TCP: | ||
98 | /* DL4 Non-RFC compliant TCP checksum found */ | ||
99 | if (*csum_field == (__force __sum16)0xFFFF) | ||
100 | csum_value_final = ~csum_value_final; | ||
101 | break; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | if (csum_value_final == ntohs((__force __be16)*csum_field)) | ||
106 | return 0; | ||
107 | else | ||
108 | return -EINVAL; | ||
109 | } | ||
110 | |||
111 | #if IS_ENABLED(CONFIG_IPV6) | ||
112 | static int | ||
113 | rmnet_map_ipv6_dl_csum_trailer(struct sk_buff *skb, | ||
114 | struct rmnet_map_dl_csum_trailer *csum_trailer) | ||
115 | { | ||
116 | __sum16 *csum_field, ip6_payload_csum, pseudo_csum, csum_temp; | ||
117 | u16 csum_value, csum_value_final; | ||
118 | __be16 ip6_hdr_csum, addend; | ||
119 | struct ipv6hdr *ip6h; | ||
120 | void *txporthdr; | ||
121 | u32 length; | ||
122 | |||
123 | ip6h = (struct ipv6hdr *)(skb->data); | ||
124 | |||
125 | txporthdr = skb->data + sizeof(struct ipv6hdr); | ||
126 | csum_field = rmnet_map_get_csum_field(ip6h->nexthdr, txporthdr); | ||
127 | |||
128 | if (!csum_field) | ||
129 | return -EPROTONOSUPPORT; | ||
130 | |||
131 | csum_value = ~ntohs(csum_trailer->csum_value); | ||
132 | ip6_hdr_csum = (__force __be16) | ||
133 | ~ntohs((__force __be16)ip_compute_csum(ip6h, | ||
134 | (int)(txporthdr - (void *)(skb->data)))); | ||
135 | ip6_payload_csum = csum16_sub((__force __sum16)csum_value, | ||
136 | ip6_hdr_csum); | ||
137 | |||
138 | length = (ip6h->nexthdr == IPPROTO_UDP) ? | ||
139 | ntohs(((struct udphdr *)txporthdr)->len) : | ||
140 | ntohs(ip6h->payload_len); | ||
141 | pseudo_csum = ~(csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, | ||
142 | length, ip6h->nexthdr, 0)); | ||
143 | addend = (__force __be16)ntohs((__force __be16)pseudo_csum); | ||
144 | pseudo_csum = csum16_add(ip6_payload_csum, addend); | ||
145 | |||
146 | addend = (__force __be16)ntohs((__force __be16)*csum_field); | ||
147 | csum_temp = ~csum16_sub(pseudo_csum, addend); | ||
148 | csum_value_final = (__force u16)csum_temp; | ||
149 | |||
150 | if (unlikely(csum_value_final == 0)) { | ||
151 | switch (ip6h->nexthdr) { | ||
152 | case IPPROTO_UDP: | ||
153 | /* RFC 2460 section 8.1 | ||
154 | * DL6 One's complement rule for UDP checksum 0 | ||
155 | */ | ||
156 | csum_value_final = ~csum_value_final; | ||
157 | break; | ||
158 | |||
159 | case IPPROTO_TCP: | ||
160 | /* DL6 Non-RFC compliant TCP checksum found */ | ||
161 | if (*csum_field == (__force __sum16)0xFFFF) | ||
162 | csum_value_final = ~csum_value_final; | ||
163 | break; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | if (csum_value_final == ntohs((__force __be16)*csum_field)) | ||
168 | return 0; | ||
169 | else | ||
170 | return -EINVAL; | ||
171 | } | ||
172 | #endif | ||
173 | |||
174 | static void rmnet_map_complement_ipv4_txporthdr_csum_field(void *iphdr) | ||
175 | { | ||
176 | struct iphdr *ip4h = (struct iphdr *)iphdr; | ||
177 | void *txphdr; | ||
178 | u16 *csum; | ||
179 | |||
180 | txphdr = iphdr + ip4h->ihl * 4; | ||
181 | |||
182 | if (ip4h->protocol == IPPROTO_TCP || ip4h->protocol == IPPROTO_UDP) { | ||
183 | csum = (u16 *)rmnet_map_get_csum_field(ip4h->protocol, txphdr); | ||
184 | *csum = ~(*csum); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | static void | ||
189 | rmnet_map_ipv4_ul_csum_header(void *iphdr, | ||
190 | struct rmnet_map_ul_csum_header *ul_header, | ||
191 | struct sk_buff *skb) | ||
192 | { | ||
193 | struct iphdr *ip4h = (struct iphdr *)iphdr; | ||
194 | __be16 *hdr = (__be16 *)ul_header, offset; | ||
195 | |||
196 | offset = htons((__force u16)(skb_transport_header(skb) - | ||
197 | (unsigned char *)iphdr)); | ||
198 | ul_header->csum_start_offset = offset; | ||
199 | ul_header->csum_insert_offset = skb->csum_offset; | ||
200 | ul_header->csum_enabled = 1; | ||
201 | if (ip4h->protocol == IPPROTO_UDP) | ||
202 | ul_header->udp_ip4_ind = 1; | ||
203 | else | ||
204 | ul_header->udp_ip4_ind = 0; | ||
205 | |||
206 | /* Changing remaining fields to network order */ | ||
207 | hdr++; | ||
208 | *hdr = htons((__force u16)*hdr); | ||
209 | |||
210 | skb->ip_summed = CHECKSUM_NONE; | ||
211 | |||
212 | rmnet_map_complement_ipv4_txporthdr_csum_field(iphdr); | ||
213 | } | ||
214 | |||
215 | #if IS_ENABLED(CONFIG_IPV6) | ||
216 | static void rmnet_map_complement_ipv6_txporthdr_csum_field(void *ip6hdr) | ||
217 | { | ||
218 | struct ipv6hdr *ip6h = (struct ipv6hdr *)ip6hdr; | ||
219 | void *txphdr; | ||
220 | u16 *csum; | ||
221 | |||
222 | txphdr = ip6hdr + sizeof(struct ipv6hdr); | ||
223 | |||
224 | if (ip6h->nexthdr == IPPROTO_TCP || ip6h->nexthdr == IPPROTO_UDP) { | ||
225 | csum = (u16 *)rmnet_map_get_csum_field(ip6h->nexthdr, txphdr); | ||
226 | *csum = ~(*csum); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | static void | ||
231 | rmnet_map_ipv6_ul_csum_header(void *ip6hdr, | ||
232 | struct rmnet_map_ul_csum_header *ul_header, | ||
233 | struct sk_buff *skb) | ||
234 | { | ||
235 | __be16 *hdr = (__be16 *)ul_header, offset; | ||
236 | |||
237 | offset = htons((__force u16)(skb_transport_header(skb) - | ||
238 | (unsigned char *)ip6hdr)); | ||
239 | ul_header->csum_start_offset = offset; | ||
240 | ul_header->csum_insert_offset = skb->csum_offset; | ||
241 | ul_header->csum_enabled = 1; | ||
242 | ul_header->udp_ip4_ind = 0; | ||
243 | |||
244 | /* Changing remaining fields to network order */ | ||
245 | hdr++; | ||
246 | *hdr = htons((__force u16)*hdr); | ||
247 | |||
248 | skb->ip_summed = CHECKSUM_NONE; | ||
249 | |||
250 | rmnet_map_complement_ipv6_txporthdr_csum_field(ip6hdr); | ||
251 | } | ||
252 | #endif | ||
253 | |||
24 | /* Adds MAP header to front of skb->data | 254 | /* Adds MAP header to front of skb->data |
25 | * Padding is calculated and set appropriately in MAP header. Mux ID is | 255 | * Padding is calculated and set appropriately in MAP header. Mux ID is |
26 | * initialized to 0. | 256 | * initialized to 0. |
@@ -32,9 +262,6 @@ struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, | |||
32 | u32 padding, map_datalen; | 262 | u32 padding, map_datalen; |
33 | u8 *padbytes; | 263 | u8 *padbytes; |
34 | 264 | ||
35 | if (skb_headroom(skb) < sizeof(struct rmnet_map_header)) | ||
36 | return NULL; | ||
37 | |||
38 | map_datalen = skb->len - hdrlen; | 265 | map_datalen = skb->len - hdrlen; |
39 | map_header = (struct rmnet_map_header *) | 266 | map_header = (struct rmnet_map_header *) |
40 | skb_push(skb, sizeof(struct rmnet_map_header)); | 267 | skb_push(skb, sizeof(struct rmnet_map_header)); |
@@ -69,7 +296,8 @@ done: | |||
69 | * returned, indicating that there are no more packets to deaggregate. Caller | 296 | * returned, indicating that there are no more packets to deaggregate. Caller |
70 | * is responsible for freeing the original skb. | 297 | * is responsible for freeing the original skb. |
71 | */ | 298 | */ |
72 | struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb) | 299 | struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb, |
300 | struct rmnet_port *port) | ||
73 | { | 301 | { |
74 | struct rmnet_map_header *maph; | 302 | struct rmnet_map_header *maph; |
75 | struct sk_buff *skbn; | 303 | struct sk_buff *skbn; |
@@ -81,6 +309,9 @@ struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb) | |||
81 | maph = (struct rmnet_map_header *)skb->data; | 309 | maph = (struct rmnet_map_header *)skb->data; |
82 | packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header); | 310 | packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header); |
83 | 311 | ||
312 | if (port->data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4) | ||
313 | packet_len += sizeof(struct rmnet_map_dl_csum_trailer); | ||
314 | |||
84 | if (((int)skb->len - (int)packet_len) < 0) | 315 | if (((int)skb->len - (int)packet_len) < 0) |
85 | return NULL; | 316 | return NULL; |
86 | 317 | ||
@@ -100,3 +331,73 @@ struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb) | |||
100 | 331 | ||
101 | return skbn; | 332 | return skbn; |
102 | } | 333 | } |
334 | |||
335 | /* Validates packet checksums. Function takes a pointer to | ||
336 | * the beginning of a buffer which contains the IP payload + | ||
337 | * padding + checksum trailer. | ||
338 | * Only IPv4 and IPv6 are supported along with TCP & UDP. | ||
339 | * Fragmented or tunneled packets are not supported. | ||
340 | */ | ||
341 | int rmnet_map_checksum_downlink_packet(struct sk_buff *skb, u16 len) | ||
342 | { | ||
343 | struct rmnet_map_dl_csum_trailer *csum_trailer; | ||
344 | |||
345 | if (unlikely(!(skb->dev->features & NETIF_F_RXCSUM))) | ||
346 | return -EOPNOTSUPP; | ||
347 | |||
348 | csum_trailer = (struct rmnet_map_dl_csum_trailer *)(skb->data + len); | ||
349 | |||
350 | if (!csum_trailer->valid) | ||
351 | return -EINVAL; | ||
352 | |||
353 | if (skb->protocol == htons(ETH_P_IP)) | ||
354 | return rmnet_map_ipv4_dl_csum_trailer(skb, csum_trailer); | ||
355 | else if (skb->protocol == htons(ETH_P_IPV6)) | ||
356 | #if IS_ENABLED(CONFIG_IPV6) | ||
357 | return rmnet_map_ipv6_dl_csum_trailer(skb, csum_trailer); | ||
358 | #else | ||
359 | return -EPROTONOSUPPORT; | ||
360 | #endif | ||
361 | |||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | /* Generates UL checksum meta info header for IPv4 and IPv6 over TCP and UDP | ||
366 | * packets that are supported for UL checksum offload. | ||
367 | */ | ||
368 | void rmnet_map_checksum_uplink_packet(struct sk_buff *skb, | ||
369 | struct net_device *orig_dev) | ||
370 | { | ||
371 | struct rmnet_map_ul_csum_header *ul_header; | ||
372 | void *iphdr; | ||
373 | |||
374 | ul_header = (struct rmnet_map_ul_csum_header *) | ||
375 | skb_push(skb, sizeof(struct rmnet_map_ul_csum_header)); | ||
376 | |||
377 | if (unlikely(!(orig_dev->features & | ||
378 | (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)))) | ||
379 | goto sw_csum; | ||
380 | |||
381 | if (skb->ip_summed == CHECKSUM_PARTIAL) { | ||
382 | iphdr = (char *)ul_header + | ||
383 | sizeof(struct rmnet_map_ul_csum_header); | ||
384 | |||
385 | if (skb->protocol == htons(ETH_P_IP)) { | ||
386 | rmnet_map_ipv4_ul_csum_header(iphdr, ul_header, skb); | ||
387 | return; | ||
388 | } else if (skb->protocol == htons(ETH_P_IPV6)) { | ||
389 | #if IS_ENABLED(CONFIG_IPV6) | ||
390 | rmnet_map_ipv6_ul_csum_header(iphdr, ul_header, skb); | ||
391 | return; | ||
392 | #else | ||
393 | goto sw_csum; | ||
394 | #endif | ||
395 | } | ||
396 | } | ||
397 | |||
398 | sw_csum: | ||
399 | ul_header->csum_start_offset = 0; | ||
400 | ul_header->csum_insert_offset = 0; | ||
401 | ul_header->csum_enabled = 0; | ||
402 | ul_header->udp_ip4_ind = 0; | ||
403 | } | ||
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h index d21428078504..de0143eaa05a 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h | |||
@@ -21,6 +21,8 @@ | |||
21 | /* Constants */ | 21 | /* Constants */ |
22 | #define RMNET_INGRESS_FORMAT_DEAGGREGATION BIT(0) | 22 | #define RMNET_INGRESS_FORMAT_DEAGGREGATION BIT(0) |
23 | #define RMNET_INGRESS_FORMAT_MAP_COMMANDS BIT(1) | 23 | #define RMNET_INGRESS_FORMAT_MAP_COMMANDS BIT(1) |
24 | #define RMNET_INGRESS_FORMAT_MAP_CKSUMV4 BIT(2) | ||
25 | #define RMNET_EGRESS_FORMAT_MAP_CKSUMV4 BIT(3) | ||
24 | 26 | ||
25 | /* Replace skb->dev to a virtual rmnet device and pass up the stack */ | 27 | /* Replace skb->dev to a virtual rmnet device and pass up the stack */ |
26 | #define RMNET_EPMODE_VND (1) | 28 | #define RMNET_EPMODE_VND (1) |
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c index 5bb29f44d114..570a227acdd8 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c | |||
@@ -188,6 +188,10 @@ int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, | |||
188 | if (rmnet_get_endpoint(port, id)) | 188 | if (rmnet_get_endpoint(port, id)) |
189 | return -EBUSY; | 189 | return -EBUSY; |
190 | 190 | ||
191 | rmnet_dev->hw_features = NETIF_F_RXCSUM; | ||
192 | rmnet_dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; | ||
193 | rmnet_dev->hw_features |= NETIF_F_SG; | ||
194 | |||
191 | rc = register_netdevice(rmnet_dev); | 195 | rc = register_netdevice(rmnet_dev); |
192 | if (!rc) { | 196 | if (!rc) { |
193 | ep->egress_dev = rmnet_dev; | 197 | ep->egress_dev = rmnet_dev; |