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

📄 reassembly.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	IPv6 fragment reassembly *	Linux INET6 implementation  * *	Authors: *	Pedro Roque		<roque@di.fc.ul.pt>	 * *	$Id: reassembly.c,v 1.26 2001/03/07 22:00:57 davem Exp $ * *	Based on: net/ipv4/ip_fragment.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. *//*  *	Fixes:	 *	Andi Kleen	Make it work with multiple hosts. *			More RFC compliance. * *      Horst von Brand Add missing #include <linux/string.h> *	Alexey Kuznetsov	SMP races, threading, cleanup. *	Patrick McHardy		LRU queue of frag heads for evictor. *	Mitsuru KANDA @USAGI	Register inet6_protocol{}. *	David Stevens and *	YOSHIFUJI,H. @USAGI	Always remove fragment header to *				calculate ICV correctly. */#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>int sysctl_ip6frag_high_thresh = 256*1024;int sysctl_ip6frag_low_thresh = 192*1024;int sysctl_ip6frag_time = IPV6_FRAG_TIMEOUT;struct ip6frag_skb_cb{	struct inet6_skb_parm	h;	int			offset;};#define FRAG6_CB(skb)	((struct ip6frag_skb_cb*)((skb)->cb))/* *	Equivalent of ipv4 struct ipq */struct frag_queue{	struct frag_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;	int			iif;	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 frag_queue	**pprev;};/* Hash table. */#define IP6Q_HASHSZ	64static struct frag_queue *ip6_frag_hash[IP6Q_HASHSZ];static rwlock_t ip6_frag_lock = RW_LOCK_UNLOCKED;static u32 ip6_frag_hash_rnd;static LIST_HEAD(ip6_frag_lru_list);int ip6_frag_nqueues = 0;static __inline__ void __fq_unlink(struct frag_queue *fq){	if(fq->next)		fq->next->pprev = fq->pprev;	*fq->pprev = fq->next;	list_del(&fq->lru_list);	ip6_frag_nqueues--;}static __inline__ void fq_unlink(struct frag_queue *fq){	write_lock(&ip6_frag_lock);	__fq_unlink(fq);	write_unlock(&ip6_frag_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 += ip6_frag_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 & (IP6Q_HASHSZ - 1);}static struct timer_list ip6_frag_secret_timer;int sysctl_ip6frag_secret_interval = 10 * 60 * HZ;static void ip6_frag_secret_rebuild(unsigned long dummy){	unsigned long now = jiffies;	int i;	write_lock(&ip6_frag_lock);	get_random_bytes(&ip6_frag_hash_rnd, sizeof(u32));	for (i = 0; i < IP6Q_HASHSZ; i++) {		struct frag_queue *q;		q = ip6_frag_hash[i];		while (q) {			struct frag_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 = ip6_frag_hash[hval]) != NULL)					q->next->pprev = &q->next;				ip6_frag_hash[hval] = q;				q->pprev = &ip6_frag_hash[hval];			}			q = next;		}	}	write_unlock(&ip6_frag_lock);	mod_timer(&ip6_frag_secret_timer, now + sysctl_ip6frag_secret_interval);}atomic_t ip6_frag_mem = ATOMIC_INIT(0);/* Memory Tracking Functions. */static inline void frag_kfree_skb(struct sk_buff *skb, int *work){	if (work)		*work -= skb->truesize;	atomic_sub(skb->truesize, &ip6_frag_mem);	kfree_skb(skb);}static inline void frag_free_queue(struct frag_queue *fq, int *work){	if (work)		*work -= sizeof(struct frag_queue);	atomic_sub(sizeof(struct frag_queue), &ip6_frag_mem);	kfree(fq);}static inline struct frag_queue *frag_alloc_queue(void){	struct frag_queue *fq = kmalloc(sizeof(struct frag_queue), GFP_ATOMIC);	if(!fq)		return NULL;	atomic_add(sizeof(struct frag_queue), &ip6_frag_mem);	return fq;}/* Destruction primitives. *//* Complete destruction of fq. */static void ip6_frag_destroy(struct frag_queue *fq, 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 frag_queue *fq, int *work){	if (atomic_dec_and_test(&fq->refcnt))		ip6_frag_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 frag_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 ip6_evictor(void){	struct frag_queue *fq;	struct list_head *tmp;	int work;	work = atomic_read(&ip6_frag_mem) - sysctl_ip6frag_low_thresh;	if (work <= 0)		return;	while(work > 0) {		read_lock(&ip6_frag_lock);		if (list_empty(&ip6_frag_lru_list)) {			read_unlock(&ip6_frag_lock);			return;		}		tmp = ip6_frag_lru_list.next;		fq = list_entry(tmp, struct frag_queue, lru_list);		atomic_inc(&fq->refcnt);		read_unlock(&ip6_frag_lock);		spin_lock(&fq->lock);		if (!(fq->last_in&COMPLETE))			fq_kill(fq);		spin_unlock(&fq->lock);		fq_put(fq, &work);		IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);	}}static void ip6_frag_expire(unsigned long data){	struct frag_queue *fq = (struct frag_queue *) data;	spin_lock(&fq->lock);	if (fq->last_in & COMPLETE)		goto out;	fq_kill(fq);	IP6_INC_STATS_BH(IPSTATS_MIB_REASMTIMEOUT);	IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);	/* Send error only if the first segment arrived. */	if (fq->last_in&FIRST_IN && fq->fragments) {		struct net_device *dev = dev_get_by_index(fq->iif);		/*		   But use as source device on which LAST ARRIVED		   segment was received. And do not use fq->dev		   pointer directly, device might already disappeared.		 */		if (dev) {			fq->fragments->dev = dev;			icmpv6_send(fq->fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0,				    dev);			dev_put(dev);		}	}out:	spin_unlock(&fq->lock);	fq_put(fq, NULL);}/* Creation primitives. */static struct frag_queue *ip6_frag_intern(unsigned int hash,					  struct frag_queue *fq_in){	struct frag_queue *fq;	write_lock(&ip6_frag_lock);#ifdef CONFIG_SMP	for (fq = ip6_frag_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(&ip6_frag_lock);			fq_in->last_in |= COMPLETE;			fq_put(fq_in, NULL);			return fq;		}	}#endif	fq = fq_in;	if (!mod_timer(&fq->timer, jiffies + sysctl_ip6frag_time))		atomic_inc(&fq->refcnt);	atomic_inc(&fq->refcnt);	if((fq->next = ip6_frag_hash[hash]) != NULL)		fq->next->pprev = &fq->next;	ip6_frag_hash[hash] = fq;	fq->pprev = &ip6_frag_hash[hash];	INIT_LIST_HEAD(&fq->lru_list);	list_add_tail(&fq->lru_list, &ip6_frag_lru_list);	ip6_frag_nqueues++;	write_unlock(&ip6_frag_lock);	return fq;}static struct frag_queue *ip6_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr *dst){	struct frag_queue *fq;	if ((fq = frag_alloc_queue()) == NULL)		goto oom;	memset(fq, 0, sizeof(struct frag_queue));	fq->id = id;	ipv6_addr_copy(&fq->saddr, src);	ipv6_addr_copy(&fq->daddr, dst);

⌨️ 快捷键说明

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