ip_nat.c revision 172776
1145522Sdarrenr/*	$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_nat.c 172776 2007-10-18 21:52:14Z darrenr $	*/
2145522Sdarrenr
353642Sguido/*
4145522Sdarrenr * Copyright (C) 1995-2003 by Darren Reed.
553642Sguido *
680482Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
753642Sguido */
8145522Sdarrenr#if defined(KERNEL) || defined(_KERNEL)
9145522Sdarrenr# undef KERNEL
10145522Sdarrenr# undef _KERNEL
11145522Sdarrenr# define        KERNEL	1
12145522Sdarrenr# define        _KERNEL	1
1353642Sguido#endif
1453642Sguido#include <sys/errno.h>
1553642Sguido#include <sys/types.h>
1653642Sguido#include <sys/param.h>
1753642Sguido#include <sys/time.h>
1853642Sguido#include <sys/file.h>
19170268Sdarrenr#if defined(_KERNEL) && defined(__NetBSD_Version__) && \
20170268Sdarrenr    (__NetBSD_Version__ >= 399002000)
21170268Sdarrenr# include <sys/kauth.h>
22170268Sdarrenr#endif
2353642Sguido#if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \
2453642Sguido    defined(_KERNEL)
25170268Sdarrenr#if defined(__NetBSD_Version__) && (__NetBSD_Version__ < 399001400)
26170268Sdarrenr#  include "opt_ipfilter_log.h"
27170268Sdarrenr# else
28170268Sdarrenr#  include "opt_ipfilter.h"
29170268Sdarrenr# endif
3053642Sguido#endif
31145522Sdarrenr#if !defined(_KERNEL)
3253642Sguido# include <stdio.h>
3353642Sguido# include <string.h>
3453642Sguido# include <stdlib.h>
35145522Sdarrenr# define _KERNEL
36145522Sdarrenr# ifdef __OpenBSD__
37145522Sdarrenrstruct file;
38145522Sdarrenr# endif
39145522Sdarrenr# include <sys/uio.h>
40145522Sdarrenr# undef _KERNEL
4153642Sguido#endif
42145522Sdarrenr#if defined(_KERNEL) && (__FreeBSD_version >= 220000)
4353642Sguido# include <sys/filio.h>
4453642Sguido# include <sys/fcntl.h>
4553642Sguido#else
4653642Sguido# include <sys/ioctl.h>
4753642Sguido#endif
48153876Sguido#if !defined(AIX)
49153876Sguido# include <sys/fcntl.h>
50153876Sguido#endif
51145522Sdarrenr#if !defined(linux)
5253642Sguido# include <sys/protosw.h>
5353642Sguido#endif
5453642Sguido#include <sys/socket.h>
55145522Sdarrenr#if defined(_KERNEL)
5653642Sguido# include <sys/systm.h>
57145522Sdarrenr# if !defined(__SVR4) && !defined(__svr4__)
5853642Sguido#  include <sys/mbuf.h>
5953642Sguido# endif
60145522Sdarrenr#endif
61145522Sdarrenr#if defined(__SVR4) || defined(__svr4__)
6253642Sguido# include <sys/filio.h>
6353642Sguido# include <sys/byteorder.h>
6453642Sguido# ifdef _KERNEL
6553642Sguido#  include <sys/dditypes.h>
6653642Sguido# endif
6753642Sguido# include <sys/stream.h>
6853642Sguido# include <sys/kmem.h>
6953642Sguido#endif
7053642Sguido#if __FreeBSD_version >= 300000
7153642Sguido# include <sys/queue.h>
7253642Sguido#endif
7353642Sguido#include <net/if.h>
7453642Sguido#if __FreeBSD_version >= 300000
7553642Sguido# include <net/if_var.h>
7653642Sguido# if defined(_KERNEL) && !defined(IPFILTER_LKM)
7753642Sguido#  include "opt_ipfilter.h"
7853642Sguido# endif
7953642Sguido#endif
8053642Sguido#ifdef sun
8153642Sguido# include <net/af.h>
8253642Sguido#endif
8353642Sguido#include <net/route.h>
8453642Sguido#include <netinet/in.h>
8553642Sguido#include <netinet/in_systm.h>
8653642Sguido#include <netinet/ip.h>
8753642Sguido
8853642Sguido#ifdef RFC1825
8953642Sguido# include <vpn/md5.h>
9053642Sguido# include <vpn/ipsec.h>
9153642Sguidoextern struct ifnet vpnif;
9253642Sguido#endif
9353642Sguido
94145522Sdarrenr#if !defined(linux)
9553642Sguido# include <netinet/ip_var.h>
9653642Sguido#endif
9753642Sguido#include <netinet/tcp.h>
9853642Sguido#include <netinet/udp.h>
9953642Sguido#include <netinet/ip_icmp.h>
10053642Sguido#include "netinet/ip_compat.h"
10153642Sguido#include <netinet/tcpip.h>
10253642Sguido#include "netinet/ip_fil.h"
10353642Sguido#include "netinet/ip_nat.h"
10453642Sguido#include "netinet/ip_frag.h"
10553642Sguido#include "netinet/ip_state.h"
10692685Sdarrenr#include "netinet/ip_proxy.h"
107145522Sdarrenr#ifdef	IPFILTER_SYNC
108145522Sdarrenr#include "netinet/ip_sync.h"
109145522Sdarrenr#endif
11053642Sguido#if (__FreeBSD_version >= 300000)
11153642Sguido# include <sys/malloc.h>
11253642Sguido#endif
113145522Sdarrenr/* END OF INCLUDES */
114145522Sdarrenr
11553642Sguido#undef	SOCKADDR_IN
11653642Sguido#define	SOCKADDR_IN	struct sockaddr_in
11753642Sguido
11880482Sdarrenr#if !defined(lint)
11980482Sdarrenrstatic const char sccsid[] = "@(#)ip_nat.c	1.11 6/5/96 (C) 1995 Darren Reed";
12080482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: head/sys/contrib/ipfilter/netinet/ip_nat.c 172776 2007-10-18 21:52:14Z darrenr $";
121172776Sdarrenr/* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.102 2007/10/16 10:08:10 darrenr Exp $"; */
12280482Sdarrenr#endif
12380482Sdarrenr
124145522Sdarrenr
125145522Sdarrenr/* ======================================================================== */
126145522Sdarrenr/* How the NAT is organised and works.                                      */
127145522Sdarrenr/*                                                                          */
128145522Sdarrenr/* Inside (interface y) NAT       Outside (interface x)                     */
129145522Sdarrenr/* -------------------- -+- -------------------------------------           */
130145522Sdarrenr/* Packet going          |   out, processsed by fr_checknatout() for x      */
131145522Sdarrenr/* ------------>         |   ------------>                                  */
132145522Sdarrenr/* src=10.1.1.1          |   src=192.1.1.1                                  */
133145522Sdarrenr/*                       |                                                  */
134145522Sdarrenr/*                       |   in, processed by fr_checknatin() for x         */
135145522Sdarrenr/* <------------         |   <------------                                  */
136145522Sdarrenr/* dst=10.1.1.1          |   dst=192.1.1.1                                  */
137145522Sdarrenr/* -------------------- -+- -------------------------------------           */
138145522Sdarrenr/* fr_checknatout() - changes ip_src and if required, sport                 */
139145522Sdarrenr/*             - creates a new mapping, if required.                        */
140145522Sdarrenr/* fr_checknatin()  - changes ip_dst and if required, dport                 */
141145522Sdarrenr/*                                                                          */
142145522Sdarrenr/* In the NAT table, internal source is recorded as "in" and externally     */
143145522Sdarrenr/* seen as "out".                                                           */
144145522Sdarrenr/* ======================================================================== */
145145522Sdarrenr
146145522Sdarrenr
14753642Sguidonat_t	**nat_table[2] = { NULL, NULL },
14853642Sguido	*nat_instances = NULL;
14953642Sguidoipnat_t	*nat_list = NULL;
150130886Sdarrenru_int	ipf_nattable_max = NAT_TABLE_MAX;
15153642Sguidou_int	ipf_nattable_sz = NAT_TABLE_SZ;
15253642Sguidou_int	ipf_natrules_sz = NAT_SIZE;
15353642Sguidou_int	ipf_rdrrules_sz = RDR_SIZE;
15460852Sdarrenru_int	ipf_hostmap_sz = HOSTMAP_SIZE;
155145522Sdarrenru_int	fr_nat_maxbucket = 0,
156145522Sdarrenr	fr_nat_maxbucket_reset = 1;
15753642Sguidou_32_t	nat_masks = 0;
15853642Sguidou_32_t	rdr_masks = 0;
159170268Sdarrenru_long	nat_last_force_flush = 0;
16053642Sguidoipnat_t	**nat_rules = NULL;
16153642Sguidoipnat_t	**rdr_rules = NULL;
162170268Sdarrenrhostmap_t	**ipf_hm_maptable  = NULL;
163170268Sdarrenrhostmap_t	*ipf_hm_maplist  = NULL;
164145522Sdarrenripftq_t	nat_tqb[IPF_TCP_NSTATES];
165145522Sdarrenripftq_t	nat_udptq;
166145522Sdarrenripftq_t	nat_icmptq;
167145522Sdarrenripftq_t	nat_iptq;
168145522Sdarrenripftq_t	*nat_utqe = NULL;
169170268Sdarrenrint	fr_nat_doflush = 0;
170145522Sdarrenr#ifdef  IPFILTER_LOG
171145522Sdarrenrint	nat_logging = 1;
172145522Sdarrenr#else
173145522Sdarrenrint	nat_logging = 0;
174145522Sdarrenr#endif
17553642Sguido
17653642Sguidou_long	fr_defnatage = DEF_NAT_AGE,
177145522Sdarrenr	fr_defnatipage = 120,		/* 60 seconds */
17853642Sguido	fr_defnaticmpage = 6;		/* 3 seconds */
17964580Sdarrenrnatstat_t nat_stats;
18060852Sdarrenrint	fr_nat_lock = 0;
181145522Sdarrenrint	fr_nat_init = 0;
182172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H)
183145522Sdarrenrextern	int		pfil_delayed_copy;
18453642Sguido#endif
18553642Sguido
186170268Sdarrenrstatic	int	nat_flush_entry __P((void *));
18753642Sguidostatic	int	nat_flushtable __P((void));
188145522Sdarrenrstatic	int	nat_clearlist __P((void));
18960852Sdarrenrstatic	void	nat_addnat __P((struct ipnat *));
19060852Sdarrenrstatic	void	nat_addrdr __P((struct ipnat *));
19153642Sguidostatic	void	nat_delrdr __P((struct ipnat *));
19253642Sguidostatic	void	nat_delnat __P((struct ipnat *));
19360852Sdarrenrstatic	int	fr_natgetent __P((caddr_t));
19460852Sdarrenrstatic	int	fr_natgetsz __P((caddr_t));
195145522Sdarrenrstatic	int	fr_natputent __P((caddr_t, int));
196170268Sdarrenrstatic	int	nat_extraflush __P((int));
197172776Sdarrenrstatic	int	nat_gettable __P((char *));
198145522Sdarrenrstatic	void	nat_tabmove __P((nat_t *));
199145522Sdarrenrstatic	int	nat_match __P((fr_info_t *, ipnat_t *));
200145522Sdarrenrstatic	INLINE	int nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *));
201145522Sdarrenrstatic	INLINE	int nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *));
20260852Sdarrenrstatic	hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr,
203145522Sdarrenr				    struct in_addr, struct in_addr, u_32_t));
204153876Sguidostatic	int	nat_icmpquerytype4 __P((int));
205145522Sdarrenrstatic	int	nat_siocaddnat __P((ipnat_t *, ipnat_t **, int));
206145522Sdarrenrstatic	void	nat_siocdelnat __P((ipnat_t *, ipnat_t **, int));
207153876Sguidostatic	int	nat_finalise __P((fr_info_t *, nat_t *, natinfo_t *,
208145522Sdarrenr				      tcphdr_t *, nat_t **, int));
209161356Sguidostatic	int	nat_resolverule __P((ipnat_t *));
210145522Sdarrenrstatic	nat_t	*fr_natclone __P((fr_info_t *, nat_t *));
211110916Sdarrenrstatic	void	nat_mssclamp __P((tcphdr_t *, u_32_t, fr_info_t *, u_short *));
212153876Sguidostatic	int	nat_wildok __P((nat_t *, int, int, int, int));
213170268Sdarrenrstatic	int	nat_getnext __P((ipftoken_t *, ipfgeniter_t *));
214170268Sdarrenrstatic	int	nat_iterator __P((ipftoken_t *, ipfgeniter_t *));
21553642Sguido
21653642Sguido
217145522Sdarrenr/* ------------------------------------------------------------------------ */
218145522Sdarrenr/* Function:    fr_natinit                                                  */
219145522Sdarrenr/* Returns:     int - 0 == success, -1 == failure                           */
220145522Sdarrenr/* Parameters:  Nil                                                         */
221145522Sdarrenr/*                                                                          */
222145522Sdarrenr/* Initialise all of the NAT locks, tables and other structures.            */
223145522Sdarrenr/* ------------------------------------------------------------------------ */
224145522Sdarrenrint fr_natinit()
22553642Sguido{
226145522Sdarrenr	int i;
227145522Sdarrenr
22853642Sguido	KMALLOCS(nat_table[0], nat_t **, sizeof(nat_t *) * ipf_nattable_sz);
22953642Sguido	if (nat_table[0] != NULL)
23053642Sguido		bzero((char *)nat_table[0], ipf_nattable_sz * sizeof(nat_t *));
23153642Sguido	else
23253642Sguido		return -1;
23353642Sguido
23453642Sguido	KMALLOCS(nat_table[1], nat_t **, sizeof(nat_t *) * ipf_nattable_sz);
23553642Sguido	if (nat_table[1] != NULL)
23653642Sguido		bzero((char *)nat_table[1], ipf_nattable_sz * sizeof(nat_t *));
23753642Sguido	else
238145522Sdarrenr		return -2;
23953642Sguido
24053642Sguido	KMALLOCS(nat_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_natrules_sz);
24153642Sguido	if (nat_rules != NULL)
24253642Sguido		bzero((char *)nat_rules, ipf_natrules_sz * sizeof(ipnat_t *));
24353642Sguido	else
244145522Sdarrenr		return -3;
24553642Sguido
24653642Sguido	KMALLOCS(rdr_rules, ipnat_t **, sizeof(ipnat_t *) * ipf_rdrrules_sz);
24753642Sguido	if (rdr_rules != NULL)
24853642Sguido		bzero((char *)rdr_rules, ipf_rdrrules_sz * sizeof(ipnat_t *));
24953642Sguido	else
250145522Sdarrenr		return -4;
25160852Sdarrenr
252170268Sdarrenr	KMALLOCS(ipf_hm_maptable, hostmap_t **, \
253170268Sdarrenr		 sizeof(hostmap_t *) * ipf_hostmap_sz);
254170268Sdarrenr	if (ipf_hm_maptable != NULL)
255170268Sdarrenr		bzero((char *)ipf_hm_maptable,
256170268Sdarrenr		      sizeof(hostmap_t *) * ipf_hostmap_sz);
25760852Sdarrenr	else
258145522Sdarrenr		return -5;
259170268Sdarrenr	ipf_hm_maplist = NULL;
260145522Sdarrenr
261145522Sdarrenr	KMALLOCS(nat_stats.ns_bucketlen[0], u_long *,
262145522Sdarrenr		 ipf_nattable_sz * sizeof(u_long));
263145522Sdarrenr	if (nat_stats.ns_bucketlen[0] == NULL)
264145522Sdarrenr		return -6;
265145522Sdarrenr	bzero((char *)nat_stats.ns_bucketlen[0],
266145522Sdarrenr	      ipf_nattable_sz * sizeof(u_long));
267145522Sdarrenr
268145522Sdarrenr	KMALLOCS(nat_stats.ns_bucketlen[1], u_long *,
269145522Sdarrenr		 ipf_nattable_sz * sizeof(u_long));
270145522Sdarrenr	if (nat_stats.ns_bucketlen[1] == NULL)
271145522Sdarrenr		return -7;
272145522Sdarrenr
273145522Sdarrenr	bzero((char *)nat_stats.ns_bucketlen[1],
274145522Sdarrenr	      ipf_nattable_sz * sizeof(u_long));
275145522Sdarrenr
276145522Sdarrenr	if (fr_nat_maxbucket == 0) {
277145522Sdarrenr		for (i = ipf_nattable_sz; i > 0; i >>= 1)
278145522Sdarrenr			fr_nat_maxbucket++;
279145522Sdarrenr		fr_nat_maxbucket *= 2;
280145522Sdarrenr	}
281145522Sdarrenr
282145522Sdarrenr	fr_sttab_init(nat_tqb);
283145522Sdarrenr	/*
284145522Sdarrenr	 * Increase this because we may have "keep state" following this too
285145522Sdarrenr	 * and packet storms can occur if this is removed too quickly.
286145522Sdarrenr	 */
287145522Sdarrenr	nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = fr_tcplastack;
288145522Sdarrenr	nat_tqb[IPF_TCP_NSTATES - 1].ifq_next = &nat_udptq;
289145522Sdarrenr	nat_udptq.ifq_ttl = fr_defnatage;
290145522Sdarrenr	nat_udptq.ifq_ref = 1;
291145522Sdarrenr	nat_udptq.ifq_head = NULL;
292145522Sdarrenr	nat_udptq.ifq_tail = &nat_udptq.ifq_head;
293145522Sdarrenr	MUTEX_INIT(&nat_udptq.ifq_lock, "nat ipftq udp tab");
294145522Sdarrenr	nat_udptq.ifq_next = &nat_icmptq;
295145522Sdarrenr	nat_icmptq.ifq_ttl = fr_defnaticmpage;
296145522Sdarrenr	nat_icmptq.ifq_ref = 1;
297145522Sdarrenr	nat_icmptq.ifq_head = NULL;
298145522Sdarrenr	nat_icmptq.ifq_tail = &nat_icmptq.ifq_head;
299145522Sdarrenr	MUTEX_INIT(&nat_icmptq.ifq_lock, "nat icmp ipftq tab");
300145522Sdarrenr	nat_icmptq.ifq_next = &nat_iptq;
301145522Sdarrenr	nat_iptq.ifq_ttl = fr_defnatipage;
302145522Sdarrenr	nat_iptq.ifq_ref = 1;
303145522Sdarrenr	nat_iptq.ifq_head = NULL;
304145522Sdarrenr	nat_iptq.ifq_tail = &nat_iptq.ifq_head;
305145522Sdarrenr	MUTEX_INIT(&nat_iptq.ifq_lock, "nat ip ipftq tab");
306145522Sdarrenr	nat_iptq.ifq_next = NULL;
307145522Sdarrenr
308145522Sdarrenr	for (i = 0; i < IPF_TCP_NSTATES; i++) {
309145522Sdarrenr		if (nat_tqb[i].ifq_ttl < fr_defnaticmpage)
310145522Sdarrenr			nat_tqb[i].ifq_ttl = fr_defnaticmpage;
311145522Sdarrenr#ifdef LARGE_NAT
312145522Sdarrenr		else if (nat_tqb[i].ifq_ttl > fr_defnatage)
313145522Sdarrenr			nat_tqb[i].ifq_ttl = fr_defnatage;
314145522Sdarrenr#endif
315145522Sdarrenr	}
316145522Sdarrenr
317145522Sdarrenr	/*
318145522Sdarrenr	 * Increase this because we may have "keep state" following
319145522Sdarrenr	 * this too and packet storms can occur if this is removed
320145522Sdarrenr	 * too quickly.
321145522Sdarrenr	 */
322145522Sdarrenr	nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = nat_tqb[IPF_TCPS_LAST_ACK].ifq_ttl;
323145522Sdarrenr
324145522Sdarrenr	RWLOCK_INIT(&ipf_nat, "ipf IP NAT rwlock");
325145522Sdarrenr	RWLOCK_INIT(&ipf_natfrag, "ipf IP NAT-Frag rwlock");
326145522Sdarrenr	MUTEX_INIT(&ipf_nat_new, "ipf nat new mutex");
327145522Sdarrenr	MUTEX_INIT(&ipf_natio, "ipf nat io mutex");
328145522Sdarrenr
329145522Sdarrenr	fr_nat_init = 1;
330145522Sdarrenr
33153642Sguido	return 0;
33253642Sguido}
33353642Sguido
33453642Sguido
335145522Sdarrenr/* ------------------------------------------------------------------------ */
336145522Sdarrenr/* Function:    nat_addrdr                                                  */
337145522Sdarrenr/* Returns:     Nil                                                         */
338145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to add                           */
339145522Sdarrenr/*                                                                          */
340145522Sdarrenr/* Adds a redirect rule to the hash table of redirect rules and the list of */
341145522Sdarrenr/* loaded NAT rules.  Updates the bitmask indicating which netmasks are in  */
342145522Sdarrenr/* use by redirect rules.                                                   */
343145522Sdarrenr/* ------------------------------------------------------------------------ */
34460852Sdarrenrstatic void nat_addrdr(n)
34553642Sguidoipnat_t *n;
34653642Sguido{
34760852Sdarrenr	ipnat_t **np;
34860852Sdarrenr	u_32_t j;
34953642Sguido	u_int hv;
35060852Sdarrenr	int k;
35153642Sguido
352145522Sdarrenr	k = count4bits(n->in_outmsk);
35360852Sdarrenr	if ((k >= 0) && (k != 32))
35460852Sdarrenr		rdr_masks |= 1 << k;
35560852Sdarrenr	j = (n->in_outip & n->in_outmsk);
35660852Sdarrenr	hv = NAT_HASH_FN(j, 0, ipf_rdrrules_sz);
35760852Sdarrenr	np = rdr_rules + hv;
35860852Sdarrenr	while (*np != NULL)
35960852Sdarrenr		np = &(*np)->in_rnext;
36060852Sdarrenr	n->in_rnext = NULL;
36160852Sdarrenr	n->in_prnext = np;
362145522Sdarrenr	n->in_hv = hv;
36360852Sdarrenr	*np = n;
36453642Sguido}
36553642Sguido
36653642Sguido
367145522Sdarrenr/* ------------------------------------------------------------------------ */
368145522Sdarrenr/* Function:    nat_addnat                                                  */
369145522Sdarrenr/* Returns:     Nil                                                         */
370145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to add                           */
371145522Sdarrenr/*                                                                          */
372145522Sdarrenr/* Adds a NAT map rule to the hash table of rules and the list of  loaded   */
373145522Sdarrenr/* NAT rules.  Updates the bitmask indicating which netmasks are in use by  */
374145522Sdarrenr/* redirect rules.                                                          */
375145522Sdarrenr/* ------------------------------------------------------------------------ */
37660852Sdarrenrstatic void nat_addnat(n)
37760852Sdarrenripnat_t *n;
37860852Sdarrenr{
37960852Sdarrenr	ipnat_t **np;
38060852Sdarrenr	u_32_t j;
38160852Sdarrenr	u_int hv;
38260852Sdarrenr	int k;
38360852Sdarrenr
384145522Sdarrenr	k = count4bits(n->in_inmsk);
38560852Sdarrenr	if ((k >= 0) && (k != 32))
38660852Sdarrenr		nat_masks |= 1 << k;
38760852Sdarrenr	j = (n->in_inip & n->in_inmsk);
38860852Sdarrenr	hv = NAT_HASH_FN(j, 0, ipf_natrules_sz);
38960852Sdarrenr	np = nat_rules + hv;
39060852Sdarrenr	while (*np != NULL)
39160852Sdarrenr		np = &(*np)->in_mnext;
39260852Sdarrenr	n->in_mnext = NULL;
39360852Sdarrenr	n->in_pmnext = np;
394145522Sdarrenr	n->in_hv = hv;
39560852Sdarrenr	*np = n;
39660852Sdarrenr}
39760852Sdarrenr
39860852Sdarrenr
399145522Sdarrenr/* ------------------------------------------------------------------------ */
400145522Sdarrenr/* Function:    nat_delrdr                                                  */
401145522Sdarrenr/* Returns:     Nil                                                         */
402145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to delete                        */
403145522Sdarrenr/*                                                                          */
404145522Sdarrenr/* Removes a redirect rule from the hash table of redirect rules.           */
405145522Sdarrenr/* ------------------------------------------------------------------------ */
40660852Sdarrenrstatic void nat_delrdr(n)
40760852Sdarrenripnat_t *n;
40860852Sdarrenr{
40960852Sdarrenr	if (n->in_rnext)
41060852Sdarrenr		n->in_rnext->in_prnext = n->in_prnext;
41160852Sdarrenr	*n->in_prnext = n->in_rnext;
41260852Sdarrenr}
41360852Sdarrenr
41460852Sdarrenr
415145522Sdarrenr/* ------------------------------------------------------------------------ */
416145522Sdarrenr/* Function:    nat_delnat                                                  */
417145522Sdarrenr/* Returns:     Nil                                                         */
418145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to delete                        */
419145522Sdarrenr/*                                                                          */
420145522Sdarrenr/* Removes a NAT map rule from the hash table of NAT map rules.             */
421145522Sdarrenr/* ------------------------------------------------------------------------ */
42253642Sguidostatic void nat_delnat(n)
42353642Sguidoipnat_t *n;
42453642Sguido{
425145522Sdarrenr	if (n->in_mnext != NULL)
42660852Sdarrenr		n->in_mnext->in_pmnext = n->in_pmnext;
42760852Sdarrenr	*n->in_pmnext = n->in_mnext;
42860852Sdarrenr}
42960852Sdarrenr
43060852Sdarrenr
431145522Sdarrenr/* ------------------------------------------------------------------------ */
432145522Sdarrenr/* Function:    nat_hostmap                                                 */
433145522Sdarrenr/* Returns:     struct hostmap* - NULL if no hostmap could be created,      */
434145522Sdarrenr/*                                else a pointer to the hostmapping to use  */
435145522Sdarrenr/* Parameters:  np(I)   - pointer to NAT rule                               */
436145522Sdarrenr/*              real(I) - real IP address                                   */
437145522Sdarrenr/*              map(I)  - mapped IP address                                 */
438145522Sdarrenr/*              port(I) - destination port number                           */
439145522Sdarrenr/* Write Locks: ipf_nat                                                     */
440145522Sdarrenr/*                                                                          */
441145522Sdarrenr/* Check if an ip address has already been allocated for a given mapping    */
442145522Sdarrenr/* that is not doing port based translation.  If is not yet allocated, then */
443145522Sdarrenr/* create a new entry if a non-NULL NAT rule pointer has been supplied.     */
444145522Sdarrenr/* ------------------------------------------------------------------------ */
445145522Sdarrenrstatic struct hostmap *nat_hostmap(np, src, dst, map, port)
44660852Sdarrenripnat_t *np;
447145522Sdarrenrstruct in_addr src;
448145522Sdarrenrstruct in_addr dst;
44960852Sdarrenrstruct in_addr map;
450145522Sdarrenru_32_t port;
45160852Sdarrenr{
45260852Sdarrenr	hostmap_t *hm;
45353642Sguido	u_int hv;
45453642Sguido
455145522Sdarrenr	hv = (src.s_addr ^ dst.s_addr);
456145522Sdarrenr	hv += src.s_addr;
457145522Sdarrenr	hv += dst.s_addr;
458145522Sdarrenr	hv %= HOSTMAP_SIZE;
459170268Sdarrenr	for (hm = ipf_hm_maptable[hv]; hm; hm = hm->hm_next)
460145522Sdarrenr		if ((hm->hm_srcip.s_addr == src.s_addr) &&
461145522Sdarrenr		    (hm->hm_dstip.s_addr == dst.s_addr) &&
462145522Sdarrenr		    ((np == NULL) || (np == hm->hm_ipnat)) &&
463145522Sdarrenr		    ((port == 0) || (port == hm->hm_port))) {
46460852Sdarrenr			hm->hm_ref++;
46560852Sdarrenr			return hm;
46660852Sdarrenr		}
46760852Sdarrenr
468145522Sdarrenr	if (np == NULL)
469145522Sdarrenr		return NULL;
470145522Sdarrenr
47160852Sdarrenr	KMALLOC(hm, hostmap_t *);
47260852Sdarrenr	if (hm) {
473170268Sdarrenr		hm->hm_next = ipf_hm_maplist;
474170268Sdarrenr		hm->hm_pnext = &ipf_hm_maplist;
475170268Sdarrenr		if (ipf_hm_maplist != NULL)
476170268Sdarrenr			ipf_hm_maplist->hm_pnext = &hm->hm_next;
477170268Sdarrenr		ipf_hm_maplist = hm;
478170268Sdarrenr		hm->hm_hnext = ipf_hm_maptable[hv];
479170268Sdarrenr		hm->hm_phnext = ipf_hm_maptable + hv;
480170268Sdarrenr		if (ipf_hm_maptable[hv] != NULL)
481170268Sdarrenr			ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext;
482170268Sdarrenr		ipf_hm_maptable[hv] = hm;
48360852Sdarrenr		hm->hm_ipnat = np;
484145522Sdarrenr		hm->hm_srcip = src;
485145522Sdarrenr		hm->hm_dstip = dst;
48660852Sdarrenr		hm->hm_mapip = map;
48760852Sdarrenr		hm->hm_ref = 1;
488145522Sdarrenr		hm->hm_port = port;
48960852Sdarrenr	}
49060852Sdarrenr	return hm;
49153642Sguido}
49253642Sguido
49353642Sguido
494145522Sdarrenr/* ------------------------------------------------------------------------ */
495170268Sdarrenr/* Function:    fr_hostmapdel                                               */
496145522Sdarrenr/* Returns:     Nil                                                         */
497170268Sdarrenr/* Parameters:  hmp(I) - pointer to hostmap structure pointer               */
498145522Sdarrenr/* Write Locks: ipf_nat                                                     */
499145522Sdarrenr/*                                                                          */
500145522Sdarrenr/* Decrement the references to this hostmap structure by one.  If this      */
501145522Sdarrenr/* reaches zero then remove it and free it.                                 */
502145522Sdarrenr/* ------------------------------------------------------------------------ */
503170268Sdarrenrvoid fr_hostmapdel(hmp)
504170268Sdarrenrstruct hostmap **hmp;
50560852Sdarrenr{
506170268Sdarrenr	struct hostmap *hm;
507170268Sdarrenr
508170268Sdarrenr	hm = *hmp;
509170268Sdarrenr	*hmp = NULL;
510170268Sdarrenr
511145522Sdarrenr	hm->hm_ref--;
51260852Sdarrenr	if (hm->hm_ref == 0) {
513170268Sdarrenr		if (hm->hm_hnext)
514170268Sdarrenr			hm->hm_hnext->hm_phnext = hm->hm_phnext;
515170268Sdarrenr		*hm->hm_phnext = hm->hm_hnext;
51660852Sdarrenr		if (hm->hm_next)
51760852Sdarrenr			hm->hm_next->hm_pnext = hm->hm_pnext;
51860852Sdarrenr		*hm->hm_pnext = hm->hm_next;
51960852Sdarrenr		KFREE(hm);
52060852Sdarrenr	}
52160852Sdarrenr}
52260852Sdarrenr
52360852Sdarrenr
524145522Sdarrenr/* ------------------------------------------------------------------------ */
525145522Sdarrenr/* Function:    fix_outcksum                                                */
526145522Sdarrenr/* Returns:     Nil                                                         */
527145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
528145522Sdarrenr/*              sp(I)  - location of 16bit checksum to update               */
529145522Sdarrenr/*              n((I)  - amount to adjust checksum by                       */
530145522Sdarrenr/*                                                                          */
531145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going out.                 */
532145522Sdarrenr/* ------------------------------------------------------------------------ */
53380482Sdarrenrvoid fix_outcksum(fin, sp, n)
53480482Sdarrenrfr_info_t *fin;
53553642Sguidou_short *sp;
53653642Sguidou_32_t n;
53753642Sguido{
538145522Sdarrenr	u_short sumshort;
539145522Sdarrenr	u_32_t sum1;
54053642Sguido
541145522Sdarrenr	if (n == 0)
54253642Sguido		return;
543145522Sdarrenr
544145522Sdarrenr	if (n & NAT_HW_CKSUM) {
54580482Sdarrenr		n &= 0xffff;
54680482Sdarrenr		n += fin->fin_dlen;
54780482Sdarrenr		n = (n & 0xffff) + (n >> 16);
54855929Sguido		*sp = n & 0xffff;
54955929Sguido		return;
55055929Sguido	}
55153642Sguido	sum1 = (~ntohs(*sp)) & 0xffff;
55253642Sguido	sum1 += (n);
55353642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
55453642Sguido	/* Again */
55553642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
55653642Sguido	sumshort = ~(u_short)sum1;
55753642Sguido	*(sp) = htons(sumshort);
55853642Sguido}
55953642Sguido
56053642Sguido
561145522Sdarrenr/* ------------------------------------------------------------------------ */
562145522Sdarrenr/* Function:    fix_incksum                                                 */
563145522Sdarrenr/* Returns:     Nil                                                         */
564145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
565145522Sdarrenr/*              sp(I)  - location of 16bit checksum to update               */
566145522Sdarrenr/*              n((I)  - amount to adjust checksum by                       */
567145522Sdarrenr/*                                                                          */
568145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going in.                  */
569145522Sdarrenr/* ------------------------------------------------------------------------ */
57080482Sdarrenrvoid fix_incksum(fin, sp, n)
57180482Sdarrenrfr_info_t *fin;
57253642Sguidou_short *sp;
57353642Sguidou_32_t n;
57453642Sguido{
575145522Sdarrenr	u_short sumshort;
576145522Sdarrenr	u_32_t sum1;
57753642Sguido
578145522Sdarrenr	if (n == 0)
57953642Sguido		return;
580145522Sdarrenr
581145522Sdarrenr	if (n & NAT_HW_CKSUM) {
58280482Sdarrenr		n &= 0xffff;
58380482Sdarrenr		n += fin->fin_dlen;
58480482Sdarrenr		n = (n & 0xffff) + (n >> 16);
58555929Sguido		*sp = n & 0xffff;
58655929Sguido		return;
58755929Sguido	}
58853642Sguido	sum1 = (~ntohs(*sp)) & 0xffff;
58953642Sguido	sum1 += ~(n) & 0xffff;
59053642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
59153642Sguido	/* Again */
59253642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
59353642Sguido	sumshort = ~(u_short)sum1;
59453642Sguido	*(sp) = htons(sumshort);
59553642Sguido}
59653642Sguido
59753642Sguido
598145522Sdarrenr/* ------------------------------------------------------------------------ */
599145522Sdarrenr/* Function:    fix_datacksum                                               */
600145522Sdarrenr/* Returns:     Nil                                                         */
601145522Sdarrenr/* Parameters:  sp(I)  - location of 16bit checksum to update               */
602145522Sdarrenr/*              n((I)  - amount to adjust checksum by                       */
603145522Sdarrenr/*                                                                          */
604145522Sdarrenr/* Fix_datacksum is used *only* for the adjustments of checksums in the     */
605145522Sdarrenr/* data section of an IP packet.                                            */
606145522Sdarrenr/*                                                                          */
607145522Sdarrenr/* The only situation in which you need to do this is when NAT'ing an       */
608145522Sdarrenr/* ICMP error message. Such a message, contains in its body the IP header   */
609145522Sdarrenr/* of the original IP packet, that causes the error.                        */
610145522Sdarrenr/*                                                                          */
611145522Sdarrenr/* You can't use fix_incksum or fix_outcksum in that case, because for the  */
612145522Sdarrenr/* kernel the data section of the ICMP error is just data, and no special   */
613145522Sdarrenr/* processing like hardware cksum or ntohs processing have been done by the */
614145522Sdarrenr/* kernel on the data section.                                              */
615145522Sdarrenr/* ------------------------------------------------------------------------ */
61667614Sdarrenrvoid fix_datacksum(sp, n)
61767614Sdarrenru_short *sp;
61867614Sdarrenru_32_t n;
61967614Sdarrenr{
620145522Sdarrenr	u_short sumshort;
621145522Sdarrenr	u_32_t sum1;
62267614Sdarrenr
623145522Sdarrenr	if (n == 0)
62467614Sdarrenr		return;
62567614Sdarrenr
62667614Sdarrenr	sum1 = (~ntohs(*sp)) & 0xffff;
62767614Sdarrenr	sum1 += (n);
62867614Sdarrenr	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
62967614Sdarrenr	/* Again */
63067614Sdarrenr	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
63167614Sdarrenr	sumshort = ~(u_short)sum1;
63267614Sdarrenr	*(sp) = htons(sumshort);
63367614Sdarrenr}
63467614Sdarrenr
63553642Sguido
636145522Sdarrenr/* ------------------------------------------------------------------------ */
637145522Sdarrenr/* Function:    fr_nat_ioctl                                                */
638145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
639145522Sdarrenr/* Parameters:  data(I) - pointer to ioctl data                             */
640145522Sdarrenr/*              cmd(I)  - ioctl command integer                             */
641145522Sdarrenr/*              mode(I) - file mode bits used with open                     */
642145522Sdarrenr/*                                                                          */
643145522Sdarrenr/* Processes an ioctl call made to operate on the IP Filter NAT device.     */
644145522Sdarrenr/* ------------------------------------------------------------------------ */
645170268Sdarrenrint fr_nat_ioctl(data, cmd, mode, uid, ctx)
646145522Sdarrenrioctlcmd_t cmd;
64753642Sguidocaddr_t data;
648170268Sdarrenrint mode, uid;
649170268Sdarrenrvoid *ctx;
65053642Sguido{
651145522Sdarrenr	ipnat_t *nat, *nt, *n = NULL, **np = NULL;
65295418Sdarrenr	int error = 0, ret, arg, getlock;
65353642Sguido	ipnat_t natd;
654170268Sdarrenr	SPL_INT(s);
65553642Sguido
65653642Sguido#if (BSD >= 199306) && defined(_KERNEL)
657170268Sdarrenr# if defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 399002000)
658170268Sdarrenr	if ((mode & FWRITE) &&
659170268Sdarrenr	     kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL,
660170268Sdarrenr				     KAUTH_REQ_NETWORK_FIREWALL_FW,
661170268Sdarrenr				     NULL, NULL, NULL)) {
66253642Sguido		return EPERM;
663170268Sdarrenr	}
664170268Sdarrenr# else
665170268Sdarrenr	if ((securelevel >= 3) && (mode & FWRITE)) {
666170268Sdarrenr		return EPERM;
667170268Sdarrenr	}
668170268Sdarrenr# endif
66953642Sguido#endif
67053642Sguido
671145522Sdarrenr#if defined(__osf__) && defined(_KERNEL)
672145522Sdarrenr	getlock = 0;
673145522Sdarrenr#else
674145522Sdarrenr	getlock = (mode & NAT_LOCKHELD) ? 0 : 1;
675145522Sdarrenr#endif
676145522Sdarrenr
67753642Sguido	nat = NULL;     /* XXX gcc -Wuninitialized */
678145522Sdarrenr	if (cmd == (ioctlcmd_t)SIOCADNAT) {
679145522Sdarrenr		KMALLOC(nt, ipnat_t *);
680145522Sdarrenr	} else {
681145522Sdarrenr		nt = NULL;
682145522Sdarrenr	}
683145522Sdarrenr
684145522Sdarrenr	if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) {
68595418Sdarrenr		if (mode & NAT_SYSSPACE) {
68695418Sdarrenr			bcopy(data, (char *)&natd, sizeof(natd));
68795418Sdarrenr			error = 0;
68895418Sdarrenr		} else {
689145522Sdarrenr			error = fr_inobj(data, &natd, IPFOBJ_IPNAT);
69095418Sdarrenr		}
69164580Sdarrenr	}
69253642Sguido
693145522Sdarrenr	if (error != 0)
69460852Sdarrenr		goto done;
69560852Sdarrenr
69653642Sguido	/*
69753642Sguido	 * For add/delete, look to see if the NAT entry is already present
69853642Sguido	 */
699145522Sdarrenr	if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) {
70053642Sguido		nat = &natd;
701145522Sdarrenr		if (nat->in_v == 0)	/* For backward compat. */
702145522Sdarrenr			nat->in_v = 4;
70353642Sguido		nat->in_flags &= IPN_USERFLAGS;
70453642Sguido		if ((nat->in_redir & NAT_MAPBLK) == 0) {
70560852Sdarrenr			if ((nat->in_flags & IPN_SPLIT) == 0)
70660852Sdarrenr				nat->in_inip &= nat->in_inmsk;
70760852Sdarrenr			if ((nat->in_flags & IPN_IPRANGE) == 0)
70853642Sguido				nat->in_outip &= nat->in_outmsk;
70953642Sguido		}
710145522Sdarrenr		MUTEX_ENTER(&ipf_natio);
711145522Sdarrenr		for (np = &nat_list; ((n = *np) != NULL); np = &n->in_next)
712170268Sdarrenr			if (bcmp((char *)&nat->in_flags, (char *)&n->in_flags,
713170268Sdarrenr					IPN_CMPSIZ) == 0) {
714170268Sdarrenr				if (nat->in_redir == NAT_REDIRECT &&
715170268Sdarrenr				    nat->in_pnext != n->in_pnext)
716170268Sdarrenr					continue;
71753642Sguido				break;
718170268Sdarrenr			}
71953642Sguido	}
72053642Sguido
72153642Sguido	switch (cmd)
72253642Sguido	{
72355929Sguido#ifdef  IPFILTER_LOG
72455929Sguido	case SIOCIPFFB :
72560852Sdarrenr	{
72660852Sdarrenr		int tmp;
72760852Sdarrenr
72855929Sguido		if (!(mode & FWRITE))
72955929Sguido			error = EPERM;
73060852Sdarrenr		else {
73160852Sdarrenr			tmp = ipflog_clear(IPL_LOGNAT);
732170268Sdarrenr			error = BCOPYOUT((char *)&tmp, (char *)data,
733170268Sdarrenr					 sizeof(tmp));
734170268Sdarrenr			if (error != 0)
735170268Sdarrenr				error = EFAULT;
73660852Sdarrenr		}
73755929Sguido		break;
73860852Sdarrenr	}
739170268Sdarrenr
740145522Sdarrenr	case SIOCSETLG :
741145522Sdarrenr		if (!(mode & FWRITE))
742145522Sdarrenr			error = EPERM;
743145522Sdarrenr		else {
744170268Sdarrenr			error = BCOPYIN((char *)data, (char *)&nat_logging,
745170268Sdarrenr					sizeof(nat_logging));
746170268Sdarrenr			if (error != 0)
747170268Sdarrenr				error = EFAULT;
748145522Sdarrenr		}
749145522Sdarrenr		break;
750170268Sdarrenr
751145522Sdarrenr	case SIOCGETLG :
752170268Sdarrenr		error = BCOPYOUT((char *)&nat_logging, (char *)data,
753170268Sdarrenr				 sizeof(nat_logging));
754170268Sdarrenr		if (error != 0)
755170268Sdarrenr			error = EFAULT;
756145522Sdarrenr		break;
757170268Sdarrenr
758145522Sdarrenr	case FIONREAD :
759145522Sdarrenr		arg = iplused[IPL_LOGNAT];
760170268Sdarrenr		error = BCOPYOUT(&arg, data, sizeof(arg));
761170268Sdarrenr		if (error != 0)
762170268Sdarrenr			error = EFAULT;
763145522Sdarrenr		break;
76455929Sguido#endif
76553642Sguido	case SIOCADNAT :
76653642Sguido		if (!(mode & FWRITE)) {
76753642Sguido			error = EPERM;
768145522Sdarrenr		} else if (n != NULL) {
76953642Sguido			error = EEXIST;
770145522Sdarrenr		} else if (nt == NULL) {
771145522Sdarrenr			error = ENOMEM;
77253642Sguido		}
773145522Sdarrenr		if (error != 0) {
774145522Sdarrenr			MUTEX_EXIT(&ipf_natio);
77553642Sguido			break;
77653642Sguido		}
777145522Sdarrenr		bcopy((char *)nat, (char *)nt, sizeof(*n));
778145522Sdarrenr		error = nat_siocaddnat(nt, np, getlock);
779145522Sdarrenr		MUTEX_EXIT(&ipf_natio);
780145522Sdarrenr		if (error == 0)
781145522Sdarrenr			nt = NULL;
78253642Sguido		break;
783170268Sdarrenr
78453642Sguido	case SIOCRMNAT :
78553642Sguido		if (!(mode & FWRITE)) {
78653642Sguido			error = EPERM;
78753642Sguido			n = NULL;
788145522Sdarrenr		} else if (n == NULL) {
789145522Sdarrenr			error = ESRCH;
79053642Sguido		}
791145522Sdarrenr
792145522Sdarrenr		if (error != 0) {
793145522Sdarrenr			MUTEX_EXIT(&ipf_natio);
79453642Sguido			break;
79553642Sguido		}
796145522Sdarrenr		nat_siocdelnat(n, np, getlock);
797145522Sdarrenr
798145522Sdarrenr		MUTEX_EXIT(&ipf_natio);
79953642Sguido		n = NULL;
80053642Sguido		break;
801170268Sdarrenr
80253642Sguido	case SIOCGNATS :
80353642Sguido		nat_stats.ns_table[0] = nat_table[0];
80453642Sguido		nat_stats.ns_table[1] = nat_table[1];
80553642Sguido		nat_stats.ns_list = nat_list;
806170268Sdarrenr		nat_stats.ns_maptable = ipf_hm_maptable;
807170268Sdarrenr		nat_stats.ns_maplist = ipf_hm_maplist;
80853642Sguido		nat_stats.ns_nattab_sz = ipf_nattable_sz;
809145522Sdarrenr		nat_stats.ns_nattab_max = ipf_nattable_max;
81053642Sguido		nat_stats.ns_rultab_sz = ipf_natrules_sz;
81153642Sguido		nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz;
81280482Sdarrenr		nat_stats.ns_hostmap_sz = ipf_hostmap_sz;
81353642Sguido		nat_stats.ns_instances = nat_instances;
81453642Sguido		nat_stats.ns_apslist = ap_sess_list;
815170268Sdarrenr		nat_stats.ns_ticks = fr_ticks;
816145522Sdarrenr		error = fr_outobj(data, &nat_stats, IPFOBJ_NATSTAT);
81753642Sguido		break;
818170268Sdarrenr
81953642Sguido	case SIOCGNATL :
82053642Sguido	    {
82153642Sguido		natlookup_t nl;
82253642Sguido
823145522Sdarrenr		if (getlock) {
824145522Sdarrenr			READ_ENTER(&ipf_nat);
825145522Sdarrenr		}
826145522Sdarrenr		error = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP);
827145522Sdarrenr		if (error == 0) {
828145522Sdarrenr			if (nat_lookupredir(&nl) != NULL) {
829145522Sdarrenr				error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP);
830145522Sdarrenr			} else {
831145522Sdarrenr				error = ESRCH;
832145522Sdarrenr			}
833145522Sdarrenr		}
834145522Sdarrenr		if (getlock) {
835145522Sdarrenr			RWLOCK_EXIT(&ipf_nat);
836145522Sdarrenr		}
83753642Sguido		break;
83853642Sguido	    }
839170268Sdarrenr
84060852Sdarrenr	case SIOCIPFFL :	/* old SIOCFLNAT & SIOCCNATL */
84153642Sguido		if (!(mode & FWRITE)) {
84253642Sguido			error = EPERM;
84353642Sguido			break;
84453642Sguido		}
845145522Sdarrenr		if (getlock) {
846145522Sdarrenr			WRITE_ENTER(&ipf_nat);
847145522Sdarrenr		}
848170268Sdarrenr
849170268Sdarrenr		error = BCOPYIN(data, &arg, sizeof(arg));
850170268Sdarrenr		if (error != 0)
851170268Sdarrenr			error = EFAULT;
852170268Sdarrenr		else {
853170268Sdarrenr			if (arg == 0)
854170268Sdarrenr				ret = nat_flushtable();
855170268Sdarrenr			else if (arg == 1)
856170268Sdarrenr				ret = nat_clearlist();
857170268Sdarrenr			else
858170268Sdarrenr				ret = nat_extraflush(arg);
859170268Sdarrenr		}
860170268Sdarrenr
861145522Sdarrenr		if (getlock) {
862145522Sdarrenr			RWLOCK_EXIT(&ipf_nat);
86360852Sdarrenr		}
864145522Sdarrenr		if (error == 0) {
865170268Sdarrenr			error = BCOPYOUT(&ret, data, sizeof(ret));
866145522Sdarrenr		}
86753642Sguido		break;
868170268Sdarrenr
869145522Sdarrenr	case SIOCPROXY :
870170268Sdarrenr		error = appr_ioctl(data, cmd, mode, ctx);
871145522Sdarrenr		break;
872170268Sdarrenr
87360852Sdarrenr	case SIOCSTLCK :
874153876Sguido		if (!(mode & FWRITE)) {
875153876Sguido			error = EPERM;
876153876Sguido		} else {
877172776Sdarrenr			error = fr_lock(data, &fr_nat_lock);
878153876Sguido		}
87953642Sguido		break;
880170268Sdarrenr
88160852Sdarrenr	case SIOCSTPUT :
882153876Sguido		if ((mode & FWRITE) != 0) {
883145522Sdarrenr			error = fr_natputent(data, getlock);
884145522Sdarrenr		} else {
88560852Sdarrenr			error = EACCES;
886145522Sdarrenr		}
88760852Sdarrenr		break;
888170268Sdarrenr
88960852Sdarrenr	case SIOCSTGSZ :
890145522Sdarrenr		if (fr_nat_lock) {
891145522Sdarrenr			if (getlock) {
892145522Sdarrenr				READ_ENTER(&ipf_nat);
893145522Sdarrenr			}
89460852Sdarrenr			error = fr_natgetsz(data);
895145522Sdarrenr			if (getlock) {
896145522Sdarrenr				RWLOCK_EXIT(&ipf_nat);
897145522Sdarrenr			}
898145522Sdarrenr		} else
89960852Sdarrenr			error = EACCES;
90060852Sdarrenr		break;
901170268Sdarrenr
90260852Sdarrenr	case SIOCSTGET :
903145522Sdarrenr		if (fr_nat_lock) {
904145522Sdarrenr			if (getlock) {
905145522Sdarrenr				READ_ENTER(&ipf_nat);
906145522Sdarrenr			}
90760852Sdarrenr			error = fr_natgetent(data);
908145522Sdarrenr			if (getlock) {
909145522Sdarrenr				RWLOCK_EXIT(&ipf_nat);
910145522Sdarrenr			}
911145522Sdarrenr		} else
91260852Sdarrenr			error = EACCES;
91360852Sdarrenr		break;
914170268Sdarrenr
915170268Sdarrenr	case SIOCGENITER :
916170268Sdarrenr	    {
917170268Sdarrenr		ipfgeniter_t iter;
918170268Sdarrenr		ipftoken_t *token;
919170268Sdarrenr
920170268Sdarrenr		SPL_SCHED(s);
921170268Sdarrenr		error = fr_inobj(data, &iter, IPFOBJ_GENITER);
922170268Sdarrenr		if (error == 0) {
923170268Sdarrenr			token = ipf_findtoken(iter.igi_type, uid, ctx);
924170268Sdarrenr			if (token != NULL) {
925170268Sdarrenr				error  = nat_iterator(token, &iter);
926170268Sdarrenr			}
927170268Sdarrenr			RWLOCK_EXIT(&ipf_tokens);
928170268Sdarrenr		}
929170268Sdarrenr		SPL_X(s);
930170268Sdarrenr		break;
931170268Sdarrenr	    }
932170268Sdarrenr
933170268Sdarrenr	case SIOCIPFDELTOK :
934170268Sdarrenr		error = BCOPYIN((caddr_t)data, (caddr_t)&arg, sizeof(arg));
935170268Sdarrenr		if (error == 0) {
936170268Sdarrenr			SPL_SCHED(s);
937170268Sdarrenr			error = ipf_deltoken(arg, uid, ctx);
938170268Sdarrenr			SPL_X(s);
939170268Sdarrenr		} else {
940170268Sdarrenr			error = EFAULT;
941170268Sdarrenr		}
942170268Sdarrenr		break;
943170268Sdarrenr
944170268Sdarrenr	case SIOCGTQTAB :
945170268Sdarrenr		error = fr_outobj(data, nat_tqb, IPFOBJ_STATETQTAB);
946170268Sdarrenr		break;
947170268Sdarrenr
948172776Sdarrenr	case SIOCGTABL :
949172776Sdarrenr		error = nat_gettable(data);
950172776Sdarrenr		break;
951172776Sdarrenr
95253642Sguido	default :
95353642Sguido		error = EINVAL;
95453642Sguido		break;
95553642Sguido	}
95660852Sdarrenrdone:
957170268Sdarrenr	if (nt != NULL)
95853642Sguido		KFREE(nt);
95953642Sguido	return error;
96053642Sguido}
96153642Sguido
96253642Sguido
963145522Sdarrenr/* ------------------------------------------------------------------------ */
964145522Sdarrenr/* Function:    nat_siocaddnat                                              */
965145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
966145522Sdarrenr/* Parameters:  n(I)       - pointer to new NAT rule                        */
967145522Sdarrenr/*              np(I)      - pointer to where to insert new NAT rule        */
968145522Sdarrenr/*              getlock(I) - flag indicating if lock on ipf_nat is held     */
969145522Sdarrenr/* Mutex Locks: ipf_natio                                                   */
970145522Sdarrenr/*                                                                          */
971145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
972145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
973145522Sdarrenr/* NAT rule table(s).                                                       */
974145522Sdarrenr/* ------------------------------------------------------------------------ */
975145522Sdarrenrstatic int nat_siocaddnat(n, np, getlock)
976145522Sdarrenripnat_t *n, **np;
977145522Sdarrenrint getlock;
978145522Sdarrenr{
979145522Sdarrenr	int error = 0, i, j;
980145522Sdarrenr
981161356Sguido	if (nat_resolverule(n) != 0)
982161356Sguido		return ENOENT;
983145522Sdarrenr
984145522Sdarrenr	if ((n->in_age[0] == 0) && (n->in_age[1] != 0))
985145522Sdarrenr		return EINVAL;
986145522Sdarrenr
987145522Sdarrenr	n->in_use = 0;
988145522Sdarrenr	if (n->in_redir & NAT_MAPBLK)
989145522Sdarrenr		n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk);
990145522Sdarrenr	else if (n->in_flags & IPN_AUTOPORTMAP)
991145522Sdarrenr		n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk);
992145522Sdarrenr	else if (n->in_flags & IPN_IPRANGE)
993145522Sdarrenr		n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip);
994145522Sdarrenr	else if (n->in_flags & IPN_SPLIT)
995145522Sdarrenr		n->in_space = 2;
996145522Sdarrenr	else if (n->in_outmsk != 0)
997145522Sdarrenr		n->in_space = ~ntohl(n->in_outmsk);
998145522Sdarrenr	else
999145522Sdarrenr		n->in_space = 1;
1000145522Sdarrenr
1001145522Sdarrenr	/*
1002145522Sdarrenr	 * Calculate the number of valid IP addresses in the output
1003145522Sdarrenr	 * mapping range.  In all cases, the range is inclusive of
1004145522Sdarrenr	 * the start and ending IP addresses.
1005145522Sdarrenr	 * If to a CIDR address, lose 2: broadcast + network address
1006145522Sdarrenr	 *                               (so subtract 1)
1007145522Sdarrenr	 * If to a range, add one.
1008145522Sdarrenr	 * If to a single IP address, set to 1.
1009145522Sdarrenr	 */
1010145522Sdarrenr	if (n->in_space) {
1011145522Sdarrenr		if ((n->in_flags & IPN_IPRANGE) != 0)
1012145522Sdarrenr			n->in_space += 1;
1013145522Sdarrenr		else
1014145522Sdarrenr			n->in_space -= 1;
1015145522Sdarrenr	} else
1016145522Sdarrenr		n->in_space = 1;
1017145522Sdarrenr
1018145522Sdarrenr	if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) &&
1019145522Sdarrenr	    ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0))
1020145522Sdarrenr		n->in_nip = ntohl(n->in_outip) + 1;
1021145522Sdarrenr	else if ((n->in_flags & IPN_SPLIT) &&
1022145522Sdarrenr		 (n->in_redir & NAT_REDIRECT))
1023145522Sdarrenr		n->in_nip = ntohl(n->in_inip);
1024145522Sdarrenr	else
1025145522Sdarrenr		n->in_nip = ntohl(n->in_outip);
1026145522Sdarrenr	if (n->in_redir & NAT_MAP) {
1027145522Sdarrenr		n->in_pnext = ntohs(n->in_pmin);
1028145522Sdarrenr		/*
1029145522Sdarrenr		 * Multiply by the number of ports made available.
1030145522Sdarrenr		 */
1031145522Sdarrenr		if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) {
1032145522Sdarrenr			n->in_space *= (ntohs(n->in_pmax) -
1033145522Sdarrenr					ntohs(n->in_pmin) + 1);
1034145522Sdarrenr			/*
1035145522Sdarrenr			 * Because two different sources can map to
1036145522Sdarrenr			 * different destinations but use the same
1037145522Sdarrenr			 * local IP#/port #.
1038145522Sdarrenr			 * If the result is smaller than in_space, then
1039145522Sdarrenr			 * we may have wrapped around 32bits.
1040145522Sdarrenr			 */
1041145522Sdarrenr			i = n->in_inmsk;
1042145522Sdarrenr			if ((i != 0) && (i != 0xffffffff)) {
1043145522Sdarrenr				j = n->in_space * (~ntohl(i) + 1);
1044145522Sdarrenr				if (j >= n->in_space)
1045145522Sdarrenr					n->in_space = j;
1046145522Sdarrenr				else
1047145522Sdarrenr					n->in_space = 0xffffffff;
1048145522Sdarrenr			}
1049145522Sdarrenr		}
1050145522Sdarrenr		/*
1051145522Sdarrenr		 * If no protocol is specified, multiple by 256 to allow for
1052145522Sdarrenr		 * at least one IP:IP mapping per protocol.
1053145522Sdarrenr		 */
1054145522Sdarrenr		if ((n->in_flags & IPN_TCPUDPICMP) == 0) {
1055145522Sdarrenr				j = n->in_space * 256;
1056145522Sdarrenr				if (j >= n->in_space)
1057145522Sdarrenr					n->in_space = j;
1058145522Sdarrenr				else
1059145522Sdarrenr					n->in_space = 0xffffffff;
1060145522Sdarrenr		}
1061145522Sdarrenr	}
1062145522Sdarrenr
1063145522Sdarrenr	/* Otherwise, these fields are preset */
1064145522Sdarrenr
1065145522Sdarrenr	if (getlock) {
1066145522Sdarrenr		WRITE_ENTER(&ipf_nat);
1067145522Sdarrenr	}
1068145522Sdarrenr	n->in_next = NULL;
1069145522Sdarrenr	*np = n;
1070145522Sdarrenr
1071145522Sdarrenr	if (n->in_age[0] != 0)
1072145522Sdarrenr		n->in_tqehead[0] = fr_addtimeoutqueue(&nat_utqe, n->in_age[0]);
1073145522Sdarrenr
1074145522Sdarrenr	if (n->in_age[1] != 0)
1075145522Sdarrenr		n->in_tqehead[1] = fr_addtimeoutqueue(&nat_utqe, n->in_age[1]);
1076145522Sdarrenr
1077145522Sdarrenr	if (n->in_redir & NAT_REDIRECT) {
1078145522Sdarrenr		n->in_flags &= ~IPN_NOTDST;
1079145522Sdarrenr		nat_addrdr(n);
1080145522Sdarrenr	}
1081145522Sdarrenr	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
1082145522Sdarrenr		n->in_flags &= ~IPN_NOTSRC;
1083145522Sdarrenr		nat_addnat(n);
1084145522Sdarrenr	}
1085170268Sdarrenr	MUTEX_INIT(&n->in_lock, "ipnat rule lock");
1086170268Sdarrenr
1087145522Sdarrenr	n = NULL;
1088145522Sdarrenr	nat_stats.ns_rules++;
1089172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H)
1090145522Sdarrenr	pfil_delayed_copy = 0;
1091145522Sdarrenr#endif
1092145522Sdarrenr	if (getlock) {
1093145522Sdarrenr		RWLOCK_EXIT(&ipf_nat);			/* WRITE */
1094145522Sdarrenr	}
1095145522Sdarrenr
1096145522Sdarrenr	return error;
1097145522Sdarrenr}
1098145522Sdarrenr
1099145522Sdarrenr
1100145522Sdarrenr/* ------------------------------------------------------------------------ */
1101145522Sdarrenr/* Function:    nat_resolvrule                                              */
1102145522Sdarrenr/* Returns:     Nil                                                         */
1103145522Sdarrenr/* Parameters:  n(I)  - pointer to NAT rule                                 */
1104145522Sdarrenr/*                                                                          */
1105145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1106145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1107145522Sdarrenr/* NAT rule table(s).                                                       */
1108145522Sdarrenr/* ------------------------------------------------------------------------ */
1109161356Sguidostatic int nat_resolverule(n)
1110145522Sdarrenripnat_t *n;
1111145522Sdarrenr{
1112145522Sdarrenr	n->in_ifnames[0][LIFNAMSIZ - 1] = '\0';
1113145522Sdarrenr	n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4);
1114145522Sdarrenr
1115145522Sdarrenr	n->in_ifnames[1][LIFNAMSIZ - 1] = '\0';
1116145522Sdarrenr	if (n->in_ifnames[1][0] == '\0') {
1117145522Sdarrenr		(void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ);
1118145522Sdarrenr		n->in_ifps[1] = n->in_ifps[0];
1119145522Sdarrenr	} else {
1120161356Sguido		n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4);
1121145522Sdarrenr	}
1122145522Sdarrenr
1123145522Sdarrenr	if (n->in_plabel[0] != '\0') {
1124145522Sdarrenr		n->in_apr = appr_lookup(n->in_p, n->in_plabel);
1125161356Sguido		if (n->in_apr == NULL)
1126161356Sguido			return -1;
1127145522Sdarrenr	}
1128161356Sguido	return 0;
1129145522Sdarrenr}
1130145522Sdarrenr
1131145522Sdarrenr
1132145522Sdarrenr/* ------------------------------------------------------------------------ */
1133145522Sdarrenr/* Function:    nat_siocdelnat                                              */
1134145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
1135145522Sdarrenr/* Parameters:  n(I)       - pointer to new NAT rule                        */
1136145522Sdarrenr/*              np(I)      - pointer to where to insert new NAT rule        */
1137145522Sdarrenr/*              getlock(I) - flag indicating if lock on ipf_nat is held     */
1138145522Sdarrenr/* Mutex Locks: ipf_natio                                                   */
1139145522Sdarrenr/*                                                                          */
1140145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1141145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1142145522Sdarrenr/* NAT rule table(s).                                                       */
1143145522Sdarrenr/* ------------------------------------------------------------------------ */
1144145522Sdarrenrstatic void nat_siocdelnat(n, np, getlock)
1145145522Sdarrenripnat_t *n, **np;
1146145522Sdarrenrint getlock;
1147145522Sdarrenr{
1148145522Sdarrenr	if (getlock) {
1149145522Sdarrenr		WRITE_ENTER(&ipf_nat);
1150145522Sdarrenr	}
1151145522Sdarrenr	if (n->in_redir & NAT_REDIRECT)
1152145522Sdarrenr		nat_delrdr(n);
1153145522Sdarrenr	if (n->in_redir & (NAT_MAPBLK|NAT_MAP))
1154145522Sdarrenr		nat_delnat(n);
1155145522Sdarrenr	if (nat_list == NULL) {
1156145522Sdarrenr		nat_masks = 0;
1157145522Sdarrenr		rdr_masks = 0;
1158145522Sdarrenr	}
1159145522Sdarrenr
1160145522Sdarrenr	if (n->in_tqehead[0] != NULL) {
1161145522Sdarrenr		if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) {
1162145522Sdarrenr			fr_freetimeoutqueue(n->in_tqehead[1]);
1163145522Sdarrenr		}
1164145522Sdarrenr	}
1165145522Sdarrenr
1166145522Sdarrenr	if (n->in_tqehead[1] != NULL) {
1167145522Sdarrenr		if (fr_deletetimeoutqueue(n->in_tqehead[1]) == 0) {
1168145522Sdarrenr			fr_freetimeoutqueue(n->in_tqehead[1]);
1169145522Sdarrenr		}
1170145522Sdarrenr	}
1171145522Sdarrenr
1172145522Sdarrenr	*np = n->in_next;
1173145522Sdarrenr
1174145522Sdarrenr	if (n->in_use == 0) {
1175145522Sdarrenr		if (n->in_apr)
1176145522Sdarrenr			appr_free(n->in_apr);
1177172776Sdarrenr		MUTEX_DESTROY(&n->in_lock);
1178145522Sdarrenr		KFREE(n);
1179145522Sdarrenr		nat_stats.ns_rules--;
1180172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H)
1181145522Sdarrenr		if (nat_stats.ns_rules == 0)
1182145522Sdarrenr			pfil_delayed_copy = 1;
1183145522Sdarrenr#endif
1184145522Sdarrenr	} else {
1185145522Sdarrenr		n->in_flags |= IPN_DELETE;
1186145522Sdarrenr		n->in_next = NULL;
1187145522Sdarrenr	}
1188145522Sdarrenr	if (getlock) {
1189145522Sdarrenr		RWLOCK_EXIT(&ipf_nat);			/* READ/WRITE */
1190145522Sdarrenr	}
1191145522Sdarrenr}
1192145522Sdarrenr
1193145522Sdarrenr
1194145522Sdarrenr/* ------------------------------------------------------------------------ */
1195145522Sdarrenr/* Function:    fr_natgetsz                                                 */
1196145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1197145522Sdarrenr/* Parameters:  data(I) - pointer to natget structure with kernel pointer   */
1198145522Sdarrenr/*                        get the size of.                                  */
1199145522Sdarrenr/*                                                                          */
1200145522Sdarrenr/* Handle SIOCSTGSZ.                                                        */
1201145522Sdarrenr/* Return the size of the nat list entry to be copied back to user space.   */
1202145522Sdarrenr/* The size of the entry is stored in the ng_sz field and the enture natget */
1203145522Sdarrenr/* structure is copied back to the user.                                    */
1204145522Sdarrenr/* ------------------------------------------------------------------------ */
120560852Sdarrenrstatic int fr_natgetsz(data)
120660852Sdarrenrcaddr_t data;
120760852Sdarrenr{
120860852Sdarrenr	ap_session_t *aps;
120960852Sdarrenr	nat_t *nat, *n;
121060852Sdarrenr	natget_t ng;
121160852Sdarrenr
1212170268Sdarrenr	if (BCOPYIN(data, &ng, sizeof(ng)) != 0)
1213170268Sdarrenr		return EFAULT;
121460852Sdarrenr
121560852Sdarrenr	nat = ng.ng_ptr;
121660852Sdarrenr	if (!nat) {
121760852Sdarrenr		nat = nat_instances;
121860852Sdarrenr		ng.ng_sz = 0;
1219145522Sdarrenr		/*
1220145522Sdarrenr		 * Empty list so the size returned is 0.  Simple.
1221145522Sdarrenr		 */
122260852Sdarrenr		if (nat == NULL) {
1223170268Sdarrenr			if (BCOPYOUT(&ng, data, sizeof(ng)) != 0)
1224170268Sdarrenr				return EFAULT;
1225145522Sdarrenr			return 0;
122660852Sdarrenr		}
122760852Sdarrenr	} else {
122860852Sdarrenr		/*
122960852Sdarrenr		 * Make sure the pointer we're copying from exists in the
123060852Sdarrenr		 * current list of entries.  Security precaution to prevent
123160852Sdarrenr		 * copying of random kernel data.
123260852Sdarrenr		 */
123360852Sdarrenr		for (n = nat_instances; n; n = n->nat_next)
123460852Sdarrenr			if (n == nat)
123560852Sdarrenr				break;
123660852Sdarrenr		if (!n)
123760852Sdarrenr			return ESRCH;
123860852Sdarrenr	}
123960852Sdarrenr
1240145522Sdarrenr	/*
1241145522Sdarrenr	 * Incluse any space required for proxy data structures.
1242145522Sdarrenr	 */
124360852Sdarrenr	ng.ng_sz = sizeof(nat_save_t);
124460852Sdarrenr	aps = nat->nat_aps;
1245145522Sdarrenr	if (aps != NULL) {
1246145522Sdarrenr		ng.ng_sz += sizeof(ap_session_t) - 4;
1247145522Sdarrenr		if (aps->aps_data != 0)
1248145522Sdarrenr			ng.ng_sz += aps->aps_psiz;
124960852Sdarrenr	}
125060852Sdarrenr
1251170268Sdarrenr	if (BCOPYOUT(&ng, data, sizeof(ng)) != 0)
1252170268Sdarrenr		return EFAULT;
1253145522Sdarrenr	return 0;
125460852Sdarrenr}
125560852Sdarrenr
125660852Sdarrenr
1257145522Sdarrenr/* ------------------------------------------------------------------------ */
1258145522Sdarrenr/* Function:    fr_natgetent                                                */
1259145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1260145522Sdarrenr/* Parameters:  data(I) - pointer to natget structure with kernel pointer   */
1261145522Sdarrenr/*                        to NAT structure to copy out.                     */
1262145522Sdarrenr/*                                                                          */
1263145522Sdarrenr/* Handle SIOCSTGET.                                                        */
1264145522Sdarrenr/* Copies out NAT entry to user space.  Any additional data held for a      */
1265145522Sdarrenr/* proxy is also copied, as to is the NAT rule which was responsible for it */
1266145522Sdarrenr/* ------------------------------------------------------------------------ */
126760852Sdarrenrstatic int fr_natgetent(data)
126860852Sdarrenrcaddr_t data;
126960852Sdarrenr{
1270145522Sdarrenr	int error, outsize;
127160852Sdarrenr	ap_session_t *aps;
1272145522Sdarrenr	nat_save_t *ipn, ipns;
1273145522Sdarrenr	nat_t *n, *nat;
127460852Sdarrenr
1275145522Sdarrenr	error = fr_inobj(data, &ipns, IPFOBJ_NATSAVE);
1276145522Sdarrenr	if (error != 0)
1277145522Sdarrenr		return error;
127860852Sdarrenr
1279145522Sdarrenr	if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920))
1280145522Sdarrenr		return EINVAL;
1281145522Sdarrenr
1282145522Sdarrenr	KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize);
1283145522Sdarrenr	if (ipn == NULL)
1284145522Sdarrenr		return ENOMEM;
1285145522Sdarrenr
1286145522Sdarrenr	ipn->ipn_dsize = ipns.ipn_dsize;
1287145522Sdarrenr	nat = ipns.ipn_next;
1288145522Sdarrenr	if (nat == NULL) {
128960852Sdarrenr		nat = nat_instances;
129060852Sdarrenr		if (nat == NULL) {
129160852Sdarrenr			if (nat_instances == NULL)
1292145522Sdarrenr				error = ENOENT;
1293145522Sdarrenr			goto finished;
129460852Sdarrenr		}
129560852Sdarrenr	} else {
129660852Sdarrenr		/*
129760852Sdarrenr		 * Make sure the pointer we're copying from exists in the
129860852Sdarrenr		 * current list of entries.  Security precaution to prevent
129960852Sdarrenr		 * copying of random kernel data.
130060852Sdarrenr		 */
130160852Sdarrenr		for (n = nat_instances; n; n = n->nat_next)
130260852Sdarrenr			if (n == nat)
130360852Sdarrenr				break;
1304145522Sdarrenr		if (n == NULL) {
1305145522Sdarrenr			error = ESRCH;
1306145522Sdarrenr			goto finished;
1307145522Sdarrenr		}
130860852Sdarrenr	}
1309145522Sdarrenr	ipn->ipn_next = nat->nat_next;
131060852Sdarrenr
1311145522Sdarrenr	/*
1312145522Sdarrenr	 * Copy the NAT structure.
1313145522Sdarrenr	 */
1314145522Sdarrenr	bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat));
131560852Sdarrenr
1316145522Sdarrenr	/*
1317145522Sdarrenr	 * If we have a pointer to the NAT rule it belongs to, save that too.
1318145522Sdarrenr	 */
1319145522Sdarrenr	if (nat->nat_ptr != NULL)
1320145522Sdarrenr		bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat,
1321145522Sdarrenr		      sizeof(ipn->ipn_ipnat));
132260852Sdarrenr
1323145522Sdarrenr	/*
1324145522Sdarrenr	 * If we also know the NAT entry has an associated filter rule,
1325145522Sdarrenr	 * save that too.
1326145522Sdarrenr	 */
1327145522Sdarrenr	if (nat->nat_fr != NULL)
1328145522Sdarrenr		bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr,
1329145522Sdarrenr		      sizeof(ipn->ipn_fr));
133060852Sdarrenr
1331145522Sdarrenr	/*
1332145522Sdarrenr	 * Last but not least, if there is an application proxy session set
1333145522Sdarrenr	 * up for this NAT entry, then copy that out too, including any
1334145522Sdarrenr	 * private data saved along side it by the proxy.
1335145522Sdarrenr	 */
1336145522Sdarrenr	aps = nat->nat_aps;
1337145522Sdarrenr	outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data);
1338145522Sdarrenr	if (aps != NULL) {
1339145522Sdarrenr		char *s;
134060852Sdarrenr
1341145522Sdarrenr		if (outsize < sizeof(*aps)) {
1342145522Sdarrenr			error = ENOBUFS;
1343145522Sdarrenr			goto finished;
134460852Sdarrenr		}
1345145522Sdarrenr
1346145522Sdarrenr		s = ipn->ipn_data;
1347145522Sdarrenr		bcopy((char *)aps, s, sizeof(*aps));
1348145522Sdarrenr		s += sizeof(*aps);
1349145522Sdarrenr		outsize -= sizeof(*aps);
1350145522Sdarrenr		if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz))
1351145522Sdarrenr			bcopy(aps->aps_data, s, aps->aps_psiz);
1352145522Sdarrenr		else
1353145522Sdarrenr			error = ENOBUFS;
135460852Sdarrenr	}
1355145522Sdarrenr	if (error == 0) {
1356145522Sdarrenr		error = fr_outobjsz(data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize);
1357145522Sdarrenr	}
1358145522Sdarrenr
1359145522Sdarrenrfinished:
1360145522Sdarrenr	if (ipn != NULL) {
1361145522Sdarrenr		KFREES(ipn, ipns.ipn_dsize);
1362145522Sdarrenr	}
136364580Sdarrenr	return error;
136460852Sdarrenr}
136560852Sdarrenr
136660852Sdarrenr
1367145522Sdarrenr/* ------------------------------------------------------------------------ */
1368145522Sdarrenr/* Function:    fr_natputent                                                */
1369145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1370145522Sdarrenr/* Parameters:  data(I) -     pointer to natget structure with NAT          */
1371145522Sdarrenr/*                            structure information to load into the kernel */
1372145522Sdarrenr/*              getlock(I) - flag indicating whether or not a write lock    */
1373145522Sdarrenr/*                           on ipf_nat is already held.                    */
1374145522Sdarrenr/*                                                                          */
1375145522Sdarrenr/* Handle SIOCSTPUT.                                                        */
1376145522Sdarrenr/* Loads a NAT table entry from user space, including a NAT rule, proxy and */
1377145522Sdarrenr/* firewall rule data structures, if pointers to them indicate so.          */
1378145522Sdarrenr/* ------------------------------------------------------------------------ */
1379145522Sdarrenrstatic int fr_natputent(data, getlock)
138060852Sdarrenrcaddr_t data;
1381145522Sdarrenrint getlock;
138260852Sdarrenr{
1383145522Sdarrenr	nat_save_t ipn, *ipnn;
138460852Sdarrenr	ap_session_t *aps;
1385145522Sdarrenr	nat_t *n, *nat;
138660852Sdarrenr	frentry_t *fr;
1387145522Sdarrenr	fr_info_t fin;
138860852Sdarrenr	ipnat_t *in;
138960852Sdarrenr	int error;
139060852Sdarrenr
1391145522Sdarrenr	error = fr_inobj(data, &ipn, IPFOBJ_NATSAVE);
1392145522Sdarrenr	if (error != 0)
1393145522Sdarrenr		return error;
1394145522Sdarrenr
1395145522Sdarrenr	/*
1396145522Sdarrenr	 * Initialise early because of code at junkput label.
1397145522Sdarrenr	 */
1398145522Sdarrenr	in = NULL;
1399145522Sdarrenr	aps = NULL;
140064580Sdarrenr	nat = NULL;
1401145522Sdarrenr	ipnn = NULL;
1402170268Sdarrenr	fr = NULL;
1403145522Sdarrenr
1404145522Sdarrenr	/*
1405145522Sdarrenr	 * New entry, copy in the rest of the NAT entry if it's size is more
1406145522Sdarrenr	 * than just the nat_t structure.
1407145522Sdarrenr	 */
1408145522Sdarrenr	if (ipn.ipn_dsize > sizeof(ipn)) {
1409145522Sdarrenr		if (ipn.ipn_dsize > 81920) {
1410145522Sdarrenr			error = ENOMEM;
1411145522Sdarrenr			goto junkput;
1412145522Sdarrenr		}
1413145522Sdarrenr
1414145522Sdarrenr		KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize);
141560852Sdarrenr		if (ipnn == NULL)
141660852Sdarrenr			return ENOMEM;
1417145522Sdarrenr
1418145522Sdarrenr		error = fr_inobjsz(data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize);
1419145522Sdarrenr		if (error != 0) {
142064580Sdarrenr			error = EFAULT;
142164580Sdarrenr			goto junkput;
142264580Sdarrenr		}
142360852Sdarrenr	} else
1424145522Sdarrenr		ipnn = &ipn;
142560852Sdarrenr
142660852Sdarrenr	KMALLOC(nat, nat_t *);
142764580Sdarrenr	if (nat == NULL) {
1428145522Sdarrenr		error = ENOMEM;
142964580Sdarrenr		goto junkput;
143064580Sdarrenr	}
143160852Sdarrenr
1432145522Sdarrenr	bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat));
143360852Sdarrenr	/*
143460852Sdarrenr	 * Initialize all these so that nat_delete() doesn't cause a crash.
143560852Sdarrenr	 */
1436145522Sdarrenr	bzero((char *)nat, offsetof(struct nat, nat_tqe));
1437145522Sdarrenr	nat->nat_tqe.tqe_pnext = NULL;
1438145522Sdarrenr	nat->nat_tqe.tqe_next = NULL;
1439145522Sdarrenr	nat->nat_tqe.tqe_ifq = NULL;
1440145522Sdarrenr	nat->nat_tqe.tqe_parent = nat;
144160852Sdarrenr
144260852Sdarrenr	/*
144360852Sdarrenr	 * Restore the rule associated with this nat session
144460852Sdarrenr	 */
1445145522Sdarrenr	in = ipnn->ipn_nat.nat_ptr;
1446145522Sdarrenr	if (in != NULL) {
144760852Sdarrenr		KMALLOC(in, ipnat_t *);
1448145522Sdarrenr		nat->nat_ptr = in;
144960852Sdarrenr		if (in == NULL) {
145060852Sdarrenr			error = ENOMEM;
145160852Sdarrenr			goto junkput;
145260852Sdarrenr		}
1453145522Sdarrenr		bzero((char *)in, offsetof(struct ipnat, in_next6));
1454145522Sdarrenr		bcopy((char *)&ipnn->ipn_ipnat, (char *)in, sizeof(*in));
145560852Sdarrenr		in->in_use = 1;
145660852Sdarrenr		in->in_flags |= IPN_DELETE;
1457145522Sdarrenr
1458145522Sdarrenr		ATOMIC_INC(nat_stats.ns_rules);
1459145522Sdarrenr
1460161356Sguido		if (nat_resolverule(in) != 0) {
1461161356Sguido			error = ESRCH;
1462161356Sguido			goto junkput;
1463161356Sguido		}
1464145522Sdarrenr	}
1465145522Sdarrenr
1466145522Sdarrenr	/*
1467145522Sdarrenr	 * Check that the NAT entry doesn't already exist in the kernel.
1468161356Sguido	 *
1469161356Sguido	 * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry.  To do
1470161356Sguido	 * this, we check to see if the inbound combination of addresses and
1471161356Sguido	 * ports is already known.  Similar logic is applied for NAT_INBOUND.
1472161356Sguido	 *
1473145522Sdarrenr	 */
1474145522Sdarrenr	bzero((char *)&fin, sizeof(fin));
1475145522Sdarrenr	fin.fin_p = nat->nat_p;
1476145522Sdarrenr	if (nat->nat_dir == NAT_OUTBOUND) {
1477170268Sdarrenr		fin.fin_ifp = nat->nat_ifps[0];
1478145522Sdarrenr		fin.fin_data[0] = ntohs(nat->nat_oport);
1479145522Sdarrenr		fin.fin_data[1] = ntohs(nat->nat_outport);
1480153876Sguido		if (getlock) {
1481153876Sguido			READ_ENTER(&ipf_nat);
1482153876Sguido		}
1483161356Sguido		n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p,
1484161356Sguido				 nat->nat_oip, nat->nat_inip);
1485153876Sguido		if (getlock) {
1486153876Sguido			RWLOCK_EXIT(&ipf_nat);
1487153876Sguido		}
1488153876Sguido		if (n != NULL) {
1489145522Sdarrenr			error = EEXIST;
1490145522Sdarrenr			goto junkput;
149160852Sdarrenr		}
1492145522Sdarrenr	} else if (nat->nat_dir == NAT_INBOUND) {
1493170268Sdarrenr		fin.fin_ifp = nat->nat_ifps[0];
1494145522Sdarrenr		fin.fin_data[0] = ntohs(nat->nat_outport);
1495145522Sdarrenr		fin.fin_data[1] = ntohs(nat->nat_oport);
1496153876Sguido		if (getlock) {
1497153876Sguido			READ_ENTER(&ipf_nat);
1498153876Sguido		}
1499161356Sguido		n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p,
1500161356Sguido				  nat->nat_outip, nat->nat_oip);
1501153876Sguido		if (getlock) {
1502153876Sguido			RWLOCK_EXIT(&ipf_nat);
1503153876Sguido		}
1504153876Sguido		if (n != NULL) {
1505145522Sdarrenr			error = EEXIST;
1506145522Sdarrenr			goto junkput;
1507145522Sdarrenr		}
1508145522Sdarrenr	} else {
1509145522Sdarrenr		error = EINVAL;
1510145522Sdarrenr		goto junkput;
151160852Sdarrenr	}
151260852Sdarrenr
151360852Sdarrenr	/*
151460852Sdarrenr	 * Restore ap_session_t structure.  Include the private data allocated
151560852Sdarrenr	 * if it was there.
151660852Sdarrenr	 */
1517145522Sdarrenr	aps = nat->nat_aps;
1518145522Sdarrenr	if (aps != NULL) {
151960852Sdarrenr		KMALLOC(aps, ap_session_t *);
1520145522Sdarrenr		nat->nat_aps = aps;
152160852Sdarrenr		if (aps == NULL) {
152260852Sdarrenr			error = ENOMEM;
152360852Sdarrenr			goto junkput;
152460852Sdarrenr		}
152560852Sdarrenr		bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps));
1526145522Sdarrenr		if (in != NULL)
152760852Sdarrenr			aps->aps_apr = in->in_apr;
1528145522Sdarrenr		else
1529145522Sdarrenr			aps->aps_apr = NULL;
1530145522Sdarrenr		if (aps->aps_psiz != 0) {
1531145522Sdarrenr			if (aps->aps_psiz > 81920) {
1532145522Sdarrenr				error = ENOMEM;
1533145522Sdarrenr				goto junkput;
1534145522Sdarrenr			}
153560852Sdarrenr			KMALLOCS(aps->aps_data, void *, aps->aps_psiz);
153660852Sdarrenr			if (aps->aps_data == NULL) {
153760852Sdarrenr				error = ENOMEM;
153860852Sdarrenr				goto junkput;
153960852Sdarrenr			}
154060852Sdarrenr			bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data,
154160852Sdarrenr			      aps->aps_psiz);
154260852Sdarrenr		} else {
154360852Sdarrenr			aps->aps_psiz = 0;
154460852Sdarrenr			aps->aps_data = NULL;
154560852Sdarrenr		}
154660852Sdarrenr	}
154760852Sdarrenr
154860852Sdarrenr	/*
154960852Sdarrenr	 * If there was a filtering rule associated with this entry then
155060852Sdarrenr	 * build up a new one.
155160852Sdarrenr	 */
1552145522Sdarrenr	fr = nat->nat_fr;
155360852Sdarrenr	if (fr != NULL) {
1554145522Sdarrenr		if ((nat->nat_flags & SI_NEWFR) != 0) {
155560852Sdarrenr			KMALLOC(fr, frentry_t *);
155660852Sdarrenr			nat->nat_fr = fr;
155760852Sdarrenr			if (fr == NULL) {
155860852Sdarrenr				error = ENOMEM;
155960852Sdarrenr				goto junkput;
156060852Sdarrenr			}
1561145522Sdarrenr			ipnn->ipn_nat.nat_fr = fr;
1562145522Sdarrenr			fr->fr_ref = 1;
1563145522Sdarrenr			(void) fr_outobj(data, ipnn, IPFOBJ_NATSAVE);
1564145522Sdarrenr			bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr));
1565161356Sguido
1566161356Sguido			fr->fr_ref = 1;
1567161356Sguido			fr->fr_dsize = 0;
1568161356Sguido			fr->fr_data = NULL;
1569161356Sguido			fr->fr_type = FR_T_NONE;
1570161356Sguido
1571145522Sdarrenr			MUTEX_NUKE(&fr->fr_lock);
1572145522Sdarrenr			MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock");
157360852Sdarrenr		} else {
1574153876Sguido			if (getlock) {
1575153876Sguido				READ_ENTER(&ipf_nat);
1576153876Sguido			}
157760852Sdarrenr			for (n = nat_instances; n; n = n->nat_next)
157860852Sdarrenr				if (n->nat_fr == fr)
157960852Sdarrenr					break;
1580145522Sdarrenr
1581145522Sdarrenr			if (n != NULL) {
1582145522Sdarrenr				MUTEX_ENTER(&fr->fr_lock);
1583145522Sdarrenr				fr->fr_ref++;
1584145522Sdarrenr				MUTEX_EXIT(&fr->fr_lock);
1585145522Sdarrenr			}
1586153876Sguido			if (getlock) {
1587153876Sguido				RWLOCK_EXIT(&ipf_nat);
1588153876Sguido			}
1589145522Sdarrenr
159060852Sdarrenr			if (!n) {
159160852Sdarrenr				error = ESRCH;
159260852Sdarrenr				goto junkput;
159360852Sdarrenr			}
159460852Sdarrenr		}
159560852Sdarrenr	}
159660852Sdarrenr
1597145522Sdarrenr	if (ipnn != &ipn) {
1598145522Sdarrenr		KFREES(ipnn, ipn.ipn_dsize);
1599145522Sdarrenr		ipnn = NULL;
1600145522Sdarrenr	}
1601145522Sdarrenr
1602145522Sdarrenr	if (getlock) {
1603145522Sdarrenr		WRITE_ENTER(&ipf_nat);
1604145522Sdarrenr	}
1605145522Sdarrenr	error = nat_insert(nat, nat->nat_rev);
1606145522Sdarrenr	if ((error == 0) && (aps != NULL)) {
1607145522Sdarrenr		aps->aps_next = ap_sess_list;
1608145522Sdarrenr		ap_sess_list = aps;
1609145522Sdarrenr	}
1610145522Sdarrenr	if (getlock) {
1611145522Sdarrenr		RWLOCK_EXIT(&ipf_nat);
1612145522Sdarrenr	}
1613145522Sdarrenr
1614145522Sdarrenr	if (error == 0)
1615145522Sdarrenr		return 0;
1616145522Sdarrenr
1617145522Sdarrenr	error = ENOMEM;
1618145522Sdarrenr
161960852Sdarrenrjunkput:
1620145522Sdarrenr	if (fr != NULL)
1621170268Sdarrenr		(void) fr_derefrule(&fr);
1622145522Sdarrenr
1623145522Sdarrenr	if ((ipnn != NULL) && (ipnn != &ipn)) {
1624145522Sdarrenr		KFREES(ipnn, ipn.ipn_dsize);
1625145522Sdarrenr	}
1626145522Sdarrenr	if (nat != NULL) {
1627145522Sdarrenr		if (aps != NULL) {
1628145522Sdarrenr			if (aps->aps_data != NULL) {
1629145522Sdarrenr				KFREES(aps->aps_data, aps->aps_psiz);
1630145522Sdarrenr			}
1631145522Sdarrenr			KFREE(aps);
1632145522Sdarrenr		}
1633145522Sdarrenr		if (in != NULL) {
1634145522Sdarrenr			if (in->in_apr)
1635145522Sdarrenr				appr_free(in->in_apr);
1636145522Sdarrenr			KFREE(in);
1637145522Sdarrenr		}
1638145522Sdarrenr		KFREE(nat);
1639145522Sdarrenr	}
164060852Sdarrenr	return error;
164160852Sdarrenr}
164260852Sdarrenr
164360852Sdarrenr
1644145522Sdarrenr/* ------------------------------------------------------------------------ */
1645145522Sdarrenr/* Function:    nat_delete                                                  */
1646145522Sdarrenr/* Returns:     Nil                                                         */
1647145522Sdarrenr/* Parameters:  natd(I)    - pointer to NAT structure to delete             */
1648145522Sdarrenr/*              logtype(I) - type of LOG record to create before deleting   */
1649145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
1650145522Sdarrenr/*                                                                          */
1651145522Sdarrenr/* Delete a nat entry from the various lists and table.  If NAT logging is  */
1652145522Sdarrenr/* enabled then generate a NAT log record for this event.                   */
1653145522Sdarrenr/* ------------------------------------------------------------------------ */
1654172776Sdarrenrvoid nat_delete(nat, logtype)
1655145522Sdarrenrstruct nat *nat;
1656145522Sdarrenrint logtype;
165753642Sguido{
165853642Sguido	struct ipnat *ipn;
1659172776Sdarrenr	int removed = 0;
166053642Sguido
1661145522Sdarrenr	if (logtype != 0 && nat_logging != 0)
1662145522Sdarrenr		nat_log(nat, logtype);
166353642Sguido
1664145522Sdarrenr	/*
1665145522Sdarrenr	 * Take it as a general indication that all the pointers are set if
1666145522Sdarrenr	 * nat_pnext is set.
1667145522Sdarrenr	 */
1668145522Sdarrenr	if (nat->nat_pnext != NULL) {
1669172776Sdarrenr		removed = 1;
1670172776Sdarrenr
1671145522Sdarrenr		nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
1672145522Sdarrenr		nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
1673145522Sdarrenr
1674145522Sdarrenr		*nat->nat_pnext = nat->nat_next;
1675145522Sdarrenr		if (nat->nat_next != NULL) {
1676145522Sdarrenr			nat->nat_next->nat_pnext = nat->nat_pnext;
1677145522Sdarrenr			nat->nat_next = NULL;
1678145522Sdarrenr		}
1679145522Sdarrenr		nat->nat_pnext = NULL;
1680145522Sdarrenr
1681145522Sdarrenr		*nat->nat_phnext[0] = nat->nat_hnext[0];
1682145522Sdarrenr		if (nat->nat_hnext[0] != NULL) {
1683145522Sdarrenr			nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
1684145522Sdarrenr			nat->nat_hnext[0] = NULL;
1685145522Sdarrenr		}
1686145522Sdarrenr		nat->nat_phnext[0] = NULL;
1687145522Sdarrenr
1688145522Sdarrenr		*nat->nat_phnext[1] = nat->nat_hnext[1];
1689145522Sdarrenr		if (nat->nat_hnext[1] != NULL) {
1690145522Sdarrenr			nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
1691145522Sdarrenr			nat->nat_hnext[1] = NULL;
1692145522Sdarrenr		}
1693145522Sdarrenr		nat->nat_phnext[1] = NULL;
1694145522Sdarrenr
1695145522Sdarrenr		if ((nat->nat_flags & SI_WILDP) != 0)
1696145522Sdarrenr			nat_stats.ns_wilds--;
169753642Sguido	}
169860852Sdarrenr
1699145522Sdarrenr	if (nat->nat_me != NULL) {
1700145522Sdarrenr		*nat->nat_me = NULL;
1701145522Sdarrenr		nat->nat_me = NULL;
1702145522Sdarrenr	}
170360852Sdarrenr
1704170268Sdarrenr	if (nat->nat_tqe.tqe_ifq != NULL)
1705170268Sdarrenr		fr_deletequeueentry(&nat->nat_tqe);
1706145522Sdarrenr
1707170268Sdarrenr	if (logtype == NL_EXPIRE)
1708170268Sdarrenr		nat_stats.ns_expire++;
1709170268Sdarrenr
1710172776Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
1711172776Sdarrenr	/*
1712172776Sdarrenr	 * NL_DESTROY should only be passed in when we've got nat_ref >= 2.
1713172776Sdarrenr	 * This happens when a nat'd packet is blocked and we want to throw
1714172776Sdarrenr	 * away the NAT session.
1715172776Sdarrenr	 */
1716172776Sdarrenr	if (logtype == NL_DESTROY) {
1717172776Sdarrenr		if (nat->nat_ref > 2) {
1718172776Sdarrenr			nat->nat_ref -= 2;
1719172776Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
1720172776Sdarrenr			if (removed)
1721172776Sdarrenr				nat_stats.ns_orphans++;
1722172776Sdarrenr			return;
1723172776Sdarrenr		}
1724172776Sdarrenr	} else if (nat->nat_ref > 1) {
1725172776Sdarrenr		nat->nat_ref--;
1726172776Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
1727172776Sdarrenr		if (removed)
1728172776Sdarrenr			nat_stats.ns_orphans++;
1729145522Sdarrenr		return;
1730145522Sdarrenr	}
1731172776Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
1732170268Sdarrenr
1733161356Sguido	/*
1734172776Sdarrenr	 * At this point, nat_ref is 1, doing "--" would make it 0..
1735161356Sguido	 */
1736172776Sdarrenr	nat->nat_ref = 0;
1737172776Sdarrenr	if (!removed)
1738172776Sdarrenr		nat_stats.ns_orphans--;
1739145522Sdarrenr
1740145522Sdarrenr#ifdef	IPFILTER_SYNC
1741145522Sdarrenr	if (nat->nat_sync)
1742145522Sdarrenr		ipfsync_del(nat->nat_sync);
1743145522Sdarrenr#endif
1744145522Sdarrenr
1745145522Sdarrenr	if (nat->nat_fr != NULL)
1746170268Sdarrenr		(void) fr_derefrule(&nat->nat_fr);
1747145522Sdarrenr
1748145522Sdarrenr	if (nat->nat_hm != NULL)
1749170268Sdarrenr		fr_hostmapdel(&nat->nat_hm);
1750145522Sdarrenr
175153642Sguido	/*
175253642Sguido	 * If there is an active reference from the nat entry to its parent
175353642Sguido	 * rule, decrement the rule's reference count and free it too if no
175453642Sguido	 * longer being used.
175553642Sguido	 */
1756145522Sdarrenr	ipn = nat->nat_ptr;
175753642Sguido	if (ipn != NULL) {
1758170268Sdarrenr		fr_ipnatderef(&ipn);
175953642Sguido	}
176053642Sguido
1761145522Sdarrenr	MUTEX_DESTROY(&nat->nat_lock);
1762145522Sdarrenr
1763145522Sdarrenr	aps_free(nat->nat_aps);
1764145522Sdarrenr	nat_stats.ns_inuse--;
1765145522Sdarrenr
176653642Sguido	/*
176753642Sguido	 * If there's a fragment table entry too for this nat entry, then
1768145522Sdarrenr	 * dereference that as well.  This is after nat_lock is released
1769145522Sdarrenr	 * because of Tru64.
177053642Sguido	 */
1771145522Sdarrenr	fr_forgetnat((void *)nat);
1772145522Sdarrenr
1773145522Sdarrenr	KFREE(nat);
177453642Sguido}
177553642Sguido
177653642Sguido
1777145522Sdarrenr/* ------------------------------------------------------------------------ */
1778145522Sdarrenr/* Function:    nat_flushtable                                              */
1779145522Sdarrenr/* Returns:     int - number of NAT rules deleted                           */
1780145522Sdarrenr/* Parameters:  Nil                                                         */
1781145522Sdarrenr/*                                                                          */
1782145522Sdarrenr/* Deletes all currently active NAT sessions.  In deleting each NAT entry a */
1783145522Sdarrenr/* log record should be emitted in nat_delete() if NAT logging is enabled.  */
1784145522Sdarrenr/* ------------------------------------------------------------------------ */
178553642Sguido/*
178653642Sguido * nat_flushtable - clear the NAT table of all mapping entries.
178753642Sguido */
178853642Sguidostatic int nat_flushtable()
178953642Sguido{
1790145522Sdarrenr	nat_t *nat;
1791145522Sdarrenr	int j = 0;
179267614Sdarrenr
179353642Sguido	/*
179453642Sguido	 * ALL NAT mappings deleted, so lets just make the deletions
179553642Sguido	 * quicker.
179653642Sguido	 */
179753642Sguido	if (nat_table[0] != NULL)
179853642Sguido		bzero((char *)nat_table[0],
179953642Sguido		      sizeof(nat_table[0]) * ipf_nattable_sz);
180053642Sguido	if (nat_table[1] != NULL)
180153642Sguido		bzero((char *)nat_table[1],
180253642Sguido		      sizeof(nat_table[1]) * ipf_nattable_sz);
180353642Sguido
1804145522Sdarrenr	while ((nat = nat_instances) != NULL) {
1805145522Sdarrenr		nat_delete(nat, NL_FLUSH);
180653642Sguido		j++;
180753642Sguido	}
1808145522Sdarrenr
180953642Sguido	nat_stats.ns_inuse = 0;
181053642Sguido	return j;
181153642Sguido}
181253642Sguido
181353642Sguido
1814145522Sdarrenr/* ------------------------------------------------------------------------ */
1815145522Sdarrenr/* Function:    nat_clearlist                                               */
1816145522Sdarrenr/* Returns:     int - number of NAT/RDR rules deleted                       */
1817145522Sdarrenr/* Parameters:  Nil                                                         */
1818145522Sdarrenr/*                                                                          */
1819145522Sdarrenr/* Delete all rules in the current list of rules.  There is nothing elegant */
1820145522Sdarrenr/* about this cleanup: simply free all entries on the list of rules and     */
1821145522Sdarrenr/* clear out the tables used for hashed NAT rule lookups.                   */
1822145522Sdarrenr/* ------------------------------------------------------------------------ */
1823145522Sdarrenrstatic int nat_clearlist()
182453642Sguido{
1825145522Sdarrenr	ipnat_t *n, **np = &nat_list;
182653642Sguido	int i = 0;
182753642Sguido
182853642Sguido	if (nat_rules != NULL)
182953642Sguido		bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz);
183053642Sguido	if (rdr_rules != NULL)
183153642Sguido		bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz);
183253642Sguido
1833145522Sdarrenr	while ((n = *np) != NULL) {
183453642Sguido		*np = n->in_next;
1835145522Sdarrenr		if (n->in_use == 0) {
1836145522Sdarrenr			if (n->in_apr != NULL)
183753642Sguido				appr_free(n->in_apr);
1838172776Sdarrenr			MUTEX_DESTROY(&n->in_lock);
183953642Sguido			KFREE(n);
184053642Sguido			nat_stats.ns_rules--;
184153642Sguido		} else {
184253642Sguido			n->in_flags |= IPN_DELETE;
184353642Sguido			n->in_next = NULL;
184453642Sguido		}
184553642Sguido		i++;
184653642Sguido	}
1847172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H)
1848145522Sdarrenr	pfil_delayed_copy = 1;
1849145522Sdarrenr#endif
185053642Sguido	nat_masks = 0;
185153642Sguido	rdr_masks = 0;
185253642Sguido	return i;
185353642Sguido}
185453642Sguido
185553642Sguido
1856145522Sdarrenr/* ------------------------------------------------------------------------ */
1857145522Sdarrenr/* Function:    nat_newmap                                                  */
1858145522Sdarrenr/* Returns:     int - -1 == error, 0 == success                             */
1859145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
1860145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
1861145522Sdarrenr/*              ni(I)  - pointer to structure with misc. information needed */
1862145522Sdarrenr/*                       to create new NAT entry.                           */
1863145522Sdarrenr/*                                                                          */
1864145522Sdarrenr/* Given an empty NAT structure, populate it with new information about a   */
1865145522Sdarrenr/* new NAT session, as defined by the matching NAT rule.                    */
1866145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
1867145522Sdarrenr/* to the new IP address for the translation.                               */
1868145522Sdarrenr/* ------------------------------------------------------------------------ */
1869145522Sdarrenrstatic INLINE int nat_newmap(fin, nat, ni)
187092685Sdarrenrfr_info_t *fin;
1871145522Sdarrenrnat_t *nat;
1872145522Sdarrenrnatinfo_t *ni;
1873145522Sdarrenr{
1874145522Sdarrenr	u_short st_port, dport, sport, port, sp, dp;
1875145522Sdarrenr	struct in_addr in, inb;
1876145522Sdarrenr	hostmap_t *hm;
1877145522Sdarrenr	u_32_t flags;
1878145522Sdarrenr	u_32_t st_ip;
1879145522Sdarrenr	ipnat_t *np;
1880145522Sdarrenr	nat_t *natl;
1881145522Sdarrenr	int l;
1882145522Sdarrenr
1883145522Sdarrenr	/*
1884145522Sdarrenr	 * If it's an outbound packet which doesn't match any existing
1885145522Sdarrenr	 * record, then create a new port
1886145522Sdarrenr	 */
1887145522Sdarrenr	l = 0;
1888145522Sdarrenr	hm = NULL;
1889145522Sdarrenr	np = ni->nai_np;
1890145522Sdarrenr	st_ip = np->in_nip;
1891145522Sdarrenr	st_port = np->in_pnext;
1892145522Sdarrenr	flags = ni->nai_flags;
1893145522Sdarrenr	sport = ni->nai_sport;
1894145522Sdarrenr	dport = ni->nai_dport;
1895145522Sdarrenr
1896145522Sdarrenr	/*
1897145522Sdarrenr	 * Do a loop until we either run out of entries to try or we find
1898145522Sdarrenr	 * a NAT mapping that isn't currently being used.  This is done
1899145522Sdarrenr	 * because the change to the source is not (usually) being fixed.
1900145522Sdarrenr	 */
1901145522Sdarrenr	do {
1902145522Sdarrenr		port = 0;
1903145522Sdarrenr		in.s_addr = htonl(np->in_nip);
1904145522Sdarrenr		if (l == 0) {
1905145522Sdarrenr			/*
1906145522Sdarrenr			 * Check to see if there is an existing NAT
1907145522Sdarrenr			 * setup for this IP address pair.
1908145522Sdarrenr			 */
1909145522Sdarrenr			hm = nat_hostmap(np, fin->fin_src, fin->fin_dst,
1910145522Sdarrenr					 in, 0);
1911145522Sdarrenr			if (hm != NULL)
1912145522Sdarrenr				in.s_addr = hm->hm_mapip.s_addr;
1913145522Sdarrenr		} else if ((l == 1) && (hm != NULL)) {
1914170268Sdarrenr			fr_hostmapdel(&hm);
1915145522Sdarrenr		}
1916145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
1917145522Sdarrenr
1918145522Sdarrenr		nat->nat_hm = hm;
1919145522Sdarrenr
1920145522Sdarrenr		if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) {
1921145522Sdarrenr			if (l > 0)
1922145522Sdarrenr				return -1;
1923145522Sdarrenr		}
1924145522Sdarrenr
1925145522Sdarrenr		if (np->in_redir == NAT_BIMAP &&
1926145522Sdarrenr		    np->in_inmsk == np->in_outmsk) {
1927145522Sdarrenr			/*
1928145522Sdarrenr			 * map the address block in a 1:1 fashion
1929145522Sdarrenr			 */
1930145522Sdarrenr			in.s_addr = np->in_outip;
1931145522Sdarrenr			in.s_addr |= fin->fin_saddr & ~np->in_inmsk;
1932145522Sdarrenr			in.s_addr = ntohl(in.s_addr);
1933145522Sdarrenr
1934145522Sdarrenr		} else if (np->in_redir & NAT_MAPBLK) {
1935145522Sdarrenr			if ((l >= np->in_ppip) || ((l > 0) &&
1936145522Sdarrenr			     !(flags & IPN_TCPUDP)))
1937145522Sdarrenr				return -1;
1938145522Sdarrenr			/*
1939145522Sdarrenr			 * map-block - Calculate destination address.
1940145522Sdarrenr			 */
1941145522Sdarrenr			in.s_addr = ntohl(fin->fin_saddr);
1942145522Sdarrenr			in.s_addr &= ntohl(~np->in_inmsk);
1943145522Sdarrenr			inb.s_addr = in.s_addr;
1944145522Sdarrenr			in.s_addr /= np->in_ippip;
1945145522Sdarrenr			in.s_addr &= ntohl(~np->in_outmsk);
1946145522Sdarrenr			in.s_addr += ntohl(np->in_outip);
1947145522Sdarrenr			/*
1948145522Sdarrenr			 * Calculate destination port.
1949145522Sdarrenr			 */
1950145522Sdarrenr			if ((flags & IPN_TCPUDP) &&
1951145522Sdarrenr			    (np->in_ppip != 0)) {
1952145522Sdarrenr				port = ntohs(sport) + l;
1953145522Sdarrenr				port %= np->in_ppip;
1954145522Sdarrenr				port += np->in_ppip *
1955145522Sdarrenr					(inb.s_addr % np->in_ippip);
1956145522Sdarrenr				port += MAPBLK_MINPORT;
1957145522Sdarrenr				port = htons(port);
1958145522Sdarrenr			}
1959145522Sdarrenr
1960145522Sdarrenr		} else if ((np->in_outip == 0) &&
1961145522Sdarrenr			   (np->in_outmsk == 0xffffffff)) {
1962145522Sdarrenr			/*
1963145522Sdarrenr			 * 0/32 - use the interface's IP address.
1964145522Sdarrenr			 */
1965145522Sdarrenr			if ((l > 0) ||
1966145522Sdarrenr			    fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp,
1967145522Sdarrenr				       &in, NULL) == -1)
1968145522Sdarrenr				return -1;
1969145522Sdarrenr			in.s_addr = ntohl(in.s_addr);
1970145522Sdarrenr
1971145522Sdarrenr		} else if ((np->in_outip == 0) && (np->in_outmsk == 0)) {
1972145522Sdarrenr			/*
1973145522Sdarrenr			 * 0/0 - use the original source address/port.
1974145522Sdarrenr			 */
1975145522Sdarrenr			if (l > 0)
1976145522Sdarrenr				return -1;
1977145522Sdarrenr			in.s_addr = ntohl(fin->fin_saddr);
1978145522Sdarrenr
1979145522Sdarrenr		} else if ((np->in_outmsk != 0xffffffff) &&
1980145522Sdarrenr			   (np->in_pnext == 0) && ((l > 0) || (hm == NULL)))
1981145522Sdarrenr			np->in_nip++;
1982145522Sdarrenr
1983145522Sdarrenr		natl = NULL;
1984145522Sdarrenr
1985145522Sdarrenr		if ((flags & IPN_TCPUDP) &&
1986145522Sdarrenr		    ((np->in_redir & NAT_MAPBLK) == 0) &&
1987145522Sdarrenr		    (np->in_flags & IPN_AUTOPORTMAP)) {
1988145522Sdarrenr			/*
1989145522Sdarrenr			 * "ports auto" (without map-block)
1990145522Sdarrenr			 */
1991145522Sdarrenr			if ((l > 0) && (l % np->in_ppip == 0)) {
1992145522Sdarrenr				if (l > np->in_space) {
1993145522Sdarrenr					return -1;
1994145522Sdarrenr				} else if ((l > np->in_ppip) &&
1995145522Sdarrenr					   np->in_outmsk != 0xffffffff)
1996145522Sdarrenr					np->in_nip++;
1997145522Sdarrenr			}
1998145522Sdarrenr			if (np->in_ppip != 0) {
1999145522Sdarrenr				port = ntohs(sport);
2000145522Sdarrenr				port += (l % np->in_ppip);
2001145522Sdarrenr				port %= np->in_ppip;
2002145522Sdarrenr				port += np->in_ppip *
2003145522Sdarrenr					(ntohl(fin->fin_saddr) %
2004145522Sdarrenr					 np->in_ippip);
2005145522Sdarrenr				port += MAPBLK_MINPORT;
2006145522Sdarrenr				port = htons(port);
2007145522Sdarrenr			}
2008145522Sdarrenr
2009145522Sdarrenr		} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
2010145522Sdarrenr			   (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) {
2011145522Sdarrenr			/*
2012145522Sdarrenr			 * Standard port translation.  Select next port.
2013145522Sdarrenr			 */
2014145522Sdarrenr			port = htons(np->in_pnext++);
2015145522Sdarrenr
2016145522Sdarrenr			if (np->in_pnext > ntohs(np->in_pmax)) {
2017145522Sdarrenr				np->in_pnext = ntohs(np->in_pmin);
2018145522Sdarrenr				if (np->in_outmsk != 0xffffffff)
2019145522Sdarrenr					np->in_nip++;
2020145522Sdarrenr			}
2021145522Sdarrenr		}
2022145522Sdarrenr
2023145522Sdarrenr		if (np->in_flags & IPN_IPRANGE) {
2024145522Sdarrenr			if (np->in_nip > ntohl(np->in_outmsk))
2025145522Sdarrenr				np->in_nip = ntohl(np->in_outip);
2026145522Sdarrenr		} else {
2027145522Sdarrenr			if ((np->in_outmsk != 0xffffffff) &&
2028145522Sdarrenr			    ((np->in_nip + 1) & ntohl(np->in_outmsk)) >
2029145522Sdarrenr			    ntohl(np->in_outip))
2030145522Sdarrenr				np->in_nip = ntohl(np->in_outip) + 1;
2031145522Sdarrenr		}
2032145522Sdarrenr
2033145522Sdarrenr		if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
2034145522Sdarrenr			port = sport;
2035145522Sdarrenr
2036145522Sdarrenr		/*
2037145522Sdarrenr		 * Here we do a lookup of the connection as seen from
2038145522Sdarrenr		 * the outside.  If an IP# pair already exists, try
2039145522Sdarrenr		 * again.  So if you have A->B becomes C->B, you can
2040145522Sdarrenr		 * also have D->E become C->E but not D->B causing
2041145522Sdarrenr		 * another C->B.  Also take protocol and ports into
2042145522Sdarrenr		 * account when determining whether a pre-existing
2043145522Sdarrenr		 * NAT setup will cause an external conflict where
2044145522Sdarrenr		 * this is appropriate.
2045145522Sdarrenr		 */
2046145522Sdarrenr		inb.s_addr = htonl(in.s_addr);
2047145522Sdarrenr		sp = fin->fin_data[0];
2048145522Sdarrenr		dp = fin->fin_data[1];
2049145522Sdarrenr		fin->fin_data[0] = fin->fin_data[1];
2050145522Sdarrenr		fin->fin_data[1] = htons(port);
2051145522Sdarrenr		natl = nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
2052145522Sdarrenr				    (u_int)fin->fin_p, fin->fin_dst, inb);
2053145522Sdarrenr		fin->fin_data[0] = sp;
2054145522Sdarrenr		fin->fin_data[1] = dp;
2055145522Sdarrenr
2056145522Sdarrenr		/*
2057145522Sdarrenr		 * Has the search wrapped around and come back to the
2058145522Sdarrenr		 * start ?
2059145522Sdarrenr		 */
2060145522Sdarrenr		if ((natl != NULL) &&
2061145522Sdarrenr		    (np->in_pnext != 0) && (st_port == np->in_pnext) &&
2062145522Sdarrenr		    (np->in_nip != 0) && (st_ip == np->in_nip))
2063145522Sdarrenr			return -1;
2064145522Sdarrenr		l++;
2065145522Sdarrenr	} while (natl != NULL);
2066145522Sdarrenr
2067145522Sdarrenr	if (np->in_space > 0)
2068145522Sdarrenr		np->in_space--;
2069145522Sdarrenr
2070145522Sdarrenr	/* Setup the NAT table */
2071145522Sdarrenr	nat->nat_inip = fin->fin_src;
2072145522Sdarrenr	nat->nat_outip.s_addr = htonl(in.s_addr);
2073145522Sdarrenr	nat->nat_oip = fin->fin_dst;
2074145522Sdarrenr	if (nat->nat_hm == NULL)
2075145522Sdarrenr		nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst,
2076145522Sdarrenr					  nat->nat_outip, 0);
2077145522Sdarrenr
2078145522Sdarrenr	/*
2079145522Sdarrenr	 * The ICMP checksum does not have a pseudo header containing
2080145522Sdarrenr	 * the IP addresses
2081145522Sdarrenr	 */
2082145522Sdarrenr	ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr));
2083145522Sdarrenr	ni->nai_sum2 = LONG_SUM(in.s_addr);
2084145522Sdarrenr	if ((flags & IPN_TCPUDP)) {
2085145522Sdarrenr		ni->nai_sum1 += ntohs(sport);
2086145522Sdarrenr		ni->nai_sum2 += ntohs(port);
2087145522Sdarrenr	}
2088145522Sdarrenr
2089145522Sdarrenr	if (flags & IPN_TCPUDP) {
2090145522Sdarrenr		nat->nat_inport = sport;
2091145522Sdarrenr		nat->nat_outport = port;	/* sport */
2092145522Sdarrenr		nat->nat_oport = dport;
2093145522Sdarrenr		((tcphdr_t *)fin->fin_dp)->th_sport = port;
2094145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
2095145522Sdarrenr		((icmphdr_t *)fin->fin_dp)->icmp_id = port;
2096145522Sdarrenr		nat->nat_inport = port;
2097145522Sdarrenr		nat->nat_outport = port;
2098145522Sdarrenr	} else if (fin->fin_p == IPPROTO_GRE) {
2099145522Sdarrenr#if 0
2100145522Sdarrenr		nat->nat_gre.gs_flags = ((grehdr_t *)fin->fin_dp)->gr_flags;
2101145522Sdarrenr		if (GRE_REV(nat->nat_gre.gs_flags) == 1) {
2102145522Sdarrenr			nat->nat_oport = 0;/*fin->fin_data[1];*/
2103145522Sdarrenr			nat->nat_inport = 0;/*fin->fin_data[0];*/
2104145522Sdarrenr			nat->nat_outport = 0;/*fin->fin_data[0];*/
2105145522Sdarrenr			nat->nat_call[0] = fin->fin_data[0];
2106145522Sdarrenr			nat->nat_call[1] = fin->fin_data[0];
2107145522Sdarrenr		}
2108145522Sdarrenr#endif
2109145522Sdarrenr	}
2110145522Sdarrenr	ni->nai_ip.s_addr = in.s_addr;
2111145522Sdarrenr	ni->nai_port = port;
2112145522Sdarrenr	ni->nai_nport = dport;
2113145522Sdarrenr	return 0;
2114145522Sdarrenr}
2115145522Sdarrenr
2116145522Sdarrenr
2117145522Sdarrenr/* ------------------------------------------------------------------------ */
2118145522Sdarrenr/* Function:    nat_newrdr                                                  */
2119145522Sdarrenr/* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
2120145522Sdarrenr/*                    allow rule to be moved if IPN_ROUNDR is set.          */
2121145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
2122145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
2123145522Sdarrenr/*              ni(I)  - pointer to structure with misc. information needed */
2124145522Sdarrenr/*                       to create new NAT entry.                           */
2125145522Sdarrenr/*                                                                          */
2126145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
2127145522Sdarrenr/* to the new IP address for the translation.                               */
2128145522Sdarrenr/* ------------------------------------------------------------------------ */
2129145522Sdarrenrstatic INLINE int nat_newrdr(fin, nat, ni)
2130145522Sdarrenrfr_info_t *fin;
2131145522Sdarrenrnat_t *nat;
2132145522Sdarrenrnatinfo_t *ni;
2133145522Sdarrenr{
2134145522Sdarrenr	u_short nport, dport, sport;
2135170268Sdarrenr	struct in_addr in, inb;
2136170268Sdarrenr	u_short sp, dp;
2137145522Sdarrenr	hostmap_t *hm;
2138145522Sdarrenr	u_32_t flags;
2139145522Sdarrenr	ipnat_t *np;
2140170268Sdarrenr	nat_t *natl;
2141145522Sdarrenr	int move;
2142145522Sdarrenr
2143145522Sdarrenr	move = 1;
2144145522Sdarrenr	hm = NULL;
2145145522Sdarrenr	in.s_addr = 0;
2146145522Sdarrenr	np = ni->nai_np;
2147145522Sdarrenr	flags = ni->nai_flags;
2148145522Sdarrenr	sport = ni->nai_sport;
2149145522Sdarrenr	dport = ni->nai_dport;
2150145522Sdarrenr
2151145522Sdarrenr	/*
2152145522Sdarrenr	 * If the matching rule has IPN_STICKY set, then we want to have the
2153145522Sdarrenr	 * same rule kick in as before.  Why would this happen?  If you have
2154145522Sdarrenr	 * a collection of rdr rules with "round-robin sticky", the current
2155145522Sdarrenr	 * packet might match a different one to the previous connection but
2156145522Sdarrenr	 * we want the same destination to be used.
2157145522Sdarrenr	 */
2158153876Sguido	if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) &&
2159153876Sguido	    ((np->in_flags & IPN_STICKY) != 0)) {
2160145522Sdarrenr		hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, in,
2161145522Sdarrenr				 (u_32_t)dport);
2162145522Sdarrenr		if (hm != NULL) {
2163145522Sdarrenr			in.s_addr = ntohl(hm->hm_mapip.s_addr);
2164145522Sdarrenr			np = hm->hm_ipnat;
2165145522Sdarrenr			ni->nai_np = np;
2166145522Sdarrenr			move = 0;
2167145522Sdarrenr		}
2168145522Sdarrenr	}
2169145522Sdarrenr
2170145522Sdarrenr	/*
2171145522Sdarrenr	 * Otherwise, it's an inbound packet. Most likely, we don't
2172145522Sdarrenr	 * want to rewrite source ports and source addresses. Instead,
2173145522Sdarrenr	 * we want to rewrite to a fixed internal address and fixed
2174145522Sdarrenr	 * internal port.
2175145522Sdarrenr	 */
2176145522Sdarrenr	if (np->in_flags & IPN_SPLIT) {
2177145522Sdarrenr		in.s_addr = np->in_nip;
2178145522Sdarrenr
2179145522Sdarrenr		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
2180153876Sguido			hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst,
2181145522Sdarrenr					 in, (u_32_t)dport);
2182145522Sdarrenr			if (hm != NULL) {
2183145522Sdarrenr				in.s_addr = hm->hm_mapip.s_addr;
2184145522Sdarrenr				move = 0;
2185145522Sdarrenr			}
2186145522Sdarrenr		}
2187145522Sdarrenr
2188145522Sdarrenr		if (hm == NULL || hm->hm_ref == 1) {
2189145522Sdarrenr			if (np->in_inip == htonl(in.s_addr)) {
2190145522Sdarrenr				np->in_nip = ntohl(np->in_inmsk);
2191145522Sdarrenr				move = 0;
2192145522Sdarrenr			} else {
2193145522Sdarrenr				np->in_nip = ntohl(np->in_inip);
2194145522Sdarrenr			}
2195145522Sdarrenr		}
2196145522Sdarrenr
2197145522Sdarrenr	} else if ((np->in_inip == 0) && (np->in_inmsk == 0xffffffff)) {
2198145522Sdarrenr		/*
2199145522Sdarrenr		 * 0/32 - use the interface's IP address.
2200145522Sdarrenr		 */
2201145522Sdarrenr		if (fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, &in, NULL) == -1)
2202145522Sdarrenr			return -1;
2203145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
2204145522Sdarrenr
2205145522Sdarrenr	} else if ((np->in_inip == 0) && (np->in_inmsk== 0)) {
2206145522Sdarrenr		/*
2207145522Sdarrenr		 * 0/0 - use the original destination address/port.
2208145522Sdarrenr		 */
2209145522Sdarrenr		in.s_addr = ntohl(fin->fin_daddr);
2210145522Sdarrenr
2211145522Sdarrenr	} else if (np->in_redir == NAT_BIMAP &&
2212145522Sdarrenr		   np->in_inmsk == np->in_outmsk) {
2213145522Sdarrenr		/*
2214145522Sdarrenr		 * map the address block in a 1:1 fashion
2215145522Sdarrenr		 */
2216145522Sdarrenr		in.s_addr = np->in_inip;
2217145522Sdarrenr		in.s_addr |= fin->fin_daddr & ~np->in_inmsk;
2218145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
2219145522Sdarrenr	} else {
2220145522Sdarrenr		in.s_addr = ntohl(np->in_inip);
2221145522Sdarrenr	}
2222145522Sdarrenr
2223145522Sdarrenr	if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
2224145522Sdarrenr		nport = dport;
2225145522Sdarrenr	else {
2226145522Sdarrenr		/*
2227145522Sdarrenr		 * Whilst not optimized for the case where
2228145522Sdarrenr		 * pmin == pmax, the gain is not significant.
2229145522Sdarrenr		 */
2230145522Sdarrenr		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
2231145522Sdarrenr		    (np->in_pmin != np->in_pmax)) {
2232145522Sdarrenr			nport = ntohs(dport) - ntohs(np->in_pmin) +
2233145522Sdarrenr				ntohs(np->in_pnext);
2234145522Sdarrenr			nport = htons(nport);
2235145522Sdarrenr		} else
2236145522Sdarrenr			nport = np->in_pnext;
2237145522Sdarrenr	}
2238145522Sdarrenr
2239145522Sdarrenr	/*
2240145522Sdarrenr	 * When the redirect-to address is set to 0.0.0.0, just
2241145522Sdarrenr	 * assume a blank `forwarding' of the packet.  We don't
2242145522Sdarrenr	 * setup any translation for this either.
2243145522Sdarrenr	 */
2244145522Sdarrenr	if (in.s_addr == 0) {
2245145522Sdarrenr		if (nport == dport)
2246145522Sdarrenr			return -1;
2247145522Sdarrenr		in.s_addr = ntohl(fin->fin_daddr);
2248145522Sdarrenr	}
2249145522Sdarrenr
2250170268Sdarrenr	/*
2251170268Sdarrenr	 * Check to see if this redirect mapping already exists and if
2252170268Sdarrenr	 * it does, return "failure" (allowing it to be created will just
2253170268Sdarrenr	 * cause one or both of these "connections" to stop working.)
2254170268Sdarrenr	 */
2255170268Sdarrenr	inb.s_addr = htonl(in.s_addr);
2256170268Sdarrenr	sp = fin->fin_data[0];
2257170268Sdarrenr	dp = fin->fin_data[1];
2258170268Sdarrenr	fin->fin_data[1] = fin->fin_data[0];
2259170268Sdarrenr	fin->fin_data[0] = ntohs(nport);
2260170268Sdarrenr	natl = nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
2261170268Sdarrenr			     (u_int)fin->fin_p, inb, fin->fin_src);
2262170268Sdarrenr	fin->fin_data[0] = sp;
2263170268Sdarrenr	fin->fin_data[1] = dp;
2264170268Sdarrenr	if (natl != NULL)
2265170268Sdarrenr		return -1;
2266170268Sdarrenr
2267145522Sdarrenr	nat->nat_inip.s_addr = htonl(in.s_addr);
2268145522Sdarrenr	nat->nat_outip = fin->fin_dst;
2269145522Sdarrenr	nat->nat_oip = fin->fin_src;
2270153876Sguido	if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
2271153876Sguido		nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, in,
2272153876Sguido					  (u_32_t)dport);
2273145522Sdarrenr
2274145522Sdarrenr	ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)) + ntohs(dport);
2275145522Sdarrenr	ni->nai_sum2 = LONG_SUM(in.s_addr) + ntohs(nport);
2276145522Sdarrenr
2277145522Sdarrenr	ni->nai_ip.s_addr = in.s_addr;
2278145522Sdarrenr	ni->nai_nport = nport;
2279145522Sdarrenr	ni->nai_port = sport;
2280145522Sdarrenr
2281145522Sdarrenr	if (flags & IPN_TCPUDP) {
2282145522Sdarrenr		nat->nat_inport = nport;
2283145522Sdarrenr		nat->nat_outport = dport;
2284145522Sdarrenr		nat->nat_oport = sport;
2285145522Sdarrenr		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
2286145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
2287145522Sdarrenr		((icmphdr_t *)fin->fin_dp)->icmp_id = nport;
2288145522Sdarrenr		nat->nat_inport = nport;
2289145522Sdarrenr		nat->nat_outport = nport;
2290145522Sdarrenr	} else if (fin->fin_p == IPPROTO_GRE) {
2291145522Sdarrenr#if 0
2292145522Sdarrenr		nat->nat_gre.gs_flags = ((grehdr_t *)fin->fin_dp)->gr_flags;
2293145522Sdarrenr		if (GRE_REV(nat->nat_gre.gs_flags) == 1) {
2294145522Sdarrenr			nat->nat_call[0] = fin->fin_data[0];
2295145522Sdarrenr			nat->nat_call[1] = fin->fin_data[1];
2296145522Sdarrenr			nat->nat_oport = 0; /*fin->fin_data[0];*/
2297145522Sdarrenr			nat->nat_inport = 0; /*fin->fin_data[1];*/
2298145522Sdarrenr			nat->nat_outport = 0; /*fin->fin_data[1];*/
2299145522Sdarrenr		}
2300145522Sdarrenr#endif
2301145522Sdarrenr	}
2302145522Sdarrenr
2303145522Sdarrenr	return move;
2304145522Sdarrenr}
2305145522Sdarrenr
2306145522Sdarrenr/* ------------------------------------------------------------------------ */
2307145522Sdarrenr/* Function:    nat_new                                                     */
2308145522Sdarrenr/* Returns:     nat_t* - NULL == failure to create new NAT structure,       */
2309145522Sdarrenr/*                       else pointer to new NAT structure                  */
2310145522Sdarrenr/* Parameters:  fin(I)       - pointer to packet information                */
2311145522Sdarrenr/*              np(I)        - pointer to NAT rule                          */
2312145522Sdarrenr/*              natsave(I)   - pointer to where to store NAT struct pointer */
2313145522Sdarrenr/*              flags(I)     - flags describing the current packet          */
2314145522Sdarrenr/*              direction(I) - direction of packet (in/out)                 */
2315145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
2316145522Sdarrenr/*                                                                          */
2317145522Sdarrenr/* Attempts to create a new NAT entry.  Does not actually change the packet */
2318145522Sdarrenr/* in any way.                                                              */
2319145522Sdarrenr/*                                                                          */
2320145522Sdarrenr/* This fucntion is in three main parts: (1) deal with creating a new NAT   */
2321145522Sdarrenr/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
2322145522Sdarrenr/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
2323145522Sdarrenr/* and (3) building that structure and putting it into the NAT table(s).    */
2324161356Sguido/*                                                                          */
2325161356Sguido/* NOTE: natsave should NOT be used top point back to an ipstate_t struct   */
2326161356Sguido/*       as it can result in memory being corrupted.                        */
2327145522Sdarrenr/* ------------------------------------------------------------------------ */
2328145522Sdarrenrnat_t *nat_new(fin, np, natsave, flags, direction)
2329145522Sdarrenrfr_info_t *fin;
233053642Sguidoipnat_t *np;
233192685Sdarrenrnat_t **natsave;
233253642Sguidou_int flags;
233353642Sguidoint direction;
233453642Sguido{
233553642Sguido	u_short port = 0, sport = 0, dport = 0, nport = 0;
233653642Sguido	tcphdr_t *tcp = NULL;
233760852Sdarrenr	hostmap_t *hm = NULL;
2338145522Sdarrenr	struct in_addr in;
233960852Sdarrenr	nat_t *nat, *natl;
2340145522Sdarrenr	u_int nflags;
2341145522Sdarrenr	natinfo_t ni;
2342145522Sdarrenr	u_32_t sumd;
2343145522Sdarrenr	int move;
2344145522Sdarrenr#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC)
2345145522Sdarrenr	qpktinfo_t *qpi = fin->fin_qpi;
234655929Sguido#endif
234753642Sguido
2348130886Sdarrenr	if (nat_stats.ns_inuse >= ipf_nattable_max) {
2349130886Sdarrenr		nat_stats.ns_memfail++;
2350170268Sdarrenr		fr_nat_doflush = 1;
2351130886Sdarrenr		return NULL;
2352130886Sdarrenr	}
2353130886Sdarrenr
2354145522Sdarrenr	move = 1;
2355145522Sdarrenr	nflags = np->in_flags & flags;
2356145522Sdarrenr	nflags &= NAT_FROMRULE;
235753642Sguido
2358145522Sdarrenr	ni.nai_np = np;
2359145522Sdarrenr	ni.nai_nflags = nflags;
2360145522Sdarrenr	ni.nai_flags = flags;
2361170268Sdarrenr	ni.nai_dport = 0;
2362170268Sdarrenr	ni.nai_sport = 0;
2363145522Sdarrenr
236453642Sguido	/* Give me a new nat */
236553642Sguido	KMALLOC(nat, nat_t *);
236660852Sdarrenr	if (nat == NULL) {
236760852Sdarrenr		nat_stats.ns_memfail++;
2368130886Sdarrenr		/*
2369130886Sdarrenr		 * Try to automatically tune the max # of entries in the
2370130886Sdarrenr		 * table allowed to be less than what will cause kmem_alloc()
2371130886Sdarrenr		 * to fail and try to eliminate panics due to out of memory
2372130886Sdarrenr		 * conditions arising.
2373130886Sdarrenr		 */
2374130886Sdarrenr		if (ipf_nattable_max > ipf_nattable_sz) {
2375130886Sdarrenr			ipf_nattable_max = nat_stats.ns_inuse - 100;
2376130886Sdarrenr			printf("ipf_nattable_max reduced to %d\n",
2377130886Sdarrenr				ipf_nattable_max);
2378130886Sdarrenr		}
237953642Sguido		return NULL;
238060852Sdarrenr	}
238153642Sguido
2382145522Sdarrenr	if (flags & IPN_TCPUDP) {
2383145522Sdarrenr		tcp = fin->fin_dp;
2384145522Sdarrenr		ni.nai_sport = htons(fin->fin_sport);
2385145522Sdarrenr		ni.nai_dport = htons(fin->fin_dport);
2386145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
2387145522Sdarrenr		/*
2388145522Sdarrenr		 * In the ICMP query NAT code, we translate the ICMP id fields
2389145522Sdarrenr		 * to make them unique. This is indepedent of the ICMP type
2390145522Sdarrenr		 * (e.g. in the unlikely event that a host sends an echo and
2391145522Sdarrenr		 * an tstamp request with the same id, both packets will have
2392145522Sdarrenr		 * their ip address/id field changed in the same way).
2393145522Sdarrenr		 */
2394145522Sdarrenr		/* The icmp_id field is used by the sender to identify the
2395145522Sdarrenr		 * process making the icmp request. (the receiver justs
2396145522Sdarrenr		 * copies it back in its response). So, it closely matches
2397145522Sdarrenr		 * the concept of source port. We overlay sport, so we can
2398145522Sdarrenr		 * maximally reuse the existing code.
2399145522Sdarrenr		 */
2400145522Sdarrenr		ni.nai_sport = ((icmphdr_t *)fin->fin_dp)->icmp_id;
2401145522Sdarrenr		ni.nai_dport = ni.nai_sport;
2402145522Sdarrenr	}
2403145522Sdarrenr
240453642Sguido	bzero((char *)nat, sizeof(*nat));
240553642Sguido	nat->nat_flags = flags;
2406170268Sdarrenr	nat->nat_redir = np->in_redir;
2407145522Sdarrenr
2408145522Sdarrenr	if ((flags & NAT_SLAVE) == 0) {
2409145522Sdarrenr		MUTEX_ENTER(&ipf_nat_new);
2410145522Sdarrenr	}
2411145522Sdarrenr
241253642Sguido	/*
241353642Sguido	 * Search the current table for a match.
241453642Sguido	 */
241553642Sguido	if (direction == NAT_OUTBOUND) {
241653642Sguido		/*
2417145522Sdarrenr		 * We can now arrange to call this for the same connection
2418145522Sdarrenr		 * because ipf_nat_new doesn't protect the code path into
2419145522Sdarrenr		 * this function.
242053642Sguido		 */
2421145522Sdarrenr		natl = nat_outlookup(fin, nflags, (u_int)fin->fin_p,
2422145522Sdarrenr				     fin->fin_src, fin->fin_dst);
2423145522Sdarrenr		if (natl != NULL) {
2424161356Sguido			KFREE(nat);
2425145522Sdarrenr			nat = natl;
2426145522Sdarrenr			goto done;
2427145522Sdarrenr		}
242853642Sguido
2429145522Sdarrenr		move = nat_newmap(fin, nat, &ni);
2430145522Sdarrenr		if (move == -1)
2431145522Sdarrenr			goto badnat;
243253642Sguido
2433145522Sdarrenr		np = ni.nai_np;
2434145522Sdarrenr		in = ni.nai_ip;
243553642Sguido	} else {
243653642Sguido		/*
2437145522Sdarrenr		 * NAT_INBOUND is used only for redirects rules
243853642Sguido		 */
2439145522Sdarrenr		natl = nat_inlookup(fin, nflags, (u_int)fin->fin_p,
2440145522Sdarrenr				    fin->fin_src, fin->fin_dst);
2441145522Sdarrenr		if (natl != NULL) {
2442161356Sguido			KFREE(nat);
2443145522Sdarrenr			nat = natl;
2444145522Sdarrenr			goto done;
244560852Sdarrenr		}
244653642Sguido
2447145522Sdarrenr		move = nat_newrdr(fin, nat, &ni);
2448145522Sdarrenr		if (move == -1)
2449145522Sdarrenr			goto badnat;
245053642Sguido
2451145522Sdarrenr		np = ni.nai_np;
2452145522Sdarrenr		in = ni.nai_ip;
2453145522Sdarrenr	}
2454145522Sdarrenr	port = ni.nai_port;
2455145522Sdarrenr	nport = ni.nai_nport;
245653642Sguido
2457145522Sdarrenr	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
2458145522Sdarrenr		if (np->in_redir == NAT_REDIRECT) {
2459145522Sdarrenr			nat_delrdr(np);
2460145522Sdarrenr			nat_addrdr(np);
2461145522Sdarrenr		} else if (np->in_redir == NAT_MAP) {
2462145522Sdarrenr			nat_delnat(np);
2463145522Sdarrenr			nat_addnat(np);
246453642Sguido		}
246553642Sguido	}
246653642Sguido
2467145522Sdarrenr	if (flags & IPN_TCPUDP) {
2468145522Sdarrenr		sport = ni.nai_sport;
2469145522Sdarrenr		dport = ni.nai_dport;
2470145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
2471145522Sdarrenr		sport = ni.nai_sport;
2472145522Sdarrenr		dport = 0;
2473145522Sdarrenr	}
2474145522Sdarrenr
2475145522Sdarrenr	CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd);
247655929Sguido	nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
2477145522Sdarrenr#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC)
2478130886Sdarrenr	if ((flags & IPN_TCP) && dohwcksum &&
2479145522Sdarrenr	    (((ill_t *)qpi->qpi_ill)->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) {
248055929Sguido		if (direction == NAT_OUTBOUND)
2481145522Sdarrenr			ni.nai_sum1 = LONG_SUM(in.s_addr);
248255929Sguido		else
2483145522Sdarrenr			ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr));
2484145522Sdarrenr		ni.nai_sum1 += LONG_SUM(ntohl(fin->fin_daddr));
2485145522Sdarrenr		ni.nai_sum1 += 30;
2486145522Sdarrenr		ni.nai_sum1 = (ni.nai_sum1 & 0xffff) + (ni.nai_sum1 >> 16);
2487145522Sdarrenr		nat->nat_sumd[1] = NAT_HW_CKSUM|(ni.nai_sum1 & 0xffff);
248855929Sguido	} else
248955929Sguido#endif
249055929Sguido		nat->nat_sumd[1] = nat->nat_sumd[0];
249153642Sguido
2492145522Sdarrenr	if ((flags & IPN_TCPUDPICMP) && ((sport != port) || (dport != nport))) {
249353642Sguido		if (direction == NAT_OUTBOUND)
2494145522Sdarrenr			ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr));
249553642Sguido		else
2496145522Sdarrenr			ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr));
249753642Sguido
2498145522Sdarrenr		ni.nai_sum2 = LONG_SUM(in.s_addr);
249953642Sguido
2500145522Sdarrenr		CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd);
250153642Sguido		nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
2502145522Sdarrenr	} else {
250355929Sguido		nat->nat_ipsumd = nat->nat_sumd[0];
2504145522Sdarrenr		if (!(flags & IPN_TCPUDPICMP)) {
2505145522Sdarrenr			nat->nat_sumd[0] = 0;
2506145522Sdarrenr			nat->nat_sumd[1] = 0;
2507145522Sdarrenr		}
2508145522Sdarrenr	}
250953642Sguido
2510145522Sdarrenr	if (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) {
2511172776Sdarrenr		fr_nat_doflush = 1;
2512145522Sdarrenr		goto badnat;
2513145522Sdarrenr	}
2514145522Sdarrenr	if (flags & SI_WILDP)
2515145522Sdarrenr		nat_stats.ns_wilds++;
2516172776Sdarrenr	fin->fin_flx |= FI_NEWNAT;
2517145522Sdarrenr	goto done;
2518145522Sdarrenrbadnat:
2519145522Sdarrenr	nat_stats.ns_badnat++;
2520145522Sdarrenr	if ((hm = nat->nat_hm) != NULL)
2521170268Sdarrenr		fr_hostmapdel(&hm);
2522145522Sdarrenr	KFREE(nat);
2523145522Sdarrenr	nat = NULL;
2524145522Sdarrenrdone:
2525145522Sdarrenr	if ((flags & NAT_SLAVE) == 0) {
2526145522Sdarrenr		MUTEX_EXIT(&ipf_nat_new);
2527145522Sdarrenr	}
2528145522Sdarrenr	return nat;
2529145522Sdarrenr}
253060852Sdarrenr
253160852Sdarrenr
2532145522Sdarrenr/* ------------------------------------------------------------------------ */
2533145522Sdarrenr/* Function:    nat_finalise                                                */
2534145522Sdarrenr/* Returns:     int - 0 == sucess, -1 == failure                            */
2535145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
2536145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
2537145522Sdarrenr/*              ni(I)  - pointer to structure with misc. information needed */
2538145522Sdarrenr/*                       to create new NAT entry.                           */
2539145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
2540145522Sdarrenr/*                                                                          */
2541145522Sdarrenr/* This is the tail end of constructing a new NAT entry and is the same     */
2542145522Sdarrenr/* for both IPv4 and IPv6.                                                  */
2543145522Sdarrenr/* ------------------------------------------------------------------------ */
2544145522Sdarrenr/*ARGSUSED*/
2545153876Sguidostatic int nat_finalise(fin, nat, ni, tcp, natsave, direction)
2546145522Sdarrenrfr_info_t *fin;
2547145522Sdarrenrnat_t *nat;
2548145522Sdarrenrnatinfo_t *ni;
2549145522Sdarrenrtcphdr_t *tcp;
2550145522Sdarrenrnat_t **natsave;
2551145522Sdarrenrint direction;
2552145522Sdarrenr{
2553145522Sdarrenr	frentry_t *fr;
2554145522Sdarrenr	ipnat_t *np;
2555145522Sdarrenr
2556145522Sdarrenr	np = ni->nai_np;
2557145522Sdarrenr
2558161356Sguido	if (np->in_ifps[0] != NULL) {
2559172776Sdarrenr		COPYIFNAME(4, np->in_ifps[0], nat->nat_ifnames[0]);
2560161356Sguido	}
2561161356Sguido	if (np->in_ifps[1] != NULL) {
2562172776Sdarrenr		COPYIFNAME(4, np->in_ifps[1], nat->nat_ifnames[1]);
2563161356Sguido	}
2564145522Sdarrenr#ifdef	IPFILTER_SYNC
2565145522Sdarrenr	if ((nat->nat_flags & SI_CLONE) == 0)
2566145522Sdarrenr		nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat);
2567145522Sdarrenr#endif
2568145522Sdarrenr
256992685Sdarrenr	nat->nat_me = natsave;
257053642Sguido	nat->nat_dir = direction;
2571161356Sguido	nat->nat_ifps[0] = np->in_ifps[0];
2572161356Sguido	nat->nat_ifps[1] = np->in_ifps[1];
257353642Sguido	nat->nat_ptr = np;
257492685Sdarrenr	nat->nat_p = fin->fin_p;
2575110916Sdarrenr	nat->nat_mssclamp = np->in_mssclamp;
2576172776Sdarrenr	if (nat->nat_p == IPPROTO_TCP)
2577172776Sdarrenr		nat->nat_seqnext[0] = ntohl(tcp->th_seq);
257892685Sdarrenr
2579145522Sdarrenr	if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0))
2580145522Sdarrenr		if (appr_new(fin, nat) == -1)
2581145522Sdarrenr			return -1;
258292685Sdarrenr
2583145522Sdarrenr	if (nat_insert(nat, fin->fin_rev) == 0) {
2584145522Sdarrenr		if (nat_logging)
2585145522Sdarrenr			nat_log(nat, (u_int)np->in_redir);
2586145522Sdarrenr		np->in_use++;
2587153876Sguido		fr = fin->fin_fr;
2588153876Sguido		nat->nat_fr = fr;
2589145522Sdarrenr		if (fr != NULL) {
2590145522Sdarrenr			MUTEX_ENTER(&fr->fr_lock);
2591145522Sdarrenr			fr->fr_ref++;
2592145522Sdarrenr			MUTEX_EXIT(&fr->fr_lock);
2593145522Sdarrenr		}
2594145522Sdarrenr		return 0;
2595145522Sdarrenr	}
259692685Sdarrenr
2597145522Sdarrenr	/*
2598145522Sdarrenr	 * nat_insert failed, so cleanup time...
2599145522Sdarrenr	 */
2600145522Sdarrenr	return -1;
260153642Sguido}
260253642Sguido
260353642Sguido
2604145522Sdarrenr/* ------------------------------------------------------------------------ */
2605145522Sdarrenr/* Function:   nat_insert                                                   */
2606145522Sdarrenr/* Returns:    int - 0 == sucess, -1 == failure                             */
2607145522Sdarrenr/* Parameters: nat(I) - pointer to NAT structure                            */
2608145522Sdarrenr/*             rev(I) - flag indicating forward/reverse direction of packet */
2609145522Sdarrenr/* Write Lock: ipf_nat                                                      */
2610145522Sdarrenr/*                                                                          */
2611145522Sdarrenr/* Insert a NAT entry into the hash tables for searching and add it to the  */
2612145522Sdarrenr/* list of active NAT entries.  Adjust global counters when complete.       */
2613145522Sdarrenr/* ------------------------------------------------------------------------ */
2614145522Sdarrenrint	nat_insert(nat, rev)
261560852Sdarrenrnat_t	*nat;
2616145522Sdarrenrint	rev;
261760852Sdarrenr{
261880482Sdarrenr	u_int hv1, hv2;
261960852Sdarrenr	nat_t **natp;
262060852Sdarrenr
2621145522Sdarrenr	/*
2622145522Sdarrenr	 * Try and return an error as early as possible, so calculate the hash
2623145522Sdarrenr	 * entry numbers first and then proceed.
2624145522Sdarrenr	 */
2625145522Sdarrenr	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
262680482Sdarrenr		hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport,
262780482Sdarrenr				  0xffffffff);
262880482Sdarrenr		hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport,
262980482Sdarrenr				  ipf_nattable_sz);
263080482Sdarrenr		hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport,
263180482Sdarrenr				  0xffffffff);
263280482Sdarrenr		hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport,
2633145522Sdarrenr				  ipf_nattable_sz);
263480482Sdarrenr	} else {
2635145522Sdarrenr		hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff);
2636145522Sdarrenr		hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1, ipf_nattable_sz);
2637145522Sdarrenr		hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff);
2638145522Sdarrenr		hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2, ipf_nattable_sz);
263980482Sdarrenr	}
264080482Sdarrenr
2641145522Sdarrenr	if (nat_stats.ns_bucketlen[0][hv1] >= fr_nat_maxbucket ||
2642145522Sdarrenr	    nat_stats.ns_bucketlen[1][hv2] >= fr_nat_maxbucket) {
2643145522Sdarrenr		return -1;
2644145522Sdarrenr	}
2645145522Sdarrenr
2646145522Sdarrenr	nat->nat_hv[0] = hv1;
2647145522Sdarrenr	nat->nat_hv[1] = hv2;
2648145522Sdarrenr
2649145522Sdarrenr	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
2650145522Sdarrenr
2651145522Sdarrenr	nat->nat_rev = rev;
2652145522Sdarrenr	nat->nat_ref = 1;
2653145522Sdarrenr	nat->nat_bytes[0] = 0;
2654145522Sdarrenr	nat->nat_pkts[0] = 0;
2655145522Sdarrenr	nat->nat_bytes[1] = 0;
2656145522Sdarrenr	nat->nat_pkts[1] = 0;
2657145522Sdarrenr
2658145522Sdarrenr	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
2659145522Sdarrenr	nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 4);
2660145522Sdarrenr
2661161356Sguido	if (nat->nat_ifnames[1][0] != '\0') {
2662145522Sdarrenr		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
2663145522Sdarrenr		nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 4);
2664145522Sdarrenr	} else {
2665145522Sdarrenr		(void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0],
2666145522Sdarrenr			       LIFNAMSIZ);
2667145522Sdarrenr		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
2668145522Sdarrenr		nat->nat_ifps[1] = nat->nat_ifps[0];
2669145522Sdarrenr	}
2670145522Sdarrenr
2671145522Sdarrenr	nat->nat_next = nat_instances;
2672145522Sdarrenr	nat->nat_pnext = &nat_instances;
2673145522Sdarrenr	if (nat_instances)
2674145522Sdarrenr		nat_instances->nat_pnext = &nat->nat_next;
2675145522Sdarrenr	nat_instances = nat;
2676145522Sdarrenr
267780482Sdarrenr	natp = &nat_table[0][hv1];
267867614Sdarrenr	if (*natp)
267967614Sdarrenr		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
268067614Sdarrenr	nat->nat_phnext[0] = natp;
268160852Sdarrenr	nat->nat_hnext[0] = *natp;
268260852Sdarrenr	*natp = nat;
2683145522Sdarrenr	nat_stats.ns_bucketlen[0][hv1]++;
268467614Sdarrenr
268580482Sdarrenr	natp = &nat_table[1][hv2];
268667614Sdarrenr	if (*natp)
268767614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
268867614Sdarrenr	nat->nat_phnext[1] = natp;
268960852Sdarrenr	nat->nat_hnext[1] = *natp;
269060852Sdarrenr	*natp = nat;
2691145522Sdarrenr	nat_stats.ns_bucketlen[1][hv2]++;
269260852Sdarrenr
2693145522Sdarrenr	fr_setnatqueue(nat, rev);
2694145522Sdarrenr
269560852Sdarrenr	nat_stats.ns_added++;
269660852Sdarrenr	nat_stats.ns_inuse++;
2697145522Sdarrenr	return 0;
269860852Sdarrenr}
269960852Sdarrenr
270060852Sdarrenr
2701145522Sdarrenr/* ------------------------------------------------------------------------ */
2702145522Sdarrenr/* Function:    nat_icmperrorlookup                                         */
2703145522Sdarrenr/* Returns:     nat_t* - point to matching NAT structure                    */
2704145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
2705145522Sdarrenr/*              dir(I) - direction of packet (in/out)                       */
2706145522Sdarrenr/*                                                                          */
2707145522Sdarrenr/* Check if the ICMP error message is related to an existing TCP, UDP or    */
2708145522Sdarrenr/* ICMP query nat entry.  It is assumed that the packet is already of the   */
2709145522Sdarrenr/* the required length.                                                     */
2710145522Sdarrenr/* ------------------------------------------------------------------------ */
2711145522Sdarrenrnat_t *nat_icmperrorlookup(fin, dir)
271253642Sguidofr_info_t *fin;
271360852Sdarrenrint dir;
271453642Sguido{
2715145522Sdarrenr	int flags = 0, type, minlen;
2716145522Sdarrenr	icmphdr_t *icmp, *orgicmp;
271753642Sguido	tcphdr_t *tcp = NULL;
2718145522Sdarrenr	u_short data[2];
2719145522Sdarrenr	nat_t *nat;
272053642Sguido	ip_t *oip;
2721145522Sdarrenr	u_int p;
272253642Sguido
2723145522Sdarrenr	icmp = fin->fin_dp;
2724145522Sdarrenr	type = icmp->icmp_type;
272553642Sguido	/*
272653642Sguido	 * Does it at least have the return (basic) IP header ?
272753642Sguido	 * Only a basic IP header (no options) should be with an ICMP error
2728145522Sdarrenr	 * header.  Also, if it's not an error type, then return.
272953642Sguido	 */
2730153876Sguido	if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR))
273153642Sguido		return NULL;
2732145522Sdarrenr
273353642Sguido	/*
2734145522Sdarrenr	 * Check packet size
273553642Sguido	 */
273653642Sguido	oip = (ip_t *)((char *)fin->fin_dp + 8);
2737145522Sdarrenr	minlen = IP_HL(oip) << 2;
2738145522Sdarrenr	if ((minlen < sizeof(ip_t)) ||
2739145522Sdarrenr	    (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen))
274053642Sguido		return NULL;
274164580Sdarrenr	/*
274264580Sdarrenr	 * Is the buffer big enough for all of it ?  It's the size of the IP
274364580Sdarrenr	 * header claimed in the encapsulated part which is of concern.  It
274464580Sdarrenr	 * may be too big to be in this buffer but not so big that it's
274564580Sdarrenr	 * outside the ICMP packet, leading to TCP deref's causing problems.
274664580Sdarrenr	 * This is possible because we don't know how big oip_hl is when we
274764580Sdarrenr	 * do the pullup early in fr_check() and thus can't gaurantee it is
274864580Sdarrenr	 * all here now.
274964580Sdarrenr	 */
275064580Sdarrenr#ifdef  _KERNEL
275164580Sdarrenr	{
275264580Sdarrenr	mb_t *m;
275364580Sdarrenr
2754145522Sdarrenr	m = fin->fin_m;
2755145522Sdarrenr# if defined(MENTAT)
275664580Sdarrenr	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr)
275764580Sdarrenr		return NULL;
275864580Sdarrenr# else
275964580Sdarrenr	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
2760145522Sdarrenr	    (char *)fin->fin_ip + M_LEN(m))
276164580Sdarrenr		return NULL;
276264580Sdarrenr# endif
276364580Sdarrenr	}
276464580Sdarrenr#endif
276564580Sdarrenr
2766145522Sdarrenr	if (fin->fin_daddr != oip->ip_src.s_addr)
2767145522Sdarrenr		return NULL;
2768145522Sdarrenr
2769145522Sdarrenr	p = oip->ip_p;
2770145522Sdarrenr	if (p == IPPROTO_TCP)
277153642Sguido		flags = IPN_TCP;
2772145522Sdarrenr	else if (p == IPPROTO_UDP)
277353642Sguido		flags = IPN_UDP;
2774145522Sdarrenr	else if (p == IPPROTO_ICMP) {
2775145522Sdarrenr		orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2));
2776145522Sdarrenr
2777145522Sdarrenr		/* see if this is related to an ICMP query */
2778145522Sdarrenr		if (nat_icmpquerytype4(orgicmp->icmp_type)) {
2779145522Sdarrenr			data[0] = fin->fin_data[0];
2780145522Sdarrenr			data[1] = fin->fin_data[1];
2781145522Sdarrenr			fin->fin_data[0] = 0;
2782145522Sdarrenr			fin->fin_data[1] = orgicmp->icmp_id;
2783145522Sdarrenr
2784145522Sdarrenr			flags = IPN_ICMPERR|IPN_ICMPQUERY;
2785145522Sdarrenr			/*
2786145522Sdarrenr			 * NOTE : dir refers to the direction of the original
2787145522Sdarrenr			 *        ip packet. By definition the icmp error
2788145522Sdarrenr			 *        message flows in the opposite direction.
2789145522Sdarrenr			 */
2790145522Sdarrenr			if (dir == NAT_INBOUND)
2791145522Sdarrenr				nat = nat_inlookup(fin, flags, p, oip->ip_dst,
2792145522Sdarrenr						   oip->ip_src);
2793145522Sdarrenr			else
2794145522Sdarrenr				nat = nat_outlookup(fin, flags, p, oip->ip_dst,
2795145522Sdarrenr						    oip->ip_src);
2796145522Sdarrenr			fin->fin_data[0] = data[0];
2797145522Sdarrenr			fin->fin_data[1] = data[1];
2798145522Sdarrenr			return nat;
2799145522Sdarrenr		}
2800145522Sdarrenr	}
2801145522Sdarrenr
280253642Sguido	if (flags & IPN_TCPUDP) {
280364580Sdarrenr		minlen += 8;		/* + 64bits of data to get ports */
2804145522Sdarrenr		if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)
280564580Sdarrenr			return NULL;
280692685Sdarrenr
280792685Sdarrenr		data[0] = fin->fin_data[0];
280892685Sdarrenr		data[1] = fin->fin_data[1];
2809145522Sdarrenr		tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2));
281092685Sdarrenr		fin->fin_data[0] = ntohs(tcp->th_dport);
281192685Sdarrenr		fin->fin_data[1] = ntohs(tcp->th_sport);
281292685Sdarrenr
281392685Sdarrenr		if (dir == NAT_INBOUND) {
2814145522Sdarrenr			nat = nat_inlookup(fin, flags, p, oip->ip_dst,
2815145522Sdarrenr					   oip->ip_src);
281692685Sdarrenr		} else {
2817145522Sdarrenr			nat = nat_outlookup(fin, flags, p, oip->ip_dst,
2818145522Sdarrenr					    oip->ip_src);
281992685Sdarrenr		}
282092685Sdarrenr		fin->fin_data[0] = data[0];
282192685Sdarrenr		fin->fin_data[1] = data[1];
282292685Sdarrenr		return nat;
282353642Sguido	}
282460852Sdarrenr	if (dir == NAT_INBOUND)
2825145522Sdarrenr		return nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
282660852Sdarrenr	else
2827145522Sdarrenr		return nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
282853642Sguido}
282953642Sguido
283053642Sguido
2831145522Sdarrenr/* ------------------------------------------------------------------------ */
2832145522Sdarrenr/* Function:    nat_icmperror                                               */
2833145522Sdarrenr/* Returns:     nat_t* - point to matching NAT structure                    */
2834145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
2835145522Sdarrenr/*              nflags(I) - NAT flags for this packet                       */
2836145522Sdarrenr/*              dir(I)    - direction of packet (in/out)                    */
2837145522Sdarrenr/*                                                                          */
2838145522Sdarrenr/* Fix up an ICMP packet which is an error message for an existing NAT      */
2839145522Sdarrenr/* session.  This will correct both packet header data and checksums.       */
2840145522Sdarrenr/*                                                                          */
2841145522Sdarrenr/* This should *ONLY* be used for incoming ICMP error packets to make sure  */
2842145522Sdarrenr/* a NAT'd ICMP packet gets correctly recognised.                           */
2843145522Sdarrenr/* ------------------------------------------------------------------------ */
2844145522Sdarrenrnat_t *nat_icmperror(fin, nflags, dir)
284553642Sguidofr_info_t *fin;
284653642Sguidou_int *nflags;
284760852Sdarrenrint dir;
284853642Sguido{
2849145522Sdarrenr	u_32_t sum1, sum2, sumd, sumd2;
2850170268Sdarrenr	struct in_addr a1, a2;
2851170268Sdarrenr	int flags, dlen, odst;
2852145522Sdarrenr	icmphdr_t *icmp;
2853145522Sdarrenr	u_short *csump;
285495418Sdarrenr	tcphdr_t *tcp;
285553642Sguido	nat_t *nat;
285653642Sguido	ip_t *oip;
2857145522Sdarrenr	void *dp;
285853642Sguido
2859145522Sdarrenr	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY)))
286063523Sdarrenr		return NULL;
286167614Sdarrenr	/*
2862145522Sdarrenr	 * nat_icmperrorlookup() will return NULL for `defective' packets.
286367614Sdarrenr	 */
2864145522Sdarrenr	if ((fin->fin_v != 4) || !(nat = nat_icmperrorlookup(fin, dir)))
286553642Sguido		return NULL;
286692685Sdarrenr
2867145522Sdarrenr	tcp = NULL;
2868145522Sdarrenr	csump = NULL;
286992685Sdarrenr	flags = 0;
2870130886Sdarrenr	sumd2 = 0;
287153642Sguido	*nflags = IPN_ICMPERR;
2872145522Sdarrenr	icmp = fin->fin_dp;
287353642Sguido	oip = (ip_t *)&icmp->icmp_ip;
2874145522Sdarrenr	dp = (((char *)oip) + (IP_HL(oip) << 2));
2875145522Sdarrenr	if (oip->ip_p == IPPROTO_TCP) {
2876145522Sdarrenr		tcp = (tcphdr_t *)dp;
2877145522Sdarrenr		csump = (u_short *)&tcp->th_sum;
287853642Sguido		flags = IPN_TCP;
2879145522Sdarrenr	} else if (oip->ip_p == IPPROTO_UDP) {
2880145522Sdarrenr		udphdr_t *udp;
2881145522Sdarrenr
2882145522Sdarrenr		udp = (udphdr_t *)dp;
2883145522Sdarrenr		tcp = (tcphdr_t *)dp;
2884145522Sdarrenr		csump = (u_short *)&udp->uh_sum;
288553642Sguido		flags = IPN_UDP;
2886145522Sdarrenr	} else if (oip->ip_p == IPPROTO_ICMP)
2887145522Sdarrenr		flags = IPN_ICMPQUERY;
2888145522Sdarrenr	dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip);
288995418Sdarrenr
289095418Sdarrenr	/*
289153642Sguido	 * Need to adjust ICMP header to include the real IP#'s and
289253642Sguido	 * port #'s.  Only apply a checksum change relative to the
2893145522Sdarrenr	 * IP address change as it will be modified again in fr_checknatout
289453642Sguido	 * for both address and port.  Two checksum changes are
289553642Sguido	 * necessary for the two header address changes.  Be careful
289653642Sguido	 * to only modify the checksum once for the port # and twice
289753642Sguido	 * for the IP#.
289853642Sguido	 */
289960852Sdarrenr
290067614Sdarrenr	/*
290167614Sdarrenr	 * Step 1
290267614Sdarrenr	 * Fix the IP addresses in the offending IP packet. You also need
2903170268Sdarrenr	 * to adjust the IP header checksum of that offending IP packet.
290467614Sdarrenr	 *
2905145522Sdarrenr	 * Normally, you would expect that the ICMP checksum of the
2906130886Sdarrenr	 * ICMP error message needs to be adjusted as well for the
2907130886Sdarrenr	 * IP address change in oip.
2908145522Sdarrenr	 * However, this is a NOP, because the ICMP checksum is
2909130886Sdarrenr	 * calculated over the complete ICMP packet, which includes the
2910145522Sdarrenr	 * changed oip IP addresses and oip->ip_sum. However, these
2911130886Sdarrenr	 * two changes cancel each other out (if the delta for
2912145522Sdarrenr	 * the IP address is x, then the delta for ip_sum is minus x),
2913130886Sdarrenr	 * so no change in the icmp_cksum is necessary.
2914130886Sdarrenr	 *
2915170268Sdarrenr	 * Inbound ICMP
2916170268Sdarrenr	 * ------------
2917170268Sdarrenr	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
2918170268Sdarrenr	 * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b)
2919170268Sdarrenr	 * - OIP_SRC(c)=nat_outip, OIP_DST(b)=nat_oip
2920170268Sdarrenr	 *
2921170268Sdarrenr	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
2922170268Sdarrenr	 * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a)
2923170268Sdarrenr	 * - OIP_SRC(b)=nat_outip, OIP_DST(a)=nat_oip
2924170268Sdarrenr	 *
2925170268Sdarrenr	 * Outbound ICMP
2926170268Sdarrenr	 * -------------
2927170268Sdarrenr	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
2928170268Sdarrenr	 * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
2929170268Sdarrenr	 * - OIP_SRC(a)=nat_oip, OIP_DST(c)=nat_inip
2930170268Sdarrenr	 *
2931170268Sdarrenr	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
2932170268Sdarrenr	 * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c)
2933170268Sdarrenr	 * - OIP_SRC(a)=nat_oip, OIP_DST(c)=nat_inip
2934170268Sdarrenr	 *
2935130886Sdarrenr	 */
2936170268Sdarrenr	odst = (oip->ip_dst.s_addr == nat->nat_oip.s_addr) ? 1 : 0;
2937170268Sdarrenr	if (odst == 1) {
2938170268Sdarrenr		a1.s_addr = ntohl(nat->nat_inip.s_addr);
2939170268Sdarrenr		a2.s_addr = ntohl(oip->ip_src.s_addr);
2940170268Sdarrenr		oip->ip_src.s_addr = htonl(a1.s_addr);
2941170268Sdarrenr	} else {
2942170268Sdarrenr		a1.s_addr = ntohl(nat->nat_outip.s_addr);
2943170268Sdarrenr		a2.s_addr = ntohl(oip->ip_dst.s_addr);
2944170268Sdarrenr		oip->ip_dst.s_addr = htonl(a1.s_addr);
2945170268Sdarrenr	}
2946130886Sdarrenr
2947170268Sdarrenr	sumd = a2.s_addr - a1.s_addr;
2948170268Sdarrenr	if (sumd != 0) {
2949170268Sdarrenr		if (a1.s_addr > a2.s_addr)
2950170268Sdarrenr			sumd--;
2951170268Sdarrenr		sumd = ~sumd;
295253642Sguido
2953170268Sdarrenr		fix_datacksum(&oip->ip_sum, sumd);
2954130886Sdarrenr	}
295567614Sdarrenr
2956170268Sdarrenr	sumd2 = sumd;
2957170268Sdarrenr	sum1 = 0;
2958170268Sdarrenr	sum2 = 0;
2959170268Sdarrenr
2960130886Sdarrenr	/*
2961170268Sdarrenr	 * Fix UDP pseudo header checksum to compensate for the
2962170268Sdarrenr	 * IP address change.
2963130886Sdarrenr	 */
2964130886Sdarrenr	if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) {
296564580Sdarrenr		/*
296667614Sdarrenr		 * Step 2 :
296767614Sdarrenr		 * For offending TCP/UDP IP packets, translate the ports as
296867614Sdarrenr		 * well, based on the NAT specification. Of course such
2969170268Sdarrenr		 * a change may be reflected in the ICMP checksum as well.
297067614Sdarrenr		 *
297167614Sdarrenr		 * Since the port fields are part of the TCP/UDP checksum
297267614Sdarrenr		 * of the offending IP packet, you need to adjust that checksum
2973161356Sguido		 * as well... except that the change in the port numbers should
2974170268Sdarrenr		 * be offset by the checksum change.  However, the TCP/UDP
2975170268Sdarrenr		 * checksum will also need to change if there has been an
2976170268Sdarrenr		 * IP address change.
297767614Sdarrenr		 */
2978170268Sdarrenr		if (odst == 1) {
2979170268Sdarrenr			sum1 = ntohs(nat->nat_inport);
2980170268Sdarrenr			sum2 = ntohs(tcp->th_sport);
2981145522Sdarrenr
2982170268Sdarrenr			tcp->th_sport = htons(sum1);
2983170268Sdarrenr		} else {
2984145522Sdarrenr			sum1 = ntohs(nat->nat_outport);
2985145522Sdarrenr			sum2 = ntohs(tcp->th_dport);
2986170268Sdarrenr
2987170268Sdarrenr			tcp->th_dport = htons(sum1);
2988145522Sdarrenr		}
298967614Sdarrenr
2990170268Sdarrenr		sumd += sum1 - sum2;
2991170268Sdarrenr		if (sumd != 0 || sumd2 != 0) {
2992145522Sdarrenr			/*
2993170268Sdarrenr			 * At this point, sumd is the delta to apply to the
2994170268Sdarrenr			 * TCP/UDP header, given the changes in both the IP
2995170268Sdarrenr			 * address and the ports and sumd2 is the delta to
2996170268Sdarrenr			 * apply to the ICMP header, given the IP address
2997170268Sdarrenr			 * change delta that may need to be applied to the
2998170268Sdarrenr			 * TCP/UDP checksum instead.
2999145522Sdarrenr			 *
3000170268Sdarrenr			 * If we will both the IP and TCP/UDP checksums
3001170268Sdarrenr			 * then the ICMP checksum changes by the address
3002170268Sdarrenr			 * delta applied to the TCP/UDP checksum.  If we
3003170268Sdarrenr			 * do not change the TCP/UDP checksum them we
3004170268Sdarrenr			 * apply the delta in ports to the ICMP checksum.
3005145522Sdarrenr			 */
3006161356Sguido			if (oip->ip_p == IPPROTO_UDP) {
3007161356Sguido				if ((dlen >= 8) && (*csump != 0)) {
3008161356Sguido					fix_datacksum(csump, sumd);
3009161356Sguido				} else {
3010170268Sdarrenr					sumd2 = sum1 - sum2;
3011170268Sdarrenr					if (sum2 > sum1)
3012170268Sdarrenr						sumd2--;
3013161356Sguido				}
3014170268Sdarrenr			} else if (oip->ip_p == IPPROTO_TCP) {
3015145522Sdarrenr				if (dlen >= 18) {
3016145522Sdarrenr					fix_datacksum(csump, sumd);
3017145522Sdarrenr				} else {
3018170268Sdarrenr					sumd2 = sum2 - sum1;
3019170268Sdarrenr					if (sum1 > sum2)
3020170268Sdarrenr						sumd2--;
302167614Sdarrenr				}
302253642Sguido			}
3023130886Sdarrenr
3024170268Sdarrenr			if (sumd2 != 0) {
3025172776Sdarrenr				ipnat_t *np;
3026172776Sdarrenr
3027172776Sdarrenr				np = nat->nat_ptr;
3028170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
3029170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
3030170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
3031172776Sdarrenr
3032172776Sdarrenr				if ((odst == 0) && (dir == NAT_OUTBOUND) &&
3033172776Sdarrenr				    (fin->fin_rev == 0) && (np != NULL) &&
3034172776Sdarrenr				    (np->in_redir & NAT_REDIRECT)) {
3035172776Sdarrenr					fix_outcksum(fin, &icmp->icmp_cksum,
3036172776Sdarrenr						     sumd2);
3037172776Sdarrenr				} else {
3038172776Sdarrenr					fix_incksum(fin, &icmp->icmp_cksum,
3039172776Sdarrenr						    sumd2);
3040172776Sdarrenr				}
3041130886Sdarrenr			}
304253642Sguido		}
3043145522Sdarrenr	} else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) {
3044145522Sdarrenr		icmphdr_t *orgicmp;
3045145522Sdarrenr
3046145522Sdarrenr		/*
3047145522Sdarrenr		 * XXX - what if this is bogus hl and we go off the end ?
3048145522Sdarrenr		 * In this case, nat_icmperrorlookup() will have returned NULL.
3049145522Sdarrenr		 */
3050145522Sdarrenr		orgicmp = (icmphdr_t *)dp;
3051145522Sdarrenr
3052170268Sdarrenr		if (odst == 1) {
3053145522Sdarrenr			if (orgicmp->icmp_id != nat->nat_inport) {
3054145522Sdarrenr
3055145522Sdarrenr				/*
3056145522Sdarrenr				 * Fix ICMP checksum (of the offening ICMP
3057145522Sdarrenr				 * query packet) to compensate the change
3058145522Sdarrenr				 * in the ICMP id of the offending ICMP
3059145522Sdarrenr				 * packet.
3060145522Sdarrenr				 *
3061145522Sdarrenr				 * Since you modify orgicmp->icmp_id with
3062145522Sdarrenr				 * a delta (say x) and you compensate that
3063145522Sdarrenr				 * in origicmp->icmp_cksum with a delta
3064145522Sdarrenr				 * minus x, you don't have to adjust the
3065145522Sdarrenr				 * overall icmp->icmp_cksum
3066145522Sdarrenr				 */
3067145522Sdarrenr				sum1 = ntohs(orgicmp->icmp_id);
3068145522Sdarrenr				sum2 = ntohs(nat->nat_inport);
3069145522Sdarrenr				CALC_SUMD(sum1, sum2, sumd);
3070145522Sdarrenr				orgicmp->icmp_id = nat->nat_inport;
3071145522Sdarrenr				fix_datacksum(&orgicmp->icmp_cksum, sumd);
3072145522Sdarrenr			}
3073145522Sdarrenr		} /* nat_dir == NAT_INBOUND is impossible for icmp queries */
307453642Sguido	}
307553642Sguido	return nat;
307653642Sguido}
307753642Sguido
307853642Sguido
307953642Sguido/*
3080145522Sdarrenr * NB: these lookups don't lock access to the list, it assumed that it has
3081145522Sdarrenr * already been done!
308253642Sguido */
3083145522Sdarrenr
3084145522Sdarrenr/* ------------------------------------------------------------------------ */
3085145522Sdarrenr/* Function:    nat_inlookup                                                */
3086145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
3087145522Sdarrenr/*                       else pointer to matching NAT entry                 */
3088145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
3089145522Sdarrenr/*              flags(I)  - NAT flags for this packet                       */
3090145522Sdarrenr/*              p(I)      - protocol for this packet                        */
3091145522Sdarrenr/*              src(I)    - source IP address                               */
3092145522Sdarrenr/*              mapdst(I) - destination IP address                          */
3093145522Sdarrenr/*                                                                          */
3094145522Sdarrenr/* Lookup a nat entry based on the mapped destination ip address/port and   */
3095145522Sdarrenr/* real source address/port.  We use this lookup when receiving a packet,   */
3096145522Sdarrenr/* we're looking for a table entry, based on the destination address.       */
3097145522Sdarrenr/*                                                                          */
3098145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
3099145522Sdarrenr/*                                                                          */
3100145522Sdarrenr/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
3101145522Sdarrenr/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
3102145522Sdarrenr/*                                                                          */
3103145522Sdarrenr/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
3104145522Sdarrenr/*            the packet is of said protocol                                */
3105145522Sdarrenr/* ------------------------------------------------------------------------ */
3106145522Sdarrenrnat_t *nat_inlookup(fin, flags, p, src, mapdst)
310792685Sdarrenrfr_info_t *fin;
3108145522Sdarrenru_int flags, p;
310953642Sguidostruct in_addr src , mapdst;
311053642Sguido{
3111145522Sdarrenr	u_short sport, dport;
3112145522Sdarrenr	grehdr_t *gre;
311392685Sdarrenr	ipnat_t *ipn;
3114145522Sdarrenr	u_int sflags;
3115145522Sdarrenr	nat_t *nat;
3116145522Sdarrenr	int nflags;
3117145522Sdarrenr	u_32_t dst;
311892685Sdarrenr	void *ifp;
311953642Sguido	u_int hv;
312053642Sguido
3121161356Sguido	ifp = fin->fin_ifp;
3122145522Sdarrenr	sport = 0;
3123145522Sdarrenr	dport = 0;
3124145522Sdarrenr	gre = NULL;
312567614Sdarrenr	dst = mapdst.s_addr;
3126145522Sdarrenr	sflags = flags & NAT_TCPUDPICMP;
3127145522Sdarrenr
3128145522Sdarrenr	switch (p)
3129145522Sdarrenr	{
3130145522Sdarrenr	case IPPROTO_TCP :
3131145522Sdarrenr	case IPPROTO_UDP :
313292685Sdarrenr		sport = htons(fin->fin_data[0]);
313392685Sdarrenr		dport = htons(fin->fin_data[1]);
3134145522Sdarrenr		break;
3135145522Sdarrenr	case IPPROTO_ICMP :
3136145522Sdarrenr		if (flags & IPN_ICMPERR)
3137145522Sdarrenr			sport = fin->fin_data[1];
3138145522Sdarrenr		else
3139145522Sdarrenr			dport = fin->fin_data[1];
3140145522Sdarrenr		break;
3141145522Sdarrenr	default :
3142145522Sdarrenr		break;
314392685Sdarrenr	}
314453642Sguido
3145145522Sdarrenr
3146145522Sdarrenr	if ((flags & SI_WILDP) != 0)
3147145522Sdarrenr		goto find_in_wild_ports;
3148145522Sdarrenr
314980482Sdarrenr	hv = NAT_HASH_FN(dst, dport, 0xffffffff);
315080482Sdarrenr	hv = NAT_HASH_FN(src.s_addr, hv + sport, ipf_nattable_sz);
315153642Sguido	nat = nat_table[1][hv];
315253642Sguido	for (; nat; nat = nat->nat_hnext[1]) {
3153161356Sguido		if (nat->nat_ifps[0] != NULL) {
3154161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
3155161356Sguido				continue;
3156161356Sguido		} else if (ifp != NULL)
3157161356Sguido			nat->nat_ifps[0] = ifp;
3158161356Sguido
315953642Sguido		nflags = nat->nat_flags;
3160145522Sdarrenr
3161145522Sdarrenr		if (nat->nat_oip.s_addr == src.s_addr &&
316267614Sdarrenr		    nat->nat_outip.s_addr == dst &&
3163145522Sdarrenr		    (((p == 0) &&
3164145522Sdarrenr		      (sflags == (nat->nat_flags & IPN_TCPUDPICMP)))
3165145522Sdarrenr		     || (p == nat->nat_p))) {
316692685Sdarrenr			switch (p)
316792685Sdarrenr			{
3168145522Sdarrenr#if 0
3169145522Sdarrenr			case IPPROTO_GRE :
3170145522Sdarrenr				if (nat->nat_call[1] != fin->fin_data[0])
3171145522Sdarrenr					continue;
3172145522Sdarrenr				break;
3173145522Sdarrenr#endif
3174145522Sdarrenr			case IPPROTO_ICMP :
3175145522Sdarrenr				if ((flags & IPN_ICMPERR) != 0) {
3176145522Sdarrenr					if (nat->nat_outport != sport)
3177145522Sdarrenr						continue;
3178145522Sdarrenr				} else {
3179145522Sdarrenr					if (nat->nat_outport != dport)
3180145522Sdarrenr						continue;
3181145522Sdarrenr				}
3182145522Sdarrenr				break;
318392685Sdarrenr			case IPPROTO_TCP :
318492685Sdarrenr			case IPPROTO_UDP :
318592685Sdarrenr				if (nat->nat_oport != sport)
318692685Sdarrenr					continue;
318792685Sdarrenr				if (nat->nat_outport != dport)
318892685Sdarrenr					continue;
318992685Sdarrenr				break;
319092685Sdarrenr			default :
319192685Sdarrenr				break;
319292685Sdarrenr			}
319392685Sdarrenr
319492685Sdarrenr			ipn = nat->nat_ptr;
319592685Sdarrenr			if ((ipn != NULL) && (nat->nat_aps != NULL))
319692685Sdarrenr				if (appr_match(fin, nat) != 0)
319792685Sdarrenr					continue;
319853642Sguido			return nat;
319992685Sdarrenr		}
320053642Sguido	}
3201145522Sdarrenr
3202145522Sdarrenr	/*
3203145522Sdarrenr	 * So if we didn't find it but there are wildcard members in the hash
3204145522Sdarrenr	 * table, go back and look for them.  We do this search and update here
3205145522Sdarrenr	 * because it is modifying the NAT table and we want to do this only
3206145522Sdarrenr	 * for the first packet that matches.  The exception, of course, is
3207145522Sdarrenr	 * for "dummy" (FI_IGNORE) lookups.
3208145522Sdarrenr	 */
3209145522Sdarrenrfind_in_wild_ports:
3210145522Sdarrenr	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
321167614Sdarrenr		return NULL;
3212145522Sdarrenr	if (nat_stats.ns_wilds == 0)
3213145522Sdarrenr		return NULL;
3214145522Sdarrenr
3215145522Sdarrenr	RWLOCK_EXIT(&ipf_nat);
3216145522Sdarrenr
321780482Sdarrenr	hv = NAT_HASH_FN(dst, 0, 0xffffffff);
3218145522Sdarrenr	hv = NAT_HASH_FN(src.s_addr, hv, ipf_nattable_sz);
3219145522Sdarrenr
3220145522Sdarrenr	WRITE_ENTER(&ipf_nat);
3221145522Sdarrenr
322267614Sdarrenr	nat = nat_table[1][hv];
322367614Sdarrenr	for (; nat; nat = nat->nat_hnext[1]) {
3224161356Sguido		if (nat->nat_ifps[0] != NULL) {
3225161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
3226161356Sguido				continue;
3227161356Sguido		} else if (ifp != NULL)
3228161356Sguido			nat->nat_ifps[0] = ifp;
3229145522Sdarrenr
3230145522Sdarrenr		if (nat->nat_p != fin->fin_p)
323167614Sdarrenr			continue;
323267614Sdarrenr		if (nat->nat_oip.s_addr != src.s_addr ||
323367614Sdarrenr		    nat->nat_outip.s_addr != dst)
323467614Sdarrenr			continue;
3235145522Sdarrenr
3236145522Sdarrenr		nflags = nat->nat_flags;
3237145522Sdarrenr		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
3238145522Sdarrenr			continue;
3239145522Sdarrenr
3240145522Sdarrenr		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
3241145522Sdarrenr			       NAT_INBOUND) == 1) {
3242145522Sdarrenr			if ((fin->fin_flx & FI_IGNORE) != 0)
3243145522Sdarrenr				break;
3244145522Sdarrenr			if ((nflags & SI_CLONE) != 0) {
3245145522Sdarrenr				nat = fr_natclone(fin, nat);
3246145522Sdarrenr				if (nat == NULL)
3247145522Sdarrenr					break;
3248145522Sdarrenr			} else {
3249145522Sdarrenr				MUTEX_ENTER(&ipf_nat_new);
3250145522Sdarrenr				nat_stats.ns_wilds--;
3251145522Sdarrenr				MUTEX_EXIT(&ipf_nat_new);
3252145522Sdarrenr			}
3253145522Sdarrenr			nat->nat_oport = sport;
3254145522Sdarrenr			nat->nat_outport = dport;
3255145522Sdarrenr			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
3256145522Sdarrenr			nat_tabmove(nat);
325767614Sdarrenr			break;
325867614Sdarrenr		}
325967614Sdarrenr	}
3260145522Sdarrenr
3261145522Sdarrenr	MUTEX_DOWNGRADE(&ipf_nat);
3262145522Sdarrenr
326367614Sdarrenr	return nat;
326453642Sguido}
326553642Sguido
326653642Sguido
3267145522Sdarrenr/* ------------------------------------------------------------------------ */
3268145522Sdarrenr/* Function:    nat_tabmove                                                 */
3269145522Sdarrenr/* Returns:     Nil                                                         */
3270145522Sdarrenr/* Parameters:  nat(I) - pointer to NAT structure                           */
3271145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
3272145522Sdarrenr/*                                                                          */
3273145522Sdarrenr/* This function is only called for TCP/UDP NAT table entries where the     */
3274145522Sdarrenr/* original was placed in the table without hashing on the ports and we now */
3275145522Sdarrenr/* want to include hashing on port numbers.                                 */
3276145522Sdarrenr/* ------------------------------------------------------------------------ */
3277145522Sdarrenrstatic void nat_tabmove(nat)
327867614Sdarrenrnat_t *nat;
327967614Sdarrenr{
328067614Sdarrenr	nat_t **natp;
3281145522Sdarrenr	u_int hv;
328267614Sdarrenr
3283145522Sdarrenr	if (nat->nat_flags & SI_CLONE)
3284145522Sdarrenr		return;
328572006Sdarrenr
328667614Sdarrenr	/*
328767614Sdarrenr	 * Remove the NAT entry from the old location
328867614Sdarrenr	 */
328967614Sdarrenr	if (nat->nat_hnext[0])
329067614Sdarrenr		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
329167614Sdarrenr	*nat->nat_phnext[0] = nat->nat_hnext[0];
3292145522Sdarrenr	nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
329367614Sdarrenr
329467614Sdarrenr	if (nat->nat_hnext[1])
329567853Sdarrenr		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
329667614Sdarrenr	*nat->nat_phnext[1] = nat->nat_hnext[1];
3297145522Sdarrenr	nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
329867614Sdarrenr
329967853Sdarrenr	/*
330067853Sdarrenr	 * Add into the NAT table in the new position
330167853Sdarrenr	 */
3302145522Sdarrenr	hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 0xffffffff);
3303145522Sdarrenr	hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport,
3304145522Sdarrenr			 ipf_nattable_sz);
3305145522Sdarrenr	nat->nat_hv[0] = hv;
330667614Sdarrenr	natp = &nat_table[0][hv];
330767614Sdarrenr	if (*natp)
330867614Sdarrenr		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
330967614Sdarrenr	nat->nat_phnext[0] = natp;
331067614Sdarrenr	nat->nat_hnext[0] = *natp;
331167614Sdarrenr	*natp = nat;
3312145522Sdarrenr	nat_stats.ns_bucketlen[0][hv]++;
331367614Sdarrenr
3314145522Sdarrenr	hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 0xffffffff);
3315145522Sdarrenr	hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport,
3316145522Sdarrenr			 ipf_nattable_sz);
3317145522Sdarrenr	nat->nat_hv[1] = hv;
331867614Sdarrenr	natp = &nat_table[1][hv];
331967614Sdarrenr	if (*natp)
332067614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
332167614Sdarrenr	nat->nat_phnext[1] = natp;
332267614Sdarrenr	nat->nat_hnext[1] = *natp;
332367614Sdarrenr	*natp = nat;
3324145522Sdarrenr	nat_stats.ns_bucketlen[1][hv]++;
332567614Sdarrenr}
332667614Sdarrenr
332767614Sdarrenr
3328145522Sdarrenr/* ------------------------------------------------------------------------ */
3329145522Sdarrenr/* Function:    nat_outlookup                                               */
3330145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
3331145522Sdarrenr/*                       else pointer to matching NAT entry                 */
3332145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
3333145522Sdarrenr/*              flags(I) - NAT flags for this packet                        */
3334145522Sdarrenr/*              p(I)     - protocol for this packet                         */
3335145522Sdarrenr/*              src(I)   - source IP address                                */
3336145522Sdarrenr/*              dst(I)   - destination IP address                           */
3337145522Sdarrenr/*              rw(I)    - 1 == write lock on ipf_nat held, 0 == read lock. */
3338145522Sdarrenr/*                                                                          */
3339145522Sdarrenr/* Lookup a nat entry based on the source 'real' ip address/port and        */
3340145522Sdarrenr/* destination address/port.  We use this lookup when sending a packet out, */
3341145522Sdarrenr/* we're looking for a table entry, based on the source address.            */
3342145522Sdarrenr/*                                                                          */
3343145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
3344145522Sdarrenr/*                                                                          */
3345145522Sdarrenr/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
3346145522Sdarrenr/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
3347145522Sdarrenr/*                                                                          */
3348145522Sdarrenr/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
3349145522Sdarrenr/*            the packet is of said protocol                                */
3350145522Sdarrenr/* ------------------------------------------------------------------------ */
3351145522Sdarrenrnat_t *nat_outlookup(fin, flags, p, src, dst)
335292685Sdarrenrfr_info_t *fin;
3353145522Sdarrenru_int flags, p;
335453642Sguidostruct in_addr src , dst;
335553642Sguido{
3356145522Sdarrenr	u_short sport, dport;
3357145522Sdarrenr	u_int sflags;
335892685Sdarrenr	ipnat_t *ipn;
335967614Sdarrenr	u_32_t srcip;
3360145522Sdarrenr	nat_t *nat;
3361145522Sdarrenr	int nflags;
336292685Sdarrenr	void *ifp;
336353642Sguido	u_int hv;
336453642Sguido
336592685Sdarrenr	ifp = fin->fin_ifp;
336667614Sdarrenr	srcip = src.s_addr;
3367145522Sdarrenr	sflags = flags & IPN_TCPUDPICMP;
3368145522Sdarrenr	sport = 0;
3369145522Sdarrenr	dport = 0;
3370145522Sdarrenr
3371145522Sdarrenr	switch (p)
3372145522Sdarrenr	{
3373145522Sdarrenr	case IPPROTO_TCP :
3374145522Sdarrenr	case IPPROTO_UDP :
3375145522Sdarrenr		sport = htons(fin->fin_data[0]);
3376145522Sdarrenr		dport = htons(fin->fin_data[1]);
3377145522Sdarrenr		break;
3378145522Sdarrenr	case IPPROTO_ICMP :
3379145522Sdarrenr		if (flags & IPN_ICMPERR)
3380145522Sdarrenr			sport = fin->fin_data[1];
3381145522Sdarrenr		else
3382145522Sdarrenr			dport = fin->fin_data[1];
3383145522Sdarrenr		break;
3384145522Sdarrenr	default :
3385145522Sdarrenr		break;
338692685Sdarrenr	}
338753642Sguido
3388145522Sdarrenr	if ((flags & SI_WILDP) != 0)
3389145522Sdarrenr		goto find_out_wild_ports;
3390145522Sdarrenr
339180482Sdarrenr	hv = NAT_HASH_FN(srcip, sport, 0xffffffff);
339280482Sdarrenr	hv = NAT_HASH_FN(dst.s_addr, hv + dport, ipf_nattable_sz);
339353642Sguido	nat = nat_table[0][hv];
339453642Sguido	for (; nat; nat = nat->nat_hnext[0]) {
3395161356Sguido		if (nat->nat_ifps[1] != NULL) {
3396161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
3397161356Sguido				continue;
3398161356Sguido		} else if (ifp != NULL)
3399161356Sguido			nat->nat_ifps[1] = ifp;
3400161356Sguido
340153642Sguido		nflags = nat->nat_flags;
340253642Sguido
3403145522Sdarrenr		if (nat->nat_inip.s_addr == srcip &&
340453642Sguido		    nat->nat_oip.s_addr == dst.s_addr &&
3405145522Sdarrenr		    (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP)))
3406145522Sdarrenr		     || (p == nat->nat_p))) {
340792685Sdarrenr			switch (p)
340892685Sdarrenr			{
3409145522Sdarrenr#if 0
3410145522Sdarrenr			case IPPROTO_GRE :
3411145522Sdarrenr				if (nat->nat_call[1] != fin->fin_data[0])
3412145522Sdarrenr					continue;
3413145522Sdarrenr				break;
3414145522Sdarrenr#endif
341592685Sdarrenr			case IPPROTO_TCP :
341692685Sdarrenr			case IPPROTO_UDP :
341792685Sdarrenr				if (nat->nat_oport != dport)
341892685Sdarrenr					continue;
341992685Sdarrenr				if (nat->nat_inport != sport)
342092685Sdarrenr					continue;
342192685Sdarrenr				break;
342292685Sdarrenr			default :
342392685Sdarrenr				break;
342492685Sdarrenr			}
342592685Sdarrenr
342692685Sdarrenr			ipn = nat->nat_ptr;
342792685Sdarrenr			if ((ipn != NULL) && (nat->nat_aps != NULL))
342892685Sdarrenr				if (appr_match(fin, nat) != 0)
342992685Sdarrenr					continue;
343053642Sguido			return nat;
343192685Sdarrenr		}
343253642Sguido	}
3433145522Sdarrenr
3434145522Sdarrenr	/*
3435145522Sdarrenr	 * So if we didn't find it but there are wildcard members in the hash
3436145522Sdarrenr	 * table, go back and look for them.  We do this search and update here
3437145522Sdarrenr	 * because it is modifying the NAT table and we want to do this only
3438145522Sdarrenr	 * for the first packet that matches.  The exception, of course, is
3439145522Sdarrenr	 * for "dummy" (FI_IGNORE) lookups.
3440145522Sdarrenr	 */
3441145522Sdarrenrfind_out_wild_ports:
3442145522Sdarrenr	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
344367614Sdarrenr		return NULL;
3444145522Sdarrenr	if (nat_stats.ns_wilds == 0)
3445145522Sdarrenr		return NULL;
344692685Sdarrenr
3447145522Sdarrenr	RWLOCK_EXIT(&ipf_nat);
3448145522Sdarrenr
3449145522Sdarrenr	hv = NAT_HASH_FN(srcip, 0, 0xffffffff);
3450145522Sdarrenr	hv = NAT_HASH_FN(dst.s_addr, hv, ipf_nattable_sz);
3451145522Sdarrenr
3452145522Sdarrenr	WRITE_ENTER(&ipf_nat);
3453145522Sdarrenr
345467614Sdarrenr	nat = nat_table[0][hv];
345567614Sdarrenr	for (; nat; nat = nat->nat_hnext[0]) {
3456161356Sguido		if (nat->nat_ifps[1] != NULL) {
3457161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
3458161356Sguido				continue;
3459161356Sguido		} else if (ifp != NULL)
3460161356Sguido			nat->nat_ifps[1] = ifp;
3461145522Sdarrenr
3462145522Sdarrenr		if (nat->nat_p != fin->fin_p)
346367614Sdarrenr			continue;
346467614Sdarrenr		if ((nat->nat_inip.s_addr != srcip) ||
346567614Sdarrenr		    (nat->nat_oip.s_addr != dst.s_addr))
346667614Sdarrenr			continue;
3467145522Sdarrenr
3468145522Sdarrenr		nflags = nat->nat_flags;
3469145522Sdarrenr		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
3470145522Sdarrenr			continue;
3471145522Sdarrenr
3472145522Sdarrenr		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
3473145522Sdarrenr			       NAT_OUTBOUND) == 1) {
3474145522Sdarrenr			if ((fin->fin_flx & FI_IGNORE) != 0)
3475145522Sdarrenr				break;
3476145522Sdarrenr			if ((nflags & SI_CLONE) != 0) {
3477145522Sdarrenr				nat = fr_natclone(fin, nat);
3478145522Sdarrenr				if (nat == NULL)
3479145522Sdarrenr					break;
3480145522Sdarrenr			} else {
3481145522Sdarrenr				MUTEX_ENTER(&ipf_nat_new);
3482145522Sdarrenr				nat_stats.ns_wilds--;
3483145522Sdarrenr				MUTEX_EXIT(&ipf_nat_new);
3484145522Sdarrenr			}
3485145522Sdarrenr			nat->nat_inport = sport;
3486145522Sdarrenr			nat->nat_oport = dport;
3487145522Sdarrenr			if (nat->nat_outport == 0)
3488145522Sdarrenr				nat->nat_outport = sport;
3489145522Sdarrenr			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
3490145522Sdarrenr			nat_tabmove(nat);
349167614Sdarrenr			break;
349267614Sdarrenr		}
349367614Sdarrenr	}
3494145522Sdarrenr
3495145522Sdarrenr	MUTEX_DOWNGRADE(&ipf_nat);
3496145522Sdarrenr
349767614Sdarrenr	return nat;
349853642Sguido}
349953642Sguido
350053642Sguido
3501145522Sdarrenr/* ------------------------------------------------------------------------ */
3502145522Sdarrenr/* Function:    nat_lookupredir                                             */
3503145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
3504145522Sdarrenr/*                       else pointer to matching NAT entry                 */
3505145522Sdarrenr/* Parameters:  np(I) - pointer to description of packet to find NAT table  */
3506145522Sdarrenr/*                      entry for.                                          */
3507145522Sdarrenr/*                                                                          */
3508145522Sdarrenr/* Lookup the NAT tables to search for a matching redirect                  */
3509161356Sguido/* The contents of natlookup_t should imitate those found in a packet that  */
3510161356Sguido/* would be translated - ie a packet coming in for RDR or going out for MAP.*/
3511161356Sguido/* We can do the lookup in one of two ways, imitating an inbound or         */
3512161356Sguido/* outbound  packet.  By default we assume outbound, unless IPN_IN is set.  */
3513161356Sguido/* For IN, the fields are set as follows:                                   */
3514161356Sguido/*     nl_real* = source information                                        */
3515161356Sguido/*     nl_out* = destination information (translated)                       */
3516161356Sguido/* For an out packet, the fields are set like this:                         */
3517161356Sguido/*     nl_in* = source information (untranslated)                           */
3518161356Sguido/*     nl_out* = destination information (translated)                       */
3519145522Sdarrenr/* ------------------------------------------------------------------------ */
352053642Sguidonat_t *nat_lookupredir(np)
3521145522Sdarrenrnatlookup_t *np;
352253642Sguido{
3523145522Sdarrenr	fr_info_t fi;
352453642Sguido	nat_t *nat;
352553642Sguido
352692685Sdarrenr	bzero((char *)&fi, sizeof(fi));
3527145522Sdarrenr	if (np->nl_flags & IPN_IN) {
3528145522Sdarrenr		fi.fin_data[0] = ntohs(np->nl_realport);
3529145522Sdarrenr		fi.fin_data[1] = ntohs(np->nl_outport);
3530145522Sdarrenr	} else {
3531145522Sdarrenr		fi.fin_data[0] = ntohs(np->nl_inport);
3532145522Sdarrenr		fi.fin_data[1] = ntohs(np->nl_outport);
3533145522Sdarrenr	}
3534145522Sdarrenr	if (np->nl_flags & IPN_TCP)
3535145522Sdarrenr		fi.fin_p = IPPROTO_TCP;
3536145522Sdarrenr	else if (np->nl_flags & IPN_UDP)
3537145522Sdarrenr		fi.fin_p = IPPROTO_UDP;
3538145522Sdarrenr	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
3539145522Sdarrenr		fi.fin_p = IPPROTO_ICMP;
354092685Sdarrenr
354153642Sguido	/*
3542145522Sdarrenr	 * We can do two sorts of lookups:
3543145522Sdarrenr	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
3544145522Sdarrenr	 * - default: we have the `in' and `out' address, look for `real'.
354553642Sguido	 */
3546145522Sdarrenr	if (np->nl_flags & IPN_IN) {
3547145522Sdarrenr		if ((nat = nat_inlookup(&fi, np->nl_flags, fi.fin_p,
3548145522Sdarrenr					np->nl_realip, np->nl_outip))) {
3549145522Sdarrenr			np->nl_inip = nat->nat_inip;
3550145522Sdarrenr			np->nl_inport = nat->nat_inport;
3551145522Sdarrenr		}
3552145522Sdarrenr	} else {
3553145522Sdarrenr		/*
3554145522Sdarrenr		 * If nl_inip is non null, this is a lookup based on the real
3555145522Sdarrenr		 * ip address. Else, we use the fake.
3556145522Sdarrenr		 */
3557145522Sdarrenr		if ((nat = nat_outlookup(&fi, np->nl_flags, fi.fin_p,
3558145522Sdarrenr					 np->nl_inip, np->nl_outip))) {
3559145522Sdarrenr
3560145522Sdarrenr			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
3561145522Sdarrenr				fr_info_t fin;
3562145522Sdarrenr				bzero((char *)&fin, sizeof(fin));
3563145522Sdarrenr				fin.fin_p = nat->nat_p;
3564145522Sdarrenr				fin.fin_data[0] = ntohs(nat->nat_outport);
3565145522Sdarrenr				fin.fin_data[1] = ntohs(nat->nat_oport);
3566145522Sdarrenr				if (nat_inlookup(&fin, np->nl_flags, fin.fin_p,
3567145522Sdarrenr						 nat->nat_outip,
3568145522Sdarrenr						 nat->nat_oip) != NULL) {
3569145522Sdarrenr					np->nl_flags &= ~IPN_FINDFORWARD;
3570145522Sdarrenr				}
3571145522Sdarrenr			}
3572145522Sdarrenr
3573145522Sdarrenr			np->nl_realip = nat->nat_outip;
3574145522Sdarrenr			np->nl_realport = nat->nat_outport;
3575145522Sdarrenr		}
3576145522Sdarrenr 	}
3577145522Sdarrenr
357853642Sguido	return nat;
357953642Sguido}
358053642Sguido
358153642Sguido
3582145522Sdarrenr/* ------------------------------------------------------------------------ */
3583145522Sdarrenr/* Function:    nat_match                                                   */
3584145522Sdarrenr/* Returns:     int - 0 == no match, 1 == match                             */
3585145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
3586145522Sdarrenr/*              np(I)    - pointer to NAT rule                              */
3587145522Sdarrenr/*                                                                          */
3588145522Sdarrenr/* Pull the matching of a packet against a NAT rule out of that complex     */
3589145522Sdarrenr/* loop inside fr_checknatin() and lay it out properly in its own function. */
3590145522Sdarrenr/* ------------------------------------------------------------------------ */
3591145522Sdarrenrstatic int nat_match(fin, np)
359260852Sdarrenrfr_info_t *fin;
359360852Sdarrenripnat_t *np;
359460852Sdarrenr{
359560852Sdarrenr	frtuc_t *ft;
359660852Sdarrenr
3597145522Sdarrenr	if (fin->fin_v != 4)
359860852Sdarrenr		return 0;
359960852Sdarrenr
360092685Sdarrenr	if (np->in_p && fin->fin_p != np->in_p)
360160852Sdarrenr		return 0;
3602145522Sdarrenr
360360852Sdarrenr	if (fin->fin_out) {
360463523Sdarrenr		if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK)))
360560852Sdarrenr			return 0;
360663523Sdarrenr		if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip)
360763523Sdarrenr		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
360860852Sdarrenr			return 0;
360963523Sdarrenr		if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip)
361063523Sdarrenr		    ^ ((np->in_flags & IPN_NOTDST) != 0))
361160852Sdarrenr			return 0;
361260852Sdarrenr	} else {
361363523Sdarrenr		if (!(np->in_redir & NAT_REDIRECT))
361460852Sdarrenr			return 0;
361563523Sdarrenr		if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip)
361663523Sdarrenr		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
361763523Sdarrenr			return 0;
361863523Sdarrenr		if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip)
361963523Sdarrenr		    ^ ((np->in_flags & IPN_NOTDST) != 0))
362063523Sdarrenr			return 0;
362160852Sdarrenr	}
362260852Sdarrenr
362360852Sdarrenr	ft = &np->in_tuc;
3624145522Sdarrenr	if (!(fin->fin_flx & FI_TCPUDP) ||
3625145522Sdarrenr	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
362660852Sdarrenr		if (ft->ftu_scmp || ft->ftu_dcmp)
362760852Sdarrenr			return 0;
362860852Sdarrenr		return 1;
362960852Sdarrenr	}
363060852Sdarrenr
3631145522Sdarrenr	return fr_tcpudpchk(fin, ft);
363260852Sdarrenr}
363360852Sdarrenr
363460852Sdarrenr
3635145522Sdarrenr/* ------------------------------------------------------------------------ */
3636145522Sdarrenr/* Function:    nat_update                                                  */
3637145522Sdarrenr/* Returns:     Nil                                                         */
3638145522Sdarrenr/* Parameters:  nat(I)    - pointer to NAT structure                        */
3639145522Sdarrenr/*              np(I)     - pointer to NAT rule                             */
3640145522Sdarrenr/*                                                                          */
3641145522Sdarrenr/* Updates the lifetime of a NAT table entry for non-TCP packets.  Must be  */
3642145522Sdarrenr/* called with fin_rev updated - i.e. after calling nat_proto().            */
3643145522Sdarrenr/* ------------------------------------------------------------------------ */
3644145522Sdarrenrvoid nat_update(fin, nat, np)
364553642Sguidofr_info_t *fin;
3646145522Sdarrenrnat_t *nat;
3647145522Sdarrenripnat_t *np;
364853642Sguido{
3649145522Sdarrenr	ipftq_t *ifq, *ifq2;
3650145522Sdarrenr	ipftqent_t *tqe;
3651145522Sdarrenr
3652145522Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
3653145522Sdarrenr	tqe = &nat->nat_tqe;
3654145522Sdarrenr	ifq = tqe->tqe_ifq;
3655145522Sdarrenr
3656145522Sdarrenr	/*
3657145522Sdarrenr	 * We allow over-riding of NAT timeouts from NAT rules, even for
3658145522Sdarrenr	 * TCP, however, if it is TCP and there is no rule timeout set,
3659145522Sdarrenr	 * then do not update the timeout here.
3660145522Sdarrenr	 */
3661145522Sdarrenr	if (np != NULL)
3662145522Sdarrenr		ifq2 = np->in_tqehead[fin->fin_rev];
3663145522Sdarrenr	else
3664145522Sdarrenr		ifq2 = NULL;
3665145522Sdarrenr
3666145522Sdarrenr	if (nat->nat_p == IPPROTO_TCP && ifq2 == NULL) {
3667172776Sdarrenr		u_32_t end, ack;
3668172776Sdarrenr		u_char tcpflags;
3669172776Sdarrenr		tcphdr_t *tcp;
3670172776Sdarrenr		int dsize;
3671172776Sdarrenr
3672172776Sdarrenr		tcp = fin->fin_dp;
3673172776Sdarrenr		tcpflags = tcp->th_flags;
3674172776Sdarrenr		dsize = fin->fin_dlen - (TCP_OFF(tcp) << 2) +
3675172776Sdarrenr			((tcpflags & TH_SYN) ? 1 : 0) +
3676172776Sdarrenr			((tcpflags & TH_FIN) ? 1 : 0);
3677172776Sdarrenr
3678172776Sdarrenr		ack = ntohl(tcp->th_ack);
3679172776Sdarrenr		end = ntohl(tcp->th_seq) + dsize;
3680172776Sdarrenr
3681172776Sdarrenr		if (SEQ_GT(ack, nat->nat_seqnext[1 - fin->fin_rev]))
3682172776Sdarrenr			nat->nat_seqnext[1 - fin->fin_rev] = ack;
3683172776Sdarrenr
3684172776Sdarrenr		if (nat->nat_seqnext[fin->fin_rev] == 0)
3685172776Sdarrenr			nat->nat_seqnext[fin->fin_rev] = end;
3686172776Sdarrenr
3687145522Sdarrenr		(void) fr_tcp_age(&nat->nat_tqe, fin, nat_tqb, 0);
3688145522Sdarrenr	} else {
3689145522Sdarrenr		if (ifq2 == NULL) {
3690145522Sdarrenr			if (nat->nat_p == IPPROTO_UDP)
3691145522Sdarrenr				ifq2 = &nat_udptq;
3692145522Sdarrenr			else if (nat->nat_p == IPPROTO_ICMP)
3693145522Sdarrenr				ifq2 = &nat_icmptq;
3694145522Sdarrenr			else
3695145522Sdarrenr				ifq2 = &nat_iptq;
3696145522Sdarrenr		}
3697145522Sdarrenr
3698145522Sdarrenr		fr_movequeue(tqe, ifq, ifq2);
3699145522Sdarrenr	}
3700145522Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
3701145522Sdarrenr}
3702145522Sdarrenr
3703145522Sdarrenr
3704145522Sdarrenr/* ------------------------------------------------------------------------ */
3705145522Sdarrenr/* Function:    fr_checknatout                                              */
3706145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
3707145522Sdarrenr/*                     0 == no packet translation occurred,                 */
3708145522Sdarrenr/*                     1 == packet was successfully translated.             */
3709145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
3710145522Sdarrenr/*              passp(I) - pointer to filtering result flags                */
3711145522Sdarrenr/*                                                                          */
3712145522Sdarrenr/* Check to see if an outcoming packet should be changed.  ICMP packets are */
3713145522Sdarrenr/* first checked to see if they match an existing entry (if an error),      */
3714145522Sdarrenr/* otherwise a search of the current NAT table is made.  If neither results */
3715145522Sdarrenr/* in a match then a search for a matching NAT rule is made.  Create a new  */
3716145522Sdarrenr/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
3717145522Sdarrenr/* packet header(s) as required.                                            */
3718145522Sdarrenr/* ------------------------------------------------------------------------ */
3719145522Sdarrenrint fr_checknatout(fin, passp)
3720145522Sdarrenrfr_info_t *fin;
3721145522Sdarrenru_32_t *passp;
3722145522Sdarrenr{
3723145522Sdarrenr	struct ifnet *ifp, *sifp;
3724145522Sdarrenr	icmphdr_t *icmp = NULL;
372553642Sguido	tcphdr_t *tcp = NULL;
3726145522Sdarrenr	int rval, natfailed;
3727145522Sdarrenr	ipnat_t *np = NULL;
3728145522Sdarrenr	u_int nflags = 0;
3729145522Sdarrenr	u_32_t ipa, iph;
3730145522Sdarrenr	int natadd = 1;
373153642Sguido	frentry_t *fr;
373253642Sguido	nat_t *nat;
373353642Sguido
3734145522Sdarrenr	if (nat_stats.ns_rules == 0 || fr_nat_lock != 0)
373553642Sguido		return 0;
373653642Sguido
3737145522Sdarrenr	natfailed = 0;
3738145522Sdarrenr	fr = fin->fin_fr;
3739145522Sdarrenr	sifp = fin->fin_ifp;
3740170268Sdarrenr	if (fr != NULL) {
3741170268Sdarrenr		ifp = fr->fr_tifs[fin->fin_rev].fd_ifp;
3742170268Sdarrenr		if ((ifp != NULL) && (ifp != (void *)-1))
3743170268Sdarrenr			fin->fin_ifp = ifp;
3744170268Sdarrenr	}
374592685Sdarrenr	ifp = fin->fin_ifp;
374653642Sguido
3747145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
3748145522Sdarrenr		switch (fin->fin_p)
3749145522Sdarrenr		{
3750145522Sdarrenr		case IPPROTO_TCP :
375153642Sguido			nflags = IPN_TCP;
3752145522Sdarrenr			break;
3753145522Sdarrenr		case IPPROTO_UDP :
375453642Sguido			nflags = IPN_UDP;
3755145522Sdarrenr			break;
3756145522Sdarrenr		case IPPROTO_ICMP :
3757145522Sdarrenr			icmp = fin->fin_dp;
3758145522Sdarrenr
3759145522Sdarrenr			/*
3760145522Sdarrenr			 * This is an incoming packet, so the destination is
3761145522Sdarrenr			 * the icmp_id and the source port equals 0
3762145522Sdarrenr			 */
3763145522Sdarrenr			if (nat_icmpquerytype4(icmp->icmp_type))
3764145522Sdarrenr				nflags = IPN_ICMPQUERY;
3765145522Sdarrenr			break;
3766145522Sdarrenr		default :
3767145522Sdarrenr			break;
376853642Sguido		}
3769145522Sdarrenr
3770145522Sdarrenr		if ((nflags & IPN_TCPUDP))
3771145522Sdarrenr			tcp = fin->fin_dp;
377253642Sguido	}
377353642Sguido
377492685Sdarrenr	ipa = fin->fin_saddr;
377553642Sguido
377653642Sguido	READ_ENTER(&ipf_nat);
377760852Sdarrenr
3778145522Sdarrenr	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
3779145522Sdarrenr	    (nat = nat_icmperror(fin, &nflags, NAT_OUTBOUND)))
3780145522Sdarrenr		/*EMPTY*/;
3781145522Sdarrenr	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
378253642Sguido		natadd = 0;
3783145522Sdarrenr	else if ((nat = nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p,
3784145522Sdarrenr				      fin->fin_src, fin->fin_dst))) {
378553642Sguido		nflags = nat->nat_flags;
378653642Sguido	} else {
3787145522Sdarrenr		u_32_t hv, msk, nmsk;
378892685Sdarrenr
378953642Sguido		/*
379053642Sguido		 * If there is no current entry in the nat table for this IP#,
379153642Sguido		 * create one for it (if there is a matching rule).
379253642Sguido		 */
3793145522Sdarrenr		RWLOCK_EXIT(&ipf_nat);
3794145522Sdarrenr		msk = 0xffffffff;
3795145522Sdarrenr		nmsk = nat_masks;
3796145522Sdarrenr		WRITE_ENTER(&ipf_nat);
379753642Sguidomaskloop:
379853642Sguido		iph = ipa & htonl(msk);
379960852Sdarrenr		hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz);
380053642Sguido		for (np = nat_rules[hv]; np; np = np->in_mnext)
380153642Sguido		{
3802161356Sguido			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
380360852Sdarrenr				continue;
3804145522Sdarrenr			if (np->in_v != fin->fin_v)
380560852Sdarrenr				continue;
3806145522Sdarrenr			if (np->in_p && (np->in_p != fin->fin_p))
3807145522Sdarrenr				continue;
3808145522Sdarrenr			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
3809145522Sdarrenr				continue;
381060852Sdarrenr			if (np->in_flags & IPN_FILTER) {
3811145522Sdarrenr				if (!nat_match(fin, np))
381260852Sdarrenr					continue;
381360852Sdarrenr			} else if ((ipa & np->in_inmsk) != np->in_inip)
381460852Sdarrenr				continue;
3815145522Sdarrenr
3816145522Sdarrenr			if ((fr != NULL) &&
3817145522Sdarrenr			    !fr_matchtag(&np->in_tag, &fr->fr_nattag))
381892685Sdarrenr				continue;
3819145522Sdarrenr
3820145522Sdarrenr			if (*np->in_plabel != '\0') {
3821145522Sdarrenr				if (((np->in_flags & IPN_FILTER) == 0) &&
3822145522Sdarrenr				    (np->in_dport != tcp->th_dport))
3823145522Sdarrenr					continue;
3824145522Sdarrenr				if (appr_ok(fin, tcp, np) == 0)
3825145522Sdarrenr					continue;
3826145522Sdarrenr			}
3827145522Sdarrenr
3828145522Sdarrenr			if ((nat = nat_new(fin, np, NULL, nflags,
3829145522Sdarrenr					   NAT_OUTBOUND))) {
383092685Sdarrenr				np->in_hits++;
383192685Sdarrenr				break;
3832145522Sdarrenr			} else
3833145522Sdarrenr				natfailed = -1;
383453642Sguido		}
3835145522Sdarrenr		if ((np == NULL) && (nmsk != 0)) {
3836145522Sdarrenr			while (nmsk) {
383753642Sguido				msk <<= 1;
3838145522Sdarrenr				if (nmsk & 0x80000000)
3839145522Sdarrenr					break;
3840145522Sdarrenr				nmsk <<= 1;
3841145522Sdarrenr			}
3842145522Sdarrenr			if (nmsk != 0) {
3843145522Sdarrenr				nmsk <<= 1;
384453642Sguido				goto maskloop;
3845145522Sdarrenr			}
384653642Sguido		}
384753642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
384853642Sguido	}
384953642Sguido
3850145522Sdarrenr	if (nat != NULL) {
3851145522Sdarrenr		rval = fr_natout(fin, nat, natadd, nflags);
3852145522Sdarrenr		if (rval == 1) {
3853145522Sdarrenr			MUTEX_ENTER(&nat->nat_lock);
3854145522Sdarrenr			nat->nat_ref++;
3855145522Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
3856170268Sdarrenr			nat->nat_touched = fr_ticks;
3857145522Sdarrenr			fin->fin_nat = nat;
3858145522Sdarrenr		}
3859145522Sdarrenr	} else
3860145522Sdarrenr		rval = natfailed;
3861145522Sdarrenr	RWLOCK_EXIT(&ipf_nat);
3862145522Sdarrenr
3863145522Sdarrenr	if (rval == -1) {
3864145522Sdarrenr		if (passp != NULL)
3865145522Sdarrenr			*passp = FR_BLOCK;
3866145522Sdarrenr		fin->fin_flx |= FI_BADNAT;
3867145522Sdarrenr	}
3868145522Sdarrenr	fin->fin_ifp = sifp;
3869145522Sdarrenr	return rval;
3870145522Sdarrenr}
3871145522Sdarrenr
3872145522Sdarrenr/* ------------------------------------------------------------------------ */
3873145522Sdarrenr/* Function:    fr_natout                                                   */
3874145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
3875145522Sdarrenr/*                     1 == packet was successfully translated.             */
3876145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
3877145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
3878145522Sdarrenr/*              natadd(I) - flag indicating if it is safe to add frag cache */
3879145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
3880145522Sdarrenr/*                                                                          */
3881145522Sdarrenr/* Translate a packet coming "out" on an interface.                         */
3882145522Sdarrenr/* ------------------------------------------------------------------------ */
3883145522Sdarrenrint fr_natout(fin, nat, natadd, nflags)
3884145522Sdarrenrfr_info_t *fin;
3885145522Sdarrenrnat_t *nat;
3886145522Sdarrenrint natadd;
3887145522Sdarrenru_32_t nflags;
3888145522Sdarrenr{
3889145522Sdarrenr	icmphdr_t *icmp;
3890145522Sdarrenr	u_short *csump;
3891145522Sdarrenr	tcphdr_t *tcp;
3892145522Sdarrenr	ipnat_t *np;
3893145522Sdarrenr	int i;
3894145522Sdarrenr
3895145522Sdarrenr	tcp = NULL;
3896145522Sdarrenr	icmp = NULL;
3897145522Sdarrenr	csump = NULL;
3898145522Sdarrenr	np = nat->nat_ptr;
3899145522Sdarrenr
3900145522Sdarrenr	if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL))
3901145522Sdarrenr		(void) fr_nat_newfrag(fin, 0, nat);
3902145522Sdarrenr
3903145522Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
3904145522Sdarrenr	nat->nat_bytes[1] += fin->fin_plen;
3905145522Sdarrenr	nat->nat_pkts[1]++;
3906145522Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
3907145522Sdarrenr
390872006Sdarrenr	/*
3909145522Sdarrenr	 * Fix up checksums, not by recalculating them, but
3910145522Sdarrenr	 * simply computing adjustments.
3911145522Sdarrenr	 * This is only done for STREAMS based IP implementations where the
3912145522Sdarrenr	 * checksum has already been calculated by IP.  In all other cases,
3913145522Sdarrenr	 * IPFilter is called before the checksum needs calculating so there
3914145522Sdarrenr	 * is no call to modify whatever is in the header now.
391572006Sdarrenr	 */
3916145522Sdarrenr	if (fin->fin_v == 4) {
391763523Sdarrenr		if (nflags == IPN_ICMPERR) {
391863523Sdarrenr			u_32_t s1, s2, sumd;
391963523Sdarrenr
392092685Sdarrenr			s1 = LONG_SUM(ntohl(fin->fin_saddr));
392163523Sdarrenr			s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr));
392263523Sdarrenr			CALC_SUMD(s1, s2, sumd);
3923145522Sdarrenr			fix_outcksum(fin, &fin->fin_ip->ip_sum, sumd);
392463523Sdarrenr		}
3925153876Sguido#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
3926153876Sguido    defined(linux) || defined(BRIDGE_IPF)
392763523Sdarrenr		else {
3928153876Sguido			/*
3929153876Sguido			 * Strictly speaking, this isn't necessary on BSD
3930153876Sguido			 * kernels because they do checksum calculation after
3931153876Sguido			 * this code has run BUT if ipfilter is being used
3932153876Sguido			 * to do NAT as a bridge, that code doesn't exist.
3933153876Sguido			 */
393463523Sdarrenr			if (nat->nat_dir == NAT_OUTBOUND)
3935145522Sdarrenr				fix_outcksum(fin, &fin->fin_ip->ip_sum,
3936145522Sdarrenr					     nat->nat_ipsumd);
393763523Sdarrenr			else
3938145522Sdarrenr				fix_incksum(fin, &fin->fin_ip->ip_sum,
3939145522Sdarrenr					    nat->nat_ipsumd);
394063523Sdarrenr		}
394153642Sguido#endif
3942145522Sdarrenr	}
394353642Sguido
3944145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
3945145522Sdarrenr		if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) {
3946145522Sdarrenr			tcp = fin->fin_dp;
394753642Sguido
3948145522Sdarrenr			tcp->th_sport = nat->nat_outport;
3949145522Sdarrenr			fin->fin_data[0] = ntohs(nat->nat_outport);
3950145522Sdarrenr		}
395153642Sguido
3952145522Sdarrenr		if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) {
3953145522Sdarrenr			icmp = fin->fin_dp;
3954145522Sdarrenr			icmp->icmp_id = nat->nat_outport;
3955145522Sdarrenr		}
3956110916Sdarrenr
3957145522Sdarrenr		csump = nat_proto(fin, nat, nflags);
3958145522Sdarrenr	}
3959110916Sdarrenr
3960145522Sdarrenr	fin->fin_ip->ip_src = nat->nat_outip;
396153642Sguido
3962145522Sdarrenr	nat_update(fin, nat, np);
396363523Sdarrenr
3964145522Sdarrenr	/*
3965145522Sdarrenr	 * The above comments do not hold for layer 4 (or higher) checksums...
3966145522Sdarrenr	 */
3967145522Sdarrenr	if (csump != NULL) {
3968145522Sdarrenr		if (nat->nat_dir == NAT_OUTBOUND)
3969145522Sdarrenr			fix_outcksum(fin, csump, nat->nat_sumd[1]);
3970145522Sdarrenr		else
3971145522Sdarrenr			fix_incksum(fin, csump, nat->nat_sumd[1]);
3972145522Sdarrenr	}
3973145522Sdarrenr#ifdef	IPFILTER_SYNC
3974145522Sdarrenr	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
3975145522Sdarrenr#endif
3976145522Sdarrenr	/* ------------------------------------------------------------- */
3977145522Sdarrenr	/* A few quick notes:						 */
3978145522Sdarrenr	/*	Following are test conditions prior to calling the 	 */
3979145522Sdarrenr	/*	appr_check routine.					 */
3980145522Sdarrenr	/*								 */
3981145522Sdarrenr	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
3982145522Sdarrenr	/*	with a redirect rule, we attempt to match the packet's	 */
3983145522Sdarrenr	/*	source port against in_dport, otherwise	we'd compare the */
3984145522Sdarrenr	/*	packet's destination.			 		 */
3985145522Sdarrenr	/* ------------------------------------------------------------- */
3986145522Sdarrenr	if ((np != NULL) && (np->in_apr != NULL)) {
3987145522Sdarrenr		i = appr_check(fin, nat);
3988145522Sdarrenr		if (i == 0)
398960852Sdarrenr			i = 1;
3990145522Sdarrenr	} else
3991145522Sdarrenr		i = 1;
3992145522Sdarrenr	ATOMIC_INCL(nat_stats.ns_mapped[1]);
3993145522Sdarrenr	fin->fin_flx |= FI_NATED;
3994145522Sdarrenr	return i;
399553642Sguido}
399653642Sguido
399753642Sguido
3998145522Sdarrenr/* ------------------------------------------------------------------------ */
3999145522Sdarrenr/* Function:    fr_checknatin                                               */
4000145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
4001145522Sdarrenr/*                     0 == no packet translation occurred,                 */
4002145522Sdarrenr/*                     1 == packet was successfully translated.             */
4003145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4004145522Sdarrenr/*              passp(I) - pointer to filtering result flags                */
4005145522Sdarrenr/*                                                                          */
4006145522Sdarrenr/* Check to see if an incoming packet should be changed.  ICMP packets are  */
4007145522Sdarrenr/* first checked to see if they match an existing entry (if an error),      */
4008145522Sdarrenr/* otherwise a search of the current NAT table is made.  If neither results */
4009145522Sdarrenr/* in a match then a search for a matching NAT rule is made.  Create a new  */
4010145522Sdarrenr/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
4011145522Sdarrenr/* packet header(s) as required.                                            */
4012145522Sdarrenr/* ------------------------------------------------------------------------ */
4013145522Sdarrenrint fr_checknatin(fin, passp)
401453642Sguidofr_info_t *fin;
4015145522Sdarrenru_32_t *passp;
401653642Sguido{
4017145522Sdarrenr	u_int nflags, natadd;
4018145522Sdarrenr	int rval, natfailed;
4019145522Sdarrenr	struct ifnet *ifp;
4020145522Sdarrenr	struct in_addr in;
4021145522Sdarrenr	icmphdr_t *icmp;
4022145522Sdarrenr	tcphdr_t *tcp;
4023145522Sdarrenr	u_short dport;
4024145522Sdarrenr	ipnat_t *np;
402553642Sguido	nat_t *nat;
402653642Sguido	u_32_t iph;
402753642Sguido
4028145522Sdarrenr	if (nat_stats.ns_rules == 0 || fr_nat_lock != 0)
402953642Sguido		return 0;
403053642Sguido
4031145522Sdarrenr	tcp = NULL;
4032145522Sdarrenr	icmp = NULL;
4033145522Sdarrenr	dport = 0;
4034145522Sdarrenr	natadd = 1;
4035145522Sdarrenr	nflags = 0;
4036145522Sdarrenr	natfailed = 0;
4037145522Sdarrenr	ifp = fin->fin_ifp;
4038145522Sdarrenr
4039145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
4040145522Sdarrenr		switch (fin->fin_p)
4041145522Sdarrenr		{
4042145522Sdarrenr		case IPPROTO_TCP :
404353642Sguido			nflags = IPN_TCP;
4044145522Sdarrenr			break;
4045145522Sdarrenr		case IPPROTO_UDP :
404653642Sguido			nflags = IPN_UDP;
4047145522Sdarrenr			break;
4048145522Sdarrenr		case IPPROTO_ICMP :
4049145522Sdarrenr			icmp = fin->fin_dp;
4050145522Sdarrenr
4051145522Sdarrenr			/*
4052145522Sdarrenr			 * This is an incoming packet, so the destination is
4053145522Sdarrenr			 * the icmp_id and the source port equals 0
4054145522Sdarrenr			 */
4055145522Sdarrenr			if (nat_icmpquerytype4(icmp->icmp_type)) {
4056145522Sdarrenr				nflags = IPN_ICMPQUERY;
4057145522Sdarrenr				dport = icmp->icmp_id;
4058145522Sdarrenr			} break;
4059145522Sdarrenr		default :
4060145522Sdarrenr			break;
4061145522Sdarrenr		}
4062145522Sdarrenr
406353642Sguido		if ((nflags & IPN_TCPUDP)) {
4064145522Sdarrenr			tcp = fin->fin_dp;
406553642Sguido			dport = tcp->th_dport;
406653642Sguido		}
406753642Sguido	}
406853642Sguido
406992685Sdarrenr	in = fin->fin_dst;
407053642Sguido
407153642Sguido	READ_ENTER(&ipf_nat);
407253642Sguido
4073145522Sdarrenr	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
4074145522Sdarrenr	    (nat = nat_icmperror(fin, &nflags, NAT_INBOUND)))
4075145522Sdarrenr		/*EMPTY*/;
4076145522Sdarrenr	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
407753642Sguido		natadd = 0;
4078145522Sdarrenr	else if ((nat = nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p,
4079145522Sdarrenr				     fin->fin_src, in))) {
408053642Sguido		nflags = nat->nat_flags;
408153642Sguido	} else {
4082145522Sdarrenr		u_32_t hv, msk, rmsk;
4083145522Sdarrenr
408453642Sguido		RWLOCK_EXIT(&ipf_nat);
4085145522Sdarrenr		rmsk = rdr_masks;
408692685Sdarrenr		msk = 0xffffffff;
408753642Sguido		WRITE_ENTER(&ipf_nat);
408853642Sguido		/*
408953642Sguido		 * If there is no current entry in the nat table for this IP#,
409053642Sguido		 * create one for it (if there is a matching rule).
409153642Sguido		 */
409253642Sguidomaskloop:
409353642Sguido		iph = in.s_addr & htonl(msk);
409460852Sdarrenr		hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz);
409560852Sdarrenr		for (np = rdr_rules[hv]; np; np = np->in_rnext) {
4096145522Sdarrenr			if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
409760852Sdarrenr				continue;
4098145522Sdarrenr			if (np->in_v != fin->fin_v)
4099138947Sdarrenr				continue;
4100145522Sdarrenr			if (np->in_p && (np->in_p != fin->fin_p))
4101145522Sdarrenr				continue;
4102145522Sdarrenr			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
4103145522Sdarrenr				continue;
410460852Sdarrenr			if (np->in_flags & IPN_FILTER) {
4105145522Sdarrenr				if (!nat_match(fin, np))
410660852Sdarrenr					continue;
4107145522Sdarrenr			} else {
4108145522Sdarrenr				if ((in.s_addr & np->in_outmsk) != np->in_outip)
4109145522Sdarrenr					continue;
4110145522Sdarrenr				if (np->in_pmin &&
4111145522Sdarrenr				    ((ntohs(np->in_pmax) < ntohs(dport)) ||
4112145522Sdarrenr				     (ntohs(dport) < ntohs(np->in_pmin))))
4113145522Sdarrenr					continue;
4114145522Sdarrenr			}
4115145522Sdarrenr
4116145522Sdarrenr			if (*np->in_plabel != '\0') {
4117145522Sdarrenr				if (!appr_ok(fin, tcp, np)) {
4118145522Sdarrenr					continue;
411953642Sguido				}
4120145522Sdarrenr			}
4121145522Sdarrenr
4122145522Sdarrenr			nat = nat_new(fin, np, NULL, nflags, NAT_INBOUND);
4123145522Sdarrenr			if (nat != NULL) {
4124145522Sdarrenr				np->in_hits++;
4125145522Sdarrenr				break;
4126145522Sdarrenr			} else
4127145522Sdarrenr				natfailed = -1;
412860852Sdarrenr		}
412960852Sdarrenr
4130145522Sdarrenr		if ((np == NULL) && (rmsk != 0)) {
4131145522Sdarrenr			while (rmsk) {
413253642Sguido				msk <<= 1;
4133145522Sdarrenr				if (rmsk & 0x80000000)
4134145522Sdarrenr					break;
4135145522Sdarrenr				rmsk <<= 1;
4136145522Sdarrenr			}
4137145522Sdarrenr			if (rmsk != 0) {
4138145522Sdarrenr				rmsk <<= 1;
413953642Sguido				goto maskloop;
4140145522Sdarrenr			}
414153642Sguido		}
414253642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
414353642Sguido	}
4144145522Sdarrenr	if (nat != NULL) {
4145145522Sdarrenr		rval = fr_natin(fin, nat, natadd, nflags);
4146145522Sdarrenr		if (rval == 1) {
4147145522Sdarrenr			MUTEX_ENTER(&nat->nat_lock);
4148145522Sdarrenr			nat->nat_ref++;
4149145522Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
4150170268Sdarrenr			nat->nat_touched = fr_ticks;
4151145522Sdarrenr			fin->fin_nat = nat;
4152145522Sdarrenr		}
4153145522Sdarrenr	} else
4154145522Sdarrenr		rval = natfailed;
4155145522Sdarrenr	RWLOCK_EXIT(&ipf_nat);
415672006Sdarrenr
4157145522Sdarrenr	if (rval == -1) {
4158145522Sdarrenr		if (passp != NULL)
4159145522Sdarrenr			*passp = FR_BLOCK;
4160145522Sdarrenr		fin->fin_flx |= FI_BADNAT;
4161145522Sdarrenr	}
4162145522Sdarrenr	return rval;
4163145522Sdarrenr}
4164145522Sdarrenr
4165145522Sdarrenr
4166145522Sdarrenr/* ------------------------------------------------------------------------ */
4167145522Sdarrenr/* Function:    fr_natin                                                    */
4168145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
4169145522Sdarrenr/*                     1 == packet was successfully translated.             */
4170145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
4171145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
4172145522Sdarrenr/*              natadd(I) - flag indicating if it is safe to add frag cache */
4173145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
4174145522Sdarrenr/* Locks Held:  ipf_nat (READ)                                              */
4175145522Sdarrenr/*                                                                          */
4176145522Sdarrenr/* Translate a packet coming "in" on an interface.                          */
4177145522Sdarrenr/* ------------------------------------------------------------------------ */
4178145522Sdarrenrint fr_natin(fin, nat, natadd, nflags)
4179145522Sdarrenrfr_info_t *fin;
4180145522Sdarrenrnat_t *nat;
4181145522Sdarrenrint natadd;
4182145522Sdarrenru_32_t nflags;
4183145522Sdarrenr{
4184145522Sdarrenr	icmphdr_t *icmp;
4185145522Sdarrenr	u_short *csump;
4186145522Sdarrenr	tcphdr_t *tcp;
4187145522Sdarrenr	ipnat_t *np;
4188145522Sdarrenr	int i;
4189145522Sdarrenr
4190145522Sdarrenr	tcp = NULL;
4191145522Sdarrenr	csump = NULL;
4192145522Sdarrenr	np = nat->nat_ptr;
4193145522Sdarrenr	fin->fin_fr = nat->nat_fr;
4194145522Sdarrenr
4195145522Sdarrenr	if (np != NULL) {
4196145522Sdarrenr		if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
4197145522Sdarrenr			(void) fr_nat_newfrag(fin, 0, nat);
4198145522Sdarrenr
4199145522Sdarrenr	/* ------------------------------------------------------------- */
4200145522Sdarrenr	/* A few quick notes:						 */
4201145522Sdarrenr	/*	Following are test conditions prior to calling the 	 */
4202145522Sdarrenr	/*	appr_check routine.					 */
4203145522Sdarrenr	/*								 */
4204145522Sdarrenr	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
4205145522Sdarrenr	/*	with a map rule, we attempt to match the packet's	 */
4206145522Sdarrenr	/*	source port against in_dport, otherwise	we'd compare the */
4207145522Sdarrenr	/*	packet's destination.			 		 */
4208145522Sdarrenr	/* ------------------------------------------------------------- */
4209145522Sdarrenr		if (np->in_apr != NULL) {
4210145522Sdarrenr			i = appr_check(fin, nat);
421160852Sdarrenr			if (i == -1) {
4212145522Sdarrenr				return -1;
421360852Sdarrenr			}
421460852Sdarrenr		}
4215145522Sdarrenr	}
421653642Sguido
4217145522Sdarrenr#ifdef	IPFILTER_SYNC
4218145522Sdarrenr	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
4219145522Sdarrenr#endif
4220145522Sdarrenr
4221145522Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
4222145522Sdarrenr	nat->nat_bytes[0] += fin->fin_plen;
4223145522Sdarrenr	nat->nat_pkts[0]++;
4224145522Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
4225145522Sdarrenr
4226145522Sdarrenr	fin->fin_ip->ip_dst = nat->nat_inip;
4227145522Sdarrenr	fin->fin_fi.fi_daddr = nat->nat_inip.s_addr;
4228145522Sdarrenr	if (nflags & IPN_TCPUDP)
4229145522Sdarrenr		tcp = fin->fin_dp;
4230145522Sdarrenr
4231145522Sdarrenr	/*
4232145522Sdarrenr	 * Fix up checksums, not by recalculating them, but
4233145522Sdarrenr	 * simply computing adjustments.
4234145522Sdarrenr	 * Why only do this for some platforms on inbound packets ?
4235145522Sdarrenr	 * Because for those that it is done, IP processing is yet to happen
4236145522Sdarrenr	 * and so the IPv4 header checksum has not yet been evaluated.
4237145522Sdarrenr	 * Perhaps it should always be done for the benefit of things like
4238145522Sdarrenr	 * fast forwarding (so that it doesn't need to be recomputed) but with
4239145522Sdarrenr	 * header checksum offloading, perhaps it is a moot point.
4240145522Sdarrenr	 */
4241145522Sdarrenr#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
4242145522Sdarrenr     defined(__osf__) || defined(linux)
4243145522Sdarrenr	if (nat->nat_dir == NAT_OUTBOUND)
4244145522Sdarrenr		fix_incksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd);
4245145522Sdarrenr	else
4246145522Sdarrenr		fix_outcksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd);
4247145522Sdarrenr#endif
4248145522Sdarrenr
4249145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
4250145522Sdarrenr		if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) {
4251145522Sdarrenr			tcp->th_dport = nat->nat_inport;
4252145522Sdarrenr			fin->fin_data[1] = ntohs(nat->nat_inport);
425392685Sdarrenr		}
425453642Sguido
4255145522Sdarrenr
4256145522Sdarrenr		if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) {
4257145522Sdarrenr			icmp = fin->fin_dp;
4258145522Sdarrenr
4259145522Sdarrenr			icmp->icmp_id = nat->nat_inport;
4260145522Sdarrenr		}
4261145522Sdarrenr
4262145522Sdarrenr		csump = nat_proto(fin, nat, nflags);
4263145522Sdarrenr	}
4264145522Sdarrenr
4265145522Sdarrenr	nat_update(fin, nat, np);
4266145522Sdarrenr
4267145522Sdarrenr	/*
4268145522Sdarrenr	 * The above comments do not hold for layer 4 (or higher) checksums...
4269145522Sdarrenr	 */
4270145522Sdarrenr	if (csump != NULL) {
427153642Sguido		if (nat->nat_dir == NAT_OUTBOUND)
4272145522Sdarrenr			fix_incksum(fin, csump, nat->nat_sumd[0]);
427353642Sguido		else
4274145522Sdarrenr			fix_outcksum(fin, csump, nat->nat_sumd[0]);
4275145522Sdarrenr	}
4276145522Sdarrenr	ATOMIC_INCL(nat_stats.ns_mapped[0]);
4277145522Sdarrenr	fin->fin_flx |= FI_NATED;
4278145522Sdarrenr	if (np != NULL && np->in_tag.ipt_num[0] != 0)
4279145522Sdarrenr		fin->fin_nattag = &np->in_tag;
4280145522Sdarrenr	return 1;
4281145522Sdarrenr}
4282130886Sdarrenr
4283130886Sdarrenr
4284145522Sdarrenr/* ------------------------------------------------------------------------ */
4285145522Sdarrenr/* Function:    nat_proto                                                   */
4286145522Sdarrenr/* Returns:     u_short* - pointer to transport header checksum to update,  */
4287145522Sdarrenr/*                         NULL if the transport protocol is not recognised */
4288145522Sdarrenr/*                         as needing a checksum update.                    */
4289145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
4290145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
4291145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
4292145522Sdarrenr/*                                                                          */
4293145522Sdarrenr/* Return the pointer to the checksum field for each protocol so understood.*/
4294145522Sdarrenr/* If support for making other changes to a protocol header is required,    */
4295145522Sdarrenr/* that is not strictly 'address' translation, such as clamping the MSS in  */
4296145522Sdarrenr/* TCP down to a specific value, then do it from here.                      */
4297145522Sdarrenr/* ------------------------------------------------------------------------ */
4298145522Sdarrenru_short *nat_proto(fin, nat, nflags)
4299145522Sdarrenrfr_info_t *fin;
4300145522Sdarrenrnat_t *nat;
4301145522Sdarrenru_int nflags;
4302145522Sdarrenr{
4303145522Sdarrenr	icmphdr_t *icmp;
4304145522Sdarrenr	u_short *csump;
4305145522Sdarrenr	tcphdr_t *tcp;
4306145522Sdarrenr	udphdr_t *udp;
430753642Sguido
4308145522Sdarrenr	csump = NULL;
4309145522Sdarrenr	if (fin->fin_out == 0) {
4310145522Sdarrenr		fin->fin_rev = (nat->nat_dir == NAT_OUTBOUND);
4311145522Sdarrenr	} else {
4312145522Sdarrenr		fin->fin_rev = (nat->nat_dir == NAT_INBOUND);
4313145522Sdarrenr	}
431453642Sguido
4315145522Sdarrenr	switch (fin->fin_p)
4316145522Sdarrenr	{
4317145522Sdarrenr	case IPPROTO_TCP :
4318145522Sdarrenr		tcp = fin->fin_dp;
4319110916Sdarrenr
4320145522Sdarrenr		csump = &tcp->th_sum;
432153642Sguido
4322145522Sdarrenr		/*
4323145522Sdarrenr		 * Do a MSS CLAMPING on a SYN packet,
4324145522Sdarrenr		 * only deal IPv4 for now.
4325145522Sdarrenr		 */
4326145522Sdarrenr		if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0)
4327145522Sdarrenr			nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump);
432860852Sdarrenr
4329145522Sdarrenr		break;
4330145522Sdarrenr
4331145522Sdarrenr	case IPPROTO_UDP :
4332145522Sdarrenr		udp = fin->fin_dp;
4333145522Sdarrenr
4334145522Sdarrenr		if (udp->uh_sum)
4335145522Sdarrenr			csump = &udp->uh_sum;
4336145522Sdarrenr		break;
4337145522Sdarrenr
4338145522Sdarrenr	case IPPROTO_ICMP :
4339145522Sdarrenr		icmp = fin->fin_dp;
4340145522Sdarrenr
4341145522Sdarrenr		if ((nflags & IPN_ICMPQUERY) != 0) {
4342145522Sdarrenr			if (icmp->icmp_cksum != 0)
4343145522Sdarrenr				csump = &icmp->icmp_cksum;
434453642Sguido		}
4345145522Sdarrenr		break;
434653642Sguido	}
4347145522Sdarrenr	return csump;
434853642Sguido}
434953642Sguido
435053642Sguido
4351145522Sdarrenr/* ------------------------------------------------------------------------ */
4352145522Sdarrenr/* Function:    fr_natunload                                                */
4353145522Sdarrenr/* Returns:     Nil                                                         */
4354145522Sdarrenr/* Parameters:  Nil                                                         */
4355145522Sdarrenr/*                                                                          */
4356145522Sdarrenr/* Free all memory used by NAT structures allocated at runtime.             */
4357145522Sdarrenr/* ------------------------------------------------------------------------ */
4358145522Sdarrenrvoid fr_natunload()
435953642Sguido{
4360145522Sdarrenr	ipftq_t *ifq, *ifqnext;
4361145522Sdarrenr
436253642Sguido	(void) nat_clearlist();
436353642Sguido	(void) nat_flushtable();
436453642Sguido
4365145522Sdarrenr	/*
4366145522Sdarrenr	 * Proxy timeout queues are not cleaned here because although they
4367145522Sdarrenr	 * exist on the NAT list, appr_unload is called after fr_natunload
4368145522Sdarrenr	 * and the proxies actually are responsible for them being created.
4369145522Sdarrenr	 * Should the proxy timeouts have their own list?  There's no real
4370145522Sdarrenr	 * justification as this is the only complication.
4371145522Sdarrenr	 */
4372145522Sdarrenr	for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) {
4373145522Sdarrenr		ifqnext = ifq->ifq_next;
4374145522Sdarrenr		if (((ifq->ifq_flags & IFQF_PROXY) == 0) &&
4375145522Sdarrenr		    (fr_deletetimeoutqueue(ifq) == 0))
4376145522Sdarrenr			fr_freetimeoutqueue(ifq);
4377145522Sdarrenr	}
4378145522Sdarrenr
437953642Sguido	if (nat_table[0] != NULL) {
438053642Sguido		KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz);
438153642Sguido		nat_table[0] = NULL;
438253642Sguido	}
438353642Sguido	if (nat_table[1] != NULL) {
438453642Sguido		KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz);
438553642Sguido		nat_table[1] = NULL;
438653642Sguido	}
438753642Sguido	if (nat_rules != NULL) {
438853642Sguido		KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz);
438953642Sguido		nat_rules = NULL;
439053642Sguido	}
439153642Sguido	if (rdr_rules != NULL) {
439253642Sguido		KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz);
439353642Sguido		rdr_rules = NULL;
439453642Sguido	}
4395170268Sdarrenr	if (ipf_hm_maptable != NULL) {
4396170268Sdarrenr		KFREES(ipf_hm_maptable, sizeof(hostmap_t *) * ipf_hostmap_sz);
4397170268Sdarrenr		ipf_hm_maptable = NULL;
439860852Sdarrenr	}
4399145522Sdarrenr	if (nat_stats.ns_bucketlen[0] != NULL) {
4400145522Sdarrenr		KFREES(nat_stats.ns_bucketlen[0],
4401145522Sdarrenr		       sizeof(u_long *) * ipf_nattable_sz);
4402145522Sdarrenr		nat_stats.ns_bucketlen[0] = NULL;
4403145522Sdarrenr	}
4404145522Sdarrenr	if (nat_stats.ns_bucketlen[1] != NULL) {
4405145522Sdarrenr		KFREES(nat_stats.ns_bucketlen[1],
4406145522Sdarrenr		       sizeof(u_long *) * ipf_nattable_sz);
4407145522Sdarrenr		nat_stats.ns_bucketlen[1] = NULL;
4408145522Sdarrenr	}
4409145522Sdarrenr
4410145522Sdarrenr	if (fr_nat_maxbucket_reset == 1)
4411145522Sdarrenr		fr_nat_maxbucket = 0;
4412145522Sdarrenr
4413145522Sdarrenr	if (fr_nat_init == 1) {
4414145522Sdarrenr		fr_nat_init = 0;
4415145522Sdarrenr		fr_sttab_destroy(nat_tqb);
4416145522Sdarrenr
4417145522Sdarrenr		RW_DESTROY(&ipf_natfrag);
4418145522Sdarrenr		RW_DESTROY(&ipf_nat);
4419145522Sdarrenr
4420145522Sdarrenr		MUTEX_DESTROY(&ipf_nat_new);
4421145522Sdarrenr		MUTEX_DESTROY(&ipf_natio);
4422145522Sdarrenr
4423145522Sdarrenr		MUTEX_DESTROY(&nat_udptq.ifq_lock);
4424145522Sdarrenr		MUTEX_DESTROY(&nat_icmptq.ifq_lock);
4425145522Sdarrenr		MUTEX_DESTROY(&nat_iptq.ifq_lock);
4426145522Sdarrenr	}
442753642Sguido}
442853642Sguido
442953642Sguido
4430145522Sdarrenr/* ------------------------------------------------------------------------ */
4431145522Sdarrenr/* Function:    fr_natexpire                                                */
4432145522Sdarrenr/* Returns:     Nil                                                         */
4433145522Sdarrenr/* Parameters:  Nil                                                         */
4434145522Sdarrenr/*                                                                          */
4435145522Sdarrenr/* Check all of the timeout queues for entries at the top which need to be  */
4436145522Sdarrenr/* expired.                                                                 */
4437145522Sdarrenr/* ------------------------------------------------------------------------ */
4438145522Sdarrenrvoid fr_natexpire()
443953642Sguido{
4440145522Sdarrenr	ipftq_t *ifq, *ifqnext;
4441145522Sdarrenr	ipftqent_t *tqe, *tqn;
4442161356Sguido	int i;
4443153876Sguido	SPL_INT(s);
444453642Sguido
444553642Sguido	SPL_NET(s);
444653642Sguido	WRITE_ENTER(&ipf_nat);
4447145522Sdarrenr	for (ifq = nat_tqb, i = 0; ifq != NULL; ifq = ifq->ifq_next) {
4448145522Sdarrenr		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
4449145522Sdarrenr			if (tqe->tqe_die > fr_ticks)
4450145522Sdarrenr				break;
4451145522Sdarrenr			tqn = tqe->tqe_next;
4452145522Sdarrenr			nat_delete(tqe->tqe_parent, NL_EXPIRE);
445353642Sguido		}
445453642Sguido	}
4455145522Sdarrenr
4456145522Sdarrenr	for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) {
4457145522Sdarrenr		ifqnext = ifq->ifq_next;
4458145522Sdarrenr
4459145522Sdarrenr		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
4460145522Sdarrenr			if (tqe->tqe_die > fr_ticks)
4461145522Sdarrenr				break;
4462145522Sdarrenr			tqn = tqe->tqe_next;
4463145522Sdarrenr			nat_delete(tqe->tqe_parent, NL_EXPIRE);
4464145522Sdarrenr		}
4465145522Sdarrenr	}
4466145522Sdarrenr
4467145522Sdarrenr	for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) {
4468145522Sdarrenr		ifqnext = ifq->ifq_next;
4469145522Sdarrenr
4470145522Sdarrenr		if (((ifq->ifq_flags & IFQF_DELETE) != 0) &&
4471145522Sdarrenr		    (ifq->ifq_ref == 0)) {
4472145522Sdarrenr			fr_freetimeoutqueue(ifq);
4473145522Sdarrenr		}
4474145522Sdarrenr	}
4475145522Sdarrenr
4476170268Sdarrenr	if (fr_nat_doflush != 0) {
4477170268Sdarrenr		nat_extraflush(2);
4478170268Sdarrenr		fr_nat_doflush = 0;
4479170268Sdarrenr	}
4480170268Sdarrenr
448153642Sguido	RWLOCK_EXIT(&ipf_nat);
448253642Sguido	SPL_X(s);
448353642Sguido}
448453642Sguido
448553642Sguido
4486145522Sdarrenr/* ------------------------------------------------------------------------ */
4487145522Sdarrenr/* Function:    fr_natsync                                                  */
4488145522Sdarrenr/* Returns:     Nil                                                         */
4489145522Sdarrenr/* Parameters:  ifp(I) - pointer to network interface                       */
4490145522Sdarrenr/*                                                                          */
4491145522Sdarrenr/* Walk through all of the currently active NAT sessions, looking for those */
4492145522Sdarrenr/* which need to have their translated address updated.                     */
4493145522Sdarrenr/* ------------------------------------------------------------------------ */
4494145522Sdarrenrvoid fr_natsync(ifp)
449553642Sguidovoid *ifp;
449653642Sguido{
4497145522Sdarrenr	u_32_t sum1, sum2, sumd;
449853642Sguido	struct in_addr in;
4499145522Sdarrenr	ipnat_t *n;
4500145522Sdarrenr	nat_t *nat;
450153642Sguido	void *ifp2;
4502153876Sguido	SPL_INT(s);
450353642Sguido
4504145522Sdarrenr	if (fr_running <= 0)
4505145522Sdarrenr		return;
4506145522Sdarrenr
450753642Sguido	/*
450853642Sguido	 * Change IP addresses for NAT sessions for any protocol except TCP
4509145522Sdarrenr	 * since it will break the TCP connection anyway.  The only rules
4510145522Sdarrenr	 * which will get changed are those which are "map ... -> 0/32",
4511145522Sdarrenr	 * where the rule specifies the address is taken from the interface.
451253642Sguido	 */
451353642Sguido	SPL_NET(s);
451453642Sguido	WRITE_ENTER(&ipf_nat);
4515145522Sdarrenr
4516145522Sdarrenr	if (fr_running <= 0) {
4517145522Sdarrenr		RWLOCK_EXIT(&ipf_nat);
4518145522Sdarrenr		return;
4519145522Sdarrenr	}
4520145522Sdarrenr
4521145522Sdarrenr	for (nat = nat_instances; nat; nat = nat->nat_next) {
4522145522Sdarrenr		if ((nat->nat_flags & IPN_TCP) != 0)
4523145522Sdarrenr			continue;
4524145522Sdarrenr		n = nat->nat_ptr;
4525145522Sdarrenr		if ((n == NULL) ||
4526145522Sdarrenr		    (n->in_outip != 0) || (n->in_outmsk != 0xffffffff))
4527145522Sdarrenr			continue;
4528145522Sdarrenr		if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) ||
4529145522Sdarrenr		     (ifp == nat->nat_ifps[1]))) {
4530145522Sdarrenr			nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], 4);
4531145522Sdarrenr			if (nat->nat_ifnames[1][0] != '\0') {
4532145522Sdarrenr				nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1],
4533145522Sdarrenr							  4);
4534145522Sdarrenr			} else
4535145522Sdarrenr				nat->nat_ifps[1] = nat->nat_ifps[0];
4536145522Sdarrenr			ifp2 = nat->nat_ifps[0];
4537145522Sdarrenr			if (ifp2 == NULL)
4538145522Sdarrenr				continue;
4539145522Sdarrenr
454053642Sguido			/*
454153642Sguido			 * Change the map-to address to be the same as the
454253642Sguido			 * new one.
454353642Sguido			 */
454453642Sguido			sum1 = nat->nat_outip.s_addr;
4545145522Sdarrenr			if (fr_ifpaddr(4, FRI_NORMAL, ifp2, &in, NULL) != -1)
454655929Sguido				nat->nat_outip = in;
454753642Sguido			sum2 = nat->nat_outip.s_addr;
454853642Sguido
454953642Sguido			if (sum1 == sum2)
455053642Sguido				continue;
455153642Sguido			/*
455253642Sguido			 * Readjust the checksum adjustment to take into
455353642Sguido			 * account the new IP#.
455453642Sguido			 */
455553642Sguido			CALC_SUMD(sum1, sum2, sumd);
455655929Sguido			/* XXX - dont change for TCP when solaris does
455755929Sguido			 * hardware checksumming.
455855929Sguido			 */
455955929Sguido			sumd += nat->nat_sumd[0];
456055929Sguido			nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
456155929Sguido			nat->nat_sumd[1] = nat->nat_sumd[0];
456253642Sguido		}
4563145522Sdarrenr	}
456453642Sguido
4565145522Sdarrenr	for (n = nat_list; (n != NULL); n = n->in_next) {
4566145522Sdarrenr		if ((ifp == NULL) || (n->in_ifps[0] == ifp))
4567145522Sdarrenr			n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4);
4568145522Sdarrenr		if ((ifp == NULL) || (n->in_ifps[1] == ifp))
4569145522Sdarrenr			n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4);
4570145522Sdarrenr	}
457160852Sdarrenr	RWLOCK_EXIT(&ipf_nat);
457253642Sguido	SPL_X(s);
457353642Sguido}
457453642Sguido
457553642Sguido
4576145522Sdarrenr/* ------------------------------------------------------------------------ */
4577145522Sdarrenr/* Function:    nat_icmpquerytype4                                          */
4578145522Sdarrenr/* Returns:     int - 1 == success, 0 == failure                            */
4579145522Sdarrenr/* Parameters:  icmptype(I) - ICMP type number                              */
4580145522Sdarrenr/*                                                                          */
4581145522Sdarrenr/* Tests to see if the ICMP type number passed is a query/response type or  */
4582145522Sdarrenr/* not.                                                                     */
4583145522Sdarrenr/* ------------------------------------------------------------------------ */
4584153876Sguidostatic int nat_icmpquerytype4(icmptype)
4585145522Sdarrenrint icmptype;
4586145522Sdarrenr{
4587145522Sdarrenr
4588145522Sdarrenr	/*
4589145522Sdarrenr	 * For the ICMP query NAT code, it is essential that both the query
4590145522Sdarrenr	 * and the reply match on the NAT rule. Because the NAT structure
4591145522Sdarrenr	 * does not keep track of the icmptype, and a single NAT structure
4592145522Sdarrenr	 * is used for all icmp types with the same src, dest and id, we
4593145522Sdarrenr	 * simply define the replies as queries as well. The funny thing is,
4594145522Sdarrenr	 * altough it seems silly to call a reply a query, this is exactly
4595145522Sdarrenr	 * as it is defined in the IPv4 specification
4596145522Sdarrenr	 */
4597145522Sdarrenr
4598145522Sdarrenr	switch (icmptype)
4599145522Sdarrenr	{
4600145522Sdarrenr
4601145522Sdarrenr	case ICMP_ECHOREPLY:
4602145522Sdarrenr	case ICMP_ECHO:
4603145522Sdarrenr	/* route aedvertisement/solliciation is currently unsupported: */
4604145522Sdarrenr	/* it would require rewriting the ICMP data section            */
4605145522Sdarrenr	case ICMP_TSTAMP:
4606145522Sdarrenr	case ICMP_TSTAMPREPLY:
4607145522Sdarrenr	case ICMP_IREQ:
4608145522Sdarrenr	case ICMP_IREQREPLY:
4609145522Sdarrenr	case ICMP_MASKREQ:
4610145522Sdarrenr	case ICMP_MASKREPLY:
4611145522Sdarrenr		return 1;
4612145522Sdarrenr	default:
4613145522Sdarrenr		return 0;
4614145522Sdarrenr	}
4615145522Sdarrenr}
4616145522Sdarrenr
4617145522Sdarrenr
4618145522Sdarrenr/* ------------------------------------------------------------------------ */
4619145522Sdarrenr/* Function:    nat_log                                                     */
4620145522Sdarrenr/* Returns:     Nil                                                         */
4621145522Sdarrenr/* Parameters:  nat(I)  - pointer to NAT structure                          */
4622145522Sdarrenr/*              type(I) - type of log entry to create                       */
4623145522Sdarrenr/*                                                                          */
4624145522Sdarrenr/* Creates a NAT log entry.                                                 */
4625145522Sdarrenr/* ------------------------------------------------------------------------ */
462653642Sguidovoid nat_log(nat, type)
462753642Sguidostruct nat *nat;
462853642Sguidou_int type;
462953642Sguido{
4630145522Sdarrenr#ifdef	IPFILTER_LOG
4631139005Smlaier# ifndef LARGE_NAT
463253642Sguido	struct ipnat *np;
4633138979Sdarrenr	int rulen;
4634138979Sdarrenr# endif
463553642Sguido	struct natlog natl;
463653642Sguido	void *items[1];
463753642Sguido	size_t sizes[1];
4638138979Sdarrenr	int types[1];
463953642Sguido
464053642Sguido	natl.nl_inip = nat->nat_inip;
464153642Sguido	natl.nl_outip = nat->nat_outip;
464253642Sguido	natl.nl_origip = nat->nat_oip;
4643145522Sdarrenr	natl.nl_bytes[0] = nat->nat_bytes[0];
4644145522Sdarrenr	natl.nl_bytes[1] = nat->nat_bytes[1];
4645145522Sdarrenr	natl.nl_pkts[0] = nat->nat_pkts[0];
4646145522Sdarrenr	natl.nl_pkts[1] = nat->nat_pkts[1];
464753642Sguido	natl.nl_origport = nat->nat_oport;
464853642Sguido	natl.nl_inport = nat->nat_inport;
464953642Sguido	natl.nl_outport = nat->nat_outport;
465057096Sguido	natl.nl_p = nat->nat_p;
465153642Sguido	natl.nl_type = type;
465253642Sguido	natl.nl_rule = -1;
4653145522Sdarrenr# ifndef LARGE_NAT
465453642Sguido	if (nat->nat_ptr != NULL) {
465553642Sguido		for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++)
465653642Sguido			if (np == nat->nat_ptr) {
465753642Sguido				natl.nl_rule = rulen;
465853642Sguido				break;
465953642Sguido			}
466053642Sguido	}
4661145522Sdarrenr# endif
466253642Sguido	items[0] = &natl;
466353642Sguido	sizes[0] = sizeof(natl);
466453642Sguido	types[0] = 0;
466553642Sguido
466653642Sguido	(void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1);
4667145522Sdarrenr#endif
466853642Sguido}
466992685Sdarrenr
467092685Sdarrenr
467192685Sdarrenr#if defined(__OpenBSD__)
4672145522Sdarrenr/* ------------------------------------------------------------------------ */
4673145522Sdarrenr/* Function:    nat_ifdetach                                                */
4674145522Sdarrenr/* Returns:     Nil                                                         */
4675145522Sdarrenr/* Parameters:  ifp(I) - pointer to network interface                       */
4676145522Sdarrenr/*                                                                          */
4677145522Sdarrenr/* Compatibility interface for OpenBSD to trigger the correct updating of   */
4678145522Sdarrenr/* interface references within IPFilter.                                    */
4679145522Sdarrenr/* ------------------------------------------------------------------------ */
468092685Sdarrenrvoid nat_ifdetach(ifp)
468192685Sdarrenrvoid *ifp;
468292685Sdarrenr{
4683145522Sdarrenr	frsync(ifp);
468492685Sdarrenr	return;
468592685Sdarrenr}
468692685Sdarrenr#endif
4687110916Sdarrenr
4688110916Sdarrenr
4689145522Sdarrenr/* ------------------------------------------------------------------------ */
4690170268Sdarrenr/* Function:    fr_ipnatderef                                               */
4691170268Sdarrenr/* Returns:     Nil                                                         */
4692170268Sdarrenr/* Parameters:  isp(I) - pointer to pointer to NAT rule                     */
4693170268Sdarrenr/* Write Locks: ipf_nat                                                     */
4694170268Sdarrenr/*                                                                          */
4695170268Sdarrenr/* ------------------------------------------------------------------------ */
4696170268Sdarrenrvoid fr_ipnatderef(inp)
4697170268Sdarrenripnat_t **inp;
4698170268Sdarrenr{
4699170268Sdarrenr	ipnat_t *in;
4700170268Sdarrenr
4701170268Sdarrenr	in = *inp;
4702170268Sdarrenr	*inp = NULL;
4703170268Sdarrenr	in->in_space++;
4704170268Sdarrenr	in->in_use--;
4705170268Sdarrenr	if (in->in_use == 0 && (in->in_flags & IPN_DELETE)) {
4706170268Sdarrenr		if (in->in_apr)
4707170268Sdarrenr			appr_free(in->in_apr);
4708172776Sdarrenr		MUTEX_DESTROY(&in->in_lock);
4709170268Sdarrenr		KFREE(in);
4710170268Sdarrenr		nat_stats.ns_rules--;
4711172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H)
4712170268Sdarrenr		if (nat_stats.ns_rules == 0)
4713170268Sdarrenr			pfil_delayed_copy = 1;
4714170268Sdarrenr#endif
4715170268Sdarrenr	}
4716170268Sdarrenr}
4717170268Sdarrenr
4718170268Sdarrenr
4719170268Sdarrenr/* ------------------------------------------------------------------------ */
4720145522Sdarrenr/* Function:    fr_natderef                                                 */
4721145522Sdarrenr/* Returns:     Nil                                                         */
4722145522Sdarrenr/* Parameters:  isp(I) - pointer to pointer to NAT table entry              */
4723145522Sdarrenr/*                                                                          */
4724145522Sdarrenr/* Decrement the reference counter for this NAT table entry and free it if  */
4725145522Sdarrenr/* there are no more things using it.                                       */
4726172776Sdarrenr/*                                                                          */
4727172776Sdarrenr/* IF nat_ref == 1 when this function is called, then we have an orphan nat */
4728172776Sdarrenr/* structure *because* it only gets called on paths _after_ nat_ref has been*/
4729172776Sdarrenr/* incremented.  If nat_ref == 1 then we shouldn't decrement it here        */
4730172776Sdarrenr/* because nat_delete() will do that and send nat_ref to -1.                */
4731172776Sdarrenr/*                                                                          */
4732172776Sdarrenr/* Holding the lock on nat_lock is required to serialise nat_delete() being */
4733172776Sdarrenr/* called from a NAT flush ioctl with a deref happening because of a packet.*/
4734145522Sdarrenr/* ------------------------------------------------------------------------ */
4735145522Sdarrenrvoid fr_natderef(natp)
4736145522Sdarrenrnat_t **natp;
4737145522Sdarrenr{
4738145522Sdarrenr	nat_t *nat;
4739145522Sdarrenr
4740145522Sdarrenr	nat = *natp;
4741145522Sdarrenr	*natp = NULL;
4742172776Sdarrenr
4743172776Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
4744172776Sdarrenr	if (nat->nat_ref > 1) {
4745172776Sdarrenr		nat->nat_ref--;
4746172776Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
4747172776Sdarrenr		return;
4748172776Sdarrenr	}
4749172776Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
4750172776Sdarrenr
4751145522Sdarrenr	WRITE_ENTER(&ipf_nat);
4752172776Sdarrenr	nat_delete(nat, NL_EXPIRE);
4753145522Sdarrenr	RWLOCK_EXIT(&ipf_nat);
4754145522Sdarrenr}
4755145522Sdarrenr
4756145522Sdarrenr
4757145522Sdarrenr/* ------------------------------------------------------------------------ */
4758145522Sdarrenr/* Function:    fr_natclone                                                 */
4759145522Sdarrenr/* Returns:     ipstate_t* - NULL == cloning failed,                        */
4760145522Sdarrenr/*                           else pointer to new state structure            */
4761145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
4762145522Sdarrenr/*              is(I)  - pointer to master state structure                  */
4763145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
4764145522Sdarrenr/*                                                                          */
4765145522Sdarrenr/* Create a "duplcate" state table entry from the master.                   */
4766145522Sdarrenr/* ------------------------------------------------------------------------ */
4767145522Sdarrenrstatic nat_t *fr_natclone(fin, nat)
4768145522Sdarrenrfr_info_t *fin;
4769145522Sdarrenrnat_t *nat;
4770145522Sdarrenr{
4771145522Sdarrenr	frentry_t *fr;
4772145522Sdarrenr	nat_t *clone;
4773145522Sdarrenr	ipnat_t *np;
4774145522Sdarrenr
4775145522Sdarrenr	KMALLOC(clone, nat_t *);
4776145522Sdarrenr	if (clone == NULL)
4777145522Sdarrenr		return NULL;
4778145522Sdarrenr	bcopy((char *)nat, (char *)clone, sizeof(*clone));
4779145522Sdarrenr
4780145522Sdarrenr	MUTEX_NUKE(&clone->nat_lock);
4781145522Sdarrenr
4782153876Sguido	clone->nat_aps = NULL;
4783153876Sguido	/*
4784153876Sguido	 * Initialize all these so that nat_delete() doesn't cause a crash.
4785153876Sguido	 */
4786153876Sguido	clone->nat_tqe.tqe_pnext = NULL;
4787153876Sguido	clone->nat_tqe.tqe_next = NULL;
4788153876Sguido	clone->nat_tqe.tqe_ifq = NULL;
4789153876Sguido	clone->nat_tqe.tqe_parent = clone;
4790153876Sguido
4791145522Sdarrenr	clone->nat_flags &= ~SI_CLONE;
4792145522Sdarrenr	clone->nat_flags |= SI_CLONED;
4793145522Sdarrenr
4794153876Sguido	if (clone->nat_hm)
4795153876Sguido		clone->nat_hm->hm_ref++;
4796145522Sdarrenr
4797145522Sdarrenr	if (nat_insert(clone, fin->fin_rev) == -1) {
4798145522Sdarrenr		KFREE(clone);
4799145522Sdarrenr		return NULL;
4800145522Sdarrenr	}
4801145522Sdarrenr	np = clone->nat_ptr;
4802145522Sdarrenr	if (np != NULL) {
4803145522Sdarrenr		if (nat_logging)
4804145522Sdarrenr			nat_log(clone, (u_int)np->in_redir);
4805145522Sdarrenr		np->in_use++;
4806145522Sdarrenr	}
4807145522Sdarrenr	fr = clone->nat_fr;
4808145522Sdarrenr	if (fr != NULL) {
4809145522Sdarrenr		MUTEX_ENTER(&fr->fr_lock);
4810145522Sdarrenr		fr->fr_ref++;
4811145522Sdarrenr		MUTEX_EXIT(&fr->fr_lock);
4812145522Sdarrenr	}
4813145522Sdarrenr
4814145522Sdarrenr	/*
4815145522Sdarrenr	 * Because the clone is created outside the normal loop of things and
4816145522Sdarrenr	 * TCP has special needs in terms of state, initialise the timeout
4817145522Sdarrenr	 * state of the new NAT from here.
4818145522Sdarrenr	 */
4819145522Sdarrenr	if (clone->nat_p == IPPROTO_TCP) {
4820153876Sguido		(void) fr_tcp_age(&clone->nat_tqe, fin, nat_tqb,
4821145522Sdarrenr				  clone->nat_flags);
4822145522Sdarrenr	}
4823145522Sdarrenr#ifdef	IPFILTER_SYNC
4824145522Sdarrenr	clone->nat_sync = ipfsync_new(SMC_NAT, fin, clone);
4825145522Sdarrenr#endif
4826145522Sdarrenr	if (nat_logging)
4827145522Sdarrenr		nat_log(clone, NL_CLONE);
4828145522Sdarrenr	return clone;
4829145522Sdarrenr}
4830145522Sdarrenr
4831145522Sdarrenr
4832145522Sdarrenr/* ------------------------------------------------------------------------ */
4833145522Sdarrenr/* Function:   nat_wildok                                                   */
4834145522Sdarrenr/* Returns:    int - 1 == packet's ports match wildcards                    */
4835145522Sdarrenr/*                   0 == packet's ports don't match wildcards              */
4836145522Sdarrenr/* Parameters: nat(I)   - NAT entry                                         */
4837145522Sdarrenr/*             sport(I) - source port                                       */
4838145522Sdarrenr/*             dport(I) - destination port                                  */
4839145522Sdarrenr/*             flags(I) - wildcard flags                                    */
4840145522Sdarrenr/*             dir(I)   - packet direction                                  */
4841145522Sdarrenr/*                                                                          */
4842145522Sdarrenr/* Use NAT entry and packet direction to determine which combination of     */
4843145522Sdarrenr/* wildcard flags should be used.                                           */
4844145522Sdarrenr/* ------------------------------------------------------------------------ */
4845153876Sguidostatic int nat_wildok(nat, sport, dport, flags, dir)
4846145522Sdarrenrnat_t *nat;
4847145522Sdarrenrint sport;
4848145522Sdarrenrint dport;
4849145522Sdarrenrint flags;
4850145522Sdarrenrint dir;
4851145522Sdarrenr{
4852145522Sdarrenr	/*
4853145522Sdarrenr	 * When called by       dir is set to
4854145522Sdarrenr	 * nat_inlookup         NAT_INBOUND (0)
4855145522Sdarrenr	 * nat_outlookup        NAT_OUTBOUND (1)
4856145522Sdarrenr	 *
4857145522Sdarrenr	 * We simply combine the packet's direction in dir with the original
4858145522Sdarrenr	 * "intended" direction of that NAT entry in nat->nat_dir to decide
4859145522Sdarrenr	 * which combination of wildcard flags to allow.
4860145522Sdarrenr	 */
4861145522Sdarrenr
4862145522Sdarrenr	switch ((dir << 1) | nat->nat_dir)
4863145522Sdarrenr	{
4864145522Sdarrenr	case 3: /* outbound packet / outbound entry */
4865145522Sdarrenr		if (((nat->nat_inport == sport) ||
4866145522Sdarrenr		    (flags & SI_W_SPORT)) &&
4867145522Sdarrenr		    ((nat->nat_oport == dport) ||
4868145522Sdarrenr		    (flags & SI_W_DPORT)))
4869145522Sdarrenr			return 1;
4870145522Sdarrenr		break;
4871145522Sdarrenr	case 2: /* outbound packet / inbound entry */
4872145522Sdarrenr		if (((nat->nat_outport == sport) ||
4873145522Sdarrenr		    (flags & SI_W_DPORT)) &&
4874145522Sdarrenr		    ((nat->nat_oport == dport) ||
4875145522Sdarrenr		    (flags & SI_W_SPORT)))
4876145522Sdarrenr			return 1;
4877145522Sdarrenr		break;
4878145522Sdarrenr	case 1: /* inbound packet / outbound entry */
4879145522Sdarrenr		if (((nat->nat_oport == sport) ||
4880145522Sdarrenr		    (flags & SI_W_DPORT)) &&
4881145522Sdarrenr		    ((nat->nat_outport == dport) ||
4882145522Sdarrenr		    (flags & SI_W_SPORT)))
4883145522Sdarrenr			return 1;
4884145522Sdarrenr		break;
4885145522Sdarrenr	case 0: /* inbound packet / inbound entry */
4886145522Sdarrenr		if (((nat->nat_oport == sport) ||
4887145522Sdarrenr		    (flags & SI_W_SPORT)) &&
4888145522Sdarrenr		    ((nat->nat_outport == dport) ||
4889145522Sdarrenr		    (flags & SI_W_DPORT)))
4890145522Sdarrenr			return 1;
4891145522Sdarrenr		break;
4892145522Sdarrenr	default:
4893145522Sdarrenr		break;
4894145522Sdarrenr	}
4895145522Sdarrenr
4896145522Sdarrenr	return(0);
4897145522Sdarrenr}
4898145522Sdarrenr
4899145522Sdarrenr
4900145522Sdarrenr/* ------------------------------------------------------------------------ */
4901145522Sdarrenr/* Function:    nat_mssclamp                                                */
4902145522Sdarrenr/* Returns:     Nil                                                         */
4903145522Sdarrenr/* Parameters:  tcp(I)    - pointer to TCP header                           */
4904145522Sdarrenr/*              maxmss(I) - value to clamp the TCP MSS to                   */
4905145522Sdarrenr/*              fin(I)    - pointer to packet information                   */
4906145522Sdarrenr/*              csump(I)  - pointer to TCP checksum                         */
4907145522Sdarrenr/*                                                                          */
4908145522Sdarrenr/* Check for MSS option and clamp it if necessary.  If found and changed,   */
4909145522Sdarrenr/* then the TCP header checksum will be updated to reflect the change in    */
4910145522Sdarrenr/* the MSS.                                                                 */
4911145522Sdarrenr/* ------------------------------------------------------------------------ */
4912110916Sdarrenrstatic void nat_mssclamp(tcp, maxmss, fin, csump)
4913110916Sdarrenrtcphdr_t *tcp;
4914110916Sdarrenru_32_t maxmss;
4915110916Sdarrenrfr_info_t *fin;
4916110916Sdarrenru_short *csump;
4917110916Sdarrenr{
4918110916Sdarrenr	u_char *cp, *ep, opt;
4919110916Sdarrenr	int hlen, advance;
4920110916Sdarrenr	u_32_t mss, sumd;
4921110916Sdarrenr
4922145522Sdarrenr	hlen = TCP_OFF(tcp) << 2;
4923110916Sdarrenr	if (hlen > sizeof(*tcp)) {
4924110916Sdarrenr		cp = (u_char *)tcp + sizeof(*tcp);
4925110916Sdarrenr		ep = (u_char *)tcp + hlen;
4926110916Sdarrenr
4927110916Sdarrenr		while (cp < ep) {
4928110916Sdarrenr			opt = cp[0];
4929110916Sdarrenr			if (opt == TCPOPT_EOL)
4930110916Sdarrenr				break;
4931110916Sdarrenr			else if (opt == TCPOPT_NOP) {
4932110916Sdarrenr				cp++;
4933110916Sdarrenr				continue;
4934110916Sdarrenr			}
4935145522Sdarrenr
4936145522Sdarrenr			if (cp + 1 >= ep)
4937110916Sdarrenr				break;
4938110916Sdarrenr			advance = cp[1];
4939145522Sdarrenr			if ((cp + advance > ep) || (advance <= 0))
4940110916Sdarrenr				break;
4941145522Sdarrenr			switch (opt)
4942145522Sdarrenr			{
4943110916Sdarrenr			case TCPOPT_MAXSEG:
4944110916Sdarrenr				if (advance != 4)
4945110916Sdarrenr					break;
4946145522Sdarrenr				mss = cp[2] * 256 + cp[3];
4947110916Sdarrenr				if (mss > maxmss) {
4948145522Sdarrenr					cp[2] = maxmss / 256;
4949145522Sdarrenr					cp[3] = maxmss & 0xff;
4950110916Sdarrenr					CALC_SUMD(mss, maxmss, sumd);
4951110916Sdarrenr					fix_outcksum(fin, csump, sumd);
4952110916Sdarrenr				}
4953110916Sdarrenr				break;
4954110916Sdarrenr			default:
4955110916Sdarrenr				/* ignore unknown options */
4956110916Sdarrenr				break;
4957110916Sdarrenr			}
4958145522Sdarrenr
4959145522Sdarrenr			cp += advance;
4960145522Sdarrenr		}
4961145522Sdarrenr	}
4962145522Sdarrenr}
4963145522Sdarrenr
4964145522Sdarrenr
4965145522Sdarrenr/* ------------------------------------------------------------------------ */
4966145522Sdarrenr/* Function:    fr_setnatqueue                                              */
4967145522Sdarrenr/* Returns:     Nil                                                         */
4968145522Sdarrenr/* Parameters:  nat(I)- pointer to NAT structure                            */
4969145522Sdarrenr/*              rev(I) - forward(0) or reverse(1) direction                 */
4970145522Sdarrenr/* Locks:       ipf_nat (read or write)                                     */
4971145522Sdarrenr/*                                                                          */
4972145522Sdarrenr/* Put the NAT entry on its default queue entry, using rev as a helped in   */
4973145522Sdarrenr/* determining which queue it should be placed on.                          */
4974145522Sdarrenr/* ------------------------------------------------------------------------ */
4975145522Sdarrenrvoid fr_setnatqueue(nat, rev)
4976145522Sdarrenrnat_t *nat;
4977145522Sdarrenrint rev;
4978145522Sdarrenr{
4979145522Sdarrenr	ipftq_t *oifq, *nifq;
4980145522Sdarrenr
4981145522Sdarrenr	if (nat->nat_ptr != NULL)
4982145522Sdarrenr		nifq = nat->nat_ptr->in_tqehead[rev];
4983145522Sdarrenr	else
4984145522Sdarrenr		nifq = NULL;
4985145522Sdarrenr
4986145522Sdarrenr	if (nifq == NULL) {
4987145522Sdarrenr		switch (nat->nat_p)
4988145522Sdarrenr		{
4989145522Sdarrenr		case IPPROTO_UDP :
4990145522Sdarrenr			nifq = &nat_udptq;
4991145522Sdarrenr			break;
4992145522Sdarrenr		case IPPROTO_ICMP :
4993145522Sdarrenr			nifq = &nat_icmptq;
4994145522Sdarrenr			break;
4995145522Sdarrenr		case IPPROTO_TCP :
4996145522Sdarrenr			nifq = nat_tqb + nat->nat_tqe.tqe_state[rev];
4997145522Sdarrenr			break;
4998145522Sdarrenr		default :
4999145522Sdarrenr			nifq = &nat_iptq;
5000145522Sdarrenr			break;
5001145522Sdarrenr		}
5002145522Sdarrenr	}
5003145522Sdarrenr
5004145522Sdarrenr	oifq = nat->nat_tqe.tqe_ifq;
5005145522Sdarrenr	/*
5006145522Sdarrenr	 * If it's currently on a timeout queue, move it from one queue to
5007145522Sdarrenr	 * another, else put it on the end of the newly determined queue.
5008145522Sdarrenr	 */
5009145522Sdarrenr	if (oifq != NULL)
5010145522Sdarrenr		fr_movequeue(&nat->nat_tqe, oifq, nifq);
5011145522Sdarrenr	else
5012145522Sdarrenr		fr_queueappend(&nat->nat_tqe, nifq, nat);
5013145522Sdarrenr	return;
5014145522Sdarrenr}
5015170268Sdarrenr
5016170268Sdarrenr
5017170268Sdarrenr/* ------------------------------------------------------------------------ */
5018170268Sdarrenr/* Function:    nat_getnext                                                 */
5019170268Sdarrenr/* Returns:     int - 0 == ok, else error                                   */
5020170268Sdarrenr/* Parameters:  t(I)   - pointer to ipftoken structure                      */
5021170268Sdarrenr/*              itp(I) - pointer to ipfgeniter_t structure                  */
5022170268Sdarrenr/*                                                                          */
5023170268Sdarrenr/* Fetch the next nat/ipnat structure pointer from the linked list and      */
5024170268Sdarrenr/* copy it out to the storage space pointed to by itp_data.  The next item  */
5025170268Sdarrenr/* in the list to look at is put back in the ipftoken struture.             */
5026170268Sdarrenr/* If we call ipf_freetoken, the accompanying pointer is set to NULL because*/
5027170268Sdarrenr/* ipf_freetoken will call a deref function for us and we dont want to call */
5028170268Sdarrenr/* that twice (second time would be in the second switch statement below.   */
5029170268Sdarrenr/* ------------------------------------------------------------------------ */
5030170268Sdarrenrstatic int nat_getnext(t, itp)
5031170268Sdarrenripftoken_t *t;
5032170268Sdarrenripfgeniter_t *itp;
5033170268Sdarrenr{
5034170268Sdarrenr	hostmap_t *hm, *nexthm = NULL, zerohm;
5035170268Sdarrenr	ipnat_t *ipn, *nextipnat = NULL, zeroipn;
5036170268Sdarrenr	nat_t *nat, *nextnat = NULL, zeronat;
5037170268Sdarrenr	int error = 0, count;
5038170268Sdarrenr	char *dst;
5039170268Sdarrenr
5040172776Sdarrenr	count = itp->igi_nitems;
5041172776Sdarrenr	if (count < 1)
5042172776Sdarrenr		return ENOSPC;
5043170268Sdarrenr
5044170268Sdarrenr	READ_ENTER(&ipf_nat);
5045170268Sdarrenr
5046170268Sdarrenr	switch (itp->igi_type)
5047170268Sdarrenr	{
5048170268Sdarrenr	case IPFGENITER_HOSTMAP :
5049170268Sdarrenr		hm = t->ipt_data;
5050170268Sdarrenr		if (hm == NULL) {
5051170268Sdarrenr			nexthm = ipf_hm_maplist;
5052170268Sdarrenr		} else {
5053170268Sdarrenr			nexthm = hm->hm_next;
5054170268Sdarrenr		}
5055170268Sdarrenr		break;
5056170268Sdarrenr
5057170268Sdarrenr	case IPFGENITER_IPNAT :
5058170268Sdarrenr		ipn = t->ipt_data;
5059170268Sdarrenr		if (ipn == NULL) {
5060170268Sdarrenr			nextipnat = nat_list;
5061170268Sdarrenr		} else {
5062170268Sdarrenr			nextipnat = ipn->in_next;
5063170268Sdarrenr		}
5064170268Sdarrenr		break;
5065170268Sdarrenr
5066170268Sdarrenr	case IPFGENITER_NAT :
5067170268Sdarrenr		nat = t->ipt_data;
5068170268Sdarrenr		if (nat == NULL) {
5069170268Sdarrenr			nextnat = nat_instances;
5070170268Sdarrenr		} else {
5071170268Sdarrenr			nextnat = nat->nat_next;
5072170268Sdarrenr		}
5073170268Sdarrenr		break;
5074170268Sdarrenr	default :
5075170268Sdarrenr		RWLOCK_EXIT(&ipf_nat);
5076170268Sdarrenr		return EINVAL;
5077170268Sdarrenr	}
5078170268Sdarrenr
5079170268Sdarrenr	dst = itp->igi_data;
5080172776Sdarrenr	for (;;) {
5081170268Sdarrenr		switch (itp->igi_type)
5082170268Sdarrenr		{
5083170268Sdarrenr		case IPFGENITER_HOSTMAP :
5084170268Sdarrenr			if (nexthm != NULL) {
5085170268Sdarrenr				if (count == 1) {
5086170268Sdarrenr					ATOMIC_INC32(nexthm->hm_ref);
5087172776Sdarrenr					t->ipt_data = nexthm;
5088170268Sdarrenr				}
5089170268Sdarrenr			} else {
5090170268Sdarrenr				bzero(&zerohm, sizeof(zerohm));
5091170268Sdarrenr				nexthm = &zerohm;
5092170268Sdarrenr				count = 1;
5093172776Sdarrenr				t->ipt_data = NULL;
5094170268Sdarrenr			}
5095170268Sdarrenr			break;
5096170268Sdarrenr
5097170268Sdarrenr		case IPFGENITER_IPNAT :
5098170268Sdarrenr			if (nextipnat != NULL) {
5099170268Sdarrenr				if (count == 1) {
5100170268Sdarrenr					MUTEX_ENTER(&nextipnat->in_lock);
5101170268Sdarrenr					nextipnat->in_use++;
5102170268Sdarrenr					MUTEX_EXIT(&nextipnat->in_lock);
5103172776Sdarrenr					t->ipt_data = nextipnat;
5104170268Sdarrenr				}
5105170268Sdarrenr			} else {
5106170268Sdarrenr				bzero(&zeroipn, sizeof(zeroipn));
5107170268Sdarrenr				nextipnat = &zeroipn;
5108170268Sdarrenr				count = 1;
5109172776Sdarrenr				t->ipt_data = NULL;
5110170268Sdarrenr			}
5111170268Sdarrenr			break;
5112170268Sdarrenr
5113170268Sdarrenr		case IPFGENITER_NAT :
5114170268Sdarrenr			if (nextnat != NULL) {
5115170268Sdarrenr				if (count == 1) {
5116170268Sdarrenr					MUTEX_ENTER(&nextnat->nat_lock);
5117170268Sdarrenr					nextnat->nat_ref++;
5118170268Sdarrenr					MUTEX_EXIT(&nextnat->nat_lock);
5119172776Sdarrenr					t->ipt_data = nextnat;
5120170268Sdarrenr				}
5121170268Sdarrenr			} else {
5122170268Sdarrenr				bzero(&zeronat, sizeof(zeronat));
5123170268Sdarrenr				nextnat = &zeronat;
5124170268Sdarrenr				count = 1;
5125172776Sdarrenr				t->ipt_data = NULL;
5126170268Sdarrenr			}
5127170268Sdarrenr			break;
5128170268Sdarrenr		default :
5129170268Sdarrenr			break;
5130170268Sdarrenr		}
5131170268Sdarrenr		RWLOCK_EXIT(&ipf_nat);
5132170268Sdarrenr
5133172776Sdarrenr		/*
5134172776Sdarrenr		 * Copying out to user space needs to be done without the lock.
5135172776Sdarrenr		 */
5136170268Sdarrenr		switch (itp->igi_type)
5137170268Sdarrenr		{
5138170268Sdarrenr		case IPFGENITER_HOSTMAP :
5139170268Sdarrenr			error = COPYOUT(nexthm, dst, sizeof(*nexthm));
5140170268Sdarrenr			if (error != 0)
5141170268Sdarrenr				error = EFAULT;
5142170268Sdarrenr			else
5143170268Sdarrenr				dst += sizeof(*nexthm);
5144170268Sdarrenr			break;
5145170268Sdarrenr
5146170268Sdarrenr		case IPFGENITER_IPNAT :
5147170268Sdarrenr			error = COPYOUT(nextipnat, dst, sizeof(*nextipnat));
5148170268Sdarrenr			if (error != 0)
5149170268Sdarrenr				error = EFAULT;
5150170268Sdarrenr			else
5151170268Sdarrenr				dst += sizeof(*nextipnat);
5152170268Sdarrenr			break;
5153170268Sdarrenr
5154170268Sdarrenr		case IPFGENITER_NAT :
5155170268Sdarrenr			error = COPYOUT(nextnat, dst, sizeof(*nextnat));
5156170268Sdarrenr			if (error != 0)
5157170268Sdarrenr				error = EFAULT;
5158170268Sdarrenr			else
5159170268Sdarrenr				dst += sizeof(*nextnat);
5160170268Sdarrenr			break;
5161170268Sdarrenr		}
5162170268Sdarrenr
5163170268Sdarrenr		if ((count == 1) || (error != 0))
5164170268Sdarrenr			break;
5165170268Sdarrenr
5166172776Sdarrenr		count--;
5167172776Sdarrenr
5168170268Sdarrenr		READ_ENTER(&ipf_nat);
5169170268Sdarrenr
5170172776Sdarrenr		/*
5171172776Sdarrenr		 * We need to have the lock again here to make sure that
5172172776Sdarrenr		 * using _next is consistent.
5173172776Sdarrenr		 */
5174170268Sdarrenr		switch (itp->igi_type)
5175170268Sdarrenr		{
5176170268Sdarrenr		case IPFGENITER_HOSTMAP :
5177172776Sdarrenr			nexthm = nexthm->hm_next;
5178170268Sdarrenr			break;
5179170268Sdarrenr		case IPFGENITER_IPNAT :
5180172776Sdarrenr			nextipnat = nextipnat->in_next;
5181170268Sdarrenr			break;
5182170268Sdarrenr		case IPFGENITER_NAT :
5183172776Sdarrenr			nextnat = nextnat->nat_next;
5184170268Sdarrenr			break;
5185170268Sdarrenr		}
5186170268Sdarrenr	}
5187170268Sdarrenr
5188172776Sdarrenr
5189172776Sdarrenr	switch (itp->igi_type)
5190172776Sdarrenr	{
5191172776Sdarrenr	case IPFGENITER_HOSTMAP :
5192172776Sdarrenr		if (hm != NULL) {
5193172776Sdarrenr			WRITE_ENTER(&ipf_nat);
5194172776Sdarrenr			fr_hostmapdel(&hm);
5195172776Sdarrenr			RWLOCK_EXIT(&ipf_nat);
5196172776Sdarrenr		}
5197172776Sdarrenr		break;
5198172776Sdarrenr	case IPFGENITER_IPNAT :
5199172776Sdarrenr		if (ipn != NULL) {
5200172776Sdarrenr			fr_ipnatderef(&ipn);
5201172776Sdarrenr		}
5202172776Sdarrenr		break;
5203172776Sdarrenr	case IPFGENITER_NAT :
5204172776Sdarrenr		if (nat != NULL) {
5205172776Sdarrenr			fr_natderef(&nat);
5206172776Sdarrenr		}
5207172776Sdarrenr		break;
5208172776Sdarrenr	default :
5209172776Sdarrenr		break;
5210172776Sdarrenr	}
5211172776Sdarrenr
5212170268Sdarrenr	return error;
5213170268Sdarrenr}
5214170268Sdarrenr
5215170268Sdarrenr
5216170268Sdarrenr/* ------------------------------------------------------------------------ */
5217170268Sdarrenr/* Function:    nat_iterator                                                */
5218170268Sdarrenr/* Returns:     int - 0 == ok, else error                                   */
5219170268Sdarrenr/* Parameters:  token(I) - pointer to ipftoken structure                    */
5220170268Sdarrenr/*              itp(I) - pointer to ipfgeniter_t structure                  */
5221170268Sdarrenr/*                                                                          */
5222170268Sdarrenr/* This function acts as a handler for the SIOCGENITER ioctls that use a    */
5223170268Sdarrenr/* generic structure to iterate through a list.  There are three different  */
5224170268Sdarrenr/* linked lists of NAT related information to go through: NAT rules, active */
5225170268Sdarrenr/* NAT mappings and the NAT fragment cache.                                 */
5226170268Sdarrenr/* ------------------------------------------------------------------------ */
5227170268Sdarrenrstatic int nat_iterator(token, itp)
5228170268Sdarrenripftoken_t *token;
5229170268Sdarrenripfgeniter_t *itp;
5230170268Sdarrenr{
5231170268Sdarrenr	int error;
5232170268Sdarrenr
5233170268Sdarrenr	if (itp->igi_data == NULL)
5234170268Sdarrenr		return EFAULT;
5235170268Sdarrenr
5236170268Sdarrenr	token->ipt_subtype = itp->igi_type;
5237170268Sdarrenr
5238170268Sdarrenr	switch (itp->igi_type)
5239170268Sdarrenr	{
5240170268Sdarrenr	case IPFGENITER_HOSTMAP :
5241170268Sdarrenr	case IPFGENITER_IPNAT :
5242170268Sdarrenr	case IPFGENITER_NAT :
5243170268Sdarrenr		error = nat_getnext(token, itp);
5244170268Sdarrenr		break;
5245170268Sdarrenr
5246170268Sdarrenr	case IPFGENITER_NATFRAG :
5247170268Sdarrenr#ifdef USE_MUTEXES
5248170268Sdarrenr		error = fr_nextfrag(token, itp, &ipfr_natlist,
5249170268Sdarrenr				    &ipfr_nattail, &ipf_natfrag);
5250170268Sdarrenr#else
5251170268Sdarrenr		error = fr_nextfrag(token, itp, &ipfr_natlist, &ipfr_nattail);
5252170268Sdarrenr#endif
5253170268Sdarrenr		break;
5254170268Sdarrenr	default :
5255170268Sdarrenr		error = EINVAL;
5256170268Sdarrenr		break;
5257170268Sdarrenr	}
5258170268Sdarrenr
5259170268Sdarrenr	return error;
5260170268Sdarrenr}
5261170268Sdarrenr
5262170268Sdarrenr
5263170268Sdarrenr/* ------------------------------------------------------------------------ */
5264170268Sdarrenr/* Function:    nat_extraflush                                              */
5265170268Sdarrenr/* Returns:     int - 0 == success, -1 == failure                           */
5266170268Sdarrenr/* Parameters:  which(I) - how to flush the active NAT table                */
5267170268Sdarrenr/* Write Locks: ipf_nat                                                     */
5268170268Sdarrenr/*                                                                          */
5269170268Sdarrenr/* Flush nat tables.  Three actions currently defined:                      */
5270170268Sdarrenr/* which == 0 : flush all nat table entries                                 */
5271170268Sdarrenr/* which == 1 : flush TCP connections which have started to close but are   */
5272170268Sdarrenr/*	      stuck for some reason.                                        */
5273170268Sdarrenr/* which == 2 : flush TCP connections which have been idle for a long time, */
5274170268Sdarrenr/*	      starting at > 4 days idle and working back in successive half-*/
5275170268Sdarrenr/*	      days to at most 12 hours old.  If this fails to free enough   */
5276170268Sdarrenr/*            slots then work backwards in half hour slots to 30 minutes.   */
5277170268Sdarrenr/*            If that too fails, then work backwards in 30 second intervals */
5278170268Sdarrenr/*            for the last 30 minutes to at worst 30 seconds idle.          */
5279170268Sdarrenr/* ------------------------------------------------------------------------ */
5280170268Sdarrenrstatic int nat_extraflush(which)
5281170268Sdarrenrint which;
5282170268Sdarrenr{
5283170268Sdarrenr	ipftq_t *ifq, *ifqnext;
5284170268Sdarrenr	nat_t *nat, **natp;
5285170268Sdarrenr	ipftqent_t *tqn;
5286170268Sdarrenr	int removed;
5287170268Sdarrenr	SPL_INT(s);
5288170268Sdarrenr
5289170268Sdarrenr	removed = 0;
5290170268Sdarrenr
5291170268Sdarrenr	SPL_NET(s);
5292170268Sdarrenr
5293170268Sdarrenr	switch (which)
5294170268Sdarrenr	{
5295170268Sdarrenr	case 0 :
5296170268Sdarrenr		/*
5297170268Sdarrenr		 * Style 0 flush removes everything...
5298170268Sdarrenr		 */
5299170268Sdarrenr		for (natp = &nat_instances; ((nat = *natp) != NULL); ) {
5300170268Sdarrenr			nat_delete(nat, NL_FLUSH);
5301170268Sdarrenr			removed++;
5302170268Sdarrenr		}
5303170268Sdarrenr		break;
5304170268Sdarrenr
5305170268Sdarrenr	case 1 :
5306170268Sdarrenr		/*
5307170268Sdarrenr		 * Since we're only interested in things that are closing,
5308170268Sdarrenr		 * we can start with the appropriate timeout queue.
5309170268Sdarrenr		 */
5310170268Sdarrenr		for (ifq = nat_tqb + IPF_TCPS_CLOSE_WAIT; ifq != NULL;
5311170268Sdarrenr		     ifq = ifq->ifq_next) {
5312170268Sdarrenr
5313170268Sdarrenr			for (tqn = ifq->ifq_head; tqn != NULL; ) {
5314170268Sdarrenr				nat = tqn->tqe_parent;
5315170268Sdarrenr				tqn = tqn->tqe_next;
5316170268Sdarrenr				if (nat->nat_p != IPPROTO_TCP)
5317170268Sdarrenr					break;
5318170268Sdarrenr				nat_delete(nat, NL_EXPIRE);
5319170268Sdarrenr				removed++;
5320170268Sdarrenr			}
5321170268Sdarrenr		}
5322170268Sdarrenr
5323170268Sdarrenr		/*
5324170268Sdarrenr		 * Also need to look through the user defined queues.
5325170268Sdarrenr		 */
5326170268Sdarrenr		for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) {
5327170268Sdarrenr			ifqnext = ifq->ifq_next;
5328170268Sdarrenr			for (tqn = ifq->ifq_head; tqn != NULL; ) {
5329170268Sdarrenr				nat = tqn->tqe_parent;
5330170268Sdarrenr				tqn = tqn->tqe_next;
5331170268Sdarrenr				if (nat->nat_p != IPPROTO_TCP)
5332170268Sdarrenr					continue;
5333170268Sdarrenr
5334170268Sdarrenr				if ((nat->nat_tcpstate[0] >
5335170268Sdarrenr				     IPF_TCPS_ESTABLISHED) &&
5336170268Sdarrenr				    (nat->nat_tcpstate[1] >
5337170268Sdarrenr				     IPF_TCPS_ESTABLISHED)) {
5338170268Sdarrenr					nat_delete(nat, NL_EXPIRE);
5339170268Sdarrenr					removed++;
5340170268Sdarrenr				}
5341170268Sdarrenr			}
5342170268Sdarrenr		}
5343170268Sdarrenr		break;
5344170268Sdarrenr
5345170268Sdarrenr		/*
5346170268Sdarrenr		 * Args 5-11 correspond to flushing those particular states
5347170268Sdarrenr		 * for TCP connections.
5348170268Sdarrenr		 */
5349170268Sdarrenr	case IPF_TCPS_CLOSE_WAIT :
5350170268Sdarrenr	case IPF_TCPS_FIN_WAIT_1 :
5351170268Sdarrenr	case IPF_TCPS_CLOSING :
5352170268Sdarrenr	case IPF_TCPS_LAST_ACK :
5353170268Sdarrenr	case IPF_TCPS_FIN_WAIT_2 :
5354170268Sdarrenr	case IPF_TCPS_TIME_WAIT :
5355170268Sdarrenr	case IPF_TCPS_CLOSED :
5356170268Sdarrenr		tqn = nat_tqb[which].ifq_head;
5357170268Sdarrenr		while (tqn != NULL) {
5358170268Sdarrenr			nat = tqn->tqe_parent;
5359170268Sdarrenr			tqn = tqn->tqe_next;
5360170268Sdarrenr			nat_delete(nat, NL_FLUSH);
5361170268Sdarrenr			removed++;
5362170268Sdarrenr		}
5363170268Sdarrenr		break;
5364170268Sdarrenr
5365170268Sdarrenr	default :
5366170268Sdarrenr		if (which < 30)
5367170268Sdarrenr			break;
5368170268Sdarrenr
5369170268Sdarrenr		/*
5370170268Sdarrenr		 * Take a large arbitrary number to mean the number of seconds
5371170268Sdarrenr		 * for which which consider to be the maximum value we'll allow
5372170268Sdarrenr		 * the expiration to be.
5373170268Sdarrenr		 */
5374170268Sdarrenr		which = IPF_TTLVAL(which);
5375170268Sdarrenr		for (natp = &nat_instances; ((nat = *natp) != NULL); ) {
5376170268Sdarrenr			if (fr_ticks - nat->nat_touched > which) {
5377170268Sdarrenr				nat_delete(nat, NL_FLUSH);
5378170268Sdarrenr				removed++;
5379170268Sdarrenr			} else
5380170268Sdarrenr				natp = &nat->nat_next;
5381170268Sdarrenr		}
5382170268Sdarrenr		break;
5383170268Sdarrenr	}
5384170268Sdarrenr
5385170268Sdarrenr	if (which != 2) {
5386170268Sdarrenr		SPL_X(s);
5387170268Sdarrenr		return removed;
5388170268Sdarrenr	}
5389170268Sdarrenr
5390170268Sdarrenr	/*
5391170268Sdarrenr	 * Asked to remove inactive entries because the table is full.
5392170268Sdarrenr	 */
5393170268Sdarrenr	if (fr_ticks - nat_last_force_flush > IPF_TTLVAL(5)) {
5394170268Sdarrenr		nat_last_force_flush = fr_ticks;
5395170268Sdarrenr		removed = ipf_queueflush(nat_flush_entry, nat_tqb, nat_utqe);
5396170268Sdarrenr	}
5397170268Sdarrenr
5398170268Sdarrenr	SPL_X(s);
5399170268Sdarrenr	return removed;
5400170268Sdarrenr}
5401170268Sdarrenr
5402170268Sdarrenr
5403170268Sdarrenr/* ------------------------------------------------------------------------ */
5404170268Sdarrenr/* Function:    nat_flush_entry                                             */
5405170268Sdarrenr/* Returns:     0 - always succeeds                                         */
5406170268Sdarrenr/* Parameters:  entry(I) - pointer to NAT entry                             */
5407170268Sdarrenr/* Write Locks: ipf_nat                                                     */
5408170268Sdarrenr/*                                                                          */
5409170268Sdarrenr/* This function is a stepping stone between ipf_queueflush() and           */
5410170268Sdarrenr/* nat_dlete().  It is used so we can provide a uniform interface via the   */
5411170268Sdarrenr/* ipf_queueflush() function.  Since the nat_delete() function returns void */
5412170268Sdarrenr/* we translate that to mean it always succeeds in deleting something.      */
5413170268Sdarrenr/* ------------------------------------------------------------------------ */
5414170268Sdarrenrstatic int nat_flush_entry(entry)
5415170268Sdarrenrvoid *entry;
5416170268Sdarrenr{
5417170268Sdarrenr	nat_delete(entry, NL_FLUSH);
5418170268Sdarrenr	return 0;
5419170268Sdarrenr}
5420172776Sdarrenr
5421172776Sdarrenr
5422172776Sdarrenr/* ------------------------------------------------------------------------ */
5423172776Sdarrenr/* Function:    nat_gettable                                                */
5424172776Sdarrenr/* Returns:     int     - 0 = success, else error                           */
5425172776Sdarrenr/* Parameters:  data(I) - pointer to ioctl data                             */
5426172776Sdarrenr/*                                                                          */
5427172776Sdarrenr/* This function handles ioctl requests for tables of nat information.      */
5428172776Sdarrenr/* At present the only table it deals with is the hash bucket statistics.   */
5429172776Sdarrenr/* ------------------------------------------------------------------------ */
5430172776Sdarrenrstatic int nat_gettable(data)
5431172776Sdarrenrchar *data;
5432172776Sdarrenr{
5433172776Sdarrenr	ipftable_t table;
5434172776Sdarrenr	int error;
5435172776Sdarrenr
5436172776Sdarrenr	error = fr_inobj(data, &table, IPFOBJ_GTABLE);
5437172776Sdarrenr	if (error != 0)
5438172776Sdarrenr		return error;
5439172776Sdarrenr
5440172776Sdarrenr	switch (table.ita_type)
5441172776Sdarrenr	{
5442172776Sdarrenr	case IPFTABLE_BUCKETS_NATIN :
5443172776Sdarrenr		error = COPYOUT(nat_stats.ns_bucketlen[0], table.ita_table,
5444172776Sdarrenr				ipf_nattable_sz * sizeof(u_long));
5445172776Sdarrenr		break;
5446172776Sdarrenr
5447172776Sdarrenr	case IPFTABLE_BUCKETS_NATOUT :
5448172776Sdarrenr		error = COPYOUT(nat_stats.ns_bucketlen[1], table.ita_table,
5449172776Sdarrenr				ipf_nattable_sz * sizeof(u_long));
5450172776Sdarrenr		break;
5451172776Sdarrenr
5452172776Sdarrenr	default :
5453172776Sdarrenr		return EINVAL;
5454172776Sdarrenr	}
5455172776Sdarrenr
5456172776Sdarrenr	if (error != 0) {
5457172776Sdarrenr		error = EFAULT;
5458172776Sdarrenr	}
5459172776Sdarrenr	return error;
5460172776Sdarrenr}
5461