xref: /freebsd/sys/netpfil/ipfilter/netinet/ip_nat.c (revision c43829a7)
13b9b51feSCy Schubert 
23b9b51feSCy Schubert /*
33b9b51feSCy Schubert  * Copyright (C) 2012 by Darren Reed.
43b9b51feSCy Schubert  *
53b9b51feSCy Schubert  * See the IPFILTER.LICENCE file for details on licencing.
63b9b51feSCy Schubert  */
73b9b51feSCy Schubert #if defined(KERNEL) || defined(_KERNEL)
83b9b51feSCy Schubert # undef KERNEL
93b9b51feSCy Schubert # undef _KERNEL
103b9b51feSCy Schubert # define        KERNEL	1
113b9b51feSCy Schubert # define        _KERNEL	1
123b9b51feSCy Schubert #endif
133b9b51feSCy Schubert #include <sys/errno.h>
143b9b51feSCy Schubert #include <sys/types.h>
153b9b51feSCy Schubert #include <sys/param.h>
163b9b51feSCy Schubert #include <sys/time.h>
173b9b51feSCy Schubert #include <sys/file.h>
183b9b51feSCy Schubert #if defined(_KERNEL) && \
193b9b51feSCy Schubert     (defined(__NetBSD_Version) && (__NetBSD_Version >= 399002000))
203b9b51feSCy Schubert # include <sys/kauth.h>
213b9b51feSCy Schubert #endif
223b9b51feSCy Schubert #if !defined(_KERNEL)
233b9b51feSCy Schubert # include <stdio.h>
243b9b51feSCy Schubert # include <string.h>
253b9b51feSCy Schubert # include <stdlib.h>
263b9b51feSCy Schubert # define KERNEL
273b9b51feSCy Schubert # ifdef _OpenBSD__
283b9b51feSCy Schubert struct file;
293b9b51feSCy Schubert # endif
303b9b51feSCy Schubert # include <sys/uio.h>
313b9b51feSCy Schubert # undef KERNEL
323b9b51feSCy Schubert #endif
333b9b51feSCy Schubert #if defined(_KERNEL) && defined(__FreeBSD__)
343b9b51feSCy Schubert # include <sys/filio.h>
353b9b51feSCy Schubert # include <sys/fcntl.h>
363b9b51feSCy Schubert #else
373b9b51feSCy Schubert # include <sys/ioctl.h>
383b9b51feSCy Schubert #endif
393b9b51feSCy Schubert # include <sys/fcntl.h>
403b9b51feSCy Schubert # include <sys/protosw.h>
413b9b51feSCy Schubert #include <sys/socket.h>
423b9b51feSCy Schubert #if defined(_KERNEL)
433b9b51feSCy Schubert # include <sys/systm.h>
44c47db49bSCy Schubert # if defined(__FreeBSD__)
45c47db49bSCy Schubert #  include <sys/jail.h>
46c47db49bSCy Schubert # endif
473b9b51feSCy Schubert # if !defined(__SVR4)
483b9b51feSCy Schubert #  include <sys/mbuf.h>
493b9b51feSCy Schubert # endif
503b9b51feSCy Schubert #endif
513b9b51feSCy Schubert #if defined(__SVR4)
523b9b51feSCy Schubert # include <sys/filio.h>
533b9b51feSCy Schubert # include <sys/byteorder.h>
543b9b51feSCy Schubert # ifdef KERNEL
553b9b51feSCy Schubert #  include <sys/dditypes.h>
563b9b51feSCy Schubert # endif
573b9b51feSCy Schubert # include <sys/stream.h>
583b9b51feSCy Schubert # include <sys/kmem.h>
593b9b51feSCy Schubert #endif
603b9b51feSCy Schubert #if defined(__FreeBSD__)
613b9b51feSCy Schubert # include <sys/queue.h>
623b9b51feSCy Schubert #endif
633b9b51feSCy Schubert #include <net/if.h>
643b9b51feSCy Schubert #if defined(__FreeBSD__)
653b9b51feSCy Schubert # include <net/if_var.h>
663b9b51feSCy Schubert #endif
673b9b51feSCy Schubert #ifdef sun
683b9b51feSCy Schubert # include <net/af.h>
693b9b51feSCy Schubert #endif
703b9b51feSCy Schubert #include <netinet/in.h>
713b9b51feSCy Schubert #include <netinet/in_systm.h>
723b9b51feSCy Schubert #include <netinet/ip.h>
733b9b51feSCy Schubert 
743b9b51feSCy Schubert #ifdef RFC1825
753b9b51feSCy Schubert # include <vpn/md5.h>
763b9b51feSCy Schubert # include <vpn/ipsec.h>
773b9b51feSCy Schubert extern struct ifnet vpnif;
783b9b51feSCy Schubert #endif
793b9b51feSCy Schubert 
803b9b51feSCy Schubert # include <netinet/ip_var.h>
813b9b51feSCy Schubert #include <netinet/tcp.h>
823b9b51feSCy Schubert #include <netinet/udp.h>
833b9b51feSCy Schubert #include <netinet/ip_icmp.h>
843b9b51feSCy Schubert #include "netinet/ip_compat.h"
853b9b51feSCy Schubert #include <netinet/tcpip.h>
863b9b51feSCy Schubert #include "netinet/ipl.h"
873b9b51feSCy Schubert #include "netinet/ip_fil.h"
883b9b51feSCy Schubert #include "netinet/ip_nat.h"
893b9b51feSCy Schubert #include "netinet/ip_frag.h"
903b9b51feSCy Schubert #include "netinet/ip_state.h"
913b9b51feSCy Schubert #include "netinet/ip_proxy.h"
923b9b51feSCy Schubert #include "netinet/ip_lookup.h"
933b9b51feSCy Schubert #include "netinet/ip_dstlist.h"
943b9b51feSCy Schubert #include "netinet/ip_sync.h"
953b9b51feSCy Schubert #if defined(__FreeBSD__)
963b9b51feSCy Schubert # include <sys/malloc.h>
973b9b51feSCy Schubert #endif
983b9b51feSCy Schubert #ifdef HAS_SYS_MD5_H
993b9b51feSCy Schubert # include <sys/md5.h>
1003b9b51feSCy Schubert #else
1013b9b51feSCy Schubert # include "md5.h"
1023b9b51feSCy Schubert #endif
1033b9b51feSCy Schubert /* END OF INCLUDES */
1043b9b51feSCy Schubert 
1053b9b51feSCy Schubert #undef	SOCKADDR_IN
1063b9b51feSCy Schubert #define	SOCKADDR_IN	struct sockaddr_in
1073b9b51feSCy Schubert 
1083b9b51feSCy Schubert 
1093b9b51feSCy Schubert 
1103b9b51feSCy Schubert #define	NATFSUM(n,v,f)	((v) == 4 ? (n)->f.in4.s_addr : (n)->f.i6[0] + \
1113b9b51feSCy Schubert 			 (n)->f.i6[1] + (n)->f.i6[2] + (n)->f.i6[3])
1123b9b51feSCy Schubert #define	NBUMP(x)	softn->(x)++
1133b9b51feSCy Schubert #define	NBUMPD(x, y)	do { \
1143b9b51feSCy Schubert 				softn->x.y++; \
1153b9b51feSCy Schubert 				DT(y); \
1163b9b51feSCy Schubert 			} while (0)
1173b9b51feSCy Schubert #define	NBUMPSIDE(y,x)	softn->ipf_nat_stats.ns_side[y].x++
1183b9b51feSCy Schubert #define	NBUMPSIDED(y,x)	do { softn->ipf_nat_stats.ns_side[y].x++; \
1193b9b51feSCy Schubert 			     DT(x); } while (0)
1203b9b51feSCy Schubert #define	NBUMPSIDEX(y,x,z) \
1213b9b51feSCy Schubert 			do { softn->ipf_nat_stats.ns_side[y].x++; \
1223b9b51feSCy Schubert 			     DT(z); } while (0)
1233b9b51feSCy Schubert #define	NBUMPSIDEDF(y,x)do { softn->ipf_nat_stats.ns_side[y].x++; \
1243b9b51feSCy Schubert 			     DT1(x, fr_info_t *, fin); } while (0)
1253b9b51feSCy Schubert 
1263b9b51feSCy Schubert static ipftuneable_t ipf_nat_tuneables[] = {
1273b9b51feSCy Schubert 	/* nat */
1283b9b51feSCy Schubert 	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_lock) },
1293b9b51feSCy Schubert 		"nat_lock",	0,	1,
1303b9b51feSCy Schubert 		stsizeof(ipf_nat_softc_t, ipf_nat_lock),
1313b9b51feSCy Schubert 		IPFT_RDONLY,		NULL,	NULL },
1323b9b51feSCy Schubert 	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_sz) },
1333b9b51feSCy Schubert 		"nat_table_size", 1,	0x7fffffff,
1343b9b51feSCy Schubert 		stsizeof(ipf_nat_softc_t, ipf_nat_table_sz),
1353b9b51feSCy Schubert 		0,			NULL,	ipf_nat_rehash },
1363b9b51feSCy Schubert 	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_max) },
1373b9b51feSCy Schubert 		"nat_table_max", 1,	0x7fffffff,
1383b9b51feSCy Schubert 		stsizeof(ipf_nat_softc_t, ipf_nat_table_max),
1393b9b51feSCy Schubert 		0,			NULL,	NULL },
1403b9b51feSCy Schubert 	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maprules_sz) },
1413b9b51feSCy Schubert 		"nat_rules_size", 1,	0x7fffffff,
1423b9b51feSCy Schubert 		stsizeof(ipf_nat_softc_t, ipf_nat_maprules_sz),
1433b9b51feSCy Schubert 		0,			NULL,	ipf_nat_rehash_rules },
1443b9b51feSCy Schubert 	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_rdrrules_sz) },
1453b9b51feSCy Schubert 		"rdr_rules_size", 1,	0x7fffffff,
1463b9b51feSCy Schubert 		stsizeof(ipf_nat_softc_t, ipf_nat_rdrrules_sz),
1473b9b51feSCy Schubert 		0,			NULL,	ipf_nat_rehash_rules },
1483b9b51feSCy Schubert 	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_hostmap_sz) },
1493b9b51feSCy Schubert 		"hostmap_size",	1,	0x7fffffff,
1503b9b51feSCy Schubert 		stsizeof(ipf_nat_softc_t, ipf_nat_hostmap_sz),
1513b9b51feSCy Schubert 		0,			NULL,	ipf_nat_hostmap_rehash },
1523b9b51feSCy Schubert 	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_maxbucket) },
1533b9b51feSCy Schubert 		"nat_maxbucket",1,	0x7fffffff,
1543b9b51feSCy Schubert 		stsizeof(ipf_nat_softc_t, ipf_nat_maxbucket),
1553b9b51feSCy Schubert 		0,			NULL,	NULL },
1563b9b51feSCy Schubert 	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_logging) },
1573b9b51feSCy Schubert 		"nat_logging",	0,	1,
1583b9b51feSCy Schubert 		stsizeof(ipf_nat_softc_t, ipf_nat_logging),
1593b9b51feSCy Schubert 		0,			NULL,	NULL },
1603b9b51feSCy Schubert 	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_doflush) },
1613b9b51feSCy Schubert 		"nat_doflush",	0,	1,
1623b9b51feSCy Schubert 		stsizeof(ipf_nat_softc_t, ipf_nat_doflush),
1633b9b51feSCy Schubert 		0,			NULL,	NULL },
1643b9b51feSCy Schubert 	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_low) },
1653b9b51feSCy Schubert 		"nat_table_wm_low",	1,	99,
1663b9b51feSCy Schubert 		stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_low),
1673b9b51feSCy Schubert 		0,			NULL,	NULL },
1683b9b51feSCy Schubert 	{ { (void *)offsetof(ipf_nat_softc_t, ipf_nat_table_wm_high) },
1693b9b51feSCy Schubert 		"nat_table_wm_high",	2,	100,
1703b9b51feSCy Schubert 		stsizeof(ipf_nat_softc_t, ipf_nat_table_wm_high),
1713b9b51feSCy Schubert 		0,			NULL,	NULL },
1723b9b51feSCy Schubert 	{ { 0 },
1733b9b51feSCy Schubert 		NULL,			0,	0,
1743b9b51feSCy Schubert 		0,
1753b9b51feSCy Schubert 		0,			NULL,	NULL }
1763b9b51feSCy Schubert };
1773b9b51feSCy Schubert 
1783b9b51feSCy Schubert /* ======================================================================== */
1793b9b51feSCy Schubert /* How the NAT is organised and works.                                      */
1803b9b51feSCy Schubert /*                                                                          */
1813b9b51feSCy Schubert /* Inside (interface y) NAT       Outside (interface x)                     */
1823b9b51feSCy Schubert /* -------------------- -+- -------------------------------------           */
183c43829a7SJose Luis Duran /* Packet going          |   out, processed by ipf_nat_checkout() for x     */
1843b9b51feSCy Schubert /* ------------>         |   ------------>                                  */
1853b9b51feSCy Schubert /* src=10.1.1.1          |   src=192.1.1.1                                  */
1863b9b51feSCy Schubert /*                       |                                                  */
1873b9b51feSCy Schubert /*                       |   in, processed by ipf_nat_checkin() for x       */
1883b9b51feSCy Schubert /* <------------         |   <------------                                  */
1893b9b51feSCy Schubert /* dst=10.1.1.1          |   dst=192.1.1.1                                  */
1903b9b51feSCy Schubert /* -------------------- -+- -------------------------------------           */
1913b9b51feSCy Schubert /* ipf_nat_checkout() - changes ip_src and if required, sport               */
1923b9b51feSCy Schubert /*             - creates a new mapping, if required.                        */
1933b9b51feSCy Schubert /* ipf_nat_checkin()  - changes ip_dst and if required, dport               */
1943b9b51feSCy Schubert /*                                                                          */
1953b9b51feSCy Schubert /* In the NAT table, internal source is recorded as "in" and externally     */
1963b9b51feSCy Schubert /* seen as "out".                                                           */
1973b9b51feSCy Schubert /* ======================================================================== */
1983b9b51feSCy Schubert 
1993b9b51feSCy Schubert 
2003b9b51feSCy Schubert #if SOLARIS && !defined(INSTANCES)
2013b9b51feSCy Schubert extern	int		pfil_delayed_copy;
2023b9b51feSCy Schubert #endif
2033b9b51feSCy Schubert 
2043b9b51feSCy Schubert static	int	ipf_nat_flush_entry(ipf_main_softc_t *, void *);
2053b9b51feSCy Schubert static	int	ipf_nat_getent(ipf_main_softc_t *, caddr_t, int);
2063b9b51feSCy Schubert static	int	ipf_nat_getsz(ipf_main_softc_t *, caddr_t, int);
2073b9b51feSCy Schubert static	int	ipf_nat_putent(ipf_main_softc_t *, caddr_t, int);
2083b9b51feSCy Schubert static	void	ipf_nat_addmap(ipf_nat_softc_t *, ipnat_t *);
2093b9b51feSCy Schubert static	void	ipf_nat_addrdr(ipf_nat_softc_t *, ipnat_t *);
2103b9b51feSCy Schubert static	int	ipf_nat_builddivertmp(ipf_nat_softc_t *, ipnat_t *);
2113b9b51feSCy Schubert static	int	ipf_nat_clearlist(ipf_main_softc_t *, ipf_nat_softc_t *);
2123b9b51feSCy Schubert static	int	ipf_nat_cmp_rules(ipnat_t *, ipnat_t *);
2133b9b51feSCy Schubert static	int	ipf_nat_decap(fr_info_t *, nat_t *);
2143b9b51feSCy Schubert static	void	ipf_nat_delrule(ipf_main_softc_t *, ipf_nat_softc_t *,
2153b9b51feSCy Schubert 				     ipnat_t *, int);
2163b9b51feSCy Schubert static	int	ipf_nat_extraflush(ipf_main_softc_t *, ipf_nat_softc_t *, int);
2173b9b51feSCy Schubert static	int	ipf_nat_finalise(fr_info_t *, nat_t *);
2183b9b51feSCy Schubert static	int	ipf_nat_flushtable(ipf_main_softc_t *, ipf_nat_softc_t *);
2193b9b51feSCy Schubert static	int	ipf_nat_getnext(ipf_main_softc_t *, ipftoken_t *,
2203b9b51feSCy Schubert 				     ipfgeniter_t *, ipfobj_t *);
2213b9b51feSCy Schubert static	int	ipf_nat_gettable(ipf_main_softc_t *, ipf_nat_softc_t *,
2223b9b51feSCy Schubert 				      char *);
2233b9b51feSCy Schubert static	hostmap_t *ipf_nat_hostmap(ipf_nat_softc_t *, ipnat_t *,
2243b9b51feSCy Schubert 					struct in_addr, struct in_addr,
2253b9b51feSCy Schubert 					struct in_addr, u_32_t);
2263b9b51feSCy Schubert static	int	ipf_nat_icmpquerytype(int);
2273b9b51feSCy Schubert static	int	ipf_nat_iterator(ipf_main_softc_t *, ipftoken_t *,
2283b9b51feSCy Schubert 				      ipfgeniter_t *, ipfobj_t *);
2293b9b51feSCy Schubert static	int	ipf_nat_match(fr_info_t *, ipnat_t *);
2303b9b51feSCy Schubert static	int	ipf_nat_matcharray(nat_t *, int *, u_long);
2313b9b51feSCy Schubert static	int	ipf_nat_matchflush(ipf_main_softc_t *, ipf_nat_softc_t *,
2323b9b51feSCy Schubert 					caddr_t);
2333b9b51feSCy Schubert static	void	ipf_nat_mssclamp(tcphdr_t *, u_32_t, fr_info_t *,
2343b9b51feSCy Schubert 				      u_short *);
2353b9b51feSCy Schubert static	int	ipf_nat_newmap(fr_info_t *, nat_t *, natinfo_t *);
2363b9b51feSCy Schubert static	int	ipf_nat_newdivert(fr_info_t *, nat_t *, natinfo_t *);
2373b9b51feSCy Schubert static	int	ipf_nat_newrdr(fr_info_t *, nat_t *, natinfo_t *);
2383b9b51feSCy Schubert static	int	ipf_nat_newrewrite(fr_info_t *, nat_t *, natinfo_t *);
2393b9b51feSCy Schubert static	int	ipf_nat_nextaddr(fr_info_t *, nat_addr_t *, u_32_t *,
2403b9b51feSCy Schubert 				      u_32_t *);
2413b9b51feSCy Schubert static	int	ipf_nat_nextaddrinit(ipf_main_softc_t *, char *,
2423b9b51feSCy Schubert 					  nat_addr_t *, int, void *);
2433b9b51feSCy Schubert static	int	ipf_nat_resolverule(ipf_main_softc_t *, ipnat_t *);
2443b9b51feSCy Schubert static	int	ipf_nat_ruleaddrinit(ipf_main_softc_t *,
2453b9b51feSCy Schubert 					  ipf_nat_softc_t *, ipnat_t *);
2463b9b51feSCy Schubert static	void	ipf_nat_rule_fini(ipf_main_softc_t *, ipnat_t *);
2473b9b51feSCy Schubert static	int	ipf_nat_rule_init(ipf_main_softc_t *, ipf_nat_softc_t *,
2483b9b51feSCy Schubert 				       ipnat_t *);
2493b9b51feSCy Schubert static	int	ipf_nat_siocaddnat(ipf_main_softc_t *, ipf_nat_softc_t *,
2503b9b51feSCy Schubert 					ipnat_t *, int);
2513b9b51feSCy Schubert static	void	ipf_nat_siocdelnat(ipf_main_softc_t *, ipf_nat_softc_t *,
2523b9b51feSCy Schubert 					ipnat_t *, int);
2533b9b51feSCy Schubert static	void	ipf_nat_tabmove(ipf_nat_softc_t *, nat_t *);
2543b9b51feSCy Schubert 
2553b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
2563b9b51feSCy Schubert /* Function:    ipf_nat_main_load                                           */
2573b9b51feSCy Schubert /* Returns:     int - 0 == success, -1 == failure                           */
2583b9b51feSCy Schubert /* Parameters:  Nil                                                         */
2593b9b51feSCy Schubert /*                                                                          */
2603b9b51feSCy Schubert /* The only global NAT structure that needs to be initialised is the filter */
2613b9b51feSCy Schubert /* rule that is used with blocking packets.                                 */
2623b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
2633b9b51feSCy Schubert int
ipf_nat_main_load(void)264064a5a95SCy Schubert ipf_nat_main_load(void)
2653b9b51feSCy Schubert {
2663b9b51feSCy Schubert 
2678c82b374SCy Schubert 	return (0);
2683b9b51feSCy Schubert }
2693b9b51feSCy Schubert 
2703b9b51feSCy Schubert 
2713b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
2723b9b51feSCy Schubert /* Function:    ipf_nat_main_unload                                         */
2733b9b51feSCy Schubert /* Returns:     int - 0 == success, -1 == failure                           */
2743b9b51feSCy Schubert /* Parameters:  Nil                                                         */
2753b9b51feSCy Schubert /*                                                                          */
2763b9b51feSCy Schubert /* A null-op function that exists as a placeholder so that the flow in      */
2773b9b51feSCy Schubert /* other functions is obvious.                                              */
2783b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
2793b9b51feSCy Schubert int
ipf_nat_main_unload(void)280064a5a95SCy Schubert ipf_nat_main_unload(void)
2813b9b51feSCy Schubert {
2828c82b374SCy Schubert 	return (0);
2833b9b51feSCy Schubert }
2843b9b51feSCy Schubert 
2853b9b51feSCy Schubert 
2863b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
2873b9b51feSCy Schubert /* Function:    ipf_nat_soft_create                                         */
2883b9b51feSCy Schubert /* Returns:     void * - NULL = failure, else pointer to NAT context        */
2893b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
2903b9b51feSCy Schubert /*                                                                          */
2913b9b51feSCy Schubert /* Allocate the initial soft context structure for NAT and populate it with */
2923b9b51feSCy Schubert /* some default values. Creating the tables is left until we call _init so  */
2933b9b51feSCy Schubert /* that sizes can be changed before we get under way.                       */
2943b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
2953b9b51feSCy Schubert void *
ipf_nat_soft_create(ipf_main_softc_t * softc)296064a5a95SCy Schubert ipf_nat_soft_create(ipf_main_softc_t *softc)
2973b9b51feSCy Schubert {
2983b9b51feSCy Schubert 	ipf_nat_softc_t *softn;
2993b9b51feSCy Schubert 
3003b9b51feSCy Schubert 	KMALLOC(softn, ipf_nat_softc_t *);
3013b9b51feSCy Schubert 	if (softn == NULL)
3028c82b374SCy Schubert 		return (NULL);
3033b9b51feSCy Schubert 
3043b9b51feSCy Schubert 	bzero((char *)softn, sizeof(*softn));
3053b9b51feSCy Schubert 
3063b9b51feSCy Schubert 	softn->ipf_nat_tune = ipf_tune_array_copy(softn,
3073b9b51feSCy Schubert 						  sizeof(ipf_nat_tuneables),
3083b9b51feSCy Schubert 						  ipf_nat_tuneables);
3093b9b51feSCy Schubert 	if (softn->ipf_nat_tune == NULL) {
3103b9b51feSCy Schubert 		ipf_nat_soft_destroy(softc, softn);
3118c82b374SCy Schubert 		return (NULL);
3123b9b51feSCy Schubert 	}
3133b9b51feSCy Schubert 	if (ipf_tune_array_link(softc, softn->ipf_nat_tune) == -1) {
3143b9b51feSCy Schubert 		ipf_nat_soft_destroy(softc, softn);
3158c82b374SCy Schubert 		return (NULL);
3163b9b51feSCy Schubert 	}
3173b9b51feSCy Schubert 
3183b9b51feSCy Schubert 	softn->ipf_nat_list_tail = &softn->ipf_nat_list;
3193b9b51feSCy Schubert 
3203b9b51feSCy Schubert 	if (softc->ipf_large_nat) {
3213b9b51feSCy Schubert 	softn->ipf_nat_table_max = NAT_TABLE_MAX_LARGE;
3223b9b51feSCy Schubert 	softn->ipf_nat_table_sz = NAT_TABLE_SZ_LARGE;
3233b9b51feSCy Schubert 	softn->ipf_nat_maprules_sz = NAT_SIZE_LARGE;
3243b9b51feSCy Schubert 	softn->ipf_nat_rdrrules_sz = RDR_SIZE_LARGE;
3253b9b51feSCy Schubert 	softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE_LARGE;
3263b9b51feSCy Schubert 	} else {
3273b9b51feSCy Schubert 	softn->ipf_nat_table_max = NAT_TABLE_MAX_NORMAL;
3283b9b51feSCy Schubert 	softn->ipf_nat_table_sz = NAT_TABLE_SZ_NORMAL;
3293b9b51feSCy Schubert 	softn->ipf_nat_maprules_sz = NAT_SIZE_NORMAL;
3303b9b51feSCy Schubert 	softn->ipf_nat_rdrrules_sz = RDR_SIZE_NORMAL;
3313b9b51feSCy Schubert 	softn->ipf_nat_hostmap_sz = HOSTMAP_SIZE_NORMAL;
3323b9b51feSCy Schubert 	}
3333b9b51feSCy Schubert 	softn->ipf_nat_doflush = 0;
3343b9b51feSCy Schubert #ifdef  IPFILTER_LOG
3353b9b51feSCy Schubert 	softn->ipf_nat_logging = 1;
3363b9b51feSCy Schubert #else
3373b9b51feSCy Schubert 	softn->ipf_nat_logging = 0;
3383b9b51feSCy Schubert #endif
3393b9b51feSCy Schubert 
3403b9b51feSCy Schubert 	softn->ipf_nat_defage = DEF_NAT_AGE;
3413b9b51feSCy Schubert 	softn->ipf_nat_defipage = IPF_TTLVAL(60);
3423b9b51feSCy Schubert 	softn->ipf_nat_deficmpage = IPF_TTLVAL(3);
3433b9b51feSCy Schubert 	softn->ipf_nat_table_wm_high = 99;
3443b9b51feSCy Schubert 	softn->ipf_nat_table_wm_low = 90;
3453b9b51feSCy Schubert 
3468c82b374SCy Schubert 	return (softn);
3473b9b51feSCy Schubert }
3483b9b51feSCy Schubert 
3493b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
3503b9b51feSCy Schubert /* Function:    ipf_nat_soft_destroy                                        */
3513b9b51feSCy Schubert /* Returns:     Nil                                                         */
3523b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
3533b9b51feSCy Schubert /*                                                                          */
3543b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
3553b9b51feSCy Schubert void
ipf_nat_soft_destroy(ipf_main_softc_t * softc,void * arg)356064a5a95SCy Schubert ipf_nat_soft_destroy(ipf_main_softc_t *softc, void *arg)
3573b9b51feSCy Schubert {
3583b9b51feSCy Schubert 	ipf_nat_softc_t *softn = arg;
3593b9b51feSCy Schubert 
3603b9b51feSCy Schubert 	if (softn->ipf_nat_tune != NULL) {
3613b9b51feSCy Schubert 		ipf_tune_array_unlink(softc, softn->ipf_nat_tune);
3623b9b51feSCy Schubert 		KFREES(softn->ipf_nat_tune, sizeof(ipf_nat_tuneables));
3633b9b51feSCy Schubert 		softn->ipf_nat_tune = NULL;
3643b9b51feSCy Schubert 	}
3653b9b51feSCy Schubert 
3663b9b51feSCy Schubert 	KFREE(softn);
3673b9b51feSCy Schubert }
3683b9b51feSCy Schubert 
3693b9b51feSCy Schubert 
3703b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
3713b9b51feSCy Schubert /* Function:    ipf_nat_init                                                */
3723b9b51feSCy Schubert /* Returns:     int - 0 == success, -1 == failure                           */
3733b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
3743b9b51feSCy Schubert /*                                                                          */
3753b9b51feSCy Schubert /* Initialise all of the NAT locks, tables and other structures.            */
3763b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
3773b9b51feSCy Schubert int
ipf_nat_soft_init(ipf_main_softc_t * softc,void * arg)378064a5a95SCy Schubert ipf_nat_soft_init(ipf_main_softc_t *softc, void *arg)
3793b9b51feSCy Schubert {
3803b9b51feSCy Schubert 	ipf_nat_softc_t *softn = arg;
3813b9b51feSCy Schubert 	ipftq_t *tq;
3823b9b51feSCy Schubert 	int i;
3833b9b51feSCy Schubert 
3843b9b51feSCy Schubert 	KMALLOCS(softn->ipf_nat_table[0], nat_t **, \
3853b9b51feSCy Schubert 		 sizeof(nat_t *) * softn->ipf_nat_table_sz);
3863b9b51feSCy Schubert 
3873b9b51feSCy Schubert 	if (softn->ipf_nat_table[0] != NULL) {
3883b9b51feSCy Schubert 		bzero((char *)softn->ipf_nat_table[0],
3893b9b51feSCy Schubert 		      softn->ipf_nat_table_sz * sizeof(nat_t *));
3903b9b51feSCy Schubert 	} else {
3918c82b374SCy Schubert 		return (-1);
3923b9b51feSCy Schubert 	}
3933b9b51feSCy Schubert 
3943b9b51feSCy Schubert 	KMALLOCS(softn->ipf_nat_table[1], nat_t **, \
3953b9b51feSCy Schubert 		 sizeof(nat_t *) * softn->ipf_nat_table_sz);
3963b9b51feSCy Schubert 
3973b9b51feSCy Schubert 	if (softn->ipf_nat_table[1] != NULL) {
3983b9b51feSCy Schubert 		bzero((char *)softn->ipf_nat_table[1],
3993b9b51feSCy Schubert 		      softn->ipf_nat_table_sz * sizeof(nat_t *));
4003b9b51feSCy Schubert 	} else {
4018c82b374SCy Schubert 		return (-2);
4023b9b51feSCy Schubert 	}
4033b9b51feSCy Schubert 
4043b9b51feSCy Schubert 	KMALLOCS(softn->ipf_nat_map_rules, ipnat_t **, \
4053b9b51feSCy Schubert 		 sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz);
4063b9b51feSCy Schubert 
4073b9b51feSCy Schubert 	if (softn->ipf_nat_map_rules != NULL) {
4083b9b51feSCy Schubert 		bzero((char *)softn->ipf_nat_map_rules,
4093b9b51feSCy Schubert 		      softn->ipf_nat_maprules_sz * sizeof(ipnat_t *));
4103b9b51feSCy Schubert 	} else {
4118c82b374SCy Schubert 		return (-3);
4123b9b51feSCy Schubert 	}
4133b9b51feSCy Schubert 
4143b9b51feSCy Schubert 	KMALLOCS(softn->ipf_nat_rdr_rules, ipnat_t **, \
4153b9b51feSCy Schubert 		 sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz);
4163b9b51feSCy Schubert 
4173b9b51feSCy Schubert 	if (softn->ipf_nat_rdr_rules != NULL) {
4183b9b51feSCy Schubert 		bzero((char *)softn->ipf_nat_rdr_rules,
4193b9b51feSCy Schubert 		      softn->ipf_nat_rdrrules_sz * sizeof(ipnat_t *));
4203b9b51feSCy Schubert 	} else {
4218c82b374SCy Schubert 		return (-4);
4223b9b51feSCy Schubert 	}
4233b9b51feSCy Schubert 
4243b9b51feSCy Schubert 	KMALLOCS(softn->ipf_hm_maptable, hostmap_t **, \
4253b9b51feSCy Schubert 		 sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz);
4263b9b51feSCy Schubert 
4273b9b51feSCy Schubert 	if (softn->ipf_hm_maptable != NULL) {
4283b9b51feSCy Schubert 		bzero((char *)softn->ipf_hm_maptable,
4293b9b51feSCy Schubert 		      sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz);
4303b9b51feSCy Schubert 	} else {
4318c82b374SCy Schubert 		return (-5);
4323b9b51feSCy Schubert 	}
4333b9b51feSCy Schubert 	softn->ipf_hm_maplist = NULL;
4343b9b51feSCy Schubert 
4353b9b51feSCy Schubert 	KMALLOCS(softn->ipf_nat_stats.ns_side[0].ns_bucketlen, u_int *,
4363b9b51feSCy Schubert 		 softn->ipf_nat_table_sz * sizeof(u_int));
4373b9b51feSCy Schubert 
4383b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen == NULL) {
4398c82b374SCy Schubert 		return (-6);
4403b9b51feSCy Schubert 	}
4413b9b51feSCy Schubert 	bzero((char *)softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
4423b9b51feSCy Schubert 	      softn->ipf_nat_table_sz * sizeof(u_int));
4433b9b51feSCy Schubert 
4443b9b51feSCy Schubert 	KMALLOCS(softn->ipf_nat_stats.ns_side[1].ns_bucketlen, u_int *,
4453b9b51feSCy Schubert 		 softn->ipf_nat_table_sz * sizeof(u_int));
4463b9b51feSCy Schubert 
4473b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen == NULL) {
4488c82b374SCy Schubert 		return (-7);
4493b9b51feSCy Schubert 	}
4503b9b51feSCy Schubert 
4513b9b51feSCy Schubert 	bzero((char *)softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
4523b9b51feSCy Schubert 	      softn->ipf_nat_table_sz * sizeof(u_int));
4533b9b51feSCy Schubert 
4543b9b51feSCy Schubert 	if (softn->ipf_nat_maxbucket == 0) {
4553b9b51feSCy Schubert 		for (i = softn->ipf_nat_table_sz; i > 0; i >>= 1)
4563b9b51feSCy Schubert 			softn->ipf_nat_maxbucket++;
4573b9b51feSCy Schubert 		softn->ipf_nat_maxbucket *= 2;
4583b9b51feSCy Schubert 	}
4593b9b51feSCy Schubert 
4603b9b51feSCy Schubert 	ipf_sttab_init(softc, softn->ipf_nat_tcptq);
4613b9b51feSCy Schubert 	/*
4623b9b51feSCy Schubert 	 * Increase this because we may have "keep state" following this too
4633b9b51feSCy Schubert 	 * and packet storms can occur if this is removed too quickly.
4643b9b51feSCy Schubert 	 */
4653b9b51feSCy Schubert 	softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack;
4663b9b51feSCy Schubert 	softn->ipf_nat_tcptq[IPF_TCP_NSTATES - 1].ifq_next =
4673b9b51feSCy Schubert 							&softn->ipf_nat_udptq;
4683b9b51feSCy Schubert 
4693b9b51feSCy Schubert 	IPFTQ_INIT(&softn->ipf_nat_udptq, softn->ipf_nat_defage,
4703b9b51feSCy Schubert 		   "nat ipftq udp tab");
4713b9b51feSCy Schubert 	softn->ipf_nat_udptq.ifq_next = &softn->ipf_nat_udpacktq;
4723b9b51feSCy Schubert 
4733b9b51feSCy Schubert 	IPFTQ_INIT(&softn->ipf_nat_udpacktq, softn->ipf_nat_defage,
4743b9b51feSCy Schubert 		   "nat ipftq udpack tab");
4753b9b51feSCy Schubert 	softn->ipf_nat_udpacktq.ifq_next = &softn->ipf_nat_icmptq;
4763b9b51feSCy Schubert 
4773b9b51feSCy Schubert 	IPFTQ_INIT(&softn->ipf_nat_icmptq, softn->ipf_nat_deficmpage,
4783b9b51feSCy Schubert 		   "nat icmp ipftq tab");
4793b9b51feSCy Schubert 	softn->ipf_nat_icmptq.ifq_next = &softn->ipf_nat_icmpacktq;
4803b9b51feSCy Schubert 
4813b9b51feSCy Schubert 	IPFTQ_INIT(&softn->ipf_nat_icmpacktq, softn->ipf_nat_defage,
4823b9b51feSCy Schubert 		   "nat icmpack ipftq tab");
4833b9b51feSCy Schubert 	softn->ipf_nat_icmpacktq.ifq_next = &softn->ipf_nat_iptq;
4843b9b51feSCy Schubert 
4853b9b51feSCy Schubert 	IPFTQ_INIT(&softn->ipf_nat_iptq, softn->ipf_nat_defipage,
4863b9b51feSCy Schubert 		   "nat ip ipftq tab");
4873b9b51feSCy Schubert 	softn->ipf_nat_iptq.ifq_next = &softn->ipf_nat_pending;
4883b9b51feSCy Schubert 
4893b9b51feSCy Schubert 	IPFTQ_INIT(&softn->ipf_nat_pending, 1, "nat pending ipftq tab");
4903b9b51feSCy Schubert 	softn->ipf_nat_pending.ifq_next = NULL;
4913b9b51feSCy Schubert 
4923b9b51feSCy Schubert 	for (i = 0, tq = softn->ipf_nat_tcptq; i < IPF_TCP_NSTATES; i++, tq++) {
4933b9b51feSCy Schubert 		if (tq->ifq_ttl < softn->ipf_nat_deficmpage)
4943b9b51feSCy Schubert 			tq->ifq_ttl = softn->ipf_nat_deficmpage;
4953b9b51feSCy Schubert 		else if (tq->ifq_ttl > softn->ipf_nat_defage && softc->ipf_large_nat)
4963b9b51feSCy Schubert 			tq->ifq_ttl = softn->ipf_nat_defage;
4973b9b51feSCy Schubert 	}
4983b9b51feSCy Schubert 
4993b9b51feSCy Schubert 	/*
5003b9b51feSCy Schubert 	 * Increase this because we may have "keep state" following
5013b9b51feSCy Schubert 	 * this too and packet storms can occur if this is removed
5023b9b51feSCy Schubert 	 * too quickly.
5033b9b51feSCy Schubert 	 */
5043b9b51feSCy Schubert 	softn->ipf_nat_tcptq[IPF_TCPS_CLOSED].ifq_ttl = softc->ipf_tcplastack;
5053b9b51feSCy Schubert 
5063b9b51feSCy Schubert 	MUTEX_INIT(&softn->ipf_nat_new, "ipf nat new mutex");
5073b9b51feSCy Schubert 	MUTEX_INIT(&softn->ipf_nat_io, "ipf nat io mutex");
5083b9b51feSCy Schubert 
5093b9b51feSCy Schubert 	softn->ipf_nat_inited = 1;
5103b9b51feSCy Schubert 
5118c82b374SCy Schubert 	return (0);
5123b9b51feSCy Schubert }
5133b9b51feSCy Schubert 
5143b9b51feSCy Schubert 
5153b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
5163b9b51feSCy Schubert /* Function:    ipf_nat_soft_fini                                           */
5173b9b51feSCy Schubert /* Returns:     Nil                                                         */
5183b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
5193b9b51feSCy Schubert /*                                                                          */
5203b9b51feSCy Schubert /* Free all memory used by NAT structures allocated at runtime.             */
5213b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
5223b9b51feSCy Schubert int
ipf_nat_soft_fini(ipf_main_softc_t * softc,void * arg)523064a5a95SCy Schubert ipf_nat_soft_fini(ipf_main_softc_t *softc, void *arg)
5243b9b51feSCy Schubert {
5253b9b51feSCy Schubert 	ipf_nat_softc_t *softn = arg;
5263b9b51feSCy Schubert 	ipftq_t *ifq, *ifqnext;
5273b9b51feSCy Schubert 
5283b9b51feSCy Schubert 	(void) ipf_nat_clearlist(softc, softn);
5293b9b51feSCy Schubert 	(void) ipf_nat_flushtable(softc, softn);
5303b9b51feSCy Schubert 
5313b9b51feSCy Schubert 	/*
5323b9b51feSCy Schubert 	 * Proxy timeout queues are not cleaned here because although they
5333b9b51feSCy Schubert 	 * exist on the NAT list, ipf_proxy_unload is called after unload
5343b9b51feSCy Schubert 	 * and the proxies actually are responsible for them being created.
5353b9b51feSCy Schubert 	 * Should the proxy timeouts have their own list?  There's no real
5363b9b51feSCy Schubert 	 * justification as this is the only complication.
5373b9b51feSCy Schubert 	 */
5383b9b51feSCy Schubert 	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) {
5393b9b51feSCy Schubert 		ifqnext = ifq->ifq_next;
5403b9b51feSCy Schubert 		if (ipf_deletetimeoutqueue(ifq) == 0)
5413b9b51feSCy Schubert 			ipf_freetimeoutqueue(softc, ifq);
5423b9b51feSCy Schubert 	}
5433b9b51feSCy Schubert 
5443b9b51feSCy Schubert 	if (softn->ipf_nat_table[0] != NULL) {
5453b9b51feSCy Schubert 		KFREES(softn->ipf_nat_table[0],
5463b9b51feSCy Schubert 		       sizeof(nat_t *) * softn->ipf_nat_table_sz);
5473b9b51feSCy Schubert 		softn->ipf_nat_table[0] = NULL;
5483b9b51feSCy Schubert 	}
5493b9b51feSCy Schubert 	if (softn->ipf_nat_table[1] != NULL) {
5503b9b51feSCy Schubert 		KFREES(softn->ipf_nat_table[1],
5513b9b51feSCy Schubert 		       sizeof(nat_t *) * softn->ipf_nat_table_sz);
5523b9b51feSCy Schubert 		softn->ipf_nat_table[1] = NULL;
5533b9b51feSCy Schubert 	}
5543b9b51feSCy Schubert 	if (softn->ipf_nat_map_rules != NULL) {
5553b9b51feSCy Schubert 		KFREES(softn->ipf_nat_map_rules,
5563b9b51feSCy Schubert 		       sizeof(ipnat_t *) * softn->ipf_nat_maprules_sz);
5573b9b51feSCy Schubert 		softn->ipf_nat_map_rules = NULL;
5583b9b51feSCy Schubert 	}
5593b9b51feSCy Schubert 	if (softn->ipf_nat_rdr_rules != NULL) {
5603b9b51feSCy Schubert 		KFREES(softn->ipf_nat_rdr_rules,
5613b9b51feSCy Schubert 		       sizeof(ipnat_t *) * softn->ipf_nat_rdrrules_sz);
5623b9b51feSCy Schubert 		softn->ipf_nat_rdr_rules = NULL;
5633b9b51feSCy Schubert 	}
5643b9b51feSCy Schubert 	if (softn->ipf_hm_maptable != NULL) {
5653b9b51feSCy Schubert 		KFREES(softn->ipf_hm_maptable,
5663b9b51feSCy Schubert 		       sizeof(hostmap_t *) * softn->ipf_nat_hostmap_sz);
5673b9b51feSCy Schubert 		softn->ipf_hm_maptable = NULL;
5683b9b51feSCy Schubert 	}
5693b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) {
5703b9b51feSCy Schubert 		KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
5713b9b51feSCy Schubert 		       sizeof(u_int) * softn->ipf_nat_table_sz);
5723b9b51feSCy Schubert 		softn->ipf_nat_stats.ns_side[0].ns_bucketlen = NULL;
5733b9b51feSCy Schubert 	}
5743b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) {
5753b9b51feSCy Schubert 		KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
5763b9b51feSCy Schubert 		       sizeof(u_int) * softn->ipf_nat_table_sz);
5773b9b51feSCy Schubert 		softn->ipf_nat_stats.ns_side[1].ns_bucketlen = NULL;
5783b9b51feSCy Schubert 	}
5793b9b51feSCy Schubert 
5803b9b51feSCy Schubert 	if (softn->ipf_nat_inited == 1) {
5813b9b51feSCy Schubert 		softn->ipf_nat_inited = 0;
5823b9b51feSCy Schubert 		ipf_sttab_destroy(softn->ipf_nat_tcptq);
5833b9b51feSCy Schubert 
5843b9b51feSCy Schubert 		MUTEX_DESTROY(&softn->ipf_nat_new);
5853b9b51feSCy Schubert 		MUTEX_DESTROY(&softn->ipf_nat_io);
5863b9b51feSCy Schubert 
5873b9b51feSCy Schubert 		MUTEX_DESTROY(&softn->ipf_nat_udptq.ifq_lock);
5883b9b51feSCy Schubert 		MUTEX_DESTROY(&softn->ipf_nat_udpacktq.ifq_lock);
5893b9b51feSCy Schubert 		MUTEX_DESTROY(&softn->ipf_nat_icmptq.ifq_lock);
5903b9b51feSCy Schubert 		MUTEX_DESTROY(&softn->ipf_nat_icmpacktq.ifq_lock);
5913b9b51feSCy Schubert 		MUTEX_DESTROY(&softn->ipf_nat_iptq.ifq_lock);
5923b9b51feSCy Schubert 		MUTEX_DESTROY(&softn->ipf_nat_pending.ifq_lock);
5933b9b51feSCy Schubert 	}
5943b9b51feSCy Schubert 
5958c82b374SCy Schubert 	return (0);
5963b9b51feSCy Schubert }
5973b9b51feSCy Schubert 
5983b9b51feSCy Schubert 
5993b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
6003b9b51feSCy Schubert /* Function:    ipf_nat_setlock                                             */
6013b9b51feSCy Schubert /* Returns:     Nil                                                         */
6023b9b51feSCy Schubert /* Parameters:  arg(I) - pointer to soft state information                  */
6033b9b51feSCy Schubert /*              tmp(I) - new lock value                                     */
6043b9b51feSCy Schubert /*                                                                          */
6053b9b51feSCy Schubert /* Set the "lock status" of NAT to the value in tmp.                        */
6063b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
6073b9b51feSCy Schubert void
ipf_nat_setlock(void * arg,int tmp)608064a5a95SCy Schubert ipf_nat_setlock(void *arg, int tmp)
6093b9b51feSCy Schubert {
6103b9b51feSCy Schubert 	ipf_nat_softc_t *softn = arg;
6113b9b51feSCy Schubert 
6123b9b51feSCy Schubert 	softn->ipf_nat_lock = tmp;
6133b9b51feSCy Schubert }
6143b9b51feSCy Schubert 
6153b9b51feSCy Schubert 
6163b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
6173b9b51feSCy Schubert /* Function:    ipf_nat_addrdr                                              */
6183b9b51feSCy Schubert /* Returns:     Nil                                                         */
6193b9b51feSCy Schubert /* Parameters:  n(I) - pointer to NAT rule to add                           */
6203b9b51feSCy Schubert /*                                                                          */
6213b9b51feSCy Schubert /* Adds a redirect rule to the hash table of redirect rules and the list of */
6223b9b51feSCy Schubert /* loaded NAT rules.  Updates the bitmask indicating which netmasks are in  */
6233b9b51feSCy Schubert /* use by redirect rules.                                                   */
6243b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
6253b9b51feSCy Schubert static void
ipf_nat_addrdr(ipf_nat_softc_t * softn,ipnat_t * n)626064a5a95SCy Schubert ipf_nat_addrdr(ipf_nat_softc_t *softn, ipnat_t *n)
6273b9b51feSCy Schubert {
6283b9b51feSCy Schubert 	ipnat_t **np;
6293b9b51feSCy Schubert 	u_32_t j;
6303b9b51feSCy Schubert 	u_int hv;
6313b9b51feSCy Schubert 	u_int rhv;
6323b9b51feSCy Schubert 	int k;
6333b9b51feSCy Schubert 
6343b9b51feSCy Schubert 	if (n->in_odstatype == FRI_NORMAL) {
6353b9b51feSCy Schubert 		k = count4bits(n->in_odstmsk);
6363b9b51feSCy Schubert 		ipf_inet_mask_add(k, &softn->ipf_nat_rdr_mask);
6373b9b51feSCy Schubert 		j = (n->in_odstaddr & n->in_odstmsk);
6383b9b51feSCy Schubert 		rhv = NAT_HASH_FN(j, 0, 0xffffffff);
6393b9b51feSCy Schubert 	} else {
6403b9b51feSCy Schubert 		ipf_inet_mask_add(0, &softn->ipf_nat_rdr_mask);
6413b9b51feSCy Schubert 		j = 0;
6423b9b51feSCy Schubert 		rhv = 0;
6433b9b51feSCy Schubert 	}
6443b9b51feSCy Schubert 	hv = rhv % softn->ipf_nat_rdrrules_sz;
6453b9b51feSCy Schubert 	np = softn->ipf_nat_rdr_rules + hv;
6463b9b51feSCy Schubert 	while (*np != NULL)
6473b9b51feSCy Schubert 		np = &(*np)->in_rnext;
6483b9b51feSCy Schubert 	n->in_rnext = NULL;
6493b9b51feSCy Schubert 	n->in_prnext = np;
6503b9b51feSCy Schubert 	n->in_hv[0] = hv;
6513b9b51feSCy Schubert 	n->in_use++;
6523b9b51feSCy Schubert 	*np = n;
6533b9b51feSCy Schubert }
6543b9b51feSCy Schubert 
6553b9b51feSCy Schubert 
6563b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
6573b9b51feSCy Schubert /* Function:    ipf_nat_addmap                                              */
6583b9b51feSCy Schubert /* Returns:     Nil                                                         */
6593b9b51feSCy Schubert /* Parameters:  n(I) - pointer to NAT rule to add                           */
6603b9b51feSCy Schubert /*                                                                          */
6613b9b51feSCy Schubert /* Adds a NAT map rule to the hash table of rules and the list of  loaded   */
6623b9b51feSCy Schubert /* NAT rules.  Updates the bitmask indicating which netmasks are in use by  */
6633b9b51feSCy Schubert /* redirect rules.                                                          */
6643b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
6653b9b51feSCy Schubert static void
ipf_nat_addmap(ipf_nat_softc_t * softn,ipnat_t * n)666064a5a95SCy Schubert ipf_nat_addmap(ipf_nat_softc_t *softn, ipnat_t *n)
6673b9b51feSCy Schubert {
6683b9b51feSCy Schubert 	ipnat_t **np;
6693b9b51feSCy Schubert 	u_32_t j;
6703b9b51feSCy Schubert 	u_int hv;
6713b9b51feSCy Schubert 	u_int rhv;
6723b9b51feSCy Schubert 	int k;
6733b9b51feSCy Schubert 
6743b9b51feSCy Schubert 	if (n->in_osrcatype == FRI_NORMAL) {
6753b9b51feSCy Schubert 		k = count4bits(n->in_osrcmsk);
6763b9b51feSCy Schubert 		ipf_inet_mask_add(k, &softn->ipf_nat_map_mask);
6773b9b51feSCy Schubert 		j = (n->in_osrcaddr & n->in_osrcmsk);
6783b9b51feSCy Schubert 		rhv = NAT_HASH_FN(j, 0, 0xffffffff);
6793b9b51feSCy Schubert 	} else {
6803b9b51feSCy Schubert 		ipf_inet_mask_add(0, &softn->ipf_nat_map_mask);
6813b9b51feSCy Schubert 		j = 0;
6823b9b51feSCy Schubert 		rhv = 0;
6833b9b51feSCy Schubert 	}
6843b9b51feSCy Schubert 	hv = rhv % softn->ipf_nat_maprules_sz;
6853b9b51feSCy Schubert 	np = softn->ipf_nat_map_rules + hv;
6863b9b51feSCy Schubert 	while (*np != NULL)
6873b9b51feSCy Schubert 		np = &(*np)->in_mnext;
6883b9b51feSCy Schubert 	n->in_mnext = NULL;
6893b9b51feSCy Schubert 	n->in_pmnext = np;
6903b9b51feSCy Schubert 	n->in_hv[1] = rhv;
6913b9b51feSCy Schubert 	n->in_use++;
6923b9b51feSCy Schubert 	*np = n;
6933b9b51feSCy Schubert }
6943b9b51feSCy Schubert 
6953b9b51feSCy Schubert 
6963b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
6973b9b51feSCy Schubert /* Function:    ipf_nat_delrdr                                              */
6983b9b51feSCy Schubert /* Returns:     Nil                                                         */
6993b9b51feSCy Schubert /* Parameters:  n(I) - pointer to NAT rule to delete                        */
7003b9b51feSCy Schubert /*                                                                          */
7013b9b51feSCy Schubert /* Removes a redirect rule from the hash table of redirect rules.           */
7023b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
7033b9b51feSCy Schubert void
ipf_nat_delrdr(ipf_nat_softc_t * softn,ipnat_t * n)704064a5a95SCy Schubert ipf_nat_delrdr(ipf_nat_softc_t *softn, ipnat_t *n)
7053b9b51feSCy Schubert {
7063b9b51feSCy Schubert 	if (n->in_odstatype == FRI_NORMAL) {
7073b9b51feSCy Schubert 		int k = count4bits(n->in_odstmsk);
7083b9b51feSCy Schubert 		ipf_inet_mask_del(k, &softn->ipf_nat_rdr_mask);
7093b9b51feSCy Schubert 	} else {
7103b9b51feSCy Schubert 		ipf_inet_mask_del(0, &softn->ipf_nat_rdr_mask);
7113b9b51feSCy Schubert 	}
7123b9b51feSCy Schubert 	if (n->in_rnext)
7133b9b51feSCy Schubert 		n->in_rnext->in_prnext = n->in_prnext;
7143b9b51feSCy Schubert 	*n->in_prnext = n->in_rnext;
7153b9b51feSCy Schubert 	n->in_use--;
7163b9b51feSCy Schubert }
7173b9b51feSCy Schubert 
7183b9b51feSCy Schubert 
7193b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
7203b9b51feSCy Schubert /* Function:    ipf_nat_delmap                                              */
7213b9b51feSCy Schubert /* Returns:     Nil                                                         */
7223b9b51feSCy Schubert /* Parameters:  n(I) - pointer to NAT rule to delete                        */
7233b9b51feSCy Schubert /*                                                                          */
7243b9b51feSCy Schubert /* Removes a NAT map rule from the hash table of NAT map rules.             */
7253b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
7263b9b51feSCy Schubert void
ipf_nat_delmap(ipf_nat_softc_t * softn,ipnat_t * n)727064a5a95SCy Schubert ipf_nat_delmap(ipf_nat_softc_t *softn, ipnat_t *n)
7283b9b51feSCy Schubert {
7293b9b51feSCy Schubert 	if (n->in_osrcatype == FRI_NORMAL) {
7303b9b51feSCy Schubert 		int k = count4bits(n->in_osrcmsk);
7313b9b51feSCy Schubert 		ipf_inet_mask_del(k, &softn->ipf_nat_map_mask);
7323b9b51feSCy Schubert 	} else {
7333b9b51feSCy Schubert 		ipf_inet_mask_del(0, &softn->ipf_nat_map_mask);
7343b9b51feSCy Schubert 	}
7353b9b51feSCy Schubert 	if (n->in_mnext != NULL)
7363b9b51feSCy Schubert 		n->in_mnext->in_pmnext = n->in_pmnext;
7373b9b51feSCy Schubert 	*n->in_pmnext = n->in_mnext;
7383b9b51feSCy Schubert 	n->in_use--;
7393b9b51feSCy Schubert }
7403b9b51feSCy Schubert 
7413b9b51feSCy Schubert 
7423b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
7433b9b51feSCy Schubert /* Function:    ipf_nat_hostmap                                             */
7443b9b51feSCy Schubert /* Returns:     struct hostmap* - NULL if no hostmap could be created,      */
7453b9b51feSCy Schubert /*                                else a pointer to the hostmapping to use  */
7463b9b51feSCy Schubert /* Parameters:  np(I)   - pointer to NAT rule                               */
7473b9b51feSCy Schubert /*              real(I) - real IP address                                   */
7483b9b51feSCy Schubert /*              map(I)  - mapped IP address                                 */
7493b9b51feSCy Schubert /*              port(I) - destination port number                           */
7503b9b51feSCy Schubert /* Write Locks: ipf_nat                                                     */
7513b9b51feSCy Schubert /*                                                                          */
7523b9b51feSCy Schubert /* Check if an ip address has already been allocated for a given mapping    */
7533b9b51feSCy Schubert /* that is not doing port based translation.  If is not yet allocated, then */
7543b9b51feSCy Schubert /* create a new entry if a non-NULL NAT rule pointer has been supplied.     */
7553b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
7563b9b51feSCy Schubert static struct hostmap *
ipf_nat_hostmap(ipf_nat_softc_t * softn,ipnat_t * np,struct in_addr src,struct in_addr dst,struct in_addr map,u_32_t port)757064a5a95SCy Schubert ipf_nat_hostmap(ipf_nat_softc_t *softn, ipnat_t *np, struct in_addr src,
758064a5a95SCy Schubert 	struct in_addr dst, struct in_addr map, u_32_t port)
7593b9b51feSCy Schubert {
7603b9b51feSCy Schubert 	hostmap_t *hm;
7613b9b51feSCy Schubert 	u_int hv, rhv;
7623b9b51feSCy Schubert 
7633b9b51feSCy Schubert 	hv = (src.s_addr ^ dst.s_addr);
7643b9b51feSCy Schubert 	hv += src.s_addr;
7653b9b51feSCy Schubert 	hv += dst.s_addr;
7663b9b51feSCy Schubert 	rhv = hv;
7673b9b51feSCy Schubert 	hv %= softn->ipf_nat_hostmap_sz;
7683b9b51feSCy Schubert 	for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_hnext)
7693b9b51feSCy Schubert 		if ((hm->hm_osrcip.s_addr == src.s_addr) &&
7703b9b51feSCy Schubert 		    (hm->hm_odstip.s_addr == dst.s_addr) &&
7713b9b51feSCy Schubert 		    ((np == NULL) || (np == hm->hm_ipnat)) &&
7723b9b51feSCy Schubert 		    ((port == 0) || (port == hm->hm_port))) {
7733b9b51feSCy Schubert 			softn->ipf_nat_stats.ns_hm_addref++;
7743b9b51feSCy Schubert 			hm->hm_ref++;
7758c82b374SCy Schubert 			return (hm);
7763b9b51feSCy Schubert 		}
7773b9b51feSCy Schubert 
7783b9b51feSCy Schubert 	if (np == NULL) {
7793b9b51feSCy Schubert 		softn->ipf_nat_stats.ns_hm_nullnp++;
7808c82b374SCy Schubert 		return (NULL);
7813b9b51feSCy Schubert 	}
7823b9b51feSCy Schubert 
7833b9b51feSCy Schubert 	KMALLOC(hm, hostmap_t *);
7843b9b51feSCy Schubert 	if (hm) {
7853b9b51feSCy Schubert 		hm->hm_next = softn->ipf_hm_maplist;
7863b9b51feSCy Schubert 		hm->hm_pnext = &softn->ipf_hm_maplist;
7873b9b51feSCy Schubert 		if (softn->ipf_hm_maplist != NULL)
7883b9b51feSCy Schubert 			softn->ipf_hm_maplist->hm_pnext = &hm->hm_next;
7893b9b51feSCy Schubert 		softn->ipf_hm_maplist = hm;
7903b9b51feSCy Schubert 		hm->hm_hnext = softn->ipf_hm_maptable[hv];
7913b9b51feSCy Schubert 		hm->hm_phnext = softn->ipf_hm_maptable + hv;
7923b9b51feSCy Schubert 		if (softn->ipf_hm_maptable[hv] != NULL)
7933b9b51feSCy Schubert 			softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext;
7943b9b51feSCy Schubert 		softn->ipf_hm_maptable[hv] = hm;
7953b9b51feSCy Schubert 		hm->hm_ipnat = np;
7963b9b51feSCy Schubert 		np->in_use++;
7973b9b51feSCy Schubert 		hm->hm_osrcip = src;
7983b9b51feSCy Schubert 		hm->hm_odstip = dst;
7993b9b51feSCy Schubert 		hm->hm_nsrcip = map;
8003b9b51feSCy Schubert 		hm->hm_ndstip.s_addr = 0;
8013b9b51feSCy Schubert 		hm->hm_ref = 1;
8023b9b51feSCy Schubert 		hm->hm_port = port;
8033b9b51feSCy Schubert 		hm->hm_hv = rhv;
8043b9b51feSCy Schubert 		hm->hm_v = 4;
8053b9b51feSCy Schubert 		softn->ipf_nat_stats.ns_hm_new++;
8063b9b51feSCy Schubert 	} else {
8073b9b51feSCy Schubert 		softn->ipf_nat_stats.ns_hm_newfail++;
8083b9b51feSCy Schubert 	}
8098c82b374SCy Schubert 	return (hm);
8103b9b51feSCy Schubert }
8113b9b51feSCy Schubert 
8123b9b51feSCy Schubert 
8133b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
8143b9b51feSCy Schubert /* Function:    ipf_nat_hostmapdel                                          */
8153b9b51feSCy Schubert /* Returns:     Nil                                                         */
8163b9b51feSCy Schubert /* Parameters:  hmp(I) - pointer to hostmap structure pointer               */
8173b9b51feSCy Schubert /* Write Locks: ipf_nat                                                     */
8183b9b51feSCy Schubert /*                                                                          */
8193b9b51feSCy Schubert /* Decrement the references to this hostmap structure by one.  If this      */
8203b9b51feSCy Schubert /* reaches zero then remove it and free it.                                 */
8213b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
8223b9b51feSCy Schubert void
ipf_nat_hostmapdel(ipf_main_softc_t * softc,struct hostmap ** hmp)823064a5a95SCy Schubert ipf_nat_hostmapdel(ipf_main_softc_t *softc, struct hostmap **hmp)
8243b9b51feSCy Schubert {
8253b9b51feSCy Schubert 	struct hostmap *hm;
8263b9b51feSCy Schubert 
8273b9b51feSCy Schubert 	hm = *hmp;
8283b9b51feSCy Schubert 	*hmp = NULL;
8293b9b51feSCy Schubert 
8303b9b51feSCy Schubert 	hm->hm_ref--;
8313b9b51feSCy Schubert 	if (hm->hm_ref == 0) {
8323b9b51feSCy Schubert 		ipf_nat_rule_deref(softc, &hm->hm_ipnat);
8333b9b51feSCy Schubert 		if (hm->hm_hnext)
8343b9b51feSCy Schubert 			hm->hm_hnext->hm_phnext = hm->hm_phnext;
8353b9b51feSCy Schubert 		*hm->hm_phnext = hm->hm_hnext;
8363b9b51feSCy Schubert 		if (hm->hm_next)
8373b9b51feSCy Schubert 			hm->hm_next->hm_pnext = hm->hm_pnext;
8383b9b51feSCy Schubert 		*hm->hm_pnext = hm->hm_next;
8393b9b51feSCy Schubert 		KFREE(hm);
8403b9b51feSCy Schubert 	}
8413b9b51feSCy Schubert }
8423b9b51feSCy Schubert 
8433b9b51feSCy Schubert 
8443b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
8453b9b51feSCy Schubert /* Function:    ipf_fix_outcksum                                            */
8463b9b51feSCy Schubert /* Returns:     Nil                                                         */
8474b5c0c9bSCy Schubert /* Parameters:  cksum(I) - ipf_cksum_t, value of fin_cksum                  */
8483b9b51feSCy Schubert /*              sp(I)  - location of 16bit checksum to update               */
8496af38b34SCy Schubert /*              n(I)  - amount to adjust checksum by                        */
8504b5c0c9bSCy Schubert /*		partial(I) - partial checksum				    */
8513b9b51feSCy Schubert /*                                                                          */
8523b9b51feSCy Schubert /* Adjusts the 16bit checksum by "n" for packets going out.                 */
8533b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
8543b9b51feSCy Schubert void
ipf_fix_outcksum(int cksum,u_short * sp,u_32_t n,u_32_t partial)855064a5a95SCy Schubert ipf_fix_outcksum(int cksum, u_short *sp, u_32_t n, u_32_t partial)
8563b9b51feSCy Schubert {
8573b9b51feSCy Schubert 	u_short sumshort;
8583b9b51feSCy Schubert 	u_32_t sum1;
8593b9b51feSCy Schubert 
8603b9b51feSCy Schubert 	if (n == 0)
8613b9b51feSCy Schubert 		return;
8623b9b51feSCy Schubert 
8633b9b51feSCy Schubert 	if (cksum == 4) {
8643b9b51feSCy Schubert 		*sp = 0;
8653b9b51feSCy Schubert 		return;
8663b9b51feSCy Schubert 	}
8673b9b51feSCy Schubert 	if (cksum == 2) {
8683b9b51feSCy Schubert 		sum1 = partial;
8693b9b51feSCy Schubert 		sum1 = (sum1 & 0xffff) + (sum1 >> 16);
8703b9b51feSCy Schubert 		*sp = htons(sum1);
8713b9b51feSCy Schubert 		return;
8723b9b51feSCy Schubert 	}
8733b9b51feSCy Schubert 	sum1 = (~ntohs(*sp)) & 0xffff;
8743b9b51feSCy Schubert 	sum1 += (n);
8753b9b51feSCy Schubert 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
8763b9b51feSCy Schubert 	/* Again */
8773b9b51feSCy Schubert 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
8783b9b51feSCy Schubert 	sumshort = ~(u_short)sum1;
8793b9b51feSCy Schubert 	*(sp) = htons(sumshort);
8803b9b51feSCy Schubert }
8813b9b51feSCy Schubert 
8823b9b51feSCy Schubert 
8833b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
8843b9b51feSCy Schubert /* Function:    ipf_fix_incksum                                             */
8853b9b51feSCy Schubert /* Returns:     Nil                                                         */
8864b5c0c9bSCy Schubert /* Parameters:  cksum(I) - ipf_cksum_t, value of fin_cksum                  */
8873b9b51feSCy Schubert /*              sp(I)  - location of 16bit checksum to update               */
8886af38b34SCy Schubert /*              n(I)  - amount to adjust checksum by                        */
8894b5c0c9bSCy Schubert /*		partial(I) - partial checksum				    */
8903b9b51feSCy Schubert /*                                                                          */
8913b9b51feSCy Schubert /* Adjusts the 16bit checksum by "n" for packets going in.                  */
8923b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
8933b9b51feSCy Schubert void
ipf_fix_incksum(int cksum,u_short * sp,u_32_t n,u_32_t partial)894064a5a95SCy Schubert ipf_fix_incksum(int cksum, u_short *sp, u_32_t n, u_32_t partial)
8953b9b51feSCy Schubert {
8963b9b51feSCy Schubert 	u_short sumshort;
8973b9b51feSCy Schubert 	u_32_t sum1;
8983b9b51feSCy Schubert 
8993b9b51feSCy Schubert 	if (n == 0)
9003b9b51feSCy Schubert 		return;
9013b9b51feSCy Schubert 
9023b9b51feSCy Schubert 	if (cksum == 4) {
9033b9b51feSCy Schubert 		*sp = 0;
9043b9b51feSCy Schubert 		return;
9053b9b51feSCy Schubert 	}
9063b9b51feSCy Schubert 	if (cksum == 2) {
9073b9b51feSCy Schubert 		sum1 = partial;
9083b9b51feSCy Schubert 		sum1 = (sum1 & 0xffff) + (sum1 >> 16);
9093b9b51feSCy Schubert 		*sp = htons(sum1);
9103b9b51feSCy Schubert 		return;
9113b9b51feSCy Schubert 	}
9123b9b51feSCy Schubert 
9133b9b51feSCy Schubert 	sum1 = (~ntohs(*sp)) & 0xffff;
9143b9b51feSCy Schubert 	sum1 += ~(n) & 0xffff;
9153b9b51feSCy Schubert 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
9163b9b51feSCy Schubert 	/* Again */
9173b9b51feSCy Schubert 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
9183b9b51feSCy Schubert 	sumshort = ~(u_short)sum1;
9193b9b51feSCy Schubert 	*(sp) = htons(sumshort);
9203b9b51feSCy Schubert }
9213b9b51feSCy Schubert 
9223b9b51feSCy Schubert 
9233b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
9243b9b51feSCy Schubert /* Function:    ipf_fix_datacksum                                           */
9253b9b51feSCy Schubert /* Returns:     Nil                                                         */
9263b9b51feSCy Schubert /* Parameters:  sp(I)  - location of 16bit checksum to update               */
9276af38b34SCy Schubert /*              n(I)  - amount to adjust checksum by                        */
9283b9b51feSCy Schubert /*                                                                          */
9293b9b51feSCy Schubert /* Fix_datacksum is used *only* for the adjustments of checksums in the     */
9303b9b51feSCy Schubert /* data section of an IP packet.                                            */
9313b9b51feSCy Schubert /*                                                                          */
9323b9b51feSCy Schubert /* The only situation in which you need to do this is when NAT'ing an       */
9333b9b51feSCy Schubert /* ICMP error message. Such a message, contains in its body the IP header   */
9343b9b51feSCy Schubert /* of the original IP packet, that causes the error.                        */
9353b9b51feSCy Schubert /*                                                                          */
9363b9b51feSCy Schubert /* You can't use fix_incksum or fix_outcksum in that case, because for the  */
9373b9b51feSCy Schubert /* kernel the data section of the ICMP error is just data, and no special   */
9383b9b51feSCy Schubert /* processing like hardware cksum or ntohs processing have been done by the */
9393b9b51feSCy Schubert /* kernel on the data section.                                              */
9403b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
9413b9b51feSCy Schubert void
ipf_fix_datacksum(u_short * sp,u_32_t n)942064a5a95SCy Schubert ipf_fix_datacksum(u_short *sp, u_32_t n)
9433b9b51feSCy Schubert {
9443b9b51feSCy Schubert 	u_short sumshort;
9453b9b51feSCy Schubert 	u_32_t sum1;
9463b9b51feSCy Schubert 
9473b9b51feSCy Schubert 	if (n == 0)
9483b9b51feSCy Schubert 		return;
9493b9b51feSCy Schubert 
9503b9b51feSCy Schubert 	sum1 = (~ntohs(*sp)) & 0xffff;
9513b9b51feSCy Schubert 	sum1 += (n);
9523b9b51feSCy Schubert 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
9533b9b51feSCy Schubert 	/* Again */
9543b9b51feSCy Schubert 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
9553b9b51feSCy Schubert 	sumshort = ~(u_short)sum1;
9563b9b51feSCy Schubert 	*(sp) = htons(sumshort);
9573b9b51feSCy Schubert }
9583b9b51feSCy Schubert 
9593b9b51feSCy Schubert 
9603b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
9613b9b51feSCy Schubert /* Function:    ipf_nat_ioctl                                               */
9623b9b51feSCy Schubert /* Returns:     int - 0 == success, != 0 == failure                         */
9633b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
9643b9b51feSCy Schubert /*              data(I)  - pointer to ioctl data                            */
9653b9b51feSCy Schubert /*              cmd(I)   - ioctl command integer                            */
9663b9b51feSCy Schubert /*              mode(I)  - file mode bits used with open                    */
9673b9b51feSCy Schubert /*              uid(I)   - uid of calling process                           */
9683b9b51feSCy Schubert /*              ctx(I)   - pointer used as key for finding context          */
9693b9b51feSCy Schubert /*                                                                          */
9703b9b51feSCy Schubert /* Processes an ioctl call made to operate on the IP Filter NAT device.     */
9713b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
9723b9b51feSCy Schubert int
ipf_nat_ioctl(ipf_main_softc_t * softc,caddr_t data,ioctlcmd_t cmd,int mode,int uid,void * ctx)973064a5a95SCy Schubert ipf_nat_ioctl(ipf_main_softc_t *softc, caddr_t data, ioctlcmd_t cmd,
974064a5a95SCy Schubert 	int mode, int uid, void *ctx)
9753b9b51feSCy Schubert {
9763b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
9773b9b51feSCy Schubert 	int error = 0, ret, arg, getlock;
9783b9b51feSCy Schubert 	ipnat_t *nat, *nt, *n;
9793b9b51feSCy Schubert 	ipnat_t natd;
9803b9b51feSCy Schubert 	SPL_INT(s);
9813b9b51feSCy Schubert 
9823b9b51feSCy Schubert #if !SOLARIS && defined(_KERNEL)
9833b9b51feSCy Schubert # if NETBSD_GE_REV(399002000)
9843b9b51feSCy Schubert 	if ((mode & FWRITE) &&
9853b9b51feSCy Schubert 	     kauth_authorize_network(curlwp->l_cred, KAUTH_NETWORK_FIREWALL,
9863b9b51feSCy Schubert 				     KAUTH_REQ_NETWORK_FIREWALL_FW,
9873b9b51feSCy Schubert 				     NULL, NULL, NULL))
9883b9b51feSCy Schubert # else
9893b9b51feSCy Schubert #  if defined(__FreeBSD__)
9903b9b51feSCy Schubert 	if (securelevel_ge(curthread->td_ucred, 3) && (mode & FWRITE))
9913b9b51feSCy Schubert #  else
9923b9b51feSCy Schubert 	if ((securelevel >= 3) && (mode & FWRITE))
9933b9b51feSCy Schubert #  endif
9943b9b51feSCy Schubert # endif
9953b9b51feSCy Schubert 	{
9963b9b51feSCy Schubert 		IPFERROR(60001);
9978c82b374SCy Schubert 		return (EPERM);
9983b9b51feSCy Schubert 	}
999c47db49bSCy Schubert # if defined(__FreeBSD__)
1000c47db49bSCy Schubert 	if (jailed_without_vnet(curthread->td_ucred)) {
1001c47db49bSCy Schubert 		IPFERROR(60076);
1002c47db49bSCy Schubert 		return (EOPNOTSUPP);
1003c47db49bSCy Schubert 	}
1004c47db49bSCy Schubert # endif
10053b9b51feSCy Schubert #endif
10063b9b51feSCy Schubert 
10073b9b51feSCy Schubert 	getlock = (mode & NAT_LOCKHELD) ? 0 : 1;
10083b9b51feSCy Schubert 
10093b9b51feSCy Schubert 	n = NULL;
10103b9b51feSCy Schubert 	nt = NULL;
10113b9b51feSCy Schubert 	nat = NULL;
10123b9b51feSCy Schubert 
10133b9b51feSCy Schubert 	if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT) ||
10143b9b51feSCy Schubert 	    (cmd == (ioctlcmd_t)SIOCPURGENAT)) {
10153b9b51feSCy Schubert 		if (mode & NAT_SYSSPACE) {
10163b9b51feSCy Schubert 			bcopy(data, (char *)&natd, sizeof(natd));
10173b9b51feSCy Schubert 			nat = &natd;
10183b9b51feSCy Schubert 			error = 0;
10193b9b51feSCy Schubert 		} else {
10203b9b51feSCy Schubert 			bzero(&natd, sizeof(natd));
10213b9b51feSCy Schubert 			error = ipf_inobj(softc, data, NULL, &natd,
10223b9b51feSCy Schubert 					  IPFOBJ_IPNAT);
10233b9b51feSCy Schubert 			if (error != 0)
10243b9b51feSCy Schubert 				goto done;
10253b9b51feSCy Schubert 
10263b9b51feSCy Schubert 			if (natd.in_size < sizeof(ipnat_t)) {
10273b9b51feSCy Schubert 				error = EINVAL;
10283b9b51feSCy Schubert 				goto done;
10293b9b51feSCy Schubert 			}
10303b9b51feSCy Schubert 			KMALLOCS(nt, ipnat_t *, natd.in_size);
10313b9b51feSCy Schubert 			if (nt == NULL) {
10323b9b51feSCy Schubert 				IPFERROR(60070);
10333b9b51feSCy Schubert 				error = ENOMEM;
10343b9b51feSCy Schubert 				goto done;
10353b9b51feSCy Schubert 			}
10363b9b51feSCy Schubert 			bzero(nt, natd.in_size);
10373b9b51feSCy Schubert 			error = ipf_inobjsz(softc, data, nt, IPFOBJ_IPNAT,
10383b9b51feSCy Schubert 					    natd.in_size);
10393b9b51feSCy Schubert 			if (error)
10403b9b51feSCy Schubert 				goto done;
10413b9b51feSCy Schubert 			nat = nt;
10423b9b51feSCy Schubert 		}
10433b9b51feSCy Schubert 
10443b9b51feSCy Schubert 		/*
10453b9b51feSCy Schubert 		 * For add/delete, look to see if the NAT entry is
10463b9b51feSCy Schubert 		 * already present
10473b9b51feSCy Schubert 		 */
10483b9b51feSCy Schubert 		nat->in_flags &= IPN_USERFLAGS;
10493b9b51feSCy Schubert 		if ((nat->in_redir & NAT_MAPBLK) == 0) {
10503b9b51feSCy Schubert 			if (nat->in_osrcatype == FRI_NORMAL ||
10513b9b51feSCy Schubert 			    nat->in_osrcatype == FRI_NONE)
10523b9b51feSCy Schubert 				nat->in_osrcaddr &= nat->in_osrcmsk;
10533b9b51feSCy Schubert 			if (nat->in_odstatype == FRI_NORMAL ||
10543b9b51feSCy Schubert 			    nat->in_odstatype == FRI_NONE)
10553b9b51feSCy Schubert 				nat->in_odstaddr &= nat->in_odstmsk;
10563b9b51feSCy Schubert 			if ((nat->in_flags & (IPN_SPLIT|IPN_SIPRANGE)) == 0) {
10573b9b51feSCy Schubert 				if (nat->in_nsrcatype == FRI_NORMAL)
10583b9b51feSCy Schubert 					nat->in_nsrcaddr &= nat->in_nsrcmsk;
10593b9b51feSCy Schubert 				if (nat->in_ndstatype == FRI_NORMAL)
10603b9b51feSCy Schubert 					nat->in_ndstaddr &= nat->in_ndstmsk;
10613b9b51feSCy Schubert 			}
10623b9b51feSCy Schubert 		}
10633b9b51feSCy Schubert 
10643b9b51feSCy Schubert 		error = ipf_nat_rule_init(softc, softn, nat);
10653b9b51feSCy Schubert 		if (error != 0)
10663b9b51feSCy Schubert 			goto done;
10673b9b51feSCy Schubert 
10683b9b51feSCy Schubert 		MUTEX_ENTER(&softn->ipf_nat_io);
10693b9b51feSCy Schubert 		for (n = softn->ipf_nat_list; n != NULL; n = n->in_next)
10703b9b51feSCy Schubert 			if (ipf_nat_cmp_rules(nat, n) == 0)
10713b9b51feSCy Schubert 				break;
10723b9b51feSCy Schubert 	}
10733b9b51feSCy Schubert 
10743b9b51feSCy Schubert 	switch (cmd)
10753b9b51feSCy Schubert 	{
10763b9b51feSCy Schubert #ifdef  IPFILTER_LOG
10773b9b51feSCy Schubert 	case SIOCIPFFB :
10783b9b51feSCy Schubert 	{
10793b9b51feSCy Schubert 		int tmp;
10803b9b51feSCy Schubert 
10813b9b51feSCy Schubert 		if (!(mode & FWRITE)) {
10823b9b51feSCy Schubert 			IPFERROR(60002);
10833b9b51feSCy Schubert 			error = EPERM;
10843b9b51feSCy Schubert 		} else {
10853b9b51feSCy Schubert 			tmp = ipf_log_clear(softc, IPL_LOGNAT);
10863b9b51feSCy Schubert 			error = BCOPYOUT(&tmp, data, sizeof(tmp));
10873b9b51feSCy Schubert 			if (error != 0) {
10883b9b51feSCy Schubert 				IPFERROR(60057);
10893b9b51feSCy Schubert 				error = EFAULT;
10903b9b51feSCy Schubert 			}
10913b9b51feSCy Schubert 		}
10923b9b51feSCy Schubert 		break;
10933b9b51feSCy Schubert 	}
10943b9b51feSCy Schubert 
10953b9b51feSCy Schubert 	case SIOCSETLG :
10963b9b51feSCy Schubert 		if (!(mode & FWRITE)) {
10973b9b51feSCy Schubert 			IPFERROR(60003);
10983b9b51feSCy Schubert 			error = EPERM;
10993b9b51feSCy Schubert 		} else {
11003b9b51feSCy Schubert 			error = BCOPYIN(data, &softn->ipf_nat_logging,
11013b9b51feSCy Schubert 					sizeof(softn->ipf_nat_logging));
11023b9b51feSCy Schubert 			if (error != 0)
11033b9b51feSCy Schubert 				error = EFAULT;
11043b9b51feSCy Schubert 		}
11053b9b51feSCy Schubert 		break;
11063b9b51feSCy Schubert 
11073b9b51feSCy Schubert 	case SIOCGETLG :
11083b9b51feSCy Schubert 		error = BCOPYOUT(&softn->ipf_nat_logging, data,
11093b9b51feSCy Schubert 				 sizeof(softn->ipf_nat_logging));
11103b9b51feSCy Schubert 		if (error != 0) {
11113b9b51feSCy Schubert 			IPFERROR(60004);
11123b9b51feSCy Schubert 			error = EFAULT;
11133b9b51feSCy Schubert 		}
11143b9b51feSCy Schubert 		break;
11153b9b51feSCy Schubert 
11163b9b51feSCy Schubert 	case FIONREAD :
11173b9b51feSCy Schubert 		arg = ipf_log_bytesused(softc, IPL_LOGNAT);
11183b9b51feSCy Schubert 		error = BCOPYOUT(&arg, data, sizeof(arg));
11193b9b51feSCy Schubert 		if (error != 0) {
11203b9b51feSCy Schubert 			IPFERROR(60005);
11213b9b51feSCy Schubert 			error = EFAULT;
11223b9b51feSCy Schubert 		}
11233b9b51feSCy Schubert 		break;
11243b9b51feSCy Schubert #endif
11253b9b51feSCy Schubert 	case SIOCADNAT :
11263b9b51feSCy Schubert 		if (!(mode & FWRITE)) {
11273b9b51feSCy Schubert 			IPFERROR(60006);
11283b9b51feSCy Schubert 			error = EPERM;
11293b9b51feSCy Schubert 		} else if (n != NULL) {
11303b9b51feSCy Schubert 			natd.in_flineno = n->in_flineno;
11313b9b51feSCy Schubert 			(void) ipf_outobj(softc, data, &natd, IPFOBJ_IPNAT);
11323b9b51feSCy Schubert 			IPFERROR(60007);
11333b9b51feSCy Schubert 			error = EEXIST;
11343b9b51feSCy Schubert 		} else if (nt == NULL) {
11353b9b51feSCy Schubert 			IPFERROR(60008);
11363b9b51feSCy Schubert 			error = ENOMEM;
11373b9b51feSCy Schubert 		}
11383b9b51feSCy Schubert 		if (error != 0) {
11393b9b51feSCy Schubert 			MUTEX_EXIT(&softn->ipf_nat_io);
11403b9b51feSCy Schubert 			break;
11413b9b51feSCy Schubert 		}
11423b9b51feSCy Schubert 		if (nat != nt)
11433b9b51feSCy Schubert 			bcopy((char *)nat, (char *)nt, sizeof(*n));
11443b9b51feSCy Schubert 		error = ipf_nat_siocaddnat(softc, softn, nt, getlock);
11453b9b51feSCy Schubert 		MUTEX_EXIT(&softn->ipf_nat_io);
11463b9b51feSCy Schubert 		if (error == 0) {
11473b9b51feSCy Schubert 			nat = NULL;
11483b9b51feSCy Schubert 			nt = NULL;
11493b9b51feSCy Schubert 		}
11503b9b51feSCy Schubert 		break;
11513b9b51feSCy Schubert 
11523b9b51feSCy Schubert 	case SIOCRMNAT :
11533b9b51feSCy Schubert 	case SIOCPURGENAT :
11543b9b51feSCy Schubert 		if (!(mode & FWRITE)) {
11553b9b51feSCy Schubert 			IPFERROR(60009);
11563b9b51feSCy Schubert 			error = EPERM;
11573b9b51feSCy Schubert 			n = NULL;
11583b9b51feSCy Schubert 		} else if (n == NULL) {
11593b9b51feSCy Schubert 			IPFERROR(60010);
11603b9b51feSCy Schubert 			error = ESRCH;
11613b9b51feSCy Schubert 		}
11623b9b51feSCy Schubert 
11633b9b51feSCy Schubert 		if (error != 0) {
11643b9b51feSCy Schubert 			MUTEX_EXIT(&softn->ipf_nat_io);
11653b9b51feSCy Schubert 			break;
11663b9b51feSCy Schubert 		}
11673b9b51feSCy Schubert 		if (cmd == (ioctlcmd_t)SIOCPURGENAT) {
11683b9b51feSCy Schubert 			error = ipf_outobjsz(softc, data, n, IPFOBJ_IPNAT,
11693b9b51feSCy Schubert 					     n->in_size);
11703b9b51feSCy Schubert 			if (error) {
11713b9b51feSCy Schubert 				MUTEX_EXIT(&softn->ipf_nat_io);
11723b9b51feSCy Schubert 				goto done;
11733b9b51feSCy Schubert 			}
11743b9b51feSCy Schubert 			n->in_flags |= IPN_PURGE;
11753b9b51feSCy Schubert 		}
11763b9b51feSCy Schubert 		ipf_nat_siocdelnat(softc, softn, n, getlock);
11773b9b51feSCy Schubert 
11783b9b51feSCy Schubert 		MUTEX_EXIT(&softn->ipf_nat_io);
11793b9b51feSCy Schubert 		n = NULL;
11803b9b51feSCy Schubert 		break;
11813b9b51feSCy Schubert 
11823b9b51feSCy Schubert 	case SIOCGNATS :
11833b9b51feSCy Schubert 	    {
11843b9b51feSCy Schubert 		natstat_t *nsp = &softn->ipf_nat_stats;
11853b9b51feSCy Schubert 
11863b9b51feSCy Schubert 		nsp->ns_side[0].ns_table = softn->ipf_nat_table[0];
11873b9b51feSCy Schubert 		nsp->ns_side[1].ns_table = softn->ipf_nat_table[1];
11883b9b51feSCy Schubert 		nsp->ns_list = softn->ipf_nat_list;
11893b9b51feSCy Schubert 		nsp->ns_maptable = softn->ipf_hm_maptable;
11903b9b51feSCy Schubert 		nsp->ns_maplist = softn->ipf_hm_maplist;
11913b9b51feSCy Schubert 		nsp->ns_nattab_sz = softn->ipf_nat_table_sz;
11923b9b51feSCy Schubert 		nsp->ns_nattab_max = softn->ipf_nat_table_max;
11933b9b51feSCy Schubert 		nsp->ns_rultab_sz = softn->ipf_nat_maprules_sz;
11943b9b51feSCy Schubert 		nsp->ns_rdrtab_sz = softn->ipf_nat_rdrrules_sz;
11953b9b51feSCy Schubert 		nsp->ns_hostmap_sz = softn->ipf_nat_hostmap_sz;
11963b9b51feSCy Schubert 		nsp->ns_instances = softn->ipf_nat_instances;
11973b9b51feSCy Schubert 		nsp->ns_ticks = softc->ipf_ticks;
11983b9b51feSCy Schubert #ifdef IPFILTER_LOGGING
11993b9b51feSCy Schubert 		nsp->ns_log_ok = ipf_log_logok(softc, IPF_LOGNAT);
12003b9b51feSCy Schubert 		nsp->ns_log_fail = ipf_log_failures(softc, IPF_LOGNAT);
12013b9b51feSCy Schubert #else
12023b9b51feSCy Schubert 		nsp->ns_log_ok = 0;
12033b9b51feSCy Schubert 		nsp->ns_log_fail = 0;
12043b9b51feSCy Schubert #endif
12053b9b51feSCy Schubert 		error = ipf_outobj(softc, data, nsp, IPFOBJ_NATSTAT);
12063b9b51feSCy Schubert 		break;
12073b9b51feSCy Schubert 	    }
12083b9b51feSCy Schubert 
12093b9b51feSCy Schubert 	case SIOCGNATL :
12103b9b51feSCy Schubert 	    {
12113b9b51feSCy Schubert 		natlookup_t nl;
12123b9b51feSCy Schubert 
12133b9b51feSCy Schubert 		error = ipf_inobj(softc, data, NULL, &nl, IPFOBJ_NATLOOKUP);
12143b9b51feSCy Schubert 		if (error == 0) {
12153b9b51feSCy Schubert 			void *ptr;
12163b9b51feSCy Schubert 
12173b9b51feSCy Schubert 			if (getlock) {
12183b9b51feSCy Schubert 				READ_ENTER(&softc->ipf_nat);
12193b9b51feSCy Schubert 			}
12203b9b51feSCy Schubert 
12213b9b51feSCy Schubert 			switch (nl.nl_v)
12223b9b51feSCy Schubert 			{
12233b9b51feSCy Schubert 			case 4 :
12243b9b51feSCy Schubert 				ptr = ipf_nat_lookupredir(&nl);
12253b9b51feSCy Schubert 				break;
12263b9b51feSCy Schubert #ifdef USE_INET6
12273b9b51feSCy Schubert 			case 6 :
12283b9b51feSCy Schubert 				ptr = ipf_nat6_lookupredir(&nl);
12293b9b51feSCy Schubert 				break;
12303b9b51feSCy Schubert #endif
12313b9b51feSCy Schubert 			default:
12323b9b51feSCy Schubert 				ptr = NULL;
12333b9b51feSCy Schubert 				break;
12343b9b51feSCy Schubert 			}
12353b9b51feSCy Schubert 
12363b9b51feSCy Schubert 			if (getlock) {
12373b9b51feSCy Schubert 				RWLOCK_EXIT(&softc->ipf_nat);
12383b9b51feSCy Schubert 			}
12393b9b51feSCy Schubert 			if (ptr != NULL) {
12403b9b51feSCy Schubert 				error = ipf_outobj(softc, data, &nl,
12413b9b51feSCy Schubert 						   IPFOBJ_NATLOOKUP);
12423b9b51feSCy Schubert 			} else {
12433b9b51feSCy Schubert 				IPFERROR(60011);
12443b9b51feSCy Schubert 				error = ESRCH;
12453b9b51feSCy Schubert 			}
12463b9b51feSCy Schubert 		}
12473b9b51feSCy Schubert 		break;
12483b9b51feSCy Schubert 	    }
12493b9b51feSCy Schubert 
12503b9b51feSCy Schubert 	case SIOCIPFFL :	/* old SIOCFLNAT & SIOCCNATL */
12513b9b51feSCy Schubert 		if (!(mode & FWRITE)) {
12523b9b51feSCy Schubert 			IPFERROR(60012);
12533b9b51feSCy Schubert 			error = EPERM;
12543b9b51feSCy Schubert 			break;
12553b9b51feSCy Schubert 		}
12563b9b51feSCy Schubert 		if (getlock) {
12573b9b51feSCy Schubert 			WRITE_ENTER(&softc->ipf_nat);
12583b9b51feSCy Schubert 		}
12593b9b51feSCy Schubert 
12603b9b51feSCy Schubert 		error = BCOPYIN(data, &arg, sizeof(arg));
12613b9b51feSCy Schubert 		if (error != 0) {
12623b9b51feSCy Schubert 			IPFERROR(60013);
12633b9b51feSCy Schubert 			error = EFAULT;
12643b9b51feSCy Schubert 		} else {
12653b9b51feSCy Schubert 			if (arg == 0)
12663b9b51feSCy Schubert 				ret = ipf_nat_flushtable(softc, softn);
12673b9b51feSCy Schubert 			else if (arg == 1)
12683b9b51feSCy Schubert 				ret = ipf_nat_clearlist(softc, softn);
12693b9b51feSCy Schubert 			else
12703b9b51feSCy Schubert 				ret = ipf_nat_extraflush(softc, softn, arg);
12713b9b51feSCy Schubert 			ipf_proxy_flush(softc->ipf_proxy_soft, arg);
12723b9b51feSCy Schubert 		}
12733b9b51feSCy Schubert 
12743b9b51feSCy Schubert 		if (getlock) {
12753b9b51feSCy Schubert 			RWLOCK_EXIT(&softc->ipf_nat);
12763b9b51feSCy Schubert 		}
12773b9b51feSCy Schubert 		if (error == 0) {
12783b9b51feSCy Schubert 			error = BCOPYOUT(&ret, data, sizeof(ret));
12793b9b51feSCy Schubert 		}
12803b9b51feSCy Schubert 		break;
12813b9b51feSCy Schubert 
12823b9b51feSCy Schubert 	case SIOCMATCHFLUSH :
12833b9b51feSCy Schubert 		if (!(mode & FWRITE)) {
12843b9b51feSCy Schubert 			IPFERROR(60014);
12853b9b51feSCy Schubert 			error = EPERM;
12863b9b51feSCy Schubert 			break;
12873b9b51feSCy Schubert 		}
12883b9b51feSCy Schubert 		if (getlock) {
12893b9b51feSCy Schubert 			WRITE_ENTER(&softc->ipf_nat);
12903b9b51feSCy Schubert 		}
12913b9b51feSCy Schubert 
12923b9b51feSCy Schubert 		error = ipf_nat_matchflush(softc, softn, data);
12933b9b51feSCy Schubert 
12943b9b51feSCy Schubert 		if (getlock) {
12953b9b51feSCy Schubert 			RWLOCK_EXIT(&softc->ipf_nat);
12963b9b51feSCy Schubert 		}
12973b9b51feSCy Schubert 		break;
12983b9b51feSCy Schubert 
12993b9b51feSCy Schubert 	case SIOCPROXY :
13003b9b51feSCy Schubert 		error = ipf_proxy_ioctl(softc, data, cmd, mode, ctx);
13013b9b51feSCy Schubert 		break;
13023b9b51feSCy Schubert 
13033b9b51feSCy Schubert 	case SIOCSTLCK :
13043b9b51feSCy Schubert 		if (!(mode & FWRITE)) {
13053b9b51feSCy Schubert 			IPFERROR(60015);
13063b9b51feSCy Schubert 			error = EPERM;
13073b9b51feSCy Schubert 		} else {
13083b9b51feSCy Schubert 			error = ipf_lock(data, &softn->ipf_nat_lock);
13093b9b51feSCy Schubert 		}
13103b9b51feSCy Schubert 		break;
13113b9b51feSCy Schubert 
13123b9b51feSCy Schubert 	case SIOCSTPUT :
13133b9b51feSCy Schubert 		if ((mode & FWRITE) != 0) {
13143b9b51feSCy Schubert 			error = ipf_nat_putent(softc, data, getlock);
13153b9b51feSCy Schubert 		} else {
13163b9b51feSCy Schubert 			IPFERROR(60016);
13173b9b51feSCy Schubert 			error = EACCES;
13183b9b51feSCy Schubert 		}
13193b9b51feSCy Schubert 		break;
13203b9b51feSCy Schubert 
13213b9b51feSCy Schubert 	case SIOCSTGSZ :
13223b9b51feSCy Schubert 		if (softn->ipf_nat_lock) {
13233b9b51feSCy Schubert 			error = ipf_nat_getsz(softc, data, getlock);
13243b9b51feSCy Schubert 		} else {
13253b9b51feSCy Schubert 			IPFERROR(60017);
13263b9b51feSCy Schubert 			error = EACCES;
13273b9b51feSCy Schubert 		}
13283b9b51feSCy Schubert 		break;
13293b9b51feSCy Schubert 
13303b9b51feSCy Schubert 	case SIOCSTGET :
13313b9b51feSCy Schubert 		if (softn->ipf_nat_lock) {
13323b9b51feSCy Schubert 			error = ipf_nat_getent(softc, data, getlock);
13333b9b51feSCy Schubert 		} else {
13343b9b51feSCy Schubert 			IPFERROR(60018);
13353b9b51feSCy Schubert 			error = EACCES;
13363b9b51feSCy Schubert 		}
13373b9b51feSCy Schubert 		break;
13383b9b51feSCy Schubert 
13393b9b51feSCy Schubert 	case SIOCGENITER :
13403b9b51feSCy Schubert 	    {
13413b9b51feSCy Schubert 		ipfgeniter_t iter;
13423b9b51feSCy Schubert 		ipftoken_t *token;
13433b9b51feSCy Schubert 		ipfobj_t obj;
13443b9b51feSCy Schubert 
13453b9b51feSCy Schubert 		error = ipf_inobj(softc, data, &obj, &iter, IPFOBJ_GENITER);
13463b9b51feSCy Schubert 		if (error != 0)
13473b9b51feSCy Schubert 			break;
13483b9b51feSCy Schubert 
13493b9b51feSCy Schubert 		SPL_SCHED(s);
13503b9b51feSCy Schubert 		token = ipf_token_find(softc, iter.igi_type, uid, ctx);
13513b9b51feSCy Schubert 		if (token != NULL) {
13523b9b51feSCy Schubert 			error  = ipf_nat_iterator(softc, token, &iter, &obj);
13533b9b51feSCy Schubert 			WRITE_ENTER(&softc->ipf_tokens);
13543b9b51feSCy Schubert 			ipf_token_deref(softc, token);
13553b9b51feSCy Schubert 			RWLOCK_EXIT(&softc->ipf_tokens);
13563b9b51feSCy Schubert 		}
13573b9b51feSCy Schubert 		SPL_X(s);
13583b9b51feSCy Schubert 		break;
13593b9b51feSCy Schubert 	    }
13603b9b51feSCy Schubert 
13613b9b51feSCy Schubert 	case SIOCIPFDELTOK :
13623b9b51feSCy Schubert 		error = BCOPYIN(data, &arg, sizeof(arg));
13633b9b51feSCy Schubert 		if (error == 0) {
13643b9b51feSCy Schubert 			SPL_SCHED(s);
13653b9b51feSCy Schubert 			error = ipf_token_del(softc, arg, uid, ctx);
13663b9b51feSCy Schubert 			SPL_X(s);
13673b9b51feSCy Schubert 		} else {
13683b9b51feSCy Schubert 			IPFERROR(60019);
13693b9b51feSCy Schubert 			error = EFAULT;
13703b9b51feSCy Schubert 		}
13713b9b51feSCy Schubert 		break;
13723b9b51feSCy Schubert 
13733b9b51feSCy Schubert 	case SIOCGTQTAB :
13743b9b51feSCy Schubert 		error = ipf_outobj(softc, data, softn->ipf_nat_tcptq,
13753b9b51feSCy Schubert 				   IPFOBJ_STATETQTAB);
13763b9b51feSCy Schubert 		break;
13773b9b51feSCy Schubert 
13783b9b51feSCy Schubert 	case SIOCGTABL :
13793b9b51feSCy Schubert 		error = ipf_nat_gettable(softc, softn, data);
13803b9b51feSCy Schubert 		break;
13813b9b51feSCy Schubert 
13823b9b51feSCy Schubert 	default :
13833b9b51feSCy Schubert 		IPFERROR(60020);
13843b9b51feSCy Schubert 		error = EINVAL;
13853b9b51feSCy Schubert 		break;
13863b9b51feSCy Schubert 	}
13873b9b51feSCy Schubert done:
13883b9b51feSCy Schubert 	if (nat != NULL)
13893b9b51feSCy Schubert 		ipf_nat_rule_fini(softc, nat);
13903b9b51feSCy Schubert 	if (nt != NULL)
13913b9b51feSCy Schubert 		KFREES(nt, nt->in_size);
13928c82b374SCy Schubert 	return (error);
13933b9b51feSCy Schubert }
13943b9b51feSCy Schubert 
13953b9b51feSCy Schubert 
13963b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
13973b9b51feSCy Schubert /* Function:    ipf_nat_siocaddnat                                          */
13983b9b51feSCy Schubert /* Returns:     int - 0 == success, != 0 == failure                         */
13993b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
14003b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
14013b9b51feSCy Schubert /*              n(I)       - pointer to new NAT rule                        */
14023b9b51feSCy Schubert /*              np(I)      - pointer to where to insert new NAT rule        */
14033b9b51feSCy Schubert /*              getlock(I) - flag indicating if lock on  is held            */
14043b9b51feSCy Schubert /* Mutex Locks: ipf_nat_io                                                  */
14053b9b51feSCy Schubert /*                                                                          */
14063b9b51feSCy Schubert /* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
14073b9b51feSCy Schubert /* from information passed to the kernel, then add it  to the appropriate   */
14083b9b51feSCy Schubert /* NAT rule table(s).                                                       */
14093b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
14103b9b51feSCy Schubert static int
ipf_nat_siocaddnat(ipf_main_softc_t * softc,ipf_nat_softc_t * softn,ipnat_t * n,int getlock)1411064a5a95SCy Schubert ipf_nat_siocaddnat(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *n,
1412064a5a95SCy Schubert 	int getlock)
14133b9b51feSCy Schubert {
14143b9b51feSCy Schubert 	int error = 0;
14153b9b51feSCy Schubert 
14163b9b51feSCy Schubert 	if (ipf_nat_resolverule(softc, n) != 0) {
14173b9b51feSCy Schubert 		IPFERROR(60022);
14188c82b374SCy Schubert 		return (ENOENT);
14193b9b51feSCy Schubert 	}
14203b9b51feSCy Schubert 
14213b9b51feSCy Schubert 	if ((n->in_age[0] == 0) && (n->in_age[1] != 0)) {
14223b9b51feSCy Schubert 		IPFERROR(60023);
14238c82b374SCy Schubert 		return (EINVAL);
14243b9b51feSCy Schubert 	}
14253b9b51feSCy Schubert 
14263b9b51feSCy Schubert 	if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) {
14273b9b51feSCy Schubert 		/*
14283b9b51feSCy Schubert 		 * Prerecord whether or not the destination of the divert
14293b9b51feSCy Schubert 		 * is local or not to the interface the packet is going
14303b9b51feSCy Schubert 		 * to be sent out.
14313b9b51feSCy Schubert 		 */
14323b9b51feSCy Schubert 		n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1],
14333b9b51feSCy Schubert 						n->in_ifps[1], &n->in_ndstip6);
14343b9b51feSCy Schubert 	}
14353b9b51feSCy Schubert 
14363b9b51feSCy Schubert 	if (getlock) {
14373b9b51feSCy Schubert 		WRITE_ENTER(&softc->ipf_nat);
14383b9b51feSCy Schubert 	}
14393b9b51feSCy Schubert 	n->in_next = NULL;
14403b9b51feSCy Schubert 	n->in_pnext = softn->ipf_nat_list_tail;
14413b9b51feSCy Schubert 	*n->in_pnext = n;
14423b9b51feSCy Schubert 	softn->ipf_nat_list_tail = &n->in_next;
14433b9b51feSCy Schubert 	n->in_use++;
14443b9b51feSCy Schubert 
14453b9b51feSCy Schubert 	if (n->in_redir & NAT_REDIRECT) {
14463b9b51feSCy Schubert 		n->in_flags &= ~IPN_NOTDST;
14473b9b51feSCy Schubert 		switch (n->in_v[0])
14483b9b51feSCy Schubert 		{
14493b9b51feSCy Schubert 		case 4 :
14503b9b51feSCy Schubert 			ipf_nat_addrdr(softn, n);
14513b9b51feSCy Schubert 			break;
14523b9b51feSCy Schubert #ifdef USE_INET6
14533b9b51feSCy Schubert 		case 6 :
14543b9b51feSCy Schubert 			ipf_nat6_addrdr(softn, n);
14553b9b51feSCy Schubert 			break;
14563b9b51feSCy Schubert #endif
14573b9b51feSCy Schubert 		default :
14583b9b51feSCy Schubert 			break;
14593b9b51feSCy Schubert 		}
14603b9b51feSCy Schubert 		ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_rdr);
14613b9b51feSCy Schubert 	}
14623b9b51feSCy Schubert 
14633b9b51feSCy Schubert 	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
14643b9b51feSCy Schubert 		n->in_flags &= ~IPN_NOTSRC;
14653b9b51feSCy Schubert 		switch (n->in_v[0])
14663b9b51feSCy Schubert 		{
14673b9b51feSCy Schubert 		case 4 :
14683b9b51feSCy Schubert 			ipf_nat_addmap(softn, n);
14693b9b51feSCy Schubert 			break;
14703b9b51feSCy Schubert #ifdef USE_INET6
14713b9b51feSCy Schubert 		case 6 :
14723b9b51feSCy Schubert 			ipf_nat6_addmap(softn, n);
14733b9b51feSCy Schubert 			break;
14743b9b51feSCy Schubert #endif
14753b9b51feSCy Schubert 		default :
14763b9b51feSCy Schubert 			break;
14773b9b51feSCy Schubert 		}
14783b9b51feSCy Schubert 		ATOMIC_INC32(softn->ipf_nat_stats.ns_rules_map);
14793b9b51feSCy Schubert 	}
14803b9b51feSCy Schubert 
14813b9b51feSCy Schubert 	if (n->in_age[0] != 0)
14823b9b51feSCy Schubert 		n->in_tqehead[0] = ipf_addtimeoutqueue(softc,
14833b9b51feSCy Schubert 						       &softn->ipf_nat_utqe,
14843b9b51feSCy Schubert 						       n->in_age[0]);
14853b9b51feSCy Schubert 
14863b9b51feSCy Schubert 	if (n->in_age[1] != 0)
14873b9b51feSCy Schubert 		n->in_tqehead[1] = ipf_addtimeoutqueue(softc,
14883b9b51feSCy Schubert 						       &softn->ipf_nat_utqe,
14893b9b51feSCy Schubert 						       n->in_age[1]);
14903b9b51feSCy Schubert 
14913b9b51feSCy Schubert 	MUTEX_INIT(&n->in_lock, "ipnat rule lock");
14923b9b51feSCy Schubert 
14933b9b51feSCy Schubert 	n = NULL;
14943b9b51feSCy Schubert 	ATOMIC_INC32(softn->ipf_nat_stats.ns_rules);
14953b9b51feSCy Schubert #if SOLARIS && !defined(INSTANCES)
14963b9b51feSCy Schubert 	pfil_delayed_copy = 0;
14973b9b51feSCy Schubert #endif
14983b9b51feSCy Schubert 	if (getlock) {
14993b9b51feSCy Schubert 		RWLOCK_EXIT(&softc->ipf_nat);			/* WRITE */
15003b9b51feSCy Schubert 	}
15013b9b51feSCy Schubert 
15028c82b374SCy Schubert 	return (error);
15033b9b51feSCy Schubert }
15043b9b51feSCy Schubert 
15053b9b51feSCy Schubert 
15063b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
15073b9b51feSCy Schubert /* Function:    ipf_nat_ruleaddrinit                                        */
15083b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
15093b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
15103b9b51feSCy Schubert /*              n(I)     - pointer to NAT rule                              */
15113b9b51feSCy Schubert /*                                                                          */
15123b9b51feSCy Schubert /* Initialise all of the NAT address structures in a NAT rule.              */
15133b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
15143b9b51feSCy Schubert static int
ipf_nat_ruleaddrinit(ipf_main_softc_t * softc,ipf_nat_softc_t * softn,ipnat_t * n)1515064a5a95SCy Schubert ipf_nat_ruleaddrinit(ipf_main_softc_t *softc, ipf_nat_softc_t *softn,
1516064a5a95SCy Schubert 	ipnat_t *n)
15173b9b51feSCy Schubert {
15183b9b51feSCy Schubert 	int idx, error;
15193b9b51feSCy Schubert 
15203b9b51feSCy Schubert 	if ((n->in_ndst.na_atype == FRI_LOOKUP) &&
15213b9b51feSCy Schubert 	    (n->in_ndst.na_type != IPLT_DSTLIST)) {
15223b9b51feSCy Schubert 		IPFERROR(60071);
15238c82b374SCy Schubert 		return (EINVAL);
15243b9b51feSCy Schubert 	}
15253b9b51feSCy Schubert 	if ((n->in_nsrc.na_atype == FRI_LOOKUP) &&
15263b9b51feSCy Schubert 	    (n->in_nsrc.na_type != IPLT_DSTLIST)) {
15273b9b51feSCy Schubert 		IPFERROR(60069);
15288c82b374SCy Schubert 		return (EINVAL);
15293b9b51feSCy Schubert 	}
15303b9b51feSCy Schubert 
15313b9b51feSCy Schubert 	if (n->in_redir == NAT_BIMAP) {
15323b9b51feSCy Schubert 		n->in_ndstaddr = n->in_osrcaddr;
15333b9b51feSCy Schubert 		n->in_ndstmsk = n->in_osrcmsk;
15343b9b51feSCy Schubert 		n->in_odstaddr = n->in_nsrcaddr;
15353b9b51feSCy Schubert 		n->in_odstmsk = n->in_nsrcmsk;
15363b9b51feSCy Schubert 
15373b9b51feSCy Schubert 	}
15383b9b51feSCy Schubert 
15393b9b51feSCy Schubert 	if (n->in_redir & NAT_REDIRECT)
15403b9b51feSCy Schubert 		idx = 1;
15413b9b51feSCy Schubert 	else
15423b9b51feSCy Schubert 		idx = 0;
15433b9b51feSCy Schubert 	/*
15443b9b51feSCy Schubert 	 * Initialise all of the address fields.
15453b9b51feSCy Schubert 	 */
15463b9b51feSCy Schubert 	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc, 1,
15473b9b51feSCy Schubert 				     n->in_ifps[idx]);
15483b9b51feSCy Schubert 	if (error != 0)
15498c82b374SCy Schubert 		return (error);
15503b9b51feSCy Schubert 
15513b9b51feSCy Schubert 	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst, 1,
15523b9b51feSCy Schubert 				     n->in_ifps[idx]);
15533b9b51feSCy Schubert 	if (error != 0)
15548c82b374SCy Schubert 		return (error);
15553b9b51feSCy Schubert 
15563b9b51feSCy Schubert 	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1,
15573b9b51feSCy Schubert 				     n->in_ifps[idx]);
15583b9b51feSCy Schubert 	if (error != 0)
15598c82b374SCy Schubert 		return (error);
15603b9b51feSCy Schubert 
15613b9b51feSCy Schubert 	error = ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst, 1,
15623b9b51feSCy Schubert 				     n->in_ifps[idx]);
15633b9b51feSCy Schubert 	if (error != 0)
15648c82b374SCy Schubert 		return (error);
15653b9b51feSCy Schubert 
15663b9b51feSCy Schubert 	if (n->in_redir & NAT_DIVERTUDP)
15673b9b51feSCy Schubert 		ipf_nat_builddivertmp(softn, n);
15683b9b51feSCy Schubert 
15698c82b374SCy Schubert 	return (0);
15703b9b51feSCy Schubert }
15713b9b51feSCy Schubert 
15723b9b51feSCy Schubert 
15733b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
15743b9b51feSCy Schubert /* Function:    ipf_nat_resolvrule                                          */
15753b9b51feSCy Schubert /* Returns:     Nil                                                         */
15763b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
15773b9b51feSCy Schubert /*              n(I)     - pointer to NAT rule                              */
15783b9b51feSCy Schubert /*                                                                          */
15793b9b51feSCy Schubert /* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
15803b9b51feSCy Schubert /* from information passed to the kernel, then add it  to the appropriate   */
15813b9b51feSCy Schubert /* NAT rule table(s).                                                       */
15823b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
15833b9b51feSCy Schubert static int
ipf_nat_resolverule(ipf_main_softc_t * softc,ipnat_t * n)1584064a5a95SCy Schubert ipf_nat_resolverule(ipf_main_softc_t *softc, ipnat_t *n)
15853b9b51feSCy Schubert {
15863b9b51feSCy Schubert 	char *base;
15873b9b51feSCy Schubert 
15883b9b51feSCy Schubert 	base = n->in_names;
15893b9b51feSCy Schubert 
15903b9b51feSCy Schubert 	n->in_ifps[0] = ipf_resolvenic(softc, base + n->in_ifnames[0],
15913b9b51feSCy Schubert 				       n->in_v[0]);
15923b9b51feSCy Schubert 
15933b9b51feSCy Schubert 	if (n->in_ifnames[1] == -1) {
15943b9b51feSCy Schubert 		n->in_ifnames[1] = n->in_ifnames[0];
15953b9b51feSCy Schubert 		n->in_ifps[1] = n->in_ifps[0];
15963b9b51feSCy Schubert 	} else {
15973b9b51feSCy Schubert 		n->in_ifps[1] = ipf_resolvenic(softc, base + n->in_ifnames[1],
15983b9b51feSCy Schubert 					       n->in_v[1]);
15993b9b51feSCy Schubert 	}
16003b9b51feSCy Schubert 
16013b9b51feSCy Schubert 	if (n->in_plabel != -1) {
16023b9b51feSCy Schubert 		if (n->in_redir & NAT_REDIRECT)
16033b9b51feSCy Schubert 			n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft,
16043b9b51feSCy Schubert 						     n->in_pr[0],
16053b9b51feSCy Schubert 						     base + n->in_plabel);
16063b9b51feSCy Schubert 		else
16073b9b51feSCy Schubert 			n->in_apr = ipf_proxy_lookup(softc->ipf_proxy_soft,
16083b9b51feSCy Schubert 						     n->in_pr[1],
16093b9b51feSCy Schubert 						     base + n->in_plabel);
16103b9b51feSCy Schubert 		if (n->in_apr == NULL)
16118c82b374SCy Schubert 			return (-1);
16123b9b51feSCy Schubert 	}
16138c82b374SCy Schubert 	return (0);
16143b9b51feSCy Schubert }
16153b9b51feSCy Schubert 
16163b9b51feSCy Schubert 
16173b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
16183b9b51feSCy Schubert /* Function:    ipf_nat_siocdelnat                                          */
16193b9b51feSCy Schubert /* Returns:     int - 0 == success, != 0 == failure                         */
16203b9b51feSCy Schubert /* Parameters:  softc(I)   - pointer to soft context main structure         */
16213b9b51feSCy Schubert /*              softn(I)   - pointer to NAT context structure               */
16223b9b51feSCy Schubert /*              n(I)       - pointer to new NAT rule                        */
16233b9b51feSCy Schubert /*              getlock(I) - flag indicating if lock on  is held            */
16243b9b51feSCy Schubert /* Mutex Locks: ipf_nat_io                                                  */
16253b9b51feSCy Schubert /*                                                                          */
16263b9b51feSCy Schubert /* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
16273b9b51feSCy Schubert /* from information passed to the kernel, then add it  to the appropriate   */
16283b9b51feSCy Schubert /* NAT rule table(s).                                                       */
16293b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
16303b9b51feSCy Schubert static void
ipf_nat_siocdelnat(ipf_main_softc_t * softc,ipf_nat_softc_t * softn,ipnat_t * n,int getlock)1631064a5a95SCy Schubert ipf_nat_siocdelnat(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *n,
1632064a5a95SCy Schubert 	int getlock)
16333b9b51feSCy Schubert {
16343b9b51feSCy Schubert 	if (getlock) {
16353b9b51feSCy Schubert 		WRITE_ENTER(&softc->ipf_nat);
16363b9b51feSCy Schubert 	}
16373b9b51feSCy Schubert 
16383b9b51feSCy Schubert 	ipf_nat_delrule(softc, softn, n, 1);
16393b9b51feSCy Schubert 
16403b9b51feSCy Schubert 	if (getlock) {
16413b9b51feSCy Schubert 		RWLOCK_EXIT(&softc->ipf_nat);			/* READ/WRITE */
16423b9b51feSCy Schubert 	}
16433b9b51feSCy Schubert }
16443b9b51feSCy Schubert 
16453b9b51feSCy Schubert 
16463b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
16473b9b51feSCy Schubert /* Function:    ipf_nat_getsz                                               */
16483b9b51feSCy Schubert /* Returns:     int - 0 == success, != 0 is the error value.                */
16493b9b51feSCy Schubert /* Parameters:  softc(I)   - pointer to soft context main structure         */
16503b9b51feSCy Schubert /*              data(I)    - pointer to natget structure with kernel        */
16513b9b51feSCy Schubert /*                           pointer get the size of.                       */
16523b9b51feSCy Schubert /*              getlock(I) - flag indicating whether or not the caller      */
16533b9b51feSCy Schubert /*                           holds a lock on ipf_nat                        */
16543b9b51feSCy Schubert /*                                                                          */
16553b9b51feSCy Schubert /* Handle SIOCSTGSZ.                                                        */
16563b9b51feSCy Schubert /* Return the size of the nat list entry to be copied back to user space.   */
16573b9b51feSCy Schubert /* The size of the entry is stored in the ng_sz field and the enture natget */
16583b9b51feSCy Schubert /* structure is copied back to the user.                                    */
16593b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
16603b9b51feSCy Schubert static int
ipf_nat_getsz(ipf_main_softc_t * softc,caddr_t data,int getlock)1661064a5a95SCy Schubert ipf_nat_getsz(ipf_main_softc_t *softc, caddr_t data, int getlock)
16623b9b51feSCy Schubert {
16633b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
16643b9b51feSCy Schubert 	ap_session_t *aps;
16653b9b51feSCy Schubert 	nat_t *nat, *n;
16663b9b51feSCy Schubert 	natget_t ng;
16673b9b51feSCy Schubert 	int error;
16683b9b51feSCy Schubert 
16693b9b51feSCy Schubert 	error = BCOPYIN(data, &ng, sizeof(ng));
16703b9b51feSCy Schubert 	if (error != 0) {
16713b9b51feSCy Schubert 		IPFERROR(60024);
16728c82b374SCy Schubert 		return (EFAULT);
16733b9b51feSCy Schubert 	}
16743b9b51feSCy Schubert 
16753b9b51feSCy Schubert 	if (getlock) {
16763b9b51feSCy Schubert 		READ_ENTER(&softc->ipf_nat);
16773b9b51feSCy Schubert 	}
16783b9b51feSCy Schubert 
16793b9b51feSCy Schubert 	nat = ng.ng_ptr;
16803b9b51feSCy Schubert 	if (!nat) {
16813b9b51feSCy Schubert 		nat = softn->ipf_nat_instances;
16823b9b51feSCy Schubert 		ng.ng_sz = 0;
16833b9b51feSCy Schubert 		/*
16843b9b51feSCy Schubert 		 * Empty list so the size returned is 0.  Simple.
16853b9b51feSCy Schubert 		 */
16863b9b51feSCy Schubert 		if (nat == NULL) {
16873b9b51feSCy Schubert 			if (getlock) {
16883b9b51feSCy Schubert 				RWLOCK_EXIT(&softc->ipf_nat);
16893b9b51feSCy Schubert 			}
16903b9b51feSCy Schubert 			error = BCOPYOUT(&ng, data, sizeof(ng));
16913b9b51feSCy Schubert 			if (error != 0) {
16923b9b51feSCy Schubert 				IPFERROR(60025);
16938c82b374SCy Schubert 				return (EFAULT);
16943b9b51feSCy Schubert 			}
16958c82b374SCy Schubert 			return (0);
16963b9b51feSCy Schubert 		}
16973b9b51feSCy Schubert 	} else {
16983b9b51feSCy Schubert 		/*
16993b9b51feSCy Schubert 		 * Make sure the pointer we're copying from exists in the
17003b9b51feSCy Schubert 		 * current list of entries.  Security precaution to prevent
17013b9b51feSCy Schubert 		 * copying of random kernel data.
17023b9b51feSCy Schubert 		 */
17033b9b51feSCy Schubert 		for (n = softn->ipf_nat_instances; n; n = n->nat_next)
17043b9b51feSCy Schubert 			if (n == nat)
17053b9b51feSCy Schubert 				break;
17063b9b51feSCy Schubert 		if (n == NULL) {
17073b9b51feSCy Schubert 			if (getlock) {
17083b9b51feSCy Schubert 				RWLOCK_EXIT(&softc->ipf_nat);
17093b9b51feSCy Schubert 			}
17103b9b51feSCy Schubert 			IPFERROR(60026);
17118c82b374SCy Schubert 			return (ESRCH);
17123b9b51feSCy Schubert 		}
17133b9b51feSCy Schubert 	}
17143b9b51feSCy Schubert 
17153b9b51feSCy Schubert 	/*
1716445ecc48SCy Schubert 	 * Include any space required for proxy data structures.
17173b9b51feSCy Schubert 	 */
17183b9b51feSCy Schubert 	ng.ng_sz = sizeof(nat_save_t);
17193b9b51feSCy Schubert 	aps = nat->nat_aps;
17203b9b51feSCy Schubert 	if (aps != NULL) {
17213b9b51feSCy Schubert 		ng.ng_sz += sizeof(ap_session_t) - 4;
17223b9b51feSCy Schubert 		if (aps->aps_data != 0)
17233b9b51feSCy Schubert 			ng.ng_sz += aps->aps_psiz;
17243b9b51feSCy Schubert 	}
17253b9b51feSCy Schubert 	if (getlock) {
17263b9b51feSCy Schubert 		RWLOCK_EXIT(&softc->ipf_nat);
17273b9b51feSCy Schubert 	}
17283b9b51feSCy Schubert 
17293b9b51feSCy Schubert 	error = BCOPYOUT(&ng, data, sizeof(ng));
17303b9b51feSCy Schubert 	if (error != 0) {
17313b9b51feSCy Schubert 		IPFERROR(60027);
17328c82b374SCy Schubert 		return (EFAULT);
17333b9b51feSCy Schubert 	}
17348c82b374SCy Schubert 	return (0);
17353b9b51feSCy Schubert }
17363b9b51feSCy Schubert 
17373b9b51feSCy Schubert 
17383b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
17393b9b51feSCy Schubert /* Function:    ipf_nat_getent                                              */
17403b9b51feSCy Schubert /* Returns:     int - 0 == success, != 0 is the error value.                */
17413b9b51feSCy Schubert /* Parameters:  softc(I)   - pointer to soft context main structure         */
17423b9b51feSCy Schubert /*              data(I)    - pointer to natget structure with kernel pointer*/
17433b9b51feSCy Schubert /*                           to NAT structure to copy out.                  */
17443b9b51feSCy Schubert /*              getlock(I) - flag indicating whether or not the caller      */
17453b9b51feSCy Schubert /*                           holds a lock on ipf_nat                        */
17463b9b51feSCy Schubert /*                                                                          */
17473b9b51feSCy Schubert /* Handle SIOCSTGET.                                                        */
17483b9b51feSCy Schubert /* Copies out NAT entry to user space.  Any additional data held for a      */
17493b9b51feSCy Schubert /* proxy is also copied, as to is the NAT rule which was responsible for it */
17503b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
17513b9b51feSCy Schubert static int
ipf_nat_getent(ipf_main_softc_t * softc,caddr_t data,int getlock)1752064a5a95SCy Schubert ipf_nat_getent(ipf_main_softc_t *softc, caddr_t data, int getlock)
17533b9b51feSCy Schubert {
17543b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
17553b9b51feSCy Schubert 	int error, outsize;
17563b9b51feSCy Schubert 	ap_session_t *aps;
17573b9b51feSCy Schubert 	nat_save_t *ipn, ipns;
17583b9b51feSCy Schubert 	nat_t *n, *nat;
17593b9b51feSCy Schubert 
17603b9b51feSCy Schubert 	error = ipf_inobj(softc, data, NULL, &ipns, IPFOBJ_NATSAVE);
17613b9b51feSCy Schubert 	if (error != 0)
17628c82b374SCy Schubert 		return (error);
17633b9b51feSCy Schubert 
17643b9b51feSCy Schubert 	if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920)) {
17653b9b51feSCy Schubert 		IPFERROR(60028);
17668c82b374SCy Schubert 		return (EINVAL);
17673b9b51feSCy Schubert 	}
17683b9b51feSCy Schubert 
17693b9b51feSCy Schubert 	KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize);
17703b9b51feSCy Schubert 	if (ipn == NULL) {
17713b9b51feSCy Schubert 		IPFERROR(60029);
17728c82b374SCy Schubert 		return (ENOMEM);
17733b9b51feSCy Schubert 	}
17743b9b51feSCy Schubert 
17753b9b51feSCy Schubert 	if (getlock) {
17763b9b51feSCy Schubert 		READ_ENTER(&softc->ipf_nat);
17773b9b51feSCy Schubert 	}
17783b9b51feSCy Schubert 
17793b9b51feSCy Schubert 	ipn->ipn_dsize = ipns.ipn_dsize;
17803b9b51feSCy Schubert 	nat = ipns.ipn_next;
17813b9b51feSCy Schubert 	if (nat == NULL) {
17823b9b51feSCy Schubert 		nat = softn->ipf_nat_instances;
17833b9b51feSCy Schubert 		if (nat == NULL) {
17843b9b51feSCy Schubert 			if (softn->ipf_nat_instances == NULL) {
17853b9b51feSCy Schubert 				IPFERROR(60030);
17863b9b51feSCy Schubert 				error = ENOENT;
17873b9b51feSCy Schubert 			}
17883b9b51feSCy Schubert 			goto finished;
17893b9b51feSCy Schubert 		}
17903b9b51feSCy Schubert 	} else {
17913b9b51feSCy Schubert 		/*
17923b9b51feSCy Schubert 		 * Make sure the pointer we're copying from exists in the
17933b9b51feSCy Schubert 		 * current list of entries.  Security precaution to prevent
17943b9b51feSCy Schubert 		 * copying of random kernel data.
17953b9b51feSCy Schubert 		 */
17963b9b51feSCy Schubert 		for (n = softn->ipf_nat_instances; n; n = n->nat_next)
17973b9b51feSCy Schubert 			if (n == nat)
17983b9b51feSCy Schubert 				break;
17993b9b51feSCy Schubert 		if (n == NULL) {
18003b9b51feSCy Schubert 			IPFERROR(60031);
18013b9b51feSCy Schubert 			error = ESRCH;
18023b9b51feSCy Schubert 			goto finished;
18033b9b51feSCy Schubert 		}
18043b9b51feSCy Schubert 	}
18053b9b51feSCy Schubert 	ipn->ipn_next = nat->nat_next;
18063b9b51feSCy Schubert 
18073b9b51feSCy Schubert 	/*
18083b9b51feSCy Schubert 	 * Copy the NAT structure.
18093b9b51feSCy Schubert 	 */
18103b9b51feSCy Schubert 	bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat));
18113b9b51feSCy Schubert 
18123b9b51feSCy Schubert 	/*
18133b9b51feSCy Schubert 	 * If we have a pointer to the NAT rule it belongs to, save that too.
18143b9b51feSCy Schubert 	 */
18153b9b51feSCy Schubert 	if (nat->nat_ptr != NULL)
18163b9b51feSCy Schubert 		bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat,
18173b9b51feSCy Schubert 		      sizeof(nat->nat_ptr));
18183b9b51feSCy Schubert 
18193b9b51feSCy Schubert 	/*
18203b9b51feSCy Schubert 	 * If we also know the NAT entry has an associated filter rule,
18213b9b51feSCy Schubert 	 * save that too.
18223b9b51feSCy Schubert 	 */
18233b9b51feSCy Schubert 	if (nat->nat_fr != NULL)
18243b9b51feSCy Schubert 		bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr,
18253b9b51feSCy Schubert 		      sizeof(ipn->ipn_fr));
18263b9b51feSCy Schubert 
18273b9b51feSCy Schubert 	/*
18283b9b51feSCy Schubert 	 * Last but not least, if there is an application proxy session set
18293b9b51feSCy Schubert 	 * up for this NAT entry, then copy that out too, including any
18303b9b51feSCy Schubert 	 * private data saved along side it by the proxy.
18313b9b51feSCy Schubert 	 */
18323b9b51feSCy Schubert 	aps = nat->nat_aps;
18333b9b51feSCy Schubert 	outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data);
18343b9b51feSCy Schubert 	if (aps != NULL) {
18353b9b51feSCy Schubert 		char *s;
18363b9b51feSCy Schubert 
18373b9b51feSCy Schubert 		if (outsize < sizeof(*aps)) {
18383b9b51feSCy Schubert 			IPFERROR(60032);
18393b9b51feSCy Schubert 			error = ENOBUFS;
18403b9b51feSCy Schubert 			goto finished;
18413b9b51feSCy Schubert 		}
18423b9b51feSCy Schubert 
18433b9b51feSCy Schubert 		s = ipn->ipn_data;
18443b9b51feSCy Schubert 		bcopy((char *)aps, s, sizeof(*aps));
18453b9b51feSCy Schubert 		s += sizeof(*aps);
18463b9b51feSCy Schubert 		outsize -= sizeof(*aps);
18473b9b51feSCy Schubert 		if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz))
18483b9b51feSCy Schubert 			bcopy(aps->aps_data, s, aps->aps_psiz);
18493b9b51feSCy Schubert 		else {
18503b9b51feSCy Schubert 			IPFERROR(60033);
18513b9b51feSCy Schubert 			error = ENOBUFS;
18523b9b51feSCy Schubert 		}
18533b9b51feSCy Schubert 	}
18543b9b51feSCy Schubert 	if (error == 0) {
18553b9b51feSCy Schubert 		error = ipf_outobjsz(softc, data, ipn, IPFOBJ_NATSAVE,
18563b9b51feSCy Schubert 				     ipns.ipn_dsize);
18573b9b51feSCy Schubert 	}
18583b9b51feSCy Schubert 
18593b9b51feSCy Schubert finished:
18603b9b51feSCy Schubert 	if (ipn != NULL) {
18613b9b51feSCy Schubert 		KFREES(ipn, ipns.ipn_dsize);
18623b9b51feSCy Schubert 	}
18633b9b51feSCy Schubert 	if (getlock) {
18643b9b51feSCy Schubert 		RWLOCK_EXIT(&softc->ipf_nat);
18653b9b51feSCy Schubert 	}
18668c82b374SCy Schubert 	return (error);
18673b9b51feSCy Schubert }
18683b9b51feSCy Schubert 
18693b9b51feSCy Schubert 
18703b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
18713b9b51feSCy Schubert /* Function:    ipf_nat_putent                                              */
18723b9b51feSCy Schubert /* Returns:     int - 0 == success, != 0 is the error value.                */
18733b9b51feSCy Schubert /* Parameters:  softc(I)   - pointer to soft context main structure         */
18743b9b51feSCy Schubert /*              data(I)    - pointer to natget structure with NAT           */
18753b9b51feSCy Schubert /*                           structure information to load into the kernel  */
18763b9b51feSCy Schubert /*              getlock(I) - flag indicating whether or not a write lock    */
18773b9b51feSCy Schubert /*                           on is already held.                            */
18783b9b51feSCy Schubert /*                                                                          */
18793b9b51feSCy Schubert /* Handle SIOCSTPUT.                                                        */
18803b9b51feSCy Schubert /* Loads a NAT table entry from user space, including a NAT rule, proxy and */
18813b9b51feSCy Schubert /* firewall rule data structures, if pointers to them indicate so.          */
18823b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
18833b9b51feSCy Schubert static int
ipf_nat_putent(ipf_main_softc_t * softc,caddr_t data,int getlock)1884064a5a95SCy Schubert ipf_nat_putent(ipf_main_softc_t *softc, caddr_t data, int getlock)
18853b9b51feSCy Schubert {
18863b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
18873b9b51feSCy Schubert 	nat_save_t ipn, *ipnn;
18883b9b51feSCy Schubert 	ap_session_t *aps;
18893b9b51feSCy Schubert 	nat_t *n, *nat;
18903b9b51feSCy Schubert 	frentry_t *fr;
18913b9b51feSCy Schubert 	fr_info_t fin;
18923b9b51feSCy Schubert 	ipnat_t *in;
18933b9b51feSCy Schubert 	int error;
18943b9b51feSCy Schubert 
18953b9b51feSCy Schubert 	error = ipf_inobj(softc, data, NULL, &ipn, IPFOBJ_NATSAVE);
18963b9b51feSCy Schubert 	if (error != 0)
18978c82b374SCy Schubert 		return (error);
18983b9b51feSCy Schubert 
18993b9b51feSCy Schubert 	/*
19003b9b51feSCy Schubert 	 * Initialise early because of code at junkput label.
19013b9b51feSCy Schubert 	 */
19023b9b51feSCy Schubert 	n = NULL;
19033b9b51feSCy Schubert 	in = NULL;
19043b9b51feSCy Schubert 	aps = NULL;
19053b9b51feSCy Schubert 	nat = NULL;
19063b9b51feSCy Schubert 	ipnn = NULL;
19073b9b51feSCy Schubert 	fr = NULL;
19083b9b51feSCy Schubert 
19093b9b51feSCy Schubert 	/*
19103b9b51feSCy Schubert 	 * New entry, copy in the rest of the NAT entry if it's size is more
19113b9b51feSCy Schubert 	 * than just the nat_t structure.
19123b9b51feSCy Schubert 	 */
19133b9b51feSCy Schubert 	if (ipn.ipn_dsize > sizeof(ipn)) {
19143b9b51feSCy Schubert 		if (ipn.ipn_dsize > 81920) {
19153b9b51feSCy Schubert 			IPFERROR(60034);
19163b9b51feSCy Schubert 			error = ENOMEM;
19173b9b51feSCy Schubert 			goto junkput;
19183b9b51feSCy Schubert 		}
19193b9b51feSCy Schubert 
19203b9b51feSCy Schubert 		KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize);
19213b9b51feSCy Schubert 		if (ipnn == NULL) {
19223b9b51feSCy Schubert 			IPFERROR(60035);
19238c82b374SCy Schubert 			return (ENOMEM);
19243b9b51feSCy Schubert 		}
19253b9b51feSCy Schubert 
19263b9b51feSCy Schubert 		bzero(ipnn, ipn.ipn_dsize);
19273b9b51feSCy Schubert 		error = ipf_inobjsz(softc, data, ipnn, IPFOBJ_NATSAVE,
19283b9b51feSCy Schubert 				    ipn.ipn_dsize);
19293b9b51feSCy Schubert 		if (error != 0) {
19303b9b51feSCy Schubert 			goto junkput;
19313b9b51feSCy Schubert 		}
19323b9b51feSCy Schubert 	} else
19333b9b51feSCy Schubert 		ipnn = &ipn;
19343b9b51feSCy Schubert 
19353b9b51feSCy Schubert 	KMALLOC(nat, nat_t *);
19363b9b51feSCy Schubert 	if (nat == NULL) {
19373b9b51feSCy Schubert 		IPFERROR(60037);
19383b9b51feSCy Schubert 		error = ENOMEM;
19393b9b51feSCy Schubert 		goto junkput;
19403b9b51feSCy Schubert 	}
19413b9b51feSCy Schubert 
19423b9b51feSCy Schubert 	bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat));
19433b9b51feSCy Schubert 
19443b9b51feSCy Schubert 	switch (nat->nat_v[0])
19453b9b51feSCy Schubert 	{
19463b9b51feSCy Schubert 	case 4:
19473b9b51feSCy Schubert #ifdef USE_INET6
19483b9b51feSCy Schubert 	case 6 :
19493b9b51feSCy Schubert #endif
19503b9b51feSCy Schubert 		break;
19513b9b51feSCy Schubert 	default :
19523b9b51feSCy Schubert 		IPFERROR(60061);
19533b9b51feSCy Schubert 		error = EPROTONOSUPPORT;
19543b9b51feSCy Schubert 		goto junkput;
19553b9b51feSCy Schubert 		/*NOTREACHED*/
19563b9b51feSCy Schubert 	}
19573b9b51feSCy Schubert 
19583b9b51feSCy Schubert 	/*
19593b9b51feSCy Schubert 	 * Initialize all these so that ipf_nat_delete() doesn't cause a crash.
19603b9b51feSCy Schubert 	 */
19613b9b51feSCy Schubert 	bzero((char *)nat, offsetof(struct nat, nat_tqe));
19623b9b51feSCy Schubert 	nat->nat_tqe.tqe_pnext = NULL;
19633b9b51feSCy Schubert 	nat->nat_tqe.tqe_next = NULL;
19643b9b51feSCy Schubert 	nat->nat_tqe.tqe_ifq = NULL;
19653b9b51feSCy Schubert 	nat->nat_tqe.tqe_parent = nat;
19663b9b51feSCy Schubert 
19673b9b51feSCy Schubert 	/*
19683b9b51feSCy Schubert 	 * Restore the rule associated with this nat session
19693b9b51feSCy Schubert 	 */
19703b9b51feSCy Schubert 	in = ipnn->ipn_nat.nat_ptr;
19713b9b51feSCy Schubert 	if (in != NULL) {
19723b9b51feSCy Schubert 		KMALLOCS(in, ipnat_t *, ipnn->ipn_ipnat.in_size);
19733b9b51feSCy Schubert 		nat->nat_ptr = in;
19743b9b51feSCy Schubert 		if (in == NULL) {
19753b9b51feSCy Schubert 			IPFERROR(60038);
19763b9b51feSCy Schubert 			error = ENOMEM;
19773b9b51feSCy Schubert 			goto junkput;
19783b9b51feSCy Schubert 		}
19793b9b51feSCy Schubert 		bcopy((char *)&ipnn->ipn_ipnat, (char *)in,
19803b9b51feSCy Schubert 		      ipnn->ipn_ipnat.in_size);
19813b9b51feSCy Schubert 		in->in_use = 1;
19823b9b51feSCy Schubert 		in->in_flags |= IPN_DELETE;
19833b9b51feSCy Schubert 
19843b9b51feSCy Schubert 		ATOMIC_INC32(softn->ipf_nat_stats.ns_rules);
19853b9b51feSCy Schubert 
19863b9b51feSCy Schubert 		if (ipf_nat_resolverule(softc, in) != 0) {
19873b9b51feSCy Schubert 			IPFERROR(60039);
19883b9b51feSCy Schubert 			error = ESRCH;
19893b9b51feSCy Schubert 			goto junkput;
19903b9b51feSCy Schubert 		}
19913b9b51feSCy Schubert 	}
19923b9b51feSCy Schubert 
19933b9b51feSCy Schubert 	/*
19943b9b51feSCy Schubert 	 * Check that the NAT entry doesn't already exist in the kernel.
19953b9b51feSCy Schubert 	 *
19963b9b51feSCy Schubert 	 * For NAT_OUTBOUND, we're lookup for a duplicate MAP entry.  To do
19973b9b51feSCy Schubert 	 * this, we check to see if the inbound combination of addresses and
19983b9b51feSCy Schubert 	 * ports is already known.  Similar logic is applied for NAT_INBOUND.
19993b9b51feSCy Schubert 	 *
20003b9b51feSCy Schubert 	 */
20013b9b51feSCy Schubert 	bzero((char *)&fin, sizeof(fin));
20023b9b51feSCy Schubert 	fin.fin_v = nat->nat_v[0];
20033b9b51feSCy Schubert 	fin.fin_p = nat->nat_pr[0];
20043b9b51feSCy Schubert 	fin.fin_rev = nat->nat_rev;
20053b9b51feSCy Schubert 	fin.fin_ifp = nat->nat_ifps[0];
20063b9b51feSCy Schubert 	fin.fin_data[0] = ntohs(nat->nat_ndport);
20073b9b51feSCy Schubert 	fin.fin_data[1] = ntohs(nat->nat_nsport);
20083b9b51feSCy Schubert 
20093b9b51feSCy Schubert 	switch (nat->nat_dir)
20103b9b51feSCy Schubert 	{
20113b9b51feSCy Schubert 	case NAT_OUTBOUND :
20123b9b51feSCy Schubert 	case NAT_DIVERTOUT :
20133b9b51feSCy Schubert 		if (getlock) {
20143b9b51feSCy Schubert 			READ_ENTER(&softc->ipf_nat);
20153b9b51feSCy Schubert 		}
20163b9b51feSCy Schubert 
20173b9b51feSCy Schubert 		fin.fin_v = nat->nat_v[1];
20183b9b51feSCy Schubert 		if (nat->nat_v[1] == 4) {
20193b9b51feSCy Schubert 			n = ipf_nat_inlookup(&fin, nat->nat_flags, fin.fin_p,
20203b9b51feSCy Schubert 					     nat->nat_ndstip, nat->nat_nsrcip);
20213b9b51feSCy Schubert #ifdef USE_INET6
20223b9b51feSCy Schubert 		} else if (nat->nat_v[1] == 6) {
20233b9b51feSCy Schubert 			n = ipf_nat6_inlookup(&fin, nat->nat_flags, fin.fin_p,
20243b9b51feSCy Schubert 					      &nat->nat_ndst6.in6,
20253b9b51feSCy Schubert 					      &nat->nat_nsrc6.in6);
20263b9b51feSCy Schubert #endif
20273b9b51feSCy Schubert 		}
20283b9b51feSCy Schubert 
20293b9b51feSCy Schubert 		if (getlock) {
20303b9b51feSCy Schubert 			RWLOCK_EXIT(&softc->ipf_nat);
20313b9b51feSCy Schubert 		}
20323b9b51feSCy Schubert 		if (n != NULL) {
20333b9b51feSCy Schubert 			IPFERROR(60040);
20343b9b51feSCy Schubert 			error = EEXIST;
20353b9b51feSCy Schubert 			goto junkput;
20363b9b51feSCy Schubert 		}
20373b9b51feSCy Schubert 		break;
20383b9b51feSCy Schubert 
20393b9b51feSCy Schubert 	case NAT_INBOUND :
20403b9b51feSCy Schubert 	case NAT_DIVERTIN :
20413b9b51feSCy Schubert 		if (getlock) {
20423b9b51feSCy Schubert 			READ_ENTER(&softc->ipf_nat);
20433b9b51feSCy Schubert 		}
20443b9b51feSCy Schubert 
20453b9b51feSCy Schubert 		if (fin.fin_v == 4) {
20463b9b51feSCy Schubert 			n = ipf_nat_outlookup(&fin, nat->nat_flags, fin.fin_p,
20473b9b51feSCy Schubert 					      nat->nat_ndstip,
20483b9b51feSCy Schubert 					      nat->nat_nsrcip);
20493b9b51feSCy Schubert #ifdef USE_INET6
20503b9b51feSCy Schubert 		} else if (fin.fin_v == 6) {
20513b9b51feSCy Schubert 			n = ipf_nat6_outlookup(&fin, nat->nat_flags, fin.fin_p,
20523b9b51feSCy Schubert 					       &nat->nat_ndst6.in6,
20533b9b51feSCy Schubert 					       &nat->nat_nsrc6.in6);
20543b9b51feSCy Schubert #endif
20553b9b51feSCy Schubert 		}
20563b9b51feSCy Schubert 
20573b9b51feSCy Schubert 		if (getlock) {
20583b9b51feSCy Schubert 			RWLOCK_EXIT(&softc->ipf_nat);
20593b9b51feSCy Schubert 		}
20603b9b51feSCy Schubert 		if (n != NULL) {
20613b9b51feSCy Schubert 			IPFERROR(60041);
20623b9b51feSCy Schubert 			error = EEXIST;
20633b9b51feSCy Schubert 			goto junkput;
20643b9b51feSCy Schubert 		}
20653b9b51feSCy Schubert 		break;
20663b9b51feSCy Schubert 
20673b9b51feSCy Schubert 	default :
20683b9b51feSCy Schubert 		IPFERROR(60042);
20693b9b51feSCy Schubert 		error = EINVAL;
20703b9b51feSCy Schubert 		goto junkput;
20713b9b51feSCy Schubert 	}
20723b9b51feSCy Schubert 
20733b9b51feSCy Schubert 	/*
20743b9b51feSCy Schubert 	 * Restore ap_session_t structure.  Include the private data allocated
20753b9b51feSCy Schubert 	 * if it was there.
20763b9b51feSCy Schubert 	 */
20773b9b51feSCy Schubert 	aps = nat->nat_aps;
20783b9b51feSCy Schubert 	if (aps != NULL) {
20793b9b51feSCy Schubert 		KMALLOC(aps, ap_session_t *);
20803b9b51feSCy Schubert 		nat->nat_aps = aps;
20813b9b51feSCy Schubert 		if (aps == NULL) {
20823b9b51feSCy Schubert 			IPFERROR(60043);
20833b9b51feSCy Schubert 			error = ENOMEM;
20843b9b51feSCy Schubert 			goto junkput;
20853b9b51feSCy Schubert 		}
20863b9b51feSCy Schubert 		bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps));
20873b9b51feSCy Schubert 		if (in != NULL)
20883b9b51feSCy Schubert 			aps->aps_apr = in->in_apr;
20893b9b51feSCy Schubert 		else
20903b9b51feSCy Schubert 			aps->aps_apr = NULL;
20913b9b51feSCy Schubert 		if (aps->aps_psiz != 0) {
20923b9b51feSCy Schubert 			if (aps->aps_psiz > 81920) {
20933b9b51feSCy Schubert 				IPFERROR(60044);
20943b9b51feSCy Schubert 				error = ENOMEM;
20953b9b51feSCy Schubert 				goto junkput;
20963b9b51feSCy Schubert 			}
20973b9b51feSCy Schubert 			KMALLOCS(aps->aps_data, void *, aps->aps_psiz);
20983b9b51feSCy Schubert 			if (aps->aps_data == NULL) {
20993b9b51feSCy Schubert 				IPFERROR(60045);
21003b9b51feSCy Schubert 				error = ENOMEM;
21013b9b51feSCy Schubert 				goto junkput;
21023b9b51feSCy Schubert 			}
21033b9b51feSCy Schubert 			bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data,
21043b9b51feSCy Schubert 			      aps->aps_psiz);
21053b9b51feSCy Schubert 		} else {
21063b9b51feSCy Schubert 			aps->aps_psiz = 0;
21073b9b51feSCy Schubert 			aps->aps_data = NULL;
21083b9b51feSCy Schubert 		}
21093b9b51feSCy Schubert 	}
21103b9b51feSCy Schubert 
21113b9b51feSCy Schubert 	/*
21123b9b51feSCy Schubert 	 * If there was a filtering rule associated with this entry then
21133b9b51feSCy Schubert 	 * build up a new one.
21143b9b51feSCy Schubert 	 */
21153b9b51feSCy Schubert 	fr = nat->nat_fr;
21163b9b51feSCy Schubert 	if (fr != NULL) {
21173b9b51feSCy Schubert 		if ((nat->nat_flags & SI_NEWFR) != 0) {
21183b9b51feSCy Schubert 			KMALLOC(fr, frentry_t *);
21193b9b51feSCy Schubert 			nat->nat_fr = fr;
21203b9b51feSCy Schubert 			if (fr == NULL) {
21213b9b51feSCy Schubert 				IPFERROR(60046);
21223b9b51feSCy Schubert 				error = ENOMEM;
21233b9b51feSCy Schubert 				goto junkput;
21243b9b51feSCy Schubert 			}
21253b9b51feSCy Schubert 			ipnn->ipn_nat.nat_fr = fr;
21263b9b51feSCy Schubert 			fr->fr_ref = 1;
21273b9b51feSCy Schubert 			(void) ipf_outobj(softc, data, ipnn, IPFOBJ_NATSAVE);
21283b9b51feSCy Schubert 			bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr));
21293b9b51feSCy Schubert 
21303b9b51feSCy Schubert 			fr->fr_ref = 1;
21313b9b51feSCy Schubert 			fr->fr_dsize = 0;
21323b9b51feSCy Schubert 			fr->fr_data = NULL;
21333b9b51feSCy Schubert 			fr->fr_type = FR_T_NONE;
21343b9b51feSCy Schubert 
21353b9b51feSCy Schubert 			MUTEX_NUKE(&fr->fr_lock);
21363b9b51feSCy Schubert 			MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock");
21373b9b51feSCy Schubert 		} else {
21383b9b51feSCy Schubert 			if (getlock) {
21393b9b51feSCy Schubert 				READ_ENTER(&softc->ipf_nat);
21403b9b51feSCy Schubert 			}
21413b9b51feSCy Schubert 			for (n = softn->ipf_nat_instances; n; n = n->nat_next)
21423b9b51feSCy Schubert 				if (n->nat_fr == fr)
21433b9b51feSCy Schubert 					break;
21443b9b51feSCy Schubert 
21453b9b51feSCy Schubert 			if (n != NULL) {
21463b9b51feSCy Schubert 				MUTEX_ENTER(&fr->fr_lock);
21473b9b51feSCy Schubert 				fr->fr_ref++;
21483b9b51feSCy Schubert 				MUTEX_EXIT(&fr->fr_lock);
21493b9b51feSCy Schubert 			}
21503b9b51feSCy Schubert 			if (getlock) {
21513b9b51feSCy Schubert 				RWLOCK_EXIT(&softc->ipf_nat);
21523b9b51feSCy Schubert 			}
21533b9b51feSCy Schubert 
21543b9b51feSCy Schubert 			if (n == NULL) {
21553b9b51feSCy Schubert 				IPFERROR(60047);
21563b9b51feSCy Schubert 				error = ESRCH;
21573b9b51feSCy Schubert 				goto junkput;
21583b9b51feSCy Schubert 			}
21593b9b51feSCy Schubert 		}
21603b9b51feSCy Schubert 	}
21613b9b51feSCy Schubert 
21623b9b51feSCy Schubert 	if (ipnn != &ipn) {
21633b9b51feSCy Schubert 		KFREES(ipnn, ipn.ipn_dsize);
21643b9b51feSCy Schubert 		ipnn = NULL;
21653b9b51feSCy Schubert 	}
21663b9b51feSCy Schubert 
21673b9b51feSCy Schubert 	if (getlock) {
21683b9b51feSCy Schubert 		WRITE_ENTER(&softc->ipf_nat);
21693b9b51feSCy Schubert 	}
21703b9b51feSCy Schubert 
21713b9b51feSCy Schubert 	if (fin.fin_v == 4)
21723b9b51feSCy Schubert 		error = ipf_nat_finalise(&fin, nat);
21733b9b51feSCy Schubert #ifdef USE_INET6
21743b9b51feSCy Schubert 	else
21753b9b51feSCy Schubert 		error = ipf_nat6_finalise(&fin, nat);
21763b9b51feSCy Schubert #endif
21773b9b51feSCy Schubert 
21783b9b51feSCy Schubert 	if (getlock) {
21793b9b51feSCy Schubert 		RWLOCK_EXIT(&softc->ipf_nat);
21803b9b51feSCy Schubert 	}
21813b9b51feSCy Schubert 
21823b9b51feSCy Schubert 	if (error == 0)
21838c82b374SCy Schubert 		return (0);
21843b9b51feSCy Schubert 
21853b9b51feSCy Schubert 	IPFERROR(60048);
21863b9b51feSCy Schubert 	error = ENOMEM;
21873b9b51feSCy Schubert 
21883b9b51feSCy Schubert junkput:
21893b9b51feSCy Schubert 	if (fr != NULL) {
21903b9b51feSCy Schubert 		(void) ipf_derefrule(softc, &fr);
21913b9b51feSCy Schubert 	}
21923b9b51feSCy Schubert 
21933b9b51feSCy Schubert 	if ((ipnn != NULL) && (ipnn != &ipn)) {
21943b9b51feSCy Schubert 		KFREES(ipnn, ipn.ipn_dsize);
21953b9b51feSCy Schubert 	}
21963b9b51feSCy Schubert 	if (nat != NULL) {
21973b9b51feSCy Schubert 		if (aps != NULL) {
21983b9b51feSCy Schubert 			if (aps->aps_data != NULL) {
21993b9b51feSCy Schubert 				KFREES(aps->aps_data, aps->aps_psiz);
22003b9b51feSCy Schubert 			}
22013b9b51feSCy Schubert 			KFREE(aps);
22023b9b51feSCy Schubert 		}
22033b9b51feSCy Schubert 		if (in != NULL) {
22043b9b51feSCy Schubert 			if (in->in_apr)
22053b9b51feSCy Schubert 				ipf_proxy_deref(in->in_apr);
22063b9b51feSCy Schubert 			KFREES(in, in->in_size);
22073b9b51feSCy Schubert 		}
22083b9b51feSCy Schubert 		KFREE(nat);
22093b9b51feSCy Schubert 	}
22108c82b374SCy Schubert 	return (error);
22113b9b51feSCy Schubert }
22123b9b51feSCy Schubert 
22133b9b51feSCy Schubert 
22143b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
22153b9b51feSCy Schubert /* Function:    ipf_nat_delete                                              */
22163b9b51feSCy Schubert /* Returns:     Nil                                                         */
22173b9b51feSCy Schubert /* Parameters:  softc(I)   - pointer to soft context main structure         */
22183b9b51feSCy Schubert /*              nat(I)     - pointer to NAT structure to delete             */
22193b9b51feSCy Schubert /*              logtype(I) - type of LOG record to create before deleting   */
22203b9b51feSCy Schubert /* Write Lock:  ipf_nat                                                     */
22213b9b51feSCy Schubert /*                                                                          */
22223b9b51feSCy Schubert /* Delete a nat entry from the various lists and table.  If NAT logging is  */
22233b9b51feSCy Schubert /* enabled then generate a NAT log record for this event.                   */
22243b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
22253b9b51feSCy Schubert void
ipf_nat_delete(ipf_main_softc_t * softc,struct nat * nat,int logtype)2226064a5a95SCy Schubert ipf_nat_delete(ipf_main_softc_t *softc, struct nat *nat, int logtype)
22273b9b51feSCy Schubert {
22283b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
22293b9b51feSCy Schubert 	int madeorphan = 0, bkt, removed = 0;
22303b9b51feSCy Schubert 	nat_stat_side_t *nss;
22313b9b51feSCy Schubert 	struct ipnat *ipn;
22323b9b51feSCy Schubert 
22333b9b51feSCy Schubert 	if (logtype != 0 && softn->ipf_nat_logging != 0)
22343b9b51feSCy Schubert 		ipf_nat_log(softc, softn, nat, logtype);
22353b9b51feSCy Schubert 
22363b9b51feSCy Schubert 	/*
22373b9b51feSCy Schubert 	 * Take it as a general indication that all the pointers are set if
22383b9b51feSCy Schubert 	 * nat_pnext is set.
22393b9b51feSCy Schubert 	 */
22403b9b51feSCy Schubert 	if (nat->nat_pnext != NULL) {
22413b9b51feSCy Schubert 		removed = 1;
22423b9b51feSCy Schubert 
22433b9b51feSCy Schubert 		bkt = nat->nat_hv[0] % softn->ipf_nat_table_sz;
22443b9b51feSCy Schubert 		nss = &softn->ipf_nat_stats.ns_side[0];
22453b9b51feSCy Schubert 		if (nss->ns_bucketlen[bkt] > 0)
22463b9b51feSCy Schubert 			nss->ns_bucketlen[bkt]--;
22473b9b51feSCy Schubert 		if (nss->ns_bucketlen[bkt] == 0) {
22483b9b51feSCy Schubert 			nss->ns_inuse--;
22493b9b51feSCy Schubert 		}
22503b9b51feSCy Schubert 
22513b9b51feSCy Schubert 		bkt = nat->nat_hv[1] % softn->ipf_nat_table_sz;
22523b9b51feSCy Schubert 		nss = &softn->ipf_nat_stats.ns_side[1];
22533b9b51feSCy Schubert 		if (nss->ns_bucketlen[bkt] > 0)
22543b9b51feSCy Schubert 			nss->ns_bucketlen[bkt]--;
22553b9b51feSCy Schubert 		if (nss->ns_bucketlen[bkt] == 0) {
22563b9b51feSCy Schubert 			nss->ns_inuse--;
22573b9b51feSCy Schubert 		}
22583b9b51feSCy Schubert 
22593b9b51feSCy Schubert 		*nat->nat_pnext = nat->nat_next;
22603b9b51feSCy Schubert 		if (nat->nat_next != NULL) {
22613b9b51feSCy Schubert 			nat->nat_next->nat_pnext = nat->nat_pnext;
22623b9b51feSCy Schubert 			nat->nat_next = NULL;
22633b9b51feSCy Schubert 		}
22643b9b51feSCy Schubert 		nat->nat_pnext = NULL;
22653b9b51feSCy Schubert 
22663b9b51feSCy Schubert 		*nat->nat_phnext[0] = nat->nat_hnext[0];
22673b9b51feSCy Schubert 		if (nat->nat_hnext[0] != NULL) {
22683b9b51feSCy Schubert 			nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
22693b9b51feSCy Schubert 			nat->nat_hnext[0] = NULL;
22703b9b51feSCy Schubert 		}
22713b9b51feSCy Schubert 		nat->nat_phnext[0] = NULL;
22723b9b51feSCy Schubert 
22733b9b51feSCy Schubert 		*nat->nat_phnext[1] = nat->nat_hnext[1];
22743b9b51feSCy Schubert 		if (nat->nat_hnext[1] != NULL) {
22753b9b51feSCy Schubert 			nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
22763b9b51feSCy Schubert 			nat->nat_hnext[1] = NULL;
22773b9b51feSCy Schubert 		}
22783b9b51feSCy Schubert 		nat->nat_phnext[1] = NULL;
22793b9b51feSCy Schubert 
22803b9b51feSCy Schubert 		if ((nat->nat_flags & SI_WILDP) != 0) {
22813b9b51feSCy Schubert 			ATOMIC_DEC32(softn->ipf_nat_stats.ns_wilds);
22823b9b51feSCy Schubert 		}
22833b9b51feSCy Schubert 		madeorphan = 1;
22843b9b51feSCy Schubert 	}
22853b9b51feSCy Schubert 
22863b9b51feSCy Schubert 	if (nat->nat_me != NULL) {
22873b9b51feSCy Schubert 		*nat->nat_me = NULL;
22883b9b51feSCy Schubert 		nat->nat_me = NULL;
22893b9b51feSCy Schubert 		nat->nat_ref--;
22903b9b51feSCy Schubert 		ASSERT(nat->nat_ref >= 0);
22913b9b51feSCy Schubert 	}
22923b9b51feSCy Schubert 
22933b9b51feSCy Schubert 	if (nat->nat_tqe.tqe_ifq != NULL) {
22943b9b51feSCy Schubert 		/*
22953b9b51feSCy Schubert 		 * No call to ipf_freetimeoutqueue() is made here, they are
22963b9b51feSCy Schubert 		 * garbage collected in ipf_nat_expire().
22973b9b51feSCy Schubert 		 */
22983b9b51feSCy Schubert 		(void) ipf_deletequeueentry(&nat->nat_tqe);
22993b9b51feSCy Schubert 	}
23003b9b51feSCy Schubert 
23013b9b51feSCy Schubert 	if (nat->nat_sync) {
23023b9b51feSCy Schubert 		ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync);
23033b9b51feSCy Schubert 		nat->nat_sync = NULL;
23043b9b51feSCy Schubert 	}
23053b9b51feSCy Schubert 
23063b9b51feSCy Schubert 	if (logtype == NL_EXPIRE)
23073b9b51feSCy Schubert 		softn->ipf_nat_stats.ns_expire++;
23083b9b51feSCy Schubert 
23093b9b51feSCy Schubert 	MUTEX_ENTER(&nat->nat_lock);
23103b9b51feSCy Schubert 	/*
23113b9b51feSCy Schubert 	 * NL_DESTROY should only be passed in when we've got nat_ref >= 2.
23123b9b51feSCy Schubert 	 * This happens when a nat'd packet is blocked and we want to throw
23133b9b51feSCy Schubert 	 * away the NAT session.
23143b9b51feSCy Schubert 	 */
23153b9b51feSCy Schubert 	if (logtype == NL_DESTROY) {
23163b9b51feSCy Schubert 		if (nat->nat_ref > 2) {
23173b9b51feSCy Schubert 			nat->nat_ref -= 2;
23183b9b51feSCy Schubert 			MUTEX_EXIT(&nat->nat_lock);
23193b9b51feSCy Schubert 			if (removed)
23203b9b51feSCy Schubert 				softn->ipf_nat_stats.ns_orphans++;
23213b9b51feSCy Schubert 			return;
23223b9b51feSCy Schubert 		}
23233b9b51feSCy Schubert 	} else if (nat->nat_ref > 1) {
23243b9b51feSCy Schubert 		nat->nat_ref--;
23253b9b51feSCy Schubert 		MUTEX_EXIT(&nat->nat_lock);
23263b9b51feSCy Schubert 		if (madeorphan == 1)
23273b9b51feSCy Schubert 			softn->ipf_nat_stats.ns_orphans++;
23283b9b51feSCy Schubert 		return;
23293b9b51feSCy Schubert 	}
23303b9b51feSCy Schubert 	ASSERT(nat->nat_ref >= 0);
23313b9b51feSCy Schubert 	MUTEX_EXIT(&nat->nat_lock);
23323b9b51feSCy Schubert 
23333b9b51feSCy Schubert 	nat->nat_ref = 0;
23343b9b51feSCy Schubert 
23353b9b51feSCy Schubert 	if (madeorphan == 0)
23363b9b51feSCy Schubert 		softn->ipf_nat_stats.ns_orphans--;
23373b9b51feSCy Schubert 
23383b9b51feSCy Schubert 	/*
23393b9b51feSCy Schubert 	 * At this point, nat_ref can be either 0 or -1
23403b9b51feSCy Schubert 	 */
23413b9b51feSCy Schubert 	softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]--;
23423b9b51feSCy Schubert 
23433b9b51feSCy Schubert 	if (nat->nat_fr != NULL) {
23443b9b51feSCy Schubert 		(void) ipf_derefrule(softc, &nat->nat_fr);
23453b9b51feSCy Schubert 	}
23463b9b51feSCy Schubert 
23473b9b51feSCy Schubert 	if (nat->nat_hm != NULL) {
23483b9b51feSCy Schubert 		ipf_nat_hostmapdel(softc, &nat->nat_hm);
23493b9b51feSCy Schubert 	}
23503b9b51feSCy Schubert 
23513b9b51feSCy Schubert 	/*
23523b9b51feSCy Schubert 	 * If there is an active reference from the nat entry to its parent
23533b9b51feSCy Schubert 	 * rule, decrement the rule's reference count and free it too if no
23543b9b51feSCy Schubert 	 * longer being used.
23553b9b51feSCy Schubert 	 */
23563b9b51feSCy Schubert 	ipn = nat->nat_ptr;
23573b9b51feSCy Schubert 	nat->nat_ptr = NULL;
23583b9b51feSCy Schubert 
23593b9b51feSCy Schubert 	if (ipn != NULL) {
23603b9b51feSCy Schubert 		ipn->in_space++;
23613b9b51feSCy Schubert 		ipf_nat_rule_deref(softc, &ipn);
23623b9b51feSCy Schubert 	}
23633b9b51feSCy Schubert 
23643b9b51feSCy Schubert 	if (nat->nat_aps != NULL) {
23653b9b51feSCy Schubert 		ipf_proxy_free(softc, nat->nat_aps);
23663b9b51feSCy Schubert 		nat->nat_aps = NULL;
23673b9b51feSCy Schubert 	}
23683b9b51feSCy Schubert 
23693b9b51feSCy Schubert 	MUTEX_DESTROY(&nat->nat_lock);
23703b9b51feSCy Schubert 
23713b9b51feSCy Schubert 	softn->ipf_nat_stats.ns_active--;
23723b9b51feSCy Schubert 
23733b9b51feSCy Schubert 	/*
23743b9b51feSCy Schubert 	 * If there's a fragment table entry too for this nat entry, then
23753b9b51feSCy Schubert 	 * dereference that as well.  This is after nat_lock is released
23763b9b51feSCy Schubert 	 * because of Tru64.
23773b9b51feSCy Schubert 	 */
23783b9b51feSCy Schubert 	ipf_frag_natforget(softc, (void *)nat);
23793b9b51feSCy Schubert 
23803b9b51feSCy Schubert 	KFREE(nat);
23813b9b51feSCy Schubert }
23823b9b51feSCy Schubert 
23833b9b51feSCy Schubert 
23843b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
23853b9b51feSCy Schubert /* Function:    ipf_nat_flushtable                                          */
23863b9b51feSCy Schubert /* Returns:     int - number of NAT rules deleted                           */
23873b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
23883b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
23893b9b51feSCy Schubert /* Write Lock:  ipf_nat                                                     */
23903b9b51feSCy Schubert /*                                                                          */
23913b9b51feSCy Schubert /* Deletes all currently active NAT sessions.  In deleting each NAT entry a */
23923b9b51feSCy Schubert /* log record should be emitted in ipf_nat_delete() if NAT logging is       */
23933b9b51feSCy Schubert /* enabled.                                                                 */
23943b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
23953b9b51feSCy Schubert /*
23963b9b51feSCy Schubert  * nat_flushtable - clear the NAT table of all mapping entries.
23973b9b51feSCy Schubert  */
23983b9b51feSCy Schubert static int
ipf_nat_flushtable(ipf_main_softc_t * softc,ipf_nat_softc_t * softn)2399064a5a95SCy Schubert ipf_nat_flushtable(ipf_main_softc_t *softc, ipf_nat_softc_t *softn)
24003b9b51feSCy Schubert {
24013b9b51feSCy Schubert 	nat_t *nat;
24023b9b51feSCy Schubert 	int j = 0;
24033b9b51feSCy Schubert 
24043b9b51feSCy Schubert 	/*
24053b9b51feSCy Schubert 	 * ALL NAT mappings deleted, so lets just make the deletions
24063b9b51feSCy Schubert 	 * quicker.
24073b9b51feSCy Schubert 	 */
24083b9b51feSCy Schubert 	if (softn->ipf_nat_table[0] != NULL)
24093b9b51feSCy Schubert 		bzero((char *)softn->ipf_nat_table[0],
24103b9b51feSCy Schubert 		      sizeof(softn->ipf_nat_table[0]) *
24113b9b51feSCy Schubert 		      softn->ipf_nat_table_sz);
24123b9b51feSCy Schubert 	if (softn->ipf_nat_table[1] != NULL)
24133b9b51feSCy Schubert 		bzero((char *)softn->ipf_nat_table[1],
24143b9b51feSCy Schubert 		      sizeof(softn->ipf_nat_table[1]) *
24153b9b51feSCy Schubert 		      softn->ipf_nat_table_sz);
24163b9b51feSCy Schubert 
24173b9b51feSCy Schubert 	while ((nat = softn->ipf_nat_instances) != NULL) {
24183b9b51feSCy Schubert 		ipf_nat_delete(softc, nat, NL_FLUSH);
24193b9b51feSCy Schubert 		j++;
24203b9b51feSCy Schubert 	}
24213b9b51feSCy Schubert 
24228c82b374SCy Schubert 	return (j);
24233b9b51feSCy Schubert }
24243b9b51feSCy Schubert 
24253b9b51feSCy Schubert 
24263b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
24273b9b51feSCy Schubert /* Function:    ipf_nat_clearlist                                           */
24283b9b51feSCy Schubert /* Returns:     int - number of NAT/RDR rules deleted                       */
24293b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
24303b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
24313b9b51feSCy Schubert /*                                                                          */
24323b9b51feSCy Schubert /* Delete all rules in the current list of rules.  There is nothing elegant */
24333b9b51feSCy Schubert /* about this cleanup: simply free all entries on the list of rules and     */
24343b9b51feSCy Schubert /* clear out the tables used for hashed NAT rule lookups.                   */
24353b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
24363b9b51feSCy Schubert static int
ipf_nat_clearlist(ipf_main_softc_t * softc,ipf_nat_softc_t * softn)2437064a5a95SCy Schubert ipf_nat_clearlist(ipf_main_softc_t *softc, ipf_nat_softc_t *softn)
24383b9b51feSCy Schubert {
24393b9b51feSCy Schubert 	ipnat_t *n;
24403b9b51feSCy Schubert 	int i = 0;
24413b9b51feSCy Schubert 
24423b9b51feSCy Schubert 	if (softn->ipf_nat_map_rules != NULL) {
24433b9b51feSCy Schubert 		bzero((char *)softn->ipf_nat_map_rules,
24443b9b51feSCy Schubert 		      sizeof(*softn->ipf_nat_map_rules) *
24453b9b51feSCy Schubert 		      softn->ipf_nat_maprules_sz);
24463b9b51feSCy Schubert 	}
24473b9b51feSCy Schubert 	if (softn->ipf_nat_rdr_rules != NULL) {
24483b9b51feSCy Schubert 		bzero((char *)softn->ipf_nat_rdr_rules,
24493b9b51feSCy Schubert 		      sizeof(*softn->ipf_nat_rdr_rules) *
24503b9b51feSCy Schubert 		      softn->ipf_nat_rdrrules_sz);
24513b9b51feSCy Schubert 	}
24523b9b51feSCy Schubert 
24533b9b51feSCy Schubert 	while ((n = softn->ipf_nat_list) != NULL) {
24543b9b51feSCy Schubert 		ipf_nat_delrule(softc, softn, n, 0);
24553b9b51feSCy Schubert 		i++;
24563b9b51feSCy Schubert 	}
24573b9b51feSCy Schubert #if SOLARIS && !defined(INSTANCES)
24583b9b51feSCy Schubert 	pfil_delayed_copy = 1;
24593b9b51feSCy Schubert #endif
24608c82b374SCy Schubert 	return (i);
24613b9b51feSCy Schubert }
24623b9b51feSCy Schubert 
24633b9b51feSCy Schubert 
24643b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
24653b9b51feSCy Schubert /* Function:    ipf_nat_delrule                                             */
24663b9b51feSCy Schubert /* Returns:     Nil                                                         */
24673b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
24683b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
24693b9b51feSCy Schubert /*              np(I)    - pointer to NAT rule to delete                    */
24703b9b51feSCy Schubert /*              purge(I) - 1 == allow purge, 0 == prevent purge             */
24713b9b51feSCy Schubert /* Locks:       WRITE(ipf_nat)                                              */
24723b9b51feSCy Schubert /*                                                                          */
24733b9b51feSCy Schubert /* Preventing "purge" from occuring is allowed because when all of the NAT  */
24743b9b51feSCy Schubert /* rules are being removed, allowing the "purge" to walk through the list   */
24753b9b51feSCy Schubert /* of NAT sessions, possibly multiple times, would be a large performance   */
24763b9b51feSCy Schubert /* hit, on the order of O(N^2).                                             */
24773b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
24783b9b51feSCy Schubert static void
ipf_nat_delrule(ipf_main_softc_t * softc,ipf_nat_softc_t * softn,ipnat_t * np,int purge)2479064a5a95SCy Schubert ipf_nat_delrule(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, ipnat_t *np,
2480064a5a95SCy Schubert 	int purge)
24813b9b51feSCy Schubert {
24823b9b51feSCy Schubert 
24833b9b51feSCy Schubert 	if (np->in_pnext != NULL) {
24843b9b51feSCy Schubert 		*np->in_pnext = np->in_next;
24853b9b51feSCy Schubert 		if (np->in_next != NULL)
24863b9b51feSCy Schubert 			np->in_next->in_pnext = np->in_pnext;
24873b9b51feSCy Schubert 		if (softn->ipf_nat_list_tail == &np->in_next)
24883b9b51feSCy Schubert 			softn->ipf_nat_list_tail = np->in_pnext;
24893b9b51feSCy Schubert 	}
24903b9b51feSCy Schubert 
24913b9b51feSCy Schubert 	if ((purge == 1) && ((np->in_flags & IPN_PURGE) != 0)) {
24923b9b51feSCy Schubert 		nat_t *next;
24933b9b51feSCy Schubert 		nat_t *nat;
24943b9b51feSCy Schubert 
24953b9b51feSCy Schubert 		for (next = softn->ipf_nat_instances; (nat = next) != NULL;) {
24963b9b51feSCy Schubert 			next = nat->nat_next;
24973b9b51feSCy Schubert 			if (nat->nat_ptr == np)
24983b9b51feSCy Schubert 				ipf_nat_delete(softc, nat, NL_PURGE);
24993b9b51feSCy Schubert 		}
25003b9b51feSCy Schubert 	}
25013b9b51feSCy Schubert 
25023b9b51feSCy Schubert 	if ((np->in_flags & IPN_DELETE) == 0) {
25033b9b51feSCy Schubert 		if (np->in_redir & NAT_REDIRECT) {
25043b9b51feSCy Schubert 			switch (np->in_v[0])
25053b9b51feSCy Schubert 			{
25063b9b51feSCy Schubert 			case 4 :
25073b9b51feSCy Schubert 				ipf_nat_delrdr(softn, np);
25083b9b51feSCy Schubert 				break;
25093b9b51feSCy Schubert #ifdef USE_INET6
25103b9b51feSCy Schubert 			case 6 :
25113b9b51feSCy Schubert 				ipf_nat6_delrdr(softn, np);
25123b9b51feSCy Schubert 				break;
25133b9b51feSCy Schubert #endif
25143b9b51feSCy Schubert 			}
25153b9b51feSCy Schubert 		}
25163b9b51feSCy Schubert 		if (np->in_redir & (NAT_MAPBLK|NAT_MAP)) {
25173b9b51feSCy Schubert 			switch (np->in_v[0])
25183b9b51feSCy Schubert 			{
25193b9b51feSCy Schubert 			case 4 :
25203b9b51feSCy Schubert 				ipf_nat_delmap(softn, np);
25213b9b51feSCy Schubert 				break;
25223b9b51feSCy Schubert #ifdef USE_INET6
25233b9b51feSCy Schubert 			case 6 :
25243b9b51feSCy Schubert 				ipf_nat6_delmap(softn, np);
25253b9b51feSCy Schubert 				break;
25263b9b51feSCy Schubert #endif
25273b9b51feSCy Schubert 			}
25283b9b51feSCy Schubert 		}
25293b9b51feSCy Schubert 	}
25303b9b51feSCy Schubert 
25313b9b51feSCy Schubert 	np->in_flags |= IPN_DELETE;
25323b9b51feSCy Schubert 	ipf_nat_rule_deref(softc, &np);
25333b9b51feSCy Schubert }
25343b9b51feSCy Schubert 
25353b9b51feSCy Schubert 
25363b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
25373b9b51feSCy Schubert /* Function:    ipf_nat_newmap                                              */
25383b9b51feSCy Schubert /* Returns:     int - -1 == error, 0 == success                             */
25393b9b51feSCy Schubert /* Parameters:  fin(I) - pointer to packet information                      */
25403b9b51feSCy Schubert /*              nat(I) - pointer to NAT entry                               */
25413b9b51feSCy Schubert /*              ni(I)  - pointer to structure with misc. information needed */
25423b9b51feSCy Schubert /*                       to create new NAT entry.                           */
25433b9b51feSCy Schubert /*                                                                          */
25443b9b51feSCy Schubert /* Given an empty NAT structure, populate it with new information about a   */
25453b9b51feSCy Schubert /* new NAT session, as defined by the matching NAT rule.                    */
25463b9b51feSCy Schubert /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
25473b9b51feSCy Schubert /* to the new IP address for the translation.                               */
25483b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
25493b9b51feSCy Schubert static int
ipf_nat_newmap(fr_info_t * fin,nat_t * nat,natinfo_t * ni)2550064a5a95SCy Schubert ipf_nat_newmap(fr_info_t *fin, nat_t *nat, natinfo_t *ni)
25513b9b51feSCy Schubert {
25523b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
25533b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
25543b9b51feSCy Schubert 	u_short st_port, dport, sport, port, sp, dp;
25553b9b51feSCy Schubert 	struct in_addr in, inb;
25563b9b51feSCy Schubert 	hostmap_t *hm;
25573b9b51feSCy Schubert 	u_32_t flags;
25583b9b51feSCy Schubert 	u_32_t st_ip;
25593b9b51feSCy Schubert 	ipnat_t *np;
25603b9b51feSCy Schubert 	nat_t *natl;
25613b9b51feSCy Schubert 	int l;
25623b9b51feSCy Schubert 
25633b9b51feSCy Schubert 	/*
25643b9b51feSCy Schubert 	 * If it's an outbound packet which doesn't match any existing
25653b9b51feSCy Schubert 	 * record, then create a new port
25663b9b51feSCy Schubert 	 */
25673b9b51feSCy Schubert 	l = 0;
25683b9b51feSCy Schubert 	hm = NULL;
25693b9b51feSCy Schubert 	np = ni->nai_np;
25703b9b51feSCy Schubert 	st_ip = np->in_snip;
25713b9b51feSCy Schubert 	st_port = np->in_spnext;
25723b9b51feSCy Schubert 	flags = nat->nat_flags;
25733b9b51feSCy Schubert 
25743b9b51feSCy Schubert 	if (flags & IPN_ICMPQUERY) {
25753b9b51feSCy Schubert 		sport = fin->fin_data[1];
25763b9b51feSCy Schubert 		dport = 0;
25773b9b51feSCy Schubert 	} else {
25783b9b51feSCy Schubert 		sport = htons(fin->fin_data[0]);
25793b9b51feSCy Schubert 		dport = htons(fin->fin_data[1]);
25803b9b51feSCy Schubert 	}
25813b9b51feSCy Schubert 
25823b9b51feSCy Schubert 	/*
25833b9b51feSCy Schubert 	 * Do a loop until we either run out of entries to try or we find
25843b9b51feSCy Schubert 	 * a NAT mapping that isn't currently being used.  This is done
25853b9b51feSCy Schubert 	 * because the change to the source is not (usually) being fixed.
25863b9b51feSCy Schubert 	 */
25873b9b51feSCy Schubert 	do {
25883b9b51feSCy Schubert 		port = 0;
25893b9b51feSCy Schubert 		in.s_addr = htonl(np->in_snip);
25903b9b51feSCy Schubert 		if (l == 0) {
25913b9b51feSCy Schubert 			/*
25923b9b51feSCy Schubert 			 * Check to see if there is an existing NAT
25933b9b51feSCy Schubert 			 * setup for this IP address pair.
25943b9b51feSCy Schubert 			 */
25953b9b51feSCy Schubert 			hm = ipf_nat_hostmap(softn, np, fin->fin_src,
25963b9b51feSCy Schubert 					     fin->fin_dst, in, 0);
25973b9b51feSCy Schubert 			if (hm != NULL)
25983b9b51feSCy Schubert 				in.s_addr = hm->hm_nsrcip.s_addr;
25993b9b51feSCy Schubert 		} else if ((l == 1) && (hm != NULL)) {
26003b9b51feSCy Schubert 			ipf_nat_hostmapdel(softc, &hm);
26013b9b51feSCy Schubert 		}
26023b9b51feSCy Schubert 		in.s_addr = ntohl(in.s_addr);
26033b9b51feSCy Schubert 
26043b9b51feSCy Schubert 		nat->nat_hm = hm;
26053b9b51feSCy Schubert 
26063b9b51feSCy Schubert 		if ((np->in_nsrcmsk == 0xffffffff) && (np->in_spnext == 0)) {
26073b9b51feSCy Schubert 			if (l > 0) {
26083b9b51feSCy Schubert 				NBUMPSIDEX(1, ns_exhausted, ns_exhausted_1);
26093b9b51feSCy Schubert 				DT4(ns_exhausted_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
26108c82b374SCy Schubert 				return (-1);
26113b9b51feSCy Schubert 			}
26123b9b51feSCy Schubert 		}
26133b9b51feSCy Schubert 
26143b9b51feSCy Schubert 		if (np->in_redir == NAT_BIMAP &&
26153b9b51feSCy Schubert 		    np->in_osrcmsk == np->in_nsrcmsk) {
26163b9b51feSCy Schubert 			/*
26173b9b51feSCy Schubert 			 * map the address block in a 1:1 fashion
26183b9b51feSCy Schubert 			 */
26193b9b51feSCy Schubert 			in.s_addr = np->in_nsrcaddr;
26203b9b51feSCy Schubert 			in.s_addr |= fin->fin_saddr & ~np->in_osrcmsk;
26213b9b51feSCy Schubert 			in.s_addr = ntohl(in.s_addr);
26223b9b51feSCy Schubert 
26233b9b51feSCy Schubert 		} else if (np->in_redir & NAT_MAPBLK) {
26243b9b51feSCy Schubert 			if ((l >= np->in_ppip) || ((l > 0) &&
26253b9b51feSCy Schubert 			     !(flags & IPN_TCPUDP))) {
26263b9b51feSCy Schubert 				NBUMPSIDEX(1, ns_exhausted, ns_exhausted_2);
26273b9b51feSCy Schubert 				DT4(ns_exhausted_2, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
26288c82b374SCy Schubert 				return (-1);
26293b9b51feSCy Schubert 			}
26303b9b51feSCy Schubert 			/*
26313b9b51feSCy Schubert 			 * map-block - Calculate destination address.
26323b9b51feSCy Schubert 			 */
26333b9b51feSCy Schubert 			in.s_addr = ntohl(fin->fin_saddr);
26343b9b51feSCy Schubert 			in.s_addr &= ntohl(~np->in_osrcmsk);
26353b9b51feSCy Schubert 			inb.s_addr = in.s_addr;
26363b9b51feSCy Schubert 			in.s_addr /= np->in_ippip;
26373b9b51feSCy Schubert 			in.s_addr &= ntohl(~np->in_nsrcmsk);
26383b9b51feSCy Schubert 			in.s_addr += ntohl(np->in_nsrcaddr);
26393b9b51feSCy Schubert 			/*
26403b9b51feSCy Schubert 			 * Calculate destination port.
26413b9b51feSCy Schubert 			 */
26423b9b51feSCy Schubert 			if ((flags & IPN_TCPUDP) &&
26433b9b51feSCy Schubert 			    (np->in_ppip != 0)) {
26443b9b51feSCy Schubert 				port = ntohs(sport) + l;
26453b9b51feSCy Schubert 				port %= np->in_ppip;
26463b9b51feSCy Schubert 				port += np->in_ppip *
26473b9b51feSCy Schubert 					(inb.s_addr % np->in_ippip);
26483b9b51feSCy Schubert 				port += MAPBLK_MINPORT;
26493b9b51feSCy Schubert 				port = htons(port);
26503b9b51feSCy Schubert 			}
26513b9b51feSCy Schubert 
26523b9b51feSCy Schubert 		} else if ((np->in_nsrcaddr == 0) &&
26533b9b51feSCy Schubert 			   (np->in_nsrcmsk == 0xffffffff)) {
26543b9b51feSCy Schubert 			i6addr_t in6;
26553b9b51feSCy Schubert 
26563b9b51feSCy Schubert 			/*
26573b9b51feSCy Schubert 			 * 0/32 - use the interface's IP address.
26583b9b51feSCy Schubert 			 */
26593b9b51feSCy Schubert 			if ((l > 0) ||
26603b9b51feSCy Schubert 			    ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp,
26613b9b51feSCy Schubert 				       &in6, NULL) == -1) {
26623b9b51feSCy Schubert 				NBUMPSIDEX(1, ns_new_ifpaddr, ns_new_ifpaddr_1);
26633b9b51feSCy Schubert 				DT4(ns_new_ifpaddr_1, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
26648c82b374SCy Schubert 				return (-1);
26653b9b51feSCy Schubert 			}
26663b9b51feSCy Schubert 			in.s_addr = ntohl(in6.in4.s_addr);
26673b9b51feSCy Schubert 
26683b9b51feSCy Schubert 		} else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) {
26693b9b51feSCy Schubert 			/*
26703b9b51feSCy Schubert 			 * 0/0 - use the original source address/port.
26713b9b51feSCy Schubert 			 */
26723b9b51feSCy Schubert 			if (l > 0) {
26733b9b51feSCy Schubert 				NBUMPSIDEX(1, ns_exhausted, ns_exhausted_3);
26743b9b51feSCy Schubert 				DT4(ns_exhausted_3, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
26758c82b374SCy Schubert 				return (-1);
26763b9b51feSCy Schubert 			}
26773b9b51feSCy Schubert 			in.s_addr = ntohl(fin->fin_saddr);
26783b9b51feSCy Schubert 
26793b9b51feSCy Schubert 		} else if ((np->in_nsrcmsk != 0xffffffff) &&
26803b9b51feSCy Schubert 			   (np->in_spnext == 0) && ((l > 0) || (hm == NULL)))
26813b9b51feSCy Schubert 			np->in_snip++;
26823b9b51feSCy Schubert 
26833b9b51feSCy Schubert 		natl = NULL;
26843b9b51feSCy Schubert 
26853b9b51feSCy Schubert 		if ((flags & IPN_TCPUDP) &&
26863b9b51feSCy Schubert 		    ((np->in_redir & NAT_MAPBLK) == 0) &&
26873b9b51feSCy Schubert 		    (np->in_flags & IPN_AUTOPORTMAP)) {
26883b9b51feSCy Schubert 			/*
26893b9b51feSCy Schubert 			 * "ports auto" (without map-block)
26903b9b51feSCy Schubert 			 */
26913b9b51feSCy Schubert 			if ((l > 0) && (l % np->in_ppip == 0)) {
26923b9b51feSCy Schubert 				if ((l > np->in_ppip) &&
26933b9b51feSCy Schubert 				    np->in_nsrcmsk != 0xffffffff)
26943b9b51feSCy Schubert 					np->in_snip++;
26953b9b51feSCy Schubert 			}
26963b9b51feSCy Schubert 			if (np->in_ppip != 0) {
26973b9b51feSCy Schubert 				port = ntohs(sport);
26983b9b51feSCy Schubert 				port += (l % np->in_ppip);
26993b9b51feSCy Schubert 				port %= np->in_ppip;
27003b9b51feSCy Schubert 				port += np->in_ppip *
27013b9b51feSCy Schubert 					(ntohl(fin->fin_saddr) %
27023b9b51feSCy Schubert 					 np->in_ippip);
27033b9b51feSCy Schubert 				port += MAPBLK_MINPORT;
27043b9b51feSCy Schubert 				port = htons(port);
27053b9b51feSCy Schubert 			}
27063b9b51feSCy Schubert 
27073b9b51feSCy Schubert 		} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
27083b9b51feSCy Schubert 			   (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) {
27093b9b51feSCy Schubert 			/*
27103b9b51feSCy Schubert 			 * Standard port translation.  Select next port.
27113b9b51feSCy Schubert 			 */
27123b9b51feSCy Schubert 			if (np->in_flags & IPN_SEQUENTIAL) {
27133b9b51feSCy Schubert 				port = np->in_spnext;
27143b9b51feSCy Schubert 			} else {
27153b9b51feSCy Schubert 				port = ipf_random() % (np->in_spmax -
27163b9b51feSCy Schubert 						       np->in_spmin + 1);
27173b9b51feSCy Schubert 				port += np->in_spmin;
27183b9b51feSCy Schubert 			}
27193b9b51feSCy Schubert 			port = htons(port);
27203b9b51feSCy Schubert 			np->in_spnext++;
27213b9b51feSCy Schubert 
27223b9b51feSCy Schubert 			if (np->in_spnext > np->in_spmax) {
27233b9b51feSCy Schubert 				np->in_spnext = np->in_spmin;
27243b9b51feSCy Schubert 				if (np->in_nsrcmsk != 0xffffffff)
27253b9b51feSCy Schubert 					np->in_snip++;
27263b9b51feSCy Schubert 			}
27273b9b51feSCy Schubert 		}
27283b9b51feSCy Schubert 
27293b9b51feSCy Schubert 		if (np->in_flags & IPN_SIPRANGE) {
27303b9b51feSCy Schubert 			if (np->in_snip > ntohl(np->in_nsrcmsk))
27313b9b51feSCy Schubert 				np->in_snip = ntohl(np->in_nsrcaddr);
27323b9b51feSCy Schubert 		} else {
27333b9b51feSCy Schubert 			if ((np->in_nsrcmsk != 0xffffffff) &&
27343b9b51feSCy Schubert 			    ((np->in_snip + 1) & ntohl(np->in_nsrcmsk)) >
27353b9b51feSCy Schubert 			    ntohl(np->in_nsrcaddr))
27363b9b51feSCy Schubert 				np->in_snip = ntohl(np->in_nsrcaddr) + 1;
27373b9b51feSCy Schubert 		}
27383b9b51feSCy Schubert 
27393b9b51feSCy Schubert 		if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
27403b9b51feSCy Schubert 			port = sport;
27413b9b51feSCy Schubert 
27423b9b51feSCy Schubert 		/*
27433b9b51feSCy Schubert 		 * Here we do a lookup of the connection as seen from
27443b9b51feSCy Schubert 		 * the outside.  If an IP# pair already exists, try
27453b9b51feSCy Schubert 		 * again.  So if you have A->B becomes C->B, you can
27463b9b51feSCy Schubert 		 * also have D->E become C->E but not D->B causing
27473b9b51feSCy Schubert 		 * another C->B.  Also take protocol and ports into
27483b9b51feSCy Schubert 		 * account when determining whether a pre-existing
27493b9b51feSCy Schubert 		 * NAT setup will cause an external conflict where
27503b9b51feSCy Schubert 		 * this is appropriate.
27513b9b51feSCy Schubert 		 */
27523b9b51feSCy Schubert 		inb.s_addr = htonl(in.s_addr);
27533b9b51feSCy Schubert 		sp = fin->fin_data[0];
27543b9b51feSCy Schubert 		dp = fin->fin_data[1];
27553b9b51feSCy Schubert 		fin->fin_data[0] = fin->fin_data[1];
27563b9b51feSCy Schubert 		fin->fin_data[1] = ntohs(port);
27573b9b51feSCy Schubert 		natl = ipf_nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
27583b9b51feSCy Schubert 					(u_int)fin->fin_p, fin->fin_dst, inb);
27593b9b51feSCy Schubert 		fin->fin_data[0] = sp;
27603b9b51feSCy Schubert 		fin->fin_data[1] = dp;
27613b9b51feSCy Schubert 
27623b9b51feSCy Schubert 		/*
27633b9b51feSCy Schubert 		 * Has the search wrapped around and come back to the
27643b9b51feSCy Schubert 		 * start ?
27653b9b51feSCy Schubert 		 */
27663b9b51feSCy Schubert 		if ((natl != NULL) &&
27673b9b51feSCy Schubert 		    (np->in_spnext != 0) && (st_port == np->in_spnext) &&
27683b9b51feSCy Schubert 		    (np->in_snip != 0) && (st_ip == np->in_snip)) {
27693b9b51feSCy Schubert 			NBUMPSIDED(1, ns_wrap);
27703b9b51feSCy Schubert 			DT4(ns_wrap, fr_info_t *, fin, nat_t *, nat, natinfo_t *, ni, ipnat_t *, np);
27718c82b374SCy Schubert 			return (-1);
27723b9b51feSCy Schubert 		}
27733b9b51feSCy Schubert 		l++;
27743b9b51feSCy Schubert 	} while (natl != NULL);
27753b9b51feSCy Schubert 
27763b9b51feSCy Schubert 	/* Setup the NAT table */
27773b9b51feSCy Schubert 	nat->nat_osrcip = fin->fin_src;
27783b9b51feSCy Schubert 	nat->nat_nsrcaddr = htonl(in.s_addr);
27793b9b51feSCy Schubert 	nat->nat_odstip = fin->fin_dst;
27803b9b51feSCy Schubert 	nat->nat_ndstip = fin->fin_dst;
27813b9b51feSCy Schubert 	if (nat->nat_hm == NULL)
27823b9b51feSCy Schubert 		nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src,
27833b9b51feSCy Schubert 					      fin->fin_dst, nat->nat_nsrcip,
27843b9b51feSCy Schubert 					      0);
27853b9b51feSCy Schubert 
27863b9b51feSCy Schubert 	if (flags & IPN_TCPUDP) {
27873b9b51feSCy Schubert 		nat->nat_osport = sport;
27883b9b51feSCy Schubert 		nat->nat_nsport = port;	/* sport */
27893b9b51feSCy Schubert 		nat->nat_odport = dport;
27903b9b51feSCy Schubert 		nat->nat_ndport = dport;
27913b9b51feSCy Schubert 		((tcphdr_t *)fin->fin_dp)->th_sport = port;
27923b9b51feSCy Schubert 	} else if (flags & IPN_ICMPQUERY) {
27933b9b51feSCy Schubert 		nat->nat_oicmpid = fin->fin_data[1];
27943b9b51feSCy Schubert 		((icmphdr_t *)fin->fin_dp)->icmp_id = port;
27953b9b51feSCy Schubert 		nat->nat_nicmpid = port;
27963b9b51feSCy Schubert 	}
27978c82b374SCy Schubert 	return (0);
27983b9b51feSCy Schubert }
27993b9b51feSCy Schubert 
28003b9b51feSCy Schubert 
28013b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
28023b9b51feSCy Schubert /* Function:    ipf_nat_newrdr                                              */
28033b9b51feSCy Schubert /* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
28043b9b51feSCy Schubert /*                    allow rule to be moved if IPN_ROUNDR is set.          */
28053b9b51feSCy Schubert /* Parameters:  fin(I) - pointer to packet information                      */
28063b9b51feSCy Schubert /*              nat(I) - pointer to NAT entry                               */
28073b9b51feSCy Schubert /*              ni(I)  - pointer to structure with misc. information needed */
28083b9b51feSCy Schubert /*                       to create new NAT entry.                           */
28093b9b51feSCy Schubert /*                                                                          */
28103b9b51feSCy Schubert /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
28113b9b51feSCy Schubert /* to the new IP address for the translation.                               */
28123b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
28133b9b51feSCy Schubert static int
ipf_nat_newrdr(fr_info_t * fin,nat_t * nat,natinfo_t * ni)2814064a5a95SCy Schubert ipf_nat_newrdr(fr_info_t *fin, nat_t *nat, natinfo_t *ni)
28153b9b51feSCy Schubert {
28163b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
28173b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
28183b9b51feSCy Schubert 	u_short nport, dport, sport;
28193b9b51feSCy Schubert 	struct in_addr in, inb;
28203b9b51feSCy Schubert 	u_short sp, dp;
28213b9b51feSCy Schubert 	hostmap_t *hm;
28223b9b51feSCy Schubert 	u_32_t flags;
28233b9b51feSCy Schubert 	ipnat_t *np;
28243b9b51feSCy Schubert 	nat_t *natl;
28253b9b51feSCy Schubert 	int move;
28263b9b51feSCy Schubert 
28273b9b51feSCy Schubert 	move = 1;
28283b9b51feSCy Schubert 	hm = NULL;
28293b9b51feSCy Schubert 	in.s_addr = 0;
28303b9b51feSCy Schubert 	np = ni->nai_np;
28313b9b51feSCy Schubert 	flags = nat->nat_flags;
28323b9b51feSCy Schubert 
28333b9b51feSCy Schubert 	if (flags & IPN_ICMPQUERY) {
28343b9b51feSCy Schubert 		dport = fin->fin_data[1];
28353b9b51feSCy Schubert 		sport = 0;
28363b9b51feSCy Schubert 	} else {
28373b9b51feSCy Schubert 		sport = htons(fin->fin_data[0]);
28383b9b51feSCy Schubert 		dport = htons(fin->fin_data[1]);
28393b9b51feSCy Schubert 	}
28403b9b51feSCy Schubert 
28413b9b51feSCy Schubert 	/* TRACE sport, dport */
28423b9b51feSCy Schubert 
28433b9b51feSCy Schubert 
28443b9b51feSCy Schubert 	/*
28453b9b51feSCy Schubert 	 * If the matching rule has IPN_STICKY set, then we want to have the
28463b9b51feSCy Schubert 	 * same rule kick in as before.  Why would this happen?  If you have
28473b9b51feSCy Schubert 	 * a collection of rdr rules with "round-robin sticky", the current
28483b9b51feSCy Schubert 	 * packet might match a different one to the previous connection but
28493b9b51feSCy Schubert 	 * we want the same destination to be used.
28503b9b51feSCy Schubert 	 */
28513b9b51feSCy Schubert 	if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) &&
28523b9b51feSCy Schubert 	    ((np->in_flags & IPN_STICKY) != 0)) {
28533b9b51feSCy Schubert 		hm = ipf_nat_hostmap(softn, NULL, fin->fin_src, fin->fin_dst,
28543b9b51feSCy Schubert 				     in, (u_32_t)dport);
28553b9b51feSCy Schubert 		if (hm != NULL) {
28563b9b51feSCy Schubert 			in.s_addr = ntohl(hm->hm_ndstip.s_addr);
28573b9b51feSCy Schubert 			np = hm->hm_ipnat;
28583b9b51feSCy Schubert 			ni->nai_np = np;
28593b9b51feSCy Schubert 			move = 0;
28603b9b51feSCy Schubert 			ipf_nat_hostmapdel(softc, &hm);
28613b9b51feSCy Schubert 		}
28623b9b51feSCy Schubert 	}
28633b9b51feSCy Schubert 
28643b9b51feSCy Schubert 	/*
28653b9b51feSCy Schubert 	 * Otherwise, it's an inbound packet. Most likely, we don't
28663b9b51feSCy Schubert 	 * want to rewrite source ports and source addresses. Instead,
28673b9b51feSCy Schubert 	 * we want to rewrite to a fixed internal address and fixed
28683b9b51feSCy Schubert 	 * internal port.
28693b9b51feSCy Schubert 	 */
28703b9b51feSCy Schubert 	if (np->in_flags & IPN_SPLIT) {
28713b9b51feSCy Schubert 		in.s_addr = np->in_dnip;
28723b9b51feSCy Schubert 		inb.s_addr = htonl(in.s_addr);
28733b9b51feSCy Schubert 
28743b9b51feSCy Schubert 		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
28753b9b51feSCy Schubert 			hm = ipf_nat_hostmap(softn, NULL, fin->fin_src,
28763b9b51feSCy Schubert 					     fin->fin_dst, inb, (u_32_t)dport);
28773b9b51feSCy Schubert 			if (hm != NULL) {
28783b9b51feSCy Schubert 				in.s_addr = hm->hm_ndstip.s_addr;
28793b9b51feSCy Schubert 				move = 0;
28803b9b51feSCy Schubert 			}
28813b9b51feSCy Schubert 		}
28823b9b51feSCy Schubert 
28833b9b51feSCy Schubert 		if (hm == NULL || hm->hm_ref == 1) {
28843b9b51feSCy Schubert 			if (np->in_ndstaddr == htonl(in.s_addr)) {
28853b9b51feSCy Schubert 				np->in_dnip = ntohl(np->in_ndstmsk);
28863b9b51feSCy Schubert 				move = 0;
28873b9b51feSCy Schubert 			} else {
28883b9b51feSCy Schubert 				np->in_dnip = ntohl(np->in_ndstaddr);
28893b9b51feSCy Schubert 			}
28903b9b51feSCy Schubert 		}
28913b9b51feSCy Schubert 		if (hm != NULL)
28923b9b51feSCy Schubert 			ipf_nat_hostmapdel(softc, &hm);
28933b9b51feSCy Schubert 
28943b9b51feSCy Schubert 	} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) {
28953b9b51feSCy Schubert 		i6addr_t in6;
28963b9b51feSCy Schubert 
28973b9b51feSCy Schubert 		/*
28983b9b51feSCy Schubert 		 * 0/32 - use the interface's IP address.
28993b9b51feSCy Schubert 		 */
29003b9b51feSCy Schubert 		if (ipf_ifpaddr(softc, 4, FRI_NORMAL, fin->fin_ifp,
29013b9b51feSCy Schubert 			       &in6, NULL) == -1) {
29023b9b51feSCy Schubert 			NBUMPSIDEX(0, ns_new_ifpaddr, ns_new_ifpaddr_2);
29033b9b51feSCy Schubert 			DT3(ns_new_ifpaddr_2, fr_info_t *, fin, nat_t *, nat, natinfo_t, ni);
29048c82b374SCy Schubert 			return (-1);
29053b9b51feSCy Schubert 		}
29063b9b51feSCy Schubert 		in.s_addr = ntohl(in6.in4.s_addr);
29073b9b51feSCy Schubert 
29083b9b51feSCy Schubert 	} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk== 0)) {
29093b9b51feSCy Schubert 		/*
29103b9b51feSCy Schubert 		 * 0/0 - use the original destination address/port.
29113b9b51feSCy Schubert 		 */
29123b9b51feSCy Schubert 		in.s_addr = ntohl(fin->fin_daddr);
29133b9b51feSCy Schubert 
29143b9b51feSCy Schubert 	} else if (np->in_redir == NAT_BIMAP &&
29153b9b51feSCy Schubert 		   np->in_ndstmsk == np->in_odstmsk) {
29163b9b51feSCy Schubert 		/*
29173b9b51feSCy Schubert 		 * map the address block in a 1:1 fashion
29183b9b51feSCy Schubert 		 */
29193b9b51feSCy Schubert 		in.s_addr = np->in_ndstaddr;
29203b9b51feSCy Schubert 		in.s_addr |= fin->fin_daddr & ~np->in_ndstmsk;
29213b9b51feSCy Schubert 		in.s_addr = ntohl(in.s_addr);
29223b9b51feSCy Schubert 	} else {
29233b9b51feSCy Schubert 		in.s_addr = ntohl(np->in_ndstaddr);
29243b9b51feSCy Schubert 	}
29253b9b51feSCy Schubert 
29263b9b51feSCy Schubert 	if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
29273b9b51feSCy Schubert 		nport = dport;
29283b9b51feSCy Schubert 	else {
29293b9b51feSCy Schubert 		/*
29303b9b51feSCy Schubert 		 * Whilst not optimized for the case where
29313b9b51feSCy Schubert 		 * pmin == pmax, the gain is not significant.
29323b9b51feSCy Schubert 		 */
29333b9b51feSCy Schubert 		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
29343b9b51feSCy Schubert 		    (np->in_odport != np->in_dtop)) {
29353b9b51feSCy Schubert 			nport = ntohs(dport) - np->in_odport + np->in_dpmax;
29363b9b51feSCy Schubert 			nport = htons(nport);
29373b9b51feSCy Schubert 		} else {
29383b9b51feSCy Schubert 			nport = htons(np->in_dpnext);
29393b9b51feSCy Schubert 			np->in_dpnext++;
29403b9b51feSCy Schubert 			if (np->in_dpnext > np->in_dpmax)
29413b9b51feSCy Schubert 				np->in_dpnext = np->in_dpmin;
29423b9b51feSCy Schubert 		}
29433b9b51feSCy Schubert 	}
29443b9b51feSCy Schubert 
29453b9b51feSCy Schubert 	/*
29463b9b51feSCy Schubert 	 * When the redirect-to address is set to 0.0.0.0, just
29473b9b51feSCy Schubert 	 * assume a blank `forwarding' of the packet.  We don't
29483b9b51feSCy Schubert 	 * setup any translation for this either.
29493b9b51feSCy Schubert 	 */
29503b9b51feSCy Schubert 	if (in.s_addr == 0) {
29513b9b51feSCy Schubert 		if (nport == dport) {
29523b9b51feSCy Schubert 			NBUMPSIDED(0, ns_xlate_null);
29538c82b374SCy Schubert 			return (-1);
29543b9b51feSCy Schubert 		}
29553b9b51feSCy Schubert 		in.s_addr = ntohl(fin->fin_daddr);
29563b9b51feSCy Schubert 	}
29573b9b51feSCy Schubert 
29583b9b51feSCy Schubert 	/*
29593b9b51feSCy Schubert 	 * Check to see if this redirect mapping already exists and if
29603b9b51feSCy Schubert 	* it does, return "failure" (allowing it to be created will just
29613b9b51feSCy Schubert 	 * cause one or both of these "connections" to stop working.)
29623b9b51feSCy Schubert 	 */
29633b9b51feSCy Schubert 	inb.s_addr = htonl(in.s_addr);
29643b9b51feSCy Schubert 	sp = fin->fin_data[0];
29653b9b51feSCy Schubert 	dp = fin->fin_data[1];
29663b9b51feSCy Schubert 	fin->fin_data[1] = fin->fin_data[0];
29673b9b51feSCy Schubert 	fin->fin_data[0] = ntohs(nport);
29683b9b51feSCy Schubert 	natl = ipf_nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
29693b9b51feSCy Schubert 			     (u_int)fin->fin_p, inb, fin->fin_src);
29703b9b51feSCy Schubert 	fin->fin_data[0] = sp;
29713b9b51feSCy Schubert 	fin->fin_data[1] = dp;
29723b9b51feSCy Schubert 	if (natl != NULL) {
29733b9b51feSCy Schubert 		DT2(ns_new_xlate_exists, fr_info_t *, fin, nat_t *, natl);
29743b9b51feSCy Schubert 		NBUMPSIDE(0, ns_xlate_exists);
29758c82b374SCy Schubert 		return (-1);
29763b9b51feSCy Schubert 	}
29773b9b51feSCy Schubert 
29783b9b51feSCy Schubert 	inb.s_addr = htonl(in.s_addr);
29793b9b51feSCy Schubert 	nat->nat_ndstaddr = htonl(in.s_addr);
29803b9b51feSCy Schubert 	nat->nat_odstip = fin->fin_dst;
29813b9b51feSCy Schubert 	nat->nat_nsrcip = fin->fin_src;
29823b9b51feSCy Schubert 	nat->nat_osrcip = fin->fin_src;
29833b9b51feSCy Schubert 	if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
29843b9b51feSCy Schubert 		nat->nat_hm = ipf_nat_hostmap(softn, np, fin->fin_src,
29853b9b51feSCy Schubert 					      fin->fin_dst, inb, (u_32_t)dport);
29863b9b51feSCy Schubert 
29873b9b51feSCy Schubert 	if (flags & IPN_TCPUDP) {
29883b9b51feSCy Schubert 		nat->nat_odport = dport;
29893b9b51feSCy Schubert 		nat->nat_ndport = nport;
29903b9b51feSCy Schubert 		nat->nat_osport = sport;
29913b9b51feSCy Schubert 		nat->nat_nsport = sport;
29923b9b51feSCy Schubert 		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
29933b9b51feSCy Schubert 	} else if (flags & IPN_ICMPQUERY) {
29943b9b51feSCy Schubert 		nat->nat_oicmpid = fin->fin_data[1];
29953b9b51feSCy Schubert 		((icmphdr_t *)fin->fin_dp)->icmp_id = nport;
29963b9b51feSCy Schubert 		nat->nat_nicmpid = nport;
29973b9b51feSCy Schubert 	}
29983b9b51feSCy Schubert 
29998c82b374SCy Schubert 	return (move);
30003b9b51feSCy Schubert }
30013b9b51feSCy Schubert 
30023b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
30033b9b51feSCy Schubert /* Function:    ipf_nat_add                                                 */
30043b9b51feSCy Schubert /* Returns:     nat_t* - NULL == failure to create new NAT structure,       */
30053b9b51feSCy Schubert /*                       else pointer to new NAT structure                  */
30063b9b51feSCy Schubert /* Parameters:  fin(I)       - pointer to packet information                */
30073b9b51feSCy Schubert /*              np(I)        - pointer to NAT rule                          */
30083b9b51feSCy Schubert /*              natsave(I)   - pointer to where to store NAT struct pointer */
30093b9b51feSCy Schubert /*              flags(I)     - flags describing the current packet          */
30103b9b51feSCy Schubert /*              direction(I) - direction of packet (in/out)                 */
30113b9b51feSCy Schubert /* Write Lock:  ipf_nat                                                     */
30123b9b51feSCy Schubert /*                                                                          */
30133b9b51feSCy Schubert /* Attempts to create a new NAT entry.  Does not actually change the packet */
30143b9b51feSCy Schubert /* in any way.                                                              */
30153b9b51feSCy Schubert /*                                                                          */
30163b9b51feSCy Schubert /* This function is in three main parts: (1) deal with creating a new NAT   */
30173b9b51feSCy Schubert /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
30183b9b51feSCy Schubert /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
30193b9b51feSCy Schubert /* and (3) building that structure and putting it into the NAT table(s).    */
30203b9b51feSCy Schubert /*                                                                          */
30213b9b51feSCy Schubert /* NOTE: natsave should NOT be used to point back to an ipstate_t struct    */
30223b9b51feSCy Schubert /*       as it can result in memory being corrupted.                        */
30233b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
30243b9b51feSCy Schubert nat_t *
ipf_nat_add(fr_info_t * fin,ipnat_t * np,nat_t ** natsave,u_int flags,int direction)3025064a5a95SCy Schubert ipf_nat_add(fr_info_t *fin, ipnat_t *np, nat_t **natsave, u_int flags,
3026064a5a95SCy Schubert 	int direction)
30273b9b51feSCy Schubert {
30283b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
30293b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
30303b9b51feSCy Schubert 	hostmap_t *hm = NULL;
30313b9b51feSCy Schubert 	nat_t *nat, *natl;
30323b9b51feSCy Schubert 	natstat_t *nsp;
30333b9b51feSCy Schubert 	u_int nflags;
30343b9b51feSCy Schubert 	natinfo_t ni;
30353b9b51feSCy Schubert 	int move;
30363b9b51feSCy Schubert 
30373b9b51feSCy Schubert 	nsp = &softn->ipf_nat_stats;
30383b9b51feSCy Schubert 
30393b9b51feSCy Schubert 	if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) >
30403b9b51feSCy Schubert 	    softn->ipf_nat_table_wm_high) {
30413b9b51feSCy Schubert 		softn->ipf_nat_doflush = 1;
30423b9b51feSCy Schubert 	}
30433b9b51feSCy Schubert 
30443b9b51feSCy Schubert 	if (nsp->ns_active >= softn->ipf_nat_table_max) {
30453b9b51feSCy Schubert 		NBUMPSIDED(fin->fin_out, ns_table_max);
30463b9b51feSCy Schubert 		DT2(ns_table_max, nat_stat_t *, nsp, ipf_nat_softc_t *, softn);
30478c82b374SCy Schubert 		return (NULL);
30483b9b51feSCy Schubert 	}
30493b9b51feSCy Schubert 
30503b9b51feSCy Schubert 	move = 1;
30513b9b51feSCy Schubert 	nflags = np->in_flags & flags;
30523b9b51feSCy Schubert 	nflags &= NAT_FROMRULE;
30533b9b51feSCy Schubert 
30543b9b51feSCy Schubert 	ni.nai_np = np;
30553b9b51feSCy Schubert 	ni.nai_dport = 0;
30563b9b51feSCy Schubert 	ni.nai_sport = 0;
30573b9b51feSCy Schubert 
30583b9b51feSCy Schubert 	/* Give me a new nat */
30593b9b51feSCy Schubert 	KMALLOC(nat, nat_t *);
30603b9b51feSCy Schubert 	if (nat == NULL) {
30613b9b51feSCy Schubert 		DT(ns_memfail);
30623b9b51feSCy Schubert 		NBUMPSIDED(fin->fin_out, ns_memfail);
30633b9b51feSCy Schubert 		/*
30643b9b51feSCy Schubert 		 * Try to automatically tune the max # of entries in the
30653b9b51feSCy Schubert 		 * table allowed to be less than what will cause kmem_alloc()
30663b9b51feSCy Schubert 		 * to fail and try to eliminate panics due to out of memory
30673b9b51feSCy Schubert 		 * conditions arising.
30683b9b51feSCy Schubert 		 */
30693b9b51feSCy Schubert 		if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) &&
30703b9b51feSCy Schubert 		    (nsp->ns_active > 100)) {
30713b9b51feSCy Schubert 			softn->ipf_nat_table_max = nsp->ns_active - 100;
30723b9b51feSCy Schubert 			printf("table_max reduced to %d\n",
30733b9b51feSCy Schubert 				softn->ipf_nat_table_max);
30743b9b51feSCy Schubert 		}
30758c82b374SCy Schubert 		return (NULL);
30763b9b51feSCy Schubert 	}
30773b9b51feSCy Schubert 
30783b9b51feSCy Schubert 	if (flags & IPN_ICMPQUERY) {
30793b9b51feSCy Schubert 		/*
30803b9b51feSCy Schubert 		 * In the ICMP query NAT code, we translate the ICMP id fields
30813b9b51feSCy Schubert 		 * to make them unique. This is indepedent of the ICMP type
30823b9b51feSCy Schubert 		 * (e.g. in the unlikely event that a host sends an echo and
30833b9b51feSCy Schubert 		 * an tstamp request with the same id, both packets will have
30843b9b51feSCy Schubert 		 * their ip address/id field changed in the same way).
30853b9b51feSCy Schubert 		 */
30863b9b51feSCy Schubert 		/* The icmp_id field is used by the sender to identify the
30873b9b51feSCy Schubert 		 * process making the icmp request. (the receiver justs
30883b9b51feSCy Schubert 		 * copies it back in its response). So, it closely matches
30893b9b51feSCy Schubert 		 * the concept of source port. We overlay sport, so we can
30903b9b51feSCy Schubert 		 * maximally reuse the existing code.
30913b9b51feSCy Schubert 		 */
30923b9b51feSCy Schubert 		ni.nai_sport = fin->fin_data[1];
30933b9b51feSCy Schubert 		ni.nai_dport = 0;
30943b9b51feSCy Schubert 	}
30953b9b51feSCy Schubert 
30963b9b51feSCy Schubert 	bzero((char *)nat, sizeof(*nat));
30973b9b51feSCy Schubert 	nat->nat_flags = flags;
30983b9b51feSCy Schubert 	nat->nat_redir = np->in_redir;
30993b9b51feSCy Schubert 	nat->nat_dir = direction;
31003b9b51feSCy Schubert 	nat->nat_pr[0] = fin->fin_p;
31013b9b51feSCy Schubert 	nat->nat_pr[1] = fin->fin_p;
31023b9b51feSCy Schubert 
31033b9b51feSCy Schubert 	/*
31043b9b51feSCy Schubert 	 * Search the current table for a match and create a new mapping
31053b9b51feSCy Schubert 	 * if there is none found.
31063b9b51feSCy Schubert 	 */
31073b9b51feSCy Schubert 	if (np->in_redir & NAT_DIVERTUDP) {
31083b9b51feSCy Schubert 		move = ipf_nat_newdivert(fin, nat, &ni);
31093b9b51feSCy Schubert 
31103b9b51feSCy Schubert 	} else if (np->in_redir & NAT_REWRITE) {
31113b9b51feSCy Schubert 		move = ipf_nat_newrewrite(fin, nat, &ni);
31123b9b51feSCy Schubert 
31133b9b51feSCy Schubert 	} else if (direction == NAT_OUTBOUND) {
31143b9b51feSCy Schubert 		/*
31153b9b51feSCy Schubert 		 * We can now arrange to call this for the same connection
31163b9b51feSCy Schubert 		 * because ipf_nat_new doesn't protect the code path into
31173b9b51feSCy Schubert 		 * this function.
31183b9b51feSCy Schubert 		 */
31193b9b51feSCy Schubert 		natl = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p,
31203b9b51feSCy Schubert 				     fin->fin_src, fin->fin_dst);
31213b9b51feSCy Schubert 		if (natl != NULL) {
31223b9b51feSCy Schubert 			KFREE(nat);
31233b9b51feSCy Schubert 			nat = natl;
31243b9b51feSCy Schubert 			goto done;
31253b9b51feSCy Schubert 		}
31263b9b51feSCy Schubert 
31273b9b51feSCy Schubert 		move = ipf_nat_newmap(fin, nat, &ni);
31283b9b51feSCy Schubert 	} else {
31293b9b51feSCy Schubert 		/*
31303b9b51feSCy Schubert 		 * NAT_INBOUND is used for redirects rules
31313b9b51feSCy Schubert 		 */
31323b9b51feSCy Schubert 		natl = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p,
31333b9b51feSCy Schubert 					fin->fin_src, fin->fin_dst);
31343b9b51feSCy Schubert 		if (natl != NULL) {
31353b9b51feSCy Schubert 			KFREE(nat);
31363b9b51feSCy Schubert 			nat = natl;
31373b9b51feSCy Schubert 			goto done;
31383b9b51feSCy Schubert 		}
31393b9b51feSCy Schubert 
31403b9b51feSCy Schubert 		move = ipf_nat_newrdr(fin, nat, &ni);
31413b9b51feSCy Schubert 	}
31423b9b51feSCy Schubert 	if (move == -1)
31433b9b51feSCy Schubert 		goto badnat;
31443b9b51feSCy Schubert 
31453b9b51feSCy Schubert 	np = ni.nai_np;
31463b9b51feSCy Schubert 
31473b9b51feSCy Schubert 	nat->nat_mssclamp = np->in_mssclamp;
31483b9b51feSCy Schubert 	nat->nat_me = natsave;
31493b9b51feSCy Schubert 	nat->nat_fr = fin->fin_fr;
31503b9b51feSCy Schubert 	nat->nat_rev = fin->fin_rev;
31513b9b51feSCy Schubert 	nat->nat_ptr = np;
31523b9b51feSCy Schubert 	nat->nat_dlocal = np->in_dlocal;
31533b9b51feSCy Schubert 
31543b9b51feSCy Schubert 	if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) {
31553b9b51feSCy Schubert 		if (ipf_proxy_new(fin, nat) == -1) {
31563b9b51feSCy Schubert 			NBUMPSIDED(fin->fin_out, ns_appr_fail);
31573b9b51feSCy Schubert 			DT3(ns_appr_fail, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np);
31583b9b51feSCy Schubert 			goto badnat;
31593b9b51feSCy Schubert 		}
31603b9b51feSCy Schubert 	}
31613b9b51feSCy Schubert 
31623b9b51feSCy Schubert 	nat->nat_ifps[0] = np->in_ifps[0];
31633b9b51feSCy Schubert 	if (np->in_ifps[0] != NULL) {
31643b9b51feSCy Schubert 		COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]);
31653b9b51feSCy Schubert 	}
31663b9b51feSCy Schubert 
31673b9b51feSCy Schubert 	nat->nat_ifps[1] = np->in_ifps[1];
31683b9b51feSCy Schubert 	if (np->in_ifps[1] != NULL) {
31693b9b51feSCy Schubert 		COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]);
31703b9b51feSCy Schubert 	}
31713b9b51feSCy Schubert 
31723b9b51feSCy Schubert 	if (ipf_nat_finalise(fin, nat) == -1) {
31733b9b51feSCy Schubert 		goto badnat;
31743b9b51feSCy Schubert 	}
31753b9b51feSCy Schubert 
31763b9b51feSCy Schubert 	np->in_use++;
31773b9b51feSCy Schubert 
31783b9b51feSCy Schubert 	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
31793b9b51feSCy Schubert 		if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) {
31803b9b51feSCy Schubert 			ipf_nat_delrdr(softn, np);
31813b9b51feSCy Schubert 			ipf_nat_addrdr(softn, np);
31823b9b51feSCy Schubert 		} else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) {
31833b9b51feSCy Schubert 			ipf_nat_delmap(softn, np);
31843b9b51feSCy Schubert 			ipf_nat_addmap(softn, np);
31853b9b51feSCy Schubert 		}
31863b9b51feSCy Schubert 	}
31873b9b51feSCy Schubert 
31883b9b51feSCy Schubert 	if (flags & SI_WILDP)
31893b9b51feSCy Schubert 		nsp->ns_wilds++;
31903b9b51feSCy Schubert 	nsp->ns_proto[nat->nat_pr[0]]++;
31913b9b51feSCy Schubert 
31923b9b51feSCy Schubert 	goto done;
31933b9b51feSCy Schubert badnat:
31943b9b51feSCy Schubert 	DT3(ns_badnatnew, fr_info_t *, fin, nat_t *, nat, ipnat_t *, np);
31953b9b51feSCy Schubert 	NBUMPSIDE(fin->fin_out, ns_badnatnew);
31963b9b51feSCy Schubert 	if ((hm = nat->nat_hm) != NULL)
31973b9b51feSCy Schubert 		ipf_nat_hostmapdel(softc, &hm);
31983b9b51feSCy Schubert 	KFREE(nat);
31993b9b51feSCy Schubert 	nat = NULL;
32003b9b51feSCy Schubert done:
32013b9b51feSCy Schubert 	if (nat != NULL && np != NULL)
32023b9b51feSCy Schubert 		np->in_hits++;
32033b9b51feSCy Schubert 	if (natsave != NULL)
32043b9b51feSCy Schubert 		*natsave = nat;
32058c82b374SCy Schubert 	return (nat);
32063b9b51feSCy Schubert }
32073b9b51feSCy Schubert 
32083b9b51feSCy Schubert 
32093b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
32103b9b51feSCy Schubert /* Function:    ipf_nat_finalise                                            */
32113b9b51feSCy Schubert /* Returns:     int - 0 == sucess, -1 == failure                            */
32123b9b51feSCy Schubert /* Parameters:  fin(I) - pointer to packet information                      */
32133b9b51feSCy Schubert /*              nat(I) - pointer to NAT entry                               */
32143b9b51feSCy Schubert /* Write Lock:  ipf_nat                                                     */
32153b9b51feSCy Schubert /*                                                                          */
32163b9b51feSCy Schubert /* This is the tail end of constructing a new NAT entry and is the same     */
32173b9b51feSCy Schubert /* for both IPv4 and IPv6.                                                  */
32183b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
32193b9b51feSCy Schubert /*ARGSUSED*/
32203b9b51feSCy Schubert static int
ipf_nat_finalise(fr_info_t * fin,nat_t * nat)3221064a5a95SCy Schubert ipf_nat_finalise(fr_info_t *fin, nat_t *nat)
32223b9b51feSCy Schubert {
32233b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
32243b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
32253b9b51feSCy Schubert 	u_32_t sum1, sum2, sumd;
32263b9b51feSCy Schubert 	frentry_t *fr;
32273b9b51feSCy Schubert 	u_32_t flags;
32283b9b51feSCy Schubert #if SOLARIS && defined(_KERNEL) && defined(ICK_M_CTL_MAGIC)
32293b9b51feSCy Schubert 	qpktinfo_t *qpi = fin->fin_qpi;
32303b9b51feSCy Schubert #endif
32313b9b51feSCy Schubert 
32323b9b51feSCy Schubert 	flags = nat->nat_flags;
32333b9b51feSCy Schubert 
32343b9b51feSCy Schubert 	switch (nat->nat_pr[0])
32353b9b51feSCy Schubert 	{
32363b9b51feSCy Schubert 	case IPPROTO_ICMP :
32373b9b51feSCy Schubert 		sum1 = LONG_SUM(ntohs(nat->nat_oicmpid));
32383b9b51feSCy Schubert 		sum2 = LONG_SUM(ntohs(nat->nat_nicmpid));
32393b9b51feSCy Schubert 		CALC_SUMD(sum1, sum2, sumd);
32403b9b51feSCy Schubert 		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
32413b9b51feSCy Schubert 
32423b9b51feSCy Schubert 		break;
32433b9b51feSCy Schubert 
32443b9b51feSCy Schubert 	default :
32453b9b51feSCy Schubert 		sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr) + \
32463b9b51feSCy Schubert 				ntohs(nat->nat_osport));
32473b9b51feSCy Schubert 		sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr) + \
32483b9b51feSCy Schubert 				ntohs(nat->nat_nsport));
32493b9b51feSCy Schubert 		CALC_SUMD(sum1, sum2, sumd);
32503b9b51feSCy Schubert 		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
32513b9b51feSCy Schubert 
32523b9b51feSCy Schubert 		sum1 = LONG_SUM(ntohl(nat->nat_odstaddr) + \
32533b9b51feSCy Schubert 				ntohs(nat->nat_odport));
32543b9b51feSCy Schubert 		sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr) + \
32553b9b51feSCy Schubert 				ntohs(nat->nat_ndport));
32563b9b51feSCy Schubert 		CALC_SUMD(sum1, sum2, sumd);
32573b9b51feSCy Schubert 		nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16);
32583b9b51feSCy Schubert 		break;
32593b9b51feSCy Schubert 	}
32603b9b51feSCy Schubert 
32613b9b51feSCy Schubert 	/*
32623b9b51feSCy Schubert 	 * Compute the partial checksum, just in case.
32633b9b51feSCy Schubert 	 * This is only ever placed into outbound packets so care needs
32643b9b51feSCy Schubert 	 * to be taken over which pair of addresses are used.
32653b9b51feSCy Schubert 	 */
32663b9b51feSCy Schubert 	if (nat->nat_dir == NAT_OUTBOUND) {
32673b9b51feSCy Schubert 		sum1 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
32683b9b51feSCy Schubert 		sum1 += LONG_SUM(ntohl(nat->nat_ndstaddr));
32693b9b51feSCy Schubert 	} else {
32703b9b51feSCy Schubert 		sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr));
32713b9b51feSCy Schubert 		sum1 += LONG_SUM(ntohl(nat->nat_odstaddr));
32723b9b51feSCy Schubert 	}
32733b9b51feSCy Schubert 	sum1 += nat->nat_pr[1];
32743b9b51feSCy Schubert 	nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16);
32753b9b51feSCy Schubert 
32763b9b51feSCy Schubert 	sum1 = LONG_SUM(ntohl(nat->nat_osrcaddr));
32773b9b51feSCy Schubert 	sum2 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
32783b9b51feSCy Schubert 	CALC_SUMD(sum1, sum2, sumd);
32793b9b51feSCy Schubert 	nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
32803b9b51feSCy Schubert 
32813b9b51feSCy Schubert 	sum1 = LONG_SUM(ntohl(nat->nat_odstaddr));
32823b9b51feSCy Schubert 	sum2 = LONG_SUM(ntohl(nat->nat_ndstaddr));
32833b9b51feSCy Schubert 	CALC_SUMD(sum1, sum2, sumd);
32843b9b51feSCy Schubert 	nat->nat_ipsumd += (sumd & 0xffff) + (sumd >> 16);
32853b9b51feSCy Schubert 
32863b9b51feSCy Schubert 	nat->nat_v[0] = 4;
32873b9b51feSCy Schubert 	nat->nat_v[1] = 4;
32883b9b51feSCy Schubert 
32893b9b51feSCy Schubert 	if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
32903b9b51feSCy Schubert 		nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
32913b9b51feSCy Schubert 	}
32923b9b51feSCy Schubert 
32933b9b51feSCy Schubert 	if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
32943b9b51feSCy Schubert 		nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
32953b9b51feSCy Schubert 	}
32963b9b51feSCy Schubert 
32973b9b51feSCy Schubert 	if ((nat->nat_flags & SI_CLONE) == 0)
32983b9b51feSCy Schubert 		nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat);
32993b9b51feSCy Schubert 
33003b9b51feSCy Schubert 	if (ipf_nat_insert(softc, softn, nat) == 0) {
33013b9b51feSCy Schubert 		if (softn->ipf_nat_logging)
33023b9b51feSCy Schubert 			ipf_nat_log(softc, softn, nat, NL_NEW);
33033b9b51feSCy Schubert 		fr = nat->nat_fr;
33043b9b51feSCy Schubert 		if (fr != NULL) {
33053b9b51feSCy Schubert 			MUTEX_ENTER(&fr->fr_lock);
33063b9b51feSCy Schubert 			fr->fr_ref++;
33073b9b51feSCy Schubert 			MUTEX_EXIT(&fr->fr_lock);
33083b9b51feSCy Schubert 		}
33098c82b374SCy Schubert 		return (0);
33103b9b51feSCy Schubert 	}
33113b9b51feSCy Schubert 
33123b9b51feSCy Schubert 	NBUMPSIDED(fin->fin_out, ns_unfinalised);
33133b9b51feSCy Schubert 	DT2(ns_unfinalised, fr_info_t *, fin, nat_t *, nat);
33143b9b51feSCy Schubert 	/*
33153b9b51feSCy Schubert 	 * nat_insert failed, so cleanup time...
33163b9b51feSCy Schubert 	 */
33173b9b51feSCy Schubert 	if (nat->nat_sync != NULL)
33183b9b51feSCy Schubert 		ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync);
33198c82b374SCy Schubert 	return (-1);
33203b9b51feSCy Schubert }
33213b9b51feSCy Schubert 
33223b9b51feSCy Schubert 
33233b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
33243b9b51feSCy Schubert /* Function:    ipf_nat_insert                                              */
33253b9b51feSCy Schubert /* Returns:     int - 0 == sucess, -1 == failure                            */
33263b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
33273b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
33283b9b51feSCy Schubert /*              nat(I) - pointer to NAT structure                           */
33293b9b51feSCy Schubert /* Write Lock:  ipf_nat                                                     */
33303b9b51feSCy Schubert /*                                                                          */
33313b9b51feSCy Schubert /* Insert a NAT entry into the hash tables for searching and add it to the  */
33323b9b51feSCy Schubert /* list of active NAT entries.  Adjust global counters when complete.       */
33333b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
33343b9b51feSCy Schubert int
ipf_nat_insert(ipf_main_softc_t * softc,ipf_nat_softc_t * softn,nat_t * nat)3335064a5a95SCy Schubert ipf_nat_insert(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, nat_t *nat)
33363b9b51feSCy Schubert {
33373b9b51feSCy Schubert 	u_int hv0, hv1;
33383b9b51feSCy Schubert 	u_int sp, dp;
33393b9b51feSCy Schubert 	ipnat_t *in;
33403b9b51feSCy Schubert 	int ret;
33413b9b51feSCy Schubert 
33423b9b51feSCy Schubert 	/*
33433b9b51feSCy Schubert 	* Try and return an error as early as possible, so calculate the hash
33443b9b51feSCy Schubert 	 * entry numbers first and then proceed.
33453b9b51feSCy Schubert 	 */
33463b9b51feSCy Schubert 	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
33473b9b51feSCy Schubert 		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
33483b9b51feSCy Schubert 			sp = nat->nat_osport;
33493b9b51feSCy Schubert 			dp = nat->nat_odport;
33503b9b51feSCy Schubert 		} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
33513b9b51feSCy Schubert 			sp = 0;
33523b9b51feSCy Schubert 			dp = nat->nat_oicmpid;
33533b9b51feSCy Schubert 		} else {
33543b9b51feSCy Schubert 			sp = 0;
33553b9b51feSCy Schubert 			dp = 0;
33563b9b51feSCy Schubert 		}
33573b9b51feSCy Schubert 		hv0 = NAT_HASH_FN(nat->nat_osrcaddr, sp, 0xffffffff);
33583b9b51feSCy Schubert 		hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0 + dp, 0xffffffff);
33593b9b51feSCy Schubert 		/*
33603b9b51feSCy Schubert 		 * TRACE nat_osrcaddr, nat_osport, nat_odstaddr,
33613b9b51feSCy Schubert 		 * nat_odport, hv0
33623b9b51feSCy Schubert 		 */
33633b9b51feSCy Schubert 
33643b9b51feSCy Schubert 		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
33653b9b51feSCy Schubert 			sp = nat->nat_nsport;
33663b9b51feSCy Schubert 			dp = nat->nat_ndport;
33673b9b51feSCy Schubert 		} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
33683b9b51feSCy Schubert 			sp = 0;
33693b9b51feSCy Schubert 			dp = nat->nat_nicmpid;
33703b9b51feSCy Schubert 		} else {
33713b9b51feSCy Schubert 			sp = 0;
33723b9b51feSCy Schubert 			dp = 0;
33733b9b51feSCy Schubert 		}
33743b9b51feSCy Schubert 		hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, sp, 0xffffffff);
33753b9b51feSCy Schubert 		hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1 + dp, 0xffffffff);
33763b9b51feSCy Schubert 		/*
33773b9b51feSCy Schubert 		 * TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr,
33783b9b51feSCy Schubert 		 * nat_ndport, hv1
33793b9b51feSCy Schubert 		 */
33803b9b51feSCy Schubert 	} else {
33813b9b51feSCy Schubert 		hv0 = NAT_HASH_FN(nat->nat_osrcaddr, 0, 0xffffffff);
33823b9b51feSCy Schubert 		hv0 = NAT_HASH_FN(nat->nat_odstaddr, hv0, 0xffffffff);
33833b9b51feSCy Schubert 		/* TRACE nat_osrcaddr, nat_odstaddr, hv0 */
33843b9b51feSCy Schubert 
33853b9b51feSCy Schubert 		hv1 = NAT_HASH_FN(nat->nat_nsrcaddr, 0, 0xffffffff);
33863b9b51feSCy Schubert 		hv1 = NAT_HASH_FN(nat->nat_ndstaddr, hv1, 0xffffffff);
33873b9b51feSCy Schubert 		/* TRACE nat_nsrcaddr, nat_ndstaddr, hv1 */
33883b9b51feSCy Schubert 	}
33893b9b51feSCy Schubert 
33903b9b51feSCy Schubert 	nat->nat_hv[0] = hv0;
33913b9b51feSCy Schubert 	nat->nat_hv[1] = hv1;
33923b9b51feSCy Schubert 
33933b9b51feSCy Schubert 	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
33943b9b51feSCy Schubert 
33953b9b51feSCy Schubert 	in = nat->nat_ptr;
33963b9b51feSCy Schubert 	nat->nat_ref = nat->nat_me ? 2 : 1;
33973b9b51feSCy Schubert 
33983b9b51feSCy Schubert 	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
33993b9b51feSCy Schubert 	nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0], 4);
34003b9b51feSCy Schubert 
34013b9b51feSCy Schubert 	if (nat->nat_ifnames[1][0] != '\0') {
34023b9b51feSCy Schubert 		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
34033b9b51feSCy Schubert 		nat->nat_ifps[1] = ipf_resolvenic(softc,
34043b9b51feSCy Schubert 						  nat->nat_ifnames[1], 4);
34053b9b51feSCy Schubert 	} else if (in->in_ifnames[1] != -1) {
34063b9b51feSCy Schubert 		char *name;
34073b9b51feSCy Schubert 
34083b9b51feSCy Schubert 		name = in->in_names + in->in_ifnames[1];
34093b9b51feSCy Schubert 		if (name[1] != '\0' && name[0] != '-' && name[0] != '*') {
34103b9b51feSCy Schubert 			(void) strncpy(nat->nat_ifnames[1],
34113b9b51feSCy Schubert 				       nat->nat_ifnames[0], LIFNAMSIZ);
34123b9b51feSCy Schubert 			nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
34133b9b51feSCy Schubert 			nat->nat_ifps[1] = nat->nat_ifps[0];
34143b9b51feSCy Schubert 		}
34153b9b51feSCy Schubert 	}
34163b9b51feSCy Schubert 	if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
34173b9b51feSCy Schubert 		nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
34183b9b51feSCy Schubert 	}
34193b9b51feSCy Schubert 	if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
34203b9b51feSCy Schubert 		nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
34213b9b51feSCy Schubert 	}
34223b9b51feSCy Schubert 
34233b9b51feSCy Schubert 	ret = ipf_nat_hashtab_add(softc, softn, nat);
34243b9b51feSCy Schubert 	if (ret == -1)
34253b9b51feSCy Schubert 		MUTEX_DESTROY(&nat->nat_lock);
34268c82b374SCy Schubert 	return (ret);
34273b9b51feSCy Schubert }
34283b9b51feSCy Schubert 
34293b9b51feSCy Schubert 
34303b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
34313b9b51feSCy Schubert /* Function:    ipf_nat_hashtab_add                                         */
34323b9b51feSCy Schubert /* Returns:     int - 0 == sucess, -1 == failure                            */
34333b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
34343b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
34353b9b51feSCy Schubert /*              nat(I) - pointer to NAT structure                           */
34363b9b51feSCy Schubert /*                                                                          */
34373b9b51feSCy Schubert /* Handle the insertion of a NAT entry into the table/list.                 */
34383b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
34393b9b51feSCy Schubert int
ipf_nat_hashtab_add(ipf_main_softc_t * softc,ipf_nat_softc_t * softn,nat_t * nat)3440064a5a95SCy Schubert ipf_nat_hashtab_add(ipf_main_softc_t *softc, ipf_nat_softc_t *softn,
3441064a5a95SCy Schubert 	nat_t *nat)
34423b9b51feSCy Schubert {
34433b9b51feSCy Schubert 	nat_t **natp;
34443b9b51feSCy Schubert 	u_int hv0;
34453b9b51feSCy Schubert 	u_int hv1;
34463b9b51feSCy Schubert 
34473b9b51feSCy Schubert 	if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) {
34483b9b51feSCy Schubert 		hv1 = nat->nat_hv[0] % softn->ipf_nat_table_sz;
34493b9b51feSCy Schubert 		hv0 = nat->nat_hv[1] % softn->ipf_nat_table_sz;
34503b9b51feSCy Schubert 	} else {
34513b9b51feSCy Schubert 		hv0 = nat->nat_hv[0] % softn->ipf_nat_table_sz;
34523b9b51feSCy Schubert 		hv1 = nat->nat_hv[1] % softn->ipf_nat_table_sz;
34533b9b51feSCy Schubert 	}
34543b9b51feSCy Schubert 
34553b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0] >=
34563b9b51feSCy Schubert 	    softn->ipf_nat_maxbucket) {
34573b9b51feSCy Schubert 		DT1(ns_bucket_max_0, int,
34583b9b51feSCy Schubert 		    softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]);
34593b9b51feSCy Schubert 		NBUMPSIDE(0, ns_bucket_max);
34608c82b374SCy Schubert 		return (-1);
34613b9b51feSCy Schubert 	}
34623b9b51feSCy Schubert 
34633b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1] >=
34643b9b51feSCy Schubert 	    softn->ipf_nat_maxbucket) {
34653b9b51feSCy Schubert 		DT1(ns_bucket_max_1, int,
34663b9b51feSCy Schubert 		    softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]);
34673b9b51feSCy Schubert 		NBUMPSIDE(1, ns_bucket_max);
34688c82b374SCy Schubert 		return (-1);
34693b9b51feSCy Schubert 	}
34703b9b51feSCy Schubert 
34713b9b51feSCy Schubert 	/*
34723b9b51feSCy Schubert 	 * The ordering of operations in the list and hash table insertion
34733b9b51feSCy Schubert 	 * is very important.  The last operation for each task should be
34743b9b51feSCy Schubert 	 * to update the top of the list, after all the "nexts" have been
34753b9b51feSCy Schubert 	 * done so that walking the list while it is being done does not
34763b9b51feSCy Schubert 	 * find strange pointers.
34773b9b51feSCy Schubert 	 *
34783b9b51feSCy Schubert 	 * Global list of NAT instances
34793b9b51feSCy Schubert 	 */
34803b9b51feSCy Schubert 	nat->nat_next = softn->ipf_nat_instances;
34813b9b51feSCy Schubert 	nat->nat_pnext = &softn->ipf_nat_instances;
34823b9b51feSCy Schubert 	if (softn->ipf_nat_instances)
34833b9b51feSCy Schubert 		softn->ipf_nat_instances->nat_pnext = &nat->nat_next;
34843b9b51feSCy Schubert 	softn->ipf_nat_instances = nat;
34853b9b51feSCy Schubert 
34863b9b51feSCy Schubert 	/*
34873b9b51feSCy Schubert 	 * Inbound hash table.
34883b9b51feSCy Schubert 	 */
34893b9b51feSCy Schubert 	natp = &softn->ipf_nat_table[0][hv0];
34903b9b51feSCy Schubert 	nat->nat_phnext[0] = natp;
34913b9b51feSCy Schubert 	nat->nat_hnext[0] = *natp;
34923b9b51feSCy Schubert 	if (*natp) {
34933b9b51feSCy Schubert 		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
34943b9b51feSCy Schubert 	} else {
34953b9b51feSCy Schubert 		NBUMPSIDE(0, ns_inuse);
34963b9b51feSCy Schubert 	}
34973b9b51feSCy Schubert 	*natp = nat;
34983b9b51feSCy Schubert 	NBUMPSIDE(0, ns_bucketlen[hv0]);
34993b9b51feSCy Schubert 
35003b9b51feSCy Schubert 	/*
35013b9b51feSCy Schubert 	 * Outbound hash table.
35023b9b51feSCy Schubert 	 */
35033b9b51feSCy Schubert 	natp = &softn->ipf_nat_table[1][hv1];
35043b9b51feSCy Schubert 	nat->nat_phnext[1] = natp;
35053b9b51feSCy Schubert 	nat->nat_hnext[1] = *natp;
35063b9b51feSCy Schubert 	if (*natp)
35073b9b51feSCy Schubert 		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
35083b9b51feSCy Schubert 	else {
35093b9b51feSCy Schubert 		NBUMPSIDE(1, ns_inuse);
35103b9b51feSCy Schubert 	}
35113b9b51feSCy Schubert 	*natp = nat;
35123b9b51feSCy Schubert 	NBUMPSIDE(1, ns_bucketlen[hv1]);
35133b9b51feSCy Schubert 
35143b9b51feSCy Schubert 	ipf_nat_setqueue(softc, softn, nat);
35153b9b51feSCy Schubert 
35163b9b51feSCy Schubert 	if (nat->nat_dir & NAT_OUTBOUND) {
35173b9b51feSCy Schubert 		NBUMPSIDE(1, ns_added);
35183b9b51feSCy Schubert 	} else {
35193b9b51feSCy Schubert 		NBUMPSIDE(0, ns_added);
35203b9b51feSCy Schubert 	}
35213b9b51feSCy Schubert 	softn->ipf_nat_stats.ns_active++;
35228c82b374SCy Schubert 	return (0);
35233b9b51feSCy Schubert }
35243b9b51feSCy Schubert 
35253b9b51feSCy Schubert 
35263b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
35273b9b51feSCy Schubert /* Function:    ipf_nat_icmperrorlookup                                     */
35283b9b51feSCy Schubert /* Returns:     nat_t* - point to matching NAT structure                    */
35293b9b51feSCy Schubert /* Parameters:  fin(I) - pointer to packet information                      */
35303b9b51feSCy Schubert /*              dir(I) - direction of packet (in/out)                       */
35313b9b51feSCy Schubert /*                                                                          */
35323b9b51feSCy Schubert /* Check if the ICMP error message is related to an existing TCP, UDP or    */
35333b9b51feSCy Schubert /* ICMP query nat entry.  It is assumed that the packet is already of the   */
35343b9b51feSCy Schubert /* the required length.                                                     */
35353b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
35363b9b51feSCy Schubert nat_t *
ipf_nat_icmperrorlookup(fr_info_t * fin,int dir)3537064a5a95SCy Schubert ipf_nat_icmperrorlookup(fr_info_t *fin, int dir)
35383b9b51feSCy Schubert {
35393b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
35403b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
35413b9b51feSCy Schubert 	int flags = 0, type, minlen;
35423b9b51feSCy Schubert 	icmphdr_t *icmp, *orgicmp;
35433b9b51feSCy Schubert 	nat_stat_side_t *nside;
35443b9b51feSCy Schubert 	tcphdr_t *tcp = NULL;
35453b9b51feSCy Schubert 	u_short data[2];
35463b9b51feSCy Schubert 	nat_t *nat;
35473b9b51feSCy Schubert 	ip_t *oip;
35483b9b51feSCy Schubert 	u_int p;
35493b9b51feSCy Schubert 
35503b9b51feSCy Schubert 	icmp = fin->fin_dp;
35513b9b51feSCy Schubert 	type = icmp->icmp_type;
35523b9b51feSCy Schubert 	nside = &softn->ipf_nat_stats.ns_side[fin->fin_out];
35533b9b51feSCy Schubert 	/*
35543b9b51feSCy Schubert 	 * Does it at least have the return (basic) IP header ?
35553b9b51feSCy Schubert 	 * Only a basic IP header (no options) should be with an ICMP error
35563b9b51feSCy Schubert 	 * header.  Also, if it's not an error type, then return.
35573b9b51feSCy Schubert 	 */
35583b9b51feSCy Schubert 	if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR)) {
35593b9b51feSCy Schubert 		ATOMIC_INCL(nside->ns_icmp_basic);
35608c82b374SCy Schubert 		return (NULL);
35613b9b51feSCy Schubert 	}
35623b9b51feSCy Schubert 
35633b9b51feSCy Schubert 	/*
35643b9b51feSCy Schubert 	 * Check packet size
35653b9b51feSCy Schubert 	 */
35663b9b51feSCy Schubert 	oip = (ip_t *)((char *)fin->fin_dp + 8);
35673b9b51feSCy Schubert 	minlen = IP_HL(oip) << 2;
35683b9b51feSCy Schubert 	if ((minlen < sizeof(ip_t)) ||
35693b9b51feSCy Schubert 	    (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)) {
35703b9b51feSCy Schubert 		ATOMIC_INCL(nside->ns_icmp_size);
35718c82b374SCy Schubert 		return (NULL);
35723b9b51feSCy Schubert 	}
35733b9b51feSCy Schubert 
35743b9b51feSCy Schubert 	/*
35753b9b51feSCy Schubert 	 * Is the buffer big enough for all of it ?  It's the size of the IP
35763b9b51feSCy Schubert 	 * header claimed in the encapsulated part which is of concern.  It
35773b9b51feSCy Schubert 	 * may be too big to be in this buffer but not so big that it's
35783b9b51feSCy Schubert 	 * outside the ICMP packet, leading to TCP deref's causing problems.
35793b9b51feSCy Schubert 	 * This is possible because we don't know how big oip_hl is when we
35803b9b51feSCy Schubert 	 * do the pullup early in ipf_check() and thus can't gaurantee it is
35813b9b51feSCy Schubert 	 * all here now.
35823b9b51feSCy Schubert 	 */
35833b9b51feSCy Schubert #ifdef  ipf_nat_KERNEL
35843b9b51feSCy Schubert 	{
35853b9b51feSCy Schubert 	mb_t *m;
35863b9b51feSCy Schubert 
35873b9b51feSCy Schubert 	m = fin->fin_m;
35883b9b51feSCy Schubert # if SOLARIS
35893b9b51feSCy Schubert 	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
35903b9b51feSCy Schubert 	    (char *)m->b_wptr) {
35913b9b51feSCy Schubert 		ATOMIC_INCL(nside->ns_icmp_mbuf);
35928c82b374SCy Schubert 		return (NULL);
35933b9b51feSCy Schubert 	}
35943b9b51feSCy Schubert # else
35953b9b51feSCy Schubert 	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
35963b9b51feSCy Schubert 	    (char *)fin->fin_ip + M_LEN(m)) {
35973b9b51feSCy Schubert 		ATOMIC_INCL(nside->ns_icmp_mbuf);
35988c82b374SCy Schubert 		return (NULL);
35993b9b51feSCy Schubert 	}
36003b9b51feSCy Schubert # endif
36013b9b51feSCy Schubert 	}
36023b9b51feSCy Schubert #endif
36033b9b51feSCy Schubert 
36043b9b51feSCy Schubert 	if (fin->fin_daddr != oip->ip_src.s_addr) {
36053b9b51feSCy Schubert 		ATOMIC_INCL(nside->ns_icmp_address);
36068c82b374SCy Schubert 		return (NULL);
36073b9b51feSCy Schubert 	}
36083b9b51feSCy Schubert 
36093b9b51feSCy Schubert 	p = oip->ip_p;
36103b9b51feSCy Schubert 	if (p == IPPROTO_TCP)
36113b9b51feSCy Schubert 		flags = IPN_TCP;
36123b9b51feSCy Schubert 	else if (p == IPPROTO_UDP)
36133b9b51feSCy Schubert 		flags = IPN_UDP;
36143b9b51feSCy Schubert 	else if (p == IPPROTO_ICMP) {
36153b9b51feSCy Schubert 		orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2));
36163b9b51feSCy Schubert 
36173b9b51feSCy Schubert 		/* see if this is related to an ICMP query */
36183b9b51feSCy Schubert 		if (ipf_nat_icmpquerytype(orgicmp->icmp_type)) {
36193b9b51feSCy Schubert 			data[0] = fin->fin_data[0];
36203b9b51feSCy Schubert 			data[1] = fin->fin_data[1];
36213b9b51feSCy Schubert 			fin->fin_data[0] = 0;
36223b9b51feSCy Schubert 			fin->fin_data[1] = orgicmp->icmp_id;
36233b9b51feSCy Schubert 
36243b9b51feSCy Schubert 			flags = IPN_ICMPERR|IPN_ICMPQUERY;
36253b9b51feSCy Schubert 			/*
36263b9b51feSCy Schubert 			 * NOTE : dir refers to the direction of the original
36273b9b51feSCy Schubert 			 *        ip packet. By definition the icmp error
36283b9b51feSCy Schubert 			 *        message flows in the opposite direction.
36293b9b51feSCy Schubert 			 */
36303b9b51feSCy Schubert 			if (dir == NAT_INBOUND)
36313b9b51feSCy Schubert 				nat = ipf_nat_inlookup(fin, flags, p,
36323b9b51feSCy Schubert 						       oip->ip_dst,
36333b9b51feSCy Schubert 						       oip->ip_src);
36343b9b51feSCy Schubert 			else
36353b9b51feSCy Schubert 				nat = ipf_nat_outlookup(fin, flags, p,
36363b9b51feSCy Schubert 							oip->ip_dst,
36373b9b51feSCy Schubert 							oip->ip_src);
36383b9b51feSCy Schubert 			fin->fin_data[0] = data[0];
36393b9b51feSCy Schubert 			fin->fin_data[1] = data[1];
36408c82b374SCy Schubert 			return (nat);
36413b9b51feSCy Schubert 		}
36423b9b51feSCy Schubert 	}
36433b9b51feSCy Schubert 
36443b9b51feSCy Schubert 	if (flags & IPN_TCPUDP) {
36453b9b51feSCy Schubert 		minlen += 8;		/* + 64bits of data to get ports */
36463b9b51feSCy Schubert 		/* TRACE (fin,minlen) */
36473b9b51feSCy Schubert 		if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) {
36483b9b51feSCy Schubert 			ATOMIC_INCL(nside->ns_icmp_short);
36498c82b374SCy Schubert 			return (NULL);
36503b9b51feSCy Schubert 		}
36513b9b51feSCy Schubert 
36523b9b51feSCy Schubert 		data[0] = fin->fin_data[0];
36533b9b51feSCy Schubert 		data[1] = fin->fin_data[1];
36543b9b51feSCy Schubert 		tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2));
36553b9b51feSCy Schubert 		fin->fin_data[0] = ntohs(tcp->th_dport);
36563b9b51feSCy Schubert 		fin->fin_data[1] = ntohs(tcp->th_sport);
36573b9b51feSCy Schubert 
36583b9b51feSCy Schubert 		if (dir == NAT_INBOUND) {
36593b9b51feSCy Schubert 			nat = ipf_nat_inlookup(fin, flags, p, oip->ip_dst,
36603b9b51feSCy Schubert 					       oip->ip_src);
36613b9b51feSCy Schubert 		} else {
36623b9b51feSCy Schubert 			nat = ipf_nat_outlookup(fin, flags, p, oip->ip_dst,
36633b9b51feSCy Schubert 					    oip->ip_src);
36643b9b51feSCy Schubert 		}
36653b9b51feSCy Schubert 		fin->fin_data[0] = data[0];
36663b9b51feSCy Schubert 		fin->fin_data[1] = data[1];
36678c82b374SCy Schubert 		return (nat);
36683b9b51feSCy Schubert 	}
36693b9b51feSCy Schubert 	if (dir == NAT_INBOUND)
36703b9b51feSCy Schubert 		nat = ipf_nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
36713b9b51feSCy Schubert 	else
36723b9b51feSCy Schubert 		nat = ipf_nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
36733b9b51feSCy Schubert 
36748c82b374SCy Schubert 	return (nat);
36753b9b51feSCy Schubert }
36763b9b51feSCy Schubert 
36773b9b51feSCy Schubert 
36783b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
36793b9b51feSCy Schubert /* Function:    ipf_nat_icmperror                                           */
36803b9b51feSCy Schubert /* Returns:     nat_t* - point to matching NAT structure                    */
36813b9b51feSCy Schubert /* Parameters:  fin(I)    - pointer to packet information                   */
36823b9b51feSCy Schubert /*              nflags(I) - NAT flags for this packet                       */
36833b9b51feSCy Schubert /*              dir(I)    - direction of packet (in/out)                    */
36843b9b51feSCy Schubert /*                                                                          */
36853b9b51feSCy Schubert /* Fix up an ICMP packet which is an error message for an existing NAT      */
36863b9b51feSCy Schubert /* session.  This will correct both packet header data and checksums.       */
36873b9b51feSCy Schubert /*                                                                          */
36883b9b51feSCy Schubert /* This should *ONLY* be used for incoming ICMP error packets to make sure  */
36893b9b51feSCy Schubert /* a NAT'd ICMP packet gets correctly recognised.                           */
36903b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
36913b9b51feSCy Schubert nat_t *
ipf_nat_icmperror(fr_info_t * fin,u_int * nflags,int dir)3692064a5a95SCy Schubert ipf_nat_icmperror(fr_info_t *fin, u_int *nflags, int dir)
36933b9b51feSCy Schubert {
36943b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
36953b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
36963b9b51feSCy Schubert 	u_32_t sum1, sum2, sumd, sumd2;
36973b9b51feSCy Schubert 	struct in_addr a1, a2, a3, a4;
36983b9b51feSCy Schubert 	int flags, dlen, odst;
36993b9b51feSCy Schubert 	icmphdr_t *icmp;
37003b9b51feSCy Schubert 	u_short *csump;
37013b9b51feSCy Schubert 	tcphdr_t *tcp;
37023b9b51feSCy Schubert 	nat_t *nat;
37033b9b51feSCy Schubert 	ip_t *oip;
37043b9b51feSCy Schubert 	void *dp;
37053b9b51feSCy Schubert 
37063b9b51feSCy Schubert 	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
37073b9b51feSCy Schubert 		NBUMPSIDED(fin->fin_out, ns_icmp_short);
37088c82b374SCy Schubert 		return (NULL);
37093b9b51feSCy Schubert 	}
37103b9b51feSCy Schubert 
37113b9b51feSCy Schubert 	/*
37123b9b51feSCy Schubert 	* ipf_nat_icmperrorlookup() will return NULL for `defective' packets.
37133b9b51feSCy Schubert 	 */
37143b9b51feSCy Schubert 	if ((fin->fin_v != 4) || !(nat = ipf_nat_icmperrorlookup(fin, dir))) {
37153b9b51feSCy Schubert 		NBUMPSIDED(fin->fin_out, ns_icmp_notfound);
37168c82b374SCy Schubert 		return (NULL);
37173b9b51feSCy Schubert 	}
37183b9b51feSCy Schubert 
37193b9b51feSCy Schubert 	tcp = NULL;
37203b9b51feSCy Schubert 	csump = NULL;
37213b9b51feSCy Schubert 	flags = 0;
37223b9b51feSCy Schubert 	sumd2 = 0;
37233b9b51feSCy Schubert 	*nflags = IPN_ICMPERR;
37243b9b51feSCy Schubert 	icmp = fin->fin_dp;
37253b9b51feSCy Schubert 	oip = (ip_t *)&icmp->icmp_ip;
37263b9b51feSCy Schubert 	dp = (((char *)oip) + (IP_HL(oip) << 2));
37273b9b51feSCy Schubert 	if (oip->ip_p == IPPROTO_TCP) {
37283b9b51feSCy Schubert 		tcp = (tcphdr_t *)dp;
37293b9b51feSCy Schubert 		csump = (u_short *)&tcp->th_sum;
37303b9b51feSCy Schubert 		flags = IPN_TCP;
37313b9b51feSCy Schubert 	} else if (oip->ip_p == IPPROTO_UDP) {
37323b9b51feSCy Schubert 		udphdr_t *udp;
37333b9b51feSCy Schubert 
37343b9b51feSCy Schubert 		udp = (udphdr_t *)dp;
37353b9b51feSCy Schubert 		tcp = (tcphdr_t *)dp;
37363b9b51feSCy Schubert 		csump = (u_short *)&udp->uh_sum;
37373b9b51feSCy Schubert 		flags = IPN_UDP;
37383b9b51feSCy Schubert 	} else if (oip->ip_p == IPPROTO_ICMP)
37393b9b51feSCy Schubert 		flags = IPN_ICMPQUERY;
37403b9b51feSCy Schubert 	dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip);
37413b9b51feSCy Schubert 
37423b9b51feSCy Schubert 	/*
37433b9b51feSCy Schubert 	 * Need to adjust ICMP header to include the real IP#'s and
37443b9b51feSCy Schubert 	 * port #'s.  Only apply a checksum change relative to the
37453b9b51feSCy Schubert 	 * IP address change as it will be modified again in ipf_nat_checkout
37463b9b51feSCy Schubert 	 * for both address and port.  Two checksum changes are
37473b9b51feSCy Schubert 	 * necessary for the two header address changes.  Be careful
37483b9b51feSCy Schubert 	 * to only modify the checksum once for the port # and twice
37493b9b51feSCy Schubert 	 * for the IP#.
37503b9b51feSCy Schubert 	 */
37513b9b51feSCy Schubert 
37523b9b51feSCy Schubert 	/*
37533b9b51feSCy Schubert 	 * Step 1
37543b9b51feSCy Schubert 	 * Fix the IP addresses in the offending IP packet. You also need
37553b9b51feSCy Schubert 	 * to adjust the IP header checksum of that offending IP packet.
37563b9b51feSCy Schubert 	 *
37573b9b51feSCy Schubert 	 * Normally, you would expect that the ICMP checksum of the
37583b9b51feSCy Schubert 	 * ICMP error message needs to be adjusted as well for the
37593b9b51feSCy Schubert 	 * IP address change in oip.
37603b9b51feSCy Schubert 	 * However, this is a NOP, because the ICMP checksum is
37613b9b51feSCy Schubert 	 * calculated over the complete ICMP packet, which includes the
37623b9b51feSCy Schubert 	 * changed oip IP addresses and oip->ip_sum. However, these
37633b9b51feSCy Schubert 	 * two changes cancel each other out (if the delta for
37643b9b51feSCy Schubert 	 * the IP address is x, then the delta for ip_sum is minus x),
37653b9b51feSCy Schubert 	 * so no change in the icmp_cksum is necessary.
37663b9b51feSCy Schubert 	 *
37673b9b51feSCy Schubert 	 * Inbound ICMP
37683b9b51feSCy Schubert 	 * ------------
37693b9b51feSCy Schubert 	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
37703b9b51feSCy Schubert 	 * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b)
37713b9b51feSCy Schubert 	 * - OIP_SRC(c)=nat_newsrcip,          OIP_DST(b)=nat_newdstip
37723b9b51feSCy Schubert 	 *=> OIP_SRC(c)=nat_oldsrcip,          OIP_DST(b)=nat_olddstip
37733b9b51feSCy Schubert 	 *
37743b9b51feSCy Schubert 	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
37753b9b51feSCy Schubert 	 * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a)
37763b9b51feSCy Schubert 	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
37773b9b51feSCy Schubert 	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
37783b9b51feSCy Schubert 	 *
37793b9b51feSCy Schubert 	 * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
37803b9b51feSCy Schubert 	 * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d)
37813b9b51feSCy Schubert 	 * - OIP_SRC(c)=nat_newsrcip,          OIP_DST(d)=nat_newdstip
37823b9b51feSCy Schubert 	 *=> OIP_SRC(c)=nat_oldsrcip,          OIP_DST(d)=nat_olddstip
37833b9b51feSCy Schubert 	 *
37843b9b51feSCy Schubert 	 * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
37853b9b51feSCy Schubert 	 * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
37863b9b51feSCy Schubert 	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
37873b9b51feSCy Schubert 	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
37883b9b51feSCy Schubert 	 *
37893b9b51feSCy Schubert 	 * Outbound ICMP
37903b9b51feSCy Schubert 	 * -------------
37913b9b51feSCy Schubert 	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
37923b9b51feSCy Schubert 	 * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
37933b9b51feSCy Schubert 	 * - OIP_SRC(b)=nat_olddstip,          OIP_DST(a)=nat_oldsrcip
37943b9b51feSCy Schubert 	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
37953b9b51feSCy Schubert 	 *
37963b9b51feSCy Schubert 	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
37973b9b51feSCy Schubert 	 * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c)
37983b9b51feSCy Schubert 	 * - OIP_SRC(a)=nat_newsrcip,          OIP_DST(c)=nat_newdstip
37993b9b51feSCy Schubert 	 *=> OIP_SRC(a)=nat_oldsrcip,          OIP_DST(c)=nat_olddstip
38003b9b51feSCy Schubert 	 *
38013b9b51feSCy Schubert 	 * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
38023b9b51feSCy Schubert 	 * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d)
38033b9b51feSCy Schubert 	 * - OIP_SRC(c)=nat_olddstip,          OIP_DST(d)=nat_oldsrcip
38043b9b51feSCy Schubert 	 *=> OIP_SRC(b)=nat_newdstip,          OIP_DST(a)=nat_newsrcip
38053b9b51feSCy Schubert 	 *
38063b9b51feSCy Schubert 	 * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
38073b9b51feSCy Schubert 	 * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a)
38083b9b51feSCy Schubert 	 * - OIP_SRC(b)=nat_newsrcip,          OIP_DST(a)=nat_newdstip
38093b9b51feSCy Schubert 	 *=> OIP_SRC(a)=nat_oldsrcip,          OIP_DST(c)=nat_olddstip
38103b9b51feSCy Schubert 	 */
38113b9b51feSCy Schubert 
38123b9b51feSCy Schubert 	if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) ||
38133b9b51feSCy Schubert 	    ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) {
38143b9b51feSCy Schubert 		a1.s_addr = ntohl(nat->nat_osrcaddr);
38153b9b51feSCy Schubert 		a4.s_addr = ntohl(oip->ip_src.s_addr);
38163b9b51feSCy Schubert 		a3.s_addr = ntohl(nat->nat_odstaddr);
38173b9b51feSCy Schubert 		a2.s_addr = ntohl(oip->ip_dst.s_addr);
38183b9b51feSCy Schubert 		oip->ip_src.s_addr = htonl(a1.s_addr);
38193b9b51feSCy Schubert 		oip->ip_dst.s_addr = htonl(a3.s_addr);
38203b9b51feSCy Schubert 		odst = 1;
38213b9b51feSCy Schubert 	} else {
38223b9b51feSCy Schubert 		a1.s_addr = ntohl(nat->nat_ndstaddr);
38233b9b51feSCy Schubert 		a2.s_addr = ntohl(oip->ip_dst.s_addr);
38243b9b51feSCy Schubert 		a3.s_addr = ntohl(nat->nat_nsrcaddr);
38253b9b51feSCy Schubert 		a4.s_addr = ntohl(oip->ip_src.s_addr);
38263b9b51feSCy Schubert 		oip->ip_dst.s_addr = htonl(a3.s_addr);
38273b9b51feSCy Schubert 		oip->ip_src.s_addr = htonl(a1.s_addr);
38283b9b51feSCy Schubert 		odst = 0;
38293b9b51feSCy Schubert 	}
38303b9b51feSCy Schubert 	sum1 = 0;
38313b9b51feSCy Schubert 	sum2 = 0;
38323b9b51feSCy Schubert 	sumd = 0;
38333b9b51feSCy Schubert 	CALC_SUMD(a2.s_addr, a3.s_addr, sum1);
38343b9b51feSCy Schubert 	CALC_SUMD(a4.s_addr, a1.s_addr, sum2);
38353b9b51feSCy Schubert 	sumd = sum2 + sum1;
38363b9b51feSCy Schubert 	if (sumd != 0)
38373b9b51feSCy Schubert 		ipf_fix_datacksum(&oip->ip_sum, sumd);
38383b9b51feSCy Schubert 
38393b9b51feSCy Schubert 	sumd2 = sumd;
38403b9b51feSCy Schubert 	sum1 = 0;
38413b9b51feSCy Schubert 	sum2 = 0;
38423b9b51feSCy Schubert 
38433b9b51feSCy Schubert 	/*
38443b9b51feSCy Schubert 	 * Fix UDP pseudo header checksum to compensate for the
38453b9b51feSCy Schubert 	 * IP address change.
38463b9b51feSCy Schubert 	 */
38473b9b51feSCy Schubert 	if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) {
38483b9b51feSCy Schubert 		u_32_t sum3, sum4, sumt;
38493b9b51feSCy Schubert 
38503b9b51feSCy Schubert 		/*
38513b9b51feSCy Schubert 		 * Step 2 :
38523b9b51feSCy Schubert 		 * For offending TCP/UDP IP packets, translate the ports as
38533b9b51feSCy Schubert 		 * well, based on the NAT specification. Of course such
38543b9b51feSCy Schubert 		 * a change may be reflected in the ICMP checksum as well.
38553b9b51feSCy Schubert 		 *
38563b9b51feSCy Schubert 		 * Since the port fields are part of the TCP/UDP checksum
38573b9b51feSCy Schubert 		 * of the offending IP packet, you need to adjust that checksum
38583b9b51feSCy Schubert 		 * as well... except that the change in the port numbers should
38593b9b51feSCy Schubert 		 * be offset by the checksum change.  However, the TCP/UDP
38603b9b51feSCy Schubert 		 * checksum will also need to change if there has been an
38613b9b51feSCy Schubert 		 * IP address change.
38623b9b51feSCy Schubert 		 */
38633b9b51feSCy Schubert 		if (odst == 1) {
38643b9b51feSCy Schubert 			sum1 = ntohs(nat->nat_osport);
38653b9b51feSCy Schubert 			sum4 = ntohs(tcp->th_sport);
38663b9b51feSCy Schubert 			sum3 = ntohs(nat->nat_odport);
38673b9b51feSCy Schubert 			sum2 = ntohs(tcp->th_dport);
38683b9b51feSCy Schubert 
38693b9b51feSCy Schubert 			tcp->th_sport = htons(sum1);
38703b9b51feSCy Schubert 			tcp->th_dport = htons(sum3);
38713b9b51feSCy Schubert 		} else {
38723b9b51feSCy Schubert 			sum1 = ntohs(nat->nat_ndport);
38733b9b51feSCy Schubert 			sum2 = ntohs(tcp->th_dport);
38743b9b51feSCy Schubert 			sum3 = ntohs(nat->nat_nsport);
38753b9b51feSCy Schubert 			sum4 = ntohs(tcp->th_sport);
38763b9b51feSCy Schubert 
38773b9b51feSCy Schubert 			tcp->th_dport = htons(sum3);
38783b9b51feSCy Schubert 			tcp->th_sport = htons(sum1);
38793b9b51feSCy Schubert 		}
38803b9b51feSCy Schubert 		CALC_SUMD(sum4, sum1, sumt);
38813b9b51feSCy Schubert 		sumd += sumt;
38823b9b51feSCy Schubert 		CALC_SUMD(sum2, sum3, sumt);
38833b9b51feSCy Schubert 		sumd += sumt;
38843b9b51feSCy Schubert 
38853b9b51feSCy Schubert 		if (sumd != 0 || sumd2 != 0) {
38863b9b51feSCy Schubert 			/*
38873b9b51feSCy Schubert 			 * At this point, sumd is the delta to apply to the
38883b9b51feSCy Schubert 			 * TCP/UDP header, given the changes in both the IP
38893b9b51feSCy Schubert 			 * address and the ports and sumd2 is the delta to
38903b9b51feSCy Schubert 			 * apply to the ICMP header, given the IP address
38913b9b51feSCy Schubert 			 * change delta that may need to be applied to the
38923b9b51feSCy Schubert 			 * TCP/UDP checksum instead.
38933b9b51feSCy Schubert 			 *
38943b9b51feSCy Schubert 			 * If we will both the IP and TCP/UDP checksums
38953b9b51feSCy Schubert 			 * then the ICMP checksum changes by the address
38963b9b51feSCy Schubert 			 * delta applied to the TCP/UDP checksum.  If we
38973b9b51feSCy Schubert 			 * do not change the TCP/UDP checksum them we
38983b9b51feSCy Schubert 			 * apply the delta in ports to the ICMP checksum.
38993b9b51feSCy Schubert 			 */
39003b9b51feSCy Schubert 			if (oip->ip_p == IPPROTO_UDP) {
39013b9b51feSCy Schubert 				if ((dlen >= 8) && (*csump != 0)) {
39023b9b51feSCy Schubert 					ipf_fix_datacksum(csump, sumd);
39033b9b51feSCy Schubert 				} else {
39043b9b51feSCy Schubert 					CALC_SUMD(sum1, sum4, sumd2);
39053b9b51feSCy Schubert 					CALC_SUMD(sum3, sum2, sumt);
39063b9b51feSCy Schubert 					sumd2 += sumt;
39073b9b51feSCy Schubert 				}
39083b9b51feSCy Schubert 			} else if (oip->ip_p == IPPROTO_TCP) {
39093b9b51feSCy Schubert 				if (dlen >= 18) {
39103b9b51feSCy Schubert 					ipf_fix_datacksum(csump, sumd);
39113b9b51feSCy Schubert 				} else {
39123b9b51feSCy Schubert 					CALC_SUMD(sum1, sum4, sumd2);
39133b9b51feSCy Schubert 					CALC_SUMD(sum3, sum2, sumt);
39143b9b51feSCy Schubert 					sumd2 += sumt;
39153b9b51feSCy Schubert 				}
39163b9b51feSCy Schubert 			}
39173b9b51feSCy Schubert 			if (sumd2 != 0) {
39183b9b51feSCy Schubert 				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
39193b9b51feSCy Schubert 				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
39203b9b51feSCy Schubert 				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
39213b9b51feSCy Schubert 				ipf_fix_incksum(0, &icmp->icmp_cksum, sumd2, 0);
39223b9b51feSCy Schubert 			}
39233b9b51feSCy Schubert 		}
39243b9b51feSCy Schubert 	} else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) {
39253b9b51feSCy Schubert 		icmphdr_t *orgicmp;
39263b9b51feSCy Schubert 
39273b9b51feSCy Schubert 		/*
39283b9b51feSCy Schubert 		 * XXX - what if this is bogus hl and we go off the end ?
39293b9b51feSCy Schubert 		 * In this case, ipf_nat_icmperrorlookup() will have
39303b9b51feSCy Schubert 		 * returned NULL.
39313b9b51feSCy Schubert 		 */
39323b9b51feSCy Schubert 		orgicmp = (icmphdr_t *)dp;
39333b9b51feSCy Schubert 
39343b9b51feSCy Schubert 		if (odst == 1) {
39353b9b51feSCy Schubert 			if (orgicmp->icmp_id != nat->nat_osport) {
39363b9b51feSCy Schubert 
39373b9b51feSCy Schubert 				/*
39383b9b51feSCy Schubert 				 * Fix ICMP checksum (of the offening ICMP
39393b9b51feSCy Schubert 				 * query packet) to compensate the change
39403b9b51feSCy Schubert 				 * in the ICMP id of the offending ICMP
39413b9b51feSCy Schubert 				 * packet.
39423b9b51feSCy Schubert 				 *
39433b9b51feSCy Schubert 				 * Since you modify orgicmp->icmp_id with
39443b9b51feSCy Schubert 				 * a delta (say x) and you compensate that
39453b9b51feSCy Schubert 				 * in origicmp->icmp_cksum with a delta
39463b9b51feSCy Schubert 				 * minus x, you don't have to adjust the
39473b9b51feSCy Schubert 				 * overall icmp->icmp_cksum
39483b9b51feSCy Schubert 				 */
39493b9b51feSCy Schubert 				sum1 = ntohs(orgicmp->icmp_id);
39503b9b51feSCy Schubert 				sum2 = ntohs(nat->nat_oicmpid);
39513b9b51feSCy Schubert 				CALC_SUMD(sum1, sum2, sumd);
39523b9b51feSCy Schubert 				orgicmp->icmp_id = nat->nat_oicmpid;
39533b9b51feSCy Schubert 				ipf_fix_datacksum(&orgicmp->icmp_cksum, sumd);
39543b9b51feSCy Schubert 			}
39553b9b51feSCy Schubert 		} /* nat_dir == NAT_INBOUND is impossible for icmp queries */
39563b9b51feSCy Schubert 	}
39578c82b374SCy Schubert 	return (nat);
39583b9b51feSCy Schubert }
39593b9b51feSCy Schubert 
39603b9b51feSCy Schubert 
39613b9b51feSCy Schubert /*
39623b9b51feSCy Schubert  *       MAP-IN    MAP-OUT   RDR-IN   RDR-OUT
39633b9b51feSCy Schubert  * osrc    X       == src    == src      X
39643b9b51feSCy Schubert  * odst    X       == dst    == dst      X
39653b9b51feSCy Schubert  * nsrc  == dst      X         X      == dst
39663b9b51feSCy Schubert  * ndst  == src      X         X      == src
39673b9b51feSCy Schubert  * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND
39683b9b51feSCy Schubert  */
39693b9b51feSCy Schubert /*
39703b9b51feSCy Schubert  * NB: these lookups don't lock access to the list, it assumed that it has
39713b9b51feSCy Schubert  * already been done!
39723b9b51feSCy Schubert  */
39733b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
39743b9b51feSCy Schubert /* Function:    ipf_nat_inlookup                                            */
39753b9b51feSCy Schubert /* Returns:     nat_t* - NULL == no match,                                  */
39763b9b51feSCy Schubert /*                       else pointer to matching NAT entry                 */
39773b9b51feSCy Schubert /* Parameters:  fin(I)    - pointer to packet information                   */
39783b9b51feSCy Schubert /*              flags(I)  - NAT flags for this packet                       */
39793b9b51feSCy Schubert /*              p(I)      - protocol for this packet                        */
39803b9b51feSCy Schubert /*              src(I)    - source IP address                               */
39813b9b51feSCy Schubert /*              mapdst(I) - destination IP address                          */
39823b9b51feSCy Schubert /*                                                                          */
39833b9b51feSCy Schubert /* Lookup a nat entry based on the mapped destination ip address/port and   */
39843b9b51feSCy Schubert /* real source address/port.  We use this lookup when receiving a packet,   */
39853b9b51feSCy Schubert /* we're looking for a table entry, based on the destination address.       */
39863b9b51feSCy Schubert /*                                                                          */
39873b9b51feSCy Schubert /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
39883b9b51feSCy Schubert /*                                                                          */
39893b9b51feSCy Schubert /* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
39903b9b51feSCy Schubert /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
39913b9b51feSCy Schubert /*                                                                          */
39923b9b51feSCy Schubert /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
39933b9b51feSCy Schubert /*            the packet is of said protocol                                */
39943b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
39953b9b51feSCy Schubert nat_t *
ipf_nat_inlookup(fr_info_t * fin,u_int flags,u_int p,struct in_addr src,struct in_addr mapdst)3996064a5a95SCy Schubert ipf_nat_inlookup(fr_info_t *fin, u_int flags, u_int p,
3997064a5a95SCy Schubert 	struct in_addr src , struct in_addr mapdst)
39983b9b51feSCy Schubert {
39993b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
40003b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
40013b9b51feSCy Schubert 	u_short sport, dport;
40023b9b51feSCy Schubert 	grehdr_t *gre;
40033b9b51feSCy Schubert 	ipnat_t *ipn;
40043b9b51feSCy Schubert 	u_int sflags;
40053b9b51feSCy Schubert 	nat_t *nat;
40063b9b51feSCy Schubert 	int nflags;
40073b9b51feSCy Schubert 	u_32_t dst;
40083b9b51feSCy Schubert 	void *ifp;
40093b9b51feSCy Schubert 	u_int hv, rhv;
40103b9b51feSCy Schubert 
40113b9b51feSCy Schubert 	ifp = fin->fin_ifp;
40123b9b51feSCy Schubert 	gre = NULL;
40133b9b51feSCy Schubert 	dst = mapdst.s_addr;
40143b9b51feSCy Schubert 	sflags = flags & NAT_TCPUDPICMP;
40153b9b51feSCy Schubert 
40163b9b51feSCy Schubert 	switch (p)
40173b9b51feSCy Schubert 	{
40183b9b51feSCy Schubert 	case IPPROTO_TCP :
40193b9b51feSCy Schubert 	case IPPROTO_UDP :
40203b9b51feSCy Schubert 		sport = htons(fin->fin_data[0]);
40213b9b51feSCy Schubert 		dport = htons(fin->fin_data[1]);
40223b9b51feSCy Schubert 		break;
40233b9b51feSCy Schubert 	case IPPROTO_ICMP :
40243b9b51feSCy Schubert 		sport = 0;
40253b9b51feSCy Schubert 		dport = fin->fin_data[1];
40263b9b51feSCy Schubert 		break;
40273b9b51feSCy Schubert 	default :
40283b9b51feSCy Schubert 		sport = 0;
40293b9b51feSCy Schubert 		dport = 0;
40303b9b51feSCy Schubert 		break;
40313b9b51feSCy Schubert 	}
40323b9b51feSCy Schubert 
40333b9b51feSCy Schubert 
40343b9b51feSCy Schubert 	if ((flags & SI_WILDP) != 0)
40353b9b51feSCy Schubert 		goto find_in_wild_ports;
40363b9b51feSCy Schubert 
40373b9b51feSCy Schubert 	rhv = NAT_HASH_FN(dst, dport, 0xffffffff);
40383b9b51feSCy Schubert 	rhv = NAT_HASH_FN(src.s_addr, rhv + sport, 0xffffffff);
40393b9b51feSCy Schubert 	hv = rhv % softn->ipf_nat_table_sz;
40403b9b51feSCy Schubert 	nat = softn->ipf_nat_table[1][hv];
40413b9b51feSCy Schubert 	/* TRACE dst, dport, src, sport, hv, nat */
40423b9b51feSCy Schubert 
40433b9b51feSCy Schubert 	for (; nat; nat = nat->nat_hnext[1]) {
40443b9b51feSCy Schubert 		if (nat->nat_ifps[0] != NULL) {
40453b9b51feSCy Schubert 			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
40463b9b51feSCy Schubert 				continue;
40473b9b51feSCy Schubert 		}
40483b9b51feSCy Schubert 
40493b9b51feSCy Schubert 		if (nat->nat_pr[0] != p)
40503b9b51feSCy Schubert 			continue;
40513b9b51feSCy Schubert 
40523b9b51feSCy Schubert 		switch (nat->nat_dir)
40533b9b51feSCy Schubert 		{
40543b9b51feSCy Schubert 		case NAT_INBOUND :
40553b9b51feSCy Schubert 		case NAT_DIVERTIN :
40563b9b51feSCy Schubert 			if (nat->nat_v[0] != 4)
40573b9b51feSCy Schubert 				continue;
40583b9b51feSCy Schubert 			if (nat->nat_osrcaddr != src.s_addr ||
40593b9b51feSCy Schubert 			    nat->nat_odstaddr != dst)
40603b9b51feSCy Schubert 				continue;
40613b9b51feSCy Schubert 			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
40623b9b51feSCy Schubert 				if (nat->nat_osport != sport)
40633b9b51feSCy Schubert 					continue;
40643b9b51feSCy Schubert 				if (nat->nat_odport != dport)
40653b9b51feSCy Schubert 					continue;
40663b9b51feSCy Schubert 
40673b9b51feSCy Schubert 			} else if (p == IPPROTO_ICMP) {
40683b9b51feSCy Schubert 				if (nat->nat_osport != dport) {
40693b9b51feSCy Schubert 					continue;
40703b9b51feSCy Schubert 				}
40713b9b51feSCy Schubert 			}
40723b9b51feSCy Schubert 			break;
40733b9b51feSCy Schubert 		case NAT_DIVERTOUT :
40743b9b51feSCy Schubert 			if (nat->nat_dlocal)
40753b9b51feSCy Schubert 				continue;
40763b9b51feSCy Schubert 		case NAT_OUTBOUND :
40773b9b51feSCy Schubert 			if (nat->nat_v[1] != 4)
40783b9b51feSCy Schubert 				continue;
40793b9b51feSCy Schubert 			if (nat->nat_dlocal)
40803b9b51feSCy Schubert 				continue;
40813b9b51feSCy Schubert 			if (nat->nat_dlocal)
40823b9b51feSCy Schubert 				continue;
40833b9b51feSCy Schubert 			if (nat->nat_ndstaddr != src.s_addr ||
40843b9b51feSCy Schubert 			    nat->nat_nsrcaddr != dst)
40853b9b51feSCy Schubert 				continue;
40863b9b51feSCy Schubert 			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
40873b9b51feSCy Schubert 				if (nat->nat_ndport != sport)
40883b9b51feSCy Schubert 					continue;
40893b9b51feSCy Schubert 				if (nat->nat_nsport != dport)
40903b9b51feSCy Schubert 					continue;
40913b9b51feSCy Schubert 
40923b9b51feSCy Schubert 			} else if (p == IPPROTO_ICMP) {
40933b9b51feSCy Schubert 				if (nat->nat_osport != dport) {
40943b9b51feSCy Schubert 					continue;
40953b9b51feSCy Schubert 				}
40963b9b51feSCy Schubert 			}
40973b9b51feSCy Schubert 			break;
40983b9b51feSCy Schubert 		}
40993b9b51feSCy Schubert 
41003b9b51feSCy Schubert 
41013b9b51feSCy Schubert 		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
41023b9b51feSCy Schubert 			ipn = nat->nat_ptr;
41033b9b51feSCy Schubert 			if ((ipn != NULL) && (nat->nat_aps != NULL))
41043b9b51feSCy Schubert 				if (ipf_proxy_match(fin, nat) != 0)
41053b9b51feSCy Schubert 					continue;
41063b9b51feSCy Schubert 		}
41073b9b51feSCy Schubert 		if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
41083b9b51feSCy Schubert 			nat->nat_ifps[0] = ifp;
41093b9b51feSCy Schubert 			nat->nat_mtu[0] = GETIFMTU_4(ifp);
41103b9b51feSCy Schubert 		}
41118c82b374SCy Schubert 		return (nat);
41123b9b51feSCy Schubert 	}
41133b9b51feSCy Schubert 
41143b9b51feSCy Schubert 	/*
41153b9b51feSCy Schubert 	 * So if we didn't find it but there are wildcard members in the hash
41163b9b51feSCy Schubert 	 * table, go back and look for them.  We do this search and update here
41173b9b51feSCy Schubert 	 * because it is modifying the NAT table and we want to do this only
41183b9b51feSCy Schubert 	 * for the first packet that matches.  The exception, of course, is
41193b9b51feSCy Schubert 	 * for "dummy" (FI_IGNORE) lookups.
41203b9b51feSCy Schubert 	 */
41213b9b51feSCy Schubert find_in_wild_ports:
41223b9b51feSCy Schubert 	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
41233b9b51feSCy Schubert 		NBUMPSIDEX(0, ns_lookup_miss, ns_lookup_miss_0);
41248c82b374SCy Schubert 		return (NULL);
41253b9b51feSCy Schubert 	}
41263b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
41273b9b51feSCy Schubert 		NBUMPSIDEX(0, ns_lookup_nowild, ns_lookup_nowild_0);
41288c82b374SCy Schubert 		return (NULL);
41293b9b51feSCy Schubert 	}
41303b9b51feSCy Schubert 
41313b9b51feSCy Schubert 	RWLOCK_EXIT(&softc->ipf_nat);
41323b9b51feSCy Schubert 
41333b9b51feSCy Schubert 	hv = NAT_HASH_FN(dst, 0, 0xffffffff);
41343b9b51feSCy Schubert 	hv = NAT_HASH_FN(src.s_addr, hv, softn->ipf_nat_table_sz);
41353b9b51feSCy Schubert 	WRITE_ENTER(&softc->ipf_nat);
41363b9b51feSCy Schubert 
41373b9b51feSCy Schubert 	nat = softn->ipf_nat_table[1][hv];
41383b9b51feSCy Schubert 	/* TRACE dst, src, hv, nat */
41393b9b51feSCy Schubert 	for (; nat; nat = nat->nat_hnext[1]) {
41403b9b51feSCy Schubert 		if (nat->nat_ifps[0] != NULL) {
41413b9b51feSCy Schubert 			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
41423b9b51feSCy Schubert 				continue;
41433b9b51feSCy Schubert 		}
41443b9b51feSCy Schubert 
41453b9b51feSCy Schubert 		if (nat->nat_pr[0] != fin->fin_p)
41463b9b51feSCy Schubert 			continue;
41473b9b51feSCy Schubert 
41483b9b51feSCy Schubert 		switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))
41493b9b51feSCy Schubert 		{
41503b9b51feSCy Schubert 		case NAT_INBOUND :
41513b9b51feSCy Schubert 			if (nat->nat_v[0] != 4)
41523b9b51feSCy Schubert 				continue;
41533b9b51feSCy Schubert 			if (nat->nat_osrcaddr != src.s_addr ||
41543b9b51feSCy Schubert 			    nat->nat_odstaddr != dst)
41553b9b51feSCy Schubert 				continue;
41563b9b51feSCy Schubert 			break;
41573b9b51feSCy Schubert 		case NAT_OUTBOUND :
41583b9b51feSCy Schubert 			if (nat->nat_v[1] != 4)
41593b9b51feSCy Schubert 				continue;
41603b9b51feSCy Schubert 			if (nat->nat_ndstaddr != src.s_addr ||
41613b9b51feSCy Schubert 			    nat->nat_nsrcaddr != dst)
41623b9b51feSCy Schubert 				continue;
41633b9b51feSCy Schubert 			break;
41643b9b51feSCy Schubert 		}
41653b9b51feSCy Schubert 
41663b9b51feSCy Schubert 		nflags = nat->nat_flags;
41673b9b51feSCy Schubert 		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
41683b9b51feSCy Schubert 			continue;
41693b9b51feSCy Schubert 
41703b9b51feSCy Schubert 		if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags,
41713b9b51feSCy Schubert 				   NAT_INBOUND) == 1) {
41723b9b51feSCy Schubert 			if ((fin->fin_flx & FI_IGNORE) != 0)
41733b9b51feSCy Schubert 				break;
41743b9b51feSCy Schubert 			if ((nflags & SI_CLONE) != 0) {
41753b9b51feSCy Schubert 				nat = ipf_nat_clone(fin, nat);
41763b9b51feSCy Schubert 				if (nat == NULL)
41773b9b51feSCy Schubert 					break;
41783b9b51feSCy Schubert 			} else {
41793b9b51feSCy Schubert 				MUTEX_ENTER(&softn->ipf_nat_new);
41803b9b51feSCy Schubert 				softn->ipf_nat_stats.ns_wilds--;
41813b9b51feSCy Schubert 				MUTEX_EXIT(&softn->ipf_nat_new);
41823b9b51feSCy Schubert 			}
41833b9b51feSCy Schubert 
41843b9b51feSCy Schubert 			if (nat->nat_dir == NAT_INBOUND) {
41853b9b51feSCy Schubert 				if (nat->nat_osport == 0) {
41863b9b51feSCy Schubert 					nat->nat_osport = sport;
41873b9b51feSCy Schubert 					nat->nat_nsport = sport;
41883b9b51feSCy Schubert 				}
41893b9b51feSCy Schubert 				if (nat->nat_odport == 0) {
41903b9b51feSCy Schubert 					nat->nat_odport = dport;
41913b9b51feSCy Schubert 					nat->nat_ndport = dport;
41923b9b51feSCy Schubert 				}
41933b9b51feSCy Schubert 			} else if (nat->nat_dir == NAT_OUTBOUND) {
41943b9b51feSCy Schubert 				if (nat->nat_osport == 0) {
41953b9b51feSCy Schubert 					nat->nat_osport = dport;
41963b9b51feSCy Schubert 					nat->nat_nsport = dport;
41973b9b51feSCy Schubert 				}
41983b9b51feSCy Schubert 				if (nat->nat_odport == 0) {
41993b9b51feSCy Schubert 					nat->nat_odport = sport;
42003b9b51feSCy Schubert 					nat->nat_ndport = sport;
42013b9b51feSCy Schubert 				}
42023b9b51feSCy Schubert 			}
42033b9b51feSCy Schubert 			if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
42043b9b51feSCy Schubert 				nat->nat_ifps[0] = ifp;
42053b9b51feSCy Schubert 				nat->nat_mtu[0] = GETIFMTU_4(ifp);
42063b9b51feSCy Schubert 			}
42073b9b51feSCy Schubert 			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
42083b9b51feSCy Schubert 			ipf_nat_tabmove(softn, nat);
42093b9b51feSCy Schubert 			break;
42103b9b51feSCy Schubert 		}
42113b9b51feSCy Schubert 	}
42123b9b51feSCy Schubert 
42133b9b51feSCy Schubert 	MUTEX_DOWNGRADE(&softc->ipf_nat);
42143b9b51feSCy Schubert 
42153b9b51feSCy Schubert 	if (nat == NULL) {
42163b9b51feSCy Schubert 		NBUMPSIDE(0, ns_lookup_miss);
42173b9b51feSCy Schubert 	}
42188c82b374SCy Schubert 	return (nat);
42193b9b51feSCy Schubert }
42203b9b51feSCy Schubert 
42213b9b51feSCy Schubert 
42223b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
42233b9b51feSCy Schubert /* Function:    ipf_nat_tabmove                                             */
42243b9b51feSCy Schubert /* Returns:     Nil                                                         */
42253b9b51feSCy Schubert /* Parameters:  softn(I) - pointer to NAT context structure                 */
42263b9b51feSCy Schubert /*              nat(I)   - pointer to NAT structure                         */
42273b9b51feSCy Schubert /* Write Lock:  ipf_nat                                                     */
42283b9b51feSCy Schubert /*                                                                          */
42293b9b51feSCy Schubert /* This function is only called for TCP/UDP NAT table entries where the     */
42303b9b51feSCy Schubert /* original was placed in the table without hashing on the ports and we now */
42313b9b51feSCy Schubert /* want to include hashing on port numbers.                                 */
42323b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
42333b9b51feSCy Schubert static void
ipf_nat_tabmove(ipf_nat_softc_t * softn,nat_t * nat)4234064a5a95SCy Schubert ipf_nat_tabmove(ipf_nat_softc_t *softn, nat_t *nat)
42353b9b51feSCy Schubert {
42363b9b51feSCy Schubert 	u_int hv0, hv1, rhv0, rhv1;
42373b9b51feSCy Schubert 	natstat_t *nsp;
42383b9b51feSCy Schubert 	nat_t **natp;
42393b9b51feSCy Schubert 
42403b9b51feSCy Schubert 	if (nat->nat_flags & SI_CLONE)
42413b9b51feSCy Schubert 		return;
42423b9b51feSCy Schubert 
42433b9b51feSCy Schubert 	nsp = &softn->ipf_nat_stats;
42443b9b51feSCy Schubert 	/*
42453b9b51feSCy Schubert 	 * Remove the NAT entry from the old location
42463b9b51feSCy Schubert 	 */
42473b9b51feSCy Schubert 	if (nat->nat_hnext[0])
42483b9b51feSCy Schubert 		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
42493b9b51feSCy Schubert 	*nat->nat_phnext[0] = nat->nat_hnext[0];
42503b9b51feSCy Schubert 	nsp->ns_side[0].ns_bucketlen[nat->nat_hv[0] %
42513b9b51feSCy Schubert 				     softn->ipf_nat_table_sz]--;
42523b9b51feSCy Schubert 
42533b9b51feSCy Schubert 	if (nat->nat_hnext[1])
42543b9b51feSCy Schubert 		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
42553b9b51feSCy Schubert 	*nat->nat_phnext[1] = nat->nat_hnext[1];
42563b9b51feSCy Schubert 	nsp->ns_side[1].ns_bucketlen[nat->nat_hv[1] %
42573b9b51feSCy Schubert 				     softn->ipf_nat_table_sz]--;
42583b9b51feSCy Schubert 
42593b9b51feSCy Schubert 	/*
42603b9b51feSCy Schubert 	 * Add into the NAT table in the new position
42613b9b51feSCy Schubert 	 */
42623b9b51feSCy Schubert 	rhv0 = NAT_HASH_FN(nat->nat_osrcaddr, nat->nat_osport, 0xffffffff);
42633b9b51feSCy Schubert 	rhv0 = NAT_HASH_FN(nat->nat_odstaddr, rhv0 + nat->nat_odport,
42643b9b51feSCy Schubert 			   0xffffffff);
42653b9b51feSCy Schubert 	rhv1 = NAT_HASH_FN(nat->nat_nsrcaddr, nat->nat_nsport, 0xffffffff);
42663b9b51feSCy Schubert 	rhv1 = NAT_HASH_FN(nat->nat_ndstaddr, rhv1 + nat->nat_ndport,
42673b9b51feSCy Schubert 			   0xffffffff);
42683b9b51feSCy Schubert 
42693b9b51feSCy Schubert 	hv0 = rhv0 % softn->ipf_nat_table_sz;
42703b9b51feSCy Schubert 	hv1 = rhv1 % softn->ipf_nat_table_sz;
42713b9b51feSCy Schubert 
42723b9b51feSCy Schubert 	if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) {
42733b9b51feSCy Schubert 		u_int swap;
42743b9b51feSCy Schubert 
42753b9b51feSCy Schubert 		swap = hv0;
42763b9b51feSCy Schubert 		hv0 = hv1;
42773b9b51feSCy Schubert 		hv1 = swap;
42783b9b51feSCy Schubert 	}
42793b9b51feSCy Schubert 
42803b9b51feSCy Schubert 	/* TRACE nat_osrcaddr, nat_osport, nat_odstaddr, nat_odport, hv0 */
42813b9b51feSCy Schubert 	/* TRACE nat_nsrcaddr, nat_nsport, nat_ndstaddr, nat_ndport, hv1 */
42823b9b51feSCy Schubert 
42833b9b51feSCy Schubert 	nat->nat_hv[0] = rhv0;
42843b9b51feSCy Schubert 	natp = &softn->ipf_nat_table[0][hv0];
42853b9b51feSCy Schubert 	if (*natp)
42863b9b51feSCy Schubert 		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
42873b9b51feSCy Schubert 	nat->nat_phnext[0] = natp;
42883b9b51feSCy Schubert 	nat->nat_hnext[0] = *natp;
42893b9b51feSCy Schubert 	*natp = nat;
42903b9b51feSCy Schubert 	nsp->ns_side[0].ns_bucketlen[hv0]++;
42913b9b51feSCy Schubert 
42923b9b51feSCy Schubert 	nat->nat_hv[1] = rhv1;
42933b9b51feSCy Schubert 	natp = &softn->ipf_nat_table[1][hv1];
42943b9b51feSCy Schubert 	if (*natp)
42953b9b51feSCy Schubert 		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
42963b9b51feSCy Schubert 	nat->nat_phnext[1] = natp;
42973b9b51feSCy Schubert 	nat->nat_hnext[1] = *natp;
42983b9b51feSCy Schubert 	*natp = nat;
42993b9b51feSCy Schubert 	nsp->ns_side[1].ns_bucketlen[hv1]++;
43003b9b51feSCy Schubert }
43013b9b51feSCy Schubert 
43023b9b51feSCy Schubert 
43033b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
43043b9b51feSCy Schubert /* Function:    ipf_nat_outlookup                                           */
43053b9b51feSCy Schubert /* Returns:     nat_t* - NULL == no match,                                  */
43063b9b51feSCy Schubert /*                       else pointer to matching NAT entry                 */
43073b9b51feSCy Schubert /* Parameters:  fin(I)   - pointer to packet information                    */
43083b9b51feSCy Schubert /*              flags(I) - NAT flags for this packet                        */
43093b9b51feSCy Schubert /*              p(I)     - protocol for this packet                         */
43103b9b51feSCy Schubert /*              src(I)   - source IP address                                */
43113b9b51feSCy Schubert /*              dst(I)   - destination IP address                           */
43123b9b51feSCy Schubert /*              rw(I)    - 1 == write lock on  held, 0 == read lock.        */
43133b9b51feSCy Schubert /*                                                                          */
43143b9b51feSCy Schubert /* Lookup a nat entry based on the source 'real' ip address/port and        */
43153b9b51feSCy Schubert /* destination address/port.  We use this lookup when sending a packet out, */
43163b9b51feSCy Schubert /* we're looking for a table entry, based on the source address.            */
43173b9b51feSCy Schubert /*                                                                          */
43183b9b51feSCy Schubert /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
43193b9b51feSCy Schubert /*                                                                          */
43203b9b51feSCy Schubert /* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
43213b9b51feSCy Schubert /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
43223b9b51feSCy Schubert /*                                                                          */
43233b9b51feSCy Schubert /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
43243b9b51feSCy Schubert /*            the packet is of said protocol                                */
43253b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
43263b9b51feSCy Schubert nat_t *
ipf_nat_outlookup(fr_info_t * fin,u_int flags,u_int p,struct in_addr src,struct in_addr dst)4327064a5a95SCy Schubert ipf_nat_outlookup(fr_info_t *fin, u_int flags, u_int p,
4328064a5a95SCy Schubert 	struct in_addr src , struct in_addr dst)
43293b9b51feSCy Schubert {
43303b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
43313b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
43323b9b51feSCy Schubert 	u_short sport, dport;
43333b9b51feSCy Schubert 	u_int sflags;
43343b9b51feSCy Schubert 	ipnat_t *ipn;
43353b9b51feSCy Schubert 	nat_t *nat;
43363b9b51feSCy Schubert 	void *ifp;
43373b9b51feSCy Schubert 	u_int hv;
43383b9b51feSCy Schubert 
43393b9b51feSCy Schubert 	ifp = fin->fin_ifp;
43403b9b51feSCy Schubert 	sflags = flags & IPN_TCPUDPICMP;
43413b9b51feSCy Schubert 
43423b9b51feSCy Schubert 	switch (p)
43433b9b51feSCy Schubert 	{
43443b9b51feSCy Schubert 	case IPPROTO_TCP :
43453b9b51feSCy Schubert 	case IPPROTO_UDP :
43463b9b51feSCy Schubert 		sport = htons(fin->fin_data[0]);
43473b9b51feSCy Schubert 		dport = htons(fin->fin_data[1]);
43483b9b51feSCy Schubert 		break;
43493b9b51feSCy Schubert 	case IPPROTO_ICMP :
43503b9b51feSCy Schubert 		sport = 0;
43513b9b51feSCy Schubert 		dport = fin->fin_data[1];
43523b9b51feSCy Schubert 		break;
43533b9b51feSCy Schubert 	default :
43543b9b51feSCy Schubert 		sport = 0;
43553b9b51feSCy Schubert 		dport = 0;
43563b9b51feSCy Schubert 		break;
43573b9b51feSCy Schubert 	}
43583b9b51feSCy Schubert 
43593b9b51feSCy Schubert 	if ((flags & SI_WILDP) != 0)
43603b9b51feSCy Schubert 		goto find_out_wild_ports;
43613b9b51feSCy Schubert 
43623b9b51feSCy Schubert 	hv = NAT_HASH_FN(src.s_addr, sport, 0xffffffff);
43633b9b51feSCy Schubert 	hv = NAT_HASH_FN(dst.s_addr, hv + dport, softn->ipf_nat_table_sz);
43643b9b51feSCy Schubert 	nat = softn->ipf_nat_table[0][hv];
43653b9b51feSCy Schubert 
43663b9b51feSCy Schubert 	/* TRACE src, sport, dst, dport, hv, nat */
43673b9b51feSCy Schubert 
43683b9b51feSCy Schubert 	for (; nat; nat = nat->nat_hnext[0]) {
43693b9b51feSCy Schubert 		if (nat->nat_ifps[1] != NULL) {
43703b9b51feSCy Schubert 			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
43713b9b51feSCy Schubert 				continue;
43723b9b51feSCy Schubert 		}
43733b9b51feSCy Schubert 
43743b9b51feSCy Schubert 		if (nat->nat_pr[1] != p)
43753b9b51feSCy Schubert 			continue;
43763b9b51feSCy Schubert 
43773b9b51feSCy Schubert 		switch (nat->nat_dir)
43783b9b51feSCy Schubert 		{
43793b9b51feSCy Schubert 		case NAT_INBOUND :
43803b9b51feSCy Schubert 		case NAT_DIVERTIN :
43813b9b51feSCy Schubert 			if (nat->nat_v[1] != 4)
43823b9b51feSCy Schubert 				continue;
43833b9b51feSCy Schubert 			if (nat->nat_ndstaddr != src.s_addr ||
43843b9b51feSCy Schubert 			    nat->nat_nsrcaddr != dst.s_addr)
43853b9b51feSCy Schubert 				continue;
43863b9b51feSCy Schubert 
43873b9b51feSCy Schubert 			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
43883b9b51feSCy Schubert 				if (nat->nat_ndport != sport)
43893b9b51feSCy Schubert 					continue;
43903b9b51feSCy Schubert 				if (nat->nat_nsport != dport)
43913b9b51feSCy Schubert 					continue;
43923b9b51feSCy Schubert 
43933b9b51feSCy Schubert 			} else if (p == IPPROTO_ICMP) {
43943b9b51feSCy Schubert 				if (nat->nat_osport != dport) {
43953b9b51feSCy Schubert 					continue;
43963b9b51feSCy Schubert 				}
43973b9b51feSCy Schubert 			}
43983b9b51feSCy Schubert 			break;
43993b9b51feSCy Schubert 		case NAT_OUTBOUND :
44003b9b51feSCy Schubert 		case NAT_DIVERTOUT :
44013b9b51feSCy Schubert 			if (nat->nat_v[0] != 4)
44023b9b51feSCy Schubert 				continue;
44033b9b51feSCy Schubert 			if (nat->nat_osrcaddr != src.s_addr ||
44043b9b51feSCy Schubert 			    nat->nat_odstaddr != dst.s_addr)
44053b9b51feSCy Schubert 				continue;
44063b9b51feSCy Schubert 
44073b9b51feSCy Schubert 			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
44083b9b51feSCy Schubert 				if (nat->nat_odport != dport)
44093b9b51feSCy Schubert 					continue;
44103b9b51feSCy Schubert 				if (nat->nat_osport != sport)
44113b9b51feSCy Schubert 					continue;
44123b9b51feSCy Schubert 
44133b9b51feSCy Schubert 			} else if (p == IPPROTO_ICMP) {
44143b9b51feSCy Schubert 				if (nat->nat_osport != dport) {
44153b9b51feSCy Schubert 					continue;
44163b9b51feSCy Schubert 				}
44173b9b51feSCy Schubert 			}
44183b9b51feSCy Schubert 			break;
44193b9b51feSCy Schubert 		}
44203b9b51feSCy Schubert 
44213b9b51feSCy Schubert 		ipn = nat->nat_ptr;
44223b9b51feSCy Schubert 		if ((ipn != NULL) && (nat->nat_aps != NULL))
44233b9b51feSCy Schubert 			if (ipf_proxy_match(fin, nat) != 0)
44243b9b51feSCy Schubert 				continue;
44253b9b51feSCy Schubert 
44263b9b51feSCy Schubert 		if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
44273b9b51feSCy Schubert 			nat->nat_ifps[1] = ifp;
44283b9b51feSCy Schubert 			nat->nat_mtu[1] = GETIFMTU_4(ifp);
44293b9b51feSCy Schubert 		}
44308c82b374SCy Schubert 		return (nat);
44313b9b51feSCy Schubert 	}
44323b9b51feSCy Schubert 
44333b9b51feSCy Schubert 	/*
44343b9b51feSCy Schubert 	 * So if we didn't find it but there are wildcard members in the hash
44353b9b51feSCy Schubert 	 * table, go back and look for them.  We do this search and update here
44363b9b51feSCy Schubert 	 * because it is modifying the NAT table and we want to do this only
44373b9b51feSCy Schubert 	 * for the first packet that matches.  The exception, of course, is
44383b9b51feSCy Schubert 	 * for "dummy" (FI_IGNORE) lookups.
44393b9b51feSCy Schubert 	 */
44403b9b51feSCy Schubert find_out_wild_ports:
44413b9b51feSCy Schubert 	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
44423b9b51feSCy Schubert 		NBUMPSIDEX(1, ns_lookup_miss, ns_lookup_miss_1);
44438c82b374SCy Schubert 		return (NULL);
44443b9b51feSCy Schubert 	}
44453b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
44463b9b51feSCy Schubert 		NBUMPSIDEX(1, ns_lookup_nowild, ns_lookup_nowild_1);
44478c82b374SCy Schubert 		return (NULL);
44483b9b51feSCy Schubert 	}
44493b9b51feSCy Schubert 
44503b9b51feSCy Schubert 	RWLOCK_EXIT(&softc->ipf_nat);
44513b9b51feSCy Schubert 
44523b9b51feSCy Schubert 	hv = NAT_HASH_FN(src.s_addr, 0, 0xffffffff);
44533b9b51feSCy Schubert 	hv = NAT_HASH_FN(dst.s_addr, hv, softn->ipf_nat_table_sz);
44543b9b51feSCy Schubert 
44553b9b51feSCy Schubert 	WRITE_ENTER(&softc->ipf_nat);
44563b9b51feSCy Schubert 
44573b9b51feSCy Schubert 	nat = softn->ipf_nat_table[0][hv];
44583b9b51feSCy Schubert 	for (; nat; nat = nat->nat_hnext[0]) {
44593b9b51feSCy Schubert 		if (nat->nat_ifps[1] != NULL) {
44603b9b51feSCy Schubert 			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
44613b9b51feSCy Schubert 				continue;
44623b9b51feSCy Schubert 		}
44633b9b51feSCy Schubert 
44643b9b51feSCy Schubert 		if (nat->nat_pr[1] != fin->fin_p)
44653b9b51feSCy Schubert 			continue;
44663b9b51feSCy Schubert 
44673b9b51feSCy Schubert 		switch (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND))
44683b9b51feSCy Schubert 		{
44693b9b51feSCy Schubert 		case NAT_INBOUND :
44703b9b51feSCy Schubert 			if (nat->nat_v[1] != 4)
44713b9b51feSCy Schubert 				continue;
44723b9b51feSCy Schubert 			if (nat->nat_ndstaddr != src.s_addr ||
44733b9b51feSCy Schubert 			    nat->nat_nsrcaddr != dst.s_addr)
44743b9b51feSCy Schubert 				continue;
44753b9b51feSCy Schubert 			break;
44763b9b51feSCy Schubert 		case NAT_OUTBOUND :
44773b9b51feSCy Schubert 			if (nat->nat_v[0] != 4)
44783b9b51feSCy Schubert 				continue;
44793b9b51feSCy Schubert 			if (nat->nat_osrcaddr != src.s_addr ||
44803b9b51feSCy Schubert 			    nat->nat_odstaddr != dst.s_addr)
44813b9b51feSCy Schubert 				continue;
44823b9b51feSCy Schubert 			break;
44833b9b51feSCy Schubert 		}
44843b9b51feSCy Schubert 
44853b9b51feSCy Schubert 		if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP)))
44863b9b51feSCy Schubert 			continue;
44873b9b51feSCy Schubert 
44883b9b51feSCy Schubert 		if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags,
44893b9b51feSCy Schubert 				   NAT_OUTBOUND) == 1) {
44903b9b51feSCy Schubert 			if ((fin->fin_flx & FI_IGNORE) != 0)
44913b9b51feSCy Schubert 				break;
44923b9b51feSCy Schubert 			if ((nat->nat_flags & SI_CLONE) != 0) {
44933b9b51feSCy Schubert 				nat = ipf_nat_clone(fin, nat);
44943b9b51feSCy Schubert 				if (nat == NULL)
44953b9b51feSCy Schubert 					break;
44963b9b51feSCy Schubert 			} else {
44973b9b51feSCy Schubert 				MUTEX_ENTER(&softn->ipf_nat_new);
44983b9b51feSCy Schubert 				softn->ipf_nat_stats.ns_wilds--;
44993b9b51feSCy Schubert 				MUTEX_EXIT(&softn->ipf_nat_new);
45003b9b51feSCy Schubert 			}
45013b9b51feSCy Schubert 
45023b9b51feSCy Schubert 			if (nat->nat_dir == NAT_OUTBOUND) {
45033b9b51feSCy Schubert 				if (nat->nat_osport == 0) {
45043b9b51feSCy Schubert 					nat->nat_osport = sport;
45053b9b51feSCy Schubert 					nat->nat_nsport = sport;
45063b9b51feSCy Schubert 				}
45073b9b51feSCy Schubert 				if (nat->nat_odport == 0) {
45083b9b51feSCy Schubert 					nat->nat_odport = dport;
45093b9b51feSCy Schubert 					nat->nat_ndport = dport;
45103b9b51feSCy Schubert 				}
45113b9b51feSCy Schubert 			} else if (nat->nat_dir == NAT_INBOUND) {
45123b9b51feSCy Schubert 				if (nat->nat_osport == 0) {
45133b9b51feSCy Schubert 					nat->nat_osport = dport;
45143b9b51feSCy Schubert 					nat->nat_nsport = dport;
45153b9b51feSCy Schubert 				}
45163b9b51feSCy Schubert 				if (nat->nat_odport == 0) {
45173b9b51feSCy Schubert 					nat->nat_odport = sport;
45183b9b51feSCy Schubert 					nat->nat_ndport = sport;
45193b9b51feSCy Schubert 				}
45203b9b51feSCy Schubert 			}
45213b9b51feSCy Schubert 			if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
45223b9b51feSCy Schubert 				nat->nat_ifps[1] = ifp;
45233b9b51feSCy Schubert 				nat->nat_mtu[1] = GETIFMTU_4(ifp);
45243b9b51feSCy Schubert 			}
45253b9b51feSCy Schubert 			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
45263b9b51feSCy Schubert 			ipf_nat_tabmove(softn, nat);
45273b9b51feSCy Schubert 			break;
45283b9b51feSCy Schubert 		}
45293b9b51feSCy Schubert 	}
45303b9b51feSCy Schubert 
45313b9b51feSCy Schubert 	MUTEX_DOWNGRADE(&softc->ipf_nat);
45323b9b51feSCy Schubert 
45333b9b51feSCy Schubert 	if (nat == NULL) {
45343b9b51feSCy Schubert 		NBUMPSIDE(1, ns_lookup_miss);
45353b9b51feSCy Schubert 	}
45368c82b374SCy Schubert 	return (nat);
45373b9b51feSCy Schubert }
45383b9b51feSCy Schubert 
45393b9b51feSCy Schubert 
45403b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
45413b9b51feSCy Schubert /* Function:    ipf_nat_lookupredir                                         */
45423b9b51feSCy Schubert /* Returns:     nat_t* - NULL == no match,                                  */
45433b9b51feSCy Schubert /*                       else pointer to matching NAT entry                 */
45443b9b51feSCy Schubert /* Parameters:  np(I) - pointer to description of packet to find NAT table  */
45453b9b51feSCy Schubert /*                      entry for.                                          */
45463b9b51feSCy Schubert /*                                                                          */
45473b9b51feSCy Schubert /* Lookup the NAT tables to search for a matching redirect                  */
45483b9b51feSCy Schubert /* The contents of natlookup_t should imitate those found in a packet that  */
45493b9b51feSCy Schubert /* would be translated - ie a packet coming in for RDR or going out for MAP.*/
45503b9b51feSCy Schubert /* We can do the lookup in one of two ways, imitating an inbound or         */
45513b9b51feSCy Schubert /* outbound  packet.  By default we assume outbound, unless IPN_IN is set.  */
45523b9b51feSCy Schubert /* For IN, the fields are set as follows:                                   */
45533b9b51feSCy Schubert /*     nl_real* = source information                                        */
45543b9b51feSCy Schubert /*     nl_out* = destination information (translated)                       */
45553b9b51feSCy Schubert /* For an out packet, the fields are set like this:                         */
45563b9b51feSCy Schubert /*     nl_in* = source information (untranslated)                           */
45573b9b51feSCy Schubert /*     nl_out* = destination information (translated)                       */
45583b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
45593b9b51feSCy Schubert nat_t *
ipf_nat_lookupredir(natlookup_t * np)4560064a5a95SCy Schubert ipf_nat_lookupredir(natlookup_t *np)
45613b9b51feSCy Schubert {
45623b9b51feSCy Schubert 	fr_info_t fi;
45633b9b51feSCy Schubert 	nat_t *nat;
45643b9b51feSCy Schubert 
45653b9b51feSCy Schubert 	bzero((char *)&fi, sizeof(fi));
45663b9b51feSCy Schubert 	if (np->nl_flags & IPN_IN) {
45673b9b51feSCy Schubert 		fi.fin_data[0] = ntohs(np->nl_realport);
45683b9b51feSCy Schubert 		fi.fin_data[1] = ntohs(np->nl_outport);
45693b9b51feSCy Schubert 	} else {
45703b9b51feSCy Schubert 		fi.fin_data[0] = ntohs(np->nl_inport);
45713b9b51feSCy Schubert 		fi.fin_data[1] = ntohs(np->nl_outport);
45723b9b51feSCy Schubert 	}
45733b9b51feSCy Schubert 	if (np->nl_flags & IPN_TCP)
45743b9b51feSCy Schubert 		fi.fin_p = IPPROTO_TCP;
45753b9b51feSCy Schubert 	else if (np->nl_flags & IPN_UDP)
45763b9b51feSCy Schubert 		fi.fin_p = IPPROTO_UDP;
45773b9b51feSCy Schubert 	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
45783b9b51feSCy Schubert 		fi.fin_p = IPPROTO_ICMP;
45793b9b51feSCy Schubert 
45803b9b51feSCy Schubert 	/*
45813b9b51feSCy Schubert 	 * We can do two sorts of lookups:
45823b9b51feSCy Schubert 	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
45833b9b51feSCy Schubert 	 * - default: we have the `in' and `out' address, look for `real'.
45843b9b51feSCy Schubert 	 */
45853b9b51feSCy Schubert 	if (np->nl_flags & IPN_IN) {
45863b9b51feSCy Schubert 		if ((nat = ipf_nat_inlookup(&fi, np->nl_flags, fi.fin_p,
45873b9b51feSCy Schubert 					    np->nl_realip, np->nl_outip))) {
45883b9b51feSCy Schubert 			np->nl_inip = nat->nat_odstip;
45893b9b51feSCy Schubert 			np->nl_inport = nat->nat_odport;
45903b9b51feSCy Schubert 		}
45913b9b51feSCy Schubert 	} else {
45923b9b51feSCy Schubert 		/*
45933b9b51feSCy Schubert 		 * If nl_inip is non null, this is a lookup based on the real
45943b9b51feSCy Schubert 		 * ip address. Else, we use the fake.
45953b9b51feSCy Schubert 		 */
45963b9b51feSCy Schubert 		if ((nat = ipf_nat_outlookup(&fi, np->nl_flags, fi.fin_p,
45973b9b51feSCy Schubert 					 np->nl_inip, np->nl_outip))) {
45983b9b51feSCy Schubert 
45993b9b51feSCy Schubert 			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
46003b9b51feSCy Schubert 				fr_info_t fin;
46013b9b51feSCy Schubert 				bzero((char *)&fin, sizeof(fin));
46023b9b51feSCy Schubert 				fin.fin_p = nat->nat_pr[0];
46033b9b51feSCy Schubert 				fin.fin_data[0] = ntohs(nat->nat_ndport);
46043b9b51feSCy Schubert 				fin.fin_data[1] = ntohs(nat->nat_nsport);
46053b9b51feSCy Schubert 				if (ipf_nat_inlookup(&fin, np->nl_flags,
46063b9b51feSCy Schubert 						     fin.fin_p, nat->nat_ndstip,
46073b9b51feSCy Schubert 						     nat->nat_nsrcip) != NULL) {
46083b9b51feSCy Schubert 					np->nl_flags &= ~IPN_FINDFORWARD;
46093b9b51feSCy Schubert 				}
46103b9b51feSCy Schubert 			}
46113b9b51feSCy Schubert 
46123b9b51feSCy Schubert 			np->nl_realip = nat->nat_odstip;
46133b9b51feSCy Schubert 			np->nl_realport = nat->nat_odport;
46143b9b51feSCy Schubert 		}
46153b9b51feSCy Schubert  	}
46163b9b51feSCy Schubert 
46178c82b374SCy Schubert 	return (nat);
46183b9b51feSCy Schubert }
46193b9b51feSCy Schubert 
46203b9b51feSCy Schubert 
46213b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
46223b9b51feSCy Schubert /* Function:    ipf_nat_match                                               */
46233b9b51feSCy Schubert /* Returns:     int - 0 == no match, 1 == match                             */
46243b9b51feSCy Schubert /* Parameters:  fin(I)   - pointer to packet information                    */
46253b9b51feSCy Schubert /*              np(I)    - pointer to NAT rule                              */
46263b9b51feSCy Schubert /*                                                                          */
46273b9b51feSCy Schubert /* Pull the matching of a packet against a NAT rule out of that complex     */
46283b9b51feSCy Schubert /* loop inside ipf_nat_checkin() and lay it out properly in its own function. */
46293b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
46303b9b51feSCy Schubert static int
ipf_nat_match(fr_info_t * fin,ipnat_t * np)4631064a5a95SCy Schubert ipf_nat_match(fr_info_t *fin, ipnat_t *np)
46323b9b51feSCy Schubert {
46333b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
46343b9b51feSCy Schubert 	frtuc_t *ft;
46353b9b51feSCy Schubert 	int match;
46363b9b51feSCy Schubert 
46373b9b51feSCy Schubert 	match = 0;
46383b9b51feSCy Schubert 	switch (np->in_osrcatype)
46393b9b51feSCy Schubert 	{
46403b9b51feSCy Schubert 	case FRI_NORMAL :
46413b9b51feSCy Schubert 		match = ((fin->fin_saddr & np->in_osrcmsk) != np->in_osrcaddr);
46423b9b51feSCy Schubert 		break;
46433b9b51feSCy Schubert 	case FRI_LOOKUP :
46443b9b51feSCy Schubert 		match = (*np->in_osrcfunc)(softc, np->in_osrcptr,
46453b9b51feSCy Schubert 					   4, &fin->fin_saddr, fin->fin_plen);
46463b9b51feSCy Schubert 		break;
46473b9b51feSCy Schubert 	}
46483b9b51feSCy Schubert 	match ^= ((np->in_flags & IPN_NOTSRC) != 0);
46493b9b51feSCy Schubert 	if (match)
46508c82b374SCy Schubert 		return (0);
46513b9b51feSCy Schubert 
46523b9b51feSCy Schubert 	match = 0;
46533b9b51feSCy Schubert 	switch (np->in_odstatype)
46543b9b51feSCy Schubert 	{
46553b9b51feSCy Schubert 	case FRI_NORMAL :
46563b9b51feSCy Schubert 		match = ((fin->fin_daddr & np->in_odstmsk) != np->in_odstaddr);
46573b9b51feSCy Schubert 		break;
46583b9b51feSCy Schubert 	case FRI_LOOKUP :
46593b9b51feSCy Schubert 		match = (*np->in_odstfunc)(softc, np->in_odstptr,
46603b9b51feSCy Schubert 					   4, &fin->fin_daddr, fin->fin_plen);
46613b9b51feSCy Schubert 		break;
46623b9b51feSCy Schubert 	}
46633b9b51feSCy Schubert 
46643b9b51feSCy Schubert 	match ^= ((np->in_flags & IPN_NOTDST) != 0);
46653b9b51feSCy Schubert 	if (match)
46668c82b374SCy Schubert 		return (0);
46673b9b51feSCy Schubert 
46683b9b51feSCy Schubert 	ft = &np->in_tuc;
46693b9b51feSCy Schubert 	if (!(fin->fin_flx & FI_TCPUDP) ||
46703b9b51feSCy Schubert 	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
46713b9b51feSCy Schubert 		if (ft->ftu_scmp || ft->ftu_dcmp)
46728c82b374SCy Schubert 			return (0);
46738c82b374SCy Schubert 		return (1);
46743b9b51feSCy Schubert 	}
46753b9b51feSCy Schubert 
46768c82b374SCy Schubert 	return (ipf_tcpudpchk(&fin->fin_fi, ft));
46773b9b51feSCy Schubert }
46783b9b51feSCy Schubert 
46793b9b51feSCy Schubert 
46803b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
46813b9b51feSCy Schubert /* Function:    ipf_nat_update                                              */
46823b9b51feSCy Schubert /* Returns:     Nil                                                         */
46833b9b51feSCy Schubert /* Parameters:  fin(I) - pointer to packet information                      */
46843b9b51feSCy Schubert /*              nat(I) - pointer to NAT structure                           */
46853b9b51feSCy Schubert /*                                                                          */
46863b9b51feSCy Schubert /* Updates the lifetime of a NAT table entry for non-TCP packets.  Must be  */
46873b9b51feSCy Schubert /* called with fin_rev updated - i.e. after calling ipf_nat_proto().        */
46883b9b51feSCy Schubert /*                                                                          */
46893b9b51feSCy Schubert /* This *MUST* be called after ipf_nat_proto() as it expects fin_rev to     */
46903b9b51feSCy Schubert /* already be set.                                                          */
46913b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
46923b9b51feSCy Schubert void
ipf_nat_update(fr_info_t * fin,nat_t * nat)4693064a5a95SCy Schubert ipf_nat_update(fr_info_t *fin, nat_t *nat)
46943b9b51feSCy Schubert {
46953b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
46963b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
46973b9b51feSCy Schubert 	ipftq_t *ifq, *ifq2;
46983b9b51feSCy Schubert 	ipftqent_t *tqe;
46993b9b51feSCy Schubert 	ipnat_t *np = nat->nat_ptr;
47003b9b51feSCy Schubert 
47013b9b51feSCy Schubert 	tqe = &nat->nat_tqe;
47023b9b51feSCy Schubert 	ifq = tqe->tqe_ifq;
47033b9b51feSCy Schubert 
47043b9b51feSCy Schubert 	/*
47053b9b51feSCy Schubert 	 * We allow over-riding of NAT timeouts from NAT rules, even for
47063b9b51feSCy Schubert 	 * TCP, however, if it is TCP and there is no rule timeout set,
47073b9b51feSCy Schubert 	 * then do not update the timeout here.
47083b9b51feSCy Schubert 	 */
47093b9b51feSCy Schubert 	if (np != NULL) {
47103b9b51feSCy Schubert 		np->in_bytes[fin->fin_rev] += fin->fin_plen;
47113b9b51feSCy Schubert 		ifq2 = np->in_tqehead[fin->fin_rev];
47123b9b51feSCy Schubert 	} else {
47133b9b51feSCy Schubert 		ifq2 = NULL;
47143b9b51feSCy Schubert 	}
47153b9b51feSCy Schubert 
47163b9b51feSCy Schubert 	if (nat->nat_pr[0] == IPPROTO_TCP && ifq2 == NULL) {
47173b9b51feSCy Schubert 		(void) ipf_tcp_age(&nat->nat_tqe, fin, softn->ipf_nat_tcptq,
47183b9b51feSCy Schubert 				   0, 2);
47193b9b51feSCy Schubert 	} else {
47203b9b51feSCy Schubert 		if (ifq2 == NULL) {
47213b9b51feSCy Schubert 			if (nat->nat_pr[0] == IPPROTO_UDP)
47223b9b51feSCy Schubert 				ifq2 = fin->fin_rev ? &softn->ipf_nat_udpacktq :
47233b9b51feSCy Schubert 						      &softn->ipf_nat_udptq;
47243b9b51feSCy Schubert 			else if (nat->nat_pr[0] == IPPROTO_ICMP ||
47253b9b51feSCy Schubert 				 nat->nat_pr[0] == IPPROTO_ICMPV6)
47263b9b51feSCy Schubert 				ifq2 = fin->fin_rev ? &softn->ipf_nat_icmpacktq:
47273b9b51feSCy Schubert 						      &softn->ipf_nat_icmptq;
47283b9b51feSCy Schubert 			else
47293b9b51feSCy Schubert 				ifq2 = &softn->ipf_nat_iptq;
47303b9b51feSCy Schubert 		}
47313b9b51feSCy Schubert 
47323b9b51feSCy Schubert 		ipf_movequeue(softc->ipf_ticks, tqe, ifq, ifq2);
47333b9b51feSCy Schubert 	}
47343b9b51feSCy Schubert }
47353b9b51feSCy Schubert 
47363b9b51feSCy Schubert 
47373b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
47383b9b51feSCy Schubert /* Function:    ipf_nat_checkout                                            */
47393b9b51feSCy Schubert /* Returns:     int - -1 == packet failed NAT checks so block it,           */
47403b9b51feSCy Schubert /*                     0 == no packet translation occurred,                 */
47413b9b51feSCy Schubert /*                     1 == packet was successfully translated.             */
47423b9b51feSCy Schubert /* Parameters:  fin(I)   - pointer to packet information                    */
47433b9b51feSCy Schubert /*              passp(I) - pointer to filtering result flags                */
47443b9b51feSCy Schubert /*                                                                          */
47453b9b51feSCy Schubert /* Check to see if an outcoming packet should be changed.  ICMP packets are */
47463b9b51feSCy Schubert /* first checked to see if they match an existing entry (if an error),      */
47473b9b51feSCy Schubert /* otherwise a search of the current NAT table is made.  If neither results */
47483b9b51feSCy Schubert /* in a match then a search for a matching NAT rule is made.  Create a new  */
47493b9b51feSCy Schubert /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
47503b9b51feSCy Schubert /* packet header(s) as required.                                            */
47513b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
47523b9b51feSCy Schubert int
ipf_nat_checkout(fr_info_t * fin,u_32_t * passp)4753064a5a95SCy Schubert ipf_nat_checkout(fr_info_t *fin, u_32_t *passp)
47543b9b51feSCy Schubert {
47553b9b51feSCy Schubert 	ipnat_t *np = NULL, *npnext;
47563b9b51feSCy Schubert 	struct ifnet *ifp, *sifp;
47573b9b51feSCy Schubert 	ipf_main_softc_t *softc;
47583b9b51feSCy Schubert 	ipf_nat_softc_t *softn;
47593b9b51feSCy Schubert 	icmphdr_t *icmp = NULL;
47603b9b51feSCy Schubert 	tcphdr_t *tcp = NULL;
47613b9b51feSCy Schubert 	int rval, natfailed;
47623b9b51feSCy Schubert 	u_int nflags = 0;
47633b9b51feSCy Schubert 	u_32_t ipa, iph;
47643b9b51feSCy Schubert 	int natadd = 1;
47653b9b51feSCy Schubert 	frentry_t *fr;
47663b9b51feSCy Schubert 	nat_t *nat;
47673b9b51feSCy Schubert 
47683b9b51feSCy Schubert 	if (fin->fin_v == 6) {
47693b9b51feSCy Schubert #ifdef USE_INET6
47708c82b374SCy Schubert 		return (ipf_nat6_checkout(fin, passp));
47713b9b51feSCy Schubert #else
47728c82b374SCy Schubert 		return (0);
47733b9b51feSCy Schubert #endif
47743b9b51feSCy Schubert 	}
47753b9b51feSCy Schubert 
47763b9b51feSCy Schubert 	softc = fin->fin_main_soft;
47773b9b51feSCy Schubert 	softn = softc->ipf_nat_soft;
47783b9b51feSCy Schubert 
47793b9b51feSCy Schubert 	if (softn->ipf_nat_lock != 0)
47808c82b374SCy Schubert 		return (0);
47813b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_rules == 0 &&
47823b9b51feSCy Schubert 	    softn->ipf_nat_instances == NULL)
47838c82b374SCy Schubert 		return (0);
47843b9b51feSCy Schubert 
47853b9b51feSCy Schubert 	natfailed = 0;
47863b9b51feSCy Schubert 	fr = fin->fin_fr;
47873b9b51feSCy Schubert 	sifp = fin->fin_ifp;
47883b9b51feSCy Schubert 	if (fr != NULL) {
47893b9b51feSCy Schubert 		ifp = fr->fr_tifs[fin->fin_rev].fd_ptr;
47903b9b51feSCy Schubert 		if ((ifp != NULL) && (ifp != (void *)-1))
47913b9b51feSCy Schubert 			fin->fin_ifp = ifp;
47923b9b51feSCy Schubert 	}
47933b9b51feSCy Schubert 	ifp = fin->fin_ifp;
47943b9b51feSCy Schubert 
47953b9b51feSCy Schubert 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
47963b9b51feSCy Schubert 		switch (fin->fin_p)
47973b9b51feSCy Schubert 		{
47983b9b51feSCy Schubert 		case IPPROTO_TCP :
47993b9b51feSCy Schubert 			nflags = IPN_TCP;
48003b9b51feSCy Schubert 			break;
48013b9b51feSCy Schubert 		case IPPROTO_UDP :
48023b9b51feSCy Schubert 			nflags = IPN_UDP;
48033b9b51feSCy Schubert 			break;
48043b9b51feSCy Schubert 		case IPPROTO_ICMP :
48053b9b51feSCy Schubert 			icmp = fin->fin_dp;
48063b9b51feSCy Schubert 
48073b9b51feSCy Schubert 			/*
48083b9b51feSCy Schubert 			 * This is an incoming packet, so the destination is
48093b9b51feSCy Schubert 			 * the icmp_id and the source port equals 0
48103b9b51feSCy Schubert 			 */
48113b9b51feSCy Schubert 			if ((fin->fin_flx & FI_ICMPQUERY) != 0)
48123b9b51feSCy Schubert 				nflags = IPN_ICMPQUERY;
48133b9b51feSCy Schubert 			break;
48143b9b51feSCy Schubert 		default :
48153b9b51feSCy Schubert 			break;
48163b9b51feSCy Schubert 		}
48173b9b51feSCy Schubert 
48183b9b51feSCy Schubert 		if ((nflags & IPN_TCPUDP))
48193b9b51feSCy Schubert 			tcp = fin->fin_dp;
48203b9b51feSCy Schubert 	}
48213b9b51feSCy Schubert 
48223b9b51feSCy Schubert 	ipa = fin->fin_saddr;
48233b9b51feSCy Schubert 
48243b9b51feSCy Schubert 	READ_ENTER(&softc->ipf_nat);
48253b9b51feSCy Schubert 
48263b9b51feSCy Schubert 	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
48273b9b51feSCy Schubert 	    (nat = ipf_nat_icmperror(fin, &nflags, NAT_OUTBOUND)))
48283b9b51feSCy Schubert 		/*EMPTY*/;
48293b9b51feSCy Schubert 	else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
48303b9b51feSCy Schubert 		natadd = 0;
48313b9b51feSCy Schubert 	else if ((nat = ipf_nat_outlookup(fin, nflags|NAT_SEARCH,
48323b9b51feSCy Schubert 				      (u_int)fin->fin_p, fin->fin_src,
48333b9b51feSCy Schubert 				      fin->fin_dst))) {
48343b9b51feSCy Schubert 		nflags = nat->nat_flags;
48353b9b51feSCy Schubert 	} else if (fin->fin_off == 0) {
48363b9b51feSCy Schubert 		u_32_t hv, msk, nmsk = 0;
48373b9b51feSCy Schubert 
48383b9b51feSCy Schubert 		/*
48393b9b51feSCy Schubert 		 * If there is no current entry in the nat table for this IP#,
48403b9b51feSCy Schubert 		 * create one for it (if there is a matching rule).
48413b9b51feSCy Schubert 		 */
48423b9b51feSCy Schubert maskloop:
48433b9b51feSCy Schubert 		msk = softn->ipf_nat_map_active_masks[nmsk];
48443b9b51feSCy Schubert 		iph = ipa & msk;
48453b9b51feSCy Schubert 		hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_maprules_sz);
48463b9b51feSCy Schubert retry_roundrobin:
48473b9b51feSCy Schubert 		for (np = softn->ipf_nat_map_rules[hv]; np; np = npnext) {
48483b9b51feSCy Schubert 			npnext = np->in_mnext;
48493b9b51feSCy Schubert 			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
48503b9b51feSCy Schubert 				continue;
48513b9b51feSCy Schubert 			if (np->in_v[0] != 4)
48523b9b51feSCy Schubert 				continue;
48533b9b51feSCy Schubert 			if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p))
48543b9b51feSCy Schubert 				continue;
48553b9b51feSCy Schubert 			if ((np->in_flags & IPN_RF) &&
48563b9b51feSCy Schubert 			    !(np->in_flags & nflags))
48573b9b51feSCy Schubert 				continue;
48583b9b51feSCy Schubert 			if (np->in_flags & IPN_FILTER) {
48593b9b51feSCy Schubert 				switch (ipf_nat_match(fin, np))
48603b9b51feSCy Schubert 				{
48613b9b51feSCy Schubert 				case 0 :
48623b9b51feSCy Schubert 					continue;
48633b9b51feSCy Schubert 				case -1 :
48643b9b51feSCy Schubert 					rval = -3;
48653b9b51feSCy Schubert 					goto outmatchfail;
48663b9b51feSCy Schubert 				case 1 :
48673b9b51feSCy Schubert 				default :
48683b9b51feSCy Schubert 					break;
48693b9b51feSCy Schubert 				}
48703b9b51feSCy Schubert 			} else if ((ipa & np->in_osrcmsk) != np->in_osrcaddr)
48713b9b51feSCy Schubert 				continue;
48723b9b51feSCy Schubert 
48733b9b51feSCy Schubert 			if ((fr != NULL) &&
48743b9b51feSCy Schubert 			    !ipf_matchtag(&np->in_tag, &fr->fr_nattag))
48753b9b51feSCy Schubert 				continue;
48763b9b51feSCy Schubert 
48773b9b51feSCy Schubert 			if (np->in_plabel != -1) {
48783b9b51feSCy Schubert 				if (((np->in_flags & IPN_FILTER) == 0) &&
48793b9b51feSCy Schubert 				    (np->in_odport != fin->fin_data[1]))
48803b9b51feSCy Schubert 					continue;
48813b9b51feSCy Schubert 				if (ipf_proxy_ok(fin, tcp, np) == 0)
48823b9b51feSCy Schubert 					continue;
48833b9b51feSCy Schubert 			}
48843b9b51feSCy Schubert 
48853b9b51feSCy Schubert 			if (np->in_flags & IPN_NO) {
48863b9b51feSCy Schubert 				np->in_hits++;
48873b9b51feSCy Schubert 				break;
48883b9b51feSCy Schubert 			}
48893b9b51feSCy Schubert 			MUTEX_ENTER(&softn->ipf_nat_new);
48903b9b51feSCy Schubert 			/*
48913b9b51feSCy Schubert 			 * If we've matched a round-robin rule but it has
48923b9b51feSCy Schubert 			 * moved in the list since we got it, start over as
48933b9b51feSCy Schubert 			 * this is now no longer correct.
48943b9b51feSCy Schubert 			 */
48953b9b51feSCy Schubert 			if (npnext != np->in_mnext) {
48963b9b51feSCy Schubert 				if ((np->in_flags & IPN_ROUNDR) != 0) {
48973b9b51feSCy Schubert 					MUTEX_EXIT(&softn->ipf_nat_new);
48983b9b51feSCy Schubert 					goto retry_roundrobin;
48993b9b51feSCy Schubert 				}
49003b9b51feSCy Schubert 				npnext = np->in_mnext;
49013b9b51feSCy Schubert 			}
49023b9b51feSCy Schubert 
49033b9b51feSCy Schubert 			nat = ipf_nat_add(fin, np, NULL, nflags, NAT_OUTBOUND);
49043b9b51feSCy Schubert 			MUTEX_EXIT(&softn->ipf_nat_new);
49053b9b51feSCy Schubert 			if (nat != NULL) {
49063b9b51feSCy Schubert 				natfailed = 0;
49073b9b51feSCy Schubert 				break;
49083b9b51feSCy Schubert 			}
49093b9b51feSCy Schubert 			natfailed = -2;
49103b9b51feSCy Schubert 		}
49113b9b51feSCy Schubert 		if ((np == NULL) && (nmsk < softn->ipf_nat_map_max)) {
49123b9b51feSCy Schubert 			nmsk++;
49133b9b51feSCy Schubert 			goto maskloop;
49143b9b51feSCy Schubert 		}
49153b9b51feSCy Schubert 	}
49163b9b51feSCy Schubert 
49173b9b51feSCy Schubert 	if (nat != NULL) {
49183b9b51feSCy Schubert 		rval = ipf_nat_out(fin, nat, natadd, nflags);
49193b9b51feSCy Schubert 		if (rval == 1) {
49203b9b51feSCy Schubert 			MUTEX_ENTER(&nat->nat_lock);
49213b9b51feSCy Schubert 			ipf_nat_update(fin, nat);
49223b9b51feSCy Schubert 			nat->nat_bytes[1] += fin->fin_plen;
49233b9b51feSCy Schubert 			nat->nat_pkts[1]++;
49243b9b51feSCy Schubert 			fin->fin_pktnum = nat->nat_pkts[1];
49253b9b51feSCy Schubert 			MUTEX_EXIT(&nat->nat_lock);
49263b9b51feSCy Schubert 		}
49273b9b51feSCy Schubert 	} else
49283b9b51feSCy Schubert 		rval = natfailed;
49293b9b51feSCy Schubert outmatchfail:
49303b9b51feSCy Schubert 	RWLOCK_EXIT(&softc->ipf_nat);
49313b9b51feSCy Schubert 
49323b9b51feSCy Schubert 	switch (rval)
49333b9b51feSCy Schubert 	{
49343b9b51feSCy Schubert 	case -3 :
49353b9b51feSCy Schubert 		/* ipf_nat_match() failure */
49363b9b51feSCy Schubert 		/* FALLTHROUGH */
49373b9b51feSCy Schubert 	case -2 :
49383b9b51feSCy Schubert 		/* retry_roundrobin loop failure */
49393b9b51feSCy Schubert 		/* FALLTHROUGH */
49403b9b51feSCy Schubert 	case -1 :
49413b9b51feSCy Schubert 		/* proxy failure detected by ipf_nat_out() */
49423b9b51feSCy Schubert 		if (passp != NULL) {
49433b9b51feSCy Schubert 			DT2(frb_natv4out, fr_info_t *, fin, int, rval);
49443b9b51feSCy Schubert 			NBUMPSIDED(1, ns_drop);
49453b9b51feSCy Schubert 			*passp = FR_BLOCK;
49463b9b51feSCy Schubert 			fin->fin_reason = FRB_NATV4;
49473b9b51feSCy Schubert 		}
49483b9b51feSCy Schubert 		fin->fin_flx |= FI_BADNAT;
49493b9b51feSCy Schubert 		NBUMPSIDED(1, ns_badnat);
49503b9b51feSCy Schubert 		rval = -1;	/* We only return -1 on error. */
49513b9b51feSCy Schubert 		break;
49523b9b51feSCy Schubert 	case 0 :
49533b9b51feSCy Schubert 		NBUMPSIDE(1, ns_ignored);
49543b9b51feSCy Schubert 		break;
49553b9b51feSCy Schubert 	case 1 :
49563b9b51feSCy Schubert 		NBUMPSIDE(1, ns_translated);
49573b9b51feSCy Schubert 		break;
49583b9b51feSCy Schubert 	}
49593b9b51feSCy Schubert 	fin->fin_ifp = sifp;
49608c82b374SCy Schubert 	return (rval);
49613b9b51feSCy Schubert }
49623b9b51feSCy Schubert 
49633b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
49643b9b51feSCy Schubert /* Function:    ipf_nat_out                                                 */
49653b9b51feSCy Schubert /* Returns:     int - -1 == packet failed NAT checks so block it,           */
49663b9b51feSCy Schubert /*                     1 == packet was successfully translated.             */
49673b9b51feSCy Schubert /* Parameters:  fin(I)    - pointer to packet information                   */
49683b9b51feSCy Schubert /*              nat(I)    - pointer to NAT structure                        */
49693b9b51feSCy Schubert /*              natadd(I) - flag indicating if it is safe to add frag cache */
49703b9b51feSCy Schubert /*              nflags(I) - NAT flags set for this packet                   */
49713b9b51feSCy Schubert /*                                                                          */
49723b9b51feSCy Schubert /* Translate a packet coming "out" on an interface.                         */
49733b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
49743b9b51feSCy Schubert int
ipf_nat_out(fr_info_t * fin,nat_t * nat,int natadd,u_32_t nflags)4975064a5a95SCy Schubert ipf_nat_out(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags)
49763b9b51feSCy Schubert {
49773b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
49783b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
49793b9b51feSCy Schubert 	icmphdr_t *icmp;
49803b9b51feSCy Schubert 	tcphdr_t *tcp;
49813b9b51feSCy Schubert 	ipnat_t *np;
49823b9b51feSCy Schubert 	int skip;
49833b9b51feSCy Schubert 	int i;
49843b9b51feSCy Schubert 
49853b9b51feSCy Schubert 	tcp = NULL;
49863b9b51feSCy Schubert 	icmp = NULL;
49873b9b51feSCy Schubert 	np = nat->nat_ptr;
49883b9b51feSCy Schubert 
49893b9b51feSCy Schubert 	if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL))
49903b9b51feSCy Schubert 		(void) ipf_frag_natnew(softc, fin, 0, nat);
49913b9b51feSCy Schubert 
49923b9b51feSCy Schubert 	/*
49933b9b51feSCy Schubert 	 * Fix up checksums, not by recalculating them, but
49943b9b51feSCy Schubert 	 * simply computing adjustments.
49953b9b51feSCy Schubert 	 * This is only done for STREAMS based IP implementations where the
49963b9b51feSCy Schubert 	 * checksum has already been calculated by IP.  In all other cases,
49973b9b51feSCy Schubert 	 * IPFilter is called before the checksum needs calculating so there
49983b9b51feSCy Schubert 	 * is no call to modify whatever is in the header now.
49993b9b51feSCy Schubert 	 */
50003b9b51feSCy Schubert 	if (nflags == IPN_ICMPERR) {
50013b9b51feSCy Schubert 		u_32_t s1, s2, sumd, msumd;
50023b9b51feSCy Schubert 
50033b9b51feSCy Schubert 		s1 = LONG_SUM(ntohl(fin->fin_saddr));
50043b9b51feSCy Schubert 		if (nat->nat_dir == NAT_OUTBOUND) {
50053b9b51feSCy Schubert 			s2 = LONG_SUM(ntohl(nat->nat_nsrcaddr));
50063b9b51feSCy Schubert 		} else {
50073b9b51feSCy Schubert 			s2 = LONG_SUM(ntohl(nat->nat_odstaddr));
50083b9b51feSCy Schubert 		}
50093b9b51feSCy Schubert 		CALC_SUMD(s1, s2, sumd);
50103b9b51feSCy Schubert 		msumd = sumd;
50113b9b51feSCy Schubert 
50123b9b51feSCy Schubert 		s1 = LONG_SUM(ntohl(fin->fin_daddr));
50133b9b51feSCy Schubert 		if (nat->nat_dir == NAT_OUTBOUND) {
50143b9b51feSCy Schubert 			s2 = LONG_SUM(ntohl(nat->nat_ndstaddr));
50153b9b51feSCy Schubert 		} else {
50163b9b51feSCy Schubert 			s2 = LONG_SUM(ntohl(nat->nat_osrcaddr));
50173b9b51feSCy Schubert 		}
50183b9b51feSCy Schubert 		CALC_SUMD(s1, s2, sumd);
50193b9b51feSCy Schubert 		msumd += sumd;
50203b9b51feSCy Schubert 
50213b9b51feSCy Schubert 		ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, msumd, 0);
50223b9b51feSCy Schubert 	}
50233b9b51feSCy Schubert #if !defined(_KERNEL) || SOLARIS || \
50243b9b51feSCy Schubert     defined(BRIDGE_IPF) || defined(__FreeBSD__)
50253b9b51feSCy Schubert 	else {
50263b9b51feSCy Schubert 		/*
5027896a0153SCy Schubert 		 * We always do this on FreeBSD because this code doesn't
5028896a0153SCy Schubert 		 * exist in fastforward.
50293b9b51feSCy Schubert 		 */
50303b9b51feSCy Schubert 		switch (nat->nat_dir)
50313b9b51feSCy Schubert 		{
50323b9b51feSCy Schubert 		case NAT_OUTBOUND :
50333b9b51feSCy Schubert 			ipf_fix_outcksum(fin->fin_cksum & FI_CK_L4PART,
50343b9b51feSCy Schubert 					 &fin->fin_ip->ip_sum,
50353b9b51feSCy Schubert 					 nat->nat_ipsumd, 0);
50363b9b51feSCy Schubert 			break;
50373b9b51feSCy Schubert 
50383b9b51feSCy Schubert 		case NAT_INBOUND :
50393b9b51feSCy Schubert 			ipf_fix_incksum(fin->fin_cksum & FI_CK_L4PART,
50403b9b51feSCy Schubert 					&fin->fin_ip->ip_sum,
50413b9b51feSCy Schubert 					nat->nat_ipsumd, 0);
50423b9b51feSCy Schubert 			break;
50433b9b51feSCy Schubert 
50443b9b51feSCy Schubert 		default :
50453b9b51feSCy Schubert 			break;
50463b9b51feSCy Schubert 		}
50473b9b51feSCy Schubert 	}
50483b9b51feSCy Schubert #endif
50493b9b51feSCy Schubert 
50503b9b51feSCy Schubert 	/*
50513b9b51feSCy Schubert 	 * Address assignment is after the checksum modification because
50523b9b51feSCy Schubert 	 * we are using the address in the packet for determining the
50533b9b51feSCy Schubert 	 * correct checksum offset (the ICMP error could be coming from
50543b9b51feSCy Schubert 	 * anyone...)
50553b9b51feSCy Schubert 	 */
50563b9b51feSCy Schubert 	switch (nat->nat_dir)
50573b9b51feSCy Schubert 	{
50583b9b51feSCy Schubert 	case NAT_OUTBOUND :
50593b9b51feSCy Schubert 		fin->fin_ip->ip_src = nat->nat_nsrcip;
50603b9b51feSCy Schubert 		fin->fin_saddr = nat->nat_nsrcaddr;
50613b9b51feSCy Schubert 		fin->fin_ip->ip_dst = nat->nat_ndstip;
50623b9b51feSCy Schubert 		fin->fin_daddr = nat->nat_ndstaddr;
50633b9b51feSCy Schubert 		break;
50643b9b51feSCy Schubert 
50653b9b51feSCy Schubert 	case NAT_INBOUND :
50663b9b51feSCy Schubert 		fin->fin_ip->ip_src = nat->nat_odstip;
50673b9b51feSCy Schubert 		fin->fin_saddr = nat->nat_ndstaddr;
50683b9b51feSCy Schubert 		fin->fin_ip->ip_dst = nat->nat_osrcip;
50693b9b51feSCy Schubert 		fin->fin_daddr = nat->nat_nsrcaddr;
50703b9b51feSCy Schubert 		break;
50713b9b51feSCy Schubert 
50723b9b51feSCy Schubert 	case NAT_DIVERTIN :
50733b9b51feSCy Schubert 	    {
50743b9b51feSCy Schubert 		mb_t *m;
50753b9b51feSCy Schubert 
50763b9b51feSCy Schubert 		skip = ipf_nat_decap(fin, nat);
50773b9b51feSCy Schubert 		if (skip <= 0) {
50783b9b51feSCy Schubert 			NBUMPSIDED(1, ns_decap_fail);
50798c82b374SCy Schubert 			return (-1);
50803b9b51feSCy Schubert 		}
50813b9b51feSCy Schubert 
50823b9b51feSCy Schubert 		m = fin->fin_m;
50833b9b51feSCy Schubert 
50843b9b51feSCy Schubert #if SOLARIS && defined(_KERNEL)
50853b9b51feSCy Schubert 		m->b_rptr += skip;
50863b9b51feSCy Schubert #else
50873b9b51feSCy Schubert 		m->m_data += skip;
50883b9b51feSCy Schubert 		m->m_len -= skip;
50893b9b51feSCy Schubert 
50903b9b51feSCy Schubert # ifdef M_PKTHDR
50913b9b51feSCy Schubert 		if (m->m_flags & M_PKTHDR)
50923b9b51feSCy Schubert 			m->m_pkthdr.len -= skip;
50933b9b51feSCy Schubert # endif
50943b9b51feSCy Schubert #endif
50953b9b51feSCy Schubert 
50963b9b51feSCy Schubert 		MUTEX_ENTER(&nat->nat_lock);
50973b9b51feSCy Schubert 		ipf_nat_update(fin, nat);
50983b9b51feSCy Schubert 		MUTEX_EXIT(&nat->nat_lock);
50993b9b51feSCy Schubert 		fin->fin_flx |= FI_NATED;
51003b9b51feSCy Schubert 		if (np != NULL && np->in_tag.ipt_num[0] != 0)
51013b9b51feSCy Schubert 			fin->fin_nattag = &np->in_tag;
51028c82b374SCy Schubert 		return (1);
51033b9b51feSCy Schubert 		/* NOTREACHED */
51043b9b51feSCy Schubert 	    }
51053b9b51feSCy Schubert 
51063b9b51feSCy Schubert 	case NAT_DIVERTOUT :
51073b9b51feSCy Schubert 	    {
51083b9b51feSCy Schubert 		u_32_t s1, s2, sumd;
51093b9b51feSCy Schubert 		udphdr_t *uh;
51103b9b51feSCy Schubert 		ip_t *ip;
51113b9b51feSCy Schubert 		mb_t *m;
51123b9b51feSCy Schubert 
51133b9b51feSCy Schubert 		m = M_DUP(np->in_divmp);
51143b9b51feSCy Schubert 		if (m == NULL) {
51153b9b51feSCy Schubert 			NBUMPSIDED(1, ns_divert_dup);
51168c82b374SCy Schubert 			return (-1);
51173b9b51feSCy Schubert 		}
51183b9b51feSCy Schubert 
51193b9b51feSCy Schubert 		ip = MTOD(m, ip_t *);
51203b9b51feSCy Schubert 		ip_fillid(ip);
51213b9b51feSCy Schubert 		s2 = ntohs(ip->ip_id);
51223b9b51feSCy Schubert 
51233b9b51feSCy Schubert 		s1 = ip->ip_len;
51243b9b51feSCy Schubert 		ip->ip_len = ntohs(ip->ip_len);
51253b9b51feSCy Schubert 		ip->ip_len += fin->fin_plen;
51263b9b51feSCy Schubert 		ip->ip_len = htons(ip->ip_len);
51273b9b51feSCy Schubert 		s2 += ntohs(ip->ip_len);
51283b9b51feSCy Schubert 		CALC_SUMD(s1, s2, sumd);
51293b9b51feSCy Schubert 
51303b9b51feSCy Schubert 		uh = (udphdr_t *)(ip + 1);
51313b9b51feSCy Schubert 		uh->uh_ulen += fin->fin_plen;
51323b9b51feSCy Schubert 		uh->uh_ulen = htons(uh->uh_ulen);
51333b9b51feSCy Schubert #if !defined(_KERNEL) || SOLARIS || \
51343b9b51feSCy Schubert     defined(BRIDGE_IPF) || defined(__FreeBSD__)
51353b9b51feSCy Schubert 		ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0);
51363b9b51feSCy Schubert #endif
51373b9b51feSCy Schubert 
51383b9b51feSCy Schubert 		PREP_MB_T(fin, m);
51393b9b51feSCy Schubert 
51403b9b51feSCy Schubert 		fin->fin_src = ip->ip_src;
51413b9b51feSCy Schubert 		fin->fin_dst = ip->ip_dst;
51423b9b51feSCy Schubert 		fin->fin_ip = ip;
51433b9b51feSCy Schubert 		fin->fin_plen += sizeof(ip_t) + 8;	/* UDP + IPv4 hdr */
51443b9b51feSCy Schubert 		fin->fin_dlen += sizeof(ip_t) + 8;	/* UDP + IPv4 hdr */
51453b9b51feSCy Schubert 
51463b9b51feSCy Schubert 		nflags &= ~IPN_TCPUDPICMP;
51473b9b51feSCy Schubert 
51483b9b51feSCy Schubert 		break;
51493b9b51feSCy Schubert 	    }
51503b9b51feSCy Schubert 
51513b9b51feSCy Schubert 	default :
51523b9b51feSCy Schubert 		break;
51533b9b51feSCy Schubert 	}
51543b9b51feSCy Schubert 
51553b9b51feSCy Schubert 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
51563b9b51feSCy Schubert 		u_short *csump;
51573b9b51feSCy Schubert 
51583b9b51feSCy Schubert 		if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) {
51593b9b51feSCy Schubert 			tcp = fin->fin_dp;
51603b9b51feSCy Schubert 
51613b9b51feSCy Schubert 			switch (nat->nat_dir)
51623b9b51feSCy Schubert 			{
51633b9b51feSCy Schubert 			case NAT_OUTBOUND :
51643b9b51feSCy Schubert 				tcp->th_sport = nat->nat_nsport;
51653b9b51feSCy Schubert 				fin->fin_data[0] = ntohs(nat->nat_nsport);
51663b9b51feSCy Schubert 				tcp->th_dport = nat->nat_ndport;
51673b9b51feSCy Schubert 				fin->fin_data[1] = ntohs(nat->nat_ndport);
51683b9b51feSCy Schubert 				break;
51693b9b51feSCy Schubert 
51703b9b51feSCy Schubert 			case NAT_INBOUND :
51713b9b51feSCy Schubert 				tcp->th_sport = nat->nat_odport;
51723b9b51feSCy Schubert 				fin->fin_data[0] = ntohs(nat->nat_odport);
51733b9b51feSCy Schubert 				tcp->th_dport = nat->nat_osport;
51743b9b51feSCy Schubert 				fin->fin_data[1] = ntohs(nat->nat_osport);
51753b9b51feSCy Schubert 				break;
51763b9b51feSCy Schubert 			}
51773b9b51feSCy Schubert 		}
51783b9b51feSCy Schubert 
51793b9b51feSCy Schubert 		if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) {
51803b9b51feSCy Schubert 			icmp = fin->fin_dp;
51813b9b51feSCy Schubert 			icmp->icmp_id = nat->nat_nicmpid;
51823b9b51feSCy Schubert 		}
51833b9b51feSCy Schubert 
51843b9b51feSCy Schubert 		csump = ipf_nat_proto(fin, nat, nflags);
51853b9b51feSCy Schubert 
51863b9b51feSCy Schubert 		/*
51873b9b51feSCy Schubert 		 * The above comments do not hold for layer 4 (or higher)
51883b9b51feSCy Schubert 		 * checksums...
51893b9b51feSCy Schubert 		 */
51903b9b51feSCy Schubert 		if (csump != NULL) {
51913b9b51feSCy Schubert 			if (nat->nat_dir == NAT_OUTBOUND)
51923b9b51feSCy Schubert 				ipf_fix_outcksum(fin->fin_cksum, csump,
51933b9b51feSCy Schubert 						 nat->nat_sumd[0],
51943b9b51feSCy Schubert 						 nat->nat_sumd[1] +
51953b9b51feSCy Schubert 						 fin->fin_dlen);
51963b9b51feSCy Schubert 			else
51973b9b51feSCy Schubert 				ipf_fix_incksum(fin->fin_cksum, csump,
51983b9b51feSCy Schubert 						nat->nat_sumd[0],
51993b9b51feSCy Schubert 						nat->nat_sumd[1] +
52003b9b51feSCy Schubert 						fin->fin_dlen);
52013b9b51feSCy Schubert 		}
52023b9b51feSCy Schubert 	}
52033b9b51feSCy Schubert 
52043b9b51feSCy Schubert 	ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
52053b9b51feSCy Schubert 	/* ------------------------------------------------------------- */
52063b9b51feSCy Schubert 	/* A few quick notes:                                            */
52073b9b51feSCy Schubert 	/*      Following are test conditions prior to calling the       */
52083b9b51feSCy Schubert 	/*      ipf_proxy_check routine.                                 */
52093b9b51feSCy Schubert 	/*                                                               */
52103b9b51feSCy Schubert 	/*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
52113b9b51feSCy Schubert 	/*      with a redirect rule, we attempt to match the packet's   */
52123b9b51feSCy Schubert 	/*      source port against in_dport, otherwise we'd compare the */
52133b9b51feSCy Schubert 	/*      packet's destination.                                    */
52143b9b51feSCy Schubert 	/* ------------------------------------------------------------- */
52153b9b51feSCy Schubert 	if ((np != NULL) && (np->in_apr != NULL)) {
52163b9b51feSCy Schubert 		i = ipf_proxy_check(fin, nat);
52173b9b51feSCy Schubert 		if (i == -1) {
52183b9b51feSCy Schubert 			NBUMPSIDED(1, ns_ipf_proxy_fail);
52193b9b51feSCy Schubert 		}
52203b9b51feSCy Schubert 	} else {
52213b9b51feSCy Schubert 		i = 1;
52223b9b51feSCy Schubert 	}
52233b9b51feSCy Schubert 	fin->fin_flx |= FI_NATED;
52248c82b374SCy Schubert 	return (i);
52253b9b51feSCy Schubert }
52263b9b51feSCy Schubert 
52273b9b51feSCy Schubert 
52283b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
52293b9b51feSCy Schubert /* Function:    ipf_nat_checkin                                             */
52303b9b51feSCy Schubert /* Returns:     int - -1 == packet failed NAT checks so block it,           */
52313b9b51feSCy Schubert /*                     0 == no packet translation occurred,                 */
52323b9b51feSCy Schubert /*                     1 == packet was successfully translated.             */
52333b9b51feSCy Schubert /* Parameters:  fin(I)   - pointer to packet information                    */
52343b9b51feSCy Schubert /*              passp(I) - pointer to filtering result flags                */
52353b9b51feSCy Schubert /*                                                                          */
52363b9b51feSCy Schubert /* Check to see if an incoming packet should be changed.  ICMP packets are  */
52373b9b51feSCy Schubert /* first checked to see if they match an existing entry (if an error),      */
52383b9b51feSCy Schubert /* otherwise a search of the current NAT table is made.  If neither results */
52393b9b51feSCy Schubert /* in a match then a search for a matching NAT rule is made.  Create a new  */
52403b9b51feSCy Schubert /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
52413b9b51feSCy Schubert /* packet header(s) as required.                                            */
52423b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
52433b9b51feSCy Schubert int
ipf_nat_checkin(fr_info_t * fin,u_32_t * passp)5244064a5a95SCy Schubert ipf_nat_checkin(fr_info_t *fin, u_32_t *passp)
52453b9b51feSCy Schubert {
52463b9b51feSCy Schubert 	ipf_main_softc_t *softc;
52473b9b51feSCy Schubert 	ipf_nat_softc_t *softn;
52483b9b51feSCy Schubert 	u_int nflags, natadd;
52493b9b51feSCy Schubert 	ipnat_t *np, *npnext;
52503b9b51feSCy Schubert 	int rval, natfailed;
52513b9b51feSCy Schubert 	struct ifnet *ifp;
52523b9b51feSCy Schubert 	struct in_addr in;
52533b9b51feSCy Schubert 	icmphdr_t *icmp;
52543b9b51feSCy Schubert 	tcphdr_t *tcp;
52553b9b51feSCy Schubert 	u_short dport;
52563b9b51feSCy Schubert 	nat_t *nat;
52573b9b51feSCy Schubert 	u_32_t iph;
52583b9b51feSCy Schubert 
52593b9b51feSCy Schubert 	softc = fin->fin_main_soft;
52603b9b51feSCy Schubert 	softn = softc->ipf_nat_soft;
52613b9b51feSCy Schubert 
52623b9b51feSCy Schubert 	if (softn->ipf_nat_lock != 0)
52638c82b374SCy Schubert 		return (0);
52643b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_rules == 0 &&
52653b9b51feSCy Schubert 	    softn->ipf_nat_instances == NULL)
52668c82b374SCy Schubert 		return (0);
52673b9b51feSCy Schubert 
52683b9b51feSCy Schubert 	tcp = NULL;
52693b9b51feSCy Schubert 	icmp = NULL;
52703b9b51feSCy Schubert 	dport = 0;
52713b9b51feSCy Schubert 	natadd = 1;
52723b9b51feSCy Schubert 	nflags = 0;
52733b9b51feSCy Schubert 	natfailed = 0;
52743b9b51feSCy Schubert 	ifp = fin->fin_ifp;
52753b9b51feSCy Schubert 
52763b9b51feSCy Schubert 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
52773b9b51feSCy Schubert 		switch (fin->fin_p)
52783b9b51feSCy Schubert 		{
52793b9b51feSCy Schubert 		case IPPROTO_TCP :
52803b9b51feSCy Schubert 			nflags = IPN_TCP;
52813b9b51feSCy Schubert 			break;
52823b9b51feSCy Schubert 		case IPPROTO_UDP :
52833b9b51feSCy Schubert 			nflags = IPN_UDP;
52843b9b51feSCy Schubert 			break;
52853b9b51feSCy Schubert 		case IPPROTO_ICMP :
52863b9b51feSCy Schubert 			icmp = fin->fin_dp;
52873b9b51feSCy Schubert 
52883b9b51feSCy Schubert 			/*
52893b9b51feSCy Schubert 			 * This is an incoming packet, so the destination is
52903b9b51feSCy Schubert 			 * the icmp_id and the source port equals 0
52913b9b51feSCy Schubert 			 */
52923b9b51feSCy Schubert 			if ((fin->fin_flx & FI_ICMPQUERY) != 0) {
52933b9b51feSCy Schubert 				nflags = IPN_ICMPQUERY;
52943b9b51feSCy Schubert 				dport = icmp->icmp_id;
52953b9b51feSCy Schubert 			} break;
52963b9b51feSCy Schubert 		default :
52973b9b51feSCy Schubert 			break;
52983b9b51feSCy Schubert 		}
52993b9b51feSCy Schubert 
53003b9b51feSCy Schubert 		if ((nflags & IPN_TCPUDP)) {
53013b9b51feSCy Schubert 			tcp = fin->fin_dp;
53023b9b51feSCy Schubert 			dport = fin->fin_data[1];
53033b9b51feSCy Schubert 		}
53043b9b51feSCy Schubert 	}
53053b9b51feSCy Schubert 
53063b9b51feSCy Schubert 	in = fin->fin_dst;
53073b9b51feSCy Schubert 
53083b9b51feSCy Schubert 	READ_ENTER(&softc->ipf_nat);
53093b9b51feSCy Schubert 
53103b9b51feSCy Schubert 	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
53113b9b51feSCy Schubert 	    (nat = ipf_nat_icmperror(fin, &nflags, NAT_INBOUND)))
53123b9b51feSCy Schubert 		/*EMPTY*/;
53133b9b51feSCy Schubert 	else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
53143b9b51feSCy Schubert 		natadd = 0;
53153b9b51feSCy Schubert 	else if ((nat = ipf_nat_inlookup(fin, nflags|NAT_SEARCH,
53163b9b51feSCy Schubert 					 (u_int)fin->fin_p,
53173b9b51feSCy Schubert 					 fin->fin_src, in))) {
53183b9b51feSCy Schubert 		nflags = nat->nat_flags;
53193b9b51feSCy Schubert 	} else if (fin->fin_off == 0) {
53203b9b51feSCy Schubert 		u_32_t hv, msk, rmsk = 0;
53213b9b51feSCy Schubert 
53223b9b51feSCy Schubert 		/*
53233b9b51feSCy Schubert 		 * If there is no current entry in the nat table for this IP#,
53243b9b51feSCy Schubert 		 * create one for it (if there is a matching rule).
53253b9b51feSCy Schubert 		 */
53263b9b51feSCy Schubert maskloop:
53273b9b51feSCy Schubert 		msk = softn->ipf_nat_rdr_active_masks[rmsk];
53283b9b51feSCy Schubert 		iph = in.s_addr & msk;
53293b9b51feSCy Schubert 		hv = NAT_HASH_FN(iph, 0, softn->ipf_nat_rdrrules_sz);
53303b9b51feSCy Schubert retry_roundrobin:
53313b9b51feSCy Schubert 		/* TRACE (iph,msk,rmsk,hv,softn->ipf_nat_rdrrules_sz) */
53323b9b51feSCy Schubert 		for (np = softn->ipf_nat_rdr_rules[hv]; np; np = npnext) {
53333b9b51feSCy Schubert 			npnext = np->in_rnext;
53343b9b51feSCy Schubert 			if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
53353b9b51feSCy Schubert 				continue;
53363b9b51feSCy Schubert 			if (np->in_v[0] != 4)
53373b9b51feSCy Schubert 				continue;
53383b9b51feSCy Schubert 			if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p))
53393b9b51feSCy Schubert 				continue;
53403b9b51feSCy Schubert 			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
53413b9b51feSCy Schubert 				continue;
53423b9b51feSCy Schubert 			if (np->in_flags & IPN_FILTER) {
53433b9b51feSCy Schubert 				switch (ipf_nat_match(fin, np))
53443b9b51feSCy Schubert 				{
53453b9b51feSCy Schubert 				case 0 :
53463b9b51feSCy Schubert 					continue;
53473b9b51feSCy Schubert 				case -1 :
53483b9b51feSCy Schubert 					rval = -3;
53493b9b51feSCy Schubert 					goto inmatchfail;
53503b9b51feSCy Schubert 				case 1 :
53513b9b51feSCy Schubert 				default :
53523b9b51feSCy Schubert 					break;
53533b9b51feSCy Schubert 				}
53543b9b51feSCy Schubert 			} else {
53553b9b51feSCy Schubert 				if ((in.s_addr & np->in_odstmsk) !=
53563b9b51feSCy Schubert 				    np->in_odstaddr)
53573b9b51feSCy Schubert 					continue;
53583b9b51feSCy Schubert 				if (np->in_odport &&
53593b9b51feSCy Schubert 				    ((np->in_dtop < dport) ||
53603b9b51feSCy Schubert 				     (dport < np->in_odport)))
53613b9b51feSCy Schubert 					continue;
53623b9b51feSCy Schubert 			}
53633b9b51feSCy Schubert 
53643b9b51feSCy Schubert 			if (np->in_plabel != -1) {
53653b9b51feSCy Schubert 				if (!ipf_proxy_ok(fin, tcp, np)) {
53663b9b51feSCy Schubert 					continue;
53673b9b51feSCy Schubert 				}
53683b9b51feSCy Schubert 			}
53693b9b51feSCy Schubert 
53703b9b51feSCy Schubert 			if (np->in_flags & IPN_NO) {
53713b9b51feSCy Schubert 				np->in_hits++;
53723b9b51feSCy Schubert 				break;
53733b9b51feSCy Schubert 			}
53743b9b51feSCy Schubert 
53753b9b51feSCy Schubert 			MUTEX_ENTER(&softn->ipf_nat_new);
53763b9b51feSCy Schubert 			/*
53773b9b51feSCy Schubert 			 * If we've matched a round-robin rule but it has
53783b9b51feSCy Schubert 			 * moved in the list since we got it, start over as
53793b9b51feSCy Schubert 			 * this is now no longer correct.
53803b9b51feSCy Schubert 			 */
53813b9b51feSCy Schubert 			if (npnext != np->in_rnext) {
53823b9b51feSCy Schubert 				if ((np->in_flags & IPN_ROUNDR) != 0) {
53833b9b51feSCy Schubert 					MUTEX_EXIT(&softn->ipf_nat_new);
53843b9b51feSCy Schubert 					goto retry_roundrobin;
53853b9b51feSCy Schubert 				}
53863b9b51feSCy Schubert 				npnext = np->in_rnext;
53873b9b51feSCy Schubert 			}
53883b9b51feSCy Schubert 
53893b9b51feSCy Schubert 			nat = ipf_nat_add(fin, np, NULL, nflags, NAT_INBOUND);
53903b9b51feSCy Schubert 			MUTEX_EXIT(&softn->ipf_nat_new);
53913b9b51feSCy Schubert 			if (nat != NULL) {
53923b9b51feSCy Schubert 				natfailed = 0;
53933b9b51feSCy Schubert 				break;
53943b9b51feSCy Schubert 			}
53953b9b51feSCy Schubert 			natfailed = -2;
53963b9b51feSCy Schubert 		}
53973b9b51feSCy Schubert 		if ((np == NULL) && (rmsk < softn->ipf_nat_rdr_max)) {
53983b9b51feSCy Schubert 			rmsk++;
53993b9b51feSCy Schubert 			goto maskloop;
54003b9b51feSCy Schubert 		}
54013b9b51feSCy Schubert 	}
54023b9b51feSCy Schubert 
54033b9b51feSCy Schubert 	if (nat != NULL) {
54043b9b51feSCy Schubert 		rval = ipf_nat_in(fin, nat, natadd, nflags);
54053b9b51feSCy Schubert 		if (rval == 1) {
54063b9b51feSCy Schubert 			MUTEX_ENTER(&nat->nat_lock);
54073b9b51feSCy Schubert 			ipf_nat_update(fin, nat);
54083b9b51feSCy Schubert 			nat->nat_bytes[0] += fin->fin_plen;
54093b9b51feSCy Schubert 			nat->nat_pkts[0]++;
54103b9b51feSCy Schubert 			fin->fin_pktnum = nat->nat_pkts[0];
54113b9b51feSCy Schubert 			MUTEX_EXIT(&nat->nat_lock);
54123b9b51feSCy Schubert 		}
54133b9b51feSCy Schubert 	} else
54143b9b51feSCy Schubert 		rval = natfailed;
54153b9b51feSCy Schubert inmatchfail:
54163b9b51feSCy Schubert 	RWLOCK_EXIT(&softc->ipf_nat);
54173b9b51feSCy Schubert 
5418b6f072f7SCy Schubert 	DT2(frb_natv4in, fr_info_t *, fin, int, rval);
54193b9b51feSCy Schubert 	switch (rval)
54203b9b51feSCy Schubert 	{
54213b9b51feSCy Schubert 	case -3 :
54223b9b51feSCy Schubert 		/* ipf_nat_match() failure */
54233b9b51feSCy Schubert 		/* FALLTHROUGH */
54243b9b51feSCy Schubert 	case -2 :
54253b9b51feSCy Schubert 		/* retry_roundrobin loop failure */
54263b9b51feSCy Schubert 		/* FALLTHROUGH */
54273b9b51feSCy Schubert 	case -1 :
54283b9b51feSCy Schubert 		/* proxy failure detected by ipf_nat_in() */
54293b9b51feSCy Schubert 		if (passp != NULL) {
54303b9b51feSCy Schubert 			NBUMPSIDED(0, ns_drop);
54313b9b51feSCy Schubert 			*passp = FR_BLOCK;
54323b9b51feSCy Schubert 			fin->fin_reason = FRB_NATV4;
54333b9b51feSCy Schubert 		}
54343b9b51feSCy Schubert 		fin->fin_flx |= FI_BADNAT;
54353b9b51feSCy Schubert 		NBUMPSIDED(0, ns_badnat);
54363b9b51feSCy Schubert 		rval = -1;	/* We only return -1 on error. */
54373b9b51feSCy Schubert 		break;
54383b9b51feSCy Schubert 	case 0 :
54393b9b51feSCy Schubert 		NBUMPSIDE(0, ns_ignored);
54403b9b51feSCy Schubert 		break;
54413b9b51feSCy Schubert 	case 1 :
54423b9b51feSCy Schubert 		NBUMPSIDE(0, ns_translated);
54433b9b51feSCy Schubert 		break;
54443b9b51feSCy Schubert 	}
54458c82b374SCy Schubert 	return (rval);
54463b9b51feSCy Schubert }
54473b9b51feSCy Schubert 
54483b9b51feSCy Schubert 
54493b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
54503b9b51feSCy Schubert /* Function:    ipf_nat_in                                                  */
54513b9b51feSCy Schubert /* Returns:     int - -1 == packet failed NAT checks so block it,           */
54523b9b51feSCy Schubert /*                     1 == packet was successfully translated.             */
54533b9b51feSCy Schubert /* Parameters:  fin(I)    - pointer to packet information                   */
54543b9b51feSCy Schubert /*              nat(I)    - pointer to NAT structure                        */
54553b9b51feSCy Schubert /*              natadd(I) - flag indicating if it is safe to add frag cache */
54563b9b51feSCy Schubert /*              nflags(I) - NAT flags set for this packet                   */
54573b9b51feSCy Schubert /* Locks Held:  ipf_nat(READ)                                               */
54583b9b51feSCy Schubert /*                                                                          */
54593b9b51feSCy Schubert /* Translate a packet coming "in" on an interface.                          */
54603b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
54613b9b51feSCy Schubert int
ipf_nat_in(fr_info_t * fin,nat_t * nat,int natadd,u_32_t nflags)5462064a5a95SCy Schubert ipf_nat_in(fr_info_t *fin, nat_t *nat, int natadd, u_32_t nflags)
54633b9b51feSCy Schubert {
54643b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
54653b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
54663b9b51feSCy Schubert 	u_32_t sumd, ipsumd, sum1, sum2;
54673b9b51feSCy Schubert 	icmphdr_t *icmp;
54683b9b51feSCy Schubert 	tcphdr_t *tcp;
54693b9b51feSCy Schubert 	ipnat_t *np;
54703b9b51feSCy Schubert 	int skip;
54713b9b51feSCy Schubert 	int i;
54723b9b51feSCy Schubert 
54733b9b51feSCy Schubert 	tcp = NULL;
54743b9b51feSCy Schubert 	np = nat->nat_ptr;
54753b9b51feSCy Schubert 	fin->fin_fr = nat->nat_fr;
54763b9b51feSCy Schubert 
54773b9b51feSCy Schubert 	if (np != NULL) {
54783b9b51feSCy Schubert 		if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
54793b9b51feSCy Schubert 			(void) ipf_frag_natnew(softc, fin, 0, nat);
54803b9b51feSCy Schubert 
54813b9b51feSCy Schubert 	/* ------------------------------------------------------------- */
54823b9b51feSCy Schubert 	/* A few quick notes:                                            */
54833b9b51feSCy Schubert 	/*      Following are test conditions prior to calling the       */
54843b9b51feSCy Schubert 	/*      ipf_proxy_check routine.                                 */
54853b9b51feSCy Schubert 	/*                                                               */
54863b9b51feSCy Schubert 	/*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
54873b9b51feSCy Schubert 	/*      with a map rule, we attempt to match the packet's        */
54883b9b51feSCy Schubert 	/*      source port against in_dport, otherwise we'd compare the */
54893b9b51feSCy Schubert 	/*      packet's destination.                                    */
54903b9b51feSCy Schubert 	/* ------------------------------------------------------------- */
54913b9b51feSCy Schubert 		if (np->in_apr != NULL) {
54923b9b51feSCy Schubert 			i = ipf_proxy_check(fin, nat);
54933b9b51feSCy Schubert 			if (i == -1) {
54943b9b51feSCy Schubert 				NBUMPSIDED(0, ns_ipf_proxy_fail);
54958c82b374SCy Schubert 				return (-1);
54963b9b51feSCy Schubert 			}
54973b9b51feSCy Schubert 		}
54983b9b51feSCy Schubert 	}
54993b9b51feSCy Schubert 
55003b9b51feSCy Schubert 	ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
55013b9b51feSCy Schubert 
55023b9b51feSCy Schubert 	ipsumd = nat->nat_ipsumd;
55033b9b51feSCy Schubert 	/*
55043b9b51feSCy Schubert 	 * Fix up checksums, not by recalculating them, but
55053b9b51feSCy Schubert 	 * simply computing adjustments.
55063b9b51feSCy Schubert 	 * Why only do this for some platforms on inbound packets ?
55073b9b51feSCy Schubert 	 * Because for those that it is done, IP processing is yet to happen
55083b9b51feSCy Schubert 	 * and so the IPv4 header checksum has not yet been evaluated.
55093b9b51feSCy Schubert 	 * Perhaps it should always be done for the benefit of things like
55103b9b51feSCy Schubert 	 * fast forwarding (so that it doesn't need to be recomputed) but with
55113b9b51feSCy Schubert 	 * header checksum offloading, perhaps it is a moot point.
55123b9b51feSCy Schubert 	 */
55133b9b51feSCy Schubert 
55143b9b51feSCy Schubert 	switch (nat->nat_dir)
55153b9b51feSCy Schubert 	{
55163b9b51feSCy Schubert 	case NAT_INBOUND :
55173b9b51feSCy Schubert 		if ((fin->fin_flx & FI_ICMPERR) == 0) {
55183b9b51feSCy Schubert 			fin->fin_ip->ip_src = nat->nat_nsrcip;
55193b9b51feSCy Schubert 			fin->fin_saddr = nat->nat_nsrcaddr;
55203b9b51feSCy Schubert 		} else {
55213b9b51feSCy Schubert 			sum1 = nat->nat_osrcaddr;
55223b9b51feSCy Schubert 			sum2 = nat->nat_nsrcaddr;
55233b9b51feSCy Schubert 			CALC_SUMD(sum1, sum2, sumd);
55243b9b51feSCy Schubert 			ipsumd -= sumd;
55253b9b51feSCy Schubert 		}
55263b9b51feSCy Schubert 		fin->fin_ip->ip_dst = nat->nat_ndstip;
55273b9b51feSCy Schubert 		fin->fin_daddr = nat->nat_ndstaddr;
55283b9b51feSCy Schubert #if !defined(_KERNEL) || SOLARIS
55293b9b51feSCy Schubert 		ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, ipsumd, 0);
55303b9b51feSCy Schubert #endif
55313b9b51feSCy Schubert 		break;
55323b9b51feSCy Schubert 
55333b9b51feSCy Schubert 	case NAT_OUTBOUND :
55343b9b51feSCy Schubert 		if ((fin->fin_flx & FI_ICMPERR) == 0) {
55353b9b51feSCy Schubert 			fin->fin_ip->ip_src = nat->nat_odstip;
55363b9b51feSCy Schubert 			fin->fin_saddr = nat->nat_odstaddr;
55373b9b51feSCy Schubert 		} else {
55383b9b51feSCy Schubert 			sum1 = nat->nat_odstaddr;
55393b9b51feSCy Schubert 			sum2 = nat->nat_ndstaddr;
55403b9b51feSCy Schubert 			CALC_SUMD(sum1, sum2, sumd);
55413b9b51feSCy Schubert 			ipsumd -= sumd;
55423b9b51feSCy Schubert 		}
55433b9b51feSCy Schubert 		fin->fin_ip->ip_dst = nat->nat_osrcip;
55443b9b51feSCy Schubert 		fin->fin_daddr = nat->nat_osrcaddr;
55453b9b51feSCy Schubert #if !defined(_KERNEL) || SOLARIS
55463b9b51feSCy Schubert 		ipf_fix_incksum(0, &fin->fin_ip->ip_sum, ipsumd, 0);
55473b9b51feSCy Schubert #endif
55483b9b51feSCy Schubert 		break;
55493b9b51feSCy Schubert 
55503b9b51feSCy Schubert 	case NAT_DIVERTIN :
55513b9b51feSCy Schubert 	    {
55523b9b51feSCy Schubert 		udphdr_t *uh;
55533b9b51feSCy Schubert 		ip_t *ip;
55543b9b51feSCy Schubert 		mb_t *m;
55553b9b51feSCy Schubert 
55563b9b51feSCy Schubert 		m = M_DUP(np->in_divmp);
55573b9b51feSCy Schubert 		if (m == NULL) {
55583b9b51feSCy Schubert 			NBUMPSIDED(0, ns_divert_dup);
55598c82b374SCy Schubert 			return (-1);
55603b9b51feSCy Schubert 		}
55613b9b51feSCy Schubert 
55623b9b51feSCy Schubert 		ip = MTOD(m, ip_t *);
55633b9b51feSCy Schubert 		ip_fillid(ip);
55643b9b51feSCy Schubert 		sum1 = ntohs(ip->ip_len);
55653b9b51feSCy Schubert 		ip->ip_len = ntohs(ip->ip_len);
55663b9b51feSCy Schubert 		ip->ip_len += fin->fin_plen;
55673b9b51feSCy Schubert 		ip->ip_len = htons(ip->ip_len);
55683b9b51feSCy Schubert 
55693b9b51feSCy Schubert 		uh = (udphdr_t *)(ip + 1);
55703b9b51feSCy Schubert 		uh->uh_ulen += fin->fin_plen;
55713b9b51feSCy Schubert 		uh->uh_ulen = htons(uh->uh_ulen);
55723b9b51feSCy Schubert 
55733b9b51feSCy Schubert 		sum2 = ntohs(ip->ip_id) + ntohs(ip->ip_len);
55743b9b51feSCy Schubert 		sum2 += ntohs(ip->ip_off) & IP_DF;
55753b9b51feSCy Schubert 		CALC_SUMD(sum1, sum2, sumd);
55763b9b51feSCy Schubert 
55773b9b51feSCy Schubert #if !defined(_KERNEL) || SOLARIS
55783b9b51feSCy Schubert 		ipf_fix_outcksum(0, &ip->ip_sum, sumd, 0);
55793b9b51feSCy Schubert #endif
55803b9b51feSCy Schubert 		PREP_MB_T(fin, m);
55813b9b51feSCy Schubert 
55823b9b51feSCy Schubert 		fin->fin_ip = ip;
55833b9b51feSCy Schubert 		fin->fin_plen += sizeof(ip_t) + 8;	/* UDP + new IPv4 hdr */
55843b9b51feSCy Schubert 		fin->fin_dlen += sizeof(ip_t) + 8;	/* UDP + old IPv4 hdr */
55853b9b51feSCy Schubert 
55863b9b51feSCy Schubert 		nflags &= ~IPN_TCPUDPICMP;
55873b9b51feSCy Schubert 
55883b9b51feSCy Schubert 		break;
55893b9b51feSCy Schubert 	    }
55903b9b51feSCy Schubert 
55913b9b51feSCy Schubert 	case NAT_DIVERTOUT :
55923b9b51feSCy Schubert 	    {
55933b9b51feSCy Schubert 		mb_t *m;
55943b9b51feSCy Schubert 
55953b9b51feSCy Schubert 		skip = ipf_nat_decap(fin, nat);
55963b9b51feSCy Schubert 		if (skip <= 0) {
55973b9b51feSCy Schubert 			NBUMPSIDED(0, ns_decap_fail);
55988c82b374SCy Schubert 			return (-1);
55993b9b51feSCy Schubert 		}
56003b9b51feSCy Schubert 
56013b9b51feSCy Schubert 		m = fin->fin_m;
56023b9b51feSCy Schubert 
56033b9b51feSCy Schubert #if SOLARIS && defined(_KERNEL)
56043b9b51feSCy Schubert 		m->b_rptr += skip;
56053b9b51feSCy Schubert #else
56063b9b51feSCy Schubert 		m->m_data += skip;
56073b9b51feSCy Schubert 		m->m_len -= skip;
56083b9b51feSCy Schubert 
56093b9b51feSCy Schubert # ifdef M_PKTHDR
56103b9b51feSCy Schubert 		if (m->m_flags & M_PKTHDR)
56113b9b51feSCy Schubert 			m->m_pkthdr.len -= skip;
56123b9b51feSCy Schubert # endif
56133b9b51feSCy Schubert #endif
56143b9b51feSCy Schubert 
56153b9b51feSCy Schubert 		ipf_nat_update(fin, nat);
56163b9b51feSCy Schubert 		nflags &= ~IPN_TCPUDPICMP;
56173b9b51feSCy Schubert 		fin->fin_flx |= FI_NATED;
56183b9b51feSCy Schubert 		if (np != NULL && np->in_tag.ipt_num[0] != 0)
56193b9b51feSCy Schubert 			fin->fin_nattag = &np->in_tag;
56208c82b374SCy Schubert 		return (1);
56213b9b51feSCy Schubert 		/* NOTREACHED */
56223b9b51feSCy Schubert 	    }
56233b9b51feSCy Schubert 	}
56243b9b51feSCy Schubert 	if (nflags & IPN_TCPUDP)
56253b9b51feSCy Schubert 		tcp = fin->fin_dp;
56263b9b51feSCy Schubert 
56273b9b51feSCy Schubert 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
56283b9b51feSCy Schubert 		u_short *csump;
56293b9b51feSCy Schubert 
56303b9b51feSCy Schubert 		if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) {
56313b9b51feSCy Schubert 			switch (nat->nat_dir)
56323b9b51feSCy Schubert 			{
56333b9b51feSCy Schubert 			case NAT_INBOUND :
56343b9b51feSCy Schubert 				tcp->th_sport = nat->nat_nsport;
56353b9b51feSCy Schubert 				fin->fin_data[0] = ntohs(nat->nat_nsport);
56363b9b51feSCy Schubert 				tcp->th_dport = nat->nat_ndport;
56373b9b51feSCy Schubert 				fin->fin_data[1] = ntohs(nat->nat_ndport);
56383b9b51feSCy Schubert 				break;
56393b9b51feSCy Schubert 
56403b9b51feSCy Schubert 			case NAT_OUTBOUND :
56413b9b51feSCy Schubert 				tcp->th_sport = nat->nat_odport;
56423b9b51feSCy Schubert 				fin->fin_data[0] = ntohs(nat->nat_odport);
56433b9b51feSCy Schubert 				tcp->th_dport = nat->nat_osport;
56443b9b51feSCy Schubert 				fin->fin_data[1] = ntohs(nat->nat_osport);
56453b9b51feSCy Schubert 				break;
56463b9b51feSCy Schubert 			}
56473b9b51feSCy Schubert 		}
56483b9b51feSCy Schubert 
56493b9b51feSCy Schubert 
56503b9b51feSCy Schubert 		if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) {
56513b9b51feSCy Schubert 			icmp = fin->fin_dp;
56523b9b51feSCy Schubert 
56533b9b51feSCy Schubert 			icmp->icmp_id = nat->nat_nicmpid;
56543b9b51feSCy Schubert 		}
56553b9b51feSCy Schubert 
56563b9b51feSCy Schubert 		csump = ipf_nat_proto(fin, nat, nflags);
56573b9b51feSCy Schubert 
56583b9b51feSCy Schubert 		/*
56593b9b51feSCy Schubert 		 * The above comments do not hold for layer 4 (or higher)
56603b9b51feSCy Schubert 		 * checksums...
56613b9b51feSCy Schubert 		 */
56623b9b51feSCy Schubert 		if (csump != NULL) {
56633b9b51feSCy Schubert 			if (nat->nat_dir == NAT_OUTBOUND)
56643b9b51feSCy Schubert 				ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0);
56653b9b51feSCy Schubert 			else
56663b9b51feSCy Schubert 				ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0);
56673b9b51feSCy Schubert 		}
56683b9b51feSCy Schubert 	}
56693b9b51feSCy Schubert 
56703b9b51feSCy Schubert 	fin->fin_flx |= FI_NATED;
56713b9b51feSCy Schubert 	if (np != NULL && np->in_tag.ipt_num[0] != 0)
56723b9b51feSCy Schubert 		fin->fin_nattag = &np->in_tag;
56738c82b374SCy Schubert 	return (1);
56743b9b51feSCy Schubert }
56753b9b51feSCy Schubert 
56763b9b51feSCy Schubert 
56773b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
56783b9b51feSCy Schubert /* Function:    ipf_nat_proto                                               */
56793b9b51feSCy Schubert /* Returns:     u_short* - pointer to transport header checksum to update,  */
56803b9b51feSCy Schubert /*                         NULL if the transport protocol is not recognised */
56813b9b51feSCy Schubert /*                         as needing a checksum update.                    */
56823b9b51feSCy Schubert /* Parameters:  fin(I)    - pointer to packet information                   */
56833b9b51feSCy Schubert /*              nat(I)    - pointer to NAT structure                        */
56843b9b51feSCy Schubert /*              nflags(I) - NAT flags set for this packet                   */
56853b9b51feSCy Schubert /*                                                                          */
56863b9b51feSCy Schubert /* Return the pointer to the checksum field for each protocol so understood.*/
56873b9b51feSCy Schubert /* If support for making other changes to a protocol header is required,    */
56883b9b51feSCy Schubert /* that is not strictly 'address' translation, such as clamping the MSS in  */
56893b9b51feSCy Schubert /* TCP down to a specific value, then do it from here.                      */
56903b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
56913b9b51feSCy Schubert u_short *
ipf_nat_proto(fr_info_t * fin,nat_t * nat,u_int nflags)5692064a5a95SCy Schubert ipf_nat_proto(fr_info_t *fin, nat_t *nat, u_int nflags)
56933b9b51feSCy Schubert {
56943b9b51feSCy Schubert 	icmphdr_t *icmp;
56953b9b51feSCy Schubert 	u_short *csump;
56963b9b51feSCy Schubert 	tcphdr_t *tcp;
56973b9b51feSCy Schubert 	udphdr_t *udp;
56983b9b51feSCy Schubert 
56993b9b51feSCy Schubert 	csump = NULL;
57003b9b51feSCy Schubert 	if (fin->fin_out == 0) {
57013b9b51feSCy Schubert 		fin->fin_rev = (nat->nat_dir & NAT_OUTBOUND);
57023b9b51feSCy Schubert 	} else {
57033b9b51feSCy Schubert 		fin->fin_rev = ((nat->nat_dir & NAT_OUTBOUND) == 0);
57043b9b51feSCy Schubert 	}
57053b9b51feSCy Schubert 
57063b9b51feSCy Schubert 	switch (fin->fin_p)
57073b9b51feSCy Schubert 	{
57083b9b51feSCy Schubert 	case IPPROTO_TCP :
57093b9b51feSCy Schubert 		tcp = fin->fin_dp;
57103b9b51feSCy Schubert 
57113b9b51feSCy Schubert 		if ((nflags & IPN_TCP) != 0)
57123b9b51feSCy Schubert 			csump = &tcp->th_sum;
57133b9b51feSCy Schubert 
57143b9b51feSCy Schubert 		/*
57153b9b51feSCy Schubert 		 * Do a MSS CLAMPING on a SYN packet,
57163b9b51feSCy Schubert 		 * only deal IPv4 for now.
57173b9b51feSCy Schubert 		 */
57183b9b51feSCy Schubert 		if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0)
57193b9b51feSCy Schubert 			ipf_nat_mssclamp(tcp, nat->nat_mssclamp, fin, csump);
57203b9b51feSCy Schubert 
57213b9b51feSCy Schubert 		break;
57223b9b51feSCy Schubert 
57233b9b51feSCy Schubert 	case IPPROTO_UDP :
57243b9b51feSCy Schubert 		udp = fin->fin_dp;
57253b9b51feSCy Schubert 
57263b9b51feSCy Schubert 		if ((nflags & IPN_UDP) != 0) {
57273b9b51feSCy Schubert 			if (udp->uh_sum != 0)
57283b9b51feSCy Schubert 				csump = &udp->uh_sum;
57293b9b51feSCy Schubert 		}
57303b9b51feSCy Schubert 		break;
57313b9b51feSCy Schubert 
57323b9b51feSCy Schubert 	case IPPROTO_ICMP :
57333b9b51feSCy Schubert 		icmp = fin->fin_dp;
57343b9b51feSCy Schubert 
57353b9b51feSCy Schubert 		if ((nflags & IPN_ICMPQUERY) != 0) {
57363b9b51feSCy Schubert 			if (icmp->icmp_cksum != 0)
57373b9b51feSCy Schubert 				csump = &icmp->icmp_cksum;
57383b9b51feSCy Schubert 		}
57393b9b51feSCy Schubert 		break;
57403b9b51feSCy Schubert 
57413b9b51feSCy Schubert #ifdef USE_INET6
57423b9b51feSCy Schubert 	case IPPROTO_ICMPV6 :
57433b9b51feSCy Schubert 	    {
57443b9b51feSCy Schubert 		struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)fin->fin_dp;
57453b9b51feSCy Schubert 
57463b9b51feSCy Schubert 		icmp6 = fin->fin_dp;
57473b9b51feSCy Schubert 
57483b9b51feSCy Schubert 		if ((nflags & IPN_ICMPQUERY) != 0) {
57493b9b51feSCy Schubert 			if (icmp6->icmp6_cksum != 0)
57503b9b51feSCy Schubert 				csump = &icmp6->icmp6_cksum;
57513b9b51feSCy Schubert 		}
57523b9b51feSCy Schubert 		break;
57533b9b51feSCy Schubert 	    }
57543b9b51feSCy Schubert #endif
57553b9b51feSCy Schubert 	}
57568c82b374SCy Schubert 	return (csump);
57573b9b51feSCy Schubert }
57583b9b51feSCy Schubert 
57593b9b51feSCy Schubert 
57603b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
57613b9b51feSCy Schubert /* Function:    ipf_nat_expire                                              */
57623b9b51feSCy Schubert /* Returns:     Nil                                                         */
57633b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
57643b9b51feSCy Schubert /*                                                                          */
57653b9b51feSCy Schubert /* Check all of the timeout queues for entries at the top which need to be  */
57663b9b51feSCy Schubert /* expired.                                                                 */
57673b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
57683b9b51feSCy Schubert void
ipf_nat_expire(ipf_main_softc_t * softc)5769064a5a95SCy Schubert ipf_nat_expire(ipf_main_softc_t *softc)
57703b9b51feSCy Schubert {
57713b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
57723b9b51feSCy Schubert 	ipftq_t *ifq, *ifqnext;
57733b9b51feSCy Schubert 	ipftqent_t *tqe, *tqn;
57743b9b51feSCy Schubert 	int i;
57753b9b51feSCy Schubert 	SPL_INT(s);
57763b9b51feSCy Schubert 
57773b9b51feSCy Schubert 	SPL_NET(s);
57783b9b51feSCy Schubert 	WRITE_ENTER(&softc->ipf_nat);
57793b9b51feSCy Schubert 	for (ifq = softn->ipf_nat_tcptq, i = 0; ifq != NULL;
57803b9b51feSCy Schubert 	     ifq = ifq->ifq_next) {
57813b9b51feSCy Schubert 		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
57823b9b51feSCy Schubert 			if (tqe->tqe_die > softc->ipf_ticks)
57833b9b51feSCy Schubert 				break;
57843b9b51feSCy Schubert 			tqn = tqe->tqe_next;
57853b9b51feSCy Schubert 			ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE);
57863b9b51feSCy Schubert 		}
57873b9b51feSCy Schubert 	}
57883b9b51feSCy Schubert 
57893b9b51feSCy Schubert 	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifq->ifq_next) {
57903b9b51feSCy Schubert 		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
57913b9b51feSCy Schubert 			if (tqe->tqe_die > softc->ipf_ticks)
57923b9b51feSCy Schubert 				break;
57933b9b51feSCy Schubert 			tqn = tqe->tqe_next;
57943b9b51feSCy Schubert 			ipf_nat_delete(softc, tqe->tqe_parent, NL_EXPIRE);
57953b9b51feSCy Schubert 		}
57963b9b51feSCy Schubert 	}
57973b9b51feSCy Schubert 
57983b9b51feSCy Schubert 	for (ifq = softn->ipf_nat_utqe; ifq != NULL; ifq = ifqnext) {
57993b9b51feSCy Schubert 		ifqnext = ifq->ifq_next;
58003b9b51feSCy Schubert 
58013b9b51feSCy Schubert 		if (((ifq->ifq_flags & IFQF_DELETE) != 0) &&
58023b9b51feSCy Schubert 		    (ifq->ifq_ref == 0)) {
58033b9b51feSCy Schubert 			ipf_freetimeoutqueue(softc, ifq);
58043b9b51feSCy Schubert 		}
58053b9b51feSCy Schubert 	}
58063b9b51feSCy Schubert 
58073b9b51feSCy Schubert 	if (softn->ipf_nat_doflush != 0) {
58083b9b51feSCy Schubert 		ipf_nat_extraflush(softc, softn, 2);
58093b9b51feSCy Schubert 		softn->ipf_nat_doflush = 0;
58103b9b51feSCy Schubert 	}
58113b9b51feSCy Schubert 
58123b9b51feSCy Schubert 	RWLOCK_EXIT(&softc->ipf_nat);
58133b9b51feSCy Schubert 	SPL_X(s);
58143b9b51feSCy Schubert }
58153b9b51feSCy Schubert 
58163b9b51feSCy Schubert 
58173b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
58183b9b51feSCy Schubert /* Function:    ipf_nat_sync                                                */
58193b9b51feSCy Schubert /* Returns:     Nil                                                         */
58203b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
58213b9b51feSCy Schubert /*              ifp(I) - pointer to network interface                       */
58223b9b51feSCy Schubert /*                                                                          */
58233b9b51feSCy Schubert /* Walk through all of the currently active NAT sessions, looking for those */
58243b9b51feSCy Schubert /* which need to have their translated address updated.                     */
58253b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
58263b9b51feSCy Schubert void
ipf_nat_sync(ipf_main_softc_t * softc,void * ifp)5827064a5a95SCy Schubert ipf_nat_sync(ipf_main_softc_t *softc, void *ifp)
58283b9b51feSCy Schubert {
58293b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
58303b9b51feSCy Schubert 	u_32_t sum1, sum2, sumd;
58313b9b51feSCy Schubert 	i6addr_t in;
58323b9b51feSCy Schubert 	ipnat_t *n;
58333b9b51feSCy Schubert 	nat_t *nat;
58343b9b51feSCy Schubert 	void *ifp2;
58353b9b51feSCy Schubert 	int idx;
58363b9b51feSCy Schubert 	SPL_INT(s);
58373b9b51feSCy Schubert 
58383b9b51feSCy Schubert 	if (softc->ipf_running <= 0)
58393b9b51feSCy Schubert 		return;
58403b9b51feSCy Schubert 
58413b9b51feSCy Schubert 	/*
58423b9b51feSCy Schubert 	 * Change IP addresses for NAT sessions for any protocol except TCP
58433b9b51feSCy Schubert 	 * since it will break the TCP connection anyway.  The only rules
58443b9b51feSCy Schubert 	 * which will get changed are those which are "map ... -> 0/32",
58453b9b51feSCy Schubert 	 * where the rule specifies the address is taken from the interface.
58463b9b51feSCy Schubert 	 */
58473b9b51feSCy Schubert 	SPL_NET(s);
58483b9b51feSCy Schubert 	WRITE_ENTER(&softc->ipf_nat);
58493b9b51feSCy Schubert 
58503b9b51feSCy Schubert 	if (softc->ipf_running <= 0) {
58513b9b51feSCy Schubert 		RWLOCK_EXIT(&softc->ipf_nat);
58523b9b51feSCy Schubert 		return;
58533b9b51feSCy Schubert 	}
58543b9b51feSCy Schubert 
58553b9b51feSCy Schubert 	for (nat = softn->ipf_nat_instances; nat; nat = nat->nat_next) {
58563b9b51feSCy Schubert 		if ((nat->nat_flags & IPN_TCP) != 0)
58573b9b51feSCy Schubert 			continue;
58583b9b51feSCy Schubert 
58593b9b51feSCy Schubert 		n = nat->nat_ptr;
58603b9b51feSCy Schubert 		if (n != NULL) {
58613b9b51feSCy Schubert 			if (n->in_v[1] == 4) {
58623b9b51feSCy Schubert 				if (n->in_redir & NAT_MAP) {
58633b9b51feSCy Schubert 					if ((n->in_nsrcaddr != 0) ||
58643b9b51feSCy Schubert 					    (n->in_nsrcmsk != 0xffffffff))
58653b9b51feSCy Schubert 						continue;
58663b9b51feSCy Schubert 				} else if (n->in_redir & NAT_REDIRECT) {
58673b9b51feSCy Schubert 					if ((n->in_ndstaddr != 0) ||
58683b9b51feSCy Schubert 					    (n->in_ndstmsk != 0xffffffff))
58693b9b51feSCy Schubert 						continue;
58703b9b51feSCy Schubert 				}
58713b9b51feSCy Schubert 			}
58723b9b51feSCy Schubert #ifdef USE_INET6
58733b9b51feSCy Schubert 			if (n->in_v[1] == 4) {
58743b9b51feSCy Schubert 				if (n->in_redir & NAT_MAP) {
58753b9b51feSCy Schubert 					if (!IP6_ISZERO(&n->in_nsrcaddr) ||
58763b9b51feSCy Schubert 					    !IP6_ISONES(&n->in_nsrcmsk))
58773b9b51feSCy Schubert 						continue;
58783b9b51feSCy Schubert 				} else if (n->in_redir & NAT_REDIRECT) {
58793b9b51feSCy Schubert 					if (!IP6_ISZERO(&n->in_ndstaddr) ||
58803b9b51feSCy Schubert 					    !IP6_ISONES(&n->in_ndstmsk))
58813b9b51feSCy Schubert 						continue;
58823b9b51feSCy Schubert 				}
58833b9b51feSCy Schubert 			}
58843b9b51feSCy Schubert #endif
58853b9b51feSCy Schubert 		}
58863b9b51feSCy Schubert 
58873b9b51feSCy Schubert 		if (((ifp == NULL) || (ifp == nat->nat_ifps[0]) ||
58883b9b51feSCy Schubert 		     (ifp == nat->nat_ifps[1]))) {
58893b9b51feSCy Schubert 			nat->nat_ifps[0] = GETIFP(nat->nat_ifnames[0],
58903b9b51feSCy Schubert 						  nat->nat_v[0]);
58913b9b51feSCy Schubert 			if ((nat->nat_ifps[0] != NULL) &&
58923b9b51feSCy Schubert 			    (nat->nat_ifps[0] != (void *)-1)) {
58933b9b51feSCy Schubert 				nat->nat_mtu[0] = GETIFMTU_4(nat->nat_ifps[0]);
58943b9b51feSCy Schubert 			}
58953b9b51feSCy Schubert 			if (nat->nat_ifnames[1][0] != '\0') {
58963b9b51feSCy Schubert 				nat->nat_ifps[1] = GETIFP(nat->nat_ifnames[1],
58973b9b51feSCy Schubert 							  nat->nat_v[1]);
58983b9b51feSCy Schubert 			} else {
58993b9b51feSCy Schubert 				nat->nat_ifps[1] = nat->nat_ifps[0];
59003b9b51feSCy Schubert 			}
59013b9b51feSCy Schubert 			if ((nat->nat_ifps[1] != NULL) &&
59023b9b51feSCy Schubert 			    (nat->nat_ifps[1] != (void *)-1)) {
59033b9b51feSCy Schubert 				nat->nat_mtu[1] = GETIFMTU_4(nat->nat_ifps[1]);
59043b9b51feSCy Schubert 			}
59053b9b51feSCy Schubert 			ifp2 = nat->nat_ifps[0];
59063b9b51feSCy Schubert 			if (ifp2 == NULL)
59073b9b51feSCy Schubert 				continue;
59083b9b51feSCy Schubert 
59093b9b51feSCy Schubert 			/*
59103b9b51feSCy Schubert 			 * Change the map-to address to be the same as the
59113b9b51feSCy Schubert 			 * new one.
59123b9b51feSCy Schubert 			 */
59133b9b51feSCy Schubert 			sum1 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6);
59143b9b51feSCy Schubert 			if (ipf_ifpaddr(softc, nat->nat_v[0], FRI_NORMAL, ifp2,
59153b9b51feSCy Schubert 				       &in, NULL) != -1) {
59163b9b51feSCy Schubert 				if (nat->nat_v[0] == 4)
59173b9b51feSCy Schubert 					nat->nat_nsrcip = in.in4;
59183b9b51feSCy Schubert 			}
59193b9b51feSCy Schubert 			sum2 = NATFSUM(nat, nat->nat_v[1], nat_nsrc6);
59203b9b51feSCy Schubert 
59213b9b51feSCy Schubert 			if (sum1 == sum2)
59223b9b51feSCy Schubert 				continue;
59233b9b51feSCy Schubert 			/*
59243b9b51feSCy Schubert 			 * Readjust the checksum adjustment to take into
59253b9b51feSCy Schubert 			 * account the new IP#.
59263b9b51feSCy Schubert 			 */
59273b9b51feSCy Schubert 			CALC_SUMD(sum1, sum2, sumd);
59283b9b51feSCy Schubert 			/* XXX - dont change for TCP when solaris does
59293b9b51feSCy Schubert 			 * hardware checksumming.
59303b9b51feSCy Schubert 			 */
59313b9b51feSCy Schubert 			sumd += nat->nat_sumd[0];
59323b9b51feSCy Schubert 			nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
59333b9b51feSCy Schubert 			nat->nat_sumd[1] = nat->nat_sumd[0];
59343b9b51feSCy Schubert 		}
59353b9b51feSCy Schubert 	}
59363b9b51feSCy Schubert 
59373b9b51feSCy Schubert 	for (n = softn->ipf_nat_list; (n != NULL); n = n->in_next) {
59383b9b51feSCy Schubert 		char *base = n->in_names;
59393b9b51feSCy Schubert 
59403b9b51feSCy Schubert 		if ((ifp == NULL) || (n->in_ifps[0] == ifp))
59413b9b51feSCy Schubert 			n->in_ifps[0] = ipf_resolvenic(softc,
59423b9b51feSCy Schubert 						       base + n->in_ifnames[0],
59433b9b51feSCy Schubert 						       n->in_v[0]);
59443b9b51feSCy Schubert 		if ((ifp == NULL) || (n->in_ifps[1] == ifp))
59453b9b51feSCy Schubert 			n->in_ifps[1] = ipf_resolvenic(softc,
59463b9b51feSCy Schubert 						       base + n->in_ifnames[1],
59473b9b51feSCy Schubert 						       n->in_v[1]);
59483b9b51feSCy Schubert 
59493b9b51feSCy Schubert 		if (n->in_redir & NAT_REDIRECT)
59503b9b51feSCy Schubert 			idx = 1;
59513b9b51feSCy Schubert 		else
59523b9b51feSCy Schubert 			idx = 0;
59533b9b51feSCy Schubert 
59543b9b51feSCy Schubert 		if (((ifp == NULL) || (n->in_ifps[idx] == ifp)) &&
59553b9b51feSCy Schubert 		    (n->in_ifps[idx] != NULL &&
59563b9b51feSCy Schubert 		     n->in_ifps[idx] != (void *)-1)) {
59573b9b51feSCy Schubert 
59583b9b51feSCy Schubert 			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_osrc,
59593b9b51feSCy Schubert 					     0, n->in_ifps[idx]);
59603b9b51feSCy Schubert 			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_odst,
59613b9b51feSCy Schubert 					     0, n->in_ifps[idx]);
59623b9b51feSCy Schubert 			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_nsrc,
59633b9b51feSCy Schubert 					     0, n->in_ifps[idx]);
59643b9b51feSCy Schubert 			ipf_nat_nextaddrinit(softc, n->in_names, &n->in_ndst,
59653b9b51feSCy Schubert 					     0, n->in_ifps[idx]);
59663b9b51feSCy Schubert 		}
59673b9b51feSCy Schubert 	}
59683b9b51feSCy Schubert 	RWLOCK_EXIT(&softc->ipf_nat);
59693b9b51feSCy Schubert 	SPL_X(s);
59703b9b51feSCy Schubert }
59713b9b51feSCy Schubert 
59723b9b51feSCy Schubert 
59733b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
59743b9b51feSCy Schubert /* Function:    ipf_nat_icmpquerytype                                       */
59753b9b51feSCy Schubert /* Returns:     int - 1 == success, 0 == failure                            */
59763b9b51feSCy Schubert /* Parameters:  icmptype(I) - ICMP type number                              */
59773b9b51feSCy Schubert /*                                                                          */
59783b9b51feSCy Schubert /* Tests to see if the ICMP type number passed is a query/response type or  */
59793b9b51feSCy Schubert /* not.                                                                     */
59803b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
59813b9b51feSCy Schubert static int
ipf_nat_icmpquerytype(int icmptype)5982064a5a95SCy Schubert ipf_nat_icmpquerytype(int icmptype)
59833b9b51feSCy Schubert {
59843b9b51feSCy Schubert 
59853b9b51feSCy Schubert 	/*
59863b9b51feSCy Schubert 	 * For the ICMP query NAT code, it is essential that both the query
59873b9b51feSCy Schubert 	 * and the reply match on the NAT rule. Because the NAT structure
59883b9b51feSCy Schubert 	 * does not keep track of the icmptype, and a single NAT structure
59893b9b51feSCy Schubert 	 * is used for all icmp types with the same src, dest and id, we
59903b9b51feSCy Schubert 	 * simply define the replies as queries as well. The funny thing is,
59913b9b51feSCy Schubert 	 * altough it seems silly to call a reply a query, this is exactly
59923b9b51feSCy Schubert 	 * as it is defined in the IPv4 specification
59933b9b51feSCy Schubert 	 */
59943b9b51feSCy Schubert 	switch (icmptype)
59953b9b51feSCy Schubert 	{
59963b9b51feSCy Schubert 	case ICMP_ECHOREPLY:
59973b9b51feSCy Schubert 	case ICMP_ECHO:
59983b9b51feSCy Schubert 	/* route advertisement/solicitation is currently unsupported: */
59993b9b51feSCy Schubert 	/* it would require rewriting the ICMP data section          */
60003b9b51feSCy Schubert 	case ICMP_TSTAMP:
60013b9b51feSCy Schubert 	case ICMP_TSTAMPREPLY:
60023b9b51feSCy Schubert 	case ICMP_IREQ:
60033b9b51feSCy Schubert 	case ICMP_IREQREPLY:
60043b9b51feSCy Schubert 	case ICMP_MASKREQ:
60053b9b51feSCy Schubert 	case ICMP_MASKREPLY:
60068c82b374SCy Schubert 		return (1);
60073b9b51feSCy Schubert 	default:
60088c82b374SCy Schubert 		return (0);
60093b9b51feSCy Schubert 	}
60103b9b51feSCy Schubert }
60113b9b51feSCy Schubert 
60123b9b51feSCy Schubert 
60133b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
60143b9b51feSCy Schubert /* Function:    nat_log                                                     */
60153b9b51feSCy Schubert /* Returns:     Nil                                                         */
60163b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
60173b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
60183b9b51feSCy Schubert /*              nat(I)    - pointer to NAT structure                        */
60193b9b51feSCy Schubert /*              action(I) - action related to NAT structure being performed */
60203b9b51feSCy Schubert /*                                                                          */
60213b9b51feSCy Schubert /* Creates a NAT log entry.                                                 */
60223b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
60233b9b51feSCy Schubert void
ipf_nat_log(ipf_main_softc_t * softc,ipf_nat_softc_t * softn,struct nat * nat,u_int action)6024064a5a95SCy Schubert ipf_nat_log(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, struct nat *nat,
6025064a5a95SCy Schubert 	u_int action)
60263b9b51feSCy Schubert {
60273b9b51feSCy Schubert #ifdef	IPFILTER_LOG
60283b9b51feSCy Schubert 	struct ipnat *np;
60293b9b51feSCy Schubert 	int rulen;
60303b9b51feSCy Schubert 	struct natlog natl;
60313b9b51feSCy Schubert 	void *items[1];
60323b9b51feSCy Schubert 	size_t sizes[1];
60333b9b51feSCy Schubert 	int types[1];
60343b9b51feSCy Schubert 
60353b9b51feSCy Schubert 	bcopy((char *)&nat->nat_osrc6, (char *)&natl.nl_osrcip,
60363b9b51feSCy Schubert 	      sizeof(natl.nl_osrcip));
60373b9b51feSCy Schubert 	bcopy((char *)&nat->nat_nsrc6, (char *)&natl.nl_nsrcip,
60383b9b51feSCy Schubert 	      sizeof(natl.nl_nsrcip));
60393b9b51feSCy Schubert 	bcopy((char *)&nat->nat_odst6, (char *)&natl.nl_odstip,
60403b9b51feSCy Schubert 	      sizeof(natl.nl_odstip));
60413b9b51feSCy Schubert 	bcopy((char *)&nat->nat_ndst6, (char *)&natl.nl_ndstip,
60423b9b51feSCy Schubert 	      sizeof(natl.nl_ndstip));
60433b9b51feSCy Schubert 
60443b9b51feSCy Schubert 	natl.nl_bytes[0] = nat->nat_bytes[0];
60453b9b51feSCy Schubert 	natl.nl_bytes[1] = nat->nat_bytes[1];
60463b9b51feSCy Schubert 	natl.nl_pkts[0] = nat->nat_pkts[0];
60473b9b51feSCy Schubert 	natl.nl_pkts[1] = nat->nat_pkts[1];
60483b9b51feSCy Schubert 	natl.nl_odstport = nat->nat_odport;
60493b9b51feSCy Schubert 	natl.nl_osrcport = nat->nat_osport;
60503b9b51feSCy Schubert 	natl.nl_nsrcport = nat->nat_nsport;
60513b9b51feSCy Schubert 	natl.nl_ndstport = nat->nat_ndport;
60523b9b51feSCy Schubert 	natl.nl_p[0] = nat->nat_pr[0];
60533b9b51feSCy Schubert 	natl.nl_p[1] = nat->nat_pr[1];
60543b9b51feSCy Schubert 	natl.nl_v[0] = nat->nat_v[0];
60553b9b51feSCy Schubert 	natl.nl_v[1] = nat->nat_v[1];
60563b9b51feSCy Schubert 	natl.nl_type = nat->nat_redir;
60573b9b51feSCy Schubert 	natl.nl_action = action;
60583b9b51feSCy Schubert 	natl.nl_rule = -1;
60593b9b51feSCy Schubert 
60603b9b51feSCy Schubert 	bcopy(nat->nat_ifnames[0], natl.nl_ifnames[0],
60613b9b51feSCy Schubert 	      sizeof(nat->nat_ifnames[0]));
60623b9b51feSCy Schubert 	bcopy(nat->nat_ifnames[1], natl.nl_ifnames[1],
60633b9b51feSCy Schubert 	      sizeof(nat->nat_ifnames[1]));
60643b9b51feSCy Schubert 
60653b9b51feSCy Schubert 	if (softc->ipf_large_nat && nat->nat_ptr != NULL) {
60663b9b51feSCy Schubert 		for (rulen = 0, np = softn->ipf_nat_list; np != NULL;
60673b9b51feSCy Schubert 		     np = np->in_next, rulen++)
60683b9b51feSCy Schubert 			if (np == nat->nat_ptr) {
60693b9b51feSCy Schubert 				natl.nl_rule = rulen;
60703b9b51feSCy Schubert 				break;
60713b9b51feSCy Schubert 			}
60723b9b51feSCy Schubert 	}
60733b9b51feSCy Schubert 	items[0] = &natl;
60743b9b51feSCy Schubert 	sizes[0] = sizeof(natl);
60753b9b51feSCy Schubert 	types[0] = 0;
60763b9b51feSCy Schubert 
60773b9b51feSCy Schubert 	(void) ipf_log_items(softc, IPL_LOGNAT, NULL, items, sizes, types, 1);
60783b9b51feSCy Schubert #endif
60793b9b51feSCy Schubert }
60803b9b51feSCy Schubert 
60813b9b51feSCy Schubert 
60823b9b51feSCy Schubert 
60833b9b51feSCy Schubert 
60843b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
60853b9b51feSCy Schubert /* Function:    ipf_nat_rule_deref                                          */
60863b9b51feSCy Schubert /* Returns:     Nil                                                         */
60873b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
60883b9b51feSCy Schubert /*              inp(I)   - pointer to pointer to NAT rule                   */
60893b9b51feSCy Schubert /* Write Locks: ipf_nat                                                     */
60903b9b51feSCy Schubert /*                                                                          */
60913b9b51feSCy Schubert /* Dropping the refernce count for a rule means that whatever held the      */
60923b9b51feSCy Schubert /* pointer to this rule (*inp) is no longer interested in it and when the   */
60933b9b51feSCy Schubert /* reference count drops to zero, any resources allocated for the rule can  */
60943b9b51feSCy Schubert /* be released and the rule itself free'd.                                  */
60953b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
60963b9b51feSCy Schubert void
ipf_nat_rule_deref(ipf_main_softc_t * softc,ipnat_t ** inp)6097064a5a95SCy Schubert ipf_nat_rule_deref(ipf_main_softc_t *softc, ipnat_t **inp)
60983b9b51feSCy Schubert {
60993b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
61003b9b51feSCy Schubert 	ipnat_t *n;
61013b9b51feSCy Schubert 
61023b9b51feSCy Schubert 	n = *inp;
61033b9b51feSCy Schubert 	*inp = NULL;
61043b9b51feSCy Schubert 	n->in_use--;
61053b9b51feSCy Schubert 	if (n->in_use > 0)
61063b9b51feSCy Schubert 		return;
61073b9b51feSCy Schubert 
61083b9b51feSCy Schubert 	if (n->in_apr != NULL)
61093b9b51feSCy Schubert 		ipf_proxy_deref(n->in_apr);
61103b9b51feSCy Schubert 
61113b9b51feSCy Schubert 	ipf_nat_rule_fini(softc, n);
61123b9b51feSCy Schubert 
61133b9b51feSCy Schubert 	if (n->in_redir & NAT_REDIRECT) {
61143b9b51feSCy Schubert 		if ((n->in_flags & IPN_PROXYRULE) == 0) {
61153b9b51feSCy Schubert 			ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_rdr);
61163b9b51feSCy Schubert 		}
61173b9b51feSCy Schubert 	}
61183b9b51feSCy Schubert 	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
61193b9b51feSCy Schubert 		if ((n->in_flags & IPN_PROXYRULE) == 0) {
61203b9b51feSCy Schubert 			ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules_map);
61213b9b51feSCy Schubert 		}
61223b9b51feSCy Schubert 	}
61233b9b51feSCy Schubert 
61243b9b51feSCy Schubert 	if (n->in_tqehead[0] != NULL) {
61253b9b51feSCy Schubert 		if (ipf_deletetimeoutqueue(n->in_tqehead[0]) == 0) {
61263b9b51feSCy Schubert 			ipf_freetimeoutqueue(softc, n->in_tqehead[0]);
61273b9b51feSCy Schubert 		}
61283b9b51feSCy Schubert 	}
61293b9b51feSCy Schubert 
61303b9b51feSCy Schubert 	if (n->in_tqehead[1] != NULL) {
61313b9b51feSCy Schubert 		if (ipf_deletetimeoutqueue(n->in_tqehead[1]) == 0) {
61323b9b51feSCy Schubert 			ipf_freetimeoutqueue(softc, n->in_tqehead[1]);
61333b9b51feSCy Schubert 		}
61343b9b51feSCy Schubert 	}
61353b9b51feSCy Schubert 
61363b9b51feSCy Schubert 	if ((n->in_flags & IPN_PROXYRULE) == 0) {
61373b9b51feSCy Schubert 		ATOMIC_DEC32(softn->ipf_nat_stats.ns_rules);
61383b9b51feSCy Schubert 	}
61393b9b51feSCy Schubert 
61403b9b51feSCy Schubert 	MUTEX_DESTROY(&n->in_lock);
61413b9b51feSCy Schubert 
61423b9b51feSCy Schubert 	KFREES(n, n->in_size);
61433b9b51feSCy Schubert 
61443b9b51feSCy Schubert #if SOLARIS && !defined(INSTANCES)
61453b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_rules == 0)
61463b9b51feSCy Schubert 		pfil_delayed_copy = 1;
61473b9b51feSCy Schubert #endif
61483b9b51feSCy Schubert }
61493b9b51feSCy Schubert 
61503b9b51feSCy Schubert 
61513b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
61523b9b51feSCy Schubert /* Function:    ipf_nat_deref                                               */
61533b9b51feSCy Schubert /* Returns:     Nil                                                         */
61543b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
61553b9b51feSCy Schubert /*              natp(I)  - pointer to pointer to NAT table entry            */
61563b9b51feSCy Schubert /*                                                                          */
61573b9b51feSCy Schubert /* Decrement the reference counter for this NAT table entry and free it if  */
61583b9b51feSCy Schubert /* there are no more things using it.                                       */
61593b9b51feSCy Schubert /*                                                                          */
61603b9b51feSCy Schubert /* IF nat_ref == 1 when this function is called, then we have an orphan nat */
61613b9b51feSCy Schubert /* structure *because* it only gets called on paths _after_ nat_ref has been*/
61623b9b51feSCy Schubert /* incremented.  If nat_ref == 1 then we shouldn't decrement it here        */
61633b9b51feSCy Schubert /* because nat_delete() will do that and send nat_ref to -1.                */
61643b9b51feSCy Schubert /*                                                                          */
61653b9b51feSCy Schubert /* Holding the lock on nat_lock is required to serialise nat_delete() being */
61663b9b51feSCy Schubert /* called from a NAT flush ioctl with a deref happening because of a packet.*/
61673b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
61683b9b51feSCy Schubert void
ipf_nat_deref(ipf_main_softc_t * softc,nat_t ** natp)6169064a5a95SCy Schubert ipf_nat_deref(ipf_main_softc_t *softc, nat_t **natp)
61703b9b51feSCy Schubert {
61713b9b51feSCy Schubert 	nat_t *nat;
61723b9b51feSCy Schubert 
61733b9b51feSCy Schubert 	nat = *natp;
61743b9b51feSCy Schubert 	*natp = NULL;
61753b9b51feSCy Schubert 
61763b9b51feSCy Schubert 	MUTEX_ENTER(&nat->nat_lock);
61773b9b51feSCy Schubert 	if (nat->nat_ref > 1) {
61783b9b51feSCy Schubert 		nat->nat_ref--;
61793b9b51feSCy Schubert 		ASSERT(nat->nat_ref >= 0);
61803b9b51feSCy Schubert 		MUTEX_EXIT(&nat->nat_lock);
61813b9b51feSCy Schubert 		return;
61823b9b51feSCy Schubert 	}
61833b9b51feSCy Schubert 	MUTEX_EXIT(&nat->nat_lock);
61843b9b51feSCy Schubert 
61853b9b51feSCy Schubert 	WRITE_ENTER(&softc->ipf_nat);
61863b9b51feSCy Schubert 	ipf_nat_delete(softc, nat, NL_EXPIRE);
61873b9b51feSCy Schubert 	RWLOCK_EXIT(&softc->ipf_nat);
61883b9b51feSCy Schubert }
61893b9b51feSCy Schubert 
61903b9b51feSCy Schubert 
61913b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
61923b9b51feSCy Schubert /* Function:    ipf_nat_clone                                               */
61933b9b51feSCy Schubert /* Returns:     ipstate_t* - NULL == cloning failed,                        */
61943b9b51feSCy Schubert /*                           else pointer to new state structure            */
61953b9b51feSCy Schubert /* Parameters:  fin(I) - pointer to packet information                      */
61963b9b51feSCy Schubert /*              is(I)  - pointer to master state structure                  */
61973b9b51feSCy Schubert /* Write Lock:  ipf_nat                                                     */
61983b9b51feSCy Schubert /*                                                                          */
61993b9b51feSCy Schubert /* Create a "duplcate" state table entry from the master.                   */
62003b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
62013b9b51feSCy Schubert nat_t *
ipf_nat_clone(fr_info_t * fin,nat_t * nat)6202064a5a95SCy Schubert ipf_nat_clone(fr_info_t *fin, nat_t *nat)
62033b9b51feSCy Schubert {
62043b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
62053b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
62063b9b51feSCy Schubert 	frentry_t *fr;
62073b9b51feSCy Schubert 	nat_t *clone;
62083b9b51feSCy Schubert 	ipnat_t *np;
62093b9b51feSCy Schubert 
62103b9b51feSCy Schubert 	KMALLOC(clone, nat_t *);
62113b9b51feSCy Schubert 	if (clone == NULL) {
62123b9b51feSCy Schubert 		NBUMPSIDED(fin->fin_out, ns_clone_nomem);
62138c82b374SCy Schubert 		return (NULL);
62143b9b51feSCy Schubert 	}
62153b9b51feSCy Schubert 	bcopy((char *)nat, (char *)clone, sizeof(*clone));
62163b9b51feSCy Schubert 
62173b9b51feSCy Schubert 	MUTEX_NUKE(&clone->nat_lock);
62183b9b51feSCy Schubert 
62193b9b51feSCy Schubert 	clone->nat_rev = fin->fin_rev;
62203b9b51feSCy Schubert 	clone->nat_aps = NULL;
62213b9b51feSCy Schubert 	/*
62223b9b51feSCy Schubert 	 * Initialize all these so that ipf_nat_delete() doesn't cause a crash.
62233b9b51feSCy Schubert 	 */
62243b9b51feSCy Schubert 	clone->nat_tqe.tqe_pnext = NULL;
62253b9b51feSCy Schubert 	clone->nat_tqe.tqe_next = NULL;
62263b9b51feSCy Schubert 	clone->nat_tqe.tqe_ifq = NULL;
62273b9b51feSCy Schubert 	clone->nat_tqe.tqe_parent = clone;
62283b9b51feSCy Schubert 
62293b9b51feSCy Schubert 	clone->nat_flags &= ~SI_CLONE;
62303b9b51feSCy Schubert 	clone->nat_flags |= SI_CLONED;
62313b9b51feSCy Schubert 
62323b9b51feSCy Schubert 	if (clone->nat_hm)
62333b9b51feSCy Schubert 		clone->nat_hm->hm_ref++;
62343b9b51feSCy Schubert 
62353b9b51feSCy Schubert 	if (ipf_nat_insert(softc, softn, clone) == -1) {
62363b9b51feSCy Schubert 		KFREE(clone);
62373b9b51feSCy Schubert 		NBUMPSIDED(fin->fin_out, ns_insert_fail);
62388c82b374SCy Schubert 		return (NULL);
62393b9b51feSCy Schubert 	}
62403b9b51feSCy Schubert 
62413b9b51feSCy Schubert 	np = clone->nat_ptr;
62423b9b51feSCy Schubert 	if (np != NULL) {
62433b9b51feSCy Schubert 		if (softn->ipf_nat_logging)
62443b9b51feSCy Schubert 			ipf_nat_log(softc, softn, clone, NL_CLONE);
62453b9b51feSCy Schubert 		np->in_use++;
62463b9b51feSCy Schubert 	}
62473b9b51feSCy Schubert 	fr = clone->nat_fr;
62483b9b51feSCy Schubert 	if (fr != NULL) {
62493b9b51feSCy Schubert 		MUTEX_ENTER(&fr->fr_lock);
62503b9b51feSCy Schubert 		fr->fr_ref++;
62513b9b51feSCy Schubert 		MUTEX_EXIT(&fr->fr_lock);
62523b9b51feSCy Schubert 	}
62533b9b51feSCy Schubert 
62543b9b51feSCy Schubert 
62553b9b51feSCy Schubert 	/*
62563b9b51feSCy Schubert 	 * Because the clone is created outside the normal loop of things and
62573b9b51feSCy Schubert 	 * TCP has special needs in terms of state, initialise the timeout
62583b9b51feSCy Schubert 	 * state of the new NAT from here.
62593b9b51feSCy Schubert 	 */
62603b9b51feSCy Schubert 	if (clone->nat_pr[0] == IPPROTO_TCP) {
62613b9b51feSCy Schubert 		(void) ipf_tcp_age(&clone->nat_tqe, fin, softn->ipf_nat_tcptq,
62623b9b51feSCy Schubert 				   clone->nat_flags, 2);
62633b9b51feSCy Schubert 	}
62643b9b51feSCy Schubert 	clone->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, clone);
62653b9b51feSCy Schubert 	if (softn->ipf_nat_logging)
62663b9b51feSCy Schubert 		ipf_nat_log(softc, softn, clone, NL_CLONE);
62678c82b374SCy Schubert 	return (clone);
62683b9b51feSCy Schubert }
62693b9b51feSCy Schubert 
62703b9b51feSCy Schubert 
62713b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
62723b9b51feSCy Schubert /* Function:   ipf_nat_wildok                                               */
62733b9b51feSCy Schubert /* Returns:    int - 1 == packet's ports match wildcards                    */
62743b9b51feSCy Schubert /*                   0 == packet's ports don't match wildcards              */
62753b9b51feSCy Schubert /* Parameters: nat(I)   - NAT entry                                         */
62763b9b51feSCy Schubert /*             sport(I) - source port                                       */
62773b9b51feSCy Schubert /*             dport(I) - destination port                                  */
62783b9b51feSCy Schubert /*             flags(I) - wildcard flags                                    */
62793b9b51feSCy Schubert /*             dir(I)   - packet direction                                  */
62803b9b51feSCy Schubert /*                                                                          */
62813b9b51feSCy Schubert /* Use NAT entry and packet direction to determine which combination of     */
62823b9b51feSCy Schubert /* wildcard flags should be used.                                           */
62833b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
62843b9b51feSCy Schubert int
ipf_nat_wildok(nat_t * nat,int sport,int dport,int flags,int dir)6285064a5a95SCy Schubert ipf_nat_wildok(nat_t *nat, int sport, int dport, int flags, int dir)
62863b9b51feSCy Schubert {
62873b9b51feSCy Schubert 	/*
62883b9b51feSCy Schubert 	 * When called by       dir is set to
62893b9b51feSCy Schubert 	 * nat_inlookup         NAT_INBOUND (0)
62903b9b51feSCy Schubert 	 * nat_outlookup        NAT_OUTBOUND (1)
62913b9b51feSCy Schubert 	 *
62923b9b51feSCy Schubert 	 * We simply combine the packet's direction in dir with the original
62933b9b51feSCy Schubert 	 * "intended" direction of that NAT entry in nat->nat_dir to decide
62943b9b51feSCy Schubert 	 * which combination of wildcard flags to allow.
62953b9b51feSCy Schubert 	 */
62963b9b51feSCy Schubert 	switch ((dir << 1) | (nat->nat_dir & (NAT_INBOUND|NAT_OUTBOUND)))
62973b9b51feSCy Schubert 	{
62983b9b51feSCy Schubert 	case 3: /* outbound packet / outbound entry */
62993b9b51feSCy Schubert 		if (((nat->nat_osport == sport) ||
63003b9b51feSCy Schubert 		    (flags & SI_W_SPORT)) &&
63013b9b51feSCy Schubert 		    ((nat->nat_odport == dport) ||
63023b9b51feSCy Schubert 		    (flags & SI_W_DPORT)))
63038c82b374SCy Schubert 			return (1);
63043b9b51feSCy Schubert 		break;
63053b9b51feSCy Schubert 	case 2: /* outbound packet / inbound entry */
63063b9b51feSCy Schubert 		if (((nat->nat_osport == dport) ||
63073b9b51feSCy Schubert 		    (flags & SI_W_SPORT)) &&
63083b9b51feSCy Schubert 		    ((nat->nat_odport == sport) ||
63093b9b51feSCy Schubert 		    (flags & SI_W_DPORT)))
63108c82b374SCy Schubert 			return (1);
63113b9b51feSCy Schubert 		break;
63123b9b51feSCy Schubert 	case 1: /* inbound packet / outbound entry */
63133b9b51feSCy Schubert 		if (((nat->nat_osport == dport) ||
63143b9b51feSCy Schubert 		    (flags & SI_W_SPORT)) &&
63153b9b51feSCy Schubert 		    ((nat->nat_odport == sport) ||
63163b9b51feSCy Schubert 		    (flags & SI_W_DPORT)))
63178c82b374SCy Schubert 			return (1);
63183b9b51feSCy Schubert 		break;
63193b9b51feSCy Schubert 	case 0: /* inbound packet / inbound entry */
63203b9b51feSCy Schubert 		if (((nat->nat_osport == sport) ||
63213b9b51feSCy Schubert 		    (flags & SI_W_SPORT)) &&
63223b9b51feSCy Schubert 		    ((nat->nat_odport == dport) ||
63233b9b51feSCy Schubert 		    (flags & SI_W_DPORT)))
63248c82b374SCy Schubert 			return (1);
63253b9b51feSCy Schubert 		break;
63263b9b51feSCy Schubert 	default:
63273b9b51feSCy Schubert 		break;
63283b9b51feSCy Schubert 	}
63293b9b51feSCy Schubert 
63303b9b51feSCy Schubert 	return (0);
63313b9b51feSCy Schubert }
63323b9b51feSCy Schubert 
63333b9b51feSCy Schubert 
63343b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
63353b9b51feSCy Schubert /* Function:    nat_mssclamp                                                */
63363b9b51feSCy Schubert /* Returns:     Nil                                                         */
63373b9b51feSCy Schubert /* Parameters:  tcp(I)    - pointer to TCP header                           */
63383b9b51feSCy Schubert /*              maxmss(I) - value to clamp the TCP MSS to                   */
63393b9b51feSCy Schubert /*              fin(I)    - pointer to packet information                   */
63403b9b51feSCy Schubert /*              csump(I)  - pointer to TCP checksum                         */
63413b9b51feSCy Schubert /*                                                                          */
63423b9b51feSCy Schubert /* Check for MSS option and clamp it if necessary.  If found and changed,   */
63433b9b51feSCy Schubert /* then the TCP header checksum will be updated to reflect the change in    */
63443b9b51feSCy Schubert /* the MSS.                                                                 */
63453b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
63463b9b51feSCy Schubert static void
ipf_nat_mssclamp(tcphdr_t * tcp,u_32_t maxmss,fr_info_t * fin,u_short * csump)6347064a5a95SCy Schubert ipf_nat_mssclamp(tcphdr_t *tcp, u_32_t maxmss, fr_info_t *fin, u_short *csump)
63483b9b51feSCy Schubert {
63493b9b51feSCy Schubert 	u_char *cp, *ep, opt;
63503b9b51feSCy Schubert 	int hlen, advance;
63513b9b51feSCy Schubert 	u_32_t mss, sumd;
63523b9b51feSCy Schubert 
63533b9b51feSCy Schubert 	hlen = TCP_OFF(tcp) << 2;
63543b9b51feSCy Schubert 	if (hlen > sizeof(*tcp)) {
63553b9b51feSCy Schubert 		cp = (u_char *)tcp + sizeof(*tcp);
63563b9b51feSCy Schubert 		ep = (u_char *)tcp + hlen;
63573b9b51feSCy Schubert 
63583b9b51feSCy Schubert 		while (cp < ep) {
63593b9b51feSCy Schubert 			opt = cp[0];
63603b9b51feSCy Schubert 			if (opt == TCPOPT_EOL)
63613b9b51feSCy Schubert 				break;
63623b9b51feSCy Schubert 			else if (opt == TCPOPT_NOP) {
63633b9b51feSCy Schubert 				cp++;
63643b9b51feSCy Schubert 				continue;
63653b9b51feSCy Schubert 			}
63663b9b51feSCy Schubert 
63673b9b51feSCy Schubert 			if (cp + 1 >= ep)
63683b9b51feSCy Schubert 				break;
63693b9b51feSCy Schubert 			advance = cp[1];
63703b9b51feSCy Schubert 			if ((cp + advance > ep) || (advance <= 0))
63713b9b51feSCy Schubert 				break;
63723b9b51feSCy Schubert 			switch (opt)
63733b9b51feSCy Schubert 			{
63743b9b51feSCy Schubert 			case TCPOPT_MAXSEG:
63753b9b51feSCy Schubert 				if (advance != 4)
63763b9b51feSCy Schubert 					break;
63773b9b51feSCy Schubert 				mss = cp[2] * 256 + cp[3];
63783b9b51feSCy Schubert 				if (mss > maxmss) {
63793b9b51feSCy Schubert 					cp[2] = maxmss / 256;
63803b9b51feSCy Schubert 					cp[3] = maxmss & 0xff;
63813b9b51feSCy Schubert 					CALC_SUMD(mss, maxmss, sumd);
63823b9b51feSCy Schubert 					ipf_fix_outcksum(0, csump, sumd, 0);
63833b9b51feSCy Schubert 				}
63843b9b51feSCy Schubert 				break;
63853b9b51feSCy Schubert 			default:
63863b9b51feSCy Schubert 				/* ignore unknown options */
63873b9b51feSCy Schubert 				break;
63883b9b51feSCy Schubert 			}
63893b9b51feSCy Schubert 
63903b9b51feSCy Schubert 			cp += advance;
63913b9b51feSCy Schubert 		}
63923b9b51feSCy Schubert 	}
63933b9b51feSCy Schubert }
63943b9b51feSCy Schubert 
63953b9b51feSCy Schubert 
63963b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
63973b9b51feSCy Schubert /* Function:    ipf_nat_setqueue                                            */
63983b9b51feSCy Schubert /* Returns:     Nil                                                         */
63993b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
64003b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
64013b9b51feSCy Schubert /*              nat(I)- pointer to NAT structure                            */
64023b9b51feSCy Schubert /* Locks:       ipf_nat (read or write)                                     */
64033b9b51feSCy Schubert /*                                                                          */
64043b9b51feSCy Schubert /* Put the NAT entry on its default queue entry, using rev as a helped in   */
64053b9b51feSCy Schubert /* determining which queue it should be placed on.                          */
64063b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
64073b9b51feSCy Schubert void
ipf_nat_setqueue(ipf_main_softc_t * softc,ipf_nat_softc_t * softn,nat_t * nat)6408064a5a95SCy Schubert ipf_nat_setqueue(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, nat_t *nat)
64093b9b51feSCy Schubert {
64103b9b51feSCy Schubert 	ipftq_t *oifq, *nifq;
64113b9b51feSCy Schubert 	int rev = nat->nat_rev;
64123b9b51feSCy Schubert 
64133b9b51feSCy Schubert 	if (nat->nat_ptr != NULL)
64143b9b51feSCy Schubert 		nifq = nat->nat_ptr->in_tqehead[rev];
64153b9b51feSCy Schubert 	else
64163b9b51feSCy Schubert 		nifq = NULL;
64173b9b51feSCy Schubert 
64183b9b51feSCy Schubert 	if (nifq == NULL) {
64193b9b51feSCy Schubert 		switch (nat->nat_pr[0])
64203b9b51feSCy Schubert 		{
64213b9b51feSCy Schubert 		case IPPROTO_UDP :
64223b9b51feSCy Schubert 			nifq = &softn->ipf_nat_udptq;
64233b9b51feSCy Schubert 			break;
64243b9b51feSCy Schubert 		case IPPROTO_ICMP :
64253b9b51feSCy Schubert 			nifq = &softn->ipf_nat_icmptq;
64263b9b51feSCy Schubert 			break;
64273b9b51feSCy Schubert 		case IPPROTO_TCP :
64283b9b51feSCy Schubert 			nifq = softn->ipf_nat_tcptq +
64293b9b51feSCy Schubert 			       nat->nat_tqe.tqe_state[rev];
64303b9b51feSCy Schubert 			break;
64313b9b51feSCy Schubert 		default :
64323b9b51feSCy Schubert 			nifq = &softn->ipf_nat_iptq;
64333b9b51feSCy Schubert 			break;
64343b9b51feSCy Schubert 		}
64353b9b51feSCy Schubert 	}
64363b9b51feSCy Schubert 
64373b9b51feSCy Schubert 	oifq = nat->nat_tqe.tqe_ifq;
64383b9b51feSCy Schubert 	/*
64393b9b51feSCy Schubert 	 * If it's currently on a timeout queue, move it from one queue to
64403b9b51feSCy Schubert 	 * another, else put it on the end of the newly determined queue.
64413b9b51feSCy Schubert 	 */
64423b9b51feSCy Schubert 	if (oifq != NULL)
64433b9b51feSCy Schubert 		ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq, nifq);
64443b9b51feSCy Schubert 	else
64453b9b51feSCy Schubert 		ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe, nifq, nat);
64463b9b51feSCy Schubert 	return;
64473b9b51feSCy Schubert }
64483b9b51feSCy Schubert 
64493b9b51feSCy Schubert 
64503b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
64513b9b51feSCy Schubert /* Function:    nat_getnext                                                 */
64523b9b51feSCy Schubert /* Returns:     int - 0 == ok, else error                                   */
64533b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
64543b9b51feSCy Schubert /*              t(I)   - pointer to ipftoken structure                      */
64553b9b51feSCy Schubert /*              itp(I) - pointer to ipfgeniter_t structure                  */
64563b9b51feSCy Schubert /*                                                                          */
64573b9b51feSCy Schubert /* Fetch the next nat/ipnat structure pointer from the linked list and      */
64583b9b51feSCy Schubert /* copy it out to the storage space pointed to by itp_data.  The next item  */
64593b9b51feSCy Schubert /* in the list to look at is put back in the ipftoken struture.             */
64603b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
64613b9b51feSCy Schubert static int
ipf_nat_getnext(ipf_main_softc_t * softc,ipftoken_t * t,ipfgeniter_t * itp,ipfobj_t * objp)6462064a5a95SCy Schubert ipf_nat_getnext(ipf_main_softc_t *softc, ipftoken_t *t, ipfgeniter_t *itp,
6463064a5a95SCy Schubert 	ipfobj_t *objp)
64643b9b51feSCy Schubert {
64653b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
64663b9b51feSCy Schubert 	hostmap_t *hm, *nexthm = NULL, zerohm;
64673b9b51feSCy Schubert 	ipnat_t *ipn, *nextipnat = NULL, zeroipn;
64683b9b51feSCy Schubert 	nat_t *nat, *nextnat = NULL, zeronat;
64693b9b51feSCy Schubert 	int error = 0;
64703b9b51feSCy Schubert 	void *nnext;
64713b9b51feSCy Schubert 
64723b9b51feSCy Schubert 	if (itp->igi_nitems != 1) {
64733b9b51feSCy Schubert 		IPFERROR(60075);
64748c82b374SCy Schubert 		return (ENOSPC);
64753b9b51feSCy Schubert 	}
64763b9b51feSCy Schubert 
64773b9b51feSCy Schubert 	READ_ENTER(&softc->ipf_nat);
64783b9b51feSCy Schubert 
64793b9b51feSCy Schubert 	switch (itp->igi_type)
64803b9b51feSCy Schubert 	{
64813b9b51feSCy Schubert 	case IPFGENITER_HOSTMAP :
64823b9b51feSCy Schubert 		hm = t->ipt_data;
64833b9b51feSCy Schubert 		if (hm == NULL) {
64843b9b51feSCy Schubert 			nexthm = softn->ipf_hm_maplist;
64853b9b51feSCy Schubert 		} else {
64863b9b51feSCy Schubert 			nexthm = hm->hm_next;
64873b9b51feSCy Schubert 		}
64883b9b51feSCy Schubert 		if (nexthm != NULL) {
64893b9b51feSCy Schubert 			ATOMIC_INC32(nexthm->hm_ref);
64903b9b51feSCy Schubert 			t->ipt_data = nexthm;
64913b9b51feSCy Schubert 		} else {
64923b9b51feSCy Schubert 			bzero(&zerohm, sizeof(zerohm));
64933b9b51feSCy Schubert 			nexthm = &zerohm;
64943b9b51feSCy Schubert 			t->ipt_data = NULL;
64953b9b51feSCy Schubert 		}
64963b9b51feSCy Schubert 		nnext = nexthm->hm_next;
64973b9b51feSCy Schubert 		break;
64983b9b51feSCy Schubert 
64993b9b51feSCy Schubert 	case IPFGENITER_IPNAT :
65003b9b51feSCy Schubert 		ipn = t->ipt_data;
65013b9b51feSCy Schubert 		if (ipn == NULL) {
65023b9b51feSCy Schubert 			nextipnat = softn->ipf_nat_list;
65033b9b51feSCy Schubert 		} else {
65043b9b51feSCy Schubert 			nextipnat = ipn->in_next;
65053b9b51feSCy Schubert 		}
65063b9b51feSCy Schubert 		if (nextipnat != NULL) {
65073b9b51feSCy Schubert 			ATOMIC_INC32(nextipnat->in_use);
65083b9b51feSCy Schubert 			t->ipt_data = nextipnat;
65093b9b51feSCy Schubert 		} else {
65103b9b51feSCy Schubert 			bzero(&zeroipn, sizeof(zeroipn));
65113b9b51feSCy Schubert 			nextipnat = &zeroipn;
65123b9b51feSCy Schubert 			t->ipt_data = NULL;
65133b9b51feSCy Schubert 		}
65143b9b51feSCy Schubert 		nnext = nextipnat->in_next;
65153b9b51feSCy Schubert 		break;
65163b9b51feSCy Schubert 
65173b9b51feSCy Schubert 	case IPFGENITER_NAT :
65183b9b51feSCy Schubert 		nat = t->ipt_data;
65193b9b51feSCy Schubert 		if (nat == NULL) {
65203b9b51feSCy Schubert 			nextnat = softn->ipf_nat_instances;
65213b9b51feSCy Schubert 		} else {
65223b9b51feSCy Schubert 			nextnat = nat->nat_next;
65233b9b51feSCy Schubert 		}
65243b9b51feSCy Schubert 		if (nextnat != NULL) {
65253b9b51feSCy Schubert 			MUTEX_ENTER(&nextnat->nat_lock);
65263b9b51feSCy Schubert 			nextnat->nat_ref++;
65273b9b51feSCy Schubert 			MUTEX_EXIT(&nextnat->nat_lock);
65283b9b51feSCy Schubert 			t->ipt_data = nextnat;
65293b9b51feSCy Schubert 		} else {
65303b9b51feSCy Schubert 			bzero(&zeronat, sizeof(zeronat));
65313b9b51feSCy Schubert 			nextnat = &zeronat;
65323b9b51feSCy Schubert 			t->ipt_data = NULL;
65333b9b51feSCy Schubert 		}
65343b9b51feSCy Schubert 		nnext = nextnat->nat_next;
65353b9b51feSCy Schubert 		break;
65363b9b51feSCy Schubert 
65373b9b51feSCy Schubert 	default :
65383b9b51feSCy Schubert 		RWLOCK_EXIT(&softc->ipf_nat);
65393b9b51feSCy Schubert 		IPFERROR(60055);
65408c82b374SCy Schubert 		return (EINVAL);
65413b9b51feSCy Schubert 	}
65423b9b51feSCy Schubert 
65433b9b51feSCy Schubert 	RWLOCK_EXIT(&softc->ipf_nat);
65443b9b51feSCy Schubert 
65453b9b51feSCy Schubert 	objp->ipfo_ptr = itp->igi_data;
65463b9b51feSCy Schubert 
65473b9b51feSCy Schubert 	switch (itp->igi_type)
65483b9b51feSCy Schubert 	{
65493b9b51feSCy Schubert 	case IPFGENITER_HOSTMAP :
65503b9b51feSCy Schubert 		error = COPYOUT(nexthm, objp->ipfo_ptr, sizeof(*nexthm));
65513b9b51feSCy Schubert 		if (error != 0) {
65523b9b51feSCy Schubert 			IPFERROR(60049);
65533b9b51feSCy Schubert 			error = EFAULT;
65543b9b51feSCy Schubert 		}
65553b9b51feSCy Schubert 		if (hm != NULL) {
65563b9b51feSCy Schubert 			WRITE_ENTER(&softc->ipf_nat);
65573b9b51feSCy Schubert 			ipf_nat_hostmapdel(softc, &hm);
65583b9b51feSCy Schubert 			RWLOCK_EXIT(&softc->ipf_nat);
65593b9b51feSCy Schubert 		}
65603b9b51feSCy Schubert 		break;
65613b9b51feSCy Schubert 
65623b9b51feSCy Schubert 	case IPFGENITER_IPNAT :
65633b9b51feSCy Schubert 		objp->ipfo_size = nextipnat->in_size;
65643b9b51feSCy Schubert 		objp->ipfo_type = IPFOBJ_IPNAT;
65653b9b51feSCy Schubert 		error = ipf_outobjk(softc, objp, nextipnat);
65663b9b51feSCy Schubert 		if (ipn != NULL) {
65673b9b51feSCy Schubert 			WRITE_ENTER(&softc->ipf_nat);
65683b9b51feSCy Schubert 			ipf_nat_rule_deref(softc, &ipn);
65693b9b51feSCy Schubert 			RWLOCK_EXIT(&softc->ipf_nat);
65703b9b51feSCy Schubert 		}
65713b9b51feSCy Schubert 		break;
65723b9b51feSCy Schubert 
65733b9b51feSCy Schubert 	case IPFGENITER_NAT :
65743b9b51feSCy Schubert 		objp->ipfo_size = sizeof(nat_t);
65753b9b51feSCy Schubert 		objp->ipfo_type = IPFOBJ_NAT;
65763b9b51feSCy Schubert 		error = ipf_outobjk(softc, objp, nextnat);
65773b9b51feSCy Schubert 		if (nat != NULL)
65783b9b51feSCy Schubert 			ipf_nat_deref(softc, &nat);
65793b9b51feSCy Schubert 
65803b9b51feSCy Schubert 		break;
65813b9b51feSCy Schubert 	}
65823b9b51feSCy Schubert 
65833b9b51feSCy Schubert 	if (nnext == NULL)
65843b9b51feSCy Schubert 		ipf_token_mark_complete(t);
65853b9b51feSCy Schubert 
65868c82b374SCy Schubert 	return (error);
65873b9b51feSCy Schubert }
65883b9b51feSCy Schubert 
65893b9b51feSCy Schubert 
65903b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
65913b9b51feSCy Schubert /* Function:    nat_extraflush                                              */
65923b9b51feSCy Schubert /* Returns:     int - 0 == success, -1 == failure                           */
65933b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
65943b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
65953b9b51feSCy Schubert /*              which(I) - how to flush the active NAT table                */
65963b9b51feSCy Schubert /* Write Locks: ipf_nat                                                     */
65973b9b51feSCy Schubert /*                                                                          */
65983b9b51feSCy Schubert /* Flush nat tables.  Three actions currently defined:                      */
65993b9b51feSCy Schubert /* which == 0 : flush all nat table entries                                 */
66003b9b51feSCy Schubert /* which == 1 : flush TCP connections which have started to close but are   */
66013b9b51feSCy Schubert /*	      stuck for some reason.                                        */
66023b9b51feSCy Schubert /* which == 2 : flush TCP connections which have been idle for a long time, */
66033b9b51feSCy Schubert /*	      starting at > 4 days idle and working back in successive half-*/
66043b9b51feSCy Schubert /*	      days to at most 12 hours old.  If this fails to free enough   */
66053b9b51feSCy Schubert /*            slots then work backwards in half hour slots to 30 minutes.   */
66063b9b51feSCy Schubert /*            If that too fails, then work backwards in 30 second intervals */
66073b9b51feSCy Schubert /*            for the last 30 minutes to at worst 30 seconds idle.          */
66083b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
66093b9b51feSCy Schubert static int
ipf_nat_extraflush(ipf_main_softc_t * softc,ipf_nat_softc_t * softn,int which)6610064a5a95SCy Schubert ipf_nat_extraflush(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, int which)
66113b9b51feSCy Schubert {
66123b9b51feSCy Schubert 	nat_t *nat, **natp;
66133b9b51feSCy Schubert 	ipftqent_t *tqn;
66143b9b51feSCy Schubert 	ipftq_t *ifq;
66153b9b51feSCy Schubert 	int removed;
66163b9b51feSCy Schubert 	SPL_INT(s);
66173b9b51feSCy Schubert 
66183b9b51feSCy Schubert 	removed = 0;
66193b9b51feSCy Schubert 
66203b9b51feSCy Schubert 	SPL_NET(s);
66213b9b51feSCy Schubert 	switch (which)
66223b9b51feSCy Schubert 	{
66233b9b51feSCy Schubert 	case 0 :
66243b9b51feSCy Schubert 		softn->ipf_nat_stats.ns_flush_all++;
66253b9b51feSCy Schubert 		/*
66263b9b51feSCy Schubert 		 * Style 0 flush removes everything...
66273b9b51feSCy Schubert 		 */
66283b9b51feSCy Schubert 		for (natp = &softn->ipf_nat_instances;
66293b9b51feSCy Schubert 		     ((nat = *natp) != NULL); ) {
66303b9b51feSCy Schubert 			ipf_nat_delete(softc, nat, NL_FLUSH);
66313b9b51feSCy Schubert 			removed++;
66323b9b51feSCy Schubert 		}
66333b9b51feSCy Schubert 		break;
66343b9b51feSCy Schubert 
66353b9b51feSCy Schubert 	case 1 :
66363b9b51feSCy Schubert 		softn->ipf_nat_stats.ns_flush_closing++;
66373b9b51feSCy Schubert 		/*
66383b9b51feSCy Schubert 		 * Since we're only interested in things that are closing,
66393b9b51feSCy Schubert 		 * we can start with the appropriate timeout queue.
66403b9b51feSCy Schubert 		 */
66413b9b51feSCy Schubert 		for (ifq = softn->ipf_nat_tcptq + IPF_TCPS_CLOSE_WAIT;
66423b9b51feSCy Schubert 		     ifq != NULL; ifq = ifq->ifq_next) {
66433b9b51feSCy Schubert 
66443b9b51feSCy Schubert 			for (tqn = ifq->ifq_head; tqn != NULL; ) {
66453b9b51feSCy Schubert 				nat = tqn->tqe_parent;
66463b9b51feSCy Schubert 				tqn = tqn->tqe_next;
66473b9b51feSCy Schubert 				if (nat->nat_pr[0] != IPPROTO_TCP ||
66483b9b51feSCy Schubert 				    nat->nat_pr[1] != IPPROTO_TCP)
66493b9b51feSCy Schubert 					break;
66503b9b51feSCy Schubert 				ipf_nat_delete(softc, nat, NL_EXPIRE);
66513b9b51feSCy Schubert 				removed++;
66523b9b51feSCy Schubert 			}
66533b9b51feSCy Schubert 		}
66543b9b51feSCy Schubert 
66553b9b51feSCy Schubert 		/*
66563b9b51feSCy Schubert 		 * Also need to look through the user defined queues.
66573b9b51feSCy Schubert 		 */
66583b9b51feSCy Schubert 		for (ifq = softn->ipf_nat_utqe; ifq != NULL;
66593b9b51feSCy Schubert 		     ifq = ifq->ifq_next) {
66603b9b51feSCy Schubert 			for (tqn = ifq->ifq_head; tqn != NULL; ) {
66613b9b51feSCy Schubert 				nat = tqn->tqe_parent;
66623b9b51feSCy Schubert 				tqn = tqn->tqe_next;
66633b9b51feSCy Schubert 				if (nat->nat_pr[0] != IPPROTO_TCP ||
66643b9b51feSCy Schubert 				    nat->nat_pr[1] != IPPROTO_TCP)
66653b9b51feSCy Schubert 					continue;
66663b9b51feSCy Schubert 
66673b9b51feSCy Schubert 				if ((nat->nat_tcpstate[0] >
66683b9b51feSCy Schubert 				     IPF_TCPS_ESTABLISHED) &&
66693b9b51feSCy Schubert 				    (nat->nat_tcpstate[1] >
66703b9b51feSCy Schubert 				     IPF_TCPS_ESTABLISHED)) {
66713b9b51feSCy Schubert 					ipf_nat_delete(softc, nat, NL_EXPIRE);
66723b9b51feSCy Schubert 					removed++;
66733b9b51feSCy Schubert 				}
66743b9b51feSCy Schubert 			}
66753b9b51feSCy Schubert 		}
66763b9b51feSCy Schubert 		break;
66773b9b51feSCy Schubert 
66783b9b51feSCy Schubert 		/*
66793b9b51feSCy Schubert 		 * Args 5-11 correspond to flushing those particular states
66803b9b51feSCy Schubert 		 * for TCP connections.
66813b9b51feSCy Schubert 		 */
66823b9b51feSCy Schubert 	case IPF_TCPS_CLOSE_WAIT :
66833b9b51feSCy Schubert 	case IPF_TCPS_FIN_WAIT_1 :
66843b9b51feSCy Schubert 	case IPF_TCPS_CLOSING :
66853b9b51feSCy Schubert 	case IPF_TCPS_LAST_ACK :
66863b9b51feSCy Schubert 	case IPF_TCPS_FIN_WAIT_2 :
66873b9b51feSCy Schubert 	case IPF_TCPS_TIME_WAIT :
66883b9b51feSCy Schubert 	case IPF_TCPS_CLOSED :
66893b9b51feSCy Schubert 		softn->ipf_nat_stats.ns_flush_state++;
66903b9b51feSCy Schubert 		tqn = softn->ipf_nat_tcptq[which].ifq_head;
66913b9b51feSCy Schubert 		while (tqn != NULL) {
66923b9b51feSCy Schubert 			nat = tqn->tqe_parent;
66933b9b51feSCy Schubert 			tqn = tqn->tqe_next;
66943b9b51feSCy Schubert 			ipf_nat_delete(softc, nat, NL_FLUSH);
66953b9b51feSCy Schubert 			removed++;
66963b9b51feSCy Schubert 		}
66973b9b51feSCy Schubert 		break;
66983b9b51feSCy Schubert 
66993b9b51feSCy Schubert 	default :
67003b9b51feSCy Schubert 		if (which < 30)
67013b9b51feSCy Schubert 			break;
67023b9b51feSCy Schubert 
67033b9b51feSCy Schubert 		softn->ipf_nat_stats.ns_flush_timeout++;
67043b9b51feSCy Schubert 		/*
67053b9b51feSCy Schubert 		 * Take a large arbitrary number to mean the number of seconds
67063b9b51feSCy Schubert 		 * for which which consider to be the maximum value we'll allow
67073b9b51feSCy Schubert 		 * the expiration to be.
67083b9b51feSCy Schubert 		 */
67093b9b51feSCy Schubert 		which = IPF_TTLVAL(which);
67103b9b51feSCy Schubert 		for (natp = &softn->ipf_nat_instances;
67113b9b51feSCy Schubert 		     ((nat = *natp) != NULL); ) {
67123b9b51feSCy Schubert 			if (softc->ipf_ticks - nat->nat_touched > which) {
67133b9b51feSCy Schubert 				ipf_nat_delete(softc, nat, NL_FLUSH);
67143b9b51feSCy Schubert 				removed++;
67153b9b51feSCy Schubert 			} else
67163b9b51feSCy Schubert 				natp = &nat->nat_next;
67173b9b51feSCy Schubert 		}
67183b9b51feSCy Schubert 		break;
67193b9b51feSCy Schubert 	}
67203b9b51feSCy Schubert 
67213b9b51feSCy Schubert 	if (which != 2) {
67223b9b51feSCy Schubert 		SPL_X(s);
67238c82b374SCy Schubert 		return (removed);
67243b9b51feSCy Schubert 	}
67253b9b51feSCy Schubert 
67263b9b51feSCy Schubert 	softn->ipf_nat_stats.ns_flush_queue++;
67273b9b51feSCy Schubert 
67283b9b51feSCy Schubert 	/*
67293b9b51feSCy Schubert 	 * Asked to remove inactive entries because the table is full, try
67303b9b51feSCy Schubert 	 * again, 3 times, if first attempt failed with a different criteria
67313b9b51feSCy Schubert 	 * each time.  The order tried in must be in decreasing age.
67323b9b51feSCy Schubert 	 * Another alternative is to implement random drop and drop N entries
67333b9b51feSCy Schubert 	 * at random until N have been freed up.
67343b9b51feSCy Schubert 	 */
67353b9b51feSCy Schubert 	if (softc->ipf_ticks - softn->ipf_nat_last_force_flush >
67363b9b51feSCy Schubert 	    IPF_TTLVAL(5)) {
67373b9b51feSCy Schubert 		softn->ipf_nat_last_force_flush = softc->ipf_ticks;
67383b9b51feSCy Schubert 
67393b9b51feSCy Schubert 		removed = ipf_queueflush(softc, ipf_nat_flush_entry,
67403b9b51feSCy Schubert 					 softn->ipf_nat_tcptq,
67413b9b51feSCy Schubert 					 softn->ipf_nat_utqe,
67423b9b51feSCy Schubert 					 &softn->ipf_nat_stats.ns_active,
67433b9b51feSCy Schubert 					 softn->ipf_nat_table_sz,
67443b9b51feSCy Schubert 					 softn->ipf_nat_table_wm_low);
67453b9b51feSCy Schubert 	}
67463b9b51feSCy Schubert 
67473b9b51feSCy Schubert 	SPL_X(s);
67488c82b374SCy Schubert 	return (removed);
67493b9b51feSCy Schubert }
67503b9b51feSCy Schubert 
67513b9b51feSCy Schubert 
67523b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
67533b9b51feSCy Schubert /* Function:    ipf_nat_flush_entry                                         */
67543b9b51feSCy Schubert /* Returns:     0 - always succeeds                                         */
67553b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
67563b9b51feSCy Schubert /*              entry(I) - pointer to NAT entry                             */
67573b9b51feSCy Schubert /* Write Locks: ipf_nat                                                     */
67583b9b51feSCy Schubert /*                                                                          */
67593b9b51feSCy Schubert /* This function is a stepping stone between ipf_queueflush() and           */
67603b9b51feSCy Schubert /* nat_dlete().  It is used so we can provide a uniform interface via the   */
67613b9b51feSCy Schubert /* ipf_queueflush() function.  Since the nat_delete() function returns void */
67623b9b51feSCy Schubert /* we translate that to mean it always succeeds in deleting something.      */
67633b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
67643b9b51feSCy Schubert static int
ipf_nat_flush_entry(ipf_main_softc_t * softc,void * entry)6765064a5a95SCy Schubert ipf_nat_flush_entry(ipf_main_softc_t *softc, void *entry)
67663b9b51feSCy Schubert {
67673b9b51feSCy Schubert 	ipf_nat_delete(softc, entry, NL_FLUSH);
67688c82b374SCy Schubert 	return (0);
67693b9b51feSCy Schubert }
67703b9b51feSCy Schubert 
67713b9b51feSCy Schubert 
67723b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
67733b9b51feSCy Schubert /* Function:    ipf_nat_iterator                                            */
67743b9b51feSCy Schubert /* Returns:     int - 0 == ok, else error                                   */
67753b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
67763b9b51feSCy Schubert /*              token(I) - pointer to ipftoken structure                    */
67773b9b51feSCy Schubert /*              itp(I)   - pointer to ipfgeniter_t structure                */
67783b9b51feSCy Schubert /*              obj(I)   - pointer to data description structure            */
67793b9b51feSCy Schubert /*                                                                          */
67803b9b51feSCy Schubert /* This function acts as a handler for the SIOCGENITER ioctls that use a    */
67813b9b51feSCy Schubert /* generic structure to iterate through a list.  There are three different  */
67823b9b51feSCy Schubert /* linked lists of NAT related information to go through: NAT rules, active */
67833b9b51feSCy Schubert /* NAT mappings and the NAT fragment cache.                                 */
67843b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
67853b9b51feSCy Schubert static int
ipf_nat_iterator(ipf_main_softc_t * softc,ipftoken_t * token,ipfgeniter_t * itp,ipfobj_t * obj)6786064a5a95SCy Schubert ipf_nat_iterator(ipf_main_softc_t *softc, ipftoken_t *token, ipfgeniter_t *itp,
6787064a5a95SCy Schubert 	ipfobj_t *obj)
67883b9b51feSCy Schubert {
67893b9b51feSCy Schubert 	int error;
67903b9b51feSCy Schubert 
67913b9b51feSCy Schubert 	if (itp->igi_data == NULL) {
67923b9b51feSCy Schubert 		IPFERROR(60052);
67938c82b374SCy Schubert 		return (EFAULT);
67943b9b51feSCy Schubert 	}
67953b9b51feSCy Schubert 
67963b9b51feSCy Schubert 	switch (itp->igi_type)
67973b9b51feSCy Schubert 	{
67983b9b51feSCy Schubert 	case IPFGENITER_HOSTMAP :
67993b9b51feSCy Schubert 	case IPFGENITER_IPNAT :
68003b9b51feSCy Schubert 	case IPFGENITER_NAT :
68013b9b51feSCy Schubert 		error = ipf_nat_getnext(softc, token, itp, obj);
68023b9b51feSCy Schubert 		break;
68033b9b51feSCy Schubert 
68043b9b51feSCy Schubert 	case IPFGENITER_NATFRAG :
68053b9b51feSCy Schubert 		error = ipf_frag_nat_next(softc, token, itp);
68063b9b51feSCy Schubert 		break;
68073b9b51feSCy Schubert 	default :
68083b9b51feSCy Schubert 		IPFERROR(60053);
68093b9b51feSCy Schubert 		error = EINVAL;
68103b9b51feSCy Schubert 		break;
68113b9b51feSCy Schubert 	}
68123b9b51feSCy Schubert 
68138c82b374SCy Schubert 	return (error);
68143b9b51feSCy Schubert }
68153b9b51feSCy Schubert 
68163b9b51feSCy Schubert 
68173b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
68183b9b51feSCy Schubert /* Function:    ipf_nat_setpending                                          */
68193b9b51feSCy Schubert /* Returns:     Nil                                                         */
68203b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
68213b9b51feSCy Schubert /*              nat(I)   - pointer to NAT structure                         */
68223b9b51feSCy Schubert /* Locks:       ipf_nat (read or write)                                     */
68233b9b51feSCy Schubert /*                                                                          */
68243b9b51feSCy Schubert /* Put the NAT entry on to the pending queue - this queue has a very short  */
68253b9b51feSCy Schubert /* lifetime where items are put that can't be deleted straight away because */
68263b9b51feSCy Schubert /* of locking issues but we want to delete them ASAP, anyway.  In calling   */
68273b9b51feSCy Schubert /* this function, it is assumed that the owner (if there is one, as shown   */
68283b9b51feSCy Schubert /* by nat_me) is no longer interested in it.                                */
68293b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
68303b9b51feSCy Schubert void
ipf_nat_setpending(ipf_main_softc_t * softc,nat_t * nat)6831064a5a95SCy Schubert ipf_nat_setpending(ipf_main_softc_t *softc, nat_t *nat)
68323b9b51feSCy Schubert {
68333b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
68343b9b51feSCy Schubert 	ipftq_t *oifq;
68353b9b51feSCy Schubert 
68363b9b51feSCy Schubert 	oifq = nat->nat_tqe.tqe_ifq;
68373b9b51feSCy Schubert 	if (oifq != NULL)
68383b9b51feSCy Schubert 		ipf_movequeue(softc->ipf_ticks, &nat->nat_tqe, oifq,
68393b9b51feSCy Schubert 			      &softn->ipf_nat_pending);
68403b9b51feSCy Schubert 	else
68413b9b51feSCy Schubert 		ipf_queueappend(softc->ipf_ticks, &nat->nat_tqe,
68423b9b51feSCy Schubert 				&softn->ipf_nat_pending, nat);
68433b9b51feSCy Schubert 
68443b9b51feSCy Schubert 	if (nat->nat_me != NULL) {
68453b9b51feSCy Schubert 		*nat->nat_me = NULL;
68463b9b51feSCy Schubert 		nat->nat_me = NULL;
68473b9b51feSCy Schubert 		nat->nat_ref--;
68483b9b51feSCy Schubert 		ASSERT(nat->nat_ref >= 0);
68493b9b51feSCy Schubert 	}
68503b9b51feSCy Schubert }
68513b9b51feSCy Schubert 
68523b9b51feSCy Schubert 
68533b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
68543b9b51feSCy Schubert /* Function:    nat_newrewrite                                              */
68553b9b51feSCy Schubert /* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
68563b9b51feSCy Schubert /*                    allow rule to be moved if IPN_ROUNDR is set.          */
68573b9b51feSCy Schubert /* Parameters:  fin(I) - pointer to packet information                      */
68583b9b51feSCy Schubert /*              nat(I) - pointer to NAT entry                               */
68593b9b51feSCy Schubert /*              ni(I)  - pointer to structure with misc. information needed */
68603b9b51feSCy Schubert /*                       to create new NAT entry.                           */
68613b9b51feSCy Schubert /* Write Lock:  ipf_nat                                                     */
68623b9b51feSCy Schubert /*                                                                          */
68633b9b51feSCy Schubert /* This function is responsible for setting up an active NAT session where  */
68643b9b51feSCy Schubert /* we are changing both the source and destination parameters at the same   */
68653b9b51feSCy Schubert /* time.  The loop in here works differently to elsewhere - each iteration  */
68663b9b51feSCy Schubert /* is responsible for changing a single parameter that can be incremented.  */
68673b9b51feSCy Schubert /* So one pass may increase the source IP#, next source port, next dest. IP#*/
68683b9b51feSCy Schubert /* and the last destination port for a total of 4 iterations to try each.   */
68693b9b51feSCy Schubert /* This is done to try and exhaustively use the translation space available.*/
68703b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
68713b9b51feSCy Schubert static int
ipf_nat_newrewrite(fr_info_t * fin,nat_t * nat,natinfo_t * nai)6872064a5a95SCy Schubert ipf_nat_newrewrite(fr_info_t *fin, nat_t *nat, natinfo_t *nai)
68733b9b51feSCy Schubert {
68743b9b51feSCy Schubert 	int src_search = 1;
68753b9b51feSCy Schubert 	int dst_search = 1;
68763b9b51feSCy Schubert 	fr_info_t frnat;
68773b9b51feSCy Schubert 	u_32_t flags;
68783b9b51feSCy Schubert 	u_short swap;
68793b9b51feSCy Schubert 	ipnat_t *np;
68803b9b51feSCy Schubert 	nat_t *natl;
68813b9b51feSCy Schubert 	int l = 0;
68823b9b51feSCy Schubert 	int changed;
68833b9b51feSCy Schubert 
68843b9b51feSCy Schubert 	natl = NULL;
68853b9b51feSCy Schubert 	changed = -1;
68863b9b51feSCy Schubert 	np = nai->nai_np;
68873b9b51feSCy Schubert 	flags = nat->nat_flags;
68883b9b51feSCy Schubert 	bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
68893b9b51feSCy Schubert 
68903b9b51feSCy Schubert 	nat->nat_hm = NULL;
68913b9b51feSCy Schubert 
68923b9b51feSCy Schubert 	do {
68933b9b51feSCy Schubert 		changed = -1;
68943b9b51feSCy Schubert 		/* TRACE (l, src_search, dst_search, np) */
68953b9b51feSCy Schubert 		DT4(ipf_nat_rewrite_1, int, l, int, src_search, int, dst_search, ipnat_t *, np);
68963b9b51feSCy Schubert 
68973b9b51feSCy Schubert 		if ((src_search == 0) && (np->in_spnext == 0) &&
68983b9b51feSCy Schubert 		    (dst_search == 0) && (np->in_dpnext == 0)) {
68993b9b51feSCy Schubert 			if (l > 0)
69008c82b374SCy Schubert 				return (-1);
69013b9b51feSCy Schubert 		}
69023b9b51feSCy Schubert 
69033b9b51feSCy Schubert 		/*
69043b9b51feSCy Schubert 		 * Find a new source address
69053b9b51feSCy Schubert 		 */
69063b9b51feSCy Schubert 		if (ipf_nat_nextaddr(fin, &np->in_nsrc, &frnat.fin_saddr,
69073b9b51feSCy Schubert 				     &frnat.fin_saddr) == -1) {
69088c82b374SCy Schubert 			return (-1);
69093b9b51feSCy Schubert 		}
69103b9b51feSCy Schubert 
69113b9b51feSCy Schubert 		if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0xffffffff)) {
69123b9b51feSCy Schubert 			src_search = 0;
69133b9b51feSCy Schubert 			if (np->in_stepnext == 0)
69143b9b51feSCy Schubert 				np->in_stepnext = 1;
69153b9b51feSCy Schubert 
69163b9b51feSCy Schubert 		} else if ((np->in_nsrcaddr == 0) && (np->in_nsrcmsk == 0)) {
69173b9b51feSCy Schubert 			src_search = 0;
69183b9b51feSCy Schubert 			if (np->in_stepnext == 0)
69193b9b51feSCy Schubert 				np->in_stepnext = 1;
69203b9b51feSCy Schubert 
69213b9b51feSCy Schubert 		} else if (np->in_nsrcmsk == 0xffffffff) {
69223b9b51feSCy Schubert 			src_search = 0;
69233b9b51feSCy Schubert 			if (np->in_stepnext == 0)
69243b9b51feSCy Schubert 				np->in_stepnext = 1;
69253b9b51feSCy Schubert 
69263b9b51feSCy Schubert 		} else if (np->in_nsrcmsk != 0xffffffff) {
69273b9b51feSCy Schubert 			if (np->in_stepnext == 0 && changed == -1) {
69283b9b51feSCy Schubert 				np->in_snip++;
69293b9b51feSCy Schubert 				np->in_stepnext++;
69303b9b51feSCy Schubert 				changed = 0;
69313b9b51feSCy Schubert 			}
69323b9b51feSCy Schubert 		}
69333b9b51feSCy Schubert 
69343b9b51feSCy Schubert 		if ((flags & IPN_TCPUDPICMP) != 0) {
69353b9b51feSCy Schubert 			if (np->in_spnext != 0)
69363b9b51feSCy Schubert 				frnat.fin_data[0] = np->in_spnext;
69373b9b51feSCy Schubert 
69383b9b51feSCy Schubert 			/*
69393b9b51feSCy Schubert 			 * Standard port translation.  Select next port.
69403b9b51feSCy Schubert 			 */
69413b9b51feSCy Schubert 			if ((flags & IPN_FIXEDSPORT) != 0) {
69423b9b51feSCy Schubert 				np->in_stepnext = 2;
69433b9b51feSCy Schubert 			} else if ((np->in_stepnext == 1) &&
69443b9b51feSCy Schubert 				   (changed == -1) && (natl != NULL)) {
69453b9b51feSCy Schubert 				np->in_spnext++;
69463b9b51feSCy Schubert 				np->in_stepnext++;
69473b9b51feSCy Schubert 				changed = 1;
69483b9b51feSCy Schubert 				if (np->in_spnext > np->in_spmax)
69493b9b51feSCy Schubert 					np->in_spnext = np->in_spmin;
69503b9b51feSCy Schubert 			}
69513b9b51feSCy Schubert 		} else {
69523b9b51feSCy Schubert 			np->in_stepnext = 2;
69533b9b51feSCy Schubert 		}
69543b9b51feSCy Schubert 		np->in_stepnext &= 0x3;
69553b9b51feSCy Schubert 
69563b9b51feSCy Schubert 		/*
69573b9b51feSCy Schubert 		 * Find a new destination address
69583b9b51feSCy Schubert 		 */
69593b9b51feSCy Schubert 		/* TRACE (fin, np, l, frnat) */
69603b9b51feSCy Schubert 		DT4(ipf_nat_rewrite_2, frinfo_t *, fin, ipnat_t *, np, int, l, frinfo_t *, &frnat);
69613b9b51feSCy Schubert 
69623b9b51feSCy Schubert 		if (ipf_nat_nextaddr(fin, &np->in_ndst, &frnat.fin_daddr,
69633b9b51feSCy Schubert 				     &frnat.fin_daddr) == -1)
69648c82b374SCy Schubert 			return (-1);
69653b9b51feSCy Schubert 		if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0xffffffff)) {
69663b9b51feSCy Schubert 			dst_search = 0;
69673b9b51feSCy Schubert 			if (np->in_stepnext == 2)
69683b9b51feSCy Schubert 				np->in_stepnext = 3;
69693b9b51feSCy Schubert 
69703b9b51feSCy Schubert 		} else if ((np->in_ndstaddr == 0) && (np->in_ndstmsk == 0)) {
69713b9b51feSCy Schubert 			dst_search = 0;
69723b9b51feSCy Schubert 			if (np->in_stepnext == 2)
69733b9b51feSCy Schubert 				np->in_stepnext = 3;
69743b9b51feSCy Schubert 
69753b9b51feSCy Schubert 		} else if (np->in_ndstmsk == 0xffffffff) {
69763b9b51feSCy Schubert 			dst_search = 0;
69773b9b51feSCy Schubert 			if (np->in_stepnext == 2)
69783b9b51feSCy Schubert 				np->in_stepnext = 3;
69793b9b51feSCy Schubert 
69803b9b51feSCy Schubert 		} else if (np->in_ndstmsk != 0xffffffff) {
69813b9b51feSCy Schubert 			if ((np->in_stepnext == 2) && (changed == -1) &&
69823b9b51feSCy Schubert 			    (natl != NULL)) {
69833b9b51feSCy Schubert 				changed = 2;
69843b9b51feSCy Schubert 				np->in_stepnext++;
69853b9b51feSCy Schubert 				np->in_dnip++;
69863b9b51feSCy Schubert 			}
69873b9b51feSCy Schubert 		}
69883b9b51feSCy Schubert 
69893b9b51feSCy Schubert 		if ((flags & IPN_TCPUDPICMP) != 0) {
69903b9b51feSCy Schubert 			if (np->in_dpnext != 0)
69913b9b51feSCy Schubert 				frnat.fin_data[1] = np->in_dpnext;
69923b9b51feSCy Schubert 
69933b9b51feSCy Schubert 			/*
69943b9b51feSCy Schubert 			 * Standard port translation.  Select next port.
69953b9b51feSCy Schubert 			 */
69963b9b51feSCy Schubert 			if ((flags & IPN_FIXEDDPORT) != 0) {
69973b9b51feSCy Schubert 				np->in_stepnext = 0;
69983b9b51feSCy Schubert 			} else if (np->in_stepnext == 3 && changed == -1) {
69993b9b51feSCy Schubert 				np->in_dpnext++;
70003b9b51feSCy Schubert 				np->in_stepnext++;
70013b9b51feSCy Schubert 				changed = 3;
70023b9b51feSCy Schubert 				if (np->in_dpnext > np->in_dpmax)
70033b9b51feSCy Schubert 					np->in_dpnext = np->in_dpmin;
70043b9b51feSCy Schubert 			}
70053b9b51feSCy Schubert 		} else {
70063b9b51feSCy Schubert 			if (np->in_stepnext == 3)
70073b9b51feSCy Schubert 				np->in_stepnext = 0;
70083b9b51feSCy Schubert 		}
70093b9b51feSCy Schubert 
70103b9b51feSCy Schubert 		/* TRACE (frnat) */
70113b9b51feSCy Schubert 		DT1(ipf_nat_rewrite_3, frinfo_t *, &frnat);
70123b9b51feSCy Schubert 
70133b9b51feSCy Schubert 		/*
70143b9b51feSCy Schubert 		 * Here we do a lookup of the connection as seen from
70153b9b51feSCy Schubert 		 * the outside.  If an IP# pair already exists, try
70163b9b51feSCy Schubert 		 * again.  So if you have A->B becomes C->B, you can
70173b9b51feSCy Schubert 		 * also have D->E become C->E but not D->B causing
70183b9b51feSCy Schubert 		 * another C->B.  Also take protocol and ports into
70193b9b51feSCy Schubert 		 * account when determining whether a pre-existing
70203b9b51feSCy Schubert 		 * NAT setup will cause an external conflict where
70213b9b51feSCy Schubert 		 * this is appropriate.
70223b9b51feSCy Schubert 		 *
70233b9b51feSCy Schubert 		 * fin_data[] is swapped around because we are doing a
70243b9b51feSCy Schubert 		 * lookup of the packet is if it were moving in the opposite
70253b9b51feSCy Schubert 		 * direction of the one we are working with now.
70263b9b51feSCy Schubert 		 */
70273b9b51feSCy Schubert 		if (flags & IPN_TCPUDP) {
70283b9b51feSCy Schubert 			swap = frnat.fin_data[0];
70293b9b51feSCy Schubert 			frnat.fin_data[0] = frnat.fin_data[1];
70303b9b51feSCy Schubert 			frnat.fin_data[1] = swap;
70313b9b51feSCy Schubert 		}
70323b9b51feSCy Schubert 		if (fin->fin_out == 1) {
70333b9b51feSCy Schubert 			natl = ipf_nat_inlookup(&frnat,
70343b9b51feSCy Schubert 						flags & ~(SI_WILDP|NAT_SEARCH),
70353b9b51feSCy Schubert 						(u_int)frnat.fin_p,
70363b9b51feSCy Schubert 						frnat.fin_dst, frnat.fin_src);
70373b9b51feSCy Schubert 
70383b9b51feSCy Schubert 		} else {
70393b9b51feSCy Schubert 			natl = ipf_nat_outlookup(&frnat,
70403b9b51feSCy Schubert 						 flags & ~(SI_WILDP|NAT_SEARCH),
70413b9b51feSCy Schubert 						 (u_int)frnat.fin_p,
70423b9b51feSCy Schubert 						 frnat.fin_dst, frnat.fin_src);
70433b9b51feSCy Schubert 		}
70443b9b51feSCy Schubert 		if (flags & IPN_TCPUDP) {
70453b9b51feSCy Schubert 			swap = frnat.fin_data[0];
70463b9b51feSCy Schubert 			frnat.fin_data[0] = frnat.fin_data[1];
70473b9b51feSCy Schubert 			frnat.fin_data[1] = swap;
70483b9b51feSCy Schubert 		}
70493b9b51feSCy Schubert 
70503b9b51feSCy Schubert 		/* TRACE natl, in_stepnext, l */
70513b9b51feSCy Schubert 		DT3(ipf_nat_rewrite_2, nat_t *, natl, ipnat_t *, np , int, l);
70523b9b51feSCy Schubert 
70533b9b51feSCy Schubert 		if ((natl != NULL) && (l > 8))	/* XXX 8 is arbitrary */
70548c82b374SCy Schubert 			return (-1);
70553b9b51feSCy Schubert 
70563b9b51feSCy Schubert 		np->in_stepnext &= 0x3;
70573b9b51feSCy Schubert 
70583b9b51feSCy Schubert 		l++;
70593b9b51feSCy Schubert 		changed = -1;
70603b9b51feSCy Schubert 	} while (natl != NULL);
70613b9b51feSCy Schubert 
70623b9b51feSCy Schubert 	nat->nat_osrcip = fin->fin_src;
70633b9b51feSCy Schubert 	nat->nat_odstip = fin->fin_dst;
70643b9b51feSCy Schubert 	nat->nat_nsrcip = frnat.fin_src;
70653b9b51feSCy Schubert 	nat->nat_ndstip = frnat.fin_dst;
70663b9b51feSCy Schubert 
70673b9b51feSCy Schubert 	if ((flags & IPN_TCPUDP) != 0) {
70683b9b51feSCy Schubert 		nat->nat_osport = htons(fin->fin_data[0]);
70693b9b51feSCy Schubert 		nat->nat_odport = htons(fin->fin_data[1]);
70703b9b51feSCy Schubert 		nat->nat_nsport = htons(frnat.fin_data[0]);
70713b9b51feSCy Schubert 		nat->nat_ndport = htons(frnat.fin_data[1]);
70723b9b51feSCy Schubert 	} else if ((flags & IPN_ICMPQUERY) != 0) {
70733b9b51feSCy Schubert 		nat->nat_oicmpid = fin->fin_data[1];
70743b9b51feSCy Schubert 		nat->nat_nicmpid = frnat.fin_data[1];
70753b9b51feSCy Schubert 	}
70763b9b51feSCy Schubert 
70778c82b374SCy Schubert 	return (0);
70783b9b51feSCy Schubert }
70793b9b51feSCy Schubert 
70803b9b51feSCy Schubert 
70813b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
70823b9b51feSCy Schubert /* Function:    nat_newdivert                                               */
70833b9b51feSCy Schubert /* Returns:     int - -1 == error, 0 == success                             */
70843b9b51feSCy Schubert /* Parameters:  fin(I) - pointer to packet information                      */
70853b9b51feSCy Schubert /*              nat(I) - pointer to NAT entry                               */
70863b9b51feSCy Schubert /*              ni(I)  - pointer to structure with misc. information needed */
70873b9b51feSCy Schubert /*                       to create new NAT entry.                           */
70883b9b51feSCy Schubert /* Write Lock:  ipf_nat                                                     */
70893b9b51feSCy Schubert /*                                                                          */
70903b9b51feSCy Schubert /* Create a new NAT  divert session as defined by the NAT rule.  This is    */
70913b9b51feSCy Schubert /* somewhat different to other NAT session creation routines because we     */
70923b9b51feSCy Schubert /* do not iterate through either port numbers or IP addresses, searching    */
70933b9b51feSCy Schubert /* for a unique mapping, however, a complimentary duplicate check is made.  */
70943b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
70953b9b51feSCy Schubert static int
ipf_nat_newdivert(fr_info_t * fin,nat_t * nat,natinfo_t * nai)7096064a5a95SCy Schubert ipf_nat_newdivert(fr_info_t *fin, nat_t *nat, natinfo_t *nai)
70973b9b51feSCy Schubert {
70983b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
70993b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
71003b9b51feSCy Schubert 	fr_info_t frnat;
71013b9b51feSCy Schubert 	ipnat_t *np;
71023b9b51feSCy Schubert 	nat_t *natl;
71033b9b51feSCy Schubert 	int p;
71043b9b51feSCy Schubert 
71053b9b51feSCy Schubert 	np = nai->nai_np;
71063b9b51feSCy Schubert 	bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
71073b9b51feSCy Schubert 
71083b9b51feSCy Schubert 	nat->nat_pr[0] = 0;
71093b9b51feSCy Schubert 	nat->nat_osrcaddr = fin->fin_saddr;
71103b9b51feSCy Schubert 	nat->nat_odstaddr = fin->fin_daddr;
71113b9b51feSCy Schubert 	frnat.fin_saddr = htonl(np->in_snip);
71123b9b51feSCy Schubert 	frnat.fin_daddr = htonl(np->in_dnip);
71133b9b51feSCy Schubert 	if ((nat->nat_flags & IPN_TCPUDP) != 0) {
71143b9b51feSCy Schubert 		nat->nat_osport = htons(fin->fin_data[0]);
71153b9b51feSCy Schubert 		nat->nat_odport = htons(fin->fin_data[1]);
71163b9b51feSCy Schubert 	} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
71173b9b51feSCy Schubert 		nat->nat_oicmpid = fin->fin_data[1];
71183b9b51feSCy Schubert 	}
71193b9b51feSCy Schubert 
71203b9b51feSCy Schubert 	if (np->in_redir & NAT_DIVERTUDP) {
71213b9b51feSCy Schubert 		frnat.fin_data[0] = np->in_spnext;
71223b9b51feSCy Schubert 		frnat.fin_data[1] = np->in_dpnext;
71233b9b51feSCy Schubert 		frnat.fin_flx |= FI_TCPUDP;
71243b9b51feSCy Schubert 		p = IPPROTO_UDP;
71253b9b51feSCy Schubert 	} else {
71263b9b51feSCy Schubert 		frnat.fin_flx &= ~FI_TCPUDP;
71273b9b51feSCy Schubert 		p = IPPROTO_IPIP;
71283b9b51feSCy Schubert 	}
71293b9b51feSCy Schubert 
71303b9b51feSCy Schubert 	if (fin->fin_out == 1) {
71313b9b51feSCy Schubert 		natl = ipf_nat_inlookup(&frnat, 0, p,
71323b9b51feSCy Schubert 					frnat.fin_dst, frnat.fin_src);
71333b9b51feSCy Schubert 
71343b9b51feSCy Schubert 	} else {
71353b9b51feSCy Schubert 		natl = ipf_nat_outlookup(&frnat, 0, p,
71363b9b51feSCy Schubert 					 frnat.fin_dst, frnat.fin_src);
71373b9b51feSCy Schubert 	}
71383b9b51feSCy Schubert 
71393b9b51feSCy Schubert 	if (natl != NULL) {
71403b9b51feSCy Schubert 		NBUMPSIDED(fin->fin_out, ns_divert_exist);
71413b9b51feSCy Schubert 		DT3(ns_divert_exist, fr_info_t *, fin, nat_t *, nat, natinfo_t, nai);
71428c82b374SCy Schubert 		return (-1);
71433b9b51feSCy Schubert 	}
71443b9b51feSCy Schubert 
71453b9b51feSCy Schubert 	nat->nat_nsrcaddr = frnat.fin_saddr;
71463b9b51feSCy Schubert 	nat->nat_ndstaddr = frnat.fin_daddr;
71473b9b51feSCy Schubert 	if ((nat->nat_flags & IPN_TCPUDP) != 0) {
71483b9b51feSCy Schubert 		nat->nat_nsport = htons(frnat.fin_data[0]);
71493b9b51feSCy Schubert 		nat->nat_ndport = htons(frnat.fin_data[1]);
71503b9b51feSCy Schubert 	} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
71513b9b51feSCy Schubert 		nat->nat_nicmpid = frnat.fin_data[1];
71523b9b51feSCy Schubert 	}
71533b9b51feSCy Schubert 
71543b9b51feSCy Schubert 	nat->nat_pr[fin->fin_out] = fin->fin_p;
71553b9b51feSCy Schubert 	nat->nat_pr[1 - fin->fin_out] = p;
71563b9b51feSCy Schubert 
71573b9b51feSCy Schubert 	if (np->in_redir & NAT_REDIRECT)
71583b9b51feSCy Schubert 		nat->nat_dir = NAT_DIVERTIN;
71593b9b51feSCy Schubert 	else
71603b9b51feSCy Schubert 		nat->nat_dir = NAT_DIVERTOUT;
71613b9b51feSCy Schubert 
71628c82b374SCy Schubert 	return (0);
71633b9b51feSCy Schubert }
71643b9b51feSCy Schubert 
71653b9b51feSCy Schubert 
71663b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
71673b9b51feSCy Schubert /* Function:    nat_builddivertmp                                           */
71683b9b51feSCy Schubert /* Returns:     int - -1 == error, 0 == success                             */
71693b9b51feSCy Schubert /* Parameters:  softn(I) - pointer to NAT context structure                 */
71703b9b51feSCy Schubert /*              np(I)    - pointer to a NAT rule                            */
71713b9b51feSCy Schubert /*                                                                          */
71723b9b51feSCy Schubert /* For divert rules, a skeleton packet representing what will be prepended  */
71733b9b51feSCy Schubert /* to the real packet is created.  Even though we don't have the full       */
71743b9b51feSCy Schubert /* packet here, a checksum is calculated that we update later when we       */
71753b9b51feSCy Schubert /* fill in the final details.  At present a 0 checksum for UDP is being set */
71763b9b51feSCy Schubert /* here because it is expected that divert will be used for localhost.      */
71773b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
71783b9b51feSCy Schubert static int
ipf_nat_builddivertmp(ipf_nat_softc_t * softn,ipnat_t * np)7179064a5a95SCy Schubert ipf_nat_builddivertmp(ipf_nat_softc_t *softn, ipnat_t *np)
71803b9b51feSCy Schubert {
71813b9b51feSCy Schubert 	udphdr_t *uh;
71823b9b51feSCy Schubert 	size_t len;
71833b9b51feSCy Schubert 	ip_t *ip;
71843b9b51feSCy Schubert 
71853b9b51feSCy Schubert 	if ((np->in_redir & NAT_DIVERTUDP) != 0)
71863b9b51feSCy Schubert 		len = sizeof(ip_t) + sizeof(udphdr_t);
71873b9b51feSCy Schubert 	else
71883b9b51feSCy Schubert 		len = sizeof(ip_t);
71893b9b51feSCy Schubert 
71903b9b51feSCy Schubert 	ALLOC_MB_T(np->in_divmp, len);
71913b9b51feSCy Schubert 	if (np->in_divmp == NULL) {
71923b9b51feSCy Schubert 		NBUMPD(ipf_nat_stats, ns_divert_build);
71938c82b374SCy Schubert 		return (-1);
71943b9b51feSCy Schubert 	}
71953b9b51feSCy Schubert 
71963b9b51feSCy Schubert 	/*
71973b9b51feSCy Schubert 	 * First, the header to get the packet diverted to the new destination
71983b9b51feSCy Schubert 	 */
71993b9b51feSCy Schubert 	ip = MTOD(np->in_divmp, ip_t *);
72003b9b51feSCy Schubert 	IP_V_A(ip, 4);
72013b9b51feSCy Schubert 	IP_HL_A(ip, 5);
72023b9b51feSCy Schubert 	ip->ip_tos = 0;
72033b9b51feSCy Schubert 	if ((np->in_redir & NAT_DIVERTUDP) != 0)
72043b9b51feSCy Schubert 		ip->ip_p = IPPROTO_UDP;
72053b9b51feSCy Schubert 	else
72063b9b51feSCy Schubert 		ip->ip_p = IPPROTO_IPIP;
72073b9b51feSCy Schubert 	ip->ip_ttl = 255;
72083b9b51feSCy Schubert 	ip->ip_off = 0;
72093b9b51feSCy Schubert 	ip->ip_sum = 0;
72103b9b51feSCy Schubert 	ip->ip_len = htons(len);
72113b9b51feSCy Schubert 	ip->ip_id = 0;
72123b9b51feSCy Schubert 	ip->ip_src.s_addr = htonl(np->in_snip);
72133b9b51feSCy Schubert 	ip->ip_dst.s_addr = htonl(np->in_dnip);
72143b9b51feSCy Schubert 	ip->ip_sum = ipf_cksum((u_short *)ip, sizeof(*ip));
72153b9b51feSCy Schubert 
72163b9b51feSCy Schubert 	if (np->in_redir & NAT_DIVERTUDP) {
72173b9b51feSCy Schubert 		uh = (udphdr_t *)(ip + 1);
72183b9b51feSCy Schubert 		uh->uh_sum = 0;
72193b9b51feSCy Schubert 		uh->uh_ulen = 8;
72203b9b51feSCy Schubert 		uh->uh_sport = htons(np->in_spnext);
72213b9b51feSCy Schubert 		uh->uh_dport = htons(np->in_dpnext);
72223b9b51feSCy Schubert 	}
72233b9b51feSCy Schubert 
72248c82b374SCy Schubert 	return (0);
72253b9b51feSCy Schubert }
72263b9b51feSCy Schubert 
72273b9b51feSCy Schubert 
72283b9b51feSCy Schubert #define	MINDECAP	(sizeof(ip_t) + sizeof(udphdr_t) + sizeof(ip_t))
72293b9b51feSCy Schubert 
72303b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
72313b9b51feSCy Schubert /* Function:    nat_decap                                                   */
72323b9b51feSCy Schubert /* Returns:     int - -1 == error, 0 == success                             */
72333b9b51feSCy Schubert /* Parameters:  fin(I) - pointer to packet information                      */
72343b9b51feSCy Schubert /*              nat(I) - pointer to current NAT session                     */
72353b9b51feSCy Schubert /*                                                                          */
72363b9b51feSCy Schubert /* This function is responsible for undoing a packet's encapsulation in the */
72373b9b51feSCy Schubert /* reverse of an encap/divert rule.  After removing the outer encapsulation */
72383b9b51feSCy Schubert /* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/
72393b9b51feSCy Schubert /* match the "new" packet as it may still be used by IPFilter elsewhere.    */
72403b9b51feSCy Schubert /* We use "dir" here as the basis for some of the expectations about the    */
72413b9b51feSCy Schubert /* outer header.  If we return an error, the goal is to leave the original  */
72423b9b51feSCy Schubert /* packet information undisturbed - this falls short at the end where we'd  */
72433b9b51feSCy Schubert /* need to back a backup copy of "fin" - expensive.                         */
72443b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
72453b9b51feSCy Schubert static int
ipf_nat_decap(fr_info_t * fin,nat_t * nat)7246064a5a95SCy Schubert ipf_nat_decap(fr_info_t *fin, nat_t *nat)
72473b9b51feSCy Schubert {
72483b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
72493b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
72503b9b51feSCy Schubert 	char *hdr;
72513b9b51feSCy Schubert 	int hlen;
72523b9b51feSCy Schubert 	int skip;
72533b9b51feSCy Schubert 	mb_t *m;
72543b9b51feSCy Schubert 
72553b9b51feSCy Schubert 	if ((fin->fin_flx & FI_ICMPERR) != 0) {
72563b9b51feSCy Schubert 		/*
72573b9b51feSCy Schubert 		 * ICMP packets don't get decapsulated, instead what we need
72583b9b51feSCy Schubert 		 * to do is change the ICMP reply from including (in the data
72593b9b51feSCy Schubert 		 * portion for errors) the encapsulated packet that we sent
72603b9b51feSCy Schubert 		 * out to something that resembles the original packet prior
72613b9b51feSCy Schubert 		 * to encapsulation.  This isn't done here - all we're doing
72623b9b51feSCy Schubert 		 * here is changing the outer address to ensure that it gets
72633b9b51feSCy Schubert 		 * targetted back to the correct system.
72643b9b51feSCy Schubert 		 */
72653b9b51feSCy Schubert 
72663b9b51feSCy Schubert 		if (nat->nat_dir & NAT_OUTBOUND) {
72673b9b51feSCy Schubert 			u_32_t sum1, sum2, sumd;
72683b9b51feSCy Schubert 
72693b9b51feSCy Schubert 			sum1 = ntohl(fin->fin_daddr);
72703b9b51feSCy Schubert 			sum2 = ntohl(nat->nat_osrcaddr);
72713b9b51feSCy Schubert 			CALC_SUMD(sum1, sum2, sumd);
72723b9b51feSCy Schubert 			fin->fin_ip->ip_dst = nat->nat_osrcip;
72733b9b51feSCy Schubert 			fin->fin_daddr = nat->nat_osrcaddr;
72743b9b51feSCy Schubert #if !defined(_KERNEL) || SOLARIS
72753b9b51feSCy Schubert 			ipf_fix_outcksum(0, &fin->fin_ip->ip_sum, sumd, 0);
72763b9b51feSCy Schubert #endif
72773b9b51feSCy Schubert 		}
72788c82b374SCy Schubert 		return (0);
72793b9b51feSCy Schubert 	}
72803b9b51feSCy Schubert 
72813b9b51feSCy Schubert 	m = fin->fin_m;
72823b9b51feSCy Schubert 	skip = fin->fin_hlen;
72833b9b51feSCy Schubert 
72843b9b51feSCy Schubert 	switch (nat->nat_dir)
72853b9b51feSCy Schubert 	{
72863b9b51feSCy Schubert 	case NAT_DIVERTIN :
72873b9b51feSCy Schubert 	case NAT_DIVERTOUT :
72883b9b51feSCy Schubert 		if (fin->fin_plen < MINDECAP)
72898c82b374SCy Schubert 			return (-1);
72903b9b51feSCy Schubert 		skip += sizeof(udphdr_t);
72913b9b51feSCy Schubert 		break;
72923b9b51feSCy Schubert 
72933b9b51feSCy Schubert 	case NAT_ENCAPIN :
72943b9b51feSCy Schubert 	case NAT_ENCAPOUT :
72953b9b51feSCy Schubert 		if (fin->fin_plen < (skip + sizeof(ip_t)))
72968c82b374SCy Schubert 			return (-1);
72973b9b51feSCy Schubert 		break;
72983b9b51feSCy Schubert 	default :
72998c82b374SCy Schubert 		return (-1);
73003b9b51feSCy Schubert 		/* NOTREACHED */
73013b9b51feSCy Schubert 	}
73023b9b51feSCy Schubert 
73033b9b51feSCy Schubert 	/*
73043b9b51feSCy Schubert 	 * The aim here is to keep the original packet details in "fin" for
73053b9b51feSCy Schubert 	 * as long as possible so that returning with an error is for the
73063b9b51feSCy Schubert 	 * original packet and there is little undoing work to do.
73073b9b51feSCy Schubert 	 */
73083b9b51feSCy Schubert 	if (M_LEN(m) < skip + sizeof(ip_t)) {
73093b9b51feSCy Schubert 		if (ipf_pr_pullup(fin, skip + sizeof(ip_t)) == -1)
73108c82b374SCy Schubert 			return (-1);
73113b9b51feSCy Schubert 	}
73123b9b51feSCy Schubert 
73133b9b51feSCy Schubert 	hdr = MTOD(fin->fin_m, char *);
73143b9b51feSCy Schubert 	fin->fin_ip = (ip_t *)(hdr + skip);
73153b9b51feSCy Schubert 	hlen = IP_HL(fin->fin_ip) << 2;
73163b9b51feSCy Schubert 
73173b9b51feSCy Schubert 	if (ipf_pr_pullup(fin, skip + hlen) == -1) {
73183b9b51feSCy Schubert 		NBUMPSIDED(fin->fin_out, ns_decap_pullup);
73198c82b374SCy Schubert 		return (-1);
73203b9b51feSCy Schubert 	}
73213b9b51feSCy Schubert 
73223b9b51feSCy Schubert 	fin->fin_hlen = hlen;
73233b9b51feSCy Schubert 	fin->fin_dlen -= skip;
73243b9b51feSCy Schubert 	fin->fin_plen -= skip;
73253b9b51feSCy Schubert 	fin->fin_ipoff += skip;
73263b9b51feSCy Schubert 
73273b9b51feSCy Schubert 	if (ipf_makefrip(hlen, (ip_t *)hdr, fin) == -1) {
73283b9b51feSCy Schubert 		NBUMPSIDED(fin->fin_out, ns_decap_bad);
73298c82b374SCy Schubert 		return (-1);
73303b9b51feSCy Schubert 	}
73313b9b51feSCy Schubert 
73328c82b374SCy Schubert 	return (skip);
73333b9b51feSCy Schubert }
73343b9b51feSCy Schubert 
73353b9b51feSCy Schubert 
73363b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
73373b9b51feSCy Schubert /* Function:    nat_nextaddr                                                */
73383b9b51feSCy Schubert /* Returns:     int - -1 == bad input (no new address),                     */
73393b9b51feSCy Schubert /*                     0 == success and dst has new address                 */
73403b9b51feSCy Schubert /* Parameters:  fin(I) - pointer to packet information                      */
73413b9b51feSCy Schubert /*              na(I)  - how to generate new address                        */
73423b9b51feSCy Schubert /*              old(I) - original address being replaced                    */
73433b9b51feSCy Schubert /*              dst(O) - where to put the new address                       */
73443b9b51feSCy Schubert /* Write Lock:  ipf_nat                                                     */
73453b9b51feSCy Schubert /*                                                                          */
73463b9b51feSCy Schubert /* This function uses the contents of the "na" structure, in combination    */
73473b9b51feSCy Schubert /* with "old" to produce a new address to store in "dst".  Not all of the   */
73483b9b51feSCy Schubert /* possible uses of "na" will result in a new address.                      */
73493b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
73503b9b51feSCy Schubert static int
ipf_nat_nextaddr(fr_info_t * fin,nat_addr_t * na,u_32_t * old,u_32_t * dst)7351064a5a95SCy Schubert ipf_nat_nextaddr(fr_info_t *fin, nat_addr_t *na, u_32_t *old, u_32_t *dst)
73523b9b51feSCy Schubert {
73533b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
73543b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
73553b9b51feSCy Schubert 	u_32_t amin, amax, new;
73563b9b51feSCy Schubert 	i6addr_t newip;
73573b9b51feSCy Schubert 	int error;
73583b9b51feSCy Schubert 
73593b9b51feSCy Schubert 	new = 0;
73603b9b51feSCy Schubert 	amin = na->na_addr[0].in4.s_addr;
73613b9b51feSCy Schubert 
73623b9b51feSCy Schubert 	switch (na->na_atype)
73633b9b51feSCy Schubert 	{
73643b9b51feSCy Schubert 	case FRI_RANGE :
73653b9b51feSCy Schubert 		amax = na->na_addr[1].in4.s_addr;
73663b9b51feSCy Schubert 		break;
73673b9b51feSCy Schubert 
73683b9b51feSCy Schubert 	case FRI_NETMASKED :
73693b9b51feSCy Schubert 	case FRI_DYNAMIC :
73703b9b51feSCy Schubert 	case FRI_NORMAL :
73713b9b51feSCy Schubert 		/*
73723b9b51feSCy Schubert 		 * Compute the maximum address by adding the inverse of the
73733b9b51feSCy Schubert 		 * netmask to the minimum address.
73743b9b51feSCy Schubert 		 */
73753b9b51feSCy Schubert 		amax = ~na->na_addr[1].in4.s_addr;
73763b9b51feSCy Schubert 		amax |= amin;
73773b9b51feSCy Schubert 		break;
73783b9b51feSCy Schubert 
73793b9b51feSCy Schubert 	case FRI_LOOKUP :
73803b9b51feSCy Schubert 		break;
73813b9b51feSCy Schubert 
73823b9b51feSCy Schubert 	case FRI_BROADCAST :
73833b9b51feSCy Schubert 	case FRI_PEERADDR :
73843b9b51feSCy Schubert 	case FRI_NETWORK :
73853b9b51feSCy Schubert 	default :
73863b9b51feSCy Schubert 		DT4(ns_na_atype, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new);
73878c82b374SCy Schubert 		return (-1);
73883b9b51feSCy Schubert 	}
73893b9b51feSCy Schubert 
73903b9b51feSCy Schubert 	error = -1;
73913b9b51feSCy Schubert 
73923b9b51feSCy Schubert 	if (na->na_atype == FRI_LOOKUP) {
73933b9b51feSCy Schubert 		if (na->na_type == IPLT_DSTLIST) {
73943b9b51feSCy Schubert 			error = ipf_dstlist_select_node(fin, na->na_ptr, dst,
73953b9b51feSCy Schubert 							NULL);
73963b9b51feSCy Schubert 		} else {
73973b9b51feSCy Schubert 			NBUMPSIDE(fin->fin_out, ns_badnextaddr);
73983b9b51feSCy Schubert 			DT4(ns_badnextaddr_1, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new);
73993b9b51feSCy Schubert 		}
74003b9b51feSCy Schubert 
74013b9b51feSCy Schubert 	} else if (na->na_atype == IPLT_NONE) {
74023b9b51feSCy Schubert 		/*
74033b9b51feSCy Schubert 		 * 0/0 as the new address means leave it alone.
74043b9b51feSCy Schubert 		 */
74053b9b51feSCy Schubert 		if (na->na_addr[0].in4.s_addr == 0 &&
74063b9b51feSCy Schubert 		    na->na_addr[1].in4.s_addr == 0) {
74073b9b51feSCy Schubert 			new = *old;
74083b9b51feSCy Schubert 
74093b9b51feSCy Schubert 		/*
74103b9b51feSCy Schubert 		 * 0/32 means get the interface's address
74113b9b51feSCy Schubert 		 */
74123b9b51feSCy Schubert 		} else if (na->na_addr[0].in4.s_addr == 0 &&
74133b9b51feSCy Schubert 			   na->na_addr[1].in4.s_addr == 0xffffffff) {
74143b9b51feSCy Schubert 			if (ipf_ifpaddr(softc, 4, na->na_atype,
74153b9b51feSCy Schubert 					fin->fin_ifp, &newip, NULL) == -1) {
74163b9b51feSCy Schubert 				NBUMPSIDED(fin->fin_out, ns_ifpaddrfail);
74173b9b51feSCy Schubert 				DT4(ns_ifpaddrfail, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new);
74188c82b374SCy Schubert 				return (-1);
74193b9b51feSCy Schubert 			}
74203b9b51feSCy Schubert 			new = newip.in4.s_addr;
74213b9b51feSCy Schubert 		} else {
74223b9b51feSCy Schubert 			new = htonl(na->na_nextip);
74233b9b51feSCy Schubert 		}
74243b9b51feSCy Schubert 		*dst = new;
74253b9b51feSCy Schubert 		error = 0;
74263b9b51feSCy Schubert 
74273b9b51feSCy Schubert 	} else {
74283b9b51feSCy Schubert 		NBUMPSIDE(fin->fin_out, ns_badnextaddr);
74293b9b51feSCy Schubert 		DT4(ns_badnextaddr_2, fr_info_t *, fin, nat_addr_t *, na, u_32_t *, old, u_32_t *, new);
74303b9b51feSCy Schubert 	}
74313b9b51feSCy Schubert 
74328c82b374SCy Schubert 	return (error);
74333b9b51feSCy Schubert }
74343b9b51feSCy Schubert 
74353b9b51feSCy Schubert 
74363b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
74373b9b51feSCy Schubert /* Function:    nat_nextaddrinit                                            */
74383b9b51feSCy Schubert /* Returns:     int - 0 == success, else error number                       */
74393b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
74403b9b51feSCy Schubert /*              na(I)      - NAT address information for generating new addr*/
74413b9b51feSCy Schubert /*              initial(I) - flag indicating if it is the first call for    */
74423b9b51feSCy Schubert /*                           this "na" structure.                           */
74433b9b51feSCy Schubert /*              ifp(I)     - network interface to derive address            */
74443b9b51feSCy Schubert /*                           information from.                              */
74453b9b51feSCy Schubert /*                                                                          */
74463b9b51feSCy Schubert /* This function is expected to be called in two scenarious: when a new NAT */
74473b9b51feSCy Schubert /* rule is loaded into the kernel and when the list of NAT rules is sync'd  */
74483b9b51feSCy Schubert /* up with the valid network interfaces (possibly due to them changing.)    */
74493b9b51feSCy Schubert /* To distinguish between these, the "initial" parameter is used.  If it is */
74503b9b51feSCy Schubert /* 1 then this indicates the rule has just been reloaded and 0 for when we  */
74513b9b51feSCy Schubert /* are updating information.  This difference is important because in       */
74523b9b51feSCy Schubert /* instances where we are not updating address information associated with  */
74533b9b51feSCy Schubert /* a network interface, we don't want to disturb what the "next" address to */
74543b9b51feSCy Schubert /* come out of ipf_nat_nextaddr() will be.                                  */
74553b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
74563b9b51feSCy Schubert static int
ipf_nat_nextaddrinit(ipf_main_softc_t * softc,char * base,nat_addr_t * na,int initial,void * ifp)7457064a5a95SCy Schubert ipf_nat_nextaddrinit(ipf_main_softc_t *softc, char *base, nat_addr_t *na,
7458064a5a95SCy Schubert 	int initial, void *ifp)
74593b9b51feSCy Schubert {
74603b9b51feSCy Schubert 
74613b9b51feSCy Schubert 	switch (na->na_atype)
74623b9b51feSCy Schubert 	{
74633b9b51feSCy Schubert 	case FRI_LOOKUP :
74643b9b51feSCy Schubert 		if (na->na_subtype == 0) {
74653b9b51feSCy Schubert 			na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT,
74663b9b51feSCy Schubert 							na->na_type,
74673b9b51feSCy Schubert 							na->na_num,
74683b9b51feSCy Schubert 							&na->na_func);
74693b9b51feSCy Schubert 		} else if (na->na_subtype == 1) {
74703b9b51feSCy Schubert 			na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT,
74713b9b51feSCy Schubert 							 na->na_type,
74723b9b51feSCy Schubert 							 base + na->na_num,
74733b9b51feSCy Schubert 							 &na->na_func);
74743b9b51feSCy Schubert 		}
74753b9b51feSCy Schubert 		if (na->na_func == NULL) {
74763b9b51feSCy Schubert 			IPFERROR(60060);
74778c82b374SCy Schubert 			return (ESRCH);
74783b9b51feSCy Schubert 		}
74793b9b51feSCy Schubert 		if (na->na_ptr == NULL) {
74803b9b51feSCy Schubert 			IPFERROR(60056);
74818c82b374SCy Schubert 			return (ESRCH);
74823b9b51feSCy Schubert 		}
74833b9b51feSCy Schubert 		break;
74843b9b51feSCy Schubert 
74853b9b51feSCy Schubert 	case FRI_DYNAMIC :
74863b9b51feSCy Schubert 	case FRI_BROADCAST :
74873b9b51feSCy Schubert 	case FRI_NETWORK :
74883b9b51feSCy Schubert 	case FRI_NETMASKED :
74893b9b51feSCy Schubert 	case FRI_PEERADDR :
74903b9b51feSCy Schubert 		if (ifp != NULL)
74913b9b51feSCy Schubert 			(void )ipf_ifpaddr(softc, 4, na->na_atype, ifp,
74923b9b51feSCy Schubert 					   &na->na_addr[0], &na->na_addr[1]);
74933b9b51feSCy Schubert 		break;
74943b9b51feSCy Schubert 
74953b9b51feSCy Schubert 	case FRI_SPLIT :
74963b9b51feSCy Schubert 	case FRI_RANGE :
74973b9b51feSCy Schubert 		if (initial)
74983b9b51feSCy Schubert 			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr);
74993b9b51feSCy Schubert 		break;
75003b9b51feSCy Schubert 
75013b9b51feSCy Schubert 	case FRI_NONE :
75023b9b51feSCy Schubert 		na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr;
75038c82b374SCy Schubert 		return (0);
75043b9b51feSCy Schubert 
75053b9b51feSCy Schubert 	case FRI_NORMAL :
75063b9b51feSCy Schubert 		na->na_addr[0].in4.s_addr &= na->na_addr[1].in4.s_addr;
75073b9b51feSCy Schubert 		break;
75083b9b51feSCy Schubert 
75093b9b51feSCy Schubert 	default :
75103b9b51feSCy Schubert 		IPFERROR(60054);
75118c82b374SCy Schubert 		return (EINVAL);
75123b9b51feSCy Schubert 	}
75133b9b51feSCy Schubert 
75143b9b51feSCy Schubert 	if (initial && (na->na_atype == FRI_NORMAL)) {
75153b9b51feSCy Schubert 		if (na->na_addr[0].in4.s_addr == 0) {
75163b9b51feSCy Schubert 			if ((na->na_addr[1].in4.s_addr == 0xffffffff) ||
75173b9b51feSCy Schubert 			    (na->na_addr[1].in4.s_addr == 0)) {
75188c82b374SCy Schubert 				return (0);
75193b9b51feSCy Schubert 			}
75203b9b51feSCy Schubert 		}
75213b9b51feSCy Schubert 
75223b9b51feSCy Schubert 		if (na->na_addr[1].in4.s_addr == 0xffffffff) {
75233b9b51feSCy Schubert 			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr);
75243b9b51feSCy Schubert 		} else {
75253b9b51feSCy Schubert 			na->na_nextip = ntohl(na->na_addr[0].in4.s_addr) + 1;
75263b9b51feSCy Schubert 		}
75273b9b51feSCy Schubert 	}
75283b9b51feSCy Schubert 
75298c82b374SCy Schubert 	return (0);
75303b9b51feSCy Schubert }
75313b9b51feSCy Schubert 
75323b9b51feSCy Schubert 
75333b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
75343b9b51feSCy Schubert /* Function:    ipf_nat_matchflush                                          */
75353b9b51feSCy Schubert /* Returns:     int - -1 == error, 0 == success                             */
75363b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
75373b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
75383b9b51feSCy Schubert /*              nat(I)   - pointer to current NAT session                   */
75393b9b51feSCy Schubert /*                                                                          */
75403b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
75413b9b51feSCy Schubert static int
ipf_nat_matchflush(ipf_main_softc_t * softc,ipf_nat_softc_t * softn,caddr_t data)7542064a5a95SCy Schubert ipf_nat_matchflush(ipf_main_softc_t *softc, ipf_nat_softc_t *softn,
7543064a5a95SCy Schubert 	caddr_t data)
75443b9b51feSCy Schubert {
75453b9b51feSCy Schubert 	int *array, flushed, error;
75463b9b51feSCy Schubert 	nat_t *nat, *natnext;
75473b9b51feSCy Schubert 	ipfobj_t obj;
75483b9b51feSCy Schubert 
75493b9b51feSCy Schubert 	error = ipf_matcharray_load(softc, data, &obj, &array);
75503b9b51feSCy Schubert 	if (error != 0)
75518c82b374SCy Schubert 		return (error);
75523b9b51feSCy Schubert 
75533b9b51feSCy Schubert 	flushed = 0;
75543b9b51feSCy Schubert 
75553b9b51feSCy Schubert 	for (nat = softn->ipf_nat_instances; nat != NULL; nat = natnext) {
75563b9b51feSCy Schubert 		natnext = nat->nat_next;
75573b9b51feSCy Schubert 		if (ipf_nat_matcharray(nat, array, softc->ipf_ticks) == 0) {
75583b9b51feSCy Schubert 			ipf_nat_delete(softc, nat, NL_FLUSH);
75593b9b51feSCy Schubert 			flushed++;
75603b9b51feSCy Schubert 		}
75613b9b51feSCy Schubert 	}
75623b9b51feSCy Schubert 
75633b9b51feSCy Schubert 	obj.ipfo_retval = flushed;
75643b9b51feSCy Schubert 	error = BCOPYOUT(&obj, data, sizeof(obj));
75653b9b51feSCy Schubert 
75663b9b51feSCy Schubert 	KFREES(array, array[0] * sizeof(*array));
75673b9b51feSCy Schubert 
75688c82b374SCy Schubert 	return (error);
75693b9b51feSCy Schubert }
75703b9b51feSCy Schubert 
75713b9b51feSCy Schubert 
75723b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
75733b9b51feSCy Schubert /* Function:    ipf_nat_matcharray                                          */
75743b9b51feSCy Schubert /* Returns:     int - -1 == error, 0 == success                             */
75753b9b51feSCy Schubert /* Parameters:  fin(I) - pointer to packet information                      */
75763b9b51feSCy Schubert /*              nat(I) - pointer to current NAT session                     */
75773b9b51feSCy Schubert /*                                                                          */
75783b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
75793b9b51feSCy Schubert static int
ipf_nat_matcharray(nat_t * nat,int * array,u_long ticks)7580064a5a95SCy Schubert ipf_nat_matcharray(nat_t *nat, int *array, u_long ticks)
75813b9b51feSCy Schubert {
75823b9b51feSCy Schubert 	int i, n, *x, e, p;
75833b9b51feSCy Schubert 
75843b9b51feSCy Schubert 	e = 0;
75853b9b51feSCy Schubert 	n = array[0];
75863b9b51feSCy Schubert 	x = array + 1;
75873b9b51feSCy Schubert 
75883b9b51feSCy Schubert 	for (; n > 0; x += 3 + x[2]) {
75893b9b51feSCy Schubert 		if (x[0] == IPF_EXP_END)
75903b9b51feSCy Schubert 			break;
75913b9b51feSCy Schubert 		e = 0;
75923b9b51feSCy Schubert 
75933b9b51feSCy Schubert 		n -= x[2] + 3;
75943b9b51feSCy Schubert 		if (n < 0)
75953b9b51feSCy Schubert 			break;
75963b9b51feSCy Schubert 
75973b9b51feSCy Schubert 		p = x[0] >> 16;
75983b9b51feSCy Schubert 		if (p != 0 && p != nat->nat_pr[1])
75993b9b51feSCy Schubert 			break;
76003b9b51feSCy Schubert 
76013b9b51feSCy Schubert 		switch (x[0])
76023b9b51feSCy Schubert 		{
76033b9b51feSCy Schubert 		case IPF_EXP_IP_PR :
76043b9b51feSCy Schubert 			for (i = 0; !e && i < x[2]; i++) {
76053b9b51feSCy Schubert 				e |= (nat->nat_pr[1] == x[i + 3]);
76063b9b51feSCy Schubert 			}
76073b9b51feSCy Schubert 			break;
76083b9b51feSCy Schubert 
76093b9b51feSCy Schubert 		case IPF_EXP_IP_SRCADDR :
76103b9b51feSCy Schubert 			if (nat->nat_v[0] == 4) {
76113b9b51feSCy Schubert 				for (i = 0; !e && i < x[2]; i++) {
76123b9b51feSCy Schubert 					e |= ((nat->nat_osrcaddr & x[i + 4]) ==
76133b9b51feSCy Schubert 					      x[i + 3]);
76143b9b51feSCy Schubert 				}
76153b9b51feSCy Schubert 			}
76163b9b51feSCy Schubert 			if (nat->nat_v[1] == 4) {
76173b9b51feSCy Schubert 				for (i = 0; !e && i < x[2]; i++) {
76183b9b51feSCy Schubert 					e |= ((nat->nat_nsrcaddr & x[i + 4]) ==
76193b9b51feSCy Schubert 					      x[i + 3]);
76203b9b51feSCy Schubert 				}
76213b9b51feSCy Schubert 			}
76223b9b51feSCy Schubert 			break;
76233b9b51feSCy Schubert 
76243b9b51feSCy Schubert 		case IPF_EXP_IP_DSTADDR :
76253b9b51feSCy Schubert 			if (nat->nat_v[0] == 4) {
76263b9b51feSCy Schubert 				for (i = 0; !e && i < x[2]; i++) {
76273b9b51feSCy Schubert 					e |= ((nat->nat_odstaddr & x[i + 4]) ==
76283b9b51feSCy Schubert 					      x[i + 3]);
76293b9b51feSCy Schubert 				}
76303b9b51feSCy Schubert 			}
76313b9b51feSCy Schubert 			if (nat->nat_v[1] == 4) {
76323b9b51feSCy Schubert 				for (i = 0; !e && i < x[2]; i++) {
76333b9b51feSCy Schubert 					e |= ((nat->nat_ndstaddr & x[i + 4]) ==
76343b9b51feSCy Schubert 					      x[i + 3]);
76353b9b51feSCy Schubert 				}
76363b9b51feSCy Schubert 			}
76373b9b51feSCy Schubert 			break;
76383b9b51feSCy Schubert 
76393b9b51feSCy Schubert 		case IPF_EXP_IP_ADDR :
76403b9b51feSCy Schubert 			for (i = 0; !e && i < x[2]; i++) {
76413b9b51feSCy Schubert 				if (nat->nat_v[0] == 4) {
76423b9b51feSCy Schubert 					e |= ((nat->nat_osrcaddr & x[i + 4]) ==
76433b9b51feSCy Schubert 					      x[i + 3]);
76443b9b51feSCy Schubert 				}
76453b9b51feSCy Schubert 				if (nat->nat_v[1] == 4) {
76463b9b51feSCy Schubert 					e |= ((nat->nat_nsrcaddr & x[i + 4]) ==
76473b9b51feSCy Schubert 					      x[i + 3]);
76483b9b51feSCy Schubert 				}
76493b9b51feSCy Schubert 				if (nat->nat_v[0] == 4) {
76503b9b51feSCy Schubert 					e |= ((nat->nat_odstaddr & x[i + 4]) ==
76513b9b51feSCy Schubert 					      x[i + 3]);
76523b9b51feSCy Schubert 				}
76533b9b51feSCy Schubert 				if (nat->nat_v[1] == 4) {
76543b9b51feSCy Schubert 					e |= ((nat->nat_ndstaddr & x[i + 4]) ==
76553b9b51feSCy Schubert 					      x[i + 3]);
76563b9b51feSCy Schubert 				}
76573b9b51feSCy Schubert 			}
76583b9b51feSCy Schubert 			break;
76593b9b51feSCy Schubert 
76603b9b51feSCy Schubert #ifdef USE_INET6
76613b9b51feSCy Schubert 		case IPF_EXP_IP6_SRCADDR :
76623b9b51feSCy Schubert 			if (nat->nat_v[0] == 6) {
76633b9b51feSCy Schubert 				for (i = 0; !e && i < x[3]; i++) {
76643b9b51feSCy Schubert 					e |= IP6_MASKEQ(&nat->nat_osrc6,
76653b9b51feSCy Schubert 							x + i + 7, x + i + 3);
76663b9b51feSCy Schubert 				}
76673b9b51feSCy Schubert 			}
76683b9b51feSCy Schubert 			if (nat->nat_v[1] == 6) {
76693b9b51feSCy Schubert 				for (i = 0; !e && i < x[3]; i++) {
76703b9b51feSCy Schubert 					e |= IP6_MASKEQ(&nat->nat_nsrc6,
76713b9b51feSCy Schubert 							x + i + 7, x + i + 3);
76723b9b51feSCy Schubert 				}
76733b9b51feSCy Schubert 			}
76743b9b51feSCy Schubert 			break;
76753b9b51feSCy Schubert 
76763b9b51feSCy Schubert 		case IPF_EXP_IP6_DSTADDR :
76773b9b51feSCy Schubert 			if (nat->nat_v[0] == 6) {
76783b9b51feSCy Schubert 				for (i = 0; !e && i < x[3]; i++) {
76793b9b51feSCy Schubert 					e |= IP6_MASKEQ(&nat->nat_odst6,
76803b9b51feSCy Schubert 							x + i + 7,
76813b9b51feSCy Schubert 							x + i + 3);
76823b9b51feSCy Schubert 				}
76833b9b51feSCy Schubert 			}
76843b9b51feSCy Schubert 			if (nat->nat_v[1] == 6) {
76853b9b51feSCy Schubert 				for (i = 0; !e && i < x[3]; i++) {
76863b9b51feSCy Schubert 					e |= IP6_MASKEQ(&nat->nat_ndst6,
76873b9b51feSCy Schubert 							x + i + 7,
76883b9b51feSCy Schubert 							x + i + 3);
76893b9b51feSCy Schubert 				}
76903b9b51feSCy Schubert 			}
76913b9b51feSCy Schubert 			break;
76923b9b51feSCy Schubert 
76933b9b51feSCy Schubert 		case IPF_EXP_IP6_ADDR :
76943b9b51feSCy Schubert 			for (i = 0; !e && i < x[3]; i++) {
76953b9b51feSCy Schubert 				if (nat->nat_v[0] == 6) {
76963b9b51feSCy Schubert 					e |= IP6_MASKEQ(&nat->nat_osrc6,
76973b9b51feSCy Schubert 							x + i + 7,
76983b9b51feSCy Schubert 							x + i + 3);
76993b9b51feSCy Schubert 				}
77003b9b51feSCy Schubert 				if (nat->nat_v[0] == 6) {
77013b9b51feSCy Schubert 					e |= IP6_MASKEQ(&nat->nat_odst6,
77023b9b51feSCy Schubert 							x + i + 7,
77033b9b51feSCy Schubert 							x + i + 3);
77043b9b51feSCy Schubert 				}
77053b9b51feSCy Schubert 				if (nat->nat_v[1] == 6) {
77063b9b51feSCy Schubert 					e |= IP6_MASKEQ(&nat->nat_nsrc6,
77073b9b51feSCy Schubert 							x + i + 7,
77083b9b51feSCy Schubert 							x + i + 3);
77093b9b51feSCy Schubert 				}
77103b9b51feSCy Schubert 				if (nat->nat_v[1] == 6) {
77113b9b51feSCy Schubert 					e |= IP6_MASKEQ(&nat->nat_ndst6,
77123b9b51feSCy Schubert 							x + i + 7,
77133b9b51feSCy Schubert 							x + i + 3);
77143b9b51feSCy Schubert 				}
77153b9b51feSCy Schubert 			}
77163b9b51feSCy Schubert 			break;
77173b9b51feSCy Schubert #endif
77183b9b51feSCy Schubert 
77193b9b51feSCy Schubert 		case IPF_EXP_UDP_PORT :
77203b9b51feSCy Schubert 		case IPF_EXP_TCP_PORT :
77213b9b51feSCy Schubert 			for (i = 0; !e && i < x[2]; i++) {
77223b9b51feSCy Schubert 				e |= (nat->nat_nsport == x[i + 3]) ||
77233b9b51feSCy Schubert 				     (nat->nat_ndport == x[i + 3]);
77243b9b51feSCy Schubert 			}
77253b9b51feSCy Schubert 			break;
77263b9b51feSCy Schubert 
77273b9b51feSCy Schubert 		case IPF_EXP_UDP_SPORT :
77283b9b51feSCy Schubert 		case IPF_EXP_TCP_SPORT :
77293b9b51feSCy Schubert 			for (i = 0; !e && i < x[2]; i++) {
77303b9b51feSCy Schubert 				e |= (nat->nat_nsport == x[i + 3]);
77313b9b51feSCy Schubert 			}
77323b9b51feSCy Schubert 			break;
77333b9b51feSCy Schubert 
77343b9b51feSCy Schubert 		case IPF_EXP_UDP_DPORT :
77353b9b51feSCy Schubert 		case IPF_EXP_TCP_DPORT :
77363b9b51feSCy Schubert 			for (i = 0; !e && i < x[2]; i++) {
77373b9b51feSCy Schubert 				e |= (nat->nat_ndport == x[i + 3]);
77383b9b51feSCy Schubert 			}
77393b9b51feSCy Schubert 			break;
77403b9b51feSCy Schubert 
77413b9b51feSCy Schubert 		case IPF_EXP_TCP_STATE :
77423b9b51feSCy Schubert 			for (i = 0; !e && i < x[2]; i++) {
77433b9b51feSCy Schubert 				e |= (nat->nat_tcpstate[0] == x[i + 3]) ||
77443b9b51feSCy Schubert 				     (nat->nat_tcpstate[1] == x[i + 3]);
77453b9b51feSCy Schubert 			}
77463b9b51feSCy Schubert 			break;
77473b9b51feSCy Schubert 
77483b9b51feSCy Schubert 		case IPF_EXP_IDLE_GT :
77493b9b51feSCy Schubert 			e |= (ticks - nat->nat_touched > x[3]);
77503b9b51feSCy Schubert 			break;
77513b9b51feSCy Schubert 		}
77523b9b51feSCy Schubert 		e ^= x[1];
77533b9b51feSCy Schubert 
77543b9b51feSCy Schubert 		if (!e)
77553b9b51feSCy Schubert 			break;
77563b9b51feSCy Schubert 	}
77573b9b51feSCy Schubert 
77588c82b374SCy Schubert 	return (e);
77593b9b51feSCy Schubert }
77603b9b51feSCy Schubert 
77613b9b51feSCy Schubert 
77623b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
77633b9b51feSCy Schubert /* Function:    ipf_nat_gettable                                            */
77643b9b51feSCy Schubert /* Returns:     int     - 0 = success, else error                           */
77653b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
77663b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
77673b9b51feSCy Schubert /*              data(I)  - pointer to ioctl data                            */
77683b9b51feSCy Schubert /*                                                                          */
77693b9b51feSCy Schubert /* This function handles ioctl requests for tables of nat information.      */
77703b9b51feSCy Schubert /* At present the only table it deals with is the hash bucket statistics.   */
77713b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
77723b9b51feSCy Schubert static int
ipf_nat_gettable(ipf_main_softc_t * softc,ipf_nat_softc_t * softn,char * data)7773064a5a95SCy Schubert ipf_nat_gettable(ipf_main_softc_t *softc, ipf_nat_softc_t *softn, char *data)
77743b9b51feSCy Schubert {
77753b9b51feSCy Schubert 	ipftable_t table;
77763b9b51feSCy Schubert 	int error;
77773b9b51feSCy Schubert 
77783b9b51feSCy Schubert 	error = ipf_inobj(softc, data, NULL, &table, IPFOBJ_GTABLE);
77793b9b51feSCy Schubert 	if (error != 0)
77808c82b374SCy Schubert 		return (error);
77813b9b51feSCy Schubert 
77823b9b51feSCy Schubert 	switch (table.ita_type)
77833b9b51feSCy Schubert 	{
77843b9b51feSCy Schubert 	case IPFTABLE_BUCKETS_NATIN :
77853b9b51feSCy Schubert 		error = COPYOUT(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
77863b9b51feSCy Schubert 				table.ita_table,
77873b9b51feSCy Schubert 				softn->ipf_nat_table_sz * sizeof(u_int));
77883b9b51feSCy Schubert 		break;
77893b9b51feSCy Schubert 
77903b9b51feSCy Schubert 	case IPFTABLE_BUCKETS_NATOUT :
77913b9b51feSCy Schubert 		error = COPYOUT(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
77923b9b51feSCy Schubert 				table.ita_table,
77933b9b51feSCy Schubert 				softn->ipf_nat_table_sz * sizeof(u_int));
77943b9b51feSCy Schubert 		break;
77953b9b51feSCy Schubert 
77963b9b51feSCy Schubert 	default :
77973b9b51feSCy Schubert 		IPFERROR(60058);
77988c82b374SCy Schubert 		return (EINVAL);
77993b9b51feSCy Schubert 	}
78003b9b51feSCy Schubert 
78013b9b51feSCy Schubert 	if (error != 0) {
78023b9b51feSCy Schubert 		IPFERROR(60059);
78033b9b51feSCy Schubert 		error = EFAULT;
78043b9b51feSCy Schubert 	}
78058c82b374SCy Schubert 	return (error);
78063b9b51feSCy Schubert }
78073b9b51feSCy Schubert 
78083b9b51feSCy Schubert 
78093b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
78103b9b51feSCy Schubert /* Function:    ipf_nat_settimeout                                          */
78113b9b51feSCy Schubert /* Returns:     int  - 0 = success, else failure			    */
78123b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
78133b9b51feSCy Schubert /*              t(I) - pointer to tunable                                   */
78143b9b51feSCy Schubert /*              p(I) - pointer to new tuning data                           */
78153b9b51feSCy Schubert /*                                                                          */
78163b9b51feSCy Schubert /* Apply the timeout change to the NAT timeout queues.                      */
78173b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
78183b9b51feSCy Schubert int
ipf_nat_settimeout(struct ipf_main_softc_s * softc,ipftuneable_t * t,ipftuneval_t * p)7819064a5a95SCy Schubert ipf_nat_settimeout(struct ipf_main_softc_s *softc, ipftuneable_t *t,
7820064a5a95SCy Schubert 	ipftuneval_t *p)
78213b9b51feSCy Schubert {
78223b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
78233b9b51feSCy Schubert 
78243b9b51feSCy Schubert 	if (!strncmp(t->ipft_name, "tcp_", 4))
78258c82b374SCy Schubert 		return (ipf_settimeout_tcp(t, p, softn->ipf_nat_tcptq));
78263b9b51feSCy Schubert 
78273b9b51feSCy Schubert 	if (!strcmp(t->ipft_name, "udp_timeout")) {
78283b9b51feSCy Schubert 		ipf_apply_timeout(&softn->ipf_nat_udptq, p->ipftu_int);
78293b9b51feSCy Schubert 	} else if (!strcmp(t->ipft_name, "udp_ack_timeout")) {
78303b9b51feSCy Schubert 		ipf_apply_timeout(&softn->ipf_nat_udpacktq, p->ipftu_int);
78313b9b51feSCy Schubert 	} else if (!strcmp(t->ipft_name, "icmp_timeout")) {
78323b9b51feSCy Schubert 		ipf_apply_timeout(&softn->ipf_nat_icmptq, p->ipftu_int);
78333b9b51feSCy Schubert 	} else if (!strcmp(t->ipft_name, "icmp_ack_timeout")) {
78343b9b51feSCy Schubert 		ipf_apply_timeout(&softn->ipf_nat_icmpacktq, p->ipftu_int);
78353b9b51feSCy Schubert 	} else if (!strcmp(t->ipft_name, "ip_timeout")) {
78363b9b51feSCy Schubert 		ipf_apply_timeout(&softn->ipf_nat_iptq, p->ipftu_int);
78373b9b51feSCy Schubert 	} else {
78383b9b51feSCy Schubert 		IPFERROR(60062);
78398c82b374SCy Schubert 		return (ESRCH);
78403b9b51feSCy Schubert 	}
78418c82b374SCy Schubert 	return (0);
78423b9b51feSCy Schubert }
78433b9b51feSCy Schubert 
78443b9b51feSCy Schubert 
78453b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
78463b9b51feSCy Schubert /* Function:    ipf_nat_rehash                                              */
78473b9b51feSCy Schubert /* Returns:     int  - 0 = success, else failure			    */
78483b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
78493b9b51feSCy Schubert /*              t(I) - pointer to tunable                                   */
78503b9b51feSCy Schubert /*              p(I) - pointer to new tuning data                           */
78513b9b51feSCy Schubert /*                                                                          */
78523b9b51feSCy Schubert /* To change the size of the basic NAT table, we need to first allocate the */
78533b9b51feSCy Schubert /* new tables (lest it fails and we've got nowhere to store all of the NAT  */
78543b9b51feSCy Schubert /* sessions currently active) and then walk through the entire list and     */
78553b9b51feSCy Schubert /* insert them into the table.  There are two tables here: an inbound one   */
78563b9b51feSCy Schubert /* and an outbound one.  Each NAT entry goes into each table once.          */
78573b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
78583b9b51feSCy Schubert int
ipf_nat_rehash(ipf_main_softc_t * softc,ipftuneable_t * t,ipftuneval_t * p)7859064a5a95SCy Schubert ipf_nat_rehash(ipf_main_softc_t *softc, ipftuneable_t *t, ipftuneval_t *p)
78603b9b51feSCy Schubert {
78613b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
78623b9b51feSCy Schubert 	nat_t **newtab[2], *nat, **natp;
78633b9b51feSCy Schubert 	u_int *bucketlens[2];
78643b9b51feSCy Schubert 	u_int maxbucket;
78653b9b51feSCy Schubert 	u_int newsize;
78663b9b51feSCy Schubert 	int error;
78673b9b51feSCy Schubert 	u_int hv;
78683b9b51feSCy Schubert 	int i;
78693b9b51feSCy Schubert 
78703b9b51feSCy Schubert 	newsize = p->ipftu_int;
78713b9b51feSCy Schubert 	/*
78723b9b51feSCy Schubert 	 * In case there is nothing to do...
78733b9b51feSCy Schubert 	 */
78743b9b51feSCy Schubert 	if (newsize == softn->ipf_nat_table_sz)
78758c82b374SCy Schubert 		return (0);
78763b9b51feSCy Schubert 
78773b9b51feSCy Schubert 	newtab[0] = NULL;
78783b9b51feSCy Schubert 	newtab[1] = NULL;
78793b9b51feSCy Schubert 	bucketlens[0] = NULL;
78803b9b51feSCy Schubert 	bucketlens[1] = NULL;
78813b9b51feSCy Schubert 	/*
78823b9b51feSCy Schubert 	 * 4 tables depend on the NAT table size: the inbound looking table,
78833b9b51feSCy Schubert 	 * the outbound lookup table and the hash chain length for each.
78843b9b51feSCy Schubert 	 */
78853b9b51feSCy Schubert 	KMALLOCS(newtab[0], nat_t **, newsize * sizeof(nat_t *));
78863b9b51feSCy Schubert 	if (newtab[0] == NULL) {
78873b9b51feSCy Schubert 		error = 60063;
78883b9b51feSCy Schubert 		goto badrehash;
78893b9b51feSCy Schubert 	}
78903b9b51feSCy Schubert 
78913b9b51feSCy Schubert 	KMALLOCS(newtab[1], nat_t **, newsize * sizeof(nat_t *));
78923b9b51feSCy Schubert 	if (newtab[1] == NULL) {
78933b9b51feSCy Schubert 		error = 60064;
78943b9b51feSCy Schubert 		goto badrehash;
78953b9b51feSCy Schubert 	}
78963b9b51feSCy Schubert 
78973b9b51feSCy Schubert 	KMALLOCS(bucketlens[0], u_int *, newsize * sizeof(u_int));
78983b9b51feSCy Schubert 	if (bucketlens[0] == NULL) {
78993b9b51feSCy Schubert 		error = 60065;
79003b9b51feSCy Schubert 		goto badrehash;
79013b9b51feSCy Schubert 	}
79023b9b51feSCy Schubert 
79033b9b51feSCy Schubert 	KMALLOCS(bucketlens[1], u_int *, newsize * sizeof(u_int));
79043b9b51feSCy Schubert 	if (bucketlens[1] == NULL) {
79053b9b51feSCy Schubert 		error = 60066;
79063b9b51feSCy Schubert 		goto badrehash;
79073b9b51feSCy Schubert 	}
79083b9b51feSCy Schubert 
79093b9b51feSCy Schubert 	/*
79103b9b51feSCy Schubert 	 * Recalculate the maximum length based on the new size.
79113b9b51feSCy Schubert 	 */
79123b9b51feSCy Schubert 	for (maxbucket = 0, i = newsize; i > 0; i >>= 1)
79133b9b51feSCy Schubert 		maxbucket++;
79143b9b51feSCy Schubert 	maxbucket *= 2;
79153b9b51feSCy Schubert 
79163b9b51feSCy Schubert 	bzero((char *)newtab[0], newsize * sizeof(nat_t *));
79173b9b51feSCy Schubert 	bzero((char *)newtab[1], newsize * sizeof(nat_t *));
79183b9b51feSCy Schubert 	bzero((char *)bucketlens[0], newsize * sizeof(u_int));
79193b9b51feSCy Schubert 	bzero((char *)bucketlens[1], newsize * sizeof(u_int));
79203b9b51feSCy Schubert 
79213b9b51feSCy Schubert 	WRITE_ENTER(&softc->ipf_nat);
79223b9b51feSCy Schubert 
79233b9b51feSCy Schubert 	if (softn->ipf_nat_table[0] != NULL) {
79243b9b51feSCy Schubert 		KFREES(softn->ipf_nat_table[0],
79253b9b51feSCy Schubert 		       softn->ipf_nat_table_sz *
79263b9b51feSCy Schubert 		       sizeof(*softn->ipf_nat_table[0]));
79273b9b51feSCy Schubert 	}
79283b9b51feSCy Schubert 	softn->ipf_nat_table[0] = newtab[0];
79293b9b51feSCy Schubert 
79303b9b51feSCy Schubert 	if (softn->ipf_nat_table[1] != NULL) {
79313b9b51feSCy Schubert 		KFREES(softn->ipf_nat_table[1],
79323b9b51feSCy Schubert 		       softn->ipf_nat_table_sz *
79333b9b51feSCy Schubert 		       sizeof(*softn->ipf_nat_table[1]));
79343b9b51feSCy Schubert 	}
79353b9b51feSCy Schubert 	softn->ipf_nat_table[1] = newtab[1];
79363b9b51feSCy Schubert 
79373b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_side[0].ns_bucketlen != NULL) {
79383b9b51feSCy Schubert 		KFREES(softn->ipf_nat_stats.ns_side[0].ns_bucketlen,
79393b9b51feSCy Schubert 		       softn->ipf_nat_table_sz * sizeof(u_int));
79403b9b51feSCy Schubert 	}
79413b9b51feSCy Schubert 	softn->ipf_nat_stats.ns_side[0].ns_bucketlen = bucketlens[0];
79423b9b51feSCy Schubert 
79433b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_side[1].ns_bucketlen != NULL) {
79443b9b51feSCy Schubert 		KFREES(softn->ipf_nat_stats.ns_side[1].ns_bucketlen,
79453b9b51feSCy Schubert 		       softn->ipf_nat_table_sz * sizeof(u_int));
79463b9b51feSCy Schubert 	}
79473b9b51feSCy Schubert 	softn->ipf_nat_stats.ns_side[1].ns_bucketlen = bucketlens[1];
79483b9b51feSCy Schubert 
79493b9b51feSCy Schubert #ifdef USE_INET6
79503b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_side6[0].ns_bucketlen != NULL) {
79513b9b51feSCy Schubert 		KFREES(softn->ipf_nat_stats.ns_side6[0].ns_bucketlen,
79523b9b51feSCy Schubert 		       softn->ipf_nat_table_sz * sizeof(u_int));
79533b9b51feSCy Schubert 	}
79543b9b51feSCy Schubert 	softn->ipf_nat_stats.ns_side6[0].ns_bucketlen = bucketlens[0];
79553b9b51feSCy Schubert 
79563b9b51feSCy Schubert 	if (softn->ipf_nat_stats.ns_side6[1].ns_bucketlen != NULL) {
79573b9b51feSCy Schubert 		KFREES(softn->ipf_nat_stats.ns_side6[1].ns_bucketlen,
79583b9b51feSCy Schubert 		       softn->ipf_nat_table_sz * sizeof(u_int));
79593b9b51feSCy Schubert 	}
79603b9b51feSCy Schubert 	softn->ipf_nat_stats.ns_side6[1].ns_bucketlen = bucketlens[1];
79613b9b51feSCy Schubert #endif
79623b9b51feSCy Schubert 
79633b9b51feSCy Schubert 	softn->ipf_nat_maxbucket = maxbucket;
79643b9b51feSCy Schubert 	softn->ipf_nat_table_sz = newsize;
79653b9b51feSCy Schubert 	/*
79663b9b51feSCy Schubert 	 * Walk through the entire list of NAT table entries and put them
79673b9b51feSCy Schubert 	 * in the new NAT table, somewhere.  Because we have a new table,
79683b9b51feSCy Schubert 	 * we need to restart the counter of how many chains are in use.
79693b9b51feSCy Schubert 	 */
79703b9b51feSCy Schubert 	softn->ipf_nat_stats.ns_side[0].ns_inuse = 0;
79713b9b51feSCy Schubert 	softn->ipf_nat_stats.ns_side[1].ns_inuse = 0;
79723b9b51feSCy Schubert #ifdef USE_INET6
79733b9b51feSCy Schubert 	softn->ipf_nat_stats.ns_side6[0].ns_inuse = 0;
79743b9b51feSCy Schubert 	softn->ipf_nat_stats.ns_side6[1].ns_inuse = 0;
79753b9b51feSCy Schubert #endif
79763b9b51feSCy Schubert 
79773b9b51feSCy Schubert 	for (nat = softn->ipf_nat_instances; nat != NULL; nat = nat->nat_next) {
79783b9b51feSCy Schubert 		nat->nat_hnext[0] = NULL;
79793b9b51feSCy Schubert 		nat->nat_phnext[0] = NULL;
79803b9b51feSCy Schubert 		hv = nat->nat_hv[0] % softn->ipf_nat_table_sz;
79813b9b51feSCy Schubert 
79823b9b51feSCy Schubert 		natp = &softn->ipf_nat_table[0][hv];
79833b9b51feSCy Schubert 		if (*natp) {
79843b9b51feSCy Schubert 			(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
79853b9b51feSCy Schubert 		} else {
79863b9b51feSCy Schubert 			NBUMPSIDE(0, ns_inuse);
79873b9b51feSCy Schubert 		}
79883b9b51feSCy Schubert 		nat->nat_phnext[0] = natp;
79893b9b51feSCy Schubert 		nat->nat_hnext[0] = *natp;
79903b9b51feSCy Schubert 		*natp = nat;
79913b9b51feSCy Schubert 		NBUMPSIDE(0, ns_bucketlen[hv]);
79923b9b51feSCy Schubert 
79933b9b51feSCy Schubert 		nat->nat_hnext[1] = NULL;
79943b9b51feSCy Schubert 		nat->nat_phnext[1] = NULL;
79953b9b51feSCy Schubert 		hv = nat->nat_hv[1] % softn->ipf_nat_table_sz;
79963b9b51feSCy Schubert 
79973b9b51feSCy Schubert 		natp = &softn->ipf_nat_table[1][hv];
79983b9b51feSCy Schubert 		if (*natp) {
79993b9b51feSCy Schubert 			(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
80003b9b51feSCy Schubert 		} else {
80013b9b51feSCy Schubert 			NBUMPSIDE(1, ns_inuse);
80023b9b51feSCy Schubert 		}
80033b9b51feSCy Schubert 		nat->nat_phnext[1] = natp;
80043b9b51feSCy Schubert 		nat->nat_hnext[1] = *natp;
80053b9b51feSCy Schubert 		*natp = nat;
80063b9b51feSCy Schubert 		NBUMPSIDE(1, ns_bucketlen[hv]);
80073b9b51feSCy Schubert 	}
80083b9b51feSCy Schubert 	RWLOCK_EXIT(&softc->ipf_nat);
80093b9b51feSCy Schubert 
80108c82b374SCy Schubert 	return (0);
80113b9b51feSCy Schubert 
80123b9b51feSCy Schubert badrehash:
80133b9b51feSCy Schubert 	if (bucketlens[1] != NULL) {
80143b9b51feSCy Schubert 		KFREES(bucketlens[0], newsize * sizeof(u_int));
80153b9b51feSCy Schubert 	}
80163b9b51feSCy Schubert 	if (bucketlens[0] != NULL) {
80173b9b51feSCy Schubert 		KFREES(bucketlens[0], newsize * sizeof(u_int));
80183b9b51feSCy Schubert 	}
80193b9b51feSCy Schubert 	if (newtab[0] != NULL) {
80203b9b51feSCy Schubert 		KFREES(newtab[0], newsize * sizeof(nat_t *));
80213b9b51feSCy Schubert 	}
80223b9b51feSCy Schubert 	if (newtab[1] != NULL) {
80233b9b51feSCy Schubert 		KFREES(newtab[1], newsize * sizeof(nat_t *));
80243b9b51feSCy Schubert 	}
80253b9b51feSCy Schubert 	IPFERROR(error);
80268c82b374SCy Schubert 	return (ENOMEM);
80273b9b51feSCy Schubert }
80283b9b51feSCy Schubert 
80293b9b51feSCy Schubert 
80303b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
80313b9b51feSCy Schubert /* Function:    ipf_nat_rehash_rules                                        */
80323b9b51feSCy Schubert /* Returns:     int  - 0 = success, else failure			    */
80333b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
80343b9b51feSCy Schubert /*              t(I) - pointer to tunable                                   */
80353b9b51feSCy Schubert /*              p(I) - pointer to new tuning data                           */
80363b9b51feSCy Schubert /*                                                                          */
80373b9b51feSCy Schubert /* All of the NAT rules hang off of a hash table that is searched with a    */
80383b9b51feSCy Schubert /* hash on address after the netmask is applied.  There is a different table*/
80393b9b51feSCy Schubert /* for both inbound rules (rdr) and outbound (map.)  The resizing will only */
80403b9b51feSCy Schubert /* affect one of these two tables.                                          */
80413b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
80423b9b51feSCy Schubert int
ipf_nat_rehash_rules(ipf_main_softc_t * softc,ipftuneable_t * t,ipftuneval_t * p)8043064a5a95SCy Schubert ipf_nat_rehash_rules(ipf_main_softc_t *softc, ipftuneable_t *t,
8044064a5a95SCy Schubert 	ipftuneval_t *p)
80453b9b51feSCy Schubert {
80463b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
80473b9b51feSCy Schubert 	ipnat_t **newtab, *np, ***old, **npp;
80483b9b51feSCy Schubert 	u_int newsize;
80493b9b51feSCy Schubert 	u_int mask;
80503b9b51feSCy Schubert 	u_int hv;
80513b9b51feSCy Schubert 
80523b9b51feSCy Schubert 	newsize = p->ipftu_int;
80533b9b51feSCy Schubert 	/*
80543b9b51feSCy Schubert 	 * In case there is nothing to do...
80553b9b51feSCy Schubert 	 */
80563b9b51feSCy Schubert 	if (newsize == *t->ipft_pint)
80578c82b374SCy Schubert 		return (0);
80583b9b51feSCy Schubert 
80593b9b51feSCy Schubert 	/*
80603b9b51feSCy Schubert 	 * All inbound rules have the NAT_REDIRECT bit set in in_redir and
80613b9b51feSCy Schubert 	 * all outbound rules have either NAT_MAP or MAT_MAPBLK set.
80623b9b51feSCy Schubert 	 * This if statement allows for some more generic code to be below,
80633b9b51feSCy Schubert 	 * rather than two huge gobs of code that almost do the same thing.
80643b9b51feSCy Schubert 	 */
80653b9b51feSCy Schubert 	if (t->ipft_pint == &softn->ipf_nat_rdrrules_sz) {
80663b9b51feSCy Schubert 		old = &softn->ipf_nat_rdr_rules;
80673b9b51feSCy Schubert 		mask = NAT_REDIRECT;
80683b9b51feSCy Schubert 	} else {
80693b9b51feSCy Schubert 		old = &softn->ipf_nat_map_rules;
80703b9b51feSCy Schubert 		mask = NAT_MAP|NAT_MAPBLK;
80713b9b51feSCy Schubert 	}
80723b9b51feSCy Schubert 
80733b9b51feSCy Schubert 	KMALLOCS(newtab, ipnat_t **, newsize * sizeof(ipnat_t *));
80743b9b51feSCy Schubert 	if (newtab == NULL) {
80753b9b51feSCy Schubert 		IPFERROR(60067);
80768c82b374SCy Schubert 		return (ENOMEM);
80773b9b51feSCy Schubert 	}
80783b9b51feSCy Schubert 
80793b9b51feSCy Schubert 	bzero((char *)newtab, newsize * sizeof(ipnat_t *));
80803b9b51feSCy Schubert 
80813b9b51feSCy Schubert 	WRITE_ENTER(&softc->ipf_nat);
80823b9b51feSCy Schubert 
80833b9b51feSCy Schubert 	if (*old != NULL) {
80843b9b51feSCy Schubert 		KFREES(*old, *t->ipft_pint * sizeof(ipnat_t **));
80853b9b51feSCy Schubert 	}
80863b9b51feSCy Schubert 	*old = newtab;
80873b9b51feSCy Schubert 	*t->ipft_pint = newsize;
80883b9b51feSCy Schubert 
80893b9b51feSCy Schubert 	for (np = softn->ipf_nat_list; np != NULL; np = np->in_next) {
80903b9b51feSCy Schubert 		if ((np->in_redir & mask) == 0)
80913b9b51feSCy Schubert 			continue;
80923b9b51feSCy Schubert 
80933b9b51feSCy Schubert 		if (np->in_redir & NAT_REDIRECT) {
80943b9b51feSCy Schubert 			np->in_rnext = NULL;
80953b9b51feSCy Schubert 			hv = np->in_hv[0] % newsize;
80963b9b51feSCy Schubert 			for (npp = newtab + hv; *npp != NULL; )
80973b9b51feSCy Schubert 				npp = &(*npp)->in_rnext;
80983b9b51feSCy Schubert 			np->in_prnext = npp;
80993b9b51feSCy Schubert 			*npp = np;
81003b9b51feSCy Schubert 		}
81013b9b51feSCy Schubert 		if (np->in_redir & NAT_MAP) {
81023b9b51feSCy Schubert 			np->in_mnext = NULL;
81033b9b51feSCy Schubert 			hv = np->in_hv[1] % newsize;
81043b9b51feSCy Schubert 			for (npp = newtab + hv; *npp != NULL; )
81053b9b51feSCy Schubert 				npp = &(*npp)->in_mnext;
81063b9b51feSCy Schubert 			np->in_pmnext = npp;
81073b9b51feSCy Schubert 			*npp = np;
81083b9b51feSCy Schubert 		}
81093b9b51feSCy Schubert 
81103b9b51feSCy Schubert 	}
81113b9b51feSCy Schubert 	RWLOCK_EXIT(&softc->ipf_nat);
81123b9b51feSCy Schubert 
81138c82b374SCy Schubert 	return (0);
81143b9b51feSCy Schubert }
81153b9b51feSCy Schubert 
81163b9b51feSCy Schubert 
81173b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
81183b9b51feSCy Schubert /* Function:    ipf_nat_hostmap_rehash                                      */
81193b9b51feSCy Schubert /* Returns:     int  - 0 = success, else failure			    */
81203b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
81213b9b51feSCy Schubert /*              t(I) - pointer to tunable                                   */
81223b9b51feSCy Schubert /*              p(I) - pointer to new tuning data                           */
81233b9b51feSCy Schubert /*                                                                          */
81243b9b51feSCy Schubert /* Allocate and populate a new hash table that will contain a reference to  */
81253b9b51feSCy Schubert /* all of the active IP# translations currently in place.                   */
81263b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
81273b9b51feSCy Schubert int
ipf_nat_hostmap_rehash(ipf_main_softc_t * softc,ipftuneable_t * t,ipftuneval_t * p)8128064a5a95SCy Schubert ipf_nat_hostmap_rehash(ipf_main_softc_t *softc, ipftuneable_t *t,
8129064a5a95SCy Schubert 	ipftuneval_t *p)
81303b9b51feSCy Schubert {
81313b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
81323b9b51feSCy Schubert 	hostmap_t *hm, **newtab;
81333b9b51feSCy Schubert 	u_int newsize;
81343b9b51feSCy Schubert 	u_int hv;
81353b9b51feSCy Schubert 
81363b9b51feSCy Schubert 	newsize = p->ipftu_int;
81373b9b51feSCy Schubert 	/*
81383b9b51feSCy Schubert 	 * In case there is nothing to do...
81393b9b51feSCy Schubert 	 */
81403b9b51feSCy Schubert 	if (newsize == *t->ipft_pint)
81418c82b374SCy Schubert 		return (0);
81423b9b51feSCy Schubert 
81433b9b51feSCy Schubert 	KMALLOCS(newtab, hostmap_t **, newsize * sizeof(hostmap_t *));
81443b9b51feSCy Schubert 	if (newtab == NULL) {
81453b9b51feSCy Schubert 		IPFERROR(60068);
81468c82b374SCy Schubert 		return (ENOMEM);
81473b9b51feSCy Schubert 	}
81483b9b51feSCy Schubert 
81493b9b51feSCy Schubert 	bzero((char *)newtab, newsize * sizeof(hostmap_t *));
81503b9b51feSCy Schubert 
81513b9b51feSCy Schubert 	WRITE_ENTER(&softc->ipf_nat);
81523b9b51feSCy Schubert 	if (softn->ipf_hm_maptable != NULL) {
81533b9b51feSCy Schubert 		KFREES(softn->ipf_hm_maptable,
81543b9b51feSCy Schubert 		       softn->ipf_nat_hostmap_sz * sizeof(hostmap_t *));
81553b9b51feSCy Schubert 	}
81563b9b51feSCy Schubert 	softn->ipf_hm_maptable = newtab;
81573b9b51feSCy Schubert 	softn->ipf_nat_hostmap_sz = newsize;
81583b9b51feSCy Schubert 
81593b9b51feSCy Schubert 	for (hm = softn->ipf_hm_maplist; hm != NULL; hm = hm->hm_next) {
81603b9b51feSCy Schubert 		hv = hm->hm_hv % softn->ipf_nat_hostmap_sz;
81613b9b51feSCy Schubert 		hm->hm_hnext = softn->ipf_hm_maptable[hv];
81623b9b51feSCy Schubert 		hm->hm_phnext = softn->ipf_hm_maptable + hv;
81633b9b51feSCy Schubert 		if (softn->ipf_hm_maptable[hv] != NULL)
81643b9b51feSCy Schubert 			softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext;
81653b9b51feSCy Schubert 		softn->ipf_hm_maptable[hv] = hm;
81663b9b51feSCy Schubert 	}
81673b9b51feSCy Schubert 	RWLOCK_EXIT(&softc->ipf_nat);
81683b9b51feSCy Schubert 
81698c82b374SCy Schubert 	return (0);
81703b9b51feSCy Schubert }
81713b9b51feSCy Schubert 
81723b9b51feSCy Schubert 
81733b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
81743b9b51feSCy Schubert /* Function:    ipf_nat_add_tq                                              */
81753b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
81763b9b51feSCy Schubert /*                                                                          */
81773b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
81783b9b51feSCy Schubert ipftq_t *
ipf_nat_add_tq(ipf_main_softc_t * softc,int ttl)8179064a5a95SCy Schubert ipf_nat_add_tq(ipf_main_softc_t *softc, int ttl)
81803b9b51feSCy Schubert {
81813b9b51feSCy Schubert 	ipf_nat_softc_t *softs = softc->ipf_nat_soft;
81823b9b51feSCy Schubert 
81838c82b374SCy Schubert 	return (ipf_addtimeoutqueue(softc, &softs->ipf_nat_utqe, ttl));
81843b9b51feSCy Schubert }
81853b9b51feSCy Schubert 
81863b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
81873b9b51feSCy Schubert /* Function:    ipf_nat_uncreate                                            */
81883b9b51feSCy Schubert /* Returns:     Nil                                                         */
81893b9b51feSCy Schubert /* Parameters:  fin(I) - pointer to packet information                      */
81903b9b51feSCy Schubert /*                                                                          */
81913b9b51feSCy Schubert /* This function is used to remove a NAT entry from the NAT table when we   */
81923b9b51feSCy Schubert /* decide that the create was actually in error. It is thus assumed that    */
81933b9b51feSCy Schubert /* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */
81943b9b51feSCy Schubert /* with the translated packet (not the original), we have to reverse the    */
81953b9b51feSCy Schubert /* lookup. Although doing the lookup is expensive (relatively speaking), it */
81963b9b51feSCy Schubert /* is not anticipated that this will be a frequent occurance for normal     */
81973b9b51feSCy Schubert /* traffic patterns.                                                        */
81983b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
81993b9b51feSCy Schubert void
ipf_nat_uncreate(fr_info_t * fin)8200064a5a95SCy Schubert ipf_nat_uncreate(fr_info_t *fin)
82013b9b51feSCy Schubert {
82023b9b51feSCy Schubert 	ipf_main_softc_t *softc = fin->fin_main_soft;
82033b9b51feSCy Schubert 	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
82043b9b51feSCy Schubert 	int nflags;
82053b9b51feSCy Schubert 	nat_t *nat;
82063b9b51feSCy Schubert 
82073b9b51feSCy Schubert 	switch (fin->fin_p)
82083b9b51feSCy Schubert 	{
82093b9b51feSCy Schubert 	case IPPROTO_TCP :
82103b9b51feSCy Schubert 		nflags = IPN_TCP;
82113b9b51feSCy Schubert 		break;
82123b9b51feSCy Schubert 	case IPPROTO_UDP :
82133b9b51feSCy Schubert 		nflags = IPN_UDP;
82143b9b51feSCy Schubert 		break;
82153b9b51feSCy Schubert 	default :
82163b9b51feSCy Schubert 		nflags = 0;
82173b9b51feSCy Schubert 		break;
82183b9b51feSCy Schubert 	}
82193b9b51feSCy Schubert 
82203b9b51feSCy Schubert 	WRITE_ENTER(&softc->ipf_nat);
82213b9b51feSCy Schubert 
82223b9b51feSCy Schubert 	if (fin->fin_out == 0) {
82233b9b51feSCy Schubert 		nat = ipf_nat_outlookup(fin, nflags, (u_int)fin->fin_p,
82243b9b51feSCy Schubert 					fin->fin_dst, fin->fin_src);
82253b9b51feSCy Schubert 	} else {
82263b9b51feSCy Schubert 		nat = ipf_nat_inlookup(fin, nflags, (u_int)fin->fin_p,
82273b9b51feSCy Schubert 				       fin->fin_src, fin->fin_dst);
82283b9b51feSCy Schubert 	}
82293b9b51feSCy Schubert 
82303b9b51feSCy Schubert 	if (nat != NULL) {
82313b9b51feSCy Schubert 		NBUMPSIDE(fin->fin_out, ns_uncreate[0]);
82323b9b51feSCy Schubert 		ipf_nat_delete(softc, nat, NL_DESTROY);
82333b9b51feSCy Schubert 	} else {
82343b9b51feSCy Schubert 		NBUMPSIDE(fin->fin_out, ns_uncreate[1]);
82353b9b51feSCy Schubert 	}
82363b9b51feSCy Schubert 
82373b9b51feSCy Schubert 	RWLOCK_EXIT(&softc->ipf_nat);
82383b9b51feSCy Schubert }
82393b9b51feSCy Schubert 
82403b9b51feSCy Schubert 
82413b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
82423b9b51feSCy Schubert /* Function:    ipf_nat_cmp_rules                                           */
82433b9b51feSCy Schubert /* Returns:     int   - 0 == success, else rules do not match.              */
82443b9b51feSCy Schubert /* Parameters:  n1(I) - first rule to compare                               */
82453b9b51feSCy Schubert /*              n2(I) - first rule to compare                               */
82463b9b51feSCy Schubert /*                                                                          */
82473b9b51feSCy Schubert /* Compare two rules using pointers to each rule. A straight bcmp will not  */
82483b9b51feSCy Schubert /* work as some fields (such as in_dst, in_pkts) actually do change once    */
82493b9b51feSCy Schubert /* the rule has been loaded into the kernel. Whilst this function returns   */
82503b9b51feSCy Schubert /* various non-zero returns, they're strictly to aid in debugging. Use of   */
82513b9b51feSCy Schubert /* this function should simply care if the result is zero or not.           */
82523b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
82533b9b51feSCy Schubert static int
ipf_nat_cmp_rules(ipnat_t * n1,ipnat_t * n2)8254064a5a95SCy Schubert ipf_nat_cmp_rules(ipnat_t *n1, ipnat_t *n2)
82553b9b51feSCy Schubert {
82563b9b51feSCy Schubert 	if (n1->in_size != n2->in_size)
82578c82b374SCy Schubert 		return (1);
82583b9b51feSCy Schubert 
82593b9b51feSCy Schubert 	if (bcmp((char *)&n1->in_v, (char *)&n2->in_v,
82603b9b51feSCy Schubert 		 offsetof(ipnat_t, in_ndst) - offsetof(ipnat_t, in_v)) != 0)
82618c82b374SCy Schubert 		return (2);
82623b9b51feSCy Schubert 
82633b9b51feSCy Schubert 	if (bcmp((char *)&n1->in_tuc, (char *)&n2->in_tuc,
82643b9b51feSCy Schubert 		 n1->in_size - offsetof(ipnat_t, in_tuc)) != 0)
82658c82b374SCy Schubert 		return (3);
82663b9b51feSCy Schubert 	if (n1->in_ndst.na_atype != n2->in_ndst.na_atype)
82678c82b374SCy Schubert 		return (5);
82683b9b51feSCy Schubert 	if (n1->in_ndst.na_function != n2->in_ndst.na_function)
82698c82b374SCy Schubert 		return (6);
82703b9b51feSCy Schubert 	if (bcmp((char *)&n1->in_ndst.na_addr, (char *)&n2->in_ndst.na_addr,
82713b9b51feSCy Schubert 		 sizeof(n1->in_ndst.na_addr)))
82728c82b374SCy Schubert 		return (7);
82733b9b51feSCy Schubert 	if (n1->in_nsrc.na_atype != n2->in_nsrc.na_atype)
82748c82b374SCy Schubert 		return (8);
82753b9b51feSCy Schubert 	if (n1->in_nsrc.na_function != n2->in_nsrc.na_function)
82768c82b374SCy Schubert 		return (9);
82773b9b51feSCy Schubert 	if (bcmp((char *)&n1->in_nsrc.na_addr, (char *)&n2->in_nsrc.na_addr,
82783b9b51feSCy Schubert 		 sizeof(n1->in_nsrc.na_addr)))
82798c82b374SCy Schubert 		return (10);
82803b9b51feSCy Schubert 	if (n1->in_odst.na_atype != n2->in_odst.na_atype)
82818c82b374SCy Schubert 		return (11);
82823b9b51feSCy Schubert 	if (n1->in_odst.na_function != n2->in_odst.na_function)
82838c82b374SCy Schubert 		return (12);
82843b9b51feSCy Schubert 	if (bcmp((char *)&n1->in_odst.na_addr, (char *)&n2->in_odst.na_addr,
82853b9b51feSCy Schubert 		 sizeof(n1->in_odst.na_addr)))
82868c82b374SCy Schubert 		return (13);
82873b9b51feSCy Schubert 	if (n1->in_osrc.na_atype != n2->in_osrc.na_atype)
82888c82b374SCy Schubert 		return (14);
82893b9b51feSCy Schubert 	if (n1->in_osrc.na_function != n2->in_osrc.na_function)
82908c82b374SCy Schubert 		return (15);
82913b9b51feSCy Schubert 	if (bcmp((char *)&n1->in_osrc.na_addr, (char *)&n2->in_osrc.na_addr,
82923b9b51feSCy Schubert 		 sizeof(n1->in_osrc.na_addr)))
82938c82b374SCy Schubert 		return (16);
82948c82b374SCy Schubert 	return (0);
82953b9b51feSCy Schubert }
82963b9b51feSCy Schubert 
82973b9b51feSCy Schubert 
82983b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
82993b9b51feSCy Schubert /* Function:    ipf_nat_rule_init                                           */
83003b9b51feSCy Schubert /* Returns:     int   - 0 == success, else rules do not match.              */
83013b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
83023b9b51feSCy Schubert /*              softn(I) - pointer to NAT context structure                 */
83033b9b51feSCy Schubert /*              n(I)     - first rule to compare                            */
83043b9b51feSCy Schubert /*                                                                          */
83053b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
83063b9b51feSCy Schubert static int
ipf_nat_rule_init(ipf_main_softc_t * softc,ipf_nat_softc_t * softn,ipnat_t * n)8307064a5a95SCy Schubert ipf_nat_rule_init(ipf_main_softc_t *softc, ipf_nat_softc_t *softn,
8308064a5a95SCy Schubert 	ipnat_t *n)
83093b9b51feSCy Schubert {
83103b9b51feSCy Schubert 	int error = 0;
83113b9b51feSCy Schubert 
83123b9b51feSCy Schubert 	if ((n->in_flags & IPN_SIPRANGE) != 0)
83133b9b51feSCy Schubert 		n->in_nsrcatype = FRI_RANGE;
83143b9b51feSCy Schubert 
83153b9b51feSCy Schubert 	if ((n->in_flags & IPN_DIPRANGE) != 0)
83163b9b51feSCy Schubert 		n->in_ndstatype = FRI_RANGE;
83173b9b51feSCy Schubert 
83183b9b51feSCy Schubert 	if ((n->in_flags & IPN_SPLIT) != 0)
83193b9b51feSCy Schubert 		n->in_ndstatype = FRI_SPLIT;
83203b9b51feSCy Schubert 
83213b9b51feSCy Schubert 	if ((n->in_redir & (NAT_MAP|NAT_REWRITE|NAT_DIVERTUDP)) != 0)
83223b9b51feSCy Schubert 		n->in_spnext = n->in_spmin;
83233b9b51feSCy Schubert 
83243b9b51feSCy Schubert 	if ((n->in_redir & (NAT_REWRITE|NAT_DIVERTUDP)) != 0) {
83253b9b51feSCy Schubert 		n->in_dpnext = n->in_dpmin;
83263b9b51feSCy Schubert 	} else if (n->in_redir == NAT_REDIRECT) {
83273b9b51feSCy Schubert 		n->in_dpnext = n->in_dpmin;
83283b9b51feSCy Schubert 	}
83293b9b51feSCy Schubert 
83303b9b51feSCy Schubert 	n->in_stepnext = 0;
83313b9b51feSCy Schubert 
83323b9b51feSCy Schubert 	switch (n->in_v[0])
83333b9b51feSCy Schubert 	{
83343b9b51feSCy Schubert 	case 4 :
83353b9b51feSCy Schubert 		error = ipf_nat_ruleaddrinit(softc, softn, n);
83363b9b51feSCy Schubert 		if (error != 0)
83378c82b374SCy Schubert 			return (error);
83383b9b51feSCy Schubert 		break;
83393b9b51feSCy Schubert #ifdef USE_INET6
83403b9b51feSCy Schubert 	case 6 :
83413b9b51feSCy Schubert 		error = ipf_nat6_ruleaddrinit(softc, softn, n);
83423b9b51feSCy Schubert 		if (error != 0)
83438c82b374SCy Schubert 			return (error);
83443b9b51feSCy Schubert 		break;
83453b9b51feSCy Schubert #endif
83463b9b51feSCy Schubert 	default :
83473b9b51feSCy Schubert 		break;
83483b9b51feSCy Schubert 	}
83493b9b51feSCy Schubert 
83503b9b51feSCy Schubert 	if (n->in_redir == (NAT_DIVERTUDP|NAT_MAP)) {
83513b9b51feSCy Schubert 		/*
83523b9b51feSCy Schubert 		 * Prerecord whether or not the destination of the divert
83533b9b51feSCy Schubert 		 * is local or not to the interface the packet is going
83543b9b51feSCy Schubert 		 * to be sent out.
83553b9b51feSCy Schubert 		 */
83563b9b51feSCy Schubert 		n->in_dlocal = ipf_deliverlocal(softc, n->in_v[1],
83573b9b51feSCy Schubert 						n->in_ifps[1], &n->in_ndstip6);
83583b9b51feSCy Schubert 	}
83593b9b51feSCy Schubert 
83608c82b374SCy Schubert 	return (error);
83613b9b51feSCy Schubert }
83623b9b51feSCy Schubert 
83633b9b51feSCy Schubert 
83643b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
83653b9b51feSCy Schubert /* Function:    ipf_nat_rule_fini                                           */
83663b9b51feSCy Schubert /* Returns:     int   - 0 == success, else rules do not match.              */
83673b9b51feSCy Schubert /* Parameters:  softc(I) - pointer to soft context main structure           */
83683b9b51feSCy Schubert /*              n(I)     - rule to work on                                  */
83693b9b51feSCy Schubert /*                                                                          */
83703b9b51feSCy Schubert /* This function is used to release any objects that were referenced during */
83713b9b51feSCy Schubert /* the rule initialisation. This is useful both when free'ing the rule and  */
83723b9b51feSCy Schubert /* when handling ioctls that need to initialise these fields but not        */
83733b9b51feSCy Schubert /* actually use them after the ioctl processing has finished.               */
83743b9b51feSCy Schubert /* ------------------------------------------------------------------------ */
83753b9b51feSCy Schubert static void
ipf_nat_rule_fini(ipf_main_softc_t * softc,ipnat_t * n)8376064a5a95SCy Schubert ipf_nat_rule_fini(ipf_main_softc_t *softc, ipnat_t *n)
83773b9b51feSCy Schubert {
83783b9b51feSCy Schubert 	if (n->in_odst.na_atype == FRI_LOOKUP && n->in_odst.na_ptr != NULL)
83793b9b51feSCy Schubert 		ipf_lookup_deref(softc, n->in_odst.na_type, n->in_odst.na_ptr);
83803b9b51feSCy Schubert 
83813b9b51feSCy Schubert 	if (n->in_osrc.na_atype == FRI_LOOKUP && n->in_osrc.na_ptr != NULL)
83823b9b51feSCy Schubert 		ipf_lookup_deref(softc, n->in_osrc.na_type, n->in_osrc.na_ptr);
83833b9b51feSCy Schubert 
83843b9b51feSCy Schubert 	if (n->in_ndst.na_atype == FRI_LOOKUP && n->in_ndst.na_ptr != NULL)
83853b9b51feSCy Schubert 		ipf_lookup_deref(softc, n->in_ndst.na_type, n->in_ndst.na_ptr);
83863b9b51feSCy Schubert 
83873b9b51feSCy Schubert 	if (n->in_nsrc.na_atype == FRI_LOOKUP && n->in_nsrc.na_ptr != NULL)
83883b9b51feSCy Schubert 		ipf_lookup_deref(softc, n->in_nsrc.na_type, n->in_nsrc.na_ptr);
83893b9b51feSCy Schubert 
83903b9b51feSCy Schubert 	if (n->in_divmp != NULL)
83913b9b51feSCy Schubert 		FREE_MB_T(n->in_divmp);
83923b9b51feSCy Schubert }
8393