ip_nat.c revision 95418
153642Sguido/*
280482Sdarrenr * Copyright (C) 1995-2001 by Darren Reed.
353642Sguido *
480482Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
553642Sguido *
653642Sguido * Added redirect stuff and a LOT of bug fixes. (mcn@EnGarde.com)
753642Sguido */
853642Sguido
953642Sguido#if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL)
1053642Sguido#define _KERNEL
1153642Sguido#endif
1253642Sguido
1392685Sdarrenr#ifdef __sgi
1492685Sdarrenr# include <sys/ptimers.h>
1592685Sdarrenr#endif
1653642Sguido#include <sys/errno.h>
1753642Sguido#include <sys/types.h>
1853642Sguido#include <sys/param.h>
1953642Sguido#include <sys/time.h>
2053642Sguido#include <sys/file.h>
2153642Sguido#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \
2253642Sguido    defined(_KERNEL)
2353642Sguido# include "opt_ipfilter_log.h"
2453642Sguido#endif
2553642Sguido#if !defined(_KERNEL) && !defined(KERNEL)
2653642Sguido# include <stdio.h>
2753642Sguido# include <string.h>
2853642Sguido# include <stdlib.h>
2953642Sguido#endif
3060852Sdarrenr#if (defined(KERNEL) || defined(_KERNEL)) && (__FreeBSD_version >= 220000)
3153642Sguido# include <sys/filio.h>
3253642Sguido# include <sys/fcntl.h>
3353642Sguido#else
3453642Sguido# include <sys/ioctl.h>
3553642Sguido#endif
3653642Sguido#include <sys/fcntl.h>
3753642Sguido#ifndef linux
3853642Sguido# include <sys/protosw.h>
3953642Sguido#endif
4053642Sguido#include <sys/socket.h>
4153642Sguido#if defined(_KERNEL) && !defined(linux)
4253642Sguido# include <sys/systm.h>
4353642Sguido#endif
4453642Sguido#if !defined(__SVR4) && !defined(__svr4__)
4553642Sguido# ifndef linux
4653642Sguido#  include <sys/mbuf.h>
4753642Sguido# endif
4853642Sguido#else
4953642Sguido# include <sys/filio.h>
5053642Sguido# include <sys/byteorder.h>
5153642Sguido# ifdef _KERNEL
5253642Sguido#  include <sys/dditypes.h>
5353642Sguido# endif
5453642Sguido# include <sys/stream.h>
5553642Sguido# include <sys/kmem.h>
5653642Sguido#endif
5753642Sguido#if __FreeBSD_version >= 300000
5853642Sguido# include <sys/queue.h>
5953642Sguido#endif
6053642Sguido#include <net/if.h>
6153642Sguido#if __FreeBSD_version >= 300000
6253642Sguido# include <net/if_var.h>
6353642Sguido# if defined(_KERNEL) && !defined(IPFILTER_LKM)
6453642Sguido#  include "opt_ipfilter.h"
6553642Sguido# endif
6653642Sguido#endif
6753642Sguido#ifdef sun
6853642Sguido# include <net/af.h>
6953642Sguido#endif
7053642Sguido#include <net/route.h>
7153642Sguido#include <netinet/in.h>
7253642Sguido#include <netinet/in_systm.h>
7353642Sguido#include <netinet/ip.h>
7453642Sguido
7553642Sguido#ifdef __sgi
7653642Sguido# ifdef IFF_DRVRLOCK /* IRIX6 */
7753642Sguido#include <sys/hashing.h>
7853642Sguido#include <netinet/in_var.h>
7953642Sguido# endif
8053642Sguido#endif
8153642Sguido
8253642Sguido#ifdef RFC1825
8353642Sguido# include <vpn/md5.h>
8453642Sguido# include <vpn/ipsec.h>
8553642Sguidoextern struct ifnet vpnif;
8653642Sguido#endif
8753642Sguido
8853642Sguido#ifndef linux
8953642Sguido# include <netinet/ip_var.h>
9080482Sdarrenr# include <netinet/tcp_fsm.h>
9153642Sguido#endif
9253642Sguido#include <netinet/tcp.h>
9353642Sguido#include <netinet/udp.h>
9453642Sguido#include <netinet/ip_icmp.h>
9553642Sguido#include "netinet/ip_compat.h"
9653642Sguido#include <netinet/tcpip.h>
9753642Sguido#include "netinet/ip_fil.h"
9853642Sguido#include "netinet/ip_nat.h"
9953642Sguido#include "netinet/ip_frag.h"
10053642Sguido#include "netinet/ip_state.h"
10192685Sdarrenr#include "netinet/ip_proxy.h"
10253642Sguido#if (__FreeBSD_version >= 300000)
10353642Sguido# include <sys/malloc.h>
10453642Sguido#endif
10553642Sguido#ifndef	MIN
10653642Sguido# define	MIN(a,b)	(((a)<(b))?(a):(b))
10753642Sguido#endif
10853642Sguido#undef	SOCKADDR_IN
10953642Sguido#define	SOCKADDR_IN	struct sockaddr_in
11053642Sguido
11180482Sdarrenr#if !defined(lint)
11280482Sdarrenrstatic const char sccsid[] = "@(#)ip_nat.c	1.11 6/5/96 (C) 1995 Darren Reed";
11380482Sdarrenr/* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.37.2.44 2001/07/21 07:17:22 darrenr Exp $"; */
11480482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_nat.c 95418 2002-04-25 03:31:39Z darrenr $";
11580482Sdarrenr#endif
11680482Sdarrenr
11753642Sguidonat_t	**nat_table[2] = { NULL, NULL },
11853642Sguido	*nat_instances = NULL;
11953642Sguidoipnat_t	*nat_list = NULL;
12053642Sguidou_int	ipf_nattable_sz = NAT_TABLE_SZ;
12153642Sguidou_int	ipf_natrules_sz = NAT_SIZE;
12253642Sguidou_int	ipf_rdrrules_sz = RDR_SIZE;
12360852Sdarrenru_int	ipf_hostmap_sz = HOSTMAP_SIZE;
12453642Sguidou_32_t	nat_masks = 0;
12553642Sguidou_32_t	rdr_masks = 0;
12653642Sguidoipnat_t	**nat_rules = NULL;
12753642Sguidoipnat_t	**rdr_rules = NULL;
12860852Sdarrenrhostmap_t	**maptable  = NULL;
12953642Sguido
13053642Sguidou_long	fr_defnatage = DEF_NAT_AGE,
13153642Sguido	fr_defnaticmpage = 6;		/* 3 seconds */
13264580Sdarrenrnatstat_t nat_stats;
13360852Sdarrenrint	fr_nat_lock = 0;
13453642Sguido#if	(SOLARIS || defined(__sgi)) && defined(_KERNEL)
13572006Sdarrenrextern	kmutex_t	ipf_rw;
13653642Sguidoextern	KRWLOCK_T	ipf_nat;
13753642Sguido#endif
13853642Sguido
13953642Sguidostatic	int	nat_flushtable __P((void));
14060852Sdarrenrstatic	void	nat_addnat __P((struct ipnat *));
14160852Sdarrenrstatic	void	nat_addrdr __P((struct ipnat *));
14260857Sdarrenrstatic	void	nat_delete __P((struct nat *));
14353642Sguidostatic	void	nat_delrdr __P((struct ipnat *));
14453642Sguidostatic	void	nat_delnat __P((struct ipnat *));
14560852Sdarrenrstatic	int	fr_natgetent __P((caddr_t));
14660852Sdarrenrstatic	int	fr_natgetsz __P((caddr_t));
14760852Sdarrenrstatic	int	fr_natputent __P((caddr_t));
14892685Sdarrenrstatic	void	nat_tabmove __P((fr_info_t *, nat_t *));
14960852Sdarrenrstatic	int	nat_match __P((fr_info_t *, ipnat_t *, ip_t *));
15060852Sdarrenrstatic	hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr,
15160852Sdarrenr				    struct in_addr));
15260852Sdarrenrstatic	void	nat_hostmapdel __P((struct hostmap *));
15353642Sguido
15453642Sguido
15553642Sguidoint nat_init()
15653642Sguido{
15753642Sguido	KMALLOCS(nat_table[0], nat_t **, sizeof(nat_t *) * ipf_nattable_sz);
15853642Sguido	if (nat_table[0] != NULL)
15953642Sguido		bzero((char *)nat_table[0], ipf_nattable_sz * sizeof(nat_t *));
16053642Sguido	else
16153642Sguido		return -1;
16253642Sguido
16353642Sguido	KMALLOCS(nat_table[1], nat_t **, sizeof(nat_t *) * ipf_nattable_sz);
16453642Sguido	if (nat_table[1] != NULL)
16553642Sguido		bzero((char *)nat_table[1], ipf_nattable_sz * sizeof(nat_t *));
16653642Sguido	else
16753642Sguido		return -1;
16853642Sguido
16953642Sguido	KMALLOCS(nat_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_natrules_sz);
17053642Sguido	if (nat_rules != NULL)
17153642Sguido		bzero((char *)nat_rules, ipf_natrules_sz * sizeof(ipnat_t *));
17253642Sguido	else
17353642Sguido		return -1;
17453642Sguido
17553642Sguido	KMALLOCS(rdr_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_rdrrules_sz);
17653642Sguido	if (rdr_rules != NULL)
17753642Sguido		bzero((char *)rdr_rules, ipf_rdrrules_sz * sizeof(ipnat_t *));
17853642Sguido	else
17953642Sguido		return -1;
18060852Sdarrenr
18160852Sdarrenr	KMALLOCS(maptable, hostmap_t **, sizeof(hostmap_t *) * ipf_hostmap_sz);
18260852Sdarrenr	if (maptable != NULL)
18360852Sdarrenr		bzero((char *)maptable, sizeof(hostmap_t *) * ipf_hostmap_sz);
18460852Sdarrenr	else
18560852Sdarrenr		return -1;
18653642Sguido	return 0;
18753642Sguido}
18853642Sguido
18953642Sguido
19060852Sdarrenrstatic void nat_addrdr(n)
19153642Sguidoipnat_t *n;
19253642Sguido{
19360852Sdarrenr	ipnat_t **np;
19460852Sdarrenr	u_32_t j;
19553642Sguido	u_int hv;
19660852Sdarrenr	int k;
19753642Sguido
19860852Sdarrenr	k = countbits(n->in_outmsk);
19960852Sdarrenr	if ((k >= 0) && (k != 32))
20060852Sdarrenr		rdr_masks |= 1 << k;
20160852Sdarrenr	j = (n->in_outip & n->in_outmsk);
20260852Sdarrenr	hv = NAT_HASH_FN(j, 0, ipf_rdrrules_sz);
20360852Sdarrenr	np = rdr_rules + hv;
20460852Sdarrenr	while (*np != NULL)
20560852Sdarrenr		np = &(*np)->in_rnext;
20660852Sdarrenr	n->in_rnext = NULL;
20760852Sdarrenr	n->in_prnext = np;
20860852Sdarrenr	*np = n;
20953642Sguido}
21053642Sguido
21153642Sguido
21260852Sdarrenrstatic void nat_addnat(n)
21360852Sdarrenripnat_t *n;
21460852Sdarrenr{
21560852Sdarrenr	ipnat_t **np;
21660852Sdarrenr	u_32_t j;
21760852Sdarrenr	u_int hv;
21860852Sdarrenr	int k;
21960852Sdarrenr
22060852Sdarrenr	k = countbits(n->in_inmsk);
22160852Sdarrenr	if ((k >= 0) && (k != 32))
22260852Sdarrenr		nat_masks |= 1 << k;
22360852Sdarrenr	j = (n->in_inip & n->in_inmsk);
22460852Sdarrenr	hv = NAT_HASH_FN(j, 0, ipf_natrules_sz);
22560852Sdarrenr	np = nat_rules + hv;
22660852Sdarrenr	while (*np != NULL)
22760852Sdarrenr		np = &(*np)->in_mnext;
22860852Sdarrenr	n->in_mnext = NULL;
22960852Sdarrenr	n->in_pmnext = np;
23060852Sdarrenr	*np = n;
23160852Sdarrenr}
23260852Sdarrenr
23360852Sdarrenr
23460852Sdarrenrstatic void nat_delrdr(n)
23560852Sdarrenripnat_t *n;
23660852Sdarrenr{
23760852Sdarrenr	if (n->in_rnext)
23860852Sdarrenr		n->in_rnext->in_prnext = n->in_prnext;
23960852Sdarrenr	*n->in_prnext = n->in_rnext;
24060852Sdarrenr}
24160852Sdarrenr
24260852Sdarrenr
24353642Sguidostatic void nat_delnat(n)
24453642Sguidoipnat_t *n;
24553642Sguido{
24660852Sdarrenr	if (n->in_mnext)
24760852Sdarrenr		n->in_mnext->in_pmnext = n->in_pmnext;
24860852Sdarrenr	*n->in_pmnext = n->in_mnext;
24960852Sdarrenr}
25060852Sdarrenr
25160852Sdarrenr
25260852Sdarrenr/*
25360852Sdarrenr * check if an ip address has already been allocated for a given mapping that
25460852Sdarrenr * is not doing port based translation.
25572006Sdarrenr *
25672006Sdarrenr * Must be called with ipf_nat held as a write lock.
25760852Sdarrenr */
25860852Sdarrenrstatic struct hostmap *nat_hostmap(np, real, map)
25960852Sdarrenripnat_t *np;
26060852Sdarrenrstruct in_addr real;
26160852Sdarrenrstruct in_addr map;
26260852Sdarrenr{
26360852Sdarrenr	hostmap_t *hm;
26453642Sguido	u_int hv;
26553642Sguido
26660852Sdarrenr	hv = real.s_addr % HOSTMAP_SIZE;
26760852Sdarrenr	for (hm = maptable[hv]; hm; hm = hm->hm_next)
26860852Sdarrenr		if ((hm->hm_realip.s_addr == real.s_addr) &&
26960852Sdarrenr		    (np == hm->hm_ipnat)) {
27060852Sdarrenr			hm->hm_ref++;
27160852Sdarrenr			return hm;
27260852Sdarrenr		}
27360852Sdarrenr
27460852Sdarrenr	KMALLOC(hm, hostmap_t *);
27560852Sdarrenr	if (hm) {
27660852Sdarrenr		hm->hm_next = maptable[hv];
27760852Sdarrenr		hm->hm_pnext = maptable + hv;
27860852Sdarrenr		if (maptable[hv])
27960852Sdarrenr			maptable[hv]->hm_pnext = &hm->hm_next;
28060852Sdarrenr		maptable[hv] = hm;
28160852Sdarrenr		hm->hm_ipnat = np;
28260852Sdarrenr		hm->hm_realip = real;
28360852Sdarrenr		hm->hm_mapip = map;
28460852Sdarrenr		hm->hm_ref = 1;
28560852Sdarrenr	}
28660852Sdarrenr	return hm;
28753642Sguido}
28853642Sguido
28953642Sguido
29072006Sdarrenr/*
29172006Sdarrenr * Must be called with ipf_nat held as a write lock.
29272006Sdarrenr */
29360852Sdarrenrstatic void nat_hostmapdel(hm)
29460852Sdarrenrstruct hostmap *hm;
29560852Sdarrenr{
29660852Sdarrenr	ATOMIC_DEC32(hm->hm_ref);
29760852Sdarrenr	if (hm->hm_ref == 0) {
29860852Sdarrenr		if (hm->hm_next)
29960852Sdarrenr			hm->hm_next->hm_pnext = hm->hm_pnext;
30060852Sdarrenr		*hm->hm_pnext = hm->hm_next;
30160852Sdarrenr		KFREE(hm);
30260852Sdarrenr	}
30360852Sdarrenr}
30460852Sdarrenr
30560852Sdarrenr
30680482Sdarrenrvoid fix_outcksum(fin, sp, n)
30780482Sdarrenrfr_info_t *fin;
30853642Sguidou_short *sp;
30953642Sguidou_32_t n;
31053642Sguido{
31153642Sguido	register u_short sumshort;
31253642Sguido	register u_32_t sum1;
31353642Sguido
31453642Sguido	if (!n)
31553642Sguido		return;
31655929Sguido	else if (n & NAT_HW_CKSUM) {
31780482Sdarrenr		n &= 0xffff;
31880482Sdarrenr		n += fin->fin_dlen;
31980482Sdarrenr		n = (n & 0xffff) + (n >> 16);
32055929Sguido		*sp = n & 0xffff;
32155929Sguido		return;
32255929Sguido	}
32353642Sguido	sum1 = (~ntohs(*sp)) & 0xffff;
32453642Sguido	sum1 += (n);
32553642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
32653642Sguido	/* Again */
32753642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
32853642Sguido	sumshort = ~(u_short)sum1;
32953642Sguido	*(sp) = htons(sumshort);
33053642Sguido}
33153642Sguido
33253642Sguido
33380482Sdarrenrvoid fix_incksum(fin, sp, n)
33480482Sdarrenrfr_info_t *fin;
33553642Sguidou_short *sp;
33653642Sguidou_32_t n;
33753642Sguido{
33853642Sguido	register u_short sumshort;
33953642Sguido	register u_32_t sum1;
34053642Sguido
34153642Sguido	if (!n)
34253642Sguido		return;
34355929Sguido	else if (n & NAT_HW_CKSUM) {
34480482Sdarrenr		n &= 0xffff;
34580482Sdarrenr		n += fin->fin_dlen;
34680482Sdarrenr		n = (n & 0xffff) + (n >> 16);
34755929Sguido		*sp = n & 0xffff;
34855929Sguido		return;
34955929Sguido	}
35053642Sguido#ifdef sparc
35153642Sguido	sum1 = (~(*sp)) & 0xffff;
35253642Sguido#else
35353642Sguido	sum1 = (~ntohs(*sp)) & 0xffff;
35453642Sguido#endif
35553642Sguido	sum1 += ~(n) & 0xffff;
35653642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
35753642Sguido	/* Again */
35853642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
35953642Sguido	sumshort = ~(u_short)sum1;
36053642Sguido	*(sp) = htons(sumshort);
36153642Sguido}
36253642Sguido
36353642Sguido
36453642Sguido/*
36567614Sdarrenr * fix_datacksum is used *only* for the adjustments of checksums in the data
36667614Sdarrenr * section of an IP packet.
36767614Sdarrenr *
36867614Sdarrenr * The only situation in which you need to do this is when NAT'ing an
36967614Sdarrenr * ICMP error message. Such a message, contains in its body the IP header
37067614Sdarrenr * of the original IP packet, that causes the error.
37167614Sdarrenr *
37267614Sdarrenr * You can't use fix_incksum or fix_outcksum in that case, because for the
37367614Sdarrenr * kernel the data section of the ICMP error is just data, and no special
37467614Sdarrenr * processing like hardware cksum or ntohs processing have been done by the
37567614Sdarrenr * kernel on the data section.
37667614Sdarrenr */
37767614Sdarrenrvoid fix_datacksum(sp, n)
37867614Sdarrenru_short *sp;
37967614Sdarrenru_32_t n;
38067614Sdarrenr{
38167614Sdarrenr	register u_short sumshort;
38267614Sdarrenr	register u_32_t sum1;
38367614Sdarrenr
38467614Sdarrenr	if (!n)
38567614Sdarrenr		return;
38667614Sdarrenr
38767614Sdarrenr	sum1 = (~ntohs(*sp)) & 0xffff;
38867614Sdarrenr	sum1 += (n);
38967614Sdarrenr	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
39067614Sdarrenr	/* Again */
39167614Sdarrenr	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
39267614Sdarrenr	sumshort = ~(u_short)sum1;
39367614Sdarrenr	*(sp) = htons(sumshort);
39467614Sdarrenr}
39567614Sdarrenr
39667614Sdarrenr/*
39753642Sguido * How the NAT is organised and works.
39853642Sguido *
39953642Sguido * Inside (interface y) NAT       Outside (interface x)
40053642Sguido * -------------------- -+- -------------------------------------
40153642Sguido * Packet going          |   out, processsed by ip_natout() for x
40253642Sguido * ------------>         |   ------------>
40353642Sguido * src=10.1.1.1          |   src=192.1.1.1
40453642Sguido *                       |
40553642Sguido *                       |   in, processed by ip_natin() for x
40653642Sguido * <------------         |   <------------
40753642Sguido * dst=10.1.1.1          |   dst=192.1.1.1
40853642Sguido * -------------------- -+- -------------------------------------
40953642Sguido * ip_natout() - changes ip_src and if required, sport
41053642Sguido *             - creates a new mapping, if required.
41153642Sguido * ip_natin()  - changes ip_dst and if required, dport
41253642Sguido *
41353642Sguido * In the NAT table, internal source is recorded as "in" and externally
41453642Sguido * seen as "out".
41553642Sguido */
41653642Sguido
41753642Sguido/*
41853642Sguido * Handle ioctls which manipulate the NAT.
41953642Sguido */
42053642Sguidoint nat_ioctl(data, cmd, mode)
42160852Sdarrenr#if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003)
42253642Sguidou_long cmd;
42353642Sguido#else
42453642Sguidoint cmd;
42553642Sguido#endif
42653642Sguidocaddr_t data;
42753642Sguidoint mode;
42853642Sguido{
42953642Sguido	register ipnat_t *nat, *nt, *n = NULL, **np = NULL;
43095418Sdarrenr	int error = 0, ret, arg, getlock;
43153642Sguido	ipnat_t natd;
43253642Sguido	u_32_t i, j;
43353642Sguido
43453642Sguido#if (BSD >= 199306) && defined(_KERNEL)
43588876Sdarrenr	if ((securelevel >= 3) && (mode & FWRITE))
43653642Sguido		return EPERM;
43753642Sguido#endif
43853642Sguido
43953642Sguido	nat = NULL;     /* XXX gcc -Wuninitialized */
44053642Sguido	KMALLOC(nt, ipnat_t *);
44195418Sdarrenr	getlock = (mode & NAT_LOCKHELD) ? 0 : 1;
44295418Sdarrenr	if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) {
44395418Sdarrenr		if (mode & NAT_SYSSPACE) {
44495418Sdarrenr			bcopy(data, (char *)&natd, sizeof(natd));
44595418Sdarrenr			error = 0;
44695418Sdarrenr		} else {
44795418Sdarrenr			error = IRCOPYPTR(data, (char *)&natd, sizeof(natd));
44895418Sdarrenr		}
44995418Sdarrenr	} else if (cmd == SIOCIPFFL) {	/* SIOCFLNAT & SIOCCNATL */
45060852Sdarrenr		error = IRCOPY(data, (char *)&arg, sizeof(arg));
45164580Sdarrenr		if (error)
45264580Sdarrenr			error = EFAULT;
45364580Sdarrenr	}
45453642Sguido
45560852Sdarrenr	if (error)
45660852Sdarrenr		goto done;
45760852Sdarrenr
45853642Sguido	/*
45953642Sguido	 * For add/delete, look to see if the NAT entry is already present
46053642Sguido	 */
46195418Sdarrenr	if (getlock == 1)
46295418Sdarrenr		WRITE_ENTER(&ipf_nat);
46353642Sguido	if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) {
46453642Sguido		nat = &natd;
46553642Sguido		nat->in_flags &= IPN_USERFLAGS;
46653642Sguido		if ((nat->in_redir & NAT_MAPBLK) == 0) {
46760852Sdarrenr			if ((nat->in_flags & IPN_SPLIT) == 0)
46860852Sdarrenr				nat->in_inip &= nat->in_inmsk;
46960852Sdarrenr			if ((nat->in_flags & IPN_IPRANGE) == 0)
47053642Sguido				nat->in_outip &= nat->in_outmsk;
47153642Sguido		}
47253642Sguido		for (np = &nat_list; (n = *np); np = &n->in_next)
47353642Sguido			if (!bcmp((char *)&nat->in_flags, (char *)&n->in_flags,
47453642Sguido					IPN_CMPSIZ))
47553642Sguido				break;
47653642Sguido	}
47753642Sguido
47853642Sguido	switch (cmd)
47953642Sguido	{
48055929Sguido#ifdef  IPFILTER_LOG
48155929Sguido	case SIOCIPFFB :
48260852Sdarrenr	{
48360852Sdarrenr		int tmp;
48460852Sdarrenr
48555929Sguido		if (!(mode & FWRITE))
48655929Sguido			error = EPERM;
48760852Sdarrenr		else {
48860852Sdarrenr			tmp = ipflog_clear(IPL_LOGNAT);
48960852Sdarrenr			IWCOPY((char *)&tmp, (char *)data, sizeof(tmp));
49060852Sdarrenr		}
49155929Sguido		break;
49260852Sdarrenr	}
49355929Sguido#endif
49453642Sguido	case SIOCADNAT :
49553642Sguido		if (!(mode & FWRITE)) {
49653642Sguido			error = EPERM;
49753642Sguido			break;
49853642Sguido		}
49953642Sguido		if (n) {
50053642Sguido			error = EEXIST;
50153642Sguido			break;
50253642Sguido		}
50353642Sguido		if (nt == NULL) {
50453642Sguido			error = ENOMEM;
50553642Sguido			break;
50653642Sguido		}
50753642Sguido		n = nt;
50853642Sguido		nt = NULL;
50953642Sguido		bcopy((char *)nat, (char *)n, sizeof(*n));
51060852Sdarrenr		n->in_ifp = (void *)GETUNIT(n->in_ifname, 4);
51153642Sguido		if (!n->in_ifp)
51253642Sguido			n->in_ifp = (void *)-1;
51353642Sguido		if (n->in_plabel[0] != '\0') {
51492685Sdarrenr			n->in_apr = appr_lookup(n->in_p, n->in_plabel);
51553642Sguido			if (!n->in_apr) {
51653642Sguido				error = ENOENT;
51753642Sguido				break;
51853642Sguido			}
51953642Sguido		}
52053642Sguido		n->in_next = NULL;
52153642Sguido		*np = n;
52253642Sguido
52363523Sdarrenr		if (n->in_redir & NAT_REDIRECT) {
52463523Sdarrenr			n->in_flags &= ~IPN_NOTDST;
52560852Sdarrenr			nat_addrdr(n);
52663523Sdarrenr		}
52763523Sdarrenr		if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
52863523Sdarrenr			n->in_flags &= ~IPN_NOTSRC;
52960852Sdarrenr			nat_addnat(n);
53063523Sdarrenr		}
53153642Sguido
53253642Sguido		n->in_use = 0;
53353642Sguido		if (n->in_redir & NAT_MAPBLK)
53453642Sguido			n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk);
53553642Sguido		else if (n->in_flags & IPN_AUTOPORTMAP)
53653642Sguido			n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk);
53760852Sdarrenr		else if (n->in_flags & IPN_IPRANGE)
53853642Sguido			n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip);
53960852Sdarrenr		else if (n->in_flags & IPN_SPLIT)
54060852Sdarrenr			n->in_space = 2;
54153642Sguido		else
54253642Sguido			n->in_space = ~ntohl(n->in_outmsk);
54353642Sguido		/*
54453642Sguido		 * Calculate the number of valid IP addresses in the output
54553642Sguido		 * mapping range.  In all cases, the range is inclusive of
54653642Sguido		 * the start and ending IP addresses.
54753642Sguido		 * If to a CIDR address, lose 2: broadcast + network address
54864580Sdarrenr		 *			         (so subtract 1)
54953642Sguido		 * If to a range, add one.
55053642Sguido		 * If to a single IP address, set to 1.
55153642Sguido		 */
55253642Sguido		if (n->in_space) {
55360852Sdarrenr			if ((n->in_flags & IPN_IPRANGE) != 0)
55453642Sguido				n->in_space += 1;
55553642Sguido			else
55653642Sguido				n->in_space -= 1;
55753642Sguido		} else
55853642Sguido			n->in_space = 1;
55953642Sguido		if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) &&
56060852Sdarrenr		    ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0))
56153642Sguido			n->in_nip = ntohl(n->in_outip) + 1;
56260852Sdarrenr		else if ((n->in_flags & IPN_SPLIT) &&
56360852Sdarrenr			 (n->in_redir & NAT_REDIRECT))
56460852Sdarrenr			n->in_nip = ntohl(n->in_inip);
56553642Sguido		else
56653642Sguido			n->in_nip = ntohl(n->in_outip);
56753642Sguido		if (n->in_redir & NAT_MAP) {
56853642Sguido			n->in_pnext = ntohs(n->in_pmin);
56953642Sguido			/*
57053642Sguido			 * Multiply by the number of ports made available.
57153642Sguido			 */
57253642Sguido			if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) {
57353642Sguido				n->in_space *= (ntohs(n->in_pmax) -
57453642Sguido						ntohs(n->in_pmin) + 1);
57553642Sguido				/*
57653642Sguido				 * Because two different sources can map to
57753642Sguido				 * different destinations but use the same
57853642Sguido				 * local IP#/port #.
57953642Sguido				 * If the result is smaller than in_space, then
58053642Sguido				 * we may have wrapped around 32bits.
58153642Sguido				 */
58253642Sguido				i = n->in_inmsk;
58353642Sguido				if ((i != 0) && (i != 0xffffffff)) {
58453642Sguido					j = n->in_space * (~ntohl(i) + 1);
58553642Sguido					if (j >= n->in_space)
58653642Sguido						n->in_space = j;
58753642Sguido					else
58853642Sguido						n->in_space = 0xffffffff;
58953642Sguido				}
59053642Sguido			}
59153642Sguido			/*
59253642Sguido			 * If no protocol is specified, multiple by 256.
59353642Sguido			 */
59453642Sguido			if ((n->in_flags & IPN_TCPUDP) == 0) {
59553642Sguido					j = n->in_space * 256;
59653642Sguido					if (j >= n->in_space)
59753642Sguido						n->in_space = j;
59853642Sguido					else
59953642Sguido						n->in_space = 0xffffffff;
60053642Sguido			}
60153642Sguido		}
60253642Sguido		/* Otherwise, these fields are preset */
60353642Sguido		n = NULL;
60453642Sguido		nat_stats.ns_rules++;
60553642Sguido		break;
60653642Sguido	case SIOCRMNAT :
60753642Sguido		if (!(mode & FWRITE)) {
60853642Sguido			error = EPERM;
60953642Sguido			n = NULL;
61053642Sguido			break;
61153642Sguido		}
61253642Sguido		if (!n) {
61353642Sguido			error = ESRCH;
61453642Sguido			break;
61553642Sguido		}
61653642Sguido		if (n->in_redir & NAT_REDIRECT)
61753642Sguido			nat_delrdr(n);
61853642Sguido		if (n->in_redir & (NAT_MAPBLK|NAT_MAP))
61953642Sguido			nat_delnat(n);
62053642Sguido		if (nat_list == NULL) {
62153642Sguido			nat_masks = 0;
62253642Sguido			rdr_masks = 0;
62353642Sguido		}
62453642Sguido		*np = n->in_next;
62553642Sguido		if (!n->in_use) {
62653642Sguido			if (n->in_apr)
62753642Sguido				appr_free(n->in_apr);
62853642Sguido			KFREE(n);
62953642Sguido			nat_stats.ns_rules--;
63053642Sguido		} else {
63153642Sguido			n->in_flags |= IPN_DELETE;
63253642Sguido			n->in_next = NULL;
63353642Sguido		}
63453642Sguido		n = NULL;
63553642Sguido		break;
63653642Sguido	case SIOCGNATS :
63753642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
63853642Sguido		nat_stats.ns_table[0] = nat_table[0];
63953642Sguido		nat_stats.ns_table[1] = nat_table[1];
64053642Sguido		nat_stats.ns_list = nat_list;
64180482Sdarrenr		nat_stats.ns_maptable = maptable;
64253642Sguido		nat_stats.ns_nattab_sz = ipf_nattable_sz;
64353642Sguido		nat_stats.ns_rultab_sz = ipf_natrules_sz;
64453642Sguido		nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz;
64580482Sdarrenr		nat_stats.ns_hostmap_sz = ipf_hostmap_sz;
64653642Sguido		nat_stats.ns_instances = nat_instances;
64753642Sguido		nat_stats.ns_apslist = ap_sess_list;
64860852Sdarrenr		error = IWCOPYPTR((char *)&nat_stats, (char *)data,
64960852Sdarrenr				  sizeof(nat_stats));
65053642Sguido		break;
65153642Sguido	case SIOCGNATL :
65253642Sguido	    {
65353642Sguido		natlookup_t nl;
65453642Sguido
65553642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
65660852Sdarrenr		error = IRCOPYPTR((char *)data, (char *)&nl, sizeof(nl));
65760852Sdarrenr		if (error)
65860852Sdarrenr			break;
65953642Sguido
66053642Sguido		if (nat_lookupredir(&nl)) {
66160852Sdarrenr			error = IWCOPYPTR((char *)&nl, (char *)data,
66260852Sdarrenr					  sizeof(nl));
66353642Sguido		} else
66453642Sguido			error = ESRCH;
66553642Sguido		break;
66653642Sguido	    }
66760852Sdarrenr	case SIOCIPFFL :	/* old SIOCFLNAT & SIOCCNATL */
66853642Sguido		if (!(mode & FWRITE)) {
66953642Sguido			error = EPERM;
67053642Sguido			break;
67153642Sguido		}
67260852Sdarrenr		error = 0;
67360852Sdarrenr		if (arg == 0)
67460852Sdarrenr			ret = nat_flushtable();
67560852Sdarrenr		else if (arg == 1)
67660852Sdarrenr			ret = nat_clearlist();
67760852Sdarrenr		else
67860852Sdarrenr			error = EINVAL;
67953642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
68060852Sdarrenr		if (!error) {
68160852Sdarrenr			error = IWCOPY((caddr_t)&ret, data, sizeof(ret));
68260852Sdarrenr			if (error)
68360852Sdarrenr				error = EFAULT;
68460852Sdarrenr		}
68553642Sguido		break;
68660852Sdarrenr	case SIOCSTLCK :
68760852Sdarrenr		error = IRCOPY(data, (caddr_t)&arg, sizeof(arg));
68860852Sdarrenr		if (!error) {
68960852Sdarrenr			error = IWCOPY((caddr_t)&fr_nat_lock, data,
69060852Sdarrenr					sizeof(fr_nat_lock));
69160852Sdarrenr			if (!error)
69260852Sdarrenr				fr_nat_lock = arg;
69364580Sdarrenr		} else
69464580Sdarrenr			error = EFAULT;
69553642Sguido		break;
69660852Sdarrenr	case SIOCSTPUT :
69760852Sdarrenr		if (fr_nat_lock)
69860852Sdarrenr			error = fr_natputent(data);
69960852Sdarrenr		else
70060852Sdarrenr			error = EACCES;
70160852Sdarrenr		break;
70260852Sdarrenr	case SIOCSTGSZ :
70360852Sdarrenr		if (fr_nat_lock)
70460852Sdarrenr			error = fr_natgetsz(data);
70560852Sdarrenr		else
70660852Sdarrenr			error = EACCES;
70760852Sdarrenr		break;
70860852Sdarrenr	case SIOCSTGET :
70960852Sdarrenr		if (fr_nat_lock)
71060852Sdarrenr			error = fr_natgetent(data);
71160852Sdarrenr		else
71260852Sdarrenr			error = EACCES;
71360852Sdarrenr		break;
71453642Sguido	case FIONREAD :
71553642Sguido#ifdef	IPFILTER_LOG
71672006Sdarrenr		arg = (int)iplused[IPL_LOGNAT];
71753642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
71872006Sdarrenr		error = IWCOPY((caddr_t)&arg, (caddr_t)data, sizeof(arg));
71964580Sdarrenr		if (error)
72064580Sdarrenr			error = EFAULT;
72153642Sguido#endif
72253642Sguido		break;
72353642Sguido	default :
72453642Sguido		error = EINVAL;
72553642Sguido		break;
72653642Sguido	}
72795418Sdarrenr	if (getlock == 1)
72895418Sdarrenr		RWLOCK_EXIT(&ipf_nat);			/* READ/WRITE */
72960852Sdarrenrdone:
73053642Sguido	if (nt)
73153642Sguido		KFREE(nt);
73253642Sguido	return error;
73353642Sguido}
73453642Sguido
73553642Sguido
73660852Sdarrenrstatic int fr_natgetsz(data)
73760852Sdarrenrcaddr_t data;
73860852Sdarrenr{
73960852Sdarrenr	ap_session_t *aps;
74060852Sdarrenr	nat_t *nat, *n;
74160852Sdarrenr	int error = 0;
74260852Sdarrenr	natget_t ng;
74360852Sdarrenr
74460852Sdarrenr	error = IRCOPY(data, (caddr_t)&ng, sizeof(ng));
74560852Sdarrenr	if (error)
74660852Sdarrenr		return EFAULT;
74760852Sdarrenr
74860852Sdarrenr	nat = ng.ng_ptr;
74960852Sdarrenr	if (!nat) {
75060852Sdarrenr		nat = nat_instances;
75160852Sdarrenr		ng.ng_sz = 0;
75260852Sdarrenr		if (nat == NULL) {
75360852Sdarrenr			error = IWCOPY((caddr_t)&ng, data, sizeof(ng));
75460852Sdarrenr			if (error)
75560852Sdarrenr				error = EFAULT;
75660852Sdarrenr			return error;
75760852Sdarrenr		}
75860852Sdarrenr	} else {
75960852Sdarrenr		/*
76060852Sdarrenr		 * Make sure the pointer we're copying from exists in the
76160852Sdarrenr		 * current list of entries.  Security precaution to prevent
76260852Sdarrenr		 * copying of random kernel data.
76360852Sdarrenr		 */
76460852Sdarrenr		for (n = nat_instances; n; n = n->nat_next)
76560852Sdarrenr			if (n == nat)
76660852Sdarrenr				break;
76760852Sdarrenr		if (!n)
76860852Sdarrenr			return ESRCH;
76960852Sdarrenr	}
77060852Sdarrenr
77160852Sdarrenr	ng.ng_sz = sizeof(nat_save_t);
77260852Sdarrenr	aps = nat->nat_aps;
77360852Sdarrenr	if ((aps != NULL) && (aps->aps_data != 0)) {
77460852Sdarrenr		ng.ng_sz += sizeof(ap_session_t);
77560852Sdarrenr		ng.ng_sz += aps->aps_psiz;
77660852Sdarrenr	}
77760852Sdarrenr
77860852Sdarrenr	error = IWCOPY((caddr_t)&ng, data, sizeof(ng));
77960852Sdarrenr	if (error)
78060852Sdarrenr		error = EFAULT;
78160852Sdarrenr	return error;
78260852Sdarrenr}
78360852Sdarrenr
78460852Sdarrenr
78560852Sdarrenrstatic int fr_natgetent(data)
78660852Sdarrenrcaddr_t data;
78760852Sdarrenr{
78864580Sdarrenr	nat_save_t ipn, *ipnp, *ipnn = NULL;
78960852Sdarrenr	register nat_t *n, *nat;
79060852Sdarrenr	ap_session_t *aps;
79160852Sdarrenr	int error;
79260852Sdarrenr
79360852Sdarrenr	error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp));
79460852Sdarrenr	if (error)
79560852Sdarrenr		return EFAULT;
79660852Sdarrenr	error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn));
79760852Sdarrenr	if (error)
79860852Sdarrenr		return EFAULT;
79960852Sdarrenr
80060852Sdarrenr	nat = ipn.ipn_next;
80160852Sdarrenr	if (!nat) {
80260852Sdarrenr		nat = nat_instances;
80360852Sdarrenr		if (nat == NULL) {
80460852Sdarrenr			if (nat_instances == NULL)
80560852Sdarrenr				return ENOENT;
80660852Sdarrenr			return 0;
80760852Sdarrenr		}
80860852Sdarrenr	} else {
80960852Sdarrenr		/*
81060852Sdarrenr		 * Make sure the pointer we're copying from exists in the
81160852Sdarrenr		 * current list of entries.  Security precaution to prevent
81260852Sdarrenr		 * copying of random kernel data.
81360852Sdarrenr		 */
81460852Sdarrenr		for (n = nat_instances; n; n = n->nat_next)
81560852Sdarrenr			if (n == nat)
81660852Sdarrenr				break;
81760852Sdarrenr		if (!n)
81860852Sdarrenr			return ESRCH;
81960852Sdarrenr	}
82060852Sdarrenr
82160852Sdarrenr	ipn.ipn_next = nat->nat_next;
82260852Sdarrenr	ipn.ipn_dsize = 0;
82360852Sdarrenr	bcopy((char *)nat, (char *)&ipn.ipn_nat, sizeof(ipn.ipn_nat));
82460852Sdarrenr	ipn.ipn_nat.nat_data = NULL;
82560852Sdarrenr
82660852Sdarrenr	if (nat->nat_ptr) {
82760852Sdarrenr		bcopy((char *)nat->nat_ptr, (char *)&ipn.ipn_ipnat,
82860852Sdarrenr		      sizeof(ipn.ipn_ipnat));
82960852Sdarrenr	}
83060852Sdarrenr
83160852Sdarrenr	if (nat->nat_fr)
83260852Sdarrenr		bcopy((char *)nat->nat_fr, (char *)&ipn.ipn_rule,
83360852Sdarrenr		      sizeof(ipn.ipn_rule));
83460852Sdarrenr
83560852Sdarrenr	if ((aps = nat->nat_aps)) {
83660852Sdarrenr		ipn.ipn_dsize = sizeof(*aps);
83760852Sdarrenr		if (aps->aps_data)
83860852Sdarrenr			ipn.ipn_dsize += aps->aps_psiz;
83960852Sdarrenr		KMALLOCS(ipnn, nat_save_t *, sizeof(*ipnn) + ipn.ipn_dsize);
84060852Sdarrenr		if (ipnn == NULL)
84164580Sdarrenr			return ENOMEM;
84260852Sdarrenr		bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn));
84360852Sdarrenr
84495418Sdarrenr		bcopy((char *)aps, (char *)ipnn->ipn_data, sizeof(*aps));
84560852Sdarrenr		if (aps->aps_data) {
84664580Sdarrenr			bcopy(aps->aps_data, ipnn->ipn_data + sizeof(*aps),
84760852Sdarrenr			      aps->aps_psiz);
84864580Sdarrenr			ipnn->ipn_dsize += aps->aps_psiz;
84960852Sdarrenr		}
85060852Sdarrenr		error = IWCOPY((caddr_t)ipnn, ipnp,
85160852Sdarrenr			       sizeof(ipn) + ipn.ipn_dsize);
85260852Sdarrenr		if (error)
85364580Sdarrenr			error = EFAULT;
85460852Sdarrenr		KFREES(ipnn, sizeof(*ipnn) + ipn.ipn_dsize);
85560852Sdarrenr	} else {
85660852Sdarrenr		error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn));
85760852Sdarrenr		if (error)
85864580Sdarrenr			error = EFAULT;
85960852Sdarrenr	}
86064580Sdarrenr	return error;
86160852Sdarrenr}
86260852Sdarrenr
86360852Sdarrenr
86460852Sdarrenrstatic int fr_natputent(data)
86560852Sdarrenrcaddr_t data;
86660852Sdarrenr{
86764580Sdarrenr	nat_save_t ipn, *ipnp, *ipnn = NULL;
86860852Sdarrenr	register nat_t *n, *nat;
86960852Sdarrenr	ap_session_t *aps;
87060852Sdarrenr	frentry_t *fr;
87160852Sdarrenr	ipnat_t *in;
87260852Sdarrenr
87360852Sdarrenr	int error;
87460852Sdarrenr
87560852Sdarrenr	error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp));
87660852Sdarrenr	if (error)
87760852Sdarrenr		return EFAULT;
87860852Sdarrenr	error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn));
87960852Sdarrenr	if (error)
88060852Sdarrenr		return EFAULT;
88164580Sdarrenr	nat = NULL;
88260852Sdarrenr	if (ipn.ipn_dsize) {
88360852Sdarrenr		KMALLOCS(ipnn, nat_save_t *, sizeof(ipn) + ipn.ipn_dsize);
88460852Sdarrenr		if (ipnn == NULL)
88560852Sdarrenr			return ENOMEM;
88660852Sdarrenr		bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn));
88760852Sdarrenr		error = IRCOPY((caddr_t)ipnp, (caddr_t)ipn.ipn_data,
88860852Sdarrenr			       ipn.ipn_dsize);
88964580Sdarrenr		if (error) {
89064580Sdarrenr			error = EFAULT;
89164580Sdarrenr			goto junkput;
89264580Sdarrenr		}
89360852Sdarrenr	} else
89460852Sdarrenr		ipnn = NULL;
89560852Sdarrenr
89660852Sdarrenr	KMALLOC(nat, nat_t *);
89764580Sdarrenr	if (nat == NULL) {
89864580Sdarrenr		error = EFAULT;
89964580Sdarrenr		goto junkput;
90064580Sdarrenr	}
90160852Sdarrenr
90260852Sdarrenr	bcopy((char *)&ipn.ipn_nat, (char *)nat, sizeof(*nat));
90360852Sdarrenr	/*
90460852Sdarrenr	 * Initialize all these so that nat_delete() doesn't cause a crash.
90560852Sdarrenr	 */
90667614Sdarrenr	nat->nat_phnext[0] = NULL;
90767614Sdarrenr	nat->nat_phnext[1] = NULL;
90860852Sdarrenr	fr = nat->nat_fr;
90960852Sdarrenr	nat->nat_fr = NULL;
91060852Sdarrenr	aps = nat->nat_aps;
91160852Sdarrenr	nat->nat_aps = NULL;
91260852Sdarrenr	in = nat->nat_ptr;
91360852Sdarrenr	nat->nat_ptr = NULL;
91487394Sguido	nat->nat_hm = NULL;
91560852Sdarrenr	nat->nat_data = NULL;
91692685Sdarrenr	nat->nat_ifp = GETUNIT(nat->nat_ifname, 4);
91760852Sdarrenr
91860852Sdarrenr	/*
91960852Sdarrenr	 * Restore the rule associated with this nat session
92060852Sdarrenr	 */
92160852Sdarrenr	if (in) {
92260852Sdarrenr		KMALLOC(in, ipnat_t *);
92360852Sdarrenr		if (in == NULL) {
92460852Sdarrenr			error = ENOMEM;
92560852Sdarrenr			goto junkput;
92660852Sdarrenr		}
92760852Sdarrenr		nat->nat_ptr = in;
92860852Sdarrenr		bcopy((char *)&ipn.ipn_ipnat, (char *)in, sizeof(*in));
92960852Sdarrenr		in->in_use = 1;
93060852Sdarrenr		in->in_flags |= IPN_DELETE;
93160852Sdarrenr		in->in_next = NULL;
93260852Sdarrenr		in->in_rnext = NULL;
93360852Sdarrenr		in->in_prnext = NULL;
93460852Sdarrenr		in->in_mnext = NULL;
93560852Sdarrenr		in->in_pmnext = NULL;
93660852Sdarrenr		in->in_ifp = GETUNIT(in->in_ifname, 4);
93760852Sdarrenr		if (in->in_plabel[0] != '\0') {
93892685Sdarrenr			in->in_apr = appr_lookup(in->in_p, in->in_plabel);
93960852Sdarrenr		}
94060852Sdarrenr	}
94160852Sdarrenr
94260852Sdarrenr	/*
94360852Sdarrenr	 * Restore ap_session_t structure.  Include the private data allocated
94460852Sdarrenr	 * if it was there.
94560852Sdarrenr	 */
94660852Sdarrenr	if (aps) {
94760852Sdarrenr		KMALLOC(aps, ap_session_t *);
94860852Sdarrenr		if (aps == NULL) {
94960852Sdarrenr			error = ENOMEM;
95060852Sdarrenr			goto junkput;
95160852Sdarrenr		}
95260852Sdarrenr		nat->nat_aps = aps;
95360852Sdarrenr		aps->aps_next = ap_sess_list;
95460852Sdarrenr		ap_sess_list = aps;
95560852Sdarrenr		bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps));
95660852Sdarrenr		if (in)
95760852Sdarrenr			aps->aps_apr = in->in_apr;
95860852Sdarrenr		if (aps->aps_psiz) {
95960852Sdarrenr			KMALLOCS(aps->aps_data, void *, aps->aps_psiz);
96060852Sdarrenr			if (aps->aps_data == NULL) {
96160852Sdarrenr				error = ENOMEM;
96260852Sdarrenr				goto junkput;
96360852Sdarrenr			}
96460852Sdarrenr			bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data,
96560852Sdarrenr			      aps->aps_psiz);
96660852Sdarrenr		} else {
96760852Sdarrenr			aps->aps_psiz = 0;
96860852Sdarrenr			aps->aps_data = NULL;
96960852Sdarrenr		}
97060852Sdarrenr	}
97160852Sdarrenr
97260852Sdarrenr	/*
97360852Sdarrenr	 * If there was a filtering rule associated with this entry then
97460852Sdarrenr	 * build up a new one.
97560852Sdarrenr	 */
97660852Sdarrenr	if (fr != NULL) {
97760852Sdarrenr		if (nat->nat_flags & FI_NEWFR) {
97860852Sdarrenr			KMALLOC(fr, frentry_t *);
97960852Sdarrenr			nat->nat_fr = fr;
98060852Sdarrenr			if (fr == NULL) {
98160852Sdarrenr				error = ENOMEM;
98260852Sdarrenr				goto junkput;
98360852Sdarrenr			}
98460852Sdarrenr			bcopy((char *)&ipn.ipn_fr, (char *)fr, sizeof(*fr));
98560852Sdarrenr			ipn.ipn_nat.nat_fr = fr;
98660852Sdarrenr			error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn));
98760852Sdarrenr			if (error) {
98860852Sdarrenr				error = EFAULT;
98960852Sdarrenr				goto junkput;
99060852Sdarrenr			}
99160852Sdarrenr		} else {
99260852Sdarrenr			for (n = nat_instances; n; n = n->nat_next)
99360852Sdarrenr				if (n->nat_fr == fr)
99460852Sdarrenr					break;
99560852Sdarrenr			if (!n) {
99660852Sdarrenr				error = ESRCH;
99760852Sdarrenr				goto junkput;
99860852Sdarrenr			}
99960852Sdarrenr		}
100060852Sdarrenr	}
100160852Sdarrenr
100260852Sdarrenr	if (ipnn)
100360852Sdarrenr		KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize);
100460852Sdarrenr	nat_insert(nat);
100560852Sdarrenr	return 0;
100660852Sdarrenrjunkput:
100760852Sdarrenr	if (ipnn)
100860852Sdarrenr		KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize);
100960852Sdarrenr	if (nat)
101060852Sdarrenr		nat_delete(nat);
101160852Sdarrenr	return error;
101260852Sdarrenr}
101360852Sdarrenr
101460852Sdarrenr
101553642Sguido/*
101653642Sguido * Delete a nat entry from the various lists and table.
101753642Sguido */
101853642Sguidostatic void nat_delete(natd)
101953642Sguidostruct nat *natd;
102053642Sguido{
102153642Sguido	struct ipnat *ipn;
102253642Sguido
102367614Sdarrenr	if (natd->nat_flags & FI_WILDP)
102467853Sdarrenr		nat_stats.ns_wilds--;
102567614Sdarrenr	if (natd->nat_hnext[0])
102667614Sdarrenr		natd->nat_hnext[0]->nat_phnext[0] = natd->nat_phnext[0];
102767614Sdarrenr	*natd->nat_phnext[0] = natd->nat_hnext[0];
102867614Sdarrenr	if (natd->nat_hnext[1])
102967614Sdarrenr		natd->nat_hnext[1]->nat_phnext[1] = natd->nat_phnext[1];
103067614Sdarrenr	*natd->nat_phnext[1] = natd->nat_hnext[1];
103192685Sdarrenr	if (natd->nat_me != NULL)
103292685Sdarrenr		*natd->nat_me = NULL;
103353642Sguido
103453642Sguido	if (natd->nat_fr != NULL) {
103560852Sdarrenr		ATOMIC_DEC32(natd->nat_fr->fr_ref);
103653642Sguido	}
103760852Sdarrenr
103860852Sdarrenr	if (natd->nat_hm != NULL)
103960852Sdarrenr		nat_hostmapdel(natd->nat_hm);
104060852Sdarrenr
104153642Sguido	/*
104253642Sguido	 * If there is an active reference from the nat entry to its parent
104353642Sguido	 * rule, decrement the rule's reference count and free it too if no
104453642Sguido	 * longer being used.
104553642Sguido	 */
104653642Sguido	ipn = natd->nat_ptr;
104753642Sguido	if (ipn != NULL) {
104853642Sguido		ipn->in_space++;
104953642Sguido		ipn->in_use--;
105053642Sguido		if (!ipn->in_use && (ipn->in_flags & IPN_DELETE)) {
105153642Sguido			if (ipn->in_apr)
105253642Sguido				appr_free(ipn->in_apr);
105353642Sguido			KFREE(ipn);
105453642Sguido			nat_stats.ns_rules--;
105553642Sguido		}
105653642Sguido	}
105753642Sguido
105860852Sdarrenr	MUTEX_DESTROY(&natd->nat_lock);
105953642Sguido	/*
106053642Sguido	 * If there's a fragment table entry too for this nat entry, then
106153642Sguido	 * dereference that as well.
106253642Sguido	 */
106353642Sguido	ipfr_forget((void *)natd);
106453642Sguido	aps_free(natd->nat_aps);
106553642Sguido	nat_stats.ns_inuse--;
106653642Sguido	KFREE(natd);
106753642Sguido}
106853642Sguido
106953642Sguido
107053642Sguido/*
107153642Sguido * nat_flushtable - clear the NAT table of all mapping entries.
107292685Sdarrenr * (this is for the dynamic mappings)
107353642Sguido */
107453642Sguidostatic int nat_flushtable()
107553642Sguido{
107653642Sguido	register nat_t *nat, **natp;
107753642Sguido	register int j = 0;
107867614Sdarrenr
107953642Sguido	/*
108053642Sguido	 * ALL NAT mappings deleted, so lets just make the deletions
108153642Sguido	 * quicker.
108253642Sguido	 */
108353642Sguido	if (nat_table[0] != NULL)
108453642Sguido		bzero((char *)nat_table[0],
108553642Sguido		      sizeof(nat_table[0]) * ipf_nattable_sz);
108653642Sguido	if (nat_table[1] != NULL)
108753642Sguido		bzero((char *)nat_table[1],
108853642Sguido		      sizeof(nat_table[1]) * ipf_nattable_sz);
108953642Sguido
109053642Sguido	for (natp = &nat_instances; (nat = *natp); ) {
109153642Sguido		*natp = nat->nat_next;
109272006Sdarrenr#ifdef	IPFILTER_LOG
109372006Sdarrenr		nat_log(nat, NL_FLUSH);
109472006Sdarrenr#endif
109553642Sguido		nat_delete(nat);
109653642Sguido		j++;
109753642Sguido	}
109853642Sguido	nat_stats.ns_inuse = 0;
109953642Sguido	return j;
110053642Sguido}
110153642Sguido
110253642Sguido
110353642Sguido/*
110453642Sguido * nat_clearlist - delete all rules in the active NAT mapping list.
110592685Sdarrenr * (this is for NAT/RDR rules)
110653642Sguido */
110792685Sdarrenrint nat_clearlist()
110853642Sguido{
110953642Sguido	register ipnat_t *n, **np = &nat_list;
111053642Sguido	int i = 0;
111153642Sguido
111253642Sguido	if (nat_rules != NULL)
111353642Sguido		bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz);
111453642Sguido	if (rdr_rules != NULL)
111553642Sguido		bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz);
111653642Sguido
111753642Sguido	while ((n = *np)) {
111853642Sguido		*np = n->in_next;
111953642Sguido		if (!n->in_use) {
112053642Sguido			if (n->in_apr)
112153642Sguido				appr_free(n->in_apr);
112253642Sguido			KFREE(n);
112353642Sguido			nat_stats.ns_rules--;
112453642Sguido		} else {
112553642Sguido			n->in_flags |= IPN_DELETE;
112653642Sguido			n->in_next = NULL;
112753642Sguido		}
112853642Sguido		i++;
112953642Sguido	}
113053642Sguido	nat_masks = 0;
113153642Sguido	rdr_masks = 0;
113253642Sguido	return i;
113353642Sguido}
113453642Sguido
113553642Sguido
113653642Sguido/*
113753642Sguido * Create a new NAT table entry.
113892685Sdarrenr * NOTE: Assumes write lock on ipf_nat has been obtained already.
113992685Sdarrenr *       If you intend on changing this, beware: appr_new() may call nat_new()
114092685Sdarrenr *       recursively!
114153642Sguido */
114292685Sdarrenrnat_t *nat_new(fin, ip, np, natsave, flags, direction)
114392685Sdarrenrfr_info_t *fin;
114492685Sdarrenrip_t *ip;
114553642Sguidoipnat_t *np;
114692685Sdarrenrnat_t **natsave;
114753642Sguidou_int flags;
114853642Sguidoint direction;
114953642Sguido{
115053642Sguido	register u_32_t sum1, sum2, sumd, l;
115153642Sguido	u_short port = 0, sport = 0, dport = 0, nport = 0;
115253642Sguido	struct in_addr in, inb;
115392685Sdarrenr	u_short nflags, sp, dp;
115453642Sguido	tcphdr_t *tcp = NULL;
115560852Sdarrenr	hostmap_t *hm = NULL;
115660852Sdarrenr	nat_t *nat, *natl;
115755929Sguido#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6)
115855929Sguido	qif_t *qf = fin->fin_qif;
115955929Sguido#endif
116053642Sguido
116153642Sguido	nflags = flags & np->in_flags;
116253642Sguido	if (flags & IPN_TCPUDP) {
116353642Sguido		tcp = (tcphdr_t *)fin->fin_dp;
116492685Sdarrenr		sport = htons(fin->fin_data[0]);
116592685Sdarrenr		dport = htons(fin->fin_data[1]);
116653642Sguido	}
116753642Sguido
116853642Sguido	/* Give me a new nat */
116953642Sguido	KMALLOC(nat, nat_t *);
117060852Sdarrenr	if (nat == NULL) {
117160852Sdarrenr		nat_stats.ns_memfail++;
117253642Sguido		return NULL;
117360852Sdarrenr	}
117453642Sguido
117553642Sguido	bzero((char *)nat, sizeof(*nat));
117653642Sguido	nat->nat_flags = flags;
117767614Sdarrenr	if (flags & FI_WILDP)
117867853Sdarrenr		nat_stats.ns_wilds++;
117953642Sguido	/*
118053642Sguido	 * Search the current table for a match.
118153642Sguido	 */
118253642Sguido	if (direction == NAT_OUTBOUND) {
118353642Sguido		/*
118453642Sguido		 * Values at which the search for a free resouce starts.
118553642Sguido		 */
118653642Sguido		u_32_t st_ip;
118753642Sguido		u_short st_port;
118853642Sguido
118953642Sguido		/*
119053642Sguido		 * If it's an outbound packet which doesn't match any existing
119153642Sguido		 * record, then create a new port
119253642Sguido		 */
119353642Sguido		l = 0;
119453642Sguido		st_ip = np->in_nip;
119553642Sguido		st_port = np->in_pnext;
119653642Sguido
119753642Sguido		do {
119853642Sguido			port = 0;
119960852Sdarrenr			in.s_addr = htonl(np->in_nip);
120053642Sguido			if (l == 0) {
120157096Sguido				/*
120257096Sguido				 * Check to see if there is an existing NAT
120357096Sguido				 * setup for this IP address pair.
120457096Sguido				 */
120592685Sdarrenr				hm = nat_hostmap(np, fin->fin_src, in);
120660852Sdarrenr				if (hm != NULL)
120760852Sdarrenr					in.s_addr = hm->hm_mapip.s_addr;
120860852Sdarrenr			} else if ((l == 1) && (hm != NULL)) {
120960852Sdarrenr				nat_hostmapdel(hm);
121060852Sdarrenr				hm = NULL;
121153642Sguido			}
121260852Sdarrenr			in.s_addr = ntohl(in.s_addr);
121353642Sguido
121460852Sdarrenr			nat->nat_hm = hm;
121560852Sdarrenr
121653642Sguido			if ((np->in_outmsk == 0xffffffff) &&
121753642Sguido			    (np->in_pnext == 0)) {
121860852Sdarrenr				if (l > 0)
121960852Sdarrenr					goto badnat;
122053642Sguido			}
122153642Sguido
122253642Sguido			if (np->in_redir & NAT_MAPBLK) {
122353642Sguido				if ((l >= np->in_ppip) || ((l > 0) &&
122460852Sdarrenr				     !(flags & IPN_TCPUDP)))
122560852Sdarrenr					goto badnat;
122653642Sguido				/*
122753642Sguido				 * map-block - Calculate destination address.
122853642Sguido				 */
122992685Sdarrenr				in.s_addr = ntohl(fin->fin_saddr);
123053642Sguido				in.s_addr &= ntohl(~np->in_inmsk);
123153642Sguido				inb.s_addr = in.s_addr;
123253642Sguido				in.s_addr /= np->in_ippip;
123353642Sguido				in.s_addr &= ntohl(~np->in_outmsk);
123453642Sguido				in.s_addr += ntohl(np->in_outip);
123553642Sguido				/*
123653642Sguido				 * Calculate destination port.
123753642Sguido				 */
123853642Sguido				if ((flags & IPN_TCPUDP) &&
123953642Sguido				    (np->in_ppip != 0)) {
124053642Sguido					port = ntohs(sport) + l;
124153642Sguido					port %= np->in_ppip;
124253642Sguido					port += np->in_ppip *
124353642Sguido						(inb.s_addr % np->in_ippip);
124453642Sguido					port += MAPBLK_MINPORT;
124553642Sguido					port = htons(port);
124653642Sguido				}
124760852Sdarrenr			} else if (!np->in_outip &&
124853642Sguido				   (np->in_outmsk == 0xffffffff)) {
124953642Sguido				/*
125053642Sguido				 * 0/32 - use the interface's IP address.
125153642Sguido				 */
125253642Sguido				if ((l > 0) ||
125360852Sdarrenr				    fr_ifpaddr(4, fin->fin_ifp, &in) == -1)
125460852Sdarrenr					goto badnat;
125555929Sguido				in.s_addr = ntohl(in.s_addr);
125660852Sdarrenr			} else if (!np->in_outip && !np->in_outmsk) {
125753642Sguido				/*
125853642Sguido				 * 0/0 - use the original source address/port.
125953642Sguido				 */
126060852Sdarrenr				if (l > 0)
126160852Sdarrenr					goto badnat;
126292685Sdarrenr				in.s_addr = ntohl(fin->fin_saddr);
126353642Sguido			} else if ((np->in_outmsk != 0xffffffff) &&
126453642Sguido				   (np->in_pnext == 0) &&
126560852Sdarrenr				   ((l > 0) || (hm == NULL)))
126653642Sguido				np->in_nip++;
126753642Sguido			natl = NULL;
126853642Sguido
126953642Sguido			if ((nflags & IPN_TCPUDP) &&
127053642Sguido			    ((np->in_redir & NAT_MAPBLK) == 0) &&
127153642Sguido			    (np->in_flags & IPN_AUTOPORTMAP)) {
127253642Sguido				if ((l > 0) && (l % np->in_ppip == 0)) {
127353642Sguido					if (l > np->in_space) {
127460852Sdarrenr						goto badnat;
127553642Sguido					} else if ((l > np->in_ppip) &&
127653642Sguido						   np->in_outmsk != 0xffffffff)
127753642Sguido						np->in_nip++;
127853642Sguido				}
127953642Sguido				if (np->in_ppip != 0) {
128053642Sguido					port = ntohs(sport);
128153642Sguido					port += (l % np->in_ppip);
128253642Sguido					port %= np->in_ppip;
128353642Sguido					port += np->in_ppip *
128492685Sdarrenr						(ntohl(fin->fin_saddr) %
128553642Sguido						 np->in_ippip);
128653642Sguido					port += MAPBLK_MINPORT;
128753642Sguido					port = htons(port);
128853642Sguido				}
128953642Sguido			} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
129053642Sguido				   (nflags & IPN_TCPUDP) &&
129153642Sguido				   (np->in_pnext != 0)) {
129253642Sguido				port = htons(np->in_pnext++);
129353642Sguido				if (np->in_pnext > ntohs(np->in_pmax)) {
129453642Sguido					np->in_pnext = ntohs(np->in_pmin);
129553642Sguido					if (np->in_outmsk != 0xffffffff)
129653642Sguido						np->in_nip++;
129753642Sguido				}
129853642Sguido			}
129953642Sguido
130060852Sdarrenr			if (np->in_flags & IPN_IPRANGE) {
130160852Sdarrenr				if (np->in_nip > ntohl(np->in_outmsk))
130253642Sguido					np->in_nip = ntohl(np->in_outip);
130353642Sguido			} else {
130453642Sguido				if ((np->in_outmsk != 0xffffffff) &&
130553642Sguido				    ((np->in_nip + 1) & ntohl(np->in_outmsk)) >
130653642Sguido				    ntohl(np->in_outip))
130753642Sguido					np->in_nip = ntohl(np->in_outip) + 1;
130853642Sguido			}
130953642Sguido
131053642Sguido			if (!port && (flags & IPN_TCPUDP))
131153642Sguido				port = sport;
131253642Sguido
131353642Sguido			/*
131453642Sguido			 * Here we do a lookup of the connection as seen from
131553642Sguido			 * the outside.  If an IP# pair already exists, try
131653642Sguido			 * again.  So if you have A->B becomes C->B, you can
131753642Sguido			 * also have D->E become C->E but not D->B causing
131853642Sguido			 * another C->B.  Also take protocol and ports into
131953642Sguido			 * account when determining whether a pre-existing
132053642Sguido			 * NAT setup will cause an external conflict where
132153642Sguido			 * this is appropriate.
132253642Sguido			 */
132353642Sguido			inb.s_addr = htonl(in.s_addr);
132492685Sdarrenr			sp = fin->fin_data[0];
132592685Sdarrenr			dp = fin->fin_data[1];
132692685Sdarrenr			fin->fin_data[0] = fin->fin_data[1];
132792685Sdarrenr			fin->fin_data[1] = htons(port);
132892685Sdarrenr			natl = nat_inlookup(fin, flags & ~FI_WILDP,
132992685Sdarrenr					    (u_int)fin->fin_p, fin->fin_dst,
133092685Sdarrenr					    inb, 1);
133192685Sdarrenr			fin->fin_data[0] = sp;
133292685Sdarrenr			fin->fin_data[1] = dp;
133353642Sguido
133453642Sguido			/*
133553642Sguido			 * Has the search wrapped around and come back to the
133653642Sguido			 * start ?
133753642Sguido			 */
133853642Sguido			if ((natl != NULL) &&
133953642Sguido			    (np->in_pnext != 0) && (st_port == np->in_pnext) &&
134060852Sdarrenr			    (np->in_nip != 0) && (st_ip == np->in_nip))
134160852Sdarrenr				goto badnat;
134253642Sguido			l++;
134353642Sguido		} while (natl != NULL);
134453642Sguido
134553642Sguido		if (np->in_space > 0)
134653642Sguido			np->in_space--;
134753642Sguido
134853642Sguido		/* Setup the NAT table */
134992685Sdarrenr		nat->nat_inip = fin->fin_src;
135053642Sguido		nat->nat_outip.s_addr = htonl(in.s_addr);
135192685Sdarrenr		nat->nat_oip = fin->fin_dst;
135260852Sdarrenr		if (nat->nat_hm == NULL)
135392685Sdarrenr			nat->nat_hm = nat_hostmap(np, fin->fin_src,
135460852Sdarrenr						  nat->nat_outip);
135553642Sguido
135692685Sdarrenr		sum1 = LONG_SUM(ntohl(fin->fin_saddr)) + ntohs(sport);
135753642Sguido		sum2 = LONG_SUM(in.s_addr) + ntohs(port);
135853642Sguido
135953642Sguido		if (flags & IPN_TCPUDP) {
136053642Sguido			nat->nat_inport = sport;
136153642Sguido			nat->nat_outport = port;	/* sport */
136253642Sguido			nat->nat_oport = dport;
136353642Sguido		}
136453642Sguido	} else {
136553642Sguido		/*
136653642Sguido		 * Otherwise, it's an inbound packet. Most likely, we don't
136753642Sguido		 * want to rewrite source ports and source addresses. Instead,
136853642Sguido		 * we want to rewrite to a fixed internal address and fixed
136953642Sguido		 * internal port.
137053642Sguido		 */
137160852Sdarrenr		if (np->in_flags & IPN_SPLIT) {
137260852Sdarrenr			in.s_addr = np->in_nip;
137360852Sdarrenr			if (np->in_inip == htonl(in.s_addr))
137460852Sdarrenr				np->in_nip = ntohl(np->in_inmsk);
137560852Sdarrenr			else {
137660852Sdarrenr				np->in_nip = ntohl(np->in_inip);
137760852Sdarrenr				if (np->in_flags & IPN_ROUNDR) {
137860852Sdarrenr					nat_delrdr(np);
137960852Sdarrenr					nat_addrdr(np);
138060852Sdarrenr				}
138160852Sdarrenr			}
138260852Sdarrenr		} else {
138360852Sdarrenr			in.s_addr = ntohl(np->in_inip);
138460852Sdarrenr			if (np->in_flags & IPN_ROUNDR) {
138560852Sdarrenr				nat_delrdr(np);
138660852Sdarrenr				nat_addrdr(np);
138760852Sdarrenr			}
138860852Sdarrenr		}
138960852Sdarrenr		if (!np->in_pnext)
139053642Sguido			nport = dport;
139160852Sdarrenr		else {
139260852Sdarrenr			/*
139360852Sdarrenr			 * Whilst not optimized for the case where
139460852Sdarrenr			 * pmin == pmax, the gain is not significant.
139560852Sdarrenr			 */
139692685Sdarrenr			if (np->in_pmin != np->in_pmax) {
139792685Sdarrenr				nport = ntohs(dport) - ntohs(np->in_pmin) +
139892685Sdarrenr					ntohs(np->in_pnext);
139992685Sdarrenr				nport = ntohs(nport);
140092685Sdarrenr			} else
140192685Sdarrenr				nport = np->in_pnext;
140260852Sdarrenr		}
140353642Sguido
140453642Sguido		/*
140553642Sguido		 * When the redirect-to address is set to 0.0.0.0, just
140692685Sdarrenr		 * assume a blank `forwarding' of the packet.
140753642Sguido		 */
140892685Sdarrenr		if (in.s_addr == 0)
140992685Sdarrenr			in.s_addr = ntohl(fin->fin_daddr);
141053642Sguido
141153642Sguido		nat->nat_inip.s_addr = htonl(in.s_addr);
141292685Sdarrenr		nat->nat_outip = fin->fin_dst;
141392685Sdarrenr		nat->nat_oip = fin->fin_src;
141453642Sguido
141592685Sdarrenr		sum1 = LONG_SUM(ntohl(fin->fin_daddr)) + ntohs(dport);
141653642Sguido		sum2 = LONG_SUM(in.s_addr) + ntohs(nport);
141753642Sguido
141853642Sguido		if (flags & IPN_TCPUDP) {
141953642Sguido			nat->nat_inport = nport;
142053642Sguido			nat->nat_outport = dport;
142153642Sguido			nat->nat_oport = sport;
142253642Sguido		}
142353642Sguido	}
142453642Sguido
142553642Sguido	CALC_SUMD(sum1, sum2, sumd);
142655929Sguido	nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
142755929Sguido#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6)
142880482Sdarrenr	if ((flags & IPN_TCPUDP) && dohwcksum &&
142955929Sguido	    (qf->qf_ill->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) {
143055929Sguido		if (direction == NAT_OUTBOUND)
143155929Sguido			sum1 = LONG_SUM(ntohl(in.s_addr));
143255929Sguido		else
143392685Sdarrenr			sum1 = LONG_SUM(ntohl(fin->fin_saddr));
143492685Sdarrenr		sum1 += LONG_SUM(ntohl(fin->fin_daddr));
143580482Sdarrenr		sum1 += IPPROTO_TCP;
143655929Sguido		sum1 = (sum1 & 0xffff) + (sum1 >> 16);
143755929Sguido		nat->nat_sumd[1] = NAT_HW_CKSUM|(sum1 & 0xffff);
143855929Sguido	} else
143955929Sguido#endif
144055929Sguido		nat->nat_sumd[1] = nat->nat_sumd[0];
144153642Sguido
144253642Sguido	if ((flags & IPN_TCPUDP) && ((sport != port) || (dport != nport))) {
144353642Sguido		if (direction == NAT_OUTBOUND)
144492685Sdarrenr			sum1 = LONG_SUM(ntohl(fin->fin_saddr));
144553642Sguido		else
144692685Sdarrenr			sum1 = LONG_SUM(ntohl(fin->fin_daddr));
144753642Sguido
144853642Sguido		sum2 = LONG_SUM(in.s_addr);
144953642Sguido
145053642Sguido		CALC_SUMD(sum1, sum2, sumd);
145153642Sguido		nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
145253642Sguido	} else
145355929Sguido		nat->nat_ipsumd = nat->nat_sumd[0];
145453642Sguido
145553642Sguido	in.s_addr = htonl(in.s_addr);
145660852Sdarrenr
145760852Sdarrenr	strncpy(nat->nat_ifname, IFNAME(fin->fin_ifp), IFNAMSIZ);
145860852Sdarrenr
145992685Sdarrenr	nat->nat_me = natsave;
146053642Sguido	nat->nat_dir = direction;
146153642Sguido	nat->nat_ifp = fin->fin_ifp;
146253642Sguido	nat->nat_ptr = np;
146392685Sdarrenr	nat->nat_p = fin->fin_p;
146453642Sguido	nat->nat_bytes = 0;
146553642Sguido	nat->nat_pkts = 0;
146653642Sguido	nat->nat_fr = fin->fin_fr;
146753642Sguido	if (nat->nat_fr != NULL) {
146860852Sdarrenr		ATOMIC_INC32(nat->nat_fr->fr_ref);
146953642Sguido	}
147053642Sguido	if (direction == NAT_OUTBOUND) {
147153642Sguido		if (flags & IPN_TCPUDP)
147253642Sguido			tcp->th_sport = port;
147353642Sguido	} else {
147453642Sguido		if (flags & IPN_TCPUDP)
147553642Sguido			tcp->th_dport = nport;
147653642Sguido	}
147792685Sdarrenr
147892685Sdarrenr	nat_insert(nat);
147992685Sdarrenr
148092685Sdarrenr	if ((np->in_apr != NULL) && (np->in_dport == 0 ||
148192685Sdarrenr	    (tcp != NULL && dport == np->in_dport)))
148292685Sdarrenr		(void) appr_new(fin, ip, nat);
148392685Sdarrenr
148453642Sguido	np->in_use++;
148572006Sdarrenr#ifdef	IPFILTER_LOG
148672006Sdarrenr	nat_log(nat, (u_int)np->in_redir);
148772006Sdarrenr#endif
148853642Sguido	return nat;
148960852Sdarrenrbadnat:
149060852Sdarrenr	nat_stats.ns_badnat++;
149160852Sdarrenr	if ((hm = nat->nat_hm) != NULL)
149260852Sdarrenr		nat_hostmapdel(hm);
149360852Sdarrenr	KFREE(nat);
149460852Sdarrenr	return NULL;
149553642Sguido}
149653642Sguido
149753642Sguido
149892685Sdarrenr/*
149992685Sdarrenr * Insert a NAT entry into the hash tables for searching and add it to the
150092685Sdarrenr * list of active NAT entries.  Adjust global counters when complete.
150192685Sdarrenr */
150260852Sdarrenrvoid	nat_insert(nat)
150360852Sdarrenrnat_t	*nat;
150460852Sdarrenr{
150580482Sdarrenr	u_int hv1, hv2;
150660852Sdarrenr	nat_t **natp;
150760852Sdarrenr
150860852Sdarrenr	MUTEX_INIT(&nat->nat_lock, "nat entry lock", NULL);
150960852Sdarrenr
151060852Sdarrenr	nat->nat_age = fr_defnatage;
151160852Sdarrenr	nat->nat_ifname[sizeof(nat->nat_ifname) - 1] = '\0';
151260852Sdarrenr	if (nat->nat_ifname[0] !='\0') {
151360852Sdarrenr		nat->nat_ifp = GETUNIT(nat->nat_ifname, 4);
151460852Sdarrenr	}
151560852Sdarrenr
151660852Sdarrenr	nat->nat_next = nat_instances;
151760852Sdarrenr	nat_instances = nat;
151867614Sdarrenr
151980482Sdarrenr	if (!(nat->nat_flags & (FI_W_SPORT|FI_W_DPORT))) {
152080482Sdarrenr		hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport,
152180482Sdarrenr				  0xffffffff);
152280482Sdarrenr		hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport,
152380482Sdarrenr				  ipf_nattable_sz);
152480482Sdarrenr		hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport,
152580482Sdarrenr				  0xffffffff);
152680482Sdarrenr		hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport,
152780482Sdarrenr				 ipf_nattable_sz);
152880482Sdarrenr	} else {
152992685Sdarrenr		hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, nat->nat_inip.s_addr,
153092685Sdarrenr				  ipf_nattable_sz);
153192685Sdarrenr		hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, nat->nat_outip.s_addr,
153292685Sdarrenr				  ipf_nattable_sz);
153380482Sdarrenr	}
153480482Sdarrenr
153580482Sdarrenr	natp = &nat_table[0][hv1];
153667614Sdarrenr	if (*natp)
153767614Sdarrenr		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
153867614Sdarrenr	nat->nat_phnext[0] = natp;
153960852Sdarrenr	nat->nat_hnext[0] = *natp;
154060852Sdarrenr	*natp = nat;
154167614Sdarrenr
154280482Sdarrenr	natp = &nat_table[1][hv2];
154367614Sdarrenr	if (*natp)
154467614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
154567614Sdarrenr	nat->nat_phnext[1] = natp;
154660852Sdarrenr	nat->nat_hnext[1] = *natp;
154760852Sdarrenr	*natp = nat;
154860852Sdarrenr
154960852Sdarrenr	nat_stats.ns_added++;
155060852Sdarrenr	nat_stats.ns_inuse++;
155160852Sdarrenr}
155260852Sdarrenr
155360852Sdarrenr
155460852Sdarrenrnat_t *nat_icmplookup(ip, fin, dir)
155553642Sguidoip_t *ip;
155653642Sguidofr_info_t *fin;
155760852Sdarrenrint dir;
155853642Sguido{
155953642Sguido	icmphdr_t *icmp;
156053642Sguido	tcphdr_t *tcp = NULL;
156153642Sguido	ip_t *oip;
156264580Sdarrenr	int flags = 0, type, minlen;
156353642Sguido
156453642Sguido	icmp = (icmphdr_t *)fin->fin_dp;
156553642Sguido	/*
156653642Sguido	 * Does it at least have the return (basic) IP header ?
156753642Sguido	 * Only a basic IP header (no options) should be with an ICMP error
156853642Sguido	 * header.
156953642Sguido	 */
157053642Sguido	if ((ip->ip_hl != 5) || (ip->ip_len < ICMPERR_MINPKTLEN))
157153642Sguido		return NULL;
157253642Sguido	type = icmp->icmp_type;
157353642Sguido	/*
157453642Sguido	 * If it's not an error type, then return.
157553642Sguido	 */
157653642Sguido	if ((type != ICMP_UNREACH) && (type != ICMP_SOURCEQUENCH) &&
157753642Sguido	    (type != ICMP_REDIRECT) && (type != ICMP_TIMXCEED) &&
157853642Sguido	    (type != ICMP_PARAMPROB))
157953642Sguido		return NULL;
158053642Sguido
158153642Sguido	oip = (ip_t *)((char *)fin->fin_dp + 8);
158264580Sdarrenr	minlen = (oip->ip_hl << 2);
158364580Sdarrenr	if (minlen < sizeof(ip_t))
158453642Sguido		return NULL;
158564580Sdarrenr	if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen)
158664580Sdarrenr		return NULL;
158764580Sdarrenr	/*
158864580Sdarrenr	 * Is the buffer big enough for all of it ?  It's the size of the IP
158964580Sdarrenr	 * header claimed in the encapsulated part which is of concern.  It
159064580Sdarrenr	 * may be too big to be in this buffer but not so big that it's
159164580Sdarrenr	 * outside the ICMP packet, leading to TCP deref's causing problems.
159264580Sdarrenr	 * This is possible because we don't know how big oip_hl is when we
159364580Sdarrenr	 * do the pullup early in fr_check() and thus can't gaurantee it is
159464580Sdarrenr	 * all here now.
159564580Sdarrenr	 */
159664580Sdarrenr#ifdef  _KERNEL
159764580Sdarrenr	{
159864580Sdarrenr	mb_t *m;
159964580Sdarrenr
160064580Sdarrenr# if SOLARIS
160164580Sdarrenr	m = fin->fin_qfm;
160264580Sdarrenr	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr)
160364580Sdarrenr		return NULL;
160464580Sdarrenr# else
160564580Sdarrenr	m = *(mb_t **)fin->fin_mp;
160664580Sdarrenr	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
160764580Sdarrenr	    (char *)ip + m->m_len)
160864580Sdarrenr		return NULL;
160964580Sdarrenr# endif
161064580Sdarrenr	}
161164580Sdarrenr#endif
161264580Sdarrenr
161353642Sguido	if (oip->ip_p == IPPROTO_TCP)
161453642Sguido		flags = IPN_TCP;
161553642Sguido	else if (oip->ip_p == IPPROTO_UDP)
161653642Sguido		flags = IPN_UDP;
161753642Sguido	if (flags & IPN_TCPUDP) {
161892685Sdarrenr		u_short	data[2];
161992685Sdarrenr		nat_t *nat;
162092685Sdarrenr
162164580Sdarrenr		minlen += 8;		/* + 64bits of data to get ports */
162264580Sdarrenr		if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen)
162364580Sdarrenr			return NULL;
162492685Sdarrenr
162592685Sdarrenr		data[0] = fin->fin_data[0];
162692685Sdarrenr		data[1] = fin->fin_data[1];
162753642Sguido		tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2));
162892685Sdarrenr		fin->fin_data[0] = ntohs(tcp->th_dport);
162992685Sdarrenr		fin->fin_data[1] = ntohs(tcp->th_sport);
163092685Sdarrenr
163192685Sdarrenr		if (dir == NAT_INBOUND) {
163292685Sdarrenr			nat = nat_inlookup(fin, flags, (u_int)oip->ip_p,
163392685Sdarrenr					    oip->ip_dst, oip->ip_src, 0);
163492685Sdarrenr		} else {
163592685Sdarrenr			nat = nat_outlookup(fin, flags, (u_int)oip->ip_p,
163692685Sdarrenr					    oip->ip_dst, oip->ip_src, 0);
163792685Sdarrenr		}
163892685Sdarrenr		fin->fin_data[0] = data[0];
163992685Sdarrenr		fin->fin_data[1] = data[1];
164092685Sdarrenr		return nat;
164153642Sguido	}
164260852Sdarrenr	if (dir == NAT_INBOUND)
164392685Sdarrenr		return nat_inlookup(fin, 0, (u_int)oip->ip_p,
164492685Sdarrenr				    oip->ip_dst, oip->ip_src, 0);
164560852Sdarrenr	else
164692685Sdarrenr		return nat_outlookup(fin, 0, (u_int)oip->ip_p,
164792685Sdarrenr				    oip->ip_dst, oip->ip_src, 0);
164853642Sguido}
164953642Sguido
165053642Sguido
165153642Sguido/*
165253642Sguido * This should *ONLY* be used for incoming packets to make sure a NAT'd ICMP
165353642Sguido * packet gets correctly recognised.
165453642Sguido */
165560852Sdarrenrnat_t *nat_icmp(ip, fin, nflags, dir)
165653642Sguidoip_t *ip;
165753642Sguidofr_info_t *fin;
165853642Sguidou_int *nflags;
165960852Sdarrenrint dir;
166053642Sguido{
166172006Sdarrenr	u_32_t sum1, sum2, sumd, sumd2 = 0;
166253642Sguido	struct in_addr in;
166395418Sdarrenr	int flags, dlen;
166453642Sguido	icmphdr_t *icmp;
166567614Sdarrenr	udphdr_t *udp;
166695418Sdarrenr	tcphdr_t *tcp;
166753642Sguido	nat_t *nat;
166853642Sguido	ip_t *oip;
166953642Sguido
167080482Sdarrenr	if ((fin->fin_fl & FI_SHORT) || (fin->fin_off != 0))
167163523Sdarrenr		return NULL;
167267614Sdarrenr	/*
167367614Sdarrenr	 * nat_icmplookup() will return NULL for `defective' packets.
167467614Sdarrenr	 */
167560852Sdarrenr	if ((ip->ip_v != 4) || !(nat = nat_icmplookup(ip, fin, dir)))
167653642Sguido		return NULL;
167792685Sdarrenr
167892685Sdarrenr	flags = 0;
167953642Sguido	*nflags = IPN_ICMPERR;
168053642Sguido	icmp = (icmphdr_t *)fin->fin_dp;
168153642Sguido	oip = (ip_t *)&icmp->icmp_ip;
168253642Sguido	if (oip->ip_p == IPPROTO_TCP)
168353642Sguido		flags = IPN_TCP;
168453642Sguido	else if (oip->ip_p == IPPROTO_UDP)
168553642Sguido		flags = IPN_UDP;
168667614Sdarrenr	udp = (udphdr_t *)((((char *)oip) + (oip->ip_hl << 2)));
168795418Sdarrenr	dlen = ip->ip_len - ((char *)udp - (char *)ip);
168853642Sguido	/*
168995418Sdarrenr	 * XXX - what if this is bogus hl and we go off the end ?
169095418Sdarrenr	 * In this case, nat_icmplookup() will have returned NULL.
169195418Sdarrenr	 */
169295418Sdarrenr	tcp = (tcphdr_t *)udp;
169395418Sdarrenr
169495418Sdarrenr	/*
169553642Sguido	 * Need to adjust ICMP header to include the real IP#'s and
169653642Sguido	 * port #'s.  Only apply a checksum change relative to the
169767614Sdarrenr	 * IP address change as it will be modified again in ip_natout
169853642Sguido	 * for both address and port.  Two checksum changes are
169953642Sguido	 * necessary for the two header address changes.  Be careful
170053642Sguido	 * to only modify the checksum once for the port # and twice
170153642Sguido	 * for the IP#.
170253642Sguido	 */
170360852Sdarrenr
170467614Sdarrenr	/*
170567614Sdarrenr	 * Step 1
170667614Sdarrenr	 * Fix the IP addresses in the offending IP packet. You also need
170767614Sdarrenr	 * to adjust the IP header checksum of that offending IP packet
170867614Sdarrenr	 * and the ICMP checksum of the ICMP error message itself.
170967614Sdarrenr	 *
171067614Sdarrenr	 * Unfortunately, for UDP and TCP, the IP addresses are also contained
171167614Sdarrenr	 * in the pseudo header that is used to compute the UDP resp. TCP
171267614Sdarrenr	 * checksum. So, we must compensate that as well. Even worse, the
171367614Sdarrenr	 * change in the UDP and TCP checksums require yet another
171467614Sdarrenr	 * adjustment of the ICMP checksum of the ICMP error message.
171567614Sdarrenr	 *
171667614Sdarrenr	 */
171767614Sdarrenr
171872006Sdarrenr	if (oip->ip_dst.s_addr == nat->nat_oip.s_addr) {
171953642Sguido		sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr));
172053642Sguido		in = nat->nat_inip;
172153642Sguido		oip->ip_src = in;
172253642Sguido	} else {
172353642Sguido		sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr));
172453642Sguido		in = nat->nat_outip;
172553642Sguido		oip->ip_dst = in;
172653642Sguido	}
172753642Sguido
172853642Sguido	sum2 = LONG_SUM(ntohl(in.s_addr));
172953642Sguido
173053642Sguido	CALC_SUMD(sum1, sum2, sumd);
173153642Sguido
173253642Sguido	if (nat->nat_dir == NAT_OUTBOUND) {
173367614Sdarrenr		/*
173467614Sdarrenr		 * Fix IP checksum of the offending IP packet to adjust for
173567614Sdarrenr		 * the change in the IP address.
173667614Sdarrenr		 *
173767614Sdarrenr		 * Normally, you would expect that the ICMP checksum of the
173867614Sdarrenr		 * ICMP error message needs to be adjusted as well for the
173967614Sdarrenr		 * IP address change in oip.
174067614Sdarrenr		 * However, this is a NOP, because the ICMP checksum is
174167614Sdarrenr		 * calculated over the complete ICMP packet, which includes the
174267614Sdarrenr		 * changed oip IP addresses and oip->ip_sum. However, these
174367614Sdarrenr		 * two changes cancel each other out (if the delta for
174467614Sdarrenr		 * the IP address is x, then the delta for ip_sum is minus x),
174567614Sdarrenr		 * so no change in the icmp_cksum is necessary.
174667614Sdarrenr		 *
174767614Sdarrenr		 * Be careful that nat_dir refers to the direction of the
174867614Sdarrenr		 * offending IP packet (oip), not to its ICMP response (icmp)
174967614Sdarrenr		 */
175067614Sdarrenr		fix_datacksum(&oip->ip_sum, sumd);
175153642Sguido
175267614Sdarrenr		/*
175367614Sdarrenr		 * Fix UDP pseudo header checksum to compensate for the
175467614Sdarrenr		 * IP address change.
175567614Sdarrenr		 */
175667614Sdarrenr		if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) {
175767614Sdarrenr			/*
175867614Sdarrenr			 * The UDP checksum is optional, only adjust it
175967614Sdarrenr			 * if it has been set.
176067614Sdarrenr			 */
176167614Sdarrenr			sum1 = ntohs(udp->uh_sum);
176267614Sdarrenr			fix_datacksum(&udp->uh_sum, sumd);
176367614Sdarrenr			sum2 = ntohs(udp->uh_sum);
176467614Sdarrenr
176567614Sdarrenr			/*
176667614Sdarrenr			 * Fix ICMP checksum to compensate the UDP
176767614Sdarrenr			 * checksum adjustment.
176867614Sdarrenr			 */
176967614Sdarrenr			CALC_SUMD(sum1, sum2, sumd);
177072006Sdarrenr			sumd2 = sumd;
177167614Sdarrenr		}
177267614Sdarrenr
177395418Sdarrenr#if 1
177467614Sdarrenr		/*
177567614Sdarrenr		 * Fix TCP pseudo header checksum to compensate for the
177667614Sdarrenr		 * IP address change. Before we can do the change, we
177767614Sdarrenr		 * must make sure that oip is sufficient large to hold
177867614Sdarrenr		 * the TCP checksum (normally it does not!).
177967614Sdarrenr		 */
178095418Sdarrenr		if (oip->ip_p == IPPROTO_TCP && dlen >= 18) {
178167614Sdarrenr
178295418Sdarrenr			sum1 = ntohs(tcp->th_sum);
178395418Sdarrenr			fix_datacksum(&tcp->th_sum, sumd);
178495418Sdarrenr			sum2 = ntohs(tcp->th_sum);
178595418Sdarrenr
178695418Sdarrenr			/*
178795418Sdarrenr			 * Fix ICMP checksum to compensate the TCP
178895418Sdarrenr			 * checksum adjustment.
178995418Sdarrenr			 */
179095418Sdarrenr			CALC_SUMD(sum1, sum2, sumd);
179195418Sdarrenr			sumd2 = sumd;
179267614Sdarrenr		}
179367614Sdarrenr#endif
179453642Sguido	} else {
179567614Sdarrenr
179667614Sdarrenr		/*
179767614Sdarrenr		 * Fix IP checksum of the offending IP packet to adjust for
179867614Sdarrenr		 * the change in the IP address.
179967614Sdarrenr		 *
180067614Sdarrenr		 * Normally, you would expect that the ICMP checksum of the
180167614Sdarrenr		 * ICMP error message needs to be adjusted as well for the
180267614Sdarrenr		 * IP address change in oip.
180367614Sdarrenr		 * However, this is a NOP, because the ICMP checksum is
180467614Sdarrenr		 * calculated over the complete ICMP packet, which includes the
180567614Sdarrenr		 * changed oip IP addresses and oip->ip_sum. However, these
180667614Sdarrenr		 * two changes cancel each other out (if the delta for
180767614Sdarrenr		 * the IP address is x, then the delta for ip_sum is minus x),
180867614Sdarrenr		 * so no change in the icmp_cksum is necessary.
180967614Sdarrenr		 *
181067614Sdarrenr		 * Be careful that nat_dir refers to the direction of the
181167614Sdarrenr		 * offending IP packet (oip), not to its ICMP response (icmp)
181267614Sdarrenr		 */
181367614Sdarrenr		fix_datacksum(&oip->ip_sum, sumd);
181467614Sdarrenr
181567614Sdarrenr/* XXX FV : without having looked at Solaris source code, it seems unlikely
181667614Sdarrenr * that SOLARIS would compensate this in the kernel (a body of an IP packet
181767614Sdarrenr * in the data section of an ICMP packet). I have the feeling that this should
181867614Sdarrenr * be unconditional, but I'm not in a position to check.
181967614Sdarrenr */
182063523Sdarrenr#if !SOLARIS && !defined(__sgi)
182167614Sdarrenr		/*
182267614Sdarrenr		 * Fix UDP pseudo header checksum to compensate for the
182367614Sdarrenr		 * IP address change.
182467614Sdarrenr		 */
182567614Sdarrenr		if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) {
182667614Sdarrenr			/*
182767614Sdarrenr			 * The UDP checksum is optional, only adjust it
182867614Sdarrenr			 * if it has been set
182967614Sdarrenr			 */
183067614Sdarrenr			sum1 = ntohs(udp->uh_sum);
183167614Sdarrenr			fix_datacksum(&udp->uh_sum, sumd);
183267614Sdarrenr			sum2 = ntohs(udp->uh_sum);
183367614Sdarrenr
183467614Sdarrenr			/*
183567614Sdarrenr			 * Fix ICMP checksum to compensate the UDP
183667614Sdarrenr			 * checksum adjustment.
183767614Sdarrenr			 */
183867614Sdarrenr			CALC_SUMD(sum1, sum2, sumd);
183972006Sdarrenr			sumd2 = sumd;
184067614Sdarrenr		}
184167614Sdarrenr
184295418Sdarrenr#if 1
184367614Sdarrenr		/*
184467614Sdarrenr		 * Fix TCP pseudo header checksum to compensate for the
184567614Sdarrenr		 * IP address change. Before we can do the change, we
184667614Sdarrenr		 * must make sure that oip is sufficient large to hold
184767614Sdarrenr		 * the TCP checksum (normally it does not!).
184867614Sdarrenr		 */
184995418Sdarrenr		if (oip->ip_p == IPPROTO_TCP && dlen >= 18) {
185067614Sdarrenr
185195418Sdarrenr			sum1 = ntohs(tcp->th_sum);
185295418Sdarrenr			fix_datacksum(&tcp->th_sum, sumd);
185395418Sdarrenr			sum2 = ntohs(tcp->th_sum);
185495418Sdarrenr
185595418Sdarrenr			/*
185695418Sdarrenr			 * Fix ICMP checksum to compensate the TCP
185795418Sdarrenr			 * checksum adjustment.
185895418Sdarrenr			 */
185995418Sdarrenr			CALC_SUMD(sum1, sum2, sumd);
186095418Sdarrenr			sumd2 = sumd;
186167614Sdarrenr		};
186263523Sdarrenr#endif
186367614Sdarrenr
186467614Sdarrenr#endif
186553642Sguido	}
186653642Sguido
186753642Sguido	if ((flags & IPN_TCPUDP) != 0) {
186864580Sdarrenr		/*
186967614Sdarrenr		 * Step 2 :
187067614Sdarrenr		 * For offending TCP/UDP IP packets, translate the ports as
187167614Sdarrenr		 * well, based on the NAT specification. Of course such
187267614Sdarrenr		 * a change must be reflected in the ICMP checksum as well.
187367614Sdarrenr		 *
187467614Sdarrenr		 * Advance notice : Now it becomes complicated :-)
187567614Sdarrenr		 *
187667614Sdarrenr		 * Since the port fields are part of the TCP/UDP checksum
187767614Sdarrenr		 * of the offending IP packet, you need to adjust that checksum
187867614Sdarrenr		 * as well... but, if you change, you must change the icmp
187967614Sdarrenr		 * checksum *again*, to reflect that change.
188067614Sdarrenr		 *
188167614Sdarrenr		 * To further complicate: the TCP checksum is not in the first
188267614Sdarrenr		 * 8 bytes of the offending ip packet, so it most likely is not
188395418Sdarrenr		 * available. Some OSses like Solaris return enough bytes to
188495418Sdarrenr		 * include the TCP checksum. So we have to check if the
188595418Sdarrenr		 * ip->ip_len actually holds the TCP checksum of the oip!
188667614Sdarrenr		 */
188767614Sdarrenr
188872006Sdarrenr		if (nat->nat_oport == tcp->th_dport) {
188953642Sguido			if (tcp->th_sport != nat->nat_inport) {
189067614Sdarrenr				/*
189167614Sdarrenr				 * Fix ICMP checksum to compensate port
189267614Sdarrenr				 * adjustment.
189367614Sdarrenr				 */
189453642Sguido				sum1 = ntohs(tcp->th_sport);
189553642Sguido				sum2 = ntohs(nat->nat_inport);
189653642Sguido				CALC_SUMD(sum1, sum2, sumd);
189772006Sdarrenr				sumd2 += sumd;
189853642Sguido				tcp->th_sport = nat->nat_inport;
189967614Sdarrenr
190067614Sdarrenr				/*
190167614Sdarrenr				 * Fix udp checksum to compensate port
190267614Sdarrenr				 * adjustment.  NOTE : the offending IP packet
190367614Sdarrenr				 * flows the other direction compared to the
190467614Sdarrenr				 * ICMP message.
190567614Sdarrenr				 *
190667614Sdarrenr				 * The UDP checksum is optional, only adjust
190767614Sdarrenr				 * it if it has been set.
190867614Sdarrenr				 */
190967614Sdarrenr				if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) {
191067614Sdarrenr
191167614Sdarrenr					sum1 = ntohs(udp->uh_sum);
191267614Sdarrenr					fix_datacksum(&udp->uh_sum, sumd);
191367614Sdarrenr					sum2 = ntohs(udp->uh_sum);
191467614Sdarrenr
191567614Sdarrenr					/*
191667614Sdarrenr					 * Fix ICMP checksum to
191767614Sdarrenr					 * compensate UDP checksum
191867614Sdarrenr					 * adjustment.
191967614Sdarrenr					 */
192067614Sdarrenr					CALC_SUMD(sum1, sum2, sumd);
192172006Sdarrenr					sumd2 += sumd;
192267614Sdarrenr				}
192395418Sdarrenr
192495418Sdarrenr				/*
192595418Sdarrenr				 * Fix tcp checksum (if present) to compensate
192695418Sdarrenr				 * port adjustment. NOTE : the offending IP
192795418Sdarrenr				 * packet flows the other direction compared to
192895418Sdarrenr				 * the ICMP message.
192995418Sdarrenr				 */
193095418Sdarrenr				if (oip->ip_p == IPPROTO_TCP && dlen >= 18) {
193195418Sdarrenr
193295418Sdarrenr					sum1 = ntohs(tcp->th_sum);
193395418Sdarrenr					fix_datacksum(&tcp->th_sum, sumd);
193495418Sdarrenr					sum2 = ntohs(tcp->th_sum);
193595418Sdarrenr
193695418Sdarrenr					/*
193795418Sdarrenr					 * Fix ICMP checksum to
193895418Sdarrenr					 * compensate TCP checksum
193995418Sdarrenr					 * adjustment.
194095418Sdarrenr					 */
194195418Sdarrenr					CALC_SUMD(sum1, sum2, sumd);
194295418Sdarrenr					sumd2 += sumd;
194395418Sdarrenr				}
194453642Sguido			}
194553642Sguido		} else {
194653642Sguido			if (tcp->th_dport != nat->nat_outport) {
194767614Sdarrenr				/*
194867614Sdarrenr				 * Fix ICMP checksum to compensate port
194967614Sdarrenr				 * adjustment.
195067614Sdarrenr				 */
195153642Sguido				sum1 = ntohs(tcp->th_dport);
195253642Sguido				sum2 = ntohs(nat->nat_outport);
195353642Sguido				CALC_SUMD(sum1, sum2, sumd);
195472006Sdarrenr				sumd2 += sumd;
195553642Sguido				tcp->th_dport = nat->nat_outport;
195667614Sdarrenr
195767614Sdarrenr				/*
195867614Sdarrenr				 * Fix udp checksum to compensate port
195967614Sdarrenr				 * adjustment.   NOTE : the offending IP
196067614Sdarrenr				 * packet flows the other direction compared
196167614Sdarrenr				 * to the ICMP message.
196267614Sdarrenr				 *
196367614Sdarrenr				 * The UDP checksum is optional, only adjust
196467614Sdarrenr				 * it if it has been set.
196567614Sdarrenr				 */
196667614Sdarrenr				if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) {
196767614Sdarrenr
196867614Sdarrenr					sum1 = ntohs(udp->uh_sum);
196967614Sdarrenr					fix_datacksum(&udp->uh_sum, sumd);
197067614Sdarrenr					sum2 = ntohs(udp->uh_sum);
197167614Sdarrenr
197267614Sdarrenr					/*
197367614Sdarrenr					 * Fix ICMP checksum to compensate
197467614Sdarrenr					 * UDP checksum adjustment.
197567614Sdarrenr					 */
197667614Sdarrenr					CALC_SUMD(sum1, sum2, sumd);
197772006Sdarrenr					sumd2 += sumd;
197867614Sdarrenr				}
197995418Sdarrenr
198095418Sdarrenr				/*
198195418Sdarrenr				 * Fix tcp checksum (if present) to compensate
198295418Sdarrenr				 * port adjustment. NOTE : the offending IP
198395418Sdarrenr				 * packet flows the other direction compared to
198495418Sdarrenr				 * the ICMP message.
198595418Sdarrenr				 */
198695418Sdarrenr				if (oip->ip_p == IPPROTO_TCP && dlen >= 18) {
198795418Sdarrenr
198895418Sdarrenr					sum1 = ntohs(tcp->th_sum);
198995418Sdarrenr					fix_datacksum(&tcp->th_sum, sumd);
199095418Sdarrenr					sum2 = ntohs(tcp->th_sum);
199195418Sdarrenr
199295418Sdarrenr					/*
199395418Sdarrenr					 * Fix ICMP checksum to compensate
199495418Sdarrenr					 * UDP checksum adjustment.
199595418Sdarrenr					 */
199695418Sdarrenr					CALC_SUMD(sum1, sum2, sumd);
199795418Sdarrenr					sumd2 += sumd;
199895418Sdarrenr				}
199953642Sguido			}
200053642Sguido		}
200172006Sdarrenr		if (sumd2) {
200272006Sdarrenr			sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
200372006Sdarrenr			sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
200472006Sdarrenr			if (nat->nat_dir == NAT_OUTBOUND) {
200580482Sdarrenr				fix_outcksum(fin, &icmp->icmp_cksum, sumd2);
200672006Sdarrenr			} else {
200780482Sdarrenr				fix_incksum(fin, &icmp->icmp_cksum, sumd2);
200872006Sdarrenr			}
200972006Sdarrenr		}
201053642Sguido	}
201180482Sdarrenr	if (oip->ip_p == IPPROTO_ICMP)
201280482Sdarrenr		nat->nat_age = fr_defnaticmpage;
201353642Sguido	return nat;
201453642Sguido}
201553642Sguido
201653642Sguido
201753642Sguido/*
201853642Sguido * NB: these lookups don't lock access to the list, it assume it has already
201953642Sguido * been done!
202053642Sguido */
202153642Sguido/*
202253642Sguido * Lookup a nat entry based on the mapped destination ip address/port and
202353642Sguido * real source address/port.  We use this lookup when receiving a packet,
202453642Sguido * we're looking for a table entry, based on the destination address.
202553642Sguido * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.
202653642Sguido */
202792685Sdarrenrnat_t *nat_inlookup(fin, flags, p, src, mapdst, rw)
202892685Sdarrenrfr_info_t *fin;
202953642Sguidoregister u_int flags, p;
203053642Sguidostruct in_addr src , mapdst;
203172006Sdarrenrint rw;
203253642Sguido{
203367614Sdarrenr	register u_short sport, dport;
203453642Sguido	register nat_t *nat;
203553642Sguido	register int nflags;
203667614Sdarrenr	register u_32_t dst;
203792685Sdarrenr	ipnat_t *ipn;
203892685Sdarrenr	void *ifp;
203953642Sguido	u_int hv;
204053642Sguido
204192685Sdarrenr	if (fin != NULL)
204292685Sdarrenr		ifp = fin->fin_ifp;
204392685Sdarrenr	else
204492685Sdarrenr		ifp = NULL;
204567614Sdarrenr	dst = mapdst.s_addr;
204692685Sdarrenr	if (flags & IPN_TCPUDP) {
204792685Sdarrenr		sport = htons(fin->fin_data[0]);
204892685Sdarrenr		dport = htons(fin->fin_data[1]);
204992685Sdarrenr	} else {
205092685Sdarrenr		sport = 0;
205192685Sdarrenr		dport = 0;
205292685Sdarrenr	}
205353642Sguido
205480482Sdarrenr	hv = NAT_HASH_FN(dst, dport, 0xffffffff);
205580482Sdarrenr	hv = NAT_HASH_FN(src.s_addr, hv + sport, ipf_nattable_sz);
205653642Sguido	nat = nat_table[1][hv];
205753642Sguido	for (; nat; nat = nat->nat_hnext[1]) {
205853642Sguido		nflags = nat->nat_flags;
205953642Sguido		if ((!ifp || ifp == nat->nat_ifp) &&
206053642Sguido		    nat->nat_oip.s_addr == src.s_addr &&
206167614Sdarrenr		    nat->nat_outip.s_addr == dst &&
206292685Sdarrenr		    ((p == 0) || (p == nat->nat_p))) {
206392685Sdarrenr			switch (p)
206492685Sdarrenr			{
206592685Sdarrenr			case IPPROTO_TCP :
206692685Sdarrenr			case IPPROTO_UDP :
206792685Sdarrenr				if (nat->nat_oport != sport)
206892685Sdarrenr					continue;
206992685Sdarrenr				if (nat->nat_outport != dport)
207092685Sdarrenr					continue;
207192685Sdarrenr				break;
207292685Sdarrenr			default :
207392685Sdarrenr				break;
207492685Sdarrenr			}
207592685Sdarrenr
207692685Sdarrenr			ipn = nat->nat_ptr;
207792685Sdarrenr			if ((ipn != NULL) && (nat->nat_aps != NULL))
207892685Sdarrenr				if (appr_match(fin, nat) != 0)
207992685Sdarrenr					continue;
208053642Sguido			return nat;
208192685Sdarrenr		}
208253642Sguido	}
208392685Sdarrenr	if (!nat_stats.ns_wilds || !(flags & FI_WILDP))
208467614Sdarrenr		return NULL;
208572006Sdarrenr	if (!rw) {
208672006Sdarrenr		RWLOCK_EXIT(&ipf_nat);
208772006Sdarrenr	}
208880482Sdarrenr	hv = NAT_HASH_FN(dst, 0, 0xffffffff);
208992685Sdarrenr	hv = NAT_HASH_FN(src.s_addr, dst, ipf_nattable_sz);
209072006Sdarrenr	if (!rw) {
209172006Sdarrenr		WRITE_ENTER(&ipf_nat);
209272006Sdarrenr	}
209367614Sdarrenr	nat = nat_table[1][hv];
209467614Sdarrenr	for (; nat; nat = nat->nat_hnext[1]) {
209567614Sdarrenr		nflags = nat->nat_flags;
209667614Sdarrenr		if (ifp && ifp != nat->nat_ifp)
209767614Sdarrenr			continue;
209867614Sdarrenr		if (!(nflags & FI_WILDP))
209967614Sdarrenr			continue;
210067614Sdarrenr		if (nat->nat_oip.s_addr != src.s_addr ||
210167614Sdarrenr		    nat->nat_outip.s_addr != dst)
210267614Sdarrenr			continue;
210367614Sdarrenr		if (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) &&
210467614Sdarrenr		    ((nat->nat_outport == dport) || (nflags & FI_W_SPORT))) {
210592685Sdarrenr			nat_tabmove(fin, nat);
210667614Sdarrenr			break;
210767614Sdarrenr		}
210867614Sdarrenr	}
210972006Sdarrenr	if (!rw) {
211072006Sdarrenr		MUTEX_DOWNGRADE(&ipf_nat);
211172006Sdarrenr	}
211267614Sdarrenr	return nat;
211353642Sguido}
211453642Sguido
211553642Sguido
211672006Sdarrenr/*
211772006Sdarrenr * This function is only called for TCP/UDP NAT table entries where the
211872006Sdarrenr * original was placed in the table without hashing on the ports and we now
211972006Sdarrenr * want to include hashing on port numbers.
212072006Sdarrenr */
212192685Sdarrenrstatic void nat_tabmove(fin, nat)
212292685Sdarrenrfr_info_t *fin;
212367614Sdarrenrnat_t *nat;
212467614Sdarrenr{
212572006Sdarrenr	register u_short sport, dport;
212692685Sdarrenr	u_int hv, nflags;
212767614Sdarrenr	nat_t **natp;
212867614Sdarrenr
212992685Sdarrenr	nflags = nat->nat_flags;
213072006Sdarrenr
213192685Sdarrenr	sport = ntohs(fin->fin_data[0]);
213292685Sdarrenr	dport = ntohs(fin->fin_data[1]);
213372006Sdarrenr
213467614Sdarrenr	/*
213567614Sdarrenr	 * Remove the NAT entry from the old location
213667614Sdarrenr	 */
213767614Sdarrenr	if (nat->nat_hnext[0])
213867614Sdarrenr		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
213967614Sdarrenr	*nat->nat_phnext[0] = nat->nat_hnext[0];
214067614Sdarrenr
214167614Sdarrenr	if (nat->nat_hnext[1])
214267853Sdarrenr		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
214367614Sdarrenr	*nat->nat_phnext[1] = nat->nat_hnext[1];
214467614Sdarrenr
214567853Sdarrenr	/*
214667853Sdarrenr	 * Add into the NAT table in the new position
214767853Sdarrenr	 */
214880482Sdarrenr	hv = NAT_HASH_FN(nat->nat_inip.s_addr, sport, 0xffffffff);
214980482Sdarrenr	hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + dport, ipf_nattable_sz);
215067614Sdarrenr	natp = &nat_table[0][hv];
215167614Sdarrenr	if (*natp)
215267614Sdarrenr		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
215367614Sdarrenr	nat->nat_phnext[0] = natp;
215467614Sdarrenr	nat->nat_hnext[0] = *natp;
215567614Sdarrenr	*natp = nat;
215667614Sdarrenr
215780482Sdarrenr	hv = NAT_HASH_FN(nat->nat_outip.s_addr, sport, 0xffffffff);
215880482Sdarrenr	hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + dport, ipf_nattable_sz);
215967614Sdarrenr	natp = &nat_table[1][hv];
216067614Sdarrenr	if (*natp)
216167614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
216267614Sdarrenr	nat->nat_phnext[1] = natp;
216367614Sdarrenr	nat->nat_hnext[1] = *natp;
216467614Sdarrenr	*natp = nat;
216567614Sdarrenr}
216667614Sdarrenr
216767614Sdarrenr
216853642Sguido/*
216953642Sguido * Lookup a nat entry based on the source 'real' ip address/port and
217053642Sguido * destination address/port.  We use this lookup when sending a packet out,
217153642Sguido * we're looking for a table entry, based on the source address.
217253642Sguido * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.
217353642Sguido */
217492685Sdarrenrnat_t *nat_outlookup(fin, flags, p, src, dst, rw)
217592685Sdarrenrfr_info_t *fin;
217653642Sguidoregister u_int flags, p;
217753642Sguidostruct in_addr src , dst;
217872006Sdarrenrint rw;
217953642Sguido{
218053642Sguido	register u_short sport, dport;
218153642Sguido	register nat_t *nat;
218253642Sguido	register int nflags;
218392685Sdarrenr	ipnat_t *ipn;
218467614Sdarrenr	u_32_t srcip;
218592685Sdarrenr	void *ifp;
218653642Sguido	u_int hv;
218753642Sguido
218892685Sdarrenr	ifp = fin->fin_ifp;
218967614Sdarrenr	srcip = src.s_addr;
219092685Sdarrenr	if (flags & IPN_TCPUDP) {
219192685Sdarrenr		sport = ntohs(fin->fin_data[0]);
219292685Sdarrenr		dport = ntohs(fin->fin_data[1]);
219392685Sdarrenr	} else {
219492685Sdarrenr		sport = 0;
219592685Sdarrenr		dport = 0;
219692685Sdarrenr	}
219753642Sguido
219880482Sdarrenr	hv = NAT_HASH_FN(srcip, sport, 0xffffffff);
219980482Sdarrenr	hv = NAT_HASH_FN(dst.s_addr, hv + dport, ipf_nattable_sz);
220053642Sguido	nat = nat_table[0][hv];
220153642Sguido	for (; nat; nat = nat->nat_hnext[0]) {
220253642Sguido		nflags = nat->nat_flags;
220353642Sguido
220453642Sguido		if ((!ifp || ifp == nat->nat_ifp) &&
220567614Sdarrenr		    nat->nat_inip.s_addr == srcip &&
220653642Sguido		    nat->nat_oip.s_addr == dst.s_addr &&
220792685Sdarrenr		    ((p == 0) || (p == nat->nat_p))) {
220892685Sdarrenr			switch (p)
220992685Sdarrenr			{
221092685Sdarrenr			case IPPROTO_TCP :
221192685Sdarrenr			case IPPROTO_UDP :
221292685Sdarrenr				if (nat->nat_oport != dport)
221392685Sdarrenr					continue;
221492685Sdarrenr				if (nat->nat_inport != sport)
221592685Sdarrenr					continue;
221692685Sdarrenr				break;
221792685Sdarrenr			default :
221892685Sdarrenr				break;
221992685Sdarrenr			}
222092685Sdarrenr
222192685Sdarrenr			ipn = nat->nat_ptr;
222292685Sdarrenr			if ((ipn != NULL) && (nat->nat_aps != NULL))
222392685Sdarrenr				if (appr_match(fin, nat) != 0)
222492685Sdarrenr					continue;
222553642Sguido			return nat;
222692685Sdarrenr		}
222753642Sguido	}
222892685Sdarrenr	if (!nat_stats.ns_wilds || !(flags & FI_WILDP))
222967614Sdarrenr		return NULL;
223072006Sdarrenr	if (!rw) {
223172006Sdarrenr		RWLOCK_EXIT(&ipf_nat);
223272006Sdarrenr	}
223392685Sdarrenr
223492685Sdarrenr	hv = NAT_HASH_FN(dst.s_addr, srcip, ipf_nattable_sz);
223572006Sdarrenr	if (!rw) {
223672006Sdarrenr		WRITE_ENTER(&ipf_nat);
223772006Sdarrenr	}
223867614Sdarrenr	nat = nat_table[0][hv];
223967614Sdarrenr	for (; nat; nat = nat->nat_hnext[0]) {
224067614Sdarrenr		nflags = nat->nat_flags;
224167614Sdarrenr		if (ifp && ifp != nat->nat_ifp)
224267614Sdarrenr			continue;
224367614Sdarrenr		if (!(nflags & FI_WILDP))
224467614Sdarrenr			continue;
224567614Sdarrenr		if ((nat->nat_inip.s_addr != srcip) ||
224667614Sdarrenr		    (nat->nat_oip.s_addr != dst.s_addr))
224767614Sdarrenr			continue;
224872006Sdarrenr		if (((nat->nat_inport == sport) || (nflags & FI_W_SPORT)) &&
224972006Sdarrenr		    ((nat->nat_oport == dport) || (nflags & FI_W_DPORT))) {
225092685Sdarrenr			nat_tabmove(fin, nat);
225167614Sdarrenr			break;
225267614Sdarrenr		}
225367614Sdarrenr	}
225472006Sdarrenr	if (!rw) {
225572006Sdarrenr		MUTEX_DOWNGRADE(&ipf_nat);
225672006Sdarrenr	}
225767614Sdarrenr	return nat;
225853642Sguido}
225953642Sguido
226053642Sguido
226153642Sguido/*
226253642Sguido * Lookup the NAT tables to search for a matching redirect
226353642Sguido */
226453642Sguidonat_t *nat_lookupredir(np)
226553642Sguidoregister natlookup_t *np;
226653642Sguido{
226753642Sguido	nat_t *nat;
226892685Sdarrenr	fr_info_t fi;
226953642Sguido
227092685Sdarrenr	bzero((char *)&fi, sizeof(fi));
227192685Sdarrenr	fi.fin_data[0] = np->nl_inport;
227292685Sdarrenr	fi.fin_data[1] = np->nl_outport;
227392685Sdarrenr
227453642Sguido	/*
227553642Sguido	 * If nl_inip is non null, this is a lookup based on the real
227653642Sguido	 * ip address. Else, we use the fake.
227753642Sguido	 */
227892685Sdarrenr	if ((nat = nat_outlookup(&fi, np->nl_flags, 0, np->nl_inip,
227992685Sdarrenr				 np->nl_outip, 0))) {
228053642Sguido		np->nl_realip = nat->nat_outip;
228153642Sguido		np->nl_realport = nat->nat_outport;
228253642Sguido	}
228353642Sguido	return nat;
228453642Sguido}
228553642Sguido
228653642Sguido
228760852Sdarrenrstatic int nat_match(fin, np, ip)
228860852Sdarrenrfr_info_t *fin;
228960852Sdarrenripnat_t *np;
229060852Sdarrenrip_t *ip;
229160852Sdarrenr{
229260852Sdarrenr	frtuc_t *ft;
229360852Sdarrenr
229460852Sdarrenr	if (ip->ip_v != 4)
229560852Sdarrenr		return 0;
229660852Sdarrenr
229792685Sdarrenr	if (np->in_p && fin->fin_p != np->in_p)
229860852Sdarrenr		return 0;
229960852Sdarrenr	if (fin->fin_out) {
230063523Sdarrenr		if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK)))
230160852Sdarrenr			return 0;
230263523Sdarrenr		if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip)
230363523Sdarrenr		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
230460852Sdarrenr			return 0;
230563523Sdarrenr		if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip)
230663523Sdarrenr		    ^ ((np->in_flags & IPN_NOTDST) != 0))
230760852Sdarrenr			return 0;
230860852Sdarrenr	} else {
230963523Sdarrenr		if (!(np->in_redir & NAT_REDIRECT))
231060852Sdarrenr			return 0;
231163523Sdarrenr		if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip)
231263523Sdarrenr		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
231363523Sdarrenr			return 0;
231463523Sdarrenr		if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip)
231563523Sdarrenr		    ^ ((np->in_flags & IPN_NOTDST) != 0))
231663523Sdarrenr			return 0;
231760852Sdarrenr	}
231860852Sdarrenr
231960852Sdarrenr	ft = &np->in_tuc;
232080482Sdarrenr	if (!(fin->fin_fl & FI_TCPUDP) ||
232180482Sdarrenr	    (fin->fin_fl & FI_SHORT) || (fin->fin_off != 0)) {
232260852Sdarrenr		if (ft->ftu_scmp || ft->ftu_dcmp)
232360852Sdarrenr			return 0;
232460852Sdarrenr		return 1;
232560852Sdarrenr	}
232660852Sdarrenr
232760852Sdarrenr	return fr_tcpudpchk(ft, fin);
232860852Sdarrenr}
232960852Sdarrenr
233060852Sdarrenr
233153642Sguido/*
233253642Sguido * Packets going out on the external interface go through this.
233353642Sguido * Here, the source address requires alteration, if anything.
233453642Sguido */
233553642Sguidoint ip_natout(ip, fin)
233653642Sguidoip_t *ip;
233753642Sguidofr_info_t *fin;
233853642Sguido{
233953642Sguido	register ipnat_t *np = NULL;
234053642Sguido	register u_32_t ipa;
234153642Sguido	tcphdr_t *tcp = NULL;
234260852Sdarrenr	u_short sport = 0, dport = 0, *csump = NULL;
234380482Sdarrenr	int natadd = 1, i, icmpset = 1;
234480482Sdarrenr	u_int nflags = 0, hv, msk;
234553642Sguido	struct ifnet *ifp;
234653642Sguido	frentry_t *fr;
234792685Sdarrenr	void *sifp;
234853642Sguido	u_32_t iph;
234953642Sguido	nat_t *nat;
235053642Sguido
235160852Sdarrenr	if (nat_list == NULL || (fr_nat_lock))
235253642Sguido		return 0;
235353642Sguido
235453642Sguido	if ((fr = fin->fin_fr) && !(fr->fr_flags & FR_DUP) &&
235592685Sdarrenr	    fr->fr_tif.fd_ifp && fr->fr_tif.fd_ifp != (void *)-1) {
235692685Sdarrenr		sifp = fin->fin_ifp;
235792685Sdarrenr		fin->fin_ifp = fr->fr_tif.fd_ifp;
235892685Sdarrenr	} else
235992685Sdarrenr		sifp = fin->fin_ifp;
236092685Sdarrenr	ifp = fin->fin_ifp;
236153642Sguido
236280482Sdarrenr	if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) {
236392685Sdarrenr		if (fin->fin_p == IPPROTO_TCP)
236453642Sguido			nflags = IPN_TCP;
236592685Sdarrenr		else if (fin->fin_p == IPPROTO_UDP)
236653642Sguido			nflags = IPN_UDP;
236753642Sguido		if ((nflags & IPN_TCPUDP)) {
236853642Sguido			tcp = (tcphdr_t *)fin->fin_dp;
236953642Sguido			sport = tcp->th_sport;
237053642Sguido			dport = tcp->th_dport;
237153642Sguido		}
237253642Sguido	}
237353642Sguido
237492685Sdarrenr	ipa = fin->fin_saddr;
237553642Sguido
237653642Sguido	READ_ENTER(&ipf_nat);
237760852Sdarrenr
237892685Sdarrenr	if ((fin->fin_p == IPPROTO_ICMP) &&
237960852Sdarrenr	    (nat = nat_icmp(ip, fin, &nflags, NAT_OUTBOUND)))
238080482Sdarrenr		icmpset = 1;
238180482Sdarrenr	else if ((fin->fin_fl & FI_FRAG) &&
238272006Sdarrenr	    (nat = ipfr_nat_knownfrag(ip, fin)))
238353642Sguido		natadd = 0;
238492685Sdarrenr	else if ((nat = nat_outlookup(fin, nflags|FI_WILDP|FI_WILDA,
238592685Sdarrenr				      (u_int)fin->fin_p, fin->fin_src,
238692685Sdarrenr				      fin->fin_dst, 0))) {
238753642Sguido		nflags = nat->nat_flags;
238853642Sguido		if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) {
238953642Sguido			if ((nflags & FI_W_SPORT) &&
239053642Sguido			    (nat->nat_inport != sport))
239153642Sguido				nat->nat_inport = sport;
239292685Sdarrenr			if ((nflags & FI_W_DPORT) &&
239392685Sdarrenr			    (nat->nat_oport != dport))
239453642Sguido				nat->nat_oport = dport;
239592685Sdarrenr
239653642Sguido			if (nat->nat_outport == 0)
239753642Sguido				nat->nat_outport = sport;
239853642Sguido			nat->nat_flags &= ~(FI_W_DPORT|FI_W_SPORT);
239953642Sguido			nflags = nat->nat_flags;
240067853Sdarrenr			nat_stats.ns_wilds--;
240153642Sguido		}
240253642Sguido	} else {
240353642Sguido		RWLOCK_EXIT(&ipf_nat);
240492685Sdarrenr
240592685Sdarrenr		msk = 0xffffffff;
240692685Sdarrenr		i = 32;
240792685Sdarrenr
240853642Sguido		WRITE_ENTER(&ipf_nat);
240953642Sguido		/*
241053642Sguido		 * If there is no current entry in the nat table for this IP#,
241153642Sguido		 * create one for it (if there is a matching rule).
241253642Sguido		 */
241353642Sguidomaskloop:
241453642Sguido		iph = ipa & htonl(msk);
241560852Sdarrenr		hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz);
241653642Sguido		for (np = nat_rules[hv]; np; np = np->in_mnext)
241753642Sguido		{
241880482Sdarrenr			if (np->in_ifp && (np->in_ifp != ifp))
241960852Sdarrenr				continue;
242060852Sdarrenr			if ((np->in_flags & IPN_RF) &&
242160852Sdarrenr			    !(np->in_flags & nflags))
242260852Sdarrenr				continue;
242360852Sdarrenr			if (np->in_flags & IPN_FILTER) {
242460852Sdarrenr				if (!nat_match(fin, np, ip))
242560852Sdarrenr					continue;
242660852Sdarrenr			} else if ((ipa & np->in_inmsk) != np->in_inip)
242760852Sdarrenr				continue;
242892685Sdarrenr			if (*np->in_plabel && !appr_ok(ip, tcp, np))
242992685Sdarrenr				continue;
243092685Sdarrenr			nat = nat_new(fin, ip, np, NULL,
243192685Sdarrenr				      (u_int)nflags, NAT_OUTBOUND);
243292685Sdarrenr			if (nat != NULL) {
243392685Sdarrenr				np->in_hits++;
243492685Sdarrenr				break;
243553642Sguido			}
243653642Sguido		}
243753642Sguido		if ((np == NULL) && (i > 0)) {
243853642Sguido			do {
243953642Sguido				i--;
244053642Sguido				msk <<= 1;
244153642Sguido			} while ((i >= 0) && ((nat_masks & (1 << i)) == 0));
244253642Sguido			if (i >= 0)
244353642Sguido				goto maskloop;
244453642Sguido		}
244553642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
244653642Sguido	}
244753642Sguido
244872006Sdarrenr	/*
244972006Sdarrenr	 * NOTE: ipf_nat must now only be held as a read lock
245072006Sdarrenr	 */
245153642Sguido	if (nat) {
245253642Sguido		np = nat->nat_ptr;
245380482Sdarrenr		if (natadd && (fin->fin_fl & FI_FRAG) && np)
245453642Sguido			ipfr_nat_newfrag(ip, fin, 0, nat);
245560852Sdarrenr		MUTEX_ENTER(&nat->nat_lock);
245692685Sdarrenr		if (fin->fin_p != IPPROTO_TCP) {
245792685Sdarrenr			if (np && np->in_age[1])
245892685Sdarrenr				nat->nat_age = np->in_age[1];
245992685Sdarrenr			else if (!icmpset && (fin->fin_p == IPPROTO_ICMP))
246092685Sdarrenr				nat->nat_age = fr_defnaticmpage;
246192685Sdarrenr			else
246292685Sdarrenr				nat->nat_age = fr_defnatage;
246392685Sdarrenr		}
246453642Sguido		nat->nat_bytes += ip->ip_len;
246553642Sguido		nat->nat_pkts++;
246660852Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
246753642Sguido
246853642Sguido		/*
246953642Sguido		 * Fix up checksums, not by recalculating them, but
247053642Sguido		 * simply computing adjustments.
247153642Sguido		 */
247263523Sdarrenr		if (nflags == IPN_ICMPERR) {
247363523Sdarrenr			u_32_t s1, s2, sumd;
247463523Sdarrenr
247592685Sdarrenr			s1 = LONG_SUM(ntohl(fin->fin_saddr));
247663523Sdarrenr			s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr));
247763523Sdarrenr			CALC_SUMD(s1, s2, sumd);
247863523Sdarrenr
247963523Sdarrenr			if (nat->nat_dir == NAT_OUTBOUND)
248092685Sdarrenr				fix_outcksum(fin, &ip->ip_sum, sumd);
248192685Sdarrenr			else
248280482Sdarrenr				fix_incksum(fin, &ip->ip_sum, sumd);
248363523Sdarrenr		}
248492685Sdarrenr#if (SOLARIS || defined(__sgi)) && defined(_KERNEL)
248563523Sdarrenr		else {
248663523Sdarrenr			if (nat->nat_dir == NAT_OUTBOUND)
248780482Sdarrenr				fix_outcksum(fin, &ip->ip_sum, nat->nat_ipsumd);
248863523Sdarrenr			else
248980482Sdarrenr				fix_incksum(fin, &ip->ip_sum, nat->nat_ipsumd);
249063523Sdarrenr		}
249153642Sguido#endif
249292685Sdarrenr		/*
249392685Sdarrenr		 * Only change the packet contents, not what is filtered upon.
249492685Sdarrenr		 */
249563523Sdarrenr		ip->ip_src = nat->nat_outip;
249653642Sguido
249780482Sdarrenr		if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) {
249853642Sguido
249992685Sdarrenr			if ((nat->nat_outport != 0) && (tcp != NULL)) {
250053642Sguido				tcp->th_sport = nat->nat_outport;
250153642Sguido				fin->fin_data[0] = ntohs(tcp->th_sport);
250253642Sguido			}
250353642Sguido
250492685Sdarrenr			if (fin->fin_p == IPPROTO_TCP) {
250553642Sguido				csump = &tcp->th_sum;
250660852Sdarrenr				MUTEX_ENTER(&nat->nat_lock);
250753642Sguido				fr_tcp_age(&nat->nat_age,
250895418Sdarrenr					   nat->nat_tcpstate, fin, 1, 0);
250953642Sguido				if (nat->nat_age < fr_defnaticmpage)
251053642Sguido					nat->nat_age = fr_defnaticmpage;
251153642Sguido#ifdef LARGE_NAT
251260852Sdarrenr				else if (nat->nat_age > fr_defnatage)
251360852Sdarrenr					nat->nat_age = fr_defnatage;
251453642Sguido#endif
251553642Sguido				/*
251653642Sguido				 * Increase this because we may have
251753642Sguido				 * "keep state" following this too and
251853642Sguido				 * packet storms can occur if this is
251953642Sguido				 * removed too quickly.
252053642Sguido				 */
252153642Sguido				if (nat->nat_age == fr_tcpclosed)
252253642Sguido					nat->nat_age = fr_tcplastack;
252360852Sdarrenr				MUTEX_EXIT(&nat->nat_lock);
252492685Sdarrenr			} else if (fin->fin_p == IPPROTO_UDP) {
252553642Sguido				udphdr_t *udp = (udphdr_t *)tcp;
252653642Sguido
252753642Sguido				if (udp->uh_sum)
252853642Sguido					csump = &udp->uh_sum;
252953642Sguido			}
253063523Sdarrenr
253153642Sguido			if (csump) {
253253642Sguido				if (nat->nat_dir == NAT_OUTBOUND)
253392685Sdarrenr					fix_outcksum(fin, csump,
253492685Sdarrenr						     nat->nat_sumd[1]);
253553642Sguido				else
253692685Sdarrenr					fix_incksum(fin, csump,
253792685Sdarrenr						    nat->nat_sumd[1]);
253853642Sguido			}
253953642Sguido		}
254060852Sdarrenr
254192685Sdarrenr		if (np && (np->in_apr != NULL) && (np->in_dport == 0 ||
254260852Sdarrenr		     (tcp != NULL && dport == np->in_dport))) {
254360852Sdarrenr			i = appr_check(ip, fin, nat);
254460852Sdarrenr			if (i == 0)
254560852Sdarrenr				i = 1;
254660852Sdarrenr		} else
254760852Sdarrenr			i = 1;
254860852Sdarrenr		ATOMIC_INCL(nat_stats.ns_mapped[1]);
254953642Sguido		RWLOCK_EXIT(&ipf_nat);	/* READ */
255092685Sdarrenr		fin->fin_ifp = sifp;
255160852Sdarrenr		return i;
255253642Sguido	}
255353642Sguido	RWLOCK_EXIT(&ipf_nat);			/* READ/WRITE */
255492685Sdarrenr	fin->fin_ifp = sifp;
255553642Sguido	return 0;
255653642Sguido}
255753642Sguido
255853642Sguido
255953642Sguido/*
256053642Sguido * Packets coming in from the external interface go through this.
256153642Sguido * Here, the destination address requires alteration, if anything.
256253642Sguido */
256353642Sguidoint ip_natin(ip, fin)
256453642Sguidoip_t *ip;
256553642Sguidofr_info_t *fin;
256653642Sguido{
256753642Sguido	register struct in_addr src;
256853642Sguido	register struct in_addr in;
256953642Sguido	register ipnat_t *np;
257080482Sdarrenr	u_short sport = 0, dport = 0, *csump = NULL;
257153642Sguido	u_int nflags = 0, natadd = 1, hv, msk;
257253642Sguido	struct ifnet *ifp = fin->fin_ifp;
257353642Sguido	tcphdr_t *tcp = NULL;
257480482Sdarrenr	int i, icmpset = 0;
257553642Sguido	nat_t *nat;
257653642Sguido	u_32_t iph;
257753642Sguido
257860852Sdarrenr	if ((nat_list == NULL) || (ip->ip_v != 4) || (fr_nat_lock))
257953642Sguido		return 0;
258053642Sguido
258180482Sdarrenr	if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) {
258292685Sdarrenr		if (fin->fin_p == IPPROTO_TCP)
258353642Sguido			nflags = IPN_TCP;
258492685Sdarrenr		else if (fin->fin_p == IPPROTO_UDP)
258553642Sguido			nflags = IPN_UDP;
258653642Sguido		if ((nflags & IPN_TCPUDP)) {
258753642Sguido			tcp = (tcphdr_t *)fin->fin_dp;
258892685Sdarrenr			sport = tcp->th_sport;
258953642Sguido			dport = tcp->th_dport;
259053642Sguido		}
259153642Sguido	}
259253642Sguido
259392685Sdarrenr	in = fin->fin_dst;
259453642Sguido	/* make sure the source address is to be redirected */
259592685Sdarrenr	src = fin->fin_src;
259653642Sguido
259753642Sguido	READ_ENTER(&ipf_nat);
259853642Sguido
259992685Sdarrenr	if ((fin->fin_p == IPPROTO_ICMP) &&
260060852Sdarrenr	    (nat = nat_icmp(ip, fin, &nflags, NAT_INBOUND)))
260180482Sdarrenr		icmpset = 1;
260280482Sdarrenr	else if ((fin->fin_fl & FI_FRAG) &&
260353642Sguido		 (nat = ipfr_nat_knownfrag(ip, fin)))
260453642Sguido		natadd = 0;
260592685Sdarrenr	else if ((nat = nat_inlookup(fin, nflags|FI_WILDP|FI_WILDA,
260692685Sdarrenr				     (u_int)fin->fin_p, fin->fin_src, in, 0))) {
260753642Sguido		nflags = nat->nat_flags;
260853642Sguido		if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) {
260953642Sguido			if ((nat->nat_oport != sport) && (nflags & FI_W_DPORT))
261053642Sguido				nat->nat_oport = sport;
261192685Sdarrenr			if ((nat->nat_outport != dport) &&
261253642Sguido				 (nflags & FI_W_SPORT))
261353642Sguido				nat->nat_outport = dport;
261453642Sguido			nat->nat_flags &= ~(FI_W_SPORT|FI_W_DPORT);
261553642Sguido			nflags = nat->nat_flags;
261667853Sdarrenr			nat_stats.ns_wilds--;
261753642Sguido		}
261853642Sguido	} else {
261953642Sguido		RWLOCK_EXIT(&ipf_nat);
262092685Sdarrenr
262192685Sdarrenr		msk = 0xffffffff;
262292685Sdarrenr		i = 32;
262392685Sdarrenr
262453642Sguido		WRITE_ENTER(&ipf_nat);
262553642Sguido		/*
262653642Sguido		 * If there is no current entry in the nat table for this IP#,
262753642Sguido		 * create one for it (if there is a matching rule).
262853642Sguido		 */
262953642Sguidomaskloop:
263053642Sguido		iph = in.s_addr & htonl(msk);
263160852Sdarrenr		hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz);
263260852Sdarrenr		for (np = rdr_rules[hv]; np; np = np->in_rnext) {
263360852Sdarrenr			if ((np->in_ifp && (np->in_ifp != ifp)) ||
263492685Sdarrenr			    (np->in_p && (np->in_p != fin->fin_p)) ||
263560852Sdarrenr			    (np->in_flags && !(nflags & np->in_flags)))
263660852Sdarrenr				continue;
263760852Sdarrenr			if (np->in_flags & IPN_FILTER) {
263860852Sdarrenr				if (!nat_match(fin, np, ip))
263960852Sdarrenr					continue;
264060852Sdarrenr			} else if ((in.s_addr & np->in_outmsk) != np->in_outip)
264160852Sdarrenr				continue;
264292685Sdarrenr			if ((!np->in_pmin || (np->in_flags & IPN_FILTER) ||
264360852Sdarrenr			     ((ntohs(np->in_pmax) >= ntohs(dport)) &&
264460852Sdarrenr			      (ntohs(dport) >= ntohs(np->in_pmin)))))
264592685Sdarrenr				if ((nat = nat_new(fin, ip, np, NULL, nflags,
264653642Sguido						    NAT_INBOUND))) {
264753642Sguido					np->in_hits++;
264853642Sguido					break;
264953642Sguido				}
265060852Sdarrenr		}
265160852Sdarrenr
265253642Sguido		if ((np == NULL) && (i > 0)) {
265353642Sguido			do {
265453642Sguido				i--;
265553642Sguido				msk <<= 1;
265653642Sguido			} while ((i >= 0) && ((rdr_masks & (1 << i)) == 0));
265753642Sguido			if (i >= 0)
265853642Sguido				goto maskloop;
265953642Sguido		}
266053642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
266153642Sguido	}
266272006Sdarrenr
266372006Sdarrenr	/*
266472006Sdarrenr	 * NOTE: ipf_nat must now only be held as a read lock
266572006Sdarrenr	 */
266653642Sguido	if (nat) {
266753642Sguido		np = nat->nat_ptr;
266853642Sguido		fin->fin_fr = nat->nat_fr;
266980482Sdarrenr		if (natadd && (fin->fin_fl & FI_FRAG) && np)
267053642Sguido			ipfr_nat_newfrag(ip, fin, 0, nat);
267192685Sdarrenr		if (np && (np->in_apr != NULL) && (np->in_dport == 0 ||
267292685Sdarrenr		     (tcp != NULL && sport == np->in_dport))) {
267360852Sdarrenr			i = appr_check(ip, fin, nat);
267460852Sdarrenr			if (i == -1) {
267560852Sdarrenr				RWLOCK_EXIT(&ipf_nat);
267660852Sdarrenr				return i;
267760852Sdarrenr			}
267860852Sdarrenr		}
267953642Sguido
268060852Sdarrenr		MUTEX_ENTER(&nat->nat_lock);
268192685Sdarrenr		if (fin->fin_p != IPPROTO_TCP) {
268292685Sdarrenr			if (np && np->in_age[0])
268392685Sdarrenr				nat->nat_age = np->in_age[0];
268492685Sdarrenr			else if (!icmpset && (fin->fin_p == IPPROTO_ICMP))
268592685Sdarrenr				nat->nat_age = fr_defnaticmpage;
268692685Sdarrenr			else
268792685Sdarrenr				nat->nat_age = fr_defnatage;
268892685Sdarrenr		}
268953642Sguido		nat->nat_bytes += ip->ip_len;
269053642Sguido		nat->nat_pkts++;
269160852Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
269253642Sguido		ip->ip_dst = nat->nat_inip;
269360852Sdarrenr		fin->fin_fi.fi_daddr = nat->nat_inip.s_addr;
269453642Sguido
269553642Sguido		/*
269653642Sguido		 * Fix up checksums, not by recalculating them, but
269753642Sguido		 * simply computing adjustments.
269853642Sguido		 */
269992685Sdarrenr#if (SOLARIS || defined(__sgi)) && defined(_KERNEL)
270053642Sguido		if (nat->nat_dir == NAT_OUTBOUND)
270180482Sdarrenr			fix_incksum(fin, &ip->ip_sum, nat->nat_ipsumd);
270253642Sguido		else
270380482Sdarrenr			fix_outcksum(fin, &ip->ip_sum, nat->nat_ipsumd);
270453642Sguido#endif
270580482Sdarrenr		if ((fin->fin_off == 0) && !(fin->fin_fl & FI_SHORT)) {
270653642Sguido
270792685Sdarrenr			if ((nat->nat_inport != 0) && (tcp != NULL)) {
270853642Sguido				tcp->th_dport = nat->nat_inport;
270953642Sguido				fin->fin_data[1] = ntohs(tcp->th_dport);
271053642Sguido			}
271153642Sguido
271292685Sdarrenr			if (fin->fin_p == IPPROTO_TCP) {
271353642Sguido				csump = &tcp->th_sum;
271460852Sdarrenr				MUTEX_ENTER(&nat->nat_lock);
271553642Sguido				fr_tcp_age(&nat->nat_age,
271695418Sdarrenr					   nat->nat_tcpstate, fin, 0, 0);
271753642Sguido				if (nat->nat_age < fr_defnaticmpage)
271853642Sguido					nat->nat_age = fr_defnaticmpage;
271953642Sguido#ifdef LARGE_NAT
272060852Sdarrenr				else if (nat->nat_age > fr_defnatage)
272160852Sdarrenr					nat->nat_age = fr_defnatage;
272253642Sguido#endif
272353642Sguido				/*
272453642Sguido				 * Increase this because we may have
272553642Sguido				 * "keep state" following this too and
272653642Sguido				 * packet storms can occur if this is
272753642Sguido				 * removed too quickly.
272853642Sguido				 */
272953642Sguido				if (nat->nat_age == fr_tcpclosed)
273053642Sguido					nat->nat_age = fr_tcplastack;
273160852Sdarrenr				MUTEX_EXIT(&nat->nat_lock);
273292685Sdarrenr			} else if (fin->fin_p == IPPROTO_UDP) {
273353642Sguido				udphdr_t *udp = (udphdr_t *)tcp;
273453642Sguido
273553642Sguido				if (udp->uh_sum)
273653642Sguido					csump = &udp->uh_sum;
273753642Sguido			}
273860852Sdarrenr
273953642Sguido			if (csump) {
274053642Sguido				if (nat->nat_dir == NAT_OUTBOUND)
274192685Sdarrenr					fix_incksum(fin, csump,
274292685Sdarrenr						    nat->nat_sumd[0]);
274353642Sguido				else
274492685Sdarrenr					fix_outcksum(fin, csump,
274592685Sdarrenr						    nat->nat_sumd[0]);
274653642Sguido			}
274753642Sguido		}
274860852Sdarrenr		ATOMIC_INCL(nat_stats.ns_mapped[0]);
274953642Sguido		RWLOCK_EXIT(&ipf_nat);			/* READ */
275053642Sguido		return 1;
275153642Sguido	}
275253642Sguido	RWLOCK_EXIT(&ipf_nat);			/* READ/WRITE */
275353642Sguido	return 0;
275453642Sguido}
275553642Sguido
275653642Sguido
275753642Sguido/*
275853642Sguido * Free all memory used by NAT structures allocated at runtime.
275953642Sguido */
276053642Sguidovoid ip_natunload()
276153642Sguido{
276253642Sguido	WRITE_ENTER(&ipf_nat);
276353642Sguido	(void) nat_clearlist();
276453642Sguido	(void) nat_flushtable();
276553642Sguido	RWLOCK_EXIT(&ipf_nat);
276653642Sguido
276753642Sguido	if (nat_table[0] != NULL) {
276853642Sguido		KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz);
276953642Sguido		nat_table[0] = NULL;
277053642Sguido	}
277153642Sguido	if (nat_table[1] != NULL) {
277253642Sguido		KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz);
277353642Sguido		nat_table[1] = NULL;
277453642Sguido	}
277553642Sguido	if (nat_rules != NULL) {
277653642Sguido		KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz);
277753642Sguido		nat_rules = NULL;
277853642Sguido	}
277953642Sguido	if (rdr_rules != NULL) {
278053642Sguido		KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz);
278153642Sguido		rdr_rules = NULL;
278253642Sguido	}
278360852Sdarrenr	if (maptable != NULL) {
278460852Sdarrenr		KFREES(maptable, sizeof(hostmap_t *) * ipf_hostmap_sz);
278560852Sdarrenr		maptable = NULL;
278660852Sdarrenr	}
278753642Sguido}
278853642Sguido
278953642Sguido
279053642Sguido/*
279153642Sguido * Slowly expire held state for NAT entries.  Timeouts are set in
279253642Sguido * expectation of this being called twice per second.
279353642Sguido */
279453642Sguidovoid ip_natexpire()
279553642Sguido{
279653642Sguido	register struct nat *nat, **natp;
279753642Sguido#if defined(_KERNEL) && !SOLARIS
279853642Sguido	int s;
279953642Sguido#endif
280053642Sguido
280153642Sguido	SPL_NET(s);
280253642Sguido	WRITE_ENTER(&ipf_nat);
280353642Sguido	for (natp = &nat_instances; (nat = *natp); ) {
280453642Sguido		nat->nat_age--;
280553642Sguido		if (nat->nat_age) {
280653642Sguido			natp = &nat->nat_next;
280753642Sguido			continue;
280853642Sguido		}
280953642Sguido		*natp = nat->nat_next;
281053642Sguido#ifdef	IPFILTER_LOG
281153642Sguido		nat_log(nat, NL_EXPIRE);
281253642Sguido#endif
281353642Sguido		nat_delete(nat);
281453642Sguido		nat_stats.ns_expire++;
281553642Sguido	}
281653642Sguido	RWLOCK_EXIT(&ipf_nat);
281753642Sguido	SPL_X(s);
281853642Sguido}
281953642Sguido
282053642Sguido
282153642Sguido/*
282253642Sguido */
282353642Sguidovoid ip_natsync(ifp)
282453642Sguidovoid *ifp;
282553642Sguido{
282653642Sguido	register ipnat_t *n;
282753642Sguido	register nat_t *nat;
282853642Sguido	register u_32_t sum1, sum2, sumd;
282953642Sguido	struct in_addr in;
283053642Sguido	ipnat_t *np;
283153642Sguido	void *ifp2;
283253642Sguido#if defined(_KERNEL) && !SOLARIS
283353642Sguido	int s;
283453642Sguido#endif
283553642Sguido
283653642Sguido	/*
283753642Sguido	 * Change IP addresses for NAT sessions for any protocol except TCP
283853642Sguido	 * since it will break the TCP connection anyway.
283953642Sguido	 */
284053642Sguido	SPL_NET(s);
284153642Sguido	WRITE_ENTER(&ipf_nat);
284253642Sguido	for (nat = nat_instances; nat; nat = nat->nat_next)
284353642Sguido		if (((ifp == NULL) || (ifp == nat->nat_ifp)) &&
284453642Sguido		    !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr) &&
284553642Sguido		    (np->in_outmsk == 0xffffffff) && !np->in_nip) {
284653642Sguido			ifp2 = nat->nat_ifp;
284753642Sguido			/*
284853642Sguido			 * Change the map-to address to be the same as the
284953642Sguido			 * new one.
285053642Sguido			 */
285153642Sguido			sum1 = nat->nat_outip.s_addr;
285260852Sdarrenr			if (fr_ifpaddr(4, ifp2, &in) != -1)
285355929Sguido				nat->nat_outip = in;
285453642Sguido			sum2 = nat->nat_outip.s_addr;
285553642Sguido
285653642Sguido			if (sum1 == sum2)
285753642Sguido				continue;
285853642Sguido			/*
285953642Sguido			 * Readjust the checksum adjustment to take into
286053642Sguido			 * account the new IP#.
286153642Sguido			 */
286253642Sguido			CALC_SUMD(sum1, sum2, sumd);
286355929Sguido			/* XXX - dont change for TCP when solaris does
286455929Sguido			 * hardware checksumming.
286555929Sguido			 */
286655929Sguido			sumd += nat->nat_sumd[0];
286755929Sguido			nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
286855929Sguido			nat->nat_sumd[1] = nat->nat_sumd[0];
286953642Sguido		}
287053642Sguido
287153642Sguido	for (n = nat_list; (n != NULL); n = n->in_next)
287255929Sguido		if (n->in_ifp == ifp) {
287360852Sdarrenr			n->in_ifp = (void *)GETUNIT(n->in_ifname, 4);
287460852Sdarrenr			if (!n->in_ifp)
287560852Sdarrenr				n->in_ifp = (void *)-1;
287660852Sdarrenr		}
287760852Sdarrenr	RWLOCK_EXIT(&ipf_nat);
287853642Sguido	SPL_X(s);
287953642Sguido}
288053642Sguido
288153642Sguido
288253642Sguido#ifdef	IPFILTER_LOG
288353642Sguidovoid nat_log(nat, type)
288453642Sguidostruct nat *nat;
288553642Sguidou_int type;
288653642Sguido{
288753642Sguido	struct ipnat *np;
288853642Sguido	struct natlog natl;
288953642Sguido	void *items[1];
289053642Sguido	size_t sizes[1];
289153642Sguido	int rulen, types[1];
289253642Sguido
289353642Sguido	natl.nl_inip = nat->nat_inip;
289453642Sguido	natl.nl_outip = nat->nat_outip;
289553642Sguido	natl.nl_origip = nat->nat_oip;
289653642Sguido	natl.nl_bytes = nat->nat_bytes;
289753642Sguido	natl.nl_pkts = nat->nat_pkts;
289853642Sguido	natl.nl_origport = nat->nat_oport;
289953642Sguido	natl.nl_inport = nat->nat_inport;
290053642Sguido	natl.nl_outport = nat->nat_outport;
290157096Sguido	natl.nl_p = nat->nat_p;
290253642Sguido	natl.nl_type = type;
290353642Sguido	natl.nl_rule = -1;
290453642Sguido#ifndef LARGE_NAT
290553642Sguido	if (nat->nat_ptr != NULL) {
290653642Sguido		for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++)
290753642Sguido			if (np == nat->nat_ptr) {
290853642Sguido				natl.nl_rule = rulen;
290953642Sguido				break;
291053642Sguido			}
291153642Sguido	}
291253642Sguido#endif
291353642Sguido	items[0] = &natl;
291453642Sguido	sizes[0] = sizeof(natl);
291553642Sguido	types[0] = 0;
291653642Sguido
291753642Sguido	(void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1);
291853642Sguido}
291953642Sguido#endif
292092685Sdarrenr
292192685Sdarrenr
292292685Sdarrenr#if defined(__OpenBSD__)
292392685Sdarrenrvoid nat_ifdetach(ifp)
292492685Sdarrenrvoid *ifp;
292592685Sdarrenr{
292692685Sdarrenr	frsync();
292792685Sdarrenr	return;
292892685Sdarrenr}
292992685Sdarrenr#endif
2930