aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorYasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>2005-11-14 18:28:18 -0500
committerDavid S. Miller <davem@davemloft.net>2005-11-14 18:28:18 -0500
commit1ba430bc3e243d38c0bb2b185bea664b04fc59df (patch)
treef675bb89f3ac344ddf25ecbdf3a89484a7f3ac4f /net
parent7686a02c0ebc11e4f881fe14db3df18569b7dbc1 (diff)
[NETFILTER] nf_conntrack: fix possibility of infinite loop while evicting nf_ct_frag6_queue
This synchronizes nf_ct_reasm with ipv6 reassembly, and fixes a possibility of an infinite loop if CPUs evict and create nf_ct_frag6_queue in parallel. Signed-off-by: Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp> Signed-off-by: Harald Welte <laforge@netfilter.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv6/netfilter/nf_conntrack_reasm.c42
1 files changed, 26 insertions, 16 deletions
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index ed7603fe5fe3..1b68d714c0a4 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -190,8 +190,10 @@ static void nf_ct_frag6_secret_rebuild(unsigned long dummy)
190atomic_t nf_ct_frag6_mem = ATOMIC_INIT(0); 190atomic_t nf_ct_frag6_mem = ATOMIC_INIT(0);
191 191
192/* Memory Tracking Functions. */ 192/* Memory Tracking Functions. */
193static inline void frag_kfree_skb(struct sk_buff *skb) 193static inline void frag_kfree_skb(struct sk_buff *skb, unsigned int *work)
194{ 194{
195 if (work)
196 *work -= skb->truesize;
195 atomic_sub(skb->truesize, &nf_ct_frag6_mem); 197 atomic_sub(skb->truesize, &nf_ct_frag6_mem);
196 if (NFCT_FRAG6_CB(skb)->orig) 198 if (NFCT_FRAG6_CB(skb)->orig)
197 kfree_skb(NFCT_FRAG6_CB(skb)->orig); 199 kfree_skb(NFCT_FRAG6_CB(skb)->orig);
@@ -199,8 +201,11 @@ static inline void frag_kfree_skb(struct sk_buff *skb)
199 kfree_skb(skb); 201 kfree_skb(skb);
200} 202}
201 203
202static inline void frag_free_queue(struct nf_ct_frag6_queue *fq) 204static inline void frag_free_queue(struct nf_ct_frag6_queue *fq,
205 unsigned int *work)
203{ 206{
207 if (work)
208 *work -= sizeof(struct nf_ct_frag6_queue);
204 atomic_sub(sizeof(struct nf_ct_frag6_queue), &nf_ct_frag6_mem); 209 atomic_sub(sizeof(struct nf_ct_frag6_queue), &nf_ct_frag6_mem);
205 kfree(fq); 210 kfree(fq);
206} 211}
@@ -218,7 +223,8 @@ static inline struct nf_ct_frag6_queue *frag_alloc_queue(void)
218/* Destruction primitives. */ 223/* Destruction primitives. */
219 224
220/* Complete destruction of fq. */ 225/* Complete destruction of fq. */
221static void nf_ct_frag6_destroy(struct nf_ct_frag6_queue *fq) 226static void nf_ct_frag6_destroy(struct nf_ct_frag6_queue *fq,
227 unsigned int *work)
222{ 228{
223 struct sk_buff *fp; 229 struct sk_buff *fp;
224 230
@@ -230,17 +236,17 @@ static void nf_ct_frag6_destroy(struct nf_ct_frag6_queue *fq)
230 while (fp) { 236 while (fp) {
231 struct sk_buff *xp = fp->next; 237 struct sk_buff *xp = fp->next;
232 238
233 frag_kfree_skb(fp); 239 frag_kfree_skb(fp, work);
234 fp = xp; 240 fp = xp;
235 } 241 }
236 242
237 frag_free_queue(fq); 243 frag_free_queue(fq, work);
238} 244}
239 245
240static __inline__ void fq_put(struct nf_ct_frag6_queue *fq) 246static __inline__ void fq_put(struct nf_ct_frag6_queue *fq, unsigned int *work)
241{ 247{
242 if (atomic_dec_and_test(&fq->refcnt)) 248 if (atomic_dec_and_test(&fq->refcnt))
243 nf_ct_frag6_destroy(fq); 249 nf_ct_frag6_destroy(fq, work);
244} 250}
245 251
246/* Kill fq entry. It is not destroyed immediately, 252/* Kill fq entry. It is not destroyed immediately,
@@ -262,10 +268,14 @@ static void nf_ct_frag6_evictor(void)
262{ 268{
263 struct nf_ct_frag6_queue *fq; 269 struct nf_ct_frag6_queue *fq;
264 struct list_head *tmp; 270 struct list_head *tmp;
271 unsigned int work;
265 272
266 for (;;) { 273 work = atomic_read(&nf_ct_frag6_mem);
267 if (atomic_read(&nf_ct_frag6_mem) <= nf_ct_frag6_low_thresh) 274 if (work <= nf_ct_frag6_low_thresh)
268 return; 275 return;
276
277 work -= nf_ct_frag6_low_thresh;
278 while (work > 0) {
269 read_lock(&nf_ct_frag6_lock); 279 read_lock(&nf_ct_frag6_lock);
270 if (list_empty(&nf_ct_frag6_lru_list)) { 280 if (list_empty(&nf_ct_frag6_lru_list)) {
271 read_unlock(&nf_ct_frag6_lock); 281 read_unlock(&nf_ct_frag6_lock);
@@ -281,7 +291,7 @@ static void nf_ct_frag6_evictor(void)
281 fq_kill(fq); 291 fq_kill(fq);
282 spin_unlock(&fq->lock); 292 spin_unlock(&fq->lock);
283 293
284 fq_put(fq); 294 fq_put(fq, &work);
285 } 295 }
286} 296}
287 297
@@ -298,7 +308,7 @@ static void nf_ct_frag6_expire(unsigned long data)
298 308
299out: 309out:
300 spin_unlock(&fq->lock); 310 spin_unlock(&fq->lock);
301 fq_put(fq); 311 fq_put(fq, NULL);
302} 312}
303 313
304/* Creation primitives. */ 314/* Creation primitives. */
@@ -318,7 +328,7 @@ static struct nf_ct_frag6_queue *nf_ct_frag6_intern(unsigned int hash,
318 atomic_inc(&fq->refcnt); 328 atomic_inc(&fq->refcnt);
319 write_unlock(&nf_ct_frag6_lock); 329 write_unlock(&nf_ct_frag6_lock);
320 fq_in->last_in |= COMPLETE; 330 fq_in->last_in |= COMPLETE;
321 fq_put(fq_in); 331 fq_put(fq_in, NULL);
322 return fq; 332 return fq;
323 } 333 }
324 } 334 }
@@ -535,7 +545,7 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
535 fq->fragments = next; 545 fq->fragments = next;
536 546
537 fq->meat -= free_it->len; 547 fq->meat -= free_it->len;
538 frag_kfree_skb(free_it); 548 frag_kfree_skb(free_it, NULL);
539 } 549 }
540 } 550 }
541 551
@@ -811,7 +821,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
811 if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) { 821 if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) {
812 spin_unlock(&fq->lock); 822 spin_unlock(&fq->lock);
813 DEBUGP("Can't insert skb to queue\n"); 823 DEBUGP("Can't insert skb to queue\n");
814 fq_put(fq); 824 fq_put(fq, NULL);
815 goto ret_orig; 825 goto ret_orig;
816 } 826 }
817 827
@@ -822,7 +832,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb)
822 } 832 }
823 spin_unlock(&fq->lock); 833 spin_unlock(&fq->lock);
824 834
825 fq_put(fq); 835 fq_put(fq, NULL);
826 return ret_skb; 836 return ret_skb;
827 837
828ret_orig: 838ret_orig: