1145522Sdarrenr/*	$FreeBSD$	*/
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$";
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;
2949145522Sdarrenr
2950145522Sdarrenr		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
2951255332Scy			hm = ipf_nat_hostmap(softn, NULL, fin->fin_src,
2952255332Scy					     fin->fin_dst, in, (u_32_t)dport);
2953145522Sdarrenr			if (hm != NULL) {
2954255332Scy				in.s_addr = hm->hm_ndstip.s_addr;
2955145522Sdarrenr				move = 0;
2956145522Sdarrenr			}
2957145522Sdarrenr		}
2958145522Sdarrenr
2959145522Sdarrenr		if (hm == NULL || hm->hm_ref == 1) {
2960255332Scy			if (np->in_ndstaddr == htonl(in.s_addr)) {
2961255332Scy				np->in_dnip = ntohl(np->in_ndstmsk);
2962145522Sdarrenr				move = 0;
2963145522Sdarrenr			} else {
2964255332Scy				np->in_dnip = ntohl(np->in_ndstaddr);
2965145522Sdarrenr			}
2966145522Sdarrenr		}
2967255332Scy		if (hm != NULL)
2968255332Scy			ipf_nat_hostmapdel(softc, &hm);
2969145522Sdarrenr
2970255332Scy	} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) {
2971255332Scy		i6addr_t in6;
2972255332Scy
2973145522Sdarrenr		/*
2974145522Sdarrenr		 * 0/32 - use the interface's IP address.
2975145522Sdarrenr		 */
2976255332Scy		if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp,
2977255332Scy			       &in6, NULL) == -1) {
2978255332Scy			NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2);
2979145522Sdarrenr			return -1;
2980255332Scy		}
2981255332Scy		in.s_addr = ntohl(in6.in4.s_addr);
2982145522Sdarrenr
2983255332Scy	} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) {
2984145522Sdarrenr		/*
2985145522Sdarrenr		 * 0/0 - use the original destination address/port.
2986145522Sdarrenr		 */
2987145522Sdarrenr		in.s_addr = ntohl(fin->fin_daddr);
2988145522Sdarrenr
2989145522Sdarrenr	} else if (np->in_redir == NAT_BIMAP &&
2990255332Scy		   np->in_ndstmsk == np->in_odstmsk) {
2991145522Sdarrenr		/*
2992145522Sdarrenr		 * map the address block in a 1:1 fashion
2993145522Sdarrenr		 */
2994255332Scy		in.s_addr = np->in_ndstaddr;
2995255332Scy		in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk;
2996145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
2997145522Sdarrenr	} else {
2998255332Scy		in.s_addr = ntohl(np->in_ndstaddr);
2999145522Sdarrenr	}
3000145522Sdarrenr
3001255332Scy	if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
3002145522Sdarrenr		nport = dport;
3003145522Sdarrenr	else {
3004145522Sdarrenr		/*
3005145522Sdarrenr		 * Whilst not optimized for the case where
3006145522Sdarrenr		 * pmin == pmax, the gain is not significant.
3007145522Sdarrenr		 */
3008145522Sdarrenr		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
3009255332Scy		    (np->in_odport != np->in_dtop)) {
3010255332Scy			nport = ntohs(dport) - np->in_odport + np->in_dpmax;
3011145522Sdarrenr			nport = htons(nport);
3012255332Scy		} else {
3013255332Scy			nport = htons(np->in_dpnext);
3014255332Scy			np->in_dpnext++;
3015255332Scy			if (np->in_dpnext > np->in_dpmax)
3016255332Scy				np->in_dpnext = np->in_dpmin;
3017255332Scy		}
3018145522Sdarrenr	}
3019145522Sdarrenr
3020145522Sdarrenr	/*
3021145522Sdarrenr	 * When the redirect-to address is set to 0.0.0.0, just
3022145522Sdarrenr	 * assume a blank `forwarding' of the packet.  We don't
3023145522Sdarrenr	 * setup any translation for this either.
3024145522Sdarrenr	 */
3025145522Sdarrenr	if (in.s_addr == 0) {
3026255332Scy		if (nport == dport) {
3027255332Scy			NBUMPSIDED(0, ns_xlate_null);
3028145522Sdarrenr			return -1;
3029255332Scy		}
3030145522Sdarrenr		in.s_addr = ntohl(fin->fin_daddr);
3031145522Sdarrenr	}
3032145522Sdarrenr
3033170268Sdarrenr	/*
3034170268Sdarrenr	 * Check to see if this redirect mapping already exists and if
3035170268Sdarrenr	 * it does, return "failure" (allowing it to be created will just
3036170268Sdarrenr	 * cause one or both of these "connections" to stop working.)
3037170268Sdarrenr	 */
3038170268Sdarrenr	inb.s_addr = htonl(in.s_addr);
3039170268Sdarrenr	sp = fin->fin_data[0];
3040170268Sdarrenr	dp = fin->fin_data[1];
3041170268Sdarrenr	fin->fin_data[1] = fin->fin_data[0];
3042170268Sdarrenr	fin->fin_data[0] = ntohs(nport);
3043255332Scy	natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
3044170268Sdarrenr			     (u_int)fin->fin_p, inb, fin->fin_src);
3045170268Sdarrenr	fin->fin_data[0] = sp;
3046170268Sdarrenr	fin->fin_data[1] = dp;
3047255332Scy	if (natl != NULL) {
3048255332Scy		DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl);
3049255332Scy		NBUMPSIDE(0, ns_xlate_exists);
3050170268Sdarrenr		return -1;
3051255332Scy	}
3052170268Sdarrenr
3053255332Scy	nat->nat_ndstaddr = htonl(in.s_addr);
3054255332Scy	nat->nat_odstip = fin->fin_dst;
3055255332Scy	nat->nat_nsrcip = fin->fin_src;
3056255332Scy	nat->nat_osrcip = fin->fin_src;
3057153876Sguido	if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
3058255332Scy		nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src,
3059255332Scy					      fin->fin_dst, in, (u_32_t)dport);
3060145522Sdarrenr
3061145522Sdarrenr	if (flags & IPN_TCPUDP) {
3062255332Scy		nat->nat_odport = dport;
3063255332Scy		nat->nat_ndport = nport;
3064255332Scy		nat->nat_osport = sport;
3065255332Scy		nat->nat_nsport = sport;
3066145522Sdarrenr		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
3067145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
3068255332Scy		nat->nat_oicmpid = fin->fin_data[1];
3069145522Sdarrenr		((icmphdr_t *)fin->fin_dp)->icmp_id = nport;
3070255332Scy		nat->nat_nicmpid = nport;
3071145522Sdarrenr	}
3072145522Sdarrenr
3073145522Sdarrenr	return move;
3074145522Sdarrenr}
3075145522Sdarrenr
3076145522Sdarrenr/* ------------------------------------------------------------------------ */
3077255332Scy/* Function:    ipf_nat_add                                                 */
3078145522Sdarrenr/* Returns:     nat_t* - NULL == failure to create new NAT structure,       */
3079145522Sdarrenr/*                       else pointer to new NAT structure                  */
3080145522Sdarrenr/* Parameters:  fin(I)       - pointer to packet information                */
3081145522Sdarrenr/*              np(I)        - pointer to NAT rule                          */
3082145522Sdarrenr/*              natsave(I)   - pointer to where to store NAT struct pointer */
3083145522Sdarrenr/*              flags(I)     - flags describing the current packet          */
3084145522Sdarrenr/*              direction(I) - direction of packet (in/out)                 */
3085145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
3086145522Sdarrenr/*                                                                          */
3087145522Sdarrenr/* Attempts to create a new NAT entry.  Does not actually change the packet */
3088145522Sdarrenr/* in any way.                                                              */
3089145522Sdarrenr/*                                                                          */
3090145522Sdarrenr/* This fucntion is in three main parts: (1) deal with creating a new NAT   */
3091145522Sdarrenr/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
3092145522Sdarrenr/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
3093145522Sdarrenr/* and (3) building that structure and putting it into the NAT table(s).    */
3094161356Sguido/*                                                                          */
3095161356Sguido/* NOTE: natsave should NOT be used top point back to an ipstate_t struct   */
3096161356Sguido/*       as it can result in memory being corrupted.                        */
3097145522Sdarrenr/* ------------------------------------------------------------------------ */
3098255332Scynat_t *
3099255332Scyipf_nat_add(fin, np, natsave, flags, direction)
3100255332Scy	fr_info_t *fin;
3101255332Scy	ipnat_t *np;
3102255332Scy	nat_t **natsave;
3103255332Scy	u_int flags;
3104255332Scy	int direction;
310553642Sguido{
3106255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3107255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
310860852Sdarrenr	hostmap_t *hm = NULL;
310960852Sdarrenr	nat_t *nat, *natl;
3110255332Scy	natstat_t *nsp;
3111145522Sdarrenr	u_int nflags;
3112145522Sdarrenr	natinfo_t ni;
3113145522Sdarrenr	int move;
311453642Sguido
3115255332Scy	nsp = &softn->ipf_nat_stats;
3116255332Scy
3117255332Scy	if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) >
3118255332Scy	    softn->ipf_nat_table_wm_high) {
3119255332Scy		softn->ipf_nat_doflush = 1;
3120255332Scy	}
3121255332Scy
3122255332Scy	if (nsp->ns_active >= softn->ipf_nat_table_max) {
3123255332Scy		NBUMPSIDED(fin->fin_out, ns_table_max);
3124130886Sdarrenr		return NULL;
3125130886Sdarrenr	}
3126130886Sdarrenr
3127145522Sdarrenr	move = 1;
3128145522Sdarrenr	nflags = np->in_flags & flags;
3129145522Sdarrenr	nflags &= NAT_FROMRULE;
313053642Sguido
3131145522Sdarrenr	ni.nai_np = np;
3132170268Sdarrenr	ni.nai_dport = 0;
3133170268Sdarrenr	ni.nai_sport = 0;
3134145522Sdarrenr
313553642Sguido	/* Give me a new nat */
313653642Sguido	KMALLOC(nat, nat_t *);
313760852Sdarrenr	if (nat == NULL) {
3138255332Scy		NBUMPSIDED(fin->fin_out, ns_memfail);
3139130886Sdarrenr		/*
3140130886Sdarrenr		 * Try to automatically tune the max # of entries in the
3141130886Sdarrenr		 * table allowed to be less than what will cause kmem_alloc()
3142130886Sdarrenr		 * to fail and try to eliminate panics due to out of memory
3143130886Sdarrenr		 * conditions arising.
3144130886Sdarrenr		 */
3145255332Scy		if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) &&
3146255332Scy		    (nsp->ns_active > 100)) {
3147255332Scy			softn->ipf_nat_table_max = nsp->ns_active - 100;
3148255332Scy			printf("table_max reduced to %d\n",
3149255332Scy				softn->ipf_nat_table_max);
3150130886Sdarrenr		}
315153642Sguido		return NULL;
315260852Sdarrenr	}
315353642Sguido
3154255332Scy	if (flags & IPN_ICMPQUERY) {
3155145522Sdarrenr		/*
3156145522Sdarrenr		 * In the ICMP query NAT code, we translate the ICMP id fields
3157145522Sdarrenr		 * to make them unique. This is indepedent of the ICMP type
3158145522Sdarrenr		 * (e.g. in the unlikely event that a host sends an echo and
3159145522Sdarrenr		 * an tstamp request with the same id, both packets will have
3160145522Sdarrenr		 * their ip address/id field changed in the same way).
3161145522Sdarrenr		 */
3162145522Sdarrenr		/* The icmp_id field is used by the sender to identify the
3163145522Sdarrenr		 * process making the icmp request. (the receiver justs
3164145522Sdarrenr		 * copies it back in its response). So, it closely matches
3165145522Sdarrenr		 * the concept of source port. We overlay sport, so we can
3166145522Sdarrenr		 * maximally reuse the existing code.
3167145522Sdarrenr		 */
3168255332Scy		ni.nai_sport = fin->fin_data[1];
3169255332Scy		ni.nai_dport = 0;
3170145522Sdarrenr	}
3171145522Sdarrenr
317253642Sguido	bzero((char *)nat, sizeof(*nat));
317353642Sguido	nat->nat_flags = flags;
3174170268Sdarrenr	nat->nat_redir = np->in_redir;
3175255332Scy	nat->nat_dir = direction;
3176255332Scy	nat->nat_pr[0] = fin->fin_p;
3177255332Scy	nat->nat_pr[1] = fin->fin_p;
3178145522Sdarrenr
317953642Sguido	/*
3180255332Scy	 * Search the current table for a match and create a new mapping
3181255332Scy	 * if there is none found.
318253642Sguido	 */
3183255332Scy	if (np->in_redir & NAT_DIVERTUDP) {
3184255332Scy		move = ipf_nat_newdivert(fin, nat, &ni);
3185255332Scy
3186255332Scy	} else if (np->in_redir & NAT_REWRITE) {
3187255332Scy		move = ipf_nat_newrewrite(fin, nat, &ni);
3188255332Scy
3189255332Scy	} else if (direction == NAT_OUTBOUND) {
319053642Sguido		/*
3191145522Sdarrenr		 * We can now arrange to call this for the same connection
3192145522Sdarrenr		 * because ipf_nat_new doesn't protect the code path into
3193145522Sdarrenr		 * this function.
319453642Sguido		 */
3195255332Scy		natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p,
3196145522Sdarrenr				     fin->fin_src, fin->fin_dst);
3197145522Sdarrenr		if (natl != NULL) {
3198161356Sguido			KFREE(nat);
3199145522Sdarrenr			nat = natl;
3200145522Sdarrenr			goto done;
3201145522Sdarrenr		}
320253642Sguido
3203255332Scy		move = ipf_nat_newmap(fin, nat, &ni);
320453642Sguido	} else {
320553642Sguido		/*
3206255332Scy		 * NAT_INBOUND is used for redirects rules
320753642Sguido		 */
3208255332Scy		natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p,
3209255332Scy					fin->fin_src, fin->fin_dst);
3210145522Sdarrenr		if (natl != NULL) {
3211161356Sguido			KFREE(nat);
3212145522Sdarrenr			nat = natl;
3213145522Sdarrenr			goto done;
321460852Sdarrenr		}
321553642Sguido
3216255332Scy		move = ipf_nat_newrdr(fin, nat, &ni);
3217145522Sdarrenr	}
3218255332Scy	if (move == -1)
3219255332Scy		goto badnat;
322053642Sguido
3221255332Scy	np = ni.nai_np;
3222255332Scy
3223255332Scy	nat->nat_mssclamp = np->in_mssclamp;
3224255332Scy	nat->nat_me = natsave;
3225255332Scy	nat->nat_fr = fin->fin_fr;
3226255332Scy	nat->nat_rev = fin->fin_rev;
3227255332Scy	nat->nat_ptr = np;
3228255332Scy	nat->nat_dlocal = np->in_dlocal;
3229255332Scy
3230255332Scy	if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) {
3231255332Scy		if (ipf_proxy_new(fin, nat) == -1) {
3232255332Scy			NBUMPSIDED(fin->fin_out, ns_appr_fail);
3233255332Scy			goto badnat;
323453642Sguido		}
323553642Sguido	}
323653642Sguido
3237255332Scy	nat->nat_ifps[0] = np->in_ifps[0];
3238255332Scy	if (np->in_ifps[0] != NULL) {
3239255332Scy		COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]);
3240145522Sdarrenr	}
3241145522Sdarrenr
3242255332Scy	nat->nat_ifps[1] = np->in_ifps[1];
3243255332Scy	if (np->in_ifps[1] != NULL) {
3244255332Scy		COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]);
3245255332Scy	}
324653642Sguido
3247255332Scy	if (ipf_nat_finalise(fin, nat) == -1) {
3248255332Scy		goto badnat;
3249255332Scy	}
325053642Sguido
3251255332Scy	np->in_use++;
325253642Sguido
3253255332Scy	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
3254255332Scy		if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) {
3255255332Scy			ipf_nat_delrdr(softn, np);
3256255332Scy			ipf_nat_addrdr(softn, np);
3257255332Scy		} else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) {
3258255332Scy			ipf_nat_delmap(softn, np);
3259255332Scy			ipf_nat_addmap(softn, np);
3260145522Sdarrenr		}
3261145522Sdarrenr	}
326253642Sguido
3263145522Sdarrenr	if (flags & SI_WILDP)
3264255332Scy		nsp->ns_wilds++;
3265255332Scy	nsp->ns_proto[nat->nat_pr[0]]++;
3266255332Scy
3267145522Sdarrenr	goto done;
3268145522Sdarrenrbadnat:
3269255332Scy	DT2(ns_badnatnew, fr_info_t *, fin, nat_t *, nat);
3270255332Scy	NBUMPSIDE(fin->fin_out, ns_badnatnew);
3271145522Sdarrenr	if ((hm = nat->nat_hm) != NULL)
3272255332Scy		ipf_nat_hostmapdel(softc, &hm);
3273145522Sdarrenr	KFREE(nat);
3274145522Sdarrenr	nat = NULL;
3275145522Sdarrenrdone:
3276255332Scy	if (nat != NULL && np != NULL)
3277255332Scy		np->in_hits++;
3278255332Scy	if (natsave != NULL)
3279255332Scy		*natsave = nat;
3280145522Sdarrenr	return nat;
3281145522Sdarrenr}
328260852Sdarrenr
328360852Sdarrenr
3284145522Sdarrenr/* ------------------------------------------------------------------------ */
3285255332Scy/* Function:    ipf_nat_finalise                                            */
3286145522Sdarrenr/* Returns:     int - 0 == sucess, -1 == failure                            */
3287145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
3288145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
3289145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
3290145522Sdarrenr/*                                                                          */
3291145522Sdarrenr/* This is the tail end of constructing a new NAT entry and is the same     */
3292145522Sdarrenr/* for both IPv4 and IPv6.                                                  */
3293145522Sdarrenr/* ------------------------------------------------------------------------ */
3294145522Sdarrenr/*ARGSUSED*/
3295255332Scystatic int
3296255332Scyipf_nat_finalise(fin, nat)
3297255332Scy	fr_info_t *fin;
3298255332Scy	nat_t *nat;
3299145522Sdarrenr{
3300255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3301255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3302255332Scy	u_32_t sum1, sum2, sumd;
3303145522Sdarrenr	frentry_t *fr;
3304255332Scy	u_32_t flags;
3305255332Scy#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC)
3306255332Scy	qpktinfo_t *qpi = fin->fin_qpi;
3307255332Scy#endif
3308145522Sdarrenr
3309255332Scy	flags = nat->nat_flags;
3310145522Sdarrenr
3311255332Scy	switch (nat->nat_pr[0])
3312255332Scy	{
3313255332Scy	case IPPROTO_ICMP :
3314255332Scy		sum1 = LONG_SUM(ntohs(nat->nat_oicmpid));
3315255332Scy		sum2 = LONG_SUM(ntohs(nat->nat_nicmpid));
3316255332Scy		CALC_SUMD(sum1, sum2, sumd);
3317255332Scy		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
3318255332Scy
3319255332Scy		break;
3320255332Scy
3321255332Scy	default :
3322255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \
3323255332Scy				ntohs(nat->nat_osport));
3324255332Scy		sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \
3325255332Scy				ntohs(nat->nat_nsport));
3326255332Scy		CALC_SUMD(sum1, sum2, sumd);
3327255332Scy		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
3328255332Scy
3329255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \
3330255332Scy				ntohs(nat->nat_odport));
3331255332Scy		sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \
3332255332Scy				ntohs(nat->nat_ndport));
3333255332Scy		CALC_SUMD(sum1, sum2, sumd);
3334255332Scy		nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16);
3335255332Scy		break;
3336161356Sguido	}
3337255332Scy
3338255332Scy	/*
3339255332Scy	 * Compute the partial checksum, just in case.
3340255332Scy	 * This is only ever placed into outbound packets so care needs
3341255332Scy	 * to be taken over which pair of addresses are used.
3342255332Scy	 */
3343255332Scy	if (nat->nat_dir == NAT_OUTBOUND) {
3344255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
3345255332Scy		sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr));
3346255332Scy	} else {
3347255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr));
3348255332Scy		sum1 += LONG_SUM(ntohl(nat->nat_odstaddr));
3349161356Sguido	}
3350255332Scy	sum1 += nat->nat_pr[1];
3351255332Scy	nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16);
3352145522Sdarrenr
3353255332Scy	sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr));
3354255332Scy	sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
3355255332Scy	CALC_SUMD(sum1, sum2, sumd);
3356255332Scy	nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
335792685Sdarrenr
3358255332Scy	sum1 = LONG_SUM(ntohl(nat->nat_odstaddr));
3359255332Scy	sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr));
3360255332Scy	CALC_SUMD(sum1, sum2, sumd);
3361255332Scy	nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16);
336292685Sdarrenr
3363255332Scy	nat->nat_v[0] = 4;
3364255332Scy	nat->nat_v[1] = 4;
3365255332Scy
3366255332Scy	if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
3367255332Scy		nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
3368255332Scy	}
3369255332Scy
3370255332Scy	if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
3371255332Scy		nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
3372255332Scy	}
3373255332Scy
3374255332Scy	if ((nat->nat_flags & SI_CLONE) == 0)
3375255332Scy		nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat);
3376255332Scy
3377255332Scy	if (ipf_nat_insert(softc, softn, nat) == 0) {
3378255332Scy		if (softn->ipf_nat_logging)
3379255332Scy			ipf_nat_log(softc, softn, nat, NL_NEW);
3380255332Scy		fr = nat->nat_fr;
3381145522Sdarrenr		if (fr != NULL) {
3382145522Sdarrenr			MUTEX_ENTER(&fr->fr_lock);
3383145522Sdarrenr			fr->fr_ref++;
3384145522Sdarrenr			MUTEX_EXIT(&fr->fr_lock);
3385145522Sdarrenr		}
3386145522Sdarrenr		return 0;
3387145522Sdarrenr	}
338892685Sdarrenr
3389255332Scy	NBUMPSIDED(fin->fin_out, ns_unfinalised);
3390145522Sdarrenr	/*
3391145522Sdarrenr	 * nat_insert failed, so cleanup time...
3392145522Sdarrenr	 */
3393255332Scy	if (nat->nat_sync != NULL)
3394255332Scy		ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync);
3395145522Sdarrenr	return -1;
339653642Sguido}
339753642Sguido
339853642Sguido
3399145522Sdarrenr/* ------------------------------------------------------------------------ */
3400255332Scy/* Function:    ipf_nat_insert                                              */
3401255332Scy/* Returns:     int - 0 == sucess, -1 == failure                            */
3402255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
3403255332Scy/*              softn(I) - pointer to NAT context structure                 */
3404255332Scy/*              nat(I) - pointer to NAT structure                           */
3405255332Scy/* Write Lock:  ipf_nat                                                     */
3406145522Sdarrenr/*                                                                          */
3407145522Sdarrenr/* Insert a NAT entry into the hash tables for searching and add it to the  */
3408145522Sdarrenr/* list of active NAT entries.  Adjust global counters when complete.       */
3409145522Sdarrenr/* ------------------------------------------------------------------------ */
3410255332Scyint
3411255332Scyipf_nat_insert(softc, softn, nat)
3412255332Scy	ipf_main_softc_t *softc;
3413255332Scy	ipf_nat_softc_t *softn;
3414255332Scy	nat_t *nat;
341560852Sdarrenr{
3416255332Scy	u_int hv0, hv1;
3417255332Scy	u_int sp, dp;
3418255332Scy	ipnat_t *in;
341960852Sdarrenr
3420145522Sdarrenr	/*
3421145522Sdarrenr	 * Try and return an error as early as possible, so calculate the hash
3422145522Sdarrenr	 * entry numbers first and then proceed.
3423145522Sdarrenr	 */
3424145522Sdarrenr	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
3425255332Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
3426255332Scy			sp = nat->nat_osport;
3427255332Scy			dp = nat->nat_odport;
3428255332Scy		} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
3429255332Scy			sp = 0;
3430255332Scy			dp = nat->nat_oicmpid;
3431255332Scy		} else {
3432255332Scy			sp = 0;
3433255332Scy			dp = 0;
3434255332Scy		}
3435255332Scy		hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff);
3436255332Scy		hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff);
3437255332Scy		/*
3438255332Scy		 * TRACE nat_osrcaddr, nat_osport, nat_odstaddr,
3439255332Scy		 * nat_odport, hv0
3440255332Scy		 */
3441255332Scy
3442255332Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
3443255332Scy			sp = nat->nat_nsport;
3444255332Scy			dp = nat->nat_ndport;
3445255332Scy		} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
3446255332Scy			sp = 0;
3447255332Scy			dp = nat->nat_nicmpid;
3448255332Scy		} else {
3449255332Scy			sp = 0;
3450255332Scy			dp = 0;
3451255332Scy		}
3452255332Scy		hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff);
3453255332Scy		hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff);
3454255332Scy		/*
3455255332Scy		 * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr,
3456255332Scy		 * nat_ndport, hv1
3457255332Scy		 */
345880482Sdarrenr	} else {
3459255332Scy		hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff);
3460255332Scy		hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff);
3461255332Scy		/* TRACE nat_osrcaddr, nat_odstaddr, hv0 */
346280482Sdarrenr
3463255332Scy		hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff);
3464255332Scy		hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff);
3465255332Scy		/* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */
3466145522Sdarrenr	}
3467145522Sdarrenr
3468255332Scy	nat->nat_hv[0] = hv0;
3469255332Scy	nat->nat_hv[1] = hv1;
3470145522Sdarrenr
3471145522Sdarrenr	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
3472145522Sdarrenr
3473255332Scy	in = nat->nat_ptr;
3474255332Scy	nat->nat_ref = nat->nat_me ? 2 : 1;
3475145522Sdarrenr
3476145522Sdarrenr	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
3477255332Scy	nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4);
3478145522Sdarrenr
3479161356Sguido	if (nat->nat_ifnames[1][0] != '\0') {
3480145522Sdarrenr		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
3481255332Scy		nat->nat_ifps[1] = ipf_resolvenic(softc,
3482255332Scy						  nat->nat_ifnames[1], 4);
3483255332Scy	} else if (in->in_ifnames[1] != -1) {
3484255332Scy		char *name;
3485255332Scy
3486255332Scy		name = in->in_names + in->in_ifnames[1];
3487255332Scy		if (name[1] != '\0' && name[0] != '-' && name[0] != '*') {
3488255332Scy			(void) strncpy(nat->nat_ifnames[1],
3489255332Scy				       nat->nat_ifnames[0], LIFNAMSIZ);
3490255332Scy			nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
3491255332Scy			nat->nat_ifps[1] = nat->nat_ifps[0];
3492255332Scy		}
3493145522Sdarrenr	}
3494255332Scy	if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
3495255332Scy		nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
3496255332Scy	}
3497255332Scy	if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
3498255332Scy		nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
3499255332Scy	}
3500145522Sdarrenr
3501255332Scy	return ipf_nat_hashtab_add(softc, softn, nat);
3502255332Scy}
3503145522Sdarrenr
3504255332Scy
3505255332Scy/* ------------------------------------------------------------------------ */
3506255332Scy/* Function:    ipf_nat_hashtab_add                                         */
3507255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
3508255332Scy/*              softn(I) - pointer to NAT context structure                 */
3509255332Scy/*              nat(I) - pointer to NAT structure                           */
3510255332Scy/*                                                                          */
3511255332Scy/* Handle the insertion of a NAT entry into the table/list.                 */
3512255332Scy/* ------------------------------------------------------------------------ */
3513255332Scyint
3514255332Scyipf_nat_hashtab_add(softc, softn, nat)
3515255332Scy	ipf_main_softc_t *softc;
3516255332Scy	ipf_nat_softc_t *softn;
3517255332Scy	nat_t *nat;
3518255332Scy{
3519255332Scy	nat_t **natp;
3520255332Scy	u_int hv0;
3521255332Scy	u_int hv1;
3522255332Scy
3523255332Scy	hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz;
3524255332Scy	hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz;
3525255332Scy
3526255332Scy	if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) {
3527255332Scy		u_int swap;
3528255332Scy
3529255332Scy		swap = hv0;
3530255332Scy		hv0 = hv1;
3531255332Scy		hv1 = swap;
3532255332Scy	}
3533255332Scy
3534255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >=
3535255332Scy	    softn->ipf_nat_maxbucket) {
3536255332Scy		DT1(ns_bucket_max_0, int,
3537255332Scy		    softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]);
3538255332Scy		NBUMPSIDE(0, ns_bucket_max);
3539255332Scy		return -1;
3540255332Scy	}
3541255332Scy
3542255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >=
3543255332Scy	    softn->ipf_nat_maxbucket) {
3544255332Scy		DT1(ns_bucket_max_1, int,
3545255332Scy		    softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]);
3546255332Scy		NBUMPSIDE(1, ns_bucket_max);
3547255332Scy		return -1;
3548255332Scy	}
3549255332Scy
3550255332Scy	/*
3551255332Scy	 * The ordering of operations in the list and hash table insertion
3552255332Scy	 * is very important.  The last operation for each task should be
3553255332Scy	 * to update the top of the list, after all the "nexts" have been
3554255332Scy	 * done so that walking the list while it is being done does not
3555255332Scy	 * find strange pointers.
3556255332Scy	 *
3557255332Scy	 * Global list of NAT instances
3558255332Scy	 */
3559255332Scy	nat->nat_next = softn->ipf_nat_instances;
3560255332Scy	nat->nat_pnext = &softn->ipf_nat_instances;
3561255332Scy	if (softn->ipf_nat_instances)
3562255332Scy		softn->ipf_nat_instances->nat_pnext = &nat->nat_next;
3563255332Scy	softn->ipf_nat_instances = nat;
3564255332Scy
3565255332Scy	/*
3566255332Scy	 * Inbound hash table.
3567255332Scy	 */
3568255332Scy	natp = &softn->ipf_nat_table[0][hv0];
356967614Sdarrenr	nat->nat_phnext[0] = natp;
357060852Sdarrenr	nat->nat_hnext[0] = *natp;
3571255332Scy	if (*natp) {
3572255332Scy		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
3573255332Scy	} else {
3574255332Scy		NBUMPSIDE(0, ns_inuse);
3575255332Scy	}
357660852Sdarrenr	*natp = nat;
3577255332Scy	NBUMPSIDE(0, ns_bucketlen[hv0]);
357867614Sdarrenr
3579255332Scy	/*
3580255332Scy	 * Outbound hash table.
3581255332Scy	 */
3582255332Scy	natp = &softn->ipf_nat_table[1][hv1];
3583255332Scy	nat->nat_phnext[1] = natp;
3584255332Scy	nat->nat_hnext[1] = *natp;
358567614Sdarrenr	if (*natp)
358667614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
3587255332Scy	else {
3588255332Scy		NBUMPSIDE(1, ns_inuse);
3589255332Scy	}
359060852Sdarrenr	*natp = nat;
3591255332Scy	NBUMPSIDE(1, ns_bucketlen[hv1]);
359260852Sdarrenr
3593255332Scy	ipf_nat_setqueue(softc, softn, nat);
3594145522Sdarrenr
3595255332Scy	if (nat->nat_dir & NAT_OUTBOUND) {
3596255332Scy		NBUMPSIDE(1, ns_added);
3597255332Scy	} else {
3598255332Scy		NBUMPSIDE(0, ns_added);
3599255332Scy	}
3600255332Scy	softn->ipf_nat_stats.ns_active++;
3601145522Sdarrenr	return 0;
360260852Sdarrenr}
360360852Sdarrenr
360460852Sdarrenr
3605145522Sdarrenr/* ------------------------------------------------------------------------ */
3606255332Scy/* Function:    ipf_nat_icmperrorlookup                                     */
3607145522Sdarrenr/* Returns:     nat_t* - point to matching NAT structure                    */
3608145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
3609145522Sdarrenr/*              dir(I) - direction of packet (in/out)                       */
3610145522Sdarrenr/*                                                                          */
3611145522Sdarrenr/* Check if the ICMP error message is related to an existing TCP, UDP or    */
3612145522Sdarrenr/* ICMP query nat entry.  It is assumed that the packet is already of the   */
3613145522Sdarrenr/* the required length.                                                     */
3614145522Sdarrenr/* ------------------------------------------------------------------------ */
3615255332Scynat_t *
3616255332Scyipf_nat_icmperrorlookup(fin, dir)
3617255332Scy	fr_info_t *fin;
3618255332Scy	int dir;
361953642Sguido{
3620255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3621255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3622145522Sdarrenr	int flags = 0, type, minlen;
3623145522Sdarrenr	icmphdr_t *icmp, *orgicmp;
3624255332Scy	nat_stat_side_t *nside;
362553642Sguido	tcphdr_t *tcp = NULL;
3626145522Sdarrenr	u_short data[2];
3627145522Sdarrenr	nat_t *nat;
362853642Sguido	ip_t *oip;
3629145522Sdarrenr	u_int p;
363053642Sguido
3631145522Sdarrenr	icmp = fin->fin_dp;
3632145522Sdarrenr	type = icmp->icmp_type;
3633255332Scy	nside = &softn->ipf_nat_stats.ns_side[fin->fin_out];
363453642Sguido	/*
363553642Sguido	 * Does it at least have the return (basic) IP header ?
363653642Sguido	 * Only a basic IP header (no options) should be with an ICMP error
3637145522Sdarrenr	 * header.  Also, if it's not an error type, then return.
363853642Sguido	 */
3639255332Scy	if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) {
3640255332Scy		ATOMIC_INCL(nside->ns_icmp_basic);
364153642Sguido		return NULL;
3642255332Scy	}
3643145522Sdarrenr
364453642Sguido	/*
3645145522Sdarrenr	 * Check packet size
364653642Sguido	 */
364753642Sguido	oip = (ip_t *)((char *)fin->fin_dp + 8);
3648145522Sdarrenr	minlen = IP_HL(oip) << 2;
3649145522Sdarrenr	if ((minlen < sizeof(ip_t)) ||
3650255332Scy	    (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) {
3651255332Scy		ATOMIC_INCL(nside->ns_icmp_size);
365253642Sguido		return NULL;
3653255332Scy	}
3654255332Scy
365564580Sdarrenr	/*
365664580Sdarrenr	 * Is the buffer big enough for all of it ?  It's the size of the IP
365764580Sdarrenr	 * header claimed in the encapsulated part which is of concern.  It
365864580Sdarrenr	 * may be too big to be in this buffer but not so big that it's
365964580Sdarrenr	 * outside the ICMP packet, leading to TCP deref's causing problems.
366064580Sdarrenr	 * This is possible because we don't know how big oip_hl is when we
3661255332Scy	 * do the pullup early in ipf_check() and thus can't gaurantee it is
366264580Sdarrenr	 * all here now.
366364580Sdarrenr	 */
3664255332Scy#ifdef  ipf_nat_KERNEL
366564580Sdarrenr	{
366664580Sdarrenr	mb_t *m;
366764580Sdarrenr
3668145522Sdarrenr	m = fin->fin_m;
3669145522Sdarrenr# if defined(MENTAT)
3670255332Scy	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
3671255332Scy	    (char *)m->b_wptr) {
3672255332Scy		ATOMIC_INCL(nside->ns_icmp_mbuf);
367364580Sdarrenr		return NULL;
3674255332Scy	}
367564580Sdarrenr# else
367664580Sdarrenr	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
3677255332Scy	    (char *)fin->fin_ip + M_LEN(m)) {
3678255332Scy		ATOMIC_INCL(nside->ns_icmp_mbuf);
367964580Sdarrenr		return NULL;
3680255332Scy	}
368164580Sdarrenr# endif
368264580Sdarrenr	}
368364580Sdarrenr#endif
368464580Sdarrenr
3685255332Scy	if (fin->fin_daddr != oip->ip_src.s_addr) {
3686255332Scy		ATOMIC_INCL(nside->ns_icmp_address);
3687145522Sdarrenr		return NULL;
3688255332Scy	}
3689145522Sdarrenr
3690145522Sdarrenr	p = oip->ip_p;
3691145522Sdarrenr	if (p == IPPROTO_TCP)
369253642Sguido		flags = IPN_TCP;
3693145522Sdarrenr	else if (p == IPPROTO_UDP)
369453642Sguido		flags = IPN_UDP;
3695145522Sdarrenr	else if (p == IPPROTO_ICMP) {
3696145522Sdarrenr		orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2));
3697145522Sdarrenr
3698145522Sdarrenr		/* see if this is related to an ICMP query */
3699255332Scy		if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) {
3700145522Sdarrenr			data[0] = fin->fin_data[0];
3701145522Sdarrenr			data[1] = fin->fin_data[1];
3702145522Sdarrenr			fin->fin_data[0] = 0;
3703145522Sdarrenr			fin->fin_data[1] = orgicmp->icmp_id;
3704145522Sdarrenr
3705145522Sdarrenr			flags = IPN_ICMPERR|IPN_ICMPQUERY;
3706145522Sdarrenr			/*
3707145522Sdarrenr			 * NOTE : dir refers to the direction of the original
3708145522Sdarrenr			 *        ip packet. By definition the icmp error
3709145522Sdarrenr			 *        message flows in the opposite direction.
3710145522Sdarrenr			 */
3711145522Sdarrenr			if (dir == NAT_INBOUND)
3712255332Scy				nat = ipf_nat_inlookup(fin, flags, p,
3713255332Scy						       oip->ip_dst,
3714255332Scy						       oip->ip_src);
3715145522Sdarrenr			else
3716255332Scy				nat = ipf_nat_outlookup(fin, flags, p,
3717255332Scy							oip->ip_dst,
3718255332Scy							oip->ip_src);
3719145522Sdarrenr			fin->fin_data[0] = data[0];
3720145522Sdarrenr			fin->fin_data[1] = data[1];
3721145522Sdarrenr			return nat;
3722145522Sdarrenr		}
3723145522Sdarrenr	}
3724255332Scy
372553642Sguido	if (flags & IPN_TCPUDP) {
372664580Sdarrenr		minlen += 8;		/* + 64bits of data to get ports */
3727255332Scy		/* TRACE (fin,minlen) */
3728255332Scy		if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) {
3729255332Scy			ATOMIC_INCL(nside->ns_icmp_short);
373064580Sdarrenr			return NULL;
3731255332Scy		}
373292685Sdarrenr
373392685Sdarrenr		data[0] = fin->fin_data[0];
373492685Sdarrenr		data[1] = fin->fin_data[1];
3735145522Sdarrenr		tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2));
373692685Sdarrenr		fin->fin_data[0] = ntohs(tcp->th_dport);
373792685Sdarrenr		fin->fin_data[1] = ntohs(tcp->th_sport);
373892685Sdarrenr
373992685Sdarrenr		if (dir == NAT_INBOUND) {
3740255332Scy			nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst,
3741255332Scy					       oip->ip_src);
374292685Sdarrenr		} else {
3743255332Scy			nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst,
3744145522Sdarrenr					    oip->ip_src);
374592685Sdarrenr		}
374692685Sdarrenr		fin->fin_data[0] = data[0];
374792685Sdarrenr		fin->fin_data[1] = data[1];
374892685Sdarrenr		return nat;
374953642Sguido	}
375060852Sdarrenr	if (dir == NAT_INBOUND)
3751255332Scy		nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
375260852Sdarrenr	else
3753255332Scy		nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
3754255332Scy
3755255332Scy	return nat;
375653642Sguido}
375753642Sguido
375853642Sguido
3759145522Sdarrenr/* ------------------------------------------------------------------------ */
3760255332Scy/* Function:    ipf_nat_icmperror                                           */
3761145522Sdarrenr/* Returns:     nat_t* - point to matching NAT structure                    */
3762145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
3763145522Sdarrenr/*              nflags(I) - NAT flags for this packet                       */
3764145522Sdarrenr/*              dir(I)    - direction of packet (in/out)                    */
3765145522Sdarrenr/*                                                                          */
3766145522Sdarrenr/* Fix up an ICMP packet which is an error message for an existing NAT      */
3767145522Sdarrenr/* session.  This will correct both packet header data and checksums.       */
3768145522Sdarrenr/*                                                                          */
3769145522Sdarrenr/* This should *ONLY* be used for incoming ICMP error packets to make sure  */
3770145522Sdarrenr/* a NAT'd ICMP packet gets correctly recognised.                           */
3771145522Sdarrenr/* ------------------------------------------------------------------------ */
3772255332Scynat_t *
3773255332Scyipf_nat_icmperror(fin, nflags, dir)
3774255332Scy	fr_info_t *fin;
3775255332Scy	u_int *nflags;
3776255332Scy	int dir;
377753642Sguido{
3778255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3779255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3780145522Sdarrenr	u_32_t sum1, sum2, sumd, sumd2;
3781255332Scy	struct in_addr a1, a2, a3, a4;
3782170268Sdarrenr	int flags, dlen, odst;
3783145522Sdarrenr	icmphdr_t *icmp;
3784145522Sdarrenr	u_short *csump;
378595418Sdarrenr	tcphdr_t *tcp;
378653642Sguido	nat_t *nat;
378753642Sguido	ip_t *oip;
3788145522Sdarrenr	void *dp;
378953642Sguido
3790255332Scy	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
3791255332Scy		NBUMPSIDED(fin->fin_out, ns_icmp_short);
379263523Sdarrenr		return NULL;
3793255332Scy	}
3794255332Scy
379567614Sdarrenr	/*
3796255332Scy	 * ipf_nat_icmperrorlookup() will return NULL for `defective' packets.
379767614Sdarrenr	 */
3798255332Scy	if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) {
3799255332Scy		NBUMPSIDED(fin->fin_out, ns_icmp_notfound);
380053642Sguido		return NULL;
3801255332Scy	}
380292685Sdarrenr
3803145522Sdarrenr	tcp = NULL;
3804145522Sdarrenr	csump = NULL;
380592685Sdarrenr	flags = 0;
3806130886Sdarrenr	sumd2 = 0;
380753642Sguido	*nflags = IPN_ICMPERR;
3808145522Sdarrenr	icmp = fin->fin_dp;
380953642Sguido	oip = (ip_t *)&icmp->icmp_ip;
3810145522Sdarrenr	dp = (((char *)oip) + (IP_HL(oip) << 2));
3811145522Sdarrenr	if (oip->ip_p == IPPROTO_TCP) {
3812145522Sdarrenr		tcp = (tcphdr_t *)dp;
3813145522Sdarrenr		csump = (u_short *)&tcp->th_sum;
381453642Sguido		flags = IPN_TCP;
3815145522Sdarrenr	} else if (oip->ip_p == IPPROTO_UDP) {
3816145522Sdarrenr		udphdr_t *udp;
3817145522Sdarrenr
3818145522Sdarrenr		udp = (udphdr_t *)dp;
3819145522Sdarrenr		tcp = (tcphdr_t *)dp;
3820145522Sdarrenr		csump = (u_short *)&udp->uh_sum;
382153642Sguido		flags = IPN_UDP;
3822145522Sdarrenr	} else if (oip->ip_p == IPPROTO_ICMP)
3823145522Sdarrenr		flags = IPN_ICMPQUERY;
3824145522Sdarrenr	dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip);
382595418Sdarrenr
382695418Sdarrenr	/*
382753642Sguido	 * Need to adjust ICMP header to include the real IP#'s and
382853642Sguido	 * port #'s.  Only apply a checksum change relative to the
3829255332Scy	 * IP address change as it will be modified again in ipf_nat_checkout
383053642Sguido	 * for both address and port.  Two checksum changes are
383153642Sguido	 * necessary for the two header address changes.  Be careful
383253642Sguido	 * to only modify the checksum once for the port # and twice
383353642Sguido	 * for the IP#.
383453642Sguido	 */
383560852Sdarrenr
383667614Sdarrenr	/*
383767614Sdarrenr	 * Step 1
383867614Sdarrenr	 * Fix the IP addresses in the offending IP packet. You also need
3839170268Sdarrenr	 * to adjust the IP header checksum of that offending IP packet.
384067614Sdarrenr	 *
3841145522Sdarrenr	 * Normally, you would expect that the ICMP checksum of the
3842130886Sdarrenr	 * ICMP error message needs to be adjusted as well for the
3843130886Sdarrenr	 * IP address change in oip.
3844145522Sdarrenr	 * However, this is a NOP, because the ICMP checksum is
3845130886Sdarrenr	 * calculated over the complete ICMP packet, which includes the
3846145522Sdarrenr	 * changed oip IP addresses and oip->ip_sum. However, these
3847130886Sdarrenr	 * two changes cancel each other out (if the delta for
3848145522Sdarrenr	 * the IP address is x, then the delta for ip_sum is minus x),
3849130886Sdarrenr	 * so no change in the icmp_cksum is necessary.
3850130886Sdarrenr	 *
3851170268Sdarrenr	 * Inbound ICMP
3852170268Sdarrenr	 * ------------
3853170268Sdarrenr	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
3854170268Sdarrenr	 * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b)
3855255332Scy	 * - OIP_SRC(c)=nat_newsrcip,          OIP_DST(b)=nat_newdstip
3856255332Scy	 *=> OIP_SRC(c)=nat_oldsrcip,          OIP_DST(b)=nat_olddstip
3857170268Sdarrenr	 *
3858170268Sdarrenr	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
3859170268Sdarrenr	 * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a)
3860255332Scy	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
3861255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3862170268Sdarrenr	 *
3863255332Scy	 * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
3864255332Scy	 * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d)
3865255332Scy	 * - OIP_SRC(c)=nat_newsrcip,          OIP_DST(d)=nat_newdstip
3866255332Scy	 *=> OIP_SRC(c)=nat_oldsrcip,          OIP_DST(d)=nat_olddstip
3867255332Scy	 *
3868255332Scy	 * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
3869255332Scy	 * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
3870255332Scy	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
3871255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3872255332Scy	 *
3873170268Sdarrenr	 * Outbound ICMP
3874170268Sdarrenr	 * -------------
3875170268Sdarrenr	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
3876170268Sdarrenr	 * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
3877255332Scy	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
3878255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3879170268Sdarrenr	 *
3880170268Sdarrenr	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
3881170268Sdarrenr	 * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c)
3882255332Scy	 * - OIP_SRC(a)=nat_newsrcip,          OIP_DST(c)=nat_newdstip
3883255332Scy	 *=> OIP_SRC(a)=nat_oldsrcip,          OIP_DST(c)=nat_olddstip
3884170268Sdarrenr	 *
3885255332Scy	 * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
3886255332Scy	 * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d)
3887255332Scy	 * - OIP_SRC(c)=nat_olddstip,          OIP_DST(d)=nat_oldsrcip
3888255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3889255332Scy	 *
3890255332Scy	 * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
3891255332Scy	 * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a)
3892255332Scy	 * - OIP_SRC(b)=nat_newsrcip,          OIP_DST(a)=nat_newdstip
3893255332Scy	 *=> OIP_SRC(a)=nat_oldsrcip,          OIP_DST(c)=nat_olddstip
3894130886Sdarrenr	 */
3895255332Scy
3896255332Scy	if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) ||
3897255332Scy	    ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) {
3898255332Scy		a1.s_addr = ntohl(nat->nat_osrcaddr);
3899255332Scy		a4.s_addr = ntohl(oip->ip_src.s_addr);
3900255332Scy		a3.s_addr = ntohl(nat->nat_odstaddr);
3901255332Scy		a2.s_addr = ntohl(oip->ip_dst.s_addr);
3902170268Sdarrenr		oip->ip_src.s_addr = htonl(a1.s_addr);
3903255332Scy		oip->ip_dst.s_addr = htonl(a3.s_addr);
3904255332Scy		odst = 1;
3905170268Sdarrenr	} else {
3906255332Scy		a1.s_addr = ntohl(nat->nat_ndstaddr);
3907170268Sdarrenr		a2.s_addr = ntohl(oip->ip_dst.s_addr);
3908255332Scy		a3.s_addr = ntohl(nat->nat_nsrcaddr);
3909255332Scy		a4.s_addr = ntohl(oip->ip_src.s_addr);
3910255332Scy		oip->ip_dst.s_addr = htonl(a3.s_addr);
3911255332Scy		oip->ip_src.s_addr = htonl(a1.s_addr);
3912255332Scy		odst = 0;
3913170268Sdarrenr	}
3914255332Scy	sum1 = 0;
3915255332Scy	sum2 = 0;
3916255332Scy	sumd = 0;
3917255332Scy	CALC_SUMD(a2.s_addr, a3.s_addr, sum1);
3918255332Scy	CALC_SUMD(a4.s_addr, a1.s_addr, sum2);
3919255332Scy	sumd = sum2 + sum1;
3920255332Scy	if (sumd != 0)
3921255332Scy		ipf_fix_datacksum(&oip->ip_sum, sumd);
3922130886Sdarrenr
3923170268Sdarrenr	sumd2 = sumd;
3924170268Sdarrenr	sum1 = 0;
3925170268Sdarrenr	sum2 = 0;
3926170268Sdarrenr
3927130886Sdarrenr	/*
3928170268Sdarrenr	 * Fix UDP pseudo header checksum to compensate for the
3929170268Sdarrenr	 * IP address change.
3930130886Sdarrenr	 */
3931130886Sdarrenr	if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) {
3932255332Scy		u_32_t sum3, sum4, sumt;
3933255332Scy
393464580Sdarrenr		/*
393567614Sdarrenr		 * Step 2 :
393667614Sdarrenr		 * For offending TCP/UDP IP packets, translate the ports as
393767614Sdarrenr		 * well, based on the NAT specification. Of course such
3938170268Sdarrenr		 * a change may be reflected in the ICMP checksum as well.
393967614Sdarrenr		 *
394067614Sdarrenr		 * Since the port fields are part of the TCP/UDP checksum
394167614Sdarrenr		 * of the offending IP packet, you need to adjust that checksum
3942255332Scy		 * as well... except that the change in the port numbers should
3943170268Sdarrenr		 * be offset by the checksum change.  However, the TCP/UDP
3944170268Sdarrenr		 * checksum will also need to change if there has been an
3945170268Sdarrenr		 * IP address change.
394667614Sdarrenr		 */
3947170268Sdarrenr		if (odst == 1) {
3948255332Scy			sum1 = ntohs(nat->nat_osport);
3949255332Scy			sum4 = ntohs(tcp->th_sport);
3950255332Scy			sum3 = ntohs(nat->nat_odport);
3951255332Scy			sum2 = ntohs(tcp->th_dport);
3952145522Sdarrenr
3953170268Sdarrenr			tcp->th_sport = htons(sum1);
3954255332Scy			tcp->th_dport = htons(sum3);
3955170268Sdarrenr		} else {
3956255332Scy			sum1 = ntohs(nat->nat_ndport);
3957145522Sdarrenr			sum2 = ntohs(tcp->th_dport);
3958255332Scy			sum3 = ntohs(nat->nat_nsport);
3959255332Scy			sum4 = ntohs(tcp->th_sport);
3960170268Sdarrenr
3961255332Scy			tcp->th_dport = htons(sum3);
3962255332Scy			tcp->th_sport = htons(sum1);
3963145522Sdarrenr		}
3964255332Scy		CALC_SUMD(sum4, sum1, sumt);
3965255332Scy		sumd += sumt;
3966255332Scy		CALC_SUMD(sum2, sum3, sumt);
3967255332Scy		sumd += sumt;
396867614Sdarrenr
3969170268Sdarrenr		if (sumd != 0 || sumd2 != 0) {
3970145522Sdarrenr			/*
3971170268Sdarrenr			 * At this point, sumd is the delta to apply to the
3972170268Sdarrenr			 * TCP/UDP header, given the changes in both the IP
3973170268Sdarrenr			 * address and the ports and sumd2 is the delta to
3974170268Sdarrenr			 * apply to the ICMP header, given the IP address
3975170268Sdarrenr			 * change delta that may need to be applied to the
3976170268Sdarrenr			 * TCP/UDP checksum instead.
3977145522Sdarrenr			 *
3978170268Sdarrenr			 * If we will both the IP and TCP/UDP checksums
3979170268Sdarrenr			 * then the ICMP checksum changes by the address
3980170268Sdarrenr			 * delta applied to the TCP/UDP checksum.  If we
3981170268Sdarrenr			 * do not change the TCP/UDP checksum them we
3982170268Sdarrenr			 * apply the delta in ports to the ICMP checksum.
3983145522Sdarrenr			 */
3984161356Sguido			if (oip->ip_p == IPPROTO_UDP) {
3985161356Sguido				if ((dlen >= 8) && (*csump != 0)) {
3986255332Scy					ipf_fix_datacksum(csump, sumd);
3987161356Sguido				} else {
3988255332Scy					CALC_SUMD(sum1, sum4, sumd2);
3989255332Scy					CALC_SUMD(sum3, sum2, sumt);
3990255332Scy					sumd2 += sumt;
3991161356Sguido				}
3992170268Sdarrenr			} else if (oip->ip_p == IPPROTO_TCP) {
3993145522Sdarrenr				if (dlen >= 18) {
3994255332Scy					ipf_fix_datacksum(csump, sumd);
3995145522Sdarrenr				} else {
3996255332Scy					CALC_SUMD(sum1, sum4, sumd2);
3997255332Scy					CALC_SUMD(sum3, sum2, sumt);
3998255332Scy					sumd2 += sumt;
399967614Sdarrenr				}
400053642Sguido			}
4001170268Sdarrenr			if (sumd2 != 0) {
4002170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
4003170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
4004170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
4005255332Scy				ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0);
4006130886Sdarrenr			}
400753642Sguido		}
4008145522Sdarrenr	} else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) {
4009145522Sdarrenr		icmphdr_t *orgicmp;
4010145522Sdarrenr
4011145522Sdarrenr		/*
4012145522Sdarrenr		 * XXX - what if this is bogus hl and we go off the end ?
4013255332Scy		 * In this case, ipf_nat_icmperrorlookup() will have
4014255332Scy		 * returned NULL.
4015145522Sdarrenr		 */
4016145522Sdarrenr		orgicmp = (icmphdr_t *)dp;
4017145522Sdarrenr
4018170268Sdarrenr		if (odst == 1) {
4019255332Scy			if (orgicmp->icmp_id != nat->nat_osport) {
4020145522Sdarrenr
4021145522Sdarrenr				/*
4022145522Sdarrenr				 * Fix ICMP checksum (of the offening ICMP
4023145522Sdarrenr				 * query packet) to compensate the change
4024145522Sdarrenr				 * in the ICMP id of the offending ICMP
4025145522Sdarrenr				 * packet.
4026145522Sdarrenr				 *
4027145522Sdarrenr				 * Since you modify orgicmp->icmp_id with
4028145522Sdarrenr				 * a delta (say x) and you compensate that
4029145522Sdarrenr				 * in origicmp->icmp_cksum with a delta
4030145522Sdarrenr				 * minus x, you don't have to adjust the
4031145522Sdarrenr				 * overall icmp->icmp_cksum
4032145522Sdarrenr				 */
4033145522Sdarrenr				sum1 = ntohs(orgicmp->icmp_id);
4034255332Scy				sum2 = ntohs(nat->nat_oicmpid);
4035145522Sdarrenr				CALC_SUMD(sum1, sum2, sumd);
4036255332Scy				orgicmp->icmp_id = nat->nat_oicmpid;
4037255332Scy				ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd);
4038145522Sdarrenr			}
4039145522Sdarrenr		} /* nat_dir == NAT_INBOUND is impossible for icmp queries */
404053642Sguido	}
404153642Sguido	return nat;
404253642Sguido}
404353642Sguido
404453642Sguido
404553642Sguido/*
4046255332Scy *       MAP-IN    MAP-OUT   RDR-IN   RDR-OUT
4047255332Scy * osrc    X       == src    == src      X
4048255332Scy * odst    X       == dst    == dst      X
4049255332Scy * nsrc  == dst      X         X      == dst
4050255332Scy * ndst  == src      X         X      == src
4051255332Scy * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND
4052255332Scy */
4053255332Scy/*
4054145522Sdarrenr * NB: these lookups don't lock access to the list, it assumed that it has
4055145522Sdarrenr * already been done!
405653642Sguido */
4057145522Sdarrenr/* ------------------------------------------------------------------------ */
4058255332Scy/* Function:    ipf_nat_inlookup                                            */
4059145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
4060145522Sdarrenr/*                       else pointer to matching NAT entry                 */
4061145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
4062145522Sdarrenr/*              flags(I)  - NAT flags for this packet                       */
4063145522Sdarrenr/*              p(I)      - protocol for this packet                        */
4064145522Sdarrenr/*              src(I)    - source IP address                               */
4065145522Sdarrenr/*              mapdst(I) - destination IP address                          */
4066145522Sdarrenr/*                                                                          */
4067145522Sdarrenr/* Lookup a nat entry based on the mapped destination ip address/port and   */
4068145522Sdarrenr/* real source address/port.  We use this lookup when receiving a packet,   */
4069145522Sdarrenr/* we're looking for a table entry, based on the destination address.       */
4070145522Sdarrenr/*                                                                          */
4071145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
4072145522Sdarrenr/*                                                                          */
4073255332Scy/* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
4074145522Sdarrenr/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
4075145522Sdarrenr/*                                                                          */
4076145522Sdarrenr/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
4077145522Sdarrenr/*            the packet is of said protocol                                */
4078145522Sdarrenr/* ------------------------------------------------------------------------ */
4079255332Scynat_t *
4080255332Scyipf_nat_inlookup(fin, flags, p, src, mapdst)
4081255332Scy	fr_info_t *fin;
4082255332Scy	u_int flags, p;
4083255332Scy	struct in_addr src , mapdst;
408453642Sguido{
4085255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
4086255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
4087145522Sdarrenr	u_short sport, dport;
4088145522Sdarrenr	grehdr_t *gre;
408992685Sdarrenr	ipnat_t *ipn;
4090145522Sdarrenr	u_int sflags;
4091145522Sdarrenr	nat_t *nat;
4092145522Sdarrenr	int nflags;
4093145522Sdarrenr	u_32_t dst;
409492685Sdarrenr	void *ifp;
4095255332Scy	u_int hv, rhv;
409653642Sguido
4097161356Sguido	ifp = fin->fin_ifp;
4098145522Sdarrenr	gre = NULL;
409967614Sdarrenr	dst = mapdst.s_addr;
4100145522Sdarrenr	sflags = flags & NAT_TCPUDPICMP;
4101145522Sdarrenr
4102145522Sdarrenr	switch (p)
4103145522Sdarrenr	{
4104145522Sdarrenr	case IPPROTO_TCP :
4105145522Sdarrenr	case IPPROTO_UDP :
410692685Sdarrenr		sport = htons(fin->fin_data[0]);
410792685Sdarrenr		dport = htons(fin->fin_data[1]);
4108145522Sdarrenr		break;
4109145522Sdarrenr	case IPPROTO_ICMP :
4110255332Scy		if (flags & IPN_ICMPERR) {
4111145522Sdarrenr			sport = fin->fin_data[1];
4112255332Scy			dport = 0;
4113255332Scy		} else {
4114145522Sdarrenr			dport = fin->fin_data[1];
4115255332Scy			sport = 0;
4116255332Scy		}
4117145522Sdarrenr		break;
4118145522Sdarrenr	default :
4119255332Scy		sport = 0;
4120255332Scy		dport = 0;
4121145522Sdarrenr		break;
412292685Sdarrenr	}
412353642Sguido
4124145522Sdarrenr
4125145522Sdarrenr	if ((flags & SI_WILDP) != 0)
4126145522Sdarrenr		goto find_in_wild_ports;
4127145522Sdarrenr
4128255332Scy	rhv = NAT_HASH_FN(dst, dport, 0xffffffff);
4129255332Scy	rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff);
4130255332Scy	hv = rhv % softn->ipf_nat_table_sz;
4131255332Scy	nat = softn->ipf_nat_table[1][hv];
4132255332Scy	/* TRACE dst, dport, src, sport, hv, nat */
4133255332Scy
413453642Sguido	for (; nat; nat = nat->nat_hnext[1]) {
4135161356Sguido		if (nat->nat_ifps[0] != NULL) {
4136161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
4137161356Sguido				continue;
4138255332Scy		}
4139161356Sguido
4140255332Scy		if (nat->nat_pr[0] != p)
4141255332Scy			continue;
4142145522Sdarrenr
4143255332Scy		switch (nat->nat_dir)
4144255332Scy		{
4145255332Scy		case NAT_INBOUND :
4146255332Scy		case NAT_DIVERTIN :
4147255332Scy			if (nat->nat_v[0] != 4)
4148255332Scy				continue;
4149255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4150255332Scy			    nat->nat_odstaddr != dst)
4151255332Scy				continue;
4152255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4153255332Scy				if (nat->nat_osport != sport)
4154145522Sdarrenr					continue;
4155255332Scy				if (nat->nat_odport != dport)
4156255332Scy					continue;
4157255332Scy
4158255332Scy			} else if (p == IPPROTO_ICMP) {
4159255332Scy				if (nat->nat_osport != dport) {
4160255332Scy					continue;
4161145522Sdarrenr				}
4162255332Scy			}
4163255332Scy			break;
4164255332Scy		case NAT_DIVERTOUT :
4165255332Scy			if (nat->nat_dlocal)
4166255332Scy				continue;
4167255332Scy		case NAT_OUTBOUND :
4168255332Scy			if (nat->nat_v[1] != 4)
4169255332Scy				continue;
4170255332Scy			if (nat->nat_dlocal)
4171255332Scy				continue;
4172255332Scy			if (nat->nat_dlocal)
4173255332Scy				continue;
4174255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4175255332Scy			    nat->nat_nsrcaddr != dst)
4176255332Scy				continue;
4177255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4178255332Scy				if (nat->nat_ndport != sport)
417992685Sdarrenr					continue;
4180255332Scy				if (nat->nat_nsport != dport)
418192685Sdarrenr					continue;
4182255332Scy
4183255332Scy			} else if (p == IPPROTO_ICMP) {
4184255332Scy				if (nat->nat_osport != dport) {
4185255332Scy					continue;
4186255332Scy				}
418792685Sdarrenr			}
4188255332Scy			break;
4189255332Scy		}
419092685Sdarrenr
4191255332Scy
4192255332Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
419392685Sdarrenr			ipn = nat->nat_ptr;
419492685Sdarrenr			if ((ipn != NULL) && (nat->nat_aps != NULL))
4195255332Scy				if (ipf_proxy_match(fin, nat) != 0)
419692685Sdarrenr					continue;
419792685Sdarrenr		}
4198255332Scy		if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
4199255332Scy			nat->nat_ifps[0] = ifp;
4200255332Scy			nat->nat_mtu[0] = GETIFMTU_4(ifp);
4201255332Scy		}
4202255332Scy		return nat;
420353642Sguido	}
4204145522Sdarrenr
4205145522Sdarrenr	/*
4206145522Sdarrenr	 * So if we didn't find it but there are wildcard members in the hash
4207145522Sdarrenr	 * table, go back and look for them.  We do this search and update here
4208145522Sdarrenr	 * because it is modifying the NAT table and we want to do this only
4209145522Sdarrenr	 * for the first packet that matches.  The exception, of course, is
4210145522Sdarrenr	 * for "dummy" (FI_IGNORE) lookups.
4211145522Sdarrenr	 */
4212145522Sdarrenrfind_in_wild_ports:
4213255332Scy	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
4214255332Scy		NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0);
421567614Sdarrenr		return NULL;
4216255332Scy	}
4217255332Scy	if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
4218255332Scy		NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0);
4219145522Sdarrenr		return NULL;
4220255332Scy	}
4221145522Sdarrenr
4222255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
4223145522Sdarrenr
422480482Sdarrenr	hv = NAT_HASH_FN(dst, 0, 0xffffffff);
4225255332Scy	hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz);
4226255332Scy	WRITE_ENTER(&softc->ipf_nat);
4227145522Sdarrenr
4228255332Scy	nat = softn->ipf_nat_table[1][hv];
4229255332Scy	/* TRACE dst, src, hv, nat */
423067614Sdarrenr	for (; nat; nat = nat->nat_hnext[1]) {
4231161356Sguido		if (nat->nat_ifps[0] != NULL) {
4232161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
4233161356Sguido				continue;
4234255332Scy		}
4235145522Sdarrenr
4236255332Scy		if (nat->nat_pr[0] != fin->fin_p)
423767614Sdarrenr			continue;
4238145522Sdarrenr
4239255332Scy		switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))
4240255332Scy		{
4241255332Scy		case NAT_INBOUND :
4242255332Scy			if (nat->nat_v[0] != 4)
4243255332Scy				continue;
4244255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4245255332Scy			    nat->nat_odstaddr != dst)
4246255332Scy				continue;
4247255332Scy			break;
4248255332Scy		case NAT_OUTBOUND :
4249255332Scy			if (nat->nat_v[1] != 4)
4250255332Scy				continue;
4251255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4252255332Scy			    nat->nat_nsrcaddr != dst)
4253255332Scy				continue;
4254255332Scy			break;
4255255332Scy		}
4256255332Scy
4257145522Sdarrenr		nflags = nat->nat_flags;
4258145522Sdarrenr		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
4259145522Sdarrenr			continue;
4260145522Sdarrenr
4261255332Scy		if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags,
4262255332Scy				   NAT_INBOUND) == 1) {
4263145522Sdarrenr			if ((fin->fin_flx & FI_IGNORE) != 0)
4264145522Sdarrenr				break;
4265145522Sdarrenr			if ((nflags & SI_CLONE) != 0) {
4266255332Scy				nat = ipf_nat_clone(fin, nat);
4267145522Sdarrenr				if (nat == NULL)
4268145522Sdarrenr					break;
4269145522Sdarrenr			} else {
4270255332Scy				MUTEX_ENTER(&softn->ipf_nat_new);
4271255332Scy				softn->ipf_nat_stats.ns_wilds--;
4272255332Scy				MUTEX_EXIT(&softn->ipf_nat_new);
4273145522Sdarrenr			}
4274255332Scy
4275255332Scy			if (nat->nat_dir == NAT_INBOUND) {
4276255332Scy				if (nat->nat_osport == 0) {
4277255332Scy					nat->nat_osport = sport;
4278255332Scy					nat->nat_nsport = sport;
4279255332Scy				}
4280255332Scy				if (nat->nat_odport == 0) {
4281255332Scy					nat->nat_odport = dport;
4282255332Scy					nat->nat_ndport = dport;
4283255332Scy				}
4284255332Scy			} else if (nat->nat_dir == NAT_OUTBOUND) {
4285255332Scy				if (nat->nat_osport == 0) {
4286255332Scy					nat->nat_osport = dport;
4287255332Scy					nat->nat_nsport = dport;
4288255332Scy				}
4289255332Scy				if (nat->nat_odport == 0) {
4290255332Scy					nat->nat_odport = sport;
4291255332Scy					nat->nat_ndport = sport;
4292255332Scy				}
4293255332Scy			}
4294255332Scy			if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
4295255332Scy				nat->nat_ifps[0] = ifp;
4296255332Scy				nat->nat_mtu[0] = GETIFMTU_4(ifp);
4297255332Scy			}
4298145522Sdarrenr			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
4299255332Scy			ipf_nat_tabmove(softn, nat);
430067614Sdarrenr			break;
430167614Sdarrenr		}
430267614Sdarrenr	}
4303145522Sdarrenr
4304255332Scy	MUTEX_DOWNGRADE(&softc->ipf_nat);
4305145522Sdarrenr
4306255332Scy	if (nat == NULL) {
4307255332Scy		NBUMPSIDE(0, ns_lookup_miss);
4308255332Scy	}
430967614Sdarrenr	return nat;
431053642Sguido}
431153642Sguido
431253642Sguido
4313145522Sdarrenr/* ------------------------------------------------------------------------ */
4314255332Scy/* Function:    ipf_nat_tabmove                                             */
4315145522Sdarrenr/* Returns:     Nil                                                         */
4316255332Scy/* Parameters:  softn(I) - pointer to NAT context structure                 */
4317255332Scy/*              nat(I)   - pointer to NAT structure                         */
4318145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
4319145522Sdarrenr/*                                                                          */
4320145522Sdarrenr/* This function is only called for TCP/UDP NAT table entries where the     */
4321145522Sdarrenr/* original was placed in the table without hashing on the ports and we now */
4322145522Sdarrenr/* want to include hashing on port numbers.                                 */
4323145522Sdarrenr/* ------------------------------------------------------------------------ */
4324255332Scystatic void
4325255332Scyipf_nat_tabmove(softn, nat)
4326255332Scy	ipf_nat_softc_t *softn;
4327255332Scy	nat_t *nat;
432867614Sdarrenr{
4329255332Scy	u_int hv0, hv1, rhv0, rhv1;
4330255332Scy	natstat_t *nsp;
433167614Sdarrenr	nat_t **natp;
433267614Sdarrenr
4333145522Sdarrenr	if (nat->nat_flags & SI_CLONE)
4334145522Sdarrenr		return;
433572006Sdarrenr
4336255332Scy	nsp = &softn->ipf_nat_stats;
433767614Sdarrenr	/*
433867614Sdarrenr	 * Remove the NAT entry from the old location
433967614Sdarrenr	 */
434067614Sdarrenr	if (nat->nat_hnext[0])
434167614Sdarrenr		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
434267614Sdarrenr	*nat->nat_phnext[0] = nat->nat_hnext[0];
4343255332Scy	nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] %
4344255332Scy				     softn->ipf_nat_table_sz]--;
434567614Sdarrenr
434667614Sdarrenr	if (nat->nat_hnext[1])
434767853Sdarrenr		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
434867614Sdarrenr	*nat->nat_phnext[1] = nat->nat_hnext[1];
4349255332Scy	nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] %
4350255332Scy				     softn->ipf_nat_table_sz]--;
435167614Sdarrenr
435267853Sdarrenr	/*
435367853Sdarrenr	 * Add into the NAT table in the new position
435467853Sdarrenr	 */
4355255332Scy	rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff);
4356255332Scy	rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport,
4357255332Scy			   0xffffffff);
4358255332Scy	rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff);
4359255332Scy	rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport,
4360255332Scy			   0xffffffff);
4361255332Scy
4362255332Scy	hv0 = rhv0 % softn->ipf_nat_table_sz;
4363255332Scy	hv1 = rhv1 % softn->ipf_nat_table_sz;
4364255332Scy
4365255332Scy	if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) {
4366255332Scy		u_int swap;
4367255332Scy
4368255332Scy		swap = hv0;
4369255332Scy		hv0 = hv1;
4370255332Scy		hv1 = swap;
4371255332Scy	}
4372255332Scy
4373255332Scy	/* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */
4374255332Scy	/* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */
4375255332Scy
4376255332Scy	nat->nat_hv[0] = rhv0;
4377255332Scy	natp = &softn->ipf_nat_table[0][hv0];
437867614Sdarrenr	if (*natp)
437967614Sdarrenr		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
438067614Sdarrenr	nat->nat_phnext[0] = natp;
438167614Sdarrenr	nat->nat_hnext[0] = *natp;
438267614Sdarrenr	*natp = nat;
4383255332Scy	nsp->ns_side[0].ns_bucketlen[hv0]++;
438467614Sdarrenr
4385255332Scy	nat->nat_hv[1] = rhv1;
4386255332Scy	natp = &softn->ipf_nat_table[1][hv1];
438767614Sdarrenr	if (*natp)
438867614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
438967614Sdarrenr	nat->nat_phnext[1] = natp;
439067614Sdarrenr	nat->nat_hnext[1] = *natp;
439167614Sdarrenr	*natp = nat;
4392255332Scy	nsp->ns_side[1].ns_bucketlen[hv1]++;
439367614Sdarrenr}
439467614Sdarrenr
439567614Sdarrenr
4396145522Sdarrenr/* ------------------------------------------------------------------------ */
4397255332Scy/* Function:    ipf_nat_outlookup                                           */
4398145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
4399145522Sdarrenr/*                       else pointer to matching NAT entry                 */
4400145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4401145522Sdarrenr/*              flags(I) - NAT flags for this packet                        */
4402145522Sdarrenr/*              p(I)     - protocol for this packet                         */
4403145522Sdarrenr/*              src(I)   - source IP address                                */
4404145522Sdarrenr/*              dst(I)   - destination IP address                           */
4405255332Scy/*              rw(I)    - 1 == write lock on  held, 0 == read lock.        */
4406145522Sdarrenr/*                                                                          */
4407145522Sdarrenr/* Lookup a nat entry based on the source 'real' ip address/port and        */
4408145522Sdarrenr/* destination address/port.  We use this lookup when sending a packet out, */
4409145522Sdarrenr/* we're looking for a table entry, based on the source address.            */
4410145522Sdarrenr/*                                                                          */
4411145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
4412145522Sdarrenr/*                                                                          */
4413255332Scy/* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
4414145522Sdarrenr/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
4415145522Sdarrenr/*                                                                          */
4416145522Sdarrenr/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
4417145522Sdarrenr/*            the packet is of said protocol                                */
4418145522Sdarrenr/* ------------------------------------------------------------------------ */
4419255332Scynat_t *
4420255332Scyipf_nat_outlookup(fin, flags, p, src, dst)
4421255332Scy	fr_info_t *fin;
4422255332Scy	u_int flags, p;
4423255332Scy	struct in_addr src , dst;
442453642Sguido{
4425255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
4426255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
4427145522Sdarrenr	u_short sport, dport;
4428145522Sdarrenr	u_int sflags;
442992685Sdarrenr	ipnat_t *ipn;
4430145522Sdarrenr	nat_t *nat;
443192685Sdarrenr	void *ifp;
443253642Sguido	u_int hv;
443353642Sguido
443492685Sdarrenr	ifp = fin->fin_ifp;
4435145522Sdarrenr	sflags = flags & IPN_TCPUDPICMP;
4436145522Sdarrenr	sport = 0;
4437145522Sdarrenr	dport = 0;
4438145522Sdarrenr
4439145522Sdarrenr	switch (p)
4440145522Sdarrenr	{
4441145522Sdarrenr	case IPPROTO_TCP :
4442145522Sdarrenr	case IPPROTO_UDP :
4443145522Sdarrenr		sport = htons(fin->fin_data[0]);
4444145522Sdarrenr		dport = htons(fin->fin_data[1]);
4445145522Sdarrenr		break;
4446145522Sdarrenr	case IPPROTO_ICMP :
4447145522Sdarrenr		if (flags & IPN_ICMPERR)
4448145522Sdarrenr			sport = fin->fin_data[1];
4449145522Sdarrenr		else
4450145522Sdarrenr			dport = fin->fin_data[1];
4451145522Sdarrenr		break;
4452145522Sdarrenr	default :
4453145522Sdarrenr		break;
445492685Sdarrenr	}
445553642Sguido
4456145522Sdarrenr	if ((flags & SI_WILDP) != 0)
4457145522Sdarrenr		goto find_out_wild_ports;
4458145522Sdarrenr
4459255332Scy	hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff);
4460255332Scy	hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz);
4461255332Scy	nat = softn->ipf_nat_table[0][hv];
4462255332Scy
4463255332Scy	/* TRACE src, sport, dst, dport, hv, nat */
4464255332Scy
446553642Sguido	for (; nat; nat = nat->nat_hnext[0]) {
4466161356Sguido		if (nat->nat_ifps[1] != NULL) {
4467161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
4468161356Sguido				continue;
4469255332Scy		}
4470161356Sguido
4471255332Scy		if (nat->nat_pr[1] != p)
4472255332Scy			continue;
447353642Sguido
4474255332Scy		switch (nat->nat_dir)
4475255332Scy		{
4476255332Scy		case NAT_INBOUND :
4477255332Scy		case NAT_DIVERTIN :
4478255332Scy			if (nat->nat_v[1] != 4)
4479255332Scy				continue;
4480255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4481255332Scy			    nat->nat_nsrcaddr != dst.s_addr)
4482255332Scy				continue;
4483255332Scy
4484255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4485255332Scy				if (nat->nat_ndport != sport)
4486145522Sdarrenr					continue;
4487255332Scy				if (nat->nat_nsport != dport)
448892685Sdarrenr					continue;
4489255332Scy
4490255332Scy			} else if (p == IPPROTO_ICMP) {
4491255332Scy				if (nat->nat_osport != dport) {
449292685Sdarrenr					continue;
4493255332Scy				}
449492685Sdarrenr			}
4495255332Scy			break;
4496255332Scy		case NAT_OUTBOUND :
4497255332Scy		case NAT_DIVERTOUT :
4498255332Scy			if (nat->nat_v[0] != 4)
4499255332Scy				continue;
4500255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4501255332Scy			    nat->nat_odstaddr != dst.s_addr)
4502255332Scy				continue;
450392685Sdarrenr
4504255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4505255332Scy				if (nat->nat_odport != dport)
450692685Sdarrenr					continue;
4507255332Scy				if (nat->nat_osport != sport)
4508255332Scy					continue;
4509255332Scy
4510255332Scy			} else if (p == IPPROTO_ICMP) {
4511255332Scy				if (nat->nat_osport != dport) {
4512255332Scy					continue;
4513255332Scy				}
4514255332Scy			}
4515255332Scy			break;
451692685Sdarrenr		}
4517255332Scy
4518255332Scy		ipn = nat->nat_ptr;
4519255332Scy		if ((ipn != NULL) && (nat->nat_aps != NULL))
4520255332Scy			if (ipf_proxy_match(fin, nat) != 0)
4521255332Scy				continue;
4522255332Scy
4523255332Scy		if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
4524255332Scy			nat->nat_ifps[1] = ifp;
4525255332Scy			nat->nat_mtu[1] = GETIFMTU_4(ifp);
4526255332Scy		}
4527255332Scy		return nat;
452853642Sguido	}
4529145522Sdarrenr
4530145522Sdarrenr	/*
4531145522Sdarrenr	 * So if we didn't find it but there are wildcard members in the hash
4532145522Sdarrenr	 * table, go back and look for them.  We do this search and update here
4533145522Sdarrenr	 * because it is modifying the NAT table and we want to do this only
4534145522Sdarrenr	 * for the first packet that matches.  The exception, of course, is
4535145522Sdarrenr	 * for "dummy" (FI_IGNORE) lookups.
4536145522Sdarrenr	 */
4537145522Sdarrenrfind_out_wild_ports:
4538255332Scy	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
4539255332Scy		NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1);
454067614Sdarrenr		return NULL;
4541255332Scy	}
4542255332Scy	if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
4543255332Scy		NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1);
4544145522Sdarrenr		return NULL;
4545255332Scy	}
454692685Sdarrenr
4547255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
4548145522Sdarrenr
4549255332Scy	hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff);
4550255332Scy	hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz);
4551145522Sdarrenr
4552255332Scy	WRITE_ENTER(&softc->ipf_nat);
4553145522Sdarrenr
4554255332Scy	nat = softn->ipf_nat_table[0][hv];
455567614Sdarrenr	for (; nat; nat = nat->nat_hnext[0]) {
4556161356Sguido		if (nat->nat_ifps[1] != NULL) {
4557161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
4558161356Sguido				continue;
4559255332Scy		}
4560145522Sdarrenr
4561255332Scy		if (nat->nat_pr[1] != fin->fin_p)
456267614Sdarrenr			continue;
4563145522Sdarrenr
4564255332Scy		switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))
4565255332Scy		{
4566255332Scy		case NAT_INBOUND :
4567255332Scy			if (nat->nat_v[1] != 4)
4568255332Scy				continue;
4569255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4570255332Scy			    nat->nat_nsrcaddr != dst.s_addr)
4571255332Scy				continue;
4572255332Scy			break;
4573255332Scy		case NAT_OUTBOUND :
4574255332Scy			if (nat->nat_v[0] != 4)
4575255332Scy				continue;
4576255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4577255332Scy			    nat->nat_odstaddr != dst.s_addr)
4578255332Scy				continue;
4579255332Scy			break;
4580255332Scy		}
4581255332Scy
4582255332Scy		if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP)))
4583145522Sdarrenr			continue;
4584145522Sdarrenr
4585255332Scy		if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags,
4586255332Scy				   NAT_OUTBOUND) == 1) {
4587145522Sdarrenr			if ((fin->fin_flx & FI_IGNORE) != 0)
4588145522Sdarrenr				break;
4589255332Scy			if ((nat->nat_flags & SI_CLONE) != 0) {
4590255332Scy				nat = ipf_nat_clone(fin, nat);
4591145522Sdarrenr				if (nat == NULL)
4592145522Sdarrenr					break;
4593145522Sdarrenr			} else {
4594255332Scy				MUTEX_ENTER(&softn->ipf_nat_new);
4595255332Scy				softn->ipf_nat_stats.ns_wilds--;
4596255332Scy				MUTEX_EXIT(&softn->ipf_nat_new);
4597145522Sdarrenr			}
4598255332Scy
4599255332Scy			if (nat->nat_dir == NAT_OUTBOUND) {
4600255332Scy				if (nat->nat_osport == 0) {
4601255332Scy					nat->nat_osport = sport;
4602255332Scy					nat->nat_nsport = sport;
4603255332Scy				}
4604255332Scy				if (nat->nat_odport == 0) {
4605255332Scy					nat->nat_odport = dport;
4606255332Scy					nat->nat_ndport = dport;
4607255332Scy				}
4608255332Scy			} else if (nat->nat_dir == NAT_INBOUND) {
4609255332Scy				if (nat->nat_osport == 0) {
4610255332Scy					nat->nat_osport = dport;
4611255332Scy					nat->nat_nsport = dport;
4612255332Scy				}
4613255332Scy				if (nat->nat_odport == 0) {
4614255332Scy					nat->nat_odport = sport;
4615255332Scy					nat->nat_ndport = sport;
4616255332Scy				}
4617255332Scy			}
4618255332Scy			if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
4619255332Scy				nat->nat_ifps[1] = ifp;
4620255332Scy				nat->nat_mtu[1] = GETIFMTU_4(ifp);
4621255332Scy			}
4622145522Sdarrenr			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
4623255332Scy			ipf_nat_tabmove(softn, nat);
462467614Sdarrenr			break;
462567614Sdarrenr		}
462667614Sdarrenr	}
4627145522Sdarrenr
4628255332Scy	MUTEX_DOWNGRADE(&softc->ipf_nat);
4629145522Sdarrenr
4630255332Scy	if (nat == NULL) {
4631255332Scy		NBUMPSIDE(1, ns_lookup_miss);
4632255332Scy	}
463367614Sdarrenr	return nat;
463453642Sguido}
463553642Sguido
463653642Sguido
4637145522Sdarrenr/* ------------------------------------------------------------------------ */
4638255332Scy/* Function:    ipf_nat_lookupredir                                         */
4639145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
4640145522Sdarrenr/*                       else pointer to matching NAT entry                 */
4641145522Sdarrenr/* Parameters:  np(I) - pointer to description of packet to find NAT table  */
4642145522Sdarrenr/*                      entry for.                                          */
4643145522Sdarrenr/*                                                                          */
4644145522Sdarrenr/* Lookup the NAT tables to search for a matching redirect                  */
4645161356Sguido/* The contents of natlookup_t should imitate those found in a packet that  */
4646161356Sguido/* would be translated - ie a packet coming in for RDR or going out for MAP.*/
4647161356Sguido/* We can do the lookup in one of two ways, imitating an inbound or         */
4648161356Sguido/* outbound  packet.  By default we assume outbound, unless IPN_IN is set.  */
4649161356Sguido/* For IN, the fields are set as follows:                                   */
4650161356Sguido/*     nl_real* = source information                                        */
4651161356Sguido/*     nl_out* = destination information (translated)                       */
4652161356Sguido/* For an out packet, the fields are set like this:                         */
4653161356Sguido/*     nl_in* = source information (untranslated)                           */
4654161356Sguido/*     nl_out* = destination information (translated)                       */
4655145522Sdarrenr/* ------------------------------------------------------------------------ */
4656255332Scynat_t *
4657255332Scyipf_nat_lookupredir(np)
4658255332Scy	natlookup_t *np;
465953642Sguido{
4660145522Sdarrenr	fr_info_t fi;
466153642Sguido	nat_t *nat;
466253642Sguido
466392685Sdarrenr	bzero((char *)&fi, sizeof(fi));
4664145522Sdarrenr	if (np->nl_flags & IPN_IN) {
4665145522Sdarrenr		fi.fin_data[0] = ntohs(np->nl_realport);
4666145522Sdarrenr		fi.fin_data[1] = ntohs(np->nl_outport);
4667145522Sdarrenr	} else {
4668145522Sdarrenr		fi.fin_data[0] = ntohs(np->nl_inport);
4669145522Sdarrenr		fi.fin_data[1] = ntohs(np->nl_outport);
4670145522Sdarrenr	}
4671145522Sdarrenr	if (np->nl_flags & IPN_TCP)
4672145522Sdarrenr		fi.fin_p = IPPROTO_TCP;
4673145522Sdarrenr	else if (np->nl_flags & IPN_UDP)
4674145522Sdarrenr		fi.fin_p = IPPROTO_UDP;
4675145522Sdarrenr	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
4676145522Sdarrenr		fi.fin_p = IPPROTO_ICMP;
467792685Sdarrenr
467853642Sguido	/*
4679145522Sdarrenr	 * We can do two sorts of lookups:
4680145522Sdarrenr	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
4681145522Sdarrenr	 * - default: we have the `in' and `out' address, look for `real'.
468253642Sguido	 */
4683145522Sdarrenr	if (np->nl_flags & IPN_IN) {
4684255332Scy		if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p,
4685255332Scy					    np->nl_realip, np->nl_outip))) {
4686255332Scy			np->nl_inip = nat->nat_odstip;
4687255332Scy			np->nl_inport = nat->nat_odport;
4688145522Sdarrenr		}
4689145522Sdarrenr	} else {
4690145522Sdarrenr		/*
4691145522Sdarrenr		 * If nl_inip is non null, this is a lookup based on the real
4692145522Sdarrenr		 * ip address. Else, we use the fake.
4693145522Sdarrenr		 */
4694255332Scy		if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p,
4695145522Sdarrenr					 np->nl_inip, np->nl_outip))) {
4696145522Sdarrenr
4697145522Sdarrenr			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
4698145522Sdarrenr				fr_info_t fin;
4699145522Sdarrenr				bzero((char *)&fin, sizeof(fin));
4700255332Scy				fin.fin_p = nat->nat_pr[0];
4701255332Scy				fin.fin_data[0] = ntohs(nat->nat_ndport);
4702255332Scy				fin.fin_data[1] = ntohs(nat->nat_nsport);
4703255332Scy				if (ipf_nat_inlookup(&fin, np->nl_flags,
4704255332Scy						     fin.fin_p, nat->nat_ndstip,
4705255332Scy						     nat->nat_nsrcip) != NULL) {
4706145522Sdarrenr					np->nl_flags &= ~IPN_FINDFORWARD;
4707145522Sdarrenr				}
4708145522Sdarrenr			}
4709145522Sdarrenr
4710255332Scy			np->nl_realip = nat->nat_ndstip;
4711255332Scy			np->nl_realport = nat->nat_ndport;
4712145522Sdarrenr		}
4713145522Sdarrenr 	}
4714145522Sdarrenr
471553642Sguido	return nat;
471653642Sguido}
471753642Sguido
471853642Sguido
4719145522Sdarrenr/* ------------------------------------------------------------------------ */
4720255332Scy/* Function:    ipf_nat_match                                               */
4721145522Sdarrenr/* Returns:     int - 0 == no match, 1 == match                             */
4722145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4723145522Sdarrenr/*              np(I)    - pointer to NAT rule                              */
4724145522Sdarrenr/*                                                                          */
4725145522Sdarrenr/* Pull the matching of a packet against a NAT rule out of that complex     */
4726255332Scy/* loop inside ipf_nat_checkin() and lay it out properly in its own function. */
4727145522Sdarrenr/* ------------------------------------------------------------------------ */
4728255332Scystatic int
4729255332Scyipf_nat_match(fin, np)
4730255332Scy	fr_info_t *fin;
4731255332Scy	ipnat_t *np;
473260852Sdarrenr{
4733255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
473460852Sdarrenr	frtuc_t *ft;
4735255332Scy	int match;
473660852Sdarrenr
4737255332Scy	match = 0;
4738255332Scy	switch (np->in_osrcatype)
4739255332Scy	{
4740255332Scy	case FRI_NORMAL :
4741255332Scy		match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr);
4742255332Scy		break;
4743255332Scy	case FRI_LOOKUP :
4744255332Scy		match = (*np->in_osrcfunc)(softc, np->in_osrcptr,
4745255332Scy					   4, &fin->fin_saddr, fin->fin_plen);
4746255332Scy		break;
4747255332Scy	}
4748255332Scy	match ^= ((np->in_flags & IPN_NOTSRC) != 0);
4749255332Scy	if (match)
475060852Sdarrenr		return 0;
475160852Sdarrenr
4752255332Scy	match = 0;
4753255332Scy	switch (np->in_odstatype)
4754255332Scy	{
4755255332Scy	case FRI_NORMAL :
4756255332Scy		match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr);
4757255332Scy		break;
4758255332Scy	case FRI_LOOKUP :
4759255332Scy		match = (*np->in_odstfunc)(softc, np->in_odstptr,
4760255332Scy					   4, &fin->fin_daddr, fin->fin_plen);
4761255332Scy		break;
4762255332Scy	}
4763255332Scy
4764255332Scy	match ^= ((np->in_flags & IPN_NOTDST) != 0);
4765255332Scy	if (match)
476660852Sdarrenr		return 0;
4767145522Sdarrenr
476860852Sdarrenr	ft = &np->in_tuc;
4769145522Sdarrenr	if (!(fin->fin_flx & FI_TCPUDP) ||
4770145522Sdarrenr	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
477160852Sdarrenr		if (ft->ftu_scmp || ft->ftu_dcmp)
477260852Sdarrenr			return 0;
477360852Sdarrenr		return 1;
477460852Sdarrenr	}
477560852Sdarrenr
4776255332Scy	return ipf_tcpudpchk(&fin->fin_fi, ft);
477760852Sdarrenr}
477860852Sdarrenr
477960852Sdarrenr
4780145522Sdarrenr/* ------------------------------------------------------------------------ */
4781255332Scy/* Function:    ipf_nat_update                                              */
4782145522Sdarrenr/* Returns:     Nil                                                         */
4783255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
4784255332Scy/*              nat(I) - pointer to NAT structure                           */
4785145522Sdarrenr/*                                                                          */
4786145522Sdarrenr/* Updates the lifetime of a NAT table entry for non-TCP packets.  Must be  */
4787255332Scy/* called with fin_rev updated - i.e. after calling ipf_nat_proto().        */
4788255332Scy/*                                                                          */
4789255332Scy/* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to     */
4790255332Scy/* already be set.                                                          */
4791145522Sdarrenr/* ------------------------------------------------------------------------ */
4792255332Scyvoid
4793255332Scyipf_nat_update(fin, nat)
4794255332Scy	fr_info_t *fin;
4795255332Scy	nat_t *nat;
479653642Sguido{
4797255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
4798255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
4799145522Sdarrenr	ipftq_t *ifq, *ifq2;
4800145522Sdarrenr	ipftqent_t *tqe;
4801255332Scy	ipnat_t *np = nat->nat_ptr;
4802145522Sdarrenr
4803145522Sdarrenr	tqe = &nat->nat_tqe;
4804145522Sdarrenr	ifq = tqe->tqe_ifq;
4805145522Sdarrenr
4806145522Sdarrenr	/*
4807145522Sdarrenr	 * We allow over-riding of NAT timeouts from NAT rules, even for
4808145522Sdarrenr	 * TCP, however, if it is TCP and there is no rule timeout set,
4809145522Sdarrenr	 * then do not update the timeout here.
4810145522Sdarrenr	 */
4811255332Scy	if (np != NULL) {
4812255332Scy		np->in_bytes[fin->fin_rev] += fin->fin_plen;
4813145522Sdarrenr		ifq2 = np->in_tqehead[fin->fin_rev];
4814255332Scy	} else {
4815145522Sdarrenr		ifq2 = NULL;
4816255332Scy	}
4817145522Sdarrenr
4818255332Scy	if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) {
4819255332Scy		(void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq,
4820255332Scy				   0, 2);
4821145522Sdarrenr	} else {
4822145522Sdarrenr		if (ifq2 == NULL) {
4823255332Scy			if (nat->nat_pr[0] == IPPROTO_UDP)
4824255332Scy				ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq :
4825255332Scy						      &softn->ipf_nat_udptq;
4826255332Scy			else if (nat->nat_pr[0] == IPPROTO_ICMP ||
4827255332Scy				 nat->nat_pr[0] == IPPROTO_ICMPV6)
4828255332Scy				ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq:
4829255332Scy						      &softn->ipf_nat_icmptq;
4830145522Sdarrenr			else
4831255332Scy				ifq2 = &softn->ipf_nat_iptq;
4832145522Sdarrenr		}
4833145522Sdarrenr
4834255332Scy		ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2);
4835145522Sdarrenr	}
4836145522Sdarrenr}
4837145522Sdarrenr
4838145522Sdarrenr
4839145522Sdarrenr/* ------------------------------------------------------------------------ */
4840255332Scy/* Function:    ipf_nat_checkout                                            */
4841145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
4842145522Sdarrenr/*                     0 == no packet translation occurred,                 */
4843145522Sdarrenr/*                     1 == packet was successfully translated.             */
4844145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4845145522Sdarrenr/*              passp(I) - pointer to filtering result flags                */
4846145522Sdarrenr/*                                                                          */
4847145522Sdarrenr/* Check to see if an outcoming packet should be changed.  ICMP packets are */
4848145522Sdarrenr/* first checked to see if they match an existing entry (if an error),      */
4849145522Sdarrenr/* otherwise a search of the current NAT table is made.  If neither results */
4850145522Sdarrenr/* in a match then a search for a matching NAT rule is made.  Create a new  */
4851145522Sdarrenr/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
4852145522Sdarrenr/* packet header(s) as required.                                            */
4853145522Sdarrenr/* ------------------------------------------------------------------------ */
4854255332Scyint
4855255332Scyipf_nat_checkout(fin, passp)
4856255332Scy	fr_info_t *fin;
4857255332Scy	u_32_t *passp;
4858145522Sdarrenr{
4859255332Scy	ipnat_t *np = NULL, *npnext;
4860145522Sdarrenr	struct ifnet *ifp, *sifp;
4861255332Scy	ipf_main_softc_t *softc;
4862255332Scy	ipf_nat_softc_t *softn;
4863145522Sdarrenr	icmphdr_t *icmp = NULL;
486453642Sguido	tcphdr_t *tcp = NULL;
4865145522Sdarrenr	int rval, natfailed;
4866145522Sdarrenr	u_int nflags = 0;
4867145522Sdarrenr	u_32_t ipa, iph;
4868145522Sdarrenr	int natadd = 1;
486953642Sguido	frentry_t *fr;
487053642Sguido	nat_t *nat;
487153642Sguido
4872255332Scy	if (fin->fin_v == 6) {
4873255332Scy#ifdef USE_INET6
4874255332Scy		return ipf_nat6_checkout(fin, passp);
4875255332Scy#else
487653642Sguido		return 0;
4877255332Scy#endif
4878255332Scy	}
487953642Sguido
4880255332Scy	softc = fin->fin_main_soft;
4881255332Scy	softn = softc->ipf_nat_soft;
4882255332Scy
4883255332Scy	if (softn->ipf_nat_lock != 0)
4884255332Scy		return 0;
4885255332Scy	if (softn->ipf_nat_stats.ns_rules == 0 &&
4886255332Scy	    softn->ipf_nat_instances == NULL)
4887255332Scy		return 0;
4888255332Scy
4889145522Sdarrenr	natfailed = 0;
4890145522Sdarrenr	fr = fin->fin_fr;
4891145522Sdarrenr	sifp = fin->fin_ifp;
4892170268Sdarrenr	if (fr != NULL) {
4893255332Scy		ifp = fr->fr_tifs[fin->fin_rev].fd_ptr;
4894170268Sdarrenr		if ((ifp != NULL) && (ifp != (void *)-1))
4895170268Sdarrenr			fin->fin_ifp = ifp;
4896170268Sdarrenr	}
489792685Sdarrenr	ifp = fin->fin_ifp;
489853642Sguido
4899145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
4900145522Sdarrenr		switch (fin->fin_p)
4901145522Sdarrenr		{
4902145522Sdarrenr		case IPPROTO_TCP :
490353642Sguido			nflags = IPN_TCP;
4904145522Sdarrenr			break;
4905145522Sdarrenr		case IPPROTO_UDP :
490653642Sguido			nflags = IPN_UDP;
4907145522Sdarrenr			break;
4908145522Sdarrenr		case IPPROTO_ICMP :
4909145522Sdarrenr			icmp = fin->fin_dp;
4910145522Sdarrenr
4911145522Sdarrenr			/*
4912145522Sdarrenr			 * This is an incoming packet, so the destination is
4913145522Sdarrenr			 * the icmp_id and the source port equals 0
4914145522Sdarrenr			 */
4915255332Scy			if ((fin->fin_flx & FI_ICMPQUERY) != 0)
4916145522Sdarrenr				nflags = IPN_ICMPQUERY;
4917145522Sdarrenr			break;
4918145522Sdarrenr		default :
4919145522Sdarrenr			break;
492053642Sguido		}
4921255332Scy
4922145522Sdarrenr		if ((nflags & IPN_TCPUDP))
4923145522Sdarrenr			tcp = fin->fin_dp;
492453642Sguido	}
492553642Sguido
492692685Sdarrenr	ipa = fin->fin_saddr;
492753642Sguido
4928255332Scy	READ_ENTER(&softc->ipf_nat);
492960852Sdarrenr
4930255332Scy	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
4931255332Scy	    (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND)))
4932145522Sdarrenr		/*EMPTY*/;
4933255332Scy	else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
493453642Sguido		natadd = 0;
4935255332Scy	else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH,
4936255332Scy				      (u_int)fin->fin_p, fin->fin_src,
4937255332Scy				      fin->fin_dst))) {
493853642Sguido		nflags = nat->nat_flags;
4939255332Scy	} else if (fin->fin_off == 0) {
4940255332Scy		u_32_t hv, msk, nmsk = 0;
494192685Sdarrenr
494253642Sguido		/*
494353642Sguido		 * If there is no current entry in the nat table for this IP#,
494453642Sguido		 * create one for it (if there is a matching rule).
494553642Sguido		 */
494653642Sguidomaskloop:
4947255332Scy		msk = softn->ipf_nat_map_active_masks[nmsk];
4948255332Scy		iph = ipa & msk;
4949255332Scy		hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz);
4950255332Scyretry_roundrobin:
4951255332Scy		for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) {
4952255332Scy			npnext = np->in_mnext;
4953161356Sguido			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
495460852Sdarrenr				continue;
4955255332Scy			if (np->in_v[0] != 4)
495660852Sdarrenr				continue;
4957255332Scy			if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p))
4958145522Sdarrenr				continue;
4959255332Scy			if ((np->in_flags & IPN_RF) &&
4960255332Scy			    !(np->in_flags & nflags))
4961145522Sdarrenr				continue;
496260852Sdarrenr			if (np->in_flags & IPN_FILTER) {
4963255332Scy				switch (ipf_nat_match(fin, np))
4964255332Scy				{
4965255332Scy				case 0 :
496660852Sdarrenr					continue;
4967255332Scy				case -1 :
4968255332Scy					rval = -1;
4969255332Scy					goto outmatchfail;
4970255332Scy				case 1 :
4971255332Scy				default :
4972255332Scy					break;
4973255332Scy				}
4974255332Scy			} else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr)
497560852Sdarrenr				continue;
4976145522Sdarrenr
4977145522Sdarrenr			if ((fr != NULL) &&
4978255332Scy			    !ipf_matchtag(&np->in_tag, &fr->fr_nattag))
497992685Sdarrenr				continue;
4980145522Sdarrenr
4981255332Scy			if (np->in_plabel != -1) {
4982145522Sdarrenr				if (((np->in_flags & IPN_FILTER) == 0) &&
4983255332Scy				    (np->in_odport != fin->fin_data[1]))
4984145522Sdarrenr					continue;
4985255332Scy				if (ipf_proxy_ok(fin, tcp, np) == 0)
4986145522Sdarrenr					continue;
4987145522Sdarrenr			}
4988145522Sdarrenr
4989255332Scy			if (np->in_flags & IPN_NO) {
499092685Sdarrenr				np->in_hits++;
499192685Sdarrenr				break;
4992145522Sdarrenr			}
4993255332Scy			MUTEX_ENTER(&softn->ipf_nat_new);
4994255332Scy			/*
4995255332Scy			 * If we've matched a round-robin rule but it has
4996255332Scy			 * moved in the list since we got it, start over as
4997255332Scy			 * this is now no longer correct.
4998255332Scy			 */
4999255332Scy			if (npnext != np->in_mnext) {
5000255332Scy				if ((np->in_flags & IPN_ROUNDR) != 0) {
5001255332Scy					MUTEX_EXIT(&softn->ipf_nat_new);
5002255332Scy					goto retry_roundrobin;
5003255332Scy				}
5004255332Scy				npnext = np->in_mnext;
5005145522Sdarrenr			}
5006255332Scy
5007255332Scy			nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND);
5008255332Scy			MUTEX_EXIT(&softn->ipf_nat_new);
5009255332Scy			if (nat != NULL) {
5010255332Scy				natfailed = 0;
5011255332Scy				break;
5012255332Scy			}
5013255332Scy			natfailed = -1;
501453642Sguido		}
5015255332Scy		if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) {
5016255332Scy			nmsk++;
5017255332Scy			goto maskloop;
5018255332Scy		}
501953642Sguido	}
502053642Sguido
5021145522Sdarrenr	if (nat != NULL) {
5022255332Scy		rval = ipf_nat_out(fin, nat, natadd, nflags);
5023145522Sdarrenr		if (rval == 1) {
5024145522Sdarrenr			MUTEX_ENTER(&nat->nat_lock);
5025255332Scy			ipf_nat_update(fin, nat);
5026255332Scy			nat->nat_bytes[1] += fin->fin_plen;
5027255332Scy			nat->nat_pkts[1]++;
5028255332Scy			fin->fin_pktnum = nat->nat_pkts[1];
5029145522Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
5030145522Sdarrenr		}
5031145522Sdarrenr	} else
5032145522Sdarrenr		rval = natfailed;
5033255332Scyoutmatchfail:
5034255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
5035145522Sdarrenr
5036255332Scy	switch (rval)
5037255332Scy	{
5038255332Scy	case -1 :
5039255332Scy		if (passp != NULL) {
5040255332Scy			DT1(frb_natv4out, fr_info_t *, fin);
5041255332Scy			NBUMPSIDED(1, ns_drop);
5042145522Sdarrenr			*passp = FR_BLOCK;
5043255332Scy			fin->fin_reason = FRB_NATV4;
5044255332Scy		}
5045145522Sdarrenr		fin->fin_flx |= FI_BADNAT;
5046255332Scy		NBUMPSIDED(1, ns_badnat);
5047255332Scy		break;
5048255332Scy	case 0 :
5049255332Scy		NBUMPSIDE(1, ns_ignored);
5050255332Scy		break;
5051255332Scy	case 1 :
5052255332Scy		NBUMPSIDE(1, ns_translated);
5053255332Scy		break;
5054145522Sdarrenr	}
5055145522Sdarrenr	fin->fin_ifp = sifp;
5056145522Sdarrenr	return rval;
5057145522Sdarrenr}
5058145522Sdarrenr
5059145522Sdarrenr/* ------------------------------------------------------------------------ */
5060255332Scy/* Function:    ipf_nat_out                                                 */
5061145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
5062145522Sdarrenr/*                     1 == packet was successfully translated.             */
5063145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
5064145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
5065145522Sdarrenr/*              natadd(I) - flag indicating if it is safe to add frag cache */
5066145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
5067145522Sdarrenr/*                                                                          */
5068145522Sdarrenr/* Translate a packet coming "out" on an interface.                         */
5069145522Sdarrenr/* ------------------------------------------------------------------------ */
5070255332Scyint
5071255332Scyipf_nat_out(fin, nat, natadd, nflags)
5072255332Scy	fr_info_t *fin;
5073255332Scy	nat_t *nat;
5074255332Scy	int natadd;
5075255332Scy	u_32_t nflags;
5076145522Sdarrenr{
5077255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
5078255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5079145522Sdarrenr	icmphdr_t *icmp;
5080145522Sdarrenr	tcphdr_t *tcp;
5081145522Sdarrenr	ipnat_t *np;
5082255332Scy	int skip;
5083145522Sdarrenr	int i;
5084145522Sdarrenr
5085145522Sdarrenr	tcp = NULL;
5086145522Sdarrenr	icmp = NULL;
5087145522Sdarrenr	np = nat->nat_ptr;
5088145522Sdarrenr
5089145522Sdarrenr	if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL))
5090255332Scy		(void) ipf_frag_natnew(softc, fin, 0, nat);
5091145522Sdarrenr
509272006Sdarrenr	/*
5093145522Sdarrenr	 * Fix up checksums, not by recalculating them, but
5094145522Sdarrenr	 * simply computing adjustments.
5095145522Sdarrenr	 * This is only done for STREAMS based IP implementations where the
5096145522Sdarrenr	 * checksum has already been calculated by IP.  In all other cases,
5097145522Sdarrenr	 * IPFilter is called before the checksum needs calculating so there
5098145522Sdarrenr	 * is no call to modify whatever is in the header now.
509972006Sdarrenr	 */
5100255332Scy	if (nflags == IPN_ICMPERR) {
5101255332Scy		u_32_t s1, s2, sumd, msumd;
510263523Sdarrenr
5103255332Scy		s1 = LONG_SUM(ntohl(fin->fin_saddr));
5104255332Scy		if (nat->nat_dir == NAT_OUTBOUND) {
5105255332Scy			s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
5106255332Scy		} else {
5107255332Scy			s2 = LONG_SUM(ntohl(nat->nat_odstaddr));
510863523Sdarrenr		}
5109255332Scy		CALC_SUMD(s1, s2, sumd);
5110255332Scy		msumd = sumd;
5111255332Scy
5112255332Scy		s1 = LONG_SUM(ntohl(fin->fin_daddr));
5113255332Scy		if (nat->nat_dir == NAT_OUTBOUND) {
5114255332Scy			s2 = LONG_SUM(ntohl(nat->nat_ndstaddr));
5115255332Scy		} else {
5116255332Scy			s2 = LONG_SUM(ntohl(nat->nat_osrcaddr));
5117255332Scy		}
5118255332Scy		CALC_SUMD(s1, s2, sumd);
5119255332Scy		msumd += sumd;
5120255332Scy
5121255332Scy		ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0);
5122255332Scy	}
5123153876Sguido#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5124153876Sguido    defined(linux) || defined(BRIDGE_IPF)
5125255332Scy	else {
5126255332Scy		/*
5127255332Scy		 * Strictly speaking, this isn't necessary on BSD
5128255332Scy		 * kernels because they do checksum calculation after
5129255332Scy		 * this code has run BUT if ipfilter is being used
5130255332Scy		 * to do NAT as a bridge, that code doesn't exist.
5131255332Scy		 */
5132255332Scy		switch (nat->nat_dir)
5133255332Scy		{
5134255332Scy		case NAT_OUTBOUND :
5135255332Scy			ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART,
5136255332Scy					 &fin->fin_ip->ip_sum,
5137255332Scy					 nat->nat_ipsumd, 0);
5138255332Scy			break;
5139255332Scy
5140255332Scy		case NAT_INBOUND :
5141255332Scy			ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART,
5142255332Scy					&fin->fin_ip->ip_sum,
5143255332Scy					nat->nat_ipsumd, 0);
5144255332Scy			break;
5145255332Scy
5146255332Scy		default :
5147255332Scy			break;
514863523Sdarrenr		}
5149255332Scy	}
515053642Sguido#endif
5151255332Scy
5152255332Scy	/*
5153255332Scy	 * Address assignment is after the checksum modification because
5154255332Scy	 * we are using the address in the packet for determining the
5155255332Scy	 * correct checksum offset (the ICMP error could be coming from
5156255332Scy	 * anyone...)
5157255332Scy	 */
5158255332Scy	switch (nat->nat_dir)
5159255332Scy	{
5160255332Scy	case NAT_OUTBOUND :
5161255332Scy		fin->fin_ip->ip_src = nat->nat_nsrcip;
5162255332Scy		fin->fin_saddr = nat->nat_nsrcaddr;
5163255332Scy		fin->fin_ip->ip_dst = nat->nat_ndstip;
5164255332Scy		fin->fin_daddr = nat->nat_ndstaddr;
5165255332Scy		break;
5166255332Scy
5167255332Scy	case NAT_INBOUND :
5168255332Scy		fin->fin_ip->ip_src = nat->nat_odstip;
5169255332Scy		fin->fin_saddr = nat->nat_ndstaddr;
5170255332Scy		fin->fin_ip->ip_dst = nat->nat_osrcip;
5171255332Scy		fin->fin_daddr = nat->nat_nsrcaddr;
5172255332Scy		break;
5173255332Scy
5174255332Scy	case NAT_DIVERTIN :
5175255332Scy	    {
5176255332Scy		mb_t *m;
5177255332Scy
5178255332Scy		skip = ipf_nat_decap(fin, nat);
5179255332Scy		if (skip <= 0) {
5180255332Scy			NBUMPSIDED(1, ns_decap_fail);
5181255332Scy			return -1;
5182255332Scy		}
5183255332Scy
5184255332Scy		m = fin->fin_m;
5185255332Scy
5186255332Scy#if defined(MENTAT) && defined(_KERNEL)
5187255332Scy		m->b_rptr += skip;
5188255332Scy#else
5189255332Scy		m->m_data += skip;
5190255332Scy		m->m_len -= skip;
5191255332Scy
5192255332Scy# ifdef M_PKTHDR
5193255332Scy		if (m->m_flags & M_PKTHDR)
5194255332Scy			m->m_pkthdr.len -= skip;
5195255332Scy# endif
5196255332Scy#endif
5197255332Scy
5198255332Scy		MUTEX_ENTER(&nat->nat_lock);
5199255332Scy		ipf_nat_update(fin, nat);
5200255332Scy		MUTEX_EXIT(&nat->nat_lock);
5201255332Scy		fin->fin_flx |= FI_NATED;
5202255332Scy		if (np != NULL && np->in_tag.ipt_num[0] != 0)
5203255332Scy			fin->fin_nattag = &np->in_tag;
5204255332Scy		return 1;
5205255332Scy		/* NOTREACHED */
5206255332Scy	    }
5207255332Scy
5208255332Scy	case NAT_DIVERTOUT :
5209255332Scy	    {
5210255332Scy		u_32_t s1, s2, sumd;
5211255332Scy		udphdr_t *uh;
5212255332Scy		ip_t *ip;
5213255332Scy		mb_t *m;
5214255332Scy
5215255332Scy		m = M_DUP(np->in_divmp);
5216255332Scy		if (m == NULL) {
5217255332Scy			NBUMPSIDED(1, ns_divert_dup);
5218255332Scy			return -1;
5219255332Scy		}
5220255332Scy
5221255332Scy		ip = MTOD(m, ip_t *);
5222255332Scy		ip->ip_id = htons(ipf_nextipid(fin));
5223255332Scy		s2 = ntohs(ip->ip_id);
5224255332Scy
5225255332Scy		s1 = ip->ip_len;
5226255332Scy		ip->ip_len = ntohs(ip->ip_len);
5227255332Scy		ip->ip_len += fin->fin_plen;
5228255332Scy		ip->ip_len = htons(ip->ip_len);
5229255332Scy		s2 += ntohs(ip->ip_len);
5230255332Scy		CALC_SUMD(s1, s2, sumd);
5231255332Scy
5232255332Scy		uh = (udphdr_t *)(ip + 1);
5233255332Scy		uh->uh_ulen += fin->fin_plen;
5234255332Scy		uh->uh_ulen = htons(uh->uh_ulen);
5235255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5236255332Scy    defined(linux) || defined(BRIDGE_IPF)
5237255332Scy		ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0);
5238255332Scy#endif
5239255332Scy
5240255332Scy		PREP_MB_T(fin, m);
5241255332Scy
5242255332Scy		fin->fin_src = ip->ip_src;
5243255332Scy		fin->fin_dst = ip->ip_dst;
5244255332Scy		fin->fin_ip = ip;
5245255332Scy		fin->fin_plen += sizeof(ip_t) + 8;	/* UDP + IPv4 hdr */
5246255332Scy		fin->fin_dlen += sizeof(ip_t) + 8;	/* UDP + IPv4 hdr */
5247255332Scy
5248255332Scy		nflags &= ~IPN_TCPUDPICMP;
5249255332Scy
5250255332Scy		break;
5251255332Scy	    }
5252255332Scy
5253255332Scy	default :
5254255332Scy		break;
5255145522Sdarrenr	}
525653642Sguido
5257145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
5258255332Scy		u_short *csump;
5259255332Scy
5260255332Scy		if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) {
5261145522Sdarrenr			tcp = fin->fin_dp;
526253642Sguido
5263255332Scy			switch (nat->nat_dir)
5264255332Scy			{
5265255332Scy			case NAT_OUTBOUND :
5266255332Scy				tcp->th_sport = nat->nat_nsport;
5267255332Scy				fin->fin_data[0] = ntohs(nat->nat_nsport);
5268255332Scy				tcp->th_dport = nat->nat_ndport;
5269255332Scy				fin->fin_data[1] = ntohs(nat->nat_ndport);
5270255332Scy				break;
5271255332Scy
5272255332Scy			case NAT_INBOUND :
5273255332Scy				tcp->th_sport = nat->nat_odport;
5274255332Scy				fin->fin_data[0] = ntohs(nat->nat_odport);
5275255332Scy				tcp->th_dport = nat->nat_osport;
5276255332Scy				fin->fin_data[1] = ntohs(nat->nat_osport);
5277255332Scy				break;
5278255332Scy			}
5279145522Sdarrenr		}
528053642Sguido
5281255332Scy		if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) {
5282145522Sdarrenr			icmp = fin->fin_dp;
5283255332Scy			icmp->icmp_id = nat->nat_nicmpid;
5284145522Sdarrenr		}
5285110916Sdarrenr
5286255332Scy		csump = ipf_nat_proto(fin, nat, nflags);
5287255332Scy
5288255332Scy		/*
5289255332Scy		 * The above comments do not hold for layer 4 (or higher)
5290255332Scy		 * checksums...
5291255332Scy		 */
5292255332Scy		if (csump != NULL) {
5293255332Scy			if (nat->nat_dir == NAT_OUTBOUND)
5294255332Scy				ipf_fix_outcksum(fin->fin_cksum, csump,
5295255332Scy						 nat->nat_sumd[0],
5296255332Scy						 nat->nat_sumd[1] +
5297255332Scy						 fin->fin_dlen);
5298255332Scy			else
5299255332Scy				ipf_fix_incksum(fin->fin_cksum, csump,
5300255332Scy						nat->nat_sumd[0],
5301255332Scy						nat->nat_sumd[1] +
5302255332Scy						fin->fin_dlen);
5303255332Scy		}
5304145522Sdarrenr	}
5305110916Sdarrenr
5306255332Scy	ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
5307145522Sdarrenr	/* ------------------------------------------------------------- */
5308255332Scy	/* A few quick notes:                                            */
5309255332Scy	/*      Following are test conditions prior to calling the       */
5310255332Scy	/*      ipf_proxy_check routine.                                 */
5311255332Scy	/*                                                               */
5312255332Scy	/*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
5313255332Scy	/*      with a redirect rule, we attempt to match the packet's   */
5314255332Scy	/*      source port against in_dport, otherwise we'd compare the */
5315255332Scy	/*      packet's destination.                                    */
5316145522Sdarrenr	/* ------------------------------------------------------------- */
5317145522Sdarrenr	if ((np != NULL) && (np->in_apr != NULL)) {
5318255332Scy		i = ipf_proxy_check(fin, nat);
5319255332Scy		if (i == 0) {
532060852Sdarrenr			i = 1;
5321255332Scy		} else if (i == -1) {
5322255332Scy			NBUMPSIDED(1, ns_ipf_proxy_fail);
5323255332Scy		}
5324255332Scy	} else {
5325145522Sdarrenr		i = 1;
5326255332Scy	}
5327145522Sdarrenr	fin->fin_flx |= FI_NATED;
5328145522Sdarrenr	return i;
532953642Sguido}
533053642Sguido
533153642Sguido
5332145522Sdarrenr/* ------------------------------------------------------------------------ */
5333255332Scy/* Function:    ipf_nat_checkin                                             */
5334145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
5335145522Sdarrenr/*                     0 == no packet translation occurred,                 */
5336145522Sdarrenr/*                     1 == packet was successfully translated.             */
5337145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
5338145522Sdarrenr/*              passp(I) - pointer to filtering result flags                */
5339145522Sdarrenr/*                                                                          */
5340145522Sdarrenr/* Check to see if an incoming packet should be changed.  ICMP packets are  */
5341145522Sdarrenr/* first checked to see if they match an existing entry (if an error),      */
5342145522Sdarrenr/* otherwise a search of the current NAT table is made.  If neither results */
5343145522Sdarrenr/* in a match then a search for a matching NAT rule is made.  Create a new  */
5344145522Sdarrenr/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
5345145522Sdarrenr/* packet header(s) as required.                                            */
5346145522Sdarrenr/* ------------------------------------------------------------------------ */
5347255332Scyint
5348255332Scyipf_nat_checkin(fin, passp)
5349255332Scy	fr_info_t *fin;
5350255332Scy	u_32_t *passp;
535153642Sguido{
5352255332Scy	ipf_main_softc_t *softc;
5353255332Scy	ipf_nat_softc_t *softn;
5354145522Sdarrenr	u_int nflags, natadd;
5355255332Scy	ipnat_t *np, *npnext;
5356145522Sdarrenr	int rval, natfailed;
5357145522Sdarrenr	struct ifnet *ifp;
5358145522Sdarrenr	struct in_addr in;
5359145522Sdarrenr	icmphdr_t *icmp;
5360145522Sdarrenr	tcphdr_t *tcp;
5361145522Sdarrenr	u_short dport;
536253642Sguido	nat_t *nat;
536353642Sguido	u_32_t iph;
536453642Sguido
5365255332Scy	softc = fin->fin_main_soft;
5366255332Scy	softn = softc->ipf_nat_soft;
5367255332Scy
5368255332Scy	if (softn->ipf_nat_lock != 0)
536953642Sguido		return 0;
5370255332Scy	if (softn->ipf_nat_stats.ns_rules == 0 &&
5371255332Scy	    softn->ipf_nat_instances == NULL)
5372255332Scy		return 0;
537353642Sguido
5374145522Sdarrenr	tcp = NULL;
5375145522Sdarrenr	icmp = NULL;
5376145522Sdarrenr	dport = 0;
5377145522Sdarrenr	natadd = 1;
5378145522Sdarrenr	nflags = 0;
5379145522Sdarrenr	natfailed = 0;
5380145522Sdarrenr	ifp = fin->fin_ifp;
5381145522Sdarrenr
5382145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
5383145522Sdarrenr		switch (fin->fin_p)
5384145522Sdarrenr		{
5385145522Sdarrenr		case IPPROTO_TCP :
538653642Sguido			nflags = IPN_TCP;
5387145522Sdarrenr			break;
5388145522Sdarrenr		case IPPROTO_UDP :
538953642Sguido			nflags = IPN_UDP;
5390145522Sdarrenr			break;
5391145522Sdarrenr		case IPPROTO_ICMP :
5392145522Sdarrenr			icmp = fin->fin_dp;
5393145522Sdarrenr
5394145522Sdarrenr			/*
5395145522Sdarrenr			 * This is an incoming packet, so the destination is
5396145522Sdarrenr			 * the icmp_id and the source port equals 0
5397145522Sdarrenr			 */
5398255332Scy			if ((fin->fin_flx & FI_ICMPQUERY) != 0) {
5399145522Sdarrenr				nflags = IPN_ICMPQUERY;
5400255332Scy				dport = icmp->icmp_id;
5401145522Sdarrenr			} break;
5402145522Sdarrenr		default :
5403145522Sdarrenr			break;
5404145522Sdarrenr		}
5405255332Scy
540653642Sguido		if ((nflags & IPN_TCPUDP)) {
5407145522Sdarrenr			tcp = fin->fin_dp;
5408255332Scy			dport = fin->fin_data[1];
540953642Sguido		}
541053642Sguido	}
541153642Sguido
541292685Sdarrenr	in = fin->fin_dst;
541353642Sguido
5414255332Scy	READ_ENTER(&softc->ipf_nat);
541553642Sguido
5416255332Scy	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
5417255332Scy	    (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND)))
5418145522Sdarrenr		/*EMPTY*/;
5419255332Scy	else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
542053642Sguido		natadd = 0;
5421255332Scy	else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH,
5422255332Scy					 (u_int)fin->fin_p,
5423255332Scy					 fin->fin_src, in))) {
542453642Sguido		nflags = nat->nat_flags;
5425255332Scy	} else if (fin->fin_off == 0) {
5426255332Scy		u_32_t hv, msk, rmsk = 0;
5427145522Sdarrenr
542853642Sguido		/*
542953642Sguido		 * If there is no current entry in the nat table for this IP#,
543053642Sguido		 * create one for it (if there is a matching rule).
543153642Sguido		 */
543253642Sguidomaskloop:
5433255332Scy		msk = softn->ipf_nat_rdr_active_masks[rmsk];
5434255332Scy		iph = in.s_addr & msk;
5435255332Scy		hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz);
5436255332Scyretry_roundrobin:
5437255332Scy		/* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */
5438255332Scy		for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) {
5439255332Scy			npnext = np->in_rnext;
5440145522Sdarrenr			if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
544160852Sdarrenr				continue;
5442255332Scy			if (np->in_v[0] != 4)
5443138947Sdarrenr				continue;
5444255332Scy			if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p))
5445145522Sdarrenr				continue;
5446145522Sdarrenr			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
5447145522Sdarrenr				continue;
544860852Sdarrenr			if (np->in_flags & IPN_FILTER) {
5449255332Scy				switch (ipf_nat_match(fin, np))
5450255332Scy				{
5451255332Scy				case 0 :
545260852Sdarrenr					continue;
5453255332Scy				case -1 :
5454255332Scy					rval = -1;
5455255332Scy					goto inmatchfail;
5456255332Scy				case 1 :
5457255332Scy				default :
5458255332Scy					break;
5459255332Scy				}
5460145522Sdarrenr			} else {
5461255332Scy				if ((in.s_addr & np->in_odstmsk) !=
5462255332Scy				    np->in_odstaddr)
5463145522Sdarrenr					continue;
5464255332Scy				if (np->in_odport &&
5465255332Scy				    ((np->in_dtop < dport) ||
5466255332Scy				     (dport < np->in_odport)))
5467145522Sdarrenr					continue;
5468145522Sdarrenr			}
5469145522Sdarrenr
5470255332Scy			if (np->in_plabel != -1) {
5471255332Scy				if (!ipf_proxy_ok(fin, tcp, np)) {
5472145522Sdarrenr					continue;
547353642Sguido				}
5474145522Sdarrenr			}
5475145522Sdarrenr
5476255332Scy			if (np->in_flags & IPN_NO) {
5477145522Sdarrenr				np->in_hits++;
5478145522Sdarrenr				break;
5479255332Scy			}
548060852Sdarrenr
5481255332Scy			MUTEX_ENTER(&softn->ipf_nat_new);
5482255332Scy			/*
5483255332Scy			 * If we've matched a round-robin rule but it has
5484255332Scy			 * moved in the list since we got it, start over as
5485255332Scy			 * this is now no longer correct.
5486255332Scy			 */
5487255332Scy			if (npnext != np->in_rnext) {
5488255332Scy				if ((np->in_flags & IPN_ROUNDR) != 0) {
5489255332Scy					MUTEX_EXIT(&softn->ipf_nat_new);
5490255332Scy					goto retry_roundrobin;
5491255332Scy				}
5492255332Scy				npnext = np->in_rnext;
5493145522Sdarrenr			}
5494255332Scy
5495255332Scy			nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND);
5496255332Scy			MUTEX_EXIT(&softn->ipf_nat_new);
5497255332Scy			if (nat != NULL) {
5498255332Scy				natfailed = 0;
5499255332Scy				break;
5500145522Sdarrenr			}
5501255332Scy			natfailed = -1;
550253642Sguido		}
5503255332Scy		if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) {
5504255332Scy			rmsk++;
5505255332Scy			goto maskloop;
5506255332Scy		}
550753642Sguido	}
5508255332Scy
5509145522Sdarrenr	if (nat != NULL) {
5510255332Scy		rval = ipf_nat_in(fin, nat, natadd, nflags);
5511145522Sdarrenr		if (rval == 1) {
5512145522Sdarrenr			MUTEX_ENTER(&nat->nat_lock);
5513255332Scy			ipf_nat_update(fin, nat);
5514255332Scy			nat->nat_bytes[0] += fin->fin_plen;
5515255332Scy			nat->nat_pkts[0]++;
5516255332Scy			fin->fin_pktnum = nat->nat_pkts[0];
5517145522Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
5518145522Sdarrenr		}
5519145522Sdarrenr	} else
5520145522Sdarrenr		rval = natfailed;
5521255332Scyinmatchfail:
5522255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
552372006Sdarrenr
5524255332Scy	switch (rval)
5525255332Scy	{
5526255332Scy	case -1 :
5527255332Scy		if (passp != NULL) {
5528255332Scy			DT1(frb_natv4in, fr_info_t *, fin);
5529255332Scy			NBUMPSIDED(0, ns_drop);
5530145522Sdarrenr			*passp = FR_BLOCK;
5531255332Scy			fin->fin_reason = FRB_NATV4;
5532255332Scy		}
5533145522Sdarrenr		fin->fin_flx |= FI_BADNAT;
5534255332Scy		NBUMPSIDED(0, ns_badnat);
5535255332Scy		break;
5536255332Scy	case 0 :
5537255332Scy		NBUMPSIDE(0, ns_ignored);
5538255332Scy		break;
5539255332Scy	case 1 :
5540255332Scy		NBUMPSIDE(0, ns_translated);
5541255332Scy		break;
5542145522Sdarrenr	}
5543145522Sdarrenr	return rval;
5544145522Sdarrenr}
5545145522Sdarrenr
5546145522Sdarrenr
5547145522Sdarrenr/* ------------------------------------------------------------------------ */
5548255332Scy/* Function:    ipf_nat_in                                                  */
5549145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
5550145522Sdarrenr/*                     1 == packet was successfully translated.             */
5551145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
5552145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
5553145522Sdarrenr/*              natadd(I) - flag indicating if it is safe to add frag cache */
5554145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
5555255332Scy/* Locks Held:  ipf_nat(READ)                                               */
5556145522Sdarrenr/*                                                                          */
5557145522Sdarrenr/* Translate a packet coming "in" on an interface.                          */
5558145522Sdarrenr/* ------------------------------------------------------------------------ */
5559255332Scyint
5560255332Scyipf_nat_in(fin, nat, natadd, nflags)
5561255332Scy	fr_info_t *fin;
5562255332Scy	nat_t *nat;
5563255332Scy	int natadd;
5564255332Scy	u_32_t nflags;
5565145522Sdarrenr{
5566255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
5567255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5568255332Scy	u_32_t sumd, ipsumd, sum1, sum2;
5569145522Sdarrenr	icmphdr_t *icmp;
5570145522Sdarrenr	tcphdr_t *tcp;
5571145522Sdarrenr	ipnat_t *np;
5572255332Scy	int skip;
5573145522Sdarrenr	int i;
5574145522Sdarrenr
5575145522Sdarrenr	tcp = NULL;
5576145522Sdarrenr	np = nat->nat_ptr;
5577145522Sdarrenr	fin->fin_fr = nat->nat_fr;
5578145522Sdarrenr
5579145522Sdarrenr	if (np != NULL) {
5580145522Sdarrenr		if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
5581255332Scy			(void) ipf_frag_natnew(softc, fin, 0, nat);
5582145522Sdarrenr
5583145522Sdarrenr	/* ------------------------------------------------------------- */
5584255332Scy	/* A few quick notes:                                            */
5585255332Scy	/*      Following are test conditions prior to calling the       */
5586255332Scy	/*      ipf_proxy_check routine.                                 */
5587255332Scy	/*                                                               */
5588255332Scy	/*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
5589255332Scy	/*      with a map rule, we attempt to match the packet's        */
5590255332Scy	/*      source port against in_dport, otherwise we'd compare the */
5591255332Scy	/*      packet's destination.                                    */
5592145522Sdarrenr	/* ------------------------------------------------------------- */
5593145522Sdarrenr		if (np->in_apr != NULL) {
5594255332Scy			i = ipf_proxy_check(fin, nat);
559560852Sdarrenr			if (i == -1) {
5596255332Scy				NBUMPSIDED(0, ns_ipf_proxy_fail);
5597145522Sdarrenr				return -1;
559860852Sdarrenr			}
559960852Sdarrenr		}
5600145522Sdarrenr	}
560153642Sguido
5602255332Scy	ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
5603145522Sdarrenr
5604255332Scy	ipsumd = nat->nat_ipsumd;
5605145522Sdarrenr	/*
5606145522Sdarrenr	 * Fix up checksums, not by recalculating them, but
5607145522Sdarrenr	 * simply computing adjustments.
5608145522Sdarrenr	 * Why only do this for some platforms on inbound packets ?
5609145522Sdarrenr	 * Because for those that it is done, IP processing is yet to happen
5610145522Sdarrenr	 * and so the IPv4 header checksum has not yet been evaluated.
5611145522Sdarrenr	 * Perhaps it should always be done for the benefit of things like
5612145522Sdarrenr	 * fast forwarding (so that it doesn't need to be recomputed) but with
5613145522Sdarrenr	 * header checksum offloading, perhaps it is a moot point.
5614145522Sdarrenr	 */
5615255332Scy
5616255332Scy	switch (nat->nat_dir)
5617255332Scy	{
5618255332Scy	case NAT_INBOUND :
5619255332Scy		if ((fin->fin_flx & FI_ICMPERR) == 0) {
5620255332Scy			fin->fin_ip->ip_src = nat->nat_nsrcip;
5621255332Scy			fin->fin_saddr = nat->nat_nsrcaddr;
5622255332Scy		} else {
5623255332Scy			sum1 = nat->nat_osrcaddr;
5624255332Scy			sum2 = nat->nat_nsrcaddr;
5625255332Scy			CALC_SUMD(sum1, sum2, sumd);
5626255332Scy			ipsumd -= sumd;
5627255332Scy		}
5628255332Scy		fin->fin_ip->ip_dst = nat->nat_ndstip;
5629255332Scy		fin->fin_daddr = nat->nat_ndstaddr;
5630145522Sdarrenr#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5631145522Sdarrenr     defined(__osf__) || defined(linux)
5632255332Scy		ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0);
5633145522Sdarrenr#endif
5634255332Scy		break;
5635145522Sdarrenr
5636255332Scy	case NAT_OUTBOUND :
5637255332Scy		if ((fin->fin_flx & FI_ICMPERR) == 0) {
5638255332Scy			fin->fin_ip->ip_src = nat->nat_odstip;
5639255332Scy			fin->fin_saddr = nat->nat_odstaddr;
5640255332Scy		} else {
5641255332Scy			sum1 = nat->nat_odstaddr;
5642255332Scy			sum2 = nat->nat_ndstaddr;
5643255332Scy			CALC_SUMD(sum1, sum2, sumd);
5644255332Scy			ipsumd -= sumd;
5645255332Scy		}
5646255332Scy		fin->fin_ip->ip_dst = nat->nat_osrcip;
5647255332Scy		fin->fin_daddr = nat->nat_osrcaddr;
5648255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5649255332Scy     defined(__osf__) || defined(linux)
5650255332Scy		ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0);
5651255332Scy#endif
5652255332Scy		break;
5653255332Scy
5654255332Scy	case NAT_DIVERTIN :
5655255332Scy	    {
5656255332Scy		udphdr_t *uh;
5657255332Scy		ip_t *ip;
5658255332Scy		mb_t *m;
5659255332Scy
5660255332Scy		m = M_DUP(np->in_divmp);
5661255332Scy		if (m == NULL) {
5662255332Scy			NBUMPSIDED(0, ns_divert_dup);
5663255332Scy			return -1;
5664255332Scy		}
5665255332Scy
5666255332Scy		ip = MTOD(m, ip_t *);
5667255332Scy		ip->ip_id = htons(ipf_nextipid(fin));
5668255332Scy		sum1 = ntohs(ip->ip_len);
5669255332Scy		ip->ip_len = ntohs(ip->ip_len);
5670255332Scy		ip->ip_len += fin->fin_plen;
5671255332Scy		ip->ip_len = htons(ip->ip_len);
5672255332Scy
5673255332Scy		uh = (udphdr_t *)(ip + 1);
5674255332Scy		uh->uh_ulen += fin->fin_plen;
5675255332Scy		uh->uh_ulen = htons(uh->uh_ulen);
5676255332Scy
5677255332Scy		sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len);
5678255332Scy		sum2 += ntohs(ip->ip_off) & IP_DF;
5679255332Scy		CALC_SUMD(sum1, sum2, sumd);
5680255332Scy
5681255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5682255332Scy     defined(__osf__) || defined(linux)
5683255332Scy		ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0);
5684255332Scy#endif
5685255332Scy		PREP_MB_T(fin, m);
5686255332Scy
5687255332Scy		fin->fin_ip = ip;
5688255332Scy		fin->fin_plen += sizeof(ip_t) + 8;	/* UDP + new IPv4 hdr */
5689255332Scy		fin->fin_dlen += sizeof(ip_t) + 8;	/* UDP + old IPv4 hdr */
5690255332Scy
5691255332Scy		nflags &= ~IPN_TCPUDPICMP;
5692255332Scy
5693255332Scy		break;
5694255332Scy	    }
5695255332Scy
5696255332Scy	case NAT_DIVERTOUT :
5697255332Scy	    {
5698255332Scy		mb_t *m;
5699255332Scy
5700255332Scy		skip = ipf_nat_decap(fin, nat);
5701255332Scy		if (skip <= 0) {
5702255332Scy			NBUMPSIDED(0, ns_decap_fail);
5703255332Scy			return -1;
5704255332Scy		}
5705255332Scy
5706255332Scy		m = fin->fin_m;
5707255332Scy
5708255332Scy#if defined(MENTAT) && defined(_KERNEL)
5709255332Scy		m->b_rptr += skip;
5710255332Scy#else
5711255332Scy		m->m_data += skip;
5712255332Scy		m->m_len -= skip;
5713255332Scy
5714255332Scy# ifdef M_PKTHDR
5715255332Scy		if (m->m_flags & M_PKTHDR)
5716255332Scy			m->m_pkthdr.len -= skip;
5717255332Scy# endif
5718255332Scy#endif
5719255332Scy
5720255332Scy		ipf_nat_update(fin, nat);
5721255332Scy		nflags &= ~IPN_TCPUDPICMP;
5722255332Scy		fin->fin_flx |= FI_NATED;
5723255332Scy		if (np != NULL && np->in_tag.ipt_num[0] != 0)
5724255332Scy			fin->fin_nattag = &np->in_tag;
5725255332Scy		return 1;
5726255332Scy		/* NOTREACHED */
5727255332Scy	    }
5728255332Scy	}
5729255332Scy	if (nflags & IPN_TCPUDP)
5730255332Scy		tcp = fin->fin_dp;
5731255332Scy
5732145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
5733255332Scy		u_short *csump;
5734255332Scy
5735255332Scy		if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) {
5736255332Scy			switch (nat->nat_dir)
5737255332Scy			{
5738255332Scy			case NAT_INBOUND :
5739255332Scy				tcp->th_sport = nat->nat_nsport;
5740255332Scy				fin->fin_data[0] = ntohs(nat->nat_nsport);
5741255332Scy				tcp->th_dport = nat->nat_ndport;
5742255332Scy				fin->fin_data[1] = ntohs(nat->nat_ndport);
5743255332Scy				break;
5744255332Scy
5745255332Scy			case NAT_OUTBOUND :
5746255332Scy				tcp->th_sport = nat->nat_odport;
5747255332Scy				fin->fin_data[0] = ntohs(nat->nat_odport);
5748255332Scy				tcp->th_dport = nat->nat_osport;
5749255332Scy				fin->fin_data[1] = ntohs(nat->nat_osport);
5750255332Scy				break;
5751255332Scy			}
575292685Sdarrenr		}
575353642Sguido
5754145522Sdarrenr
5755255332Scy		if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) {
5756145522Sdarrenr			icmp = fin->fin_dp;
5757145522Sdarrenr
5758255332Scy			icmp->icmp_id = nat->nat_nicmpid;
5759145522Sdarrenr		}
5760145522Sdarrenr
5761255332Scy		csump = ipf_nat_proto(fin, nat, nflags);
5762255332Scy
5763255332Scy		/*
5764255332Scy		 * The above comments do not hold for layer 4 (or higher)
5765255332Scy		 * checksums...
5766255332Scy		 */
5767255332Scy		if (csump != NULL) {
5768255332Scy			if (nat->nat_dir == NAT_OUTBOUND)
5769255332Scy				ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0);
5770255332Scy			else
5771255332Scy				ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0);
5772255332Scy		}
5773145522Sdarrenr	}
5774145522Sdarrenr
5775145522Sdarrenr	fin->fin_flx |= FI_NATED;
5776145522Sdarrenr	if (np != NULL && np->in_tag.ipt_num[0] != 0)
5777145522Sdarrenr		fin->fin_nattag = &np->in_tag;
5778145522Sdarrenr	return 1;
5779145522Sdarrenr}
5780130886Sdarrenr
5781130886Sdarrenr
5782145522Sdarrenr/* ------------------------------------------------------------------------ */
5783255332Scy/* Function:    ipf_nat_proto                                               */
5784145522Sdarrenr/* Returns:     u_short* - pointer to transport header checksum to update,  */
5785145522Sdarrenr/*                         NULL if the transport protocol is not recognised */
5786145522Sdarrenr/*                         as needing a checksum update.                    */
5787145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
5788145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
5789145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
5790145522Sdarrenr/*                                                                          */
5791145522Sdarrenr/* Return the pointer to the checksum field for each protocol so understood.*/
5792145522Sdarrenr/* If support for making other changes to a protocol header is required,    */
5793145522Sdarrenr/* that is not strictly 'address' translation, such as clamping the MSS in  */
5794145522Sdarrenr/* TCP down to a specific value, then do it from here.                      */
5795145522Sdarrenr/* ------------------------------------------------------------------------ */
5796255332Scyu_short *
5797255332Scyipf_nat_proto(fin, nat, nflags)
5798255332Scy	fr_info_t *fin;
5799255332Scy	nat_t *nat;
5800255332Scy	u_int nflags;
5801145522Sdarrenr{
5802145522Sdarrenr	icmphdr_t *icmp;
5803145522Sdarrenr	u_short *csump;
5804145522Sdarrenr	tcphdr_t *tcp;
5805145522Sdarrenr	udphdr_t *udp;
580653642Sguido
5807145522Sdarrenr	csump = NULL;
5808145522Sdarrenr	if (fin->fin_out == 0) {
5809255332Scy		fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND);
5810145522Sdarrenr	} else {
5811255332Scy		fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0);
5812145522Sdarrenr	}
581353642Sguido
5814145522Sdarrenr	switch (fin->fin_p)
5815145522Sdarrenr	{
5816145522Sdarrenr	case IPPROTO_TCP :
5817145522Sdarrenr		tcp = fin->fin_dp;
5818110916Sdarrenr
5819255332Scy		if ((nflags & IPN_TCP) != 0)
5820255332Scy			csump = &tcp->th_sum;
582153642Sguido
5822145522Sdarrenr		/*
5823145522Sdarrenr		 * Do a MSS CLAMPING on a SYN packet,
5824145522Sdarrenr		 * only deal IPv4 for now.
5825145522Sdarrenr		 */
5826145522Sdarrenr		if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0)
5827255332Scy			ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump);
582860852Sdarrenr
5829145522Sdarrenr		break;
5830145522Sdarrenr
5831145522Sdarrenr	case IPPROTO_UDP :
5832145522Sdarrenr		udp = fin->fin_dp;
5833145522Sdarrenr
5834255332Scy		if ((nflags & IPN_UDP) != 0) {
5835255332Scy			if (udp->uh_sum != 0)
5836255332Scy				csump = &udp->uh_sum;
5837255332Scy		}
5838145522Sdarrenr		break;
5839145522Sdarrenr
5840145522Sdarrenr	case IPPROTO_ICMP :
5841145522Sdarrenr		icmp = fin->fin_dp;
5842145522Sdarrenr
5843145522Sdarrenr		if ((nflags & IPN_ICMPQUERY) != 0) {
5844145522Sdarrenr			if (icmp->icmp_cksum != 0)
5845145522Sdarrenr				csump = &icmp->icmp_cksum;
584653642Sguido		}
5847145522Sdarrenr		break;
584853642Sguido
5849255332Scy#ifdef USE_INET6
5850255332Scy	case IPPROTO_ICMPV6 :
5851255332Scy	    {
5852255332Scy		struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp;
585353642Sguido
5854255332Scy		icmp6 = fin->fin_dp;
5855145522Sdarrenr
5856255332Scy		if ((nflags & IPN_ICMPQUERY) != 0) {
5857255332Scy			if (icmp6->icmp6_cksum != 0)
5858255332Scy				csump = &icmp6->icmp6_cksum;
5859255332Scy		}
5860255332Scy		break;
5861255332Scy	    }
5862255332Scy#endif
5863145522Sdarrenr	}
5864255332Scy	return csump;
586553642Sguido}
586653642Sguido
586753642Sguido
5868145522Sdarrenr/* ------------------------------------------------------------------------ */
5869255332Scy/* Function:    ipf_nat_expire                                              */
5870145522Sdarrenr/* Returns:     Nil                                                         */
5871255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
5872145522Sdarrenr/*                                                                          */
5873145522Sdarrenr/* Check all of the timeout queues for entries at the top which need to be  */
5874145522Sdarrenr/* expired.                                                                 */
5875145522Sdarrenr/* ------------------------------------------------------------------------ */
5876255332Scyvoid
5877255332Scyipf_nat_expire(softc)
5878255332Scy	ipf_main_softc_t *softc;
587953642Sguido{
5880255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5881145522Sdarrenr	ipftq_t *ifq, *ifqnext;
5882145522Sdarrenr	ipftqent_t *tqe, *tqn;
5883161356Sguido	int i;
5884153876Sguido	SPL_INT(s);
588553642Sguido
588653642Sguido	SPL_NET(s);
5887255332Scy	WRITE_ENTER(&softc->ipf_nat);
5888255332Scy	for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL;
5889255332Scy	     ifq = ifq->ifq_next) {
5890145522Sdarrenr		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
5891255332Scy			if (tqe->tqe_die > softc->ipf_ticks)
5892145522Sdarrenr				break;
5893145522Sdarrenr			tqn = tqe->tqe_next;
5894255332Scy			ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE);
589553642Sguido		}
589653642Sguido	}
5897145522Sdarrenr
5898255332Scy	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) {
5899145522Sdarrenr		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
5900255332Scy			if (tqe->tqe_die > softc->ipf_ticks)
5901145522Sdarrenr				break;
5902145522Sdarrenr			tqn = tqe->tqe_next;
5903255332Scy			ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE);
5904145522Sdarrenr		}
5905145522Sdarrenr	}
5906145522Sdarrenr
5907255332Scy	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) {
5908145522Sdarrenr		ifqnext = ifq->ifq_next;
5909145522Sdarrenr
5910145522Sdarrenr		if (((ifq->ifq_flags & IFQF_DELETE) != 0) &&
5911145522Sdarrenr		    (ifq->ifq_ref == 0)) {
5912255332Scy			ipf_freetimeoutqueue(softc, ifq);
5913145522Sdarrenr		}
5914145522Sdarrenr	}
5915145522Sdarrenr
5916255332Scy	if (softn->ipf_nat_doflush != 0) {
5917255332Scy		ipf_nat_extraflush(softc, softn, 2);
5918255332Scy		softn->ipf_nat_doflush = 0;
5919170268Sdarrenr	}
5920170268Sdarrenr
5921255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
592253642Sguido	SPL_X(s);
592353642Sguido}
592453642Sguido
592553642Sguido
5926145522Sdarrenr/* ------------------------------------------------------------------------ */
5927255332Scy/* Function:    ipf_nat_sync                                                */
5928145522Sdarrenr/* Returns:     Nil                                                         */
5929255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
5930255332Scy/*              ifp(I) - pointer to network interface                       */
5931145522Sdarrenr/*                                                                          */
5932145522Sdarrenr/* Walk through all of the currently active NAT sessions, looking for those */
5933145522Sdarrenr/* which need to have their translated address updated.                     */
5934145522Sdarrenr/* ------------------------------------------------------------------------ */
5935255332Scyvoid
5936255332Scyipf_nat_sync(softc, ifp)
5937255332Scy	ipf_main_softc_t *softc;
5938255332Scy	void *ifp;
593953642Sguido{
5940255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5941145522Sdarrenr	u_32_t sum1, sum2, sumd;
5942255332Scy	i6addr_t in;
5943145522Sdarrenr	ipnat_t *n;
5944145522Sdarrenr	nat_t *nat;
594553642Sguido	void *ifp2;
5946255332Scy	int idx;
5947153876Sguido	SPL_INT(s);
594853642Sguido
5949255332Scy	if (softc->ipf_running <= 0)
5950145522Sdarrenr		return;
5951145522Sdarrenr
595253642Sguido	/*
595353642Sguido	 * Change IP addresses for NAT sessions for any protocol except TCP
5954145522Sdarrenr	 * since it will break the TCP connection anyway.  The only rules
5955145522Sdarrenr	 * which will get changed are those which are "map ... -> 0/32",
5956145522Sdarrenr	 * where the rule specifies the address is taken from the interface.
595753642Sguido	 */
595853642Sguido	SPL_NET(s);
5959255332Scy	WRITE_ENTER(&softc->ipf_nat);
5960145522Sdarrenr
5961255332Scy	if (softc->ipf_running <= 0) {
5962255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
5963145522Sdarrenr		return;
5964145522Sdarrenr	}
5965145522Sdarrenr
5966255332Scy	for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) {
5967145522Sdarrenr		if ((nat->nat_flags & IPN_TCP) != 0)
5968145522Sdarrenr			continue;
5969255332Scy
5970145522Sdarrenr		n = nat->nat_ptr;
5971255332Scy		if (n != NULL) {
5972255332Scy			if (n->in_v[1] == 4) {
5973255332Scy				if (n->in_redir & NAT_MAP) {
5974255332Scy					if ((n->in_nsrcaddr != 0) ||
5975255332Scy					    (n->in_nsrcmsk != 0xffffffff))
5976255332Scy						continue;
5977255332Scy				} else if (n->in_redir & NAT_REDIRECT) {
5978255332Scy					if ((n->in_ndstaddr != 0) ||
5979255332Scy					    (n->in_ndstmsk != 0xffffffff))
5980255332Scy						continue;
5981255332Scy				}
5982255332Scy			}
5983255332Scy#ifdef USE_INET6
5984255332Scy			if (n->in_v[1] == 4) {
5985255332Scy				if (n->in_redir & NAT_MAP) {
5986255332Scy					if (!IP6_ISZERO(&n->in_nsrcaddr) ||
5987255332Scy					    !IP6_ISONES(&n->in_nsrcmsk))
5988255332Scy						continue;
5989255332Scy				} else if (n->in_redir & NAT_REDIRECT) {
5990255332Scy					if (!IP6_ISZERO(&n->in_ndstaddr) ||
5991255332Scy					    !IP6_ISONES(&n->in_ndstmsk))
5992255332Scy						continue;
5993255332Scy				}
5994255332Scy			}
5995255332Scy#endif
5996255332Scy		}
5997255332Scy
5998145522Sdarrenr		if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) ||
5999145522Sdarrenr		     (ifp == nat->nat_ifps[1]))) {
6000255332Scy			nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0],
6001255332Scy						  nat->nat_v[0]);
6002255332Scy			if ((nat->nat_ifps[0] != NULL) &&
6003255332Scy			    (nat->nat_ifps[0] != (void *)-1)) {
6004255332Scy				nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
6005255332Scy			}
6006145522Sdarrenr			if (nat->nat_ifnames[1][0] != '\0') {
6007145522Sdarrenr				nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1],
6008255332Scy							  nat->nat_v[1]);
6009255332Scy			} else {
6010145522Sdarrenr				nat->nat_ifps[1] = nat->nat_ifps[0];
6011255332Scy			}
6012255332Scy			if ((nat->nat_ifps[1] != NULL) &&
6013255332Scy			    (nat->nat_ifps[1] != (void *)-1)) {
6014255332Scy				nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
6015255332Scy			}
6016145522Sdarrenr			ifp2 = nat->nat_ifps[0];
6017145522Sdarrenr			if (ifp2 == NULL)
6018145522Sdarrenr				continue;
6019145522Sdarrenr
602053642Sguido			/*
602153642Sguido			 * Change the map-to address to be the same as the
602253642Sguido			 * new one.
602353642Sguido			 */
6024255332Scy			sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6);
6025255332Scy			if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2,
6026255332Scy				       &in, NULL) != -1) {
6027255332Scy				if (nat->nat_v[0] == 4)
6028255332Scy					nat->nat_nsrcip = in.in4;
6029255332Scy			}
6030255332Scy			sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6);
603153642Sguido
603253642Sguido			if (sum1 == sum2)
603353642Sguido				continue;
603453642Sguido			/*
603553642Sguido			 * Readjust the checksum adjustment to take into
603653642Sguido			 * account the new IP#.
603753642Sguido			 */
603853642Sguido			CALC_SUMD(sum1, sum2, sumd);
603955929Sguido			/* XXX - dont change for TCP when solaris does
604055929Sguido			 * hardware checksumming.
604155929Sguido			 */
604255929Sguido			sumd += nat->nat_sumd[0];
604355929Sguido			nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
604455929Sguido			nat->nat_sumd[1] = nat->nat_sumd[0];
604553642Sguido		}
6046145522Sdarrenr	}
604753642Sguido
6048255332Scy	for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) {
6049255332Scy		char *base = n->in_names;
6050255332Scy
6051145522Sdarrenr		if ((ifp == NULL) || (n->in_ifps[0] == ifp))
6052255332Scy			n->in_ifps[0] = ipf_resolvenic(softc,
6053255332Scy						       base + n->in_ifnames[0],
6054255332Scy						       n->in_v[0]);
6055145522Sdarrenr		if ((ifp == NULL) || (n->in_ifps[1] == ifp))
6056255332Scy			n->in_ifps[1] = ipf_resolvenic(softc,
6057255332Scy						       base + n->in_ifnames[1],
6058255332Scy						       n->in_v[1]);
6059255332Scy
6060255332Scy		if (n->in_redir & NAT_REDIRECT)
6061255332Scy			idx = 1;
6062255332Scy		else
6063255332Scy			idx = 0;
6064255332Scy
6065255332Scy		if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) &&
6066255332Scy		    (n->in_ifps[idx] != NULL &&
6067255332Scy		     n->in_ifps[idx] != (void *)-1)) {
6068255332Scy
6069255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc,
6070255332Scy					     0, n->in_ifps[idx]);
6071255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst,
6072255332Scy					     0, n->in_ifps[idx]);
6073255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc,
6074255332Scy					     0, n->in_ifps[idx]);
6075255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst,
6076255332Scy					     0, n->in_ifps[idx]);
6077255332Scy		}
6078145522Sdarrenr	}
6079255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
608053642Sguido	SPL_X(s);
608153642Sguido}
608253642Sguido
608353642Sguido
6084145522Sdarrenr/* ------------------------------------------------------------------------ */
6085255332Scy/* Function:    ipf_nat_icmpquerytype                                       */
6086145522Sdarrenr/* Returns:     int - 1 == success, 0 == failure                            */
6087145522Sdarrenr/* Parameters:  icmptype(I) - ICMP type number                              */
6088145522Sdarrenr/*                                                                          */
6089145522Sdarrenr/* Tests to see if the ICMP type number passed is a query/response type or  */
6090145522Sdarrenr/* not.                                                                     */
6091145522Sdarrenr/* ------------------------------------------------------------------------ */
6092255332Scystatic int
6093255332Scyipf_nat_icmpquerytype(icmptype)
6094255332Scy	int icmptype;
6095145522Sdarrenr{
6096145522Sdarrenr
6097145522Sdarrenr	/*
6098145522Sdarrenr	 * For the ICMP query NAT code, it is essential that both the query
6099145522Sdarrenr	 * and the reply match on the NAT rule. Because the NAT structure
6100145522Sdarrenr	 * does not keep track of the icmptype, and a single NAT structure
6101145522Sdarrenr	 * is used for all icmp types with the same src, dest and id, we
6102145522Sdarrenr	 * simply define the replies as queries as well. The funny thing is,
6103145522Sdarrenr	 * altough it seems silly to call a reply a query, this is exactly
6104145522Sdarrenr	 * as it is defined in the IPv4 specification
6105145522Sdarrenr	 */
6106145522Sdarrenr	switch (icmptype)
6107145522Sdarrenr	{
6108145522Sdarrenr	case ICMP_ECHOREPLY:
6109145522Sdarrenr	case ICMP_ECHO:
6110145522Sdarrenr	/* route aedvertisement/solliciation is currently unsupported: */
6111145522Sdarrenr	/* it would require rewriting the ICMP data section            */
6112145522Sdarrenr	case ICMP_TSTAMP:
6113145522Sdarrenr	case ICMP_TSTAMPREPLY:
6114145522Sdarrenr	case ICMP_IREQ:
6115145522Sdarrenr	case ICMP_IREQREPLY:
6116145522Sdarrenr	case ICMP_MASKREQ:
6117145522Sdarrenr	case ICMP_MASKREPLY:
6118145522Sdarrenr		return 1;
6119145522Sdarrenr	default:
6120145522Sdarrenr		return 0;
6121145522Sdarrenr	}
6122145522Sdarrenr}
6123145522Sdarrenr
6124145522Sdarrenr
6125145522Sdarrenr/* ------------------------------------------------------------------------ */
6126145522Sdarrenr/* Function:    nat_log                                                     */
6127145522Sdarrenr/* Returns:     Nil                                                         */
6128255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6129255332Scy/*              softn(I) - pointer to NAT context structure                 */
6130255332Scy/*              nat(I)    - pointer to NAT structure                        */
6131255332Scy/*              action(I) - action related to NAT structure being performed */
6132145522Sdarrenr/*                                                                          */
6133145522Sdarrenr/* Creates a NAT log entry.                                                 */
6134145522Sdarrenr/* ------------------------------------------------------------------------ */
6135255332Scyvoid
6136255332Scyipf_nat_log(softc, softn, nat, action)
6137255332Scy	ipf_main_softc_t *softc;
6138255332Scy	ipf_nat_softc_t *softn;
6139255332Scy	struct nat *nat;
6140255332Scy	u_int action;
614153642Sguido{
6142145522Sdarrenr#ifdef	IPFILTER_LOG
6143139005Smlaier# ifndef LARGE_NAT
614453642Sguido	struct ipnat *np;
6145138979Sdarrenr	int rulen;
6146138979Sdarrenr# endif
614753642Sguido	struct natlog natl;
614853642Sguido	void *items[1];
614953642Sguido	size_t sizes[1];
6150138979Sdarrenr	int types[1];
615153642Sguido
6152255332Scy	bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip,
6153255332Scy	      sizeof(natl.nl_osrcip));
6154255332Scy	bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip,
6155255332Scy	      sizeof(natl.nl_nsrcip));
6156255332Scy	bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip,
6157255332Scy	      sizeof(natl.nl_odstip));
6158255332Scy	bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip,
6159255332Scy	      sizeof(natl.nl_ndstip));
6160255332Scy
6161145522Sdarrenr	natl.nl_bytes[0] = nat->nat_bytes[0];
6162145522Sdarrenr	natl.nl_bytes[1] = nat->nat_bytes[1];
6163145522Sdarrenr	natl.nl_pkts[0] = nat->nat_pkts[0];
6164145522Sdarrenr	natl.nl_pkts[1] = nat->nat_pkts[1];
6165255332Scy	natl.nl_odstport = nat->nat_odport;
6166255332Scy	natl.nl_osrcport = nat->nat_osport;
6167255332Scy	natl.nl_nsrcport = nat->nat_nsport;
6168255332Scy	natl.nl_ndstport = nat->nat_ndport;
6169255332Scy	natl.nl_p[0] = nat->nat_pr[0];
6170255332Scy	natl.nl_p[1] = nat->nat_pr[1];
6171255332Scy	natl.nl_v[0] = nat->nat_v[0];
6172255332Scy	natl.nl_v[1] = nat->nat_v[1];
6173255332Scy	natl.nl_type = nat->nat_redir;
6174255332Scy	natl.nl_action = action;
617553642Sguido	natl.nl_rule = -1;
6176255332Scy
6177255332Scy	bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0],
6178255332Scy	      sizeof(nat->nat_ifnames[0]));
6179255332Scy	bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1],
6180255332Scy	      sizeof(nat->nat_ifnames[1]));
6181255332Scy
6182145522Sdarrenr# ifndef LARGE_NAT
618353642Sguido	if (nat->nat_ptr != NULL) {
6184255332Scy		for (rulen = 0, np = softn->ipf_nat_list; np != NULL;
6185255332Scy		     np = np->in_next, rulen++)
618653642Sguido			if (np == nat->nat_ptr) {
618753642Sguido				natl.nl_rule = rulen;
618853642Sguido				break;
618953642Sguido			}
619053642Sguido	}
6191145522Sdarrenr# endif
619253642Sguido	items[0] = &natl;
619353642Sguido	sizes[0] = sizeof(natl);
619453642Sguido	types[0] = 0;
619553642Sguido
6196255332Scy	(void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1);
6197145522Sdarrenr#endif
619853642Sguido}
619992685Sdarrenr
620092685Sdarrenr
620192685Sdarrenr#if defined(__OpenBSD__)
6202145522Sdarrenr/* ------------------------------------------------------------------------ */
6203255332Scy/* Function:    ipf_nat_ifdetach                                            */
6204145522Sdarrenr/* Returns:     Nil                                                         */
6205145522Sdarrenr/* Parameters:  ifp(I) - pointer to network interface                       */
6206145522Sdarrenr/*                                                                          */
6207145522Sdarrenr/* Compatibility interface for OpenBSD to trigger the correct updating of   */
6208145522Sdarrenr/* interface references within IPFilter.                                    */
6209145522Sdarrenr/* ------------------------------------------------------------------------ */
6210255332Scyvoid
6211255332Scyipf_nat_ifdetach(ifp)
6212255332Scy	void *ifp;
621392685Sdarrenr{
6214255332Scy	ipf_main_softc_t *softc;
6215255332Scy
6216255332Scy	softc = ipf_get_softc(0);
6217255332Scy
6218255332Scy	ipf_sync(ifp);
621992685Sdarrenr	return;
622092685Sdarrenr}
622192685Sdarrenr#endif
6222110916Sdarrenr
6223110916Sdarrenr
6224145522Sdarrenr/* ------------------------------------------------------------------------ */
6225255332Scy/* Function:    ipf_nat_rule_deref                                          */
6226170268Sdarrenr/* Returns:     Nil                                                         */
6227255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6228255332Scy/*              inp(I)   - pointer to pointer to NAT rule                   */
6229170268Sdarrenr/* Write Locks: ipf_nat                                                     */
6230170268Sdarrenr/*                                                                          */
6231255332Scy/* Dropping the refernce count for a rule means that whatever held the      */
6232255332Scy/* pointer to this rule (*inp) is no longer interested in it and when the   */
6233255332Scy/* reference count drops to zero, any resources allocated for the rule can  */
6234255332Scy/* be released and the rule itself free'd.                                  */
6235170268Sdarrenr/* ------------------------------------------------------------------------ */
6236255332Scyvoid
6237255332Scyipf_nat_rule_deref(softc, inp)
6238255332Scy	ipf_main_softc_t *softc;
6239255332Scy	ipnat_t **inp;
6240170268Sdarrenr{
6241255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6242255332Scy	ipnat_t *n;
6243170268Sdarrenr
6244255332Scy	n = *inp;
6245170268Sdarrenr	*inp = NULL;
6246255332Scy	n->in_use--;
6247255332Scy	if (n->in_use > 0)
6248255332Scy		return;
6249255332Scy
6250255332Scy	if (n->in_apr != NULL)
6251255332Scy		ipf_proxy_deref(n->in_apr);
6252255332Scy
6253255332Scy	ipf_nat_rule_fini(softc, n);
6254255332Scy
6255255332Scy	if (n->in_redir & NAT_REDIRECT) {
6256255332Scy		if ((n->in_flags & IPN_PROXYRULE) == 0) {
6257255332Scy			ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr);
6258255332Scy		}
6259255332Scy	}
6260255332Scy	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
6261255332Scy		if ((n->in_flags & IPN_PROXYRULE) == 0) {
6262255332Scy			ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map);
6263255332Scy		}
6264255332Scy	}
6265255332Scy
6266255332Scy	if (n->in_tqehead[0] != NULL) {
6267255332Scy		if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) {
6268255332Scy			ipf_freetimeoutqueue(softc, n->in_tqehead[1]);
6269255332Scy		}
6270255332Scy	}
6271255332Scy
6272255332Scy	if (n->in_tqehead[1] != NULL) {
6273255332Scy		if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) {
6274255332Scy			ipf_freetimeoutqueue(softc, n->in_tqehead[1]);
6275255332Scy		}
6276255332Scy	}
6277255332Scy
6278255332Scy	if ((n->in_flags & IPN_PROXYRULE) == 0) {
6279255332Scy		ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules);
6280255332Scy	}
6281255332Scy
6282255332Scy	MUTEX_DESTROY(&n->in_lock);
6283255332Scy
6284255332Scy	KFREES(n, n->in_size);
6285255332Scy
6286255332Scy#if SOLARIS && !defined(INSTANCES)
6287255332Scy	if (softn->ipf_nat_stats.ns_rules == 0)
6288255332Scy		pfil_delayed_copy = 1;
6289170268Sdarrenr#endif
6290170268Sdarrenr}
6291170268Sdarrenr
6292170268Sdarrenr
6293170268Sdarrenr/* ------------------------------------------------------------------------ */
6294255332Scy/* Function:    ipf_nat_deref                                               */
6295145522Sdarrenr/* Returns:     Nil                                                         */
6296255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6297255332Scy/*              natp(I)  - pointer to pointer to NAT table entry            */
6298145522Sdarrenr/*                                                                          */
6299145522Sdarrenr/* Decrement the reference counter for this NAT table entry and free it if  */
6300145522Sdarrenr/* there are no more things using it.                                       */
6301172776Sdarrenr/*                                                                          */
6302172776Sdarrenr/* IF nat_ref == 1 when this function is called, then we have an orphan nat */
6303172776Sdarrenr/* structure *because* it only gets called on paths _after_ nat_ref has been*/
6304172776Sdarrenr/* incremented.  If nat_ref == 1 then we shouldn't decrement it here        */
6305172776Sdarrenr/* because nat_delete() will do that and send nat_ref to -1.                */
6306172776Sdarrenr/*                                                                          */
6307172776Sdarrenr/* Holding the lock on nat_lock is required to serialise nat_delete() being */
6308172776Sdarrenr/* called from a NAT flush ioctl with a deref happening because of a packet.*/
6309145522Sdarrenr/* ------------------------------------------------------------------------ */
6310255332Scyvoid
6311255332Scyipf_nat_deref(softc, natp)
6312255332Scy	ipf_main_softc_t *softc;
6313255332Scy	nat_t **natp;
6314145522Sdarrenr{
6315145522Sdarrenr	nat_t *nat;
6316145522Sdarrenr
6317145522Sdarrenr	nat = *natp;
6318145522Sdarrenr	*natp = NULL;
6319172776Sdarrenr
6320172776Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
6321172776Sdarrenr	if (nat->nat_ref > 1) {
6322172776Sdarrenr		nat->nat_ref--;
6323255332Scy		ASSERT(nat->nat_ref >= 0);
6324172776Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
6325172776Sdarrenr		return;
6326172776Sdarrenr	}
6327172776Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
6328172776Sdarrenr
6329255332Scy	WRITE_ENTER(&softc->ipf_nat);
6330255332Scy	ipf_nat_delete(softc, nat, NL_EXPIRE);
6331255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
6332145522Sdarrenr}
6333145522Sdarrenr
6334145522Sdarrenr
6335145522Sdarrenr/* ------------------------------------------------------------------------ */
6336255332Scy/* Function:    ipf_nat_clone                                               */
6337145522Sdarrenr/* Returns:     ipstate_t* - NULL == cloning failed,                        */
6338145522Sdarrenr/*                           else pointer to new state structure            */
6339145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
6340145522Sdarrenr/*              is(I)  - pointer to master state structure                  */
6341145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
6342145522Sdarrenr/*                                                                          */
6343145522Sdarrenr/* Create a "duplcate" state table entry from the master.                   */
6344145522Sdarrenr/* ------------------------------------------------------------------------ */
6345255332Scynat_t *
6346255332Scyipf_nat_clone(fin, nat)
6347255332Scy	fr_info_t *fin;
6348255332Scy	nat_t *nat;
6349145522Sdarrenr{
6350255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
6351255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6352145522Sdarrenr	frentry_t *fr;
6353145522Sdarrenr	nat_t *clone;
6354145522Sdarrenr	ipnat_t *np;
6355145522Sdarrenr
6356145522Sdarrenr	KMALLOC(clone, nat_t *);
6357255332Scy	if (clone == NULL) {
6358255332Scy		NBUMPSIDED(fin->fin_out, ns_clone_nomem);
6359145522Sdarrenr		return NULL;
6360255332Scy	}
6361145522Sdarrenr	bcopy((char *)nat, (char *)clone, sizeof(*clone));
6362145522Sdarrenr
6363145522Sdarrenr	MUTEX_NUKE(&clone->nat_lock);
6364145522Sdarrenr
6365255332Scy	clone->nat_rev = fin->fin_rev;
6366153876Sguido	clone->nat_aps = NULL;
6367153876Sguido	/*
6368255332Scy	 * Initialize all these so that ipf_nat_delete() doesn't cause a crash.
6369153876Sguido	 */
6370153876Sguido	clone->nat_tqe.tqe_pnext = NULL;
6371153876Sguido	clone->nat_tqe.tqe_next = NULL;
6372153876Sguido	clone->nat_tqe.tqe_ifq = NULL;
6373153876Sguido	clone->nat_tqe.tqe_parent = clone;
6374153876Sguido
6375145522Sdarrenr	clone->nat_flags &= ~SI_CLONE;
6376145522Sdarrenr	clone->nat_flags |= SI_CLONED;
6377145522Sdarrenr
6378153876Sguido	if (clone->nat_hm)
6379153876Sguido		clone->nat_hm->hm_ref++;
6380145522Sdarrenr
6381255332Scy	if (ipf_nat_insert(softc, softn, clone) == -1) {
6382145522Sdarrenr		KFREE(clone);
6383255332Scy		NBUMPSIDED(fin->fin_out, ns_insert_fail);
6384145522Sdarrenr		return NULL;
6385145522Sdarrenr	}
6386255332Scy
6387145522Sdarrenr	np = clone->nat_ptr;
6388145522Sdarrenr	if (np != NULL) {
6389255332Scy		if (softn->ipf_nat_logging)
6390255332Scy			ipf_nat_log(softc, softn, clone, NL_CLONE);
6391145522Sdarrenr		np->in_use++;
6392145522Sdarrenr	}
6393145522Sdarrenr	fr = clone->nat_fr;
6394145522Sdarrenr	if (fr != NULL) {
6395145522Sdarrenr		MUTEX_ENTER(&fr->fr_lock);
6396145522Sdarrenr		fr->fr_ref++;
6397145522Sdarrenr		MUTEX_EXIT(&fr->fr_lock);
6398145522Sdarrenr	}
6399145522Sdarrenr
6400255332Scy
6401145522Sdarrenr	/*
6402145522Sdarrenr	 * Because the clone is created outside the normal loop of things and
6403145522Sdarrenr	 * TCP has special needs in terms of state, initialise the timeout
6404145522Sdarrenr	 * state of the new NAT from here.
6405145522Sdarrenr	 */
6406255332Scy	if (clone->nat_pr[0] == IPPROTO_TCP) {
6407255332Scy		(void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq,
6408255332Scy				   clone->nat_flags, 2);
6409145522Sdarrenr	}
6410255332Scy	clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone);
6411255332Scy	if (softn->ipf_nat_logging)
6412255332Scy		ipf_nat_log(softc, softn, clone, NL_CLONE);
6413145522Sdarrenr	return clone;
6414145522Sdarrenr}
6415145522Sdarrenr
6416145522Sdarrenr
6417145522Sdarrenr/* ------------------------------------------------------------------------ */
6418255332Scy/* Function:   ipf_nat_wildok                                               */
6419145522Sdarrenr/* Returns:    int - 1 == packet's ports match wildcards                    */
6420145522Sdarrenr/*                   0 == packet's ports don't match wildcards              */
6421145522Sdarrenr/* Parameters: nat(I)   - NAT entry                                         */
6422145522Sdarrenr/*             sport(I) - source port                                       */
6423145522Sdarrenr/*             dport(I) - destination port                                  */
6424145522Sdarrenr/*             flags(I) - wildcard flags                                    */
6425145522Sdarrenr/*             dir(I)   - packet direction                                  */
6426145522Sdarrenr/*                                                                          */
6427145522Sdarrenr/* Use NAT entry and packet direction to determine which combination of     */
6428145522Sdarrenr/* wildcard flags should be used.                                           */
6429145522Sdarrenr/* ------------------------------------------------------------------------ */
6430255332Scyint
6431255332Scyipf_nat_wildok(nat, sport, dport, flags, dir)
6432255332Scy	nat_t *nat;
6433255332Scy	int sport, dport, flags, dir;
6434145522Sdarrenr{
6435145522Sdarrenr	/*
6436145522Sdarrenr	 * When called by       dir is set to
6437145522Sdarrenr	 * nat_inlookup         NAT_INBOUND (0)
6438145522Sdarrenr	 * nat_outlookup        NAT_OUTBOUND (1)
6439145522Sdarrenr	 *
6440145522Sdarrenr	 * We simply combine the packet's direction in dir with the original
6441145522Sdarrenr	 * "intended" direction of that NAT entry in nat->nat_dir to decide
6442145522Sdarrenr	 * which combination of wildcard flags to allow.
6443145522Sdarrenr	 */
6444255332Scy	switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)))
6445145522Sdarrenr	{
6446145522Sdarrenr	case 3: /* outbound packet / outbound entry */
6447255332Scy		if (((nat->nat_osport == sport) ||
6448145522Sdarrenr		    (flags & SI_W_SPORT)) &&
6449255332Scy		    ((nat->nat_odport == dport) ||
6450145522Sdarrenr		    (flags & SI_W_DPORT)))
6451145522Sdarrenr			return 1;
6452145522Sdarrenr		break;
6453145522Sdarrenr	case 2: /* outbound packet / inbound entry */
6454255332Scy		if (((nat->nat_osport == dport) ||
6455255332Scy		    (flags & SI_W_SPORT)) &&
6456255332Scy		    ((nat->nat_odport == sport) ||
6457255332Scy		    (flags & SI_W_DPORT)))
6458145522Sdarrenr			return 1;
6459145522Sdarrenr		break;
6460145522Sdarrenr	case 1: /* inbound packet / outbound entry */
6461255332Scy		if (((nat->nat_osport == dport) ||
6462255332Scy		    (flags & SI_W_SPORT)) &&
6463255332Scy		    ((nat->nat_odport == sport) ||
6464255332Scy		    (flags & SI_W_DPORT)))
6465145522Sdarrenr			return 1;
6466145522Sdarrenr		break;
6467145522Sdarrenr	case 0: /* inbound packet / inbound entry */
6468255332Scy		if (((nat->nat_osport == sport) ||
6469145522Sdarrenr		    (flags & SI_W_SPORT)) &&
6470255332Scy		    ((nat->nat_odport == dport) ||
6471145522Sdarrenr		    (flags & SI_W_DPORT)))
6472145522Sdarrenr			return 1;
6473145522Sdarrenr		break;
6474145522Sdarrenr	default:
6475145522Sdarrenr		break;
6476145522Sdarrenr	}
6477145522Sdarrenr
6478145522Sdarrenr	return(0);
6479145522Sdarrenr}
6480145522Sdarrenr
6481145522Sdarrenr
6482145522Sdarrenr/* ------------------------------------------------------------------------ */
6483145522Sdarrenr/* Function:    nat_mssclamp                                                */
6484145522Sdarrenr/* Returns:     Nil                                                         */
6485145522Sdarrenr/* Parameters:  tcp(I)    - pointer to TCP header                           */
6486145522Sdarrenr/*              maxmss(I) - value to clamp the TCP MSS to                   */
6487145522Sdarrenr/*              fin(I)    - pointer to packet information                   */
6488145522Sdarrenr/*              csump(I)  - pointer to TCP checksum                         */
6489145522Sdarrenr/*                                                                          */
6490145522Sdarrenr/* Check for MSS option and clamp it if necessary.  If found and changed,   */
6491145522Sdarrenr/* then the TCP header checksum will be updated to reflect the change in    */
6492145522Sdarrenr/* the MSS.                                                                 */
6493145522Sdarrenr/* ------------------------------------------------------------------------ */
6494255332Scystatic void
6495255332Scyipf_nat_mssclamp(tcp, maxmss, fin, csump)
6496255332Scy	tcphdr_t *tcp;
6497255332Scy	u_32_t maxmss;
6498255332Scy	fr_info_t *fin;
6499255332Scy	u_short *csump;
6500110916Sdarrenr{
6501110916Sdarrenr	u_char *cp, *ep, opt;
6502110916Sdarrenr	int hlen, advance;
6503110916Sdarrenr	u_32_t mss, sumd;
6504110916Sdarrenr
6505145522Sdarrenr	hlen = TCP_OFF(tcp) << 2;
6506110916Sdarrenr	if (hlen > sizeof(*tcp)) {
6507110916Sdarrenr		cp = (u_char *)tcp + sizeof(*tcp);
6508110916Sdarrenr		ep = (u_char *)tcp + hlen;
6509110916Sdarrenr
6510110916Sdarrenr		while (cp < ep) {
6511110916Sdarrenr			opt = cp[0];
6512110916Sdarrenr			if (opt == TCPOPT_EOL)
6513110916Sdarrenr				break;
6514110916Sdarrenr			else if (opt == TCPOPT_NOP) {
6515110916Sdarrenr				cp++;
6516110916Sdarrenr				continue;
6517110916Sdarrenr			}
6518145522Sdarrenr
6519145522Sdarrenr			if (cp + 1 >= ep)
6520110916Sdarrenr				break;
6521110916Sdarrenr			advance = cp[1];
6522145522Sdarrenr			if ((cp + advance > ep) || (advance <= 0))
6523110916Sdarrenr				break;
6524145522Sdarrenr			switch (opt)
6525145522Sdarrenr			{
6526110916Sdarrenr			case TCPOPT_MAXSEG:
6527110916Sdarrenr				if (advance != 4)
6528110916Sdarrenr					break;
6529145522Sdarrenr				mss = cp[2] * 256 + cp[3];
6530110916Sdarrenr				if (mss > maxmss) {
6531145522Sdarrenr					cp[2] = maxmss / 256;
6532145522Sdarrenr					cp[3] = maxmss & 0xff;
6533110916Sdarrenr					CALC_SUMD(mss, maxmss, sumd);
6534255332Scy					ipf_fix_outcksum(0, csump, sumd, 0);
6535110916Sdarrenr				}
6536110916Sdarrenr				break;
6537110916Sdarrenr			default:
6538110916Sdarrenr				/* ignore unknown options */
6539110916Sdarrenr				break;
6540110916Sdarrenr			}
6541145522Sdarrenr
6542145522Sdarrenr			cp += advance;
6543145522Sdarrenr		}
6544145522Sdarrenr	}
6545145522Sdarrenr}
6546145522Sdarrenr
6547145522Sdarrenr
6548145522Sdarrenr/* ------------------------------------------------------------------------ */
6549255332Scy/* Function:    ipf_nat_setqueue                                            */
6550145522Sdarrenr/* Returns:     Nil                                                         */
6551255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6552255332Scy/*              softn(I) - pointer to NAT context structure                 */
6553255332Scy/*              nat(I)- pointer to NAT structure                            */
6554145522Sdarrenr/* Locks:       ipf_nat (read or write)                                     */
6555145522Sdarrenr/*                                                                          */
6556145522Sdarrenr/* Put the NAT entry on its default queue entry, using rev as a helped in   */
6557145522Sdarrenr/* determining which queue it should be placed on.                          */
6558145522Sdarrenr/* ------------------------------------------------------------------------ */
6559255332Scyvoid
6560255332Scyipf_nat_setqueue(softc, softn, nat)
6561255332Scy	ipf_main_softc_t *softc;
6562255332Scy	ipf_nat_softc_t *softn;
6563255332Scy	nat_t *nat;
6564145522Sdarrenr{
6565145522Sdarrenr	ipftq_t *oifq, *nifq;
6566255332Scy	int rev = nat->nat_rev;
6567145522Sdarrenr
6568145522Sdarrenr	if (nat->nat_ptr != NULL)
6569145522Sdarrenr		nifq = nat->nat_ptr->in_tqehead[rev];
6570145522Sdarrenr	else
6571145522Sdarrenr		nifq = NULL;
6572145522Sdarrenr
6573145522Sdarrenr	if (nifq == NULL) {
6574255332Scy		switch (nat->nat_pr[0])
6575145522Sdarrenr		{
6576145522Sdarrenr		case IPPROTO_UDP :
6577255332Scy			nifq = &softn->ipf_nat_udptq;
6578145522Sdarrenr			break;
6579145522Sdarrenr		case IPPROTO_ICMP :
6580255332Scy			nifq = &softn->ipf_nat_icmptq;
6581145522Sdarrenr			break;
6582145522Sdarrenr		case IPPROTO_TCP :
6583255332Scy			nifq = softn->ipf_nat_tcptq +
6584255332Scy			       nat->nat_tqe.tqe_state[rev];
6585145522Sdarrenr			break;
6586145522Sdarrenr		default :
6587255332Scy			nifq = &softn->ipf_nat_iptq;
6588145522Sdarrenr			break;
6589145522Sdarrenr		}
6590145522Sdarrenr	}
6591145522Sdarrenr
6592145522Sdarrenr	oifq = nat->nat_tqe.tqe_ifq;
6593145522Sdarrenr	/*
6594145522Sdarrenr	 * If it's currently on a timeout queue, move it from one queue to
6595145522Sdarrenr	 * another, else put it on the end of the newly determined queue.
6596145522Sdarrenr	 */
6597145522Sdarrenr	if (oifq != NULL)
6598255332Scy		ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq);
6599145522Sdarrenr	else
6600255332Scy		ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat);
6601145522Sdarrenr	return;
6602145522Sdarrenr}
6603170268Sdarrenr
6604170268Sdarrenr
6605170268Sdarrenr/* ------------------------------------------------------------------------ */
6606170268Sdarrenr/* Function:    nat_getnext                                                 */
6607170268Sdarrenr/* Returns:     int - 0 == ok, else error                                   */
6608255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6609255332Scy/*              t(I)   - pointer to ipftoken structure                      */
6610170268Sdarrenr/*              itp(I) - pointer to ipfgeniter_t structure                  */
6611170268Sdarrenr/*                                                                          */
6612170268Sdarrenr/* Fetch the next nat/ipnat structure pointer from the linked list and      */
6613170268Sdarrenr/* copy it out to the storage space pointed to by itp_data.  The next item  */
6614170268Sdarrenr/* in the list to look at is put back in the ipftoken struture.             */
6615170268Sdarrenr/* ------------------------------------------------------------------------ */
6616255332Scystatic int
6617255332Scyipf_nat_getnext(softc, t, itp, objp)
6618255332Scy	ipf_main_softc_t *softc;
6619255332Scy	ipftoken_t *t;
6620255332Scy	ipfgeniter_t *itp;
6621255332Scy	ipfobj_t *objp;
6622170268Sdarrenr{
6623255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6624170268Sdarrenr	hostmap_t *hm, *nexthm = NULL, zerohm;
6625170268Sdarrenr	ipnat_t *ipn, *nextipnat = NULL, zeroipn;
6626170268Sdarrenr	nat_t *nat, *nextnat = NULL, zeronat;
6627255332Scy	int error = 0;
6628255332Scy	void *nnext;
6629170268Sdarrenr
6630255332Scy	if (itp->igi_nitems != 1) {
6631255332Scy		IPFERROR(60075);
6632172776Sdarrenr		return ENOSPC;
6633255332Scy	}
6634170268Sdarrenr
6635255332Scy	READ_ENTER(&softc->ipf_nat);
6636170268Sdarrenr
6637170268Sdarrenr	switch (itp->igi_type)
6638170268Sdarrenr	{
6639170268Sdarrenr	case IPFGENITER_HOSTMAP :
6640170268Sdarrenr		hm = t->ipt_data;
6641170268Sdarrenr		if (hm == NULL) {
6642255332Scy			nexthm = softn->ipf_hm_maplist;
6643170268Sdarrenr		} else {
6644170268Sdarrenr			nexthm = hm->hm_next;
6645170268Sdarrenr		}
6646255332Scy		if (nexthm != NULL) {
6647255332Scy			ATOMIC_INC32(nexthm->hm_ref);
6648255332Scy			t->ipt_data = nexthm;
6649255332Scy		} else {
6650255332Scy			bzero(&zerohm, sizeof(zerohm));
6651255332Scy			nexthm = &zerohm;
6652255332Scy			t->ipt_data = NULL;
6653255332Scy		}
6654255332Scy		nnext = nexthm->hm_next;
6655170268Sdarrenr		break;
6656170268Sdarrenr
6657170268Sdarrenr	case IPFGENITER_IPNAT :
6658170268Sdarrenr		ipn = t->ipt_data;
6659170268Sdarrenr		if (ipn == NULL) {
6660255332Scy			nextipnat = softn->ipf_nat_list;
6661170268Sdarrenr		} else {
6662170268Sdarrenr			nextipnat = ipn->in_next;
6663170268Sdarrenr		}
6664255332Scy		if (nextipnat != NULL) {
6665255332Scy			ATOMIC_INC32(nextipnat->in_use);
6666255332Scy			t->ipt_data = nextipnat;
6667255332Scy		} else {
6668255332Scy			bzero(&zeroipn, sizeof(zeroipn));
6669255332Scy			nextipnat = &zeroipn;
6670255332Scy			t->ipt_data = NULL;
6671255332Scy		}
6672255332Scy		nnext = nextipnat->in_next;
6673170268Sdarrenr		break;
6674170268Sdarrenr
6675170268Sdarrenr	case IPFGENITER_NAT :
6676170268Sdarrenr		nat = t->ipt_data;
6677170268Sdarrenr		if (nat == NULL) {
6678255332Scy			nextnat = softn->ipf_nat_instances;
6679170268Sdarrenr		} else {
6680170268Sdarrenr			nextnat = nat->nat_next;
6681170268Sdarrenr		}
6682255332Scy		if (nextnat != NULL) {
6683255332Scy			MUTEX_ENTER(&nextnat->nat_lock);
6684255332Scy			nextnat->nat_ref++;
6685255332Scy			MUTEX_EXIT(&nextnat->nat_lock);
6686255332Scy			t->ipt_data = nextnat;
6687255332Scy		} else {
6688255332Scy			bzero(&zeronat, sizeof(zeronat));
6689255332Scy			nextnat = &zeronat;
6690255332Scy			t->ipt_data = NULL;
6691255332Scy		}
6692255332Scy		nnext = nextnat->nat_next;
6693170268Sdarrenr		break;
6694255332Scy
6695170268Sdarrenr	default :
6696255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
6697255332Scy		IPFERROR(60055);
6698170268Sdarrenr		return EINVAL;
6699170268Sdarrenr	}
6700170268Sdarrenr
6701255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
6702170268Sdarrenr
6703255332Scy	objp->ipfo_ptr = itp->igi_data;
6704170268Sdarrenr
6705172776Sdarrenr	switch (itp->igi_type)
6706172776Sdarrenr	{
6707172776Sdarrenr	case IPFGENITER_HOSTMAP :
6708255332Scy		error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm));
6709255332Scy		if (error != 0) {
6710255332Scy			IPFERROR(60049);
6711255332Scy			error = EFAULT;
6712255332Scy		}
6713172776Sdarrenr		if (hm != NULL) {
6714255332Scy			WRITE_ENTER(&softc->ipf_nat);
6715255332Scy			ipf_nat_hostmapdel(softc, &hm);
6716255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
6717172776Sdarrenr		}
6718172776Sdarrenr		break;
6719255332Scy
6720172776Sdarrenr	case IPFGENITER_IPNAT :
6721255332Scy		objp->ipfo_size = nextipnat->in_size;
6722255332Scy		objp->ipfo_type = IPFOBJ_IPNAT;
6723255332Scy		error = ipf_outobjk(softc, objp, nextipnat);
6724172776Sdarrenr		if (ipn != NULL) {
6725255332Scy			WRITE_ENTER(&softc->ipf_nat);
6726255332Scy			ipf_nat_rule_deref(softc, &ipn);
6727255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
6728172776Sdarrenr		}
6729172776Sdarrenr		break;
6730172776Sdarrenr
6731170268Sdarrenr	case IPFGENITER_NAT :
6732255332Scy		objp->ipfo_size = sizeof(nat_t);
6733255332Scy		objp->ipfo_type = IPFOBJ_NAT;
6734255332Scy		error = ipf_outobjk(softc, objp, nextnat);
6735255332Scy		if (nat != NULL)
6736255332Scy			ipf_nat_deref(softc, &nat);
6737170268Sdarrenr
6738170268Sdarrenr		break;
6739170268Sdarrenr	}
6740170268Sdarrenr
6741255332Scy	if (nnext == NULL)
6742255332Scy		ipf_token_mark_complete(t);
6743255332Scy
6744170268Sdarrenr	return error;
6745170268Sdarrenr}
6746170268Sdarrenr
6747170268Sdarrenr
6748170268Sdarrenr/* ------------------------------------------------------------------------ */
6749170268Sdarrenr/* Function:    nat_extraflush                                              */
6750170268Sdarrenr/* Returns:     int - 0 == success, -1 == failure                           */
6751255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6752255332Scy/*              softn(I) - pointer to NAT context structure                 */
6753255332Scy/*              which(I) - how to flush the active NAT table                */
6754170268Sdarrenr/* Write Locks: ipf_nat                                                     */
6755170268Sdarrenr/*                                                                          */
6756170268Sdarrenr/* Flush nat tables.  Three actions currently defined:                      */
6757170268Sdarrenr/* which == 0 : flush all nat table entries                                 */
6758170268Sdarrenr/* which == 1 : flush TCP connections which have started to close but are   */
6759170268Sdarrenr/*	      stuck for some reason.                                        */
6760170268Sdarrenr/* which == 2 : flush TCP connections which have been idle for a long time, */
6761170268Sdarrenr/*	      starting at > 4 days idle and working back in successive half-*/
6762170268Sdarrenr/*	      days to at most 12 hours old.  If this fails to free enough   */
6763170268Sdarrenr/*            slots then work backwards in half hour slots to 30 minutes.   */
6764170268Sdarrenr/*            If that too fails, then work backwards in 30 second intervals */
6765170268Sdarrenr/*            for the last 30 minutes to at worst 30 seconds idle.          */
6766170268Sdarrenr/* ------------------------------------------------------------------------ */
6767255332Scystatic int
6768255332Scyipf_nat_extraflush(softc, softn, which)
6769255332Scy	ipf_main_softc_t *softc;
6770255332Scy	ipf_nat_softc_t *softn;
6771255332Scy	int which;
6772170268Sdarrenr{
6773170268Sdarrenr	nat_t *nat, **natp;
6774170268Sdarrenr	ipftqent_t *tqn;
6775255332Scy	ipftq_t *ifq;
6776170268Sdarrenr	int removed;
6777170268Sdarrenr	SPL_INT(s);
6778170268Sdarrenr
6779170268Sdarrenr	removed = 0;
6780170268Sdarrenr
6781170268Sdarrenr	SPL_NET(s);
6782170268Sdarrenr	switch (which)
6783170268Sdarrenr	{
6784170268Sdarrenr	case 0 :
6785255332Scy		softn->ipf_nat_stats.ns_flush_all++;
6786170268Sdarrenr		/*
6787170268Sdarrenr		 * Style 0 flush removes everything...
6788170268Sdarrenr		 */
6789255332Scy		for (natp = &softn->ipf_nat_instances;
6790255332Scy		     ((nat = *natp) != NULL); ) {
6791255332Scy			ipf_nat_delete(softc, nat, NL_FLUSH);
6792170268Sdarrenr			removed++;
6793170268Sdarrenr		}
6794170268Sdarrenr		break;
6795170268Sdarrenr
6796170268Sdarrenr	case 1 :
6797255332Scy		softn->ipf_nat_stats.ns_flush_closing++;
6798170268Sdarrenr		/*
6799170268Sdarrenr		 * Since we're only interested in things that are closing,
6800170268Sdarrenr		 * we can start with the appropriate timeout queue.
6801170268Sdarrenr		 */
6802255332Scy		for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT;
6803255332Scy		     ifq != NULL; ifq = ifq->ifq_next) {
6804170268Sdarrenr
6805170268Sdarrenr			for (tqn = ifq->ifq_head; tqn != NULL; ) {
6806170268Sdarrenr				nat = tqn->tqe_parent;
6807170268Sdarrenr				tqn = tqn->tqe_next;
6808255332Scy				if (nat->nat_pr[0] != IPPROTO_TCP ||
6809255332Scy				    nat->nat_pr[1] != IPPROTO_TCP)
6810170268Sdarrenr					break;
6811255332Scy				ipf_nat_delete(softc, nat, NL_EXPIRE);
6812170268Sdarrenr				removed++;
6813170268Sdarrenr			}
6814170268Sdarrenr		}
6815170268Sdarrenr
6816170268Sdarrenr		/*
6817170268Sdarrenr		 * Also need to look through the user defined queues.
6818170268Sdarrenr		 */
6819255332Scy		for (ifq = softn->ipf_nat_utqe; ifq != NULL;
6820255332Scy		     ifq = ifq->ifq_next) {
6821170268Sdarrenr			for (tqn = ifq->ifq_head; tqn != NULL; ) {
6822170268Sdarrenr				nat = tqn->tqe_parent;
6823170268Sdarrenr				tqn = tqn->tqe_next;
6824255332Scy				if (nat->nat_pr[0] != IPPROTO_TCP ||
6825255332Scy				    nat->nat_pr[1] != IPPROTO_TCP)
6826170268Sdarrenr					continue;
6827170268Sdarrenr
6828170268Sdarrenr				if ((nat->nat_tcpstate[0] >
6829170268Sdarrenr				     IPF_TCPS_ESTABLISHED) &&
6830170268Sdarrenr				    (nat->nat_tcpstate[1] >
6831170268Sdarrenr				     IPF_TCPS_ESTABLISHED)) {
6832255332Scy					ipf_nat_delete(softc, nat, NL_EXPIRE);
6833170268Sdarrenr					removed++;
6834170268Sdarrenr				}
6835170268Sdarrenr			}
6836170268Sdarrenr		}
6837170268Sdarrenr		break;
6838170268Sdarrenr
6839170268Sdarrenr		/*
6840170268Sdarrenr		 * Args 5-11 correspond to flushing those particular states
6841170268Sdarrenr		 * for TCP connections.
6842170268Sdarrenr		 */
6843170268Sdarrenr	case IPF_TCPS_CLOSE_WAIT :
6844170268Sdarrenr	case IPF_TCPS_FIN_WAIT_1 :
6845170268Sdarrenr	case IPF_TCPS_CLOSING :
6846170268Sdarrenr	case IPF_TCPS_LAST_ACK :
6847170268Sdarrenr	case IPF_TCPS_FIN_WAIT_2 :
6848170268Sdarrenr	case IPF_TCPS_TIME_WAIT :
6849170268Sdarrenr	case IPF_TCPS_CLOSED :
6850255332Scy		softn->ipf_nat_stats.ns_flush_state++;
6851255332Scy		tqn = softn->ipf_nat_tcptq[which].ifq_head;
6852170268Sdarrenr		while (tqn != NULL) {
6853170268Sdarrenr			nat = tqn->tqe_parent;
6854170268Sdarrenr			tqn = tqn->tqe_next;
6855255332Scy			ipf_nat_delete(softc, nat, NL_FLUSH);
6856170268Sdarrenr			removed++;
6857170268Sdarrenr		}
6858170268Sdarrenr		break;
6859255332Scy
6860170268Sdarrenr	default :
6861170268Sdarrenr		if (which < 30)
6862170268Sdarrenr			break;
6863255332Scy
6864255332Scy		softn->ipf_nat_stats.ns_flush_timeout++;
6865170268Sdarrenr		/*
6866170268Sdarrenr		 * Take a large arbitrary number to mean the number of seconds
6867170268Sdarrenr		 * for which which consider to be the maximum value we'll allow
6868170268Sdarrenr		 * the expiration to be.
6869170268Sdarrenr		 */
6870170268Sdarrenr		which = IPF_TTLVAL(which);
6871255332Scy		for (natp = &softn->ipf_nat_instances;
6872255332Scy		     ((nat = *natp) != NULL); ) {
6873255332Scy			if (softc->ipf_ticks - nat->nat_touched > which) {
6874255332Scy				ipf_nat_delete(softc, nat, NL_FLUSH);
6875170268Sdarrenr				removed++;
6876170268Sdarrenr			} else
6877170268Sdarrenr				natp = &nat->nat_next;
6878170268Sdarrenr		}
6879170268Sdarrenr		break;
6880170268Sdarrenr	}
6881170268Sdarrenr
6882170268Sdarrenr	if (which != 2) {
6883170268Sdarrenr		SPL_X(s);
6884170268Sdarrenr		return removed;
6885170268Sdarrenr	}
6886170268Sdarrenr
6887255332Scy	softn->ipf_nat_stats.ns_flush_queue++;
6888255332Scy
6889170268Sdarrenr	/*
6890255332Scy	 * Asked to remove inactive entries because the table is full, try
6891255332Scy	 * again, 3 times, if first attempt failed with a different criteria
6892255332Scy	 * each time.  The order tried in must be in decreasing age.
6893255332Scy	 * Another alternative is to implement random drop and drop N entries
6894255332Scy	 * at random until N have been freed up.
6895170268Sdarrenr	 */
6896255332Scy	if (softc->ipf_ticks - softn->ipf_nat_last_force_flush >
6897255332Scy	    IPF_TTLVAL(5)) {
6898255332Scy		softn->ipf_nat_last_force_flush = softc->ipf_ticks;
6899255332Scy
6900255332Scy		removed = ipf_queueflush(softc, ipf_nat_flush_entry,
6901255332Scy					 softn->ipf_nat_tcptq,
6902255332Scy					 softn->ipf_nat_utqe,
6903255332Scy					 &softn->ipf_nat_stats.ns_active,
6904255332Scy					 softn->ipf_nat_table_sz,
6905255332Scy					 softn->ipf_nat_table_wm_low);
6906170268Sdarrenr	}
6907170268Sdarrenr
6908170268Sdarrenr	SPL_X(s);
6909170268Sdarrenr	return removed;
6910170268Sdarrenr}
6911170268Sdarrenr
6912170268Sdarrenr
6913170268Sdarrenr/* ------------------------------------------------------------------------ */
6914255332Scy/* Function:    ipf_nat_flush_entry                                         */
6915170268Sdarrenr/* Returns:     0 - always succeeds                                         */
6916255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6917255332Scy/*              entry(I) - pointer to NAT entry                             */
6918170268Sdarrenr/* Write Locks: ipf_nat                                                     */
6919170268Sdarrenr/*                                                                          */
6920170268Sdarrenr/* This function is a stepping stone between ipf_queueflush() and           */
6921170268Sdarrenr/* nat_dlete().  It is used so we can provide a uniform interface via the   */
6922170268Sdarrenr/* ipf_queueflush() function.  Since the nat_delete() function returns void */
6923170268Sdarrenr/* we translate that to mean it always succeeds in deleting something.      */
6924170268Sdarrenr/* ------------------------------------------------------------------------ */
6925255332Scystatic int
6926255332Scyipf_nat_flush_entry(softc, entry)
6927255332Scy	ipf_main_softc_t *softc;
6928255332Scy	void *entry;
6929170268Sdarrenr{
6930255332Scy	ipf_nat_delete(softc, entry, NL_FLUSH);
6931170268Sdarrenr	return 0;
6932170268Sdarrenr}
6933172776Sdarrenr
6934172776Sdarrenr
6935172776Sdarrenr/* ------------------------------------------------------------------------ */
6936255332Scy/* Function:    ipf_nat_iterator                                            */
6937255332Scy/* Returns:     int - 0 == ok, else error                                   */
6938255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6939255332Scy/*              token(I) - pointer to ipftoken structure                    */
6940255332Scy/*              itp(I)   - pointer to ipfgeniter_t structure                */
6941255332Scy/*              obj(I)   - pointer to data description structure            */
6942255332Scy/*                                                                          */
6943255332Scy/* This function acts as a handler for the SIOCGENITER ioctls that use a    */
6944255332Scy/* generic structure to iterate through a list.  There are three different  */
6945255332Scy/* linked lists of NAT related information to go through: NAT rules, active */
6946255332Scy/* NAT mappings and the NAT fragment cache.                                 */
6947255332Scy/* ------------------------------------------------------------------------ */
6948255332Scystatic int
6949255332Scyipf_nat_iterator(softc, token, itp, obj)
6950255332Scy	ipf_main_softc_t *softc;
6951255332Scy	ipftoken_t *token;
6952255332Scy	ipfgeniter_t *itp;
6953255332Scy	ipfobj_t *obj;
6954255332Scy{
6955255332Scy	int error;
6956255332Scy
6957255332Scy	if (itp->igi_data == NULL) {
6958255332Scy		IPFERROR(60052);
6959255332Scy		return EFAULT;
6960255332Scy	}
6961255332Scy
6962255332Scy	switch (itp->igi_type)
6963255332Scy	{
6964255332Scy	case IPFGENITER_HOSTMAP :
6965255332Scy	case IPFGENITER_IPNAT :
6966255332Scy	case IPFGENITER_NAT :
6967255332Scy		error = ipf_nat_getnext(softc, token, itp, obj);
6968255332Scy		break;
6969255332Scy
6970255332Scy	case IPFGENITER_NATFRAG :
6971255332Scy		error = ipf_frag_nat_next(softc, token, itp);
6972255332Scy		break;
6973255332Scy	default :
6974255332Scy		IPFERROR(60053);
6975255332Scy		error = EINVAL;
6976255332Scy		break;
6977255332Scy	}
6978255332Scy
6979255332Scy	return error;
6980255332Scy}
6981255332Scy
6982255332Scy
6983255332Scy/* ------------------------------------------------------------------------ */
6984255332Scy/* Function:    ipf_nat_setpending                                          */
6985255332Scy/* Returns:     Nil                                                         */
6986255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6987255332Scy/*              nat(I)   - pointer to NAT structure                         */
6988255332Scy/* Locks:       ipf_nat (read or write)                                     */
6989255332Scy/*                                                                          */
6990255332Scy/* Put the NAT entry on to the pending queue - this queue has a very short  */
6991255332Scy/* lifetime where items are put that can't be deleted straight away because */
6992255332Scy/* of locking issues but we want to delete them ASAP, anyway.  In calling   */
6993255332Scy/* this function, it is assumed that the owner (if there is one, as shown   */
6994255332Scy/* by nat_me) is no longer interested in it.                                */
6995255332Scy/* ------------------------------------------------------------------------ */
6996255332Scyvoid
6997255332Scyipf_nat_setpending(softc, nat)
6998255332Scy	ipf_main_softc_t *softc;
6999255332Scy	nat_t *nat;
7000255332Scy{
7001255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7002255332Scy	ipftq_t *oifq;
7003255332Scy
7004255332Scy	oifq = nat->nat_tqe.tqe_ifq;
7005255332Scy	if (oifq != NULL)
7006255332Scy		ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq,
7007255332Scy			      &softn->ipf_nat_pending);
7008255332Scy	else
7009255332Scy		ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe,
7010255332Scy				&softn->ipf_nat_pending, nat);
7011255332Scy
7012255332Scy	if (nat->nat_me != NULL) {
7013255332Scy		*nat->nat_me = NULL;
7014255332Scy		nat->nat_me = NULL;
7015255332Scy		nat->nat_ref--;
7016255332Scy		ASSERT(nat->nat_ref >= 0);
7017255332Scy	}
7018255332Scy}
7019255332Scy
7020255332Scy
7021255332Scy/* ------------------------------------------------------------------------ */
7022255332Scy/* Function:    nat_newrewrite                                              */
7023255332Scy/* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
7024255332Scy/*                    allow rule to be moved if IPN_ROUNDR is set.          */
7025255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7026255332Scy/*              nat(I) - pointer to NAT entry                               */
7027255332Scy/*              ni(I)  - pointer to structure with misc. information needed */
7028255332Scy/*                       to create new NAT entry.                           */
7029255332Scy/* Write Lock:  ipf_nat                                                     */
7030255332Scy/*                                                                          */
7031255332Scy/* This function is responsible for setting up an active NAT session where  */
7032255332Scy/* we are changing both the source and destination parameters at the same   */
7033255332Scy/* time.  The loop in here works differently to elsewhere - each iteration  */
7034255332Scy/* is responsible for changing a single parameter that can be incremented.  */
7035255332Scy/* So one pass may increase the source IP#, next source port, next dest. IP#*/
7036255332Scy/* and the last destination port for a total of 4 iterations to try each.   */
7037255332Scy/* This is done to try and exhaustively use the translation space available.*/
7038255332Scy/* ------------------------------------------------------------------------ */
7039255332Scystatic int
7040255332Scyipf_nat_newrewrite(fin, nat, nai)
7041255332Scy	fr_info_t *fin;
7042255332Scy	nat_t *nat;
7043255332Scy	natinfo_t *nai;
7044255332Scy{
7045255332Scy	int src_search = 1;
7046255332Scy	int dst_search = 1;
7047255332Scy	fr_info_t frnat;
7048255332Scy	u_32_t flags;
7049255332Scy	u_short swap;
7050255332Scy	ipnat_t *np;
7051255332Scy	nat_t *natl;
7052255332Scy	int l = 0;
7053255332Scy	int changed;
7054255332Scy
7055255332Scy	natl = NULL;
7056255332Scy	changed = -1;
7057255332Scy	np = nai->nai_np;
7058255332Scy	flags = nat->nat_flags;
7059255332Scy	bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
7060255332Scy
7061255332Scy	nat->nat_hm = NULL;
7062255332Scy
7063255332Scy	do {
7064255332Scy		changed = -1;
7065255332Scy		/* TRACE (l, src_search, dst_search, np) */
7066255332Scy
7067255332Scy		if ((src_search == 0) && (np->in_spnext == 0) &&
7068255332Scy		    (dst_search == 0) && (np->in_dpnext == 0)) {
7069255332Scy			if (l > 0)
7070255332Scy				return -1;
7071255332Scy		}
7072255332Scy
7073255332Scy		/*
7074255332Scy		 * Find a new source address
7075255332Scy		 */
7076255332Scy		if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr,
7077255332Scy				     &frnat.fin_saddr) == -1) {
7078255332Scy			return -1;
7079255332Scy		}
7080255332Scy
7081255332Scy		if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) {
7082255332Scy			src_search = 0;
7083255332Scy			if (np->in_stepnext == 0)
7084255332Scy				np->in_stepnext = 1;
7085255332Scy
7086255332Scy		} else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) {
7087255332Scy			src_search = 0;
7088255332Scy			if (np->in_stepnext == 0)
7089255332Scy				np->in_stepnext = 1;
7090255332Scy
7091255332Scy		} else if (np->in_nsrcmsk == 0xffffffff) {
7092255332Scy			src_search = 0;
7093255332Scy			if (np->in_stepnext == 0)
7094255332Scy				np->in_stepnext = 1;
7095255332Scy
7096255332Scy		} else if (np->in_nsrcmsk != 0xffffffff) {
7097255332Scy			if (np->in_stepnext == 0 && changed == -1) {
7098255332Scy				np->in_snip++;
7099255332Scy				np->in_stepnext++;
7100255332Scy				changed = 0;
7101255332Scy			}
7102255332Scy		}
7103255332Scy
7104255332Scy		if ((flags & IPN_TCPUDPICMP) != 0) {
7105255332Scy			if (np->in_spnext != 0)
7106255332Scy				frnat.fin_data[0] = np->in_spnext;
7107255332Scy
7108255332Scy			/*
7109255332Scy			 * Standard port translation.  Select next port.
7110255332Scy			 */
7111255332Scy			if ((flags & IPN_FIXEDSPORT) != 0) {
7112255332Scy				np->in_stepnext = 2;
7113255332Scy			} else if ((np->in_stepnext == 1) &&
7114255332Scy				   (changed == -1) && (natl != NULL)) {
7115255332Scy				np->in_spnext++;
7116255332Scy				np->in_stepnext++;
7117255332Scy				changed = 1;
7118255332Scy				if (np->in_spnext > np->in_spmax)
7119255332Scy					np->in_spnext = np->in_spmin;
7120255332Scy			}
7121255332Scy		} else {
7122255332Scy			np->in_stepnext = 2;
7123255332Scy		}
7124255332Scy		np->in_stepnext &= 0x3;
7125255332Scy
7126255332Scy		/*
7127255332Scy		 * Find a new destination address
7128255332Scy		 */
7129255332Scy		/* TRACE (fin, np, l, frnat) */
7130255332Scy
7131255332Scy		if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr,
7132255332Scy				     &frnat.fin_daddr) == -1)
7133255332Scy			return -1;
7134255332Scy		if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) {
7135255332Scy			dst_search = 0;
7136255332Scy			if (np->in_stepnext == 2)
7137255332Scy				np->in_stepnext = 3;
7138255332Scy
7139255332Scy		} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) {
7140255332Scy			dst_search = 0;
7141255332Scy			if (np->in_stepnext == 2)
7142255332Scy				np->in_stepnext = 3;
7143255332Scy
7144255332Scy		} else if (np->in_ndstmsk == 0xffffffff) {
7145255332Scy			dst_search = 0;
7146255332Scy			if (np->in_stepnext == 2)
7147255332Scy				np->in_stepnext = 3;
7148255332Scy
7149255332Scy		} else if (np->in_ndstmsk != 0xffffffff) {
7150255332Scy			if ((np->in_stepnext == 2) && (changed == -1) &&
7151255332Scy			    (natl != NULL)) {
7152255332Scy				changed = 2;
7153255332Scy				np->in_stepnext++;
7154255332Scy				np->in_dnip++;
7155255332Scy			}
7156255332Scy		}
7157255332Scy
7158255332Scy		if ((flags & IPN_TCPUDPICMP) != 0) {
7159255332Scy			if (np->in_dpnext != 0)
7160255332Scy				frnat.fin_data[1] = np->in_dpnext;
7161255332Scy
7162255332Scy			/*
7163255332Scy			 * Standard port translation.  Select next port.
7164255332Scy			 */
7165255332Scy			if ((flags & IPN_FIXEDDPORT) != 0) {
7166255332Scy				np->in_stepnext = 0;
7167255332Scy			} else if (np->in_stepnext == 3 && changed == -1) {
7168255332Scy				np->in_dpnext++;
7169255332Scy				np->in_stepnext++;
7170255332Scy				changed = 3;
7171255332Scy				if (np->in_dpnext > np->in_dpmax)
7172255332Scy					np->in_dpnext = np->in_dpmin;
7173255332Scy			}
7174255332Scy		} else {
7175255332Scy			if (np->in_stepnext == 3)
7176255332Scy				np->in_stepnext = 0;
7177255332Scy		}
7178255332Scy
7179255332Scy		/* TRACE (frnat) */
7180255332Scy
7181255332Scy		/*
7182255332Scy		 * Here we do a lookup of the connection as seen from
7183255332Scy		 * the outside.  If an IP# pair already exists, try
7184255332Scy		 * again.  So if you have A->B becomes C->B, you can
7185255332Scy		 * also have D->E become C->E but not D->B causing
7186255332Scy		 * another C->B.  Also take protocol and ports into
7187255332Scy		 * account when determining whether a pre-existing
7188255332Scy		 * NAT setup will cause an external conflict where
7189255332Scy		 * this is appropriate.
7190255332Scy		 *
7191255332Scy		 * fin_data[] is swapped around because we are doing a
7192255332Scy		 * lookup of the packet is if it were moving in the opposite
7193255332Scy		 * direction of the one we are working with now.
7194255332Scy		 */
7195255332Scy		if (flags & IPN_TCPUDP) {
7196255332Scy			swap = frnat.fin_data[0];
7197255332Scy			frnat.fin_data[0] = frnat.fin_data[1];
7198255332Scy			frnat.fin_data[1] = swap;
7199255332Scy		}
7200255332Scy		if (fin->fin_out == 1) {
7201255332Scy			natl = ipf_nat_inlookup(&frnat,
7202255332Scy						flags & ~(SI_WILDP|NAT_SEARCH),
7203255332Scy						(u_int)frnat.fin_p,
7204255332Scy						frnat.fin_dst, frnat.fin_src);
7205255332Scy
7206255332Scy		} else {
7207255332Scy			natl = ipf_nat_outlookup(&frnat,
7208255332Scy						 flags & ~(SI_WILDP|NAT_SEARCH),
7209255332Scy						 (u_int)frnat.fin_p,
7210255332Scy						 frnat.fin_dst, frnat.fin_src);
7211255332Scy		}
7212255332Scy		if (flags & IPN_TCPUDP) {
7213255332Scy			swap = frnat.fin_data[0];
7214255332Scy			frnat.fin_data[0] = frnat.fin_data[1];
7215255332Scy			frnat.fin_data[1] = swap;
7216255332Scy		}
7217255332Scy
7218255332Scy		/* TRACE natl, in_stepnext, l */
7219255332Scy
7220255332Scy		if ((natl != NULL) && (l > 8))	/* XXX 8 is arbitrary */
7221255332Scy			return -1;
7222255332Scy
7223255332Scy		np->in_stepnext &= 0x3;
7224255332Scy
7225255332Scy		l++;
7226255332Scy		changed = -1;
7227255332Scy	} while (natl != NULL);
7228255332Scy
7229255332Scy	nat->nat_osrcip = fin->fin_src;
7230255332Scy	nat->nat_odstip = fin->fin_dst;
7231255332Scy	nat->nat_nsrcip = frnat.fin_src;
7232255332Scy	nat->nat_ndstip = frnat.fin_dst;
7233255332Scy
7234255332Scy	if ((flags & IPN_TCPUDP) != 0) {
7235255332Scy		nat->nat_osport = htons(fin->fin_data[0]);
7236255332Scy		nat->nat_odport = htons(fin->fin_data[1]);
7237255332Scy		nat->nat_nsport = htons(frnat.fin_data[0]);
7238255332Scy		nat->nat_ndport = htons(frnat.fin_data[1]);
7239255332Scy	} else if ((flags & IPN_ICMPQUERY) != 0) {
7240255332Scy		nat->nat_oicmpid = fin->fin_data[1];
7241255332Scy		nat->nat_nicmpid = frnat.fin_data[1];
7242255332Scy	}
7243255332Scy
7244255332Scy	return 0;
7245255332Scy}
7246255332Scy
7247255332Scy
7248255332Scy/* ------------------------------------------------------------------------ */
7249255332Scy/* Function:    nat_newdivert                                               */
7250255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7251255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7252255332Scy/*              nat(I) - pointer to NAT entry                               */
7253255332Scy/*              ni(I)  - pointer to structure with misc. information needed */
7254255332Scy/*                       to create new NAT entry.                           */
7255255332Scy/* Write Lock:  ipf_nat                                                     */
7256255332Scy/*                                                                          */
7257255332Scy/* Create a new NAT  divert session as defined by the NAT rule.  This is    */
7258255332Scy/* somewhat different to other NAT session creation routines because we     */
7259255332Scy/* do not iterate through either port numbers or IP addresses, searching    */
7260255332Scy/* for a unique mapping, however, a complimentary duplicate check is made.  */
7261255332Scy/* ------------------------------------------------------------------------ */
7262255332Scystatic int
7263255332Scyipf_nat_newdivert(fin, nat, nai)
7264255332Scy	fr_info_t *fin;
7265255332Scy	nat_t *nat;
7266255332Scy	natinfo_t *nai;
7267255332Scy{
7268255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
7269255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7270255332Scy	fr_info_t frnat;
7271255332Scy	ipnat_t *np;
7272255332Scy	nat_t *natl;
7273255332Scy	int p;
7274255332Scy
7275255332Scy	np = nai->nai_np;
7276255332Scy	bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
7277255332Scy
7278255332Scy	nat->nat_pr[0] = 0;
7279255332Scy	nat->nat_osrcaddr = fin->fin_saddr;
7280255332Scy	nat->nat_odstaddr = fin->fin_daddr;
7281255332Scy	frnat.fin_saddr = htonl(np->in_snip);
7282255332Scy	frnat.fin_daddr = htonl(np->in_dnip);
7283255332Scy	if ((nat->nat_flags & IPN_TCPUDP) != 0) {
7284255332Scy		nat->nat_osport = htons(fin->fin_data[0]);
7285255332Scy		nat->nat_odport = htons(fin->fin_data[1]);
7286255332Scy	} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
7287255332Scy		nat->nat_oicmpid = fin->fin_data[1];
7288255332Scy	}
7289255332Scy
7290255332Scy	if (np->in_redir & NAT_DIVERTUDP) {
7291255332Scy		frnat.fin_data[0] = np->in_spnext;
7292255332Scy		frnat.fin_data[1] = np->in_dpnext;
7293255332Scy		frnat.fin_flx |= FI_TCPUDP;
7294255332Scy		p = IPPROTO_UDP;
7295255332Scy	} else {
7296255332Scy		frnat.fin_flx &= ~FI_TCPUDP;
7297255332Scy		p = IPPROTO_IPIP;
7298255332Scy	}
7299255332Scy
7300255332Scy	if (fin->fin_out == 1) {
7301255332Scy		natl = ipf_nat_inlookup(&frnat, 0, p,
7302255332Scy					frnat.fin_dst, frnat.fin_src);
7303255332Scy
7304255332Scy	} else {
7305255332Scy		natl = ipf_nat_outlookup(&frnat, 0, p,
7306255332Scy					 frnat.fin_dst, frnat.fin_src);
7307255332Scy	}
7308255332Scy
7309255332Scy	if (natl != NULL) {
7310255332Scy		NBUMPSIDED(fin->fin_out, ns_divert_exist);
7311255332Scy		return -1;
7312255332Scy	}
7313255332Scy
7314255332Scy	nat->nat_nsrcaddr = frnat.fin_saddr;
7315255332Scy	nat->nat_ndstaddr = frnat.fin_daddr;
7316255332Scy	if ((nat->nat_flags & IPN_TCPUDP) != 0) {
7317255332Scy		nat->nat_nsport = htons(frnat.fin_data[0]);
7318255332Scy		nat->nat_ndport = htons(frnat.fin_data[1]);
7319255332Scy	} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
7320255332Scy		nat->nat_nicmpid = frnat.fin_data[1];
7321255332Scy	}
7322255332Scy
7323255332Scy	nat->nat_pr[fin->fin_out] = fin->fin_p;
7324255332Scy	nat->nat_pr[1 - fin->fin_out] = p;
7325255332Scy
7326255332Scy	if (np->in_redir & NAT_REDIRECT)
7327255332Scy		nat->nat_dir = NAT_DIVERTIN;
7328255332Scy	else
7329255332Scy		nat->nat_dir = NAT_DIVERTOUT;
7330255332Scy
7331255332Scy	return 0;
7332255332Scy}
7333255332Scy
7334255332Scy
7335255332Scy/* ------------------------------------------------------------------------ */
7336255332Scy/* Function:    nat_builddivertmp                                           */
7337255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7338255332Scy/* Parameters:  softn(I) - pointer to NAT context structure                 */
7339255332Scy/*              np(I)    - pointer to a NAT rule                            */
7340255332Scy/*                                                                          */
7341255332Scy/* For divert rules, a skeleton packet representing what will be prepended  */
7342255332Scy/* to the real packet is created.  Even though we don't have the full       */
7343255332Scy/* packet here, a checksum is calculated that we update later when we       */
7344255332Scy/* fill in the final details.  At present a 0 checksum for UDP is being set */
7345255332Scy/* here because it is expected that divert will be used for localhost.      */
7346255332Scy/* ------------------------------------------------------------------------ */
7347255332Scystatic int
7348255332Scyipf_nat_builddivertmp(softn, np)
7349255332Scy	ipf_nat_softc_t *softn;
7350255332Scy	ipnat_t *np;
7351255332Scy{
7352255332Scy	udphdr_t *uh;
7353255332Scy	size_t len;
7354255332Scy	ip_t *ip;
7355255332Scy
7356255332Scy	if ((np->in_redir & NAT_DIVERTUDP) != 0)
7357255332Scy		len = sizeof(ip_t) + sizeof(udphdr_t);
7358255332Scy	else
7359255332Scy		len = sizeof(ip_t);
7360255332Scy
7361255332Scy	ALLOC_MB_T(np->in_divmp, len);
7362255332Scy	if (np->in_divmp == NULL) {
7363255332Scy		NBUMPD(ipf_nat_stats, ns_divert_build);
7364255332Scy		return -1;
7365255332Scy	}
7366255332Scy
7367255332Scy	/*
7368255332Scy	 * First, the header to get the packet diverted to the new destination
7369255332Scy	 */
7370255332Scy	ip = MTOD(np->in_divmp, ip_t *);
7371255332Scy	IP_V_A(ip, 4);
7372255332Scy	IP_HL_A(ip, 5);
7373255332Scy	ip->ip_tos = 0;
7374255332Scy	if ((np->in_redir & NAT_DIVERTUDP) != 0)
7375255332Scy		ip->ip_p = IPPROTO_UDP;
7376255332Scy	else
7377255332Scy		ip->ip_p = IPPROTO_IPIP;
7378255332Scy	ip->ip_ttl = 255;
7379255332Scy	ip->ip_off = 0;
7380255332Scy	ip->ip_sum = 0;
7381255332Scy	ip->ip_len = htons(len);
7382255332Scy	ip->ip_id = 0;
7383255332Scy	ip->ip_src.s_addr = htonl(np->in_snip);
7384255332Scy	ip->ip_dst.s_addr = htonl(np->in_dnip);
7385255332Scy	ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip));
7386255332Scy
7387255332Scy	if (np->in_redir & NAT_DIVERTUDP) {
7388255332Scy		uh = (udphdr_t *)(ip + 1);
7389255332Scy		uh->uh_sum = 0;
7390255332Scy		uh->uh_ulen = 8;
7391255332Scy		uh->uh_sport = htons(np->in_spnext);
7392255332Scy		uh->uh_dport = htons(np->in_dpnext);
7393255332Scy	}
7394255332Scy
7395255332Scy	return 0;
7396255332Scy}
7397255332Scy
7398255332Scy
7399255332Scy#define	MINDECAP	(sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t))
7400255332Scy
7401255332Scy/* ------------------------------------------------------------------------ */
7402255332Scy/* Function:    nat_decap                                                   */
7403255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7404255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7405255332Scy/*              nat(I) - pointer to current NAT session                     */
7406255332Scy/*                                                                          */
7407255332Scy/* This function is responsible for undoing a packet's encapsulation in the */
7408255332Scy/* reverse of an encap/divert rule.  After removing the outer encapsulation */
7409255332Scy/* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/
7410255332Scy/* match the "new" packet as it may still be used by IPFilter elsewhere.    */
7411255332Scy/* We use "dir" here as the basis for some of the expectations about the    */
7412255332Scy/* outer header.  If we return an error, the goal is to leave the original  */
7413255332Scy/* packet information undisturbed - this falls short at the end where we'd  */
7414255332Scy/* need to back a backup copy of "fin" - expensive.                         */
7415255332Scy/* ------------------------------------------------------------------------ */
7416255332Scystatic int
7417255332Scyipf_nat_decap(fin, nat)
7418255332Scy	fr_info_t *fin;
7419255332Scy	nat_t *nat;
7420255332Scy{
7421255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
7422255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7423255332Scy	char *hdr;
7424255332Scy	int hlen;
7425255332Scy	int skip;
7426255332Scy	mb_t *m;
7427255332Scy
7428255332Scy	if ((fin->fin_flx & FI_ICMPERR) != 0) {
7429255332Scy		/*
7430255332Scy		 * ICMP packets don't get decapsulated, instead what we need
7431255332Scy		 * to do is change the ICMP reply from including (in the data
7432255332Scy		 * portion for errors) the encapsulated packet that we sent
7433255332Scy		 * out to something that resembles the original packet prior
7434255332Scy		 * to encapsulation.  This isn't done here - all we're doing
7435255332Scy		 * here is changing the outer address to ensure that it gets
7436255332Scy		 * targetted back to the correct system.
7437255332Scy		 */
7438255332Scy
7439255332Scy		if (nat->nat_dir & NAT_OUTBOUND) {
7440255332Scy			u_32_t sum1, sum2, sumd;
7441255332Scy
7442255332Scy			sum1 = ntohl(fin->fin_daddr);
7443255332Scy			sum2 = ntohl(nat->nat_osrcaddr);
7444255332Scy			CALC_SUMD(sum1, sum2, sumd);
7445255332Scy			fin->fin_ip->ip_dst = nat->nat_osrcip;
7446255332Scy			fin->fin_daddr = nat->nat_osrcaddr;
7447255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
7448255332Scy     defined(__osf__) || defined(linux)
7449255332Scy			ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0);
7450255332Scy#endif
7451255332Scy		}
7452255332Scy		return 0;
7453255332Scy	}
7454255332Scy
7455255332Scy	m = fin->fin_m;
7456255332Scy	skip = fin->fin_hlen;
7457255332Scy
7458255332Scy	switch (nat->nat_dir)
7459255332Scy	{
7460255332Scy	case NAT_DIVERTIN :
7461255332Scy	case NAT_DIVERTOUT :
7462255332Scy		if (fin->fin_plen < MINDECAP)
7463255332Scy			return -1;
7464255332Scy		skip += sizeof(udphdr_t);
7465255332Scy		break;
7466255332Scy
7467255332Scy	case NAT_ENCAPIN :
7468255332Scy	case NAT_ENCAPOUT :
7469255332Scy		if (fin->fin_plen < (skip + sizeof(ip_t)))
7470255332Scy			return -1;
7471255332Scy		break;
7472255332Scy	default :
7473255332Scy		return -1;
7474255332Scy		/* NOTREACHED */
7475255332Scy	}
7476255332Scy
7477255332Scy	/*
7478255332Scy	 * The aim here is to keep the original packet details in "fin" for
7479255332Scy	 * as long as possible so that returning with an error is for the
7480255332Scy	 * original packet and there is little undoing work to do.
7481255332Scy	 */
7482255332Scy	if (M_LEN(m) < skip + sizeof(ip_t)) {
7483255332Scy		if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1)
7484255332Scy			return -1;
7485255332Scy	}
7486255332Scy
7487255332Scy	hdr = MTOD(fin->fin_m, char *);
7488255332Scy	fin->fin_ip = (ip_t *)(hdr + skip);
7489255332Scy	hlen = IP_HL(fin->fin_ip) << 2;
7490255332Scy
7491255332Scy	if (ipf_pr_pullup(fin, skip + hlen) == -1) {
7492255332Scy		NBUMPSIDED(fin->fin_out, ns_decap_pullup);
7493255332Scy		return -1;
7494255332Scy	}
7495255332Scy
7496255332Scy	fin->fin_hlen = hlen;
7497255332Scy	fin->fin_dlen -= skip;
7498255332Scy	fin->fin_plen -= skip;
7499255332Scy	fin->fin_ipoff += skip;
7500255332Scy
7501255332Scy	if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) {
7502255332Scy		NBUMPSIDED(fin->fin_out, ns_decap_bad);
7503255332Scy		return -1;
7504255332Scy	}
7505255332Scy
7506255332Scy	return skip;
7507255332Scy}
7508255332Scy
7509255332Scy
7510255332Scy/* ------------------------------------------------------------------------ */
7511255332Scy/* Function:    nat_nextaddr                                                */
7512255332Scy/* Returns:     int - -1 == bad input (no new address),                     */
7513255332Scy/*                     0 == success and dst has new address                 */
7514255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7515255332Scy/*              na(I)  - how to generate new address                        */
7516255332Scy/*              old(I) - original address being replaced                    */
7517255332Scy/*              dst(O) - where to put the new address                       */
7518255332Scy/* Write Lock:  ipf_nat                                                     */
7519255332Scy/*                                                                          */
7520255332Scy/* This function uses the contents of the "na" structure, in combination    */
7521255332Scy/* with "old" to produce a new address to store in "dst".  Not all of the   */
7522255332Scy/* possible uses of "na" will result in a new address.                      */
7523255332Scy/* ------------------------------------------------------------------------ */
7524255332Scystatic int
7525255332Scyipf_nat_nextaddr(fin, na, old, dst)
7526255332Scy	fr_info_t *fin;
7527255332Scy	nat_addr_t *na;
7528255332Scy	u_32_t *old, *dst;
7529255332Scy{
7530255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
7531255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7532255332Scy	u_32_t amin, amax, new;
7533255332Scy	i6addr_t newip;
7534255332Scy	int error;
7535255332Scy
7536255332Scy	new = 0;
7537255332Scy	amin = na->na_addr[0].in4.s_addr;
7538255332Scy
7539255332Scy	switch (na->na_atype)
7540255332Scy	{
7541255332Scy	case FRI_RANGE :
7542255332Scy		amax = na->na_addr[1].in4.s_addr;
7543255332Scy		break;
7544255332Scy
7545255332Scy	case FRI_NETMASKED :
7546255332Scy	case FRI_DYNAMIC :
7547255332Scy	case FRI_NORMAL :
7548255332Scy		/*
7549255332Scy		 * Compute the maximum address by adding the inverse of the
7550255332Scy		 * netmask to the minimum address.
7551255332Scy		 */
7552255332Scy		amax = ~na->na_addr[1].in4.s_addr;
7553255332Scy		amax |= amin;
7554255332Scy		break;
7555255332Scy
7556255332Scy	case FRI_LOOKUP :
7557255332Scy		break;
7558255332Scy
7559255332Scy	case FRI_BROADCAST :
7560255332Scy	case FRI_PEERADDR :
7561255332Scy	case FRI_NETWORK :
7562255332Scy	default :
7563255332Scy		return -1;
7564255332Scy	}
7565255332Scy
7566255332Scy	error = -1;
7567255332Scy
7568255332Scy	if (na->na_atype == FRI_LOOKUP) {
7569255332Scy		if (na->na_type == IPLT_DSTLIST) {
7570255332Scy			error = ipf_dstlist_select_node(fin, na->na_ptr, dst,
7571255332Scy							NULL);
7572255332Scy		} else {
7573255332Scy			NBUMPSIDE(fin->fin_out, ns_badnextaddr);
7574255332Scy		}
7575255332Scy
7576255332Scy	} else if (na->na_atype == IPLT_NONE) {
7577255332Scy		/*
7578255332Scy		 * 0/0 as the new address means leave it alone.
7579255332Scy		 */
7580255332Scy		if (na->na_addr[0].in4.s_addr == 0 &&
7581255332Scy		    na->na_addr[1].in4.s_addr == 0) {
7582255332Scy			new = *old;
7583255332Scy
7584255332Scy		/*
7585255332Scy		 * 0/32 means get the interface's address
7586255332Scy		 */
7587255332Scy		} else if (na->na_addr[0].in4.s_addr == 0 &&
7588255332Scy			   na->na_addr[1].in4.s_addr == 0xffffffff) {
7589255332Scy			if (ipf_ifpaddr(softc, 4, na->na_atype,
7590255332Scy					fin->fin_ifp, &newip, NULL) == -1) {
7591255332Scy				NBUMPSIDED(fin->fin_out, ns_ifpaddrfail);
7592255332Scy				return -1;
7593255332Scy			}
7594255332Scy			new = newip.in4.s_addr;
7595255332Scy		} else {
7596255332Scy			new = htonl(na->na_nextip);
7597255332Scy		}
7598255332Scy		*dst = new;
7599255332Scy		error = 0;
7600255332Scy
7601255332Scy	} else {
7602255332Scy		NBUMPSIDE(fin->fin_out, ns_badnextaddr);
7603255332Scy	}
7604255332Scy
7605255332Scy	return error;
7606255332Scy}
7607255332Scy
7608255332Scy
7609255332Scy/* ------------------------------------------------------------------------ */
7610255332Scy/* Function:    nat_nextaddrinit                                            */
7611255332Scy/* Returns:     int - 0 == success, else error number                       */
7612255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7613255332Scy/*              na(I)      - NAT address information for generating new addr*/
7614255332Scy/*              initial(I) - flag indicating if it is the first call for    */
7615255332Scy/*                           this "na" structure.                           */
7616255332Scy/*              ifp(I)     - network interface to derive address            */
7617255332Scy/*                           information from.                              */
7618255332Scy/*                                                                          */
7619255332Scy/* This function is expected to be called in two scenarious: when a new NAT */
7620255332Scy/* rule is loaded into the kernel and when the list of NAT rules is sync'd  */
7621255332Scy/* up with the valid network interfaces (possibly due to them changing.)    */
7622255332Scy/* To distinguish between these, the "initial" parameter is used.  If it is */
7623255332Scy/* 1 then this indicates the rule has just been reloaded and 0 for when we  */
7624255332Scy/* are updating information.  This difference is important because in       */
7625255332Scy/* instances where we are not updating address information associated with  */
7626255332Scy/* a network interface, we don't want to disturb what the "next" address to */
7627255332Scy/* come out of ipf_nat_nextaddr() will be.                                  */
7628255332Scy/* ------------------------------------------------------------------------ */
7629255332Scystatic int
7630255332Scyipf_nat_nextaddrinit(softc, base, na, initial, ifp)
7631255332Scy	ipf_main_softc_t *softc;
7632255332Scy	char *base;
7633255332Scy	nat_addr_t *na;
7634255332Scy	int initial;
7635255332Scy	void *ifp;
7636255332Scy{
7637255332Scy
7638255332Scy	switch (na->na_atype)
7639255332Scy	{
7640255332Scy	case FRI_LOOKUP :
7641255332Scy		if (na->na_subtype == 0) {
7642255332Scy			na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT,
7643255332Scy							na->na_type,
7644255332Scy							na->na_num,
7645255332Scy							&na->na_func);
7646255332Scy		} else if (na->na_subtype == 1) {
7647255332Scy			na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT,
7648255332Scy							 na->na_type,
7649255332Scy							 base + na->na_num,
7650255332Scy							 &na->na_func);
7651255332Scy		}
7652255332Scy		if (na->na_func == NULL) {
7653255332Scy			IPFERROR(60060);
7654255332Scy			return ESRCH;
7655255332Scy		}
7656255332Scy		if (na->na_ptr == NULL) {
7657255332Scy			IPFERROR(60056);
7658255332Scy			return ESRCH;
7659255332Scy		}
7660255332Scy		break;
7661255332Scy
7662255332Scy	case FRI_DYNAMIC :
7663255332Scy	case FRI_BROADCAST :
7664255332Scy	case FRI_NETWORK :
7665255332Scy	case FRI_NETMASKED :
7666255332Scy	case FRI_PEERADDR :
7667255332Scy		if (ifp != NULL)
7668255332Scy			(void )ipf_ifpaddr(softc, 4, na->na_atype, ifp,
7669255332Scy					   &na->na_addr[0], &na->na_addr[1]);
7670255332Scy		break;
7671255332Scy
7672255332Scy	case FRI_SPLIT :
7673255332Scy	case FRI_RANGE :
7674255332Scy		if (initial)
7675255332Scy			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr);
7676255332Scy		break;
7677255332Scy
7678255332Scy	case FRI_NONE :
7679255332Scy		na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr;
7680255332Scy		return 0;
7681255332Scy
7682255332Scy	case FRI_NORMAL :
7683255332Scy		na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr;
7684255332Scy		break;
7685255332Scy
7686255332Scy	default :
7687255332Scy		IPFERROR(60054);
7688255332Scy		return EINVAL;
7689255332Scy	}
7690255332Scy
7691255332Scy	if (initial && (na->na_atype == FRI_NORMAL)) {
7692255332Scy		if (na->na_addr[0].in4.s_addr == 0) {
7693255332Scy			if ((na->na_addr[1].in4.s_addr == 0xffffffff) ||
7694255332Scy			    (na->na_addr[1].in4.s_addr == 0)) {
7695255332Scy				return 0;
7696255332Scy			}
7697255332Scy		}
7698255332Scy
7699255332Scy		if (na->na_addr[1].in4.s_addr == 0xffffffff) {
7700255332Scy			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr);
7701255332Scy		} else {
7702255332Scy			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1;
7703255332Scy		}
7704255332Scy	}
7705255332Scy
7706255332Scy	return 0;
7707255332Scy}
7708255332Scy
7709255332Scy
7710255332Scy/* ------------------------------------------------------------------------ */
7711255332Scy/* Function:    ipf_nat_matchflush                                          */
7712255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7713255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7714255332Scy/*              softn(I) - pointer to NAT context structure                 */
7715255332Scy/*              nat(I)   - pointer to current NAT session                   */
7716255332Scy/*                                                                          */
7717255332Scy/* ------------------------------------------------------------------------ */
7718255332Scystatic int
7719255332Scyipf_nat_matchflush(softc, softn, data)
7720255332Scy	ipf_main_softc_t *softc;
7721255332Scy	ipf_nat_softc_t *softn;
7722255332Scy	caddr_t data;
7723255332Scy{
7724255332Scy	int *array, flushed, error;
7725255332Scy	nat_t *nat, *natnext;
7726255332Scy	ipfobj_t obj;
7727255332Scy
7728255332Scy	error = ipf_matcharray_load(softc, data, &obj, &array);
7729255332Scy	if (error != 0)
7730255332Scy		return error;
7731255332Scy
7732255332Scy	flushed = 0;
7733255332Scy
7734255332Scy	for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) {
7735255332Scy		natnext = nat->nat_next;
7736255332Scy		if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) {
7737255332Scy			ipf_nat_delete(softc, nat, NL_FLUSH);
7738255332Scy			flushed++;
7739255332Scy		}
7740255332Scy	}
7741255332Scy
7742255332Scy	obj.ipfo_retval = flushed;
7743255332Scy	error = BCOPYOUT(&obj, data, sizeof(obj));
7744255332Scy
7745255332Scy	KFREES(array, array[0] * sizeof(*array));
7746255332Scy
7747255332Scy	return error;
7748255332Scy}
7749255332Scy
7750255332Scy
7751255332Scy/* ------------------------------------------------------------------------ */
7752255332Scy/* Function:    ipf_nat_matcharray                                          */
7753255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7754255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7755255332Scy/*              nat(I) - pointer to current NAT session                     */
7756255332Scy/*                                                                          */
7757255332Scy/* ------------------------------------------------------------------------ */
7758255332Scystatic int
7759255332Scyipf_nat_matcharray(nat, array, ticks)
7760255332Scy	nat_t *nat;
7761255332Scy	int *array;
7762255332Scy	u_long ticks;
7763255332Scy{
7764255332Scy	int i, n, *x, e, p;
7765255332Scy
7766255332Scy	e = 0;
7767255332Scy	n = array[0];
7768255332Scy	x = array + 1;
7769255332Scy
7770255332Scy	for (; n > 0; x += 3 + x[2]) {
7771255332Scy		if (x[0] == IPF_EXP_END)
7772255332Scy			break;
7773255332Scy		e = 0;
7774255332Scy
7775255332Scy		n -= x[2] + 3;
7776255332Scy		if (n < 0)
7777255332Scy			break;
7778255332Scy
7779255332Scy		p = x[0] >> 16;
7780255332Scy		if (p != 0 && p != nat->nat_pr[1])
7781255332Scy			break;
7782255332Scy
7783255332Scy		switch (x[0])
7784255332Scy		{
7785255332Scy		case IPF_EXP_IP_PR :
7786255332Scy			for (i = 0; !e && i < x[2]; i++) {
7787255332Scy				e |= (nat->nat_pr[1] == x[i + 3]);
7788255332Scy			}
7789255332Scy			break;
7790255332Scy
7791255332Scy		case IPF_EXP_IP_SRCADDR :
7792255332Scy			if (nat->nat_v[0] == 4) {
7793255332Scy				for (i = 0; !e && i < x[2]; i++) {
7794255332Scy					e |= ((nat->nat_osrcaddr & x[i + 4]) ==
7795255332Scy					      x[i + 3]);
7796255332Scy				}
7797255332Scy			}
7798255332Scy			if (nat->nat_v[1] == 4) {
7799255332Scy				for (i = 0; !e && i < x[2]; i++) {
7800255332Scy					e |= ((nat->nat_nsrcaddr & x[i + 4]) ==
7801255332Scy					      x[i + 3]);
7802255332Scy				}
7803255332Scy			}
7804255332Scy			break;
7805255332Scy
7806255332Scy		case IPF_EXP_IP_DSTADDR :
7807255332Scy			if (nat->nat_v[0] == 4) {
7808255332Scy				for (i = 0; !e && i < x[2]; i++) {
7809255332Scy					e |= ((nat->nat_odstaddr & x[i + 4]) ==
7810255332Scy					      x[i + 3]);
7811255332Scy				}
7812255332Scy			}
7813255332Scy			if (nat->nat_v[1] == 4) {
7814255332Scy				for (i = 0; !e && i < x[2]; i++) {
7815255332Scy					e |= ((nat->nat_ndstaddr & x[i + 4]) ==
7816255332Scy					      x[i + 3]);
7817255332Scy				}
7818255332Scy			}
7819255332Scy			break;
7820255332Scy
7821255332Scy		case IPF_EXP_IP_ADDR :
7822255332Scy			for (i = 0; !e && i < x[2]; i++) {
7823255332Scy				if (nat->nat_v[0] == 4) {
7824255332Scy					e |= ((nat->nat_osrcaddr & x[i + 4]) ==
7825255332Scy					      x[i + 3]);
7826255332Scy				}
7827255332Scy				if (nat->nat_v[1] == 4) {
7828255332Scy					e |= ((nat->nat_nsrcaddr & x[i + 4]) ==
7829255332Scy					      x[i + 3]);
7830255332Scy				}
7831255332Scy				if (nat->nat_v[0] == 4) {
7832255332Scy					e |= ((nat->nat_odstaddr & x[i + 4]) ==
7833255332Scy					      x[i + 3]);
7834255332Scy				}
7835255332Scy				if (nat->nat_v[1] == 4) {
7836255332Scy					e |= ((nat->nat_ndstaddr & x[i + 4]) ==
7837255332Scy					      x[i + 3]);
7838255332Scy				}
7839255332Scy			}
7840255332Scy			break;
7841255332Scy
7842255332Scy#ifdef USE_INET6
7843255332Scy		case IPF_EXP_IP6_SRCADDR :
7844255332Scy			if (nat->nat_v[0] == 6) {
7845255332Scy				for (i = 0; !e && i < x[3]; i++) {
7846255332Scy					e |= IP6_MASKEQ(&nat->nat_osrc6,
7847255332Scy							x + i + 7, x + i + 3);
7848255332Scy				}
7849255332Scy			}
7850255332Scy			if (nat->nat_v[1] == 6) {
7851255332Scy				for (i = 0; !e && i < x[3]; i++) {
7852255332Scy					e |= IP6_MASKEQ(&nat->nat_nsrc6,
7853255332Scy							x + i + 7, x + i + 3);
7854255332Scy				}
7855255332Scy			}
7856255332Scy			break;
7857255332Scy
7858255332Scy		case IPF_EXP_IP6_DSTADDR :
7859255332Scy			if (nat->nat_v[0] == 6) {
7860255332Scy				for (i = 0; !e && i < x[3]; i++) {
7861255332Scy					e |= IP6_MASKEQ(&nat->nat_odst6,
7862255332Scy							x + i + 7,
7863255332Scy							x + i + 3);
7864255332Scy				}
7865255332Scy			}
7866255332Scy			if (nat->nat_v[1] == 6) {
7867255332Scy				for (i = 0; !e && i < x[3]; i++) {
7868255332Scy					e |= IP6_MASKEQ(&nat->nat_ndst6,
7869255332Scy							x + i + 7,
7870255332Scy							x + i + 3);
7871255332Scy				}
7872255332Scy			}
7873255332Scy			break;
7874255332Scy
7875255332Scy		case IPF_EXP_IP6_ADDR :
7876255332Scy			for (i = 0; !e && i < x[3]; i++) {
7877255332Scy				if (nat->nat_v[0] == 6) {
7878255332Scy					e |= IP6_MASKEQ(&nat->nat_osrc6,
7879255332Scy							x + i + 7,
7880255332Scy							x + i + 3);
7881255332Scy				}
7882255332Scy				if (nat->nat_v[0] == 6) {
7883255332Scy					e |= IP6_MASKEQ(&nat->nat_odst6,
7884255332Scy							x + i + 7,
7885255332Scy							x + i + 3);
7886255332Scy				}
7887255332Scy				if (nat->nat_v[1] == 6) {
7888255332Scy					e |= IP6_MASKEQ(&nat->nat_nsrc6,
7889255332Scy							x + i + 7,
7890255332Scy							x + i + 3);
7891255332Scy				}
7892255332Scy				if (nat->nat_v[1] == 6) {
7893255332Scy					e |= IP6_MASKEQ(&nat->nat_ndst6,
7894255332Scy							x + i + 7,
7895255332Scy							x + i + 3);
7896255332Scy				}
7897255332Scy			}
7898255332Scy			break;
7899255332Scy#endif
7900255332Scy
7901255332Scy		case IPF_EXP_UDP_PORT :
7902255332Scy		case IPF_EXP_TCP_PORT :
7903255332Scy			for (i = 0; !e && i < x[2]; i++) {
7904255332Scy				e |= (nat->nat_nsport == x[i + 3]) ||
7905255332Scy				     (nat->nat_ndport == x[i + 3]);
7906255332Scy			}
7907255332Scy			break;
7908255332Scy
7909255332Scy		case IPF_EXP_UDP_SPORT :
7910255332Scy		case IPF_EXP_TCP_SPORT :
7911255332Scy			for (i = 0; !e && i < x[2]; i++) {
7912255332Scy				e |= (nat->nat_nsport == x[i + 3]);
7913255332Scy			}
7914255332Scy			break;
7915255332Scy
7916255332Scy		case IPF_EXP_UDP_DPORT :
7917255332Scy		case IPF_EXP_TCP_DPORT :
7918255332Scy			for (i = 0; !e && i < x[2]; i++) {
7919255332Scy				e |= (nat->nat_ndport == x[i + 3]);
7920255332Scy			}
7921255332Scy			break;
7922255332Scy
7923255332Scy		case IPF_EXP_TCP_STATE :
7924255332Scy			for (i = 0; !e && i < x[2]; i++) {
7925255332Scy				e |= (nat->nat_tcpstate[0] == x[i + 3]) ||
7926255332Scy				     (nat->nat_tcpstate[1] == x[i + 3]);
7927255332Scy			}
7928255332Scy			break;
7929255332Scy
7930255332Scy		case IPF_EXP_IDLE_GT :
7931255332Scy			e |= (ticks - nat->nat_touched > x[3]);
7932255332Scy			break;
7933255332Scy		}
7934255332Scy		e ^= x[1];
7935255332Scy
7936255332Scy		if (!e)
7937255332Scy			break;
7938255332Scy	}
7939255332Scy
7940255332Scy	return e;
7941255332Scy}
7942255332Scy
7943255332Scy
7944255332Scy/* ------------------------------------------------------------------------ */
7945255332Scy/* Function:    ipf_nat_gettable                                            */
7946172776Sdarrenr/* Returns:     int     - 0 = success, else error                           */
7947255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7948255332Scy/*              softn(I) - pointer to NAT context structure                 */
7949255332Scy/*              data(I)  - pointer to ioctl data                            */
7950172776Sdarrenr/*                                                                          */
7951172776Sdarrenr/* This function handles ioctl requests for tables of nat information.      */
7952172776Sdarrenr/* At present the only table it deals with is the hash bucket statistics.   */
7953172776Sdarrenr/* ------------------------------------------------------------------------ */
7954255332Scystatic int
7955255332Scyipf_nat_gettable(softc, softn, data)
7956255332Scy	ipf_main_softc_t *softc;
7957255332Scy	ipf_nat_softc_t *softn;
7958255332Scy	char *data;
7959172776Sdarrenr{
7960172776Sdarrenr	ipftable_t table;
7961172776Sdarrenr	int error;
7962172776Sdarrenr
7963255332Scy	error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE);
7964172776Sdarrenr	if (error != 0)
7965172776Sdarrenr		return error;
7966172776Sdarrenr
7967172776Sdarrenr	switch (table.ita_type)
7968172776Sdarrenr	{
7969172776Sdarrenr	case IPFTABLE_BUCKETS_NATIN :
7970255332Scy		error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
7971255332Scy				table.ita_table,
7972255332Scy				softn->ipf_nat_table_sz * sizeof(u_int));
7973172776Sdarrenr		break;
7974172776Sdarrenr
7975172776Sdarrenr	case IPFTABLE_BUCKETS_NATOUT :
7976255332Scy		error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
7977255332Scy				table.ita_table,
7978255332Scy				softn->ipf_nat_table_sz * sizeof(u_int));
7979172776Sdarrenr		break;
7980172776Sdarrenr
7981172776Sdarrenr	default :
7982255332Scy		IPFERROR(60058);
7983172776Sdarrenr		return EINVAL;
7984172776Sdarrenr	}
7985172776Sdarrenr
7986172776Sdarrenr	if (error != 0) {
7987255332Scy		IPFERROR(60059);
7988172776Sdarrenr		error = EFAULT;
7989172776Sdarrenr	}
7990172776Sdarrenr	return error;
7991172776Sdarrenr}
7992255332Scy
7993255332Scy
7994255332Scy/* ------------------------------------------------------------------------ */
7995255332Scy/* Function:    ipf_nat_settimeout                                          */
7996255332Scy/* Returns:     int  - 0 = success, else failure			    */
7997255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7998255332Scy/*              t(I) - pointer to tunable                                   */
7999255332Scy/*              p(I) - pointer to new tuning data                           */
8000255332Scy/*                                                                          */
8001255332Scy/* Apply the timeout change to the NAT timeout queues.                      */
8002255332Scy/* ------------------------------------------------------------------------ */
8003255332Scyint
8004255332Scyipf_nat_settimeout(softc, t, p)
8005255332Scy	struct ipf_main_softc_s *softc;
8006255332Scy	ipftuneable_t *t;
8007255332Scy	ipftuneval_t *p;
8008255332Scy{
8009255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8010255332Scy
8011255332Scy	if (!strncmp(t->ipft_name, "tcp_", 4))
8012255332Scy		return ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq);
8013255332Scy
8014255332Scy	if (!strcmp(t->ipft_name, "udp_timeout")) {
8015255332Scy		ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int);
8016255332Scy	} else if (!strcmp(t->ipft_name, "udp_ack_timeout")) {
8017255332Scy		ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int);
8018255332Scy	} else if (!strcmp(t->ipft_name, "icmp_timeout")) {
8019255332Scy		ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int);
8020255332Scy	} else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) {
8021255332Scy		ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int);
8022255332Scy	} else if (!strcmp(t->ipft_name, "ip_timeout")) {
8023255332Scy		ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int);
8024255332Scy	} else {
8025255332Scy		IPFERROR(60062);
8026255332Scy		return ESRCH;
8027255332Scy	}
8028255332Scy	return 0;
8029255332Scy}
8030255332Scy
8031255332Scy
8032255332Scy/* ------------------------------------------------------------------------ */
8033255332Scy/* Function:    ipf_nat_rehash                                              */
8034255332Scy/* Returns:     int  - 0 = success, else failure			    */
8035255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8036255332Scy/*              t(I) - pointer to tunable                                   */
8037255332Scy/*              p(I) - pointer to new tuning data                           */
8038255332Scy/*                                                                          */
8039255332Scy/* To change the size of the basic NAT table, we need to first allocate the */
8040255332Scy/* new tables (lest it fails and we've got nowhere to store all of the NAT  */
8041255332Scy/* sessions currently active) and then walk through the entire list and     */
8042255332Scy/* insert them into the table.  There are two tables here: an inbound one   */
8043255332Scy/* and an outbound one.  Each NAT entry goes into each table once.          */
8044255332Scy/* ------------------------------------------------------------------------ */
8045255332Scyint
8046255332Scyipf_nat_rehash(softc, t, p)
8047255332Scy	ipf_main_softc_t *softc;
8048255332Scy	ipftuneable_t *t;
8049255332Scy	ipftuneval_t *p;
8050255332Scy{
8051255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8052255332Scy	nat_t **newtab[2], *nat, **natp;
8053255332Scy	u_int *bucketlens[2];
8054255332Scy	u_int maxbucket;
8055255332Scy	u_int newsize;
8056255332Scy	int error;
8057255332Scy	u_int hv;
8058255332Scy	int i;
8059255332Scy
8060255332Scy	newsize = p->ipftu_int;
8061255332Scy	/*
8062255332Scy	 * In case there is nothing to do...
8063255332Scy	 */
8064255332Scy	if (newsize == softn->ipf_nat_table_sz)
8065255332Scy		return 0;
8066255332Scy
8067255332Scy	newtab[0] = NULL;
8068255332Scy	newtab[1] = NULL;
8069255332Scy	bucketlens[0] = NULL;
8070255332Scy	bucketlens[1] = NULL;
8071255332Scy	/*
8072255332Scy	 * 4 tables depend on the NAT table size: the inbound looking table,
8073255332Scy	 * the outbound lookup table and the hash chain length for each.
8074255332Scy	 */
8075255332Scy	KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *));
8076255332Scy	if (newtab == NULL) {
8077255332Scy		error = 60063;
8078255332Scy		goto badrehash;
8079255332Scy	}
8080255332Scy
8081255332Scy	KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *));
8082255332Scy	if (newtab == NULL) {
8083255332Scy		error = 60064;
8084255332Scy		goto badrehash;
8085255332Scy	}
8086255332Scy
8087255332Scy	KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int));
8088255332Scy	if (bucketlens[0] == NULL) {
8089255332Scy		error = 60065;
8090255332Scy		goto badrehash;
8091255332Scy	}
8092255332Scy
8093255332Scy	KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int));
8094255332Scy	if (bucketlens[1] == NULL) {
8095255332Scy		error = 60066;
8096255332Scy		goto badrehash;
8097255332Scy	}
8098255332Scy
8099255332Scy	/*
8100255332Scy	 * Recalculate the maximum length based on the new size.
8101255332Scy	 */
8102255332Scy	for (maxbucket = 0, i = newsize; i > 0; i >>= 1)
8103255332Scy		maxbucket++;
8104255332Scy	maxbucket *= 2;
8105255332Scy
8106255332Scy	bzero((char *)newtab[0], newsize * sizeof(nat_t *));
8107255332Scy	bzero((char *)newtab[1], newsize * sizeof(nat_t *));
8108255332Scy	bzero((char *)bucketlens[0], newsize * sizeof(u_int));
8109255332Scy	bzero((char *)bucketlens[1], newsize * sizeof(u_int));
8110255332Scy
8111255332Scy	WRITE_ENTER(&softc->ipf_nat);
8112255332Scy
8113255332Scy	if (softn->ipf_nat_table[0] != NULL) {
8114255332Scy		KFREES(softn->ipf_nat_table[0],
8115255332Scy		       softn->ipf_nat_table_sz *
8116255332Scy		       sizeof(*softn->ipf_nat_table[0]));
8117255332Scy	}
8118255332Scy	softn->ipf_nat_table[0] = newtab[0];
8119255332Scy
8120255332Scy	if (softn->ipf_nat_table[1] != NULL) {
8121255332Scy		KFREES(softn->ipf_nat_table[1],
8122255332Scy		       softn->ipf_nat_table_sz *
8123255332Scy		       sizeof(*softn->ipf_nat_table[1]));
8124255332Scy	}
8125255332Scy	softn->ipf_nat_table[1] = newtab[1];
8126255332Scy
8127255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) {
8128255332Scy		KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
8129255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8130255332Scy	}
8131255332Scy	softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0];
8132255332Scy
8133255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) {
8134255332Scy		KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
8135255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8136255332Scy	}
8137255332Scy	softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1];
8138255332Scy
8139255332Scy#ifdef USE_INET6
8140255332Scy	if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) {
8141255332Scy		KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen,
8142255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8143255332Scy	}
8144255332Scy	softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0];
8145255332Scy
8146255332Scy	if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) {
8147255332Scy		KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen,
8148255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8149255332Scy	}
8150255332Scy	softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1];
8151255332Scy#endif
8152255332Scy
8153255332Scy	softn->ipf_nat_maxbucket = maxbucket;
8154255332Scy	softn->ipf_nat_table_sz = newsize;
8155255332Scy	/*
8156255332Scy	 * Walk through the entire list of NAT table entries and put them
8157255332Scy	 * in the new NAT table, somewhere.  Because we have a new table,
8158255332Scy	 * we need to restart the counter of how many chains are in use.
8159255332Scy	 */
8160255332Scy	softn->ipf_nat_stats.ns_side[0].ns_inuse = 0;
8161255332Scy	softn->ipf_nat_stats.ns_side[1].ns_inuse = 0;
8162255332Scy#ifdef USE_INET6
8163255332Scy	softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0;
8164255332Scy	softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0;
8165255332Scy#endif
8166255332Scy
8167255332Scy	for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) {
8168255332Scy		nat->nat_hnext[0] = NULL;
8169255332Scy		nat->nat_phnext[0] = NULL;
8170255332Scy		hv = nat->nat_hv[0] % softn->ipf_nat_table_sz;
8171255332Scy
8172255332Scy		natp = &softn->ipf_nat_table[0][hv];
8173255332Scy		if (*natp) {
8174255332Scy			(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
8175255332Scy		} else {
8176255332Scy			NBUMPSIDE(0, ns_inuse);
8177255332Scy		}
8178255332Scy		nat->nat_phnext[0] = natp;
8179255332Scy		nat->nat_hnext[0] = *natp;
8180255332Scy		*natp = nat;
8181255332Scy		NBUMPSIDE(0, ns_bucketlen[hv]);
8182255332Scy
8183255332Scy		nat->nat_hnext[1] = NULL;
8184255332Scy		nat->nat_phnext[1] = NULL;
8185255332Scy		hv = nat->nat_hv[1] % softn->ipf_nat_table_sz;
8186255332Scy
8187255332Scy		natp = &softn->ipf_nat_table[1][hv];
8188255332Scy		if (*natp) {
8189255332Scy			(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
8190255332Scy		} else {
8191255332Scy			NBUMPSIDE(1, ns_inuse);
8192255332Scy		}
8193255332Scy		nat->nat_phnext[1] = natp;
8194255332Scy		nat->nat_hnext[1] = *natp;
8195255332Scy		*natp = nat;
8196255332Scy		NBUMPSIDE(1, ns_bucketlen[hv]);
8197255332Scy	}
8198255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8199255332Scy
8200255332Scy	return 0;
8201255332Scy
8202255332Scybadrehash:
8203255332Scy	if (bucketlens[1] != NULL) {
8204255332Scy		KFREES(bucketlens[0], newsize * sizeof(u_int));
8205255332Scy	}
8206255332Scy	if (bucketlens[0] != NULL) {
8207255332Scy		KFREES(bucketlens[0], newsize * sizeof(u_int));
8208255332Scy	}
8209255332Scy	if (newtab[0] != NULL) {
8210255332Scy		KFREES(newtab[0], newsize * sizeof(nat_t *));
8211255332Scy	}
8212255332Scy	if (newtab[1] != NULL) {
8213255332Scy		KFREES(newtab[1], newsize * sizeof(nat_t *));
8214255332Scy	}
8215255332Scy	IPFERROR(error);
8216255332Scy	return ENOMEM;
8217255332Scy}
8218255332Scy
8219255332Scy
8220255332Scy/* ------------------------------------------------------------------------ */
8221255332Scy/* Function:    ipf_nat_rehash_rules                                        */
8222255332Scy/* Returns:     int  - 0 = success, else failure			    */
8223255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8224255332Scy/*              t(I) - pointer to tunable                                   */
8225255332Scy/*              p(I) - pointer to new tuning data                           */
8226255332Scy/*                                                                          */
8227255332Scy/* All of the NAT rules hang off of a hash table that is searched with a    */
8228255332Scy/* hash on address after the netmask is applied.  There is a different table*/
8229255332Scy/* for both inbound rules (rdr) and outbound (map.)  The resizing will only */
8230255332Scy/* affect one of these two tables.                                          */
8231255332Scy/* ------------------------------------------------------------------------ */
8232255332Scyint
8233255332Scyipf_nat_rehash_rules(softc, t, p)
8234255332Scy	ipf_main_softc_t *softc;
8235255332Scy	ipftuneable_t *t;
8236255332Scy	ipftuneval_t *p;
8237255332Scy{
8238255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8239255332Scy	ipnat_t **newtab, *np, ***old, **npp;
8240255332Scy	u_int newsize;
8241255332Scy	u_int mask;
8242255332Scy	u_int hv;
8243255332Scy
8244255332Scy	newsize = p->ipftu_int;
8245255332Scy	/*
8246255332Scy	 * In case there is nothing to do...
8247255332Scy	 */
8248255332Scy	if (newsize == *t->ipft_pint)
8249255332Scy		return 0;
8250255332Scy
8251255332Scy	/*
8252255332Scy	 * All inbound rules have the NAT_REDIRECT bit set in in_redir and
8253255332Scy	 * all outbound rules have either NAT_MAP or MAT_MAPBLK set.
8254255332Scy	 * This if statement allows for some more generic code to be below,
8255255332Scy	 * rather than two huge gobs of code that almost do the same thing.
8256255332Scy	 */
8257255332Scy	if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) {
8258255332Scy		old = &softn->ipf_nat_rdr_rules;
8259255332Scy		mask = NAT_REDIRECT;
8260255332Scy	} else {
8261255332Scy		old = &softn->ipf_nat_map_rules;
8262255332Scy		mask = NAT_MAP|NAT_MAPBLK;
8263255332Scy	}
8264255332Scy
8265255332Scy	KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *));
8266255332Scy	if (newtab == NULL) {
8267255332Scy		IPFERROR(60067);
8268255332Scy		return ENOMEM;
8269255332Scy	}
8270255332Scy
8271255332Scy	bzero((char *)newtab, newsize * sizeof(ipnat_t *));
8272255332Scy
8273255332Scy	WRITE_ENTER(&softc->ipf_nat);
8274255332Scy
8275255332Scy	if (*old != NULL) {
8276255332Scy		KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **));
8277255332Scy	}
8278255332Scy	*old = newtab;
8279255332Scy	*t->ipft_pint = newsize;
8280255332Scy
8281255332Scy	for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) {
8282255332Scy		if ((np->in_redir & mask) == 0)
8283255332Scy			continue;
8284255332Scy
8285255332Scy		if (np->in_redir & NAT_REDIRECT) {
8286255332Scy			np->in_rnext = NULL;
8287255332Scy			hv = np->in_hv[0] % newsize;
8288255332Scy			for (npp = newtab + hv; *npp != NULL; )
8289255332Scy				npp = &(*npp)->in_rnext;
8290255332Scy			np->in_prnext = npp;
8291255332Scy			*npp = np;
8292255332Scy		}
8293255332Scy		if (np->in_redir & NAT_MAP) {
8294255332Scy			np->in_mnext = NULL;
8295255332Scy			hv = np->in_hv[1] % newsize;
8296255332Scy			for (npp = newtab + hv; *npp != NULL; )
8297255332Scy				npp = &(*npp)->in_mnext;
8298255332Scy			np->in_pmnext = npp;
8299255332Scy			*npp = np;
8300255332Scy		}
8301255332Scy
8302255332Scy	}
8303255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8304255332Scy
8305255332Scy	return 0;
8306255332Scy}
8307255332Scy
8308255332Scy
8309255332Scy/* ------------------------------------------------------------------------ */
8310255332Scy/* Function:    ipf_nat_hostmap_rehash                                      */
8311255332Scy/* Returns:     int  - 0 = success, else failure			    */
8312255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8313255332Scy/*              t(I) - pointer to tunable                                   */
8314255332Scy/*              p(I) - pointer to new tuning data                           */
8315255332Scy/*                                                                          */
8316255332Scy/* Allocate and populate a new hash table that will contain a reference to  */
8317255332Scy/* all of the active IP# translations currently in place.                   */
8318255332Scy/* ------------------------------------------------------------------------ */
8319255332Scyint
8320255332Scyipf_nat_hostmap_rehash(softc, t, p)
8321255332Scy	ipf_main_softc_t *softc;
8322255332Scy	ipftuneable_t *t;
8323255332Scy	ipftuneval_t *p;
8324255332Scy{
8325255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8326255332Scy	hostmap_t *hm, **newtab;
8327255332Scy	u_int newsize;
8328255332Scy	u_int hv;
8329255332Scy
8330255332Scy	newsize = p->ipftu_int;
8331255332Scy	/*
8332255332Scy	 * In case there is nothing to do...
8333255332Scy	 */
8334255332Scy	if (newsize == *t->ipft_pint)
8335255332Scy		return 0;
8336255332Scy
8337255332Scy	KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *));
8338255332Scy	if (newtab == NULL) {
8339255332Scy		IPFERROR(60068);
8340255332Scy		return ENOMEM;
8341255332Scy	}
8342255332Scy
8343255332Scy	bzero((char *)newtab, newsize * sizeof(hostmap_t *));
8344255332Scy
8345255332Scy	WRITE_ENTER(&softc->ipf_nat);
8346255332Scy	if (softn->ipf_hm_maptable != NULL) {
8347255332Scy		KFREES(softn->ipf_hm_maptable,
8348255332Scy		       softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *));
8349255332Scy	}
8350255332Scy	softn->ipf_hm_maptable = newtab;
8351255332Scy	softn->ipf_nat_hostmap_sz = newsize;
8352255332Scy
8353255332Scy	for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) {
8354255332Scy		hv = hm->hm_hv % softn->ipf_nat_hostmap_sz;
8355255332Scy		hm->hm_hnext = softn->ipf_hm_maptable[hv];
8356255332Scy		hm->hm_phnext = softn->ipf_hm_maptable + hv;
8357255332Scy		if (softn->ipf_hm_maptable[hv] != NULL)
8358255332Scy			softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext;
8359255332Scy		softn->ipf_hm_maptable[hv] = hm;
8360255332Scy	}
8361255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8362255332Scy
8363255332Scy	return 0;
8364255332Scy}
8365255332Scy
8366255332Scy
8367255332Scy/* ------------------------------------------------------------------------ */
8368255332Scy/* Function:    ipf_nat_add_tq                                              */
8369255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8370255332Scy/*                                                                          */
8371255332Scy/* ------------------------------------------------------------------------ */
8372255332Scyipftq_t *
8373255332Scyipf_nat_add_tq(softc, ttl)
8374255332Scy	ipf_main_softc_t *softc;
8375255332Scy	int ttl;
8376255332Scy{
8377255332Scy	ipf_nat_softc_t *softs = softc->ipf_nat_soft;
8378255332Scy
8379255332Scy	return ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl);
8380255332Scy}
8381255332Scy
8382255332Scy/* ------------------------------------------------------------------------ */
8383255332Scy/* Function:    ipf_nat_uncreate                                            */
8384255332Scy/* Returns:     Nil                                                         */
8385255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
8386255332Scy/*                                                                          */
8387255332Scy/* This function is used to remove a NAT entry from the NAT table when we   */
8388255332Scy/* decide that the create was actually in error. It is thus assumed that    */
8389255332Scy/* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */
8390255332Scy/* with the translated packet (not the original), we have to reverse the    */
8391255332Scy/* lookup. Although doing the lookup is expensive (relatively speaking), it */
8392255332Scy/* is not anticipated that this will be a frequent occurance for normal     */
8393255332Scy/* traffic patterns.                                                        */
8394255332Scy/* ------------------------------------------------------------------------ */
8395255332Scyvoid
8396255332Scyipf_nat_uncreate(fin)
8397255332Scy	fr_info_t *fin;
8398255332Scy{
8399255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
8400255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8401255332Scy	int nflags;
8402255332Scy	nat_t *nat;
8403255332Scy
8404255332Scy	switch (fin->fin_p)
8405255332Scy	{
8406255332Scy	case IPPROTO_TCP :
8407255332Scy		nflags = IPN_TCP;
8408255332Scy		break;
8409255332Scy	case IPPROTO_UDP :
8410255332Scy		nflags = IPN_UDP;
8411255332Scy		break;
8412255332Scy	default :
8413255332Scy		nflags = 0;
8414255332Scy		break;
8415255332Scy	}
8416255332Scy
8417255332Scy	WRITE_ENTER(&softc->ipf_nat);
8418255332Scy
8419255332Scy	if (fin->fin_out == 0) {
8420255332Scy		nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p,
8421255332Scy					fin->fin_dst, fin->fin_src);
8422255332Scy	} else {
8423255332Scy		nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p,
8424255332Scy				       fin->fin_src, fin->fin_dst);
8425255332Scy	}
8426255332Scy
8427255332Scy	if (nat != NULL) {
8428255332Scy		NBUMPSIDE(fin->fin_out, ns_uncreate[0]);
8429255332Scy		ipf_nat_delete(softc, nat, NL_DESTROY);
8430255332Scy	} else {
8431255332Scy		NBUMPSIDE(fin->fin_out, ns_uncreate[1]);
8432255332Scy	}
8433255332Scy
8434255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8435255332Scy}
8436255332Scy
8437255332Scy
8438255332Scy/* ------------------------------------------------------------------------ */
8439255332Scy/* Function:    ipf_nat_cmp_rules                                           */
8440255332Scy/* Returns:     int   - 0 == success, else rules do not match.              */
8441255332Scy/* Parameters:  n1(I) - first rule to compare                               */
8442255332Scy/*              n2(I) - first rule to compare                               */
8443255332Scy/*                                                                          */
8444255332Scy/* Compare two rules using pointers to each rule. A straight bcmp will not  */
8445255332Scy/* work as some fields (such as in_dst, in_pkts) actually do change once    */
8446255332Scy/* the rule has been loaded into the kernel. Whilst this function returns   */
8447255332Scy/* various non-zero returns, they're strictly to aid in debugging. Use of   */
8448255332Scy/* this function should simply care if the result is zero or not.           */
8449255332Scy/* ------------------------------------------------------------------------ */
8450255332Scystatic int
8451255332Scyipf_nat_cmp_rules(n1, n2)
8452255332Scy	ipnat_t *n1, *n2;
8453255332Scy{
8454255332Scy	if (n1->in_size != n2->in_size)
8455255332Scy		return 1;
8456255332Scy
8457255332Scy	if (bcmp((char *)&n1->in_v, (char *)&n2->in_v,
8458255332Scy		 offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0)
8459255332Scy		return 2;
8460255332Scy
8461255332Scy	if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc,
8462255332Scy		 n1->in_size - offsetof(ipnat_t, in_tuc)) != 0)
8463255332Scy		return 3;
8464255332Scy	if (n1->in_ndst.na_atype != n2->in_ndst.na_atype)
8465255332Scy		return 5;
8466255332Scy	if (n1->in_ndst.na_function != n2->in_ndst.na_function)
8467255332Scy		return 6;
8468255332Scy	if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr,
8469255332Scy		 sizeof(n1->in_ndst.na_addr)))
8470255332Scy		return 7;
8471255332Scy	if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype)
8472255332Scy		return 8;
8473255332Scy	if (n1->in_nsrc.na_function != n2->in_nsrc.na_function)
8474255332Scy		return 9;
8475255332Scy	if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr,
8476255332Scy		 sizeof(n1->in_nsrc.na_addr)))
8477255332Scy		return 10;
8478255332Scy	if (n1->in_odst.na_atype != n2->in_odst.na_atype)
8479255332Scy		return 11;
8480255332Scy	if (n1->in_odst.na_function != n2->in_odst.na_function)
8481255332Scy		return 12;
8482255332Scy	if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr,
8483255332Scy		 sizeof(n1->in_odst.na_addr)))
8484255332Scy		return 13;
8485255332Scy	if (n1->in_osrc.na_atype != n2->in_osrc.na_atype)
8486255332Scy		return 14;
8487255332Scy	if (n1->in_osrc.na_function != n2->in_osrc.na_function)
8488255332Scy		return 15;
8489255332Scy	if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr,
8490255332Scy		 sizeof(n1->in_osrc.na_addr)))
8491255332Scy		return 16;
8492255332Scy	return 0;
8493255332Scy}
8494255332Scy
8495255332Scy
8496255332Scy/* ------------------------------------------------------------------------ */
8497255332Scy/* Function:    ipf_nat_rule_init                                           */
8498255332Scy/* Returns:     int   - 0 == success, else rules do not match.              */
8499255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8500255332Scy/*              softn(I) - pointer to NAT context structure                 */
8501255332Scy/*              n(I)     - first rule to compare                            */
8502255332Scy/*                                                                          */
8503255332Scy/* ------------------------------------------------------------------------ */
8504255332Scystatic int
8505255332Scyipf_nat_rule_init(softc, softn, n)
8506255332Scy	ipf_main_softc_t *softc;
8507255332Scy	ipf_nat_softc_t *softn;
8508255332Scy	ipnat_t *n;
8509255332Scy{
8510255332Scy	int error = 0;
8511255332Scy
8512255332Scy	if ((n->in_flags & IPN_SIPRANGE) != 0)
8513255332Scy		n->in_nsrcatype = FRI_RANGE;
8514255332Scy
8515255332Scy	if ((n->in_flags & IPN_DIPRANGE) != 0)
8516255332Scy		n->in_ndstatype = FRI_RANGE;
8517255332Scy
8518255332Scy	if ((n->in_flags & IPN_SPLIT) != 0)
8519255332Scy		n->in_ndstatype = FRI_SPLIT;
8520255332Scy
8521255332Scy	if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0)
8522255332Scy		n->in_spnext = n->in_spmin;
8523255332Scy
8524255332Scy	if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) {
8525255332Scy		n->in_dpnext = n->in_dpmin;
8526255332Scy	} else if (n->in_redir == NAT_REDIRECT) {
8527255332Scy		n->in_dpnext = n->in_dpmin;
8528255332Scy	}
8529255332Scy
8530255332Scy	n->in_stepnext = 0;
8531255332Scy
8532255332Scy	switch (n->in_v[0])
8533255332Scy	{
8534255332Scy	case 4 :
8535255332Scy		error = ipf_nat_ruleaddrinit(softc, softn, n);
8536255332Scy		if (error != 0)
8537255332Scy			return error;
8538255332Scy		break;
8539255332Scy#ifdef USE_INET6
8540255332Scy	case 6 :
8541255332Scy		error = ipf_nat6_ruleaddrinit(softc, softn, n);
8542255332Scy		if (error != 0)
8543255332Scy			return error;
8544255332Scy		break;
8545255332Scy#endif
8546255332Scy	default :
8547255332Scy		break;
8548255332Scy	}
8549255332Scy
8550255332Scy	if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) {
8551255332Scy		/*
8552255332Scy		 * Prerecord whether or not the destination of the divert
8553255332Scy		 * is local or not to the interface the packet is going
8554255332Scy		 * to be sent out.
8555255332Scy		 */
8556255332Scy		n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1],
8557255332Scy						n->in_ifps[1], &n->in_ndstip6);
8558255332Scy	}
8559255332Scy
8560255332Scy	return error;
8561255332Scy}
8562255332Scy
8563255332Scy
8564255332Scy/* ------------------------------------------------------------------------ */
8565255332Scy/* Function:    ipf_nat_rule_fini                                           */
8566255332Scy/* Returns:     int   - 0 == success, else rules do not match.              */
8567255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8568255332Scy/*              n(I)     - rule to work on                                  */
8569255332Scy/*                                                                          */
8570255332Scy/* This function is used to release any objects that were referenced during */
8571255332Scy/* the rule initialisation. This is useful both when free'ing the rule and  */
8572255332Scy/* when handling ioctls that need to initialise these fields but not        */
8573255332Scy/* actually use them after the ioctl processing has finished.               */
8574255332Scy/* ------------------------------------------------------------------------ */
8575255332Scystatic void
8576255332Scyipf_nat_rule_fini(softc, n)
8577255332Scy	ipf_main_softc_t *softc;
8578255332Scy	ipnat_t *n;
8579255332Scy{
8580255332Scy	if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL)
8581255332Scy		ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr);
8582255332Scy
8583255332Scy	if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL)
8584255332Scy		ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr);
8585255332Scy
8586255332Scy	if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL)
8587255332Scy		ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr);
8588255332Scy
8589255332Scy	if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL)
8590255332Scy		ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr);
8591255332Scy
8592255332Scy	if (n->in_divmp != NULL)
8593255332Scy		FREE_MB_T(n->in_divmp);
8594255332Scy}
8595