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

📄 ip_conntrack_core.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* Connection state tracking for netfilter.  This is separated from,   but required by, the NAT layer; it can also be used by an iptables   extension. *//* (C) 1999-2001 Paul `Rusty' Russell   * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 23 Apr 2001: Harald Welte <laforge@gnumonks.org> * 	- new API and handling of conntrack/nat helpers * 	- now capable of multiple expectations for one master * 16 Jul 2002: Harald Welte <laforge@gnumonks.org> * 	- add usage/reference counts to ip_conntrack_expect *	- export ip_conntrack[_expect]_{find_get,put} functions * */#include <linux/config.h>#include <linux/types.h>#include <linux/icmp.h>#include <linux/ip.h>#include <linux/netfilter.h>#include <linux/netfilter_ipv4.h>#include <linux/module.h>#include <linux/skbuff.h>#include <linux/proc_fs.h>#include <linux/vmalloc.h>#include <net/checksum.h>#include <net/ip.h>#include <linux/stddef.h>#include <linux/sysctl.h>#include <linux/slab.h>#include <linux/random.h>#include <linux/jhash.h>#include <linux/err.h>#include <linux/percpu.h>#include <linux/moduleparam.h>#include <linux/notifier.h>/* ip_conntrack_lock protects the main hash table, protocol/helper/expected   registrations, conntrack timers*/#define ASSERT_READ_LOCK(x)#define ASSERT_WRITE_LOCK(x)#include <linux/netfilter_ipv4/ip_conntrack.h>#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>#include <linux/netfilter_ipv4/ip_conntrack_helper.h>#include <linux/netfilter_ipv4/ip_conntrack_core.h>#include <linux/netfilter_ipv4/listhelp.h>#define IP_CONNTRACK_VERSION	"2.4"#if 0#define DEBUGP printk#else#define DEBUGP(format, args...)#endifDEFINE_RWLOCK(ip_conntrack_lock);/* ip_conntrack_standalone needs this */atomic_t ip_conntrack_count = ATOMIC_INIT(0);void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL;LIST_HEAD(ip_conntrack_expect_list);struct ip_conntrack_protocol *ip_ct_protos[MAX_IP_CT_PROTO];static LIST_HEAD(helpers);unsigned int ip_conntrack_htable_size = 0;int ip_conntrack_max;struct list_head *ip_conntrack_hash;static kmem_cache_t *ip_conntrack_cachep __read_mostly;static kmem_cache_t *ip_conntrack_expect_cachep __read_mostly;struct ip_conntrack ip_conntrack_untracked;unsigned int ip_ct_log_invalid;static LIST_HEAD(unconfirmed);static int ip_conntrack_vmalloc;static unsigned int ip_conntrack_next_id = 1;static unsigned int ip_conntrack_expect_next_id = 1;#ifdef CONFIG_IP_NF_CONNTRACK_EVENTSstruct notifier_block *ip_conntrack_chain;struct notifier_block *ip_conntrack_expect_chain;DEFINE_PER_CPU(struct ip_conntrack_ecache, ip_conntrack_ecache);/* deliver cached events and clear cache entry - must be called with locally * disabled softirqs */static inline void__ip_ct_deliver_cached_events(struct ip_conntrack_ecache *ecache){	DEBUGP("ecache: delivering events for %p\n", ecache->ct);	if (is_confirmed(ecache->ct) && !is_dying(ecache->ct) && ecache->events)		notifier_call_chain(&ip_conntrack_chain, ecache->events,				    ecache->ct);	ecache->events = 0;	ip_conntrack_put(ecache->ct);	ecache->ct = NULL;}/* Deliver all cached events for a particular conntrack. This is called * by code prior to async packet handling or freeing the skb */void ip_ct_deliver_cached_events(const struct ip_conntrack *ct){	struct ip_conntrack_ecache *ecache;		local_bh_disable();	ecache = &__get_cpu_var(ip_conntrack_ecache);	if (ecache->ct == ct)		__ip_ct_deliver_cached_events(ecache);	local_bh_enable();}void __ip_ct_event_cache_init(struct ip_conntrack *ct){	struct ip_conntrack_ecache *ecache;	/* take care of delivering potentially old events */	ecache = &__get_cpu_var(ip_conntrack_ecache);	BUG_ON(ecache->ct == ct);	if (ecache->ct)		__ip_ct_deliver_cached_events(ecache);	/* initialize for this conntrack/packet */	ecache->ct = ct;	nf_conntrack_get(&ct->ct_general);}/* flush the event cache - touches other CPU's data and must not be called while * packets are still passing through the code */static void ip_ct_event_cache_flush(void){	struct ip_conntrack_ecache *ecache;	int cpu;	for_each_cpu(cpu) {		ecache = &per_cpu(ip_conntrack_ecache, cpu);		if (ecache->ct)			ip_conntrack_put(ecache->ct);	}}#elsestatic inline void ip_ct_event_cache_flush(void) {}#endif /* CONFIG_IP_NF_CONNTRACK_EVENTS */DEFINE_PER_CPU(struct ip_conntrack_stat, ip_conntrack_stat);static int ip_conntrack_hash_rnd_initted;static unsigned int ip_conntrack_hash_rnd;static u_int32_t __hash_conntrack(const struct ip_conntrack_tuple *tuple,			    unsigned int size, unsigned int rnd){	return (jhash_3words(tuple->src.ip,	                     (tuple->dst.ip ^ tuple->dst.protonum),	                     (tuple->src.u.all | (tuple->dst.u.all << 16)),	                     rnd) % size);}static u_int32_thash_conntrack(const struct ip_conntrack_tuple *tuple){	return __hash_conntrack(tuple, ip_conntrack_htable_size,				ip_conntrack_hash_rnd);}intip_ct_get_tuple(const struct iphdr *iph,		const struct sk_buff *skb,		unsigned int dataoff,		struct ip_conntrack_tuple *tuple,		const struct ip_conntrack_protocol *protocol){	/* Never happen */	if (iph->frag_off & htons(IP_OFFSET)) {		printk("ip_conntrack_core: Frag of proto %u.\n",		       iph->protocol);		return 0;	}	tuple->src.ip = iph->saddr;	tuple->dst.ip = iph->daddr;	tuple->dst.protonum = iph->protocol;	tuple->dst.dir = IP_CT_DIR_ORIGINAL;	return protocol->pkt_to_tuple(skb, dataoff, tuple);}intip_ct_invert_tuple(struct ip_conntrack_tuple *inverse,		   const struct ip_conntrack_tuple *orig,		   const struct ip_conntrack_protocol *protocol){	inverse->src.ip = orig->dst.ip;	inverse->dst.ip = orig->src.ip;	inverse->dst.protonum = orig->dst.protonum;	inverse->dst.dir = !orig->dst.dir;	return protocol->invert_tuple(inverse, orig);}/* ip_conntrack_expect helper functions */void ip_ct_unlink_expect(struct ip_conntrack_expect *exp){	ASSERT_WRITE_LOCK(&ip_conntrack_lock);	IP_NF_ASSERT(!timer_pending(&exp->timeout));	list_del(&exp->list);	CONNTRACK_STAT_INC(expect_delete);	exp->master->expecting--;	ip_conntrack_expect_put(exp);}static void expectation_timed_out(unsigned long ul_expect){	struct ip_conntrack_expect *exp = (void *)ul_expect;	write_lock_bh(&ip_conntrack_lock);	ip_ct_unlink_expect(exp);	write_unlock_bh(&ip_conntrack_lock);	ip_conntrack_expect_put(exp);}struct ip_conntrack_expect *__ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple){	struct ip_conntrack_expect *i;		list_for_each_entry(i, &ip_conntrack_expect_list, list) {		if (ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)) {			atomic_inc(&i->use);			return i;		}	}	return NULL;}/* Just find a expectation corresponding to a tuple. */struct ip_conntrack_expect *ip_conntrack_expect_find(const struct ip_conntrack_tuple *tuple){	struct ip_conntrack_expect *i;		read_lock_bh(&ip_conntrack_lock);	i = __ip_conntrack_expect_find(tuple);	read_unlock_bh(&ip_conntrack_lock);	return i;}/* If an expectation for this connection is found, it gets delete from * global list then returned. */static struct ip_conntrack_expect *find_expectation(const struct ip_conntrack_tuple *tuple){	struct ip_conntrack_expect *i;	list_for_each_entry(i, &ip_conntrack_expect_list, list) {		/* If master is not in hash table yet (ie. packet hasn't left		   this machine yet), how can other end know about expected?		   Hence these are not the droids you are looking for (if		   master ct never got confirmed, we'd hold a reference to it		   and weird things would happen to future packets). */		if (ip_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)		    && is_confirmed(i->master)) {			if (i->flags & IP_CT_EXPECT_PERMANENT) {				atomic_inc(&i->use);				return i;			} else if (del_timer(&i->timeout)) {				ip_ct_unlink_expect(i);				return i;			}		}	}	return NULL;}/* delete all expectations for this conntrack */void ip_ct_remove_expectations(struct ip_conntrack *ct){	struct ip_conntrack_expect *i, *tmp;	/* Optimization: most connection never expect any others. */	if (ct->expecting == 0)		return;	list_for_each_entry_safe(i, tmp, &ip_conntrack_expect_list, list) {		if (i->master == ct && del_timer(&i->timeout)) {			ip_ct_unlink_expect(i);			ip_conntrack_expect_put(i);		}	}}static voidclean_from_lists(struct ip_conntrack *ct){	unsigned int ho, hr;		DEBUGP("clean_from_lists(%p)\n", ct);	ASSERT_WRITE_LOCK(&ip_conntrack_lock);	ho = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);	hr = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);	LIST_DELETE(&ip_conntrack_hash[ho], &ct->tuplehash[IP_CT_DIR_ORIGINAL]);	LIST_DELETE(&ip_conntrack_hash[hr], &ct->tuplehash[IP_CT_DIR_REPLY]);	/* Destroy all pending expectations */	ip_ct_remove_expectations(ct);}static voiddestroy_conntrack(struct nf_conntrack *nfct){	struct ip_conntrack *ct = (struct ip_conntrack *)nfct;	struct ip_conntrack_protocol *proto;	DEBUGP("destroy_conntrack(%p)\n", ct);	IP_NF_ASSERT(atomic_read(&nfct->use) == 0);	IP_NF_ASSERT(!timer_pending(&ct->timeout));	ip_conntrack_event(IPCT_DESTROY, ct);	set_bit(IPS_DYING_BIT, &ct->status);	/* To make sure we don't get any weird locking issues here:	 * destroy_conntrack() MUST NOT be called with a write lock	 * to ip_conntrack_lock!!! -HW */	proto = __ip_conntrack_proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum);	if (proto && proto->destroy)		proto->destroy(ct);	if (ip_conntrack_destroyed)		ip_conntrack_destroyed(ct);	write_lock_bh(&ip_conntrack_lock);	/* Expectations will have been removed in clean_from_lists,	 * except TFTP can create an expectation on the first packet,	 * before connection is in the list, so we need to clean here,	 * too. */	ip_ct_remove_expectations(ct);	/* We overload first tuple to link into unconfirmed list. */	if (!is_confirmed(ct)) {		BUG_ON(list_empty(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list));		list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list);	}	CONNTRACK_STAT_INC(delete);	write_unlock_bh(&ip_conntrack_lock);	if (ct->master)		ip_conntrack_put(ct->master);	DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct);	ip_conntrack_free(ct);}static void death_by_timeout(unsigned long ul_conntrack){	struct ip_conntrack *ct = (void *)ul_conntrack;	write_lock_bh(&ip_conntrack_lock);	/* Inside lock so preempt is disabled on module removal path.	 * Otherwise we can get spurious warnings. */	CONNTRACK_STAT_INC(delete_list);	clean_from_lists(ct);	write_unlock_bh(&ip_conntrack_lock);	ip_conntrack_put(ct);}static inline intconntrack_tuple_cmp(const struct ip_conntrack_tuple_hash *i,		    const struct ip_conntrack_tuple *tuple,		    const struct ip_conntrack *ignored_conntrack){	ASSERT_READ_LOCK(&ip_conntrack_lock);	return tuplehash_to_ctrack(i) != ignored_conntrack		&& ip_ct_tuple_equal(tuple, &i->tuple);}struct ip_conntrack_tuple_hash *__ip_conntrack_find(const struct ip_conntrack_tuple *tuple,		    const struct ip_conntrack *ignored_conntrack){	struct ip_conntrack_tuple_hash *h;	unsigned int hash = hash_conntrack(tuple);	ASSERT_READ_LOCK(&ip_conntrack_lock);	list_for_each_entry(h, &ip_conntrack_hash[hash], list) {		if (conntrack_tuple_cmp(h, tuple, ignored_conntrack)) {			CONNTRACK_STAT_INC(found);			return h;		}		CONNTRACK_STAT_INC(searched);	}	return NULL;}/* Find a connection corresponding to a tuple. */struct ip_conntrack_tuple_hash *ip_conntrack_find_get(const struct ip_conntrack_tuple *tuple,		      const struct ip_conntrack *ignored_conntrack){	struct ip_conntrack_tuple_hash *h;	read_lock_bh(&ip_conntrack_lock);	h = __ip_conntrack_find(tuple, ignored_conntrack);	if (h)		atomic_inc(&tuplehash_to_ctrack(h)->ct_general.use);	read_unlock_bh(&ip_conntrack_lock);	return h;}static void __ip_conntrack_hash_insert(struct ip_conntrack *ct,					unsigned int hash,					unsigned int repl_hash) {	ct->id = ++ip_conntrack_next_id;	list_prepend(&ip_conntrack_hash[hash],		     &ct->tuplehash[IP_CT_DIR_ORIGINAL].list);	list_prepend(&ip_conntrack_hash[repl_hash],		     &ct->tuplehash[IP_CT_DIR_REPLY].list);}void ip_conntrack_hash_insert(struct ip_conntrack *ct){	unsigned int hash, repl_hash;	hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);	repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);	write_lock_bh(&ip_conntrack_lock);	__ip_conntrack_hash_insert(ct, hash, repl_hash);	write_unlock_bh(&ip_conntrack_lock);}/* Confirm a connection given skb; places it in hash table */int__ip_conntrack_confirm(struct sk_buff **pskb){	unsigned int hash, repl_hash;	struct ip_conntrack *ct;	enum ip_conntrack_info ctinfo;	ct = ip_conntrack_get(*pskb, &ctinfo);	/* ipt_REJECT uses ip_conntrack_attach to attach related	   ICMP/TCP RST packets in other direction.  Actual packet	   which created connection will be IP_CT_NEW or for an	   expected connection, IP_CT_RELATED. */	if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)		return NF_ACCEPT;	hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);	repl_hash = hash_conntrack(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);	/* We're not in hash table, and we refuse to set up related	   connections for unconfirmed conns.  But packet copies and	   REJECT will give spurious warnings here. */	/* IP_NF_ASSERT(atomic_read(&ct->ct_general.use) == 1); */	/* No external references means noone else could have           confirmed us. */	IP_NF_ASSERT(!is_confirmed(ct));	DEBUGP("Confirming conntrack %p\n", ct);	write_lock_bh(&ip_conntrack_lock);	/* See if there's one in the list already, including reverse:           NAT could have grabbed it without realizing, since we're           not in the hash.  If there is, we lost race. */	if (!LIST_FIND(&ip_conntrack_hash[hash],		       conntrack_tuple_cmp,		       struct ip_conntrack_tuple_hash *,		       &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple, NULL)	    && !LIST_FIND(&ip_conntrack_hash[repl_hash],			  conntrack_tuple_cmp,			  struct ip_conntrack_tuple_hash *,			  &ct->tuplehash[IP_CT_DIR_REPLY].tuple, NULL)) {		/* Remove from unconfirmed list */		list_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].list);		__ip_conntrack_hash_insert(ct, hash, repl_hash);		/* Timer relative to confirmation time, not original		   setting time, otherwise we'd get timer wrap in		   weird delay cases. */		ct->timeout.expires += jiffies;		add_timer(&ct->timeout);		atomic_inc(&ct->ct_general.use);		set_bit(IPS_CONFIRMED_BIT, &ct->status);		CONNTRACK_STAT_INC(insert);		write_unlock_bh(&ip_conntrack_lock);		if (ct->helper)			ip_conntrack_event_cache(IPCT_HELPER, *pskb);#ifdef CONFIG_IP_NF_NAT_NEEDED		if (test_bit(IPS_SRC_NAT_DONE_BIT, &ct->status) ||		    test_bit(IPS_DST_NAT_DONE_BIT, &ct->status))			ip_conntrack_event_cache(IPCT_NATINFO, *pskb);#endif		ip_conntrack_event_cache(master_ct(ct) ?					 IPCT_RELATED : IPCT_NEW, *pskb);		return NF_ACCEPT;	}	CONNTRACK_STAT_INC(insert_failed);	write_unlock_bh(&ip_conntrack_lock);	return NF_DROP;}/* Returns true if a connection correspondings to the tuple (required   for NAT). */int

⌨️ 快捷键说明

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