diff options
Diffstat (limited to 'net/ipv6/reassembly.c')
| -rw-r--r-- | net/ipv6/reassembly.c | 71 |
1 files changed, 15 insertions, 56 deletions
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 545c4141b755..64cfef1b0a4c 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c | |||
| @@ -149,13 +149,6 @@ int ip6_frag_match(struct inet_frag_queue *q, void *a) | |||
| 149 | } | 149 | } |
| 150 | EXPORT_SYMBOL(ip6_frag_match); | 150 | EXPORT_SYMBOL(ip6_frag_match); |
| 151 | 151 | ||
| 152 | /* Memory Tracking Functions. */ | ||
| 153 | static void frag_kfree_skb(struct netns_frags *nf, struct sk_buff *skb) | ||
| 154 | { | ||
| 155 | atomic_sub(skb->truesize, &nf->mem); | ||
| 156 | kfree_skb(skb); | ||
| 157 | } | ||
| 158 | |||
| 159 | void ip6_frag_init(struct inet_frag_queue *q, void *a) | 152 | void ip6_frag_init(struct inet_frag_queue *q, void *a) |
| 160 | { | 153 | { |
| 161 | struct frag_queue *fq = container_of(q, struct frag_queue, q); | 154 | struct frag_queue *fq = container_of(q, struct frag_queue, q); |
| @@ -346,58 +339,22 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, | |||
| 346 | } | 339 | } |
| 347 | 340 | ||
| 348 | found: | 341 | found: |
| 349 | /* We found where to put this one. Check for overlap with | 342 | /* RFC5722, Section 4: |
| 350 | * preceding fragment, and, if needed, align things so that | 343 | * When reassembling an IPv6 datagram, if |
| 351 | * any overlaps are eliminated. | 344 | * one or more its constituent fragments is determined to be an |
| 345 | * overlapping fragment, the entire datagram (and any constituent | ||
| 346 | * fragments, including those not yet received) MUST be silently | ||
| 347 | * discarded. | ||
| 352 | */ | 348 | */ |
| 353 | if (prev) { | ||
| 354 | int i = (FRAG6_CB(prev)->offset + prev->len) - offset; | ||
| 355 | 349 | ||
| 356 | if (i > 0) { | 350 | /* Check for overlap with preceding fragment. */ |
| 357 | offset += i; | 351 | if (prev && |
| 358 | if (end <= offset) | 352 | (FRAG6_CB(prev)->offset + prev->len) - offset > 0) |
| 359 | goto err; | 353 | goto discard_fq; |
| 360 | if (!pskb_pull(skb, i)) | ||
| 361 | goto err; | ||
| 362 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) | ||
| 363 | skb->ip_summed = CHECKSUM_NONE; | ||
| 364 | } | ||
| 365 | } | ||
| 366 | 354 | ||
| 367 | /* Look for overlap with succeeding segments. | 355 | /* Look for overlap with succeeding segment. */ |
| 368 | * If we can merge fragments, do it. | 356 | if (next && FRAG6_CB(next)->offset < end) |
| 369 | */ | 357 | goto discard_fq; |
| 370 | while (next && FRAG6_CB(next)->offset < end) { | ||
| 371 | int i = end - FRAG6_CB(next)->offset; /* overlap is 'i' bytes */ | ||
| 372 | |||
| 373 | if (i < next->len) { | ||
| 374 | /* Eat head of the next overlapped fragment | ||
| 375 | * and leave the loop. The next ones cannot overlap. | ||
| 376 | */ | ||
| 377 | if (!pskb_pull(next, i)) | ||
| 378 | goto err; | ||
| 379 | FRAG6_CB(next)->offset += i; /* next fragment */ | ||
| 380 | fq->q.meat -= i; | ||
| 381 | if (next->ip_summed != CHECKSUM_UNNECESSARY) | ||
| 382 | next->ip_summed = CHECKSUM_NONE; | ||
| 383 | break; | ||
| 384 | } else { | ||
| 385 | struct sk_buff *free_it = next; | ||
| 386 | |||
| 387 | /* Old fragment is completely overridden with | ||
| 388 | * new one drop it. | ||
| 389 | */ | ||
| 390 | next = next->next; | ||
| 391 | |||
| 392 | if (prev) | ||
| 393 | prev->next = next; | ||
| 394 | else | ||
| 395 | fq->q.fragments = next; | ||
| 396 | |||
| 397 | fq->q.meat -= free_it->len; | ||
| 398 | frag_kfree_skb(fq->q.net, free_it); | ||
| 399 | } | ||
| 400 | } | ||
| 401 | 358 | ||
| 402 | FRAG6_CB(skb)->offset = offset; | 359 | FRAG6_CB(skb)->offset = offset; |
| 403 | 360 | ||
| @@ -436,6 +393,8 @@ found: | |||
| 436 | write_unlock(&ip6_frags.lock); | 393 | write_unlock(&ip6_frags.lock); |
| 437 | return -1; | 394 | return -1; |
| 438 | 395 | ||
| 396 | discard_fq: | ||
| 397 | fq_kill(fq); | ||
| 439 | err: | 398 | err: |
| 440 | IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), | 399 | IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), |
| 441 | IPSTATS_MIB_REASMFAILS); | 400 | IPSTATS_MIB_REASMFAILS); |
