xref: /linux/net/ipv4/inet_fragment.c (revision 321a3a99e4717b960e21c62fc6a140d21453df7f)
17eb95156SPavel Emelyanov /*
27eb95156SPavel Emelyanov  * inet fragments management
37eb95156SPavel Emelyanov  *
47eb95156SPavel Emelyanov  *		This program is free software; you can redistribute it and/or
57eb95156SPavel Emelyanov  *		modify it under the terms of the GNU General Public License
67eb95156SPavel Emelyanov  *		as published by the Free Software Foundation; either version
77eb95156SPavel Emelyanov  *		2 of the License, or (at your option) any later version.
87eb95156SPavel Emelyanov  *
97eb95156SPavel Emelyanov  * 		Authors:	Pavel Emelyanov <xemul@openvz.org>
107eb95156SPavel Emelyanov  *				Started as consolidation of ipv4/ip_fragment.c,
117eb95156SPavel Emelyanov  *				ipv6/reassembly. and ipv6 nf conntrack reassembly
127eb95156SPavel Emelyanov  */
137eb95156SPavel Emelyanov 
147eb95156SPavel Emelyanov #include <linux/list.h>
157eb95156SPavel Emelyanov #include <linux/spinlock.h>
167eb95156SPavel Emelyanov #include <linux/module.h>
177eb95156SPavel Emelyanov #include <linux/timer.h>
187eb95156SPavel Emelyanov #include <linux/mm.h>
19*321a3a99SPavel Emelyanov #include <linux/random.h>
207eb95156SPavel Emelyanov 
217eb95156SPavel Emelyanov #include <net/inet_frag.h>
227eb95156SPavel Emelyanov 
23*321a3a99SPavel Emelyanov static void inet_frag_secret_rebuild(unsigned long dummy)
24*321a3a99SPavel Emelyanov {
25*321a3a99SPavel Emelyanov 	struct inet_frags *f = (struct inet_frags *)dummy;
26*321a3a99SPavel Emelyanov 	unsigned long now = jiffies;
27*321a3a99SPavel Emelyanov 	int i;
28*321a3a99SPavel Emelyanov 
29*321a3a99SPavel Emelyanov 	write_lock(&f->lock);
30*321a3a99SPavel Emelyanov 	get_random_bytes(&f->rnd, sizeof(u32));
31*321a3a99SPavel Emelyanov 	for (i = 0; i < INETFRAGS_HASHSZ; i++) {
32*321a3a99SPavel Emelyanov 		struct inet_frag_queue *q;
33*321a3a99SPavel Emelyanov 		struct hlist_node *p, *n;
34*321a3a99SPavel Emelyanov 
35*321a3a99SPavel Emelyanov 		hlist_for_each_entry_safe(q, p, n, &f->hash[i], list) {
36*321a3a99SPavel Emelyanov 			unsigned int hval = f->hashfn(q);
37*321a3a99SPavel Emelyanov 
38*321a3a99SPavel Emelyanov 			if (hval != i) {
39*321a3a99SPavel Emelyanov 				hlist_del(&q->list);
40*321a3a99SPavel Emelyanov 
41*321a3a99SPavel Emelyanov 				/* Relink to new hash chain. */
42*321a3a99SPavel Emelyanov 				hlist_add_head(&q->list, &f->hash[hval]);
43*321a3a99SPavel Emelyanov 			}
44*321a3a99SPavel Emelyanov 		}
45*321a3a99SPavel Emelyanov 	}
46*321a3a99SPavel Emelyanov 	write_unlock(&f->lock);
47*321a3a99SPavel Emelyanov 
48*321a3a99SPavel Emelyanov 	mod_timer(&f->secret_timer, now + f->ctl->secret_interval);
49*321a3a99SPavel Emelyanov }
50*321a3a99SPavel Emelyanov 
517eb95156SPavel Emelyanov void inet_frags_init(struct inet_frags *f)
527eb95156SPavel Emelyanov {
537eb95156SPavel Emelyanov 	int i;
547eb95156SPavel Emelyanov 
557eb95156SPavel Emelyanov 	for (i = 0; i < INETFRAGS_HASHSZ; i++)
567eb95156SPavel Emelyanov 		INIT_HLIST_HEAD(&f->hash[i]);
577eb95156SPavel Emelyanov 
587eb95156SPavel Emelyanov 	INIT_LIST_HEAD(&f->lru_list);
597eb95156SPavel Emelyanov 	rwlock_init(&f->lock);
607eb95156SPavel Emelyanov 
617eb95156SPavel Emelyanov 	f->rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^
627eb95156SPavel Emelyanov 				   (jiffies ^ (jiffies >> 6)));
637eb95156SPavel Emelyanov 
647eb95156SPavel Emelyanov 	f->nqueues = 0;
657eb95156SPavel Emelyanov 	atomic_set(&f->mem, 0);
667eb95156SPavel Emelyanov 
67*321a3a99SPavel Emelyanov 	init_timer(&f->secret_timer);
68*321a3a99SPavel Emelyanov 	f->secret_timer.function = inet_frag_secret_rebuild;
69*321a3a99SPavel Emelyanov 	f->secret_timer.data = (unsigned long)f;
70*321a3a99SPavel Emelyanov 	f->secret_timer.expires = jiffies + f->ctl->secret_interval;
71*321a3a99SPavel Emelyanov 	add_timer(&f->secret_timer);
727eb95156SPavel Emelyanov }
737eb95156SPavel Emelyanov EXPORT_SYMBOL(inet_frags_init);
747eb95156SPavel Emelyanov 
757eb95156SPavel Emelyanov void inet_frags_fini(struct inet_frags *f)
767eb95156SPavel Emelyanov {
77*321a3a99SPavel Emelyanov 	del_timer(&f->secret_timer);
787eb95156SPavel Emelyanov }
797eb95156SPavel Emelyanov EXPORT_SYMBOL(inet_frags_fini);
80277e650dSPavel Emelyanov 
81277e650dSPavel Emelyanov static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f)
82277e650dSPavel Emelyanov {
83277e650dSPavel Emelyanov 	write_lock(&f->lock);
84277e650dSPavel Emelyanov 	hlist_del(&fq->list);
85277e650dSPavel Emelyanov 	list_del(&fq->lru_list);
86277e650dSPavel Emelyanov 	f->nqueues--;
87277e650dSPavel Emelyanov 	write_unlock(&f->lock);
88277e650dSPavel Emelyanov }
89277e650dSPavel Emelyanov 
90277e650dSPavel Emelyanov void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f)
91277e650dSPavel Emelyanov {
92277e650dSPavel Emelyanov 	if (del_timer(&fq->timer))
93277e650dSPavel Emelyanov 		atomic_dec(&fq->refcnt);
94277e650dSPavel Emelyanov 
95277e650dSPavel Emelyanov 	if (!(fq->last_in & COMPLETE)) {
96277e650dSPavel Emelyanov 		fq_unlink(fq, f);
97277e650dSPavel Emelyanov 		atomic_dec(&fq->refcnt);
98277e650dSPavel Emelyanov 		fq->last_in |= COMPLETE;
99277e650dSPavel Emelyanov 	}
100277e650dSPavel Emelyanov }
101277e650dSPavel Emelyanov 
102277e650dSPavel Emelyanov EXPORT_SYMBOL(inet_frag_kill);
103