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

📄 ip_nat_core.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* NAT for netfilter; shared with compatibility layer. *//* (c) 1999 Paul `Rusty' Russell.  Licenced under the GNU General   Public Licence. */#ifdef MODULE#define __NO_VERSION__#endif#include <linux/version.h>#include <linux/module.h>#include <linux/types.h>#include <linux/timer.h>#include <linux/skbuff.h>#include <linux/netfilter_ipv4.h>#include <linux/brlock.h>#include <net/checksum.h>#include <net/icmp.h>#include <net/ip.h>#include <net/tcp.h>  /* For tcp_prot in getorigdst */#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)#include <linux/netfilter_ipv4/ip_nat.h>#include <linux/netfilter_ipv4/ip_nat_protocol.h>#include <linux/netfilter_ipv4/ip_nat_core.h>#include <linux/netfilter_ipv4/ip_nat_helper.h>#include <linux/netfilter_ipv4/listhelp.h>#if 0#define DEBUGP printk#else#define DEBUGP(format, args...)#endifDECLARE_RWLOCK(ip_nat_lock);#define IP_NAT_HTABLE_SIZE 64static struct list_head bysource[IP_NAT_HTABLE_SIZE];static struct list_head byipsproto[IP_NAT_HTABLE_SIZE];LIST_HEAD(protos);static LIST_HEAD(helpers);extern struct ip_nat_protocol unknown_nat_protocol;/* We keep extra hashes for each conntrack, for fast searching. */static inline size_thash_by_ipsproto(u_int32_t src, u_int32_t dst, u_int16_t proto){	/* Modified src and dst, to ensure we don't create two           identical streams. */	return (src + dst + proto) % IP_NAT_HTABLE_SIZE;}static inline size_thash_by_src(const struct ip_conntrack_manip *manip, u_int16_t proto){	/* Original src, to ensure we map it consistently if poss. */	return (manip->ip + manip->u.all + proto) % IP_NAT_HTABLE_SIZE;}/* Noone using conntrack by the time this called. */static void ip_nat_cleanup_conntrack(struct ip_conntrack *conn){	struct ip_nat_info *info = &conn->nat.info;	if (!info->initialized)		return;	IP_NF_ASSERT(info->bysource.conntrack);	IP_NF_ASSERT(info->byipsproto.conntrack);	WRITE_LOCK(&ip_nat_lock);	LIST_DELETE(&bysource[hash_by_src(&conn->tuplehash[IP_CT_DIR_ORIGINAL]					  .tuple.src,					  conn->tuplehash[IP_CT_DIR_ORIGINAL]					  .tuple.dst.protonum)],		    &info->bysource);	LIST_DELETE(&byipsproto		    [hash_by_ipsproto(conn->tuplehash[IP_CT_DIR_REPLY]				      .tuple.src.ip,				      conn->tuplehash[IP_CT_DIR_REPLY]				      .tuple.dst.ip,				      conn->tuplehash[IP_CT_DIR_REPLY]				      .tuple.dst.protonum)],		    &info->byipsproto);	WRITE_UNLOCK(&ip_nat_lock);}/* We do checksum mangling, so if they were wrong before they're still * wrong.  Also works for incomplete packets (eg. ICMP dest * unreachables.) */u_int16_tip_nat_cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck){	u_int32_t diffs[] = { oldvalinv, newval };	return csum_fold(csum_partial((char *)diffs, sizeof(diffs),				      oldcheck^0xFFFF));}static inline int cmp_proto(const struct ip_nat_protocol *i, int proto){	return i->protonum == proto;}struct ip_nat_protocol *find_nat_proto(u_int16_t protonum){	struct ip_nat_protocol *i;	MUST_BE_READ_LOCKED(&ip_nat_lock);	i = LIST_FIND(&protos, cmp_proto, struct ip_nat_protocol *, protonum);	if (!i)		i = &unknown_nat_protocol;	return i;}/* Is this tuple already taken? (not by us) */intip_nat_used_tuple(const struct ip_conntrack_tuple *tuple,		  const struct ip_conntrack *ignored_conntrack){	/* Conntrack tracking doesn't keep track of outgoing tuples; only	   incoming ones.  NAT means they don't have a fixed mapping,	   so we invert the tuple and look for the incoming reply.	   We could keep a separate hash if this proves too slow. */	struct ip_conntrack_tuple reply;	invert_tuplepr(&reply, tuple);	return ip_conntrack_tuple_taken(&reply, ignored_conntrack);}/* Does tuple + the source manip come within the range mr */static intin_range(const struct ip_conntrack_tuple *tuple,	 const struct ip_conntrack_manip *manip,	 const struct ip_nat_multi_range *mr){	struct ip_nat_protocol *proto = find_nat_proto(tuple->dst.protonum);	unsigned int i;	struct ip_conntrack_tuple newtuple = { *manip, tuple->dst };	for (i = 0; i < mr->rangesize; i++) {		/* If we are allowed to map IPs, then we must be in the		   range specified, otherwise we must be unchanged. */		if (mr->range[i].flags & IP_NAT_RANGE_MAP_IPS) {			if (ntohl(newtuple.src.ip) < ntohl(mr->range[i].min_ip)			    || (ntohl(newtuple.src.ip)				> ntohl(mr->range[i].max_ip)))				continue;		} else {			if (newtuple.src.ip != tuple->src.ip)				continue;		}		if ((mr->range[i].flags & IP_NAT_RANGE_PROTO_SPECIFIED)		    && proto->in_range(&newtuple, IP_NAT_MANIP_SRC,				       &mr->range[i].min, &mr->range[i].max))			return 1;	}	return 0;}static inline intsrc_cmp(const struct ip_nat_hash *i,	const struct ip_conntrack_tuple *tuple,	const struct ip_nat_multi_range *mr){	return (i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum		== tuple->dst.protonum		&& i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip		== tuple->src.ip		&& i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.all		== tuple->src.u.all		&& in_range(tuple,			    &i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL]			    .tuple.src,			    mr));}/* Only called for SRC manip */static struct ip_conntrack_manip *find_appropriate_src(const struct ip_conntrack_tuple *tuple,		     const struct ip_nat_multi_range *mr){	unsigned int h = hash_by_src(&tuple->src, tuple->dst.protonum);	struct ip_nat_hash *i;	MUST_BE_READ_LOCKED(&ip_nat_lock);	i = LIST_FIND(&bysource[h], src_cmp, struct ip_nat_hash *, tuple, mr);	if (i)		return &i->conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src;	else		return NULL;}/* If it's really a local destination manip, it may need to do a   source manip too. */static intdo_extra_mangle(u_int32_t var_ip, u_int32_t *other_ipp){	struct rtable *rt;	/* FIXME: IPTOS_TOS(iph->tos) --RR */	if (ip_route_output(&rt, var_ip, 0, 0, 0) != 0) {		DEBUGP("do_extra_mangle: Can't get route to %u.%u.%u.%u\n",		       NIPQUAD(var_ip));		return 0;	}	*other_ipp = rt->rt_src;	ip_rt_put(rt);	return 1;}/* Simple way to iterate through all. */static inline int fake_cmp(const struct ip_nat_hash *i,			   u_int32_t src, u_int32_t dst, u_int16_t protonum,			   unsigned int *score,			   const struct ip_conntrack *conntrack){	/* Compare backwards: we're dealing with OUTGOING tuples, and           inside the conntrack is the REPLY tuple.  Don't count this           conntrack. */	if (i->conntrack != conntrack	    && i->conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip == dst	    && i->conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip == src	    && (i->conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum		== protonum))		(*score)++;	return 0;}static inline unsigned intcount_maps(u_int32_t src, u_int32_t dst, u_int16_t protonum,	   const struct ip_conntrack *conntrack){	unsigned int score = 0;	MUST_BE_READ_LOCKED(&ip_nat_lock);	LIST_FIND(&byipsproto[hash_by_ipsproto(src, dst, protonum)],		  fake_cmp, struct ip_nat_hash *, src, dst, protonum, &score,		  conntrack);	return score;}/* For [FUTURE] fragmentation handling, we want the least-used   src-ip/dst-ip/proto triple.  Fairness doesn't come into it.  Thus   if the range specifies 1.2.3.4 ports 10000-10005 and 1.2.3.5 ports   1-65535, we don't do pro-rata allocation based on ports; we choose   the ip with the lowest src-ip/dst-ip/proto usage.   If an allocation then fails (eg. all 6 ports used in the 1.2.3.4   range), we eliminate that and try again.  This is not the most   efficient approach, but if you're worried about that, don't hand us   ranges you don't really have.  */static struct ip_nat_range *find_best_ips_proto(struct ip_conntrack_tuple *tuple,		    const struct ip_nat_multi_range *mr,		    const struct ip_conntrack *conntrack,		    unsigned int hooknum){	unsigned int i;	struct {		const struct ip_nat_range *range;		unsigned int score;		struct ip_conntrack_tuple tuple;	} best = { NULL,  0xFFFFFFFF };	u_int32_t *var_ipp, *other_ipp, saved_ip, orig_dstip;	if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) {		var_ipp = &tuple->src.ip;		saved_ip = tuple->dst.ip;		other_ipp = &tuple->dst.ip;	} else {		var_ipp = &tuple->dst.ip;		saved_ip = tuple->src.ip;		other_ipp = &tuple->src.ip;	}	/* Don't do do_extra_mangle unless neccessary (overrides           explicit socket bindings, for example) */	orig_dstip = tuple->dst.ip;	IP_NF_ASSERT(mr->rangesize >= 1);	for (i = 0; i < mr->rangesize; i++) {		u_int32_t minip, maxip;		/* Don't do ranges which are already eliminated. */		if (mr->range[i].flags & IP_NAT_RANGE_FULL) {			continue;		}		if (mr->range[i].flags & IP_NAT_RANGE_MAP_IPS) {			minip = mr->range[i].min_ip;			maxip = mr->range[i].max_ip;		} else			minip = maxip = *var_ipp;		for (*var_ipp = minip;		     ntohl(*var_ipp) <= ntohl(maxip);		     *var_ipp = htonl(ntohl(*var_ipp) + 1)) {			unsigned int score;			/* Reset the other ip in case it was mangled by			 * do_extra_mangle last time. */			*other_ipp = saved_ip;			if (hooknum == NF_IP_LOCAL_OUT			    && *var_ipp != orig_dstip			    && !do_extra_mangle(*var_ipp, other_ipp)) {				DEBUGP("Range %u %u.%u.%u.%u rt failed!\n",				       i, NIPQUAD(*var_ipp));				/* Can't route?  This whole range part is				 * probably screwed, but keep trying				 * anyway. */				continue;			}			/* Count how many others map onto this. */			score = count_maps(tuple->src.ip, tuple->dst.ip,					   tuple->dst.protonum, conntrack);			if (score < best.score) {				/* Optimization: doesn't get any better than				   this. */				if (score == 0)					return (struct ip_nat_range *)						&mr->range[i];				best.score = score;				best.tuple = *tuple;				best.range = &mr->range[i];			}		}	}	*tuple = best.tuple;	/* Discard const. */	return (struct ip_nat_range *)best.range;}/* Fast version doesn't iterate through hash chains, but only handles   common case of single IP address (null NAT, masquerade) */static struct ip_nat_range *find_best_ips_proto_fast(struct ip_conntrack_tuple *tuple,			 const struct ip_nat_multi_range *mr,			 const struct ip_conntrack *conntrack,			 unsigned int hooknum){	if (mr->rangesize != 1	    || (mr->range[0].flags & IP_NAT_RANGE_FULL)	    || ((mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)		&& mr->range[0].min_ip != mr->range[0].max_ip))		return find_best_ips_proto(tuple, mr, conntrack, hooknum);	if (mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) {		if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)			tuple->src.ip = mr->range[0].min_ip;		else {			/* Only do extra mangle when required (breaks                           socket binding) */			if (tuple->dst.ip != mr->range[0].min_ip			    && hooknum == NF_IP_LOCAL_OUT			    && !do_extra_mangle(mr->range[0].min_ip,						&tuple->src.ip))				return NULL;			tuple->dst.ip = mr->range[0].min_ip;		}	}	/* Discard const. */	return (struct ip_nat_range *)&mr->range[0];}static intget_unique_tuple(struct ip_conntrack_tuple *tuple,		 const struct ip_conntrack_tuple *orig_tuple,		 const struct ip_nat_multi_range *mrr,		 struct ip_conntrack *conntrack,		 unsigned int hooknum){	struct ip_nat_protocol *proto		= find_nat_proto(orig_tuple->dst.protonum);	struct ip_nat_range *rptr;	unsigned int i;	int ret;	/* We temporarily use flags for marking full parts, but we	   always clean up afterwards */	struct ip_nat_multi_range *mr = (void *)mrr;	/* 1) If this srcip/proto/src-proto-part is currently mapped,	   and that same mapping gives a unique tuple within the given	   range, use that.	   This is only required for source (ie. NAT/masq) mappings.	   So far, we don't do local source mappings, so multiple	   manips not an issue.  */	if (hooknum == NF_IP_POST_ROUTING) {		struct ip_conntrack_manip *manip;		manip = find_appropriate_src(orig_tuple, mr);		if (manip) {			/* Apply same source manipulation. */			*tuple = ((struct ip_conntrack_tuple)				  { *manip, orig_tuple->dst });			DEBUGP("get_unique_tuple: Found current src map\n");			return 1;		}	}	/* 2) Select the least-used IP/proto combination in the given	   range.	*/	*tuple = *orig_tuple;	while ((rptr = find_best_ips_proto_fast(tuple, mr, conntrack, hooknum))	       != NULL) {		DEBUGP("Found best for "); DUMP_TUPLE(tuple);		/* 3) The per-protocol part of the manip is made to		   map into the range to make a unique tuple. */		/* Only bother mapping if it's not already in range		   and unique */		if ((!(rptr->flags & IP_NAT_RANGE_PROTO_SPECIFIED)		     || proto->in_range(tuple, HOOK2MANIP(hooknum),					&rptr->min, &rptr->max))		    && !ip_nat_used_tuple(tuple, conntrack)) {			ret = 1;			goto clear_fulls;		} else {			if (proto->unique_tuple(tuple, rptr,						HOOK2MANIP(hooknum),						conntrack)) {				/* Must be unique. */				IP_NF_ASSERT(!ip_nat_used_tuple(tuple,								conntrack));				ret = 1;				goto clear_fulls;			}			DEBUGP("Protocol can't get unique tuple.\n");		}		/* Eliminate that from range, and try again. */		rptr->flags |= IP_NAT_RANGE_FULL;		*tuple = *orig_tuple;	}	ret = 0; clear_fulls:	/* Clear full flags. */	IP_NF_ASSERT(mr->rangesize >= 1);	for (i = 0; i < mr->rangesize; i++)		mr->range[i].flags &= ~IP_NAT_RANGE_FULL;

⌨️ 快捷键说明

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