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

📄 reassembly.c

📁 ARM 嵌入式 系统 设计与实例开发 实验教材 二源码
💻 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. */#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/sched.h>#include <linux/net.h>#include <linux/netdevice.h>#include <linux/in6.h>#include <linux/ipv6.h>#include <linux/icmpv6.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;	__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;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;	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 __inline__ unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr,					  struct in6_addr *daddr){	unsigned int h = saddr->s6_addr32[3] ^ daddr->s6_addr32[3] ^ id;	h ^= (h>>16);	h ^= (h>>8);	return h & (IP6Q_HASHSZ - 1);}atomic_t ip6_frag_mem = ATOMIC_INIT(0);/* Memory Tracking Functions. */extern __inline__ void frag_kfree_skb(struct sk_buff *skb){	atomic_sub(skb->truesize, &ip6_frag_mem);	kfree_skb(skb);}extern __inline__ void frag_free_queue(struct frag_queue *fq){	atomic_sub(sizeof(struct frag_queue), &ip6_frag_mem);	kfree(fq);}extern __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){	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);		fp = xp;	}	frag_free_queue(fq);}static __inline__ void fq_put(struct frag_queue *fq){	if (atomic_dec_and_test(&fq->refcnt))		ip6_frag_destroy(fq);}/* 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){	int i, progress;	do {		if (atomic_read(&ip6_frag_mem) <= sysctl_ip6frag_low_thresh)			return;		progress = 0;		for (i = 0; i < IP6Q_HASHSZ; i++) {			struct frag_queue *fq;			if (ip6_frag_hash[i] == NULL)				continue;			read_lock(&ip6_frag_lock);			if ((fq = ip6_frag_hash[i]) != NULL) {				/* find the oldest queue for this hash bucket */				while (fq->next)					fq = fq->next;				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);				IP6_INC_STATS_BH(Ip6ReasmFails);				progress = 1;				continue;			}			read_unlock(&ip6_frag_lock);		}	} while (progress);}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(Ip6ReasmTimeout);	IP6_INC_STATS_BH(Ip6ReasmFails);	/* 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);}/* 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);			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];	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);	/* init_timer has been done by the memset */	fq->timer.function = ip6_frag_expire;	fq->timer.data = (long) fq;	fq->lock = SPIN_LOCK_UNLOCKED;	atomic_set(&fq->refcnt, 1);	return ip6_frag_intern(hash, fq);oom:	IP6_INC_STATS_BH(Ip6ReasmFails);	return NULL;}static __inline__ struct frag_queue *fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst){	struct frag_queue *fq;	unsigned int hash = ip6qhashfn(id, src, dst);	read_lock(&ip6_frag_lock);	for(fq = ip6_frag_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);

⌨️ 快捷键说明

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