Blame view

net/ipv4/inet_fragment.c 6.35 KB
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  /*
   * inet fragments management
   *
   *		This program is free software; you can redistribute it and/or
   *		modify it under the terms of the GNU General Public License
   *		as published by the Free Software Foundation; either version
   *		2 of the License, or (at your option) any later version.
   *
   * 		Authors:	Pavel Emelyanov <xemul@openvz.org>
   *				Started as consolidation of ipv4/ip_fragment.c,
   *				ipv6/reassembly. and ipv6 nf conntrack reassembly
   */
  
  #include <linux/list.h>
  #include <linux/spinlock.h>
  #include <linux/module.h>
  #include <linux/timer.h>
  #include <linux/mm.h>
321a3a99e   Pavel Emelyanov   [INET]: Consolida...
19
  #include <linux/random.h>
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
20
21
  #include <linux/skbuff.h>
  #include <linux/rtnetlink.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
22
  #include <linux/slab.h>
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
23
24
  
  #include <net/inet_frag.h>
321a3a99e   Pavel Emelyanov   [INET]: Consolida...
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  static void inet_frag_secret_rebuild(unsigned long dummy)
  {
  	struct inet_frags *f = (struct inet_frags *)dummy;
  	unsigned long now = jiffies;
  	int i;
  
  	write_lock(&f->lock);
  	get_random_bytes(&f->rnd, sizeof(u32));
  	for (i = 0; i < INETFRAGS_HASHSZ; i++) {
  		struct inet_frag_queue *q;
  		struct hlist_node *p, *n;
  
  		hlist_for_each_entry_safe(q, p, n, &f->hash[i], list) {
  			unsigned int hval = f->hashfn(q);
  
  			if (hval != i) {
  				hlist_del(&q->list);
  
  				/* Relink to new hash chain. */
  				hlist_add_head(&q->list, &f->hash[hval]);
  			}
  		}
  	}
  	write_unlock(&f->lock);
3b4bc4a2b   Pavel Emelyanov   [NETNS][FRAGS]: I...
49
  	mod_timer(&f->secret_timer, now + f->secret_interval);
321a3a99e   Pavel Emelyanov   [INET]: Consolida...
50
  }
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
51
52
53
54
55
56
  void inet_frags_init(struct inet_frags *f)
  {
  	int i;
  
  	for (i = 0; i < INETFRAGS_HASHSZ; i++)
  		INIT_HLIST_HEAD(&f->hash[i]);
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
57
58
59
60
  	rwlock_init(&f->lock);
  
  	f->rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^
  				   (jiffies ^ (jiffies >> 6)));
b24b8a247   Pavel Emelyanov   [NET]: Convert in...
61
62
  	setup_timer(&f->secret_timer, inet_frag_secret_rebuild,
  			(unsigned long)f);
3b4bc4a2b   Pavel Emelyanov   [NETNS][FRAGS]: I...
63
  	f->secret_timer.expires = jiffies + f->secret_interval;
321a3a99e   Pavel Emelyanov   [INET]: Consolida...
64
  	add_timer(&f->secret_timer);
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
65
66
  }
  EXPORT_SYMBOL(inet_frags_init);
e5a2bb842   Pavel Emelyanov   [NETNS][FRAGS]: M...
67
68
69
  void inet_frags_init_net(struct netns_frags *nf)
  {
  	nf->nqueues = 0;
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
70
  	atomic_set(&nf->mem, 0);
3140c25c8   Pavel Emelyanov   [NETNS][FRAGS]: M...
71
  	INIT_LIST_HEAD(&nf->lru_list);
e5a2bb842   Pavel Emelyanov   [NETNS][FRAGS]: M...
72
73
  }
  EXPORT_SYMBOL(inet_frags_init_net);
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
74
75
  void inet_frags_fini(struct inet_frags *f)
  {
321a3a99e   Pavel Emelyanov   [INET]: Consolida...
76
  	del_timer(&f->secret_timer);
7eb95156d   Pavel Emelyanov   [INET]: Collect f...
77
78
  }
  EXPORT_SYMBOL(inet_frags_fini);
277e650dd   Pavel Emelyanov   [INET]: Consolida...
79

81566e832   Pavel Emelyanov   [NETNS][FRAGS]: M...
80
81
82
  void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f)
  {
  	nf->low_thresh = 0;
e8e16b706   David S. Miller   [INET]: inet_frag...
83
84
  
  	local_bh_disable();
81566e832   Pavel Emelyanov   [NETNS][FRAGS]: M...
85
  	inet_frag_evictor(nf, f);
e8e16b706   David S. Miller   [INET]: inet_frag...
86
  	local_bh_enable();
81566e832   Pavel Emelyanov   [NETNS][FRAGS]: M...
87
88
  }
  EXPORT_SYMBOL(inet_frags_exit_net);
277e650dd   Pavel Emelyanov   [INET]: Consolida...
89
90
91
92
93
  static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f)
  {
  	write_lock(&f->lock);
  	hlist_del(&fq->list);
  	list_del(&fq->lru_list);
e5a2bb842   Pavel Emelyanov   [NETNS][FRAGS]: M...
94
  	fq->net->nqueues--;
277e650dd   Pavel Emelyanov   [INET]: Consolida...
95
96
97
98
99
100
101
  	write_unlock(&f->lock);
  }
  
  void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f)
  {
  	if (del_timer(&fq->timer))
  		atomic_dec(&fq->refcnt);
bc578a54f   Joe Perches   [NET]: Rename ine...
102
  	if (!(fq->last_in & INET_FRAG_COMPLETE)) {
277e650dd   Pavel Emelyanov   [INET]: Consolida...
103
104
  		fq_unlink(fq, f);
  		atomic_dec(&fq->refcnt);
bc578a54f   Joe Perches   [NET]: Rename ine...
105
  		fq->last_in |= INET_FRAG_COMPLETE;
277e650dd   Pavel Emelyanov   [INET]: Consolida...
106
107
  	}
  }
277e650dd   Pavel Emelyanov   [INET]: Consolida...
108
  EXPORT_SYMBOL(inet_frag_kill);
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
109

6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
110
111
  static inline void frag_kfree_skb(struct netns_frags *nf, struct inet_frags *f,
  		struct sk_buff *skb, int *work)
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
112
113
114
  {
  	if (work)
  		*work -= skb->truesize;
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
115
  	atomic_sub(skb->truesize, &nf->mem);
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
116
117
118
119
120
121
122
123
124
  	if (f->skb_free)
  		f->skb_free(skb);
  	kfree_skb(skb);
  }
  
  void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f,
  					int *work)
  {
  	struct sk_buff *fp;
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
125
  	struct netns_frags *nf;
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
126

547b792ca   Ilpo Järvinen   net: convert BUG_...
127
128
  	WARN_ON(!(q->last_in & INET_FRAG_COMPLETE));
  	WARN_ON(del_timer(&q->timer) != 0);
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
129
130
131
  
  	/* Release all fragment data. */
  	fp = q->fragments;
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
132
  	nf = q->net;
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
133
134
  	while (fp) {
  		struct sk_buff *xp = fp->next;
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
135
  		frag_kfree_skb(nf, f, fp, work);
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
136
137
138
139
140
  		fp = xp;
  	}
  
  	if (work)
  		*work -= f->qsize;
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
141
  	atomic_sub(f->qsize, &nf->mem);
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
142

c95477090   Pavel Emelyanov   [INET]: Consolida...
143
144
145
  	if (f->destructor)
  		f->destructor(q);
  	kfree(q);
1e4b82873   Pavel Emelyanov   [INET]: Consolida...
146
147
148
  
  }
  EXPORT_SYMBOL(inet_frag_destroy);
8e7999c44   Pavel Emelyanov   [INET]: Consolida...
149

6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
150
  int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f)
8e7999c44   Pavel Emelyanov   [INET]: Consolida...
151
152
153
  {
  	struct inet_frag_queue *q;
  	int work, evicted = 0;
e31e0bdc7   Pavel Emelyanov   [NETNS][FRAGS]: M...
154
  	work = atomic_read(&nf->mem) - nf->low_thresh;
8e7999c44   Pavel Emelyanov   [INET]: Consolida...
155
156
  	while (work > 0) {
  		read_lock(&f->lock);
3140c25c8   Pavel Emelyanov   [NETNS][FRAGS]: M...
157
  		if (list_empty(&nf->lru_list)) {
8e7999c44   Pavel Emelyanov   [INET]: Consolida...
158
159
160
  			read_unlock(&f->lock);
  			break;
  		}
3140c25c8   Pavel Emelyanov   [NETNS][FRAGS]: M...
161
  		q = list_first_entry(&nf->lru_list,
8e7999c44   Pavel Emelyanov   [INET]: Consolida...
162
163
164
165
166
  				struct inet_frag_queue, lru_list);
  		atomic_inc(&q->refcnt);
  		read_unlock(&f->lock);
  
  		spin_lock(&q->lock);
bc578a54f   Joe Perches   [NET]: Rename ine...
167
  		if (!(q->last_in & INET_FRAG_COMPLETE))
8e7999c44   Pavel Emelyanov   [INET]: Consolida...
168
169
170
171
172
173
174
175
176
177
178
  			inet_frag_kill(q, f);
  		spin_unlock(&q->lock);
  
  		if (atomic_dec_and_test(&q->refcnt))
  			inet_frag_destroy(q, f, &work);
  		evicted++;
  	}
  
  	return evicted;
  }
  EXPORT_SYMBOL(inet_frag_evictor);
2588fe1d7   Pavel Emelyanov   [INET]: Consolida...
179

ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
180
181
  static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf,
  		struct inet_frag_queue *qp_in, struct inet_frags *f,
9a375803f   Pavel Emelyanov   inet fragments: f...
182
  		void *arg)
2588fe1d7   Pavel Emelyanov   [INET]: Consolida...
183
184
185
186
187
  {
  	struct inet_frag_queue *qp;
  #ifdef CONFIG_SMP
  	struct hlist_node *n;
  #endif
9a375803f   Pavel Emelyanov   inet fragments: f...
188
  	unsigned int hash;
2588fe1d7   Pavel Emelyanov   [INET]: Consolida...
189
190
  
  	write_lock(&f->lock);
9a375803f   Pavel Emelyanov   inet fragments: f...
191
192
193
194
195
196
  	/*
  	 * While we stayed w/o the lock other CPU could update
  	 * the rnd seed, so we need to re-calculate the hash
  	 * chain. Fortunatelly the qp_in can be used to get one.
  	 */
  	hash = f->hashfn(qp_in);
2588fe1d7   Pavel Emelyanov   [INET]: Consolida...
197
198
199
200
201
202
  #ifdef CONFIG_SMP
  	/* With SMP race we have to recheck hash table, because
  	 * such entry could be created on other cpu, while we
  	 * promoted read lock to write lock.
  	 */
  	hlist_for_each_entry(qp, n, &f->hash[hash], list) {
ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
203
  		if (qp->net == nf && f->match(qp, arg)) {
2588fe1d7   Pavel Emelyanov   [INET]: Consolida...
204
205
  			atomic_inc(&qp->refcnt);
  			write_unlock(&f->lock);
bc578a54f   Joe Perches   [NET]: Rename ine...
206
  			qp_in->last_in |= INET_FRAG_COMPLETE;
2588fe1d7   Pavel Emelyanov   [INET]: Consolida...
207
208
209
210
211
212
  			inet_frag_put(qp_in, f);
  			return qp;
  		}
  	}
  #endif
  	qp = qp_in;
b2fd5321d   Pavel Emelyanov   [NETNS][FRAGS]: M...
213
  	if (!mod_timer(&qp->timer, jiffies + nf->timeout))
2588fe1d7   Pavel Emelyanov   [INET]: Consolida...
214
215
216
217
  		atomic_inc(&qp->refcnt);
  
  	atomic_inc(&qp->refcnt);
  	hlist_add_head(&qp->list, &f->hash[hash]);
3140c25c8   Pavel Emelyanov   [NETNS][FRAGS]: M...
218
  	list_add_tail(&qp->lru_list, &nf->lru_list);
e5a2bb842   Pavel Emelyanov   [NETNS][FRAGS]: M...
219
  	nf->nqueues++;
2588fe1d7   Pavel Emelyanov   [INET]: Consolida...
220
221
222
  	write_unlock(&f->lock);
  	return qp;
  }
e521db9d7   Pavel Emelyanov   [INET]: Consolida...
223

ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
224
225
  static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf,
  		struct inet_frags *f, void *arg)
e521db9d7   Pavel Emelyanov   [INET]: Consolida...
226
227
228
229
230
231
  {
  	struct inet_frag_queue *q;
  
  	q = kzalloc(f->qsize, GFP_ATOMIC);
  	if (q == NULL)
  		return NULL;
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
232
  	f->constructor(q, arg);
6ddc08222   Pavel Emelyanov   [NETNS][FRAGS]: M...
233
  	atomic_add(f->qsize, &nf->mem);
e521db9d7   Pavel Emelyanov   [INET]: Consolida...
234
235
236
  	setup_timer(&q->timer, f->frag_expire, (unsigned long)q);
  	spin_lock_init(&q->lock);
  	atomic_set(&q->refcnt, 1);
ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
237
  	q->net = nf;
e521db9d7   Pavel Emelyanov   [INET]: Consolida...
238
239
240
  
  	return q;
  }
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
241

ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
242
  static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf,
9a375803f   Pavel Emelyanov   inet fragments: f...
243
  		struct inet_frags *f, void *arg)
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
244
245
  {
  	struct inet_frag_queue *q;
ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
246
  	q = inet_frag_alloc(nf, f, arg);
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
247
248
  	if (q == NULL)
  		return NULL;
9a375803f   Pavel Emelyanov   inet fragments: f...
249
  	return inet_frag_intern(nf, q, f, arg);
c6fda2822   Pavel Emelyanov   [INET]: Consolida...
250
  }
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
251

ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
252
253
  struct inet_frag_queue *inet_frag_find(struct netns_frags *nf,
  		struct inet_frags *f, void *key, unsigned int hash)
56bca31ff   Hannes Eder   inet fragments: f...
254
  	__releases(&f->lock)
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
255
256
257
  {
  	struct inet_frag_queue *q;
  	struct hlist_node *n;
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
258
  	hlist_for_each_entry(q, n, &f->hash[hash], list) {
ac18e7509   Pavel Emelyanov   [NETNS][FRAGS]: M...
259
  		if (q->net == nf && f->match(q, key)) {
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
260
261
262
263
264
265
  			atomic_inc(&q->refcnt);
  			read_unlock(&f->lock);
  			return q;
  		}
  	}
  	read_unlock(&f->lock);
9a375803f   Pavel Emelyanov   inet fragments: f...
266
  	return inet_frag_create(nf, f, key);
abd6523d1   Pavel Emelyanov   [INET]: Consolida...
267
268
  }
  EXPORT_SYMBOL(inet_frag_find);