diff options
-rw-r--r-- | net/ipv4/fou.c | 170 |
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 | ||
66 | static 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 | |||
66 | static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr) | 119 | static 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 | ||
119 | drop: | 196 | drop: |
@@ -164,6 +241,66 @@ out_unlock: | |||
164 | return err; | 241 | return err; |
165 | } | 242 | } |
166 | 243 | ||
244 | static 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 | |||
167 | static struct sk_buff **gue_gro_receive(struct sk_buff **head, | 304 | static 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) { |