diff options
Diffstat (limited to 'net/ipv6/reassembly.c')
| -rw-r--r-- | net/ipv6/reassembly.c | 116 |
1 files changed, 39 insertions, 77 deletions
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 2cddea3bd6be..64cfef1b0a4c 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c | |||
| @@ -41,6 +41,7 @@ | |||
| 41 | #include <linux/random.h> | 41 | #include <linux/random.h> |
| 42 | #include <linux/jhash.h> | 42 | #include <linux/jhash.h> |
| 43 | #include <linux/skbuff.h> | 43 | #include <linux/skbuff.h> |
| 44 | #include <linux/slab.h> | ||
| 44 | 45 | ||
| 45 | #include <net/sock.h> | 46 | #include <net/sock.h> |
| 46 | #include <net/snmp.h> | 47 | #include <net/snmp.h> |
| @@ -148,16 +149,6 @@ int ip6_frag_match(struct inet_frag_queue *q, void *a) | |||
| 148 | } | 149 | } |
| 149 | EXPORT_SYMBOL(ip6_frag_match); | 150 | EXPORT_SYMBOL(ip6_frag_match); |
| 150 | 151 | ||
| 151 | /* Memory Tracking Functions. */ | ||
| 152 | static inline void frag_kfree_skb(struct netns_frags *nf, | ||
| 153 | struct sk_buff *skb, int *work) | ||
| 154 | { | ||
| 155 | if (work) | ||
| 156 | *work -= skb->truesize; | ||
| 157 | atomic_sub(skb->truesize, &nf->mem); | ||
| 158 | kfree_skb(skb); | ||
| 159 | } | ||
| 160 | |||
| 161 | void ip6_frag_init(struct inet_frag_queue *q, void *a) | 152 | void ip6_frag_init(struct inet_frag_queue *q, void *a) |
| 162 | { | 153 | { |
| 163 | struct frag_queue *fq = container_of(q, struct frag_queue, q); | 154 | struct frag_queue *fq = container_of(q, struct frag_queue, q); |
| @@ -228,7 +219,7 @@ static void ip6_frag_expire(unsigned long data) | |||
| 228 | pointer directly, device might already disappeared. | 219 | pointer directly, device might already disappeared. |
| 229 | */ | 220 | */ |
| 230 | fq->q.fragments->dev = dev; | 221 | fq->q.fragments->dev = dev; |
| 231 | icmpv6_send(fq->q.fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0, dev); | 222 | icmpv6_send(fq->q.fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0); |
| 232 | out_rcu_unlock: | 223 | out_rcu_unlock: |
| 233 | rcu_read_unlock(); | 224 | rcu_read_unlock(); |
| 234 | out: | 225 | out: |
| @@ -237,8 +228,7 @@ out: | |||
| 237 | } | 228 | } |
| 238 | 229 | ||
| 239 | static __inline__ struct frag_queue * | 230 | static __inline__ struct frag_queue * |
| 240 | fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst, | 231 | fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst) |
| 241 | struct inet6_dev *idev) | ||
| 242 | { | 232 | { |
| 243 | struct inet_frag_queue *q; | 233 | struct inet_frag_queue *q; |
| 244 | struct ip6_create_arg arg; | 234 | struct ip6_create_arg arg; |
| @@ -254,13 +244,9 @@ fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst, | |||
| 254 | 244 | ||
| 255 | q = inet_frag_find(&net->ipv6.frags, &ip6_frags, &arg, hash); | 245 | q = inet_frag_find(&net->ipv6.frags, &ip6_frags, &arg, hash); |
| 256 | if (q == NULL) | 246 | if (q == NULL) |
| 257 | goto oom; | 247 | return NULL; |
| 258 | 248 | ||
| 259 | return container_of(q, struct frag_queue, q); | 249 | return container_of(q, struct frag_queue, q); |
| 260 | |||
| 261 | oom: | ||
| 262 | IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_REASMFAILS); | ||
| 263 | return NULL; | ||
| 264 | } | 250 | } |
| 265 | 251 | ||
| 266 | static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, | 252 | static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, |
| @@ -340,6 +326,11 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, | |||
| 340 | * in the chain of fragments so far. We must know where to put | 326 | * in the chain of fragments so far. We must know where to put |
| 341 | * this fragment, right? | 327 | * this fragment, right? |
| 342 | */ | 328 | */ |
| 329 | prev = fq->q.fragments_tail; | ||
| 330 | if (!prev || FRAG6_CB(prev)->offset < offset) { | ||
| 331 | next = NULL; | ||
| 332 | goto found; | ||
| 333 | } | ||
| 343 | prev = NULL; | 334 | prev = NULL; |
| 344 | for(next = fq->q.fragments; next != NULL; next = next->next) { | 335 | for(next = fq->q.fragments; next != NULL; next = next->next) { |
| 345 | if (FRAG6_CB(next)->offset >= offset) | 336 | if (FRAG6_CB(next)->offset >= offset) |
| @@ -347,63 +338,30 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, | |||
| 347 | prev = next; | 338 | prev = next; |
| 348 | } | 339 | } |
| 349 | 340 | ||
| 350 | /* We found where to put this one. Check for overlap with | 341 | found: |
| 351 | * preceding fragment, and, if needed, align things so that | 342 | /* RFC5722, Section 4: |
| 352 | * any overlaps are eliminated. | 343 | * When reassembling an IPv6 datagram, if |
| 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. | ||
| 353 | */ | 348 | */ |
| 354 | if (prev) { | ||
| 355 | int i = (FRAG6_CB(prev)->offset + prev->len) - offset; | ||
| 356 | 349 | ||
| 357 | if (i > 0) { | 350 | /* Check for overlap with preceding fragment. */ |
| 358 | offset += i; | 351 | if (prev && |
| 359 | if (end <= offset) | 352 | (FRAG6_CB(prev)->offset + prev->len) - offset > 0) |
| 360 | goto err; | 353 | goto discard_fq; |
| 361 | if (!pskb_pull(skb, i)) | ||
| 362 | goto err; | ||
| 363 | if (skb->ip_summed != CHECKSUM_UNNECESSARY) | ||
| 364 | skb->ip_summed = CHECKSUM_NONE; | ||
| 365 | } | ||
| 366 | } | ||
| 367 | 354 | ||
| 368 | /* Look for overlap with succeeding segments. | 355 | /* Look for overlap with succeeding segment. */ |
| 369 | * If we can merge fragments, do it. | 356 | if (next && FRAG6_CB(next)->offset < end) |
| 370 | */ | 357 | goto discard_fq; |
| 371 | while (next && FRAG6_CB(next)->offset < end) { | ||
| 372 | int i = end - FRAG6_CB(next)->offset; /* overlap is 'i' bytes */ | ||
| 373 | |||
| 374 | if (i < next->len) { | ||
| 375 | /* Eat head of the next overlapped fragment | ||
| 376 | * and leave the loop. The next ones cannot overlap. | ||
| 377 | */ | ||
| 378 | if (!pskb_pull(next, i)) | ||
| 379 | goto err; | ||
| 380 | FRAG6_CB(next)->offset += i; /* next fragment */ | ||
| 381 | fq->q.meat -= i; | ||
| 382 | if (next->ip_summed != CHECKSUM_UNNECESSARY) | ||
| 383 | next->ip_summed = CHECKSUM_NONE; | ||
| 384 | break; | ||
| 385 | } else { | ||
| 386 | struct sk_buff *free_it = next; | ||
| 387 | |||
| 388 | /* Old fragment is completely overridden with | ||
| 389 | * new one drop it. | ||
| 390 | */ | ||
| 391 | next = next->next; | ||
| 392 | |||
| 393 | if (prev) | ||
| 394 | prev->next = next; | ||
| 395 | else | ||
| 396 | fq->q.fragments = next; | ||
| 397 | |||
| 398 | fq->q.meat -= free_it->len; | ||
| 399 | frag_kfree_skb(fq->q.net, free_it, NULL); | ||
| 400 | } | ||
| 401 | } | ||
| 402 | 358 | ||
| 403 | FRAG6_CB(skb)->offset = offset; | 359 | FRAG6_CB(skb)->offset = offset; |
| 404 | 360 | ||
| 405 | /* Insert this fragment in the chain of fragments. */ | 361 | /* Insert this fragment in the chain of fragments. */ |
| 406 | skb->next = next; | 362 | skb->next = next; |
| 363 | if (!next) | ||
| 364 | fq->q.fragments_tail = skb; | ||
| 407 | if (prev) | 365 | if (prev) |
| 408 | prev->next = skb; | 366 | prev->next = skb; |
| 409 | else | 367 | else |
| @@ -435,6 +393,8 @@ static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, | |||
| 435 | write_unlock(&ip6_frags.lock); | 393 | write_unlock(&ip6_frags.lock); |
| 436 | return -1; | 394 | return -1; |
| 437 | 395 | ||
| 396 | discard_fq: | ||
| 397 | fq_kill(fq); | ||
| 438 | err: | 398 | err: |
| 439 | IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), | 399 | IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), |
| 440 | IPSTATS_MIB_REASMFAILS); | 400 | IPSTATS_MIB_REASMFAILS); |
| @@ -470,6 +430,8 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev, | |||
| 470 | goto out_oom; | 430 | goto out_oom; |
| 471 | 431 | ||
| 472 | fp->next = head->next; | 432 | fp->next = head->next; |
| 433 | if (!fp->next) | ||
| 434 | fq->q.fragments_tail = fp; | ||
| 473 | prev->next = fp; | 435 | prev->next = fp; |
| 474 | 436 | ||
| 475 | skb_morph(head, fq->q.fragments); | 437 | skb_morph(head, fq->q.fragments); |
| @@ -528,7 +490,6 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev, | |||
| 528 | skb_shinfo(head)->frag_list = head->next; | 490 | skb_shinfo(head)->frag_list = head->next; |
| 529 | skb_reset_transport_header(head); | 491 | skb_reset_transport_header(head); |
| 530 | skb_push(head, head->data - skb_network_header(head)); | 492 | skb_push(head, head->data - skb_network_header(head)); |
| 531 | atomic_sub(head->truesize, &fq->q.net->mem); | ||
| 532 | 493 | ||
| 533 | for (fp=head->next; fp; fp = fp->next) { | 494 | for (fp=head->next; fp; fp = fp->next) { |
| 534 | head->data_len += fp->len; | 495 | head->data_len += fp->len; |
| @@ -538,8 +499,8 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev, | |||
| 538 | else if (head->ip_summed == CHECKSUM_COMPLETE) | 499 | else if (head->ip_summed == CHECKSUM_COMPLETE) |
| 539 | head->csum = csum_add(head->csum, fp->csum); | 500 | head->csum = csum_add(head->csum, fp->csum); |
| 540 | head->truesize += fp->truesize; | 501 | head->truesize += fp->truesize; |
| 541 | atomic_sub(fp->truesize, &fq->q.net->mem); | ||
| 542 | } | 502 | } |
| 503 | atomic_sub(head->truesize, &fq->q.net->mem); | ||
| 543 | 504 | ||
| 544 | head->next = NULL; | 505 | head->next = NULL; |
| 545 | head->dev = dev; | 506 | head->dev = dev; |
| @@ -557,6 +518,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev, | |||
| 557 | IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMOKS); | 518 | IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMOKS); |
| 558 | rcu_read_unlock(); | 519 | rcu_read_unlock(); |
| 559 | fq->q.fragments = NULL; | 520 | fq->q.fragments = NULL; |
| 521 | fq->q.fragments_tail = NULL; | ||
| 560 | return 1; | 522 | return 1; |
| 561 | 523 | ||
| 562 | out_oversize: | 524 | out_oversize: |
| @@ -606,8 +568,8 @@ static int ipv6_frag_rcv(struct sk_buff *skb) | |||
| 606 | if (atomic_read(&net->ipv6.frags.mem) > net->ipv6.frags.high_thresh) | 568 | if (atomic_read(&net->ipv6.frags.mem) > net->ipv6.frags.high_thresh) |
| 607 | ip6_evictor(net, ip6_dst_idev(skb_dst(skb))); | 569 | ip6_evictor(net, ip6_dst_idev(skb_dst(skb))); |
| 608 | 570 | ||
| 609 | if ((fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr, | 571 | fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr); |
| 610 | ip6_dst_idev(skb_dst(skb)))) != NULL) { | 572 | if (fq != NULL) { |
| 611 | int ret; | 573 | int ret; |
| 612 | 574 | ||
| 613 | spin_lock(&fq->q.lock); | 575 | spin_lock(&fq->q.lock); |
| @@ -672,7 +634,7 @@ static struct ctl_table ip6_frags_ctl_table[] = { | |||
| 672 | { } | 634 | { } |
| 673 | }; | 635 | }; |
| 674 | 636 | ||
| 675 | static int ip6_frags_ns_sysctl_register(struct net *net) | 637 | static int __net_init ip6_frags_ns_sysctl_register(struct net *net) |
| 676 | { | 638 | { |
| 677 | struct ctl_table *table; | 639 | struct ctl_table *table; |
| 678 | struct ctl_table_header *hdr; | 640 | struct ctl_table_header *hdr; |
| @@ -702,7 +664,7 @@ err_alloc: | |||
| 702 | return -ENOMEM; | 664 | return -ENOMEM; |
| 703 | } | 665 | } |
| 704 | 666 | ||
| 705 | static void ip6_frags_ns_sysctl_unregister(struct net *net) | 667 | static void __net_exit ip6_frags_ns_sysctl_unregister(struct net *net) |
| 706 | { | 668 | { |
| 707 | struct ctl_table *table; | 669 | struct ctl_table *table; |
| 708 | 670 | ||
| @@ -745,10 +707,10 @@ static inline void ip6_frags_sysctl_unregister(void) | |||
| 745 | } | 707 | } |
| 746 | #endif | 708 | #endif |
| 747 | 709 | ||
| 748 | static int ipv6_frags_init_net(struct net *net) | 710 | static int __net_init ipv6_frags_init_net(struct net *net) |
| 749 | { | 711 | { |
| 750 | net->ipv6.frags.high_thresh = 256 * 1024; | 712 | net->ipv6.frags.high_thresh = IPV6_FRAG_HIGH_THRESH; |
| 751 | net->ipv6.frags.low_thresh = 192 * 1024; | 713 | net->ipv6.frags.low_thresh = IPV6_FRAG_LOW_THRESH; |
| 752 | net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT; | 714 | net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT; |
| 753 | 715 | ||
| 754 | inet_frags_init_net(&net->ipv6.frags); | 716 | inet_frags_init_net(&net->ipv6.frags); |
| @@ -756,7 +718,7 @@ static int ipv6_frags_init_net(struct net *net) | |||
| 756 | return ip6_frags_ns_sysctl_register(net); | 718 | return ip6_frags_ns_sysctl_register(net); |
| 757 | } | 719 | } |
| 758 | 720 | ||
| 759 | static void ipv6_frags_exit_net(struct net *net) | 721 | static void __net_exit ipv6_frags_exit_net(struct net *net) |
| 760 | { | 722 | { |
| 761 | ip6_frags_ns_sysctl_unregister(net); | 723 | ip6_frags_ns_sysctl_unregister(net); |
| 762 | inet_frags_exit_net(&net->ipv6.frags, &ip6_frags); | 724 | inet_frags_exit_net(&net->ipv6.frags, &ip6_frags); |
