ip_nat.c revision 67614
153642Sguido/*
260852Sdarrenr * Copyright (C) 1995-2000 by Darren Reed.
353642Sguido *
453642Sguido * Redistribution and use in source and binary forms are permitted
553642Sguido * provided that this notice is preserved and due credit is given
653642Sguido * to the original author and the contributors.
753642Sguido *
853642Sguido * Added redirect stuff and a LOT of bug fixes. (mcn@EnGarde.com)
953642Sguido */
1053642Sguido#if !defined(lint)
1153642Sguidostatic const char sccsid[] = "@(#)ip_nat.c	1.11 6/5/96 (C) 1995 Darren Reed";
1263523Sdarrenr/*static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.37.2.16 2000/07/18 13:57:40 darrenr Exp $";*/
1357126Sguidostatic const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_nat.c 67614 2000-10-26 12:33:42Z darrenr $";
1453642Sguido#endif
1553642Sguido
1653642Sguido#if defined(__FreeBSD__) && defined(KERNEL) && !defined(_KERNEL)
1753642Sguido#define _KERNEL
1853642Sguido#endif
1953642Sguido
2053642Sguido#include <sys/errno.h>
2153642Sguido#include <sys/types.h>
2253642Sguido#include <sys/param.h>
2353642Sguido#include <sys/time.h>
2453642Sguido#include <sys/file.h>
2553642Sguido#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \
2653642Sguido    defined(_KERNEL)
2753642Sguido# include "opt_ipfilter_log.h"
2853642Sguido#endif
2953642Sguido#if !defined(_KERNEL) && !defined(KERNEL)
3053642Sguido# include <stdio.h>
3153642Sguido# include <string.h>
3253642Sguido# include <stdlib.h>
3353642Sguido#endif
3460852Sdarrenr#if (defined(KERNEL) || defined(_KERNEL)) && (__FreeBSD_version >= 220000)
3553642Sguido# include <sys/filio.h>
3653642Sguido# include <sys/fcntl.h>
3753642Sguido#else
3853642Sguido# include <sys/ioctl.h>
3953642Sguido#endif
4053642Sguido#include <sys/fcntl.h>
4153642Sguido#include <sys/uio.h>
4253642Sguido#ifndef linux
4353642Sguido# include <sys/protosw.h>
4453642Sguido#endif
4553642Sguido#include <sys/socket.h>
4653642Sguido#if defined(_KERNEL) && !defined(linux)
4753642Sguido# include <sys/systm.h>
4853642Sguido#endif
4953642Sguido#if !defined(__SVR4) && !defined(__svr4__)
5053642Sguido# ifndef linux
5153642Sguido#  include <sys/mbuf.h>
5253642Sguido# endif
5353642Sguido#else
5453642Sguido# include <sys/filio.h>
5553642Sguido# include <sys/byteorder.h>
5653642Sguido# ifdef _KERNEL
5753642Sguido#  include <sys/dditypes.h>
5853642Sguido# endif
5953642Sguido# include <sys/stream.h>
6053642Sguido# include <sys/kmem.h>
6153642Sguido#endif
6253642Sguido#if __FreeBSD_version >= 300000
6353642Sguido# include <sys/queue.h>
6453642Sguido#endif
6553642Sguido#include <net/if.h>
6653642Sguido#if __FreeBSD_version >= 300000
6753642Sguido# include <net/if_var.h>
6853642Sguido# if defined(_KERNEL) && !defined(IPFILTER_LKM)
6953642Sguido#  include "opt_ipfilter.h"
7053642Sguido# endif
7153642Sguido#endif
7253642Sguido#ifdef sun
7353642Sguido# include <net/af.h>
7453642Sguido#endif
7553642Sguido#include <net/route.h>
7653642Sguido#include <netinet/in.h>
7753642Sguido#include <netinet/in_systm.h>
7853642Sguido#include <netinet/ip.h>
7953642Sguido
8053642Sguido#ifdef __sgi
8153642Sguido# ifdef IFF_DRVRLOCK /* IRIX6 */
8253642Sguido#include <sys/hashing.h>
8353642Sguido#include <netinet/in_var.h>
8453642Sguido# endif
8553642Sguido#endif
8653642Sguido
8753642Sguido#ifdef RFC1825
8853642Sguido# include <vpn/md5.h>
8953642Sguido# include <vpn/ipsec.h>
9053642Sguidoextern struct ifnet vpnif;
9153642Sguido#endif
9253642Sguido
9353642Sguido#ifndef linux
9453642Sguido# include <netinet/ip_var.h>
9553642Sguido#endif
9653642Sguido#include <netinet/tcp.h>
9753642Sguido#include <netinet/udp.h>
9853642Sguido#include <netinet/ip_icmp.h>
9953642Sguido#include "netinet/ip_compat.h"
10053642Sguido#include <netinet/tcpip.h>
10153642Sguido#include "netinet/ip_fil.h"
10253642Sguido#include "netinet/ip_proxy.h"
10353642Sguido#include "netinet/ip_nat.h"
10453642Sguido#include "netinet/ip_frag.h"
10553642Sguido#include "netinet/ip_state.h"
10653642Sguido#if (__FreeBSD_version >= 300000)
10753642Sguido# include <sys/malloc.h>
10853642Sguido#endif
10953642Sguido#ifndef	MIN
11053642Sguido# define	MIN(a,b)	(((a)<(b))?(a):(b))
11153642Sguido#endif
11253642Sguido#undef	SOCKADDR_IN
11353642Sguido#define	SOCKADDR_IN	struct sockaddr_in
11453642Sguido
11553642Sguidonat_t	**nat_table[2] = { NULL, NULL },
11653642Sguido	*nat_instances = NULL;
11753642Sguidoipnat_t	*nat_list = NULL;
11853642Sguidou_int	ipf_nattable_sz = NAT_TABLE_SZ;
11953642Sguidou_int	ipf_natrules_sz = NAT_SIZE;
12053642Sguidou_int	ipf_rdrrules_sz = RDR_SIZE;
12160852Sdarrenru_int	ipf_hostmap_sz = HOSTMAP_SIZE;
12267614Sdarrenrint	nat_wilds = 0;
12353642Sguidou_32_t	nat_masks = 0;
12453642Sguidou_32_t	rdr_masks = 0;
12553642Sguidoipnat_t	**nat_rules = NULL;
12653642Sguidoipnat_t	**rdr_rules = NULL;
12760852Sdarrenrhostmap_t	**maptable  = NULL;
12853642Sguido
12953642Sguidou_long	fr_defnatage = DEF_NAT_AGE,
13053642Sguido	fr_defnaticmpage = 6;		/* 3 seconds */
13164580Sdarrenrnatstat_t nat_stats;
13260852Sdarrenrint	fr_nat_lock = 0;
13353642Sguido#if	(SOLARIS || defined(__sgi)) && defined(_KERNEL)
13460852Sdarrenrextern	kmutex_t	ipf_rw, ipf_hostmap;
13553642Sguidoextern	KRWLOCK_T	ipf_nat;
13653642Sguido#endif
13753642Sguido
13853642Sguidostatic	int	nat_flushtable __P((void));
13953642Sguidostatic	int	nat_clearlist __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));
14867614Sdarrenrstatic	void	nat_tabmove __P((nat_t *, u_int));
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.
25560852Sdarrenr */
25660852Sdarrenrstatic struct hostmap *nat_hostmap(np, real, map)
25760852Sdarrenripnat_t *np;
25860852Sdarrenrstruct in_addr real;
25960852Sdarrenrstruct in_addr map;
26060852Sdarrenr{
26160852Sdarrenr	hostmap_t *hm;
26253642Sguido	u_int hv;
26353642Sguido
26460852Sdarrenr	MUTEX_ENTER(&ipf_hostmap);
26560852Sdarrenr	hv = real.s_addr % HOSTMAP_SIZE;
26660852Sdarrenr	for (hm = maptable[hv]; hm; hm = hm->hm_next)
26760852Sdarrenr		if ((hm->hm_realip.s_addr == real.s_addr) &&
26860852Sdarrenr		    (np == hm->hm_ipnat)) {
26960852Sdarrenr			hm->hm_ref++;
27060852Sdarrenr			MUTEX_EXIT(&ipf_hostmap);
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	MUTEX_EXIT(&ipf_hostmap);
28760852Sdarrenr	return hm;
28853642Sguido}
28953642Sguido
29053642Sguido
29160852Sdarrenrstatic void nat_hostmapdel(hm)
29260852Sdarrenrstruct hostmap *hm;
29360852Sdarrenr{
29460852Sdarrenr	MUTEX_ENTER(&ipf_hostmap);
29560852Sdarrenr	ATOMIC_DEC32(hm->hm_ref);
29660852Sdarrenr	if (hm->hm_ref == 0) {
29760852Sdarrenr		if (hm->hm_next)
29860852Sdarrenr			hm->hm_next->hm_pnext = hm->hm_pnext;
29960852Sdarrenr		*hm->hm_pnext = hm->hm_next;
30060852Sdarrenr		KFREE(hm);
30160852Sdarrenr	}
30260852Sdarrenr	MUTEX_EXIT(&ipf_hostmap);
30360852Sdarrenr}
30460852Sdarrenr
30560852Sdarrenr
30667614Sdarrenrvoid fix_outcksum(sp, n)
30753642Sguidou_short *sp;
30853642Sguidou_32_t n;
30953642Sguido{
31053642Sguido	register u_short sumshort;
31153642Sguido	register u_32_t sum1;
31253642Sguido
31353642Sguido	if (!n)
31453642Sguido		return;
31555929Sguido#if SOLARIS2 >= 6
31655929Sguido	else if (n & NAT_HW_CKSUM) {
31755929Sguido		*sp = n & 0xffff;
31855929Sguido		return;
31955929Sguido	}
32055929Sguido#endif
32153642Sguido	sum1 = (~ntohs(*sp)) & 0xffff;
32253642Sguido	sum1 += (n);
32353642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
32453642Sguido	/* Again */
32553642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
32653642Sguido	sumshort = ~(u_short)sum1;
32753642Sguido	*(sp) = htons(sumshort);
32853642Sguido}
32953642Sguido
33053642Sguido
33167614Sdarrenrvoid fix_incksum(sp, n)
33253642Sguidou_short *sp;
33353642Sguidou_32_t n;
33453642Sguido{
33553642Sguido	register u_short sumshort;
33653642Sguido	register u_32_t sum1;
33753642Sguido
33853642Sguido	if (!n)
33953642Sguido		return;
34055929Sguido#if SOLARIS2 >= 6
34155929Sguido	else if (n & NAT_HW_CKSUM) {
34255929Sguido		*sp = n & 0xffff;
34355929Sguido		return;
34455929Sguido	}
34555929Sguido#endif
34653642Sguido#ifdef sparc
34753642Sguido	sum1 = (~(*sp)) & 0xffff;
34853642Sguido#else
34953642Sguido	sum1 = (~ntohs(*sp)) & 0xffff;
35053642Sguido#endif
35153642Sguido	sum1 += ~(n) & 0xffff;
35253642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
35353642Sguido	/* Again */
35453642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
35553642Sguido	sumshort = ~(u_short)sum1;
35653642Sguido	*(sp) = htons(sumshort);
35753642Sguido}
35853642Sguido
35953642Sguido
36053642Sguido/*
36167614Sdarrenr * fix_datacksum is used *only* for the adjustments of checksums in the data
36267614Sdarrenr * section of an IP packet.
36367614Sdarrenr *
36467614Sdarrenr * The only situation in which you need to do this is when NAT'ing an
36567614Sdarrenr * ICMP error message. Such a message, contains in its body the IP header
36667614Sdarrenr * of the original IP packet, that causes the error.
36767614Sdarrenr *
36867614Sdarrenr * You can't use fix_incksum or fix_outcksum in that case, because for the
36967614Sdarrenr * kernel the data section of the ICMP error is just data, and no special
37067614Sdarrenr * processing like hardware cksum or ntohs processing have been done by the
37167614Sdarrenr * kernel on the data section.
37267614Sdarrenr */
37367614Sdarrenrvoid fix_datacksum(sp, n)
37467614Sdarrenru_short *sp;
37567614Sdarrenru_32_t n;
37667614Sdarrenr{
37767614Sdarrenr	register u_short sumshort;
37867614Sdarrenr	register u_32_t sum1;
37967614Sdarrenr
38067614Sdarrenr	if (!n)
38167614Sdarrenr		return;
38267614Sdarrenr
38367614Sdarrenr	sum1 = (~ntohs(*sp)) & 0xffff;
38467614Sdarrenr	sum1 += (n);
38567614Sdarrenr	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
38667614Sdarrenr	/* Again */
38767614Sdarrenr	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
38867614Sdarrenr	sumshort = ~(u_short)sum1;
38967614Sdarrenr	*(sp) = htons(sumshort);
39067614Sdarrenr}
39167614Sdarrenr
39267614Sdarrenr/*
39353642Sguido * How the NAT is organised and works.
39453642Sguido *
39553642Sguido * Inside (interface y) NAT       Outside (interface x)
39653642Sguido * -------------------- -+- -------------------------------------
39753642Sguido * Packet going          |   out, processsed by ip_natout() for x
39853642Sguido * ------------>         |   ------------>
39953642Sguido * src=10.1.1.1          |   src=192.1.1.1
40053642Sguido *                       |
40153642Sguido *                       |   in, processed by ip_natin() for x
40253642Sguido * <------------         |   <------------
40353642Sguido * dst=10.1.1.1          |   dst=192.1.1.1
40453642Sguido * -------------------- -+- -------------------------------------
40553642Sguido * ip_natout() - changes ip_src and if required, sport
40653642Sguido *             - creates a new mapping, if required.
40753642Sguido * ip_natin()  - changes ip_dst and if required, dport
40853642Sguido *
40953642Sguido * In the NAT table, internal source is recorded as "in" and externally
41053642Sguido * seen as "out".
41153642Sguido */
41253642Sguido
41353642Sguido/*
41453642Sguido * Handle ioctls which manipulate the NAT.
41553642Sguido */
41653642Sguidoint nat_ioctl(data, cmd, mode)
41760852Sdarrenr#if defined(__NetBSD__) || defined(__OpenBSD__) || (__FreeBSD_version >= 300003)
41853642Sguidou_long cmd;
41953642Sguido#else
42053642Sguidoint cmd;
42153642Sguido#endif
42253642Sguidocaddr_t data;
42353642Sguidoint mode;
42453642Sguido{
42553642Sguido	register ipnat_t *nat, *nt, *n = NULL, **np = NULL;
42660852Sdarrenr	int error = 0, ret, arg;
42753642Sguido	ipnat_t natd;
42853642Sguido	u_32_t i, j;
42953642Sguido
43053642Sguido#if (BSD >= 199306) && defined(_KERNEL)
43153642Sguido	if ((securelevel >= 2) && (mode & FWRITE))
43253642Sguido		return EPERM;
43353642Sguido#endif
43453642Sguido
43553642Sguido	nat = NULL;     /* XXX gcc -Wuninitialized */
43653642Sguido	KMALLOC(nt, ipnat_t *);
43753642Sguido	if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT))
43860852Sdarrenr		error = IRCOPYPTR(data, (char *)&natd, sizeof(natd));
43964580Sdarrenr	else if (cmd == SIOCIPFFL) {	/* SIOCFLNAT & SIOCCNATL */
44060852Sdarrenr		error = IRCOPY(data, (char *)&arg, sizeof(arg));
44164580Sdarrenr		if (error)
44264580Sdarrenr			error = EFAULT;
44364580Sdarrenr	}
44453642Sguido
44560852Sdarrenr	if (error)
44660852Sdarrenr		goto done;
44760852Sdarrenr
44853642Sguido	/*
44953642Sguido	 * For add/delete, look to see if the NAT entry is already present
45053642Sguido	 */
45153642Sguido	WRITE_ENTER(&ipf_nat);
45253642Sguido	if ((cmd == SIOCADNAT) || (cmd == SIOCRMNAT)) {
45353642Sguido		nat = &natd;
45453642Sguido		nat->in_flags &= IPN_USERFLAGS;
45553642Sguido		if ((nat->in_redir & NAT_MAPBLK) == 0) {
45660852Sdarrenr			if ((nat->in_flags & IPN_SPLIT) == 0)
45760852Sdarrenr				nat->in_inip &= nat->in_inmsk;
45860852Sdarrenr			if ((nat->in_flags & IPN_IPRANGE) == 0)
45953642Sguido				nat->in_outip &= nat->in_outmsk;
46053642Sguido		}
46153642Sguido		for (np = &nat_list; (n = *np); np = &n->in_next)
46253642Sguido			if (!bcmp((char *)&nat->in_flags, (char *)&n->in_flags,
46353642Sguido					IPN_CMPSIZ))
46453642Sguido				break;
46553642Sguido	}
46653642Sguido
46753642Sguido	switch (cmd)
46853642Sguido	{
46955929Sguido#ifdef  IPFILTER_LOG
47055929Sguido	case SIOCIPFFB :
47160852Sdarrenr	{
47260852Sdarrenr		int tmp;
47360852Sdarrenr
47455929Sguido		if (!(mode & FWRITE))
47555929Sguido			error = EPERM;
47660852Sdarrenr		else {
47760852Sdarrenr			tmp = ipflog_clear(IPL_LOGNAT);
47860852Sdarrenr			IWCOPY((char *)&tmp, (char *)data, sizeof(tmp));
47960852Sdarrenr		}
48055929Sguido		break;
48160852Sdarrenr	}
48255929Sguido#endif
48353642Sguido	case SIOCADNAT :
48453642Sguido		if (!(mode & FWRITE)) {
48553642Sguido			error = EPERM;
48653642Sguido			break;
48753642Sguido		}
48853642Sguido		if (n) {
48953642Sguido			error = EEXIST;
49053642Sguido			break;
49153642Sguido		}
49253642Sguido		if (nt == NULL) {
49353642Sguido			error = ENOMEM;
49453642Sguido			break;
49553642Sguido		}
49653642Sguido		n = nt;
49753642Sguido		nt = NULL;
49853642Sguido		bcopy((char *)nat, (char *)n, sizeof(*n));
49960852Sdarrenr		n->in_ifp = (void *)GETUNIT(n->in_ifname, 4);
50053642Sguido		if (!n->in_ifp)
50153642Sguido			n->in_ifp = (void *)-1;
50253642Sguido		if (n->in_plabel[0] != '\0') {
50353642Sguido			n->in_apr = appr_match(n->in_p, n->in_plabel);
50453642Sguido			if (!n->in_apr) {
50553642Sguido				error = ENOENT;
50653642Sguido				break;
50753642Sguido			}
50853642Sguido		}
50953642Sguido		n->in_next = NULL;
51053642Sguido		*np = n;
51153642Sguido
51263523Sdarrenr		if (n->in_redir & NAT_REDIRECT) {
51363523Sdarrenr			n->in_flags &= ~IPN_NOTDST;
51460852Sdarrenr			nat_addrdr(n);
51563523Sdarrenr		}
51663523Sdarrenr		if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
51763523Sdarrenr			n->in_flags &= ~IPN_NOTSRC;
51860852Sdarrenr			nat_addnat(n);
51963523Sdarrenr		}
52053642Sguido
52153642Sguido		n->in_use = 0;
52253642Sguido		if (n->in_redir & NAT_MAPBLK)
52353642Sguido			n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk);
52453642Sguido		else if (n->in_flags & IPN_AUTOPORTMAP)
52553642Sguido			n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk);
52660852Sdarrenr		else if (n->in_flags & IPN_IPRANGE)
52753642Sguido			n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip);
52860852Sdarrenr		else if (n->in_flags & IPN_SPLIT)
52960852Sdarrenr			n->in_space = 2;
53053642Sguido		else
53153642Sguido			n->in_space = ~ntohl(n->in_outmsk);
53253642Sguido		/*
53353642Sguido		 * Calculate the number of valid IP addresses in the output
53453642Sguido		 * mapping range.  In all cases, the range is inclusive of
53553642Sguido		 * the start and ending IP addresses.
53653642Sguido		 * If to a CIDR address, lose 2: broadcast + network address
53764580Sdarrenr		 *			         (so subtract 1)
53853642Sguido		 * If to a range, add one.
53953642Sguido		 * If to a single IP address, set to 1.
54053642Sguido		 */
54153642Sguido		if (n->in_space) {
54260852Sdarrenr			if ((n->in_flags & IPN_IPRANGE) != 0)
54353642Sguido				n->in_space += 1;
54453642Sguido			else
54553642Sguido				n->in_space -= 1;
54653642Sguido		} else
54753642Sguido			n->in_space = 1;
54853642Sguido		if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) &&
54960852Sdarrenr		    ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0))
55053642Sguido			n->in_nip = ntohl(n->in_outip) + 1;
55160852Sdarrenr		else if ((n->in_flags & IPN_SPLIT) &&
55260852Sdarrenr			 (n->in_redir & NAT_REDIRECT))
55360852Sdarrenr			n->in_nip = ntohl(n->in_inip);
55453642Sguido		else
55553642Sguido			n->in_nip = ntohl(n->in_outip);
55653642Sguido		if (n->in_redir & NAT_MAP) {
55753642Sguido			n->in_pnext = ntohs(n->in_pmin);
55853642Sguido			/*
55953642Sguido			 * Multiply by the number of ports made available.
56053642Sguido			 */
56153642Sguido			if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) {
56253642Sguido				n->in_space *= (ntohs(n->in_pmax) -
56353642Sguido						ntohs(n->in_pmin) + 1);
56453642Sguido				/*
56553642Sguido				 * Because two different sources can map to
56653642Sguido				 * different destinations but use the same
56753642Sguido				 * local IP#/port #.
56853642Sguido				 * If the result is smaller than in_space, then
56953642Sguido				 * we may have wrapped around 32bits.
57053642Sguido				 */
57153642Sguido				i = n->in_inmsk;
57253642Sguido				if ((i != 0) && (i != 0xffffffff)) {
57353642Sguido					j = n->in_space * (~ntohl(i) + 1);
57453642Sguido					if (j >= n->in_space)
57553642Sguido						n->in_space = j;
57653642Sguido					else
57753642Sguido						n->in_space = 0xffffffff;
57853642Sguido				}
57953642Sguido			}
58053642Sguido			/*
58153642Sguido			 * If no protocol is specified, multiple by 256.
58253642Sguido			 */
58353642Sguido			if ((n->in_flags & IPN_TCPUDP) == 0) {
58453642Sguido					j = n->in_space * 256;
58553642Sguido					if (j >= n->in_space)
58653642Sguido						n->in_space = j;
58753642Sguido					else
58853642Sguido						n->in_space = 0xffffffff;
58953642Sguido			}
59053642Sguido		}
59153642Sguido		/* Otherwise, these fields are preset */
59253642Sguido		n = NULL;
59353642Sguido		nat_stats.ns_rules++;
59453642Sguido		break;
59553642Sguido	case SIOCRMNAT :
59653642Sguido		if (!(mode & FWRITE)) {
59753642Sguido			error = EPERM;
59853642Sguido			n = NULL;
59953642Sguido			break;
60053642Sguido		}
60153642Sguido		if (!n) {
60253642Sguido			error = ESRCH;
60353642Sguido			break;
60453642Sguido		}
60553642Sguido		if (n->in_redir & NAT_REDIRECT)
60653642Sguido			nat_delrdr(n);
60753642Sguido		if (n->in_redir & (NAT_MAPBLK|NAT_MAP))
60853642Sguido			nat_delnat(n);
60953642Sguido		if (nat_list == NULL) {
61053642Sguido			nat_masks = 0;
61153642Sguido			rdr_masks = 0;
61253642Sguido		}
61353642Sguido		*np = n->in_next;
61453642Sguido		if (!n->in_use) {
61553642Sguido			if (n->in_apr)
61653642Sguido				appr_free(n->in_apr);
61753642Sguido			KFREE(n);
61853642Sguido			nat_stats.ns_rules--;
61953642Sguido		} else {
62053642Sguido			n->in_flags |= IPN_DELETE;
62153642Sguido			n->in_next = NULL;
62253642Sguido		}
62353642Sguido		n = NULL;
62453642Sguido		break;
62553642Sguido	case SIOCGNATS :
62653642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
62753642Sguido		nat_stats.ns_table[0] = nat_table[0];
62853642Sguido		nat_stats.ns_table[1] = nat_table[1];
62953642Sguido		nat_stats.ns_list = nat_list;
63053642Sguido		nat_stats.ns_nattab_sz = ipf_nattable_sz;
63153642Sguido		nat_stats.ns_rultab_sz = ipf_natrules_sz;
63253642Sguido		nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz;
63353642Sguido		nat_stats.ns_instances = nat_instances;
63453642Sguido		nat_stats.ns_apslist = ap_sess_list;
63560852Sdarrenr		error = IWCOPYPTR((char *)&nat_stats, (char *)data,
63660852Sdarrenr				  sizeof(nat_stats));
63753642Sguido		break;
63853642Sguido	case SIOCGNATL :
63953642Sguido	    {
64053642Sguido		natlookup_t nl;
64153642Sguido
64253642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
64360852Sdarrenr		error = IRCOPYPTR((char *)data, (char *)&nl, sizeof(nl));
64460852Sdarrenr		if (error)
64560852Sdarrenr			break;
64653642Sguido
64753642Sguido		if (nat_lookupredir(&nl)) {
64860852Sdarrenr			error = IWCOPYPTR((char *)&nl, (char *)data,
64960852Sdarrenr					  sizeof(nl));
65053642Sguido		} else
65153642Sguido			error = ESRCH;
65253642Sguido		break;
65353642Sguido	    }
65460852Sdarrenr	case SIOCIPFFL :	/* old SIOCFLNAT & SIOCCNATL */
65553642Sguido		if (!(mode & FWRITE)) {
65653642Sguido			error = EPERM;
65753642Sguido			break;
65853642Sguido		}
65960852Sdarrenr		error = 0;
66060852Sdarrenr		if (arg == 0)
66160852Sdarrenr			ret = nat_flushtable();
66260852Sdarrenr		else if (arg == 1)
66360852Sdarrenr			ret = nat_clearlist();
66460852Sdarrenr		else
66560852Sdarrenr			error = EINVAL;
66653642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
66760852Sdarrenr		if (!error) {
66860852Sdarrenr			error = IWCOPY((caddr_t)&ret, data, sizeof(ret));
66960852Sdarrenr			if (error)
67060852Sdarrenr				error = EFAULT;
67160852Sdarrenr		}
67253642Sguido		break;
67360852Sdarrenr	case SIOCSTLCK :
67460852Sdarrenr		error = IRCOPY(data, (caddr_t)&arg, sizeof(arg));
67560852Sdarrenr		if (!error) {
67660852Sdarrenr			error = IWCOPY((caddr_t)&fr_nat_lock, data,
67760852Sdarrenr					sizeof(fr_nat_lock));
67860852Sdarrenr			if (!error)
67960852Sdarrenr				fr_nat_lock = arg;
68064580Sdarrenr		} else
68164580Sdarrenr			error = EFAULT;
68253642Sguido		break;
68360852Sdarrenr	case SIOCSTPUT :
68460852Sdarrenr		if (fr_nat_lock)
68560852Sdarrenr			error = fr_natputent(data);
68660852Sdarrenr		else
68760852Sdarrenr			error = EACCES;
68860852Sdarrenr		break;
68960852Sdarrenr	case SIOCSTGSZ :
69060852Sdarrenr		if (fr_nat_lock)
69160852Sdarrenr			error = fr_natgetsz(data);
69260852Sdarrenr		else
69360852Sdarrenr			error = EACCES;
69460852Sdarrenr		break;
69560852Sdarrenr	case SIOCSTGET :
69660852Sdarrenr		if (fr_nat_lock)
69760852Sdarrenr			error = fr_natgetent(data);
69860852Sdarrenr		else
69960852Sdarrenr			error = EACCES;
70060852Sdarrenr		break;
70153642Sguido	case FIONREAD :
70253642Sguido#ifdef	IPFILTER_LOG
70353642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
70460852Sdarrenr		error = IWCOPY((caddr_t)&iplused[IPL_LOGNAT], (caddr_t)data,
70560852Sdarrenr			       sizeof(iplused[IPL_LOGNAT]));
70664580Sdarrenr		if (error)
70764580Sdarrenr			error = EFAULT;
70853642Sguido#endif
70953642Sguido		break;
71053642Sguido	default :
71153642Sguido		error = EINVAL;
71253642Sguido		break;
71353642Sguido	}
71453642Sguido	RWLOCK_EXIT(&ipf_nat);			/* READ/WRITE */
71560852Sdarrenrdone:
71653642Sguido	if (nt)
71753642Sguido		KFREE(nt);
71853642Sguido	return error;
71953642Sguido}
72053642Sguido
72153642Sguido
72260852Sdarrenrstatic int fr_natgetsz(data)
72360852Sdarrenrcaddr_t data;
72460852Sdarrenr{
72560852Sdarrenr	ap_session_t *aps;
72660852Sdarrenr	nat_t *nat, *n;
72760852Sdarrenr	int error = 0;
72860852Sdarrenr	natget_t ng;
72960852Sdarrenr
73060852Sdarrenr	error = IRCOPY(data, (caddr_t)&ng, sizeof(ng));
73160852Sdarrenr	if (error)
73260852Sdarrenr		return EFAULT;
73360852Sdarrenr
73460852Sdarrenr	nat = ng.ng_ptr;
73560852Sdarrenr	if (!nat) {
73660852Sdarrenr		nat = nat_instances;
73760852Sdarrenr		ng.ng_sz = 0;
73860852Sdarrenr		if (nat == NULL) {
73960852Sdarrenr			error = IWCOPY((caddr_t)&ng, data, sizeof(ng));
74060852Sdarrenr			if (error)
74160852Sdarrenr				error = EFAULT;
74260852Sdarrenr			return error;
74360852Sdarrenr		}
74460852Sdarrenr	} else {
74560852Sdarrenr		/*
74660852Sdarrenr		 * Make sure the pointer we're copying from exists in the
74760852Sdarrenr		 * current list of entries.  Security precaution to prevent
74860852Sdarrenr		 * copying of random kernel data.
74960852Sdarrenr		 */
75060852Sdarrenr		for (n = nat_instances; n; n = n->nat_next)
75160852Sdarrenr			if (n == nat)
75260852Sdarrenr				break;
75360852Sdarrenr		if (!n)
75460852Sdarrenr			return ESRCH;
75560852Sdarrenr	}
75660852Sdarrenr
75760852Sdarrenr	ng.ng_sz = sizeof(nat_save_t);
75860852Sdarrenr	aps = nat->nat_aps;
75960852Sdarrenr	if ((aps != NULL) && (aps->aps_data != 0)) {
76060852Sdarrenr		ng.ng_sz += sizeof(ap_session_t);
76160852Sdarrenr		ng.ng_sz += aps->aps_psiz;
76260852Sdarrenr	}
76360852Sdarrenr
76460852Sdarrenr	error = IWCOPY((caddr_t)&ng, data, sizeof(ng));
76560852Sdarrenr	if (error)
76660852Sdarrenr		error = EFAULT;
76760852Sdarrenr	return error;
76860852Sdarrenr}
76960852Sdarrenr
77060852Sdarrenr
77160852Sdarrenrstatic int fr_natgetent(data)
77260852Sdarrenrcaddr_t data;
77360852Sdarrenr{
77464580Sdarrenr	nat_save_t ipn, *ipnp, *ipnn = NULL;
77560852Sdarrenr	register nat_t *n, *nat;
77660852Sdarrenr	ap_session_t *aps;
77760852Sdarrenr	int error;
77860852Sdarrenr
77960852Sdarrenr	error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp));
78060852Sdarrenr	if (error)
78160852Sdarrenr		return EFAULT;
78260852Sdarrenr	error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn));
78360852Sdarrenr	if (error)
78460852Sdarrenr		return EFAULT;
78560852Sdarrenr
78660852Sdarrenr	nat = ipn.ipn_next;
78760852Sdarrenr	if (!nat) {
78860852Sdarrenr		nat = nat_instances;
78960852Sdarrenr		if (nat == NULL) {
79060852Sdarrenr			if (nat_instances == NULL)
79160852Sdarrenr				return ENOENT;
79260852Sdarrenr			return 0;
79360852Sdarrenr		}
79460852Sdarrenr	} else {
79560852Sdarrenr		/*
79660852Sdarrenr		 * Make sure the pointer we're copying from exists in the
79760852Sdarrenr		 * current list of entries.  Security precaution to prevent
79860852Sdarrenr		 * copying of random kernel data.
79960852Sdarrenr		 */
80060852Sdarrenr		for (n = nat_instances; n; n = n->nat_next)
80160852Sdarrenr			if (n == nat)
80260852Sdarrenr				break;
80360852Sdarrenr		if (!n)
80460852Sdarrenr			return ESRCH;
80560852Sdarrenr	}
80660852Sdarrenr
80760852Sdarrenr	ipn.ipn_next = nat->nat_next;
80860852Sdarrenr	ipn.ipn_dsize = 0;
80960852Sdarrenr	bcopy((char *)nat, (char *)&ipn.ipn_nat, sizeof(ipn.ipn_nat));
81060852Sdarrenr	ipn.ipn_nat.nat_data = NULL;
81160852Sdarrenr
81260852Sdarrenr	if (nat->nat_ptr) {
81360852Sdarrenr		bcopy((char *)nat->nat_ptr, (char *)&ipn.ipn_ipnat,
81460852Sdarrenr		      sizeof(ipn.ipn_ipnat));
81560852Sdarrenr	}
81660852Sdarrenr
81760852Sdarrenr	if (nat->nat_fr)
81860852Sdarrenr		bcopy((char *)nat->nat_fr, (char *)&ipn.ipn_rule,
81960852Sdarrenr		      sizeof(ipn.ipn_rule));
82060852Sdarrenr
82160852Sdarrenr	if ((aps = nat->nat_aps)) {
82260852Sdarrenr		ipn.ipn_dsize = sizeof(*aps);
82360852Sdarrenr		if (aps->aps_data)
82460852Sdarrenr			ipn.ipn_dsize += aps->aps_psiz;
82560852Sdarrenr		KMALLOCS(ipnn, nat_save_t *, sizeof(*ipnn) + ipn.ipn_dsize);
82660852Sdarrenr		if (ipnn == NULL)
82764580Sdarrenr			return ENOMEM;
82860852Sdarrenr		bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn));
82960852Sdarrenr
83064580Sdarrenr		bcopy((char *)aps, ipnn->ipn_data, sizeof(*aps));
83160852Sdarrenr		if (aps->aps_data) {
83264580Sdarrenr			bcopy(aps->aps_data, ipnn->ipn_data + sizeof(*aps),
83360852Sdarrenr			      aps->aps_psiz);
83464580Sdarrenr			ipnn->ipn_dsize += aps->aps_psiz;
83560852Sdarrenr		}
83660852Sdarrenr		error = IWCOPY((caddr_t)ipnn, ipnp,
83760852Sdarrenr			       sizeof(ipn) + ipn.ipn_dsize);
83860852Sdarrenr		if (error)
83964580Sdarrenr			error = EFAULT;
84060852Sdarrenr		KFREES(ipnn, sizeof(*ipnn) + ipn.ipn_dsize);
84160852Sdarrenr	} else {
84260852Sdarrenr		error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn));
84360852Sdarrenr		if (error)
84464580Sdarrenr			error = EFAULT;
84560852Sdarrenr	}
84664580Sdarrenr	return error;
84760852Sdarrenr}
84860852Sdarrenr
84960852Sdarrenr
85060852Sdarrenrstatic int fr_natputent(data)
85160852Sdarrenrcaddr_t data;
85260852Sdarrenr{
85364580Sdarrenr	nat_save_t ipn, *ipnp, *ipnn = NULL;
85460852Sdarrenr	register nat_t *n, *nat;
85560852Sdarrenr	ap_session_t *aps;
85660852Sdarrenr	frentry_t *fr;
85760852Sdarrenr	ipnat_t *in;
85860852Sdarrenr
85960852Sdarrenr	int error;
86060852Sdarrenr
86160852Sdarrenr	error = IRCOPY(data, (caddr_t)&ipnp, sizeof(ipnp));
86260852Sdarrenr	if (error)
86360852Sdarrenr		return EFAULT;
86460852Sdarrenr	error = IRCOPY((caddr_t)ipnp, (caddr_t)&ipn, sizeof(ipn));
86560852Sdarrenr	if (error)
86660852Sdarrenr		return EFAULT;
86764580Sdarrenr	nat = NULL;
86860852Sdarrenr	if (ipn.ipn_dsize) {
86960852Sdarrenr		KMALLOCS(ipnn, nat_save_t *, sizeof(ipn) + ipn.ipn_dsize);
87060852Sdarrenr		if (ipnn == NULL)
87160852Sdarrenr			return ENOMEM;
87260852Sdarrenr		bcopy((char *)&ipn, (char *)ipnn, sizeof(ipn));
87360852Sdarrenr		error = IRCOPY((caddr_t)ipnp, (caddr_t)ipn.ipn_data,
87460852Sdarrenr			       ipn.ipn_dsize);
87564580Sdarrenr		if (error) {
87664580Sdarrenr			error = EFAULT;
87764580Sdarrenr			goto junkput;
87864580Sdarrenr		}
87960852Sdarrenr	} else
88060852Sdarrenr		ipnn = NULL;
88160852Sdarrenr
88260852Sdarrenr	KMALLOC(nat, nat_t *);
88364580Sdarrenr	if (nat == NULL) {
88464580Sdarrenr		error = EFAULT;
88564580Sdarrenr		goto junkput;
88664580Sdarrenr	}
88760852Sdarrenr
88860852Sdarrenr	bcopy((char *)&ipn.ipn_nat, (char *)nat, sizeof(*nat));
88960852Sdarrenr	/*
89060852Sdarrenr	 * Initialize all these so that nat_delete() doesn't cause a crash.
89160852Sdarrenr	 */
89267614Sdarrenr	nat->nat_phnext[0] = NULL;
89367614Sdarrenr	nat->nat_phnext[1] = NULL;
89460852Sdarrenr	fr = nat->nat_fr;
89560852Sdarrenr	nat->nat_fr = NULL;
89660852Sdarrenr	aps = nat->nat_aps;
89760852Sdarrenr	nat->nat_aps = NULL;
89860852Sdarrenr	in = nat->nat_ptr;
89960852Sdarrenr	nat->nat_ptr = NULL;
90060852Sdarrenr	nat->nat_data = NULL;
90160852Sdarrenr
90260852Sdarrenr	/*
90360852Sdarrenr	 * Restore the rule associated with this nat session
90460852Sdarrenr	 */
90560852Sdarrenr	if (in) {
90660852Sdarrenr		KMALLOC(in, ipnat_t *);
90760852Sdarrenr		if (in == NULL) {
90860852Sdarrenr			error = ENOMEM;
90960852Sdarrenr			goto junkput;
91060852Sdarrenr		}
91160852Sdarrenr		nat->nat_ptr = in;
91260852Sdarrenr		bcopy((char *)&ipn.ipn_ipnat, (char *)in, sizeof(*in));
91360852Sdarrenr		in->in_use = 1;
91460852Sdarrenr		in->in_flags |= IPN_DELETE;
91560852Sdarrenr		in->in_next = NULL;
91660852Sdarrenr		in->in_rnext = NULL;
91760852Sdarrenr		in->in_prnext = NULL;
91860852Sdarrenr		in->in_mnext = NULL;
91960852Sdarrenr		in->in_pmnext = NULL;
92060852Sdarrenr		in->in_ifp = GETUNIT(in->in_ifname, 4);
92160852Sdarrenr		if (in->in_plabel[0] != '\0') {
92260852Sdarrenr			in->in_apr = appr_match(in->in_p, in->in_plabel);
92360852Sdarrenr		}
92460852Sdarrenr	}
92560852Sdarrenr
92660852Sdarrenr	/*
92760852Sdarrenr	 * Restore ap_session_t structure.  Include the private data allocated
92860852Sdarrenr	 * if it was there.
92960852Sdarrenr	 */
93060852Sdarrenr	if (aps) {
93160852Sdarrenr		KMALLOC(aps, ap_session_t *);
93260852Sdarrenr		if (aps == NULL) {
93360852Sdarrenr			error = ENOMEM;
93460852Sdarrenr			goto junkput;
93560852Sdarrenr		}
93660852Sdarrenr		nat->nat_aps = aps;
93760852Sdarrenr		aps->aps_next = ap_sess_list;
93860852Sdarrenr		ap_sess_list = aps;
93960852Sdarrenr		bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps));
94060852Sdarrenr		if (in)
94160852Sdarrenr			aps->aps_apr = in->in_apr;
94260852Sdarrenr		if (aps->aps_psiz) {
94360852Sdarrenr			KMALLOCS(aps->aps_data, void *, aps->aps_psiz);
94460852Sdarrenr			if (aps->aps_data == NULL) {
94560852Sdarrenr				error = ENOMEM;
94660852Sdarrenr				goto junkput;
94760852Sdarrenr			}
94860852Sdarrenr			bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data,
94960852Sdarrenr			      aps->aps_psiz);
95060852Sdarrenr		} else {
95160852Sdarrenr			aps->aps_psiz = 0;
95260852Sdarrenr			aps->aps_data = NULL;
95360852Sdarrenr		}
95460852Sdarrenr	}
95560852Sdarrenr
95660852Sdarrenr	/*
95760852Sdarrenr	 * If there was a filtering rule associated with this entry then
95860852Sdarrenr	 * build up a new one.
95960852Sdarrenr	 */
96060852Sdarrenr	if (fr != NULL) {
96160852Sdarrenr		if (nat->nat_flags & FI_NEWFR) {
96260852Sdarrenr			KMALLOC(fr, frentry_t *);
96360852Sdarrenr			nat->nat_fr = fr;
96460852Sdarrenr			if (fr == NULL) {
96560852Sdarrenr				error = ENOMEM;
96660852Sdarrenr				goto junkput;
96760852Sdarrenr			}
96860852Sdarrenr			bcopy((char *)&ipn.ipn_fr, (char *)fr, sizeof(*fr));
96960852Sdarrenr			ipn.ipn_nat.nat_fr = fr;
97060852Sdarrenr			error = IWCOPY((caddr_t)&ipn, ipnp, sizeof(ipn));
97160852Sdarrenr			if (error) {
97260852Sdarrenr				error = EFAULT;
97360852Sdarrenr				goto junkput;
97460852Sdarrenr			}
97560852Sdarrenr		} else {
97660852Sdarrenr			for (n = nat_instances; n; n = n->nat_next)
97760852Sdarrenr				if (n->nat_fr == fr)
97860852Sdarrenr					break;
97960852Sdarrenr			if (!n) {
98060852Sdarrenr				error = ESRCH;
98160852Sdarrenr				goto junkput;
98260852Sdarrenr			}
98360852Sdarrenr		}
98460852Sdarrenr	}
98560852Sdarrenr
98660852Sdarrenr	if (ipnn)
98760852Sdarrenr		KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize);
98860852Sdarrenr	nat_insert(nat);
98960852Sdarrenr	return 0;
99060852Sdarrenrjunkput:
99160852Sdarrenr	if (ipnn)
99260852Sdarrenr		KFREES(ipnn, sizeof(ipn) + ipn.ipn_dsize);
99360852Sdarrenr	if (nat)
99460852Sdarrenr		nat_delete(nat);
99560852Sdarrenr	return error;
99660852Sdarrenr}
99760852Sdarrenr
99860852Sdarrenr
99953642Sguido/*
100053642Sguido * Delete a nat entry from the various lists and table.
100153642Sguido */
100253642Sguidostatic void nat_delete(natd)
100353642Sguidostruct nat *natd;
100453642Sguido{
100553642Sguido	struct ipnat *ipn;
100653642Sguido
100767614Sdarrenr	if (natd->nat_flags & FI_WILDP)
100867614Sdarrenr		nat_wilds--;
100967614Sdarrenr	if (natd->nat_hnext[0])
101067614Sdarrenr		natd->nat_hnext[0]->nat_phnext[0] = natd->nat_phnext[0];
101167614Sdarrenr	*natd->nat_phnext[0] = natd->nat_hnext[0];
101267614Sdarrenr	if (natd->nat_hnext[1])
101367614Sdarrenr		natd->nat_hnext[1]->nat_phnext[1] = natd->nat_phnext[1];
101467614Sdarrenr	*natd->nat_phnext[1] = natd->nat_hnext[1];
101553642Sguido
101653642Sguido	if (natd->nat_fr != NULL) {
101760852Sdarrenr		ATOMIC_DEC32(natd->nat_fr->fr_ref);
101853642Sguido	}
101960852Sdarrenr
102060852Sdarrenr	if (natd->nat_hm != NULL)
102160852Sdarrenr		nat_hostmapdel(natd->nat_hm);
102260852Sdarrenr
102353642Sguido	/*
102453642Sguido	 * If there is an active reference from the nat entry to its parent
102553642Sguido	 * rule, decrement the rule's reference count and free it too if no
102653642Sguido	 * longer being used.
102753642Sguido	 */
102853642Sguido	ipn = natd->nat_ptr;
102953642Sguido	if (ipn != NULL) {
103053642Sguido		ipn->in_space++;
103153642Sguido		ipn->in_use--;
103253642Sguido		if (!ipn->in_use && (ipn->in_flags & IPN_DELETE)) {
103353642Sguido			if (ipn->in_apr)
103453642Sguido				appr_free(ipn->in_apr);
103553642Sguido			KFREE(ipn);
103653642Sguido			nat_stats.ns_rules--;
103753642Sguido		}
103853642Sguido	}
103953642Sguido
104060852Sdarrenr	MUTEX_DESTROY(&natd->nat_lock);
104153642Sguido	/*
104253642Sguido	 * If there's a fragment table entry too for this nat entry, then
104353642Sguido	 * dereference that as well.
104453642Sguido	 */
104553642Sguido	ipfr_forget((void *)natd);
104653642Sguido	aps_free(natd->nat_aps);
104753642Sguido	nat_stats.ns_inuse--;
104853642Sguido	KFREE(natd);
104953642Sguido}
105053642Sguido
105153642Sguido
105253642Sguido/*
105353642Sguido * nat_flushtable - clear the NAT table of all mapping entries.
105453642Sguido */
105553642Sguidostatic int nat_flushtable()
105653642Sguido{
105753642Sguido	register nat_t *nat, **natp;
105853642Sguido	register int j = 0;
105967614Sdarrenr
106053642Sguido	/*
106153642Sguido	 * ALL NAT mappings deleted, so lets just make the deletions
106253642Sguido	 * quicker.
106353642Sguido	 */
106453642Sguido	if (nat_table[0] != NULL)
106553642Sguido		bzero((char *)nat_table[0],
106653642Sguido		      sizeof(nat_table[0]) * ipf_nattable_sz);
106753642Sguido	if (nat_table[1] != NULL)
106853642Sguido		bzero((char *)nat_table[1],
106953642Sguido		      sizeof(nat_table[1]) * ipf_nattable_sz);
107053642Sguido
107153642Sguido	for (natp = &nat_instances; (nat = *natp); ) {
107253642Sguido		*natp = nat->nat_next;
107353642Sguido		nat_delete(nat);
107453642Sguido		j++;
107553642Sguido	}
107653642Sguido	nat_stats.ns_inuse = 0;
107753642Sguido	return j;
107853642Sguido}
107953642Sguido
108053642Sguido
108153642Sguido/*
108253642Sguido * nat_clearlist - delete all rules in the active NAT mapping list.
108353642Sguido */
108453642Sguidostatic int nat_clearlist()
108553642Sguido{
108653642Sguido	register ipnat_t *n, **np = &nat_list;
108753642Sguido	int i = 0;
108853642Sguido
108953642Sguido	if (nat_rules != NULL)
109053642Sguido		bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz);
109153642Sguido	if (rdr_rules != NULL)
109253642Sguido		bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz);
109353642Sguido
109453642Sguido	while ((n = *np)) {
109553642Sguido		*np = n->in_next;
109653642Sguido		if (!n->in_use) {
109753642Sguido			if (n->in_apr)
109853642Sguido				appr_free(n->in_apr);
109953642Sguido			KFREE(n);
110053642Sguido			nat_stats.ns_rules--;
110153642Sguido		} else {
110253642Sguido			n->in_flags |= IPN_DELETE;
110353642Sguido			n->in_next = NULL;
110453642Sguido		}
110553642Sguido		i++;
110653642Sguido	}
110753642Sguido	nat_masks = 0;
110853642Sguido	rdr_masks = 0;
110953642Sguido	return i;
111053642Sguido}
111153642Sguido
111253642Sguido
111353642Sguido/*
111453642Sguido * Create a new NAT table entry.
111553642Sguido * NOTE: assumes write lock on ipf_nat has been obtained already.
111653642Sguido */
111753642Sguidonat_t *nat_new(np, ip, fin, flags, direction)
111853642Sguidoipnat_t *np;
111953642Sguidoip_t *ip;
112053642Sguidofr_info_t *fin;
112153642Sguidou_int flags;
112253642Sguidoint direction;
112353642Sguido{
112453642Sguido	register u_32_t sum1, sum2, sumd, l;
112553642Sguido	u_short port = 0, sport = 0, dport = 0, nport = 0;
112653642Sguido	struct in_addr in, inb;
112753642Sguido	tcphdr_t *tcp = NULL;
112860852Sdarrenr	hostmap_t *hm = NULL;
112960852Sdarrenr	nat_t *nat, *natl;
113053642Sguido	u_short nflags;
113155929Sguido#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6)
113255929Sguido	qif_t *qf = fin->fin_qif;
113355929Sguido#endif
113453642Sguido
113553642Sguido	nflags = flags & np->in_flags;
113653642Sguido	if (flags & IPN_TCPUDP) {
113753642Sguido		tcp = (tcphdr_t *)fin->fin_dp;
113853642Sguido		sport = tcp->th_sport;
113953642Sguido		dport = tcp->th_dport;
114053642Sguido	}
114153642Sguido
114253642Sguido	/* Give me a new nat */
114353642Sguido	KMALLOC(nat, nat_t *);
114460852Sdarrenr	if (nat == NULL) {
114560852Sdarrenr		nat_stats.ns_memfail++;
114653642Sguido		return NULL;
114760852Sdarrenr	}
114853642Sguido
114953642Sguido	bzero((char *)nat, sizeof(*nat));
115053642Sguido	nat->nat_flags = flags;
115167614Sdarrenr	if (flags & FI_WILDP)
115267614Sdarrenr		nat_wilds++;
115353642Sguido	/*
115453642Sguido	 * Search the current table for a match.
115553642Sguido	 */
115653642Sguido	if (direction == NAT_OUTBOUND) {
115753642Sguido		/*
115853642Sguido		 * Values at which the search for a free resouce starts.
115953642Sguido		 */
116053642Sguido		u_32_t st_ip;
116153642Sguido		u_short st_port;
116253642Sguido
116353642Sguido		/*
116453642Sguido		 * If it's an outbound packet which doesn't match any existing
116553642Sguido		 * record, then create a new port
116653642Sguido		 */
116753642Sguido		l = 0;
116853642Sguido		st_ip = np->in_nip;
116953642Sguido		st_port = np->in_pnext;
117053642Sguido
117153642Sguido		do {
117253642Sguido			port = 0;
117360852Sdarrenr			in.s_addr = htonl(np->in_nip);
117453642Sguido			if (l == 0) {
117557096Sguido				/*
117657096Sguido				 * Check to see if there is an existing NAT
117757096Sguido				 * setup for this IP address pair.
117857096Sguido				 */
117960852Sdarrenr				hm = nat_hostmap(np, ip->ip_src, in);
118060852Sdarrenr				if (hm != NULL)
118160852Sdarrenr					in.s_addr = hm->hm_mapip.s_addr;
118260852Sdarrenr			} else if ((l == 1) && (hm != NULL)) {
118360852Sdarrenr				nat_hostmapdel(hm);
118460852Sdarrenr				hm = NULL;
118553642Sguido			}
118660852Sdarrenr			in.s_addr = ntohl(in.s_addr);
118753642Sguido
118860852Sdarrenr			nat->nat_hm = hm;
118960852Sdarrenr
119053642Sguido			if ((np->in_outmsk == 0xffffffff) &&
119153642Sguido			    (np->in_pnext == 0)) {
119260852Sdarrenr				if (l > 0)
119360852Sdarrenr					goto badnat;
119453642Sguido			}
119553642Sguido
119653642Sguido			if (np->in_redir & NAT_MAPBLK) {
119753642Sguido				if ((l >= np->in_ppip) || ((l > 0) &&
119860852Sdarrenr				     !(flags & IPN_TCPUDP)))
119960852Sdarrenr					goto badnat;
120053642Sguido				/*
120153642Sguido				 * map-block - Calculate destination address.
120253642Sguido				 */
120353642Sguido				in.s_addr = ntohl(ip->ip_src.s_addr);
120453642Sguido				in.s_addr &= ntohl(~np->in_inmsk);
120553642Sguido				inb.s_addr = in.s_addr;
120653642Sguido				in.s_addr /= np->in_ippip;
120753642Sguido				in.s_addr &= ntohl(~np->in_outmsk);
120853642Sguido				in.s_addr += ntohl(np->in_outip);
120953642Sguido				/*
121053642Sguido				 * Calculate destination port.
121153642Sguido				 */
121253642Sguido				if ((flags & IPN_TCPUDP) &&
121353642Sguido				    (np->in_ppip != 0)) {
121453642Sguido					port = ntohs(sport) + l;
121553642Sguido					port %= np->in_ppip;
121653642Sguido					port += np->in_ppip *
121753642Sguido						(inb.s_addr % np->in_ippip);
121853642Sguido					port += MAPBLK_MINPORT;
121953642Sguido					port = htons(port);
122053642Sguido				}
122160852Sdarrenr			} else if (!np->in_outip &&
122253642Sguido				   (np->in_outmsk == 0xffffffff)) {
122353642Sguido				/*
122453642Sguido				 * 0/32 - use the interface's IP address.
122553642Sguido				 */
122653642Sguido				if ((l > 0) ||
122760852Sdarrenr				    fr_ifpaddr(4, fin->fin_ifp, &in) == -1)
122860852Sdarrenr					goto badnat;
122955929Sguido				in.s_addr = ntohl(in.s_addr);
123060852Sdarrenr			} else if (!np->in_outip && !np->in_outmsk) {
123153642Sguido				/*
123253642Sguido				 * 0/0 - use the original source address/port.
123353642Sguido				 */
123460852Sdarrenr				if (l > 0)
123560852Sdarrenr					goto badnat;
123653642Sguido				in.s_addr = ntohl(ip->ip_src.s_addr);
123753642Sguido			} else if ((np->in_outmsk != 0xffffffff) &&
123853642Sguido				   (np->in_pnext == 0) &&
123960852Sdarrenr				   ((l > 0) || (hm == NULL)))
124053642Sguido				np->in_nip++;
124153642Sguido			natl = NULL;
124253642Sguido
124353642Sguido			if ((nflags & IPN_TCPUDP) &&
124453642Sguido			    ((np->in_redir & NAT_MAPBLK) == 0) &&
124553642Sguido			    (np->in_flags & IPN_AUTOPORTMAP)) {
124653642Sguido				if ((l > 0) && (l % np->in_ppip == 0)) {
124753642Sguido					if (l > np->in_space) {
124860852Sdarrenr						goto badnat;
124953642Sguido					} else if ((l > np->in_ppip) &&
125053642Sguido						   np->in_outmsk != 0xffffffff)
125153642Sguido						np->in_nip++;
125253642Sguido				}
125353642Sguido				if (np->in_ppip != 0) {
125453642Sguido					port = ntohs(sport);
125553642Sguido					port += (l % np->in_ppip);
125653642Sguido					port %= np->in_ppip;
125753642Sguido					port += np->in_ppip *
125853642Sguido						(ntohl(ip->ip_src.s_addr) %
125953642Sguido						 np->in_ippip);
126053642Sguido					port += MAPBLK_MINPORT;
126153642Sguido					port = htons(port);
126253642Sguido				}
126353642Sguido			} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
126453642Sguido				   (nflags & IPN_TCPUDP) &&
126553642Sguido				   (np->in_pnext != 0)) {
126653642Sguido				port = htons(np->in_pnext++);
126753642Sguido				if (np->in_pnext > ntohs(np->in_pmax)) {
126853642Sguido					np->in_pnext = ntohs(np->in_pmin);
126953642Sguido					if (np->in_outmsk != 0xffffffff)
127053642Sguido						np->in_nip++;
127153642Sguido				}
127253642Sguido			}
127353642Sguido
127460852Sdarrenr			if (np->in_flags & IPN_IPRANGE) {
127560852Sdarrenr				if (np->in_nip > ntohl(np->in_outmsk))
127653642Sguido					np->in_nip = ntohl(np->in_outip);
127753642Sguido			} else {
127853642Sguido				if ((np->in_outmsk != 0xffffffff) &&
127953642Sguido				    ((np->in_nip + 1) & ntohl(np->in_outmsk)) >
128053642Sguido				    ntohl(np->in_outip))
128153642Sguido					np->in_nip = ntohl(np->in_outip) + 1;
128253642Sguido			}
128353642Sguido
128453642Sguido			if (!port && (flags & IPN_TCPUDP))
128553642Sguido				port = sport;
128653642Sguido
128753642Sguido			/*
128853642Sguido			 * Here we do a lookup of the connection as seen from
128953642Sguido			 * the outside.  If an IP# pair already exists, try
129053642Sguido			 * again.  So if you have A->B becomes C->B, you can
129153642Sguido			 * also have D->E become C->E but not D->B causing
129253642Sguido			 * another C->B.  Also take protocol and ports into
129353642Sguido			 * account when determining whether a pre-existing
129453642Sguido			 * NAT setup will cause an external conflict where
129553642Sguido			 * this is appropriate.
129653642Sguido			 */
129753642Sguido			inb.s_addr = htonl(in.s_addr);
129860852Sdarrenr			natl = nat_inlookup(fin->fin_ifp, flags & ~FI_WILDP,
129953642Sguido					    (u_int)ip->ip_p, ip->ip_dst, inb,
130053642Sguido					    (port << 16) | dport);
130153642Sguido
130253642Sguido			/*
130353642Sguido			 * Has the search wrapped around and come back to the
130453642Sguido			 * start ?
130553642Sguido			 */
130653642Sguido			if ((natl != NULL) &&
130753642Sguido			    (np->in_pnext != 0) && (st_port == np->in_pnext) &&
130860852Sdarrenr			    (np->in_nip != 0) && (st_ip == np->in_nip))
130960852Sdarrenr				goto badnat;
131053642Sguido			l++;
131153642Sguido		} while (natl != NULL);
131253642Sguido
131353642Sguido		if (np->in_space > 0)
131453642Sguido			np->in_space--;
131553642Sguido
131653642Sguido		/* Setup the NAT table */
131753642Sguido		nat->nat_inip = ip->ip_src;
131853642Sguido		nat->nat_outip.s_addr = htonl(in.s_addr);
131953642Sguido		nat->nat_oip = ip->ip_dst;
132060852Sdarrenr		if (nat->nat_hm == NULL)
132160852Sdarrenr			nat->nat_hm = nat_hostmap(np, ip->ip_src,
132260852Sdarrenr						  nat->nat_outip);
132353642Sguido
132453642Sguido		sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr)) + ntohs(sport);
132553642Sguido		sum2 = LONG_SUM(in.s_addr) + ntohs(port);
132653642Sguido
132753642Sguido		if (flags & IPN_TCPUDP) {
132853642Sguido			nat->nat_inport = sport;
132953642Sguido			nat->nat_outport = port;	/* sport */
133053642Sguido			nat->nat_oport = dport;
133153642Sguido		}
133253642Sguido	} else {
133353642Sguido		/*
133453642Sguido		 * Otherwise, it's an inbound packet. Most likely, we don't
133553642Sguido		 * want to rewrite source ports and source addresses. Instead,
133653642Sguido		 * we want to rewrite to a fixed internal address and fixed
133753642Sguido		 * internal port.
133853642Sguido		 */
133960852Sdarrenr		if (np->in_flags & IPN_SPLIT) {
134060852Sdarrenr			in.s_addr = np->in_nip;
134160852Sdarrenr			if (np->in_inip == htonl(in.s_addr))
134260852Sdarrenr				np->in_nip = ntohl(np->in_inmsk);
134360852Sdarrenr			else {
134460852Sdarrenr				np->in_nip = ntohl(np->in_inip);
134560852Sdarrenr				if (np->in_flags & IPN_ROUNDR) {
134660852Sdarrenr					nat_delrdr(np);
134760852Sdarrenr					nat_addrdr(np);
134860852Sdarrenr				}
134960852Sdarrenr			}
135060852Sdarrenr		} else {
135160852Sdarrenr			in.s_addr = ntohl(np->in_inip);
135260852Sdarrenr			if (np->in_flags & IPN_ROUNDR) {
135360852Sdarrenr				nat_delrdr(np);
135460852Sdarrenr				nat_addrdr(np);
135560852Sdarrenr			}
135660852Sdarrenr		}
135760852Sdarrenr		if (!np->in_pnext)
135853642Sguido			nport = dport;
135960852Sdarrenr		else {
136060852Sdarrenr			/*
136160852Sdarrenr			 * Whilst not optimized for the case where
136260852Sdarrenr			 * pmin == pmax, the gain is not significant.
136360852Sdarrenr			 */
136460852Sdarrenr			nport = ntohs(dport) - ntohs(np->in_pmin) +
136560852Sdarrenr				ntohs(np->in_pnext);
136660852Sdarrenr			nport = htons(nport);
136760852Sdarrenr		}
136853642Sguido
136953642Sguido		/*
137053642Sguido		 * When the redirect-to address is set to 0.0.0.0, just
137153642Sguido		 * assume a blank `forwarding' of the packet.  We don't
137253642Sguido		 * setup any translation for this either.
137353642Sguido		 */
137460852Sdarrenr		if (in.s_addr == 0) {
137560852Sdarrenr			if (nport == dport)
137660852Sdarrenr				goto badnat;
137760852Sdarrenr			in.s_addr = ntohl(ip->ip_dst.s_addr);
137853642Sguido		}
137953642Sguido
138053642Sguido		nat->nat_inip.s_addr = htonl(in.s_addr);
138153642Sguido		nat->nat_outip = ip->ip_dst;
138253642Sguido		nat->nat_oip = ip->ip_src;
138353642Sguido
138453642Sguido		sum1 = LONG_SUM(ntohl(ip->ip_dst.s_addr)) + ntohs(dport);
138553642Sguido		sum2 = LONG_SUM(in.s_addr) + ntohs(nport);
138653642Sguido
138753642Sguido		if (flags & IPN_TCPUDP) {
138853642Sguido			nat->nat_inport = nport;
138953642Sguido			nat->nat_outport = dport;
139053642Sguido			nat->nat_oport = sport;
139153642Sguido		}
139253642Sguido	}
139353642Sguido
139453642Sguido	CALC_SUMD(sum1, sum2, sumd);
139555929Sguido	nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
139655929Sguido#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6)
139755929Sguido	if ((flags == IPN_TCP) && dohwcksum &&
139855929Sguido	    (qf->qf_ill->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) {
139955929Sguido		if (direction == NAT_OUTBOUND)
140055929Sguido			sum1 = LONG_SUM(ntohl(in.s_addr));
140155929Sguido		else
140255929Sguido			sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr));
140355929Sguido		sum1 += LONG_SUM(ntohl(ip->ip_dst.s_addr));
140455929Sguido		sum1 += 30;
140555929Sguido		sum1 = (sum1 & 0xffff) + (sum1 >> 16);
140655929Sguido		nat->nat_sumd[1] = NAT_HW_CKSUM|(sum1 & 0xffff);
140755929Sguido	} else
140855929Sguido#endif
140955929Sguido		nat->nat_sumd[1] = nat->nat_sumd[0];
141053642Sguido
141153642Sguido	if ((flags & IPN_TCPUDP) && ((sport != port) || (dport != nport))) {
141253642Sguido		if (direction == NAT_OUTBOUND)
141353642Sguido			sum1 = LONG_SUM(ntohl(ip->ip_src.s_addr));
141453642Sguido		else
141553642Sguido			sum1 = LONG_SUM(ntohl(ip->ip_dst.s_addr));
141653642Sguido
141753642Sguido		sum2 = LONG_SUM(in.s_addr);
141853642Sguido
141953642Sguido		CALC_SUMD(sum1, sum2, sumd);
142053642Sguido		nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
142153642Sguido	} else
142255929Sguido		nat->nat_ipsumd = nat->nat_sumd[0];
142353642Sguido
142453642Sguido	in.s_addr = htonl(in.s_addr);
142560852Sdarrenr
142660852Sdarrenr#ifdef  _KERNEL
142760852Sdarrenr	strncpy(nat->nat_ifname, IFNAME(fin->fin_ifp), IFNAMSIZ);
142860852Sdarrenr#endif
142960852Sdarrenr	nat_insert(nat);
143060852Sdarrenr
143153642Sguido	nat->nat_dir = direction;
143253642Sguido	nat->nat_ifp = fin->fin_ifp;
143353642Sguido	nat->nat_ptr = np;
143453642Sguido	nat->nat_p = ip->ip_p;
143553642Sguido	nat->nat_bytes = 0;
143653642Sguido	nat->nat_pkts = 0;
143753642Sguido	nat->nat_fr = fin->fin_fr;
143853642Sguido	if (nat->nat_fr != NULL) {
143960852Sdarrenr		ATOMIC_INC32(nat->nat_fr->fr_ref);
144053642Sguido	}
144153642Sguido	if (direction == NAT_OUTBOUND) {
144253642Sguido		if (flags & IPN_TCPUDP)
144353642Sguido			tcp->th_sport = port;
144453642Sguido	} else {
144553642Sguido		if (flags & IPN_TCPUDP)
144653642Sguido			tcp->th_dport = nport;
144753642Sguido	}
144853642Sguido	np->in_use++;
144953642Sguido	return nat;
145060852Sdarrenrbadnat:
145160852Sdarrenr	nat_stats.ns_badnat++;
145260852Sdarrenr	if ((hm = nat->nat_hm) != NULL)
145360852Sdarrenr		nat_hostmapdel(hm);
145460852Sdarrenr	KFREE(nat);
145560852Sdarrenr	return NULL;
145653642Sguido}
145753642Sguido
145853642Sguido
145960852Sdarrenrvoid	nat_insert(nat)
146060852Sdarrenrnat_t	*nat;
146160852Sdarrenr{
146260852Sdarrenr	nat_t **natp;
146360852Sdarrenr	u_int hv;
146460852Sdarrenr
146560852Sdarrenr	MUTEX_INIT(&nat->nat_lock, "nat entry lock", NULL);
146660852Sdarrenr
146760852Sdarrenr	nat->nat_age = fr_defnatage;
146860852Sdarrenr	nat->nat_ifname[sizeof(nat->nat_ifname) - 1] = '\0';
146960852Sdarrenr	if (nat->nat_ifname[0] !='\0') {
147060852Sdarrenr		nat->nat_ifp = GETUNIT(nat->nat_ifname, 4);
147160852Sdarrenr	}
147260852Sdarrenr
147360852Sdarrenr	nat->nat_next = nat_instances;
147460852Sdarrenr	nat_instances = nat;
147567614Sdarrenr
147660852Sdarrenr	hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport,
147760852Sdarrenr			 ipf_nattable_sz);
147860852Sdarrenr	natp = &nat_table[0][hv];
147967614Sdarrenr	if (*natp)
148067614Sdarrenr		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
148167614Sdarrenr	nat->nat_phnext[0] = natp;
148260852Sdarrenr	nat->nat_hnext[0] = *natp;
148360852Sdarrenr	*natp = nat;
148467614Sdarrenr
148560852Sdarrenr	hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport,
148660852Sdarrenr			 ipf_nattable_sz);
148760852Sdarrenr	natp = &nat_table[1][hv];
148867614Sdarrenr	if (*natp)
148967614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
149067614Sdarrenr	nat->nat_phnext[1] = natp;
149160852Sdarrenr	nat->nat_hnext[1] = *natp;
149260852Sdarrenr	*natp = nat;
149360852Sdarrenr
149460852Sdarrenr	nat_stats.ns_added++;
149560852Sdarrenr	nat_stats.ns_inuse++;
149660852Sdarrenr}
149760852Sdarrenr
149860852Sdarrenr
149960852Sdarrenrnat_t *nat_icmplookup(ip, fin, dir)
150053642Sguidoip_t *ip;
150153642Sguidofr_info_t *fin;
150260852Sdarrenrint dir;
150353642Sguido{
150453642Sguido	icmphdr_t *icmp;
150553642Sguido	tcphdr_t *tcp = NULL;
150653642Sguido	ip_t *oip;
150764580Sdarrenr	int flags = 0, type, minlen;
150853642Sguido
150953642Sguido	icmp = (icmphdr_t *)fin->fin_dp;
151053642Sguido	/*
151153642Sguido	 * Does it at least have the return (basic) IP header ?
151253642Sguido	 * Only a basic IP header (no options) should be with an ICMP error
151353642Sguido	 * header.
151453642Sguido	 */
151553642Sguido	if ((ip->ip_hl != 5) || (ip->ip_len < ICMPERR_MINPKTLEN))
151653642Sguido		return NULL;
151753642Sguido	type = icmp->icmp_type;
151853642Sguido	/*
151953642Sguido	 * If it's not an error type, then return.
152053642Sguido	 */
152153642Sguido	if ((type != ICMP_UNREACH) && (type != ICMP_SOURCEQUENCH) &&
152253642Sguido	    (type != ICMP_REDIRECT) && (type != ICMP_TIMXCEED) &&
152353642Sguido	    (type != ICMP_PARAMPROB))
152453642Sguido		return NULL;
152553642Sguido
152653642Sguido	oip = (ip_t *)((char *)fin->fin_dp + 8);
152764580Sdarrenr	minlen = (oip->ip_hl << 2);
152864580Sdarrenr	if (minlen < sizeof(ip_t))
152953642Sguido		return NULL;
153064580Sdarrenr	if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen)
153164580Sdarrenr		return NULL;
153264580Sdarrenr	/*
153364580Sdarrenr	 * Is the buffer big enough for all of it ?  It's the size of the IP
153464580Sdarrenr	 * header claimed in the encapsulated part which is of concern.  It
153564580Sdarrenr	 * may be too big to be in this buffer but not so big that it's
153664580Sdarrenr	 * outside the ICMP packet, leading to TCP deref's causing problems.
153764580Sdarrenr	 * This is possible because we don't know how big oip_hl is when we
153864580Sdarrenr	 * do the pullup early in fr_check() and thus can't gaurantee it is
153964580Sdarrenr	 * all here now.
154064580Sdarrenr	 */
154164580Sdarrenr#ifdef  _KERNEL
154264580Sdarrenr	{
154364580Sdarrenr	mb_t *m;
154464580Sdarrenr
154564580Sdarrenr# if SOLARIS
154664580Sdarrenr	m = fin->fin_qfm;
154764580Sdarrenr	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr)
154864580Sdarrenr		return NULL;
154964580Sdarrenr# else
155064580Sdarrenr	m = *(mb_t **)fin->fin_mp;
155164580Sdarrenr	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
155264580Sdarrenr	    (char *)ip + m->m_len)
155364580Sdarrenr		return NULL;
155464580Sdarrenr# endif
155564580Sdarrenr	}
155664580Sdarrenr#endif
155764580Sdarrenr
155853642Sguido	if (oip->ip_p == IPPROTO_TCP)
155953642Sguido		flags = IPN_TCP;
156053642Sguido	else if (oip->ip_p == IPPROTO_UDP)
156153642Sguido		flags = IPN_UDP;
156253642Sguido	if (flags & IPN_TCPUDP) {
156364580Sdarrenr		minlen += 8;		/* + 64bits of data to get ports */
156464580Sdarrenr		if (ip->ip_len < ICMPERR_IPICMPHLEN + minlen)
156564580Sdarrenr			return NULL;
156653642Sguido		tcp = (tcphdr_t *)((char *)oip + (oip->ip_hl << 2));
156760852Sdarrenr		if (dir == NAT_INBOUND)
156860852Sdarrenr			return nat_inlookup(fin->fin_ifp, flags,
156960852Sdarrenr				(u_int)oip->ip_p, oip->ip_dst, oip->ip_src,
157060852Sdarrenr				(tcp->th_sport << 16) | tcp->th_dport);
157160852Sdarrenr		else
157260852Sdarrenr			return nat_outlookup(fin->fin_ifp, flags,
157360852Sdarrenr				(u_int)oip->ip_p, oip->ip_dst, oip->ip_src,
157460852Sdarrenr				(tcp->th_sport << 16) | tcp->th_dport);
157553642Sguido	}
157660852Sdarrenr	if (dir == NAT_INBOUND)
157760852Sdarrenr		return nat_inlookup(fin->fin_ifp, 0, (u_int)oip->ip_p,
157860852Sdarrenr			oip->ip_dst, oip->ip_src, 0);
157960852Sdarrenr	else
158060852Sdarrenr		return nat_outlookup(fin->fin_ifp, 0, (u_int)oip->ip_p,
158160852Sdarrenr			oip->ip_dst, oip->ip_src, 0);
158253642Sguido}
158353642Sguido
158453642Sguido
158553642Sguido/*
158653642Sguido * This should *ONLY* be used for incoming packets to make sure a NAT'd ICMP
158753642Sguido * packet gets correctly recognised.
158853642Sguido */
158960852Sdarrenrnat_t *nat_icmp(ip, fin, nflags, dir)
159053642Sguidoip_t *ip;
159153642Sguidofr_info_t *fin;
159253642Sguidou_int *nflags;
159360852Sdarrenrint dir;
159453642Sguido{
159553642Sguido	u_32_t sum1, sum2, sumd;
159653642Sguido	struct in_addr in;
159753642Sguido	icmphdr_t *icmp;
159867614Sdarrenr	udphdr_t *udp;
159953642Sguido	nat_t *nat;
160053642Sguido	ip_t *oip;
160153642Sguido	int flags = 0;
160253642Sguido
160363523Sdarrenr	if ((fin->fin_fi.fi_fl & FI_SHORT) || (ip->ip_off & IP_OFFMASK))
160463523Sdarrenr		return NULL;
160567614Sdarrenr	/*
160667614Sdarrenr	 * nat_icmplookup() will return NULL for `defective' packets.
160767614Sdarrenr	 */
160860852Sdarrenr	if ((ip->ip_v != 4) || !(nat = nat_icmplookup(ip, fin, dir)))
160953642Sguido		return NULL;
161053642Sguido	*nflags = IPN_ICMPERR;
161153642Sguido	icmp = (icmphdr_t *)fin->fin_dp;
161253642Sguido	oip = (ip_t *)&icmp->icmp_ip;
161353642Sguido	if (oip->ip_p == IPPROTO_TCP)
161453642Sguido		flags = IPN_TCP;
161553642Sguido	else if (oip->ip_p == IPPROTO_UDP)
161653642Sguido		flags = IPN_UDP;
161767614Sdarrenr	udp = (udphdr_t *)((((char *)oip) + (oip->ip_hl << 2)));
161853642Sguido	/*
161953642Sguido	 * Need to adjust ICMP header to include the real IP#'s and
162053642Sguido	 * port #'s.  Only apply a checksum change relative to the
162167614Sdarrenr	 * IP address change as it will be modified again in ip_natout
162253642Sguido	 * for both address and port.  Two checksum changes are
162353642Sguido	 * necessary for the two header address changes.  Be careful
162453642Sguido	 * to only modify the checksum once for the port # and twice
162553642Sguido	 * for the IP#.
162653642Sguido	 */
162760852Sdarrenr
162867614Sdarrenr	/*
162967614Sdarrenr	 * Step 1
163067614Sdarrenr	 * Fix the IP addresses in the offending IP packet. You also need
163167614Sdarrenr	 * to adjust the IP header checksum of that offending IP packet
163267614Sdarrenr	 * and the ICMP checksum of the ICMP error message itself.
163367614Sdarrenr	 *
163467614Sdarrenr	 * Unfortunately, for UDP and TCP, the IP addresses are also contained
163567614Sdarrenr	 * in the pseudo header that is used to compute the UDP resp. TCP
163667614Sdarrenr	 * checksum. So, we must compensate that as well. Even worse, the
163767614Sdarrenr	 * change in the UDP and TCP checksums require yet another
163867614Sdarrenr	 * adjustment of the ICMP checksum of the ICMP error message.
163967614Sdarrenr	 *
164067614Sdarrenr	 * For the moment we forget about TCP, because that checksum is not
164167614Sdarrenr	 * in the first 8 bytes, so it will not be available in most cases.
164267614Sdarrenr	 */
164367614Sdarrenr
164453642Sguido	if (nat->nat_dir == NAT_OUTBOUND) {
164553642Sguido		sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr));
164653642Sguido		in = nat->nat_inip;
164753642Sguido		oip->ip_src = in;
164853642Sguido	} else {
164953642Sguido		sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr));
165053642Sguido		in = nat->nat_outip;
165153642Sguido		oip->ip_dst = in;
165253642Sguido	}
165353642Sguido
165453642Sguido	sum2 = LONG_SUM(ntohl(in.s_addr));
165553642Sguido
165653642Sguido	CALC_SUMD(sum1, sum2, sumd);
165753642Sguido
165853642Sguido	if (nat->nat_dir == NAT_OUTBOUND) {
165967614Sdarrenr		/*
166067614Sdarrenr		 * Fix IP checksum of the offending IP packet to adjust for
166167614Sdarrenr		 * the change in the IP address.
166267614Sdarrenr		 *
166367614Sdarrenr		 * Normally, you would expect that the ICMP checksum of the
166467614Sdarrenr		 * ICMP error message needs to be adjusted as well for the
166567614Sdarrenr		 * IP address change in oip.
166667614Sdarrenr		 * However, this is a NOP, because the ICMP checksum is
166767614Sdarrenr		 * calculated over the complete ICMP packet, which includes the
166867614Sdarrenr		 * changed oip IP addresses and oip->ip_sum. However, these
166967614Sdarrenr		 * two changes cancel each other out (if the delta for
167067614Sdarrenr		 * the IP address is x, then the delta for ip_sum is minus x),
167167614Sdarrenr		 * so no change in the icmp_cksum is necessary.
167267614Sdarrenr		 *
167367614Sdarrenr		 * Be careful that nat_dir refers to the direction of the
167467614Sdarrenr		 * offending IP packet (oip), not to its ICMP response (icmp)
167567614Sdarrenr		 */
167667614Sdarrenr		fix_datacksum(&oip->ip_sum, sumd);
167753642Sguido
167867614Sdarrenr		/*
167967614Sdarrenr		 * Fix UDP pseudo header checksum to compensate for the
168067614Sdarrenr		 * IP address change.
168167614Sdarrenr		 */
168267614Sdarrenr		if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) {
168367614Sdarrenr			/*
168467614Sdarrenr			 * The UDP checksum is optional, only adjust it
168567614Sdarrenr			 * if it has been set.
168667614Sdarrenr			 */
168767614Sdarrenr			sum1 = ntohs(udp->uh_sum);
168867614Sdarrenr			fix_datacksum(&udp->uh_sum, sumd);
168967614Sdarrenr			sum2 = ntohs(udp->uh_sum);
169067614Sdarrenr
169167614Sdarrenr			/*
169267614Sdarrenr			 * Fix ICMP checksum to compensate the UDP
169367614Sdarrenr			 * checksum adjustment.
169467614Sdarrenr			 */
169567614Sdarrenr			CALC_SUMD(sum1, sum2, sumd);
169667614Sdarrenr			fix_outcksum(&icmp->icmp_cksum, sumd);
169767614Sdarrenr		}
169867614Sdarrenr
169967614Sdarrenr#if 0
170067614Sdarrenr		/*
170167614Sdarrenr		 * Fix TCP pseudo header checksum to compensate for the
170267614Sdarrenr		 * IP address change. Before we can do the change, we
170367614Sdarrenr		 * must make sure that oip is sufficient large to hold
170467614Sdarrenr		 * the TCP checksum (normally it does not!).
170567614Sdarrenr		 */
170667614Sdarrenr		if (oip->ip_p == IPPROTO_TCP) {
170767614Sdarrenr
170867614Sdarrenr		}
170967614Sdarrenr#endif
171053642Sguido	} else {
171167614Sdarrenr
171267614Sdarrenr		/*
171367614Sdarrenr		 * Fix IP checksum of the offending IP packet to adjust for
171467614Sdarrenr		 * the change in the IP address.
171567614Sdarrenr		 *
171667614Sdarrenr		 * Normally, you would expect that the ICMP checksum of the
171767614Sdarrenr		 * ICMP error message needs to be adjusted as well for the
171867614Sdarrenr		 * IP address change in oip.
171967614Sdarrenr		 * However, this is a NOP, because the ICMP checksum is
172067614Sdarrenr		 * calculated over the complete ICMP packet, which includes the
172167614Sdarrenr		 * changed oip IP addresses and oip->ip_sum. However, these
172267614Sdarrenr		 * two changes cancel each other out (if the delta for
172367614Sdarrenr		 * the IP address is x, then the delta for ip_sum is minus x),
172467614Sdarrenr		 * so no change in the icmp_cksum is necessary.
172567614Sdarrenr		 *
172667614Sdarrenr		 * Be careful that nat_dir refers to the direction of the
172767614Sdarrenr		 * offending IP packet (oip), not to its ICMP response (icmp)
172867614Sdarrenr		 */
172967614Sdarrenr		fix_datacksum(&oip->ip_sum, sumd);
173067614Sdarrenr
173167614Sdarrenr/* XXX FV : without having looked at Solaris source code, it seems unlikely
173267614Sdarrenr * that SOLARIS would compensate this in the kernel (a body of an IP packet
173367614Sdarrenr * in the data section of an ICMP packet). I have the feeling that this should
173467614Sdarrenr * be unconditional, but I'm not in a position to check.
173567614Sdarrenr */
173663523Sdarrenr#if !SOLARIS && !defined(__sgi)
173767614Sdarrenr		/*
173867614Sdarrenr		 * Fix UDP pseudo header checksum to compensate for the
173967614Sdarrenr		 * IP address change.
174067614Sdarrenr		 */
174167614Sdarrenr		if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) {
174267614Sdarrenr			/*
174367614Sdarrenr			 * The UDP checksum is optional, only adjust it
174467614Sdarrenr			 * if it has been set
174567614Sdarrenr			 */
174667614Sdarrenr			sum1 = ntohs(udp->uh_sum);
174767614Sdarrenr			fix_datacksum(&udp->uh_sum, sumd);
174867614Sdarrenr			sum2 = ntohs(udp->uh_sum);
174967614Sdarrenr
175067614Sdarrenr			/*
175167614Sdarrenr			 * Fix ICMP checksum to compensate the UDP
175267614Sdarrenr			 * checksum adjustment.
175367614Sdarrenr			 */
175467614Sdarrenr			CALC_SUMD(sum1, sum2, sumd);
175567614Sdarrenr			fix_incksum(&icmp->icmp_cksum, sumd);
175667614Sdarrenr		}
175767614Sdarrenr
175867614Sdarrenr#if 0
175967614Sdarrenr		/*
176067614Sdarrenr		 * Fix TCP pseudo header checksum to compensate for the
176167614Sdarrenr		 * IP address change. Before we can do the change, we
176267614Sdarrenr		 * must make sure that oip is sufficient large to hold
176367614Sdarrenr		 * the TCP checksum (normally it does not!).
176467614Sdarrenr		 */
176567614Sdarrenr		if (oip->ip_p == IPPROTO_TCP) {
176667614Sdarrenr
176767614Sdarrenr		};
176863523Sdarrenr#endif
176967614Sdarrenr
177067614Sdarrenr#endif
177153642Sguido	}
177253642Sguido
177353642Sguido	if ((flags & IPN_TCPUDP) != 0) {
177453642Sguido		tcphdr_t *tcp;
177553642Sguido
177664580Sdarrenr		/*
177764580Sdarrenr		 * XXX - what if this is bogus hl and we go off the end ?
177864580Sdarrenr		 * In this case, nat_icmpinlookup() will have returned NULL.
177964580Sdarrenr		 */
178067614Sdarrenr		tcp = (tcphdr_t *)udp;
178153642Sguido
178267614Sdarrenr		/*
178367614Sdarrenr		 * Step 2 :
178467614Sdarrenr		 * For offending TCP/UDP IP packets, translate the ports as
178567614Sdarrenr		 * well, based on the NAT specification. Of course such
178667614Sdarrenr		 * a change must be reflected in the ICMP checksum as well.
178767614Sdarrenr		 *
178867614Sdarrenr		 * Advance notice : Now it becomes complicated :-)
178967614Sdarrenr		 *
179067614Sdarrenr		 * Since the port fields are part of the TCP/UDP checksum
179167614Sdarrenr		 * of the offending IP packet, you need to adjust that checksum
179267614Sdarrenr		 * as well... but, if you change, you must change the icmp
179367614Sdarrenr		 * checksum *again*, to reflect that change.
179467614Sdarrenr		 *
179567614Sdarrenr		 * To further complicate: the TCP checksum is not in the first
179667614Sdarrenr		 * 8 bytes of the offending ip packet, so it most likely is not
179767614Sdarrenr		 * available (we might have to fix that if the encounter a
179867614Sdarrenr		 * device that returns more than 8 data bytes on icmp error)
179967614Sdarrenr		 */
180067614Sdarrenr
180153642Sguido		if (nat->nat_dir == NAT_OUTBOUND) {
180253642Sguido			if (tcp->th_sport != nat->nat_inport) {
180367614Sdarrenr				/*
180467614Sdarrenr				 * Fix ICMP checksum to compensate port
180567614Sdarrenr				 * adjustment.
180667614Sdarrenr				 */
180753642Sguido				sum1 = ntohs(tcp->th_sport);
180853642Sguido				sum2 = ntohs(nat->nat_inport);
180953642Sguido				CALC_SUMD(sum1, sum2, sumd);
181053642Sguido				tcp->th_sport = nat->nat_inport;
181167614Sdarrenr				fix_outcksum(&icmp->icmp_cksum, sumd);
181267614Sdarrenr
181367614Sdarrenr				/*
181467614Sdarrenr				 * Fix udp checksum to compensate port
181567614Sdarrenr				 * adjustment.  NOTE : the offending IP packet
181667614Sdarrenr				 * flows the other direction compared to the
181767614Sdarrenr				 * ICMP message.
181867614Sdarrenr				 *
181967614Sdarrenr				 * The UDP checksum is optional, only adjust
182067614Sdarrenr				 * it if it has been set.
182167614Sdarrenr				 */
182267614Sdarrenr				if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) {
182367614Sdarrenr
182467614Sdarrenr					sum1 = ntohs(udp->uh_sum);
182567614Sdarrenr					fix_datacksum(&udp->uh_sum, sumd);
182667614Sdarrenr					sum2 = ntohs(udp->uh_sum);
182767614Sdarrenr
182867614Sdarrenr					/*
182967614Sdarrenr					 * Fix ICMP checksum to
183067614Sdarrenr					 * compensate UDP checksum
183167614Sdarrenr					 * adjustment.
183267614Sdarrenr					 */
183367614Sdarrenr					CALC_SUMD(sum1, sum2, sumd);
183467614Sdarrenr					fix_outcksum(&icmp->icmp_cksum, sumd);
183567614Sdarrenr				}
183653642Sguido			}
183753642Sguido		} else {
183867614Sdarrenr
183953642Sguido			if (tcp->th_dport != nat->nat_outport) {
184067614Sdarrenr				/*
184167614Sdarrenr				 * Fix ICMP checksum to compensate port
184267614Sdarrenr				 * adjustment.
184367614Sdarrenr				 */
184453642Sguido				sum1 = ntohs(tcp->th_dport);
184553642Sguido				sum2 = ntohs(nat->nat_outport);
184653642Sguido				CALC_SUMD(sum1, sum2, sumd);
184753642Sguido				tcp->th_dport = nat->nat_outport;
184867614Sdarrenr				fix_incksum(&icmp->icmp_cksum, sumd);
184967614Sdarrenr
185067614Sdarrenr				/*
185167614Sdarrenr				 * Fix udp checksum to compensate port
185267614Sdarrenr				 * adjustment.   NOTE : the offending IP
185367614Sdarrenr				 * packet flows the other direction compared
185467614Sdarrenr				 * to the ICMP message.
185567614Sdarrenr				 *
185667614Sdarrenr				 * The UDP checksum is optional, only adjust
185767614Sdarrenr				 * it if it has been set.
185867614Sdarrenr				 */
185967614Sdarrenr				if (oip->ip_p == IPPROTO_UDP && udp->uh_sum) {
186067614Sdarrenr
186167614Sdarrenr					sum1 = ntohs(udp->uh_sum);
186267614Sdarrenr					fix_datacksum(&udp->uh_sum, sumd);
186367614Sdarrenr					sum2 = ntohs(udp->uh_sum);
186467614Sdarrenr
186567614Sdarrenr					/*
186667614Sdarrenr					 * Fix ICMP checksum to compensate
186767614Sdarrenr					 * UDP checksum adjustment.
186867614Sdarrenr					 */
186967614Sdarrenr					CALC_SUMD(sum1, sum2, sumd);
187067614Sdarrenr					fix_incksum(&icmp->icmp_cksum, sumd);
187167614Sdarrenr				}
187253642Sguido			}
187353642Sguido		}
187453642Sguido	}
187553642Sguido	nat->nat_age = fr_defnaticmpage;
187653642Sguido	return nat;
187753642Sguido}
187853642Sguido
187953642Sguido
188053642Sguido/*
188153642Sguido * NB: these lookups don't lock access to the list, it assume it has already
188253642Sguido * been done!
188353642Sguido */
188453642Sguido/*
188553642Sguido * Lookup a nat entry based on the mapped destination ip address/port and
188653642Sguido * real source address/port.  We use this lookup when receiving a packet,
188753642Sguido * we're looking for a table entry, based on the destination address.
188853642Sguido * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.
188953642Sguido */
189053642Sguidonat_t *nat_inlookup(ifp, flags, p, src, mapdst, ports)
189153642Sguidovoid *ifp;
189253642Sguidoregister u_int flags, p;
189353642Sguidostruct in_addr src , mapdst;
189453642Sguidou_32_t ports;
189553642Sguido{
189667614Sdarrenr	register u_short sport, dport;
189753642Sguido	register nat_t *nat;
189853642Sguido	register int nflags;
189967614Sdarrenr	register u_32_t dst;
190053642Sguido	u_int hv;
190153642Sguido
190267614Sdarrenr	dst = mapdst.s_addr;
190367614Sdarrenr	dport = ports >> 16;
190453642Sguido	sport = ports & 0xffff;
190553642Sguido	flags &= IPN_TCPUDP;
190653642Sguido
190767614Sdarrenr	hv = NAT_HASH_FN(dst, dport, ipf_nattable_sz);
190853642Sguido	nat = nat_table[1][hv];
190953642Sguido	for (; nat; nat = nat->nat_hnext[1]) {
191053642Sguido		nflags = nat->nat_flags;
191153642Sguido		if ((!ifp || ifp == nat->nat_ifp) &&
191253642Sguido		    nat->nat_oip.s_addr == src.s_addr &&
191367614Sdarrenr		    nat->nat_outip.s_addr == dst &&
191453642Sguido		    (((p == 0) && (flags == (nat->nat_flags & IPN_TCPUDP)))
191553642Sguido		     || (p == nat->nat_p)) && (!flags ||
191653642Sguido		     (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) &&
191767614Sdarrenr		      ((nat->nat_outport == dport) || (nflags & FI_W_SPORT)))))
191853642Sguido			return nat;
191953642Sguido	}
192067614Sdarrenr	if (!nat_wilds || !(flags & IPN_TCPUDP))
192167614Sdarrenr		return NULL;
192267614Sdarrenr	RWLOCK_EXIT(&ipf_nat);
192367614Sdarrenr	hv = NAT_HASH_FN(dst, 0, ipf_nattable_sz);
192467614Sdarrenr	WRITE_ENTER(&ipf_nat);
192567614Sdarrenr	nat = nat_table[1][hv];
192667614Sdarrenr	for (; nat; nat = nat->nat_hnext[1]) {
192767614Sdarrenr		nflags = nat->nat_flags;
192867614Sdarrenr		if (ifp && ifp != nat->nat_ifp)
192967614Sdarrenr			continue;
193067614Sdarrenr		if (!(nflags & IPN_TCPUDP))
193167614Sdarrenr			continue;
193267614Sdarrenr		if (!(nflags & FI_WILDP))
193367614Sdarrenr			continue;
193467614Sdarrenr		if (nat->nat_oip.s_addr != src.s_addr ||
193567614Sdarrenr		    nat->nat_outip.s_addr != dst)
193667614Sdarrenr			continue;
193767614Sdarrenr		if (((nat->nat_oport == sport) || (nflags & FI_W_DPORT)) &&
193867614Sdarrenr		    ((nat->nat_outport == dport) || (nflags & FI_W_SPORT))) {
193967614Sdarrenr			hv = NAT_HASH_FN(dst, dport, ipf_nattable_sz);
194067614Sdarrenr			nat_tabmove(nat, hv);
194167614Sdarrenr			break;
194267614Sdarrenr		}
194367614Sdarrenr	}
194467614Sdarrenr	MUTEX_DOWNGRADE(&ipf_nat);
194567614Sdarrenr	return nat;
194653642Sguido}
194753642Sguido
194853642Sguido
194967614Sdarrenrstatic void nat_tabmove(nat, hv)
195067614Sdarrenrnat_t *nat;
195167614Sdarrenru_int hv;
195267614Sdarrenr{
195367614Sdarrenr	nat_t **natp;
195467614Sdarrenr
195567614Sdarrenr	/*
195667614Sdarrenr	 * Remove the NAT entry from the old location
195767614Sdarrenr	 */
195867614Sdarrenr	if (nat->nat_hnext[0])
195967614Sdarrenr		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
196067614Sdarrenr	*nat->nat_phnext[0] = nat->nat_hnext[0];
196167614Sdarrenr
196267614Sdarrenr	if (nat->nat_hnext[1])
196367614Sdarrenr		nat->nat_hnext[0]->nat_phnext[1] = nat->nat_phnext[1];
196467614Sdarrenr	*nat->nat_phnext[1] = nat->nat_hnext[1];
196567614Sdarrenr
196667614Sdarrenr	natp = &nat_table[0][hv];
196767614Sdarrenr	if (*natp)
196867614Sdarrenr		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
196967614Sdarrenr	nat->nat_phnext[0] = natp;
197067614Sdarrenr	nat->nat_hnext[0] = *natp;
197167614Sdarrenr	*natp = nat;
197267614Sdarrenr
197367614Sdarrenr	/*
197467614Sdarrenr	 * Add into the NAT table in the new position
197567614Sdarrenr	 */
197667614Sdarrenr	natp = &nat_table[1][hv];
197767614Sdarrenr	if (*natp)
197867614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
197967614Sdarrenr	nat->nat_phnext[1] = natp;
198067614Sdarrenr	nat->nat_hnext[1] = *natp;
198167614Sdarrenr	*natp = nat;
198267614Sdarrenr}
198367614Sdarrenr
198467614Sdarrenr
198553642Sguido/*
198653642Sguido * Lookup a nat entry based on the source 'real' ip address/port and
198753642Sguido * destination address/port.  We use this lookup when sending a packet out,
198853642Sguido * we're looking for a table entry, based on the source address.
198953642Sguido * NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.
199053642Sguido */
199153642Sguidonat_t *nat_outlookup(ifp, flags, p, src, dst, ports)
199253642Sguidovoid *ifp;
199353642Sguidoregister u_int flags, p;
199453642Sguidostruct in_addr src , dst;
199553642Sguidou_32_t ports;
199653642Sguido{
199753642Sguido	register u_short sport, dport;
199853642Sguido	register nat_t *nat;
199953642Sguido	register int nflags;
200067614Sdarrenr	u_32_t srcip;
200153642Sguido	u_int hv;
200253642Sguido
200353642Sguido	sport = ports & 0xffff;
200453642Sguido	dport = ports >> 16;
200553642Sguido	flags &= IPN_TCPUDP;
200667614Sdarrenr	srcip = src.s_addr;
200753642Sguido
200867614Sdarrenr	hv = NAT_HASH_FN(srcip, sport, ipf_nattable_sz);
200953642Sguido	nat = nat_table[0][hv];
201053642Sguido	for (; nat; nat = nat->nat_hnext[0]) {
201153642Sguido		nflags = nat->nat_flags;
201253642Sguido
201353642Sguido		if ((!ifp || ifp == nat->nat_ifp) &&
201467614Sdarrenr		    nat->nat_inip.s_addr == srcip &&
201553642Sguido		    nat->nat_oip.s_addr == dst.s_addr &&
201653642Sguido		    (((p == 0) && (flags == (nat->nat_flags & IPN_TCPUDP)))
201753642Sguido		     || (p == nat->nat_p)) && (!flags ||
201853642Sguido		     ((nat->nat_inport == sport || nflags & FI_W_SPORT) &&
201953642Sguido		      (nat->nat_oport == dport || nflags & FI_W_DPORT))))
202053642Sguido			return nat;
202153642Sguido	}
202267614Sdarrenr	if (!nat_wilds || !(flags & IPN_TCPUDP))
202367614Sdarrenr		return NULL;
202467614Sdarrenr	RWLOCK_EXIT(&ipf_nat);
202567614Sdarrenr	hv = NAT_HASH_FN(srcip, 0, ipf_nattable_sz);
202667614Sdarrenr	WRITE_ENTER(&ipf_nat);
202767614Sdarrenr	nat = nat_table[0][hv];
202867614Sdarrenr	for (; nat; nat = nat->nat_hnext[0]) {
202967614Sdarrenr		nflags = nat->nat_flags;
203067614Sdarrenr		if (ifp && ifp != nat->nat_ifp)
203167614Sdarrenr			continue;
203267614Sdarrenr		if (!(nflags & IPN_TCPUDP))
203367614Sdarrenr			continue;
203467614Sdarrenr		if (!(nflags & FI_WILDP))
203567614Sdarrenr			continue;
203667614Sdarrenr		if ((nat->nat_inip.s_addr != srcip) ||
203767614Sdarrenr		    (nat->nat_oip.s_addr != dst.s_addr))
203867614Sdarrenr			continue;
203967614Sdarrenr		if (((nat->nat_inport == sport) || (nflags & FI_W_DPORT)) &&
204067614Sdarrenr		    ((nat->nat_oport == dport) || (nflags & FI_W_SPORT))) {
204167614Sdarrenr			hv = NAT_HASH_FN(srcip, sport, ipf_nattable_sz);
204267614Sdarrenr			nat_tabmove(nat, hv);
204367614Sdarrenr			break;
204467614Sdarrenr		}
204567614Sdarrenr	}
204667614Sdarrenr	MUTEX_DOWNGRADE(&ipf_nat);
204767614Sdarrenr	return nat;
204853642Sguido}
204953642Sguido
205053642Sguido
205153642Sguido/*
205253642Sguido * Lookup the NAT tables to search for a matching redirect
205353642Sguido */
205453642Sguidonat_t *nat_lookupredir(np)
205553642Sguidoregister natlookup_t *np;
205653642Sguido{
205753642Sguido	u_32_t ports;
205853642Sguido	nat_t *nat;
205953642Sguido
206053642Sguido	ports = (np->nl_outport << 16) | np->nl_inport;
206153642Sguido	/*
206253642Sguido	 * If nl_inip is non null, this is a lookup based on the real
206353642Sguido	 * ip address. Else, we use the fake.
206453642Sguido	 */
206553642Sguido	if ((nat = nat_outlookup(NULL, np->nl_flags, 0, np->nl_inip,
206653642Sguido				 np->nl_outip, ports))) {
206753642Sguido		np->nl_realip = nat->nat_outip;
206853642Sguido		np->nl_realport = nat->nat_outport;
206953642Sguido	}
207053642Sguido	return nat;
207153642Sguido}
207253642Sguido
207353642Sguido
207460852Sdarrenrstatic int nat_match(fin, np, ip)
207560852Sdarrenrfr_info_t *fin;
207660852Sdarrenripnat_t *np;
207760852Sdarrenrip_t *ip;
207860852Sdarrenr{
207960852Sdarrenr	frtuc_t *ft;
208060852Sdarrenr
208160852Sdarrenr	if (ip->ip_v != 4)
208260852Sdarrenr		return 0;
208360852Sdarrenr
208460852Sdarrenr	if (np->in_p && ip->ip_p != np->in_p)
208560852Sdarrenr		return 0;
208660852Sdarrenr	if (fin->fin_out) {
208763523Sdarrenr		if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK)))
208860852Sdarrenr			return 0;
208963523Sdarrenr		if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip)
209063523Sdarrenr		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
209160852Sdarrenr			return 0;
209263523Sdarrenr		if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip)
209363523Sdarrenr		    ^ ((np->in_flags & IPN_NOTDST) != 0))
209460852Sdarrenr			return 0;
209560852Sdarrenr	} else {
209663523Sdarrenr		if (!(np->in_redir & NAT_REDIRECT))
209760852Sdarrenr			return 0;
209863523Sdarrenr		if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip)
209963523Sdarrenr		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
210063523Sdarrenr			return 0;
210163523Sdarrenr		if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip)
210263523Sdarrenr		    ^ ((np->in_flags & IPN_NOTDST) != 0))
210363523Sdarrenr			return 0;
210460852Sdarrenr	}
210560852Sdarrenr
210660852Sdarrenr	ft = &np->in_tuc;
210763523Sdarrenr	if (!(fin->fin_fi.fi_fl & FI_TCPUDP) ||
210863523Sdarrenr	    (fin->fin_fi.fi_fl & FI_SHORT) || (ip->ip_off & IP_OFFMASK)) {
210960852Sdarrenr		if (ft->ftu_scmp || ft->ftu_dcmp)
211060852Sdarrenr			return 0;
211160852Sdarrenr		return 1;
211260852Sdarrenr	}
211360852Sdarrenr
211460852Sdarrenr	return fr_tcpudpchk(ft, fin);
211560852Sdarrenr}
211660852Sdarrenr
211760852Sdarrenr
211853642Sguido/*
211953642Sguido * Packets going out on the external interface go through this.
212053642Sguido * Here, the source address requires alteration, if anything.
212153642Sguido */
212253642Sguidoint ip_natout(ip, fin)
212353642Sguidoip_t *ip;
212453642Sguidofr_info_t *fin;
212553642Sguido{
212653642Sguido	register ipnat_t *np = NULL;
212753642Sguido	register u_32_t ipa;
212853642Sguido	tcphdr_t *tcp = NULL;
212960852Sdarrenr	u_short sport = 0, dport = 0, *csump = NULL;
213053642Sguido	struct ifnet *ifp;
213153642Sguido	int natadd = 1;
213253642Sguido	frentry_t *fr;
213360852Sdarrenr	u_int nflags = 0, hv, msk;
213453642Sguido	u_32_t iph;
213553642Sguido	nat_t *nat;
213653642Sguido	int i;
213753642Sguido
213860852Sdarrenr	if (nat_list == NULL || (fr_nat_lock))
213953642Sguido		return 0;
214053642Sguido
214153642Sguido	if ((fr = fin->fin_fr) && !(fr->fr_flags & FR_DUP) &&
214253642Sguido	    fr->fr_tif.fd_ifp && fr->fr_tif.fd_ifp != (void *)-1)
214353642Sguido		ifp = fr->fr_tif.fd_ifp;
214453642Sguido	else
214553642Sguido		ifp = fin->fin_ifp;
214653642Sguido
214753642Sguido	if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) {
214853642Sguido		if (ip->ip_p == IPPROTO_TCP)
214953642Sguido			nflags = IPN_TCP;
215053642Sguido		else if (ip->ip_p == IPPROTO_UDP)
215153642Sguido			nflags = IPN_UDP;
215253642Sguido		if ((nflags & IPN_TCPUDP)) {
215353642Sguido			tcp = (tcphdr_t *)fin->fin_dp;
215453642Sguido			sport = tcp->th_sport;
215553642Sguido			dport = tcp->th_dport;
215653642Sguido		}
215753642Sguido	}
215853642Sguido
215953642Sguido	ipa = ip->ip_src.s_addr;
216053642Sguido
216153642Sguido	READ_ENTER(&ipf_nat);
216260852Sdarrenr
216360852Sdarrenr	if ((ip->ip_p == IPPROTO_ICMP) &&
216460852Sdarrenr	    (nat = nat_icmp(ip, fin, &nflags, NAT_OUTBOUND)))
216560852Sdarrenr		;
216660852Sdarrenr	else if ((ip->ip_off & (IP_OFFMASK|IP_MF)) &&
216760852Sdarrenr			(nat = ipfr_nat_knownfrag(ip, fin)))
216853642Sguido		natadd = 0;
216953642Sguido	else if ((nat = nat_outlookup(ifp, nflags, (u_int)ip->ip_p, ip->ip_src,
217053642Sguido				      ip->ip_dst, (dport << 16) | sport))) {
217153642Sguido		nflags = nat->nat_flags;
217253642Sguido		if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) {
217353642Sguido			if ((nflags & FI_W_SPORT) &&
217453642Sguido			    (nat->nat_inport != sport))
217553642Sguido				nat->nat_inport = sport;
217653642Sguido			else if ((nflags & FI_W_DPORT) &&
217753642Sguido				 (nat->nat_oport != dport))
217853642Sguido				nat->nat_oport = dport;
217953642Sguido			if (nat->nat_outport == 0)
218053642Sguido				nat->nat_outport = sport;
218153642Sguido			nat->nat_flags &= ~(FI_W_DPORT|FI_W_SPORT);
218253642Sguido			nflags = nat->nat_flags;
218367614Sdarrenr			nat_wilds--;
218453642Sguido		}
218553642Sguido	} else {
218653642Sguido		RWLOCK_EXIT(&ipf_nat);
218753642Sguido		WRITE_ENTER(&ipf_nat);
218853642Sguido		/*
218953642Sguido		 * If there is no current entry in the nat table for this IP#,
219053642Sguido		 * create one for it (if there is a matching rule).
219153642Sguido		 */
219253642Sguido		msk = 0xffffffff;
219353642Sguido		i = 32;
219453642Sguidomaskloop:
219553642Sguido		iph = ipa & htonl(msk);
219660852Sdarrenr		hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz);
219753642Sguido		for (np = nat_rules[hv]; np; np = np->in_mnext)
219853642Sguido		{
219960852Sdarrenr			if ((np->in_ifp && (np->in_ifp != ifp)) ||
220060852Sdarrenr			    !np->in_space)
220160852Sdarrenr				continue;
220260852Sdarrenr			if ((np->in_flags & IPN_RF) &&
220360852Sdarrenr			    !(np->in_flags & nflags))
220460852Sdarrenr				continue;
220560852Sdarrenr			if (np->in_flags & IPN_FILTER) {
220660852Sdarrenr				if (!nat_match(fin, np, ip))
220760852Sdarrenr					continue;
220860852Sdarrenr			} else if ((ipa & np->in_inmsk) != np->in_inip)
220960852Sdarrenr				continue;
221060852Sdarrenr			if (np->in_redir & (NAT_MAP|NAT_MAPBLK)) {
221153642Sguido				if (*np->in_plabel && !appr_ok(ip, tcp, np))
221253642Sguido					continue;
221353642Sguido				/*
221453642Sguido				 * If it's a redirection, then we don't want to
221553642Sguido				 * create new outgoing port stuff.
221653642Sguido				 * Redirections are only for incoming
221753642Sguido				 * connections.
221853642Sguido				 */
221953642Sguido				if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK)))
222053642Sguido					continue;
222153642Sguido				if ((nat = nat_new(np, ip, fin, (u_int)nflags,
222253642Sguido						    NAT_OUTBOUND))) {
222353642Sguido					np->in_hits++;
222453642Sguido#ifdef	IPFILTER_LOG
222553642Sguido					nat_log(nat, (u_int)np->in_redir);
222653642Sguido#endif
222753642Sguido					break;
222853642Sguido				}
222953642Sguido			}
223053642Sguido		}
223153642Sguido		if ((np == NULL) && (i > 0)) {
223253642Sguido			do {
223353642Sguido				i--;
223453642Sguido				msk <<= 1;
223553642Sguido			} while ((i >= 0) && ((nat_masks & (1 << i)) == 0));
223653642Sguido			if (i >= 0)
223753642Sguido				goto maskloop;
223853642Sguido		}
223953642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
224053642Sguido	}
224153642Sguido
224253642Sguido	if (nat) {
224353642Sguido		np = nat->nat_ptr;
224453642Sguido		if (natadd && fin->fin_fi.fi_fl & FI_FRAG)
224553642Sguido			ipfr_nat_newfrag(ip, fin, 0, nat);
224660852Sdarrenr		MUTEX_ENTER(&nat->nat_lock);
224753642Sguido		nat->nat_age = fr_defnatage;
224853642Sguido		nat->nat_bytes += ip->ip_len;
224953642Sguido		nat->nat_pkts++;
225060852Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
225153642Sguido
225253642Sguido		/*
225353642Sguido		 * Fix up checksums, not by recalculating them, but
225453642Sguido		 * simply computing adjustments.
225553642Sguido		 */
225663523Sdarrenr		if (nflags == IPN_ICMPERR) {
225763523Sdarrenr			u_32_t s1, s2, sumd;
225863523Sdarrenr
225963523Sdarrenr			s1 = LONG_SUM(ntohl(ip->ip_src.s_addr));
226063523Sdarrenr			s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr));
226163523Sdarrenr			CALC_SUMD(s1, s2, sumd);
226263523Sdarrenr
226363523Sdarrenr			if (nat->nat_dir == NAT_OUTBOUND)
226467614Sdarrenr				fix_incksum(&ip->ip_sum, sumd);
226563523Sdarrenr			else
226667614Sdarrenr				fix_outcksum(&ip->ip_sum, sumd);
226763523Sdarrenr		}
226853642Sguido#if SOLARIS || defined(__sgi)
226963523Sdarrenr		else {
227063523Sdarrenr			if (nat->nat_dir == NAT_OUTBOUND)
227167614Sdarrenr				fix_outcksum(&ip->ip_sum, nat->nat_ipsumd);
227263523Sdarrenr			else
227367614Sdarrenr				fix_incksum(&ip->ip_sum, nat->nat_ipsumd);
227463523Sdarrenr		}
227553642Sguido#endif
227663523Sdarrenr		ip->ip_src = nat->nat_outip;
227753642Sguido
227853642Sguido		if (!(ip->ip_off & IP_OFFMASK) &&
227953642Sguido		    !(fin->fin_fi.fi_fl & FI_SHORT)) {
228053642Sguido
228153642Sguido			if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) {
228253642Sguido				tcp->th_sport = nat->nat_outport;
228353642Sguido				fin->fin_data[0] = ntohs(tcp->th_sport);
228453642Sguido			}
228553642Sguido
228653642Sguido			if (ip->ip_p == IPPROTO_TCP) {
228753642Sguido				csump = &tcp->th_sum;
228860852Sdarrenr				MUTEX_ENTER(&nat->nat_lock);
228953642Sguido				fr_tcp_age(&nat->nat_age,
229060852Sdarrenr					   nat->nat_tcpstate, fin, 1);
229153642Sguido				if (nat->nat_age < fr_defnaticmpage)
229253642Sguido					nat->nat_age = fr_defnaticmpage;
229353642Sguido#ifdef LARGE_NAT
229460852Sdarrenr				else if (nat->nat_age > fr_defnatage)
229560852Sdarrenr					nat->nat_age = fr_defnatage;
229653642Sguido#endif
229753642Sguido				/*
229853642Sguido				 * Increase this because we may have
229953642Sguido				 * "keep state" following this too and
230053642Sguido				 * packet storms can occur if this is
230153642Sguido				 * removed too quickly.
230253642Sguido				 */
230353642Sguido				if (nat->nat_age == fr_tcpclosed)
230453642Sguido					nat->nat_age = fr_tcplastack;
230560852Sdarrenr				MUTEX_EXIT(&nat->nat_lock);
230653642Sguido			} else if (ip->ip_p == IPPROTO_UDP) {
230753642Sguido				udphdr_t *udp = (udphdr_t *)tcp;
230853642Sguido
230953642Sguido				if (udp->uh_sum)
231053642Sguido					csump = &udp->uh_sum;
231160852Sdarrenr			} else if (ip->ip_p == IPPROTO_ICMP) {
231260852Sdarrenr				nat->nat_age = fr_defnaticmpage;
231353642Sguido			}
231463523Sdarrenr
231553642Sguido			if (csump) {
231653642Sguido				if (nat->nat_dir == NAT_OUTBOUND)
231767614Sdarrenr					fix_outcksum(csump, nat->nat_sumd[1]);
231853642Sguido				else
231967614Sdarrenr					fix_incksum(csump, nat->nat_sumd[1]);
232053642Sguido			}
232153642Sguido		}
232260852Sdarrenr
232353642Sguido		if ((np->in_apr != NULL) && (np->in_dport == 0 ||
232460852Sdarrenr		     (tcp != NULL && dport == np->in_dport))) {
232560852Sdarrenr			i = appr_check(ip, fin, nat);
232660852Sdarrenr			if (i == 0)
232760852Sdarrenr				i = 1;
232860852Sdarrenr		} else
232960852Sdarrenr			i = 1;
233060852Sdarrenr		ATOMIC_INCL(nat_stats.ns_mapped[1]);
233153642Sguido		RWLOCK_EXIT(&ipf_nat);	/* READ */
233260852Sdarrenr		return i;
233353642Sguido	}
233453642Sguido	RWLOCK_EXIT(&ipf_nat);			/* READ/WRITE */
233553642Sguido	return 0;
233653642Sguido}
233753642Sguido
233853642Sguido
233953642Sguido/*
234053642Sguido * Packets coming in from the external interface go through this.
234153642Sguido * Here, the destination address requires alteration, if anything.
234253642Sguido */
234353642Sguidoint ip_natin(ip, fin)
234453642Sguidoip_t *ip;
234553642Sguidofr_info_t *fin;
234653642Sguido{
234753642Sguido	register struct in_addr src;
234853642Sguido	register struct in_addr in;
234953642Sguido	register ipnat_t *np;
235053642Sguido	u_int nflags = 0, natadd = 1, hv, msk;
235153642Sguido	struct ifnet *ifp = fin->fin_ifp;
235253642Sguido	tcphdr_t *tcp = NULL;
235353642Sguido	u_short sport = 0, dport = 0, *csump = NULL;
235453642Sguido	nat_t *nat;
235553642Sguido	u_32_t iph;
235653642Sguido	int i;
235753642Sguido
235860852Sdarrenr	if ((nat_list == NULL) || (ip->ip_v != 4) || (fr_nat_lock))
235953642Sguido		return 0;
236053642Sguido
236153642Sguido	if (!(ip->ip_off & IP_OFFMASK) && !(fin->fin_fi.fi_fl & FI_SHORT)) {
236253642Sguido		if (ip->ip_p == IPPROTO_TCP)
236353642Sguido			nflags = IPN_TCP;
236453642Sguido		else if (ip->ip_p == IPPROTO_UDP)
236553642Sguido			nflags = IPN_UDP;
236653642Sguido		if ((nflags & IPN_TCPUDP)) {
236753642Sguido			tcp = (tcphdr_t *)fin->fin_dp;
236853642Sguido			dport = tcp->th_dport;
236953642Sguido			sport = tcp->th_sport;
237053642Sguido		}
237153642Sguido	}
237253642Sguido
237353642Sguido	in = ip->ip_dst;
237453642Sguido	/* make sure the source address is to be redirected */
237553642Sguido	src = ip->ip_src;
237653642Sguido
237753642Sguido	READ_ENTER(&ipf_nat);
237853642Sguido
237960852Sdarrenr	if ((ip->ip_p == IPPROTO_ICMP) &&
238060852Sdarrenr	    (nat = nat_icmp(ip, fin, &nflags, NAT_INBOUND)))
238153642Sguido		;
238263523Sdarrenr	else if ((ip->ip_off & (IP_OFFMASK|IP_MF)) &&
238353642Sguido		 (nat = ipfr_nat_knownfrag(ip, fin)))
238453642Sguido		natadd = 0;
238553642Sguido	else if ((nat = nat_inlookup(fin->fin_ifp, nflags, (u_int)ip->ip_p,
238653642Sguido				     ip->ip_src, in, (dport << 16) | sport))) {
238753642Sguido		nflags = nat->nat_flags;
238853642Sguido		if ((nflags & (FI_W_SPORT|FI_W_DPORT)) != 0) {
238953642Sguido			if ((nat->nat_oport != sport) && (nflags & FI_W_DPORT))
239053642Sguido				nat->nat_oport = sport;
239153642Sguido			else if ((nat->nat_outport != dport) &&
239253642Sguido				 (nflags & FI_W_SPORT))
239353642Sguido				nat->nat_outport = dport;
239453642Sguido			nat->nat_flags &= ~(FI_W_SPORT|FI_W_DPORT);
239553642Sguido			nflags = nat->nat_flags;
239667614Sdarrenr			nat_wilds--;
239753642Sguido		}
239853642Sguido	} else {
239953642Sguido		RWLOCK_EXIT(&ipf_nat);
240053642Sguido		WRITE_ENTER(&ipf_nat);
240153642Sguido		/*
240253642Sguido		 * If there is no current entry in the nat table for this IP#,
240353642Sguido		 * create one for it (if there is a matching rule).
240453642Sguido		 */
240553642Sguido		msk = 0xffffffff;
240653642Sguido		i = 32;
240753642Sguidomaskloop:
240853642Sguido		iph = in.s_addr & htonl(msk);
240960852Sdarrenr		hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz);
241060852Sdarrenr		for (np = rdr_rules[hv]; np; np = np->in_rnext) {
241160852Sdarrenr			if ((np->in_ifp && (np->in_ifp != ifp)) ||
241260852Sdarrenr			    (np->in_p && (np->in_p != ip->ip_p)) ||
241360852Sdarrenr			    (np->in_flags && !(nflags & np->in_flags)))
241460852Sdarrenr				continue;
241560852Sdarrenr			if (np->in_flags & IPN_FILTER) {
241660852Sdarrenr				if (!nat_match(fin, np, ip))
241760852Sdarrenr					continue;
241860852Sdarrenr			} else if ((in.s_addr & np->in_outmsk) != np->in_outip)
241960852Sdarrenr				continue;
242060852Sdarrenr			if ((np->in_redir & NAT_REDIRECT) &&
242163523Sdarrenr			    (!np->in_pmin || (np->in_flags & IPN_FILTER) ||
242260852Sdarrenr			     ((ntohs(np->in_pmax) >= ntohs(dport)) &&
242360852Sdarrenr			      (ntohs(dport) >= ntohs(np->in_pmin)))))
242453642Sguido				if ((nat = nat_new(np, ip, fin, nflags,
242553642Sguido						    NAT_INBOUND))) {
242653642Sguido					np->in_hits++;
242753642Sguido#ifdef	IPFILTER_LOG
242853642Sguido					nat_log(nat, (u_int)np->in_redir);
242953642Sguido#endif
243053642Sguido					break;
243153642Sguido				}
243260852Sdarrenr		}
243360852Sdarrenr
243453642Sguido		if ((np == NULL) && (i > 0)) {
243553642Sguido			do {
243653642Sguido				i--;
243753642Sguido				msk <<= 1;
243853642Sguido			} while ((i >= 0) && ((rdr_masks & (1 << i)) == 0));
243953642Sguido			if (i >= 0)
244053642Sguido				goto maskloop;
244153642Sguido		}
244253642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
244353642Sguido	}
244453642Sguido	if (nat) {
244553642Sguido		np = nat->nat_ptr;
244653642Sguido		fin->fin_fr = nat->nat_fr;
244753642Sguido		if (natadd && fin->fin_fi.fi_fl & FI_FRAG)
244853642Sguido			ipfr_nat_newfrag(ip, fin, 0, nat);
244953642Sguido		if ((np->in_apr != NULL) && (np->in_dport == 0 ||
245060852Sdarrenr		    (tcp != NULL && sport == np->in_dport))) {
245160852Sdarrenr			i = appr_check(ip, fin, nat);
245260852Sdarrenr			if (i == -1) {
245360852Sdarrenr				RWLOCK_EXIT(&ipf_nat);
245460852Sdarrenr				return i;
245560852Sdarrenr			}
245660852Sdarrenr		}
245753642Sguido
245860852Sdarrenr		MUTEX_ENTER(&nat->nat_lock);
245953642Sguido		if (nflags != IPN_ICMPERR)
246053642Sguido			nat->nat_age = fr_defnatage;
246153642Sguido
246253642Sguido		nat->nat_bytes += ip->ip_len;
246353642Sguido		nat->nat_pkts++;
246460852Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
246553642Sguido		ip->ip_dst = nat->nat_inip;
246660852Sdarrenr		fin->fin_fi.fi_daddr = nat->nat_inip.s_addr;
246753642Sguido
246853642Sguido		/*
246953642Sguido		 * Fix up checksums, not by recalculating them, but
247053642Sguido		 * simply computing adjustments.
247153642Sguido		 */
247253642Sguido#if SOLARIS || defined(__sgi)
247353642Sguido		if (nat->nat_dir == NAT_OUTBOUND)
247467614Sdarrenr			fix_incksum(&ip->ip_sum, nat->nat_ipsumd);
247553642Sguido		else
247667614Sdarrenr			fix_outcksum(&ip->ip_sum, nat->nat_ipsumd);
247753642Sguido#endif
247853642Sguido		if (!(ip->ip_off & IP_OFFMASK) &&
247953642Sguido		    !(fin->fin_fi.fi_fl & FI_SHORT)) {
248053642Sguido
248153642Sguido			if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) {
248253642Sguido				tcp->th_dport = nat->nat_inport;
248353642Sguido				fin->fin_data[1] = ntohs(tcp->th_dport);
248453642Sguido			}
248553642Sguido
248653642Sguido			if (ip->ip_p == IPPROTO_TCP) {
248753642Sguido				csump = &tcp->th_sum;
248860852Sdarrenr				MUTEX_ENTER(&nat->nat_lock);
248953642Sguido				fr_tcp_age(&nat->nat_age,
249060852Sdarrenr					   nat->nat_tcpstate, fin, 0);
249153642Sguido				if (nat->nat_age < fr_defnaticmpage)
249253642Sguido					nat->nat_age = fr_defnaticmpage;
249353642Sguido#ifdef LARGE_NAT
249460852Sdarrenr				else if (nat->nat_age > fr_defnatage)
249560852Sdarrenr					nat->nat_age = fr_defnatage;
249653642Sguido#endif
249753642Sguido				/*
249853642Sguido				 * Increase this because we may have
249953642Sguido				 * "keep state" following this too and
250053642Sguido				 * packet storms can occur if this is
250153642Sguido				 * removed too quickly.
250253642Sguido				 */
250353642Sguido				if (nat->nat_age == fr_tcpclosed)
250453642Sguido					nat->nat_age = fr_tcplastack;
250560852Sdarrenr				MUTEX_EXIT(&nat->nat_lock);
250653642Sguido			} else if (ip->ip_p == IPPROTO_UDP) {
250753642Sguido				udphdr_t *udp = (udphdr_t *)tcp;
250853642Sguido
250953642Sguido				if (udp->uh_sum)
251053642Sguido					csump = &udp->uh_sum;
251160852Sdarrenr			} else if (ip->ip_p == IPPROTO_ICMP) {
251260852Sdarrenr				nat->nat_age = fr_defnaticmpage;
251353642Sguido			}
251460852Sdarrenr
251553642Sguido			if (csump) {
251653642Sguido				if (nat->nat_dir == NAT_OUTBOUND)
251767614Sdarrenr					fix_incksum(csump, nat->nat_sumd[0]);
251853642Sguido				else
251967614Sdarrenr					fix_outcksum(csump, nat->nat_sumd[0]);
252053642Sguido			}
252153642Sguido		}
252260852Sdarrenr		ATOMIC_INCL(nat_stats.ns_mapped[0]);
252353642Sguido		RWLOCK_EXIT(&ipf_nat);			/* READ */
252453642Sguido		return 1;
252553642Sguido	}
252653642Sguido	RWLOCK_EXIT(&ipf_nat);			/* READ/WRITE */
252753642Sguido	return 0;
252853642Sguido}
252953642Sguido
253053642Sguido
253153642Sguido/*
253253642Sguido * Free all memory used by NAT structures allocated at runtime.
253353642Sguido */
253453642Sguidovoid ip_natunload()
253553642Sguido{
253653642Sguido	WRITE_ENTER(&ipf_nat);
253753642Sguido	(void) nat_clearlist();
253853642Sguido	(void) nat_flushtable();
253953642Sguido	RWLOCK_EXIT(&ipf_nat);
254053642Sguido
254153642Sguido	if (nat_table[0] != NULL) {
254253642Sguido		KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz);
254353642Sguido		nat_table[0] = NULL;
254453642Sguido	}
254553642Sguido	if (nat_table[1] != NULL) {
254653642Sguido		KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz);
254753642Sguido		nat_table[1] = NULL;
254853642Sguido	}
254953642Sguido	if (nat_rules != NULL) {
255053642Sguido		KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz);
255153642Sguido		nat_rules = NULL;
255253642Sguido	}
255353642Sguido	if (rdr_rules != NULL) {
255453642Sguido		KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz);
255553642Sguido		rdr_rules = NULL;
255653642Sguido	}
255760852Sdarrenr	if (maptable != NULL) {
255860852Sdarrenr		KFREES(maptable, sizeof(hostmap_t *) * ipf_hostmap_sz);
255960852Sdarrenr		maptable = NULL;
256060852Sdarrenr	}
256153642Sguido}
256253642Sguido
256353642Sguido
256453642Sguido/*
256553642Sguido * Slowly expire held state for NAT entries.  Timeouts are set in
256653642Sguido * expectation of this being called twice per second.
256753642Sguido */
256853642Sguidovoid ip_natexpire()
256953642Sguido{
257053642Sguido	register struct nat *nat, **natp;
257153642Sguido#if defined(_KERNEL) && !SOLARIS
257253642Sguido	int s;
257353642Sguido#endif
257453642Sguido
257553642Sguido	SPL_NET(s);
257653642Sguido	WRITE_ENTER(&ipf_nat);
257753642Sguido	for (natp = &nat_instances; (nat = *natp); ) {
257853642Sguido		nat->nat_age--;
257953642Sguido		if (nat->nat_age) {
258053642Sguido			natp = &nat->nat_next;
258153642Sguido			continue;
258253642Sguido		}
258353642Sguido		*natp = nat->nat_next;
258453642Sguido#ifdef	IPFILTER_LOG
258553642Sguido		nat_log(nat, NL_EXPIRE);
258653642Sguido#endif
258753642Sguido		nat_delete(nat);
258853642Sguido		nat_stats.ns_expire++;
258953642Sguido	}
259053642Sguido	RWLOCK_EXIT(&ipf_nat);
259153642Sguido	SPL_X(s);
259253642Sguido}
259353642Sguido
259453642Sguido
259553642Sguido/*
259653642Sguido */
259753642Sguidovoid ip_natsync(ifp)
259853642Sguidovoid *ifp;
259953642Sguido{
260053642Sguido	register ipnat_t *n;
260153642Sguido	register nat_t *nat;
260253642Sguido	register u_32_t sum1, sum2, sumd;
260353642Sguido	struct in_addr in;
260453642Sguido	ipnat_t *np;
260553642Sguido	void *ifp2;
260653642Sguido#if defined(_KERNEL) && !SOLARIS
260753642Sguido	int s;
260853642Sguido#endif
260953642Sguido
261053642Sguido	/*
261153642Sguido	 * Change IP addresses for NAT sessions for any protocol except TCP
261253642Sguido	 * since it will break the TCP connection anyway.
261353642Sguido	 */
261453642Sguido	SPL_NET(s);
261553642Sguido	WRITE_ENTER(&ipf_nat);
261653642Sguido	for (nat = nat_instances; nat; nat = nat->nat_next)
261753642Sguido		if (((ifp == NULL) || (ifp == nat->nat_ifp)) &&
261853642Sguido		    !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr) &&
261953642Sguido		    (np->in_outmsk == 0xffffffff) && !np->in_nip) {
262053642Sguido			ifp2 = nat->nat_ifp;
262153642Sguido			/*
262253642Sguido			 * Change the map-to address to be the same as the
262353642Sguido			 * new one.
262453642Sguido			 */
262553642Sguido			sum1 = nat->nat_outip.s_addr;
262660852Sdarrenr			if (fr_ifpaddr(4, ifp2, &in) != -1)
262755929Sguido				nat->nat_outip = in;
262853642Sguido			sum2 = nat->nat_outip.s_addr;
262953642Sguido
263053642Sguido			if (sum1 == sum2)
263153642Sguido				continue;
263253642Sguido			/*
263353642Sguido			 * Readjust the checksum adjustment to take into
263453642Sguido			 * account the new IP#.
263553642Sguido			 */
263653642Sguido			CALC_SUMD(sum1, sum2, sumd);
263755929Sguido			/* XXX - dont change for TCP when solaris does
263855929Sguido			 * hardware checksumming.
263955929Sguido			 */
264055929Sguido			sumd += nat->nat_sumd[0];
264155929Sguido			nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
264255929Sguido			nat->nat_sumd[1] = nat->nat_sumd[0];
264353642Sguido		}
264453642Sguido
264553642Sguido	for (n = nat_list; (n != NULL); n = n->in_next)
264655929Sguido		if (n->in_ifp == ifp) {
264760852Sdarrenr			n->in_ifp = (void *)GETUNIT(n->in_ifname, 4);
264860852Sdarrenr			if (!n->in_ifp)
264960852Sdarrenr				n->in_ifp = (void *)-1;
265060852Sdarrenr		}
265160852Sdarrenr	RWLOCK_EXIT(&ipf_nat);
265253642Sguido	SPL_X(s);
265353642Sguido}
265453642Sguido
265553642Sguido
265653642Sguido#ifdef	IPFILTER_LOG
265753642Sguidovoid nat_log(nat, type)
265853642Sguidostruct nat *nat;
265953642Sguidou_int type;
266053642Sguido{
266153642Sguido	struct ipnat *np;
266253642Sguido	struct natlog natl;
266353642Sguido	void *items[1];
266453642Sguido	size_t sizes[1];
266553642Sguido	int rulen, types[1];
266653642Sguido
266753642Sguido	natl.nl_inip = nat->nat_inip;
266853642Sguido	natl.nl_outip = nat->nat_outip;
266953642Sguido	natl.nl_origip = nat->nat_oip;
267053642Sguido	natl.nl_bytes = nat->nat_bytes;
267153642Sguido	natl.nl_pkts = nat->nat_pkts;
267253642Sguido	natl.nl_origport = nat->nat_oport;
267353642Sguido	natl.nl_inport = nat->nat_inport;
267453642Sguido	natl.nl_outport = nat->nat_outport;
267557096Sguido	natl.nl_p = nat->nat_p;
267653642Sguido	natl.nl_type = type;
267753642Sguido	natl.nl_rule = -1;
267853642Sguido#ifndef LARGE_NAT
267953642Sguido	if (nat->nat_ptr != NULL) {
268053642Sguido		for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++)
268153642Sguido			if (np == nat->nat_ptr) {
268253642Sguido				natl.nl_rule = rulen;
268353642Sguido				break;
268453642Sguido			}
268553642Sguido	}
268653642Sguido#endif
268753642Sguido	items[0] = &natl;
268853642Sguido	sizes[0] = sizeof(natl);
268953642Sguido	types[0] = 0;
269053642Sguido
269153642Sguido	(void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1);
269253642Sguido}
269353642Sguido#endif
2694