aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/reassembly.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/reassembly.c')
-rw-r--r--net/ipv6/reassembly.c59
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);
233out_rcu_unlock:
234 rcu_read_unlock();
231out: 235out:
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
238static __inline__ struct frag_queue * 240static __inline__ struct frag_queue *
239fq_find(struct net *net, __be32 id, struct in6_addr *src, struct in6_addr *dst, 241fq_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
259oom:
260 IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_REASMFAILS);
261 return NULL;
262} 260}
263 261
264static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, 262static 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
637static struct ctl_table ip6_frags_ns_ctl_table[] = { 635static 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
666static struct ctl_table ip6_frags_ctl_table[] = { 660static 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
679static int ip6_frags_ns_sysctl_register(struct net *net) 671static 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
702err_reg: 694err_reg:
703 if (net != &init_net) 695 if (!net_eq(net, &init_net))
704 kfree(table); 696 kfree(table);
705err_alloc: 697err_alloc:
706 return -ENOMEM; 698 return -ENOMEM;
707} 699}
708 700
709static void ip6_frags_ns_sysctl_unregister(struct net *net) 701static 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
718static struct ctl_table_header *ip6_ctl_header; 711static 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
751static int ipv6_frags_init_net(struct net *net) 744static 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
762static void ipv6_frags_exit_net(struct net *net) 755static 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);