1145522Sdarrenr/*	$FreeBSD$	*/
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$";
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 *));
193173181Sdarrenrstatic	int	fr_natgetent __P((caddr_t, int));
194173181Sdarrenrstatic	int	fr_natgetsz __P((caddr_t, int));
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
665192895Sjamie#  if defined(__FreeBSD_version) && (__FreeBSD_version >= 500034)
666192895Sjamie	if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE)) {
667192895Sjamie#  else
668170268Sdarrenr	if ((securelevel >= 3) && (mode & FWRITE)) {
669192895Sjamie#  endif
670170268Sdarrenr		return EPERM;
671170268Sdarrenr	}
672170268Sdarrenr# endif
67353642Sguido#endif
67453642Sguido
675145522Sdarrenr#if defined(__osf__) && defined(_KERNEL)
676145522Sdarrenr	getlock = 0;
677145522Sdarrenr#else
678145522Sdarrenr	getlock = (mode & NAT_LOCKHELD) ? 0 : 1;
679145522Sdarrenr#endif
680145522Sdarrenr
68153642Sguido	nat = NULL;     /* XXX gcc -Wuninitialized */
682145522Sdarrenr	if (cmd == (ioctlcmd_t)SIOCADNAT) {
683145522Sdarrenr		KMALLOC(nt, ipnat_t *);
684145522Sdarrenr	} else {
685145522Sdarrenr		nt = NULL;
686145522Sdarrenr	}
687145522Sdarrenr
688145522Sdarrenr	if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) {
68995418Sdarrenr		if (mode & NAT_SYSSPACE) {
69095418Sdarrenr			bcopy(data, (char *)&natd, sizeof(natd));
69195418Sdarrenr			error = 0;
69295418Sdarrenr		} else {
693145522Sdarrenr			error = fr_inobj(data, &natd, IPFOBJ_IPNAT);
69495418Sdarrenr		}
69564580Sdarrenr	}
69653642Sguido
697145522Sdarrenr	if (error != 0)
69860852Sdarrenr		goto done;
69960852Sdarrenr
70053642Sguido	/*
70153642Sguido	 * For add/delete, look to see if the NAT entry is already present
70253642Sguido	 */
703145522Sdarrenr	if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) {
70453642Sguido		nat = &natd;
705145522Sdarrenr		if (nat->in_v == 0)	/* For backward compat. */
706145522Sdarrenr			nat->in_v = 4;
70753642Sguido		nat->in_flags &= IPN_USERFLAGS;
70853642Sguido		if ((nat->in_redir & NAT_MAPBLK) == 0) {
70960852Sdarrenr			if ((nat->in_flags & IPN_SPLIT) == 0)
71060852Sdarrenr				nat->in_inip &= nat->in_inmsk;
71160852Sdarrenr			if ((nat->in_flags & IPN_IPRANGE) == 0)
71253642Sguido				nat->in_outip &= nat->in_outmsk;
71353642Sguido		}
714145522Sdarrenr		MUTEX_ENTER(&ipf_natio);
715145522Sdarrenr		for (np = &nat_list; ((n = *np) != NULL); np = &n->in_next)
716170268Sdarrenr			if (bcmp((char *)&nat->in_flags, (char *)&n->in_flags,
717170268Sdarrenr					IPN_CMPSIZ) == 0) {
718170268Sdarrenr				if (nat->in_redir == NAT_REDIRECT &&
719170268Sdarrenr				    nat->in_pnext != n->in_pnext)
720170268Sdarrenr					continue;
72153642Sguido				break;
722170268Sdarrenr			}
72353642Sguido	}
72453642Sguido
72553642Sguido	switch (cmd)
72653642Sguido	{
72755929Sguido#ifdef  IPFILTER_LOG
72855929Sguido	case SIOCIPFFB :
72960852Sdarrenr	{
73060852Sdarrenr		int tmp;
73160852Sdarrenr
73255929Sguido		if (!(mode & FWRITE))
73355929Sguido			error = EPERM;
73460852Sdarrenr		else {
73560852Sdarrenr			tmp = ipflog_clear(IPL_LOGNAT);
736170268Sdarrenr			error = BCOPYOUT((char *)&tmp, (char *)data,
737170268Sdarrenr					 sizeof(tmp));
738170268Sdarrenr			if (error != 0)
739170268Sdarrenr				error = EFAULT;
74060852Sdarrenr		}
74155929Sguido		break;
74260852Sdarrenr	}
743170268Sdarrenr
744145522Sdarrenr	case SIOCSETLG :
745145522Sdarrenr		if (!(mode & FWRITE))
746145522Sdarrenr			error = EPERM;
747145522Sdarrenr		else {
748170268Sdarrenr			error = BCOPYIN((char *)data, (char *)&nat_logging,
749170268Sdarrenr					sizeof(nat_logging));
750170268Sdarrenr			if (error != 0)
751170268Sdarrenr				error = EFAULT;
752145522Sdarrenr		}
753145522Sdarrenr		break;
754170268Sdarrenr
755145522Sdarrenr	case SIOCGETLG :
756170268Sdarrenr		error = BCOPYOUT((char *)&nat_logging, (char *)data,
757170268Sdarrenr				 sizeof(nat_logging));
758170268Sdarrenr		if (error != 0)
759170268Sdarrenr			error = EFAULT;
760145522Sdarrenr		break;
761170268Sdarrenr
762145522Sdarrenr	case FIONREAD :
763145522Sdarrenr		arg = iplused[IPL_LOGNAT];
764170268Sdarrenr		error = BCOPYOUT(&arg, data, sizeof(arg));
765170268Sdarrenr		if (error != 0)
766170268Sdarrenr			error = EFAULT;
767145522Sdarrenr		break;
76855929Sguido#endif
76953642Sguido	case SIOCADNAT :
77053642Sguido		if (!(mode & FWRITE)) {
77153642Sguido			error = EPERM;
772145522Sdarrenr		} else if (n != NULL) {
77353642Sguido			error = EEXIST;
774145522Sdarrenr		} else if (nt == NULL) {
775145522Sdarrenr			error = ENOMEM;
77653642Sguido		}
777145522Sdarrenr		if (error != 0) {
778145522Sdarrenr			MUTEX_EXIT(&ipf_natio);
77953642Sguido			break;
78053642Sguido		}
781145522Sdarrenr		bcopy((char *)nat, (char *)nt, sizeof(*n));
782145522Sdarrenr		error = nat_siocaddnat(nt, np, getlock);
783145522Sdarrenr		MUTEX_EXIT(&ipf_natio);
784145522Sdarrenr		if (error == 0)
785145522Sdarrenr			nt = NULL;
78653642Sguido		break;
787170268Sdarrenr
78853642Sguido	case SIOCRMNAT :
78953642Sguido		if (!(mode & FWRITE)) {
79053642Sguido			error = EPERM;
79153642Sguido			n = NULL;
792145522Sdarrenr		} else if (n == NULL) {
793145522Sdarrenr			error = ESRCH;
79453642Sguido		}
795145522Sdarrenr
796145522Sdarrenr		if (error != 0) {
797145522Sdarrenr			MUTEX_EXIT(&ipf_natio);
79853642Sguido			break;
79953642Sguido		}
800145522Sdarrenr		nat_siocdelnat(n, np, getlock);
801145522Sdarrenr
802145522Sdarrenr		MUTEX_EXIT(&ipf_natio);
80353642Sguido		n = NULL;
80453642Sguido		break;
805170268Sdarrenr
80653642Sguido	case SIOCGNATS :
80753642Sguido		nat_stats.ns_table[0] = nat_table[0];
80853642Sguido		nat_stats.ns_table[1] = nat_table[1];
80953642Sguido		nat_stats.ns_list = nat_list;
810170268Sdarrenr		nat_stats.ns_maptable = ipf_hm_maptable;
811170268Sdarrenr		nat_stats.ns_maplist = ipf_hm_maplist;
81253642Sguido		nat_stats.ns_nattab_sz = ipf_nattable_sz;
813145522Sdarrenr		nat_stats.ns_nattab_max = ipf_nattable_max;
81453642Sguido		nat_stats.ns_rultab_sz = ipf_natrules_sz;
81553642Sguido		nat_stats.ns_rdrtab_sz = ipf_rdrrules_sz;
81680482Sdarrenr		nat_stats.ns_hostmap_sz = ipf_hostmap_sz;
81753642Sguido		nat_stats.ns_instances = nat_instances;
81853642Sguido		nat_stats.ns_apslist = ap_sess_list;
819170268Sdarrenr		nat_stats.ns_ticks = fr_ticks;
820145522Sdarrenr		error = fr_outobj(data, &nat_stats, IPFOBJ_NATSTAT);
82153642Sguido		break;
822170268Sdarrenr
82353642Sguido	case SIOCGNATL :
82453642Sguido	    {
82553642Sguido		natlookup_t nl;
82653642Sguido
827145522Sdarrenr		error = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP);
828145522Sdarrenr		if (error == 0) {
829173181Sdarrenr			void *ptr;
830173181Sdarrenr
831173181Sdarrenr			if (getlock) {
832173181Sdarrenr				READ_ENTER(&ipf_nat);
833173181Sdarrenr			}
834173181Sdarrenr			ptr = nat_lookupredir(&nl);
835173181Sdarrenr			if (getlock) {
836173181Sdarrenr				RWLOCK_EXIT(&ipf_nat);
837173181Sdarrenr			}
838173181Sdarrenr			if (ptr != NULL) {
839145522Sdarrenr				error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP);
840145522Sdarrenr			} else {
841145522Sdarrenr				error = ESRCH;
842145522Sdarrenr			}
843145522Sdarrenr		}
84453642Sguido		break;
84553642Sguido	    }
846170268Sdarrenr
84760852Sdarrenr	case SIOCIPFFL :	/* old SIOCFLNAT & SIOCCNATL */
84853642Sguido		if (!(mode & FWRITE)) {
84953642Sguido			error = EPERM;
85053642Sguido			break;
85153642Sguido		}
852145522Sdarrenr		if (getlock) {
853145522Sdarrenr			WRITE_ENTER(&ipf_nat);
854145522Sdarrenr		}
855170268Sdarrenr
856170268Sdarrenr		error = BCOPYIN(data, &arg, sizeof(arg));
857170268Sdarrenr		if (error != 0)
858170268Sdarrenr			error = EFAULT;
859170268Sdarrenr		else {
860170268Sdarrenr			if (arg == 0)
861170268Sdarrenr				ret = nat_flushtable();
862170268Sdarrenr			else if (arg == 1)
863170268Sdarrenr				ret = nat_clearlist();
864170268Sdarrenr			else
865170268Sdarrenr				ret = nat_extraflush(arg);
866170268Sdarrenr		}
867170268Sdarrenr
868145522Sdarrenr		if (getlock) {
869145522Sdarrenr			RWLOCK_EXIT(&ipf_nat);
87060852Sdarrenr		}
871145522Sdarrenr		if (error == 0) {
872170268Sdarrenr			error = BCOPYOUT(&ret, data, sizeof(ret));
873145522Sdarrenr		}
87453642Sguido		break;
875170268Sdarrenr
876145522Sdarrenr	case SIOCPROXY :
877170268Sdarrenr		error = appr_ioctl(data, cmd, mode, ctx);
878145522Sdarrenr		break;
879170268Sdarrenr
88060852Sdarrenr	case SIOCSTLCK :
881153876Sguido		if (!(mode & FWRITE)) {
882153876Sguido			error = EPERM;
883153876Sguido		} else {
884172776Sdarrenr			error = fr_lock(data, &fr_nat_lock);
885153876Sguido		}
88653642Sguido		break;
887170268Sdarrenr
88860852Sdarrenr	case SIOCSTPUT :
889153876Sguido		if ((mode & FWRITE) != 0) {
890145522Sdarrenr			error = fr_natputent(data, getlock);
891145522Sdarrenr		} else {
89260852Sdarrenr			error = EACCES;
893145522Sdarrenr		}
89460852Sdarrenr		break;
895170268Sdarrenr
89660852Sdarrenr	case SIOCSTGSZ :
897145522Sdarrenr		if (fr_nat_lock) {
898173181Sdarrenr			error = fr_natgetsz(data, getlock);
899145522Sdarrenr		} else
90060852Sdarrenr			error = EACCES;
90160852Sdarrenr		break;
902170268Sdarrenr
90360852Sdarrenr	case SIOCSTGET :
904145522Sdarrenr		if (fr_nat_lock) {
905173181Sdarrenr			error = fr_natgetent(data, getlock);
906145522Sdarrenr		} else
90760852Sdarrenr			error = EACCES;
90860852Sdarrenr		break;
909170268Sdarrenr
910170268Sdarrenr	case SIOCGENITER :
911170268Sdarrenr	    {
912170268Sdarrenr		ipfgeniter_t iter;
913170268Sdarrenr		ipftoken_t *token;
914170268Sdarrenr
915170268Sdarrenr		SPL_SCHED(s);
916170268Sdarrenr		error = fr_inobj(data, &iter, IPFOBJ_GENITER);
917170268Sdarrenr		if (error == 0) {
918170268Sdarrenr			token = ipf_findtoken(iter.igi_type, uid, ctx);
919170268Sdarrenr			if (token != NULL) {
920170268Sdarrenr				error  = nat_iterator(token, &iter);
921170268Sdarrenr			}
922170268Sdarrenr			RWLOCK_EXIT(&ipf_tokens);
923170268Sdarrenr		}
924170268Sdarrenr		SPL_X(s);
925170268Sdarrenr		break;
926170268Sdarrenr	    }
927170268Sdarrenr
928170268Sdarrenr	case SIOCIPFDELTOK :
929170268Sdarrenr		error = BCOPYIN((caddr_t)data, (caddr_t)&arg, sizeof(arg));
930170268Sdarrenr		if (error == 0) {
931170268Sdarrenr			SPL_SCHED(s);
932170268Sdarrenr			error = ipf_deltoken(arg, uid, ctx);
933170268Sdarrenr			SPL_X(s);
934170268Sdarrenr		} else {
935170268Sdarrenr			error = EFAULT;
936170268Sdarrenr		}
937170268Sdarrenr		break;
938170268Sdarrenr
939170268Sdarrenr	case SIOCGTQTAB :
940170268Sdarrenr		error = fr_outobj(data, nat_tqb, IPFOBJ_STATETQTAB);
941170268Sdarrenr		break;
942170268Sdarrenr
943172776Sdarrenr	case SIOCGTABL :
944172776Sdarrenr		error = nat_gettable(data);
945172776Sdarrenr		break;
946172776Sdarrenr
94753642Sguido	default :
94853642Sguido		error = EINVAL;
94953642Sguido		break;
95053642Sguido	}
95160852Sdarrenrdone:
952170268Sdarrenr	if (nt != NULL)
95353642Sguido		KFREE(nt);
95453642Sguido	return error;
95553642Sguido}
95653642Sguido
95753642Sguido
958145522Sdarrenr/* ------------------------------------------------------------------------ */
959145522Sdarrenr/* Function:    nat_siocaddnat                                              */
960145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
961145522Sdarrenr/* Parameters:  n(I)       - pointer to new NAT rule                        */
962145522Sdarrenr/*              np(I)      - pointer to where to insert new NAT rule        */
963145522Sdarrenr/*              getlock(I) - flag indicating if lock on ipf_nat is held     */
964145522Sdarrenr/* Mutex Locks: ipf_natio                                                   */
965145522Sdarrenr/*                                                                          */
966145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
967145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
968145522Sdarrenr/* NAT rule table(s).                                                       */
969145522Sdarrenr/* ------------------------------------------------------------------------ */
970145522Sdarrenrstatic int nat_siocaddnat(n, np, getlock)
971145522Sdarrenripnat_t *n, **np;
972145522Sdarrenrint getlock;
973145522Sdarrenr{
974145522Sdarrenr	int error = 0, i, j;
975145522Sdarrenr
976161356Sguido	if (nat_resolverule(n) != 0)
977161356Sguido		return ENOENT;
978145522Sdarrenr
979145522Sdarrenr	if ((n->in_age[0] == 0) && (n->in_age[1] != 0))
980145522Sdarrenr		return EINVAL;
981145522Sdarrenr
982145522Sdarrenr	n->in_use = 0;
983145522Sdarrenr	if (n->in_redir & NAT_MAPBLK)
984145522Sdarrenr		n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk);
985145522Sdarrenr	else if (n->in_flags & IPN_AUTOPORTMAP)
986145522Sdarrenr		n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk);
987145522Sdarrenr	else if (n->in_flags & IPN_IPRANGE)
988145522Sdarrenr		n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip);
989145522Sdarrenr	else if (n->in_flags & IPN_SPLIT)
990145522Sdarrenr		n->in_space = 2;
991145522Sdarrenr	else if (n->in_outmsk != 0)
992145522Sdarrenr		n->in_space = ~ntohl(n->in_outmsk);
993145522Sdarrenr	else
994145522Sdarrenr		n->in_space = 1;
995145522Sdarrenr
996145522Sdarrenr	/*
997145522Sdarrenr	 * Calculate the number of valid IP addresses in the output
998145522Sdarrenr	 * mapping range.  In all cases, the range is inclusive of
999145522Sdarrenr	 * the start and ending IP addresses.
1000145522Sdarrenr	 * If to a CIDR address, lose 2: broadcast + network address
1001145522Sdarrenr	 *                               (so subtract 1)
1002145522Sdarrenr	 * If to a range, add one.
1003145522Sdarrenr	 * If to a single IP address, set to 1.
1004145522Sdarrenr	 */
1005145522Sdarrenr	if (n->in_space) {
1006145522Sdarrenr		if ((n->in_flags & IPN_IPRANGE) != 0)
1007145522Sdarrenr			n->in_space += 1;
1008145522Sdarrenr		else
1009145522Sdarrenr			n->in_space -= 1;
1010145522Sdarrenr	} else
1011145522Sdarrenr		n->in_space = 1;
1012145522Sdarrenr
1013145522Sdarrenr	if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) &&
1014145522Sdarrenr	    ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0))
1015145522Sdarrenr		n->in_nip = ntohl(n->in_outip) + 1;
1016145522Sdarrenr	else if ((n->in_flags & IPN_SPLIT) &&
1017145522Sdarrenr		 (n->in_redir & NAT_REDIRECT))
1018145522Sdarrenr		n->in_nip = ntohl(n->in_inip);
1019145522Sdarrenr	else
1020145522Sdarrenr		n->in_nip = ntohl(n->in_outip);
1021145522Sdarrenr	if (n->in_redir & NAT_MAP) {
1022145522Sdarrenr		n->in_pnext = ntohs(n->in_pmin);
1023145522Sdarrenr		/*
1024145522Sdarrenr		 * Multiply by the number of ports made available.
1025145522Sdarrenr		 */
1026145522Sdarrenr		if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) {
1027145522Sdarrenr			n->in_space *= (ntohs(n->in_pmax) -
1028145522Sdarrenr					ntohs(n->in_pmin) + 1);
1029145522Sdarrenr			/*
1030145522Sdarrenr			 * Because two different sources can map to
1031145522Sdarrenr			 * different destinations but use the same
1032145522Sdarrenr			 * local IP#/port #.
1033145522Sdarrenr			 * If the result is smaller than in_space, then
1034145522Sdarrenr			 * we may have wrapped around 32bits.
1035145522Sdarrenr			 */
1036145522Sdarrenr			i = n->in_inmsk;
1037145522Sdarrenr			if ((i != 0) && (i != 0xffffffff)) {
1038145522Sdarrenr				j = n->in_space * (~ntohl(i) + 1);
1039145522Sdarrenr				if (j >= n->in_space)
1040145522Sdarrenr					n->in_space = j;
1041145522Sdarrenr				else
1042145522Sdarrenr					n->in_space = 0xffffffff;
1043145522Sdarrenr			}
1044145522Sdarrenr		}
1045145522Sdarrenr		/*
1046145522Sdarrenr		 * If no protocol is specified, multiple by 256 to allow for
1047145522Sdarrenr		 * at least one IP:IP mapping per protocol.
1048145522Sdarrenr		 */
1049145522Sdarrenr		if ((n->in_flags & IPN_TCPUDPICMP) == 0) {
1050145522Sdarrenr				j = n->in_space * 256;
1051145522Sdarrenr				if (j >= n->in_space)
1052145522Sdarrenr					n->in_space = j;
1053145522Sdarrenr				else
1054145522Sdarrenr					n->in_space = 0xffffffff;
1055145522Sdarrenr		}
1056145522Sdarrenr	}
1057145522Sdarrenr
1058145522Sdarrenr	/* Otherwise, these fields are preset */
1059145522Sdarrenr
1060145522Sdarrenr	if (getlock) {
1061145522Sdarrenr		WRITE_ENTER(&ipf_nat);
1062145522Sdarrenr	}
1063145522Sdarrenr	n->in_next = NULL;
1064145522Sdarrenr	*np = n;
1065145522Sdarrenr
1066145522Sdarrenr	if (n->in_age[0] != 0)
1067145522Sdarrenr		n->in_tqehead[0] = fr_addtimeoutqueue(&nat_utqe, n->in_age[0]);
1068145522Sdarrenr
1069145522Sdarrenr	if (n->in_age[1] != 0)
1070145522Sdarrenr		n->in_tqehead[1] = fr_addtimeoutqueue(&nat_utqe, n->in_age[1]);
1071145522Sdarrenr
1072145522Sdarrenr	if (n->in_redir & NAT_REDIRECT) {
1073145522Sdarrenr		n->in_flags &= ~IPN_NOTDST;
1074145522Sdarrenr		nat_addrdr(n);
1075145522Sdarrenr	}
1076145522Sdarrenr	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
1077145522Sdarrenr		n->in_flags &= ~IPN_NOTSRC;
1078145522Sdarrenr		nat_addnat(n);
1079145522Sdarrenr	}
1080170268Sdarrenr	MUTEX_INIT(&n->in_lock, "ipnat rule lock");
1081170268Sdarrenr
1082145522Sdarrenr	n = NULL;
1083145522Sdarrenr	nat_stats.ns_rules++;
1084172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H)
1085145522Sdarrenr	pfil_delayed_copy = 0;
1086145522Sdarrenr#endif
1087145522Sdarrenr	if (getlock) {
1088145522Sdarrenr		RWLOCK_EXIT(&ipf_nat);			/* WRITE */
1089145522Sdarrenr	}
1090145522Sdarrenr
1091145522Sdarrenr	return error;
1092145522Sdarrenr}
1093145522Sdarrenr
1094145522Sdarrenr
1095145522Sdarrenr/* ------------------------------------------------------------------------ */
1096145522Sdarrenr/* Function:    nat_resolvrule                                              */
1097145522Sdarrenr/* Returns:     Nil                                                         */
1098145522Sdarrenr/* Parameters:  n(I)  - pointer to NAT rule                                 */
1099145522Sdarrenr/*                                                                          */
1100145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1101145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1102145522Sdarrenr/* NAT rule table(s).                                                       */
1103145522Sdarrenr/* ------------------------------------------------------------------------ */
1104161356Sguidostatic int nat_resolverule(n)
1105145522Sdarrenripnat_t *n;
1106145522Sdarrenr{
1107145522Sdarrenr	n->in_ifnames[0][LIFNAMSIZ - 1] = '\0';
1108145522Sdarrenr	n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4);
1109145522Sdarrenr
1110145522Sdarrenr	n->in_ifnames[1][LIFNAMSIZ - 1] = '\0';
1111145522Sdarrenr	if (n->in_ifnames[1][0] == '\0') {
1112145522Sdarrenr		(void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ);
1113145522Sdarrenr		n->in_ifps[1] = n->in_ifps[0];
1114145522Sdarrenr	} else {
1115161356Sguido		n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4);
1116145522Sdarrenr	}
1117145522Sdarrenr
1118145522Sdarrenr	if (n->in_plabel[0] != '\0') {
1119145522Sdarrenr		n->in_apr = appr_lookup(n->in_p, n->in_plabel);
1120161356Sguido		if (n->in_apr == NULL)
1121161356Sguido			return -1;
1122145522Sdarrenr	}
1123161356Sguido	return 0;
1124145522Sdarrenr}
1125145522Sdarrenr
1126145522Sdarrenr
1127145522Sdarrenr/* ------------------------------------------------------------------------ */
1128145522Sdarrenr/* Function:    nat_siocdelnat                                              */
1129145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
1130145522Sdarrenr/* Parameters:  n(I)       - pointer to new NAT rule                        */
1131145522Sdarrenr/*              np(I)      - pointer to where to insert new NAT rule        */
1132145522Sdarrenr/*              getlock(I) - flag indicating if lock on ipf_nat is held     */
1133145522Sdarrenr/* Mutex Locks: ipf_natio                                                   */
1134145522Sdarrenr/*                                                                          */
1135145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1136145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1137145522Sdarrenr/* NAT rule table(s).                                                       */
1138145522Sdarrenr/* ------------------------------------------------------------------------ */
1139145522Sdarrenrstatic void nat_siocdelnat(n, np, getlock)
1140145522Sdarrenripnat_t *n, **np;
1141145522Sdarrenrint getlock;
1142145522Sdarrenr{
1143145522Sdarrenr	if (getlock) {
1144145522Sdarrenr		WRITE_ENTER(&ipf_nat);
1145145522Sdarrenr	}
1146145522Sdarrenr	if (n->in_redir & NAT_REDIRECT)
1147145522Sdarrenr		nat_delrdr(n);
1148145522Sdarrenr	if (n->in_redir & (NAT_MAPBLK|NAT_MAP))
1149145522Sdarrenr		nat_delnat(n);
1150145522Sdarrenr	if (nat_list == NULL) {
1151145522Sdarrenr		nat_masks = 0;
1152145522Sdarrenr		rdr_masks = 0;
1153145522Sdarrenr	}
1154145522Sdarrenr
1155145522Sdarrenr	if (n->in_tqehead[0] != NULL) {
1156145522Sdarrenr		if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) {
1157145522Sdarrenr			fr_freetimeoutqueue(n->in_tqehead[1]);
1158145522Sdarrenr		}
1159145522Sdarrenr	}
1160145522Sdarrenr
1161145522Sdarrenr	if (n->in_tqehead[1] != NULL) {
1162145522Sdarrenr		if (fr_deletetimeoutqueue(n->in_tqehead[1]) == 0) {
1163145522Sdarrenr			fr_freetimeoutqueue(n->in_tqehead[1]);
1164145522Sdarrenr		}
1165145522Sdarrenr	}
1166145522Sdarrenr
1167145522Sdarrenr	*np = n->in_next;
1168145522Sdarrenr
1169145522Sdarrenr	if (n->in_use == 0) {
1170145522Sdarrenr		if (n->in_apr)
1171145522Sdarrenr			appr_free(n->in_apr);
1172172776Sdarrenr		MUTEX_DESTROY(&n->in_lock);
1173145522Sdarrenr		KFREE(n);
1174145522Sdarrenr		nat_stats.ns_rules--;
1175172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H)
1176145522Sdarrenr		if (nat_stats.ns_rules == 0)
1177145522Sdarrenr			pfil_delayed_copy = 1;
1178145522Sdarrenr#endif
1179145522Sdarrenr	} else {
1180145522Sdarrenr		n->in_flags |= IPN_DELETE;
1181145522Sdarrenr		n->in_next = NULL;
1182145522Sdarrenr	}
1183145522Sdarrenr	if (getlock) {
1184145522Sdarrenr		RWLOCK_EXIT(&ipf_nat);			/* READ/WRITE */
1185145522Sdarrenr	}
1186145522Sdarrenr}
1187145522Sdarrenr
1188145522Sdarrenr
1189145522Sdarrenr/* ------------------------------------------------------------------------ */
1190145522Sdarrenr/* Function:    fr_natgetsz                                                 */
1191145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1192145522Sdarrenr/* Parameters:  data(I) - pointer to natget structure with kernel pointer   */
1193145522Sdarrenr/*                        get the size of.                                  */
1194145522Sdarrenr/*                                                                          */
1195145522Sdarrenr/* Handle SIOCSTGSZ.                                                        */
1196145522Sdarrenr/* Return the size of the nat list entry to be copied back to user space.   */
1197145522Sdarrenr/* The size of the entry is stored in the ng_sz field and the enture natget */
1198145522Sdarrenr/* structure is copied back to the user.                                    */
1199145522Sdarrenr/* ------------------------------------------------------------------------ */
1200173181Sdarrenrstatic int fr_natgetsz(data, getlock)
120160852Sdarrenrcaddr_t data;
1202173181Sdarrenrint getlock;
120360852Sdarrenr{
120460852Sdarrenr	ap_session_t *aps;
120560852Sdarrenr	nat_t *nat, *n;
120660852Sdarrenr	natget_t ng;
120760852Sdarrenr
1208170268Sdarrenr	if (BCOPYIN(data, &ng, sizeof(ng)) != 0)
1209170268Sdarrenr		return EFAULT;
121060852Sdarrenr
1211173181Sdarrenr	if (getlock) {
1212173181Sdarrenr		READ_ENTER(&ipf_nat);
1213173181Sdarrenr	}
1214173181Sdarrenr
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) {
1223173181Sdarrenr			if (getlock) {
1224173181Sdarrenr				RWLOCK_EXIT(&ipf_nat);
1225173181Sdarrenr			}
1226170268Sdarrenr			if (BCOPYOUT(&ng, data, sizeof(ng)) != 0)
1227170268Sdarrenr				return EFAULT;
1228145522Sdarrenr			return 0;
122960852Sdarrenr		}
123060852Sdarrenr	} else {
123160852Sdarrenr		/*
123260852Sdarrenr		 * Make sure the pointer we're copying from exists in the
123360852Sdarrenr		 * current list of entries.  Security precaution to prevent
123460852Sdarrenr		 * copying of random kernel data.
123560852Sdarrenr		 */
123660852Sdarrenr		for (n = nat_instances; n; n = n->nat_next)
123760852Sdarrenr			if (n == nat)
123860852Sdarrenr				break;
1239173181Sdarrenr		if (n == NULL) {
1240173181Sdarrenr			if (getlock) {
1241173181Sdarrenr				RWLOCK_EXIT(&ipf_nat);
1242173181Sdarrenr			}
124360852Sdarrenr			return ESRCH;
1244173181Sdarrenr		}
124560852Sdarrenr	}
124660852Sdarrenr
1247145522Sdarrenr	/*
1248145522Sdarrenr	 * Incluse any space required for proxy data structures.
1249145522Sdarrenr	 */
125060852Sdarrenr	ng.ng_sz = sizeof(nat_save_t);
125160852Sdarrenr	aps = nat->nat_aps;
1252145522Sdarrenr	if (aps != NULL) {
1253145522Sdarrenr		ng.ng_sz += sizeof(ap_session_t) - 4;
1254145522Sdarrenr		if (aps->aps_data != 0)
1255145522Sdarrenr			ng.ng_sz += aps->aps_psiz;
125660852Sdarrenr	}
1257173181Sdarrenr	if (getlock) {
1258173181Sdarrenr		RWLOCK_EXIT(&ipf_nat);
1259173181Sdarrenr	}
126060852Sdarrenr
1261170268Sdarrenr	if (BCOPYOUT(&ng, data, sizeof(ng)) != 0)
1262170268Sdarrenr		return EFAULT;
1263145522Sdarrenr	return 0;
126460852Sdarrenr}
126560852Sdarrenr
126660852Sdarrenr
1267145522Sdarrenr/* ------------------------------------------------------------------------ */
1268145522Sdarrenr/* Function:    fr_natgetent                                                */
1269145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1270145522Sdarrenr/* Parameters:  data(I) - pointer to natget structure with kernel pointer   */
1271145522Sdarrenr/*                        to NAT structure to copy out.                     */
1272145522Sdarrenr/*                                                                          */
1273145522Sdarrenr/* Handle SIOCSTGET.                                                        */
1274145522Sdarrenr/* Copies out NAT entry to user space.  Any additional data held for a      */
1275145522Sdarrenr/* proxy is also copied, as to is the NAT rule which was responsible for it */
1276145522Sdarrenr/* ------------------------------------------------------------------------ */
1277173181Sdarrenrstatic int fr_natgetent(data, getlock)
127860852Sdarrenrcaddr_t data;
1279173181Sdarrenrint getlock;
128060852Sdarrenr{
1281145522Sdarrenr	int error, outsize;
128260852Sdarrenr	ap_session_t *aps;
1283145522Sdarrenr	nat_save_t *ipn, ipns;
1284145522Sdarrenr	nat_t *n, *nat;
128560852Sdarrenr
1286145522Sdarrenr	error = fr_inobj(data, &ipns, IPFOBJ_NATSAVE);
1287145522Sdarrenr	if (error != 0)
1288145522Sdarrenr		return error;
128960852Sdarrenr
1290145522Sdarrenr	if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920))
1291145522Sdarrenr		return EINVAL;
1292145522Sdarrenr
1293145522Sdarrenr	KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize);
1294145522Sdarrenr	if (ipn == NULL)
1295145522Sdarrenr		return ENOMEM;
1296145522Sdarrenr
1297173181Sdarrenr	if (getlock) {
1298173181Sdarrenr		READ_ENTER(&ipf_nat);
1299173181Sdarrenr	}
1300173181Sdarrenr
1301145522Sdarrenr	ipn->ipn_dsize = ipns.ipn_dsize;
1302145522Sdarrenr	nat = ipns.ipn_next;
1303145522Sdarrenr	if (nat == NULL) {
130460852Sdarrenr		nat = nat_instances;
130560852Sdarrenr		if (nat == NULL) {
130660852Sdarrenr			if (nat_instances == NULL)
1307145522Sdarrenr				error = ENOENT;
1308145522Sdarrenr			goto finished;
130960852Sdarrenr		}
131060852Sdarrenr	} else {
131160852Sdarrenr		/*
131260852Sdarrenr		 * Make sure the pointer we're copying from exists in the
131360852Sdarrenr		 * current list of entries.  Security precaution to prevent
131460852Sdarrenr		 * copying of random kernel data.
131560852Sdarrenr		 */
131660852Sdarrenr		for (n = nat_instances; n; n = n->nat_next)
131760852Sdarrenr			if (n == nat)
131860852Sdarrenr				break;
1319145522Sdarrenr		if (n == NULL) {
1320145522Sdarrenr			error = ESRCH;
1321145522Sdarrenr			goto finished;
1322145522Sdarrenr		}
132360852Sdarrenr	}
1324145522Sdarrenr	ipn->ipn_next = nat->nat_next;
132560852Sdarrenr
1326145522Sdarrenr	/*
1327145522Sdarrenr	 * Copy the NAT structure.
1328145522Sdarrenr	 */
1329145522Sdarrenr	bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat));
133060852Sdarrenr
1331145522Sdarrenr	/*
1332145522Sdarrenr	 * If we have a pointer to the NAT rule it belongs to, save that too.
1333145522Sdarrenr	 */
1334145522Sdarrenr	if (nat->nat_ptr != NULL)
1335145522Sdarrenr		bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat,
1336145522Sdarrenr		      sizeof(ipn->ipn_ipnat));
133760852Sdarrenr
1338145522Sdarrenr	/*
1339145522Sdarrenr	 * If we also know the NAT entry has an associated filter rule,
1340145522Sdarrenr	 * save that too.
1341145522Sdarrenr	 */
1342145522Sdarrenr	if (nat->nat_fr != NULL)
1343145522Sdarrenr		bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr,
1344145522Sdarrenr		      sizeof(ipn->ipn_fr));
134560852Sdarrenr
1346145522Sdarrenr	/*
1347145522Sdarrenr	 * Last but not least, if there is an application proxy session set
1348145522Sdarrenr	 * up for this NAT entry, then copy that out too, including any
1349145522Sdarrenr	 * private data saved along side it by the proxy.
1350145522Sdarrenr	 */
1351145522Sdarrenr	aps = nat->nat_aps;
1352145522Sdarrenr	outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data);
1353145522Sdarrenr	if (aps != NULL) {
1354145522Sdarrenr		char *s;
135560852Sdarrenr
1356145522Sdarrenr		if (outsize < sizeof(*aps)) {
1357145522Sdarrenr			error = ENOBUFS;
1358145522Sdarrenr			goto finished;
135960852Sdarrenr		}
1360145522Sdarrenr
1361145522Sdarrenr		s = ipn->ipn_data;
1362145522Sdarrenr		bcopy((char *)aps, s, sizeof(*aps));
1363145522Sdarrenr		s += sizeof(*aps);
1364145522Sdarrenr		outsize -= sizeof(*aps);
1365145522Sdarrenr		if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz))
1366145522Sdarrenr			bcopy(aps->aps_data, s, aps->aps_psiz);
1367145522Sdarrenr		else
1368145522Sdarrenr			error = ENOBUFS;
136960852Sdarrenr	}
1370145522Sdarrenr	if (error == 0) {
1371173181Sdarrenr		if (getlock) {
1372173181Sdarrenr			RWLOCK_EXIT(&ipf_nat);
1373173181Sdarrenr			getlock = 0;
1374173181Sdarrenr		}
1375145522Sdarrenr		error = fr_outobjsz(data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize);
1376145522Sdarrenr	}
1377145522Sdarrenr
1378145522Sdarrenrfinished:
1379173181Sdarrenr	if (getlock) {
1380173181Sdarrenr		RWLOCK_EXIT(&ipf_nat);
1381173181Sdarrenr	}
1382145522Sdarrenr	if (ipn != NULL) {
1383145522Sdarrenr		KFREES(ipn, ipns.ipn_dsize);
1384145522Sdarrenr	}
138564580Sdarrenr	return error;
138660852Sdarrenr}
138760852Sdarrenr
138860852Sdarrenr
1389145522Sdarrenr/* ------------------------------------------------------------------------ */
1390145522Sdarrenr/* Function:    fr_natputent                                                */
1391145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1392145522Sdarrenr/* Parameters:  data(I) -     pointer to natget structure with NAT          */
1393145522Sdarrenr/*                            structure information to load into the kernel */
1394145522Sdarrenr/*              getlock(I) - flag indicating whether or not a write lock    */
1395145522Sdarrenr/*                           on ipf_nat is already held.                    */
1396145522Sdarrenr/*                                                                          */
1397145522Sdarrenr/* Handle SIOCSTPUT.                                                        */
1398145522Sdarrenr/* Loads a NAT table entry from user space, including a NAT rule, proxy and */
1399145522Sdarrenr/* firewall rule data structures, if pointers to them indicate so.          */
1400145522Sdarrenr/* ------------------------------------------------------------------------ */
1401145522Sdarrenrstatic int fr_natputent(data, getlock)
140260852Sdarrenrcaddr_t data;
1403145522Sdarrenrint getlock;
140460852Sdarrenr{
1405145522Sdarrenr	nat_save_t ipn, *ipnn;
140660852Sdarrenr	ap_session_t *aps;
1407145522Sdarrenr	nat_t *n, *nat;
140860852Sdarrenr	frentry_t *fr;
1409145522Sdarrenr	fr_info_t fin;
141060852Sdarrenr	ipnat_t *in;
141160852Sdarrenr	int error;
141260852Sdarrenr
1413145522Sdarrenr	error = fr_inobj(data, &ipn, IPFOBJ_NATSAVE);
1414145522Sdarrenr	if (error != 0)
1415145522Sdarrenr		return error;
1416145522Sdarrenr
1417145522Sdarrenr	/*
1418145522Sdarrenr	 * Initialise early because of code at junkput label.
1419145522Sdarrenr	 */
1420145522Sdarrenr	in = NULL;
1421145522Sdarrenr	aps = NULL;
142264580Sdarrenr	nat = NULL;
1423145522Sdarrenr	ipnn = NULL;
1424170268Sdarrenr	fr = NULL;
1425145522Sdarrenr
1426145522Sdarrenr	/*
1427145522Sdarrenr	 * New entry, copy in the rest of the NAT entry if it's size is more
1428145522Sdarrenr	 * than just the nat_t structure.
1429145522Sdarrenr	 */
1430145522Sdarrenr	if (ipn.ipn_dsize > sizeof(ipn)) {
1431145522Sdarrenr		if (ipn.ipn_dsize > 81920) {
1432145522Sdarrenr			error = ENOMEM;
1433145522Sdarrenr			goto junkput;
1434145522Sdarrenr		}
1435145522Sdarrenr
1436145522Sdarrenr		KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize);
143760852Sdarrenr		if (ipnn == NULL)
143860852Sdarrenr			return ENOMEM;
1439145522Sdarrenr
1440145522Sdarrenr		error = fr_inobjsz(data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize);
1441145522Sdarrenr		if (error != 0) {
144264580Sdarrenr			error = EFAULT;
144364580Sdarrenr			goto junkput;
144464580Sdarrenr		}
144560852Sdarrenr	} else
1446145522Sdarrenr		ipnn = &ipn;
144760852Sdarrenr
144860852Sdarrenr	KMALLOC(nat, nat_t *);
144964580Sdarrenr	if (nat == NULL) {
1450145522Sdarrenr		error = ENOMEM;
145164580Sdarrenr		goto junkput;
145264580Sdarrenr	}
145360852Sdarrenr
1454145522Sdarrenr	bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat));
145560852Sdarrenr	/*
145660852Sdarrenr	 * Initialize all these so that nat_delete() doesn't cause a crash.
145760852Sdarrenr	 */
1458145522Sdarrenr	bzero((char *)nat, offsetof(struct nat, nat_tqe));
1459145522Sdarrenr	nat->nat_tqe.tqe_pnext = NULL;
1460145522Sdarrenr	nat->nat_tqe.tqe_next = NULL;
1461145522Sdarrenr	nat->nat_tqe.tqe_ifq = NULL;
1462145522Sdarrenr	nat->nat_tqe.tqe_parent = nat;
146360852Sdarrenr
146460852Sdarrenr	/*
146560852Sdarrenr	 * Restore the rule associated with this nat session
146660852Sdarrenr	 */
1467145522Sdarrenr	in = ipnn->ipn_nat.nat_ptr;
1468145522Sdarrenr	if (in != NULL) {
146960852Sdarrenr		KMALLOC(in, ipnat_t *);
1470145522Sdarrenr		nat->nat_ptr = in;
147160852Sdarrenr		if (in == NULL) {
147260852Sdarrenr			error = ENOMEM;
147360852Sdarrenr			goto junkput;
147460852Sdarrenr		}
1475145522Sdarrenr		bzero((char *)in, offsetof(struct ipnat, in_next6));
1476145522Sdarrenr		bcopy((char *)&ipnn->ipn_ipnat, (char *)in, sizeof(*in));
147760852Sdarrenr		in->in_use = 1;
147860852Sdarrenr		in->in_flags |= IPN_DELETE;
1479145522Sdarrenr
1480145522Sdarrenr		ATOMIC_INC(nat_stats.ns_rules);
1481145522Sdarrenr
1482161356Sguido		if (nat_resolverule(in) != 0) {
1483161356Sguido			error = ESRCH;
1484161356Sguido			goto junkput;
1485161356Sguido		}
1486145522Sdarrenr	}
1487145522Sdarrenr
1488145522Sdarrenr	/*
1489145522Sdarrenr	 * Check that the NAT entry doesn't already exist in the kernel.
1490161356Sguido	 *
1491161356Sguido	 * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry.  To do
1492161356Sguido	 * this, we check to see if the inbound combination of addresses and
1493161356Sguido	 * ports is already known.  Similar logic is applied for NAT_INBOUND.
1494161356Sguido	 *
1495145522Sdarrenr	 */
1496145522Sdarrenr	bzero((char *)&fin, sizeof(fin));
1497145522Sdarrenr	fin.fin_p = nat->nat_p;
1498145522Sdarrenr	if (nat->nat_dir == NAT_OUTBOUND) {
1499170268Sdarrenr		fin.fin_ifp = nat->nat_ifps[0];
1500145522Sdarrenr		fin.fin_data[0] = ntohs(nat->nat_oport);
1501145522Sdarrenr		fin.fin_data[1] = ntohs(nat->nat_outport);
1502153876Sguido		if (getlock) {
1503153876Sguido			READ_ENTER(&ipf_nat);
1504153876Sguido		}
1505161356Sguido		n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p,
1506161356Sguido				 nat->nat_oip, nat->nat_inip);
1507153876Sguido		if (getlock) {
1508153876Sguido			RWLOCK_EXIT(&ipf_nat);
1509153876Sguido		}
1510153876Sguido		if (n != NULL) {
1511145522Sdarrenr			error = EEXIST;
1512145522Sdarrenr			goto junkput;
151360852Sdarrenr		}
1514145522Sdarrenr	} else if (nat->nat_dir == NAT_INBOUND) {
1515170268Sdarrenr		fin.fin_ifp = nat->nat_ifps[0];
1516145522Sdarrenr		fin.fin_data[0] = ntohs(nat->nat_outport);
1517145522Sdarrenr		fin.fin_data[1] = ntohs(nat->nat_oport);
1518153876Sguido		if (getlock) {
1519153876Sguido			READ_ENTER(&ipf_nat);
1520153876Sguido		}
1521161356Sguido		n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p,
1522161356Sguido				  nat->nat_outip, nat->nat_oip);
1523153876Sguido		if (getlock) {
1524153876Sguido			RWLOCK_EXIT(&ipf_nat);
1525153876Sguido		}
1526153876Sguido		if (n != NULL) {
1527145522Sdarrenr			error = EEXIST;
1528145522Sdarrenr			goto junkput;
1529145522Sdarrenr		}
1530145522Sdarrenr	} else {
1531145522Sdarrenr		error = EINVAL;
1532145522Sdarrenr		goto junkput;
153360852Sdarrenr	}
153460852Sdarrenr
153560852Sdarrenr	/*
153660852Sdarrenr	 * Restore ap_session_t structure.  Include the private data allocated
153760852Sdarrenr	 * if it was there.
153860852Sdarrenr	 */
1539145522Sdarrenr	aps = nat->nat_aps;
1540145522Sdarrenr	if (aps != NULL) {
154160852Sdarrenr		KMALLOC(aps, ap_session_t *);
1542145522Sdarrenr		nat->nat_aps = aps;
154360852Sdarrenr		if (aps == NULL) {
154460852Sdarrenr			error = ENOMEM;
154560852Sdarrenr			goto junkput;
154660852Sdarrenr		}
154760852Sdarrenr		bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps));
1548145522Sdarrenr		if (in != NULL)
154960852Sdarrenr			aps->aps_apr = in->in_apr;
1550145522Sdarrenr		else
1551145522Sdarrenr			aps->aps_apr = NULL;
1552145522Sdarrenr		if (aps->aps_psiz != 0) {
1553145522Sdarrenr			if (aps->aps_psiz > 81920) {
1554145522Sdarrenr				error = ENOMEM;
1555145522Sdarrenr				goto junkput;
1556145522Sdarrenr			}
155760852Sdarrenr			KMALLOCS(aps->aps_data, void *, aps->aps_psiz);
155860852Sdarrenr			if (aps->aps_data == NULL) {
155960852Sdarrenr				error = ENOMEM;
156060852Sdarrenr				goto junkput;
156160852Sdarrenr			}
156260852Sdarrenr			bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data,
156360852Sdarrenr			      aps->aps_psiz);
156460852Sdarrenr		} else {
156560852Sdarrenr			aps->aps_psiz = 0;
156660852Sdarrenr			aps->aps_data = NULL;
156760852Sdarrenr		}
156860852Sdarrenr	}
156960852Sdarrenr
157060852Sdarrenr	/*
157160852Sdarrenr	 * If there was a filtering rule associated with this entry then
157260852Sdarrenr	 * build up a new one.
157360852Sdarrenr	 */
1574145522Sdarrenr	fr = nat->nat_fr;
157560852Sdarrenr	if (fr != NULL) {
1576145522Sdarrenr		if ((nat->nat_flags & SI_NEWFR) != 0) {
157760852Sdarrenr			KMALLOC(fr, frentry_t *);
157860852Sdarrenr			nat->nat_fr = fr;
157960852Sdarrenr			if (fr == NULL) {
158060852Sdarrenr				error = ENOMEM;
158160852Sdarrenr				goto junkput;
158260852Sdarrenr			}
1583145522Sdarrenr			ipnn->ipn_nat.nat_fr = fr;
1584145522Sdarrenr			fr->fr_ref = 1;
1585145522Sdarrenr			(void) fr_outobj(data, ipnn, IPFOBJ_NATSAVE);
1586145522Sdarrenr			bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr));
1587161356Sguido
1588161356Sguido			fr->fr_ref = 1;
1589161356Sguido			fr->fr_dsize = 0;
1590161356Sguido			fr->fr_data = NULL;
1591161356Sguido			fr->fr_type = FR_T_NONE;
1592161356Sguido
1593145522Sdarrenr			MUTEX_NUKE(&fr->fr_lock);
1594145522Sdarrenr			MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock");
159560852Sdarrenr		} else {
1596153876Sguido			if (getlock) {
1597153876Sguido				READ_ENTER(&ipf_nat);
1598153876Sguido			}
159960852Sdarrenr			for (n = nat_instances; n; n = n->nat_next)
160060852Sdarrenr				if (n->nat_fr == fr)
160160852Sdarrenr					break;
1602145522Sdarrenr
1603145522Sdarrenr			if (n != NULL) {
1604145522Sdarrenr				MUTEX_ENTER(&fr->fr_lock);
1605145522Sdarrenr				fr->fr_ref++;
1606145522Sdarrenr				MUTEX_EXIT(&fr->fr_lock);
1607145522Sdarrenr			}
1608153876Sguido			if (getlock) {
1609153876Sguido				RWLOCK_EXIT(&ipf_nat);
1610153876Sguido			}
1611145522Sdarrenr
161260852Sdarrenr			if (!n) {
161360852Sdarrenr				error = ESRCH;
161460852Sdarrenr				goto junkput;
161560852Sdarrenr			}
161660852Sdarrenr		}
161760852Sdarrenr	}
161860852Sdarrenr
1619145522Sdarrenr	if (ipnn != &ipn) {
1620145522Sdarrenr		KFREES(ipnn, ipn.ipn_dsize);
1621145522Sdarrenr		ipnn = NULL;
1622145522Sdarrenr	}
1623145522Sdarrenr
1624145522Sdarrenr	if (getlock) {
1625145522Sdarrenr		WRITE_ENTER(&ipf_nat);
1626145522Sdarrenr	}
1627145522Sdarrenr	error = nat_insert(nat, nat->nat_rev);
1628145522Sdarrenr	if ((error == 0) && (aps != NULL)) {
1629145522Sdarrenr		aps->aps_next = ap_sess_list;
1630145522Sdarrenr		ap_sess_list = aps;
1631145522Sdarrenr	}
1632145522Sdarrenr	if (getlock) {
1633145522Sdarrenr		RWLOCK_EXIT(&ipf_nat);
1634145522Sdarrenr	}
1635145522Sdarrenr
1636145522Sdarrenr	if (error == 0)
1637145522Sdarrenr		return 0;
1638145522Sdarrenr
1639145522Sdarrenr	error = ENOMEM;
1640145522Sdarrenr
164160852Sdarrenrjunkput:
1642145522Sdarrenr	if (fr != NULL)
1643170268Sdarrenr		(void) fr_derefrule(&fr);
1644145522Sdarrenr
1645145522Sdarrenr	if ((ipnn != NULL) && (ipnn != &ipn)) {
1646145522Sdarrenr		KFREES(ipnn, ipn.ipn_dsize);
1647145522Sdarrenr	}
1648145522Sdarrenr	if (nat != NULL) {
1649145522Sdarrenr		if (aps != NULL) {
1650145522Sdarrenr			if (aps->aps_data != NULL) {
1651145522Sdarrenr				KFREES(aps->aps_data, aps->aps_psiz);
1652145522Sdarrenr			}
1653145522Sdarrenr			KFREE(aps);
1654145522Sdarrenr		}
1655145522Sdarrenr		if (in != NULL) {
1656145522Sdarrenr			if (in->in_apr)
1657145522Sdarrenr				appr_free(in->in_apr);
1658145522Sdarrenr			KFREE(in);
1659145522Sdarrenr		}
1660145522Sdarrenr		KFREE(nat);
1661145522Sdarrenr	}
166260852Sdarrenr	return error;
166360852Sdarrenr}
166460852Sdarrenr
166560852Sdarrenr
1666145522Sdarrenr/* ------------------------------------------------------------------------ */
1667145522Sdarrenr/* Function:    nat_delete                                                  */
1668145522Sdarrenr/* Returns:     Nil                                                         */
1669145522Sdarrenr/* Parameters:  natd(I)    - pointer to NAT structure to delete             */
1670145522Sdarrenr/*              logtype(I) - type of LOG record to create before deleting   */
1671145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
1672145522Sdarrenr/*                                                                          */
1673145522Sdarrenr/* Delete a nat entry from the various lists and table.  If NAT logging is  */
1674145522Sdarrenr/* enabled then generate a NAT log record for this event.                   */
1675145522Sdarrenr/* ------------------------------------------------------------------------ */
1676172776Sdarrenrvoid nat_delete(nat, logtype)
1677145522Sdarrenrstruct nat *nat;
1678145522Sdarrenrint logtype;
167953642Sguido{
168053642Sguido	struct ipnat *ipn;
1681172776Sdarrenr	int removed = 0;
168253642Sguido
1683145522Sdarrenr	if (logtype != 0 && nat_logging != 0)
1684145522Sdarrenr		nat_log(nat, logtype);
1685180778Sdarrenr#if defined(NEED_LOCAL_RAND) && defined(_KERNEL)
1686180778Sdarrenr	ipf_rand_push(nat, sizeof(*nat));
1687180778Sdarrenr#endif
168853642Sguido
1689145522Sdarrenr	/*
1690145522Sdarrenr	 * Take it as a general indication that all the pointers are set if
1691145522Sdarrenr	 * nat_pnext is set.
1692145522Sdarrenr	 */
1693145522Sdarrenr	if (nat->nat_pnext != NULL) {
1694172776Sdarrenr		removed = 1;
1695172776Sdarrenr
1696145522Sdarrenr		nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
1697145522Sdarrenr		nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
1698145522Sdarrenr
1699145522Sdarrenr		*nat->nat_pnext = nat->nat_next;
1700145522Sdarrenr		if (nat->nat_next != NULL) {
1701145522Sdarrenr			nat->nat_next->nat_pnext = nat->nat_pnext;
1702145522Sdarrenr			nat->nat_next = NULL;
1703145522Sdarrenr		}
1704145522Sdarrenr		nat->nat_pnext = NULL;
1705145522Sdarrenr
1706145522Sdarrenr		*nat->nat_phnext[0] = nat->nat_hnext[0];
1707145522Sdarrenr		if (nat->nat_hnext[0] != NULL) {
1708145522Sdarrenr			nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
1709145522Sdarrenr			nat->nat_hnext[0] = NULL;
1710145522Sdarrenr		}
1711145522Sdarrenr		nat->nat_phnext[0] = NULL;
1712145522Sdarrenr
1713145522Sdarrenr		*nat->nat_phnext[1] = nat->nat_hnext[1];
1714145522Sdarrenr		if (nat->nat_hnext[1] != NULL) {
1715145522Sdarrenr			nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
1716145522Sdarrenr			nat->nat_hnext[1] = NULL;
1717145522Sdarrenr		}
1718145522Sdarrenr		nat->nat_phnext[1] = NULL;
1719145522Sdarrenr
1720145522Sdarrenr		if ((nat->nat_flags & SI_WILDP) != 0)
1721145522Sdarrenr			nat_stats.ns_wilds--;
172253642Sguido	}
172360852Sdarrenr
1724145522Sdarrenr	if (nat->nat_me != NULL) {
1725145522Sdarrenr		*nat->nat_me = NULL;
1726145522Sdarrenr		nat->nat_me = NULL;
1727145522Sdarrenr	}
172860852Sdarrenr
1729170268Sdarrenr	if (nat->nat_tqe.tqe_ifq != NULL)
1730170268Sdarrenr		fr_deletequeueentry(&nat->nat_tqe);
1731145522Sdarrenr
1732170268Sdarrenr	if (logtype == NL_EXPIRE)
1733170268Sdarrenr		nat_stats.ns_expire++;
1734170268Sdarrenr
1735172776Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
1736172776Sdarrenr	/*
1737172776Sdarrenr	 * NL_DESTROY should only be passed in when we've got nat_ref >= 2.
1738172776Sdarrenr	 * This happens when a nat'd packet is blocked and we want to throw
1739172776Sdarrenr	 * away the NAT session.
1740172776Sdarrenr	 */
1741172776Sdarrenr	if (logtype == NL_DESTROY) {
1742172776Sdarrenr		if (nat->nat_ref > 2) {
1743172776Sdarrenr			nat->nat_ref -= 2;
1744172776Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
1745172776Sdarrenr			if (removed)
1746172776Sdarrenr				nat_stats.ns_orphans++;
1747172776Sdarrenr			return;
1748172776Sdarrenr		}
1749172776Sdarrenr	} else if (nat->nat_ref > 1) {
1750172776Sdarrenr		nat->nat_ref--;
1751172776Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
1752172776Sdarrenr		if (removed)
1753172776Sdarrenr			nat_stats.ns_orphans++;
1754145522Sdarrenr		return;
1755145522Sdarrenr	}
1756172776Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
1757170268Sdarrenr
1758161356Sguido	/*
1759172776Sdarrenr	 * At this point, nat_ref is 1, doing "--" would make it 0..
1760161356Sguido	 */
1761172776Sdarrenr	nat->nat_ref = 0;
1762172776Sdarrenr	if (!removed)
1763172776Sdarrenr		nat_stats.ns_orphans--;
1764145522Sdarrenr
1765145522Sdarrenr#ifdef	IPFILTER_SYNC
1766145522Sdarrenr	if (nat->nat_sync)
1767145522Sdarrenr		ipfsync_del(nat->nat_sync);
1768145522Sdarrenr#endif
1769145522Sdarrenr
1770145522Sdarrenr	if (nat->nat_fr != NULL)
1771170268Sdarrenr		(void) fr_derefrule(&nat->nat_fr);
1772145522Sdarrenr
1773145522Sdarrenr	if (nat->nat_hm != NULL)
1774170268Sdarrenr		fr_hostmapdel(&nat->nat_hm);
1775145522Sdarrenr
177653642Sguido	/*
177753642Sguido	 * If there is an active reference from the nat entry to its parent
177853642Sguido	 * rule, decrement the rule's reference count and free it too if no
177953642Sguido	 * longer being used.
178053642Sguido	 */
1781145522Sdarrenr	ipn = nat->nat_ptr;
178253642Sguido	if (ipn != NULL) {
1783170268Sdarrenr		fr_ipnatderef(&ipn);
178453642Sguido	}
178553642Sguido
1786145522Sdarrenr	MUTEX_DESTROY(&nat->nat_lock);
1787145522Sdarrenr
1788145522Sdarrenr	aps_free(nat->nat_aps);
1789145522Sdarrenr	nat_stats.ns_inuse--;
1790145522Sdarrenr
179153642Sguido	/*
179253642Sguido	 * If there's a fragment table entry too for this nat entry, then
1793145522Sdarrenr	 * dereference that as well.  This is after nat_lock is released
1794145522Sdarrenr	 * because of Tru64.
179553642Sguido	 */
1796145522Sdarrenr	fr_forgetnat((void *)nat);
1797145522Sdarrenr
1798145522Sdarrenr	KFREE(nat);
179953642Sguido}
180053642Sguido
180153642Sguido
1802145522Sdarrenr/* ------------------------------------------------------------------------ */
1803145522Sdarrenr/* Function:    nat_flushtable                                              */
1804145522Sdarrenr/* Returns:     int - number of NAT rules deleted                           */
1805145522Sdarrenr/* Parameters:  Nil                                                         */
1806145522Sdarrenr/*                                                                          */
1807145522Sdarrenr/* Deletes all currently active NAT sessions.  In deleting each NAT entry a */
1808145522Sdarrenr/* log record should be emitted in nat_delete() if NAT logging is enabled.  */
1809145522Sdarrenr/* ------------------------------------------------------------------------ */
181053642Sguido/*
181153642Sguido * nat_flushtable - clear the NAT table of all mapping entries.
181253642Sguido */
181353642Sguidostatic int nat_flushtable()
181453642Sguido{
1815145522Sdarrenr	nat_t *nat;
1816145522Sdarrenr	int j = 0;
181767614Sdarrenr
181853642Sguido	/*
181953642Sguido	 * ALL NAT mappings deleted, so lets just make the deletions
182053642Sguido	 * quicker.
182153642Sguido	 */
182253642Sguido	if (nat_table[0] != NULL)
182353642Sguido		bzero((char *)nat_table[0],
182453642Sguido		      sizeof(nat_table[0]) * ipf_nattable_sz);
182553642Sguido	if (nat_table[1] != NULL)
182653642Sguido		bzero((char *)nat_table[1],
182753642Sguido		      sizeof(nat_table[1]) * ipf_nattable_sz);
182853642Sguido
1829145522Sdarrenr	while ((nat = nat_instances) != NULL) {
1830145522Sdarrenr		nat_delete(nat, NL_FLUSH);
183153642Sguido		j++;
183253642Sguido	}
1833145522Sdarrenr
183453642Sguido	nat_stats.ns_inuse = 0;
183553642Sguido	return j;
183653642Sguido}
183753642Sguido
183853642Sguido
1839145522Sdarrenr/* ------------------------------------------------------------------------ */
1840145522Sdarrenr/* Function:    nat_clearlist                                               */
1841145522Sdarrenr/* Returns:     int - number of NAT/RDR rules deleted                       */
1842145522Sdarrenr/* Parameters:  Nil                                                         */
1843145522Sdarrenr/*                                                                          */
1844145522Sdarrenr/* Delete all rules in the current list of rules.  There is nothing elegant */
1845145522Sdarrenr/* about this cleanup: simply free all entries on the list of rules and     */
1846145522Sdarrenr/* clear out the tables used for hashed NAT rule lookups.                   */
1847145522Sdarrenr/* ------------------------------------------------------------------------ */
1848145522Sdarrenrstatic int nat_clearlist()
184953642Sguido{
1850145522Sdarrenr	ipnat_t *n, **np = &nat_list;
185153642Sguido	int i = 0;
185253642Sguido
185353642Sguido	if (nat_rules != NULL)
185453642Sguido		bzero((char *)nat_rules, sizeof(*nat_rules) * ipf_natrules_sz);
185553642Sguido	if (rdr_rules != NULL)
185653642Sguido		bzero((char *)rdr_rules, sizeof(*rdr_rules) * ipf_rdrrules_sz);
185753642Sguido
1858145522Sdarrenr	while ((n = *np) != NULL) {
185953642Sguido		*np = n->in_next;
1860145522Sdarrenr		if (n->in_use == 0) {
1861145522Sdarrenr			if (n->in_apr != NULL)
186253642Sguido				appr_free(n->in_apr);
1863172776Sdarrenr			MUTEX_DESTROY(&n->in_lock);
186453642Sguido			KFREE(n);
186553642Sguido			nat_stats.ns_rules--;
186653642Sguido		} else {
186753642Sguido			n->in_flags |= IPN_DELETE;
186853642Sguido			n->in_next = NULL;
186953642Sguido		}
187053642Sguido		i++;
187153642Sguido	}
1872172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H)
1873145522Sdarrenr	pfil_delayed_copy = 1;
1874145522Sdarrenr#endif
187553642Sguido	nat_masks = 0;
187653642Sguido	rdr_masks = 0;
187753642Sguido	return i;
187853642Sguido}
187953642Sguido
188053642Sguido
1881145522Sdarrenr/* ------------------------------------------------------------------------ */
1882145522Sdarrenr/* Function:    nat_newmap                                                  */
1883145522Sdarrenr/* Returns:     int - -1 == error, 0 == success                             */
1884145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
1885145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
1886145522Sdarrenr/*              ni(I)  - pointer to structure with misc. information needed */
1887145522Sdarrenr/*                       to create new NAT entry.                           */
1888145522Sdarrenr/*                                                                          */
1889145522Sdarrenr/* Given an empty NAT structure, populate it with new information about a   */
1890145522Sdarrenr/* new NAT session, as defined by the matching NAT rule.                    */
1891145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
1892145522Sdarrenr/* to the new IP address for the translation.                               */
1893145522Sdarrenr/* ------------------------------------------------------------------------ */
1894145522Sdarrenrstatic INLINE int nat_newmap(fin, nat, ni)
189592685Sdarrenrfr_info_t *fin;
1896145522Sdarrenrnat_t *nat;
1897145522Sdarrenrnatinfo_t *ni;
1898145522Sdarrenr{
1899145522Sdarrenr	u_short st_port, dport, sport, port, sp, dp;
1900145522Sdarrenr	struct in_addr in, inb;
1901145522Sdarrenr	hostmap_t *hm;
1902145522Sdarrenr	u_32_t flags;
1903145522Sdarrenr	u_32_t st_ip;
1904145522Sdarrenr	ipnat_t *np;
1905145522Sdarrenr	nat_t *natl;
1906145522Sdarrenr	int l;
1907145522Sdarrenr
1908145522Sdarrenr	/*
1909145522Sdarrenr	 * If it's an outbound packet which doesn't match any existing
1910145522Sdarrenr	 * record, then create a new port
1911145522Sdarrenr	 */
1912145522Sdarrenr	l = 0;
1913145522Sdarrenr	hm = NULL;
1914145522Sdarrenr	np = ni->nai_np;
1915145522Sdarrenr	st_ip = np->in_nip;
1916145522Sdarrenr	st_port = np->in_pnext;
1917145522Sdarrenr	flags = ni->nai_flags;
1918145522Sdarrenr	sport = ni->nai_sport;
1919145522Sdarrenr	dport = ni->nai_dport;
1920145522Sdarrenr
1921145522Sdarrenr	/*
1922145522Sdarrenr	 * Do a loop until we either run out of entries to try or we find
1923145522Sdarrenr	 * a NAT mapping that isn't currently being used.  This is done
1924145522Sdarrenr	 * because the change to the source is not (usually) being fixed.
1925145522Sdarrenr	 */
1926145522Sdarrenr	do {
1927145522Sdarrenr		port = 0;
1928145522Sdarrenr		in.s_addr = htonl(np->in_nip);
1929145522Sdarrenr		if (l == 0) {
1930145522Sdarrenr			/*
1931145522Sdarrenr			 * Check to see if there is an existing NAT
1932145522Sdarrenr			 * setup for this IP address pair.
1933145522Sdarrenr			 */
1934145522Sdarrenr			hm = nat_hostmap(np, fin->fin_src, fin->fin_dst,
1935145522Sdarrenr					 in, 0);
1936145522Sdarrenr			if (hm != NULL)
1937145522Sdarrenr				in.s_addr = hm->hm_mapip.s_addr;
1938145522Sdarrenr		} else if ((l == 1) && (hm != NULL)) {
1939170268Sdarrenr			fr_hostmapdel(&hm);
1940145522Sdarrenr		}
1941145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
1942145522Sdarrenr
1943145522Sdarrenr		nat->nat_hm = hm;
1944145522Sdarrenr
1945145522Sdarrenr		if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) {
1946145522Sdarrenr			if (l > 0)
1947145522Sdarrenr				return -1;
1948145522Sdarrenr		}
1949145522Sdarrenr
1950145522Sdarrenr		if (np->in_redir == NAT_BIMAP &&
1951145522Sdarrenr		    np->in_inmsk == np->in_outmsk) {
1952145522Sdarrenr			/*
1953145522Sdarrenr			 * map the address block in a 1:1 fashion
1954145522Sdarrenr			 */
1955145522Sdarrenr			in.s_addr = np->in_outip;
1956145522Sdarrenr			in.s_addr |= fin->fin_saddr & ~np->in_inmsk;
1957145522Sdarrenr			in.s_addr = ntohl(in.s_addr);
1958145522Sdarrenr
1959145522Sdarrenr		} else if (np->in_redir & NAT_MAPBLK) {
1960145522Sdarrenr			if ((l >= np->in_ppip) || ((l > 0) &&
1961145522Sdarrenr			     !(flags & IPN_TCPUDP)))
1962145522Sdarrenr				return -1;
1963145522Sdarrenr			/*
1964145522Sdarrenr			 * map-block - Calculate destination address.
1965145522Sdarrenr			 */
1966145522Sdarrenr			in.s_addr = ntohl(fin->fin_saddr);
1967145522Sdarrenr			in.s_addr &= ntohl(~np->in_inmsk);
1968145522Sdarrenr			inb.s_addr = in.s_addr;
1969145522Sdarrenr			in.s_addr /= np->in_ippip;
1970145522Sdarrenr			in.s_addr &= ntohl(~np->in_outmsk);
1971145522Sdarrenr			in.s_addr += ntohl(np->in_outip);
1972145522Sdarrenr			/*
1973145522Sdarrenr			 * Calculate destination port.
1974145522Sdarrenr			 */
1975145522Sdarrenr			if ((flags & IPN_TCPUDP) &&
1976145522Sdarrenr			    (np->in_ppip != 0)) {
1977145522Sdarrenr				port = ntohs(sport) + l;
1978145522Sdarrenr				port %= np->in_ppip;
1979145522Sdarrenr				port += np->in_ppip *
1980145522Sdarrenr					(inb.s_addr % np->in_ippip);
1981145522Sdarrenr				port += MAPBLK_MINPORT;
1982145522Sdarrenr				port = htons(port);
1983145522Sdarrenr			}
1984145522Sdarrenr
1985145522Sdarrenr		} else if ((np->in_outip == 0) &&
1986145522Sdarrenr			   (np->in_outmsk == 0xffffffff)) {
1987145522Sdarrenr			/*
1988145522Sdarrenr			 * 0/32 - use the interface's IP address.
1989145522Sdarrenr			 */
1990145522Sdarrenr			if ((l > 0) ||
1991145522Sdarrenr			    fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp,
1992145522Sdarrenr				       &in, NULL) == -1)
1993145522Sdarrenr				return -1;
1994145522Sdarrenr			in.s_addr = ntohl(in.s_addr);
1995145522Sdarrenr
1996145522Sdarrenr		} else if ((np->in_outip == 0) && (np->in_outmsk == 0)) {
1997145522Sdarrenr			/*
1998145522Sdarrenr			 * 0/0 - use the original source address/port.
1999145522Sdarrenr			 */
2000145522Sdarrenr			if (l > 0)
2001145522Sdarrenr				return -1;
2002145522Sdarrenr			in.s_addr = ntohl(fin->fin_saddr);
2003145522Sdarrenr
2004145522Sdarrenr		} else if ((np->in_outmsk != 0xffffffff) &&
2005145522Sdarrenr			   (np->in_pnext == 0) && ((l > 0) || (hm == NULL)))
2006145522Sdarrenr			np->in_nip++;
2007145522Sdarrenr
2008145522Sdarrenr		natl = NULL;
2009145522Sdarrenr
2010145522Sdarrenr		if ((flags & IPN_TCPUDP) &&
2011145522Sdarrenr		    ((np->in_redir & NAT_MAPBLK) == 0) &&
2012145522Sdarrenr		    (np->in_flags & IPN_AUTOPORTMAP)) {
2013145522Sdarrenr			/*
2014145522Sdarrenr			 * "ports auto" (without map-block)
2015145522Sdarrenr			 */
2016145522Sdarrenr			if ((l > 0) && (l % np->in_ppip == 0)) {
2017145522Sdarrenr				if (l > np->in_space) {
2018145522Sdarrenr					return -1;
2019145522Sdarrenr				} else if ((l > np->in_ppip) &&
2020145522Sdarrenr					   np->in_outmsk != 0xffffffff)
2021145522Sdarrenr					np->in_nip++;
2022145522Sdarrenr			}
2023145522Sdarrenr			if (np->in_ppip != 0) {
2024145522Sdarrenr				port = ntohs(sport);
2025145522Sdarrenr				port += (l % np->in_ppip);
2026145522Sdarrenr				port %= np->in_ppip;
2027145522Sdarrenr				port += np->in_ppip *
2028145522Sdarrenr					(ntohl(fin->fin_saddr) %
2029145522Sdarrenr					 np->in_ippip);
2030145522Sdarrenr				port += MAPBLK_MINPORT;
2031145522Sdarrenr				port = htons(port);
2032145522Sdarrenr			}
2033145522Sdarrenr
2034145522Sdarrenr		} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
2035145522Sdarrenr			   (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) {
2036145522Sdarrenr			/*
2037145522Sdarrenr			 * Standard port translation.  Select next port.
2038145522Sdarrenr			 */
2039180778Sdarrenr			if (np->in_flags & IPN_SEQUENTIAL) {
2040180832Sdarrenr				port = np->in_pnext;
2041180778Sdarrenr			} else {
2042180778Sdarrenr				port = ipf_random() % (ntohs(np->in_pmax) -
2043180778Sdarrenr						       ntohs(np->in_pmin));
2044180832Sdarrenr				port += ntohs(np->in_pmin);
2045180778Sdarrenr			}
2046180832Sdarrenr			port = htons(port);
2047180778Sdarrenr			np->in_pnext++;
2048145522Sdarrenr
2049145522Sdarrenr			if (np->in_pnext > ntohs(np->in_pmax)) {
2050145522Sdarrenr				np->in_pnext = ntohs(np->in_pmin);
2051145522Sdarrenr				if (np->in_outmsk != 0xffffffff)
2052145522Sdarrenr					np->in_nip++;
2053145522Sdarrenr			}
2054145522Sdarrenr		}
2055145522Sdarrenr
2056145522Sdarrenr		if (np->in_flags & IPN_IPRANGE) {
2057145522Sdarrenr			if (np->in_nip > ntohl(np->in_outmsk))
2058145522Sdarrenr				np->in_nip = ntohl(np->in_outip);
2059145522Sdarrenr		} else {
2060145522Sdarrenr			if ((np->in_outmsk != 0xffffffff) &&
2061145522Sdarrenr			    ((np->in_nip + 1) & ntohl(np->in_outmsk)) >
2062145522Sdarrenr			    ntohl(np->in_outip))
2063145522Sdarrenr				np->in_nip = ntohl(np->in_outip) + 1;
2064145522Sdarrenr		}
2065145522Sdarrenr
2066145522Sdarrenr		if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
2067145522Sdarrenr			port = sport;
2068145522Sdarrenr
2069145522Sdarrenr		/*
2070145522Sdarrenr		 * Here we do a lookup of the connection as seen from
2071145522Sdarrenr		 * the outside.  If an IP# pair already exists, try
2072145522Sdarrenr		 * again.  So if you have A->B becomes C->B, you can
2073145522Sdarrenr		 * also have D->E become C->E but not D->B causing
2074145522Sdarrenr		 * another C->B.  Also take protocol and ports into
2075145522Sdarrenr		 * account when determining whether a pre-existing
2076145522Sdarrenr		 * NAT setup will cause an external conflict where
2077145522Sdarrenr		 * this is appropriate.
2078145522Sdarrenr		 */
2079145522Sdarrenr		inb.s_addr = htonl(in.s_addr);
2080145522Sdarrenr		sp = fin->fin_data[0];
2081145522Sdarrenr		dp = fin->fin_data[1];
2082145522Sdarrenr		fin->fin_data[0] = fin->fin_data[1];
2083145522Sdarrenr		fin->fin_data[1] = htons(port);
2084145522Sdarrenr		natl = nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
2085145522Sdarrenr				    (u_int)fin->fin_p, fin->fin_dst, inb);
2086145522Sdarrenr		fin->fin_data[0] = sp;
2087145522Sdarrenr		fin->fin_data[1] = dp;
2088145522Sdarrenr
2089145522Sdarrenr		/*
2090145522Sdarrenr		 * Has the search wrapped around and come back to the
2091145522Sdarrenr		 * start ?
2092145522Sdarrenr		 */
2093145522Sdarrenr		if ((natl != NULL) &&
2094145522Sdarrenr		    (np->in_pnext != 0) && (st_port == np->in_pnext) &&
2095145522Sdarrenr		    (np->in_nip != 0) && (st_ip == np->in_nip))
2096145522Sdarrenr			return -1;
2097145522Sdarrenr		l++;
2098145522Sdarrenr	} while (natl != NULL);
2099145522Sdarrenr
2100145522Sdarrenr	if (np->in_space > 0)
2101145522Sdarrenr		np->in_space--;
2102145522Sdarrenr
2103145522Sdarrenr	/* Setup the NAT table */
2104145522Sdarrenr	nat->nat_inip = fin->fin_src;
2105145522Sdarrenr	nat->nat_outip.s_addr = htonl(in.s_addr);
2106145522Sdarrenr	nat->nat_oip = fin->fin_dst;
2107145522Sdarrenr	if (nat->nat_hm == NULL)
2108145522Sdarrenr		nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst,
2109145522Sdarrenr					  nat->nat_outip, 0);
2110145522Sdarrenr
2111145522Sdarrenr	/*
2112145522Sdarrenr	 * The ICMP checksum does not have a pseudo header containing
2113145522Sdarrenr	 * the IP addresses
2114145522Sdarrenr	 */
2115145522Sdarrenr	ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr));
2116145522Sdarrenr	ni->nai_sum2 = LONG_SUM(in.s_addr);
2117145522Sdarrenr	if ((flags & IPN_TCPUDP)) {
2118145522Sdarrenr		ni->nai_sum1 += ntohs(sport);
2119145522Sdarrenr		ni->nai_sum2 += ntohs(port);
2120145522Sdarrenr	}
2121145522Sdarrenr
2122145522Sdarrenr	if (flags & IPN_TCPUDP) {
2123145522Sdarrenr		nat->nat_inport = sport;
2124145522Sdarrenr		nat->nat_outport = port;	/* sport */
2125145522Sdarrenr		nat->nat_oport = dport;
2126145522Sdarrenr		((tcphdr_t *)fin->fin_dp)->th_sport = port;
2127145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
2128145522Sdarrenr		((icmphdr_t *)fin->fin_dp)->icmp_id = port;
2129145522Sdarrenr		nat->nat_inport = port;
2130145522Sdarrenr		nat->nat_outport = port;
2131145522Sdarrenr	} else if (fin->fin_p == IPPROTO_GRE) {
2132145522Sdarrenr#if 0
2133145522Sdarrenr		nat->nat_gre.gs_flags = ((grehdr_t *)fin->fin_dp)->gr_flags;
2134145522Sdarrenr		if (GRE_REV(nat->nat_gre.gs_flags) == 1) {
2135145522Sdarrenr			nat->nat_oport = 0;/*fin->fin_data[1];*/
2136145522Sdarrenr			nat->nat_inport = 0;/*fin->fin_data[0];*/
2137145522Sdarrenr			nat->nat_outport = 0;/*fin->fin_data[0];*/
2138145522Sdarrenr			nat->nat_call[0] = fin->fin_data[0];
2139145522Sdarrenr			nat->nat_call[1] = fin->fin_data[0];
2140145522Sdarrenr		}
2141145522Sdarrenr#endif
2142145522Sdarrenr	}
2143145522Sdarrenr	ni->nai_ip.s_addr = in.s_addr;
2144145522Sdarrenr	ni->nai_port = port;
2145145522Sdarrenr	ni->nai_nport = dport;
2146145522Sdarrenr	return 0;
2147145522Sdarrenr}
2148145522Sdarrenr
2149145522Sdarrenr
2150145522Sdarrenr/* ------------------------------------------------------------------------ */
2151145522Sdarrenr/* Function:    nat_newrdr                                                  */
2152145522Sdarrenr/* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
2153145522Sdarrenr/*                    allow rule to be moved if IPN_ROUNDR is set.          */
2154145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
2155145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
2156145522Sdarrenr/*              ni(I)  - pointer to structure with misc. information needed */
2157145522Sdarrenr/*                       to create new NAT entry.                           */
2158145522Sdarrenr/*                                                                          */
2159145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
2160145522Sdarrenr/* to the new IP address for the translation.                               */
2161145522Sdarrenr/* ------------------------------------------------------------------------ */
2162145522Sdarrenrstatic INLINE int nat_newrdr(fin, nat, ni)
2163145522Sdarrenrfr_info_t *fin;
2164145522Sdarrenrnat_t *nat;
2165145522Sdarrenrnatinfo_t *ni;
2166145522Sdarrenr{
2167145522Sdarrenr	u_short nport, dport, sport;
2168170268Sdarrenr	struct in_addr in, inb;
2169170268Sdarrenr	u_short sp, dp;
2170145522Sdarrenr	hostmap_t *hm;
2171145522Sdarrenr	u_32_t flags;
2172145522Sdarrenr	ipnat_t *np;
2173170268Sdarrenr	nat_t *natl;
2174145522Sdarrenr	int move;
2175145522Sdarrenr
2176145522Sdarrenr	move = 1;
2177145522Sdarrenr	hm = NULL;
2178145522Sdarrenr	in.s_addr = 0;
2179145522Sdarrenr	np = ni->nai_np;
2180145522Sdarrenr	flags = ni->nai_flags;
2181145522Sdarrenr	sport = ni->nai_sport;
2182145522Sdarrenr	dport = ni->nai_dport;
2183145522Sdarrenr
2184145522Sdarrenr	/*
2185145522Sdarrenr	 * If the matching rule has IPN_STICKY set, then we want to have the
2186145522Sdarrenr	 * same rule kick in as before.  Why would this happen?  If you have
2187145522Sdarrenr	 * a collection of rdr rules with "round-robin sticky", the current
2188145522Sdarrenr	 * packet might match a different one to the previous connection but
2189145522Sdarrenr	 * we want the same destination to be used.
2190145522Sdarrenr	 */
2191153876Sguido	if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) &&
2192153876Sguido	    ((np->in_flags & IPN_STICKY) != 0)) {
2193145522Sdarrenr		hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, in,
2194145522Sdarrenr				 (u_32_t)dport);
2195145522Sdarrenr		if (hm != NULL) {
2196145522Sdarrenr			in.s_addr = ntohl(hm->hm_mapip.s_addr);
2197145522Sdarrenr			np = hm->hm_ipnat;
2198145522Sdarrenr			ni->nai_np = np;
2199145522Sdarrenr			move = 0;
2200145522Sdarrenr		}
2201145522Sdarrenr	}
2202145522Sdarrenr
2203145522Sdarrenr	/*
2204145522Sdarrenr	 * Otherwise, it's an inbound packet. Most likely, we don't
2205145522Sdarrenr	 * want to rewrite source ports and source addresses. Instead,
2206145522Sdarrenr	 * we want to rewrite to a fixed internal address and fixed
2207145522Sdarrenr	 * internal port.
2208145522Sdarrenr	 */
2209145522Sdarrenr	if (np->in_flags & IPN_SPLIT) {
2210145522Sdarrenr		in.s_addr = np->in_nip;
2211145522Sdarrenr
2212145522Sdarrenr		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
2213153876Sguido			hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst,
2214145522Sdarrenr					 in, (u_32_t)dport);
2215145522Sdarrenr			if (hm != NULL) {
2216145522Sdarrenr				in.s_addr = hm->hm_mapip.s_addr;
2217145522Sdarrenr				move = 0;
2218145522Sdarrenr			}
2219145522Sdarrenr		}
2220145522Sdarrenr
2221145522Sdarrenr		if (hm == NULL || hm->hm_ref == 1) {
2222145522Sdarrenr			if (np->in_inip == htonl(in.s_addr)) {
2223145522Sdarrenr				np->in_nip = ntohl(np->in_inmsk);
2224145522Sdarrenr				move = 0;
2225145522Sdarrenr			} else {
2226145522Sdarrenr				np->in_nip = ntohl(np->in_inip);
2227145522Sdarrenr			}
2228145522Sdarrenr		}
2229145522Sdarrenr
2230145522Sdarrenr	} else if ((np->in_inip == 0) && (np->in_inmsk == 0xffffffff)) {
2231145522Sdarrenr		/*
2232145522Sdarrenr		 * 0/32 - use the interface's IP address.
2233145522Sdarrenr		 */
2234145522Sdarrenr		if (fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, &in, NULL) == -1)
2235145522Sdarrenr			return -1;
2236145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
2237145522Sdarrenr
2238145522Sdarrenr	} else if ((np->in_inip == 0) && (np->in_inmsk== 0)) {
2239145522Sdarrenr		/*
2240145522Sdarrenr		 * 0/0 - use the original destination address/port.
2241145522Sdarrenr		 */
2242145522Sdarrenr		in.s_addr = ntohl(fin->fin_daddr);
2243145522Sdarrenr
2244145522Sdarrenr	} else if (np->in_redir == NAT_BIMAP &&
2245145522Sdarrenr		   np->in_inmsk == np->in_outmsk) {
2246145522Sdarrenr		/*
2247145522Sdarrenr		 * map the address block in a 1:1 fashion
2248145522Sdarrenr		 */
2249145522Sdarrenr		in.s_addr = np->in_inip;
2250145522Sdarrenr		in.s_addr |= fin->fin_daddr & ~np->in_inmsk;
2251145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
2252145522Sdarrenr	} else {
2253145522Sdarrenr		in.s_addr = ntohl(np->in_inip);
2254145522Sdarrenr	}
2255145522Sdarrenr
2256145522Sdarrenr	if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
2257145522Sdarrenr		nport = dport;
2258145522Sdarrenr	else {
2259145522Sdarrenr		/*
2260145522Sdarrenr		 * Whilst not optimized for the case where
2261145522Sdarrenr		 * pmin == pmax, the gain is not significant.
2262145522Sdarrenr		 */
2263145522Sdarrenr		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
2264145522Sdarrenr		    (np->in_pmin != np->in_pmax)) {
2265145522Sdarrenr			nport = ntohs(dport) - ntohs(np->in_pmin) +
2266145522Sdarrenr				ntohs(np->in_pnext);
2267145522Sdarrenr			nport = htons(nport);
2268145522Sdarrenr		} else
2269145522Sdarrenr			nport = np->in_pnext;
2270145522Sdarrenr	}
2271145522Sdarrenr
2272145522Sdarrenr	/*
2273145522Sdarrenr	 * When the redirect-to address is set to 0.0.0.0, just
2274145522Sdarrenr	 * assume a blank `forwarding' of the packet.  We don't
2275145522Sdarrenr	 * setup any translation for this either.
2276145522Sdarrenr	 */
2277145522Sdarrenr	if (in.s_addr == 0) {
2278145522Sdarrenr		if (nport == dport)
2279145522Sdarrenr			return -1;
2280145522Sdarrenr		in.s_addr = ntohl(fin->fin_daddr);
2281145522Sdarrenr	}
2282145522Sdarrenr
2283170268Sdarrenr	/*
2284170268Sdarrenr	 * Check to see if this redirect mapping already exists and if
2285170268Sdarrenr	 * it does, return "failure" (allowing it to be created will just
2286170268Sdarrenr	 * cause one or both of these "connections" to stop working.)
2287170268Sdarrenr	 */
2288170268Sdarrenr	inb.s_addr = htonl(in.s_addr);
2289170268Sdarrenr	sp = fin->fin_data[0];
2290170268Sdarrenr	dp = fin->fin_data[1];
2291170268Sdarrenr	fin->fin_data[1] = fin->fin_data[0];
2292170268Sdarrenr	fin->fin_data[0] = ntohs(nport);
2293170268Sdarrenr	natl = nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
2294170268Sdarrenr			     (u_int)fin->fin_p, inb, fin->fin_src);
2295170268Sdarrenr	fin->fin_data[0] = sp;
2296170268Sdarrenr	fin->fin_data[1] = dp;
2297170268Sdarrenr	if (natl != NULL)
2298170268Sdarrenr		return -1;
2299170268Sdarrenr
2300145522Sdarrenr	nat->nat_inip.s_addr = htonl(in.s_addr);
2301145522Sdarrenr	nat->nat_outip = fin->fin_dst;
2302145522Sdarrenr	nat->nat_oip = fin->fin_src;
2303153876Sguido	if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
2304153876Sguido		nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst, in,
2305153876Sguido					  (u_32_t)dport);
2306145522Sdarrenr
2307145522Sdarrenr	ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)) + ntohs(dport);
2308145522Sdarrenr	ni->nai_sum2 = LONG_SUM(in.s_addr) + ntohs(nport);
2309145522Sdarrenr
2310145522Sdarrenr	ni->nai_ip.s_addr = in.s_addr;
2311145522Sdarrenr	ni->nai_nport = nport;
2312145522Sdarrenr	ni->nai_port = sport;
2313145522Sdarrenr
2314145522Sdarrenr	if (flags & IPN_TCPUDP) {
2315145522Sdarrenr		nat->nat_inport = nport;
2316145522Sdarrenr		nat->nat_outport = dport;
2317145522Sdarrenr		nat->nat_oport = sport;
2318145522Sdarrenr		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
2319145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
2320145522Sdarrenr		((icmphdr_t *)fin->fin_dp)->icmp_id = nport;
2321145522Sdarrenr		nat->nat_inport = nport;
2322145522Sdarrenr		nat->nat_outport = nport;
2323145522Sdarrenr	} else if (fin->fin_p == IPPROTO_GRE) {
2324145522Sdarrenr#if 0
2325145522Sdarrenr		nat->nat_gre.gs_flags = ((grehdr_t *)fin->fin_dp)->gr_flags;
2326145522Sdarrenr		if (GRE_REV(nat->nat_gre.gs_flags) == 1) {
2327145522Sdarrenr			nat->nat_call[0] = fin->fin_data[0];
2328145522Sdarrenr			nat->nat_call[1] = fin->fin_data[1];
2329145522Sdarrenr			nat->nat_oport = 0; /*fin->fin_data[0];*/
2330145522Sdarrenr			nat->nat_inport = 0; /*fin->fin_data[1];*/
2331145522Sdarrenr			nat->nat_outport = 0; /*fin->fin_data[1];*/
2332145522Sdarrenr		}
2333145522Sdarrenr#endif
2334145522Sdarrenr	}
2335145522Sdarrenr
2336145522Sdarrenr	return move;
2337145522Sdarrenr}
2338145522Sdarrenr
2339145522Sdarrenr/* ------------------------------------------------------------------------ */
2340145522Sdarrenr/* Function:    nat_new                                                     */
2341145522Sdarrenr/* Returns:     nat_t* - NULL == failure to create new NAT structure,       */
2342145522Sdarrenr/*                       else pointer to new NAT structure                  */
2343145522Sdarrenr/* Parameters:  fin(I)       - pointer to packet information                */
2344145522Sdarrenr/*              np(I)        - pointer to NAT rule                          */
2345145522Sdarrenr/*              natsave(I)   - pointer to where to store NAT struct pointer */
2346145522Sdarrenr/*              flags(I)     - flags describing the current packet          */
2347145522Sdarrenr/*              direction(I) - direction of packet (in/out)                 */
2348145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
2349145522Sdarrenr/*                                                                          */
2350145522Sdarrenr/* Attempts to create a new NAT entry.  Does not actually change the packet */
2351145522Sdarrenr/* in any way.                                                              */
2352145522Sdarrenr/*                                                                          */
2353145522Sdarrenr/* This fucntion is in three main parts: (1) deal with creating a new NAT   */
2354145522Sdarrenr/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
2355145522Sdarrenr/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
2356145522Sdarrenr/* and (3) building that structure and putting it into the NAT table(s).    */
2357161356Sguido/*                                                                          */
2358161356Sguido/* NOTE: natsave should NOT be used top point back to an ipstate_t struct   */
2359161356Sguido/*       as it can result in memory being corrupted.                        */
2360145522Sdarrenr/* ------------------------------------------------------------------------ */
2361145522Sdarrenrnat_t *nat_new(fin, np, natsave, flags, direction)
2362145522Sdarrenrfr_info_t *fin;
236353642Sguidoipnat_t *np;
236492685Sdarrenrnat_t **natsave;
236553642Sguidou_int flags;
236653642Sguidoint direction;
236753642Sguido{
236853642Sguido	u_short port = 0, sport = 0, dport = 0, nport = 0;
236953642Sguido	tcphdr_t *tcp = NULL;
237060852Sdarrenr	hostmap_t *hm = NULL;
2371145522Sdarrenr	struct in_addr in;
237260852Sdarrenr	nat_t *nat, *natl;
2373145522Sdarrenr	u_int nflags;
2374145522Sdarrenr	natinfo_t ni;
2375145522Sdarrenr	u_32_t sumd;
2376145522Sdarrenr	int move;
2377145522Sdarrenr#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC)
2378145522Sdarrenr	qpktinfo_t *qpi = fin->fin_qpi;
237955929Sguido#endif
238053642Sguido
2381130886Sdarrenr	if (nat_stats.ns_inuse >= ipf_nattable_max) {
2382130886Sdarrenr		nat_stats.ns_memfail++;
2383170268Sdarrenr		fr_nat_doflush = 1;
2384130886Sdarrenr		return NULL;
2385130886Sdarrenr	}
2386130886Sdarrenr
2387145522Sdarrenr	move = 1;
2388145522Sdarrenr	nflags = np->in_flags & flags;
2389145522Sdarrenr	nflags &= NAT_FROMRULE;
239053642Sguido
2391145522Sdarrenr	ni.nai_np = np;
2392145522Sdarrenr	ni.nai_nflags = nflags;
2393145522Sdarrenr	ni.nai_flags = flags;
2394170268Sdarrenr	ni.nai_dport = 0;
2395170268Sdarrenr	ni.nai_sport = 0;
2396145522Sdarrenr
239753642Sguido	/* Give me a new nat */
239853642Sguido	KMALLOC(nat, nat_t *);
239960852Sdarrenr	if (nat == NULL) {
240060852Sdarrenr		nat_stats.ns_memfail++;
2401130886Sdarrenr		/*
2402130886Sdarrenr		 * Try to automatically tune the max # of entries in the
2403130886Sdarrenr		 * table allowed to be less than what will cause kmem_alloc()
2404130886Sdarrenr		 * to fail and try to eliminate panics due to out of memory
2405130886Sdarrenr		 * conditions arising.
2406130886Sdarrenr		 */
2407130886Sdarrenr		if (ipf_nattable_max > ipf_nattable_sz) {
2408130886Sdarrenr			ipf_nattable_max = nat_stats.ns_inuse - 100;
2409130886Sdarrenr			printf("ipf_nattable_max reduced to %d\n",
2410130886Sdarrenr				ipf_nattable_max);
2411130886Sdarrenr		}
241253642Sguido		return NULL;
241360852Sdarrenr	}
241453642Sguido
2415145522Sdarrenr	if (flags & IPN_TCPUDP) {
2416145522Sdarrenr		tcp = fin->fin_dp;
2417145522Sdarrenr		ni.nai_sport = htons(fin->fin_sport);
2418145522Sdarrenr		ni.nai_dport = htons(fin->fin_dport);
2419145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
2420145522Sdarrenr		/*
2421145522Sdarrenr		 * In the ICMP query NAT code, we translate the ICMP id fields
2422145522Sdarrenr		 * to make them unique. This is indepedent of the ICMP type
2423145522Sdarrenr		 * (e.g. in the unlikely event that a host sends an echo and
2424145522Sdarrenr		 * an tstamp request with the same id, both packets will have
2425145522Sdarrenr		 * their ip address/id field changed in the same way).
2426145522Sdarrenr		 */
2427145522Sdarrenr		/* The icmp_id field is used by the sender to identify the
2428145522Sdarrenr		 * process making the icmp request. (the receiver justs
2429145522Sdarrenr		 * copies it back in its response). So, it closely matches
2430145522Sdarrenr		 * the concept of source port. We overlay sport, so we can
2431145522Sdarrenr		 * maximally reuse the existing code.
2432145522Sdarrenr		 */
2433145522Sdarrenr		ni.nai_sport = ((icmphdr_t *)fin->fin_dp)->icmp_id;
2434145522Sdarrenr		ni.nai_dport = ni.nai_sport;
2435145522Sdarrenr	}
2436145522Sdarrenr
243753642Sguido	bzero((char *)nat, sizeof(*nat));
243853642Sguido	nat->nat_flags = flags;
2439170268Sdarrenr	nat->nat_redir = np->in_redir;
2440145522Sdarrenr
2441145522Sdarrenr	if ((flags & NAT_SLAVE) == 0) {
2442145522Sdarrenr		MUTEX_ENTER(&ipf_nat_new);
2443145522Sdarrenr	}
2444145522Sdarrenr
244553642Sguido	/*
244653642Sguido	 * Search the current table for a match.
244753642Sguido	 */
244853642Sguido	if (direction == NAT_OUTBOUND) {
244953642Sguido		/*
2450145522Sdarrenr		 * We can now arrange to call this for the same connection
2451145522Sdarrenr		 * because ipf_nat_new doesn't protect the code path into
2452145522Sdarrenr		 * this function.
245353642Sguido		 */
2454145522Sdarrenr		natl = nat_outlookup(fin, nflags, (u_int)fin->fin_p,
2455145522Sdarrenr				     fin->fin_src, fin->fin_dst);
2456145522Sdarrenr		if (natl != NULL) {
2457161356Sguido			KFREE(nat);
2458145522Sdarrenr			nat = natl;
2459145522Sdarrenr			goto done;
2460145522Sdarrenr		}
246153642Sguido
2462145522Sdarrenr		move = nat_newmap(fin, nat, &ni);
2463145522Sdarrenr		if (move == -1)
2464145522Sdarrenr			goto badnat;
246553642Sguido
2466145522Sdarrenr		np = ni.nai_np;
2467145522Sdarrenr		in = ni.nai_ip;
246853642Sguido	} else {
246953642Sguido		/*
2470145522Sdarrenr		 * NAT_INBOUND is used only for redirects rules
247153642Sguido		 */
2472145522Sdarrenr		natl = nat_inlookup(fin, nflags, (u_int)fin->fin_p,
2473145522Sdarrenr				    fin->fin_src, fin->fin_dst);
2474145522Sdarrenr		if (natl != NULL) {
2475161356Sguido			KFREE(nat);
2476145522Sdarrenr			nat = natl;
2477145522Sdarrenr			goto done;
247860852Sdarrenr		}
247953642Sguido
2480145522Sdarrenr		move = nat_newrdr(fin, nat, &ni);
2481145522Sdarrenr		if (move == -1)
2482145522Sdarrenr			goto badnat;
248353642Sguido
2484145522Sdarrenr		np = ni.nai_np;
2485145522Sdarrenr		in = ni.nai_ip;
2486145522Sdarrenr	}
2487145522Sdarrenr	port = ni.nai_port;
2488145522Sdarrenr	nport = ni.nai_nport;
248953642Sguido
2490145522Sdarrenr	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
2491145522Sdarrenr		if (np->in_redir == NAT_REDIRECT) {
2492145522Sdarrenr			nat_delrdr(np);
2493145522Sdarrenr			nat_addrdr(np);
2494145522Sdarrenr		} else if (np->in_redir == NAT_MAP) {
2495145522Sdarrenr			nat_delnat(np);
2496145522Sdarrenr			nat_addnat(np);
249753642Sguido		}
249853642Sguido	}
249953642Sguido
2500145522Sdarrenr	if (flags & IPN_TCPUDP) {
2501145522Sdarrenr		sport = ni.nai_sport;
2502145522Sdarrenr		dport = ni.nai_dport;
2503145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
2504145522Sdarrenr		sport = ni.nai_sport;
2505145522Sdarrenr		dport = 0;
2506145522Sdarrenr	}
2507145522Sdarrenr
2508145522Sdarrenr	CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd);
250955929Sguido	nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
2510145522Sdarrenr#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC)
2511130886Sdarrenr	if ((flags & IPN_TCP) && dohwcksum &&
2512145522Sdarrenr	    (((ill_t *)qpi->qpi_ill)->ill_ick.ick_magic == ICK_M_CTL_MAGIC)) {
251355929Sguido		if (direction == NAT_OUTBOUND)
2514145522Sdarrenr			ni.nai_sum1 = LONG_SUM(in.s_addr);
251555929Sguido		else
2516145522Sdarrenr			ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr));
2517145522Sdarrenr		ni.nai_sum1 += LONG_SUM(ntohl(fin->fin_daddr));
2518145522Sdarrenr		ni.nai_sum1 += 30;
2519145522Sdarrenr		ni.nai_sum1 = (ni.nai_sum1 & 0xffff) + (ni.nai_sum1 >> 16);
2520145522Sdarrenr		nat->nat_sumd[1] = NAT_HW_CKSUM|(ni.nai_sum1 & 0xffff);
252155929Sguido	} else
252255929Sguido#endif
252355929Sguido		nat->nat_sumd[1] = nat->nat_sumd[0];
252453642Sguido
2525145522Sdarrenr	if ((flags & IPN_TCPUDPICMP) && ((sport != port) || (dport != nport))) {
252653642Sguido		if (direction == NAT_OUTBOUND)
2527145522Sdarrenr			ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr));
252853642Sguido		else
2529145522Sdarrenr			ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr));
253053642Sguido
2531145522Sdarrenr		ni.nai_sum2 = LONG_SUM(in.s_addr);
253253642Sguido
2533145522Sdarrenr		CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd);
253453642Sguido		nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
2535145522Sdarrenr	} else {
253655929Sguido		nat->nat_ipsumd = nat->nat_sumd[0];
2537145522Sdarrenr		if (!(flags & IPN_TCPUDPICMP)) {
2538145522Sdarrenr			nat->nat_sumd[0] = 0;
2539145522Sdarrenr			nat->nat_sumd[1] = 0;
2540145522Sdarrenr		}
2541145522Sdarrenr	}
254253642Sguido
2543145522Sdarrenr	if (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) {
2544172776Sdarrenr		fr_nat_doflush = 1;
2545145522Sdarrenr		goto badnat;
2546145522Sdarrenr	}
2547145522Sdarrenr	if (flags & SI_WILDP)
2548145522Sdarrenr		nat_stats.ns_wilds++;
2549172776Sdarrenr	fin->fin_flx |= FI_NEWNAT;
2550145522Sdarrenr	goto done;
2551145522Sdarrenrbadnat:
2552145522Sdarrenr	nat_stats.ns_badnat++;
2553145522Sdarrenr	if ((hm = nat->nat_hm) != NULL)
2554170268Sdarrenr		fr_hostmapdel(&hm);
2555145522Sdarrenr	KFREE(nat);
2556145522Sdarrenr	nat = NULL;
2557145522Sdarrenrdone:
2558145522Sdarrenr	if ((flags & NAT_SLAVE) == 0) {
2559145522Sdarrenr		MUTEX_EXIT(&ipf_nat_new);
2560145522Sdarrenr	}
2561145522Sdarrenr	return nat;
2562145522Sdarrenr}
256360852Sdarrenr
256460852Sdarrenr
2565145522Sdarrenr/* ------------------------------------------------------------------------ */
2566145522Sdarrenr/* Function:    nat_finalise                                                */
2567145522Sdarrenr/* Returns:     int - 0 == sucess, -1 == failure                            */
2568145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
2569145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
2570145522Sdarrenr/*              ni(I)  - pointer to structure with misc. information needed */
2571145522Sdarrenr/*                       to create new NAT entry.                           */
2572145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
2573145522Sdarrenr/*                                                                          */
2574145522Sdarrenr/* This is the tail end of constructing a new NAT entry and is the same     */
2575145522Sdarrenr/* for both IPv4 and IPv6.                                                  */
2576145522Sdarrenr/* ------------------------------------------------------------------------ */
2577145522Sdarrenr/*ARGSUSED*/
2578153876Sguidostatic int nat_finalise(fin, nat, ni, tcp, natsave, direction)
2579145522Sdarrenrfr_info_t *fin;
2580145522Sdarrenrnat_t *nat;
2581145522Sdarrenrnatinfo_t *ni;
2582145522Sdarrenrtcphdr_t *tcp;
2583145522Sdarrenrnat_t **natsave;
2584145522Sdarrenrint direction;
2585145522Sdarrenr{
2586145522Sdarrenr	frentry_t *fr;
2587145522Sdarrenr	ipnat_t *np;
2588145522Sdarrenr
2589145522Sdarrenr	np = ni->nai_np;
2590145522Sdarrenr
2591161356Sguido	if (np->in_ifps[0] != NULL) {
2592172776Sdarrenr		COPYIFNAME(4, np->in_ifps[0], nat->nat_ifnames[0]);
2593161356Sguido	}
2594161356Sguido	if (np->in_ifps[1] != NULL) {
2595172776Sdarrenr		COPYIFNAME(4, np->in_ifps[1], nat->nat_ifnames[1]);
2596161356Sguido	}
2597145522Sdarrenr#ifdef	IPFILTER_SYNC
2598145522Sdarrenr	if ((nat->nat_flags & SI_CLONE) == 0)
2599145522Sdarrenr		nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat);
2600145522Sdarrenr#endif
2601145522Sdarrenr
260292685Sdarrenr	nat->nat_me = natsave;
260353642Sguido	nat->nat_dir = direction;
2604161356Sguido	nat->nat_ifps[0] = np->in_ifps[0];
2605161356Sguido	nat->nat_ifps[1] = np->in_ifps[1];
260653642Sguido	nat->nat_ptr = np;
260792685Sdarrenr	nat->nat_p = fin->fin_p;
2608110916Sdarrenr	nat->nat_mssclamp = np->in_mssclamp;
2609172776Sdarrenr	if (nat->nat_p == IPPROTO_TCP)
2610172776Sdarrenr		nat->nat_seqnext[0] = ntohl(tcp->th_seq);
261192685Sdarrenr
2612145522Sdarrenr	if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0))
2613145522Sdarrenr		if (appr_new(fin, nat) == -1)
2614145522Sdarrenr			return -1;
261592685Sdarrenr
2616145522Sdarrenr	if (nat_insert(nat, fin->fin_rev) == 0) {
2617145522Sdarrenr		if (nat_logging)
2618145522Sdarrenr			nat_log(nat, (u_int)np->in_redir);
2619145522Sdarrenr		np->in_use++;
2620153876Sguido		fr = fin->fin_fr;
2621153876Sguido		nat->nat_fr = fr;
2622145522Sdarrenr		if (fr != NULL) {
2623145522Sdarrenr			MUTEX_ENTER(&fr->fr_lock);
2624145522Sdarrenr			fr->fr_ref++;
2625145522Sdarrenr			MUTEX_EXIT(&fr->fr_lock);
2626145522Sdarrenr		}
2627145522Sdarrenr		return 0;
2628145522Sdarrenr	}
262992685Sdarrenr
2630145522Sdarrenr	/*
2631145522Sdarrenr	 * nat_insert failed, so cleanup time...
2632145522Sdarrenr	 */
2633145522Sdarrenr	return -1;
263453642Sguido}
263553642Sguido
263653642Sguido
2637145522Sdarrenr/* ------------------------------------------------------------------------ */
2638145522Sdarrenr/* Function:   nat_insert                                                   */
2639145522Sdarrenr/* Returns:    int - 0 == sucess, -1 == failure                             */
2640145522Sdarrenr/* Parameters: nat(I) - pointer to NAT structure                            */
2641145522Sdarrenr/*             rev(I) - flag indicating forward/reverse direction of packet */
2642145522Sdarrenr/* Write Lock: ipf_nat                                                      */
2643145522Sdarrenr/*                                                                          */
2644145522Sdarrenr/* Insert a NAT entry into the hash tables for searching and add it to the  */
2645145522Sdarrenr/* list of active NAT entries.  Adjust global counters when complete.       */
2646145522Sdarrenr/* ------------------------------------------------------------------------ */
2647145522Sdarrenrint	nat_insert(nat, rev)
264860852Sdarrenrnat_t	*nat;
2649145522Sdarrenrint	rev;
265060852Sdarrenr{
265180482Sdarrenr	u_int hv1, hv2;
265260852Sdarrenr	nat_t **natp;
265360852Sdarrenr
2654145522Sdarrenr	/*
2655145522Sdarrenr	 * Try and return an error as early as possible, so calculate the hash
2656145522Sdarrenr	 * entry numbers first and then proceed.
2657145522Sdarrenr	 */
2658145522Sdarrenr	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
265980482Sdarrenr		hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport,
266080482Sdarrenr				  0xffffffff);
266180482Sdarrenr		hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport,
266280482Sdarrenr				  ipf_nattable_sz);
266380482Sdarrenr		hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport,
266480482Sdarrenr				  0xffffffff);
266580482Sdarrenr		hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport,
2666145522Sdarrenr				  ipf_nattable_sz);
266780482Sdarrenr	} else {
2668145522Sdarrenr		hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff);
2669145522Sdarrenr		hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1, ipf_nattable_sz);
2670145522Sdarrenr		hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff);
2671145522Sdarrenr		hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2, ipf_nattable_sz);
267280482Sdarrenr	}
267380482Sdarrenr
2674145522Sdarrenr	if (nat_stats.ns_bucketlen[0][hv1] >= fr_nat_maxbucket ||
2675145522Sdarrenr	    nat_stats.ns_bucketlen[1][hv2] >= fr_nat_maxbucket) {
2676145522Sdarrenr		return -1;
2677145522Sdarrenr	}
2678145522Sdarrenr
2679145522Sdarrenr	nat->nat_hv[0] = hv1;
2680145522Sdarrenr	nat->nat_hv[1] = hv2;
2681145522Sdarrenr
2682145522Sdarrenr	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
2683145522Sdarrenr
2684145522Sdarrenr	nat->nat_rev = rev;
2685145522Sdarrenr	nat->nat_ref = 1;
2686145522Sdarrenr	nat->nat_bytes[0] = 0;
2687145522Sdarrenr	nat->nat_pkts[0] = 0;
2688145522Sdarrenr	nat->nat_bytes[1] = 0;
2689145522Sdarrenr	nat->nat_pkts[1] = 0;
2690145522Sdarrenr
2691145522Sdarrenr	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
2692145522Sdarrenr	nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 4);
2693145522Sdarrenr
2694161356Sguido	if (nat->nat_ifnames[1][0] != '\0') {
2695145522Sdarrenr		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
2696145522Sdarrenr		nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 4);
2697145522Sdarrenr	} else {
2698145522Sdarrenr		(void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0],
2699145522Sdarrenr			       LIFNAMSIZ);
2700145522Sdarrenr		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
2701145522Sdarrenr		nat->nat_ifps[1] = nat->nat_ifps[0];
2702145522Sdarrenr	}
2703145522Sdarrenr
2704145522Sdarrenr	nat->nat_next = nat_instances;
2705145522Sdarrenr	nat->nat_pnext = &nat_instances;
2706145522Sdarrenr	if (nat_instances)
2707145522Sdarrenr		nat_instances->nat_pnext = &nat->nat_next;
2708145522Sdarrenr	nat_instances = nat;
2709145522Sdarrenr
271080482Sdarrenr	natp = &nat_table[0][hv1];
271167614Sdarrenr	if (*natp)
271267614Sdarrenr		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
271367614Sdarrenr	nat->nat_phnext[0] = natp;
271460852Sdarrenr	nat->nat_hnext[0] = *natp;
271560852Sdarrenr	*natp = nat;
2716145522Sdarrenr	nat_stats.ns_bucketlen[0][hv1]++;
271767614Sdarrenr
271880482Sdarrenr	natp = &nat_table[1][hv2];
271967614Sdarrenr	if (*natp)
272067614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
272167614Sdarrenr	nat->nat_phnext[1] = natp;
272260852Sdarrenr	nat->nat_hnext[1] = *natp;
272360852Sdarrenr	*natp = nat;
2724145522Sdarrenr	nat_stats.ns_bucketlen[1][hv2]++;
272560852Sdarrenr
2726145522Sdarrenr	fr_setnatqueue(nat, rev);
2727145522Sdarrenr
272860852Sdarrenr	nat_stats.ns_added++;
272960852Sdarrenr	nat_stats.ns_inuse++;
2730145522Sdarrenr	return 0;
273160852Sdarrenr}
273260852Sdarrenr
273360852Sdarrenr
2734145522Sdarrenr/* ------------------------------------------------------------------------ */
2735145522Sdarrenr/* Function:    nat_icmperrorlookup                                         */
2736145522Sdarrenr/* Returns:     nat_t* - point to matching NAT structure                    */
2737145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
2738145522Sdarrenr/*              dir(I) - direction of packet (in/out)                       */
2739145522Sdarrenr/*                                                                          */
2740145522Sdarrenr/* Check if the ICMP error message is related to an existing TCP, UDP or    */
2741145522Sdarrenr/* ICMP query nat entry.  It is assumed that the packet is already of the   */
2742145522Sdarrenr/* the required length.                                                     */
2743145522Sdarrenr/* ------------------------------------------------------------------------ */
2744145522Sdarrenrnat_t *nat_icmperrorlookup(fin, dir)
274553642Sguidofr_info_t *fin;
274660852Sdarrenrint dir;
274753642Sguido{
2748145522Sdarrenr	int flags = 0, type, minlen;
2749145522Sdarrenr	icmphdr_t *icmp, *orgicmp;
275053642Sguido	tcphdr_t *tcp = NULL;
2751145522Sdarrenr	u_short data[2];
2752145522Sdarrenr	nat_t *nat;
275353642Sguido	ip_t *oip;
2754145522Sdarrenr	u_int p;
275553642Sguido
2756145522Sdarrenr	icmp = fin->fin_dp;
2757145522Sdarrenr	type = icmp->icmp_type;
275853642Sguido	/*
275953642Sguido	 * Does it at least have the return (basic) IP header ?
276053642Sguido	 * Only a basic IP header (no options) should be with an ICMP error
2761145522Sdarrenr	 * header.  Also, if it's not an error type, then return.
276253642Sguido	 */
2763153876Sguido	if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR))
276453642Sguido		return NULL;
2765145522Sdarrenr
276653642Sguido	/*
2767145522Sdarrenr	 * Check packet size
276853642Sguido	 */
276953642Sguido	oip = (ip_t *)((char *)fin->fin_dp + 8);
2770145522Sdarrenr	minlen = IP_HL(oip) << 2;
2771145522Sdarrenr	if ((minlen < sizeof(ip_t)) ||
2772145522Sdarrenr	    (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen))
277353642Sguido		return NULL;
277464580Sdarrenr	/*
277564580Sdarrenr	 * Is the buffer big enough for all of it ?  It's the size of the IP
277664580Sdarrenr	 * header claimed in the encapsulated part which is of concern.  It
277764580Sdarrenr	 * may be too big to be in this buffer but not so big that it's
277864580Sdarrenr	 * outside the ICMP packet, leading to TCP deref's causing problems.
277964580Sdarrenr	 * This is possible because we don't know how big oip_hl is when we
278064580Sdarrenr	 * do the pullup early in fr_check() and thus can't gaurantee it is
278164580Sdarrenr	 * all here now.
278264580Sdarrenr	 */
278364580Sdarrenr#ifdef  _KERNEL
278464580Sdarrenr	{
278564580Sdarrenr	mb_t *m;
278664580Sdarrenr
2787145522Sdarrenr	m = fin->fin_m;
2788145522Sdarrenr# if defined(MENTAT)
278964580Sdarrenr	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr)
279064580Sdarrenr		return NULL;
279164580Sdarrenr# else
279264580Sdarrenr	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
2793145522Sdarrenr	    (char *)fin->fin_ip + M_LEN(m))
279464580Sdarrenr		return NULL;
279564580Sdarrenr# endif
279664580Sdarrenr	}
279764580Sdarrenr#endif
279864580Sdarrenr
2799145522Sdarrenr	if (fin->fin_daddr != oip->ip_src.s_addr)
2800145522Sdarrenr		return NULL;
2801145522Sdarrenr
2802145522Sdarrenr	p = oip->ip_p;
2803145522Sdarrenr	if (p == IPPROTO_TCP)
280453642Sguido		flags = IPN_TCP;
2805145522Sdarrenr	else if (p == IPPROTO_UDP)
280653642Sguido		flags = IPN_UDP;
2807145522Sdarrenr	else if (p == IPPROTO_ICMP) {
2808145522Sdarrenr		orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2));
2809145522Sdarrenr
2810145522Sdarrenr		/* see if this is related to an ICMP query */
2811145522Sdarrenr		if (nat_icmpquerytype4(orgicmp->icmp_type)) {
2812145522Sdarrenr			data[0] = fin->fin_data[0];
2813145522Sdarrenr			data[1] = fin->fin_data[1];
2814145522Sdarrenr			fin->fin_data[0] = 0;
2815145522Sdarrenr			fin->fin_data[1] = orgicmp->icmp_id;
2816145522Sdarrenr
2817145522Sdarrenr			flags = IPN_ICMPERR|IPN_ICMPQUERY;
2818145522Sdarrenr			/*
2819145522Sdarrenr			 * NOTE : dir refers to the direction of the original
2820145522Sdarrenr			 *        ip packet. By definition the icmp error
2821145522Sdarrenr			 *        message flows in the opposite direction.
2822145522Sdarrenr			 */
2823145522Sdarrenr			if (dir == NAT_INBOUND)
2824145522Sdarrenr				nat = nat_inlookup(fin, flags, p, oip->ip_dst,
2825145522Sdarrenr						   oip->ip_src);
2826145522Sdarrenr			else
2827145522Sdarrenr				nat = nat_outlookup(fin, flags, p, oip->ip_dst,
2828145522Sdarrenr						    oip->ip_src);
2829145522Sdarrenr			fin->fin_data[0] = data[0];
2830145522Sdarrenr			fin->fin_data[1] = data[1];
2831145522Sdarrenr			return nat;
2832145522Sdarrenr		}
2833145522Sdarrenr	}
2834145522Sdarrenr
283553642Sguido	if (flags & IPN_TCPUDP) {
283664580Sdarrenr		minlen += 8;		/* + 64bits of data to get ports */
2837145522Sdarrenr		if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)
283864580Sdarrenr			return NULL;
283992685Sdarrenr
284092685Sdarrenr		data[0] = fin->fin_data[0];
284192685Sdarrenr		data[1] = fin->fin_data[1];
2842145522Sdarrenr		tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2));
284392685Sdarrenr		fin->fin_data[0] = ntohs(tcp->th_dport);
284492685Sdarrenr		fin->fin_data[1] = ntohs(tcp->th_sport);
284592685Sdarrenr
284692685Sdarrenr		if (dir == NAT_INBOUND) {
2847145522Sdarrenr			nat = nat_inlookup(fin, flags, p, oip->ip_dst,
2848145522Sdarrenr					   oip->ip_src);
284992685Sdarrenr		} else {
2850145522Sdarrenr			nat = nat_outlookup(fin, flags, p, oip->ip_dst,
2851145522Sdarrenr					    oip->ip_src);
285292685Sdarrenr		}
285392685Sdarrenr		fin->fin_data[0] = data[0];
285492685Sdarrenr		fin->fin_data[1] = data[1];
285592685Sdarrenr		return nat;
285653642Sguido	}
285760852Sdarrenr	if (dir == NAT_INBOUND)
2858145522Sdarrenr		return nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
285960852Sdarrenr	else
2860145522Sdarrenr		return nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
286153642Sguido}
286253642Sguido
286353642Sguido
2864145522Sdarrenr/* ------------------------------------------------------------------------ */
2865145522Sdarrenr/* Function:    nat_icmperror                                               */
2866145522Sdarrenr/* Returns:     nat_t* - point to matching NAT structure                    */
2867145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
2868145522Sdarrenr/*              nflags(I) - NAT flags for this packet                       */
2869145522Sdarrenr/*              dir(I)    - direction of packet (in/out)                    */
2870145522Sdarrenr/*                                                                          */
2871145522Sdarrenr/* Fix up an ICMP packet which is an error message for an existing NAT      */
2872145522Sdarrenr/* session.  This will correct both packet header data and checksums.       */
2873145522Sdarrenr/*                                                                          */
2874145522Sdarrenr/* This should *ONLY* be used for incoming ICMP error packets to make sure  */
2875145522Sdarrenr/* a NAT'd ICMP packet gets correctly recognised.                           */
2876145522Sdarrenr/* ------------------------------------------------------------------------ */
2877145522Sdarrenrnat_t *nat_icmperror(fin, nflags, dir)
287853642Sguidofr_info_t *fin;
287953642Sguidou_int *nflags;
288060852Sdarrenrint dir;
288153642Sguido{
2882145522Sdarrenr	u_32_t sum1, sum2, sumd, sumd2;
2883170268Sdarrenr	struct in_addr a1, a2;
2884170268Sdarrenr	int flags, dlen, odst;
2885145522Sdarrenr	icmphdr_t *icmp;
2886145522Sdarrenr	u_short *csump;
288795418Sdarrenr	tcphdr_t *tcp;
288853642Sguido	nat_t *nat;
288953642Sguido	ip_t *oip;
2890145522Sdarrenr	void *dp;
289153642Sguido
2892145522Sdarrenr	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY)))
289363523Sdarrenr		return NULL;
289467614Sdarrenr	/*
2895145522Sdarrenr	 * nat_icmperrorlookup() will return NULL for `defective' packets.
289667614Sdarrenr	 */
2897145522Sdarrenr	if ((fin->fin_v != 4) || !(nat = nat_icmperrorlookup(fin, dir)))
289853642Sguido		return NULL;
289992685Sdarrenr
2900145522Sdarrenr	tcp = NULL;
2901145522Sdarrenr	csump = NULL;
290292685Sdarrenr	flags = 0;
2903130886Sdarrenr	sumd2 = 0;
290453642Sguido	*nflags = IPN_ICMPERR;
2905145522Sdarrenr	icmp = fin->fin_dp;
290653642Sguido	oip = (ip_t *)&icmp->icmp_ip;
2907145522Sdarrenr	dp = (((char *)oip) + (IP_HL(oip) << 2));
2908145522Sdarrenr	if (oip->ip_p == IPPROTO_TCP) {
2909145522Sdarrenr		tcp = (tcphdr_t *)dp;
2910145522Sdarrenr		csump = (u_short *)&tcp->th_sum;
291153642Sguido		flags = IPN_TCP;
2912145522Sdarrenr	} else if (oip->ip_p == IPPROTO_UDP) {
2913145522Sdarrenr		udphdr_t *udp;
2914145522Sdarrenr
2915145522Sdarrenr		udp = (udphdr_t *)dp;
2916145522Sdarrenr		tcp = (tcphdr_t *)dp;
2917145522Sdarrenr		csump = (u_short *)&udp->uh_sum;
291853642Sguido		flags = IPN_UDP;
2919145522Sdarrenr	} else if (oip->ip_p == IPPROTO_ICMP)
2920145522Sdarrenr		flags = IPN_ICMPQUERY;
2921145522Sdarrenr	dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip);
292295418Sdarrenr
292395418Sdarrenr	/*
292453642Sguido	 * Need to adjust ICMP header to include the real IP#'s and
292553642Sguido	 * port #'s.  Only apply a checksum change relative to the
2926145522Sdarrenr	 * IP address change as it will be modified again in fr_checknatout
292753642Sguido	 * for both address and port.  Two checksum changes are
292853642Sguido	 * necessary for the two header address changes.  Be careful
292953642Sguido	 * to only modify the checksum once for the port # and twice
293053642Sguido	 * for the IP#.
293153642Sguido	 */
293260852Sdarrenr
293367614Sdarrenr	/*
293467614Sdarrenr	 * Step 1
293567614Sdarrenr	 * Fix the IP addresses in the offending IP packet. You also need
2936170268Sdarrenr	 * to adjust the IP header checksum of that offending IP packet.
293767614Sdarrenr	 *
2938145522Sdarrenr	 * Normally, you would expect that the ICMP checksum of the
2939130886Sdarrenr	 * ICMP error message needs to be adjusted as well for the
2940130886Sdarrenr	 * IP address change in oip.
2941145522Sdarrenr	 * However, this is a NOP, because the ICMP checksum is
2942130886Sdarrenr	 * calculated over the complete ICMP packet, which includes the
2943145522Sdarrenr	 * changed oip IP addresses and oip->ip_sum. However, these
2944130886Sdarrenr	 * two changes cancel each other out (if the delta for
2945145522Sdarrenr	 * the IP address is x, then the delta for ip_sum is minus x),
2946130886Sdarrenr	 * so no change in the icmp_cksum is necessary.
2947130886Sdarrenr	 *
2948170268Sdarrenr	 * Inbound ICMP
2949170268Sdarrenr	 * ------------
2950170268Sdarrenr	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
2951170268Sdarrenr	 * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b)
2952170268Sdarrenr	 * - OIP_SRC(c)=nat_outip, OIP_DST(b)=nat_oip
2953170268Sdarrenr	 *
2954170268Sdarrenr	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
2955170268Sdarrenr	 * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a)
2956170268Sdarrenr	 * - OIP_SRC(b)=nat_outip, OIP_DST(a)=nat_oip
2957170268Sdarrenr	 *
2958170268Sdarrenr	 * Outbound ICMP
2959170268Sdarrenr	 * -------------
2960170268Sdarrenr	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
2961170268Sdarrenr	 * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
2962170268Sdarrenr	 * - OIP_SRC(a)=nat_oip, OIP_DST(c)=nat_inip
2963170268Sdarrenr	 *
2964170268Sdarrenr	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
2965170268Sdarrenr	 * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c)
2966170268Sdarrenr	 * - OIP_SRC(a)=nat_oip, OIP_DST(c)=nat_inip
2967170268Sdarrenr	 *
2968130886Sdarrenr	 */
2969170268Sdarrenr	odst = (oip->ip_dst.s_addr == nat->nat_oip.s_addr) ? 1 : 0;
2970170268Sdarrenr	if (odst == 1) {
2971170268Sdarrenr		a1.s_addr = ntohl(nat->nat_inip.s_addr);
2972170268Sdarrenr		a2.s_addr = ntohl(oip->ip_src.s_addr);
2973170268Sdarrenr		oip->ip_src.s_addr = htonl(a1.s_addr);
2974170268Sdarrenr	} else {
2975170268Sdarrenr		a1.s_addr = ntohl(nat->nat_outip.s_addr);
2976170268Sdarrenr		a2.s_addr = ntohl(oip->ip_dst.s_addr);
2977170268Sdarrenr		oip->ip_dst.s_addr = htonl(a1.s_addr);
2978170268Sdarrenr	}
2979130886Sdarrenr
2980170268Sdarrenr	sumd = a2.s_addr - a1.s_addr;
2981170268Sdarrenr	if (sumd != 0) {
2982170268Sdarrenr		if (a1.s_addr > a2.s_addr)
2983170268Sdarrenr			sumd--;
2984170268Sdarrenr		sumd = ~sumd;
298553642Sguido
2986170268Sdarrenr		fix_datacksum(&oip->ip_sum, sumd);
2987130886Sdarrenr	}
298867614Sdarrenr
2989170268Sdarrenr	sumd2 = sumd;
2990170268Sdarrenr	sum1 = 0;
2991170268Sdarrenr	sum2 = 0;
2992170268Sdarrenr
2993130886Sdarrenr	/*
2994170268Sdarrenr	 * Fix UDP pseudo header checksum to compensate for the
2995170268Sdarrenr	 * IP address change.
2996130886Sdarrenr	 */
2997130886Sdarrenr	if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) {
299864580Sdarrenr		/*
299967614Sdarrenr		 * Step 2 :
300067614Sdarrenr		 * For offending TCP/UDP IP packets, translate the ports as
300167614Sdarrenr		 * well, based on the NAT specification. Of course such
3002170268Sdarrenr		 * a change may be reflected in the ICMP checksum as well.
300367614Sdarrenr		 *
300467614Sdarrenr		 * Since the port fields are part of the TCP/UDP checksum
300567614Sdarrenr		 * of the offending IP packet, you need to adjust that checksum
3006161356Sguido		 * as well... except that the change in the port numbers should
3007170268Sdarrenr		 * be offset by the checksum change.  However, the TCP/UDP
3008170268Sdarrenr		 * checksum will also need to change if there has been an
3009170268Sdarrenr		 * IP address change.
301067614Sdarrenr		 */
3011170268Sdarrenr		if (odst == 1) {
3012170268Sdarrenr			sum1 = ntohs(nat->nat_inport);
3013170268Sdarrenr			sum2 = ntohs(tcp->th_sport);
3014145522Sdarrenr
3015170268Sdarrenr			tcp->th_sport = htons(sum1);
3016170268Sdarrenr		} else {
3017145522Sdarrenr			sum1 = ntohs(nat->nat_outport);
3018145522Sdarrenr			sum2 = ntohs(tcp->th_dport);
3019170268Sdarrenr
3020170268Sdarrenr			tcp->th_dport = htons(sum1);
3021145522Sdarrenr		}
302267614Sdarrenr
3023170268Sdarrenr		sumd += sum1 - sum2;
3024170268Sdarrenr		if (sumd != 0 || sumd2 != 0) {
3025145522Sdarrenr			/*
3026170268Sdarrenr			 * At this point, sumd is the delta to apply to the
3027170268Sdarrenr			 * TCP/UDP header, given the changes in both the IP
3028170268Sdarrenr			 * address and the ports and sumd2 is the delta to
3029170268Sdarrenr			 * apply to the ICMP header, given the IP address
3030170268Sdarrenr			 * change delta that may need to be applied to the
3031170268Sdarrenr			 * TCP/UDP checksum instead.
3032145522Sdarrenr			 *
3033170268Sdarrenr			 * If we will both the IP and TCP/UDP checksums
3034170268Sdarrenr			 * then the ICMP checksum changes by the address
3035170268Sdarrenr			 * delta applied to the TCP/UDP checksum.  If we
3036170268Sdarrenr			 * do not change the TCP/UDP checksum them we
3037170268Sdarrenr			 * apply the delta in ports to the ICMP checksum.
3038145522Sdarrenr			 */
3039161356Sguido			if (oip->ip_p == IPPROTO_UDP) {
3040161356Sguido				if ((dlen >= 8) && (*csump != 0)) {
3041161356Sguido					fix_datacksum(csump, sumd);
3042161356Sguido				} else {
3043170268Sdarrenr					sumd2 = sum1 - sum2;
3044170268Sdarrenr					if (sum2 > sum1)
3045170268Sdarrenr						sumd2--;
3046161356Sguido				}
3047170268Sdarrenr			} else if (oip->ip_p == IPPROTO_TCP) {
3048145522Sdarrenr				if (dlen >= 18) {
3049145522Sdarrenr					fix_datacksum(csump, sumd);
3050145522Sdarrenr				} else {
3051170268Sdarrenr					sumd2 = sum2 - sum1;
3052170268Sdarrenr					if (sum1 > sum2)
3053170268Sdarrenr						sumd2--;
305467614Sdarrenr				}
305553642Sguido			}
3056130886Sdarrenr
3057170268Sdarrenr			if (sumd2 != 0) {
3058172776Sdarrenr				ipnat_t *np;
3059172776Sdarrenr
3060172776Sdarrenr				np = nat->nat_ptr;
3061170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
3062170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
3063170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
3064172776Sdarrenr
3065172776Sdarrenr				if ((odst == 0) && (dir == NAT_OUTBOUND) &&
3066172776Sdarrenr				    (fin->fin_rev == 0) && (np != NULL) &&
3067172776Sdarrenr				    (np->in_redir & NAT_REDIRECT)) {
3068172776Sdarrenr					fix_outcksum(fin, &icmp->icmp_cksum,
3069172776Sdarrenr						     sumd2);
3070172776Sdarrenr				} else {
3071172776Sdarrenr					fix_incksum(fin, &icmp->icmp_cksum,
3072172776Sdarrenr						    sumd2);
3073172776Sdarrenr				}
3074130886Sdarrenr			}
307553642Sguido		}
3076145522Sdarrenr	} else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) {
3077145522Sdarrenr		icmphdr_t *orgicmp;
3078145522Sdarrenr
3079145522Sdarrenr		/*
3080145522Sdarrenr		 * XXX - what if this is bogus hl and we go off the end ?
3081145522Sdarrenr		 * In this case, nat_icmperrorlookup() will have returned NULL.
3082145522Sdarrenr		 */
3083145522Sdarrenr		orgicmp = (icmphdr_t *)dp;
3084145522Sdarrenr
3085170268Sdarrenr		if (odst == 1) {
3086145522Sdarrenr			if (orgicmp->icmp_id != nat->nat_inport) {
3087145522Sdarrenr
3088145522Sdarrenr				/*
3089145522Sdarrenr				 * Fix ICMP checksum (of the offening ICMP
3090145522Sdarrenr				 * query packet) to compensate the change
3091145522Sdarrenr				 * in the ICMP id of the offending ICMP
3092145522Sdarrenr				 * packet.
3093145522Sdarrenr				 *
3094145522Sdarrenr				 * Since you modify orgicmp->icmp_id with
3095145522Sdarrenr				 * a delta (say x) and you compensate that
3096145522Sdarrenr				 * in origicmp->icmp_cksum with a delta
3097145522Sdarrenr				 * minus x, you don't have to adjust the
3098145522Sdarrenr				 * overall icmp->icmp_cksum
3099145522Sdarrenr				 */
3100145522Sdarrenr				sum1 = ntohs(orgicmp->icmp_id);
3101145522Sdarrenr				sum2 = ntohs(nat->nat_inport);
3102145522Sdarrenr				CALC_SUMD(sum1, sum2, sumd);
3103145522Sdarrenr				orgicmp->icmp_id = nat->nat_inport;
3104145522Sdarrenr				fix_datacksum(&orgicmp->icmp_cksum, sumd);
3105145522Sdarrenr			}
3106145522Sdarrenr		} /* nat_dir == NAT_INBOUND is impossible for icmp queries */
310753642Sguido	}
310853642Sguido	return nat;
310953642Sguido}
311053642Sguido
311153642Sguido
311253642Sguido/*
3113145522Sdarrenr * NB: these lookups don't lock access to the list, it assumed that it has
3114145522Sdarrenr * already been done!
311553642Sguido */
3116145522Sdarrenr
3117145522Sdarrenr/* ------------------------------------------------------------------------ */
3118145522Sdarrenr/* Function:    nat_inlookup                                                */
3119145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
3120145522Sdarrenr/*                       else pointer to matching NAT entry                 */
3121145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
3122145522Sdarrenr/*              flags(I)  - NAT flags for this packet                       */
3123145522Sdarrenr/*              p(I)      - protocol for this packet                        */
3124145522Sdarrenr/*              src(I)    - source IP address                               */
3125145522Sdarrenr/*              mapdst(I) - destination IP address                          */
3126145522Sdarrenr/*                                                                          */
3127145522Sdarrenr/* Lookup a nat entry based on the mapped destination ip address/port and   */
3128145522Sdarrenr/* real source address/port.  We use this lookup when receiving a packet,   */
3129145522Sdarrenr/* we're looking for a table entry, based on the destination address.       */
3130145522Sdarrenr/*                                                                          */
3131145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
3132145522Sdarrenr/*                                                                          */
3133145522Sdarrenr/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
3134145522Sdarrenr/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
3135145522Sdarrenr/*                                                                          */
3136145522Sdarrenr/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
3137145522Sdarrenr/*            the packet is of said protocol                                */
3138145522Sdarrenr/* ------------------------------------------------------------------------ */
3139145522Sdarrenrnat_t *nat_inlookup(fin, flags, p, src, mapdst)
314092685Sdarrenrfr_info_t *fin;
3141145522Sdarrenru_int flags, p;
314253642Sguidostruct in_addr src , mapdst;
314353642Sguido{
3144145522Sdarrenr	u_short sport, dport;
3145145522Sdarrenr	grehdr_t *gre;
314692685Sdarrenr	ipnat_t *ipn;
3147145522Sdarrenr	u_int sflags;
3148145522Sdarrenr	nat_t *nat;
3149145522Sdarrenr	int nflags;
3150145522Sdarrenr	u_32_t dst;
315192685Sdarrenr	void *ifp;
315253642Sguido	u_int hv;
315353642Sguido
3154161356Sguido	ifp = fin->fin_ifp;
3155145522Sdarrenr	sport = 0;
3156145522Sdarrenr	dport = 0;
3157145522Sdarrenr	gre = NULL;
315867614Sdarrenr	dst = mapdst.s_addr;
3159145522Sdarrenr	sflags = flags & NAT_TCPUDPICMP;
3160145522Sdarrenr
3161145522Sdarrenr	switch (p)
3162145522Sdarrenr	{
3163145522Sdarrenr	case IPPROTO_TCP :
3164145522Sdarrenr	case IPPROTO_UDP :
316592685Sdarrenr		sport = htons(fin->fin_data[0]);
316692685Sdarrenr		dport = htons(fin->fin_data[1]);
3167145522Sdarrenr		break;
3168145522Sdarrenr	case IPPROTO_ICMP :
3169145522Sdarrenr		if (flags & IPN_ICMPERR)
3170145522Sdarrenr			sport = fin->fin_data[1];
3171145522Sdarrenr		else
3172145522Sdarrenr			dport = fin->fin_data[1];
3173145522Sdarrenr		break;
3174145522Sdarrenr	default :
3175145522Sdarrenr		break;
317692685Sdarrenr	}
317753642Sguido
3178145522Sdarrenr
3179145522Sdarrenr	if ((flags & SI_WILDP) != 0)
3180145522Sdarrenr		goto find_in_wild_ports;
3181145522Sdarrenr
318280482Sdarrenr	hv = NAT_HASH_FN(dst, dport, 0xffffffff);
318380482Sdarrenr	hv = NAT_HASH_FN(src.s_addr, hv + sport, ipf_nattable_sz);
318453642Sguido	nat = nat_table[1][hv];
318553642Sguido	for (; nat; nat = nat->nat_hnext[1]) {
3186161356Sguido		if (nat->nat_ifps[0] != NULL) {
3187161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
3188161356Sguido				continue;
3189161356Sguido		} else if (ifp != NULL)
3190161356Sguido			nat->nat_ifps[0] = ifp;
3191161356Sguido
319253642Sguido		nflags = nat->nat_flags;
3193145522Sdarrenr
3194145522Sdarrenr		if (nat->nat_oip.s_addr == src.s_addr &&
319567614Sdarrenr		    nat->nat_outip.s_addr == dst &&
3196145522Sdarrenr		    (((p == 0) &&
3197145522Sdarrenr		      (sflags == (nat->nat_flags & IPN_TCPUDPICMP)))
3198145522Sdarrenr		     || (p == nat->nat_p))) {
319992685Sdarrenr			switch (p)
320092685Sdarrenr			{
3201145522Sdarrenr#if 0
3202145522Sdarrenr			case IPPROTO_GRE :
3203145522Sdarrenr				if (nat->nat_call[1] != fin->fin_data[0])
3204145522Sdarrenr					continue;
3205145522Sdarrenr				break;
3206145522Sdarrenr#endif
3207145522Sdarrenr			case IPPROTO_ICMP :
3208145522Sdarrenr				if ((flags & IPN_ICMPERR) != 0) {
3209145522Sdarrenr					if (nat->nat_outport != sport)
3210145522Sdarrenr						continue;
3211145522Sdarrenr				} else {
3212145522Sdarrenr					if (nat->nat_outport != dport)
3213145522Sdarrenr						continue;
3214145522Sdarrenr				}
3215145522Sdarrenr				break;
321692685Sdarrenr			case IPPROTO_TCP :
321792685Sdarrenr			case IPPROTO_UDP :
321892685Sdarrenr				if (nat->nat_oport != sport)
321992685Sdarrenr					continue;
322092685Sdarrenr				if (nat->nat_outport != dport)
322192685Sdarrenr					continue;
322292685Sdarrenr				break;
322392685Sdarrenr			default :
322492685Sdarrenr				break;
322592685Sdarrenr			}
322692685Sdarrenr
322792685Sdarrenr			ipn = nat->nat_ptr;
322892685Sdarrenr			if ((ipn != NULL) && (nat->nat_aps != NULL))
322992685Sdarrenr				if (appr_match(fin, nat) != 0)
323092685Sdarrenr					continue;
323153642Sguido			return nat;
323292685Sdarrenr		}
323353642Sguido	}
3234145522Sdarrenr
3235145522Sdarrenr	/*
3236145522Sdarrenr	 * So if we didn't find it but there are wildcard members in the hash
3237145522Sdarrenr	 * table, go back and look for them.  We do this search and update here
3238145522Sdarrenr	 * because it is modifying the NAT table and we want to do this only
3239145522Sdarrenr	 * for the first packet that matches.  The exception, of course, is
3240145522Sdarrenr	 * for "dummy" (FI_IGNORE) lookups.
3241145522Sdarrenr	 */
3242145522Sdarrenrfind_in_wild_ports:
3243145522Sdarrenr	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
324467614Sdarrenr		return NULL;
3245145522Sdarrenr	if (nat_stats.ns_wilds == 0)
3246145522Sdarrenr		return NULL;
3247145522Sdarrenr
3248145522Sdarrenr	RWLOCK_EXIT(&ipf_nat);
3249145522Sdarrenr
325080482Sdarrenr	hv = NAT_HASH_FN(dst, 0, 0xffffffff);
3251145522Sdarrenr	hv = NAT_HASH_FN(src.s_addr, hv, ipf_nattable_sz);
3252145522Sdarrenr
3253145522Sdarrenr	WRITE_ENTER(&ipf_nat);
3254145522Sdarrenr
325567614Sdarrenr	nat = nat_table[1][hv];
325667614Sdarrenr	for (; nat; nat = nat->nat_hnext[1]) {
3257161356Sguido		if (nat->nat_ifps[0] != NULL) {
3258161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
3259161356Sguido				continue;
3260161356Sguido		} else if (ifp != NULL)
3261161356Sguido			nat->nat_ifps[0] = ifp;
3262145522Sdarrenr
3263145522Sdarrenr		if (nat->nat_p != fin->fin_p)
326467614Sdarrenr			continue;
326567614Sdarrenr		if (nat->nat_oip.s_addr != src.s_addr ||
326667614Sdarrenr		    nat->nat_outip.s_addr != dst)
326767614Sdarrenr			continue;
3268145522Sdarrenr
3269145522Sdarrenr		nflags = nat->nat_flags;
3270145522Sdarrenr		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
3271145522Sdarrenr			continue;
3272145522Sdarrenr
3273145522Sdarrenr		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
3274145522Sdarrenr			       NAT_INBOUND) == 1) {
3275145522Sdarrenr			if ((fin->fin_flx & FI_IGNORE) != 0)
3276145522Sdarrenr				break;
3277145522Sdarrenr			if ((nflags & SI_CLONE) != 0) {
3278145522Sdarrenr				nat = fr_natclone(fin, nat);
3279145522Sdarrenr				if (nat == NULL)
3280145522Sdarrenr					break;
3281145522Sdarrenr			} else {
3282145522Sdarrenr				MUTEX_ENTER(&ipf_nat_new);
3283145522Sdarrenr				nat_stats.ns_wilds--;
3284145522Sdarrenr				MUTEX_EXIT(&ipf_nat_new);
3285145522Sdarrenr			}
3286145522Sdarrenr			nat->nat_oport = sport;
3287145522Sdarrenr			nat->nat_outport = dport;
3288145522Sdarrenr			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
3289145522Sdarrenr			nat_tabmove(nat);
329067614Sdarrenr			break;
329167614Sdarrenr		}
329267614Sdarrenr	}
3293145522Sdarrenr
3294145522Sdarrenr	MUTEX_DOWNGRADE(&ipf_nat);
3295145522Sdarrenr
329667614Sdarrenr	return nat;
329753642Sguido}
329853642Sguido
329953642Sguido
3300145522Sdarrenr/* ------------------------------------------------------------------------ */
3301145522Sdarrenr/* Function:    nat_tabmove                                                 */
3302145522Sdarrenr/* Returns:     Nil                                                         */
3303145522Sdarrenr/* Parameters:  nat(I) - pointer to NAT structure                           */
3304145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
3305145522Sdarrenr/*                                                                          */
3306145522Sdarrenr/* This function is only called for TCP/UDP NAT table entries where the     */
3307145522Sdarrenr/* original was placed in the table without hashing on the ports and we now */
3308145522Sdarrenr/* want to include hashing on port numbers.                                 */
3309145522Sdarrenr/* ------------------------------------------------------------------------ */
3310145522Sdarrenrstatic void nat_tabmove(nat)
331167614Sdarrenrnat_t *nat;
331267614Sdarrenr{
331367614Sdarrenr	nat_t **natp;
3314145522Sdarrenr	u_int hv;
331567614Sdarrenr
3316145522Sdarrenr	if (nat->nat_flags & SI_CLONE)
3317145522Sdarrenr		return;
331872006Sdarrenr
331967614Sdarrenr	/*
332067614Sdarrenr	 * Remove the NAT entry from the old location
332167614Sdarrenr	 */
332267614Sdarrenr	if (nat->nat_hnext[0])
332367614Sdarrenr		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
332467614Sdarrenr	*nat->nat_phnext[0] = nat->nat_hnext[0];
3325145522Sdarrenr	nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
332667614Sdarrenr
332767614Sdarrenr	if (nat->nat_hnext[1])
332867853Sdarrenr		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
332967614Sdarrenr	*nat->nat_phnext[1] = nat->nat_hnext[1];
3330145522Sdarrenr	nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
333167614Sdarrenr
333267853Sdarrenr	/*
333367853Sdarrenr	 * Add into the NAT table in the new position
333467853Sdarrenr	 */
3335145522Sdarrenr	hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 0xffffffff);
3336145522Sdarrenr	hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport,
3337145522Sdarrenr			 ipf_nattable_sz);
3338145522Sdarrenr	nat->nat_hv[0] = hv;
333967614Sdarrenr	natp = &nat_table[0][hv];
334067614Sdarrenr	if (*natp)
334167614Sdarrenr		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
334267614Sdarrenr	nat->nat_phnext[0] = natp;
334367614Sdarrenr	nat->nat_hnext[0] = *natp;
334467614Sdarrenr	*natp = nat;
3345145522Sdarrenr	nat_stats.ns_bucketlen[0][hv]++;
334667614Sdarrenr
3347145522Sdarrenr	hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 0xffffffff);
3348145522Sdarrenr	hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport,
3349145522Sdarrenr			 ipf_nattable_sz);
3350145522Sdarrenr	nat->nat_hv[1] = hv;
335167614Sdarrenr	natp = &nat_table[1][hv];
335267614Sdarrenr	if (*natp)
335367614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
335467614Sdarrenr	nat->nat_phnext[1] = natp;
335567614Sdarrenr	nat->nat_hnext[1] = *natp;
335667614Sdarrenr	*natp = nat;
3357145522Sdarrenr	nat_stats.ns_bucketlen[1][hv]++;
335867614Sdarrenr}
335967614Sdarrenr
336067614Sdarrenr
3361145522Sdarrenr/* ------------------------------------------------------------------------ */
3362145522Sdarrenr/* Function:    nat_outlookup                                               */
3363145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
3364145522Sdarrenr/*                       else pointer to matching NAT entry                 */
3365145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
3366145522Sdarrenr/*              flags(I) - NAT flags for this packet                        */
3367145522Sdarrenr/*              p(I)     - protocol for this packet                         */
3368145522Sdarrenr/*              src(I)   - source IP address                                */
3369145522Sdarrenr/*              dst(I)   - destination IP address                           */
3370145522Sdarrenr/*              rw(I)    - 1 == write lock on ipf_nat held, 0 == read lock. */
3371145522Sdarrenr/*                                                                          */
3372145522Sdarrenr/* Lookup a nat entry based on the source 'real' ip address/port and        */
3373145522Sdarrenr/* destination address/port.  We use this lookup when sending a packet out, */
3374145522Sdarrenr/* we're looking for a table entry, based on the source address.            */
3375145522Sdarrenr/*                                                                          */
3376145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
3377145522Sdarrenr/*                                                                          */
3378145522Sdarrenr/* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
3379145522Sdarrenr/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
3380145522Sdarrenr/*                                                                          */
3381145522Sdarrenr/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
3382145522Sdarrenr/*            the packet is of said protocol                                */
3383145522Sdarrenr/* ------------------------------------------------------------------------ */
3384145522Sdarrenrnat_t *nat_outlookup(fin, flags, p, src, dst)
338592685Sdarrenrfr_info_t *fin;
3386145522Sdarrenru_int flags, p;
338753642Sguidostruct in_addr src , dst;
338853642Sguido{
3389145522Sdarrenr	u_short sport, dport;
3390145522Sdarrenr	u_int sflags;
339192685Sdarrenr	ipnat_t *ipn;
339267614Sdarrenr	u_32_t srcip;
3393145522Sdarrenr	nat_t *nat;
3394145522Sdarrenr	int nflags;
339592685Sdarrenr	void *ifp;
339653642Sguido	u_int hv;
339753642Sguido
339892685Sdarrenr	ifp = fin->fin_ifp;
339967614Sdarrenr	srcip = src.s_addr;
3400145522Sdarrenr	sflags = flags & IPN_TCPUDPICMP;
3401145522Sdarrenr	sport = 0;
3402145522Sdarrenr	dport = 0;
3403145522Sdarrenr
3404145522Sdarrenr	switch (p)
3405145522Sdarrenr	{
3406145522Sdarrenr	case IPPROTO_TCP :
3407145522Sdarrenr	case IPPROTO_UDP :
3408145522Sdarrenr		sport = htons(fin->fin_data[0]);
3409145522Sdarrenr		dport = htons(fin->fin_data[1]);
3410145522Sdarrenr		break;
3411145522Sdarrenr	case IPPROTO_ICMP :
3412145522Sdarrenr		if (flags & IPN_ICMPERR)
3413145522Sdarrenr			sport = fin->fin_data[1];
3414145522Sdarrenr		else
3415145522Sdarrenr			dport = fin->fin_data[1];
3416145522Sdarrenr		break;
3417145522Sdarrenr	default :
3418145522Sdarrenr		break;
341992685Sdarrenr	}
342053642Sguido
3421145522Sdarrenr	if ((flags & SI_WILDP) != 0)
3422145522Sdarrenr		goto find_out_wild_ports;
3423145522Sdarrenr
342480482Sdarrenr	hv = NAT_HASH_FN(srcip, sport, 0xffffffff);
342580482Sdarrenr	hv = NAT_HASH_FN(dst.s_addr, hv + dport, ipf_nattable_sz);
342653642Sguido	nat = nat_table[0][hv];
342753642Sguido	for (; nat; nat = nat->nat_hnext[0]) {
3428161356Sguido		if (nat->nat_ifps[1] != NULL) {
3429161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
3430161356Sguido				continue;
3431161356Sguido		} else if (ifp != NULL)
3432161356Sguido			nat->nat_ifps[1] = ifp;
3433161356Sguido
343453642Sguido		nflags = nat->nat_flags;
343553642Sguido
3436145522Sdarrenr		if (nat->nat_inip.s_addr == srcip &&
343753642Sguido		    nat->nat_oip.s_addr == dst.s_addr &&
3438145522Sdarrenr		    (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP)))
3439145522Sdarrenr		     || (p == nat->nat_p))) {
344092685Sdarrenr			switch (p)
344192685Sdarrenr			{
3442145522Sdarrenr#if 0
3443145522Sdarrenr			case IPPROTO_GRE :
3444145522Sdarrenr				if (nat->nat_call[1] != fin->fin_data[0])
3445145522Sdarrenr					continue;
3446145522Sdarrenr				break;
3447145522Sdarrenr#endif
344892685Sdarrenr			case IPPROTO_TCP :
344992685Sdarrenr			case IPPROTO_UDP :
345092685Sdarrenr				if (nat->nat_oport != dport)
345192685Sdarrenr					continue;
345292685Sdarrenr				if (nat->nat_inport != sport)
345392685Sdarrenr					continue;
345492685Sdarrenr				break;
345592685Sdarrenr			default :
345692685Sdarrenr				break;
345792685Sdarrenr			}
345892685Sdarrenr
345992685Sdarrenr			ipn = nat->nat_ptr;
346092685Sdarrenr			if ((ipn != NULL) && (nat->nat_aps != NULL))
346192685Sdarrenr				if (appr_match(fin, nat) != 0)
346292685Sdarrenr					continue;
346353642Sguido			return nat;
346492685Sdarrenr		}
346553642Sguido	}
3466145522Sdarrenr
3467145522Sdarrenr	/*
3468145522Sdarrenr	 * So if we didn't find it but there are wildcard members in the hash
3469145522Sdarrenr	 * table, go back and look for them.  We do this search and update here
3470145522Sdarrenr	 * because it is modifying the NAT table and we want to do this only
3471145522Sdarrenr	 * for the first packet that matches.  The exception, of course, is
3472145522Sdarrenr	 * for "dummy" (FI_IGNORE) lookups.
3473145522Sdarrenr	 */
3474145522Sdarrenrfind_out_wild_ports:
3475145522Sdarrenr	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
347667614Sdarrenr		return NULL;
3477145522Sdarrenr	if (nat_stats.ns_wilds == 0)
3478145522Sdarrenr		return NULL;
347992685Sdarrenr
3480145522Sdarrenr	RWLOCK_EXIT(&ipf_nat);
3481145522Sdarrenr
3482145522Sdarrenr	hv = NAT_HASH_FN(srcip, 0, 0xffffffff);
3483145522Sdarrenr	hv = NAT_HASH_FN(dst.s_addr, hv, ipf_nattable_sz);
3484145522Sdarrenr
3485145522Sdarrenr	WRITE_ENTER(&ipf_nat);
3486145522Sdarrenr
348767614Sdarrenr	nat = nat_table[0][hv];
348867614Sdarrenr	for (; nat; nat = nat->nat_hnext[0]) {
3489161356Sguido		if (nat->nat_ifps[1] != NULL) {
3490161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
3491161356Sguido				continue;
3492161356Sguido		} else if (ifp != NULL)
3493161356Sguido			nat->nat_ifps[1] = ifp;
3494145522Sdarrenr
3495145522Sdarrenr		if (nat->nat_p != fin->fin_p)
349667614Sdarrenr			continue;
349767614Sdarrenr		if ((nat->nat_inip.s_addr != srcip) ||
349867614Sdarrenr		    (nat->nat_oip.s_addr != dst.s_addr))
349967614Sdarrenr			continue;
3500145522Sdarrenr
3501145522Sdarrenr		nflags = nat->nat_flags;
3502145522Sdarrenr		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
3503145522Sdarrenr			continue;
3504145522Sdarrenr
3505145522Sdarrenr		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
3506145522Sdarrenr			       NAT_OUTBOUND) == 1) {
3507145522Sdarrenr			if ((fin->fin_flx & FI_IGNORE) != 0)
3508145522Sdarrenr				break;
3509145522Sdarrenr			if ((nflags & SI_CLONE) != 0) {
3510145522Sdarrenr				nat = fr_natclone(fin, nat);
3511145522Sdarrenr				if (nat == NULL)
3512145522Sdarrenr					break;
3513145522Sdarrenr			} else {
3514145522Sdarrenr				MUTEX_ENTER(&ipf_nat_new);
3515145522Sdarrenr				nat_stats.ns_wilds--;
3516145522Sdarrenr				MUTEX_EXIT(&ipf_nat_new);
3517145522Sdarrenr			}
3518145522Sdarrenr			nat->nat_inport = sport;
3519145522Sdarrenr			nat->nat_oport = dport;
3520145522Sdarrenr			if (nat->nat_outport == 0)
3521145522Sdarrenr				nat->nat_outport = sport;
3522145522Sdarrenr			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
3523145522Sdarrenr			nat_tabmove(nat);
352467614Sdarrenr			break;
352567614Sdarrenr		}
352667614Sdarrenr	}
3527145522Sdarrenr
3528145522Sdarrenr	MUTEX_DOWNGRADE(&ipf_nat);
3529145522Sdarrenr
353067614Sdarrenr	return nat;
353153642Sguido}
353253642Sguido
353353642Sguido
3534145522Sdarrenr/* ------------------------------------------------------------------------ */
3535145522Sdarrenr/* Function:    nat_lookupredir                                             */
3536145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
3537145522Sdarrenr/*                       else pointer to matching NAT entry                 */
3538145522Sdarrenr/* Parameters:  np(I) - pointer to description of packet to find NAT table  */
3539145522Sdarrenr/*                      entry for.                                          */
3540145522Sdarrenr/*                                                                          */
3541145522Sdarrenr/* Lookup the NAT tables to search for a matching redirect                  */
3542161356Sguido/* The contents of natlookup_t should imitate those found in a packet that  */
3543161356Sguido/* would be translated - ie a packet coming in for RDR or going out for MAP.*/
3544161356Sguido/* We can do the lookup in one of two ways, imitating an inbound or         */
3545161356Sguido/* outbound  packet.  By default we assume outbound, unless IPN_IN is set.  */
3546161356Sguido/* For IN, the fields are set as follows:                                   */
3547161356Sguido/*     nl_real* = source information                                        */
3548161356Sguido/*     nl_out* = destination information (translated)                       */
3549161356Sguido/* For an out packet, the fields are set like this:                         */
3550161356Sguido/*     nl_in* = source information (untranslated)                           */
3551161356Sguido/*     nl_out* = destination information (translated)                       */
3552145522Sdarrenr/* ------------------------------------------------------------------------ */
355353642Sguidonat_t *nat_lookupredir(np)
3554145522Sdarrenrnatlookup_t *np;
355553642Sguido{
3556145522Sdarrenr	fr_info_t fi;
355753642Sguido	nat_t *nat;
355853642Sguido
355992685Sdarrenr	bzero((char *)&fi, sizeof(fi));
3560145522Sdarrenr	if (np->nl_flags & IPN_IN) {
3561145522Sdarrenr		fi.fin_data[0] = ntohs(np->nl_realport);
3562145522Sdarrenr		fi.fin_data[1] = ntohs(np->nl_outport);
3563145522Sdarrenr	} else {
3564145522Sdarrenr		fi.fin_data[0] = ntohs(np->nl_inport);
3565145522Sdarrenr		fi.fin_data[1] = ntohs(np->nl_outport);
3566145522Sdarrenr	}
3567145522Sdarrenr	if (np->nl_flags & IPN_TCP)
3568145522Sdarrenr		fi.fin_p = IPPROTO_TCP;
3569145522Sdarrenr	else if (np->nl_flags & IPN_UDP)
3570145522Sdarrenr		fi.fin_p = IPPROTO_UDP;
3571145522Sdarrenr	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
3572145522Sdarrenr		fi.fin_p = IPPROTO_ICMP;
357392685Sdarrenr
357453642Sguido	/*
3575145522Sdarrenr	 * We can do two sorts of lookups:
3576145522Sdarrenr	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
3577145522Sdarrenr	 * - default: we have the `in' and `out' address, look for `real'.
357853642Sguido	 */
3579145522Sdarrenr	if (np->nl_flags & IPN_IN) {
3580145522Sdarrenr		if ((nat = nat_inlookup(&fi, np->nl_flags, fi.fin_p,
3581145522Sdarrenr					np->nl_realip, np->nl_outip))) {
3582145522Sdarrenr			np->nl_inip = nat->nat_inip;
3583145522Sdarrenr			np->nl_inport = nat->nat_inport;
3584145522Sdarrenr		}
3585145522Sdarrenr	} else {
3586145522Sdarrenr		/*
3587145522Sdarrenr		 * If nl_inip is non null, this is a lookup based on the real
3588145522Sdarrenr		 * ip address. Else, we use the fake.
3589145522Sdarrenr		 */
3590145522Sdarrenr		if ((nat = nat_outlookup(&fi, np->nl_flags, fi.fin_p,
3591145522Sdarrenr					 np->nl_inip, np->nl_outip))) {
3592145522Sdarrenr
3593145522Sdarrenr			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
3594145522Sdarrenr				fr_info_t fin;
3595145522Sdarrenr				bzero((char *)&fin, sizeof(fin));
3596145522Sdarrenr				fin.fin_p = nat->nat_p;
3597145522Sdarrenr				fin.fin_data[0] = ntohs(nat->nat_outport);
3598145522Sdarrenr				fin.fin_data[1] = ntohs(nat->nat_oport);
3599145522Sdarrenr				if (nat_inlookup(&fin, np->nl_flags, fin.fin_p,
3600145522Sdarrenr						 nat->nat_outip,
3601145522Sdarrenr						 nat->nat_oip) != NULL) {
3602145522Sdarrenr					np->nl_flags &= ~IPN_FINDFORWARD;
3603145522Sdarrenr				}
3604145522Sdarrenr			}
3605145522Sdarrenr
3606145522Sdarrenr			np->nl_realip = nat->nat_outip;
3607145522Sdarrenr			np->nl_realport = nat->nat_outport;
3608145522Sdarrenr		}
3609145522Sdarrenr 	}
3610145522Sdarrenr
361153642Sguido	return nat;
361253642Sguido}
361353642Sguido
361453642Sguido
3615145522Sdarrenr/* ------------------------------------------------------------------------ */
3616145522Sdarrenr/* Function:    nat_match                                                   */
3617145522Sdarrenr/* Returns:     int - 0 == no match, 1 == match                             */
3618145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
3619145522Sdarrenr/*              np(I)    - pointer to NAT rule                              */
3620145522Sdarrenr/*                                                                          */
3621145522Sdarrenr/* Pull the matching of a packet against a NAT rule out of that complex     */
3622145522Sdarrenr/* loop inside fr_checknatin() and lay it out properly in its own function. */
3623145522Sdarrenr/* ------------------------------------------------------------------------ */
3624145522Sdarrenrstatic int nat_match(fin, np)
362560852Sdarrenrfr_info_t *fin;
362660852Sdarrenripnat_t *np;
362760852Sdarrenr{
362860852Sdarrenr	frtuc_t *ft;
362960852Sdarrenr
3630145522Sdarrenr	if (fin->fin_v != 4)
363160852Sdarrenr		return 0;
363260852Sdarrenr
363392685Sdarrenr	if (np->in_p && fin->fin_p != np->in_p)
363460852Sdarrenr		return 0;
3635145522Sdarrenr
363660852Sdarrenr	if (fin->fin_out) {
363763523Sdarrenr		if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK)))
363860852Sdarrenr			return 0;
363963523Sdarrenr		if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip)
364063523Sdarrenr		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
364160852Sdarrenr			return 0;
364263523Sdarrenr		if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip)
364363523Sdarrenr		    ^ ((np->in_flags & IPN_NOTDST) != 0))
364460852Sdarrenr			return 0;
364560852Sdarrenr	} else {
364663523Sdarrenr		if (!(np->in_redir & NAT_REDIRECT))
364760852Sdarrenr			return 0;
364863523Sdarrenr		if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip)
364963523Sdarrenr		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
365063523Sdarrenr			return 0;
365163523Sdarrenr		if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip)
365263523Sdarrenr		    ^ ((np->in_flags & IPN_NOTDST) != 0))
365363523Sdarrenr			return 0;
365460852Sdarrenr	}
365560852Sdarrenr
365660852Sdarrenr	ft = &np->in_tuc;
3657145522Sdarrenr	if (!(fin->fin_flx & FI_TCPUDP) ||
3658145522Sdarrenr	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
365960852Sdarrenr		if (ft->ftu_scmp || ft->ftu_dcmp)
366060852Sdarrenr			return 0;
366160852Sdarrenr		return 1;
366260852Sdarrenr	}
366360852Sdarrenr
3664145522Sdarrenr	return fr_tcpudpchk(fin, ft);
366560852Sdarrenr}
366660852Sdarrenr
366760852Sdarrenr
3668145522Sdarrenr/* ------------------------------------------------------------------------ */
3669145522Sdarrenr/* Function:    nat_update                                                  */
3670145522Sdarrenr/* Returns:     Nil                                                         */
3671145522Sdarrenr/* Parameters:  nat(I)    - pointer to NAT structure                        */
3672145522Sdarrenr/*              np(I)     - pointer to NAT rule                             */
3673145522Sdarrenr/*                                                                          */
3674145522Sdarrenr/* Updates the lifetime of a NAT table entry for non-TCP packets.  Must be  */
3675145522Sdarrenr/* called with fin_rev updated - i.e. after calling nat_proto().            */
3676145522Sdarrenr/* ------------------------------------------------------------------------ */
3677145522Sdarrenrvoid nat_update(fin, nat, np)
367853642Sguidofr_info_t *fin;
3679145522Sdarrenrnat_t *nat;
3680145522Sdarrenripnat_t *np;
368153642Sguido{
3682145522Sdarrenr	ipftq_t *ifq, *ifq2;
3683145522Sdarrenr	ipftqent_t *tqe;
3684145522Sdarrenr
3685145522Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
3686145522Sdarrenr	tqe = &nat->nat_tqe;
3687145522Sdarrenr	ifq = tqe->tqe_ifq;
3688145522Sdarrenr
3689145522Sdarrenr	/*
3690145522Sdarrenr	 * We allow over-riding of NAT timeouts from NAT rules, even for
3691145522Sdarrenr	 * TCP, however, if it is TCP and there is no rule timeout set,
3692145522Sdarrenr	 * then do not update the timeout here.
3693145522Sdarrenr	 */
3694145522Sdarrenr	if (np != NULL)
3695145522Sdarrenr		ifq2 = np->in_tqehead[fin->fin_rev];
3696145522Sdarrenr	else
3697145522Sdarrenr		ifq2 = NULL;
3698145522Sdarrenr
3699145522Sdarrenr	if (nat->nat_p == IPPROTO_TCP && ifq2 == NULL) {
3700172776Sdarrenr		u_32_t end, ack;
3701172776Sdarrenr		u_char tcpflags;
3702172776Sdarrenr		tcphdr_t *tcp;
3703172776Sdarrenr		int dsize;
3704172776Sdarrenr
3705172776Sdarrenr		tcp = fin->fin_dp;
3706172776Sdarrenr		tcpflags = tcp->th_flags;
3707172776Sdarrenr		dsize = fin->fin_dlen - (TCP_OFF(tcp) << 2) +
3708172776Sdarrenr			((tcpflags & TH_SYN) ? 1 : 0) +
3709172776Sdarrenr			((tcpflags & TH_FIN) ? 1 : 0);
3710172776Sdarrenr
3711172776Sdarrenr		ack = ntohl(tcp->th_ack);
3712172776Sdarrenr		end = ntohl(tcp->th_seq) + dsize;
3713172776Sdarrenr
3714172776Sdarrenr		if (SEQ_GT(ack, nat->nat_seqnext[1 - fin->fin_rev]))
3715172776Sdarrenr			nat->nat_seqnext[1 - fin->fin_rev] = ack;
3716172776Sdarrenr
3717172776Sdarrenr		if (nat->nat_seqnext[fin->fin_rev] == 0)
3718172776Sdarrenr			nat->nat_seqnext[fin->fin_rev] = end;
3719172776Sdarrenr
3720145522Sdarrenr		(void) fr_tcp_age(&nat->nat_tqe, fin, nat_tqb, 0);
3721145522Sdarrenr	} else {
3722145522Sdarrenr		if (ifq2 == NULL) {
3723145522Sdarrenr			if (nat->nat_p == IPPROTO_UDP)
3724145522Sdarrenr				ifq2 = &nat_udptq;
3725145522Sdarrenr			else if (nat->nat_p == IPPROTO_ICMP)
3726145522Sdarrenr				ifq2 = &nat_icmptq;
3727145522Sdarrenr			else
3728145522Sdarrenr				ifq2 = &nat_iptq;
3729145522Sdarrenr		}
3730145522Sdarrenr
3731145522Sdarrenr		fr_movequeue(tqe, ifq, ifq2);
3732145522Sdarrenr	}
3733145522Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
3734145522Sdarrenr}
3735145522Sdarrenr
3736145522Sdarrenr
3737145522Sdarrenr/* ------------------------------------------------------------------------ */
3738145522Sdarrenr/* Function:    fr_checknatout                                              */
3739145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
3740145522Sdarrenr/*                     0 == no packet translation occurred,                 */
3741145522Sdarrenr/*                     1 == packet was successfully translated.             */
3742145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
3743145522Sdarrenr/*              passp(I) - pointer to filtering result flags                */
3744145522Sdarrenr/*                                                                          */
3745145522Sdarrenr/* Check to see if an outcoming packet should be changed.  ICMP packets are */
3746145522Sdarrenr/* first checked to see if they match an existing entry (if an error),      */
3747145522Sdarrenr/* otherwise a search of the current NAT table is made.  If neither results */
3748145522Sdarrenr/* in a match then a search for a matching NAT rule is made.  Create a new  */
3749145522Sdarrenr/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
3750145522Sdarrenr/* packet header(s) as required.                                            */
3751145522Sdarrenr/* ------------------------------------------------------------------------ */
3752145522Sdarrenrint fr_checknatout(fin, passp)
3753145522Sdarrenrfr_info_t *fin;
3754145522Sdarrenru_32_t *passp;
3755145522Sdarrenr{
3756145522Sdarrenr	struct ifnet *ifp, *sifp;
3757145522Sdarrenr	icmphdr_t *icmp = NULL;
375853642Sguido	tcphdr_t *tcp = NULL;
3759145522Sdarrenr	int rval, natfailed;
3760145522Sdarrenr	ipnat_t *np = NULL;
3761145522Sdarrenr	u_int nflags = 0;
3762145522Sdarrenr	u_32_t ipa, iph;
3763145522Sdarrenr	int natadd = 1;
376453642Sguido	frentry_t *fr;
376553642Sguido	nat_t *nat;
376653642Sguido
3767145522Sdarrenr	if (nat_stats.ns_rules == 0 || fr_nat_lock != 0)
376853642Sguido		return 0;
376953642Sguido
3770145522Sdarrenr	natfailed = 0;
3771145522Sdarrenr	fr = fin->fin_fr;
3772145522Sdarrenr	sifp = fin->fin_ifp;
3773170268Sdarrenr	if (fr != NULL) {
3774170268Sdarrenr		ifp = fr->fr_tifs[fin->fin_rev].fd_ifp;
3775170268Sdarrenr		if ((ifp != NULL) && (ifp != (void *)-1))
3776170268Sdarrenr			fin->fin_ifp = ifp;
3777170268Sdarrenr	}
377892685Sdarrenr	ifp = fin->fin_ifp;
377953642Sguido
3780145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
3781145522Sdarrenr		switch (fin->fin_p)
3782145522Sdarrenr		{
3783145522Sdarrenr		case IPPROTO_TCP :
378453642Sguido			nflags = IPN_TCP;
3785145522Sdarrenr			break;
3786145522Sdarrenr		case IPPROTO_UDP :
378753642Sguido			nflags = IPN_UDP;
3788145522Sdarrenr			break;
3789145522Sdarrenr		case IPPROTO_ICMP :
3790145522Sdarrenr			icmp = fin->fin_dp;
3791145522Sdarrenr
3792145522Sdarrenr			/*
3793145522Sdarrenr			 * This is an incoming packet, so the destination is
3794145522Sdarrenr			 * the icmp_id and the source port equals 0
3795145522Sdarrenr			 */
3796145522Sdarrenr			if (nat_icmpquerytype4(icmp->icmp_type))
3797145522Sdarrenr				nflags = IPN_ICMPQUERY;
3798145522Sdarrenr			break;
3799145522Sdarrenr		default :
3800145522Sdarrenr			break;
380153642Sguido		}
3802145522Sdarrenr
3803145522Sdarrenr		if ((nflags & IPN_TCPUDP))
3804145522Sdarrenr			tcp = fin->fin_dp;
380553642Sguido	}
380653642Sguido
380792685Sdarrenr	ipa = fin->fin_saddr;
380853642Sguido
380953642Sguido	READ_ENTER(&ipf_nat);
381060852Sdarrenr
3811180778Sdarrenr	if (((fin->fin_flx & FI_ICMPERR) != 0) &&
3812145522Sdarrenr	    (nat = nat_icmperror(fin, &nflags, NAT_OUTBOUND)))
3813145522Sdarrenr		/*EMPTY*/;
3814145522Sdarrenr	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
381553642Sguido		natadd = 0;
3816145522Sdarrenr	else if ((nat = nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p,
3817145522Sdarrenr				      fin->fin_src, fin->fin_dst))) {
381853642Sguido		nflags = nat->nat_flags;
381953642Sguido	} else {
3820145522Sdarrenr		u_32_t hv, msk, nmsk;
382192685Sdarrenr
382253642Sguido		/*
382353642Sguido		 * If there is no current entry in the nat table for this IP#,
382453642Sguido		 * create one for it (if there is a matching rule).
382553642Sguido		 */
3826145522Sdarrenr		RWLOCK_EXIT(&ipf_nat);
3827145522Sdarrenr		msk = 0xffffffff;
3828145522Sdarrenr		nmsk = nat_masks;
3829145522Sdarrenr		WRITE_ENTER(&ipf_nat);
383053642Sguidomaskloop:
383153642Sguido		iph = ipa & htonl(msk);
383260852Sdarrenr		hv = NAT_HASH_FN(iph, 0, ipf_natrules_sz);
383353642Sguido		for (np = nat_rules[hv]; np; np = np->in_mnext)
383453642Sguido		{
3835161356Sguido			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
383660852Sdarrenr				continue;
3837145522Sdarrenr			if (np->in_v != fin->fin_v)
383860852Sdarrenr				continue;
3839145522Sdarrenr			if (np->in_p && (np->in_p != fin->fin_p))
3840145522Sdarrenr				continue;
3841145522Sdarrenr			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
3842145522Sdarrenr				continue;
384360852Sdarrenr			if (np->in_flags & IPN_FILTER) {
3844145522Sdarrenr				if (!nat_match(fin, np))
384560852Sdarrenr					continue;
384660852Sdarrenr			} else if ((ipa & np->in_inmsk) != np->in_inip)
384760852Sdarrenr				continue;
3848145522Sdarrenr
3849145522Sdarrenr			if ((fr != NULL) &&
3850145522Sdarrenr			    !fr_matchtag(&np->in_tag, &fr->fr_nattag))
385192685Sdarrenr				continue;
3852145522Sdarrenr
3853145522Sdarrenr			if (*np->in_plabel != '\0') {
3854145522Sdarrenr				if (((np->in_flags & IPN_FILTER) == 0) &&
3855145522Sdarrenr				    (np->in_dport != tcp->th_dport))
3856145522Sdarrenr					continue;
3857145522Sdarrenr				if (appr_ok(fin, tcp, np) == 0)
3858145522Sdarrenr					continue;
3859145522Sdarrenr			}
3860145522Sdarrenr
3861145522Sdarrenr			if ((nat = nat_new(fin, np, NULL, nflags,
3862145522Sdarrenr					   NAT_OUTBOUND))) {
386392685Sdarrenr				np->in_hits++;
386492685Sdarrenr				break;
3865145522Sdarrenr			} else
3866145522Sdarrenr				natfailed = -1;
386753642Sguido		}
3868145522Sdarrenr		if ((np == NULL) && (nmsk != 0)) {
3869145522Sdarrenr			while (nmsk) {
387053642Sguido				msk <<= 1;
3871145522Sdarrenr				if (nmsk & 0x80000000)
3872145522Sdarrenr					break;
3873145522Sdarrenr				nmsk <<= 1;
3874145522Sdarrenr			}
3875145522Sdarrenr			if (nmsk != 0) {
3876145522Sdarrenr				nmsk <<= 1;
387753642Sguido				goto maskloop;
3878145522Sdarrenr			}
387953642Sguido		}
388053642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
388153642Sguido	}
388253642Sguido
3883145522Sdarrenr	if (nat != NULL) {
3884145522Sdarrenr		rval = fr_natout(fin, nat, natadd, nflags);
3885145522Sdarrenr		if (rval == 1) {
3886145522Sdarrenr			MUTEX_ENTER(&nat->nat_lock);
3887145522Sdarrenr			nat->nat_ref++;
3888145522Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
3889170268Sdarrenr			nat->nat_touched = fr_ticks;
3890145522Sdarrenr			fin->fin_nat = nat;
3891145522Sdarrenr		}
3892145522Sdarrenr	} else
3893145522Sdarrenr		rval = natfailed;
3894145522Sdarrenr	RWLOCK_EXIT(&ipf_nat);
3895145522Sdarrenr
3896145522Sdarrenr	if (rval == -1) {
3897145522Sdarrenr		if (passp != NULL)
3898145522Sdarrenr			*passp = FR_BLOCK;
3899145522Sdarrenr		fin->fin_flx |= FI_BADNAT;
3900145522Sdarrenr	}
3901145522Sdarrenr	fin->fin_ifp = sifp;
3902145522Sdarrenr	return rval;
3903145522Sdarrenr}
3904145522Sdarrenr
3905145522Sdarrenr/* ------------------------------------------------------------------------ */
3906145522Sdarrenr/* Function:    fr_natout                                                   */
3907145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
3908145522Sdarrenr/*                     1 == packet was successfully translated.             */
3909145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
3910145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
3911145522Sdarrenr/*              natadd(I) - flag indicating if it is safe to add frag cache */
3912145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
3913145522Sdarrenr/*                                                                          */
3914145522Sdarrenr/* Translate a packet coming "out" on an interface.                         */
3915145522Sdarrenr/* ------------------------------------------------------------------------ */
3916145522Sdarrenrint fr_natout(fin, nat, natadd, nflags)
3917145522Sdarrenrfr_info_t *fin;
3918145522Sdarrenrnat_t *nat;
3919145522Sdarrenrint natadd;
3920145522Sdarrenru_32_t nflags;
3921145522Sdarrenr{
3922145522Sdarrenr	icmphdr_t *icmp;
3923145522Sdarrenr	u_short *csump;
3924145522Sdarrenr	tcphdr_t *tcp;
3925145522Sdarrenr	ipnat_t *np;
3926145522Sdarrenr	int i;
3927145522Sdarrenr
3928145522Sdarrenr	tcp = NULL;
3929145522Sdarrenr	icmp = NULL;
3930145522Sdarrenr	csump = NULL;
3931145522Sdarrenr	np = nat->nat_ptr;
3932145522Sdarrenr
3933145522Sdarrenr	if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL))
3934145522Sdarrenr		(void) fr_nat_newfrag(fin, 0, nat);
3935145522Sdarrenr
3936145522Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
3937145522Sdarrenr	nat->nat_bytes[1] += fin->fin_plen;
3938145522Sdarrenr	nat->nat_pkts[1]++;
3939145522Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
3940145522Sdarrenr
394172006Sdarrenr	/*
3942145522Sdarrenr	 * Fix up checksums, not by recalculating them, but
3943145522Sdarrenr	 * simply computing adjustments.
3944145522Sdarrenr	 * This is only done for STREAMS based IP implementations where the
3945145522Sdarrenr	 * checksum has already been calculated by IP.  In all other cases,
3946145522Sdarrenr	 * IPFilter is called before the checksum needs calculating so there
3947145522Sdarrenr	 * is no call to modify whatever is in the header now.
394872006Sdarrenr	 */
3949145522Sdarrenr	if (fin->fin_v == 4) {
395063523Sdarrenr		if (nflags == IPN_ICMPERR) {
395163523Sdarrenr			u_32_t s1, s2, sumd;
395263523Sdarrenr
395392685Sdarrenr			s1 = LONG_SUM(ntohl(fin->fin_saddr));
395463523Sdarrenr			s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr));
395563523Sdarrenr			CALC_SUMD(s1, s2, sumd);
3956145522Sdarrenr			fix_outcksum(fin, &fin->fin_ip->ip_sum, sumd);
395763523Sdarrenr		}
3958153876Sguido#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
3959153876Sguido    defined(linux) || defined(BRIDGE_IPF)
396063523Sdarrenr		else {
3961153876Sguido			/*
3962153876Sguido			 * Strictly speaking, this isn't necessary on BSD
3963153876Sguido			 * kernels because they do checksum calculation after
3964153876Sguido			 * this code has run BUT if ipfilter is being used
3965153876Sguido			 * to do NAT as a bridge, that code doesn't exist.
3966153876Sguido			 */
396763523Sdarrenr			if (nat->nat_dir == NAT_OUTBOUND)
3968145522Sdarrenr				fix_outcksum(fin, &fin->fin_ip->ip_sum,
3969145522Sdarrenr					     nat->nat_ipsumd);
397063523Sdarrenr			else
3971145522Sdarrenr				fix_incksum(fin, &fin->fin_ip->ip_sum,
3972145522Sdarrenr					    nat->nat_ipsumd);
397363523Sdarrenr		}
397453642Sguido#endif
3975145522Sdarrenr	}
397653642Sguido
3977145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
3978145522Sdarrenr		if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) {
3979145522Sdarrenr			tcp = fin->fin_dp;
398053642Sguido
3981145522Sdarrenr			tcp->th_sport = nat->nat_outport;
3982145522Sdarrenr			fin->fin_data[0] = ntohs(nat->nat_outport);
3983145522Sdarrenr		}
398453642Sguido
3985145522Sdarrenr		if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) {
3986145522Sdarrenr			icmp = fin->fin_dp;
3987145522Sdarrenr			icmp->icmp_id = nat->nat_outport;
3988145522Sdarrenr		}
3989110916Sdarrenr
3990145522Sdarrenr		csump = nat_proto(fin, nat, nflags);
3991145522Sdarrenr	}
3992110916Sdarrenr
3993145522Sdarrenr	fin->fin_ip->ip_src = nat->nat_outip;
399453642Sguido
3995145522Sdarrenr	nat_update(fin, nat, np);
399663523Sdarrenr
3997145522Sdarrenr	/*
3998145522Sdarrenr	 * The above comments do not hold for layer 4 (or higher) checksums...
3999145522Sdarrenr	 */
4000145522Sdarrenr	if (csump != NULL) {
4001145522Sdarrenr		if (nat->nat_dir == NAT_OUTBOUND)
4002145522Sdarrenr			fix_outcksum(fin, csump, nat->nat_sumd[1]);
4003145522Sdarrenr		else
4004145522Sdarrenr			fix_incksum(fin, csump, nat->nat_sumd[1]);
4005145522Sdarrenr	}
4006145522Sdarrenr#ifdef	IPFILTER_SYNC
4007145522Sdarrenr	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
4008145522Sdarrenr#endif
4009145522Sdarrenr	/* ------------------------------------------------------------- */
4010145522Sdarrenr	/* A few quick notes:						 */
4011145522Sdarrenr	/*	Following are test conditions prior to calling the 	 */
4012145522Sdarrenr	/*	appr_check routine.					 */
4013145522Sdarrenr	/*								 */
4014145522Sdarrenr	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
4015145522Sdarrenr	/*	with a redirect rule, we attempt to match the packet's	 */
4016145522Sdarrenr	/*	source port against in_dport, otherwise	we'd compare the */
4017145522Sdarrenr	/*	packet's destination.			 		 */
4018145522Sdarrenr	/* ------------------------------------------------------------- */
4019145522Sdarrenr	if ((np != NULL) && (np->in_apr != NULL)) {
4020145522Sdarrenr		i = appr_check(fin, nat);
4021145522Sdarrenr		if (i == 0)
402260852Sdarrenr			i = 1;
4023145522Sdarrenr	} else
4024145522Sdarrenr		i = 1;
4025145522Sdarrenr	ATOMIC_INCL(nat_stats.ns_mapped[1]);
4026145522Sdarrenr	fin->fin_flx |= FI_NATED;
4027145522Sdarrenr	return i;
402853642Sguido}
402953642Sguido
403053642Sguido
4031145522Sdarrenr/* ------------------------------------------------------------------------ */
4032145522Sdarrenr/* Function:    fr_checknatin                                               */
4033145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
4034145522Sdarrenr/*                     0 == no packet translation occurred,                 */
4035145522Sdarrenr/*                     1 == packet was successfully translated.             */
4036145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4037145522Sdarrenr/*              passp(I) - pointer to filtering result flags                */
4038145522Sdarrenr/*                                                                          */
4039145522Sdarrenr/* Check to see if an incoming packet should be changed.  ICMP packets are  */
4040145522Sdarrenr/* first checked to see if they match an existing entry (if an error),      */
4041145522Sdarrenr/* otherwise a search of the current NAT table is made.  If neither results */
4042145522Sdarrenr/* in a match then a search for a matching NAT rule is made.  Create a new  */
4043145522Sdarrenr/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
4044145522Sdarrenr/* packet header(s) as required.                                            */
4045145522Sdarrenr/* ------------------------------------------------------------------------ */
4046145522Sdarrenrint fr_checknatin(fin, passp)
404753642Sguidofr_info_t *fin;
4048145522Sdarrenru_32_t *passp;
404953642Sguido{
4050145522Sdarrenr	u_int nflags, natadd;
4051145522Sdarrenr	int rval, natfailed;
4052145522Sdarrenr	struct ifnet *ifp;
4053145522Sdarrenr	struct in_addr in;
4054145522Sdarrenr	icmphdr_t *icmp;
4055145522Sdarrenr	tcphdr_t *tcp;
4056145522Sdarrenr	u_short dport;
4057145522Sdarrenr	ipnat_t *np;
405853642Sguido	nat_t *nat;
405953642Sguido	u_32_t iph;
406053642Sguido
4061145522Sdarrenr	if (nat_stats.ns_rules == 0 || fr_nat_lock != 0)
406253642Sguido		return 0;
406353642Sguido
4064145522Sdarrenr	tcp = NULL;
4065145522Sdarrenr	icmp = NULL;
4066145522Sdarrenr	dport = 0;
4067145522Sdarrenr	natadd = 1;
4068145522Sdarrenr	nflags = 0;
4069145522Sdarrenr	natfailed = 0;
4070145522Sdarrenr	ifp = fin->fin_ifp;
4071145522Sdarrenr
4072145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
4073145522Sdarrenr		switch (fin->fin_p)
4074145522Sdarrenr		{
4075145522Sdarrenr		case IPPROTO_TCP :
407653642Sguido			nflags = IPN_TCP;
4077145522Sdarrenr			break;
4078145522Sdarrenr		case IPPROTO_UDP :
407953642Sguido			nflags = IPN_UDP;
4080145522Sdarrenr			break;
4081145522Sdarrenr		case IPPROTO_ICMP :
4082145522Sdarrenr			icmp = fin->fin_dp;
4083145522Sdarrenr
4084145522Sdarrenr			/*
4085145522Sdarrenr			 * This is an incoming packet, so the destination is
4086145522Sdarrenr			 * the icmp_id and the source port equals 0
4087145522Sdarrenr			 */
4088145522Sdarrenr			if (nat_icmpquerytype4(icmp->icmp_type)) {
4089145522Sdarrenr				nflags = IPN_ICMPQUERY;
4090145522Sdarrenr				dport = icmp->icmp_id;
4091145522Sdarrenr			} break;
4092145522Sdarrenr		default :
4093145522Sdarrenr			break;
4094145522Sdarrenr		}
4095145522Sdarrenr
409653642Sguido		if ((nflags & IPN_TCPUDP)) {
4097145522Sdarrenr			tcp = fin->fin_dp;
409853642Sguido			dport = tcp->th_dport;
409953642Sguido		}
410053642Sguido	}
410153642Sguido
410292685Sdarrenr	in = fin->fin_dst;
410353642Sguido
410453642Sguido	READ_ENTER(&ipf_nat);
410553642Sguido
4106180778Sdarrenr	if (((fin->fin_flx & FI_ICMPERR) != 0) &&
4107145522Sdarrenr	    (nat = nat_icmperror(fin, &nflags, NAT_INBOUND)))
4108145522Sdarrenr		/*EMPTY*/;
4109145522Sdarrenr	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
411053642Sguido		natadd = 0;
4111145522Sdarrenr	else if ((nat = nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p,
4112145522Sdarrenr				     fin->fin_src, in))) {
411353642Sguido		nflags = nat->nat_flags;
411453642Sguido	} else {
4115145522Sdarrenr		u_32_t hv, msk, rmsk;
4116145522Sdarrenr
411753642Sguido		RWLOCK_EXIT(&ipf_nat);
4118145522Sdarrenr		rmsk = rdr_masks;
411992685Sdarrenr		msk = 0xffffffff;
412053642Sguido		WRITE_ENTER(&ipf_nat);
412153642Sguido		/*
412253642Sguido		 * If there is no current entry in the nat table for this IP#,
412353642Sguido		 * create one for it (if there is a matching rule).
412453642Sguido		 */
412553642Sguidomaskloop:
412653642Sguido		iph = in.s_addr & htonl(msk);
412760852Sdarrenr		hv = NAT_HASH_FN(iph, 0, ipf_rdrrules_sz);
412860852Sdarrenr		for (np = rdr_rules[hv]; np; np = np->in_rnext) {
4129145522Sdarrenr			if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
413060852Sdarrenr				continue;
4131145522Sdarrenr			if (np->in_v != fin->fin_v)
4132138947Sdarrenr				continue;
4133145522Sdarrenr			if (np->in_p && (np->in_p != fin->fin_p))
4134145522Sdarrenr				continue;
4135145522Sdarrenr			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
4136145522Sdarrenr				continue;
413760852Sdarrenr			if (np->in_flags & IPN_FILTER) {
4138145522Sdarrenr				if (!nat_match(fin, np))
413960852Sdarrenr					continue;
4140145522Sdarrenr			} else {
4141145522Sdarrenr				if ((in.s_addr & np->in_outmsk) != np->in_outip)
4142145522Sdarrenr					continue;
4143145522Sdarrenr				if (np->in_pmin &&
4144145522Sdarrenr				    ((ntohs(np->in_pmax) < ntohs(dport)) ||
4145145522Sdarrenr				     (ntohs(dport) < ntohs(np->in_pmin))))
4146145522Sdarrenr					continue;
4147145522Sdarrenr			}
4148145522Sdarrenr
4149145522Sdarrenr			if (*np->in_plabel != '\0') {
4150145522Sdarrenr				if (!appr_ok(fin, tcp, np)) {
4151145522Sdarrenr					continue;
415253642Sguido				}
4153145522Sdarrenr			}
4154145522Sdarrenr
4155145522Sdarrenr			nat = nat_new(fin, np, NULL, nflags, NAT_INBOUND);
4156145522Sdarrenr			if (nat != NULL) {
4157145522Sdarrenr				np->in_hits++;
4158145522Sdarrenr				break;
4159145522Sdarrenr			} else
4160145522Sdarrenr				natfailed = -1;
416160852Sdarrenr		}
416260852Sdarrenr
4163145522Sdarrenr		if ((np == NULL) && (rmsk != 0)) {
4164145522Sdarrenr			while (rmsk) {
416553642Sguido				msk <<= 1;
4166145522Sdarrenr				if (rmsk & 0x80000000)
4167145522Sdarrenr					break;
4168145522Sdarrenr				rmsk <<= 1;
4169145522Sdarrenr			}
4170145522Sdarrenr			if (rmsk != 0) {
4171145522Sdarrenr				rmsk <<= 1;
417253642Sguido				goto maskloop;
4173145522Sdarrenr			}
417453642Sguido		}
417553642Sguido		MUTEX_DOWNGRADE(&ipf_nat);
417653642Sguido	}
4177145522Sdarrenr	if (nat != NULL) {
4178145522Sdarrenr		rval = fr_natin(fin, nat, natadd, nflags);
4179145522Sdarrenr		if (rval == 1) {
4180145522Sdarrenr			MUTEX_ENTER(&nat->nat_lock);
4181145522Sdarrenr			nat->nat_ref++;
4182145522Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
4183170268Sdarrenr			nat->nat_touched = fr_ticks;
4184145522Sdarrenr			fin->fin_nat = nat;
4185145522Sdarrenr		}
4186145522Sdarrenr	} else
4187145522Sdarrenr		rval = natfailed;
4188145522Sdarrenr	RWLOCK_EXIT(&ipf_nat);
418972006Sdarrenr
4190145522Sdarrenr	if (rval == -1) {
4191145522Sdarrenr		if (passp != NULL)
4192145522Sdarrenr			*passp = FR_BLOCK;
4193145522Sdarrenr		fin->fin_flx |= FI_BADNAT;
4194145522Sdarrenr	}
4195145522Sdarrenr	return rval;
4196145522Sdarrenr}
4197145522Sdarrenr
4198145522Sdarrenr
4199145522Sdarrenr/* ------------------------------------------------------------------------ */
4200145522Sdarrenr/* Function:    fr_natin                                                    */
4201145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
4202145522Sdarrenr/*                     1 == packet was successfully translated.             */
4203145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
4204145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
4205145522Sdarrenr/*              natadd(I) - flag indicating if it is safe to add frag cache */
4206145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
4207145522Sdarrenr/* Locks Held:  ipf_nat (READ)                                              */
4208145522Sdarrenr/*                                                                          */
4209145522Sdarrenr/* Translate a packet coming "in" on an interface.                          */
4210145522Sdarrenr/* ------------------------------------------------------------------------ */
4211145522Sdarrenrint fr_natin(fin, nat, natadd, nflags)
4212145522Sdarrenrfr_info_t *fin;
4213145522Sdarrenrnat_t *nat;
4214145522Sdarrenrint natadd;
4215145522Sdarrenru_32_t nflags;
4216145522Sdarrenr{
4217145522Sdarrenr	icmphdr_t *icmp;
4218145522Sdarrenr	u_short *csump;
4219145522Sdarrenr	tcphdr_t *tcp;
4220145522Sdarrenr	ipnat_t *np;
4221145522Sdarrenr	int i;
4222145522Sdarrenr
4223145522Sdarrenr	tcp = NULL;
4224145522Sdarrenr	csump = NULL;
4225145522Sdarrenr	np = nat->nat_ptr;
4226145522Sdarrenr	fin->fin_fr = nat->nat_fr;
4227145522Sdarrenr
4228145522Sdarrenr	if (np != NULL) {
4229145522Sdarrenr		if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
4230145522Sdarrenr			(void) fr_nat_newfrag(fin, 0, nat);
4231145522Sdarrenr
4232145522Sdarrenr	/* ------------------------------------------------------------- */
4233145522Sdarrenr	/* A few quick notes:						 */
4234145522Sdarrenr	/*	Following are test conditions prior to calling the 	 */
4235145522Sdarrenr	/*	appr_check routine.					 */
4236145522Sdarrenr	/*								 */
4237145522Sdarrenr	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
4238145522Sdarrenr	/*	with a map rule, we attempt to match the packet's	 */
4239145522Sdarrenr	/*	source port against in_dport, otherwise	we'd compare the */
4240145522Sdarrenr	/*	packet's destination.			 		 */
4241145522Sdarrenr	/* ------------------------------------------------------------- */
4242145522Sdarrenr		if (np->in_apr != NULL) {
4243145522Sdarrenr			i = appr_check(fin, nat);
424460852Sdarrenr			if (i == -1) {
4245145522Sdarrenr				return -1;
424660852Sdarrenr			}
424760852Sdarrenr		}
4248145522Sdarrenr	}
424953642Sguido
4250145522Sdarrenr#ifdef	IPFILTER_SYNC
4251145522Sdarrenr	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
4252145522Sdarrenr#endif
4253145522Sdarrenr
4254145522Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
4255145522Sdarrenr	nat->nat_bytes[0] += fin->fin_plen;
4256145522Sdarrenr	nat->nat_pkts[0]++;
4257145522Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
4258145522Sdarrenr
4259145522Sdarrenr	fin->fin_ip->ip_dst = nat->nat_inip;
4260145522Sdarrenr	fin->fin_fi.fi_daddr = nat->nat_inip.s_addr;
4261145522Sdarrenr	if (nflags & IPN_TCPUDP)
4262145522Sdarrenr		tcp = fin->fin_dp;
4263145522Sdarrenr
4264145522Sdarrenr	/*
4265145522Sdarrenr	 * Fix up checksums, not by recalculating them, but
4266145522Sdarrenr	 * simply computing adjustments.
4267145522Sdarrenr	 * Why only do this for some platforms on inbound packets ?
4268145522Sdarrenr	 * Because for those that it is done, IP processing is yet to happen
4269145522Sdarrenr	 * and so the IPv4 header checksum has not yet been evaluated.
4270145522Sdarrenr	 * Perhaps it should always be done for the benefit of things like
4271145522Sdarrenr	 * fast forwarding (so that it doesn't need to be recomputed) but with
4272145522Sdarrenr	 * header checksum offloading, perhaps it is a moot point.
4273145522Sdarrenr	 */
4274145522Sdarrenr#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
4275145522Sdarrenr     defined(__osf__) || defined(linux)
4276145522Sdarrenr	if (nat->nat_dir == NAT_OUTBOUND)
4277145522Sdarrenr		fix_incksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd);
4278145522Sdarrenr	else
4279145522Sdarrenr		fix_outcksum(fin, &fin->fin_ip->ip_sum, nat->nat_ipsumd);
4280145522Sdarrenr#endif
4281145522Sdarrenr
4282145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
4283145522Sdarrenr		if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) {
4284145522Sdarrenr			tcp->th_dport = nat->nat_inport;
4285145522Sdarrenr			fin->fin_data[1] = ntohs(nat->nat_inport);
428692685Sdarrenr		}
428753642Sguido
4288145522Sdarrenr
4289145522Sdarrenr		if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) {
4290145522Sdarrenr			icmp = fin->fin_dp;
4291145522Sdarrenr
4292145522Sdarrenr			icmp->icmp_id = nat->nat_inport;
4293145522Sdarrenr		}
4294145522Sdarrenr
4295145522Sdarrenr		csump = nat_proto(fin, nat, nflags);
4296145522Sdarrenr	}
4297145522Sdarrenr
4298145522Sdarrenr	nat_update(fin, nat, np);
4299145522Sdarrenr
4300145522Sdarrenr	/*
4301145522Sdarrenr	 * The above comments do not hold for layer 4 (or higher) checksums...
4302145522Sdarrenr	 */
4303145522Sdarrenr	if (csump != NULL) {
430453642Sguido		if (nat->nat_dir == NAT_OUTBOUND)
4305145522Sdarrenr			fix_incksum(fin, csump, nat->nat_sumd[0]);
430653642Sguido		else
4307145522Sdarrenr			fix_outcksum(fin, csump, nat->nat_sumd[0]);
4308145522Sdarrenr	}
4309145522Sdarrenr	ATOMIC_INCL(nat_stats.ns_mapped[0]);
4310145522Sdarrenr	fin->fin_flx |= FI_NATED;
4311145522Sdarrenr	if (np != NULL && np->in_tag.ipt_num[0] != 0)
4312145522Sdarrenr		fin->fin_nattag = &np->in_tag;
4313145522Sdarrenr	return 1;
4314145522Sdarrenr}
4315130886Sdarrenr
4316130886Sdarrenr
4317145522Sdarrenr/* ------------------------------------------------------------------------ */
4318145522Sdarrenr/* Function:    nat_proto                                                   */
4319145522Sdarrenr/* Returns:     u_short* - pointer to transport header checksum to update,  */
4320145522Sdarrenr/*                         NULL if the transport protocol is not recognised */
4321145522Sdarrenr/*                         as needing a checksum update.                    */
4322145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
4323145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
4324145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
4325145522Sdarrenr/*                                                                          */
4326145522Sdarrenr/* Return the pointer to the checksum field for each protocol so understood.*/
4327145522Sdarrenr/* If support for making other changes to a protocol header is required,    */
4328145522Sdarrenr/* that is not strictly 'address' translation, such as clamping the MSS in  */
4329145522Sdarrenr/* TCP down to a specific value, then do it from here.                      */
4330145522Sdarrenr/* ------------------------------------------------------------------------ */
4331145522Sdarrenru_short *nat_proto(fin, nat, nflags)
4332145522Sdarrenrfr_info_t *fin;
4333145522Sdarrenrnat_t *nat;
4334145522Sdarrenru_int nflags;
4335145522Sdarrenr{
4336145522Sdarrenr	icmphdr_t *icmp;
4337145522Sdarrenr	u_short *csump;
4338145522Sdarrenr	tcphdr_t *tcp;
4339145522Sdarrenr	udphdr_t *udp;
434053642Sguido
4341145522Sdarrenr	csump = NULL;
4342145522Sdarrenr	if (fin->fin_out == 0) {
4343145522Sdarrenr		fin->fin_rev = (nat->nat_dir == NAT_OUTBOUND);
4344145522Sdarrenr	} else {
4345145522Sdarrenr		fin->fin_rev = (nat->nat_dir == NAT_INBOUND);
4346145522Sdarrenr	}
434753642Sguido
4348145522Sdarrenr	switch (fin->fin_p)
4349145522Sdarrenr	{
4350145522Sdarrenr	case IPPROTO_TCP :
4351145522Sdarrenr		tcp = fin->fin_dp;
4352110916Sdarrenr
4353145522Sdarrenr		csump = &tcp->th_sum;
435453642Sguido
4355145522Sdarrenr		/*
4356145522Sdarrenr		 * Do a MSS CLAMPING on a SYN packet,
4357145522Sdarrenr		 * only deal IPv4 for now.
4358145522Sdarrenr		 */
4359145522Sdarrenr		if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0)
4360145522Sdarrenr			nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump);
436160852Sdarrenr
4362145522Sdarrenr		break;
4363145522Sdarrenr
4364145522Sdarrenr	case IPPROTO_UDP :
4365145522Sdarrenr		udp = fin->fin_dp;
4366145522Sdarrenr
4367145522Sdarrenr		if (udp->uh_sum)
4368145522Sdarrenr			csump = &udp->uh_sum;
4369145522Sdarrenr		break;
4370145522Sdarrenr
4371145522Sdarrenr	case IPPROTO_ICMP :
4372145522Sdarrenr		icmp = fin->fin_dp;
4373145522Sdarrenr
4374145522Sdarrenr		if ((nflags & IPN_ICMPQUERY) != 0) {
4375145522Sdarrenr			if (icmp->icmp_cksum != 0)
4376145522Sdarrenr				csump = &icmp->icmp_cksum;
437753642Sguido		}
4378145522Sdarrenr		break;
437953642Sguido	}
4380145522Sdarrenr	return csump;
438153642Sguido}
438253642Sguido
438353642Sguido
4384145522Sdarrenr/* ------------------------------------------------------------------------ */
4385145522Sdarrenr/* Function:    fr_natunload                                                */
4386145522Sdarrenr/* Returns:     Nil                                                         */
4387145522Sdarrenr/* Parameters:  Nil                                                         */
4388145522Sdarrenr/*                                                                          */
4389145522Sdarrenr/* Free all memory used by NAT structures allocated at runtime.             */
4390145522Sdarrenr/* ------------------------------------------------------------------------ */
4391145522Sdarrenrvoid fr_natunload()
439253642Sguido{
4393145522Sdarrenr	ipftq_t *ifq, *ifqnext;
4394145522Sdarrenr
439553642Sguido	(void) nat_clearlist();
439653642Sguido	(void) nat_flushtable();
439753642Sguido
4398145522Sdarrenr	/*
4399145522Sdarrenr	 * Proxy timeout queues are not cleaned here because although they
4400145522Sdarrenr	 * exist on the NAT list, appr_unload is called after fr_natunload
4401145522Sdarrenr	 * and the proxies actually are responsible for them being created.
4402145522Sdarrenr	 * Should the proxy timeouts have their own list?  There's no real
4403145522Sdarrenr	 * justification as this is the only complication.
4404145522Sdarrenr	 */
4405145522Sdarrenr	for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) {
4406145522Sdarrenr		ifqnext = ifq->ifq_next;
4407145522Sdarrenr		if (((ifq->ifq_flags & IFQF_PROXY) == 0) &&
4408145522Sdarrenr		    (fr_deletetimeoutqueue(ifq) == 0))
4409145522Sdarrenr			fr_freetimeoutqueue(ifq);
4410145522Sdarrenr	}
4411145522Sdarrenr
441253642Sguido	if (nat_table[0] != NULL) {
441353642Sguido		KFREES(nat_table[0], sizeof(nat_t *) * ipf_nattable_sz);
441453642Sguido		nat_table[0] = NULL;
441553642Sguido	}
441653642Sguido	if (nat_table[1] != NULL) {
441753642Sguido		KFREES(nat_table[1], sizeof(nat_t *) * ipf_nattable_sz);
441853642Sguido		nat_table[1] = NULL;
441953642Sguido	}
442053642Sguido	if (nat_rules != NULL) {
442153642Sguido		KFREES(nat_rules, sizeof(ipnat_t *) * ipf_natrules_sz);
442253642Sguido		nat_rules = NULL;
442353642Sguido	}
442453642Sguido	if (rdr_rules != NULL) {
442553642Sguido		KFREES(rdr_rules, sizeof(ipnat_t *) * ipf_rdrrules_sz);
442653642Sguido		rdr_rules = NULL;
442753642Sguido	}
4428170268Sdarrenr	if (ipf_hm_maptable != NULL) {
4429170268Sdarrenr		KFREES(ipf_hm_maptable, sizeof(hostmap_t *) * ipf_hostmap_sz);
4430170268Sdarrenr		ipf_hm_maptable = NULL;
443160852Sdarrenr	}
4432145522Sdarrenr	if (nat_stats.ns_bucketlen[0] != NULL) {
4433145522Sdarrenr		KFREES(nat_stats.ns_bucketlen[0],
4434145522Sdarrenr		       sizeof(u_long *) * ipf_nattable_sz);
4435145522Sdarrenr		nat_stats.ns_bucketlen[0] = NULL;
4436145522Sdarrenr	}
4437145522Sdarrenr	if (nat_stats.ns_bucketlen[1] != NULL) {
4438145522Sdarrenr		KFREES(nat_stats.ns_bucketlen[1],
4439145522Sdarrenr		       sizeof(u_long *) * ipf_nattable_sz);
4440145522Sdarrenr		nat_stats.ns_bucketlen[1] = NULL;
4441145522Sdarrenr	}
4442145522Sdarrenr
4443145522Sdarrenr	if (fr_nat_maxbucket_reset == 1)
4444145522Sdarrenr		fr_nat_maxbucket = 0;
4445145522Sdarrenr
4446145522Sdarrenr	if (fr_nat_init == 1) {
4447145522Sdarrenr		fr_nat_init = 0;
4448145522Sdarrenr		fr_sttab_destroy(nat_tqb);
4449145522Sdarrenr
4450145522Sdarrenr		RW_DESTROY(&ipf_natfrag);
4451145522Sdarrenr		RW_DESTROY(&ipf_nat);
4452145522Sdarrenr
4453145522Sdarrenr		MUTEX_DESTROY(&ipf_nat_new);
4454145522Sdarrenr		MUTEX_DESTROY(&ipf_natio);
4455145522Sdarrenr
4456145522Sdarrenr		MUTEX_DESTROY(&nat_udptq.ifq_lock);
4457145522Sdarrenr		MUTEX_DESTROY(&nat_icmptq.ifq_lock);
4458145522Sdarrenr		MUTEX_DESTROY(&nat_iptq.ifq_lock);
4459145522Sdarrenr	}
446053642Sguido}
446153642Sguido
446253642Sguido
4463145522Sdarrenr/* ------------------------------------------------------------------------ */
4464145522Sdarrenr/* Function:    fr_natexpire                                                */
4465145522Sdarrenr/* Returns:     Nil                                                         */
4466145522Sdarrenr/* Parameters:  Nil                                                         */
4467145522Sdarrenr/*                                                                          */
4468145522Sdarrenr/* Check all of the timeout queues for entries at the top which need to be  */
4469145522Sdarrenr/* expired.                                                                 */
4470145522Sdarrenr/* ------------------------------------------------------------------------ */
4471145522Sdarrenrvoid fr_natexpire()
447253642Sguido{
4473145522Sdarrenr	ipftq_t *ifq, *ifqnext;
4474145522Sdarrenr	ipftqent_t *tqe, *tqn;
4475161356Sguido	int i;
4476153876Sguido	SPL_INT(s);
447753642Sguido
447853642Sguido	SPL_NET(s);
447953642Sguido	WRITE_ENTER(&ipf_nat);
4480145522Sdarrenr	for (ifq = nat_tqb, i = 0; ifq != NULL; ifq = ifq->ifq_next) {
4481145522Sdarrenr		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
4482145522Sdarrenr			if (tqe->tqe_die > fr_ticks)
4483145522Sdarrenr				break;
4484145522Sdarrenr			tqn = tqe->tqe_next;
4485145522Sdarrenr			nat_delete(tqe->tqe_parent, NL_EXPIRE);
448653642Sguido		}
448753642Sguido	}
4488145522Sdarrenr
4489145522Sdarrenr	for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) {
4490145522Sdarrenr		ifqnext = ifq->ifq_next;
4491145522Sdarrenr
4492145522Sdarrenr		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
4493145522Sdarrenr			if (tqe->tqe_die > fr_ticks)
4494145522Sdarrenr				break;
4495145522Sdarrenr			tqn = tqe->tqe_next;
4496145522Sdarrenr			nat_delete(tqe->tqe_parent, NL_EXPIRE);
4497145522Sdarrenr		}
4498145522Sdarrenr	}
4499145522Sdarrenr
4500145522Sdarrenr	for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) {
4501145522Sdarrenr		ifqnext = ifq->ifq_next;
4502145522Sdarrenr
4503145522Sdarrenr		if (((ifq->ifq_flags & IFQF_DELETE) != 0) &&
4504145522Sdarrenr		    (ifq->ifq_ref == 0)) {
4505145522Sdarrenr			fr_freetimeoutqueue(ifq);
4506145522Sdarrenr		}
4507145522Sdarrenr	}
4508145522Sdarrenr
4509170268Sdarrenr	if (fr_nat_doflush != 0) {
4510170268Sdarrenr		nat_extraflush(2);
4511170268Sdarrenr		fr_nat_doflush = 0;
4512170268Sdarrenr	}
4513170268Sdarrenr
451453642Sguido	RWLOCK_EXIT(&ipf_nat);
451553642Sguido	SPL_X(s);
451653642Sguido}
451753642Sguido
451853642Sguido
4519145522Sdarrenr/* ------------------------------------------------------------------------ */
4520145522Sdarrenr/* Function:    fr_natsync                                                  */
4521145522Sdarrenr/* Returns:     Nil                                                         */
4522145522Sdarrenr/* Parameters:  ifp(I) - pointer to network interface                       */
4523145522Sdarrenr/*                                                                          */
4524145522Sdarrenr/* Walk through all of the currently active NAT sessions, looking for those */
4525145522Sdarrenr/* which need to have their translated address updated.                     */
4526145522Sdarrenr/* ------------------------------------------------------------------------ */
4527145522Sdarrenrvoid fr_natsync(ifp)
452853642Sguidovoid *ifp;
452953642Sguido{
4530145522Sdarrenr	u_32_t sum1, sum2, sumd;
453153642Sguido	struct in_addr in;
4532145522Sdarrenr	ipnat_t *n;
4533145522Sdarrenr	nat_t *nat;
453453642Sguido	void *ifp2;
4535153876Sguido	SPL_INT(s);
453653642Sguido
4537145522Sdarrenr	if (fr_running <= 0)
4538145522Sdarrenr		return;
4539145522Sdarrenr
454053642Sguido	/*
454153642Sguido	 * Change IP addresses for NAT sessions for any protocol except TCP
4542145522Sdarrenr	 * since it will break the TCP connection anyway.  The only rules
4543145522Sdarrenr	 * which will get changed are those which are "map ... -> 0/32",
4544145522Sdarrenr	 * where the rule specifies the address is taken from the interface.
454553642Sguido	 */
454653642Sguido	SPL_NET(s);
454753642Sguido	WRITE_ENTER(&ipf_nat);
4548145522Sdarrenr
4549145522Sdarrenr	if (fr_running <= 0) {
4550145522Sdarrenr		RWLOCK_EXIT(&ipf_nat);
4551145522Sdarrenr		return;
4552145522Sdarrenr	}
4553145522Sdarrenr
4554145522Sdarrenr	for (nat = nat_instances; nat; nat = nat->nat_next) {
4555145522Sdarrenr		if ((nat->nat_flags & IPN_TCP) != 0)
4556145522Sdarrenr			continue;
4557145522Sdarrenr		n = nat->nat_ptr;
4558145522Sdarrenr		if ((n == NULL) ||
4559145522Sdarrenr		    (n->in_outip != 0) || (n->in_outmsk != 0xffffffff))
4560145522Sdarrenr			continue;
4561145522Sdarrenr		if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) ||
4562145522Sdarrenr		     (ifp == nat->nat_ifps[1]))) {
4563145522Sdarrenr			nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0], 4);
4564145522Sdarrenr			if (nat->nat_ifnames[1][0] != '\0') {
4565145522Sdarrenr				nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1],
4566145522Sdarrenr							  4);
4567145522Sdarrenr			} else
4568145522Sdarrenr				nat->nat_ifps[1] = nat->nat_ifps[0];
4569145522Sdarrenr			ifp2 = nat->nat_ifps[0];
4570145522Sdarrenr			if (ifp2 == NULL)
4571145522Sdarrenr				continue;
4572145522Sdarrenr
457353642Sguido			/*
457453642Sguido			 * Change the map-to address to be the same as the
457553642Sguido			 * new one.
457653642Sguido			 */
457753642Sguido			sum1 = nat->nat_outip.s_addr;
4578145522Sdarrenr			if (fr_ifpaddr(4, FRI_NORMAL, ifp2, &in, NULL) != -1)
457955929Sguido				nat->nat_outip = in;
458053642Sguido			sum2 = nat->nat_outip.s_addr;
458153642Sguido
458253642Sguido			if (sum1 == sum2)
458353642Sguido				continue;
458453642Sguido			/*
458553642Sguido			 * Readjust the checksum adjustment to take into
458653642Sguido			 * account the new IP#.
458753642Sguido			 */
458853642Sguido			CALC_SUMD(sum1, sum2, sumd);
458955929Sguido			/* XXX - dont change for TCP when solaris does
459055929Sguido			 * hardware checksumming.
459155929Sguido			 */
459255929Sguido			sumd += nat->nat_sumd[0];
459355929Sguido			nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
459455929Sguido			nat->nat_sumd[1] = nat->nat_sumd[0];
459553642Sguido		}
4596145522Sdarrenr	}
459753642Sguido
4598145522Sdarrenr	for (n = nat_list; (n != NULL); n = n->in_next) {
4599145522Sdarrenr		if ((ifp == NULL) || (n->in_ifps[0] == ifp))
4600145522Sdarrenr			n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4);
4601145522Sdarrenr		if ((ifp == NULL) || (n->in_ifps[1] == ifp))
4602145522Sdarrenr			n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4);
4603145522Sdarrenr	}
460460852Sdarrenr	RWLOCK_EXIT(&ipf_nat);
460553642Sguido	SPL_X(s);
460653642Sguido}
460753642Sguido
460853642Sguido
4609145522Sdarrenr/* ------------------------------------------------------------------------ */
4610145522Sdarrenr/* Function:    nat_icmpquerytype4                                          */
4611145522Sdarrenr/* Returns:     int - 1 == success, 0 == failure                            */
4612145522Sdarrenr/* Parameters:  icmptype(I) - ICMP type number                              */
4613145522Sdarrenr/*                                                                          */
4614145522Sdarrenr/* Tests to see if the ICMP type number passed is a query/response type or  */
4615145522Sdarrenr/* not.                                                                     */
4616145522Sdarrenr/* ------------------------------------------------------------------------ */
4617153876Sguidostatic int nat_icmpquerytype4(icmptype)
4618145522Sdarrenrint icmptype;
4619145522Sdarrenr{
4620145522Sdarrenr
4621145522Sdarrenr	/*
4622145522Sdarrenr	 * For the ICMP query NAT code, it is essential that both the query
4623145522Sdarrenr	 * and the reply match on the NAT rule. Because the NAT structure
4624145522Sdarrenr	 * does not keep track of the icmptype, and a single NAT structure
4625145522Sdarrenr	 * is used for all icmp types with the same src, dest and id, we
4626145522Sdarrenr	 * simply define the replies as queries as well. The funny thing is,
4627145522Sdarrenr	 * altough it seems silly to call a reply a query, this is exactly
4628145522Sdarrenr	 * as it is defined in the IPv4 specification
4629145522Sdarrenr	 */
4630145522Sdarrenr
4631145522Sdarrenr	switch (icmptype)
4632145522Sdarrenr	{
4633145522Sdarrenr
4634145522Sdarrenr	case ICMP_ECHOREPLY:
4635145522Sdarrenr	case ICMP_ECHO:
4636145522Sdarrenr	/* route aedvertisement/solliciation is currently unsupported: */
4637145522Sdarrenr	/* it would require rewriting the ICMP data section            */
4638145522Sdarrenr	case ICMP_TSTAMP:
4639145522Sdarrenr	case ICMP_TSTAMPREPLY:
4640145522Sdarrenr	case ICMP_IREQ:
4641145522Sdarrenr	case ICMP_IREQREPLY:
4642145522Sdarrenr	case ICMP_MASKREQ:
4643145522Sdarrenr	case ICMP_MASKREPLY:
4644145522Sdarrenr		return 1;
4645145522Sdarrenr	default:
4646145522Sdarrenr		return 0;
4647145522Sdarrenr	}
4648145522Sdarrenr}
4649145522Sdarrenr
4650145522Sdarrenr
4651145522Sdarrenr/* ------------------------------------------------------------------------ */
4652145522Sdarrenr/* Function:    nat_log                                                     */
4653145522Sdarrenr/* Returns:     Nil                                                         */
4654145522Sdarrenr/* Parameters:  nat(I)  - pointer to NAT structure                          */
4655145522Sdarrenr/*              type(I) - type of log entry to create                       */
4656145522Sdarrenr/*                                                                          */
4657145522Sdarrenr/* Creates a NAT log entry.                                                 */
4658145522Sdarrenr/* ------------------------------------------------------------------------ */
465953642Sguidovoid nat_log(nat, type)
466053642Sguidostruct nat *nat;
466153642Sguidou_int type;
466253642Sguido{
4663145522Sdarrenr#ifdef	IPFILTER_LOG
4664139005Smlaier# ifndef LARGE_NAT
466553642Sguido	struct ipnat *np;
4666138979Sdarrenr	int rulen;
4667138979Sdarrenr# endif
466853642Sguido	struct natlog natl;
466953642Sguido	void *items[1];
467053642Sguido	size_t sizes[1];
4671138979Sdarrenr	int types[1];
467253642Sguido
467353642Sguido	natl.nl_inip = nat->nat_inip;
467453642Sguido	natl.nl_outip = nat->nat_outip;
467553642Sguido	natl.nl_origip = nat->nat_oip;
4676145522Sdarrenr	natl.nl_bytes[0] = nat->nat_bytes[0];
4677145522Sdarrenr	natl.nl_bytes[1] = nat->nat_bytes[1];
4678145522Sdarrenr	natl.nl_pkts[0] = nat->nat_pkts[0];
4679145522Sdarrenr	natl.nl_pkts[1] = nat->nat_pkts[1];
468053642Sguido	natl.nl_origport = nat->nat_oport;
468153642Sguido	natl.nl_inport = nat->nat_inport;
468253642Sguido	natl.nl_outport = nat->nat_outport;
468357096Sguido	natl.nl_p = nat->nat_p;
468453642Sguido	natl.nl_type = type;
468553642Sguido	natl.nl_rule = -1;
4686145522Sdarrenr# ifndef LARGE_NAT
468753642Sguido	if (nat->nat_ptr != NULL) {
468853642Sguido		for (rulen = 0, np = nat_list; np; np = np->in_next, rulen++)
468953642Sguido			if (np == nat->nat_ptr) {
469053642Sguido				natl.nl_rule = rulen;
469153642Sguido				break;
469253642Sguido			}
469353642Sguido	}
4694145522Sdarrenr# endif
469553642Sguido	items[0] = &natl;
469653642Sguido	sizes[0] = sizeof(natl);
469753642Sguido	types[0] = 0;
469853642Sguido
469953642Sguido	(void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1);
4700145522Sdarrenr#endif
470153642Sguido}
470292685Sdarrenr
470392685Sdarrenr
470492685Sdarrenr#if defined(__OpenBSD__)
4705145522Sdarrenr/* ------------------------------------------------------------------------ */
4706145522Sdarrenr/* Function:    nat_ifdetach                                                */
4707145522Sdarrenr/* Returns:     Nil                                                         */
4708145522Sdarrenr/* Parameters:  ifp(I) - pointer to network interface                       */
4709145522Sdarrenr/*                                                                          */
4710145522Sdarrenr/* Compatibility interface for OpenBSD to trigger the correct updating of   */
4711145522Sdarrenr/* interface references within IPFilter.                                    */
4712145522Sdarrenr/* ------------------------------------------------------------------------ */
471392685Sdarrenrvoid nat_ifdetach(ifp)
471492685Sdarrenrvoid *ifp;
471592685Sdarrenr{
4716145522Sdarrenr	frsync(ifp);
471792685Sdarrenr	return;
471892685Sdarrenr}
471992685Sdarrenr#endif
4720110916Sdarrenr
4721110916Sdarrenr
4722145522Sdarrenr/* ------------------------------------------------------------------------ */
4723170268Sdarrenr/* Function:    fr_ipnatderef                                               */
4724170268Sdarrenr/* Returns:     Nil                                                         */
4725170268Sdarrenr/* Parameters:  isp(I) - pointer to pointer to NAT rule                     */
4726170268Sdarrenr/* Write Locks: ipf_nat                                                     */
4727170268Sdarrenr/*                                                                          */
4728170268Sdarrenr/* ------------------------------------------------------------------------ */
4729170268Sdarrenrvoid fr_ipnatderef(inp)
4730170268Sdarrenripnat_t **inp;
4731170268Sdarrenr{
4732170268Sdarrenr	ipnat_t *in;
4733170268Sdarrenr
4734170268Sdarrenr	in = *inp;
4735170268Sdarrenr	*inp = NULL;
4736170268Sdarrenr	in->in_space++;
4737170268Sdarrenr	in->in_use--;
4738170268Sdarrenr	if (in->in_use == 0 && (in->in_flags & IPN_DELETE)) {
4739170268Sdarrenr		if (in->in_apr)
4740170268Sdarrenr			appr_free(in->in_apr);
4741172776Sdarrenr		MUTEX_DESTROY(&in->in_lock);
4742170268Sdarrenr		KFREE(in);
4743170268Sdarrenr		nat_stats.ns_rules--;
4744172776Sdarrenr#if SOLARIS && !defined(_INET_IP_STACK_H)
4745170268Sdarrenr		if (nat_stats.ns_rules == 0)
4746170268Sdarrenr			pfil_delayed_copy = 1;
4747170268Sdarrenr#endif
4748170268Sdarrenr	}
4749170268Sdarrenr}
4750170268Sdarrenr
4751170268Sdarrenr
4752170268Sdarrenr/* ------------------------------------------------------------------------ */
4753145522Sdarrenr/* Function:    fr_natderef                                                 */
4754145522Sdarrenr/* Returns:     Nil                                                         */
4755145522Sdarrenr/* Parameters:  isp(I) - pointer to pointer to NAT table entry              */
4756145522Sdarrenr/*                                                                          */
4757145522Sdarrenr/* Decrement the reference counter for this NAT table entry and free it if  */
4758145522Sdarrenr/* there are no more things using it.                                       */
4759172776Sdarrenr/*                                                                          */
4760172776Sdarrenr/* IF nat_ref == 1 when this function is called, then we have an orphan nat */
4761172776Sdarrenr/* structure *because* it only gets called on paths _after_ nat_ref has been*/
4762172776Sdarrenr/* incremented.  If nat_ref == 1 then we shouldn't decrement it here        */
4763172776Sdarrenr/* because nat_delete() will do that and send nat_ref to -1.                */
4764172776Sdarrenr/*                                                                          */
4765172776Sdarrenr/* Holding the lock on nat_lock is required to serialise nat_delete() being */
4766172776Sdarrenr/* called from a NAT flush ioctl with a deref happening because of a packet.*/
4767145522Sdarrenr/* ------------------------------------------------------------------------ */
4768145522Sdarrenrvoid fr_natderef(natp)
4769145522Sdarrenrnat_t **natp;
4770145522Sdarrenr{
4771145522Sdarrenr	nat_t *nat;
4772145522Sdarrenr
4773145522Sdarrenr	nat = *natp;
4774145522Sdarrenr	*natp = NULL;
4775172776Sdarrenr
4776172776Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
4777172776Sdarrenr	if (nat->nat_ref > 1) {
4778172776Sdarrenr		nat->nat_ref--;
4779172776Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
4780172776Sdarrenr		return;
4781172776Sdarrenr	}
4782172776Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
4783172776Sdarrenr
4784145522Sdarrenr	WRITE_ENTER(&ipf_nat);
4785172776Sdarrenr	nat_delete(nat, NL_EXPIRE);
4786145522Sdarrenr	RWLOCK_EXIT(&ipf_nat);
4787145522Sdarrenr}
4788145522Sdarrenr
4789145522Sdarrenr
4790145522Sdarrenr/* ------------------------------------------------------------------------ */
4791145522Sdarrenr/* Function:    fr_natclone                                                 */
4792145522Sdarrenr/* Returns:     ipstate_t* - NULL == cloning failed,                        */
4793145522Sdarrenr/*                           else pointer to new state structure            */
4794145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
4795145522Sdarrenr/*              is(I)  - pointer to master state structure                  */
4796145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
4797145522Sdarrenr/*                                                                          */
4798145522Sdarrenr/* Create a "duplcate" state table entry from the master.                   */
4799145522Sdarrenr/* ------------------------------------------------------------------------ */
4800145522Sdarrenrstatic nat_t *fr_natclone(fin, nat)
4801145522Sdarrenrfr_info_t *fin;
4802145522Sdarrenrnat_t *nat;
4803145522Sdarrenr{
4804145522Sdarrenr	frentry_t *fr;
4805145522Sdarrenr	nat_t *clone;
4806145522Sdarrenr	ipnat_t *np;
4807145522Sdarrenr
4808145522Sdarrenr	KMALLOC(clone, nat_t *);
4809145522Sdarrenr	if (clone == NULL)
4810145522Sdarrenr		return NULL;
4811145522Sdarrenr	bcopy((char *)nat, (char *)clone, sizeof(*clone));
4812145522Sdarrenr
4813145522Sdarrenr	MUTEX_NUKE(&clone->nat_lock);
4814145522Sdarrenr
4815153876Sguido	clone->nat_aps = NULL;
4816153876Sguido	/*
4817153876Sguido	 * Initialize all these so that nat_delete() doesn't cause a crash.
4818153876Sguido	 */
4819153876Sguido	clone->nat_tqe.tqe_pnext = NULL;
4820153876Sguido	clone->nat_tqe.tqe_next = NULL;
4821153876Sguido	clone->nat_tqe.tqe_ifq = NULL;
4822153876Sguido	clone->nat_tqe.tqe_parent = clone;
4823153876Sguido
4824145522Sdarrenr	clone->nat_flags &= ~SI_CLONE;
4825145522Sdarrenr	clone->nat_flags |= SI_CLONED;
4826145522Sdarrenr
4827153876Sguido	if (clone->nat_hm)
4828153876Sguido		clone->nat_hm->hm_ref++;
4829145522Sdarrenr
4830145522Sdarrenr	if (nat_insert(clone, fin->fin_rev) == -1) {
4831145522Sdarrenr		KFREE(clone);
4832145522Sdarrenr		return NULL;
4833145522Sdarrenr	}
4834145522Sdarrenr	np = clone->nat_ptr;
4835145522Sdarrenr	if (np != NULL) {
4836145522Sdarrenr		if (nat_logging)
4837145522Sdarrenr			nat_log(clone, (u_int)np->in_redir);
4838145522Sdarrenr		np->in_use++;
4839145522Sdarrenr	}
4840145522Sdarrenr	fr = clone->nat_fr;
4841145522Sdarrenr	if (fr != NULL) {
4842145522Sdarrenr		MUTEX_ENTER(&fr->fr_lock);
4843145522Sdarrenr		fr->fr_ref++;
4844145522Sdarrenr		MUTEX_EXIT(&fr->fr_lock);
4845145522Sdarrenr	}
4846145522Sdarrenr
4847145522Sdarrenr	/*
4848145522Sdarrenr	 * Because the clone is created outside the normal loop of things and
4849145522Sdarrenr	 * TCP has special needs in terms of state, initialise the timeout
4850145522Sdarrenr	 * state of the new NAT from here.
4851145522Sdarrenr	 */
4852145522Sdarrenr	if (clone->nat_p == IPPROTO_TCP) {
4853153876Sguido		(void) fr_tcp_age(&clone->nat_tqe, fin, nat_tqb,
4854145522Sdarrenr				  clone->nat_flags);
4855145522Sdarrenr	}
4856145522Sdarrenr#ifdef	IPFILTER_SYNC
4857145522Sdarrenr	clone->nat_sync = ipfsync_new(SMC_NAT, fin, clone);
4858145522Sdarrenr#endif
4859145522Sdarrenr	if (nat_logging)
4860145522Sdarrenr		nat_log(clone, NL_CLONE);
4861145522Sdarrenr	return clone;
4862145522Sdarrenr}
4863145522Sdarrenr
4864145522Sdarrenr
4865145522Sdarrenr/* ------------------------------------------------------------------------ */
4866145522Sdarrenr/* Function:   nat_wildok                                                   */
4867145522Sdarrenr/* Returns:    int - 1 == packet's ports match wildcards                    */
4868145522Sdarrenr/*                   0 == packet's ports don't match wildcards              */
4869145522Sdarrenr/* Parameters: nat(I)   - NAT entry                                         */
4870145522Sdarrenr/*             sport(I) - source port                                       */
4871145522Sdarrenr/*             dport(I) - destination port                                  */
4872145522Sdarrenr/*             flags(I) - wildcard flags                                    */
4873145522Sdarrenr/*             dir(I)   - packet direction                                  */
4874145522Sdarrenr/*                                                                          */
4875145522Sdarrenr/* Use NAT entry and packet direction to determine which combination of     */
4876145522Sdarrenr/* wildcard flags should be used.                                           */
4877145522Sdarrenr/* ------------------------------------------------------------------------ */
4878153876Sguidostatic int nat_wildok(nat, sport, dport, flags, dir)
4879145522Sdarrenrnat_t *nat;
4880145522Sdarrenrint sport;
4881145522Sdarrenrint dport;
4882145522Sdarrenrint flags;
4883145522Sdarrenrint dir;
4884145522Sdarrenr{
4885145522Sdarrenr	/*
4886145522Sdarrenr	 * When called by       dir is set to
4887145522Sdarrenr	 * nat_inlookup         NAT_INBOUND (0)
4888145522Sdarrenr	 * nat_outlookup        NAT_OUTBOUND (1)
4889145522Sdarrenr	 *
4890145522Sdarrenr	 * We simply combine the packet's direction in dir with the original
4891145522Sdarrenr	 * "intended" direction of that NAT entry in nat->nat_dir to decide
4892145522Sdarrenr	 * which combination of wildcard flags to allow.
4893145522Sdarrenr	 */
4894145522Sdarrenr
4895145522Sdarrenr	switch ((dir << 1) | nat->nat_dir)
4896145522Sdarrenr	{
4897145522Sdarrenr	case 3: /* outbound packet / outbound entry */
4898145522Sdarrenr		if (((nat->nat_inport == sport) ||
4899145522Sdarrenr		    (flags & SI_W_SPORT)) &&
4900145522Sdarrenr		    ((nat->nat_oport == dport) ||
4901145522Sdarrenr		    (flags & SI_W_DPORT)))
4902145522Sdarrenr			return 1;
4903145522Sdarrenr		break;
4904145522Sdarrenr	case 2: /* outbound packet / inbound entry */
4905145522Sdarrenr		if (((nat->nat_outport == sport) ||
4906145522Sdarrenr		    (flags & SI_W_DPORT)) &&
4907145522Sdarrenr		    ((nat->nat_oport == dport) ||
4908145522Sdarrenr		    (flags & SI_W_SPORT)))
4909145522Sdarrenr			return 1;
4910145522Sdarrenr		break;
4911145522Sdarrenr	case 1: /* inbound packet / outbound entry */
4912145522Sdarrenr		if (((nat->nat_oport == sport) ||
4913145522Sdarrenr		    (flags & SI_W_DPORT)) &&
4914145522Sdarrenr		    ((nat->nat_outport == dport) ||
4915145522Sdarrenr		    (flags & SI_W_SPORT)))
4916145522Sdarrenr			return 1;
4917145522Sdarrenr		break;
4918145522Sdarrenr	case 0: /* inbound packet / inbound entry */
4919145522Sdarrenr		if (((nat->nat_oport == sport) ||
4920145522Sdarrenr		    (flags & SI_W_SPORT)) &&
4921145522Sdarrenr		    ((nat->nat_outport == dport) ||
4922145522Sdarrenr		    (flags & SI_W_DPORT)))
4923145522Sdarrenr			return 1;
4924145522Sdarrenr		break;
4925145522Sdarrenr	default:
4926145522Sdarrenr		break;
4927145522Sdarrenr	}
4928145522Sdarrenr
4929145522Sdarrenr	return(0);
4930145522Sdarrenr}
4931145522Sdarrenr
4932145522Sdarrenr
4933145522Sdarrenr/* ------------------------------------------------------------------------ */
4934145522Sdarrenr/* Function:    nat_mssclamp                                                */
4935145522Sdarrenr/* Returns:     Nil                                                         */
4936145522Sdarrenr/* Parameters:  tcp(I)    - pointer to TCP header                           */
4937145522Sdarrenr/*              maxmss(I) - value to clamp the TCP MSS to                   */
4938145522Sdarrenr/*              fin(I)    - pointer to packet information                   */
4939145522Sdarrenr/*              csump(I)  - pointer to TCP checksum                         */
4940145522Sdarrenr/*                                                                          */
4941145522Sdarrenr/* Check for MSS option and clamp it if necessary.  If found and changed,   */
4942145522Sdarrenr/* then the TCP header checksum will be updated to reflect the change in    */
4943145522Sdarrenr/* the MSS.                                                                 */
4944145522Sdarrenr/* ------------------------------------------------------------------------ */
4945110916Sdarrenrstatic void nat_mssclamp(tcp, maxmss, fin, csump)
4946110916Sdarrenrtcphdr_t *tcp;
4947110916Sdarrenru_32_t maxmss;
4948110916Sdarrenrfr_info_t *fin;
4949110916Sdarrenru_short *csump;
4950110916Sdarrenr{
4951110916Sdarrenr	u_char *cp, *ep, opt;
4952110916Sdarrenr	int hlen, advance;
4953110916Sdarrenr	u_32_t mss, sumd;
4954110916Sdarrenr
4955145522Sdarrenr	hlen = TCP_OFF(tcp) << 2;
4956110916Sdarrenr	if (hlen > sizeof(*tcp)) {
4957110916Sdarrenr		cp = (u_char *)tcp + sizeof(*tcp);
4958110916Sdarrenr		ep = (u_char *)tcp + hlen;
4959110916Sdarrenr
4960110916Sdarrenr		while (cp < ep) {
4961110916Sdarrenr			opt = cp[0];
4962110916Sdarrenr			if (opt == TCPOPT_EOL)
4963110916Sdarrenr				break;
4964110916Sdarrenr			else if (opt == TCPOPT_NOP) {
4965110916Sdarrenr				cp++;
4966110916Sdarrenr				continue;
4967110916Sdarrenr			}
4968145522Sdarrenr
4969145522Sdarrenr			if (cp + 1 >= ep)
4970110916Sdarrenr				break;
4971110916Sdarrenr			advance = cp[1];
4972145522Sdarrenr			if ((cp + advance > ep) || (advance <= 0))
4973110916Sdarrenr				break;
4974145522Sdarrenr			switch (opt)
4975145522Sdarrenr			{
4976110916Sdarrenr			case TCPOPT_MAXSEG:
4977110916Sdarrenr				if (advance != 4)
4978110916Sdarrenr					break;
4979145522Sdarrenr				mss = cp[2] * 256 + cp[3];
4980110916Sdarrenr				if (mss > maxmss) {
4981145522Sdarrenr					cp[2] = maxmss / 256;
4982145522Sdarrenr					cp[3] = maxmss & 0xff;
4983110916Sdarrenr					CALC_SUMD(mss, maxmss, sumd);
4984110916Sdarrenr					fix_outcksum(fin, csump, sumd);
4985110916Sdarrenr				}
4986110916Sdarrenr				break;
4987110916Sdarrenr			default:
4988110916Sdarrenr				/* ignore unknown options */
4989110916Sdarrenr				break;
4990110916Sdarrenr			}
4991145522Sdarrenr
4992145522Sdarrenr			cp += advance;
4993145522Sdarrenr		}
4994145522Sdarrenr	}
4995145522Sdarrenr}
4996145522Sdarrenr
4997145522Sdarrenr
4998145522Sdarrenr/* ------------------------------------------------------------------------ */
4999145522Sdarrenr/* Function:    fr_setnatqueue                                              */
5000145522Sdarrenr/* Returns:     Nil                                                         */
5001145522Sdarrenr/* Parameters:  nat(I)- pointer to NAT structure                            */
5002145522Sdarrenr/*              rev(I) - forward(0) or reverse(1) direction                 */
5003145522Sdarrenr/* Locks:       ipf_nat (read or write)                                     */
5004145522Sdarrenr/*                                                                          */
5005145522Sdarrenr/* Put the NAT entry on its default queue entry, using rev as a helped in   */
5006145522Sdarrenr/* determining which queue it should be placed on.                          */
5007145522Sdarrenr/* ------------------------------------------------------------------------ */
5008145522Sdarrenrvoid fr_setnatqueue(nat, rev)
5009145522Sdarrenrnat_t *nat;
5010145522Sdarrenrint rev;
5011145522Sdarrenr{
5012145522Sdarrenr	ipftq_t *oifq, *nifq;
5013145522Sdarrenr
5014145522Sdarrenr	if (nat->nat_ptr != NULL)
5015145522Sdarrenr		nifq = nat->nat_ptr->in_tqehead[rev];
5016145522Sdarrenr	else
5017145522Sdarrenr		nifq = NULL;
5018145522Sdarrenr
5019145522Sdarrenr	if (nifq == NULL) {
5020145522Sdarrenr		switch (nat->nat_p)
5021145522Sdarrenr		{
5022145522Sdarrenr		case IPPROTO_UDP :
5023145522Sdarrenr			nifq = &nat_udptq;
5024145522Sdarrenr			break;
5025145522Sdarrenr		case IPPROTO_ICMP :
5026145522Sdarrenr			nifq = &nat_icmptq;
5027145522Sdarrenr			break;
5028145522Sdarrenr		case IPPROTO_TCP :
5029145522Sdarrenr			nifq = nat_tqb + nat->nat_tqe.tqe_state[rev];
5030145522Sdarrenr			break;
5031145522Sdarrenr		default :
5032145522Sdarrenr			nifq = &nat_iptq;
5033145522Sdarrenr			break;
5034145522Sdarrenr		}
5035145522Sdarrenr	}
5036145522Sdarrenr
5037145522Sdarrenr	oifq = nat->nat_tqe.tqe_ifq;
5038145522Sdarrenr	/*
5039145522Sdarrenr	 * If it's currently on a timeout queue, move it from one queue to
5040145522Sdarrenr	 * another, else put it on the end of the newly determined queue.
5041145522Sdarrenr	 */
5042145522Sdarrenr	if (oifq != NULL)
5043145522Sdarrenr		fr_movequeue(&nat->nat_tqe, oifq, nifq);
5044145522Sdarrenr	else
5045145522Sdarrenr		fr_queueappend(&nat->nat_tqe, nifq, nat);
5046145522Sdarrenr	return;
5047145522Sdarrenr}
5048170268Sdarrenr
5049170268Sdarrenr
5050170268Sdarrenr/* ------------------------------------------------------------------------ */
5051170268Sdarrenr/* Function:    nat_getnext                                                 */
5052170268Sdarrenr/* Returns:     int - 0 == ok, else error                                   */
5053170268Sdarrenr/* Parameters:  t(I)   - pointer to ipftoken structure                      */
5054170268Sdarrenr/*              itp(I) - pointer to ipfgeniter_t structure                  */
5055170268Sdarrenr/*                                                                          */
5056170268Sdarrenr/* Fetch the next nat/ipnat structure pointer from the linked list and      */
5057170268Sdarrenr/* copy it out to the storage space pointed to by itp_data.  The next item  */
5058170268Sdarrenr/* in the list to look at is put back in the ipftoken struture.             */
5059170268Sdarrenr/* If we call ipf_freetoken, the accompanying pointer is set to NULL because*/
5060170268Sdarrenr/* ipf_freetoken will call a deref function for us and we dont want to call */
5061170268Sdarrenr/* that twice (second time would be in the second switch statement below.   */
5062170268Sdarrenr/* ------------------------------------------------------------------------ */
5063170268Sdarrenrstatic int nat_getnext(t, itp)
5064170268Sdarrenripftoken_t *t;
5065170268Sdarrenripfgeniter_t *itp;
5066170268Sdarrenr{
5067170268Sdarrenr	hostmap_t *hm, *nexthm = NULL, zerohm;
5068170268Sdarrenr	ipnat_t *ipn, *nextipnat = NULL, zeroipn;
5069170268Sdarrenr	nat_t *nat, *nextnat = NULL, zeronat;
5070170268Sdarrenr	int error = 0, count;
5071170268Sdarrenr	char *dst;
5072170268Sdarrenr
5073172776Sdarrenr	count = itp->igi_nitems;
5074172776Sdarrenr	if (count < 1)
5075172776Sdarrenr		return ENOSPC;
5076170268Sdarrenr
5077170268Sdarrenr	READ_ENTER(&ipf_nat);
5078170268Sdarrenr
5079170268Sdarrenr	switch (itp->igi_type)
5080170268Sdarrenr	{
5081170268Sdarrenr	case IPFGENITER_HOSTMAP :
5082170268Sdarrenr		hm = t->ipt_data;
5083170268Sdarrenr		if (hm == NULL) {
5084170268Sdarrenr			nexthm = ipf_hm_maplist;
5085170268Sdarrenr		} else {
5086170268Sdarrenr			nexthm = hm->hm_next;
5087170268Sdarrenr		}
5088170268Sdarrenr		break;
5089170268Sdarrenr
5090170268Sdarrenr	case IPFGENITER_IPNAT :
5091170268Sdarrenr		ipn = t->ipt_data;
5092170268Sdarrenr		if (ipn == NULL) {
5093170268Sdarrenr			nextipnat = nat_list;
5094170268Sdarrenr		} else {
5095170268Sdarrenr			nextipnat = ipn->in_next;
5096170268Sdarrenr		}
5097170268Sdarrenr		break;
5098170268Sdarrenr
5099170268Sdarrenr	case IPFGENITER_NAT :
5100170268Sdarrenr		nat = t->ipt_data;
5101170268Sdarrenr		if (nat == NULL) {
5102170268Sdarrenr			nextnat = nat_instances;
5103170268Sdarrenr		} else {
5104170268Sdarrenr			nextnat = nat->nat_next;
5105170268Sdarrenr		}
5106170268Sdarrenr		break;
5107170268Sdarrenr	default :
5108170268Sdarrenr		RWLOCK_EXIT(&ipf_nat);
5109170268Sdarrenr		return EINVAL;
5110170268Sdarrenr	}
5111170268Sdarrenr
5112170268Sdarrenr	dst = itp->igi_data;
5113172776Sdarrenr	for (;;) {
5114170268Sdarrenr		switch (itp->igi_type)
5115170268Sdarrenr		{
5116170268Sdarrenr		case IPFGENITER_HOSTMAP :
5117170268Sdarrenr			if (nexthm != NULL) {
5118170268Sdarrenr				if (count == 1) {
5119170268Sdarrenr					ATOMIC_INC32(nexthm->hm_ref);
5120172776Sdarrenr					t->ipt_data = nexthm;
5121170268Sdarrenr				}
5122170268Sdarrenr			} else {
5123170268Sdarrenr				bzero(&zerohm, sizeof(zerohm));
5124170268Sdarrenr				nexthm = &zerohm;
5125170268Sdarrenr				count = 1;
5126172776Sdarrenr				t->ipt_data = NULL;
5127170268Sdarrenr			}
5128170268Sdarrenr			break;
5129170268Sdarrenr
5130170268Sdarrenr		case IPFGENITER_IPNAT :
5131170268Sdarrenr			if (nextipnat != NULL) {
5132170268Sdarrenr				if (count == 1) {
5133170268Sdarrenr					MUTEX_ENTER(&nextipnat->in_lock);
5134170268Sdarrenr					nextipnat->in_use++;
5135170268Sdarrenr					MUTEX_EXIT(&nextipnat->in_lock);
5136172776Sdarrenr					t->ipt_data = nextipnat;
5137170268Sdarrenr				}
5138170268Sdarrenr			} else {
5139170268Sdarrenr				bzero(&zeroipn, sizeof(zeroipn));
5140170268Sdarrenr				nextipnat = &zeroipn;
5141170268Sdarrenr				count = 1;
5142172776Sdarrenr				t->ipt_data = NULL;
5143170268Sdarrenr			}
5144170268Sdarrenr			break;
5145170268Sdarrenr
5146170268Sdarrenr		case IPFGENITER_NAT :
5147170268Sdarrenr			if (nextnat != NULL) {
5148170268Sdarrenr				if (count == 1) {
5149170268Sdarrenr					MUTEX_ENTER(&nextnat->nat_lock);
5150170268Sdarrenr					nextnat->nat_ref++;
5151170268Sdarrenr					MUTEX_EXIT(&nextnat->nat_lock);
5152172776Sdarrenr					t->ipt_data = nextnat;
5153170268Sdarrenr				}
5154170268Sdarrenr			} else {
5155170268Sdarrenr				bzero(&zeronat, sizeof(zeronat));
5156170268Sdarrenr				nextnat = &zeronat;
5157170268Sdarrenr				count = 1;
5158172776Sdarrenr				t->ipt_data = NULL;
5159170268Sdarrenr			}
5160170268Sdarrenr			break;
5161170268Sdarrenr		default :
5162170268Sdarrenr			break;
5163170268Sdarrenr		}
5164170268Sdarrenr		RWLOCK_EXIT(&ipf_nat);
5165170268Sdarrenr
5166172776Sdarrenr		/*
5167172776Sdarrenr		 * Copying out to user space needs to be done without the lock.
5168172776Sdarrenr		 */
5169170268Sdarrenr		switch (itp->igi_type)
5170170268Sdarrenr		{
5171170268Sdarrenr		case IPFGENITER_HOSTMAP :
5172170268Sdarrenr			error = COPYOUT(nexthm, dst, sizeof(*nexthm));
5173170268Sdarrenr			if (error != 0)
5174170268Sdarrenr				error = EFAULT;
5175170268Sdarrenr			else
5176170268Sdarrenr				dst += sizeof(*nexthm);
5177170268Sdarrenr			break;
5178170268Sdarrenr
5179170268Sdarrenr		case IPFGENITER_IPNAT :
5180170268Sdarrenr			error = COPYOUT(nextipnat, dst, sizeof(*nextipnat));
5181170268Sdarrenr			if (error != 0)
5182170268Sdarrenr				error = EFAULT;
5183170268Sdarrenr			else
5184170268Sdarrenr				dst += sizeof(*nextipnat);
5185170268Sdarrenr			break;
5186170268Sdarrenr
5187170268Sdarrenr		case IPFGENITER_NAT :
5188170268Sdarrenr			error = COPYOUT(nextnat, dst, sizeof(*nextnat));
5189170268Sdarrenr			if (error != 0)
5190170268Sdarrenr				error = EFAULT;
5191170268Sdarrenr			else
5192170268Sdarrenr				dst += sizeof(*nextnat);
5193170268Sdarrenr			break;
5194170268Sdarrenr		}
5195170268Sdarrenr
5196170268Sdarrenr		if ((count == 1) || (error != 0))
5197170268Sdarrenr			break;
5198170268Sdarrenr
5199172776Sdarrenr		count--;
5200172776Sdarrenr
5201170268Sdarrenr		READ_ENTER(&ipf_nat);
5202170268Sdarrenr
5203172776Sdarrenr		/*
5204172776Sdarrenr		 * We need to have the lock again here to make sure that
5205172776Sdarrenr		 * using _next is consistent.
5206172776Sdarrenr		 */
5207170268Sdarrenr		switch (itp->igi_type)
5208170268Sdarrenr		{
5209170268Sdarrenr		case IPFGENITER_HOSTMAP :
5210172776Sdarrenr			nexthm = nexthm->hm_next;
5211170268Sdarrenr			break;
5212170268Sdarrenr		case IPFGENITER_IPNAT :
5213172776Sdarrenr			nextipnat = nextipnat->in_next;
5214170268Sdarrenr			break;
5215170268Sdarrenr		case IPFGENITER_NAT :
5216172776Sdarrenr			nextnat = nextnat->nat_next;
5217170268Sdarrenr			break;
5218170268Sdarrenr		}
5219170268Sdarrenr	}
5220170268Sdarrenr
5221172776Sdarrenr
5222172776Sdarrenr	switch (itp->igi_type)
5223172776Sdarrenr	{
5224172776Sdarrenr	case IPFGENITER_HOSTMAP :
5225172776Sdarrenr		if (hm != NULL) {
5226172776Sdarrenr			WRITE_ENTER(&ipf_nat);
5227172776Sdarrenr			fr_hostmapdel(&hm);
5228172776Sdarrenr			RWLOCK_EXIT(&ipf_nat);
5229172776Sdarrenr		}
5230172776Sdarrenr		break;
5231172776Sdarrenr	case IPFGENITER_IPNAT :
5232172776Sdarrenr		if (ipn != NULL) {
5233172776Sdarrenr			fr_ipnatderef(&ipn);
5234172776Sdarrenr		}
5235172776Sdarrenr		break;
5236172776Sdarrenr	case IPFGENITER_NAT :
5237172776Sdarrenr		if (nat != NULL) {
5238172776Sdarrenr			fr_natderef(&nat);
5239172776Sdarrenr		}
5240172776Sdarrenr		break;
5241172776Sdarrenr	default :
5242172776Sdarrenr		break;
5243172776Sdarrenr	}
5244172776Sdarrenr
5245170268Sdarrenr	return error;
5246170268Sdarrenr}
5247170268Sdarrenr
5248170268Sdarrenr
5249170268Sdarrenr/* ------------------------------------------------------------------------ */
5250170268Sdarrenr/* Function:    nat_iterator                                                */
5251170268Sdarrenr/* Returns:     int - 0 == ok, else error                                   */
5252170268Sdarrenr/* Parameters:  token(I) - pointer to ipftoken structure                    */
5253170268Sdarrenr/*              itp(I) - pointer to ipfgeniter_t structure                  */
5254170268Sdarrenr/*                                                                          */
5255170268Sdarrenr/* This function acts as a handler for the SIOCGENITER ioctls that use a    */
5256170268Sdarrenr/* generic structure to iterate through a list.  There are three different  */
5257170268Sdarrenr/* linked lists of NAT related information to go through: NAT rules, active */
5258170268Sdarrenr/* NAT mappings and the NAT fragment cache.                                 */
5259170268Sdarrenr/* ------------------------------------------------------------------------ */
5260170268Sdarrenrstatic int nat_iterator(token, itp)
5261170268Sdarrenripftoken_t *token;
5262170268Sdarrenripfgeniter_t *itp;
5263170268Sdarrenr{
5264170268Sdarrenr	int error;
5265170268Sdarrenr
5266170268Sdarrenr	if (itp->igi_data == NULL)
5267170268Sdarrenr		return EFAULT;
5268170268Sdarrenr
5269170268Sdarrenr	token->ipt_subtype = itp->igi_type;
5270170268Sdarrenr
5271170268Sdarrenr	switch (itp->igi_type)
5272170268Sdarrenr	{
5273170268Sdarrenr	case IPFGENITER_HOSTMAP :
5274170268Sdarrenr	case IPFGENITER_IPNAT :
5275170268Sdarrenr	case IPFGENITER_NAT :
5276170268Sdarrenr		error = nat_getnext(token, itp);
5277170268Sdarrenr		break;
5278170268Sdarrenr
5279170268Sdarrenr	case IPFGENITER_NATFRAG :
5280170268Sdarrenr#ifdef USE_MUTEXES
5281170268Sdarrenr		error = fr_nextfrag(token, itp, &ipfr_natlist,
5282170268Sdarrenr				    &ipfr_nattail, &ipf_natfrag);
5283170268Sdarrenr#else
5284170268Sdarrenr		error = fr_nextfrag(token, itp, &ipfr_natlist, &ipfr_nattail);
5285170268Sdarrenr#endif
5286170268Sdarrenr		break;
5287170268Sdarrenr	default :
5288170268Sdarrenr		error = EINVAL;
5289170268Sdarrenr		break;
5290170268Sdarrenr	}
5291170268Sdarrenr
5292170268Sdarrenr	return error;
5293170268Sdarrenr}
5294170268Sdarrenr
5295170268Sdarrenr
5296170268Sdarrenr/* ------------------------------------------------------------------------ */
5297170268Sdarrenr/* Function:    nat_extraflush                                              */
5298170268Sdarrenr/* Returns:     int - 0 == success, -1 == failure                           */
5299170268Sdarrenr/* Parameters:  which(I) - how to flush the active NAT table                */
5300170268Sdarrenr/* Write Locks: ipf_nat                                                     */
5301170268Sdarrenr/*                                                                          */
5302170268Sdarrenr/* Flush nat tables.  Three actions currently defined:                      */
5303170268Sdarrenr/* which == 0 : flush all nat table entries                                 */
5304170268Sdarrenr/* which == 1 : flush TCP connections which have started to close but are   */
5305170268Sdarrenr/*	      stuck for some reason.                                        */
5306170268Sdarrenr/* which == 2 : flush TCP connections which have been idle for a long time, */
5307170268Sdarrenr/*	      starting at > 4 days idle and working back in successive half-*/
5308170268Sdarrenr/*	      days to at most 12 hours old.  If this fails to free enough   */
5309170268Sdarrenr/*            slots then work backwards in half hour slots to 30 minutes.   */
5310170268Sdarrenr/*            If that too fails, then work backwards in 30 second intervals */
5311170268Sdarrenr/*            for the last 30 minutes to at worst 30 seconds idle.          */
5312170268Sdarrenr/* ------------------------------------------------------------------------ */
5313170268Sdarrenrstatic int nat_extraflush(which)
5314170268Sdarrenrint which;
5315170268Sdarrenr{
5316170268Sdarrenr	ipftq_t *ifq, *ifqnext;
5317170268Sdarrenr	nat_t *nat, **natp;
5318170268Sdarrenr	ipftqent_t *tqn;
5319170268Sdarrenr	int removed;
5320170268Sdarrenr	SPL_INT(s);
5321170268Sdarrenr
5322170268Sdarrenr	removed = 0;
5323170268Sdarrenr
5324170268Sdarrenr	SPL_NET(s);
5325170268Sdarrenr
5326170268Sdarrenr	switch (which)
5327170268Sdarrenr	{
5328170268Sdarrenr	case 0 :
5329170268Sdarrenr		/*
5330170268Sdarrenr		 * Style 0 flush removes everything...
5331170268Sdarrenr		 */
5332170268Sdarrenr		for (natp = &nat_instances; ((nat = *natp) != NULL); ) {
5333170268Sdarrenr			nat_delete(nat, NL_FLUSH);
5334170268Sdarrenr			removed++;
5335170268Sdarrenr		}
5336170268Sdarrenr		break;
5337170268Sdarrenr
5338170268Sdarrenr	case 1 :
5339170268Sdarrenr		/*
5340170268Sdarrenr		 * Since we're only interested in things that are closing,
5341170268Sdarrenr		 * we can start with the appropriate timeout queue.
5342170268Sdarrenr		 */
5343170268Sdarrenr		for (ifq = nat_tqb + IPF_TCPS_CLOSE_WAIT; ifq != NULL;
5344170268Sdarrenr		     ifq = ifq->ifq_next) {
5345170268Sdarrenr
5346170268Sdarrenr			for (tqn = ifq->ifq_head; tqn != NULL; ) {
5347170268Sdarrenr				nat = tqn->tqe_parent;
5348170268Sdarrenr				tqn = tqn->tqe_next;
5349170268Sdarrenr				if (nat->nat_p != IPPROTO_TCP)
5350170268Sdarrenr					break;
5351170268Sdarrenr				nat_delete(nat, NL_EXPIRE);
5352170268Sdarrenr				removed++;
5353170268Sdarrenr			}
5354170268Sdarrenr		}
5355170268Sdarrenr
5356170268Sdarrenr		/*
5357170268Sdarrenr		 * Also need to look through the user defined queues.
5358170268Sdarrenr		 */
5359170268Sdarrenr		for (ifq = nat_utqe; ifq != NULL; ifq = ifqnext) {
5360170268Sdarrenr			ifqnext = ifq->ifq_next;
5361170268Sdarrenr			for (tqn = ifq->ifq_head; tqn != NULL; ) {
5362170268Sdarrenr				nat = tqn->tqe_parent;
5363170268Sdarrenr				tqn = tqn->tqe_next;
5364170268Sdarrenr				if (nat->nat_p != IPPROTO_TCP)
5365170268Sdarrenr					continue;
5366170268Sdarrenr
5367170268Sdarrenr				if ((nat->nat_tcpstate[0] >
5368170268Sdarrenr				     IPF_TCPS_ESTABLISHED) &&
5369170268Sdarrenr				    (nat->nat_tcpstate[1] >
5370170268Sdarrenr				     IPF_TCPS_ESTABLISHED)) {
5371170268Sdarrenr					nat_delete(nat, NL_EXPIRE);
5372170268Sdarrenr					removed++;
5373170268Sdarrenr				}
5374170268Sdarrenr			}
5375170268Sdarrenr		}
5376170268Sdarrenr		break;
5377170268Sdarrenr
5378170268Sdarrenr		/*
5379170268Sdarrenr		 * Args 5-11 correspond to flushing those particular states
5380170268Sdarrenr		 * for TCP connections.
5381170268Sdarrenr		 */
5382170268Sdarrenr	case IPF_TCPS_CLOSE_WAIT :
5383170268Sdarrenr	case IPF_TCPS_FIN_WAIT_1 :
5384170268Sdarrenr	case IPF_TCPS_CLOSING :
5385170268Sdarrenr	case IPF_TCPS_LAST_ACK :
5386170268Sdarrenr	case IPF_TCPS_FIN_WAIT_2 :
5387170268Sdarrenr	case IPF_TCPS_TIME_WAIT :
5388170268Sdarrenr	case IPF_TCPS_CLOSED :
5389170268Sdarrenr		tqn = nat_tqb[which].ifq_head;
5390170268Sdarrenr		while (tqn != NULL) {
5391170268Sdarrenr			nat = tqn->tqe_parent;
5392170268Sdarrenr			tqn = tqn->tqe_next;
5393170268Sdarrenr			nat_delete(nat, NL_FLUSH);
5394170268Sdarrenr			removed++;
5395170268Sdarrenr		}
5396170268Sdarrenr		break;
5397170268Sdarrenr
5398170268Sdarrenr	default :
5399170268Sdarrenr		if (which < 30)
5400170268Sdarrenr			break;
5401170268Sdarrenr
5402170268Sdarrenr		/*
5403170268Sdarrenr		 * Take a large arbitrary number to mean the number of seconds
5404170268Sdarrenr		 * for which which consider to be the maximum value we'll allow
5405170268Sdarrenr		 * the expiration to be.
5406170268Sdarrenr		 */
5407170268Sdarrenr		which = IPF_TTLVAL(which);
5408170268Sdarrenr		for (natp = &nat_instances; ((nat = *natp) != NULL); ) {
5409170268Sdarrenr			if (fr_ticks - nat->nat_touched > which) {
5410170268Sdarrenr				nat_delete(nat, NL_FLUSH);
5411170268Sdarrenr				removed++;
5412170268Sdarrenr			} else
5413170268Sdarrenr				natp = &nat->nat_next;
5414170268Sdarrenr		}
5415170268Sdarrenr		break;
5416170268Sdarrenr	}
5417170268Sdarrenr
5418170268Sdarrenr	if (which != 2) {
5419170268Sdarrenr		SPL_X(s);
5420170268Sdarrenr		return removed;
5421170268Sdarrenr	}
5422170268Sdarrenr
5423170268Sdarrenr	/*
5424170268Sdarrenr	 * Asked to remove inactive entries because the table is full.
5425170268Sdarrenr	 */
5426170268Sdarrenr	if (fr_ticks - nat_last_force_flush > IPF_TTLVAL(5)) {
5427170268Sdarrenr		nat_last_force_flush = fr_ticks;
5428170268Sdarrenr		removed = ipf_queueflush(nat_flush_entry, nat_tqb, nat_utqe);
5429170268Sdarrenr	}
5430170268Sdarrenr
5431170268Sdarrenr	SPL_X(s);
5432170268Sdarrenr	return removed;
5433170268Sdarrenr}
5434170268Sdarrenr
5435170268Sdarrenr
5436170268Sdarrenr/* ------------------------------------------------------------------------ */
5437170268Sdarrenr/* Function:    nat_flush_entry                                             */
5438170268Sdarrenr/* Returns:     0 - always succeeds                                         */
5439170268Sdarrenr/* Parameters:  entry(I) - pointer to NAT entry                             */
5440170268Sdarrenr/* Write Locks: ipf_nat                                                     */
5441170268Sdarrenr/*                                                                          */
5442170268Sdarrenr/* This function is a stepping stone between ipf_queueflush() and           */
5443170268Sdarrenr/* nat_dlete().  It is used so we can provide a uniform interface via the   */
5444170268Sdarrenr/* ipf_queueflush() function.  Since the nat_delete() function returns void */
5445170268Sdarrenr/* we translate that to mean it always succeeds in deleting something.      */
5446170268Sdarrenr/* ------------------------------------------------------------------------ */
5447170268Sdarrenrstatic int nat_flush_entry(entry)
5448170268Sdarrenrvoid *entry;
5449170268Sdarrenr{
5450170268Sdarrenr	nat_delete(entry, NL_FLUSH);
5451170268Sdarrenr	return 0;
5452170268Sdarrenr}
5453172776Sdarrenr
5454172776Sdarrenr
5455172776Sdarrenr/* ------------------------------------------------------------------------ */
5456172776Sdarrenr/* Function:    nat_gettable                                                */
5457172776Sdarrenr/* Returns:     int     - 0 = success, else error                           */
5458172776Sdarrenr/* Parameters:  data(I) - pointer to ioctl data                             */
5459172776Sdarrenr/*                                                                          */
5460172776Sdarrenr/* This function handles ioctl requests for tables of nat information.      */
5461172776Sdarrenr/* At present the only table it deals with is the hash bucket statistics.   */
5462172776Sdarrenr/* ------------------------------------------------------------------------ */
5463172776Sdarrenrstatic int nat_gettable(data)
5464172776Sdarrenrchar *data;
5465172776Sdarrenr{
5466172776Sdarrenr	ipftable_t table;
5467172776Sdarrenr	int error;
5468172776Sdarrenr
5469172776Sdarrenr	error = fr_inobj(data, &table, IPFOBJ_GTABLE);
5470172776Sdarrenr	if (error != 0)
5471172776Sdarrenr		return error;
5472172776Sdarrenr
5473172776Sdarrenr	switch (table.ita_type)
5474172776Sdarrenr	{
5475172776Sdarrenr	case IPFTABLE_BUCKETS_NATIN :
5476172776Sdarrenr		error = COPYOUT(nat_stats.ns_bucketlen[0], table.ita_table,
5477172776Sdarrenr				ipf_nattable_sz * sizeof(u_long));
5478172776Sdarrenr		break;
5479172776Sdarrenr
5480172776Sdarrenr	case IPFTABLE_BUCKETS_NATOUT :
5481172776Sdarrenr		error = COPYOUT(nat_stats.ns_bucketlen[1], table.ita_table,
5482172776Sdarrenr				ipf_nattable_sz * sizeof(u_long));
5483172776Sdarrenr		break;
5484172776Sdarrenr
5485172776Sdarrenr	default :
5486172776Sdarrenr		return EINVAL;
5487172776Sdarrenr	}
5488172776Sdarrenr
5489172776Sdarrenr	if (error != 0) {
5490172776Sdarrenr		error = EFAULT;
5491172776Sdarrenr	}
5492172776Sdarrenr	return error;
5493172776Sdarrenr}
5494