diff options
Diffstat (limited to 'net/ipv6/reassembly.c')
-rw-r--r-- | net/ipv6/reassembly.c | 59 |
1 files changed, 26 insertions, 33 deletions
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index da5bd0ed83df..6d4292ff5854 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> |
@@ -72,6 +73,7 @@ struct frag_queue | |||
72 | struct inet_frag_queue q; | 73 | struct inet_frag_queue q; |
73 | 74 | ||
74 | __be32 id; /* fragment id */ | 75 | __be32 id; /* fragment id */ |
76 | u32 user; | ||
75 | struct in6_addr saddr; | 77 | struct in6_addr saddr; |
76 | struct in6_addr daddr; | 78 | struct in6_addr daddr; |
77 | 79 | ||
@@ -141,7 +143,7 @@ int ip6_frag_match(struct inet_frag_queue *q, void *a) | |||
141 | struct ip6_create_arg *arg = a; | 143 | struct ip6_create_arg *arg = a; |
142 | 144 | ||
143 | fq = container_of(q, struct frag_queue, q); | 145 | fq = container_of(q, struct frag_queue, q); |
144 | return (fq->id == arg->id && | 146 | return (fq->id == arg->id && fq->user == arg->user && |
145 | ipv6_addr_equal(&fq->saddr, arg->src) && | 147 | ipv6_addr_equal(&fq->saddr, arg->src) && |
146 | ipv6_addr_equal(&fq->daddr, arg->dst)); | 148 | ipv6_addr_equal(&fq->daddr, arg->dst)); |
147 | } | 149 | } |
@@ -163,6 +165,7 @@ void ip6_frag_init(struct inet_frag_queue *q, void *a) | |||
163 | struct ip6_create_arg *arg = a; | 165 | struct ip6_create_arg *arg = a; |
164 | 166 | ||
165 | fq->id = arg->id; | 167 | fq->id = arg->id; |
168 | fq->user = arg->user; | ||
166 | ipv6_addr_copy(&fq->saddr, arg->src); | 169 | ipv6_addr_copy(&fq->saddr, arg->src); |
167 | ipv6_addr_copy(&fq->daddr, arg->dst); | 170 | ipv6_addr_copy(&fq->daddr, arg->dst); |
168 | } | 171 | } |
@@ -208,18 +211,17 @@ static void ip6_frag_expire(unsigned long data) | |||
208 | fq_kill(fq); | 211 | fq_kill(fq); |
209 | 212 | ||
210 | net = container_of(fq->q.net, struct net, ipv6.frags); | 213 | net = container_of(fq->q.net, struct net, ipv6.frags); |
211 | dev = dev_get_by_index(net, fq->iif); | 214 | rcu_read_lock(); |
215 | dev = dev_get_by_index_rcu(net, fq->iif); | ||
212 | if (!dev) | 216 | if (!dev) |
213 | goto out; | 217 | goto out_rcu_unlock; |
214 | 218 | ||
215 | rcu_read_lock(); | ||
216 | IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT); | 219 | IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMTIMEOUT); |
217 | IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS); | 220 | IP6_INC_STATS_BH(net, __in6_dev_get(dev), IPSTATS_MIB_REASMFAILS); |
218 | rcu_read_unlock(); | ||
219 | 221 | ||
220 | /* Don't send error if the first segment did not arrive. */ | 222 | /* Don't send error if the first segment did not arrive. */ |
221 | if (!(fq->q.last_in & INET_FRAG_FIRST_IN) || !fq->q.fragments) | 223 | if (!(fq->q.last_in & INET_FRAG_FIRST_IN) || !fq->q.fragments) |
222 | goto out; | 224 | goto out_rcu_unlock; |
223 | 225 | ||
224 | /* | 226 | /* |
225 | But use as source device on which LAST ARRIVED | 227 | But use as source device on which LAST ARRIVED |
@@ -227,23 +229,23 @@ static void ip6_frag_expire(unsigned long data) | |||
227 | pointer directly, device might already disappeared. | 229 | pointer directly, device might already disappeared. |
228 | */ | 230 | */ |
229 | fq->q.fragments->dev = dev; | 231 | fq->q.fragments->dev = dev; |
230 | icmpv6_send(fq->q.fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0, dev); | 232 | icmpv6_send(fq->q.fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0); |
233 | out_rcu_unlock: | ||
234 | rcu_read_unlock(); | ||
231 | out: | 235 | out: |
232 | if (dev) | ||
233 | dev_put(dev); | ||
234 | spin_unlock(&fq->q.lock); | 236 | spin_unlock(&fq->q.lock); |
235 | fq_put(fq); | 237 | fq_put(fq); |
236 | } | 238 | } |
237 | 239 | ||
238 | static __inline__ struct frag_queue * | 240 | static __inline__ struct frag_queue * |
239 | fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst, | 241 | fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst) |
240 | struct inet6_dev *idev) | ||
241 | { | 242 | { |
242 | struct inet_frag_queue *q; | 243 | struct inet_frag_queue *q; |
243 | struct ip6_create_arg arg; | 244 | struct ip6_create_arg arg; |
244 | unsigned int hash; | 245 | unsigned int hash; |
245 | 246 | ||
246 | arg.id = id; | 247 | arg.id = id; |
248 | arg.user = IP6_DEFRAG_LOCAL_DELIVER; | ||
247 | arg.src = src; | 249 | arg.src = src; |
248 | arg.dst = dst; | 250 | arg.dst = dst; |
249 | 251 | ||
@@ -252,13 +254,9 @@ fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst, | |||
252 | 254 | ||
253 | q = inet_frag_find(&net->ipv6.frags, &ip6_frags, &arg, hash); | 255 | q = inet_frag_find(&net->ipv6.frags, &ip6_frags, &arg, hash); |
254 | if (q == NULL) | 256 | if (q == NULL) |
255 | goto oom; | 257 | return NULL; |
256 | 258 | ||
257 | return container_of(q, struct frag_queue, q); | 259 | return container_of(q, struct frag_queue, q); |
258 | |||
259 | oom: | ||
260 | IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_REASMFAILS); | ||
261 | return NULL; | ||
262 | } | 260 | } |
263 | 261 | ||
264 | static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, | 262 | static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, |
@@ -604,8 +602,8 @@ static int ipv6_frag_rcv(struct sk_buff *skb) | |||
604 | if (atomic_read(&net->ipv6.frags.mem) > net->ipv6.frags.high_thresh) | 602 | if (atomic_read(&net->ipv6.frags.mem) > net->ipv6.frags.high_thresh) |
605 | ip6_evictor(net, ip6_dst_idev(skb_dst(skb))); | 603 | ip6_evictor(net, ip6_dst_idev(skb_dst(skb))); |
606 | 604 | ||
607 | if ((fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr, | 605 | fq = fq_find(net, fhdr->identification, &hdr->saddr, &hdr->daddr); |
608 | ip6_dst_idev(skb_dst(skb)))) != NULL) { | 606 | if (fq != NULL) { |
609 | int ret; | 607 | int ret; |
610 | 608 | ||
611 | spin_lock(&fq->q.lock); | 609 | spin_lock(&fq->q.lock); |
@@ -636,7 +634,6 @@ static const struct inet6_protocol frag_protocol = | |||
636 | #ifdef CONFIG_SYSCTL | 634 | #ifdef CONFIG_SYSCTL |
637 | static struct ctl_table ip6_frags_ns_ctl_table[] = { | 635 | static struct ctl_table ip6_frags_ns_ctl_table[] = { |
638 | { | 636 | { |
639 | .ctl_name = NET_IPV6_IP6FRAG_HIGH_THRESH, | ||
640 | .procname = "ip6frag_high_thresh", | 637 | .procname = "ip6frag_high_thresh", |
641 | .data = &init_net.ipv6.frags.high_thresh, | 638 | .data = &init_net.ipv6.frags.high_thresh, |
642 | .maxlen = sizeof(int), | 639 | .maxlen = sizeof(int), |
@@ -644,7 +641,6 @@ static struct ctl_table ip6_frags_ns_ctl_table[] = { | |||
644 | .proc_handler = proc_dointvec | 641 | .proc_handler = proc_dointvec |
645 | }, | 642 | }, |
646 | { | 643 | { |
647 | .ctl_name = NET_IPV6_IP6FRAG_LOW_THRESH, | ||
648 | .procname = "ip6frag_low_thresh", | 644 | .procname = "ip6frag_low_thresh", |
649 | .data = &init_net.ipv6.frags.low_thresh, | 645 | .data = &init_net.ipv6.frags.low_thresh, |
650 | .maxlen = sizeof(int), | 646 | .maxlen = sizeof(int), |
@@ -652,37 +648,33 @@ static struct ctl_table ip6_frags_ns_ctl_table[] = { | |||
652 | .proc_handler = proc_dointvec | 648 | .proc_handler = proc_dointvec |
653 | }, | 649 | }, |
654 | { | 650 | { |
655 | .ctl_name = NET_IPV6_IP6FRAG_TIME, | ||
656 | .procname = "ip6frag_time", | 651 | .procname = "ip6frag_time", |
657 | .data = &init_net.ipv6.frags.timeout, | 652 | .data = &init_net.ipv6.frags.timeout, |
658 | .maxlen = sizeof(int), | 653 | .maxlen = sizeof(int), |
659 | .mode = 0644, | 654 | .mode = 0644, |
660 | .proc_handler = proc_dointvec_jiffies, | 655 | .proc_handler = proc_dointvec_jiffies, |
661 | .strategy = sysctl_jiffies, | ||
662 | }, | 656 | }, |
663 | { } | 657 | { } |
664 | }; | 658 | }; |
665 | 659 | ||
666 | static struct ctl_table ip6_frags_ctl_table[] = { | 660 | static struct ctl_table ip6_frags_ctl_table[] = { |
667 | { | 661 | { |
668 | .ctl_name = NET_IPV6_IP6FRAG_SECRET_INTERVAL, | ||
669 | .procname = "ip6frag_secret_interval", | 662 | .procname = "ip6frag_secret_interval", |
670 | .data = &ip6_frags.secret_interval, | 663 | .data = &ip6_frags.secret_interval, |
671 | .maxlen = sizeof(int), | 664 | .maxlen = sizeof(int), |
672 | .mode = 0644, | 665 | .mode = 0644, |
673 | .proc_handler = proc_dointvec_jiffies, | 666 | .proc_handler = proc_dointvec_jiffies, |
674 | .strategy = sysctl_jiffies | ||
675 | }, | 667 | }, |
676 | { } | 668 | { } |
677 | }; | 669 | }; |
678 | 670 | ||
679 | static int ip6_frags_ns_sysctl_register(struct net *net) | 671 | static int __net_init ip6_frags_ns_sysctl_register(struct net *net) |
680 | { | 672 | { |
681 | struct ctl_table *table; | 673 | struct ctl_table *table; |
682 | struct ctl_table_header *hdr; | 674 | struct ctl_table_header *hdr; |
683 | 675 | ||
684 | table = ip6_frags_ns_ctl_table; | 676 | table = ip6_frags_ns_ctl_table; |
685 | if (net != &init_net) { | 677 | if (!net_eq(net, &init_net)) { |
686 | table = kmemdup(table, sizeof(ip6_frags_ns_ctl_table), GFP_KERNEL); | 678 | table = kmemdup(table, sizeof(ip6_frags_ns_ctl_table), GFP_KERNEL); |
687 | if (table == NULL) | 679 | if (table == NULL) |
688 | goto err_alloc; | 680 | goto err_alloc; |
@@ -700,19 +692,20 @@ static int ip6_frags_ns_sysctl_register(struct net *net) | |||
700 | return 0; | 692 | return 0; |
701 | 693 | ||
702 | err_reg: | 694 | err_reg: |
703 | if (net != &init_net) | 695 | if (!net_eq(net, &init_net)) |
704 | kfree(table); | 696 | kfree(table); |
705 | err_alloc: | 697 | err_alloc: |
706 | return -ENOMEM; | 698 | return -ENOMEM; |
707 | } | 699 | } |
708 | 700 | ||
709 | static void ip6_frags_ns_sysctl_unregister(struct net *net) | 701 | static void __net_exit ip6_frags_ns_sysctl_unregister(struct net *net) |
710 | { | 702 | { |
711 | struct ctl_table *table; | 703 | struct ctl_table *table; |
712 | 704 | ||
713 | table = net->ipv6.sysctl.frags_hdr->ctl_table_arg; | 705 | table = net->ipv6.sysctl.frags_hdr->ctl_table_arg; |
714 | unregister_net_sysctl_table(net->ipv6.sysctl.frags_hdr); | 706 | unregister_net_sysctl_table(net->ipv6.sysctl.frags_hdr); |
715 | kfree(table); | 707 | if (!net_eq(net, &init_net)) |
708 | kfree(table); | ||
716 | } | 709 | } |
717 | 710 | ||
718 | static struct ctl_table_header *ip6_ctl_header; | 711 | static struct ctl_table_header *ip6_ctl_header; |
@@ -748,10 +741,10 @@ static inline void ip6_frags_sysctl_unregister(void) | |||
748 | } | 741 | } |
749 | #endif | 742 | #endif |
750 | 743 | ||
751 | static int ipv6_frags_init_net(struct net *net) | 744 | static int __net_init ipv6_frags_init_net(struct net *net) |
752 | { | 745 | { |
753 | net->ipv6.frags.high_thresh = 256 * 1024; | 746 | net->ipv6.frags.high_thresh = IPV6_FRAG_HIGH_THRESH; |
754 | net->ipv6.frags.low_thresh = 192 * 1024; | 747 | net->ipv6.frags.low_thresh = IPV6_FRAG_LOW_THRESH; |
755 | net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT; | 748 | net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT; |
756 | 749 | ||
757 | inet_frags_init_net(&net->ipv6.frags); | 750 | inet_frags_init_net(&net->ipv6.frags); |
@@ -759,7 +752,7 @@ static int ipv6_frags_init_net(struct net *net) | |||
759 | return ip6_frags_ns_sysctl_register(net); | 752 | return ip6_frags_ns_sysctl_register(net); |
760 | } | 753 | } |
761 | 754 | ||
762 | static void ipv6_frags_exit_net(struct net *net) | 755 | static void __net_exit ipv6_frags_exit_net(struct net *net) |
763 | { | 756 | { |
764 | ip6_frags_ns_sysctl_unregister(net); | 757 | ip6_frags_ns_sysctl_unregister(net); |
765 | inet_frags_exit_net(&net->ipv6.frags, &ip6_frags); | 758 | inet_frags_exit_net(&net->ipv6.frags, &ip6_frags); |