1145522Sdarrenr/*	$FreeBSD: stable/11/sys/contrib/ipfilter/netinet/ip_nat.c 369541 2021-04-01 13:29:16Z git2svn $	*/
2145522Sdarrenr
353642Sguido/*
4255332Scy * Copyright (C) 2012 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>
19255332Scy#if defined(_KERNEL) && \
20255332Scy    (defined(__NetBSD_Version) && (__NetBSD_Version >= 399002000))
21170268Sdarrenr# include <sys/kauth.h>
22170268Sdarrenr#endif
23145522Sdarrenr#if !defined(_KERNEL)
2453642Sguido# include <stdio.h>
2553642Sguido# include <string.h>
2653642Sguido# include <stdlib.h>
27255332Scy# define KERNEL
28255332Scy# ifdef _OpenBSD__
29145522Sdarrenrstruct file;
30145522Sdarrenr# endif
31145522Sdarrenr# include <sys/uio.h>
32255332Scy# undef KERNEL
3353642Sguido#endif
34369277Scy#if defined(_KERNEL) && defined(__FreeBSD__)
3553642Sguido# include <sys/filio.h>
3653642Sguido# include <sys/fcntl.h>
3753642Sguido#else
3853642Sguido# include <sys/ioctl.h>
3953642Sguido#endif
40153876Sguido# include <sys/fcntl.h>
4153642Sguido# include <sys/protosw.h>
4253642Sguido#include <sys/socket.h>
43145522Sdarrenr#if defined(_KERNEL)
4453642Sguido# include <sys/systm.h>
45344833Scy# if !defined(__SVR4)
4653642Sguido#  include <sys/mbuf.h>
4753642Sguido# endif
48145522Sdarrenr#endif
49344833Scy#if defined(__SVR4)
5053642Sguido# include <sys/filio.h>
5153642Sguido# include <sys/byteorder.h>
52255332Scy# ifdef KERNEL
5353642Sguido#  include <sys/dditypes.h>
5453642Sguido# endif
5553642Sguido# include <sys/stream.h>
5653642Sguido# include <sys/kmem.h>
5753642Sguido#endif
58369277Scy#if defined(__FreeBSD__)
5953642Sguido# include <sys/queue.h>
6053642Sguido#endif
6153642Sguido#include <net/if.h>
62369277Scy#if defined(__FreeBSD__)
6353642Sguido# include <net/if_var.h>
6453642Sguido#endif
6553642Sguido#ifdef sun
6653642Sguido# include <net/af.h>
6753642Sguido#endif
6853642Sguido#include <netinet/in.h>
6953642Sguido#include <netinet/in_systm.h>
7053642Sguido#include <netinet/ip.h>
7153642Sguido
7253642Sguido#ifdef RFC1825
7353642Sguido# include <vpn/md5.h>
7453642Sguido# include <vpn/ipsec.h>
7553642Sguidoextern struct ifnet vpnif;
7653642Sguido#endif
7753642Sguido
7853642Sguido# include <netinet/ip_var.h>
7953642Sguido#include <netinet/tcp.h>
8053642Sguido#include <netinet/udp.h>
8153642Sguido#include <netinet/ip_icmp.h>
8253642Sguido#include "netinet/ip_compat.h"
8353642Sguido#include <netinet/tcpip.h>
84255332Scy#include "netinet/ipl.h"
8553642Sguido#include "netinet/ip_fil.h"
8653642Sguido#include "netinet/ip_nat.h"
8753642Sguido#include "netinet/ip_frag.h"
8853642Sguido#include "netinet/ip_state.h"
8992685Sdarrenr#include "netinet/ip_proxy.h"
90255332Scy#include "netinet/ip_lookup.h"
91255332Scy#include "netinet/ip_dstlist.h"
92145522Sdarrenr#include "netinet/ip_sync.h"
93369277Scy#if defined(__FreeBSD__)
9453642Sguido# include <sys/malloc.h>
9553642Sguido#endif
96255332Scy#ifdef HAS_SYS_MD5_H
97255332Scy# include <sys/md5.h>
98255332Scy#else
99255332Scy# include "md5.h"
100255332Scy#endif
101145522Sdarrenr/* END OF INCLUDES */
102145522Sdarrenr
10353642Sguido#undef	SOCKADDR_IN
10453642Sguido#define	SOCKADDR_IN	struct sockaddr_in
10553642Sguido
10680482Sdarrenr#if !defined(lint)
10780482Sdarrenrstatic const char sccsid[] = "@(#)ip_nat.c	1.11 6/5/96 (C) 1995 Darren Reed";
10880482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: stable/11/sys/contrib/ipfilter/netinet/ip_nat.c 369541 2021-04-01 13:29:16Z git2svn $";
109172776Sdarrenr/* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.102 2007/10/16 10:08:10 darrenr Exp $"; */
11080482Sdarrenr#endif
11180482Sdarrenr
112145522Sdarrenr
113255332Scy#define	NATFSUM(n,v,f)	((v) == 4 ? (n)->f.in4.s_addr : (n)->f.i6[0] + \
114255332Scy			 (n)->f.i6[1] + (n)->f.i6[2] + (n)->f.i6[3])
115255332Scy#define	NBUMP(x)	softn->(x)++
116255332Scy#define	NBUMPD(x, y)	do { \
117255332Scy				softn->x.y++; \
118255332Scy				DT(y); \
119255332Scy			} while (0)
120255332Scy#define	NBUMPSIDE(y,x)	softn->ipf_nat_stats.ns_side[y].x++
121255332Scy#define	NBUMPSIDED(y,x)	do { softn->ipf_nat_stats.ns_side[y].x++; \
122255332Scy			     DT(x); } while (0)
123255332Scy#define	NBUMPSIDEX(y,x,z) \
124255332Scy			do { softn->ipf_nat_stats.ns_side[y].x++; \
125255332Scy			     DT(z); } while (0)
126255332Scy#define	NBUMPSIDEDF(y,x)do { softn->ipf_nat_stats.ns_side[y].x++; \
127255332Scy			     DT1(x, fr_info_t *, fin); } while (0)
128255332Scy
129255332Scystatic ipftuneable_t ipf_nat_tuneables[] = {
130255332Scy	/* nat */
131255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_lock) },
132255332Scy		"nat_lock",	0,	1,
133255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_lock),
134255332Scy		IPFT_RDONLY,		NULL,	NULL },
135255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz) },
136255332Scy		"nat_table_size", 1,	0x7fffffff,
137255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_table_sz),
138255332Scy		0,			NULL,	ipf_nat_rehash },
139255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max) },
140255332Scy		"nat_table_max", 1,	0x7fffffff,
141255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_table_max),
142255332Scy		0,			NULL,	NULL },
143255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz) },
144255332Scy		"nat_rules_size", 1,	0x7fffffff,
145255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_maprules_sz),
146255332Scy		0,			NULL,	ipf_nat_rehash_rules },
147255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz) },
148255332Scy		"rdr_rules_size", 1,	0x7fffffff,
149255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_rdrrules_sz),
150255332Scy		0,			NULL,	ipf_nat_rehash_rules },
151255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz) },
152255332Scy		"hostmap_size",	1,	0x7fffffff,
153255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_hostmap_sz),
154255332Scy		0,			NULL,	ipf_nat_hostmap_rehash },
155255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maxbucket) },
156255332Scy		"nat_maxbucket",1,	0x7fffffff,
157255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_maxbucket),
158255332Scy		0,			NULL,	NULL },
159255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_logging) },
160255332Scy		"nat_logging",	0,	1,
161255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_logging),
162255332Scy		0,			NULL,	NULL },
163255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_doflush) },
164255332Scy		"nat_doflush",	0,	1,
165255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_doflush),
166255332Scy		0,			NULL,	NULL },
167255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_low) },
168255332Scy		"nat_table_wm_low",	1,	99,
169255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_low),
170255332Scy		0,			NULL,	NULL },
171255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_high) },
172255332Scy		"nat_table_wm_high",	2,	100,
173255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_high),
174255332Scy		0,			NULL,	NULL },
175255332Scy	{ { 0 },
176255332Scy		NULL,			0,	0,
177255332Scy		0,
178255332Scy		0,			NULL,	NULL }
179255332Scy};
180255332Scy
181145522Sdarrenr/* ======================================================================== */
182145522Sdarrenr/* How the NAT is organised and works.                                      */
183145522Sdarrenr/*                                                                          */
184145522Sdarrenr/* Inside (interface y) NAT       Outside (interface x)                     */
185145522Sdarrenr/* -------------------- -+- -------------------------------------           */
186255332Scy/* Packet going          |   out, processsed by ipf_nat_checkout() for x    */
187145522Sdarrenr/* ------------>         |   ------------>                                  */
188145522Sdarrenr/* src=10.1.1.1          |   src=192.1.1.1                                  */
189145522Sdarrenr/*                       |                                                  */
190255332Scy/*                       |   in, processed by ipf_nat_checkin() for x       */
191145522Sdarrenr/* <------------         |   <------------                                  */
192145522Sdarrenr/* dst=10.1.1.1          |   dst=192.1.1.1                                  */
193145522Sdarrenr/* -------------------- -+- -------------------------------------           */
194255332Scy/* ipf_nat_checkout() - changes ip_src and if required, sport               */
195145522Sdarrenr/*             - creates a new mapping, if required.                        */
196255332Scy/* ipf_nat_checkin()  - changes ip_dst and if required, dport               */
197145522Sdarrenr/*                                                                          */
198145522Sdarrenr/* In the NAT table, internal source is recorded as "in" and externally     */
199145522Sdarrenr/* seen as "out".                                                           */
200145522Sdarrenr/* ======================================================================== */
201145522Sdarrenr
202145522Sdarrenr
203255332Scy#if SOLARIS && !defined(INSTANCES)
204255332Scyextern	int		pfil_delayed_copy;
205255332Scy#endif
206255332Scy
207369245Sgit2svnstatic	int	ipf_nat_flush_entry(ipf_main_softc_t *, void *);
208369245Sgit2svnstatic	int	ipf_nat_getent(ipf_main_softc_t *, caddr_t, int);
209369245Sgit2svnstatic	int	ipf_nat_getsz(ipf_main_softc_t *, caddr_t, int);
210369245Sgit2svnstatic	int	ipf_nat_putent(ipf_main_softc_t *, caddr_t, int);
211369245Sgit2svnstatic	void	ipf_nat_addmap(ipf_nat_softc_t *, ipnat_t *);
212369245Sgit2svnstatic	void	ipf_nat_addrdr(ipf_nat_softc_t *, ipnat_t *);
213369245Sgit2svnstatic	int	ipf_nat_builddivertmp(ipf_nat_softc_t *, ipnat_t *);
214369245Sgit2svnstatic	int	ipf_nat_clearlist(ipf_main_softc_t *, ipf_nat_softc_t *);
215369245Sgit2svnstatic	int	ipf_nat_cmp_rules(ipnat_t *, ipnat_t *);
216369245Sgit2svnstatic	int	ipf_nat_decap(fr_info_t *, nat_t *);
217369245Sgit2svnstatic	void	ipf_nat_delrule(ipf_main_softc_t *, ipf_nat_softc_t *,
218369245Sgit2svn				     ipnat_t *, int);
219369245Sgit2svnstatic	int	ipf_nat_extraflush(ipf_main_softc_t *, ipf_nat_softc_t *, int);
220369245Sgit2svnstatic	int	ipf_nat_finalise(fr_info_t *, nat_t *);
221369245Sgit2svnstatic	int	ipf_nat_flushtable(ipf_main_softc_t *, ipf_nat_softc_t *);
222369245Sgit2svnstatic	int	ipf_nat_getnext(ipf_main_softc_t *, ipftoken_t *,
223369245Sgit2svn				     ipfgeniter_t *, ipfobj_t *);
224369245Sgit2svnstatic	int	ipf_nat_gettable(ipf_main_softc_t *, ipf_nat_softc_t *,
225369245Sgit2svn				      char *);
226369245Sgit2svnstatic	hostmap_t *ipf_nat_hostmap(ipf_nat_softc_t *, ipnat_t *,
227255332Scy					struct in_addr, struct in_addr,
228369245Sgit2svn					struct in_addr, u_32_t);
229369245Sgit2svnstatic	int	ipf_nat_icmpquerytype(int);
230369245Sgit2svnstatic	int	ipf_nat_iterator(ipf_main_softc_t *, ipftoken_t *,
231369245Sgit2svn				      ipfgeniter_t *, ipfobj_t *);
232369245Sgit2svnstatic	int	ipf_nat_match(fr_info_t *, ipnat_t *);
233369245Sgit2svnstatic	int	ipf_nat_matcharray(nat_t *, int *, u_long);
234369245Sgit2svnstatic	int	ipf_nat_matchflush(ipf_main_softc_t *, ipf_nat_softc_t *,
235369245Sgit2svn					caddr_t);
236369245Sgit2svnstatic	void	ipf_nat_mssclamp(tcphdr_t *, u_32_t, fr_info_t *,
237369245Sgit2svn				      u_short *);
238369245Sgit2svnstatic	int	ipf_nat_newmap(fr_info_t *, nat_t *, natinfo_t *);
239369245Sgit2svnstatic	int	ipf_nat_newdivert(fr_info_t *, nat_t *, natinfo_t *);
240369245Sgit2svnstatic	int	ipf_nat_newrdr(fr_info_t *, nat_t *, natinfo_t *);
241369245Sgit2svnstatic	int	ipf_nat_newrewrite(fr_info_t *, nat_t *, natinfo_t *);
242369245Sgit2svnstatic	int	ipf_nat_nextaddr(fr_info_t *, nat_addr_t *, u_32_t *,
243369245Sgit2svn				      u_32_t *);
244369245Sgit2svnstatic	int	ipf_nat_nextaddrinit(ipf_main_softc_t *, char *,
245369245Sgit2svn					  nat_addr_t *, int, void *);
246369245Sgit2svnstatic	int	ipf_nat_resolverule(ipf_main_softc_t *, ipnat_t *);
247369245Sgit2svnstatic	int	ipf_nat_ruleaddrinit(ipf_main_softc_t *,
248369245Sgit2svn					  ipf_nat_softc_t *, ipnat_t *);
249369245Sgit2svnstatic	void	ipf_nat_rule_fini(ipf_main_softc_t *, ipnat_t *);
250369245Sgit2svnstatic	int	ipf_nat_rule_init(ipf_main_softc_t *, ipf_nat_softc_t *,
251369245Sgit2svn				       ipnat_t *);
252369245Sgit2svnstatic	int	ipf_nat_siocaddnat(ipf_main_softc_t *, ipf_nat_softc_t *,
253369245Sgit2svn					ipnat_t *, int);
254369245Sgit2svnstatic	void	ipf_nat_siocdelnat(ipf_main_softc_t *, ipf_nat_softc_t *,
255369245Sgit2svn					ipnat_t *, int);
256369245Sgit2svnstatic	void	ipf_nat_tabmove(ipf_nat_softc_t *, nat_t *);
257255332Scy
258255332Scy/* ------------------------------------------------------------------------ */
259255332Scy/* Function:    ipf_nat_main_load                                           */
260255332Scy/* Returns:     int - 0 == success, -1 == failure                           */
261255332Scy/* Parameters:  Nil                                                         */
262255332Scy/*                                                                          */
263255332Scy/* The only global NAT structure that needs to be initialised is the filter */
264255332Scy/* rule that is used with blocking packets.                                 */
265255332Scy/* ------------------------------------------------------------------------ */
266255332Scyint
267255332Scyipf_nat_main_load()
268255332Scy{
269255332Scy
270255332Scy	return 0;
271255332Scy}
272255332Scy
273255332Scy
274255332Scy/* ------------------------------------------------------------------------ */
275255332Scy/* Function:    ipf_nat_main_unload                                         */
276255332Scy/* Returns:     int - 0 == success, -1 == failure                           */
277255332Scy/* Parameters:  Nil                                                         */
278255332Scy/*                                                                          */
279255332Scy/* A null-op function that exists as a placeholder so that the flow in      */
280255332Scy/* other functions is obvious.                                              */
281255332Scy/* ------------------------------------------------------------------------ */
282255332Scyint
283255332Scyipf_nat_main_unload()
284255332Scy{
285255332Scy	return 0;
286255332Scy}
287255332Scy
288255332Scy
289255332Scy/* ------------------------------------------------------------------------ */
290255332Scy/* Function:    ipf_nat_soft_create                                         */
291255332Scy/* Returns:     void * - NULL = failure, else pointer to NAT context        */
292255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
293255332Scy/*                                                                          */
294255332Scy/* Allocate the initial soft context structure for NAT and populate it with */
295255332Scy/* some default values. Creating the tables is left until we call _init so  */
296255332Scy/* that sizes can be changed before we get under way.                       */
297255332Scy/* ------------------------------------------------------------------------ */
298255332Scyvoid *
299255332Scyipf_nat_soft_create(softc)
300255332Scy	ipf_main_softc_t *softc;
301255332Scy{
302255332Scy	ipf_nat_softc_t *softn;
303255332Scy
304255332Scy	KMALLOC(softn, ipf_nat_softc_t *);
305255332Scy	if (softn == NULL)
306255332Scy		return NULL;
307255332Scy
308255332Scy	bzero((char *)softn, sizeof(*softn));
309255332Scy
310255332Scy	softn->ipf_nat_tune = ipf_tune_array_copy(softn,
311255332Scy						  sizeof(ipf_nat_tuneables),
312255332Scy						  ipf_nat_tuneables);
313255332Scy	if (softn->ipf_nat_tune == NULL) {
314255332Scy		ipf_nat_soft_destroy(softc, softn);
315255332Scy		return NULL;
316255332Scy	}
317255332Scy	if (ipf_tune_array_link(softc, softn->ipf_nat_tune) == -1) {
318255332Scy		ipf_nat_soft_destroy(softc, softn);
319255332Scy		return NULL;
320255332Scy	}
321255332Scy
322255332Scy	softn->ipf_nat_list_tail = &softn->ipf_nat_list;
323255332Scy
324255332Scy	softn->ipf_nat_table_max = NAT_TABLE_MAX;
325255332Scy	softn->ipf_nat_table_sz = NAT_TABLE_SZ;
326255332Scy	softn->ipf_nat_maprules_sz = NAT_SIZE;
327255332Scy	softn->ipf_nat_rdrrules_sz = RDR_SIZE;
328255332Scy	softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE;
329255332Scy	softn->ipf_nat_doflush = 0;
330145522Sdarrenr#ifdef  IPFILTER_LOG
331255332Scy	softn->ipf_nat_logging = 1;
332145522Sdarrenr#else
333255332Scy	softn->ipf_nat_logging = 0;
334145522Sdarrenr#endif
33553642Sguido
336255332Scy	softn->ipf_nat_defage = DEF_NAT_AGE;
337255332Scy	softn->ipf_nat_defipage = IPF_TTLVAL(60);
338255332Scy	softn->ipf_nat_deficmpage = IPF_TTLVAL(3);
339255332Scy	softn->ipf_nat_table_wm_high = 99;
340255332Scy	softn->ipf_nat_table_wm_low = 90;
34153642Sguido
342255332Scy	return softn;
343255332Scy}
34453642Sguido
345255332Scy/* ------------------------------------------------------------------------ */
346255332Scy/* Function:    ipf_nat_soft_destroy                                        */
347255332Scy/* Returns:     Nil                                                         */
348255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
349255332Scy/*                                                                          */
350255332Scy/* ------------------------------------------------------------------------ */
351255332Scyvoid
352255332Scyipf_nat_soft_destroy(softc, arg)
353255332Scy	ipf_main_softc_t *softc;
354255332Scy	void *arg;
355255332Scy{
356255332Scy	ipf_nat_softc_t *softn = arg;
35753642Sguido
358255332Scy	if (softn->ipf_nat_tune != NULL) {
359255332Scy		ipf_tune_array_unlink(softc, softn->ipf_nat_tune);
360255332Scy		KFREES(softn->ipf_nat_tune, sizeof(ipf_nat_tuneables));
361255332Scy		softn->ipf_nat_tune = NULL;
362255332Scy	}
363255332Scy
364255332Scy	KFREE(softn);
365255332Scy}
366255332Scy
367255332Scy
368145522Sdarrenr/* ------------------------------------------------------------------------ */
369255332Scy/* Function:    ipf_nat_init                                                */
370145522Sdarrenr/* Returns:     int - 0 == success, -1 == failure                           */
371255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
372145522Sdarrenr/*                                                                          */
373145522Sdarrenr/* Initialise all of the NAT locks, tables and other structures.            */
374145522Sdarrenr/* ------------------------------------------------------------------------ */
375255332Scyint
376255332Scyipf_nat_soft_init(softc, arg)
377255332Scy	ipf_main_softc_t *softc;
378255332Scy	void *arg;
37953642Sguido{
380255332Scy	ipf_nat_softc_t *softn = arg;
381255332Scy	ipftq_t *tq;
382145522Sdarrenr	int i;
383145522Sdarrenr
384255332Scy	KMALLOCS(softn->ipf_nat_table[0], nat_t **, \
385255332Scy		 sizeof(nat_t *) * softn->ipf_nat_table_sz);
386255332Scy
387255332Scy	if (softn->ipf_nat_table[0] != NULL) {
388255332Scy		bzero((char *)softn->ipf_nat_table[0],
389255332Scy		      softn->ipf_nat_table_sz * sizeof(nat_t *));
390255332Scy	} else {
39153642Sguido		return -1;
392255332Scy	}
39353642Sguido
394255332Scy	KMALLOCS(softn->ipf_nat_table[1], nat_t **, \
395255332Scy		 sizeof(nat_t *) * softn->ipf_nat_table_sz);
396255332Scy
397255332Scy	if (softn->ipf_nat_table[1] != NULL) {
398255332Scy		bzero((char *)softn->ipf_nat_table[1],
399255332Scy		      softn->ipf_nat_table_sz * sizeof(nat_t *));
400255332Scy	} else {
401145522Sdarrenr		return -2;
402255332Scy	}
40353642Sguido
404255332Scy	KMALLOCS(softn->ipf_nat_map_rules, ipnat_t **, \
405255332Scy		 sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz);
406255332Scy
407255332Scy	if (softn->ipf_nat_map_rules != NULL) {
408255332Scy		bzero((char *)softn->ipf_nat_map_rules,
409255332Scy		      softn->ipf_nat_maprules_sz * sizeof(ipnat_t *));
410255332Scy	} else {
411145522Sdarrenr		return -3;
412255332Scy	}
41353642Sguido
414255332Scy	KMALLOCS(softn->ipf_nat_rdr_rules, ipnat_t **, \
415255332Scy		 sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz);
416255332Scy
417255332Scy	if (softn->ipf_nat_rdr_rules != NULL) {
418255332Scy		bzero((char *)softn->ipf_nat_rdr_rules,
419255332Scy		      softn->ipf_nat_rdrrules_sz * sizeof(ipnat_t *));
420255332Scy	} else {
421145522Sdarrenr		return -4;
422255332Scy	}
42360852Sdarrenr
424255332Scy	KMALLOCS(softn->ipf_hm_maptable, hostmap_t **, \
425255332Scy		 sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz);
426255332Scy
427255332Scy	if (softn->ipf_hm_maptable != NULL) {
428255332Scy		bzero((char *)softn->ipf_hm_maptable,
429255332Scy		      sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz);
430255332Scy	} else {
431145522Sdarrenr		return -5;
432255332Scy	}
433255332Scy	softn->ipf_hm_maplist = NULL;
434145522Sdarrenr
435255332Scy	KMALLOCS(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, u_int *,
436255332Scy		 softn->ipf_nat_table_sz * sizeof(u_int));
437255332Scy
438255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen == NULL) {
439145522Sdarrenr		return -6;
440255332Scy	}
441255332Scy	bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
442255332Scy	      softn->ipf_nat_table_sz * sizeof(u_int));
443145522Sdarrenr
444255332Scy	KMALLOCS(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, u_int *,
445255332Scy		 softn->ipf_nat_table_sz * sizeof(u_int));
446255332Scy
447255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen == NULL) {
448145522Sdarrenr		return -7;
449255332Scy	}
450145522Sdarrenr
451255332Scy	bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
452255332Scy	      softn->ipf_nat_table_sz * sizeof(u_int));
453145522Sdarrenr
454255332Scy	if (softn->ipf_nat_maxbucket == 0) {
455255332Scy		for (i = softn->ipf_nat_table_sz; i > 0; i >>= 1)
456255332Scy			softn->ipf_nat_maxbucket++;
457255332Scy		softn->ipf_nat_maxbucket *= 2;
458145522Sdarrenr	}
459145522Sdarrenr
460255332Scy	ipf_sttab_init(softc, softn->ipf_nat_tcptq);
461145522Sdarrenr	/*
462145522Sdarrenr	 * Increase this because we may have "keep state" following this too
463145522Sdarrenr	 * and packet storms can occur if this is removed too quickly.
464145522Sdarrenr	 */
465255332Scy	softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack;
466255332Scy	softn->ipf_nat_tcptq[IPF_TCP_NSTATES - 1].ifq_next =
467255332Scy							&softn->ipf_nat_udptq;
468145522Sdarrenr
469255332Scy	IPFTQ_INIT(&softn->ipf_nat_udptq, softn->ipf_nat_defage,
470255332Scy		   "nat ipftq udp tab");
471255332Scy	softn->ipf_nat_udptq.ifq_next = &softn->ipf_nat_udpacktq;
472255332Scy
473255332Scy	IPFTQ_INIT(&softn->ipf_nat_udpacktq, softn->ipf_nat_defage,
474255332Scy		   "nat ipftq udpack tab");
475255332Scy	softn->ipf_nat_udpacktq.ifq_next = &softn->ipf_nat_icmptq;
476255332Scy
477255332Scy	IPFTQ_INIT(&softn->ipf_nat_icmptq, softn->ipf_nat_deficmpage,
478255332Scy		   "nat icmp ipftq tab");
479255332Scy	softn->ipf_nat_icmptq.ifq_next = &softn->ipf_nat_icmpacktq;
480255332Scy
481255332Scy	IPFTQ_INIT(&softn->ipf_nat_icmpacktq, softn->ipf_nat_defage,
482255332Scy		   "nat icmpack ipftq tab");
483255332Scy	softn->ipf_nat_icmpacktq.ifq_next = &softn->ipf_nat_iptq;
484255332Scy
485255332Scy	IPFTQ_INIT(&softn->ipf_nat_iptq, softn->ipf_nat_defipage,
486255332Scy		   "nat ip ipftq tab");
487255332Scy	softn->ipf_nat_iptq.ifq_next = &softn->ipf_nat_pending;
488255332Scy
489255332Scy	IPFTQ_INIT(&softn->ipf_nat_pending, 1, "nat pending ipftq tab");
490255332Scy	softn->ipf_nat_pending.ifq_next = NULL;
491255332Scy
492255332Scy	for (i = 0, tq = softn->ipf_nat_tcptq; i < IPF_TCP_NSTATES; i++, tq++) {
493255332Scy		if (tq->ifq_ttl < softn->ipf_nat_deficmpage)
494255332Scy			tq->ifq_ttl = softn->ipf_nat_deficmpage;
495145522Sdarrenr#ifdef LARGE_NAT
496255332Scy		else if (tq->ifq_ttl > softn->ipf_nat_defage)
497255332Scy			tq->ifq_ttl = softn->ipf_nat_defage;
498145522Sdarrenr#endif
499145522Sdarrenr	}
500145522Sdarrenr
501145522Sdarrenr	/*
502145522Sdarrenr	 * Increase this because we may have "keep state" following
503145522Sdarrenr	 * this too and packet storms can occur if this is removed
504145522Sdarrenr	 * too quickly.
505145522Sdarrenr	 */
506255332Scy	softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack;
507145522Sdarrenr
508255332Scy	MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex");
509255332Scy	MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex");
510145522Sdarrenr
511255332Scy	softn->ipf_nat_inited = 1;
512145522Sdarrenr
51353642Sguido	return 0;
51453642Sguido}
51553642Sguido
51653642Sguido
517145522Sdarrenr/* ------------------------------------------------------------------------ */
518255332Scy/* Function:    ipf_nat_soft_fini                                           */
519145522Sdarrenr/* Returns:     Nil                                                         */
520255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
521255332Scy/*                                                                          */
522255332Scy/* Free all memory used by NAT structures allocated at runtime.             */
523255332Scy/* ------------------------------------------------------------------------ */
524255332Scyint
525255332Scyipf_nat_soft_fini(softc, arg)
526255332Scy	ipf_main_softc_t *softc;
527255332Scy	void *arg;
528255332Scy{
529255332Scy	ipf_nat_softc_t *softn = arg;
530255332Scy	ipftq_t *ifq, *ifqnext;
531255332Scy
532255332Scy	(void) ipf_nat_clearlist(softc, softn);
533255332Scy	(void) ipf_nat_flushtable(softc, softn);
534255332Scy
535255332Scy	/*
536255332Scy	 * Proxy timeout queues are not cleaned here because although they
537255332Scy	 * exist on the NAT list, ipf_proxy_unload is called after unload
538255332Scy	 * and the proxies actually are responsible for them being created.
539255332Scy	 * Should the proxy timeouts have their own list?  There's no real
540255332Scy	 * justification as this is the only complication.
541255332Scy	 */
542255332Scy	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) {
543255332Scy		ifqnext = ifq->ifq_next;
544255332Scy		if (ipf_deletetimeoutqueue(ifq) == 0)
545255332Scy			ipf_freetimeoutqueue(softc, ifq);
546255332Scy	}
547255332Scy
548255332Scy	if (softn->ipf_nat_table[0] != NULL) {
549255332Scy		KFREES(softn->ipf_nat_table[0],
550255332Scy		       sizeof(nat_t *) * softn->ipf_nat_table_sz);
551255332Scy		softn->ipf_nat_table[0] = NULL;
552255332Scy	}
553255332Scy	if (softn->ipf_nat_table[1] != NULL) {
554255332Scy		KFREES(softn->ipf_nat_table[1],
555255332Scy		       sizeof(nat_t *) * softn->ipf_nat_table_sz);
556255332Scy		softn->ipf_nat_table[1] = NULL;
557255332Scy	}
558255332Scy	if (softn->ipf_nat_map_rules != NULL) {
559255332Scy		KFREES(softn->ipf_nat_map_rules,
560255332Scy		       sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz);
561255332Scy		softn->ipf_nat_map_rules = NULL;
562255332Scy	}
563255332Scy	if (softn->ipf_nat_rdr_rules != NULL) {
564255332Scy		KFREES(softn->ipf_nat_rdr_rules,
565255332Scy		       sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz);
566255332Scy		softn->ipf_nat_rdr_rules = NULL;
567255332Scy	}
568255332Scy	if (softn->ipf_hm_maptable != NULL) {
569255332Scy		KFREES(softn->ipf_hm_maptable,
570255332Scy		       sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz);
571255332Scy		softn->ipf_hm_maptable = NULL;
572255332Scy	}
573255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) {
574255332Scy		KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
575255332Scy		       sizeof(u_int) * softn->ipf_nat_table_sz);
576255332Scy		softn->ipf_nat_stats.ns_side[0].ns_bucketlen = NULL;
577255332Scy	}
578255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) {
579255332Scy		KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
580255332Scy		       sizeof(u_int) * softn->ipf_nat_table_sz);
581255332Scy		softn->ipf_nat_stats.ns_side[1].ns_bucketlen = NULL;
582255332Scy	}
583255332Scy
584255332Scy	if (softn->ipf_nat_inited == 1) {
585255332Scy		softn->ipf_nat_inited = 0;
586255332Scy		ipf_sttab_destroy(softn->ipf_nat_tcptq);
587255332Scy
588255332Scy		MUTEX_DESTROY(&softn->ipf_nat_new);
589255332Scy		MUTEX_DESTROY(&softn->ipf_nat_io);
590255332Scy
591255332Scy		MUTEX_DESTROY(&softn->ipf_nat_udptq.ifq_lock);
592255332Scy		MUTEX_DESTROY(&softn->ipf_nat_udpacktq.ifq_lock);
593255332Scy		MUTEX_DESTROY(&softn->ipf_nat_icmptq.ifq_lock);
594255332Scy		MUTEX_DESTROY(&softn->ipf_nat_icmpacktq.ifq_lock);
595255332Scy		MUTEX_DESTROY(&softn->ipf_nat_iptq.ifq_lock);
596255332Scy		MUTEX_DESTROY(&softn->ipf_nat_pending.ifq_lock);
597255332Scy	}
598255332Scy
599255332Scy	return 0;
600255332Scy}
601255332Scy
602255332Scy
603255332Scy/* ------------------------------------------------------------------------ */
604255332Scy/* Function:    ipf_nat_setlock                                             */
605255332Scy/* Returns:     Nil                                                         */
606255332Scy/* Parameters:  arg(I) - pointer to soft state information                  */
607255332Scy/*              tmp(I) - new lock value                                     */
608255332Scy/*                                                                          */
609255332Scy/* Set the "lock status" of NAT to the value in tmp.                        */
610255332Scy/* ------------------------------------------------------------------------ */
611255332Scyvoid
612255332Scyipf_nat_setlock(arg, tmp)
613255332Scy	void *arg;
614255332Scy	int tmp;
615255332Scy{
616255332Scy	ipf_nat_softc_t *softn = arg;
617255332Scy
618255332Scy	softn->ipf_nat_lock = tmp;
619255332Scy}
620255332Scy
621255332Scy
622255332Scy/* ------------------------------------------------------------------------ */
623255332Scy/* Function:    ipf_nat_addrdr                                              */
624255332Scy/* Returns:     Nil                                                         */
625145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to add                           */
626145522Sdarrenr/*                                                                          */
627145522Sdarrenr/* Adds a redirect rule to the hash table of redirect rules and the list of */
628145522Sdarrenr/* loaded NAT rules.  Updates the bitmask indicating which netmasks are in  */
629145522Sdarrenr/* use by redirect rules.                                                   */
630145522Sdarrenr/* ------------------------------------------------------------------------ */
631255332Scystatic void
632255332Scyipf_nat_addrdr(softn, n)
633255332Scy	ipf_nat_softc_t *softn;
634255332Scy	ipnat_t *n;
63553642Sguido{
63660852Sdarrenr	ipnat_t **np;
63760852Sdarrenr	u_32_t j;
63853642Sguido	u_int hv;
639255332Scy	u_int rhv;
64060852Sdarrenr	int k;
64153642Sguido
642255332Scy	if (n->in_odstatype == FRI_NORMAL) {
643255332Scy		k = count4bits(n->in_odstmsk);
644255332Scy		ipf_inet_mask_add(k, &softn->ipf_nat_rdr_mask);
645255332Scy		j = (n->in_odstaddr & n->in_odstmsk);
646255332Scy		rhv = NAT_HASH_FN(j, 0, 0xffffffff);
647255332Scy	} else {
648255332Scy		ipf_inet_mask_add(0, &softn->ipf_nat_rdr_mask);
649255332Scy		j = 0;
650255332Scy		rhv = 0;
651255332Scy	}
652255332Scy	hv = rhv % softn->ipf_nat_rdrrules_sz;
653255332Scy	np = softn->ipf_nat_rdr_rules + hv;
65460852Sdarrenr	while (*np != NULL)
65560852Sdarrenr		np = &(*np)->in_rnext;
65660852Sdarrenr	n->in_rnext = NULL;
65760852Sdarrenr	n->in_prnext = np;
658255332Scy	n->in_hv[0] = hv;
659255332Scy	n->in_use++;
66060852Sdarrenr	*np = n;
66153642Sguido}
66253642Sguido
66353642Sguido
664145522Sdarrenr/* ------------------------------------------------------------------------ */
665255332Scy/* Function:    ipf_nat_addmap                                              */
666145522Sdarrenr/* Returns:     Nil                                                         */
667145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to add                           */
668145522Sdarrenr/*                                                                          */
669145522Sdarrenr/* Adds a NAT map rule to the hash table of rules and the list of  loaded   */
670145522Sdarrenr/* NAT rules.  Updates the bitmask indicating which netmasks are in use by  */
671145522Sdarrenr/* redirect rules.                                                          */
672145522Sdarrenr/* ------------------------------------------------------------------------ */
673255332Scystatic void
674255332Scyipf_nat_addmap(softn, n)
675255332Scy	ipf_nat_softc_t *softn;
676255332Scy	ipnat_t *n;
67760852Sdarrenr{
67860852Sdarrenr	ipnat_t **np;
67960852Sdarrenr	u_32_t j;
68060852Sdarrenr	u_int hv;
681255332Scy	u_int rhv;
68260852Sdarrenr	int k;
68360852Sdarrenr
684255332Scy	if (n->in_osrcatype == FRI_NORMAL) {
685255332Scy		k = count4bits(n->in_osrcmsk);
686255332Scy		ipf_inet_mask_add(k, &softn->ipf_nat_map_mask);
687255332Scy		j = (n->in_osrcaddr & n->in_osrcmsk);
688255332Scy		rhv = NAT_HASH_FN(j, 0, 0xffffffff);
689255332Scy	} else {
690255332Scy		ipf_inet_mask_add(0, &softn->ipf_nat_map_mask);
691255332Scy		j = 0;
692255332Scy		rhv = 0;
693255332Scy	}
694255332Scy	hv = rhv % softn->ipf_nat_maprules_sz;
695255332Scy	np = softn->ipf_nat_map_rules + hv;
69660852Sdarrenr	while (*np != NULL)
69760852Sdarrenr		np = &(*np)->in_mnext;
69860852Sdarrenr	n->in_mnext = NULL;
69960852Sdarrenr	n->in_pmnext = np;
700255332Scy	n->in_hv[1] = rhv;
701255332Scy	n->in_use++;
70260852Sdarrenr	*np = n;
70360852Sdarrenr}
70460852Sdarrenr
70560852Sdarrenr
706145522Sdarrenr/* ------------------------------------------------------------------------ */
707255332Scy/* Function:    ipf_nat_delrdr                                              */
708145522Sdarrenr/* Returns:     Nil                                                         */
709145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to delete                        */
710145522Sdarrenr/*                                                                          */
711145522Sdarrenr/* Removes a redirect rule from the hash table of redirect rules.           */
712145522Sdarrenr/* ------------------------------------------------------------------------ */
713255332Scyvoid
714255332Scyipf_nat_delrdr(softn, n)
715255332Scy	ipf_nat_softc_t *softn;
716255332Scy	ipnat_t *n;
71760852Sdarrenr{
718255332Scy	if (n->in_odstatype == FRI_NORMAL) {
719255332Scy		int k = count4bits(n->in_odstmsk);
720255332Scy		ipf_inet_mask_del(k, &softn->ipf_nat_rdr_mask);
721255332Scy	} else {
722255332Scy		ipf_inet_mask_del(0, &softn->ipf_nat_rdr_mask);
723255332Scy	}
72460852Sdarrenr	if (n->in_rnext)
72560852Sdarrenr		n->in_rnext->in_prnext = n->in_prnext;
72660852Sdarrenr	*n->in_prnext = n->in_rnext;
727255332Scy	n->in_use--;
72860852Sdarrenr}
72960852Sdarrenr
73060852Sdarrenr
731145522Sdarrenr/* ------------------------------------------------------------------------ */
732255332Scy/* Function:    ipf_nat_delmap                                              */
733145522Sdarrenr/* Returns:     Nil                                                         */
734145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to delete                        */
735145522Sdarrenr/*                                                                          */
736145522Sdarrenr/* Removes a NAT map rule from the hash table of NAT map rules.             */
737145522Sdarrenr/* ------------------------------------------------------------------------ */
738255332Scyvoid
739255332Scyipf_nat_delmap(softn, n)
740255332Scy	ipf_nat_softc_t *softn;
741255332Scy	ipnat_t *n;
74253642Sguido{
743255332Scy	if (n->in_osrcatype == FRI_NORMAL) {
744255332Scy		int k = count4bits(n->in_osrcmsk);
745255332Scy		ipf_inet_mask_del(k, &softn->ipf_nat_map_mask);
746255332Scy	} else {
747255332Scy		ipf_inet_mask_del(0, &softn->ipf_nat_map_mask);
748255332Scy	}
749145522Sdarrenr	if (n->in_mnext != NULL)
75060852Sdarrenr		n->in_mnext->in_pmnext = n->in_pmnext;
75160852Sdarrenr	*n->in_pmnext = n->in_mnext;
752255332Scy	n->in_use--;
75360852Sdarrenr}
75460852Sdarrenr
75560852Sdarrenr
756145522Sdarrenr/* ------------------------------------------------------------------------ */
757255332Scy/* Function:    ipf_nat_hostmap                                             */
758145522Sdarrenr/* Returns:     struct hostmap* - NULL if no hostmap could be created,      */
759145522Sdarrenr/*                                else a pointer to the hostmapping to use  */
760145522Sdarrenr/* Parameters:  np(I)   - pointer to NAT rule                               */
761145522Sdarrenr/*              real(I) - real IP address                                   */
762145522Sdarrenr/*              map(I)  - mapped IP address                                 */
763145522Sdarrenr/*              port(I) - destination port number                           */
764145522Sdarrenr/* Write Locks: ipf_nat                                                     */
765145522Sdarrenr/*                                                                          */
766145522Sdarrenr/* Check if an ip address has already been allocated for a given mapping    */
767145522Sdarrenr/* that is not doing port based translation.  If is not yet allocated, then */
768145522Sdarrenr/* create a new entry if a non-NULL NAT rule pointer has been supplied.     */
769145522Sdarrenr/* ------------------------------------------------------------------------ */
770255332Scystatic struct hostmap *
771255332Scyipf_nat_hostmap(softn, np, src, dst, map, port)
772255332Scy	ipf_nat_softc_t *softn;
773255332Scy	ipnat_t *np;
774255332Scy	struct in_addr src;
775255332Scy	struct in_addr dst;
776255332Scy	struct in_addr map;
777255332Scy	u_32_t port;
77860852Sdarrenr{
77960852Sdarrenr	hostmap_t *hm;
780255332Scy	u_int hv, rhv;
78153642Sguido
782145522Sdarrenr	hv = (src.s_addr ^ dst.s_addr);
783145522Sdarrenr	hv += src.s_addr;
784145522Sdarrenr	hv += dst.s_addr;
785255332Scy	rhv = hv;
786255332Scy	hv %= softn->ipf_nat_hostmap_sz;
787255332Scy	for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_hnext)
788255332Scy		if ((hm->hm_osrcip.s_addr == src.s_addr) &&
789255332Scy		    (hm->hm_odstip.s_addr == dst.s_addr) &&
790145522Sdarrenr		    ((np == NULL) || (np == hm->hm_ipnat)) &&
791145522Sdarrenr		    ((port == 0) || (port == hm->hm_port))) {
792255332Scy			softn->ipf_nat_stats.ns_hm_addref++;
79360852Sdarrenr			hm->hm_ref++;
79460852Sdarrenr			return hm;
79560852Sdarrenr		}
79660852Sdarrenr
797255332Scy	if (np == NULL) {
798255332Scy		softn->ipf_nat_stats.ns_hm_nullnp++;
799145522Sdarrenr		return NULL;
800255332Scy	}
801145522Sdarrenr
80260852Sdarrenr	KMALLOC(hm, hostmap_t *);
80360852Sdarrenr	if (hm) {
804255332Scy		hm->hm_next = softn->ipf_hm_maplist;
805255332Scy		hm->hm_pnext = &softn->ipf_hm_maplist;
806255332Scy		if (softn->ipf_hm_maplist != NULL)
807255332Scy			softn->ipf_hm_maplist->hm_pnext = &hm->hm_next;
808255332Scy		softn->ipf_hm_maplist = hm;
809255332Scy		hm->hm_hnext = softn->ipf_hm_maptable[hv];
810255332Scy		hm->hm_phnext = softn->ipf_hm_maptable + hv;
811255332Scy		if (softn->ipf_hm_maptable[hv] != NULL)
812255332Scy			softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext;
813255332Scy		softn->ipf_hm_maptable[hv] = hm;
81460852Sdarrenr		hm->hm_ipnat = np;
815255332Scy		np->in_use++;
816255332Scy		hm->hm_osrcip = src;
817255332Scy		hm->hm_odstip = dst;
818255332Scy		hm->hm_nsrcip = map;
819255332Scy		hm->hm_ndstip.s_addr = 0;
82060852Sdarrenr		hm->hm_ref = 1;
821145522Sdarrenr		hm->hm_port = port;
822255332Scy		hm->hm_hv = rhv;
823255332Scy		hm->hm_v = 4;
824255332Scy		softn->ipf_nat_stats.ns_hm_new++;
825255332Scy	} else {
826255332Scy		softn->ipf_nat_stats.ns_hm_newfail++;
82760852Sdarrenr	}
82860852Sdarrenr	return hm;
82953642Sguido}
83053642Sguido
83153642Sguido
832145522Sdarrenr/* ------------------------------------------------------------------------ */
833255332Scy/* Function:    ipf_nat_hostmapdel                                          */
834145522Sdarrenr/* Returns:     Nil                                                         */
835170268Sdarrenr/* Parameters:  hmp(I) - pointer to hostmap structure pointer               */
836145522Sdarrenr/* Write Locks: ipf_nat                                                     */
837145522Sdarrenr/*                                                                          */
838145522Sdarrenr/* Decrement the references to this hostmap structure by one.  If this      */
839145522Sdarrenr/* reaches zero then remove it and free it.                                 */
840145522Sdarrenr/* ------------------------------------------------------------------------ */
841255332Scyvoid
842255332Scyipf_nat_hostmapdel(softc, hmp)
843255332Scy	ipf_main_softc_t *softc;
844255332Scy	struct hostmap **hmp;
84560852Sdarrenr{
846170268Sdarrenr	struct hostmap *hm;
847170268Sdarrenr
848170268Sdarrenr	hm = *hmp;
849170268Sdarrenr	*hmp = NULL;
850170268Sdarrenr
851145522Sdarrenr	hm->hm_ref--;
85260852Sdarrenr	if (hm->hm_ref == 0) {
853255332Scy		ipf_nat_rule_deref(softc, &hm->hm_ipnat);
854170268Sdarrenr		if (hm->hm_hnext)
855170268Sdarrenr			hm->hm_hnext->hm_phnext = hm->hm_phnext;
856170268Sdarrenr		*hm->hm_phnext = hm->hm_hnext;
85760852Sdarrenr		if (hm->hm_next)
85860852Sdarrenr			hm->hm_next->hm_pnext = hm->hm_pnext;
85960852Sdarrenr		*hm->hm_pnext = hm->hm_next;
86060852Sdarrenr		KFREE(hm);
86160852Sdarrenr	}
86260852Sdarrenr}
86360852Sdarrenr
86460852Sdarrenr
865145522Sdarrenr/* ------------------------------------------------------------------------ */
866255332Scy/* Function:    ipf_fix_outcksum                                            */
867145522Sdarrenr/* Returns:     Nil                                                         */
868145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
869145522Sdarrenr/*              sp(I)  - location of 16bit checksum to update               */
870145522Sdarrenr/*              n((I)  - amount to adjust checksum by                       */
871145522Sdarrenr/*                                                                          */
872145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going out.                 */
873145522Sdarrenr/* ------------------------------------------------------------------------ */
874255332Scyvoid
875255332Scyipf_fix_outcksum(cksum, sp, n, partial)
876255332Scy	int cksum;
877255332Scy	u_short *sp;
878255332Scy	u_32_t n, partial;
87953642Sguido{
880145522Sdarrenr	u_short sumshort;
881145522Sdarrenr	u_32_t sum1;
88253642Sguido
883145522Sdarrenr	if (n == 0)
88453642Sguido		return;
885145522Sdarrenr
886255332Scy	if (cksum == 4) {
887255332Scy		*sp = 0;
88855929Sguido		return;
88955929Sguido	}
890255332Scy	if (cksum == 2) {
891255332Scy		sum1 = partial;
892255332Scy		sum1 = (sum1 & 0xffff) + (sum1 >> 16);
893255332Scy		*sp = htons(sum1);
894255332Scy		return;
895255332Scy	}
89653642Sguido	sum1 = (~ntohs(*sp)) & 0xffff;
89753642Sguido	sum1 += (n);
89853642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
89953642Sguido	/* Again */
90053642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
90153642Sguido	sumshort = ~(u_short)sum1;
90253642Sguido	*(sp) = htons(sumshort);
90353642Sguido}
90453642Sguido
90553642Sguido
906145522Sdarrenr/* ------------------------------------------------------------------------ */
907255332Scy/* Function:    ipf_fix_incksum                                             */
908145522Sdarrenr/* Returns:     Nil                                                         */
909145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
910145522Sdarrenr/*              sp(I)  - location of 16bit checksum to update               */
911145522Sdarrenr/*              n((I)  - amount to adjust checksum by                       */
912145522Sdarrenr/*                                                                          */
913145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going in.                  */
914145522Sdarrenr/* ------------------------------------------------------------------------ */
915255332Scyvoid
916255332Scyipf_fix_incksum(cksum, sp, n, partial)
917255332Scy	int cksum;
918255332Scy	u_short *sp;
919255332Scy	u_32_t n, partial;
92053642Sguido{
921145522Sdarrenr	u_short sumshort;
922145522Sdarrenr	u_32_t sum1;
92353642Sguido
924145522Sdarrenr	if (n == 0)
92553642Sguido		return;
926145522Sdarrenr
927255332Scy	if (cksum == 4) {
928255332Scy		*sp = 0;
92955929Sguido		return;
93055929Sguido	}
931255332Scy	if (cksum == 2) {
932255332Scy		sum1 = partial;
933255332Scy		sum1 = (sum1 & 0xffff) + (sum1 >> 16);
934255332Scy		*sp = htons(sum1);
935255332Scy		return;
936255332Scy	}
937255332Scy
93853642Sguido	sum1 = (~ntohs(*sp)) & 0xffff;
93953642Sguido	sum1 += ~(n) & 0xffff;
94053642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
94153642Sguido	/* Again */
94253642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
94353642Sguido	sumshort = ~(u_short)sum1;
94453642Sguido	*(sp) = htons(sumshort);
94553642Sguido}
94653642Sguido
94753642Sguido
948145522Sdarrenr/* ------------------------------------------------------------------------ */
949255332Scy/* Function:    ipf_fix_datacksum                                           */
950145522Sdarrenr/* Returns:     Nil                                                         */
951145522Sdarrenr/* Parameters:  sp(I)  - location of 16bit checksum to update               */
952145522Sdarrenr/*              n((I)  - amount to adjust checksum by                       */
953145522Sdarrenr/*                                                                          */
954145522Sdarrenr/* Fix_datacksum is used *only* for the adjustments of checksums in the     */
955145522Sdarrenr/* data section of an IP packet.                                            */
956145522Sdarrenr/*                                                                          */
957145522Sdarrenr/* The only situation in which you need to do this is when NAT'ing an       */
958145522Sdarrenr/* ICMP error message. Such a message, contains in its body the IP header   */
959145522Sdarrenr/* of the original IP packet, that causes the error.                        */
960145522Sdarrenr/*                                                                          */
961145522Sdarrenr/* You can't use fix_incksum or fix_outcksum in that case, because for the  */
962145522Sdarrenr/* kernel the data section of the ICMP error is just data, and no special   */
963145522Sdarrenr/* processing like hardware cksum or ntohs processing have been done by the */
964145522Sdarrenr/* kernel on the data section.                                              */
965145522Sdarrenr/* ------------------------------------------------------------------------ */
966255332Scyvoid
967255332Scyipf_fix_datacksum(sp, n)
968255332Scy	u_short *sp;
969255332Scy	u_32_t n;
97067614Sdarrenr{
971145522Sdarrenr	u_short sumshort;
972145522Sdarrenr	u_32_t sum1;
97367614Sdarrenr
974145522Sdarrenr	if (n == 0)
97567614Sdarrenr		return;
97667614Sdarrenr
97767614Sdarrenr	sum1 = (~ntohs(*sp)) & 0xffff;
97867614Sdarrenr	sum1 += (n);
97967614Sdarrenr	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
98067614Sdarrenr	/* Again */
98167614Sdarrenr	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
98267614Sdarrenr	sumshort = ~(u_short)sum1;
98367614Sdarrenr	*(sp) = htons(sumshort);
98467614Sdarrenr}
98567614Sdarrenr
98653642Sguido
987145522Sdarrenr/* ------------------------------------------------------------------------ */
988255332Scy/* Function:    ipf_nat_ioctl                                               */
989145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
990255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
991255332Scy/*              data(I)  - pointer to ioctl data                            */
992255332Scy/*              cmd(I)   - ioctl command integer                            */
993255332Scy/*              mode(I)  - file mode bits used with open                    */
994255332Scy/*              uid(I)   - uid of calling process                           */
995255332Scy/*              ctx(I)   - pointer used as key for finding context          */
996145522Sdarrenr/*                                                                          */
997145522Sdarrenr/* Processes an ioctl call made to operate on the IP Filter NAT device.     */
998145522Sdarrenr/* ------------------------------------------------------------------------ */
999255332Scyint
1000255332Scyipf_nat_ioctl(softc, data, cmd, mode, uid, ctx)
1001255332Scy	ipf_main_softc_t *softc;
1002255332Scy	ioctlcmd_t cmd;
1003255332Scy	caddr_t data;
1004255332Scy	int mode, uid;
1005255332Scy	void *ctx;
100653642Sguido{
1007255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
100895418Sdarrenr	int error = 0, ret, arg, getlock;
1009255332Scy	ipnat_t *nat, *nt, *n;
101053642Sguido	ipnat_t natd;
1011170268Sdarrenr	SPL_INT(s);
101253642Sguido
1013358666Scy#if !SOLARIS && defined(_KERNEL)
1014255332Scy# if NETBSD_GE_REV(399002000)
1015170268Sdarrenr	if ((mode & FWRITE) &&
1016170268Sdarrenr	     kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL,
1017170268Sdarrenr				     KAUTH_REQ_NETWORK_FIREWALL_FW,
1018255332Scy				     NULL, NULL, NULL))
1019170268Sdarrenr# else
1020369277Scy#  if defined(__FreeBSD__)
1021255332Scy	if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE))
1022192895Sjamie#  else
1023255332Scy	if ((securelevel >= 3) && (mode & FWRITE))
1024192895Sjamie#  endif
1025255332Scy# endif
1026255332Scy	{
1027255332Scy		IPFERROR(60001);
1028170268Sdarrenr		return EPERM;
1029170268Sdarrenr	}
103053642Sguido#endif
103153642Sguido
1032145522Sdarrenr	getlock = (mode & NAT_LOCKHELD) ? 0 : 1;
1033145522Sdarrenr
1034255332Scy	n = NULL;
1035255332Scy	nt = NULL;
1036255332Scy	nat = NULL;
1037145522Sdarrenr
1038255332Scy	if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT) ||
1039255332Scy	    (cmd == (ioctlcmd_t)SIOCPURGENAT)) {
104095418Sdarrenr		if (mode & NAT_SYSSPACE) {
104195418Sdarrenr			bcopy(data, (char *)&natd, sizeof(natd));
1042255332Scy			nat = &natd;
104395418Sdarrenr			error = 0;
104495418Sdarrenr		} else {
1045255332Scy			bzero(&natd, sizeof(natd));
1046255332Scy			error = ipf_inobj(softc, data, NULL, &natd,
1047255332Scy					  IPFOBJ_IPNAT);
1048255332Scy			if (error != 0)
1049255332Scy				goto done;
1050255332Scy
1051255332Scy			if (natd.in_size < sizeof(ipnat_t)) {
1052255332Scy				error = EINVAL;
1053255332Scy				goto done;
1054255332Scy			}
1055255332Scy			KMALLOCS(nt, ipnat_t *, natd.in_size);
1056255332Scy			if (nt == NULL) {
1057255332Scy				IPFERROR(60070);
1058255332Scy				error = ENOMEM;
1059255332Scy				goto done;
1060255332Scy			}
1061255332Scy			bzero(nt, natd.in_size);
1062255332Scy			error = ipf_inobjsz(softc, data, nt, IPFOBJ_IPNAT,
1063255332Scy					    natd.in_size);
1064255332Scy			if (error)
1065255332Scy				goto done;
1066255332Scy			nat = nt;
106795418Sdarrenr		}
106853642Sguido
1069255332Scy		/*
1070255332Scy		 * For add/delete, look to see if the NAT entry is
1071255332Scy		 * already present
1072255332Scy		 */
107353642Sguido		nat->in_flags &= IPN_USERFLAGS;
107453642Sguido		if ((nat->in_redir & NAT_MAPBLK) == 0) {
1075255332Scy			if (nat->in_osrcatype == FRI_NORMAL ||
1076255332Scy			    nat->in_osrcatype == FRI_NONE)
1077255332Scy				nat->in_osrcaddr &= nat->in_osrcmsk;
1078255332Scy			if (nat->in_odstatype == FRI_NORMAL ||
1079255332Scy			    nat->in_odstatype == FRI_NONE)
1080255332Scy				nat->in_odstaddr &= nat->in_odstmsk;
1081255332Scy			if ((nat->in_flags & (IPN_SPLIT|IPN_SIPRANGE)) == 0) {
1082255332Scy				if (nat->in_nsrcatype == FRI_NORMAL)
1083255332Scy					nat->in_nsrcaddr &= nat->in_nsrcmsk;
1084255332Scy				if (nat->in_ndstatype == FRI_NORMAL)
1085255332Scy					nat->in_ndstaddr &= nat->in_ndstmsk;
1086255332Scy			}
108753642Sguido		}
1088255332Scy
1089255332Scy		error = ipf_nat_rule_init(softc, softn, nat);
1090255332Scy		if (error != 0)
1091255332Scy			goto done;
1092255332Scy
1093255332Scy		MUTEX_ENTER(&softn->ipf_nat_io);
1094255332Scy		for (n = softn->ipf_nat_list; n != NULL; n = n->in_next)
1095255332Scy			if (ipf_nat_cmp_rules(nat, n) == 0)
109653642Sguido				break;
109753642Sguido	}
109853642Sguido
109953642Sguido	switch (cmd)
110053642Sguido	{
110155929Sguido#ifdef  IPFILTER_LOG
110255929Sguido	case SIOCIPFFB :
110360852Sdarrenr	{
110460852Sdarrenr		int tmp;
110560852Sdarrenr
1106255332Scy		if (!(mode & FWRITE)) {
1107255332Scy			IPFERROR(60002);
110855929Sguido			error = EPERM;
1109255332Scy		} else {
1110255332Scy			tmp = ipf_log_clear(softc, IPL_LOGNAT);
1111255332Scy			error = BCOPYOUT(&tmp, data, sizeof(tmp));
1112255332Scy			if (error != 0) {
1113255332Scy				IPFERROR(60057);
1114170268Sdarrenr				error = EFAULT;
1115255332Scy			}
111660852Sdarrenr		}
111755929Sguido		break;
111860852Sdarrenr	}
1119170268Sdarrenr
1120145522Sdarrenr	case SIOCSETLG :
1121255332Scy		if (!(mode & FWRITE)) {
1122255332Scy			IPFERROR(60003);
1123145522Sdarrenr			error = EPERM;
1124255332Scy		} else {
1125255332Scy			error = BCOPYIN(data, &softn->ipf_nat_logging,
1126255332Scy					sizeof(softn->ipf_nat_logging));
1127170268Sdarrenr			if (error != 0)
1128170268Sdarrenr				error = EFAULT;
1129145522Sdarrenr		}
1130145522Sdarrenr		break;
1131170268Sdarrenr
1132145522Sdarrenr	case SIOCGETLG :
1133255332Scy		error = BCOPYOUT(&softn->ipf_nat_logging, data,
1134255332Scy				 sizeof(softn->ipf_nat_logging));
1135255332Scy		if (error != 0) {
1136255332Scy			IPFERROR(60004);
1137170268Sdarrenr			error = EFAULT;
1138255332Scy		}
1139145522Sdarrenr		break;
1140170268Sdarrenr
1141145522Sdarrenr	case FIONREAD :
1142255332Scy		arg = ipf_log_bytesused(softc, IPL_LOGNAT);
1143170268Sdarrenr		error = BCOPYOUT(&arg, data, sizeof(arg));
1144255332Scy		if (error != 0) {
1145255332Scy			IPFERROR(60005);
1146170268Sdarrenr			error = EFAULT;
1147255332Scy		}
1148145522Sdarrenr		break;
114955929Sguido#endif
115053642Sguido	case SIOCADNAT :
115153642Sguido		if (!(mode & FWRITE)) {
1152255332Scy			IPFERROR(60006);
115353642Sguido			error = EPERM;
1154145522Sdarrenr		} else if (n != NULL) {
1155255332Scy			natd.in_flineno = n->in_flineno;
1156255332Scy			(void) ipf_outobj(softc, data, &natd, IPFOBJ_IPNAT);
1157255332Scy			IPFERROR(60007);
115853642Sguido			error = EEXIST;
1159145522Sdarrenr		} else if (nt == NULL) {
1160255332Scy			IPFERROR(60008);
1161145522Sdarrenr			error = ENOMEM;
116253642Sguido		}
1163145522Sdarrenr		if (error != 0) {
1164255332Scy			MUTEX_EXIT(&softn->ipf_nat_io);
116553642Sguido			break;
116653642Sguido		}
1167255332Scy		if (nat != nt)
1168255332Scy			bcopy((char *)nat, (char *)nt, sizeof(*n));
1169255332Scy		error = ipf_nat_siocaddnat(softc, softn, nt, getlock);
1170255332Scy		MUTEX_EXIT(&softn->ipf_nat_io);
1171255332Scy		if (error == 0) {
1172255332Scy			nat = NULL;
1173145522Sdarrenr			nt = NULL;
1174255332Scy		}
117553642Sguido		break;
1176170268Sdarrenr
117753642Sguido	case SIOCRMNAT :
1178255332Scy	case SIOCPURGENAT :
117953642Sguido		if (!(mode & FWRITE)) {
1180255332Scy			IPFERROR(60009);
118153642Sguido			error = EPERM;
118253642Sguido			n = NULL;
1183145522Sdarrenr		} else if (n == NULL) {
1184255332Scy			IPFERROR(60010);
1185145522Sdarrenr			error = ESRCH;
118653642Sguido		}
1187145522Sdarrenr
1188145522Sdarrenr		if (error != 0) {
1189255332Scy			MUTEX_EXIT(&softn->ipf_nat_io);
119053642Sguido			break;
119153642Sguido		}
1192255332Scy		if (cmd == (ioctlcmd_t)SIOCPURGENAT) {
1193255332Scy			error = ipf_outobjsz(softc, data, n, IPFOBJ_IPNAT,
1194255332Scy					     n->in_size);
1195255332Scy			if (error) {
1196255332Scy				MUTEX_EXIT(&softn->ipf_nat_io);
1197255332Scy				goto done;
1198255332Scy			}
1199255332Scy			n->in_flags |= IPN_PURGE;
1200255332Scy		}
1201255332Scy		ipf_nat_siocdelnat(softc, softn, n, getlock);
1202145522Sdarrenr
1203255332Scy		MUTEX_EXIT(&softn->ipf_nat_io);
120453642Sguido		n = NULL;
120553642Sguido		break;
1206170268Sdarrenr
120753642Sguido	case SIOCGNATS :
1208255332Scy	    {
1209255332Scy		natstat_t *nsp = &softn->ipf_nat_stats;
1210255332Scy
1211255332Scy		nsp->ns_side[0].ns_table = softn->ipf_nat_table[0];
1212255332Scy		nsp->ns_side[1].ns_table = softn->ipf_nat_table[1];
1213255332Scy		nsp->ns_list = softn->ipf_nat_list;
1214255332Scy		nsp->ns_maptable = softn->ipf_hm_maptable;
1215255332Scy		nsp->ns_maplist = softn->ipf_hm_maplist;
1216255332Scy		nsp->ns_nattab_sz = softn->ipf_nat_table_sz;
1217255332Scy		nsp->ns_nattab_max = softn->ipf_nat_table_max;
1218255332Scy		nsp->ns_rultab_sz = softn->ipf_nat_maprules_sz;
1219255332Scy		nsp->ns_rdrtab_sz = softn->ipf_nat_rdrrules_sz;
1220255332Scy		nsp->ns_hostmap_sz = softn->ipf_nat_hostmap_sz;
1221255332Scy		nsp->ns_instances = softn->ipf_nat_instances;
1222255332Scy		nsp->ns_ticks = softc->ipf_ticks;
1223255332Scy#ifdef IPFILTER_LOGGING
1224255332Scy		nsp->ns_log_ok = ipf_log_logok(softc, IPF_LOGNAT);
1225255332Scy		nsp->ns_log_fail = ipf_log_failures(softc, IPF_LOGNAT);
1226255332Scy#else
1227255332Scy		nsp->ns_log_ok = 0;
1228255332Scy		nsp->ns_log_fail = 0;
1229255332Scy#endif
1230255332Scy		error = ipf_outobj(softc, data, nsp, IPFOBJ_NATSTAT);
123153642Sguido		break;
1232255332Scy	    }
1233170268Sdarrenr
123453642Sguido	case SIOCGNATL :
123553642Sguido	    {
123653642Sguido		natlookup_t nl;
123753642Sguido
1238255332Scy		error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP);
1239145522Sdarrenr		if (error == 0) {
1240173181Sdarrenr			void *ptr;
1241173181Sdarrenr
1242173181Sdarrenr			if (getlock) {
1243255332Scy				READ_ENTER(&softc->ipf_nat);
1244173181Sdarrenr			}
1245255332Scy
1246255332Scy			switch (nl.nl_v)
1247255332Scy			{
1248255332Scy			case 4 :
1249255332Scy				ptr = ipf_nat_lookupredir(&nl);
1250255332Scy				break;
1251255332Scy#ifdef USE_INET6
1252255332Scy			case 6 :
1253255332Scy				ptr = ipf_nat6_lookupredir(&nl);
1254255332Scy				break;
1255255332Scy#endif
1256255332Scy			default:
1257255332Scy				ptr = NULL;
1258255332Scy				break;
1259255332Scy			}
1260255332Scy
1261173181Sdarrenr			if (getlock) {
1262255332Scy				RWLOCK_EXIT(&softc->ipf_nat);
1263173181Sdarrenr			}
1264173181Sdarrenr			if (ptr != NULL) {
1265255332Scy				error = ipf_outobj(softc, data, &nl,
1266255332Scy						   IPFOBJ_NATLOOKUP);
1267145522Sdarrenr			} else {
1268255332Scy				IPFERROR(60011);
1269145522Sdarrenr				error = ESRCH;
1270145522Sdarrenr			}
1271145522Sdarrenr		}
127253642Sguido		break;
127353642Sguido	    }
1274170268Sdarrenr
127560852Sdarrenr	case SIOCIPFFL :	/* old SIOCFLNAT & SIOCCNATL */
127653642Sguido		if (!(mode & FWRITE)) {
1277255332Scy			IPFERROR(60012);
127853642Sguido			error = EPERM;
127953642Sguido			break;
128053642Sguido		}
1281145522Sdarrenr		if (getlock) {
1282255332Scy			WRITE_ENTER(&softc->ipf_nat);
1283145522Sdarrenr		}
1284170268Sdarrenr
1285170268Sdarrenr		error = BCOPYIN(data, &arg, sizeof(arg));
1286255332Scy		if (error != 0) {
1287255332Scy			IPFERROR(60013);
1288170268Sdarrenr			error = EFAULT;
1289255332Scy		} else {
1290170268Sdarrenr			if (arg == 0)
1291255332Scy				ret = ipf_nat_flushtable(softc, softn);
1292170268Sdarrenr			else if (arg == 1)
1293255332Scy				ret = ipf_nat_clearlist(softc, softn);
1294170268Sdarrenr			else
1295255332Scy				ret = ipf_nat_extraflush(softc, softn, arg);
1296255332Scy			ipf_proxy_flush(softc->ipf_proxy_soft, arg);
1297170268Sdarrenr		}
1298170268Sdarrenr
1299145522Sdarrenr		if (getlock) {
1300255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
130160852Sdarrenr		}
1302145522Sdarrenr		if (error == 0) {
1303170268Sdarrenr			error = BCOPYOUT(&ret, data, sizeof(ret));
1304145522Sdarrenr		}
130553642Sguido		break;
1306170268Sdarrenr
1307255332Scy	case SIOCMATCHFLUSH :
1308255332Scy		if (!(mode & FWRITE)) {
1309255332Scy			IPFERROR(60014);
1310255332Scy			error = EPERM;
1311255332Scy			break;
1312255332Scy		}
1313255332Scy		if (getlock) {
1314255332Scy			WRITE_ENTER(&softc->ipf_nat);
1315255332Scy		}
1316255332Scy
1317255332Scy		error = ipf_nat_matchflush(softc, softn, data);
1318255332Scy
1319255332Scy		if (getlock) {
1320255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
1321255332Scy		}
1322255332Scy		break;
1323255332Scy
1324145522Sdarrenr	case SIOCPROXY :
1325255332Scy		error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx);
1326145522Sdarrenr		break;
1327170268Sdarrenr
132860852Sdarrenr	case SIOCSTLCK :
1329153876Sguido		if (!(mode & FWRITE)) {
1330255332Scy			IPFERROR(60015);
1331153876Sguido			error = EPERM;
1332153876Sguido		} else {
1333255332Scy			error = ipf_lock(data, &softn->ipf_nat_lock);
1334153876Sguido		}
133553642Sguido		break;
1336170268Sdarrenr
133760852Sdarrenr	case SIOCSTPUT :
1338153876Sguido		if ((mode & FWRITE) != 0) {
1339255332Scy			error = ipf_nat_putent(softc, data, getlock);
1340145522Sdarrenr		} else {
1341255332Scy			IPFERROR(60016);
134260852Sdarrenr			error = EACCES;
1343145522Sdarrenr		}
134460852Sdarrenr		break;
1345170268Sdarrenr
134660852Sdarrenr	case SIOCSTGSZ :
1347255332Scy		if (softn->ipf_nat_lock) {
1348255332Scy			error = ipf_nat_getsz(softc, data, getlock);
1349255332Scy		} else {
1350255332Scy			IPFERROR(60017);
135160852Sdarrenr			error = EACCES;
1352255332Scy		}
135360852Sdarrenr		break;
1354170268Sdarrenr
135560852Sdarrenr	case SIOCSTGET :
1356255332Scy		if (softn->ipf_nat_lock) {
1357255332Scy			error = ipf_nat_getent(softc, data, getlock);
1358255332Scy		} else {
1359255332Scy			IPFERROR(60018);
136060852Sdarrenr			error = EACCES;
1361255332Scy		}
136260852Sdarrenr		break;
1363170268Sdarrenr
1364170268Sdarrenr	case SIOCGENITER :
1365170268Sdarrenr	    {
1366170268Sdarrenr		ipfgeniter_t iter;
1367170268Sdarrenr		ipftoken_t *token;
1368255332Scy		ipfobj_t obj;
1369170268Sdarrenr
1370255332Scy		error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER);
1371255332Scy		if (error != 0)
1372255332Scy			break;
1373255332Scy
1374170268Sdarrenr		SPL_SCHED(s);
1375255332Scy		token = ipf_token_find(softc, iter.igi_type, uid, ctx);
1376255332Scy		if (token != NULL) {
1377255332Scy			error  = ipf_nat_iterator(softc, token, &iter, &obj);
1378255332Scy			WRITE_ENTER(&softc->ipf_tokens);
1379255332Scy			ipf_token_deref(softc, token);
1380255332Scy			RWLOCK_EXIT(&softc->ipf_tokens);
1381170268Sdarrenr		}
1382170268Sdarrenr		SPL_X(s);
1383170268Sdarrenr		break;
1384170268Sdarrenr	    }
1385170268Sdarrenr
1386170268Sdarrenr	case SIOCIPFDELTOK :
1387255332Scy		error = BCOPYIN(data, &arg, sizeof(arg));
1388170268Sdarrenr		if (error == 0) {
1389170268Sdarrenr			SPL_SCHED(s);
1390255332Scy			error = ipf_token_del(softc, arg, uid, ctx);
1391170268Sdarrenr			SPL_X(s);
1392170268Sdarrenr		} else {
1393255332Scy			IPFERROR(60019);
1394170268Sdarrenr			error = EFAULT;
1395170268Sdarrenr		}
1396170268Sdarrenr		break;
1397170268Sdarrenr
1398170268Sdarrenr	case SIOCGTQTAB :
1399255332Scy		error = ipf_outobj(softc, data, softn->ipf_nat_tcptq,
1400255332Scy				   IPFOBJ_STATETQTAB);
1401170268Sdarrenr		break;
1402170268Sdarrenr
1403172776Sdarrenr	case SIOCGTABL :
1404255332Scy		error = ipf_nat_gettable(softc, softn, data);
1405172776Sdarrenr		break;
1406172776Sdarrenr
140753642Sguido	default :
1408255332Scy		IPFERROR(60020);
140953642Sguido		error = EINVAL;
141053642Sguido		break;
141153642Sguido	}
141260852Sdarrenrdone:
1413255332Scy	if (nat != NULL)
1414255332Scy		ipf_nat_rule_fini(softc, nat);
1415170268Sdarrenr	if (nt != NULL)
1416255332Scy		KFREES(nt, nt->in_size);
141753642Sguido	return error;
141853642Sguido}
141953642Sguido
142053642Sguido
1421145522Sdarrenr/* ------------------------------------------------------------------------ */
1422255332Scy/* Function:    ipf_nat_siocaddnat                                          */
1423145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
1424255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1425255332Scy/*              softn(I) - pointer to NAT context structure                 */
1426255332Scy/*              n(I)       - pointer to new NAT rule                        */
1427145522Sdarrenr/*              np(I)      - pointer to where to insert new NAT rule        */
1428255332Scy/*              getlock(I) - flag indicating if lock on  is held            */
1429255332Scy/* Mutex Locks: ipf_nat_io                                                   */
1430145522Sdarrenr/*                                                                          */
1431145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1432145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1433145522Sdarrenr/* NAT rule table(s).                                                       */
1434145522Sdarrenr/* ------------------------------------------------------------------------ */
1435255332Scystatic int
1436255332Scyipf_nat_siocaddnat(softc, softn, n, getlock)
1437255332Scy	ipf_main_softc_t *softc;
1438255332Scy	ipf_nat_softc_t *softn;
1439255332Scy	ipnat_t *n;
1440255332Scy	int getlock;
1441145522Sdarrenr{
1442255332Scy	int error = 0;
1443145522Sdarrenr
1444255332Scy	if (ipf_nat_resolverule(softc, n) != 0) {
1445255332Scy		IPFERROR(60022);
1446161356Sguido		return ENOENT;
1447255332Scy	}
1448145522Sdarrenr
1449255332Scy	if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) {
1450255332Scy		IPFERROR(60023);
1451145522Sdarrenr		return EINVAL;
1452255332Scy	}
1453145522Sdarrenr
1454255332Scy	if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) {
1455145522Sdarrenr		/*
1456255332Scy		 * Prerecord whether or not the destination of the divert
1457255332Scy		 * is local or not to the interface the packet is going
1458255332Scy		 * to be sent out.
1459145522Sdarrenr		 */
1460255332Scy		n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1],
1461255332Scy						n->in_ifps[1], &n->in_ndstip6);
1462145522Sdarrenr	}
1463145522Sdarrenr
1464145522Sdarrenr	if (getlock) {
1465255332Scy		WRITE_ENTER(&softc->ipf_nat);
1466145522Sdarrenr	}
1467145522Sdarrenr	n->in_next = NULL;
1468255332Scy	n->in_pnext = softn->ipf_nat_list_tail;
1469255332Scy	*n->in_pnext = n;
1470255332Scy	softn->ipf_nat_list_tail = &n->in_next;
1471255332Scy	n->in_use++;
1472145522Sdarrenr
1473145522Sdarrenr	if (n->in_redir & NAT_REDIRECT) {
1474145522Sdarrenr		n->in_flags &= ~IPN_NOTDST;
1475255332Scy		switch (n->in_v[0])
1476255332Scy		{
1477255332Scy		case 4 :
1478255332Scy			ipf_nat_addrdr(softn, n);
1479255332Scy			break;
1480255332Scy#ifdef USE_INET6
1481255332Scy		case 6 :
1482255332Scy			ipf_nat6_addrdr(softn, n);
1483255332Scy			break;
1484255332Scy#endif
1485255332Scy		default :
1486255332Scy			break;
1487255332Scy		}
1488255332Scy		ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_rdr);
1489145522Sdarrenr	}
1490255332Scy
1491145522Sdarrenr	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
1492145522Sdarrenr		n->in_flags &= ~IPN_NOTSRC;
1493255332Scy		switch (n->in_v[0])
1494255332Scy		{
1495255332Scy		case 4 :
1496255332Scy			ipf_nat_addmap(softn, n);
1497255332Scy			break;
1498255332Scy#ifdef USE_INET6
1499255332Scy		case 6 :
1500255332Scy			ipf_nat6_addmap(softn, n);
1501255332Scy			break;
1502255332Scy#endif
1503255332Scy		default :
1504255332Scy			break;
1505255332Scy		}
1506255332Scy		ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_map);
1507145522Sdarrenr	}
1508255332Scy
1509255332Scy	if (n->in_age[0] != 0)
1510255332Scy		n->in_tqehead[0] = ipf_addtimeoutqueue(softc,
1511255332Scy						       &softn->ipf_nat_utqe,
1512255332Scy						       n->in_age[0]);
1513255332Scy
1514255332Scy	if (n->in_age[1] != 0)
1515255332Scy		n->in_tqehead[1] = ipf_addtimeoutqueue(softc,
1516255332Scy						       &softn->ipf_nat_utqe,
1517255332Scy						       n->in_age[1]);
1518255332Scy
1519170268Sdarrenr	MUTEX_INIT(&n->in_lock, "ipnat rule lock");
1520170268Sdarrenr
1521145522Sdarrenr	n = NULL;
1522255332Scy	ATOMIC_INC32(softn->ipf_nat_stats.ns_rules);
1523255332Scy#if SOLARIS && !defined(INSTANCES)
1524145522Sdarrenr	pfil_delayed_copy = 0;
1525145522Sdarrenr#endif
1526145522Sdarrenr	if (getlock) {
1527255332Scy		RWLOCK_EXIT(&softc->ipf_nat);			/* WRITE */
1528145522Sdarrenr	}
1529145522Sdarrenr
1530145522Sdarrenr	return error;
1531145522Sdarrenr}
1532145522Sdarrenr
1533145522Sdarrenr
1534145522Sdarrenr/* ------------------------------------------------------------------------ */
1535255332Scy/* Function:    ipf_nat_ruleaddrinit                                        */
1536255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1537255332Scy/*              softn(I) - pointer to NAT context structure                 */
1538255332Scy/*              n(I)     - pointer to NAT rule                              */
1539255332Scy/*                                                                          */
1540255332Scy/* Initialise all of the NAT address structures in a NAT rule.              */
1541255332Scy/* ------------------------------------------------------------------------ */
1542255332Scystatic int
1543255332Scyipf_nat_ruleaddrinit(softc, softn, n)
1544255332Scy	ipf_main_softc_t *softc;
1545255332Scy	ipf_nat_softc_t *softn;
1546255332Scy	ipnat_t *n;
1547255332Scy{
1548255332Scy	int idx, error;
1549255332Scy
1550255332Scy	if ((n->in_ndst.na_atype == FRI_LOOKUP) &&
1551255332Scy	    (n->in_ndst.na_type != IPLT_DSTLIST)) {
1552255332Scy		IPFERROR(60071);
1553255332Scy		return EINVAL;
1554255332Scy	}
1555255332Scy	if ((n->in_nsrc.na_atype == FRI_LOOKUP) &&
1556255332Scy	    (n->in_nsrc.na_type != IPLT_DSTLIST)) {
1557255332Scy		IPFERROR(60069);
1558255332Scy		return EINVAL;
1559255332Scy	}
1560255332Scy
1561255332Scy	if (n->in_redir == NAT_BIMAP) {
1562255332Scy		n->in_ndstaddr = n->in_osrcaddr;
1563255332Scy		n->in_ndstmsk = n->in_osrcmsk;
1564255332Scy		n->in_odstaddr = n->in_nsrcaddr;
1565255332Scy		n->in_odstmsk = n->in_nsrcmsk;
1566255332Scy
1567255332Scy	}
1568255332Scy
1569255332Scy	if (n->in_redir & NAT_REDIRECT)
1570255332Scy		idx = 1;
1571255332Scy	else
1572255332Scy		idx = 0;
1573255332Scy	/*
1574255332Scy	 * Initialise all of the address fields.
1575255332Scy	 */
1576255332Scy	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 1,
1577255332Scy				     n->in_ifps[idx]);
1578255332Scy	if (error != 0)
1579255332Scy		return error;
1580255332Scy
1581255332Scy	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 1,
1582255332Scy				     n->in_ifps[idx]);
1583255332Scy	if (error != 0)
1584255332Scy		return error;
1585255332Scy
1586255332Scy	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1,
1587255332Scy				     n->in_ifps[idx]);
1588255332Scy	if (error != 0)
1589255332Scy		return error;
1590255332Scy
1591255332Scy	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 1,
1592255332Scy				     n->in_ifps[idx]);
1593255332Scy	if (error != 0)
1594255332Scy		return error;
1595255332Scy
1596255332Scy	if (n->in_redir & NAT_DIVERTUDP)
1597255332Scy		ipf_nat_builddivertmp(softn, n);
1598255332Scy
1599255332Scy	return 0;
1600255332Scy}
1601255332Scy
1602255332Scy
1603255332Scy/* ------------------------------------------------------------------------ */
1604255332Scy/* Function:    ipf_nat_resolvrule                                          */
1605145522Sdarrenr/* Returns:     Nil                                                         */
1606255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1607255332Scy/*              n(I)     - pointer to NAT rule                              */
1608145522Sdarrenr/*                                                                          */
1609145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1610145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1611145522Sdarrenr/* NAT rule table(s).                                                       */
1612145522Sdarrenr/* ------------------------------------------------------------------------ */
1613255332Scystatic int
1614255332Scyipf_nat_resolverule(softc, n)
1615255332Scy	ipf_main_softc_t *softc;
1616255332Scy	ipnat_t *n;
1617145522Sdarrenr{
1618255332Scy	char *base;
1619145522Sdarrenr
1620255332Scy	base = n->in_names;
1621255332Scy
1622255332Scy	n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0],
1623255332Scy				       n->in_v[0]);
1624255332Scy
1625255332Scy	if (n->in_ifnames[1] == -1) {
1626255332Scy		n->in_ifnames[1] = n->in_ifnames[0];
1627145522Sdarrenr		n->in_ifps[1] = n->in_ifps[0];
1628145522Sdarrenr	} else {
1629255332Scy		n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1],
1630255332Scy					       n->in_v[1]);
1631145522Sdarrenr	}
1632145522Sdarrenr
1633255332Scy	if (n->in_plabel != -1) {
1634255332Scy		if (n->in_redir & NAT_REDIRECT)
1635255332Scy			n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft,
1636255332Scy						     n->in_pr[0],
1637255332Scy						     base + n->in_plabel);
1638255332Scy		else
1639255332Scy			n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft,
1640255332Scy						     n->in_pr[1],
1641255332Scy						     base + n->in_plabel);
1642161356Sguido		if (n->in_apr == NULL)
1643161356Sguido			return -1;
1644145522Sdarrenr	}
1645161356Sguido	return 0;
1646145522Sdarrenr}
1647145522Sdarrenr
1648145522Sdarrenr
1649145522Sdarrenr/* ------------------------------------------------------------------------ */
1650255332Scy/* Function:    ipf_nat_siocdelnat                                          */
1651145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
1652255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
1653255332Scy/*              softn(I)   - pointer to NAT context structure               */
1654255332Scy/*              n(I)       - pointer to new NAT rule                        */
1655255332Scy/*              getlock(I) - flag indicating if lock on  is held            */
1656255332Scy/* Mutex Locks: ipf_nat_io                                                  */
1657145522Sdarrenr/*                                                                          */
1658145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1659145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1660145522Sdarrenr/* NAT rule table(s).                                                       */
1661145522Sdarrenr/* ------------------------------------------------------------------------ */
1662255332Scystatic void
1663255332Scyipf_nat_siocdelnat(softc, softn, n, getlock)
1664255332Scy	ipf_main_softc_t *softc;
1665255332Scy	ipf_nat_softc_t *softn;
1666255332Scy	ipnat_t *n;
1667255332Scy	int getlock;
1668145522Sdarrenr{
1669145522Sdarrenr	if (getlock) {
1670255332Scy		WRITE_ENTER(&softc->ipf_nat);
1671145522Sdarrenr	}
1672145522Sdarrenr
1673255332Scy	ipf_nat_delrule(softc, softn, n, 1);
1674145522Sdarrenr
1675145522Sdarrenr	if (getlock) {
1676255332Scy		RWLOCK_EXIT(&softc->ipf_nat);			/* READ/WRITE */
1677145522Sdarrenr	}
1678145522Sdarrenr}
1679145522Sdarrenr
1680145522Sdarrenr
1681145522Sdarrenr/* ------------------------------------------------------------------------ */
1682255332Scy/* Function:    ipf_nat_getsz                                               */
1683145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1684255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
1685255332Scy/*              data(I)    - pointer to natget structure with kernel        */
1686255332Scy/*                           pointer get the size of.                       */
1687255332Scy/*              getlock(I) - flag indicating whether or not the caller      */
1688255332Scy/*                           holds a lock on ipf_nat                        */
1689145522Sdarrenr/*                                                                          */
1690145522Sdarrenr/* Handle SIOCSTGSZ.                                                        */
1691145522Sdarrenr/* Return the size of the nat list entry to be copied back to user space.   */
1692145522Sdarrenr/* The size of the entry is stored in the ng_sz field and the enture natget */
1693145522Sdarrenr/* structure is copied back to the user.                                    */
1694145522Sdarrenr/* ------------------------------------------------------------------------ */
1695255332Scystatic int
1696255332Scyipf_nat_getsz(softc, data, getlock)
1697255332Scy	ipf_main_softc_t *softc;
1698255332Scy	caddr_t data;
1699255332Scy	int getlock;
170060852Sdarrenr{
1701255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
170260852Sdarrenr	ap_session_t *aps;
170360852Sdarrenr	nat_t *nat, *n;
170460852Sdarrenr	natget_t ng;
1705255332Scy	int error;
170660852Sdarrenr
1707255332Scy	error = BCOPYIN(data, &ng, sizeof(ng));
1708255332Scy	if (error != 0) {
1709255332Scy		IPFERROR(60024);
1710170268Sdarrenr		return EFAULT;
1711255332Scy	}
171260852Sdarrenr
1713173181Sdarrenr	if (getlock) {
1714255332Scy		READ_ENTER(&softc->ipf_nat);
1715173181Sdarrenr	}
1716173181Sdarrenr
171760852Sdarrenr	nat = ng.ng_ptr;
171860852Sdarrenr	if (!nat) {
1719255332Scy		nat = softn->ipf_nat_instances;
172060852Sdarrenr		ng.ng_sz = 0;
1721145522Sdarrenr		/*
1722145522Sdarrenr		 * Empty list so the size returned is 0.  Simple.
1723145522Sdarrenr		 */
172460852Sdarrenr		if (nat == NULL) {
1725173181Sdarrenr			if (getlock) {
1726255332Scy				RWLOCK_EXIT(&softc->ipf_nat);
1727173181Sdarrenr			}
1728255332Scy			error = BCOPYOUT(&ng, data, sizeof(ng));
1729255332Scy			if (error != 0) {
1730255332Scy				IPFERROR(60025);
1731170268Sdarrenr				return EFAULT;
1732255332Scy			}
1733145522Sdarrenr			return 0;
173460852Sdarrenr		}
173560852Sdarrenr	} else {
173660852Sdarrenr		/*
173760852Sdarrenr		 * Make sure the pointer we're copying from exists in the
173860852Sdarrenr		 * current list of entries.  Security precaution to prevent
173960852Sdarrenr		 * copying of random kernel data.
174060852Sdarrenr		 */
1741255332Scy		for (n = softn->ipf_nat_instances; n; n = n->nat_next)
174260852Sdarrenr			if (n == nat)
174360852Sdarrenr				break;
1744173181Sdarrenr		if (n == NULL) {
1745173181Sdarrenr			if (getlock) {
1746255332Scy				RWLOCK_EXIT(&softc->ipf_nat);
1747173181Sdarrenr			}
1748255332Scy			IPFERROR(60026);
174960852Sdarrenr			return ESRCH;
1750173181Sdarrenr		}
175160852Sdarrenr	}
175260852Sdarrenr
1753145522Sdarrenr	/*
1754145522Sdarrenr	 * Incluse any space required for proxy data structures.
1755145522Sdarrenr	 */
175660852Sdarrenr	ng.ng_sz = sizeof(nat_save_t);
175760852Sdarrenr	aps = nat->nat_aps;
1758145522Sdarrenr	if (aps != NULL) {
1759145522Sdarrenr		ng.ng_sz += sizeof(ap_session_t) - 4;
1760145522Sdarrenr		if (aps->aps_data != 0)
1761145522Sdarrenr			ng.ng_sz += aps->aps_psiz;
176260852Sdarrenr	}
1763173181Sdarrenr	if (getlock) {
1764255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
1765173181Sdarrenr	}
176660852Sdarrenr
1767255332Scy	error = BCOPYOUT(&ng, data, sizeof(ng));
1768255332Scy	if (error != 0) {
1769255332Scy		IPFERROR(60027);
1770170268Sdarrenr		return EFAULT;
1771255332Scy	}
1772145522Sdarrenr	return 0;
177360852Sdarrenr}
177460852Sdarrenr
177560852Sdarrenr
1776145522Sdarrenr/* ------------------------------------------------------------------------ */
1777255332Scy/* Function:    ipf_nat_getent                                              */
1778145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1779255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
1780255332Scy/*              data(I)    - pointer to natget structure with kernel pointer*/
1781255332Scy/*                           to NAT structure to copy out.                  */
1782255332Scy/*              getlock(I) - flag indicating whether or not the caller      */
1783255332Scy/*                           holds a lock on ipf_nat                        */
1784145522Sdarrenr/*                                                                          */
1785145522Sdarrenr/* Handle SIOCSTGET.                                                        */
1786145522Sdarrenr/* Copies out NAT entry to user space.  Any additional data held for a      */
1787145522Sdarrenr/* proxy is also copied, as to is the NAT rule which was responsible for it */
1788145522Sdarrenr/* ------------------------------------------------------------------------ */
1789255332Scystatic int
1790255332Scyipf_nat_getent(softc, data, getlock)
1791255332Scy	ipf_main_softc_t *softc;
1792255332Scy	caddr_t data;
1793255332Scy	int getlock;
179460852Sdarrenr{
1795255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1796145522Sdarrenr	int error, outsize;
179760852Sdarrenr	ap_session_t *aps;
1798145522Sdarrenr	nat_save_t *ipn, ipns;
1799145522Sdarrenr	nat_t *n, *nat;
180060852Sdarrenr
1801255332Scy	error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE);
1802145522Sdarrenr	if (error != 0)
1803145522Sdarrenr		return error;
180460852Sdarrenr
1805255332Scy	if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) {
1806255332Scy		IPFERROR(60028);
1807145522Sdarrenr		return EINVAL;
1808255332Scy	}
1809145522Sdarrenr
1810145522Sdarrenr	KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize);
1811255332Scy	if (ipn == NULL) {
1812255332Scy		IPFERROR(60029);
1813145522Sdarrenr		return ENOMEM;
1814255332Scy	}
1815145522Sdarrenr
1816173181Sdarrenr	if (getlock) {
1817255332Scy		READ_ENTER(&softc->ipf_nat);
1818173181Sdarrenr	}
1819173181Sdarrenr
1820145522Sdarrenr	ipn->ipn_dsize = ipns.ipn_dsize;
1821145522Sdarrenr	nat = ipns.ipn_next;
1822145522Sdarrenr	if (nat == NULL) {
1823255332Scy		nat = softn->ipf_nat_instances;
182460852Sdarrenr		if (nat == NULL) {
1825255332Scy			if (softn->ipf_nat_instances == NULL) {
1826255332Scy				IPFERROR(60030);
1827145522Sdarrenr				error = ENOENT;
1828255332Scy			}
1829145522Sdarrenr			goto finished;
183060852Sdarrenr		}
183160852Sdarrenr	} else {
183260852Sdarrenr		/*
183360852Sdarrenr		 * Make sure the pointer we're copying from exists in the
183460852Sdarrenr		 * current list of entries.  Security precaution to prevent
183560852Sdarrenr		 * copying of random kernel data.
183660852Sdarrenr		 */
1837255332Scy		for (n = softn->ipf_nat_instances; n; n = n->nat_next)
183860852Sdarrenr			if (n == nat)
183960852Sdarrenr				break;
1840145522Sdarrenr		if (n == NULL) {
1841255332Scy			IPFERROR(60031);
1842145522Sdarrenr			error = ESRCH;
1843145522Sdarrenr			goto finished;
1844145522Sdarrenr		}
184560852Sdarrenr	}
1846145522Sdarrenr	ipn->ipn_next = nat->nat_next;
184760852Sdarrenr
1848145522Sdarrenr	/*
1849145522Sdarrenr	 * Copy the NAT structure.
1850145522Sdarrenr	 */
1851145522Sdarrenr	bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat));
185260852Sdarrenr
1853145522Sdarrenr	/*
1854145522Sdarrenr	 * If we have a pointer to the NAT rule it belongs to, save that too.
1855145522Sdarrenr	 */
1856145522Sdarrenr	if (nat->nat_ptr != NULL)
1857145522Sdarrenr		bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat,
1858255332Scy		      ipn->ipn_ipnat.in_size);
185960852Sdarrenr
1860145522Sdarrenr	/*
1861145522Sdarrenr	 * If we also know the NAT entry has an associated filter rule,
1862145522Sdarrenr	 * save that too.
1863145522Sdarrenr	 */
1864145522Sdarrenr	if (nat->nat_fr != NULL)
1865145522Sdarrenr		bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr,
1866145522Sdarrenr		      sizeof(ipn->ipn_fr));
186760852Sdarrenr
1868145522Sdarrenr	/*
1869145522Sdarrenr	 * Last but not least, if there is an application proxy session set
1870145522Sdarrenr	 * up for this NAT entry, then copy that out too, including any
1871145522Sdarrenr	 * private data saved along side it by the proxy.
1872145522Sdarrenr	 */
1873145522Sdarrenr	aps = nat->nat_aps;
1874145522Sdarrenr	outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data);
1875145522Sdarrenr	if (aps != NULL) {
1876145522Sdarrenr		char *s;
187760852Sdarrenr
1878145522Sdarrenr		if (outsize < sizeof(*aps)) {
1879255332Scy			IPFERROR(60032);
1880145522Sdarrenr			error = ENOBUFS;
1881145522Sdarrenr			goto finished;
188260852Sdarrenr		}
1883145522Sdarrenr
1884145522Sdarrenr		s = ipn->ipn_data;
1885145522Sdarrenr		bcopy((char *)aps, s, sizeof(*aps));
1886145522Sdarrenr		s += sizeof(*aps);
1887145522Sdarrenr		outsize -= sizeof(*aps);
1888145522Sdarrenr		if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz))
1889145522Sdarrenr			bcopy(aps->aps_data, s, aps->aps_psiz);
1890255332Scy		else {
1891255332Scy			IPFERROR(60033);
1892145522Sdarrenr			error = ENOBUFS;
1893255332Scy		}
189460852Sdarrenr	}
1895145522Sdarrenr	if (error == 0) {
1896255332Scy		error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE,
1897255332Scy				     ipns.ipn_dsize);
1898145522Sdarrenr	}
1899145522Sdarrenr
1900145522Sdarrenrfinished:
1901145522Sdarrenr	if (ipn != NULL) {
1902145522Sdarrenr		KFREES(ipn, ipns.ipn_dsize);
1903145522Sdarrenr	}
1904344113Scy	if (getlock) {
1905344113Scy		RWLOCK_EXIT(&softc->ipf_nat);
1906344113Scy	}
190764580Sdarrenr	return error;
190860852Sdarrenr}
190960852Sdarrenr
191060852Sdarrenr
1911145522Sdarrenr/* ------------------------------------------------------------------------ */
1912255332Scy/* Function:    ipf_nat_putent                                              */
1913145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1914255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
1915255332Scy/*              data(I)    - pointer to natget structure with NAT           */
1916255332Scy/*                           structure information to load into the kernel  */
1917145522Sdarrenr/*              getlock(I) - flag indicating whether or not a write lock    */
1918255332Scy/*                           on is already held.                            */
1919145522Sdarrenr/*                                                                          */
1920145522Sdarrenr/* Handle SIOCSTPUT.                                                        */
1921145522Sdarrenr/* Loads a NAT table entry from user space, including a NAT rule, proxy and */
1922145522Sdarrenr/* firewall rule data structures, if pointers to them indicate so.          */
1923145522Sdarrenr/* ------------------------------------------------------------------------ */
1924255332Scystatic int
1925255332Scyipf_nat_putent(softc, data, getlock)
1926255332Scy	ipf_main_softc_t *softc;
1927255332Scy	caddr_t data;
1928255332Scy	int getlock;
192960852Sdarrenr{
1930255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1931145522Sdarrenr	nat_save_t ipn, *ipnn;
193260852Sdarrenr	ap_session_t *aps;
1933145522Sdarrenr	nat_t *n, *nat;
193460852Sdarrenr	frentry_t *fr;
1935145522Sdarrenr	fr_info_t fin;
193660852Sdarrenr	ipnat_t *in;
193760852Sdarrenr	int error;
193860852Sdarrenr
1939255332Scy	error = ipf_inobj(softc, data, NULL, &ipn, IPFOBJ_NATSAVE);
1940145522Sdarrenr	if (error != 0)
1941145522Sdarrenr		return error;
1942145522Sdarrenr
1943145522Sdarrenr	/*
1944145522Sdarrenr	 * Initialise early because of code at junkput label.
1945145522Sdarrenr	 */
1946255332Scy	n = NULL;
1947145522Sdarrenr	in = NULL;
1948145522Sdarrenr	aps = NULL;
194964580Sdarrenr	nat = NULL;
1950145522Sdarrenr	ipnn = NULL;
1951170268Sdarrenr	fr = NULL;
1952145522Sdarrenr
1953145522Sdarrenr	/*
1954145522Sdarrenr	 * New entry, copy in the rest of the NAT entry if it's size is more
1955145522Sdarrenr	 * than just the nat_t structure.
1956145522Sdarrenr	 */
1957145522Sdarrenr	if (ipn.ipn_dsize > sizeof(ipn)) {
1958145522Sdarrenr		if (ipn.ipn_dsize > 81920) {
1959255332Scy			IPFERROR(60034);
1960145522Sdarrenr			error = ENOMEM;
1961145522Sdarrenr			goto junkput;
1962145522Sdarrenr		}
1963145522Sdarrenr
1964145522Sdarrenr		KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize);
1965255332Scy		if (ipnn == NULL) {
1966255332Scy			IPFERROR(60035);
196760852Sdarrenr			return ENOMEM;
1968255332Scy		}
1969145522Sdarrenr
1970255332Scy		bzero(ipnn, ipn.ipn_dsize);
1971255332Scy		error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE,
1972255332Scy				    ipn.ipn_dsize);
1973145522Sdarrenr		if (error != 0) {
197464580Sdarrenr			goto junkput;
197564580Sdarrenr		}
197660852Sdarrenr	} else
1977145522Sdarrenr		ipnn = &ipn;
197860852Sdarrenr
197960852Sdarrenr	KMALLOC(nat, nat_t *);
198064580Sdarrenr	if (nat == NULL) {
1981255332Scy		IPFERROR(60037);
1982145522Sdarrenr		error = ENOMEM;
198364580Sdarrenr		goto junkput;
198464580Sdarrenr	}
198560852Sdarrenr
1986145522Sdarrenr	bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat));
1987255332Scy
1988255332Scy	switch (nat->nat_v[0])
1989255332Scy	{
1990255332Scy	case 4:
1991255332Scy#ifdef USE_INET6
1992255332Scy	case 6 :
1993255332Scy#endif
1994255332Scy		break;
1995255332Scy	default :
1996255332Scy		IPFERROR(60061);
1997255332Scy		error = EPROTONOSUPPORT;
1998255332Scy		goto junkput;
1999255332Scy		/*NOTREACHED*/
2000255332Scy	}
2001255332Scy
200260852Sdarrenr	/*
2003255332Scy	 * Initialize all these so that ipf_nat_delete() doesn't cause a crash.
200460852Sdarrenr	 */
2005145522Sdarrenr	bzero((char *)nat, offsetof(struct nat, nat_tqe));
2006145522Sdarrenr	nat->nat_tqe.tqe_pnext = NULL;
2007145522Sdarrenr	nat->nat_tqe.tqe_next = NULL;
2008145522Sdarrenr	nat->nat_tqe.tqe_ifq = NULL;
2009145522Sdarrenr	nat->nat_tqe.tqe_parent = nat;
201060852Sdarrenr
201160852Sdarrenr	/*
201260852Sdarrenr	 * Restore the rule associated with this nat session
201360852Sdarrenr	 */
2014145522Sdarrenr	in = ipnn->ipn_nat.nat_ptr;
2015145522Sdarrenr	if (in != NULL) {
2016255332Scy		KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size);
2017145522Sdarrenr		nat->nat_ptr = in;
201860852Sdarrenr		if (in == NULL) {
2019255332Scy			IPFERROR(60038);
202060852Sdarrenr			error = ENOMEM;
202160852Sdarrenr			goto junkput;
202260852Sdarrenr		}
2023255332Scy		bcopy((char *)&ipnn->ipn_ipnat, (char *)in,
2024255332Scy		      ipnn->ipn_ipnat.in_size);
202560852Sdarrenr		in->in_use = 1;
202660852Sdarrenr		in->in_flags |= IPN_DELETE;
2027145522Sdarrenr
2028255332Scy		ATOMIC_INC32(softn->ipf_nat_stats.ns_rules);
2029145522Sdarrenr
2030255332Scy		if (ipf_nat_resolverule(softc, in) != 0) {
2031255332Scy			IPFERROR(60039);
2032161356Sguido			error = ESRCH;
2033161356Sguido			goto junkput;
2034161356Sguido		}
2035145522Sdarrenr	}
2036145522Sdarrenr
2037145522Sdarrenr	/*
2038145522Sdarrenr	 * Check that the NAT entry doesn't already exist in the kernel.
2039161356Sguido	 *
2040161356Sguido	 * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry.  To do
2041161356Sguido	 * this, we check to see if the inbound combination of addresses and
2042161356Sguido	 * ports is already known.  Similar logic is applied for NAT_INBOUND.
2043255332Scy	 *
2044145522Sdarrenr	 */
2045145522Sdarrenr	bzero((char *)&fin, sizeof(fin));
2046255332Scy	fin.fin_v = nat->nat_v[0];
2047255332Scy	fin.fin_p = nat->nat_pr[0];
2048255332Scy	fin.fin_rev = nat->nat_rev;
2049255332Scy	fin.fin_ifp = nat->nat_ifps[0];
2050255332Scy	fin.fin_data[0] = ntohs(nat->nat_ndport);
2051255332Scy	fin.fin_data[1] = ntohs(nat->nat_nsport);
2052255332Scy
2053255332Scy	switch (nat->nat_dir)
2054255332Scy	{
2055255332Scy	case NAT_OUTBOUND :
2056255332Scy	case NAT_DIVERTOUT :
2057153876Sguido		if (getlock) {
2058255332Scy			READ_ENTER(&softc->ipf_nat);
2059153876Sguido		}
2060255332Scy
2061255332Scy		fin.fin_v = nat->nat_v[1];
2062255332Scy		if (nat->nat_v[1] == 4) {
2063255332Scy			n = ipf_nat_inlookup(&fin, nat->nat_flags, fin.fin_p,
2064255332Scy					     nat->nat_ndstip, nat->nat_nsrcip);
2065255332Scy#ifdef USE_INET6
2066255332Scy		} else if (nat->nat_v[1] == 6) {
2067255332Scy			n = ipf_nat6_inlookup(&fin, nat->nat_flags, fin.fin_p,
2068255332Scy					      &nat->nat_ndst6.in6,
2069255332Scy					      &nat->nat_nsrc6.in6);
2070255332Scy#endif
2071255332Scy		}
2072255332Scy
2073153876Sguido		if (getlock) {
2074255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
2075153876Sguido		}
2076153876Sguido		if (n != NULL) {
2077255332Scy			IPFERROR(60040);
2078145522Sdarrenr			error = EEXIST;
2079145522Sdarrenr			goto junkput;
208060852Sdarrenr		}
2081255332Scy		break;
2082255332Scy
2083255332Scy	case NAT_INBOUND :
2084255332Scy	case NAT_DIVERTIN :
2085153876Sguido		if (getlock) {
2086255332Scy			READ_ENTER(&softc->ipf_nat);
2087153876Sguido		}
2088255332Scy
2089255332Scy		if (fin.fin_v == 4) {
2090255332Scy			n = ipf_nat_outlookup(&fin, nat->nat_flags, fin.fin_p,
2091255332Scy					      nat->nat_ndstip,
2092255332Scy					      nat->nat_nsrcip);
2093255332Scy#ifdef USE_INET6
2094255332Scy		} else if (fin.fin_v == 6) {
2095255332Scy			n = ipf_nat6_outlookup(&fin, nat->nat_flags, fin.fin_p,
2096255332Scy					       &nat->nat_ndst6.in6,
2097255332Scy					       &nat->nat_nsrc6.in6);
2098255332Scy#endif
2099255332Scy		}
2100255332Scy
2101153876Sguido		if (getlock) {
2102255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
2103153876Sguido		}
2104153876Sguido		if (n != NULL) {
2105255332Scy			IPFERROR(60041);
2106145522Sdarrenr			error = EEXIST;
2107145522Sdarrenr			goto junkput;
2108145522Sdarrenr		}
2109255332Scy		break;
2110255332Scy
2111255332Scy	default :
2112255332Scy		IPFERROR(60042);
2113145522Sdarrenr		error = EINVAL;
2114145522Sdarrenr		goto junkput;
211560852Sdarrenr	}
211660852Sdarrenr
211760852Sdarrenr	/*
211860852Sdarrenr	 * Restore ap_session_t structure.  Include the private data allocated
211960852Sdarrenr	 * if it was there.
212060852Sdarrenr	 */
2121145522Sdarrenr	aps = nat->nat_aps;
2122145522Sdarrenr	if (aps != NULL) {
212360852Sdarrenr		KMALLOC(aps, ap_session_t *);
2124145522Sdarrenr		nat->nat_aps = aps;
212560852Sdarrenr		if (aps == NULL) {
2126255332Scy			IPFERROR(60043);
212760852Sdarrenr			error = ENOMEM;
212860852Sdarrenr			goto junkput;
212960852Sdarrenr		}
213060852Sdarrenr		bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps));
2131145522Sdarrenr		if (in != NULL)
213260852Sdarrenr			aps->aps_apr = in->in_apr;
2133145522Sdarrenr		else
2134145522Sdarrenr			aps->aps_apr = NULL;
2135145522Sdarrenr		if (aps->aps_psiz != 0) {
2136145522Sdarrenr			if (aps->aps_psiz > 81920) {
2137255332Scy				IPFERROR(60044);
2138145522Sdarrenr				error = ENOMEM;
2139145522Sdarrenr				goto junkput;
2140145522Sdarrenr			}
214160852Sdarrenr			KMALLOCS(aps->aps_data, void *, aps->aps_psiz);
214260852Sdarrenr			if (aps->aps_data == NULL) {
2143255332Scy				IPFERROR(60045);
214460852Sdarrenr				error = ENOMEM;
214560852Sdarrenr				goto junkput;
214660852Sdarrenr			}
214760852Sdarrenr			bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data,
214860852Sdarrenr			      aps->aps_psiz);
214960852Sdarrenr		} else {
215060852Sdarrenr			aps->aps_psiz = 0;
215160852Sdarrenr			aps->aps_data = NULL;
215260852Sdarrenr		}
215360852Sdarrenr	}
215460852Sdarrenr
215560852Sdarrenr	/*
215660852Sdarrenr	 * If there was a filtering rule associated with this entry then
215760852Sdarrenr	 * build up a new one.
215860852Sdarrenr	 */
2159145522Sdarrenr	fr = nat->nat_fr;
216060852Sdarrenr	if (fr != NULL) {
2161145522Sdarrenr		if ((nat->nat_flags & SI_NEWFR) != 0) {
216260852Sdarrenr			KMALLOC(fr, frentry_t *);
216360852Sdarrenr			nat->nat_fr = fr;
216460852Sdarrenr			if (fr == NULL) {
2165255332Scy				IPFERROR(60046);
216660852Sdarrenr				error = ENOMEM;
216760852Sdarrenr				goto junkput;
216860852Sdarrenr			}
2169145522Sdarrenr			ipnn->ipn_nat.nat_fr = fr;
2170145522Sdarrenr			fr->fr_ref = 1;
2171255332Scy			(void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE);
2172145522Sdarrenr			bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr));
2173161356Sguido
2174161356Sguido			fr->fr_ref = 1;
2175161356Sguido			fr->fr_dsize = 0;
2176161356Sguido			fr->fr_data = NULL;
2177161356Sguido			fr->fr_type = FR_T_NONE;
2178161356Sguido
2179145522Sdarrenr			MUTEX_NUKE(&fr->fr_lock);
2180145522Sdarrenr			MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock");
218160852Sdarrenr		} else {
2182153876Sguido			if (getlock) {
2183255332Scy				READ_ENTER(&softc->ipf_nat);
2184153876Sguido			}
2185255332Scy			for (n = softn->ipf_nat_instances; n; n = n->nat_next)
218660852Sdarrenr				if (n->nat_fr == fr)
218760852Sdarrenr					break;
2188145522Sdarrenr
2189145522Sdarrenr			if (n != NULL) {
2190145522Sdarrenr				MUTEX_ENTER(&fr->fr_lock);
2191145522Sdarrenr				fr->fr_ref++;
2192145522Sdarrenr				MUTEX_EXIT(&fr->fr_lock);
2193145522Sdarrenr			}
2194153876Sguido			if (getlock) {
2195255332Scy				RWLOCK_EXIT(&softc->ipf_nat);
2196153876Sguido			}
2197145522Sdarrenr
2198255332Scy			if (n == NULL) {
2199255332Scy				IPFERROR(60047);
220060852Sdarrenr				error = ESRCH;
220160852Sdarrenr				goto junkput;
220260852Sdarrenr			}
220360852Sdarrenr		}
220460852Sdarrenr	}
220560852Sdarrenr
2206145522Sdarrenr	if (ipnn != &ipn) {
2207145522Sdarrenr		KFREES(ipnn, ipn.ipn_dsize);
2208145522Sdarrenr		ipnn = NULL;
2209145522Sdarrenr	}
2210145522Sdarrenr
2211145522Sdarrenr	if (getlock) {
2212255332Scy		WRITE_ENTER(&softc->ipf_nat);
2213145522Sdarrenr	}
2214255332Scy
2215255332Scy	if (fin.fin_v == 4)
2216255332Scy		error = ipf_nat_finalise(&fin, nat);
2217255332Scy#ifdef USE_INET6
2218255332Scy	else
2219255332Scy		error = ipf_nat6_finalise(&fin, nat);
2220255332Scy#endif
2221255332Scy
2222145522Sdarrenr	if (getlock) {
2223255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
2224145522Sdarrenr	}
2225145522Sdarrenr
2226145522Sdarrenr	if (error == 0)
2227145522Sdarrenr		return 0;
2228145522Sdarrenr
2229255332Scy	IPFERROR(60048);
2230145522Sdarrenr	error = ENOMEM;
2231145522Sdarrenr
223260852Sdarrenrjunkput:
2233255332Scy	if (fr != NULL) {
2234255332Scy		(void) ipf_derefrule(softc, &fr);
2235255332Scy	}
2236145522Sdarrenr
2237145522Sdarrenr	if ((ipnn != NULL) && (ipnn != &ipn)) {
2238145522Sdarrenr		KFREES(ipnn, ipn.ipn_dsize);
2239145522Sdarrenr	}
2240145522Sdarrenr	if (nat != NULL) {
2241145522Sdarrenr		if (aps != NULL) {
2242145522Sdarrenr			if (aps->aps_data != NULL) {
2243145522Sdarrenr				KFREES(aps->aps_data, aps->aps_psiz);
2244145522Sdarrenr			}
2245145522Sdarrenr			KFREE(aps);
2246145522Sdarrenr		}
2247145522Sdarrenr		if (in != NULL) {
2248145522Sdarrenr			if (in->in_apr)
2249255332Scy				ipf_proxy_deref(in->in_apr);
2250255332Scy			KFREES(in, in->in_size);
2251145522Sdarrenr		}
2252145522Sdarrenr		KFREE(nat);
2253145522Sdarrenr	}
225460852Sdarrenr	return error;
225560852Sdarrenr}
225660852Sdarrenr
225760852Sdarrenr
2258145522Sdarrenr/* ------------------------------------------------------------------------ */
2259255332Scy/* Function:    ipf_nat_delete                                              */
2260145522Sdarrenr/* Returns:     Nil                                                         */
2261255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
2262255332Scy/*              nat(I)     - pointer to NAT structure to delete             */
2263145522Sdarrenr/*              logtype(I) - type of LOG record to create before deleting   */
2264145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
2265145522Sdarrenr/*                                                                          */
2266145522Sdarrenr/* Delete a nat entry from the various lists and table.  If NAT logging is  */
2267145522Sdarrenr/* enabled then generate a NAT log record for this event.                   */
2268145522Sdarrenr/* ------------------------------------------------------------------------ */
2269255332Scyvoid
2270255332Scyipf_nat_delete(softc, nat, logtype)
2271255332Scy	ipf_main_softc_t *softc;
2272255332Scy	struct nat *nat;
2273255332Scy	int logtype;
227453642Sguido{
2275255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2276255332Scy	int madeorphan = 0, bkt, removed = 0;
2277255332Scy	nat_stat_side_t *nss;
227853642Sguido	struct ipnat *ipn;
227953642Sguido
2280255332Scy	if (logtype != 0 && softn->ipf_nat_logging != 0)
2281255332Scy		ipf_nat_log(softc, softn, nat, logtype);
228253642Sguido
2283145522Sdarrenr	/*
2284145522Sdarrenr	 * Take it as a general indication that all the pointers are set if
2285145522Sdarrenr	 * nat_pnext is set.
2286145522Sdarrenr	 */
2287145522Sdarrenr	if (nat->nat_pnext != NULL) {
2288172776Sdarrenr		removed = 1;
2289172776Sdarrenr
2290255332Scy		bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz;
2291255332Scy		nss = &softn->ipf_nat_stats.ns_side[0];
2292338171Scy		if (nss->ns_bucketlen[bkt] > 0)
2293338171Scy			nss->ns_bucketlen[bkt]--;
2294255332Scy		if (nss->ns_bucketlen[bkt] == 0) {
2295255332Scy			nss->ns_inuse--;
2296255332Scy		}
2297145522Sdarrenr
2298255332Scy		bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz;
2299255332Scy		nss = &softn->ipf_nat_stats.ns_side[1];
2300338171Scy		if (nss->ns_bucketlen[bkt] > 0)
2301338171Scy			nss->ns_bucketlen[bkt]--;
2302255332Scy		if (nss->ns_bucketlen[bkt] == 0) {
2303255332Scy			nss->ns_inuse--;
2304255332Scy		}
2305255332Scy
2306145522Sdarrenr		*nat->nat_pnext = nat->nat_next;
2307145522Sdarrenr		if (nat->nat_next != NULL) {
2308145522Sdarrenr			nat->nat_next->nat_pnext = nat->nat_pnext;
2309145522Sdarrenr			nat->nat_next = NULL;
2310145522Sdarrenr		}
2311145522Sdarrenr		nat->nat_pnext = NULL;
2312145522Sdarrenr
2313145522Sdarrenr		*nat->nat_phnext[0] = nat->nat_hnext[0];
2314145522Sdarrenr		if (nat->nat_hnext[0] != NULL) {
2315145522Sdarrenr			nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
2316145522Sdarrenr			nat->nat_hnext[0] = NULL;
2317145522Sdarrenr		}
2318145522Sdarrenr		nat->nat_phnext[0] = NULL;
2319145522Sdarrenr
2320145522Sdarrenr		*nat->nat_phnext[1] = nat->nat_hnext[1];
2321145522Sdarrenr		if (nat->nat_hnext[1] != NULL) {
2322145522Sdarrenr			nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
2323145522Sdarrenr			nat->nat_hnext[1] = NULL;
2324145522Sdarrenr		}
2325145522Sdarrenr		nat->nat_phnext[1] = NULL;
2326145522Sdarrenr
2327255332Scy		if ((nat->nat_flags & SI_WILDP) != 0) {
2328255332Scy			ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds);
2329255332Scy		}
2330255332Scy		madeorphan = 1;
233153642Sguido	}
233260852Sdarrenr
2333145522Sdarrenr	if (nat->nat_me != NULL) {
2334145522Sdarrenr		*nat->nat_me = NULL;
2335145522Sdarrenr		nat->nat_me = NULL;
2336255332Scy		nat->nat_ref--;
2337255332Scy		ASSERT(nat->nat_ref >= 0);
2338145522Sdarrenr	}
233960852Sdarrenr
2340255332Scy	if (nat->nat_tqe.tqe_ifq != NULL) {
2341255332Scy		/*
2342255332Scy		 * No call to ipf_freetimeoutqueue() is made here, they are
2343255332Scy		 * garbage collected in ipf_nat_expire().
2344255332Scy		 */
2345255332Scy		(void) ipf_deletequeueentry(&nat->nat_tqe);
2346255332Scy	}
2347145522Sdarrenr
2348255332Scy	if (nat->nat_sync) {
2349255332Scy		ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync);
2350255332Scy		nat->nat_sync = NULL;
2351255332Scy	}
2352255332Scy
2353170268Sdarrenr	if (logtype == NL_EXPIRE)
2354255332Scy		softn->ipf_nat_stats.ns_expire++;
2355170268Sdarrenr
2356172776Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
2357172776Sdarrenr	/*
2358172776Sdarrenr	 * NL_DESTROY should only be passed in when we've got nat_ref >= 2.
2359172776Sdarrenr	 * This happens when a nat'd packet is blocked and we want to throw
2360172776Sdarrenr	 * away the NAT session.
2361172776Sdarrenr	 */
2362172776Sdarrenr	if (logtype == NL_DESTROY) {
2363172776Sdarrenr		if (nat->nat_ref > 2) {
2364172776Sdarrenr			nat->nat_ref -= 2;
2365172776Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
2366172776Sdarrenr			if (removed)
2367255332Scy				softn->ipf_nat_stats.ns_orphans++;
2368172776Sdarrenr			return;
2369172776Sdarrenr		}
2370172776Sdarrenr	} else if (nat->nat_ref > 1) {
2371172776Sdarrenr		nat->nat_ref--;
2372172776Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
2373255332Scy		if (madeorphan == 1)
2374255332Scy			softn->ipf_nat_stats.ns_orphans++;
2375145522Sdarrenr		return;
2376145522Sdarrenr	}
2377255332Scy	ASSERT(nat->nat_ref >= 0);
2378172776Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
2379170268Sdarrenr
2380255332Scy	nat->nat_ref = 0;
2381255332Scy
2382255332Scy	if (madeorphan == 0)
2383255332Scy		softn->ipf_nat_stats.ns_orphans--;
2384255332Scy
2385161356Sguido	/*
2386255332Scy	 * At this point, nat_ref can be either 0 or -1
2387161356Sguido	 */
2388255332Scy	softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--;
2389145522Sdarrenr
2390255332Scy	if (nat->nat_fr != NULL) {
2391255332Scy		(void) ipf_derefrule(softc, &nat->nat_fr);
2392255332Scy	}
2393145522Sdarrenr
2394255332Scy	if (nat->nat_hm != NULL) {
2395255332Scy		ipf_nat_hostmapdel(softc, &nat->nat_hm);
2396255332Scy	}
2397145522Sdarrenr
239853642Sguido	/*
239953642Sguido	 * If there is an active reference from the nat entry to its parent
240053642Sguido	 * rule, decrement the rule's reference count and free it too if no
240153642Sguido	 * longer being used.
240253642Sguido	 */
2403145522Sdarrenr	ipn = nat->nat_ptr;
2404255332Scy	nat->nat_ptr = NULL;
2405255332Scy
240653642Sguido	if (ipn != NULL) {
2407255332Scy		ipn->in_space++;
2408255332Scy		ipf_nat_rule_deref(softc, &ipn);
240953642Sguido	}
241053642Sguido
2411255332Scy	if (nat->nat_aps != NULL) {
2412255332Scy		ipf_proxy_free(softc, nat->nat_aps);
2413255332Scy		nat->nat_aps = NULL;
2414255332Scy	}
2415255332Scy
2416145522Sdarrenr	MUTEX_DESTROY(&nat->nat_lock);
2417145522Sdarrenr
2418255332Scy	softn->ipf_nat_stats.ns_active--;
2419145522Sdarrenr
242053642Sguido	/*
242153642Sguido	 * If there's a fragment table entry too for this nat entry, then
2422145522Sdarrenr	 * dereference that as well.  This is after nat_lock is released
2423145522Sdarrenr	 * because of Tru64.
242453642Sguido	 */
2425255332Scy	ipf_frag_natforget(softc, (void *)nat);
2426145522Sdarrenr
2427145522Sdarrenr	KFREE(nat);
242853642Sguido}
242953642Sguido
243053642Sguido
2431145522Sdarrenr/* ------------------------------------------------------------------------ */
2432255332Scy/* Function:    ipf_nat_flushtable                                          */
2433145522Sdarrenr/* Returns:     int - number of NAT rules deleted                           */
2434255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
2435255332Scy/*              softn(I) - pointer to NAT context structure                 */
2436255332Scy/* Write Lock:  ipf_nat                                                     */
2437145522Sdarrenr/*                                                                          */
2438145522Sdarrenr/* Deletes all currently active NAT sessions.  In deleting each NAT entry a */
2439255332Scy/* log record should be emitted in ipf_nat_delete() if NAT logging is       */
2440255332Scy/* enabled.                                                                 */
2441145522Sdarrenr/* ------------------------------------------------------------------------ */
244253642Sguido/*
244353642Sguido * nat_flushtable - clear the NAT table of all mapping entries.
244453642Sguido */
2445255332Scystatic int
2446255332Scyipf_nat_flushtable(softc, softn)
2447255332Scy	ipf_main_softc_t *softc;
2448255332Scy	ipf_nat_softc_t *softn;
244953642Sguido{
2450145522Sdarrenr	nat_t *nat;
2451145522Sdarrenr	int j = 0;
245267614Sdarrenr
245353642Sguido	/*
245453642Sguido	 * ALL NAT mappings deleted, so lets just make the deletions
245553642Sguido	 * quicker.
245653642Sguido	 */
2457255332Scy	if (softn->ipf_nat_table[0] != NULL)
2458255332Scy		bzero((char *)softn->ipf_nat_table[0],
2459255332Scy		      sizeof(softn->ipf_nat_table[0]) *
2460255332Scy		      softn->ipf_nat_table_sz);
2461255332Scy	if (softn->ipf_nat_table[1] != NULL)
2462255332Scy		bzero((char *)softn->ipf_nat_table[1],
2463255332Scy		      sizeof(softn->ipf_nat_table[1]) *
2464255332Scy		      softn->ipf_nat_table_sz);
246553642Sguido
2466255332Scy	while ((nat = softn->ipf_nat_instances) != NULL) {
2467255332Scy		ipf_nat_delete(softc, nat, NL_FLUSH);
246853642Sguido		j++;
246953642Sguido	}
2470145522Sdarrenr
247153642Sguido	return j;
247253642Sguido}
247353642Sguido
247453642Sguido
2475145522Sdarrenr/* ------------------------------------------------------------------------ */
2476255332Scy/* Function:    ipf_nat_clearlist                                           */
2477145522Sdarrenr/* Returns:     int - number of NAT/RDR rules deleted                       */
2478255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
2479255332Scy/*              softn(I) - pointer to NAT context structure                 */
2480145522Sdarrenr/*                                                                          */
2481145522Sdarrenr/* Delete all rules in the current list of rules.  There is nothing elegant */
2482145522Sdarrenr/* about this cleanup: simply free all entries on the list of rules and     */
2483145522Sdarrenr/* clear out the tables used for hashed NAT rule lookups.                   */
2484145522Sdarrenr/* ------------------------------------------------------------------------ */
2485255332Scystatic int
2486255332Scyipf_nat_clearlist(softc, softn)
2487255332Scy	ipf_main_softc_t *softc;
2488255332Scy	ipf_nat_softc_t *softn;
248953642Sguido{
2490255332Scy	ipnat_t *n;
249153642Sguido	int i = 0;
249253642Sguido
2493255332Scy	if (softn->ipf_nat_map_rules != NULL) {
2494255332Scy		bzero((char *)softn->ipf_nat_map_rules,
2495255332Scy		      sizeof(*softn->ipf_nat_map_rules) *
2496255332Scy		      softn->ipf_nat_maprules_sz);
2497255332Scy	}
2498255332Scy	if (softn->ipf_nat_rdr_rules != NULL) {
2499255332Scy		bzero((char *)softn->ipf_nat_rdr_rules,
2500255332Scy		      sizeof(*softn->ipf_nat_rdr_rules) *
2501255332Scy		      softn->ipf_nat_rdrrules_sz);
2502255332Scy	}
250353642Sguido
2504255332Scy	while ((n = softn->ipf_nat_list) != NULL) {
2505255332Scy		ipf_nat_delrule(softc, softn, n, 0);
250653642Sguido		i++;
250753642Sguido	}
2508255332Scy#if SOLARIS && !defined(INSTANCES)
2509145522Sdarrenr	pfil_delayed_copy = 1;
2510145522Sdarrenr#endif
251153642Sguido	return i;
251253642Sguido}
251353642Sguido
251453642Sguido
2515145522Sdarrenr/* ------------------------------------------------------------------------ */
2516255332Scy/* Function:    ipf_nat_delrule                                             */
2517255332Scy/* Returns:     Nil                                                         */
2518255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
2519255332Scy/*              softn(I) - pointer to NAT context structure                 */
2520255332Scy/*              np(I)    - pointer to NAT rule to delete                    */
2521255332Scy/*              purge(I) - 1 == allow purge, 0 == prevent purge             */
2522255332Scy/* Locks:       WRITE(ipf_nat)                                              */
2523255332Scy/*                                                                          */
2524255332Scy/* Preventing "purge" from occuring is allowed because when all of the NAT  */
2525255332Scy/* rules are being removed, allowing the "purge" to walk through the list   */
2526255332Scy/* of NAT sessions, possibly multiple times, would be a large performance   */
2527255332Scy/* hit, on the order of O(N^2).                                             */
2528255332Scy/* ------------------------------------------------------------------------ */
2529255332Scystatic void
2530255332Scyipf_nat_delrule(softc, softn, np, purge)
2531255332Scy	ipf_main_softc_t *softc;
2532255332Scy	ipf_nat_softc_t *softn;
2533255332Scy	ipnat_t *np;
2534255332Scy	int purge;
2535255332Scy{
2536255332Scy
2537255332Scy	if (np->in_pnext != NULL) {
2538255332Scy		*np->in_pnext = np->in_next;
2539255332Scy		if (np->in_next != NULL)
2540255332Scy			np->in_next->in_pnext = np->in_pnext;
2541255332Scy		if (softn->ipf_nat_list_tail == &np->in_next)
2542255332Scy			softn->ipf_nat_list_tail = np->in_pnext;
2543255332Scy	}
2544255332Scy
2545255332Scy	if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) {
2546255332Scy		nat_t *next;
2547255332Scy		nat_t *nat;
2548255332Scy
2549255332Scy		for (next = softn->ipf_nat_instances; (nat = next) != NULL;) {
2550255332Scy			next = nat->nat_next;
2551255332Scy			if (nat->nat_ptr == np)
2552255332Scy				ipf_nat_delete(softc, nat, NL_PURGE);
2553255332Scy		}
2554255332Scy	}
2555255332Scy
2556255332Scy	if ((np->in_flags & IPN_DELETE) == 0) {
2557255332Scy		if (np->in_redir & NAT_REDIRECT) {
2558255332Scy			switch (np->in_v[0])
2559255332Scy			{
2560255332Scy			case 4 :
2561255332Scy				ipf_nat_delrdr(softn, np);
2562255332Scy				break;
2563255332Scy#ifdef USE_INET6
2564255332Scy			case 6 :
2565255332Scy				ipf_nat6_delrdr(softn, np);
2566255332Scy				break;
2567255332Scy#endif
2568255332Scy			}
2569255332Scy		}
2570255332Scy		if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) {
2571255332Scy			switch (np->in_v[0])
2572255332Scy			{
2573255332Scy			case 4 :
2574255332Scy				ipf_nat_delmap(softn, np);
2575255332Scy				break;
2576255332Scy#ifdef USE_INET6
2577255332Scy			case 6 :
2578255332Scy				ipf_nat6_delmap(softn, np);
2579255332Scy				break;
2580255332Scy#endif
2581255332Scy			}
2582255332Scy		}
2583255332Scy	}
2584255332Scy
2585255332Scy	np->in_flags |= IPN_DELETE;
2586255332Scy	ipf_nat_rule_deref(softc, &np);
2587255332Scy}
2588255332Scy
2589255332Scy
2590255332Scy/* ------------------------------------------------------------------------ */
2591255332Scy/* Function:    ipf_nat_newmap                                              */
2592145522Sdarrenr/* Returns:     int - -1 == error, 0 == success                             */
2593145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
2594145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
2595145522Sdarrenr/*              ni(I)  - pointer to structure with misc. information needed */
2596145522Sdarrenr/*                       to create new NAT entry.                           */
2597145522Sdarrenr/*                                                                          */
2598145522Sdarrenr/* Given an empty NAT structure, populate it with new information about a   */
2599145522Sdarrenr/* new NAT session, as defined by the matching NAT rule.                    */
2600145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
2601145522Sdarrenr/* to the new IP address for the translation.                               */
2602145522Sdarrenr/* ------------------------------------------------------------------------ */
2603255332Scystatic int
2604255332Scyipf_nat_newmap(fin, nat, ni)
2605255332Scy	fr_info_t *fin;
2606255332Scy	nat_t *nat;
2607255332Scy	natinfo_t *ni;
2608145522Sdarrenr{
2609255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
2610255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2611145522Sdarrenr	u_short st_port, dport, sport, port, sp, dp;
2612145522Sdarrenr	struct in_addr in, inb;
2613145522Sdarrenr	hostmap_t *hm;
2614145522Sdarrenr	u_32_t flags;
2615145522Sdarrenr	u_32_t st_ip;
2616145522Sdarrenr	ipnat_t *np;
2617145522Sdarrenr	nat_t *natl;
2618145522Sdarrenr	int l;
2619145522Sdarrenr
2620145522Sdarrenr	/*
2621145522Sdarrenr	 * If it's an outbound packet which doesn't match any existing
2622145522Sdarrenr	 * record, then create a new port
2623145522Sdarrenr	 */
2624145522Sdarrenr	l = 0;
2625145522Sdarrenr	hm = NULL;
2626145522Sdarrenr	np = ni->nai_np;
2627255332Scy	st_ip = np->in_snip;
2628255332Scy	st_port = np->in_spnext;
2629255332Scy	flags = nat->nat_flags;
2630145522Sdarrenr
2631255332Scy	if (flags & IPN_ICMPQUERY) {
2632255332Scy		sport = fin->fin_data[1];
2633255332Scy		dport = 0;
2634255332Scy	} else {
2635255332Scy		sport = htons(fin->fin_data[0]);
2636255332Scy		dport = htons(fin->fin_data[1]);
2637255332Scy	}
2638255332Scy
2639145522Sdarrenr	/*
2640145522Sdarrenr	 * Do a loop until we either run out of entries to try or we find
2641145522Sdarrenr	 * a NAT mapping that isn't currently being used.  This is done
2642145522Sdarrenr	 * because the change to the source is not (usually) being fixed.
2643145522Sdarrenr	 */
2644145522Sdarrenr	do {
2645145522Sdarrenr		port = 0;
2646255332Scy		in.s_addr = htonl(np->in_snip);
2647145522Sdarrenr		if (l == 0) {
2648145522Sdarrenr			/*
2649145522Sdarrenr			 * Check to see if there is an existing NAT
2650145522Sdarrenr			 * setup for this IP address pair.
2651145522Sdarrenr			 */
2652255332Scy			hm = ipf_nat_hostmap(softn, np, fin->fin_src,
2653255332Scy					     fin->fin_dst, in, 0);
2654145522Sdarrenr			if (hm != NULL)
2655255332Scy				in.s_addr = hm->hm_nsrcip.s_addr;
2656145522Sdarrenr		} else if ((l == 1) && (hm != NULL)) {
2657255332Scy			ipf_nat_hostmapdel(softc, &hm);
2658145522Sdarrenr		}
2659145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
2660145522Sdarrenr
2661145522Sdarrenr		nat->nat_hm = hm;
2662145522Sdarrenr
2663255332Scy		if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) {
2664255332Scy			if (l > 0) {
2665255332Scy				NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1);
2666338170Scy				DT4(ns_exhausted_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
2667145522Sdarrenr				return -1;
2668255332Scy			}
2669145522Sdarrenr		}
2670145522Sdarrenr
2671145522Sdarrenr		if (np->in_redir == NAT_BIMAP &&
2672255332Scy		    np->in_osrcmsk == np->in_nsrcmsk) {
2673145522Sdarrenr			/*
2674145522Sdarrenr			 * map the address block in a 1:1 fashion
2675145522Sdarrenr			 */
2676255332Scy			in.s_addr = np->in_nsrcaddr;
2677255332Scy			in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk;
2678145522Sdarrenr			in.s_addr = ntohl(in.s_addr);
2679145522Sdarrenr
2680145522Sdarrenr		} else if (np->in_redir & NAT_MAPBLK) {
2681145522Sdarrenr			if ((l >= np->in_ppip) || ((l > 0) &&
2682255332Scy			     !(flags & IPN_TCPUDP))) {
2683255332Scy				NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2);
2684338170Scy				DT4(ns_exhausted_2, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
2685145522Sdarrenr				return -1;
2686255332Scy			}
2687145522Sdarrenr			/*
2688145522Sdarrenr			 * map-block - Calculate destination address.
2689145522Sdarrenr			 */
2690145522Sdarrenr			in.s_addr = ntohl(fin->fin_saddr);
2691255332Scy			in.s_addr &= ntohl(~np->in_osrcmsk);
2692145522Sdarrenr			inb.s_addr = in.s_addr;
2693145522Sdarrenr			in.s_addr /= np->in_ippip;
2694255332Scy			in.s_addr &= ntohl(~np->in_nsrcmsk);
2695255332Scy			in.s_addr += ntohl(np->in_nsrcaddr);
2696145522Sdarrenr			/*
2697145522Sdarrenr			 * Calculate destination port.
2698145522Sdarrenr			 */
2699145522Sdarrenr			if ((flags & IPN_TCPUDP) &&
2700145522Sdarrenr			    (np->in_ppip != 0)) {
2701145522Sdarrenr				port = ntohs(sport) + l;
2702145522Sdarrenr				port %= np->in_ppip;
2703145522Sdarrenr				port += np->in_ppip *
2704145522Sdarrenr					(inb.s_addr % np->in_ippip);
2705145522Sdarrenr				port += MAPBLK_MINPORT;
2706145522Sdarrenr				port = htons(port);
2707145522Sdarrenr			}
2708145522Sdarrenr
2709255332Scy		} else if ((np->in_nsrcaddr == 0) &&
2710255332Scy			   (np->in_nsrcmsk == 0xffffffff)) {
2711255332Scy			i6addr_t in6;
2712255332Scy
2713145522Sdarrenr			/*
2714145522Sdarrenr			 * 0/32 - use the interface's IP address.
2715145522Sdarrenr			 */
2716145522Sdarrenr			if ((l > 0) ||
2717255332Scy			    ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp,
2718255332Scy				       &in6, NULL) == -1) {
2719255332Scy				NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1);
2720338170Scy				DT4(ns_new_ifpaddr_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
2721145522Sdarrenr				return -1;
2722255332Scy			}
2723255332Scy			in.s_addr = ntohl(in6.in4.s_addr);
2724145522Sdarrenr
2725255332Scy		} else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) {
2726145522Sdarrenr			/*
2727145522Sdarrenr			 * 0/0 - use the original source address/port.
2728145522Sdarrenr			 */
2729255332Scy			if (l > 0) {
2730255332Scy				NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3);
2731338170Scy				DT4(ns_exhausted_3, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
2732145522Sdarrenr				return -1;
2733255332Scy			}
2734145522Sdarrenr			in.s_addr = ntohl(fin->fin_saddr);
2735145522Sdarrenr
2736255332Scy		} else if ((np->in_nsrcmsk != 0xffffffff) &&
2737255332Scy			   (np->in_spnext == 0) && ((l > 0) || (hm == NULL)))
2738255332Scy			np->in_snip++;
2739145522Sdarrenr
2740145522Sdarrenr		natl = NULL;
2741145522Sdarrenr
2742145522Sdarrenr		if ((flags & IPN_TCPUDP) &&
2743145522Sdarrenr		    ((np->in_redir & NAT_MAPBLK) == 0) &&
2744145522Sdarrenr		    (np->in_flags & IPN_AUTOPORTMAP)) {
2745145522Sdarrenr			/*
2746145522Sdarrenr			 * "ports auto" (without map-block)
2747145522Sdarrenr			 */
2748145522Sdarrenr			if ((l > 0) && (l % np->in_ppip == 0)) {
2749255332Scy				if ((l > np->in_ppip) &&
2750255332Scy				    np->in_nsrcmsk != 0xffffffff)
2751255332Scy					np->in_snip++;
2752145522Sdarrenr			}
2753145522Sdarrenr			if (np->in_ppip != 0) {
2754145522Sdarrenr				port = ntohs(sport);
2755145522Sdarrenr				port += (l % np->in_ppip);
2756145522Sdarrenr				port %= np->in_ppip;
2757145522Sdarrenr				port += np->in_ppip *
2758145522Sdarrenr					(ntohl(fin->fin_saddr) %
2759145522Sdarrenr					 np->in_ippip);
2760145522Sdarrenr				port += MAPBLK_MINPORT;
2761145522Sdarrenr				port = htons(port);
2762145522Sdarrenr			}
2763145522Sdarrenr
2764145522Sdarrenr		} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
2765255332Scy			   (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) {
2766145522Sdarrenr			/*
2767145522Sdarrenr			 * Standard port translation.  Select next port.
2768145522Sdarrenr			 */
2769180778Sdarrenr			if (np->in_flags & IPN_SEQUENTIAL) {
2770255332Scy				port = np->in_spnext;
2771180778Sdarrenr			} else {
2772255332Scy				port = ipf_random() % (np->in_spmax -
2773255332Scy						       np->in_spmin + 1);
2774255332Scy				port += np->in_spmin;
2775180778Sdarrenr			}
2776180832Sdarrenr			port = htons(port);
2777255332Scy			np->in_spnext++;
2778145522Sdarrenr
2779255332Scy			if (np->in_spnext > np->in_spmax) {
2780255332Scy				np->in_spnext = np->in_spmin;
2781255332Scy				if (np->in_nsrcmsk != 0xffffffff)
2782255332Scy					np->in_snip++;
2783145522Sdarrenr			}
2784145522Sdarrenr		}
2785145522Sdarrenr
2786255332Scy		if (np->in_flags & IPN_SIPRANGE) {
2787255332Scy			if (np->in_snip > ntohl(np->in_nsrcmsk))
2788255332Scy				np->in_snip = ntohl(np->in_nsrcaddr);
2789145522Sdarrenr		} else {
2790255332Scy			if ((np->in_nsrcmsk != 0xffffffff) &&
2791255332Scy			    ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) >
2792255332Scy			    ntohl(np->in_nsrcaddr))
2793255332Scy				np->in_snip = ntohl(np->in_nsrcaddr) + 1;
2794145522Sdarrenr		}
2795145522Sdarrenr
2796145522Sdarrenr		if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
2797145522Sdarrenr			port = sport;
2798145522Sdarrenr
2799145522Sdarrenr		/*
2800145522Sdarrenr		 * Here we do a lookup of the connection as seen from
2801145522Sdarrenr		 * the outside.  If an IP# pair already exists, try
2802145522Sdarrenr		 * again.  So if you have A->B becomes C->B, you can
2803145522Sdarrenr		 * also have D->E become C->E but not D->B causing
2804145522Sdarrenr		 * another C->B.  Also take protocol and ports into
2805145522Sdarrenr		 * account when determining whether a pre-existing
2806145522Sdarrenr		 * NAT setup will cause an external conflict where
2807145522Sdarrenr		 * this is appropriate.
2808145522Sdarrenr		 */
2809145522Sdarrenr		inb.s_addr = htonl(in.s_addr);
2810145522Sdarrenr		sp = fin->fin_data[0];
2811145522Sdarrenr		dp = fin->fin_data[1];
2812145522Sdarrenr		fin->fin_data[0] = fin->fin_data[1];
2813255332Scy		fin->fin_data[1] = ntohs(port);
2814255332Scy		natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
2815255332Scy					(u_int)fin->fin_p, fin->fin_dst, inb);
2816145522Sdarrenr		fin->fin_data[0] = sp;
2817145522Sdarrenr		fin->fin_data[1] = dp;
2818145522Sdarrenr
2819145522Sdarrenr		/*
2820145522Sdarrenr		 * Has the search wrapped around and come back to the
2821145522Sdarrenr		 * start ?
2822145522Sdarrenr		 */
2823145522Sdarrenr		if ((natl != NULL) &&
2824255332Scy		    (np->in_spnext != 0) && (st_port == np->in_spnext) &&
2825255332Scy		    (np->in_snip != 0) && (st_ip == np->in_snip)) {
2826255332Scy			NBUMPSIDED(1, ns_wrap);
2827338170Scy			DT4(ns_wrap, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
2828145522Sdarrenr			return -1;
2829255332Scy		}
2830145522Sdarrenr		l++;
2831145522Sdarrenr	} while (natl != NULL);
2832145522Sdarrenr
2833145522Sdarrenr	/* Setup the NAT table */
2834255332Scy	nat->nat_osrcip = fin->fin_src;
2835255332Scy	nat->nat_nsrcaddr = htonl(in.s_addr);
2836255332Scy	nat->nat_odstip = fin->fin_dst;
2837255332Scy	nat->nat_ndstip = fin->fin_dst;
2838145522Sdarrenr	if (nat->nat_hm == NULL)
2839255332Scy		nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src,
2840255332Scy					      fin->fin_dst, nat->nat_nsrcip,
2841255332Scy					      0);
2842145522Sdarrenr
2843145522Sdarrenr	if (flags & IPN_TCPUDP) {
2844255332Scy		nat->nat_osport = sport;
2845255332Scy		nat->nat_nsport = port;	/* sport */
2846255332Scy		nat->nat_odport = dport;
2847255332Scy		nat->nat_ndport = dport;
2848145522Sdarrenr		((tcphdr_t *)fin->fin_dp)->th_sport = port;
2849145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
2850255332Scy		nat->nat_oicmpid = fin->fin_data[1];
2851145522Sdarrenr		((icmphdr_t *)fin->fin_dp)->icmp_id = port;
2852255332Scy		nat->nat_nicmpid = port;
2853145522Sdarrenr	}
2854145522Sdarrenr	return 0;
2855145522Sdarrenr}
2856145522Sdarrenr
2857145522Sdarrenr
2858145522Sdarrenr/* ------------------------------------------------------------------------ */
2859255332Scy/* Function:    ipf_nat_newrdr                                              */
2860145522Sdarrenr/* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
2861145522Sdarrenr/*                    allow rule to be moved if IPN_ROUNDR is set.          */
2862145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
2863145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
2864145522Sdarrenr/*              ni(I)  - pointer to structure with misc. information needed */
2865145522Sdarrenr/*                       to create new NAT entry.                           */
2866145522Sdarrenr/*                                                                          */
2867145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
2868145522Sdarrenr/* to the new IP address for the translation.                               */
2869145522Sdarrenr/* ------------------------------------------------------------------------ */
2870255332Scystatic int
2871255332Scyipf_nat_newrdr(fin, nat, ni)
2872255332Scy	fr_info_t *fin;
2873255332Scy	nat_t *nat;
2874255332Scy	natinfo_t *ni;
2875145522Sdarrenr{
2876255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
2877255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2878145522Sdarrenr	u_short nport, dport, sport;
2879170268Sdarrenr	struct in_addr in, inb;
2880170268Sdarrenr	u_short sp, dp;
2881145522Sdarrenr	hostmap_t *hm;
2882145522Sdarrenr	u_32_t flags;
2883145522Sdarrenr	ipnat_t *np;
2884170268Sdarrenr	nat_t *natl;
2885145522Sdarrenr	int move;
2886145522Sdarrenr
2887145522Sdarrenr	move = 1;
2888145522Sdarrenr	hm = NULL;
2889145522Sdarrenr	in.s_addr = 0;
2890145522Sdarrenr	np = ni->nai_np;
2891255332Scy	flags = nat->nat_flags;
2892145522Sdarrenr
2893255332Scy	if (flags & IPN_ICMPQUERY) {
2894255332Scy		dport = fin->fin_data[1];
2895255332Scy		sport = 0;
2896255332Scy	} else {
2897255332Scy		sport = htons(fin->fin_data[0]);
2898255332Scy		dport = htons(fin->fin_data[1]);
2899255332Scy	}
2900255332Scy
2901255332Scy	/* TRACE sport, dport */
2902255332Scy
2903255332Scy
2904145522Sdarrenr	/*
2905145522Sdarrenr	 * If the matching rule has IPN_STICKY set, then we want to have the
2906145522Sdarrenr	 * same rule kick in as before.  Why would this happen?  If you have
2907145522Sdarrenr	 * a collection of rdr rules with "round-robin sticky", the current
2908145522Sdarrenr	 * packet might match a different one to the previous connection but
2909145522Sdarrenr	 * we want the same destination to be used.
2910145522Sdarrenr	 */
2911153876Sguido	if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) &&
2912153876Sguido	    ((np->in_flags & IPN_STICKY) != 0)) {
2913255332Scy		hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst,
2914255332Scy				     in, (u_32_t)dport);
2915145522Sdarrenr		if (hm != NULL) {
2916255332Scy			in.s_addr = ntohl(hm->hm_ndstip.s_addr);
2917145522Sdarrenr			np = hm->hm_ipnat;
2918145522Sdarrenr			ni->nai_np = np;
2919145522Sdarrenr			move = 0;
2920255332Scy			ipf_nat_hostmapdel(softc, &hm);
2921145522Sdarrenr		}
2922145522Sdarrenr	}
2923145522Sdarrenr
2924145522Sdarrenr	/*
2925145522Sdarrenr	 * Otherwise, it's an inbound packet. Most likely, we don't
2926145522Sdarrenr	 * want to rewrite source ports and source addresses. Instead,
2927145522Sdarrenr	 * we want to rewrite to a fixed internal address and fixed
2928145522Sdarrenr	 * internal port.
2929145522Sdarrenr	 */
2930145522Sdarrenr	if (np->in_flags & IPN_SPLIT) {
2931255332Scy		in.s_addr = np->in_dnip;
2932272555Scy		inb.s_addr = htonl(in.s_addr);
2933145522Sdarrenr
2934145522Sdarrenr		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
2935255332Scy			hm = ipf_nat_hostmap(softn, NULL, fin->fin_src,
2936272555Scy					     fin->fin_dst, inb, (u_32_t)dport);
2937145522Sdarrenr			if (hm != NULL) {
2938255332Scy				in.s_addr = hm->hm_ndstip.s_addr;
2939145522Sdarrenr				move = 0;
2940145522Sdarrenr			}
2941145522Sdarrenr		}
2942145522Sdarrenr
2943145522Sdarrenr		if (hm == NULL || hm->hm_ref == 1) {
2944255332Scy			if (np->in_ndstaddr == htonl(in.s_addr)) {
2945255332Scy				np->in_dnip = ntohl(np->in_ndstmsk);
2946145522Sdarrenr				move = 0;
2947145522Sdarrenr			} else {
2948255332Scy				np->in_dnip = ntohl(np->in_ndstaddr);
2949145522Sdarrenr			}
2950145522Sdarrenr		}
2951255332Scy		if (hm != NULL)
2952255332Scy			ipf_nat_hostmapdel(softc, &hm);
2953145522Sdarrenr
2954255332Scy	} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) {
2955255332Scy		i6addr_t in6;
2956255332Scy
2957145522Sdarrenr		/*
2958145522Sdarrenr		 * 0/32 - use the interface's IP address.
2959145522Sdarrenr		 */
2960255332Scy		if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp,
2961255332Scy			       &in6, NULL) == -1) {
2962255332Scy			NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2);
2963338170Scy			DT3(ns_new_ifpaddr_2, fr_info_t *, fin, nat_t *, nat, natinfo_t, ni);
2964145522Sdarrenr			return -1;
2965255332Scy		}
2966255332Scy		in.s_addr = ntohl(in6.in4.s_addr);
2967145522Sdarrenr
2968255332Scy	} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) {
2969145522Sdarrenr		/*
2970145522Sdarrenr		 * 0/0 - use the original destination address/port.
2971145522Sdarrenr		 */
2972145522Sdarrenr		in.s_addr = ntohl(fin->fin_daddr);
2973145522Sdarrenr
2974145522Sdarrenr	} else if (np->in_redir == NAT_BIMAP &&
2975255332Scy		   np->in_ndstmsk == np->in_odstmsk) {
2976145522Sdarrenr		/*
2977145522Sdarrenr		 * map the address block in a 1:1 fashion
2978145522Sdarrenr		 */
2979255332Scy		in.s_addr = np->in_ndstaddr;
2980255332Scy		in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk;
2981145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
2982145522Sdarrenr	} else {
2983255332Scy		in.s_addr = ntohl(np->in_ndstaddr);
2984145522Sdarrenr	}
2985145522Sdarrenr
2986255332Scy	if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
2987145522Sdarrenr		nport = dport;
2988145522Sdarrenr	else {
2989145522Sdarrenr		/*
2990145522Sdarrenr		 * Whilst not optimized for the case where
2991145522Sdarrenr		 * pmin == pmax, the gain is not significant.
2992145522Sdarrenr		 */
2993145522Sdarrenr		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
2994255332Scy		    (np->in_odport != np->in_dtop)) {
2995255332Scy			nport = ntohs(dport) - np->in_odport + np->in_dpmax;
2996145522Sdarrenr			nport = htons(nport);
2997255332Scy		} else {
2998255332Scy			nport = htons(np->in_dpnext);
2999255332Scy			np->in_dpnext++;
3000255332Scy			if (np->in_dpnext > np->in_dpmax)
3001255332Scy				np->in_dpnext = np->in_dpmin;
3002255332Scy		}
3003145522Sdarrenr	}
3004145522Sdarrenr
3005145522Sdarrenr	/*
3006145522Sdarrenr	 * When the redirect-to address is set to 0.0.0.0, just
3007145522Sdarrenr	 * assume a blank `forwarding' of the packet.  We don't
3008145522Sdarrenr	 * setup any translation for this either.
3009145522Sdarrenr	 */
3010145522Sdarrenr	if (in.s_addr == 0) {
3011255332Scy		if (nport == dport) {
3012255332Scy			NBUMPSIDED(0, ns_xlate_null);
3013145522Sdarrenr			return -1;
3014255332Scy		}
3015145522Sdarrenr		in.s_addr = ntohl(fin->fin_daddr);
3016145522Sdarrenr	}
3017145522Sdarrenr
3018170268Sdarrenr	/*
3019170268Sdarrenr	 * Check to see if this redirect mapping already exists and if
3020170268Sdarrenr	 * it does, return "failure" (allowing it to be created will just
3021170268Sdarrenr	 * cause one or both of these "connections" to stop working.)
3022170268Sdarrenr	 */
3023170268Sdarrenr	inb.s_addr = htonl(in.s_addr);
3024170268Sdarrenr	sp = fin->fin_data[0];
3025170268Sdarrenr	dp = fin->fin_data[1];
3026170268Sdarrenr	fin->fin_data[1] = fin->fin_data[0];
3027170268Sdarrenr	fin->fin_data[0] = ntohs(nport);
3028255332Scy	natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
3029170268Sdarrenr			     (u_int)fin->fin_p, inb, fin->fin_src);
3030170268Sdarrenr	fin->fin_data[0] = sp;
3031170268Sdarrenr	fin->fin_data[1] = dp;
3032255332Scy	if (natl != NULL) {
3033255332Scy		DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl);
3034255332Scy		NBUMPSIDE(0, ns_xlate_exists);
3035170268Sdarrenr		return -1;
3036255332Scy	}
3037170268Sdarrenr
3038272555Scy	inb.s_addr = htonl(in.s_addr);
3039255332Scy	nat->nat_ndstaddr = htonl(in.s_addr);
3040255332Scy	nat->nat_odstip = fin->fin_dst;
3041255332Scy	nat->nat_nsrcip = fin->fin_src;
3042255332Scy	nat->nat_osrcip = fin->fin_src;
3043153876Sguido	if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
3044255332Scy		nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src,
3045272555Scy					      fin->fin_dst, inb, (u_32_t)dport);
3046145522Sdarrenr
3047145522Sdarrenr	if (flags & IPN_TCPUDP) {
3048255332Scy		nat->nat_odport = dport;
3049255332Scy		nat->nat_ndport = nport;
3050255332Scy		nat->nat_osport = sport;
3051255332Scy		nat->nat_nsport = sport;
3052145522Sdarrenr		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
3053145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
3054255332Scy		nat->nat_oicmpid = fin->fin_data[1];
3055145522Sdarrenr		((icmphdr_t *)fin->fin_dp)->icmp_id = nport;
3056255332Scy		nat->nat_nicmpid = nport;
3057145522Sdarrenr	}
3058145522Sdarrenr
3059145522Sdarrenr	return move;
3060145522Sdarrenr}
3061145522Sdarrenr
3062145522Sdarrenr/* ------------------------------------------------------------------------ */
3063255332Scy/* Function:    ipf_nat_add                                                 */
3064145522Sdarrenr/* Returns:     nat_t* - NULL == failure to create new NAT structure,       */
3065145522Sdarrenr/*                       else pointer to new NAT structure                  */
3066145522Sdarrenr/* Parameters:  fin(I)       - pointer to packet information                */
3067145522Sdarrenr/*              np(I)        - pointer to NAT rule                          */
3068145522Sdarrenr/*              natsave(I)   - pointer to where to store NAT struct pointer */
3069145522Sdarrenr/*              flags(I)     - flags describing the current packet          */
3070145522Sdarrenr/*              direction(I) - direction of packet (in/out)                 */
3071145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
3072145522Sdarrenr/*                                                                          */
3073145522Sdarrenr/* Attempts to create a new NAT entry.  Does not actually change the packet */
3074145522Sdarrenr/* in any way.                                                              */
3075145522Sdarrenr/*                                                                          */
3076351634Scy/* This function is in three main parts: (1) deal with creating a new NAT   */
3077145522Sdarrenr/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
3078145522Sdarrenr/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
3079145522Sdarrenr/* and (3) building that structure and putting it into the NAT table(s).    */
3080161356Sguido/*                                                                          */
3081351635Scy/* NOTE: natsave should NOT be used to point back to an ipstate_t struct    */
3082161356Sguido/*       as it can result in memory being corrupted.                        */
3083145522Sdarrenr/* ------------------------------------------------------------------------ */
3084255332Scynat_t *
3085255332Scyipf_nat_add(fin, np, natsave, flags, direction)
3086255332Scy	fr_info_t *fin;
3087255332Scy	ipnat_t *np;
3088255332Scy	nat_t **natsave;
3089255332Scy	u_int flags;
3090255332Scy	int direction;
309153642Sguido{
3092255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3093255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
309460852Sdarrenr	hostmap_t *hm = NULL;
309560852Sdarrenr	nat_t *nat, *natl;
3096255332Scy	natstat_t *nsp;
3097145522Sdarrenr	u_int nflags;
3098145522Sdarrenr	natinfo_t ni;
3099145522Sdarrenr	int move;
310053642Sguido
3101255332Scy	nsp = &softn->ipf_nat_stats;
3102255332Scy
3103255332Scy	if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) >
3104255332Scy	    softn->ipf_nat_table_wm_high) {
3105255332Scy		softn->ipf_nat_doflush = 1;
3106255332Scy	}
3107255332Scy
3108255332Scy	if (nsp->ns_active >= softn->ipf_nat_table_max) {
3109255332Scy		NBUMPSIDED(fin->fin_out, ns_table_max);
3110338170Scy		DT2(ns_table_max, nat_stat_t *, nsp, ipf_nat_softc_t *, softn);
3111130886Sdarrenr		return NULL;
3112130886Sdarrenr	}
3113130886Sdarrenr
3114145522Sdarrenr	move = 1;
3115145522Sdarrenr	nflags = np->in_flags & flags;
3116145522Sdarrenr	nflags &= NAT_FROMRULE;
311753642Sguido
3118145522Sdarrenr	ni.nai_np = np;
3119170268Sdarrenr	ni.nai_dport = 0;
3120170268Sdarrenr	ni.nai_sport = 0;
3121145522Sdarrenr
312253642Sguido	/* Give me a new nat */
312353642Sguido	KMALLOC(nat, nat_t *);
312460852Sdarrenr	if (nat == NULL) {
3125338170Scy		DT(ns_memfail);
3126255332Scy		NBUMPSIDED(fin->fin_out, ns_memfail);
3127130886Sdarrenr		/*
3128130886Sdarrenr		 * Try to automatically tune the max # of entries in the
3129130886Sdarrenr		 * table allowed to be less than what will cause kmem_alloc()
3130130886Sdarrenr		 * to fail and try to eliminate panics due to out of memory
3131130886Sdarrenr		 * conditions arising.
3132130886Sdarrenr		 */
3133255332Scy		if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) &&
3134255332Scy		    (nsp->ns_active > 100)) {
3135255332Scy			softn->ipf_nat_table_max = nsp->ns_active - 100;
3136255332Scy			printf("table_max reduced to %d\n",
3137255332Scy				softn->ipf_nat_table_max);
3138130886Sdarrenr		}
313953642Sguido		return NULL;
314060852Sdarrenr	}
314153642Sguido
3142255332Scy	if (flags & IPN_ICMPQUERY) {
3143145522Sdarrenr		/*
3144145522Sdarrenr		 * In the ICMP query NAT code, we translate the ICMP id fields
3145145522Sdarrenr		 * to make them unique. This is indepedent of the ICMP type
3146145522Sdarrenr		 * (e.g. in the unlikely event that a host sends an echo and
3147145522Sdarrenr		 * an tstamp request with the same id, both packets will have
3148145522Sdarrenr		 * their ip address/id field changed in the same way).
3149145522Sdarrenr		 */
3150145522Sdarrenr		/* The icmp_id field is used by the sender to identify the
3151145522Sdarrenr		 * process making the icmp request. (the receiver justs
3152145522Sdarrenr		 * copies it back in its response). So, it closely matches
3153145522Sdarrenr		 * the concept of source port. We overlay sport, so we can
3154145522Sdarrenr		 * maximally reuse the existing code.
3155145522Sdarrenr		 */
3156255332Scy		ni.nai_sport = fin->fin_data[1];
3157255332Scy		ni.nai_dport = 0;
3158145522Sdarrenr	}
3159145522Sdarrenr
316053642Sguido	bzero((char *)nat, sizeof(*nat));
316153642Sguido	nat->nat_flags = flags;
3162170268Sdarrenr	nat->nat_redir = np->in_redir;
3163255332Scy	nat->nat_dir = direction;
3164255332Scy	nat->nat_pr[0] = fin->fin_p;
3165255332Scy	nat->nat_pr[1] = fin->fin_p;
3166145522Sdarrenr
316753642Sguido	/*
3168255332Scy	 * Search the current table for a match and create a new mapping
3169255332Scy	 * if there is none found.
317053642Sguido	 */
3171255332Scy	if (np->in_redir & NAT_DIVERTUDP) {
3172255332Scy		move = ipf_nat_newdivert(fin, nat, &ni);
3173255332Scy
3174255332Scy	} else if (np->in_redir & NAT_REWRITE) {
3175255332Scy		move = ipf_nat_newrewrite(fin, nat, &ni);
3176255332Scy
3177255332Scy	} else if (direction == NAT_OUTBOUND) {
317853642Sguido		/*
3179145522Sdarrenr		 * We can now arrange to call this for the same connection
3180145522Sdarrenr		 * because ipf_nat_new doesn't protect the code path into
3181145522Sdarrenr		 * this function.
318253642Sguido		 */
3183255332Scy		natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p,
3184145522Sdarrenr				     fin->fin_src, fin->fin_dst);
3185145522Sdarrenr		if (natl != NULL) {
3186161356Sguido			KFREE(nat);
3187145522Sdarrenr			nat = natl;
3188145522Sdarrenr			goto done;
3189145522Sdarrenr		}
319053642Sguido
3191255332Scy		move = ipf_nat_newmap(fin, nat, &ni);
319253642Sguido	} else {
319353642Sguido		/*
3194255332Scy		 * NAT_INBOUND is used for redirects rules
319553642Sguido		 */
3196255332Scy		natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p,
3197255332Scy					fin->fin_src, fin->fin_dst);
3198145522Sdarrenr		if (natl != NULL) {
3199161356Sguido			KFREE(nat);
3200145522Sdarrenr			nat = natl;
3201145522Sdarrenr			goto done;
320260852Sdarrenr		}
320353642Sguido
3204255332Scy		move = ipf_nat_newrdr(fin, nat, &ni);
3205145522Sdarrenr	}
3206255332Scy	if (move == -1)
3207255332Scy		goto badnat;
320853642Sguido
3209255332Scy	np = ni.nai_np;
3210255332Scy
3211255332Scy	nat->nat_mssclamp = np->in_mssclamp;
3212255332Scy	nat->nat_me = natsave;
3213255332Scy	nat->nat_fr = fin->fin_fr;
3214255332Scy	nat->nat_rev = fin->fin_rev;
3215255332Scy	nat->nat_ptr = np;
3216255332Scy	nat->nat_dlocal = np->in_dlocal;
3217255332Scy
3218255332Scy	if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) {
3219255332Scy		if (ipf_proxy_new(fin, nat) == -1) {
3220255332Scy			NBUMPSIDED(fin->fin_out, ns_appr_fail);
3221338170Scy			DT3(ns_appr_fail, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np);
3222255332Scy			goto badnat;
322353642Sguido		}
322453642Sguido	}
322553642Sguido
3226255332Scy	nat->nat_ifps[0] = np->in_ifps[0];
3227255332Scy	if (np->in_ifps[0] != NULL) {
3228255332Scy		COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]);
3229145522Sdarrenr	}
3230145522Sdarrenr
3231255332Scy	nat->nat_ifps[1] = np->in_ifps[1];
3232255332Scy	if (np->in_ifps[1] != NULL) {
3233255332Scy		COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]);
3234255332Scy	}
323553642Sguido
3236255332Scy	if (ipf_nat_finalise(fin, nat) == -1) {
3237255332Scy		goto badnat;
3238255332Scy	}
323953642Sguido
3240255332Scy	np->in_use++;
324153642Sguido
3242255332Scy	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
3243255332Scy		if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) {
3244255332Scy			ipf_nat_delrdr(softn, np);
3245255332Scy			ipf_nat_addrdr(softn, np);
3246255332Scy		} else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) {
3247255332Scy			ipf_nat_delmap(softn, np);
3248255332Scy			ipf_nat_addmap(softn, np);
3249145522Sdarrenr		}
3250145522Sdarrenr	}
325153642Sguido
3252145522Sdarrenr	if (flags & SI_WILDP)
3253255332Scy		nsp->ns_wilds++;
3254255332Scy	nsp->ns_proto[nat->nat_pr[0]]++;
3255255332Scy
3256145522Sdarrenr	goto done;
3257145522Sdarrenrbadnat:
3258338169Scy	DT3(ns_badnatnew, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np);
3259255332Scy	NBUMPSIDE(fin->fin_out, ns_badnatnew);
3260145522Sdarrenr	if ((hm = nat->nat_hm) != NULL)
3261255332Scy		ipf_nat_hostmapdel(softc, &hm);
3262145522Sdarrenr	KFREE(nat);
3263145522Sdarrenr	nat = NULL;
3264145522Sdarrenrdone:
3265255332Scy	if (nat != NULL && np != NULL)
3266255332Scy		np->in_hits++;
3267255332Scy	if (natsave != NULL)
3268255332Scy		*natsave = nat;
3269145522Sdarrenr	return nat;
3270145522Sdarrenr}
327160852Sdarrenr
327260852Sdarrenr
3273145522Sdarrenr/* ------------------------------------------------------------------------ */
3274255332Scy/* Function:    ipf_nat_finalise                                            */
3275145522Sdarrenr/* Returns:     int - 0 == sucess, -1 == failure                            */
3276145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
3277145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
3278145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
3279145522Sdarrenr/*                                                                          */
3280145522Sdarrenr/* This is the tail end of constructing a new NAT entry and is the same     */
3281145522Sdarrenr/* for both IPv4 and IPv6.                                                  */
3282145522Sdarrenr/* ------------------------------------------------------------------------ */
3283145522Sdarrenr/*ARGSUSED*/
3284255332Scystatic int
3285255332Scyipf_nat_finalise(fin, nat)
3286255332Scy	fr_info_t *fin;
3287255332Scy	nat_t *nat;
3288145522Sdarrenr{
3289255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3290255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3291255332Scy	u_32_t sum1, sum2, sumd;
3292145522Sdarrenr	frentry_t *fr;
3293255332Scy	u_32_t flags;
3294344833Scy#if SOLARIS && defined(_KERNEL) && defined(ICK_M_CTL_MAGIC)
3295255332Scy	qpktinfo_t *qpi = fin->fin_qpi;
3296255332Scy#endif
3297145522Sdarrenr
3298255332Scy	flags = nat->nat_flags;
3299145522Sdarrenr
3300255332Scy	switch (nat->nat_pr[0])
3301255332Scy	{
3302255332Scy	case IPPROTO_ICMP :
3303255332Scy		sum1 = LONG_SUM(ntohs(nat->nat_oicmpid));
3304255332Scy		sum2 = LONG_SUM(ntohs(nat->nat_nicmpid));
3305255332Scy		CALC_SUMD(sum1, sum2, sumd);
3306255332Scy		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
3307255332Scy
3308255332Scy		break;
3309255332Scy
3310255332Scy	default :
3311255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \
3312255332Scy				ntohs(nat->nat_osport));
3313255332Scy		sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \
3314255332Scy				ntohs(nat->nat_nsport));
3315255332Scy		CALC_SUMD(sum1, sum2, sumd);
3316255332Scy		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
3317255332Scy
3318255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \
3319255332Scy				ntohs(nat->nat_odport));
3320255332Scy		sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \
3321255332Scy				ntohs(nat->nat_ndport));
3322255332Scy		CALC_SUMD(sum1, sum2, sumd);
3323255332Scy		nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16);
3324255332Scy		break;
3325161356Sguido	}
3326255332Scy
3327255332Scy	/*
3328255332Scy	 * Compute the partial checksum, just in case.
3329255332Scy	 * This is only ever placed into outbound packets so care needs
3330255332Scy	 * to be taken over which pair of addresses are used.
3331255332Scy	 */
3332255332Scy	if (nat->nat_dir == NAT_OUTBOUND) {
3333255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
3334255332Scy		sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr));
3335255332Scy	} else {
3336255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr));
3337255332Scy		sum1 += LONG_SUM(ntohl(nat->nat_odstaddr));
3338161356Sguido	}
3339255332Scy	sum1 += nat->nat_pr[1];
3340255332Scy	nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16);
3341145522Sdarrenr
3342255332Scy	sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr));
3343255332Scy	sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
3344255332Scy	CALC_SUMD(sum1, sum2, sumd);
3345255332Scy	nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
334692685Sdarrenr
3347255332Scy	sum1 = LONG_SUM(ntohl(nat->nat_odstaddr));
3348255332Scy	sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr));
3349255332Scy	CALC_SUMD(sum1, sum2, sumd);
3350255332Scy	nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16);
335192685Sdarrenr
3352255332Scy	nat->nat_v[0] = 4;
3353255332Scy	nat->nat_v[1] = 4;
3354255332Scy
3355255332Scy	if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
3356255332Scy		nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
3357255332Scy	}
3358255332Scy
3359255332Scy	if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
3360255332Scy		nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
3361255332Scy	}
3362255332Scy
3363255332Scy	if ((nat->nat_flags & SI_CLONE) == 0)
3364255332Scy		nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat);
3365255332Scy
3366255332Scy	if (ipf_nat_insert(softc, softn, nat) == 0) {
3367255332Scy		if (softn->ipf_nat_logging)
3368255332Scy			ipf_nat_log(softc, softn, nat, NL_NEW);
3369255332Scy		fr = nat->nat_fr;
3370145522Sdarrenr		if (fr != NULL) {
3371145522Sdarrenr			MUTEX_ENTER(&fr->fr_lock);
3372145522Sdarrenr			fr->fr_ref++;
3373145522Sdarrenr			MUTEX_EXIT(&fr->fr_lock);
3374145522Sdarrenr		}
3375145522Sdarrenr		return 0;
3376145522Sdarrenr	}
337792685Sdarrenr
3378255332Scy	NBUMPSIDED(fin->fin_out, ns_unfinalised);
3379338170Scy	DT2(ns_unfinalised, fr_info_t *, fin, nat_t *, nat);
3380145522Sdarrenr	/*
3381145522Sdarrenr	 * nat_insert failed, so cleanup time...
3382145522Sdarrenr	 */
3383255332Scy	if (nat->nat_sync != NULL)
3384255332Scy		ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync);
3385145522Sdarrenr	return -1;
338653642Sguido}
338753642Sguido
338853642Sguido
3389145522Sdarrenr/* ------------------------------------------------------------------------ */
3390255332Scy/* Function:    ipf_nat_insert                                              */
3391255332Scy/* Returns:     int - 0 == sucess, -1 == failure                            */
3392255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
3393255332Scy/*              softn(I) - pointer to NAT context structure                 */
3394255332Scy/*              nat(I) - pointer to NAT structure                           */
3395255332Scy/* Write Lock:  ipf_nat                                                     */
3396145522Sdarrenr/*                                                                          */
3397145522Sdarrenr/* Insert a NAT entry into the hash tables for searching and add it to the  */
3398145522Sdarrenr/* list of active NAT entries.  Adjust global counters when complete.       */
3399145522Sdarrenr/* ------------------------------------------------------------------------ */
3400255332Scyint
3401255332Scyipf_nat_insert(softc, softn, nat)
3402255332Scy	ipf_main_softc_t *softc;
3403255332Scy	ipf_nat_softc_t *softn;
3404255332Scy	nat_t *nat;
340560852Sdarrenr{
3406255332Scy	u_int hv0, hv1;
3407255332Scy	u_int sp, dp;
3408255332Scy	ipnat_t *in;
3409351635Scy	int ret;
341060852Sdarrenr
3411145522Sdarrenr	/*
3412145522Sdarrenr	 * Try and return an error as early as possible, so calculate the hash
3413145522Sdarrenr	 * entry numbers first and then proceed.
3414145522Sdarrenr	 */
3415145522Sdarrenr	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
3416255332Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
3417255332Scy			sp = nat->nat_osport;
3418255332Scy			dp = nat->nat_odport;
3419255332Scy		} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
3420255332Scy			sp = 0;
3421255332Scy			dp = nat->nat_oicmpid;
3422255332Scy		} else {
3423255332Scy			sp = 0;
3424255332Scy			dp = 0;
3425255332Scy		}
3426255332Scy		hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff);
3427255332Scy		hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff);
3428255332Scy		/*
3429255332Scy		 * TRACE nat_osrcaddr, nat_osport, nat_odstaddr,
3430255332Scy		 * nat_odport, hv0
3431255332Scy		 */
3432255332Scy
3433255332Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
3434255332Scy			sp = nat->nat_nsport;
3435255332Scy			dp = nat->nat_ndport;
3436255332Scy		} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
3437255332Scy			sp = 0;
3438255332Scy			dp = nat->nat_nicmpid;
3439255332Scy		} else {
3440255332Scy			sp = 0;
3441255332Scy			dp = 0;
3442255332Scy		}
3443255332Scy		hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff);
3444255332Scy		hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff);
3445255332Scy		/*
3446255332Scy		 * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr,
3447255332Scy		 * nat_ndport, hv1
3448255332Scy		 */
344980482Sdarrenr	} else {
3450255332Scy		hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff);
3451255332Scy		hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff);
3452255332Scy		/* TRACE nat_osrcaddr, nat_odstaddr, hv0 */
345380482Sdarrenr
3454255332Scy		hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff);
3455255332Scy		hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff);
3456255332Scy		/* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */
3457145522Sdarrenr	}
3458145522Sdarrenr
3459255332Scy	nat->nat_hv[0] = hv0;
3460255332Scy	nat->nat_hv[1] = hv1;
3461145522Sdarrenr
3462145522Sdarrenr	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
3463145522Sdarrenr
3464255332Scy	in = nat->nat_ptr;
3465255332Scy	nat->nat_ref = nat->nat_me ? 2 : 1;
3466145522Sdarrenr
3467145522Sdarrenr	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
3468255332Scy	nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4);
3469145522Sdarrenr
3470161356Sguido	if (nat->nat_ifnames[1][0] != '\0') {
3471145522Sdarrenr		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
3472255332Scy		nat->nat_ifps[1] = ipf_resolvenic(softc,
3473255332Scy						  nat->nat_ifnames[1], 4);
3474255332Scy	} else if (in->in_ifnames[1] != -1) {
3475255332Scy		char *name;
3476255332Scy
3477255332Scy		name = in->in_names + in->in_ifnames[1];
3478255332Scy		if (name[1] != '\0' && name[0] != '-' && name[0] != '*') {
3479255332Scy			(void) strncpy(nat->nat_ifnames[1],
3480255332Scy				       nat->nat_ifnames[0], LIFNAMSIZ);
3481255332Scy			nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
3482255332Scy			nat->nat_ifps[1] = nat->nat_ifps[0];
3483255332Scy		}
3484145522Sdarrenr	}
3485255332Scy	if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
3486255332Scy		nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
3487255332Scy	}
3488255332Scy	if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
3489255332Scy		nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
3490255332Scy	}
3491145522Sdarrenr
3492351635Scy	ret = ipf_nat_hashtab_add(softc, softn, nat);
3493351635Scy	if (ret == -1)
3494351635Scy		MUTEX_DESTROY(&nat->nat_lock);
3495351635Scy	return ret;
3496255332Scy}
3497145522Sdarrenr
3498255332Scy
3499255332Scy/* ------------------------------------------------------------------------ */
3500255332Scy/* Function:    ipf_nat_hashtab_add                                         */
3501351636Scy/* Returns:     int - 0 == sucess, -1 == failure                            */
3502255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
3503255332Scy/*              softn(I) - pointer to NAT context structure                 */
3504255332Scy/*              nat(I) - pointer to NAT structure                           */
3505255332Scy/*                                                                          */
3506255332Scy/* Handle the insertion of a NAT entry into the table/list.                 */
3507255332Scy/* ------------------------------------------------------------------------ */
3508255332Scyint
3509255332Scyipf_nat_hashtab_add(softc, softn, nat)
3510255332Scy	ipf_main_softc_t *softc;
3511255332Scy	ipf_nat_softc_t *softn;
3512255332Scy	nat_t *nat;
3513255332Scy{
3514255332Scy	nat_t **natp;
3515255332Scy	u_int hv0;
3516255332Scy	u_int hv1;
3517255332Scy
3518255332Scy	hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz;
3519255332Scy	hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz;
3520255332Scy
3521255332Scy	if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) {
3522255332Scy		u_int swap;
3523255332Scy
3524255332Scy		swap = hv0;
3525255332Scy		hv0 = hv1;
3526255332Scy		hv1 = swap;
3527255332Scy	}
3528255332Scy
3529255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >=
3530255332Scy	    softn->ipf_nat_maxbucket) {
3531255332Scy		DT1(ns_bucket_max_0, int,
3532255332Scy		    softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]);
3533255332Scy		NBUMPSIDE(0, ns_bucket_max);
3534255332Scy		return -1;
3535255332Scy	}
3536255332Scy
3537255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >=
3538255332Scy	    softn->ipf_nat_maxbucket) {
3539255332Scy		DT1(ns_bucket_max_1, int,
3540255332Scy		    softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]);
3541255332Scy		NBUMPSIDE(1, ns_bucket_max);
3542255332Scy		return -1;
3543255332Scy	}
3544255332Scy
3545255332Scy	/*
3546255332Scy	 * The ordering of operations in the list and hash table insertion
3547255332Scy	 * is very important.  The last operation for each task should be
3548255332Scy	 * to update the top of the list, after all the "nexts" have been
3549255332Scy	 * done so that walking the list while it is being done does not
3550255332Scy	 * find strange pointers.
3551255332Scy	 *
3552255332Scy	 * Global list of NAT instances
3553255332Scy	 */
3554255332Scy	nat->nat_next = softn->ipf_nat_instances;
3555255332Scy	nat->nat_pnext = &softn->ipf_nat_instances;
3556255332Scy	if (softn->ipf_nat_instances)
3557255332Scy		softn->ipf_nat_instances->nat_pnext = &nat->nat_next;
3558255332Scy	softn->ipf_nat_instances = nat;
3559255332Scy
3560255332Scy	/*
3561255332Scy	 * Inbound hash table.
3562255332Scy	 */
3563255332Scy	natp = &softn->ipf_nat_table[0][hv0];
356467614Sdarrenr	nat->nat_phnext[0] = natp;
356560852Sdarrenr	nat->nat_hnext[0] = *natp;
3566255332Scy	if (*natp) {
3567255332Scy		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
3568255332Scy	} else {
3569255332Scy		NBUMPSIDE(0, ns_inuse);
3570255332Scy	}
357160852Sdarrenr	*natp = nat;
3572255332Scy	NBUMPSIDE(0, ns_bucketlen[hv0]);
357367614Sdarrenr
3574255332Scy	/*
3575255332Scy	 * Outbound hash table.
3576255332Scy	 */
3577255332Scy	natp = &softn->ipf_nat_table[1][hv1];
3578255332Scy	nat->nat_phnext[1] = natp;
3579255332Scy	nat->nat_hnext[1] = *natp;
358067614Sdarrenr	if (*natp)
358167614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
3582255332Scy	else {
3583255332Scy		NBUMPSIDE(1, ns_inuse);
3584255332Scy	}
358560852Sdarrenr	*natp = nat;
3586255332Scy	NBUMPSIDE(1, ns_bucketlen[hv1]);
358760852Sdarrenr
3588255332Scy	ipf_nat_setqueue(softc, softn, nat);
3589145522Sdarrenr
3590255332Scy	if (nat->nat_dir & NAT_OUTBOUND) {
3591255332Scy		NBUMPSIDE(1, ns_added);
3592255332Scy	} else {
3593255332Scy		NBUMPSIDE(0, ns_added);
3594255332Scy	}
3595255332Scy	softn->ipf_nat_stats.ns_active++;
3596145522Sdarrenr	return 0;
359760852Sdarrenr}
359860852Sdarrenr
359960852Sdarrenr
3600145522Sdarrenr/* ------------------------------------------------------------------------ */
3601255332Scy/* Function:    ipf_nat_icmperrorlookup                                     */
3602145522Sdarrenr/* Returns:     nat_t* - point to matching NAT structure                    */
3603145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
3604145522Sdarrenr/*              dir(I) - direction of packet (in/out)                       */
3605145522Sdarrenr/*                                                                          */
3606145522Sdarrenr/* Check if the ICMP error message is related to an existing TCP, UDP or    */
3607145522Sdarrenr/* ICMP query nat entry.  It is assumed that the packet is already of the   */
3608145522Sdarrenr/* the required length.                                                     */
3609145522Sdarrenr/* ------------------------------------------------------------------------ */
3610255332Scynat_t *
3611255332Scyipf_nat_icmperrorlookup(fin, dir)
3612255332Scy	fr_info_t *fin;
3613255332Scy	int dir;
361453642Sguido{
3615255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3616255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3617145522Sdarrenr	int flags = 0, type, minlen;
3618145522Sdarrenr	icmphdr_t *icmp, *orgicmp;
3619255332Scy	nat_stat_side_t *nside;
362053642Sguido	tcphdr_t *tcp = NULL;
3621145522Sdarrenr	u_short data[2];
3622145522Sdarrenr	nat_t *nat;
362353642Sguido	ip_t *oip;
3624145522Sdarrenr	u_int p;
362553642Sguido
3626145522Sdarrenr	icmp = fin->fin_dp;
3627145522Sdarrenr	type = icmp->icmp_type;
3628255332Scy	nside = &softn->ipf_nat_stats.ns_side[fin->fin_out];
362953642Sguido	/*
363053642Sguido	 * Does it at least have the return (basic) IP header ?
363153642Sguido	 * Only a basic IP header (no options) should be with an ICMP error
3632145522Sdarrenr	 * header.  Also, if it's not an error type, then return.
363353642Sguido	 */
3634255332Scy	if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) {
3635255332Scy		ATOMIC_INCL(nside->ns_icmp_basic);
363653642Sguido		return NULL;
3637255332Scy	}
3638145522Sdarrenr
363953642Sguido	/*
3640145522Sdarrenr	 * Check packet size
364153642Sguido	 */
364253642Sguido	oip = (ip_t *)((char *)fin->fin_dp + 8);
3643145522Sdarrenr	minlen = IP_HL(oip) << 2;
3644145522Sdarrenr	if ((minlen < sizeof(ip_t)) ||
3645255332Scy	    (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) {
3646255332Scy		ATOMIC_INCL(nside->ns_icmp_size);
364753642Sguido		return NULL;
3648255332Scy	}
3649255332Scy
365064580Sdarrenr	/*
365164580Sdarrenr	 * Is the buffer big enough for all of it ?  It's the size of the IP
365264580Sdarrenr	 * header claimed in the encapsulated part which is of concern.  It
365364580Sdarrenr	 * may be too big to be in this buffer but not so big that it's
365464580Sdarrenr	 * outside the ICMP packet, leading to TCP deref's causing problems.
365564580Sdarrenr	 * This is possible because we don't know how big oip_hl is when we
3656255332Scy	 * do the pullup early in ipf_check() and thus can't gaurantee it is
365764580Sdarrenr	 * all here now.
365864580Sdarrenr	 */
3659255332Scy#ifdef  ipf_nat_KERNEL
366064580Sdarrenr	{
366164580Sdarrenr	mb_t *m;
366264580Sdarrenr
3663145522Sdarrenr	m = fin->fin_m;
3664369272Scy# if SOLARIS
3665255332Scy	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
3666255332Scy	    (char *)m->b_wptr) {
3667255332Scy		ATOMIC_INCL(nside->ns_icmp_mbuf);
366864580Sdarrenr		return NULL;
3669255332Scy	}
367064580Sdarrenr# else
367164580Sdarrenr	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
3672255332Scy	    (char *)fin->fin_ip + M_LEN(m)) {
3673255332Scy		ATOMIC_INCL(nside->ns_icmp_mbuf);
367464580Sdarrenr		return NULL;
3675255332Scy	}
367664580Sdarrenr# endif
367764580Sdarrenr	}
367864580Sdarrenr#endif
367964580Sdarrenr
3680255332Scy	if (fin->fin_daddr != oip->ip_src.s_addr) {
3681255332Scy		ATOMIC_INCL(nside->ns_icmp_address);
3682145522Sdarrenr		return NULL;
3683255332Scy	}
3684145522Sdarrenr
3685145522Sdarrenr	p = oip->ip_p;
3686145522Sdarrenr	if (p == IPPROTO_TCP)
368753642Sguido		flags = IPN_TCP;
3688145522Sdarrenr	else if (p == IPPROTO_UDP)
368953642Sguido		flags = IPN_UDP;
3690145522Sdarrenr	else if (p == IPPROTO_ICMP) {
3691145522Sdarrenr		orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2));
3692145522Sdarrenr
3693145522Sdarrenr		/* see if this is related to an ICMP query */
3694255332Scy		if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) {
3695145522Sdarrenr			data[0] = fin->fin_data[0];
3696145522Sdarrenr			data[1] = fin->fin_data[1];
3697145522Sdarrenr			fin->fin_data[0] = 0;
3698145522Sdarrenr			fin->fin_data[1] = orgicmp->icmp_id;
3699145522Sdarrenr
3700145522Sdarrenr			flags = IPN_ICMPERR|IPN_ICMPQUERY;
3701145522Sdarrenr			/*
3702145522Sdarrenr			 * NOTE : dir refers to the direction of the original
3703145522Sdarrenr			 *        ip packet. By definition the icmp error
3704145522Sdarrenr			 *        message flows in the opposite direction.
3705145522Sdarrenr			 */
3706145522Sdarrenr			if (dir == NAT_INBOUND)
3707255332Scy				nat = ipf_nat_inlookup(fin, flags, p,
3708255332Scy						       oip->ip_dst,
3709255332Scy						       oip->ip_src);
3710145522Sdarrenr			else
3711255332Scy				nat = ipf_nat_outlookup(fin, flags, p,
3712255332Scy							oip->ip_dst,
3713255332Scy							oip->ip_src);
3714145522Sdarrenr			fin->fin_data[0] = data[0];
3715145522Sdarrenr			fin->fin_data[1] = data[1];
3716145522Sdarrenr			return nat;
3717145522Sdarrenr		}
3718145522Sdarrenr	}
3719255332Scy
372053642Sguido	if (flags & IPN_TCPUDP) {
372164580Sdarrenr		minlen += 8;		/* + 64bits of data to get ports */
3722255332Scy		/* TRACE (fin,minlen) */
3723255332Scy		if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) {
3724255332Scy			ATOMIC_INCL(nside->ns_icmp_short);
372564580Sdarrenr			return NULL;
3726255332Scy		}
372792685Sdarrenr
372892685Sdarrenr		data[0] = fin->fin_data[0];
372992685Sdarrenr		data[1] = fin->fin_data[1];
3730145522Sdarrenr		tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2));
373192685Sdarrenr		fin->fin_data[0] = ntohs(tcp->th_dport);
373292685Sdarrenr		fin->fin_data[1] = ntohs(tcp->th_sport);
373392685Sdarrenr
373492685Sdarrenr		if (dir == NAT_INBOUND) {
3735255332Scy			nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst,
3736255332Scy					       oip->ip_src);
373792685Sdarrenr		} else {
3738255332Scy			nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst,
3739145522Sdarrenr					    oip->ip_src);
374092685Sdarrenr		}
374192685Sdarrenr		fin->fin_data[0] = data[0];
374292685Sdarrenr		fin->fin_data[1] = data[1];
374392685Sdarrenr		return nat;
374453642Sguido	}
374560852Sdarrenr	if (dir == NAT_INBOUND)
3746255332Scy		nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
374760852Sdarrenr	else
3748255332Scy		nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
3749255332Scy
3750255332Scy	return nat;
375153642Sguido}
375253642Sguido
375353642Sguido
3754145522Sdarrenr/* ------------------------------------------------------------------------ */
3755255332Scy/* Function:    ipf_nat_icmperror                                           */
3756145522Sdarrenr/* Returns:     nat_t* - point to matching NAT structure                    */
3757145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
3758145522Sdarrenr/*              nflags(I) - NAT flags for this packet                       */
3759145522Sdarrenr/*              dir(I)    - direction of packet (in/out)                    */
3760145522Sdarrenr/*                                                                          */
3761145522Sdarrenr/* Fix up an ICMP packet which is an error message for an existing NAT      */
3762145522Sdarrenr/* session.  This will correct both packet header data and checksums.       */
3763145522Sdarrenr/*                                                                          */
3764145522Sdarrenr/* This should *ONLY* be used for incoming ICMP error packets to make sure  */
3765145522Sdarrenr/* a NAT'd ICMP packet gets correctly recognised.                           */
3766145522Sdarrenr/* ------------------------------------------------------------------------ */
3767255332Scynat_t *
3768255332Scyipf_nat_icmperror(fin, nflags, dir)
3769255332Scy	fr_info_t *fin;
3770255332Scy	u_int *nflags;
3771255332Scy	int dir;
377253642Sguido{
3773255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3774255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3775145522Sdarrenr	u_32_t sum1, sum2, sumd, sumd2;
3776255332Scy	struct in_addr a1, a2, a3, a4;
3777170268Sdarrenr	int flags, dlen, odst;
3778145522Sdarrenr	icmphdr_t *icmp;
3779145522Sdarrenr	u_short *csump;
378095418Sdarrenr	tcphdr_t *tcp;
378153642Sguido	nat_t *nat;
378253642Sguido	ip_t *oip;
3783145522Sdarrenr	void *dp;
378453642Sguido
3785255332Scy	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
3786255332Scy		NBUMPSIDED(fin->fin_out, ns_icmp_short);
378763523Sdarrenr		return NULL;
3788255332Scy	}
3789255332Scy
379067614Sdarrenr	/*
3791255332Scy	 * ipf_nat_icmperrorlookup() will return NULL for `defective' packets.
379267614Sdarrenr	 */
3793255332Scy	if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) {
3794255332Scy		NBUMPSIDED(fin->fin_out, ns_icmp_notfound);
379553642Sguido		return NULL;
3796255332Scy	}
379792685Sdarrenr
3798145522Sdarrenr	tcp = NULL;
3799145522Sdarrenr	csump = NULL;
380092685Sdarrenr	flags = 0;
3801130886Sdarrenr	sumd2 = 0;
380253642Sguido	*nflags = IPN_ICMPERR;
3803145522Sdarrenr	icmp = fin->fin_dp;
380453642Sguido	oip = (ip_t *)&icmp->icmp_ip;
3805145522Sdarrenr	dp = (((char *)oip) + (IP_HL(oip) << 2));
3806145522Sdarrenr	if (oip->ip_p == IPPROTO_TCP) {
3807145522Sdarrenr		tcp = (tcphdr_t *)dp;
3808145522Sdarrenr		csump = (u_short *)&tcp->th_sum;
380953642Sguido		flags = IPN_TCP;
3810145522Sdarrenr	} else if (oip->ip_p == IPPROTO_UDP) {
3811145522Sdarrenr		udphdr_t *udp;
3812145522Sdarrenr
3813145522Sdarrenr		udp = (udphdr_t *)dp;
3814145522Sdarrenr		tcp = (tcphdr_t *)dp;
3815145522Sdarrenr		csump = (u_short *)&udp->uh_sum;
381653642Sguido		flags = IPN_UDP;
3817145522Sdarrenr	} else if (oip->ip_p == IPPROTO_ICMP)
3818145522Sdarrenr		flags = IPN_ICMPQUERY;
3819145522Sdarrenr	dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip);
382095418Sdarrenr
382195418Sdarrenr	/*
382253642Sguido	 * Need to adjust ICMP header to include the real IP#'s and
382353642Sguido	 * port #'s.  Only apply a checksum change relative to the
3824255332Scy	 * IP address change as it will be modified again in ipf_nat_checkout
382553642Sguido	 * for both address and port.  Two checksum changes are
382653642Sguido	 * necessary for the two header address changes.  Be careful
382753642Sguido	 * to only modify the checksum once for the port # and twice
382853642Sguido	 * for the IP#.
382953642Sguido	 */
383060852Sdarrenr
383167614Sdarrenr	/*
383267614Sdarrenr	 * Step 1
383367614Sdarrenr	 * Fix the IP addresses in the offending IP packet. You also need
3834170268Sdarrenr	 * to adjust the IP header checksum of that offending IP packet.
383567614Sdarrenr	 *
3836145522Sdarrenr	 * Normally, you would expect that the ICMP checksum of the
3837130886Sdarrenr	 * ICMP error message needs to be adjusted as well for the
3838130886Sdarrenr	 * IP address change in oip.
3839145522Sdarrenr	 * However, this is a NOP, because the ICMP checksum is
3840130886Sdarrenr	 * calculated over the complete ICMP packet, which includes the
3841145522Sdarrenr	 * changed oip IP addresses and oip->ip_sum. However, these
3842130886Sdarrenr	 * two changes cancel each other out (if the delta for
3843145522Sdarrenr	 * the IP address is x, then the delta for ip_sum is minus x),
3844130886Sdarrenr	 * so no change in the icmp_cksum is necessary.
3845130886Sdarrenr	 *
3846170268Sdarrenr	 * Inbound ICMP
3847170268Sdarrenr	 * ------------
3848170268Sdarrenr	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
3849170268Sdarrenr	 * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b)
3850255332Scy	 * - OIP_SRC(c)=nat_newsrcip,          OIP_DST(b)=nat_newdstip
3851255332Scy	 *=> OIP_SRC(c)=nat_oldsrcip,          OIP_DST(b)=nat_olddstip
3852170268Sdarrenr	 *
3853170268Sdarrenr	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
3854170268Sdarrenr	 * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a)
3855255332Scy	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
3856255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3857170268Sdarrenr	 *
3858255332Scy	 * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
3859255332Scy	 * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d)
3860255332Scy	 * - OIP_SRC(c)=nat_newsrcip,          OIP_DST(d)=nat_newdstip
3861255332Scy	 *=> OIP_SRC(c)=nat_oldsrcip,          OIP_DST(d)=nat_olddstip
3862255332Scy	 *
3863255332Scy	 * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
3864255332Scy	 * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
3865255332Scy	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
3866255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3867255332Scy	 *
3868170268Sdarrenr	 * Outbound ICMP
3869170268Sdarrenr	 * -------------
3870170268Sdarrenr	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
3871170268Sdarrenr	 * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
3872255332Scy	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
3873255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3874170268Sdarrenr	 *
3875170268Sdarrenr	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
3876170268Sdarrenr	 * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c)
3877255332Scy	 * - OIP_SRC(a)=nat_newsrcip,          OIP_DST(c)=nat_newdstip
3878255332Scy	 *=> OIP_SRC(a)=nat_oldsrcip,          OIP_DST(c)=nat_olddstip
3879170268Sdarrenr	 *
3880255332Scy	 * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
3881255332Scy	 * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d)
3882255332Scy	 * - OIP_SRC(c)=nat_olddstip,          OIP_DST(d)=nat_oldsrcip
3883255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3884255332Scy	 *
3885255332Scy	 * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
3886255332Scy	 * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a)
3887255332Scy	 * - OIP_SRC(b)=nat_newsrcip,          OIP_DST(a)=nat_newdstip
3888255332Scy	 *=> OIP_SRC(a)=nat_oldsrcip,          OIP_DST(c)=nat_olddstip
3889130886Sdarrenr	 */
3890255332Scy
3891255332Scy	if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) ||
3892255332Scy	    ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) {
3893255332Scy		a1.s_addr = ntohl(nat->nat_osrcaddr);
3894255332Scy		a4.s_addr = ntohl(oip->ip_src.s_addr);
3895255332Scy		a3.s_addr = ntohl(nat->nat_odstaddr);
3896255332Scy		a2.s_addr = ntohl(oip->ip_dst.s_addr);
3897170268Sdarrenr		oip->ip_src.s_addr = htonl(a1.s_addr);
3898255332Scy		oip->ip_dst.s_addr = htonl(a3.s_addr);
3899255332Scy		odst = 1;
3900170268Sdarrenr	} else {
3901255332Scy		a1.s_addr = ntohl(nat->nat_ndstaddr);
3902170268Sdarrenr		a2.s_addr = ntohl(oip->ip_dst.s_addr);
3903255332Scy		a3.s_addr = ntohl(nat->nat_nsrcaddr);
3904255332Scy		a4.s_addr = ntohl(oip->ip_src.s_addr);
3905255332Scy		oip->ip_dst.s_addr = htonl(a3.s_addr);
3906255332Scy		oip->ip_src.s_addr = htonl(a1.s_addr);
3907255332Scy		odst = 0;
3908170268Sdarrenr	}
3909255332Scy	sum1 = 0;
3910255332Scy	sum2 = 0;
3911255332Scy	sumd = 0;
3912255332Scy	CALC_SUMD(a2.s_addr, a3.s_addr, sum1);
3913255332Scy	CALC_SUMD(a4.s_addr, a1.s_addr, sum2);
3914255332Scy	sumd = sum2 + sum1;
3915255332Scy	if (sumd != 0)
3916255332Scy		ipf_fix_datacksum(&oip->ip_sum, sumd);
3917130886Sdarrenr
3918170268Sdarrenr	sumd2 = sumd;
3919170268Sdarrenr	sum1 = 0;
3920170268Sdarrenr	sum2 = 0;
3921170268Sdarrenr
3922130886Sdarrenr	/*
3923170268Sdarrenr	 * Fix UDP pseudo header checksum to compensate for the
3924170268Sdarrenr	 * IP address change.
3925130886Sdarrenr	 */
3926130886Sdarrenr	if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) {
3927255332Scy		u_32_t sum3, sum4, sumt;
3928255332Scy
392964580Sdarrenr		/*
393067614Sdarrenr		 * Step 2 :
393167614Sdarrenr		 * For offending TCP/UDP IP packets, translate the ports as
393267614Sdarrenr		 * well, based on the NAT specification. Of course such
3933170268Sdarrenr		 * a change may be reflected in the ICMP checksum as well.
393467614Sdarrenr		 *
393567614Sdarrenr		 * Since the port fields are part of the TCP/UDP checksum
393667614Sdarrenr		 * of the offending IP packet, you need to adjust that checksum
3937255332Scy		 * as well... except that the change in the port numbers should
3938170268Sdarrenr		 * be offset by the checksum change.  However, the TCP/UDP
3939170268Sdarrenr		 * checksum will also need to change if there has been an
3940170268Sdarrenr		 * IP address change.
394167614Sdarrenr		 */
3942170268Sdarrenr		if (odst == 1) {
3943255332Scy			sum1 = ntohs(nat->nat_osport);
3944255332Scy			sum4 = ntohs(tcp->th_sport);
3945255332Scy			sum3 = ntohs(nat->nat_odport);
3946255332Scy			sum2 = ntohs(tcp->th_dport);
3947145522Sdarrenr
3948170268Sdarrenr			tcp->th_sport = htons(sum1);
3949255332Scy			tcp->th_dport = htons(sum3);
3950170268Sdarrenr		} else {
3951255332Scy			sum1 = ntohs(nat->nat_ndport);
3952145522Sdarrenr			sum2 = ntohs(tcp->th_dport);
3953255332Scy			sum3 = ntohs(nat->nat_nsport);
3954255332Scy			sum4 = ntohs(tcp->th_sport);
3955170268Sdarrenr
3956255332Scy			tcp->th_dport = htons(sum3);
3957255332Scy			tcp->th_sport = htons(sum1);
3958145522Sdarrenr		}
3959255332Scy		CALC_SUMD(sum4, sum1, sumt);
3960255332Scy		sumd += sumt;
3961255332Scy		CALC_SUMD(sum2, sum3, sumt);
3962255332Scy		sumd += sumt;
396367614Sdarrenr
3964170268Sdarrenr		if (sumd != 0 || sumd2 != 0) {
3965145522Sdarrenr			/*
3966170268Sdarrenr			 * At this point, sumd is the delta to apply to the
3967170268Sdarrenr			 * TCP/UDP header, given the changes in both the IP
3968170268Sdarrenr			 * address and the ports and sumd2 is the delta to
3969170268Sdarrenr			 * apply to the ICMP header, given the IP address
3970170268Sdarrenr			 * change delta that may need to be applied to the
3971170268Sdarrenr			 * TCP/UDP checksum instead.
3972145522Sdarrenr			 *
3973170268Sdarrenr			 * If we will both the IP and TCP/UDP checksums
3974170268Sdarrenr			 * then the ICMP checksum changes by the address
3975170268Sdarrenr			 * delta applied to the TCP/UDP checksum.  If we
3976170268Sdarrenr			 * do not change the TCP/UDP checksum them we
3977170268Sdarrenr			 * apply the delta in ports to the ICMP checksum.
3978145522Sdarrenr			 */
3979161356Sguido			if (oip->ip_p == IPPROTO_UDP) {
3980161356Sguido				if ((dlen >= 8) && (*csump != 0)) {
3981255332Scy					ipf_fix_datacksum(csump, sumd);
3982161356Sguido				} else {
3983255332Scy					CALC_SUMD(sum1, sum4, sumd2);
3984255332Scy					CALC_SUMD(sum3, sum2, sumt);
3985255332Scy					sumd2 += sumt;
3986161356Sguido				}
3987170268Sdarrenr			} else if (oip->ip_p == IPPROTO_TCP) {
3988145522Sdarrenr				if (dlen >= 18) {
3989255332Scy					ipf_fix_datacksum(csump, sumd);
3990145522Sdarrenr				} else {
3991255332Scy					CALC_SUMD(sum1, sum4, sumd2);
3992255332Scy					CALC_SUMD(sum3, sum2, sumt);
3993255332Scy					sumd2 += sumt;
399467614Sdarrenr				}
399553642Sguido			}
3996170268Sdarrenr			if (sumd2 != 0) {
3997170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
3998170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
3999170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
4000255332Scy				ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0);
4001130886Sdarrenr			}
400253642Sguido		}
4003145522Sdarrenr	} else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) {
4004145522Sdarrenr		icmphdr_t *orgicmp;
4005145522Sdarrenr
4006145522Sdarrenr		/*
4007145522Sdarrenr		 * XXX - what if this is bogus hl and we go off the end ?
4008255332Scy		 * In this case, ipf_nat_icmperrorlookup() will have
4009255332Scy		 * returned NULL.
4010145522Sdarrenr		 */
4011145522Sdarrenr		orgicmp = (icmphdr_t *)dp;
4012145522Sdarrenr
4013170268Sdarrenr		if (odst == 1) {
4014255332Scy			if (orgicmp->icmp_id != nat->nat_osport) {
4015145522Sdarrenr
4016145522Sdarrenr				/*
4017145522Sdarrenr				 * Fix ICMP checksum (of the offening ICMP
4018145522Sdarrenr				 * query packet) to compensate the change
4019145522Sdarrenr				 * in the ICMP id of the offending ICMP
4020145522Sdarrenr				 * packet.
4021145522Sdarrenr				 *
4022145522Sdarrenr				 * Since you modify orgicmp->icmp_id with
4023145522Sdarrenr				 * a delta (say x) and you compensate that
4024145522Sdarrenr				 * in origicmp->icmp_cksum with a delta
4025145522Sdarrenr				 * minus x, you don't have to adjust the
4026145522Sdarrenr				 * overall icmp->icmp_cksum
4027145522Sdarrenr				 */
4028145522Sdarrenr				sum1 = ntohs(orgicmp->icmp_id);
4029255332Scy				sum2 = ntohs(nat->nat_oicmpid);
4030145522Sdarrenr				CALC_SUMD(sum1, sum2, sumd);
4031255332Scy				orgicmp->icmp_id = nat->nat_oicmpid;
4032255332Scy				ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd);
4033145522Sdarrenr			}
4034145522Sdarrenr		} /* nat_dir == NAT_INBOUND is impossible for icmp queries */
403553642Sguido	}
403653642Sguido	return nat;
403753642Sguido}
403853642Sguido
403953642Sguido
404053642Sguido/*
4041255332Scy *       MAP-IN    MAP-OUT   RDR-IN   RDR-OUT
4042255332Scy * osrc    X       == src    == src      X
4043255332Scy * odst    X       == dst    == dst      X
4044255332Scy * nsrc  == dst      X         X      == dst
4045255332Scy * ndst  == src      X         X      == src
4046255332Scy * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND
4047255332Scy */
4048255332Scy/*
4049145522Sdarrenr * NB: these lookups don't lock access to the list, it assumed that it has
4050145522Sdarrenr * already been done!
405153642Sguido */
4052145522Sdarrenr/* ------------------------------------------------------------------------ */
4053255332Scy/* Function:    ipf_nat_inlookup                                            */
4054145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
4055145522Sdarrenr/*                       else pointer to matching NAT entry                 */
4056145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
4057145522Sdarrenr/*              flags(I)  - NAT flags for this packet                       */
4058145522Sdarrenr/*              p(I)      - protocol for this packet                        */
4059145522Sdarrenr/*              src(I)    - source IP address                               */
4060145522Sdarrenr/*              mapdst(I) - destination IP address                          */
4061145522Sdarrenr/*                                                                          */
4062145522Sdarrenr/* Lookup a nat entry based on the mapped destination ip address/port and   */
4063145522Sdarrenr/* real source address/port.  We use this lookup when receiving a packet,   */
4064145522Sdarrenr/* we're looking for a table entry, based on the destination address.       */
4065145522Sdarrenr/*                                                                          */
4066145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
4067145522Sdarrenr/*                                                                          */
4068255332Scy/* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
4069145522Sdarrenr/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
4070145522Sdarrenr/*                                                                          */
4071145522Sdarrenr/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
4072145522Sdarrenr/*            the packet is of said protocol                                */
4073145522Sdarrenr/* ------------------------------------------------------------------------ */
4074255332Scynat_t *
4075255332Scyipf_nat_inlookup(fin, flags, p, src, mapdst)
4076255332Scy	fr_info_t *fin;
4077255332Scy	u_int flags, p;
4078255332Scy	struct in_addr src , mapdst;
407953642Sguido{
4080255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
4081255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
4082145522Sdarrenr	u_short sport, dport;
4083145522Sdarrenr	grehdr_t *gre;
408492685Sdarrenr	ipnat_t *ipn;
4085145522Sdarrenr	u_int sflags;
4086145522Sdarrenr	nat_t *nat;
4087145522Sdarrenr	int nflags;
4088145522Sdarrenr	u_32_t dst;
408992685Sdarrenr	void *ifp;
4090255332Scy	u_int hv, rhv;
409153642Sguido
4092161356Sguido	ifp = fin->fin_ifp;
4093145522Sdarrenr	gre = NULL;
409467614Sdarrenr	dst = mapdst.s_addr;
4095145522Sdarrenr	sflags = flags & NAT_TCPUDPICMP;
4096145522Sdarrenr
4097145522Sdarrenr	switch (p)
4098145522Sdarrenr	{
4099145522Sdarrenr	case IPPROTO_TCP :
4100145522Sdarrenr	case IPPROTO_UDP :
410192685Sdarrenr		sport = htons(fin->fin_data[0]);
410292685Sdarrenr		dport = htons(fin->fin_data[1]);
4103145522Sdarrenr		break;
4104145522Sdarrenr	case IPPROTO_ICMP :
4105323199Scy		sport = 0;
4106323199Scy		dport = fin->fin_data[1];
4107145522Sdarrenr		break;
4108145522Sdarrenr	default :
4109255332Scy		sport = 0;
4110255332Scy		dport = 0;
4111145522Sdarrenr		break;
411292685Sdarrenr	}
411353642Sguido
4114145522Sdarrenr
4115145522Sdarrenr	if ((flags & SI_WILDP) != 0)
4116145522Sdarrenr		goto find_in_wild_ports;
4117145522Sdarrenr
4118255332Scy	rhv = NAT_HASH_FN(dst, dport, 0xffffffff);
4119255332Scy	rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff);
4120255332Scy	hv = rhv % softn->ipf_nat_table_sz;
4121255332Scy	nat = softn->ipf_nat_table[1][hv];
4122255332Scy	/* TRACE dst, dport, src, sport, hv, nat */
4123255332Scy
412453642Sguido	for (; nat; nat = nat->nat_hnext[1]) {
4125161356Sguido		if (nat->nat_ifps[0] != NULL) {
4126161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
4127161356Sguido				continue;
4128255332Scy		}
4129161356Sguido
4130255332Scy		if (nat->nat_pr[0] != p)
4131255332Scy			continue;
4132145522Sdarrenr
4133255332Scy		switch (nat->nat_dir)
4134255332Scy		{
4135255332Scy		case NAT_INBOUND :
4136255332Scy		case NAT_DIVERTIN :
4137255332Scy			if (nat->nat_v[0] != 4)
4138255332Scy				continue;
4139255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4140255332Scy			    nat->nat_odstaddr != dst)
4141255332Scy				continue;
4142255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4143255332Scy				if (nat->nat_osport != sport)
4144145522Sdarrenr					continue;
4145255332Scy				if (nat->nat_odport != dport)
4146255332Scy					continue;
4147255332Scy
4148255332Scy			} else if (p == IPPROTO_ICMP) {
4149255332Scy				if (nat->nat_osport != dport) {
4150255332Scy					continue;
4151145522Sdarrenr				}
4152255332Scy			}
4153255332Scy			break;
4154255332Scy		case NAT_DIVERTOUT :
4155255332Scy			if (nat->nat_dlocal)
4156255332Scy				continue;
4157255332Scy		case NAT_OUTBOUND :
4158255332Scy			if (nat->nat_v[1] != 4)
4159255332Scy				continue;
4160255332Scy			if (nat->nat_dlocal)
4161255332Scy				continue;
4162255332Scy			if (nat->nat_dlocal)
4163255332Scy				continue;
4164255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4165255332Scy			    nat->nat_nsrcaddr != dst)
4166255332Scy				continue;
4167255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4168255332Scy				if (nat->nat_ndport != sport)
416992685Sdarrenr					continue;
4170255332Scy				if (nat->nat_nsport != dport)
417192685Sdarrenr					continue;
4172255332Scy
4173255332Scy			} else if (p == IPPROTO_ICMP) {
4174255332Scy				if (nat->nat_osport != dport) {
4175255332Scy					continue;
4176255332Scy				}
417792685Sdarrenr			}
4178255332Scy			break;
4179255332Scy		}
418092685Sdarrenr
4181255332Scy
4182255332Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
418392685Sdarrenr			ipn = nat->nat_ptr;
418492685Sdarrenr			if ((ipn != NULL) && (nat->nat_aps != NULL))
4185255332Scy				if (ipf_proxy_match(fin, nat) != 0)
418692685Sdarrenr					continue;
418792685Sdarrenr		}
4188255332Scy		if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
4189255332Scy			nat->nat_ifps[0] = ifp;
4190255332Scy			nat->nat_mtu[0] = GETIFMTU_4(ifp);
4191255332Scy		}
4192255332Scy		return nat;
419353642Sguido	}
4194145522Sdarrenr
4195145522Sdarrenr	/*
4196145522Sdarrenr	 * So if we didn't find it but there are wildcard members in the hash
4197145522Sdarrenr	 * table, go back and look for them.  We do this search and update here
4198145522Sdarrenr	 * because it is modifying the NAT table and we want to do this only
4199145522Sdarrenr	 * for the first packet that matches.  The exception, of course, is
4200145522Sdarrenr	 * for "dummy" (FI_IGNORE) lookups.
4201145522Sdarrenr	 */
4202145522Sdarrenrfind_in_wild_ports:
4203255332Scy	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
4204255332Scy		NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0);
420567614Sdarrenr		return NULL;
4206255332Scy	}
4207255332Scy	if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
4208255332Scy		NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0);
4209145522Sdarrenr		return NULL;
4210255332Scy	}
4211145522Sdarrenr
4212255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
4213145522Sdarrenr
421480482Sdarrenr	hv = NAT_HASH_FN(dst, 0, 0xffffffff);
4215255332Scy	hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz);
4216255332Scy	WRITE_ENTER(&softc->ipf_nat);
4217145522Sdarrenr
4218255332Scy	nat = softn->ipf_nat_table[1][hv];
4219255332Scy	/* TRACE dst, src, hv, nat */
422067614Sdarrenr	for (; nat; nat = nat->nat_hnext[1]) {
4221161356Sguido		if (nat->nat_ifps[0] != NULL) {
4222161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
4223161356Sguido				continue;
4224255332Scy		}
4225145522Sdarrenr
4226255332Scy		if (nat->nat_pr[0] != fin->fin_p)
422767614Sdarrenr			continue;
4228145522Sdarrenr
4229255332Scy		switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))
4230255332Scy		{
4231255332Scy		case NAT_INBOUND :
4232255332Scy			if (nat->nat_v[0] != 4)
4233255332Scy				continue;
4234255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4235255332Scy			    nat->nat_odstaddr != dst)
4236255332Scy				continue;
4237255332Scy			break;
4238255332Scy		case NAT_OUTBOUND :
4239255332Scy			if (nat->nat_v[1] != 4)
4240255332Scy				continue;
4241255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4242255332Scy			    nat->nat_nsrcaddr != dst)
4243255332Scy				continue;
4244255332Scy			break;
4245255332Scy		}
4246255332Scy
4247145522Sdarrenr		nflags = nat->nat_flags;
4248145522Sdarrenr		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
4249145522Sdarrenr			continue;
4250145522Sdarrenr
4251255332Scy		if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags,
4252255332Scy				   NAT_INBOUND) == 1) {
4253145522Sdarrenr			if ((fin->fin_flx & FI_IGNORE) != 0)
4254145522Sdarrenr				break;
4255145522Sdarrenr			if ((nflags & SI_CLONE) != 0) {
4256255332Scy				nat = ipf_nat_clone(fin, nat);
4257145522Sdarrenr				if (nat == NULL)
4258145522Sdarrenr					break;
4259145522Sdarrenr			} else {
4260255332Scy				MUTEX_ENTER(&softn->ipf_nat_new);
4261255332Scy				softn->ipf_nat_stats.ns_wilds--;
4262255332Scy				MUTEX_EXIT(&softn->ipf_nat_new);
4263145522Sdarrenr			}
4264255332Scy
4265255332Scy			if (nat->nat_dir == NAT_INBOUND) {
4266255332Scy				if (nat->nat_osport == 0) {
4267255332Scy					nat->nat_osport = sport;
4268255332Scy					nat->nat_nsport = sport;
4269255332Scy				}
4270255332Scy				if (nat->nat_odport == 0) {
4271255332Scy					nat->nat_odport = dport;
4272255332Scy					nat->nat_ndport = dport;
4273255332Scy				}
4274255332Scy			} else if (nat->nat_dir == NAT_OUTBOUND) {
4275255332Scy				if (nat->nat_osport == 0) {
4276255332Scy					nat->nat_osport = dport;
4277255332Scy					nat->nat_nsport = dport;
4278255332Scy				}
4279255332Scy				if (nat->nat_odport == 0) {
4280255332Scy					nat->nat_odport = sport;
4281255332Scy					nat->nat_ndport = sport;
4282255332Scy				}
4283255332Scy			}
4284255332Scy			if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
4285255332Scy				nat->nat_ifps[0] = ifp;
4286255332Scy				nat->nat_mtu[0] = GETIFMTU_4(ifp);
4287255332Scy			}
4288145522Sdarrenr			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
4289255332Scy			ipf_nat_tabmove(softn, nat);
429067614Sdarrenr			break;
429167614Sdarrenr		}
429267614Sdarrenr	}
4293145522Sdarrenr
4294255332Scy	MUTEX_DOWNGRADE(&softc->ipf_nat);
4295145522Sdarrenr
4296255332Scy	if (nat == NULL) {
4297255332Scy		NBUMPSIDE(0, ns_lookup_miss);
4298255332Scy	}
429967614Sdarrenr	return nat;
430053642Sguido}
430153642Sguido
430253642Sguido
4303145522Sdarrenr/* ------------------------------------------------------------------------ */
4304255332Scy/* Function:    ipf_nat_tabmove                                             */
4305145522Sdarrenr/* Returns:     Nil                                                         */
4306255332Scy/* Parameters:  softn(I) - pointer to NAT context structure                 */
4307255332Scy/*              nat(I)   - pointer to NAT structure                         */
4308145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
4309145522Sdarrenr/*                                                                          */
4310145522Sdarrenr/* This function is only called for TCP/UDP NAT table entries where the     */
4311145522Sdarrenr/* original was placed in the table without hashing on the ports and we now */
4312145522Sdarrenr/* want to include hashing on port numbers.                                 */
4313145522Sdarrenr/* ------------------------------------------------------------------------ */
4314255332Scystatic void
4315255332Scyipf_nat_tabmove(softn, nat)
4316255332Scy	ipf_nat_softc_t *softn;
4317255332Scy	nat_t *nat;
431867614Sdarrenr{
4319255332Scy	u_int hv0, hv1, rhv0, rhv1;
4320255332Scy	natstat_t *nsp;
432167614Sdarrenr	nat_t **natp;
432267614Sdarrenr
4323145522Sdarrenr	if (nat->nat_flags & SI_CLONE)
4324145522Sdarrenr		return;
432572006Sdarrenr
4326255332Scy	nsp = &softn->ipf_nat_stats;
432767614Sdarrenr	/*
432867614Sdarrenr	 * Remove the NAT entry from the old location
432967614Sdarrenr	 */
433067614Sdarrenr	if (nat->nat_hnext[0])
433167614Sdarrenr		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
433267614Sdarrenr	*nat->nat_phnext[0] = nat->nat_hnext[0];
4333255332Scy	nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] %
4334255332Scy				     softn->ipf_nat_table_sz]--;
433567614Sdarrenr
433667614Sdarrenr	if (nat->nat_hnext[1])
433767853Sdarrenr		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
433867614Sdarrenr	*nat->nat_phnext[1] = nat->nat_hnext[1];
4339255332Scy	nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] %
4340255332Scy				     softn->ipf_nat_table_sz]--;
434167614Sdarrenr
434267853Sdarrenr	/*
434367853Sdarrenr	 * Add into the NAT table in the new position
434467853Sdarrenr	 */
4345255332Scy	rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff);
4346255332Scy	rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport,
4347255332Scy			   0xffffffff);
4348255332Scy	rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff);
4349255332Scy	rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport,
4350255332Scy			   0xffffffff);
4351255332Scy
4352255332Scy	hv0 = rhv0 % softn->ipf_nat_table_sz;
4353255332Scy	hv1 = rhv1 % softn->ipf_nat_table_sz;
4354255332Scy
4355255332Scy	if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) {
4356255332Scy		u_int swap;
4357255332Scy
4358255332Scy		swap = hv0;
4359255332Scy		hv0 = hv1;
4360255332Scy		hv1 = swap;
4361255332Scy	}
4362255332Scy
4363255332Scy	/* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */
4364255332Scy	/* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */
4365255332Scy
4366255332Scy	nat->nat_hv[0] = rhv0;
4367255332Scy	natp = &softn->ipf_nat_table[0][hv0];
436867614Sdarrenr	if (*natp)
436967614Sdarrenr		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
437067614Sdarrenr	nat->nat_phnext[0] = natp;
437167614Sdarrenr	nat->nat_hnext[0] = *natp;
437267614Sdarrenr	*natp = nat;
4373255332Scy	nsp->ns_side[0].ns_bucketlen[hv0]++;
437467614Sdarrenr
4375255332Scy	nat->nat_hv[1] = rhv1;
4376255332Scy	natp = &softn->ipf_nat_table[1][hv1];
437767614Sdarrenr	if (*natp)
437867614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
437967614Sdarrenr	nat->nat_phnext[1] = natp;
438067614Sdarrenr	nat->nat_hnext[1] = *natp;
438167614Sdarrenr	*natp = nat;
4382255332Scy	nsp->ns_side[1].ns_bucketlen[hv1]++;
438367614Sdarrenr}
438467614Sdarrenr
438567614Sdarrenr
4386145522Sdarrenr/* ------------------------------------------------------------------------ */
4387255332Scy/* Function:    ipf_nat_outlookup                                           */
4388145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
4389145522Sdarrenr/*                       else pointer to matching NAT entry                 */
4390145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4391145522Sdarrenr/*              flags(I) - NAT flags for this packet                        */
4392145522Sdarrenr/*              p(I)     - protocol for this packet                         */
4393145522Sdarrenr/*              src(I)   - source IP address                                */
4394145522Sdarrenr/*              dst(I)   - destination IP address                           */
4395255332Scy/*              rw(I)    - 1 == write lock on  held, 0 == read lock.        */
4396145522Sdarrenr/*                                                                          */
4397145522Sdarrenr/* Lookup a nat entry based on the source 'real' ip address/port and        */
4398145522Sdarrenr/* destination address/port.  We use this lookup when sending a packet out, */
4399145522Sdarrenr/* we're looking for a table entry, based on the source address.            */
4400145522Sdarrenr/*                                                                          */
4401145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
4402145522Sdarrenr/*                                                                          */
4403255332Scy/* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
4404145522Sdarrenr/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
4405145522Sdarrenr/*                                                                          */
4406145522Sdarrenr/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
4407145522Sdarrenr/*            the packet is of said protocol                                */
4408145522Sdarrenr/* ------------------------------------------------------------------------ */
4409255332Scynat_t *
4410255332Scyipf_nat_outlookup(fin, flags, p, src, dst)
4411255332Scy	fr_info_t *fin;
4412255332Scy	u_int flags, p;
4413255332Scy	struct in_addr src , dst;
441453642Sguido{
4415255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
4416255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
4417145522Sdarrenr	u_short sport, dport;
4418145522Sdarrenr	u_int sflags;
441992685Sdarrenr	ipnat_t *ipn;
4420145522Sdarrenr	nat_t *nat;
442192685Sdarrenr	void *ifp;
442253642Sguido	u_int hv;
442353642Sguido
442492685Sdarrenr	ifp = fin->fin_ifp;
4425145522Sdarrenr	sflags = flags & IPN_TCPUDPICMP;
4426145522Sdarrenr
4427145522Sdarrenr	switch (p)
4428145522Sdarrenr	{
4429145522Sdarrenr	case IPPROTO_TCP :
4430145522Sdarrenr	case IPPROTO_UDP :
4431145522Sdarrenr		sport = htons(fin->fin_data[0]);
4432145522Sdarrenr		dport = htons(fin->fin_data[1]);
4433145522Sdarrenr		break;
4434145522Sdarrenr	case IPPROTO_ICMP :
4435323199Scy		sport = 0;
4436323199Scy		dport = fin->fin_data[1];
4437145522Sdarrenr		break;
4438145522Sdarrenr	default :
4439323199Scy		sport = 0;
4440323199Scy		dport = 0;
4441145522Sdarrenr		break;
444292685Sdarrenr	}
444353642Sguido
4444145522Sdarrenr	if ((flags & SI_WILDP) != 0)
4445145522Sdarrenr		goto find_out_wild_ports;
4446145522Sdarrenr
4447255332Scy	hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff);
4448255332Scy	hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz);
4449255332Scy	nat = softn->ipf_nat_table[0][hv];
4450255332Scy
4451255332Scy	/* TRACE src, sport, dst, dport, hv, nat */
4452255332Scy
445353642Sguido	for (; nat; nat = nat->nat_hnext[0]) {
4454161356Sguido		if (nat->nat_ifps[1] != NULL) {
4455161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
4456161356Sguido				continue;
4457255332Scy		}
4458161356Sguido
4459255332Scy		if (nat->nat_pr[1] != p)
4460255332Scy			continue;
446153642Sguido
4462255332Scy		switch (nat->nat_dir)
4463255332Scy		{
4464255332Scy		case NAT_INBOUND :
4465255332Scy		case NAT_DIVERTIN :
4466255332Scy			if (nat->nat_v[1] != 4)
4467255332Scy				continue;
4468255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4469255332Scy			    nat->nat_nsrcaddr != dst.s_addr)
4470255332Scy				continue;
4471255332Scy
4472255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4473255332Scy				if (nat->nat_ndport != sport)
4474145522Sdarrenr					continue;
4475255332Scy				if (nat->nat_nsport != dport)
447692685Sdarrenr					continue;
4477255332Scy
4478255332Scy			} else if (p == IPPROTO_ICMP) {
4479255332Scy				if (nat->nat_osport != dport) {
448092685Sdarrenr					continue;
4481255332Scy				}
448292685Sdarrenr			}
4483255332Scy			break;
4484255332Scy		case NAT_OUTBOUND :
4485255332Scy		case NAT_DIVERTOUT :
4486255332Scy			if (nat->nat_v[0] != 4)
4487255332Scy				continue;
4488255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4489255332Scy			    nat->nat_odstaddr != dst.s_addr)
4490255332Scy				continue;
449192685Sdarrenr
4492255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4493255332Scy				if (nat->nat_odport != dport)
449492685Sdarrenr					continue;
4495255332Scy				if (nat->nat_osport != sport)
4496255332Scy					continue;
4497255332Scy
4498255332Scy			} else if (p == IPPROTO_ICMP) {
4499255332Scy				if (nat->nat_osport != dport) {
4500255332Scy					continue;
4501255332Scy				}
4502255332Scy			}
4503255332Scy			break;
450492685Sdarrenr		}
4505255332Scy
4506255332Scy		ipn = nat->nat_ptr;
4507255332Scy		if ((ipn != NULL) && (nat->nat_aps != NULL))
4508255332Scy			if (ipf_proxy_match(fin, nat) != 0)
4509255332Scy				continue;
4510255332Scy
4511255332Scy		if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
4512255332Scy			nat->nat_ifps[1] = ifp;
4513255332Scy			nat->nat_mtu[1] = GETIFMTU_4(ifp);
4514255332Scy		}
4515255332Scy		return nat;
451653642Sguido	}
4517145522Sdarrenr
4518145522Sdarrenr	/*
4519145522Sdarrenr	 * So if we didn't find it but there are wildcard members in the hash
4520145522Sdarrenr	 * table, go back and look for them.  We do this search and update here
4521145522Sdarrenr	 * because it is modifying the NAT table and we want to do this only
4522145522Sdarrenr	 * for the first packet that matches.  The exception, of course, is
4523145522Sdarrenr	 * for "dummy" (FI_IGNORE) lookups.
4524145522Sdarrenr	 */
4525145522Sdarrenrfind_out_wild_ports:
4526255332Scy	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
4527255332Scy		NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1);
452867614Sdarrenr		return NULL;
4529255332Scy	}
4530255332Scy	if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
4531255332Scy		NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1);
4532145522Sdarrenr		return NULL;
4533255332Scy	}
453492685Sdarrenr
4535255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
4536145522Sdarrenr
4537255332Scy	hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff);
4538255332Scy	hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz);
4539145522Sdarrenr
4540255332Scy	WRITE_ENTER(&softc->ipf_nat);
4541145522Sdarrenr
4542255332Scy	nat = softn->ipf_nat_table[0][hv];
454367614Sdarrenr	for (; nat; nat = nat->nat_hnext[0]) {
4544161356Sguido		if (nat->nat_ifps[1] != NULL) {
4545161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
4546161356Sguido				continue;
4547255332Scy		}
4548145522Sdarrenr
4549255332Scy		if (nat->nat_pr[1] != fin->fin_p)
455067614Sdarrenr			continue;
4551145522Sdarrenr
4552255332Scy		switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))
4553255332Scy		{
4554255332Scy		case NAT_INBOUND :
4555255332Scy			if (nat->nat_v[1] != 4)
4556255332Scy				continue;
4557255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4558255332Scy			    nat->nat_nsrcaddr != dst.s_addr)
4559255332Scy				continue;
4560255332Scy			break;
4561255332Scy		case NAT_OUTBOUND :
4562255332Scy			if (nat->nat_v[0] != 4)
4563255332Scy				continue;
4564255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4565255332Scy			    nat->nat_odstaddr != dst.s_addr)
4566255332Scy				continue;
4567255332Scy			break;
4568255332Scy		}
4569255332Scy
4570255332Scy		if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP)))
4571145522Sdarrenr			continue;
4572145522Sdarrenr
4573255332Scy		if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags,
4574255332Scy				   NAT_OUTBOUND) == 1) {
4575145522Sdarrenr			if ((fin->fin_flx & FI_IGNORE) != 0)
4576145522Sdarrenr				break;
4577255332Scy			if ((nat->nat_flags & SI_CLONE) != 0) {
4578255332Scy				nat = ipf_nat_clone(fin, nat);
4579145522Sdarrenr				if (nat == NULL)
4580145522Sdarrenr					break;
4581145522Sdarrenr			} else {
4582255332Scy				MUTEX_ENTER(&softn->ipf_nat_new);
4583255332Scy				softn->ipf_nat_stats.ns_wilds--;
4584255332Scy				MUTEX_EXIT(&softn->ipf_nat_new);
4585145522Sdarrenr			}
4586255332Scy
4587255332Scy			if (nat->nat_dir == NAT_OUTBOUND) {
4588255332Scy				if (nat->nat_osport == 0) {
4589255332Scy					nat->nat_osport = sport;
4590255332Scy					nat->nat_nsport = sport;
4591255332Scy				}
4592255332Scy				if (nat->nat_odport == 0) {
4593255332Scy					nat->nat_odport = dport;
4594255332Scy					nat->nat_ndport = dport;
4595255332Scy				}
4596255332Scy			} else if (nat->nat_dir == NAT_INBOUND) {
4597255332Scy				if (nat->nat_osport == 0) {
4598255332Scy					nat->nat_osport = dport;
4599255332Scy					nat->nat_nsport = dport;
4600255332Scy				}
4601255332Scy				if (nat->nat_odport == 0) {
4602255332Scy					nat->nat_odport = sport;
4603255332Scy					nat->nat_ndport = sport;
4604255332Scy				}
4605255332Scy			}
4606255332Scy			if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
4607255332Scy				nat->nat_ifps[1] = ifp;
4608255332Scy				nat->nat_mtu[1] = GETIFMTU_4(ifp);
4609255332Scy			}
4610145522Sdarrenr			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
4611255332Scy			ipf_nat_tabmove(softn, nat);
461267614Sdarrenr			break;
461367614Sdarrenr		}
461467614Sdarrenr	}
4615145522Sdarrenr
4616255332Scy	MUTEX_DOWNGRADE(&softc->ipf_nat);
4617145522Sdarrenr
4618255332Scy	if (nat == NULL) {
4619255332Scy		NBUMPSIDE(1, ns_lookup_miss);
4620255332Scy	}
462167614Sdarrenr	return nat;
462253642Sguido}
462353642Sguido
462453642Sguido
4625145522Sdarrenr/* ------------------------------------------------------------------------ */
4626255332Scy/* Function:    ipf_nat_lookupredir                                         */
4627145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
4628145522Sdarrenr/*                       else pointer to matching NAT entry                 */
4629145522Sdarrenr/* Parameters:  np(I) - pointer to description of packet to find NAT table  */
4630145522Sdarrenr/*                      entry for.                                          */
4631145522Sdarrenr/*                                                                          */
4632145522Sdarrenr/* Lookup the NAT tables to search for a matching redirect                  */
4633161356Sguido/* The contents of natlookup_t should imitate those found in a packet that  */
4634161356Sguido/* would be translated - ie a packet coming in for RDR or going out for MAP.*/
4635161356Sguido/* We can do the lookup in one of two ways, imitating an inbound or         */
4636161356Sguido/* outbound  packet.  By default we assume outbound, unless IPN_IN is set.  */
4637161356Sguido/* For IN, the fields are set as follows:                                   */
4638161356Sguido/*     nl_real* = source information                                        */
4639161356Sguido/*     nl_out* = destination information (translated)                       */
4640161356Sguido/* For an out packet, the fields are set like this:                         */
4641161356Sguido/*     nl_in* = source information (untranslated)                           */
4642161356Sguido/*     nl_out* = destination information (translated)                       */
4643145522Sdarrenr/* ------------------------------------------------------------------------ */
4644255332Scynat_t *
4645255332Scyipf_nat_lookupredir(np)
4646255332Scy	natlookup_t *np;
464753642Sguido{
4648145522Sdarrenr	fr_info_t fi;
464953642Sguido	nat_t *nat;
465053642Sguido
465192685Sdarrenr	bzero((char *)&fi, sizeof(fi));
4652145522Sdarrenr	if (np->nl_flags & IPN_IN) {
4653145522Sdarrenr		fi.fin_data[0] = ntohs(np->nl_realport);
4654145522Sdarrenr		fi.fin_data[1] = ntohs(np->nl_outport);
4655145522Sdarrenr	} else {
4656145522Sdarrenr		fi.fin_data[0] = ntohs(np->nl_inport);
4657145522Sdarrenr		fi.fin_data[1] = ntohs(np->nl_outport);
4658145522Sdarrenr	}
4659145522Sdarrenr	if (np->nl_flags & IPN_TCP)
4660145522Sdarrenr		fi.fin_p = IPPROTO_TCP;
4661145522Sdarrenr	else if (np->nl_flags & IPN_UDP)
4662145522Sdarrenr		fi.fin_p = IPPROTO_UDP;
4663145522Sdarrenr	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
4664145522Sdarrenr		fi.fin_p = IPPROTO_ICMP;
466592685Sdarrenr
466653642Sguido	/*
4667145522Sdarrenr	 * We can do two sorts of lookups:
4668145522Sdarrenr	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
4669145522Sdarrenr	 * - default: we have the `in' and `out' address, look for `real'.
467053642Sguido	 */
4671145522Sdarrenr	if (np->nl_flags & IPN_IN) {
4672255332Scy		if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p,
4673255332Scy					    np->nl_realip, np->nl_outip))) {
4674255332Scy			np->nl_inip = nat->nat_odstip;
4675255332Scy			np->nl_inport = nat->nat_odport;
4676145522Sdarrenr		}
4677145522Sdarrenr	} else {
4678145522Sdarrenr		/*
4679145522Sdarrenr		 * If nl_inip is non null, this is a lookup based on the real
4680145522Sdarrenr		 * ip address. Else, we use the fake.
4681145522Sdarrenr		 */
4682255332Scy		if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p,
4683145522Sdarrenr					 np->nl_inip, np->nl_outip))) {
4684145522Sdarrenr
4685145522Sdarrenr			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
4686145522Sdarrenr				fr_info_t fin;
4687145522Sdarrenr				bzero((char *)&fin, sizeof(fin));
4688255332Scy				fin.fin_p = nat->nat_pr[0];
4689255332Scy				fin.fin_data[0] = ntohs(nat->nat_ndport);
4690255332Scy				fin.fin_data[1] = ntohs(nat->nat_nsport);
4691255332Scy				if (ipf_nat_inlookup(&fin, np->nl_flags,
4692255332Scy						     fin.fin_p, nat->nat_ndstip,
4693255332Scy						     nat->nat_nsrcip) != NULL) {
4694145522Sdarrenr					np->nl_flags &= ~IPN_FINDFORWARD;
4695145522Sdarrenr				}
4696145522Sdarrenr			}
4697145522Sdarrenr
4698315079Scy			np->nl_realip = nat->nat_odstip;
4699315079Scy			np->nl_realport = nat->nat_odport;
4700145522Sdarrenr		}
4701145522Sdarrenr 	}
4702145522Sdarrenr
470353642Sguido	return nat;
470453642Sguido}
470553642Sguido
470653642Sguido
4707145522Sdarrenr/* ------------------------------------------------------------------------ */
4708255332Scy/* Function:    ipf_nat_match                                               */
4709145522Sdarrenr/* Returns:     int - 0 == no match, 1 == match                             */
4710145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4711145522Sdarrenr/*              np(I)    - pointer to NAT rule                              */
4712145522Sdarrenr/*                                                                          */
4713145522Sdarrenr/* Pull the matching of a packet against a NAT rule out of that complex     */
4714255332Scy/* loop inside ipf_nat_checkin() and lay it out properly in its own function. */
4715145522Sdarrenr/* ------------------------------------------------------------------------ */
4716255332Scystatic int
4717255332Scyipf_nat_match(fin, np)
4718255332Scy	fr_info_t *fin;
4719255332Scy	ipnat_t *np;
472060852Sdarrenr{
4721255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
472260852Sdarrenr	frtuc_t *ft;
4723255332Scy	int match;
472460852Sdarrenr
4725255332Scy	match = 0;
4726255332Scy	switch (np->in_osrcatype)
4727255332Scy	{
4728255332Scy	case FRI_NORMAL :
4729255332Scy		match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr);
4730255332Scy		break;
4731255332Scy	case FRI_LOOKUP :
4732255332Scy		match = (*np->in_osrcfunc)(softc, np->in_osrcptr,
4733255332Scy					   4, &fin->fin_saddr, fin->fin_plen);
4734255332Scy		break;
4735255332Scy	}
4736255332Scy	match ^= ((np->in_flags & IPN_NOTSRC) != 0);
4737255332Scy	if (match)
473860852Sdarrenr		return 0;
473960852Sdarrenr
4740255332Scy	match = 0;
4741255332Scy	switch (np->in_odstatype)
4742255332Scy	{
4743255332Scy	case FRI_NORMAL :
4744255332Scy		match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr);
4745255332Scy		break;
4746255332Scy	case FRI_LOOKUP :
4747255332Scy		match = (*np->in_odstfunc)(softc, np->in_odstptr,
4748255332Scy					   4, &fin->fin_daddr, fin->fin_plen);
4749255332Scy		break;
4750255332Scy	}
4751255332Scy
4752255332Scy	match ^= ((np->in_flags & IPN_NOTDST) != 0);
4753255332Scy	if (match)
475460852Sdarrenr		return 0;
4755145522Sdarrenr
475660852Sdarrenr	ft = &np->in_tuc;
4757145522Sdarrenr	if (!(fin->fin_flx & FI_TCPUDP) ||
4758145522Sdarrenr	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
475960852Sdarrenr		if (ft->ftu_scmp || ft->ftu_dcmp)
476060852Sdarrenr			return 0;
476160852Sdarrenr		return 1;
476260852Sdarrenr	}
476360852Sdarrenr
4764255332Scy	return ipf_tcpudpchk(&fin->fin_fi, ft);
476560852Sdarrenr}
476660852Sdarrenr
476760852Sdarrenr
4768145522Sdarrenr/* ------------------------------------------------------------------------ */
4769255332Scy/* Function:    ipf_nat_update                                              */
4770145522Sdarrenr/* Returns:     Nil                                                         */
4771255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
4772255332Scy/*              nat(I) - pointer to NAT structure                           */
4773145522Sdarrenr/*                                                                          */
4774145522Sdarrenr/* Updates the lifetime of a NAT table entry for non-TCP packets.  Must be  */
4775255332Scy/* called with fin_rev updated - i.e. after calling ipf_nat_proto().        */
4776255332Scy/*                                                                          */
4777255332Scy/* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to     */
4778255332Scy/* already be set.                                                          */
4779145522Sdarrenr/* ------------------------------------------------------------------------ */
4780255332Scyvoid
4781255332Scyipf_nat_update(fin, nat)
4782255332Scy	fr_info_t *fin;
4783255332Scy	nat_t *nat;
478453642Sguido{
4785255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
4786255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
4787145522Sdarrenr	ipftq_t *ifq, *ifq2;
4788145522Sdarrenr	ipftqent_t *tqe;
4789255332Scy	ipnat_t *np = nat->nat_ptr;
4790145522Sdarrenr
4791145522Sdarrenr	tqe = &nat->nat_tqe;
4792145522Sdarrenr	ifq = tqe->tqe_ifq;
4793145522Sdarrenr
4794145522Sdarrenr	/*
4795145522Sdarrenr	 * We allow over-riding of NAT timeouts from NAT rules, even for
4796145522Sdarrenr	 * TCP, however, if it is TCP and there is no rule timeout set,
4797145522Sdarrenr	 * then do not update the timeout here.
4798145522Sdarrenr	 */
4799255332Scy	if (np != NULL) {
4800255332Scy		np->in_bytes[fin->fin_rev] += fin->fin_plen;
4801145522Sdarrenr		ifq2 = np->in_tqehead[fin->fin_rev];
4802255332Scy	} else {
4803145522Sdarrenr		ifq2 = NULL;
4804255332Scy	}
4805145522Sdarrenr
4806255332Scy	if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) {
4807255332Scy		(void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq,
4808255332Scy				   0, 2);
4809145522Sdarrenr	} else {
4810145522Sdarrenr		if (ifq2 == NULL) {
4811255332Scy			if (nat->nat_pr[0] == IPPROTO_UDP)
4812255332Scy				ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq :
4813255332Scy						      &softn->ipf_nat_udptq;
4814255332Scy			else if (nat->nat_pr[0] == IPPROTO_ICMP ||
4815255332Scy				 nat->nat_pr[0] == IPPROTO_ICMPV6)
4816255332Scy				ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq:
4817255332Scy						      &softn->ipf_nat_icmptq;
4818145522Sdarrenr			else
4819255332Scy				ifq2 = &softn->ipf_nat_iptq;
4820145522Sdarrenr		}
4821145522Sdarrenr
4822255332Scy		ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2);
4823145522Sdarrenr	}
4824145522Sdarrenr}
4825145522Sdarrenr
4826145522Sdarrenr
4827145522Sdarrenr/* ------------------------------------------------------------------------ */
4828255332Scy/* Function:    ipf_nat_checkout                                            */
4829145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
4830145522Sdarrenr/*                     0 == no packet translation occurred,                 */
4831145522Sdarrenr/*                     1 == packet was successfully translated.             */
4832145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4833145522Sdarrenr/*              passp(I) - pointer to filtering result flags                */
4834145522Sdarrenr/*                                                                          */
4835145522Sdarrenr/* Check to see if an outcoming packet should be changed.  ICMP packets are */
4836145522Sdarrenr/* first checked to see if they match an existing entry (if an error),      */
4837145522Sdarrenr/* otherwise a search of the current NAT table is made.  If neither results */
4838145522Sdarrenr/* in a match then a search for a matching NAT rule is made.  Create a new  */
4839145522Sdarrenr/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
4840145522Sdarrenr/* packet header(s) as required.                                            */
4841145522Sdarrenr/* ------------------------------------------------------------------------ */
4842255332Scyint
4843255332Scyipf_nat_checkout(fin, passp)
4844255332Scy	fr_info_t *fin;
4845255332Scy	u_32_t *passp;
4846145522Sdarrenr{
4847255332Scy	ipnat_t *np = NULL, *npnext;
4848145522Sdarrenr	struct ifnet *ifp, *sifp;
4849255332Scy	ipf_main_softc_t *softc;
4850255332Scy	ipf_nat_softc_t *softn;
4851145522Sdarrenr	icmphdr_t *icmp = NULL;
485253642Sguido	tcphdr_t *tcp = NULL;
4853145522Sdarrenr	int rval, natfailed;
4854145522Sdarrenr	u_int nflags = 0;
4855145522Sdarrenr	u_32_t ipa, iph;
4856145522Sdarrenr	int natadd = 1;
485753642Sguido	frentry_t *fr;
485853642Sguido	nat_t *nat;
485953642Sguido
4860255332Scy	if (fin->fin_v == 6) {
4861255332Scy#ifdef USE_INET6
4862255332Scy		return ipf_nat6_checkout(fin, passp);
4863255332Scy#else
486453642Sguido		return 0;
4865255332Scy#endif
4866255332Scy	}
486753642Sguido
4868255332Scy	softc = fin->fin_main_soft;
4869255332Scy	softn = softc->ipf_nat_soft;
4870255332Scy
4871255332Scy	if (softn->ipf_nat_lock != 0)
4872255332Scy		return 0;
4873255332Scy	if (softn->ipf_nat_stats.ns_rules == 0 &&
4874255332Scy	    softn->ipf_nat_instances == NULL)
4875255332Scy		return 0;
4876255332Scy
4877145522Sdarrenr	natfailed = 0;
4878145522Sdarrenr	fr = fin->fin_fr;
4879145522Sdarrenr	sifp = fin->fin_ifp;
4880170268Sdarrenr	if (fr != NULL) {
4881255332Scy		ifp = fr->fr_tifs[fin->fin_rev].fd_ptr;
4882170268Sdarrenr		if ((ifp != NULL) && (ifp != (void *)-1))
4883170268Sdarrenr			fin->fin_ifp = ifp;
4884170268Sdarrenr	}
488592685Sdarrenr	ifp = fin->fin_ifp;
488653642Sguido
4887145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
4888145522Sdarrenr		switch (fin->fin_p)
4889145522Sdarrenr		{
4890145522Sdarrenr		case IPPROTO_TCP :
489153642Sguido			nflags = IPN_TCP;
4892145522Sdarrenr			break;
4893145522Sdarrenr		case IPPROTO_UDP :
489453642Sguido			nflags = IPN_UDP;
4895145522Sdarrenr			break;
4896145522Sdarrenr		case IPPROTO_ICMP :
4897145522Sdarrenr			icmp = fin->fin_dp;
4898145522Sdarrenr
4899145522Sdarrenr			/*
4900145522Sdarrenr			 * This is an incoming packet, so the destination is
4901145522Sdarrenr			 * the icmp_id and the source port equals 0
4902145522Sdarrenr			 */
4903255332Scy			if ((fin->fin_flx & FI_ICMPQUERY) != 0)
4904145522Sdarrenr				nflags = IPN_ICMPQUERY;
4905145522Sdarrenr			break;
4906145522Sdarrenr		default :
4907145522Sdarrenr			break;
490853642Sguido		}
4909255332Scy
4910145522Sdarrenr		if ((nflags & IPN_TCPUDP))
4911145522Sdarrenr			tcp = fin->fin_dp;
491253642Sguido	}
491353642Sguido
491492685Sdarrenr	ipa = fin->fin_saddr;
491553642Sguido
4916255332Scy	READ_ENTER(&softc->ipf_nat);
491760852Sdarrenr
4918255332Scy	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
4919255332Scy	    (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND)))
4920145522Sdarrenr		/*EMPTY*/;
4921255332Scy	else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
492253642Sguido		natadd = 0;
4923255332Scy	else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH,
4924255332Scy				      (u_int)fin->fin_p, fin->fin_src,
4925255332Scy				      fin->fin_dst))) {
492653642Sguido		nflags = nat->nat_flags;
4927255332Scy	} else if (fin->fin_off == 0) {
4928255332Scy		u_32_t hv, msk, nmsk = 0;
492992685Sdarrenr
493053642Sguido		/*
493153642Sguido		 * If there is no current entry in the nat table for this IP#,
493253642Sguido		 * create one for it (if there is a matching rule).
493353642Sguido		 */
493453642Sguidomaskloop:
4935255332Scy		msk = softn->ipf_nat_map_active_masks[nmsk];
4936255332Scy		iph = ipa & msk;
4937255332Scy		hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz);
4938255332Scyretry_roundrobin:
4939255332Scy		for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) {
4940255332Scy			npnext = np->in_mnext;
4941161356Sguido			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
494260852Sdarrenr				continue;
4943255332Scy			if (np->in_v[0] != 4)
494460852Sdarrenr				continue;
4945255332Scy			if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p))
4946145522Sdarrenr				continue;
4947255332Scy			if ((np->in_flags & IPN_RF) &&
4948255332Scy			    !(np->in_flags & nflags))
4949145522Sdarrenr				continue;
495060852Sdarrenr			if (np->in_flags & IPN_FILTER) {
4951255332Scy				switch (ipf_nat_match(fin, np))
4952255332Scy				{
4953255332Scy				case 0 :
495460852Sdarrenr					continue;
4955255332Scy				case -1 :
4956337948Scy					rval = -3;
4957255332Scy					goto outmatchfail;
4958255332Scy				case 1 :
4959255332Scy				default :
4960255332Scy					break;
4961255332Scy				}
4962255332Scy			} else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr)
496360852Sdarrenr				continue;
4964145522Sdarrenr
4965145522Sdarrenr			if ((fr != NULL) &&
4966255332Scy			    !ipf_matchtag(&np->in_tag, &fr->fr_nattag))
496792685Sdarrenr				continue;
4968145522Sdarrenr
4969255332Scy			if (np->in_plabel != -1) {
4970145522Sdarrenr				if (((np->in_flags & IPN_FILTER) == 0) &&
4971255332Scy				    (np->in_odport != fin->fin_data[1]))
4972145522Sdarrenr					continue;
4973255332Scy				if (ipf_proxy_ok(fin, tcp, np) == 0)
4974145522Sdarrenr					continue;
4975145522Sdarrenr			}
4976145522Sdarrenr
4977255332Scy			if (np->in_flags & IPN_NO) {
497892685Sdarrenr				np->in_hits++;
497992685Sdarrenr				break;
4980145522Sdarrenr			}
4981255332Scy			MUTEX_ENTER(&softn->ipf_nat_new);
4982255332Scy			/*
4983255332Scy			 * If we've matched a round-robin rule but it has
4984255332Scy			 * moved in the list since we got it, start over as
4985255332Scy			 * this is now no longer correct.
4986255332Scy			 */
4987255332Scy			if (npnext != np->in_mnext) {
4988255332Scy				if ((np->in_flags & IPN_ROUNDR) != 0) {
4989255332Scy					MUTEX_EXIT(&softn->ipf_nat_new);
4990255332Scy					goto retry_roundrobin;
4991255332Scy				}
4992255332Scy				npnext = np->in_mnext;
4993145522Sdarrenr			}
4994255332Scy
4995255332Scy			nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND);
4996255332Scy			MUTEX_EXIT(&softn->ipf_nat_new);
4997255332Scy			if (nat != NULL) {
4998255332Scy				natfailed = 0;
4999255332Scy				break;
5000255332Scy			}
5001337948Scy			natfailed = -2;
500253642Sguido		}
5003255332Scy		if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) {
5004255332Scy			nmsk++;
5005255332Scy			goto maskloop;
5006255332Scy		}
500753642Sguido	}
500853642Sguido
5009145522Sdarrenr	if (nat != NULL) {
5010255332Scy		rval = ipf_nat_out(fin, nat, natadd, nflags);
5011145522Sdarrenr		if (rval == 1) {
5012145522Sdarrenr			MUTEX_ENTER(&nat->nat_lock);
5013255332Scy			ipf_nat_update(fin, nat);
5014255332Scy			nat->nat_bytes[1] += fin->fin_plen;
5015255332Scy			nat->nat_pkts[1]++;
5016255332Scy			fin->fin_pktnum = nat->nat_pkts[1];
5017145522Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
5018145522Sdarrenr		}
5019145522Sdarrenr	} else
5020145522Sdarrenr		rval = natfailed;
5021255332Scyoutmatchfail:
5022255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
5023145522Sdarrenr
5024255332Scy	switch (rval)
5025255332Scy	{
5026337948Scy	case -3 :
5027337948Scy		/* ipf_nat_match() failure */
5028337948Scy		/* FALLTHROUGH */
5029337948Scy	case -2 :
5030337948Scy		/* retry_roundrobin loop failure */
5031337948Scy		/* FALLTHROUGH */
5032255332Scy	case -1 :
5033337948Scy		/* proxy failure detected by ipf_nat_out() */
5034255332Scy		if (passp != NULL) {
5035337948Scy			DT2(frb_natv4out, fr_info_t *, fin, int, rval);
5036255332Scy			NBUMPSIDED(1, ns_drop);
5037145522Sdarrenr			*passp = FR_BLOCK;
5038255332Scy			fin->fin_reason = FRB_NATV4;
5039255332Scy		}
5040145522Sdarrenr		fin->fin_flx |= FI_BADNAT;
5041255332Scy		NBUMPSIDED(1, ns_badnat);
5042337948Scy		rval = -1;	/* We only return -1 on error. */
5043255332Scy		break;
5044255332Scy	case 0 :
5045255332Scy		NBUMPSIDE(1, ns_ignored);
5046255332Scy		break;
5047255332Scy	case 1 :
5048255332Scy		NBUMPSIDE(1, ns_translated);
5049255332Scy		break;
5050145522Sdarrenr	}
5051145522Sdarrenr	fin->fin_ifp = sifp;
5052145522Sdarrenr	return rval;
5053145522Sdarrenr}
5054145522Sdarrenr
5055145522Sdarrenr/* ------------------------------------------------------------------------ */
5056255332Scy/* Function:    ipf_nat_out                                                 */
5057145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
5058145522Sdarrenr/*                     1 == packet was successfully translated.             */
5059145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
5060145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
5061145522Sdarrenr/*              natadd(I) - flag indicating if it is safe to add frag cache */
5062145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
5063145522Sdarrenr/*                                                                          */
5064145522Sdarrenr/* Translate a packet coming "out" on an interface.                         */
5065145522Sdarrenr/* ------------------------------------------------------------------------ */
5066255332Scyint
5067255332Scyipf_nat_out(fin, nat, natadd, nflags)
5068255332Scy	fr_info_t *fin;
5069255332Scy	nat_t *nat;
5070255332Scy	int natadd;
5071255332Scy	u_32_t nflags;
5072145522Sdarrenr{
5073255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
5074255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5075145522Sdarrenr	icmphdr_t *icmp;
5076145522Sdarrenr	tcphdr_t *tcp;
5077145522Sdarrenr	ipnat_t *np;
5078255332Scy	int skip;
5079145522Sdarrenr	int i;
5080145522Sdarrenr
5081145522Sdarrenr	tcp = NULL;
5082145522Sdarrenr	icmp = NULL;
5083145522Sdarrenr	np = nat->nat_ptr;
5084145522Sdarrenr
5085145522Sdarrenr	if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL))
5086255332Scy		(void) ipf_frag_natnew(softc, fin, 0, nat);
5087145522Sdarrenr
508872006Sdarrenr	/*
5089145522Sdarrenr	 * Fix up checksums, not by recalculating them, but
5090145522Sdarrenr	 * simply computing adjustments.
5091145522Sdarrenr	 * This is only done for STREAMS based IP implementations where the
5092145522Sdarrenr	 * checksum has already been calculated by IP.  In all other cases,
5093145522Sdarrenr	 * IPFilter is called before the checksum needs calculating so there
5094145522Sdarrenr	 * is no call to modify whatever is in the header now.
509572006Sdarrenr	 */
5096255332Scy	if (nflags == IPN_ICMPERR) {
5097255332Scy		u_32_t s1, s2, sumd, msumd;
509863523Sdarrenr
5099255332Scy		s1 = LONG_SUM(ntohl(fin->fin_saddr));
5100255332Scy		if (nat->nat_dir == NAT_OUTBOUND) {
5101255332Scy			s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
5102255332Scy		} else {
5103255332Scy			s2 = LONG_SUM(ntohl(nat->nat_odstaddr));
510463523Sdarrenr		}
5105255332Scy		CALC_SUMD(s1, s2, sumd);
5106255332Scy		msumd = sumd;
5107255332Scy
5108255332Scy		s1 = LONG_SUM(ntohl(fin->fin_daddr));
5109255332Scy		if (nat->nat_dir == NAT_OUTBOUND) {
5110255332Scy			s2 = LONG_SUM(ntohl(nat->nat_ndstaddr));
5111255332Scy		} else {
5112255332Scy			s2 = LONG_SUM(ntohl(nat->nat_osrcaddr));
5113255332Scy		}
5114255332Scy		CALC_SUMD(s1, s2, sumd);
5115255332Scy		msumd += sumd;
5116255332Scy
5117255332Scy		ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0);
5118255332Scy	}
5119369272Scy#if !defined(_KERNEL) || SOLARIS || \
5120366531Scy    defined(BRIDGE_IPF) || defined(__FreeBSD__)
5121255332Scy	else {
5122255332Scy		/*
5123255332Scy		 * Strictly speaking, this isn't necessary on BSD
5124255332Scy		 * kernels because they do checksum calculation after
5125255332Scy		 * this code has run BUT if ipfilter is being used
5126255332Scy		 * to do NAT as a bridge, that code doesn't exist.
5127255332Scy		 */
5128255332Scy		switch (nat->nat_dir)
5129255332Scy		{
5130255332Scy		case NAT_OUTBOUND :
5131255332Scy			ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART,
5132255332Scy					 &fin->fin_ip->ip_sum,
5133255332Scy					 nat->nat_ipsumd, 0);
5134255332Scy			break;
5135255332Scy
5136255332Scy		case NAT_INBOUND :
5137255332Scy			ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART,
5138255332Scy					&fin->fin_ip->ip_sum,
5139255332Scy					nat->nat_ipsumd, 0);
5140255332Scy			break;
5141255332Scy
5142255332Scy		default :
5143255332Scy			break;
514463523Sdarrenr		}
5145255332Scy	}
514653642Sguido#endif
5147255332Scy
5148255332Scy	/*
5149255332Scy	 * Address assignment is after the checksum modification because
5150255332Scy	 * we are using the address in the packet for determining the
5151255332Scy	 * correct checksum offset (the ICMP error could be coming from
5152255332Scy	 * anyone...)
5153255332Scy	 */
5154255332Scy	switch (nat->nat_dir)
5155255332Scy	{
5156255332Scy	case NAT_OUTBOUND :
5157255332Scy		fin->fin_ip->ip_src = nat->nat_nsrcip;
5158255332Scy		fin->fin_saddr = nat->nat_nsrcaddr;
5159255332Scy		fin->fin_ip->ip_dst = nat->nat_ndstip;
5160255332Scy		fin->fin_daddr = nat->nat_ndstaddr;
5161255332Scy		break;
5162255332Scy
5163255332Scy	case NAT_INBOUND :
5164255332Scy		fin->fin_ip->ip_src = nat->nat_odstip;
5165255332Scy		fin->fin_saddr = nat->nat_ndstaddr;
5166255332Scy		fin->fin_ip->ip_dst = nat->nat_osrcip;
5167255332Scy		fin->fin_daddr = nat->nat_nsrcaddr;
5168255332Scy		break;
5169255332Scy
5170255332Scy	case NAT_DIVERTIN :
5171255332Scy	    {
5172255332Scy		mb_t *m;
5173255332Scy
5174255332Scy		skip = ipf_nat_decap(fin, nat);
5175255332Scy		if (skip <= 0) {
5176255332Scy			NBUMPSIDED(1, ns_decap_fail);
5177255332Scy			return -1;
5178255332Scy		}
5179255332Scy
5180255332Scy		m = fin->fin_m;
5181255332Scy
5182369272Scy#if SOLARIS && defined(_KERNEL)
5183255332Scy		m->b_rptr += skip;
5184255332Scy#else
5185255332Scy		m->m_data += skip;
5186255332Scy		m->m_len -= skip;
5187255332Scy
5188255332Scy# ifdef M_PKTHDR
5189255332Scy		if (m->m_flags & M_PKTHDR)
5190255332Scy			m->m_pkthdr.len -= skip;
5191255332Scy# endif
5192255332Scy#endif
5193255332Scy
5194255332Scy		MUTEX_ENTER(&nat->nat_lock);
5195255332Scy		ipf_nat_update(fin, nat);
5196255332Scy		MUTEX_EXIT(&nat->nat_lock);
5197255332Scy		fin->fin_flx |= FI_NATED;
5198255332Scy		if (np != NULL && np->in_tag.ipt_num[0] != 0)
5199255332Scy			fin->fin_nattag = &np->in_tag;
5200255332Scy		return 1;
5201255332Scy		/* NOTREACHED */
5202255332Scy	    }
5203255332Scy
5204255332Scy	case NAT_DIVERTOUT :
5205255332Scy	    {
5206255332Scy		u_32_t s1, s2, sumd;
5207255332Scy		udphdr_t *uh;
5208255332Scy		ip_t *ip;
5209255332Scy		mb_t *m;
5210255332Scy
5211255332Scy		m = M_DUP(np->in_divmp);
5212255332Scy		if (m == NULL) {
5213255332Scy			NBUMPSIDED(1, ns_divert_dup);
5214255332Scy			return -1;
5215255332Scy		}
5216255332Scy
5217255332Scy		ip = MTOD(m, ip_t *);
5218280971Sglebius		ip_fillid(ip);
5219255332Scy		s2 = ntohs(ip->ip_id);
5220255332Scy
5221255332Scy		s1 = ip->ip_len;
5222255332Scy		ip->ip_len = ntohs(ip->ip_len);
5223255332Scy		ip->ip_len += fin->fin_plen;
5224255332Scy		ip->ip_len = htons(ip->ip_len);
5225255332Scy		s2 += ntohs(ip->ip_len);
5226255332Scy		CALC_SUMD(s1, s2, sumd);
5227255332Scy
5228255332Scy		uh = (udphdr_t *)(ip + 1);
5229255332Scy		uh->uh_ulen += fin->fin_plen;
5230255332Scy		uh->uh_ulen = htons(uh->uh_ulen);
5231369272Scy#if !defined(_KERNEL) || SOLARIS || \
5232344833Scy    defined(BRIDGE_IPF) || defined(__FreeBSD__)
5233255332Scy		ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0);
5234255332Scy#endif
5235255332Scy
5236255332Scy		PREP_MB_T(fin, m);
5237255332Scy
5238255332Scy		fin->fin_src = ip->ip_src;
5239255332Scy		fin->fin_dst = ip->ip_dst;
5240255332Scy		fin->fin_ip = ip;
5241255332Scy		fin->fin_plen += sizeof(ip_t) + 8;	/* UDP + IPv4 hdr */
5242255332Scy		fin->fin_dlen += sizeof(ip_t) + 8;	/* UDP + IPv4 hdr */
5243255332Scy
5244255332Scy		nflags &= ~IPN_TCPUDPICMP;
5245255332Scy
5246255332Scy		break;
5247255332Scy	    }
5248255332Scy
5249255332Scy	default :
5250255332Scy		break;
5251145522Sdarrenr	}
525253642Sguido
5253145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
5254255332Scy		u_short *csump;
5255255332Scy
5256255332Scy		if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) {
5257145522Sdarrenr			tcp = fin->fin_dp;
525853642Sguido
5259255332Scy			switch (nat->nat_dir)
5260255332Scy			{
5261255332Scy			case NAT_OUTBOUND :
5262255332Scy				tcp->th_sport = nat->nat_nsport;
5263255332Scy				fin->fin_data[0] = ntohs(nat->nat_nsport);
5264255332Scy				tcp->th_dport = nat->nat_ndport;
5265255332Scy				fin->fin_data[1] = ntohs(nat->nat_ndport);
5266255332Scy				break;
5267255332Scy
5268255332Scy			case NAT_INBOUND :
5269255332Scy				tcp->th_sport = nat->nat_odport;
5270255332Scy				fin->fin_data[0] = ntohs(nat->nat_odport);
5271255332Scy				tcp->th_dport = nat->nat_osport;
5272255332Scy				fin->fin_data[1] = ntohs(nat->nat_osport);
5273255332Scy				break;
5274255332Scy			}
5275145522Sdarrenr		}
527653642Sguido
5277255332Scy		if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) {
5278145522Sdarrenr			icmp = fin->fin_dp;
5279255332Scy			icmp->icmp_id = nat->nat_nicmpid;
5280145522Sdarrenr		}
5281110916Sdarrenr
5282255332Scy		csump = ipf_nat_proto(fin, nat, nflags);
5283255332Scy
5284255332Scy		/*
5285255332Scy		 * The above comments do not hold for layer 4 (or higher)
5286255332Scy		 * checksums...
5287255332Scy		 */
5288255332Scy		if (csump != NULL) {
5289255332Scy			if (nat->nat_dir == NAT_OUTBOUND)
5290255332Scy				ipf_fix_outcksum(fin->fin_cksum, csump,
5291255332Scy						 nat->nat_sumd[0],
5292255332Scy						 nat->nat_sumd[1] +
5293255332Scy						 fin->fin_dlen);
5294255332Scy			else
5295255332Scy				ipf_fix_incksum(fin->fin_cksum, csump,
5296255332Scy						nat->nat_sumd[0],
5297255332Scy						nat->nat_sumd[1] +
5298255332Scy						fin->fin_dlen);
5299255332Scy		}
5300145522Sdarrenr	}
5301110916Sdarrenr
5302255332Scy	ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
5303145522Sdarrenr	/* ------------------------------------------------------------- */
5304255332Scy	/* A few quick notes:                                            */
5305255332Scy	/*      Following are test conditions prior to calling the       */
5306255332Scy	/*      ipf_proxy_check routine.                                 */
5307255332Scy	/*                                                               */
5308255332Scy	/*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
5309255332Scy	/*      with a redirect rule, we attempt to match the packet's   */
5310255332Scy	/*      source port against in_dport, otherwise we'd compare the */
5311255332Scy	/*      packet's destination.                                    */
5312145522Sdarrenr	/* ------------------------------------------------------------- */
5313145522Sdarrenr	if ((np != NULL) && (np->in_apr != NULL)) {
5314255332Scy		i = ipf_proxy_check(fin, nat);
5315369541Sgit2svn		if (i == -1) {
5316255332Scy			NBUMPSIDED(1, ns_ipf_proxy_fail);
5317255332Scy		}
5318255332Scy	} else {
5319145522Sdarrenr		i = 1;
5320255332Scy	}
5321145522Sdarrenr	fin->fin_flx |= FI_NATED;
5322145522Sdarrenr	return i;
532353642Sguido}
532453642Sguido
532553642Sguido
5326145522Sdarrenr/* ------------------------------------------------------------------------ */
5327255332Scy/* Function:    ipf_nat_checkin                                             */
5328145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
5329145522Sdarrenr/*                     0 == no packet translation occurred,                 */
5330145522Sdarrenr/*                     1 == packet was successfully translated.             */
5331145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
5332145522Sdarrenr/*              passp(I) - pointer to filtering result flags                */
5333145522Sdarrenr/*                                                                          */
5334145522Sdarrenr/* Check to see if an incoming packet should be changed.  ICMP packets are  */
5335145522Sdarrenr/* first checked to see if they match an existing entry (if an error),      */
5336145522Sdarrenr/* otherwise a search of the current NAT table is made.  If neither results */
5337145522Sdarrenr/* in a match then a search for a matching NAT rule is made.  Create a new  */
5338145522Sdarrenr/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
5339145522Sdarrenr/* packet header(s) as required.                                            */
5340145522Sdarrenr/* ------------------------------------------------------------------------ */
5341255332Scyint
5342255332Scyipf_nat_checkin(fin, passp)
5343255332Scy	fr_info_t *fin;
5344255332Scy	u_32_t *passp;
534553642Sguido{
5346255332Scy	ipf_main_softc_t *softc;
5347255332Scy	ipf_nat_softc_t *softn;
5348145522Sdarrenr	u_int nflags, natadd;
5349255332Scy	ipnat_t *np, *npnext;
5350145522Sdarrenr	int rval, natfailed;
5351145522Sdarrenr	struct ifnet *ifp;
5352145522Sdarrenr	struct in_addr in;
5353145522Sdarrenr	icmphdr_t *icmp;
5354145522Sdarrenr	tcphdr_t *tcp;
5355145522Sdarrenr	u_short dport;
535653642Sguido	nat_t *nat;
535753642Sguido	u_32_t iph;
535853642Sguido
5359255332Scy	softc = fin->fin_main_soft;
5360255332Scy	softn = softc->ipf_nat_soft;
5361255332Scy
5362255332Scy	if (softn->ipf_nat_lock != 0)
536353642Sguido		return 0;
5364255332Scy	if (softn->ipf_nat_stats.ns_rules == 0 &&
5365255332Scy	    softn->ipf_nat_instances == NULL)
5366255332Scy		return 0;
536753642Sguido
5368145522Sdarrenr	tcp = NULL;
5369145522Sdarrenr	icmp = NULL;
5370145522Sdarrenr	dport = 0;
5371145522Sdarrenr	natadd = 1;
5372145522Sdarrenr	nflags = 0;
5373145522Sdarrenr	natfailed = 0;
5374145522Sdarrenr	ifp = fin->fin_ifp;
5375145522Sdarrenr
5376145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
5377145522Sdarrenr		switch (fin->fin_p)
5378145522Sdarrenr		{
5379145522Sdarrenr		case IPPROTO_TCP :
538053642Sguido			nflags = IPN_TCP;
5381145522Sdarrenr			break;
5382145522Sdarrenr		case IPPROTO_UDP :
538353642Sguido			nflags = IPN_UDP;
5384145522Sdarrenr			break;
5385145522Sdarrenr		case IPPROTO_ICMP :
5386145522Sdarrenr			icmp = fin->fin_dp;
5387145522Sdarrenr
5388145522Sdarrenr			/*
5389145522Sdarrenr			 * This is an incoming packet, so the destination is
5390145522Sdarrenr			 * the icmp_id and the source port equals 0
5391145522Sdarrenr			 */
5392255332Scy			if ((fin->fin_flx & FI_ICMPQUERY) != 0) {
5393145522Sdarrenr				nflags = IPN_ICMPQUERY;
5394255332Scy				dport = icmp->icmp_id;
5395145522Sdarrenr			} break;
5396145522Sdarrenr		default :
5397145522Sdarrenr			break;
5398145522Sdarrenr		}
5399255332Scy
540053642Sguido		if ((nflags & IPN_TCPUDP)) {
5401145522Sdarrenr			tcp = fin->fin_dp;
5402255332Scy			dport = fin->fin_data[1];
540353642Sguido		}
540453642Sguido	}
540553642Sguido
540692685Sdarrenr	in = fin->fin_dst;
540753642Sguido
5408255332Scy	READ_ENTER(&softc->ipf_nat);
540953642Sguido
5410255332Scy	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
5411255332Scy	    (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND)))
5412145522Sdarrenr		/*EMPTY*/;
5413255332Scy	else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
541453642Sguido		natadd = 0;
5415255332Scy	else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH,
5416255332Scy					 (u_int)fin->fin_p,
5417255332Scy					 fin->fin_src, in))) {
541853642Sguido		nflags = nat->nat_flags;
5419255332Scy	} else if (fin->fin_off == 0) {
5420255332Scy		u_32_t hv, msk, rmsk = 0;
5421145522Sdarrenr
542253642Sguido		/*
542353642Sguido		 * If there is no current entry in the nat table for this IP#,
542453642Sguido		 * create one for it (if there is a matching rule).
542553642Sguido		 */
542653642Sguidomaskloop:
5427255332Scy		msk = softn->ipf_nat_rdr_active_masks[rmsk];
5428255332Scy		iph = in.s_addr & msk;
5429255332Scy		hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz);
5430255332Scyretry_roundrobin:
5431255332Scy		/* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */
5432255332Scy		for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) {
5433255332Scy			npnext = np->in_rnext;
5434145522Sdarrenr			if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
543560852Sdarrenr				continue;
5436255332Scy			if (np->in_v[0] != 4)
5437138947Sdarrenr				continue;
5438255332Scy			if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p))
5439145522Sdarrenr				continue;
5440145522Sdarrenr			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
5441145522Sdarrenr				continue;
544260852Sdarrenr			if (np->in_flags & IPN_FILTER) {
5443255332Scy				switch (ipf_nat_match(fin, np))
5444255332Scy				{
5445255332Scy				case 0 :
544660852Sdarrenr					continue;
5447255332Scy				case -1 :
5448337948Scy					rval = -3;
5449255332Scy					goto inmatchfail;
5450255332Scy				case 1 :
5451255332Scy				default :
5452255332Scy					break;
5453255332Scy				}
5454145522Sdarrenr			} else {
5455255332Scy				if ((in.s_addr & np->in_odstmsk) !=
5456255332Scy				    np->in_odstaddr)
5457145522Sdarrenr					continue;
5458255332Scy				if (np->in_odport &&
5459255332Scy				    ((np->in_dtop < dport) ||
5460255332Scy				     (dport < np->in_odport)))
5461145522Sdarrenr					continue;
5462145522Sdarrenr			}
5463145522Sdarrenr
5464255332Scy			if (np->in_plabel != -1) {
5465255332Scy				if (!ipf_proxy_ok(fin, tcp, np)) {
5466145522Sdarrenr					continue;
546753642Sguido				}
5468145522Sdarrenr			}
5469145522Sdarrenr
5470255332Scy			if (np->in_flags & IPN_NO) {
5471145522Sdarrenr				np->in_hits++;
5472145522Sdarrenr				break;
5473255332Scy			}
547460852Sdarrenr
5475255332Scy			MUTEX_ENTER(&softn->ipf_nat_new);
5476255332Scy			/*
5477255332Scy			 * If we've matched a round-robin rule but it has
5478255332Scy			 * moved in the list since we got it, start over as
5479255332Scy			 * this is now no longer correct.
5480255332Scy			 */
5481255332Scy			if (npnext != np->in_rnext) {
5482255332Scy				if ((np->in_flags & IPN_ROUNDR) != 0) {
5483255332Scy					MUTEX_EXIT(&softn->ipf_nat_new);
5484255332Scy					goto retry_roundrobin;
5485255332Scy				}
5486255332Scy				npnext = np->in_rnext;
5487145522Sdarrenr			}
5488255332Scy
5489255332Scy			nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND);
5490255332Scy			MUTEX_EXIT(&softn->ipf_nat_new);
5491255332Scy			if (nat != NULL) {
5492255332Scy				natfailed = 0;
5493255332Scy				break;
5494145522Sdarrenr			}
5495337948Scy			natfailed = -2;
549653642Sguido		}
5497255332Scy		if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) {
5498255332Scy			rmsk++;
5499255332Scy			goto maskloop;
5500255332Scy		}
550153642Sguido	}
5502255332Scy
5503145522Sdarrenr	if (nat != NULL) {
5504255332Scy		rval = ipf_nat_in(fin, nat, natadd, nflags);
5505145522Sdarrenr		if (rval == 1) {
5506145522Sdarrenr			MUTEX_ENTER(&nat->nat_lock);
5507255332Scy			ipf_nat_update(fin, nat);
5508255332Scy			nat->nat_bytes[0] += fin->fin_plen;
5509255332Scy			nat->nat_pkts[0]++;
5510255332Scy			fin->fin_pktnum = nat->nat_pkts[0];
5511145522Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
5512145522Sdarrenr		}
5513145522Sdarrenr	} else
5514145522Sdarrenr		rval = natfailed;
5515255332Scyinmatchfail:
5516255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
551772006Sdarrenr
5518255332Scy	switch (rval)
5519255332Scy	{
5520337948Scy	case -3 :
5521337948Scy		/* ipf_nat_match() failure */
5522337948Scy		/* FALLTHROUGH */
5523337948Scy	case -2 :
5524337948Scy		/* retry_roundrobin loop failure */
5525337948Scy		/* FALLTHROUGH */
5526255332Scy	case -1 :
5527337948Scy		/* proxy failure detected by ipf_nat_out() */
5528255332Scy		if (passp != NULL) {
5529337948Scy			DT2(frb_natv4in, fr_info_t *, fin, int, rval);
5530255332Scy			NBUMPSIDED(0, ns_drop);
5531145522Sdarrenr			*passp = FR_BLOCK;
5532255332Scy			fin->fin_reason = FRB_NATV4;
5533255332Scy		}
5534145522Sdarrenr		fin->fin_flx |= FI_BADNAT;
5535255332Scy		NBUMPSIDED(0, ns_badnat);
5536337948Scy		rval = -1;	/* We only return -1 on error. */
5537255332Scy		break;
5538255332Scy	case 0 :
5539255332Scy		NBUMPSIDE(0, ns_ignored);
5540255332Scy		break;
5541255332Scy	case 1 :
5542255332Scy		NBUMPSIDE(0, ns_translated);
5543255332Scy		break;
5544145522Sdarrenr	}
5545145522Sdarrenr	return rval;
5546145522Sdarrenr}
5547145522Sdarrenr
5548145522Sdarrenr
5549145522Sdarrenr/* ------------------------------------------------------------------------ */
5550255332Scy/* Function:    ipf_nat_in                                                  */
5551145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
5552145522Sdarrenr/*                     1 == packet was successfully translated.             */
5553145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
5554145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
5555145522Sdarrenr/*              natadd(I) - flag indicating if it is safe to add frag cache */
5556145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
5557255332Scy/* Locks Held:  ipf_nat(READ)                                               */
5558145522Sdarrenr/*                                                                          */
5559145522Sdarrenr/* Translate a packet coming "in" on an interface.                          */
5560145522Sdarrenr/* ------------------------------------------------------------------------ */
5561255332Scyint
5562255332Scyipf_nat_in(fin, nat, natadd, nflags)
5563255332Scy	fr_info_t *fin;
5564255332Scy	nat_t *nat;
5565255332Scy	int natadd;
5566255332Scy	u_32_t nflags;
5567145522Sdarrenr{
5568255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
5569255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5570255332Scy	u_32_t sumd, ipsumd, sum1, sum2;
5571145522Sdarrenr	icmphdr_t *icmp;
5572145522Sdarrenr	tcphdr_t *tcp;
5573145522Sdarrenr	ipnat_t *np;
5574255332Scy	int skip;
5575145522Sdarrenr	int i;
5576145522Sdarrenr
5577145522Sdarrenr	tcp = NULL;
5578145522Sdarrenr	np = nat->nat_ptr;
5579145522Sdarrenr	fin->fin_fr = nat->nat_fr;
5580145522Sdarrenr
5581145522Sdarrenr	if (np != NULL) {
5582145522Sdarrenr		if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
5583255332Scy			(void) ipf_frag_natnew(softc, fin, 0, nat);
5584145522Sdarrenr
5585145522Sdarrenr	/* ------------------------------------------------------------- */
5586255332Scy	/* A few quick notes:                                            */
5587255332Scy	/*      Following are test conditions prior to calling the       */
5588255332Scy	/*      ipf_proxy_check routine.                                 */
5589255332Scy	/*                                                               */
5590255332Scy	/*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
5591255332Scy	/*      with a map rule, we attempt to match the packet's        */
5592255332Scy	/*      source port against in_dport, otherwise we'd compare the */
5593255332Scy	/*      packet's destination.                                    */
5594145522Sdarrenr	/* ------------------------------------------------------------- */
5595145522Sdarrenr		if (np->in_apr != NULL) {
5596255332Scy			i = ipf_proxy_check(fin, nat);
559760852Sdarrenr			if (i == -1) {
5598255332Scy				NBUMPSIDED(0, ns_ipf_proxy_fail);
5599145522Sdarrenr				return -1;
560060852Sdarrenr			}
560160852Sdarrenr		}
5602145522Sdarrenr	}
560353642Sguido
5604255332Scy	ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
5605145522Sdarrenr
5606255332Scy	ipsumd = nat->nat_ipsumd;
5607145522Sdarrenr	/*
5608145522Sdarrenr	 * Fix up checksums, not by recalculating them, but
5609145522Sdarrenr	 * simply computing adjustments.
5610145522Sdarrenr	 * Why only do this for some platforms on inbound packets ?
5611145522Sdarrenr	 * Because for those that it is done, IP processing is yet to happen
5612145522Sdarrenr	 * and so the IPv4 header checksum has not yet been evaluated.
5613145522Sdarrenr	 * Perhaps it should always be done for the benefit of things like
5614145522Sdarrenr	 * fast forwarding (so that it doesn't need to be recomputed) but with
5615145522Sdarrenr	 * header checksum offloading, perhaps it is a moot point.
5616145522Sdarrenr	 */
5617255332Scy
5618255332Scy	switch (nat->nat_dir)
5619255332Scy	{
5620255332Scy	case NAT_INBOUND :
5621255332Scy		if ((fin->fin_flx & FI_ICMPERR) == 0) {
5622255332Scy			fin->fin_ip->ip_src = nat->nat_nsrcip;
5623255332Scy			fin->fin_saddr = nat->nat_nsrcaddr;
5624255332Scy		} else {
5625255332Scy			sum1 = nat->nat_osrcaddr;
5626255332Scy			sum2 = nat->nat_nsrcaddr;
5627255332Scy			CALC_SUMD(sum1, sum2, sumd);
5628255332Scy			ipsumd -= sumd;
5629255332Scy		}
5630255332Scy		fin->fin_ip->ip_dst = nat->nat_ndstip;
5631255332Scy		fin->fin_daddr = nat->nat_ndstaddr;
5632369272Scy#if !defined(_KERNEL) || SOLARIS
5633255332Scy		ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0);
5634145522Sdarrenr#endif
5635255332Scy		break;
5636145522Sdarrenr
5637255332Scy	case NAT_OUTBOUND :
5638255332Scy		if ((fin->fin_flx & FI_ICMPERR) == 0) {
5639255332Scy			fin->fin_ip->ip_src = nat->nat_odstip;
5640255332Scy			fin->fin_saddr = nat->nat_odstaddr;
5641255332Scy		} else {
5642255332Scy			sum1 = nat->nat_odstaddr;
5643255332Scy			sum2 = nat->nat_ndstaddr;
5644255332Scy			CALC_SUMD(sum1, sum2, sumd);
5645255332Scy			ipsumd -= sumd;
5646255332Scy		}
5647255332Scy		fin->fin_ip->ip_dst = nat->nat_osrcip;
5648255332Scy		fin->fin_daddr = nat->nat_osrcaddr;
5649369272Scy#if !defined(_KERNEL) || SOLARIS
5650255332Scy		ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0);
5651255332Scy#endif
5652255332Scy		break;
5653255332Scy
5654255332Scy	case NAT_DIVERTIN :
5655255332Scy	    {
5656255332Scy		udphdr_t *uh;
5657255332Scy		ip_t *ip;
5658255332Scy		mb_t *m;
5659255332Scy
5660255332Scy		m = M_DUP(np->in_divmp);
5661255332Scy		if (m == NULL) {
5662255332Scy			NBUMPSIDED(0, ns_divert_dup);
5663255332Scy			return -1;
5664255332Scy		}
5665255332Scy
5666255332Scy		ip = MTOD(m, ip_t *);
5667280971Sglebius		ip_fillid(ip);
5668255332Scy		sum1 = ntohs(ip->ip_len);
5669255332Scy		ip->ip_len = ntohs(ip->ip_len);
5670255332Scy		ip->ip_len += fin->fin_plen;
5671255332Scy		ip->ip_len = htons(ip->ip_len);
5672255332Scy
5673255332Scy		uh = (udphdr_t *)(ip + 1);
5674255332Scy		uh->uh_ulen += fin->fin_plen;
5675255332Scy		uh->uh_ulen = htons(uh->uh_ulen);
5676255332Scy
5677255332Scy		sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len);
5678255332Scy		sum2 += ntohs(ip->ip_off) & IP_DF;
5679255332Scy		CALC_SUMD(sum1, sum2, sumd);
5680255332Scy
5681369272Scy#if !defined(_KERNEL) || SOLARIS
5682255332Scy		ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0);
5683255332Scy#endif
5684255332Scy		PREP_MB_T(fin, m);
5685255332Scy
5686255332Scy		fin->fin_ip = ip;
5687255332Scy		fin->fin_plen += sizeof(ip_t) + 8;	/* UDP + new IPv4 hdr */
5688255332Scy		fin->fin_dlen += sizeof(ip_t) + 8;	/* UDP + old IPv4 hdr */
5689255332Scy
5690255332Scy		nflags &= ~IPN_TCPUDPICMP;
5691255332Scy
5692255332Scy		break;
5693255332Scy	    }
5694255332Scy
5695255332Scy	case NAT_DIVERTOUT :
5696255332Scy	    {
5697255332Scy		mb_t *m;
5698255332Scy
5699255332Scy		skip = ipf_nat_decap(fin, nat);
5700255332Scy		if (skip <= 0) {
5701255332Scy			NBUMPSIDED(0, ns_decap_fail);
5702255332Scy			return -1;
5703255332Scy		}
5704255332Scy
5705255332Scy		m = fin->fin_m;
5706255332Scy
5707369272Scy#if SOLARIS && defined(_KERNEL)
5708255332Scy		m->b_rptr += skip;
5709255332Scy#else
5710255332Scy		m->m_data += skip;
5711255332Scy		m->m_len -= skip;
5712255332Scy
5713255332Scy# ifdef M_PKTHDR
5714255332Scy		if (m->m_flags & M_PKTHDR)
5715255332Scy			m->m_pkthdr.len -= skip;
5716255332Scy# endif
5717255332Scy#endif
5718255332Scy
5719255332Scy		ipf_nat_update(fin, nat);
5720255332Scy		nflags &= ~IPN_TCPUDPICMP;
5721255332Scy		fin->fin_flx |= FI_NATED;
5722255332Scy		if (np != NULL && np->in_tag.ipt_num[0] != 0)
5723255332Scy			fin->fin_nattag = &np->in_tag;
5724255332Scy		return 1;
5725255332Scy		/* NOTREACHED */
5726255332Scy	    }
5727255332Scy	}
5728255332Scy	if (nflags & IPN_TCPUDP)
5729255332Scy		tcp = fin->fin_dp;
5730255332Scy
5731145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
5732255332Scy		u_short *csump;
5733255332Scy
5734255332Scy		if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) {
5735255332Scy			switch (nat->nat_dir)
5736255332Scy			{
5737255332Scy			case NAT_INBOUND :
5738255332Scy				tcp->th_sport = nat->nat_nsport;
5739255332Scy				fin->fin_data[0] = ntohs(nat->nat_nsport);
5740255332Scy				tcp->th_dport = nat->nat_ndport;
5741255332Scy				fin->fin_data[1] = ntohs(nat->nat_ndport);
5742255332Scy				break;
5743255332Scy
5744255332Scy			case NAT_OUTBOUND :
5745255332Scy				tcp->th_sport = nat->nat_odport;
5746255332Scy				fin->fin_data[0] = ntohs(nat->nat_odport);
5747255332Scy				tcp->th_dport = nat->nat_osport;
5748255332Scy				fin->fin_data[1] = ntohs(nat->nat_osport);
5749255332Scy				break;
5750255332Scy			}
575192685Sdarrenr		}
575253642Sguido
5753145522Sdarrenr
5754255332Scy		if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) {
5755145522Sdarrenr			icmp = fin->fin_dp;
5756145522Sdarrenr
5757255332Scy			icmp->icmp_id = nat->nat_nicmpid;
5758145522Sdarrenr		}
5759145522Sdarrenr
5760255332Scy		csump = ipf_nat_proto(fin, nat, nflags);
5761255332Scy
5762255332Scy		/*
5763255332Scy		 * The above comments do not hold for layer 4 (or higher)
5764255332Scy		 * checksums...
5765255332Scy		 */
5766255332Scy		if (csump != NULL) {
5767255332Scy			if (nat->nat_dir == NAT_OUTBOUND)
5768255332Scy				ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0);
5769255332Scy			else
5770255332Scy				ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0);
5771255332Scy		}
5772145522Sdarrenr	}
5773145522Sdarrenr
5774145522Sdarrenr	fin->fin_flx |= FI_NATED;
5775145522Sdarrenr	if (np != NULL && np->in_tag.ipt_num[0] != 0)
5776145522Sdarrenr		fin->fin_nattag = &np->in_tag;
5777145522Sdarrenr	return 1;
5778145522Sdarrenr}
5779130886Sdarrenr
5780130886Sdarrenr
5781145522Sdarrenr/* ------------------------------------------------------------------------ */
5782255332Scy/* Function:    ipf_nat_proto                                               */
5783145522Sdarrenr/* Returns:     u_short* - pointer to transport header checksum to update,  */
5784145522Sdarrenr/*                         NULL if the transport protocol is not recognised */
5785145522Sdarrenr/*                         as needing a checksum update.                    */
5786145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
5787145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
5788145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
5789145522Sdarrenr/*                                                                          */
5790145522Sdarrenr/* Return the pointer to the checksum field for each protocol so understood.*/
5791145522Sdarrenr/* If support for making other changes to a protocol header is required,    */
5792145522Sdarrenr/* that is not strictly 'address' translation, such as clamping the MSS in  */
5793145522Sdarrenr/* TCP down to a specific value, then do it from here.                      */
5794145522Sdarrenr/* ------------------------------------------------------------------------ */
5795255332Scyu_short *
5796255332Scyipf_nat_proto(fin, nat, nflags)
5797255332Scy	fr_info_t *fin;
5798255332Scy	nat_t *nat;
5799255332Scy	u_int nflags;
5800145522Sdarrenr{
5801145522Sdarrenr	icmphdr_t *icmp;
5802145522Sdarrenr	u_short *csump;
5803145522Sdarrenr	tcphdr_t *tcp;
5804145522Sdarrenr	udphdr_t *udp;
580553642Sguido
5806145522Sdarrenr	csump = NULL;
5807145522Sdarrenr	if (fin->fin_out == 0) {
5808255332Scy		fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND);
5809145522Sdarrenr	} else {
5810255332Scy		fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0);
5811145522Sdarrenr	}
581253642Sguido
5813145522Sdarrenr	switch (fin->fin_p)
5814145522Sdarrenr	{
5815145522Sdarrenr	case IPPROTO_TCP :
5816145522Sdarrenr		tcp = fin->fin_dp;
5817110916Sdarrenr
5818255332Scy		if ((nflags & IPN_TCP) != 0)
5819255332Scy			csump = &tcp->th_sum;
582053642Sguido
5821145522Sdarrenr		/*
5822145522Sdarrenr		 * Do a MSS CLAMPING on a SYN packet,
5823145522Sdarrenr		 * only deal IPv4 for now.
5824145522Sdarrenr		 */
5825145522Sdarrenr		if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0)
5826255332Scy			ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump);
582760852Sdarrenr
5828145522Sdarrenr		break;
5829145522Sdarrenr
5830145522Sdarrenr	case IPPROTO_UDP :
5831145522Sdarrenr		udp = fin->fin_dp;
5832145522Sdarrenr
5833255332Scy		if ((nflags & IPN_UDP) != 0) {
5834255332Scy			if (udp->uh_sum != 0)
5835255332Scy				csump = &udp->uh_sum;
5836255332Scy		}
5837145522Sdarrenr		break;
5838145522Sdarrenr
5839145522Sdarrenr	case IPPROTO_ICMP :
5840145522Sdarrenr		icmp = fin->fin_dp;
5841145522Sdarrenr
5842145522Sdarrenr		if ((nflags & IPN_ICMPQUERY) != 0) {
5843145522Sdarrenr			if (icmp->icmp_cksum != 0)
5844145522Sdarrenr				csump = &icmp->icmp_cksum;
584553642Sguido		}
5846145522Sdarrenr		break;
584753642Sguido
5848255332Scy#ifdef USE_INET6
5849255332Scy	case IPPROTO_ICMPV6 :
5850255332Scy	    {
5851255332Scy		struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp;
585253642Sguido
5853255332Scy		icmp6 = fin->fin_dp;
5854145522Sdarrenr
5855255332Scy		if ((nflags & IPN_ICMPQUERY) != 0) {
5856255332Scy			if (icmp6->icmp6_cksum != 0)
5857255332Scy				csump = &icmp6->icmp6_cksum;
5858255332Scy		}
5859255332Scy		break;
5860255332Scy	    }
5861255332Scy#endif
5862145522Sdarrenr	}
5863255332Scy	return csump;
586453642Sguido}
586553642Sguido
586653642Sguido
5867145522Sdarrenr/* ------------------------------------------------------------------------ */
5868255332Scy/* Function:    ipf_nat_expire                                              */
5869145522Sdarrenr/* Returns:     Nil                                                         */
5870255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
5871145522Sdarrenr/*                                                                          */
5872145522Sdarrenr/* Check all of the timeout queues for entries at the top which need to be  */
5873145522Sdarrenr/* expired.                                                                 */
5874145522Sdarrenr/* ------------------------------------------------------------------------ */
5875255332Scyvoid
5876255332Scyipf_nat_expire(softc)
5877255332Scy	ipf_main_softc_t *softc;
587853642Sguido{
5879255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5880145522Sdarrenr	ipftq_t *ifq, *ifqnext;
5881145522Sdarrenr	ipftqent_t *tqe, *tqn;
5882161356Sguido	int i;
5883153876Sguido	SPL_INT(s);
588453642Sguido
588553642Sguido	SPL_NET(s);
5886255332Scy	WRITE_ENTER(&softc->ipf_nat);
5887255332Scy	for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL;
5888255332Scy	     ifq = ifq->ifq_next) {
5889145522Sdarrenr		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
5890255332Scy			if (tqe->tqe_die > softc->ipf_ticks)
5891145522Sdarrenr				break;
5892145522Sdarrenr			tqn = tqe->tqe_next;
5893255332Scy			ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE);
589453642Sguido		}
589553642Sguido	}
5896145522Sdarrenr
5897255332Scy	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) {
5898145522Sdarrenr		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
5899255332Scy			if (tqe->tqe_die > softc->ipf_ticks)
5900145522Sdarrenr				break;
5901145522Sdarrenr			tqn = tqe->tqe_next;
5902255332Scy			ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE);
5903145522Sdarrenr		}
5904145522Sdarrenr	}
5905145522Sdarrenr
5906255332Scy	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) {
5907145522Sdarrenr		ifqnext = ifq->ifq_next;
5908145522Sdarrenr
5909145522Sdarrenr		if (((ifq->ifq_flags & IFQF_DELETE) != 0) &&
5910145522Sdarrenr		    (ifq->ifq_ref == 0)) {
5911255332Scy			ipf_freetimeoutqueue(softc, ifq);
5912145522Sdarrenr		}
5913145522Sdarrenr	}
5914145522Sdarrenr
5915255332Scy	if (softn->ipf_nat_doflush != 0) {
5916255332Scy		ipf_nat_extraflush(softc, softn, 2);
5917255332Scy		softn->ipf_nat_doflush = 0;
5918170268Sdarrenr	}
5919170268Sdarrenr
5920255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
592153642Sguido	SPL_X(s);
592253642Sguido}
592353642Sguido
592453642Sguido
5925145522Sdarrenr/* ------------------------------------------------------------------------ */
5926255332Scy/* Function:    ipf_nat_sync                                                */
5927145522Sdarrenr/* Returns:     Nil                                                         */
5928255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
5929255332Scy/*              ifp(I) - pointer to network interface                       */
5930145522Sdarrenr/*                                                                          */
5931145522Sdarrenr/* Walk through all of the currently active NAT sessions, looking for those */
5932145522Sdarrenr/* which need to have their translated address updated.                     */
5933145522Sdarrenr/* ------------------------------------------------------------------------ */
5934255332Scyvoid
5935255332Scyipf_nat_sync(softc, ifp)
5936255332Scy	ipf_main_softc_t *softc;
5937255332Scy	void *ifp;
593853642Sguido{
5939255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5940145522Sdarrenr	u_32_t sum1, sum2, sumd;
5941255332Scy	i6addr_t in;
5942145522Sdarrenr	ipnat_t *n;
5943145522Sdarrenr	nat_t *nat;
594453642Sguido	void *ifp2;
5945255332Scy	int idx;
5946153876Sguido	SPL_INT(s);
594753642Sguido
5948255332Scy	if (softc->ipf_running <= 0)
5949145522Sdarrenr		return;
5950145522Sdarrenr
595153642Sguido	/*
595253642Sguido	 * Change IP addresses for NAT sessions for any protocol except TCP
5953145522Sdarrenr	 * since it will break the TCP connection anyway.  The only rules
5954145522Sdarrenr	 * which will get changed are those which are "map ... -> 0/32",
5955145522Sdarrenr	 * where the rule specifies the address is taken from the interface.
595653642Sguido	 */
595753642Sguido	SPL_NET(s);
5958255332Scy	WRITE_ENTER(&softc->ipf_nat);
5959145522Sdarrenr
5960255332Scy	if (softc->ipf_running <= 0) {
5961255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
5962145522Sdarrenr		return;
5963145522Sdarrenr	}
5964145522Sdarrenr
5965255332Scy	for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) {
5966145522Sdarrenr		if ((nat->nat_flags & IPN_TCP) != 0)
5967145522Sdarrenr			continue;
5968255332Scy
5969145522Sdarrenr		n = nat->nat_ptr;
5970255332Scy		if (n != NULL) {
5971255332Scy			if (n->in_v[1] == 4) {
5972255332Scy				if (n->in_redir & NAT_MAP) {
5973255332Scy					if ((n->in_nsrcaddr != 0) ||
5974255332Scy					    (n->in_nsrcmsk != 0xffffffff))
5975255332Scy						continue;
5976255332Scy				} else if (n->in_redir & NAT_REDIRECT) {
5977255332Scy					if ((n->in_ndstaddr != 0) ||
5978255332Scy					    (n->in_ndstmsk != 0xffffffff))
5979255332Scy						continue;
5980255332Scy				}
5981255332Scy			}
5982255332Scy#ifdef USE_INET6
5983255332Scy			if (n->in_v[1] == 4) {
5984255332Scy				if (n->in_redir & NAT_MAP) {
5985255332Scy					if (!IP6_ISZERO(&n->in_nsrcaddr) ||
5986255332Scy					    !IP6_ISONES(&n->in_nsrcmsk))
5987255332Scy						continue;
5988255332Scy				} else if (n->in_redir & NAT_REDIRECT) {
5989255332Scy					if (!IP6_ISZERO(&n->in_ndstaddr) ||
5990255332Scy					    !IP6_ISONES(&n->in_ndstmsk))
5991255332Scy						continue;
5992255332Scy				}
5993255332Scy			}
5994255332Scy#endif
5995255332Scy		}
5996255332Scy
5997145522Sdarrenr		if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) ||
5998145522Sdarrenr		     (ifp == nat->nat_ifps[1]))) {
5999255332Scy			nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0],
6000255332Scy						  nat->nat_v[0]);
6001255332Scy			if ((nat->nat_ifps[0] != NULL) &&
6002255332Scy			    (nat->nat_ifps[0] != (void *)-1)) {
6003255332Scy				nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
6004255332Scy			}
6005145522Sdarrenr			if (nat->nat_ifnames[1][0] != '\0') {
6006145522Sdarrenr				nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1],
6007255332Scy							  nat->nat_v[1]);
6008255332Scy			} else {
6009145522Sdarrenr				nat->nat_ifps[1] = nat->nat_ifps[0];
6010255332Scy			}
6011255332Scy			if ((nat->nat_ifps[1] != NULL) &&
6012255332Scy			    (nat->nat_ifps[1] != (void *)-1)) {
6013255332Scy				nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
6014255332Scy			}
6015145522Sdarrenr			ifp2 = nat->nat_ifps[0];
6016145522Sdarrenr			if (ifp2 == NULL)
6017145522Sdarrenr				continue;
6018145522Sdarrenr
601953642Sguido			/*
602053642Sguido			 * Change the map-to address to be the same as the
602153642Sguido			 * new one.
602253642Sguido			 */
6023255332Scy			sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6);
6024255332Scy			if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2,
6025255332Scy				       &in, NULL) != -1) {
6026255332Scy				if (nat->nat_v[0] == 4)
6027255332Scy					nat->nat_nsrcip = in.in4;
6028255332Scy			}
6029255332Scy			sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6);
603053642Sguido
603153642Sguido			if (sum1 == sum2)
603253642Sguido				continue;
603353642Sguido			/*
603453642Sguido			 * Readjust the checksum adjustment to take into
603553642Sguido			 * account the new IP#.
603653642Sguido			 */
603753642Sguido			CALC_SUMD(sum1, sum2, sumd);
603855929Sguido			/* XXX - dont change for TCP when solaris does
603955929Sguido			 * hardware checksumming.
604055929Sguido			 */
604155929Sguido			sumd += nat->nat_sumd[0];
604255929Sguido			nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
604355929Sguido			nat->nat_sumd[1] = nat->nat_sumd[0];
604453642Sguido		}
6045145522Sdarrenr	}
604653642Sguido
6047255332Scy	for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) {
6048255332Scy		char *base = n->in_names;
6049255332Scy
6050145522Sdarrenr		if ((ifp == NULL) || (n->in_ifps[0] == ifp))
6051255332Scy			n->in_ifps[0] = ipf_resolvenic(softc,
6052255332Scy						       base + n->in_ifnames[0],
6053255332Scy						       n->in_v[0]);
6054145522Sdarrenr		if ((ifp == NULL) || (n->in_ifps[1] == ifp))
6055255332Scy			n->in_ifps[1] = ipf_resolvenic(softc,
6056255332Scy						       base + n->in_ifnames[1],
6057255332Scy						       n->in_v[1]);
6058255332Scy
6059255332Scy		if (n->in_redir & NAT_REDIRECT)
6060255332Scy			idx = 1;
6061255332Scy		else
6062255332Scy			idx = 0;
6063255332Scy
6064255332Scy		if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) &&
6065255332Scy		    (n->in_ifps[idx] != NULL &&
6066255332Scy		     n->in_ifps[idx] != (void *)-1)) {
6067255332Scy
6068255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc,
6069255332Scy					     0, n->in_ifps[idx]);
6070255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst,
6071255332Scy					     0, n->in_ifps[idx]);
6072255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc,
6073255332Scy					     0, n->in_ifps[idx]);
6074255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst,
6075255332Scy					     0, n->in_ifps[idx]);
6076255332Scy		}
6077145522Sdarrenr	}
6078255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
607953642Sguido	SPL_X(s);
608053642Sguido}
608153642Sguido
608253642Sguido
6083145522Sdarrenr/* ------------------------------------------------------------------------ */
6084255332Scy/* Function:    ipf_nat_icmpquerytype                                       */
6085145522Sdarrenr/* Returns:     int - 1 == success, 0 == failure                            */
6086145522Sdarrenr/* Parameters:  icmptype(I) - ICMP type number                              */
6087145522Sdarrenr/*                                                                          */
6088145522Sdarrenr/* Tests to see if the ICMP type number passed is a query/response type or  */
6089145522Sdarrenr/* not.                                                                     */
6090145522Sdarrenr/* ------------------------------------------------------------------------ */
6091255332Scystatic int
6092255332Scyipf_nat_icmpquerytype(icmptype)
6093255332Scy	int icmptype;
6094145522Sdarrenr{
6095145522Sdarrenr
6096145522Sdarrenr	/*
6097145522Sdarrenr	 * For the ICMP query NAT code, it is essential that both the query
6098145522Sdarrenr	 * and the reply match on the NAT rule. Because the NAT structure
6099145522Sdarrenr	 * does not keep track of the icmptype, and a single NAT structure
6100145522Sdarrenr	 * is used for all icmp types with the same src, dest and id, we
6101145522Sdarrenr	 * simply define the replies as queries as well. The funny thing is,
6102145522Sdarrenr	 * altough it seems silly to call a reply a query, this is exactly
6103145522Sdarrenr	 * as it is defined in the IPv4 specification
6104145522Sdarrenr	 */
6105145522Sdarrenr	switch (icmptype)
6106145522Sdarrenr	{
6107145522Sdarrenr	case ICMP_ECHOREPLY:
6108145522Sdarrenr	case ICMP_ECHO:
6109324513Scy	/* route advertisement/solicitation is currently unsupported: */
6110324513Scy	/* it would require rewriting the ICMP data section          */
6111145522Sdarrenr	case ICMP_TSTAMP:
6112145522Sdarrenr	case ICMP_TSTAMPREPLY:
6113145522Sdarrenr	case ICMP_IREQ:
6114145522Sdarrenr	case ICMP_IREQREPLY:
6115145522Sdarrenr	case ICMP_MASKREQ:
6116145522Sdarrenr	case ICMP_MASKREPLY:
6117145522Sdarrenr		return 1;
6118145522Sdarrenr	default:
6119145522Sdarrenr		return 0;
6120145522Sdarrenr	}
6121145522Sdarrenr}
6122145522Sdarrenr
6123145522Sdarrenr
6124145522Sdarrenr/* ------------------------------------------------------------------------ */
6125145522Sdarrenr/* Function:    nat_log                                                     */
6126145522Sdarrenr/* Returns:     Nil                                                         */
6127255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6128255332Scy/*              softn(I) - pointer to NAT context structure                 */
6129255332Scy/*              nat(I)    - pointer to NAT structure                        */
6130255332Scy/*              action(I) - action related to NAT structure being performed */
6131145522Sdarrenr/*                                                                          */
6132145522Sdarrenr/* Creates a NAT log entry.                                                 */
6133145522Sdarrenr/* ------------------------------------------------------------------------ */
6134255332Scyvoid
6135255332Scyipf_nat_log(softc, softn, nat, action)
6136255332Scy	ipf_main_softc_t *softc;
6137255332Scy	ipf_nat_softc_t *softn;
6138255332Scy	struct nat *nat;
6139255332Scy	u_int action;
614053642Sguido{
6141145522Sdarrenr#ifdef	IPFILTER_LOG
6142139005Smlaier# ifndef LARGE_NAT
614353642Sguido	struct ipnat *np;
6144138979Sdarrenr	int rulen;
6145138979Sdarrenr# endif
614653642Sguido	struct natlog natl;
614753642Sguido	void *items[1];
614853642Sguido	size_t sizes[1];
6149138979Sdarrenr	int types[1];
615053642Sguido
6151255332Scy	bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip,
6152255332Scy	      sizeof(natl.nl_osrcip));
6153255332Scy	bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip,
6154255332Scy	      sizeof(natl.nl_nsrcip));
6155255332Scy	bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip,
6156255332Scy	      sizeof(natl.nl_odstip));
6157255332Scy	bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip,
6158255332Scy	      sizeof(natl.nl_ndstip));
6159255332Scy
6160145522Sdarrenr	natl.nl_bytes[0] = nat->nat_bytes[0];
6161145522Sdarrenr	natl.nl_bytes[1] = nat->nat_bytes[1];
6162145522Sdarrenr	natl.nl_pkts[0] = nat->nat_pkts[0];
6163145522Sdarrenr	natl.nl_pkts[1] = nat->nat_pkts[1];
6164255332Scy	natl.nl_odstport = nat->nat_odport;
6165255332Scy	natl.nl_osrcport = nat->nat_osport;
6166255332Scy	natl.nl_nsrcport = nat->nat_nsport;
6167255332Scy	natl.nl_ndstport = nat->nat_ndport;
6168255332Scy	natl.nl_p[0] = nat->nat_pr[0];
6169255332Scy	natl.nl_p[1] = nat->nat_pr[1];
6170255332Scy	natl.nl_v[0] = nat->nat_v[0];
6171255332Scy	natl.nl_v[1] = nat->nat_v[1];
6172255332Scy	natl.nl_type = nat->nat_redir;
6173255332Scy	natl.nl_action = action;
617453642Sguido	natl.nl_rule = -1;
6175255332Scy
6176255332Scy	bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0],
6177255332Scy	      sizeof(nat->nat_ifnames[0]));
6178255332Scy	bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1],
6179255332Scy	      sizeof(nat->nat_ifnames[1]));
6180255332Scy
6181145522Sdarrenr# ifndef LARGE_NAT
618253642Sguido	if (nat->nat_ptr != NULL) {
6183255332Scy		for (rulen = 0, np = softn->ipf_nat_list; np != NULL;
6184255332Scy		     np = np->in_next, rulen++)
618553642Sguido			if (np == nat->nat_ptr) {
618653642Sguido				natl.nl_rule = rulen;
618753642Sguido				break;
618853642Sguido			}
618953642Sguido	}
6190145522Sdarrenr# endif
619153642Sguido	items[0] = &natl;
619253642Sguido	sizes[0] = sizeof(natl);
619353642Sguido	types[0] = 0;
619453642Sguido
6195255332Scy	(void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1);
6196145522Sdarrenr#endif
619753642Sguido}
619892685Sdarrenr
619992685Sdarrenr
6200255332Scy
6201255332Scy
6202145522Sdarrenr/* ------------------------------------------------------------------------ */
6203255332Scy/* Function:    ipf_nat_rule_deref                                          */
6204170268Sdarrenr/* Returns:     Nil                                                         */
6205255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6206255332Scy/*              inp(I)   - pointer to pointer to NAT rule                   */
6207170268Sdarrenr/* Write Locks: ipf_nat                                                     */
6208170268Sdarrenr/*                                                                          */
6209255332Scy/* Dropping the refernce count for a rule means that whatever held the      */
6210255332Scy/* pointer to this rule (*inp) is no longer interested in it and when the   */
6211255332Scy/* reference count drops to zero, any resources allocated for the rule can  */
6212255332Scy/* be released and the rule itself free'd.                                  */
6213170268Sdarrenr/* ------------------------------------------------------------------------ */
6214255332Scyvoid
6215255332Scyipf_nat_rule_deref(softc, inp)
6216255332Scy	ipf_main_softc_t *softc;
6217255332Scy	ipnat_t **inp;
6218170268Sdarrenr{
6219255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6220255332Scy	ipnat_t *n;
6221170268Sdarrenr
6222255332Scy	n = *inp;
6223170268Sdarrenr	*inp = NULL;
6224255332Scy	n->in_use--;
6225255332Scy	if (n->in_use > 0)
6226255332Scy		return;
6227255332Scy
6228255332Scy	if (n->in_apr != NULL)
6229255332Scy		ipf_proxy_deref(n->in_apr);
6230255332Scy
6231255332Scy	ipf_nat_rule_fini(softc, n);
6232255332Scy
6233255332Scy	if (n->in_redir & NAT_REDIRECT) {
6234255332Scy		if ((n->in_flags & IPN_PROXYRULE) == 0) {
6235255332Scy			ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr);
6236255332Scy		}
6237255332Scy	}
6238255332Scy	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
6239255332Scy		if ((n->in_flags & IPN_PROXYRULE) == 0) {
6240255332Scy			ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map);
6241255332Scy		}
6242255332Scy	}
6243255332Scy
6244255332Scy	if (n->in_tqehead[0] != NULL) {
6245255332Scy		if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) {
6246255332Scy			ipf_freetimeoutqueue(softc, n->in_tqehead[1]);
6247255332Scy		}
6248255332Scy	}
6249255332Scy
6250255332Scy	if (n->in_tqehead[1] != NULL) {
6251255332Scy		if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) {
6252255332Scy			ipf_freetimeoutqueue(softc, n->in_tqehead[1]);
6253255332Scy		}
6254255332Scy	}
6255255332Scy
6256255332Scy	if ((n->in_flags & IPN_PROXYRULE) == 0) {
6257255332Scy		ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules);
6258255332Scy	}
6259255332Scy
6260255332Scy	MUTEX_DESTROY(&n->in_lock);
6261255332Scy
6262255332Scy	KFREES(n, n->in_size);
6263255332Scy
6264255332Scy#if SOLARIS && !defined(INSTANCES)
6265255332Scy	if (softn->ipf_nat_stats.ns_rules == 0)
6266255332Scy		pfil_delayed_copy = 1;
6267170268Sdarrenr#endif
6268170268Sdarrenr}
6269170268Sdarrenr
6270170268Sdarrenr
6271170268Sdarrenr/* ------------------------------------------------------------------------ */
6272255332Scy/* Function:    ipf_nat_deref                                               */
6273145522Sdarrenr/* Returns:     Nil                                                         */
6274255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6275255332Scy/*              natp(I)  - pointer to pointer to NAT table entry            */
6276145522Sdarrenr/*                                                                          */
6277145522Sdarrenr/* Decrement the reference counter for this NAT table entry and free it if  */
6278145522Sdarrenr/* there are no more things using it.                                       */
6279172776Sdarrenr/*                                                                          */
6280172776Sdarrenr/* IF nat_ref == 1 when this function is called, then we have an orphan nat */
6281172776Sdarrenr/* structure *because* it only gets called on paths _after_ nat_ref has been*/
6282172776Sdarrenr/* incremented.  If nat_ref == 1 then we shouldn't decrement it here        */
6283172776Sdarrenr/* because nat_delete() will do that and send nat_ref to -1.                */
6284172776Sdarrenr/*                                                                          */
6285172776Sdarrenr/* Holding the lock on nat_lock is required to serialise nat_delete() being */
6286172776Sdarrenr/* called from a NAT flush ioctl with a deref happening because of a packet.*/
6287145522Sdarrenr/* ------------------------------------------------------------------------ */
6288255332Scyvoid
6289255332Scyipf_nat_deref(softc, natp)
6290255332Scy	ipf_main_softc_t *softc;
6291255332Scy	nat_t **natp;
6292145522Sdarrenr{
6293145522Sdarrenr	nat_t *nat;
6294145522Sdarrenr
6295145522Sdarrenr	nat = *natp;
6296145522Sdarrenr	*natp = NULL;
6297172776Sdarrenr
6298172776Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
6299172776Sdarrenr	if (nat->nat_ref > 1) {
6300172776Sdarrenr		nat->nat_ref--;
6301255332Scy		ASSERT(nat->nat_ref >= 0);
6302172776Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
6303172776Sdarrenr		return;
6304172776Sdarrenr	}
6305172776Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
6306172776Sdarrenr
6307255332Scy	WRITE_ENTER(&softc->ipf_nat);
6308255332Scy	ipf_nat_delete(softc, nat, NL_EXPIRE);
6309255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
6310145522Sdarrenr}
6311145522Sdarrenr
6312145522Sdarrenr
6313145522Sdarrenr/* ------------------------------------------------------------------------ */
6314255332Scy/* Function:    ipf_nat_clone                                               */
6315145522Sdarrenr/* Returns:     ipstate_t* - NULL == cloning failed,                        */
6316145522Sdarrenr/*                           else pointer to new state structure            */
6317145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
6318145522Sdarrenr/*              is(I)  - pointer to master state structure                  */
6319145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
6320145522Sdarrenr/*                                                                          */
6321145522Sdarrenr/* Create a "duplcate" state table entry from the master.                   */
6322145522Sdarrenr/* ------------------------------------------------------------------------ */
6323255332Scynat_t *
6324255332Scyipf_nat_clone(fin, nat)
6325255332Scy	fr_info_t *fin;
6326255332Scy	nat_t *nat;
6327145522Sdarrenr{
6328255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
6329255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6330145522Sdarrenr	frentry_t *fr;
6331145522Sdarrenr	nat_t *clone;
6332145522Sdarrenr	ipnat_t *np;
6333145522Sdarrenr
6334145522Sdarrenr	KMALLOC(clone, nat_t *);
6335255332Scy	if (clone == NULL) {
6336255332Scy		NBUMPSIDED(fin->fin_out, ns_clone_nomem);
6337145522Sdarrenr		return NULL;
6338255332Scy	}
6339145522Sdarrenr	bcopy((char *)nat, (char *)clone, sizeof(*clone));
6340145522Sdarrenr
6341145522Sdarrenr	MUTEX_NUKE(&clone->nat_lock);
6342145522Sdarrenr
6343255332Scy	clone->nat_rev = fin->fin_rev;
6344153876Sguido	clone->nat_aps = NULL;
6345153876Sguido	/*
6346255332Scy	 * Initialize all these so that ipf_nat_delete() doesn't cause a crash.
6347153876Sguido	 */
6348153876Sguido	clone->nat_tqe.tqe_pnext = NULL;
6349153876Sguido	clone->nat_tqe.tqe_next = NULL;
6350153876Sguido	clone->nat_tqe.tqe_ifq = NULL;
6351153876Sguido	clone->nat_tqe.tqe_parent = clone;
6352153876Sguido
6353145522Sdarrenr	clone->nat_flags &= ~SI_CLONE;
6354145522Sdarrenr	clone->nat_flags |= SI_CLONED;
6355145522Sdarrenr
6356153876Sguido	if (clone->nat_hm)
6357153876Sguido		clone->nat_hm->hm_ref++;
6358145522Sdarrenr
6359255332Scy	if (ipf_nat_insert(softc, softn, clone) == -1) {
6360145522Sdarrenr		KFREE(clone);
6361255332Scy		NBUMPSIDED(fin->fin_out, ns_insert_fail);
6362145522Sdarrenr		return NULL;
6363145522Sdarrenr	}
6364255332Scy
6365145522Sdarrenr	np = clone->nat_ptr;
6366145522Sdarrenr	if (np != NULL) {
6367255332Scy		if (softn->ipf_nat_logging)
6368255332Scy			ipf_nat_log(softc, softn, clone, NL_CLONE);
6369145522Sdarrenr		np->in_use++;
6370145522Sdarrenr	}
6371145522Sdarrenr	fr = clone->nat_fr;
6372145522Sdarrenr	if (fr != NULL) {
6373145522Sdarrenr		MUTEX_ENTER(&fr->fr_lock);
6374145522Sdarrenr		fr->fr_ref++;
6375145522Sdarrenr		MUTEX_EXIT(&fr->fr_lock);
6376145522Sdarrenr	}
6377145522Sdarrenr
6378255332Scy
6379145522Sdarrenr	/*
6380145522Sdarrenr	 * Because the clone is created outside the normal loop of things and
6381145522Sdarrenr	 * TCP has special needs in terms of state, initialise the timeout
6382145522Sdarrenr	 * state of the new NAT from here.
6383145522Sdarrenr	 */
6384255332Scy	if (clone->nat_pr[0] == IPPROTO_TCP) {
6385255332Scy		(void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq,
6386255332Scy				   clone->nat_flags, 2);
6387145522Sdarrenr	}
6388255332Scy	clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone);
6389255332Scy	if (softn->ipf_nat_logging)
6390255332Scy		ipf_nat_log(softc, softn, clone, NL_CLONE);
6391145522Sdarrenr	return clone;
6392145522Sdarrenr}
6393145522Sdarrenr
6394145522Sdarrenr
6395145522Sdarrenr/* ------------------------------------------------------------------------ */
6396255332Scy/* Function:   ipf_nat_wildok                                               */
6397145522Sdarrenr/* Returns:    int - 1 == packet's ports match wildcards                    */
6398145522Sdarrenr/*                   0 == packet's ports don't match wildcards              */
6399145522Sdarrenr/* Parameters: nat(I)   - NAT entry                                         */
6400145522Sdarrenr/*             sport(I) - source port                                       */
6401145522Sdarrenr/*             dport(I) - destination port                                  */
6402145522Sdarrenr/*             flags(I) - wildcard flags                                    */
6403145522Sdarrenr/*             dir(I)   - packet direction                                  */
6404145522Sdarrenr/*                                                                          */
6405145522Sdarrenr/* Use NAT entry and packet direction to determine which combination of     */
6406145522Sdarrenr/* wildcard flags should be used.                                           */
6407145522Sdarrenr/* ------------------------------------------------------------------------ */
6408255332Scyint
6409255332Scyipf_nat_wildok(nat, sport, dport, flags, dir)
6410255332Scy	nat_t *nat;
6411255332Scy	int sport, dport, flags, dir;
6412145522Sdarrenr{
6413145522Sdarrenr	/*
6414145522Sdarrenr	 * When called by       dir is set to
6415145522Sdarrenr	 * nat_inlookup         NAT_INBOUND (0)
6416145522Sdarrenr	 * nat_outlookup        NAT_OUTBOUND (1)
6417145522Sdarrenr	 *
6418145522Sdarrenr	 * We simply combine the packet's direction in dir with the original
6419145522Sdarrenr	 * "intended" direction of that NAT entry in nat->nat_dir to decide
6420145522Sdarrenr	 * which combination of wildcard flags to allow.
6421145522Sdarrenr	 */
6422255332Scy	switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)))
6423145522Sdarrenr	{
6424145522Sdarrenr	case 3: /* outbound packet / outbound entry */
6425255332Scy		if (((nat->nat_osport == sport) ||
6426145522Sdarrenr		    (flags & SI_W_SPORT)) &&
6427255332Scy		    ((nat->nat_odport == dport) ||
6428145522Sdarrenr		    (flags & SI_W_DPORT)))
6429145522Sdarrenr			return 1;
6430145522Sdarrenr		break;
6431145522Sdarrenr	case 2: /* outbound packet / inbound entry */
6432255332Scy		if (((nat->nat_osport == dport) ||
6433255332Scy		    (flags & SI_W_SPORT)) &&
6434255332Scy		    ((nat->nat_odport == sport) ||
6435255332Scy		    (flags & SI_W_DPORT)))
6436145522Sdarrenr			return 1;
6437145522Sdarrenr		break;
6438145522Sdarrenr	case 1: /* inbound packet / outbound entry */
6439255332Scy		if (((nat->nat_osport == dport) ||
6440255332Scy		    (flags & SI_W_SPORT)) &&
6441255332Scy		    ((nat->nat_odport == sport) ||
6442255332Scy		    (flags & SI_W_DPORT)))
6443145522Sdarrenr			return 1;
6444145522Sdarrenr		break;
6445145522Sdarrenr	case 0: /* inbound packet / inbound entry */
6446255332Scy		if (((nat->nat_osport == sport) ||
6447145522Sdarrenr		    (flags & SI_W_SPORT)) &&
6448255332Scy		    ((nat->nat_odport == dport) ||
6449145522Sdarrenr		    (flags & SI_W_DPORT)))
6450145522Sdarrenr			return 1;
6451145522Sdarrenr		break;
6452145522Sdarrenr	default:
6453145522Sdarrenr		break;
6454145522Sdarrenr	}
6455145522Sdarrenr
6456145522Sdarrenr	return(0);
6457145522Sdarrenr}
6458145522Sdarrenr
6459145522Sdarrenr
6460145522Sdarrenr/* ------------------------------------------------------------------------ */
6461145522Sdarrenr/* Function:    nat_mssclamp                                                */
6462145522Sdarrenr/* Returns:     Nil                                                         */
6463145522Sdarrenr/* Parameters:  tcp(I)    - pointer to TCP header                           */
6464145522Sdarrenr/*              maxmss(I) - value to clamp the TCP MSS to                   */
6465145522Sdarrenr/*              fin(I)    - pointer to packet information                   */
6466145522Sdarrenr/*              csump(I)  - pointer to TCP checksum                         */
6467145522Sdarrenr/*                                                                          */
6468145522Sdarrenr/* Check for MSS option and clamp it if necessary.  If found and changed,   */
6469145522Sdarrenr/* then the TCP header checksum will be updated to reflect the change in    */
6470145522Sdarrenr/* the MSS.                                                                 */
6471145522Sdarrenr/* ------------------------------------------------------------------------ */
6472255332Scystatic void
6473255332Scyipf_nat_mssclamp(tcp, maxmss, fin, csump)
6474255332Scy	tcphdr_t *tcp;
6475255332Scy	u_32_t maxmss;
6476255332Scy	fr_info_t *fin;
6477255332Scy	u_short *csump;
6478110916Sdarrenr{
6479110916Sdarrenr	u_char *cp, *ep, opt;
6480110916Sdarrenr	int hlen, advance;
6481110916Sdarrenr	u_32_t mss, sumd;
6482110916Sdarrenr
6483145522Sdarrenr	hlen = TCP_OFF(tcp) << 2;
6484110916Sdarrenr	if (hlen > sizeof(*tcp)) {
6485110916Sdarrenr		cp = (u_char *)tcp + sizeof(*tcp);
6486110916Sdarrenr		ep = (u_char *)tcp + hlen;
6487110916Sdarrenr
6488110916Sdarrenr		while (cp < ep) {
6489110916Sdarrenr			opt = cp[0];
6490110916Sdarrenr			if (opt == TCPOPT_EOL)
6491110916Sdarrenr				break;
6492110916Sdarrenr			else if (opt == TCPOPT_NOP) {
6493110916Sdarrenr				cp++;
6494110916Sdarrenr				continue;
6495110916Sdarrenr			}
6496145522Sdarrenr
6497145522Sdarrenr			if (cp + 1 >= ep)
6498110916Sdarrenr				break;
6499110916Sdarrenr			advance = cp[1];
6500145522Sdarrenr			if ((cp + advance > ep) || (advance <= 0))
6501110916Sdarrenr				break;
6502145522Sdarrenr			switch (opt)
6503145522Sdarrenr			{
6504110916Sdarrenr			case TCPOPT_MAXSEG:
6505110916Sdarrenr				if (advance != 4)
6506110916Sdarrenr					break;
6507145522Sdarrenr				mss = cp[2] * 256 + cp[3];
6508110916Sdarrenr				if (mss > maxmss) {
6509145522Sdarrenr					cp[2] = maxmss / 256;
6510145522Sdarrenr					cp[3] = maxmss & 0xff;
6511110916Sdarrenr					CALC_SUMD(mss, maxmss, sumd);
6512255332Scy					ipf_fix_outcksum(0, csump, sumd, 0);
6513110916Sdarrenr				}
6514110916Sdarrenr				break;
6515110916Sdarrenr			default:
6516110916Sdarrenr				/* ignore unknown options */
6517110916Sdarrenr				break;
6518110916Sdarrenr			}
6519145522Sdarrenr
6520145522Sdarrenr			cp += advance;
6521145522Sdarrenr		}
6522145522Sdarrenr	}
6523145522Sdarrenr}
6524145522Sdarrenr
6525145522Sdarrenr
6526145522Sdarrenr/* ------------------------------------------------------------------------ */
6527255332Scy/* Function:    ipf_nat_setqueue                                            */
6528145522Sdarrenr/* Returns:     Nil                                                         */
6529255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6530255332Scy/*              softn(I) - pointer to NAT context structure                 */
6531255332Scy/*              nat(I)- pointer to NAT structure                            */
6532145522Sdarrenr/* Locks:       ipf_nat (read or write)                                     */
6533145522Sdarrenr/*                                                                          */
6534145522Sdarrenr/* Put the NAT entry on its default queue entry, using rev as a helped in   */
6535145522Sdarrenr/* determining which queue it should be placed on.                          */
6536145522Sdarrenr/* ------------------------------------------------------------------------ */
6537255332Scyvoid
6538255332Scyipf_nat_setqueue(softc, softn, nat)
6539255332Scy	ipf_main_softc_t *softc;
6540255332Scy	ipf_nat_softc_t *softn;
6541255332Scy	nat_t *nat;
6542145522Sdarrenr{
6543145522Sdarrenr	ipftq_t *oifq, *nifq;
6544255332Scy	int rev = nat->nat_rev;
6545145522Sdarrenr
6546145522Sdarrenr	if (nat->nat_ptr != NULL)
6547145522Sdarrenr		nifq = nat->nat_ptr->in_tqehead[rev];
6548145522Sdarrenr	else
6549145522Sdarrenr		nifq = NULL;
6550145522Sdarrenr
6551145522Sdarrenr	if (nifq == NULL) {
6552255332Scy		switch (nat->nat_pr[0])
6553145522Sdarrenr		{
6554145522Sdarrenr		case IPPROTO_UDP :
6555255332Scy			nifq = &softn->ipf_nat_udptq;
6556145522Sdarrenr			break;
6557145522Sdarrenr		case IPPROTO_ICMP :
6558255332Scy			nifq = &softn->ipf_nat_icmptq;
6559145522Sdarrenr			break;
6560145522Sdarrenr		case IPPROTO_TCP :
6561255332Scy			nifq = softn->ipf_nat_tcptq +
6562255332Scy			       nat->nat_tqe.tqe_state[rev];
6563145522Sdarrenr			break;
6564145522Sdarrenr		default :
6565255332Scy			nifq = &softn->ipf_nat_iptq;
6566145522Sdarrenr			break;
6567145522Sdarrenr		}
6568145522Sdarrenr	}
6569145522Sdarrenr
6570145522Sdarrenr	oifq = nat->nat_tqe.tqe_ifq;
6571145522Sdarrenr	/*
6572145522Sdarrenr	 * If it's currently on a timeout queue, move it from one queue to
6573145522Sdarrenr	 * another, else put it on the end of the newly determined queue.
6574145522Sdarrenr	 */
6575145522Sdarrenr	if (oifq != NULL)
6576255332Scy		ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq);
6577145522Sdarrenr	else
6578255332Scy		ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat);
6579145522Sdarrenr	return;
6580145522Sdarrenr}
6581170268Sdarrenr
6582170268Sdarrenr
6583170268Sdarrenr/* ------------------------------------------------------------------------ */
6584170268Sdarrenr/* Function:    nat_getnext                                                 */
6585170268Sdarrenr/* Returns:     int - 0 == ok, else error                                   */
6586255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6587255332Scy/*              t(I)   - pointer to ipftoken structure                      */
6588170268Sdarrenr/*              itp(I) - pointer to ipfgeniter_t structure                  */
6589170268Sdarrenr/*                                                                          */
6590170268Sdarrenr/* Fetch the next nat/ipnat structure pointer from the linked list and      */
6591170268Sdarrenr/* copy it out to the storage space pointed to by itp_data.  The next item  */
6592170268Sdarrenr/* in the list to look at is put back in the ipftoken struture.             */
6593170268Sdarrenr/* ------------------------------------------------------------------------ */
6594255332Scystatic int
6595255332Scyipf_nat_getnext(softc, t, itp, objp)
6596255332Scy	ipf_main_softc_t *softc;
6597255332Scy	ipftoken_t *t;
6598255332Scy	ipfgeniter_t *itp;
6599255332Scy	ipfobj_t *objp;
6600170268Sdarrenr{
6601255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6602170268Sdarrenr	hostmap_t *hm, *nexthm = NULL, zerohm;
6603170268Sdarrenr	ipnat_t *ipn, *nextipnat = NULL, zeroipn;
6604170268Sdarrenr	nat_t *nat, *nextnat = NULL, zeronat;
6605255332Scy	int error = 0;
6606255332Scy	void *nnext;
6607170268Sdarrenr
6608255332Scy	if (itp->igi_nitems != 1) {
6609255332Scy		IPFERROR(60075);
6610172776Sdarrenr		return ENOSPC;
6611255332Scy	}
6612170268Sdarrenr
6613255332Scy	READ_ENTER(&softc->ipf_nat);
6614170268Sdarrenr
6615170268Sdarrenr	switch (itp->igi_type)
6616170268Sdarrenr	{
6617170268Sdarrenr	case IPFGENITER_HOSTMAP :
6618170268Sdarrenr		hm = t->ipt_data;
6619170268Sdarrenr		if (hm == NULL) {
6620255332Scy			nexthm = softn->ipf_hm_maplist;
6621170268Sdarrenr		} else {
6622170268Sdarrenr			nexthm = hm->hm_next;
6623170268Sdarrenr		}
6624255332Scy		if (nexthm != NULL) {
6625255332Scy			ATOMIC_INC32(nexthm->hm_ref);
6626255332Scy			t->ipt_data = nexthm;
6627255332Scy		} else {
6628255332Scy			bzero(&zerohm, sizeof(zerohm));
6629255332Scy			nexthm = &zerohm;
6630255332Scy			t->ipt_data = NULL;
6631255332Scy		}
6632255332Scy		nnext = nexthm->hm_next;
6633170268Sdarrenr		break;
6634170268Sdarrenr
6635170268Sdarrenr	case IPFGENITER_IPNAT :
6636170268Sdarrenr		ipn = t->ipt_data;
6637170268Sdarrenr		if (ipn == NULL) {
6638255332Scy			nextipnat = softn->ipf_nat_list;
6639170268Sdarrenr		} else {
6640170268Sdarrenr			nextipnat = ipn->in_next;
6641170268Sdarrenr		}
6642255332Scy		if (nextipnat != NULL) {
6643255332Scy			ATOMIC_INC32(nextipnat->in_use);
6644255332Scy			t->ipt_data = nextipnat;
6645255332Scy		} else {
6646255332Scy			bzero(&zeroipn, sizeof(zeroipn));
6647255332Scy			nextipnat = &zeroipn;
6648255332Scy			t->ipt_data = NULL;
6649255332Scy		}
6650255332Scy		nnext = nextipnat->in_next;
6651170268Sdarrenr		break;
6652170268Sdarrenr
6653170268Sdarrenr	case IPFGENITER_NAT :
6654170268Sdarrenr		nat = t->ipt_data;
6655170268Sdarrenr		if (nat == NULL) {
6656255332Scy			nextnat = softn->ipf_nat_instances;
6657170268Sdarrenr		} else {
6658170268Sdarrenr			nextnat = nat->nat_next;
6659170268Sdarrenr		}
6660255332Scy		if (nextnat != NULL) {
6661255332Scy			MUTEX_ENTER(&nextnat->nat_lock);
6662255332Scy			nextnat->nat_ref++;
6663255332Scy			MUTEX_EXIT(&nextnat->nat_lock);
6664255332Scy			t->ipt_data = nextnat;
6665255332Scy		} else {
6666255332Scy			bzero(&zeronat, sizeof(zeronat));
6667255332Scy			nextnat = &zeronat;
6668255332Scy			t->ipt_data = NULL;
6669255332Scy		}
6670255332Scy		nnext = nextnat->nat_next;
6671170268Sdarrenr		break;
6672255332Scy
6673170268Sdarrenr	default :
6674255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
6675255332Scy		IPFERROR(60055);
6676170268Sdarrenr		return EINVAL;
6677170268Sdarrenr	}
6678170268Sdarrenr
6679255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
6680170268Sdarrenr
6681255332Scy	objp->ipfo_ptr = itp->igi_data;
6682170268Sdarrenr
6683172776Sdarrenr	switch (itp->igi_type)
6684172776Sdarrenr	{
6685172776Sdarrenr	case IPFGENITER_HOSTMAP :
6686255332Scy		error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm));
6687255332Scy		if (error != 0) {
6688255332Scy			IPFERROR(60049);
6689255332Scy			error = EFAULT;
6690255332Scy		}
6691172776Sdarrenr		if (hm != NULL) {
6692255332Scy			WRITE_ENTER(&softc->ipf_nat);
6693255332Scy			ipf_nat_hostmapdel(softc, &hm);
6694255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
6695172776Sdarrenr		}
6696172776Sdarrenr		break;
6697255332Scy
6698172776Sdarrenr	case IPFGENITER_IPNAT :
6699255332Scy		objp->ipfo_size = nextipnat->in_size;
6700255332Scy		objp->ipfo_type = IPFOBJ_IPNAT;
6701255332Scy		error = ipf_outobjk(softc, objp, nextipnat);
6702172776Sdarrenr		if (ipn != NULL) {
6703255332Scy			WRITE_ENTER(&softc->ipf_nat);
6704255332Scy			ipf_nat_rule_deref(softc, &ipn);
6705255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
6706172776Sdarrenr		}
6707172776Sdarrenr		break;
6708172776Sdarrenr
6709170268Sdarrenr	case IPFGENITER_NAT :
6710255332Scy		objp->ipfo_size = sizeof(nat_t);
6711255332Scy		objp->ipfo_type = IPFOBJ_NAT;
6712255332Scy		error = ipf_outobjk(softc, objp, nextnat);
6713255332Scy		if (nat != NULL)
6714255332Scy			ipf_nat_deref(softc, &nat);
6715170268Sdarrenr
6716170268Sdarrenr		break;
6717170268Sdarrenr	}
6718170268Sdarrenr
6719255332Scy	if (nnext == NULL)
6720255332Scy		ipf_token_mark_complete(t);
6721255332Scy
6722170268Sdarrenr	return error;
6723170268Sdarrenr}
6724170268Sdarrenr
6725170268Sdarrenr
6726170268Sdarrenr/* ------------------------------------------------------------------------ */
6727170268Sdarrenr/* Function:    nat_extraflush                                              */
6728170268Sdarrenr/* Returns:     int - 0 == success, -1 == failure                           */
6729255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6730255332Scy/*              softn(I) - pointer to NAT context structure                 */
6731255332Scy/*              which(I) - how to flush the active NAT table                */
6732170268Sdarrenr/* Write Locks: ipf_nat                                                     */
6733170268Sdarrenr/*                                                                          */
6734170268Sdarrenr/* Flush nat tables.  Three actions currently defined:                      */
6735170268Sdarrenr/* which == 0 : flush all nat table entries                                 */
6736170268Sdarrenr/* which == 1 : flush TCP connections which have started to close but are   */
6737170268Sdarrenr/*	      stuck for some reason.                                        */
6738170268Sdarrenr/* which == 2 : flush TCP connections which have been idle for a long time, */
6739170268Sdarrenr/*	      starting at > 4 days idle and working back in successive half-*/
6740170268Sdarrenr/*	      days to at most 12 hours old.  If this fails to free enough   */
6741170268Sdarrenr/*            slots then work backwards in half hour slots to 30 minutes.   */
6742170268Sdarrenr/*            If that too fails, then work backwards in 30 second intervals */
6743170268Sdarrenr/*            for the last 30 minutes to at worst 30 seconds idle.          */
6744170268Sdarrenr/* ------------------------------------------------------------------------ */
6745255332Scystatic int
6746255332Scyipf_nat_extraflush(softc, softn, which)
6747255332Scy	ipf_main_softc_t *softc;
6748255332Scy	ipf_nat_softc_t *softn;
6749255332Scy	int which;
6750170268Sdarrenr{
6751170268Sdarrenr	nat_t *nat, **natp;
6752170268Sdarrenr	ipftqent_t *tqn;
6753255332Scy	ipftq_t *ifq;
6754170268Sdarrenr	int removed;
6755170268Sdarrenr	SPL_INT(s);
6756170268Sdarrenr
6757170268Sdarrenr	removed = 0;
6758170268Sdarrenr
6759170268Sdarrenr	SPL_NET(s);
6760170268Sdarrenr	switch (which)
6761170268Sdarrenr	{
6762170268Sdarrenr	case 0 :
6763255332Scy		softn->ipf_nat_stats.ns_flush_all++;
6764170268Sdarrenr		/*
6765170268Sdarrenr		 * Style 0 flush removes everything...
6766170268Sdarrenr		 */
6767255332Scy		for (natp = &softn->ipf_nat_instances;
6768255332Scy		     ((nat = *natp) != NULL); ) {
6769255332Scy			ipf_nat_delete(softc, nat, NL_FLUSH);
6770170268Sdarrenr			removed++;
6771170268Sdarrenr		}
6772170268Sdarrenr		break;
6773170268Sdarrenr
6774170268Sdarrenr	case 1 :
6775255332Scy		softn->ipf_nat_stats.ns_flush_closing++;
6776170268Sdarrenr		/*
6777170268Sdarrenr		 * Since we're only interested in things that are closing,
6778170268Sdarrenr		 * we can start with the appropriate timeout queue.
6779170268Sdarrenr		 */
6780255332Scy		for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT;
6781255332Scy		     ifq != NULL; ifq = ifq->ifq_next) {
6782170268Sdarrenr
6783170268Sdarrenr			for (tqn = ifq->ifq_head; tqn != NULL; ) {
6784170268Sdarrenr				nat = tqn->tqe_parent;
6785170268Sdarrenr				tqn = tqn->tqe_next;
6786255332Scy				if (nat->nat_pr[0] != IPPROTO_TCP ||
6787255332Scy				    nat->nat_pr[1] != IPPROTO_TCP)
6788170268Sdarrenr					break;
6789255332Scy				ipf_nat_delete(softc, nat, NL_EXPIRE);
6790170268Sdarrenr				removed++;
6791170268Sdarrenr			}
6792170268Sdarrenr		}
6793170268Sdarrenr
6794170268Sdarrenr		/*
6795170268Sdarrenr		 * Also need to look through the user defined queues.
6796170268Sdarrenr		 */
6797255332Scy		for (ifq = softn->ipf_nat_utqe; ifq != NULL;
6798255332Scy		     ifq = ifq->ifq_next) {
6799170268Sdarrenr			for (tqn = ifq->ifq_head; tqn != NULL; ) {
6800170268Sdarrenr				nat = tqn->tqe_parent;
6801170268Sdarrenr				tqn = tqn->tqe_next;
6802255332Scy				if (nat->nat_pr[0] != IPPROTO_TCP ||
6803255332Scy				    nat->nat_pr[1] != IPPROTO_TCP)
6804170268Sdarrenr					continue;
6805170268Sdarrenr
6806170268Sdarrenr				if ((nat->nat_tcpstate[0] >
6807170268Sdarrenr				     IPF_TCPS_ESTABLISHED) &&
6808170268Sdarrenr				    (nat->nat_tcpstate[1] >
6809170268Sdarrenr				     IPF_TCPS_ESTABLISHED)) {
6810255332Scy					ipf_nat_delete(softc, nat, NL_EXPIRE);
6811170268Sdarrenr					removed++;
6812170268Sdarrenr				}
6813170268Sdarrenr			}
6814170268Sdarrenr		}
6815170268Sdarrenr		break;
6816170268Sdarrenr
6817170268Sdarrenr		/*
6818170268Sdarrenr		 * Args 5-11 correspond to flushing those particular states
6819170268Sdarrenr		 * for TCP connections.
6820170268Sdarrenr		 */
6821170268Sdarrenr	case IPF_TCPS_CLOSE_WAIT :
6822170268Sdarrenr	case IPF_TCPS_FIN_WAIT_1 :
6823170268Sdarrenr	case IPF_TCPS_CLOSING :
6824170268Sdarrenr	case IPF_TCPS_LAST_ACK :
6825170268Sdarrenr	case IPF_TCPS_FIN_WAIT_2 :
6826170268Sdarrenr	case IPF_TCPS_TIME_WAIT :
6827170268Sdarrenr	case IPF_TCPS_CLOSED :
6828255332Scy		softn->ipf_nat_stats.ns_flush_state++;
6829255332Scy		tqn = softn->ipf_nat_tcptq[which].ifq_head;
6830170268Sdarrenr		while (tqn != NULL) {
6831170268Sdarrenr			nat = tqn->tqe_parent;
6832170268Sdarrenr			tqn = tqn->tqe_next;
6833255332Scy			ipf_nat_delete(softc, nat, NL_FLUSH);
6834170268Sdarrenr			removed++;
6835170268Sdarrenr		}
6836170268Sdarrenr		break;
6837255332Scy
6838170268Sdarrenr	default :
6839170268Sdarrenr		if (which < 30)
6840170268Sdarrenr			break;
6841255332Scy
6842255332Scy		softn->ipf_nat_stats.ns_flush_timeout++;
6843170268Sdarrenr		/*
6844170268Sdarrenr		 * Take a large arbitrary number to mean the number of seconds
6845170268Sdarrenr		 * for which which consider to be the maximum value we'll allow
6846170268Sdarrenr		 * the expiration to be.
6847170268Sdarrenr		 */
6848170268Sdarrenr		which = IPF_TTLVAL(which);
6849255332Scy		for (natp = &softn->ipf_nat_instances;
6850255332Scy		     ((nat = *natp) != NULL); ) {
6851255332Scy			if (softc->ipf_ticks - nat->nat_touched > which) {
6852255332Scy				ipf_nat_delete(softc, nat, NL_FLUSH);
6853170268Sdarrenr				removed++;
6854170268Sdarrenr			} else
6855170268Sdarrenr				natp = &nat->nat_next;
6856170268Sdarrenr		}
6857170268Sdarrenr		break;
6858170268Sdarrenr	}
6859170268Sdarrenr
6860170268Sdarrenr	if (which != 2) {
6861170268Sdarrenr		SPL_X(s);
6862170268Sdarrenr		return removed;
6863170268Sdarrenr	}
6864170268Sdarrenr
6865255332Scy	softn->ipf_nat_stats.ns_flush_queue++;
6866255332Scy
6867170268Sdarrenr	/*
6868255332Scy	 * Asked to remove inactive entries because the table is full, try
6869255332Scy	 * again, 3 times, if first attempt failed with a different criteria
6870255332Scy	 * each time.  The order tried in must be in decreasing age.
6871255332Scy	 * Another alternative is to implement random drop and drop N entries
6872255332Scy	 * at random until N have been freed up.
6873170268Sdarrenr	 */
6874255332Scy	if (softc->ipf_ticks - softn->ipf_nat_last_force_flush >
6875255332Scy	    IPF_TTLVAL(5)) {
6876255332Scy		softn->ipf_nat_last_force_flush = softc->ipf_ticks;
6877255332Scy
6878255332Scy		removed = ipf_queueflush(softc, ipf_nat_flush_entry,
6879255332Scy					 softn->ipf_nat_tcptq,
6880255332Scy					 softn->ipf_nat_utqe,
6881255332Scy					 &softn->ipf_nat_stats.ns_active,
6882255332Scy					 softn->ipf_nat_table_sz,
6883255332Scy					 softn->ipf_nat_table_wm_low);
6884170268Sdarrenr	}
6885170268Sdarrenr
6886170268Sdarrenr	SPL_X(s);
6887170268Sdarrenr	return removed;
6888170268Sdarrenr}
6889170268Sdarrenr
6890170268Sdarrenr
6891170268Sdarrenr/* ------------------------------------------------------------------------ */
6892255332Scy/* Function:    ipf_nat_flush_entry                                         */
6893170268Sdarrenr/* Returns:     0 - always succeeds                                         */
6894255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6895255332Scy/*              entry(I) - pointer to NAT entry                             */
6896170268Sdarrenr/* Write Locks: ipf_nat                                                     */
6897170268Sdarrenr/*                                                                          */
6898170268Sdarrenr/* This function is a stepping stone between ipf_queueflush() and           */
6899170268Sdarrenr/* nat_dlete().  It is used so we can provide a uniform interface via the   */
6900170268Sdarrenr/* ipf_queueflush() function.  Since the nat_delete() function returns void */
6901170268Sdarrenr/* we translate that to mean it always succeeds in deleting something.      */
6902170268Sdarrenr/* ------------------------------------------------------------------------ */
6903255332Scystatic int
6904255332Scyipf_nat_flush_entry(softc, entry)
6905255332Scy	ipf_main_softc_t *softc;
6906255332Scy	void *entry;
6907170268Sdarrenr{
6908255332Scy	ipf_nat_delete(softc, entry, NL_FLUSH);
6909170268Sdarrenr	return 0;
6910170268Sdarrenr}
6911172776Sdarrenr
6912172776Sdarrenr
6913172776Sdarrenr/* ------------------------------------------------------------------------ */
6914255332Scy/* Function:    ipf_nat_iterator                                            */
6915255332Scy/* Returns:     int - 0 == ok, else error                                   */
6916255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6917255332Scy/*              token(I) - pointer to ipftoken structure                    */
6918255332Scy/*              itp(I)   - pointer to ipfgeniter_t structure                */
6919255332Scy/*              obj(I)   - pointer to data description structure            */
6920255332Scy/*                                                                          */
6921255332Scy/* This function acts as a handler for the SIOCGENITER ioctls that use a    */
6922255332Scy/* generic structure to iterate through a list.  There are three different  */
6923255332Scy/* linked lists of NAT related information to go through: NAT rules, active */
6924255332Scy/* NAT mappings and the NAT fragment cache.                                 */
6925255332Scy/* ------------------------------------------------------------------------ */
6926255332Scystatic int
6927255332Scyipf_nat_iterator(softc, token, itp, obj)
6928255332Scy	ipf_main_softc_t *softc;
6929255332Scy	ipftoken_t *token;
6930255332Scy	ipfgeniter_t *itp;
6931255332Scy	ipfobj_t *obj;
6932255332Scy{
6933255332Scy	int error;
6934255332Scy
6935255332Scy	if (itp->igi_data == NULL) {
6936255332Scy		IPFERROR(60052);
6937255332Scy		return EFAULT;
6938255332Scy	}
6939255332Scy
6940255332Scy	switch (itp->igi_type)
6941255332Scy	{
6942255332Scy	case IPFGENITER_HOSTMAP :
6943255332Scy	case IPFGENITER_IPNAT :
6944255332Scy	case IPFGENITER_NAT :
6945255332Scy		error = ipf_nat_getnext(softc, token, itp, obj);
6946255332Scy		break;
6947255332Scy
6948255332Scy	case IPFGENITER_NATFRAG :
6949255332Scy		error = ipf_frag_nat_next(softc, token, itp);
6950255332Scy		break;
6951255332Scy	default :
6952255332Scy		IPFERROR(60053);
6953255332Scy		error = EINVAL;
6954255332Scy		break;
6955255332Scy	}
6956255332Scy
6957255332Scy	return error;
6958255332Scy}
6959255332Scy
6960255332Scy
6961255332Scy/* ------------------------------------------------------------------------ */
6962255332Scy/* Function:    ipf_nat_setpending                                          */
6963255332Scy/* Returns:     Nil                                                         */
6964255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6965255332Scy/*              nat(I)   - pointer to NAT structure                         */
6966255332Scy/* Locks:       ipf_nat (read or write)                                     */
6967255332Scy/*                                                                          */
6968255332Scy/* Put the NAT entry on to the pending queue - this queue has a very short  */
6969255332Scy/* lifetime where items are put that can't be deleted straight away because */
6970255332Scy/* of locking issues but we want to delete them ASAP, anyway.  In calling   */
6971255332Scy/* this function, it is assumed that the owner (if there is one, as shown   */
6972255332Scy/* by nat_me) is no longer interested in it.                                */
6973255332Scy/* ------------------------------------------------------------------------ */
6974255332Scyvoid
6975255332Scyipf_nat_setpending(softc, nat)
6976255332Scy	ipf_main_softc_t *softc;
6977255332Scy	nat_t *nat;
6978255332Scy{
6979255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6980255332Scy	ipftq_t *oifq;
6981255332Scy
6982255332Scy	oifq = nat->nat_tqe.tqe_ifq;
6983255332Scy	if (oifq != NULL)
6984255332Scy		ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq,
6985255332Scy			      &softn->ipf_nat_pending);
6986255332Scy	else
6987255332Scy		ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe,
6988255332Scy				&softn->ipf_nat_pending, nat);
6989255332Scy
6990255332Scy	if (nat->nat_me != NULL) {
6991255332Scy		*nat->nat_me = NULL;
6992255332Scy		nat->nat_me = NULL;
6993255332Scy		nat->nat_ref--;
6994255332Scy		ASSERT(nat->nat_ref >= 0);
6995255332Scy	}
6996255332Scy}
6997255332Scy
6998255332Scy
6999255332Scy/* ------------------------------------------------------------------------ */
7000255332Scy/* Function:    nat_newrewrite                                              */
7001255332Scy/* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
7002255332Scy/*                    allow rule to be moved if IPN_ROUNDR is set.          */
7003255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7004255332Scy/*              nat(I) - pointer to NAT entry                               */
7005255332Scy/*              ni(I)  - pointer to structure with misc. information needed */
7006255332Scy/*                       to create new NAT entry.                           */
7007255332Scy/* Write Lock:  ipf_nat                                                     */
7008255332Scy/*                                                                          */
7009255332Scy/* This function is responsible for setting up an active NAT session where  */
7010255332Scy/* we are changing both the source and destination parameters at the same   */
7011255332Scy/* time.  The loop in here works differently to elsewhere - each iteration  */
7012255332Scy/* is responsible for changing a single parameter that can be incremented.  */
7013255332Scy/* So one pass may increase the source IP#, next source port, next dest. IP#*/
7014255332Scy/* and the last destination port for a total of 4 iterations to try each.   */
7015255332Scy/* This is done to try and exhaustively use the translation space available.*/
7016255332Scy/* ------------------------------------------------------------------------ */
7017255332Scystatic int
7018255332Scyipf_nat_newrewrite(fin, nat, nai)
7019255332Scy	fr_info_t *fin;
7020255332Scy	nat_t *nat;
7021255332Scy	natinfo_t *nai;
7022255332Scy{
7023255332Scy	int src_search = 1;
7024255332Scy	int dst_search = 1;
7025255332Scy	fr_info_t frnat;
7026255332Scy	u_32_t flags;
7027255332Scy	u_short swap;
7028255332Scy	ipnat_t *np;
7029255332Scy	nat_t *natl;
7030255332Scy	int l = 0;
7031255332Scy	int changed;
7032255332Scy
7033255332Scy	natl = NULL;
7034255332Scy	changed = -1;
7035255332Scy	np = nai->nai_np;
7036255332Scy	flags = nat->nat_flags;
7037255332Scy	bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
7038255332Scy
7039255332Scy	nat->nat_hm = NULL;
7040255332Scy
7041255332Scy	do {
7042255332Scy		changed = -1;
7043255332Scy		/* TRACE (l, src_search, dst_search, np) */
7044338170Scy		DT4(ipf_nat_rewrite_1, int, l, int, src_search, int, dst_search, ipnat_t *, np);
7045255332Scy
7046255332Scy		if ((src_search == 0) && (np->in_spnext == 0) &&
7047255332Scy		    (dst_search == 0) && (np->in_dpnext == 0)) {
7048255332Scy			if (l > 0)
7049255332Scy				return -1;
7050255332Scy		}
7051255332Scy
7052255332Scy		/*
7053255332Scy		 * Find a new source address
7054255332Scy		 */
7055255332Scy		if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr,
7056255332Scy				     &frnat.fin_saddr) == -1) {
7057255332Scy			return -1;
7058255332Scy		}
7059255332Scy
7060255332Scy		if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) {
7061255332Scy			src_search = 0;
7062255332Scy			if (np->in_stepnext == 0)
7063255332Scy				np->in_stepnext = 1;
7064255332Scy
7065255332Scy		} else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) {
7066255332Scy			src_search = 0;
7067255332Scy			if (np->in_stepnext == 0)
7068255332Scy				np->in_stepnext = 1;
7069255332Scy
7070255332Scy		} else if (np->in_nsrcmsk == 0xffffffff) {
7071255332Scy			src_search = 0;
7072255332Scy			if (np->in_stepnext == 0)
7073255332Scy				np->in_stepnext = 1;
7074255332Scy
7075255332Scy		} else if (np->in_nsrcmsk != 0xffffffff) {
7076255332Scy			if (np->in_stepnext == 0 && changed == -1) {
7077255332Scy				np->in_snip++;
7078255332Scy				np->in_stepnext++;
7079255332Scy				changed = 0;
7080255332Scy			}
7081255332Scy		}
7082255332Scy
7083255332Scy		if ((flags & IPN_TCPUDPICMP) != 0) {
7084255332Scy			if (np->in_spnext != 0)
7085255332Scy				frnat.fin_data[0] = np->in_spnext;
7086255332Scy
7087255332Scy			/*
7088255332Scy			 * Standard port translation.  Select next port.
7089255332Scy			 */
7090255332Scy			if ((flags & IPN_FIXEDSPORT) != 0) {
7091255332Scy				np->in_stepnext = 2;
7092255332Scy			} else if ((np->in_stepnext == 1) &&
7093255332Scy				   (changed == -1) && (natl != NULL)) {
7094255332Scy				np->in_spnext++;
7095255332Scy				np->in_stepnext++;
7096255332Scy				changed = 1;
7097255332Scy				if (np->in_spnext > np->in_spmax)
7098255332Scy					np->in_spnext = np->in_spmin;
7099255332Scy			}
7100255332Scy		} else {
7101255332Scy			np->in_stepnext = 2;
7102255332Scy		}
7103255332Scy		np->in_stepnext &= 0x3;
7104255332Scy
7105255332Scy		/*
7106255332Scy		 * Find a new destination address
7107255332Scy		 */
7108255332Scy		/* TRACE (fin, np, l, frnat) */
7109338170Scy		DT4(ipf_nat_rewrite_2, frinfo_t *, fin, ipnat_t *, np, int, l, frinfo_t *, &frnat);
7110255332Scy
7111255332Scy		if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr,
7112255332Scy				     &frnat.fin_daddr) == -1)
7113255332Scy			return -1;
7114255332Scy		if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) {
7115255332Scy			dst_search = 0;
7116255332Scy			if (np->in_stepnext == 2)
7117255332Scy				np->in_stepnext = 3;
7118255332Scy
7119255332Scy		} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) {
7120255332Scy			dst_search = 0;
7121255332Scy			if (np->in_stepnext == 2)
7122255332Scy				np->in_stepnext = 3;
7123255332Scy
7124255332Scy		} else if (np->in_ndstmsk == 0xffffffff) {
7125255332Scy			dst_search = 0;
7126255332Scy			if (np->in_stepnext == 2)
7127255332Scy				np->in_stepnext = 3;
7128255332Scy
7129255332Scy		} else if (np->in_ndstmsk != 0xffffffff) {
7130255332Scy			if ((np->in_stepnext == 2) && (changed == -1) &&
7131255332Scy			    (natl != NULL)) {
7132255332Scy				changed = 2;
7133255332Scy				np->in_stepnext++;
7134255332Scy				np->in_dnip++;
7135255332Scy			}
7136255332Scy		}
7137255332Scy
7138255332Scy		if ((flags & IPN_TCPUDPICMP) != 0) {
7139255332Scy			if (np->in_dpnext != 0)
7140255332Scy				frnat.fin_data[1] = np->in_dpnext;
7141255332Scy
7142255332Scy			/*
7143255332Scy			 * Standard port translation.  Select next port.
7144255332Scy			 */
7145255332Scy			if ((flags & IPN_FIXEDDPORT) != 0) {
7146255332Scy				np->in_stepnext = 0;
7147255332Scy			} else if (np->in_stepnext == 3 && changed == -1) {
7148255332Scy				np->in_dpnext++;
7149255332Scy				np->in_stepnext++;
7150255332Scy				changed = 3;
7151255332Scy				if (np->in_dpnext > np->in_dpmax)
7152255332Scy					np->in_dpnext = np->in_dpmin;
7153255332Scy			}
7154255332Scy		} else {
7155255332Scy			if (np->in_stepnext == 3)
7156255332Scy				np->in_stepnext = 0;
7157255332Scy		}
7158255332Scy
7159255332Scy		/* TRACE (frnat) */
7160338170Scy		DT1(ipf_nat_rewrite_3, frinfo_t *, &frnat);
7161255332Scy
7162255332Scy		/*
7163255332Scy		 * Here we do a lookup of the connection as seen from
7164255332Scy		 * the outside.  If an IP# pair already exists, try
7165255332Scy		 * again.  So if you have A->B becomes C->B, you can
7166255332Scy		 * also have D->E become C->E but not D->B causing
7167255332Scy		 * another C->B.  Also take protocol and ports into
7168255332Scy		 * account when determining whether a pre-existing
7169255332Scy		 * NAT setup will cause an external conflict where
7170255332Scy		 * this is appropriate.
7171255332Scy		 *
7172255332Scy		 * fin_data[] is swapped around because we are doing a
7173255332Scy		 * lookup of the packet is if it were moving in the opposite
7174255332Scy		 * direction of the one we are working with now.
7175255332Scy		 */
7176255332Scy		if (flags & IPN_TCPUDP) {
7177255332Scy			swap = frnat.fin_data[0];
7178255332Scy			frnat.fin_data[0] = frnat.fin_data[1];
7179255332Scy			frnat.fin_data[1] = swap;
7180255332Scy		}
7181255332Scy		if (fin->fin_out == 1) {
7182255332Scy			natl = ipf_nat_inlookup(&frnat,
7183255332Scy						flags & ~(SI_WILDP|NAT_SEARCH),
7184255332Scy						(u_int)frnat.fin_p,
7185255332Scy						frnat.fin_dst, frnat.fin_src);
7186255332Scy
7187255332Scy		} else {
7188255332Scy			natl = ipf_nat_outlookup(&frnat,
7189255332Scy						 flags & ~(SI_WILDP|NAT_SEARCH),
7190255332Scy						 (u_int)frnat.fin_p,
7191255332Scy						 frnat.fin_dst, frnat.fin_src);
7192255332Scy		}
7193255332Scy		if (flags & IPN_TCPUDP) {
7194255332Scy			swap = frnat.fin_data[0];
7195255332Scy			frnat.fin_data[0] = frnat.fin_data[1];
7196255332Scy			frnat.fin_data[1] = swap;
7197255332Scy		}
7198255332Scy
7199255332Scy		/* TRACE natl, in_stepnext, l */
7200338170Scy		DT3(ipf_nat_rewrite_2, nat_t *, natl, ipnat_t *, np , int, l);
7201255332Scy
7202255332Scy		if ((natl != NULL) && (l > 8))	/* XXX 8 is arbitrary */
7203255332Scy			return -1;
7204255332Scy
7205255332Scy		np->in_stepnext &= 0x3;
7206255332Scy
7207255332Scy		l++;
7208255332Scy		changed = -1;
7209255332Scy	} while (natl != NULL);
7210255332Scy
7211255332Scy	nat->nat_osrcip = fin->fin_src;
7212255332Scy	nat->nat_odstip = fin->fin_dst;
7213255332Scy	nat->nat_nsrcip = frnat.fin_src;
7214255332Scy	nat->nat_ndstip = frnat.fin_dst;
7215255332Scy
7216255332Scy	if ((flags & IPN_TCPUDP) != 0) {
7217255332Scy		nat->nat_osport = htons(fin->fin_data[0]);
7218255332Scy		nat->nat_odport = htons(fin->fin_data[1]);
7219255332Scy		nat->nat_nsport = htons(frnat.fin_data[0]);
7220255332Scy		nat->nat_ndport = htons(frnat.fin_data[1]);
7221255332Scy	} else if ((flags & IPN_ICMPQUERY) != 0) {
7222255332Scy		nat->nat_oicmpid = fin->fin_data[1];
7223255332Scy		nat->nat_nicmpid = frnat.fin_data[1];
7224255332Scy	}
7225255332Scy
7226255332Scy	return 0;
7227255332Scy}
7228255332Scy
7229255332Scy
7230255332Scy/* ------------------------------------------------------------------------ */
7231255332Scy/* Function:    nat_newdivert                                               */
7232255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7233255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7234255332Scy/*              nat(I) - pointer to NAT entry                               */
7235255332Scy/*              ni(I)  - pointer to structure with misc. information needed */
7236255332Scy/*                       to create new NAT entry.                           */
7237255332Scy/* Write Lock:  ipf_nat                                                     */
7238255332Scy/*                                                                          */
7239255332Scy/* Create a new NAT  divert session as defined by the NAT rule.  This is    */
7240255332Scy/* somewhat different to other NAT session creation routines because we     */
7241255332Scy/* do not iterate through either port numbers or IP addresses, searching    */
7242255332Scy/* for a unique mapping, however, a complimentary duplicate check is made.  */
7243255332Scy/* ------------------------------------------------------------------------ */
7244255332Scystatic int
7245255332Scyipf_nat_newdivert(fin, nat, nai)
7246255332Scy	fr_info_t *fin;
7247255332Scy	nat_t *nat;
7248255332Scy	natinfo_t *nai;
7249255332Scy{
7250255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
7251255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7252255332Scy	fr_info_t frnat;
7253255332Scy	ipnat_t *np;
7254255332Scy	nat_t *natl;
7255255332Scy	int p;
7256255332Scy
7257255332Scy	np = nai->nai_np;
7258255332Scy	bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
7259255332Scy
7260255332Scy	nat->nat_pr[0] = 0;
7261255332Scy	nat->nat_osrcaddr = fin->fin_saddr;
7262255332Scy	nat->nat_odstaddr = fin->fin_daddr;
7263255332Scy	frnat.fin_saddr = htonl(np->in_snip);
7264255332Scy	frnat.fin_daddr = htonl(np->in_dnip);
7265255332Scy	if ((nat->nat_flags & IPN_TCPUDP) != 0) {
7266255332Scy		nat->nat_osport = htons(fin->fin_data[0]);
7267255332Scy		nat->nat_odport = htons(fin->fin_data[1]);
7268255332Scy	} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
7269255332Scy		nat->nat_oicmpid = fin->fin_data[1];
7270255332Scy	}
7271255332Scy
7272255332Scy	if (np->in_redir & NAT_DIVERTUDP) {
7273255332Scy		frnat.fin_data[0] = np->in_spnext;
7274255332Scy		frnat.fin_data[1] = np->in_dpnext;
7275255332Scy		frnat.fin_flx |= FI_TCPUDP;
7276255332Scy		p = IPPROTO_UDP;
7277255332Scy	} else {
7278255332Scy		frnat.fin_flx &= ~FI_TCPUDP;
7279255332Scy		p = IPPROTO_IPIP;
7280255332Scy	}
7281255332Scy
7282255332Scy	if (fin->fin_out == 1) {
7283255332Scy		natl = ipf_nat_inlookup(&frnat, 0, p,
7284255332Scy					frnat.fin_dst, frnat.fin_src);
7285255332Scy
7286255332Scy	} else {
7287255332Scy		natl = ipf_nat_outlookup(&frnat, 0, p,
7288255332Scy					 frnat.fin_dst, frnat.fin_src);
7289255332Scy	}
7290255332Scy
7291255332Scy	if (natl != NULL) {
7292255332Scy		NBUMPSIDED(fin->fin_out, ns_divert_exist);
7293338170Scy		DT3(ns_divert_exist, fr_info_t *, fin, nat_t *, nat, natinfo_t, nai);
7294255332Scy		return -1;
7295255332Scy	}
7296255332Scy
7297255332Scy	nat->nat_nsrcaddr = frnat.fin_saddr;
7298255332Scy	nat->nat_ndstaddr = frnat.fin_daddr;
7299255332Scy	if ((nat->nat_flags & IPN_TCPUDP) != 0) {
7300255332Scy		nat->nat_nsport = htons(frnat.fin_data[0]);
7301255332Scy		nat->nat_ndport = htons(frnat.fin_data[1]);
7302255332Scy	} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
7303255332Scy		nat->nat_nicmpid = frnat.fin_data[1];
7304255332Scy	}
7305255332Scy
7306255332Scy	nat->nat_pr[fin->fin_out] = fin->fin_p;
7307255332Scy	nat->nat_pr[1 - fin->fin_out] = p;
7308255332Scy
7309255332Scy	if (np->in_redir & NAT_REDIRECT)
7310255332Scy		nat->nat_dir = NAT_DIVERTIN;
7311255332Scy	else
7312255332Scy		nat->nat_dir = NAT_DIVERTOUT;
7313255332Scy
7314255332Scy	return 0;
7315255332Scy}
7316255332Scy
7317255332Scy
7318255332Scy/* ------------------------------------------------------------------------ */
7319255332Scy/* Function:    nat_builddivertmp                                           */
7320255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7321255332Scy/* Parameters:  softn(I) - pointer to NAT context structure                 */
7322255332Scy/*              np(I)    - pointer to a NAT rule                            */
7323255332Scy/*                                                                          */
7324255332Scy/* For divert rules, a skeleton packet representing what will be prepended  */
7325255332Scy/* to the real packet is created.  Even though we don't have the full       */
7326255332Scy/* packet here, a checksum is calculated that we update later when we       */
7327255332Scy/* fill in the final details.  At present a 0 checksum for UDP is being set */
7328255332Scy/* here because it is expected that divert will be used for localhost.      */
7329255332Scy/* ------------------------------------------------------------------------ */
7330255332Scystatic int
7331255332Scyipf_nat_builddivertmp(softn, np)
7332255332Scy	ipf_nat_softc_t *softn;
7333255332Scy	ipnat_t *np;
7334255332Scy{
7335255332Scy	udphdr_t *uh;
7336255332Scy	size_t len;
7337255332Scy	ip_t *ip;
7338255332Scy
7339255332Scy	if ((np->in_redir & NAT_DIVERTUDP) != 0)
7340255332Scy		len = sizeof(ip_t) + sizeof(udphdr_t);
7341255332Scy	else
7342255332Scy		len = sizeof(ip_t);
7343255332Scy
7344255332Scy	ALLOC_MB_T(np->in_divmp, len);
7345255332Scy	if (np->in_divmp == NULL) {
7346255332Scy		NBUMPD(ipf_nat_stats, ns_divert_build);
7347255332Scy		return -1;
7348255332Scy	}
7349255332Scy
7350255332Scy	/*
7351255332Scy	 * First, the header to get the packet diverted to the new destination
7352255332Scy	 */
7353255332Scy	ip = MTOD(np->in_divmp, ip_t *);
7354255332Scy	IP_V_A(ip, 4);
7355255332Scy	IP_HL_A(ip, 5);
7356255332Scy	ip->ip_tos = 0;
7357255332Scy	if ((np->in_redir & NAT_DIVERTUDP) != 0)
7358255332Scy		ip->ip_p = IPPROTO_UDP;
7359255332Scy	else
7360255332Scy		ip->ip_p = IPPROTO_IPIP;
7361255332Scy	ip->ip_ttl = 255;
7362255332Scy	ip->ip_off = 0;
7363255332Scy	ip->ip_sum = 0;
7364255332Scy	ip->ip_len = htons(len);
7365255332Scy	ip->ip_id = 0;
7366255332Scy	ip->ip_src.s_addr = htonl(np->in_snip);
7367255332Scy	ip->ip_dst.s_addr = htonl(np->in_dnip);
7368255332Scy	ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip));
7369255332Scy
7370255332Scy	if (np->in_redir & NAT_DIVERTUDP) {
7371255332Scy		uh = (udphdr_t *)(ip + 1);
7372255332Scy		uh->uh_sum = 0;
7373255332Scy		uh->uh_ulen = 8;
7374255332Scy		uh->uh_sport = htons(np->in_spnext);
7375255332Scy		uh->uh_dport = htons(np->in_dpnext);
7376255332Scy	}
7377255332Scy
7378255332Scy	return 0;
7379255332Scy}
7380255332Scy
7381255332Scy
7382255332Scy#define	MINDECAP	(sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t))
7383255332Scy
7384255332Scy/* ------------------------------------------------------------------------ */
7385255332Scy/* Function:    nat_decap                                                   */
7386255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7387255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7388255332Scy/*              nat(I) - pointer to current NAT session                     */
7389255332Scy/*                                                                          */
7390255332Scy/* This function is responsible for undoing a packet's encapsulation in the */
7391255332Scy/* reverse of an encap/divert rule.  After removing the outer encapsulation */
7392255332Scy/* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/
7393255332Scy/* match the "new" packet as it may still be used by IPFilter elsewhere.    */
7394255332Scy/* We use "dir" here as the basis for some of the expectations about the    */
7395255332Scy/* outer header.  If we return an error, the goal is to leave the original  */
7396255332Scy/* packet information undisturbed - this falls short at the end where we'd  */
7397255332Scy/* need to back a backup copy of "fin" - expensive.                         */
7398255332Scy/* ------------------------------------------------------------------------ */
7399255332Scystatic int
7400255332Scyipf_nat_decap(fin, nat)
7401255332Scy	fr_info_t *fin;
7402255332Scy	nat_t *nat;
7403255332Scy{
7404255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
7405255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7406255332Scy	char *hdr;
7407255332Scy	int hlen;
7408255332Scy	int skip;
7409255332Scy	mb_t *m;
7410255332Scy
7411255332Scy	if ((fin->fin_flx & FI_ICMPERR) != 0) {
7412255332Scy		/*
7413255332Scy		 * ICMP packets don't get decapsulated, instead what we need
7414255332Scy		 * to do is change the ICMP reply from including (in the data
7415255332Scy		 * portion for errors) the encapsulated packet that we sent
7416255332Scy		 * out to something that resembles the original packet prior
7417255332Scy		 * to encapsulation.  This isn't done here - all we're doing
7418255332Scy		 * here is changing the outer address to ensure that it gets
7419255332Scy		 * targetted back to the correct system.
7420255332Scy		 */
7421255332Scy
7422255332Scy		if (nat->nat_dir & NAT_OUTBOUND) {
7423255332Scy			u_32_t sum1, sum2, sumd;
7424255332Scy
7425255332Scy			sum1 = ntohl(fin->fin_daddr);
7426255332Scy			sum2 = ntohl(nat->nat_osrcaddr);
7427255332Scy			CALC_SUMD(sum1, sum2, sumd);
7428255332Scy			fin->fin_ip->ip_dst = nat->nat_osrcip;
7429255332Scy			fin->fin_daddr = nat->nat_osrcaddr;
7430369272Scy#if !defined(_KERNEL) || SOLARIS
7431255332Scy			ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0);
7432255332Scy#endif
7433255332Scy		}
7434255332Scy		return 0;
7435255332Scy	}
7436255332Scy
7437255332Scy	m = fin->fin_m;
7438255332Scy	skip = fin->fin_hlen;
7439255332Scy
7440255332Scy	switch (nat->nat_dir)
7441255332Scy	{
7442255332Scy	case NAT_DIVERTIN :
7443255332Scy	case NAT_DIVERTOUT :
7444255332Scy		if (fin->fin_plen < MINDECAP)
7445255332Scy			return -1;
7446255332Scy		skip += sizeof(udphdr_t);
7447255332Scy		break;
7448255332Scy
7449255332Scy	case NAT_ENCAPIN :
7450255332Scy	case NAT_ENCAPOUT :
7451255332Scy		if (fin->fin_plen < (skip + sizeof(ip_t)))
7452255332Scy			return -1;
7453255332Scy		break;
7454255332Scy	default :
7455255332Scy		return -1;
7456255332Scy		/* NOTREACHED */
7457255332Scy	}
7458255332Scy
7459255332Scy	/*
7460255332Scy	 * The aim here is to keep the original packet details in "fin" for
7461255332Scy	 * as long as possible so that returning with an error is for the
7462255332Scy	 * original packet and there is little undoing work to do.
7463255332Scy	 */
7464255332Scy	if (M_LEN(m) < skip + sizeof(ip_t)) {
7465255332Scy		if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1)
7466255332Scy			return -1;
7467255332Scy	}
7468255332Scy
7469255332Scy	hdr = MTOD(fin->fin_m, char *);
7470255332Scy	fin->fin_ip = (ip_t *)(hdr + skip);
7471255332Scy	hlen = IP_HL(fin->fin_ip) << 2;
7472255332Scy
7473255332Scy	if (ipf_pr_pullup(fin, skip + hlen) == -1) {
7474255332Scy		NBUMPSIDED(fin->fin_out, ns_decap_pullup);
7475255332Scy		return -1;
7476255332Scy	}
7477255332Scy
7478255332Scy	fin->fin_hlen = hlen;
7479255332Scy	fin->fin_dlen -= skip;
7480255332Scy	fin->fin_plen -= skip;
7481255332Scy	fin->fin_ipoff += skip;
7482255332Scy
7483255332Scy	if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) {
7484255332Scy		NBUMPSIDED(fin->fin_out, ns_decap_bad);
7485255332Scy		return -1;
7486255332Scy	}
7487255332Scy
7488255332Scy	return skip;
7489255332Scy}
7490255332Scy
7491255332Scy
7492255332Scy/* ------------------------------------------------------------------------ */
7493255332Scy/* Function:    nat_nextaddr                                                */
7494255332Scy/* Returns:     int - -1 == bad input (no new address),                     */
7495255332Scy/*                     0 == success and dst has new address                 */
7496255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7497255332Scy/*              na(I)  - how to generate new address                        */
7498255332Scy/*              old(I) - original address being replaced                    */
7499255332Scy/*              dst(O) - where to put the new address                       */
7500255332Scy/* Write Lock:  ipf_nat                                                     */
7501255332Scy/*                                                                          */
7502255332Scy/* This function uses the contents of the "na" structure, in combination    */
7503255332Scy/* with "old" to produce a new address to store in "dst".  Not all of the   */
7504255332Scy/* possible uses of "na" will result in a new address.                      */
7505255332Scy/* ------------------------------------------------------------------------ */
7506255332Scystatic int
7507255332Scyipf_nat_nextaddr(fin, na, old, dst)
7508255332Scy	fr_info_t *fin;
7509255332Scy	nat_addr_t *na;
7510255332Scy	u_32_t *old, *dst;
7511255332Scy{
7512255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
7513255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7514255332Scy	u_32_t amin, amax, new;
7515255332Scy	i6addr_t newip;
7516255332Scy	int error;
7517255332Scy
7518255332Scy	new = 0;
7519255332Scy	amin = na->na_addr[0].in4.s_addr;
7520255332Scy
7521255332Scy	switch (na->na_atype)
7522255332Scy	{
7523255332Scy	case FRI_RANGE :
7524255332Scy		amax = na->na_addr[1].in4.s_addr;
7525255332Scy		break;
7526255332Scy
7527255332Scy	case FRI_NETMASKED :
7528255332Scy	case FRI_DYNAMIC :
7529255332Scy	case FRI_NORMAL :
7530255332Scy		/*
7531255332Scy		 * Compute the maximum address by adding the inverse of the
7532255332Scy		 * netmask to the minimum address.
7533255332Scy		 */
7534255332Scy		amax = ~na->na_addr[1].in4.s_addr;
7535255332Scy		amax |= amin;
7536255332Scy		break;
7537255332Scy
7538255332Scy	case FRI_LOOKUP :
7539255332Scy		break;
7540255332Scy
7541255332Scy	case FRI_BROADCAST :
7542255332Scy	case FRI_PEERADDR :
7543255332Scy	case FRI_NETWORK :
7544255332Scy	default :
7545338170Scy		DT4(ns_na_atype, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new);
7546255332Scy		return -1;
7547255332Scy	}
7548255332Scy
7549255332Scy	error = -1;
7550255332Scy
7551255332Scy	if (na->na_atype == FRI_LOOKUP) {
7552255332Scy		if (na->na_type == IPLT_DSTLIST) {
7553255332Scy			error = ipf_dstlist_select_node(fin, na->na_ptr, dst,
7554255332Scy							NULL);
7555255332Scy		} else {
7556255332Scy			NBUMPSIDE(fin->fin_out, ns_badnextaddr);
7557338170Scy			DT4(ns_badnextaddr_1, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new);
7558255332Scy		}
7559255332Scy
7560255332Scy	} else if (na->na_atype == IPLT_NONE) {
7561255332Scy		/*
7562255332Scy		 * 0/0 as the new address means leave it alone.
7563255332Scy		 */
7564255332Scy		if (na->na_addr[0].in4.s_addr == 0 &&
7565255332Scy		    na->na_addr[1].in4.s_addr == 0) {
7566255332Scy			new = *old;
7567255332Scy
7568255332Scy		/*
7569255332Scy		 * 0/32 means get the interface's address
7570255332Scy		 */
7571255332Scy		} else if (na->na_addr[0].in4.s_addr == 0 &&
7572255332Scy			   na->na_addr[1].in4.s_addr == 0xffffffff) {
7573255332Scy			if (ipf_ifpaddr(softc, 4, na->na_atype,
7574255332Scy					fin->fin_ifp, &newip, NULL) == -1) {
7575255332Scy				NBUMPSIDED(fin->fin_out, ns_ifpaddrfail);
7576338170Scy				DT4(ns_ifpaddrfail, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new);
7577255332Scy				return -1;
7578255332Scy			}
7579255332Scy			new = newip.in4.s_addr;
7580255332Scy		} else {
7581255332Scy			new = htonl(na->na_nextip);
7582255332Scy		}
7583255332Scy		*dst = new;
7584255332Scy		error = 0;
7585255332Scy
7586255332Scy	} else {
7587255332Scy		NBUMPSIDE(fin->fin_out, ns_badnextaddr);
7588338170Scy		DT4(ns_badnextaddr_2, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new);
7589255332Scy	}
7590255332Scy
7591255332Scy	return error;
7592255332Scy}
7593255332Scy
7594255332Scy
7595255332Scy/* ------------------------------------------------------------------------ */
7596255332Scy/* Function:    nat_nextaddrinit                                            */
7597255332Scy/* Returns:     int - 0 == success, else error number                       */
7598255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7599255332Scy/*              na(I)      - NAT address information for generating new addr*/
7600255332Scy/*              initial(I) - flag indicating if it is the first call for    */
7601255332Scy/*                           this "na" structure.                           */
7602255332Scy/*              ifp(I)     - network interface to derive address            */
7603255332Scy/*                           information from.                              */
7604255332Scy/*                                                                          */
7605255332Scy/* This function is expected to be called in two scenarious: when a new NAT */
7606255332Scy/* rule is loaded into the kernel and when the list of NAT rules is sync'd  */
7607255332Scy/* up with the valid network interfaces (possibly due to them changing.)    */
7608255332Scy/* To distinguish between these, the "initial" parameter is used.  If it is */
7609255332Scy/* 1 then this indicates the rule has just been reloaded and 0 for when we  */
7610255332Scy/* are updating information.  This difference is important because in       */
7611255332Scy/* instances where we are not updating address information associated with  */
7612255332Scy/* a network interface, we don't want to disturb what the "next" address to */
7613255332Scy/* come out of ipf_nat_nextaddr() will be.                                  */
7614255332Scy/* ------------------------------------------------------------------------ */
7615255332Scystatic int
7616255332Scyipf_nat_nextaddrinit(softc, base, na, initial, ifp)
7617255332Scy	ipf_main_softc_t *softc;
7618255332Scy	char *base;
7619255332Scy	nat_addr_t *na;
7620255332Scy	int initial;
7621255332Scy	void *ifp;
7622255332Scy{
7623255332Scy
7624255332Scy	switch (na->na_atype)
7625255332Scy	{
7626255332Scy	case FRI_LOOKUP :
7627255332Scy		if (na->na_subtype == 0) {
7628255332Scy			na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT,
7629255332Scy							na->na_type,
7630255332Scy							na->na_num,
7631255332Scy							&na->na_func);
7632255332Scy		} else if (na->na_subtype == 1) {
7633255332Scy			na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT,
7634255332Scy							 na->na_type,
7635255332Scy							 base + na->na_num,
7636255332Scy							 &na->na_func);
7637255332Scy		}
7638255332Scy		if (na->na_func == NULL) {
7639255332Scy			IPFERROR(60060);
7640255332Scy			return ESRCH;
7641255332Scy		}
7642255332Scy		if (na->na_ptr == NULL) {
7643255332Scy			IPFERROR(60056);
7644255332Scy			return ESRCH;
7645255332Scy		}
7646255332Scy		break;
7647255332Scy
7648255332Scy	case FRI_DYNAMIC :
7649255332Scy	case FRI_BROADCAST :
7650255332Scy	case FRI_NETWORK :
7651255332Scy	case FRI_NETMASKED :
7652255332Scy	case FRI_PEERADDR :
7653255332Scy		if (ifp != NULL)
7654255332Scy			(void )ipf_ifpaddr(softc, 4, na->na_atype, ifp,
7655255332Scy					   &na->na_addr[0], &na->na_addr[1]);
7656255332Scy		break;
7657255332Scy
7658255332Scy	case FRI_SPLIT :
7659255332Scy	case FRI_RANGE :
7660255332Scy		if (initial)
7661255332Scy			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr);
7662255332Scy		break;
7663255332Scy
7664255332Scy	case FRI_NONE :
7665255332Scy		na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr;
7666255332Scy		return 0;
7667255332Scy
7668255332Scy	case FRI_NORMAL :
7669255332Scy		na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr;
7670255332Scy		break;
7671255332Scy
7672255332Scy	default :
7673255332Scy		IPFERROR(60054);
7674255332Scy		return EINVAL;
7675255332Scy	}
7676255332Scy
7677255332Scy	if (initial && (na->na_atype == FRI_NORMAL)) {
7678255332Scy		if (na->na_addr[0].in4.s_addr == 0) {
7679255332Scy			if ((na->na_addr[1].in4.s_addr == 0xffffffff) ||
7680255332Scy			    (na->na_addr[1].in4.s_addr == 0)) {
7681255332Scy				return 0;
7682255332Scy			}
7683255332Scy		}
7684255332Scy
7685255332Scy		if (na->na_addr[1].in4.s_addr == 0xffffffff) {
7686255332Scy			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr);
7687255332Scy		} else {
7688255332Scy			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1;
7689255332Scy		}
7690255332Scy	}
7691255332Scy
7692255332Scy	return 0;
7693255332Scy}
7694255332Scy
7695255332Scy
7696255332Scy/* ------------------------------------------------------------------------ */
7697255332Scy/* Function:    ipf_nat_matchflush                                          */
7698255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7699255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7700255332Scy/*              softn(I) - pointer to NAT context structure                 */
7701255332Scy/*              nat(I)   - pointer to current NAT session                   */
7702255332Scy/*                                                                          */
7703255332Scy/* ------------------------------------------------------------------------ */
7704255332Scystatic int
7705255332Scyipf_nat_matchflush(softc, softn, data)
7706255332Scy	ipf_main_softc_t *softc;
7707255332Scy	ipf_nat_softc_t *softn;
7708255332Scy	caddr_t data;
7709255332Scy{
7710255332Scy	int *array, flushed, error;
7711255332Scy	nat_t *nat, *natnext;
7712255332Scy	ipfobj_t obj;
7713255332Scy
7714255332Scy	error = ipf_matcharray_load(softc, data, &obj, &array);
7715255332Scy	if (error != 0)
7716255332Scy		return error;
7717255332Scy
7718255332Scy	flushed = 0;
7719255332Scy
7720255332Scy	for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) {
7721255332Scy		natnext = nat->nat_next;
7722255332Scy		if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) {
7723255332Scy			ipf_nat_delete(softc, nat, NL_FLUSH);
7724255332Scy			flushed++;
7725255332Scy		}
7726255332Scy	}
7727255332Scy
7728255332Scy	obj.ipfo_retval = flushed;
7729255332Scy	error = BCOPYOUT(&obj, data, sizeof(obj));
7730255332Scy
7731255332Scy	KFREES(array, array[0] * sizeof(*array));
7732255332Scy
7733255332Scy	return error;
7734255332Scy}
7735255332Scy
7736255332Scy
7737255332Scy/* ------------------------------------------------------------------------ */
7738255332Scy/* Function:    ipf_nat_matcharray                                          */
7739255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7740255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7741255332Scy/*              nat(I) - pointer to current NAT session                     */
7742255332Scy/*                                                                          */
7743255332Scy/* ------------------------------------------------------------------------ */
7744255332Scystatic int
7745255332Scyipf_nat_matcharray(nat, array, ticks)
7746255332Scy	nat_t *nat;
7747255332Scy	int *array;
7748255332Scy	u_long ticks;
7749255332Scy{
7750255332Scy	int i, n, *x, e, p;
7751255332Scy
7752255332Scy	e = 0;
7753255332Scy	n = array[0];
7754255332Scy	x = array + 1;
7755255332Scy
7756255332Scy	for (; n > 0; x += 3 + x[2]) {
7757255332Scy		if (x[0] == IPF_EXP_END)
7758255332Scy			break;
7759255332Scy		e = 0;
7760255332Scy
7761255332Scy		n -= x[2] + 3;
7762255332Scy		if (n < 0)
7763255332Scy			break;
7764255332Scy
7765255332Scy		p = x[0] >> 16;
7766255332Scy		if (p != 0 && p != nat->nat_pr[1])
7767255332Scy			break;
7768255332Scy
7769255332Scy		switch (x[0])
7770255332Scy		{
7771255332Scy		case IPF_EXP_IP_PR :
7772255332Scy			for (i = 0; !e && i < x[2]; i++) {
7773255332Scy				e |= (nat->nat_pr[1] == x[i + 3]);
7774255332Scy			}
7775255332Scy			break;
7776255332Scy
7777255332Scy		case IPF_EXP_IP_SRCADDR :
7778255332Scy			if (nat->nat_v[0] == 4) {
7779255332Scy				for (i = 0; !e && i < x[2]; i++) {
7780255332Scy					e |= ((nat->nat_osrcaddr & x[i + 4]) ==
7781255332Scy					      x[i + 3]);
7782255332Scy				}
7783255332Scy			}
7784255332Scy			if (nat->nat_v[1] == 4) {
7785255332Scy				for (i = 0; !e && i < x[2]; i++) {
7786255332Scy					e |= ((nat->nat_nsrcaddr & x[i + 4]) ==
7787255332Scy					      x[i + 3]);
7788255332Scy				}
7789255332Scy			}
7790255332Scy			break;
7791255332Scy
7792255332Scy		case IPF_EXP_IP_DSTADDR :
7793255332Scy			if (nat->nat_v[0] == 4) {
7794255332Scy				for (i = 0; !e && i < x[2]; i++) {
7795255332Scy					e |= ((nat->nat_odstaddr & x[i + 4]) ==
7796255332Scy					      x[i + 3]);
7797255332Scy				}
7798255332Scy			}
7799255332Scy			if (nat->nat_v[1] == 4) {
7800255332Scy				for (i = 0; !e && i < x[2]; i++) {
7801255332Scy					e |= ((nat->nat_ndstaddr & x[i + 4]) ==
7802255332Scy					      x[i + 3]);
7803255332Scy				}
7804255332Scy			}
7805255332Scy			break;
7806255332Scy
7807255332Scy		case IPF_EXP_IP_ADDR :
7808255332Scy			for (i = 0; !e && i < x[2]; i++) {
7809255332Scy				if (nat->nat_v[0] == 4) {
7810255332Scy					e |= ((nat->nat_osrcaddr & x[i + 4]) ==
7811255332Scy					      x[i + 3]);
7812255332Scy				}
7813255332Scy				if (nat->nat_v[1] == 4) {
7814255332Scy					e |= ((nat->nat_nsrcaddr & x[i + 4]) ==
7815255332Scy					      x[i + 3]);
7816255332Scy				}
7817255332Scy				if (nat->nat_v[0] == 4) {
7818255332Scy					e |= ((nat->nat_odstaddr & x[i + 4]) ==
7819255332Scy					      x[i + 3]);
7820255332Scy				}
7821255332Scy				if (nat->nat_v[1] == 4) {
7822255332Scy					e |= ((nat->nat_ndstaddr & x[i + 4]) ==
7823255332Scy					      x[i + 3]);
7824255332Scy				}
7825255332Scy			}
7826255332Scy			break;
7827255332Scy
7828255332Scy#ifdef USE_INET6
7829255332Scy		case IPF_EXP_IP6_SRCADDR :
7830255332Scy			if (nat->nat_v[0] == 6) {
7831255332Scy				for (i = 0; !e && i < x[3]; i++) {
7832255332Scy					e |= IP6_MASKEQ(&nat->nat_osrc6,
7833255332Scy							x + i + 7, x + i + 3);
7834255332Scy				}
7835255332Scy			}
7836255332Scy			if (nat->nat_v[1] == 6) {
7837255332Scy				for (i = 0; !e && i < x[3]; i++) {
7838255332Scy					e |= IP6_MASKEQ(&nat->nat_nsrc6,
7839255332Scy							x + i + 7, x + i + 3);
7840255332Scy				}
7841255332Scy			}
7842255332Scy			break;
7843255332Scy
7844255332Scy		case IPF_EXP_IP6_DSTADDR :
7845255332Scy			if (nat->nat_v[0] == 6) {
7846255332Scy				for (i = 0; !e && i < x[3]; i++) {
7847255332Scy					e |= IP6_MASKEQ(&nat->nat_odst6,
7848255332Scy							x + i + 7,
7849255332Scy							x + i + 3);
7850255332Scy				}
7851255332Scy			}
7852255332Scy			if (nat->nat_v[1] == 6) {
7853255332Scy				for (i = 0; !e && i < x[3]; i++) {
7854255332Scy					e |= IP6_MASKEQ(&nat->nat_ndst6,
7855255332Scy							x + i + 7,
7856255332Scy							x + i + 3);
7857255332Scy				}
7858255332Scy			}
7859255332Scy			break;
7860255332Scy
7861255332Scy		case IPF_EXP_IP6_ADDR :
7862255332Scy			for (i = 0; !e && i < x[3]; i++) {
7863255332Scy				if (nat->nat_v[0] == 6) {
7864255332Scy					e |= IP6_MASKEQ(&nat->nat_osrc6,
7865255332Scy							x + i + 7,
7866255332Scy							x + i + 3);
7867255332Scy				}
7868255332Scy				if (nat->nat_v[0] == 6) {
7869255332Scy					e |= IP6_MASKEQ(&nat->nat_odst6,
7870255332Scy							x + i + 7,
7871255332Scy							x + i + 3);
7872255332Scy				}
7873255332Scy				if (nat->nat_v[1] == 6) {
7874255332Scy					e |= IP6_MASKEQ(&nat->nat_nsrc6,
7875255332Scy							x + i + 7,
7876255332Scy							x + i + 3);
7877255332Scy				}
7878255332Scy				if (nat->nat_v[1] == 6) {
7879255332Scy					e |= IP6_MASKEQ(&nat->nat_ndst6,
7880255332Scy							x + i + 7,
7881255332Scy							x + i + 3);
7882255332Scy				}
7883255332Scy			}
7884255332Scy			break;
7885255332Scy#endif
7886255332Scy
7887255332Scy		case IPF_EXP_UDP_PORT :
7888255332Scy		case IPF_EXP_TCP_PORT :
7889255332Scy			for (i = 0; !e && i < x[2]; i++) {
7890255332Scy				e |= (nat->nat_nsport == x[i + 3]) ||
7891255332Scy				     (nat->nat_ndport == x[i + 3]);
7892255332Scy			}
7893255332Scy			break;
7894255332Scy
7895255332Scy		case IPF_EXP_UDP_SPORT :
7896255332Scy		case IPF_EXP_TCP_SPORT :
7897255332Scy			for (i = 0; !e && i < x[2]; i++) {
7898255332Scy				e |= (nat->nat_nsport == x[i + 3]);
7899255332Scy			}
7900255332Scy			break;
7901255332Scy
7902255332Scy		case IPF_EXP_UDP_DPORT :
7903255332Scy		case IPF_EXP_TCP_DPORT :
7904255332Scy			for (i = 0; !e && i < x[2]; i++) {
7905255332Scy				e |= (nat->nat_ndport == x[i + 3]);
7906255332Scy			}
7907255332Scy			break;
7908255332Scy
7909255332Scy		case IPF_EXP_TCP_STATE :
7910255332Scy			for (i = 0; !e && i < x[2]; i++) {
7911255332Scy				e |= (nat->nat_tcpstate[0] == x[i + 3]) ||
7912255332Scy				     (nat->nat_tcpstate[1] == x[i + 3]);
7913255332Scy			}
7914255332Scy			break;
7915255332Scy
7916255332Scy		case IPF_EXP_IDLE_GT :
7917255332Scy			e |= (ticks - nat->nat_touched > x[3]);
7918255332Scy			break;
7919255332Scy		}
7920255332Scy		e ^= x[1];
7921255332Scy
7922255332Scy		if (!e)
7923255332Scy			break;
7924255332Scy	}
7925255332Scy
7926255332Scy	return e;
7927255332Scy}
7928255332Scy
7929255332Scy
7930255332Scy/* ------------------------------------------------------------------------ */
7931255332Scy/* Function:    ipf_nat_gettable                                            */
7932172776Sdarrenr/* Returns:     int     - 0 = success, else error                           */
7933255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7934255332Scy/*              softn(I) - pointer to NAT context structure                 */
7935255332Scy/*              data(I)  - pointer to ioctl data                            */
7936172776Sdarrenr/*                                                                          */
7937172776Sdarrenr/* This function handles ioctl requests for tables of nat information.      */
7938172776Sdarrenr/* At present the only table it deals with is the hash bucket statistics.   */
7939172776Sdarrenr/* ------------------------------------------------------------------------ */
7940255332Scystatic int
7941255332Scyipf_nat_gettable(softc, softn, data)
7942255332Scy	ipf_main_softc_t *softc;
7943255332Scy	ipf_nat_softc_t *softn;
7944255332Scy	char *data;
7945172776Sdarrenr{
7946172776Sdarrenr	ipftable_t table;
7947172776Sdarrenr	int error;
7948172776Sdarrenr
7949255332Scy	error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE);
7950172776Sdarrenr	if (error != 0)
7951172776Sdarrenr		return error;
7952172776Sdarrenr
7953172776Sdarrenr	switch (table.ita_type)
7954172776Sdarrenr	{
7955172776Sdarrenr	case IPFTABLE_BUCKETS_NATIN :
7956255332Scy		error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
7957255332Scy				table.ita_table,
7958255332Scy				softn->ipf_nat_table_sz * sizeof(u_int));
7959172776Sdarrenr		break;
7960172776Sdarrenr
7961172776Sdarrenr	case IPFTABLE_BUCKETS_NATOUT :
7962255332Scy		error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
7963255332Scy				table.ita_table,
7964255332Scy				softn->ipf_nat_table_sz * sizeof(u_int));
7965172776Sdarrenr		break;
7966172776Sdarrenr
7967172776Sdarrenr	default :
7968255332Scy		IPFERROR(60058);
7969172776Sdarrenr		return EINVAL;
7970172776Sdarrenr	}
7971172776Sdarrenr
7972172776Sdarrenr	if (error != 0) {
7973255332Scy		IPFERROR(60059);
7974172776Sdarrenr		error = EFAULT;
7975172776Sdarrenr	}
7976172776Sdarrenr	return error;
7977172776Sdarrenr}
7978255332Scy
7979255332Scy
7980255332Scy/* ------------------------------------------------------------------------ */
7981255332Scy/* Function:    ipf_nat_settimeout                                          */
7982255332Scy/* Returns:     int  - 0 = success, else failure			    */
7983255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7984255332Scy/*              t(I) - pointer to tunable                                   */
7985255332Scy/*              p(I) - pointer to new tuning data                           */
7986255332Scy/*                                                                          */
7987255332Scy/* Apply the timeout change to the NAT timeout queues.                      */
7988255332Scy/* ------------------------------------------------------------------------ */
7989255332Scyint
7990255332Scyipf_nat_settimeout(softc, t, p)
7991255332Scy	struct ipf_main_softc_s *softc;
7992255332Scy	ipftuneable_t *t;
7993255332Scy	ipftuneval_t *p;
7994255332Scy{
7995255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7996255332Scy
7997255332Scy	if (!strncmp(t->ipft_name, "tcp_", 4))
7998255332Scy		return ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq);
7999255332Scy
8000255332Scy	if (!strcmp(t->ipft_name, "udp_timeout")) {
8001255332Scy		ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int);
8002255332Scy	} else if (!strcmp(t->ipft_name, "udp_ack_timeout")) {
8003255332Scy		ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int);
8004255332Scy	} else if (!strcmp(t->ipft_name, "icmp_timeout")) {
8005255332Scy		ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int);
8006255332Scy	} else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) {
8007255332Scy		ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int);
8008255332Scy	} else if (!strcmp(t->ipft_name, "ip_timeout")) {
8009255332Scy		ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int);
8010255332Scy	} else {
8011255332Scy		IPFERROR(60062);
8012255332Scy		return ESRCH;
8013255332Scy	}
8014255332Scy	return 0;
8015255332Scy}
8016255332Scy
8017255332Scy
8018255332Scy/* ------------------------------------------------------------------------ */
8019255332Scy/* Function:    ipf_nat_rehash                                              */
8020255332Scy/* Returns:     int  - 0 = success, else failure			    */
8021255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8022255332Scy/*              t(I) - pointer to tunable                                   */
8023255332Scy/*              p(I) - pointer to new tuning data                           */
8024255332Scy/*                                                                          */
8025255332Scy/* To change the size of the basic NAT table, we need to first allocate the */
8026255332Scy/* new tables (lest it fails and we've got nowhere to store all of the NAT  */
8027255332Scy/* sessions currently active) and then walk through the entire list and     */
8028255332Scy/* insert them into the table.  There are two tables here: an inbound one   */
8029255332Scy/* and an outbound one.  Each NAT entry goes into each table once.          */
8030255332Scy/* ------------------------------------------------------------------------ */
8031255332Scyint
8032255332Scyipf_nat_rehash(softc, t, p)
8033255332Scy	ipf_main_softc_t *softc;
8034255332Scy	ipftuneable_t *t;
8035255332Scy	ipftuneval_t *p;
8036255332Scy{
8037255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8038255332Scy	nat_t **newtab[2], *nat, **natp;
8039255332Scy	u_int *bucketlens[2];
8040255332Scy	u_int maxbucket;
8041255332Scy	u_int newsize;
8042255332Scy	int error;
8043255332Scy	u_int hv;
8044255332Scy	int i;
8045255332Scy
8046255332Scy	newsize = p->ipftu_int;
8047255332Scy	/*
8048255332Scy	 * In case there is nothing to do...
8049255332Scy	 */
8050255332Scy	if (newsize == softn->ipf_nat_table_sz)
8051255332Scy		return 0;
8052255332Scy
8053255332Scy	newtab[0] = NULL;
8054255332Scy	newtab[1] = NULL;
8055255332Scy	bucketlens[0] = NULL;
8056255332Scy	bucketlens[1] = NULL;
8057255332Scy	/*
8058255332Scy	 * 4 tables depend on the NAT table size: the inbound looking table,
8059255332Scy	 * the outbound lookup table and the hash chain length for each.
8060255332Scy	 */
8061255332Scy	KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *));
8062288242Sbz	if (newtab[0] == NULL) {
8063255332Scy		error = 60063;
8064255332Scy		goto badrehash;
8065255332Scy	}
8066255332Scy
8067255332Scy	KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *));
8068288242Sbz	if (newtab[1] == NULL) {
8069255332Scy		error = 60064;
8070255332Scy		goto badrehash;
8071255332Scy	}
8072255332Scy
8073255332Scy	KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int));
8074255332Scy	if (bucketlens[0] == NULL) {
8075255332Scy		error = 60065;
8076255332Scy		goto badrehash;
8077255332Scy	}
8078255332Scy
8079255332Scy	KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int));
8080255332Scy	if (bucketlens[1] == NULL) {
8081255332Scy		error = 60066;
8082255332Scy		goto badrehash;
8083255332Scy	}
8084255332Scy
8085255332Scy	/*
8086255332Scy	 * Recalculate the maximum length based on the new size.
8087255332Scy	 */
8088255332Scy	for (maxbucket = 0, i = newsize; i > 0; i >>= 1)
8089255332Scy		maxbucket++;
8090255332Scy	maxbucket *= 2;
8091255332Scy
8092255332Scy	bzero((char *)newtab[0], newsize * sizeof(nat_t *));
8093255332Scy	bzero((char *)newtab[1], newsize * sizeof(nat_t *));
8094255332Scy	bzero((char *)bucketlens[0], newsize * sizeof(u_int));
8095255332Scy	bzero((char *)bucketlens[1], newsize * sizeof(u_int));
8096255332Scy
8097255332Scy	WRITE_ENTER(&softc->ipf_nat);
8098255332Scy
8099255332Scy	if (softn->ipf_nat_table[0] != NULL) {
8100255332Scy		KFREES(softn->ipf_nat_table[0],
8101255332Scy		       softn->ipf_nat_table_sz *
8102255332Scy		       sizeof(*softn->ipf_nat_table[0]));
8103255332Scy	}
8104255332Scy	softn->ipf_nat_table[0] = newtab[0];
8105255332Scy
8106255332Scy	if (softn->ipf_nat_table[1] != NULL) {
8107255332Scy		KFREES(softn->ipf_nat_table[1],
8108255332Scy		       softn->ipf_nat_table_sz *
8109255332Scy		       sizeof(*softn->ipf_nat_table[1]));
8110255332Scy	}
8111255332Scy	softn->ipf_nat_table[1] = newtab[1];
8112255332Scy
8113255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) {
8114255332Scy		KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
8115255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8116255332Scy	}
8117255332Scy	softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0];
8118255332Scy
8119255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) {
8120255332Scy		KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
8121255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8122255332Scy	}
8123255332Scy	softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1];
8124255332Scy
8125255332Scy#ifdef USE_INET6
8126255332Scy	if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) {
8127255332Scy		KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen,
8128255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8129255332Scy	}
8130255332Scy	softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0];
8131255332Scy
8132255332Scy	if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) {
8133255332Scy		KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen,
8134255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8135255332Scy	}
8136255332Scy	softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1];
8137255332Scy#endif
8138255332Scy
8139255332Scy	softn->ipf_nat_maxbucket = maxbucket;
8140255332Scy	softn->ipf_nat_table_sz = newsize;
8141255332Scy	/*
8142255332Scy	 * Walk through the entire list of NAT table entries and put them
8143255332Scy	 * in the new NAT table, somewhere.  Because we have a new table,
8144255332Scy	 * we need to restart the counter of how many chains are in use.
8145255332Scy	 */
8146255332Scy	softn->ipf_nat_stats.ns_side[0].ns_inuse = 0;
8147255332Scy	softn->ipf_nat_stats.ns_side[1].ns_inuse = 0;
8148255332Scy#ifdef USE_INET6
8149255332Scy	softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0;
8150255332Scy	softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0;
8151255332Scy#endif
8152255332Scy
8153255332Scy	for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) {
8154255332Scy		nat->nat_hnext[0] = NULL;
8155255332Scy		nat->nat_phnext[0] = NULL;
8156255332Scy		hv = nat->nat_hv[0] % softn->ipf_nat_table_sz;
8157255332Scy
8158255332Scy		natp = &softn->ipf_nat_table[0][hv];
8159255332Scy		if (*natp) {
8160255332Scy			(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
8161255332Scy		} else {
8162255332Scy			NBUMPSIDE(0, ns_inuse);
8163255332Scy		}
8164255332Scy		nat->nat_phnext[0] = natp;
8165255332Scy		nat->nat_hnext[0] = *natp;
8166255332Scy		*natp = nat;
8167255332Scy		NBUMPSIDE(0, ns_bucketlen[hv]);
8168255332Scy
8169255332Scy		nat->nat_hnext[1] = NULL;
8170255332Scy		nat->nat_phnext[1] = NULL;
8171255332Scy		hv = nat->nat_hv[1] % softn->ipf_nat_table_sz;
8172255332Scy
8173255332Scy		natp = &softn->ipf_nat_table[1][hv];
8174255332Scy		if (*natp) {
8175255332Scy			(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
8176255332Scy		} else {
8177255332Scy			NBUMPSIDE(1, ns_inuse);
8178255332Scy		}
8179255332Scy		nat->nat_phnext[1] = natp;
8180255332Scy		nat->nat_hnext[1] = *natp;
8181255332Scy		*natp = nat;
8182255332Scy		NBUMPSIDE(1, ns_bucketlen[hv]);
8183255332Scy	}
8184255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8185255332Scy
8186255332Scy	return 0;
8187255332Scy
8188255332Scybadrehash:
8189255332Scy	if (bucketlens[1] != NULL) {
8190255332Scy		KFREES(bucketlens[0], newsize * sizeof(u_int));
8191255332Scy	}
8192255332Scy	if (bucketlens[0] != NULL) {
8193255332Scy		KFREES(bucketlens[0], newsize * sizeof(u_int));
8194255332Scy	}
8195255332Scy	if (newtab[0] != NULL) {
8196255332Scy		KFREES(newtab[0], newsize * sizeof(nat_t *));
8197255332Scy	}
8198255332Scy	if (newtab[1] != NULL) {
8199255332Scy		KFREES(newtab[1], newsize * sizeof(nat_t *));
8200255332Scy	}
8201255332Scy	IPFERROR(error);
8202255332Scy	return ENOMEM;
8203255332Scy}
8204255332Scy
8205255332Scy
8206255332Scy/* ------------------------------------------------------------------------ */
8207255332Scy/* Function:    ipf_nat_rehash_rules                                        */
8208255332Scy/* Returns:     int  - 0 = success, else failure			    */
8209255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8210255332Scy/*              t(I) - pointer to tunable                                   */
8211255332Scy/*              p(I) - pointer to new tuning data                           */
8212255332Scy/*                                                                          */
8213255332Scy/* All of the NAT rules hang off of a hash table that is searched with a    */
8214255332Scy/* hash on address after the netmask is applied.  There is a different table*/
8215255332Scy/* for both inbound rules (rdr) and outbound (map.)  The resizing will only */
8216255332Scy/* affect one of these two tables.                                          */
8217255332Scy/* ------------------------------------------------------------------------ */
8218255332Scyint
8219255332Scyipf_nat_rehash_rules(softc, t, p)
8220255332Scy	ipf_main_softc_t *softc;
8221255332Scy	ipftuneable_t *t;
8222255332Scy	ipftuneval_t *p;
8223255332Scy{
8224255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8225255332Scy	ipnat_t **newtab, *np, ***old, **npp;
8226255332Scy	u_int newsize;
8227255332Scy	u_int mask;
8228255332Scy	u_int hv;
8229255332Scy
8230255332Scy	newsize = p->ipftu_int;
8231255332Scy	/*
8232255332Scy	 * In case there is nothing to do...
8233255332Scy	 */
8234255332Scy	if (newsize == *t->ipft_pint)
8235255332Scy		return 0;
8236255332Scy
8237255332Scy	/*
8238255332Scy	 * All inbound rules have the NAT_REDIRECT bit set in in_redir and
8239255332Scy	 * all outbound rules have either NAT_MAP or MAT_MAPBLK set.
8240255332Scy	 * This if statement allows for some more generic code to be below,
8241255332Scy	 * rather than two huge gobs of code that almost do the same thing.
8242255332Scy	 */
8243255332Scy	if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) {
8244255332Scy		old = &softn->ipf_nat_rdr_rules;
8245255332Scy		mask = NAT_REDIRECT;
8246255332Scy	} else {
8247255332Scy		old = &softn->ipf_nat_map_rules;
8248255332Scy		mask = NAT_MAP|NAT_MAPBLK;
8249255332Scy	}
8250255332Scy
8251255332Scy	KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *));
8252255332Scy	if (newtab == NULL) {
8253255332Scy		IPFERROR(60067);
8254255332Scy		return ENOMEM;
8255255332Scy	}
8256255332Scy
8257255332Scy	bzero((char *)newtab, newsize * sizeof(ipnat_t *));
8258255332Scy
8259255332Scy	WRITE_ENTER(&softc->ipf_nat);
8260255332Scy
8261255332Scy	if (*old != NULL) {
8262255332Scy		KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **));
8263255332Scy	}
8264255332Scy	*old = newtab;
8265255332Scy	*t->ipft_pint = newsize;
8266255332Scy
8267255332Scy	for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) {
8268255332Scy		if ((np->in_redir & mask) == 0)
8269255332Scy			continue;
8270255332Scy
8271255332Scy		if (np->in_redir & NAT_REDIRECT) {
8272255332Scy			np->in_rnext = NULL;
8273255332Scy			hv = np->in_hv[0] % newsize;
8274255332Scy			for (npp = newtab + hv; *npp != NULL; )
8275255332Scy				npp = &(*npp)->in_rnext;
8276255332Scy			np->in_prnext = npp;
8277255332Scy			*npp = np;
8278255332Scy		}
8279255332Scy		if (np->in_redir & NAT_MAP) {
8280255332Scy			np->in_mnext = NULL;
8281255332Scy			hv = np->in_hv[1] % newsize;
8282255332Scy			for (npp = newtab + hv; *npp != NULL; )
8283255332Scy				npp = &(*npp)->in_mnext;
8284255332Scy			np->in_pmnext = npp;
8285255332Scy			*npp = np;
8286255332Scy		}
8287255332Scy
8288255332Scy	}
8289255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8290255332Scy
8291255332Scy	return 0;
8292255332Scy}
8293255332Scy
8294255332Scy
8295255332Scy/* ------------------------------------------------------------------------ */
8296255332Scy/* Function:    ipf_nat_hostmap_rehash                                      */
8297255332Scy/* Returns:     int  - 0 = success, else failure			    */
8298255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8299255332Scy/*              t(I) - pointer to tunable                                   */
8300255332Scy/*              p(I) - pointer to new tuning data                           */
8301255332Scy/*                                                                          */
8302255332Scy/* Allocate and populate a new hash table that will contain a reference to  */
8303255332Scy/* all of the active IP# translations currently in place.                   */
8304255332Scy/* ------------------------------------------------------------------------ */
8305255332Scyint
8306255332Scyipf_nat_hostmap_rehash(softc, t, p)
8307255332Scy	ipf_main_softc_t *softc;
8308255332Scy	ipftuneable_t *t;
8309255332Scy	ipftuneval_t *p;
8310255332Scy{
8311255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8312255332Scy	hostmap_t *hm, **newtab;
8313255332Scy	u_int newsize;
8314255332Scy	u_int hv;
8315255332Scy
8316255332Scy	newsize = p->ipftu_int;
8317255332Scy	/*
8318255332Scy	 * In case there is nothing to do...
8319255332Scy	 */
8320255332Scy	if (newsize == *t->ipft_pint)
8321255332Scy		return 0;
8322255332Scy
8323255332Scy	KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *));
8324255332Scy	if (newtab == NULL) {
8325255332Scy		IPFERROR(60068);
8326255332Scy		return ENOMEM;
8327255332Scy	}
8328255332Scy
8329255332Scy	bzero((char *)newtab, newsize * sizeof(hostmap_t *));
8330255332Scy
8331255332Scy	WRITE_ENTER(&softc->ipf_nat);
8332255332Scy	if (softn->ipf_hm_maptable != NULL) {
8333255332Scy		KFREES(softn->ipf_hm_maptable,
8334255332Scy		       softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *));
8335255332Scy	}
8336255332Scy	softn->ipf_hm_maptable = newtab;
8337255332Scy	softn->ipf_nat_hostmap_sz = newsize;
8338255332Scy
8339255332Scy	for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) {
8340255332Scy		hv = hm->hm_hv % softn->ipf_nat_hostmap_sz;
8341255332Scy		hm->hm_hnext = softn->ipf_hm_maptable[hv];
8342255332Scy		hm->hm_phnext = softn->ipf_hm_maptable + hv;
8343255332Scy		if (softn->ipf_hm_maptable[hv] != NULL)
8344255332Scy			softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext;
8345255332Scy		softn->ipf_hm_maptable[hv] = hm;
8346255332Scy	}
8347255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8348255332Scy
8349255332Scy	return 0;
8350255332Scy}
8351255332Scy
8352255332Scy
8353255332Scy/* ------------------------------------------------------------------------ */
8354255332Scy/* Function:    ipf_nat_add_tq                                              */
8355255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8356255332Scy/*                                                                          */
8357255332Scy/* ------------------------------------------------------------------------ */
8358255332Scyipftq_t *
8359255332Scyipf_nat_add_tq(softc, ttl)
8360255332Scy	ipf_main_softc_t *softc;
8361255332Scy	int ttl;
8362255332Scy{
8363255332Scy	ipf_nat_softc_t *softs = softc->ipf_nat_soft;
8364255332Scy
8365255332Scy	return ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl);
8366255332Scy}
8367255332Scy
8368255332Scy/* ------------------------------------------------------------------------ */
8369255332Scy/* Function:    ipf_nat_uncreate                                            */
8370255332Scy/* Returns:     Nil                                                         */
8371255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
8372255332Scy/*                                                                          */
8373255332Scy/* This function is used to remove a NAT entry from the NAT table when we   */
8374255332Scy/* decide that the create was actually in error. It is thus assumed that    */
8375255332Scy/* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */
8376255332Scy/* with the translated packet (not the original), we have to reverse the    */
8377255332Scy/* lookup. Although doing the lookup is expensive (relatively speaking), it */
8378255332Scy/* is not anticipated that this will be a frequent occurance for normal     */
8379255332Scy/* traffic patterns.                                                        */
8380255332Scy/* ------------------------------------------------------------------------ */
8381255332Scyvoid
8382255332Scyipf_nat_uncreate(fin)
8383255332Scy	fr_info_t *fin;
8384255332Scy{
8385255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
8386255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8387255332Scy	int nflags;
8388255332Scy	nat_t *nat;
8389255332Scy
8390255332Scy	switch (fin->fin_p)
8391255332Scy	{
8392255332Scy	case IPPROTO_TCP :
8393255332Scy		nflags = IPN_TCP;
8394255332Scy		break;
8395255332Scy	case IPPROTO_UDP :
8396255332Scy		nflags = IPN_UDP;
8397255332Scy		break;
8398255332Scy	default :
8399255332Scy		nflags = 0;
8400255332Scy		break;
8401255332Scy	}
8402255332Scy
8403255332Scy	WRITE_ENTER(&softc->ipf_nat);
8404255332Scy
8405255332Scy	if (fin->fin_out == 0) {
8406255332Scy		nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p,
8407255332Scy					fin->fin_dst, fin->fin_src);
8408255332Scy	} else {
8409255332Scy		nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p,
8410255332Scy				       fin->fin_src, fin->fin_dst);
8411255332Scy	}
8412255332Scy
8413255332Scy	if (nat != NULL) {
8414255332Scy		NBUMPSIDE(fin->fin_out, ns_uncreate[0]);
8415255332Scy		ipf_nat_delete(softc, nat, NL_DESTROY);
8416255332Scy	} else {
8417255332Scy		NBUMPSIDE(fin->fin_out, ns_uncreate[1]);
8418255332Scy	}
8419255332Scy
8420255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8421255332Scy}
8422255332Scy
8423255332Scy
8424255332Scy/* ------------------------------------------------------------------------ */
8425255332Scy/* Function:    ipf_nat_cmp_rules                                           */
8426255332Scy/* Returns:     int   - 0 == success, else rules do not match.              */
8427255332Scy/* Parameters:  n1(I) - first rule to compare                               */
8428255332Scy/*              n2(I) - first rule to compare                               */
8429255332Scy/*                                                                          */
8430255332Scy/* Compare two rules using pointers to each rule. A straight bcmp will not  */
8431255332Scy/* work as some fields (such as in_dst, in_pkts) actually do change once    */
8432255332Scy/* the rule has been loaded into the kernel. Whilst this function returns   */
8433255332Scy/* various non-zero returns, they're strictly to aid in debugging. Use of   */
8434255332Scy/* this function should simply care if the result is zero or not.           */
8435255332Scy/* ------------------------------------------------------------------------ */
8436255332Scystatic int
8437255332Scyipf_nat_cmp_rules(n1, n2)
8438255332Scy	ipnat_t *n1, *n2;
8439255332Scy{
8440255332Scy	if (n1->in_size != n2->in_size)
8441255332Scy		return 1;
8442255332Scy
8443255332Scy	if (bcmp((char *)&n1->in_v, (char *)&n2->in_v,
8444255332Scy		 offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0)
8445255332Scy		return 2;
8446255332Scy
8447255332Scy	if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc,
8448255332Scy		 n1->in_size - offsetof(ipnat_t, in_tuc)) != 0)
8449255332Scy		return 3;
8450255332Scy	if (n1->in_ndst.na_atype != n2->in_ndst.na_atype)
8451255332Scy		return 5;
8452255332Scy	if (n1->in_ndst.na_function != n2->in_ndst.na_function)
8453255332Scy		return 6;
8454255332Scy	if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr,
8455255332Scy		 sizeof(n1->in_ndst.na_addr)))
8456255332Scy		return 7;
8457255332Scy	if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype)
8458255332Scy		return 8;
8459255332Scy	if (n1->in_nsrc.na_function != n2->in_nsrc.na_function)
8460255332Scy		return 9;
8461255332Scy	if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr,
8462255332Scy		 sizeof(n1->in_nsrc.na_addr)))
8463255332Scy		return 10;
8464255332Scy	if (n1->in_odst.na_atype != n2->in_odst.na_atype)
8465255332Scy		return 11;
8466255332Scy	if (n1->in_odst.na_function != n2->in_odst.na_function)
8467255332Scy		return 12;
8468255332Scy	if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr,
8469255332Scy		 sizeof(n1->in_odst.na_addr)))
8470255332Scy		return 13;
8471255332Scy	if (n1->in_osrc.na_atype != n2->in_osrc.na_atype)
8472255332Scy		return 14;
8473255332Scy	if (n1->in_osrc.na_function != n2->in_osrc.na_function)
8474255332Scy		return 15;
8475255332Scy	if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr,
8476255332Scy		 sizeof(n1->in_osrc.na_addr)))
8477255332Scy		return 16;
8478255332Scy	return 0;
8479255332Scy}
8480255332Scy
8481255332Scy
8482255332Scy/* ------------------------------------------------------------------------ */
8483255332Scy/* Function:    ipf_nat_rule_init                                           */
8484255332Scy/* Returns:     int   - 0 == success, else rules do not match.              */
8485255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8486255332Scy/*              softn(I) - pointer to NAT context structure                 */
8487255332Scy/*              n(I)     - first rule to compare                            */
8488255332Scy/*                                                                          */
8489255332Scy/* ------------------------------------------------------------------------ */
8490255332Scystatic int
8491255332Scyipf_nat_rule_init(softc, softn, n)
8492255332Scy	ipf_main_softc_t *softc;
8493255332Scy	ipf_nat_softc_t *softn;
8494255332Scy	ipnat_t *n;
8495255332Scy{
8496255332Scy	int error = 0;
8497255332Scy
8498255332Scy	if ((n->in_flags & IPN_SIPRANGE) != 0)
8499255332Scy		n->in_nsrcatype = FRI_RANGE;
8500255332Scy
8501255332Scy	if ((n->in_flags & IPN_DIPRANGE) != 0)
8502255332Scy		n->in_ndstatype = FRI_RANGE;
8503255332Scy
8504255332Scy	if ((n->in_flags & IPN_SPLIT) != 0)
8505255332Scy		n->in_ndstatype = FRI_SPLIT;
8506255332Scy
8507255332Scy	if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0)
8508255332Scy		n->in_spnext = n->in_spmin;
8509255332Scy
8510255332Scy	if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) {
8511255332Scy		n->in_dpnext = n->in_dpmin;
8512255332Scy	} else if (n->in_redir == NAT_REDIRECT) {
8513255332Scy		n->in_dpnext = n->in_dpmin;
8514255332Scy	}
8515255332Scy
8516255332Scy	n->in_stepnext = 0;
8517255332Scy
8518255332Scy	switch (n->in_v[0])
8519255332Scy	{
8520255332Scy	case 4 :
8521255332Scy		error = ipf_nat_ruleaddrinit(softc, softn, n);
8522255332Scy		if (error != 0)
8523255332Scy			return error;
8524255332Scy		break;
8525255332Scy#ifdef USE_INET6
8526255332Scy	case 6 :
8527255332Scy		error = ipf_nat6_ruleaddrinit(softc, softn, n);
8528255332Scy		if (error != 0)
8529255332Scy			return error;
8530255332Scy		break;
8531255332Scy#endif
8532255332Scy	default :
8533255332Scy		break;
8534255332Scy	}
8535255332Scy
8536255332Scy	if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) {
8537255332Scy		/*
8538255332Scy		 * Prerecord whether or not the destination of the divert
8539255332Scy		 * is local or not to the interface the packet is going
8540255332Scy		 * to be sent out.
8541255332Scy		 */
8542255332Scy		n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1],
8543255332Scy						n->in_ifps[1], &n->in_ndstip6);
8544255332Scy	}
8545255332Scy
8546255332Scy	return error;
8547255332Scy}
8548255332Scy
8549255332Scy
8550255332Scy/* ------------------------------------------------------------------------ */
8551255332Scy/* Function:    ipf_nat_rule_fini                                           */
8552255332Scy/* Returns:     int   - 0 == success, else rules do not match.              */
8553255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8554255332Scy/*              n(I)     - rule to work on                                  */
8555255332Scy/*                                                                          */
8556255332Scy/* This function is used to release any objects that were referenced during */
8557255332Scy/* the rule initialisation. This is useful both when free'ing the rule and  */
8558255332Scy/* when handling ioctls that need to initialise these fields but not        */
8559255332Scy/* actually use them after the ioctl processing has finished.               */
8560255332Scy/* ------------------------------------------------------------------------ */
8561255332Scystatic void
8562255332Scyipf_nat_rule_fini(softc, n)
8563255332Scy	ipf_main_softc_t *softc;
8564255332Scy	ipnat_t *n;
8565255332Scy{
8566255332Scy	if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL)
8567255332Scy		ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr);
8568255332Scy
8569255332Scy	if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL)
8570255332Scy		ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr);
8571255332Scy
8572255332Scy	if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL)
8573255332Scy		ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr);
8574255332Scy
8575255332Scy	if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL)
8576255332Scy		ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr);
8577255332Scy
8578255332Scy	if (n->in_divmp != NULL)
8579255332Scy		FREE_MB_T(n->in_divmp);
8580255332Scy}
8581