⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nf_conntrack_reasm.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * IPv6 fragment reassembly for connection tracking * * Copyright (C)2004 USAGI/WIDE Project * * Author: *	Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> * * Based on: net/ipv6/reassembly.c * * 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. */#include <linux/config.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/string.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/jiffies.h>#include <linux/net.h>#include <linux/list.h>#include <linux/netdevice.h>#include <linux/in6.h>#include <linux/ipv6.h>#include <linux/icmpv6.h>#include <linux/random.h>#include <linux/jhash.h>#include <net/sock.h>#include <net/snmp.h>#include <net/ipv6.h>#include <net/protocol.h>#include <net/transp_v6.h>#include <net/rawv6.h>#include <net/ndisc.h>#include <net/addrconf.h>#include <linux/sysctl.h>#include <linux/netfilter.h>#include <linux/netfilter_ipv6.h>#include <linux/kernel.h>#include <linux/module.h>#if 0#define DEBUGP printk#else#define DEBUGP(format, args...)#endif#define NF_CT_FRAG6_HIGH_THRESH 262144 /* == 256*1024 */#define NF_CT_FRAG6_LOW_THRESH 196608  /* == 192*1024 */#define NF_CT_FRAG6_TIMEOUT IPV6_FRAG_TIMEOUTunsigned int nf_ct_frag6_high_thresh = 256*1024;unsigned int nf_ct_frag6_low_thresh = 192*1024;unsigned long nf_ct_frag6_timeout = IPV6_FRAG_TIMEOUT;struct nf_ct_frag6_skb_cb{	struct inet6_skb_parm	h;	int			offset;	struct sk_buff		*orig;};#define NFCT_FRAG6_CB(skb)	((struct nf_ct_frag6_skb_cb*)((skb)->cb))struct nf_ct_frag6_queue{	struct nf_ct_frag6_queue	*next;	struct list_head lru_list;		/* lru list member	*/	__u32			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;	struct timeval		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;	struct nf_ct_frag6_queue	**pprev;};/* Hash table. */#define FRAG6Q_HASHSZ	64static struct nf_ct_frag6_queue *nf_ct_frag6_hash[FRAG6Q_HASHSZ];static rwlock_t nf_ct_frag6_lock = RW_LOCK_UNLOCKED;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){	if (fq->next)		fq->next->pprev = fq->pprev;	*fq->pprev = fq->next;	list_del(&fq->lru_list);	nf_ct_frag6_nqueues--;}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 unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr,			       struct in6_addr *daddr){	u32 a, b, c;	a = saddr->s6_addr32[0];	b = saddr->s6_addr32[1];	c = saddr->s6_addr32[2];	a += JHASH_GOLDEN_RATIO;	b += JHASH_GOLDEN_RATIO;	c += nf_ct_frag6_hash_rnd;	__jhash_mix(a, b, c);	a += saddr->s6_addr32[3];	b += daddr->s6_addr32[0];	c += daddr->s6_addr32[1];	__jhash_mix(a, b, c);	a += daddr->s6_addr32[2];	b += daddr->s6_addr32[3];	c += id;	__jhash_mix(a, b, c);	return c & (FRAG6Q_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){	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;		q = nf_ct_frag6_hash[i];		while (q) {			struct nf_ct_frag6_queue *next = q->next;			unsigned int hval = ip6qhashfn(q->id,						       &q->saddr,						       &q->daddr);			if (hval != i) {				/* Unlink. */				if (q->next)					q->next->pprev = q->pprev;				*q->pprev = q->next;				/* Relink to new hash chain. */				if ((q->next = nf_ct_frag6_hash[hval]) != NULL)					q->next->pprev = &q->next;				nf_ct_frag6_hash[hval] = q;				q->pprev = &nf_ct_frag6_hash[hval];			}			q = next;		}	}	write_unlock(&nf_ct_frag6_lock);	mod_timer(&nf_ct_frag6_secret_timer, now + nf_ct_frag6_secret_interval);}atomic_t nf_ct_frag6_mem = ATOMIC_INIT(0);/* 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);	kfree_skb(skb);}static inline void frag_free_queue(struct nf_ct_frag6_queue *fq,				   unsigned int *work){	if (work)		*work -= sizeof(struct nf_ct_frag6_queue);	atomic_sub(sizeof(struct nf_ct_frag6_queue), &nf_ct_frag6_mem);	kfree(fq);}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);	if (!fq)		return NULL;	atomic_add(sizeof(struct nf_ct_frag6_queue), &nf_ct_frag6_mem);	return fq;}/* Destruction primitives. *//* Complete destruction of fq. */static void nf_ct_frag6_destroy(struct nf_ct_frag6_queue *fq,				unsigned int *work){	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);}/* Kill fq entry. It is not destroyed immediately, * because caller (and someone more) holds reference count. */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;	}}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);	}}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);	if (fq->last_in & COMPLETE)		goto out;	fq_kill(fq);out:	spin_unlock(&fq->lock);	fq_put(fq, NULL);}/* Creation primitives. */static struct nf_ct_frag6_queue *nf_ct_frag6_intern(unsigned int hash,					  struct nf_ct_frag6_queue *fq_in){	struct nf_ct_frag6_queue *fq;	write_lock(&nf_ct_frag6_lock);#ifdef CONFIG_SMP	for (fq = nf_ct_frag6_hash[hash]; fq; fq = fq->next) {		if (fq->id == fq_in->id && 		    !ipv6_addr_cmp(&fq_in->saddr, &fq->saddr) &&		    !ipv6_addr_cmp(&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);			return fq;		}	}#endif	fq = fq_in;	if (!mod_timer(&fq->timer, jiffies + nf_ct_frag6_timeout))		atomic_inc(&fq->refcnt);	atomic_inc(&fq->refcnt);	if ((fq->next = nf_ct_frag6_hash[hash]) != NULL)		fq->next->pprev = &fq->next;	nf_ct_frag6_hash[hash] = fq;	fq->pprev = &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);	return fq;}static struct nf_ct_frag6_queue *nf_ct_frag6_create(unsigned int hash, u32 id, struct in6_addr *src,				   struct in6_addr *dst){	struct nf_ct_frag6_queue *fq;	if ((fq = frag_alloc_queue()) == NULL) {		DEBUGP("Can't alloc new queue\n");		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);	init_timer(&fq->timer);	fq->timer.function = nf_ct_frag6_expire;	fq->timer.data = (long) fq;	fq->lock = SPIN_LOCK_UNLOCKED;	atomic_set(&fq->refcnt, 1);	return nf_ct_frag6_intern(hash, fq);oom:	return NULL;}static __inline__ struct nf_ct_frag6_queue *fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst){	struct nf_ct_frag6_queue *fq;	unsigned int hash = ip6qhashfn(id, src, dst);	read_lock(&nf_ct_frag6_lock);	for (fq = nf_ct_frag6_hash[hash]; fq; fq = fq->next) {		if (fq->id == id && 		    !ipv6_addr_cmp(src, &fq->saddr) &&		    !ipv6_addr_cmp(dst, &fq->daddr)) {			atomic_inc(&fq->refcnt);			read_unlock(&nf_ct_frag6_lock);			return fq;		}	}	read_unlock(&nf_ct_frag6_lock);	return nf_ct_frag6_create(hash, id, src, dst);}static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb, 			     struct frag_hdr *fhdr, int nhoff){	struct sk_buff *prev, *next;	int offset, end;	if (fq->last_in & COMPLETE) {		DEBUGP("Allready completed\n");		goto err;	}	offset = ntohs(fhdr->frag_off) & ~0x7;	end = offset + (ntohs(skb->nh.ipv6h->payload_len) -			((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));	if ((unsigned int)end > IPV6_MAXPLEN) {		DEBUGP("offset is too large.\n"); 		return -1;	} 	if (skb->ip_summed == CHECKSUM_HW) 		skb->csum = csum_sub(skb->csum, 				     csum_partial(skb->nh.raw,						  (u8*)(fhdr + 1) - skb->nh.raw,						  0));	/* Is this the final fragment? */	if (!(fhdr->frag_off & htons(IP6_MF))) {		/* 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)) {			DEBUGP("already received last fragment\n");			goto err;		}		fq->last_in |= LAST_IN;		fq->len = end;	} else {		/* Check if the fragment is rounded to 8 bytes.		 * Required by the RFC.		 */		if (end & 0x7) {			/* RFC2460 says always send parameter problem in			 * this case. -DaveM

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -