aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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) {