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