aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorTom Herbert <therbert@google.com>2014-11-04 12:06:57 -0500
committerDavid S. Miller <davem@davemloft.net>2014-11-05 16:30:04 -0500
commita8d31c128bf574bed2fa29e0512b24d446018a50 (patch)
tree01bfc4d3ef16fe7e5a4da0be1e7f3fd432e7495f /net
parentb17f709a24013fcbb257f6f89b4d81ac9fdf0d18 (diff)
gue: Receive side of remote checksum offload
Add processing of the remote checksum offload option in both the normal path as well as the GRO path. The implements patching the affected checksum to derive the offloaded checksum. Signed-off-by: Tom Herbert <therbert@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/fou.c170
1 files changed, 161 insertions, 9 deletions
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index fb0db99adf9e..740ae099a0d9 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -63,6 +63,59 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
63 return -fou->protocol; 63 return -fou->protocol;
64} 64}
65 65
66static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
67 void *data, int hdrlen, u8 ipproto)
68{
69 __be16 *pd = data;
70 u16 start = ntohs(pd[0]);
71 u16 offset = ntohs(pd[1]);
72 u16 poffset = 0;
73 u16 plen;
74 __wsum csum, delta;
75 __sum16 *psum;
76
77 if (skb->remcsum_offload) {
78 /* Already processed in GRO path */
79 skb->remcsum_offload = 0;
80 return guehdr;
81 }
82
83 if (start > skb->len - hdrlen ||
84 offset > skb->len - hdrlen - sizeof(u16))
85 return NULL;
86
87 if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE))
88 __skb_checksum_complete(skb);
89
90 plen = hdrlen + offset + sizeof(u16);
91 if (!pskb_may_pull(skb, plen))
92 return NULL;
93 guehdr = (struct guehdr *)&udp_hdr(skb)[1];
94
95 if (ipproto == IPPROTO_IP && sizeof(struct iphdr) < plen) {
96 struct iphdr *ip = (struct iphdr *)(skb->data + hdrlen);
97
98 /* If next header happens to be IP we can skip that for the
99 * checksum calculation since the IP header checksum is zero
100 * if correct.
101 */
102 poffset = ip->ihl * 4;
103 }
104
105 csum = csum_sub(skb->csum, skb_checksum(skb, poffset + hdrlen,
106 start - poffset - hdrlen, 0));
107
108 /* Set derived checksum in packet */
109 psum = (__sum16 *)(skb->data + hdrlen + offset);
110 delta = csum_sub(csum_fold(csum), *psum);
111 *psum = csum_fold(csum);
112
113 /* Adjust skb->csum since we changed the packet */
114 skb->csum = csum_add(skb->csum, delta);
115
116 return guehdr;
117}
118
66static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr) 119static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr)
67{ 120{
68 /* No support yet */ 121 /* No support yet */
@@ -76,6 +129,7 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
76 size_t len, optlen, hdrlen; 129 size_t len, optlen, hdrlen;
77 struct guehdr *guehdr; 130 struct guehdr *guehdr;
78 void *data; 131 void *data;
132 u16 doffset = 0;
79 133
80 if (!fou) 134 if (!fou)
81 return 1; 135 return 1;
@@ -100,20 +154,43 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
100 if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen)) 154 if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen))
101 goto drop; 155 goto drop;
102 156
103 /* Pull UDP and GUE headers */ 157 hdrlen = sizeof(struct guehdr) + optlen;
104 fou_recv_pull(skb, len); 158
159 ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
160
161 /* Pull UDP header now, skb->data points to guehdr */
162 __skb_pull(skb, sizeof(struct udphdr));
163
164 /* Pull csum through the guehdr now . This can be used if
165 * there is a remote checksum offload.
166 */
167 skb_postpull_rcsum(skb, udp_hdr(skb), len);
105 168
106 data = &guehdr[1]; 169 data = &guehdr[1];
107 170
108 if (guehdr->flags & GUE_FLAG_PRIV) { 171 if (guehdr->flags & GUE_FLAG_PRIV) {
109 data += GUE_LEN_PRIV; 172 __be32 flags = *(__be32 *)(data + doffset);
173
174 doffset += GUE_LEN_PRIV;
110 175
111 /* Process private flags */ 176 if (flags & GUE_PFLAG_REMCSUM) {
177 guehdr = gue_remcsum(skb, guehdr, data + doffset,
178 hdrlen, guehdr->proto_ctype);
179 if (!guehdr)
180 goto drop;
181
182 data = &guehdr[1];
183
184 doffset += GUE_PLEN_REMCSUM;
185 }
112 } 186 }
113 187
114 if (unlikely(guehdr->control)) 188 if (unlikely(guehdr->control))
115 return gue_control_message(skb, guehdr); 189 return gue_control_message(skb, guehdr);
116 190
191 __skb_pull(skb, hdrlen);
192 skb_reset_transport_header(skb);
193
117 return -guehdr->proto_ctype; 194 return -guehdr->proto_ctype;
118 195
119drop: 196drop:
@@ -164,6 +241,66 @@ out_unlock:
164 return err; 241 return err;
165} 242}
166 243
244static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
245 struct guehdr *guehdr, void *data,
246 size_t hdrlen, u8 ipproto)
247{
248 __be16 *pd = data;
249 u16 start = ntohs(pd[0]);
250 u16 offset = ntohs(pd[1]);
251 u16 poffset = 0;
252 u16 plen;
253 void *ptr;
254 __wsum csum, delta;
255 __sum16 *psum;
256
257 if (skb->remcsum_offload)
258 return guehdr;
259
260 if (start > skb_gro_len(skb) - hdrlen ||
261 offset > skb_gro_len(skb) - hdrlen - sizeof(u16) ||
262 !NAPI_GRO_CB(skb)->csum_valid || skb->remcsum_offload)
263 return NULL;
264
265 plen = hdrlen + offset + sizeof(u16);
266
267 /* Pull checksum that will be written */
268 if (skb_gro_header_hard(skb, off + plen)) {
269 guehdr = skb_gro_header_slow(skb, off + plen, off);
270 if (!guehdr)
271 return NULL;
272 }
273
274 ptr = (void *)guehdr + hdrlen;
275
276 if (ipproto == IPPROTO_IP &&
277 (hdrlen + sizeof(struct iphdr) < plen)) {
278 struct iphdr *ip = (struct iphdr *)(ptr + hdrlen);
279
280 /* If next header happens to be IP we can skip
281 * that for the checksum calculation since the
282 * IP header checksum is zero if correct.
283 */
284 poffset = ip->ihl * 4;
285 }
286
287 csum = csum_sub(NAPI_GRO_CB(skb)->csum,
288 csum_partial(ptr + poffset, start - poffset, 0));
289
290 /* Set derived checksum in packet */
291 psum = (__sum16 *)(ptr + offset);
292 delta = csum_sub(csum_fold(csum), *psum);
293 *psum = csum_fold(csum);
294
295 /* Adjust skb->csum since we changed the packet */
296 skb->csum = csum_add(skb->csum, delta);
297 NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta);
298
299 skb->remcsum_offload = 1;
300
301 return guehdr;
302}
303
167static struct sk_buff **gue_gro_receive(struct sk_buff **head, 304static struct sk_buff **gue_gro_receive(struct sk_buff **head,
168 struct sk_buff *skb) 305 struct sk_buff *skb)
169{ 306{
@@ -174,6 +311,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
174 struct guehdr *guehdr; 311 struct guehdr *guehdr;
175 size_t len, optlen, hdrlen, off; 312 size_t len, optlen, hdrlen, off;
176 void *data; 313 void *data;
314 u16 doffset = 0;
177 int flush = 1; 315 int flush = 1;
178 316
179 off = skb_gro_offset(skb); 317 off = skb_gro_offset(skb);
@@ -201,19 +339,33 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
201 339
202 hdrlen = sizeof(*guehdr) + optlen; 340 hdrlen = sizeof(*guehdr) + optlen;
203 341
204 skb_gro_pull(skb, hdrlen); 342 /* Adjust NAPI_GRO_CB(skb)->csum to account for guehdr,
205 343 * this is needed if there is a remote checkcsum offload.
206 /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/ 344 */
207 skb_gro_postpull_rcsum(skb, guehdr, hdrlen); 345 skb_gro_postpull_rcsum(skb, guehdr, hdrlen);
208 346
209 data = &guehdr[1]; 347 data = &guehdr[1];
210 348
211 if (guehdr->flags & GUE_FLAG_PRIV) { 349 if (guehdr->flags & GUE_FLAG_PRIV) {
212 data += GUE_LEN_PRIV; 350 __be32 flags = *(__be32 *)(data + doffset);
213 351
214 /* Process private flags */ 352 doffset += GUE_LEN_PRIV;
353
354 if (flags & GUE_PFLAG_REMCSUM) {
355 guehdr = gue_gro_remcsum(skb, off, guehdr,
356 data + doffset, hdrlen,
357 guehdr->proto_ctype);
358 if (!guehdr)
359 goto out;
360
361 data = &guehdr[1];
362
363 doffset += GUE_PLEN_REMCSUM;
364 }
215 } 365 }
216 366
367 skb_gro_pull(skb, hdrlen);
368
217 flush = 0; 369 flush = 0;
218 370
219 for (p = *head; p; p = p->next) { 371 for (p = *head; p; p = p->next) {