aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/netfilter/nf_conntrack_reasm.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/netfilter/nf_conntrack_reasm.c')
-rw-r--r--net/ipv6/netfilter/nf_conntrack_reasm.c218
1 files changed, 123 insertions, 95 deletions
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index c9c78c2e666..18bd9bbbd1c 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -57,41 +57,27 @@ struct nf_ct_frag6_skb_cb
57 57
58#define NFCT_FRAG6_CB(skb) ((struct nf_ct_frag6_skb_cb*)((skb)->cb)) 58#define NFCT_FRAG6_CB(skb) ((struct nf_ct_frag6_skb_cb*)((skb)->cb))
59 59
60struct nf_ct_frag6_queue
61{
62 struct inet_frag_queue q;
63
64 __be32 id; /* fragment id */
65 u32 user;
66 struct in6_addr saddr;
67 struct in6_addr daddr;
68
69 unsigned int csum;
70 __u16 nhoffset;
71};
72
73static struct inet_frags nf_frags; 60static struct inet_frags nf_frags;
74static struct netns_frags nf_init_frags;
75 61
76#ifdef CONFIG_SYSCTL 62#ifdef CONFIG_SYSCTL
77static struct ctl_table nf_ct_frag6_sysctl_table[] = { 63static struct ctl_table nf_ct_frag6_sysctl_table[] = {
78 { 64 {
79 .procname = "nf_conntrack_frag6_timeout", 65 .procname = "nf_conntrack_frag6_timeout",
80 .data = &nf_init_frags.timeout, 66 .data = &init_net.nf_frag.frags.timeout,
81 .maxlen = sizeof(unsigned int), 67 .maxlen = sizeof(unsigned int),
82 .mode = 0644, 68 .mode = 0644,
83 .proc_handler = proc_dointvec_jiffies, 69 .proc_handler = proc_dointvec_jiffies,
84 }, 70 },
85 { 71 {
86 .procname = "nf_conntrack_frag6_low_thresh", 72 .procname = "nf_conntrack_frag6_low_thresh",
87 .data = &nf_init_frags.low_thresh, 73 .data = &init_net.nf_frag.frags.low_thresh,
88 .maxlen = sizeof(unsigned int), 74 .maxlen = sizeof(unsigned int),
89 .mode = 0644, 75 .mode = 0644,
90 .proc_handler = proc_dointvec, 76 .proc_handler = proc_dointvec,
91 }, 77 },
92 { 78 {
93 .procname = "nf_conntrack_frag6_high_thresh", 79 .procname = "nf_conntrack_frag6_high_thresh",
94 .data = &nf_init_frags.high_thresh, 80 .data = &init_net.nf_frag.frags.high_thresh,
95 .maxlen = sizeof(unsigned int), 81 .maxlen = sizeof(unsigned int),
96 .mode = 0644, 82 .mode = 0644,
97 .proc_handler = proc_dointvec, 83 .proc_handler = proc_dointvec,
@@ -99,68 +85,86 @@ static struct ctl_table nf_ct_frag6_sysctl_table[] = {
99 { } 85 { }
100}; 86};
101 87
102static struct ctl_table_header *nf_ct_frag6_sysctl_header; 88static int __net_init nf_ct_frag6_sysctl_register(struct net *net)
103#endif
104
105static unsigned int nf_hashfn(struct inet_frag_queue *q)
106{ 89{
107 const struct nf_ct_frag6_queue *nq; 90 struct ctl_table *table;
91 struct ctl_table_header *hdr;
92
93 table = nf_ct_frag6_sysctl_table;
94 if (!net_eq(net, &init_net)) {
95 table = kmemdup(table, sizeof(nf_ct_frag6_sysctl_table),
96 GFP_KERNEL);
97 if (table == NULL)
98 goto err_alloc;
99
100 table[0].data = &net->ipv6.frags.high_thresh;
101 table[1].data = &net->ipv6.frags.low_thresh;
102 table[2].data = &net->ipv6.frags.timeout;
103 }
108 104
109 nq = container_of(q, struct nf_ct_frag6_queue, q); 105 hdr = register_net_sysctl(net, "net/netfilter", table);
110 return inet6_hash_frag(nq->id, &nq->saddr, &nq->daddr, nf_frags.rnd); 106 if (hdr == NULL)
107 goto err_reg;
108
109 net->nf_frag.sysctl.frags_hdr = hdr;
110 return 0;
111
112err_reg:
113 if (!net_eq(net, &init_net))
114 kfree(table);
115err_alloc:
116 return -ENOMEM;
111} 117}
112 118
113static void nf_skb_free(struct sk_buff *skb) 119static void __net_exit nf_ct_frags6_sysctl_unregister(struct net *net)
114{ 120{
115 if (NFCT_FRAG6_CB(skb)->orig) 121 struct ctl_table *table;
116 kfree_skb(NFCT_FRAG6_CB(skb)->orig);
117}
118 122
119/* Destruction primitives. */ 123 table = net->nf_frag.sysctl.frags_hdr->ctl_table_arg;
124 unregister_net_sysctl_table(net->nf_frag.sysctl.frags_hdr);
125 if (!net_eq(net, &init_net))
126 kfree(table);
127}
120 128
121static __inline__ void fq_put(struct nf_ct_frag6_queue *fq) 129#else
130static int __net_init nf_ct_frag6_sysctl_register(struct net *net)
122{ 131{
123 inet_frag_put(&fq->q, &nf_frags); 132 return 0;
124} 133}
134static void __net_exit nf_ct_frags6_sysctl_unregister(struct net *net)
135{
136}
137#endif
125 138
126/* Kill fq entry. It is not destroyed immediately, 139static unsigned int nf_hashfn(struct inet_frag_queue *q)
127 * because caller (and someone more) holds reference count.
128 */
129static __inline__ void fq_kill(struct nf_ct_frag6_queue *fq)
130{ 140{
131 inet_frag_kill(&fq->q, &nf_frags); 141 const struct frag_queue *nq;
142
143 nq = container_of(q, struct frag_queue, q);
144 return inet6_hash_frag(nq->id, &nq->saddr, &nq->daddr, nf_frags.rnd);
132} 145}
133 146
134static void nf_ct_frag6_evictor(void) 147static void nf_skb_free(struct sk_buff *skb)
135{ 148{
136 local_bh_disable(); 149 if (NFCT_FRAG6_CB(skb)->orig)
137 inet_frag_evictor(&nf_init_frags, &nf_frags); 150 kfree_skb(NFCT_FRAG6_CB(skb)->orig);
138 local_bh_enable();
139} 151}
140 152
141static void nf_ct_frag6_expire(unsigned long data) 153static void nf_ct_frag6_expire(unsigned long data)
142{ 154{
143 struct nf_ct_frag6_queue *fq; 155 struct frag_queue *fq;
144 156 struct net *net;
145 fq = container_of((struct inet_frag_queue *)data,
146 struct nf_ct_frag6_queue, q);
147
148 spin_lock(&fq->q.lock);
149 157
150 if (fq->q.last_in & INET_FRAG_COMPLETE) 158 fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q);
151 goto out; 159 net = container_of(fq->q.net, struct net, nf_frag.frags);
152 160
153 fq_kill(fq); 161 ip6_expire_frag_queue(net, fq, &nf_frags);
154
155out:
156 spin_unlock(&fq->q.lock);
157 fq_put(fq);
158} 162}
159 163
160/* Creation primitives. */ 164/* Creation primitives. */
161 165static inline struct frag_queue *fq_find(struct net *net, __be32 id,
162static __inline__ struct nf_ct_frag6_queue * 166 u32 user, struct in6_addr *src,
163fq_find(__be32 id, u32 user, struct in6_addr *src, struct in6_addr *dst) 167 struct in6_addr *dst)
164{ 168{
165 struct inet_frag_queue *q; 169 struct inet_frag_queue *q;
166 struct ip6_create_arg arg; 170 struct ip6_create_arg arg;
@@ -174,22 +178,23 @@ fq_find(__be32 id, u32 user, struct in6_addr *src, struct in6_addr *dst)
174 read_lock_bh(&nf_frags.lock); 178 read_lock_bh(&nf_frags.lock);
175 hash = inet6_hash_frag(id, src, dst, nf_frags.rnd); 179 hash = inet6_hash_frag(id, src, dst, nf_frags.rnd);
176 180
177 q = inet_frag_find(&nf_init_frags, &nf_frags, &arg, hash); 181 q = inet_frag_find(&net->nf_frag.frags, &nf_frags, &arg, hash);
178 local_bh_enable(); 182 local_bh_enable();
179 if (q == NULL) 183 if (q == NULL)
180 goto oom; 184 goto oom;
181 185
182 return container_of(q, struct nf_ct_frag6_queue, q); 186 return container_of(q, struct frag_queue, q);
183 187
184oom: 188oom:
185 return NULL; 189 return NULL;
186} 190}
187 191
188 192
189static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, 193static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
190 const struct frag_hdr *fhdr, int nhoff) 194 const struct frag_hdr *fhdr, int nhoff)
191{ 195{
192 struct sk_buff *prev, *next; 196 struct sk_buff *prev, *next;
197 unsigned int payload_len;
193 int offset, end; 198 int offset, end;
194 199
195 if (fq->q.last_in & INET_FRAG_COMPLETE) { 200 if (fq->q.last_in & INET_FRAG_COMPLETE) {
@@ -197,8 +202,10 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
197 goto err; 202 goto err;
198 } 203 }
199 204
205 payload_len = ntohs(ipv6_hdr(skb)->payload_len);
206
200 offset = ntohs(fhdr->frag_off) & ~0x7; 207 offset = ntohs(fhdr->frag_off) & ~0x7;
201 end = offset + (ntohs(ipv6_hdr(skb)->payload_len) - 208 end = offset + (payload_len -
202 ((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1))); 209 ((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1)));
203 210
204 if ((unsigned int)end > IPV6_MAXPLEN) { 211 if ((unsigned int)end > IPV6_MAXPLEN) {
@@ -307,7 +314,9 @@ found:
307 skb->dev = NULL; 314 skb->dev = NULL;
308 fq->q.stamp = skb->tstamp; 315 fq->q.stamp = skb->tstamp;
309 fq->q.meat += skb->len; 316 fq->q.meat += skb->len;
310 atomic_add(skb->truesize, &nf_init_frags.mem); 317 if (payload_len > fq->q.max_size)
318 fq->q.max_size = payload_len;
319 atomic_add(skb->truesize, &fq->q.net->mem);
311 320
312 /* The first fragment. 321 /* The first fragment.
313 * nhoffset is obtained from the first fragment, of course. 322 * nhoffset is obtained from the first fragment, of course.
@@ -317,12 +326,12 @@ found:
317 fq->q.last_in |= INET_FRAG_FIRST_IN; 326 fq->q.last_in |= INET_FRAG_FIRST_IN;
318 } 327 }
319 write_lock(&nf_frags.lock); 328 write_lock(&nf_frags.lock);
320 list_move_tail(&fq->q.lru_list, &nf_init_frags.lru_list); 329 list_move_tail(&fq->q.lru_list, &fq->q.net->lru_list);
321 write_unlock(&nf_frags.lock); 330 write_unlock(&nf_frags.lock);
322 return 0; 331 return 0;
323 332
324discard_fq: 333discard_fq:
325 fq_kill(fq); 334 inet_frag_kill(&fq->q, &nf_frags);
326err: 335err:
327 return -1; 336 return -1;
328} 337}
@@ -337,12 +346,12 @@ err:
337 * the last and the first frames arrived and all the bits are here. 346 * the last and the first frames arrived and all the bits are here.
338 */ 347 */
339static struct sk_buff * 348static struct sk_buff *
340nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev) 349nf_ct_frag6_reasm(struct frag_queue *fq, struct net_device *dev)
341{ 350{
342 struct sk_buff *fp, *op, *head = fq->q.fragments; 351 struct sk_buff *fp, *op, *head = fq->q.fragments;
343 int payload_len; 352 int payload_len;
344 353
345 fq_kill(fq); 354 inet_frag_kill(&fq->q, &nf_frags);
346 355
347 WARN_ON(head == NULL); 356 WARN_ON(head == NULL);
348 WARN_ON(NFCT_FRAG6_CB(head)->offset != 0); 357 WARN_ON(NFCT_FRAG6_CB(head)->offset != 0);
@@ -386,7 +395,7 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
386 clone->ip_summed = head->ip_summed; 395 clone->ip_summed = head->ip_summed;
387 396
388 NFCT_FRAG6_CB(clone)->orig = NULL; 397 NFCT_FRAG6_CB(clone)->orig = NULL;
389 atomic_add(clone->truesize, &nf_init_frags.mem); 398 atomic_add(clone->truesize, &fq->q.net->mem);
390 } 399 }
391 400
392 /* We have to remove fragment header from datagram and to relocate 401 /* We have to remove fragment header from datagram and to relocate
@@ -410,12 +419,14 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev)
410 head->csum = csum_add(head->csum, fp->csum); 419 head->csum = csum_add(head->csum, fp->csum);
411 head->truesize += fp->truesize; 420 head->truesize += fp->truesize;
412 } 421 }
413 atomic_sub(head->truesize, &nf_init_frags.mem); 422 atomic_sub(head->truesize, &fq->q.net->mem);
414 423
424 head->local_df = 1;
415 head->next = NULL; 425 head->next = NULL;
416 head->dev = dev; 426 head->dev = dev;
417 head->tstamp = fq->q.stamp; 427 head->tstamp = fq->q.stamp;
418 ipv6_hdr(head)->payload_len = htons(payload_len); 428 ipv6_hdr(head)->payload_len = htons(payload_len);
429 IP6CB(head)->frag_max_size = sizeof(struct ipv6hdr) + fq->q.max_size;
419 430
420 /* Yes, and fold redundant checksum back. 8) */ 431 /* Yes, and fold redundant checksum back. 8) */
421 if (head->ip_summed == CHECKSUM_COMPLETE) 432 if (head->ip_summed == CHECKSUM_COMPLETE)
@@ -520,8 +531,10 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user)
520{ 531{
521 struct sk_buff *clone; 532 struct sk_buff *clone;
522 struct net_device *dev = skb->dev; 533 struct net_device *dev = skb->dev;
534 struct net *net = skb_dst(skb) ? dev_net(skb_dst(skb)->dev)
535 : dev_net(skb->dev);
523 struct frag_hdr *fhdr; 536 struct frag_hdr *fhdr;
524 struct nf_ct_frag6_queue *fq; 537 struct frag_queue *fq;
525 struct ipv6hdr *hdr; 538 struct ipv6hdr *hdr;
526 int fhoff, nhoff; 539 int fhoff, nhoff;
527 u8 prevhdr; 540 u8 prevhdr;
@@ -553,10 +566,11 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user)
553 hdr = ipv6_hdr(clone); 566 hdr = ipv6_hdr(clone);
554 fhdr = (struct frag_hdr *)skb_transport_header(clone); 567 fhdr = (struct frag_hdr *)skb_transport_header(clone);
555 568
556 if (atomic_read(&nf_init_frags.mem) > nf_init_frags.high_thresh) 569 local_bh_disable();
557 nf_ct_frag6_evictor(); 570 inet_frag_evictor(&net->nf_frag.frags, &nf_frags, false);
571 local_bh_enable();
558 572
559 fq = fq_find(fhdr->identification, user, &hdr->saddr, &hdr->daddr); 573 fq = fq_find(net, fhdr->identification, user, &hdr->saddr, &hdr->daddr);
560 if (fq == NULL) { 574 if (fq == NULL) {
561 pr_debug("Can't find and can't create new queue\n"); 575 pr_debug("Can't find and can't create new queue\n");
562 goto ret_orig; 576 goto ret_orig;
@@ -567,7 +581,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user)
567 if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) { 581 if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) {
568 spin_unlock_bh(&fq->q.lock); 582 spin_unlock_bh(&fq->q.lock);
569 pr_debug("Can't insert skb to queue\n"); 583 pr_debug("Can't insert skb to queue\n");
570 fq_put(fq); 584 inet_frag_put(&fq->q, &nf_frags);
571 goto ret_orig; 585 goto ret_orig;
572 } 586 }
573 587
@@ -579,7 +593,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user)
579 } 593 }
580 spin_unlock_bh(&fq->q.lock); 594 spin_unlock_bh(&fq->q.lock);
581 595
582 fq_put(fq); 596 inet_frag_put(&fq->q, &nf_frags);
583 return ret_skb; 597 return ret_skb;
584 598
585ret_orig: 599ret_orig:
@@ -592,6 +606,7 @@ void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
592 int (*okfn)(struct sk_buff *)) 606 int (*okfn)(struct sk_buff *))
593{ 607{
594 struct sk_buff *s, *s2; 608 struct sk_buff *s, *s2;
609 unsigned int ret = 0;
595 610
596 for (s = NFCT_FRAG6_CB(skb)->orig; s;) { 611 for (s = NFCT_FRAG6_CB(skb)->orig; s;) {
597 nf_conntrack_put_reasm(s->nfct_reasm); 612 nf_conntrack_put_reasm(s->nfct_reasm);
@@ -601,49 +616,62 @@ void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
601 s2 = s->next; 616 s2 = s->next;
602 s->next = NULL; 617 s->next = NULL;
603 618
604 NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, s, in, out, okfn, 619 if (ret != -ECANCELED)
605 NF_IP6_PRI_CONNTRACK_DEFRAG + 1); 620 ret = NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, s,
621 in, out, okfn,
622 NF_IP6_PRI_CONNTRACK_DEFRAG + 1);
623 else
624 kfree_skb(s);
625
606 s = s2; 626 s = s2;
607 } 627 }
608 nf_conntrack_put_reasm(skb); 628 nf_conntrack_put_reasm(skb);
609} 629}
610 630
631static int nf_ct_net_init(struct net *net)
632{
633 net->nf_frag.frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
634 net->nf_frag.frags.low_thresh = IPV6_FRAG_LOW_THRESH;
635 net->nf_frag.frags.timeout = IPV6_FRAG_TIMEOUT;
636 inet_frags_init_net(&net->nf_frag.frags);
637
638 return nf_ct_frag6_sysctl_register(net);
639}
640
641static void nf_ct_net_exit(struct net *net)
642{
643 nf_ct_frags6_sysctl_unregister(net);
644 inet_frags_exit_net(&net->nf_frag.frags, &nf_frags);
645}
646
647static struct pernet_operations nf_ct_net_ops = {
648 .init = nf_ct_net_init,
649 .exit = nf_ct_net_exit,
650};
651
611int nf_ct_frag6_init(void) 652int nf_ct_frag6_init(void)
612{ 653{
654 int ret = 0;
655
613 nf_frags.hashfn = nf_hashfn; 656 nf_frags.hashfn = nf_hashfn;
614 nf_frags.constructor = ip6_frag_init; 657 nf_frags.constructor = ip6_frag_init;
615 nf_frags.destructor = NULL; 658 nf_frags.destructor = NULL;
616 nf_frags.skb_free = nf_skb_free; 659 nf_frags.skb_free = nf_skb_free;
617 nf_frags.qsize = sizeof(struct nf_ct_frag6_queue); 660 nf_frags.qsize = sizeof(struct frag_queue);
618 nf_frags.match = ip6_frag_match; 661 nf_frags.match = ip6_frag_match;
619 nf_frags.frag_expire = nf_ct_frag6_expire; 662 nf_frags.frag_expire = nf_ct_frag6_expire;
620 nf_frags.secret_interval = 10 * 60 * HZ; 663 nf_frags.secret_interval = 10 * 60 * HZ;
621 nf_init_frags.timeout = IPV6_FRAG_TIMEOUT;
622 nf_init_frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
623 nf_init_frags.low_thresh = IPV6_FRAG_LOW_THRESH;
624 inet_frags_init_net(&nf_init_frags);
625 inet_frags_init(&nf_frags); 664 inet_frags_init(&nf_frags);
626 665
627#ifdef CONFIG_SYSCTL 666 ret = register_pernet_subsys(&nf_ct_net_ops);
628 nf_ct_frag6_sysctl_header = register_net_sysctl(&init_net, "net/netfilter", 667 if (ret)
629 nf_ct_frag6_sysctl_table);
630 if (!nf_ct_frag6_sysctl_header) {
631 inet_frags_fini(&nf_frags); 668 inet_frags_fini(&nf_frags);
632 return -ENOMEM;
633 }
634#endif
635 669
636 return 0; 670 return ret;
637} 671}
638 672
639void nf_ct_frag6_cleanup(void) 673void nf_ct_frag6_cleanup(void)
640{ 674{
641#ifdef CONFIG_SYSCTL 675 unregister_pernet_subsys(&nf_ct_net_ops);
642 unregister_net_sysctl_table(nf_ct_frag6_sysctl_header);
643 nf_ct_frag6_sysctl_header = NULL;
644#endif
645 inet_frags_fini(&nf_frags); 676 inet_frags_fini(&nf_frags);
646
647 nf_init_frags.low_thresh = 0;
648 nf_ct_frag6_evictor();
649} 677}