diff options
Diffstat (limited to 'net/ipv6/netfilter/nf_conntrack_reasm.c')
-rw-r--r-- | net/ipv6/netfilter/nf_conntrack_reasm.c | 314 |
1 files changed, 101 insertions, 213 deletions
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 25442a8c1ba8..726fafd41961 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -31,6 +31,7 @@ #include <net/sock.h> #include <net/snmp.h> +#include <net/inet_frag.h> #include <net/ipv6.h> #include <net/protocol.h> @@ -48,10 +49,6 @@ #define NF_CT_FRAG6_LOW_THRESH 196608 /* == 192*1024 */ #define NF_CT_FRAG6_TIMEOUT IPV6_FRAG_TIMEOUT -unsigned int nf_ct_frag6_high_thresh __read_mostly = 256*1024; -unsigned int nf_ct_frag6_low_thresh __read_mostly = 192*1024; -unsigned long nf_ct_frag6_timeout __read_mostly = IPV6_FRAG_TIMEOUT; - struct nf_ct_frag6_skb_cb { struct inet6_skb_parm h; @@ -63,51 +60,24 @@ struct nf_ct_frag6_skb_cb struct nf_ct_frag6_queue { - struct hlist_node list; - struct list_head lru_list; /* lru list member */ + struct inet_frag_queue q; __be32 id; /* fragment id */ struct in6_addr saddr; struct in6_addr daddr; - spinlock_t lock; - atomic_t refcnt; - struct timer_list timer; /* expire timer */ - struct sk_buff *fragments; - int len; - int meat; - ktime_t stamp; unsigned int csum; - __u8 last_in; /* has first/last segment arrived? */ -#define COMPLETE 4 -#define FIRST_IN 2 -#define LAST_IN 1 __u16 nhoffset; }; -/* Hash table. */ - -#define FRAG6Q_HASHSZ 64 - -static struct hlist_head nf_ct_frag6_hash[FRAG6Q_HASHSZ]; -static DEFINE_RWLOCK(nf_ct_frag6_lock); -static u32 nf_ct_frag6_hash_rnd; -static LIST_HEAD(nf_ct_frag6_lru_list); -int nf_ct_frag6_nqueues = 0; - -static __inline__ void __fq_unlink(struct nf_ct_frag6_queue *fq) -{ - hlist_del(&fq->list); - list_del(&fq->lru_list); - nf_ct_frag6_nqueues--; -} +struct inet_frags_ctl nf_frags_ctl __read_mostly = { + .high_thresh = 256 * 1024, + .low_thresh = 192 * 1024, + .timeout = IPV6_FRAG_TIMEOUT, + .secret_interval = 10 * 60 * HZ, +}; -static __inline__ void fq_unlink(struct nf_ct_frag6_queue *fq) -{ - write_lock(&nf_ct_frag6_lock); - __fq_unlink(fq); - write_unlock(&nf_ct_frag6_lock); -} +static struct inet_frags nf_frags; static unsigned int ip6qhashfn(__be32 id, struct in6_addr *saddr, struct in6_addr *daddr) @@ -120,7 +90,7 @@ static unsigned int ip6qhashfn(__be32 id, struct in6_addr *saddr, a += JHASH_GOLDEN_RATIO; b += JHASH_GOLDEN_RATIO; - c += nf_ct_frag6_hash_rnd; + c += nf_frags.rnd; __jhash_mix(a, b, c); a += (__force u32)saddr->s6_addr32[3]; @@ -133,100 +103,54 @@ static unsigned int ip6qhashfn(__be32 id, struct in6_addr *saddr, c += (__force u32)id; __jhash_mix(a, b, c); - return c & (FRAG6Q_HASHSZ - 1); + return c & (INETFRAGS_HASHSZ - 1); } -static struct timer_list nf_ct_frag6_secret_timer; -int nf_ct_frag6_secret_interval = 10 * 60 * HZ; - -static void nf_ct_frag6_secret_rebuild(unsigned long dummy) +static unsigned int nf_hashfn(struct inet_frag_queue *q) { - unsigned long now = jiffies; - int i; - - write_lock(&nf_ct_frag6_lock); - get_random_bytes(&nf_ct_frag6_hash_rnd, sizeof(u32)); - for (i = 0; i < FRAG6Q_HASHSZ; i++) { - struct nf_ct_frag6_queue *q; - struct hlist_node *p, *n; - - hlist_for_each_entry_safe(q, p, n, &nf_ct_frag6_hash[i], list) { - unsigned int hval = ip6qhashfn(q->id, - &q->saddr, - &q->daddr); - if (hval != i) { - hlist_del(&q->list); - /* Relink to new hash chain. */ - hlist_add_head(&q->list, - &nf_ct_frag6_hash[hval]); - } - } - } - write_unlock(&nf_ct_frag6_lock); + struct nf_ct_frag6_queue *nq; - mod_timer(&nf_ct_frag6_secret_timer, now + nf_ct_frag6_secret_interval); + nq = container_of(q, struct nf_ct_frag6_queue, q); + return ip6qhashfn(nq->id, &nq->saddr, &nq->daddr); } -atomic_t nf_ct_frag6_mem = ATOMIC_INIT(0); +static void nf_skb_free(struct sk_buff *skb) +{ + if (NFCT_FRAG6_CB(skb)->orig) + kfree_skb(NFCT_FRAG6_CB(skb)->orig); +} /* Memory Tracking Functions. */ static inline void frag_kfree_skb(struct sk_buff *skb, unsigned int *work) { if (work) *work -= skb->truesize; - atomic_sub(skb->truesize, &nf_ct_frag6_mem); - if (NFCT_FRAG6_CB(skb)->orig) - kfree_skb(NFCT_FRAG6_CB(skb)->orig); - + atomic_sub(skb->truesize, &nf_frags.mem); + nf_skb_free(skb); kfree_skb(skb); } -static inline void frag_free_queue(struct nf_ct_frag6_queue *fq, - unsigned int *work) +static void nf_frag_free(struct inet_frag_queue *q) { - if (work) - *work -= sizeof(struct nf_ct_frag6_queue); - atomic_sub(sizeof(struct nf_ct_frag6_queue), &nf_ct_frag6_mem); - kfree(fq); + kfree(container_of(q, struct nf_ct_frag6_queue, q)); } static inline struct nf_ct_frag6_queue *frag_alloc_queue(void) { - struct nf_ct_frag6_queue *fq = kmalloc(sizeof(struct nf_ct_frag6_queue), GFP_ATOMIC); + struct nf_ct_frag6_queue *fq; - if (!fq) + fq = kzalloc(sizeof(struct nf_ct_frag6_queue), GFP_ATOMIC); + if (fq == NULL) return NULL; - atomic_add(sizeof(struct nf_ct_frag6_queue), &nf_ct_frag6_mem); + atomic_add(sizeof(struct nf_ct_frag6_queue), &nf_frags.mem); return fq; } /* Destruction primitives. */ -/* Complete destruction of fq. */ -static void nf_ct_frag6_destroy(struct nf_ct_frag6_queue *fq, - unsigned int *work) +static __inline__ void fq_put(struct nf_ct_frag6_queue *fq) { - struct sk_buff *fp; - - BUG_TRAP(fq->last_in&COMPLETE); - BUG_TRAP(del_timer(&fq->timer) == 0); - - /* Release all fragment data. */ - fp = fq->fragments; - while (fp) { - struct sk_buff *xp = fp->next; - - frag_kfree_skb(fp, work); - fp = xp; - } - - frag_free_queue(fq, work); -} - -static __inline__ void fq_put(struct nf_ct_frag6_queue *fq, unsigned int *work) -{ - if (atomic_dec_and_test(&fq->refcnt)) - nf_ct_frag6_destroy(fq, work); + inet_frag_put(&fq->q, &nf_frags); } /* Kill fq entry. It is not destroyed immediately, @@ -234,62 +158,28 @@ static __inline__ void fq_put(struct nf_ct_frag6_queue *fq, unsigned int *work) */ static __inline__ void fq_kill(struct nf_ct_frag6_queue *fq) { - if (del_timer(&fq->timer)) - atomic_dec(&fq->refcnt); - - if (!(fq->last_in & COMPLETE)) { - fq_unlink(fq); - atomic_dec(&fq->refcnt); - fq->last_in |= COMPLETE; - } + inet_frag_kill(&fq->q, &nf_frags); } static void nf_ct_frag6_evictor(void) { - struct nf_ct_frag6_queue *fq; - struct list_head *tmp; - unsigned int work; - - work = atomic_read(&nf_ct_frag6_mem); - if (work <= nf_ct_frag6_low_thresh) - return; - - work -= nf_ct_frag6_low_thresh; - while (work > 0) { - read_lock(&nf_ct_frag6_lock); - if (list_empty(&nf_ct_frag6_lru_list)) { - read_unlock(&nf_ct_frag6_lock); - return; - } - tmp = nf_ct_frag6_lru_list.next; - BUG_ON(tmp == NULL); - fq = list_entry(tmp, struct nf_ct_frag6_queue, lru_list); - atomic_inc(&fq->refcnt); - read_unlock(&nf_ct_frag6_lock); - - spin_lock(&fq->lock); - if (!(fq->last_in&COMPLETE)) - fq_kill(fq); - spin_unlock(&fq->lock); - - fq_put(fq, &work); - } + inet_frag_evictor(&nf_frags); } static void nf_ct_frag6_expire(unsigned long data) { struct nf_ct_frag6_queue *fq = (struct nf_ct_frag6_queue *) data; - spin_lock(&fq->lock); + spin_lock(&fq->q.lock); - if (fq->last_in & COMPLETE) + if (fq->q.last_in & COMPLETE) goto out; fq_kill(fq); out: - spin_unlock(&fq->lock); - fq_put(fq, NULL); + spin_unlock(&fq->q.lock); + fq_put(fq); } /* Creation primitives. */ @@ -302,31 +192,31 @@ static struct nf_ct_frag6_queue *nf_ct_frag6_intern(unsigned int hash, struct hlist_node *n; #endif - write_lock(&nf_ct_frag6_lock); + write_lock(&nf_frags.lock); #ifdef CONFIG_SMP - hlist_for_each_entry(fq, n, &nf_ct_frag6_hash[hash], list) { + hlist_for_each_entry(fq, n, &nf_frags.hash[hash], q.list) { if (fq->id == fq_in->id && ipv6_addr_equal(&fq_in->saddr, &fq->saddr) && ipv6_addr_equal(&fq_in->daddr, &fq->daddr)) { - atomic_inc(&fq->refcnt); - write_unlock(&nf_ct_frag6_lock); - fq_in->last_in |= COMPLETE; - fq_put(fq_in, NULL); + atomic_inc(&fq->q.refcnt); + write_unlock(&nf_frags.lock); + fq_in->q.last_in |= COMPLETE; + fq_put(fq_in); return fq; } } #endif fq = fq_in; - if (!mod_timer(&fq->timer, jiffies + nf_ct_frag6_timeout)) - atomic_inc(&fq->refcnt); + if (!mod_timer(&fq->q.timer, jiffies + nf_frags_ctl.timeout)) + atomic_inc(&fq->q.refcnt); - atomic_inc(&fq->refcnt); - hlist_add_head(&fq->list, &nf_ct_frag6_hash[hash]); - INIT_LIST_HEAD(&fq->lru_list); - list_add_tail(&fq->lru_list, &nf_ct_frag6_lru_list); - nf_ct_frag6_nqueues++; - write_unlock(&nf_ct_frag6_lock); + atomic_inc(&fq->q.refcnt); + hlist_add_head(&fq->q.list, &nf_frags.hash[hash]); + INIT_LIST_HEAD(&fq->q.lru_list); + list_add_tail(&fq->q.lru_list, &nf_frags.lru_list); + nf_frags.nqueues++; + write_unlock(&nf_frags.lock); return fq; } @@ -341,15 +231,13 @@ nf_ct_frag6_create(unsigned int hash, __be32 id, struct in6_addr *src, str goto oom; } - memset(fq, 0, sizeof(struct nf_ct_frag6_queue)); - fq->id = id; ipv6_addr_copy(&fq->saddr, src); ipv6_addr_copy(&fq->daddr, dst); - setup_timer(&fq->timer, nf_ct_frag6_expire, (unsigned long)fq); - spin_lock_init(&fq->lock); - atomic_set(&fq->refcnt, 1); + setup_timer(&fq->q.timer, nf_ct_frag6_expire, (unsigned long)fq); + spin_lock_init(&fq->q.lock); + atomic_set(&fq->q.refcnt, 1); return nf_ct_frag6_intern(hash, fq); @@ -364,17 +252,17 @@ fq_find(__be32 id, struct in6_addr *src, struct in6_addr *dst) struct hlist_node *n; unsigned int hash = ip6qhashfn(id, src, dst); - read_lock(&nf_ct_frag6_lock); - hlist_for_each_entry(fq, n, &nf_ct_frag6_hash[hash], list) { + read_lock(&nf_frags.lock); + hlist_for_each_entry(fq, n, &nf_frags.hash[hash], q.list) { if (fq->id == id && ipv6_addr_equal(src, &fq->saddr) && ipv6_addr_equal(dst, &fq->daddr)) { - atomic_inc(&fq->refcnt); - read_unlock(&nf_ct_frag6_lock); + atomic_inc(&fq->q.refcnt); + read_unlock(&nf_frags.lock); return fq; } } - read_unlock(&nf_ct_frag6_lock); + read_unlock(&nf_frags.lock); return nf_ct_frag6_create(hash, id, src, dst); } @@ -386,7 +274,7 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, struct sk_buff *prev, *next; int offset, end; - if (fq->last_in & COMPLETE) { + if (fq->q.last_in & COMPLETE) { pr_debug("Allready completed\n"); goto err; } @@ -412,13 +300,13 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, /* If we already have some bits beyond end * or have different end, the segment is corrupted. */ - if (end < fq->len || - ((fq->last_in & LAST_IN) && end != fq->len)) { + if (end < fq->q.len || + ((fq->q.last_in & LAST_IN) && end != fq->q.len)) { pr_debug("already received last fragment\n"); goto err; } - fq->last_in |= LAST_IN; - fq->len = end; + fq->q.last_in |= LAST_IN; + fq->q.len = end; } else { /* Check if the fragment is rounded to 8 bytes. * Required by the RFC. @@ -430,13 +318,13 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, pr_debug("end of fragment not rounded to 8 bytes.\n"); return -1; } - if (end > fq->len) { + if (end > fq->q.len) { /* Some bits beyond end -> corruption. */ - if (fq->last_in & LAST_IN) { + if (fq->q.last_in & LAST_IN) { pr_debug("last packet already reached.\n"); goto err; } - fq->len = end; + fq->q.len = end; } } @@ -458,7 +346,7 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, * this fragment, right? */ prev = NULL; - for (next = fq->fragments; next != NULL; next = next->next) { + for (next = fq->q.fragments; next != NULL; next = next->next) { if (NFCT_FRAG6_CB(next)->offset >= offset) break; /* bingo! */ prev = next; @@ -503,7 +391,7 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, /* next fragment */ NFCT_FRAG6_CB(next)->offset += i; - fq->meat -= i; + fq->q.meat -= i; if (next->ip_summed != CHECKSUM_UNNECESSARY) next->ip_summed = CHECKSUM_NONE; break; @@ -518,9 +406,9 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, if (prev) prev->next = next; else - fq->fragments = next; + fq->q.fragments = next; - fq->meat -= free_it->len; + fq->q.meat -= free_it->len; frag_kfree_skb(free_it, NULL); } } @@ -532,23 +420,23 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, if (prev) prev->next = skb; else - fq->fragments = skb; + fq->q.fragments = skb; skb->dev = NULL; - fq->stamp = skb->tstamp; - fq->meat += skb->len; - atomic_add(skb->truesize, &nf_ct_frag6_mem); + fq->q.stamp = skb->tstamp; + fq->q.meat += skb->len; + atomic_add(skb->truesize, &nf_frags.mem); /* The first fragment. * nhoffset is obtained from the first fragment, of course. */ if (offset == 0) { fq->nhoffset = nhoff; - fq->last_in |= FIRST_IN; + fq->q.last_in |= FIRST_IN; } - write_lock(&nf_ct_frag6_lock); - list_move_tail(&fq->lru_list, &nf_ct_frag6_lru_list); - write_unlock(&nf_ct_frag6_lock); + write_lock(&nf_frags.lock); + list_move_tail(&fq->q.lru_list, &nf_frags.lru_list); + write_unlock(&nf_frags.lock); return 0; err: @@ -567,7 +455,7 @@ err: static struct sk_buff * nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev) { - struct sk_buff *fp, *op, *head = fq->fragments; + struct sk_buff *fp, *op, *head = fq->q.fragments; int payload_len; fq_kill(fq); @@ -577,7 +465,7 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev) /* Unfragmented part is taken from the first segment. */ payload_len = ((head->data - skb_network_header(head)) - - sizeof(struct ipv6hdr) + fq->len - + sizeof(struct ipv6hdr) + fq->q.len - sizeof(struct frag_hdr)); if (payload_len > IPV6_MAXPLEN) { pr_debug("payload len is too large.\n"); @@ -614,7 +502,7 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev) clone->ip_summed = head->ip_summed; NFCT_FRAG6_CB(clone)->orig = NULL; - atomic_add(clone->truesize, &nf_ct_frag6_mem); + atomic_add(clone->truesize, &nf_frags.mem); } /* We have to remove fragment header from datagram and to relocate @@ -628,7 +516,7 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev) skb_shinfo(head)->frag_list = head->next; skb_reset_transport_header(head); skb_push(head, head->data - skb_network_header(head)); - atomic_sub(head->truesize, &nf_ct_frag6_mem); + atomic_sub(head->truesize, &nf_frags.mem); for (fp=head->next; fp; fp = fp->next) { head->data_len += fp->len; @@ -638,12 +526,12 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev) else if (head->ip_summed == CHECKSUM_COMPLETE) head->csum = csum_add(head->csum, fp->csum); head->truesize += fp->truesize; - atomic_sub(fp->truesize, &nf_ct_frag6_mem); + atomic_sub(fp->truesize, &nf_frags.mem); } head->next = NULL; head->dev = dev; - head->tstamp = fq->stamp; + head->tstamp = fq->q.stamp; ipv6_hdr(head)->payload_len = htons(payload_len); /* Yes, and fold redundant checksum back. 8) */ @@ -652,7 +540,7 @@ nf_ct_frag6_reasm(struct nf_ct_frag6_queue *fq, struct net_device *dev) skb_network_header_len(head), head->csum); - fq->fragments = NULL; + fq->q.fragments = NULL; /* all original skbs are linked into the NFCT_FRAG6_CB(head).orig */ fp = skb_shinfo(head)->frag_list; @@ -788,7 +676,7 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb) goto ret_orig; } - if (atomic_read(&nf_ct_frag6_mem) > nf_ct_frag6_high_thresh) + if (atomic_read(&nf_frags.mem) > nf_frags_ctl.high_thresh) nf_ct_frag6_evictor(); fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr); @@ -797,23 +685,23 @@ struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb) goto ret_orig; } - spin_lock(&fq->lock); + spin_lock(&fq->q.lock); if (nf_ct_frag6_queue(fq, clone, fhdr, nhoff) < 0) { - spin_unlock(&fq->lock); + spin_unlock(&fq->q.lock); pr_debug("Can't insert skb to queue\n"); - fq_put(fq, NULL); + fq_put(fq); goto ret_orig; } - if (fq->last_in == (FIRST_IN|LAST_IN) && fq->meat == fq->len) { + if (fq->q.last_in == (FIRST_IN|LAST_IN) && fq->q.meat == fq->q.len) { ret_skb = nf_ct_frag6_reasm(fq, dev); if (ret_skb == NULL) pr_debug("Can't reassemble fragmented packets\n"); } - spin_unlock(&fq->lock); + spin_unlock(&fq->q.lock); - fq_put(fq, NULL); + fq_put(fq); return ret_skb; ret_orig: @@ -859,20 +747,20 @@ int nf_ct_frag6_kfree_frags(struct sk_buff *skb) int nf_ct_frag6_init(void) { - nf_ct_frag6_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^ - (jiffies ^ (jiffies >> 6))); - - setup_timer(&nf_ct_frag6_secret_timer, nf_ct_frag6_secret_rebuild, 0); - nf_ct_frag6_secret_timer.expires = jiffies - + nf_ct_frag6_secret_interval; - add_timer(&nf_ct_frag6_secret_timer); + nf_frags.ctl = &nf_frags_ctl; + nf_frags.hashfn = nf_hashfn; + nf_frags.destructor = nf_frag_free; + nf_frags.skb_free = nf_skb_free; + nf_frags.qsize = sizeof(struct nf_ct_frag6_queue); + inet_frags_init(&nf_frags); return 0; } void nf_ct_frag6_cleanup(void) { - del_timer(&nf_ct_frag6_secret_timer); - nf_ct_frag6_low_thresh = 0; + inet_frags_fini(&nf_frags); + + nf_frags_ctl.low_thresh = 0; nf_ct_frag6_evictor(); } |