aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Herbert <therbert@google.com>2015-02-10 19:30:27 -0500
committerDavid S. Miller <davem@davemloft.net>2015-02-11 18:12:09 -0500
commit26c4f7da3e413da697a7beb22ad496390eda7da0 (patch)
tree55182c7a68fc4129369ffce2ac30a897cc3d55a1
parent13101602c4a9f653d59af9469040797bc5b361ca (diff)
net: Fix remcsum in GRO path to not change packet
Remote checksum offload processing is currently the same for both the GRO and non-GRO path. When the remote checksum offload option is encountered, the checksum field referred to is modified in the packet. So in the GRO case, the packet is modified in the GRO path and then the operation is skipped when the packet goes through the normal path based on skb->remcsum_offload. There is a problem in that the packet may be modified in the GRO path, but then forwarded off host still containing the remote checksum option. A remote host will again perform RCO but now the checksum verification will fail since GRO RCO already modified the checksum. To fix this, we ensure that GRO restores a packet to it's original state before returning. In this model, when GRO processes a remote checksum option it still changes the checksum per the algorithm but on return from lower layer processing the checksum is restored to its original value. In this patch we add define gro_remcsum structure which is passed to skb_gro_remcsum_process to save offset and delta for the checksum being changed. After lower layer processing, skb_gro_remcsum_cleanup is called to restore the checksum before returning from GRO. Signed-off-by: Tom Herbert <therbert@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/vxlan.c19
-rw-r--r--include/linux/netdevice.h25
-rw-r--r--include/net/checksum.h5
-rw-r--r--net/ipv4/fou.c20
4 files changed, 47 insertions, 22 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 0e57e862c399..30310a63475a 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -555,12 +555,12 @@ static int vxlan_fdb_append(struct vxlan_fdb *f,
555static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb, 555static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb,
556 unsigned int off, 556 unsigned int off,
557 struct vxlanhdr *vh, size_t hdrlen, 557 struct vxlanhdr *vh, size_t hdrlen,
558 u32 data) 558 u32 data, struct gro_remcsum *grc)
559{ 559{
560 size_t start, offset, plen; 560 size_t start, offset, plen;
561 561
562 if (skb->remcsum_offload) 562 if (skb->remcsum_offload)
563 return vh; 563 return NULL;
564 564
565 if (!NAPI_GRO_CB(skb)->csum_valid) 565 if (!NAPI_GRO_CB(skb)->csum_valid)
566 return NULL; 566 return NULL;
@@ -579,7 +579,8 @@ static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb,
579 return NULL; 579 return NULL;
580 } 580 }
581 581
582 skb_gro_remcsum_process(skb, (void *)vh + hdrlen, start, offset); 582 skb_gro_remcsum_process(skb, (void *)vh + hdrlen,
583 start, offset, grc);
583 584
584 skb->remcsum_offload = 1; 585 skb->remcsum_offload = 1;
585 586
@@ -597,6 +598,9 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head,
597 struct vxlan_sock *vs = container_of(uoff, struct vxlan_sock, 598 struct vxlan_sock *vs = container_of(uoff, struct vxlan_sock,
598 udp_offloads); 599 udp_offloads);
599 u32 flags; 600 u32 flags;
601 struct gro_remcsum grc;
602
603 skb_gro_remcsum_init(&grc);
600 604
601 off_vx = skb_gro_offset(skb); 605 off_vx = skb_gro_offset(skb);
602 hlen = off_vx + sizeof(*vh); 606 hlen = off_vx + sizeof(*vh);
@@ -614,7 +618,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head,
614 618
615 if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) { 619 if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) {
616 vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr), 620 vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr),
617 ntohl(vh->vx_vni)); 621 ntohl(vh->vx_vni), &grc);
618 622
619 if (!vh) 623 if (!vh)
620 goto out; 624 goto out;
@@ -637,6 +641,7 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head,
637 pp = eth_gro_receive(head, skb); 641 pp = eth_gro_receive(head, skb);
638 642
639out: 643out:
644 skb_gro_remcsum_cleanup(skb, &grc);
640 NAPI_GRO_CB(skb)->flush |= flush; 645 NAPI_GRO_CB(skb)->flush |= flush;
641 646
642 return pp; 647 return pp;
@@ -1154,12 +1159,6 @@ static struct vxlanhdr *vxlan_remcsum(struct sk_buff *skb, struct vxlanhdr *vh,
1154{ 1159{
1155 size_t start, offset, plen; 1160 size_t start, offset, plen;
1156 1161
1157 if (skb->remcsum_offload) {
1158 /* Already processed in GRO path */
1159 skb->remcsum_offload = 0;
1160 return vh;
1161 }
1162
1163 start = (data & VXLAN_RCO_MASK) << VXLAN_RCO_SHIFT; 1162 start = (data & VXLAN_RCO_MASK) << VXLAN_RCO_SHIFT;
1164 offset = start + ((data & VXLAN_RCO_UDP) ? 1163 offset = start + ((data & VXLAN_RCO_UDP) ?
1165 offsetof(struct udphdr, check) : 1164 offsetof(struct udphdr, check) :
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d115256ed5a2..3aa02457fe4f 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2321,8 +2321,19 @@ do { \
2321 compute_pseudo(skb, proto)); \ 2321 compute_pseudo(skb, proto)); \
2322} while (0) 2322} while (0)
2323 2323
2324struct gro_remcsum {
2325 int offset;
2326 __wsum delta;
2327};
2328
2329static inline void skb_gro_remcsum_init(struct gro_remcsum *grc)
2330{
2331 grc->delta = 0;
2332}
2333
2324static inline void skb_gro_remcsum_process(struct sk_buff *skb, void *ptr, 2334static inline void skb_gro_remcsum_process(struct sk_buff *skb, void *ptr,
2325 int start, int offset) 2335 int start, int offset,
2336 struct gro_remcsum *grc)
2326{ 2337{
2327 __wsum delta; 2338 __wsum delta;
2328 2339
@@ -2331,10 +2342,20 @@ static inline void skb_gro_remcsum_process(struct sk_buff *skb, void *ptr,
2331 delta = remcsum_adjust(ptr, NAPI_GRO_CB(skb)->csum, start, offset); 2342 delta = remcsum_adjust(ptr, NAPI_GRO_CB(skb)->csum, start, offset);
2332 2343
2333 /* Adjust skb->csum since we changed the packet */ 2344 /* Adjust skb->csum since we changed the packet */
2334 skb->csum = csum_add(skb->csum, delta);
2335 NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta); 2345 NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta);
2346
2347 grc->offset = (ptr + offset) - (void *)skb->head;
2348 grc->delta = delta;
2336} 2349}
2337 2350
2351static inline void skb_gro_remcsum_cleanup(struct sk_buff *skb,
2352 struct gro_remcsum *grc)
2353{
2354 if (!grc->delta)
2355 return;
2356
2357 remcsum_unadjust((__sum16 *)(skb->head + grc->offset), grc->delta);
2358}
2338 2359
2339static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev, 2360static inline int dev_hard_header(struct sk_buff *skb, struct net_device *dev,
2340 unsigned short type, 2361 unsigned short type,
diff --git a/include/net/checksum.h b/include/net/checksum.h
index e339a9513e29..0a55ac715077 100644
--- a/include/net/checksum.h
+++ b/include/net/checksum.h
@@ -167,4 +167,9 @@ static inline __wsum remcsum_adjust(void *ptr, __wsum csum,
167 return delta; 167 return delta;
168} 168}
169 169
170static inline void remcsum_unadjust(__sum16 *psum, __wsum delta)
171{
172 *psum = csum_fold(csum_sub(delta, *psum));
173}
174
170#endif 175#endif
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index 92ddea1e6457..7fa8d36e56d4 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -71,12 +71,6 @@ static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
71 size_t offset = ntohs(pd[1]); 71 size_t offset = ntohs(pd[1]);
72 size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start); 72 size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
73 73
74 if (skb->remcsum_offload) {
75 /* Already processed in GRO path */
76 skb->remcsum_offload = 0;
77 return guehdr;
78 }
79
80 if (!pskb_may_pull(skb, plen)) 74 if (!pskb_may_pull(skb, plen))
81 return NULL; 75 return NULL;
82 guehdr = (struct guehdr *)&udp_hdr(skb)[1]; 76 guehdr = (struct guehdr *)&udp_hdr(skb)[1];
@@ -214,7 +208,8 @@ out_unlock:
214 208
215static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off, 209static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
216 struct guehdr *guehdr, void *data, 210 struct guehdr *guehdr, void *data,
217 size_t hdrlen, u8 ipproto) 211 size_t hdrlen, u8 ipproto,
212 struct gro_remcsum *grc)
218{ 213{
219 __be16 *pd = data; 214 __be16 *pd = data;
220 size_t start = ntohs(pd[0]); 215 size_t start = ntohs(pd[0]);
@@ -222,7 +217,7 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
222 size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start); 217 size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
223 218
224 if (skb->remcsum_offload) 219 if (skb->remcsum_offload)
225 return guehdr; 220 return NULL;
226 221
227 if (!NAPI_GRO_CB(skb)->csum_valid) 222 if (!NAPI_GRO_CB(skb)->csum_valid)
228 return NULL; 223 return NULL;
@@ -234,7 +229,8 @@ static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
234 return NULL; 229 return NULL;
235 } 230 }
236 231
237 skb_gro_remcsum_process(skb, (void *)guehdr + hdrlen, start, offset); 232 skb_gro_remcsum_process(skb, (void *)guehdr + hdrlen,
233 start, offset, grc);
238 234
239 skb->remcsum_offload = 1; 235 skb->remcsum_offload = 1;
240 236
@@ -254,6 +250,9 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
254 void *data; 250 void *data;
255 u16 doffset = 0; 251 u16 doffset = 0;
256 int flush = 1; 252 int flush = 1;
253 struct gro_remcsum grc;
254
255 skb_gro_remcsum_init(&grc);
257 256
258 off = skb_gro_offset(skb); 257 off = skb_gro_offset(skb);
259 len = off + sizeof(*guehdr); 258 len = off + sizeof(*guehdr);
@@ -295,7 +294,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
295 if (flags & GUE_PFLAG_REMCSUM) { 294 if (flags & GUE_PFLAG_REMCSUM) {
296 guehdr = gue_gro_remcsum(skb, off, guehdr, 295 guehdr = gue_gro_remcsum(skb, off, guehdr,
297 data + doffset, hdrlen, 296 data + doffset, hdrlen,
298 guehdr->proto_ctype); 297 guehdr->proto_ctype, &grc);
299 if (!guehdr) 298 if (!guehdr)
300 goto out; 299 goto out;
301 300
@@ -345,6 +344,7 @@ out_unlock:
345 rcu_read_unlock(); 344 rcu_read_unlock();
346out: 345out:
347 NAPI_GRO_CB(skb)->flush |= flush; 346 NAPI_GRO_CB(skb)->flush |= flush;
347 skb_gro_remcsum_cleanup(skb, &grc);
348 348
349 return pp; 349 return pp;
350} 350}