aboutsummaryrefslogtreecommitdiffstats
path: root/net
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 /net
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>
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/fou.c20
1 files changed, 10 insertions, 10 deletions
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}