diff options
Diffstat (limited to 'net/ipv6/reassembly.c')
-rw-r--r-- | net/ipv6/reassembly.c | 87 |
1 files changed, 50 insertions, 37 deletions
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index f39bbedd1327..6f9a9046510f 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c | |||
@@ -47,6 +47,7 @@ | |||
47 | #include <net/snmp.h> | 47 | #include <net/snmp.h> |
48 | 48 | ||
49 | #include <net/ipv6.h> | 49 | #include <net/ipv6.h> |
50 | #include <net/ip6_route.h> | ||
50 | #include <net/protocol.h> | 51 | #include <net/protocol.h> |
51 | #include <net/transp_v6.h> | 52 | #include <net/transp_v6.h> |
52 | #include <net/rawv6.h> | 53 | #include <net/rawv6.h> |
@@ -76,7 +77,7 @@ struct frag_queue | |||
76 | struct hlist_node list; | 77 | struct hlist_node list; |
77 | struct list_head lru_list; /* lru list member */ | 78 | struct list_head lru_list; /* lru list member */ |
78 | 79 | ||
79 | __u32 id; /* fragment id */ | 80 | __be32 id; /* fragment id */ |
80 | struct in6_addr saddr; | 81 | struct in6_addr saddr; |
81 | struct in6_addr daddr; | 82 | struct in6_addr daddr; |
82 | 83 | ||
@@ -124,28 +125,28 @@ static __inline__ void fq_unlink(struct frag_queue *fq) | |||
124 | * callers should be careful not to use the hash value outside the ipfrag_lock | 125 | * callers should be careful not to use the hash value outside the ipfrag_lock |
125 | * as doing so could race with ipfrag_hash_rnd being recalculated. | 126 | * as doing so could race with ipfrag_hash_rnd being recalculated. |
126 | */ | 127 | */ |
127 | static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr, | 128 | static unsigned int ip6qhashfn(__be32 id, struct in6_addr *saddr, |
128 | struct in6_addr *daddr) | 129 | struct in6_addr *daddr) |
129 | { | 130 | { |
130 | u32 a, b, c; | 131 | u32 a, b, c; |
131 | 132 | ||
132 | a = saddr->s6_addr32[0]; | 133 | a = (__force u32)saddr->s6_addr32[0]; |
133 | b = saddr->s6_addr32[1]; | 134 | b = (__force u32)saddr->s6_addr32[1]; |
134 | c = saddr->s6_addr32[2]; | 135 | c = (__force u32)saddr->s6_addr32[2]; |
135 | 136 | ||
136 | a += JHASH_GOLDEN_RATIO; | 137 | a += JHASH_GOLDEN_RATIO; |
137 | b += JHASH_GOLDEN_RATIO; | 138 | b += JHASH_GOLDEN_RATIO; |
138 | c += ip6_frag_hash_rnd; | 139 | c += ip6_frag_hash_rnd; |
139 | __jhash_mix(a, b, c); | 140 | __jhash_mix(a, b, c); |
140 | 141 | ||
141 | a += saddr->s6_addr32[3]; | 142 | a += (__force u32)saddr->s6_addr32[3]; |
142 | b += daddr->s6_addr32[0]; | 143 | b += (__force u32)daddr->s6_addr32[0]; |
143 | c += daddr->s6_addr32[1]; | 144 | c += (__force u32)daddr->s6_addr32[1]; |
144 | __jhash_mix(a, b, c); | 145 | __jhash_mix(a, b, c); |
145 | 146 | ||
146 | a += daddr->s6_addr32[2]; | 147 | a += (__force u32)daddr->s6_addr32[2]; |
147 | b += daddr->s6_addr32[3]; | 148 | b += (__force u32)daddr->s6_addr32[3]; |
148 | c += id; | 149 | c += (__force u32)id; |
149 | __jhash_mix(a, b, c); | 150 | __jhash_mix(a, b, c); |
150 | 151 | ||
151 | return c & (IP6Q_HASHSZ - 1); | 152 | return c & (IP6Q_HASHSZ - 1); |
@@ -257,7 +258,7 @@ static __inline__ void fq_kill(struct frag_queue *fq) | |||
257 | } | 258 | } |
258 | } | 259 | } |
259 | 260 | ||
260 | static void ip6_evictor(void) | 261 | static void ip6_evictor(struct inet6_dev *idev) |
261 | { | 262 | { |
262 | struct frag_queue *fq; | 263 | struct frag_queue *fq; |
263 | struct list_head *tmp; | 264 | struct list_head *tmp; |
@@ -284,14 +285,14 @@ static void ip6_evictor(void) | |||
284 | spin_unlock(&fq->lock); | 285 | spin_unlock(&fq->lock); |
285 | 286 | ||
286 | fq_put(fq, &work); | 287 | fq_put(fq, &work); |
287 | IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); | 288 | IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMFAILS); |
288 | } | 289 | } |
289 | } | 290 | } |
290 | 291 | ||
291 | static void ip6_frag_expire(unsigned long data) | 292 | static void ip6_frag_expire(unsigned long data) |
292 | { | 293 | { |
293 | struct frag_queue *fq = (struct frag_queue *) data; | 294 | struct frag_queue *fq = (struct frag_queue *) data; |
294 | struct net_device *dev; | 295 | struct net_device *dev = NULL; |
295 | 296 | ||
296 | spin_lock(&fq->lock); | 297 | spin_lock(&fq->lock); |
297 | 298 | ||
@@ -300,17 +301,19 @@ static void ip6_frag_expire(unsigned long data) | |||
300 | 301 | ||
301 | fq_kill(fq); | 302 | fq_kill(fq); |
302 | 303 | ||
303 | IP6_INC_STATS_BH(IPSTATS_MIB_REASMTIMEOUT); | 304 | dev = dev_get_by_index(fq->iif); |
304 | IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); | 305 | if (!dev) |
306 | goto out; | ||
307 | |||
308 | rcu_read_lock(); | ||
309 | IP6_INC_STATS_BH(__in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT); | ||
310 | IP6_INC_STATS_BH(__in6_dev_get(dev), IPSTATS_MIB_REASMFAILS); | ||
311 | rcu_read_unlock(); | ||
305 | 312 | ||
306 | /* Don't send error if the first segment did not arrive. */ | 313 | /* Don't send error if the first segment did not arrive. */ |
307 | if (!(fq->last_in&FIRST_IN) || !fq->fragments) | 314 | if (!(fq->last_in&FIRST_IN) || !fq->fragments) |
308 | goto out; | 315 | goto out; |
309 | 316 | ||
310 | dev = dev_get_by_index(fq->iif); | ||
311 | if (!dev) | ||
312 | goto out; | ||
313 | |||
314 | /* | 317 | /* |
315 | But use as source device on which LAST ARRIVED | 318 | But use as source device on which LAST ARRIVED |
316 | segment was received. And do not use fq->dev | 319 | segment was received. And do not use fq->dev |
@@ -318,8 +321,9 @@ static void ip6_frag_expire(unsigned long data) | |||
318 | */ | 321 | */ |
319 | fq->fragments->dev = dev; | 322 | fq->fragments->dev = dev; |
320 | icmpv6_send(fq->fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0, dev); | 323 | icmpv6_send(fq->fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0, dev); |
321 | dev_put(dev); | ||
322 | out: | 324 | out: |
325 | if (dev) | ||
326 | dev_put(dev); | ||
323 | spin_unlock(&fq->lock); | 327 | spin_unlock(&fq->lock); |
324 | fq_put(fq, NULL); | 328 | fq_put(fq, NULL); |
325 | } | 329 | } |
@@ -366,7 +370,8 @@ static struct frag_queue *ip6_frag_intern(struct frag_queue *fq_in) | |||
366 | 370 | ||
367 | 371 | ||
368 | static struct frag_queue * | 372 | static struct frag_queue * |
369 | ip6_frag_create(u32 id, struct in6_addr *src, struct in6_addr *dst) | 373 | ip6_frag_create(__be32 id, struct in6_addr *src, struct in6_addr *dst, |
374 | struct inet6_dev *idev) | ||
370 | { | 375 | { |
371 | struct frag_queue *fq; | 376 | struct frag_queue *fq; |
372 | 377 | ||
@@ -386,12 +391,13 @@ ip6_frag_create(u32 id, struct in6_addr *src, struct in6_addr *dst) | |||
386 | return ip6_frag_intern(fq); | 391 | return ip6_frag_intern(fq); |
387 | 392 | ||
388 | oom: | 393 | oom: |
389 | IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); | 394 | IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMFAILS); |
390 | return NULL; | 395 | return NULL; |
391 | } | 396 | } |
392 | 397 | ||
393 | static __inline__ struct frag_queue * | 398 | static __inline__ struct frag_queue * |
394 | fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst) | 399 | fq_find(__be32 id, struct in6_addr *src, struct in6_addr *dst, |
400 | struct inet6_dev *idev) | ||
395 | { | 401 | { |
396 | struct frag_queue *fq; | 402 | struct frag_queue *fq; |
397 | struct hlist_node *n; | 403 | struct hlist_node *n; |
@@ -410,7 +416,7 @@ fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst) | |||
410 | } | 416 | } |
411 | read_unlock(&ip6_frag_lock); | 417 | read_unlock(&ip6_frag_lock); |
412 | 418 | ||
413 | return ip6_frag_create(id, src, dst); | 419 | return ip6_frag_create(id, src, dst, idev); |
414 | } | 420 | } |
415 | 421 | ||
416 | 422 | ||
@@ -428,7 +434,8 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, | |||
428 | ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1))); | 434 | ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1))); |
429 | 435 | ||
430 | if ((unsigned int)end > IPV6_MAXPLEN) { | 436 | if ((unsigned int)end > IPV6_MAXPLEN) { |
431 | IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); | 437 | IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), |
438 | IPSTATS_MIB_INHDRERRORS); | ||
432 | icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off - skb->nh.raw); | 439 | icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off - skb->nh.raw); |
433 | return; | 440 | return; |
434 | } | 441 | } |
@@ -455,7 +462,8 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, | |||
455 | /* RFC2460 says always send parameter problem in | 462 | /* RFC2460 says always send parameter problem in |
456 | * this case. -DaveM | 463 | * this case. -DaveM |
457 | */ | 464 | */ |
458 | IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); | 465 | IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), |
466 | IPSTATS_MIB_INHDRERRORS); | ||
459 | icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, | 467 | icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, |
460 | offsetof(struct ipv6hdr, payload_len)); | 468 | offsetof(struct ipv6hdr, payload_len)); |
461 | return; | 469 | return; |
@@ -571,7 +579,7 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, | |||
571 | return; | 579 | return; |
572 | 580 | ||
573 | err: | 581 | err: |
574 | IP6_INC_STATS(IPSTATS_MIB_REASMFAILS); | 582 | IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMFAILS); |
575 | kfree_skb(skb); | 583 | kfree_skb(skb); |
576 | } | 584 | } |
577 | 585 | ||
@@ -665,7 +673,9 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in, | |||
665 | if (head->ip_summed == CHECKSUM_COMPLETE) | 673 | if (head->ip_summed == CHECKSUM_COMPLETE) |
666 | head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum); | 674 | head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum); |
667 | 675 | ||
668 | IP6_INC_STATS_BH(IPSTATS_MIB_REASMOKS); | 676 | rcu_read_lock(); |
677 | IP6_INC_STATS_BH(__in6_dev_get(dev), IPSTATS_MIB_REASMOKS); | ||
678 | rcu_read_unlock(); | ||
669 | fq->fragments = NULL; | 679 | fq->fragments = NULL; |
670 | return 1; | 680 | return 1; |
671 | 681 | ||
@@ -677,7 +687,9 @@ out_oom: | |||
677 | if (net_ratelimit()) | 687 | if (net_ratelimit()) |
678 | printk(KERN_DEBUG "ip6_frag_reasm: no memory for reassembly\n"); | 688 | printk(KERN_DEBUG "ip6_frag_reasm: no memory for reassembly\n"); |
679 | out_fail: | 689 | out_fail: |
680 | IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); | 690 | rcu_read_lock(); |
691 | IP6_INC_STATS_BH(__in6_dev_get(dev), IPSTATS_MIB_REASMFAILS); | ||
692 | rcu_read_unlock(); | ||
681 | return -1; | 693 | return -1; |
682 | } | 694 | } |
683 | 695 | ||
@@ -691,16 +703,16 @@ static int ipv6_frag_rcv(struct sk_buff **skbp) | |||
691 | 703 | ||
692 | hdr = skb->nh.ipv6h; | 704 | hdr = skb->nh.ipv6h; |
693 | 705 | ||
694 | IP6_INC_STATS_BH(IPSTATS_MIB_REASMREQDS); | 706 | IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMREQDS); |
695 | 707 | ||
696 | /* Jumbo payload inhibits frag. header */ | 708 | /* Jumbo payload inhibits frag. header */ |
697 | if (hdr->payload_len==0) { | 709 | if (hdr->payload_len==0) { |
698 | IP6_INC_STATS(IPSTATS_MIB_INHDRERRORS); | 710 | IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); |
699 | icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw); | 711 | icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw); |
700 | return -1; | 712 | return -1; |
701 | } | 713 | } |
702 | if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+sizeof(struct frag_hdr))) { | 714 | if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+sizeof(struct frag_hdr))) { |
703 | IP6_INC_STATS(IPSTATS_MIB_INHDRERRORS); | 715 | IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); |
704 | icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw); | 716 | icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw); |
705 | return -1; | 717 | return -1; |
706 | } | 718 | } |
@@ -711,16 +723,17 @@ static int ipv6_frag_rcv(struct sk_buff **skbp) | |||
711 | if (!(fhdr->frag_off & htons(0xFFF9))) { | 723 | if (!(fhdr->frag_off & htons(0xFFF9))) { |
712 | /* It is not a fragmented frame */ | 724 | /* It is not a fragmented frame */ |
713 | skb->h.raw += sizeof(struct frag_hdr); | 725 | skb->h.raw += sizeof(struct frag_hdr); |
714 | IP6_INC_STATS_BH(IPSTATS_MIB_REASMOKS); | 726 | IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMOKS); |
715 | 727 | ||
716 | IP6CB(skb)->nhoff = (u8*)fhdr - skb->nh.raw; | 728 | IP6CB(skb)->nhoff = (u8*)fhdr - skb->nh.raw; |
717 | return 1; | 729 | return 1; |
718 | } | 730 | } |
719 | 731 | ||
720 | if (atomic_read(&ip6_frag_mem) > sysctl_ip6frag_high_thresh) | 732 | if (atomic_read(&ip6_frag_mem) > sysctl_ip6frag_high_thresh) |
721 | ip6_evictor(); | 733 | ip6_evictor(ip6_dst_idev(skb->dst)); |
722 | 734 | ||
723 | if ((fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr)) != NULL) { | 735 | if ((fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr, |
736 | ip6_dst_idev(skb->dst))) != NULL) { | ||
724 | int ret = -1; | 737 | int ret = -1; |
725 | 738 | ||
726 | spin_lock(&fq->lock); | 739 | spin_lock(&fq->lock); |
@@ -736,7 +749,7 @@ static int ipv6_frag_rcv(struct sk_buff **skbp) | |||
736 | return ret; | 749 | return ret; |
737 | } | 750 | } |
738 | 751 | ||
739 | IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); | 752 | IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMFAILS); |
740 | kfree_skb(skb); | 753 | kfree_skb(skb); |
741 | return -1; | 754 | return -1; |
742 | } | 755 | } |