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

📄 ip_nat.c

📁 NAT协议完整源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * Copyright (C) 1995-2000 by Darren Reed. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and due credit is given * to the original author and the contributors. * * Added redirect stuff and a LOT of bug fixes. (mcn@EnGarde.com) */#if !defined(lint)static const char sccsid[] = "@(#)ip_nat.c	1.11 6/5/96 (C) 1995 Darren Reed";static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.37.2.32 2001/01/10 06:19:11 darrenr Exp $";#endif#if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL)#define _KERNEL#endif#include <sys/errno.h>#include <sys/types.h>#include <sys/param.h>#include <sys/time.h>#include <sys/file.h>#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \    defined(_KERNEL)# include "opt_ipfilter_log.h"#endif#if !defined(_KERNEL) && !defined(KERNEL)# include <stdio.h># include <string.h># include <stdlib.h>#endif#if (defined(KERNEL) || defined(_KERNEL)) && (__FreeBSD_version >= 220000)# include <sys/filio.h># include <sys/fcntl.h>#else# include <sys/ioctl.h>#endif#include <sys/fcntl.h>#include <sys/uio.h>#ifndef linux# include <sys/protosw.h>#endif#include <sys/socket.h>#if defined(_KERNEL) && !defined(linux)# include <sys/systm.h>#endif#if !defined(__SVR4) && !defined(__svr4__)# ifndef linux#  include <sys/mbuf.h># endif#else# include <sys/filio.h># include <sys/byteorder.h># ifdef _KERNEL#  include <sys/dditypes.h># endif# include <sys/stream.h># include <sys/kmem.h>#endif#if __FreeBSD_version >= 300000# include <sys/queue.h>#endif#include <net/if.h>#if __FreeBSD_version >= 300000# include <net/if_var.h># if defined(_KERNEL) && !defined(IPFILTER_LKM)#  include "opt_ipfilter.h"# endif#endif#ifdef sun# include <net/af.h>#endif#include <net/route.h>#include <netinet/in.h>#include <netinet/in_systm.h>#include <netinet/ip.h>#ifdef __sgi# ifdef IFF_DRVRLOCK /* IRIX6 */#include <sys/hashing.h>#include <netinet/in_var.h># endif#endif#ifdef RFC1825# include <vpn/md5.h># include <vpn/ipsec.h>extern struct ifnet vpnif;#endif#ifndef linux# include <netinet/ip_var.h>#endif#include <netinet/tcp.h>#include <netinet/udp.h>#include <netinet/ip_icmp.h>#include "netinet/ip_compat.h"#include <netinet/tcpip.h>#include "netinet/ip_fil.h"#include "netinet/ip_proxy.h"#include "netinet/ip_nat.h"#include "netinet/ip_frag.h"#include "netinet/ip_state.h"#if (__FreeBSD_version >= 300000)# include <sys/malloc.h>#endif#ifndef	MIN# define	MIN(a,b)	(((a)<(b))?(a):(b))#endif#undef	SOCKADDR_IN#define	SOCKADDR_IN	struct sockaddr_innat_t	**nat_table[2] = { NULL, NULL },	*nat_instances = NULL;ipnat_t	*nat_list = NULL;u_int	ipf_nattable_sz = NAT_TABLE_SZ;u_int	ipf_natrules_sz = NAT_SIZE;u_int	ipf_rdrrules_sz = RDR_SIZE;u_int	ipf_hostmap_sz = HOSTMAP_SIZE;u_32_t	nat_masks = 0;u_32_t	rdr_masks = 0;ipnat_t	**nat_rules = NULL;ipnat_t	**rdr_rules = NULL;hostmap_t	**maptable  = NULL;u_long	fr_defnatage = DEF_NAT_AGE,	fr_defnaticmpage = 6;		/* 3 seconds */natstat_t nat_stats;int	fr_nat_lock = 0;#if	(SOLARIS || defined(__sgi)) && defined(_KERNEL)extern	kmutex_t	ipf_rw;extern	KRWLOCK_T	ipf_nat;#endifstatic	int	nat_flushtable __P((void));static	int	nat_clearlist __P((void));static	void	nat_addnat __P((struct ipnat *));static	void	nat_addrdr __P((struct ipnat *));static	void	nat_delete __P((struct nat *));static	void	nat_delrdr __P((struct ipnat *));static	void	nat_delnat __P((struct ipnat *));static	int	fr_natgetent __P((caddr_t));static	int	fr_natgetsz __P((caddr_t));static	int	fr_natputent __P((caddr_t));static	void	nat_tabmove __P((nat_t *, u_32_t));static	int	nat_match __P((fr_info_t *, ipnat_t *, ip_t *));static	hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr,				    struct in_addr));static	void	nat_hostmapdel __P((struct hostmap *));int nat_init(){	KMALLOCS(nat_table[0], nat_t **, sizeof(nat_t *) * ipf_nattable_sz);	if (nat_table[0] != NULL)		bzero((char *)nat_table[0], ipf_nattable_sz * sizeof(nat_t *));	else		return -1;	KMALLOCS(nat_table[1], nat_t **, sizeof(nat_t *) * ipf_nattable_sz);	if (nat_table[1] != NULL)		bzero((char *)nat_table[1], ipf_nattable_sz * sizeof(nat_t *));	else		return -1;	KMALLOCS(nat_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_natrules_sz);	if (nat_rules != NULL)		bzero((char *)nat_rules, ipf_natrules_sz * sizeof(ipnat_t *));	else		return -1;	KMALLOCS(rdr_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_rdrrules_sz);	if (rdr_rules != NULL)		bzero((char *)rdr_rules, ipf_rdrrules_sz * sizeof(ipnat_t *));	else		return -1;	KMALLOCS(maptable, hostmap_t **, sizeof(hostmap_t *) * ipf_hostmap_sz);	if (maptable != NULL)		bzero((char *)maptable, sizeof(hostmap_t *) * ipf_hostmap_sz);	else		return -1;	return 0;}static void nat_addrdr(n)ipnat_t *n;{	ipnat_t **np;	u_32_t j;	u_int hv;	int k;	k = countbits(n->in_outmsk);	if ((k >= 0) && (k != 32))		rdr_masks |= 1 << k;	j = (n->in_outip & n->in_outmsk);	hv = NAT_HASH_FN(j, 0, ipf_rdrrules_sz);	np = rdr_rules + hv;	while (*np != NULL)		np = &(*np)->in_rnext;	n->in_rnext = NULL;	n->in_prnext = np;	*np = n;}static void nat_addnat(n)ipnat_t *n;{	ipnat_t **np;	u_32_t j;	u_int hv;	int k;	k = countbits(n->in_inmsk);	if ((k >= 0) && (k != 32))		nat_masks |= 1 << k;	j = (n->in_inip & n->in_inmsk);	hv = NAT_HASH_FN(j, 0, ipf_natrules_sz);	np = nat_rules + hv;	while (*np != NULL)		np = &(*np)->in_mnext;	n->in_mnext = NULL;	n->in_pmnext = np;	*np = n;}static void nat_delrdr(n)ipnat_t *n;{	if (n->in_rnext)		n->in_rnext->in_prnext = n->in_prnext;	*n->in_prnext = n->in_rnext;}static void nat_delnat(n)ipnat_t *n;{	if (n->in_mnext)		n->in_mnext->in_pmnext = n->in_pmnext;	*n->in_pmnext = n->in_mnext;}/* * check if an ip address has already been allocated for a given mapping that * is not doing port based translation. * * Must be called with ipf_nat held as a write lock. */static struct hostmap *nat_hostmap(np, real, map)ipnat_t *np;struct in_addr real;struct in_addr map;{	hostmap_t *hm;	u_int hv;	hv = real.s_addr % HOSTMAP_SIZE;	for (hm = maptable[hv]; hm; hm = hm->hm_next)		if ((hm->hm_realip.s_addr == real.s_addr) &&		    (np == hm->hm_ipnat)) {			hm->hm_ref++;			return hm;		}	KMALLOC(hm, hostmap_t *);	if (hm) {		hm->hm_next = maptable[hv];		hm->hm_pnext = maptable + hv;		if (maptable[hv])			maptable[hv]->hm_pnext = &hm->hm_next;		maptable[hv] = hm;		hm->hm_ipnat = np;		hm->hm_realip = real;		hm->hm_mapip = map;		hm->hm_ref = 1;	}	return hm;}/* * Must be called with ipf_nat held as a write lock. */static void nat_hostmapdel(hm)struct hostmap *hm;{	ATOMIC_DEC32(hm->hm_ref);	if (hm->hm_ref == 0) {		if (hm->hm_next)			hm->hm_next->hm_pnext = hm->hm_pnext;		*hm->hm_pnext = hm->hm_next;		KFREE(hm);	}}void fix_outcksum(sp, n)u_short *sp;u_32_t n;{	register u_short sumshort;	register u_32_t sum1;	if (!n)		return;#if SOLARIS2 >= 6	else if (n & NAT_HW_CKSUM) {		*sp = n & 0xffff;		return;	}#endif	sum1 = (~ntohs(*sp)) & 0xffff;	sum1 += (n);	sum1 = (sum1 >> 16) + (sum1 & 0xffff);	/* Again */	sum1 = (sum1 >> 16) + (sum1 & 0xffff);	sumshort = ~(u_short)sum1;	*(sp) = htons(sumshort);}void fix_incksum(sp, n)u_short *sp;u_32_t n;{	register u_short sumshort;	register u_32_t sum1;	if (!n)		return;#if SOLARIS2 >= 6	else if (n & NAT_HW_CKSUM) {		*sp = n & 0xffff;		return;	}#endif#ifdef sparc	sum1 = (~(*sp)) & 0xffff;#else	sum1 = (~ntohs(*sp)) & 0xffff;#endif	sum1 += ~(n) & 0xffff;	sum1 = (sum1 >> 16) + (sum1 & 0xffff);	/* Again */	sum1 = (sum1 >> 16) + (sum1 & 0xffff);	sumshort = ~(u_short)sum1;	*(sp) = htons(sumshort);}/* * fix_datacksum is used *only* for the adjustments of checksums in the data * section of an IP packet. * * The only situation in which you need to do this is when NAT'ing an  * ICMP error message. Such a message, contains in its body the IP header * of the original IP packet, that causes the error. * * You can't use fix_incksum or fix_outcksum in that case, because for the * kernel the data section of the ICMP error is just data, and no special  * processing like hardware cksum or ntohs processing have been done by the  * kernel on the data section. */void fix_datacksum(sp, n)u_short *sp;u_32_t n;{	register u_short sumshort;	register u_32_t sum1;	if (!n)		return;	sum1 = (~ntohs(*sp)) & 0xffff;	sum1 += (n);	sum1 = (sum1 >> 16) + (sum1 & 0xffff);	/* Again */	sum1 = (sum1 >> 16) + (sum1 & 0xffff);	sumshort = ~(u_short)sum1;	*(sp) = htons(sumshort);}/* * How the NAT is organised and works. * * Inside (interface y) NAT       Outside (interface x) * -------------------- -+- ------------------------------------- * Packet going          |   out, processsed by ip_natout() for x * ------------>         |   ------------> * src=10.1.1.1          |   src=192.1.1.1 *                       | *                       |   in, processed by ip_natin() for x * <------------         |   <------------ * dst=10.1.1.1          |   dst=192.1.1.1 * -------------------- -+- ------------------------------------- * ip_natout() - changes ip_src and if required, sport *             - creates a new mapping, if required. * ip_natin()  - changes ip_dst and if required, dport * * In the NAT table, internal source is recorded as "in" and externally * seen as "out". *//* * Handle ioctls which manipulate the NAT. */int nat_ioctl(data, cmd, mode)#if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003)u_long cmd;#elseint cmd;#endifcaddr_t data;int mode;{	register ipnat_t *nat, *nt, *n = NULL, **np = NULL;	int error = 0, ret, arg;	ipnat_t natd;	u_32_t i, j;#if (BSD >= 199306) && defined(_KERNEL)	if ((securelevel >= 2) && (mode & FWRITE))		return EPERM;#endif	nat = NULL;     /* XXX gcc -Wuninitialized */	KMALLOC(nt, ipnat_t *);	if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT))		error = IRCOPYPTR(data, (char *)&natd, sizeof(natd));	else if (cmd == SIOCIPFFL) {	/* SIOCFLNAT & SIOCCNATL */		error = IRCOPY(data, (char *)&arg, sizeof(arg));		if (error)			error = EFAULT;	}	if (error)		goto done;	/*	 * For add/delete, look to see if the NAT entry is already present	 */	WRITE_ENTER(&ipf_nat);	if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) {		nat = &natd;		nat->in_flags &= IPN_USERFLAGS;		if ((nat->in_redir & NAT_MAPBLK) == 0) {			if ((nat->in_flags & IPN_SPLIT) == 0)				nat->in_inip &= nat->in_inmsk;			if ((nat->in_flags & IPN_IPRANGE) == 0)				nat->in_outip &= nat->in_outmsk;		}		for (np = &nat_list; (n = *np); np = &n->in_next)			if (!bcmp((char *)&nat->in_flags, (char *)&n->in_flags,					IPN_CMPSIZ))				break;	}	switch (cmd)	{#ifdef  IPFILTER_LOG	case SIOCIPFFB :	{		int tmp;		if (!(mode & FWRITE))			error = EPERM;		else {			tmp = ipflog_clear(IPL_LOGNAT);			IWCOPY((char *)&tmp, (char *)data, sizeof(tmp));		}		break;	}#endif	case SIOCADNAT :		if (!(mode & FWRITE)) {			error = EPERM;			break;		}		if (n) {			error = EEXIST;			break;		}		if (nt == NULL) {			error = ENOMEM;			break;		}		n = nt;		nt = NULL;		bcopy((char *)nat, (char *)n, sizeof(*n));		n->in_ifp = (void *)GETUNIT(n->in_ifname, 4);		if (!n->in_ifp)			n->in_ifp = (void *)-1;		if (n->in_plabel[0] != '\0') {			n->in_apr = appr_match(n->in_p, n->in_plabel);			if (!n->in_apr) {				error = ENOENT;				break;			}		}		n->in_next = NULL;		*np = n;		if (n->in_redir & NAT_REDIRECT) {			n->in_flags &= ~IPN_NOTDST;			nat_addrdr(n);		}		if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {			n->in_flags &= ~IPN_NOTSRC;			nat_addnat(n);		}		n->in_use = 0;		if (n->in_redir & NAT_MAPBLK)			n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk);		else if (n->in_flags & IPN_AUTOPORTMAP)			n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk);		else if (n->in_flags & IPN_IPRANGE)			n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip);		else if (n->in_flags & IPN_SPLIT)			n->in_space = 2;		else			n->in_space = ~ntohl(n->in_outmsk);		/*		 * Calculate the number of valid IP addresses in the output		 * mapping range.  In all cases, the range is inclusive of		 * the start and ending IP addresses.		 * If to a CIDR address, lose 2: broadcast + network address		 *			         (so subtract 1)		 * If to a range, add one.		 * If to a single IP address, set to 1.

⌨️ 快捷键说明

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