1145522Sdarrenr/*	$FreeBSD: stable/10/sys/contrib/ipfilter/netinet/ip_nat.c 344113 2019-02-14 00:52:03Z cy $	*/
2145522Sdarrenr
353642Sguido/*
4255332Scy * Copyright (C) 2012 by Darren Reed.
553642Sguido *
680482Sdarrenr * See the IPFILTER.LICENCE file for details on licencing.
753642Sguido */
8145522Sdarrenr#if defined(KERNEL) || defined(_KERNEL)
9145522Sdarrenr# undef KERNEL
10145522Sdarrenr# undef _KERNEL
11145522Sdarrenr# define        KERNEL	1
12145522Sdarrenr# define        _KERNEL	1
1353642Sguido#endif
1453642Sguido#include <sys/errno.h>
1553642Sguido#include <sys/types.h>
1653642Sguido#include <sys/param.h>
1753642Sguido#include <sys/time.h>
1853642Sguido#include <sys/file.h>
19255332Scy#if defined(_KERNEL) && \
20255332Scy    (defined(__NetBSD_Version) && (__NetBSD_Version >= 399002000))
21170268Sdarrenr# include <sys/kauth.h>
22170268Sdarrenr#endif
23145522Sdarrenr#if !defined(_KERNEL)
2453642Sguido# include <stdio.h>
2553642Sguido# include <string.h>
2653642Sguido# include <stdlib.h>
27255332Scy# define KERNEL
28255332Scy# ifdef _OpenBSD__
29145522Sdarrenrstruct file;
30145522Sdarrenr# endif
31145522Sdarrenr# include <sys/uio.h>
32255332Scy# undef KERNEL
3353642Sguido#endif
34255332Scy#if defined(_KERNEL) && \
35255332Scy    defined(__FreeBSD_version) && (__FreeBSD_version >= 220000)
3653642Sguido# include <sys/filio.h>
3753642Sguido# include <sys/fcntl.h>
3853642Sguido#else
3953642Sguido# include <sys/ioctl.h>
4053642Sguido#endif
41153876Sguido#if !defined(AIX)
42153876Sguido# include <sys/fcntl.h>
43153876Sguido#endif
44145522Sdarrenr#if !defined(linux)
4553642Sguido# include <sys/protosw.h>
4653642Sguido#endif
4753642Sguido#include <sys/socket.h>
48145522Sdarrenr#if defined(_KERNEL)
4953642Sguido# include <sys/systm.h>
50145522Sdarrenr# if !defined(__SVR4) && !defined(__svr4__)
5153642Sguido#  include <sys/mbuf.h>
5253642Sguido# endif
53145522Sdarrenr#endif
54145522Sdarrenr#if defined(__SVR4) || defined(__svr4__)
5553642Sguido# include <sys/filio.h>
5653642Sguido# include <sys/byteorder.h>
57255332Scy# ifdef KERNEL
5853642Sguido#  include <sys/dditypes.h>
5953642Sguido# endif
6053642Sguido# include <sys/stream.h>
6153642Sguido# include <sys/kmem.h>
6253642Sguido#endif
6353642Sguido#if __FreeBSD_version >= 300000
6453642Sguido# include <sys/queue.h>
6553642Sguido#endif
6653642Sguido#include <net/if.h>
6753642Sguido#if __FreeBSD_version >= 300000
6853642Sguido# include <net/if_var.h>
6953642Sguido#endif
7053642Sguido#ifdef sun
7153642Sguido# include <net/af.h>
7253642Sguido#endif
7353642Sguido#include <netinet/in.h>
7453642Sguido#include <netinet/in_systm.h>
7553642Sguido#include <netinet/ip.h>
7653642Sguido
7753642Sguido#ifdef RFC1825
7853642Sguido# include <vpn/md5.h>
7953642Sguido# include <vpn/ipsec.h>
8053642Sguidoextern struct ifnet vpnif;
8153642Sguido#endif
8253642Sguido
83145522Sdarrenr#if !defined(linux)
8453642Sguido# include <netinet/ip_var.h>
8553642Sguido#endif
8653642Sguido#include <netinet/tcp.h>
8753642Sguido#include <netinet/udp.h>
8853642Sguido#include <netinet/ip_icmp.h>
8953642Sguido#include "netinet/ip_compat.h"
9053642Sguido#include <netinet/tcpip.h>
91255332Scy#include "netinet/ipl.h"
9253642Sguido#include "netinet/ip_fil.h"
9353642Sguido#include "netinet/ip_nat.h"
9453642Sguido#include "netinet/ip_frag.h"
9553642Sguido#include "netinet/ip_state.h"
9692685Sdarrenr#include "netinet/ip_proxy.h"
97255332Scy#include "netinet/ip_lookup.h"
98255332Scy#include "netinet/ip_dstlist.h"
99145522Sdarrenr#include "netinet/ip_sync.h"
100255332Scy#if FREEBSD_GE_REV(300000)
10153642Sguido# include <sys/malloc.h>
10253642Sguido#endif
103255332Scy#ifdef HAS_SYS_MD5_H
104255332Scy# include <sys/md5.h>
105255332Scy#else
106255332Scy# include "md5.h"
107255332Scy#endif
108145522Sdarrenr/* END OF INCLUDES */
109145522Sdarrenr
11053642Sguido#undef	SOCKADDR_IN
11153642Sguido#define	SOCKADDR_IN	struct sockaddr_in
11253642Sguido
11380482Sdarrenr#if !defined(lint)
11480482Sdarrenrstatic const char sccsid[] = "@(#)ip_nat.c	1.11 6/5/96 (C) 1995 Darren Reed";
11580482Sdarrenrstatic const char rcsid[] = "@(#)$FreeBSD: stable/10/sys/contrib/ipfilter/netinet/ip_nat.c 344113 2019-02-14 00:52:03Z cy $";
116172776Sdarrenr/* static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.102 2007/10/16 10:08:10 darrenr Exp $"; */
11780482Sdarrenr#endif
11880482Sdarrenr
119145522Sdarrenr
120255332Scy#define	NATFSUM(n,v,f)	((v) == 4 ? (n)->f.in4.s_addr : (n)->f.i6[0] + \
121255332Scy			 (n)->f.i6[1] + (n)->f.i6[2] + (n)->f.i6[3])
122255332Scy#define	NBUMP(x)	softn->(x)++
123255332Scy#define	NBUMPD(x, y)	do { \
124255332Scy				softn->x.y++; \
125255332Scy				DT(y); \
126255332Scy			} while (0)
127255332Scy#define	NBUMPSIDE(y,x)	softn->ipf_nat_stats.ns_side[y].x++
128255332Scy#define	NBUMPSIDED(y,x)	do { softn->ipf_nat_stats.ns_side[y].x++; \
129255332Scy			     DT(x); } while (0)
130255332Scy#define	NBUMPSIDEX(y,x,z) \
131255332Scy			do { softn->ipf_nat_stats.ns_side[y].x++; \
132255332Scy			     DT(z); } while (0)
133255332Scy#define	NBUMPSIDEDF(y,x)do { softn->ipf_nat_stats.ns_side[y].x++; \
134255332Scy			     DT1(x, fr_info_t *, fin); } while (0)
135255332Scy
136255332Scyfrentry_t	ipfnatblock;
137255332Scy
138255332Scystatic ipftuneable_t ipf_nat_tuneables[] = {
139255332Scy	/* nat */
140255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_lock) },
141255332Scy		"nat_lock",	0,	1,
142255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_lock),
143255332Scy		IPFT_RDONLY,		NULL,	NULL },
144255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz) },
145255332Scy		"nat_table_size", 1,	0x7fffffff,
146255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_table_sz),
147255332Scy		0,			NULL,	ipf_nat_rehash },
148255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max) },
149255332Scy		"nat_table_max", 1,	0x7fffffff,
150255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_table_max),
151255332Scy		0,			NULL,	NULL },
152255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz) },
153255332Scy		"nat_rules_size", 1,	0x7fffffff,
154255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_maprules_sz),
155255332Scy		0,			NULL,	ipf_nat_rehash_rules },
156255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz) },
157255332Scy		"rdr_rules_size", 1,	0x7fffffff,
158255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_rdrrules_sz),
159255332Scy		0,			NULL,	ipf_nat_rehash_rules },
160255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz) },
161255332Scy		"hostmap_size",	1,	0x7fffffff,
162255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_hostmap_sz),
163255332Scy		0,			NULL,	ipf_nat_hostmap_rehash },
164255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maxbucket) },
165255332Scy		"nat_maxbucket",1,	0x7fffffff,
166255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_maxbucket),
167255332Scy		0,			NULL,	NULL },
168255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_logging) },
169255332Scy		"nat_logging",	0,	1,
170255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_logging),
171255332Scy		0,			NULL,	NULL },
172255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_doflush) },
173255332Scy		"nat_doflush",	0,	1,
174255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_doflush),
175255332Scy		0,			NULL,	NULL },
176255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_low) },
177255332Scy		"nat_table_wm_low",	1,	99,
178255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_low),
179255332Scy		0,			NULL,	NULL },
180255332Scy	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_high) },
181255332Scy		"nat_table_wm_high",	2,	100,
182255332Scy		stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_high),
183255332Scy		0,			NULL,	NULL },
184255332Scy	{ { 0 },
185255332Scy		NULL,			0,	0,
186255332Scy		0,
187255332Scy		0,			NULL,	NULL }
188255332Scy};
189255332Scy
190145522Sdarrenr/* ======================================================================== */
191145522Sdarrenr/* How the NAT is organised and works.                                      */
192145522Sdarrenr/*                                                                          */
193145522Sdarrenr/* Inside (interface y) NAT       Outside (interface x)                     */
194145522Sdarrenr/* -------------------- -+- -------------------------------------           */
195255332Scy/* Packet going          |   out, processsed by ipf_nat_checkout() for x    */
196145522Sdarrenr/* ------------>         |   ------------>                                  */
197145522Sdarrenr/* src=10.1.1.1          |   src=192.1.1.1                                  */
198145522Sdarrenr/*                       |                                                  */
199255332Scy/*                       |   in, processed by ipf_nat_checkin() for x       */
200145522Sdarrenr/* <------------         |   <------------                                  */
201145522Sdarrenr/* dst=10.1.1.1          |   dst=192.1.1.1                                  */
202145522Sdarrenr/* -------------------- -+- -------------------------------------           */
203255332Scy/* ipf_nat_checkout() - changes ip_src and if required, sport               */
204145522Sdarrenr/*             - creates a new mapping, if required.                        */
205255332Scy/* ipf_nat_checkin()  - changes ip_dst and if required, dport               */
206145522Sdarrenr/*                                                                          */
207145522Sdarrenr/* In the NAT table, internal source is recorded as "in" and externally     */
208145522Sdarrenr/* seen as "out".                                                           */
209145522Sdarrenr/* ======================================================================== */
210145522Sdarrenr
211145522Sdarrenr
212255332Scy#if SOLARIS && !defined(INSTANCES)
213255332Scyextern	int		pfil_delayed_copy;
214255332Scy#endif
215255332Scy
216255332Scystatic	int	ipf_nat_flush_entry __P((ipf_main_softc_t *, void *));
217255332Scystatic	int	ipf_nat_getent __P((ipf_main_softc_t *, caddr_t, int));
218255332Scystatic	int	ipf_nat_getsz __P((ipf_main_softc_t *, caddr_t, int));
219255332Scystatic	int	ipf_nat_putent __P((ipf_main_softc_t *, caddr_t, int));
220255332Scystatic	void	ipf_nat_addmap __P((ipf_nat_softc_t *, ipnat_t *));
221255332Scystatic	void	ipf_nat_addrdr __P((ipf_nat_softc_t *, ipnat_t *));
222255332Scystatic	int	ipf_nat_builddivertmp __P((ipf_nat_softc_t *, ipnat_t *));
223255332Scystatic	int	ipf_nat_clearlist __P((ipf_main_softc_t *, ipf_nat_softc_t *));
224255332Scystatic	int	ipf_nat_cmp_rules __P((ipnat_t *, ipnat_t *));
225255332Scystatic	int	ipf_nat_decap __P((fr_info_t *, nat_t *));
226255332Scystatic	void	ipf_nat_delrule __P((ipf_main_softc_t *, ipf_nat_softc_t *,
227255332Scy				     ipnat_t *, int));
228255332Scystatic	int	ipf_nat_extraflush __P((ipf_main_softc_t *, ipf_nat_softc_t *, int));
229255332Scystatic	int	ipf_nat_finalise __P((fr_info_t *, nat_t *));
230255332Scystatic	int	ipf_nat_flushtable __P((ipf_main_softc_t *, ipf_nat_softc_t *));
231255332Scystatic	int	ipf_nat_getnext __P((ipf_main_softc_t *, ipftoken_t *,
232255332Scy				     ipfgeniter_t *, ipfobj_t *));
233255332Scystatic	int	ipf_nat_gettable __P((ipf_main_softc_t *, ipf_nat_softc_t *,
234255332Scy				      char *));
235255332Scystatic	hostmap_t *ipf_nat_hostmap __P((ipf_nat_softc_t *, ipnat_t *,
236255332Scy					struct in_addr, struct in_addr,
237255332Scy					struct in_addr, u_32_t));
238255332Scystatic	int	ipf_nat_icmpquerytype __P((int));
239255332Scystatic	int	ipf_nat_iterator __P((ipf_main_softc_t *, ipftoken_t *,
240255332Scy				      ipfgeniter_t *, ipfobj_t *));
241255332Scystatic	int	ipf_nat_match __P((fr_info_t *, ipnat_t *));
242255332Scystatic	int	ipf_nat_matcharray __P((nat_t *, int *, u_long));
243255332Scystatic	int	ipf_nat_matchflush __P((ipf_main_softc_t *, ipf_nat_softc_t *,
244255332Scy					caddr_t));
245255332Scystatic	void	ipf_nat_mssclamp __P((tcphdr_t *, u_32_t, fr_info_t *,
246255332Scy				      u_short *));
247255332Scystatic	int	ipf_nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *));
248255332Scystatic	int	ipf_nat_newdivert __P((fr_info_t *, nat_t *, natinfo_t *));
249255332Scystatic	int	ipf_nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *));
250255332Scystatic	int	ipf_nat_newrewrite __P((fr_info_t *, nat_t *, natinfo_t *));
251255332Scystatic	int	ipf_nat_nextaddr __P((fr_info_t *, nat_addr_t *, u_32_t *,
252255332Scy				      u_32_t *));
253255332Scystatic	int	ipf_nat_nextaddrinit __P((ipf_main_softc_t *, char *,
254255332Scy					  nat_addr_t *, int, void *));
255255332Scystatic	int	ipf_nat_resolverule __P((ipf_main_softc_t *, ipnat_t *));
256255332Scystatic	int	ipf_nat_ruleaddrinit __P((ipf_main_softc_t *,
257255332Scy					  ipf_nat_softc_t *, ipnat_t *));
258255332Scystatic	void	ipf_nat_rule_fini __P((ipf_main_softc_t *, ipnat_t *));
259255332Scystatic	int	ipf_nat_rule_init __P((ipf_main_softc_t *, ipf_nat_softc_t *,
260255332Scy				       ipnat_t *));
261255332Scystatic	int	ipf_nat_siocaddnat __P((ipf_main_softc_t *, ipf_nat_softc_t *,
262255332Scy					ipnat_t *, int));
263255332Scystatic	void	ipf_nat_siocdelnat __P((ipf_main_softc_t *, ipf_nat_softc_t *,
264255332Scy					ipnat_t *, int));
265255332Scystatic	void	ipf_nat_tabmove __P((ipf_nat_softc_t *, nat_t *));
266255332Scy
267255332Scy/* ------------------------------------------------------------------------ */
268255332Scy/* Function:    ipf_nat_main_load                                           */
269255332Scy/* Returns:     int - 0 == success, -1 == failure                           */
270255332Scy/* Parameters:  Nil                                                         */
271255332Scy/*                                                                          */
272255332Scy/* The only global NAT structure that needs to be initialised is the filter */
273255332Scy/* rule that is used with blocking packets.                                 */
274255332Scy/* ------------------------------------------------------------------------ */
275255332Scyint
276255332Scyipf_nat_main_load()
277255332Scy{
278255332Scy	bzero((char *)&ipfnatblock, sizeof(ipfnatblock));
279255332Scy	ipfnatblock.fr_flags = FR_BLOCK|FR_QUICK;
280255332Scy	ipfnatblock.fr_ref = 1;
281255332Scy
282255332Scy	return 0;
283255332Scy}
284255332Scy
285255332Scy
286255332Scy/* ------------------------------------------------------------------------ */
287255332Scy/* Function:    ipf_nat_main_unload                                         */
288255332Scy/* Returns:     int - 0 == success, -1 == failure                           */
289255332Scy/* Parameters:  Nil                                                         */
290255332Scy/*                                                                          */
291255332Scy/* A null-op function that exists as a placeholder so that the flow in      */
292255332Scy/* other functions is obvious.                                              */
293255332Scy/* ------------------------------------------------------------------------ */
294255332Scyint
295255332Scyipf_nat_main_unload()
296255332Scy{
297255332Scy	return 0;
298255332Scy}
299255332Scy
300255332Scy
301255332Scy/* ------------------------------------------------------------------------ */
302255332Scy/* Function:    ipf_nat_soft_create                                         */
303255332Scy/* Returns:     void * - NULL = failure, else pointer to NAT context        */
304255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
305255332Scy/*                                                                          */
306255332Scy/* Allocate the initial soft context structure for NAT and populate it with */
307255332Scy/* some default values. Creating the tables is left until we call _init so  */
308255332Scy/* that sizes can be changed before we get under way.                       */
309255332Scy/* ------------------------------------------------------------------------ */
310255332Scyvoid *
311255332Scyipf_nat_soft_create(softc)
312255332Scy	ipf_main_softc_t *softc;
313255332Scy{
314255332Scy	ipf_nat_softc_t *softn;
315255332Scy
316255332Scy	KMALLOC(softn, ipf_nat_softc_t *);
317255332Scy	if (softn == NULL)
318255332Scy		return NULL;
319255332Scy
320255332Scy	bzero((char *)softn, sizeof(*softn));
321255332Scy
322255332Scy	softn->ipf_nat_tune = ipf_tune_array_copy(softn,
323255332Scy						  sizeof(ipf_nat_tuneables),
324255332Scy						  ipf_nat_tuneables);
325255332Scy	if (softn->ipf_nat_tune == NULL) {
326255332Scy		ipf_nat_soft_destroy(softc, softn);
327255332Scy		return NULL;
328255332Scy	}
329255332Scy	if (ipf_tune_array_link(softc, softn->ipf_nat_tune) == -1) {
330255332Scy		ipf_nat_soft_destroy(softc, softn);
331255332Scy		return NULL;
332255332Scy	}
333255332Scy
334255332Scy	softn->ipf_nat_list_tail = &softn->ipf_nat_list;
335255332Scy
336255332Scy	softn->ipf_nat_table_max = NAT_TABLE_MAX;
337255332Scy	softn->ipf_nat_table_sz = NAT_TABLE_SZ;
338255332Scy	softn->ipf_nat_maprules_sz = NAT_SIZE;
339255332Scy	softn->ipf_nat_rdrrules_sz = RDR_SIZE;
340255332Scy	softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE;
341255332Scy	softn->ipf_nat_doflush = 0;
342145522Sdarrenr#ifdef  IPFILTER_LOG
343255332Scy	softn->ipf_nat_logging = 1;
344145522Sdarrenr#else
345255332Scy	softn->ipf_nat_logging = 0;
346145522Sdarrenr#endif
34753642Sguido
348255332Scy	softn->ipf_nat_defage = DEF_NAT_AGE;
349255332Scy	softn->ipf_nat_defipage = IPF_TTLVAL(60);
350255332Scy	softn->ipf_nat_deficmpage = IPF_TTLVAL(3);
351255332Scy	softn->ipf_nat_table_wm_high = 99;
352255332Scy	softn->ipf_nat_table_wm_low = 90;
35353642Sguido
354255332Scy	return softn;
355255332Scy}
35653642Sguido
357255332Scy/* ------------------------------------------------------------------------ */
358255332Scy/* Function:    ipf_nat_soft_destroy                                        */
359255332Scy/* Returns:     Nil                                                         */
360255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
361255332Scy/*                                                                          */
362255332Scy/* ------------------------------------------------------------------------ */
363255332Scyvoid
364255332Scyipf_nat_soft_destroy(softc, arg)
365255332Scy	ipf_main_softc_t *softc;
366255332Scy	void *arg;
367255332Scy{
368255332Scy	ipf_nat_softc_t *softn = arg;
36953642Sguido
370255332Scy	if (softn->ipf_nat_tune != NULL) {
371255332Scy		ipf_tune_array_unlink(softc, softn->ipf_nat_tune);
372255332Scy		KFREES(softn->ipf_nat_tune, sizeof(ipf_nat_tuneables));
373255332Scy		softn->ipf_nat_tune = NULL;
374255332Scy	}
375255332Scy
376255332Scy	KFREE(softn);
377255332Scy}
378255332Scy
379255332Scy
380145522Sdarrenr/* ------------------------------------------------------------------------ */
381255332Scy/* Function:    ipf_nat_init                                                */
382145522Sdarrenr/* Returns:     int - 0 == success, -1 == failure                           */
383255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
384145522Sdarrenr/*                                                                          */
385145522Sdarrenr/* Initialise all of the NAT locks, tables and other structures.            */
386145522Sdarrenr/* ------------------------------------------------------------------------ */
387255332Scyint
388255332Scyipf_nat_soft_init(softc, arg)
389255332Scy	ipf_main_softc_t *softc;
390255332Scy	void *arg;
39153642Sguido{
392255332Scy	ipf_nat_softc_t *softn = arg;
393255332Scy	ipftq_t *tq;
394145522Sdarrenr	int i;
395145522Sdarrenr
396255332Scy	KMALLOCS(softn->ipf_nat_table[0], nat_t **, \
397255332Scy		 sizeof(nat_t *) * softn->ipf_nat_table_sz);
398255332Scy
399255332Scy	if (softn->ipf_nat_table[0] != NULL) {
400255332Scy		bzero((char *)softn->ipf_nat_table[0],
401255332Scy		      softn->ipf_nat_table_sz * sizeof(nat_t *));
402255332Scy	} else {
40353642Sguido		return -1;
404255332Scy	}
40553642Sguido
406255332Scy	KMALLOCS(softn->ipf_nat_table[1], nat_t **, \
407255332Scy		 sizeof(nat_t *) * softn->ipf_nat_table_sz);
408255332Scy
409255332Scy	if (softn->ipf_nat_table[1] != NULL) {
410255332Scy		bzero((char *)softn->ipf_nat_table[1],
411255332Scy		      softn->ipf_nat_table_sz * sizeof(nat_t *));
412255332Scy	} else {
413145522Sdarrenr		return -2;
414255332Scy	}
41553642Sguido
416255332Scy	KMALLOCS(softn->ipf_nat_map_rules, ipnat_t **, \
417255332Scy		 sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz);
418255332Scy
419255332Scy	if (softn->ipf_nat_map_rules != NULL) {
420255332Scy		bzero((char *)softn->ipf_nat_map_rules,
421255332Scy		      softn->ipf_nat_maprules_sz * sizeof(ipnat_t *));
422255332Scy	} else {
423145522Sdarrenr		return -3;
424255332Scy	}
42553642Sguido
426255332Scy	KMALLOCS(softn->ipf_nat_rdr_rules, ipnat_t **, \
427255332Scy		 sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz);
428255332Scy
429255332Scy	if (softn->ipf_nat_rdr_rules != NULL) {
430255332Scy		bzero((char *)softn->ipf_nat_rdr_rules,
431255332Scy		      softn->ipf_nat_rdrrules_sz * sizeof(ipnat_t *));
432255332Scy	} else {
433145522Sdarrenr		return -4;
434255332Scy	}
43560852Sdarrenr
436255332Scy	KMALLOCS(softn->ipf_hm_maptable, hostmap_t **, \
437255332Scy		 sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz);
438255332Scy
439255332Scy	if (softn->ipf_hm_maptable != NULL) {
440255332Scy		bzero((char *)softn->ipf_hm_maptable,
441255332Scy		      sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz);
442255332Scy	} else {
443145522Sdarrenr		return -5;
444255332Scy	}
445255332Scy	softn->ipf_hm_maplist = NULL;
446145522Sdarrenr
447255332Scy	KMALLOCS(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, u_int *,
448255332Scy		 softn->ipf_nat_table_sz * sizeof(u_int));
449255332Scy
450255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen == NULL) {
451145522Sdarrenr		return -6;
452255332Scy	}
453255332Scy	bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
454255332Scy	      softn->ipf_nat_table_sz * sizeof(u_int));
455145522Sdarrenr
456255332Scy	KMALLOCS(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, u_int *,
457255332Scy		 softn->ipf_nat_table_sz * sizeof(u_int));
458255332Scy
459255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen == NULL) {
460145522Sdarrenr		return -7;
461255332Scy	}
462145522Sdarrenr
463255332Scy	bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
464255332Scy	      softn->ipf_nat_table_sz * sizeof(u_int));
465145522Sdarrenr
466255332Scy	if (softn->ipf_nat_maxbucket == 0) {
467255332Scy		for (i = softn->ipf_nat_table_sz; i > 0; i >>= 1)
468255332Scy			softn->ipf_nat_maxbucket++;
469255332Scy		softn->ipf_nat_maxbucket *= 2;
470145522Sdarrenr	}
471145522Sdarrenr
472255332Scy	ipf_sttab_init(softc, softn->ipf_nat_tcptq);
473145522Sdarrenr	/*
474145522Sdarrenr	 * Increase this because we may have "keep state" following this too
475145522Sdarrenr	 * and packet storms can occur if this is removed too quickly.
476145522Sdarrenr	 */
477255332Scy	softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack;
478255332Scy	softn->ipf_nat_tcptq[IPF_TCP_NSTATES - 1].ifq_next =
479255332Scy							&softn->ipf_nat_udptq;
480145522Sdarrenr
481255332Scy	IPFTQ_INIT(&softn->ipf_nat_udptq, softn->ipf_nat_defage,
482255332Scy		   "nat ipftq udp tab");
483255332Scy	softn->ipf_nat_udptq.ifq_next = &softn->ipf_nat_udpacktq;
484255332Scy
485255332Scy	IPFTQ_INIT(&softn->ipf_nat_udpacktq, softn->ipf_nat_defage,
486255332Scy		   "nat ipftq udpack tab");
487255332Scy	softn->ipf_nat_udpacktq.ifq_next = &softn->ipf_nat_icmptq;
488255332Scy
489255332Scy	IPFTQ_INIT(&softn->ipf_nat_icmptq, softn->ipf_nat_deficmpage,
490255332Scy		   "nat icmp ipftq tab");
491255332Scy	softn->ipf_nat_icmptq.ifq_next = &softn->ipf_nat_icmpacktq;
492255332Scy
493255332Scy	IPFTQ_INIT(&softn->ipf_nat_icmpacktq, softn->ipf_nat_defage,
494255332Scy		   "nat icmpack ipftq tab");
495255332Scy	softn->ipf_nat_icmpacktq.ifq_next = &softn->ipf_nat_iptq;
496255332Scy
497255332Scy	IPFTQ_INIT(&softn->ipf_nat_iptq, softn->ipf_nat_defipage,
498255332Scy		   "nat ip ipftq tab");
499255332Scy	softn->ipf_nat_iptq.ifq_next = &softn->ipf_nat_pending;
500255332Scy
501255332Scy	IPFTQ_INIT(&softn->ipf_nat_pending, 1, "nat pending ipftq tab");
502255332Scy	softn->ipf_nat_pending.ifq_next = NULL;
503255332Scy
504255332Scy	for (i = 0, tq = softn->ipf_nat_tcptq; i < IPF_TCP_NSTATES; i++, tq++) {
505255332Scy		if (tq->ifq_ttl < softn->ipf_nat_deficmpage)
506255332Scy			tq->ifq_ttl = softn->ipf_nat_deficmpage;
507145522Sdarrenr#ifdef LARGE_NAT
508255332Scy		else if (tq->ifq_ttl > softn->ipf_nat_defage)
509255332Scy			tq->ifq_ttl = softn->ipf_nat_defage;
510145522Sdarrenr#endif
511145522Sdarrenr	}
512145522Sdarrenr
513145522Sdarrenr	/*
514145522Sdarrenr	 * Increase this because we may have "keep state" following
515145522Sdarrenr	 * this too and packet storms can occur if this is removed
516145522Sdarrenr	 * too quickly.
517145522Sdarrenr	 */
518255332Scy	softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack;
519145522Sdarrenr
520255332Scy	MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex");
521255332Scy	MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex");
522145522Sdarrenr
523255332Scy	softn->ipf_nat_inited = 1;
524145522Sdarrenr
52553642Sguido	return 0;
52653642Sguido}
52753642Sguido
52853642Sguido
529145522Sdarrenr/* ------------------------------------------------------------------------ */
530255332Scy/* Function:    ipf_nat_soft_fini                                           */
531145522Sdarrenr/* Returns:     Nil                                                         */
532255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
533255332Scy/*                                                                          */
534255332Scy/* Free all memory used by NAT structures allocated at runtime.             */
535255332Scy/* ------------------------------------------------------------------------ */
536255332Scyint
537255332Scyipf_nat_soft_fini(softc, arg)
538255332Scy	ipf_main_softc_t *softc;
539255332Scy	void *arg;
540255332Scy{
541255332Scy	ipf_nat_softc_t *softn = arg;
542255332Scy	ipftq_t *ifq, *ifqnext;
543255332Scy
544255332Scy	(void) ipf_nat_clearlist(softc, softn);
545255332Scy	(void) ipf_nat_flushtable(softc, softn);
546255332Scy
547255332Scy	/*
548255332Scy	 * Proxy timeout queues are not cleaned here because although they
549255332Scy	 * exist on the NAT list, ipf_proxy_unload is called after unload
550255332Scy	 * and the proxies actually are responsible for them being created.
551255332Scy	 * Should the proxy timeouts have their own list?  There's no real
552255332Scy	 * justification as this is the only complication.
553255332Scy	 */
554255332Scy	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) {
555255332Scy		ifqnext = ifq->ifq_next;
556255332Scy		if (ipf_deletetimeoutqueue(ifq) == 0)
557255332Scy			ipf_freetimeoutqueue(softc, ifq);
558255332Scy	}
559255332Scy
560255332Scy	if (softn->ipf_nat_table[0] != NULL) {
561255332Scy		KFREES(softn->ipf_nat_table[0],
562255332Scy		       sizeof(nat_t *) * softn->ipf_nat_table_sz);
563255332Scy		softn->ipf_nat_table[0] = NULL;
564255332Scy	}
565255332Scy	if (softn->ipf_nat_table[1] != NULL) {
566255332Scy		KFREES(softn->ipf_nat_table[1],
567255332Scy		       sizeof(nat_t *) * softn->ipf_nat_table_sz);
568255332Scy		softn->ipf_nat_table[1] = NULL;
569255332Scy	}
570255332Scy	if (softn->ipf_nat_map_rules != NULL) {
571255332Scy		KFREES(softn->ipf_nat_map_rules,
572255332Scy		       sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz);
573255332Scy		softn->ipf_nat_map_rules = NULL;
574255332Scy	}
575255332Scy	if (softn->ipf_nat_rdr_rules != NULL) {
576255332Scy		KFREES(softn->ipf_nat_rdr_rules,
577255332Scy		       sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz);
578255332Scy		softn->ipf_nat_rdr_rules = NULL;
579255332Scy	}
580255332Scy	if (softn->ipf_hm_maptable != NULL) {
581255332Scy		KFREES(softn->ipf_hm_maptable,
582255332Scy		       sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz);
583255332Scy		softn->ipf_hm_maptable = NULL;
584255332Scy	}
585255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) {
586255332Scy		KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
587255332Scy		       sizeof(u_int) * softn->ipf_nat_table_sz);
588255332Scy		softn->ipf_nat_stats.ns_side[0].ns_bucketlen = NULL;
589255332Scy	}
590255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) {
591255332Scy		KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
592255332Scy		       sizeof(u_int) * softn->ipf_nat_table_sz);
593255332Scy		softn->ipf_nat_stats.ns_side[1].ns_bucketlen = NULL;
594255332Scy	}
595255332Scy
596255332Scy	if (softn->ipf_nat_inited == 1) {
597255332Scy		softn->ipf_nat_inited = 0;
598255332Scy		ipf_sttab_destroy(softn->ipf_nat_tcptq);
599255332Scy
600255332Scy		MUTEX_DESTROY(&softn->ipf_nat_new);
601255332Scy		MUTEX_DESTROY(&softn->ipf_nat_io);
602255332Scy
603255332Scy		MUTEX_DESTROY(&softn->ipf_nat_udptq.ifq_lock);
604255332Scy		MUTEX_DESTROY(&softn->ipf_nat_udpacktq.ifq_lock);
605255332Scy		MUTEX_DESTROY(&softn->ipf_nat_icmptq.ifq_lock);
606255332Scy		MUTEX_DESTROY(&softn->ipf_nat_icmpacktq.ifq_lock);
607255332Scy		MUTEX_DESTROY(&softn->ipf_nat_iptq.ifq_lock);
608255332Scy		MUTEX_DESTROY(&softn->ipf_nat_pending.ifq_lock);
609255332Scy	}
610255332Scy
611255332Scy	return 0;
612255332Scy}
613255332Scy
614255332Scy
615255332Scy/* ------------------------------------------------------------------------ */
616255332Scy/* Function:    ipf_nat_setlock                                             */
617255332Scy/* Returns:     Nil                                                         */
618255332Scy/* Parameters:  arg(I) - pointer to soft state information                  */
619255332Scy/*              tmp(I) - new lock value                                     */
620255332Scy/*                                                                          */
621255332Scy/* Set the "lock status" of NAT to the value in tmp.                        */
622255332Scy/* ------------------------------------------------------------------------ */
623255332Scyvoid
624255332Scyipf_nat_setlock(arg, tmp)
625255332Scy	void *arg;
626255332Scy	int tmp;
627255332Scy{
628255332Scy	ipf_nat_softc_t *softn = arg;
629255332Scy
630255332Scy	softn->ipf_nat_lock = tmp;
631255332Scy}
632255332Scy
633255332Scy
634255332Scy/* ------------------------------------------------------------------------ */
635255332Scy/* Function:    ipf_nat_addrdr                                              */
636255332Scy/* Returns:     Nil                                                         */
637145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to add                           */
638145522Sdarrenr/*                                                                          */
639145522Sdarrenr/* Adds a redirect rule to the hash table of redirect rules and the list of */
640145522Sdarrenr/* loaded NAT rules.  Updates the bitmask indicating which netmasks are in  */
641145522Sdarrenr/* use by redirect rules.                                                   */
642145522Sdarrenr/* ------------------------------------------------------------------------ */
643255332Scystatic void
644255332Scyipf_nat_addrdr(softn, n)
645255332Scy	ipf_nat_softc_t *softn;
646255332Scy	ipnat_t *n;
64753642Sguido{
64860852Sdarrenr	ipnat_t **np;
64960852Sdarrenr	u_32_t j;
65053642Sguido	u_int hv;
651255332Scy	u_int rhv;
65260852Sdarrenr	int k;
65353642Sguido
654255332Scy	if (n->in_odstatype == FRI_NORMAL) {
655255332Scy		k = count4bits(n->in_odstmsk);
656255332Scy		ipf_inet_mask_add(k, &softn->ipf_nat_rdr_mask);
657255332Scy		j = (n->in_odstaddr & n->in_odstmsk);
658255332Scy		rhv = NAT_HASH_FN(j, 0, 0xffffffff);
659255332Scy	} else {
660255332Scy		ipf_inet_mask_add(0, &softn->ipf_nat_rdr_mask);
661255332Scy		j = 0;
662255332Scy		rhv = 0;
663255332Scy	}
664255332Scy	hv = rhv % softn->ipf_nat_rdrrules_sz;
665255332Scy	np = softn->ipf_nat_rdr_rules + hv;
66660852Sdarrenr	while (*np != NULL)
66760852Sdarrenr		np = &(*np)->in_rnext;
66860852Sdarrenr	n->in_rnext = NULL;
66960852Sdarrenr	n->in_prnext = np;
670255332Scy	n->in_hv[0] = hv;
671255332Scy	n->in_use++;
67260852Sdarrenr	*np = n;
67353642Sguido}
67453642Sguido
67553642Sguido
676145522Sdarrenr/* ------------------------------------------------------------------------ */
677255332Scy/* Function:    ipf_nat_addmap                                              */
678145522Sdarrenr/* Returns:     Nil                                                         */
679145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to add                           */
680145522Sdarrenr/*                                                                          */
681145522Sdarrenr/* Adds a NAT map rule to the hash table of rules and the list of  loaded   */
682145522Sdarrenr/* NAT rules.  Updates the bitmask indicating which netmasks are in use by  */
683145522Sdarrenr/* redirect rules.                                                          */
684145522Sdarrenr/* ------------------------------------------------------------------------ */
685255332Scystatic void
686255332Scyipf_nat_addmap(softn, n)
687255332Scy	ipf_nat_softc_t *softn;
688255332Scy	ipnat_t *n;
68960852Sdarrenr{
69060852Sdarrenr	ipnat_t **np;
69160852Sdarrenr	u_32_t j;
69260852Sdarrenr	u_int hv;
693255332Scy	u_int rhv;
69460852Sdarrenr	int k;
69560852Sdarrenr
696255332Scy	if (n->in_osrcatype == FRI_NORMAL) {
697255332Scy		k = count4bits(n->in_osrcmsk);
698255332Scy		ipf_inet_mask_add(k, &softn->ipf_nat_map_mask);
699255332Scy		j = (n->in_osrcaddr & n->in_osrcmsk);
700255332Scy		rhv = NAT_HASH_FN(j, 0, 0xffffffff);
701255332Scy	} else {
702255332Scy		ipf_inet_mask_add(0, &softn->ipf_nat_map_mask);
703255332Scy		j = 0;
704255332Scy		rhv = 0;
705255332Scy	}
706255332Scy	hv = rhv % softn->ipf_nat_maprules_sz;
707255332Scy	np = softn->ipf_nat_map_rules + hv;
70860852Sdarrenr	while (*np != NULL)
70960852Sdarrenr		np = &(*np)->in_mnext;
71060852Sdarrenr	n->in_mnext = NULL;
71160852Sdarrenr	n->in_pmnext = np;
712255332Scy	n->in_hv[1] = rhv;
713255332Scy	n->in_use++;
71460852Sdarrenr	*np = n;
71560852Sdarrenr}
71660852Sdarrenr
71760852Sdarrenr
718145522Sdarrenr/* ------------------------------------------------------------------------ */
719255332Scy/* Function:    ipf_nat_delrdr                                              */
720145522Sdarrenr/* Returns:     Nil                                                         */
721145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to delete                        */
722145522Sdarrenr/*                                                                          */
723145522Sdarrenr/* Removes a redirect rule from the hash table of redirect rules.           */
724145522Sdarrenr/* ------------------------------------------------------------------------ */
725255332Scyvoid
726255332Scyipf_nat_delrdr(softn, n)
727255332Scy	ipf_nat_softc_t *softn;
728255332Scy	ipnat_t *n;
72960852Sdarrenr{
730255332Scy	if (n->in_odstatype == FRI_NORMAL) {
731255332Scy		int k = count4bits(n->in_odstmsk);
732255332Scy		ipf_inet_mask_del(k, &softn->ipf_nat_rdr_mask);
733255332Scy	} else {
734255332Scy		ipf_inet_mask_del(0, &softn->ipf_nat_rdr_mask);
735255332Scy	}
73660852Sdarrenr	if (n->in_rnext)
73760852Sdarrenr		n->in_rnext->in_prnext = n->in_prnext;
73860852Sdarrenr	*n->in_prnext = n->in_rnext;
739255332Scy	n->in_use--;
74060852Sdarrenr}
74160852Sdarrenr
74260852Sdarrenr
743145522Sdarrenr/* ------------------------------------------------------------------------ */
744255332Scy/* Function:    ipf_nat_delmap                                              */
745145522Sdarrenr/* Returns:     Nil                                                         */
746145522Sdarrenr/* Parameters:  n(I) - pointer to NAT rule to delete                        */
747145522Sdarrenr/*                                                                          */
748145522Sdarrenr/* Removes a NAT map rule from the hash table of NAT map rules.             */
749145522Sdarrenr/* ------------------------------------------------------------------------ */
750255332Scyvoid
751255332Scyipf_nat_delmap(softn, n)
752255332Scy	ipf_nat_softc_t *softn;
753255332Scy	ipnat_t *n;
75453642Sguido{
755255332Scy	if (n->in_osrcatype == FRI_NORMAL) {
756255332Scy		int k = count4bits(n->in_osrcmsk);
757255332Scy		ipf_inet_mask_del(k, &softn->ipf_nat_map_mask);
758255332Scy	} else {
759255332Scy		ipf_inet_mask_del(0, &softn->ipf_nat_map_mask);
760255332Scy	}
761145522Sdarrenr	if (n->in_mnext != NULL)
76260852Sdarrenr		n->in_mnext->in_pmnext = n->in_pmnext;
76360852Sdarrenr	*n->in_pmnext = n->in_mnext;
764255332Scy	n->in_use--;
76560852Sdarrenr}
76660852Sdarrenr
76760852Sdarrenr
768145522Sdarrenr/* ------------------------------------------------------------------------ */
769255332Scy/* Function:    ipf_nat_hostmap                                             */
770145522Sdarrenr/* Returns:     struct hostmap* - NULL if no hostmap could be created,      */
771145522Sdarrenr/*                                else a pointer to the hostmapping to use  */
772145522Sdarrenr/* Parameters:  np(I)   - pointer to NAT rule                               */
773145522Sdarrenr/*              real(I) - real IP address                                   */
774145522Sdarrenr/*              map(I)  - mapped IP address                                 */
775145522Sdarrenr/*              port(I) - destination port number                           */
776145522Sdarrenr/* Write Locks: ipf_nat                                                     */
777145522Sdarrenr/*                                                                          */
778145522Sdarrenr/* Check if an ip address has already been allocated for a given mapping    */
779145522Sdarrenr/* that is not doing port based translation.  If is not yet allocated, then */
780145522Sdarrenr/* create a new entry if a non-NULL NAT rule pointer has been supplied.     */
781145522Sdarrenr/* ------------------------------------------------------------------------ */
782255332Scystatic struct hostmap *
783255332Scyipf_nat_hostmap(softn, np, src, dst, map, port)
784255332Scy	ipf_nat_softc_t *softn;
785255332Scy	ipnat_t *np;
786255332Scy	struct in_addr src;
787255332Scy	struct in_addr dst;
788255332Scy	struct in_addr map;
789255332Scy	u_32_t port;
79060852Sdarrenr{
79160852Sdarrenr	hostmap_t *hm;
792255332Scy	u_int hv, rhv;
79353642Sguido
794145522Sdarrenr	hv = (src.s_addr ^ dst.s_addr);
795145522Sdarrenr	hv += src.s_addr;
796145522Sdarrenr	hv += dst.s_addr;
797255332Scy	rhv = hv;
798255332Scy	hv %= softn->ipf_nat_hostmap_sz;
799255332Scy	for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_hnext)
800255332Scy		if ((hm->hm_osrcip.s_addr == src.s_addr) &&
801255332Scy		    (hm->hm_odstip.s_addr == dst.s_addr) &&
802145522Sdarrenr		    ((np == NULL) || (np == hm->hm_ipnat)) &&
803145522Sdarrenr		    ((port == 0) || (port == hm->hm_port))) {
804255332Scy			softn->ipf_nat_stats.ns_hm_addref++;
80560852Sdarrenr			hm->hm_ref++;
80660852Sdarrenr			return hm;
80760852Sdarrenr		}
80860852Sdarrenr
809255332Scy	if (np == NULL) {
810255332Scy		softn->ipf_nat_stats.ns_hm_nullnp++;
811145522Sdarrenr		return NULL;
812255332Scy	}
813145522Sdarrenr
81460852Sdarrenr	KMALLOC(hm, hostmap_t *);
81560852Sdarrenr	if (hm) {
816255332Scy		hm->hm_next = softn->ipf_hm_maplist;
817255332Scy		hm->hm_pnext = &softn->ipf_hm_maplist;
818255332Scy		if (softn->ipf_hm_maplist != NULL)
819255332Scy			softn->ipf_hm_maplist->hm_pnext = &hm->hm_next;
820255332Scy		softn->ipf_hm_maplist = hm;
821255332Scy		hm->hm_hnext = softn->ipf_hm_maptable[hv];
822255332Scy		hm->hm_phnext = softn->ipf_hm_maptable + hv;
823255332Scy		if (softn->ipf_hm_maptable[hv] != NULL)
824255332Scy			softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext;
825255332Scy		softn->ipf_hm_maptable[hv] = hm;
82660852Sdarrenr		hm->hm_ipnat = np;
827255332Scy		np->in_use++;
828255332Scy		hm->hm_osrcip = src;
829255332Scy		hm->hm_odstip = dst;
830255332Scy		hm->hm_nsrcip = map;
831255332Scy		hm->hm_ndstip.s_addr = 0;
83260852Sdarrenr		hm->hm_ref = 1;
833145522Sdarrenr		hm->hm_port = port;
834255332Scy		hm->hm_hv = rhv;
835255332Scy		hm->hm_v = 4;
836255332Scy		softn->ipf_nat_stats.ns_hm_new++;
837255332Scy	} else {
838255332Scy		softn->ipf_nat_stats.ns_hm_newfail++;
83960852Sdarrenr	}
84060852Sdarrenr	return hm;
84153642Sguido}
84253642Sguido
84353642Sguido
844145522Sdarrenr/* ------------------------------------------------------------------------ */
845255332Scy/* Function:    ipf_nat_hostmapdel                                          */
846145522Sdarrenr/* Returns:     Nil                                                         */
847170268Sdarrenr/* Parameters:  hmp(I) - pointer to hostmap structure pointer               */
848145522Sdarrenr/* Write Locks: ipf_nat                                                     */
849145522Sdarrenr/*                                                                          */
850145522Sdarrenr/* Decrement the references to this hostmap structure by one.  If this      */
851145522Sdarrenr/* reaches zero then remove it and free it.                                 */
852145522Sdarrenr/* ------------------------------------------------------------------------ */
853255332Scyvoid
854255332Scyipf_nat_hostmapdel(softc, hmp)
855255332Scy	ipf_main_softc_t *softc;
856255332Scy	struct hostmap **hmp;
85760852Sdarrenr{
858170268Sdarrenr	struct hostmap *hm;
859170268Sdarrenr
860170268Sdarrenr	hm = *hmp;
861170268Sdarrenr	*hmp = NULL;
862170268Sdarrenr
863145522Sdarrenr	hm->hm_ref--;
86460852Sdarrenr	if (hm->hm_ref == 0) {
865255332Scy		ipf_nat_rule_deref(softc, &hm->hm_ipnat);
866170268Sdarrenr		if (hm->hm_hnext)
867170268Sdarrenr			hm->hm_hnext->hm_phnext = hm->hm_phnext;
868170268Sdarrenr		*hm->hm_phnext = hm->hm_hnext;
86960852Sdarrenr		if (hm->hm_next)
87060852Sdarrenr			hm->hm_next->hm_pnext = hm->hm_pnext;
87160852Sdarrenr		*hm->hm_pnext = hm->hm_next;
87260852Sdarrenr		KFREE(hm);
87360852Sdarrenr	}
87460852Sdarrenr}
87560852Sdarrenr
87660852Sdarrenr
877145522Sdarrenr/* ------------------------------------------------------------------------ */
878255332Scy/* Function:    ipf_fix_outcksum                                            */
879145522Sdarrenr/* Returns:     Nil                                                         */
880145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
881145522Sdarrenr/*              sp(I)  - location of 16bit checksum to update               */
882145522Sdarrenr/*              n((I)  - amount to adjust checksum by                       */
883145522Sdarrenr/*                                                                          */
884145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going out.                 */
885145522Sdarrenr/* ------------------------------------------------------------------------ */
886255332Scyvoid
887255332Scyipf_fix_outcksum(cksum, sp, n, partial)
888255332Scy	int cksum;
889255332Scy	u_short *sp;
890255332Scy	u_32_t n, partial;
89153642Sguido{
892145522Sdarrenr	u_short sumshort;
893145522Sdarrenr	u_32_t sum1;
89453642Sguido
895145522Sdarrenr	if (n == 0)
89653642Sguido		return;
897145522Sdarrenr
898255332Scy	if (cksum == 4) {
899255332Scy		*sp = 0;
90055929Sguido		return;
90155929Sguido	}
902255332Scy	if (cksum == 2) {
903255332Scy		sum1 = partial;
904255332Scy		sum1 = (sum1 & 0xffff) + (sum1 >> 16);
905255332Scy		*sp = htons(sum1);
906255332Scy		return;
907255332Scy	}
90853642Sguido	sum1 = (~ntohs(*sp)) & 0xffff;
90953642Sguido	sum1 += (n);
91053642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
91153642Sguido	/* Again */
91253642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
91353642Sguido	sumshort = ~(u_short)sum1;
91453642Sguido	*(sp) = htons(sumshort);
91553642Sguido}
91653642Sguido
91753642Sguido
918145522Sdarrenr/* ------------------------------------------------------------------------ */
919255332Scy/* Function:    ipf_fix_incksum                                             */
920145522Sdarrenr/* Returns:     Nil                                                         */
921145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
922145522Sdarrenr/*              sp(I)  - location of 16bit checksum to update               */
923145522Sdarrenr/*              n((I)  - amount to adjust checksum by                       */
924145522Sdarrenr/*                                                                          */
925145522Sdarrenr/* Adjusts the 16bit checksum by "n" for packets going in.                  */
926145522Sdarrenr/* ------------------------------------------------------------------------ */
927255332Scyvoid
928255332Scyipf_fix_incksum(cksum, sp, n, partial)
929255332Scy	int cksum;
930255332Scy	u_short *sp;
931255332Scy	u_32_t n, partial;
93253642Sguido{
933145522Sdarrenr	u_short sumshort;
934145522Sdarrenr	u_32_t sum1;
93553642Sguido
936145522Sdarrenr	if (n == 0)
93753642Sguido		return;
938145522Sdarrenr
939255332Scy	if (cksum == 4) {
940255332Scy		*sp = 0;
94155929Sguido		return;
94255929Sguido	}
943255332Scy	if (cksum == 2) {
944255332Scy		sum1 = partial;
945255332Scy		sum1 = (sum1 & 0xffff) + (sum1 >> 16);
946255332Scy		*sp = htons(sum1);
947255332Scy		return;
948255332Scy	}
949255332Scy
95053642Sguido	sum1 = (~ntohs(*sp)) & 0xffff;
95153642Sguido	sum1 += ~(n) & 0xffff;
95253642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
95353642Sguido	/* Again */
95453642Sguido	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
95553642Sguido	sumshort = ~(u_short)sum1;
95653642Sguido	*(sp) = htons(sumshort);
95753642Sguido}
95853642Sguido
95953642Sguido
960145522Sdarrenr/* ------------------------------------------------------------------------ */
961255332Scy/* Function:    ipf_fix_datacksum                                           */
962145522Sdarrenr/* Returns:     Nil                                                         */
963145522Sdarrenr/* Parameters:  sp(I)  - location of 16bit checksum to update               */
964145522Sdarrenr/*              n((I)  - amount to adjust checksum by                       */
965145522Sdarrenr/*                                                                          */
966145522Sdarrenr/* Fix_datacksum is used *only* for the adjustments of checksums in the     */
967145522Sdarrenr/* data section of an IP packet.                                            */
968145522Sdarrenr/*                                                                          */
969145522Sdarrenr/* The only situation in which you need to do this is when NAT'ing an       */
970145522Sdarrenr/* ICMP error message. Such a message, contains in its body the IP header   */
971145522Sdarrenr/* of the original IP packet, that causes the error.                        */
972145522Sdarrenr/*                                                                          */
973145522Sdarrenr/* You can't use fix_incksum or fix_outcksum in that case, because for the  */
974145522Sdarrenr/* kernel the data section of the ICMP error is just data, and no special   */
975145522Sdarrenr/* processing like hardware cksum or ntohs processing have been done by the */
976145522Sdarrenr/* kernel on the data section.                                              */
977145522Sdarrenr/* ------------------------------------------------------------------------ */
978255332Scyvoid
979255332Scyipf_fix_datacksum(sp, n)
980255332Scy	u_short *sp;
981255332Scy	u_32_t n;
98267614Sdarrenr{
983145522Sdarrenr	u_short sumshort;
984145522Sdarrenr	u_32_t sum1;
98567614Sdarrenr
986145522Sdarrenr	if (n == 0)
98767614Sdarrenr		return;
98867614Sdarrenr
98967614Sdarrenr	sum1 = (~ntohs(*sp)) & 0xffff;
99067614Sdarrenr	sum1 += (n);
99167614Sdarrenr	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
99267614Sdarrenr	/* Again */
99367614Sdarrenr	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
99467614Sdarrenr	sumshort = ~(u_short)sum1;
99567614Sdarrenr	*(sp) = htons(sumshort);
99667614Sdarrenr}
99767614Sdarrenr
99853642Sguido
999145522Sdarrenr/* ------------------------------------------------------------------------ */
1000255332Scy/* Function:    ipf_nat_ioctl                                               */
1001145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
1002255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1003255332Scy/*              data(I)  - pointer to ioctl data                            */
1004255332Scy/*              cmd(I)   - ioctl command integer                            */
1005255332Scy/*              mode(I)  - file mode bits used with open                    */
1006255332Scy/*              uid(I)   - uid of calling process                           */
1007255332Scy/*              ctx(I)   - pointer used as key for finding context          */
1008145522Sdarrenr/*                                                                          */
1009145522Sdarrenr/* Processes an ioctl call made to operate on the IP Filter NAT device.     */
1010145522Sdarrenr/* ------------------------------------------------------------------------ */
1011255332Scyint
1012255332Scyipf_nat_ioctl(softc, data, cmd, mode, uid, ctx)
1013255332Scy	ipf_main_softc_t *softc;
1014255332Scy	ioctlcmd_t cmd;
1015255332Scy	caddr_t data;
1016255332Scy	int mode, uid;
1017255332Scy	void *ctx;
101853642Sguido{
1019255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
102095418Sdarrenr	int error = 0, ret, arg, getlock;
1021255332Scy	ipnat_t *nat, *nt, *n;
102253642Sguido	ipnat_t natd;
1023170268Sdarrenr	SPL_INT(s);
102453642Sguido
1025255332Scy#if BSD_GE_YEAR(199306) && defined(_KERNEL)
1026255332Scy# if NETBSD_GE_REV(399002000)
1027170268Sdarrenr	if ((mode & FWRITE) &&
1028170268Sdarrenr	     kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL,
1029170268Sdarrenr				     KAUTH_REQ_NETWORK_FIREWALL_FW,
1030255332Scy				     NULL, NULL, NULL))
1031170268Sdarrenr# else
1032192895Sjamie#  if defined(__FreeBSD_version) && (__FreeBSD_version >= 500034)
1033255332Scy	if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE))
1034192895Sjamie#  else
1035255332Scy	if ((securelevel >= 3) && (mode & FWRITE))
1036192895Sjamie#  endif
1037255332Scy# endif
1038255332Scy	{
1039255332Scy		IPFERROR(60001);
1040170268Sdarrenr		return EPERM;
1041170268Sdarrenr	}
104253642Sguido#endif
104353642Sguido
1044145522Sdarrenr#if defined(__osf__) && defined(_KERNEL)
1045145522Sdarrenr	getlock = 0;
1046145522Sdarrenr#else
1047145522Sdarrenr	getlock = (mode & NAT_LOCKHELD) ? 0 : 1;
1048145522Sdarrenr#endif
1049145522Sdarrenr
1050255332Scy	n = NULL;
1051255332Scy	nt = NULL;
1052255332Scy	nat = NULL;
1053145522Sdarrenr
1054255332Scy	if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT) ||
1055255332Scy	    (cmd == (ioctlcmd_t)SIOCPURGENAT)) {
105695418Sdarrenr		if (mode & NAT_SYSSPACE) {
105795418Sdarrenr			bcopy(data, (char *)&natd, sizeof(natd));
1058255332Scy			nat = &natd;
105995418Sdarrenr			error = 0;
106095418Sdarrenr		} else {
1061255332Scy			bzero(&natd, sizeof(natd));
1062255332Scy			error = ipf_inobj(softc, data, NULL, &natd,
1063255332Scy					  IPFOBJ_IPNAT);
1064255332Scy			if (error != 0)
1065255332Scy				goto done;
1066255332Scy
1067255332Scy			if (natd.in_size < sizeof(ipnat_t)) {
1068255332Scy				error = EINVAL;
1069255332Scy				goto done;
1070255332Scy			}
1071255332Scy			KMALLOCS(nt, ipnat_t *, natd.in_size);
1072255332Scy			if (nt == NULL) {
1073255332Scy				IPFERROR(60070);
1074255332Scy				error = ENOMEM;
1075255332Scy				goto done;
1076255332Scy			}
1077255332Scy			bzero(nt, natd.in_size);
1078255332Scy			error = ipf_inobjsz(softc, data, nt, IPFOBJ_IPNAT,
1079255332Scy					    natd.in_size);
1080255332Scy			if (error)
1081255332Scy				goto done;
1082255332Scy			nat = nt;
108395418Sdarrenr		}
108453642Sguido
1085255332Scy		/*
1086255332Scy		 * For add/delete, look to see if the NAT entry is
1087255332Scy		 * already present
1088255332Scy		 */
108953642Sguido		nat->in_flags &= IPN_USERFLAGS;
109053642Sguido		if ((nat->in_redir & NAT_MAPBLK) == 0) {
1091255332Scy			if (nat->in_osrcatype == FRI_NORMAL ||
1092255332Scy			    nat->in_osrcatype == FRI_NONE)
1093255332Scy				nat->in_osrcaddr &= nat->in_osrcmsk;
1094255332Scy			if (nat->in_odstatype == FRI_NORMAL ||
1095255332Scy			    nat->in_odstatype == FRI_NONE)
1096255332Scy				nat->in_odstaddr &= nat->in_odstmsk;
1097255332Scy			if ((nat->in_flags & (IPN_SPLIT|IPN_SIPRANGE)) == 0) {
1098255332Scy				if (nat->in_nsrcatype == FRI_NORMAL)
1099255332Scy					nat->in_nsrcaddr &= nat->in_nsrcmsk;
1100255332Scy				if (nat->in_ndstatype == FRI_NORMAL)
1101255332Scy					nat->in_ndstaddr &= nat->in_ndstmsk;
1102255332Scy			}
110353642Sguido		}
1104255332Scy
1105255332Scy		error = ipf_nat_rule_init(softc, softn, nat);
1106255332Scy		if (error != 0)
1107255332Scy			goto done;
1108255332Scy
1109255332Scy		MUTEX_ENTER(&softn->ipf_nat_io);
1110255332Scy		for (n = softn->ipf_nat_list; n != NULL; n = n->in_next)
1111255332Scy			if (ipf_nat_cmp_rules(nat, n) == 0)
111253642Sguido				break;
111353642Sguido	}
111453642Sguido
111553642Sguido	switch (cmd)
111653642Sguido	{
111755929Sguido#ifdef  IPFILTER_LOG
111855929Sguido	case SIOCIPFFB :
111960852Sdarrenr	{
112060852Sdarrenr		int tmp;
112160852Sdarrenr
1122255332Scy		if (!(mode & FWRITE)) {
1123255332Scy			IPFERROR(60002);
112455929Sguido			error = EPERM;
1125255332Scy		} else {
1126255332Scy			tmp = ipf_log_clear(softc, IPL_LOGNAT);
1127255332Scy			error = BCOPYOUT(&tmp, data, sizeof(tmp));
1128255332Scy			if (error != 0) {
1129255332Scy				IPFERROR(60057);
1130170268Sdarrenr				error = EFAULT;
1131255332Scy			}
113260852Sdarrenr		}
113355929Sguido		break;
113460852Sdarrenr	}
1135170268Sdarrenr
1136145522Sdarrenr	case SIOCSETLG :
1137255332Scy		if (!(mode & FWRITE)) {
1138255332Scy			IPFERROR(60003);
1139145522Sdarrenr			error = EPERM;
1140255332Scy		} else {
1141255332Scy			error = BCOPYIN(data, &softn->ipf_nat_logging,
1142255332Scy					sizeof(softn->ipf_nat_logging));
1143170268Sdarrenr			if (error != 0)
1144170268Sdarrenr				error = EFAULT;
1145145522Sdarrenr		}
1146145522Sdarrenr		break;
1147170268Sdarrenr
1148145522Sdarrenr	case SIOCGETLG :
1149255332Scy		error = BCOPYOUT(&softn->ipf_nat_logging, data,
1150255332Scy				 sizeof(softn->ipf_nat_logging));
1151255332Scy		if (error != 0) {
1152255332Scy			IPFERROR(60004);
1153170268Sdarrenr			error = EFAULT;
1154255332Scy		}
1155145522Sdarrenr		break;
1156170268Sdarrenr
1157145522Sdarrenr	case FIONREAD :
1158255332Scy		arg = ipf_log_bytesused(softc, IPL_LOGNAT);
1159170268Sdarrenr		error = BCOPYOUT(&arg, data, sizeof(arg));
1160255332Scy		if (error != 0) {
1161255332Scy			IPFERROR(60005);
1162170268Sdarrenr			error = EFAULT;
1163255332Scy		}
1164145522Sdarrenr		break;
116555929Sguido#endif
116653642Sguido	case SIOCADNAT :
116753642Sguido		if (!(mode & FWRITE)) {
1168255332Scy			IPFERROR(60006);
116953642Sguido			error = EPERM;
1170145522Sdarrenr		} else if (n != NULL) {
1171255332Scy			natd.in_flineno = n->in_flineno;
1172255332Scy			(void) ipf_outobj(softc, data, &natd, IPFOBJ_IPNAT);
1173255332Scy			IPFERROR(60007);
117453642Sguido			error = EEXIST;
1175145522Sdarrenr		} else if (nt == NULL) {
1176255332Scy			IPFERROR(60008);
1177145522Sdarrenr			error = ENOMEM;
117853642Sguido		}
1179145522Sdarrenr		if (error != 0) {
1180255332Scy			MUTEX_EXIT(&softn->ipf_nat_io);
118153642Sguido			break;
118253642Sguido		}
1183255332Scy		if (nat != nt)
1184255332Scy			bcopy((char *)nat, (char *)nt, sizeof(*n));
1185255332Scy		error = ipf_nat_siocaddnat(softc, softn, nt, getlock);
1186255332Scy		MUTEX_EXIT(&softn->ipf_nat_io);
1187255332Scy		if (error == 0) {
1188255332Scy			nat = NULL;
1189145522Sdarrenr			nt = NULL;
1190255332Scy		}
119153642Sguido		break;
1192170268Sdarrenr
119353642Sguido	case SIOCRMNAT :
1194255332Scy	case SIOCPURGENAT :
119553642Sguido		if (!(mode & FWRITE)) {
1196255332Scy			IPFERROR(60009);
119753642Sguido			error = EPERM;
119853642Sguido			n = NULL;
1199145522Sdarrenr		} else if (n == NULL) {
1200255332Scy			IPFERROR(60010);
1201145522Sdarrenr			error = ESRCH;
120253642Sguido		}
1203145522Sdarrenr
1204145522Sdarrenr		if (error != 0) {
1205255332Scy			MUTEX_EXIT(&softn->ipf_nat_io);
120653642Sguido			break;
120753642Sguido		}
1208255332Scy		if (cmd == (ioctlcmd_t)SIOCPURGENAT) {
1209255332Scy			error = ipf_outobjsz(softc, data, n, IPFOBJ_IPNAT,
1210255332Scy					     n->in_size);
1211255332Scy			if (error) {
1212255332Scy				MUTEX_EXIT(&softn->ipf_nat_io);
1213255332Scy				goto done;
1214255332Scy			}
1215255332Scy			n->in_flags |= IPN_PURGE;
1216255332Scy		}
1217255332Scy		ipf_nat_siocdelnat(softc, softn, n, getlock);
1218145522Sdarrenr
1219255332Scy		MUTEX_EXIT(&softn->ipf_nat_io);
122053642Sguido		n = NULL;
122153642Sguido		break;
1222170268Sdarrenr
122353642Sguido	case SIOCGNATS :
1224255332Scy	    {
1225255332Scy		natstat_t *nsp = &softn->ipf_nat_stats;
1226255332Scy
1227255332Scy		nsp->ns_side[0].ns_table = softn->ipf_nat_table[0];
1228255332Scy		nsp->ns_side[1].ns_table = softn->ipf_nat_table[1];
1229255332Scy		nsp->ns_list = softn->ipf_nat_list;
1230255332Scy		nsp->ns_maptable = softn->ipf_hm_maptable;
1231255332Scy		nsp->ns_maplist = softn->ipf_hm_maplist;
1232255332Scy		nsp->ns_nattab_sz = softn->ipf_nat_table_sz;
1233255332Scy		nsp->ns_nattab_max = softn->ipf_nat_table_max;
1234255332Scy		nsp->ns_rultab_sz = softn->ipf_nat_maprules_sz;
1235255332Scy		nsp->ns_rdrtab_sz = softn->ipf_nat_rdrrules_sz;
1236255332Scy		nsp->ns_hostmap_sz = softn->ipf_nat_hostmap_sz;
1237255332Scy		nsp->ns_instances = softn->ipf_nat_instances;
1238255332Scy		nsp->ns_ticks = softc->ipf_ticks;
1239255332Scy#ifdef IPFILTER_LOGGING
1240255332Scy		nsp->ns_log_ok = ipf_log_logok(softc, IPF_LOGNAT);
1241255332Scy		nsp->ns_log_fail = ipf_log_failures(softc, IPF_LOGNAT);
1242255332Scy#else
1243255332Scy		nsp->ns_log_ok = 0;
1244255332Scy		nsp->ns_log_fail = 0;
1245255332Scy#endif
1246255332Scy		error = ipf_outobj(softc, data, nsp, IPFOBJ_NATSTAT);
124753642Sguido		break;
1248255332Scy	    }
1249170268Sdarrenr
125053642Sguido	case SIOCGNATL :
125153642Sguido	    {
125253642Sguido		natlookup_t nl;
125353642Sguido
1254255332Scy		error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP);
1255145522Sdarrenr		if (error == 0) {
1256173181Sdarrenr			void *ptr;
1257173181Sdarrenr
1258173181Sdarrenr			if (getlock) {
1259255332Scy				READ_ENTER(&softc->ipf_nat);
1260173181Sdarrenr			}
1261255332Scy
1262255332Scy			switch (nl.nl_v)
1263255332Scy			{
1264255332Scy			case 4 :
1265255332Scy				ptr = ipf_nat_lookupredir(&nl);
1266255332Scy				break;
1267255332Scy#ifdef USE_INET6
1268255332Scy			case 6 :
1269255332Scy				ptr = ipf_nat6_lookupredir(&nl);
1270255332Scy				break;
1271255332Scy#endif
1272255332Scy			default:
1273255332Scy				ptr = NULL;
1274255332Scy				break;
1275255332Scy			}
1276255332Scy
1277173181Sdarrenr			if (getlock) {
1278255332Scy				RWLOCK_EXIT(&softc->ipf_nat);
1279173181Sdarrenr			}
1280173181Sdarrenr			if (ptr != NULL) {
1281255332Scy				error = ipf_outobj(softc, data, &nl,
1282255332Scy						   IPFOBJ_NATLOOKUP);
1283145522Sdarrenr			} else {
1284255332Scy				IPFERROR(60011);
1285145522Sdarrenr				error = ESRCH;
1286145522Sdarrenr			}
1287145522Sdarrenr		}
128853642Sguido		break;
128953642Sguido	    }
1290170268Sdarrenr
129160852Sdarrenr	case SIOCIPFFL :	/* old SIOCFLNAT & SIOCCNATL */
129253642Sguido		if (!(mode & FWRITE)) {
1293255332Scy			IPFERROR(60012);
129453642Sguido			error = EPERM;
129553642Sguido			break;
129653642Sguido		}
1297145522Sdarrenr		if (getlock) {
1298255332Scy			WRITE_ENTER(&softc->ipf_nat);
1299145522Sdarrenr		}
1300170268Sdarrenr
1301170268Sdarrenr		error = BCOPYIN(data, &arg, sizeof(arg));
1302255332Scy		if (error != 0) {
1303255332Scy			IPFERROR(60013);
1304170268Sdarrenr			error = EFAULT;
1305255332Scy		} else {
1306170268Sdarrenr			if (arg == 0)
1307255332Scy				ret = ipf_nat_flushtable(softc, softn);
1308170268Sdarrenr			else if (arg == 1)
1309255332Scy				ret = ipf_nat_clearlist(softc, softn);
1310170268Sdarrenr			else
1311255332Scy				ret = ipf_nat_extraflush(softc, softn, arg);
1312255332Scy			ipf_proxy_flush(softc->ipf_proxy_soft, arg);
1313170268Sdarrenr		}
1314170268Sdarrenr
1315145522Sdarrenr		if (getlock) {
1316255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
131760852Sdarrenr		}
1318145522Sdarrenr		if (error == 0) {
1319170268Sdarrenr			error = BCOPYOUT(&ret, data, sizeof(ret));
1320145522Sdarrenr		}
132153642Sguido		break;
1322170268Sdarrenr
1323255332Scy	case SIOCMATCHFLUSH :
1324255332Scy		if (!(mode & FWRITE)) {
1325255332Scy			IPFERROR(60014);
1326255332Scy			error = EPERM;
1327255332Scy			break;
1328255332Scy		}
1329255332Scy		if (getlock) {
1330255332Scy			WRITE_ENTER(&softc->ipf_nat);
1331255332Scy		}
1332255332Scy
1333255332Scy		error = ipf_nat_matchflush(softc, softn, data);
1334255332Scy
1335255332Scy		if (getlock) {
1336255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
1337255332Scy		}
1338255332Scy		break;
1339255332Scy
1340145522Sdarrenr	case SIOCPROXY :
1341255332Scy		error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx);
1342145522Sdarrenr		break;
1343170268Sdarrenr
134460852Sdarrenr	case SIOCSTLCK :
1345153876Sguido		if (!(mode & FWRITE)) {
1346255332Scy			IPFERROR(60015);
1347153876Sguido			error = EPERM;
1348153876Sguido		} else {
1349255332Scy			error = ipf_lock(data, &softn->ipf_nat_lock);
1350153876Sguido		}
135153642Sguido		break;
1352170268Sdarrenr
135360852Sdarrenr	case SIOCSTPUT :
1354153876Sguido		if ((mode & FWRITE) != 0) {
1355255332Scy			error = ipf_nat_putent(softc, data, getlock);
1356145522Sdarrenr		} else {
1357255332Scy			IPFERROR(60016);
135860852Sdarrenr			error = EACCES;
1359145522Sdarrenr		}
136060852Sdarrenr		break;
1361170268Sdarrenr
136260852Sdarrenr	case SIOCSTGSZ :
1363255332Scy		if (softn->ipf_nat_lock) {
1364255332Scy			error = ipf_nat_getsz(softc, data, getlock);
1365255332Scy		} else {
1366255332Scy			IPFERROR(60017);
136760852Sdarrenr			error = EACCES;
1368255332Scy		}
136960852Sdarrenr		break;
1370170268Sdarrenr
137160852Sdarrenr	case SIOCSTGET :
1372255332Scy		if (softn->ipf_nat_lock) {
1373255332Scy			error = ipf_nat_getent(softc, data, getlock);
1374255332Scy		} else {
1375255332Scy			IPFERROR(60018);
137660852Sdarrenr			error = EACCES;
1377255332Scy		}
137860852Sdarrenr		break;
1379170268Sdarrenr
1380170268Sdarrenr	case SIOCGENITER :
1381170268Sdarrenr	    {
1382170268Sdarrenr		ipfgeniter_t iter;
1383170268Sdarrenr		ipftoken_t *token;
1384255332Scy		ipfobj_t obj;
1385170268Sdarrenr
1386255332Scy		error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER);
1387255332Scy		if (error != 0)
1388255332Scy			break;
1389255332Scy
1390170268Sdarrenr		SPL_SCHED(s);
1391255332Scy		token = ipf_token_find(softc, iter.igi_type, uid, ctx);
1392255332Scy		if (token != NULL) {
1393255332Scy			error  = ipf_nat_iterator(softc, token, &iter, &obj);
1394255332Scy			WRITE_ENTER(&softc->ipf_tokens);
1395255332Scy			ipf_token_deref(softc, token);
1396255332Scy			RWLOCK_EXIT(&softc->ipf_tokens);
1397170268Sdarrenr		}
1398170268Sdarrenr		SPL_X(s);
1399170268Sdarrenr		break;
1400170268Sdarrenr	    }
1401170268Sdarrenr
1402170268Sdarrenr	case SIOCIPFDELTOK :
1403255332Scy		error = BCOPYIN(data, &arg, sizeof(arg));
1404170268Sdarrenr		if (error == 0) {
1405170268Sdarrenr			SPL_SCHED(s);
1406255332Scy			error = ipf_token_del(softc, arg, uid, ctx);
1407170268Sdarrenr			SPL_X(s);
1408170268Sdarrenr		} else {
1409255332Scy			IPFERROR(60019);
1410170268Sdarrenr			error = EFAULT;
1411170268Sdarrenr		}
1412170268Sdarrenr		break;
1413170268Sdarrenr
1414170268Sdarrenr	case SIOCGTQTAB :
1415255332Scy		error = ipf_outobj(softc, data, softn->ipf_nat_tcptq,
1416255332Scy				   IPFOBJ_STATETQTAB);
1417170268Sdarrenr		break;
1418170268Sdarrenr
1419172776Sdarrenr	case SIOCGTABL :
1420255332Scy		error = ipf_nat_gettable(softc, softn, data);
1421172776Sdarrenr		break;
1422172776Sdarrenr
142353642Sguido	default :
1424255332Scy		IPFERROR(60020);
142553642Sguido		error = EINVAL;
142653642Sguido		break;
142753642Sguido	}
142860852Sdarrenrdone:
1429255332Scy	if (nat != NULL)
1430255332Scy		ipf_nat_rule_fini(softc, nat);
1431170268Sdarrenr	if (nt != NULL)
1432255332Scy		KFREES(nt, nt->in_size);
143353642Sguido	return error;
143453642Sguido}
143553642Sguido
143653642Sguido
1437145522Sdarrenr/* ------------------------------------------------------------------------ */
1438255332Scy/* Function:    ipf_nat_siocaddnat                                          */
1439145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
1440255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1441255332Scy/*              softn(I) - pointer to NAT context structure                 */
1442255332Scy/*              n(I)       - pointer to new NAT rule                        */
1443145522Sdarrenr/*              np(I)      - pointer to where to insert new NAT rule        */
1444255332Scy/*              getlock(I) - flag indicating if lock on  is held            */
1445255332Scy/* Mutex Locks: ipf_nat_io                                                   */
1446145522Sdarrenr/*                                                                          */
1447145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1448145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1449145522Sdarrenr/* NAT rule table(s).                                                       */
1450145522Sdarrenr/* ------------------------------------------------------------------------ */
1451255332Scystatic int
1452255332Scyipf_nat_siocaddnat(softc, softn, n, getlock)
1453255332Scy	ipf_main_softc_t *softc;
1454255332Scy	ipf_nat_softc_t *softn;
1455255332Scy	ipnat_t *n;
1456255332Scy	int getlock;
1457145522Sdarrenr{
1458255332Scy	int error = 0;
1459145522Sdarrenr
1460255332Scy	if (ipf_nat_resolverule(softc, n) != 0) {
1461255332Scy		IPFERROR(60022);
1462161356Sguido		return ENOENT;
1463255332Scy	}
1464145522Sdarrenr
1465255332Scy	if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) {
1466255332Scy		IPFERROR(60023);
1467145522Sdarrenr		return EINVAL;
1468255332Scy	}
1469145522Sdarrenr
1470255332Scy	if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) {
1471145522Sdarrenr		/*
1472255332Scy		 * Prerecord whether or not the destination of the divert
1473255332Scy		 * is local or not to the interface the packet is going
1474255332Scy		 * to be sent out.
1475145522Sdarrenr		 */
1476255332Scy		n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1],
1477255332Scy						n->in_ifps[1], &n->in_ndstip6);
1478145522Sdarrenr	}
1479145522Sdarrenr
1480145522Sdarrenr	if (getlock) {
1481255332Scy		WRITE_ENTER(&softc->ipf_nat);
1482145522Sdarrenr	}
1483145522Sdarrenr	n->in_next = NULL;
1484255332Scy	n->in_pnext = softn->ipf_nat_list_tail;
1485255332Scy	*n->in_pnext = n;
1486255332Scy	softn->ipf_nat_list_tail = &n->in_next;
1487255332Scy	n->in_use++;
1488145522Sdarrenr
1489145522Sdarrenr	if (n->in_redir & NAT_REDIRECT) {
1490145522Sdarrenr		n->in_flags &= ~IPN_NOTDST;
1491255332Scy		switch (n->in_v[0])
1492255332Scy		{
1493255332Scy		case 4 :
1494255332Scy			ipf_nat_addrdr(softn, n);
1495255332Scy			break;
1496255332Scy#ifdef USE_INET6
1497255332Scy		case 6 :
1498255332Scy			ipf_nat6_addrdr(softn, n);
1499255332Scy			break;
1500255332Scy#endif
1501255332Scy		default :
1502255332Scy			break;
1503255332Scy		}
1504255332Scy		ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_rdr);
1505145522Sdarrenr	}
1506255332Scy
1507145522Sdarrenr	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
1508145522Sdarrenr		n->in_flags &= ~IPN_NOTSRC;
1509255332Scy		switch (n->in_v[0])
1510255332Scy		{
1511255332Scy		case 4 :
1512255332Scy			ipf_nat_addmap(softn, n);
1513255332Scy			break;
1514255332Scy#ifdef USE_INET6
1515255332Scy		case 6 :
1516255332Scy			ipf_nat6_addmap(softn, n);
1517255332Scy			break;
1518255332Scy#endif
1519255332Scy		default :
1520255332Scy			break;
1521255332Scy		}
1522255332Scy		ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_map);
1523145522Sdarrenr	}
1524255332Scy
1525255332Scy	if (n->in_age[0] != 0)
1526255332Scy		n->in_tqehead[0] = ipf_addtimeoutqueue(softc,
1527255332Scy						       &softn->ipf_nat_utqe,
1528255332Scy						       n->in_age[0]);
1529255332Scy
1530255332Scy	if (n->in_age[1] != 0)
1531255332Scy		n->in_tqehead[1] = ipf_addtimeoutqueue(softc,
1532255332Scy						       &softn->ipf_nat_utqe,
1533255332Scy						       n->in_age[1]);
1534255332Scy
1535170268Sdarrenr	MUTEX_INIT(&n->in_lock, "ipnat rule lock");
1536170268Sdarrenr
1537145522Sdarrenr	n = NULL;
1538255332Scy	ATOMIC_INC32(softn->ipf_nat_stats.ns_rules);
1539255332Scy#if SOLARIS && !defined(INSTANCES)
1540145522Sdarrenr	pfil_delayed_copy = 0;
1541145522Sdarrenr#endif
1542145522Sdarrenr	if (getlock) {
1543255332Scy		RWLOCK_EXIT(&softc->ipf_nat);			/* WRITE */
1544145522Sdarrenr	}
1545145522Sdarrenr
1546145522Sdarrenr	return error;
1547145522Sdarrenr}
1548145522Sdarrenr
1549145522Sdarrenr
1550145522Sdarrenr/* ------------------------------------------------------------------------ */
1551255332Scy/* Function:    ipf_nat_ruleaddrinit                                        */
1552255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1553255332Scy/*              softn(I) - pointer to NAT context structure                 */
1554255332Scy/*              n(I)     - pointer to NAT rule                              */
1555255332Scy/*                                                                          */
1556255332Scy/* Initialise all of the NAT address structures in a NAT rule.              */
1557255332Scy/* ------------------------------------------------------------------------ */
1558255332Scystatic int
1559255332Scyipf_nat_ruleaddrinit(softc, softn, n)
1560255332Scy	ipf_main_softc_t *softc;
1561255332Scy	ipf_nat_softc_t *softn;
1562255332Scy	ipnat_t *n;
1563255332Scy{
1564255332Scy	int idx, error;
1565255332Scy
1566255332Scy	if ((n->in_ndst.na_atype == FRI_LOOKUP) &&
1567255332Scy	    (n->in_ndst.na_type != IPLT_DSTLIST)) {
1568255332Scy		IPFERROR(60071);
1569255332Scy		return EINVAL;
1570255332Scy	}
1571255332Scy	if ((n->in_nsrc.na_atype == FRI_LOOKUP) &&
1572255332Scy	    (n->in_nsrc.na_type != IPLT_DSTLIST)) {
1573255332Scy		IPFERROR(60069);
1574255332Scy		return EINVAL;
1575255332Scy	}
1576255332Scy
1577255332Scy	if (n->in_redir == NAT_BIMAP) {
1578255332Scy		n->in_ndstaddr = n->in_osrcaddr;
1579255332Scy		n->in_ndstmsk = n->in_osrcmsk;
1580255332Scy		n->in_odstaddr = n->in_nsrcaddr;
1581255332Scy		n->in_odstmsk = n->in_nsrcmsk;
1582255332Scy
1583255332Scy	}
1584255332Scy
1585255332Scy	if (n->in_redir & NAT_REDIRECT)
1586255332Scy		idx = 1;
1587255332Scy	else
1588255332Scy		idx = 0;
1589255332Scy	/*
1590255332Scy	 * Initialise all of the address fields.
1591255332Scy	 */
1592255332Scy	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 1,
1593255332Scy				     n->in_ifps[idx]);
1594255332Scy	if (error != 0)
1595255332Scy		return error;
1596255332Scy
1597255332Scy	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 1,
1598255332Scy				     n->in_ifps[idx]);
1599255332Scy	if (error != 0)
1600255332Scy		return error;
1601255332Scy
1602255332Scy	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1,
1603255332Scy				     n->in_ifps[idx]);
1604255332Scy	if (error != 0)
1605255332Scy		return error;
1606255332Scy
1607255332Scy	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 1,
1608255332Scy				     n->in_ifps[idx]);
1609255332Scy	if (error != 0)
1610255332Scy		return error;
1611255332Scy
1612255332Scy	if (n->in_redir & NAT_DIVERTUDP)
1613255332Scy		ipf_nat_builddivertmp(softn, n);
1614255332Scy
1615255332Scy	return 0;
1616255332Scy}
1617255332Scy
1618255332Scy
1619255332Scy/* ------------------------------------------------------------------------ */
1620255332Scy/* Function:    ipf_nat_resolvrule                                          */
1621145522Sdarrenr/* Returns:     Nil                                                         */
1622255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1623255332Scy/*              n(I)     - pointer to NAT rule                              */
1624145522Sdarrenr/*                                                                          */
1625145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1626145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1627145522Sdarrenr/* NAT rule table(s).                                                       */
1628145522Sdarrenr/* ------------------------------------------------------------------------ */
1629255332Scystatic int
1630255332Scyipf_nat_resolverule(softc, n)
1631255332Scy	ipf_main_softc_t *softc;
1632255332Scy	ipnat_t *n;
1633145522Sdarrenr{
1634255332Scy	char *base;
1635145522Sdarrenr
1636255332Scy	base = n->in_names;
1637255332Scy
1638255332Scy	n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0],
1639255332Scy				       n->in_v[0]);
1640255332Scy
1641255332Scy	if (n->in_ifnames[1] == -1) {
1642255332Scy		n->in_ifnames[1] = n->in_ifnames[0];
1643145522Sdarrenr		n->in_ifps[1] = n->in_ifps[0];
1644145522Sdarrenr	} else {
1645255332Scy		n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1],
1646255332Scy					       n->in_v[1]);
1647145522Sdarrenr	}
1648145522Sdarrenr
1649255332Scy	if (n->in_plabel != -1) {
1650255332Scy		if (n->in_redir & NAT_REDIRECT)
1651255332Scy			n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft,
1652255332Scy						     n->in_pr[0],
1653255332Scy						     base + n->in_plabel);
1654255332Scy		else
1655255332Scy			n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft,
1656255332Scy						     n->in_pr[1],
1657255332Scy						     base + n->in_plabel);
1658161356Sguido		if (n->in_apr == NULL)
1659161356Sguido			return -1;
1660145522Sdarrenr	}
1661161356Sguido	return 0;
1662145522Sdarrenr}
1663145522Sdarrenr
1664145522Sdarrenr
1665145522Sdarrenr/* ------------------------------------------------------------------------ */
1666255332Scy/* Function:    ipf_nat_siocdelnat                                          */
1667145522Sdarrenr/* Returns:     int - 0 == success, != 0 == failure                         */
1668255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
1669255332Scy/*              softn(I)   - pointer to NAT context structure               */
1670255332Scy/*              n(I)       - pointer to new NAT rule                        */
1671255332Scy/*              getlock(I) - flag indicating if lock on  is held            */
1672255332Scy/* Mutex Locks: ipf_nat_io                                                  */
1673145522Sdarrenr/*                                                                          */
1674145522Sdarrenr/* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
1675145522Sdarrenr/* from information passed to the kernel, then add it  to the appropriate   */
1676145522Sdarrenr/* NAT rule table(s).                                                       */
1677145522Sdarrenr/* ------------------------------------------------------------------------ */
1678255332Scystatic void
1679255332Scyipf_nat_siocdelnat(softc, softn, n, getlock)
1680255332Scy	ipf_main_softc_t *softc;
1681255332Scy	ipf_nat_softc_t *softn;
1682255332Scy	ipnat_t *n;
1683255332Scy	int getlock;
1684145522Sdarrenr{
1685145522Sdarrenr	if (getlock) {
1686255332Scy		WRITE_ENTER(&softc->ipf_nat);
1687145522Sdarrenr	}
1688145522Sdarrenr
1689255332Scy	ipf_nat_delrule(softc, softn, n, 1);
1690145522Sdarrenr
1691145522Sdarrenr	if (getlock) {
1692255332Scy		RWLOCK_EXIT(&softc->ipf_nat);			/* READ/WRITE */
1693145522Sdarrenr	}
1694145522Sdarrenr}
1695145522Sdarrenr
1696145522Sdarrenr
1697145522Sdarrenr/* ------------------------------------------------------------------------ */
1698255332Scy/* Function:    ipf_nat_getsz                                               */
1699145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1700255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
1701255332Scy/*              data(I)    - pointer to natget structure with kernel        */
1702255332Scy/*                           pointer get the size of.                       */
1703255332Scy/*              getlock(I) - flag indicating whether or not the caller      */
1704255332Scy/*                           holds a lock on ipf_nat                        */
1705145522Sdarrenr/*                                                                          */
1706145522Sdarrenr/* Handle SIOCSTGSZ.                                                        */
1707145522Sdarrenr/* Return the size of the nat list entry to be copied back to user space.   */
1708145522Sdarrenr/* The size of the entry is stored in the ng_sz field and the enture natget */
1709145522Sdarrenr/* structure is copied back to the user.                                    */
1710145522Sdarrenr/* ------------------------------------------------------------------------ */
1711255332Scystatic int
1712255332Scyipf_nat_getsz(softc, data, getlock)
1713255332Scy	ipf_main_softc_t *softc;
1714255332Scy	caddr_t data;
1715255332Scy	int getlock;
171660852Sdarrenr{
1717255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
171860852Sdarrenr	ap_session_t *aps;
171960852Sdarrenr	nat_t *nat, *n;
172060852Sdarrenr	natget_t ng;
1721255332Scy	int error;
172260852Sdarrenr
1723255332Scy	error = BCOPYIN(data, &ng, sizeof(ng));
1724255332Scy	if (error != 0) {
1725255332Scy		IPFERROR(60024);
1726170268Sdarrenr		return EFAULT;
1727255332Scy	}
172860852Sdarrenr
1729173181Sdarrenr	if (getlock) {
1730255332Scy		READ_ENTER(&softc->ipf_nat);
1731173181Sdarrenr	}
1732173181Sdarrenr
173360852Sdarrenr	nat = ng.ng_ptr;
173460852Sdarrenr	if (!nat) {
1735255332Scy		nat = softn->ipf_nat_instances;
173660852Sdarrenr		ng.ng_sz = 0;
1737145522Sdarrenr		/*
1738145522Sdarrenr		 * Empty list so the size returned is 0.  Simple.
1739145522Sdarrenr		 */
174060852Sdarrenr		if (nat == NULL) {
1741173181Sdarrenr			if (getlock) {
1742255332Scy				RWLOCK_EXIT(&softc->ipf_nat);
1743173181Sdarrenr			}
1744255332Scy			error = BCOPYOUT(&ng, data, sizeof(ng));
1745255332Scy			if (error != 0) {
1746255332Scy				IPFERROR(60025);
1747170268Sdarrenr				return EFAULT;
1748255332Scy			}
1749145522Sdarrenr			return 0;
175060852Sdarrenr		}
175160852Sdarrenr	} else {
175260852Sdarrenr		/*
175360852Sdarrenr		 * Make sure the pointer we're copying from exists in the
175460852Sdarrenr		 * current list of entries.  Security precaution to prevent
175560852Sdarrenr		 * copying of random kernel data.
175660852Sdarrenr		 */
1757255332Scy		for (n = softn->ipf_nat_instances; n; n = n->nat_next)
175860852Sdarrenr			if (n == nat)
175960852Sdarrenr				break;
1760173181Sdarrenr		if (n == NULL) {
1761173181Sdarrenr			if (getlock) {
1762255332Scy				RWLOCK_EXIT(&softc->ipf_nat);
1763173181Sdarrenr			}
1764255332Scy			IPFERROR(60026);
176560852Sdarrenr			return ESRCH;
1766173181Sdarrenr		}
176760852Sdarrenr	}
176860852Sdarrenr
1769145522Sdarrenr	/*
1770145522Sdarrenr	 * Incluse any space required for proxy data structures.
1771145522Sdarrenr	 */
177260852Sdarrenr	ng.ng_sz = sizeof(nat_save_t);
177360852Sdarrenr	aps = nat->nat_aps;
1774145522Sdarrenr	if (aps != NULL) {
1775145522Sdarrenr		ng.ng_sz += sizeof(ap_session_t) - 4;
1776145522Sdarrenr		if (aps->aps_data != 0)
1777145522Sdarrenr			ng.ng_sz += aps->aps_psiz;
177860852Sdarrenr	}
1779173181Sdarrenr	if (getlock) {
1780255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
1781173181Sdarrenr	}
178260852Sdarrenr
1783255332Scy	error = BCOPYOUT(&ng, data, sizeof(ng));
1784255332Scy	if (error != 0) {
1785255332Scy		IPFERROR(60027);
1786170268Sdarrenr		return EFAULT;
1787255332Scy	}
1788145522Sdarrenr	return 0;
178960852Sdarrenr}
179060852Sdarrenr
179160852Sdarrenr
1792145522Sdarrenr/* ------------------------------------------------------------------------ */
1793255332Scy/* Function:    ipf_nat_getent                                              */
1794145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1795255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
1796255332Scy/*              data(I)    - pointer to natget structure with kernel pointer*/
1797255332Scy/*                           to NAT structure to copy out.                  */
1798255332Scy/*              getlock(I) - flag indicating whether or not the caller      */
1799255332Scy/*                           holds a lock on ipf_nat                        */
1800145522Sdarrenr/*                                                                          */
1801145522Sdarrenr/* Handle SIOCSTGET.                                                        */
1802145522Sdarrenr/* Copies out NAT entry to user space.  Any additional data held for a      */
1803145522Sdarrenr/* proxy is also copied, as to is the NAT rule which was responsible for it */
1804145522Sdarrenr/* ------------------------------------------------------------------------ */
1805255332Scystatic int
1806255332Scyipf_nat_getent(softc, data, getlock)
1807255332Scy	ipf_main_softc_t *softc;
1808255332Scy	caddr_t data;
1809255332Scy	int getlock;
181060852Sdarrenr{
1811255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1812145522Sdarrenr	int error, outsize;
181360852Sdarrenr	ap_session_t *aps;
1814145522Sdarrenr	nat_save_t *ipn, ipns;
1815145522Sdarrenr	nat_t *n, *nat;
181660852Sdarrenr
1817255332Scy	error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE);
1818145522Sdarrenr	if (error != 0)
1819145522Sdarrenr		return error;
182060852Sdarrenr
1821255332Scy	if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) {
1822255332Scy		IPFERROR(60028);
1823145522Sdarrenr		return EINVAL;
1824255332Scy	}
1825145522Sdarrenr
1826145522Sdarrenr	KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize);
1827255332Scy	if (ipn == NULL) {
1828255332Scy		IPFERROR(60029);
1829145522Sdarrenr		return ENOMEM;
1830255332Scy	}
1831145522Sdarrenr
1832173181Sdarrenr	if (getlock) {
1833255332Scy		READ_ENTER(&softc->ipf_nat);
1834173181Sdarrenr	}
1835173181Sdarrenr
1836145522Sdarrenr	ipn->ipn_dsize = ipns.ipn_dsize;
1837145522Sdarrenr	nat = ipns.ipn_next;
1838145522Sdarrenr	if (nat == NULL) {
1839255332Scy		nat = softn->ipf_nat_instances;
184060852Sdarrenr		if (nat == NULL) {
1841255332Scy			if (softn->ipf_nat_instances == NULL) {
1842255332Scy				IPFERROR(60030);
1843145522Sdarrenr				error = ENOENT;
1844255332Scy			}
1845145522Sdarrenr			goto finished;
184660852Sdarrenr		}
184760852Sdarrenr	} else {
184860852Sdarrenr		/*
184960852Sdarrenr		 * Make sure the pointer we're copying from exists in the
185060852Sdarrenr		 * current list of entries.  Security precaution to prevent
185160852Sdarrenr		 * copying of random kernel data.
185260852Sdarrenr		 */
1853255332Scy		for (n = softn->ipf_nat_instances; n; n = n->nat_next)
185460852Sdarrenr			if (n == nat)
185560852Sdarrenr				break;
1856145522Sdarrenr		if (n == NULL) {
1857255332Scy			IPFERROR(60031);
1858145522Sdarrenr			error = ESRCH;
1859145522Sdarrenr			goto finished;
1860145522Sdarrenr		}
186160852Sdarrenr	}
1862145522Sdarrenr	ipn->ipn_next = nat->nat_next;
186360852Sdarrenr
1864145522Sdarrenr	/*
1865145522Sdarrenr	 * Copy the NAT structure.
1866145522Sdarrenr	 */
1867145522Sdarrenr	bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat));
186860852Sdarrenr
1869145522Sdarrenr	/*
1870145522Sdarrenr	 * If we have a pointer to the NAT rule it belongs to, save that too.
1871145522Sdarrenr	 */
1872145522Sdarrenr	if (nat->nat_ptr != NULL)
1873145522Sdarrenr		bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat,
1874255332Scy		      ipn->ipn_ipnat.in_size);
187560852Sdarrenr
1876145522Sdarrenr	/*
1877145522Sdarrenr	 * If we also know the NAT entry has an associated filter rule,
1878145522Sdarrenr	 * save that too.
1879145522Sdarrenr	 */
1880145522Sdarrenr	if (nat->nat_fr != NULL)
1881145522Sdarrenr		bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr,
1882145522Sdarrenr		      sizeof(ipn->ipn_fr));
188360852Sdarrenr
1884145522Sdarrenr	/*
1885145522Sdarrenr	 * Last but not least, if there is an application proxy session set
1886145522Sdarrenr	 * up for this NAT entry, then copy that out too, including any
1887145522Sdarrenr	 * private data saved along side it by the proxy.
1888145522Sdarrenr	 */
1889145522Sdarrenr	aps = nat->nat_aps;
1890145522Sdarrenr	outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data);
1891145522Sdarrenr	if (aps != NULL) {
1892145522Sdarrenr		char *s;
189360852Sdarrenr
1894145522Sdarrenr		if (outsize < sizeof(*aps)) {
1895255332Scy			IPFERROR(60032);
1896145522Sdarrenr			error = ENOBUFS;
1897145522Sdarrenr			goto finished;
189860852Sdarrenr		}
1899145522Sdarrenr
1900145522Sdarrenr		s = ipn->ipn_data;
1901145522Sdarrenr		bcopy((char *)aps, s, sizeof(*aps));
1902145522Sdarrenr		s += sizeof(*aps);
1903145522Sdarrenr		outsize -= sizeof(*aps);
1904145522Sdarrenr		if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz))
1905145522Sdarrenr			bcopy(aps->aps_data, s, aps->aps_psiz);
1906255332Scy		else {
1907255332Scy			IPFERROR(60033);
1908145522Sdarrenr			error = ENOBUFS;
1909255332Scy		}
191060852Sdarrenr	}
1911145522Sdarrenr	if (error == 0) {
1912255332Scy		error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE,
1913255332Scy				     ipns.ipn_dsize);
1914145522Sdarrenr	}
1915145522Sdarrenr
1916145522Sdarrenrfinished:
1917145522Sdarrenr	if (ipn != NULL) {
1918145522Sdarrenr		KFREES(ipn, ipns.ipn_dsize);
1919145522Sdarrenr	}
1920344113Scy	if (getlock) {
1921344113Scy		RWLOCK_EXIT(&softc->ipf_nat);
1922344113Scy	}
192364580Sdarrenr	return error;
192460852Sdarrenr}
192560852Sdarrenr
192660852Sdarrenr
1927145522Sdarrenr/* ------------------------------------------------------------------------ */
1928255332Scy/* Function:    ipf_nat_putent                                              */
1929145522Sdarrenr/* Returns:     int - 0 == success, != 0 is the error value.                */
1930255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
1931255332Scy/*              data(I)    - pointer to natget structure with NAT           */
1932255332Scy/*                           structure information to load into the kernel  */
1933145522Sdarrenr/*              getlock(I) - flag indicating whether or not a write lock    */
1934255332Scy/*                           on is already held.                            */
1935145522Sdarrenr/*                                                                          */
1936145522Sdarrenr/* Handle SIOCSTPUT.                                                        */
1937145522Sdarrenr/* Loads a NAT table entry from user space, including a NAT rule, proxy and */
1938145522Sdarrenr/* firewall rule data structures, if pointers to them indicate so.          */
1939145522Sdarrenr/* ------------------------------------------------------------------------ */
1940255332Scystatic int
1941255332Scyipf_nat_putent(softc, data, getlock)
1942255332Scy	ipf_main_softc_t *softc;
1943255332Scy	caddr_t data;
1944255332Scy	int getlock;
194560852Sdarrenr{
1946255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1947145522Sdarrenr	nat_save_t ipn, *ipnn;
194860852Sdarrenr	ap_session_t *aps;
1949145522Sdarrenr	nat_t *n, *nat;
195060852Sdarrenr	frentry_t *fr;
1951145522Sdarrenr	fr_info_t fin;
195260852Sdarrenr	ipnat_t *in;
195360852Sdarrenr	int error;
195460852Sdarrenr
1955255332Scy	error = ipf_inobj(softc, data, NULL, &ipn, IPFOBJ_NATSAVE);
1956145522Sdarrenr	if (error != 0)
1957145522Sdarrenr		return error;
1958145522Sdarrenr
1959145522Sdarrenr	/*
1960145522Sdarrenr	 * Initialise early because of code at junkput label.
1961145522Sdarrenr	 */
1962255332Scy	n = NULL;
1963145522Sdarrenr	in = NULL;
1964145522Sdarrenr	aps = NULL;
196564580Sdarrenr	nat = NULL;
1966145522Sdarrenr	ipnn = NULL;
1967170268Sdarrenr	fr = NULL;
1968145522Sdarrenr
1969145522Sdarrenr	/*
1970145522Sdarrenr	 * New entry, copy in the rest of the NAT entry if it's size is more
1971145522Sdarrenr	 * than just the nat_t structure.
1972145522Sdarrenr	 */
1973145522Sdarrenr	if (ipn.ipn_dsize > sizeof(ipn)) {
1974145522Sdarrenr		if (ipn.ipn_dsize > 81920) {
1975255332Scy			IPFERROR(60034);
1976145522Sdarrenr			error = ENOMEM;
1977145522Sdarrenr			goto junkput;
1978145522Sdarrenr		}
1979145522Sdarrenr
1980145522Sdarrenr		KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize);
1981255332Scy		if (ipnn == NULL) {
1982255332Scy			IPFERROR(60035);
198360852Sdarrenr			return ENOMEM;
1984255332Scy		}
1985145522Sdarrenr
1986255332Scy		bzero(ipnn, ipn.ipn_dsize);
1987255332Scy		error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE,
1988255332Scy				    ipn.ipn_dsize);
1989145522Sdarrenr		if (error != 0) {
199064580Sdarrenr			goto junkput;
199164580Sdarrenr		}
199260852Sdarrenr	} else
1993145522Sdarrenr		ipnn = &ipn;
199460852Sdarrenr
199560852Sdarrenr	KMALLOC(nat, nat_t *);
199664580Sdarrenr	if (nat == NULL) {
1997255332Scy		IPFERROR(60037);
1998145522Sdarrenr		error = ENOMEM;
199964580Sdarrenr		goto junkput;
200064580Sdarrenr	}
200160852Sdarrenr
2002145522Sdarrenr	bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat));
2003255332Scy
2004255332Scy	switch (nat->nat_v[0])
2005255332Scy	{
2006255332Scy	case 4:
2007255332Scy#ifdef USE_INET6
2008255332Scy	case 6 :
2009255332Scy#endif
2010255332Scy		break;
2011255332Scy	default :
2012255332Scy		IPFERROR(60061);
2013255332Scy		error = EPROTONOSUPPORT;
2014255332Scy		goto junkput;
2015255332Scy		/*NOTREACHED*/
2016255332Scy	}
2017255332Scy
201860852Sdarrenr	/*
2019255332Scy	 * Initialize all these so that ipf_nat_delete() doesn't cause a crash.
202060852Sdarrenr	 */
2021145522Sdarrenr	bzero((char *)nat, offsetof(struct nat, nat_tqe));
2022145522Sdarrenr	nat->nat_tqe.tqe_pnext = NULL;
2023145522Sdarrenr	nat->nat_tqe.tqe_next = NULL;
2024145522Sdarrenr	nat->nat_tqe.tqe_ifq = NULL;
2025145522Sdarrenr	nat->nat_tqe.tqe_parent = nat;
202660852Sdarrenr
202760852Sdarrenr	/*
202860852Sdarrenr	 * Restore the rule associated with this nat session
202960852Sdarrenr	 */
2030145522Sdarrenr	in = ipnn->ipn_nat.nat_ptr;
2031145522Sdarrenr	if (in != NULL) {
2032255332Scy		KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size);
2033145522Sdarrenr		nat->nat_ptr = in;
203460852Sdarrenr		if (in == NULL) {
2035255332Scy			IPFERROR(60038);
203660852Sdarrenr			error = ENOMEM;
203760852Sdarrenr			goto junkput;
203860852Sdarrenr		}
2039255332Scy		bcopy((char *)&ipnn->ipn_ipnat, (char *)in,
2040255332Scy		      ipnn->ipn_ipnat.in_size);
204160852Sdarrenr		in->in_use = 1;
204260852Sdarrenr		in->in_flags |= IPN_DELETE;
2043145522Sdarrenr
2044255332Scy		ATOMIC_INC32(softn->ipf_nat_stats.ns_rules);
2045145522Sdarrenr
2046255332Scy		if (ipf_nat_resolverule(softc, in) != 0) {
2047255332Scy			IPFERROR(60039);
2048161356Sguido			error = ESRCH;
2049161356Sguido			goto junkput;
2050161356Sguido		}
2051145522Sdarrenr	}
2052145522Sdarrenr
2053145522Sdarrenr	/*
2054145522Sdarrenr	 * Check that the NAT entry doesn't already exist in the kernel.
2055161356Sguido	 *
2056161356Sguido	 * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry.  To do
2057161356Sguido	 * this, we check to see if the inbound combination of addresses and
2058161356Sguido	 * ports is already known.  Similar logic is applied for NAT_INBOUND.
2059255332Scy	 *
2060145522Sdarrenr	 */
2061145522Sdarrenr	bzero((char *)&fin, sizeof(fin));
2062255332Scy	fin.fin_v = nat->nat_v[0];
2063255332Scy	fin.fin_p = nat->nat_pr[0];
2064255332Scy	fin.fin_rev = nat->nat_rev;
2065255332Scy	fin.fin_ifp = nat->nat_ifps[0];
2066255332Scy	fin.fin_data[0] = ntohs(nat->nat_ndport);
2067255332Scy	fin.fin_data[1] = ntohs(nat->nat_nsport);
2068255332Scy
2069255332Scy	switch (nat->nat_dir)
2070255332Scy	{
2071255332Scy	case NAT_OUTBOUND :
2072255332Scy	case NAT_DIVERTOUT :
2073153876Sguido		if (getlock) {
2074255332Scy			READ_ENTER(&softc->ipf_nat);
2075153876Sguido		}
2076255332Scy
2077255332Scy		fin.fin_v = nat->nat_v[1];
2078255332Scy		if (nat->nat_v[1] == 4) {
2079255332Scy			n = ipf_nat_inlookup(&fin, nat->nat_flags, fin.fin_p,
2080255332Scy					     nat->nat_ndstip, nat->nat_nsrcip);
2081255332Scy#ifdef USE_INET6
2082255332Scy		} else if (nat->nat_v[1] == 6) {
2083255332Scy			n = ipf_nat6_inlookup(&fin, nat->nat_flags, fin.fin_p,
2084255332Scy					      &nat->nat_ndst6.in6,
2085255332Scy					      &nat->nat_nsrc6.in6);
2086255332Scy#endif
2087255332Scy		}
2088255332Scy
2089153876Sguido		if (getlock) {
2090255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
2091153876Sguido		}
2092153876Sguido		if (n != NULL) {
2093255332Scy			IPFERROR(60040);
2094145522Sdarrenr			error = EEXIST;
2095145522Sdarrenr			goto junkput;
209660852Sdarrenr		}
2097255332Scy		break;
2098255332Scy
2099255332Scy	case NAT_INBOUND :
2100255332Scy	case NAT_DIVERTIN :
2101153876Sguido		if (getlock) {
2102255332Scy			READ_ENTER(&softc->ipf_nat);
2103153876Sguido		}
2104255332Scy
2105255332Scy		if (fin.fin_v == 4) {
2106255332Scy			n = ipf_nat_outlookup(&fin, nat->nat_flags, fin.fin_p,
2107255332Scy					      nat->nat_ndstip,
2108255332Scy					      nat->nat_nsrcip);
2109255332Scy#ifdef USE_INET6
2110255332Scy		} else if (fin.fin_v == 6) {
2111255332Scy			n = ipf_nat6_outlookup(&fin, nat->nat_flags, fin.fin_p,
2112255332Scy					       &nat->nat_ndst6.in6,
2113255332Scy					       &nat->nat_nsrc6.in6);
2114255332Scy#endif
2115255332Scy		}
2116255332Scy
2117153876Sguido		if (getlock) {
2118255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
2119153876Sguido		}
2120153876Sguido		if (n != NULL) {
2121255332Scy			IPFERROR(60041);
2122145522Sdarrenr			error = EEXIST;
2123145522Sdarrenr			goto junkput;
2124145522Sdarrenr		}
2125255332Scy		break;
2126255332Scy
2127255332Scy	default :
2128255332Scy		IPFERROR(60042);
2129145522Sdarrenr		error = EINVAL;
2130145522Sdarrenr		goto junkput;
213160852Sdarrenr	}
213260852Sdarrenr
213360852Sdarrenr	/*
213460852Sdarrenr	 * Restore ap_session_t structure.  Include the private data allocated
213560852Sdarrenr	 * if it was there.
213660852Sdarrenr	 */
2137145522Sdarrenr	aps = nat->nat_aps;
2138145522Sdarrenr	if (aps != NULL) {
213960852Sdarrenr		KMALLOC(aps, ap_session_t *);
2140145522Sdarrenr		nat->nat_aps = aps;
214160852Sdarrenr		if (aps == NULL) {
2142255332Scy			IPFERROR(60043);
214360852Sdarrenr			error = ENOMEM;
214460852Sdarrenr			goto junkput;
214560852Sdarrenr		}
214660852Sdarrenr		bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps));
2147145522Sdarrenr		if (in != NULL)
214860852Sdarrenr			aps->aps_apr = in->in_apr;
2149145522Sdarrenr		else
2150145522Sdarrenr			aps->aps_apr = NULL;
2151145522Sdarrenr		if (aps->aps_psiz != 0) {
2152145522Sdarrenr			if (aps->aps_psiz > 81920) {
2153255332Scy				IPFERROR(60044);
2154145522Sdarrenr				error = ENOMEM;
2155145522Sdarrenr				goto junkput;
2156145522Sdarrenr			}
215760852Sdarrenr			KMALLOCS(aps->aps_data, void *, aps->aps_psiz);
215860852Sdarrenr			if (aps->aps_data == NULL) {
2159255332Scy				IPFERROR(60045);
216060852Sdarrenr				error = ENOMEM;
216160852Sdarrenr				goto junkput;
216260852Sdarrenr			}
216360852Sdarrenr			bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data,
216460852Sdarrenr			      aps->aps_psiz);
216560852Sdarrenr		} else {
216660852Sdarrenr			aps->aps_psiz = 0;
216760852Sdarrenr			aps->aps_data = NULL;
216860852Sdarrenr		}
216960852Sdarrenr	}
217060852Sdarrenr
217160852Sdarrenr	/*
217260852Sdarrenr	 * If there was a filtering rule associated with this entry then
217360852Sdarrenr	 * build up a new one.
217460852Sdarrenr	 */
2175145522Sdarrenr	fr = nat->nat_fr;
217660852Sdarrenr	if (fr != NULL) {
2177145522Sdarrenr		if ((nat->nat_flags & SI_NEWFR) != 0) {
217860852Sdarrenr			KMALLOC(fr, frentry_t *);
217960852Sdarrenr			nat->nat_fr = fr;
218060852Sdarrenr			if (fr == NULL) {
2181255332Scy				IPFERROR(60046);
218260852Sdarrenr				error = ENOMEM;
218360852Sdarrenr				goto junkput;
218460852Sdarrenr			}
2185145522Sdarrenr			ipnn->ipn_nat.nat_fr = fr;
2186145522Sdarrenr			fr->fr_ref = 1;
2187255332Scy			(void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE);
2188145522Sdarrenr			bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr));
2189161356Sguido
2190161356Sguido			fr->fr_ref = 1;
2191161356Sguido			fr->fr_dsize = 0;
2192161356Sguido			fr->fr_data = NULL;
2193161356Sguido			fr->fr_type = FR_T_NONE;
2194161356Sguido
2195145522Sdarrenr			MUTEX_NUKE(&fr->fr_lock);
2196145522Sdarrenr			MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock");
219760852Sdarrenr		} else {
2198153876Sguido			if (getlock) {
2199255332Scy				READ_ENTER(&softc->ipf_nat);
2200153876Sguido			}
2201255332Scy			for (n = softn->ipf_nat_instances; n; n = n->nat_next)
220260852Sdarrenr				if (n->nat_fr == fr)
220360852Sdarrenr					break;
2204145522Sdarrenr
2205145522Sdarrenr			if (n != NULL) {
2206145522Sdarrenr				MUTEX_ENTER(&fr->fr_lock);
2207145522Sdarrenr				fr->fr_ref++;
2208145522Sdarrenr				MUTEX_EXIT(&fr->fr_lock);
2209145522Sdarrenr			}
2210153876Sguido			if (getlock) {
2211255332Scy				RWLOCK_EXIT(&softc->ipf_nat);
2212153876Sguido			}
2213145522Sdarrenr
2214255332Scy			if (n == NULL) {
2215255332Scy				IPFERROR(60047);
221660852Sdarrenr				error = ESRCH;
221760852Sdarrenr				goto junkput;
221860852Sdarrenr			}
221960852Sdarrenr		}
222060852Sdarrenr	}
222160852Sdarrenr
2222145522Sdarrenr	if (ipnn != &ipn) {
2223145522Sdarrenr		KFREES(ipnn, ipn.ipn_dsize);
2224145522Sdarrenr		ipnn = NULL;
2225145522Sdarrenr	}
2226145522Sdarrenr
2227145522Sdarrenr	if (getlock) {
2228255332Scy		WRITE_ENTER(&softc->ipf_nat);
2229145522Sdarrenr	}
2230255332Scy
2231255332Scy	if (fin.fin_v == 4)
2232255332Scy		error = ipf_nat_finalise(&fin, nat);
2233255332Scy#ifdef USE_INET6
2234255332Scy	else
2235255332Scy		error = ipf_nat6_finalise(&fin, nat);
2236255332Scy#endif
2237255332Scy
2238145522Sdarrenr	if (getlock) {
2239255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
2240145522Sdarrenr	}
2241145522Sdarrenr
2242145522Sdarrenr	if (error == 0)
2243145522Sdarrenr		return 0;
2244145522Sdarrenr
2245255332Scy	IPFERROR(60048);
2246145522Sdarrenr	error = ENOMEM;
2247145522Sdarrenr
224860852Sdarrenrjunkput:
2249255332Scy	if (fr != NULL) {
2250255332Scy		(void) ipf_derefrule(softc, &fr);
2251255332Scy	}
2252145522Sdarrenr
2253145522Sdarrenr	if ((ipnn != NULL) && (ipnn != &ipn)) {
2254145522Sdarrenr		KFREES(ipnn, ipn.ipn_dsize);
2255145522Sdarrenr	}
2256145522Sdarrenr	if (nat != NULL) {
2257145522Sdarrenr		if (aps != NULL) {
2258145522Sdarrenr			if (aps->aps_data != NULL) {
2259145522Sdarrenr				KFREES(aps->aps_data, aps->aps_psiz);
2260145522Sdarrenr			}
2261145522Sdarrenr			KFREE(aps);
2262145522Sdarrenr		}
2263145522Sdarrenr		if (in != NULL) {
2264145522Sdarrenr			if (in->in_apr)
2265255332Scy				ipf_proxy_deref(in->in_apr);
2266255332Scy			KFREES(in, in->in_size);
2267145522Sdarrenr		}
2268145522Sdarrenr		KFREE(nat);
2269145522Sdarrenr	}
227060852Sdarrenr	return error;
227160852Sdarrenr}
227260852Sdarrenr
227360852Sdarrenr
2274145522Sdarrenr/* ------------------------------------------------------------------------ */
2275255332Scy/* Function:    ipf_nat_delete                                              */
2276145522Sdarrenr/* Returns:     Nil                                                         */
2277255332Scy/* Parameters:  softc(I)   - pointer to soft context main structure         */
2278255332Scy/*              nat(I)     - pointer to NAT structure to delete             */
2279145522Sdarrenr/*              logtype(I) - type of LOG record to create before deleting   */
2280145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
2281145522Sdarrenr/*                                                                          */
2282145522Sdarrenr/* Delete a nat entry from the various lists and table.  If NAT logging is  */
2283145522Sdarrenr/* enabled then generate a NAT log record for this event.                   */
2284145522Sdarrenr/* ------------------------------------------------------------------------ */
2285255332Scyvoid
2286255332Scyipf_nat_delete(softc, nat, logtype)
2287255332Scy	ipf_main_softc_t *softc;
2288255332Scy	struct nat *nat;
2289255332Scy	int logtype;
229053642Sguido{
2291255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2292255332Scy	int madeorphan = 0, bkt, removed = 0;
2293255332Scy	nat_stat_side_t *nss;
229453642Sguido	struct ipnat *ipn;
229553642Sguido
2296255332Scy	if (logtype != 0 && softn->ipf_nat_logging != 0)
2297255332Scy		ipf_nat_log(softc, softn, nat, logtype);
229853642Sguido
2299145522Sdarrenr	/*
2300145522Sdarrenr	 * Take it as a general indication that all the pointers are set if
2301145522Sdarrenr	 * nat_pnext is set.
2302145522Sdarrenr	 */
2303145522Sdarrenr	if (nat->nat_pnext != NULL) {
2304172776Sdarrenr		removed = 1;
2305172776Sdarrenr
2306255332Scy		bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz;
2307255332Scy		nss = &softn->ipf_nat_stats.ns_side[0];
2308338171Scy		if (nss->ns_bucketlen[bkt] > 0)
2309338171Scy			nss->ns_bucketlen[bkt]--;
2310255332Scy		if (nss->ns_bucketlen[bkt] == 0) {
2311255332Scy			nss->ns_inuse--;
2312255332Scy		}
2313145522Sdarrenr
2314255332Scy		bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz;
2315255332Scy		nss = &softn->ipf_nat_stats.ns_side[1];
2316338171Scy		if (nss->ns_bucketlen[bkt] > 0)
2317338171Scy			nss->ns_bucketlen[bkt]--;
2318255332Scy		if (nss->ns_bucketlen[bkt] == 0) {
2319255332Scy			nss->ns_inuse--;
2320255332Scy		}
2321255332Scy
2322145522Sdarrenr		*nat->nat_pnext = nat->nat_next;
2323145522Sdarrenr		if (nat->nat_next != NULL) {
2324145522Sdarrenr			nat->nat_next->nat_pnext = nat->nat_pnext;
2325145522Sdarrenr			nat->nat_next = NULL;
2326145522Sdarrenr		}
2327145522Sdarrenr		nat->nat_pnext = NULL;
2328145522Sdarrenr
2329145522Sdarrenr		*nat->nat_phnext[0] = nat->nat_hnext[0];
2330145522Sdarrenr		if (nat->nat_hnext[0] != NULL) {
2331145522Sdarrenr			nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
2332145522Sdarrenr			nat->nat_hnext[0] = NULL;
2333145522Sdarrenr		}
2334145522Sdarrenr		nat->nat_phnext[0] = NULL;
2335145522Sdarrenr
2336145522Sdarrenr		*nat->nat_phnext[1] = nat->nat_hnext[1];
2337145522Sdarrenr		if (nat->nat_hnext[1] != NULL) {
2338145522Sdarrenr			nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
2339145522Sdarrenr			nat->nat_hnext[1] = NULL;
2340145522Sdarrenr		}
2341145522Sdarrenr		nat->nat_phnext[1] = NULL;
2342145522Sdarrenr
2343255332Scy		if ((nat->nat_flags & SI_WILDP) != 0) {
2344255332Scy			ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds);
2345255332Scy		}
2346255332Scy		madeorphan = 1;
234753642Sguido	}
234860852Sdarrenr
2349145522Sdarrenr	if (nat->nat_me != NULL) {
2350145522Sdarrenr		*nat->nat_me = NULL;
2351145522Sdarrenr		nat->nat_me = NULL;
2352255332Scy		nat->nat_ref--;
2353255332Scy		ASSERT(nat->nat_ref >= 0);
2354145522Sdarrenr	}
235560852Sdarrenr
2356255332Scy	if (nat->nat_tqe.tqe_ifq != NULL) {
2357255332Scy		/*
2358255332Scy		 * No call to ipf_freetimeoutqueue() is made here, they are
2359255332Scy		 * garbage collected in ipf_nat_expire().
2360255332Scy		 */
2361255332Scy		(void) ipf_deletequeueentry(&nat->nat_tqe);
2362255332Scy	}
2363145522Sdarrenr
2364255332Scy	if (nat->nat_sync) {
2365255332Scy		ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync);
2366255332Scy		nat->nat_sync = NULL;
2367255332Scy	}
2368255332Scy
2369170268Sdarrenr	if (logtype == NL_EXPIRE)
2370255332Scy		softn->ipf_nat_stats.ns_expire++;
2371170268Sdarrenr
2372172776Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
2373172776Sdarrenr	/*
2374172776Sdarrenr	 * NL_DESTROY should only be passed in when we've got nat_ref >= 2.
2375172776Sdarrenr	 * This happens when a nat'd packet is blocked and we want to throw
2376172776Sdarrenr	 * away the NAT session.
2377172776Sdarrenr	 */
2378172776Sdarrenr	if (logtype == NL_DESTROY) {
2379172776Sdarrenr		if (nat->nat_ref > 2) {
2380172776Sdarrenr			nat->nat_ref -= 2;
2381172776Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
2382172776Sdarrenr			if (removed)
2383255332Scy				softn->ipf_nat_stats.ns_orphans++;
2384172776Sdarrenr			return;
2385172776Sdarrenr		}
2386172776Sdarrenr	} else if (nat->nat_ref > 1) {
2387172776Sdarrenr		nat->nat_ref--;
2388172776Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
2389255332Scy		if (madeorphan == 1)
2390255332Scy			softn->ipf_nat_stats.ns_orphans++;
2391145522Sdarrenr		return;
2392145522Sdarrenr	}
2393255332Scy	ASSERT(nat->nat_ref >= 0);
2394172776Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
2395170268Sdarrenr
2396255332Scy	nat->nat_ref = 0;
2397255332Scy
2398255332Scy	if (madeorphan == 0)
2399255332Scy		softn->ipf_nat_stats.ns_orphans--;
2400255332Scy
2401161356Sguido	/*
2402255332Scy	 * At this point, nat_ref can be either 0 or -1
2403161356Sguido	 */
2404255332Scy	softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--;
2405145522Sdarrenr
2406255332Scy	if (nat->nat_fr != NULL) {
2407255332Scy		(void) ipf_derefrule(softc, &nat->nat_fr);
2408255332Scy	}
2409145522Sdarrenr
2410255332Scy	if (nat->nat_hm != NULL) {
2411255332Scy		ipf_nat_hostmapdel(softc, &nat->nat_hm);
2412255332Scy	}
2413145522Sdarrenr
241453642Sguido	/*
241553642Sguido	 * If there is an active reference from the nat entry to its parent
241653642Sguido	 * rule, decrement the rule's reference count and free it too if no
241753642Sguido	 * longer being used.
241853642Sguido	 */
2419145522Sdarrenr	ipn = nat->nat_ptr;
2420255332Scy	nat->nat_ptr = NULL;
2421255332Scy
242253642Sguido	if (ipn != NULL) {
2423255332Scy		ipn->in_space++;
2424255332Scy		ipf_nat_rule_deref(softc, &ipn);
242553642Sguido	}
242653642Sguido
2427255332Scy	if (nat->nat_aps != NULL) {
2428255332Scy		ipf_proxy_free(softc, nat->nat_aps);
2429255332Scy		nat->nat_aps = NULL;
2430255332Scy	}
2431255332Scy
2432145522Sdarrenr	MUTEX_DESTROY(&nat->nat_lock);
2433145522Sdarrenr
2434255332Scy	softn->ipf_nat_stats.ns_active--;
2435145522Sdarrenr
243653642Sguido	/*
243753642Sguido	 * If there's a fragment table entry too for this nat entry, then
2438145522Sdarrenr	 * dereference that as well.  This is after nat_lock is released
2439145522Sdarrenr	 * because of Tru64.
244053642Sguido	 */
2441255332Scy	ipf_frag_natforget(softc, (void *)nat);
2442145522Sdarrenr
2443145522Sdarrenr	KFREE(nat);
244453642Sguido}
244553642Sguido
244653642Sguido
2447145522Sdarrenr/* ------------------------------------------------------------------------ */
2448255332Scy/* Function:    ipf_nat_flushtable                                          */
2449145522Sdarrenr/* Returns:     int - number of NAT rules deleted                           */
2450255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
2451255332Scy/*              softn(I) - pointer to NAT context structure                 */
2452255332Scy/* Write Lock:  ipf_nat                                                     */
2453145522Sdarrenr/*                                                                          */
2454145522Sdarrenr/* Deletes all currently active NAT sessions.  In deleting each NAT entry a */
2455255332Scy/* log record should be emitted in ipf_nat_delete() if NAT logging is       */
2456255332Scy/* enabled.                                                                 */
2457145522Sdarrenr/* ------------------------------------------------------------------------ */
245853642Sguido/*
245953642Sguido * nat_flushtable - clear the NAT table of all mapping entries.
246053642Sguido */
2461255332Scystatic int
2462255332Scyipf_nat_flushtable(softc, softn)
2463255332Scy	ipf_main_softc_t *softc;
2464255332Scy	ipf_nat_softc_t *softn;
246553642Sguido{
2466145522Sdarrenr	nat_t *nat;
2467145522Sdarrenr	int j = 0;
246867614Sdarrenr
246953642Sguido	/*
247053642Sguido	 * ALL NAT mappings deleted, so lets just make the deletions
247153642Sguido	 * quicker.
247253642Sguido	 */
2473255332Scy	if (softn->ipf_nat_table[0] != NULL)
2474255332Scy		bzero((char *)softn->ipf_nat_table[0],
2475255332Scy		      sizeof(softn->ipf_nat_table[0]) *
2476255332Scy		      softn->ipf_nat_table_sz);
2477255332Scy	if (softn->ipf_nat_table[1] != NULL)
2478255332Scy		bzero((char *)softn->ipf_nat_table[1],
2479255332Scy		      sizeof(softn->ipf_nat_table[1]) *
2480255332Scy		      softn->ipf_nat_table_sz);
248153642Sguido
2482255332Scy	while ((nat = softn->ipf_nat_instances) != NULL) {
2483255332Scy		ipf_nat_delete(softc, nat, NL_FLUSH);
248453642Sguido		j++;
248553642Sguido	}
2486145522Sdarrenr
248753642Sguido	return j;
248853642Sguido}
248953642Sguido
249053642Sguido
2491145522Sdarrenr/* ------------------------------------------------------------------------ */
2492255332Scy/* Function:    ipf_nat_clearlist                                           */
2493145522Sdarrenr/* Returns:     int - number of NAT/RDR rules deleted                       */
2494255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
2495255332Scy/*              softn(I) - pointer to NAT context structure                 */
2496145522Sdarrenr/*                                                                          */
2497145522Sdarrenr/* Delete all rules in the current list of rules.  There is nothing elegant */
2498145522Sdarrenr/* about this cleanup: simply free all entries on the list of rules and     */
2499145522Sdarrenr/* clear out the tables used for hashed NAT rule lookups.                   */
2500145522Sdarrenr/* ------------------------------------------------------------------------ */
2501255332Scystatic int
2502255332Scyipf_nat_clearlist(softc, softn)
2503255332Scy	ipf_main_softc_t *softc;
2504255332Scy	ipf_nat_softc_t *softn;
250553642Sguido{
2506255332Scy	ipnat_t *n;
250753642Sguido	int i = 0;
250853642Sguido
2509255332Scy	if (softn->ipf_nat_map_rules != NULL) {
2510255332Scy		bzero((char *)softn->ipf_nat_map_rules,
2511255332Scy		      sizeof(*softn->ipf_nat_map_rules) *
2512255332Scy		      softn->ipf_nat_maprules_sz);
2513255332Scy	}
2514255332Scy	if (softn->ipf_nat_rdr_rules != NULL) {
2515255332Scy		bzero((char *)softn->ipf_nat_rdr_rules,
2516255332Scy		      sizeof(*softn->ipf_nat_rdr_rules) *
2517255332Scy		      softn->ipf_nat_rdrrules_sz);
2518255332Scy	}
251953642Sguido
2520255332Scy	while ((n = softn->ipf_nat_list) != NULL) {
2521255332Scy		ipf_nat_delrule(softc, softn, n, 0);
252253642Sguido		i++;
252353642Sguido	}
2524255332Scy#if SOLARIS && !defined(INSTANCES)
2525145522Sdarrenr	pfil_delayed_copy = 1;
2526145522Sdarrenr#endif
252753642Sguido	return i;
252853642Sguido}
252953642Sguido
253053642Sguido
2531145522Sdarrenr/* ------------------------------------------------------------------------ */
2532255332Scy/* Function:    ipf_nat_delrule                                             */
2533255332Scy/* Returns:     Nil                                                         */
2534255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
2535255332Scy/*              softn(I) - pointer to NAT context structure                 */
2536255332Scy/*              np(I)    - pointer to NAT rule to delete                    */
2537255332Scy/*              purge(I) - 1 == allow purge, 0 == prevent purge             */
2538255332Scy/* Locks:       WRITE(ipf_nat)                                              */
2539255332Scy/*                                                                          */
2540255332Scy/* Preventing "purge" from occuring is allowed because when all of the NAT  */
2541255332Scy/* rules are being removed, allowing the "purge" to walk through the list   */
2542255332Scy/* of NAT sessions, possibly multiple times, would be a large performance   */
2543255332Scy/* hit, on the order of O(N^2).                                             */
2544255332Scy/* ------------------------------------------------------------------------ */
2545255332Scystatic void
2546255332Scyipf_nat_delrule(softc, softn, np, purge)
2547255332Scy	ipf_main_softc_t *softc;
2548255332Scy	ipf_nat_softc_t *softn;
2549255332Scy	ipnat_t *np;
2550255332Scy	int purge;
2551255332Scy{
2552255332Scy
2553255332Scy	if (np->in_pnext != NULL) {
2554255332Scy		*np->in_pnext = np->in_next;
2555255332Scy		if (np->in_next != NULL)
2556255332Scy			np->in_next->in_pnext = np->in_pnext;
2557255332Scy		if (softn->ipf_nat_list_tail == &np->in_next)
2558255332Scy			softn->ipf_nat_list_tail = np->in_pnext;
2559255332Scy	}
2560255332Scy
2561255332Scy	if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) {
2562255332Scy		nat_t *next;
2563255332Scy		nat_t *nat;
2564255332Scy
2565255332Scy		for (next = softn->ipf_nat_instances; (nat = next) != NULL;) {
2566255332Scy			next = nat->nat_next;
2567255332Scy			if (nat->nat_ptr == np)
2568255332Scy				ipf_nat_delete(softc, nat, NL_PURGE);
2569255332Scy		}
2570255332Scy	}
2571255332Scy
2572255332Scy	if ((np->in_flags & IPN_DELETE) == 0) {
2573255332Scy		if (np->in_redir & NAT_REDIRECT) {
2574255332Scy			switch (np->in_v[0])
2575255332Scy			{
2576255332Scy			case 4 :
2577255332Scy				ipf_nat_delrdr(softn, np);
2578255332Scy				break;
2579255332Scy#ifdef USE_INET6
2580255332Scy			case 6 :
2581255332Scy				ipf_nat6_delrdr(softn, np);
2582255332Scy				break;
2583255332Scy#endif
2584255332Scy			}
2585255332Scy		}
2586255332Scy		if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) {
2587255332Scy			switch (np->in_v[0])
2588255332Scy			{
2589255332Scy			case 4 :
2590255332Scy				ipf_nat_delmap(softn, np);
2591255332Scy				break;
2592255332Scy#ifdef USE_INET6
2593255332Scy			case 6 :
2594255332Scy				ipf_nat6_delmap(softn, np);
2595255332Scy				break;
2596255332Scy#endif
2597255332Scy			}
2598255332Scy		}
2599255332Scy	}
2600255332Scy
2601255332Scy	np->in_flags |= IPN_DELETE;
2602255332Scy	ipf_nat_rule_deref(softc, &np);
2603255332Scy}
2604255332Scy
2605255332Scy
2606255332Scy/* ------------------------------------------------------------------------ */
2607255332Scy/* Function:    ipf_nat_newmap                                              */
2608145522Sdarrenr/* Returns:     int - -1 == error, 0 == success                             */
2609145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
2610145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
2611145522Sdarrenr/*              ni(I)  - pointer to structure with misc. information needed */
2612145522Sdarrenr/*                       to create new NAT entry.                           */
2613145522Sdarrenr/*                                                                          */
2614145522Sdarrenr/* Given an empty NAT structure, populate it with new information about a   */
2615145522Sdarrenr/* new NAT session, as defined by the matching NAT rule.                    */
2616145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
2617145522Sdarrenr/* to the new IP address for the translation.                               */
2618145522Sdarrenr/* ------------------------------------------------------------------------ */
2619255332Scystatic int
2620255332Scyipf_nat_newmap(fin, nat, ni)
2621255332Scy	fr_info_t *fin;
2622255332Scy	nat_t *nat;
2623255332Scy	natinfo_t *ni;
2624145522Sdarrenr{
2625255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
2626255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2627145522Sdarrenr	u_short st_port, dport, sport, port, sp, dp;
2628145522Sdarrenr	struct in_addr in, inb;
2629145522Sdarrenr	hostmap_t *hm;
2630145522Sdarrenr	u_32_t flags;
2631145522Sdarrenr	u_32_t st_ip;
2632145522Sdarrenr	ipnat_t *np;
2633145522Sdarrenr	nat_t *natl;
2634145522Sdarrenr	int l;
2635145522Sdarrenr
2636145522Sdarrenr	/*
2637145522Sdarrenr	 * If it's an outbound packet which doesn't match any existing
2638145522Sdarrenr	 * record, then create a new port
2639145522Sdarrenr	 */
2640145522Sdarrenr	l = 0;
2641145522Sdarrenr	hm = NULL;
2642145522Sdarrenr	np = ni->nai_np;
2643255332Scy	st_ip = np->in_snip;
2644255332Scy	st_port = np->in_spnext;
2645255332Scy	flags = nat->nat_flags;
2646145522Sdarrenr
2647255332Scy	if (flags & IPN_ICMPQUERY) {
2648255332Scy		sport = fin->fin_data[1];
2649255332Scy		dport = 0;
2650255332Scy	} else {
2651255332Scy		sport = htons(fin->fin_data[0]);
2652255332Scy		dport = htons(fin->fin_data[1]);
2653255332Scy	}
2654255332Scy
2655145522Sdarrenr	/*
2656145522Sdarrenr	 * Do a loop until we either run out of entries to try or we find
2657145522Sdarrenr	 * a NAT mapping that isn't currently being used.  This is done
2658145522Sdarrenr	 * because the change to the source is not (usually) being fixed.
2659145522Sdarrenr	 */
2660145522Sdarrenr	do {
2661145522Sdarrenr		port = 0;
2662255332Scy		in.s_addr = htonl(np->in_snip);
2663145522Sdarrenr		if (l == 0) {
2664145522Sdarrenr			/*
2665145522Sdarrenr			 * Check to see if there is an existing NAT
2666145522Sdarrenr			 * setup for this IP address pair.
2667145522Sdarrenr			 */
2668255332Scy			hm = ipf_nat_hostmap(softn, np, fin->fin_src,
2669255332Scy					     fin->fin_dst, in, 0);
2670145522Sdarrenr			if (hm != NULL)
2671255332Scy				in.s_addr = hm->hm_nsrcip.s_addr;
2672145522Sdarrenr		} else if ((l == 1) && (hm != NULL)) {
2673255332Scy			ipf_nat_hostmapdel(softc, &hm);
2674145522Sdarrenr		}
2675145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
2676145522Sdarrenr
2677145522Sdarrenr		nat->nat_hm = hm;
2678145522Sdarrenr
2679255332Scy		if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) {
2680255332Scy			if (l > 0) {
2681255332Scy				NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1);
2682338170Scy				DT4(ns_exhausted_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
2683145522Sdarrenr				return -1;
2684255332Scy			}
2685145522Sdarrenr		}
2686145522Sdarrenr
2687145522Sdarrenr		if (np->in_redir == NAT_BIMAP &&
2688255332Scy		    np->in_osrcmsk == np->in_nsrcmsk) {
2689145522Sdarrenr			/*
2690145522Sdarrenr			 * map the address block in a 1:1 fashion
2691145522Sdarrenr			 */
2692255332Scy			in.s_addr = np->in_nsrcaddr;
2693255332Scy			in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk;
2694145522Sdarrenr			in.s_addr = ntohl(in.s_addr);
2695145522Sdarrenr
2696145522Sdarrenr		} else if (np->in_redir & NAT_MAPBLK) {
2697145522Sdarrenr			if ((l >= np->in_ppip) || ((l > 0) &&
2698255332Scy			     !(flags & IPN_TCPUDP))) {
2699255332Scy				NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2);
2700338170Scy				DT4(ns_exhausted_2, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
2701145522Sdarrenr				return -1;
2702255332Scy			}
2703145522Sdarrenr			/*
2704145522Sdarrenr			 * map-block - Calculate destination address.
2705145522Sdarrenr			 */
2706145522Sdarrenr			in.s_addr = ntohl(fin->fin_saddr);
2707255332Scy			in.s_addr &= ntohl(~np->in_osrcmsk);
2708145522Sdarrenr			inb.s_addr = in.s_addr;
2709145522Sdarrenr			in.s_addr /= np->in_ippip;
2710255332Scy			in.s_addr &= ntohl(~np->in_nsrcmsk);
2711255332Scy			in.s_addr += ntohl(np->in_nsrcaddr);
2712145522Sdarrenr			/*
2713145522Sdarrenr			 * Calculate destination port.
2714145522Sdarrenr			 */
2715145522Sdarrenr			if ((flags & IPN_TCPUDP) &&
2716145522Sdarrenr			    (np->in_ppip != 0)) {
2717145522Sdarrenr				port = ntohs(sport) + l;
2718145522Sdarrenr				port %= np->in_ppip;
2719145522Sdarrenr				port += np->in_ppip *
2720145522Sdarrenr					(inb.s_addr % np->in_ippip);
2721145522Sdarrenr				port += MAPBLK_MINPORT;
2722145522Sdarrenr				port = htons(port);
2723145522Sdarrenr			}
2724145522Sdarrenr
2725255332Scy		} else if ((np->in_nsrcaddr == 0) &&
2726255332Scy			   (np->in_nsrcmsk == 0xffffffff)) {
2727255332Scy			i6addr_t in6;
2728255332Scy
2729145522Sdarrenr			/*
2730145522Sdarrenr			 * 0/32 - use the interface's IP address.
2731145522Sdarrenr			 */
2732145522Sdarrenr			if ((l > 0) ||
2733255332Scy			    ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp,
2734255332Scy				       &in6, NULL) == -1) {
2735255332Scy				NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1);
2736338170Scy				DT4(ns_new_ifpaddr_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
2737145522Sdarrenr				return -1;
2738255332Scy			}
2739255332Scy			in.s_addr = ntohl(in6.in4.s_addr);
2740145522Sdarrenr
2741255332Scy		} else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) {
2742145522Sdarrenr			/*
2743145522Sdarrenr			 * 0/0 - use the original source address/port.
2744145522Sdarrenr			 */
2745255332Scy			if (l > 0) {
2746255332Scy				NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3);
2747338170Scy				DT4(ns_exhausted_3, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
2748145522Sdarrenr				return -1;
2749255332Scy			}
2750145522Sdarrenr			in.s_addr = ntohl(fin->fin_saddr);
2751145522Sdarrenr
2752255332Scy		} else if ((np->in_nsrcmsk != 0xffffffff) &&
2753255332Scy			   (np->in_spnext == 0) && ((l > 0) || (hm == NULL)))
2754255332Scy			np->in_snip++;
2755145522Sdarrenr
2756145522Sdarrenr		natl = NULL;
2757145522Sdarrenr
2758145522Sdarrenr		if ((flags & IPN_TCPUDP) &&
2759145522Sdarrenr		    ((np->in_redir & NAT_MAPBLK) == 0) &&
2760145522Sdarrenr		    (np->in_flags & IPN_AUTOPORTMAP)) {
2761145522Sdarrenr			/*
2762145522Sdarrenr			 * "ports auto" (without map-block)
2763145522Sdarrenr			 */
2764145522Sdarrenr			if ((l > 0) && (l % np->in_ppip == 0)) {
2765255332Scy				if ((l > np->in_ppip) &&
2766255332Scy				    np->in_nsrcmsk != 0xffffffff)
2767255332Scy					np->in_snip++;
2768145522Sdarrenr			}
2769145522Sdarrenr			if (np->in_ppip != 0) {
2770145522Sdarrenr				port = ntohs(sport);
2771145522Sdarrenr				port += (l % np->in_ppip);
2772145522Sdarrenr				port %= np->in_ppip;
2773145522Sdarrenr				port += np->in_ppip *
2774145522Sdarrenr					(ntohl(fin->fin_saddr) %
2775145522Sdarrenr					 np->in_ippip);
2776145522Sdarrenr				port += MAPBLK_MINPORT;
2777145522Sdarrenr				port = htons(port);
2778145522Sdarrenr			}
2779145522Sdarrenr
2780145522Sdarrenr		} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
2781255332Scy			   (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) {
2782145522Sdarrenr			/*
2783145522Sdarrenr			 * Standard port translation.  Select next port.
2784145522Sdarrenr			 */
2785180778Sdarrenr			if (np->in_flags & IPN_SEQUENTIAL) {
2786255332Scy				port = np->in_spnext;
2787180778Sdarrenr			} else {
2788255332Scy				port = ipf_random() % (np->in_spmax -
2789255332Scy						       np->in_spmin + 1);
2790255332Scy				port += np->in_spmin;
2791180778Sdarrenr			}
2792180832Sdarrenr			port = htons(port);
2793255332Scy			np->in_spnext++;
2794145522Sdarrenr
2795255332Scy			if (np->in_spnext > np->in_spmax) {
2796255332Scy				np->in_spnext = np->in_spmin;
2797255332Scy				if (np->in_nsrcmsk != 0xffffffff)
2798255332Scy					np->in_snip++;
2799145522Sdarrenr			}
2800145522Sdarrenr		}
2801145522Sdarrenr
2802255332Scy		if (np->in_flags & IPN_SIPRANGE) {
2803255332Scy			if (np->in_snip > ntohl(np->in_nsrcmsk))
2804255332Scy				np->in_snip = ntohl(np->in_nsrcaddr);
2805145522Sdarrenr		} else {
2806255332Scy			if ((np->in_nsrcmsk != 0xffffffff) &&
2807255332Scy			    ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) >
2808255332Scy			    ntohl(np->in_nsrcaddr))
2809255332Scy				np->in_snip = ntohl(np->in_nsrcaddr) + 1;
2810145522Sdarrenr		}
2811145522Sdarrenr
2812145522Sdarrenr		if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
2813145522Sdarrenr			port = sport;
2814145522Sdarrenr
2815145522Sdarrenr		/*
2816145522Sdarrenr		 * Here we do a lookup of the connection as seen from
2817145522Sdarrenr		 * the outside.  If an IP# pair already exists, try
2818145522Sdarrenr		 * again.  So if you have A->B becomes C->B, you can
2819145522Sdarrenr		 * also have D->E become C->E but not D->B causing
2820145522Sdarrenr		 * another C->B.  Also take protocol and ports into
2821145522Sdarrenr		 * account when determining whether a pre-existing
2822145522Sdarrenr		 * NAT setup will cause an external conflict where
2823145522Sdarrenr		 * this is appropriate.
2824145522Sdarrenr		 */
2825145522Sdarrenr		inb.s_addr = htonl(in.s_addr);
2826145522Sdarrenr		sp = fin->fin_data[0];
2827145522Sdarrenr		dp = fin->fin_data[1];
2828145522Sdarrenr		fin->fin_data[0] = fin->fin_data[1];
2829255332Scy		fin->fin_data[1] = ntohs(port);
2830255332Scy		natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
2831255332Scy					(u_int)fin->fin_p, fin->fin_dst, inb);
2832145522Sdarrenr		fin->fin_data[0] = sp;
2833145522Sdarrenr		fin->fin_data[1] = dp;
2834145522Sdarrenr
2835145522Sdarrenr		/*
2836145522Sdarrenr		 * Has the search wrapped around and come back to the
2837145522Sdarrenr		 * start ?
2838145522Sdarrenr		 */
2839145522Sdarrenr		if ((natl != NULL) &&
2840255332Scy		    (np->in_spnext != 0) && (st_port == np->in_spnext) &&
2841255332Scy		    (np->in_snip != 0) && (st_ip == np->in_snip)) {
2842255332Scy			NBUMPSIDED(1, ns_wrap);
2843338170Scy			DT4(ns_wrap, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
2844145522Sdarrenr			return -1;
2845255332Scy		}
2846145522Sdarrenr		l++;
2847145522Sdarrenr	} while (natl != NULL);
2848145522Sdarrenr
2849145522Sdarrenr	/* Setup the NAT table */
2850255332Scy	nat->nat_osrcip = fin->fin_src;
2851255332Scy	nat->nat_nsrcaddr = htonl(in.s_addr);
2852255332Scy	nat->nat_odstip = fin->fin_dst;
2853255332Scy	nat->nat_ndstip = fin->fin_dst;
2854145522Sdarrenr	if (nat->nat_hm == NULL)
2855255332Scy		nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src,
2856255332Scy					      fin->fin_dst, nat->nat_nsrcip,
2857255332Scy					      0);
2858145522Sdarrenr
2859145522Sdarrenr	if (flags & IPN_TCPUDP) {
2860255332Scy		nat->nat_osport = sport;
2861255332Scy		nat->nat_nsport = port;	/* sport */
2862255332Scy		nat->nat_odport = dport;
2863255332Scy		nat->nat_ndport = dport;
2864145522Sdarrenr		((tcphdr_t *)fin->fin_dp)->th_sport = port;
2865145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
2866255332Scy		nat->nat_oicmpid = fin->fin_data[1];
2867145522Sdarrenr		((icmphdr_t *)fin->fin_dp)->icmp_id = port;
2868255332Scy		nat->nat_nicmpid = port;
2869145522Sdarrenr	}
2870145522Sdarrenr	return 0;
2871145522Sdarrenr}
2872145522Sdarrenr
2873145522Sdarrenr
2874145522Sdarrenr/* ------------------------------------------------------------------------ */
2875255332Scy/* Function:    ipf_nat_newrdr                                              */
2876145522Sdarrenr/* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
2877145522Sdarrenr/*                    allow rule to be moved if IPN_ROUNDR is set.          */
2878145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
2879145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
2880145522Sdarrenr/*              ni(I)  - pointer to structure with misc. information needed */
2881145522Sdarrenr/*                       to create new NAT entry.                           */
2882145522Sdarrenr/*                                                                          */
2883145522Sdarrenr/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
2884145522Sdarrenr/* to the new IP address for the translation.                               */
2885145522Sdarrenr/* ------------------------------------------------------------------------ */
2886255332Scystatic int
2887255332Scyipf_nat_newrdr(fin, nat, ni)
2888255332Scy	fr_info_t *fin;
2889255332Scy	nat_t *nat;
2890255332Scy	natinfo_t *ni;
2891145522Sdarrenr{
2892255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
2893255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2894145522Sdarrenr	u_short nport, dport, sport;
2895170268Sdarrenr	struct in_addr in, inb;
2896170268Sdarrenr	u_short sp, dp;
2897145522Sdarrenr	hostmap_t *hm;
2898145522Sdarrenr	u_32_t flags;
2899145522Sdarrenr	ipnat_t *np;
2900170268Sdarrenr	nat_t *natl;
2901145522Sdarrenr	int move;
2902145522Sdarrenr
2903145522Sdarrenr	move = 1;
2904145522Sdarrenr	hm = NULL;
2905145522Sdarrenr	in.s_addr = 0;
2906145522Sdarrenr	np = ni->nai_np;
2907255332Scy	flags = nat->nat_flags;
2908145522Sdarrenr
2909255332Scy	if (flags & IPN_ICMPQUERY) {
2910255332Scy		dport = fin->fin_data[1];
2911255332Scy		sport = 0;
2912255332Scy	} else {
2913255332Scy		sport = htons(fin->fin_data[0]);
2914255332Scy		dport = htons(fin->fin_data[1]);
2915255332Scy	}
2916255332Scy
2917255332Scy	/* TRACE sport, dport */
2918255332Scy
2919255332Scy
2920145522Sdarrenr	/*
2921145522Sdarrenr	 * If the matching rule has IPN_STICKY set, then we want to have the
2922145522Sdarrenr	 * same rule kick in as before.  Why would this happen?  If you have
2923145522Sdarrenr	 * a collection of rdr rules with "round-robin sticky", the current
2924145522Sdarrenr	 * packet might match a different one to the previous connection but
2925145522Sdarrenr	 * we want the same destination to be used.
2926145522Sdarrenr	 */
2927153876Sguido	if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) &&
2928153876Sguido	    ((np->in_flags & IPN_STICKY) != 0)) {
2929255332Scy		hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst,
2930255332Scy				     in, (u_32_t)dport);
2931145522Sdarrenr		if (hm != NULL) {
2932255332Scy			in.s_addr = ntohl(hm->hm_ndstip.s_addr);
2933145522Sdarrenr			np = hm->hm_ipnat;
2934145522Sdarrenr			ni->nai_np = np;
2935145522Sdarrenr			move = 0;
2936255332Scy			ipf_nat_hostmapdel(softc, &hm);
2937145522Sdarrenr		}
2938145522Sdarrenr	}
2939145522Sdarrenr
2940145522Sdarrenr	/*
2941145522Sdarrenr	 * Otherwise, it's an inbound packet. Most likely, we don't
2942145522Sdarrenr	 * want to rewrite source ports and source addresses. Instead,
2943145522Sdarrenr	 * we want to rewrite to a fixed internal address and fixed
2944145522Sdarrenr	 * internal port.
2945145522Sdarrenr	 */
2946145522Sdarrenr	if (np->in_flags & IPN_SPLIT) {
2947255332Scy		in.s_addr = np->in_dnip;
2948272998Scy		inb.s_addr = htonl(in.s_addr);
2949145522Sdarrenr
2950145522Sdarrenr		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
2951255332Scy			hm = ipf_nat_hostmap(softn, NULL, fin->fin_src,
2952272998Scy					     fin->fin_dst, inb, (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);
2979338170Scy			DT3(ns_new_ifpaddr_2, fr_info_t *, fin, nat_t *, nat, natinfo_t, ni);
2980145522Sdarrenr			return -1;
2981255332Scy		}
2982255332Scy		in.s_addr = ntohl(in6.in4.s_addr);
2983145522Sdarrenr
2984255332Scy	} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) {
2985145522Sdarrenr		/*
2986145522Sdarrenr		 * 0/0 - use the original destination address/port.
2987145522Sdarrenr		 */
2988145522Sdarrenr		in.s_addr = ntohl(fin->fin_daddr);
2989145522Sdarrenr
2990145522Sdarrenr	} else if (np->in_redir == NAT_BIMAP &&
2991255332Scy		   np->in_ndstmsk == np->in_odstmsk) {
2992145522Sdarrenr		/*
2993145522Sdarrenr		 * map the address block in a 1:1 fashion
2994145522Sdarrenr		 */
2995255332Scy		in.s_addr = np->in_ndstaddr;
2996255332Scy		in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk;
2997145522Sdarrenr		in.s_addr = ntohl(in.s_addr);
2998145522Sdarrenr	} else {
2999255332Scy		in.s_addr = ntohl(np->in_ndstaddr);
3000145522Sdarrenr	}
3001145522Sdarrenr
3002255332Scy	if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
3003145522Sdarrenr		nport = dport;
3004145522Sdarrenr	else {
3005145522Sdarrenr		/*
3006145522Sdarrenr		 * Whilst not optimized for the case where
3007145522Sdarrenr		 * pmin == pmax, the gain is not significant.
3008145522Sdarrenr		 */
3009145522Sdarrenr		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
3010255332Scy		    (np->in_odport != np->in_dtop)) {
3011255332Scy			nport = ntohs(dport) - np->in_odport + np->in_dpmax;
3012145522Sdarrenr			nport = htons(nport);
3013255332Scy		} else {
3014255332Scy			nport = htons(np->in_dpnext);
3015255332Scy			np->in_dpnext++;
3016255332Scy			if (np->in_dpnext > np->in_dpmax)
3017255332Scy				np->in_dpnext = np->in_dpmin;
3018255332Scy		}
3019145522Sdarrenr	}
3020145522Sdarrenr
3021145522Sdarrenr	/*
3022145522Sdarrenr	 * When the redirect-to address is set to 0.0.0.0, just
3023145522Sdarrenr	 * assume a blank `forwarding' of the packet.  We don't
3024145522Sdarrenr	 * setup any translation for this either.
3025145522Sdarrenr	 */
3026145522Sdarrenr	if (in.s_addr == 0) {
3027255332Scy		if (nport == dport) {
3028255332Scy			NBUMPSIDED(0, ns_xlate_null);
3029145522Sdarrenr			return -1;
3030255332Scy		}
3031145522Sdarrenr		in.s_addr = ntohl(fin->fin_daddr);
3032145522Sdarrenr	}
3033145522Sdarrenr
3034170268Sdarrenr	/*
3035170268Sdarrenr	 * Check to see if this redirect mapping already exists and if
3036170268Sdarrenr	 * it does, return "failure" (allowing it to be created will just
3037170268Sdarrenr	 * cause one or both of these "connections" to stop working.)
3038170268Sdarrenr	 */
3039170268Sdarrenr	inb.s_addr = htonl(in.s_addr);
3040170268Sdarrenr	sp = fin->fin_data[0];
3041170268Sdarrenr	dp = fin->fin_data[1];
3042170268Sdarrenr	fin->fin_data[1] = fin->fin_data[0];
3043170268Sdarrenr	fin->fin_data[0] = ntohs(nport);
3044255332Scy	natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
3045170268Sdarrenr			     (u_int)fin->fin_p, inb, fin->fin_src);
3046170268Sdarrenr	fin->fin_data[0] = sp;
3047170268Sdarrenr	fin->fin_data[1] = dp;
3048255332Scy	if (natl != NULL) {
3049255332Scy		DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl);
3050255332Scy		NBUMPSIDE(0, ns_xlate_exists);
3051170268Sdarrenr		return -1;
3052255332Scy	}
3053170268Sdarrenr
3054272998Scy	inb.s_addr = htonl(in.s_addr);
3055255332Scy	nat->nat_ndstaddr = htonl(in.s_addr);
3056255332Scy	nat->nat_odstip = fin->fin_dst;
3057255332Scy	nat->nat_nsrcip = fin->fin_src;
3058255332Scy	nat->nat_osrcip = fin->fin_src;
3059153876Sguido	if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
3060255332Scy		nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src,
3061272998Scy					      fin->fin_dst, inb, (u_32_t)dport);
3062145522Sdarrenr
3063145522Sdarrenr	if (flags & IPN_TCPUDP) {
3064255332Scy		nat->nat_odport = dport;
3065255332Scy		nat->nat_ndport = nport;
3066255332Scy		nat->nat_osport = sport;
3067255332Scy		nat->nat_nsport = sport;
3068145522Sdarrenr		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
3069145522Sdarrenr	} else if (flags & IPN_ICMPQUERY) {
3070255332Scy		nat->nat_oicmpid = fin->fin_data[1];
3071145522Sdarrenr		((icmphdr_t *)fin->fin_dp)->icmp_id = nport;
3072255332Scy		nat->nat_nicmpid = nport;
3073145522Sdarrenr	}
3074145522Sdarrenr
3075145522Sdarrenr	return move;
3076145522Sdarrenr}
3077145522Sdarrenr
3078145522Sdarrenr/* ------------------------------------------------------------------------ */
3079255332Scy/* Function:    ipf_nat_add                                                 */
3080145522Sdarrenr/* Returns:     nat_t* - NULL == failure to create new NAT structure,       */
3081145522Sdarrenr/*                       else pointer to new NAT structure                  */
3082145522Sdarrenr/* Parameters:  fin(I)       - pointer to packet information                */
3083145522Sdarrenr/*              np(I)        - pointer to NAT rule                          */
3084145522Sdarrenr/*              natsave(I)   - pointer to where to store NAT struct pointer */
3085145522Sdarrenr/*              flags(I)     - flags describing the current packet          */
3086145522Sdarrenr/*              direction(I) - direction of packet (in/out)                 */
3087145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
3088145522Sdarrenr/*                                                                          */
3089145522Sdarrenr/* Attempts to create a new NAT entry.  Does not actually change the packet */
3090145522Sdarrenr/* in any way.                                                              */
3091145522Sdarrenr/*                                                                          */
3092145522Sdarrenr/* This fucntion is in three main parts: (1) deal with creating a new NAT   */
3093145522Sdarrenr/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
3094145522Sdarrenr/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
3095145522Sdarrenr/* and (3) building that structure and putting it into the NAT table(s).    */
3096161356Sguido/*                                                                          */
3097161356Sguido/* NOTE: natsave should NOT be used top point back to an ipstate_t struct   */
3098161356Sguido/*       as it can result in memory being corrupted.                        */
3099145522Sdarrenr/* ------------------------------------------------------------------------ */
3100255332Scynat_t *
3101255332Scyipf_nat_add(fin, np, natsave, flags, direction)
3102255332Scy	fr_info_t *fin;
3103255332Scy	ipnat_t *np;
3104255332Scy	nat_t **natsave;
3105255332Scy	u_int flags;
3106255332Scy	int direction;
310753642Sguido{
3108255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3109255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
311060852Sdarrenr	hostmap_t *hm = NULL;
311160852Sdarrenr	nat_t *nat, *natl;
3112255332Scy	natstat_t *nsp;
3113145522Sdarrenr	u_int nflags;
3114145522Sdarrenr	natinfo_t ni;
3115145522Sdarrenr	int move;
311653642Sguido
3117255332Scy	nsp = &softn->ipf_nat_stats;
3118255332Scy
3119255332Scy	if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) >
3120255332Scy	    softn->ipf_nat_table_wm_high) {
3121255332Scy		softn->ipf_nat_doflush = 1;
3122255332Scy	}
3123255332Scy
3124255332Scy	if (nsp->ns_active >= softn->ipf_nat_table_max) {
3125255332Scy		NBUMPSIDED(fin->fin_out, ns_table_max);
3126338170Scy		DT2(ns_table_max, nat_stat_t *, nsp, ipf_nat_softc_t *, softn);
3127130886Sdarrenr		return NULL;
3128130886Sdarrenr	}
3129130886Sdarrenr
3130145522Sdarrenr	move = 1;
3131145522Sdarrenr	nflags = np->in_flags & flags;
3132145522Sdarrenr	nflags &= NAT_FROMRULE;
313353642Sguido
3134145522Sdarrenr	ni.nai_np = np;
3135170268Sdarrenr	ni.nai_dport = 0;
3136170268Sdarrenr	ni.nai_sport = 0;
3137145522Sdarrenr
313853642Sguido	/* Give me a new nat */
313953642Sguido	KMALLOC(nat, nat_t *);
314060852Sdarrenr	if (nat == NULL) {
3141338170Scy		DT(ns_memfail);
3142255332Scy		NBUMPSIDED(fin->fin_out, ns_memfail);
3143130886Sdarrenr		/*
3144130886Sdarrenr		 * Try to automatically tune the max # of entries in the
3145130886Sdarrenr		 * table allowed to be less than what will cause kmem_alloc()
3146130886Sdarrenr		 * to fail and try to eliminate panics due to out of memory
3147130886Sdarrenr		 * conditions arising.
3148130886Sdarrenr		 */
3149255332Scy		if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) &&
3150255332Scy		    (nsp->ns_active > 100)) {
3151255332Scy			softn->ipf_nat_table_max = nsp->ns_active - 100;
3152255332Scy			printf("table_max reduced to %d\n",
3153255332Scy				softn->ipf_nat_table_max);
3154130886Sdarrenr		}
315553642Sguido		return NULL;
315660852Sdarrenr	}
315753642Sguido
3158255332Scy	if (flags & IPN_ICMPQUERY) {
3159145522Sdarrenr		/*
3160145522Sdarrenr		 * In the ICMP query NAT code, we translate the ICMP id fields
3161145522Sdarrenr		 * to make them unique. This is indepedent of the ICMP type
3162145522Sdarrenr		 * (e.g. in the unlikely event that a host sends an echo and
3163145522Sdarrenr		 * an tstamp request with the same id, both packets will have
3164145522Sdarrenr		 * their ip address/id field changed in the same way).
3165145522Sdarrenr		 */
3166145522Sdarrenr		/* The icmp_id field is used by the sender to identify the
3167145522Sdarrenr		 * process making the icmp request. (the receiver justs
3168145522Sdarrenr		 * copies it back in its response). So, it closely matches
3169145522Sdarrenr		 * the concept of source port. We overlay sport, so we can
3170145522Sdarrenr		 * maximally reuse the existing code.
3171145522Sdarrenr		 */
3172255332Scy		ni.nai_sport = fin->fin_data[1];
3173255332Scy		ni.nai_dport = 0;
3174145522Sdarrenr	}
3175145522Sdarrenr
317653642Sguido	bzero((char *)nat, sizeof(*nat));
317753642Sguido	nat->nat_flags = flags;
3178170268Sdarrenr	nat->nat_redir = np->in_redir;
3179255332Scy	nat->nat_dir = direction;
3180255332Scy	nat->nat_pr[0] = fin->fin_p;
3181255332Scy	nat->nat_pr[1] = fin->fin_p;
3182145522Sdarrenr
318353642Sguido	/*
3184255332Scy	 * Search the current table for a match and create a new mapping
3185255332Scy	 * if there is none found.
318653642Sguido	 */
3187255332Scy	if (np->in_redir & NAT_DIVERTUDP) {
3188255332Scy		move = ipf_nat_newdivert(fin, nat, &ni);
3189255332Scy
3190255332Scy	} else if (np->in_redir & NAT_REWRITE) {
3191255332Scy		move = ipf_nat_newrewrite(fin, nat, &ni);
3192255332Scy
3193255332Scy	} else if (direction == NAT_OUTBOUND) {
319453642Sguido		/*
3195145522Sdarrenr		 * We can now arrange to call this for the same connection
3196145522Sdarrenr		 * because ipf_nat_new doesn't protect the code path into
3197145522Sdarrenr		 * this function.
319853642Sguido		 */
3199255332Scy		natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p,
3200145522Sdarrenr				     fin->fin_src, fin->fin_dst);
3201145522Sdarrenr		if (natl != NULL) {
3202161356Sguido			KFREE(nat);
3203145522Sdarrenr			nat = natl;
3204145522Sdarrenr			goto done;
3205145522Sdarrenr		}
320653642Sguido
3207255332Scy		move = ipf_nat_newmap(fin, nat, &ni);
320853642Sguido	} else {
320953642Sguido		/*
3210255332Scy		 * NAT_INBOUND is used for redirects rules
321153642Sguido		 */
3212255332Scy		natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p,
3213255332Scy					fin->fin_src, fin->fin_dst);
3214145522Sdarrenr		if (natl != NULL) {
3215161356Sguido			KFREE(nat);
3216145522Sdarrenr			nat = natl;
3217145522Sdarrenr			goto done;
321860852Sdarrenr		}
321953642Sguido
3220255332Scy		move = ipf_nat_newrdr(fin, nat, &ni);
3221145522Sdarrenr	}
3222255332Scy	if (move == -1)
3223255332Scy		goto badnat;
322453642Sguido
3225255332Scy	np = ni.nai_np;
3226255332Scy
3227255332Scy	nat->nat_mssclamp = np->in_mssclamp;
3228255332Scy	nat->nat_me = natsave;
3229255332Scy	nat->nat_fr = fin->fin_fr;
3230255332Scy	nat->nat_rev = fin->fin_rev;
3231255332Scy	nat->nat_ptr = np;
3232255332Scy	nat->nat_dlocal = np->in_dlocal;
3233255332Scy
3234255332Scy	if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) {
3235255332Scy		if (ipf_proxy_new(fin, nat) == -1) {
3236255332Scy			NBUMPSIDED(fin->fin_out, ns_appr_fail);
3237338170Scy			DT3(ns_appr_fail, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np);
3238255332Scy			goto badnat;
323953642Sguido		}
324053642Sguido	}
324153642Sguido
3242255332Scy	nat->nat_ifps[0] = np->in_ifps[0];
3243255332Scy	if (np->in_ifps[0] != NULL) {
3244255332Scy		COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]);
3245145522Sdarrenr	}
3246145522Sdarrenr
3247255332Scy	nat->nat_ifps[1] = np->in_ifps[1];
3248255332Scy	if (np->in_ifps[1] != NULL) {
3249255332Scy		COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]);
3250255332Scy	}
325153642Sguido
3252255332Scy	if (ipf_nat_finalise(fin, nat) == -1) {
3253255332Scy		goto badnat;
3254255332Scy	}
325553642Sguido
3256255332Scy	np->in_use++;
325753642Sguido
3258255332Scy	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
3259255332Scy		if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) {
3260255332Scy			ipf_nat_delrdr(softn, np);
3261255332Scy			ipf_nat_addrdr(softn, np);
3262255332Scy		} else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) {
3263255332Scy			ipf_nat_delmap(softn, np);
3264255332Scy			ipf_nat_addmap(softn, np);
3265145522Sdarrenr		}
3266145522Sdarrenr	}
326753642Sguido
3268145522Sdarrenr	if (flags & SI_WILDP)
3269255332Scy		nsp->ns_wilds++;
3270255332Scy	nsp->ns_proto[nat->nat_pr[0]]++;
3271255332Scy
3272145522Sdarrenr	goto done;
3273145522Sdarrenrbadnat:
3274338169Scy	DT3(ns_badnatnew, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np);
3275255332Scy	NBUMPSIDE(fin->fin_out, ns_badnatnew);
3276145522Sdarrenr	if ((hm = nat->nat_hm) != NULL)
3277255332Scy		ipf_nat_hostmapdel(softc, &hm);
3278145522Sdarrenr	KFREE(nat);
3279145522Sdarrenr	nat = NULL;
3280145522Sdarrenrdone:
3281255332Scy	if (nat != NULL && np != NULL)
3282255332Scy		np->in_hits++;
3283255332Scy	if (natsave != NULL)
3284255332Scy		*natsave = nat;
3285145522Sdarrenr	return nat;
3286145522Sdarrenr}
328760852Sdarrenr
328860852Sdarrenr
3289145522Sdarrenr/* ------------------------------------------------------------------------ */
3290255332Scy/* Function:    ipf_nat_finalise                                            */
3291145522Sdarrenr/* Returns:     int - 0 == sucess, -1 == failure                            */
3292145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
3293145522Sdarrenr/*              nat(I) - pointer to NAT entry                               */
3294145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
3295145522Sdarrenr/*                                                                          */
3296145522Sdarrenr/* This is the tail end of constructing a new NAT entry and is the same     */
3297145522Sdarrenr/* for both IPv4 and IPv6.                                                  */
3298145522Sdarrenr/* ------------------------------------------------------------------------ */
3299145522Sdarrenr/*ARGSUSED*/
3300255332Scystatic int
3301255332Scyipf_nat_finalise(fin, nat)
3302255332Scy	fr_info_t *fin;
3303255332Scy	nat_t *nat;
3304145522Sdarrenr{
3305255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3306255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3307255332Scy	u_32_t sum1, sum2, sumd;
3308145522Sdarrenr	frentry_t *fr;
3309255332Scy	u_32_t flags;
3310255332Scy#if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6) && defined(ICK_M_CTL_MAGIC)
3311255332Scy	qpktinfo_t *qpi = fin->fin_qpi;
3312255332Scy#endif
3313145522Sdarrenr
3314255332Scy	flags = nat->nat_flags;
3315145522Sdarrenr
3316255332Scy	switch (nat->nat_pr[0])
3317255332Scy	{
3318255332Scy	case IPPROTO_ICMP :
3319255332Scy		sum1 = LONG_SUM(ntohs(nat->nat_oicmpid));
3320255332Scy		sum2 = LONG_SUM(ntohs(nat->nat_nicmpid));
3321255332Scy		CALC_SUMD(sum1, sum2, sumd);
3322255332Scy		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
3323255332Scy
3324255332Scy		break;
3325255332Scy
3326255332Scy	default :
3327255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \
3328255332Scy				ntohs(nat->nat_osport));
3329255332Scy		sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \
3330255332Scy				ntohs(nat->nat_nsport));
3331255332Scy		CALC_SUMD(sum1, sum2, sumd);
3332255332Scy		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
3333255332Scy
3334255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \
3335255332Scy				ntohs(nat->nat_odport));
3336255332Scy		sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \
3337255332Scy				ntohs(nat->nat_ndport));
3338255332Scy		CALC_SUMD(sum1, sum2, sumd);
3339255332Scy		nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16);
3340255332Scy		break;
3341161356Sguido	}
3342255332Scy
3343255332Scy	/*
3344255332Scy	 * Compute the partial checksum, just in case.
3345255332Scy	 * This is only ever placed into outbound packets so care needs
3346255332Scy	 * to be taken over which pair of addresses are used.
3347255332Scy	 */
3348255332Scy	if (nat->nat_dir == NAT_OUTBOUND) {
3349255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
3350255332Scy		sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr));
3351255332Scy	} else {
3352255332Scy		sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr));
3353255332Scy		sum1 += LONG_SUM(ntohl(nat->nat_odstaddr));
3354161356Sguido	}
3355255332Scy	sum1 += nat->nat_pr[1];
3356255332Scy	nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16);
3357145522Sdarrenr
3358255332Scy	sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr));
3359255332Scy	sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
3360255332Scy	CALC_SUMD(sum1, sum2, sumd);
3361255332Scy	nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
336292685Sdarrenr
3363255332Scy	sum1 = LONG_SUM(ntohl(nat->nat_odstaddr));
3364255332Scy	sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr));
3365255332Scy	CALC_SUMD(sum1, sum2, sumd);
3366255332Scy	nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16);
336792685Sdarrenr
3368255332Scy	nat->nat_v[0] = 4;
3369255332Scy	nat->nat_v[1] = 4;
3370255332Scy
3371255332Scy	if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
3372255332Scy		nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
3373255332Scy	}
3374255332Scy
3375255332Scy	if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
3376255332Scy		nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
3377255332Scy	}
3378255332Scy
3379255332Scy	if ((nat->nat_flags & SI_CLONE) == 0)
3380255332Scy		nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat);
3381255332Scy
3382255332Scy	if (ipf_nat_insert(softc, softn, nat) == 0) {
3383255332Scy		if (softn->ipf_nat_logging)
3384255332Scy			ipf_nat_log(softc, softn, nat, NL_NEW);
3385255332Scy		fr = nat->nat_fr;
3386145522Sdarrenr		if (fr != NULL) {
3387145522Sdarrenr			MUTEX_ENTER(&fr->fr_lock);
3388145522Sdarrenr			fr->fr_ref++;
3389145522Sdarrenr			MUTEX_EXIT(&fr->fr_lock);
3390145522Sdarrenr		}
3391145522Sdarrenr		return 0;
3392145522Sdarrenr	}
339392685Sdarrenr
3394255332Scy	NBUMPSIDED(fin->fin_out, ns_unfinalised);
3395338170Scy	DT2(ns_unfinalised, fr_info_t *, fin, nat_t *, nat);
3396145522Sdarrenr	/*
3397145522Sdarrenr	 * nat_insert failed, so cleanup time...
3398145522Sdarrenr	 */
3399255332Scy	if (nat->nat_sync != NULL)
3400255332Scy		ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync);
3401145522Sdarrenr	return -1;
340253642Sguido}
340353642Sguido
340453642Sguido
3405145522Sdarrenr/* ------------------------------------------------------------------------ */
3406255332Scy/* Function:    ipf_nat_insert                                              */
3407255332Scy/* Returns:     int - 0 == sucess, -1 == failure                            */
3408255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
3409255332Scy/*              softn(I) - pointer to NAT context structure                 */
3410255332Scy/*              nat(I) - pointer to NAT structure                           */
3411255332Scy/* Write Lock:  ipf_nat                                                     */
3412145522Sdarrenr/*                                                                          */
3413145522Sdarrenr/* Insert a NAT entry into the hash tables for searching and add it to the  */
3414145522Sdarrenr/* list of active NAT entries.  Adjust global counters when complete.       */
3415145522Sdarrenr/* ------------------------------------------------------------------------ */
3416255332Scyint
3417255332Scyipf_nat_insert(softc, softn, nat)
3418255332Scy	ipf_main_softc_t *softc;
3419255332Scy	ipf_nat_softc_t *softn;
3420255332Scy	nat_t *nat;
342160852Sdarrenr{
3422255332Scy	u_int hv0, hv1;
3423255332Scy	u_int sp, dp;
3424255332Scy	ipnat_t *in;
342560852Sdarrenr
3426145522Sdarrenr	/*
3427145522Sdarrenr	 * Try and return an error as early as possible, so calculate the hash
3428145522Sdarrenr	 * entry numbers first and then proceed.
3429145522Sdarrenr	 */
3430145522Sdarrenr	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
3431255332Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
3432255332Scy			sp = nat->nat_osport;
3433255332Scy			dp = nat->nat_odport;
3434255332Scy		} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
3435255332Scy			sp = 0;
3436255332Scy			dp = nat->nat_oicmpid;
3437255332Scy		} else {
3438255332Scy			sp = 0;
3439255332Scy			dp = 0;
3440255332Scy		}
3441255332Scy		hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff);
3442255332Scy		hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff);
3443255332Scy		/*
3444255332Scy		 * TRACE nat_osrcaddr, nat_osport, nat_odstaddr,
3445255332Scy		 * nat_odport, hv0
3446255332Scy		 */
3447255332Scy
3448255332Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
3449255332Scy			sp = nat->nat_nsport;
3450255332Scy			dp = nat->nat_ndport;
3451255332Scy		} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
3452255332Scy			sp = 0;
3453255332Scy			dp = nat->nat_nicmpid;
3454255332Scy		} else {
3455255332Scy			sp = 0;
3456255332Scy			dp = 0;
3457255332Scy		}
3458255332Scy		hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff);
3459255332Scy		hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff);
3460255332Scy		/*
3461255332Scy		 * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr,
3462255332Scy		 * nat_ndport, hv1
3463255332Scy		 */
346480482Sdarrenr	} else {
3465255332Scy		hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff);
3466255332Scy		hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff);
3467255332Scy		/* TRACE nat_osrcaddr, nat_odstaddr, hv0 */
346880482Sdarrenr
3469255332Scy		hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff);
3470255332Scy		hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff);
3471255332Scy		/* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */
3472145522Sdarrenr	}
3473145522Sdarrenr
3474255332Scy	nat->nat_hv[0] = hv0;
3475255332Scy	nat->nat_hv[1] = hv1;
3476145522Sdarrenr
3477145522Sdarrenr	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
3478145522Sdarrenr
3479255332Scy	in = nat->nat_ptr;
3480255332Scy	nat->nat_ref = nat->nat_me ? 2 : 1;
3481145522Sdarrenr
3482145522Sdarrenr	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
3483255332Scy	nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4);
3484145522Sdarrenr
3485161356Sguido	if (nat->nat_ifnames[1][0] != '\0') {
3486145522Sdarrenr		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
3487255332Scy		nat->nat_ifps[1] = ipf_resolvenic(softc,
3488255332Scy						  nat->nat_ifnames[1], 4);
3489255332Scy	} else if (in->in_ifnames[1] != -1) {
3490255332Scy		char *name;
3491255332Scy
3492255332Scy		name = in->in_names + in->in_ifnames[1];
3493255332Scy		if (name[1] != '\0' && name[0] != '-' && name[0] != '*') {
3494255332Scy			(void) strncpy(nat->nat_ifnames[1],
3495255332Scy				       nat->nat_ifnames[0], LIFNAMSIZ);
3496255332Scy			nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
3497255332Scy			nat->nat_ifps[1] = nat->nat_ifps[0];
3498255332Scy		}
3499145522Sdarrenr	}
3500255332Scy	if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
3501255332Scy		nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
3502255332Scy	}
3503255332Scy	if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
3504255332Scy		nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
3505255332Scy	}
3506145522Sdarrenr
3507255332Scy	return ipf_nat_hashtab_add(softc, softn, nat);
3508255332Scy}
3509145522Sdarrenr
3510255332Scy
3511255332Scy/* ------------------------------------------------------------------------ */
3512255332Scy/* Function:    ipf_nat_hashtab_add                                         */
3513255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
3514255332Scy/*              softn(I) - pointer to NAT context structure                 */
3515255332Scy/*              nat(I) - pointer to NAT structure                           */
3516255332Scy/*                                                                          */
3517255332Scy/* Handle the insertion of a NAT entry into the table/list.                 */
3518255332Scy/* ------------------------------------------------------------------------ */
3519255332Scyint
3520255332Scyipf_nat_hashtab_add(softc, softn, nat)
3521255332Scy	ipf_main_softc_t *softc;
3522255332Scy	ipf_nat_softc_t *softn;
3523255332Scy	nat_t *nat;
3524255332Scy{
3525255332Scy	nat_t **natp;
3526255332Scy	u_int hv0;
3527255332Scy	u_int hv1;
3528255332Scy
3529255332Scy	hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz;
3530255332Scy	hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz;
3531255332Scy
3532255332Scy	if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) {
3533255332Scy		u_int swap;
3534255332Scy
3535255332Scy		swap = hv0;
3536255332Scy		hv0 = hv1;
3537255332Scy		hv1 = swap;
3538255332Scy	}
3539255332Scy
3540255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >=
3541255332Scy	    softn->ipf_nat_maxbucket) {
3542255332Scy		DT1(ns_bucket_max_0, int,
3543255332Scy		    softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]);
3544255332Scy		NBUMPSIDE(0, ns_bucket_max);
3545255332Scy		return -1;
3546255332Scy	}
3547255332Scy
3548255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >=
3549255332Scy	    softn->ipf_nat_maxbucket) {
3550255332Scy		DT1(ns_bucket_max_1, int,
3551255332Scy		    softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]);
3552255332Scy		NBUMPSIDE(1, ns_bucket_max);
3553255332Scy		return -1;
3554255332Scy	}
3555255332Scy
3556255332Scy	/*
3557255332Scy	 * The ordering of operations in the list and hash table insertion
3558255332Scy	 * is very important.  The last operation for each task should be
3559255332Scy	 * to update the top of the list, after all the "nexts" have been
3560255332Scy	 * done so that walking the list while it is being done does not
3561255332Scy	 * find strange pointers.
3562255332Scy	 *
3563255332Scy	 * Global list of NAT instances
3564255332Scy	 */
3565255332Scy	nat->nat_next = softn->ipf_nat_instances;
3566255332Scy	nat->nat_pnext = &softn->ipf_nat_instances;
3567255332Scy	if (softn->ipf_nat_instances)
3568255332Scy		softn->ipf_nat_instances->nat_pnext = &nat->nat_next;
3569255332Scy	softn->ipf_nat_instances = nat;
3570255332Scy
3571255332Scy	/*
3572255332Scy	 * Inbound hash table.
3573255332Scy	 */
3574255332Scy	natp = &softn->ipf_nat_table[0][hv0];
357567614Sdarrenr	nat->nat_phnext[0] = natp;
357660852Sdarrenr	nat->nat_hnext[0] = *natp;
3577255332Scy	if (*natp) {
3578255332Scy		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
3579255332Scy	} else {
3580255332Scy		NBUMPSIDE(0, ns_inuse);
3581255332Scy	}
358260852Sdarrenr	*natp = nat;
3583255332Scy	NBUMPSIDE(0, ns_bucketlen[hv0]);
358467614Sdarrenr
3585255332Scy	/*
3586255332Scy	 * Outbound hash table.
3587255332Scy	 */
3588255332Scy	natp = &softn->ipf_nat_table[1][hv1];
3589255332Scy	nat->nat_phnext[1] = natp;
3590255332Scy	nat->nat_hnext[1] = *natp;
359167614Sdarrenr	if (*natp)
359267614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
3593255332Scy	else {
3594255332Scy		NBUMPSIDE(1, ns_inuse);
3595255332Scy	}
359660852Sdarrenr	*natp = nat;
3597255332Scy	NBUMPSIDE(1, ns_bucketlen[hv1]);
359860852Sdarrenr
3599255332Scy	ipf_nat_setqueue(softc, softn, nat);
3600145522Sdarrenr
3601255332Scy	if (nat->nat_dir & NAT_OUTBOUND) {
3602255332Scy		NBUMPSIDE(1, ns_added);
3603255332Scy	} else {
3604255332Scy		NBUMPSIDE(0, ns_added);
3605255332Scy	}
3606255332Scy	softn->ipf_nat_stats.ns_active++;
3607145522Sdarrenr	return 0;
360860852Sdarrenr}
360960852Sdarrenr
361060852Sdarrenr
3611145522Sdarrenr/* ------------------------------------------------------------------------ */
3612255332Scy/* Function:    ipf_nat_icmperrorlookup                                     */
3613145522Sdarrenr/* Returns:     nat_t* - point to matching NAT structure                    */
3614145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
3615145522Sdarrenr/*              dir(I) - direction of packet (in/out)                       */
3616145522Sdarrenr/*                                                                          */
3617145522Sdarrenr/* Check if the ICMP error message is related to an existing TCP, UDP or    */
3618145522Sdarrenr/* ICMP query nat entry.  It is assumed that the packet is already of the   */
3619145522Sdarrenr/* the required length.                                                     */
3620145522Sdarrenr/* ------------------------------------------------------------------------ */
3621255332Scynat_t *
3622255332Scyipf_nat_icmperrorlookup(fin, dir)
3623255332Scy	fr_info_t *fin;
3624255332Scy	int dir;
362553642Sguido{
3626255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3627255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3628145522Sdarrenr	int flags = 0, type, minlen;
3629145522Sdarrenr	icmphdr_t *icmp, *orgicmp;
3630255332Scy	nat_stat_side_t *nside;
363153642Sguido	tcphdr_t *tcp = NULL;
3632145522Sdarrenr	u_short data[2];
3633145522Sdarrenr	nat_t *nat;
363453642Sguido	ip_t *oip;
3635145522Sdarrenr	u_int p;
363653642Sguido
3637145522Sdarrenr	icmp = fin->fin_dp;
3638145522Sdarrenr	type = icmp->icmp_type;
3639255332Scy	nside = &softn->ipf_nat_stats.ns_side[fin->fin_out];
364053642Sguido	/*
364153642Sguido	 * Does it at least have the return (basic) IP header ?
364253642Sguido	 * Only a basic IP header (no options) should be with an ICMP error
3643145522Sdarrenr	 * header.  Also, if it's not an error type, then return.
364453642Sguido	 */
3645255332Scy	if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) {
3646255332Scy		ATOMIC_INCL(nside->ns_icmp_basic);
364753642Sguido		return NULL;
3648255332Scy	}
3649145522Sdarrenr
365053642Sguido	/*
3651145522Sdarrenr	 * Check packet size
365253642Sguido	 */
365353642Sguido	oip = (ip_t *)((char *)fin->fin_dp + 8);
3654145522Sdarrenr	minlen = IP_HL(oip) << 2;
3655145522Sdarrenr	if ((minlen < sizeof(ip_t)) ||
3656255332Scy	    (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) {
3657255332Scy		ATOMIC_INCL(nside->ns_icmp_size);
365853642Sguido		return NULL;
3659255332Scy	}
3660255332Scy
366164580Sdarrenr	/*
366264580Sdarrenr	 * Is the buffer big enough for all of it ?  It's the size of the IP
366364580Sdarrenr	 * header claimed in the encapsulated part which is of concern.  It
366464580Sdarrenr	 * may be too big to be in this buffer but not so big that it's
366564580Sdarrenr	 * outside the ICMP packet, leading to TCP deref's causing problems.
366664580Sdarrenr	 * This is possible because we don't know how big oip_hl is when we
3667255332Scy	 * do the pullup early in ipf_check() and thus can't gaurantee it is
366864580Sdarrenr	 * all here now.
366964580Sdarrenr	 */
3670255332Scy#ifdef  ipf_nat_KERNEL
367164580Sdarrenr	{
367264580Sdarrenr	mb_t *m;
367364580Sdarrenr
3674145522Sdarrenr	m = fin->fin_m;
3675145522Sdarrenr# if defined(MENTAT)
3676255332Scy	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
3677255332Scy	    (char *)m->b_wptr) {
3678255332Scy		ATOMIC_INCL(nside->ns_icmp_mbuf);
367964580Sdarrenr		return NULL;
3680255332Scy	}
368164580Sdarrenr# else
368264580Sdarrenr	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
3683255332Scy	    (char *)fin->fin_ip + M_LEN(m)) {
3684255332Scy		ATOMIC_INCL(nside->ns_icmp_mbuf);
368564580Sdarrenr		return NULL;
3686255332Scy	}
368764580Sdarrenr# endif
368864580Sdarrenr	}
368964580Sdarrenr#endif
369064580Sdarrenr
3691255332Scy	if (fin->fin_daddr != oip->ip_src.s_addr) {
3692255332Scy		ATOMIC_INCL(nside->ns_icmp_address);
3693145522Sdarrenr		return NULL;
3694255332Scy	}
3695145522Sdarrenr
3696145522Sdarrenr	p = oip->ip_p;
3697145522Sdarrenr	if (p == IPPROTO_TCP)
369853642Sguido		flags = IPN_TCP;
3699145522Sdarrenr	else if (p == IPPROTO_UDP)
370053642Sguido		flags = IPN_UDP;
3701145522Sdarrenr	else if (p == IPPROTO_ICMP) {
3702145522Sdarrenr		orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2));
3703145522Sdarrenr
3704145522Sdarrenr		/* see if this is related to an ICMP query */
3705255332Scy		if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) {
3706145522Sdarrenr			data[0] = fin->fin_data[0];
3707145522Sdarrenr			data[1] = fin->fin_data[1];
3708145522Sdarrenr			fin->fin_data[0] = 0;
3709145522Sdarrenr			fin->fin_data[1] = orgicmp->icmp_id;
3710145522Sdarrenr
3711145522Sdarrenr			flags = IPN_ICMPERR|IPN_ICMPQUERY;
3712145522Sdarrenr			/*
3713145522Sdarrenr			 * NOTE : dir refers to the direction of the original
3714145522Sdarrenr			 *        ip packet. By definition the icmp error
3715145522Sdarrenr			 *        message flows in the opposite direction.
3716145522Sdarrenr			 */
3717145522Sdarrenr			if (dir == NAT_INBOUND)
3718255332Scy				nat = ipf_nat_inlookup(fin, flags, p,
3719255332Scy						       oip->ip_dst,
3720255332Scy						       oip->ip_src);
3721145522Sdarrenr			else
3722255332Scy				nat = ipf_nat_outlookup(fin, flags, p,
3723255332Scy							oip->ip_dst,
3724255332Scy							oip->ip_src);
3725145522Sdarrenr			fin->fin_data[0] = data[0];
3726145522Sdarrenr			fin->fin_data[1] = data[1];
3727145522Sdarrenr			return nat;
3728145522Sdarrenr		}
3729145522Sdarrenr	}
3730255332Scy
373153642Sguido	if (flags & IPN_TCPUDP) {
373264580Sdarrenr		minlen += 8;		/* + 64bits of data to get ports */
3733255332Scy		/* TRACE (fin,minlen) */
3734255332Scy		if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) {
3735255332Scy			ATOMIC_INCL(nside->ns_icmp_short);
373664580Sdarrenr			return NULL;
3737255332Scy		}
373892685Sdarrenr
373992685Sdarrenr		data[0] = fin->fin_data[0];
374092685Sdarrenr		data[1] = fin->fin_data[1];
3741145522Sdarrenr		tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2));
374292685Sdarrenr		fin->fin_data[0] = ntohs(tcp->th_dport);
374392685Sdarrenr		fin->fin_data[1] = ntohs(tcp->th_sport);
374492685Sdarrenr
374592685Sdarrenr		if (dir == NAT_INBOUND) {
3746255332Scy			nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst,
3747255332Scy					       oip->ip_src);
374892685Sdarrenr		} else {
3749255332Scy			nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst,
3750145522Sdarrenr					    oip->ip_src);
375192685Sdarrenr		}
375292685Sdarrenr		fin->fin_data[0] = data[0];
375392685Sdarrenr		fin->fin_data[1] = data[1];
375492685Sdarrenr		return nat;
375553642Sguido	}
375660852Sdarrenr	if (dir == NAT_INBOUND)
3757255332Scy		nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
375860852Sdarrenr	else
3759255332Scy		nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
3760255332Scy
3761255332Scy	return nat;
376253642Sguido}
376353642Sguido
376453642Sguido
3765145522Sdarrenr/* ------------------------------------------------------------------------ */
3766255332Scy/* Function:    ipf_nat_icmperror                                           */
3767145522Sdarrenr/* Returns:     nat_t* - point to matching NAT structure                    */
3768145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
3769145522Sdarrenr/*              nflags(I) - NAT flags for this packet                       */
3770145522Sdarrenr/*              dir(I)    - direction of packet (in/out)                    */
3771145522Sdarrenr/*                                                                          */
3772145522Sdarrenr/* Fix up an ICMP packet which is an error message for an existing NAT      */
3773145522Sdarrenr/* session.  This will correct both packet header data and checksums.       */
3774145522Sdarrenr/*                                                                          */
3775145522Sdarrenr/* This should *ONLY* be used for incoming ICMP error packets to make sure  */
3776145522Sdarrenr/* a NAT'd ICMP packet gets correctly recognised.                           */
3777145522Sdarrenr/* ------------------------------------------------------------------------ */
3778255332Scynat_t *
3779255332Scyipf_nat_icmperror(fin, nflags, dir)
3780255332Scy	fr_info_t *fin;
3781255332Scy	u_int *nflags;
3782255332Scy	int dir;
378353642Sguido{
3784255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3785255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3786145522Sdarrenr	u_32_t sum1, sum2, sumd, sumd2;
3787255332Scy	struct in_addr a1, a2, a3, a4;
3788170268Sdarrenr	int flags, dlen, odst;
3789145522Sdarrenr	icmphdr_t *icmp;
3790145522Sdarrenr	u_short *csump;
379195418Sdarrenr	tcphdr_t *tcp;
379253642Sguido	nat_t *nat;
379353642Sguido	ip_t *oip;
3794145522Sdarrenr	void *dp;
379553642Sguido
3796255332Scy	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
3797255332Scy		NBUMPSIDED(fin->fin_out, ns_icmp_short);
379863523Sdarrenr		return NULL;
3799255332Scy	}
3800255332Scy
380167614Sdarrenr	/*
3802255332Scy	 * ipf_nat_icmperrorlookup() will return NULL for `defective' packets.
380367614Sdarrenr	 */
3804255332Scy	if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) {
3805255332Scy		NBUMPSIDED(fin->fin_out, ns_icmp_notfound);
380653642Sguido		return NULL;
3807255332Scy	}
380892685Sdarrenr
3809145522Sdarrenr	tcp = NULL;
3810145522Sdarrenr	csump = NULL;
381192685Sdarrenr	flags = 0;
3812130886Sdarrenr	sumd2 = 0;
381353642Sguido	*nflags = IPN_ICMPERR;
3814145522Sdarrenr	icmp = fin->fin_dp;
381553642Sguido	oip = (ip_t *)&icmp->icmp_ip;
3816145522Sdarrenr	dp = (((char *)oip) + (IP_HL(oip) << 2));
3817145522Sdarrenr	if (oip->ip_p == IPPROTO_TCP) {
3818145522Sdarrenr		tcp = (tcphdr_t *)dp;
3819145522Sdarrenr		csump = (u_short *)&tcp->th_sum;
382053642Sguido		flags = IPN_TCP;
3821145522Sdarrenr	} else if (oip->ip_p == IPPROTO_UDP) {
3822145522Sdarrenr		udphdr_t *udp;
3823145522Sdarrenr
3824145522Sdarrenr		udp = (udphdr_t *)dp;
3825145522Sdarrenr		tcp = (tcphdr_t *)dp;
3826145522Sdarrenr		csump = (u_short *)&udp->uh_sum;
382753642Sguido		flags = IPN_UDP;
3828145522Sdarrenr	} else if (oip->ip_p == IPPROTO_ICMP)
3829145522Sdarrenr		flags = IPN_ICMPQUERY;
3830145522Sdarrenr	dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip);
383195418Sdarrenr
383295418Sdarrenr	/*
383353642Sguido	 * Need to adjust ICMP header to include the real IP#'s and
383453642Sguido	 * port #'s.  Only apply a checksum change relative to the
3835255332Scy	 * IP address change as it will be modified again in ipf_nat_checkout
383653642Sguido	 * for both address and port.  Two checksum changes are
383753642Sguido	 * necessary for the two header address changes.  Be careful
383853642Sguido	 * to only modify the checksum once for the port # and twice
383953642Sguido	 * for the IP#.
384053642Sguido	 */
384160852Sdarrenr
384267614Sdarrenr	/*
384367614Sdarrenr	 * Step 1
384467614Sdarrenr	 * Fix the IP addresses in the offending IP packet. You also need
3845170268Sdarrenr	 * to adjust the IP header checksum of that offending IP packet.
384667614Sdarrenr	 *
3847145522Sdarrenr	 * Normally, you would expect that the ICMP checksum of the
3848130886Sdarrenr	 * ICMP error message needs to be adjusted as well for the
3849130886Sdarrenr	 * IP address change in oip.
3850145522Sdarrenr	 * However, this is a NOP, because the ICMP checksum is
3851130886Sdarrenr	 * calculated over the complete ICMP packet, which includes the
3852145522Sdarrenr	 * changed oip IP addresses and oip->ip_sum. However, these
3853130886Sdarrenr	 * two changes cancel each other out (if the delta for
3854145522Sdarrenr	 * the IP address is x, then the delta for ip_sum is minus x),
3855130886Sdarrenr	 * so no change in the icmp_cksum is necessary.
3856130886Sdarrenr	 *
3857170268Sdarrenr	 * Inbound ICMP
3858170268Sdarrenr	 * ------------
3859170268Sdarrenr	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
3860170268Sdarrenr	 * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b)
3861255332Scy	 * - OIP_SRC(c)=nat_newsrcip,          OIP_DST(b)=nat_newdstip
3862255332Scy	 *=> OIP_SRC(c)=nat_oldsrcip,          OIP_DST(b)=nat_olddstip
3863170268Sdarrenr	 *
3864170268Sdarrenr	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
3865170268Sdarrenr	 * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a)
3866255332Scy	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
3867255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3868170268Sdarrenr	 *
3869255332Scy	 * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
3870255332Scy	 * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d)
3871255332Scy	 * - OIP_SRC(c)=nat_newsrcip,          OIP_DST(d)=nat_newdstip
3872255332Scy	 *=> OIP_SRC(c)=nat_oldsrcip,          OIP_DST(d)=nat_olddstip
3873255332Scy	 *
3874255332Scy	 * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
3875255332Scy	 * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
3876255332Scy	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
3877255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3878255332Scy	 *
3879170268Sdarrenr	 * Outbound ICMP
3880170268Sdarrenr	 * -------------
3881170268Sdarrenr	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
3882170268Sdarrenr	 * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
3883255332Scy	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
3884255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3885170268Sdarrenr	 *
3886170268Sdarrenr	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
3887170268Sdarrenr	 * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c)
3888255332Scy	 * - OIP_SRC(a)=nat_newsrcip,          OIP_DST(c)=nat_newdstip
3889255332Scy	 *=> OIP_SRC(a)=nat_oldsrcip,          OIP_DST(c)=nat_olddstip
3890170268Sdarrenr	 *
3891255332Scy	 * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
3892255332Scy	 * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d)
3893255332Scy	 * - OIP_SRC(c)=nat_olddstip,          OIP_DST(d)=nat_oldsrcip
3894255332Scy	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
3895255332Scy	 *
3896255332Scy	 * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
3897255332Scy	 * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a)
3898255332Scy	 * - OIP_SRC(b)=nat_newsrcip,          OIP_DST(a)=nat_newdstip
3899255332Scy	 *=> OIP_SRC(a)=nat_oldsrcip,          OIP_DST(c)=nat_olddstip
3900130886Sdarrenr	 */
3901255332Scy
3902255332Scy	if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) ||
3903255332Scy	    ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) {
3904255332Scy		a1.s_addr = ntohl(nat->nat_osrcaddr);
3905255332Scy		a4.s_addr = ntohl(oip->ip_src.s_addr);
3906255332Scy		a3.s_addr = ntohl(nat->nat_odstaddr);
3907255332Scy		a2.s_addr = ntohl(oip->ip_dst.s_addr);
3908170268Sdarrenr		oip->ip_src.s_addr = htonl(a1.s_addr);
3909255332Scy		oip->ip_dst.s_addr = htonl(a3.s_addr);
3910255332Scy		odst = 1;
3911170268Sdarrenr	} else {
3912255332Scy		a1.s_addr = ntohl(nat->nat_ndstaddr);
3913170268Sdarrenr		a2.s_addr = ntohl(oip->ip_dst.s_addr);
3914255332Scy		a3.s_addr = ntohl(nat->nat_nsrcaddr);
3915255332Scy		a4.s_addr = ntohl(oip->ip_src.s_addr);
3916255332Scy		oip->ip_dst.s_addr = htonl(a3.s_addr);
3917255332Scy		oip->ip_src.s_addr = htonl(a1.s_addr);
3918255332Scy		odst = 0;
3919170268Sdarrenr	}
3920255332Scy	sum1 = 0;
3921255332Scy	sum2 = 0;
3922255332Scy	sumd = 0;
3923255332Scy	CALC_SUMD(a2.s_addr, a3.s_addr, sum1);
3924255332Scy	CALC_SUMD(a4.s_addr, a1.s_addr, sum2);
3925255332Scy	sumd = sum2 + sum1;
3926255332Scy	if (sumd != 0)
3927255332Scy		ipf_fix_datacksum(&oip->ip_sum, sumd);
3928130886Sdarrenr
3929170268Sdarrenr	sumd2 = sumd;
3930170268Sdarrenr	sum1 = 0;
3931170268Sdarrenr	sum2 = 0;
3932170268Sdarrenr
3933130886Sdarrenr	/*
3934170268Sdarrenr	 * Fix UDP pseudo header checksum to compensate for the
3935170268Sdarrenr	 * IP address change.
3936130886Sdarrenr	 */
3937130886Sdarrenr	if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) {
3938255332Scy		u_32_t sum3, sum4, sumt;
3939255332Scy
394064580Sdarrenr		/*
394167614Sdarrenr		 * Step 2 :
394267614Sdarrenr		 * For offending TCP/UDP IP packets, translate the ports as
394367614Sdarrenr		 * well, based on the NAT specification. Of course such
3944170268Sdarrenr		 * a change may be reflected in the ICMP checksum as well.
394567614Sdarrenr		 *
394667614Sdarrenr		 * Since the port fields are part of the TCP/UDP checksum
394767614Sdarrenr		 * of the offending IP packet, you need to adjust that checksum
3948255332Scy		 * as well... except that the change in the port numbers should
3949170268Sdarrenr		 * be offset by the checksum change.  However, the TCP/UDP
3950170268Sdarrenr		 * checksum will also need to change if there has been an
3951170268Sdarrenr		 * IP address change.
395267614Sdarrenr		 */
3953170268Sdarrenr		if (odst == 1) {
3954255332Scy			sum1 = ntohs(nat->nat_osport);
3955255332Scy			sum4 = ntohs(tcp->th_sport);
3956255332Scy			sum3 = ntohs(nat->nat_odport);
3957255332Scy			sum2 = ntohs(tcp->th_dport);
3958145522Sdarrenr
3959170268Sdarrenr			tcp->th_sport = htons(sum1);
3960255332Scy			tcp->th_dport = htons(sum3);
3961170268Sdarrenr		} else {
3962255332Scy			sum1 = ntohs(nat->nat_ndport);
3963145522Sdarrenr			sum2 = ntohs(tcp->th_dport);
3964255332Scy			sum3 = ntohs(nat->nat_nsport);
3965255332Scy			sum4 = ntohs(tcp->th_sport);
3966170268Sdarrenr
3967255332Scy			tcp->th_dport = htons(sum3);
3968255332Scy			tcp->th_sport = htons(sum1);
3969145522Sdarrenr		}
3970255332Scy		CALC_SUMD(sum4, sum1, sumt);
3971255332Scy		sumd += sumt;
3972255332Scy		CALC_SUMD(sum2, sum3, sumt);
3973255332Scy		sumd += sumt;
397467614Sdarrenr
3975170268Sdarrenr		if (sumd != 0 || sumd2 != 0) {
3976145522Sdarrenr			/*
3977170268Sdarrenr			 * At this point, sumd is the delta to apply to the
3978170268Sdarrenr			 * TCP/UDP header, given the changes in both the IP
3979170268Sdarrenr			 * address and the ports and sumd2 is the delta to
3980170268Sdarrenr			 * apply to the ICMP header, given the IP address
3981170268Sdarrenr			 * change delta that may need to be applied to the
3982170268Sdarrenr			 * TCP/UDP checksum instead.
3983145522Sdarrenr			 *
3984170268Sdarrenr			 * If we will both the IP and TCP/UDP checksums
3985170268Sdarrenr			 * then the ICMP checksum changes by the address
3986170268Sdarrenr			 * delta applied to the TCP/UDP checksum.  If we
3987170268Sdarrenr			 * do not change the TCP/UDP checksum them we
3988170268Sdarrenr			 * apply the delta in ports to the ICMP checksum.
3989145522Sdarrenr			 */
3990161356Sguido			if (oip->ip_p == IPPROTO_UDP) {
3991161356Sguido				if ((dlen >= 8) && (*csump != 0)) {
3992255332Scy					ipf_fix_datacksum(csump, sumd);
3993161356Sguido				} else {
3994255332Scy					CALC_SUMD(sum1, sum4, sumd2);
3995255332Scy					CALC_SUMD(sum3, sum2, sumt);
3996255332Scy					sumd2 += sumt;
3997161356Sguido				}
3998170268Sdarrenr			} else if (oip->ip_p == IPPROTO_TCP) {
3999145522Sdarrenr				if (dlen >= 18) {
4000255332Scy					ipf_fix_datacksum(csump, sumd);
4001145522Sdarrenr				} else {
4002255332Scy					CALC_SUMD(sum1, sum4, sumd2);
4003255332Scy					CALC_SUMD(sum3, sum2, sumt);
4004255332Scy					sumd2 += sumt;
400567614Sdarrenr				}
400653642Sguido			}
4007170268Sdarrenr			if (sumd2 != 0) {
4008170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
4009170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
4010170268Sdarrenr				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
4011255332Scy				ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0);
4012130886Sdarrenr			}
401353642Sguido		}
4014145522Sdarrenr	} else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) {
4015145522Sdarrenr		icmphdr_t *orgicmp;
4016145522Sdarrenr
4017145522Sdarrenr		/*
4018145522Sdarrenr		 * XXX - what if this is bogus hl and we go off the end ?
4019255332Scy		 * In this case, ipf_nat_icmperrorlookup() will have
4020255332Scy		 * returned NULL.
4021145522Sdarrenr		 */
4022145522Sdarrenr		orgicmp = (icmphdr_t *)dp;
4023145522Sdarrenr
4024170268Sdarrenr		if (odst == 1) {
4025255332Scy			if (orgicmp->icmp_id != nat->nat_osport) {
4026145522Sdarrenr
4027145522Sdarrenr				/*
4028145522Sdarrenr				 * Fix ICMP checksum (of the offening ICMP
4029145522Sdarrenr				 * query packet) to compensate the change
4030145522Sdarrenr				 * in the ICMP id of the offending ICMP
4031145522Sdarrenr				 * packet.
4032145522Sdarrenr				 *
4033145522Sdarrenr				 * Since you modify orgicmp->icmp_id with
4034145522Sdarrenr				 * a delta (say x) and you compensate that
4035145522Sdarrenr				 * in origicmp->icmp_cksum with a delta
4036145522Sdarrenr				 * minus x, you don't have to adjust the
4037145522Sdarrenr				 * overall icmp->icmp_cksum
4038145522Sdarrenr				 */
4039145522Sdarrenr				sum1 = ntohs(orgicmp->icmp_id);
4040255332Scy				sum2 = ntohs(nat->nat_oicmpid);
4041145522Sdarrenr				CALC_SUMD(sum1, sum2, sumd);
4042255332Scy				orgicmp->icmp_id = nat->nat_oicmpid;
4043255332Scy				ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd);
4044145522Sdarrenr			}
4045145522Sdarrenr		} /* nat_dir == NAT_INBOUND is impossible for icmp queries */
404653642Sguido	}
404753642Sguido	return nat;
404853642Sguido}
404953642Sguido
405053642Sguido
405153642Sguido/*
4052255332Scy *       MAP-IN    MAP-OUT   RDR-IN   RDR-OUT
4053255332Scy * osrc    X       == src    == src      X
4054255332Scy * odst    X       == dst    == dst      X
4055255332Scy * nsrc  == dst      X         X      == dst
4056255332Scy * ndst  == src      X         X      == src
4057255332Scy * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND
4058255332Scy */
4059255332Scy/*
4060145522Sdarrenr * NB: these lookups don't lock access to the list, it assumed that it has
4061145522Sdarrenr * already been done!
406253642Sguido */
4063145522Sdarrenr/* ------------------------------------------------------------------------ */
4064255332Scy/* Function:    ipf_nat_inlookup                                            */
4065145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
4066145522Sdarrenr/*                       else pointer to matching NAT entry                 */
4067145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
4068145522Sdarrenr/*              flags(I)  - NAT flags for this packet                       */
4069145522Sdarrenr/*              p(I)      - protocol for this packet                        */
4070145522Sdarrenr/*              src(I)    - source IP address                               */
4071145522Sdarrenr/*              mapdst(I) - destination IP address                          */
4072145522Sdarrenr/*                                                                          */
4073145522Sdarrenr/* Lookup a nat entry based on the mapped destination ip address/port and   */
4074145522Sdarrenr/* real source address/port.  We use this lookup when receiving a packet,   */
4075145522Sdarrenr/* we're looking for a table entry, based on the destination address.       */
4076145522Sdarrenr/*                                                                          */
4077145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
4078145522Sdarrenr/*                                                                          */
4079255332Scy/* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
4080145522Sdarrenr/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
4081145522Sdarrenr/*                                                                          */
4082145522Sdarrenr/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
4083145522Sdarrenr/*            the packet is of said protocol                                */
4084145522Sdarrenr/* ------------------------------------------------------------------------ */
4085255332Scynat_t *
4086255332Scyipf_nat_inlookup(fin, flags, p, src, mapdst)
4087255332Scy	fr_info_t *fin;
4088255332Scy	u_int flags, p;
4089255332Scy	struct in_addr src , mapdst;
409053642Sguido{
4091255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
4092255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
4093145522Sdarrenr	u_short sport, dport;
4094145522Sdarrenr	grehdr_t *gre;
409592685Sdarrenr	ipnat_t *ipn;
4096145522Sdarrenr	u_int sflags;
4097145522Sdarrenr	nat_t *nat;
4098145522Sdarrenr	int nflags;
4099145522Sdarrenr	u_32_t dst;
410092685Sdarrenr	void *ifp;
4101255332Scy	u_int hv, rhv;
410253642Sguido
4103161356Sguido	ifp = fin->fin_ifp;
4104145522Sdarrenr	gre = NULL;
410567614Sdarrenr	dst = mapdst.s_addr;
4106145522Sdarrenr	sflags = flags & NAT_TCPUDPICMP;
4107145522Sdarrenr
4108145522Sdarrenr	switch (p)
4109145522Sdarrenr	{
4110145522Sdarrenr	case IPPROTO_TCP :
4111145522Sdarrenr	case IPPROTO_UDP :
411292685Sdarrenr		sport = htons(fin->fin_data[0]);
411392685Sdarrenr		dport = htons(fin->fin_data[1]);
4114145522Sdarrenr		break;
4115145522Sdarrenr	case IPPROTO_ICMP :
4116323231Scy		sport = 0;
4117323231Scy		dport = fin->fin_data[1];
4118145522Sdarrenr		break;
4119145522Sdarrenr	default :
4120255332Scy		sport = 0;
4121255332Scy		dport = 0;
4122145522Sdarrenr		break;
412392685Sdarrenr	}
412453642Sguido
4125145522Sdarrenr
4126145522Sdarrenr	if ((flags & SI_WILDP) != 0)
4127145522Sdarrenr		goto find_in_wild_ports;
4128145522Sdarrenr
4129255332Scy	rhv = NAT_HASH_FN(dst, dport, 0xffffffff);
4130255332Scy	rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff);
4131255332Scy	hv = rhv % softn->ipf_nat_table_sz;
4132255332Scy	nat = softn->ipf_nat_table[1][hv];
4133255332Scy	/* TRACE dst, dport, src, sport, hv, nat */
4134255332Scy
413553642Sguido	for (; nat; nat = nat->nat_hnext[1]) {
4136161356Sguido		if (nat->nat_ifps[0] != NULL) {
4137161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
4138161356Sguido				continue;
4139255332Scy		}
4140161356Sguido
4141255332Scy		if (nat->nat_pr[0] != p)
4142255332Scy			continue;
4143145522Sdarrenr
4144255332Scy		switch (nat->nat_dir)
4145255332Scy		{
4146255332Scy		case NAT_INBOUND :
4147255332Scy		case NAT_DIVERTIN :
4148255332Scy			if (nat->nat_v[0] != 4)
4149255332Scy				continue;
4150255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4151255332Scy			    nat->nat_odstaddr != dst)
4152255332Scy				continue;
4153255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4154255332Scy				if (nat->nat_osport != sport)
4155145522Sdarrenr					continue;
4156255332Scy				if (nat->nat_odport != dport)
4157255332Scy					continue;
4158255332Scy
4159255332Scy			} else if (p == IPPROTO_ICMP) {
4160255332Scy				if (nat->nat_osport != dport) {
4161255332Scy					continue;
4162145522Sdarrenr				}
4163255332Scy			}
4164255332Scy			break;
4165255332Scy		case NAT_DIVERTOUT :
4166255332Scy			if (nat->nat_dlocal)
4167255332Scy				continue;
4168255332Scy		case NAT_OUTBOUND :
4169255332Scy			if (nat->nat_v[1] != 4)
4170255332Scy				continue;
4171255332Scy			if (nat->nat_dlocal)
4172255332Scy				continue;
4173255332Scy			if (nat->nat_dlocal)
4174255332Scy				continue;
4175255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4176255332Scy			    nat->nat_nsrcaddr != dst)
4177255332Scy				continue;
4178255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4179255332Scy				if (nat->nat_ndport != sport)
418092685Sdarrenr					continue;
4181255332Scy				if (nat->nat_nsport != dport)
418292685Sdarrenr					continue;
4183255332Scy
4184255332Scy			} else if (p == IPPROTO_ICMP) {
4185255332Scy				if (nat->nat_osport != dport) {
4186255332Scy					continue;
4187255332Scy				}
418892685Sdarrenr			}
4189255332Scy			break;
4190255332Scy		}
419192685Sdarrenr
4192255332Scy
4193255332Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
419492685Sdarrenr			ipn = nat->nat_ptr;
419592685Sdarrenr			if ((ipn != NULL) && (nat->nat_aps != NULL))
4196255332Scy				if (ipf_proxy_match(fin, nat) != 0)
419792685Sdarrenr					continue;
419892685Sdarrenr		}
4199255332Scy		if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
4200255332Scy			nat->nat_ifps[0] = ifp;
4201255332Scy			nat->nat_mtu[0] = GETIFMTU_4(ifp);
4202255332Scy		}
4203255332Scy		return nat;
420453642Sguido	}
4205145522Sdarrenr
4206145522Sdarrenr	/*
4207145522Sdarrenr	 * So if we didn't find it but there are wildcard members in the hash
4208145522Sdarrenr	 * table, go back and look for them.  We do this search and update here
4209145522Sdarrenr	 * because it is modifying the NAT table and we want to do this only
4210145522Sdarrenr	 * for the first packet that matches.  The exception, of course, is
4211145522Sdarrenr	 * for "dummy" (FI_IGNORE) lookups.
4212145522Sdarrenr	 */
4213145522Sdarrenrfind_in_wild_ports:
4214255332Scy	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
4215255332Scy		NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0);
421667614Sdarrenr		return NULL;
4217255332Scy	}
4218255332Scy	if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
4219255332Scy		NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0);
4220145522Sdarrenr		return NULL;
4221255332Scy	}
4222145522Sdarrenr
4223255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
4224145522Sdarrenr
422580482Sdarrenr	hv = NAT_HASH_FN(dst, 0, 0xffffffff);
4226255332Scy	hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz);
4227255332Scy	WRITE_ENTER(&softc->ipf_nat);
4228145522Sdarrenr
4229255332Scy	nat = softn->ipf_nat_table[1][hv];
4230255332Scy	/* TRACE dst, src, hv, nat */
423167614Sdarrenr	for (; nat; nat = nat->nat_hnext[1]) {
4232161356Sguido		if (nat->nat_ifps[0] != NULL) {
4233161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
4234161356Sguido				continue;
4235255332Scy		}
4236145522Sdarrenr
4237255332Scy		if (nat->nat_pr[0] != fin->fin_p)
423867614Sdarrenr			continue;
4239145522Sdarrenr
4240255332Scy		switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))
4241255332Scy		{
4242255332Scy		case NAT_INBOUND :
4243255332Scy			if (nat->nat_v[0] != 4)
4244255332Scy				continue;
4245255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4246255332Scy			    nat->nat_odstaddr != dst)
4247255332Scy				continue;
4248255332Scy			break;
4249255332Scy		case NAT_OUTBOUND :
4250255332Scy			if (nat->nat_v[1] != 4)
4251255332Scy				continue;
4252255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4253255332Scy			    nat->nat_nsrcaddr != dst)
4254255332Scy				continue;
4255255332Scy			break;
4256255332Scy		}
4257255332Scy
4258145522Sdarrenr		nflags = nat->nat_flags;
4259145522Sdarrenr		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
4260145522Sdarrenr			continue;
4261145522Sdarrenr
4262255332Scy		if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags,
4263255332Scy				   NAT_INBOUND) == 1) {
4264145522Sdarrenr			if ((fin->fin_flx & FI_IGNORE) != 0)
4265145522Sdarrenr				break;
4266145522Sdarrenr			if ((nflags & SI_CLONE) != 0) {
4267255332Scy				nat = ipf_nat_clone(fin, nat);
4268145522Sdarrenr				if (nat == NULL)
4269145522Sdarrenr					break;
4270145522Sdarrenr			} else {
4271255332Scy				MUTEX_ENTER(&softn->ipf_nat_new);
4272255332Scy				softn->ipf_nat_stats.ns_wilds--;
4273255332Scy				MUTEX_EXIT(&softn->ipf_nat_new);
4274145522Sdarrenr			}
4275255332Scy
4276255332Scy			if (nat->nat_dir == NAT_INBOUND) {
4277255332Scy				if (nat->nat_osport == 0) {
4278255332Scy					nat->nat_osport = sport;
4279255332Scy					nat->nat_nsport = sport;
4280255332Scy				}
4281255332Scy				if (nat->nat_odport == 0) {
4282255332Scy					nat->nat_odport = dport;
4283255332Scy					nat->nat_ndport = dport;
4284255332Scy				}
4285255332Scy			} else if (nat->nat_dir == NAT_OUTBOUND) {
4286255332Scy				if (nat->nat_osport == 0) {
4287255332Scy					nat->nat_osport = dport;
4288255332Scy					nat->nat_nsport = dport;
4289255332Scy				}
4290255332Scy				if (nat->nat_odport == 0) {
4291255332Scy					nat->nat_odport = sport;
4292255332Scy					nat->nat_ndport = sport;
4293255332Scy				}
4294255332Scy			}
4295255332Scy			if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
4296255332Scy				nat->nat_ifps[0] = ifp;
4297255332Scy				nat->nat_mtu[0] = GETIFMTU_4(ifp);
4298255332Scy			}
4299145522Sdarrenr			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
4300255332Scy			ipf_nat_tabmove(softn, nat);
430167614Sdarrenr			break;
430267614Sdarrenr		}
430367614Sdarrenr	}
4304145522Sdarrenr
4305255332Scy	MUTEX_DOWNGRADE(&softc->ipf_nat);
4306145522Sdarrenr
4307255332Scy	if (nat == NULL) {
4308255332Scy		NBUMPSIDE(0, ns_lookup_miss);
4309255332Scy	}
431067614Sdarrenr	return nat;
431153642Sguido}
431253642Sguido
431353642Sguido
4314145522Sdarrenr/* ------------------------------------------------------------------------ */
4315255332Scy/* Function:    ipf_nat_tabmove                                             */
4316145522Sdarrenr/* Returns:     Nil                                                         */
4317255332Scy/* Parameters:  softn(I) - pointer to NAT context structure                 */
4318255332Scy/*              nat(I)   - pointer to NAT structure                         */
4319145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
4320145522Sdarrenr/*                                                                          */
4321145522Sdarrenr/* This function is only called for TCP/UDP NAT table entries where the     */
4322145522Sdarrenr/* original was placed in the table without hashing on the ports and we now */
4323145522Sdarrenr/* want to include hashing on port numbers.                                 */
4324145522Sdarrenr/* ------------------------------------------------------------------------ */
4325255332Scystatic void
4326255332Scyipf_nat_tabmove(softn, nat)
4327255332Scy	ipf_nat_softc_t *softn;
4328255332Scy	nat_t *nat;
432967614Sdarrenr{
4330255332Scy	u_int hv0, hv1, rhv0, rhv1;
4331255332Scy	natstat_t *nsp;
433267614Sdarrenr	nat_t **natp;
433367614Sdarrenr
4334145522Sdarrenr	if (nat->nat_flags & SI_CLONE)
4335145522Sdarrenr		return;
433672006Sdarrenr
4337255332Scy	nsp = &softn->ipf_nat_stats;
433867614Sdarrenr	/*
433967614Sdarrenr	 * Remove the NAT entry from the old location
434067614Sdarrenr	 */
434167614Sdarrenr	if (nat->nat_hnext[0])
434267614Sdarrenr		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
434367614Sdarrenr	*nat->nat_phnext[0] = nat->nat_hnext[0];
4344255332Scy	nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] %
4345255332Scy				     softn->ipf_nat_table_sz]--;
434667614Sdarrenr
434767614Sdarrenr	if (nat->nat_hnext[1])
434867853Sdarrenr		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
434967614Sdarrenr	*nat->nat_phnext[1] = nat->nat_hnext[1];
4350255332Scy	nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] %
4351255332Scy				     softn->ipf_nat_table_sz]--;
435267614Sdarrenr
435367853Sdarrenr	/*
435467853Sdarrenr	 * Add into the NAT table in the new position
435567853Sdarrenr	 */
4356255332Scy	rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff);
4357255332Scy	rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport,
4358255332Scy			   0xffffffff);
4359255332Scy	rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff);
4360255332Scy	rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport,
4361255332Scy			   0xffffffff);
4362255332Scy
4363255332Scy	hv0 = rhv0 % softn->ipf_nat_table_sz;
4364255332Scy	hv1 = rhv1 % softn->ipf_nat_table_sz;
4365255332Scy
4366255332Scy	if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) {
4367255332Scy		u_int swap;
4368255332Scy
4369255332Scy		swap = hv0;
4370255332Scy		hv0 = hv1;
4371255332Scy		hv1 = swap;
4372255332Scy	}
4373255332Scy
4374255332Scy	/* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */
4375255332Scy	/* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */
4376255332Scy
4377255332Scy	nat->nat_hv[0] = rhv0;
4378255332Scy	natp = &softn->ipf_nat_table[0][hv0];
437967614Sdarrenr	if (*natp)
438067614Sdarrenr		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
438167614Sdarrenr	nat->nat_phnext[0] = natp;
438267614Sdarrenr	nat->nat_hnext[0] = *natp;
438367614Sdarrenr	*natp = nat;
4384255332Scy	nsp->ns_side[0].ns_bucketlen[hv0]++;
438567614Sdarrenr
4386255332Scy	nat->nat_hv[1] = rhv1;
4387255332Scy	natp = &softn->ipf_nat_table[1][hv1];
438867614Sdarrenr	if (*natp)
438967614Sdarrenr		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
439067614Sdarrenr	nat->nat_phnext[1] = natp;
439167614Sdarrenr	nat->nat_hnext[1] = *natp;
439267614Sdarrenr	*natp = nat;
4393255332Scy	nsp->ns_side[1].ns_bucketlen[hv1]++;
439467614Sdarrenr}
439567614Sdarrenr
439667614Sdarrenr
4397145522Sdarrenr/* ------------------------------------------------------------------------ */
4398255332Scy/* Function:    ipf_nat_outlookup                                           */
4399145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
4400145522Sdarrenr/*                       else pointer to matching NAT entry                 */
4401145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4402145522Sdarrenr/*              flags(I) - NAT flags for this packet                        */
4403145522Sdarrenr/*              p(I)     - protocol for this packet                         */
4404145522Sdarrenr/*              src(I)   - source IP address                                */
4405145522Sdarrenr/*              dst(I)   - destination IP address                           */
4406255332Scy/*              rw(I)    - 1 == write lock on  held, 0 == read lock.        */
4407145522Sdarrenr/*                                                                          */
4408145522Sdarrenr/* Lookup a nat entry based on the source 'real' ip address/port and        */
4409145522Sdarrenr/* destination address/port.  We use this lookup when sending a packet out, */
4410145522Sdarrenr/* we're looking for a table entry, based on the source address.            */
4411145522Sdarrenr/*                                                                          */
4412145522Sdarrenr/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
4413145522Sdarrenr/*                                                                          */
4414255332Scy/* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
4415145522Sdarrenr/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
4416145522Sdarrenr/*                                                                          */
4417145522Sdarrenr/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
4418145522Sdarrenr/*            the packet is of said protocol                                */
4419145522Sdarrenr/* ------------------------------------------------------------------------ */
4420255332Scynat_t *
4421255332Scyipf_nat_outlookup(fin, flags, p, src, dst)
4422255332Scy	fr_info_t *fin;
4423255332Scy	u_int flags, p;
4424255332Scy	struct in_addr src , dst;
442553642Sguido{
4426255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
4427255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
4428145522Sdarrenr	u_short sport, dport;
4429145522Sdarrenr	u_int sflags;
443092685Sdarrenr	ipnat_t *ipn;
4431145522Sdarrenr	nat_t *nat;
443292685Sdarrenr	void *ifp;
443353642Sguido	u_int hv;
443453642Sguido
443592685Sdarrenr	ifp = fin->fin_ifp;
4436145522Sdarrenr	sflags = flags & IPN_TCPUDPICMP;
4437145522Sdarrenr
4438145522Sdarrenr	switch (p)
4439145522Sdarrenr	{
4440145522Sdarrenr	case IPPROTO_TCP :
4441145522Sdarrenr	case IPPROTO_UDP :
4442145522Sdarrenr		sport = htons(fin->fin_data[0]);
4443145522Sdarrenr		dport = htons(fin->fin_data[1]);
4444145522Sdarrenr		break;
4445145522Sdarrenr	case IPPROTO_ICMP :
4446323231Scy		sport = 0;
4447323231Scy		dport = fin->fin_data[1];
4448145522Sdarrenr		break;
4449145522Sdarrenr	default :
4450323231Scy		sport = 0;
4451323231Scy		dport = 0;
4452145522Sdarrenr		break;
445392685Sdarrenr	}
445453642Sguido
4455145522Sdarrenr	if ((flags & SI_WILDP) != 0)
4456145522Sdarrenr		goto find_out_wild_ports;
4457145522Sdarrenr
4458255332Scy	hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff);
4459255332Scy	hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz);
4460255332Scy	nat = softn->ipf_nat_table[0][hv];
4461255332Scy
4462255332Scy	/* TRACE src, sport, dst, dport, hv, nat */
4463255332Scy
446453642Sguido	for (; nat; nat = nat->nat_hnext[0]) {
4465161356Sguido		if (nat->nat_ifps[1] != NULL) {
4466161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
4467161356Sguido				continue;
4468255332Scy		}
4469161356Sguido
4470255332Scy		if (nat->nat_pr[1] != p)
4471255332Scy			continue;
447253642Sguido
4473255332Scy		switch (nat->nat_dir)
4474255332Scy		{
4475255332Scy		case NAT_INBOUND :
4476255332Scy		case NAT_DIVERTIN :
4477255332Scy			if (nat->nat_v[1] != 4)
4478255332Scy				continue;
4479255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4480255332Scy			    nat->nat_nsrcaddr != dst.s_addr)
4481255332Scy				continue;
4482255332Scy
4483255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4484255332Scy				if (nat->nat_ndport != sport)
4485145522Sdarrenr					continue;
4486255332Scy				if (nat->nat_nsport != dport)
448792685Sdarrenr					continue;
4488255332Scy
4489255332Scy			} else if (p == IPPROTO_ICMP) {
4490255332Scy				if (nat->nat_osport != dport) {
449192685Sdarrenr					continue;
4492255332Scy				}
449392685Sdarrenr			}
4494255332Scy			break;
4495255332Scy		case NAT_OUTBOUND :
4496255332Scy		case NAT_DIVERTOUT :
4497255332Scy			if (nat->nat_v[0] != 4)
4498255332Scy				continue;
4499255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4500255332Scy			    nat->nat_odstaddr != dst.s_addr)
4501255332Scy				continue;
450292685Sdarrenr
4503255332Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
4504255332Scy				if (nat->nat_odport != dport)
450592685Sdarrenr					continue;
4506255332Scy				if (nat->nat_osport != sport)
4507255332Scy					continue;
4508255332Scy
4509255332Scy			} else if (p == IPPROTO_ICMP) {
4510255332Scy				if (nat->nat_osport != dport) {
4511255332Scy					continue;
4512255332Scy				}
4513255332Scy			}
4514255332Scy			break;
451592685Sdarrenr		}
4516255332Scy
4517255332Scy		ipn = nat->nat_ptr;
4518255332Scy		if ((ipn != NULL) && (nat->nat_aps != NULL))
4519255332Scy			if (ipf_proxy_match(fin, nat) != 0)
4520255332Scy				continue;
4521255332Scy
4522255332Scy		if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
4523255332Scy			nat->nat_ifps[1] = ifp;
4524255332Scy			nat->nat_mtu[1] = GETIFMTU_4(ifp);
4525255332Scy		}
4526255332Scy		return nat;
452753642Sguido	}
4528145522Sdarrenr
4529145522Sdarrenr	/*
4530145522Sdarrenr	 * So if we didn't find it but there are wildcard members in the hash
4531145522Sdarrenr	 * table, go back and look for them.  We do this search and update here
4532145522Sdarrenr	 * because it is modifying the NAT table and we want to do this only
4533145522Sdarrenr	 * for the first packet that matches.  The exception, of course, is
4534145522Sdarrenr	 * for "dummy" (FI_IGNORE) lookups.
4535145522Sdarrenr	 */
4536145522Sdarrenrfind_out_wild_ports:
4537255332Scy	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
4538255332Scy		NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1);
453967614Sdarrenr		return NULL;
4540255332Scy	}
4541255332Scy	if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
4542255332Scy		NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1);
4543145522Sdarrenr		return NULL;
4544255332Scy	}
454592685Sdarrenr
4546255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
4547145522Sdarrenr
4548255332Scy	hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff);
4549255332Scy	hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz);
4550145522Sdarrenr
4551255332Scy	WRITE_ENTER(&softc->ipf_nat);
4552145522Sdarrenr
4553255332Scy	nat = softn->ipf_nat_table[0][hv];
455467614Sdarrenr	for (; nat; nat = nat->nat_hnext[0]) {
4555161356Sguido		if (nat->nat_ifps[1] != NULL) {
4556161356Sguido			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
4557161356Sguido				continue;
4558255332Scy		}
4559145522Sdarrenr
4560255332Scy		if (nat->nat_pr[1] != fin->fin_p)
456167614Sdarrenr			continue;
4562145522Sdarrenr
4563255332Scy		switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))
4564255332Scy		{
4565255332Scy		case NAT_INBOUND :
4566255332Scy			if (nat->nat_v[1] != 4)
4567255332Scy				continue;
4568255332Scy			if (nat->nat_ndstaddr != src.s_addr ||
4569255332Scy			    nat->nat_nsrcaddr != dst.s_addr)
4570255332Scy				continue;
4571255332Scy			break;
4572255332Scy		case NAT_OUTBOUND :
4573255332Scy			if (nat->nat_v[0] != 4)
4574255332Scy				continue;
4575255332Scy			if (nat->nat_osrcaddr != src.s_addr ||
4576255332Scy			    nat->nat_odstaddr != dst.s_addr)
4577255332Scy				continue;
4578255332Scy			break;
4579255332Scy		}
4580255332Scy
4581255332Scy		if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP)))
4582145522Sdarrenr			continue;
4583145522Sdarrenr
4584255332Scy		if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags,
4585255332Scy				   NAT_OUTBOUND) == 1) {
4586145522Sdarrenr			if ((fin->fin_flx & FI_IGNORE) != 0)
4587145522Sdarrenr				break;
4588255332Scy			if ((nat->nat_flags & SI_CLONE) != 0) {
4589255332Scy				nat = ipf_nat_clone(fin, nat);
4590145522Sdarrenr				if (nat == NULL)
4591145522Sdarrenr					break;
4592145522Sdarrenr			} else {
4593255332Scy				MUTEX_ENTER(&softn->ipf_nat_new);
4594255332Scy				softn->ipf_nat_stats.ns_wilds--;
4595255332Scy				MUTEX_EXIT(&softn->ipf_nat_new);
4596145522Sdarrenr			}
4597255332Scy
4598255332Scy			if (nat->nat_dir == NAT_OUTBOUND) {
4599255332Scy				if (nat->nat_osport == 0) {
4600255332Scy					nat->nat_osport = sport;
4601255332Scy					nat->nat_nsport = sport;
4602255332Scy				}
4603255332Scy				if (nat->nat_odport == 0) {
4604255332Scy					nat->nat_odport = dport;
4605255332Scy					nat->nat_ndport = dport;
4606255332Scy				}
4607255332Scy			} else if (nat->nat_dir == NAT_INBOUND) {
4608255332Scy				if (nat->nat_osport == 0) {
4609255332Scy					nat->nat_osport = dport;
4610255332Scy					nat->nat_nsport = dport;
4611255332Scy				}
4612255332Scy				if (nat->nat_odport == 0) {
4613255332Scy					nat->nat_odport = sport;
4614255332Scy					nat->nat_ndport = sport;
4615255332Scy				}
4616255332Scy			}
4617255332Scy			if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
4618255332Scy				nat->nat_ifps[1] = ifp;
4619255332Scy				nat->nat_mtu[1] = GETIFMTU_4(ifp);
4620255332Scy			}
4621145522Sdarrenr			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
4622255332Scy			ipf_nat_tabmove(softn, nat);
462367614Sdarrenr			break;
462467614Sdarrenr		}
462567614Sdarrenr	}
4626145522Sdarrenr
4627255332Scy	MUTEX_DOWNGRADE(&softc->ipf_nat);
4628145522Sdarrenr
4629255332Scy	if (nat == NULL) {
4630255332Scy		NBUMPSIDE(1, ns_lookup_miss);
4631255332Scy	}
463267614Sdarrenr	return nat;
463353642Sguido}
463453642Sguido
463553642Sguido
4636145522Sdarrenr/* ------------------------------------------------------------------------ */
4637255332Scy/* Function:    ipf_nat_lookupredir                                         */
4638145522Sdarrenr/* Returns:     nat_t* - NULL == no match,                                  */
4639145522Sdarrenr/*                       else pointer to matching NAT entry                 */
4640145522Sdarrenr/* Parameters:  np(I) - pointer to description of packet to find NAT table  */
4641145522Sdarrenr/*                      entry for.                                          */
4642145522Sdarrenr/*                                                                          */
4643145522Sdarrenr/* Lookup the NAT tables to search for a matching redirect                  */
4644161356Sguido/* The contents of natlookup_t should imitate those found in a packet that  */
4645161356Sguido/* would be translated - ie a packet coming in for RDR or going out for MAP.*/
4646161356Sguido/* We can do the lookup in one of two ways, imitating an inbound or         */
4647161356Sguido/* outbound  packet.  By default we assume outbound, unless IPN_IN is set.  */
4648161356Sguido/* For IN, the fields are set as follows:                                   */
4649161356Sguido/*     nl_real* = source information                                        */
4650161356Sguido/*     nl_out* = destination information (translated)                       */
4651161356Sguido/* For an out packet, the fields are set like this:                         */
4652161356Sguido/*     nl_in* = source information (untranslated)                           */
4653161356Sguido/*     nl_out* = destination information (translated)                       */
4654145522Sdarrenr/* ------------------------------------------------------------------------ */
4655255332Scynat_t *
4656255332Scyipf_nat_lookupredir(np)
4657255332Scy	natlookup_t *np;
465853642Sguido{
4659145522Sdarrenr	fr_info_t fi;
466053642Sguido	nat_t *nat;
466153642Sguido
466292685Sdarrenr	bzero((char *)&fi, sizeof(fi));
4663145522Sdarrenr	if (np->nl_flags & IPN_IN) {
4664145522Sdarrenr		fi.fin_data[0] = ntohs(np->nl_realport);
4665145522Sdarrenr		fi.fin_data[1] = ntohs(np->nl_outport);
4666145522Sdarrenr	} else {
4667145522Sdarrenr		fi.fin_data[0] = ntohs(np->nl_inport);
4668145522Sdarrenr		fi.fin_data[1] = ntohs(np->nl_outport);
4669145522Sdarrenr	}
4670145522Sdarrenr	if (np->nl_flags & IPN_TCP)
4671145522Sdarrenr		fi.fin_p = IPPROTO_TCP;
4672145522Sdarrenr	else if (np->nl_flags & IPN_UDP)
4673145522Sdarrenr		fi.fin_p = IPPROTO_UDP;
4674145522Sdarrenr	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
4675145522Sdarrenr		fi.fin_p = IPPROTO_ICMP;
467692685Sdarrenr
467753642Sguido	/*
4678145522Sdarrenr	 * We can do two sorts of lookups:
4679145522Sdarrenr	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
4680145522Sdarrenr	 * - default: we have the `in' and `out' address, look for `real'.
468153642Sguido	 */
4682145522Sdarrenr	if (np->nl_flags & IPN_IN) {
4683255332Scy		if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p,
4684255332Scy					    np->nl_realip, np->nl_outip))) {
4685255332Scy			np->nl_inip = nat->nat_odstip;
4686255332Scy			np->nl_inport = nat->nat_odport;
4687145522Sdarrenr		}
4688145522Sdarrenr	} else {
4689145522Sdarrenr		/*
4690145522Sdarrenr		 * If nl_inip is non null, this is a lookup based on the real
4691145522Sdarrenr		 * ip address. Else, we use the fake.
4692145522Sdarrenr		 */
4693255332Scy		if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p,
4694145522Sdarrenr					 np->nl_inip, np->nl_outip))) {
4695145522Sdarrenr
4696145522Sdarrenr			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
4697145522Sdarrenr				fr_info_t fin;
4698145522Sdarrenr				bzero((char *)&fin, sizeof(fin));
4699255332Scy				fin.fin_p = nat->nat_pr[0];
4700255332Scy				fin.fin_data[0] = ntohs(nat->nat_ndport);
4701255332Scy				fin.fin_data[1] = ntohs(nat->nat_nsport);
4702255332Scy				if (ipf_nat_inlookup(&fin, np->nl_flags,
4703255332Scy						     fin.fin_p, nat->nat_ndstip,
4704255332Scy						     nat->nat_nsrcip) != NULL) {
4705145522Sdarrenr					np->nl_flags &= ~IPN_FINDFORWARD;
4706145522Sdarrenr				}
4707145522Sdarrenr			}
4708145522Sdarrenr
4709315079Scy			np->nl_realip = nat->nat_odstip;
4710315079Scy			np->nl_realport = nat->nat_odport;
4711145522Sdarrenr		}
4712145522Sdarrenr 	}
4713145522Sdarrenr
471453642Sguido	return nat;
471553642Sguido}
471653642Sguido
471753642Sguido
4718145522Sdarrenr/* ------------------------------------------------------------------------ */
4719255332Scy/* Function:    ipf_nat_match                                               */
4720145522Sdarrenr/* Returns:     int - 0 == no match, 1 == match                             */
4721145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4722145522Sdarrenr/*              np(I)    - pointer to NAT rule                              */
4723145522Sdarrenr/*                                                                          */
4724145522Sdarrenr/* Pull the matching of a packet against a NAT rule out of that complex     */
4725255332Scy/* loop inside ipf_nat_checkin() and lay it out properly in its own function. */
4726145522Sdarrenr/* ------------------------------------------------------------------------ */
4727255332Scystatic int
4728255332Scyipf_nat_match(fin, np)
4729255332Scy	fr_info_t *fin;
4730255332Scy	ipnat_t *np;
473160852Sdarrenr{
4732255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
473360852Sdarrenr	frtuc_t *ft;
4734255332Scy	int match;
473560852Sdarrenr
4736255332Scy	match = 0;
4737255332Scy	switch (np->in_osrcatype)
4738255332Scy	{
4739255332Scy	case FRI_NORMAL :
4740255332Scy		match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr);
4741255332Scy		break;
4742255332Scy	case FRI_LOOKUP :
4743255332Scy		match = (*np->in_osrcfunc)(softc, np->in_osrcptr,
4744255332Scy					   4, &fin->fin_saddr, fin->fin_plen);
4745255332Scy		break;
4746255332Scy	}
4747255332Scy	match ^= ((np->in_flags & IPN_NOTSRC) != 0);
4748255332Scy	if (match)
474960852Sdarrenr		return 0;
475060852Sdarrenr
4751255332Scy	match = 0;
4752255332Scy	switch (np->in_odstatype)
4753255332Scy	{
4754255332Scy	case FRI_NORMAL :
4755255332Scy		match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr);
4756255332Scy		break;
4757255332Scy	case FRI_LOOKUP :
4758255332Scy		match = (*np->in_odstfunc)(softc, np->in_odstptr,
4759255332Scy					   4, &fin->fin_daddr, fin->fin_plen);
4760255332Scy		break;
4761255332Scy	}
4762255332Scy
4763255332Scy	match ^= ((np->in_flags & IPN_NOTDST) != 0);
4764255332Scy	if (match)
476560852Sdarrenr		return 0;
4766145522Sdarrenr
476760852Sdarrenr	ft = &np->in_tuc;
4768145522Sdarrenr	if (!(fin->fin_flx & FI_TCPUDP) ||
4769145522Sdarrenr	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
477060852Sdarrenr		if (ft->ftu_scmp || ft->ftu_dcmp)
477160852Sdarrenr			return 0;
477260852Sdarrenr		return 1;
477360852Sdarrenr	}
477460852Sdarrenr
4775255332Scy	return ipf_tcpudpchk(&fin->fin_fi, ft);
477660852Sdarrenr}
477760852Sdarrenr
477860852Sdarrenr
4779145522Sdarrenr/* ------------------------------------------------------------------------ */
4780255332Scy/* Function:    ipf_nat_update                                              */
4781145522Sdarrenr/* Returns:     Nil                                                         */
4782255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
4783255332Scy/*              nat(I) - pointer to NAT structure                           */
4784145522Sdarrenr/*                                                                          */
4785145522Sdarrenr/* Updates the lifetime of a NAT table entry for non-TCP packets.  Must be  */
4786255332Scy/* called with fin_rev updated - i.e. after calling ipf_nat_proto().        */
4787255332Scy/*                                                                          */
4788255332Scy/* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to     */
4789255332Scy/* already be set.                                                          */
4790145522Sdarrenr/* ------------------------------------------------------------------------ */
4791255332Scyvoid
4792255332Scyipf_nat_update(fin, nat)
4793255332Scy	fr_info_t *fin;
4794255332Scy	nat_t *nat;
479553642Sguido{
4796255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
4797255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
4798145522Sdarrenr	ipftq_t *ifq, *ifq2;
4799145522Sdarrenr	ipftqent_t *tqe;
4800255332Scy	ipnat_t *np = nat->nat_ptr;
4801145522Sdarrenr
4802145522Sdarrenr	tqe = &nat->nat_tqe;
4803145522Sdarrenr	ifq = tqe->tqe_ifq;
4804145522Sdarrenr
4805145522Sdarrenr	/*
4806145522Sdarrenr	 * We allow over-riding of NAT timeouts from NAT rules, even for
4807145522Sdarrenr	 * TCP, however, if it is TCP and there is no rule timeout set,
4808145522Sdarrenr	 * then do not update the timeout here.
4809145522Sdarrenr	 */
4810255332Scy	if (np != NULL) {
4811255332Scy		np->in_bytes[fin->fin_rev] += fin->fin_plen;
4812145522Sdarrenr		ifq2 = np->in_tqehead[fin->fin_rev];
4813255332Scy	} else {
4814145522Sdarrenr		ifq2 = NULL;
4815255332Scy	}
4816145522Sdarrenr
4817255332Scy	if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) {
4818255332Scy		(void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq,
4819255332Scy				   0, 2);
4820145522Sdarrenr	} else {
4821145522Sdarrenr		if (ifq2 == NULL) {
4822255332Scy			if (nat->nat_pr[0] == IPPROTO_UDP)
4823255332Scy				ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq :
4824255332Scy						      &softn->ipf_nat_udptq;
4825255332Scy			else if (nat->nat_pr[0] == IPPROTO_ICMP ||
4826255332Scy				 nat->nat_pr[0] == IPPROTO_ICMPV6)
4827255332Scy				ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq:
4828255332Scy						      &softn->ipf_nat_icmptq;
4829145522Sdarrenr			else
4830255332Scy				ifq2 = &softn->ipf_nat_iptq;
4831145522Sdarrenr		}
4832145522Sdarrenr
4833255332Scy		ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2);
4834145522Sdarrenr	}
4835145522Sdarrenr}
4836145522Sdarrenr
4837145522Sdarrenr
4838145522Sdarrenr/* ------------------------------------------------------------------------ */
4839255332Scy/* Function:    ipf_nat_checkout                                            */
4840145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
4841145522Sdarrenr/*                     0 == no packet translation occurred,                 */
4842145522Sdarrenr/*                     1 == packet was successfully translated.             */
4843145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
4844145522Sdarrenr/*              passp(I) - pointer to filtering result flags                */
4845145522Sdarrenr/*                                                                          */
4846145522Sdarrenr/* Check to see if an outcoming packet should be changed.  ICMP packets are */
4847145522Sdarrenr/* first checked to see if they match an existing entry (if an error),      */
4848145522Sdarrenr/* otherwise a search of the current NAT table is made.  If neither results */
4849145522Sdarrenr/* in a match then a search for a matching NAT rule is made.  Create a new  */
4850145522Sdarrenr/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
4851145522Sdarrenr/* packet header(s) as required.                                            */
4852145522Sdarrenr/* ------------------------------------------------------------------------ */
4853255332Scyint
4854255332Scyipf_nat_checkout(fin, passp)
4855255332Scy	fr_info_t *fin;
4856255332Scy	u_32_t *passp;
4857145522Sdarrenr{
4858255332Scy	ipnat_t *np = NULL, *npnext;
4859145522Sdarrenr	struct ifnet *ifp, *sifp;
4860255332Scy	ipf_main_softc_t *softc;
4861255332Scy	ipf_nat_softc_t *softn;
4862145522Sdarrenr	icmphdr_t *icmp = NULL;
486353642Sguido	tcphdr_t *tcp = NULL;
4864145522Sdarrenr	int rval, natfailed;
4865145522Sdarrenr	u_int nflags = 0;
4866145522Sdarrenr	u_32_t ipa, iph;
4867145522Sdarrenr	int natadd = 1;
486853642Sguido	frentry_t *fr;
486953642Sguido	nat_t *nat;
487053642Sguido
4871255332Scy	if (fin->fin_v == 6) {
4872255332Scy#ifdef USE_INET6
4873255332Scy		return ipf_nat6_checkout(fin, passp);
4874255332Scy#else
487553642Sguido		return 0;
4876255332Scy#endif
4877255332Scy	}
487853642Sguido
4879255332Scy	softc = fin->fin_main_soft;
4880255332Scy	softn = softc->ipf_nat_soft;
4881255332Scy
4882255332Scy	if (softn->ipf_nat_lock != 0)
4883255332Scy		return 0;
4884255332Scy	if (softn->ipf_nat_stats.ns_rules == 0 &&
4885255332Scy	    softn->ipf_nat_instances == NULL)
4886255332Scy		return 0;
4887255332Scy
4888145522Sdarrenr	natfailed = 0;
4889145522Sdarrenr	fr = fin->fin_fr;
4890145522Sdarrenr	sifp = fin->fin_ifp;
4891170268Sdarrenr	if (fr != NULL) {
4892255332Scy		ifp = fr->fr_tifs[fin->fin_rev].fd_ptr;
4893170268Sdarrenr		if ((ifp != NULL) && (ifp != (void *)-1))
4894170268Sdarrenr			fin->fin_ifp = ifp;
4895170268Sdarrenr	}
489692685Sdarrenr	ifp = fin->fin_ifp;
489753642Sguido
4898145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
4899145522Sdarrenr		switch (fin->fin_p)
4900145522Sdarrenr		{
4901145522Sdarrenr		case IPPROTO_TCP :
490253642Sguido			nflags = IPN_TCP;
4903145522Sdarrenr			break;
4904145522Sdarrenr		case IPPROTO_UDP :
490553642Sguido			nflags = IPN_UDP;
4906145522Sdarrenr			break;
4907145522Sdarrenr		case IPPROTO_ICMP :
4908145522Sdarrenr			icmp = fin->fin_dp;
4909145522Sdarrenr
4910145522Sdarrenr			/*
4911145522Sdarrenr			 * This is an incoming packet, so the destination is
4912145522Sdarrenr			 * the icmp_id and the source port equals 0
4913145522Sdarrenr			 */
4914255332Scy			if ((fin->fin_flx & FI_ICMPQUERY) != 0)
4915145522Sdarrenr				nflags = IPN_ICMPQUERY;
4916145522Sdarrenr			break;
4917145522Sdarrenr		default :
4918145522Sdarrenr			break;
491953642Sguido		}
4920255332Scy
4921145522Sdarrenr		if ((nflags & IPN_TCPUDP))
4922145522Sdarrenr			tcp = fin->fin_dp;
492353642Sguido	}
492453642Sguido
492592685Sdarrenr	ipa = fin->fin_saddr;
492653642Sguido
4927255332Scy	READ_ENTER(&softc->ipf_nat);
492860852Sdarrenr
4929255332Scy	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
4930255332Scy	    (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND)))
4931145522Sdarrenr		/*EMPTY*/;
4932255332Scy	else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
493353642Sguido		natadd = 0;
4934255332Scy	else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH,
4935255332Scy				      (u_int)fin->fin_p, fin->fin_src,
4936255332Scy				      fin->fin_dst))) {
493753642Sguido		nflags = nat->nat_flags;
4938255332Scy	} else if (fin->fin_off == 0) {
4939255332Scy		u_32_t hv, msk, nmsk = 0;
494092685Sdarrenr
494153642Sguido		/*
494253642Sguido		 * If there is no current entry in the nat table for this IP#,
494353642Sguido		 * create one for it (if there is a matching rule).
494453642Sguido		 */
494553642Sguidomaskloop:
4946255332Scy		msk = softn->ipf_nat_map_active_masks[nmsk];
4947255332Scy		iph = ipa & msk;
4948255332Scy		hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz);
4949255332Scyretry_roundrobin:
4950255332Scy		for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) {
4951255332Scy			npnext = np->in_mnext;
4952161356Sguido			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
495360852Sdarrenr				continue;
4954255332Scy			if (np->in_v[0] != 4)
495560852Sdarrenr				continue;
4956255332Scy			if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p))
4957145522Sdarrenr				continue;
4958255332Scy			if ((np->in_flags & IPN_RF) &&
4959255332Scy			    !(np->in_flags & nflags))
4960145522Sdarrenr				continue;
496160852Sdarrenr			if (np->in_flags & IPN_FILTER) {
4962255332Scy				switch (ipf_nat_match(fin, np))
4963255332Scy				{
4964255332Scy				case 0 :
496560852Sdarrenr					continue;
4966255332Scy				case -1 :
4967337948Scy					rval = -3;
4968255332Scy					goto outmatchfail;
4969255332Scy				case 1 :
4970255332Scy				default :
4971255332Scy					break;
4972255332Scy				}
4973255332Scy			} else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr)
497460852Sdarrenr				continue;
4975145522Sdarrenr
4976145522Sdarrenr			if ((fr != NULL) &&
4977255332Scy			    !ipf_matchtag(&np->in_tag, &fr->fr_nattag))
497892685Sdarrenr				continue;
4979145522Sdarrenr
4980255332Scy			if (np->in_plabel != -1) {
4981145522Sdarrenr				if (((np->in_flags & IPN_FILTER) == 0) &&
4982255332Scy				    (np->in_odport != fin->fin_data[1]))
4983145522Sdarrenr					continue;
4984255332Scy				if (ipf_proxy_ok(fin, tcp, np) == 0)
4985145522Sdarrenr					continue;
4986145522Sdarrenr			}
4987145522Sdarrenr
4988255332Scy			if (np->in_flags & IPN_NO) {
498992685Sdarrenr				np->in_hits++;
499092685Sdarrenr				break;
4991145522Sdarrenr			}
4992255332Scy			MUTEX_ENTER(&softn->ipf_nat_new);
4993255332Scy			/*
4994255332Scy			 * If we've matched a round-robin rule but it has
4995255332Scy			 * moved in the list since we got it, start over as
4996255332Scy			 * this is now no longer correct.
4997255332Scy			 */
4998255332Scy			if (npnext != np->in_mnext) {
4999255332Scy				if ((np->in_flags & IPN_ROUNDR) != 0) {
5000255332Scy					MUTEX_EXIT(&softn->ipf_nat_new);
5001255332Scy					goto retry_roundrobin;
5002255332Scy				}
5003255332Scy				npnext = np->in_mnext;
5004145522Sdarrenr			}
5005255332Scy
5006255332Scy			nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND);
5007255332Scy			MUTEX_EXIT(&softn->ipf_nat_new);
5008255332Scy			if (nat != NULL) {
5009255332Scy				natfailed = 0;
5010255332Scy				break;
5011255332Scy			}
5012337948Scy			natfailed = -2;
501353642Sguido		}
5014255332Scy		if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) {
5015255332Scy			nmsk++;
5016255332Scy			goto maskloop;
5017255332Scy		}
501853642Sguido	}
501953642Sguido
5020145522Sdarrenr	if (nat != NULL) {
5021255332Scy		rval = ipf_nat_out(fin, nat, natadd, nflags);
5022145522Sdarrenr		if (rval == 1) {
5023145522Sdarrenr			MUTEX_ENTER(&nat->nat_lock);
5024255332Scy			ipf_nat_update(fin, nat);
5025255332Scy			nat->nat_bytes[1] += fin->fin_plen;
5026255332Scy			nat->nat_pkts[1]++;
5027255332Scy			fin->fin_pktnum = nat->nat_pkts[1];
5028145522Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
5029145522Sdarrenr		}
5030145522Sdarrenr	} else
5031145522Sdarrenr		rval = natfailed;
5032255332Scyoutmatchfail:
5033255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
5034145522Sdarrenr
5035255332Scy	switch (rval)
5036255332Scy	{
5037337948Scy	case -3 :
5038337948Scy		/* ipf_nat_match() failure */
5039337948Scy		/* FALLTHROUGH */
5040337948Scy	case -2 :
5041337948Scy		/* retry_roundrobin loop failure */
5042337948Scy		/* FALLTHROUGH */
5043255332Scy	case -1 :
5044337948Scy		/* proxy failure detected by ipf_nat_out() */
5045255332Scy		if (passp != NULL) {
5046337948Scy			DT2(frb_natv4out, fr_info_t *, fin, int, rval);
5047255332Scy			NBUMPSIDED(1, ns_drop);
5048145522Sdarrenr			*passp = FR_BLOCK;
5049255332Scy			fin->fin_reason = FRB_NATV4;
5050255332Scy		}
5051145522Sdarrenr		fin->fin_flx |= FI_BADNAT;
5052255332Scy		NBUMPSIDED(1, ns_badnat);
5053337948Scy		rval = -1;	/* We only return -1 on error. */
5054255332Scy		break;
5055255332Scy	case 0 :
5056255332Scy		NBUMPSIDE(1, ns_ignored);
5057255332Scy		break;
5058255332Scy	case 1 :
5059255332Scy		NBUMPSIDE(1, ns_translated);
5060255332Scy		break;
5061145522Sdarrenr	}
5062145522Sdarrenr	fin->fin_ifp = sifp;
5063145522Sdarrenr	return rval;
5064145522Sdarrenr}
5065145522Sdarrenr
5066145522Sdarrenr/* ------------------------------------------------------------------------ */
5067255332Scy/* Function:    ipf_nat_out                                                 */
5068145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
5069145522Sdarrenr/*                     1 == packet was successfully translated.             */
5070145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
5071145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
5072145522Sdarrenr/*              natadd(I) - flag indicating if it is safe to add frag cache */
5073145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
5074145522Sdarrenr/*                                                                          */
5075145522Sdarrenr/* Translate a packet coming "out" on an interface.                         */
5076145522Sdarrenr/* ------------------------------------------------------------------------ */
5077255332Scyint
5078255332Scyipf_nat_out(fin, nat, natadd, nflags)
5079255332Scy	fr_info_t *fin;
5080255332Scy	nat_t *nat;
5081255332Scy	int natadd;
5082255332Scy	u_32_t nflags;
5083145522Sdarrenr{
5084255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
5085255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5086145522Sdarrenr	icmphdr_t *icmp;
5087145522Sdarrenr	tcphdr_t *tcp;
5088145522Sdarrenr	ipnat_t *np;
5089255332Scy	int skip;
5090145522Sdarrenr	int i;
5091145522Sdarrenr
5092145522Sdarrenr	tcp = NULL;
5093145522Sdarrenr	icmp = NULL;
5094145522Sdarrenr	np = nat->nat_ptr;
5095145522Sdarrenr
5096145522Sdarrenr	if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL))
5097255332Scy		(void) ipf_frag_natnew(softc, fin, 0, nat);
5098145522Sdarrenr
509972006Sdarrenr	/*
5100145522Sdarrenr	 * Fix up checksums, not by recalculating them, but
5101145522Sdarrenr	 * simply computing adjustments.
5102145522Sdarrenr	 * This is only done for STREAMS based IP implementations where the
5103145522Sdarrenr	 * checksum has already been calculated by IP.  In all other cases,
5104145522Sdarrenr	 * IPFilter is called before the checksum needs calculating so there
5105145522Sdarrenr	 * is no call to modify whatever is in the header now.
510672006Sdarrenr	 */
5107255332Scy	if (nflags == IPN_ICMPERR) {
5108255332Scy		u_32_t s1, s2, sumd, msumd;
510963523Sdarrenr
5110255332Scy		s1 = LONG_SUM(ntohl(fin->fin_saddr));
5111255332Scy		if (nat->nat_dir == NAT_OUTBOUND) {
5112255332Scy			s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
5113255332Scy		} else {
5114255332Scy			s2 = LONG_SUM(ntohl(nat->nat_odstaddr));
511563523Sdarrenr		}
5116255332Scy		CALC_SUMD(s1, s2, sumd);
5117255332Scy		msumd = sumd;
5118255332Scy
5119255332Scy		s1 = LONG_SUM(ntohl(fin->fin_daddr));
5120255332Scy		if (nat->nat_dir == NAT_OUTBOUND) {
5121255332Scy			s2 = LONG_SUM(ntohl(nat->nat_ndstaddr));
5122255332Scy		} else {
5123255332Scy			s2 = LONG_SUM(ntohl(nat->nat_osrcaddr));
5124255332Scy		}
5125255332Scy		CALC_SUMD(s1, s2, sumd);
5126255332Scy		msumd += sumd;
5127255332Scy
5128255332Scy		ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0);
5129255332Scy	}
5130153876Sguido#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5131292811Scy    defined(linux) || defined(BRIDGE_IPF) || defined(__FreeBSD__)
5132255332Scy	else {
5133255332Scy		/*
5134255332Scy		 * Strictly speaking, this isn't necessary on BSD
5135255332Scy		 * kernels because they do checksum calculation after
5136255332Scy		 * this code has run BUT if ipfilter is being used
5137255332Scy		 * to do NAT as a bridge, that code doesn't exist.
5138255332Scy		 */
5139255332Scy		switch (nat->nat_dir)
5140255332Scy		{
5141255332Scy		case NAT_OUTBOUND :
5142255332Scy			ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART,
5143255332Scy					 &fin->fin_ip->ip_sum,
5144255332Scy					 nat->nat_ipsumd, 0);
5145255332Scy			break;
5146255332Scy
5147255332Scy		case NAT_INBOUND :
5148255332Scy			ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART,
5149255332Scy					&fin->fin_ip->ip_sum,
5150255332Scy					nat->nat_ipsumd, 0);
5151255332Scy			break;
5152255332Scy
5153255332Scy		default :
5154255332Scy			break;
515563523Sdarrenr		}
5156255332Scy	}
515753642Sguido#endif
5158255332Scy
5159255332Scy	/*
5160255332Scy	 * Address assignment is after the checksum modification because
5161255332Scy	 * we are using the address in the packet for determining the
5162255332Scy	 * correct checksum offset (the ICMP error could be coming from
5163255332Scy	 * anyone...)
5164255332Scy	 */
5165255332Scy	switch (nat->nat_dir)
5166255332Scy	{
5167255332Scy	case NAT_OUTBOUND :
5168255332Scy		fin->fin_ip->ip_src = nat->nat_nsrcip;
5169255332Scy		fin->fin_saddr = nat->nat_nsrcaddr;
5170255332Scy		fin->fin_ip->ip_dst = nat->nat_ndstip;
5171255332Scy		fin->fin_daddr = nat->nat_ndstaddr;
5172255332Scy		break;
5173255332Scy
5174255332Scy	case NAT_INBOUND :
5175255332Scy		fin->fin_ip->ip_src = nat->nat_odstip;
5176255332Scy		fin->fin_saddr = nat->nat_ndstaddr;
5177255332Scy		fin->fin_ip->ip_dst = nat->nat_osrcip;
5178255332Scy		fin->fin_daddr = nat->nat_nsrcaddr;
5179255332Scy		break;
5180255332Scy
5181255332Scy	case NAT_DIVERTIN :
5182255332Scy	    {
5183255332Scy		mb_t *m;
5184255332Scy
5185255332Scy		skip = ipf_nat_decap(fin, nat);
5186255332Scy		if (skip <= 0) {
5187255332Scy			NBUMPSIDED(1, ns_decap_fail);
5188255332Scy			return -1;
5189255332Scy		}
5190255332Scy
5191255332Scy		m = fin->fin_m;
5192255332Scy
5193255332Scy#if defined(MENTAT) && defined(_KERNEL)
5194255332Scy		m->b_rptr += skip;
5195255332Scy#else
5196255332Scy		m->m_data += skip;
5197255332Scy		m->m_len -= skip;
5198255332Scy
5199255332Scy# ifdef M_PKTHDR
5200255332Scy		if (m->m_flags & M_PKTHDR)
5201255332Scy			m->m_pkthdr.len -= skip;
5202255332Scy# endif
5203255332Scy#endif
5204255332Scy
5205255332Scy		MUTEX_ENTER(&nat->nat_lock);
5206255332Scy		ipf_nat_update(fin, nat);
5207255332Scy		MUTEX_EXIT(&nat->nat_lock);
5208255332Scy		fin->fin_flx |= FI_NATED;
5209255332Scy		if (np != NULL && np->in_tag.ipt_num[0] != 0)
5210255332Scy			fin->fin_nattag = &np->in_tag;
5211255332Scy		return 1;
5212255332Scy		/* NOTREACHED */
5213255332Scy	    }
5214255332Scy
5215255332Scy	case NAT_DIVERTOUT :
5216255332Scy	    {
5217255332Scy		u_32_t s1, s2, sumd;
5218255332Scy		udphdr_t *uh;
5219255332Scy		ip_t *ip;
5220255332Scy		mb_t *m;
5221255332Scy
5222255332Scy		m = M_DUP(np->in_divmp);
5223255332Scy		if (m == NULL) {
5224255332Scy			NBUMPSIDED(1, ns_divert_dup);
5225255332Scy			return -1;
5226255332Scy		}
5227255332Scy
5228255332Scy		ip = MTOD(m, ip_t *);
5229255332Scy		ip->ip_id = htons(ipf_nextipid(fin));
5230255332Scy		s2 = ntohs(ip->ip_id);
5231255332Scy
5232255332Scy		s1 = ip->ip_len;
5233255332Scy		ip->ip_len = ntohs(ip->ip_len);
5234255332Scy		ip->ip_len += fin->fin_plen;
5235255332Scy		ip->ip_len = htons(ip->ip_len);
5236255332Scy		s2 += ntohs(ip->ip_len);
5237255332Scy		CALC_SUMD(s1, s2, sumd);
5238255332Scy
5239255332Scy		uh = (udphdr_t *)(ip + 1);
5240255332Scy		uh->uh_ulen += fin->fin_plen;
5241255332Scy		uh->uh_ulen = htons(uh->uh_ulen);
5242255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5243292979Scy    defined(linux) || defined(BRIDGE_IPF) || defined(__FreeBSD__)
5244255332Scy		ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0);
5245255332Scy#endif
5246255332Scy
5247255332Scy		PREP_MB_T(fin, m);
5248255332Scy
5249255332Scy		fin->fin_src = ip->ip_src;
5250255332Scy		fin->fin_dst = ip->ip_dst;
5251255332Scy		fin->fin_ip = ip;
5252255332Scy		fin->fin_plen += sizeof(ip_t) + 8;	/* UDP + IPv4 hdr */
5253255332Scy		fin->fin_dlen += sizeof(ip_t) + 8;	/* UDP + IPv4 hdr */
5254255332Scy
5255255332Scy		nflags &= ~IPN_TCPUDPICMP;
5256255332Scy
5257255332Scy		break;
5258255332Scy	    }
5259255332Scy
5260255332Scy	default :
5261255332Scy		break;
5262145522Sdarrenr	}
526353642Sguido
5264145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
5265255332Scy		u_short *csump;
5266255332Scy
5267255332Scy		if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) {
5268145522Sdarrenr			tcp = fin->fin_dp;
526953642Sguido
5270255332Scy			switch (nat->nat_dir)
5271255332Scy			{
5272255332Scy			case NAT_OUTBOUND :
5273255332Scy				tcp->th_sport = nat->nat_nsport;
5274255332Scy				fin->fin_data[0] = ntohs(nat->nat_nsport);
5275255332Scy				tcp->th_dport = nat->nat_ndport;
5276255332Scy				fin->fin_data[1] = ntohs(nat->nat_ndport);
5277255332Scy				break;
5278255332Scy
5279255332Scy			case NAT_INBOUND :
5280255332Scy				tcp->th_sport = nat->nat_odport;
5281255332Scy				fin->fin_data[0] = ntohs(nat->nat_odport);
5282255332Scy				tcp->th_dport = nat->nat_osport;
5283255332Scy				fin->fin_data[1] = ntohs(nat->nat_osport);
5284255332Scy				break;
5285255332Scy			}
5286145522Sdarrenr		}
528753642Sguido
5288255332Scy		if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) {
5289145522Sdarrenr			icmp = fin->fin_dp;
5290255332Scy			icmp->icmp_id = nat->nat_nicmpid;
5291145522Sdarrenr		}
5292110916Sdarrenr
5293255332Scy		csump = ipf_nat_proto(fin, nat, nflags);
5294255332Scy
5295255332Scy		/*
5296255332Scy		 * The above comments do not hold for layer 4 (or higher)
5297255332Scy		 * checksums...
5298255332Scy		 */
5299255332Scy		if (csump != NULL) {
5300255332Scy			if (nat->nat_dir == NAT_OUTBOUND)
5301255332Scy				ipf_fix_outcksum(fin->fin_cksum, csump,
5302255332Scy						 nat->nat_sumd[0],
5303255332Scy						 nat->nat_sumd[1] +
5304255332Scy						 fin->fin_dlen);
5305255332Scy			else
5306255332Scy				ipf_fix_incksum(fin->fin_cksum, csump,
5307255332Scy						nat->nat_sumd[0],
5308255332Scy						nat->nat_sumd[1] +
5309255332Scy						fin->fin_dlen);
5310255332Scy		}
5311145522Sdarrenr	}
5312110916Sdarrenr
5313255332Scy	ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
5314145522Sdarrenr	/* ------------------------------------------------------------- */
5315255332Scy	/* A few quick notes:                                            */
5316255332Scy	/*      Following are test conditions prior to calling the       */
5317255332Scy	/*      ipf_proxy_check routine.                                 */
5318255332Scy	/*                                                               */
5319255332Scy	/*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
5320255332Scy	/*      with a redirect rule, we attempt to match the packet's   */
5321255332Scy	/*      source port against in_dport, otherwise we'd compare the */
5322255332Scy	/*      packet's destination.                                    */
5323145522Sdarrenr	/* ------------------------------------------------------------- */
5324145522Sdarrenr	if ((np != NULL) && (np->in_apr != NULL)) {
5325255332Scy		i = ipf_proxy_check(fin, nat);
5326255332Scy		if (i == 0) {
532760852Sdarrenr			i = 1;
5328255332Scy		} else if (i == -1) {
5329255332Scy			NBUMPSIDED(1, ns_ipf_proxy_fail);
5330255332Scy		}
5331255332Scy	} else {
5332145522Sdarrenr		i = 1;
5333255332Scy	}
5334145522Sdarrenr	fin->fin_flx |= FI_NATED;
5335145522Sdarrenr	return i;
533653642Sguido}
533753642Sguido
533853642Sguido
5339145522Sdarrenr/* ------------------------------------------------------------------------ */
5340255332Scy/* Function:    ipf_nat_checkin                                             */
5341145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
5342145522Sdarrenr/*                     0 == no packet translation occurred,                 */
5343145522Sdarrenr/*                     1 == packet was successfully translated.             */
5344145522Sdarrenr/* Parameters:  fin(I)   - pointer to packet information                    */
5345145522Sdarrenr/*              passp(I) - pointer to filtering result flags                */
5346145522Sdarrenr/*                                                                          */
5347145522Sdarrenr/* Check to see if an incoming packet should be changed.  ICMP packets are  */
5348145522Sdarrenr/* first checked to see if they match an existing entry (if an error),      */
5349145522Sdarrenr/* otherwise a search of the current NAT table is made.  If neither results */
5350145522Sdarrenr/* in a match then a search for a matching NAT rule is made.  Create a new  */
5351145522Sdarrenr/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
5352145522Sdarrenr/* packet header(s) as required.                                            */
5353145522Sdarrenr/* ------------------------------------------------------------------------ */
5354255332Scyint
5355255332Scyipf_nat_checkin(fin, passp)
5356255332Scy	fr_info_t *fin;
5357255332Scy	u_32_t *passp;
535853642Sguido{
5359255332Scy	ipf_main_softc_t *softc;
5360255332Scy	ipf_nat_softc_t *softn;
5361145522Sdarrenr	u_int nflags, natadd;
5362255332Scy	ipnat_t *np, *npnext;
5363145522Sdarrenr	int rval, natfailed;
5364145522Sdarrenr	struct ifnet *ifp;
5365145522Sdarrenr	struct in_addr in;
5366145522Sdarrenr	icmphdr_t *icmp;
5367145522Sdarrenr	tcphdr_t *tcp;
5368145522Sdarrenr	u_short dport;
536953642Sguido	nat_t *nat;
537053642Sguido	u_32_t iph;
537153642Sguido
5372255332Scy	softc = fin->fin_main_soft;
5373255332Scy	softn = softc->ipf_nat_soft;
5374255332Scy
5375255332Scy	if (softn->ipf_nat_lock != 0)
537653642Sguido		return 0;
5377255332Scy	if (softn->ipf_nat_stats.ns_rules == 0 &&
5378255332Scy	    softn->ipf_nat_instances == NULL)
5379255332Scy		return 0;
538053642Sguido
5381145522Sdarrenr	tcp = NULL;
5382145522Sdarrenr	icmp = NULL;
5383145522Sdarrenr	dport = 0;
5384145522Sdarrenr	natadd = 1;
5385145522Sdarrenr	nflags = 0;
5386145522Sdarrenr	natfailed = 0;
5387145522Sdarrenr	ifp = fin->fin_ifp;
5388145522Sdarrenr
5389145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
5390145522Sdarrenr		switch (fin->fin_p)
5391145522Sdarrenr		{
5392145522Sdarrenr		case IPPROTO_TCP :
539353642Sguido			nflags = IPN_TCP;
5394145522Sdarrenr			break;
5395145522Sdarrenr		case IPPROTO_UDP :
539653642Sguido			nflags = IPN_UDP;
5397145522Sdarrenr			break;
5398145522Sdarrenr		case IPPROTO_ICMP :
5399145522Sdarrenr			icmp = fin->fin_dp;
5400145522Sdarrenr
5401145522Sdarrenr			/*
5402145522Sdarrenr			 * This is an incoming packet, so the destination is
5403145522Sdarrenr			 * the icmp_id and the source port equals 0
5404145522Sdarrenr			 */
5405255332Scy			if ((fin->fin_flx & FI_ICMPQUERY) != 0) {
5406145522Sdarrenr				nflags = IPN_ICMPQUERY;
5407255332Scy				dport = icmp->icmp_id;
5408145522Sdarrenr			} break;
5409145522Sdarrenr		default :
5410145522Sdarrenr			break;
5411145522Sdarrenr		}
5412255332Scy
541353642Sguido		if ((nflags & IPN_TCPUDP)) {
5414145522Sdarrenr			tcp = fin->fin_dp;
5415255332Scy			dport = fin->fin_data[1];
541653642Sguido		}
541753642Sguido	}
541853642Sguido
541992685Sdarrenr	in = fin->fin_dst;
542053642Sguido
5421255332Scy	READ_ENTER(&softc->ipf_nat);
542253642Sguido
5423255332Scy	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
5424255332Scy	    (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND)))
5425145522Sdarrenr		/*EMPTY*/;
5426255332Scy	else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
542753642Sguido		natadd = 0;
5428255332Scy	else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH,
5429255332Scy					 (u_int)fin->fin_p,
5430255332Scy					 fin->fin_src, in))) {
543153642Sguido		nflags = nat->nat_flags;
5432255332Scy	} else if (fin->fin_off == 0) {
5433255332Scy		u_32_t hv, msk, rmsk = 0;
5434145522Sdarrenr
543553642Sguido		/*
543653642Sguido		 * If there is no current entry in the nat table for this IP#,
543753642Sguido		 * create one for it (if there is a matching rule).
543853642Sguido		 */
543953642Sguidomaskloop:
5440255332Scy		msk = softn->ipf_nat_rdr_active_masks[rmsk];
5441255332Scy		iph = in.s_addr & msk;
5442255332Scy		hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz);
5443255332Scyretry_roundrobin:
5444255332Scy		/* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */
5445255332Scy		for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) {
5446255332Scy			npnext = np->in_rnext;
5447145522Sdarrenr			if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
544860852Sdarrenr				continue;
5449255332Scy			if (np->in_v[0] != 4)
5450138947Sdarrenr				continue;
5451255332Scy			if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p))
5452145522Sdarrenr				continue;
5453145522Sdarrenr			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
5454145522Sdarrenr				continue;
545560852Sdarrenr			if (np->in_flags & IPN_FILTER) {
5456255332Scy				switch (ipf_nat_match(fin, np))
5457255332Scy				{
5458255332Scy				case 0 :
545960852Sdarrenr					continue;
5460255332Scy				case -1 :
5461337948Scy					rval = -3;
5462255332Scy					goto inmatchfail;
5463255332Scy				case 1 :
5464255332Scy				default :
5465255332Scy					break;
5466255332Scy				}
5467145522Sdarrenr			} else {
5468255332Scy				if ((in.s_addr & np->in_odstmsk) !=
5469255332Scy				    np->in_odstaddr)
5470145522Sdarrenr					continue;
5471255332Scy				if (np->in_odport &&
5472255332Scy				    ((np->in_dtop < dport) ||
5473255332Scy				     (dport < np->in_odport)))
5474145522Sdarrenr					continue;
5475145522Sdarrenr			}
5476145522Sdarrenr
5477255332Scy			if (np->in_plabel != -1) {
5478255332Scy				if (!ipf_proxy_ok(fin, tcp, np)) {
5479145522Sdarrenr					continue;
548053642Sguido				}
5481145522Sdarrenr			}
5482145522Sdarrenr
5483255332Scy			if (np->in_flags & IPN_NO) {
5484145522Sdarrenr				np->in_hits++;
5485145522Sdarrenr				break;
5486255332Scy			}
548760852Sdarrenr
5488255332Scy			MUTEX_ENTER(&softn->ipf_nat_new);
5489255332Scy			/*
5490255332Scy			 * If we've matched a round-robin rule but it has
5491255332Scy			 * moved in the list since we got it, start over as
5492255332Scy			 * this is now no longer correct.
5493255332Scy			 */
5494255332Scy			if (npnext != np->in_rnext) {
5495255332Scy				if ((np->in_flags & IPN_ROUNDR) != 0) {
5496255332Scy					MUTEX_EXIT(&softn->ipf_nat_new);
5497255332Scy					goto retry_roundrobin;
5498255332Scy				}
5499255332Scy				npnext = np->in_rnext;
5500145522Sdarrenr			}
5501255332Scy
5502255332Scy			nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND);
5503255332Scy			MUTEX_EXIT(&softn->ipf_nat_new);
5504255332Scy			if (nat != NULL) {
5505255332Scy				natfailed = 0;
5506255332Scy				break;
5507145522Sdarrenr			}
5508337948Scy			natfailed = -2;
550953642Sguido		}
5510255332Scy		if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) {
5511255332Scy			rmsk++;
5512255332Scy			goto maskloop;
5513255332Scy		}
551453642Sguido	}
5515255332Scy
5516145522Sdarrenr	if (nat != NULL) {
5517255332Scy		rval = ipf_nat_in(fin, nat, natadd, nflags);
5518145522Sdarrenr		if (rval == 1) {
5519145522Sdarrenr			MUTEX_ENTER(&nat->nat_lock);
5520255332Scy			ipf_nat_update(fin, nat);
5521255332Scy			nat->nat_bytes[0] += fin->fin_plen;
5522255332Scy			nat->nat_pkts[0]++;
5523255332Scy			fin->fin_pktnum = nat->nat_pkts[0];
5524145522Sdarrenr			MUTEX_EXIT(&nat->nat_lock);
5525145522Sdarrenr		}
5526145522Sdarrenr	} else
5527145522Sdarrenr		rval = natfailed;
5528255332Scyinmatchfail:
5529255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
553072006Sdarrenr
5531255332Scy	switch (rval)
5532255332Scy	{
5533337948Scy	case -3 :
5534337948Scy		/* ipf_nat_match() failure */
5535337948Scy		/* FALLTHROUGH */
5536337948Scy	case -2 :
5537337948Scy		/* retry_roundrobin loop failure */
5538337948Scy		/* FALLTHROUGH */
5539255332Scy	case -1 :
5540337948Scy		/* proxy failure detected by ipf_nat_in() */
5541255332Scy		if (passp != NULL) {
5542337948Scy			DT2(frb_natv4in, fr_info_t *, fin, int, rval);
5543255332Scy			NBUMPSIDED(0, ns_drop);
5544145522Sdarrenr			*passp = FR_BLOCK;
5545255332Scy			fin->fin_reason = FRB_NATV4;
5546255332Scy		}
5547145522Sdarrenr		fin->fin_flx |= FI_BADNAT;
5548255332Scy		NBUMPSIDED(0, ns_badnat);
5549337948Scy		rval = -1;	/* We only return -1 on error. */
5550255332Scy		break;
5551255332Scy	case 0 :
5552255332Scy		NBUMPSIDE(0, ns_ignored);
5553255332Scy		break;
5554255332Scy	case 1 :
5555255332Scy		NBUMPSIDE(0, ns_translated);
5556255332Scy		break;
5557145522Sdarrenr	}
5558145522Sdarrenr	return rval;
5559145522Sdarrenr}
5560145522Sdarrenr
5561145522Sdarrenr
5562145522Sdarrenr/* ------------------------------------------------------------------------ */
5563255332Scy/* Function:    ipf_nat_in                                                  */
5564145522Sdarrenr/* Returns:     int - -1 == packet failed NAT checks so block it,           */
5565145522Sdarrenr/*                     1 == packet was successfully translated.             */
5566145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
5567145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
5568145522Sdarrenr/*              natadd(I) - flag indicating if it is safe to add frag cache */
5569145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
5570255332Scy/* Locks Held:  ipf_nat(READ)                                               */
5571145522Sdarrenr/*                                                                          */
5572145522Sdarrenr/* Translate a packet coming "in" on an interface.                          */
5573145522Sdarrenr/* ------------------------------------------------------------------------ */
5574255332Scyint
5575255332Scyipf_nat_in(fin, nat, natadd, nflags)
5576255332Scy	fr_info_t *fin;
5577255332Scy	nat_t *nat;
5578255332Scy	int natadd;
5579255332Scy	u_32_t nflags;
5580145522Sdarrenr{
5581255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
5582255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5583255332Scy	u_32_t sumd, ipsumd, sum1, sum2;
5584145522Sdarrenr	icmphdr_t *icmp;
5585145522Sdarrenr	tcphdr_t *tcp;
5586145522Sdarrenr	ipnat_t *np;
5587255332Scy	int skip;
5588145522Sdarrenr	int i;
5589145522Sdarrenr
5590145522Sdarrenr	tcp = NULL;
5591145522Sdarrenr	np = nat->nat_ptr;
5592145522Sdarrenr	fin->fin_fr = nat->nat_fr;
5593145522Sdarrenr
5594145522Sdarrenr	if (np != NULL) {
5595145522Sdarrenr		if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
5596255332Scy			(void) ipf_frag_natnew(softc, fin, 0, nat);
5597145522Sdarrenr
5598145522Sdarrenr	/* ------------------------------------------------------------- */
5599255332Scy	/* A few quick notes:                                            */
5600255332Scy	/*      Following are test conditions prior to calling the       */
5601255332Scy	/*      ipf_proxy_check routine.                                 */
5602255332Scy	/*                                                               */
5603255332Scy	/*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
5604255332Scy	/*      with a map rule, we attempt to match the packet's        */
5605255332Scy	/*      source port against in_dport, otherwise we'd compare the */
5606255332Scy	/*      packet's destination.                                    */
5607145522Sdarrenr	/* ------------------------------------------------------------- */
5608145522Sdarrenr		if (np->in_apr != NULL) {
5609255332Scy			i = ipf_proxy_check(fin, nat);
561060852Sdarrenr			if (i == -1) {
5611255332Scy				NBUMPSIDED(0, ns_ipf_proxy_fail);
5612145522Sdarrenr				return -1;
561360852Sdarrenr			}
561460852Sdarrenr		}
5615145522Sdarrenr	}
561653642Sguido
5617255332Scy	ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
5618145522Sdarrenr
5619255332Scy	ipsumd = nat->nat_ipsumd;
5620145522Sdarrenr	/*
5621145522Sdarrenr	 * Fix up checksums, not by recalculating them, but
5622145522Sdarrenr	 * simply computing adjustments.
5623145522Sdarrenr	 * Why only do this for some platforms on inbound packets ?
5624145522Sdarrenr	 * Because for those that it is done, IP processing is yet to happen
5625145522Sdarrenr	 * and so the IPv4 header checksum has not yet been evaluated.
5626145522Sdarrenr	 * Perhaps it should always be done for the benefit of things like
5627145522Sdarrenr	 * fast forwarding (so that it doesn't need to be recomputed) but with
5628145522Sdarrenr	 * header checksum offloading, perhaps it is a moot point.
5629145522Sdarrenr	 */
5630255332Scy
5631255332Scy	switch (nat->nat_dir)
5632255332Scy	{
5633255332Scy	case NAT_INBOUND :
5634255332Scy		if ((fin->fin_flx & FI_ICMPERR) == 0) {
5635255332Scy			fin->fin_ip->ip_src = nat->nat_nsrcip;
5636255332Scy			fin->fin_saddr = nat->nat_nsrcaddr;
5637255332Scy		} else {
5638255332Scy			sum1 = nat->nat_osrcaddr;
5639255332Scy			sum2 = nat->nat_nsrcaddr;
5640255332Scy			CALC_SUMD(sum1, sum2, sumd);
5641255332Scy			ipsumd -= sumd;
5642255332Scy		}
5643255332Scy		fin->fin_ip->ip_dst = nat->nat_ndstip;
5644255332Scy		fin->fin_daddr = nat->nat_ndstaddr;
5645145522Sdarrenr#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5646145522Sdarrenr     defined(__osf__) || defined(linux)
5647255332Scy		ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0);
5648145522Sdarrenr#endif
5649255332Scy		break;
5650145522Sdarrenr
5651255332Scy	case NAT_OUTBOUND :
5652255332Scy		if ((fin->fin_flx & FI_ICMPERR) == 0) {
5653255332Scy			fin->fin_ip->ip_src = nat->nat_odstip;
5654255332Scy			fin->fin_saddr = nat->nat_odstaddr;
5655255332Scy		} else {
5656255332Scy			sum1 = nat->nat_odstaddr;
5657255332Scy			sum2 = nat->nat_ndstaddr;
5658255332Scy			CALC_SUMD(sum1, sum2, sumd);
5659255332Scy			ipsumd -= sumd;
5660255332Scy		}
5661255332Scy		fin->fin_ip->ip_dst = nat->nat_osrcip;
5662255332Scy		fin->fin_daddr = nat->nat_osrcaddr;
5663255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5664255332Scy     defined(__osf__) || defined(linux)
5665255332Scy		ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0);
5666255332Scy#endif
5667255332Scy		break;
5668255332Scy
5669255332Scy	case NAT_DIVERTIN :
5670255332Scy	    {
5671255332Scy		udphdr_t *uh;
5672255332Scy		ip_t *ip;
5673255332Scy		mb_t *m;
5674255332Scy
5675255332Scy		m = M_DUP(np->in_divmp);
5676255332Scy		if (m == NULL) {
5677255332Scy			NBUMPSIDED(0, ns_divert_dup);
5678255332Scy			return -1;
5679255332Scy		}
5680255332Scy
5681255332Scy		ip = MTOD(m, ip_t *);
5682255332Scy		ip->ip_id = htons(ipf_nextipid(fin));
5683255332Scy		sum1 = ntohs(ip->ip_len);
5684255332Scy		ip->ip_len = ntohs(ip->ip_len);
5685255332Scy		ip->ip_len += fin->fin_plen;
5686255332Scy		ip->ip_len = htons(ip->ip_len);
5687255332Scy
5688255332Scy		uh = (udphdr_t *)(ip + 1);
5689255332Scy		uh->uh_ulen += fin->fin_plen;
5690255332Scy		uh->uh_ulen = htons(uh->uh_ulen);
5691255332Scy
5692255332Scy		sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len);
5693255332Scy		sum2 += ntohs(ip->ip_off) & IP_DF;
5694255332Scy		CALC_SUMD(sum1, sum2, sumd);
5695255332Scy
5696255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
5697255332Scy     defined(__osf__) || defined(linux)
5698255332Scy		ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0);
5699255332Scy#endif
5700255332Scy		PREP_MB_T(fin, m);
5701255332Scy
5702255332Scy		fin->fin_ip = ip;
5703255332Scy		fin->fin_plen += sizeof(ip_t) + 8;	/* UDP + new IPv4 hdr */
5704255332Scy		fin->fin_dlen += sizeof(ip_t) + 8;	/* UDP + old IPv4 hdr */
5705255332Scy
5706255332Scy		nflags &= ~IPN_TCPUDPICMP;
5707255332Scy
5708255332Scy		break;
5709255332Scy	    }
5710255332Scy
5711255332Scy	case NAT_DIVERTOUT :
5712255332Scy	    {
5713255332Scy		mb_t *m;
5714255332Scy
5715255332Scy		skip = ipf_nat_decap(fin, nat);
5716255332Scy		if (skip <= 0) {
5717255332Scy			NBUMPSIDED(0, ns_decap_fail);
5718255332Scy			return -1;
5719255332Scy		}
5720255332Scy
5721255332Scy		m = fin->fin_m;
5722255332Scy
5723255332Scy#if defined(MENTAT) && defined(_KERNEL)
5724255332Scy		m->b_rptr += skip;
5725255332Scy#else
5726255332Scy		m->m_data += skip;
5727255332Scy		m->m_len -= skip;
5728255332Scy
5729255332Scy# ifdef M_PKTHDR
5730255332Scy		if (m->m_flags & M_PKTHDR)
5731255332Scy			m->m_pkthdr.len -= skip;
5732255332Scy# endif
5733255332Scy#endif
5734255332Scy
5735255332Scy		ipf_nat_update(fin, nat);
5736255332Scy		nflags &= ~IPN_TCPUDPICMP;
5737255332Scy		fin->fin_flx |= FI_NATED;
5738255332Scy		if (np != NULL && np->in_tag.ipt_num[0] != 0)
5739255332Scy			fin->fin_nattag = &np->in_tag;
5740255332Scy		return 1;
5741255332Scy		/* NOTREACHED */
5742255332Scy	    }
5743255332Scy	}
5744255332Scy	if (nflags & IPN_TCPUDP)
5745255332Scy		tcp = fin->fin_dp;
5746255332Scy
5747145522Sdarrenr	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
5748255332Scy		u_short *csump;
5749255332Scy
5750255332Scy		if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) {
5751255332Scy			switch (nat->nat_dir)
5752255332Scy			{
5753255332Scy			case NAT_INBOUND :
5754255332Scy				tcp->th_sport = nat->nat_nsport;
5755255332Scy				fin->fin_data[0] = ntohs(nat->nat_nsport);
5756255332Scy				tcp->th_dport = nat->nat_ndport;
5757255332Scy				fin->fin_data[1] = ntohs(nat->nat_ndport);
5758255332Scy				break;
5759255332Scy
5760255332Scy			case NAT_OUTBOUND :
5761255332Scy				tcp->th_sport = nat->nat_odport;
5762255332Scy				fin->fin_data[0] = ntohs(nat->nat_odport);
5763255332Scy				tcp->th_dport = nat->nat_osport;
5764255332Scy				fin->fin_data[1] = ntohs(nat->nat_osport);
5765255332Scy				break;
5766255332Scy			}
576792685Sdarrenr		}
576853642Sguido
5769145522Sdarrenr
5770255332Scy		if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) {
5771145522Sdarrenr			icmp = fin->fin_dp;
5772145522Sdarrenr
5773255332Scy			icmp->icmp_id = nat->nat_nicmpid;
5774145522Sdarrenr		}
5775145522Sdarrenr
5776255332Scy		csump = ipf_nat_proto(fin, nat, nflags);
5777255332Scy
5778255332Scy		/*
5779255332Scy		 * The above comments do not hold for layer 4 (or higher)
5780255332Scy		 * checksums...
5781255332Scy		 */
5782255332Scy		if (csump != NULL) {
5783255332Scy			if (nat->nat_dir == NAT_OUTBOUND)
5784255332Scy				ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0);
5785255332Scy			else
5786255332Scy				ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0);
5787255332Scy		}
5788145522Sdarrenr	}
5789145522Sdarrenr
5790145522Sdarrenr	fin->fin_flx |= FI_NATED;
5791145522Sdarrenr	if (np != NULL && np->in_tag.ipt_num[0] != 0)
5792145522Sdarrenr		fin->fin_nattag = &np->in_tag;
5793145522Sdarrenr	return 1;
5794145522Sdarrenr}
5795130886Sdarrenr
5796130886Sdarrenr
5797145522Sdarrenr/* ------------------------------------------------------------------------ */
5798255332Scy/* Function:    ipf_nat_proto                                               */
5799145522Sdarrenr/* Returns:     u_short* - pointer to transport header checksum to update,  */
5800145522Sdarrenr/*                         NULL if the transport protocol is not recognised */
5801145522Sdarrenr/*                         as needing a checksum update.                    */
5802145522Sdarrenr/* Parameters:  fin(I)    - pointer to packet information                   */
5803145522Sdarrenr/*              nat(I)    - pointer to NAT structure                        */
5804145522Sdarrenr/*              nflags(I) - NAT flags set for this packet                   */
5805145522Sdarrenr/*                                                                          */
5806145522Sdarrenr/* Return the pointer to the checksum field for each protocol so understood.*/
5807145522Sdarrenr/* If support for making other changes to a protocol header is required,    */
5808145522Sdarrenr/* that is not strictly 'address' translation, such as clamping the MSS in  */
5809145522Sdarrenr/* TCP down to a specific value, then do it from here.                      */
5810145522Sdarrenr/* ------------------------------------------------------------------------ */
5811255332Scyu_short *
5812255332Scyipf_nat_proto(fin, nat, nflags)
5813255332Scy	fr_info_t *fin;
5814255332Scy	nat_t *nat;
5815255332Scy	u_int nflags;
5816145522Sdarrenr{
5817145522Sdarrenr	icmphdr_t *icmp;
5818145522Sdarrenr	u_short *csump;
5819145522Sdarrenr	tcphdr_t *tcp;
5820145522Sdarrenr	udphdr_t *udp;
582153642Sguido
5822145522Sdarrenr	csump = NULL;
5823145522Sdarrenr	if (fin->fin_out == 0) {
5824255332Scy		fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND);
5825145522Sdarrenr	} else {
5826255332Scy		fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0);
5827145522Sdarrenr	}
582853642Sguido
5829145522Sdarrenr	switch (fin->fin_p)
5830145522Sdarrenr	{
5831145522Sdarrenr	case IPPROTO_TCP :
5832145522Sdarrenr		tcp = fin->fin_dp;
5833110916Sdarrenr
5834255332Scy		if ((nflags & IPN_TCP) != 0)
5835255332Scy			csump = &tcp->th_sum;
583653642Sguido
5837145522Sdarrenr		/*
5838145522Sdarrenr		 * Do a MSS CLAMPING on a SYN packet,
5839145522Sdarrenr		 * only deal IPv4 for now.
5840145522Sdarrenr		 */
5841145522Sdarrenr		if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0)
5842255332Scy			ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump);
584360852Sdarrenr
5844145522Sdarrenr		break;
5845145522Sdarrenr
5846145522Sdarrenr	case IPPROTO_UDP :
5847145522Sdarrenr		udp = fin->fin_dp;
5848145522Sdarrenr
5849255332Scy		if ((nflags & IPN_UDP) != 0) {
5850255332Scy			if (udp->uh_sum != 0)
5851255332Scy				csump = &udp->uh_sum;
5852255332Scy		}
5853145522Sdarrenr		break;
5854145522Sdarrenr
5855145522Sdarrenr	case IPPROTO_ICMP :
5856145522Sdarrenr		icmp = fin->fin_dp;
5857145522Sdarrenr
5858145522Sdarrenr		if ((nflags & IPN_ICMPQUERY) != 0) {
5859145522Sdarrenr			if (icmp->icmp_cksum != 0)
5860145522Sdarrenr				csump = &icmp->icmp_cksum;
586153642Sguido		}
5862145522Sdarrenr		break;
586353642Sguido
5864255332Scy#ifdef USE_INET6
5865255332Scy	case IPPROTO_ICMPV6 :
5866255332Scy	    {
5867255332Scy		struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp;
586853642Sguido
5869255332Scy		icmp6 = fin->fin_dp;
5870145522Sdarrenr
5871255332Scy		if ((nflags & IPN_ICMPQUERY) != 0) {
5872255332Scy			if (icmp6->icmp6_cksum != 0)
5873255332Scy				csump = &icmp6->icmp6_cksum;
5874255332Scy		}
5875255332Scy		break;
5876255332Scy	    }
5877255332Scy#endif
5878145522Sdarrenr	}
5879255332Scy	return csump;
588053642Sguido}
588153642Sguido
588253642Sguido
5883145522Sdarrenr/* ------------------------------------------------------------------------ */
5884255332Scy/* Function:    ipf_nat_expire                                              */
5885145522Sdarrenr/* Returns:     Nil                                                         */
5886255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
5887145522Sdarrenr/*                                                                          */
5888145522Sdarrenr/* Check all of the timeout queues for entries at the top which need to be  */
5889145522Sdarrenr/* expired.                                                                 */
5890145522Sdarrenr/* ------------------------------------------------------------------------ */
5891255332Scyvoid
5892255332Scyipf_nat_expire(softc)
5893255332Scy	ipf_main_softc_t *softc;
589453642Sguido{
5895255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5896145522Sdarrenr	ipftq_t *ifq, *ifqnext;
5897145522Sdarrenr	ipftqent_t *tqe, *tqn;
5898161356Sguido	int i;
5899153876Sguido	SPL_INT(s);
590053642Sguido
590153642Sguido	SPL_NET(s);
5902255332Scy	WRITE_ENTER(&softc->ipf_nat);
5903255332Scy	for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL;
5904255332Scy	     ifq = ifq->ifq_next) {
5905145522Sdarrenr		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
5906255332Scy			if (tqe->tqe_die > softc->ipf_ticks)
5907145522Sdarrenr				break;
5908145522Sdarrenr			tqn = tqe->tqe_next;
5909255332Scy			ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE);
591053642Sguido		}
591153642Sguido	}
5912145522Sdarrenr
5913255332Scy	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) {
5914145522Sdarrenr		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
5915255332Scy			if (tqe->tqe_die > softc->ipf_ticks)
5916145522Sdarrenr				break;
5917145522Sdarrenr			tqn = tqe->tqe_next;
5918255332Scy			ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE);
5919145522Sdarrenr		}
5920145522Sdarrenr	}
5921145522Sdarrenr
5922255332Scy	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) {
5923145522Sdarrenr		ifqnext = ifq->ifq_next;
5924145522Sdarrenr
5925145522Sdarrenr		if (((ifq->ifq_flags & IFQF_DELETE) != 0) &&
5926145522Sdarrenr		    (ifq->ifq_ref == 0)) {
5927255332Scy			ipf_freetimeoutqueue(softc, ifq);
5928145522Sdarrenr		}
5929145522Sdarrenr	}
5930145522Sdarrenr
5931255332Scy	if (softn->ipf_nat_doflush != 0) {
5932255332Scy		ipf_nat_extraflush(softc, softn, 2);
5933255332Scy		softn->ipf_nat_doflush = 0;
5934170268Sdarrenr	}
5935170268Sdarrenr
5936255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
593753642Sguido	SPL_X(s);
593853642Sguido}
593953642Sguido
594053642Sguido
5941145522Sdarrenr/* ------------------------------------------------------------------------ */
5942255332Scy/* Function:    ipf_nat_sync                                                */
5943145522Sdarrenr/* Returns:     Nil                                                         */
5944255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
5945255332Scy/*              ifp(I) - pointer to network interface                       */
5946145522Sdarrenr/*                                                                          */
5947145522Sdarrenr/* Walk through all of the currently active NAT sessions, looking for those */
5948145522Sdarrenr/* which need to have their translated address updated.                     */
5949145522Sdarrenr/* ------------------------------------------------------------------------ */
5950255332Scyvoid
5951255332Scyipf_nat_sync(softc, ifp)
5952255332Scy	ipf_main_softc_t *softc;
5953255332Scy	void *ifp;
595453642Sguido{
5955255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
5956145522Sdarrenr	u_32_t sum1, sum2, sumd;
5957255332Scy	i6addr_t in;
5958145522Sdarrenr	ipnat_t *n;
5959145522Sdarrenr	nat_t *nat;
596053642Sguido	void *ifp2;
5961255332Scy	int idx;
5962153876Sguido	SPL_INT(s);
596353642Sguido
5964255332Scy	if (softc->ipf_running <= 0)
5965145522Sdarrenr		return;
5966145522Sdarrenr
596753642Sguido	/*
596853642Sguido	 * Change IP addresses for NAT sessions for any protocol except TCP
5969145522Sdarrenr	 * since it will break the TCP connection anyway.  The only rules
5970145522Sdarrenr	 * which will get changed are those which are "map ... -> 0/32",
5971145522Sdarrenr	 * where the rule specifies the address is taken from the interface.
597253642Sguido	 */
597353642Sguido	SPL_NET(s);
5974255332Scy	WRITE_ENTER(&softc->ipf_nat);
5975145522Sdarrenr
5976255332Scy	if (softc->ipf_running <= 0) {
5977255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
5978145522Sdarrenr		return;
5979145522Sdarrenr	}
5980145522Sdarrenr
5981255332Scy	for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) {
5982145522Sdarrenr		if ((nat->nat_flags & IPN_TCP) != 0)
5983145522Sdarrenr			continue;
5984255332Scy
5985145522Sdarrenr		n = nat->nat_ptr;
5986255332Scy		if (n != NULL) {
5987255332Scy			if (n->in_v[1] == 4) {
5988255332Scy				if (n->in_redir & NAT_MAP) {
5989255332Scy					if ((n->in_nsrcaddr != 0) ||
5990255332Scy					    (n->in_nsrcmsk != 0xffffffff))
5991255332Scy						continue;
5992255332Scy				} else if (n->in_redir & NAT_REDIRECT) {
5993255332Scy					if ((n->in_ndstaddr != 0) ||
5994255332Scy					    (n->in_ndstmsk != 0xffffffff))
5995255332Scy						continue;
5996255332Scy				}
5997255332Scy			}
5998255332Scy#ifdef USE_INET6
5999255332Scy			if (n->in_v[1] == 4) {
6000255332Scy				if (n->in_redir & NAT_MAP) {
6001255332Scy					if (!IP6_ISZERO(&n->in_nsrcaddr) ||
6002255332Scy					    !IP6_ISONES(&n->in_nsrcmsk))
6003255332Scy						continue;
6004255332Scy				} else if (n->in_redir & NAT_REDIRECT) {
6005255332Scy					if (!IP6_ISZERO(&n->in_ndstaddr) ||
6006255332Scy					    !IP6_ISONES(&n->in_ndstmsk))
6007255332Scy						continue;
6008255332Scy				}
6009255332Scy			}
6010255332Scy#endif
6011255332Scy		}
6012255332Scy
6013145522Sdarrenr		if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) ||
6014145522Sdarrenr		     (ifp == nat->nat_ifps[1]))) {
6015255332Scy			nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0],
6016255332Scy						  nat->nat_v[0]);
6017255332Scy			if ((nat->nat_ifps[0] != NULL) &&
6018255332Scy			    (nat->nat_ifps[0] != (void *)-1)) {
6019255332Scy				nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
6020255332Scy			}
6021145522Sdarrenr			if (nat->nat_ifnames[1][0] != '\0') {
6022145522Sdarrenr				nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1],
6023255332Scy							  nat->nat_v[1]);
6024255332Scy			} else {
6025145522Sdarrenr				nat->nat_ifps[1] = nat->nat_ifps[0];
6026255332Scy			}
6027255332Scy			if ((nat->nat_ifps[1] != NULL) &&
6028255332Scy			    (nat->nat_ifps[1] != (void *)-1)) {
6029255332Scy				nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
6030255332Scy			}
6031145522Sdarrenr			ifp2 = nat->nat_ifps[0];
6032145522Sdarrenr			if (ifp2 == NULL)
6033145522Sdarrenr				continue;
6034145522Sdarrenr
603553642Sguido			/*
603653642Sguido			 * Change the map-to address to be the same as the
603753642Sguido			 * new one.
603853642Sguido			 */
6039255332Scy			sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6);
6040255332Scy			if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2,
6041255332Scy				       &in, NULL) != -1) {
6042255332Scy				if (nat->nat_v[0] == 4)
6043255332Scy					nat->nat_nsrcip = in.in4;
6044255332Scy			}
6045255332Scy			sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6);
604653642Sguido
604753642Sguido			if (sum1 == sum2)
604853642Sguido				continue;
604953642Sguido			/*
605053642Sguido			 * Readjust the checksum adjustment to take into
605153642Sguido			 * account the new IP#.
605253642Sguido			 */
605353642Sguido			CALC_SUMD(sum1, sum2, sumd);
605455929Sguido			/* XXX - dont change for TCP when solaris does
605555929Sguido			 * hardware checksumming.
605655929Sguido			 */
605755929Sguido			sumd += nat->nat_sumd[0];
605855929Sguido			nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
605955929Sguido			nat->nat_sumd[1] = nat->nat_sumd[0];
606053642Sguido		}
6061145522Sdarrenr	}
606253642Sguido
6063255332Scy	for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) {
6064255332Scy		char *base = n->in_names;
6065255332Scy
6066145522Sdarrenr		if ((ifp == NULL) || (n->in_ifps[0] == ifp))
6067255332Scy			n->in_ifps[0] = ipf_resolvenic(softc,
6068255332Scy						       base + n->in_ifnames[0],
6069255332Scy						       n->in_v[0]);
6070145522Sdarrenr		if ((ifp == NULL) || (n->in_ifps[1] == ifp))
6071255332Scy			n->in_ifps[1] = ipf_resolvenic(softc,
6072255332Scy						       base + n->in_ifnames[1],
6073255332Scy						       n->in_v[1]);
6074255332Scy
6075255332Scy		if (n->in_redir & NAT_REDIRECT)
6076255332Scy			idx = 1;
6077255332Scy		else
6078255332Scy			idx = 0;
6079255332Scy
6080255332Scy		if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) &&
6081255332Scy		    (n->in_ifps[idx] != NULL &&
6082255332Scy		     n->in_ifps[idx] != (void *)-1)) {
6083255332Scy
6084255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc,
6085255332Scy					     0, n->in_ifps[idx]);
6086255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst,
6087255332Scy					     0, n->in_ifps[idx]);
6088255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc,
6089255332Scy					     0, n->in_ifps[idx]);
6090255332Scy			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst,
6091255332Scy					     0, n->in_ifps[idx]);
6092255332Scy		}
6093145522Sdarrenr	}
6094255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
609553642Sguido	SPL_X(s);
609653642Sguido}
609753642Sguido
609853642Sguido
6099145522Sdarrenr/* ------------------------------------------------------------------------ */
6100255332Scy/* Function:    ipf_nat_icmpquerytype                                       */
6101145522Sdarrenr/* Returns:     int - 1 == success, 0 == failure                            */
6102145522Sdarrenr/* Parameters:  icmptype(I) - ICMP type number                              */
6103145522Sdarrenr/*                                                                          */
6104145522Sdarrenr/* Tests to see if the ICMP type number passed is a query/response type or  */
6105145522Sdarrenr/* not.                                                                     */
6106145522Sdarrenr/* ------------------------------------------------------------------------ */
6107255332Scystatic int
6108255332Scyipf_nat_icmpquerytype(icmptype)
6109255332Scy	int icmptype;
6110145522Sdarrenr{
6111145522Sdarrenr
6112145522Sdarrenr	/*
6113145522Sdarrenr	 * For the ICMP query NAT code, it is essential that both the query
6114145522Sdarrenr	 * and the reply match on the NAT rule. Because the NAT structure
6115145522Sdarrenr	 * does not keep track of the icmptype, and a single NAT structure
6116145522Sdarrenr	 * is used for all icmp types with the same src, dest and id, we
6117145522Sdarrenr	 * simply define the replies as queries as well. The funny thing is,
6118145522Sdarrenr	 * altough it seems silly to call a reply a query, this is exactly
6119145522Sdarrenr	 * as it is defined in the IPv4 specification
6120145522Sdarrenr	 */
6121145522Sdarrenr	switch (icmptype)
6122145522Sdarrenr	{
6123145522Sdarrenr	case ICMP_ECHOREPLY:
6124145522Sdarrenr	case ICMP_ECHO:
6125324513Scy	/* route advertisement/solicitation is currently unsupported: */
6126324513Scy	/* it would require rewriting the ICMP data section          */
6127145522Sdarrenr	case ICMP_TSTAMP:
6128145522Sdarrenr	case ICMP_TSTAMPREPLY:
6129145522Sdarrenr	case ICMP_IREQ:
6130145522Sdarrenr	case ICMP_IREQREPLY:
6131145522Sdarrenr	case ICMP_MASKREQ:
6132145522Sdarrenr	case ICMP_MASKREPLY:
6133145522Sdarrenr		return 1;
6134145522Sdarrenr	default:
6135145522Sdarrenr		return 0;
6136145522Sdarrenr	}
6137145522Sdarrenr}
6138145522Sdarrenr
6139145522Sdarrenr
6140145522Sdarrenr/* ------------------------------------------------------------------------ */
6141145522Sdarrenr/* Function:    nat_log                                                     */
6142145522Sdarrenr/* Returns:     Nil                                                         */
6143255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6144255332Scy/*              softn(I) - pointer to NAT context structure                 */
6145255332Scy/*              nat(I)    - pointer to NAT structure                        */
6146255332Scy/*              action(I) - action related to NAT structure being performed */
6147145522Sdarrenr/*                                                                          */
6148145522Sdarrenr/* Creates a NAT log entry.                                                 */
6149145522Sdarrenr/* ------------------------------------------------------------------------ */
6150255332Scyvoid
6151255332Scyipf_nat_log(softc, softn, nat, action)
6152255332Scy	ipf_main_softc_t *softc;
6153255332Scy	ipf_nat_softc_t *softn;
6154255332Scy	struct nat *nat;
6155255332Scy	u_int action;
615653642Sguido{
6157145522Sdarrenr#ifdef	IPFILTER_LOG
6158139005Smlaier# ifndef LARGE_NAT
615953642Sguido	struct ipnat *np;
6160138979Sdarrenr	int rulen;
6161138979Sdarrenr# endif
616253642Sguido	struct natlog natl;
616353642Sguido	void *items[1];
616453642Sguido	size_t sizes[1];
6165138979Sdarrenr	int types[1];
616653642Sguido
6167255332Scy	bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip,
6168255332Scy	      sizeof(natl.nl_osrcip));
6169255332Scy	bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip,
6170255332Scy	      sizeof(natl.nl_nsrcip));
6171255332Scy	bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip,
6172255332Scy	      sizeof(natl.nl_odstip));
6173255332Scy	bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip,
6174255332Scy	      sizeof(natl.nl_ndstip));
6175255332Scy
6176145522Sdarrenr	natl.nl_bytes[0] = nat->nat_bytes[0];
6177145522Sdarrenr	natl.nl_bytes[1] = nat->nat_bytes[1];
6178145522Sdarrenr	natl.nl_pkts[0] = nat->nat_pkts[0];
6179145522Sdarrenr	natl.nl_pkts[1] = nat->nat_pkts[1];
6180255332Scy	natl.nl_odstport = nat->nat_odport;
6181255332Scy	natl.nl_osrcport = nat->nat_osport;
6182255332Scy	natl.nl_nsrcport = nat->nat_nsport;
6183255332Scy	natl.nl_ndstport = nat->nat_ndport;
6184255332Scy	natl.nl_p[0] = nat->nat_pr[0];
6185255332Scy	natl.nl_p[1] = nat->nat_pr[1];
6186255332Scy	natl.nl_v[0] = nat->nat_v[0];
6187255332Scy	natl.nl_v[1] = nat->nat_v[1];
6188255332Scy	natl.nl_type = nat->nat_redir;
6189255332Scy	natl.nl_action = action;
619053642Sguido	natl.nl_rule = -1;
6191255332Scy
6192255332Scy	bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0],
6193255332Scy	      sizeof(nat->nat_ifnames[0]));
6194255332Scy	bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1],
6195255332Scy	      sizeof(nat->nat_ifnames[1]));
6196255332Scy
6197145522Sdarrenr# ifndef LARGE_NAT
619853642Sguido	if (nat->nat_ptr != NULL) {
6199255332Scy		for (rulen = 0, np = softn->ipf_nat_list; np != NULL;
6200255332Scy		     np = np->in_next, rulen++)
620153642Sguido			if (np == nat->nat_ptr) {
620253642Sguido				natl.nl_rule = rulen;
620353642Sguido				break;
620453642Sguido			}
620553642Sguido	}
6206145522Sdarrenr# endif
620753642Sguido	items[0] = &natl;
620853642Sguido	sizes[0] = sizeof(natl);
620953642Sguido	types[0] = 0;
621053642Sguido
6211255332Scy	(void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1);
6212145522Sdarrenr#endif
621353642Sguido}
621492685Sdarrenr
621592685Sdarrenr
621692685Sdarrenr#if defined(__OpenBSD__)
6217145522Sdarrenr/* ------------------------------------------------------------------------ */
6218255332Scy/* Function:    ipf_nat_ifdetach                                            */
6219145522Sdarrenr/* Returns:     Nil                                                         */
6220145522Sdarrenr/* Parameters:  ifp(I) - pointer to network interface                       */
6221145522Sdarrenr/*                                                                          */
6222145522Sdarrenr/* Compatibility interface for OpenBSD to trigger the correct updating of   */
6223145522Sdarrenr/* interface references within IPFilter.                                    */
6224145522Sdarrenr/* ------------------------------------------------------------------------ */
6225255332Scyvoid
6226255332Scyipf_nat_ifdetach(ifp)
6227255332Scy	void *ifp;
622892685Sdarrenr{
6229255332Scy	ipf_main_softc_t *softc;
6230255332Scy
6231255332Scy	softc = ipf_get_softc(0);
6232255332Scy
6233255332Scy	ipf_sync(ifp);
623492685Sdarrenr	return;
623592685Sdarrenr}
623692685Sdarrenr#endif
6237110916Sdarrenr
6238110916Sdarrenr
6239145522Sdarrenr/* ------------------------------------------------------------------------ */
6240255332Scy/* Function:    ipf_nat_rule_deref                                          */
6241170268Sdarrenr/* Returns:     Nil                                                         */
6242255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6243255332Scy/*              inp(I)   - pointer to pointer to NAT rule                   */
6244170268Sdarrenr/* Write Locks: ipf_nat                                                     */
6245170268Sdarrenr/*                                                                          */
6246255332Scy/* Dropping the refernce count for a rule means that whatever held the      */
6247255332Scy/* pointer to this rule (*inp) is no longer interested in it and when the   */
6248255332Scy/* reference count drops to zero, any resources allocated for the rule can  */
6249255332Scy/* be released and the rule itself free'd.                                  */
6250170268Sdarrenr/* ------------------------------------------------------------------------ */
6251255332Scyvoid
6252255332Scyipf_nat_rule_deref(softc, inp)
6253255332Scy	ipf_main_softc_t *softc;
6254255332Scy	ipnat_t **inp;
6255170268Sdarrenr{
6256255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6257255332Scy	ipnat_t *n;
6258170268Sdarrenr
6259255332Scy	n = *inp;
6260170268Sdarrenr	*inp = NULL;
6261255332Scy	n->in_use--;
6262255332Scy	if (n->in_use > 0)
6263255332Scy		return;
6264255332Scy
6265255332Scy	if (n->in_apr != NULL)
6266255332Scy		ipf_proxy_deref(n->in_apr);
6267255332Scy
6268255332Scy	ipf_nat_rule_fini(softc, n);
6269255332Scy
6270255332Scy	if (n->in_redir & NAT_REDIRECT) {
6271255332Scy		if ((n->in_flags & IPN_PROXYRULE) == 0) {
6272255332Scy			ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr);
6273255332Scy		}
6274255332Scy	}
6275255332Scy	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
6276255332Scy		if ((n->in_flags & IPN_PROXYRULE) == 0) {
6277255332Scy			ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map);
6278255332Scy		}
6279255332Scy	}
6280255332Scy
6281255332Scy	if (n->in_tqehead[0] != NULL) {
6282255332Scy		if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) {
6283255332Scy			ipf_freetimeoutqueue(softc, n->in_tqehead[1]);
6284255332Scy		}
6285255332Scy	}
6286255332Scy
6287255332Scy	if (n->in_tqehead[1] != NULL) {
6288255332Scy		if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) {
6289255332Scy			ipf_freetimeoutqueue(softc, n->in_tqehead[1]);
6290255332Scy		}
6291255332Scy	}
6292255332Scy
6293255332Scy	if ((n->in_flags & IPN_PROXYRULE) == 0) {
6294255332Scy		ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules);
6295255332Scy	}
6296255332Scy
6297255332Scy	MUTEX_DESTROY(&n->in_lock);
6298255332Scy
6299255332Scy	KFREES(n, n->in_size);
6300255332Scy
6301255332Scy#if SOLARIS && !defined(INSTANCES)
6302255332Scy	if (softn->ipf_nat_stats.ns_rules == 0)
6303255332Scy		pfil_delayed_copy = 1;
6304170268Sdarrenr#endif
6305170268Sdarrenr}
6306170268Sdarrenr
6307170268Sdarrenr
6308170268Sdarrenr/* ------------------------------------------------------------------------ */
6309255332Scy/* Function:    ipf_nat_deref                                               */
6310145522Sdarrenr/* Returns:     Nil                                                         */
6311255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6312255332Scy/*              natp(I)  - pointer to pointer to NAT table entry            */
6313145522Sdarrenr/*                                                                          */
6314145522Sdarrenr/* Decrement the reference counter for this NAT table entry and free it if  */
6315145522Sdarrenr/* there are no more things using it.                                       */
6316172776Sdarrenr/*                                                                          */
6317172776Sdarrenr/* IF nat_ref == 1 when this function is called, then we have an orphan nat */
6318172776Sdarrenr/* structure *because* it only gets called on paths _after_ nat_ref has been*/
6319172776Sdarrenr/* incremented.  If nat_ref == 1 then we shouldn't decrement it here        */
6320172776Sdarrenr/* because nat_delete() will do that and send nat_ref to -1.                */
6321172776Sdarrenr/*                                                                          */
6322172776Sdarrenr/* Holding the lock on nat_lock is required to serialise nat_delete() being */
6323172776Sdarrenr/* called from a NAT flush ioctl with a deref happening because of a packet.*/
6324145522Sdarrenr/* ------------------------------------------------------------------------ */
6325255332Scyvoid
6326255332Scyipf_nat_deref(softc, natp)
6327255332Scy	ipf_main_softc_t *softc;
6328255332Scy	nat_t **natp;
6329145522Sdarrenr{
6330145522Sdarrenr	nat_t *nat;
6331145522Sdarrenr
6332145522Sdarrenr	nat = *natp;
6333145522Sdarrenr	*natp = NULL;
6334172776Sdarrenr
6335172776Sdarrenr	MUTEX_ENTER(&nat->nat_lock);
6336172776Sdarrenr	if (nat->nat_ref > 1) {
6337172776Sdarrenr		nat->nat_ref--;
6338255332Scy		ASSERT(nat->nat_ref >= 0);
6339172776Sdarrenr		MUTEX_EXIT(&nat->nat_lock);
6340172776Sdarrenr		return;
6341172776Sdarrenr	}
6342172776Sdarrenr	MUTEX_EXIT(&nat->nat_lock);
6343172776Sdarrenr
6344255332Scy	WRITE_ENTER(&softc->ipf_nat);
6345255332Scy	ipf_nat_delete(softc, nat, NL_EXPIRE);
6346255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
6347145522Sdarrenr}
6348145522Sdarrenr
6349145522Sdarrenr
6350145522Sdarrenr/* ------------------------------------------------------------------------ */
6351255332Scy/* Function:    ipf_nat_clone                                               */
6352145522Sdarrenr/* Returns:     ipstate_t* - NULL == cloning failed,                        */
6353145522Sdarrenr/*                           else pointer to new state structure            */
6354145522Sdarrenr/* Parameters:  fin(I) - pointer to packet information                      */
6355145522Sdarrenr/*              is(I)  - pointer to master state structure                  */
6356145522Sdarrenr/* Write Lock:  ipf_nat                                                     */
6357145522Sdarrenr/*                                                                          */
6358145522Sdarrenr/* Create a "duplcate" state table entry from the master.                   */
6359145522Sdarrenr/* ------------------------------------------------------------------------ */
6360255332Scynat_t *
6361255332Scyipf_nat_clone(fin, nat)
6362255332Scy	fr_info_t *fin;
6363255332Scy	nat_t *nat;
6364145522Sdarrenr{
6365255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
6366255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6367145522Sdarrenr	frentry_t *fr;
6368145522Sdarrenr	nat_t *clone;
6369145522Sdarrenr	ipnat_t *np;
6370145522Sdarrenr
6371145522Sdarrenr	KMALLOC(clone, nat_t *);
6372255332Scy	if (clone == NULL) {
6373255332Scy		NBUMPSIDED(fin->fin_out, ns_clone_nomem);
6374145522Sdarrenr		return NULL;
6375255332Scy	}
6376145522Sdarrenr	bcopy((char *)nat, (char *)clone, sizeof(*clone));
6377145522Sdarrenr
6378145522Sdarrenr	MUTEX_NUKE(&clone->nat_lock);
6379145522Sdarrenr
6380255332Scy	clone->nat_rev = fin->fin_rev;
6381153876Sguido	clone->nat_aps = NULL;
6382153876Sguido	/*
6383255332Scy	 * Initialize all these so that ipf_nat_delete() doesn't cause a crash.
6384153876Sguido	 */
6385153876Sguido	clone->nat_tqe.tqe_pnext = NULL;
6386153876Sguido	clone->nat_tqe.tqe_next = NULL;
6387153876Sguido	clone->nat_tqe.tqe_ifq = NULL;
6388153876Sguido	clone->nat_tqe.tqe_parent = clone;
6389153876Sguido
6390145522Sdarrenr	clone->nat_flags &= ~SI_CLONE;
6391145522Sdarrenr	clone->nat_flags |= SI_CLONED;
6392145522Sdarrenr
6393153876Sguido	if (clone->nat_hm)
6394153876Sguido		clone->nat_hm->hm_ref++;
6395145522Sdarrenr
6396255332Scy	if (ipf_nat_insert(softc, softn, clone) == -1) {
6397145522Sdarrenr		KFREE(clone);
6398255332Scy		NBUMPSIDED(fin->fin_out, ns_insert_fail);
6399145522Sdarrenr		return NULL;
6400145522Sdarrenr	}
6401255332Scy
6402145522Sdarrenr	np = clone->nat_ptr;
6403145522Sdarrenr	if (np != NULL) {
6404255332Scy		if (softn->ipf_nat_logging)
6405255332Scy			ipf_nat_log(softc, softn, clone, NL_CLONE);
6406145522Sdarrenr		np->in_use++;
6407145522Sdarrenr	}
6408145522Sdarrenr	fr = clone->nat_fr;
6409145522Sdarrenr	if (fr != NULL) {
6410145522Sdarrenr		MUTEX_ENTER(&fr->fr_lock);
6411145522Sdarrenr		fr->fr_ref++;
6412145522Sdarrenr		MUTEX_EXIT(&fr->fr_lock);
6413145522Sdarrenr	}
6414145522Sdarrenr
6415255332Scy
6416145522Sdarrenr	/*
6417145522Sdarrenr	 * Because the clone is created outside the normal loop of things and
6418145522Sdarrenr	 * TCP has special needs in terms of state, initialise the timeout
6419145522Sdarrenr	 * state of the new NAT from here.
6420145522Sdarrenr	 */
6421255332Scy	if (clone->nat_pr[0] == IPPROTO_TCP) {
6422255332Scy		(void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq,
6423255332Scy				   clone->nat_flags, 2);
6424145522Sdarrenr	}
6425255332Scy	clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone);
6426255332Scy	if (softn->ipf_nat_logging)
6427255332Scy		ipf_nat_log(softc, softn, clone, NL_CLONE);
6428145522Sdarrenr	return clone;
6429145522Sdarrenr}
6430145522Sdarrenr
6431145522Sdarrenr
6432145522Sdarrenr/* ------------------------------------------------------------------------ */
6433255332Scy/* Function:   ipf_nat_wildok                                               */
6434145522Sdarrenr/* Returns:    int - 1 == packet's ports match wildcards                    */
6435145522Sdarrenr/*                   0 == packet's ports don't match wildcards              */
6436145522Sdarrenr/* Parameters: nat(I)   - NAT entry                                         */
6437145522Sdarrenr/*             sport(I) - source port                                       */
6438145522Sdarrenr/*             dport(I) - destination port                                  */
6439145522Sdarrenr/*             flags(I) - wildcard flags                                    */
6440145522Sdarrenr/*             dir(I)   - packet direction                                  */
6441145522Sdarrenr/*                                                                          */
6442145522Sdarrenr/* Use NAT entry and packet direction to determine which combination of     */
6443145522Sdarrenr/* wildcard flags should be used.                                           */
6444145522Sdarrenr/* ------------------------------------------------------------------------ */
6445255332Scyint
6446255332Scyipf_nat_wildok(nat, sport, dport, flags, dir)
6447255332Scy	nat_t *nat;
6448255332Scy	int sport, dport, flags, dir;
6449145522Sdarrenr{
6450145522Sdarrenr	/*
6451145522Sdarrenr	 * When called by       dir is set to
6452145522Sdarrenr	 * nat_inlookup         NAT_INBOUND (0)
6453145522Sdarrenr	 * nat_outlookup        NAT_OUTBOUND (1)
6454145522Sdarrenr	 *
6455145522Sdarrenr	 * We simply combine the packet's direction in dir with the original
6456145522Sdarrenr	 * "intended" direction of that NAT entry in nat->nat_dir to decide
6457145522Sdarrenr	 * which combination of wildcard flags to allow.
6458145522Sdarrenr	 */
6459255332Scy	switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)))
6460145522Sdarrenr	{
6461145522Sdarrenr	case 3: /* outbound packet / outbound entry */
6462255332Scy		if (((nat->nat_osport == sport) ||
6463145522Sdarrenr		    (flags & SI_W_SPORT)) &&
6464255332Scy		    ((nat->nat_odport == dport) ||
6465145522Sdarrenr		    (flags & SI_W_DPORT)))
6466145522Sdarrenr			return 1;
6467145522Sdarrenr		break;
6468145522Sdarrenr	case 2: /* outbound packet / inbound entry */
6469255332Scy		if (((nat->nat_osport == dport) ||
6470255332Scy		    (flags & SI_W_SPORT)) &&
6471255332Scy		    ((nat->nat_odport == sport) ||
6472255332Scy		    (flags & SI_W_DPORT)))
6473145522Sdarrenr			return 1;
6474145522Sdarrenr		break;
6475145522Sdarrenr	case 1: /* inbound packet / outbound entry */
6476255332Scy		if (((nat->nat_osport == dport) ||
6477255332Scy		    (flags & SI_W_SPORT)) &&
6478255332Scy		    ((nat->nat_odport == sport) ||
6479255332Scy		    (flags & SI_W_DPORT)))
6480145522Sdarrenr			return 1;
6481145522Sdarrenr		break;
6482145522Sdarrenr	case 0: /* inbound packet / inbound entry */
6483255332Scy		if (((nat->nat_osport == sport) ||
6484145522Sdarrenr		    (flags & SI_W_SPORT)) &&
6485255332Scy		    ((nat->nat_odport == dport) ||
6486145522Sdarrenr		    (flags & SI_W_DPORT)))
6487145522Sdarrenr			return 1;
6488145522Sdarrenr		break;
6489145522Sdarrenr	default:
6490145522Sdarrenr		break;
6491145522Sdarrenr	}
6492145522Sdarrenr
6493145522Sdarrenr	return(0);
6494145522Sdarrenr}
6495145522Sdarrenr
6496145522Sdarrenr
6497145522Sdarrenr/* ------------------------------------------------------------------------ */
6498145522Sdarrenr/* Function:    nat_mssclamp                                                */
6499145522Sdarrenr/* Returns:     Nil                                                         */
6500145522Sdarrenr/* Parameters:  tcp(I)    - pointer to TCP header                           */
6501145522Sdarrenr/*              maxmss(I) - value to clamp the TCP MSS to                   */
6502145522Sdarrenr/*              fin(I)    - pointer to packet information                   */
6503145522Sdarrenr/*              csump(I)  - pointer to TCP checksum                         */
6504145522Sdarrenr/*                                                                          */
6505145522Sdarrenr/* Check for MSS option and clamp it if necessary.  If found and changed,   */
6506145522Sdarrenr/* then the TCP header checksum will be updated to reflect the change in    */
6507145522Sdarrenr/* the MSS.                                                                 */
6508145522Sdarrenr/* ------------------------------------------------------------------------ */
6509255332Scystatic void
6510255332Scyipf_nat_mssclamp(tcp, maxmss, fin, csump)
6511255332Scy	tcphdr_t *tcp;
6512255332Scy	u_32_t maxmss;
6513255332Scy	fr_info_t *fin;
6514255332Scy	u_short *csump;
6515110916Sdarrenr{
6516110916Sdarrenr	u_char *cp, *ep, opt;
6517110916Sdarrenr	int hlen, advance;
6518110916Sdarrenr	u_32_t mss, sumd;
6519110916Sdarrenr
6520145522Sdarrenr	hlen = TCP_OFF(tcp) << 2;
6521110916Sdarrenr	if (hlen > sizeof(*tcp)) {
6522110916Sdarrenr		cp = (u_char *)tcp + sizeof(*tcp);
6523110916Sdarrenr		ep = (u_char *)tcp + hlen;
6524110916Sdarrenr
6525110916Sdarrenr		while (cp < ep) {
6526110916Sdarrenr			opt = cp[0];
6527110916Sdarrenr			if (opt == TCPOPT_EOL)
6528110916Sdarrenr				break;
6529110916Sdarrenr			else if (opt == TCPOPT_NOP) {
6530110916Sdarrenr				cp++;
6531110916Sdarrenr				continue;
6532110916Sdarrenr			}
6533145522Sdarrenr
6534145522Sdarrenr			if (cp + 1 >= ep)
6535110916Sdarrenr				break;
6536110916Sdarrenr			advance = cp[1];
6537145522Sdarrenr			if ((cp + advance > ep) || (advance <= 0))
6538110916Sdarrenr				break;
6539145522Sdarrenr			switch (opt)
6540145522Sdarrenr			{
6541110916Sdarrenr			case TCPOPT_MAXSEG:
6542110916Sdarrenr				if (advance != 4)
6543110916Sdarrenr					break;
6544145522Sdarrenr				mss = cp[2] * 256 + cp[3];
6545110916Sdarrenr				if (mss > maxmss) {
6546145522Sdarrenr					cp[2] = maxmss / 256;
6547145522Sdarrenr					cp[3] = maxmss & 0xff;
6548110916Sdarrenr					CALC_SUMD(mss, maxmss, sumd);
6549255332Scy					ipf_fix_outcksum(0, csump, sumd, 0);
6550110916Sdarrenr				}
6551110916Sdarrenr				break;
6552110916Sdarrenr			default:
6553110916Sdarrenr				/* ignore unknown options */
6554110916Sdarrenr				break;
6555110916Sdarrenr			}
6556145522Sdarrenr
6557145522Sdarrenr			cp += advance;
6558145522Sdarrenr		}
6559145522Sdarrenr	}
6560145522Sdarrenr}
6561145522Sdarrenr
6562145522Sdarrenr
6563145522Sdarrenr/* ------------------------------------------------------------------------ */
6564255332Scy/* Function:    ipf_nat_setqueue                                            */
6565145522Sdarrenr/* Returns:     Nil                                                         */
6566255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6567255332Scy/*              softn(I) - pointer to NAT context structure                 */
6568255332Scy/*              nat(I)- pointer to NAT structure                            */
6569145522Sdarrenr/* Locks:       ipf_nat (read or write)                                     */
6570145522Sdarrenr/*                                                                          */
6571145522Sdarrenr/* Put the NAT entry on its default queue entry, using rev as a helped in   */
6572145522Sdarrenr/* determining which queue it should be placed on.                          */
6573145522Sdarrenr/* ------------------------------------------------------------------------ */
6574255332Scyvoid
6575255332Scyipf_nat_setqueue(softc, softn, nat)
6576255332Scy	ipf_main_softc_t *softc;
6577255332Scy	ipf_nat_softc_t *softn;
6578255332Scy	nat_t *nat;
6579145522Sdarrenr{
6580145522Sdarrenr	ipftq_t *oifq, *nifq;
6581255332Scy	int rev = nat->nat_rev;
6582145522Sdarrenr
6583145522Sdarrenr	if (nat->nat_ptr != NULL)
6584145522Sdarrenr		nifq = nat->nat_ptr->in_tqehead[rev];
6585145522Sdarrenr	else
6586145522Sdarrenr		nifq = NULL;
6587145522Sdarrenr
6588145522Sdarrenr	if (nifq == NULL) {
6589255332Scy		switch (nat->nat_pr[0])
6590145522Sdarrenr		{
6591145522Sdarrenr		case IPPROTO_UDP :
6592255332Scy			nifq = &softn->ipf_nat_udptq;
6593145522Sdarrenr			break;
6594145522Sdarrenr		case IPPROTO_ICMP :
6595255332Scy			nifq = &softn->ipf_nat_icmptq;
6596145522Sdarrenr			break;
6597145522Sdarrenr		case IPPROTO_TCP :
6598255332Scy			nifq = softn->ipf_nat_tcptq +
6599255332Scy			       nat->nat_tqe.tqe_state[rev];
6600145522Sdarrenr			break;
6601145522Sdarrenr		default :
6602255332Scy			nifq = &softn->ipf_nat_iptq;
6603145522Sdarrenr			break;
6604145522Sdarrenr		}
6605145522Sdarrenr	}
6606145522Sdarrenr
6607145522Sdarrenr	oifq = nat->nat_tqe.tqe_ifq;
6608145522Sdarrenr	/*
6609145522Sdarrenr	 * If it's currently on a timeout queue, move it from one queue to
6610145522Sdarrenr	 * another, else put it on the end of the newly determined queue.
6611145522Sdarrenr	 */
6612145522Sdarrenr	if (oifq != NULL)
6613255332Scy		ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq);
6614145522Sdarrenr	else
6615255332Scy		ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat);
6616145522Sdarrenr	return;
6617145522Sdarrenr}
6618170268Sdarrenr
6619170268Sdarrenr
6620170268Sdarrenr/* ------------------------------------------------------------------------ */
6621170268Sdarrenr/* Function:    nat_getnext                                                 */
6622170268Sdarrenr/* Returns:     int - 0 == ok, else error                                   */
6623255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6624255332Scy/*              t(I)   - pointer to ipftoken structure                      */
6625170268Sdarrenr/*              itp(I) - pointer to ipfgeniter_t structure                  */
6626170268Sdarrenr/*                                                                          */
6627170268Sdarrenr/* Fetch the next nat/ipnat structure pointer from the linked list and      */
6628170268Sdarrenr/* copy it out to the storage space pointed to by itp_data.  The next item  */
6629170268Sdarrenr/* in the list to look at is put back in the ipftoken struture.             */
6630170268Sdarrenr/* ------------------------------------------------------------------------ */
6631255332Scystatic int
6632255332Scyipf_nat_getnext(softc, t, itp, objp)
6633255332Scy	ipf_main_softc_t *softc;
6634255332Scy	ipftoken_t *t;
6635255332Scy	ipfgeniter_t *itp;
6636255332Scy	ipfobj_t *objp;
6637170268Sdarrenr{
6638255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
6639170268Sdarrenr	hostmap_t *hm, *nexthm = NULL, zerohm;
6640170268Sdarrenr	ipnat_t *ipn, *nextipnat = NULL, zeroipn;
6641170268Sdarrenr	nat_t *nat, *nextnat = NULL, zeronat;
6642255332Scy	int error = 0;
6643255332Scy	void *nnext;
6644170268Sdarrenr
6645255332Scy	if (itp->igi_nitems != 1) {
6646255332Scy		IPFERROR(60075);
6647172776Sdarrenr		return ENOSPC;
6648255332Scy	}
6649170268Sdarrenr
6650255332Scy	READ_ENTER(&softc->ipf_nat);
6651170268Sdarrenr
6652170268Sdarrenr	switch (itp->igi_type)
6653170268Sdarrenr	{
6654170268Sdarrenr	case IPFGENITER_HOSTMAP :
6655170268Sdarrenr		hm = t->ipt_data;
6656170268Sdarrenr		if (hm == NULL) {
6657255332Scy			nexthm = softn->ipf_hm_maplist;
6658170268Sdarrenr		} else {
6659170268Sdarrenr			nexthm = hm->hm_next;
6660170268Sdarrenr		}
6661255332Scy		if (nexthm != NULL) {
6662255332Scy			ATOMIC_INC32(nexthm->hm_ref);
6663255332Scy			t->ipt_data = nexthm;
6664255332Scy		} else {
6665255332Scy			bzero(&zerohm, sizeof(zerohm));
6666255332Scy			nexthm = &zerohm;
6667255332Scy			t->ipt_data = NULL;
6668255332Scy		}
6669255332Scy		nnext = nexthm->hm_next;
6670170268Sdarrenr		break;
6671170268Sdarrenr
6672170268Sdarrenr	case IPFGENITER_IPNAT :
6673170268Sdarrenr		ipn = t->ipt_data;
6674170268Sdarrenr		if (ipn == NULL) {
6675255332Scy			nextipnat = softn->ipf_nat_list;
6676170268Sdarrenr		} else {
6677170268Sdarrenr			nextipnat = ipn->in_next;
6678170268Sdarrenr		}
6679255332Scy		if (nextipnat != NULL) {
6680255332Scy			ATOMIC_INC32(nextipnat->in_use);
6681255332Scy			t->ipt_data = nextipnat;
6682255332Scy		} else {
6683255332Scy			bzero(&zeroipn, sizeof(zeroipn));
6684255332Scy			nextipnat = &zeroipn;
6685255332Scy			t->ipt_data = NULL;
6686255332Scy		}
6687255332Scy		nnext = nextipnat->in_next;
6688170268Sdarrenr		break;
6689170268Sdarrenr
6690170268Sdarrenr	case IPFGENITER_NAT :
6691170268Sdarrenr		nat = t->ipt_data;
6692170268Sdarrenr		if (nat == NULL) {
6693255332Scy			nextnat = softn->ipf_nat_instances;
6694170268Sdarrenr		} else {
6695170268Sdarrenr			nextnat = nat->nat_next;
6696170268Sdarrenr		}
6697255332Scy		if (nextnat != NULL) {
6698255332Scy			MUTEX_ENTER(&nextnat->nat_lock);
6699255332Scy			nextnat->nat_ref++;
6700255332Scy			MUTEX_EXIT(&nextnat->nat_lock);
6701255332Scy			t->ipt_data = nextnat;
6702255332Scy		} else {
6703255332Scy			bzero(&zeronat, sizeof(zeronat));
6704255332Scy			nextnat = &zeronat;
6705255332Scy			t->ipt_data = NULL;
6706255332Scy		}
6707255332Scy		nnext = nextnat->nat_next;
6708170268Sdarrenr		break;
6709255332Scy
6710170268Sdarrenr	default :
6711255332Scy		RWLOCK_EXIT(&softc->ipf_nat);
6712255332Scy		IPFERROR(60055);
6713170268Sdarrenr		return EINVAL;
6714170268Sdarrenr	}
6715170268Sdarrenr
6716255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
6717170268Sdarrenr
6718255332Scy	objp->ipfo_ptr = itp->igi_data;
6719170268Sdarrenr
6720172776Sdarrenr	switch (itp->igi_type)
6721172776Sdarrenr	{
6722172776Sdarrenr	case IPFGENITER_HOSTMAP :
6723255332Scy		error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm));
6724255332Scy		if (error != 0) {
6725255332Scy			IPFERROR(60049);
6726255332Scy			error = EFAULT;
6727255332Scy		}
6728172776Sdarrenr		if (hm != NULL) {
6729255332Scy			WRITE_ENTER(&softc->ipf_nat);
6730255332Scy			ipf_nat_hostmapdel(softc, &hm);
6731255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
6732172776Sdarrenr		}
6733172776Sdarrenr		break;
6734255332Scy
6735172776Sdarrenr	case IPFGENITER_IPNAT :
6736255332Scy		objp->ipfo_size = nextipnat->in_size;
6737255332Scy		objp->ipfo_type = IPFOBJ_IPNAT;
6738255332Scy		error = ipf_outobjk(softc, objp, nextipnat);
6739172776Sdarrenr		if (ipn != NULL) {
6740255332Scy			WRITE_ENTER(&softc->ipf_nat);
6741255332Scy			ipf_nat_rule_deref(softc, &ipn);
6742255332Scy			RWLOCK_EXIT(&softc->ipf_nat);
6743172776Sdarrenr		}
6744172776Sdarrenr		break;
6745172776Sdarrenr
6746170268Sdarrenr	case IPFGENITER_NAT :
6747255332Scy		objp->ipfo_size = sizeof(nat_t);
6748255332Scy		objp->ipfo_type = IPFOBJ_NAT;
6749255332Scy		error = ipf_outobjk(softc, objp, nextnat);
6750255332Scy		if (nat != NULL)
6751255332Scy			ipf_nat_deref(softc, &nat);
6752170268Sdarrenr
6753170268Sdarrenr		break;
6754170268Sdarrenr	}
6755170268Sdarrenr
6756255332Scy	if (nnext == NULL)
6757255332Scy		ipf_token_mark_complete(t);
6758255332Scy
6759170268Sdarrenr	return error;
6760170268Sdarrenr}
6761170268Sdarrenr
6762170268Sdarrenr
6763170268Sdarrenr/* ------------------------------------------------------------------------ */
6764170268Sdarrenr/* Function:    nat_extraflush                                              */
6765170268Sdarrenr/* Returns:     int - 0 == success, -1 == failure                           */
6766255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6767255332Scy/*              softn(I) - pointer to NAT context structure                 */
6768255332Scy/*              which(I) - how to flush the active NAT table                */
6769170268Sdarrenr/* Write Locks: ipf_nat                                                     */
6770170268Sdarrenr/*                                                                          */
6771170268Sdarrenr/* Flush nat tables.  Three actions currently defined:                      */
6772170268Sdarrenr/* which == 0 : flush all nat table entries                                 */
6773170268Sdarrenr/* which == 1 : flush TCP connections which have started to close but are   */
6774170268Sdarrenr/*	      stuck for some reason.                                        */
6775170268Sdarrenr/* which == 2 : flush TCP connections which have been idle for a long time, */
6776170268Sdarrenr/*	      starting at > 4 days idle and working back in successive half-*/
6777170268Sdarrenr/*	      days to at most 12 hours old.  If this fails to free enough   */
6778170268Sdarrenr/*            slots then work backwards in half hour slots to 30 minutes.   */
6779170268Sdarrenr/*            If that too fails, then work backwards in 30 second intervals */
6780170268Sdarrenr/*            for the last 30 minutes to at worst 30 seconds idle.          */
6781170268Sdarrenr/* ------------------------------------------------------------------------ */
6782255332Scystatic int
6783255332Scyipf_nat_extraflush(softc, softn, which)
6784255332Scy	ipf_main_softc_t *softc;
6785255332Scy	ipf_nat_softc_t *softn;
6786255332Scy	int which;
6787170268Sdarrenr{
6788170268Sdarrenr	nat_t *nat, **natp;
6789170268Sdarrenr	ipftqent_t *tqn;
6790255332Scy	ipftq_t *ifq;
6791170268Sdarrenr	int removed;
6792170268Sdarrenr	SPL_INT(s);
6793170268Sdarrenr
6794170268Sdarrenr	removed = 0;
6795170268Sdarrenr
6796170268Sdarrenr	SPL_NET(s);
6797170268Sdarrenr	switch (which)
6798170268Sdarrenr	{
6799170268Sdarrenr	case 0 :
6800255332Scy		softn->ipf_nat_stats.ns_flush_all++;
6801170268Sdarrenr		/*
6802170268Sdarrenr		 * Style 0 flush removes everything...
6803170268Sdarrenr		 */
6804255332Scy		for (natp = &softn->ipf_nat_instances;
6805255332Scy		     ((nat = *natp) != NULL); ) {
6806255332Scy			ipf_nat_delete(softc, nat, NL_FLUSH);
6807170268Sdarrenr			removed++;
6808170268Sdarrenr		}
6809170268Sdarrenr		break;
6810170268Sdarrenr
6811170268Sdarrenr	case 1 :
6812255332Scy		softn->ipf_nat_stats.ns_flush_closing++;
6813170268Sdarrenr		/*
6814170268Sdarrenr		 * Since we're only interested in things that are closing,
6815170268Sdarrenr		 * we can start with the appropriate timeout queue.
6816170268Sdarrenr		 */
6817255332Scy		for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT;
6818255332Scy		     ifq != NULL; ifq = ifq->ifq_next) {
6819170268Sdarrenr
6820170268Sdarrenr			for (tqn = ifq->ifq_head; tqn != NULL; ) {
6821170268Sdarrenr				nat = tqn->tqe_parent;
6822170268Sdarrenr				tqn = tqn->tqe_next;
6823255332Scy				if (nat->nat_pr[0] != IPPROTO_TCP ||
6824255332Scy				    nat->nat_pr[1] != IPPROTO_TCP)
6825170268Sdarrenr					break;
6826255332Scy				ipf_nat_delete(softc, nat, NL_EXPIRE);
6827170268Sdarrenr				removed++;
6828170268Sdarrenr			}
6829170268Sdarrenr		}
6830170268Sdarrenr
6831170268Sdarrenr		/*
6832170268Sdarrenr		 * Also need to look through the user defined queues.
6833170268Sdarrenr		 */
6834255332Scy		for (ifq = softn->ipf_nat_utqe; ifq != NULL;
6835255332Scy		     ifq = ifq->ifq_next) {
6836170268Sdarrenr			for (tqn = ifq->ifq_head; tqn != NULL; ) {
6837170268Sdarrenr				nat = tqn->tqe_parent;
6838170268Sdarrenr				tqn = tqn->tqe_next;
6839255332Scy				if (nat->nat_pr[0] != IPPROTO_TCP ||
6840255332Scy				    nat->nat_pr[1] != IPPROTO_TCP)
6841170268Sdarrenr					continue;
6842170268Sdarrenr
6843170268Sdarrenr				if ((nat->nat_tcpstate[0] >
6844170268Sdarrenr				     IPF_TCPS_ESTABLISHED) &&
6845170268Sdarrenr				    (nat->nat_tcpstate[1] >
6846170268Sdarrenr				     IPF_TCPS_ESTABLISHED)) {
6847255332Scy					ipf_nat_delete(softc, nat, NL_EXPIRE);
6848170268Sdarrenr					removed++;
6849170268Sdarrenr				}
6850170268Sdarrenr			}
6851170268Sdarrenr		}
6852170268Sdarrenr		break;
6853170268Sdarrenr
6854170268Sdarrenr		/*
6855170268Sdarrenr		 * Args 5-11 correspond to flushing those particular states
6856170268Sdarrenr		 * for TCP connections.
6857170268Sdarrenr		 */
6858170268Sdarrenr	case IPF_TCPS_CLOSE_WAIT :
6859170268Sdarrenr	case IPF_TCPS_FIN_WAIT_1 :
6860170268Sdarrenr	case IPF_TCPS_CLOSING :
6861170268Sdarrenr	case IPF_TCPS_LAST_ACK :
6862170268Sdarrenr	case IPF_TCPS_FIN_WAIT_2 :
6863170268Sdarrenr	case IPF_TCPS_TIME_WAIT :
6864170268Sdarrenr	case IPF_TCPS_CLOSED :
6865255332Scy		softn->ipf_nat_stats.ns_flush_state++;
6866255332Scy		tqn = softn->ipf_nat_tcptq[which].ifq_head;
6867170268Sdarrenr		while (tqn != NULL) {
6868170268Sdarrenr			nat = tqn->tqe_parent;
6869170268Sdarrenr			tqn = tqn->tqe_next;
6870255332Scy			ipf_nat_delete(softc, nat, NL_FLUSH);
6871170268Sdarrenr			removed++;
6872170268Sdarrenr		}
6873170268Sdarrenr		break;
6874255332Scy
6875170268Sdarrenr	default :
6876170268Sdarrenr		if (which < 30)
6877170268Sdarrenr			break;
6878255332Scy
6879255332Scy		softn->ipf_nat_stats.ns_flush_timeout++;
6880170268Sdarrenr		/*
6881170268Sdarrenr		 * Take a large arbitrary number to mean the number of seconds
6882170268Sdarrenr		 * for which which consider to be the maximum value we'll allow
6883170268Sdarrenr		 * the expiration to be.
6884170268Sdarrenr		 */
6885170268Sdarrenr		which = IPF_TTLVAL(which);
6886255332Scy		for (natp = &softn->ipf_nat_instances;
6887255332Scy		     ((nat = *natp) != NULL); ) {
6888255332Scy			if (softc->ipf_ticks - nat->nat_touched > which) {
6889255332Scy				ipf_nat_delete(softc, nat, NL_FLUSH);
6890170268Sdarrenr				removed++;
6891170268Sdarrenr			} else
6892170268Sdarrenr				natp = &nat->nat_next;
6893170268Sdarrenr		}
6894170268Sdarrenr		break;
6895170268Sdarrenr	}
6896170268Sdarrenr
6897170268Sdarrenr	if (which != 2) {
6898170268Sdarrenr		SPL_X(s);
6899170268Sdarrenr		return removed;
6900170268Sdarrenr	}
6901170268Sdarrenr
6902255332Scy	softn->ipf_nat_stats.ns_flush_queue++;
6903255332Scy
6904170268Sdarrenr	/*
6905255332Scy	 * Asked to remove inactive entries because the table is full, try
6906255332Scy	 * again, 3 times, if first attempt failed with a different criteria
6907255332Scy	 * each time.  The order tried in must be in decreasing age.
6908255332Scy	 * Another alternative is to implement random drop and drop N entries
6909255332Scy	 * at random until N have been freed up.
6910170268Sdarrenr	 */
6911255332Scy	if (softc->ipf_ticks - softn->ipf_nat_last_force_flush >
6912255332Scy	    IPF_TTLVAL(5)) {
6913255332Scy		softn->ipf_nat_last_force_flush = softc->ipf_ticks;
6914255332Scy
6915255332Scy		removed = ipf_queueflush(softc, ipf_nat_flush_entry,
6916255332Scy					 softn->ipf_nat_tcptq,
6917255332Scy					 softn->ipf_nat_utqe,
6918255332Scy					 &softn->ipf_nat_stats.ns_active,
6919255332Scy					 softn->ipf_nat_table_sz,
6920255332Scy					 softn->ipf_nat_table_wm_low);
6921170268Sdarrenr	}
6922170268Sdarrenr
6923170268Sdarrenr	SPL_X(s);
6924170268Sdarrenr	return removed;
6925170268Sdarrenr}
6926170268Sdarrenr
6927170268Sdarrenr
6928170268Sdarrenr/* ------------------------------------------------------------------------ */
6929255332Scy/* Function:    ipf_nat_flush_entry                                         */
6930170268Sdarrenr/* Returns:     0 - always succeeds                                         */
6931255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6932255332Scy/*              entry(I) - pointer to NAT entry                             */
6933170268Sdarrenr/* Write Locks: ipf_nat                                                     */
6934170268Sdarrenr/*                                                                          */
6935170268Sdarrenr/* This function is a stepping stone between ipf_queueflush() and           */
6936170268Sdarrenr/* nat_dlete().  It is used so we can provide a uniform interface via the   */
6937170268Sdarrenr/* ipf_queueflush() function.  Since the nat_delete() function returns void */
6938170268Sdarrenr/* we translate that to mean it always succeeds in deleting something.      */
6939170268Sdarrenr/* ------------------------------------------------------------------------ */
6940255332Scystatic int
6941255332Scyipf_nat_flush_entry(softc, entry)
6942255332Scy	ipf_main_softc_t *softc;
6943255332Scy	void *entry;
6944170268Sdarrenr{
6945255332Scy	ipf_nat_delete(softc, entry, NL_FLUSH);
6946170268Sdarrenr	return 0;
6947170268Sdarrenr}
6948172776Sdarrenr
6949172776Sdarrenr
6950172776Sdarrenr/* ------------------------------------------------------------------------ */
6951255332Scy/* Function:    ipf_nat_iterator                                            */
6952255332Scy/* Returns:     int - 0 == ok, else error                                   */
6953255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
6954255332Scy/*              token(I) - pointer to ipftoken structure                    */
6955255332Scy/*              itp(I)   - pointer to ipfgeniter_t structure                */
6956255332Scy/*              obj(I)   - pointer to data description structure            */
6957255332Scy/*                                                                          */
6958255332Scy/* This function acts as a handler for the SIOCGENITER ioctls that use a    */
6959255332Scy/* generic structure to iterate through a list.  There are three different  */
6960255332Scy/* linked lists of NAT related information to go through: NAT rules, active */
6961255332Scy/* NAT mappings and the NAT fragment cache.                                 */
6962255332Scy/* ------------------------------------------------------------------------ */
6963255332Scystatic int
6964255332Scyipf_nat_iterator(softc, token, itp, obj)
6965255332Scy	ipf_main_softc_t *softc;
6966255332Scy	ipftoken_t *token;
6967255332Scy	ipfgeniter_t *itp;
6968255332Scy	ipfobj_t *obj;
6969255332Scy{
6970255332Scy	int error;
6971255332Scy
6972255332Scy	if (itp->igi_data == NULL) {
6973255332Scy		IPFERROR(60052);
6974255332Scy		return EFAULT;
6975255332Scy	}
6976255332Scy
6977255332Scy	switch (itp->igi_type)
6978255332Scy	{
6979255332Scy	case IPFGENITER_HOSTMAP :
6980255332Scy	case IPFGENITER_IPNAT :
6981255332Scy	case IPFGENITER_NAT :
6982255332Scy		error = ipf_nat_getnext(softc, token, itp, obj);
6983255332Scy		break;
6984255332Scy
6985255332Scy	case IPFGENITER_NATFRAG :
6986255332Scy		error = ipf_frag_nat_next(softc, token, itp);
6987255332Scy		break;
6988255332Scy	default :
6989255332Scy		IPFERROR(60053);
6990255332Scy		error = EINVAL;
6991255332Scy		break;
6992255332Scy	}
6993255332Scy
6994255332Scy	return error;
6995255332Scy}
6996255332Scy
6997255332Scy
6998255332Scy/* ------------------------------------------------------------------------ */
6999255332Scy/* Function:    ipf_nat_setpending                                          */
7000255332Scy/* Returns:     Nil                                                         */
7001255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7002255332Scy/*              nat(I)   - pointer to NAT structure                         */
7003255332Scy/* Locks:       ipf_nat (read or write)                                     */
7004255332Scy/*                                                                          */
7005255332Scy/* Put the NAT entry on to the pending queue - this queue has a very short  */
7006255332Scy/* lifetime where items are put that can't be deleted straight away because */
7007255332Scy/* of locking issues but we want to delete them ASAP, anyway.  In calling   */
7008255332Scy/* this function, it is assumed that the owner (if there is one, as shown   */
7009255332Scy/* by nat_me) is no longer interested in it.                                */
7010255332Scy/* ------------------------------------------------------------------------ */
7011255332Scyvoid
7012255332Scyipf_nat_setpending(softc, nat)
7013255332Scy	ipf_main_softc_t *softc;
7014255332Scy	nat_t *nat;
7015255332Scy{
7016255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7017255332Scy	ipftq_t *oifq;
7018255332Scy
7019255332Scy	oifq = nat->nat_tqe.tqe_ifq;
7020255332Scy	if (oifq != NULL)
7021255332Scy		ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq,
7022255332Scy			      &softn->ipf_nat_pending);
7023255332Scy	else
7024255332Scy		ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe,
7025255332Scy				&softn->ipf_nat_pending, nat);
7026255332Scy
7027255332Scy	if (nat->nat_me != NULL) {
7028255332Scy		*nat->nat_me = NULL;
7029255332Scy		nat->nat_me = NULL;
7030255332Scy		nat->nat_ref--;
7031255332Scy		ASSERT(nat->nat_ref >= 0);
7032255332Scy	}
7033255332Scy}
7034255332Scy
7035255332Scy
7036255332Scy/* ------------------------------------------------------------------------ */
7037255332Scy/* Function:    nat_newrewrite                                              */
7038255332Scy/* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
7039255332Scy/*                    allow rule to be moved if IPN_ROUNDR is set.          */
7040255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7041255332Scy/*              nat(I) - pointer to NAT entry                               */
7042255332Scy/*              ni(I)  - pointer to structure with misc. information needed */
7043255332Scy/*                       to create new NAT entry.                           */
7044255332Scy/* Write Lock:  ipf_nat                                                     */
7045255332Scy/*                                                                          */
7046255332Scy/* This function is responsible for setting up an active NAT session where  */
7047255332Scy/* we are changing both the source and destination parameters at the same   */
7048255332Scy/* time.  The loop in here works differently to elsewhere - each iteration  */
7049255332Scy/* is responsible for changing a single parameter that can be incremented.  */
7050255332Scy/* So one pass may increase the source IP#, next source port, next dest. IP#*/
7051255332Scy/* and the last destination port for a total of 4 iterations to try each.   */
7052255332Scy/* This is done to try and exhaustively use the translation space available.*/
7053255332Scy/* ------------------------------------------------------------------------ */
7054255332Scystatic int
7055255332Scyipf_nat_newrewrite(fin, nat, nai)
7056255332Scy	fr_info_t *fin;
7057255332Scy	nat_t *nat;
7058255332Scy	natinfo_t *nai;
7059255332Scy{
7060255332Scy	int src_search = 1;
7061255332Scy	int dst_search = 1;
7062255332Scy	fr_info_t frnat;
7063255332Scy	u_32_t flags;
7064255332Scy	u_short swap;
7065255332Scy	ipnat_t *np;
7066255332Scy	nat_t *natl;
7067255332Scy	int l = 0;
7068255332Scy	int changed;
7069255332Scy
7070255332Scy	natl = NULL;
7071255332Scy	changed = -1;
7072255332Scy	np = nai->nai_np;
7073255332Scy	flags = nat->nat_flags;
7074255332Scy	bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
7075255332Scy
7076255332Scy	nat->nat_hm = NULL;
7077255332Scy
7078255332Scy	do {
7079255332Scy		changed = -1;
7080255332Scy		/* TRACE (l, src_search, dst_search, np) */
7081338170Scy		DT4(ipf_nat_rewrite_1, int, l, int, src_search, int, dst_search, ipnat_t *, np);
7082255332Scy
7083255332Scy		if ((src_search == 0) && (np->in_spnext == 0) &&
7084255332Scy		    (dst_search == 0) && (np->in_dpnext == 0)) {
7085255332Scy			if (l > 0)
7086255332Scy				return -1;
7087255332Scy		}
7088255332Scy
7089255332Scy		/*
7090255332Scy		 * Find a new source address
7091255332Scy		 */
7092255332Scy		if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr,
7093255332Scy				     &frnat.fin_saddr) == -1) {
7094255332Scy			return -1;
7095255332Scy		}
7096255332Scy
7097255332Scy		if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) {
7098255332Scy			src_search = 0;
7099255332Scy			if (np->in_stepnext == 0)
7100255332Scy				np->in_stepnext = 1;
7101255332Scy
7102255332Scy		} else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) {
7103255332Scy			src_search = 0;
7104255332Scy			if (np->in_stepnext == 0)
7105255332Scy				np->in_stepnext = 1;
7106255332Scy
7107255332Scy		} else if (np->in_nsrcmsk == 0xffffffff) {
7108255332Scy			src_search = 0;
7109255332Scy			if (np->in_stepnext == 0)
7110255332Scy				np->in_stepnext = 1;
7111255332Scy
7112255332Scy		} else if (np->in_nsrcmsk != 0xffffffff) {
7113255332Scy			if (np->in_stepnext == 0 && changed == -1) {
7114255332Scy				np->in_snip++;
7115255332Scy				np->in_stepnext++;
7116255332Scy				changed = 0;
7117255332Scy			}
7118255332Scy		}
7119255332Scy
7120255332Scy		if ((flags & IPN_TCPUDPICMP) != 0) {
7121255332Scy			if (np->in_spnext != 0)
7122255332Scy				frnat.fin_data[0] = np->in_spnext;
7123255332Scy
7124255332Scy			/*
7125255332Scy			 * Standard port translation.  Select next port.
7126255332Scy			 */
7127255332Scy			if ((flags & IPN_FIXEDSPORT) != 0) {
7128255332Scy				np->in_stepnext = 2;
7129255332Scy			} else if ((np->in_stepnext == 1) &&
7130255332Scy				   (changed == -1) && (natl != NULL)) {
7131255332Scy				np->in_spnext++;
7132255332Scy				np->in_stepnext++;
7133255332Scy				changed = 1;
7134255332Scy				if (np->in_spnext > np->in_spmax)
7135255332Scy					np->in_spnext = np->in_spmin;
7136255332Scy			}
7137255332Scy		} else {
7138255332Scy			np->in_stepnext = 2;
7139255332Scy		}
7140255332Scy		np->in_stepnext &= 0x3;
7141255332Scy
7142255332Scy		/*
7143255332Scy		 * Find a new destination address
7144255332Scy		 */
7145255332Scy		/* TRACE (fin, np, l, frnat) */
7146338170Scy		DT4(ipf_nat_rewrite_2, frinfo_t *, fin, ipnat_t *, np, int, l, frinfo_t *, &frnat);
7147255332Scy
7148255332Scy		if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr,
7149255332Scy				     &frnat.fin_daddr) == -1)
7150255332Scy			return -1;
7151255332Scy		if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) {
7152255332Scy			dst_search = 0;
7153255332Scy			if (np->in_stepnext == 2)
7154255332Scy				np->in_stepnext = 3;
7155255332Scy
7156255332Scy		} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) {
7157255332Scy			dst_search = 0;
7158255332Scy			if (np->in_stepnext == 2)
7159255332Scy				np->in_stepnext = 3;
7160255332Scy
7161255332Scy		} else if (np->in_ndstmsk == 0xffffffff) {
7162255332Scy			dst_search = 0;
7163255332Scy			if (np->in_stepnext == 2)
7164255332Scy				np->in_stepnext = 3;
7165255332Scy
7166255332Scy		} else if (np->in_ndstmsk != 0xffffffff) {
7167255332Scy			if ((np->in_stepnext == 2) && (changed == -1) &&
7168255332Scy			    (natl != NULL)) {
7169255332Scy				changed = 2;
7170255332Scy				np->in_stepnext++;
7171255332Scy				np->in_dnip++;
7172255332Scy			}
7173255332Scy		}
7174255332Scy
7175255332Scy		if ((flags & IPN_TCPUDPICMP) != 0) {
7176255332Scy			if (np->in_dpnext != 0)
7177255332Scy				frnat.fin_data[1] = np->in_dpnext;
7178255332Scy
7179255332Scy			/*
7180255332Scy			 * Standard port translation.  Select next port.
7181255332Scy			 */
7182255332Scy			if ((flags & IPN_FIXEDDPORT) != 0) {
7183255332Scy				np->in_stepnext = 0;
7184255332Scy			} else if (np->in_stepnext == 3 && changed == -1) {
7185255332Scy				np->in_dpnext++;
7186255332Scy				np->in_stepnext++;
7187255332Scy				changed = 3;
7188255332Scy				if (np->in_dpnext > np->in_dpmax)
7189255332Scy					np->in_dpnext = np->in_dpmin;
7190255332Scy			}
7191255332Scy		} else {
7192255332Scy			if (np->in_stepnext == 3)
7193255332Scy				np->in_stepnext = 0;
7194255332Scy		}
7195255332Scy
7196255332Scy		/* TRACE (frnat) */
7197338170Scy		DT1(ipf_nat_rewrite_3, frinfo_t *, &frnat);
7198255332Scy
7199255332Scy		/*
7200255332Scy		 * Here we do a lookup of the connection as seen from
7201255332Scy		 * the outside.  If an IP# pair already exists, try
7202255332Scy		 * again.  So if you have A->B becomes C->B, you can
7203255332Scy		 * also have D->E become C->E but not D->B causing
7204255332Scy		 * another C->B.  Also take protocol and ports into
7205255332Scy		 * account when determining whether a pre-existing
7206255332Scy		 * NAT setup will cause an external conflict where
7207255332Scy		 * this is appropriate.
7208255332Scy		 *
7209255332Scy		 * fin_data[] is swapped around because we are doing a
7210255332Scy		 * lookup of the packet is if it were moving in the opposite
7211255332Scy		 * direction of the one we are working with now.
7212255332Scy		 */
7213255332Scy		if (flags & IPN_TCPUDP) {
7214255332Scy			swap = frnat.fin_data[0];
7215255332Scy			frnat.fin_data[0] = frnat.fin_data[1];
7216255332Scy			frnat.fin_data[1] = swap;
7217255332Scy		}
7218255332Scy		if (fin->fin_out == 1) {
7219255332Scy			natl = ipf_nat_inlookup(&frnat,
7220255332Scy						flags & ~(SI_WILDP|NAT_SEARCH),
7221255332Scy						(u_int)frnat.fin_p,
7222255332Scy						frnat.fin_dst, frnat.fin_src);
7223255332Scy
7224255332Scy		} else {
7225255332Scy			natl = ipf_nat_outlookup(&frnat,
7226255332Scy						 flags & ~(SI_WILDP|NAT_SEARCH),
7227255332Scy						 (u_int)frnat.fin_p,
7228255332Scy						 frnat.fin_dst, frnat.fin_src);
7229255332Scy		}
7230255332Scy		if (flags & IPN_TCPUDP) {
7231255332Scy			swap = frnat.fin_data[0];
7232255332Scy			frnat.fin_data[0] = frnat.fin_data[1];
7233255332Scy			frnat.fin_data[1] = swap;
7234255332Scy		}
7235255332Scy
7236255332Scy		/* TRACE natl, in_stepnext, l */
7237338170Scy		DT3(ipf_nat_rewrite_2, nat_t *, natl, ipnat_t *, np , int, l);
7238255332Scy
7239255332Scy		if ((natl != NULL) && (l > 8))	/* XXX 8 is arbitrary */
7240255332Scy			return -1;
7241255332Scy
7242255332Scy		np->in_stepnext &= 0x3;
7243255332Scy
7244255332Scy		l++;
7245255332Scy		changed = -1;
7246255332Scy	} while (natl != NULL);
7247255332Scy
7248255332Scy	nat->nat_osrcip = fin->fin_src;
7249255332Scy	nat->nat_odstip = fin->fin_dst;
7250255332Scy	nat->nat_nsrcip = frnat.fin_src;
7251255332Scy	nat->nat_ndstip = frnat.fin_dst;
7252255332Scy
7253255332Scy	if ((flags & IPN_TCPUDP) != 0) {
7254255332Scy		nat->nat_osport = htons(fin->fin_data[0]);
7255255332Scy		nat->nat_odport = htons(fin->fin_data[1]);
7256255332Scy		nat->nat_nsport = htons(frnat.fin_data[0]);
7257255332Scy		nat->nat_ndport = htons(frnat.fin_data[1]);
7258255332Scy	} else if ((flags & IPN_ICMPQUERY) != 0) {
7259255332Scy		nat->nat_oicmpid = fin->fin_data[1];
7260255332Scy		nat->nat_nicmpid = frnat.fin_data[1];
7261255332Scy	}
7262255332Scy
7263255332Scy	return 0;
7264255332Scy}
7265255332Scy
7266255332Scy
7267255332Scy/* ------------------------------------------------------------------------ */
7268255332Scy/* Function:    nat_newdivert                                               */
7269255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7270255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7271255332Scy/*              nat(I) - pointer to NAT entry                               */
7272255332Scy/*              ni(I)  - pointer to structure with misc. information needed */
7273255332Scy/*                       to create new NAT entry.                           */
7274255332Scy/* Write Lock:  ipf_nat                                                     */
7275255332Scy/*                                                                          */
7276255332Scy/* Create a new NAT  divert session as defined by the NAT rule.  This is    */
7277255332Scy/* somewhat different to other NAT session creation routines because we     */
7278255332Scy/* do not iterate through either port numbers or IP addresses, searching    */
7279255332Scy/* for a unique mapping, however, a complimentary duplicate check is made.  */
7280255332Scy/* ------------------------------------------------------------------------ */
7281255332Scystatic int
7282255332Scyipf_nat_newdivert(fin, nat, nai)
7283255332Scy	fr_info_t *fin;
7284255332Scy	nat_t *nat;
7285255332Scy	natinfo_t *nai;
7286255332Scy{
7287255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
7288255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7289255332Scy	fr_info_t frnat;
7290255332Scy	ipnat_t *np;
7291255332Scy	nat_t *natl;
7292255332Scy	int p;
7293255332Scy
7294255332Scy	np = nai->nai_np;
7295255332Scy	bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
7296255332Scy
7297255332Scy	nat->nat_pr[0] = 0;
7298255332Scy	nat->nat_osrcaddr = fin->fin_saddr;
7299255332Scy	nat->nat_odstaddr = fin->fin_daddr;
7300255332Scy	frnat.fin_saddr = htonl(np->in_snip);
7301255332Scy	frnat.fin_daddr = htonl(np->in_dnip);
7302255332Scy	if ((nat->nat_flags & IPN_TCPUDP) != 0) {
7303255332Scy		nat->nat_osport = htons(fin->fin_data[0]);
7304255332Scy		nat->nat_odport = htons(fin->fin_data[1]);
7305255332Scy	} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
7306255332Scy		nat->nat_oicmpid = fin->fin_data[1];
7307255332Scy	}
7308255332Scy
7309255332Scy	if (np->in_redir & NAT_DIVERTUDP) {
7310255332Scy		frnat.fin_data[0] = np->in_spnext;
7311255332Scy		frnat.fin_data[1] = np->in_dpnext;
7312255332Scy		frnat.fin_flx |= FI_TCPUDP;
7313255332Scy		p = IPPROTO_UDP;
7314255332Scy	} else {
7315255332Scy		frnat.fin_flx &= ~FI_TCPUDP;
7316255332Scy		p = IPPROTO_IPIP;
7317255332Scy	}
7318255332Scy
7319255332Scy	if (fin->fin_out == 1) {
7320255332Scy		natl = ipf_nat_inlookup(&frnat, 0, p,
7321255332Scy					frnat.fin_dst, frnat.fin_src);
7322255332Scy
7323255332Scy	} else {
7324255332Scy		natl = ipf_nat_outlookup(&frnat, 0, p,
7325255332Scy					 frnat.fin_dst, frnat.fin_src);
7326255332Scy	}
7327255332Scy
7328255332Scy	if (natl != NULL) {
7329255332Scy		NBUMPSIDED(fin->fin_out, ns_divert_exist);
7330338170Scy		DT3(ns_divert_exist, fr_info_t *, fin, nat_t *, nat, natinfo_t, nai);
7331255332Scy		return -1;
7332255332Scy	}
7333255332Scy
7334255332Scy	nat->nat_nsrcaddr = frnat.fin_saddr;
7335255332Scy	nat->nat_ndstaddr = frnat.fin_daddr;
7336255332Scy	if ((nat->nat_flags & IPN_TCPUDP) != 0) {
7337255332Scy		nat->nat_nsport = htons(frnat.fin_data[0]);
7338255332Scy		nat->nat_ndport = htons(frnat.fin_data[1]);
7339255332Scy	} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
7340255332Scy		nat->nat_nicmpid = frnat.fin_data[1];
7341255332Scy	}
7342255332Scy
7343255332Scy	nat->nat_pr[fin->fin_out] = fin->fin_p;
7344255332Scy	nat->nat_pr[1 - fin->fin_out] = p;
7345255332Scy
7346255332Scy	if (np->in_redir & NAT_REDIRECT)
7347255332Scy		nat->nat_dir = NAT_DIVERTIN;
7348255332Scy	else
7349255332Scy		nat->nat_dir = NAT_DIVERTOUT;
7350255332Scy
7351255332Scy	return 0;
7352255332Scy}
7353255332Scy
7354255332Scy
7355255332Scy/* ------------------------------------------------------------------------ */
7356255332Scy/* Function:    nat_builddivertmp                                           */
7357255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7358255332Scy/* Parameters:  softn(I) - pointer to NAT context structure                 */
7359255332Scy/*              np(I)    - pointer to a NAT rule                            */
7360255332Scy/*                                                                          */
7361255332Scy/* For divert rules, a skeleton packet representing what will be prepended  */
7362255332Scy/* to the real packet is created.  Even though we don't have the full       */
7363255332Scy/* packet here, a checksum is calculated that we update later when we       */
7364255332Scy/* fill in the final details.  At present a 0 checksum for UDP is being set */
7365255332Scy/* here because it is expected that divert will be used for localhost.      */
7366255332Scy/* ------------------------------------------------------------------------ */
7367255332Scystatic int
7368255332Scyipf_nat_builddivertmp(softn, np)
7369255332Scy	ipf_nat_softc_t *softn;
7370255332Scy	ipnat_t *np;
7371255332Scy{
7372255332Scy	udphdr_t *uh;
7373255332Scy	size_t len;
7374255332Scy	ip_t *ip;
7375255332Scy
7376255332Scy	if ((np->in_redir & NAT_DIVERTUDP) != 0)
7377255332Scy		len = sizeof(ip_t) + sizeof(udphdr_t);
7378255332Scy	else
7379255332Scy		len = sizeof(ip_t);
7380255332Scy
7381255332Scy	ALLOC_MB_T(np->in_divmp, len);
7382255332Scy	if (np->in_divmp == NULL) {
7383255332Scy		NBUMPD(ipf_nat_stats, ns_divert_build);
7384255332Scy		return -1;
7385255332Scy	}
7386255332Scy
7387255332Scy	/*
7388255332Scy	 * First, the header to get the packet diverted to the new destination
7389255332Scy	 */
7390255332Scy	ip = MTOD(np->in_divmp, ip_t *);
7391255332Scy	IP_V_A(ip, 4);
7392255332Scy	IP_HL_A(ip, 5);
7393255332Scy	ip->ip_tos = 0;
7394255332Scy	if ((np->in_redir & NAT_DIVERTUDP) != 0)
7395255332Scy		ip->ip_p = IPPROTO_UDP;
7396255332Scy	else
7397255332Scy		ip->ip_p = IPPROTO_IPIP;
7398255332Scy	ip->ip_ttl = 255;
7399255332Scy	ip->ip_off = 0;
7400255332Scy	ip->ip_sum = 0;
7401255332Scy	ip->ip_len = htons(len);
7402255332Scy	ip->ip_id = 0;
7403255332Scy	ip->ip_src.s_addr = htonl(np->in_snip);
7404255332Scy	ip->ip_dst.s_addr = htonl(np->in_dnip);
7405255332Scy	ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip));
7406255332Scy
7407255332Scy	if (np->in_redir & NAT_DIVERTUDP) {
7408255332Scy		uh = (udphdr_t *)(ip + 1);
7409255332Scy		uh->uh_sum = 0;
7410255332Scy		uh->uh_ulen = 8;
7411255332Scy		uh->uh_sport = htons(np->in_spnext);
7412255332Scy		uh->uh_dport = htons(np->in_dpnext);
7413255332Scy	}
7414255332Scy
7415255332Scy	return 0;
7416255332Scy}
7417255332Scy
7418255332Scy
7419255332Scy#define	MINDECAP	(sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t))
7420255332Scy
7421255332Scy/* ------------------------------------------------------------------------ */
7422255332Scy/* Function:    nat_decap                                                   */
7423255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7424255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7425255332Scy/*              nat(I) - pointer to current NAT session                     */
7426255332Scy/*                                                                          */
7427255332Scy/* This function is responsible for undoing a packet's encapsulation in the */
7428255332Scy/* reverse of an encap/divert rule.  After removing the outer encapsulation */
7429255332Scy/* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/
7430255332Scy/* match the "new" packet as it may still be used by IPFilter elsewhere.    */
7431255332Scy/* We use "dir" here as the basis for some of the expectations about the    */
7432255332Scy/* outer header.  If we return an error, the goal is to leave the original  */
7433255332Scy/* packet information undisturbed - this falls short at the end where we'd  */
7434255332Scy/* need to back a backup copy of "fin" - expensive.                         */
7435255332Scy/* ------------------------------------------------------------------------ */
7436255332Scystatic int
7437255332Scyipf_nat_decap(fin, nat)
7438255332Scy	fr_info_t *fin;
7439255332Scy	nat_t *nat;
7440255332Scy{
7441255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
7442255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7443255332Scy	char *hdr;
7444255332Scy	int hlen;
7445255332Scy	int skip;
7446255332Scy	mb_t *m;
7447255332Scy
7448255332Scy	if ((fin->fin_flx & FI_ICMPERR) != 0) {
7449255332Scy		/*
7450255332Scy		 * ICMP packets don't get decapsulated, instead what we need
7451255332Scy		 * to do is change the ICMP reply from including (in the data
7452255332Scy		 * portion for errors) the encapsulated packet that we sent
7453255332Scy		 * out to something that resembles the original packet prior
7454255332Scy		 * to encapsulation.  This isn't done here - all we're doing
7455255332Scy		 * here is changing the outer address to ensure that it gets
7456255332Scy		 * targetted back to the correct system.
7457255332Scy		 */
7458255332Scy
7459255332Scy		if (nat->nat_dir & NAT_OUTBOUND) {
7460255332Scy			u_32_t sum1, sum2, sumd;
7461255332Scy
7462255332Scy			sum1 = ntohl(fin->fin_daddr);
7463255332Scy			sum2 = ntohl(nat->nat_osrcaddr);
7464255332Scy			CALC_SUMD(sum1, sum2, sumd);
7465255332Scy			fin->fin_ip->ip_dst = nat->nat_osrcip;
7466255332Scy			fin->fin_daddr = nat->nat_osrcaddr;
7467255332Scy#if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
7468255332Scy     defined(__osf__) || defined(linux)
7469255332Scy			ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0);
7470255332Scy#endif
7471255332Scy		}
7472255332Scy		return 0;
7473255332Scy	}
7474255332Scy
7475255332Scy	m = fin->fin_m;
7476255332Scy	skip = fin->fin_hlen;
7477255332Scy
7478255332Scy	switch (nat->nat_dir)
7479255332Scy	{
7480255332Scy	case NAT_DIVERTIN :
7481255332Scy	case NAT_DIVERTOUT :
7482255332Scy		if (fin->fin_plen < MINDECAP)
7483255332Scy			return -1;
7484255332Scy		skip += sizeof(udphdr_t);
7485255332Scy		break;
7486255332Scy
7487255332Scy	case NAT_ENCAPIN :
7488255332Scy	case NAT_ENCAPOUT :
7489255332Scy		if (fin->fin_plen < (skip + sizeof(ip_t)))
7490255332Scy			return -1;
7491255332Scy		break;
7492255332Scy	default :
7493255332Scy		return -1;
7494255332Scy		/* NOTREACHED */
7495255332Scy	}
7496255332Scy
7497255332Scy	/*
7498255332Scy	 * The aim here is to keep the original packet details in "fin" for
7499255332Scy	 * as long as possible so that returning with an error is for the
7500255332Scy	 * original packet and there is little undoing work to do.
7501255332Scy	 */
7502255332Scy	if (M_LEN(m) < skip + sizeof(ip_t)) {
7503255332Scy		if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1)
7504255332Scy			return -1;
7505255332Scy	}
7506255332Scy
7507255332Scy	hdr = MTOD(fin->fin_m, char *);
7508255332Scy	fin->fin_ip = (ip_t *)(hdr + skip);
7509255332Scy	hlen = IP_HL(fin->fin_ip) << 2;
7510255332Scy
7511255332Scy	if (ipf_pr_pullup(fin, skip + hlen) == -1) {
7512255332Scy		NBUMPSIDED(fin->fin_out, ns_decap_pullup);
7513255332Scy		return -1;
7514255332Scy	}
7515255332Scy
7516255332Scy	fin->fin_hlen = hlen;
7517255332Scy	fin->fin_dlen -= skip;
7518255332Scy	fin->fin_plen -= skip;
7519255332Scy	fin->fin_ipoff += skip;
7520255332Scy
7521255332Scy	if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) {
7522255332Scy		NBUMPSIDED(fin->fin_out, ns_decap_bad);
7523255332Scy		return -1;
7524255332Scy	}
7525255332Scy
7526255332Scy	return skip;
7527255332Scy}
7528255332Scy
7529255332Scy
7530255332Scy/* ------------------------------------------------------------------------ */
7531255332Scy/* Function:    nat_nextaddr                                                */
7532255332Scy/* Returns:     int - -1 == bad input (no new address),                     */
7533255332Scy/*                     0 == success and dst has new address                 */
7534255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7535255332Scy/*              na(I)  - how to generate new address                        */
7536255332Scy/*              old(I) - original address being replaced                    */
7537255332Scy/*              dst(O) - where to put the new address                       */
7538255332Scy/* Write Lock:  ipf_nat                                                     */
7539255332Scy/*                                                                          */
7540255332Scy/* This function uses the contents of the "na" structure, in combination    */
7541255332Scy/* with "old" to produce a new address to store in "dst".  Not all of the   */
7542255332Scy/* possible uses of "na" will result in a new address.                      */
7543255332Scy/* ------------------------------------------------------------------------ */
7544255332Scystatic int
7545255332Scyipf_nat_nextaddr(fin, na, old, dst)
7546255332Scy	fr_info_t *fin;
7547255332Scy	nat_addr_t *na;
7548255332Scy	u_32_t *old, *dst;
7549255332Scy{
7550255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
7551255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
7552255332Scy	u_32_t amin, amax, new;
7553255332Scy	i6addr_t newip;
7554255332Scy	int error;
7555255332Scy
7556255332Scy	new = 0;
7557255332Scy	amin = na->na_addr[0].in4.s_addr;
7558255332Scy
7559255332Scy	switch (na->na_atype)
7560255332Scy	{
7561255332Scy	case FRI_RANGE :
7562255332Scy		amax = na->na_addr[1].in4.s_addr;
7563255332Scy		break;
7564255332Scy
7565255332Scy	case FRI_NETMASKED :
7566255332Scy	case FRI_DYNAMIC :
7567255332Scy	case FRI_NORMAL :
7568255332Scy		/*
7569255332Scy		 * Compute the maximum address by adding the inverse of the
7570255332Scy		 * netmask to the minimum address.
7571255332Scy		 */
7572255332Scy		amax = ~na->na_addr[1].in4.s_addr;
7573255332Scy		amax |= amin;
7574255332Scy		break;
7575255332Scy
7576255332Scy	case FRI_LOOKUP :
7577255332Scy		break;
7578255332Scy
7579255332Scy	case FRI_BROADCAST :
7580255332Scy	case FRI_PEERADDR :
7581255332Scy	case FRI_NETWORK :
7582255332Scy	default :
7583338170Scy		DT4(ns_na_atype, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new);
7584255332Scy		return -1;
7585255332Scy	}
7586255332Scy
7587255332Scy	error = -1;
7588255332Scy
7589255332Scy	if (na->na_atype == FRI_LOOKUP) {
7590255332Scy		if (na->na_type == IPLT_DSTLIST) {
7591255332Scy			error = ipf_dstlist_select_node(fin, na->na_ptr, dst,
7592255332Scy							NULL);
7593255332Scy		} else {
7594255332Scy			NBUMPSIDE(fin->fin_out, ns_badnextaddr);
7595338170Scy			DT4(ns_badnextaddr_1, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new);
7596255332Scy		}
7597255332Scy
7598255332Scy	} else if (na->na_atype == IPLT_NONE) {
7599255332Scy		/*
7600255332Scy		 * 0/0 as the new address means leave it alone.
7601255332Scy		 */
7602255332Scy		if (na->na_addr[0].in4.s_addr == 0 &&
7603255332Scy		    na->na_addr[1].in4.s_addr == 0) {
7604255332Scy			new = *old;
7605255332Scy
7606255332Scy		/*
7607255332Scy		 * 0/32 means get the interface's address
7608255332Scy		 */
7609255332Scy		} else if (na->na_addr[0].in4.s_addr == 0 &&
7610255332Scy			   na->na_addr[1].in4.s_addr == 0xffffffff) {
7611255332Scy			if (ipf_ifpaddr(softc, 4, na->na_atype,
7612255332Scy					fin->fin_ifp, &newip, NULL) == -1) {
7613255332Scy				NBUMPSIDED(fin->fin_out, ns_ifpaddrfail);
7614338170Scy				DT4(ns_ifpaddrfail, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new);
7615255332Scy				return -1;
7616255332Scy			}
7617255332Scy			new = newip.in4.s_addr;
7618255332Scy		} else {
7619255332Scy			new = htonl(na->na_nextip);
7620255332Scy		}
7621255332Scy		*dst = new;
7622255332Scy		error = 0;
7623255332Scy
7624255332Scy	} else {
7625255332Scy		NBUMPSIDE(fin->fin_out, ns_badnextaddr);
7626338170Scy		DT4(ns_badnextaddr_2, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new);
7627255332Scy	}
7628255332Scy
7629255332Scy	return error;
7630255332Scy}
7631255332Scy
7632255332Scy
7633255332Scy/* ------------------------------------------------------------------------ */
7634255332Scy/* Function:    nat_nextaddrinit                                            */
7635255332Scy/* Returns:     int - 0 == success, else error number                       */
7636255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7637255332Scy/*              na(I)      - NAT address information for generating new addr*/
7638255332Scy/*              initial(I) - flag indicating if it is the first call for    */
7639255332Scy/*                           this "na" structure.                           */
7640255332Scy/*              ifp(I)     - network interface to derive address            */
7641255332Scy/*                           information from.                              */
7642255332Scy/*                                                                          */
7643255332Scy/* This function is expected to be called in two scenarious: when a new NAT */
7644255332Scy/* rule is loaded into the kernel and when the list of NAT rules is sync'd  */
7645255332Scy/* up with the valid network interfaces (possibly due to them changing.)    */
7646255332Scy/* To distinguish between these, the "initial" parameter is used.  If it is */
7647255332Scy/* 1 then this indicates the rule has just been reloaded and 0 for when we  */
7648255332Scy/* are updating information.  This difference is important because in       */
7649255332Scy/* instances where we are not updating address information associated with  */
7650255332Scy/* a network interface, we don't want to disturb what the "next" address to */
7651255332Scy/* come out of ipf_nat_nextaddr() will be.                                  */
7652255332Scy/* ------------------------------------------------------------------------ */
7653255332Scystatic int
7654255332Scyipf_nat_nextaddrinit(softc, base, na, initial, ifp)
7655255332Scy	ipf_main_softc_t *softc;
7656255332Scy	char *base;
7657255332Scy	nat_addr_t *na;
7658255332Scy	int initial;
7659255332Scy	void *ifp;
7660255332Scy{
7661255332Scy
7662255332Scy	switch (na->na_atype)
7663255332Scy	{
7664255332Scy	case FRI_LOOKUP :
7665255332Scy		if (na->na_subtype == 0) {
7666255332Scy			na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT,
7667255332Scy							na->na_type,
7668255332Scy							na->na_num,
7669255332Scy							&na->na_func);
7670255332Scy		} else if (na->na_subtype == 1) {
7671255332Scy			na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT,
7672255332Scy							 na->na_type,
7673255332Scy							 base + na->na_num,
7674255332Scy							 &na->na_func);
7675255332Scy		}
7676255332Scy		if (na->na_func == NULL) {
7677255332Scy			IPFERROR(60060);
7678255332Scy			return ESRCH;
7679255332Scy		}
7680255332Scy		if (na->na_ptr == NULL) {
7681255332Scy			IPFERROR(60056);
7682255332Scy			return ESRCH;
7683255332Scy		}
7684255332Scy		break;
7685255332Scy
7686255332Scy	case FRI_DYNAMIC :
7687255332Scy	case FRI_BROADCAST :
7688255332Scy	case FRI_NETWORK :
7689255332Scy	case FRI_NETMASKED :
7690255332Scy	case FRI_PEERADDR :
7691255332Scy		if (ifp != NULL)
7692255332Scy			(void )ipf_ifpaddr(softc, 4, na->na_atype, ifp,
7693255332Scy					   &na->na_addr[0], &na->na_addr[1]);
7694255332Scy		break;
7695255332Scy
7696255332Scy	case FRI_SPLIT :
7697255332Scy	case FRI_RANGE :
7698255332Scy		if (initial)
7699255332Scy			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr);
7700255332Scy		break;
7701255332Scy
7702255332Scy	case FRI_NONE :
7703255332Scy		na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr;
7704255332Scy		return 0;
7705255332Scy
7706255332Scy	case FRI_NORMAL :
7707255332Scy		na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr;
7708255332Scy		break;
7709255332Scy
7710255332Scy	default :
7711255332Scy		IPFERROR(60054);
7712255332Scy		return EINVAL;
7713255332Scy	}
7714255332Scy
7715255332Scy	if (initial && (na->na_atype == FRI_NORMAL)) {
7716255332Scy		if (na->na_addr[0].in4.s_addr == 0) {
7717255332Scy			if ((na->na_addr[1].in4.s_addr == 0xffffffff) ||
7718255332Scy			    (na->na_addr[1].in4.s_addr == 0)) {
7719255332Scy				return 0;
7720255332Scy			}
7721255332Scy		}
7722255332Scy
7723255332Scy		if (na->na_addr[1].in4.s_addr == 0xffffffff) {
7724255332Scy			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr);
7725255332Scy		} else {
7726255332Scy			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1;
7727255332Scy		}
7728255332Scy	}
7729255332Scy
7730255332Scy	return 0;
7731255332Scy}
7732255332Scy
7733255332Scy
7734255332Scy/* ------------------------------------------------------------------------ */
7735255332Scy/* Function:    ipf_nat_matchflush                                          */
7736255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7737255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7738255332Scy/*              softn(I) - pointer to NAT context structure                 */
7739255332Scy/*              nat(I)   - pointer to current NAT session                   */
7740255332Scy/*                                                                          */
7741255332Scy/* ------------------------------------------------------------------------ */
7742255332Scystatic int
7743255332Scyipf_nat_matchflush(softc, softn, data)
7744255332Scy	ipf_main_softc_t *softc;
7745255332Scy	ipf_nat_softc_t *softn;
7746255332Scy	caddr_t data;
7747255332Scy{
7748255332Scy	int *array, flushed, error;
7749255332Scy	nat_t *nat, *natnext;
7750255332Scy	ipfobj_t obj;
7751255332Scy
7752255332Scy	error = ipf_matcharray_load(softc, data, &obj, &array);
7753255332Scy	if (error != 0)
7754255332Scy		return error;
7755255332Scy
7756255332Scy	flushed = 0;
7757255332Scy
7758255332Scy	for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) {
7759255332Scy		natnext = nat->nat_next;
7760255332Scy		if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) {
7761255332Scy			ipf_nat_delete(softc, nat, NL_FLUSH);
7762255332Scy			flushed++;
7763255332Scy		}
7764255332Scy	}
7765255332Scy
7766255332Scy	obj.ipfo_retval = flushed;
7767255332Scy	error = BCOPYOUT(&obj, data, sizeof(obj));
7768255332Scy
7769255332Scy	KFREES(array, array[0] * sizeof(*array));
7770255332Scy
7771255332Scy	return error;
7772255332Scy}
7773255332Scy
7774255332Scy
7775255332Scy/* ------------------------------------------------------------------------ */
7776255332Scy/* Function:    ipf_nat_matcharray                                          */
7777255332Scy/* Returns:     int - -1 == error, 0 == success                             */
7778255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
7779255332Scy/*              nat(I) - pointer to current NAT session                     */
7780255332Scy/*                                                                          */
7781255332Scy/* ------------------------------------------------------------------------ */
7782255332Scystatic int
7783255332Scyipf_nat_matcharray(nat, array, ticks)
7784255332Scy	nat_t *nat;
7785255332Scy	int *array;
7786255332Scy	u_long ticks;
7787255332Scy{
7788255332Scy	int i, n, *x, e, p;
7789255332Scy
7790255332Scy	e = 0;
7791255332Scy	n = array[0];
7792255332Scy	x = array + 1;
7793255332Scy
7794255332Scy	for (; n > 0; x += 3 + x[2]) {
7795255332Scy		if (x[0] == IPF_EXP_END)
7796255332Scy			break;
7797255332Scy		e = 0;
7798255332Scy
7799255332Scy		n -= x[2] + 3;
7800255332Scy		if (n < 0)
7801255332Scy			break;
7802255332Scy
7803255332Scy		p = x[0] >> 16;
7804255332Scy		if (p != 0 && p != nat->nat_pr[1])
7805255332Scy			break;
7806255332Scy
7807255332Scy		switch (x[0])
7808255332Scy		{
7809255332Scy		case IPF_EXP_IP_PR :
7810255332Scy			for (i = 0; !e && i < x[2]; i++) {
7811255332Scy				e |= (nat->nat_pr[1] == x[i + 3]);
7812255332Scy			}
7813255332Scy			break;
7814255332Scy
7815255332Scy		case IPF_EXP_IP_SRCADDR :
7816255332Scy			if (nat->nat_v[0] == 4) {
7817255332Scy				for (i = 0; !e && i < x[2]; i++) {
7818255332Scy					e |= ((nat->nat_osrcaddr & x[i + 4]) ==
7819255332Scy					      x[i + 3]);
7820255332Scy				}
7821255332Scy			}
7822255332Scy			if (nat->nat_v[1] == 4) {
7823255332Scy				for (i = 0; !e && i < x[2]; i++) {
7824255332Scy					e |= ((nat->nat_nsrcaddr & x[i + 4]) ==
7825255332Scy					      x[i + 3]);
7826255332Scy				}
7827255332Scy			}
7828255332Scy			break;
7829255332Scy
7830255332Scy		case IPF_EXP_IP_DSTADDR :
7831255332Scy			if (nat->nat_v[0] == 4) {
7832255332Scy				for (i = 0; !e && i < x[2]; i++) {
7833255332Scy					e |= ((nat->nat_odstaddr & x[i + 4]) ==
7834255332Scy					      x[i + 3]);
7835255332Scy				}
7836255332Scy			}
7837255332Scy			if (nat->nat_v[1] == 4) {
7838255332Scy				for (i = 0; !e && i < x[2]; i++) {
7839255332Scy					e |= ((nat->nat_ndstaddr & x[i + 4]) ==
7840255332Scy					      x[i + 3]);
7841255332Scy				}
7842255332Scy			}
7843255332Scy			break;
7844255332Scy
7845255332Scy		case IPF_EXP_IP_ADDR :
7846255332Scy			for (i = 0; !e && i < x[2]; i++) {
7847255332Scy				if (nat->nat_v[0] == 4) {
7848255332Scy					e |= ((nat->nat_osrcaddr & x[i + 4]) ==
7849255332Scy					      x[i + 3]);
7850255332Scy				}
7851255332Scy				if (nat->nat_v[1] == 4) {
7852255332Scy					e |= ((nat->nat_nsrcaddr & x[i + 4]) ==
7853255332Scy					      x[i + 3]);
7854255332Scy				}
7855255332Scy				if (nat->nat_v[0] == 4) {
7856255332Scy					e |= ((nat->nat_odstaddr & x[i + 4]) ==
7857255332Scy					      x[i + 3]);
7858255332Scy				}
7859255332Scy				if (nat->nat_v[1] == 4) {
7860255332Scy					e |= ((nat->nat_ndstaddr & x[i + 4]) ==
7861255332Scy					      x[i + 3]);
7862255332Scy				}
7863255332Scy			}
7864255332Scy			break;
7865255332Scy
7866255332Scy#ifdef USE_INET6
7867255332Scy		case IPF_EXP_IP6_SRCADDR :
7868255332Scy			if (nat->nat_v[0] == 6) {
7869255332Scy				for (i = 0; !e && i < x[3]; i++) {
7870255332Scy					e |= IP6_MASKEQ(&nat->nat_osrc6,
7871255332Scy							x + i + 7, x + i + 3);
7872255332Scy				}
7873255332Scy			}
7874255332Scy			if (nat->nat_v[1] == 6) {
7875255332Scy				for (i = 0; !e && i < x[3]; i++) {
7876255332Scy					e |= IP6_MASKEQ(&nat->nat_nsrc6,
7877255332Scy							x + i + 7, x + i + 3);
7878255332Scy				}
7879255332Scy			}
7880255332Scy			break;
7881255332Scy
7882255332Scy		case IPF_EXP_IP6_DSTADDR :
7883255332Scy			if (nat->nat_v[0] == 6) {
7884255332Scy				for (i = 0; !e && i < x[3]; i++) {
7885255332Scy					e |= IP6_MASKEQ(&nat->nat_odst6,
7886255332Scy							x + i + 7,
7887255332Scy							x + i + 3);
7888255332Scy				}
7889255332Scy			}
7890255332Scy			if (nat->nat_v[1] == 6) {
7891255332Scy				for (i = 0; !e && i < x[3]; i++) {
7892255332Scy					e |= IP6_MASKEQ(&nat->nat_ndst6,
7893255332Scy							x + i + 7,
7894255332Scy							x + i + 3);
7895255332Scy				}
7896255332Scy			}
7897255332Scy			break;
7898255332Scy
7899255332Scy		case IPF_EXP_IP6_ADDR :
7900255332Scy			for (i = 0; !e && i < x[3]; i++) {
7901255332Scy				if (nat->nat_v[0] == 6) {
7902255332Scy					e |= IP6_MASKEQ(&nat->nat_osrc6,
7903255332Scy							x + i + 7,
7904255332Scy							x + i + 3);
7905255332Scy				}
7906255332Scy				if (nat->nat_v[0] == 6) {
7907255332Scy					e |= IP6_MASKEQ(&nat->nat_odst6,
7908255332Scy							x + i + 7,
7909255332Scy							x + i + 3);
7910255332Scy				}
7911255332Scy				if (nat->nat_v[1] == 6) {
7912255332Scy					e |= IP6_MASKEQ(&nat->nat_nsrc6,
7913255332Scy							x + i + 7,
7914255332Scy							x + i + 3);
7915255332Scy				}
7916255332Scy				if (nat->nat_v[1] == 6) {
7917255332Scy					e |= IP6_MASKEQ(&nat->nat_ndst6,
7918255332Scy							x + i + 7,
7919255332Scy							x + i + 3);
7920255332Scy				}
7921255332Scy			}
7922255332Scy			break;
7923255332Scy#endif
7924255332Scy
7925255332Scy		case IPF_EXP_UDP_PORT :
7926255332Scy		case IPF_EXP_TCP_PORT :
7927255332Scy			for (i = 0; !e && i < x[2]; i++) {
7928255332Scy				e |= (nat->nat_nsport == x[i + 3]) ||
7929255332Scy				     (nat->nat_ndport == x[i + 3]);
7930255332Scy			}
7931255332Scy			break;
7932255332Scy
7933255332Scy		case IPF_EXP_UDP_SPORT :
7934255332Scy		case IPF_EXP_TCP_SPORT :
7935255332Scy			for (i = 0; !e && i < x[2]; i++) {
7936255332Scy				e |= (nat->nat_nsport == x[i + 3]);
7937255332Scy			}
7938255332Scy			break;
7939255332Scy
7940255332Scy		case IPF_EXP_UDP_DPORT :
7941255332Scy		case IPF_EXP_TCP_DPORT :
7942255332Scy			for (i = 0; !e && i < x[2]; i++) {
7943255332Scy				e |= (nat->nat_ndport == x[i + 3]);
7944255332Scy			}
7945255332Scy			break;
7946255332Scy
7947255332Scy		case IPF_EXP_TCP_STATE :
7948255332Scy			for (i = 0; !e && i < x[2]; i++) {
7949255332Scy				e |= (nat->nat_tcpstate[0] == x[i + 3]) ||
7950255332Scy				     (nat->nat_tcpstate[1] == x[i + 3]);
7951255332Scy			}
7952255332Scy			break;
7953255332Scy
7954255332Scy		case IPF_EXP_IDLE_GT :
7955255332Scy			e |= (ticks - nat->nat_touched > x[3]);
7956255332Scy			break;
7957255332Scy		}
7958255332Scy		e ^= x[1];
7959255332Scy
7960255332Scy		if (!e)
7961255332Scy			break;
7962255332Scy	}
7963255332Scy
7964255332Scy	return e;
7965255332Scy}
7966255332Scy
7967255332Scy
7968255332Scy/* ------------------------------------------------------------------------ */
7969255332Scy/* Function:    ipf_nat_gettable                                            */
7970172776Sdarrenr/* Returns:     int     - 0 = success, else error                           */
7971255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
7972255332Scy/*              softn(I) - pointer to NAT context structure                 */
7973255332Scy/*              data(I)  - pointer to ioctl data                            */
7974172776Sdarrenr/*                                                                          */
7975172776Sdarrenr/* This function handles ioctl requests for tables of nat information.      */
7976172776Sdarrenr/* At present the only table it deals with is the hash bucket statistics.   */
7977172776Sdarrenr/* ------------------------------------------------------------------------ */
7978255332Scystatic int
7979255332Scyipf_nat_gettable(softc, softn, data)
7980255332Scy	ipf_main_softc_t *softc;
7981255332Scy	ipf_nat_softc_t *softn;
7982255332Scy	char *data;
7983172776Sdarrenr{
7984172776Sdarrenr	ipftable_t table;
7985172776Sdarrenr	int error;
7986172776Sdarrenr
7987255332Scy	error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE);
7988172776Sdarrenr	if (error != 0)
7989172776Sdarrenr		return error;
7990172776Sdarrenr
7991172776Sdarrenr	switch (table.ita_type)
7992172776Sdarrenr	{
7993172776Sdarrenr	case IPFTABLE_BUCKETS_NATIN :
7994255332Scy		error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
7995255332Scy				table.ita_table,
7996255332Scy				softn->ipf_nat_table_sz * sizeof(u_int));
7997172776Sdarrenr		break;
7998172776Sdarrenr
7999172776Sdarrenr	case IPFTABLE_BUCKETS_NATOUT :
8000255332Scy		error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
8001255332Scy				table.ita_table,
8002255332Scy				softn->ipf_nat_table_sz * sizeof(u_int));
8003172776Sdarrenr		break;
8004172776Sdarrenr
8005172776Sdarrenr	default :
8006255332Scy		IPFERROR(60058);
8007172776Sdarrenr		return EINVAL;
8008172776Sdarrenr	}
8009172776Sdarrenr
8010172776Sdarrenr	if (error != 0) {
8011255332Scy		IPFERROR(60059);
8012172776Sdarrenr		error = EFAULT;
8013172776Sdarrenr	}
8014172776Sdarrenr	return error;
8015172776Sdarrenr}
8016255332Scy
8017255332Scy
8018255332Scy/* ------------------------------------------------------------------------ */
8019255332Scy/* Function:    ipf_nat_settimeout                                          */
8020255332Scy/* Returns:     int  - 0 = success, else failure			    */
8021255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8022255332Scy/*              t(I) - pointer to tunable                                   */
8023255332Scy/*              p(I) - pointer to new tuning data                           */
8024255332Scy/*                                                                          */
8025255332Scy/* Apply the timeout change to the NAT timeout queues.                      */
8026255332Scy/* ------------------------------------------------------------------------ */
8027255332Scyint
8028255332Scyipf_nat_settimeout(softc, t, p)
8029255332Scy	struct ipf_main_softc_s *softc;
8030255332Scy	ipftuneable_t *t;
8031255332Scy	ipftuneval_t *p;
8032255332Scy{
8033255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8034255332Scy
8035255332Scy	if (!strncmp(t->ipft_name, "tcp_", 4))
8036255332Scy		return ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq);
8037255332Scy
8038255332Scy	if (!strcmp(t->ipft_name, "udp_timeout")) {
8039255332Scy		ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int);
8040255332Scy	} else if (!strcmp(t->ipft_name, "udp_ack_timeout")) {
8041255332Scy		ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int);
8042255332Scy	} else if (!strcmp(t->ipft_name, "icmp_timeout")) {
8043255332Scy		ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int);
8044255332Scy	} else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) {
8045255332Scy		ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int);
8046255332Scy	} else if (!strcmp(t->ipft_name, "ip_timeout")) {
8047255332Scy		ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int);
8048255332Scy	} else {
8049255332Scy		IPFERROR(60062);
8050255332Scy		return ESRCH;
8051255332Scy	}
8052255332Scy	return 0;
8053255332Scy}
8054255332Scy
8055255332Scy
8056255332Scy/* ------------------------------------------------------------------------ */
8057255332Scy/* Function:    ipf_nat_rehash                                              */
8058255332Scy/* Returns:     int  - 0 = success, else failure			    */
8059255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8060255332Scy/*              t(I) - pointer to tunable                                   */
8061255332Scy/*              p(I) - pointer to new tuning data                           */
8062255332Scy/*                                                                          */
8063255332Scy/* To change the size of the basic NAT table, we need to first allocate the */
8064255332Scy/* new tables (lest it fails and we've got nowhere to store all of the NAT  */
8065255332Scy/* sessions currently active) and then walk through the entire list and     */
8066255332Scy/* insert them into the table.  There are two tables here: an inbound one   */
8067255332Scy/* and an outbound one.  Each NAT entry goes into each table once.          */
8068255332Scy/* ------------------------------------------------------------------------ */
8069255332Scyint
8070255332Scyipf_nat_rehash(softc, t, p)
8071255332Scy	ipf_main_softc_t *softc;
8072255332Scy	ipftuneable_t *t;
8073255332Scy	ipftuneval_t *p;
8074255332Scy{
8075255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8076255332Scy	nat_t **newtab[2], *nat, **natp;
8077255332Scy	u_int *bucketlens[2];
8078255332Scy	u_int maxbucket;
8079255332Scy	u_int newsize;
8080255332Scy	int error;
8081255332Scy	u_int hv;
8082255332Scy	int i;
8083255332Scy
8084255332Scy	newsize = p->ipftu_int;
8085255332Scy	/*
8086255332Scy	 * In case there is nothing to do...
8087255332Scy	 */
8088255332Scy	if (newsize == softn->ipf_nat_table_sz)
8089255332Scy		return 0;
8090255332Scy
8091255332Scy	newtab[0] = NULL;
8092255332Scy	newtab[1] = NULL;
8093255332Scy	bucketlens[0] = NULL;
8094255332Scy	bucketlens[1] = NULL;
8095255332Scy	/*
8096255332Scy	 * 4 tables depend on the NAT table size: the inbound looking table,
8097255332Scy	 * the outbound lookup table and the hash chain length for each.
8098255332Scy	 */
8099255332Scy	KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *));
8100255332Scy	if (newtab == NULL) {
8101255332Scy		error = 60063;
8102255332Scy		goto badrehash;
8103255332Scy	}
8104255332Scy
8105255332Scy	KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *));
8106255332Scy	if (newtab == NULL) {
8107255332Scy		error = 60064;
8108255332Scy		goto badrehash;
8109255332Scy	}
8110255332Scy
8111255332Scy	KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int));
8112255332Scy	if (bucketlens[0] == NULL) {
8113255332Scy		error = 60065;
8114255332Scy		goto badrehash;
8115255332Scy	}
8116255332Scy
8117255332Scy	KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int));
8118255332Scy	if (bucketlens[1] == NULL) {
8119255332Scy		error = 60066;
8120255332Scy		goto badrehash;
8121255332Scy	}
8122255332Scy
8123255332Scy	/*
8124255332Scy	 * Recalculate the maximum length based on the new size.
8125255332Scy	 */
8126255332Scy	for (maxbucket = 0, i = newsize; i > 0; i >>= 1)
8127255332Scy		maxbucket++;
8128255332Scy	maxbucket *= 2;
8129255332Scy
8130255332Scy	bzero((char *)newtab[0], newsize * sizeof(nat_t *));
8131255332Scy	bzero((char *)newtab[1], newsize * sizeof(nat_t *));
8132255332Scy	bzero((char *)bucketlens[0], newsize * sizeof(u_int));
8133255332Scy	bzero((char *)bucketlens[1], newsize * sizeof(u_int));
8134255332Scy
8135255332Scy	WRITE_ENTER(&softc->ipf_nat);
8136255332Scy
8137255332Scy	if (softn->ipf_nat_table[0] != NULL) {
8138255332Scy		KFREES(softn->ipf_nat_table[0],
8139255332Scy		       softn->ipf_nat_table_sz *
8140255332Scy		       sizeof(*softn->ipf_nat_table[0]));
8141255332Scy	}
8142255332Scy	softn->ipf_nat_table[0] = newtab[0];
8143255332Scy
8144255332Scy	if (softn->ipf_nat_table[1] != NULL) {
8145255332Scy		KFREES(softn->ipf_nat_table[1],
8146255332Scy		       softn->ipf_nat_table_sz *
8147255332Scy		       sizeof(*softn->ipf_nat_table[1]));
8148255332Scy	}
8149255332Scy	softn->ipf_nat_table[1] = newtab[1];
8150255332Scy
8151255332Scy	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) {
8152255332Scy		KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
8153255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8154255332Scy	}
8155255332Scy	softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0];
8156255332Scy
8157255332Scy	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) {
8158255332Scy		KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
8159255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8160255332Scy	}
8161255332Scy	softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1];
8162255332Scy
8163255332Scy#ifdef USE_INET6
8164255332Scy	if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) {
8165255332Scy		KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen,
8166255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8167255332Scy	}
8168255332Scy	softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0];
8169255332Scy
8170255332Scy	if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) {
8171255332Scy		KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen,
8172255332Scy		       softn->ipf_nat_table_sz * sizeof(u_int));
8173255332Scy	}
8174255332Scy	softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1];
8175255332Scy#endif
8176255332Scy
8177255332Scy	softn->ipf_nat_maxbucket = maxbucket;
8178255332Scy	softn->ipf_nat_table_sz = newsize;
8179255332Scy	/*
8180255332Scy	 * Walk through the entire list of NAT table entries and put them
8181255332Scy	 * in the new NAT table, somewhere.  Because we have a new table,
8182255332Scy	 * we need to restart the counter of how many chains are in use.
8183255332Scy	 */
8184255332Scy	softn->ipf_nat_stats.ns_side[0].ns_inuse = 0;
8185255332Scy	softn->ipf_nat_stats.ns_side[1].ns_inuse = 0;
8186255332Scy#ifdef USE_INET6
8187255332Scy	softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0;
8188255332Scy	softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0;
8189255332Scy#endif
8190255332Scy
8191255332Scy	for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) {
8192255332Scy		nat->nat_hnext[0] = NULL;
8193255332Scy		nat->nat_phnext[0] = NULL;
8194255332Scy		hv = nat->nat_hv[0] % softn->ipf_nat_table_sz;
8195255332Scy
8196255332Scy		natp = &softn->ipf_nat_table[0][hv];
8197255332Scy		if (*natp) {
8198255332Scy			(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
8199255332Scy		} else {
8200255332Scy			NBUMPSIDE(0, ns_inuse);
8201255332Scy		}
8202255332Scy		nat->nat_phnext[0] = natp;
8203255332Scy		nat->nat_hnext[0] = *natp;
8204255332Scy		*natp = nat;
8205255332Scy		NBUMPSIDE(0, ns_bucketlen[hv]);
8206255332Scy
8207255332Scy		nat->nat_hnext[1] = NULL;
8208255332Scy		nat->nat_phnext[1] = NULL;
8209255332Scy		hv = nat->nat_hv[1] % softn->ipf_nat_table_sz;
8210255332Scy
8211255332Scy		natp = &softn->ipf_nat_table[1][hv];
8212255332Scy		if (*natp) {
8213255332Scy			(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
8214255332Scy		} else {
8215255332Scy			NBUMPSIDE(1, ns_inuse);
8216255332Scy		}
8217255332Scy		nat->nat_phnext[1] = natp;
8218255332Scy		nat->nat_hnext[1] = *natp;
8219255332Scy		*natp = nat;
8220255332Scy		NBUMPSIDE(1, ns_bucketlen[hv]);
8221255332Scy	}
8222255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8223255332Scy
8224255332Scy	return 0;
8225255332Scy
8226255332Scybadrehash:
8227255332Scy	if (bucketlens[1] != NULL) {
8228255332Scy		KFREES(bucketlens[0], newsize * sizeof(u_int));
8229255332Scy	}
8230255332Scy	if (bucketlens[0] != NULL) {
8231255332Scy		KFREES(bucketlens[0], newsize * sizeof(u_int));
8232255332Scy	}
8233255332Scy	if (newtab[0] != NULL) {
8234255332Scy		KFREES(newtab[0], newsize * sizeof(nat_t *));
8235255332Scy	}
8236255332Scy	if (newtab[1] != NULL) {
8237255332Scy		KFREES(newtab[1], newsize * sizeof(nat_t *));
8238255332Scy	}
8239255332Scy	IPFERROR(error);
8240255332Scy	return ENOMEM;
8241255332Scy}
8242255332Scy
8243255332Scy
8244255332Scy/* ------------------------------------------------------------------------ */
8245255332Scy/* Function:    ipf_nat_rehash_rules                                        */
8246255332Scy/* Returns:     int  - 0 = success, else failure			    */
8247255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8248255332Scy/*              t(I) - pointer to tunable                                   */
8249255332Scy/*              p(I) - pointer to new tuning data                           */
8250255332Scy/*                                                                          */
8251255332Scy/* All of the NAT rules hang off of a hash table that is searched with a    */
8252255332Scy/* hash on address after the netmask is applied.  There is a different table*/
8253255332Scy/* for both inbound rules (rdr) and outbound (map.)  The resizing will only */
8254255332Scy/* affect one of these two tables.                                          */
8255255332Scy/* ------------------------------------------------------------------------ */
8256255332Scyint
8257255332Scyipf_nat_rehash_rules(softc, t, p)
8258255332Scy	ipf_main_softc_t *softc;
8259255332Scy	ipftuneable_t *t;
8260255332Scy	ipftuneval_t *p;
8261255332Scy{
8262255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8263255332Scy	ipnat_t **newtab, *np, ***old, **npp;
8264255332Scy	u_int newsize;
8265255332Scy	u_int mask;
8266255332Scy	u_int hv;
8267255332Scy
8268255332Scy	newsize = p->ipftu_int;
8269255332Scy	/*
8270255332Scy	 * In case there is nothing to do...
8271255332Scy	 */
8272255332Scy	if (newsize == *t->ipft_pint)
8273255332Scy		return 0;
8274255332Scy
8275255332Scy	/*
8276255332Scy	 * All inbound rules have the NAT_REDIRECT bit set in in_redir and
8277255332Scy	 * all outbound rules have either NAT_MAP or MAT_MAPBLK set.
8278255332Scy	 * This if statement allows for some more generic code to be below,
8279255332Scy	 * rather than two huge gobs of code that almost do the same thing.
8280255332Scy	 */
8281255332Scy	if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) {
8282255332Scy		old = &softn->ipf_nat_rdr_rules;
8283255332Scy		mask = NAT_REDIRECT;
8284255332Scy	} else {
8285255332Scy		old = &softn->ipf_nat_map_rules;
8286255332Scy		mask = NAT_MAP|NAT_MAPBLK;
8287255332Scy	}
8288255332Scy
8289255332Scy	KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *));
8290255332Scy	if (newtab == NULL) {
8291255332Scy		IPFERROR(60067);
8292255332Scy		return ENOMEM;
8293255332Scy	}
8294255332Scy
8295255332Scy	bzero((char *)newtab, newsize * sizeof(ipnat_t *));
8296255332Scy
8297255332Scy	WRITE_ENTER(&softc->ipf_nat);
8298255332Scy
8299255332Scy	if (*old != NULL) {
8300255332Scy		KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **));
8301255332Scy	}
8302255332Scy	*old = newtab;
8303255332Scy	*t->ipft_pint = newsize;
8304255332Scy
8305255332Scy	for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) {
8306255332Scy		if ((np->in_redir & mask) == 0)
8307255332Scy			continue;
8308255332Scy
8309255332Scy		if (np->in_redir & NAT_REDIRECT) {
8310255332Scy			np->in_rnext = NULL;
8311255332Scy			hv = np->in_hv[0] % newsize;
8312255332Scy			for (npp = newtab + hv; *npp != NULL; )
8313255332Scy				npp = &(*npp)->in_rnext;
8314255332Scy			np->in_prnext = npp;
8315255332Scy			*npp = np;
8316255332Scy		}
8317255332Scy		if (np->in_redir & NAT_MAP) {
8318255332Scy			np->in_mnext = NULL;
8319255332Scy			hv = np->in_hv[1] % newsize;
8320255332Scy			for (npp = newtab + hv; *npp != NULL; )
8321255332Scy				npp = &(*npp)->in_mnext;
8322255332Scy			np->in_pmnext = npp;
8323255332Scy			*npp = np;
8324255332Scy		}
8325255332Scy
8326255332Scy	}
8327255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8328255332Scy
8329255332Scy	return 0;
8330255332Scy}
8331255332Scy
8332255332Scy
8333255332Scy/* ------------------------------------------------------------------------ */
8334255332Scy/* Function:    ipf_nat_hostmap_rehash                                      */
8335255332Scy/* Returns:     int  - 0 = success, else failure			    */
8336255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8337255332Scy/*              t(I) - pointer to tunable                                   */
8338255332Scy/*              p(I) - pointer to new tuning data                           */
8339255332Scy/*                                                                          */
8340255332Scy/* Allocate and populate a new hash table that will contain a reference to  */
8341255332Scy/* all of the active IP# translations currently in place.                   */
8342255332Scy/* ------------------------------------------------------------------------ */
8343255332Scyint
8344255332Scyipf_nat_hostmap_rehash(softc, t, p)
8345255332Scy	ipf_main_softc_t *softc;
8346255332Scy	ipftuneable_t *t;
8347255332Scy	ipftuneval_t *p;
8348255332Scy{
8349255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8350255332Scy	hostmap_t *hm, **newtab;
8351255332Scy	u_int newsize;
8352255332Scy	u_int hv;
8353255332Scy
8354255332Scy	newsize = p->ipftu_int;
8355255332Scy	/*
8356255332Scy	 * In case there is nothing to do...
8357255332Scy	 */
8358255332Scy	if (newsize == *t->ipft_pint)
8359255332Scy		return 0;
8360255332Scy
8361255332Scy	KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *));
8362255332Scy	if (newtab == NULL) {
8363255332Scy		IPFERROR(60068);
8364255332Scy		return ENOMEM;
8365255332Scy	}
8366255332Scy
8367255332Scy	bzero((char *)newtab, newsize * sizeof(hostmap_t *));
8368255332Scy
8369255332Scy	WRITE_ENTER(&softc->ipf_nat);
8370255332Scy	if (softn->ipf_hm_maptable != NULL) {
8371255332Scy		KFREES(softn->ipf_hm_maptable,
8372255332Scy		       softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *));
8373255332Scy	}
8374255332Scy	softn->ipf_hm_maptable = newtab;
8375255332Scy	softn->ipf_nat_hostmap_sz = newsize;
8376255332Scy
8377255332Scy	for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) {
8378255332Scy		hv = hm->hm_hv % softn->ipf_nat_hostmap_sz;
8379255332Scy		hm->hm_hnext = softn->ipf_hm_maptable[hv];
8380255332Scy		hm->hm_phnext = softn->ipf_hm_maptable + hv;
8381255332Scy		if (softn->ipf_hm_maptable[hv] != NULL)
8382255332Scy			softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext;
8383255332Scy		softn->ipf_hm_maptable[hv] = hm;
8384255332Scy	}
8385255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8386255332Scy
8387255332Scy	return 0;
8388255332Scy}
8389255332Scy
8390255332Scy
8391255332Scy/* ------------------------------------------------------------------------ */
8392255332Scy/* Function:    ipf_nat_add_tq                                              */
8393255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8394255332Scy/*                                                                          */
8395255332Scy/* ------------------------------------------------------------------------ */
8396255332Scyipftq_t *
8397255332Scyipf_nat_add_tq(softc, ttl)
8398255332Scy	ipf_main_softc_t *softc;
8399255332Scy	int ttl;
8400255332Scy{
8401255332Scy	ipf_nat_softc_t *softs = softc->ipf_nat_soft;
8402255332Scy
8403255332Scy	return ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl);
8404255332Scy}
8405255332Scy
8406255332Scy/* ------------------------------------------------------------------------ */
8407255332Scy/* Function:    ipf_nat_uncreate                                            */
8408255332Scy/* Returns:     Nil                                                         */
8409255332Scy/* Parameters:  fin(I) - pointer to packet information                      */
8410255332Scy/*                                                                          */
8411255332Scy/* This function is used to remove a NAT entry from the NAT table when we   */
8412255332Scy/* decide that the create was actually in error. It is thus assumed that    */
8413255332Scy/* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */
8414255332Scy/* with the translated packet (not the original), we have to reverse the    */
8415255332Scy/* lookup. Although doing the lookup is expensive (relatively speaking), it */
8416255332Scy/* is not anticipated that this will be a frequent occurance for normal     */
8417255332Scy/* traffic patterns.                                                        */
8418255332Scy/* ------------------------------------------------------------------------ */
8419255332Scyvoid
8420255332Scyipf_nat_uncreate(fin)
8421255332Scy	fr_info_t *fin;
8422255332Scy{
8423255332Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
8424255332Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
8425255332Scy	int nflags;
8426255332Scy	nat_t *nat;
8427255332Scy
8428255332Scy	switch (fin->fin_p)
8429255332Scy	{
8430255332Scy	case IPPROTO_TCP :
8431255332Scy		nflags = IPN_TCP;
8432255332Scy		break;
8433255332Scy	case IPPROTO_UDP :
8434255332Scy		nflags = IPN_UDP;
8435255332Scy		break;
8436255332Scy	default :
8437255332Scy		nflags = 0;
8438255332Scy		break;
8439255332Scy	}
8440255332Scy
8441255332Scy	WRITE_ENTER(&softc->ipf_nat);
8442255332Scy
8443255332Scy	if (fin->fin_out == 0) {
8444255332Scy		nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p,
8445255332Scy					fin->fin_dst, fin->fin_src);
8446255332Scy	} else {
8447255332Scy		nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p,
8448255332Scy				       fin->fin_src, fin->fin_dst);
8449255332Scy	}
8450255332Scy
8451255332Scy	if (nat != NULL) {
8452255332Scy		NBUMPSIDE(fin->fin_out, ns_uncreate[0]);
8453255332Scy		ipf_nat_delete(softc, nat, NL_DESTROY);
8454255332Scy	} else {
8455255332Scy		NBUMPSIDE(fin->fin_out, ns_uncreate[1]);
8456255332Scy	}
8457255332Scy
8458255332Scy	RWLOCK_EXIT(&softc->ipf_nat);
8459255332Scy}
8460255332Scy
8461255332Scy
8462255332Scy/* ------------------------------------------------------------------------ */
8463255332Scy/* Function:    ipf_nat_cmp_rules                                           */
8464255332Scy/* Returns:     int   - 0 == success, else rules do not match.              */
8465255332Scy/* Parameters:  n1(I) - first rule to compare                               */
8466255332Scy/*              n2(I) - first rule to compare                               */
8467255332Scy/*                                                                          */
8468255332Scy/* Compare two rules using pointers to each rule. A straight bcmp will not  */
8469255332Scy/* work as some fields (such as in_dst, in_pkts) actually do change once    */
8470255332Scy/* the rule has been loaded into the kernel. Whilst this function returns   */
8471255332Scy/* various non-zero returns, they're strictly to aid in debugging. Use of   */
8472255332Scy/* this function should simply care if the result is zero or not.           */
8473255332Scy/* ------------------------------------------------------------------------ */
8474255332Scystatic int
8475255332Scyipf_nat_cmp_rules(n1, n2)
8476255332Scy	ipnat_t *n1, *n2;
8477255332Scy{
8478255332Scy	if (n1->in_size != n2->in_size)
8479255332Scy		return 1;
8480255332Scy
8481255332Scy	if (bcmp((char *)&n1->in_v, (char *)&n2->in_v,
8482255332Scy		 offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0)
8483255332Scy		return 2;
8484255332Scy
8485255332Scy	if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc,
8486255332Scy		 n1->in_size - offsetof(ipnat_t, in_tuc)) != 0)
8487255332Scy		return 3;
8488255332Scy	if (n1->in_ndst.na_atype != n2->in_ndst.na_atype)
8489255332Scy		return 5;
8490255332Scy	if (n1->in_ndst.na_function != n2->in_ndst.na_function)
8491255332Scy		return 6;
8492255332Scy	if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr,
8493255332Scy		 sizeof(n1->in_ndst.na_addr)))
8494255332Scy		return 7;
8495255332Scy	if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype)
8496255332Scy		return 8;
8497255332Scy	if (n1->in_nsrc.na_function != n2->in_nsrc.na_function)
8498255332Scy		return 9;
8499255332Scy	if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr,
8500255332Scy		 sizeof(n1->in_nsrc.na_addr)))
8501255332Scy		return 10;
8502255332Scy	if (n1->in_odst.na_atype != n2->in_odst.na_atype)
8503255332Scy		return 11;
8504255332Scy	if (n1->in_odst.na_function != n2->in_odst.na_function)
8505255332Scy		return 12;
8506255332Scy	if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr,
8507255332Scy		 sizeof(n1->in_odst.na_addr)))
8508255332Scy		return 13;
8509255332Scy	if (n1->in_osrc.na_atype != n2->in_osrc.na_atype)
8510255332Scy		return 14;
8511255332Scy	if (n1->in_osrc.na_function != n2->in_osrc.na_function)
8512255332Scy		return 15;
8513255332Scy	if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr,
8514255332Scy		 sizeof(n1->in_osrc.na_addr)))
8515255332Scy		return 16;
8516255332Scy	return 0;
8517255332Scy}
8518255332Scy
8519255332Scy
8520255332Scy/* ------------------------------------------------------------------------ */
8521255332Scy/* Function:    ipf_nat_rule_init                                           */
8522255332Scy/* Returns:     int   - 0 == success, else rules do not match.              */
8523255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8524255332Scy/*              softn(I) - pointer to NAT context structure                 */
8525255332Scy/*              n(I)     - first rule to compare                            */
8526255332Scy/*                                                                          */
8527255332Scy/* ------------------------------------------------------------------------ */
8528255332Scystatic int
8529255332Scyipf_nat_rule_init(softc, softn, n)
8530255332Scy	ipf_main_softc_t *softc;
8531255332Scy	ipf_nat_softc_t *softn;
8532255332Scy	ipnat_t *n;
8533255332Scy{
8534255332Scy	int error = 0;
8535255332Scy
8536255332Scy	if ((n->in_flags & IPN_SIPRANGE) != 0)
8537255332Scy		n->in_nsrcatype = FRI_RANGE;
8538255332Scy
8539255332Scy	if ((n->in_flags & IPN_DIPRANGE) != 0)
8540255332Scy		n->in_ndstatype = FRI_RANGE;
8541255332Scy
8542255332Scy	if ((n->in_flags & IPN_SPLIT) != 0)
8543255332Scy		n->in_ndstatype = FRI_SPLIT;
8544255332Scy
8545255332Scy	if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0)
8546255332Scy		n->in_spnext = n->in_spmin;
8547255332Scy
8548255332Scy	if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) {
8549255332Scy		n->in_dpnext = n->in_dpmin;
8550255332Scy	} else if (n->in_redir == NAT_REDIRECT) {
8551255332Scy		n->in_dpnext = n->in_dpmin;
8552255332Scy	}
8553255332Scy
8554255332Scy	n->in_stepnext = 0;
8555255332Scy
8556255332Scy	switch (n->in_v[0])
8557255332Scy	{
8558255332Scy	case 4 :
8559255332Scy		error = ipf_nat_ruleaddrinit(softc, softn, n);
8560255332Scy		if (error != 0)
8561255332Scy			return error;
8562255332Scy		break;
8563255332Scy#ifdef USE_INET6
8564255332Scy	case 6 :
8565255332Scy		error = ipf_nat6_ruleaddrinit(softc, softn, n);
8566255332Scy		if (error != 0)
8567255332Scy			return error;
8568255332Scy		break;
8569255332Scy#endif
8570255332Scy	default :
8571255332Scy		break;
8572255332Scy	}
8573255332Scy
8574255332Scy	if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) {
8575255332Scy		/*
8576255332Scy		 * Prerecord whether or not the destination of the divert
8577255332Scy		 * is local or not to the interface the packet is going
8578255332Scy		 * to be sent out.
8579255332Scy		 */
8580255332Scy		n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1],
8581255332Scy						n->in_ifps[1], &n->in_ndstip6);
8582255332Scy	}
8583255332Scy
8584255332Scy	return error;
8585255332Scy}
8586255332Scy
8587255332Scy
8588255332Scy/* ------------------------------------------------------------------------ */
8589255332Scy/* Function:    ipf_nat_rule_fini                                           */
8590255332Scy/* Returns:     int   - 0 == success, else rules do not match.              */
8591255332Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
8592255332Scy/*              n(I)     - rule to work on                                  */
8593255332Scy/*                                                                          */
8594255332Scy/* This function is used to release any objects that were referenced during */
8595255332Scy/* the rule initialisation. This is useful both when free'ing the rule and  */
8596255332Scy/* when handling ioctls that need to initialise these fields but not        */
8597255332Scy/* actually use them after the ioctl processing has finished.               */
8598255332Scy/* ------------------------------------------------------------------------ */
8599255332Scystatic void
8600255332Scyipf_nat_rule_fini(softc, n)
8601255332Scy	ipf_main_softc_t *softc;
8602255332Scy	ipnat_t *n;
8603255332Scy{
8604255332Scy	if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL)
8605255332Scy		ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr);
8606255332Scy
8607255332Scy	if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL)
8608255332Scy		ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr);
8609255332Scy
8610255332Scy	if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL)
8611255332Scy		ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr);
8612255332Scy
8613255332Scy	if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL)
8614255332Scy		ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr);
8615255332Scy
8616255332Scy	if (n->in_divmp != NULL)
8617255332Scy		FREE_MB_T(n->in_divmp);
8618255332Scy}
8619