1145522Sdarrenr/*	$FreeBSD: releng/10.3/sys/contrib/ipfilter/netinet/ip_nat.c 292979 2015-12-31 06:01:07Z cy $	*/
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
34255332Scy#if defined(_KERNEL) && \
35255332Scy    defined(__FreeBSD_version) && (__FreeBSD_version >= 220000)
3653642Sguido# include <sys/filio.h>
3753642Sguido# include <sys/fcntl.h>
3853642Sguido#else
3953642Sguido# include <sys/ioctl.h>
4053642Sguido#endif
41153876Sguido#if !defined(AIX)
42153876Sguido# include <sys/fcntl.h>
43153876Sguido#endif
44145522Sdarrenr#if !defined(linux)
4553642Sguido# include <sys/protosw.h>
4653642Sguido#endif
4753642Sguido#include <sys/socket.h>
48145522Sdarrenr#if defined(_KERNEL)
4953642Sguido# include <sys/systm.h>
50145522Sdarrenr# if !defined(__SVR4) && !defined(__svr4__)
5153642Sguido#  include <sys/mbuf.h>
5253642Sguido# endif
53145522Sdarrenr#endif
54145522Sdarrenr#if defined(__SVR4) || defined(__svr4__)
5553642Sguido# include <sys/filio.h>
5653642Sguido# include <sys/byteorder.h>
57255332Scy# ifdef KERNEL
5853642Sguido#  include <sys/dditypes.h>
5953642Sguido# endif
6053642Sguido# include <sys/stream.h>
6153642Sguido# include <sys/kmem.h>
6253642Sguido#endif
6353642Sguido#if __FreeBSD_version >= 300000
6453642Sguido# include <sys/queue.h>
6553642Sguido#endif
6653642Sguido#include <net/if.h>
6753642Sguido#if __FreeBSD_version >= 300000
6853642Sguido# include <net/if_var.h>
6953642Sguido#endif
7053642Sguido#ifdef sun
7153642Sguido# include <net/af.h>
7253642Sguido#endif
7353642Sguido#include <netinet/in.h>
7453642Sguido#include <netinet/in_systm.h>
7553642Sguido#include <netinet/ip.h>
7653642Sguido
7753642Sguido#ifdef RFC1825
7853642Sguido# include <vpn/md5.h>
7953642Sguido# include <vpn/ipsec.h>
8053642Sguidoextern struct ifnet vpnif;
8153642Sguido#endif
8253642Sguido
83145522Sdarrenr#if !defined(linux)
8453642Sguido# include <netinet/ip_var.h>
8553642Sguido#endif
8653642Sguido#include <netinet/tcp.h>
8753642Sguido#include <netinet/udp.h>
8853642Sguido#include <netinet/ip_icmp.h>
8953642Sguido#include "netinet/ip_compat.h"
9053642Sguido#include <netinet/tcpip.h>
91255332Scy#include "netinet/ipl.h"
9253642Sguido#include "netinet/ip_fil.h"
9353642Sguido#include "netinet/ip_nat.h"
9453642Sguido#include "netinet/ip_frag.h"
9553642Sguido#include "netinet/ip_state.h"
9692685Sdarrenr#include "netinet/ip_proxy.h"
97255332Scy#include "netinet/ip_lookup.h"
98255332Scy#include "netinet/ip_dstlist.h"
99145522Sdarrenr#include "netinet/ip_sync.h"
100255332Scy#if FREEBSD_GE_REV(300000)
10153642Sguido# include <sys/malloc.h>
10253642Sguido#endif
103255332Scy#ifdef HAS_SYS_MD5_H
104255332Scy# include <sys/md5.h>
105255332Scy#else
106255332Scy# include "md5.h"
107255332Scy#endif
108145522Sdarrenr/* END OF INCLUDES */
109145522Sdarrenr
11053642Sguido#undef	SOCKADDR_IN
11153642Sguido#define	SOCKADDR_IN	struct sockaddr_in
11253642Sguido
11380482Sdarrenr#if !defined(lint)
11480482Sdarrenrstatic const char sccsid[] = "@(#)ip_nat.c	1.11 6/5/96 (C) 1995 Darren Reed";
11580482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: releng/10.3/sys/contrib/ipfilter/netinet/ip_nat.c 292979 2015-12-31 06:01:07Z cy $";
116172776Sdarrenr/* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.102 2007/10/16 10:08:10 darrenr Exp $"; */
11780482Sdarrenr#endif
11880482Sdarrenr
119145522Sdarrenr
120255332Scy#define	NATFSUM(n,v,f)	((v) == 4 ? (n)->f.in4.s_addr : (n)->f.i6[0] + \
121255332Scy			 (n)->f.i6[1] + (n)->f.i6[2] + (n)->f.i6[3])
122255332Scy#define	NBUMP(x)	softn->(x)++
123255332Scy#define	NBUMPD(x, y)	do { \
124255332Scy				softn->x.y++; \
125255332Scy				DT(y); \
126255332Scy			} while (0)
127255332Scy#define	NBUMPSIDE(y,x)	softn->ipf_nat_stats.ns_side[y].x++
128255332Scy#define	NBUMPSIDED(y,x)	do { softn->ipf_nat_stats.ns_side[y].x++; \
129255332Scy			     DT(x); } while (0)
130255332Scy#define	NBUMPSIDEX(y,x,z) \
131255332Scy			do { softn->ipf_nat_stats.ns_side[y].x++; \
132255332Scy			     DT(z); } while (0)
133255332Scy#define	NBUMPSIDEDF(y,x)do { softn->ipf_nat_stats.ns_side[y].x++; \
134255332Scy			     DT1(x, fr_info_t *, fin); } while (0)
135255332Scy
136255332Scyfrentry_t	ipfnatblock;
137255332Scy
138255332Scystatic ipftuneable_t ipf_nat_tuneables[] = {
139255332Scy	/* nat */
140255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_lock) },
141255332Scy		"nat_lock",	0,	1,
142255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_lock),
143255332Scy		IPFT_RDONLY,		NULL,	NULL },
144255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz) },
145255332Scy		"nat_table_size", 1,	0x7fffffff,
146255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_table_sz),
147255332Scy		0,			NULL,	ipf_nat_rehash },
148255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max) },
149255332Scy		"nat_table_max", 1,	0x7fffffff,
150255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_table_max),
151255332Scy		0,			NULL,	NULL },
152255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz) },
153255332Scy		"nat_rules_size", 1,	0x7fffffff,
154255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_maprules_sz),
155255332Scy		0,			NULL,	ipf_nat_rehash_rules },
156255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz) },
157255332Scy		"rdr_rules_size", 1,	0x7fffffff,
158255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_rdrrules_sz),
159255332Scy		0,			NULL,	ipf_nat_rehash_rules },
160255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz) },
161255332Scy		"hostmap_size",	1,	0x7fffffff,
162255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_hostmap_sz),
163255332Scy		0,			NULL,	ipf_nat_hostmap_rehash },
164255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maxbucket) },
165255332Scy		"nat_maxbucket",1,	0x7fffffff,
166255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_maxbucket),
167255332Scy		0,			NULL,	NULL },
168255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_logging) },
169255332Scy		"nat_logging",	0,	1,
170255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_logging),
171255332Scy		0,			NULL,	NULL },
172255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_doflush) },
173255332Scy		"nat_doflush",	0,	1,
174255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_doflush),
175255332Scy		0,			NULL,	NULL },
176255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_low) },
177255332Scy		"nat_table_wm_low",	1,	99,
178255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_low),
179255332Scy		0,			NULL,	NULL },
180255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_high) },
181255332Scy		"nat_table_wm_high",	2,	100,
182255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_high),
183255332Scy		0,			NULL,	NULL },
184255332Scy	{ { 0 },
185255332Scy		NULL,			0,	0,
186255332Scy		0,
187255332Scy		0,			NULL,	NULL }
188255332Scy};
189255332Scy
190145522Sdarrenr/* ======================================================================== */
191145522Sdarrenr/* How the NAT is organised and works.                                      */
192145522Sdarrenr/*                                                                          */
193145522Sdarrenr/* Inside (interface y) NAT       Outside (interface x)                     */
194145522Sdarrenr/* -------------------- -+- -------------------------------------           */
195255332Scy/* Packet going          |   out, processsed by ipf_nat_checkout() for x    */
196145522Sdarrenr/* ------------>         |   ------------>                                  */
197145522Sdarrenr/* src=10.1.1.1          |   src=192.1.1.1                                  */
198145522Sdarrenr/*                       |                                                  */
199255332Scy/*                       |   in, processed by ipf_nat_checkin() for x       */
200145522Sdarrenr/* <------------         |   <------------                                  */
201145522Sdarrenr/* dst=10.1.1.1          |   dst=192.1.1.1                                  */
202145522Sdarrenr/* -------------------- -+- -------------------------------------           */
203255332Scy/* ipf_nat_checkout() - changes ip_src and if required, sport               */
204145522Sdarrenr/*             - creates a new mapping, if required.                        */
205255332Scy/* ipf_nat_checkin()  - changes ip_dst and if required, dport               */
206145522Sdarrenr/*                                                                          */
207145522Sdarrenr/* In the NAT table, internal source is recorded as "in" and externally     */
208145522Sdarrenr/* seen as "out".                                                           */
209145522Sdarrenr/* ======================================================================== */
210145522Sdarrenr
211145522Sdarrenr
212255332Scy#if SOLARIS && !defined(INSTANCES)
213255332Scyextern	int		pfil_delayed_copy;
214255332Scy#endif
215255332Scy
216255332Scystatic	int	ipf_nat_flush_entry __P((ipf_main_softc_t *, void *));
217255332Scystatic	int	ipf_nat_getent __P((ipf_main_softc_t *, caddr_t, int));
218255332Scystatic	int	ipf_nat_getsz __P((ipf_main_softc_t *, caddr_t, int));
219255332Scystatic	int	ipf_nat_putent __P((ipf_main_softc_t *, caddr_t, int));
220255332Scystatic	void	ipf_nat_addmap __P((ipf_nat_softc_t *, ipnat_t *));
221255332Scystatic	void	ipf_nat_addrdr __P((ipf_nat_softc_t *, ipnat_t *));
222255332Scystatic	int	ipf_nat_builddivertmp __P((ipf_nat_softc_t *, ipnat_t *));
223255332Scystatic	int	ipf_nat_clearlist __P((ipf_main_softc_t *, ipf_nat_softc_t *));
224255332Scystatic	int	ipf_nat_cmp_rules __P((ipnat_t *, ipnat_t *));
225255332Scystatic	int	ipf_nat_decap __P((fr_info_t *, nat_t *));
226255332Scystatic	void	ipf_nat_delrule __P((ipf_main_softc_t *, ipf_nat_softc_t *,
227255332Scy				     ipnat_t *, int));
228255332Scystatic	int	ipf_nat_extraflush __P((ipf_main_softc_t *, ipf_nat_softc_t *, int));
229255332Scystatic	int	ipf_nat_finalise __P((fr_info_t *, nat_t *));
230255332Scystatic	int	ipf_nat_flushtable __P((ipf_main_softc_t *, ipf_nat_softc_t *));
231255332Scystatic	int	ipf_nat_getnext __P((ipf_main_softc_t *, ipftoken_t *,
232255332Scy				     ipfgeniter_t *, ipfobj_t *));
233255332Scystatic	int	ipf_nat_gettable __P((ipf_main_softc_t *, ipf_nat_softc_t *,
234255332Scy				      char *));
235255332Scystatic	hostmap_t *ipf_nat_hostmap __P((ipf_nat_softc_t *, ipnat_t *,
236255332Scy					struct in_addr, struct in_addr,
237255332Scy					struct in_addr, u_32_t));
238255332Scystatic	int	ipf_nat_icmpquerytype __P((int));
239255332Scystatic	int	ipf_nat_iterator __P((ipf_main_softc_t *, ipftoken_t *,
240255332Scy				      ipfgeniter_t *, ipfobj_t *));
241255332Scystatic	int	ipf_nat_match __P((fr_info_t *, ipnat_t *));
242255332Scystatic	int	ipf_nat_matcharray __P((nat_t *, int *, u_long));
243255332Scystatic	int	ipf_nat_matchflush __P((ipf_main_softc_t *, ipf_nat_softc_t *,
244255332Scy					caddr_t));
245255332Scystatic	void	ipf_nat_mssclamp __P((tcphdr_t *, u_32_t, fr_info_t *,
246255332Scy				      u_short *));
247255332Scystatic	int	ipf_nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *));
248255332Scystatic	int	ipf_nat_newdivert __P((fr_info_t *, nat_t *, natinfo_t *));
249255332Scystatic	int	ipf_nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *));
250255332Scystatic	int	ipf_nat_newrewrite __P((fr_info_t *, nat_t *, natinfo_t *));
251255332Scystatic	int	ipf_nat_nextaddr __P((fr_info_t *, nat_addr_t *, u_32_t *,
252255332Scy				      u_32_t *));
253255332Scystatic	int	ipf_nat_nextaddrinit __P((ipf_main_softc_t *, char *,
254255332Scy					  nat_addr_t *, int, void *));
255255332Scystatic	int	ipf_nat_resolverule __P((ipf_main_softc_t *, ipnat_t *));
256255332Scystatic	int	ipf_nat_ruleaddrinit __P((ipf_main_softc_t *,
257255332Scy					  ipf_nat_softc_t *, ipnat_t *));
258255332Scystatic	void	ipf_nat_rule_fini __P((ipf_main_softc_t *, ipnat_t *));
259255332Scystatic	int	ipf_nat_rule_init __P((ipf_main_softc_t *, ipf_nat_softc_t *,
260255332Scy				       ipnat_t *));
261255332Scystatic	int	ipf_nat_siocaddnat __P((ipf_main_softc_t *, ipf_nat_softc_t *,
262255332Scy					ipnat_t *, int));
263255332Scystatic	void	ipf_nat_siocdelnat __P((ipf_main_softc_t *, ipf_nat_softc_t *,
264255332Scy					ipnat_t *, int));
265255332Scystatic	void	ipf_nat_tabmove __P((ipf_nat_softc_t *, nat_t *));
266255332Scy
267255332Scy/* ------------------------------------------------------------------------ */
268255332Scy/* Function:    ipf_nat_main_load                                           */
269255332Scy/* Returns:     int - 0 == success, -1 == failure                           */
270255332Scy/* Parameters:  Nil                                                         */
271255332Scy/*                                                                          */
272255332Scy/* The only global NAT structure that needs to be initialised is the filter */
273255332Scy/* rule that is used with blocking packets.                                 */
274255332Scy/* ------------------------------------------------------------------------ */
275255332Scyint
276255332Scyipf_nat_main_load()
277255332Scy{
278255332Scy	bzero((char *)&ipfnatblock, sizeof(ipfnatblock));
279255332Scy	ipfnatblock.fr_flags = FR_BLOCK|FR_QUICK;
280255332Scy	ipfnatblock.fr_ref = 1;
281255332Scy
282255332Scy	return 0;
283255332Scy}
284255332Scy
285255332Scy
286255332Scy/* ------------------------------------------------------------------------ */
287255332Scy/* Function:    ipf_nat_main_unload                                         */
288255332Scy/* Returns:     int - 0 == success, -1 == failure                           */
289255332Scy/* Parameters:  Nil                                                         */
290255332Scy/*                                                                          */
291255332Scy/* A null-op function that exists as a placeholder so that the flow in      */
292255332Scy/* other functions is obvious.                                              */
293255332Scy/* ------------------------------------------------------------------------ */
294255332Scyint
295255332Scyipf_nat_main_unload()
296255332Scy{
297255332Scy	return 0;
298255332Scy}
299255332Scy
300255332Scy
301255332Scy/* ------------------------------------------------------------------------ */
302255332Scy/* Function:    ipf_nat_soft_create                                         */
303255332Scy/* Returns:     void * - NULL = failure, else pointer to NAT context        */
304255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
305255332Scy/*                                                                          */
306255332Scy/* Allocate the initial soft context structure for NAT and populate it with */
307255332Scy/* some default values. Creating the tables is left until we call _init so  */
308255332Scy/* that sizes can be changed before we get under way.                       */
309255332Scy/* ------------------------------------------------------------------------ */
310255332Scyvoid *
311255332Scyipf_nat_soft_create(softc)
312255332Scy	ipf_main_softc_t *softc;
313255332Scy{
314255332Scy	ipf_nat_softc_t *softn;
315255332Scy
316255332Scy	KMALLOC(softn, ipf_nat_softc_t *);
317255332Scy	if (softn == NULL)
318255332Scy		return NULL;
319255332Scy
320255332Scy	bzero((char *)softn, sizeof(*softn));
321255332Scy
322255332Scy	softn->ipf_nat_tune = ipf_tune_array_copy(softn,
323255332Scy						  sizeof(ipf_nat_tuneables),
324255332Scy						  ipf_nat_tuneables);
325255332Scy	if (softn->ipf_nat_tune == NULL) {
326255332Scy		ipf_nat_soft_destroy(softc, softn);
327255332Scy		return NULL;
328255332Scy	}
329255332Scy	if (ipf_tune_array_link(softc, softn->ipf_nat_tune) == -1) {
330255332Scy		ipf_nat_soft_destroy(softc, softn);
331255332Scy		return NULL;
332255332Scy	}
333255332Scy
334255332Scy	softn->ipf_nat_list_tail = &softn->ipf_nat_list;
335255332Scy
336255332Scy	softn->ipf_nat_table_max = NAT_TABLE_MAX;
337255332Scy	softn->ipf_nat_table_sz = NAT_TABLE_SZ;
338255332Scy	softn->ipf_nat_maprules_sz = NAT_SIZE;
339255332Scy	softn->ipf_nat_rdrrules_sz = RDR_SIZE;
340255332Scy	softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE;
341255332Scy	softn->ipf_nat_doflush = 0;
342145522Sdarrenr#ifdef  IPFILTER_LOG
343255332Scy	softn->ipf_nat_logging = 1;
344145522Sdarrenr#else
345255332Scy	softn->ipf_nat_logging = 0;
346145522Sdarrenr#endif
34753642Sguido
348255332Scy	softn->ipf_nat_defage = DEF_NAT_AGE;
349255332Scy	softn->ipf_nat_defipage = IPF_TTLVAL(60);
350255332Scy	softn->ipf_nat_deficmpage = IPF_TTLVAL(3);
351255332Scy	softn->ipf_nat_table_wm_high = 99;
352255332Scy	softn->ipf_nat_table_wm_low = 90;
35353642Sguido
354255332Scy	return softn;
355255332Scy}
35653642Sguido
357255332Scy/* ------------------------------------------------------------------------ */
358255332Scy/* Function:    ipf_nat_soft_destroy                                        */
359255332Scy/* Returns:     Nil                                                         */
360255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
361255332Scy/*                                                                          */
362255332Scy/* ------------------------------------------------------------------------ */
363255332Scyvoid
364255332Scyipf_nat_soft_destroy(softc, arg)
365255332Scy	ipf_main_softc_t *softc;
366255332Scy	void *arg;
367255332Scy{
368255332Scy	ipf_nat_softc_t *softn = arg;
36953642Sguido
370255332Scy	if (softn->ipf_nat_tune != NULL) {
371255332Scy		ipf_tune_array_unlink(softc, softn->ipf_nat_tune);
372255332Scy		KFREES(softn->ipf_nat_tune, sizeof(ipf_nat_tuneables));
373255332Scy		softn->ipf_nat_tune = NULL;
374255332Scy	}
375255332Scy
376255332Scy	KFREE(softn);
377255332Scy}
378255332Scy
379255332Scy
380145522Sdarrenr/* ------------------------------------------------------------------------ */
381255332Scy/* Function:    ipf_nat_init                                                */
382145522Sdarrenr/* Returns:     int - 0 == success, -1 == failure                           */
383255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
384145522Sdarrenr/*                                                                          */
385145522Sdarrenr/* Initialise all of the NAT locks, tables and other structures.            */
386145522Sdarrenr/* ------------------------------------------------------------------------ */
387255332Scyint
388255332Scyipf_nat_soft_init(softc, arg)
389255332Scy	ipf_main_softc_t *softc;
390255332Scy	void *arg;
39153642Sguido{
392255332Scy	ipf_nat_softc_t *softn = arg;
393255332Scy	ipftq_t *tq;
394145522Sdarrenr	int i;
395145522Sdarrenr
396255332Scy	KMALLOCS(softn->ipf_nat_table[0], nat_t **, \
397255332Scy		 sizeof(nat_t *) * softn->ipf_nat_table_sz);
398255332Scy
399255332Scy	if (softn->ipf_nat_table[0] != NULL) {
400255332Scy		bzero((char *)softn->ipf_nat_table[0],
401255332Scy		      softn->ipf_nat_table_sz * sizeof(nat_t *));
402255332Scy	} else {
40353642Sguido		return -1;
404255332Scy	}
40553642Sguido
406255332Scy	KMALLOCS(softn->ipf_nat_table[1], nat_t **, \
407255332Scy		 sizeof(nat_t *) * softn->ipf_nat_table_sz);
408255332Scy
409255332Scy	if (softn->ipf_nat_table[1] != NULL) {
410255332Scy		bzero((char *)softn->ipf_nat_table[1],
411255332Scy		      softn->ipf_nat_table_sz * sizeof(nat_t *));
412255332Scy	} else {
413145522Sdarrenr		return -2;
414255332Scy	}
41553642Sguido
416255332Scy	KMALLOCS(softn->ipf_nat_map_rules, ipnat_t **, \
417255332Scy		 sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz);
418255332Scy
419255332Scy	if (softn->ipf_nat_map_rules != NULL) {
420255332Scy		bzero((char *)softn->ipf_nat_map_rules,
421255332Scy		      softn->ipf_nat_maprules_sz * sizeof(ipnat_t *));
422255332Scy	} else {
423145522Sdarrenr		return -3;
424255332Scy	}
42553642Sguido
426255332Scy	KMALLOCS(softn->ipf_nat_rdr_rules, ipnat_t **, \
427255332Scy		 sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz);
428255332Scy
429255332Scy	if (softn->ipf_nat_rdr_rules != NULL) {
430255332Scy		bzero((char *)softn->ipf_nat_rdr_rules,
431255332Scy		      softn->ipf_nat_rdrrules_sz * sizeof(ipnat_t *));
432255332Scy	} else {
433145522Sdarrenr		return -4;
434255332Scy	}
43560852Sdarrenr
436255332Scy	KMALLOCS(softn->ipf_hm_maptable, hostmap_t **, \
437255332Scy		 sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz);
438255332Scy
439255332Scy	if (softn->ipf_hm_maptable != NULL) {
440255332Scy		bzero((char *)softn->ipf_hm_maptable,
441255332Scy		      sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz);
442255332Scy	} else {
443145522Sdarrenr		return -5;
444255332Scy	}
445255332Scy	softn->ipf_hm_maplist = NULL;
446145522Sdarrenr
447255332Scy	KMALLOCS(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, u_int *,
448255332Scy		 softn->ipf_nat_table_sz * sizeof(u_int));
449255332Scy
450255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen == NULL) {
451145522Sdarrenr		return -6;
452255332Scy	}
453255332Scy	bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
454255332Scy	      softn->ipf_nat_table_sz * sizeof(u_int));
455145522Sdarrenr
456255332Scy	KMALLOCS(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, u_int *,
457255332Scy		 softn->ipf_nat_table_sz * sizeof(u_int));
458255332Scy
459255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen == NULL) {
460145522Sdarrenr		return -7;
461255332Scy	}
462145522Sdarrenr
463255332Scy	bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
464255332Scy	      softn->ipf_nat_table_sz * sizeof(u_int));
465145522Sdarrenr
466255332Scy	if (softn->ipf_nat_maxbucket == 0) {
467255332Scy		for (i = softn->ipf_nat_table_sz; i > 0; i >>= 1)
468255332Scy			softn->ipf_nat_maxbucket++;
469255332Scy		softn->ipf_nat_maxbucket *= 2;
470145522Sdarrenr	}
471145522Sdarrenr
472255332Scy	ipf_sttab_init(softc, softn->ipf_nat_tcptq);
473145522Sdarrenr	/*
474145522Sdarrenr	 * Increase this because we may have "keep state" following this too
475145522Sdarrenr	 * and packet storms can occur if this is removed too quickly.
476145522Sdarrenr	 */
477255332Scy	softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack;
478255332Scy	softn->ipf_nat_tcptq[IPF_TCP_NSTATES - 1].ifq_next =
479255332Scy							&softn->ipf_nat_udptq;
480145522Sdarrenr
481255332Scy	IPFTQ_INIT(&softn->ipf_nat_udptq, softn->ipf_nat_defage,
482255332Scy		   "nat ipftq udp tab");
483255332Scy	softn->ipf_nat_udptq.ifq_next = &softn->ipf_nat_udpacktq;
484255332Scy
485255332Scy	IPFTQ_INIT(&softn->ipf_nat_udpacktq, softn->ipf_nat_defage,
486255332Scy		   "nat ipftq udpack tab");
487255332Scy	softn->ipf_nat_udpacktq.ifq_next = &softn->ipf_nat_icmptq;
488255332Scy
489255332Scy	IPFTQ_INIT(&softn->ipf_nat_icmptq, softn->ipf_nat_deficmpage,
490255332Scy		   "nat icmp ipftq tab");
491255332Scy	softn->ipf_nat_icmptq.ifq_next = &softn->ipf_nat_icmpacktq;
492255332Scy
493255332Scy	IPFTQ_INIT(&softn->ipf_nat_icmpacktq, softn->ipf_nat_defage,
494255332Scy		   "nat icmpack ipftq tab");
495255332Scy	softn->ipf_nat_icmpacktq.ifq_next = &softn->ipf_nat_iptq;
496255332Scy
497255332Scy	IPFTQ_INIT(&softn->ipf_nat_iptq, softn->ipf_nat_defipage,
498255332Scy		   "nat ip ipftq tab");
499255332Scy	softn->ipf_nat_iptq.ifq_next = &softn->ipf_nat_pending;
500255332Scy
501255332Scy	IPFTQ_INIT(&softn->ipf_nat_pending, 1, "nat pending ipftq tab");
502255332Scy	softn->ipf_nat_pending.ifq_next = NULL;
503255332Scy
504255332Scy	for (i = 0, tq = softn->ipf_nat_tcptq; i < IPF_TCP_NSTATES; i++, tq++) {
505255332Scy		if (tq->ifq_ttl < softn->ipf_nat_deficmpage)
506255332Scy			tq->ifq_ttl = softn->ipf_nat_deficmpage;
507145522Sdarrenr#ifdef LARGE_NAT
508255332Scy		else if (tq->ifq_ttl > softn->ipf_nat_defage)
509255332Scy			tq->ifq_ttl = softn->ipf_nat_defage;
510145522Sdarrenr#endif
511145522Sdarrenr	}
512145522Sdarrenr
513145522Sdarrenr	/*
514145522Sdarrenr	 * Increase this because we may have "keep state" following
515145522Sdarrenr	 * this too and packet storms can occur if this is removed
516145522Sdarrenr	 * too quickly.
517145522Sdarrenr	 */
518255332Scy	softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack;
519145522Sdarrenr
520255332Scy	MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex");
521255332Scy	MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex");
522145522Sdarrenr
523255332Scy	softn->ipf_nat_inited = 1;
524145522Sdarrenr
52553642Sguido	return 0;
52653642Sguido}
52753642Sguido
52853642Sguido
529145522Sdarrenr/* ------------------------------------------------------------------------ */
530255332Scy/* Function:    ipf_nat_soft_fini                                           */
531145522Sdarrenr/* Returns:     Nil                                                         */
532255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
533255332Scy/*                                                                          */
534255332Scy/* Free all memory used by NAT structures allocated at runtime.             */
535255332Scy/* ------------------------------------------------------------------------ */
536255332Scyint
537255332Scyipf_nat_soft_fini(softc, arg)
538255332Scy	ipf_main_softc_t *softc;
539255332Scy	void *arg;
540255332Scy{
541255332Scy	ipf_nat_softc_t *softn = arg;
542255332Scy	ipftq_t *ifq, *ifqnext;
543255332Scy
544255332Scy	(void) ipf_nat_clearlist(softc, softn);
545255332Scy	(void) ipf_nat_flushtable(softc, softn);
546255332Scy
547255332Scy	/*
548255332Scy	 * Proxy timeout queues are not cleaned here because although they
549255332Scy	 * exist on the NAT list, ipf_proxy_unload is called after unload
550255332Scy	 * and the proxies actually are responsible for them being created.
551255332Scy	 * Should the proxy timeouts have their own list?  There's no real
552255332Scy	 * justification as this is the only complication.
553255332Scy	 */
554255332Scy	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) {
555255332Scy		ifqnext = ifq->ifq_next;
556255332Scy		if (ipf_deletetimeoutqueue(ifq) == 0)
557255332Scy			ipf_freetimeoutqueue(softc, ifq);
558255332Scy	}
559255332Scy
560255332Scy	if (softn->ipf_nat_table[0] != NULL) {
561255332Scy		KFREES(softn->ipf_nat_table[0],
562255332Scy		       sizeof(nat_t *) * softn->ipf_nat_table_sz);
563255332Scy		softn->ipf_nat_table[0] = NULL;
564255332Scy	}
565255332Scy	if (softn->ipf_nat_table[1] != NULL) {
566255332Scy		KFREES(softn->ipf_nat_table[1],
567255332Scy		       sizeof(nat_t *) * softn->ipf_nat_table_sz);
568255332Scy		softn->ipf_nat_table[1] = NULL;
569255332Scy	}
570255332Scy	if (softn->ipf_nat_map_rules != NULL) {
571255332Scy		KFREES(softn->ipf_nat_map_rules,
572255332Scy		       sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz);
573255332Scy		softn->ipf_nat_map_rules = NULL;
574255332Scy	}
575255332Scy	if (softn->ipf_nat_rdr_rules != NULL) {
576255332Scy		KFREES(softn->ipf_nat_rdr_rules,
577255332Scy		       sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz);
578255332Scy		softn->ipf_nat_rdr_rules = NULL;
579255332Scy	}
580255332Scy	if (softn->ipf_hm_maptable != NULL) {
581255332Scy		KFREES(softn->ipf_hm_maptable,
582255332Scy		       sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz);
583255332Scy		softn->ipf_hm_maptable = NULL;
584255332Scy	}
585255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) {
586255332Scy		KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
587255332Scy		       sizeof(u_int) * softn->ipf_nat_table_sz);
588255332Scy		softn->ipf_nat_stats.ns_side[0].ns_bucketlen = NULL;
589255332Scy	}
590255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) {
591255332Scy		KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
592255332Scy		       sizeof(u_int) * softn->ipf_nat_table_sz);
593255332Scy		softn->ipf_nat_stats.ns_side[1].ns_bucketlen = NULL;
594255332Scy	}
595255332Scy
596255332Scy	if (softn->ipf_nat_inited == 1) {
597255332Scy		softn->ipf_nat_inited = 0;
598255332Scy		ipf_sttab_destroy(softn->ipf_nat_tcptq);
599255332Scy
600255332Scy		MUTEX_DESTROY(&softn->ipf_nat_new);
601255332Scy		MUTEX_DESTROY(&softn->ipf_nat_io);
602255332Scy
603255332Scy		MUTEX_DESTROY(&softn->ipf_nat_udptq.ifq_lock);
604255332Scy		MUTEX_DESTROY(&softn->ipf_nat_udpacktq.ifq_lock);
605255332Scy		MUTEX_DESTROY(&softn->ipf_nat_icmptq.ifq_lock);
606255332Scy		MUTEX_DESTROY(&softn->ipf_nat_icmpacktq.ifq_lock);
607255332Scy		MUTEX_DESTROY(&softn->ipf_nat_iptq.ifq_lock);
608255332Scy		MUTEX_DESTROY(&softn->ipf_nat_pending.ifq_lock);
609255332Scy	}
610255332Scy
611255332Scy	return 0;
612255332Scy}
613255332Scy
614255332Scy
615255332Scy/* ------------------------------------------------------------------------ */
616255332Scy/* Function:    ipf_nat_setlock                                             */
617255332Scy/* Returns:     Nil                                                         */
618255332Scy/* Parameters:  arg(I) - pointer to soft state information                  */
619255332Scy/*              tmp(I) - new lock value                                     */
620255332Scy/*                                                                          */
621255332Scy/* Set the "lock status" of NAT to the value in tmp.                        */
622255332Scy/* ------------------------------------------------------------------------ */
623255332Scyvoid
624255332Scyipf_nat_setlock(arg, tmp)
625255332Scy	void *arg;
626255332Scy	int tmp;
627255332Scy{
628255332Scy	ipf_nat_softc_t *softn = arg;
629255332Scy
630255332Scy	softn->ipf_nat_lock = tmp;
631255332Scy}
632255332Scy
633255332Scy
634255332Scy/* ------------------------------------------------------------------------ */
635255332Scy/* Function:    ipf_nat_addrdr                                              */
636255332Scy/* Returns:     Nil                                                         */
637145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to add                           */
638145522Sdarrenr/*                                                                          */
639145522Sdarrenr/* Adds a redirect rule to the hash table of redirect rules and the list of */
640145522Sdarrenr/* loaded NAT rules.  Updates the bitmask indicating which netmasks are in  */
641145522Sdarrenr/* use by redirect rules.                                                   */
642145522Sdarrenr/* ------------------------------------------------------------------------ */
643255332Scystatic void
644255332Scyipf_nat_addrdr(softn, n)
645255332Scy	ipf_nat_softc_t *softn;
646255332Scy	ipnat_t *n;
64753642Sguido{
64860852Sdarrenr	ipnat_t **np;
64960852Sdarrenr	u_32_t j;
65053642Sguido	u_int hv;
651255332Scy	u_int rhv;
65260852Sdarrenr	int k;
65353642Sguido
654255332Scy	if (n->in_odstatype == FRI_NORMAL) {
655255332Scy		k = count4bits(n->in_odstmsk);
656255332Scy		ipf_inet_mask_add(k, &softn->ipf_nat_rdr_mask);
657255332Scy		j = (n->in_odstaddr & n->in_odstmsk);
658255332Scy		rhv = NAT_HASH_FN(j, 0, 0xffffffff);
659255332Scy	} else {
660255332Scy		ipf_inet_mask_add(0, &softn->ipf_nat_rdr_mask);
661255332Scy		j = 0;
662255332Scy		rhv = 0;
663255332Scy	}
664255332Scy	hv = rhv % softn->ipf_nat_rdrrules_sz;
665255332Scy	np = softn->ipf_nat_rdr_rules + hv;
66660852Sdarrenr	while (*np != NULL)
66760852Sdarrenr		np = &(*np)->in_rnext;
66860852Sdarrenr	n->in_rnext = NULL;
66960852Sdarrenr	n->in_prnext = np;
670255332Scy	n->in_hv[0] = hv;
671255332Scy	n->in_use++;
67260852Sdarrenr	*np = n;
67353642Sguido}
67453642Sguido
67553642Sguido
676145522Sdarrenr/* ------------------------------------------------------------------------ */
677255332Scy/* Function:    ipf_nat_addmap                                              */
678145522Sdarrenr/* Returns:     Nil                                                         */
679145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to add                           */
680145522Sdarrenr/*                                                                          */
681145522Sdarrenr/* Adds a NAT map rule to the hash table of rules and the list of  loaded   */
682145522Sdarrenr/* NAT rules.  Updates the bitmask indicating which netmasks are in use by  */
683145522Sdarrenr/* redirect rules.                                                          */
684145522Sdarrenr/* ------------------------------------------------------------------------ */
685255332Scystatic void
686255332Scyipf_nat_addmap(softn, n)
687255332Scy	ipf_nat_softc_t *softn;
688255332Scy	ipnat_t *n;
68960852Sdarrenr{
69060852Sdarrenr	ipnat_t **np;
69160852Sdarrenr	u_32_t j;
69260852Sdarrenr	u_int hv;
693255332Scy	u_int rhv;
69460852Sdarrenr	int k;
69560852Sdarrenr
696255332Scy	if (n->in_osrcatype == FRI_NORMAL) {
697255332Scy		k = count4bits(n->in_osrcmsk);
698255332Scy		ipf_inet_mask_add(k, &softn->ipf_nat_map_mask);
699255332Scy		j = (n->in_osrcaddr & n->in_osrcmsk);
700255332Scy		rhv = NAT_HASH_FN(j, 0, 0xffffffff);
701255332Scy	} else {
702255332Scy		ipf_inet_mask_add(0, &softn->ipf_nat_map_mask);
703255332Scy		j = 0;
704255332Scy		rhv = 0;
705255332Scy	}
706255332Scy	hv = rhv % softn->ipf_nat_maprules_sz;
707255332Scy	np = softn->ipf_nat_map_rules + hv;
70860852Sdarrenr	while (*np != NULL)
70960852Sdarrenr		np = &(*np)->in_mnext;
71060852Sdarrenr	n->in_mnext = NULL;
71160852Sdarrenr	n->in_pmnext = np;
712255332Scy	n->in_hv[1] = rhv;
713255332Scy	n->in_use++;
71460852Sdarrenr	*np = n;
71560852Sdarrenr}
71660852Sdarrenr
71760852Sdarrenr
718145522Sdarrenr/* ------------------------------------------------------------------------ */
719255332Scy/* Function:    ipf_nat_delrdr                                              */
720145522Sdarrenr/* Returns:     Nil                                                         */
721145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to delete                        */
722145522Sdarrenr/*                                                                          */
723145522Sdarrenr/* Removes a redirect rule from the hash table of redirect rules.           */
724145522Sdarrenr/* ------------------------------------------------------------------------ */
725255332Scyvoid
726255332Scyipf_nat_delrdr(softn, n)
727255332Scy	ipf_nat_softc_t *softn;
728255332Scy	ipnat_t *n;
72960852Sdarrenr{
730255332Scy	if (n->in_odstatype == FRI_NORMAL) {
731255332Scy		int k = count4bits(n->in_odstmsk);
732255332Scy		ipf_inet_mask_del(k, &softn->ipf_nat_rdr_mask);
733255332Scy	} else {
734255332Scy		ipf_inet_mask_del(0, &softn->ipf_nat_rdr_mask);
735255332Scy	}
73660852Sdarrenr	if (n->in_rnext)
73760852Sdarrenr		n->in_rnext->in_prnext = n->in_prnext;
73860852Sdarrenr	*n->in_prnext = n->in_rnext;
739255332Scy	n->in_use--;
74060852Sdarrenr}
74160852Sdarrenr
74260852Sdarrenr
743145522Sdarrenr/* ------------------------------------------------------------------------ */
744255332Scy/* Function:    ipf_nat_delmap                                              */
745145522Sdarrenr/* Returns:     Nil                                                         */
746145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to delete                        */
747145522Sdarrenr/*                                                                          */
748145522Sdarrenr/* Removes a NAT map rule from the hash table of NAT map rules.             */
749145522Sdarrenr/* ------------------------------------------------------------------------ */
750255332Scyvoid
751255332Scyipf_nat_delmap(softn, n)
752255332Scy	ipf_nat_softc_t *softn;
753255332Scy	ipnat_t *n;
75453642Sguido{
755255332Scy	if (n->in_osrcatype == FRI_NORMAL) {
756255332Scy		int k = count4bits(n->in_osrcmsk);
757255332Scy		ipf_inet_mask_del(k, &softn->ipf_nat_map_mask);
758255332Scy	} else {
759255332Scy		ipf_inet_mask_del(0, &softn->ipf_nat_map_mask);
760255332Scy	}
761145522Sdarrenr	if (n->in_mnext != NULL)
76260852Sdarrenr		n->in_mnext->in_pmnext = n->in_pmnext;
76360852Sdarrenr	*n->in_pmnext = n->in_mnext;
764255332Scy	n->in_use--;
76560852Sdarrenr}
76660852Sdarrenr
76760852Sdarrenr
768145522Sdarrenr/* ------------------------------------------------------------------------ */
769255332Scy/* Function:    ipf_nat_hostmap                                             */
770145522Sdarrenr/* Returns:     struct hostmap* - NULL if no hostmap could be created,      */
771145522Sdarrenr/*                                else a pointer to the hostmapping to use  */
772145522Sdarrenr/* Parameters:  np(I)   - pointer to NAT rule                               */
773145522Sdarrenr/*              real(I) - real IP address                                   */
774145522Sdarrenr/*              map(I)  - mapped IP address                                 */
775145522Sdarrenr/*              port(I) - destination port number                           */
776145522Sdarrenr/* Write Locks: ipf_nat                                                     */
777145522Sdarrenr/*                                                                          */
778145522Sdarrenr/* Check if an ip address has already been allocated for a given mapping    */
779145522Sdarrenr/* that is not doing port based translation.  If is not yet allocated, then */
780145522Sdarrenr/* create a new entry if a non-NULL NAT rule pointer has been supplied.     */
781145522Sdarrenr/* ------------------------------------------------------------------------ */
782255332Scystatic struct hostmap *
783255332Scyipf_nat_hostmap(softn, np, src, dst, map, port)
784255332Scy	ipf_nat_softc_t *softn;
785255332Scy	ipnat_t *np;
786255332Scy	struct in_addr src;
787255332Scy	struct in_addr dst;
788255332Scy	struct in_addr map;
789255332Scy	u_32_t port;
79060852Sdarrenr{
79160852Sdarrenr	hostmap_t *hm;
792255332Scy	u_int hv, rhv;
79353642Sguido
794145522Sdarrenr	hv = (src.s_addr ^ dst.s_addr);
795145522Sdarrenr	hv += src.s_addr;
796145522Sdarrenr	hv += dst.s_addr;
797255332Scy	rhv = hv;
798255332Scy	hv %= softn->ipf_nat_hostmap_sz;
799255332Scy	for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_hnext)
800255332Scy		if ((hm->hm_osrcip.s_addr == src.s_addr) &&
801255332Scy		    (hm->hm_odstip.s_addr == dst.s_addr) &&
802145522Sdarrenr		    ((np == NULL) || (np == hm->hm_ipnat)) &&
803145522Sdarrenr		    ((port == 0) || (port == hm->hm_port))) {
804255332Scy			softn->ipf_nat_stats.ns_hm_addref++;
80560852Sdarrenr			hm->hm_ref++;
80660852Sdarrenr			return hm;
80760852Sdarrenr		}
80860852Sdarrenr
809255332Scy	if (np == NULL) {
810255332Scy		softn->ipf_nat_stats.ns_hm_nullnp++;
811145522Sdarrenr		return NULL;
812255332Scy	}
813145522Sdarrenr
81460852Sdarrenr	KMALLOC(hm, hostmap_t *);
81560852Sdarrenr	if (hm) {
816255332Scy		hm->hm_next = softn->ipf_hm_maplist;
817255332Scy		hm->hm_pnext = &softn->ipf_hm_maplist;
818255332Scy		if (softn->ipf_hm_maplist != NULL)
819255332Scy			softn->ipf_hm_maplist->hm_pnext = &hm->hm_next;
820255332Scy		softn->ipf_hm_maplist = hm;
821255332Scy		hm->hm_hnext = softn->ipf_hm_maptable[hv];
822255332Scy		hm->hm_phnext = softn->ipf_hm_maptable + hv;
823255332Scy		if (softn->ipf_hm_maptable[hv] != NULL)
824255332Scy			softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext;
825255332Scy		softn->ipf_hm_maptable[hv] = hm;
82660852Sdarrenr		hm->hm_ipnat = np;
827255332Scy		np->in_use++;
828255332Scy		hm->hm_osrcip = src;
829255332Scy		hm->hm_odstip = dst;
830255332Scy		hm->hm_nsrcip = map;
831255332Scy		hm->hm_ndstip.s_addr = 0;
83260852Sdarrenr		hm->hm_ref = 1;
833145522Sdarrenr		hm->hm_port = port;
834255332Scy		hm->hm_hv = rhv;
835255332Scy		hm->hm_v = 4;
836255332Scy		softn->ipf_nat_stats.ns_hm_new++;
837255332Scy	} else {
838255332Scy		softn->ipf_nat_stats.ns_hm_newfail++;
83960852Sdarrenr	}
84060852Sdarrenr	return hm;
84153642Sguido}
84253642Sguido
84353642Sguido
844145522Sdarrenr/* ------------------------------------------------------------------------ */
845255332Scy/* Function:    ipf_nat_hostmapdel                                          */
846145522Sdarrenr/* Returns:     Nil                                                         */
847170268Sdarrenr/* Parameters:  hmp(I) - pointer to hostmap structure pointer               */
848145522Sdarrenr/* Write Locks: ipf_nat                                                     */
849145522Sdarrenr/*                                                                          */
850145522Sdarrenr/* Decrement the references to this hostmap structure by one.  If this      */
851145522Sdarrenr/* reaches zero then remove it and free it.                                 */
852145522Sdarrenr/* ------------------------------------------------------------------------ */
853255332Scyvoid
854255332Scyipf_nat_hostmapdel(softc, hmp)
855255332Scy	ipf_main_softc_t *softc;
856255332Scy	struct hostmap **hmp;
85760852Sdarrenr{
858170268Sdarrenr	struct hostmap *hm;
859170268Sdarrenr
860170268Sdarrenr	hm = *hmp;
861170268Sdarrenr	*hmp = NULL;
862170268Sdarrenr
863145522Sdarrenr	hm->hm_ref--;
86460852Sdarrenr	if (hm->hm_ref == 0) {
865255332Scy		ipf_nat_rule_deref(softc, &hm->hm_ipnat);
866170268Sdarrenr		if (hm->hm_hnext)
867170268Sdarrenr			hm->hm_hnext->hm_phnext = hm->hm_phnext;
868170268Sdarrenr		*hm->hm_phnext = hm->hm_hnext;
86960852Sdarrenr		if (hm->hm_next)
87060852Sdarrenr			hm->hm_next->hm_pnext = hm->hm_pnext;
87160852Sdarrenr		*hm->hm_pnext = hm->hm_next;
87260852Sdarrenr		KFREE(hm);
87360852Sdarrenr	}
87460852Sdarrenr}
87560852Sdarrenr
87660852Sdarrenr
877145522Sdarrenr/* ------------------------------------------------------------------------ */
878255332Scy/* Function:    ipf_fix_outcksum                                            */
879145522Sdarrenr/* Returns:     Nil                                                         */
880145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
881145522Sdarrenr/*              sp(I)  - location of 16bit checksum to update               */
882145522Sdarrenr/*              n((I)  - amount to adjust checksum by                       */
883145522Sdarrenr/*                                                                          */
884145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going out.                 */
885145522Sdarrenr/* ------------------------------------------------------------------------ */
886255332Scyvoid
887255332Scyipf_fix_outcksum(cksum, sp, n, partial)
888255332Scy	int cksum;
889255332Scy	u_short *sp;
890255332Scy	u_32_t n, partial;
89153642Sguido{
892145522Sdarrenr	u_short sumshort;
893145522Sdarrenr	u_32_t sum1;
89453642Sguido
895145522Sdarrenr	if (n == 0)
89653642Sguido		return;
897145522Sdarrenr
898255332Scy	if (cksum == 4) {
899255332Scy		*sp = 0;
90055929Sguido		return;
90155929Sguido	}
902255332Scy	if (cksum == 2) {
903255332Scy		sum1 = partial;
904255332Scy		sum1 = (sum1 & 0xffff) + (sum1 >> 16);
905255332Scy		*sp = htons(sum1);
906255332Scy		return;
907255332Scy	}
90853642Sguido	sum1 = (~ntohs(*sp)) & 0xffff;
90953642Sguido	sum1 += (n);
91053642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
91153642Sguido	/* Again */
91253642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
91353642Sguido	sumshort = ~(u_short)sum1;
91453642Sguido	*(sp) = htons(sumshort);
91553642Sguido}
91653642Sguido
91753642Sguido
918145522Sdarrenr/* ------------------------------------------------------------------------ */
919255332Scy/* Function:    ipf_fix_incksum                                             */
920145522Sdarrenr/* Returns:     Nil                                                         */
921145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
922145522Sdarrenr/*              sp(I)  - location of 16bit checksum to update               */
923145522Sdarrenr/*              n((I)  - amount to adjust checksum by                       */
924145522Sdarrenr/*                                                                          */
925145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going in.                  */
926145522Sdarrenr/* ------------------------------------------------------------------------ */
927255332Scyvoid
928255332Scyipf_fix_incksum(cksum, sp, n, partial)
929255332Scy	int cksum;
930255332Scy	u_short *sp;
931255332Scy	u_32_t n, partial;
93253642Sguido{
933145522Sdarrenr	u_short sumshort;
934145522Sdarrenr	u_32_t sum1;
93553642Sguido
936145522Sdarrenr	if (n == 0)
93753642Sguido		return;
938145522Sdarrenr
939255332Scy	if (cksum == 4) {
940255332Scy		*sp = 0;
94155929Sguido		return;
94255929Sguido	}
943255332Scy	if (cksum == 2) {
944255332Scy		sum1 = partial;
945255332Scy		sum1 = (sum1 & 0xffff) + (sum1 >> 16);
946255332Scy		*sp = htons(sum1);
947255332Scy		return;
948255332Scy	}
949255332Scy
95053642Sguido	sum1 = (~ntohs(*sp)) & 0xffff;
95153642Sguido	sum1 += ~(n) & 0xffff;
95253642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
95353642Sguido	/* Again */
95453642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
95553642Sguido	sumshort = ~(u_short)sum1;
95653642Sguido	*(sp) = htons(sumshort);
95753642Sguido}
95853642Sguido
95953642Sguido
960145522Sdarrenr/* ------------------------------------------------------------------------ */
961255332Scy/* Function:    ipf_fix_datacksum                                           */
962145522Sdarrenr/* Returns:     Nil                                                         */
963145522Sdarrenr/* Parameters:  sp(I)  - location of 16bit checksum to update               */
964145522Sdarrenr/*              n((I)  - amount to adjust checksum by                       */
965145522Sdarrenr/*                                                                          */
966145522Sdarrenr/* Fix_datacksum is used *only* for the adjustments of checksums in the     */
967145522Sdarrenr/* data section of an IP packet.                                            */
968145522Sdarrenr/*                                                                          */
969145522Sdarrenr/* The only situation in which you need to do this is when NAT'ing an       */
970145522Sdarrenr/* ICMP error message. Such a message, contains in its body the IP header   */
971145522Sdarrenr/* of the original IP packet, that causes the error.                        */
972145522Sdarrenr/*                                                                          */
973145522Sdarrenr/* You can't use fix_incksum or fix_outcksum in that case, because for the  */
974145522Sdarrenr/* kernel the data section of the ICMP error is just data, and no special   */
975145522Sdarrenr/* processing like hardware cksum or ntohs processing have been done by the */
976145522Sdarrenr/* kernel on the data section.                                              */
977145522Sdarrenr/* ------------------------------------------------------------------------ */
978255332Scyvoid
979255332Scyipf_fix_datacksum(sp, n)
980255332Scy	u_short *sp;
981255332Scy	u_32_t n;
98267614Sdarrenr{
983145522Sdarrenr	u_short sumshort;
984145522Sdarrenr	u_32_t sum1;
98567614Sdarrenr
986145522Sdarrenr	if (n == 0)
98767614Sdarrenr		return;
98867614Sdarrenr
98967614Sdarrenr	sum1 = (~ntohs(*sp)) & 0xffff;
99067614Sdarrenr	sum1 += (n);
99167614Sdarrenr	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
99267614Sdarrenr	/* Again */
99367614Sdarrenr	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
99467614Sdarrenr	sumshort = ~(u_short)sum1;
99567614Sdarrenr	*(sp) = htons(sumshort);
99667614Sdarrenr}
99767614Sdarrenr
99853642Sguido
999145522Sdarrenr/* ------------------------------------------------------------------------ */
1000255332Scy/* Function:    ipf_nat_ioctl                                               */
1001145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
1002255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1003255332Scy/*              data(I)  - pointer to ioctl data                            */
1004255332Scy/*              cmd(I)   - ioctl command integer                            */
1005255332Scy/*              mode(I)  - file mode bits used with open                    */
1006255332Scy/*              uid(I)   - uid of calling process                           */
1007255332Scy/*              ctx(I)   - pointer used as key for finding context          */
1008145522Sdarrenr/*                                                                          */
1009145522Sdarrenr/* Processes an ioctl call made to operate on the IP Filter NAT device.     */
1010145522Sdarrenr/* ------------------------------------------------------------------------ */
1011255332Scyint
1012255332Scyipf_nat_ioctl(softc, data, cmd, mode, uid, ctx)
1013255332Scy	ipf_main_softc_t *softc;
1014255332Scy	ioctlcmd_t cmd;
1015255332Scy	caddr_t data;
1016255332Scy	int mode, uid;
1017255332Scy	void *ctx;
101853642Sguido{
1019255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
102095418Sdarrenr	int error = 0, ret, arg, getlock;
1021255332Scy	ipnat_t *nat, *nt, *n;
102253642Sguido	ipnat_t natd;
1023170268Sdarrenr	SPL_INT(s);
102453642Sguido
1025255332Scy#if BSD_GE_YEAR(199306) && defined(_KERNEL)
1026255332Scy# if NETBSD_GE_REV(399002000)
1027170268Sdarrenr	if ((mode & FWRITE) &&
1028170268Sdarrenr	     kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL,
1029170268Sdarrenr				     KAUTH_REQ_NETWORK_FIREWALL_FW,
1030255332Scy				     NULL, NULL, NULL))
1031170268Sdarrenr# else
1032192895Sjamie#  if defined(__FreeBSD_version) && (__FreeBSD_version >= 500034)
1033255332Scy	if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE))
1034192895Sjamie#  else
1035255332Scy	if ((securelevel >= 3) && (mode & FWRITE))
1036192895Sjamie#  endif
1037255332Scy# endif
1038255332Scy	{
1039255332Scy		IPFERROR(60001);
1040170268Sdarrenr		return EPERM;
1041170268Sdarrenr	}
104253642Sguido#endif
104353642Sguido
1044145522Sdarrenr#if defined(__osf__) && defined(_KERNEL)
1045145522Sdarrenr	getlock = 0;
1046145522Sdarrenr#else
1047145522Sdarrenr	getlock = (mode & NAT_LOCKHELD) ? 0 : 1;
1048145522Sdarrenr#endif
1049145522Sdarrenr
1050255332Scy	n = NULL;
1051255332Scy	nt = NULL;
1052255332Scy	nat = NULL;
1053145522Sdarrenr
1054255332Scy	if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT) ||
1055255332Scy	    (cmd == (ioctlcmd_t)SIOCPURGENAT)) {
105695418Sdarrenr		if (mode & NAT_SYSSPACE) {
105795418Sdarrenr			bcopy(data, (char *)&natd, sizeof(natd));
1058255332Scy			nat = &natd;
105995418Sdarrenr			error = 0;
106095418Sdarrenr		} else {
1061255332Scy			bzero(&natd, sizeof(natd));
1062255332Scy			error = ipf_inobj(softc, data, NULL, &natd,
1063255332Scy					  IPFOBJ_IPNAT);
1064255332Scy			if (error != 0)
1065255332Scy				goto done;
1066255332Scy
1067255332Scy			if (natd.in_size < sizeof(ipnat_t)) {
1068255332Scy				error = EINVAL;
1069255332Scy				goto done;
1070255332Scy			}
1071255332Scy			KMALLOCS(nt, ipnat_t *, natd.in_size);
1072255332Scy			if (nt == NULL) {
1073255332Scy				IPFERROR(60070);
1074255332Scy				error = ENOMEM;
1075255332Scy				goto done;
1076255332Scy			}
1077255332Scy			bzero(nt, natd.in_size);
1078255332Scy			error = ipf_inobjsz(softc, data, nt, IPFOBJ_IPNAT,
1079255332Scy					    natd.in_size);
1080255332Scy			if (error)
1081255332Scy				goto done;
1082255332Scy			nat = nt;
108395418Sdarrenr		}
108453642Sguido
1085255332Scy		/*
1086255332Scy		 * For add/delete, look to see if the NAT entry is
1087255332Scy		 * already present
1088255332Scy		 */
108953642Sguido		nat->in_flags &= IPN_USERFLAGS;
109053642Sguido		if ((nat->in_redir & NAT_MAPBLK) == 0) {
1091255332Scy			if (nat->in_osrcatype == FRI_NORMAL ||
1092255332Scy			    nat->in_osrcatype == FRI_NONE)
1093255332Scy				nat->in_osrcaddr &= nat->in_osrcmsk;
1094255332Scy			if (nat->in_odstatype == FRI_NORMAL ||
1095255332Scy			    nat->in_odstatype == FRI_NONE)
1096255332Scy				nat->in_odstaddr &= nat->in_odstmsk;
1097255332Scy			if ((nat->in_flags & (IPN_SPLIT|IPN_SIPRANGE)) == 0) {
1098255332Scy				if (nat->in_nsrcatype == FRI_NORMAL)
1099255332Scy					nat->in_nsrcaddr &= nat->in_nsrcmsk;
1100255332Scy				if (nat->in_ndstatype == FRI_NORMAL)
1101255332Scy					nat->in_ndstaddr &= nat->in_ndstmsk;
1102255332Scy			}
110353642Sguido		}
1104255332Scy
1105255332Scy		error = ipf_nat_rule_init(softc, softn, nat);
1106255332Scy		if (error != 0)
1107255332Scy			goto done;
1108255332Scy
1109255332Scy		MUTEX_ENTER(&softn->ipf_nat_io);
1110255332Scy		for (n = softn->ipf_nat_list; n != NULL; n = n->in_next)
1111255332Scy			if (ipf_nat_cmp_rules(nat, n) == 0)
111253642Sguido				break;
111353642Sguido	}
111453642Sguido
111553642Sguido	switch (cmd)
111653642Sguido	{
111755929Sguido#ifdef  IPFILTER_LOG
111855929Sguido	case SIOCIPFFB :
111960852Sdarrenr	{
112060852Sdarrenr		int tmp;
112160852Sdarrenr
1122255332Scy		if (!(mode & FWRITE)) {
1123255332Scy			IPFERROR(60002);
112455929Sguido			error = EPERM;
1125255332Scy		} else {
1126255332Scy			tmp = ipf_log_clear(softc, IPL_LOGNAT);
1127255332Scy			error = BCOPYOUT(&tmp, data, sizeof(tmp));
1128255332Scy			if (error != 0) {
1129255332Scy				IPFERROR(60057);
1130170268Sdarrenr				error = EFAULT;
1131255332Scy			}
113260852Sdarrenr		}
113355929Sguido		break;
113460852Sdarrenr	}
1135170268Sdarrenr
1136145522Sdarrenr	case SIOCSETLG :
1137255332Scy		if (!(mode & FWRITE)) {
1138255332Scy			IPFERROR(60003);
1139145522Sdarrenr			error = EPERM;
1140255332Scy		} else {
1141255332Scy			error = BCOPYIN(data, &softn->ipf_nat_logging,
1142255332Scy					sizeof(softn->ipf_nat_logging));
1143170268Sdarrenr			if (error != 0)
1144170268Sdarrenr				error = EFAULT;
1145145522Sdarrenr		}
1146145522Sdarrenr		break;
1147170268Sdarrenr
1148145522Sdarrenr	case SIOCGETLG :
1149255332Scy		error = BCOPYOUT(&softn->ipf_nat_logging, data,
1150255332Scy				 sizeof(softn->ipf_nat_logging));
1151255332Scy		if (error != 0) {
1152255332Scy			IPFERROR(60004);
1153170268Sdarrenr			error = EFAULT;
1154255332Scy		}
1155145522Sdarrenr		break;
1156170268Sdarrenr
1157145522Sdarrenr	case FIONREAD :
1158255332Scy		arg = ipf_log_bytesused(softc, IPL_LOGNAT);
1159170268Sdarrenr		error = BCOPYOUT(&arg, data, sizeof(arg));
1160255332Scy		if (error != 0) {
1161255332Scy			IPFERROR(60005);
1162170268Sdarrenr			error = EFAULT;
1163255332Scy		}
1164145522Sdarrenr		break;
116555929Sguido#endif
116653642Sguido	case SIOCADNAT :
116753642Sguido		if (!(mode & FWRITE)) {
1168255332Scy			IPFERROR(60006);
116953642Sguido			error = EPERM;
1170145522Sdarrenr		} else if (n != NULL) {
1171255332Scy			natd.in_flineno = n->in_flineno;
1172255332Scy			(void) ipf_outobj(softc, data, &natd, IPFOBJ_IPNAT);
1173255332Scy			IPFERROR(60007);
117453642Sguido			error = EEXIST;
1175145522Sdarrenr		} else if (nt == NULL) {
1176255332Scy			IPFERROR(60008);
1177145522Sdarrenr			error = ENOMEM;
117853642Sguido		}
1179145522Sdarrenr		if (error != 0) {
1180255332Scy			MUTEX_EXIT(&softn->ipf_nat_io);
118153642Sguido			break;
118253642Sguido		}
1183255332Scy		if (nat != nt)
1184255332Scy			bcopy((char *)nat, (char *)nt, sizeof(*n));
1185255332Scy		error = ipf_nat_siocaddnat(softc, softn, nt, getlock);
1186255332Scy		MUTEX_EXIT(&softn->ipf_nat_io);
1187255332Scy		if (error == 0) {
1188255332Scy			nat = NULL;
1189145522Sdarrenr			nt = NULL;
1190255332Scy		}
119153642Sguido		break;
1192170268Sdarrenr
119353642Sguido	case SIOCRMNAT :
1194255332Scy	case SIOCPURGENAT :
119553642Sguido		if (!(mode & FWRITE)) {
1196255332Scy			IPFERROR(60009);
119753642Sguido			error = EPERM;
119853642Sguido			n = NULL;
1199145522Sdarrenr		} else if (n == NULL) {
1200255332Scy			IPFERROR(60010);
1201145522Sdarrenr			error = ESRCH;
120253642Sguido		}
1203145522Sdarrenr
1204145522Sdarrenr		if (error != 0) {
1205255332Scy			MUTEX_EXIT(&softn->ipf_nat_io);
120653642Sguido			break;
120753642Sguido		}
1208255332Scy		if (cmd == (ioctlcmd_t)SIOCPURGENAT) {
1209255332Scy			error = ipf_outobjsz(softc, data, n, IPFOBJ_IPNAT,
1210255332Scy					     n->in_size);
1211255332Scy			if (error) {
1212255332Scy				MUTEX_EXIT(&softn->ipf_nat_io);
1213255332Scy				goto done;
1214255332Scy			}
1215255332Scy			n->in_flags |= IPN_PURGE;
1216255332Scy		}
1217255332Scy		ipf_nat_siocdelnat(softc, softn, n, getlock);
1218145522Sdarrenr
1219255332Scy		MUTEX_EXIT(&softn->ipf_nat_io);
122053642Sguido		n = NULL;
122153642Sguido		break;
1222170268Sdarrenr
122353642Sguido	case SIOCGNATS :
1224255332Scy	    {
1225255332Scy		natstat_t *nsp = &softn->ipf_nat_stats;
1226255332Scy
1227255332Scy		nsp->ns_side[0].ns_table = softn->ipf_nat_table[0];
1228255332Scy		nsp->ns_side[1].ns_table = softn->ipf_nat_table[1];
1229255332Scy		nsp->ns_list = softn->ipf_nat_list;
1230255332Scy		nsp->ns_maptable = softn->ipf_hm_maptable;
1231255332Scy		nsp->ns_maplist = softn->ipf_hm_maplist;
1232255332Scy		nsp->ns_nattab_sz = softn->ipf_nat_table_sz;
1233255332Scy		nsp->ns_nattab_max = softn->ipf_nat_table_max;
1234255332Scy		nsp->ns_rultab_sz = softn->ipf_nat_maprules_sz;
1235255332Scy		nsp->ns_rdrtab_sz = softn->ipf_nat_rdrrules_sz;
1236255332Scy		nsp->ns_hostmap_sz = softn->ipf_nat_hostmap_sz;
1237255332Scy		nsp->ns_instances = softn->ipf_nat_instances;
1238255332Scy		nsp->ns_ticks = softc->ipf_ticks;
1239255332Scy#ifdef IPFILTER_LOGGING
1240255332Scy		nsp->ns_log_ok = ipf_log_logok(softc, IPF_LOGNAT);
1241255332Scy		nsp->ns_log_fail = ipf_log_failures(softc, IPF_LOGNAT);
1242255332Scy#else
1243255332Scy		nsp->ns_log_ok = 0;
1244255332Scy		nsp->ns_log_fail = 0;
1245255332Scy#endif
1246255332Scy		error = ipf_outobj(softc, data, nsp, IPFOBJ_NATSTAT);
124753642Sguido		break;
1248255332Scy	    }
1249170268Sdarrenr
125053642Sguido	case SIOCGNATL :
125153642Sguido	    {
125253642Sguido		natlookup_t nl;
125353642Sguido
1254255332Scy		error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP);
1255145522Sdarrenr		if (error == 0) {
1256173181Sdarrenr			void *ptr;
1257173181Sdarrenr
1258173181Sdarrenr			if (getlock) {
1259255332Scy				READ_ENTER(&softc->ipf_nat);
1260173181Sdarrenr			}
1261255332Scy
1262255332Scy			switch (nl.nl_v)
1263255332Scy			{
1264255332Scy			case 4 :
1265255332Scy				ptr = ipf_nat_lookupredir(&nl);
1266255332Scy				break;
1267255332Scy#ifdef USE_INET6
1268255332Scy			case 6 :
1269255332Scy				ptr = ipf_nat6_lookupredir(&nl);
1270255332Scy				break;
1271255332Scy#endif
1272255332Scy			default:
1273255332Scy				ptr = NULL;
1274255332Scy				break;
1275255332Scy			}
1276255332Scy
1277173181Sdarrenr			if (getlock) {
1278255332Scy				RWLOCK_EXIT(&softc->ipf_nat);
1279173181Sdarrenr			}
1280173181Sdarrenr			if (ptr != NULL) {
1281255332Scy				error = ipf_outobj(softc, data, &nl,
1282255332Scy						   IPFOBJ_NATLOOKUP);
1283145522Sdarrenr			} else {
1284255332Scy				IPFERROR(60011);
1285145522Sdarrenr				error = ESRCH;
1286145522Sdarrenr			}
1287145522Sdarrenr		}
128853642Sguido		break;
128953642Sguido	    }
1290170268Sdarrenr
129160852Sdarrenr	case SIOCIPFFL :	/* old SIOCFLNAT & SIOCCNATL */
129253642Sguido		if (!(mode & FWRITE)) {
1293255332Scy			IPFERROR(60012);
129453642Sguido			error = EPERM;
129553642Sguido			break;
129653642Sguido		}
1297145522Sdarrenr		if (getlock) {
1298255332Scy			WRITE_ENTER(&softc->ipf_nat);
1299145522Sdarrenr		}
1300170268Sdarrenr
1301170268Sdarrenr		error = BCOPYIN(data, &arg, sizeof(arg));
1302255332Scy		if (error != 0) {
1303255332Scy			IPFERROR(60013);
1304170268Sdarrenr			error = EFAULT;
1305255332Scy		} else {
1306170268Sdarrenr			if (arg == 0)
1307255332Scy				ret = ipf_nat_flushtable(softc, softn);
1308170268Sdarrenr			else if (arg == 1)
1309255332Scy				ret = ipf_nat_clearlist(softc, softn);
1310170268Sdarrenr			else
1311255332Scy				ret = ipf_nat_extraflush(softc, softn, arg);
1312255332Scy			ipf_proxy_flush(softc->ipf_proxy_soft, arg);
1313170268Sdarrenr		}
1314170268Sdarrenr
1315145522Sdarrenr		if (getlock) {
1316255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
131760852Sdarrenr		}
1318145522Sdarrenr		if (error == 0) {
1319170268Sdarrenr			error = BCOPYOUT(&ret, data, sizeof(ret));
1320145522Sdarrenr		}
132153642Sguido		break;
1322170268Sdarrenr
1323255332Scy	case SIOCMATCHFLUSH :
1324255332Scy		if (!(mode & FWRITE)) {
1325255332Scy			IPFERROR(60014);
1326255332Scy			error = EPERM;
1327255332Scy			break;
1328255332Scy		}
1329255332Scy		if (getlock) {
1330255332Scy			WRITE_ENTER(&softc->ipf_nat);
1331255332Scy		}
1332255332Scy
1333255332Scy		error = ipf_nat_matchflush(softc, softn, data);
1334255332Scy
1335255332Scy		if (getlock) {
1336255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
1337255332Scy		}
1338255332Scy		break;
1339255332Scy
1340145522Sdarrenr	case SIOCPROXY :
1341255332Scy		error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx);
1342145522Sdarrenr		break;
1343170268Sdarrenr
134460852Sdarrenr	case SIOCSTLCK :
1345153876Sguido		if (!(mode & FWRITE)) {
1346255332Scy			IPFERROR(60015);
1347153876Sguido			error = EPERM;
1348153876Sguido		} else {
1349255332Scy			error = ipf_lock(data, &softn->ipf_nat_lock);
1350153876Sguido		}
135153642Sguido		break;
1352170268Sdarrenr
135360852Sdarrenr	case SIOCSTPUT :
1354153876Sguido		if ((mode & FWRITE) != 0) {
1355255332Scy			error = ipf_nat_putent(softc, data, getlock);
1356145522Sdarrenr		} else {
1357255332Scy			IPFERROR(60016);
135860852Sdarrenr			error = EACCES;
1359145522Sdarrenr		}
136060852Sdarrenr		break;
1361170268Sdarrenr
136260852Sdarrenr	case SIOCSTGSZ :
1363255332Scy		if (softn->ipf_nat_lock) {
1364255332Scy			error = ipf_nat_getsz(softc, data, getlock);
1365255332Scy		} else {
1366255332Scy			IPFERROR(60017);
136760852Sdarrenr			error = EACCES;
1368255332Scy		}
136960852Sdarrenr		break;
1370170268Sdarrenr
137160852Sdarrenr	case SIOCSTGET :
1372255332Scy		if (softn->ipf_nat_lock) {
1373255332Scy			error = ipf_nat_getent(softc, data, getlock);
1374255332Scy		} else {
1375255332Scy			IPFERROR(60018);
137660852Sdarrenr			error = EACCES;
1377255332Scy		}
137860852Sdarrenr		break;
1379170268Sdarrenr
1380170268Sdarrenr	case SIOCGENITER :
1381170268Sdarrenr	    {
1382170268Sdarrenr		ipfgeniter_t iter;
1383170268Sdarrenr		ipftoken_t *token;
1384255332Scy		ipfobj_t obj;
1385170268Sdarrenr
1386255332Scy		error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER);
1387255332Scy		if (error != 0)
1388255332Scy			break;
1389255332Scy
1390170268Sdarrenr		SPL_SCHED(s);
1391255332Scy		token = ipf_token_find(softc, iter.igi_type, uid, ctx);
1392255332Scy		if (token != NULL) {
1393255332Scy			error  = ipf_nat_iterator(softc, token, &iter, &obj);
1394255332Scy			WRITE_ENTER(&softc->ipf_tokens);
1395255332Scy			ipf_token_deref(softc, token);
1396255332Scy			RWLOCK_EXIT(&softc->ipf_tokens);
1397170268Sdarrenr		}
1398170268Sdarrenr		SPL_X(s);
1399170268Sdarrenr		break;
1400170268Sdarrenr	    }
1401170268Sdarrenr
1402170268Sdarrenr	case SIOCIPFDELTOK :
1403255332Scy		error = BCOPYIN(data, &arg, sizeof(arg));
1404170268Sdarrenr		if (error == 0) {
1405170268Sdarrenr			SPL_SCHED(s);
1406255332Scy			error = ipf_token_del(softc, arg, uid, ctx);
1407170268Sdarrenr			SPL_X(s);
1408170268Sdarrenr		} else {
1409255332Scy			IPFERROR(60019);
1410170268Sdarrenr			error = EFAULT;
1411170268Sdarrenr		}
1412170268Sdarrenr		break;
1413170268Sdarrenr
1414170268Sdarrenr	case SIOCGTQTAB :
1415255332Scy		error = ipf_outobj(softc, data, softn->ipf_nat_tcptq,
1416255332Scy				   IPFOBJ_STATETQTAB);
1417170268Sdarrenr		break;
1418170268Sdarrenr
1419172776Sdarrenr	case SIOCGTABL :
1420255332Scy		error = ipf_nat_gettable(softc, softn, data);
1421172776Sdarrenr		break;
1422172776Sdarrenr
142353642Sguido	default :
1424255332Scy		IPFERROR(60020);
142553642Sguido		error = EINVAL;
142653642Sguido		break;
142753642Sguido	}
142860852Sdarrenrdone:
1429255332Scy	if (nat != NULL)
1430255332Scy		ipf_nat_rule_fini(softc, nat);
1431170268Sdarrenr	if (nt != NULL)
1432255332Scy		KFREES(nt, nt->in_size);
143353642Sguido	return error;
143453642Sguido}
143553642Sguido
143653642Sguido
1437145522Sdarrenr/* ------------------------------------------------------------------------ */
1438255332Scy/* Function:    ipf_nat_siocaddnat                                          */
1439145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
1440255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1441255332Scy/*              softn(I) - pointer to NAT context structure                 */
1442255332Scy/*              n(I)       - pointer to new NAT rule                        */
1443145522Sdarrenr/*              np(I)      - pointer to where to insert new NAT rule        */
1444255332Scy/*              getlock(I) - flag indicating if lock on  is held            */
1445255332Scy/* Mutex Locks: ipf_nat_io                                                   */
1446145522Sdarrenr/*                                                                          */
1447145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1448145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1449145522Sdarrenr/* NAT rule table(s).                                                       */
1450145522Sdarrenr/* ------------------------------------------------------------------------ */
1451255332Scystatic int
1452255332Scyipf_nat_siocaddnat(softc, softn, n, getlock)
1453255332Scy	ipf_main_softc_t *softc;
1454255332Scy	ipf_nat_softc_t *softn;
1455255332Scy	ipnat_t *n;
1456255332Scy	int getlock;
1457145522Sdarrenr{
1458255332Scy	int error = 0;
1459145522Sdarrenr
1460255332Scy	if (ipf_nat_resolverule(softc, n) != 0) {
1461255332Scy		IPFERROR(60022);
1462161356Sguido		return ENOENT;
1463255332Scy	}
1464145522Sdarrenr
1465255332Scy	if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) {
1466255332Scy		IPFERROR(60023);
1467145522Sdarrenr		return EINVAL;
1468255332Scy	}
1469145522Sdarrenr
1470255332Scy	if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) {
1471145522Sdarrenr		/*
1472255332Scy		 * Prerecord whether or not the destination of the divert
1473255332Scy		 * is local or not to the interface the packet is going
1474255332Scy		 * to be sent out.
1475145522Sdarrenr		 */
1476255332Scy		n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1],
1477255332Scy						n->in_ifps[1], &n->in_ndstip6);
1478145522Sdarrenr	}
1479145522Sdarrenr
1480145522Sdarrenr	if (getlock) {
1481255332Scy		WRITE_ENTER(&softc->ipf_nat);
1482145522Sdarrenr	}
1483145522Sdarrenr	n->in_next = NULL;
1484255332Scy	n->in_pnext = softn->ipf_nat_list_tail;
1485255332Scy	*n->in_pnext = n;
1486255332Scy	softn->ipf_nat_list_tail = &n->in_next;
1487255332Scy	n->in_use++;
1488145522Sdarrenr
1489145522Sdarrenr	if (n->in_redir & NAT_REDIRECT) {
1490145522Sdarrenr		n->in_flags &= ~IPN_NOTDST;
1491255332Scy		switch (n->in_v[0])
1492255332Scy		{
1493255332Scy		case 4 :
1494255332Scy			ipf_nat_addrdr(softn, n);
1495255332Scy			break;
1496255332Scy#ifdef USE_INET6
1497255332Scy		case 6 :
1498255332Scy			ipf_nat6_addrdr(softn, n);
1499255332Scy			break;
1500255332Scy#endif
1501255332Scy		default :
1502255332Scy			break;
1503255332Scy		}
1504255332Scy		ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_rdr);
1505145522Sdarrenr	}
1506255332Scy
1507145522Sdarrenr	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
1508145522Sdarrenr		n->in_flags &= ~IPN_NOTSRC;
1509255332Scy		switch (n->in_v[0])
1510255332Scy		{
1511255332Scy		case 4 :
1512255332Scy			ipf_nat_addmap(softn, n);
1513255332Scy			break;
1514255332Scy#ifdef USE_INET6
1515255332Scy		case 6 :
1516255332Scy			ipf_nat6_addmap(softn, n);
1517255332Scy			break;
1518255332Scy#endif
1519255332Scy		default :
1520255332Scy			break;
1521255332Scy		}
1522255332Scy		ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_map);
1523145522Sdarrenr	}
1524255332Scy
1525255332Scy	if (n->in_age[0] != 0)
1526255332Scy		n->in_tqehead[0] = ipf_addtimeoutqueue(softc,
1527255332Scy						       &softn->ipf_nat_utqe,
1528255332Scy						       n->in_age[0]);
1529255332Scy
1530255332Scy	if (n->in_age[1] != 0)
1531255332Scy		n->in_tqehead[1] = ipf_addtimeoutqueue(softc,
1532255332Scy						       &softn->ipf_nat_utqe,
1533255332Scy						       n->in_age[1]);
1534255332Scy
1535170268Sdarrenr	MUTEX_INIT(&n->in_lock, "ipnat rule lock");
1536170268Sdarrenr
1537145522Sdarrenr	n = NULL;
1538255332Scy	ATOMIC_INC32(softn->ipf_nat_stats.ns_rules);
1539255332Scy#if SOLARIS && !defined(INSTANCES)
1540145522Sdarrenr	pfil_delayed_copy = 0;
1541145522Sdarrenr#endif
1542145522Sdarrenr	if (getlock) {
1543255332Scy		RWLOCK_EXIT(&softc->ipf_nat);			/* WRITE */
1544145522Sdarrenr	}
1545145522Sdarrenr
1546145522Sdarrenr	return error;
1547145522Sdarrenr}
1548145522Sdarrenr
1549145522Sdarrenr
1550145522Sdarrenr/* ------------------------------------------------------------------------ */
1551255332Scy/* Function:    ipf_nat_ruleaddrinit                                        */
1552255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1553255332Scy/*              softn(I) - pointer to NAT context structure                 */
1554255332Scy/*              n(I)     - pointer to NAT rule                              */
1555255332Scy/*                                                                          */
1556255332Scy/* Initialise all of the NAT address structures in a NAT rule.              */
1557255332Scy/* ------------------------------------------------------------------------ */
1558255332Scystatic int
1559255332Scyipf_nat_ruleaddrinit(softc, softn, n)
1560255332Scy	ipf_main_softc_t *softc;
1561255332Scy	ipf_nat_softc_t *softn;
1562255332Scy	ipnat_t *n;
1563255332Scy{
1564255332Scy	int idx, error;
1565255332Scy
1566255332Scy	if ((n->in_ndst.na_atype == FRI_LOOKUP) &&
1567255332Scy	    (n->in_ndst.na_type != IPLT_DSTLIST)) {
1568255332Scy		IPFERROR(60071);
1569255332Scy		return EINVAL;
1570255332Scy	}
1571255332Scy	if ((n->in_nsrc.na_atype == FRI_LOOKUP) &&
1572255332Scy	    (n->in_nsrc.na_type != IPLT_DSTLIST)) {
1573255332Scy		IPFERROR(60069);
1574255332Scy		return EINVAL;
1575255332Scy	}
1576255332Scy
1577255332Scy	if (n->in_redir == NAT_BIMAP) {
1578255332Scy		n->in_ndstaddr = n->in_osrcaddr;
1579255332Scy		n->in_ndstmsk = n->in_osrcmsk;
1580255332Scy		n->in_odstaddr = n->in_nsrcaddr;
1581255332Scy		n->in_odstmsk = n->in_nsrcmsk;
1582255332Scy
1583255332Scy	}
1584255332Scy
1585255332Scy	if (n->in_redir & NAT_REDIRECT)
1586255332Scy		idx = 1;
1587255332Scy	else
1588255332Scy		idx = 0;
1589255332Scy	/*
1590255332Scy	 * Initialise all of the address fields.
1591255332Scy	 */
1592255332Scy	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 1,
1593255332Scy				     n->in_ifps[idx]);
1594255332Scy	if (error != 0)
1595255332Scy		return error;
1596255332Scy
1597255332Scy	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 1,
1598255332Scy				     n->in_ifps[idx]);
1599255332Scy	if (error != 0)
1600255332Scy		return error;
1601255332Scy
1602255332Scy	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1,
1603255332Scy				     n->in_ifps[idx]);
1604255332Scy	if (error != 0)
1605255332Scy		return error;
1606255332Scy
1607255332Scy	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 1,
1608255332Scy				     n->in_ifps[idx]);
1609255332Scy	if (error != 0)
1610255332Scy		return error;
1611255332Scy
1612255332Scy	if (n->in_redir & NAT_DIVERTUDP)
1613255332Scy		ipf_nat_builddivertmp(softn, n);
1614255332Scy
1615255332Scy	return 0;
1616255332Scy}
1617255332Scy
1618255332Scy
1619255332Scy/* ------------------------------------------------------------------------ */
1620255332Scy/* Function:    ipf_nat_resolvrule                                          */
1621145522Sdarrenr/* Returns:     Nil                                                         */
1622255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1623255332Scy/*              n(I)     - pointer to NAT rule                              */
1624145522Sdarrenr/*                                                                          */
1625145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1626145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1627145522Sdarrenr/* NAT rule table(s).                                                       */
1628145522Sdarrenr/* ------------------------------------------------------------------------ */
1629255332Scystatic int
1630255332Scyipf_nat_resolverule(softc, n)
1631255332Scy	ipf_main_softc_t *softc;
1632255332Scy	ipnat_t *n;
1633145522Sdarrenr{
1634255332Scy	char *base;
1635145522Sdarrenr
1636255332Scy	base = n->in_names;
1637255332Scy
1638255332Scy	n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0],
1639255332Scy				       n->in_v[0]);
1640255332Scy
1641255332Scy	if (n->in_ifnames[1] == -1) {
1642255332Scy		n->in_ifnames[1] = n->in_ifnames[0];
1643145522Sdarrenr		n->in_ifps[1] = n->in_ifps[0];
1644145522Sdarrenr	} else {
1645255332Scy		n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1],
1646255332Scy					       n->in_v[1]);
1647145522Sdarrenr	}
1648145522Sdarrenr
1649255332Scy	if (n->in_plabel != -1) {
1650255332Scy		if (n->in_redir & NAT_REDIRECT)
1651255332Scy			n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft,
1652255332Scy						     n->in_pr[0],
1653255332Scy						     base + n->in_plabel);
1654255332Scy		else
1655255332Scy			n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft,
1656255332Scy						     n->in_pr[1],
1657255332Scy						     base + n->in_plabel);
1658161356Sguido		if (n->in_apr == NULL)
1659161356Sguido			return -1;
1660145522Sdarrenr	}
1661161356Sguido	return 0;
1662145522Sdarrenr}
1663145522Sdarrenr
1664145522Sdarrenr
1665145522Sdarrenr/* ------------------------------------------------------------------------ */
1666255332Scy/* Function:    ipf_nat_siocdelnat                                          */
1667145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
1668255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
1669255332Scy/*              softn(I)   - pointer to NAT context structure               */
1670255332Scy/*              n(I)       - pointer to new NAT rule                        */
1671255332Scy/*              getlock(I) - flag indicating if lock on  is held            */
1672255332Scy/* Mutex Locks: ipf_nat_io                                                  */
1673145522Sdarrenr/*                                                                          */
1674145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1675145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1676145522Sdarrenr/* NAT rule table(s).                                                       */
1677145522Sdarrenr/* ------------------------------------------------------------------------ */
1678255332Scystatic void
1679255332Scyipf_nat_siocdelnat(softc, softn, n, getlock)
1680255332Scy	ipf_main_softc_t *softc;
1681255332Scy	ipf_nat_softc_t *softn;
1682255332Scy	ipnat_t *n;
1683255332Scy	int getlock;
1684145522Sdarrenr{
1685255332Scy#ifdef IPF_NAT6
1686255332Scy	int i;
1687255332Scy#endif
1688255332Scy
1689145522Sdarrenr	if (getlock) {
1690255332Scy		WRITE_ENTER(&softc->ipf_nat);
1691145522Sdarrenr	}
1692145522Sdarrenr
1693255332Scy	ipf_nat_delrule(softc, softn, n, 1);
1694145522Sdarrenr
1695145522Sdarrenr	if (getlock) {
1696255332Scy		RWLOCK_EXIT(&softc->ipf_nat);			/* READ/WRITE */
1697145522Sdarrenr	}
1698145522Sdarrenr}
1699145522Sdarrenr
1700145522Sdarrenr
1701145522Sdarrenr/* ------------------------------------------------------------------------ */
1702255332Scy/* Function:    ipf_nat_getsz                                               */
1703145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1704255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
1705255332Scy/*              data(I)    - pointer to natget structure with kernel        */
1706255332Scy/*                           pointer get the size of.                       */
1707255332Scy/*              getlock(I) - flag indicating whether or not the caller      */
1708255332Scy/*                           holds a lock on ipf_nat                        */
1709145522Sdarrenr/*                                                                          */
1710145522Sdarrenr/* Handle SIOCSTGSZ.                                                        */
1711145522Sdarrenr/* Return the size of the nat list entry to be copied back to user space.   */
1712145522Sdarrenr/* The size of the entry is stored in the ng_sz field and the enture natget */
1713145522Sdarrenr/* structure is copied back to the user.                                    */
1714145522Sdarrenr/* ------------------------------------------------------------------------ */
1715255332Scystatic int
1716255332Scyipf_nat_getsz(softc, data, getlock)
1717255332Scy	ipf_main_softc_t *softc;
1718255332Scy	caddr_t data;
1719255332Scy	int getlock;
172060852Sdarrenr{
1721255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
172260852Sdarrenr	ap_session_t *aps;
172360852Sdarrenr	nat_t *nat, *n;
172460852Sdarrenr	natget_t ng;
1725255332Scy	int error;
172660852Sdarrenr
1727255332Scy	error = BCOPYIN(data, &ng, sizeof(ng));
1728255332Scy	if (error != 0) {
1729255332Scy		IPFERROR(60024);
1730170268Sdarrenr		return EFAULT;
1731255332Scy	}
173260852Sdarrenr
1733173181Sdarrenr	if (getlock) {
1734255332Scy		READ_ENTER(&softc->ipf_nat);
1735173181Sdarrenr	}
1736173181Sdarrenr
173760852Sdarrenr	nat = ng.ng_ptr;
173860852Sdarrenr	if (!nat) {
1739255332Scy		nat = softn->ipf_nat_instances;
174060852Sdarrenr		ng.ng_sz = 0;
1741145522Sdarrenr		/*
1742145522Sdarrenr		 * Empty list so the size returned is 0.  Simple.
1743145522Sdarrenr		 */
174460852Sdarrenr		if (nat == NULL) {
1745173181Sdarrenr			if (getlock) {
1746255332Scy				RWLOCK_EXIT(&softc->ipf_nat);
1747173181Sdarrenr			}
1748255332Scy			error = BCOPYOUT(&ng, data, sizeof(ng));
1749255332Scy			if (error != 0) {
1750255332Scy				IPFERROR(60025);
1751170268Sdarrenr				return EFAULT;
1752255332Scy			}
1753145522Sdarrenr			return 0;
175460852Sdarrenr		}
175560852Sdarrenr	} else {
175660852Sdarrenr		/*
175760852Sdarrenr		 * Make sure the pointer we're copying from exists in the
175860852Sdarrenr		 * current list of entries.  Security precaution to prevent
175960852Sdarrenr		 * copying of random kernel data.
176060852Sdarrenr		 */
1761255332Scy		for (n = softn->ipf_nat_instances; n; n = n->nat_next)
176260852Sdarrenr			if (n == nat)
176360852Sdarrenr				break;
1764173181Sdarrenr		if (n == NULL) {
1765173181Sdarrenr			if (getlock) {
1766255332Scy				RWLOCK_EXIT(&softc->ipf_nat);
1767173181Sdarrenr			}
1768255332Scy			IPFERROR(60026);
176960852Sdarrenr			return ESRCH;
1770173181Sdarrenr		}
177160852Sdarrenr	}
177260852Sdarrenr
1773145522Sdarrenr	/*
1774145522Sdarrenr	 * Incluse any space required for proxy data structures.
1775145522Sdarrenr	 */
177660852Sdarrenr	ng.ng_sz = sizeof(nat_save_t);
177760852Sdarrenr	aps = nat->nat_aps;
1778145522Sdarrenr	if (aps != NULL) {
1779145522Sdarrenr		ng.ng_sz += sizeof(ap_session_t) - 4;
1780145522Sdarrenr		if (aps->aps_data != 0)
1781145522Sdarrenr			ng.ng_sz += aps->aps_psiz;
178260852Sdarrenr	}
1783173181Sdarrenr	if (getlock) {
1784255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
1785173181Sdarrenr	}
178660852Sdarrenr
1787255332Scy	error = BCOPYOUT(&ng, data, sizeof(ng));
1788255332Scy	if (error != 0) {
1789255332Scy		IPFERROR(60027);
1790170268Sdarrenr		return EFAULT;
1791255332Scy	}
1792145522Sdarrenr	return 0;
179360852Sdarrenr}
179460852Sdarrenr
179560852Sdarrenr
1796145522Sdarrenr/* ------------------------------------------------------------------------ */
1797255332Scy/* Function:    ipf_nat_getent                                              */
1798145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1799255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
1800255332Scy/*              data(I)    - pointer to natget structure with kernel pointer*/
1801255332Scy/*                           to NAT structure to copy out.                  */
1802255332Scy/*              getlock(I) - flag indicating whether or not the caller      */
1803255332Scy/*                           holds a lock on ipf_nat                        */
1804145522Sdarrenr/*                                                                          */
1805145522Sdarrenr/* Handle SIOCSTGET.                                                        */
1806145522Sdarrenr/* Copies out NAT entry to user space.  Any additional data held for a      */
1807145522Sdarrenr/* proxy is also copied, as to is the NAT rule which was responsible for it */
1808145522Sdarrenr/* ------------------------------------------------------------------------ */
1809255332Scystatic int
1810255332Scyipf_nat_getent(softc, data, getlock)
1811255332Scy	ipf_main_softc_t *softc;
1812255332Scy	caddr_t data;
1813255332Scy	int getlock;
181460852Sdarrenr{
1815255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1816145522Sdarrenr	int error, outsize;
181760852Sdarrenr	ap_session_t *aps;
1818145522Sdarrenr	nat_save_t *ipn, ipns;
1819145522Sdarrenr	nat_t *n, *nat;
182060852Sdarrenr
1821255332Scy	error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE);
1822145522Sdarrenr	if (error != 0)
1823145522Sdarrenr		return error;
182460852Sdarrenr
1825255332Scy	if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) {
1826255332Scy		IPFERROR(60028);
1827145522Sdarrenr		return EINVAL;
1828255332Scy	}
1829145522Sdarrenr
1830145522Sdarrenr	KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize);
1831255332Scy	if (ipn == NULL) {
1832255332Scy		IPFERROR(60029);
1833145522Sdarrenr		return ENOMEM;
1834255332Scy	}
1835145522Sdarrenr
1836173181Sdarrenr	if (getlock) {
1837255332Scy		READ_ENTER(&softc->ipf_nat);
1838173181Sdarrenr	}
1839173181Sdarrenr
1840145522Sdarrenr	ipn->ipn_dsize = ipns.ipn_dsize;
1841145522Sdarrenr	nat = ipns.ipn_next;
1842145522Sdarrenr	if (nat == NULL) {
1843255332Scy		nat = softn->ipf_nat_instances;
184460852Sdarrenr		if (nat == NULL) {
1845255332Scy			if (softn->ipf_nat_instances == NULL) {
1846255332Scy				IPFERROR(60030);
1847145522Sdarrenr				error = ENOENT;
1848255332Scy			}
1849145522Sdarrenr			goto finished;
185060852Sdarrenr		}
185160852Sdarrenr	} else {
185260852Sdarrenr		/*
185360852Sdarrenr		 * Make sure the pointer we're copying from exists in the
185460852Sdarrenr		 * current list of entries.  Security precaution to prevent
185560852Sdarrenr		 * copying of random kernel data.
185660852Sdarrenr		 */
1857255332Scy		for (n = softn->ipf_nat_instances; n; n = n->nat_next)
185860852Sdarrenr			if (n == nat)
185960852Sdarrenr				break;
1860145522Sdarrenr		if (n == NULL) {
1861255332Scy			IPFERROR(60031);
1862145522Sdarrenr			error = ESRCH;
1863145522Sdarrenr			goto finished;
1864145522Sdarrenr		}
186560852Sdarrenr	}
1866145522Sdarrenr	ipn->ipn_next = nat->nat_next;
186760852Sdarrenr
1868145522Sdarrenr	/*
1869145522Sdarrenr	 * Copy the NAT structure.
1870145522Sdarrenr	 */
1871145522Sdarrenr	bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat));
187260852Sdarrenr
1873145522Sdarrenr	/*
1874145522Sdarrenr	 * If we have a pointer to the NAT rule it belongs to, save that too.
1875145522Sdarrenr	 */
1876145522Sdarrenr	if (nat->nat_ptr != NULL)
1877145522Sdarrenr		bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat,
1878255332Scy		      ipn->ipn_ipnat.in_size);
187960852Sdarrenr
1880145522Sdarrenr	/*
1881145522Sdarrenr	 * If we also know the NAT entry has an associated filter rule,
1882145522Sdarrenr	 * save that too.
1883145522Sdarrenr	 */
1884145522Sdarrenr	if (nat->nat_fr != NULL)
1885145522Sdarrenr		bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr,
1886145522Sdarrenr		      sizeof(ipn->ipn_fr));
188760852Sdarrenr
1888145522Sdarrenr	/*
1889145522Sdarrenr	 * Last but not least, if there is an application proxy session set
1890145522Sdarrenr	 * up for this NAT entry, then copy that out too, including any
1891145522Sdarrenr	 * private data saved along side it by the proxy.
1892145522Sdarrenr	 */
1893145522Sdarrenr	aps = nat->nat_aps;
1894145522Sdarrenr	outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data);
1895145522Sdarrenr	if (aps != NULL) {
1896145522Sdarrenr		char *s;
189760852Sdarrenr
1898145522Sdarrenr		if (outsize < sizeof(*aps)) {
1899255332Scy			IPFERROR(60032);
1900145522Sdarrenr			error = ENOBUFS;
1901145522Sdarrenr			goto finished;
190260852Sdarrenr		}
1903145522Sdarrenr
1904145522Sdarrenr		s = ipn->ipn_data;
1905145522Sdarrenr		bcopy((char *)aps, s, sizeof(*aps));
1906145522Sdarrenr		s += sizeof(*aps);
1907145522Sdarrenr		outsize -= sizeof(*aps);
1908145522Sdarrenr		if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz))
1909145522Sdarrenr			bcopy(aps->aps_data, s, aps->aps_psiz);
1910255332Scy		else {
1911255332Scy			IPFERROR(60033);
1912145522Sdarrenr			error = ENOBUFS;
1913255332Scy		}
191460852Sdarrenr	}
1915145522Sdarrenr	if (error == 0) {
1916173181Sdarrenr		if (getlock) {
1917255332Scy			READ_ENTER(&softc->ipf_nat);
1918173181Sdarrenr			getlock = 0;
1919173181Sdarrenr		}
1920255332Scy		error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE,
1921255332Scy				     ipns.ipn_dsize);
1922145522Sdarrenr	}
1923145522Sdarrenr
1924145522Sdarrenrfinished:
1925173181Sdarrenr	if (getlock) {
1926255332Scy		READ_ENTER(&softc->ipf_nat);
1927173181Sdarrenr	}
1928145522Sdarrenr	if (ipn != NULL) {
1929145522Sdarrenr		KFREES(ipn, ipns.ipn_dsize);
1930145522Sdarrenr	}
193164580Sdarrenr	return error;
193260852Sdarrenr}
193360852Sdarrenr
193460852Sdarrenr
1935145522Sdarrenr/* ------------------------------------------------------------------------ */
1936255332Scy/* Function:    ipf_nat_putent                                              */
1937145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1938255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
1939255332Scy/*              data(I)    - pointer to natget structure with NAT           */
1940255332Scy/*                           structure information to load into the kernel  */
1941145522Sdarrenr/*              getlock(I) - flag indicating whether or not a write lock    */
1942255332Scy/*                           on is already held.                            */
1943145522Sdarrenr/*                                                                          */
1944145522Sdarrenr/* Handle SIOCSTPUT.                                                        */
1945145522Sdarrenr/* Loads a NAT table entry from user space, including a NAT rule, proxy and */
1946145522Sdarrenr/* firewall rule data structures, if pointers to them indicate so.          */
1947145522Sdarrenr/* ------------------------------------------------------------------------ */
1948255332Scystatic int
1949255332Scyipf_nat_putent(softc, data, getlock)
1950255332Scy	ipf_main_softc_t *softc;
1951255332Scy	caddr_t data;
1952255332Scy	int getlock;
195360852Sdarrenr{
1954255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1955145522Sdarrenr	nat_save_t ipn, *ipnn;
195660852Sdarrenr	ap_session_t *aps;
1957145522Sdarrenr	nat_t *n, *nat;
195860852Sdarrenr	frentry_t *fr;
1959145522Sdarrenr	fr_info_t fin;
196060852Sdarrenr	ipnat_t *in;
196160852Sdarrenr	int error;
196260852Sdarrenr
1963255332Scy	error = ipf_inobj(softc, data, NULL, &ipn, IPFOBJ_NATSAVE);
1964145522Sdarrenr	if (error != 0)
1965145522Sdarrenr		return error;
1966145522Sdarrenr
1967145522Sdarrenr	/*
1968145522Sdarrenr	 * Initialise early because of code at junkput label.
1969145522Sdarrenr	 */
1970255332Scy	n = NULL;
1971145522Sdarrenr	in = NULL;
1972145522Sdarrenr	aps = NULL;
197364580Sdarrenr	nat = NULL;
1974145522Sdarrenr	ipnn = NULL;
1975170268Sdarrenr	fr = NULL;
1976145522Sdarrenr
1977145522Sdarrenr	/*
1978145522Sdarrenr	 * New entry, copy in the rest of the NAT entry if it's size is more
1979145522Sdarrenr	 * than just the nat_t structure.
1980145522Sdarrenr	 */
1981145522Sdarrenr	if (ipn.ipn_dsize > sizeof(ipn)) {
1982145522Sdarrenr		if (ipn.ipn_dsize > 81920) {
1983255332Scy			IPFERROR(60034);
1984145522Sdarrenr			error = ENOMEM;
1985145522Sdarrenr			goto junkput;
1986145522Sdarrenr		}
1987145522Sdarrenr
1988145522Sdarrenr		KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize);
1989255332Scy		if (ipnn == NULL) {
1990255332Scy			IPFERROR(60035);
199160852Sdarrenr			return ENOMEM;
1992255332Scy		}
1993145522Sdarrenr
1994255332Scy		bzero(ipnn, ipn.ipn_dsize);
1995255332Scy		error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE,
1996255332Scy				    ipn.ipn_dsize);
1997145522Sdarrenr		if (error != 0) {
199864580Sdarrenr			goto junkput;
199964580Sdarrenr		}
200060852Sdarrenr	} else
2001145522Sdarrenr		ipnn = &ipn;
200260852Sdarrenr
200360852Sdarrenr	KMALLOC(nat, nat_t *);
200464580Sdarrenr	if (nat == NULL) {
2005255332Scy		IPFERROR(60037);
2006145522Sdarrenr		error = ENOMEM;
200764580Sdarrenr		goto junkput;
200864580Sdarrenr	}
200960852Sdarrenr
2010145522Sdarrenr	bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat));
2011255332Scy
2012255332Scy	switch (nat->nat_v[0])
2013255332Scy	{
2014255332Scy	case 4:
2015255332Scy#ifdef USE_INET6
2016255332Scy	case 6 :
2017255332Scy#endif
2018255332Scy		break;
2019255332Scy	default :
2020255332Scy		IPFERROR(60061);
2021255332Scy		error = EPROTONOSUPPORT;
2022255332Scy		goto junkput;
2023255332Scy		/*NOTREACHED*/
2024255332Scy	}
2025255332Scy
202660852Sdarrenr	/*
2027255332Scy	 * Initialize all these so that ipf_nat_delete() doesn't cause a crash.
202860852Sdarrenr	 */
2029145522Sdarrenr	bzero((char *)nat, offsetof(struct nat, nat_tqe));
2030145522Sdarrenr	nat->nat_tqe.tqe_pnext = NULL;
2031145522Sdarrenr	nat->nat_tqe.tqe_next = NULL;
2032145522Sdarrenr	nat->nat_tqe.tqe_ifq = NULL;
2033145522Sdarrenr	nat->nat_tqe.tqe_parent = nat;
203460852Sdarrenr
203560852Sdarrenr	/*
203660852Sdarrenr	 * Restore the rule associated with this nat session
203760852Sdarrenr	 */
2038145522Sdarrenr	in = ipnn->ipn_nat.nat_ptr;
2039145522Sdarrenr	if (in != NULL) {
2040255332Scy		KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size);
2041145522Sdarrenr		nat->nat_ptr = in;
204260852Sdarrenr		if (in == NULL) {
2043255332Scy			IPFERROR(60038);
204460852Sdarrenr			error = ENOMEM;
204560852Sdarrenr			goto junkput;
204660852Sdarrenr		}
2047255332Scy		bcopy((char *)&ipnn->ipn_ipnat, (char *)in,
2048255332Scy		      ipnn->ipn_ipnat.in_size);
204960852Sdarrenr		in->in_use = 1;
205060852Sdarrenr		in->in_flags |= IPN_DELETE;
2051145522Sdarrenr
2052255332Scy		ATOMIC_INC32(softn->ipf_nat_stats.ns_rules);
2053145522Sdarrenr
2054255332Scy		if (ipf_nat_resolverule(softc, in) != 0) {
2055255332Scy			IPFERROR(60039);
2056161356Sguido			error = ESRCH;
2057161356Sguido			goto junkput;
2058161356Sguido		}
2059145522Sdarrenr	}
2060145522Sdarrenr
2061145522Sdarrenr	/*
2062145522Sdarrenr	 * Check that the NAT entry doesn't already exist in the kernel.
2063161356Sguido	 *
2064161356Sguido	 * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry.  To do
2065161356Sguido	 * this, we check to see if the inbound combination of addresses and
2066161356Sguido	 * ports is already known.  Similar logic is applied for NAT_INBOUND.
2067255332Scy	 *
2068145522Sdarrenr	 */
2069145522Sdarrenr	bzero((char *)&fin, sizeof(fin));
2070255332Scy	fin.fin_v = nat->nat_v[0];
2071255332Scy	fin.fin_p = nat->nat_pr[0];
2072255332Scy	fin.fin_rev = nat->nat_rev;
2073255332Scy	fin.fin_ifp = nat->nat_ifps[0];
2074255332Scy	fin.fin_data[0] = ntohs(nat->nat_ndport);
2075255332Scy	fin.fin_data[1] = ntohs(nat->nat_nsport);
2076255332Scy
2077255332Scy	switch (nat->nat_dir)
2078255332Scy	{
2079255332Scy	case NAT_OUTBOUND :
2080255332Scy	case NAT_DIVERTOUT :
2081153876Sguido		if (getlock) {
2082255332Scy			READ_ENTER(&softc->ipf_nat);
2083153876Sguido		}
2084255332Scy
2085255332Scy		fin.fin_v = nat->nat_v[1];
2086255332Scy		if (nat->nat_v[1] == 4) {
2087255332Scy			n = ipf_nat_inlookup(&fin, nat->nat_flags, fin.fin_p,
2088255332Scy					     nat->nat_ndstip, nat->nat_nsrcip);
2089255332Scy#ifdef USE_INET6
2090255332Scy		} else if (nat->nat_v[1] == 6) {
2091255332Scy			n = ipf_nat6_inlookup(&fin, nat->nat_flags, fin.fin_p,
2092255332Scy					      &nat->nat_ndst6.in6,
2093255332Scy					      &nat->nat_nsrc6.in6);
2094255332Scy#endif
2095255332Scy		}
2096255332Scy
2097153876Sguido		if (getlock) {
2098255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
2099153876Sguido		}
2100153876Sguido		if (n != NULL) {
2101255332Scy			IPFERROR(60040);
2102145522Sdarrenr			error = EEXIST;
2103145522Sdarrenr			goto junkput;
210460852Sdarrenr		}
2105255332Scy		break;
2106255332Scy
2107255332Scy	case NAT_INBOUND :
2108255332Scy	case NAT_DIVERTIN :
2109153876Sguido		if (getlock) {
2110255332Scy			READ_ENTER(&softc->ipf_nat);
2111153876Sguido		}
2112255332Scy
2113255332Scy		if (fin.fin_v == 4) {
2114255332Scy			n = ipf_nat_outlookup(&fin, nat->nat_flags, fin.fin_p,
2115255332Scy					      nat->nat_ndstip,
2116255332Scy					      nat->nat_nsrcip);
2117255332Scy#ifdef USE_INET6
2118255332Scy		} else if (fin.fin_v == 6) {
2119255332Scy			n = ipf_nat6_outlookup(&fin, nat->nat_flags, fin.fin_p,
2120255332Scy					       &nat->nat_ndst6.in6,
2121255332Scy					       &nat->nat_nsrc6.in6);
2122255332Scy#endif
2123255332Scy		}
2124255332Scy
2125153876Sguido		if (getlock) {
2126255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
2127153876Sguido		}
2128153876Sguido		if (n != NULL) {
2129255332Scy			IPFERROR(60041);
2130145522Sdarrenr			error = EEXIST;
2131145522Sdarrenr			goto junkput;
2132145522Sdarrenr		}
2133255332Scy		break;
2134255332Scy
2135255332Scy	default :
2136255332Scy		IPFERROR(60042);
2137145522Sdarrenr		error = EINVAL;
2138145522Sdarrenr		goto junkput;
213960852Sdarrenr	}
214060852Sdarrenr
214160852Sdarrenr	/*
214260852Sdarrenr	 * Restore ap_session_t structure.  Include the private data allocated
214360852Sdarrenr	 * if it was there.
214460852Sdarrenr	 */
2145145522Sdarrenr	aps = nat->nat_aps;
2146145522Sdarrenr	if (aps != NULL) {
214760852Sdarrenr		KMALLOC(aps, ap_session_t *);
2148145522Sdarrenr		nat->nat_aps = aps;
214960852Sdarrenr		if (aps == NULL) {
2150255332Scy			IPFERROR(60043);
215160852Sdarrenr			error = ENOMEM;
215260852Sdarrenr			goto junkput;
215360852Sdarrenr		}
215460852Sdarrenr		bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps));
2155145522Sdarrenr		if (in != NULL)
215660852Sdarrenr			aps->aps_apr = in->in_apr;
2157145522Sdarrenr		else
2158145522Sdarrenr			aps->aps_apr = NULL;
2159145522Sdarrenr		if (aps->aps_psiz != 0) {
2160145522Sdarrenr			if (aps->aps_psiz > 81920) {
2161255332Scy				IPFERROR(60044);
2162145522Sdarrenr				error = ENOMEM;
2163145522Sdarrenr				goto junkput;
2164145522Sdarrenr			}
216560852Sdarrenr			KMALLOCS(aps->aps_data, void *, aps->aps_psiz);
216660852Sdarrenr			if (aps->aps_data == NULL) {
2167255332Scy				IPFERROR(60045);
216860852Sdarrenr				error = ENOMEM;
216960852Sdarrenr				goto junkput;
217060852Sdarrenr			}
217160852Sdarrenr			bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data,
217260852Sdarrenr			      aps->aps_psiz);
217360852Sdarrenr		} else {
217460852Sdarrenr			aps->aps_psiz = 0;
217560852Sdarrenr			aps->aps_data = NULL;
217660852Sdarrenr		}
217760852Sdarrenr	}
217860852Sdarrenr
217960852Sdarrenr	/*
218060852Sdarrenr	 * If there was a filtering rule associated with this entry then
218160852Sdarrenr	 * build up a new one.
218260852Sdarrenr	 */
2183145522Sdarrenr	fr = nat->nat_fr;
218460852Sdarrenr	if (fr != NULL) {
2185145522Sdarrenr		if ((nat->nat_flags & SI_NEWFR) != 0) {
218660852Sdarrenr			KMALLOC(fr, frentry_t *);
218760852Sdarrenr			nat->nat_fr = fr;
218860852Sdarrenr			if (fr == NULL) {
2189255332Scy				IPFERROR(60046);
219060852Sdarrenr				error = ENOMEM;
219160852Sdarrenr				goto junkput;
219260852Sdarrenr			}
2193145522Sdarrenr			ipnn->ipn_nat.nat_fr = fr;
2194145522Sdarrenr			fr->fr_ref = 1;
2195255332Scy			(void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE);
2196145522Sdarrenr			bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr));
2197161356Sguido
2198161356Sguido			fr->fr_ref = 1;
2199161356Sguido			fr->fr_dsize = 0;
2200161356Sguido			fr->fr_data = NULL;
2201161356Sguido			fr->fr_type = FR_T_NONE;
2202161356Sguido
2203145522Sdarrenr			MUTEX_NUKE(&fr->fr_lock);
2204145522Sdarrenr			MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock");
220560852Sdarrenr		} else {
2206153876Sguido			if (getlock) {
2207255332Scy				READ_ENTER(&softc->ipf_nat);
2208153876Sguido			}
2209255332Scy			for (n = softn->ipf_nat_instances; n; n = n->nat_next)
221060852Sdarrenr				if (n->nat_fr == fr)
221160852Sdarrenr					break;
2212145522Sdarrenr
2213145522Sdarrenr			if (n != NULL) {
2214145522Sdarrenr				MUTEX_ENTER(&fr->fr_lock);
2215145522Sdarrenr				fr->fr_ref++;
2216145522Sdarrenr				MUTEX_EXIT(&fr->fr_lock);
2217145522Sdarrenr			}
2218153876Sguido			if (getlock) {
2219255332Scy				RWLOCK_EXIT(&softc->ipf_nat);
2220153876Sguido			}
2221145522Sdarrenr
2222255332Scy			if (n == NULL) {
2223255332Scy				IPFERROR(60047);
222460852Sdarrenr				error = ESRCH;
222560852Sdarrenr				goto junkput;
222660852Sdarrenr			}
222760852Sdarrenr		}
222860852Sdarrenr	}
222960852Sdarrenr
2230145522Sdarrenr	if (ipnn != &ipn) {
2231145522Sdarrenr		KFREES(ipnn, ipn.ipn_dsize);
2232145522Sdarrenr		ipnn = NULL;
2233145522Sdarrenr	}
2234145522Sdarrenr
2235145522Sdarrenr	if (getlock) {
2236255332Scy		WRITE_ENTER(&softc->ipf_nat);
2237145522Sdarrenr	}
2238255332Scy
2239255332Scy	if (fin.fin_v == 4)
2240255332Scy		error = ipf_nat_finalise(&fin, nat);
2241255332Scy#ifdef USE_INET6
2242255332Scy	else
2243255332Scy		error = ipf_nat6_finalise(&fin, nat);
2244255332Scy#endif
2245255332Scy
2246145522Sdarrenr	if (getlock) {
2247255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
2248145522Sdarrenr	}
2249145522Sdarrenr
2250145522Sdarrenr	if (error == 0)
2251145522Sdarrenr		return 0;
2252145522Sdarrenr
2253255332Scy	IPFERROR(60048);
2254145522Sdarrenr	error = ENOMEM;
2255145522Sdarrenr
225660852Sdarrenrjunkput:
2257255332Scy	if (fr != NULL) {
2258255332Scy		(void) ipf_derefrule(softc, &fr);
2259255332Scy	}
2260145522Sdarrenr
2261145522Sdarrenr	if ((ipnn != NULL) && (ipnn != &ipn)) {
2262145522Sdarrenr		KFREES(ipnn, ipn.ipn_dsize);
2263145522Sdarrenr	}
2264145522Sdarrenr	if (nat != NULL) {
2265145522Sdarrenr		if (aps != NULL) {
2266145522Sdarrenr			if (aps->aps_data != NULL) {
2267145522Sdarrenr				KFREES(aps->aps_data, aps->aps_psiz);
2268145522Sdarrenr			}
2269145522Sdarrenr			KFREE(aps);
2270145522Sdarrenr		}
2271145522Sdarrenr		if (in != NULL) {
2272145522Sdarrenr			if (in->in_apr)
2273255332Scy				ipf_proxy_deref(in->in_apr);
2274255332Scy			KFREES(in, in->in_size);
2275145522Sdarrenr		}
2276145522Sdarrenr		KFREE(nat);
2277145522Sdarrenr	}
227860852Sdarrenr	return error;
227960852Sdarrenr}
228060852Sdarrenr
228160852Sdarrenr
2282145522Sdarrenr/* ------------------------------------------------------------------------ */
2283255332Scy/* Function:    ipf_nat_delete                                              */
2284145522Sdarrenr/* Returns:     Nil                                                         */
2285255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
2286255332Scy/*              nat(I)     - pointer to NAT structure to delete             */
2287145522Sdarrenr/*              logtype(I) - type of LOG record to create before deleting   */
2288145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
2289145522Sdarrenr/*                                                                          */
2290145522Sdarrenr/* Delete a nat entry from the various lists and table.  If NAT logging is  */
2291145522Sdarrenr/* enabled then generate a NAT log record for this event.                   */
2292145522Sdarrenr/* ------------------------------------------------------------------------ */
2293255332Scyvoid
2294255332Scyipf_nat_delete(softc, nat, logtype)
2295255332Scy	ipf_main_softc_t *softc;
2296255332Scy	struct nat *nat;
2297255332Scy	int logtype;
229853642Sguido{
2299255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2300255332Scy	int madeorphan = 0, bkt, removed = 0;
2301255332Scy	nat_stat_side_t *nss;
230253642Sguido	struct ipnat *ipn;
230353642Sguido
2304255332Scy	if (logtype != 0 && softn->ipf_nat_logging != 0)
2305255332Scy		ipf_nat_log(softc, softn, nat, logtype);
230653642Sguido
2307145522Sdarrenr	/*
2308145522Sdarrenr	 * Take it as a general indication that all the pointers are set if
2309145522Sdarrenr	 * nat_pnext is set.
2310145522Sdarrenr	 */
2311145522Sdarrenr	if (nat->nat_pnext != NULL) {
2312172776Sdarrenr		removed = 1;
2313172776Sdarrenr
2314255332Scy		bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz;
2315255332Scy		nss = &softn->ipf_nat_stats.ns_side[0];
2316255332Scy		nss->ns_bucketlen[bkt]--;
2317255332Scy		if (nss->ns_bucketlen[bkt] == 0) {
2318255332Scy			nss->ns_inuse--;
2319255332Scy		}
2320145522Sdarrenr
2321255332Scy		bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz;
2322255332Scy		nss = &softn->ipf_nat_stats.ns_side[1];
2323255332Scy		nss->ns_bucketlen[bkt]--;
2324255332Scy		if (nss->ns_bucketlen[bkt] == 0) {
2325255332Scy			nss->ns_inuse--;
2326255332Scy		}
2327255332Scy
2328145522Sdarrenr		*nat->nat_pnext = nat->nat_next;
2329145522Sdarrenr		if (nat->nat_next != NULL) {
2330145522Sdarrenr			nat->nat_next->nat_pnext = nat->nat_pnext;
2331145522Sdarrenr			nat->nat_next = NULL;
2332145522Sdarrenr		}
2333145522Sdarrenr		nat->nat_pnext = NULL;
2334145522Sdarrenr
2335145522Sdarrenr		*nat->nat_phnext[0] = nat->nat_hnext[0];
2336145522Sdarrenr		if (nat->nat_hnext[0] != NULL) {
2337145522Sdarrenr			nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
2338145522Sdarrenr			nat->nat_hnext[0] = NULL;
2339145522Sdarrenr		}
2340145522Sdarrenr		nat->nat_phnext[0] = NULL;
2341145522Sdarrenr
2342145522Sdarrenr		*nat->nat_phnext[1] = nat->nat_hnext[1];
2343145522Sdarrenr		if (nat->nat_hnext[1] != NULL) {
2344145522Sdarrenr			nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
2345145522Sdarrenr			nat->nat_hnext[1] = NULL;
2346145522Sdarrenr		}
2347145522Sdarrenr		nat->nat_phnext[1] = NULL;
2348145522Sdarrenr
2349255332Scy		if ((nat->nat_flags & SI_WILDP) != 0) {
2350255332Scy			ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds);
2351255332Scy		}
2352255332Scy		madeorphan = 1;
235353642Sguido	}
235460852Sdarrenr
2355145522Sdarrenr	if (nat->nat_me != NULL) {
2356145522Sdarrenr		*nat->nat_me = NULL;
2357145522Sdarrenr		nat->nat_me = NULL;
2358255332Scy		nat->nat_ref--;
2359255332Scy		ASSERT(nat->nat_ref >= 0);
2360145522Sdarrenr	}
236160852Sdarrenr
2362255332Scy	if (nat->nat_tqe.tqe_ifq != NULL) {
2363255332Scy		/*
2364255332Scy		 * No call to ipf_freetimeoutqueue() is made here, they are
2365255332Scy		 * garbage collected in ipf_nat_expire().
2366255332Scy		 */
2367255332Scy		(void) ipf_deletequeueentry(&nat->nat_tqe);
2368255332Scy	}
2369145522Sdarrenr
2370255332Scy	if (nat->nat_sync) {
2371255332Scy		ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync);
2372255332Scy		nat->nat_sync = NULL;
2373255332Scy	}
2374255332Scy
2375170268Sdarrenr	if (logtype == NL_EXPIRE)
2376255332Scy		softn->ipf_nat_stats.ns_expire++;
2377170268Sdarrenr
2378172776Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
2379172776Sdarrenr	/*
2380172776Sdarrenr	 * NL_DESTROY should only be passed in when we've got nat_ref >= 2.
2381172776Sdarrenr	 * This happens when a nat'd packet is blocked and we want to throw
2382172776Sdarrenr	 * away the NAT session.
2383172776Sdarrenr	 */
2384172776Sdarrenr	if (logtype == NL_DESTROY) {
2385172776Sdarrenr		if (nat->nat_ref > 2) {
2386172776Sdarrenr			nat->nat_ref -= 2;
2387172776Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
2388172776Sdarrenr			if (removed)
2389255332Scy				softn->ipf_nat_stats.ns_orphans++;
2390172776Sdarrenr			return;
2391172776Sdarrenr		}
2392172776Sdarrenr	} else if (nat->nat_ref > 1) {
2393172776Sdarrenr		nat->nat_ref--;
2394172776Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
2395255332Scy		if (madeorphan == 1)
2396255332Scy			softn->ipf_nat_stats.ns_orphans++;
2397145522Sdarrenr		return;
2398145522Sdarrenr	}
2399255332Scy	ASSERT(nat->nat_ref >= 0);
2400172776Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
2401170268Sdarrenr
2402255332Scy	nat->nat_ref = 0;
2403255332Scy
2404255332Scy	if (madeorphan == 0)
2405255332Scy		softn->ipf_nat_stats.ns_orphans--;
2406255332Scy
2407161356Sguido	/*
2408255332Scy	 * At this point, nat_ref can be either 0 or -1
2409161356Sguido	 */
2410255332Scy	softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--;
2411145522Sdarrenr
2412255332Scy	if (nat->nat_fr != NULL) {
2413255332Scy		(void) ipf_derefrule(softc, &nat->nat_fr);
2414255332Scy	}
2415145522Sdarrenr
2416255332Scy	if (nat->nat_hm != NULL) {
2417255332Scy		ipf_nat_hostmapdel(softc, &nat->nat_hm);
2418255332Scy	}
2419145522Sdarrenr
242053642Sguido	/*
242153642Sguido	 * If there is an active reference from the nat entry to its parent
242253642Sguido	 * rule, decrement the rule's reference count and free it too if no
242353642Sguido	 * longer being used.
242453642Sguido	 */
2425145522Sdarrenr	ipn = nat->nat_ptr;
2426255332Scy	nat->nat_ptr = NULL;
2427255332Scy
242853642Sguido	if (ipn != NULL) {
2429255332Scy		ipn->in_space++;
2430255332Scy		ipf_nat_rule_deref(softc, &ipn);
243153642Sguido	}
243253642Sguido
2433255332Scy	if (nat->nat_aps != NULL) {
2434255332Scy		ipf_proxy_free(softc, nat->nat_aps);
2435255332Scy		nat->nat_aps = NULL;
2436255332Scy	}
2437255332Scy
2438145522Sdarrenr	MUTEX_DESTROY(&nat->nat_lock);
2439145522Sdarrenr
2440255332Scy	softn->ipf_nat_stats.ns_active--;
2441145522Sdarrenr
244253642Sguido	/*
244353642Sguido	 * If there's a fragment table entry too for this nat entry, then
2444145522Sdarrenr	 * dereference that as well.  This is after nat_lock is released
2445145522Sdarrenr	 * because of Tru64.
244653642Sguido	 */
2447255332Scy	ipf_frag_natforget(softc, (void *)nat);
2448145522Sdarrenr
2449145522Sdarrenr	KFREE(nat);
245053642Sguido}
245153642Sguido
245253642Sguido
2453145522Sdarrenr/* ------------------------------------------------------------------------ */
2454255332Scy/* Function:    ipf_nat_flushtable                                          */
2455145522Sdarrenr/* Returns:     int - number of NAT rules deleted                           */
2456255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
2457255332Scy/*              softn(I) - pointer to NAT context structure                 */
2458255332Scy/* Write Lock:  ipf_nat                                                     */
2459145522Sdarrenr/*                                                                          */
2460145522Sdarrenr/* Deletes all currently active NAT sessions.  In deleting each NAT entry a */
2461255332Scy/* log record should be emitted in ipf_nat_delete() if NAT logging is       */
2462255332Scy/* enabled.                                                                 */
2463145522Sdarrenr/* ------------------------------------------------------------------------ */
246453642Sguido/*
246553642Sguido * nat_flushtable - clear the NAT table of all mapping entries.
246653642Sguido */
2467255332Scystatic int
2468255332Scyipf_nat_flushtable(softc, softn)
2469255332Scy	ipf_main_softc_t *softc;
2470255332Scy	ipf_nat_softc_t *softn;
247153642Sguido{
2472145522Sdarrenr	nat_t *nat;
2473145522Sdarrenr	int j = 0;
247467614Sdarrenr
247553642Sguido	/*
247653642Sguido	 * ALL NAT mappings deleted, so lets just make the deletions
247753642Sguido	 * quicker.
247853642Sguido	 */
2479255332Scy	if (softn->ipf_nat_table[0] != NULL)
2480255332Scy		bzero((char *)softn->ipf_nat_table[0],
2481255332Scy		      sizeof(softn->ipf_nat_table[0]) *
2482255332Scy		      softn->ipf_nat_table_sz);
2483255332Scy	if (softn->ipf_nat_table[1] != NULL)
2484255332Scy		bzero((char *)softn->ipf_nat_table[1],
2485255332Scy		      sizeof(softn->ipf_nat_table[1]) *
2486255332Scy		      softn->ipf_nat_table_sz);
248753642Sguido
2488255332Scy	while ((nat = softn->ipf_nat_instances) != NULL) {
2489255332Scy		ipf_nat_delete(softc, nat, NL_FLUSH);
249053642Sguido		j++;
249153642Sguido	}
2492145522Sdarrenr
249353642Sguido	return j;
249453642Sguido}
249553642Sguido
249653642Sguido
2497145522Sdarrenr/* ------------------------------------------------------------------------ */
2498255332Scy/* Function:    ipf_nat_clearlist                                           */
2499145522Sdarrenr/* Returns:     int - number of NAT/RDR rules deleted                       */
2500255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
2501255332Scy/*              softn(I) - pointer to NAT context structure                 */
2502145522Sdarrenr/*                                                                          */
2503145522Sdarrenr/* Delete all rules in the current list of rules.  There is nothing elegant */
2504145522Sdarrenr/* about this cleanup: simply free all entries on the list of rules and     */
2505145522Sdarrenr/* clear out the tables used for hashed NAT rule lookups.                   */
2506145522Sdarrenr/* ------------------------------------------------------------------------ */
2507255332Scystatic int
2508255332Scyipf_nat_clearlist(softc, softn)
2509255332Scy	ipf_main_softc_t *softc;
2510255332Scy	ipf_nat_softc_t *softn;
251153642Sguido{
2512255332Scy	ipnat_t *n;
251353642Sguido	int i = 0;
251453642Sguido
2515255332Scy	if (softn->ipf_nat_map_rules != NULL) {
2516255332Scy		bzero((char *)softn->ipf_nat_map_rules,
2517255332Scy		      sizeof(*softn->ipf_nat_map_rules) *
2518255332Scy		      softn->ipf_nat_maprules_sz);
2519255332Scy	}
2520255332Scy	if (softn->ipf_nat_rdr_rules != NULL) {
2521255332Scy		bzero((char *)softn->ipf_nat_rdr_rules,
2522255332Scy		      sizeof(*softn->ipf_nat_rdr_rules) *
2523255332Scy		      softn->ipf_nat_rdrrules_sz);
2524255332Scy	}
252553642Sguido
2526255332Scy	while ((n = softn->ipf_nat_list) != NULL) {
2527255332Scy		ipf_nat_delrule(softc, softn, n, 0);
252853642Sguido		i++;
252953642Sguido	}
2530255332Scy#if SOLARIS && !defined(INSTANCES)
2531145522Sdarrenr	pfil_delayed_copy = 1;
2532145522Sdarrenr#endif
253353642Sguido	return i;
253453642Sguido}
253553642Sguido
253653642Sguido
2537145522Sdarrenr/* ------------------------------------------------------------------------ */
2538255332Scy/* Function:    ipf_nat_delrule                                             */
2539255332Scy/* Returns:     Nil                                                         */
2540255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
2541255332Scy/*              softn(I) - pointer to NAT context structure                 */
2542255332Scy/*              np(I)    - pointer to NAT rule to delete                    */
2543255332Scy/*              purge(I) - 1 == allow purge, 0 == prevent purge             */
2544255332Scy/* Locks:       WRITE(ipf_nat)                                              */
2545255332Scy/*                                                                          */
2546255332Scy/* Preventing "purge" from occuring is allowed because when all of the NAT  */
2547255332Scy/* rules are being removed, allowing the "purge" to walk through the list   */
2548255332Scy/* of NAT sessions, possibly multiple times, would be a large performance   */
2549255332Scy/* hit, on the order of O(N^2).                                             */
2550255332Scy/* ------------------------------------------------------------------------ */
2551255332Scystatic void
2552255332Scyipf_nat_delrule(softc, softn, np, purge)
2553255332Scy	ipf_main_softc_t *softc;
2554255332Scy	ipf_nat_softc_t *softn;
2555255332Scy	ipnat_t *np;
2556255332Scy	int purge;
2557255332Scy{
2558255332Scy
2559255332Scy	if (np->in_pnext != NULL) {
2560255332Scy		*np->in_pnext = np->in_next;
2561255332Scy		if (np->in_next != NULL)
2562255332Scy			np->in_next->in_pnext = np->in_pnext;
2563255332Scy		if (softn->ipf_nat_list_tail == &np->in_next)
2564255332Scy			softn->ipf_nat_list_tail = np->in_pnext;
2565255332Scy	}
2566255332Scy
2567255332Scy	if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) {
2568255332Scy		nat_t *next;
2569255332Scy		nat_t *nat;
2570255332Scy
2571255332Scy		for (next = softn->ipf_nat_instances; (nat = next) != NULL;) {
2572255332Scy			next = nat->nat_next;
2573255332Scy			if (nat->nat_ptr == np)
2574255332Scy				ipf_nat_delete(softc, nat, NL_PURGE);
2575255332Scy		}
2576255332Scy	}
2577255332Scy
2578255332Scy	if ((np->in_flags & IPN_DELETE) == 0) {
2579255332Scy		if (np->in_redir & NAT_REDIRECT) {
2580255332Scy			switch (np->in_v[0])
2581255332Scy			{
2582255332Scy			case 4 :
2583255332Scy				ipf_nat_delrdr(softn, np);
2584255332Scy				break;
2585255332Scy#ifdef USE_INET6
2586255332Scy			case 6 :
2587255332Scy				ipf_nat6_delrdr(softn, np);
2588255332Scy				break;
2589255332Scy#endif
2590255332Scy			}
2591255332Scy		}
2592255332Scy		if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) {
2593255332Scy			switch (np->in_v[0])
2594255332Scy			{
2595255332Scy			case 4 :
2596255332Scy				ipf_nat_delmap(softn, np);
2597255332Scy				break;
2598255332Scy#ifdef USE_INET6
2599255332Scy			case 6 :
2600255332Scy				ipf_nat6_delmap(softn, np);
2601255332Scy				break;
2602255332Scy#endif
2603255332Scy			}
2604255332Scy		}
2605255332Scy	}
2606255332Scy
2607255332Scy	np->in_flags |= IPN_DELETE;
2608255332Scy	ipf_nat_rule_deref(softc, &np);
2609255332Scy}
2610255332Scy
2611255332Scy
2612255332Scy/* ------------------------------------------------------------------------ */
2613255332Scy/* Function:    ipf_nat_newmap                                              */
2614145522Sdarrenr/* Returns:     int - -1 == error, 0 == success                             */
2615145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
2616145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
2617145522Sdarrenr/*              ni(I)  - pointer to structure with misc. information needed */
2618145522Sdarrenr/*                       to create new NAT entry.                           */
2619145522Sdarrenr/*                                                                          */
2620145522Sdarrenr/* Given an empty NAT structure, populate it with new information about a   */
2621145522Sdarrenr/* new NAT session, as defined by the matching NAT rule.                    */
2622145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
2623145522Sdarrenr/* to the new IP address for the translation.                               */
2624145522Sdarrenr/* ------------------------------------------------------------------------ */
2625255332Scystatic int
2626255332Scyipf_nat_newmap(fin, nat, ni)
2627255332Scy	fr_info_t *fin;
2628255332Scy	nat_t *nat;
2629255332Scy	natinfo_t *ni;
2630145522Sdarrenr{
2631255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
2632255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2633145522Sdarrenr	u_short st_port, dport, sport, port, sp, dp;
2634145522Sdarrenr	struct in_addr in, inb;
2635145522Sdarrenr	hostmap_t *hm;
2636145522Sdarrenr	u_32_t flags;
2637145522Sdarrenr	u_32_t st_ip;
2638145522Sdarrenr	ipnat_t *np;
2639145522Sdarrenr	nat_t *natl;
2640145522Sdarrenr	int l;
2641145522Sdarrenr
2642145522Sdarrenr	/*
2643145522Sdarrenr	 * If it's an outbound packet which doesn't match any existing
2644145522Sdarrenr	 * record, then create a new port
2645145522Sdarrenr	 */
2646145522Sdarrenr	l = 0;
2647145522Sdarrenr	hm = NULL;
2648145522Sdarrenr	np = ni->nai_np;
2649255332Scy	st_ip = np->in_snip;
2650255332Scy	st_port = np->in_spnext;
2651255332Scy	flags = nat->nat_flags;
2652145522Sdarrenr
2653255332Scy	if (flags & IPN_ICMPQUERY) {
2654255332Scy		sport = fin->fin_data[1];
2655255332Scy		dport = 0;
2656255332Scy	} else {
2657255332Scy		sport = htons(fin->fin_data[0]);
2658255332Scy		dport = htons(fin->fin_data[1]);
2659255332Scy	}
2660255332Scy
2661145522Sdarrenr	/*
2662145522Sdarrenr	 * Do a loop until we either run out of entries to try or we find
2663145522Sdarrenr	 * a NAT mapping that isn't currently being used.  This is done
2664145522Sdarrenr	 * because the change to the source is not (usually) being fixed.
2665145522Sdarrenr	 */
2666145522Sdarrenr	do {
2667145522Sdarrenr		port = 0;
2668255332Scy		in.s_addr = htonl(np->in_snip);
2669145522Sdarrenr		if (l == 0) {
2670145522Sdarrenr			/*
2671145522Sdarrenr			 * Check to see if there is an existing NAT
2672145522Sdarrenr			 * setup for this IP address pair.
2673145522Sdarrenr			 */
2674255332Scy			hm = ipf_nat_hostmap(softn, np, fin->fin_src,
2675255332Scy					     fin->fin_dst, in, 0);
2676145522Sdarrenr			if (hm != NULL)
2677255332Scy				in.s_addr = hm->hm_nsrcip.s_addr;
2678145522Sdarrenr		} else if ((l == 1) && (hm != NULL)) {
2679255332Scy			ipf_nat_hostmapdel(softc, &hm);
2680145522Sdarrenr		}
2681145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
2682145522Sdarrenr
2683145522Sdarrenr		nat->nat_hm = hm;
2684145522Sdarrenr
2685255332Scy		if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) {
2686255332Scy			if (l > 0) {
2687255332Scy				NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1);
2688145522Sdarrenr				return -1;
2689255332Scy			}
2690145522Sdarrenr		}
2691145522Sdarrenr
2692145522Sdarrenr		if (np->in_redir == NAT_BIMAP &&
2693255332Scy		    np->in_osrcmsk == np->in_nsrcmsk) {
2694145522Sdarrenr			/*
2695145522Sdarrenr			 * map the address block in a 1:1 fashion
2696145522Sdarrenr			 */
2697255332Scy			in.s_addr = np->in_nsrcaddr;
2698255332Scy			in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk;
2699145522Sdarrenr			in.s_addr = ntohl(in.s_addr);
2700145522Sdarrenr
2701145522Sdarrenr		} else if (np->in_redir & NAT_MAPBLK) {
2702145522Sdarrenr			if ((l >= np->in_ppip) || ((l > 0) &&
2703255332Scy			     !(flags & IPN_TCPUDP))) {
2704255332Scy				NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2);
2705145522Sdarrenr				return -1;
2706255332Scy			}
2707145522Sdarrenr			/*
2708145522Sdarrenr			 * map-block - Calculate destination address.
2709145522Sdarrenr			 */
2710145522Sdarrenr			in.s_addr = ntohl(fin->fin_saddr);
2711255332Scy			in.s_addr &= ntohl(~np->in_osrcmsk);
2712145522Sdarrenr			inb.s_addr = in.s_addr;
2713145522Sdarrenr			in.s_addr /= np->in_ippip;
2714255332Scy			in.s_addr &= ntohl(~np->in_nsrcmsk);
2715255332Scy			in.s_addr += ntohl(np->in_nsrcaddr);
2716145522Sdarrenr			/*
2717145522Sdarrenr			 * Calculate destination port.
2718145522Sdarrenr			 */
2719145522Sdarrenr			if ((flags & IPN_TCPUDP) &&
2720145522Sdarrenr			    (np->in_ppip != 0)) {
2721145522Sdarrenr				port = ntohs(sport) + l;
2722145522Sdarrenr				port %= np->in_ppip;
2723145522Sdarrenr				port += np->in_ppip *
2724145522Sdarrenr					(inb.s_addr % np->in_ippip);
2725145522Sdarrenr				port += MAPBLK_MINPORT;
2726145522Sdarrenr				port = htons(port);
2727145522Sdarrenr			}
2728145522Sdarrenr
2729255332Scy		} else if ((np->in_nsrcaddr == 0) &&
2730255332Scy			   (np->in_nsrcmsk == 0xffffffff)) {
2731255332Scy			i6addr_t in6;
2732255332Scy
2733145522Sdarrenr			/*
2734145522Sdarrenr			 * 0/32 - use the interface's IP address.
2735145522Sdarrenr			 */
2736145522Sdarrenr			if ((l > 0) ||
2737255332Scy			    ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp,
2738255332Scy				       &in6, NULL) == -1) {
2739255332Scy				NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1);
2740145522Sdarrenr				return -1;
2741255332Scy			}
2742255332Scy			in.s_addr = ntohl(in6.in4.s_addr);
2743145522Sdarrenr
2744255332Scy		} else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) {
2745145522Sdarrenr			/*
2746145522Sdarrenr			 * 0/0 - use the original source address/port.
2747145522Sdarrenr			 */
2748255332Scy			if (l > 0) {
2749255332Scy				NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3);
2750145522Sdarrenr				return -1;
2751255332Scy			}
2752145522Sdarrenr			in.s_addr = ntohl(fin->fin_saddr);
2753145522Sdarrenr
2754255332Scy		} else if ((np->in_nsrcmsk != 0xffffffff) &&
2755255332Scy			   (np->in_spnext == 0) && ((l > 0) || (hm == NULL)))
2756255332Scy			np->in_snip++;
2757145522Sdarrenr
2758145522Sdarrenr		natl = NULL;
2759145522Sdarrenr
2760145522Sdarrenr		if ((flags & IPN_TCPUDP) &&
2761145522Sdarrenr		    ((np->in_redir & NAT_MAPBLK) == 0) &&
2762145522Sdarrenr		    (np->in_flags & IPN_AUTOPORTMAP)) {
2763145522Sdarrenr			/*
2764145522Sdarrenr			 * "ports auto" (without map-block)
2765145522Sdarrenr			 */
2766145522Sdarrenr			if ((l > 0) && (l % np->in_ppip == 0)) {
2767255332Scy				if ((l > np->in_ppip) &&
2768255332Scy				    np->in_nsrcmsk != 0xffffffff)
2769255332Scy					np->in_snip++;
2770145522Sdarrenr			}
2771145522Sdarrenr			if (np->in_ppip != 0) {
2772145522Sdarrenr				port = ntohs(sport);
2773145522Sdarrenr				port += (l % np->in_ppip);
2774145522Sdarrenr				port %= np->in_ppip;
2775145522Sdarrenr				port += np->in_ppip *
2776145522Sdarrenr					(ntohl(fin->fin_saddr) %
2777145522Sdarrenr					 np->in_ippip);
2778145522Sdarrenr				port += MAPBLK_MINPORT;
2779145522Sdarrenr				port = htons(port);
2780145522Sdarrenr			}
2781145522Sdarrenr
2782145522Sdarrenr		} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
2783255332Scy			   (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) {
2784145522Sdarrenr			/*
2785145522Sdarrenr			 * Standard port translation.  Select next port.
2786145522Sdarrenr			 */
2787180778Sdarrenr			if (np->in_flags & IPN_SEQUENTIAL) {
2788255332Scy				port = np->in_spnext;
2789180778Sdarrenr			} else {
2790255332Scy				port = ipf_random() % (np->in_spmax -
2791255332Scy						       np->in_spmin + 1);
2792255332Scy				port += np->in_spmin;
2793180778Sdarrenr			}
2794180832Sdarrenr			port = htons(port);
2795255332Scy			np->in_spnext++;
2796145522Sdarrenr
2797255332Scy			if (np->in_spnext > np->in_spmax) {
2798255332Scy				np->in_spnext = np->in_spmin;
2799255332Scy				if (np->in_nsrcmsk != 0xffffffff)
2800255332Scy					np->in_snip++;
2801145522Sdarrenr			}
2802145522Sdarrenr		}
2803145522Sdarrenr
2804255332Scy		if (np->in_flags & IPN_SIPRANGE) {
2805255332Scy			if (np->in_snip > ntohl(np->in_nsrcmsk))
2806255332Scy				np->in_snip = ntohl(np->in_nsrcaddr);
2807145522Sdarrenr		} else {
2808255332Scy			if ((np->in_nsrcmsk != 0xffffffff) &&
2809255332Scy			    ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) >
2810255332Scy			    ntohl(np->in_nsrcaddr))
2811255332Scy				np->in_snip = ntohl(np->in_nsrcaddr) + 1;
2812145522Sdarrenr		}
2813145522Sdarrenr
2814145522Sdarrenr		if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
2815145522Sdarrenr			port = sport;
2816145522Sdarrenr
2817145522Sdarrenr		/*
2818145522Sdarrenr		 * Here we do a lookup of the connection as seen from
2819145522Sdarrenr		 * the outside.  If an IP# pair already exists, try
2820145522Sdarrenr		 * again.  So if you have A->B becomes C->B, you can
2821145522Sdarrenr		 * also have D->E become C->E but not D->B causing
2822145522Sdarrenr		 * another C->B.  Also take protocol and ports into
2823145522Sdarrenr		 * account when determining whether a pre-existing
2824145522Sdarrenr		 * NAT setup will cause an external conflict where
2825145522Sdarrenr		 * this is appropriate.
2826145522Sdarrenr		 */
2827145522Sdarrenr		inb.s_addr = htonl(in.s_addr);
2828145522Sdarrenr		sp = fin->fin_data[0];
2829145522Sdarrenr		dp = fin->fin_data[1];
2830145522Sdarrenr		fin->fin_data[0] = fin->fin_data[1];
2831255332Scy		fin->fin_data[1] = ntohs(port);
2832255332Scy		natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
2833255332Scy					(u_int)fin->fin_p, fin->fin_dst, inb);
2834145522Sdarrenr		fin->fin_data[0] = sp;
2835145522Sdarrenr		fin->fin_data[1] = dp;
2836145522Sdarrenr
2837145522Sdarrenr		/*
2838145522Sdarrenr		 * Has the search wrapped around and come back to the
2839145522Sdarrenr		 * start ?
2840145522Sdarrenr		 */
2841145522Sdarrenr		if ((natl != NULL) &&
2842255332Scy		    (np->in_spnext != 0) && (st_port == np->in_spnext) &&
2843255332Scy		    (np->in_snip != 0) && (st_ip == np->in_snip)) {
2844255332Scy			NBUMPSIDED(1, ns_wrap);
2845145522Sdarrenr			return -1;
2846255332Scy		}
2847145522Sdarrenr		l++;
2848145522Sdarrenr	} while (natl != NULL);
2849145522Sdarrenr
2850145522Sdarrenr	/* Setup the NAT table */
2851255332Scy	nat->nat_osrcip = fin->fin_src;
2852255332Scy	nat->nat_nsrcaddr = htonl(in.s_addr);
2853255332Scy	nat->nat_odstip = fin->fin_dst;
2854255332Scy	nat->nat_ndstip = fin->fin_dst;
2855145522Sdarrenr	if (nat->nat_hm == NULL)
2856255332Scy		nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src,
2857255332Scy					      fin->fin_dst, nat->nat_nsrcip,
2858255332Scy					      0);
2859145522Sdarrenr
2860145522Sdarrenr	if (flags & IPN_TCPUDP) {
2861255332Scy		nat->nat_osport = sport;
2862255332Scy		nat->nat_nsport = port;	/* sport */
2863255332Scy		nat->nat_odport = dport;
2864255332Scy		nat->nat_ndport = dport;
2865145522Sdarrenr		((tcphdr_t *)fin->fin_dp)->th_sport = port;
2866145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
2867255332Scy		nat->nat_oicmpid = fin->fin_data[1];
2868145522Sdarrenr		((icmphdr_t *)fin->fin_dp)->icmp_id = port;
2869255332Scy		nat->nat_nicmpid = port;
2870145522Sdarrenr	}
2871145522Sdarrenr	return 0;
2872145522Sdarrenr}
2873145522Sdarrenr
2874145522Sdarrenr
2875145522Sdarrenr/* ------------------------------------------------------------------------ */
2876255332Scy/* Function:    ipf_nat_newrdr                                              */
2877145522Sdarrenr/* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
2878145522Sdarrenr/*                    allow rule to be moved if IPN_ROUNDR is set.          */
2879145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
2880145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
2881145522Sdarrenr/*              ni(I)  - pointer to structure with misc. information needed */
2882145522Sdarrenr/*                       to create new NAT entry.                           */
2883145522Sdarrenr/*                                                                          */
2884145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
2885145522Sdarrenr/* to the new IP address for the translation.                               */
2886145522Sdarrenr/* ------------------------------------------------------------------------ */
2887255332Scystatic int
2888255332Scyipf_nat_newrdr(fin, nat, ni)
2889255332Scy	fr_info_t *fin;
2890255332Scy	nat_t *nat;
2891255332Scy	natinfo_t *ni;
2892145522Sdarrenr{
2893255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
2894255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2895145522Sdarrenr	u_short nport, dport, sport;
2896170268Sdarrenr	struct in_addr in, inb;
2897170268Sdarrenr	u_short sp, dp;
2898145522Sdarrenr	hostmap_t *hm;
2899145522Sdarrenr	u_32_t flags;
2900145522Sdarrenr	ipnat_t *np;
2901170268Sdarrenr	nat_t *natl;
2902145522Sdarrenr	int move;
2903145522Sdarrenr
2904145522Sdarrenr	move = 1;
2905145522Sdarrenr	hm = NULL;
2906145522Sdarrenr	in.s_addr = 0;
2907145522Sdarrenr	np = ni->nai_np;
2908255332Scy	flags = nat->nat_flags;
2909145522Sdarrenr
2910255332Scy	if (flags & IPN_ICMPQUERY) {
2911255332Scy		dport = fin->fin_data[1];
2912255332Scy		sport = 0;
2913255332Scy	} else {
2914255332Scy		sport = htons(fin->fin_data[0]);
2915255332Scy		dport = htons(fin->fin_data[1]);
2916255332Scy	}
2917255332Scy
2918255332Scy	/* TRACE sport, dport */
2919255332Scy
2920255332Scy
2921145522Sdarrenr	/*
2922145522Sdarrenr	 * If the matching rule has IPN_STICKY set, then we want to have the
2923145522Sdarrenr	 * same rule kick in as before.  Why would this happen?  If you have
2924145522Sdarrenr	 * a collection of rdr rules with "round-robin sticky", the current
2925145522Sdarrenr	 * packet might match a different one to the previous connection but
2926145522Sdarrenr	 * we want the same destination to be used.
2927145522Sdarrenr	 */
2928153876Sguido	if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) &&
2929153876Sguido	    ((np->in_flags & IPN_STICKY) != 0)) {
2930255332Scy		hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst,
2931255332Scy				     in, (u_32_t)dport);
2932145522Sdarrenr		if (hm != NULL) {
2933255332Scy			in.s_addr = ntohl(hm->hm_ndstip.s_addr);
2934145522Sdarrenr			np = hm->hm_ipnat;
2935145522Sdarrenr			ni->nai_np = np;
2936145522Sdarrenr			move = 0;
2937255332Scy			ipf_nat_hostmapdel(softc, &hm);
2938145522Sdarrenr		}
2939145522Sdarrenr	}
2940145522Sdarrenr
2941145522Sdarrenr	/*
2942145522Sdarrenr	 * Otherwise, it's an inbound packet. Most likely, we don't
2943145522Sdarrenr	 * want to rewrite source ports and source addresses. Instead,
2944145522Sdarrenr	 * we want to rewrite to a fixed internal address and fixed
2945145522Sdarrenr	 * internal port.
2946145522Sdarrenr	 */
2947145522Sdarrenr	if (np->in_flags & IPN_SPLIT) {
2948255332Scy		in.s_addr = np->in_dnip;
2949272998Scy		inb.s_addr = htonl(in.s_addr);
2950145522Sdarrenr
2951145522Sdarrenr		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
2952255332Scy			hm = ipf_nat_hostmap(softn, NULL, fin->fin_src,
2953272998Scy					     fin->fin_dst, inb, (u_32_t)dport);
2954145522Sdarrenr			if (hm != NULL) {
2955255332Scy				in.s_addr = hm->hm_ndstip.s_addr;
2956145522Sdarrenr				move = 0;
2957145522Sdarrenr			}
2958145522Sdarrenr		}
2959145522Sdarrenr
2960145522Sdarrenr		if (hm == NULL || hm->hm_ref == 1) {
2961255332Scy			if (np->in_ndstaddr == htonl(in.s_addr)) {
2962255332Scy				np->in_dnip = ntohl(np->in_ndstmsk);
2963145522Sdarrenr				move = 0;
2964145522Sdarrenr			} else {
2965255332Scy				np->in_dnip = ntohl(np->in_ndstaddr);
2966145522Sdarrenr			}
2967145522Sdarrenr		}
2968255332Scy		if (hm != NULL)
2969255332Scy			ipf_nat_hostmapdel(softc, &hm);
2970145522Sdarrenr
2971255332Scy	} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) {
2972255332Scy		i6addr_t in6;
2973255332Scy
2974145522Sdarrenr		/*
2975145522Sdarrenr		 * 0/32 - use the interface's IP address.
2976145522Sdarrenr		 */
2977255332Scy		if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp,
2978255332Scy			       &in6, NULL) == -1) {
2979255332Scy			NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2);
2980145522Sdarrenr			return -1;
2981255332Scy		}
2982255332Scy		in.s_addr = ntohl(in6.in4.s_addr);
2983145522Sdarrenr
2984255332Scy	} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) {
2985145522Sdarrenr		/*
2986145522Sdarrenr		 * 0/0 - use the original destination address/port.
2987145522Sdarrenr		 */
2988145522Sdarrenr		in.s_addr = ntohl(fin->fin_daddr);
2989145522Sdarrenr
2990145522Sdarrenr	} else if (np->in_redir == NAT_BIMAP &&
2991255332Scy		   np->in_ndstmsk == np->in_odstmsk) {
2992145522Sdarrenr		/*
2993145522Sdarrenr		 * map the address block in a 1:1 fashion
2994145522Sdarrenr		 */
2995255332Scy		in.s_addr = np->in_ndstaddr;
2996255332Scy		in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk;
2997145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
2998145522Sdarrenr	} else {
2999255332Scy		in.s_addr = ntohl(np->in_ndstaddr);
3000145522Sdarrenr	}
3001145522Sdarrenr
3002255332Scy	if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
3003145522Sdarrenr		nport = dport;
3004145522Sdarrenr	else {
3005145522Sdarrenr		/*
3006145522Sdarrenr		 * Whilst not optimized for the case where
3007145522Sdarrenr		 * pmin == pmax, the gain is not significant.
3008145522Sdarrenr		 */
3009145522Sdarrenr		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
3010255332Scy		    (np->in_odport != np->in_dtop)) {
3011255332Scy			nport = ntohs(dport) - np->in_odport + np->in_dpmax;
3012145522Sdarrenr			nport = htons(nport);
3013255332Scy		} else {
3014255332Scy			nport = htons(np->in_dpnext);
3015255332Scy			np->in_dpnext++;
3016255332Scy			if (np->in_dpnext > np->in_dpmax)
3017255332Scy				np->in_dpnext = np->in_dpmin;
3018255332Scy		}
3019145522Sdarrenr	}
3020145522Sdarrenr
3021145522Sdarrenr	/*
3022145522Sdarrenr	 * When the redirect-to address is set to 0.0.0.0, just
3023145522Sdarrenr	 * assume a blank `forwarding' of the packet.  We don't
3024145522Sdarrenr	 * setup any translation for this either.
3025145522Sdarrenr	 */
3026145522Sdarrenr	if (in.s_addr == 0) {
3027255332Scy		if (nport == dport) {
3028255332Scy			NBUMPSIDED(0, ns_xlate_null);
3029145522Sdarrenr			return -1;
3030255332Scy		}
3031145522Sdarrenr		in.s_addr = ntohl(fin->fin_daddr);
3032145522Sdarrenr	}
3033145522Sdarrenr
3034170268Sdarrenr	/*
3035170268Sdarrenr	 * Check to see if this redirect mapping already exists and if
3036170268Sdarrenr	 * it does, return "failure" (allowing it to be created will just
3037170268Sdarrenr	 * cause one or both of these "connections" to stop working.)
3038170268Sdarrenr	 */
3039170268Sdarrenr	inb.s_addr = htonl(in.s_addr);
3040170268Sdarrenr	sp = fin->fin_data[0];
3041170268Sdarrenr	dp = fin->fin_data[1];
3042170268Sdarrenr	fin->fin_data[1] = fin->fin_data[0];
3043170268Sdarrenr	fin->fin_data[0] = ntohs(nport);
3044255332Scy	natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
3045170268Sdarrenr			     (u_int)fin->fin_p, inb, fin->fin_src);
3046170268Sdarrenr	fin->fin_data[0] = sp;
3047170268Sdarrenr	fin->fin_data[1] = dp;
3048255332Scy	if (natl != NULL) {
3049255332Scy		DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl);
3050255332Scy		NBUMPSIDE(0, ns_xlate_exists);
3051170268Sdarrenr		return -1;
3052255332Scy	}
3053170268Sdarrenr
3054272998Scy	inb.s_addr = htonl(in.s_addr);
3055255332Scy	nat->nat_ndstaddr = htonl(in.s_addr);
3056255332Scy	nat->nat_odstip = fin->fin_dst;
3057255332Scy	nat->nat_nsrcip = fin->fin_src;
3058255332Scy	nat->nat_osrcip = fin->fin_src;
3059153876Sguido	if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
3060255332Scy		nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src,
3061272998Scy					      fin->fin_dst, inb, (u_32_t)dport);
3062145522Sdarrenr
3063145522Sdarrenr	if (flags & IPN_TCPUDP) {
3064255332Scy		nat->nat_odport = dport;
3065255332Scy		nat->nat_ndport = nport;
3066255332Scy		nat->nat_osport = sport;
3067255332Scy		nat->nat_nsport = sport;
3068145522Sdarrenr		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
3069145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
3070255332Scy		nat->nat_oicmpid = fin->fin_data[1];
3071145522Sdarrenr		((icmphdr_t *)fin->fin_dp)->icmp_id = nport;
3072255332Scy		nat->nat_nicmpid = nport;
3073145522Sdarrenr	}
3074145522Sdarrenr
3075145522Sdarrenr	return move;
3076145522Sdarrenr}
3077145522Sdarrenr
3078145522Sdarrenr/* ------------------------------------------------------------------------ */
3079255332Scy/* Function:    ipf_nat_add                                                 */
3080145522Sdarrenr/* Returns:     nat_t* - NULL == failure to create new NAT structure,       */
3081145522Sdarrenr/*                       else pointer to new NAT structure                  */
3082145522Sdarrenr/* Parameters:  fin(I)       - pointer to packet information                */
3083145522Sdarrenr/*              np(I)        - pointer to NAT rule                          */
3084145522Sdarrenr/*              natsave(I)   - pointer to where to store NAT struct pointer */
3085145522Sdarrenr/*              flags(I)     - flags describing the current packet          */
3086145522Sdarrenr/*              direction(I) - direction of packet (in/out)                 */
3087145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
3088145522Sdarrenr/*                                                                          */
3089145522Sdarrenr/* Attempts to create a new NAT entry.  Does not actually change the packet */
3090145522Sdarrenr/* in any way.                                                              */
3091145522Sdarrenr/*                                                                          */
3092145522Sdarrenr/* This fucntion is in three main parts: (1) deal with creating a new NAT   */
3093145522Sdarrenr/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
3094145522Sdarrenr/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
3095145522Sdarrenr/* and (3) building that structure and putting it into the NAT table(s).    */
3096161356Sguido/*                                                                          */
3097161356Sguido/* NOTE: natsave should NOT be used top point back to an ipstate_t struct   */
3098161356Sguido/*       as it can result in memory being corrupted.                        */
3099145522Sdarrenr/* ------------------------------------------------------------------------ */
3100255332Scynat_t *
3101255332Scyipf_nat_add(fin, np, natsave, flags, direction)
3102255332Scy	fr_info_t *fin;
3103255332Scy	ipnat_t *np;
3104255332Scy	nat_t **natsave;
3105255332Scy	u_int flags;
3106255332Scy	int direction;
310753642Sguido{
3108255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3109255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
311060852Sdarrenr	hostmap_t *hm = NULL;
311160852Sdarrenr	nat_t *nat, *natl;
3112255332Scy	natstat_t *nsp;
3113145522Sdarrenr	u_int nflags;
3114145522Sdarrenr	natinfo_t ni;
3115145522Sdarrenr	int move;
311653642Sguido
3117255332Scy	nsp = &softn->ipf_nat_stats;
3118255332Scy
3119255332Scy	if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) >
3120255332Scy	    softn->ipf_nat_table_wm_high) {
3121255332Scy		softn->ipf_nat_doflush = 1;
3122255332Scy	}
3123255332Scy
3124255332Scy	if (nsp->ns_active >= softn->ipf_nat_table_max) {
3125255332Scy		NBUMPSIDED(fin->fin_out, ns_table_max);
3126130886Sdarrenr		return NULL;
3127130886Sdarrenr	}
3128130886Sdarrenr
3129145522Sdarrenr	move = 1;
3130145522Sdarrenr	nflags = np->in_flags & flags;
3131145522Sdarrenr	nflags &= NAT_FROMRULE;
313253642Sguido
3133145522Sdarrenr	ni.nai_np = np;
3134170268Sdarrenr	ni.nai_dport = 0;
3135170268Sdarrenr	ni.nai_sport = 0;
3136145522Sdarrenr
313753642Sguido	/* Give me a new nat */
313853642Sguido	KMALLOC(nat, nat_t *);
313960852Sdarrenr	if (nat == NULL) {
3140255332Scy		NBUMPSIDED(fin->fin_out, ns_memfail);
3141130886Sdarrenr		/*
3142130886Sdarrenr		 * Try to automatically tune the max # of entries in the
3143130886Sdarrenr		 * table allowed to be less than what will cause kmem_alloc()
3144130886Sdarrenr		 * to fail and try to eliminate panics due to out of memory
3145130886Sdarrenr		 * conditions arising.
3146130886Sdarrenr		 */
3147255332Scy		if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) &&
3148255332Scy		    (nsp->ns_active > 100)) {
3149255332Scy			softn->ipf_nat_table_max = nsp->ns_active - 100;
3150255332Scy			printf("table_max reduced to %d\n",
3151255332Scy				softn->ipf_nat_table_max);
3152130886Sdarrenr		}
315353642Sguido		return NULL;
315460852Sdarrenr	}
315553642Sguido
3156255332Scy	if (flags & IPN_ICMPQUERY) {
3157145522Sdarrenr		/*
3158145522Sdarrenr		 * In the ICMP query NAT code, we translate the ICMP id fields
3159145522Sdarrenr		 * to make them unique. This is indepedent of the ICMP type
3160145522Sdarrenr		 * (e.g. in the unlikely event that a host sends an echo and
3161145522Sdarrenr		 * an tstamp request with the same id, both packets will have
3162145522Sdarrenr		 * their ip address/id field changed in the same way).
3163145522Sdarrenr		 */
3164145522Sdarrenr		/* The icmp_id field is used by the sender to identify the
3165145522Sdarrenr		 * process making the icmp request. (the receiver justs
3166145522Sdarrenr		 * copies it back in its response). So, it closely matches
3167145522Sdarrenr		 * the concept of source port. We overlay sport, so we can
3168145522Sdarrenr		 * maximally reuse the existing code.
3169145522Sdarrenr		 */
3170255332Scy		ni.nai_sport = fin->fin_data[1];
3171255332Scy		ni.nai_dport = 0;
3172145522Sdarrenr	}
3173145522Sdarrenr
317453642Sguido	bzero((char *)nat, sizeof(*nat));
317553642Sguido	nat->nat_flags = flags;
3176170268Sdarrenr	nat->nat_redir = np->in_redir;
3177255332Scy	nat->nat_dir = direction;
3178255332Scy	nat->nat_pr[0] = fin->fin_p;
3179255332Scy	nat->nat_pr[1] = fin->fin_p;
3180145522Sdarrenr
318153642Sguido	/*
3182255332Scy	 * Search the current table for a match and create a new mapping
3183255332Scy	 * if there is none found.
318453642Sguido	 */
3185255332Scy	if (np->in_redir & NAT_DIVERTUDP) {
3186255332Scy		move = ipf_nat_newdivert(fin, nat, &ni);
3187255332Scy
3188255332Scy	} else if (np->in_redir & NAT_REWRITE) {
3189255332Scy		move = ipf_nat_newrewrite(fin, nat, &ni);
3190255332Scy
3191255332Scy	} else if (direction == NAT_OUTBOUND) {
319253642Sguido		/*
3193145522Sdarrenr		 * We can now arrange to call this for the same connection
3194145522Sdarrenr		 * because ipf_nat_new doesn't protect the code path into
3195145522Sdarrenr		 * this function.
319653642Sguido		 */
3197255332Scy		natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p,
3198145522Sdarrenr				     fin->fin_src, fin->fin_dst);
3199145522Sdarrenr		if (natl != NULL) {
3200161356Sguido			KFREE(nat);
3201145522Sdarrenr			nat = natl;
3202145522Sdarrenr			goto done;
3203145522Sdarrenr		}
320453642Sguido
3205255332Scy		move = ipf_nat_newmap(fin, nat, &ni);
320653642Sguido	} else {
320753642Sguido		/*
3208255332Scy		 * NAT_INBOUND is used for redirects rules
320953642Sguido		 */
3210255332Scy		natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p,
3211255332Scy					fin->fin_src, fin->fin_dst);
3212145522Sdarrenr		if (natl != NULL) {
3213161356Sguido			KFREE(nat);
3214145522Sdarrenr			nat = natl;
3215145522Sdarrenr			goto done;
321660852Sdarrenr		}
321753642Sguido
3218255332Scy		move = ipf_nat_newrdr(fin, nat, &ni);
3219145522Sdarrenr	}
3220255332Scy	if (move == -1)
3221255332Scy		goto badnat;
322253642Sguido
3223255332Scy	np = ni.nai_np;
3224255332Scy
3225255332Scy	nat->nat_mssclamp = np->in_mssclamp;
3226255332Scy	nat->nat_me = natsave;
3227255332Scy	nat->nat_fr = fin->fin_fr;
3228255332Scy	nat->nat_rev = fin->fin_rev;
3229255332Scy	nat->nat_ptr = np;
3230255332Scy	nat->nat_dlocal = np->in_dlocal;
3231255332Scy
3232255332Scy	if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) {
3233255332Scy		if (ipf_proxy_new(fin, nat) == -1) {
3234255332Scy			NBUMPSIDED(fin->fin_out, ns_appr_fail);
3235255332Scy			goto badnat;
323653642Sguido		}
323753642Sguido	}
323853642Sguido
3239255332Scy	nat->nat_ifps[0] = np->in_ifps[0];
3240255332Scy	if (np->in_ifps[0] != NULL) {
3241255332Scy		COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]);
3242145522Sdarrenr	}
3243145522Sdarrenr
3244255332Scy	nat->nat_ifps[1] = np->in_ifps[1];
3245255332Scy	if (np->in_ifps[1] != NULL) {
3246255332Scy		COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]);
3247255332Scy	}
324853642Sguido
3249255332Scy	if (ipf_nat_finalise(fin, nat) == -1) {
3250255332Scy		goto badnat;
3251255332Scy	}
325253642Sguido
3253255332Scy	np->in_use++;
325453642Sguido
3255255332Scy	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
3256255332Scy		if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) {
3257255332Scy			ipf_nat_delrdr(softn, np);
3258255332Scy			ipf_nat_addrdr(softn, np);
3259255332Scy		} else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) {
3260255332Scy			ipf_nat_delmap(softn, np);
3261255332Scy			ipf_nat_addmap(softn, np);
3262145522Sdarrenr		}
3263145522Sdarrenr	}
326453642Sguido
3265145522Sdarrenr	if (flags & SI_WILDP)
3266255332Scy		nsp->ns_wilds++;
3267255332Scy	nsp->ns_proto[nat->nat_pr[0]]++;
3268255332Scy
3269145522Sdarrenr	goto done;
3270145522Sdarrenrbadnat:
3271255332Scy	DT2(ns_badnatnew, fr_info_t *, fin, nat_t *, nat);
3272255332Scy	NBUMPSIDE(fin->fin_out, ns_badnatnew);
3273145522Sdarrenr	if ((hm = nat->nat_hm) != NULL)
3274255332Scy		ipf_nat_hostmapdel(softc, &hm);
3275145522Sdarrenr	KFREE(nat);
3276145522Sdarrenr	nat = NULL;
3277145522Sdarrenrdone:
3278255332Scy	if (nat != NULL && np != NULL)
3279255332Scy		np->in_hits++;
3280255332Scy	if (natsave != NULL)
3281255332Scy		*natsave = nat;
3282145522Sdarrenr	return nat;
3283145522Sdarrenr}
328460852Sdarrenr
328560852Sdarrenr
3286145522Sdarrenr/* ------------------------------------------------------------------------ */
3287255332Scy/* Function:    ipf_nat_finalise                                            */
3288145522Sdarrenr/* Returns:     int - 0 == sucess, -1 == failure                            */
3289145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
3290145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
3291145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
3292145522Sdarrenr/*                                                                          */
3293145522Sdarrenr/* This is the tail end of constructing a new NAT entry and is the same     */
3294145522Sdarrenr/* for both IPv4 and IPv6.                                                  */
3295145522Sdarrenr/* ------------------------------------------------------------------------ */
3296145522Sdarrenr/*ARGSUSED*/
3297255332Scystatic int
3298255332Scyipf_nat_finalise(fin, nat)
3299255332Scy	fr_info_t *fin;
3300255332Scy	nat_t *nat;
3301145522Sdarrenr{
3302255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3303255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3304255332Scy	u_32_t sum1, sum2, sumd;
3305145522Sdarrenr	frentry_t *fr;
3306255332Scy	u_32_t flags;
3307255332Scy#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC)
3308255332Scy	qpktinfo_t *qpi = fin->fin_qpi;
3309255332Scy#endif
3310145522Sdarrenr
3311255332Scy	flags = nat->nat_flags;
3312145522Sdarrenr
3313255332Scy	switch (nat->nat_pr[0])
3314255332Scy	{
3315255332Scy	case IPPROTO_ICMP :
3316255332Scy		sum1 = LONG_SUM(ntohs(nat->nat_oicmpid));
3317255332Scy		sum2 = LONG_SUM(ntohs(nat->nat_nicmpid));
3318255332Scy		CALC_SUMD(sum1, sum2, sumd);
3319255332Scy		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
3320255332Scy
3321255332Scy		break;
3322255332Scy
3323255332Scy	default :
3324255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \
3325255332Scy				ntohs(nat->nat_osport));
3326255332Scy		sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \
3327255332Scy				ntohs(nat->nat_nsport));
3328255332Scy		CALC_SUMD(sum1, sum2, sumd);
3329255332Scy		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
3330255332Scy
3331255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \
3332255332Scy				ntohs(nat->nat_odport));
3333255332Scy		sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \
3334255332Scy				ntohs(nat->nat_ndport));
3335255332Scy		CALC_SUMD(sum1, sum2, sumd);
3336255332Scy		nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16);
3337255332Scy		break;
3338161356Sguido	}
3339255332Scy
3340255332Scy	/*
3341255332Scy	 * Compute the partial checksum, just in case.
3342255332Scy	 * This is only ever placed into outbound packets so care needs
3343255332Scy	 * to be taken over which pair of addresses are used.
3344255332Scy	 */
3345255332Scy	if (nat->nat_dir == NAT_OUTBOUND) {
3346255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
3347255332Scy		sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr));
3348255332Scy	} else {
3349255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr));
3350255332Scy		sum1 += LONG_SUM(ntohl(nat->nat_odstaddr));
3351161356Sguido	}
3352255332Scy	sum1 += nat->nat_pr[1];
3353255332Scy	nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16);
3354145522Sdarrenr
3355255332Scy	sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr));
3356255332Scy	sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
3357255332Scy	CALC_SUMD(sum1, sum2, sumd);
3358255332Scy	nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
335992685Sdarrenr
3360255332Scy	sum1 = LONG_SUM(ntohl(nat->nat_odstaddr));
3361255332Scy	sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr));
3362255332Scy	CALC_SUMD(sum1, sum2, sumd);
3363255332Scy	nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16);
336492685Sdarrenr
3365255332Scy	nat->nat_v[0] = 4;
3366255332Scy	nat->nat_v[1] = 4;
3367255332Scy
3368255332Scy	if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
3369255332Scy		nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
3370255332Scy	}
3371255332Scy
3372255332Scy	if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
3373255332Scy		nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
3374255332Scy	}
3375255332Scy
3376255332Scy	if ((nat->nat_flags & SI_CLONE) == 0)
3377255332Scy		nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat);
3378255332Scy
3379255332Scy	if (ipf_nat_insert(softc, softn, nat) == 0) {
3380255332Scy		if (softn->ipf_nat_logging)
3381255332Scy			ipf_nat_log(softc, softn, nat, NL_NEW);
3382255332Scy		fr = nat->nat_fr;
3383145522Sdarrenr		if (fr != NULL) {
3384145522Sdarrenr			MUTEX_ENTER(&fr->fr_lock);
3385145522Sdarrenr			fr->fr_ref++;
3386145522Sdarrenr			MUTEX_EXIT(&fr->fr_lock);
3387145522Sdarrenr		}
3388145522Sdarrenr		return 0;
3389145522Sdarrenr	}
339092685Sdarrenr
3391255332Scy	NBUMPSIDED(fin->fin_out, ns_unfinalised);
3392145522Sdarrenr	/*
3393145522Sdarrenr	 * nat_insert failed, so cleanup time...
3394145522Sdarrenr	 */
3395255332Scy	if (nat->nat_sync != NULL)
3396255332Scy		ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync);
3397145522Sdarrenr	return -1;
339853642Sguido}
339953642Sguido
340053642Sguido
3401145522Sdarrenr/* ------------------------------------------------------------------------ */
3402255332Scy/* Function:    ipf_nat_insert                                              */
3403255332Scy/* Returns:     int - 0 == sucess, -1 == failure                            */
3404255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
3405255332Scy/*              softn(I) - pointer to NAT context structure                 */
3406255332Scy/*              nat(I) - pointer to NAT structure                           */
3407255332Scy/* Write Lock:  ipf_nat                                                     */
3408145522Sdarrenr/*                                                                          */
3409145522Sdarrenr/* Insert a NAT entry into the hash tables for searching and add it to the  */
3410145522Sdarrenr/* list of active NAT entries.  Adjust global counters when complete.       */
3411145522Sdarrenr/* ------------------------------------------------------------------------ */
3412255332Scyint
3413255332Scyipf_nat_insert(softc, softn, nat)
3414255332Scy	ipf_main_softc_t *softc;
3415255332Scy	ipf_nat_softc_t *softn;
3416255332Scy	nat_t *nat;
341760852Sdarrenr{
3418255332Scy	u_int hv0, hv1;
3419255332Scy	u_int sp, dp;
3420255332Scy	ipnat_t *in;
342160852Sdarrenr
3422145522Sdarrenr	/*
3423145522Sdarrenr	 * Try and return an error as early as possible, so calculate the hash
3424145522Sdarrenr	 * entry numbers first and then proceed.
3425145522Sdarrenr	 */
3426145522Sdarrenr	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
3427255332Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
3428255332Scy			sp = nat->nat_osport;
3429255332Scy			dp = nat->nat_odport;
3430255332Scy		} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
3431255332Scy			sp = 0;
3432255332Scy			dp = nat->nat_oicmpid;
3433255332Scy		} else {
3434255332Scy			sp = 0;
3435255332Scy			dp = 0;
3436255332Scy		}
3437255332Scy		hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff);
3438255332Scy		hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff);
3439255332Scy		/*
3440255332Scy		 * TRACE nat_osrcaddr, nat_osport, nat_odstaddr,
3441255332Scy		 * nat_odport, hv0
3442255332Scy		 */
3443255332Scy
3444255332Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
3445255332Scy			sp = nat->nat_nsport;
3446255332Scy			dp = nat->nat_ndport;
3447255332Scy		} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
3448255332Scy			sp = 0;
3449255332Scy			dp = nat->nat_nicmpid;
3450255332Scy		} else {
3451255332Scy			sp = 0;
3452255332Scy			dp = 0;
3453255332Scy		}
3454255332Scy		hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff);
3455255332Scy		hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff);
3456255332Scy		/*
3457255332Scy		 * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr,
3458255332Scy		 * nat_ndport, hv1
3459255332Scy		 */
346080482Sdarrenr	} else {
3461255332Scy		hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff);
3462255332Scy		hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff);
3463255332Scy		/* TRACE nat_osrcaddr, nat_odstaddr, hv0 */
346480482Sdarrenr
3465255332Scy		hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff);
3466255332Scy		hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff);
3467255332Scy		/* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */
3468145522Sdarrenr	}
3469145522Sdarrenr
3470255332Scy	nat->nat_hv[0] = hv0;
3471255332Scy	nat->nat_hv[1] = hv1;
3472145522Sdarrenr
3473145522Sdarrenr	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
3474145522Sdarrenr
3475255332Scy	in = nat->nat_ptr;
3476255332Scy	nat->nat_ref = nat->nat_me ? 2 : 1;
3477145522Sdarrenr
3478145522Sdarrenr	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
3479255332Scy	nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4);
3480145522Sdarrenr
3481161356Sguido	if (nat->nat_ifnames[1][0] != '\0') {
3482145522Sdarrenr		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
3483255332Scy		nat->nat_ifps[1] = ipf_resolvenic(softc,
3484255332Scy						  nat->nat_ifnames[1], 4);
3485255332Scy	} else if (in->in_ifnames[1] != -1) {
3486255332Scy		char *name;
3487255332Scy
3488255332Scy		name = in->in_names + in->in_ifnames[1];
3489255332Scy		if (name[1] != '\0' && name[0] != '-' && name[0] != '*') {
3490255332Scy			(void) strncpy(nat->nat_ifnames[1],
3491255332Scy				       nat->nat_ifnames[0], LIFNAMSIZ);
3492255332Scy			nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
3493255332Scy			nat->nat_ifps[1] = nat->nat_ifps[0];
3494255332Scy		}
3495145522Sdarrenr	}
3496255332Scy	if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
3497255332Scy		nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
3498255332Scy	}
3499255332Scy	if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
3500255332Scy		nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
3501255332Scy	}
3502145522Sdarrenr
3503255332Scy	return ipf_nat_hashtab_add(softc, softn, nat);
3504255332Scy}
3505145522Sdarrenr
3506255332Scy
3507255332Scy/* ------------------------------------------------------------------------ */
3508255332Scy/* Function:    ipf_nat_hashtab_add                                         */
3509255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
3510255332Scy/*              softn(I) - pointer to NAT context structure                 */
3511255332Scy/*              nat(I) - pointer to NAT structure                           */
3512255332Scy/*                                                                          */
3513255332Scy/* Handle the insertion of a NAT entry into the table/list.                 */
3514255332Scy/* ------------------------------------------------------------------------ */
3515255332Scyint
3516255332Scyipf_nat_hashtab_add(softc, softn, nat)
3517255332Scy	ipf_main_softc_t *softc;
3518255332Scy	ipf_nat_softc_t *softn;
3519255332Scy	nat_t *nat;
3520255332Scy{
3521255332Scy	nat_t **natp;
3522255332Scy	u_int hv0;
3523255332Scy	u_int hv1;
3524255332Scy
3525255332Scy	hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz;
3526255332Scy	hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz;
3527255332Scy
3528255332Scy	if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) {
3529255332Scy		u_int swap;
3530255332Scy
3531255332Scy		swap = hv0;
3532255332Scy		hv0 = hv1;
3533255332Scy		hv1 = swap;
3534255332Scy	}
3535255332Scy
3536255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >=
3537255332Scy	    softn->ipf_nat_maxbucket) {
3538255332Scy		DT1(ns_bucket_max_0, int,
3539255332Scy		    softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]);
3540255332Scy		NBUMPSIDE(0, ns_bucket_max);
3541255332Scy		return -1;
3542255332Scy	}
3543255332Scy
3544255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >=
3545255332Scy	    softn->ipf_nat_maxbucket) {
3546255332Scy		DT1(ns_bucket_max_1, int,
3547255332Scy		    softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]);
3548255332Scy		NBUMPSIDE(1, ns_bucket_max);
3549255332Scy		return -1;
3550255332Scy	}
3551255332Scy
3552255332Scy	/*
3553255332Scy	 * The ordering of operations in the list and hash table insertion
3554255332Scy	 * is very important.  The last operation for each task should be
3555255332Scy	 * to update the top of the list, after all the "nexts" have been
3556255332Scy	 * done so that walking the list while it is being done does not
3557255332Scy	 * find strange pointers.
3558255332Scy	 *
3559255332Scy	 * Global list of NAT instances
3560255332Scy	 */
3561255332Scy	nat->nat_next = softn->ipf_nat_instances;
3562255332Scy	nat->nat_pnext = &softn->ipf_nat_instances;
3563255332Scy	if (softn->ipf_nat_instances)
3564255332Scy		softn->ipf_nat_instances->nat_pnext = &nat->nat_next;
3565255332Scy	softn->ipf_nat_instances = nat;
3566255332Scy
3567255332Scy	/*
3568255332Scy	 * Inbound hash table.
3569255332Scy	 */
3570255332Scy	natp = &softn->ipf_nat_table[0][hv0];
357167614Sdarrenr	nat->nat_phnext[0] = natp;
357260852Sdarrenr	nat->nat_hnext[0] = *natp;
3573255332Scy	if (*natp) {
3574255332Scy		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
3575255332Scy	} else {
3576255332Scy		NBUMPSIDE(0, ns_inuse);
3577255332Scy	}
357860852Sdarrenr	*natp = nat;
3579255332Scy	NBUMPSIDE(0, ns_bucketlen[hv0]);
358067614Sdarrenr
3581255332Scy	/*
3582255332Scy	 * Outbound hash table.
3583255332Scy	 */
3584255332Scy	natp = &softn->ipf_nat_table[1][hv1];
3585255332Scy	nat->nat_phnext[1] = natp;
3586255332Scy	nat->nat_hnext[1] = *natp;
358767614Sdarrenr	if (*natp)
358867614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
3589255332Scy	else {
3590255332Scy		NBUMPSIDE(1, ns_inuse);
3591255332Scy	}
359260852Sdarrenr	*natp = nat;
3593255332Scy	NBUMPSIDE(1, ns_bucketlen[hv1]);
359460852Sdarrenr
3595255332Scy	ipf_nat_setqueue(softc, softn, nat);
3596145522Sdarrenr
3597255332Scy	if (nat->nat_dir & NAT_OUTBOUND) {
3598255332Scy		NBUMPSIDE(1, ns_added);
3599255332Scy	} else {
3600255332Scy		NBUMPSIDE(0, ns_added);
3601255332Scy	}
3602255332Scy	softn->ipf_nat_stats.ns_active++;
3603145522Sdarrenr	return 0;
360460852Sdarrenr}
360560852Sdarrenr
360660852Sdarrenr
3607145522Sdarrenr/* ------------------------------------------------------------------------ */
3608255332Scy/* Function:    ipf_nat_icmperrorlookup                                     */
3609145522Sdarrenr/* Returns:     nat_t* - point to matching NAT structure                    */
3610145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
3611145522Sdarrenr/*              dir(I) - direction of packet (in/out)                       */
3612145522Sdarrenr/*                                                                          */
3613145522Sdarrenr/* Check if the ICMP error message is related to an existing TCP, UDP or    */
3614145522Sdarrenr/* ICMP query nat entry.  It is assumed that the packet is already of the   */
3615145522Sdarrenr/* the required length.                                                     */
3616145522Sdarrenr/* ------------------------------------------------------------------------ */
3617255332Scynat_t *
3618255332Scyipf_nat_icmperrorlookup(fin, dir)
3619255332Scy	fr_info_t *fin;
3620255332Scy	int dir;
362153642Sguido{
3622255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3623255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3624145522Sdarrenr	int flags = 0, type, minlen;
3625145522Sdarrenr	icmphdr_t *icmp, *orgicmp;
3626255332Scy	nat_stat_side_t *nside;
362753642Sguido	tcphdr_t *tcp = NULL;
3628145522Sdarrenr	u_short data[2];
3629145522Sdarrenr	nat_t *nat;
363053642Sguido	ip_t *oip;
3631145522Sdarrenr	u_int p;
363253642Sguido
3633145522Sdarrenr	icmp = fin->fin_dp;
3634145522Sdarrenr	type = icmp->icmp_type;
3635255332Scy	nside = &softn->ipf_nat_stats.ns_side[fin->fin_out];
363653642Sguido	/*
363753642Sguido	 * Does it at least have the return (basic) IP header ?
363853642Sguido	 * Only a basic IP header (no options) should be with an ICMP error
3639145522Sdarrenr	 * header.  Also, if it's not an error type, then return.
364053642Sguido	 */
3641255332Scy	if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) {
3642255332Scy		ATOMIC_INCL(nside->ns_icmp_basic);
364353642Sguido		return NULL;
3644255332Scy	}
3645145522Sdarrenr
364653642Sguido	/*
3647145522Sdarrenr	 * Check packet size
364853642Sguido	 */
364953642Sguido	oip = (ip_t *)((char *)fin->fin_dp + 8);
3650145522Sdarrenr	minlen = IP_HL(oip) << 2;
3651145522Sdarrenr	if ((minlen < sizeof(ip_t)) ||
3652255332Scy	    (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) {
3653255332Scy		ATOMIC_INCL(nside->ns_icmp_size);
365453642Sguido		return NULL;
3655255332Scy	}
3656255332Scy
365764580Sdarrenr	/*
365864580Sdarrenr	 * Is the buffer big enough for all of it ?  It's the size of the IP
365964580Sdarrenr	 * header claimed in the encapsulated part which is of concern.  It
366064580Sdarrenr	 * may be too big to be in this buffer but not so big that it's
366164580Sdarrenr	 * outside the ICMP packet, leading to TCP deref's causing problems.
366264580Sdarrenr	 * This is possible because we don't know how big oip_hl is when we
3663255332Scy	 * do the pullup early in ipf_check() and thus can't gaurantee it is
366464580Sdarrenr	 * all here now.
366564580Sdarrenr	 */
3666255332Scy#ifdef  ipf_nat_KERNEL
366764580Sdarrenr	{
366864580Sdarrenr	mb_t *m;
366964580Sdarrenr
3670145522Sdarrenr	m = fin->fin_m;
3671145522Sdarrenr# if defined(MENTAT)
3672255332Scy	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
3673255332Scy	    (char *)m->b_wptr) {
3674255332Scy		ATOMIC_INCL(nside->ns_icmp_mbuf);
367564580Sdarrenr		return NULL;
3676255332Scy	}
367764580Sdarrenr# else
367864580Sdarrenr	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
3679255332Scy	    (char *)fin->fin_ip + M_LEN(m)) {
3680255332Scy		ATOMIC_INCL(nside->ns_icmp_mbuf);
368164580Sdarrenr		return NULL;
3682255332Scy	}
368364580Sdarrenr# endif
368464580Sdarrenr	}
368564580Sdarrenr#endif
368664580Sdarrenr
3687255332Scy	if (fin->fin_daddr != oip->ip_src.s_addr) {
3688255332Scy		ATOMIC_INCL(nside->ns_icmp_address);
3689145522Sdarrenr		return NULL;
3690255332Scy	}
3691145522Sdarrenr
3692145522Sdarrenr	p = oip->ip_p;
3693145522Sdarrenr	if (p == IPPROTO_TCP)
369453642Sguido		flags = IPN_TCP;
3695145522Sdarrenr	else if (p == IPPROTO_UDP)
369653642Sguido		flags = IPN_UDP;
3697145522Sdarrenr	else if (p == IPPROTO_ICMP) {
3698145522Sdarrenr		orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2));
3699145522Sdarrenr
3700145522Sdarrenr		/* see if this is related to an ICMP query */
3701255332Scy		if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) {
3702145522Sdarrenr			data[0] = fin->fin_data[0];
3703145522Sdarrenr			data[1] = fin->fin_data[1];
3704145522Sdarrenr			fin->fin_data[0] = 0;
3705145522Sdarrenr			fin->fin_data[1] = orgicmp->icmp_id;
3706145522Sdarrenr
3707145522Sdarrenr			flags = IPN_ICMPERR|IPN_ICMPQUERY;
3708145522Sdarrenr			/*
3709145522Sdarrenr			 * NOTE : dir refers to the direction of the original
3710145522Sdarrenr			 *        ip packet. By definition the icmp error
3711145522Sdarrenr			 *        message flows in the opposite direction.
3712145522Sdarrenr			 */
3713145522Sdarrenr			if (dir == NAT_INBOUND)
3714255332Scy				nat = ipf_nat_inlookup(fin, flags, p,
3715255332Scy						       oip->ip_dst,
3716255332Scy						       oip->ip_src);
3717145522Sdarrenr			else
3718255332Scy				nat = ipf_nat_outlookup(fin, flags, p,
3719255332Scy							oip->ip_dst,
3720255332Scy							oip->ip_src);
3721145522Sdarrenr			fin->fin_data[0] = data[0];
3722145522Sdarrenr			fin->fin_data[1] = data[1];
3723145522Sdarrenr			return nat;
3724145522Sdarrenr		}
3725145522Sdarrenr	}
3726255332Scy
372753642Sguido	if (flags & IPN_TCPUDP) {
372864580Sdarrenr		minlen += 8;		/* + 64bits of data to get ports */
3729255332Scy		/* TRACE (fin,minlen) */
3730255332Scy		if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) {
3731255332Scy			ATOMIC_INCL(nside->ns_icmp_short);
373264580Sdarrenr			return NULL;
3733255332Scy		}
373492685Sdarrenr
373592685Sdarrenr		data[0] = fin->fin_data[0];
373692685Sdarrenr		data[1] = fin->fin_data[1];
3737145522Sdarrenr		tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2));
373892685Sdarrenr		fin->fin_data[0] = ntohs(tcp->th_dport);
373992685Sdarrenr		fin->fin_data[1] = ntohs(tcp->th_sport);
374092685Sdarrenr
374192685Sdarrenr		if (dir == NAT_INBOUND) {
3742255332Scy			nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst,
3743255332Scy					       oip->ip_src);
374492685Sdarrenr		} else {
3745255332Scy			nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst,
3746145522Sdarrenr					    oip->ip_src);
374792685Sdarrenr		}
374892685Sdarrenr		fin->fin_data[0] = data[0];
374992685Sdarrenr		fin->fin_data[1] = data[1];
375092685Sdarrenr		return nat;
375153642Sguido	}
375260852Sdarrenr	if (dir == NAT_INBOUND)
3753255332Scy		nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
375460852Sdarrenr	else
3755255332Scy		nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
3756255332Scy
3757255332Scy	return nat;
375853642Sguido}
375953642Sguido
376053642Sguido
3761145522Sdarrenr/* ------------------------------------------------------------------------ */
3762255332Scy/* Function:    ipf_nat_icmperror                                           */
3763145522Sdarrenr/* Returns:     nat_t* - point to matching NAT structure                    */
3764145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
3765145522Sdarrenr/*              nflags(I) - NAT flags for this packet                       */
3766145522Sdarrenr/*              dir(I)    - direction of packet (in/out)                    */
3767145522Sdarrenr/*                                                                          */
3768145522Sdarrenr/* Fix up an ICMP packet which is an error message for an existing NAT      */
3769145522Sdarrenr/* session.  This will correct both packet header data and checksums.       */
3770145522Sdarrenr/*                                                                          */
3771145522Sdarrenr/* This should *ONLY* be used for incoming ICMP error packets to make sure  */
3772145522Sdarrenr/* a NAT'd ICMP packet gets correctly recognised.                           */
3773145522Sdarrenr/* ------------------------------------------------------------------------ */
3774255332Scynat_t *
3775255332Scyipf_nat_icmperror(fin, nflags, dir)
3776255332Scy	fr_info_t *fin;
3777255332Scy	u_int *nflags;
3778255332Scy	int dir;
377953642Sguido{
3780255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3781255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3782145522Sdarrenr	u_32_t sum1, sum2, sumd, sumd2;
3783255332Scy	struct in_addr a1, a2, a3, a4;
3784170268Sdarrenr	int flags, dlen, odst;
3785145522Sdarrenr	icmphdr_t *icmp;
3786145522Sdarrenr	u_short *csump;
378795418Sdarrenr	tcphdr_t *tcp;
378853642Sguido	nat_t *nat;
378953642Sguido	ip_t *oip;
3790145522Sdarrenr	void *dp;
379153642Sguido
3792255332Scy	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
3793255332Scy		NBUMPSIDED(fin->fin_out, ns_icmp_short);
379463523Sdarrenr		return NULL;
3795255332Scy	}
3796255332Scy
379767614Sdarrenr	/*
3798255332Scy	 * ipf_nat_icmperrorlookup() will return NULL for `defective' packets.
379967614Sdarrenr	 */
3800255332Scy	if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) {
3801255332Scy		NBUMPSIDED(fin->fin_out, ns_icmp_notfound);
380253642Sguido		return NULL;
3803255332Scy	}
380492685Sdarrenr
3805145522Sdarrenr	tcp = NULL;
3806145522Sdarrenr	csump = NULL;
380792685Sdarrenr	flags = 0;
3808130886Sdarrenr	sumd2 = 0;
380953642Sguido	*nflags = IPN_ICMPERR;
3810145522Sdarrenr	icmp = fin->fin_dp;
381153642Sguido	oip = (ip_t *)&icmp->icmp_ip;
3812145522Sdarrenr	dp = (((char *)oip) + (IP_HL(oip) << 2));
3813145522Sdarrenr	if (oip->ip_p == IPPROTO_TCP) {
3814145522Sdarrenr		tcp = (tcphdr_t *)dp;
3815145522Sdarrenr		csump = (u_short *)&tcp->th_sum;
381653642Sguido		flags = IPN_TCP;
3817145522Sdarrenr	} else if (oip->ip_p == IPPROTO_UDP) {
3818145522Sdarrenr		udphdr_t *udp;
3819145522Sdarrenr
3820145522Sdarrenr		udp = (udphdr_t *)dp;
3821145522Sdarrenr		tcp = (tcphdr_t *)dp;
3822145522Sdarrenr		csump = (u_short *)&udp->uh_sum;
382353642Sguido		flags = IPN_UDP;
3824145522Sdarrenr	} else if (oip->ip_p == IPPROTO_ICMP)
3825145522Sdarrenr		flags = IPN_ICMPQUERY;
3826145522Sdarrenr	dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip);
382795418Sdarrenr
382895418Sdarrenr	/*
382953642Sguido	 * Need to adjust ICMP header to include the real IP#'s and
383053642Sguido	 * port #'s.  Only apply a checksum change relative to the
3831255332Scy	 * IP address change as it will be modified again in ipf_nat_checkout
383253642Sguido	 * for both address and port.  Two checksum changes are
383353642Sguido	 * necessary for the two header address changes.  Be careful
383453642Sguido	 * to only modify the checksum once for the port # and twice
383553642Sguido	 * for the IP#.
383653642Sguido	 */
383760852Sdarrenr
383867614Sdarrenr	/*
383967614Sdarrenr	 * Step 1
384067614Sdarrenr	 * Fix the IP addresses in the offending IP packet. You also need
3841170268Sdarrenr	 * to adjust the IP header checksum of that offending IP packet.
384267614Sdarrenr	 *
3843145522Sdarrenr	 * Normally, you would expect that the ICMP checksum of the
3844130886Sdarrenr	 * ICMP error message needs to be adjusted as well for the
3845130886Sdarrenr	 * IP address change in oip.
3846145522Sdarrenr	 * However, this is a NOP, because the ICMP checksum is
3847130886Sdarrenr	 * calculated over the complete ICMP packet, which includes the
3848145522Sdarrenr	 * changed oip IP addresses and oip->ip_sum. However, these
3849130886Sdarrenr	 * two changes cancel each other out (if the delta for
3850145522Sdarrenr	 * the IP address is x, then the delta for ip_sum is minus x),
3851130886Sdarrenr	 * so no change in the icmp_cksum is necessary.
3852130886Sdarrenr	 *
3853170268Sdarrenr	 * Inbound ICMP
3854170268Sdarrenr	 * ------------
3855170268Sdarrenr	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
3856170268Sdarrenr	 * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b)
3857255332Scy	 * - OIP_SRC(c)=nat_newsrcip,          OIP_DST(b)=nat_newdstip
3858255332Scy	 *=> OIP_SRC(c)=nat_oldsrcip,          OIP_DST(b)=nat_olddstip
3859170268Sdarrenr	 *
3860170268Sdarrenr	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
3861170268Sdarrenr	 * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a)
3862255332Scy	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
3863255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3864170268Sdarrenr	 *
3865255332Scy	 * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
3866255332Scy	 * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d)
3867255332Scy	 * - OIP_SRC(c)=nat_newsrcip,          OIP_DST(d)=nat_newdstip
3868255332Scy	 *=> OIP_SRC(c)=nat_oldsrcip,          OIP_DST(d)=nat_olddstip
3869255332Scy	 *
3870255332Scy	 * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
3871255332Scy	 * - response to outgoing packet (d,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
3874255332Scy	 *
3875170268Sdarrenr	 * Outbound ICMP
3876170268Sdarrenr	 * -------------
3877170268Sdarrenr	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
3878170268Sdarrenr	 * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
3879255332Scy	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
3880255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3881170268Sdarrenr	 *
3882170268Sdarrenr	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
3883170268Sdarrenr	 * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c)
3884255332Scy	 * - OIP_SRC(a)=nat_newsrcip,          OIP_DST(c)=nat_newdstip
3885255332Scy	 *=> OIP_SRC(a)=nat_oldsrcip,          OIP_DST(c)=nat_olddstip
3886170268Sdarrenr	 *
3887255332Scy	 * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
3888255332Scy	 * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d)
3889255332Scy	 * - OIP_SRC(c)=nat_olddstip,          OIP_DST(d)=nat_oldsrcip
3890255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3891255332Scy	 *
3892255332Scy	 * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
3893255332Scy	 * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a)
3894255332Scy	 * - OIP_SRC(b)=nat_newsrcip,          OIP_DST(a)=nat_newdstip
3895255332Scy	 *=> OIP_SRC(a)=nat_oldsrcip,          OIP_DST(c)=nat_olddstip
3896130886Sdarrenr	 */
3897255332Scy
3898255332Scy	if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) ||
3899255332Scy	    ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) {
3900255332Scy		a1.s_addr = ntohl(nat->nat_osrcaddr);
3901255332Scy		a4.s_addr = ntohl(oip->ip_src.s_addr);
3902255332Scy		a3.s_addr = ntohl(nat->nat_odstaddr);
3903255332Scy		a2.s_addr = ntohl(oip->ip_dst.s_addr);
3904170268Sdarrenr		oip->ip_src.s_addr = htonl(a1.s_addr);
3905255332Scy		oip->ip_dst.s_addr = htonl(a3.s_addr);
3906255332Scy		odst = 1;
3907170268Sdarrenr	} else {
3908255332Scy		a1.s_addr = ntohl(nat->nat_ndstaddr);
3909170268Sdarrenr		a2.s_addr = ntohl(oip->ip_dst.s_addr);
3910255332Scy		a3.s_addr = ntohl(nat->nat_nsrcaddr);
3911255332Scy		a4.s_addr = ntohl(oip->ip_src.s_addr);
3912255332Scy		oip->ip_dst.s_addr = htonl(a3.s_addr);
3913255332Scy		oip->ip_src.s_addr = htonl(a1.s_addr);
3914255332Scy		odst = 0;
3915170268Sdarrenr	}
3916255332Scy	sum1 = 0;
3917255332Scy	sum2 = 0;
3918255332Scy	sumd = 0;
3919255332Scy	CALC_SUMD(a2.s_addr, a3.s_addr, sum1);
3920255332Scy	CALC_SUMD(a4.s_addr, a1.s_addr, sum2);
3921255332Scy	sumd = sum2 + sum1;
3922255332Scy	if (sumd != 0)
3923255332Scy		ipf_fix_datacksum(&oip->ip_sum, sumd);
3924130886Sdarrenr
3925170268Sdarrenr	sumd2 = sumd;
3926170268Sdarrenr	sum1 = 0;
3927170268Sdarrenr	sum2 = 0;
3928170268Sdarrenr
3929130886Sdarrenr	/*
3930170268Sdarrenr	 * Fix UDP pseudo header checksum to compensate for the
3931170268Sdarrenr	 * IP address change.
3932130886Sdarrenr	 */
3933130886Sdarrenr	if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) {
3934255332Scy		u_32_t sum3, sum4, sumt;
3935255332Scy
393664580Sdarrenr		/*
393767614Sdarrenr		 * Step 2 :
393867614Sdarrenr		 * For offending TCP/UDP IP packets, translate the ports as
393967614Sdarrenr		 * well, based on the NAT specification. Of course such
3940170268Sdarrenr		 * a change may be reflected in the ICMP checksum as well.
394167614Sdarrenr		 *
394267614Sdarrenr		 * Since the port fields are part of the TCP/UDP checksum
394367614Sdarrenr		 * of the offending IP packet, you need to adjust that checksum
3944255332Scy		 * as well... except that the change in the port numbers should
3945170268Sdarrenr		 * be offset by the checksum change.  However, the TCP/UDP
3946170268Sdarrenr		 * checksum will also need to change if there has been an
3947170268Sdarrenr		 * IP address change.
394867614Sdarrenr		 */
3949170268Sdarrenr		if (odst == 1) {
3950255332Scy			sum1 = ntohs(nat->nat_osport);
3951255332Scy			sum4 = ntohs(tcp->th_sport);
3952255332Scy			sum3 = ntohs(nat->nat_odport);
3953255332Scy			sum2 = ntohs(tcp->th_dport);
3954145522Sdarrenr
3955170268Sdarrenr			tcp->th_sport = htons(sum1);
3956255332Scy			tcp->th_dport = htons(sum3);
3957170268Sdarrenr		} else {
3958255332Scy			sum1 = ntohs(nat->nat_ndport);
3959145522Sdarrenr			sum2 = ntohs(tcp->th_dport);
3960255332Scy			sum3 = ntohs(nat->nat_nsport);
3961255332Scy			sum4 = ntohs(tcp->th_sport);
3962170268Sdarrenr
3963255332Scy			tcp->th_dport = htons(sum3);
3964255332Scy			tcp->th_sport = htons(sum1);
3965145522Sdarrenr		}
3966255332Scy		CALC_SUMD(sum4, sum1, sumt);
3967255332Scy		sumd += sumt;
3968255332Scy		CALC_SUMD(sum2, sum3, sumt);
3969255332Scy		sumd += sumt;
397067614Sdarrenr
3971170268Sdarrenr		if (sumd != 0 || sumd2 != 0) {
3972145522Sdarrenr			/*
3973170268Sdarrenr			 * At this point, sumd is the delta to apply to the
3974170268Sdarrenr			 * TCP/UDP header, given the changes in both the IP
3975170268Sdarrenr			 * address and the ports and sumd2 is the delta to
3976170268Sdarrenr			 * apply to the ICMP header, given the IP address
3977170268Sdarrenr			 * change delta that may need to be applied to the
3978170268Sdarrenr			 * TCP/UDP checksum instead.
3979145522Sdarrenr			 *
3980170268Sdarrenr			 * If we will both the IP and TCP/UDP checksums
3981170268Sdarrenr			 * then the ICMP checksum changes by the address
3982170268Sdarrenr			 * delta applied to the TCP/UDP checksum.  If we
3983170268Sdarrenr			 * do not change the TCP/UDP checksum them we
3984170268Sdarrenr			 * apply the delta in ports to the ICMP checksum.
3985145522Sdarrenr			 */
3986161356Sguido			if (oip->ip_p == IPPROTO_UDP) {
3987161356Sguido				if ((dlen >= 8) && (*csump != 0)) {
3988255332Scy					ipf_fix_datacksum(csump, sumd);
3989161356Sguido				} else {
3990255332Scy					CALC_SUMD(sum1, sum4, sumd2);
3991255332Scy					CALC_SUMD(sum3, sum2, sumt);
3992255332Scy					sumd2 += sumt;
3993161356Sguido				}
3994170268Sdarrenr			} else if (oip->ip_p == IPPROTO_TCP) {
3995145522Sdarrenr				if (dlen >= 18) {
3996255332Scy					ipf_fix_datacksum(csump, sumd);
3997145522Sdarrenr				} else {
3998255332Scy					CALC_SUMD(sum1, sum4, sumd2);
3999255332Scy					CALC_SUMD(sum3, sum2, sumt);
4000255332Scy					sumd2 += sumt;
400167614Sdarrenr				}
400253642Sguido			}
4003170268Sdarrenr			if (sumd2 != 0) {
4004170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
4005170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
4006170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
4007255332Scy				ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0);
4008130886Sdarrenr			}
400953642Sguido		}
4010145522Sdarrenr	} else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) {
4011145522Sdarrenr		icmphdr_t *orgicmp;
4012145522Sdarrenr
4013145522Sdarrenr		/*
4014145522Sdarrenr		 * XXX - what if this is bogus hl and we go off the end ?
4015255332Scy		 * In this case, ipf_nat_icmperrorlookup() will have
4016255332Scy		 * returned NULL.
4017145522Sdarrenr		 */
4018145522Sdarrenr		orgicmp = (icmphdr_t *)dp;
4019145522Sdarrenr
4020170268Sdarrenr		if (odst == 1) {
4021255332Scy			if (orgicmp->icmp_id != nat->nat_osport) {
4022145522Sdarrenr
4023145522Sdarrenr				/*
4024145522Sdarrenr				 * Fix ICMP checksum (of the offening ICMP
4025145522Sdarrenr				 * query packet) to compensate the change
4026145522Sdarrenr				 * in the ICMP id of the offending ICMP
4027145522Sdarrenr				 * packet.
4028145522Sdarrenr				 *
4029145522Sdarrenr				 * Since you modify orgicmp->icmp_id with
4030145522Sdarrenr				 * a delta (say x) and you compensate that
4031145522Sdarrenr				 * in origicmp->icmp_cksum with a delta
4032145522Sdarrenr				 * minus x, you don't have to adjust the
4033145522Sdarrenr				 * overall icmp->icmp_cksum
4034145522Sdarrenr				 */
4035145522Sdarrenr				sum1 = ntohs(orgicmp->icmp_id);
4036255332Scy				sum2 = ntohs(nat->nat_oicmpid);
4037145522Sdarrenr				CALC_SUMD(sum1, sum2, sumd);
4038255332Scy				orgicmp->icmp_id = nat->nat_oicmpid;
4039255332Scy				ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd);
4040145522Sdarrenr			}
4041145522Sdarrenr		} /* nat_dir == NAT_INBOUND is impossible for icmp queries */
404253642Sguido	}
404353642Sguido	return nat;
404453642Sguido}
404553642Sguido
404653642Sguido
404753642Sguido/*
4048255332Scy *       MAP-IN    MAP-OUT   RDR-IN   RDR-OUT
4049255332Scy * osrc    X       == src    == src      X
4050255332Scy * odst    X       == dst    == dst      X
4051255332Scy * nsrc  == dst      X         X      == dst
4052255332Scy * ndst  == src      X         X      == src
4053255332Scy * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND
4054255332Scy */
4055255332Scy/*
4056145522Sdarrenr * NB: these lookups don't lock access to the list, it assumed that it has
4057145522Sdarrenr * already been done!
405853642Sguido */
4059145522Sdarrenr/* ------------------------------------------------------------------------ */
4060255332Scy/* Function:    ipf_nat_inlookup                                            */
4061145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
4062145522Sdarrenr/*                       else pointer to matching NAT entry                 */
4063145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
4064145522Sdarrenr/*              flags(I)  - NAT flags for this packet                       */
4065145522Sdarrenr/*              p(I)      - protocol for this packet                        */
4066145522Sdarrenr/*              src(I)    - source IP address                               */
4067145522Sdarrenr/*              mapdst(I) - destination IP address                          */
4068145522Sdarrenr/*                                                                          */
4069145522Sdarrenr/* Lookup a nat entry based on the mapped destination ip address/port and   */
4070145522Sdarrenr/* real source address/port.  We use this lookup when receiving a packet,   */
4071145522Sdarrenr/* we're looking for a table entry, based on the destination address.       */
4072145522Sdarrenr/*                                                                          */
4073145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
4074145522Sdarrenr/*                                                                          */
4075255332Scy/* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
4076145522Sdarrenr/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
4077145522Sdarrenr/*                                                                          */
4078145522Sdarrenr/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
4079145522Sdarrenr/*            the packet is of said protocol                                */
4080145522Sdarrenr/* ------------------------------------------------------------------------ */
4081255332Scynat_t *
4082255332Scyipf_nat_inlookup(fin, flags, p, src, mapdst)
4083255332Scy	fr_info_t *fin;
4084255332Scy	u_int flags, p;
4085255332Scy	struct in_addr src , mapdst;
408653642Sguido{
4087255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
4088255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
4089145522Sdarrenr	u_short sport, dport;
4090145522Sdarrenr	grehdr_t *gre;
409192685Sdarrenr	ipnat_t *ipn;
4092145522Sdarrenr	u_int sflags;
4093145522Sdarrenr	nat_t *nat;
4094145522Sdarrenr	int nflags;
4095145522Sdarrenr	u_32_t dst;
409692685Sdarrenr	void *ifp;
4097255332Scy	u_int hv, rhv;
409853642Sguido
4099161356Sguido	ifp = fin->fin_ifp;
4100145522Sdarrenr	gre = NULL;
410167614Sdarrenr	dst = mapdst.s_addr;
4102145522Sdarrenr	sflags = flags & NAT_TCPUDPICMP;
4103145522Sdarrenr
4104145522Sdarrenr	switch (p)
4105145522Sdarrenr	{
4106145522Sdarrenr	case IPPROTO_TCP :
4107145522Sdarrenr	case IPPROTO_UDP :
410892685Sdarrenr		sport = htons(fin->fin_data[0]);
410992685Sdarrenr		dport = htons(fin->fin_data[1]);
4110145522Sdarrenr		break;
4111145522Sdarrenr	case IPPROTO_ICMP :
4112255332Scy		if (flags & IPN_ICMPERR) {
4113145522Sdarrenr			sport = fin->fin_data[1];
4114255332Scy			dport = 0;
4115255332Scy		} else {
4116145522Sdarrenr			dport = fin->fin_data[1];
4117255332Scy			sport = 0;
4118255332Scy		}
4119145522Sdarrenr		break;
4120145522Sdarrenr	default :
4121255332Scy		sport = 0;
4122255332Scy		dport = 0;
4123145522Sdarrenr		break;
412492685Sdarrenr	}
412553642Sguido
4126145522Sdarrenr
4127145522Sdarrenr	if ((flags & SI_WILDP) != 0)
4128145522Sdarrenr		goto find_in_wild_ports;
4129145522Sdarrenr
4130255332Scy	rhv = NAT_HASH_FN(dst, dport, 0xffffffff);
4131255332Scy	rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff);
4132255332Scy	hv = rhv % softn->ipf_nat_table_sz;
4133255332Scy	nat = softn->ipf_nat_table[1][hv];
4134255332Scy	/* TRACE dst, dport, src, sport, hv, nat */
4135255332Scy
413653642Sguido	for (; nat; nat = nat->nat_hnext[1]) {
4137161356Sguido		if (nat->nat_ifps[0] != NULL) {
4138161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
4139161356Sguido				continue;
4140255332Scy		}
4141161356Sguido
4142255332Scy		if (nat->nat_pr[0] != p)
4143255332Scy			continue;
4144145522Sdarrenr
4145255332Scy		switch (nat->nat_dir)
4146255332Scy		{
4147255332Scy		case NAT_INBOUND :
4148255332Scy		case NAT_DIVERTIN :
4149255332Scy			if (nat->nat_v[0] != 4)
4150255332Scy				continue;
4151255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4152255332Scy			    nat->nat_odstaddr != dst)
4153255332Scy				continue;
4154255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4155255332Scy				if (nat->nat_osport != sport)
4156145522Sdarrenr					continue;
4157255332Scy				if (nat->nat_odport != dport)
4158255332Scy					continue;
4159255332Scy
4160255332Scy			} else if (p == IPPROTO_ICMP) {
4161255332Scy				if (nat->nat_osport != dport) {
4162255332Scy					continue;
4163145522Sdarrenr				}
4164255332Scy			}
4165255332Scy			break;
4166255332Scy		case NAT_DIVERTOUT :
4167255332Scy			if (nat->nat_dlocal)
4168255332Scy				continue;
4169255332Scy		case NAT_OUTBOUND :
4170255332Scy			if (nat->nat_v[1] != 4)
4171255332Scy				continue;
4172255332Scy			if (nat->nat_dlocal)
4173255332Scy				continue;
4174255332Scy			if (nat->nat_dlocal)
4175255332Scy				continue;
4176255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4177255332Scy			    nat->nat_nsrcaddr != dst)
4178255332Scy				continue;
4179255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4180255332Scy				if (nat->nat_ndport != sport)
418192685Sdarrenr					continue;
4182255332Scy				if (nat->nat_nsport != dport)
418392685Sdarrenr					continue;
4184255332Scy
4185255332Scy			} else if (p == IPPROTO_ICMP) {
4186255332Scy				if (nat->nat_osport != dport) {
4187255332Scy					continue;
4188255332Scy				}
418992685Sdarrenr			}
4190255332Scy			break;
4191255332Scy		}
419292685Sdarrenr
4193255332Scy
4194255332Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
419592685Sdarrenr			ipn = nat->nat_ptr;
419692685Sdarrenr			if ((ipn != NULL) && (nat->nat_aps != NULL))
4197255332Scy				if (ipf_proxy_match(fin, nat) != 0)
419892685Sdarrenr					continue;
419992685Sdarrenr		}
4200255332Scy		if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
4201255332Scy			nat->nat_ifps[0] = ifp;
4202255332Scy			nat->nat_mtu[0] = GETIFMTU_4(ifp);
4203255332Scy		}
4204255332Scy		return nat;
420553642Sguido	}
4206145522Sdarrenr
4207145522Sdarrenr	/*
4208145522Sdarrenr	 * So if we didn't find it but there are wildcard members in the hash
4209145522Sdarrenr	 * table, go back and look for them.  We do this search and update here
4210145522Sdarrenr	 * because it is modifying the NAT table and we want to do this only
4211145522Sdarrenr	 * for the first packet that matches.  The exception, of course, is
4212145522Sdarrenr	 * for "dummy" (FI_IGNORE) lookups.
4213145522Sdarrenr	 */
4214145522Sdarrenrfind_in_wild_ports:
4215255332Scy	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
4216255332Scy		NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0);
421767614Sdarrenr		return NULL;
4218255332Scy	}
4219255332Scy	if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
4220255332Scy		NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0);
4221145522Sdarrenr		return NULL;
4222255332Scy	}
4223145522Sdarrenr
4224255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
4225145522Sdarrenr
422680482Sdarrenr	hv = NAT_HASH_FN(dst, 0, 0xffffffff);
4227255332Scy	hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz);
4228255332Scy	WRITE_ENTER(&softc->ipf_nat);
4229145522Sdarrenr
4230255332Scy	nat = softn->ipf_nat_table[1][hv];
4231255332Scy	/* TRACE dst, src, hv, nat */
423267614Sdarrenr	for (; nat; nat = nat->nat_hnext[1]) {
4233161356Sguido		if (nat->nat_ifps[0] != NULL) {
4234161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
4235161356Sguido				continue;
4236255332Scy		}
4237145522Sdarrenr
4238255332Scy		if (nat->nat_pr[0] != fin->fin_p)
423967614Sdarrenr			continue;
4240145522Sdarrenr
4241255332Scy		switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))
4242255332Scy		{
4243255332Scy		case NAT_INBOUND :
4244255332Scy			if (nat->nat_v[0] != 4)
4245255332Scy				continue;
4246255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4247255332Scy			    nat->nat_odstaddr != dst)
4248255332Scy				continue;
4249255332Scy			break;
4250255332Scy		case NAT_OUTBOUND :
4251255332Scy			if (nat->nat_v[1] != 4)
4252255332Scy				continue;
4253255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4254255332Scy			    nat->nat_nsrcaddr != dst)
4255255332Scy				continue;
4256255332Scy			break;
4257255332Scy		}
4258255332Scy
4259145522Sdarrenr		nflags = nat->nat_flags;
4260145522Sdarrenr		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
4261145522Sdarrenr			continue;
4262145522Sdarrenr
4263255332Scy		if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags,
4264255332Scy				   NAT_INBOUND) == 1) {
4265145522Sdarrenr			if ((fin->fin_flx & FI_IGNORE) != 0)
4266145522Sdarrenr				break;
4267145522Sdarrenr			if ((nflags & SI_CLONE) != 0) {
4268255332Scy				nat = ipf_nat_clone(fin, nat);
4269145522Sdarrenr				if (nat == NULL)
4270145522Sdarrenr					break;
4271145522Sdarrenr			} else {
4272255332Scy				MUTEX_ENTER(&softn->ipf_nat_new);
4273255332Scy				softn->ipf_nat_stats.ns_wilds--;
4274255332Scy				MUTEX_EXIT(&softn->ipf_nat_new);
4275145522Sdarrenr			}
4276255332Scy
4277255332Scy			if (nat->nat_dir == NAT_INBOUND) {
4278255332Scy				if (nat->nat_osport == 0) {
4279255332Scy					nat->nat_osport = sport;
4280255332Scy					nat->nat_nsport = sport;
4281255332Scy				}
4282255332Scy				if (nat->nat_odport == 0) {
4283255332Scy					nat->nat_odport = dport;
4284255332Scy					nat->nat_ndport = dport;
4285255332Scy				}
4286255332Scy			} else if (nat->nat_dir == NAT_OUTBOUND) {
4287255332Scy				if (nat->nat_osport == 0) {
4288255332Scy					nat->nat_osport = dport;
4289255332Scy					nat->nat_nsport = dport;
4290255332Scy				}
4291255332Scy				if (nat->nat_odport == 0) {
4292255332Scy					nat->nat_odport = sport;
4293255332Scy					nat->nat_ndport = sport;
4294255332Scy				}
4295255332Scy			}
4296255332Scy			if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
4297255332Scy				nat->nat_ifps[0] = ifp;
4298255332Scy				nat->nat_mtu[0] = GETIFMTU_4(ifp);
4299255332Scy			}
4300145522Sdarrenr			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
4301255332Scy			ipf_nat_tabmove(softn, nat);
430267614Sdarrenr			break;
430367614Sdarrenr		}
430467614Sdarrenr	}
4305145522Sdarrenr
4306255332Scy	MUTEX_DOWNGRADE(&softc->ipf_nat);
4307145522Sdarrenr
4308255332Scy	if (nat == NULL) {
4309255332Scy		NBUMPSIDE(0, ns_lookup_miss);
4310255332Scy	}
431167614Sdarrenr	return nat;
431253642Sguido}
431353642Sguido
431453642Sguido
4315145522Sdarrenr/* ------------------------------------------------------------------------ */
4316255332Scy/* Function:    ipf_nat_tabmove                                             */
4317145522Sdarrenr/* Returns:     Nil                                                         */
4318255332Scy/* Parameters:  softn(I) - pointer to NAT context structure                 */
4319255332Scy/*              nat(I)   - pointer to NAT structure                         */
4320145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
4321145522Sdarrenr/*                                                                          */
4322145522Sdarrenr/* This function is only called for TCP/UDP NAT table entries where the     */
4323145522Sdarrenr/* original was placed in the table without hashing on the ports and we now */
4324145522Sdarrenr/* want to include hashing on port numbers.                                 */
4325145522Sdarrenr/* ------------------------------------------------------------------------ */
4326255332Scystatic void
4327255332Scyipf_nat_tabmove(softn, nat)
4328255332Scy	ipf_nat_softc_t *softn;
4329255332Scy	nat_t *nat;
433067614Sdarrenr{
4331255332Scy	u_int hv0, hv1, rhv0, rhv1;
4332255332Scy	natstat_t *nsp;
433367614Sdarrenr	nat_t **natp;
433467614Sdarrenr
4335145522Sdarrenr	if (nat->nat_flags & SI_CLONE)
4336145522Sdarrenr		return;
433772006Sdarrenr
4338255332Scy	nsp = &softn->ipf_nat_stats;
433967614Sdarrenr	/*
434067614Sdarrenr	 * Remove the NAT entry from the old location
434167614Sdarrenr	 */
434267614Sdarrenr	if (nat->nat_hnext[0])
434367614Sdarrenr		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
434467614Sdarrenr	*nat->nat_phnext[0] = nat->nat_hnext[0];
4345255332Scy	nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] %
4346255332Scy				     softn->ipf_nat_table_sz]--;
434767614Sdarrenr
434867614Sdarrenr	if (nat->nat_hnext[1])
434967853Sdarrenr		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
435067614Sdarrenr	*nat->nat_phnext[1] = nat->nat_hnext[1];
4351255332Scy	nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] %
4352255332Scy				     softn->ipf_nat_table_sz]--;
435367614Sdarrenr
435467853Sdarrenr	/*
435567853Sdarrenr	 * Add into the NAT table in the new position
435667853Sdarrenr	 */
4357255332Scy	rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff);
4358255332Scy	rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport,
4359255332Scy			   0xffffffff);
4360255332Scy	rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff);
4361255332Scy	rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport,
4362255332Scy			   0xffffffff);
4363255332Scy
4364255332Scy	hv0 = rhv0 % softn->ipf_nat_table_sz;
4365255332Scy	hv1 = rhv1 % softn->ipf_nat_table_sz;
4366255332Scy
4367255332Scy	if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) {
4368255332Scy		u_int swap;
4369255332Scy
4370255332Scy		swap = hv0;
4371255332Scy		hv0 = hv1;
4372255332Scy		hv1 = swap;
4373255332Scy	}
4374255332Scy
4375255332Scy	/* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */
4376255332Scy	/* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */
4377255332Scy
4378255332Scy	nat->nat_hv[0] = rhv0;
4379255332Scy	natp = &softn->ipf_nat_table[0][hv0];
438067614Sdarrenr	if (*natp)
438167614Sdarrenr		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
438267614Sdarrenr	nat->nat_phnext[0] = natp;
438367614Sdarrenr	nat->nat_hnext[0] = *natp;
438467614Sdarrenr	*natp = nat;
4385255332Scy	nsp->ns_side[0].ns_bucketlen[hv0]++;
438667614Sdarrenr
4387255332Scy	nat->nat_hv[1] = rhv1;
4388255332Scy	natp = &softn->ipf_nat_table[1][hv1];
438967614Sdarrenr	if (*natp)
439067614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
439167614Sdarrenr	nat->nat_phnext[1] = natp;
439267614Sdarrenr	nat->nat_hnext[1] = *natp;
439367614Sdarrenr	*natp = nat;
4394255332Scy	nsp->ns_side[1].ns_bucketlen[hv1]++;
439567614Sdarrenr}
439667614Sdarrenr
439767614Sdarrenr
4398145522Sdarrenr/* ------------------------------------------------------------------------ */
4399255332Scy/* Function:    ipf_nat_outlookup                                           */
4400145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
4401145522Sdarrenr/*                       else pointer to matching NAT entry                 */
4402145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4403145522Sdarrenr/*              flags(I) - NAT flags for this packet                        */
4404145522Sdarrenr/*              p(I)     - protocol for this packet                         */
4405145522Sdarrenr/*              src(I)   - source IP address                                */
4406145522Sdarrenr/*              dst(I)   - destination IP address                           */
4407255332Scy/*              rw(I)    - 1 == write lock on  held, 0 == read lock.        */
4408145522Sdarrenr/*                                                                          */
4409145522Sdarrenr/* Lookup a nat entry based on the source 'real' ip address/port and        */
4410145522Sdarrenr/* destination address/port.  We use this lookup when sending a packet out, */
4411145522Sdarrenr/* we're looking for a table entry, based on the source address.            */
4412145522Sdarrenr/*                                                                          */
4413145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
4414145522Sdarrenr/*                                                                          */
4415255332Scy/* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
4416145522Sdarrenr/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
4417145522Sdarrenr/*                                                                          */
4418145522Sdarrenr/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
4419145522Sdarrenr/*            the packet is of said protocol                                */
4420145522Sdarrenr/* ------------------------------------------------------------------------ */
4421255332Scynat_t *
4422255332Scyipf_nat_outlookup(fin, flags, p, src, dst)
4423255332Scy	fr_info_t *fin;
4424255332Scy	u_int flags, p;
4425255332Scy	struct in_addr src , dst;
442653642Sguido{
4427255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
4428255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
4429145522Sdarrenr	u_short sport, dport;
4430145522Sdarrenr	u_int sflags;
443192685Sdarrenr	ipnat_t *ipn;
4432145522Sdarrenr	nat_t *nat;
443392685Sdarrenr	void *ifp;
443453642Sguido	u_int hv;
443553642Sguido
443692685Sdarrenr	ifp = fin->fin_ifp;
4437145522Sdarrenr	sflags = flags & IPN_TCPUDPICMP;
4438145522Sdarrenr	sport = 0;
4439145522Sdarrenr	dport = 0;
4440145522Sdarrenr
4441145522Sdarrenr	switch (p)
4442145522Sdarrenr	{
4443145522Sdarrenr	case IPPROTO_TCP :
4444145522Sdarrenr	case IPPROTO_UDP :
4445145522Sdarrenr		sport = htons(fin->fin_data[0]);
4446145522Sdarrenr		dport = htons(fin->fin_data[1]);
4447145522Sdarrenr		break;
4448145522Sdarrenr	case IPPROTO_ICMP :
4449145522Sdarrenr		if (flags & IPN_ICMPERR)
4450145522Sdarrenr			sport = fin->fin_data[1];
4451145522Sdarrenr		else
4452145522Sdarrenr			dport = fin->fin_data[1];
4453145522Sdarrenr		break;
4454145522Sdarrenr	default :
4455145522Sdarrenr		break;
445692685Sdarrenr	}
445753642Sguido
4458145522Sdarrenr	if ((flags & SI_WILDP) != 0)
4459145522Sdarrenr		goto find_out_wild_ports;
4460145522Sdarrenr
4461255332Scy	hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff);
4462255332Scy	hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz);
4463255332Scy	nat = softn->ipf_nat_table[0][hv];
4464255332Scy
4465255332Scy	/* TRACE src, sport, dst, dport, hv, nat */
4466255332Scy
446753642Sguido	for (; nat; nat = nat->nat_hnext[0]) {
4468161356Sguido		if (nat->nat_ifps[1] != NULL) {
4469161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
4470161356Sguido				continue;
4471255332Scy		}
4472161356Sguido
4473255332Scy		if (nat->nat_pr[1] != p)
4474255332Scy			continue;
447553642Sguido
4476255332Scy		switch (nat->nat_dir)
4477255332Scy		{
4478255332Scy		case NAT_INBOUND :
4479255332Scy		case NAT_DIVERTIN :
4480255332Scy			if (nat->nat_v[1] != 4)
4481255332Scy				continue;
4482255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4483255332Scy			    nat->nat_nsrcaddr != dst.s_addr)
4484255332Scy				continue;
4485255332Scy
4486255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4487255332Scy				if (nat->nat_ndport != sport)
4488145522Sdarrenr					continue;
4489255332Scy				if (nat->nat_nsport != dport)
449092685Sdarrenr					continue;
4491255332Scy
4492255332Scy			} else if (p == IPPROTO_ICMP) {
4493255332Scy				if (nat->nat_osport != dport) {
449492685Sdarrenr					continue;
4495255332Scy				}
449692685Sdarrenr			}
4497255332Scy			break;
4498255332Scy		case NAT_OUTBOUND :
4499255332Scy		case NAT_DIVERTOUT :
4500255332Scy			if (nat->nat_v[0] != 4)
4501255332Scy				continue;
4502255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4503255332Scy			    nat->nat_odstaddr != dst.s_addr)
4504255332Scy				continue;
450592685Sdarrenr
4506255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4507255332Scy				if (nat->nat_odport != dport)
450892685Sdarrenr					continue;
4509255332Scy				if (nat->nat_osport != sport)
4510255332Scy					continue;
4511255332Scy
4512255332Scy			} else if (p == IPPROTO_ICMP) {
4513255332Scy				if (nat->nat_osport != dport) {
4514255332Scy					continue;
4515255332Scy				}
4516255332Scy			}
4517255332Scy			break;
451892685Sdarrenr		}
4519255332Scy
4520255332Scy		ipn = nat->nat_ptr;
4521255332Scy		if ((ipn != NULL) && (nat->nat_aps != NULL))
4522255332Scy			if (ipf_proxy_match(fin, nat) != 0)
4523255332Scy				continue;
4524255332Scy
4525255332Scy		if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
4526255332Scy			nat->nat_ifps[1] = ifp;
4527255332Scy			nat->nat_mtu[1] = GETIFMTU_4(ifp);
4528255332Scy		}
4529255332Scy		return nat;
453053642Sguido	}
4531145522Sdarrenr
4532145522Sdarrenr	/*
4533145522Sdarrenr	 * So if we didn't find it but there are wildcard members in the hash
4534145522Sdarrenr	 * table, go back and look for them.  We do this search and update here
4535145522Sdarrenr	 * because it is modifying the NAT table and we want to do this only
4536145522Sdarrenr	 * for the first packet that matches.  The exception, of course, is
4537145522Sdarrenr	 * for "dummy" (FI_IGNORE) lookups.
4538145522Sdarrenr	 */
4539145522Sdarrenrfind_out_wild_ports:
4540255332Scy	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
4541255332Scy		NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1);
454267614Sdarrenr		return NULL;
4543255332Scy	}
4544255332Scy	if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
4545255332Scy		NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1);
4546145522Sdarrenr		return NULL;
4547255332Scy	}
454892685Sdarrenr
4549255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
4550145522Sdarrenr
4551255332Scy	hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff);
4552255332Scy	hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz);
4553145522Sdarrenr
4554255332Scy	WRITE_ENTER(&softc->ipf_nat);
4555145522Sdarrenr
4556255332Scy	nat = softn->ipf_nat_table[0][hv];
455767614Sdarrenr	for (; nat; nat = nat->nat_hnext[0]) {
4558161356Sguido		if (nat->nat_ifps[1] != NULL) {
4559161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
4560161356Sguido				continue;
4561255332Scy		}
4562145522Sdarrenr
4563255332Scy		if (nat->nat_pr[1] != fin->fin_p)
456467614Sdarrenr			continue;
4565145522Sdarrenr
4566255332Scy		switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))
4567255332Scy		{
4568255332Scy		case NAT_INBOUND :
4569255332Scy			if (nat->nat_v[1] != 4)
4570255332Scy				continue;
4571255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4572255332Scy			    nat->nat_nsrcaddr != dst.s_addr)
4573255332Scy				continue;
4574255332Scy			break;
4575255332Scy		case NAT_OUTBOUND :
4576255332Scy			if (nat->nat_v[0] != 4)
4577255332Scy				continue;
4578255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4579255332Scy			    nat->nat_odstaddr != dst.s_addr)
4580255332Scy				continue;
4581255332Scy			break;
4582255332Scy		}
4583255332Scy
4584255332Scy		if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP)))
4585145522Sdarrenr			continue;
4586145522Sdarrenr
4587255332Scy		if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags,
4588255332Scy				   NAT_OUTBOUND) == 1) {
4589145522Sdarrenr			if ((fin->fin_flx & FI_IGNORE) != 0)
4590145522Sdarrenr				break;
4591255332Scy			if ((nat->nat_flags & SI_CLONE) != 0) {
4592255332Scy				nat = ipf_nat_clone(fin, nat);
4593145522Sdarrenr				if (nat == NULL)
4594145522Sdarrenr					break;
4595145522Sdarrenr			} else {
4596255332Scy				MUTEX_ENTER(&softn->ipf_nat_new);
4597255332Scy				softn->ipf_nat_stats.ns_wilds--;
4598255332Scy				MUTEX_EXIT(&softn->ipf_nat_new);
4599145522Sdarrenr			}
4600255332Scy
4601255332Scy			if (nat->nat_dir == NAT_OUTBOUND) {
4602255332Scy				if (nat->nat_osport == 0) {
4603255332Scy					nat->nat_osport = sport;
4604255332Scy					nat->nat_nsport = sport;
4605255332Scy				}
4606255332Scy				if (nat->nat_odport == 0) {
4607255332Scy					nat->nat_odport = dport;
4608255332Scy					nat->nat_ndport = dport;
4609255332Scy				}
4610255332Scy			} else if (nat->nat_dir == NAT_INBOUND) {
4611255332Scy				if (nat->nat_osport == 0) {
4612255332Scy					nat->nat_osport = dport;
4613255332Scy					nat->nat_nsport = dport;
4614255332Scy				}
4615255332Scy				if (nat->nat_odport == 0) {
4616255332Scy					nat->nat_odport = sport;
4617255332Scy					nat->nat_ndport = sport;
4618255332Scy				}
4619255332Scy			}
4620255332Scy			if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
4621255332Scy				nat->nat_ifps[1] = ifp;
4622255332Scy				nat->nat_mtu[1] = GETIFMTU_4(ifp);
4623255332Scy			}
4624145522Sdarrenr			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
4625255332Scy			ipf_nat_tabmove(softn, nat);
462667614Sdarrenr			break;
462767614Sdarrenr		}
462867614Sdarrenr	}
4629145522Sdarrenr
4630255332Scy	MUTEX_DOWNGRADE(&softc->ipf_nat);
4631145522Sdarrenr
4632255332Scy	if (nat == NULL) {
4633255332Scy		NBUMPSIDE(1, ns_lookup_miss);
4634255332Scy	}
463567614Sdarrenr	return nat;
463653642Sguido}
463753642Sguido
463853642Sguido
4639145522Sdarrenr/* ------------------------------------------------------------------------ */
4640255332Scy/* Function:    ipf_nat_lookupredir                                         */
4641145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
4642145522Sdarrenr/*                       else pointer to matching NAT entry                 */
4643145522Sdarrenr/* Parameters:  np(I) - pointer to description of packet to find NAT table  */
4644145522Sdarrenr/*                      entry for.                                          */
4645145522Sdarrenr/*                                                                          */
4646145522Sdarrenr/* Lookup the NAT tables to search for a matching redirect                  */
4647161356Sguido/* The contents of natlookup_t should imitate those found in a packet that  */
4648161356Sguido/* would be translated - ie a packet coming in for RDR or going out for MAP.*/
4649161356Sguido/* We can do the lookup in one of two ways, imitating an inbound or         */
4650161356Sguido/* outbound  packet.  By default we assume outbound, unless IPN_IN is set.  */
4651161356Sguido/* For IN, the fields are set as follows:                                   */
4652161356Sguido/*     nl_real* = source information                                        */
4653161356Sguido/*     nl_out* = destination information (translated)                       */
4654161356Sguido/* For an out packet, the fields are set like this:                         */
4655161356Sguido/*     nl_in* = source information (untranslated)                           */
4656161356Sguido/*     nl_out* = destination information (translated)                       */
4657145522Sdarrenr/* ------------------------------------------------------------------------ */
4658255332Scynat_t *
4659255332Scyipf_nat_lookupredir(np)
4660255332Scy	natlookup_t *np;
466153642Sguido{
4662145522Sdarrenr	fr_info_t fi;
466353642Sguido	nat_t *nat;
466453642Sguido
466592685Sdarrenr	bzero((char *)&fi, sizeof(fi));
4666145522Sdarrenr	if (np->nl_flags & IPN_IN) {
4667145522Sdarrenr		fi.fin_data[0] = ntohs(np->nl_realport);
4668145522Sdarrenr		fi.fin_data[1] = ntohs(np->nl_outport);
4669145522Sdarrenr	} else {
4670145522Sdarrenr		fi.fin_data[0] = ntohs(np->nl_inport);
4671145522Sdarrenr		fi.fin_data[1] = ntohs(np->nl_outport);
4672145522Sdarrenr	}
4673145522Sdarrenr	if (np->nl_flags & IPN_TCP)
4674145522Sdarrenr		fi.fin_p = IPPROTO_TCP;
4675145522Sdarrenr	else if (np->nl_flags & IPN_UDP)
4676145522Sdarrenr		fi.fin_p = IPPROTO_UDP;
4677145522Sdarrenr	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
4678145522Sdarrenr		fi.fin_p = IPPROTO_ICMP;
467992685Sdarrenr
468053642Sguido	/*
4681145522Sdarrenr	 * We can do two sorts of lookups:
4682145522Sdarrenr	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
4683145522Sdarrenr	 * - default: we have the `in' and `out' address, look for `real'.
468453642Sguido	 */
4685145522Sdarrenr	if (np->nl_flags & IPN_IN) {
4686255332Scy		if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p,
4687255332Scy					    np->nl_realip, np->nl_outip))) {
4688255332Scy			np->nl_inip = nat->nat_odstip;
4689255332Scy			np->nl_inport = nat->nat_odport;
4690145522Sdarrenr		}
4691145522Sdarrenr	} else {
4692145522Sdarrenr		/*
4693145522Sdarrenr		 * If nl_inip is non null, this is a lookup based on the real
4694145522Sdarrenr		 * ip address. Else, we use the fake.
4695145522Sdarrenr		 */
4696255332Scy		if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p,
4697145522Sdarrenr					 np->nl_inip, np->nl_outip))) {
4698145522Sdarrenr
4699145522Sdarrenr			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
4700145522Sdarrenr				fr_info_t fin;
4701145522Sdarrenr				bzero((char *)&fin, sizeof(fin));
4702255332Scy				fin.fin_p = nat->nat_pr[0];
4703255332Scy				fin.fin_data[0] = ntohs(nat->nat_ndport);
4704255332Scy				fin.fin_data[1] = ntohs(nat->nat_nsport);
4705255332Scy				if (ipf_nat_inlookup(&fin, np->nl_flags,
4706255332Scy						     fin.fin_p, nat->nat_ndstip,
4707255332Scy						     nat->nat_nsrcip) != NULL) {
4708145522Sdarrenr					np->nl_flags &= ~IPN_FINDFORWARD;
4709145522Sdarrenr				}
4710145522Sdarrenr			}
4711145522Sdarrenr
4712255332Scy			np->nl_realip = nat->nat_ndstip;
4713255332Scy			np->nl_realport = nat->nat_ndport;
4714145522Sdarrenr		}
4715145522Sdarrenr 	}
4716145522Sdarrenr
471753642Sguido	return nat;
471853642Sguido}
471953642Sguido
472053642Sguido
4721145522Sdarrenr/* ------------------------------------------------------------------------ */
4722255332Scy/* Function:    ipf_nat_match                                               */
4723145522Sdarrenr/* Returns:     int - 0 == no match, 1 == match                             */
4724145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4725145522Sdarrenr/*              np(I)    - pointer to NAT rule                              */
4726145522Sdarrenr/*                                                                          */
4727145522Sdarrenr/* Pull the matching of a packet against a NAT rule out of that complex     */
4728255332Scy/* loop inside ipf_nat_checkin() and lay it out properly in its own function. */
4729145522Sdarrenr/* ------------------------------------------------------------------------ */
4730255332Scystatic int
4731255332Scyipf_nat_match(fin, np)
4732255332Scy	fr_info_t *fin;
4733255332Scy	ipnat_t *np;
473460852Sdarrenr{
4735255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
473660852Sdarrenr	frtuc_t *ft;
4737255332Scy	int match;
473860852Sdarrenr
4739255332Scy	match = 0;
4740255332Scy	switch (np->in_osrcatype)
4741255332Scy	{
4742255332Scy	case FRI_NORMAL :
4743255332Scy		match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr);
4744255332Scy		break;
4745255332Scy	case FRI_LOOKUP :
4746255332Scy		match = (*np->in_osrcfunc)(softc, np->in_osrcptr,
4747255332Scy					   4, &fin->fin_saddr, fin->fin_plen);
4748255332Scy		break;
4749255332Scy	}
4750255332Scy	match ^= ((np->in_flags & IPN_NOTSRC) != 0);
4751255332Scy	if (match)
475260852Sdarrenr		return 0;
475360852Sdarrenr
4754255332Scy	match = 0;
4755255332Scy	switch (np->in_odstatype)
4756255332Scy	{
4757255332Scy	case FRI_NORMAL :
4758255332Scy		match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr);
4759255332Scy		break;
4760255332Scy	case FRI_LOOKUP :
4761255332Scy		match = (*np->in_odstfunc)(softc, np->in_odstptr,
4762255332Scy					   4, &fin->fin_daddr, fin->fin_plen);
4763255332Scy		break;
4764255332Scy	}
4765255332Scy
4766255332Scy	match ^= ((np->in_flags & IPN_NOTDST) != 0);
4767255332Scy	if (match)
476860852Sdarrenr		return 0;
4769145522Sdarrenr
477060852Sdarrenr	ft = &np->in_tuc;
4771145522Sdarrenr	if (!(fin->fin_flx & FI_TCPUDP) ||
4772145522Sdarrenr	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
477360852Sdarrenr		if (ft->ftu_scmp || ft->ftu_dcmp)
477460852Sdarrenr			return 0;
477560852Sdarrenr		return 1;
477660852Sdarrenr	}
477760852Sdarrenr
4778255332Scy	return ipf_tcpudpchk(&fin->fin_fi, ft);
477960852Sdarrenr}
478060852Sdarrenr
478160852Sdarrenr
4782145522Sdarrenr/* ------------------------------------------------------------------------ */
4783255332Scy/* Function:    ipf_nat_update                                              */
4784145522Sdarrenr/* Returns:     Nil                                                         */
4785255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
4786255332Scy/*              nat(I) - pointer to NAT structure                           */
4787145522Sdarrenr/*                                                                          */
4788145522Sdarrenr/* Updates the lifetime of a NAT table entry for non-TCP packets.  Must be  */
4789255332Scy/* called with fin_rev updated - i.e. after calling ipf_nat_proto().        */
4790255332Scy/*                                                                          */
4791255332Scy/* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to     */
4792255332Scy/* already be set.                                                          */
4793145522Sdarrenr/* ------------------------------------------------------------------------ */
4794255332Scyvoid
4795255332Scyipf_nat_update(fin, nat)
4796255332Scy	fr_info_t *fin;
4797255332Scy	nat_t *nat;
479853642Sguido{
4799255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
4800255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
4801145522Sdarrenr	ipftq_t *ifq, *ifq2;
4802145522Sdarrenr	ipftqent_t *tqe;
4803255332Scy	ipnat_t *np = nat->nat_ptr;
4804145522Sdarrenr
4805145522Sdarrenr	tqe = &nat->nat_tqe;
4806145522Sdarrenr	ifq = tqe->tqe_ifq;
4807145522Sdarrenr
4808145522Sdarrenr	/*
4809145522Sdarrenr	 * We allow over-riding of NAT timeouts from NAT rules, even for
4810145522Sdarrenr	 * TCP, however, if it is TCP and there is no rule timeout set,
4811145522Sdarrenr	 * then do not update the timeout here.
4812145522Sdarrenr	 */
4813255332Scy	if (np != NULL) {
4814255332Scy		np->in_bytes[fin->fin_rev] += fin->fin_plen;
4815145522Sdarrenr		ifq2 = np->in_tqehead[fin->fin_rev];
4816255332Scy	} else {
4817145522Sdarrenr		ifq2 = NULL;
4818255332Scy	}
4819145522Sdarrenr
4820255332Scy	if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) {
4821255332Scy		(void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq,
4822255332Scy				   0, 2);
4823145522Sdarrenr	} else {
4824145522Sdarrenr		if (ifq2 == NULL) {
4825255332Scy			if (nat->nat_pr[0] == IPPROTO_UDP)
4826255332Scy				ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq :
4827255332Scy						      &softn->ipf_nat_udptq;
4828255332Scy			else if (nat->nat_pr[0] == IPPROTO_ICMP ||
4829255332Scy				 nat->nat_pr[0] == IPPROTO_ICMPV6)
4830255332Scy				ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq:
4831255332Scy						      &softn->ipf_nat_icmptq;
4832145522Sdarrenr			else
4833255332Scy				ifq2 = &softn->ipf_nat_iptq;
4834145522Sdarrenr		}
4835145522Sdarrenr
4836255332Scy		ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2);
4837145522Sdarrenr	}
4838145522Sdarrenr}
4839145522Sdarrenr
4840145522Sdarrenr
4841145522Sdarrenr/* ------------------------------------------------------------------------ */
4842255332Scy/* Function:    ipf_nat_checkout                                            */
4843145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
4844145522Sdarrenr/*                     0 == no packet translation occurred,                 */
4845145522Sdarrenr/*                     1 == packet was successfully translated.             */
4846145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4847145522Sdarrenr/*              passp(I) - pointer to filtering result flags                */
4848145522Sdarrenr/*                                                                          */
4849145522Sdarrenr/* Check to see if an outcoming packet should be changed.  ICMP packets are */
4850145522Sdarrenr/* first checked to see if they match an existing entry (if an error),      */
4851145522Sdarrenr/* otherwise a search of the current NAT table is made.  If neither results */
4852145522Sdarrenr/* in a match then a search for a matching NAT rule is made.  Create a new  */
4853145522Sdarrenr/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
4854145522Sdarrenr/* packet header(s) as required.                                            */
4855145522Sdarrenr/* ------------------------------------------------------------------------ */
4856255332Scyint
4857255332Scyipf_nat_checkout(fin, passp)
4858255332Scy	fr_info_t *fin;
4859255332Scy	u_32_t *passp;
4860145522Sdarrenr{
4861255332Scy	ipnat_t *np = NULL, *npnext;
4862145522Sdarrenr	struct ifnet *ifp, *sifp;
4863255332Scy	ipf_main_softc_t *softc;
4864255332Scy	ipf_nat_softc_t *softn;
4865145522Sdarrenr	icmphdr_t *icmp = NULL;
486653642Sguido	tcphdr_t *tcp = NULL;
4867145522Sdarrenr	int rval, natfailed;
4868145522Sdarrenr	u_int nflags = 0;
4869145522Sdarrenr	u_32_t ipa, iph;
4870145522Sdarrenr	int natadd = 1;
487153642Sguido	frentry_t *fr;
487253642Sguido	nat_t *nat;
487353642Sguido
4874255332Scy	if (fin->fin_v == 6) {
4875255332Scy#ifdef USE_INET6
4876255332Scy		return ipf_nat6_checkout(fin, passp);
4877255332Scy#else
487853642Sguido		return 0;
4879255332Scy#endif
4880255332Scy	}
488153642Sguido
4882255332Scy	softc = fin->fin_main_soft;
4883255332Scy	softn = softc->ipf_nat_soft;
4884255332Scy
4885255332Scy	if (softn->ipf_nat_lock != 0)
4886255332Scy		return 0;
4887255332Scy	if (softn->ipf_nat_stats.ns_rules == 0 &&
4888255332Scy	    softn->ipf_nat_instances == NULL)
4889255332Scy		return 0;
4890255332Scy
4891145522Sdarrenr	natfailed = 0;
4892145522Sdarrenr	fr = fin->fin_fr;
4893145522Sdarrenr	sifp = fin->fin_ifp;
4894170268Sdarrenr	if (fr != NULL) {
4895255332Scy		ifp = fr->fr_tifs[fin->fin_rev].fd_ptr;
4896170268Sdarrenr		if ((ifp != NULL) && (ifp != (void *)-1))
4897170268Sdarrenr			fin->fin_ifp = ifp;
4898170268Sdarrenr	}
489992685Sdarrenr	ifp = fin->fin_ifp;
490053642Sguido
4901145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
4902145522Sdarrenr		switch (fin->fin_p)
4903145522Sdarrenr		{
4904145522Sdarrenr		case IPPROTO_TCP :
490553642Sguido			nflags = IPN_TCP;
4906145522Sdarrenr			break;
4907145522Sdarrenr		case IPPROTO_UDP :
490853642Sguido			nflags = IPN_UDP;
4909145522Sdarrenr			break;
4910145522Sdarrenr		case IPPROTO_ICMP :
4911145522Sdarrenr			icmp = fin->fin_dp;
4912145522Sdarrenr
4913145522Sdarrenr			/*
4914145522Sdarrenr			 * This is an incoming packet, so the destination is
4915145522Sdarrenr			 * the icmp_id and the source port equals 0
4916145522Sdarrenr			 */
4917255332Scy			if ((fin->fin_flx & FI_ICMPQUERY) != 0)
4918145522Sdarrenr				nflags = IPN_ICMPQUERY;
4919145522Sdarrenr			break;
4920145522Sdarrenr		default :
4921145522Sdarrenr			break;
492253642Sguido		}
4923255332Scy
4924145522Sdarrenr		if ((nflags & IPN_TCPUDP))
4925145522Sdarrenr			tcp = fin->fin_dp;
492653642Sguido	}
492753642Sguido
492892685Sdarrenr	ipa = fin->fin_saddr;
492953642Sguido
4930255332Scy	READ_ENTER(&softc->ipf_nat);
493160852Sdarrenr
4932255332Scy	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
4933255332Scy	    (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND)))
4934145522Sdarrenr		/*EMPTY*/;
4935255332Scy	else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
493653642Sguido		natadd = 0;
4937255332Scy	else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH,
4938255332Scy				      (u_int)fin->fin_p, fin->fin_src,
4939255332Scy				      fin->fin_dst))) {
494053642Sguido		nflags = nat->nat_flags;
4941255332Scy	} else if (fin->fin_off == 0) {
4942255332Scy		u_32_t hv, msk, nmsk = 0;
494392685Sdarrenr
494453642Sguido		/*
494553642Sguido		 * If there is no current entry in the nat table for this IP#,
494653642Sguido		 * create one for it (if there is a matching rule).
494753642Sguido		 */
494853642Sguidomaskloop:
4949255332Scy		msk = softn->ipf_nat_map_active_masks[nmsk];
4950255332Scy		iph = ipa & msk;
4951255332Scy		hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz);
4952255332Scyretry_roundrobin:
4953255332Scy		for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) {
4954255332Scy			npnext = np->in_mnext;
4955161356Sguido			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
495660852Sdarrenr				continue;
4957255332Scy			if (np->in_v[0] != 4)
495860852Sdarrenr				continue;
4959255332Scy			if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p))
4960145522Sdarrenr				continue;
4961255332Scy			if ((np->in_flags & IPN_RF) &&
4962255332Scy			    !(np->in_flags & nflags))
4963145522Sdarrenr				continue;
496460852Sdarrenr			if (np->in_flags & IPN_FILTER) {
4965255332Scy				switch (ipf_nat_match(fin, np))
4966255332Scy				{
4967255332Scy				case 0 :
496860852Sdarrenr					continue;
4969255332Scy				case -1 :
4970255332Scy					rval = -1;
4971255332Scy					goto outmatchfail;
4972255332Scy				case 1 :
4973255332Scy				default :
4974255332Scy					break;
4975255332Scy				}
4976255332Scy			} else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr)
497760852Sdarrenr				continue;
4978145522Sdarrenr
4979145522Sdarrenr			if ((fr != NULL) &&
4980255332Scy			    !ipf_matchtag(&np->in_tag, &fr->fr_nattag))
498192685Sdarrenr				continue;
4982145522Sdarrenr
4983255332Scy			if (np->in_plabel != -1) {
4984145522Sdarrenr				if (((np->in_flags & IPN_FILTER) == 0) &&
4985255332Scy				    (np->in_odport != fin->fin_data[1]))
4986145522Sdarrenr					continue;
4987255332Scy				if (ipf_proxy_ok(fin, tcp, np) == 0)
4988145522Sdarrenr					continue;
4989145522Sdarrenr			}
4990145522Sdarrenr
4991255332Scy			if (np->in_flags & IPN_NO) {
499292685Sdarrenr				np->in_hits++;
499392685Sdarrenr				break;
4994145522Sdarrenr			}
4995255332Scy			MUTEX_ENTER(&softn->ipf_nat_new);
4996255332Scy			/*
4997255332Scy			 * If we've matched a round-robin rule but it has
4998255332Scy			 * moved in the list since we got it, start over as
4999255332Scy			 * this is now no longer correct.
5000255332Scy			 */
5001255332Scy			if (npnext != np->in_mnext) {
5002255332Scy				if ((np->in_flags & IPN_ROUNDR) != 0) {
5003255332Scy					MUTEX_EXIT(&softn->ipf_nat_new);
5004255332Scy					goto retry_roundrobin;
5005255332Scy				}
5006255332Scy				npnext = np->in_mnext;
5007145522Sdarrenr			}
5008255332Scy
5009255332Scy			nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND);
5010255332Scy			MUTEX_EXIT(&softn->ipf_nat_new);
5011255332Scy			if (nat != NULL) {
5012255332Scy				natfailed = 0;
5013255332Scy				break;
5014255332Scy			}
5015255332Scy			natfailed = -1;
501653642Sguido		}
5017255332Scy		if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) {
5018255332Scy			nmsk++;
5019255332Scy			goto maskloop;
5020255332Scy		}
502153642Sguido	}
502253642Sguido
5023145522Sdarrenr	if (nat != NULL) {
5024255332Scy		rval = ipf_nat_out(fin, nat, natadd, nflags);
5025145522Sdarrenr		if (rval == 1) {
5026145522Sdarrenr			MUTEX_ENTER(&nat->nat_lock);
5027255332Scy			ipf_nat_update(fin, nat);
5028255332Scy			nat->nat_bytes[1] += fin->fin_plen;
5029255332Scy			nat->nat_pkts[1]++;
5030255332Scy			fin->fin_pktnum = nat->nat_pkts[1];
5031145522Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
5032145522Sdarrenr		}
5033145522Sdarrenr	} else
5034145522Sdarrenr		rval = natfailed;
5035255332Scyoutmatchfail:
5036255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
5037145522Sdarrenr
5038255332Scy	switch (rval)
5039255332Scy	{
5040255332Scy	case -1 :
5041255332Scy		if (passp != NULL) {
5042255332Scy			DT1(frb_natv4out, fr_info_t *, fin);
5043255332Scy			NBUMPSIDED(1, ns_drop);
5044145522Sdarrenr			*passp = FR_BLOCK;
5045255332Scy			fin->fin_reason = FRB_NATV4;
5046255332Scy		}
5047145522Sdarrenr		fin->fin_flx |= FI_BADNAT;
5048255332Scy		NBUMPSIDED(1, ns_badnat);
5049255332Scy		break;
5050255332Scy	case 0 :
5051255332Scy		NBUMPSIDE(1, ns_ignored);
5052255332Scy		break;
5053255332Scy	case 1 :
5054255332Scy		NBUMPSIDE(1, ns_translated);
5055255332Scy		break;
5056145522Sdarrenr	}
5057145522Sdarrenr	fin->fin_ifp = sifp;
5058145522Sdarrenr	return rval;
5059145522Sdarrenr}
5060145522Sdarrenr
5061145522Sdarrenr/* ------------------------------------------------------------------------ */
5062255332Scy/* Function:    ipf_nat_out                                                 */
5063145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
5064145522Sdarrenr/*                     1 == packet was successfully translated.             */
5065145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
5066145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
5067145522Sdarrenr/*              natadd(I) - flag indicating if it is safe to add frag cache */
5068145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
5069145522Sdarrenr/*                                                                          */
5070145522Sdarrenr/* Translate a packet coming "out" on an interface.                         */
5071145522Sdarrenr/* ------------------------------------------------------------------------ */
5072255332Scyint
5073255332Scyipf_nat_out(fin, nat, natadd, nflags)
5074255332Scy	fr_info_t *fin;
5075255332Scy	nat_t *nat;
5076255332Scy	int natadd;
5077255332Scy	u_32_t nflags;
5078145522Sdarrenr{
5079255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
5080255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5081145522Sdarrenr	icmphdr_t *icmp;
5082145522Sdarrenr	tcphdr_t *tcp;
5083145522Sdarrenr	ipnat_t *np;
5084255332Scy	int skip;
5085145522Sdarrenr	int i;
5086145522Sdarrenr
5087145522Sdarrenr	tcp = NULL;
5088145522Sdarrenr	icmp = NULL;
5089145522Sdarrenr	np = nat->nat_ptr;
5090145522Sdarrenr
5091145522Sdarrenr	if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL))
5092255332Scy		(void) ipf_frag_natnew(softc, fin, 0, nat);
5093145522Sdarrenr
509472006Sdarrenr	/*
5095145522Sdarrenr	 * Fix up checksums, not by recalculating them, but
5096145522Sdarrenr	 * simply computing adjustments.
5097145522Sdarrenr	 * This is only done for STREAMS based IP implementations where the
5098145522Sdarrenr	 * checksum has already been calculated by IP.  In all other cases,
5099145522Sdarrenr	 * IPFilter is called before the checksum needs calculating so there
5100145522Sdarrenr	 * is no call to modify whatever is in the header now.
510172006Sdarrenr	 */
5102255332Scy	if (nflags == IPN_ICMPERR) {
5103255332Scy		u_32_t s1, s2, sumd, msumd;
510463523Sdarrenr
5105255332Scy		s1 = LONG_SUM(ntohl(fin->fin_saddr));
5106255332Scy		if (nat->nat_dir == NAT_OUTBOUND) {
5107255332Scy			s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
5108255332Scy		} else {
5109255332Scy			s2 = LONG_SUM(ntohl(nat->nat_odstaddr));
511063523Sdarrenr		}
5111255332Scy		CALC_SUMD(s1, s2, sumd);
5112255332Scy		msumd = sumd;
5113255332Scy
5114255332Scy		s1 = LONG_SUM(ntohl(fin->fin_daddr));
5115255332Scy		if (nat->nat_dir == NAT_OUTBOUND) {
5116255332Scy			s2 = LONG_SUM(ntohl(nat->nat_ndstaddr));
5117255332Scy		} else {
5118255332Scy			s2 = LONG_SUM(ntohl(nat->nat_osrcaddr));
5119255332Scy		}
5120255332Scy		CALC_SUMD(s1, s2, sumd);
5121255332Scy		msumd += sumd;
5122255332Scy
5123255332Scy		ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0);
5124255332Scy	}
5125153876Sguido#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5126292811Scy    defined(linux) || defined(BRIDGE_IPF) || defined(__FreeBSD__)
5127255332Scy	else {
5128255332Scy		/*
5129255332Scy		 * Strictly speaking, this isn't necessary on BSD
5130255332Scy		 * kernels because they do checksum calculation after
5131255332Scy		 * this code has run BUT if ipfilter is being used
5132255332Scy		 * to do NAT as a bridge, that code doesn't exist.
5133255332Scy		 */
5134255332Scy		switch (nat->nat_dir)
5135255332Scy		{
5136255332Scy		case NAT_OUTBOUND :
5137255332Scy			ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART,
5138255332Scy					 &fin->fin_ip->ip_sum,
5139255332Scy					 nat->nat_ipsumd, 0);
5140255332Scy			break;
5141255332Scy
5142255332Scy		case NAT_INBOUND :
5143255332Scy			ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART,
5144255332Scy					&fin->fin_ip->ip_sum,
5145255332Scy					nat->nat_ipsumd, 0);
5146255332Scy			break;
5147255332Scy
5148255332Scy		default :
5149255332Scy			break;
515063523Sdarrenr		}
5151255332Scy	}
515253642Sguido#endif
5153255332Scy
5154255332Scy	/*
5155255332Scy	 * Address assignment is after the checksum modification because
5156255332Scy	 * we are using the address in the packet for determining the
5157255332Scy	 * correct checksum offset (the ICMP error could be coming from
5158255332Scy	 * anyone...)
5159255332Scy	 */
5160255332Scy	switch (nat->nat_dir)
5161255332Scy	{
5162255332Scy	case NAT_OUTBOUND :
5163255332Scy		fin->fin_ip->ip_src = nat->nat_nsrcip;
5164255332Scy		fin->fin_saddr = nat->nat_nsrcaddr;
5165255332Scy		fin->fin_ip->ip_dst = nat->nat_ndstip;
5166255332Scy		fin->fin_daddr = nat->nat_ndstaddr;
5167255332Scy		break;
5168255332Scy
5169255332Scy	case NAT_INBOUND :
5170255332Scy		fin->fin_ip->ip_src = nat->nat_odstip;
5171255332Scy		fin->fin_saddr = nat->nat_ndstaddr;
5172255332Scy		fin->fin_ip->ip_dst = nat->nat_osrcip;
5173255332Scy		fin->fin_daddr = nat->nat_nsrcaddr;
5174255332Scy		break;
5175255332Scy
5176255332Scy	case NAT_DIVERTIN :
5177255332Scy	    {
5178255332Scy		mb_t *m;
5179255332Scy
5180255332Scy		skip = ipf_nat_decap(fin, nat);
5181255332Scy		if (skip <= 0) {
5182255332Scy			NBUMPSIDED(1, ns_decap_fail);
5183255332Scy			return -1;
5184255332Scy		}
5185255332Scy
5186255332Scy		m = fin->fin_m;
5187255332Scy
5188255332Scy#if defined(MENTAT) && defined(_KERNEL)
5189255332Scy		m->b_rptr += skip;
5190255332Scy#else
5191255332Scy		m->m_data += skip;
5192255332Scy		m->m_len -= skip;
5193255332Scy
5194255332Scy# ifdef M_PKTHDR
5195255332Scy		if (m->m_flags & M_PKTHDR)
5196255332Scy			m->m_pkthdr.len -= skip;
5197255332Scy# endif
5198255332Scy#endif
5199255332Scy
5200255332Scy		MUTEX_ENTER(&nat->nat_lock);
5201255332Scy		ipf_nat_update(fin, nat);
5202255332Scy		MUTEX_EXIT(&nat->nat_lock);
5203255332Scy		fin->fin_flx |= FI_NATED;
5204255332Scy		if (np != NULL && np->in_tag.ipt_num[0] != 0)
5205255332Scy			fin->fin_nattag = &np->in_tag;
5206255332Scy		return 1;
5207255332Scy		/* NOTREACHED */
5208255332Scy	    }
5209255332Scy
5210255332Scy	case NAT_DIVERTOUT :
5211255332Scy	    {
5212255332Scy		u_32_t s1, s2, sumd;
5213255332Scy		udphdr_t *uh;
5214255332Scy		ip_t *ip;
5215255332Scy		mb_t *m;
5216255332Scy
5217255332Scy		m = M_DUP(np->in_divmp);
5218255332Scy		if (m == NULL) {
5219255332Scy			NBUMPSIDED(1, ns_divert_dup);
5220255332Scy			return -1;
5221255332Scy		}
5222255332Scy
5223255332Scy		ip = MTOD(m, ip_t *);
5224255332Scy		ip->ip_id = htons(ipf_nextipid(fin));
5225255332Scy		s2 = ntohs(ip->ip_id);
5226255332Scy
5227255332Scy		s1 = ip->ip_len;
5228255332Scy		ip->ip_len = ntohs(ip->ip_len);
5229255332Scy		ip->ip_len += fin->fin_plen;
5230255332Scy		ip->ip_len = htons(ip->ip_len);
5231255332Scy		s2 += ntohs(ip->ip_len);
5232255332Scy		CALC_SUMD(s1, s2, sumd);
5233255332Scy
5234255332Scy		uh = (udphdr_t *)(ip + 1);
5235255332Scy		uh->uh_ulen += fin->fin_plen;
5236255332Scy		uh->uh_ulen = htons(uh->uh_ulen);
5237255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5238292979Scy    defined(linux) || defined(BRIDGE_IPF) || defined(__FreeBSD__)
5239255332Scy		ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0);
5240255332Scy#endif
5241255332Scy
5242255332Scy		PREP_MB_T(fin, m);
5243255332Scy
5244255332Scy		fin->fin_src = ip->ip_src;
5245255332Scy		fin->fin_dst = ip->ip_dst;
5246255332Scy		fin->fin_ip = ip;
5247255332Scy		fin->fin_plen += sizeof(ip_t) + 8;	/* UDP + IPv4 hdr */
5248255332Scy		fin->fin_dlen += sizeof(ip_t) + 8;	/* UDP + IPv4 hdr */
5249255332Scy
5250255332Scy		nflags &= ~IPN_TCPUDPICMP;
5251255332Scy
5252255332Scy		break;
5253255332Scy	    }
5254255332Scy
5255255332Scy	default :
5256255332Scy		break;
5257145522Sdarrenr	}
525853642Sguido
5259145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
5260255332Scy		u_short *csump;
5261255332Scy
5262255332Scy		if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) {
5263145522Sdarrenr			tcp = fin->fin_dp;
526453642Sguido
5265255332Scy			switch (nat->nat_dir)
5266255332Scy			{
5267255332Scy			case NAT_OUTBOUND :
5268255332Scy				tcp->th_sport = nat->nat_nsport;
5269255332Scy				fin->fin_data[0] = ntohs(nat->nat_nsport);
5270255332Scy				tcp->th_dport = nat->nat_ndport;
5271255332Scy				fin->fin_data[1] = ntohs(nat->nat_ndport);
5272255332Scy				break;
5273255332Scy
5274255332Scy			case NAT_INBOUND :
5275255332Scy				tcp->th_sport = nat->nat_odport;
5276255332Scy				fin->fin_data[0] = ntohs(nat->nat_odport);
5277255332Scy				tcp->th_dport = nat->nat_osport;
5278255332Scy				fin->fin_data[1] = ntohs(nat->nat_osport);
5279255332Scy				break;
5280255332Scy			}
5281145522Sdarrenr		}
528253642Sguido
5283255332Scy		if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) {
5284145522Sdarrenr			icmp = fin->fin_dp;
5285255332Scy			icmp->icmp_id = nat->nat_nicmpid;
5286145522Sdarrenr		}
5287110916Sdarrenr
5288255332Scy		csump = ipf_nat_proto(fin, nat, nflags);
5289255332Scy
5290255332Scy		/*
5291255332Scy		 * The above comments do not hold for layer 4 (or higher)
5292255332Scy		 * checksums...
5293255332Scy		 */
5294255332Scy		if (csump != NULL) {
5295255332Scy			if (nat->nat_dir == NAT_OUTBOUND)
5296255332Scy				ipf_fix_outcksum(fin->fin_cksum, csump,
5297255332Scy						 nat->nat_sumd[0],
5298255332Scy						 nat->nat_sumd[1] +
5299255332Scy						 fin->fin_dlen);
5300255332Scy			else
5301255332Scy				ipf_fix_incksum(fin->fin_cksum, csump,
5302255332Scy						nat->nat_sumd[0],
5303255332Scy						nat->nat_sumd[1] +
5304255332Scy						fin->fin_dlen);
5305255332Scy		}
5306145522Sdarrenr	}
5307110916Sdarrenr
5308255332Scy	ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
5309145522Sdarrenr	/* ------------------------------------------------------------- */
5310255332Scy	/* A few quick notes:                                            */
5311255332Scy	/*      Following are test conditions prior to calling the       */
5312255332Scy	/*      ipf_proxy_check routine.                                 */
5313255332Scy	/*                                                               */
5314255332Scy	/*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
5315255332Scy	/*      with a redirect rule, we attempt to match the packet's   */
5316255332Scy	/*      source port against in_dport, otherwise we'd compare the */
5317255332Scy	/*      packet's destination.                                    */
5318145522Sdarrenr	/* ------------------------------------------------------------- */
5319145522Sdarrenr	if ((np != NULL) && (np->in_apr != NULL)) {
5320255332Scy		i = ipf_proxy_check(fin, nat);
5321255332Scy		if (i == 0) {
532260852Sdarrenr			i = 1;
5323255332Scy		} else if (i == -1) {
5324255332Scy			NBUMPSIDED(1, ns_ipf_proxy_fail);
5325255332Scy		}
5326255332Scy	} else {
5327145522Sdarrenr		i = 1;
5328255332Scy	}
5329145522Sdarrenr	fin->fin_flx |= FI_NATED;
5330145522Sdarrenr	return i;
533153642Sguido}
533253642Sguido
533353642Sguido
5334145522Sdarrenr/* ------------------------------------------------------------------------ */
5335255332Scy/* Function:    ipf_nat_checkin                                             */
5336145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
5337145522Sdarrenr/*                     0 == no packet translation occurred,                 */
5338145522Sdarrenr/*                     1 == packet was successfully translated.             */
5339145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
5340145522Sdarrenr/*              passp(I) - pointer to filtering result flags                */
5341145522Sdarrenr/*                                                                          */
5342145522Sdarrenr/* Check to see if an incoming packet should be changed.  ICMP packets are  */
5343145522Sdarrenr/* first checked to see if they match an existing entry (if an error),      */
5344145522Sdarrenr/* otherwise a search of the current NAT table is made.  If neither results */
5345145522Sdarrenr/* in a match then a search for a matching NAT rule is made.  Create a new  */
5346145522Sdarrenr/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
5347145522Sdarrenr/* packet header(s) as required.                                            */
5348145522Sdarrenr/* ------------------------------------------------------------------------ */
5349255332Scyint
5350255332Scyipf_nat_checkin(fin, passp)
5351255332Scy	fr_info_t *fin;
5352255332Scy	u_32_t *passp;
535353642Sguido{
5354255332Scy	ipf_main_softc_t *softc;
5355255332Scy	ipf_nat_softc_t *softn;
5356145522Sdarrenr	u_int nflags, natadd;
5357255332Scy	ipnat_t *np, *npnext;
5358145522Sdarrenr	int rval, natfailed;
5359145522Sdarrenr	struct ifnet *ifp;
5360145522Sdarrenr	struct in_addr in;
5361145522Sdarrenr	icmphdr_t *icmp;
5362145522Sdarrenr	tcphdr_t *tcp;
5363145522Sdarrenr	u_short dport;
536453642Sguido	nat_t *nat;
536553642Sguido	u_32_t iph;
536653642Sguido
5367255332Scy	softc = fin->fin_main_soft;
5368255332Scy	softn = softc->ipf_nat_soft;
5369255332Scy
5370255332Scy	if (softn->ipf_nat_lock != 0)
537153642Sguido		return 0;
5372255332Scy	if (softn->ipf_nat_stats.ns_rules == 0 &&
5373255332Scy	    softn->ipf_nat_instances == NULL)
5374255332Scy		return 0;
537553642Sguido
5376145522Sdarrenr	tcp = NULL;
5377145522Sdarrenr	icmp = NULL;
5378145522Sdarrenr	dport = 0;
5379145522Sdarrenr	natadd = 1;
5380145522Sdarrenr	nflags = 0;
5381145522Sdarrenr	natfailed = 0;
5382145522Sdarrenr	ifp = fin->fin_ifp;
5383145522Sdarrenr
5384145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
5385145522Sdarrenr		switch (fin->fin_p)
5386145522Sdarrenr		{
5387145522Sdarrenr		case IPPROTO_TCP :
538853642Sguido			nflags = IPN_TCP;
5389145522Sdarrenr			break;
5390145522Sdarrenr		case IPPROTO_UDP :
539153642Sguido			nflags = IPN_UDP;
5392145522Sdarrenr			break;
5393145522Sdarrenr		case IPPROTO_ICMP :
5394145522Sdarrenr			icmp = fin->fin_dp;
5395145522Sdarrenr
5396145522Sdarrenr			/*
5397145522Sdarrenr			 * This is an incoming packet, so the destination is
5398145522Sdarrenr			 * the icmp_id and the source port equals 0
5399145522Sdarrenr			 */
5400255332Scy			if ((fin->fin_flx & FI_ICMPQUERY) != 0) {
5401145522Sdarrenr				nflags = IPN_ICMPQUERY;
5402255332Scy				dport = icmp->icmp_id;
5403145522Sdarrenr			} break;
5404145522Sdarrenr		default :
5405145522Sdarrenr			break;
5406145522Sdarrenr		}
5407255332Scy
540853642Sguido		if ((nflags & IPN_TCPUDP)) {
5409145522Sdarrenr			tcp = fin->fin_dp;
5410255332Scy			dport = fin->fin_data[1];
541153642Sguido		}
541253642Sguido	}
541353642Sguido
541492685Sdarrenr	in = fin->fin_dst;
541553642Sguido
5416255332Scy	READ_ENTER(&softc->ipf_nat);
541753642Sguido
5418255332Scy	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
5419255332Scy	    (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND)))
5420145522Sdarrenr		/*EMPTY*/;
5421255332Scy	else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
542253642Sguido		natadd = 0;
5423255332Scy	else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH,
5424255332Scy					 (u_int)fin->fin_p,
5425255332Scy					 fin->fin_src, in))) {
542653642Sguido		nflags = nat->nat_flags;
5427255332Scy	} else if (fin->fin_off == 0) {
5428255332Scy		u_32_t hv, msk, rmsk = 0;
5429145522Sdarrenr
543053642Sguido		/*
543153642Sguido		 * If there is no current entry in the nat table for this IP#,
543253642Sguido		 * create one for it (if there is a matching rule).
543353642Sguido		 */
543453642Sguidomaskloop:
5435255332Scy		msk = softn->ipf_nat_rdr_active_masks[rmsk];
5436255332Scy		iph = in.s_addr & msk;
5437255332Scy		hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz);
5438255332Scyretry_roundrobin:
5439255332Scy		/* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */
5440255332Scy		for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) {
5441255332Scy			npnext = np->in_rnext;
5442145522Sdarrenr			if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
544360852Sdarrenr				continue;
5444255332Scy			if (np->in_v[0] != 4)
5445138947Sdarrenr				continue;
5446255332Scy			if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p))
5447145522Sdarrenr				continue;
5448145522Sdarrenr			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
5449145522Sdarrenr				continue;
545060852Sdarrenr			if (np->in_flags & IPN_FILTER) {
5451255332Scy				switch (ipf_nat_match(fin, np))
5452255332Scy				{
5453255332Scy				case 0 :
545460852Sdarrenr					continue;
5455255332Scy				case -1 :
5456255332Scy					rval = -1;
5457255332Scy					goto inmatchfail;
5458255332Scy				case 1 :
5459255332Scy				default :
5460255332Scy					break;
5461255332Scy				}
5462145522Sdarrenr			} else {
5463255332Scy				if ((in.s_addr & np->in_odstmsk) !=
5464255332Scy				    np->in_odstaddr)
5465145522Sdarrenr					continue;
5466255332Scy				if (np->in_odport &&
5467255332Scy				    ((np->in_dtop < dport) ||
5468255332Scy				     (dport < np->in_odport)))
5469145522Sdarrenr					continue;
5470145522Sdarrenr			}
5471145522Sdarrenr
5472255332Scy			if (np->in_plabel != -1) {
5473255332Scy				if (!ipf_proxy_ok(fin, tcp, np)) {
5474145522Sdarrenr					continue;
547553642Sguido				}
5476145522Sdarrenr			}
5477145522Sdarrenr
5478255332Scy			if (np->in_flags & IPN_NO) {
5479145522Sdarrenr				np->in_hits++;
5480145522Sdarrenr				break;
5481255332Scy			}
548260852Sdarrenr
5483255332Scy			MUTEX_ENTER(&softn->ipf_nat_new);
5484255332Scy			/*
5485255332Scy			 * If we've matched a round-robin rule but it has
5486255332Scy			 * moved in the list since we got it, start over as
5487255332Scy			 * this is now no longer correct.
5488255332Scy			 */
5489255332Scy			if (npnext != np->in_rnext) {
5490255332Scy				if ((np->in_flags & IPN_ROUNDR) != 0) {
5491255332Scy					MUTEX_EXIT(&softn->ipf_nat_new);
5492255332Scy					goto retry_roundrobin;
5493255332Scy				}
5494255332Scy				npnext = np->in_rnext;
5495145522Sdarrenr			}
5496255332Scy
5497255332Scy			nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND);
5498255332Scy			MUTEX_EXIT(&softn->ipf_nat_new);
5499255332Scy			if (nat != NULL) {
5500255332Scy				natfailed = 0;
5501255332Scy				break;
5502145522Sdarrenr			}
5503255332Scy			natfailed = -1;
550453642Sguido		}
5505255332Scy		if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) {
5506255332Scy			rmsk++;
5507255332Scy			goto maskloop;
5508255332Scy		}
550953642Sguido	}
5510255332Scy
5511145522Sdarrenr	if (nat != NULL) {
5512255332Scy		rval = ipf_nat_in(fin, nat, natadd, nflags);
5513145522Sdarrenr		if (rval == 1) {
5514145522Sdarrenr			MUTEX_ENTER(&nat->nat_lock);
5515255332Scy			ipf_nat_update(fin, nat);
5516255332Scy			nat->nat_bytes[0] += fin->fin_plen;
5517255332Scy			nat->nat_pkts[0]++;
5518255332Scy			fin->fin_pktnum = nat->nat_pkts[0];
5519145522Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
5520145522Sdarrenr		}
5521145522Sdarrenr	} else
5522145522Sdarrenr		rval = natfailed;
5523255332Scyinmatchfail:
5524255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
552572006Sdarrenr
5526255332Scy	switch (rval)
5527255332Scy	{
5528255332Scy	case -1 :
5529255332Scy		if (passp != NULL) {
5530255332Scy			DT1(frb_natv4in, fr_info_t *, fin);
5531255332Scy			NBUMPSIDED(0, ns_drop);
5532145522Sdarrenr			*passp = FR_BLOCK;
5533255332Scy			fin->fin_reason = FRB_NATV4;
5534255332Scy		}
5535145522Sdarrenr		fin->fin_flx |= FI_BADNAT;
5536255332Scy		NBUMPSIDED(0, ns_badnat);
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;
5632145522Sdarrenr#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5633145522Sdarrenr     defined(__osf__) || defined(linux)
5634255332Scy		ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0);
5635145522Sdarrenr#endif
5636255332Scy		break;
5637145522Sdarrenr
5638255332Scy	case NAT_OUTBOUND :
5639255332Scy		if ((fin->fin_flx & FI_ICMPERR) == 0) {
5640255332Scy			fin->fin_ip->ip_src = nat->nat_odstip;
5641255332Scy			fin->fin_saddr = nat->nat_odstaddr;
5642255332Scy		} else {
5643255332Scy			sum1 = nat->nat_odstaddr;
5644255332Scy			sum2 = nat->nat_ndstaddr;
5645255332Scy			CALC_SUMD(sum1, sum2, sumd);
5646255332Scy			ipsumd -= sumd;
5647255332Scy		}
5648255332Scy		fin->fin_ip->ip_dst = nat->nat_osrcip;
5649255332Scy		fin->fin_daddr = nat->nat_osrcaddr;
5650255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5651255332Scy     defined(__osf__) || defined(linux)
5652255332Scy		ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0);
5653255332Scy#endif
5654255332Scy		break;
5655255332Scy
5656255332Scy	case NAT_DIVERTIN :
5657255332Scy	    {
5658255332Scy		udphdr_t *uh;
5659255332Scy		ip_t *ip;
5660255332Scy		mb_t *m;
5661255332Scy
5662255332Scy		m = M_DUP(np->in_divmp);
5663255332Scy		if (m == NULL) {
5664255332Scy			NBUMPSIDED(0, ns_divert_dup);
5665255332Scy			return -1;
5666255332Scy		}
5667255332Scy
5668255332Scy		ip = MTOD(m, ip_t *);
5669255332Scy		ip->ip_id = htons(ipf_nextipid(fin));
5670255332Scy		sum1 = ntohs(ip->ip_len);
5671255332Scy		ip->ip_len = ntohs(ip->ip_len);
5672255332Scy		ip->ip_len += fin->fin_plen;
5673255332Scy		ip->ip_len = htons(ip->ip_len);
5674255332Scy
5675255332Scy		uh = (udphdr_t *)(ip + 1);
5676255332Scy		uh->uh_ulen += fin->fin_plen;
5677255332Scy		uh->uh_ulen = htons(uh->uh_ulen);
5678255332Scy
5679255332Scy		sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len);
5680255332Scy		sum2 += ntohs(ip->ip_off) & IP_DF;
5681255332Scy		CALC_SUMD(sum1, sum2, sumd);
5682255332Scy
5683255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5684255332Scy     defined(__osf__) || defined(linux)
5685255332Scy		ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0);
5686255332Scy#endif
5687255332Scy		PREP_MB_T(fin, m);
5688255332Scy
5689255332Scy		fin->fin_ip = ip;
5690255332Scy		fin->fin_plen += sizeof(ip_t) + 8;	/* UDP + new IPv4 hdr */
5691255332Scy		fin->fin_dlen += sizeof(ip_t) + 8;	/* UDP + old IPv4 hdr */
5692255332Scy
5693255332Scy		nflags &= ~IPN_TCPUDPICMP;
5694255332Scy
5695255332Scy		break;
5696255332Scy	    }
5697255332Scy
5698255332Scy	case NAT_DIVERTOUT :
5699255332Scy	    {
5700255332Scy		mb_t *m;
5701255332Scy
5702255332Scy		skip = ipf_nat_decap(fin, nat);
5703255332Scy		if (skip <= 0) {
5704255332Scy			NBUMPSIDED(0, ns_decap_fail);
5705255332Scy			return -1;
5706255332Scy		}
5707255332Scy
5708255332Scy		m = fin->fin_m;
5709255332Scy
5710255332Scy#if defined(MENTAT) && defined(_KERNEL)
5711255332Scy		m->b_rptr += skip;
5712255332Scy#else
5713255332Scy		m->m_data += skip;
5714255332Scy		m->m_len -= skip;
5715255332Scy
5716255332Scy# ifdef M_PKTHDR
5717255332Scy		if (m->m_flags & M_PKTHDR)
5718255332Scy			m->m_pkthdr.len -= skip;
5719255332Scy# endif
5720255332Scy#endif
5721255332Scy
5722255332Scy		ipf_nat_update(fin, nat);
5723255332Scy		nflags &= ~IPN_TCPUDPICMP;
5724255332Scy		fin->fin_flx |= FI_NATED;
5725255332Scy		if (np != NULL && np->in_tag.ipt_num[0] != 0)
5726255332Scy			fin->fin_nattag = &np->in_tag;
5727255332Scy		return 1;
5728255332Scy		/* NOTREACHED */
5729255332Scy	    }
5730255332Scy	}
5731255332Scy	if (nflags & IPN_TCPUDP)
5732255332Scy		tcp = fin->fin_dp;
5733255332Scy
5734145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
5735255332Scy		u_short *csump;
5736255332Scy
5737255332Scy		if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) {
5738255332Scy			switch (nat->nat_dir)
5739255332Scy			{
5740255332Scy			case NAT_INBOUND :
5741255332Scy				tcp->th_sport = nat->nat_nsport;
5742255332Scy				fin->fin_data[0] = ntohs(nat->nat_nsport);
5743255332Scy				tcp->th_dport = nat->nat_ndport;
5744255332Scy				fin->fin_data[1] = ntohs(nat->nat_ndport);
5745255332Scy				break;
5746255332Scy
5747255332Scy			case NAT_OUTBOUND :
5748255332Scy				tcp->th_sport = nat->nat_odport;
5749255332Scy				fin->fin_data[0] = ntohs(nat->nat_odport);
5750255332Scy				tcp->th_dport = nat->nat_osport;
5751255332Scy				fin->fin_data[1] = ntohs(nat->nat_osport);
5752255332Scy				break;
5753255332Scy			}
575492685Sdarrenr		}
575553642Sguido
5756145522Sdarrenr
5757255332Scy		if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) {
5758145522Sdarrenr			icmp = fin->fin_dp;
5759145522Sdarrenr
5760255332Scy			icmp->icmp_id = nat->nat_nicmpid;
5761145522Sdarrenr		}
5762145522Sdarrenr
5763255332Scy		csump = ipf_nat_proto(fin, nat, nflags);
5764255332Scy
5765255332Scy		/*
5766255332Scy		 * The above comments do not hold for layer 4 (or higher)
5767255332Scy		 * checksums...
5768255332Scy		 */
5769255332Scy		if (csump != NULL) {
5770255332Scy			if (nat->nat_dir == NAT_OUTBOUND)
5771255332Scy				ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0);
5772255332Scy			else
5773255332Scy				ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0);
5774255332Scy		}
5775145522Sdarrenr	}
5776145522Sdarrenr
5777145522Sdarrenr	fin->fin_flx |= FI_NATED;
5778145522Sdarrenr	if (np != NULL && np->in_tag.ipt_num[0] != 0)
5779145522Sdarrenr		fin->fin_nattag = &np->in_tag;
5780145522Sdarrenr	return 1;
5781145522Sdarrenr}
5782130886Sdarrenr
5783130886Sdarrenr
5784145522Sdarrenr/* ------------------------------------------------------------------------ */
5785255332Scy/* Function:    ipf_nat_proto                                               */
5786145522Sdarrenr/* Returns:     u_short* - pointer to transport header checksum to update,  */
5787145522Sdarrenr/*                         NULL if the transport protocol is not recognised */
5788145522Sdarrenr/*                         as needing a checksum update.                    */
5789145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
5790145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
5791145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
5792145522Sdarrenr/*                                                                          */
5793145522Sdarrenr/* Return the pointer to the checksum field for each protocol so understood.*/
5794145522Sdarrenr/* If support for making other changes to a protocol header is required,    */
5795145522Sdarrenr/* that is not strictly 'address' translation, such as clamping the MSS in  */
5796145522Sdarrenr/* TCP down to a specific value, then do it from here.                      */
5797145522Sdarrenr/* ------------------------------------------------------------------------ */
5798255332Scyu_short *
5799255332Scyipf_nat_proto(fin, nat, nflags)
5800255332Scy	fr_info_t *fin;
5801255332Scy	nat_t *nat;
5802255332Scy	u_int nflags;
5803145522Sdarrenr{
5804145522Sdarrenr	icmphdr_t *icmp;
5805145522Sdarrenr	u_short *csump;
5806145522Sdarrenr	tcphdr_t *tcp;
5807145522Sdarrenr	udphdr_t *udp;
580853642Sguido
5809145522Sdarrenr	csump = NULL;
5810145522Sdarrenr	if (fin->fin_out == 0) {
5811255332Scy		fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND);
5812145522Sdarrenr	} else {
5813255332Scy		fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0);
5814145522Sdarrenr	}
581553642Sguido
5816145522Sdarrenr	switch (fin->fin_p)
5817145522Sdarrenr	{
5818145522Sdarrenr	case IPPROTO_TCP :
5819145522Sdarrenr		tcp = fin->fin_dp;
5820110916Sdarrenr
5821255332Scy		if ((nflags & IPN_TCP) != 0)
5822255332Scy			csump = &tcp->th_sum;
582353642Sguido
5824145522Sdarrenr		/*
5825145522Sdarrenr		 * Do a MSS CLAMPING on a SYN packet,
5826145522Sdarrenr		 * only deal IPv4 for now.
5827145522Sdarrenr		 */
5828145522Sdarrenr		if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0)
5829255332Scy			ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump);
583060852Sdarrenr
5831145522Sdarrenr		break;
5832145522Sdarrenr
5833145522Sdarrenr	case IPPROTO_UDP :
5834145522Sdarrenr		udp = fin->fin_dp;
5835145522Sdarrenr
5836255332Scy		if ((nflags & IPN_UDP) != 0) {
5837255332Scy			if (udp->uh_sum != 0)
5838255332Scy				csump = &udp->uh_sum;
5839255332Scy		}
5840145522Sdarrenr		break;
5841145522Sdarrenr
5842145522Sdarrenr	case IPPROTO_ICMP :
5843145522Sdarrenr		icmp = fin->fin_dp;
5844145522Sdarrenr
5845145522Sdarrenr		if ((nflags & IPN_ICMPQUERY) != 0) {
5846145522Sdarrenr			if (icmp->icmp_cksum != 0)
5847145522Sdarrenr				csump = &icmp->icmp_cksum;
584853642Sguido		}
5849145522Sdarrenr		break;
585053642Sguido
5851255332Scy#ifdef USE_INET6
5852255332Scy	case IPPROTO_ICMPV6 :
5853255332Scy	    {
5854255332Scy		struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp;
585553642Sguido
5856255332Scy		icmp6 = fin->fin_dp;
5857145522Sdarrenr
5858255332Scy		if ((nflags & IPN_ICMPQUERY) != 0) {
5859255332Scy			if (icmp6->icmp6_cksum != 0)
5860255332Scy				csump = &icmp6->icmp6_cksum;
5861255332Scy		}
5862255332Scy		break;
5863255332Scy	    }
5864255332Scy#endif
5865145522Sdarrenr	}
5866255332Scy	return csump;
586753642Sguido}
586853642Sguido
586953642Sguido
5870145522Sdarrenr/* ------------------------------------------------------------------------ */
5871255332Scy/* Function:    ipf_nat_expire                                              */
5872145522Sdarrenr/* Returns:     Nil                                                         */
5873255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
5874145522Sdarrenr/*                                                                          */
5875145522Sdarrenr/* Check all of the timeout queues for entries at the top which need to be  */
5876145522Sdarrenr/* expired.                                                                 */
5877145522Sdarrenr/* ------------------------------------------------------------------------ */
5878255332Scyvoid
5879255332Scyipf_nat_expire(softc)
5880255332Scy	ipf_main_softc_t *softc;
588153642Sguido{
5882255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5883145522Sdarrenr	ipftq_t *ifq, *ifqnext;
5884145522Sdarrenr	ipftqent_t *tqe, *tqn;
5885161356Sguido	int i;
5886153876Sguido	SPL_INT(s);
588753642Sguido
588853642Sguido	SPL_NET(s);
5889255332Scy	WRITE_ENTER(&softc->ipf_nat);
5890255332Scy	for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL;
5891255332Scy	     ifq = ifq->ifq_next) {
5892145522Sdarrenr		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
5893255332Scy			if (tqe->tqe_die > softc->ipf_ticks)
5894145522Sdarrenr				break;
5895145522Sdarrenr			tqn = tqe->tqe_next;
5896255332Scy			ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE);
589753642Sguido		}
589853642Sguido	}
5899145522Sdarrenr
5900255332Scy	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) {
5901145522Sdarrenr		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
5902255332Scy			if (tqe->tqe_die > softc->ipf_ticks)
5903145522Sdarrenr				break;
5904145522Sdarrenr			tqn = tqe->tqe_next;
5905255332Scy			ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE);
5906145522Sdarrenr		}
5907145522Sdarrenr	}
5908145522Sdarrenr
5909255332Scy	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) {
5910145522Sdarrenr		ifqnext = ifq->ifq_next;
5911145522Sdarrenr
5912145522Sdarrenr		if (((ifq->ifq_flags & IFQF_DELETE) != 0) &&
5913145522Sdarrenr		    (ifq->ifq_ref == 0)) {
5914255332Scy			ipf_freetimeoutqueue(softc, ifq);
5915145522Sdarrenr		}
5916145522Sdarrenr	}
5917145522Sdarrenr
5918255332Scy	if (softn->ipf_nat_doflush != 0) {
5919255332Scy		ipf_nat_extraflush(softc, softn, 2);
5920255332Scy		softn->ipf_nat_doflush = 0;
5921170268Sdarrenr	}
5922170268Sdarrenr
5923255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
592453642Sguido	SPL_X(s);
592553642Sguido}
592653642Sguido
592753642Sguido
5928145522Sdarrenr/* ------------------------------------------------------------------------ */
5929255332Scy/* Function:    ipf_nat_sync                                                */
5930145522Sdarrenr/* Returns:     Nil                                                         */
5931255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
5932255332Scy/*              ifp(I) - pointer to network interface                       */
5933145522Sdarrenr/*                                                                          */
5934145522Sdarrenr/* Walk through all of the currently active NAT sessions, looking for those */
5935145522Sdarrenr/* which need to have their translated address updated.                     */
5936145522Sdarrenr/* ------------------------------------------------------------------------ */
5937255332Scyvoid
5938255332Scyipf_nat_sync(softc, ifp)
5939255332Scy	ipf_main_softc_t *softc;
5940255332Scy	void *ifp;
594153642Sguido{
5942255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5943145522Sdarrenr	u_32_t sum1, sum2, sumd;
5944255332Scy	i6addr_t in;
5945145522Sdarrenr	ipnat_t *n;
5946145522Sdarrenr	nat_t *nat;
594753642Sguido	void *ifp2;
5948255332Scy	int idx;
5949153876Sguido	SPL_INT(s);
595053642Sguido
5951255332Scy	if (softc->ipf_running <= 0)
5952145522Sdarrenr		return;
5953145522Sdarrenr
595453642Sguido	/*
595553642Sguido	 * Change IP addresses for NAT sessions for any protocol except TCP
5956145522Sdarrenr	 * since it will break the TCP connection anyway.  The only rules
5957145522Sdarrenr	 * which will get changed are those which are "map ... -> 0/32",
5958145522Sdarrenr	 * where the rule specifies the address is taken from the interface.
595953642Sguido	 */
596053642Sguido	SPL_NET(s);
5961255332Scy	WRITE_ENTER(&softc->ipf_nat);
5962145522Sdarrenr
5963255332Scy	if (softc->ipf_running <= 0) {
5964255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
5965145522Sdarrenr		return;
5966145522Sdarrenr	}
5967145522Sdarrenr
5968255332Scy	for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) {
5969145522Sdarrenr		if ((nat->nat_flags & IPN_TCP) != 0)
5970145522Sdarrenr			continue;
5971255332Scy
5972145522Sdarrenr		n = nat->nat_ptr;
5973255332Scy		if (n != NULL) {
5974255332Scy			if (n->in_v[1] == 4) {
5975255332Scy				if (n->in_redir & NAT_MAP) {
5976255332Scy					if ((n->in_nsrcaddr != 0) ||
5977255332Scy					    (n->in_nsrcmsk != 0xffffffff))
5978255332Scy						continue;
5979255332Scy				} else if (n->in_redir & NAT_REDIRECT) {
5980255332Scy					if ((n->in_ndstaddr != 0) ||
5981255332Scy					    (n->in_ndstmsk != 0xffffffff))
5982255332Scy						continue;
5983255332Scy				}
5984255332Scy			}
5985255332Scy#ifdef USE_INET6
5986255332Scy			if (n->in_v[1] == 4) {
5987255332Scy				if (n->in_redir & NAT_MAP) {
5988255332Scy					if (!IP6_ISZERO(&n->in_nsrcaddr) ||
5989255332Scy					    !IP6_ISONES(&n->in_nsrcmsk))
5990255332Scy						continue;
5991255332Scy				} else if (n->in_redir & NAT_REDIRECT) {
5992255332Scy					if (!IP6_ISZERO(&n->in_ndstaddr) ||
5993255332Scy					    !IP6_ISONES(&n->in_ndstmsk))
5994255332Scy						continue;
5995255332Scy				}
5996255332Scy			}
5997255332Scy#endif
5998255332Scy		}
5999255332Scy
6000145522Sdarrenr		if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) ||
6001145522Sdarrenr		     (ifp == nat->nat_ifps[1]))) {
6002255332Scy			nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0],
6003255332Scy						  nat->nat_v[0]);
6004255332Scy			if ((nat->nat_ifps[0] != NULL) &&
6005255332Scy			    (nat->nat_ifps[0] != (void *)-1)) {
6006255332Scy				nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
6007255332Scy			}
6008145522Sdarrenr			if (nat->nat_ifnames[1][0] != '\0') {
6009145522Sdarrenr				nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1],
6010255332Scy							  nat->nat_v[1]);
6011255332Scy			} else {
6012145522Sdarrenr				nat->nat_ifps[1] = nat->nat_ifps[0];
6013255332Scy			}
6014255332Scy			if ((nat->nat_ifps[1] != NULL) &&
6015255332Scy			    (nat->nat_ifps[1] != (void *)-1)) {
6016255332Scy				nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
6017255332Scy			}
6018145522Sdarrenr			ifp2 = nat->nat_ifps[0];
6019145522Sdarrenr			if (ifp2 == NULL)
6020145522Sdarrenr				continue;
6021145522Sdarrenr
602253642Sguido			/*
602353642Sguido			 * Change the map-to address to be the same as the
602453642Sguido			 * new one.
602553642Sguido			 */
6026255332Scy			sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6);
6027255332Scy			if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2,
6028255332Scy				       &in, NULL) != -1) {
6029255332Scy				if (nat->nat_v[0] == 4)
6030255332Scy					nat->nat_nsrcip = in.in4;
6031255332Scy			}
6032255332Scy			sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6);
603353642Sguido
603453642Sguido			if (sum1 == sum2)
603553642Sguido				continue;
603653642Sguido			/*
603753642Sguido			 * Readjust the checksum adjustment to take into
603853642Sguido			 * account the new IP#.
603953642Sguido			 */
604053642Sguido			CALC_SUMD(sum1, sum2, sumd);
604155929Sguido			/* XXX - dont change for TCP when solaris does
604255929Sguido			 * hardware checksumming.
604355929Sguido			 */
604455929Sguido			sumd += nat->nat_sumd[0];
604555929Sguido			nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
604655929Sguido			nat->nat_sumd[1] = nat->nat_sumd[0];
604753642Sguido		}
6048145522Sdarrenr	}
604953642Sguido
6050255332Scy	for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) {
6051255332Scy		char *base = n->in_names;
6052255332Scy
6053145522Sdarrenr		if ((ifp == NULL) || (n->in_ifps[0] == ifp))
6054255332Scy			n->in_ifps[0] = ipf_resolvenic(softc,
6055255332Scy						       base + n->in_ifnames[0],
6056255332Scy						       n->in_v[0]);
6057145522Sdarrenr		if ((ifp == NULL) || (n->in_ifps[1] == ifp))
6058255332Scy			n->in_ifps[1] = ipf_resolvenic(softc,
6059255332Scy						       base + n->in_ifnames[1],
6060255332Scy						       n->in_v[1]);
6061255332Scy
6062255332Scy		if (n->in_redir & NAT_REDIRECT)
6063255332Scy			idx = 1;
6064255332Scy		else
6065255332Scy			idx = 0;
6066255332Scy
6067255332Scy		if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) &&
6068255332Scy		    (n->in_ifps[idx] != NULL &&
6069255332Scy		     n->in_ifps[idx] != (void *)-1)) {
6070255332Scy
6071255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc,
6072255332Scy					     0, n->in_ifps[idx]);
6073255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst,
6074255332Scy					     0, n->in_ifps[idx]);
6075255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc,
6076255332Scy					     0, n->in_ifps[idx]);
6077255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst,
6078255332Scy					     0, n->in_ifps[idx]);
6079255332Scy		}
6080145522Sdarrenr	}
6081255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
608253642Sguido	SPL_X(s);
608353642Sguido}
608453642Sguido
608553642Sguido
6086145522Sdarrenr/* ------------------------------------------------------------------------ */
6087255332Scy/* Function:    ipf_nat_icmpquerytype                                       */
6088145522Sdarrenr/* Returns:     int - 1 == success, 0 == failure                            */
6089145522Sdarrenr/* Parameters:  icmptype(I) - ICMP type number                              */
6090145522Sdarrenr/*                                                                          */
6091145522Sdarrenr/* Tests to see if the ICMP type number passed is a query/response type or  */
6092145522Sdarrenr/* not.                                                                     */
6093145522Sdarrenr/* ------------------------------------------------------------------------ */
6094255332Scystatic int
6095255332Scyipf_nat_icmpquerytype(icmptype)
6096255332Scy	int icmptype;
6097145522Sdarrenr{
6098145522Sdarrenr
6099145522Sdarrenr	/*
6100145522Sdarrenr	 * For the ICMP query NAT code, it is essential that both the query
6101145522Sdarrenr	 * and the reply match on the NAT rule. Because the NAT structure
6102145522Sdarrenr	 * does not keep track of the icmptype, and a single NAT structure
6103145522Sdarrenr	 * is used for all icmp types with the same src, dest and id, we
6104145522Sdarrenr	 * simply define the replies as queries as well. The funny thing is,
6105145522Sdarrenr	 * altough it seems silly to call a reply a query, this is exactly
6106145522Sdarrenr	 * as it is defined in the IPv4 specification
6107145522Sdarrenr	 */
6108145522Sdarrenr	switch (icmptype)
6109145522Sdarrenr	{
6110145522Sdarrenr	case ICMP_ECHOREPLY:
6111145522Sdarrenr	case ICMP_ECHO:
6112145522Sdarrenr	/* route aedvertisement/solliciation is currently unsupported: */
6113145522Sdarrenr	/* it would require rewriting the ICMP data section            */
6114145522Sdarrenr	case ICMP_TSTAMP:
6115145522Sdarrenr	case ICMP_TSTAMPREPLY:
6116145522Sdarrenr	case ICMP_IREQ:
6117145522Sdarrenr	case ICMP_IREQREPLY:
6118145522Sdarrenr	case ICMP_MASKREQ:
6119145522Sdarrenr	case ICMP_MASKREPLY:
6120145522Sdarrenr		return 1;
6121145522Sdarrenr	default:
6122145522Sdarrenr		return 0;
6123145522Sdarrenr	}
6124145522Sdarrenr}
6125145522Sdarrenr
6126145522Sdarrenr
6127145522Sdarrenr/* ------------------------------------------------------------------------ */
6128145522Sdarrenr/* Function:    nat_log                                                     */
6129145522Sdarrenr/* Returns:     Nil                                                         */
6130255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6131255332Scy/*              softn(I) - pointer to NAT context structure                 */
6132255332Scy/*              nat(I)    - pointer to NAT structure                        */
6133255332Scy/*              action(I) - action related to NAT structure being performed */
6134145522Sdarrenr/*                                                                          */
6135145522Sdarrenr/* Creates a NAT log entry.                                                 */
6136145522Sdarrenr/* ------------------------------------------------------------------------ */
6137255332Scyvoid
6138255332Scyipf_nat_log(softc, softn, nat, action)
6139255332Scy	ipf_main_softc_t *softc;
6140255332Scy	ipf_nat_softc_t *softn;
6141255332Scy	struct nat *nat;
6142255332Scy	u_int action;
614353642Sguido{
6144145522Sdarrenr#ifdef	IPFILTER_LOG
6145139005Smlaier# ifndef LARGE_NAT
614653642Sguido	struct ipnat *np;
6147138979Sdarrenr	int rulen;
6148138979Sdarrenr# endif
614953642Sguido	struct natlog natl;
615053642Sguido	void *items[1];
615153642Sguido	size_t sizes[1];
6152138979Sdarrenr	int types[1];
615353642Sguido
6154255332Scy	bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip,
6155255332Scy	      sizeof(natl.nl_osrcip));
6156255332Scy	bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip,
6157255332Scy	      sizeof(natl.nl_nsrcip));
6158255332Scy	bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip,
6159255332Scy	      sizeof(natl.nl_odstip));
6160255332Scy	bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip,
6161255332Scy	      sizeof(natl.nl_ndstip));
6162255332Scy
6163145522Sdarrenr	natl.nl_bytes[0] = nat->nat_bytes[0];
6164145522Sdarrenr	natl.nl_bytes[1] = nat->nat_bytes[1];
6165145522Sdarrenr	natl.nl_pkts[0] = nat->nat_pkts[0];
6166145522Sdarrenr	natl.nl_pkts[1] = nat->nat_pkts[1];
6167255332Scy	natl.nl_odstport = nat->nat_odport;
6168255332Scy	natl.nl_osrcport = nat->nat_osport;
6169255332Scy	natl.nl_nsrcport = nat->nat_nsport;
6170255332Scy	natl.nl_ndstport = nat->nat_ndport;
6171255332Scy	natl.nl_p[0] = nat->nat_pr[0];
6172255332Scy	natl.nl_p[1] = nat->nat_pr[1];
6173255332Scy	natl.nl_v[0] = nat->nat_v[0];
6174255332Scy	natl.nl_v[1] = nat->nat_v[1];
6175255332Scy	natl.nl_type = nat->nat_redir;
6176255332Scy	natl.nl_action = action;
617753642Sguido	natl.nl_rule = -1;
6178255332Scy
6179255332Scy	bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0],
6180255332Scy	      sizeof(nat->nat_ifnames[0]));
6181255332Scy	bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1],
6182255332Scy	      sizeof(nat->nat_ifnames[1]));
6183255332Scy
6184145522Sdarrenr# ifndef LARGE_NAT
618553642Sguido	if (nat->nat_ptr != NULL) {
6186255332Scy		for (rulen = 0, np = softn->ipf_nat_list; np != NULL;
6187255332Scy		     np = np->in_next, rulen++)
618853642Sguido			if (np == nat->nat_ptr) {
618953642Sguido				natl.nl_rule = rulen;
619053642Sguido				break;
619153642Sguido			}
619253642Sguido	}
6193145522Sdarrenr# endif
619453642Sguido	items[0] = &natl;
619553642Sguido	sizes[0] = sizeof(natl);
619653642Sguido	types[0] = 0;
619753642Sguido
6198255332Scy	(void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1);
6199145522Sdarrenr#endif
620053642Sguido}
620192685Sdarrenr
620292685Sdarrenr
620392685Sdarrenr#if defined(__OpenBSD__)
6204145522Sdarrenr/* ------------------------------------------------------------------------ */
6205255332Scy/* Function:    ipf_nat_ifdetach                                            */
6206145522Sdarrenr/* Returns:     Nil                                                         */
6207145522Sdarrenr/* Parameters:  ifp(I) - pointer to network interface                       */
6208145522Sdarrenr/*                                                                          */
6209145522Sdarrenr/* Compatibility interface for OpenBSD to trigger the correct updating of   */
6210145522Sdarrenr/* interface references within IPFilter.                                    */
6211145522Sdarrenr/* ------------------------------------------------------------------------ */
6212255332Scyvoid
6213255332Scyipf_nat_ifdetach(ifp)
6214255332Scy	void *ifp;
621592685Sdarrenr{
6216255332Scy	ipf_main_softc_t *softc;
6217255332Scy
6218255332Scy	softc = ipf_get_softc(0);
6219255332Scy
6220255332Scy	ipf_sync(ifp);
622192685Sdarrenr	return;
622292685Sdarrenr}
622392685Sdarrenr#endif
6224110916Sdarrenr
6225110916Sdarrenr
6226145522Sdarrenr/* ------------------------------------------------------------------------ */
6227255332Scy/* Function:    ipf_nat_rule_deref                                          */
6228170268Sdarrenr/* Returns:     Nil                                                         */
6229255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6230255332Scy/*              inp(I)   - pointer to pointer to NAT rule                   */
6231170268Sdarrenr/* Write Locks: ipf_nat                                                     */
6232170268Sdarrenr/*                                                                          */
6233255332Scy/* Dropping the refernce count for a rule means that whatever held the      */
6234255332Scy/* pointer to this rule (*inp) is no longer interested in it and when the   */
6235255332Scy/* reference count drops to zero, any resources allocated for the rule can  */
6236255332Scy/* be released and the rule itself free'd.                                  */
6237170268Sdarrenr/* ------------------------------------------------------------------------ */
6238255332Scyvoid
6239255332Scyipf_nat_rule_deref(softc, inp)
6240255332Scy	ipf_main_softc_t *softc;
6241255332Scy	ipnat_t **inp;
6242170268Sdarrenr{
6243255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6244255332Scy	ipnat_t *n;
6245170268Sdarrenr
6246255332Scy	n = *inp;
6247170268Sdarrenr	*inp = NULL;
6248255332Scy	n->in_use--;
6249255332Scy	if (n->in_use > 0)
6250255332Scy		return;
6251255332Scy
6252255332Scy	if (n->in_apr != NULL)
6253255332Scy		ipf_proxy_deref(n->in_apr);
6254255332Scy
6255255332Scy	ipf_nat_rule_fini(softc, n);
6256255332Scy
6257255332Scy	if (n->in_redir & NAT_REDIRECT) {
6258255332Scy		if ((n->in_flags & IPN_PROXYRULE) == 0) {
6259255332Scy			ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr);
6260255332Scy		}
6261255332Scy	}
6262255332Scy	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
6263255332Scy		if ((n->in_flags & IPN_PROXYRULE) == 0) {
6264255332Scy			ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map);
6265255332Scy		}
6266255332Scy	}
6267255332Scy
6268255332Scy	if (n->in_tqehead[0] != NULL) {
6269255332Scy		if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) {
6270255332Scy			ipf_freetimeoutqueue(softc, n->in_tqehead[1]);
6271255332Scy		}
6272255332Scy	}
6273255332Scy
6274255332Scy	if (n->in_tqehead[1] != NULL) {
6275255332Scy		if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) {
6276255332Scy			ipf_freetimeoutqueue(softc, n->in_tqehead[1]);
6277255332Scy		}
6278255332Scy	}
6279255332Scy
6280255332Scy	if ((n->in_flags & IPN_PROXYRULE) == 0) {
6281255332Scy		ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules);
6282255332Scy	}
6283255332Scy
6284255332Scy	MUTEX_DESTROY(&n->in_lock);
6285255332Scy
6286255332Scy	KFREES(n, n->in_size);
6287255332Scy
6288255332Scy#if SOLARIS && !defined(INSTANCES)
6289255332Scy	if (softn->ipf_nat_stats.ns_rules == 0)
6290255332Scy		pfil_delayed_copy = 1;
6291170268Sdarrenr#endif
6292170268Sdarrenr}
6293170268Sdarrenr
6294170268Sdarrenr
6295170268Sdarrenr/* ------------------------------------------------------------------------ */
6296255332Scy/* Function:    ipf_nat_deref                                               */
6297145522Sdarrenr/* Returns:     Nil                                                         */
6298255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6299255332Scy/*              natp(I)  - pointer to pointer to NAT table entry            */
6300145522Sdarrenr/*                                                                          */
6301145522Sdarrenr/* Decrement the reference counter for this NAT table entry and free it if  */
6302145522Sdarrenr/* there are no more things using it.                                       */
6303172776Sdarrenr/*                                                                          */
6304172776Sdarrenr/* IF nat_ref == 1 when this function is called, then we have an orphan nat */
6305172776Sdarrenr/* structure *because* it only gets called on paths _after_ nat_ref has been*/
6306172776Sdarrenr/* incremented.  If nat_ref == 1 then we shouldn't decrement it here        */
6307172776Sdarrenr/* because nat_delete() will do that and send nat_ref to -1.                */
6308172776Sdarrenr/*                                                                          */
6309172776Sdarrenr/* Holding the lock on nat_lock is required to serialise nat_delete() being */
6310172776Sdarrenr/* called from a NAT flush ioctl with a deref happening because of a packet.*/
6311145522Sdarrenr/* ------------------------------------------------------------------------ */
6312255332Scyvoid
6313255332Scyipf_nat_deref(softc, natp)
6314255332Scy	ipf_main_softc_t *softc;
6315255332Scy	nat_t **natp;
6316145522Sdarrenr{
6317145522Sdarrenr	nat_t *nat;
6318145522Sdarrenr
6319145522Sdarrenr	nat = *natp;
6320145522Sdarrenr	*natp = NULL;
6321172776Sdarrenr
6322172776Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
6323172776Sdarrenr	if (nat->nat_ref > 1) {
6324172776Sdarrenr		nat->nat_ref--;
6325255332Scy		ASSERT(nat->nat_ref >= 0);
6326172776Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
6327172776Sdarrenr		return;
6328172776Sdarrenr	}
6329172776Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
6330172776Sdarrenr
6331255332Scy	WRITE_ENTER(&softc->ipf_nat);
6332255332Scy	ipf_nat_delete(softc, nat, NL_EXPIRE);
6333255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
6334145522Sdarrenr}
6335145522Sdarrenr
6336145522Sdarrenr
6337145522Sdarrenr/* ------------------------------------------------------------------------ */
6338255332Scy/* Function:    ipf_nat_clone                                               */
6339145522Sdarrenr/* Returns:     ipstate_t* - NULL == cloning failed,                        */
6340145522Sdarrenr/*                           else pointer to new state structure            */
6341145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
6342145522Sdarrenr/*              is(I)  - pointer to master state structure                  */
6343145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
6344145522Sdarrenr/*                                                                          */
6345145522Sdarrenr/* Create a "duplcate" state table entry from the master.                   */
6346145522Sdarrenr/* ------------------------------------------------------------------------ */
6347255332Scynat_t *
6348255332Scyipf_nat_clone(fin, nat)
6349255332Scy	fr_info_t *fin;
6350255332Scy	nat_t *nat;
6351145522Sdarrenr{
6352255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
6353255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6354145522Sdarrenr	frentry_t *fr;
6355145522Sdarrenr	nat_t *clone;
6356145522Sdarrenr	ipnat_t *np;
6357145522Sdarrenr
6358145522Sdarrenr	KMALLOC(clone, nat_t *);
6359255332Scy	if (clone == NULL) {
6360255332Scy		NBUMPSIDED(fin->fin_out, ns_clone_nomem);
6361145522Sdarrenr		return NULL;
6362255332Scy	}
6363145522Sdarrenr	bcopy((char *)nat, (char *)clone, sizeof(*clone));
6364145522Sdarrenr
6365145522Sdarrenr	MUTEX_NUKE(&clone->nat_lock);
6366145522Sdarrenr
6367255332Scy	clone->nat_rev = fin->fin_rev;
6368153876Sguido	clone->nat_aps = NULL;
6369153876Sguido	/*
6370255332Scy	 * Initialize all these so that ipf_nat_delete() doesn't cause a crash.
6371153876Sguido	 */
6372153876Sguido	clone->nat_tqe.tqe_pnext = NULL;
6373153876Sguido	clone->nat_tqe.tqe_next = NULL;
6374153876Sguido	clone->nat_tqe.tqe_ifq = NULL;
6375153876Sguido	clone->nat_tqe.tqe_parent = clone;
6376153876Sguido
6377145522Sdarrenr	clone->nat_flags &= ~SI_CLONE;
6378145522Sdarrenr	clone->nat_flags |= SI_CLONED;
6379145522Sdarrenr
6380153876Sguido	if (clone->nat_hm)
6381153876Sguido		clone->nat_hm->hm_ref++;
6382145522Sdarrenr
6383255332Scy	if (ipf_nat_insert(softc, softn, clone) == -1) {
6384145522Sdarrenr		KFREE(clone);
6385255332Scy		NBUMPSIDED(fin->fin_out, ns_insert_fail);
6386145522Sdarrenr		return NULL;
6387145522Sdarrenr	}
6388255332Scy
6389145522Sdarrenr	np = clone->nat_ptr;
6390145522Sdarrenr	if (np != NULL) {
6391255332Scy		if (softn->ipf_nat_logging)
6392255332Scy			ipf_nat_log(softc, softn, clone, NL_CLONE);
6393145522Sdarrenr		np->in_use++;
6394145522Sdarrenr	}
6395145522Sdarrenr	fr = clone->nat_fr;
6396145522Sdarrenr	if (fr != NULL) {
6397145522Sdarrenr		MUTEX_ENTER(&fr->fr_lock);
6398145522Sdarrenr		fr->fr_ref++;
6399145522Sdarrenr		MUTEX_EXIT(&fr->fr_lock);
6400145522Sdarrenr	}
6401145522Sdarrenr
6402255332Scy
6403145522Sdarrenr	/*
6404145522Sdarrenr	 * Because the clone is created outside the normal loop of things and
6405145522Sdarrenr	 * TCP has special needs in terms of state, initialise the timeout
6406145522Sdarrenr	 * state of the new NAT from here.
6407145522Sdarrenr	 */
6408255332Scy	if (clone->nat_pr[0] == IPPROTO_TCP) {
6409255332Scy		(void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq,
6410255332Scy				   clone->nat_flags, 2);
6411145522Sdarrenr	}
6412255332Scy	clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone);
6413255332Scy	if (softn->ipf_nat_logging)
6414255332Scy		ipf_nat_log(softc, softn, clone, NL_CLONE);
6415145522Sdarrenr	return clone;
6416145522Sdarrenr}
6417145522Sdarrenr
6418145522Sdarrenr
6419145522Sdarrenr/* ------------------------------------------------------------------------ */
6420255332Scy/* Function:   ipf_nat_wildok                                               */
6421145522Sdarrenr/* Returns:    int - 1 == packet's ports match wildcards                    */
6422145522Sdarrenr/*                   0 == packet's ports don't match wildcards              */
6423145522Sdarrenr/* Parameters: nat(I)   - NAT entry                                         */
6424145522Sdarrenr/*             sport(I) - source port                                       */
6425145522Sdarrenr/*             dport(I) - destination port                                  */
6426145522Sdarrenr/*             flags(I) - wildcard flags                                    */
6427145522Sdarrenr/*             dir(I)   - packet direction                                  */
6428145522Sdarrenr/*                                                                          */
6429145522Sdarrenr/* Use NAT entry and packet direction to determine which combination of     */
6430145522Sdarrenr/* wildcard flags should be used.                                           */
6431145522Sdarrenr/* ------------------------------------------------------------------------ */
6432255332Scyint
6433255332Scyipf_nat_wildok(nat, sport, dport, flags, dir)
6434255332Scy	nat_t *nat;
6435255332Scy	int sport, dport, flags, dir;
6436145522Sdarrenr{
6437145522Sdarrenr	/*
6438145522Sdarrenr	 * When called by       dir is set to
6439145522Sdarrenr	 * nat_inlookup         NAT_INBOUND (0)
6440145522Sdarrenr	 * nat_outlookup        NAT_OUTBOUND (1)
6441145522Sdarrenr	 *
6442145522Sdarrenr	 * We simply combine the packet's direction in dir with the original
6443145522Sdarrenr	 * "intended" direction of that NAT entry in nat->nat_dir to decide
6444145522Sdarrenr	 * which combination of wildcard flags to allow.
6445145522Sdarrenr	 */
6446255332Scy	switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)))
6447145522Sdarrenr	{
6448145522Sdarrenr	case 3: /* outbound packet / outbound entry */
6449255332Scy		if (((nat->nat_osport == sport) ||
6450145522Sdarrenr		    (flags & SI_W_SPORT)) &&
6451255332Scy		    ((nat->nat_odport == dport) ||
6452145522Sdarrenr		    (flags & SI_W_DPORT)))
6453145522Sdarrenr			return 1;
6454145522Sdarrenr		break;
6455145522Sdarrenr	case 2: /* outbound packet / inbound entry */
6456255332Scy		if (((nat->nat_osport == dport) ||
6457255332Scy		    (flags & SI_W_SPORT)) &&
6458255332Scy		    ((nat->nat_odport == sport) ||
6459255332Scy		    (flags & SI_W_DPORT)))
6460145522Sdarrenr			return 1;
6461145522Sdarrenr		break;
6462145522Sdarrenr	case 1: /* inbound packet / outbound entry */
6463255332Scy		if (((nat->nat_osport == dport) ||
6464255332Scy		    (flags & SI_W_SPORT)) &&
6465255332Scy		    ((nat->nat_odport == sport) ||
6466255332Scy		    (flags & SI_W_DPORT)))
6467145522Sdarrenr			return 1;
6468145522Sdarrenr		break;
6469145522Sdarrenr	case 0: /* inbound packet / inbound entry */
6470255332Scy		if (((nat->nat_osport == sport) ||
6471145522Sdarrenr		    (flags & SI_W_SPORT)) &&
6472255332Scy		    ((nat->nat_odport == dport) ||
6473145522Sdarrenr		    (flags & SI_W_DPORT)))
6474145522Sdarrenr			return 1;
6475145522Sdarrenr		break;
6476145522Sdarrenr	default:
6477145522Sdarrenr		break;
6478145522Sdarrenr	}
6479145522Sdarrenr
6480145522Sdarrenr	return(0);
6481145522Sdarrenr}
6482145522Sdarrenr
6483145522Sdarrenr
6484145522Sdarrenr/* ------------------------------------------------------------------------ */
6485145522Sdarrenr/* Function:    nat_mssclamp                                                */
6486145522Sdarrenr/* Returns:     Nil                                                         */
6487145522Sdarrenr/* Parameters:  tcp(I)    - pointer to TCP header                           */
6488145522Sdarrenr/*              maxmss(I) - value to clamp the TCP MSS to                   */
6489145522Sdarrenr/*              fin(I)    - pointer to packet information                   */
6490145522Sdarrenr/*              csump(I)  - pointer to TCP checksum                         */
6491145522Sdarrenr/*                                                                          */
6492145522Sdarrenr/* Check for MSS option and clamp it if necessary.  If found and changed,   */
6493145522Sdarrenr/* then the TCP header checksum will be updated to reflect the change in    */
6494145522Sdarrenr/* the MSS.                                                                 */
6495145522Sdarrenr/* ------------------------------------------------------------------------ */
6496255332Scystatic void
6497255332Scyipf_nat_mssclamp(tcp, maxmss, fin, csump)
6498255332Scy	tcphdr_t *tcp;
6499255332Scy	u_32_t maxmss;
6500255332Scy	fr_info_t *fin;
6501255332Scy	u_short *csump;
6502110916Sdarrenr{
6503110916Sdarrenr	u_char *cp, *ep, opt;
6504110916Sdarrenr	int hlen, advance;
6505110916Sdarrenr	u_32_t mss, sumd;
6506110916Sdarrenr
6507145522Sdarrenr	hlen = TCP_OFF(tcp) << 2;
6508110916Sdarrenr	if (hlen > sizeof(*tcp)) {
6509110916Sdarrenr		cp = (u_char *)tcp + sizeof(*tcp);
6510110916Sdarrenr		ep = (u_char *)tcp + hlen;
6511110916Sdarrenr
6512110916Sdarrenr		while (cp < ep) {
6513110916Sdarrenr			opt = cp[0];
6514110916Sdarrenr			if (opt == TCPOPT_EOL)
6515110916Sdarrenr				break;
6516110916Sdarrenr			else if (opt == TCPOPT_NOP) {
6517110916Sdarrenr				cp++;
6518110916Sdarrenr				continue;
6519110916Sdarrenr			}
6520145522Sdarrenr
6521145522Sdarrenr			if (cp + 1 >= ep)
6522110916Sdarrenr				break;
6523110916Sdarrenr			advance = cp[1];
6524145522Sdarrenr			if ((cp + advance > ep) || (advance <= 0))
6525110916Sdarrenr				break;
6526145522Sdarrenr			switch (opt)
6527145522Sdarrenr			{
6528110916Sdarrenr			case TCPOPT_MAXSEG:
6529110916Sdarrenr				if (advance != 4)
6530110916Sdarrenr					break;
6531145522Sdarrenr				mss = cp[2] * 256 + cp[3];
6532110916Sdarrenr				if (mss > maxmss) {
6533145522Sdarrenr					cp[2] = maxmss / 256;
6534145522Sdarrenr					cp[3] = maxmss & 0xff;
6535110916Sdarrenr					CALC_SUMD(mss, maxmss, sumd);
6536255332Scy					ipf_fix_outcksum(0, csump, sumd, 0);
6537110916Sdarrenr				}
6538110916Sdarrenr				break;
6539110916Sdarrenr			default:
6540110916Sdarrenr				/* ignore unknown options */
6541110916Sdarrenr				break;
6542110916Sdarrenr			}
6543145522Sdarrenr
6544145522Sdarrenr			cp += advance;
6545145522Sdarrenr		}
6546145522Sdarrenr	}
6547145522Sdarrenr}
6548145522Sdarrenr
6549145522Sdarrenr
6550145522Sdarrenr/* ------------------------------------------------------------------------ */
6551255332Scy/* Function:    ipf_nat_setqueue                                            */
6552145522Sdarrenr/* Returns:     Nil                                                         */
6553255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6554255332Scy/*              softn(I) - pointer to NAT context structure                 */
6555255332Scy/*              nat(I)- pointer to NAT structure                            */
6556145522Sdarrenr/* Locks:       ipf_nat (read or write)                                     */
6557145522Sdarrenr/*                                                                          */
6558145522Sdarrenr/* Put the NAT entry on its default queue entry, using rev as a helped in   */
6559145522Sdarrenr/* determining which queue it should be placed on.                          */
6560145522Sdarrenr/* ------------------------------------------------------------------------ */
6561255332Scyvoid
6562255332Scyipf_nat_setqueue(softc, softn, nat)
6563255332Scy	ipf_main_softc_t *softc;
6564255332Scy	ipf_nat_softc_t *softn;
6565255332Scy	nat_t *nat;
6566145522Sdarrenr{
6567145522Sdarrenr	ipftq_t *oifq, *nifq;
6568255332Scy	int rev = nat->nat_rev;
6569145522Sdarrenr
6570145522Sdarrenr	if (nat->nat_ptr != NULL)
6571145522Sdarrenr		nifq = nat->nat_ptr->in_tqehead[rev];
6572145522Sdarrenr	else
6573145522Sdarrenr		nifq = NULL;
6574145522Sdarrenr
6575145522Sdarrenr	if (nifq == NULL) {
6576255332Scy		switch (nat->nat_pr[0])
6577145522Sdarrenr		{
6578145522Sdarrenr		case IPPROTO_UDP :
6579255332Scy			nifq = &softn->ipf_nat_udptq;
6580145522Sdarrenr			break;
6581145522Sdarrenr		case IPPROTO_ICMP :
6582255332Scy			nifq = &softn->ipf_nat_icmptq;
6583145522Sdarrenr			break;
6584145522Sdarrenr		case IPPROTO_TCP :
6585255332Scy			nifq = softn->ipf_nat_tcptq +
6586255332Scy			       nat->nat_tqe.tqe_state[rev];
6587145522Sdarrenr			break;
6588145522Sdarrenr		default :
6589255332Scy			nifq = &softn->ipf_nat_iptq;
6590145522Sdarrenr			break;
6591145522Sdarrenr		}
6592145522Sdarrenr	}
6593145522Sdarrenr
6594145522Sdarrenr	oifq = nat->nat_tqe.tqe_ifq;
6595145522Sdarrenr	/*
6596145522Sdarrenr	 * If it's currently on a timeout queue, move it from one queue to
6597145522Sdarrenr	 * another, else put it on the end of the newly determined queue.
6598145522Sdarrenr	 */
6599145522Sdarrenr	if (oifq != NULL)
6600255332Scy		ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq);
6601145522Sdarrenr	else
6602255332Scy		ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat);
6603145522Sdarrenr	return;
6604145522Sdarrenr}
6605170268Sdarrenr
6606170268Sdarrenr
6607170268Sdarrenr/* ------------------------------------------------------------------------ */
6608170268Sdarrenr/* Function:    nat_getnext                                                 */
6609170268Sdarrenr/* Returns:     int - 0 == ok, else error                                   */
6610255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6611255332Scy/*              t(I)   - pointer to ipftoken structure                      */
6612170268Sdarrenr/*              itp(I) - pointer to ipfgeniter_t structure                  */
6613170268Sdarrenr/*                                                                          */
6614170268Sdarrenr/* Fetch the next nat/ipnat structure pointer from the linked list and      */
6615170268Sdarrenr/* copy it out to the storage space pointed to by itp_data.  The next item  */
6616170268Sdarrenr/* in the list to look at is put back in the ipftoken struture.             */
6617170268Sdarrenr/* ------------------------------------------------------------------------ */
6618255332Scystatic int
6619255332Scyipf_nat_getnext(softc, t, itp, objp)
6620255332Scy	ipf_main_softc_t *softc;
6621255332Scy	ipftoken_t *t;
6622255332Scy	ipfgeniter_t *itp;
6623255332Scy	ipfobj_t *objp;
6624170268Sdarrenr{
6625255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6626170268Sdarrenr	hostmap_t *hm, *nexthm = NULL, zerohm;
6627170268Sdarrenr	ipnat_t *ipn, *nextipnat = NULL, zeroipn;
6628170268Sdarrenr	nat_t *nat, *nextnat = NULL, zeronat;
6629255332Scy	int error = 0;
6630255332Scy	void *nnext;
6631170268Sdarrenr
6632255332Scy	if (itp->igi_nitems != 1) {
6633255332Scy		IPFERROR(60075);
6634172776Sdarrenr		return ENOSPC;
6635255332Scy	}
6636170268Sdarrenr
6637255332Scy	READ_ENTER(&softc->ipf_nat);
6638170268Sdarrenr
6639170268Sdarrenr	switch (itp->igi_type)
6640170268Sdarrenr	{
6641170268Sdarrenr	case IPFGENITER_HOSTMAP :
6642170268Sdarrenr		hm = t->ipt_data;
6643170268Sdarrenr		if (hm == NULL) {
6644255332Scy			nexthm = softn->ipf_hm_maplist;
6645170268Sdarrenr		} else {
6646170268Sdarrenr			nexthm = hm->hm_next;
6647170268Sdarrenr		}
6648255332Scy		if (nexthm != NULL) {
6649255332Scy			ATOMIC_INC32(nexthm->hm_ref);
6650255332Scy			t->ipt_data = nexthm;
6651255332Scy		} else {
6652255332Scy			bzero(&zerohm, sizeof(zerohm));
6653255332Scy			nexthm = &zerohm;
6654255332Scy			t->ipt_data = NULL;
6655255332Scy		}
6656255332Scy		nnext = nexthm->hm_next;
6657170268Sdarrenr		break;
6658170268Sdarrenr
6659170268Sdarrenr	case IPFGENITER_IPNAT :
6660170268Sdarrenr		ipn = t->ipt_data;
6661170268Sdarrenr		if (ipn == NULL) {
6662255332Scy			nextipnat = softn->ipf_nat_list;
6663170268Sdarrenr		} else {
6664170268Sdarrenr			nextipnat = ipn->in_next;
6665170268Sdarrenr		}
6666255332Scy		if (nextipnat != NULL) {
6667255332Scy			ATOMIC_INC32(nextipnat->in_use);
6668255332Scy			t->ipt_data = nextipnat;
6669255332Scy		} else {
6670255332Scy			bzero(&zeroipn, sizeof(zeroipn));
6671255332Scy			nextipnat = &zeroipn;
6672255332Scy			t->ipt_data = NULL;
6673255332Scy		}
6674255332Scy		nnext = nextipnat->in_next;
6675170268Sdarrenr		break;
6676170268Sdarrenr
6677170268Sdarrenr	case IPFGENITER_NAT :
6678170268Sdarrenr		nat = t->ipt_data;
6679170268Sdarrenr		if (nat == NULL) {
6680255332Scy			nextnat = softn->ipf_nat_instances;
6681170268Sdarrenr		} else {
6682170268Sdarrenr			nextnat = nat->nat_next;
6683170268Sdarrenr		}
6684255332Scy		if (nextnat != NULL) {
6685255332Scy			MUTEX_ENTER(&nextnat->nat_lock);
6686255332Scy			nextnat->nat_ref++;
6687255332Scy			MUTEX_EXIT(&nextnat->nat_lock);
6688255332Scy			t->ipt_data = nextnat;
6689255332Scy		} else {
6690255332Scy			bzero(&zeronat, sizeof(zeronat));
6691255332Scy			nextnat = &zeronat;
6692255332Scy			t->ipt_data = NULL;
6693255332Scy		}
6694255332Scy		nnext = nextnat->nat_next;
6695170268Sdarrenr		break;
6696255332Scy
6697170268Sdarrenr	default :
6698255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
6699255332Scy		IPFERROR(60055);
6700170268Sdarrenr		return EINVAL;
6701170268Sdarrenr	}
6702170268Sdarrenr
6703255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
6704170268Sdarrenr
6705255332Scy	objp->ipfo_ptr = itp->igi_data;
6706170268Sdarrenr
6707172776Sdarrenr	switch (itp->igi_type)
6708172776Sdarrenr	{
6709172776Sdarrenr	case IPFGENITER_HOSTMAP :
6710255332Scy		error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm));
6711255332Scy		if (error != 0) {
6712255332Scy			IPFERROR(60049);
6713255332Scy			error = EFAULT;
6714255332Scy		}
6715172776Sdarrenr		if (hm != NULL) {
6716255332Scy			WRITE_ENTER(&softc->ipf_nat);
6717255332Scy			ipf_nat_hostmapdel(softc, &hm);
6718255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
6719172776Sdarrenr		}
6720172776Sdarrenr		break;
6721255332Scy
6722172776Sdarrenr	case IPFGENITER_IPNAT :
6723255332Scy		objp->ipfo_size = nextipnat->in_size;
6724255332Scy		objp->ipfo_type = IPFOBJ_IPNAT;
6725255332Scy		error = ipf_outobjk(softc, objp, nextipnat);
6726172776Sdarrenr		if (ipn != NULL) {
6727255332Scy			WRITE_ENTER(&softc->ipf_nat);
6728255332Scy			ipf_nat_rule_deref(softc, &ipn);
6729255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
6730172776Sdarrenr		}
6731172776Sdarrenr		break;
6732172776Sdarrenr
6733170268Sdarrenr	case IPFGENITER_NAT :
6734255332Scy		objp->ipfo_size = sizeof(nat_t);
6735255332Scy		objp->ipfo_type = IPFOBJ_NAT;
6736255332Scy		error = ipf_outobjk(softc, objp, nextnat);
6737255332Scy		if (nat != NULL)
6738255332Scy			ipf_nat_deref(softc, &nat);
6739170268Sdarrenr
6740170268Sdarrenr		break;
6741170268Sdarrenr	}
6742170268Sdarrenr
6743255332Scy	if (nnext == NULL)
6744255332Scy		ipf_token_mark_complete(t);
6745255332Scy
6746170268Sdarrenr	return error;
6747170268Sdarrenr}
6748170268Sdarrenr
6749170268Sdarrenr
6750170268Sdarrenr/* ------------------------------------------------------------------------ */
6751170268Sdarrenr/* Function:    nat_extraflush                                              */
6752170268Sdarrenr/* Returns:     int - 0 == success, -1 == failure                           */
6753255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6754255332Scy/*              softn(I) - pointer to NAT context structure                 */
6755255332Scy/*              which(I) - how to flush the active NAT table                */
6756170268Sdarrenr/* Write Locks: ipf_nat                                                     */
6757170268Sdarrenr/*                                                                          */
6758170268Sdarrenr/* Flush nat tables.  Three actions currently defined:                      */
6759170268Sdarrenr/* which == 0 : flush all nat table entries                                 */
6760170268Sdarrenr/* which == 1 : flush TCP connections which have started to close but are   */
6761170268Sdarrenr/*	      stuck for some reason.                                        */
6762170268Sdarrenr/* which == 2 : flush TCP connections which have been idle for a long time, */
6763170268Sdarrenr/*	      starting at > 4 days idle and working back in successive half-*/
6764170268Sdarrenr/*	      days to at most 12 hours old.  If this fails to free enough   */
6765170268Sdarrenr/*            slots then work backwards in half hour slots to 30 minutes.   */
6766170268Sdarrenr/*            If that too fails, then work backwards in 30 second intervals */
6767170268Sdarrenr/*            for the last 30 minutes to at worst 30 seconds idle.          */
6768170268Sdarrenr/* ------------------------------------------------------------------------ */
6769255332Scystatic int
6770255332Scyipf_nat_extraflush(softc, softn, which)
6771255332Scy	ipf_main_softc_t *softc;
6772255332Scy	ipf_nat_softc_t *softn;
6773255332Scy	int which;
6774170268Sdarrenr{
6775170268Sdarrenr	nat_t *nat, **natp;
6776170268Sdarrenr	ipftqent_t *tqn;
6777255332Scy	ipftq_t *ifq;
6778170268Sdarrenr	int removed;
6779170268Sdarrenr	SPL_INT(s);
6780170268Sdarrenr
6781170268Sdarrenr	removed = 0;
6782170268Sdarrenr
6783170268Sdarrenr	SPL_NET(s);
6784170268Sdarrenr	switch (which)
6785170268Sdarrenr	{
6786170268Sdarrenr	case 0 :
6787255332Scy		softn->ipf_nat_stats.ns_flush_all++;
6788170268Sdarrenr		/*
6789170268Sdarrenr		 * Style 0 flush removes everything...
6790170268Sdarrenr		 */
6791255332Scy		for (natp = &softn->ipf_nat_instances;
6792255332Scy		     ((nat = *natp) != NULL); ) {
6793255332Scy			ipf_nat_delete(softc, nat, NL_FLUSH);
6794170268Sdarrenr			removed++;
6795170268Sdarrenr		}
6796170268Sdarrenr		break;
6797170268Sdarrenr
6798170268Sdarrenr	case 1 :
6799255332Scy		softn->ipf_nat_stats.ns_flush_closing++;
6800170268Sdarrenr		/*
6801170268Sdarrenr		 * Since we're only interested in things that are closing,
6802170268Sdarrenr		 * we can start with the appropriate timeout queue.
6803170268Sdarrenr		 */
6804255332Scy		for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT;
6805255332Scy		     ifq != NULL; ifq = ifq->ifq_next) {
6806170268Sdarrenr
6807170268Sdarrenr			for (tqn = ifq->ifq_head; tqn != NULL; ) {
6808170268Sdarrenr				nat = tqn->tqe_parent;
6809170268Sdarrenr				tqn = tqn->tqe_next;
6810255332Scy				if (nat->nat_pr[0] != IPPROTO_TCP ||
6811255332Scy				    nat->nat_pr[1] != IPPROTO_TCP)
6812170268Sdarrenr					break;
6813255332Scy				ipf_nat_delete(softc, nat, NL_EXPIRE);
6814170268Sdarrenr				removed++;
6815170268Sdarrenr			}
6816170268Sdarrenr		}
6817170268Sdarrenr
6818170268Sdarrenr		/*
6819170268Sdarrenr		 * Also need to look through the user defined queues.
6820170268Sdarrenr		 */
6821255332Scy		for (ifq = softn->ipf_nat_utqe; ifq != NULL;
6822255332Scy		     ifq = ifq->ifq_next) {
6823170268Sdarrenr			for (tqn = ifq->ifq_head; tqn != NULL; ) {
6824170268Sdarrenr				nat = tqn->tqe_parent;
6825170268Sdarrenr				tqn = tqn->tqe_next;
6826255332Scy				if (nat->nat_pr[0] != IPPROTO_TCP ||
6827255332Scy				    nat->nat_pr[1] != IPPROTO_TCP)
6828170268Sdarrenr					continue;
6829170268Sdarrenr
6830170268Sdarrenr				if ((nat->nat_tcpstate[0] >
6831170268Sdarrenr				     IPF_TCPS_ESTABLISHED) &&
6832170268Sdarrenr				    (nat->nat_tcpstate[1] >
6833170268Sdarrenr				     IPF_TCPS_ESTABLISHED)) {
6834255332Scy					ipf_nat_delete(softc, nat, NL_EXPIRE);
6835170268Sdarrenr					removed++;
6836170268Sdarrenr				}
6837170268Sdarrenr			}
6838170268Sdarrenr		}
6839170268Sdarrenr		break;
6840170268Sdarrenr
6841170268Sdarrenr		/*
6842170268Sdarrenr		 * Args 5-11 correspond to flushing those particular states
6843170268Sdarrenr		 * for TCP connections.
6844170268Sdarrenr		 */
6845170268Sdarrenr	case IPF_TCPS_CLOSE_WAIT :
6846170268Sdarrenr	case IPF_TCPS_FIN_WAIT_1 :
6847170268Sdarrenr	case IPF_TCPS_CLOSING :
6848170268Sdarrenr	case IPF_TCPS_LAST_ACK :
6849170268Sdarrenr	case IPF_TCPS_FIN_WAIT_2 :
6850170268Sdarrenr	case IPF_TCPS_TIME_WAIT :
6851170268Sdarrenr	case IPF_TCPS_CLOSED :
6852255332Scy		softn->ipf_nat_stats.ns_flush_state++;
6853255332Scy		tqn = softn->ipf_nat_tcptq[which].ifq_head;
6854170268Sdarrenr		while (tqn != NULL) {
6855170268Sdarrenr			nat = tqn->tqe_parent;
6856170268Sdarrenr			tqn = tqn->tqe_next;
6857255332Scy			ipf_nat_delete(softc, nat, NL_FLUSH);
6858170268Sdarrenr			removed++;
6859170268Sdarrenr		}
6860170268Sdarrenr		break;
6861255332Scy
6862170268Sdarrenr	default :
6863170268Sdarrenr		if (which < 30)
6864170268Sdarrenr			break;
6865255332Scy
6866255332Scy		softn->ipf_nat_stats.ns_flush_timeout++;
6867170268Sdarrenr		/*
6868170268Sdarrenr		 * Take a large arbitrary number to mean the number of seconds
6869170268Sdarrenr		 * for which which consider to be the maximum value we'll allow
6870170268Sdarrenr		 * the expiration to be.
6871170268Sdarrenr		 */
6872170268Sdarrenr		which = IPF_TTLVAL(which);
6873255332Scy		for (natp = &softn->ipf_nat_instances;
6874255332Scy		     ((nat = *natp) != NULL); ) {
6875255332Scy			if (softc->ipf_ticks - nat->nat_touched > which) {
6876255332Scy				ipf_nat_delete(softc, nat, NL_FLUSH);
6877170268Sdarrenr				removed++;
6878170268Sdarrenr			} else
6879170268Sdarrenr				natp = &nat->nat_next;
6880170268Sdarrenr		}
6881170268Sdarrenr		break;
6882170268Sdarrenr	}
6883170268Sdarrenr
6884170268Sdarrenr	if (which != 2) {
6885170268Sdarrenr		SPL_X(s);
6886170268Sdarrenr		return removed;
6887170268Sdarrenr	}
6888170268Sdarrenr
6889255332Scy	softn->ipf_nat_stats.ns_flush_queue++;
6890255332Scy
6891170268Sdarrenr	/*
6892255332Scy	 * Asked to remove inactive entries because the table is full, try
6893255332Scy	 * again, 3 times, if first attempt failed with a different criteria
6894255332Scy	 * each time.  The order tried in must be in decreasing age.
6895255332Scy	 * Another alternative is to implement random drop and drop N entries
6896255332Scy	 * at random until N have been freed up.
6897170268Sdarrenr	 */
6898255332Scy	if (softc->ipf_ticks - softn->ipf_nat_last_force_flush >
6899255332Scy	    IPF_TTLVAL(5)) {
6900255332Scy		softn->ipf_nat_last_force_flush = softc->ipf_ticks;
6901255332Scy
6902255332Scy		removed = ipf_queueflush(softc, ipf_nat_flush_entry,
6903255332Scy					 softn->ipf_nat_tcptq,
6904255332Scy					 softn->ipf_nat_utqe,
6905255332Scy					 &softn->ipf_nat_stats.ns_active,
6906255332Scy					 softn->ipf_nat_table_sz,
6907255332Scy					 softn->ipf_nat_table_wm_low);
6908170268Sdarrenr	}
6909170268Sdarrenr
6910170268Sdarrenr	SPL_X(s);
6911170268Sdarrenr	return removed;
6912170268Sdarrenr}
6913170268Sdarrenr
6914170268Sdarrenr
6915170268Sdarrenr/* ------------------------------------------------------------------------ */
6916255332Scy/* Function:    ipf_nat_flush_entry                                         */
6917170268Sdarrenr/* Returns:     0 - always succeeds                                         */
6918255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6919255332Scy/*              entry(I) - pointer to NAT entry                             */
6920170268Sdarrenr/* Write Locks: ipf_nat                                                     */
6921170268Sdarrenr/*                                                                          */
6922170268Sdarrenr/* This function is a stepping stone between ipf_queueflush() and           */
6923170268Sdarrenr/* nat_dlete().  It is used so we can provide a uniform interface via the   */
6924170268Sdarrenr/* ipf_queueflush() function.  Since the nat_delete() function returns void */
6925170268Sdarrenr/* we translate that to mean it always succeeds in deleting something.      */
6926170268Sdarrenr/* ------------------------------------------------------------------------ */
6927255332Scystatic int
6928255332Scyipf_nat_flush_entry(softc, entry)
6929255332Scy	ipf_main_softc_t *softc;
6930255332Scy	void *entry;
6931170268Sdarrenr{
6932255332Scy	ipf_nat_delete(softc, entry, NL_FLUSH);
6933170268Sdarrenr	return 0;
6934170268Sdarrenr}
6935172776Sdarrenr
6936172776Sdarrenr
6937172776Sdarrenr/* ------------------------------------------------------------------------ */
6938255332Scy/* Function:    ipf_nat_iterator                                            */
6939255332Scy/* Returns:     int - 0 == ok, else error                                   */
6940255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6941255332Scy/*              token(I) - pointer to ipftoken structure                    */
6942255332Scy/*              itp(I)   - pointer to ipfgeniter_t structure                */
6943255332Scy/*              obj(I)   - pointer to data description structure            */
6944255332Scy/*                                                                          */
6945255332Scy/* This function acts as a handler for the SIOCGENITER ioctls that use a    */
6946255332Scy/* generic structure to iterate through a list.  There are three different  */
6947255332Scy/* linked lists of NAT related information to go through: NAT rules, active */
6948255332Scy/* NAT mappings and the NAT fragment cache.                                 */
6949255332Scy/* ------------------------------------------------------------------------ */
6950255332Scystatic int
6951255332Scyipf_nat_iterator(softc, token, itp, obj)
6952255332Scy	ipf_main_softc_t *softc;
6953255332Scy	ipftoken_t *token;
6954255332Scy	ipfgeniter_t *itp;
6955255332Scy	ipfobj_t *obj;
6956255332Scy{
6957255332Scy	int error;
6958255332Scy
6959255332Scy	if (itp->igi_data == NULL) {
6960255332Scy		IPFERROR(60052);
6961255332Scy		return EFAULT;
6962255332Scy	}
6963255332Scy
6964255332Scy	switch (itp->igi_type)
6965255332Scy	{
6966255332Scy	case IPFGENITER_HOSTMAP :
6967255332Scy	case IPFGENITER_IPNAT :
6968255332Scy	case IPFGENITER_NAT :
6969255332Scy		error = ipf_nat_getnext(softc, token, itp, obj);
6970255332Scy		break;
6971255332Scy
6972255332Scy	case IPFGENITER_NATFRAG :
6973255332Scy		error = ipf_frag_nat_next(softc, token, itp);
6974255332Scy		break;
6975255332Scy	default :
6976255332Scy		IPFERROR(60053);
6977255332Scy		error = EINVAL;
6978255332Scy		break;
6979255332Scy	}
6980255332Scy
6981255332Scy	return error;
6982255332Scy}
6983255332Scy
6984255332Scy
6985255332Scy/* ------------------------------------------------------------------------ */
6986255332Scy/* Function:    ipf_nat_setpending                                          */
6987255332Scy/* Returns:     Nil                                                         */
6988255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6989255332Scy/*              nat(I)   - pointer to NAT structure                         */
6990255332Scy/* Locks:       ipf_nat (read or write)                                     */
6991255332Scy/*                                                                          */
6992255332Scy/* Put the NAT entry on to the pending queue - this queue has a very short  */
6993255332Scy/* lifetime where items are put that can't be deleted straight away because */
6994255332Scy/* of locking issues but we want to delete them ASAP, anyway.  In calling   */
6995255332Scy/* this function, it is assumed that the owner (if there is one, as shown   */
6996255332Scy/* by nat_me) is no longer interested in it.                                */
6997255332Scy/* ------------------------------------------------------------------------ */
6998255332Scyvoid
6999255332Scyipf_nat_setpending(softc, nat)
7000255332Scy	ipf_main_softc_t *softc;
7001255332Scy	nat_t *nat;
7002255332Scy{
7003255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7004255332Scy	ipftq_t *oifq;
7005255332Scy
7006255332Scy	oifq = nat->nat_tqe.tqe_ifq;
7007255332Scy	if (oifq != NULL)
7008255332Scy		ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq,
7009255332Scy			      &softn->ipf_nat_pending);
7010255332Scy	else
7011255332Scy		ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe,
7012255332Scy				&softn->ipf_nat_pending, nat);
7013255332Scy
7014255332Scy	if (nat->nat_me != NULL) {
7015255332Scy		*nat->nat_me = NULL;
7016255332Scy		nat->nat_me = NULL;
7017255332Scy		nat->nat_ref--;
7018255332Scy		ASSERT(nat->nat_ref >= 0);
7019255332Scy	}
7020255332Scy}
7021255332Scy
7022255332Scy
7023255332Scy/* ------------------------------------------------------------------------ */
7024255332Scy/* Function:    nat_newrewrite                                              */
7025255332Scy/* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
7026255332Scy/*                    allow rule to be moved if IPN_ROUNDR is set.          */
7027255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7028255332Scy/*              nat(I) - pointer to NAT entry                               */
7029255332Scy/*              ni(I)  - pointer to structure with misc. information needed */
7030255332Scy/*                       to create new NAT entry.                           */
7031255332Scy/* Write Lock:  ipf_nat                                                     */
7032255332Scy/*                                                                          */
7033255332Scy/* This function is responsible for setting up an active NAT session where  */
7034255332Scy/* we are changing both the source and destination parameters at the same   */
7035255332Scy/* time.  The loop in here works differently to elsewhere - each iteration  */
7036255332Scy/* is responsible for changing a single parameter that can be incremented.  */
7037255332Scy/* So one pass may increase the source IP#, next source port, next dest. IP#*/
7038255332Scy/* and the last destination port for a total of 4 iterations to try each.   */
7039255332Scy/* This is done to try and exhaustively use the translation space available.*/
7040255332Scy/* ------------------------------------------------------------------------ */
7041255332Scystatic int
7042255332Scyipf_nat_newrewrite(fin, nat, nai)
7043255332Scy	fr_info_t *fin;
7044255332Scy	nat_t *nat;
7045255332Scy	natinfo_t *nai;
7046255332Scy{
7047255332Scy	int src_search = 1;
7048255332Scy	int dst_search = 1;
7049255332Scy	fr_info_t frnat;
7050255332Scy	u_32_t flags;
7051255332Scy	u_short swap;
7052255332Scy	ipnat_t *np;
7053255332Scy	nat_t *natl;
7054255332Scy	int l = 0;
7055255332Scy	int changed;
7056255332Scy
7057255332Scy	natl = NULL;
7058255332Scy	changed = -1;
7059255332Scy	np = nai->nai_np;
7060255332Scy	flags = nat->nat_flags;
7061255332Scy	bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
7062255332Scy
7063255332Scy	nat->nat_hm = NULL;
7064255332Scy
7065255332Scy	do {
7066255332Scy		changed = -1;
7067255332Scy		/* TRACE (l, src_search, dst_search, np) */
7068255332Scy
7069255332Scy		if ((src_search == 0) && (np->in_spnext == 0) &&
7070255332Scy		    (dst_search == 0) && (np->in_dpnext == 0)) {
7071255332Scy			if (l > 0)
7072255332Scy				return -1;
7073255332Scy		}
7074255332Scy
7075255332Scy		/*
7076255332Scy		 * Find a new source address
7077255332Scy		 */
7078255332Scy		if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr,
7079255332Scy				     &frnat.fin_saddr) == -1) {
7080255332Scy			return -1;
7081255332Scy		}
7082255332Scy
7083255332Scy		if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) {
7084255332Scy			src_search = 0;
7085255332Scy			if (np->in_stepnext == 0)
7086255332Scy				np->in_stepnext = 1;
7087255332Scy
7088255332Scy		} else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) {
7089255332Scy			src_search = 0;
7090255332Scy			if (np->in_stepnext == 0)
7091255332Scy				np->in_stepnext = 1;
7092255332Scy
7093255332Scy		} else if (np->in_nsrcmsk == 0xffffffff) {
7094255332Scy			src_search = 0;
7095255332Scy			if (np->in_stepnext == 0)
7096255332Scy				np->in_stepnext = 1;
7097255332Scy
7098255332Scy		} else if (np->in_nsrcmsk != 0xffffffff) {
7099255332Scy			if (np->in_stepnext == 0 && changed == -1) {
7100255332Scy				np->in_snip++;
7101255332Scy				np->in_stepnext++;
7102255332Scy				changed = 0;
7103255332Scy			}
7104255332Scy		}
7105255332Scy
7106255332Scy		if ((flags & IPN_TCPUDPICMP) != 0) {
7107255332Scy			if (np->in_spnext != 0)
7108255332Scy				frnat.fin_data[0] = np->in_spnext;
7109255332Scy
7110255332Scy			/*
7111255332Scy			 * Standard port translation.  Select next port.
7112255332Scy			 */
7113255332Scy			if ((flags & IPN_FIXEDSPORT) != 0) {
7114255332Scy				np->in_stepnext = 2;
7115255332Scy			} else if ((np->in_stepnext == 1) &&
7116255332Scy				   (changed == -1) && (natl != NULL)) {
7117255332Scy				np->in_spnext++;
7118255332Scy				np->in_stepnext++;
7119255332Scy				changed = 1;
7120255332Scy				if (np->in_spnext > np->in_spmax)
7121255332Scy					np->in_spnext = np->in_spmin;
7122255332Scy			}
7123255332Scy		} else {
7124255332Scy			np->in_stepnext = 2;
7125255332Scy		}
7126255332Scy		np->in_stepnext &= 0x3;
7127255332Scy
7128255332Scy		/*
7129255332Scy		 * Find a new destination address
7130255332Scy		 */
7131255332Scy		/* TRACE (fin, np, l, frnat) */
7132255332Scy
7133255332Scy		if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr,
7134255332Scy				     &frnat.fin_daddr) == -1)
7135255332Scy			return -1;
7136255332Scy		if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) {
7137255332Scy			dst_search = 0;
7138255332Scy			if (np->in_stepnext == 2)
7139255332Scy				np->in_stepnext = 3;
7140255332Scy
7141255332Scy		} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) {
7142255332Scy			dst_search = 0;
7143255332Scy			if (np->in_stepnext == 2)
7144255332Scy				np->in_stepnext = 3;
7145255332Scy
7146255332Scy		} else if (np->in_ndstmsk == 0xffffffff) {
7147255332Scy			dst_search = 0;
7148255332Scy			if (np->in_stepnext == 2)
7149255332Scy				np->in_stepnext = 3;
7150255332Scy
7151255332Scy		} else if (np->in_ndstmsk != 0xffffffff) {
7152255332Scy			if ((np->in_stepnext == 2) && (changed == -1) &&
7153255332Scy			    (natl != NULL)) {
7154255332Scy				changed = 2;
7155255332Scy				np->in_stepnext++;
7156255332Scy				np->in_dnip++;
7157255332Scy			}
7158255332Scy		}
7159255332Scy
7160255332Scy		if ((flags & IPN_TCPUDPICMP) != 0) {
7161255332Scy			if (np->in_dpnext != 0)
7162255332Scy				frnat.fin_data[1] = np->in_dpnext;
7163255332Scy
7164255332Scy			/*
7165255332Scy			 * Standard port translation.  Select next port.
7166255332Scy			 */
7167255332Scy			if ((flags & IPN_FIXEDDPORT) != 0) {
7168255332Scy				np->in_stepnext = 0;
7169255332Scy			} else if (np->in_stepnext == 3 && changed == -1) {
7170255332Scy				np->in_dpnext++;
7171255332Scy				np->in_stepnext++;
7172255332Scy				changed = 3;
7173255332Scy				if (np->in_dpnext > np->in_dpmax)
7174255332Scy					np->in_dpnext = np->in_dpmin;
7175255332Scy			}
7176255332Scy		} else {
7177255332Scy			if (np->in_stepnext == 3)
7178255332Scy				np->in_stepnext = 0;
7179255332Scy		}
7180255332Scy
7181255332Scy		/* TRACE (frnat) */
7182255332Scy
7183255332Scy		/*
7184255332Scy		 * Here we do a lookup of the connection as seen from
7185255332Scy		 * the outside.  If an IP# pair already exists, try
7186255332Scy		 * again.  So if you have A->B becomes C->B, you can
7187255332Scy		 * also have D->E become C->E but not D->B causing
7188255332Scy		 * another C->B.  Also take protocol and ports into
7189255332Scy		 * account when determining whether a pre-existing
7190255332Scy		 * NAT setup will cause an external conflict where
7191255332Scy		 * this is appropriate.
7192255332Scy		 *
7193255332Scy		 * fin_data[] is swapped around because we are doing a
7194255332Scy		 * lookup of the packet is if it were moving in the opposite
7195255332Scy		 * direction of the one we are working with now.
7196255332Scy		 */
7197255332Scy		if (flags & IPN_TCPUDP) {
7198255332Scy			swap = frnat.fin_data[0];
7199255332Scy			frnat.fin_data[0] = frnat.fin_data[1];
7200255332Scy			frnat.fin_data[1] = swap;
7201255332Scy		}
7202255332Scy		if (fin->fin_out == 1) {
7203255332Scy			natl = ipf_nat_inlookup(&frnat,
7204255332Scy						flags & ~(SI_WILDP|NAT_SEARCH),
7205255332Scy						(u_int)frnat.fin_p,
7206255332Scy						frnat.fin_dst, frnat.fin_src);
7207255332Scy
7208255332Scy		} else {
7209255332Scy			natl = ipf_nat_outlookup(&frnat,
7210255332Scy						 flags & ~(SI_WILDP|NAT_SEARCH),
7211255332Scy						 (u_int)frnat.fin_p,
7212255332Scy						 frnat.fin_dst, frnat.fin_src);
7213255332Scy		}
7214255332Scy		if (flags & IPN_TCPUDP) {
7215255332Scy			swap = frnat.fin_data[0];
7216255332Scy			frnat.fin_data[0] = frnat.fin_data[1];
7217255332Scy			frnat.fin_data[1] = swap;
7218255332Scy		}
7219255332Scy
7220255332Scy		/* TRACE natl, in_stepnext, l */
7221255332Scy
7222255332Scy		if ((natl != NULL) && (l > 8))	/* XXX 8 is arbitrary */
7223255332Scy			return -1;
7224255332Scy
7225255332Scy		np->in_stepnext &= 0x3;
7226255332Scy
7227255332Scy		l++;
7228255332Scy		changed = -1;
7229255332Scy	} while (natl != NULL);
7230255332Scy
7231255332Scy	nat->nat_osrcip = fin->fin_src;
7232255332Scy	nat->nat_odstip = fin->fin_dst;
7233255332Scy	nat->nat_nsrcip = frnat.fin_src;
7234255332Scy	nat->nat_ndstip = frnat.fin_dst;
7235255332Scy
7236255332Scy	if ((flags & IPN_TCPUDP) != 0) {
7237255332Scy		nat->nat_osport = htons(fin->fin_data[0]);
7238255332Scy		nat->nat_odport = htons(fin->fin_data[1]);
7239255332Scy		nat->nat_nsport = htons(frnat.fin_data[0]);
7240255332Scy		nat->nat_ndport = htons(frnat.fin_data[1]);
7241255332Scy	} else if ((flags & IPN_ICMPQUERY) != 0) {
7242255332Scy		nat->nat_oicmpid = fin->fin_data[1];
7243255332Scy		nat->nat_nicmpid = frnat.fin_data[1];
7244255332Scy	}
7245255332Scy
7246255332Scy	return 0;
7247255332Scy}
7248255332Scy
7249255332Scy
7250255332Scy/* ------------------------------------------------------------------------ */
7251255332Scy/* Function:    nat_newdivert                                               */
7252255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7253255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7254255332Scy/*              nat(I) - pointer to NAT entry                               */
7255255332Scy/*              ni(I)  - pointer to structure with misc. information needed */
7256255332Scy/*                       to create new NAT entry.                           */
7257255332Scy/* Write Lock:  ipf_nat                                                     */
7258255332Scy/*                                                                          */
7259255332Scy/* Create a new NAT  divert session as defined by the NAT rule.  This is    */
7260255332Scy/* somewhat different to other NAT session creation routines because we     */
7261255332Scy/* do not iterate through either port numbers or IP addresses, searching    */
7262255332Scy/* for a unique mapping, however, a complimentary duplicate check is made.  */
7263255332Scy/* ------------------------------------------------------------------------ */
7264255332Scystatic int
7265255332Scyipf_nat_newdivert(fin, nat, nai)
7266255332Scy	fr_info_t *fin;
7267255332Scy	nat_t *nat;
7268255332Scy	natinfo_t *nai;
7269255332Scy{
7270255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
7271255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7272255332Scy	fr_info_t frnat;
7273255332Scy	ipnat_t *np;
7274255332Scy	nat_t *natl;
7275255332Scy	int p;
7276255332Scy
7277255332Scy	np = nai->nai_np;
7278255332Scy	bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
7279255332Scy
7280255332Scy	nat->nat_pr[0] = 0;
7281255332Scy	nat->nat_osrcaddr = fin->fin_saddr;
7282255332Scy	nat->nat_odstaddr = fin->fin_daddr;
7283255332Scy	frnat.fin_saddr = htonl(np->in_snip);
7284255332Scy	frnat.fin_daddr = htonl(np->in_dnip);
7285255332Scy	if ((nat->nat_flags & IPN_TCPUDP) != 0) {
7286255332Scy		nat->nat_osport = htons(fin->fin_data[0]);
7287255332Scy		nat->nat_odport = htons(fin->fin_data[1]);
7288255332Scy	} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
7289255332Scy		nat->nat_oicmpid = fin->fin_data[1];
7290255332Scy	}
7291255332Scy
7292255332Scy	if (np->in_redir & NAT_DIVERTUDP) {
7293255332Scy		frnat.fin_data[0] = np->in_spnext;
7294255332Scy		frnat.fin_data[1] = np->in_dpnext;
7295255332Scy		frnat.fin_flx |= FI_TCPUDP;
7296255332Scy		p = IPPROTO_UDP;
7297255332Scy	} else {
7298255332Scy		frnat.fin_flx &= ~FI_TCPUDP;
7299255332Scy		p = IPPROTO_IPIP;
7300255332Scy	}
7301255332Scy
7302255332Scy	if (fin->fin_out == 1) {
7303255332Scy		natl = ipf_nat_inlookup(&frnat, 0, p,
7304255332Scy					frnat.fin_dst, frnat.fin_src);
7305255332Scy
7306255332Scy	} else {
7307255332Scy		natl = ipf_nat_outlookup(&frnat, 0, p,
7308255332Scy					 frnat.fin_dst, frnat.fin_src);
7309255332Scy	}
7310255332Scy
7311255332Scy	if (natl != NULL) {
7312255332Scy		NBUMPSIDED(fin->fin_out, ns_divert_exist);
7313255332Scy		return -1;
7314255332Scy	}
7315255332Scy
7316255332Scy	nat->nat_nsrcaddr = frnat.fin_saddr;
7317255332Scy	nat->nat_ndstaddr = frnat.fin_daddr;
7318255332Scy	if ((nat->nat_flags & IPN_TCPUDP) != 0) {
7319255332Scy		nat->nat_nsport = htons(frnat.fin_data[0]);
7320255332Scy		nat->nat_ndport = htons(frnat.fin_data[1]);
7321255332Scy	} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
7322255332Scy		nat->nat_nicmpid = frnat.fin_data[1];
7323255332Scy	}
7324255332Scy
7325255332Scy	nat->nat_pr[fin->fin_out] = fin->fin_p;
7326255332Scy	nat->nat_pr[1 - fin->fin_out] = p;
7327255332Scy
7328255332Scy	if (np->in_redir & NAT_REDIRECT)
7329255332Scy		nat->nat_dir = NAT_DIVERTIN;
7330255332Scy	else
7331255332Scy		nat->nat_dir = NAT_DIVERTOUT;
7332255332Scy
7333255332Scy	return 0;
7334255332Scy}
7335255332Scy
7336255332Scy
7337255332Scy/* ------------------------------------------------------------------------ */
7338255332Scy/* Function:    nat_builddivertmp                                           */
7339255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7340255332Scy/* Parameters:  softn(I) - pointer to NAT context structure                 */
7341255332Scy/*              np(I)    - pointer to a NAT rule                            */
7342255332Scy/*                                                                          */
7343255332Scy/* For divert rules, a skeleton packet representing what will be prepended  */
7344255332Scy/* to the real packet is created.  Even though we don't have the full       */
7345255332Scy/* packet here, a checksum is calculated that we update later when we       */
7346255332Scy/* fill in the final details.  At present a 0 checksum for UDP is being set */
7347255332Scy/* here because it is expected that divert will be used for localhost.      */
7348255332Scy/* ------------------------------------------------------------------------ */
7349255332Scystatic int
7350255332Scyipf_nat_builddivertmp(softn, np)
7351255332Scy	ipf_nat_softc_t *softn;
7352255332Scy	ipnat_t *np;
7353255332Scy{
7354255332Scy	udphdr_t *uh;
7355255332Scy	size_t len;
7356255332Scy	ip_t *ip;
7357255332Scy
7358255332Scy	if ((np->in_redir & NAT_DIVERTUDP) != 0)
7359255332Scy		len = sizeof(ip_t) + sizeof(udphdr_t);
7360255332Scy	else
7361255332Scy		len = sizeof(ip_t);
7362255332Scy
7363255332Scy	ALLOC_MB_T(np->in_divmp, len);
7364255332Scy	if (np->in_divmp == NULL) {
7365255332Scy		NBUMPD(ipf_nat_stats, ns_divert_build);
7366255332Scy		return -1;
7367255332Scy	}
7368255332Scy
7369255332Scy	/*
7370255332Scy	 * First, the header to get the packet diverted to the new destination
7371255332Scy	 */
7372255332Scy	ip = MTOD(np->in_divmp, ip_t *);
7373255332Scy	IP_V_A(ip, 4);
7374255332Scy	IP_HL_A(ip, 5);
7375255332Scy	ip->ip_tos = 0;
7376255332Scy	if ((np->in_redir & NAT_DIVERTUDP) != 0)
7377255332Scy		ip->ip_p = IPPROTO_UDP;
7378255332Scy	else
7379255332Scy		ip->ip_p = IPPROTO_IPIP;
7380255332Scy	ip->ip_ttl = 255;
7381255332Scy	ip->ip_off = 0;
7382255332Scy	ip->ip_sum = 0;
7383255332Scy	ip->ip_len = htons(len);
7384255332Scy	ip->ip_id = 0;
7385255332Scy	ip->ip_src.s_addr = htonl(np->in_snip);
7386255332Scy	ip->ip_dst.s_addr = htonl(np->in_dnip);
7387255332Scy	ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip));
7388255332Scy
7389255332Scy	if (np->in_redir & NAT_DIVERTUDP) {
7390255332Scy		uh = (udphdr_t *)(ip + 1);
7391255332Scy		uh->uh_sum = 0;
7392255332Scy		uh->uh_ulen = 8;
7393255332Scy		uh->uh_sport = htons(np->in_spnext);
7394255332Scy		uh->uh_dport = htons(np->in_dpnext);
7395255332Scy	}
7396255332Scy
7397255332Scy	return 0;
7398255332Scy}
7399255332Scy
7400255332Scy
7401255332Scy#define	MINDECAP	(sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t))
7402255332Scy
7403255332Scy/* ------------------------------------------------------------------------ */
7404255332Scy/* Function:    nat_decap                                                   */
7405255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7406255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7407255332Scy/*              nat(I) - pointer to current NAT session                     */
7408255332Scy/*                                                                          */
7409255332Scy/* This function is responsible for undoing a packet's encapsulation in the */
7410255332Scy/* reverse of an encap/divert rule.  After removing the outer encapsulation */
7411255332Scy/* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/
7412255332Scy/* match the "new" packet as it may still be used by IPFilter elsewhere.    */
7413255332Scy/* We use "dir" here as the basis for some of the expectations about the    */
7414255332Scy/* outer header.  If we return an error, the goal is to leave the original  */
7415255332Scy/* packet information undisturbed - this falls short at the end where we'd  */
7416255332Scy/* need to back a backup copy of "fin" - expensive.                         */
7417255332Scy/* ------------------------------------------------------------------------ */
7418255332Scystatic int
7419255332Scyipf_nat_decap(fin, nat)
7420255332Scy	fr_info_t *fin;
7421255332Scy	nat_t *nat;
7422255332Scy{
7423255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
7424255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7425255332Scy	char *hdr;
7426255332Scy	int hlen;
7427255332Scy	int skip;
7428255332Scy	mb_t *m;
7429255332Scy
7430255332Scy	if ((fin->fin_flx & FI_ICMPERR) != 0) {
7431255332Scy		/*
7432255332Scy		 * ICMP packets don't get decapsulated, instead what we need
7433255332Scy		 * to do is change the ICMP reply from including (in the data
7434255332Scy		 * portion for errors) the encapsulated packet that we sent
7435255332Scy		 * out to something that resembles the original packet prior
7436255332Scy		 * to encapsulation.  This isn't done here - all we're doing
7437255332Scy		 * here is changing the outer address to ensure that it gets
7438255332Scy		 * targetted back to the correct system.
7439255332Scy		 */
7440255332Scy
7441255332Scy		if (nat->nat_dir & NAT_OUTBOUND) {
7442255332Scy			u_32_t sum1, sum2, sumd;
7443255332Scy
7444255332Scy			sum1 = ntohl(fin->fin_daddr);
7445255332Scy			sum2 = ntohl(nat->nat_osrcaddr);
7446255332Scy			CALC_SUMD(sum1, sum2, sumd);
7447255332Scy			fin->fin_ip->ip_dst = nat->nat_osrcip;
7448255332Scy			fin->fin_daddr = nat->nat_osrcaddr;
7449255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
7450255332Scy     defined(__osf__) || defined(linux)
7451255332Scy			ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0);
7452255332Scy#endif
7453255332Scy		}
7454255332Scy		return 0;
7455255332Scy	}
7456255332Scy
7457255332Scy	m = fin->fin_m;
7458255332Scy	skip = fin->fin_hlen;
7459255332Scy
7460255332Scy	switch (nat->nat_dir)
7461255332Scy	{
7462255332Scy	case NAT_DIVERTIN :
7463255332Scy	case NAT_DIVERTOUT :
7464255332Scy		if (fin->fin_plen < MINDECAP)
7465255332Scy			return -1;
7466255332Scy		skip += sizeof(udphdr_t);
7467255332Scy		break;
7468255332Scy
7469255332Scy	case NAT_ENCAPIN :
7470255332Scy	case NAT_ENCAPOUT :
7471255332Scy		if (fin->fin_plen < (skip + sizeof(ip_t)))
7472255332Scy			return -1;
7473255332Scy		break;
7474255332Scy	default :
7475255332Scy		return -1;
7476255332Scy		/* NOTREACHED */
7477255332Scy	}
7478255332Scy
7479255332Scy	/*
7480255332Scy	 * The aim here is to keep the original packet details in "fin" for
7481255332Scy	 * as long as possible so that returning with an error is for the
7482255332Scy	 * original packet and there is little undoing work to do.
7483255332Scy	 */
7484255332Scy	if (M_LEN(m) < skip + sizeof(ip_t)) {
7485255332Scy		if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1)
7486255332Scy			return -1;
7487255332Scy	}
7488255332Scy
7489255332Scy	hdr = MTOD(fin->fin_m, char *);
7490255332Scy	fin->fin_ip = (ip_t *)(hdr + skip);
7491255332Scy	hlen = IP_HL(fin->fin_ip) << 2;
7492255332Scy
7493255332Scy	if (ipf_pr_pullup(fin, skip + hlen) == -1) {
7494255332Scy		NBUMPSIDED(fin->fin_out, ns_decap_pullup);
7495255332Scy		return -1;
7496255332Scy	}
7497255332Scy
7498255332Scy	fin->fin_hlen = hlen;
7499255332Scy	fin->fin_dlen -= skip;
7500255332Scy	fin->fin_plen -= skip;
7501255332Scy	fin->fin_ipoff += skip;
7502255332Scy
7503255332Scy	if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) {
7504255332Scy		NBUMPSIDED(fin->fin_out, ns_decap_bad);
7505255332Scy		return -1;
7506255332Scy	}
7507255332Scy
7508255332Scy	return skip;
7509255332Scy}
7510255332Scy
7511255332Scy
7512255332Scy/* ------------------------------------------------------------------------ */
7513255332Scy/* Function:    nat_nextaddr                                                */
7514255332Scy/* Returns:     int - -1 == bad input (no new address),                     */
7515255332Scy/*                     0 == success and dst has new address                 */
7516255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7517255332Scy/*              na(I)  - how to generate new address                        */
7518255332Scy/*              old(I) - original address being replaced                    */
7519255332Scy/*              dst(O) - where to put the new address                       */
7520255332Scy/* Write Lock:  ipf_nat                                                     */
7521255332Scy/*                                                                          */
7522255332Scy/* This function uses the contents of the "na" structure, in combination    */
7523255332Scy/* with "old" to produce a new address to store in "dst".  Not all of the   */
7524255332Scy/* possible uses of "na" will result in a new address.                      */
7525255332Scy/* ------------------------------------------------------------------------ */
7526255332Scystatic int
7527255332Scyipf_nat_nextaddr(fin, na, old, dst)
7528255332Scy	fr_info_t *fin;
7529255332Scy	nat_addr_t *na;
7530255332Scy	u_32_t *old, *dst;
7531255332Scy{
7532255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
7533255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7534255332Scy	u_32_t amin, amax, new;
7535255332Scy	i6addr_t newip;
7536255332Scy	int error;
7537255332Scy
7538255332Scy	new = 0;
7539255332Scy	amin = na->na_addr[0].in4.s_addr;
7540255332Scy
7541255332Scy	switch (na->na_atype)
7542255332Scy	{
7543255332Scy	case FRI_RANGE :
7544255332Scy		amax = na->na_addr[1].in4.s_addr;
7545255332Scy		break;
7546255332Scy
7547255332Scy	case FRI_NETMASKED :
7548255332Scy	case FRI_DYNAMIC :
7549255332Scy	case FRI_NORMAL :
7550255332Scy		/*
7551255332Scy		 * Compute the maximum address by adding the inverse of the
7552255332Scy		 * netmask to the minimum address.
7553255332Scy		 */
7554255332Scy		amax = ~na->na_addr[1].in4.s_addr;
7555255332Scy		amax |= amin;
7556255332Scy		break;
7557255332Scy
7558255332Scy	case FRI_LOOKUP :
7559255332Scy		break;
7560255332Scy
7561255332Scy	case FRI_BROADCAST :
7562255332Scy	case FRI_PEERADDR :
7563255332Scy	case FRI_NETWORK :
7564255332Scy	default :
7565255332Scy		return -1;
7566255332Scy	}
7567255332Scy
7568255332Scy	error = -1;
7569255332Scy
7570255332Scy	if (na->na_atype == FRI_LOOKUP) {
7571255332Scy		if (na->na_type == IPLT_DSTLIST) {
7572255332Scy			error = ipf_dstlist_select_node(fin, na->na_ptr, dst,
7573255332Scy							NULL);
7574255332Scy		} else {
7575255332Scy			NBUMPSIDE(fin->fin_out, ns_badnextaddr);
7576255332Scy		}
7577255332Scy
7578255332Scy	} else if (na->na_atype == IPLT_NONE) {
7579255332Scy		/*
7580255332Scy		 * 0/0 as the new address means leave it alone.
7581255332Scy		 */
7582255332Scy		if (na->na_addr[0].in4.s_addr == 0 &&
7583255332Scy		    na->na_addr[1].in4.s_addr == 0) {
7584255332Scy			new = *old;
7585255332Scy
7586255332Scy		/*
7587255332Scy		 * 0/32 means get the interface's address
7588255332Scy		 */
7589255332Scy		} else if (na->na_addr[0].in4.s_addr == 0 &&
7590255332Scy			   na->na_addr[1].in4.s_addr == 0xffffffff) {
7591255332Scy			if (ipf_ifpaddr(softc, 4, na->na_atype,
7592255332Scy					fin->fin_ifp, &newip, NULL) == -1) {
7593255332Scy				NBUMPSIDED(fin->fin_out, ns_ifpaddrfail);
7594255332Scy				return -1;
7595255332Scy			}
7596255332Scy			new = newip.in4.s_addr;
7597255332Scy		} else {
7598255332Scy			new = htonl(na->na_nextip);
7599255332Scy		}
7600255332Scy		*dst = new;
7601255332Scy		error = 0;
7602255332Scy
7603255332Scy	} else {
7604255332Scy		NBUMPSIDE(fin->fin_out, ns_badnextaddr);
7605255332Scy	}
7606255332Scy
7607255332Scy	return error;
7608255332Scy}
7609255332Scy
7610255332Scy
7611255332Scy/* ------------------------------------------------------------------------ */
7612255332Scy/* Function:    nat_nextaddrinit                                            */
7613255332Scy/* Returns:     int - 0 == success, else error number                       */
7614255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7615255332Scy/*              na(I)      - NAT address information for generating new addr*/
7616255332Scy/*              initial(I) - flag indicating if it is the first call for    */
7617255332Scy/*                           this "na" structure.                           */
7618255332Scy/*              ifp(I)     - network interface to derive address            */
7619255332Scy/*                           information from.                              */
7620255332Scy/*                                                                          */
7621255332Scy/* This function is expected to be called in two scenarious: when a new NAT */
7622255332Scy/* rule is loaded into the kernel and when the list of NAT rules is sync'd  */
7623255332Scy/* up with the valid network interfaces (possibly due to them changing.)    */
7624255332Scy/* To distinguish between these, the "initial" parameter is used.  If it is */
7625255332Scy/* 1 then this indicates the rule has just been reloaded and 0 for when we  */
7626255332Scy/* are updating information.  This difference is important because in       */
7627255332Scy/* instances where we are not updating address information associated with  */
7628255332Scy/* a network interface, we don't want to disturb what the "next" address to */
7629255332Scy/* come out of ipf_nat_nextaddr() will be.                                  */
7630255332Scy/* ------------------------------------------------------------------------ */
7631255332Scystatic int
7632255332Scyipf_nat_nextaddrinit(softc, base, na, initial, ifp)
7633255332Scy	ipf_main_softc_t *softc;
7634255332Scy	char *base;
7635255332Scy	nat_addr_t *na;
7636255332Scy	int initial;
7637255332Scy	void *ifp;
7638255332Scy{
7639255332Scy
7640255332Scy	switch (na->na_atype)
7641255332Scy	{
7642255332Scy	case FRI_LOOKUP :
7643255332Scy		if (na->na_subtype == 0) {
7644255332Scy			na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT,
7645255332Scy							na->na_type,
7646255332Scy							na->na_num,
7647255332Scy							&na->na_func);
7648255332Scy		} else if (na->na_subtype == 1) {
7649255332Scy			na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT,
7650255332Scy							 na->na_type,
7651255332Scy							 base + na->na_num,
7652255332Scy							 &na->na_func);
7653255332Scy		}
7654255332Scy		if (na->na_func == NULL) {
7655255332Scy			IPFERROR(60060);
7656255332Scy			return ESRCH;
7657255332Scy		}
7658255332Scy		if (na->na_ptr == NULL) {
7659255332Scy			IPFERROR(60056);
7660255332Scy			return ESRCH;
7661255332Scy		}
7662255332Scy		break;
7663255332Scy
7664255332Scy	case FRI_DYNAMIC :
7665255332Scy	case FRI_BROADCAST :
7666255332Scy	case FRI_NETWORK :
7667255332Scy	case FRI_NETMASKED :
7668255332Scy	case FRI_PEERADDR :
7669255332Scy		if (ifp != NULL)
7670255332Scy			(void )ipf_ifpaddr(softc, 4, na->na_atype, ifp,
7671255332Scy					   &na->na_addr[0], &na->na_addr[1]);
7672255332Scy		break;
7673255332Scy
7674255332Scy	case FRI_SPLIT :
7675255332Scy	case FRI_RANGE :
7676255332Scy		if (initial)
7677255332Scy			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr);
7678255332Scy		break;
7679255332Scy
7680255332Scy	case FRI_NONE :
7681255332Scy		na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr;
7682255332Scy		return 0;
7683255332Scy
7684255332Scy	case FRI_NORMAL :
7685255332Scy		na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr;
7686255332Scy		break;
7687255332Scy
7688255332Scy	default :
7689255332Scy		IPFERROR(60054);
7690255332Scy		return EINVAL;
7691255332Scy	}
7692255332Scy
7693255332Scy	if (initial && (na->na_atype == FRI_NORMAL)) {
7694255332Scy		if (na->na_addr[0].in4.s_addr == 0) {
7695255332Scy			if ((na->na_addr[1].in4.s_addr == 0xffffffff) ||
7696255332Scy			    (na->na_addr[1].in4.s_addr == 0)) {
7697255332Scy				return 0;
7698255332Scy			}
7699255332Scy		}
7700255332Scy
7701255332Scy		if (na->na_addr[1].in4.s_addr == 0xffffffff) {
7702255332Scy			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr);
7703255332Scy		} else {
7704255332Scy			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1;
7705255332Scy		}
7706255332Scy	}
7707255332Scy
7708255332Scy	return 0;
7709255332Scy}
7710255332Scy
7711255332Scy
7712255332Scy/* ------------------------------------------------------------------------ */
7713255332Scy/* Function:    ipf_nat_matchflush                                          */
7714255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7715255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7716255332Scy/*              softn(I) - pointer to NAT context structure                 */
7717255332Scy/*              nat(I)   - pointer to current NAT session                   */
7718255332Scy/*                                                                          */
7719255332Scy/* ------------------------------------------------------------------------ */
7720255332Scystatic int
7721255332Scyipf_nat_matchflush(softc, softn, data)
7722255332Scy	ipf_main_softc_t *softc;
7723255332Scy	ipf_nat_softc_t *softn;
7724255332Scy	caddr_t data;
7725255332Scy{
7726255332Scy	int *array, flushed, error;
7727255332Scy	nat_t *nat, *natnext;
7728255332Scy	ipfobj_t obj;
7729255332Scy
7730255332Scy	error = ipf_matcharray_load(softc, data, &obj, &array);
7731255332Scy	if (error != 0)
7732255332Scy		return error;
7733255332Scy
7734255332Scy	flushed = 0;
7735255332Scy
7736255332Scy	for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) {
7737255332Scy		natnext = nat->nat_next;
7738255332Scy		if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) {
7739255332Scy			ipf_nat_delete(softc, nat, NL_FLUSH);
7740255332Scy			flushed++;
7741255332Scy		}
7742255332Scy	}
7743255332Scy
7744255332Scy	obj.ipfo_retval = flushed;
7745255332Scy	error = BCOPYOUT(&obj, data, sizeof(obj));
7746255332Scy
7747255332Scy	KFREES(array, array[0] * sizeof(*array));
7748255332Scy
7749255332Scy	return error;
7750255332Scy}
7751255332Scy
7752255332Scy
7753255332Scy/* ------------------------------------------------------------------------ */
7754255332Scy/* Function:    ipf_nat_matcharray                                          */
7755255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7756255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7757255332Scy/*              nat(I) - pointer to current NAT session                     */
7758255332Scy/*                                                                          */
7759255332Scy/* ------------------------------------------------------------------------ */
7760255332Scystatic int
7761255332Scyipf_nat_matcharray(nat, array, ticks)
7762255332Scy	nat_t *nat;
7763255332Scy	int *array;
7764255332Scy	u_long ticks;
7765255332Scy{
7766255332Scy	int i, n, *x, e, p;
7767255332Scy
7768255332Scy	e = 0;
7769255332Scy	n = array[0];
7770255332Scy	x = array + 1;
7771255332Scy
7772255332Scy	for (; n > 0; x += 3 + x[2]) {
7773255332Scy		if (x[0] == IPF_EXP_END)
7774255332Scy			break;
7775255332Scy		e = 0;
7776255332Scy
7777255332Scy		n -= x[2] + 3;
7778255332Scy		if (n < 0)
7779255332Scy			break;
7780255332Scy
7781255332Scy		p = x[0] >> 16;
7782255332Scy		if (p != 0 && p != nat->nat_pr[1])
7783255332Scy			break;
7784255332Scy
7785255332Scy		switch (x[0])
7786255332Scy		{
7787255332Scy		case IPF_EXP_IP_PR :
7788255332Scy			for (i = 0; !e && i < x[2]; i++) {
7789255332Scy				e |= (nat->nat_pr[1] == x[i + 3]);
7790255332Scy			}
7791255332Scy			break;
7792255332Scy
7793255332Scy		case IPF_EXP_IP_SRCADDR :
7794255332Scy			if (nat->nat_v[0] == 4) {
7795255332Scy				for (i = 0; !e && i < x[2]; i++) {
7796255332Scy					e |= ((nat->nat_osrcaddr & x[i + 4]) ==
7797255332Scy					      x[i + 3]);
7798255332Scy				}
7799255332Scy			}
7800255332Scy			if (nat->nat_v[1] == 4) {
7801255332Scy				for (i = 0; !e && i < x[2]; i++) {
7802255332Scy					e |= ((nat->nat_nsrcaddr & x[i + 4]) ==
7803255332Scy					      x[i + 3]);
7804255332Scy				}
7805255332Scy			}
7806255332Scy			break;
7807255332Scy
7808255332Scy		case IPF_EXP_IP_DSTADDR :
7809255332Scy			if (nat->nat_v[0] == 4) {
7810255332Scy				for (i = 0; !e && i < x[2]; i++) {
7811255332Scy					e |= ((nat->nat_odstaddr & x[i + 4]) ==
7812255332Scy					      x[i + 3]);
7813255332Scy				}
7814255332Scy			}
7815255332Scy			if (nat->nat_v[1] == 4) {
7816255332Scy				for (i = 0; !e && i < x[2]; i++) {
7817255332Scy					e |= ((nat->nat_ndstaddr & x[i + 4]) ==
7818255332Scy					      x[i + 3]);
7819255332Scy				}
7820255332Scy			}
7821255332Scy			break;
7822255332Scy
7823255332Scy		case IPF_EXP_IP_ADDR :
7824255332Scy			for (i = 0; !e && i < x[2]; i++) {
7825255332Scy				if (nat->nat_v[0] == 4) {
7826255332Scy					e |= ((nat->nat_osrcaddr & x[i + 4]) ==
7827255332Scy					      x[i + 3]);
7828255332Scy				}
7829255332Scy				if (nat->nat_v[1] == 4) {
7830255332Scy					e |= ((nat->nat_nsrcaddr & x[i + 4]) ==
7831255332Scy					      x[i + 3]);
7832255332Scy				}
7833255332Scy				if (nat->nat_v[0] == 4) {
7834255332Scy					e |= ((nat->nat_odstaddr & x[i + 4]) ==
7835255332Scy					      x[i + 3]);
7836255332Scy				}
7837255332Scy				if (nat->nat_v[1] == 4) {
7838255332Scy					e |= ((nat->nat_ndstaddr & x[i + 4]) ==
7839255332Scy					      x[i + 3]);
7840255332Scy				}
7841255332Scy			}
7842255332Scy			break;
7843255332Scy
7844255332Scy#ifdef USE_INET6
7845255332Scy		case IPF_EXP_IP6_SRCADDR :
7846255332Scy			if (nat->nat_v[0] == 6) {
7847255332Scy				for (i = 0; !e && i < x[3]; i++) {
7848255332Scy					e |= IP6_MASKEQ(&nat->nat_osrc6,
7849255332Scy							x + i + 7, 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_nsrc6,
7855255332Scy							x + i + 7, x + i + 3);
7856255332Scy				}
7857255332Scy			}
7858255332Scy			break;
7859255332Scy
7860255332Scy		case IPF_EXP_IP6_DSTADDR :
7861255332Scy			if (nat->nat_v[0] == 6) {
7862255332Scy				for (i = 0; !e && i < x[3]; i++) {
7863255332Scy					e |= IP6_MASKEQ(&nat->nat_odst6,
7864255332Scy							x + i + 7,
7865255332Scy							x + i + 3);
7866255332Scy				}
7867255332Scy			}
7868255332Scy			if (nat->nat_v[1] == 6) {
7869255332Scy				for (i = 0; !e && i < x[3]; i++) {
7870255332Scy					e |= IP6_MASKEQ(&nat->nat_ndst6,
7871255332Scy							x + i + 7,
7872255332Scy							x + i + 3);
7873255332Scy				}
7874255332Scy			}
7875255332Scy			break;
7876255332Scy
7877255332Scy		case IPF_EXP_IP6_ADDR :
7878255332Scy			for (i = 0; !e && i < x[3]; i++) {
7879255332Scy				if (nat->nat_v[0] == 6) {
7880255332Scy					e |= IP6_MASKEQ(&nat->nat_osrc6,
7881255332Scy							x + i + 7,
7882255332Scy							x + i + 3);
7883255332Scy				}
7884255332Scy				if (nat->nat_v[0] == 6) {
7885255332Scy					e |= IP6_MASKEQ(&nat->nat_odst6,
7886255332Scy							x + i + 7,
7887255332Scy							x + i + 3);
7888255332Scy				}
7889255332Scy				if (nat->nat_v[1] == 6) {
7890255332Scy					e |= IP6_MASKEQ(&nat->nat_nsrc6,
7891255332Scy							x + i + 7,
7892255332Scy							x + i + 3);
7893255332Scy				}
7894255332Scy				if (nat->nat_v[1] == 6) {
7895255332Scy					e |= IP6_MASKEQ(&nat->nat_ndst6,
7896255332Scy							x + i + 7,
7897255332Scy							x + i + 3);
7898255332Scy				}
7899255332Scy			}
7900255332Scy			break;
7901255332Scy#endif
7902255332Scy
7903255332Scy		case IPF_EXP_UDP_PORT :
7904255332Scy		case IPF_EXP_TCP_PORT :
7905255332Scy			for (i = 0; !e && i < x[2]; i++) {
7906255332Scy				e |= (nat->nat_nsport == x[i + 3]) ||
7907255332Scy				     (nat->nat_ndport == x[i + 3]);
7908255332Scy			}
7909255332Scy			break;
7910255332Scy
7911255332Scy		case IPF_EXP_UDP_SPORT :
7912255332Scy		case IPF_EXP_TCP_SPORT :
7913255332Scy			for (i = 0; !e && i < x[2]; i++) {
7914255332Scy				e |= (nat->nat_nsport == x[i + 3]);
7915255332Scy			}
7916255332Scy			break;
7917255332Scy
7918255332Scy		case IPF_EXP_UDP_DPORT :
7919255332Scy		case IPF_EXP_TCP_DPORT :
7920255332Scy			for (i = 0; !e && i < x[2]; i++) {
7921255332Scy				e |= (nat->nat_ndport == x[i + 3]);
7922255332Scy			}
7923255332Scy			break;
7924255332Scy
7925255332Scy		case IPF_EXP_TCP_STATE :
7926255332Scy			for (i = 0; !e && i < x[2]; i++) {
7927255332Scy				e |= (nat->nat_tcpstate[0] == x[i + 3]) ||
7928255332Scy				     (nat->nat_tcpstate[1] == x[i + 3]);
7929255332Scy			}
7930255332Scy			break;
7931255332Scy
7932255332Scy		case IPF_EXP_IDLE_GT :
7933255332Scy			e |= (ticks - nat->nat_touched > x[3]);
7934255332Scy			break;
7935255332Scy		}
7936255332Scy		e ^= x[1];
7937255332Scy
7938255332Scy		if (!e)
7939255332Scy			break;
7940255332Scy	}
7941255332Scy
7942255332Scy	return e;
7943255332Scy}
7944255332Scy
7945255332Scy
7946255332Scy/* ------------------------------------------------------------------------ */
7947255332Scy/* Function:    ipf_nat_gettable                                            */
7948172776Sdarrenr/* Returns:     int     - 0 = success, else error                           */
7949255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7950255332Scy/*              softn(I) - pointer to NAT context structure                 */
7951255332Scy/*              data(I)  - pointer to ioctl data                            */
7952172776Sdarrenr/*                                                                          */
7953172776Sdarrenr/* This function handles ioctl requests for tables of nat information.      */
7954172776Sdarrenr/* At present the only table it deals with is the hash bucket statistics.   */
7955172776Sdarrenr/* ------------------------------------------------------------------------ */
7956255332Scystatic int
7957255332Scyipf_nat_gettable(softc, softn, data)
7958255332Scy	ipf_main_softc_t *softc;
7959255332Scy	ipf_nat_softc_t *softn;
7960255332Scy	char *data;
7961172776Sdarrenr{
7962172776Sdarrenr	ipftable_t table;
7963172776Sdarrenr	int error;
7964172776Sdarrenr
7965255332Scy	error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE);
7966172776Sdarrenr	if (error != 0)
7967172776Sdarrenr		return error;
7968172776Sdarrenr
7969172776Sdarrenr	switch (table.ita_type)
7970172776Sdarrenr	{
7971172776Sdarrenr	case IPFTABLE_BUCKETS_NATIN :
7972255332Scy		error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
7973255332Scy				table.ita_table,
7974255332Scy				softn->ipf_nat_table_sz * sizeof(u_int));
7975172776Sdarrenr		break;
7976172776Sdarrenr
7977172776Sdarrenr	case IPFTABLE_BUCKETS_NATOUT :
7978255332Scy		error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
7979255332Scy				table.ita_table,
7980255332Scy				softn->ipf_nat_table_sz * sizeof(u_int));
7981172776Sdarrenr		break;
7982172776Sdarrenr
7983172776Sdarrenr	default :
7984255332Scy		IPFERROR(60058);
7985172776Sdarrenr		return EINVAL;
7986172776Sdarrenr	}
7987172776Sdarrenr
7988172776Sdarrenr	if (error != 0) {
7989255332Scy		IPFERROR(60059);
7990172776Sdarrenr		error = EFAULT;
7991172776Sdarrenr	}
7992172776Sdarrenr	return error;
7993172776Sdarrenr}
7994255332Scy
7995255332Scy
7996255332Scy/* ------------------------------------------------------------------------ */
7997255332Scy/* Function:    ipf_nat_settimeout                                          */
7998255332Scy/* Returns:     int  - 0 = success, else failure			    */
7999255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8000255332Scy/*              t(I) - pointer to tunable                                   */
8001255332Scy/*              p(I) - pointer to new tuning data                           */
8002255332Scy/*                                                                          */
8003255332Scy/* Apply the timeout change to the NAT timeout queues.                      */
8004255332Scy/* ------------------------------------------------------------------------ */
8005255332Scyint
8006255332Scyipf_nat_settimeout(softc, t, p)
8007255332Scy	struct ipf_main_softc_s *softc;
8008255332Scy	ipftuneable_t *t;
8009255332Scy	ipftuneval_t *p;
8010255332Scy{
8011255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8012255332Scy
8013255332Scy	if (!strncmp(t->ipft_name, "tcp_", 4))
8014255332Scy		return ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq);
8015255332Scy
8016255332Scy	if (!strcmp(t->ipft_name, "udp_timeout")) {
8017255332Scy		ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int);
8018255332Scy	} else if (!strcmp(t->ipft_name, "udp_ack_timeout")) {
8019255332Scy		ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int);
8020255332Scy	} else if (!strcmp(t->ipft_name, "icmp_timeout")) {
8021255332Scy		ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int);
8022255332Scy	} else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) {
8023255332Scy		ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int);
8024255332Scy	} else if (!strcmp(t->ipft_name, "ip_timeout")) {
8025255332Scy		ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int);
8026255332Scy	} else {
8027255332Scy		IPFERROR(60062);
8028255332Scy		return ESRCH;
8029255332Scy	}
8030255332Scy	return 0;
8031255332Scy}
8032255332Scy
8033255332Scy
8034255332Scy/* ------------------------------------------------------------------------ */
8035255332Scy/* Function:    ipf_nat_rehash                                              */
8036255332Scy/* Returns:     int  - 0 = success, else failure			    */
8037255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8038255332Scy/*              t(I) - pointer to tunable                                   */
8039255332Scy/*              p(I) - pointer to new tuning data                           */
8040255332Scy/*                                                                          */
8041255332Scy/* To change the size of the basic NAT table, we need to first allocate the */
8042255332Scy/* new tables (lest it fails and we've got nowhere to store all of the NAT  */
8043255332Scy/* sessions currently active) and then walk through the entire list and     */
8044255332Scy/* insert them into the table.  There are two tables here: an inbound one   */
8045255332Scy/* and an outbound one.  Each NAT entry goes into each table once.          */
8046255332Scy/* ------------------------------------------------------------------------ */
8047255332Scyint
8048255332Scyipf_nat_rehash(softc, t, p)
8049255332Scy	ipf_main_softc_t *softc;
8050255332Scy	ipftuneable_t *t;
8051255332Scy	ipftuneval_t *p;
8052255332Scy{
8053255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8054255332Scy	nat_t **newtab[2], *nat, **natp;
8055255332Scy	u_int *bucketlens[2];
8056255332Scy	u_int maxbucket;
8057255332Scy	u_int newsize;
8058255332Scy	int error;
8059255332Scy	u_int hv;
8060255332Scy	int i;
8061255332Scy
8062255332Scy	newsize = p->ipftu_int;
8063255332Scy	/*
8064255332Scy	 * In case there is nothing to do...
8065255332Scy	 */
8066255332Scy	if (newsize == softn->ipf_nat_table_sz)
8067255332Scy		return 0;
8068255332Scy
8069255332Scy	newtab[0] = NULL;
8070255332Scy	newtab[1] = NULL;
8071255332Scy	bucketlens[0] = NULL;
8072255332Scy	bucketlens[1] = NULL;
8073255332Scy	/*
8074255332Scy	 * 4 tables depend on the NAT table size: the inbound looking table,
8075255332Scy	 * the outbound lookup table and the hash chain length for each.
8076255332Scy	 */
8077255332Scy	KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *));
8078255332Scy	if (newtab == NULL) {
8079255332Scy		error = 60063;
8080255332Scy		goto badrehash;
8081255332Scy	}
8082255332Scy
8083255332Scy	KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *));
8084255332Scy	if (newtab == NULL) {
8085255332Scy		error = 60064;
8086255332Scy		goto badrehash;
8087255332Scy	}
8088255332Scy
8089255332Scy	KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int));
8090255332Scy	if (bucketlens[0] == NULL) {
8091255332Scy		error = 60065;
8092255332Scy		goto badrehash;
8093255332Scy	}
8094255332Scy
8095255332Scy	KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int));
8096255332Scy	if (bucketlens[1] == NULL) {
8097255332Scy		error = 60066;
8098255332Scy		goto badrehash;
8099255332Scy	}
8100255332Scy
8101255332Scy	/*
8102255332Scy	 * Recalculate the maximum length based on the new size.
8103255332Scy	 */
8104255332Scy	for (maxbucket = 0, i = newsize; i > 0; i >>= 1)
8105255332Scy		maxbucket++;
8106255332Scy	maxbucket *= 2;
8107255332Scy
8108255332Scy	bzero((char *)newtab[0], newsize * sizeof(nat_t *));
8109255332Scy	bzero((char *)newtab[1], newsize * sizeof(nat_t *));
8110255332Scy	bzero((char *)bucketlens[0], newsize * sizeof(u_int));
8111255332Scy	bzero((char *)bucketlens[1], newsize * sizeof(u_int));
8112255332Scy
8113255332Scy	WRITE_ENTER(&softc->ipf_nat);
8114255332Scy
8115255332Scy	if (softn->ipf_nat_table[0] != NULL) {
8116255332Scy		KFREES(softn->ipf_nat_table[0],
8117255332Scy		       softn->ipf_nat_table_sz *
8118255332Scy		       sizeof(*softn->ipf_nat_table[0]));
8119255332Scy	}
8120255332Scy	softn->ipf_nat_table[0] = newtab[0];
8121255332Scy
8122255332Scy	if (softn->ipf_nat_table[1] != NULL) {
8123255332Scy		KFREES(softn->ipf_nat_table[1],
8124255332Scy		       softn->ipf_nat_table_sz *
8125255332Scy		       sizeof(*softn->ipf_nat_table[1]));
8126255332Scy	}
8127255332Scy	softn->ipf_nat_table[1] = newtab[1];
8128255332Scy
8129255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) {
8130255332Scy		KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
8131255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8132255332Scy	}
8133255332Scy	softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0];
8134255332Scy
8135255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) {
8136255332Scy		KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
8137255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8138255332Scy	}
8139255332Scy	softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1];
8140255332Scy
8141255332Scy#ifdef USE_INET6
8142255332Scy	if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) {
8143255332Scy		KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen,
8144255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8145255332Scy	}
8146255332Scy	softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0];
8147255332Scy
8148255332Scy	if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) {
8149255332Scy		KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen,
8150255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8151255332Scy	}
8152255332Scy	softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1];
8153255332Scy#endif
8154255332Scy
8155255332Scy	softn->ipf_nat_maxbucket = maxbucket;
8156255332Scy	softn->ipf_nat_table_sz = newsize;
8157255332Scy	/*
8158255332Scy	 * Walk through the entire list of NAT table entries and put them
8159255332Scy	 * in the new NAT table, somewhere.  Because we have a new table,
8160255332Scy	 * we need to restart the counter of how many chains are in use.
8161255332Scy	 */
8162255332Scy	softn->ipf_nat_stats.ns_side[0].ns_inuse = 0;
8163255332Scy	softn->ipf_nat_stats.ns_side[1].ns_inuse = 0;
8164255332Scy#ifdef USE_INET6
8165255332Scy	softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0;
8166255332Scy	softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0;
8167255332Scy#endif
8168255332Scy
8169255332Scy	for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) {
8170255332Scy		nat->nat_hnext[0] = NULL;
8171255332Scy		nat->nat_phnext[0] = NULL;
8172255332Scy		hv = nat->nat_hv[0] % softn->ipf_nat_table_sz;
8173255332Scy
8174255332Scy		natp = &softn->ipf_nat_table[0][hv];
8175255332Scy		if (*natp) {
8176255332Scy			(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
8177255332Scy		} else {
8178255332Scy			NBUMPSIDE(0, ns_inuse);
8179255332Scy		}
8180255332Scy		nat->nat_phnext[0] = natp;
8181255332Scy		nat->nat_hnext[0] = *natp;
8182255332Scy		*natp = nat;
8183255332Scy		NBUMPSIDE(0, ns_bucketlen[hv]);
8184255332Scy
8185255332Scy		nat->nat_hnext[1] = NULL;
8186255332Scy		nat->nat_phnext[1] = NULL;
8187255332Scy		hv = nat->nat_hv[1] % softn->ipf_nat_table_sz;
8188255332Scy
8189255332Scy		natp = &softn->ipf_nat_table[1][hv];
8190255332Scy		if (*natp) {
8191255332Scy			(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
8192255332Scy		} else {
8193255332Scy			NBUMPSIDE(1, ns_inuse);
8194255332Scy		}
8195255332Scy		nat->nat_phnext[1] = natp;
8196255332Scy		nat->nat_hnext[1] = *natp;
8197255332Scy		*natp = nat;
8198255332Scy		NBUMPSIDE(1, ns_bucketlen[hv]);
8199255332Scy	}
8200255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8201255332Scy
8202255332Scy	return 0;
8203255332Scy
8204255332Scybadrehash:
8205255332Scy	if (bucketlens[1] != NULL) {
8206255332Scy		KFREES(bucketlens[0], newsize * sizeof(u_int));
8207255332Scy	}
8208255332Scy	if (bucketlens[0] != NULL) {
8209255332Scy		KFREES(bucketlens[0], newsize * sizeof(u_int));
8210255332Scy	}
8211255332Scy	if (newtab[0] != NULL) {
8212255332Scy		KFREES(newtab[0], newsize * sizeof(nat_t *));
8213255332Scy	}
8214255332Scy	if (newtab[1] != NULL) {
8215255332Scy		KFREES(newtab[1], newsize * sizeof(nat_t *));
8216255332Scy	}
8217255332Scy	IPFERROR(error);
8218255332Scy	return ENOMEM;
8219255332Scy}
8220255332Scy
8221255332Scy
8222255332Scy/* ------------------------------------------------------------------------ */
8223255332Scy/* Function:    ipf_nat_rehash_rules                                        */
8224255332Scy/* Returns:     int  - 0 = success, else failure			    */
8225255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8226255332Scy/*              t(I) - pointer to tunable                                   */
8227255332Scy/*              p(I) - pointer to new tuning data                           */
8228255332Scy/*                                                                          */
8229255332Scy/* All of the NAT rules hang off of a hash table that is searched with a    */
8230255332Scy/* hash on address after the netmask is applied.  There is a different table*/
8231255332Scy/* for both inbound rules (rdr) and outbound (map.)  The resizing will only */
8232255332Scy/* affect one of these two tables.                                          */
8233255332Scy/* ------------------------------------------------------------------------ */
8234255332Scyint
8235255332Scyipf_nat_rehash_rules(softc, t, p)
8236255332Scy	ipf_main_softc_t *softc;
8237255332Scy	ipftuneable_t *t;
8238255332Scy	ipftuneval_t *p;
8239255332Scy{
8240255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8241255332Scy	ipnat_t **newtab, *np, ***old, **npp;
8242255332Scy	u_int newsize;
8243255332Scy	u_int mask;
8244255332Scy	u_int hv;
8245255332Scy
8246255332Scy	newsize = p->ipftu_int;
8247255332Scy	/*
8248255332Scy	 * In case there is nothing to do...
8249255332Scy	 */
8250255332Scy	if (newsize == *t->ipft_pint)
8251255332Scy		return 0;
8252255332Scy
8253255332Scy	/*
8254255332Scy	 * All inbound rules have the NAT_REDIRECT bit set in in_redir and
8255255332Scy	 * all outbound rules have either NAT_MAP or MAT_MAPBLK set.
8256255332Scy	 * This if statement allows for some more generic code to be below,
8257255332Scy	 * rather than two huge gobs of code that almost do the same thing.
8258255332Scy	 */
8259255332Scy	if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) {
8260255332Scy		old = &softn->ipf_nat_rdr_rules;
8261255332Scy		mask = NAT_REDIRECT;
8262255332Scy	} else {
8263255332Scy		old = &softn->ipf_nat_map_rules;
8264255332Scy		mask = NAT_MAP|NAT_MAPBLK;
8265255332Scy	}
8266255332Scy
8267255332Scy	KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *));
8268255332Scy	if (newtab == NULL) {
8269255332Scy		IPFERROR(60067);
8270255332Scy		return ENOMEM;
8271255332Scy	}
8272255332Scy
8273255332Scy	bzero((char *)newtab, newsize * sizeof(ipnat_t *));
8274255332Scy
8275255332Scy	WRITE_ENTER(&softc->ipf_nat);
8276255332Scy
8277255332Scy	if (*old != NULL) {
8278255332Scy		KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **));
8279255332Scy	}
8280255332Scy	*old = newtab;
8281255332Scy	*t->ipft_pint = newsize;
8282255332Scy
8283255332Scy	for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) {
8284255332Scy		if ((np->in_redir & mask) == 0)
8285255332Scy			continue;
8286255332Scy
8287255332Scy		if (np->in_redir & NAT_REDIRECT) {
8288255332Scy			np->in_rnext = NULL;
8289255332Scy			hv = np->in_hv[0] % newsize;
8290255332Scy			for (npp = newtab + hv; *npp != NULL; )
8291255332Scy				npp = &(*npp)->in_rnext;
8292255332Scy			np->in_prnext = npp;
8293255332Scy			*npp = np;
8294255332Scy		}
8295255332Scy		if (np->in_redir & NAT_MAP) {
8296255332Scy			np->in_mnext = NULL;
8297255332Scy			hv = np->in_hv[1] % newsize;
8298255332Scy			for (npp = newtab + hv; *npp != NULL; )
8299255332Scy				npp = &(*npp)->in_mnext;
8300255332Scy			np->in_pmnext = npp;
8301255332Scy			*npp = np;
8302255332Scy		}
8303255332Scy
8304255332Scy	}
8305255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8306255332Scy
8307255332Scy	return 0;
8308255332Scy}
8309255332Scy
8310255332Scy
8311255332Scy/* ------------------------------------------------------------------------ */
8312255332Scy/* Function:    ipf_nat_hostmap_rehash                                      */
8313255332Scy/* Returns:     int  - 0 = success, else failure			    */
8314255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8315255332Scy/*              t(I) - pointer to tunable                                   */
8316255332Scy/*              p(I) - pointer to new tuning data                           */
8317255332Scy/*                                                                          */
8318255332Scy/* Allocate and populate a new hash table that will contain a reference to  */
8319255332Scy/* all of the active IP# translations currently in place.                   */
8320255332Scy/* ------------------------------------------------------------------------ */
8321255332Scyint
8322255332Scyipf_nat_hostmap_rehash(softc, t, p)
8323255332Scy	ipf_main_softc_t *softc;
8324255332Scy	ipftuneable_t *t;
8325255332Scy	ipftuneval_t *p;
8326255332Scy{
8327255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8328255332Scy	hostmap_t *hm, **newtab;
8329255332Scy	u_int newsize;
8330255332Scy	u_int hv;
8331255332Scy
8332255332Scy	newsize = p->ipftu_int;
8333255332Scy	/*
8334255332Scy	 * In case there is nothing to do...
8335255332Scy	 */
8336255332Scy	if (newsize == *t->ipft_pint)
8337255332Scy		return 0;
8338255332Scy
8339255332Scy	KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *));
8340255332Scy	if (newtab == NULL) {
8341255332Scy		IPFERROR(60068);
8342255332Scy		return ENOMEM;
8343255332Scy	}
8344255332Scy
8345255332Scy	bzero((char *)newtab, newsize * sizeof(hostmap_t *));
8346255332Scy
8347255332Scy	WRITE_ENTER(&softc->ipf_nat);
8348255332Scy	if (softn->ipf_hm_maptable != NULL) {
8349255332Scy		KFREES(softn->ipf_hm_maptable,
8350255332Scy		       softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *));
8351255332Scy	}
8352255332Scy	softn->ipf_hm_maptable = newtab;
8353255332Scy	softn->ipf_nat_hostmap_sz = newsize;
8354255332Scy
8355255332Scy	for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) {
8356255332Scy		hv = hm->hm_hv % softn->ipf_nat_hostmap_sz;
8357255332Scy		hm->hm_hnext = softn->ipf_hm_maptable[hv];
8358255332Scy		hm->hm_phnext = softn->ipf_hm_maptable + hv;
8359255332Scy		if (softn->ipf_hm_maptable[hv] != NULL)
8360255332Scy			softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext;
8361255332Scy		softn->ipf_hm_maptable[hv] = hm;
8362255332Scy	}
8363255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8364255332Scy
8365255332Scy	return 0;
8366255332Scy}
8367255332Scy
8368255332Scy
8369255332Scy/* ------------------------------------------------------------------------ */
8370255332Scy/* Function:    ipf_nat_add_tq                                              */
8371255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8372255332Scy/*                                                                          */
8373255332Scy/* ------------------------------------------------------------------------ */
8374255332Scyipftq_t *
8375255332Scyipf_nat_add_tq(softc, ttl)
8376255332Scy	ipf_main_softc_t *softc;
8377255332Scy	int ttl;
8378255332Scy{
8379255332Scy	ipf_nat_softc_t *softs = softc->ipf_nat_soft;
8380255332Scy
8381255332Scy	return ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl);
8382255332Scy}
8383255332Scy
8384255332Scy/* ------------------------------------------------------------------------ */
8385255332Scy/* Function:    ipf_nat_uncreate                                            */
8386255332Scy/* Returns:     Nil                                                         */
8387255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
8388255332Scy/*                                                                          */
8389255332Scy/* This function is used to remove a NAT entry from the NAT table when we   */
8390255332Scy/* decide that the create was actually in error. It is thus assumed that    */
8391255332Scy/* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */
8392255332Scy/* with the translated packet (not the original), we have to reverse the    */
8393255332Scy/* lookup. Although doing the lookup is expensive (relatively speaking), it */
8394255332Scy/* is not anticipated that this will be a frequent occurance for normal     */
8395255332Scy/* traffic patterns.                                                        */
8396255332Scy/* ------------------------------------------------------------------------ */
8397255332Scyvoid
8398255332Scyipf_nat_uncreate(fin)
8399255332Scy	fr_info_t *fin;
8400255332Scy{
8401255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
8402255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8403255332Scy	int nflags;
8404255332Scy	nat_t *nat;
8405255332Scy
8406255332Scy	switch (fin->fin_p)
8407255332Scy	{
8408255332Scy	case IPPROTO_TCP :
8409255332Scy		nflags = IPN_TCP;
8410255332Scy		break;
8411255332Scy	case IPPROTO_UDP :
8412255332Scy		nflags = IPN_UDP;
8413255332Scy		break;
8414255332Scy	default :
8415255332Scy		nflags = 0;
8416255332Scy		break;
8417255332Scy	}
8418255332Scy
8419255332Scy	WRITE_ENTER(&softc->ipf_nat);
8420255332Scy
8421255332Scy	if (fin->fin_out == 0) {
8422255332Scy		nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p,
8423255332Scy					fin->fin_dst, fin->fin_src);
8424255332Scy	} else {
8425255332Scy		nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p,
8426255332Scy				       fin->fin_src, fin->fin_dst);
8427255332Scy	}
8428255332Scy
8429255332Scy	if (nat != NULL) {
8430255332Scy		NBUMPSIDE(fin->fin_out, ns_uncreate[0]);
8431255332Scy		ipf_nat_delete(softc, nat, NL_DESTROY);
8432255332Scy	} else {
8433255332Scy		NBUMPSIDE(fin->fin_out, ns_uncreate[1]);
8434255332Scy	}
8435255332Scy
8436255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8437255332Scy}
8438255332Scy
8439255332Scy
8440255332Scy/* ------------------------------------------------------------------------ */
8441255332Scy/* Function:    ipf_nat_cmp_rules                                           */
8442255332Scy/* Returns:     int   - 0 == success, else rules do not match.              */
8443255332Scy/* Parameters:  n1(I) - first rule to compare                               */
8444255332Scy/*              n2(I) - first rule to compare                               */
8445255332Scy/*                                                                          */
8446255332Scy/* Compare two rules using pointers to each rule. A straight bcmp will not  */
8447255332Scy/* work as some fields (such as in_dst, in_pkts) actually do change once    */
8448255332Scy/* the rule has been loaded into the kernel. Whilst this function returns   */
8449255332Scy/* various non-zero returns, they're strictly to aid in debugging. Use of   */
8450255332Scy/* this function should simply care if the result is zero or not.           */
8451255332Scy/* ------------------------------------------------------------------------ */
8452255332Scystatic int
8453255332Scyipf_nat_cmp_rules(n1, n2)
8454255332Scy	ipnat_t *n1, *n2;
8455255332Scy{
8456255332Scy	if (n1->in_size != n2->in_size)
8457255332Scy		return 1;
8458255332Scy
8459255332Scy	if (bcmp((char *)&n1->in_v, (char *)&n2->in_v,
8460255332Scy		 offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0)
8461255332Scy		return 2;
8462255332Scy
8463255332Scy	if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc,
8464255332Scy		 n1->in_size - offsetof(ipnat_t, in_tuc)) != 0)
8465255332Scy		return 3;
8466255332Scy	if (n1->in_ndst.na_atype != n2->in_ndst.na_atype)
8467255332Scy		return 5;
8468255332Scy	if (n1->in_ndst.na_function != n2->in_ndst.na_function)
8469255332Scy		return 6;
8470255332Scy	if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr,
8471255332Scy		 sizeof(n1->in_ndst.na_addr)))
8472255332Scy		return 7;
8473255332Scy	if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype)
8474255332Scy		return 8;
8475255332Scy	if (n1->in_nsrc.na_function != n2->in_nsrc.na_function)
8476255332Scy		return 9;
8477255332Scy	if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr,
8478255332Scy		 sizeof(n1->in_nsrc.na_addr)))
8479255332Scy		return 10;
8480255332Scy	if (n1->in_odst.na_atype != n2->in_odst.na_atype)
8481255332Scy		return 11;
8482255332Scy	if (n1->in_odst.na_function != n2->in_odst.na_function)
8483255332Scy		return 12;
8484255332Scy	if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr,
8485255332Scy		 sizeof(n1->in_odst.na_addr)))
8486255332Scy		return 13;
8487255332Scy	if (n1->in_osrc.na_atype != n2->in_osrc.na_atype)
8488255332Scy		return 14;
8489255332Scy	if (n1->in_osrc.na_function != n2->in_osrc.na_function)
8490255332Scy		return 15;
8491255332Scy	if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr,
8492255332Scy		 sizeof(n1->in_osrc.na_addr)))
8493255332Scy		return 16;
8494255332Scy	return 0;
8495255332Scy}
8496255332Scy
8497255332Scy
8498255332Scy/* ------------------------------------------------------------------------ */
8499255332Scy/* Function:    ipf_nat_rule_init                                           */
8500255332Scy/* Returns:     int   - 0 == success, else rules do not match.              */
8501255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8502255332Scy/*              softn(I) - pointer to NAT context structure                 */
8503255332Scy/*              n(I)     - first rule to compare                            */
8504255332Scy/*                                                                          */
8505255332Scy/* ------------------------------------------------------------------------ */
8506255332Scystatic int
8507255332Scyipf_nat_rule_init(softc, softn, n)
8508255332Scy	ipf_main_softc_t *softc;
8509255332Scy	ipf_nat_softc_t *softn;
8510255332Scy	ipnat_t *n;
8511255332Scy{
8512255332Scy	int error = 0;
8513255332Scy
8514255332Scy	if ((n->in_flags & IPN_SIPRANGE) != 0)
8515255332Scy		n->in_nsrcatype = FRI_RANGE;
8516255332Scy
8517255332Scy	if ((n->in_flags & IPN_DIPRANGE) != 0)
8518255332Scy		n->in_ndstatype = FRI_RANGE;
8519255332Scy
8520255332Scy	if ((n->in_flags & IPN_SPLIT) != 0)
8521255332Scy		n->in_ndstatype = FRI_SPLIT;
8522255332Scy
8523255332Scy	if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0)
8524255332Scy		n->in_spnext = n->in_spmin;
8525255332Scy
8526255332Scy	if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) {
8527255332Scy		n->in_dpnext = n->in_dpmin;
8528255332Scy	} else if (n->in_redir == NAT_REDIRECT) {
8529255332Scy		n->in_dpnext = n->in_dpmin;
8530255332Scy	}
8531255332Scy
8532255332Scy	n->in_stepnext = 0;
8533255332Scy
8534255332Scy	switch (n->in_v[0])
8535255332Scy	{
8536255332Scy	case 4 :
8537255332Scy		error = ipf_nat_ruleaddrinit(softc, softn, n);
8538255332Scy		if (error != 0)
8539255332Scy			return error;
8540255332Scy		break;
8541255332Scy#ifdef USE_INET6
8542255332Scy	case 6 :
8543255332Scy		error = ipf_nat6_ruleaddrinit(softc, softn, n);
8544255332Scy		if (error != 0)
8545255332Scy			return error;
8546255332Scy		break;
8547255332Scy#endif
8548255332Scy	default :
8549255332Scy		break;
8550255332Scy	}
8551255332Scy
8552255332Scy	if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) {
8553255332Scy		/*
8554255332Scy		 * Prerecord whether or not the destination of the divert
8555255332Scy		 * is local or not to the interface the packet is going
8556255332Scy		 * to be sent out.
8557255332Scy		 */
8558255332Scy		n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1],
8559255332Scy						n->in_ifps[1], &n->in_ndstip6);
8560255332Scy	}
8561255332Scy
8562255332Scy	return error;
8563255332Scy}
8564255332Scy
8565255332Scy
8566255332Scy/* ------------------------------------------------------------------------ */
8567255332Scy/* Function:    ipf_nat_rule_fini                                           */
8568255332Scy/* Returns:     int   - 0 == success, else rules do not match.              */
8569255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8570255332Scy/*              n(I)     - rule to work on                                  */
8571255332Scy/*                                                                          */
8572255332Scy/* This function is used to release any objects that were referenced during */
8573255332Scy/* the rule initialisation. This is useful both when free'ing the rule and  */
8574255332Scy/* when handling ioctls that need to initialise these fields but not        */
8575255332Scy/* actually use them after the ioctl processing has finished.               */
8576255332Scy/* ------------------------------------------------------------------------ */
8577255332Scystatic void
8578255332Scyipf_nat_rule_fini(softc, n)
8579255332Scy	ipf_main_softc_t *softc;
8580255332Scy	ipnat_t *n;
8581255332Scy{
8582255332Scy	if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL)
8583255332Scy		ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr);
8584255332Scy
8585255332Scy	if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL)
8586255332Scy		ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr);
8587255332Scy
8588255332Scy	if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL)
8589255332Scy		ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr);
8590255332Scy
8591255332Scy	if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL)
8592255332Scy		ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr);
8593255332Scy
8594255332Scy	if (n->in_divmp != NULL)
8595255332Scy		FREE_MB_T(n->in_divmp);
8596255332Scy}
8597