xref: /onnv-gate/usr/src/uts/common/inet/ipf/ip_nat.c (revision 4380:f81c6be79aba)
12393Syz155240 /*
22393Syz155240  * Copyright (C) 1995-2003 by Darren Reed.
32393Syz155240  *
42393Syz155240  * See the IPFILTER.LICENCE file for details on licencing.
52393Syz155240  *
63448Sdh155122  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
72393Syz155240  * Use is subject to license terms.
82393Syz155240  */
92393Syz155240 
10*4380Sjojemann #pragma ident	"%Z%%M%	%I%	%E% SMI"$
112393Syz155240 
122393Syz155240 #if defined(KERNEL) || defined(_KERNEL)
132393Syz155240 # undef KERNEL
142393Syz155240 # undef _KERNEL
152393Syz155240 # define        KERNEL	1
162393Syz155240 # define        _KERNEL	1
172393Syz155240 #endif
182393Syz155240 #include <sys/errno.h>
192393Syz155240 #include <sys/types.h>
202393Syz155240 #include <sys/param.h>
212393Syz155240 #include <sys/time.h>
222393Syz155240 #include <sys/file.h>
232393Syz155240 #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \
242393Syz155240     defined(_KERNEL)
252393Syz155240 # include "opt_ipfilter_log.h"
262393Syz155240 #endif
272393Syz155240 #if !defined(_KERNEL)
282393Syz155240 # include <stdio.h>
292393Syz155240 # include <string.h>
302393Syz155240 # include <stdlib.h>
312393Syz155240 # define _KERNEL
322393Syz155240 # ifdef __OpenBSD__
332393Syz155240 struct file;
342393Syz155240 # endif
352393Syz155240 # include <sys/uio.h>
362393Syz155240 # undef _KERNEL
372393Syz155240 #endif
382393Syz155240 #if defined(_KERNEL) && (__FreeBSD_version >= 220000)
392393Syz155240 # include <sys/filio.h>
402393Syz155240 # include <sys/fcntl.h>
412393Syz155240 #else
422393Syz155240 # include <sys/ioctl.h>
432393Syz155240 #endif
442393Syz155240 #if !defined(AIX)
452393Syz155240 # include <sys/fcntl.h>
462393Syz155240 #endif
472393Syz155240 #if !defined(linux)
482393Syz155240 # include <sys/protosw.h>
492393Syz155240 #endif
502393Syz155240 #include <sys/socket.h>
512393Syz155240 #if defined(_KERNEL)
522393Syz155240 # include <sys/systm.h>
532393Syz155240 # if !defined(__SVR4) && !defined(__svr4__)
542393Syz155240 #  include <sys/mbuf.h>
552393Syz155240 # endif
562393Syz155240 #endif
572393Syz155240 #if defined(__SVR4) || defined(__svr4__)
582393Syz155240 # include <sys/filio.h>
592393Syz155240 # include <sys/byteorder.h>
602393Syz155240 # ifdef _KERNEL
612393Syz155240 #  include <sys/dditypes.h>
622393Syz155240 # endif
632393Syz155240 # include <sys/stream.h>
642393Syz155240 # include <sys/kmem.h>
652393Syz155240 #endif
662393Syz155240 #if __FreeBSD_version >= 300000
672393Syz155240 # include <sys/queue.h>
682393Syz155240 #endif
692393Syz155240 #include <net/if.h>
702393Syz155240 #if __FreeBSD_version >= 300000
712393Syz155240 # include <net/if_var.h>
722393Syz155240 # if defined(_KERNEL) && !defined(IPFILTER_LKM)
732393Syz155240 #  include "opt_ipfilter.h"
742393Syz155240 # endif
752393Syz155240 #endif
762393Syz155240 #ifdef sun
772393Syz155240 # include <net/af.h>
782393Syz155240 #endif
792393Syz155240 #include <net/route.h>
802393Syz155240 #include <netinet/in.h>
812393Syz155240 #include <netinet/in_systm.h>
822393Syz155240 #include <netinet/ip.h>
832393Syz155240 
842393Syz155240 #ifdef RFC1825
852393Syz155240 # include <vpn/md5.h>
862393Syz155240 # include <vpn/ipsec.h>
872393Syz155240 extern struct ifnet vpnif;
882393Syz155240 #endif
892393Syz155240 
902393Syz155240 #if !defined(linux)
912393Syz155240 # include <netinet/ip_var.h>
922393Syz155240 #endif
932393Syz155240 #include <netinet/tcp.h>
942393Syz155240 #include <netinet/udp.h>
952393Syz155240 #include <netinet/ip_icmp.h>
962393Syz155240 #include "netinet/ip_compat.h"
972393Syz155240 #include <netinet/tcpip.h>
982393Syz155240 #include "netinet/ip_fil.h"
992393Syz155240 #include "netinet/ip_nat.h"
1002393Syz155240 #include "netinet/ip_frag.h"
1012393Syz155240 #include "netinet/ip_state.h"
1022393Syz155240 #include "netinet/ip_proxy.h"
1033448Sdh155122 #include "netinet/ipf_stack.h"
1042393Syz155240 #ifdef	IPFILTER_SYNC
1052393Syz155240 #include "netinet/ip_sync.h"
1062393Syz155240 #endif
1072393Syz155240 #if (__FreeBSD_version >= 300000)
1082393Syz155240 # include <sys/malloc.h>
1092393Syz155240 #endif
1102393Syz155240 /* END OF INCLUDES */
1112393Syz155240 
1122393Syz155240 #undef	SOCKADDR_IN
1132393Syz155240 #define	SOCKADDR_IN	struct sockaddr_in
1142393Syz155240 
1152393Syz155240 #if !defined(lint)
1162393Syz155240 static const char sccsid[] = "@(#)ip_nat.c	1.11 6/5/96 (C) 1995 Darren Reed";
1172393Syz155240 static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.42 2005/08/11 19:51:36 darrenr Exp $";
1182393Syz155240 #endif
1192393Syz155240 
1202393Syz155240 
1212393Syz155240 /* ======================================================================== */
1222393Syz155240 /* How the NAT is organised and works.                                      */
1232393Syz155240 /*                                                                          */
1242393Syz155240 /* Inside (interface y) NAT       Outside (interface x)                     */
1252393Syz155240 /* -------------------- -+- -------------------------------------           */
1262393Syz155240 /* Packet going          |   out, processsed by fr_checknatout() for x      */
1272393Syz155240 /* ------------>         |   ------------>                                  */
1282393Syz155240 /* src=10.1.1.1          |   src=192.1.1.1                                  */
1292393Syz155240 /*                       |                                                  */
1302393Syz155240 /*                       |   in, processed by fr_checknatin() for x         */
1312393Syz155240 /* <------------         |   <------------                                  */
1322393Syz155240 /* dst=10.1.1.1          |   dst=192.1.1.1                                  */
1332393Syz155240 /* -------------------- -+- -------------------------------------           */
1342393Syz155240 /* fr_checknatout() - changes ip_src and if required, sport                 */
1352393Syz155240 /*             - creates a new mapping, if required.                        */
1362393Syz155240 /* fr_checknatin()  - changes ip_dst and if required, dport                 */
1372393Syz155240 /*                                                                          */
1382393Syz155240 /* In the NAT table, internal source is recorded as "in" and externally     */
1392393Syz155240 /* seen as "out".                                                           */
1402393Syz155240 /* ======================================================================== */
1412393Syz155240 
1422393Syz155240 
1433448Sdh155122 
1443448Sdh155122 static	int	nat_flushtable __P((ipf_stack_t *));
1453448Sdh155122 static	int	nat_clearlist __P((ipf_stack_t *));
1463448Sdh155122 static	void	nat_addnat __P((struct ipnat *, ipf_stack_t *));
1473448Sdh155122 static	void	nat_addrdr __P((struct ipnat *, ipf_stack_t *));
1483448Sdh155122 static	void	nat_delete __P((struct nat *, int, ipf_stack_t *));
1492393Syz155240 static	void	nat_delrdr __P((struct ipnat *));
1502393Syz155240 static	void	nat_delnat __P((struct ipnat *));
1513448Sdh155122 static	int	fr_natgetent __P((caddr_t, ipf_stack_t *));
1523448Sdh155122 static	int	fr_natgetsz __P((caddr_t, ipf_stack_t *));
1533448Sdh155122 static	int	fr_natputent __P((caddr_t, int, ipf_stack_t *));
1543448Sdh155122 static	void	nat_tabmove __P((nat_t *, ipf_stack_t *));
1552393Syz155240 static	int	nat_match __P((fr_info_t *, ipnat_t *));
1562393Syz155240 static	INLINE	int nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *));
1572393Syz155240 static	INLINE	int nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *));
1582393Syz155240 static	hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr,
1593448Sdh155122 				    struct in_addr, struct in_addr, u_32_t,
1603448Sdh155122 				    ipf_stack_t *));
1612393Syz155240 static	void	nat_hostmapdel __P((struct hostmap *));
1622393Syz155240 static	INLINE	int nat_icmpquerytype4 __P((int));
1633448Sdh155122 static	int	nat_siocaddnat __P((ipnat_t *, ipnat_t **, int,
1643448Sdh155122 				    ipf_stack_t *));
1653448Sdh155122 static	void	nat_siocdelnat __P((ipnat_t *, ipnat_t **, int,
1663448Sdh155122 				    ipf_stack_t *));
1673448Sdh155122 static	INLINE	int nat_icmperrortype4 __P((int));
1682393Syz155240 static	INLINE	int nat_finalise __P((fr_info_t *, nat_t *, natinfo_t *,
1692393Syz155240 				      tcphdr_t *, nat_t **, int));
170*4380Sjojemann static	INLINE	int nat_resolverule __P((ipnat_t *, ipf_stack_t *));
1712393Syz155240 static	nat_t	*fr_natclone __P((fr_info_t *, nat_t *));
1722958Sdr146992 static	void	nat_mssclamp __P((tcphdr_t *, u_32_t, u_short *));
1732393Syz155240 static	INLINE	int nat_wildok __P((nat_t *, int, int, int, int));
1743448Sdh155122 static	int	nat_getnext __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *));
1753448Sdh155122 static	int	nat_iterator __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *));
1762393Syz155240 
1772393Syz155240 
1782393Syz155240 /* ------------------------------------------------------------------------ */
1792393Syz155240 /* Function:    fr_natinit                                                  */
1802393Syz155240 /* Returns:     int - 0 == success, -1 == failure                           */
1812393Syz155240 /* Parameters:  Nil                                                         */
1822393Syz155240 /*                                                                          */
1832393Syz155240 /* Initialise all of the NAT locks, tables and other structures.            */
1842393Syz155240 /* ------------------------------------------------------------------------ */
1853448Sdh155122 int fr_natinit(ifs)
1863448Sdh155122 ipf_stack_t *ifs;
1872393Syz155240 {
1882393Syz155240 	int i;
1892393Syz155240 
1903448Sdh155122 	KMALLOCS(ifs->ifs_nat_table[0], nat_t **,
1913448Sdh155122 		 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz);
1923448Sdh155122 	if (ifs->ifs_nat_table[0] != NULL)
1933448Sdh155122 		bzero((char *)ifs->ifs_nat_table[0],
1943448Sdh155122 		      ifs->ifs_ipf_nattable_sz * sizeof(nat_t *));
1952393Syz155240 	else
1962393Syz155240 		return -1;
1972393Syz155240 
1983448Sdh155122 	KMALLOCS(ifs->ifs_nat_table[1], nat_t **,
1993448Sdh155122 		 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz);
2003448Sdh155122 	if (ifs->ifs_nat_table[1] != NULL)
2013448Sdh155122 		bzero((char *)ifs->ifs_nat_table[1],
2023448Sdh155122 		      ifs->ifs_ipf_nattable_sz * sizeof(nat_t *));
2032393Syz155240 	else
2042393Syz155240 		return -2;
2052393Syz155240 
2063448Sdh155122 	KMALLOCS(ifs->ifs_nat_rules, ipnat_t **,
2073448Sdh155122 		 sizeof(ipnat_t *) * ifs->ifs_ipf_natrules_sz);
2083448Sdh155122 	if (ifs->ifs_nat_rules != NULL)
2093448Sdh155122 		bzero((char *)ifs->ifs_nat_rules,
2103448Sdh155122 		      ifs->ifs_ipf_natrules_sz * sizeof(ipnat_t *));
2112393Syz155240 	else
2122393Syz155240 		return -3;
2132393Syz155240 
2143448Sdh155122 	KMALLOCS(ifs->ifs_rdr_rules, ipnat_t **,
2153448Sdh155122 		 sizeof(ipnat_t *) * ifs->ifs_ipf_rdrrules_sz);
2163448Sdh155122 	if (ifs->ifs_rdr_rules != NULL)
2173448Sdh155122 		bzero((char *)ifs->ifs_rdr_rules,
2183448Sdh155122 		      ifs->ifs_ipf_rdrrules_sz * sizeof(ipnat_t *));
2192393Syz155240 	else
2202393Syz155240 		return -4;
2212393Syz155240 
2223448Sdh155122 	KMALLOCS(ifs->ifs_maptable, hostmap_t **,
2233448Sdh155122 		 sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz);
2243448Sdh155122 	if (ifs->ifs_maptable != NULL)
2253448Sdh155122 		bzero((char *)ifs->ifs_maptable,
2263448Sdh155122 		      sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz);
2272393Syz155240 	else
2282393Syz155240 		return -5;
2292393Syz155240 
2303448Sdh155122 	ifs->ifs_ipf_hm_maplist = NULL;
2313448Sdh155122 
2323448Sdh155122 	KMALLOCS(ifs->ifs_nat_stats.ns_bucketlen[0], u_long *,
2333448Sdh155122 		 ifs->ifs_ipf_nattable_sz * sizeof(u_long));
2343448Sdh155122 	if (ifs->ifs_nat_stats.ns_bucketlen[0] == NULL)
2353448Sdh155122 		return -1;
2363448Sdh155122 	bzero((char *)ifs->ifs_nat_stats.ns_bucketlen[0],
2373448Sdh155122 	      ifs->ifs_ipf_nattable_sz * sizeof(u_long));
2383448Sdh155122 
2393448Sdh155122 	KMALLOCS(ifs->ifs_nat_stats.ns_bucketlen[1], u_long *,
2403448Sdh155122 		 ifs->ifs_ipf_nattable_sz * sizeof(u_long));
2413448Sdh155122 	if (ifs->ifs_nat_stats.ns_bucketlen[1] == NULL)
2423448Sdh155122 		return -1;
2433448Sdh155122 	bzero((char *)ifs->ifs_nat_stats.ns_bucketlen[1],
2443448Sdh155122 	      ifs->ifs_ipf_nattable_sz * sizeof(u_long));
2453448Sdh155122 
2463448Sdh155122 	if (ifs->ifs_fr_nat_maxbucket == 0) {
2473448Sdh155122 		for (i = ifs->ifs_ipf_nattable_sz; i > 0; i >>= 1)
2483448Sdh155122 			ifs->ifs_fr_nat_maxbucket++;
2493448Sdh155122 		ifs->ifs_fr_nat_maxbucket *= 2;
2502393Syz155240 	}
2512393Syz155240 
2523448Sdh155122 	fr_sttab_init(ifs->ifs_nat_tqb, ifs);
2532393Syz155240 	/*
2542393Syz155240 	 * Increase this because we may have "keep state" following this too
2552393Syz155240 	 * and packet storms can occur if this is removed too quickly.
2562393Syz155240 	 */
2573448Sdh155122 	ifs->ifs_nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = ifs->ifs_fr_tcplastack;
2583448Sdh155122 	ifs->ifs_nat_tqb[IPF_TCP_NSTATES - 1].ifq_next = &ifs->ifs_nat_udptq;
2593448Sdh155122 	ifs->ifs_nat_udptq.ifq_ttl = ifs->ifs_fr_defnatage;
2603448Sdh155122 	ifs->ifs_nat_udptq.ifq_ref = 1;
2613448Sdh155122 	ifs->ifs_nat_udptq.ifq_head = NULL;
2623448Sdh155122 	ifs->ifs_nat_udptq.ifq_tail = &ifs->ifs_nat_udptq.ifq_head;
2633448Sdh155122 	MUTEX_INIT(&ifs->ifs_nat_udptq.ifq_lock, "nat ipftq udp tab");
2643448Sdh155122 	ifs->ifs_nat_udptq.ifq_next = &ifs->ifs_nat_icmptq;
2653448Sdh155122 	ifs->ifs_nat_icmptq.ifq_ttl = ifs->ifs_fr_defnaticmpage;
2663448Sdh155122 	ifs->ifs_nat_icmptq.ifq_ref = 1;
2673448Sdh155122 	ifs->ifs_nat_icmptq.ifq_head = NULL;
2683448Sdh155122 	ifs->ifs_nat_icmptq.ifq_tail = &ifs->ifs_nat_icmptq.ifq_head;
2693448Sdh155122 	MUTEX_INIT(&ifs->ifs_nat_icmptq.ifq_lock, "nat icmp ipftq tab");
2703448Sdh155122 	ifs->ifs_nat_icmptq.ifq_next = &ifs->ifs_nat_iptq;
2713448Sdh155122 	ifs->ifs_nat_iptq.ifq_ttl = ifs->ifs_fr_defnatipage;
2723448Sdh155122 	ifs->ifs_nat_iptq.ifq_ref = 1;
2733448Sdh155122 	ifs->ifs_nat_iptq.ifq_head = NULL;
2743448Sdh155122 	ifs->ifs_nat_iptq.ifq_tail = &ifs->ifs_nat_iptq.ifq_head;
2753448Sdh155122 	MUTEX_INIT(&ifs->ifs_nat_iptq.ifq_lock, "nat ip ipftq tab");
2763448Sdh155122 	ifs->ifs_nat_iptq.ifq_next = NULL;
2772393Syz155240 
2782393Syz155240 	for (i = 0; i < IPF_TCP_NSTATES; i++) {
2793448Sdh155122 		if (ifs->ifs_nat_tqb[i].ifq_ttl < ifs->ifs_fr_defnaticmpage)
2803448Sdh155122 			ifs->ifs_nat_tqb[i].ifq_ttl = ifs->ifs_fr_defnaticmpage;
2812393Syz155240 #ifdef LARGE_NAT
2823448Sdh155122 		else if (ifs->ifs_nat_tqb[i].ifq_ttl > ifs->ifs_fr_defnatage)
2833448Sdh155122 			ifs->ifs_nat_tqb[i].ifq_ttl = ifs->ifs_fr_defnatage;
2842393Syz155240 #endif
2852393Syz155240 	}
2862393Syz155240 
2872393Syz155240 	/*
2882393Syz155240 	 * Increase this because we may have "keep state" following
2892393Syz155240 	 * this too and packet storms can occur if this is removed
2902393Syz155240 	 * too quickly.
2912393Syz155240 	 */
2923448Sdh155122 	ifs->ifs_nat_tqb[IPF_TCPS_CLOSED].ifq_ttl =
2933448Sdh155122 	    ifs->ifs_nat_tqb[IPF_TCPS_LAST_ACK].ifq_ttl;
2943448Sdh155122 
2953448Sdh155122 	RWLOCK_INIT(&ifs->ifs_ipf_nat, "ipf IP NAT rwlock");
2963448Sdh155122 	RWLOCK_INIT(&ifs->ifs_ipf_natfrag, "ipf IP NAT-Frag rwlock");
2973448Sdh155122 	MUTEX_INIT(&ifs->ifs_ipf_nat_new, "ipf nat new mutex");
2983448Sdh155122 	MUTEX_INIT(&ifs->ifs_ipf_natio, "ipf nat io mutex");
2993448Sdh155122 
3003448Sdh155122 	ifs->ifs_fr_nat_init = 1;
3012393Syz155240 
3022393Syz155240 	return 0;
3032393Syz155240 }
3042393Syz155240 
3052393Syz155240 
3062393Syz155240 /* ------------------------------------------------------------------------ */
3072393Syz155240 /* Function:    nat_addrdr                                                  */
3082393Syz155240 /* Returns:     Nil                                                         */
3092393Syz155240 /* Parameters:  n(I) - pointer to NAT rule to add                           */
3102393Syz155240 /*                                                                          */
3112393Syz155240 /* Adds a redirect rule to the hash table of redirect rules and the list of */
3122393Syz155240 /* loaded NAT rules.  Updates the bitmask indicating which netmasks are in  */
3132393Syz155240 /* use by redirect rules.                                                   */
3142393Syz155240 /* ------------------------------------------------------------------------ */
3153448Sdh155122 static void nat_addrdr(n, ifs)
3162393Syz155240 ipnat_t *n;
3173448Sdh155122 ipf_stack_t *ifs;
3182393Syz155240 {
3192393Syz155240 	ipnat_t **np;
3202393Syz155240 	u_32_t j;
3212393Syz155240 	u_int hv;
3222393Syz155240 	int k;
3232393Syz155240 
3242393Syz155240 	k = count4bits(n->in_outmsk);
3252393Syz155240 	if ((k >= 0) && (k != 32))
3263448Sdh155122 		ifs->ifs_rdr_masks |= 1 << k;
3272393Syz155240 	j = (n->in_outip & n->in_outmsk);
3283448Sdh155122 	hv = NAT_HASH_FN(j, 0, ifs->ifs_ipf_rdrrules_sz);
3293448Sdh155122 	np = ifs->ifs_rdr_rules + hv;
3302393Syz155240 	while (*np != NULL)
3312393Syz155240 		np = &(*np)->in_rnext;
3322393Syz155240 	n->in_rnext = NULL;
3332393Syz155240 	n->in_prnext = np;
3342393Syz155240 	n->in_hv = hv;
3352393Syz155240 	*np = n;
3362393Syz155240 }
3372393Syz155240 
3382393Syz155240 
3392393Syz155240 /* ------------------------------------------------------------------------ */
3402393Syz155240 /* Function:    nat_addnat                                                  */
3412393Syz155240 /* Returns:     Nil                                                         */
3422393Syz155240 /* Parameters:  n(I) - pointer to NAT rule to add                           */
3432393Syz155240 /*                                                                          */
3442393Syz155240 /* Adds a NAT map rule to the hash table of rules and the list of  loaded   */
3452393Syz155240 /* NAT rules.  Updates the bitmask indicating which netmasks are in use by  */
3462393Syz155240 /* redirect rules.                                                          */
3472393Syz155240 /* ------------------------------------------------------------------------ */
3483448Sdh155122 static void nat_addnat(n, ifs)
3492393Syz155240 ipnat_t *n;
3503448Sdh155122 ipf_stack_t *ifs;
3512393Syz155240 {
3522393Syz155240 	ipnat_t **np;
3532393Syz155240 	u_32_t j;
3542393Syz155240 	u_int hv;
3552393Syz155240 	int k;
3562393Syz155240 
3572393Syz155240 	k = count4bits(n->in_inmsk);
3582393Syz155240 	if ((k >= 0) && (k != 32))
3593448Sdh155122 		ifs->ifs_nat_masks |= 1 << k;
3602393Syz155240 	j = (n->in_inip & n->in_inmsk);
3613448Sdh155122 	hv = NAT_HASH_FN(j, 0, ifs->ifs_ipf_natrules_sz);
3623448Sdh155122 	np = ifs->ifs_nat_rules + hv;
3632393Syz155240 	while (*np != NULL)
3642393Syz155240 		np = &(*np)->in_mnext;
3652393Syz155240 	n->in_mnext = NULL;
3662393Syz155240 	n->in_pmnext = np;
3672393Syz155240 	n->in_hv = hv;
3682393Syz155240 	*np = n;
3692393Syz155240 }
3702393Syz155240 
3712393Syz155240 
3722393Syz155240 /* ------------------------------------------------------------------------ */
3732393Syz155240 /* Function:    nat_delrdr                                                  */
3742393Syz155240 /* Returns:     Nil                                                         */
3752393Syz155240 /* Parameters:  n(I) - pointer to NAT rule to delete                        */
3762393Syz155240 /*                                                                          */
3772393Syz155240 /* Removes a redirect rule from the hash table of redirect rules.           */
3782393Syz155240 /* ------------------------------------------------------------------------ */
3792393Syz155240 static void nat_delrdr(n)
3802393Syz155240 ipnat_t *n;
3812393Syz155240 {
3822393Syz155240 	if (n->in_rnext)
3832393Syz155240 		n->in_rnext->in_prnext = n->in_prnext;
3842393Syz155240 	*n->in_prnext = n->in_rnext;
3852393Syz155240 }
3862393Syz155240 
3872393Syz155240 
3882393Syz155240 /* ------------------------------------------------------------------------ */
3892393Syz155240 /* Function:    nat_delnat                                                  */
3902393Syz155240 /* Returns:     Nil                                                         */
3912393Syz155240 /* Parameters:  n(I) - pointer to NAT rule to delete                        */
3922393Syz155240 /*                                                                          */
3932393Syz155240 /* Removes a NAT map rule from the hash table of NAT map rules.             */
3942393Syz155240 /* ------------------------------------------------------------------------ */
3952393Syz155240 static void nat_delnat(n)
3962393Syz155240 ipnat_t *n;
3972393Syz155240 {
3982393Syz155240 	if (n->in_mnext != NULL)
3992393Syz155240 		n->in_mnext->in_pmnext = n->in_pmnext;
4002393Syz155240 	*n->in_pmnext = n->in_mnext;
4012393Syz155240 }
4022393Syz155240 
4032393Syz155240 
4042393Syz155240 /* ------------------------------------------------------------------------ */
4052393Syz155240 /* Function:    nat_hostmap                                                 */
4062393Syz155240 /* Returns:     struct hostmap* - NULL if no hostmap could be created,      */
4072393Syz155240 /*                                else a pointer to the hostmapping to use  */
4082393Syz155240 /* Parameters:  np(I)   - pointer to NAT rule                               */
4092393Syz155240 /*              real(I) - real IP address                                   */
4102393Syz155240 /*              map(I)  - mapped IP address                                 */
4112393Syz155240 /*              port(I) - destination port number                           */
4122393Syz155240 /* Write Locks: ipf_nat                                                     */
4132393Syz155240 /*                                                                          */
4142393Syz155240 /* Check if an ip address has already been allocated for a given mapping    */
4152393Syz155240 /* that is not doing port based translation.  If is not yet allocated, then */
4162393Syz155240 /* create a new entry if a non-NULL NAT rule pointer has been supplied.     */
4172393Syz155240 /* ------------------------------------------------------------------------ */
4183448Sdh155122 static struct hostmap *nat_hostmap(np, src, dst, map, port, ifs)
4192393Syz155240 ipnat_t *np;
4202393Syz155240 struct in_addr src;
4212393Syz155240 struct in_addr dst;
4222393Syz155240 struct in_addr map;
4232393Syz155240 u_32_t port;
4243448Sdh155122 ipf_stack_t *ifs;
4252393Syz155240 {
4262393Syz155240 	hostmap_t *hm;
4272393Syz155240 	u_int hv;
4282393Syz155240 
4292393Syz155240 	hv = (src.s_addr ^ dst.s_addr);
4302393Syz155240 	hv += src.s_addr;
4312393Syz155240 	hv += dst.s_addr;
4322393Syz155240 	hv %= HOSTMAP_SIZE;
4333448Sdh155122 	for (hm = ifs->ifs_maptable[hv]; hm; hm = hm->hm_next)
4342393Syz155240 		if ((hm->hm_srcip.s_addr == src.s_addr) &&
4352393Syz155240 		    (hm->hm_dstip.s_addr == dst.s_addr) &&
4362393Syz155240 		    ((np == NULL) || (np == hm->hm_ipnat)) &&
4372393Syz155240 		    ((port == 0) || (port == hm->hm_port))) {
4382393Syz155240 			hm->hm_ref++;
4392393Syz155240 			return hm;
4402393Syz155240 		}
4412393Syz155240 
4422393Syz155240 	if (np == NULL)
4432393Syz155240 		return NULL;
4442393Syz155240 
4452393Syz155240 	KMALLOC(hm, hostmap_t *);
4462393Syz155240 	if (hm) {
4473448Sdh155122 		hm->hm_hnext = ifs->ifs_ipf_hm_maplist;
4483448Sdh155122 		hm->hm_phnext = &ifs->ifs_ipf_hm_maplist;
4493448Sdh155122 		if (ifs->ifs_ipf_hm_maplist != NULL)
4503448Sdh155122 			ifs->ifs_ipf_hm_maplist->hm_phnext = &hm->hm_hnext;
4513448Sdh155122 		ifs->ifs_ipf_hm_maplist = hm;
4523448Sdh155122 
4533448Sdh155122 		hm->hm_next = ifs->ifs_maptable[hv];
4543448Sdh155122 		hm->hm_pnext = ifs->ifs_maptable + hv;
4553448Sdh155122 		if (ifs->ifs_maptable[hv] != NULL)
4563448Sdh155122 			ifs->ifs_maptable[hv]->hm_pnext = &hm->hm_next;
4573448Sdh155122 		ifs->ifs_maptable[hv] = hm;
4582393Syz155240 		hm->hm_ipnat = np;
4592393Syz155240 		hm->hm_srcip = src;
4602393Syz155240 		hm->hm_dstip = dst;
4612393Syz155240 		hm->hm_mapip = map;
4622393Syz155240 		hm->hm_ref = 1;
4632393Syz155240 		hm->hm_port = port;
4642393Syz155240 	}
4652393Syz155240 	return hm;
4662393Syz155240 }
4672393Syz155240 
4682393Syz155240 
4692393Syz155240 /* ------------------------------------------------------------------------ */
4702393Syz155240 /* Function:    nat_hostmapdel                                              */
4712393Syz155240 /* Returns:     Nil                                                         */
4722393Syz155240 /* Parameters:  hm(I) - pointer to hostmap structure                        */
4732393Syz155240 /* Write Locks: ipf_nat                                                     */
4742393Syz155240 /*                                                                          */
4752393Syz155240 /* Decrement the references to this hostmap structure by one.  If this      */
4762393Syz155240 /* reaches zero then remove it and free it.                                 */
4772393Syz155240 /* ------------------------------------------------------------------------ */
4782393Syz155240 static void nat_hostmapdel(hm)
4792393Syz155240 struct hostmap *hm;
4802393Syz155240 {
4812393Syz155240 	hm->hm_ref--;
4822393Syz155240 	if (hm->hm_ref == 0) {
4832393Syz155240 		if (hm->hm_next)
4842393Syz155240 			hm->hm_next->hm_pnext = hm->hm_pnext;
4852393Syz155240 		*hm->hm_pnext = hm->hm_next;
4863448Sdh155122 		if (hm->hm_hnext)
4873448Sdh155122 			hm->hm_hnext->hm_phnext = hm->hm_phnext;
4883448Sdh155122 		*hm->hm_phnext = hm->hm_hnext;
4892393Syz155240 		KFREE(hm);
4902393Syz155240 	}
4912393Syz155240 }
4922393Syz155240 
4933448Sdh155122 void fr_hostmapderef(hmp)
4943448Sdh155122 struct hostmap **hmp;
4953448Sdh155122 {
4963448Sdh155122 	struct hostmap *hm;
4973448Sdh155122 
4983448Sdh155122 	hm = *hmp;
4993448Sdh155122 	*hmp = NULL;
5003448Sdh155122 	hm->hm_ref--;
5013448Sdh155122 	if (hm->hm_ref == 0)
5023448Sdh155122 		nat_hostmapdel(hm);
5033448Sdh155122 }
5043448Sdh155122 
5052393Syz155240 
5062393Syz155240 /* ------------------------------------------------------------------------ */
5072393Syz155240 /* Function:    fix_outcksum                                                */
5082393Syz155240 /* Returns:     Nil                                                         */
5092958Sdr146992 /* Parameters:  sp(I)  - location of 16bit checksum to update               */
5102393Syz155240 /*              n((I)  - amount to adjust checksum by                       */
5112393Syz155240 /*                                                                          */
5122393Syz155240 /* Adjusts the 16bit checksum by "n" for packets going out.                 */
5132393Syz155240 /* ------------------------------------------------------------------------ */
5142958Sdr146992 void fix_outcksum(sp, n)
5152393Syz155240 u_short *sp;
5162393Syz155240 u_32_t n;
5172393Syz155240 {
5182393Syz155240 	u_short sumshort;
5192393Syz155240 	u_32_t sum1;
5202393Syz155240 
5212393Syz155240 	if (n == 0)
5222393Syz155240 		return;
5232393Syz155240 
5242393Syz155240 	sum1 = (~ntohs(*sp)) & 0xffff;
5252393Syz155240 	sum1 += (n);
5262393Syz155240 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
5272393Syz155240 	/* Again */
5282393Syz155240 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
5292393Syz155240 	sumshort = ~(u_short)sum1;
5302393Syz155240 	*(sp) = htons(sumshort);
5312393Syz155240 }
5322393Syz155240 
5332393Syz155240 
5342393Syz155240 /* ------------------------------------------------------------------------ */
5352393Syz155240 /* Function:    fix_incksum                                                 */
5362393Syz155240 /* Returns:     Nil                                                         */
5372958Sdr146992 /* Parameters:  sp(I)  - location of 16bit checksum to update               */
5382393Syz155240 /*              n((I)  - amount to adjust checksum by                       */
5392393Syz155240 /*                                                                          */
5402393Syz155240 /* Adjusts the 16bit checksum by "n" for packets going in.                  */
5412393Syz155240 /* ------------------------------------------------------------------------ */
5422958Sdr146992 void fix_incksum(sp, n)
5432393Syz155240 u_short *sp;
5442393Syz155240 u_32_t n;
5452393Syz155240 {
5462393Syz155240 	u_short sumshort;
5472393Syz155240 	u_32_t sum1;
5482393Syz155240 
5492393Syz155240 	if (n == 0)
5502393Syz155240 		return;
5512393Syz155240 
5522393Syz155240 	sum1 = (~ntohs(*sp)) & 0xffff;
5532393Syz155240 	sum1 += ~(n) & 0xffff;
5542393Syz155240 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
5552393Syz155240 	/* Again */
5562393Syz155240 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
5572393Syz155240 	sumshort = ~(u_short)sum1;
5582393Syz155240 	*(sp) = htons(sumshort);
5592393Syz155240 }
5602393Syz155240 
5612393Syz155240 
5622393Syz155240 /* ------------------------------------------------------------------------ */
5632393Syz155240 /* Function:    fix_datacksum                                               */
5642393Syz155240 /* Returns:     Nil                                                         */
5652393Syz155240 /* Parameters:  sp(I)  - location of 16bit checksum to update               */
5662393Syz155240 /*              n((I)  - amount to adjust checksum by                       */
5672393Syz155240 /*                                                                          */
5682393Syz155240 /* Fix_datacksum is used *only* for the adjustments of checksums in the     */
5692393Syz155240 /* data section of an IP packet.                                            */
5702393Syz155240 /*                                                                          */
5712393Syz155240 /* The only situation in which you need to do this is when NAT'ing an       */
5722393Syz155240 /* ICMP error message. Such a message, contains in its body the IP header   */
5732393Syz155240 /* of the original IP packet, that causes the error.                        */
5742393Syz155240 /*                                                                          */
5752393Syz155240 /* You can't use fix_incksum or fix_outcksum in that case, because for the  */
5762393Syz155240 /* kernel the data section of the ICMP error is just data, and no special   */
5772393Syz155240 /* processing like hardware cksum or ntohs processing have been done by the */
5782393Syz155240 /* kernel on the data section.                                              */
5792393Syz155240 /* ------------------------------------------------------------------------ */
5802393Syz155240 void fix_datacksum(sp, n)
5812393Syz155240 u_short *sp;
5822393Syz155240 u_32_t n;
5832393Syz155240 {
5842393Syz155240 	u_short sumshort;
5852393Syz155240 	u_32_t sum1;
5862393Syz155240 
5872393Syz155240 	if (n == 0)
5882393Syz155240 		return;
5892393Syz155240 
5902393Syz155240 	sum1 = (~ntohs(*sp)) & 0xffff;
5912393Syz155240 	sum1 += (n);
5922393Syz155240 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
5932393Syz155240 	/* Again */
5942393Syz155240 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
5952393Syz155240 	sumshort = ~(u_short)sum1;
5962393Syz155240 	*(sp) = htons(sumshort);
5972393Syz155240 }
5982393Syz155240 
5992393Syz155240 
6002393Syz155240 /* ------------------------------------------------------------------------ */
6012393Syz155240 /* Function:    fr_nat_ioctl                                                */
6022393Syz155240 /* Returns:     int - 0 == success, != 0 == failure                         */
6032393Syz155240 /* Parameters:  data(I) - pointer to ioctl data                             */
6042393Syz155240 /*              cmd(I)  - ioctl command integer                             */
6052393Syz155240 /*              mode(I) - file mode bits used with open                     */
6062393Syz155240 /*                                                                          */
6072393Syz155240 /* Processes an ioctl call made to operate on the IP Filter NAT device.     */
6082393Syz155240 /* ------------------------------------------------------------------------ */
6093448Sdh155122 int fr_nat_ioctl(data, cmd, mode, uid, ctx, ifs)
6102393Syz155240 ioctlcmd_t cmd;
6112393Syz155240 caddr_t data;
6123448Sdh155122 int mode, uid;
6133448Sdh155122 void *ctx;
6143448Sdh155122 ipf_stack_t *ifs;
6152393Syz155240 {
6162393Syz155240 	ipnat_t *nat, *nt, *n = NULL, **np = NULL;
6172393Syz155240 	int error = 0, ret, arg, getlock;
6182393Syz155240 	ipnat_t natd;
6192393Syz155240 
6202393Syz155240 #if (BSD >= 199306) && defined(_KERNEL)
6212393Syz155240 	if ((securelevel >= 2) && (mode & FWRITE))
6222393Syz155240 		return EPERM;
6232393Syz155240 #endif
6242393Syz155240 
6252393Syz155240 #if defined(__osf__) && defined(_KERNEL)
6262393Syz155240 	getlock = 0;
6272393Syz155240 #else
6282393Syz155240 	getlock = (mode & NAT_LOCKHELD) ? 0 : 1;
6292393Syz155240 #endif
6302393Syz155240 
6312393Syz155240 	nat = NULL;     /* XXX gcc -Wuninitialized */
6322393Syz155240 	if (cmd == (ioctlcmd_t)SIOCADNAT) {
6332393Syz155240 		KMALLOC(nt, ipnat_t *);
6342393Syz155240 	} else {
6352393Syz155240 		nt = NULL;
6362393Syz155240 	}
6372393Syz155240 
6382393Syz155240 	if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) {
6392393Syz155240 		if (mode & NAT_SYSSPACE) {
6402393Syz155240 			bcopy(data, (char *)&natd, sizeof(natd));
6412393Syz155240 			error = 0;
6422393Syz155240 		} else {
6432393Syz155240 			error = fr_inobj(data, &natd, IPFOBJ_IPNAT);
6442393Syz155240 		}
6452393Syz155240 
6462393Syz155240 	} else if (cmd == (ioctlcmd_t)SIOCIPFFL) { /* SIOCFLNAT & SIOCCNATL */
6472393Syz155240 		BCOPYIN(data, &arg, sizeof(arg));
6482393Syz155240 	}
6492393Syz155240 
6502393Syz155240 	if (error != 0)
6512393Syz155240 		goto done;
6522393Syz155240 
6532393Syz155240 	/*
6542393Syz155240 	 * For add/delete, look to see if the NAT entry is already present
6552393Syz155240 	 */
6562393Syz155240 	if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) {
6572393Syz155240 		nat = &natd;
6582393Syz155240 		if (nat->in_v == 0)	/* For backward compat. */
6592393Syz155240 			nat->in_v = 4;
6602393Syz155240 		nat->in_flags &= IPN_USERFLAGS;
6612393Syz155240 		if ((nat->in_redir & NAT_MAPBLK) == 0) {
6622393Syz155240 			if ((nat->in_flags & IPN_SPLIT) == 0)
6632393Syz155240 				nat->in_inip &= nat->in_inmsk;
6642393Syz155240 			if ((nat->in_flags & IPN_IPRANGE) == 0)
6652393Syz155240 				nat->in_outip &= nat->in_outmsk;
6662393Syz155240 		}
6673448Sdh155122 		MUTEX_ENTER(&ifs->ifs_ipf_natio);
6683448Sdh155122 		for (np = &ifs->ifs_nat_list; ((n = *np) != NULL);
6693448Sdh155122 		     np = &n->in_next)
6702393Syz155240 			if (!bcmp((char *)&nat->in_flags, (char *)&n->in_flags,
6712393Syz155240 					IPN_CMPSIZ))
6722393Syz155240 				break;
6732393Syz155240 	}
6742393Syz155240 
6752393Syz155240 	switch (cmd)
6762393Syz155240 	{
6773448Sdh155122 	case SIOCGENITER :
6783448Sdh155122 	    {
6793448Sdh155122 		ipfgeniter_t iter;
6803448Sdh155122 		ipftoken_t *token;
6813448Sdh155122 
6823448Sdh155122 		error = fr_inobj(data, &iter, IPFOBJ_GENITER);
6833448Sdh155122 		if (error != 0)
6843448Sdh155122 			break;
6853448Sdh155122 
6863448Sdh155122 		token = ipf_findtoken(iter.igi_type, uid, ctx, ifs);
6873448Sdh155122 		if (token != NULL)
6883448Sdh155122 			error  = nat_iterator(token, &iter, ifs);
6893448Sdh155122 		else
6903448Sdh155122 			error = ESRCH;
6913448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_tokens);
6923448Sdh155122 		break;
6933448Sdh155122 	    }
6942393Syz155240 #ifdef  IPFILTER_LOG
6952393Syz155240 	case SIOCIPFFB :
6962393Syz155240 	{
6972393Syz155240 		int tmp;
6982393Syz155240 
6992393Syz155240 		if (!(mode & FWRITE))
7002393Syz155240 			error = EPERM;
7012393Syz155240 		else {
7023448Sdh155122 			tmp = ipflog_clear(IPL_LOGNAT, ifs);
7032393Syz155240 			BCOPYOUT((char *)&tmp, (char *)data, sizeof(tmp));
7042393Syz155240 		}
7052393Syz155240 		break;
7062393Syz155240 	}
7072393Syz155240 	case SIOCSETLG :
7082393Syz155240 		if (!(mode & FWRITE))
7092393Syz155240 			error = EPERM;
7102393Syz155240 		else {
7113448Sdh155122 			BCOPYIN((char *)data,
7123448Sdh155122 				       (char *)&ifs->ifs_nat_logging,
7133448Sdh155122 				sizeof(ifs->ifs_nat_logging));
7142393Syz155240 		}
7152393Syz155240 		break;
7162393Syz155240 	case SIOCGETLG :
7173448Sdh155122 		BCOPYOUT((char *)&ifs->ifs_nat_logging, (char *)data,
7183448Sdh155122 			sizeof(ifs->ifs_nat_logging));
7192393Syz155240 		break;
7202393Syz155240 	case FIONREAD :
7213448Sdh155122 		arg = ifs->ifs_iplused[IPL_LOGNAT];
7222393Syz155240 		BCOPYOUT(&arg, data, sizeof(arg));
7232393Syz155240 		break;
7242393Syz155240 #endif
7252393Syz155240 	case SIOCADNAT :
7262393Syz155240 		if (!(mode & FWRITE)) {
7272393Syz155240 			error = EPERM;
7282393Syz155240 		} else if (n != NULL) {
7292393Syz155240 			error = EEXIST;
7302393Syz155240 		} else if (nt == NULL) {
7312393Syz155240 			error = ENOMEM;
7322393Syz155240 		}
7332393Syz155240 		if (error != 0) {
7343448Sdh155122 			MUTEX_EXIT(&ifs->ifs_ipf_natio);
7352393Syz155240 			break;
7362393Syz155240 		}
7372393Syz155240 		bcopy((char *)nat, (char *)nt, sizeof(*n));
7383448Sdh155122 		error = nat_siocaddnat(nt, np, getlock, ifs);
7393448Sdh155122 		MUTEX_EXIT(&ifs->ifs_ipf_natio);
7402393Syz155240 		if (error == 0)
7412393Syz155240 			nt = NULL;
7422393Syz155240 		break;
7432393Syz155240 	case SIOCRMNAT :
7442393Syz155240 		if (!(mode & FWRITE)) {
7452393Syz155240 			error = EPERM;
7462393Syz155240 			n = NULL;
7472393Syz155240 		} else if (n == NULL) {
7482393Syz155240 			error = ESRCH;
7492393Syz155240 		}
7502393Syz155240 
7512393Syz155240 		if (error != 0) {
7523448Sdh155122 			MUTEX_EXIT(&ifs->ifs_ipf_natio);
7532393Syz155240 			break;
7542393Syz155240 		}
7553448Sdh155122 		nat_siocdelnat(n, np, getlock, ifs);
7563448Sdh155122 
7573448Sdh155122 		MUTEX_EXIT(&ifs->ifs_ipf_natio);
7582393Syz155240 		n = NULL;
7592393Syz155240 		break;
7602393Syz155240 	case SIOCGNATS :
7613448Sdh155122 		ifs->ifs_nat_stats.ns_table[0] = ifs->ifs_nat_table[0];
7623448Sdh155122 		ifs->ifs_nat_stats.ns_table[1] = ifs->ifs_nat_table[1];
7633448Sdh155122 		ifs->ifs_nat_stats.ns_list = ifs->ifs_nat_list;
7643448Sdh155122 		ifs->ifs_nat_stats.ns_maptable = ifs->ifs_maptable;
7653448Sdh155122 		ifs->ifs_nat_stats.ns_maplist = ifs->ifs_ipf_hm_maplist;
7663448Sdh155122 		ifs->ifs_nat_stats.ns_nattab_max = ifs->ifs_ipf_nattable_max;
7673448Sdh155122 		ifs->ifs_nat_stats.ns_nattab_sz = ifs->ifs_ipf_nattable_sz;
7683448Sdh155122 		ifs->ifs_nat_stats.ns_rultab_sz = ifs->ifs_ipf_natrules_sz;
7693448Sdh155122 		ifs->ifs_nat_stats.ns_rdrtab_sz = ifs->ifs_ipf_rdrrules_sz;
7703448Sdh155122 		ifs->ifs_nat_stats.ns_hostmap_sz = ifs->ifs_ipf_hostmap_sz;
7713448Sdh155122 		ifs->ifs_nat_stats.ns_instances = ifs->ifs_nat_instances;
7723448Sdh155122 		ifs->ifs_nat_stats.ns_apslist = ifs->ifs_ap_sess_list;
7733448Sdh155122 		error = fr_outobj(data, &ifs->ifs_nat_stats, IPFOBJ_NATSTAT);
7742393Syz155240 		break;
7752393Syz155240 	case SIOCGNATL :
7762393Syz155240 	    {
7772393Syz155240 		natlookup_t nl;
7782393Syz155240 
7792393Syz155240 		if (getlock) {
7803448Sdh155122 			READ_ENTER(&ifs->ifs_ipf_nat);
7812393Syz155240 		}
7822393Syz155240 		error = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP);
7832393Syz155240 		if (error == 0) {
7843448Sdh155122 			if (nat_lookupredir(&nl, ifs) != NULL) {
7852393Syz155240 				error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP);
7862393Syz155240 			} else {
7872393Syz155240 				error = ESRCH;
7882393Syz155240 			}
7892393Syz155240 		}
7902393Syz155240 		if (getlock) {
7913448Sdh155122 			RWLOCK_EXIT(&ifs->ifs_ipf_nat);
7922393Syz155240 		}
7932393Syz155240 		break;
7942393Syz155240 	    }
7952393Syz155240 	case SIOCIPFFL :	/* old SIOCFLNAT & SIOCCNATL */
7962393Syz155240 		if (!(mode & FWRITE)) {
7972393Syz155240 			error = EPERM;
7982393Syz155240 			break;
7992393Syz155240 		}
8002393Syz155240 		if (getlock) {
8013448Sdh155122 			WRITE_ENTER(&ifs->ifs_ipf_nat);
8022393Syz155240 		}
8032393Syz155240 		error = 0;
8042393Syz155240 		if (arg == 0)
8053448Sdh155122 			ret = nat_flushtable(ifs);
8062393Syz155240 		else if (arg == 1)
8073448Sdh155122 			ret = nat_clearlist(ifs);
8082393Syz155240 		else
8092393Syz155240 			error = EINVAL;
8102393Syz155240 		if (getlock) {
8113448Sdh155122 			RWLOCK_EXIT(&ifs->ifs_ipf_nat);
8122393Syz155240 		}
8132393Syz155240 		if (error == 0) {
8142393Syz155240 			BCOPYOUT(&ret, data, sizeof(ret));
8152393Syz155240 		}
8162393Syz155240 		break;
8172393Syz155240 	case SIOCPROXY :
8183448Sdh155122 		error = appr_ioctl(data, cmd, mode, ifs);
8192393Syz155240 		break;
8202393Syz155240 	case SIOCSTLCK :
8212393Syz155240 		if (!(mode & FWRITE)) {
8222393Syz155240 			error = EPERM;
8232393Syz155240 		} else {
8243448Sdh155122 			fr_lock(data, &ifs->ifs_fr_nat_lock);
8252393Syz155240 		}
8262393Syz155240 		break;
8272393Syz155240 	case SIOCSTPUT :
8282761Sjojemann 		if ((mode & FWRITE) != 0) {
8293448Sdh155122 			error = fr_natputent(data, getlock, ifs);
8302393Syz155240 		} else {
8312393Syz155240 			error = EACCES;
8322393Syz155240 		}
8332393Syz155240 		break;
8342393Syz155240 	case SIOCSTGSZ :
8353448Sdh155122 		if (ifs->ifs_fr_nat_lock) {
8362393Syz155240 			if (getlock) {
8373448Sdh155122 				READ_ENTER(&ifs->ifs_ipf_nat);
8382393Syz155240 			}
8393448Sdh155122 			error = fr_natgetsz(data, ifs);
8402393Syz155240 			if (getlock) {
8413448Sdh155122 				RWLOCK_EXIT(&ifs->ifs_ipf_nat);
8422393Syz155240 			}
8432393Syz155240 		} else
8442393Syz155240 			error = EACCES;
8452393Syz155240 		break;
8462393Syz155240 	case SIOCSTGET :
8473448Sdh155122 		if (ifs->ifs_fr_nat_lock) {
8482393Syz155240 			if (getlock) {
8493448Sdh155122 				READ_ENTER(&ifs->ifs_ipf_nat);
8502393Syz155240 			}
8513448Sdh155122 			error = fr_natgetent(data, ifs);
8522393Syz155240 			if (getlock) {
8533448Sdh155122 				RWLOCK_EXIT(&ifs->ifs_ipf_nat);
8542393Syz155240 			}
8552393Syz155240 		} else
8562393Syz155240 			error = EACCES;
8572393Syz155240 		break;
8583448Sdh155122 	case SIOCIPFDELTOK :
8593448Sdh155122 		(void) BCOPYIN((caddr_t)data, (caddr_t)&arg, sizeof(arg));
8603448Sdh155122 		error = ipf_deltoken(arg, uid, ctx, ifs);
8613448Sdh155122 		break;
8622393Syz155240 	default :
8632393Syz155240 		error = EINVAL;
8642393Syz155240 		break;
8652393Syz155240 	}
8662393Syz155240 done:
8672393Syz155240 	if (nt)
8682393Syz155240 		KFREE(nt);
8692393Syz155240 	return error;
8702393Syz155240 }
8712393Syz155240 
8722393Syz155240 
8732393Syz155240 /* ------------------------------------------------------------------------ */
8742393Syz155240 /* Function:    nat_siocaddnat                                              */
8752393Syz155240 /* Returns:     int - 0 == success, != 0 == failure                         */
8762393Syz155240 /* Parameters:  n(I)       - pointer to new NAT rule                        */
8772393Syz155240 /*              np(I)      - pointer to where to insert new NAT rule        */
8782393Syz155240 /*              getlock(I) - flag indicating if lock on ipf_nat is held     */
8792393Syz155240 /* Mutex Locks: ipf_natio                                                   */
8802393Syz155240 /*                                                                          */
8812393Syz155240 /* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
8822393Syz155240 /* from information passed to the kernel, then add it  to the appropriate   */
8832393Syz155240 /* NAT rule table(s).                                                       */
8842393Syz155240 /* ------------------------------------------------------------------------ */
8853448Sdh155122 static int nat_siocaddnat(n, np, getlock, ifs)
8862393Syz155240 ipnat_t *n, **np;
8872393Syz155240 int getlock;
8883448Sdh155122 ipf_stack_t *ifs;
8892393Syz155240 {
8902393Syz155240 	int error = 0, i, j;
8912393Syz155240 
892*4380Sjojemann 	if (nat_resolverule(n, ifs) != 0)
893*4380Sjojemann 		return ENOENT;
8942393Syz155240 
8952393Syz155240 	if ((n->in_age[0] == 0) && (n->in_age[1] != 0))
8962393Syz155240 		return EINVAL;
8972393Syz155240 
8982393Syz155240 	n->in_use = 0;
8992393Syz155240 	if (n->in_redir & NAT_MAPBLK)
9002393Syz155240 		n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk);
9012393Syz155240 	else if (n->in_flags & IPN_AUTOPORTMAP)
9022393Syz155240 		n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk);
9032393Syz155240 	else if (n->in_flags & IPN_IPRANGE)
9042393Syz155240 		n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip);
9052393Syz155240 	else if (n->in_flags & IPN_SPLIT)
9062393Syz155240 		n->in_space = 2;
9072393Syz155240 	else if (n->in_outmsk != 0)
9082393Syz155240 		n->in_space = ~ntohl(n->in_outmsk);
9092393Syz155240 	else
9102393Syz155240 		n->in_space = 1;
9112393Syz155240 
9122393Syz155240 	/*
9132393Syz155240 	 * Calculate the number of valid IP addresses in the output
9142393Syz155240 	 * mapping range.  In all cases, the range is inclusive of
9152393Syz155240 	 * the start and ending IP addresses.
9162393Syz155240 	 * If to a CIDR address, lose 2: broadcast + network address
9172393Syz155240 	 *                               (so subtract 1)
9182393Syz155240 	 * If to a range, add one.
9192393Syz155240 	 * If to a single IP address, set to 1.
9202393Syz155240 	 */
9212393Syz155240 	if (n->in_space) {
9222393Syz155240 		if ((n->in_flags & IPN_IPRANGE) != 0)
9232393Syz155240 			n->in_space += 1;
9242393Syz155240 		else
9252393Syz155240 			n->in_space -= 1;
9262393Syz155240 	} else
9272393Syz155240 		n->in_space = 1;
9282393Syz155240 
9292393Syz155240 	if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) &&
9302393Syz155240 	    ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0))
9312393Syz155240 		n->in_nip = ntohl(n->in_outip) + 1;
9322393Syz155240 	else if ((n->in_flags & IPN_SPLIT) &&
9332393Syz155240 		 (n->in_redir & NAT_REDIRECT))
9342393Syz155240 		n->in_nip = ntohl(n->in_inip);
9352393Syz155240 	else
9362393Syz155240 		n->in_nip = ntohl(n->in_outip);
9372393Syz155240 	if (n->in_redir & NAT_MAP) {
9382393Syz155240 		n->in_pnext = ntohs(n->in_pmin);
9392393Syz155240 		/*
9402393Syz155240 		 * Multiply by the number of ports made available.
9412393Syz155240 		 */
9422393Syz155240 		if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) {
9432393Syz155240 			n->in_space *= (ntohs(n->in_pmax) -
9442393Syz155240 					ntohs(n->in_pmin) + 1);
9452393Syz155240 			/*
9462393Syz155240 			 * Because two different sources can map to
9472393Syz155240 			 * different destinations but use the same
9482393Syz155240 			 * local IP#/port #.
9492393Syz155240 			 * If the result is smaller than in_space, then
9502393Syz155240 			 * we may have wrapped around 32bits.
9512393Syz155240 			 */
9522393Syz155240 			i = n->in_inmsk;
9532393Syz155240 			if ((i != 0) && (i != 0xffffffff)) {
9542393Syz155240 				j = n->in_space * (~ntohl(i) + 1);
9552393Syz155240 				if (j >= n->in_space)
9562393Syz155240 					n->in_space = j;
9572393Syz155240 				else
9582393Syz155240 					n->in_space = 0xffffffff;
9592393Syz155240 			}
9602393Syz155240 		}
9612393Syz155240 		/*
9622393Syz155240 		 * If no protocol is specified, multiple by 256 to allow for
9632393Syz155240 		 * at least one IP:IP mapping per protocol.
9642393Syz155240 		 */
9652393Syz155240 		if ((n->in_flags & IPN_TCPUDPICMP) == 0) {
9662393Syz155240 				j = n->in_space * 256;
9672393Syz155240 				if (j >= n->in_space)
9682393Syz155240 					n->in_space = j;
9692393Syz155240 				else
9702393Syz155240 					n->in_space = 0xffffffff;
9712393Syz155240 		}
9722393Syz155240 	}
9732393Syz155240 
9742393Syz155240 	/* Otherwise, these fields are preset */
9752393Syz155240 
9762393Syz155240 	if (getlock) {
9773448Sdh155122 		WRITE_ENTER(&ifs->ifs_ipf_nat);
9782393Syz155240 	}
9792393Syz155240 	n->in_next = NULL;
9802393Syz155240 	*np = n;
9812393Syz155240 
9822393Syz155240 	if (n->in_age[0] != 0)
9833448Sdh155122 	    n->in_tqehead[0] = fr_addtimeoutqueue(&ifs->ifs_nat_utqe,
9843448Sdh155122 						  n->in_age[0], ifs);
9852393Syz155240 
9862393Syz155240 	if (n->in_age[1] != 0)
9873448Sdh155122 	    n->in_tqehead[1] = fr_addtimeoutqueue(&ifs->ifs_nat_utqe,
9883448Sdh155122 						  n->in_age[1], ifs);
9892393Syz155240 
9902393Syz155240 	if (n->in_redir & NAT_REDIRECT) {
9912393Syz155240 		n->in_flags &= ~IPN_NOTDST;
9923448Sdh155122 		nat_addrdr(n, ifs);
9932393Syz155240 	}
9942393Syz155240 	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
9952393Syz155240 		n->in_flags &= ~IPN_NOTSRC;
9963448Sdh155122 		nat_addnat(n, ifs);
9972393Syz155240 	}
9982393Syz155240 	n = NULL;
9993448Sdh155122 	ifs->ifs_nat_stats.ns_rules++;
10002393Syz155240 	if (getlock) {
10013448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);			/* WRITE */
10022393Syz155240 	}
10032393Syz155240 
10042393Syz155240 	return error;
10052393Syz155240 }
10062393Syz155240 
10072393Syz155240 
10082393Syz155240 /* ------------------------------------------------------------------------ */
10092393Syz155240 /* Function:    nat_resolvrule                                              */
1010*4380Sjojemann /* Returns:     int - 0 == success, -1 == failure                           */
10112393Syz155240 /* Parameters:  n(I)  - pointer to NAT rule                                 */
10122393Syz155240 /*                                                                          */
1013*4380Sjojemann /* Resolve some of the details inside the NAT rule.  Includes resolving	    */
1014*4380Sjojemann /* any specified interfaces and proxy labels, and determines whether or not */
1015*4380Sjojemann /* all proxy labels are correctly specified.				    */
1016*4380Sjojemann /*									    */
1017*4380Sjojemann /* Called by nat_siocaddnat() (SIOCADNAT) and fr_natputent (SIOCSTPUT).     */
10182393Syz155240 /* ------------------------------------------------------------------------ */
1019*4380Sjojemann static int nat_resolverule(n, ifs)
10202393Syz155240 ipnat_t *n;
10213448Sdh155122 ipf_stack_t *ifs;
10222393Syz155240 {
10232393Syz155240 	n->in_ifnames[0][LIFNAMSIZ - 1] = '\0';
10243448Sdh155122 	n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], 4, ifs);
10252393Syz155240 
10262393Syz155240 	n->in_ifnames[1][LIFNAMSIZ - 1] = '\0';
10272393Syz155240 	if (n->in_ifnames[1][0] == '\0') {
10282393Syz155240 		(void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ);
10292393Syz155240 		n->in_ifps[1] = n->in_ifps[0];
10302393Syz155240 	} else {
10313894Sjojemann 		n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], 4, ifs);
10322393Syz155240 	}
10332393Syz155240 
10342393Syz155240 	if (n->in_plabel[0] != '\0') {
10353448Sdh155122 		n->in_apr = appr_lookup(n->in_p, n->in_plabel, ifs);
1036*4380Sjojemann 		if (n->in_apr == NULL)
1037*4380Sjojemann 			return -1;
10382393Syz155240 	}
1039*4380Sjojemann 	return 0;
10402393Syz155240 }
10412393Syz155240 
10422393Syz155240 
10432393Syz155240 /* ------------------------------------------------------------------------ */
10442393Syz155240 /* Function:    nat_siocdelnat                                              */
10452393Syz155240 /* Returns:     int - 0 == success, != 0 == failure                         */
10462393Syz155240 /* Parameters:  n(I)       - pointer to new NAT rule                        */
10472393Syz155240 /*              np(I)      - pointer to where to insert new NAT rule        */
10482393Syz155240 /*              getlock(I) - flag indicating if lock on ipf_nat is held     */
10492393Syz155240 /* Mutex Locks: ipf_natio                                                   */
10502393Syz155240 /*                                                                          */
10512393Syz155240 /* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
10522393Syz155240 /* from information passed to the kernel, then add it  to the appropriate   */
10532393Syz155240 /* NAT rule table(s).                                                       */
10542393Syz155240 /* ------------------------------------------------------------------------ */
10553448Sdh155122 static void nat_siocdelnat(n, np, getlock, ifs)
10562393Syz155240 ipnat_t *n, **np;
10572393Syz155240 int getlock;
10583448Sdh155122 ipf_stack_t *ifs;
10592393Syz155240 {
10602393Syz155240 	if (getlock) {
10613448Sdh155122 		WRITE_ENTER(&ifs->ifs_ipf_nat);
10622393Syz155240 	}
10632393Syz155240 	if (n->in_redir & NAT_REDIRECT)
10642393Syz155240 		nat_delrdr(n);
10652393Syz155240 	if (n->in_redir & (NAT_MAPBLK|NAT_MAP))
10662393Syz155240 		nat_delnat(n);
10673448Sdh155122 	if (ifs->ifs_nat_list == NULL) {
10683448Sdh155122 		ifs->ifs_nat_masks = 0;
10693448Sdh155122 		ifs->ifs_rdr_masks = 0;
10702393Syz155240 	}
10712393Syz155240 
10722393Syz155240 	if (n->in_tqehead[0] != NULL) {
10732393Syz155240 		if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) {
10743448Sdh155122 			fr_freetimeoutqueue(n->in_tqehead[1], ifs);
10752393Syz155240 		}
10762393Syz155240 	}
10772393Syz155240 
10782393Syz155240 	if (n->in_tqehead[1] != NULL) {
10792393Syz155240 		if (fr_deletetimeoutqueue(n->in_tqehead[1]) == 0) {
10803448Sdh155122 			fr_freetimeoutqueue(n->in_tqehead[1], ifs);
10812393Syz155240 		}
10822393Syz155240 	}
10832393Syz155240 
10842393Syz155240 	*np = n->in_next;
10852393Syz155240 
10862393Syz155240 	if (n->in_use == 0) {
10872393Syz155240 		if (n->in_apr)
10882393Syz155240 			appr_free(n->in_apr);
10892393Syz155240 		KFREE(n);
10903448Sdh155122 		ifs->ifs_nat_stats.ns_rules--;
10912393Syz155240 	} else {
10922393Syz155240 		n->in_flags |= IPN_DELETE;
10932393Syz155240 		n->in_next = NULL;
10942393Syz155240 	}
10952393Syz155240 	if (getlock) {
10963448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);			/* READ/WRITE */
10972393Syz155240 	}
10982393Syz155240 }
10992393Syz155240 
11002393Syz155240 
11012393Syz155240 /* ------------------------------------------------------------------------ */
11022393Syz155240 /* Function:    fr_natgetsz                                                 */
11032393Syz155240 /* Returns:     int - 0 == success, != 0 is the error value.                */
11042393Syz155240 /* Parameters:  data(I) - pointer to natget structure with kernel pointer   */
11052393Syz155240 /*                        get the size of.                                  */
11062393Syz155240 /*                                                                          */
11072393Syz155240 /* Handle SIOCSTGSZ.                                                        */
11082393Syz155240 /* Return the size of the nat list entry to be copied back to user space.   */
11092393Syz155240 /* The size of the entry is stored in the ng_sz field and the enture natget */
11102393Syz155240 /* structure is copied back to the user.                                    */
11112393Syz155240 /* ------------------------------------------------------------------------ */
11123448Sdh155122 static int fr_natgetsz(data, ifs)
11132393Syz155240 caddr_t data;
11143448Sdh155122 ipf_stack_t *ifs;
11152393Syz155240 {
11162393Syz155240 	ap_session_t *aps;
11172393Syz155240 	nat_t *nat, *n;
11182393Syz155240 	natget_t ng;
11192393Syz155240 
11202393Syz155240 	BCOPYIN(data, &ng, sizeof(ng));
11212393Syz155240 
11222393Syz155240 	nat = ng.ng_ptr;
11232393Syz155240 	if (!nat) {
11243448Sdh155122 		nat = ifs->ifs_nat_instances;
11252393Syz155240 		ng.ng_sz = 0;
11262393Syz155240 		/*
11272393Syz155240 		 * Empty list so the size returned is 0.  Simple.
11282393Syz155240 		 */
11292393Syz155240 		if (nat == NULL) {
11302393Syz155240 			BCOPYOUT(&ng, data, sizeof(ng));
11312393Syz155240 			return 0;
11322393Syz155240 		}
11332393Syz155240 	} else {
11342393Syz155240 		/*
11352393Syz155240 		 * Make sure the pointer we're copying from exists in the
11362393Syz155240 		 * current list of entries.  Security precaution to prevent
11372393Syz155240 		 * copying of random kernel data.
11382393Syz155240 		 */
11393448Sdh155122 		for (n = ifs->ifs_nat_instances; n; n = n->nat_next)
11402393Syz155240 			if (n == nat)
11412393Syz155240 				break;
11422393Syz155240 		if (!n)
11432393Syz155240 			return ESRCH;
11442393Syz155240 	}
11452393Syz155240 
11462393Syz155240 	/*
11472393Syz155240 	 * Incluse any space required for proxy data structures.
11482393Syz155240 	 */
11492393Syz155240 	ng.ng_sz = sizeof(nat_save_t);
11502393Syz155240 	aps = nat->nat_aps;
11512393Syz155240 	if (aps != NULL) {
11522393Syz155240 		ng.ng_sz += sizeof(ap_session_t) - 4;
11532393Syz155240 		if (aps->aps_data != 0)
11542393Syz155240 			ng.ng_sz += aps->aps_psiz;
11552393Syz155240 	}
11562393Syz155240 
11572393Syz155240 	BCOPYOUT(&ng, data, sizeof(ng));
11582393Syz155240 	return 0;
11592393Syz155240 }
11602393Syz155240 
11612393Syz155240 
11622393Syz155240 /* ------------------------------------------------------------------------ */
11632393Syz155240 /* Function:    fr_natgetent                                                */
11642393Syz155240 /* Returns:     int - 0 == success, != 0 is the error value.                */
11652393Syz155240 /* Parameters:  data(I) - pointer to natget structure with kernel pointer   */
11662393Syz155240 /*                        to NAT structure to copy out.                     */
11672393Syz155240 /*                                                                          */
11682393Syz155240 /* Handle SIOCSTGET.                                                        */
11692393Syz155240 /* Copies out NAT entry to user space.  Any additional data held for a      */
11702393Syz155240 /* proxy is also copied, as to is the NAT rule which was responsible for it */
11712393Syz155240 /* ------------------------------------------------------------------------ */
11723448Sdh155122 static int fr_natgetent(data, ifs)
11732393Syz155240 caddr_t data;
11743448Sdh155122 ipf_stack_t *ifs;
11752393Syz155240 {
11762393Syz155240 	int error, outsize;
11772393Syz155240 	ap_session_t *aps;
11782393Syz155240 	nat_save_t *ipn, ipns;
11792393Syz155240 	nat_t *n, *nat;
11802393Syz155240 
11812393Syz155240 	error = fr_inobj(data, &ipns, IPFOBJ_NATSAVE);
11822393Syz155240 	if (error != 0)
11832393Syz155240 		return error;
11842393Syz155240 
11852393Syz155240 	if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920))
11862393Syz155240 		return EINVAL;
11872393Syz155240 
11882393Syz155240 	KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize);
11892393Syz155240 	if (ipn == NULL)
11902393Syz155240 		return ENOMEM;
11912393Syz155240 
11922393Syz155240 	ipn->ipn_dsize = ipns.ipn_dsize;
11932393Syz155240 	nat = ipns.ipn_next;
11942393Syz155240 	if (nat == NULL) {
11953448Sdh155122 		nat = ifs->ifs_nat_instances;
11962393Syz155240 		if (nat == NULL) {
11973448Sdh155122 			if (ifs->ifs_nat_instances == NULL)
11982393Syz155240 				error = ENOENT;
11992393Syz155240 			goto finished;
12002393Syz155240 		}
12012393Syz155240 	} else {
12022393Syz155240 		/*
12032393Syz155240 		 * Make sure the pointer we're copying from exists in the
12042393Syz155240 		 * current list of entries.  Security precaution to prevent
12052393Syz155240 		 * copying of random kernel data.
12062393Syz155240 		 */
12073448Sdh155122 		for (n = ifs->ifs_nat_instances; n; n = n->nat_next)
12082393Syz155240 			if (n == nat)
12092393Syz155240 				break;
12102393Syz155240 		if (n == NULL) {
12112393Syz155240 			error = ESRCH;
12122393Syz155240 			goto finished;
12132393Syz155240 		}
12142393Syz155240 	}
12152393Syz155240 	ipn->ipn_next = nat->nat_next;
12162393Syz155240 
12172393Syz155240 	/*
12182393Syz155240 	 * Copy the NAT structure.
12192393Syz155240 	 */
12202393Syz155240 	bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat));
12212393Syz155240 
12222393Syz155240 	/*
12232393Syz155240 	 * If we have a pointer to the NAT rule it belongs to, save that too.
12242393Syz155240 	 */
12252393Syz155240 	if (nat->nat_ptr != NULL)
12262393Syz155240 		bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat,
12272393Syz155240 		      sizeof(ipn->ipn_ipnat));
12282393Syz155240 
12292393Syz155240 	/*
12302393Syz155240 	 * If we also know the NAT entry has an associated filter rule,
12312393Syz155240 	 * save that too.
12322393Syz155240 	 */
12332393Syz155240 	if (nat->nat_fr != NULL)
12342393Syz155240 		bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr,
12352393Syz155240 		      sizeof(ipn->ipn_fr));
12362393Syz155240 
12372393Syz155240 	/*
12382393Syz155240 	 * Last but not least, if there is an application proxy session set
12392393Syz155240 	 * up for this NAT entry, then copy that out too, including any
12402393Syz155240 	 * private data saved along side it by the proxy.
12412393Syz155240 	 */
12422393Syz155240 	aps = nat->nat_aps;
12432393Syz155240 	outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data);
12442393Syz155240 	if (aps != NULL) {
12452393Syz155240 		char *s;
12462393Syz155240 
12472393Syz155240 		if (outsize < sizeof(*aps)) {
12482393Syz155240 			error = ENOBUFS;
12492393Syz155240 			goto finished;
12502393Syz155240 		}
12512393Syz155240 
12522393Syz155240 		s = ipn->ipn_data;
12532393Syz155240 		bcopy((char *)aps, s, sizeof(*aps));
12542393Syz155240 		s += sizeof(*aps);
12552393Syz155240 		outsize -= sizeof(*aps);
12562393Syz155240 		if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz))
12572393Syz155240 			bcopy(aps->aps_data, s, aps->aps_psiz);
12582393Syz155240 		else
12592393Syz155240 			error = ENOBUFS;
12602393Syz155240 	}
12612393Syz155240 	if (error == 0) {
12622393Syz155240 		error = fr_outobjsz(data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize);
12632393Syz155240 	}
12642393Syz155240 
12652393Syz155240 finished:
12662393Syz155240 	if (ipn != NULL) {
12672393Syz155240 		KFREES(ipn, ipns.ipn_dsize);
12682393Syz155240 	}
12692393Syz155240 	return error;
12702393Syz155240 }
12712393Syz155240 
12722393Syz155240 
12732393Syz155240 /* ------------------------------------------------------------------------ */
12742393Syz155240 /* Function:    fr_natputent                                                */
12752393Syz155240 /* Returns:     int - 0 == success, != 0 is the error value.                */
12762393Syz155240 /* Parameters:  data(I) -     pointer to natget structure with NAT          */
12772393Syz155240 /*                            structure information to load into the kernel */
12782393Syz155240 /*              getlock(I) - flag indicating whether or not a write lock    */
12792393Syz155240 /*                           on ipf_nat is already held.                    */
12802393Syz155240 /*                                                                          */
12812393Syz155240 /* Handle SIOCSTPUT.                                                        */
12822393Syz155240 /* Loads a NAT table entry from user space, including a NAT rule, proxy and */
12832393Syz155240 /* firewall rule data structures, if pointers to them indicate so.          */
12842393Syz155240 /* ------------------------------------------------------------------------ */
12853448Sdh155122 static int fr_natputent(data, getlock, ifs)
12862393Syz155240 caddr_t data;
12872393Syz155240 int getlock;
12883448Sdh155122 ipf_stack_t *ifs;
12892393Syz155240 {
12902393Syz155240 	nat_save_t ipn, *ipnn;
12912393Syz155240 	ap_session_t *aps;
12922393Syz155240 	nat_t *n, *nat;
12932393Syz155240 	frentry_t *fr;
12942393Syz155240 	fr_info_t fin;
12952393Syz155240 	ipnat_t *in;
12962393Syz155240 	int error;
12972393Syz155240 
12982393Syz155240 	error = fr_inobj(data, &ipn, IPFOBJ_NATSAVE);
12992393Syz155240 	if (error != 0)
13002393Syz155240 		return error;
13012393Syz155240 
13022393Syz155240 	/*
13032393Syz155240 	 * Initialise early because of code at junkput label.
13042393Syz155240 	 */
13052393Syz155240 	in = NULL;
13062393Syz155240 	aps = NULL;
13072393Syz155240 	nat = NULL;
13082393Syz155240 	ipnn = NULL;
13092393Syz155240 
13102393Syz155240 	/*
13112393Syz155240 	 * New entry, copy in the rest of the NAT entry if it's size is more
13122393Syz155240 	 * than just the nat_t structure.
13132393Syz155240 	 */
13142393Syz155240 	fr = NULL;
13152393Syz155240 	if (ipn.ipn_dsize > sizeof(ipn)) {
13162393Syz155240 		if (ipn.ipn_dsize > 81920) {
13172393Syz155240 			error = ENOMEM;
13182393Syz155240 			goto junkput;
13192393Syz155240 		}
13202393Syz155240 
13212393Syz155240 		KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize);
13222393Syz155240 		if (ipnn == NULL)
13232393Syz155240 			return ENOMEM;
13242393Syz155240 
13252393Syz155240 		error = fr_inobjsz(data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize);
13262393Syz155240 		if (error != 0) {
13272393Syz155240 			error = EFAULT;
13282393Syz155240 			goto junkput;
13292393Syz155240 		}
13302393Syz155240 	} else
13312393Syz155240 		ipnn = &ipn;
13322393Syz155240 
13332393Syz155240 	KMALLOC(nat, nat_t *);
13342393Syz155240 	if (nat == NULL) {
13352393Syz155240 		error = ENOMEM;
13362393Syz155240 		goto junkput;
13372393Syz155240 	}
13382393Syz155240 
13392393Syz155240 	bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat));
13402393Syz155240 	/*
13412393Syz155240 	 * Initialize all these so that nat_delete() doesn't cause a crash.
13422393Syz155240 	 */
13432393Syz155240 	bzero((char *)nat, offsetof(struct nat, nat_tqe));
13442393Syz155240 	nat->nat_tqe.tqe_pnext = NULL;
13452393Syz155240 	nat->nat_tqe.tqe_next = NULL;
13462393Syz155240 	nat->nat_tqe.tqe_ifq = NULL;
13472393Syz155240 	nat->nat_tqe.tqe_parent = nat;
13482393Syz155240 
13492393Syz155240 	/*
13502393Syz155240 	 * Restore the rule associated with this nat session
13512393Syz155240 	 */
13522393Syz155240 	in = ipnn->ipn_nat.nat_ptr;
13532393Syz155240 	if (in != NULL) {
13542393Syz155240 		KMALLOC(in, ipnat_t *);
13552393Syz155240 		nat->nat_ptr = in;
13562393Syz155240 		if (in == NULL) {
13572393Syz155240 			error = ENOMEM;
13582393Syz155240 			goto junkput;
13592393Syz155240 		}
13602393Syz155240 		bzero((char *)in, offsetof(struct ipnat, in_next6));
13612393Syz155240 		bcopy((char *)&ipnn->ipn_ipnat, (char *)in, sizeof(*in));
13622393Syz155240 		in->in_use = 1;
13632393Syz155240 		in->in_flags |= IPN_DELETE;
13642393Syz155240 
13653448Sdh155122 		ATOMIC_INC(ifs->ifs_nat_stats.ns_rules);
13663448Sdh155122 
1367*4380Sjojemann 		if (nat_resolverule(in, ifs) != 0) {
1368*4380Sjojemann 			error = ESRCH;
1369*4380Sjojemann 			goto junkput;
1370*4380Sjojemann 		}
13712393Syz155240 	}
13722393Syz155240 
13732393Syz155240 	/*
13742393Syz155240 	 * Check that the NAT entry doesn't already exist in the kernel.
13752393Syz155240 	 */
13762393Syz155240 	bzero((char *)&fin, sizeof(fin));
13772393Syz155240 	fin.fin_p = nat->nat_p;
13782393Syz155240 	if (nat->nat_dir == NAT_OUTBOUND) {
13792393Syz155240 		fin.fin_data[0] = ntohs(nat->nat_oport);
13802393Syz155240 		fin.fin_data[1] = ntohs(nat->nat_outport);
13812508Syz155240 		fin.fin_ifp = nat->nat_ifps[0];
13822393Syz155240 		if (getlock) {
13833448Sdh155122 			READ_ENTER(&ifs->ifs_ipf_nat);
13842393Syz155240 		}
13852393Syz155240 		n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p,
13862393Syz155240 			nat->nat_oip, nat->nat_outip);
13872393Syz155240 		if (getlock) {
13883448Sdh155122 			RWLOCK_EXIT(&ifs->ifs_ipf_nat);
13892393Syz155240 		}
13902393Syz155240 		if (n != NULL) {
13912393Syz155240 			error = EEXIST;
13922393Syz155240 			goto junkput;
13932393Syz155240 		}
13942393Syz155240 	} else if (nat->nat_dir == NAT_INBOUND) {
13952393Syz155240 		fin.fin_data[0] = ntohs(nat->nat_inport);
13962393Syz155240 		fin.fin_data[1] = ntohs(nat->nat_oport);
13972508Syz155240 		fin.fin_ifp = nat->nat_ifps[1];
13982393Syz155240 		if (getlock) {
13993448Sdh155122 			READ_ENTER(&ifs->ifs_ipf_nat);
14002393Syz155240 		}
14012393Syz155240 		n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p,
14022508Syz155240 			nat->nat_inip, nat->nat_oip);
14032393Syz155240 		if (getlock) {
14043448Sdh155122 			RWLOCK_EXIT(&ifs->ifs_ipf_nat);
14052393Syz155240 		}
14062393Syz155240 		if (n != NULL) {
14072393Syz155240 			error = EEXIST;
14082393Syz155240 			goto junkput;
14092393Syz155240 		}
14102393Syz155240 	} else {
14112393Syz155240 		error = EINVAL;
14122393Syz155240 		goto junkput;
14132393Syz155240 	}
14142393Syz155240 
14152393Syz155240 	/*
14162393Syz155240 	 * Restore ap_session_t structure.  Include the private data allocated
14172393Syz155240 	 * if it was there.
14182393Syz155240 	 */
14192393Syz155240 	aps = nat->nat_aps;
14202393Syz155240 	if (aps != NULL) {
14212393Syz155240 		KMALLOC(aps, ap_session_t *);
14222393Syz155240 		nat->nat_aps = aps;
14232393Syz155240 		if (aps == NULL) {
14242393Syz155240 			error = ENOMEM;
14252393Syz155240 			goto junkput;
14262393Syz155240 		}
14272393Syz155240 		bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps));
14282393Syz155240 		if (in != NULL)
14292393Syz155240 			aps->aps_apr = in->in_apr;
14302393Syz155240 		else
14312393Syz155240 			aps->aps_apr = NULL;
14322393Syz155240 		if (aps->aps_psiz != 0) {
14332393Syz155240 			if (aps->aps_psiz > 81920) {
14342393Syz155240 				error = ENOMEM;
14352393Syz155240 				goto junkput;
14362393Syz155240 			}
14372393Syz155240 			KMALLOCS(aps->aps_data, void *, aps->aps_psiz);
14382393Syz155240 			if (aps->aps_data == NULL) {
14392393Syz155240 				error = ENOMEM;
14402393Syz155240 				goto junkput;
14412393Syz155240 			}
14422393Syz155240 			bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data,
14432393Syz155240 			      aps->aps_psiz);
14442393Syz155240 		} else {
14452393Syz155240 			aps->aps_psiz = 0;
14462393Syz155240 			aps->aps_data = NULL;
14472393Syz155240 		}
14482393Syz155240 	}
14492393Syz155240 
14502393Syz155240 	/*
14512393Syz155240 	 * If there was a filtering rule associated with this entry then
14522393Syz155240 	 * build up a new one.
14532393Syz155240 	 */
14542393Syz155240 	fr = nat->nat_fr;
14552393Syz155240 	if (fr != NULL) {
14562393Syz155240 		if ((nat->nat_flags & SI_NEWFR) != 0) {
14572393Syz155240 			KMALLOC(fr, frentry_t *);
14582393Syz155240 			nat->nat_fr = fr;
14592393Syz155240 			if (fr == NULL) {
14602393Syz155240 				error = ENOMEM;
14612393Syz155240 				goto junkput;
14622393Syz155240 			}
14632393Syz155240 			ipnn->ipn_nat.nat_fr = fr;
14642393Syz155240 			(void) fr_outobj(data, ipnn, IPFOBJ_NATSAVE);
14652393Syz155240 			bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr));
1466*4380Sjojemann 
1467*4380Sjojemann 			fr->fr_ref = 1;
1468*4380Sjojemann 			fr->fr_dsize = 0;
1469*4380Sjojemann 			fr->fr_data = NULL;
1470*4380Sjojemann 			fr->fr_type = FR_T_NONE;
1471*4380Sjojemann 
14722393Syz155240 			MUTEX_NUKE(&fr->fr_lock);
14732393Syz155240 			MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock");
14742393Syz155240 		} else {
1475*4380Sjojemann 			if (getlock) {
1476*4380Sjojemann 				READ_ENTER(&ifs->ifs_ipf_nat);
1477*4380Sjojemann 			}
14783448Sdh155122 			for (n = ifs->ifs_nat_instances; n; n = n->nat_next)
14792393Syz155240 				if (n->nat_fr == fr)
14802393Syz155240 					break;
14812393Syz155240 
14822393Syz155240 			if (n != NULL) {
14832393Syz155240 				MUTEX_ENTER(&fr->fr_lock);
14842393Syz155240 				fr->fr_ref++;
14852393Syz155240 				MUTEX_EXIT(&fr->fr_lock);
14862393Syz155240 			}
1487*4380Sjojemann 			if (getlock) {
1488*4380Sjojemann 				RWLOCK_EXIT(&ifs->ifs_ipf_nat);
1489*4380Sjojemann 			}
14902393Syz155240 			if (!n) {
14912393Syz155240 				error = ESRCH;
14922393Syz155240 				goto junkput;
14932393Syz155240 			}
14942393Syz155240 		}
14952393Syz155240 	}
14962393Syz155240 
14972393Syz155240 	if (ipnn != &ipn) {
14982393Syz155240 		KFREES(ipnn, ipn.ipn_dsize);
14992393Syz155240 		ipnn = NULL;
15002393Syz155240 	}
15012393Syz155240 
15022393Syz155240 	if (getlock) {
15033448Sdh155122 		WRITE_ENTER(&ifs->ifs_ipf_nat);
15042393Syz155240 	}
15053448Sdh155122 	error = nat_insert(nat, nat->nat_rev, ifs);
15062393Syz155240 	if ((error == 0) && (aps != NULL)) {
15073448Sdh155122 		aps->aps_next = ifs->ifs_ap_sess_list;
15083448Sdh155122 		ifs->ifs_ap_sess_list = aps;
15092393Syz155240 	}
15102393Syz155240 	if (getlock) {
15113448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
15122393Syz155240 	}
15132393Syz155240 
15142393Syz155240 	if (error == 0)
15152393Syz155240 		return 0;
15162393Syz155240 
15172393Syz155240 	error = ENOMEM;
15182393Syz155240 
15192393Syz155240 junkput:
15202393Syz155240 	if (fr != NULL)
15213448Sdh155122 		(void) fr_derefrule(&fr, ifs);
15222393Syz155240 
15232393Syz155240 	if ((ipnn != NULL) && (ipnn != &ipn)) {
15242393Syz155240 		KFREES(ipnn, ipn.ipn_dsize);
15252393Syz155240 	}
15262393Syz155240 	if (nat != NULL) {
15272393Syz155240 		if (aps != NULL) {
15282393Syz155240 			if (aps->aps_data != NULL) {
15292393Syz155240 				KFREES(aps->aps_data, aps->aps_psiz);
15302393Syz155240 			}
15312393Syz155240 			KFREE(aps);
15322393Syz155240 		}
15332393Syz155240 		if (in != NULL) {
15342393Syz155240 			if (in->in_apr)
15352393Syz155240 				appr_free(in->in_apr);
15362393Syz155240 			KFREE(in);
15372393Syz155240 		}
15382393Syz155240 		KFREE(nat);
15392393Syz155240 	}
15402393Syz155240 	return error;
15412393Syz155240 }
15422393Syz155240 
15432393Syz155240 
15442393Syz155240 /* ------------------------------------------------------------------------ */
15452393Syz155240 /* Function:    nat_delete                                                  */
15462393Syz155240 /* Returns:     Nil                                                         */
15472393Syz155240 /* Parameters:  natd(I)    - pointer to NAT structure to delete             */
15482393Syz155240 /*              logtype(I) - type of LOG record to create before deleting   */
15492393Syz155240 /* Write Lock:  ipf_nat                                                     */
15502393Syz155240 /*                                                                          */
15512393Syz155240 /* Delete a nat entry from the various lists and table.  If NAT logging is  */
15522393Syz155240 /* enabled then generate a NAT log record for this event.                   */
15532393Syz155240 /* ------------------------------------------------------------------------ */
15543448Sdh155122 static void nat_delete(nat, logtype, ifs)
15552393Syz155240 struct nat *nat;
15562393Syz155240 int logtype;
15573448Sdh155122 ipf_stack_t *ifs;
15582393Syz155240 {
15592393Syz155240 	struct ipnat *ipn;
15602393Syz155240 
15613448Sdh155122 	if (logtype != 0 && ifs->ifs_nat_logging != 0)
15623448Sdh155122 		nat_log(nat, logtype, ifs);
15633448Sdh155122 
15643448Sdh155122 	MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
15652393Syz155240 
15662393Syz155240 	/*
15672393Syz155240 	 * Take it as a general indication that all the pointers are set if
15682393Syz155240 	 * nat_pnext is set.
15692393Syz155240 	 */
15702393Syz155240 	if (nat->nat_pnext != NULL) {
15713448Sdh155122 		ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
15723448Sdh155122 		ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
15732393Syz155240 
15742393Syz155240 		*nat->nat_pnext = nat->nat_next;
15752393Syz155240 		if (nat->nat_next != NULL) {
15762393Syz155240 			nat->nat_next->nat_pnext = nat->nat_pnext;
15772393Syz155240 			nat->nat_next = NULL;
15782393Syz155240 		}
15792393Syz155240 		nat->nat_pnext = NULL;
15802393Syz155240 
15812393Syz155240 		*nat->nat_phnext[0] = nat->nat_hnext[0];
15822393Syz155240 		if (nat->nat_hnext[0] != NULL) {
15832393Syz155240 			nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
15842393Syz155240 			nat->nat_hnext[0] = NULL;
15852393Syz155240 		}
15862393Syz155240 		nat->nat_phnext[0] = NULL;
15872393Syz155240 
15882393Syz155240 		*nat->nat_phnext[1] = nat->nat_hnext[1];
15892393Syz155240 		if (nat->nat_hnext[1] != NULL) {
15902393Syz155240 			nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
15912393Syz155240 			nat->nat_hnext[1] = NULL;
15922393Syz155240 		}
15932393Syz155240 		nat->nat_phnext[1] = NULL;
15942393Syz155240 
15952393Syz155240 		if ((nat->nat_flags & SI_WILDP) != 0)
15963448Sdh155122 			ifs->ifs_nat_stats.ns_wilds--;
15972393Syz155240 	}
15982393Syz155240 
15992393Syz155240 	if (nat->nat_me != NULL) {
16002393Syz155240 		*nat->nat_me = NULL;
16012393Syz155240 		nat->nat_me = NULL;
16022393Syz155240 	}
16032393Syz155240 
16042393Syz155240 	fr_deletequeueentry(&nat->nat_tqe);
16052393Syz155240 
16062393Syz155240 	nat->nat_ref--;
16072393Syz155240 	if (nat->nat_ref > 0) {
16083448Sdh155122 		MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
16092393Syz155240 		return;
16102393Syz155240 	}
16112393Syz155240 
16122393Syz155240 #ifdef	IPFILTER_SYNC
16132393Syz155240 	if (nat->nat_sync)
16142393Syz155240 		ipfsync_del(nat->nat_sync);
16152393Syz155240 #endif
16162393Syz155240 
16172393Syz155240 	if (nat->nat_fr != NULL)
16183448Sdh155122 		(void)fr_derefrule(&nat->nat_fr, ifs);
16192393Syz155240 
16202393Syz155240 	if (nat->nat_hm != NULL)
16212393Syz155240 		nat_hostmapdel(nat->nat_hm);
16222393Syz155240 
16232393Syz155240 	/*
16242393Syz155240 	 * If there is an active reference from the nat entry to its parent
16252393Syz155240 	 * rule, decrement the rule's reference count and free it too if no
16262393Syz155240 	 * longer being used.
16272393Syz155240 	 */
16282393Syz155240 	ipn = nat->nat_ptr;
16292393Syz155240 	if (ipn != NULL) {
16302393Syz155240 		ipn->in_space++;
16312393Syz155240 		ipn->in_use--;
16322393Syz155240 		if (ipn->in_use == 0 && (ipn->in_flags & IPN_DELETE)) {
16332393Syz155240 			if (ipn->in_apr)
16342393Syz155240 				appr_free(ipn->in_apr);
16352393Syz155240 			KFREE(ipn);
16363448Sdh155122 			ifs->ifs_nat_stats.ns_rules--;
16372393Syz155240 		}
16382393Syz155240 	}
16392393Syz155240 
16402393Syz155240 	MUTEX_DESTROY(&nat->nat_lock);
16412393Syz155240 
16423448Sdh155122 	aps_free(nat->nat_aps, ifs);
16433448Sdh155122 	ifs->ifs_nat_stats.ns_inuse--;
16443448Sdh155122 	MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
16452393Syz155240 
16462393Syz155240 	/*
16472393Syz155240 	 * If there's a fragment table entry too for this nat entry, then
16482393Syz155240 	 * dereference that as well.  This is after nat_lock is released
16492393Syz155240 	 * because of Tru64.
16502393Syz155240 	 */
16513448Sdh155122 	fr_forgetnat((void *)nat, ifs);
16522393Syz155240 
16532393Syz155240 	KFREE(nat);
16542393Syz155240 }
16552393Syz155240 
16562393Syz155240 
16572393Syz155240 /* ------------------------------------------------------------------------ */
16582393Syz155240 /* Function:    nat_flushtable                                              */
16592393Syz155240 /* Returns:     int - number of NAT rules deleted                           */
16602393Syz155240 /* Parameters:  Nil                                                         */
16612393Syz155240 /*                                                                          */
16622393Syz155240 /* Deletes all currently active NAT sessions.  In deleting each NAT entry a */
16632393Syz155240 /* log record should be emitted in nat_delete() if NAT logging is enabled.  */
16642393Syz155240 /* ------------------------------------------------------------------------ */
16652393Syz155240 /*
16662393Syz155240  * nat_flushtable - clear the NAT table of all mapping entries.
16672393Syz155240  */
16683448Sdh155122 static int nat_flushtable(ifs)
16693448Sdh155122 ipf_stack_t *ifs;
16702393Syz155240 {
16712393Syz155240 	nat_t *nat;
16722393Syz155240 	int j = 0;
16732393Syz155240 
16742393Syz155240 	/*
16752393Syz155240 	 * ALL NAT mappings deleted, so lets just make the deletions
16762393Syz155240 	 * quicker.
16772393Syz155240 	 */
16783448Sdh155122 	if (ifs->ifs_nat_table[0] != NULL)
16793448Sdh155122 		bzero((char *)ifs->ifs_nat_table[0],
16803448Sdh155122 		      sizeof(ifs->ifs_nat_table[0]) * ifs->ifs_ipf_nattable_sz);
16813448Sdh155122 	if (ifs->ifs_nat_table[1] != NULL)
16823448Sdh155122 		bzero((char *)ifs->ifs_nat_table[1],
16833448Sdh155122 		      sizeof(ifs->ifs_nat_table[1]) * ifs->ifs_ipf_nattable_sz);
16843448Sdh155122 
16853448Sdh155122 	while ((nat = ifs->ifs_nat_instances) != NULL) {
16863448Sdh155122 		nat_delete(nat, NL_FLUSH, ifs);
16872393Syz155240 		j++;
16882393Syz155240 	}
16892393Syz155240 
16903448Sdh155122 	ifs->ifs_nat_stats.ns_inuse = 0;
16912393Syz155240 	return j;
16922393Syz155240 }
16932393Syz155240 
16942393Syz155240 
16952393Syz155240 /* ------------------------------------------------------------------------ */
16962393Syz155240 /* Function:    nat_clearlist                                               */
16972393Syz155240 /* Returns:     int - number of NAT/RDR rules deleted                       */
16982393Syz155240 /* Parameters:  Nil                                                         */
16992393Syz155240 /*                                                                          */
17002393Syz155240 /* Delete all rules in the current list of rules.  There is nothing elegant */
17012393Syz155240 /* about this cleanup: simply free all entries on the list of rules and     */
17022393Syz155240 /* clear out the tables used for hashed NAT rule lookups.                   */
17032393Syz155240 /* ------------------------------------------------------------------------ */
17043448Sdh155122 static int nat_clearlist(ifs)
17053448Sdh155122 ipf_stack_t *ifs;
17062393Syz155240 {
17073448Sdh155122 	ipnat_t *n, **np = &ifs->ifs_nat_list;
17082393Syz155240 	int i = 0;
17092393Syz155240 
17103448Sdh155122 	if (ifs->ifs_nat_rules != NULL)
17113448Sdh155122 		bzero((char *)ifs->ifs_nat_rules,
17123448Sdh155122 		      sizeof(*ifs->ifs_nat_rules) * ifs->ifs_ipf_natrules_sz);
17133448Sdh155122 	if (ifs->ifs_rdr_rules != NULL)
17143448Sdh155122 		bzero((char *)ifs->ifs_rdr_rules,
17153448Sdh155122 		      sizeof(*ifs->ifs_rdr_rules) * ifs->ifs_ipf_rdrrules_sz);
17162393Syz155240 
17172393Syz155240 	while ((n = *np) != NULL) {
17182393Syz155240 		*np = n->in_next;
17192393Syz155240 		if (n->in_use == 0) {
17202393Syz155240 			if (n->in_apr != NULL)
17212393Syz155240 				appr_free(n->in_apr);
17222393Syz155240 			KFREE(n);
17233448Sdh155122 			ifs->ifs_nat_stats.ns_rules--;
17242393Syz155240 		} else {
17252393Syz155240 			n->in_flags |= IPN_DELETE;
17262393Syz155240 			n->in_next = NULL;
17272393Syz155240 		}
17282393Syz155240 		i++;
17292393Syz155240 	}
17303448Sdh155122 	ifs->ifs_nat_masks = 0;
17313448Sdh155122 	ifs->ifs_rdr_masks = 0;
17322393Syz155240 	return i;
17332393Syz155240 }
17342393Syz155240 
17352393Syz155240 
17362393Syz155240 /* ------------------------------------------------------------------------ */
17372393Syz155240 /* Function:    nat_newmap                                                  */
17382393Syz155240 /* Returns:     int - -1 == error, 0 == success                             */
17392393Syz155240 /* Parameters:  fin(I) - pointer to packet information                      */
17402393Syz155240 /*              nat(I) - pointer to NAT entry                               */
17412393Syz155240 /*              ni(I)  - pointer to structure with misc. information needed */
17422393Syz155240 /*                       to create new NAT entry.                           */
17432393Syz155240 /*                                                                          */
17442393Syz155240 /* Given an empty NAT structure, populate it with new information about a   */
17452393Syz155240 /* new NAT session, as defined by the matching NAT rule.                    */
17462393Syz155240 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
17472393Syz155240 /* to the new IP address for the translation.                               */
17482393Syz155240 /* ------------------------------------------------------------------------ */
17492393Syz155240 static INLINE int nat_newmap(fin, nat, ni)
17502393Syz155240 fr_info_t *fin;
17512393Syz155240 nat_t *nat;
17522393Syz155240 natinfo_t *ni;
17532393Syz155240 {
17542393Syz155240 	u_short st_port, dport, sport, port, sp, dp;
17552393Syz155240 	struct in_addr in, inb;
17562393Syz155240 	hostmap_t *hm;
17572393Syz155240 	u_32_t flags;
17582393Syz155240 	u_32_t st_ip;
17592393Syz155240 	ipnat_t *np;
17602393Syz155240 	nat_t *natl;
17612393Syz155240 	int l;
17623448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
17632393Syz155240 
17642393Syz155240 	/*
17652393Syz155240 	 * If it's an outbound packet which doesn't match any existing
17662393Syz155240 	 * record, then create a new port
17672393Syz155240 	 */
17682393Syz155240 	l = 0;
17692393Syz155240 	hm = NULL;
17702393Syz155240 	np = ni->nai_np;
17712393Syz155240 	st_ip = np->in_nip;
17722393Syz155240 	st_port = np->in_pnext;
17732393Syz155240 	flags = ni->nai_flags;
17742393Syz155240 	sport = ni->nai_sport;
17752393Syz155240 	dport = ni->nai_dport;
17762393Syz155240 
17772393Syz155240 	/*
17782393Syz155240 	 * Do a loop until we either run out of entries to try or we find
17792393Syz155240 	 * a NAT mapping that isn't currently being used.  This is done
17802393Syz155240 	 * because the change to the source is not (usually) being fixed.
17812393Syz155240 	 */
17822393Syz155240 	do {
17832393Syz155240 		port = 0;
17842393Syz155240 		in.s_addr = htonl(np->in_nip);
17852393Syz155240 		if (l == 0) {
17862393Syz155240 			/*
17872393Syz155240 			 * Check to see if there is an existing NAT
17882393Syz155240 			 * setup for this IP address pair.
17892393Syz155240 			 */
17902393Syz155240 			hm = nat_hostmap(np, fin->fin_src, fin->fin_dst,
17913448Sdh155122 					 in, 0, ifs);
17922393Syz155240 			if (hm != NULL)
17932393Syz155240 				in.s_addr = hm->hm_mapip.s_addr;
17942393Syz155240 		} else if ((l == 1) && (hm != NULL)) {
17952393Syz155240 			nat_hostmapdel(hm);
17962393Syz155240 			hm = NULL;
17972393Syz155240 		}
17982393Syz155240 		in.s_addr = ntohl(in.s_addr);
17992393Syz155240 
18002393Syz155240 		nat->nat_hm = hm;
18012393Syz155240 
18022393Syz155240 		if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) {
18032393Syz155240 			if (l > 0)
18042393Syz155240 				return -1;
18052393Syz155240 		}
18062393Syz155240 
18072393Syz155240 		if (np->in_redir == NAT_BIMAP &&
18082393Syz155240 		    np->in_inmsk == np->in_outmsk) {
18092393Syz155240 			/*
18102393Syz155240 			 * map the address block in a 1:1 fashion
18112393Syz155240 			 */
18122393Syz155240 			in.s_addr = np->in_outip;
18132393Syz155240 			in.s_addr |= fin->fin_saddr & ~np->in_inmsk;
18142393Syz155240 			in.s_addr = ntohl(in.s_addr);
18152393Syz155240 
18162393Syz155240 		} else if (np->in_redir & NAT_MAPBLK) {
18172393Syz155240 			if ((l >= np->in_ppip) || ((l > 0) &&
18182393Syz155240 			     !(flags & IPN_TCPUDP)))
18192393Syz155240 				return -1;
18202393Syz155240 			/*
18212393Syz155240 			 * map-block - Calculate destination address.
18222393Syz155240 			 */
18232393Syz155240 			in.s_addr = ntohl(fin->fin_saddr);
18242393Syz155240 			in.s_addr &= ntohl(~np->in_inmsk);
18252393Syz155240 			inb.s_addr = in.s_addr;
18262393Syz155240 			in.s_addr /= np->in_ippip;
18272393Syz155240 			in.s_addr &= ntohl(~np->in_outmsk);
18282393Syz155240 			in.s_addr += ntohl(np->in_outip);
18292393Syz155240 			/*
18302393Syz155240 			 * Calculate destination port.
18312393Syz155240 			 */
18322393Syz155240 			if ((flags & IPN_TCPUDP) &&
18332393Syz155240 			    (np->in_ppip != 0)) {
18342393Syz155240 				port = ntohs(sport) + l;
18352393Syz155240 				port %= np->in_ppip;
18362393Syz155240 				port += np->in_ppip *
18372393Syz155240 					(inb.s_addr % np->in_ippip);
18382393Syz155240 				port += MAPBLK_MINPORT;
18392393Syz155240 				port = htons(port);
18402393Syz155240 			}
18412393Syz155240 
18422393Syz155240 		} else if ((np->in_outip == 0) &&
18432393Syz155240 			   (np->in_outmsk == 0xffffffff)) {
18442393Syz155240 			/*
18452393Syz155240 			 * 0/32 - use the interface's IP address.
18462393Syz155240 			 */
18472393Syz155240 			if ((l > 0) ||
18482393Syz155240 			    fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp,
18493448Sdh155122 				       &in, NULL, fin->fin_ifs) == -1)
18502393Syz155240 				return -1;
18512393Syz155240 			in.s_addr = ntohl(in.s_addr);
18522393Syz155240 
18532393Syz155240 		} else if ((np->in_outip == 0) && (np->in_outmsk == 0)) {
18542393Syz155240 			/*
18552393Syz155240 			 * 0/0 - use the original source address/port.
18562393Syz155240 			 */
18572393Syz155240 			if (l > 0)
18582393Syz155240 				return -1;
18592393Syz155240 			in.s_addr = ntohl(fin->fin_saddr);
18602393Syz155240 
18612393Syz155240 		} else if ((np->in_outmsk != 0xffffffff) &&
18622393Syz155240 			   (np->in_pnext == 0) && ((l > 0) || (hm == NULL)))
18632393Syz155240 			np->in_nip++;
18642393Syz155240 
18652393Syz155240 		natl = NULL;
18662393Syz155240 
18672393Syz155240 		if ((flags & IPN_TCPUDP) &&
18682393Syz155240 		    ((np->in_redir & NAT_MAPBLK) == 0) &&
18692393Syz155240 		    (np->in_flags & IPN_AUTOPORTMAP)) {
18702393Syz155240 			/*
18712393Syz155240 			 * "ports auto" (without map-block)
18722393Syz155240 			 */
18732393Syz155240 			if ((l > 0) && (l % np->in_ppip == 0)) {
18742393Syz155240 				if (l > np->in_space) {
18752393Syz155240 					return -1;
18762393Syz155240 				} else if ((l > np->in_ppip) &&
18772393Syz155240 					   np->in_outmsk != 0xffffffff)
18782393Syz155240 					np->in_nip++;
18792393Syz155240 			}
18802393Syz155240 			if (np->in_ppip != 0) {
18812393Syz155240 				port = ntohs(sport);
18822393Syz155240 				port += (l % np->in_ppip);
18832393Syz155240 				port %= np->in_ppip;
18842393Syz155240 				port += np->in_ppip *
18852393Syz155240 					(ntohl(fin->fin_saddr) %
18862393Syz155240 					 np->in_ippip);
18872393Syz155240 				port += MAPBLK_MINPORT;
18882393Syz155240 				port = htons(port);
18892393Syz155240 			}
18902393Syz155240 
18912393Syz155240 		} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
18922393Syz155240 			   (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) {
18932393Syz155240 			/*
18942393Syz155240 			 * Standard port translation.  Select next port.
18952393Syz155240 			 */
18962393Syz155240 			port = htons(np->in_pnext++);
18972393Syz155240 
18982393Syz155240 			if (np->in_pnext > ntohs(np->in_pmax)) {
18992393Syz155240 				np->in_pnext = ntohs(np->in_pmin);
19002393Syz155240 				if (np->in_outmsk != 0xffffffff)
19012393Syz155240 					np->in_nip++;
19022393Syz155240 			}
19032393Syz155240 		}
19042393Syz155240 
19052393Syz155240 		if (np->in_flags & IPN_IPRANGE) {
19062393Syz155240 			if (np->in_nip > ntohl(np->in_outmsk))
19072393Syz155240 				np->in_nip = ntohl(np->in_outip);
19082393Syz155240 		} else {
19092393Syz155240 			if ((np->in_outmsk != 0xffffffff) &&
19102393Syz155240 			    ((np->in_nip + 1) & ntohl(np->in_outmsk)) >
19112393Syz155240 			    ntohl(np->in_outip))
19122393Syz155240 				np->in_nip = ntohl(np->in_outip) + 1;
19132393Syz155240 		}
19142393Syz155240 
19152393Syz155240 		if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
19162393Syz155240 			port = sport;
19172393Syz155240 
19182393Syz155240 		/*
19192393Syz155240 		 * Here we do a lookup of the connection as seen from
19202393Syz155240 		 * the outside.  If an IP# pair already exists, try
19212393Syz155240 		 * again.  So if you have A->B becomes C->B, you can
19222393Syz155240 		 * also have D->E become C->E but not D->B causing
19232393Syz155240 		 * another C->B.  Also take protocol and ports into
19242393Syz155240 		 * account when determining whether a pre-existing
19252393Syz155240 		 * NAT setup will cause an external conflict where
19262393Syz155240 		 * this is appropriate.
19272393Syz155240 		 */
19282393Syz155240 		inb.s_addr = htonl(in.s_addr);
19292393Syz155240 		sp = fin->fin_data[0];
19302393Syz155240 		dp = fin->fin_data[1];
19312393Syz155240 		fin->fin_data[0] = fin->fin_data[1];
19322393Syz155240 		fin->fin_data[1] = htons(port);
19332393Syz155240 		natl = nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
19342393Syz155240 				    (u_int)fin->fin_p, fin->fin_dst, inb);
19352393Syz155240 		fin->fin_data[0] = sp;
19362393Syz155240 		fin->fin_data[1] = dp;
19372393Syz155240 
19382393Syz155240 		/*
19392393Syz155240 		 * Has the search wrapped around and come back to the
19402393Syz155240 		 * start ?
19412393Syz155240 		 */
19422393Syz155240 		if ((natl != NULL) &&
19432393Syz155240 		    (np->in_pnext != 0) && (st_port == np->in_pnext) &&
19442393Syz155240 		    (np->in_nip != 0) && (st_ip == np->in_nip))
19452393Syz155240 			return -1;
19462393Syz155240 		l++;
19472393Syz155240 	} while (natl != NULL);
19482393Syz155240 
19492393Syz155240 	if (np->in_space > 0)
19502393Syz155240 		np->in_space--;
19512393Syz155240 
19522393Syz155240 	/* Setup the NAT table */
19532393Syz155240 	nat->nat_inip = fin->fin_src;
19542393Syz155240 	nat->nat_outip.s_addr = htonl(in.s_addr);
19552393Syz155240 	nat->nat_oip = fin->fin_dst;
19562393Syz155240 	if (nat->nat_hm == NULL)
19572393Syz155240 		nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst,
19583448Sdh155122 					  nat->nat_outip, 0, ifs);
19592393Syz155240 
19602393Syz155240 	/*
19612393Syz155240 	 * The ICMP checksum does not have a pseudo header containing
19622393Syz155240 	 * the IP addresses
19632393Syz155240 	 */
19642393Syz155240 	ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr));
19652393Syz155240 	ni->nai_sum2 = LONG_SUM(in.s_addr);
19662393Syz155240 	if ((flags & IPN_TCPUDP)) {
19672393Syz155240 		ni->nai_sum1 += ntohs(sport);
19682393Syz155240 		ni->nai_sum2 += ntohs(port);
19692393Syz155240 	}
19702393Syz155240 
19712393Syz155240 	if (flags & IPN_TCPUDP) {
19722393Syz155240 		nat->nat_inport = sport;
19732393Syz155240 		nat->nat_outport = port;	/* sport */
19742393Syz155240 		nat->nat_oport = dport;
19752393Syz155240 		((tcphdr_t *)fin->fin_dp)->th_sport = port;
19762393Syz155240 	} else if (flags & IPN_ICMPQUERY) {
19772393Syz155240 		((icmphdr_t *)fin->fin_dp)->icmp_id = port;
19782393Syz155240 		nat->nat_inport = port;
19792393Syz155240 		nat->nat_outport = port;
19802393Syz155240 	}
19812393Syz155240 
19822393Syz155240 	ni->nai_ip.s_addr = in.s_addr;
19832393Syz155240 	ni->nai_port = port;
19842393Syz155240 	ni->nai_nport = dport;
19852393Syz155240 	return 0;
19862393Syz155240 }
19872393Syz155240 
19882393Syz155240 
19892393Syz155240 /* ------------------------------------------------------------------------ */
19902393Syz155240 /* Function:    nat_newrdr                                                  */
19912393Syz155240 /* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
19922393Syz155240 /*                    allow rule to be moved if IPN_ROUNDR is set.          */
19932393Syz155240 /* Parameters:  fin(I) - pointer to packet information                      */
19942393Syz155240 /*              nat(I) - pointer to NAT entry                               */
19952393Syz155240 /*              ni(I)  - pointer to structure with misc. information needed */
19962393Syz155240 /*                       to create new NAT entry.                           */
19972393Syz155240 /*                                                                          */
19982393Syz155240 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
19992393Syz155240 /* to the new IP address for the translation.                               */
20002393Syz155240 /* ------------------------------------------------------------------------ */
20012393Syz155240 static INLINE int nat_newrdr(fin, nat, ni)
20022393Syz155240 fr_info_t *fin;
20032393Syz155240 nat_t *nat;
20042393Syz155240 natinfo_t *ni;
20052393Syz155240 {
20062393Syz155240 	u_short nport, dport, sport;
20072393Syz155240 	struct in_addr in;
20082393Syz155240 	hostmap_t *hm;
20092393Syz155240 	u_32_t flags;
20102393Syz155240 	ipnat_t *np;
20112393Syz155240 	int move;
20123448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
20132393Syz155240 
20142393Syz155240 	move = 1;
20152393Syz155240 	hm = NULL;
20162393Syz155240 	in.s_addr = 0;
20172393Syz155240 	np = ni->nai_np;
20182393Syz155240 	flags = ni->nai_flags;
20192393Syz155240 	sport = ni->nai_sport;
20202393Syz155240 	dport = ni->nai_dport;
20212393Syz155240 
20222393Syz155240 	/*
20232393Syz155240 	 * If the matching rule has IPN_STICKY set, then we want to have the
20242393Syz155240 	 * same rule kick in as before.  Why would this happen?  If you have
20252393Syz155240 	 * a collection of rdr rules with "round-robin sticky", the current
20262393Syz155240 	 * packet might match a different one to the previous connection but
20272393Syz155240 	 * we want the same destination to be used.
20282393Syz155240 	 */
20292393Syz155240 	if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) ==
20302393Syz155240 	    (IPN_ROUNDR|IPN_STICKY)) {
20312393Syz155240 		hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, in,
20323448Sdh155122 				 (u_32_t)dport, ifs);
20332393Syz155240 		if (hm != NULL) {
20342393Syz155240 			in.s_addr = ntohl(hm->hm_mapip.s_addr);
20352393Syz155240 			np = hm->hm_ipnat;
20362393Syz155240 			ni->nai_np = np;
20372393Syz155240 			move = 0;
20382393Syz155240 		}
20392393Syz155240 	}
20402393Syz155240 
20412393Syz155240 	/*
20422393Syz155240 	 * Otherwise, it's an inbound packet. Most likely, we don't
20432393Syz155240 	 * want to rewrite source ports and source addresses. Instead,
20442393Syz155240 	 * we want to rewrite to a fixed internal address and fixed
20452393Syz155240 	 * internal port.
20462393Syz155240 	 */
20472393Syz155240 	if (np->in_flags & IPN_SPLIT) {
20482393Syz155240 		in.s_addr = np->in_nip;
20492393Syz155240 
20502393Syz155240 		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
20512393Syz155240 			hm = nat_hostmap(np, fin->fin_src, fin->fin_dst,
20523448Sdh155122 					 in, (u_32_t)dport, ifs);
20532393Syz155240 			if (hm != NULL) {
20542393Syz155240 				in.s_addr = hm->hm_mapip.s_addr;
20552393Syz155240 				move = 0;
20562393Syz155240 			}
20572393Syz155240 		}
20582393Syz155240 
20592393Syz155240 		if (hm == NULL || hm->hm_ref == 1) {
20602393Syz155240 			if (np->in_inip == htonl(in.s_addr)) {
20612393Syz155240 				np->in_nip = ntohl(np->in_inmsk);
20622393Syz155240 				move = 0;
20632393Syz155240 			} else {
20642393Syz155240 				np->in_nip = ntohl(np->in_inip);
20652393Syz155240 			}
20662393Syz155240 		}
20672393Syz155240 
20682393Syz155240 	} else if ((np->in_inip == 0) && (np->in_inmsk == 0xffffffff)) {
20692393Syz155240 		/*
20702393Syz155240 		 * 0/32 - use the interface's IP address.
20712393Syz155240 		 */
20723448Sdh155122 		if (fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, &in, NULL,
20733448Sdh155122 			   fin->fin_ifs) == -1)
20742393Syz155240 			return -1;
20752393Syz155240 		in.s_addr = ntohl(in.s_addr);
20762393Syz155240 
20772393Syz155240 	} else if ((np->in_inip == 0) && (np->in_inmsk== 0)) {
20782393Syz155240 		/*
20792393Syz155240 		 * 0/0 - use the original destination address/port.
20802393Syz155240 		 */
20812393Syz155240 		in.s_addr = ntohl(fin->fin_daddr);
20822393Syz155240 
20832393Syz155240 	} else if (np->in_redir == NAT_BIMAP &&
20842393Syz155240 		   np->in_inmsk == np->in_outmsk) {
20852393Syz155240 		/*
20862393Syz155240 		 * map the address block in a 1:1 fashion
20872393Syz155240 		 */
20882393Syz155240 		in.s_addr = np->in_inip;
20892393Syz155240 		in.s_addr |= fin->fin_daddr & ~np->in_inmsk;
20902393Syz155240 		in.s_addr = ntohl(in.s_addr);
20912393Syz155240 	} else {
20922393Syz155240 		in.s_addr = ntohl(np->in_inip);
20932393Syz155240 	}
20942393Syz155240 
20952393Syz155240 	if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
20962393Syz155240 		nport = dport;
20972393Syz155240 	else {
20982393Syz155240 		/*
20992393Syz155240 		 * Whilst not optimized for the case where
21002393Syz155240 		 * pmin == pmax, the gain is not significant.
21012393Syz155240 		 */
21022393Syz155240 		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
21032393Syz155240 		    (np->in_pmin != np->in_pmax)) {
21042393Syz155240 			nport = ntohs(dport) - ntohs(np->in_pmin) +
21052393Syz155240 				ntohs(np->in_pnext);
21062393Syz155240 			nport = htons(nport);
21072393Syz155240 		} else
21082393Syz155240 			nport = np->in_pnext;
21092393Syz155240 	}
21102393Syz155240 
21112393Syz155240 	/*
21122393Syz155240 	 * When the redirect-to address is set to 0.0.0.0, just
21132393Syz155240 	 * assume a blank `forwarding' of the packet.  We don't
21142393Syz155240 	 * setup any translation for this either.
21152393Syz155240 	 */
21162393Syz155240 	if (in.s_addr == 0) {
21172393Syz155240 		if (nport == dport)
21182393Syz155240 			return -1;
21192393Syz155240 		in.s_addr = ntohl(fin->fin_daddr);
21202393Syz155240 	}
21212393Syz155240 
21222393Syz155240 	nat->nat_inip.s_addr = htonl(in.s_addr);
21232393Syz155240 	nat->nat_outip = fin->fin_dst;
21242393Syz155240 	nat->nat_oip = fin->fin_src;
21252393Syz155240 
21262393Syz155240 	ni->nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr)) + ntohs(dport);
21272393Syz155240 	ni->nai_sum2 = LONG_SUM(in.s_addr) + ntohs(nport);
21282393Syz155240 
21292393Syz155240 	ni->nai_ip.s_addr = in.s_addr;
21302393Syz155240 	ni->nai_nport = nport;
21312393Syz155240 	ni->nai_port = sport;
21322393Syz155240 
21332393Syz155240 	if (flags & IPN_TCPUDP) {
21342393Syz155240 		nat->nat_inport = nport;
21352393Syz155240 		nat->nat_outport = dport;
21362393Syz155240 		nat->nat_oport = sport;
21372393Syz155240 		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
21382393Syz155240 	} else if (flags & IPN_ICMPQUERY) {
21392393Syz155240 		((icmphdr_t *)fin->fin_dp)->icmp_id = nport;
21402393Syz155240 		nat->nat_inport = nport;
21412393Syz155240 		nat->nat_outport = nport;
21422393Syz155240 	}
21432393Syz155240 
21442393Syz155240 	return move;
21452393Syz155240 }
21462393Syz155240 
21472393Syz155240 /* ------------------------------------------------------------------------ */
21482393Syz155240 /* Function:    nat_new                                                     */
21492393Syz155240 /* Returns:     nat_t* - NULL == failure to create new NAT structure,       */
21502393Syz155240 /*                       else pointer to new NAT structure                  */
21512393Syz155240 /* Parameters:  fin(I)       - pointer to packet information                */
21522393Syz155240 /*              np(I)        - pointer to NAT rule                          */
21532393Syz155240 /*              natsave(I)   - pointer to where to store NAT struct pointer */
21542393Syz155240 /*              flags(I)     - flags describing the current packet          */
21552393Syz155240 /*              direction(I) - direction of packet (in/out)                 */
21562393Syz155240 /* Write Lock:  ipf_nat                                                     */
21572393Syz155240 /*                                                                          */
21582393Syz155240 /* Attempts to create a new NAT entry.  Does not actually change the packet */
21592393Syz155240 /* in any way.                                                              */
21602393Syz155240 /*                                                                          */
21612393Syz155240 /* This fucntion is in three main parts: (1) deal with creating a new NAT   */
21622393Syz155240 /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
21632393Syz155240 /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
21642393Syz155240 /* and (3) building that structure and putting it into the NAT table(s).    */
21652393Syz155240 /* ------------------------------------------------------------------------ */
21662393Syz155240 nat_t *nat_new(fin, np, natsave, flags, direction)
21672393Syz155240 fr_info_t *fin;
21682393Syz155240 ipnat_t *np;
21692393Syz155240 nat_t **natsave;
21702393Syz155240 u_int flags;
21712393Syz155240 int direction;
21722393Syz155240 {
21732393Syz155240 	u_short port = 0, sport = 0, dport = 0, nport = 0;
21742393Syz155240 	tcphdr_t *tcp = NULL;
21752393Syz155240 	hostmap_t *hm = NULL;
21762393Syz155240 	struct in_addr in;
21772393Syz155240 	nat_t *nat, *natl;
21782393Syz155240 	u_int nflags;
21792393Syz155240 	natinfo_t ni;
21802393Syz155240 	u_32_t sumd;
21812393Syz155240 	int move;
21823448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
21833448Sdh155122 
21843448Sdh155122 	if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) {
21853448Sdh155122 		ifs->ifs_nat_stats.ns_memfail++;
21862393Syz155240 		return NULL;
21872393Syz155240 	}
21882393Syz155240 
21892393Syz155240 	move = 1;
21902393Syz155240 	nflags = np->in_flags & flags;
21912393Syz155240 	nflags &= NAT_FROMRULE;
21922393Syz155240 
21932393Syz155240 	ni.nai_np = np;
21942393Syz155240 	ni.nai_nflags = nflags;
21952393Syz155240 	ni.nai_flags = flags;
21962393Syz155240 
21972393Syz155240 	/* Give me a new nat */
21982393Syz155240 	KMALLOC(nat, nat_t *);
21992393Syz155240 	if (nat == NULL) {
22003448Sdh155122 		ifs->ifs_nat_stats.ns_memfail++;
22012393Syz155240 		/*
22022393Syz155240 		 * Try to automatically tune the max # of entries in the
22032393Syz155240 		 * table allowed to be less than what will cause kmem_alloc()
22042393Syz155240 		 * to fail and try to eliminate panics due to out of memory
22052393Syz155240 		 * conditions arising.
22062393Syz155240 		 */
22073448Sdh155122 		if (ifs->ifs_ipf_nattable_max > ifs->ifs_ipf_nattable_sz) {
22083448Sdh155122 			ifs->ifs_ipf_nattable_max = ifs->ifs_nat_stats.ns_inuse - 100;
22092393Syz155240 			printf("ipf_nattable_max reduced to %d\n",
22103448Sdh155122 				ifs->ifs_ipf_nattable_max);
22112393Syz155240 		}
22122393Syz155240 		return NULL;
22132393Syz155240 	}
22142393Syz155240 
22152393Syz155240 	if (flags & IPN_TCPUDP) {
22162393Syz155240 		tcp = fin->fin_dp;
22172393Syz155240 		ni.nai_sport = htons(fin->fin_sport);
22182393Syz155240 		ni.nai_dport = htons(fin->fin_dport);
22192393Syz155240 	} else if (flags & IPN_ICMPQUERY) {
22202393Syz155240 		/*
22212393Syz155240 		 * In the ICMP query NAT code, we translate the ICMP id fields
22222393Syz155240 		 * to make them unique. This is indepedent of the ICMP type
22232393Syz155240 		 * (e.g. in the unlikely event that a host sends an echo and
22242393Syz155240 		 * an tstamp request with the same id, both packets will have
22252393Syz155240 		 * their ip address/id field changed in the same way).
22262393Syz155240 		 */
22272393Syz155240 		/* The icmp_id field is used by the sender to identify the
22282393Syz155240 		 * process making the icmp request. (the receiver justs
22292393Syz155240 		 * copies it back in its response). So, it closely matches
22302393Syz155240 		 * the concept of source port. We overlay sport, so we can
22312393Syz155240 		 * maximally reuse the existing code.
22322393Syz155240 		 */
22332393Syz155240 		ni.nai_sport = ((icmphdr_t *)fin->fin_dp)->icmp_id;
22342393Syz155240 		ni.nai_dport = ni.nai_sport;
22352393Syz155240 	}
22362393Syz155240 
22372393Syz155240 	bzero((char *)nat, sizeof(*nat));
22382393Syz155240 	nat->nat_flags = flags;
22393448Sdh155122 	nat->nat_redir = np->in_redir;
22402393Syz155240 
22412393Syz155240 	if ((flags & NAT_SLAVE) == 0) {
22423448Sdh155122 		MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
22432393Syz155240 	}
22442393Syz155240 
22452393Syz155240 	/*
22462393Syz155240 	 * Search the current table for a match.
22472393Syz155240 	 */
22482393Syz155240 	if (direction == NAT_OUTBOUND) {
22492393Syz155240 		/*
22502393Syz155240 		 * We can now arrange to call this for the same connection
22512393Syz155240 		 * because ipf_nat_new doesn't protect the code path into
22522393Syz155240 		 * this function.
22532393Syz155240 		 */
22542393Syz155240 		natl = nat_outlookup(fin, nflags, (u_int)fin->fin_p,
22552393Syz155240 				     fin->fin_src, fin->fin_dst);
22562393Syz155240 		if (natl != NULL) {
2257*4380Sjojemann 			KFREE(nat);
22582393Syz155240 			nat = natl;
22592393Syz155240 			goto done;
22602393Syz155240 		}
22612393Syz155240 
22622393Syz155240 		move = nat_newmap(fin, nat, &ni);
22632393Syz155240 		if (move == -1)
22642393Syz155240 			goto badnat;
22652393Syz155240 
22662393Syz155240 		np = ni.nai_np;
22672393Syz155240 		in = ni.nai_ip;
22682393Syz155240 	} else {
22692393Syz155240 		/*
22702393Syz155240 		 * NAT_INBOUND is used only for redirects rules
22712393Syz155240 		 */
22722393Syz155240 		natl = nat_inlookup(fin, nflags, (u_int)fin->fin_p,
22732393Syz155240 				    fin->fin_src, fin->fin_dst);
22742393Syz155240 		if (natl != NULL) {
2275*4380Sjojemann 			KFREE(nat);
22762393Syz155240 			nat = natl;
22772393Syz155240 			goto done;
22782393Syz155240 		}
22792393Syz155240 
22802393Syz155240 		move = nat_newrdr(fin, nat, &ni);
22812393Syz155240 		if (move == -1)
22822393Syz155240 			goto badnat;
22832393Syz155240 
22842393Syz155240 		np = ni.nai_np;
22852393Syz155240 		in = ni.nai_ip;
22862393Syz155240 	}
22872393Syz155240 	port = ni.nai_port;
22882393Syz155240 	nport = ni.nai_nport;
22892393Syz155240 
22902393Syz155240 	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
22912393Syz155240 		if (np->in_redir == NAT_REDIRECT) {
22922393Syz155240 			nat_delrdr(np);
22933448Sdh155122 			nat_addrdr(np, ifs);
22942393Syz155240 		} else if (np->in_redir == NAT_MAP) {
22952393Syz155240 			nat_delnat(np);
22963448Sdh155122 			nat_addnat(np, ifs);
22972393Syz155240 		}
22982393Syz155240 	}
22992393Syz155240 
23002393Syz155240 	if (flags & IPN_TCPUDP) {
23012393Syz155240 		sport = ni.nai_sport;
23022393Syz155240 		dport = ni.nai_dport;
23032393Syz155240 	} else if (flags & IPN_ICMPQUERY) {
23042393Syz155240 		sport = ni.nai_sport;
23052393Syz155240 		dport = 0;
23062393Syz155240 	}
23072393Syz155240 
23082958Sdr146992 	/*
23092958Sdr146992 	 * nat_sumd[0] stores adjustment value including both IP address and
23102958Sdr146992 	 * port number changes. nat_sumd[1] stores adjustment value only for
23112958Sdr146992 	 * IP address changes, to be used for pseudo header adjustment, in
23122958Sdr146992 	 * case hardware partial checksum offload is offered.
23132958Sdr146992 	 */
23142393Syz155240 	CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd);
23152393Syz155240 	nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
23162958Sdr146992 #if SOLARIS && defined(_KERNEL) && (SOLARIS2 >= 6)
23172958Sdr146992 	if (flags & IPN_TCPUDP) {
23182958Sdr146992 		ni.nai_sum1 = LONG_SUM(in.s_addr);
23192393Syz155240 		if (direction == NAT_OUTBOUND)
23202958Sdr146992 			ni.nai_sum2 = LONG_SUM(ntohl(fin->fin_saddr));
23212393Syz155240 		else
23222958Sdr146992 			ni.nai_sum2 = LONG_SUM(ntohl(fin->fin_daddr));
23232958Sdr146992 
23242958Sdr146992 		CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd);
23252958Sdr146992 		nat->nat_sumd[1] = (sumd & 0xffff) + (sumd >> 16);
23262393Syz155240 	} else
23272393Syz155240 #endif
23282393Syz155240 		nat->nat_sumd[1] = nat->nat_sumd[0];
23292393Syz155240 
23302393Syz155240 	if ((flags & IPN_TCPUDPICMP) && ((sport != port) || (dport != nport))) {
23312393Syz155240 		if (direction == NAT_OUTBOUND)
23322393Syz155240 			ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_saddr));
23332393Syz155240 		else
23342393Syz155240 			ni.nai_sum1 = LONG_SUM(ntohl(fin->fin_daddr));
23352393Syz155240 
23362393Syz155240 		ni.nai_sum2 = LONG_SUM(in.s_addr);
23372393Syz155240 
23382393Syz155240 		CALC_SUMD(ni.nai_sum1, ni.nai_sum2, sumd);
23392393Syz155240 		nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
23402393Syz155240 	} else {
23412393Syz155240 		nat->nat_ipsumd = nat->nat_sumd[0];
23422393Syz155240 		if (!(flags & IPN_TCPUDPICMP)) {
23432393Syz155240 			nat->nat_sumd[0] = 0;
23442393Syz155240 			nat->nat_sumd[1] = 0;
23452393Syz155240 		}
23462393Syz155240 	}
23472393Syz155240 
23482393Syz155240 	if (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) {
23492393Syz155240 		goto badnat;
23502393Syz155240 	}
23512393Syz155240 	if (flags & SI_WILDP)
23523448Sdh155122 		ifs->ifs_nat_stats.ns_wilds++;
23532393Syz155240 	goto done;
23542393Syz155240 badnat:
23553448Sdh155122 	ifs->ifs_nat_stats.ns_badnat++;
23562393Syz155240 	if ((hm = nat->nat_hm) != NULL)
23572393Syz155240 		nat_hostmapdel(hm);
23582393Syz155240 	KFREE(nat);
23592393Syz155240 	nat = NULL;
23602393Syz155240 done:
23612393Syz155240 	if ((flags & NAT_SLAVE) == 0) {
23623448Sdh155122 		MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
23632393Syz155240 	}
23642393Syz155240 	return nat;
23652393Syz155240 }
23662393Syz155240 
23672393Syz155240 
23682393Syz155240 /* ------------------------------------------------------------------------ */
23692393Syz155240 /* Function:    nat_finalise                                                */
23702393Syz155240 /* Returns:     int - 0 == sucess, -1 == failure                            */
23712393Syz155240 /* Parameters:  fin(I) - pointer to packet information                      */
23722393Syz155240 /*              nat(I) - pointer to NAT entry                               */
23732393Syz155240 /*              ni(I)  - pointer to structure with misc. information needed */
23742393Syz155240 /*                       to create new NAT entry.                           */
23752393Syz155240 /* Write Lock:  ipf_nat                                                     */
23762393Syz155240 /*                                                                          */
23772393Syz155240 /* This is the tail end of constructing a new NAT entry and is the same     */
23782393Syz155240 /* for both IPv4 and IPv6.                                                  */
23792393Syz155240 /* ------------------------------------------------------------------------ */
23802393Syz155240 /*ARGSUSED*/
23812393Syz155240 static INLINE int nat_finalise(fin, nat, ni, tcp, natsave, direction)
23822393Syz155240 fr_info_t *fin;
23832393Syz155240 nat_t *nat;
23842393Syz155240 natinfo_t *ni;
23852393Syz155240 tcphdr_t *tcp;
23862393Syz155240 nat_t **natsave;
23872393Syz155240 int direction;
23882393Syz155240 {
23892393Syz155240 	frentry_t *fr;
23902393Syz155240 	ipnat_t *np;
23913448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
23922393Syz155240 
23932393Syz155240 	np = ni->nai_np;
23942393Syz155240 
23952958Sdr146992 	COPYIFNAME(fin->fin_ifp, nat->nat_ifnames[0], fin->fin_v);
23962958Sdr146992 
23972393Syz155240 #ifdef	IPFILTER_SYNC
23982393Syz155240 	if ((nat->nat_flags & SI_CLONE) == 0)
23992393Syz155240 		nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat);
24002393Syz155240 #endif
24012393Syz155240 
24022393Syz155240 	nat->nat_me = natsave;
24032393Syz155240 	nat->nat_dir = direction;
24042508Syz155240 	nat->nat_ifps[0] = np->in_ifps[0];
24052508Syz155240 	nat->nat_ifps[1] = np->in_ifps[1];
24062393Syz155240 	nat->nat_ptr = np;
24072393Syz155240 	nat->nat_p = fin->fin_p;
24082393Syz155240 	nat->nat_mssclamp = np->in_mssclamp;
24092393Syz155240 	fr = fin->fin_fr;
24102393Syz155240 	nat->nat_fr = fr;
24112393Syz155240 
24122393Syz155240 	if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0))
24132393Syz155240 		if (appr_new(fin, nat) == -1)
24142393Syz155240 			return -1;
24152393Syz155240 
24163448Sdh155122 	if (nat_insert(nat, fin->fin_rev, ifs) == 0) {
24173448Sdh155122 		if (ifs->ifs_nat_logging)
24183448Sdh155122 			nat_log(nat, (u_int)np->in_redir, ifs);
24192393Syz155240 		np->in_use++;
24202393Syz155240 		if (fr != NULL) {
24212393Syz155240 			MUTEX_ENTER(&fr->fr_lock);
24222393Syz155240 			fr->fr_ref++;
24232393Syz155240 			MUTEX_EXIT(&fr->fr_lock);
24242393Syz155240 		}
24252393Syz155240 		return 0;
24262393Syz155240 	}
24272393Syz155240 
24282393Syz155240 	/*
24292393Syz155240 	 * nat_insert failed, so cleanup time...
24302393Syz155240 	 */
24312393Syz155240 	return -1;
24322393Syz155240 }
24332393Syz155240 
24342393Syz155240 
24352393Syz155240 /* ------------------------------------------------------------------------ */
24362393Syz155240 /* Function:   nat_insert                                                   */
24372393Syz155240 /* Returns:    int - 0 == sucess, -1 == failure                             */
24382393Syz155240 /* Parameters: nat(I) - pointer to NAT structure                            */
24392393Syz155240 /*             rev(I) - flag indicating forward/reverse direction of packet */
24402393Syz155240 /* Write Lock: ipf_nat                                                      */
24412393Syz155240 /*                                                                          */
24422393Syz155240 /* Insert a NAT entry into the hash tables for searching and add it to the  */
24432393Syz155240 /* list of active NAT entries.  Adjust global counters when complete.       */
24442393Syz155240 /* ------------------------------------------------------------------------ */
24453448Sdh155122 int	nat_insert(nat, rev, ifs)
24462393Syz155240 nat_t	*nat;
24472393Syz155240 int	rev;
24483448Sdh155122 ipf_stack_t *ifs;
24492393Syz155240 {
24502393Syz155240 	u_int hv1, hv2;
24512393Syz155240 	nat_t **natp;
24522393Syz155240 
24532393Syz155240 	/*
24542393Syz155240 	 * Try and return an error as early as possible, so calculate the hash
24552393Syz155240 	 * entry numbers first and then proceed.
24562393Syz155240 	 */
24572393Syz155240 	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
24582393Syz155240 		hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport,
24592393Syz155240 				  0xffffffff);
24602393Syz155240 		hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport,
24613448Sdh155122 				  ifs->ifs_ipf_nattable_sz);
24622393Syz155240 		hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport,
24632393Syz155240 				  0xffffffff);
24642393Syz155240 		hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport,
24653448Sdh155122 				  ifs->ifs_ipf_nattable_sz);
24662393Syz155240 	} else {
24672393Syz155240 		hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff);
24683448Sdh155122 		hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1,
24693448Sdh155122 				  ifs->ifs_ipf_nattable_sz);
24702393Syz155240 		hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff);
24713448Sdh155122 		hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2,
24723448Sdh155122 				  ifs->ifs_ipf_nattable_sz);
24732393Syz155240 	}
24742393Syz155240 
24753448Sdh155122 	if (ifs->ifs_nat_stats.ns_bucketlen[0][hv1] >= ifs->ifs_fr_nat_maxbucket ||
24763448Sdh155122 	    ifs->ifs_nat_stats.ns_bucketlen[1][hv2] >= ifs->ifs_fr_nat_maxbucket) {
24772393Syz155240 		return -1;
24782393Syz155240 	}
24792393Syz155240 
24802393Syz155240 	nat->nat_hv[0] = hv1;
24812393Syz155240 	nat->nat_hv[1] = hv2;
24822393Syz155240 
24832393Syz155240 	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
24842393Syz155240 
24852393Syz155240 	nat->nat_rev = rev;
24862393Syz155240 	nat->nat_ref = 1;
24872393Syz155240 	nat->nat_bytes[0] = 0;
24882393Syz155240 	nat->nat_pkts[0] = 0;
24892393Syz155240 	nat->nat_bytes[1] = 0;
24902393Syz155240 	nat->nat_pkts[1] = 0;
24912393Syz155240 
24922393Syz155240 	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
24933448Sdh155122 	nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 4, ifs);
24942393Syz155240 
24952393Syz155240 	if (nat->nat_ifnames[1][0] !='\0') {
24962393Syz155240 		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
24973448Sdh155122 		nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 4, ifs);
24982393Syz155240 	} else {
24992393Syz155240 		(void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0],
25002393Syz155240 			       LIFNAMSIZ);
25012393Syz155240 		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
25022393Syz155240 		nat->nat_ifps[1] = nat->nat_ifps[0];
25032393Syz155240 	}
25042393Syz155240 
25053448Sdh155122 	nat->nat_next = ifs->ifs_nat_instances;
25063448Sdh155122 	nat->nat_pnext = &ifs->ifs_nat_instances;
25073448Sdh155122 	if (ifs->ifs_nat_instances)
25083448Sdh155122 		ifs->ifs_nat_instances->nat_pnext = &nat->nat_next;
25093448Sdh155122 	ifs->ifs_nat_instances = nat;
25103448Sdh155122 
25113448Sdh155122 	natp = &ifs->ifs_nat_table[0][hv1];
25122393Syz155240 	if (*natp)
25132393Syz155240 		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
25142393Syz155240 	nat->nat_phnext[0] = natp;
25152393Syz155240 	nat->nat_hnext[0] = *natp;
25162393Syz155240 	*natp = nat;
25173448Sdh155122 	ifs->ifs_nat_stats.ns_bucketlen[0][hv1]++;
25183448Sdh155122 
25193448Sdh155122 	natp = &ifs->ifs_nat_table[1][hv2];
25202393Syz155240 	if (*natp)
25212393Syz155240 		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
25222393Syz155240 	nat->nat_phnext[1] = natp;
25232393Syz155240 	nat->nat_hnext[1] = *natp;
25242393Syz155240 	*natp = nat;
25253448Sdh155122 	ifs->ifs_nat_stats.ns_bucketlen[1][hv2]++;
25263448Sdh155122 
25273448Sdh155122 	fr_setnatqueue(nat, rev, ifs);
25283448Sdh155122 
25293448Sdh155122 	ifs->ifs_nat_stats.ns_added++;
25303448Sdh155122 	ifs->ifs_nat_stats.ns_inuse++;
25312393Syz155240 	return 0;
25322393Syz155240 }
25332393Syz155240 
25342393Syz155240 
25352393Syz155240 /* ------------------------------------------------------------------------ */
25362393Syz155240 /* Function:    nat_icmperrorlookup                                         */
25372393Syz155240 /* Returns:     nat_t* - point to matching NAT structure                    */
25382393Syz155240 /* Parameters:  fin(I) - pointer to packet information                      */
25392393Syz155240 /*              dir(I) - direction of packet (in/out)                       */
25402393Syz155240 /*                                                                          */
25412393Syz155240 /* Check if the ICMP error message is related to an existing TCP, UDP or    */
25422393Syz155240 /* ICMP query nat entry.  It is assumed that the packet is already of the   */
25432393Syz155240 /* the required length.                                                     */
25442393Syz155240 /* ------------------------------------------------------------------------ */
25452393Syz155240 nat_t *nat_icmperrorlookup(fin, dir)
25462393Syz155240 fr_info_t *fin;
25472393Syz155240 int dir;
25482393Syz155240 {
25492393Syz155240 	int flags = 0, minlen;
25502393Syz155240 	icmphdr_t *orgicmp;
25512393Syz155240 	tcphdr_t *tcp = NULL;
25522393Syz155240 	u_short data[2];
25532393Syz155240 	nat_t *nat;
25542393Syz155240 	ip_t *oip;
25552393Syz155240 	u_int p;
25562393Syz155240 
25572393Syz155240 	/*
25582393Syz155240 	 * Does it at least have the return (basic) IP header ?
25592393Syz155240 	 * Only a basic IP header (no options) should be with an ICMP error
25602393Syz155240 	 * header.  Also, if it's not an error type, then return.
25612393Syz155240 	 */
25622393Syz155240 	if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR))
25632393Syz155240 		return NULL;
25642393Syz155240 
25652393Syz155240 	/*
25662393Syz155240 	 * Check packet size
25672393Syz155240 	 */
25682393Syz155240 	oip = (ip_t *)((char *)fin->fin_dp + 8);
25692393Syz155240 	minlen = IP_HL(oip) << 2;
25702393Syz155240 	if ((minlen < sizeof(ip_t)) ||
25712393Syz155240 	    (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen))
25722393Syz155240 		return NULL;
25732393Syz155240 	/*
25742393Syz155240 	 * Is the buffer big enough for all of it ?  It's the size of the IP
25752393Syz155240 	 * header claimed in the encapsulated part which is of concern.  It
25762393Syz155240 	 * may be too big to be in this buffer but not so big that it's
25772393Syz155240 	 * outside the ICMP packet, leading to TCP deref's causing problems.
25782393Syz155240 	 * This is possible because we don't know how big oip_hl is when we
25792393Syz155240 	 * do the pullup early in fr_check() and thus can't gaurantee it is
25802393Syz155240 	 * all here now.
25812393Syz155240 	 */
25822393Syz155240 #ifdef  _KERNEL
25832393Syz155240 	{
25842393Syz155240 	mb_t *m;
25852393Syz155240 
25862393Syz155240 	m = fin->fin_m;
25872393Syz155240 # if defined(MENTAT)
25882393Syz155240 	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr)
25892393Syz155240 		return NULL;
25902393Syz155240 # else
25912393Syz155240 	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
25922393Syz155240 	    (char *)fin->fin_ip + M_LEN(m))
25932393Syz155240 		return NULL;
25942393Syz155240 # endif
25952393Syz155240 	}
25962393Syz155240 #endif
25972393Syz155240 
25982393Syz155240 	if (fin->fin_daddr != oip->ip_src.s_addr)
25992393Syz155240 		return NULL;
26002393Syz155240 
26012393Syz155240 	p = oip->ip_p;
26022393Syz155240 	if (p == IPPROTO_TCP)
26032393Syz155240 		flags = IPN_TCP;
26042393Syz155240 	else if (p == IPPROTO_UDP)
26052393Syz155240 		flags = IPN_UDP;
26062393Syz155240 	else if (p == IPPROTO_ICMP) {
26072393Syz155240 		orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2));
26082393Syz155240 
26092393Syz155240 		/* see if this is related to an ICMP query */
26102393Syz155240 		if (nat_icmpquerytype4(orgicmp->icmp_type)) {
26112393Syz155240 			data[0] = fin->fin_data[0];
26122393Syz155240 			data[1] = fin->fin_data[1];
26132393Syz155240 			fin->fin_data[0] = 0;
26142393Syz155240 			fin->fin_data[1] = orgicmp->icmp_id;
26152393Syz155240 
26162393Syz155240 			flags = IPN_ICMPERR|IPN_ICMPQUERY;
26172393Syz155240 			/*
26182393Syz155240 			 * NOTE : dir refers to the direction of the original
26192393Syz155240 			 *        ip packet. By definition the icmp error
26202393Syz155240 			 *        message flows in the opposite direction.
26212393Syz155240 			 */
26222393Syz155240 			if (dir == NAT_INBOUND)
26232393Syz155240 				nat = nat_inlookup(fin, flags, p, oip->ip_dst,
26242393Syz155240 						   oip->ip_src);
26252393Syz155240 			else
26262393Syz155240 				nat = nat_outlookup(fin, flags, p, oip->ip_dst,
26272393Syz155240 						    oip->ip_src);
26282393Syz155240 			fin->fin_data[0] = data[0];
26292393Syz155240 			fin->fin_data[1] = data[1];
26302393Syz155240 			return nat;
26312393Syz155240 		}
26322393Syz155240 	}
26332393Syz155240 
26342393Syz155240 	if (flags & IPN_TCPUDP) {
26352393Syz155240 		minlen += 8;		/* + 64bits of data to get ports */
26362393Syz155240 		if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)
26372393Syz155240 			return NULL;
26382393Syz155240 
26392393Syz155240 		data[0] = fin->fin_data[0];
26402393Syz155240 		data[1] = fin->fin_data[1];
26412393Syz155240 		tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2));
26422393Syz155240 		fin->fin_data[0] = ntohs(tcp->th_dport);
26432393Syz155240 		fin->fin_data[1] = ntohs(tcp->th_sport);
26442393Syz155240 
26452393Syz155240 		if (dir == NAT_INBOUND) {
26462393Syz155240 			nat = nat_inlookup(fin, flags, p, oip->ip_dst,
26472393Syz155240 					   oip->ip_src);
26482393Syz155240 		} else {
26492393Syz155240 			nat = nat_outlookup(fin, flags, p, oip->ip_dst,
26502393Syz155240 					    oip->ip_src);
26512393Syz155240 		}
26522393Syz155240 		fin->fin_data[0] = data[0];
26532393Syz155240 		fin->fin_data[1] = data[1];
26542393Syz155240 		return nat;
26552393Syz155240 	}
26562393Syz155240 	if (dir == NAT_INBOUND)
26572393Syz155240 		return nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
26582393Syz155240 	else
26592393Syz155240 		return nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
26602393Syz155240 }
26612393Syz155240 
26622393Syz155240 
26632393Syz155240 /* ------------------------------------------------------------------------ */
26642393Syz155240 /* Function:    nat_icmperror                                               */
26652393Syz155240 /* Returns:     nat_t* - point to matching NAT structure                    */
26662393Syz155240 /* Parameters:  fin(I)    - pointer to packet information                   */
26672393Syz155240 /*              nflags(I) - NAT flags for this packet                       */
26682393Syz155240 /*              dir(I)    - direction of packet (in/out)                    */
26692393Syz155240 /*                                                                          */
26702393Syz155240 /* Fix up an ICMP packet which is an error message for an existing NAT      */
26712393Syz155240 /* session.  This will correct both packet header data and checksums.       */
26722393Syz155240 /*                                                                          */
26732393Syz155240 /* This should *ONLY* be used for incoming ICMP error packets to make sure  */
26742393Syz155240 /* a NAT'd ICMP packet gets correctly recognised.                           */
26752393Syz155240 /* ------------------------------------------------------------------------ */
26762393Syz155240 nat_t *nat_icmperror(fin, nflags, dir)
26772393Syz155240 fr_info_t *fin;
26782393Syz155240 u_int *nflags;
26792393Syz155240 int dir;
26802393Syz155240 {
26812693Sjojemann 	u_32_t sum1, sum2, sumd, psum1, psum2, psumd, sumd2;
26822393Syz155240 	struct in_addr in;
26832693Sjojemann 	icmphdr_t *icmp, *orgicmp;
26842693Sjojemann 	int dlen;
26852693Sjojemann 	udphdr_t *udp;
26862393Syz155240 	tcphdr_t *tcp;
26872393Syz155240 	nat_t *nat;
26882393Syz155240 	ip_t *oip;
26892393Syz155240 	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY)))
26902393Syz155240 		return NULL;
26912693Sjojemann 
26922393Syz155240 	/*
26932693Sjojemann 	 * nat_icmperrorlookup() looks up nat entry associated with the
26942693Sjojemann 	 * offending IP packet and returns pointer to the entry, or NULL
26952693Sjojemann 	 * if packet wasn't natted or for `defective' packets.
26962393Syz155240 	 */
26972693Sjojemann 
26982393Syz155240 	if ((fin->fin_v != 4) || !(nat = nat_icmperrorlookup(fin, dir)))
26992393Syz155240 		return NULL;
27002393Syz155240 
27012393Syz155240 	sumd2 = 0;
27022393Syz155240 	*nflags = IPN_ICMPERR;
27032393Syz155240 	icmp = fin->fin_dp;
27042393Syz155240 	oip = (ip_t *)&icmp->icmp_ip;
27052693Sjojemann 	udp = (udphdr_t *)((((char *)oip) + (IP_HL(oip) << 2)));
27062693Sjojemann 	tcp = (tcphdr_t *)udp;
27072693Sjojemann 	dlen = fin->fin_plen - ((char *)udp - (char *)fin->fin_ip);
27082393Syz155240 
27092393Syz155240 	/*
27102393Syz155240 	 * Need to adjust ICMP header to include the real IP#'s and
27112693Sjojemann 	 * port #'s.  There are three steps required.
27122693Sjojemann 	 *
27132393Syz155240 	 * Step 1
27142693Sjojemann 	 * Fix the IP addresses in the offending IP packet and update
27152693Sjojemann 	 * ip header checksum to compensate for the change.
27162393Syz155240 	 *
27172693Sjojemann 	 * No update needed here for icmp_cksum because the ICMP checksum
27182693Sjojemann 	 * is calculated over the complete ICMP packet, which includes the
27192693Sjojemann 	 * changed oip IP addresses and oip->ip_sum.  These two changes
27202693Sjojemann 	 * cancel each other out (if the delta for the IP address is x,
27212693Sjojemann 	 * then the delta for ip_sum is minus x).
27222393Syz155240 	 */
27232393Syz155240 
27242393Syz155240 	if (oip->ip_dst.s_addr == nat->nat_oip.s_addr) {
27252393Syz155240 		sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr));
27262393Syz155240 		in = nat->nat_inip;
27272393Syz155240 		oip->ip_src = in;
27282393Syz155240 	} else {
27292393Syz155240 		sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr));
27302393Syz155240 		in = nat->nat_outip;
27312393Syz155240 		oip->ip_dst = in;
27322393Syz155240 	}
27332393Syz155240 
27342393Syz155240 	sum2 = LONG_SUM(ntohl(in.s_addr));
27352393Syz155240 	CALC_SUMD(sum1, sum2, sumd);
27362393Syz155240 	fix_datacksum(&oip->ip_sum, sumd);
27372393Syz155240 
27382393Syz155240 	/*
27392693Sjojemann 	 * Step 2
27402693Sjojemann 	 * Perform other adjustments based on protocol of offending packet.
27412393Syz155240 	 */
27422693Sjojemann 
27432693Sjojemann 	switch (oip->ip_p) {
27442693Sjojemann 		case IPPROTO_TCP :
27452693Sjojemann 		case IPPROTO_UDP :
27462393Syz155240 
27472393Syz155240 			/*
27482693Sjojemann 			* For offending TCP/UDP IP packets, translate the ports
27492693Sjojemann 			* based on the NAT specification.
27502693Sjojemann 			*
27512693Sjojemann 			* Advance notice : Now it becomes complicated :-)
27522693Sjojemann 			*
27532693Sjojemann 			* Since the port and IP addresse fields are both part
27542693Sjojemann 			* of the TCP/UDP checksum of the offending IP packet,
27552693Sjojemann 			* we need to adjust that checksum as well.
27562693Sjojemann 			*
27572693Sjojemann 			* To further complicate things, the TCP/UDP checksum
27582693Sjojemann 			* may not be present.  We must check to see if the
27592693Sjojemann 			* length of the data portion is big enough to hold
27602693Sjojemann 			* the checksum.  In the UDP case, a test to determine
27612693Sjojemann 			* if the checksum is even set is also required.
27622693Sjojemann 			*
27632693Sjojemann 			* Any changes to an IP address, port or checksum within
27642693Sjojemann 			* the ICMP packet requires a change to icmp_cksum.
27652693Sjojemann 			*
27662693Sjojemann 			* Be extremely careful here ... The change is dependent
27672693Sjojemann 			* upon whether or not the TCP/UPD checksum is present.
27682693Sjojemann 			*
27692693Sjojemann 			* If TCP/UPD checksum is present, the icmp_cksum must
27702693Sjojemann 			* compensate for checksum modification resulting from
27712693Sjojemann 			* IP address change only.  Port change and resulting
27722693Sjojemann 			* data checksum adjustments cancel each other out.
27732693Sjojemann 			*
27742693Sjojemann 			* If TCP/UDP checksum is not present, icmp_cksum must
27752693Sjojemann 			* compensate for port change only.  The IP address
27762693Sjojemann 			* change does not modify anything else in this case.
27772693Sjojemann 			*/
27782693Sjojemann 
27792693Sjojemann 			psum1 = 0;
27802693Sjojemann 			psum2 = 0;
27812693Sjojemann 			psumd = 0;
27822693Sjojemann 
27832693Sjojemann 			if ((tcp->th_dport == nat->nat_oport) &&
27842693Sjojemann 			    (tcp->th_sport != nat->nat_inport)) {
27852393Syz155240 
27862393Syz155240 				/*
27872693Sjojemann 				 * Translate the source port.
27882693Sjojemann 				 */
27892693Sjojemann 
27902693Sjojemann 				psum1 = ntohs(tcp->th_sport);
27912693Sjojemann 				psum2 = ntohs(nat->nat_inport);
27922693Sjojemann 				tcp->th_sport = nat->nat_inport;
27932693Sjojemann 
27942693Sjojemann 			} else if ((tcp->th_sport == nat->nat_oport) &&
27952693Sjojemann 				    (tcp->th_dport != nat->nat_outport)) {
27962693Sjojemann 
27972693Sjojemann 				/*
27982693Sjojemann 				 * Translate the destination port.
27992393Syz155240 				 */
28002693Sjojemann 
28012693Sjojemann 				psum1 = ntohs(tcp->th_dport);
28022693Sjojemann 				psum2 = ntohs(nat->nat_outport);
28032693Sjojemann 				tcp->th_dport = nat->nat_outport;
28042693Sjojemann 			}
28052693Sjojemann 
28062693Sjojemann 			if ((oip->ip_p == IPPROTO_TCP) && (dlen >= 18)) {
28072693Sjojemann 
28082693Sjojemann 				/*
28092693Sjojemann 				 * TCP checksum present.
28102693Sjojemann 				 *
28112693Sjojemann 				 * Adjust data checksum and icmp checksum to
28122693Sjojemann 				 * compensate for any IP address change.
28132693Sjojemann 				 */
28142693Sjojemann 
28152693Sjojemann 				sum1 = ntohs(tcp->th_sum);
28162693Sjojemann 				fix_datacksum(&tcp->th_sum, sumd);
28172693Sjojemann 				sum2 = ntohs(tcp->th_sum);
28182693Sjojemann 				sumd2 = sumd << 1;
28192393Syz155240 				CALC_SUMD(sum1, sum2, sumd);
28202393Syz155240 				sumd2 += sumd;
28212693Sjojemann 
28222693Sjojemann 				/*
28232693Sjojemann 				 * Also make data checksum adjustment to
28242693Sjojemann 				 * compensate for any port change.
28252693Sjojemann 				 */
28262693Sjojemann 
28272693Sjojemann 				if (psum1 != psum2) {
28282693Sjojemann 					CALC_SUMD(psum1, psum2, psumd);
28292693Sjojemann 					fix_datacksum(&tcp->th_sum, psumd);
28302393Syz155240 				}
28312693Sjojemann 
28322693Sjojemann 			} else if ((oip->ip_p == IPPROTO_UDP) &&
28332693Sjojemann 				   (dlen >= 8) && (udp->uh_sum != 0)) {
28342693Sjojemann 
28352693Sjojemann 				/*
28362693Sjojemann 				 * The UDP checksum is present and set.
28372693Sjojemann 				 *
28382693Sjojemann 				 * Adjust data checksum and icmp checksum to
28392693Sjojemann 				 * compensate for any IP address change.
28402693Sjojemann 				 */
28412693Sjojemann 
28422693Sjojemann 				sum1 = ntohs(udp->uh_sum);
28432693Sjojemann 				fix_datacksum(&udp->uh_sum, sumd);
28442693Sjojemann 				sum2 = ntohs(udp->uh_sum);
28452693Sjojemann 				sumd2 = sumd << 1;
28462693Sjojemann 				CALC_SUMD(sum1, sum2, sumd);
28472393Syz155240 				sumd2 += sumd;
28482393Syz155240 
28492393Syz155240 				/*
28502693Sjojemann 				 * Also make data checksum adjustment to
28512693Sjojemann 				 * compensate for any port change.
28522393Syz155240 				 */
28532693Sjojemann 
28542693Sjojemann 				if (psum1 != psum2) {
28552693Sjojemann 					CALC_SUMD(psum1, psum2, psumd);
28562693Sjojemann 					fix_datacksum(&udp->uh_sum, psumd);
28572693Sjojemann 				}
28582693Sjojemann 
28592693Sjojemann 			} else {
28602693Sjojemann 
28612693Sjojemann 				/*
28622693Sjojemann 				 * Data checksum was not present.
28632693Sjojemann 				 *
28642693Sjojemann 				 * Compensate for any port change.
28652693Sjojemann 				 */
28662693Sjojemann 
28672693Sjojemann 				CALC_SUMD(psum2, psum1, psumd);
28682693Sjojemann 				sumd2 += psumd;
28692393Syz155240 			}
28702693Sjojemann 			break;
28712693Sjojemann 
28722693Sjojemann 		case IPPROTO_ICMP :
28732693Sjojemann 
28742693Sjojemann 			orgicmp = (icmphdr_t *)udp;
28752693Sjojemann 
28762693Sjojemann 			if ((nat->nat_dir == NAT_OUTBOUND) &&
28772693Sjojemann 			    (orgicmp->icmp_id != nat->nat_inport) &&
28782693Sjojemann 			    (dlen >= 8)) {
28792393Syz155240 
28802393Syz155240 				/*
28812393Syz155240 				 * Fix ICMP checksum (of the offening ICMP
28822393Syz155240 				 * query packet) to compensate the change
28832393Syz155240 				 * in the ICMP id of the offending ICMP
28842393Syz155240 				 * packet.
28852393Syz155240 				 *
28862393Syz155240 				 * Since you modify orgicmp->icmp_id with
28872393Syz155240 				 * a delta (say x) and you compensate that
28882393Syz155240 				 * in origicmp->icmp_cksum with a delta
28892393Syz155240 				 * minus x, you don't have to adjust the
28902393Syz155240 				 * overall icmp->icmp_cksum
28912393Syz155240 				 */
28922693Sjojemann 
28932393Syz155240 				sum1 = ntohs(orgicmp->icmp_id);
28942393Syz155240 				sum2 = ntohs(nat->nat_inport);
28952393Syz155240 				CALC_SUMD(sum1, sum2, sumd);
28962393Syz155240 				orgicmp->icmp_id = nat->nat_inport;
28972393Syz155240 				fix_datacksum(&orgicmp->icmp_cksum, sumd);
28982693Sjojemann 
28992693Sjojemann 			} /* nat_dir can't be NAT_INBOUND for icmp queries */
29002693Sjojemann 
29012693Sjojemann 			break;
29022693Sjojemann 
29032693Sjojemann 		default :
29042693Sjojemann 
29052693Sjojemann 			break;
29062693Sjojemann 
29072693Sjojemann 	} /* switch (oip->ip_p) */
29082693Sjojemann 
29092693Sjojemann 	/*
29102693Sjojemann 	 * Step 3
29112693Sjojemann 	 * Make the adjustments to icmp checksum.
29122693Sjojemann 	 */
29132693Sjojemann 
29142693Sjojemann 	if (sumd2 != 0) {
29152693Sjojemann 		sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
29162693Sjojemann 		sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
29172958Sdr146992 		fix_incksum(&icmp->icmp_cksum, sumd2);
29182393Syz155240 	}
29192393Syz155240 	return nat;
29202393Syz155240 }
29212393Syz155240 
29222393Syz155240 
29232393Syz155240 /*
29242393Syz155240  * NB: these lookups don't lock access to the list, it assumed that it has
29252393Syz155240  * already been done!
29262393Syz155240  */
29272393Syz155240 
29282393Syz155240 /* ------------------------------------------------------------------------ */
29292393Syz155240 /* Function:    nat_inlookup                                                */
29302393Syz155240 /* Returns:     nat_t* - NULL == no match,                                  */
29312393Syz155240 /*                       else pointer to matching NAT entry                 */
29322393Syz155240 /* Parameters:  fin(I)    - pointer to packet information                   */
29332393Syz155240 /*              flags(I)  - NAT flags for this packet                       */
29342393Syz155240 /*              p(I)      - protocol for this packet                        */
29352393Syz155240 /*              src(I)    - source IP address                               */
29362393Syz155240 /*              mapdst(I) - destination IP address                          */
29372393Syz155240 /*                                                                          */
29382393Syz155240 /* Lookup a nat entry based on the mapped destination ip address/port and   */
29392393Syz155240 /* real source address/port.  We use this lookup when receiving a packet,   */
29402393Syz155240 /* we're looking for a table entry, based on the destination address.       */
29412393Syz155240 /*                                                                          */
29422393Syz155240 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
29432393Syz155240 /*                                                                          */
29442393Syz155240 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
29452393Syz155240 /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
29462393Syz155240 /*                                                                          */
29472393Syz155240 /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
29482393Syz155240 /*            the packet is of said protocol                                */
29492393Syz155240 /* ------------------------------------------------------------------------ */
29502393Syz155240 nat_t *nat_inlookup(fin, flags, p, src, mapdst)
29512393Syz155240 fr_info_t *fin;
29522393Syz155240 u_int flags, p;
29532393Syz155240 struct in_addr src , mapdst;
29542393Syz155240 {
29552393Syz155240 	u_short sport, dport;
29562393Syz155240 	ipnat_t *ipn;
29572393Syz155240 	u_int sflags;
29582393Syz155240 	nat_t *nat;
29592393Syz155240 	int nflags;
29602393Syz155240 	u_32_t dst;
29612393Syz155240 	void *ifp;
29622393Syz155240 	u_int hv;
29633448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
29642393Syz155240 
29652393Syz155240 	if (fin != NULL)
29662393Syz155240 		ifp = fin->fin_ifp;
29672393Syz155240 	else
29682393Syz155240 		ifp = NULL;
29692393Syz155240 	sport = 0;
29702393Syz155240 	dport = 0;
29712393Syz155240 	dst = mapdst.s_addr;
29722393Syz155240 	sflags = flags & NAT_TCPUDPICMP;
29732393Syz155240 
29742393Syz155240 	switch (p)
29752393Syz155240 	{
29762393Syz155240 	case IPPROTO_TCP :
29772393Syz155240 	case IPPROTO_UDP :
29782393Syz155240 		sport = htons(fin->fin_data[0]);
29792393Syz155240 		dport = htons(fin->fin_data[1]);
29802393Syz155240 		break;
29812393Syz155240 	case IPPROTO_ICMP :
29822393Syz155240 		if (flags & IPN_ICMPERR)
29832393Syz155240 			sport = fin->fin_data[1];
29842393Syz155240 		else
29852393Syz155240 			dport = fin->fin_data[1];
29862393Syz155240 		break;
29872393Syz155240 	default :
29882393Syz155240 		break;
29892393Syz155240 	}
29902393Syz155240 
29912393Syz155240 
29922393Syz155240 	if ((flags & SI_WILDP) != 0)
29932393Syz155240 		goto find_in_wild_ports;
29942393Syz155240 
29952393Syz155240 	hv = NAT_HASH_FN(dst, dport, 0xffffffff);
29963448Sdh155122 	hv = NAT_HASH_FN(src.s_addr, hv + sport, ifs->ifs_ipf_nattable_sz);
29973448Sdh155122 	nat = ifs->ifs_nat_table[1][hv];
29982393Syz155240 	for (; nat; nat = nat->nat_hnext[1]) {
29992508Syz155240 		if (nat->nat_ifps[0] != NULL) {
30002508Syz155240 			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
30012508Syz155240 				continue;
30022508Syz155240 		} else if (ifp != NULL)
30032508Syz155240 			nat->nat_ifps[0] = ifp;
30042508Syz155240 
30052393Syz155240 		nflags = nat->nat_flags;
30062393Syz155240 
30072393Syz155240 		if (nat->nat_oip.s_addr == src.s_addr &&
30082393Syz155240 		    nat->nat_outip.s_addr == dst &&
30092393Syz155240 		    (((p == 0) &&
30102393Syz155240 		      (sflags == (nat->nat_flags & IPN_TCPUDPICMP)))
30112393Syz155240 		     || (p == nat->nat_p))) {
30122393Syz155240 			switch (p)
30132393Syz155240 			{
30142393Syz155240 #if 0
30152393Syz155240 			case IPPROTO_GRE :
30162393Syz155240 				if (nat->nat_call[1] != fin->fin_data[0])
30172393Syz155240 					continue;
30182393Syz155240 				break;
30192393Syz155240 #endif
30202393Syz155240 			case IPPROTO_ICMP :
30212393Syz155240 				if ((flags & IPN_ICMPERR) != 0) {
30222393Syz155240 					if (nat->nat_outport != sport)
30232393Syz155240 						continue;
30242393Syz155240 				} else {
30252393Syz155240 					if (nat->nat_outport != dport)
30262393Syz155240 						continue;
30272393Syz155240 				}
30282393Syz155240 				break;
30292393Syz155240 			case IPPROTO_TCP :
30302393Syz155240 			case IPPROTO_UDP :
30312393Syz155240 				if (nat->nat_oport != sport)
30322393Syz155240 					continue;
30332393Syz155240 				if (nat->nat_outport != dport)
30342393Syz155240 					continue;
30352393Syz155240 				break;
30362393Syz155240 			default :
30372393Syz155240 				break;
30382393Syz155240 			}
30392393Syz155240 
30402393Syz155240 			ipn = nat->nat_ptr;
30412393Syz155240 			if ((ipn != NULL) && (nat->nat_aps != NULL))
30422393Syz155240 				if (appr_match(fin, nat) != 0)
30432393Syz155240 					continue;
30442393Syz155240 			return nat;
30452393Syz155240 		}
30462393Syz155240 	}
30472393Syz155240 
30482393Syz155240 	/*
30492393Syz155240 	 * So if we didn't find it but there are wildcard members in the hash
30502393Syz155240 	 * table, go back and look for them.  We do this search and update here
30512393Syz155240 	 * because it is modifying the NAT table and we want to do this only
30522393Syz155240 	 * for the first packet that matches.  The exception, of course, is
30532393Syz155240 	 * for "dummy" (FI_IGNORE) lookups.
30542393Syz155240 	 */
30552393Syz155240 find_in_wild_ports:
30562393Syz155240 	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
30572393Syz155240 		return NULL;
30583448Sdh155122 	if (ifs->ifs_nat_stats.ns_wilds == 0)
30592393Syz155240 		return NULL;
30602393Syz155240 
30613448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
30622393Syz155240 
30632393Syz155240 	hv = NAT_HASH_FN(dst, 0, 0xffffffff);
30643448Sdh155122 	hv = NAT_HASH_FN(src.s_addr, hv, ifs->ifs_ipf_nattable_sz);
30653448Sdh155122 
30663448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
30673448Sdh155122 
30683448Sdh155122 	nat = ifs->ifs_nat_table[1][hv];
30692393Syz155240 	for (; nat; nat = nat->nat_hnext[1]) {
30702508Syz155240 		if (nat->nat_ifps[0] != NULL) {
30712508Syz155240 			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
30722508Syz155240 				continue;
30732508Syz155240 		} else if (ifp != NULL)
30742508Syz155240 			nat->nat_ifps[0] = ifp;
30752393Syz155240 
30762393Syz155240 		if (nat->nat_p != fin->fin_p)
30772393Syz155240 			continue;
30782393Syz155240 		if (nat->nat_oip.s_addr != src.s_addr ||
30792393Syz155240 		    nat->nat_outip.s_addr != dst)
30802393Syz155240 			continue;
30812393Syz155240 
30822393Syz155240 		nflags = nat->nat_flags;
30832393Syz155240 		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
30842393Syz155240 			continue;
30852393Syz155240 
30862393Syz155240 		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
30872393Syz155240 			       NAT_INBOUND) == 1) {
30882393Syz155240 			if ((fin->fin_flx & FI_IGNORE) != 0)
30892393Syz155240 				break;
30902393Syz155240 			if ((nflags & SI_CLONE) != 0) {
30912393Syz155240 				nat = fr_natclone(fin, nat);
30922393Syz155240 				if (nat == NULL)
30932393Syz155240 					break;
30942393Syz155240 			} else {
30953448Sdh155122 				MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
30963448Sdh155122 				ifs->ifs_nat_stats.ns_wilds--;
30973448Sdh155122 				MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
30982393Syz155240 			}
30992393Syz155240 			nat->nat_oport = sport;
31002393Syz155240 			nat->nat_outport = dport;
31012393Syz155240 			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
31023448Sdh155122 			nat_tabmove(nat, ifs);
31032393Syz155240 			break;
31042393Syz155240 		}
31052393Syz155240 	}
31062393Syz155240 
31073448Sdh155122 	MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
31082393Syz155240 
31092393Syz155240 	return nat;
31102393Syz155240 }
31112393Syz155240 
31122393Syz155240 
31132393Syz155240 /* ------------------------------------------------------------------------ */
31142393Syz155240 /* Function:    nat_tabmove                                                 */
31152393Syz155240 /* Returns:     Nil                                                         */
31162393Syz155240 /* Parameters:  nat(I) - pointer to NAT structure                           */
31172393Syz155240 /* Write Lock:  ipf_nat                                                     */
31182393Syz155240 /*                                                                          */
31192393Syz155240 /* This function is only called for TCP/UDP NAT table entries where the     */
31202393Syz155240 /* original was placed in the table without hashing on the ports and we now */
31212393Syz155240 /* want to include hashing on port numbers.                                 */
31222393Syz155240 /* ------------------------------------------------------------------------ */
31233448Sdh155122 static void nat_tabmove(nat, ifs)
31242393Syz155240 nat_t *nat;
31253448Sdh155122 ipf_stack_t *ifs;
31262393Syz155240 {
31272393Syz155240 	nat_t **natp;
31282393Syz155240 	u_int hv;
31292393Syz155240 
31302393Syz155240 	if (nat->nat_flags & SI_CLONE)
31312393Syz155240 		return;
31322393Syz155240 
31332393Syz155240 	/*
31342393Syz155240 	 * Remove the NAT entry from the old location
31352393Syz155240 	 */
31362393Syz155240 	if (nat->nat_hnext[0])
31372393Syz155240 		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
31382393Syz155240 	*nat->nat_phnext[0] = nat->nat_hnext[0];
31393448Sdh155122 	ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
31402393Syz155240 
31412393Syz155240 	if (nat->nat_hnext[1])
31422393Syz155240 		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
31432393Syz155240 	*nat->nat_phnext[1] = nat->nat_hnext[1];
31443448Sdh155122 	ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
31452393Syz155240 
31462393Syz155240 	/*
31472393Syz155240 	 * Add into the NAT table in the new position
31482393Syz155240 	 */
31492393Syz155240 	hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 0xffffffff);
31502393Syz155240 	hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport,
31513448Sdh155122 			 ifs->ifs_ipf_nattable_sz);
31522393Syz155240 	nat->nat_hv[0] = hv;
31533448Sdh155122 	natp = &ifs->ifs_nat_table[0][hv];
31542393Syz155240 	if (*natp)
31552393Syz155240 		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
31562393Syz155240 	nat->nat_phnext[0] = natp;
31572393Syz155240 	nat->nat_hnext[0] = *natp;
31582393Syz155240 	*natp = nat;
31593448Sdh155122 	ifs->ifs_nat_stats.ns_bucketlen[0][hv]++;
31602393Syz155240 
31612393Syz155240 	hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 0xffffffff);
31622393Syz155240 	hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport,
31633448Sdh155122 			 ifs->ifs_ipf_nattable_sz);
31642393Syz155240 	nat->nat_hv[1] = hv;
31653448Sdh155122 	natp = &ifs->ifs_nat_table[1][hv];
31662393Syz155240 	if (*natp)
31672393Syz155240 		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
31682393Syz155240 	nat->nat_phnext[1] = natp;
31692393Syz155240 	nat->nat_hnext[1] = *natp;
31702393Syz155240 	*natp = nat;
31713448Sdh155122 	ifs->ifs_nat_stats.ns_bucketlen[1][hv]++;
31722393Syz155240 }
31732393Syz155240 
31742393Syz155240 
31752393Syz155240 /* ------------------------------------------------------------------------ */
31762393Syz155240 /* Function:    nat_outlookup                                               */
31772393Syz155240 /* Returns:     nat_t* - NULL == no match,                                  */
31782393Syz155240 /*                       else pointer to matching NAT entry                 */
31792393Syz155240 /* Parameters:  fin(I)   - pointer to packet information                    */
31802393Syz155240 /*              flags(I) - NAT flags for this packet                        */
31812393Syz155240 /*              p(I)     - protocol for this packet                         */
31822393Syz155240 /*              src(I)   - source IP address                                */
31832393Syz155240 /*              dst(I)   - destination IP address                           */
31842393Syz155240 /*              rw(I)    - 1 == write lock on ipf_nat held, 0 == read lock. */
31852393Syz155240 /*                                                                          */
31862393Syz155240 /* Lookup a nat entry based on the source 'real' ip address/port and        */
31872393Syz155240 /* destination address/port.  We use this lookup when sending a packet out, */
31882393Syz155240 /* we're looking for a table entry, based on the source address.            */
31892393Syz155240 /*                                                                          */
31902393Syz155240 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
31912393Syz155240 /*                                                                          */
31922393Syz155240 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
31932393Syz155240 /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
31942393Syz155240 /*                                                                          */
31952393Syz155240 /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
31962393Syz155240 /*            the packet is of said protocol                                */
31972393Syz155240 /* ------------------------------------------------------------------------ */
31982393Syz155240 nat_t *nat_outlookup(fin, flags, p, src, dst)
31992393Syz155240 fr_info_t *fin;
32002393Syz155240 u_int flags, p;
32012393Syz155240 struct in_addr src , dst;
32022393Syz155240 {
32032393Syz155240 	u_short sport, dport;
32042393Syz155240 	u_int sflags;
32052393Syz155240 	ipnat_t *ipn;
32062393Syz155240 	u_32_t srcip;
32072393Syz155240 	nat_t *nat;
32082393Syz155240 	int nflags;
32092393Syz155240 	void *ifp;
32102393Syz155240 	u_int hv;
32113448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
32122508Syz155240 
32133894Sjojemann 	ifp = fin->fin_ifp;
32142508Syz155240 
32152393Syz155240 	srcip = src.s_addr;
32162393Syz155240 	sflags = flags & IPN_TCPUDPICMP;
32172393Syz155240 	sport = 0;
32182393Syz155240 	dport = 0;
32192393Syz155240 
32202393Syz155240 	switch (p)
32212393Syz155240 	{
32222393Syz155240 	case IPPROTO_TCP :
32232393Syz155240 	case IPPROTO_UDP :
32242393Syz155240 		sport = htons(fin->fin_data[0]);
32252393Syz155240 		dport = htons(fin->fin_data[1]);
32262393Syz155240 		break;
32272393Syz155240 	case IPPROTO_ICMP :
32282393Syz155240 		if (flags & IPN_ICMPERR)
32292393Syz155240 			sport = fin->fin_data[1];
32302393Syz155240 		else
32312393Syz155240 			dport = fin->fin_data[1];
32322393Syz155240 		break;
32332393Syz155240 	default :
32342393Syz155240 		break;
32352393Syz155240 	}
32362393Syz155240 
32372393Syz155240 	if ((flags & SI_WILDP) != 0)
32382393Syz155240 		goto find_out_wild_ports;
32392393Syz155240 
32402393Syz155240 	hv = NAT_HASH_FN(srcip, sport, 0xffffffff);
32413448Sdh155122 	hv = NAT_HASH_FN(dst.s_addr, hv + dport, ifs->ifs_ipf_nattable_sz);
32423448Sdh155122 	nat = ifs->ifs_nat_table[0][hv];
32432393Syz155240 	for (; nat; nat = nat->nat_hnext[0]) {
32442508Syz155240 		if (nat->nat_ifps[1] != NULL) {
32452508Syz155240 			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
32462508Syz155240 				continue;
32472508Syz155240 		} else if (ifp != NULL)
32482508Syz155240 			nat->nat_ifps[1] = ifp;
32492508Syz155240 
32502393Syz155240 		nflags = nat->nat_flags;
32512508Syz155240 
32522393Syz155240 		if (nat->nat_inip.s_addr == srcip &&
32532393Syz155240 		    nat->nat_oip.s_addr == dst.s_addr &&
32542393Syz155240 		    (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP)))
32552393Syz155240 		     || (p == nat->nat_p))) {
32562393Syz155240 			switch (p)
32572393Syz155240 			{
32582393Syz155240 #if 0
32592393Syz155240 			case IPPROTO_GRE :
32602393Syz155240 				if (nat->nat_call[1] != fin->fin_data[0])
32612393Syz155240 					continue;
32622393Syz155240 				break;
32632393Syz155240 #endif
32642393Syz155240 			case IPPROTO_TCP :
32652393Syz155240 			case IPPROTO_UDP :
32662393Syz155240 				if (nat->nat_oport != dport)
32672393Syz155240 					continue;
32682393Syz155240 				if (nat->nat_inport != sport)
32692393Syz155240 					continue;
32702393Syz155240 				break;
32712393Syz155240 			default :
32722393Syz155240 				break;
32732393Syz155240 			}
32742393Syz155240 
32752393Syz155240 			ipn = nat->nat_ptr;
32762393Syz155240 			if ((ipn != NULL) && (nat->nat_aps != NULL))
32772393Syz155240 				if (appr_match(fin, nat) != 0)
32782393Syz155240 					continue;
32792393Syz155240 			return nat;
32802393Syz155240 		}
32812393Syz155240 	}
32822393Syz155240 
32832393Syz155240 	/*
32842393Syz155240 	 * So if we didn't find it but there are wildcard members in the hash
32852393Syz155240 	 * table, go back and look for them.  We do this search and update here
32862393Syz155240 	 * because it is modifying the NAT table and we want to do this only
32872393Syz155240 	 * for the first packet that matches.  The exception, of course, is
32882393Syz155240 	 * for "dummy" (FI_IGNORE) lookups.
32892393Syz155240 	 */
32902393Syz155240 find_out_wild_ports:
32912393Syz155240 	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
32922393Syz155240 		return NULL;
32933448Sdh155122 	if (ifs->ifs_nat_stats.ns_wilds == 0)
32942393Syz155240 		return NULL;
32952393Syz155240 
32963448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
32972393Syz155240 
32982393Syz155240 	hv = NAT_HASH_FN(srcip, 0, 0xffffffff);
32993448Sdh155122 	hv = NAT_HASH_FN(dst.s_addr, hv, ifs->ifs_ipf_nattable_sz);
33003448Sdh155122 
33013448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
33023448Sdh155122 
33033448Sdh155122 	nat = ifs->ifs_nat_table[0][hv];
33042393Syz155240 	for (; nat; nat = nat->nat_hnext[0]) {
33052508Syz155240 		if (nat->nat_ifps[1] != NULL) {
33062508Syz155240 			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
33073448Sdh155122 				continue;
33082508Syz155240 		} else if (ifp != NULL)
33092508Syz155240 			nat->nat_ifps[1] = ifp;
33102393Syz155240 
33112393Syz155240 		if (nat->nat_p != fin->fin_p)
33122393Syz155240 			continue;
33132393Syz155240 		if ((nat->nat_inip.s_addr != srcip) ||
33142393Syz155240 		    (nat->nat_oip.s_addr != dst.s_addr))
33152393Syz155240 			continue;
33162393Syz155240 
33172393Syz155240 		nflags = nat->nat_flags;
33182393Syz155240 		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
33192393Syz155240 			continue;
33202393Syz155240 
33212393Syz155240 		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
33222393Syz155240 			       NAT_OUTBOUND) == 1) {
33232393Syz155240 			if ((fin->fin_flx & FI_IGNORE) != 0)
33242393Syz155240 				break;
33252393Syz155240 			if ((nflags & SI_CLONE) != 0) {
33262393Syz155240 				nat = fr_natclone(fin, nat);
33272393Syz155240 				if (nat == NULL)
33282393Syz155240 					break;
33292393Syz155240 			} else {
33303448Sdh155122 				MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
33313448Sdh155122 				ifs->ifs_nat_stats.ns_wilds--;
33323448Sdh155122 				MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
33332393Syz155240 			}
33342393Syz155240 			nat->nat_inport = sport;
33352393Syz155240 			nat->nat_oport = dport;
33362393Syz155240 			if (nat->nat_outport == 0)
33372393Syz155240 				nat->nat_outport = sport;
33382393Syz155240 			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
33393448Sdh155122 			nat_tabmove(nat, ifs);
33402393Syz155240 			break;
33412393Syz155240 		}
33422393Syz155240 	}
33432393Syz155240 
33443448Sdh155122 	MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
33452393Syz155240 
33462393Syz155240 	return nat;
33472393Syz155240 }
33482393Syz155240 
33492393Syz155240 
33502393Syz155240 /* ------------------------------------------------------------------------ */
33512393Syz155240 /* Function:    nat_lookupredir                                             */
33522393Syz155240 /* Returns:     nat_t* - NULL == no match,                                  */
33532393Syz155240 /*                       else pointer to matching NAT entry                 */
33542393Syz155240 /* Parameters:  np(I) - pointer to description of packet to find NAT table  */
33552393Syz155240 /*                      entry for.                                          */
33562393Syz155240 /*                                                                          */
33572393Syz155240 /* Lookup the NAT tables to search for a matching redirect                  */
33582393Syz155240 /* ------------------------------------------------------------------------ */
33593448Sdh155122 nat_t *nat_lookupredir(np, ifs)
33602393Syz155240 natlookup_t *np;
33613448Sdh155122 ipf_stack_t *ifs;
33622393Syz155240 {
33632393Syz155240 	fr_info_t fi;
33642393Syz155240 	nat_t *nat;
33652393Syz155240 
33662393Syz155240 	bzero((char *)&fi, sizeof(fi));
33672393Syz155240 	if (np->nl_flags & IPN_IN) {
33682393Syz155240 		fi.fin_data[0] = ntohs(np->nl_realport);
33692393Syz155240 		fi.fin_data[1] = ntohs(np->nl_outport);
33702393Syz155240 	} else {
33712393Syz155240 		fi.fin_data[0] = ntohs(np->nl_inport);
33722393Syz155240 		fi.fin_data[1] = ntohs(np->nl_outport);
33732393Syz155240 	}
33742393Syz155240 	if (np->nl_flags & IPN_TCP)
33752393Syz155240 		fi.fin_p = IPPROTO_TCP;
33762393Syz155240 	else if (np->nl_flags & IPN_UDP)
33772393Syz155240 		fi.fin_p = IPPROTO_UDP;
33782393Syz155240 	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
33792393Syz155240 		fi.fin_p = IPPROTO_ICMP;
33802393Syz155240 
33813448Sdh155122 	fi.fin_ifs = ifs;
33822393Syz155240 	/*
33832393Syz155240 	 * We can do two sorts of lookups:
33842393Syz155240 	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
33852393Syz155240 	 * - default: we have the `in' and `out' address, look for `real'.
33862393Syz155240 	 */
33872393Syz155240 	if (np->nl_flags & IPN_IN) {
33882393Syz155240 		if ((nat = nat_inlookup(&fi, np->nl_flags, fi.fin_p,
33892393Syz155240 					np->nl_realip, np->nl_outip))) {
33902393Syz155240 			np->nl_inip = nat->nat_inip;
33912393Syz155240 			np->nl_inport = nat->nat_inport;
33922393Syz155240 		}
33932393Syz155240 	} else {
33942393Syz155240 		/*
33952393Syz155240 		 * If nl_inip is non null, this is a lookup based on the real
33962393Syz155240 		 * ip address. Else, we use the fake.
33972393Syz155240 		 */
33982393Syz155240 		if ((nat = nat_outlookup(&fi, np->nl_flags, fi.fin_p,
33992393Syz155240 					 np->nl_inip, np->nl_outip))) {
34002393Syz155240 
34012393Syz155240 			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
34022393Syz155240 				fr_info_t fin;
34032393Syz155240 				bzero((char *)&fin, sizeof(fin));
34042393Syz155240 				fin.fin_p = nat->nat_p;
34052393Syz155240 				fin.fin_data[0] = ntohs(nat->nat_outport);
34062393Syz155240 				fin.fin_data[1] = ntohs(nat->nat_oport);
34073448Sdh155122 				fin.fin_ifs = ifs;
34082393Syz155240 				if (nat_inlookup(&fin, np->nl_flags, fin.fin_p,
34092393Syz155240 						 nat->nat_outip,
34102393Syz155240 						 nat->nat_oip) != NULL) {
34112393Syz155240 					np->nl_flags &= ~IPN_FINDFORWARD;
34122393Syz155240 				}
34132393Syz155240 			}
34142393Syz155240 
34152393Syz155240 			np->nl_realip = nat->nat_outip;
34162393Syz155240 			np->nl_realport = nat->nat_outport;
34172393Syz155240 		}
34182393Syz155240  	}
34192393Syz155240 
34202393Syz155240 	return nat;
34212393Syz155240 }
34222393Syz155240 
34232393Syz155240 
34242393Syz155240 /* ------------------------------------------------------------------------ */
34252393Syz155240 /* Function:    nat_match                                                   */
34262393Syz155240 /* Returns:     int - 0 == no match, 1 == match                             */
34272393Syz155240 /* Parameters:  fin(I)   - pointer to packet information                    */
34282393Syz155240 /*              np(I)    - pointer to NAT rule                              */
34292393Syz155240 /*                                                                          */
34302393Syz155240 /* Pull the matching of a packet against a NAT rule out of that complex     */
34312393Syz155240 /* loop inside fr_checknatin() and lay it out properly in its own function. */
34322393Syz155240 /* ------------------------------------------------------------------------ */
34332393Syz155240 static int nat_match(fin, np)
34342393Syz155240 fr_info_t *fin;
34352393Syz155240 ipnat_t *np;
34362393Syz155240 {
34372393Syz155240 	frtuc_t *ft;
34382393Syz155240 
34392393Syz155240 	if (fin->fin_v != 4)
34402393Syz155240 		return 0;
34412393Syz155240 
34422393Syz155240 	if (np->in_p && fin->fin_p != np->in_p)
34432393Syz155240 		return 0;
34442393Syz155240 
34452393Syz155240 	if (fin->fin_out) {
34462393Syz155240 		if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK)))
34472393Syz155240 			return 0;
34482393Syz155240 		if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip)
34492393Syz155240 		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
34502393Syz155240 			return 0;
34512393Syz155240 		if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip)
34522393Syz155240 		    ^ ((np->in_flags & IPN_NOTDST) != 0))
34532393Syz155240 			return 0;
34542393Syz155240 	} else {
34552393Syz155240 		if (!(np->in_redir & NAT_REDIRECT))
34562393Syz155240 			return 0;
34572393Syz155240 		if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip)
34582393Syz155240 		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
34592393Syz155240 			return 0;
34602393Syz155240 		if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip)
34612393Syz155240 		    ^ ((np->in_flags & IPN_NOTDST) != 0))
34622393Syz155240 			return 0;
34632393Syz155240 	}
34642393Syz155240 
34652393Syz155240 	ft = &np->in_tuc;
34662393Syz155240 	if (!(fin->fin_flx & FI_TCPUDP) ||
34672393Syz155240 	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
34682393Syz155240 		if (ft->ftu_scmp || ft->ftu_dcmp)
34692393Syz155240 			return 0;
34702393Syz155240 		return 1;
34712393Syz155240 	}
34722393Syz155240 
34732393Syz155240 	return fr_tcpudpchk(fin, ft);
34742393Syz155240 }
34752393Syz155240 
34762393Syz155240 
34772393Syz155240 /* ------------------------------------------------------------------------ */
34782393Syz155240 /* Function:    nat_update                                                  */
34792393Syz155240 /* Returns:     Nil                                                         */
34802393Syz155240 /* Parameters:  nat(I)    - pointer to NAT structure                        */
34812393Syz155240 /*              np(I)     - pointer to NAT rule                             */
34822393Syz155240 /*                                                                          */
34832393Syz155240 /* Updates the lifetime of a NAT table entry for non-TCP packets.  Must be  */
34842393Syz155240 /* called with fin_rev updated - i.e. after calling nat_proto().            */
34852393Syz155240 /* ------------------------------------------------------------------------ */
34862393Syz155240 void nat_update(fin, nat, np)
34872393Syz155240 fr_info_t *fin;
34882393Syz155240 nat_t *nat;
34892393Syz155240 ipnat_t *np;
34902393Syz155240 {
34912393Syz155240 	ipftq_t *ifq, *ifq2;
34922393Syz155240 	ipftqent_t *tqe;
34933448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
34942393Syz155240 
34952393Syz155240 	MUTEX_ENTER(&nat->nat_lock);
34962393Syz155240 	tqe = &nat->nat_tqe;
34972393Syz155240 	ifq = tqe->tqe_ifq;
34982393Syz155240 
34992393Syz155240 	/*
35002393Syz155240 	 * We allow over-riding of NAT timeouts from NAT rules, even for
35012393Syz155240 	 * TCP, however, if it is TCP and there is no rule timeout set,
35022393Syz155240 	 * then do not update the timeout here.
35032393Syz155240 	 */
35042393Syz155240 	if (np != NULL)
35052393Syz155240 		ifq2 = np->in_tqehead[fin->fin_rev];
35062393Syz155240 	else
35072393Syz155240 		ifq2 = NULL;
35082393Syz155240 
35092393Syz155240 	if (nat->nat_p == IPPROTO_TCP && ifq2 == NULL) {
35103448Sdh155122 		(void) fr_tcp_age(&nat->nat_tqe, fin, ifs->ifs_nat_tqb, 0);
35112393Syz155240 	} else {
35122393Syz155240 		if (ifq2 == NULL) {
35132393Syz155240 			if (nat->nat_p == IPPROTO_UDP)
35143448Sdh155122 				ifq2 = &ifs->ifs_nat_udptq;
35152393Syz155240 			else if (nat->nat_p == IPPROTO_ICMP)
35163448Sdh155122 				ifq2 = &ifs->ifs_nat_icmptq;
35172393Syz155240 			else
35183448Sdh155122 				ifq2 = &ifs->ifs_nat_iptq;
35192393Syz155240 		}
35202393Syz155240 
35213448Sdh155122 		fr_movequeue(tqe, ifq, ifq2, ifs);
35222393Syz155240 	}
35232393Syz155240 	MUTEX_EXIT(&nat->nat_lock);
35242393Syz155240 }
35252393Syz155240 
35262393Syz155240 
35272393Syz155240 /* ------------------------------------------------------------------------ */
35282393Syz155240 /* Function:    fr_checknatout                                              */
35292393Syz155240 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
35302393Syz155240 /*                     0 == no packet translation occurred,                 */
35312393Syz155240 /*                     1 == packet was successfully translated.             */
35322393Syz155240 /* Parameters:  fin(I)   - pointer to packet information                    */
35332393Syz155240 /*              passp(I) - pointer to filtering result flags                */
35342393Syz155240 /*                                                                          */
35352393Syz155240 /* Check to see if an outcoming packet should be changed.  ICMP packets are */
35362393Syz155240 /* first checked to see if they match an existing entry (if an error),      */
35372393Syz155240 /* otherwise a search of the current NAT table is made.  If neither results */
35382393Syz155240 /* in a match then a search for a matching NAT rule is made.  Create a new  */
35392393Syz155240 /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
35402393Syz155240 /* packet header(s) as required.                                            */
35412393Syz155240 /* ------------------------------------------------------------------------ */
35422393Syz155240 int fr_checknatout(fin, passp)
35432393Syz155240 fr_info_t *fin;
35442393Syz155240 u_32_t *passp;
35452393Syz155240 {
35462393Syz155240 	struct ifnet *ifp, *sifp;
35472393Syz155240 	icmphdr_t *icmp = NULL;
35482393Syz155240 	tcphdr_t *tcp = NULL;
35492393Syz155240 	int rval, natfailed;
35502393Syz155240 	ipnat_t *np = NULL;
35512393Syz155240 	u_int nflags = 0;
35522393Syz155240 	u_32_t ipa, iph;
35532393Syz155240 	int natadd = 1;
35542393Syz155240 	frentry_t *fr;
35552393Syz155240 	nat_t *nat;
35563448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
35573448Sdh155122 
35583448Sdh155122 	if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0)
35592393Syz155240 		return 0;
35602393Syz155240 
35612393Syz155240 	natfailed = 0;
35622393Syz155240 	fr = fin->fin_fr;
35632393Syz155240 	sifp = fin->fin_ifp;
35642393Syz155240 	if ((fr != NULL) && !(fr->fr_flags & FR_DUP) &&
35653894Sjojemann 	    fr->fr_tifs[fin->fin_rev].fd_ifp &&
35663894Sjojemann 	    fr->fr_tifs[fin->fin_rev].fd_ifp != (void *)-1)
35673894Sjojemann 		fin->fin_ifp = fr->fr_tifs[fin->fin_rev].fd_ifp;
35682393Syz155240 	ifp = fin->fin_ifp;
35692393Syz155240 
35702393Syz155240 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
35712393Syz155240 		switch (fin->fin_p)
35722393Syz155240 		{
35732393Syz155240 		case IPPROTO_TCP :
35742393Syz155240 			nflags = IPN_TCP;
35752393Syz155240 			break;
35762393Syz155240 		case IPPROTO_UDP :
35772393Syz155240 			nflags = IPN_UDP;
35782393Syz155240 			break;
35792393Syz155240 		case IPPROTO_ICMP :
35802393Syz155240 			icmp = fin->fin_dp;
35812393Syz155240 
35822393Syz155240 			/*
35832393Syz155240 			 * This is an incoming packet, so the destination is
35842393Syz155240 			 * the icmp_id and the source port equals 0
35852393Syz155240 			 */
35862393Syz155240 			if (nat_icmpquerytype4(icmp->icmp_type))
35872393Syz155240 				nflags = IPN_ICMPQUERY;
35882393Syz155240 			break;
35892393Syz155240 		default :
35902393Syz155240 			break;
35912393Syz155240 		}
35922393Syz155240 
35932393Syz155240 		if ((nflags & IPN_TCPUDP))
35942393Syz155240 			tcp = fin->fin_dp;
35952393Syz155240 	}
35962393Syz155240 
35972393Syz155240 	ipa = fin->fin_saddr;
35982393Syz155240 
35993448Sdh155122 	READ_ENTER(&ifs->ifs_ipf_nat);
36002393Syz155240 
36012393Syz155240 	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
36022393Syz155240 	    (nat = nat_icmperror(fin, &nflags, NAT_OUTBOUND)))
36032393Syz155240 		/*EMPTY*/;
36042393Syz155240 	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
36052393Syz155240 		natadd = 0;
36062393Syz155240 	else if ((nat = nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p,
36072393Syz155240 				      fin->fin_src, fin->fin_dst))) {
36082393Syz155240 		nflags = nat->nat_flags;
36092393Syz155240 	} else {
36102393Syz155240 		u_32_t hv, msk, nmsk;
36112393Syz155240 
36122393Syz155240 		/*
36132393Syz155240 		 * If there is no current entry in the nat table for this IP#,
36142393Syz155240 		 * create one for it (if there is a matching rule).
36152393Syz155240 		 */
36163448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
36172393Syz155240 		msk = 0xffffffff;
36183448Sdh155122 		nmsk = ifs->ifs_nat_masks;
36193448Sdh155122 		WRITE_ENTER(&ifs->ifs_ipf_nat);
36202393Syz155240 maskloop:
36212393Syz155240 		iph = ipa & htonl(msk);
36223448Sdh155122 		hv = NAT_HASH_FN(iph, 0, ifs->ifs_ipf_natrules_sz);
36233448Sdh155122 		for (np = ifs->ifs_nat_rules[hv]; np; np = np->in_mnext)
36242393Syz155240 		{
36252508Syz155240 			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
36262393Syz155240 				continue;
36272393Syz155240 			if (np->in_v != fin->fin_v)
36282393Syz155240 				continue;
36292393Syz155240 			if (np->in_p && (np->in_p != fin->fin_p))
36302393Syz155240 				continue;
36312393Syz155240 			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
36322393Syz155240 				continue;
36332393Syz155240 			if (np->in_flags & IPN_FILTER) {
36342393Syz155240 				if (!nat_match(fin, np))
36352393Syz155240 					continue;
36362393Syz155240 			} else if ((ipa & np->in_inmsk) != np->in_inip)
36372393Syz155240 				continue;
36382393Syz155240 
36392393Syz155240 			if ((fr != NULL) &&
36402393Syz155240 			    !fr_matchtag(&np->in_tag, &fr->fr_nattag))
36412393Syz155240 				continue;
36422393Syz155240 
36432393Syz155240 			if (*np->in_plabel != '\0') {
36442393Syz155240 				if (((np->in_flags & IPN_FILTER) == 0) &&
36452393Syz155240 				    (np->in_dport != tcp->th_dport))
36462393Syz155240 					continue;
36472393Syz155240 				if (appr_ok(fin, tcp, np) == 0)
36482393Syz155240 					continue;
36492393Syz155240 			}
36502393Syz155240 
36512393Syz155240 			if ((nat = nat_new(fin, np, NULL, nflags,
36522393Syz155240 					   NAT_OUTBOUND))) {
36532393Syz155240 				np->in_hits++;
36542393Syz155240 				break;
36552393Syz155240 			} else
36562393Syz155240 				natfailed = -1;
36572393Syz155240 		}
36582393Syz155240 		if ((np == NULL) && (nmsk != 0)) {
36592393Syz155240 			while (nmsk) {
36602393Syz155240 				msk <<= 1;
36612393Syz155240 				if (nmsk & 0x80000000)
36622393Syz155240 					break;
36632393Syz155240 				nmsk <<= 1;
36642393Syz155240 			}
36652393Syz155240 			if (nmsk != 0) {
36662393Syz155240 				nmsk <<= 1;
36672393Syz155240 				goto maskloop;
36682393Syz155240 			}
36692393Syz155240 		}
36703448Sdh155122 		MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
36712393Syz155240 	}
36722393Syz155240 
36732393Syz155240 	if (nat != NULL) {
36742393Syz155240 		rval = fr_natout(fin, nat, natadd, nflags);
36752393Syz155240 		if (rval == 1) {
36762393Syz155240 			MUTEX_ENTER(&nat->nat_lock);
36772393Syz155240 			nat->nat_ref++;
36782393Syz155240 			MUTEX_EXIT(&nat->nat_lock);
36792393Syz155240 			fin->fin_nat = nat;
36802393Syz155240 		}
36812393Syz155240 	} else
36822393Syz155240 		rval = natfailed;
36833448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
36842393Syz155240 
36852393Syz155240 	if (rval == -1) {
36862393Syz155240 		if (passp != NULL)
36872393Syz155240 			*passp = FR_BLOCK;
36882393Syz155240 		fin->fin_flx |= FI_BADNAT;
36892393Syz155240 	}
36902393Syz155240 	fin->fin_ifp = sifp;
36912393Syz155240 	return rval;
36922393Syz155240 }
36932393Syz155240 
36942393Syz155240 /* ------------------------------------------------------------------------ */
36952393Syz155240 /* Function:    fr_natout                                                   */
36962393Syz155240 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
36972393Syz155240 /*                     1 == packet was successfully translated.             */
36982393Syz155240 /* Parameters:  fin(I)    - pointer to packet information                   */
36992393Syz155240 /*              nat(I)    - pointer to NAT structure                        */
37002393Syz155240 /*              natadd(I) - flag indicating if it is safe to add frag cache */
37012393Syz155240 /*              nflags(I) - NAT flags set for this packet                   */
37022393Syz155240 /*                                                                          */
37032393Syz155240 /* Translate a packet coming "out" on an interface.                         */
37042393Syz155240 /* ------------------------------------------------------------------------ */
37052393Syz155240 int fr_natout(fin, nat, natadd, nflags)
37062393Syz155240 fr_info_t *fin;
37072393Syz155240 nat_t *nat;
37082393Syz155240 int natadd;
37092393Syz155240 u_32_t nflags;
37102393Syz155240 {
37112393Syz155240 	icmphdr_t *icmp;
37122393Syz155240 	u_short *csump;
37132958Sdr146992 	u_32_t sumd;
37142393Syz155240 	tcphdr_t *tcp;
37152393Syz155240 	ipnat_t *np;
37162393Syz155240 	int i;
37173448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
37182393Syz155240 
37192958Sdr146992 #if SOLARIS && defined(_KERNEL)
37202958Sdr146992 	net_data_t net_data_p;
37212958Sdr146992 	if (fin->fin_v == 4)
37223448Sdh155122 		net_data_p = ifs->ifs_ipf_ipv4;
37232958Sdr146992 	else
37243448Sdh155122 		net_data_p = ifs->ifs_ipf_ipv6;
37252958Sdr146992 #endif
37262958Sdr146992 
37272393Syz155240 	tcp = NULL;
37282393Syz155240 	icmp = NULL;
37292393Syz155240 	csump = NULL;
37302393Syz155240 	np = nat->nat_ptr;
37312393Syz155240 
37322393Syz155240 	if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL))
37332393Syz155240 		(void) fr_nat_newfrag(fin, 0, nat);
37342393Syz155240 
37352393Syz155240 	MUTEX_ENTER(&nat->nat_lock);
37362393Syz155240 	nat->nat_bytes[1] += fin->fin_plen;
37372393Syz155240 	nat->nat_pkts[1]++;
37382393Syz155240 	MUTEX_EXIT(&nat->nat_lock);
37392958Sdr146992 
37402393Syz155240 	/*
37412393Syz155240 	 * Fix up checksums, not by recalculating them, but
37422393Syz155240 	 * simply computing adjustments.
37432393Syz155240 	 * This is only done for STREAMS based IP implementations where the
37442393Syz155240 	 * checksum has already been calculated by IP.  In all other cases,
37452393Syz155240 	 * IPFilter is called before the checksum needs calculating so there
37462393Syz155240 	 * is no call to modify whatever is in the header now.
37472393Syz155240 	 */
37482958Sdr146992 	ASSERT(fin->fin_m != NULL);
37492958Sdr146992 	if (fin->fin_v == 4 && !NET_IS_HCK_L3_FULL(net_data_p, fin->fin_m)) {
37502393Syz155240 		if (nflags == IPN_ICMPERR) {
37512958Sdr146992 			u_32_t s1, s2;
37522393Syz155240 
37532393Syz155240 			s1 = LONG_SUM(ntohl(fin->fin_saddr));
37542393Syz155240 			s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr));
37552393Syz155240 			CALC_SUMD(s1, s2, sumd);
37562958Sdr146992 
37572958Sdr146992 			fix_outcksum(&fin->fin_ip->ip_sum, sumd);
37582393Syz155240 		}
37592393Syz155240 #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
37602393Syz155240     defined(linux) || defined(BRIDGE_IPF)
37612393Syz155240 		else {
37622393Syz155240 			/*
37632393Syz155240 			 * Strictly speaking, this isn't necessary on BSD
37642393Syz155240 			 * kernels because they do checksum calculation after
37652393Syz155240 			 * this code has run BUT if ipfilter is being used
37662393Syz155240 			 * to do NAT as a bridge, that code doesn't exist.
37672393Syz155240 			 */
37682393Syz155240 			if (nat->nat_dir == NAT_OUTBOUND)
37692958Sdr146992 				fix_outcksum(&fin->fin_ip->ip_sum,
37702958Sdr146992 					    nat->nat_ipsumd);
37712393Syz155240 			else
37722958Sdr146992 				fix_incksum(&fin->fin_ip->ip_sum,
37732958Sdr146992 				 	   nat->nat_ipsumd);
37742393Syz155240 		}
37752393Syz155240 #endif
37762393Syz155240 	}
37772393Syz155240 
37782393Syz155240 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
37792393Syz155240 		if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) {
37802393Syz155240 			tcp = fin->fin_dp;
37812393Syz155240 
37822393Syz155240 			tcp->th_sport = nat->nat_outport;
37832393Syz155240 			fin->fin_data[0] = ntohs(nat->nat_outport);
37842393Syz155240 		}
37852393Syz155240 
37862393Syz155240 		if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) {
37872393Syz155240 			icmp = fin->fin_dp;
37882393Syz155240 			icmp->icmp_id = nat->nat_outport;
37892393Syz155240 		}
37902393Syz155240 
37912393Syz155240 		csump = nat_proto(fin, nat, nflags);
37922393Syz155240 	}
37932393Syz155240 
37942393Syz155240 	fin->fin_ip->ip_src = nat->nat_outip;
37952393Syz155240 
37962393Syz155240 	nat_update(fin, nat, np);
37972393Syz155240 
37982393Syz155240 	/*
37992393Syz155240 	 * The above comments do not hold for layer 4 (or higher) checksums...
38002393Syz155240 	 */
38012958Sdr146992 	if (csump != NULL && !NET_IS_HCK_L4_FULL(net_data_p, fin->fin_m)) {
38022958Sdr146992 		if (nflags & IPN_TCPUDP &&
38032958Sdr146992 	   	    NET_IS_HCK_L4_PART(net_data_p, fin->fin_m))
38042958Sdr146992 			sumd = nat->nat_sumd[1];
38052958Sdr146992 		else
38062958Sdr146992 			sumd = nat->nat_sumd[0];
38072958Sdr146992 
38082393Syz155240 		if (nat->nat_dir == NAT_OUTBOUND)
38092958Sdr146992 			fix_outcksum(csump, sumd);
38102393Syz155240 		else
38112958Sdr146992 			fix_incksum(csump, sumd);
38122393Syz155240 	}
38132393Syz155240 #ifdef	IPFILTER_SYNC
38142393Syz155240 	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
38152393Syz155240 #endif
38162393Syz155240 	/* ------------------------------------------------------------- */
38172393Syz155240 	/* A few quick notes:						 */
38182393Syz155240 	/*	Following are test conditions prior to calling the 	 */
38192393Syz155240 	/*	appr_check routine.					 */
38202393Syz155240 	/*								 */
38212393Syz155240 	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
38222393Syz155240 	/*	with a redirect rule, we attempt to match the packet's	 */
38232393Syz155240 	/*	source port against in_dport, otherwise	we'd compare the */
38242393Syz155240 	/*	packet's destination.			 		 */
38252393Syz155240 	/* ------------------------------------------------------------- */
38262393Syz155240 	if ((np != NULL) && (np->in_apr != NULL)) {
38272393Syz155240 		i = appr_check(fin, nat);
38282393Syz155240 		if (i == 0)
38292393Syz155240 			i = 1;
38302393Syz155240 	} else
38312393Syz155240 		i = 1;
38323448Sdh155122 	ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[1]);
38332393Syz155240 	fin->fin_flx |= FI_NATED;
38342393Syz155240 	return i;
38352393Syz155240 }
38362393Syz155240 
38372393Syz155240 
38382393Syz155240 /* ------------------------------------------------------------------------ */
38392393Syz155240 /* Function:    fr_checknatin                                               */
38402393Syz155240 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
38412393Syz155240 /*                     0 == no packet translation occurred,                 */
38422393Syz155240 /*                     1 == packet was successfully translated.             */
38432393Syz155240 /* Parameters:  fin(I)   - pointer to packet information                    */
38442393Syz155240 /*              passp(I) - pointer to filtering result flags                */
38452393Syz155240 /*                                                                          */
38462393Syz155240 /* Check to see if an incoming packet should be changed.  ICMP packets are  */
38472393Syz155240 /* first checked to see if they match an existing entry (if an error),      */
38482393Syz155240 /* otherwise a search of the current NAT table is made.  If neither results */
38492393Syz155240 /* in a match then a search for a matching NAT rule is made.  Create a new  */
38502393Syz155240 /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
38512393Syz155240 /* packet header(s) as required.                                            */
38522393Syz155240 /* ------------------------------------------------------------------------ */
38532393Syz155240 int fr_checknatin(fin, passp)
38542393Syz155240 fr_info_t *fin;
38552393Syz155240 u_32_t *passp;
38562393Syz155240 {
38572393Syz155240 	u_int nflags, natadd;
38582393Syz155240 	int rval, natfailed;
38592393Syz155240 	struct ifnet *ifp;
38602393Syz155240 	struct in_addr in;
38612393Syz155240 	icmphdr_t *icmp;
38622393Syz155240 	tcphdr_t *tcp;
38632393Syz155240 	u_short dport;
38642393Syz155240 	ipnat_t *np;
38652393Syz155240 	nat_t *nat;
38662393Syz155240 	u_32_t iph;
38673448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
38683448Sdh155122 
38693448Sdh155122 	if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0)
38702393Syz155240 		return 0;
38712393Syz155240 
38722393Syz155240 	tcp = NULL;
38732393Syz155240 	icmp = NULL;
38742393Syz155240 	dport = 0;
38752393Syz155240 	natadd = 1;
38762393Syz155240 	nflags = 0;
38772393Syz155240 	natfailed = 0;
38782393Syz155240 	ifp = fin->fin_ifp;
38792393Syz155240 
38802393Syz155240 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
38812393Syz155240 		switch (fin->fin_p)
38822393Syz155240 		{
38832393Syz155240 		case IPPROTO_TCP :
38842393Syz155240 			nflags = IPN_TCP;
38852393Syz155240 			break;
38862393Syz155240 		case IPPROTO_UDP :
38872393Syz155240 			nflags = IPN_UDP;
38882393Syz155240 			break;
38892393Syz155240 		case IPPROTO_ICMP :
38902393Syz155240 			icmp = fin->fin_dp;
38912393Syz155240 
38922393Syz155240 			/*
38932393Syz155240 			 * This is an incoming packet, so the destination is
38942393Syz155240 			 * the icmp_id and the source port equals 0
38952393Syz155240 			 */
38962393Syz155240 			if (nat_icmpquerytype4(icmp->icmp_type)) {
38972393Syz155240 				nflags = IPN_ICMPQUERY;
38982393Syz155240 				dport = icmp->icmp_id;
38992393Syz155240 			} break;
39002393Syz155240 		default :
39012393Syz155240 			break;
39022393Syz155240 		}
39032393Syz155240 
39042393Syz155240 		if ((nflags & IPN_TCPUDP)) {
39052393Syz155240 			tcp = fin->fin_dp;
39062393Syz155240 			dport = tcp->th_dport;
39072393Syz155240 		}
39082393Syz155240 	}
39092393Syz155240 
39102393Syz155240 	in = fin->fin_dst;
39112393Syz155240 
39123448Sdh155122 	READ_ENTER(&ifs->ifs_ipf_nat);
39132393Syz155240 
39142393Syz155240 	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
39152393Syz155240 	    (nat = nat_icmperror(fin, &nflags, NAT_INBOUND)))
39162393Syz155240 		/*EMPTY*/;
39172393Syz155240 	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
39182393Syz155240 		natadd = 0;
39192393Syz155240 	else if ((nat = nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p,
39202393Syz155240 				     fin->fin_src, in))) {
39212393Syz155240 		nflags = nat->nat_flags;
39222393Syz155240 	} else {
39232393Syz155240 		u_32_t hv, msk, rmsk;
39242393Syz155240 
39253448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
39263448Sdh155122 		rmsk = ifs->ifs_rdr_masks;
39272393Syz155240 		msk = 0xffffffff;
39283448Sdh155122 		WRITE_ENTER(&ifs->ifs_ipf_nat);
39292393Syz155240 		/*
39302393Syz155240 		 * If there is no current entry in the nat table for this IP#,
39312393Syz155240 		 * create one for it (if there is a matching rule).
39322393Syz155240 		 */
39332393Syz155240 maskloop:
39342393Syz155240 		iph = in.s_addr & htonl(msk);
39353448Sdh155122 		hv = NAT_HASH_FN(iph, 0, ifs->ifs_ipf_rdrrules_sz);
39363448Sdh155122 		for (np = ifs->ifs_rdr_rules[hv]; np; np = np->in_rnext) {
39372393Syz155240 			if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
39382393Syz155240 				continue;
39392393Syz155240 			if (np->in_v != fin->fin_v)
39402393Syz155240 				continue;
39412393Syz155240 			if (np->in_p && (np->in_p != fin->fin_p))
39422393Syz155240 				continue;
39432393Syz155240 			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
39442393Syz155240 				continue;
39452393Syz155240 			if (np->in_flags & IPN_FILTER) {
39462393Syz155240 				if (!nat_match(fin, np))
39472393Syz155240 					continue;
39482393Syz155240 			} else {
39492393Syz155240 				if ((in.s_addr & np->in_outmsk) != np->in_outip)
39502393Syz155240 					continue;
39512393Syz155240 				if (np->in_pmin &&
39522393Syz155240 				    ((ntohs(np->in_pmax) < ntohs(dport)) ||
39532393Syz155240 				     (ntohs(dport) < ntohs(np->in_pmin))))
39542393Syz155240 					continue;
39552393Syz155240 			}
39562393Syz155240 
39572393Syz155240 			if (*np->in_plabel != '\0') {
39582393Syz155240 				if (!appr_ok(fin, tcp, np)) {
39592393Syz155240 					continue;
39602393Syz155240 				}
39612393Syz155240 			}
39622393Syz155240 
39632393Syz155240 			nat = nat_new(fin, np, NULL, nflags, NAT_INBOUND);
39642393Syz155240 			if (nat != NULL) {
39652393Syz155240 				np->in_hits++;
39662393Syz155240 				break;
39672393Syz155240 			} else
39682393Syz155240 				natfailed = -1;
39692393Syz155240 		}
39702393Syz155240 
39712393Syz155240 		if ((np == NULL) && (rmsk != 0)) {
39722393Syz155240 			while (rmsk) {
39732393Syz155240 				msk <<= 1;
39742393Syz155240 				if (rmsk & 0x80000000)
39752393Syz155240 					break;
39762393Syz155240 				rmsk <<= 1;
39772393Syz155240 			}
39782393Syz155240 			if (rmsk != 0) {
39792393Syz155240 				rmsk <<= 1;
39802393Syz155240 				goto maskloop;
39812393Syz155240 			}
39822393Syz155240 		}
39833448Sdh155122 		MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
39842393Syz155240 	}
39852393Syz155240 	if (nat != NULL) {
39862393Syz155240 		rval = fr_natin(fin, nat, natadd, nflags);
39872393Syz155240 		if (rval == 1) {
39882393Syz155240 			MUTEX_ENTER(&nat->nat_lock);
39892393Syz155240 			nat->nat_ref++;
39902393Syz155240 			MUTEX_EXIT(&nat->nat_lock);
39912393Syz155240 			fin->fin_nat = nat;
39922393Syz155240 			fin->fin_state = nat->nat_state;
39932393Syz155240 		}
39942393Syz155240 	} else
39952393Syz155240 		rval = natfailed;
39963448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
39972393Syz155240 
39982393Syz155240 	if (rval == -1) {
39992393Syz155240 		if (passp != NULL)
40002393Syz155240 			*passp = FR_BLOCK;
40012393Syz155240 		fin->fin_flx |= FI_BADNAT;
40022393Syz155240 	}
40032393Syz155240 	return rval;
40042393Syz155240 }
40052393Syz155240 
40062393Syz155240 
40072393Syz155240 /* ------------------------------------------------------------------------ */
40082393Syz155240 /* Function:    fr_natin                                                    */
40092393Syz155240 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
40102393Syz155240 /*                     1 == packet was successfully translated.             */
40112393Syz155240 /* Parameters:  fin(I)    - pointer to packet information                   */
40122393Syz155240 /*              nat(I)    - pointer to NAT structure                        */
40132393Syz155240 /*              natadd(I) - flag indicating if it is safe to add frag cache */
40142393Syz155240 /*              nflags(I) - NAT flags set for this packet                   */
40152393Syz155240 /* Locks Held:  ipf_nat (READ)                                              */
40162393Syz155240 /*                                                                          */
40172393Syz155240 /* Translate a packet coming "in" on an interface.                          */
40182393Syz155240 /* ------------------------------------------------------------------------ */
40192393Syz155240 int fr_natin(fin, nat, natadd, nflags)
40202393Syz155240 fr_info_t *fin;
40212393Syz155240 nat_t *nat;
40222393Syz155240 int natadd;
40232393Syz155240 u_32_t nflags;
40242393Syz155240 {
40252393Syz155240 	icmphdr_t *icmp;
40262958Sdr146992 	u_short *csump, *csump1;
40272958Sdr146992 	u_32_t sumd;
40282393Syz155240 	tcphdr_t *tcp;
40292393Syz155240 	ipnat_t *np;
40302393Syz155240 	int i;
40313448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
40322393Syz155240 
40332958Sdr146992 #if SOLARIS && defined(_KERNEL)
40342958Sdr146992 	net_data_t net_data_p;
40352958Sdr146992 	if (fin->fin_v == 4)
40363448Sdh155122 		net_data_p = ifs->ifs_ipf_ipv4;
40372958Sdr146992 	else
40383448Sdh155122 		net_data_p = ifs->ifs_ipf_ipv6;
40392958Sdr146992 #endif
40402958Sdr146992 
40412393Syz155240 	tcp = NULL;
40422393Syz155240 	csump = NULL;
40432393Syz155240 	np = nat->nat_ptr;
40442393Syz155240 	fin->fin_fr = nat->nat_fr;
40452393Syz155240 
40462393Syz155240 	if (np != NULL) {
40472393Syz155240 		if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
40482393Syz155240 			(void) fr_nat_newfrag(fin, 0, nat);
40492393Syz155240 
40502393Syz155240 	/* ------------------------------------------------------------- */
40512393Syz155240 	/* A few quick notes:						 */
40522393Syz155240 	/*	Following are test conditions prior to calling the 	 */
40532393Syz155240 	/*	appr_check routine.					 */
40542393Syz155240 	/*								 */
40552393Syz155240 	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
40562393Syz155240 	/*	with a map rule, we attempt to match the packet's	 */
40572393Syz155240 	/*	source port against in_dport, otherwise	we'd compare the */
40582393Syz155240 	/*	packet's destination.			 		 */
40592393Syz155240 	/* ------------------------------------------------------------- */
40602393Syz155240 		if (np->in_apr != NULL) {
40612393Syz155240 			i = appr_check(fin, nat);
40622393Syz155240 			if (i == -1) {
40632393Syz155240 				return -1;
40642393Syz155240 			}
40652393Syz155240 		}
40662393Syz155240 	}
40672393Syz155240 
40682393Syz155240 #ifdef	IPFILTER_SYNC
40692393Syz155240 	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
40702393Syz155240 #endif
40712393Syz155240 
40722393Syz155240 	MUTEX_ENTER(&nat->nat_lock);
40732393Syz155240 	nat->nat_bytes[0] += fin->fin_plen;
40742393Syz155240 	nat->nat_pkts[0]++;
40752393Syz155240 	MUTEX_EXIT(&nat->nat_lock);
40762393Syz155240 
40772393Syz155240 	fin->fin_ip->ip_dst = nat->nat_inip;
40782393Syz155240 	fin->fin_fi.fi_daddr = nat->nat_inip.s_addr;
40792393Syz155240 	if (nflags & IPN_TCPUDP)
40802393Syz155240 		tcp = fin->fin_dp;
40812393Syz155240 
40822393Syz155240 	/*
40832393Syz155240 	 * Fix up checksums, not by recalculating them, but
40842393Syz155240 	 * simply computing adjustments.
40852393Syz155240 	 * Why only do this for some platforms on inbound packets ?
40862393Syz155240 	 * Because for those that it is done, IP processing is yet to happen
40872393Syz155240 	 * and so the IPv4 header checksum has not yet been evaluated.
40882393Syz155240 	 * Perhaps it should always be done for the benefit of things like
40892393Syz155240 	 * fast forwarding (so that it doesn't need to be recomputed) but with
40902393Syz155240 	 * header checksum offloading, perhaps it is a moot point.
40912393Syz155240 	 */
40922393Syz155240 #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
40932393Syz155240      defined(__osf__) || defined(linux)
40942393Syz155240 	if (nat->nat_dir == NAT_OUTBOUND)
40952958Sdr146992 		fix_incksum(&fin->fin_ip->ip_sum, nat->nat_ipsumd);
40962393Syz155240 	else
40972958Sdr146992 		fix_outcksum(&fin->fin_ip->ip_sum, nat->nat_ipsumd);
40982393Syz155240 #endif
40992393Syz155240 
41002393Syz155240 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
41012393Syz155240 		if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) {
41022393Syz155240 			tcp->th_dport = nat->nat_inport;
41032393Syz155240 			fin->fin_data[1] = ntohs(nat->nat_inport);
41042393Syz155240 		}
41052393Syz155240 
41062393Syz155240 
41072393Syz155240 		if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) {
41082393Syz155240 			icmp = fin->fin_dp;
41092393Syz155240 
41102393Syz155240 			icmp->icmp_id = nat->nat_inport;
41112393Syz155240 		}
41122393Syz155240 
41132393Syz155240 		csump = nat_proto(fin, nat, nflags);
41142393Syz155240 	}
41152393Syz155240 
41162393Syz155240 	nat_update(fin, nat, np);
41172393Syz155240 
41182958Sdr146992 #if SOLARIS && defined(_KERNEL)
41192958Sdr146992 	if (nflags & IPN_TCPUDP &&
41202958Sdr146992 	    NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) {
41212958Sdr146992 		sumd = nat->nat_sumd[1];
41222958Sdr146992 		csump1 = &(fin->fin_m->b_datap->db_struioun.cksum.cksum_val.u16);
41232958Sdr146992 		if (csump1 != NULL) {
41242958Sdr146992 			if (nat->nat_dir == NAT_OUTBOUND)
41252958Sdr146992 				fix_incksum(csump1, sumd);
41262958Sdr146992 			else
41272958Sdr146992 				fix_outcksum(csump1, sumd);
41282958Sdr146992 		}
41292958Sdr146992 	} else
41302958Sdr146992 #endif
41312958Sdr146992 		sumd = nat->nat_sumd[0];
41322958Sdr146992 
41332393Syz155240 	/*
41342958Sdr146992 	 * Inbound packets always need to have their address adjusted in case
41352958Sdr146992 	 * code following this validates it.
41362393Syz155240 	 */
41372393Syz155240 	if (csump != NULL) {
41382393Syz155240 		if (nat->nat_dir == NAT_OUTBOUND)
41392958Sdr146992 			fix_incksum(csump, sumd);
41402393Syz155240 		else
41412958Sdr146992 			fix_outcksum(csump, sumd);
41422393Syz155240 	}
41433448Sdh155122 	ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[0]);
41442393Syz155240 	fin->fin_flx |= FI_NATED;
41452393Syz155240 	if (np != NULL && np->in_tag.ipt_num[0] != 0)
41462393Syz155240 		fin->fin_nattag = &np->in_tag;
41472393Syz155240 	return 1;
41482393Syz155240 }
41492393Syz155240 
41502393Syz155240 
41512393Syz155240 /* ------------------------------------------------------------------------ */
41522393Syz155240 /* Function:    nat_proto                                                   */
41532393Syz155240 /* Returns:     u_short* - pointer to transport header checksum to update,  */
41542393Syz155240 /*                         NULL if the transport protocol is not recognised */
41552393Syz155240 /*                         as needing a checksum update.                    */
41562393Syz155240 /* Parameters:  fin(I)    - pointer to packet information                   */
41572393Syz155240 /*              nat(I)    - pointer to NAT structure                        */
41582393Syz155240 /*              nflags(I) - NAT flags set for this packet                   */
41592393Syz155240 /*                                                                          */
41602393Syz155240 /* Return the pointer to the checksum field for each protocol so understood.*/
41612393Syz155240 /* If support for making other changes to a protocol header is required,    */
41622393Syz155240 /* that is not strictly 'address' translation, such as clamping the MSS in  */
41632393Syz155240 /* TCP down to a specific value, then do it from here.                      */
41642393Syz155240 /* ------------------------------------------------------------------------ */
41652393Syz155240 u_short *nat_proto(fin, nat, nflags)
41662393Syz155240 fr_info_t *fin;
41672393Syz155240 nat_t *nat;
41682393Syz155240 u_int nflags;
41692393Syz155240 {
41702393Syz155240 	icmphdr_t *icmp;
41712393Syz155240 	u_short *csump;
41722393Syz155240 	tcphdr_t *tcp;
41732393Syz155240 	udphdr_t *udp;
41742393Syz155240 
41752393Syz155240 	csump = NULL;
41762393Syz155240 	if (fin->fin_out == 0) {
41772393Syz155240 		fin->fin_rev = (nat->nat_dir == NAT_OUTBOUND);
41782393Syz155240 	} else {
41792393Syz155240 		fin->fin_rev = (nat->nat_dir == NAT_INBOUND);
41802393Syz155240 	}
41812393Syz155240 
41822393Syz155240 	switch (fin->fin_p)
41832393Syz155240 	{
41842393Syz155240 	case IPPROTO_TCP :
41852393Syz155240 		tcp = fin->fin_dp;
41862393Syz155240 
41872393Syz155240 		csump = &tcp->th_sum;
41882393Syz155240 
41892393Syz155240 		/*
41902393Syz155240 		 * Do a MSS CLAMPING on a SYN packet,
41912393Syz155240 		 * only deal IPv4 for now.
41922393Syz155240 		 */
41932393Syz155240 		if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0)
41942958Sdr146992 			nat_mssclamp(tcp, nat->nat_mssclamp, csump);
41952393Syz155240 
41962393Syz155240 		break;
41972393Syz155240 
41982393Syz155240 	case IPPROTO_UDP :
41992393Syz155240 		udp = fin->fin_dp;
42002393Syz155240 
42012393Syz155240 		if (udp->uh_sum)
42022393Syz155240 			csump = &udp->uh_sum;
42032393Syz155240 		break;
42042393Syz155240 
42052393Syz155240 	case IPPROTO_ICMP :
42062393Syz155240 		icmp = fin->fin_dp;
42072393Syz155240 
42082393Syz155240 		if ((nflags & IPN_ICMPQUERY) != 0) {
42092393Syz155240 			if (icmp->icmp_cksum != 0)
42102393Syz155240 				csump = &icmp->icmp_cksum;
42112393Syz155240 		}
42122393Syz155240 		break;
42132393Syz155240 	}
42142393Syz155240 	return csump;
42152393Syz155240 }
42162393Syz155240 
42172393Syz155240 
42182393Syz155240 /* ------------------------------------------------------------------------ */
42192393Syz155240 /* Function:    fr_natunload                                                */
42202393Syz155240 /* Returns:     Nil                                                         */
42212393Syz155240 /* Parameters:  Nil                                                         */
42222393Syz155240 /*                                                                          */
42232393Syz155240 /* Free all memory used by NAT structures allocated at runtime.             */
42242393Syz155240 /* ------------------------------------------------------------------------ */
42253448Sdh155122 void fr_natunload(ifs)
42263448Sdh155122 ipf_stack_t *ifs;
42272393Syz155240 {
42282393Syz155240 	ipftq_t *ifq, *ifqnext;
42292393Syz155240 
42303448Sdh155122 	(void) nat_clearlist(ifs);
42313448Sdh155122 	(void) nat_flushtable(ifs);
42322393Syz155240 
42332393Syz155240 	/*
42342393Syz155240 	 * Proxy timeout queues are not cleaned here because although they
42352393Syz155240 	 * exist on the NAT list, appr_unload is called after fr_natunload
42362393Syz155240 	 * and the proxies actually are responsible for them being created.
42372393Syz155240 	 * Should the proxy timeouts have their own list?  There's no real
42382393Syz155240 	 * justification as this is the only complication.
42392393Syz155240 	 */
42403448Sdh155122 	for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) {
42412393Syz155240 		ifqnext = ifq->ifq_next;
42422393Syz155240 		if (((ifq->ifq_flags & IFQF_PROXY) == 0) &&
42432393Syz155240 		    (fr_deletetimeoutqueue(ifq) == 0))
42443448Sdh155122 			fr_freetimeoutqueue(ifq, ifs);
42452393Syz155240 	}
42462393Syz155240 
42473448Sdh155122 	if (ifs->ifs_nat_table[0] != NULL) {
42483448Sdh155122 		KFREES(ifs->ifs_nat_table[0],
42493448Sdh155122 		       sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz);
42503448Sdh155122 		ifs->ifs_nat_table[0] = NULL;
42512393Syz155240 	}
42523448Sdh155122 	if (ifs->ifs_nat_table[1] != NULL) {
42533448Sdh155122 		KFREES(ifs->ifs_nat_table[1],
42543448Sdh155122 		       sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz);
42553448Sdh155122 		ifs->ifs_nat_table[1] = NULL;
42562393Syz155240 	}
42573448Sdh155122 	if (ifs->ifs_nat_rules != NULL) {
42583448Sdh155122 		KFREES(ifs->ifs_nat_rules,
42593448Sdh155122 		       sizeof(ipnat_t *) * ifs->ifs_ipf_natrules_sz);
42603448Sdh155122 		ifs->ifs_nat_rules = NULL;
42612393Syz155240 	}
42623448Sdh155122 	if (ifs->ifs_rdr_rules != NULL) {
42633448Sdh155122 		KFREES(ifs->ifs_rdr_rules,
42643448Sdh155122 		       sizeof(ipnat_t *) * ifs->ifs_ipf_rdrrules_sz);
42653448Sdh155122 		ifs->ifs_rdr_rules = NULL;
42662393Syz155240 	}
42673448Sdh155122 	if (ifs->ifs_maptable != NULL) {
42683448Sdh155122 		KFREES(ifs->ifs_maptable,
42693448Sdh155122 		       sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz);
42703448Sdh155122 		ifs->ifs_maptable = NULL;
42712393Syz155240 	}
42723448Sdh155122 	if (ifs->ifs_nat_stats.ns_bucketlen[0] != NULL) {
42733448Sdh155122 		KFREES(ifs->ifs_nat_stats.ns_bucketlen[0],
42743448Sdh155122 		       sizeof(u_long *) * ifs->ifs_ipf_nattable_sz);
42753448Sdh155122 		ifs->ifs_nat_stats.ns_bucketlen[0] = NULL;
42762393Syz155240 	}
42773448Sdh155122 	if (ifs->ifs_nat_stats.ns_bucketlen[1] != NULL) {
42783448Sdh155122 		KFREES(ifs->ifs_nat_stats.ns_bucketlen[1],
42793448Sdh155122 		       sizeof(u_long *) * ifs->ifs_ipf_nattable_sz);
42803448Sdh155122 		ifs->ifs_nat_stats.ns_bucketlen[1] = NULL;
42812393Syz155240 	}
42822393Syz155240 
42833448Sdh155122 	if (ifs->ifs_fr_nat_maxbucket_reset == 1)
42843448Sdh155122 		ifs->ifs_fr_nat_maxbucket = 0;
42853448Sdh155122 
42863448Sdh155122 	if (ifs->ifs_fr_nat_init == 1) {
42873448Sdh155122 		ifs->ifs_fr_nat_init = 0;
42883448Sdh155122 		fr_sttab_destroy(ifs->ifs_nat_tqb);
42893448Sdh155122 
42903448Sdh155122 		RW_DESTROY(&ifs->ifs_ipf_natfrag);
42913448Sdh155122 		RW_DESTROY(&ifs->ifs_ipf_nat);
42923448Sdh155122 
42933448Sdh155122 		MUTEX_DESTROY(&ifs->ifs_ipf_nat_new);
42943448Sdh155122 		MUTEX_DESTROY(&ifs->ifs_ipf_natio);
42953448Sdh155122 
42963448Sdh155122 		MUTEX_DESTROY(&ifs->ifs_nat_udptq.ifq_lock);
42973448Sdh155122 		MUTEX_DESTROY(&ifs->ifs_nat_icmptq.ifq_lock);
42983448Sdh155122 		MUTEX_DESTROY(&ifs->ifs_nat_iptq.ifq_lock);
42992393Syz155240 	}
43002393Syz155240 }
43012393Syz155240 
43022393Syz155240 
43032393Syz155240 /* ------------------------------------------------------------------------ */
43042393Syz155240 /* Function:    fr_natexpire                                                */
43052393Syz155240 /* Returns:     Nil                                                         */
43062393Syz155240 /* Parameters:  Nil                                                         */
43072393Syz155240 /*                                                                          */
43082393Syz155240 /* Check all of the timeout queues for entries at the top which need to be  */
43092393Syz155240 /* expired.                                                                 */
43102393Syz155240 /* ------------------------------------------------------------------------ */
43113448Sdh155122 void fr_natexpire(ifs)
43123448Sdh155122 ipf_stack_t *ifs;
43132393Syz155240 {
43142393Syz155240 	ipftq_t *ifq, *ifqnext;
43152393Syz155240 	ipftqent_t *tqe, *tqn;
43162393Syz155240 	int i;
43172393Syz155240 	SPL_INT(s);
43182393Syz155240 
43192393Syz155240 	SPL_NET(s);
43203448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
43213448Sdh155122 	for (ifq = ifs->ifs_nat_tqb, i = 0; ifq != NULL; ifq = ifq->ifq_next) {
43222393Syz155240 		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
43233448Sdh155122 			if (tqe->tqe_die > ifs->ifs_fr_ticks)
43242393Syz155240 				break;
43252393Syz155240 			tqn = tqe->tqe_next;
43263448Sdh155122 			nat_delete(tqe->tqe_parent, NL_EXPIRE, ifs);
43272393Syz155240 		}
43282393Syz155240 	}
43292393Syz155240 
43303448Sdh155122 	for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) {
43312393Syz155240 		ifqnext = ifq->ifq_next;
43322393Syz155240 
43332393Syz155240 		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
43343448Sdh155122 			if (tqe->tqe_die > ifs->ifs_fr_ticks)
43352393Syz155240 				break;
43362393Syz155240 			tqn = tqe->tqe_next;
43373448Sdh155122 			nat_delete(tqe->tqe_parent, NL_EXPIRE, ifs);
43382393Syz155240 		}
43392393Syz155240 	}
43402393Syz155240 
43413448Sdh155122 	for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) {
43422393Syz155240 		ifqnext = ifq->ifq_next;
43432393Syz155240 
43442393Syz155240 		if (((ifq->ifq_flags & IFQF_DELETE) != 0) &&
43452393Syz155240 		    (ifq->ifq_ref == 0)) {
43463448Sdh155122 			fr_freetimeoutqueue(ifq, ifs);
43472393Syz155240 		}
43482393Syz155240 	}
43492393Syz155240 
43503448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
43512393Syz155240 	SPL_X(s);
43522393Syz155240 }
43532393Syz155240 
43542393Syz155240 
43552393Syz155240 /* ------------------------------------------------------------------------ */
43562958Sdr146992 /* Function:    fr_nataddrsync                                              */
43572393Syz155240 /* Returns:     Nil                                                         */
43582958Sdr146992 /* Parameters:  ifp(I) -  pointer to network interface                      */
43592958Sdr146992 /*              addr(I) - pointer to new network address                    */
43602393Syz155240 /*                                                                          */
43612393Syz155240 /* Walk through all of the currently active NAT sessions, looking for those */
43622958Sdr146992 /* which need to have their translated address updated (where the interface */
43632958Sdr146992 /* matches the one passed in) and change it, recalculating the checksum sum */
43642958Sdr146992 /* difference too.                                                          */
43652393Syz155240 /* ------------------------------------------------------------------------ */
43663448Sdh155122 void fr_nataddrsync(ifp, addr, ifs)
43672393Syz155240 void *ifp;
43682958Sdr146992 struct in_addr *addr;
43693448Sdh155122 ipf_stack_t *ifs;
43702393Syz155240 {
43712393Syz155240 	u_32_t sum1, sum2, sumd;
43722393Syz155240 	nat_t *nat;
43732958Sdr146992 	ipnat_t *np;
43742393Syz155240 	SPL_INT(s);
43752393Syz155240 
43763448Sdh155122 	if (ifs->ifs_fr_running <= 0)
43772393Syz155240 		return;
43782393Syz155240 
43792393Syz155240 	SPL_NET(s);
43803448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
43813448Sdh155122 
43823448Sdh155122 	if (ifs->ifs_fr_running <= 0) {
43833448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
43842393Syz155240 		return;
43852393Syz155240 	}
43862393Syz155240 
43872958Sdr146992 	/*
43882958Sdr146992 	 * Change IP addresses for NAT sessions for any protocol except TCP
43892958Sdr146992 	 * since it will break the TCP connection anyway.  The only rules
43902958Sdr146992 	 * which will get changed are those which are "map ... -> 0/32",
43912958Sdr146992 	 * where the rule specifies the address is taken from the interface.
43922958Sdr146992 	 */
43933448Sdh155122 	for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) {
43942958Sdr146992 		if (addr != NULL) {
43952958Sdr146992 			if (((ifp != NULL) && ifp != (nat->nat_ifps[0])) ||
43962958Sdr146992 			    ((nat->nat_flags & IPN_TCP) != 0))
43972958Sdr146992 				continue;
43982958Sdr146992 			if (((np = nat->nat_ptr) == NULL) ||
43992958Sdr146992 			    (np->in_nip || (np->in_outmsk != 0xffffffff)))
44002393Syz155240 				continue;
44012393Syz155240 
44022393Syz155240 			/*
44032393Syz155240 			 * Change the map-to address to be the same as the
44042393Syz155240 			 * new one.
44052393Syz155240 			 */
44062393Syz155240 			sum1 = nat->nat_outip.s_addr;
44072958Sdr146992 			nat->nat_outip = *addr;
44082958Sdr146992 			sum2 = nat->nat_outip.s_addr;
44092958Sdr146992 
44102958Sdr146992 		} else if (((ifp == NULL) || (ifp == nat->nat_ifps[0])) &&
44112958Sdr146992 		    !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr) &&
44122958Sdr146992 		    (np->in_outmsk == 0xffffffff) && !np->in_nip) {
44132958Sdr146992 			struct in_addr in;
44142958Sdr146992 
44152958Sdr146992 			/*
44162958Sdr146992 			 * Change the map-to address to be the same as the
44172958Sdr146992 			 * new one.
44182958Sdr146992 			 */
44192958Sdr146992 			sum1 = nat->nat_outip.s_addr;
44202958Sdr146992 			if (fr_ifpaddr(4, FRI_NORMAL, nat->nat_ifps[0],
44213448Sdh155122 				       &in, NULL, ifs) != -1)
44222393Syz155240 				nat->nat_outip = in;
44232393Syz155240 			sum2 = nat->nat_outip.s_addr;
44242958Sdr146992 		} else {
44252958Sdr146992 			continue;
44262393Syz155240 		}
44272958Sdr146992 
44282958Sdr146992 		if (sum1 == sum2)
44292958Sdr146992 			continue;
44302958Sdr146992 		/*
44312958Sdr146992 		 * Readjust the checksum adjustment to take into
44322958Sdr146992 		 * account the new IP#.
44332958Sdr146992 		 */
44342958Sdr146992 		CALC_SUMD(sum1, sum2, sumd);
44352958Sdr146992 		/* XXX - dont change for TCP when solaris does
44362958Sdr146992 		 * hardware checksumming.
44372958Sdr146992 		 */
44382958Sdr146992 		sumd += nat->nat_sumd[0];
44392958Sdr146992 		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
44402958Sdr146992 		nat->nat_sumd[1] = nat->nat_sumd[0];
44412393Syz155240 	}
44422393Syz155240 
44433448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
44442958Sdr146992 	SPL_X(s);
44452958Sdr146992 }
44462958Sdr146992 
44472958Sdr146992 
44482958Sdr146992 /* ------------------------------------------------------------------------ */
44492958Sdr146992 /* Function:    fr_natifpsync                                               */
44502958Sdr146992 /* Returns:     Nil                                                         */
44512958Sdr146992 /* Parameters:  action(I) - how we are syncing                              */
44522958Sdr146992 /*              ifp(I)    - pointer to network interface                    */
44532958Sdr146992 /*              name(I)   - name of interface to sync to                    */
44542958Sdr146992 /*                                                                          */
44552958Sdr146992 /* This function is used to resync the mapping of interface names and their */
44562958Sdr146992 /* respective 'pointers'.  For "action == IPFSYNC_RESYNC", resync all       */
44572958Sdr146992 /* interfaces by doing a new lookup of name to 'pointer'.  For "action ==   */
44582958Sdr146992 /* IPFSYNC_NEWIFP", treat ifp as the new pointer value associated with      */
44592958Sdr146992 /* "name" and for "action == IPFSYNC_OLDIFP", ifp is a pointer for which    */
44602958Sdr146992 /* there is no longer any interface associated with it.                     */
44612958Sdr146992 /* ------------------------------------------------------------------------ */
44623448Sdh155122 void fr_natifpsync(action, ifp, name, ifs)
44632958Sdr146992 int action;
44642958Sdr146992 void *ifp;
44652958Sdr146992 char *name;
44663448Sdh155122 ipf_stack_t *ifs;
44672958Sdr146992 {
44682958Sdr146992 #if defined(_KERNEL) && !defined(MENTAT) && defined(USE_SPL)
44692958Sdr146992 	int s;
44702958Sdr146992 #endif
44712958Sdr146992 	nat_t *nat;
44722958Sdr146992 	ipnat_t *n;
44732958Sdr146992 
44743448Sdh155122 	if (ifs->ifs_fr_running <= 0)
44752958Sdr146992 		return;
44762958Sdr146992 
44772958Sdr146992 	SPL_NET(s);
44783448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
44793448Sdh155122 
44803448Sdh155122 	if (ifs->ifs_fr_running <= 0) {
44813448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
44822958Sdr146992 		return;
44832958Sdr146992 	}
44842958Sdr146992 
44852958Sdr146992 	switch (action)
44862958Sdr146992 	{
44872958Sdr146992 	case IPFSYNC_RESYNC :
44883448Sdh155122 		for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) {
44892958Sdr146992 			if ((ifp == nat->nat_ifps[0]) ||
44902958Sdr146992 			    (nat->nat_ifps[0] == (void *)-1)) {
44912958Sdr146992 				nat->nat_ifps[0] =
44923448Sdh155122 				    fr_resolvenic(nat->nat_ifnames[0], 4, ifs);
44932958Sdr146992 			}
44942958Sdr146992 
44952958Sdr146992 			if ((ifp == nat->nat_ifps[1]) ||
44962958Sdr146992 			    (nat->nat_ifps[1] == (void *)-1)) {
44972958Sdr146992 				nat->nat_ifps[1] =
44983448Sdh155122 				    fr_resolvenic(nat->nat_ifnames[1], 4, ifs);
44992958Sdr146992 			}
45002958Sdr146992 		}
45012958Sdr146992 
45023448Sdh155122 		for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) {
45032958Sdr146992 			if (n->in_ifps[0] == ifp ||
45042958Sdr146992 			    n->in_ifps[0] == (void *)-1) {
45052958Sdr146992 				n->in_ifps[0] =
45063448Sdh155122 				    fr_resolvenic(n->in_ifnames[0], 4, ifs);
45072958Sdr146992 			}
45082958Sdr146992 			if (n->in_ifps[1] == ifp ||
45092958Sdr146992 			    n->in_ifps[1] == (void *)-1) {
45102958Sdr146992 				n->in_ifps[1] =
45113448Sdh155122 				    fr_resolvenic(n->in_ifnames[1], 4, ifs);
45122958Sdr146992 			}
45132958Sdr146992 		}
45142958Sdr146992 		break;
45152958Sdr146992 	case IPFSYNC_NEWIFP :
45163448Sdh155122 		for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) {
45172958Sdr146992 			if (!strncmp(name, nat->nat_ifnames[0],
45182958Sdr146992 				     sizeof(nat->nat_ifnames[0])))
45192958Sdr146992 				nat->nat_ifps[0] = ifp;
45202958Sdr146992 			if (!strncmp(name, nat->nat_ifnames[1],
45212958Sdr146992 				     sizeof(nat->nat_ifnames[1])))
45222958Sdr146992 				nat->nat_ifps[1] = ifp;
45232958Sdr146992 		}
45243448Sdh155122 		for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) {
45252958Sdr146992 			if (!strncmp(name, n->in_ifnames[0],
45262958Sdr146992 				     sizeof(n->in_ifnames[0])))
45272958Sdr146992 				n->in_ifps[0] = ifp;
45282958Sdr146992 			if (!strncmp(name, n->in_ifnames[1],
45292958Sdr146992 				     sizeof(n->in_ifnames[1])))
45302958Sdr146992 				n->in_ifps[1] = ifp;
45312958Sdr146992 		}
45322958Sdr146992 		break;
45332958Sdr146992 	case IPFSYNC_OLDIFP :
45343448Sdh155122 		for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) {
45352958Sdr146992 			if (ifp == nat->nat_ifps[0])
45362958Sdr146992 				nat->nat_ifps[0] = (void *)-1;
45372958Sdr146992 			if (ifp == nat->nat_ifps[1])
45382958Sdr146992 				nat->nat_ifps[1] = (void *)-1;
45392958Sdr146992 		}
45403448Sdh155122 		for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) {
45412958Sdr146992 			if (n->in_ifps[0] == ifp)
45422958Sdr146992 				n->in_ifps[0] = (void *)-1;
45432958Sdr146992 			if (n->in_ifps[1] == ifp)
45442958Sdr146992 				n->in_ifps[1] = (void *)-1;
45452958Sdr146992 		}
45462958Sdr146992 		break;
45472393Syz155240 	}
45483448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
45492393Syz155240 	SPL_X(s);
45502393Syz155240 }
45512393Syz155240 
45522393Syz155240 
45532393Syz155240 /* ------------------------------------------------------------------------ */
45542393Syz155240 /* Function:    nat_icmpquerytype4                                          */
45552393Syz155240 /* Returns:     int - 1 == success, 0 == failure                            */
45562393Syz155240 /* Parameters:  icmptype(I) - ICMP type number                              */
45572393Syz155240 /*                                                                          */
45582393Syz155240 /* Tests to see if the ICMP type number passed is a query/response type or  */
45592393Syz155240 /* not.                                                                     */
45602393Syz155240 /* ------------------------------------------------------------------------ */
45612393Syz155240 static INLINE int nat_icmpquerytype4(icmptype)
45622393Syz155240 int icmptype;
45632393Syz155240 {
45642393Syz155240 
45652393Syz155240 	/*
45662393Syz155240 	 * For the ICMP query NAT code, it is essential that both the query
45672393Syz155240 	 * and the reply match on the NAT rule. Because the NAT structure
45682393Syz155240 	 * does not keep track of the icmptype, and a single NAT structure
45692393Syz155240 	 * is used for all icmp types with the same src, dest and id, we
45702393Syz155240 	 * simply define the replies as queries as well. The funny thing is,
45712393Syz155240 	 * altough it seems silly to call a reply a query, this is exactly
45722393Syz155240 	 * as it is defined in the IPv4 specification
45732393Syz155240 	 */
45742393Syz155240 
45752393Syz155240 	switch (icmptype)
45762393Syz155240 	{
45772393Syz155240 
45782393Syz155240 	case ICMP_ECHOREPLY:
45792393Syz155240 	case ICMP_ECHO:
45802393Syz155240 	/* route aedvertisement/solliciation is currently unsupported: */
45812393Syz155240 	/* it would require rewriting the ICMP data section            */
45822393Syz155240 	case ICMP_TSTAMP:
45832393Syz155240 	case ICMP_TSTAMPREPLY:
45842393Syz155240 	case ICMP_IREQ:
45852393Syz155240 	case ICMP_IREQREPLY:
45862393Syz155240 	case ICMP_MASKREQ:
45872393Syz155240 	case ICMP_MASKREPLY:
45882393Syz155240 		return 1;
45892393Syz155240 	default:
45902393Syz155240 		return 0;
45912393Syz155240 	}
45922393Syz155240 }
45932393Syz155240 
45942393Syz155240 
45952393Syz155240 /* ------------------------------------------------------------------------ */
45962393Syz155240 /* Function:    nat_log                                                     */
45972393Syz155240 /* Returns:     Nil                                                         */
45982393Syz155240 /* Parameters:  nat(I)  - pointer to NAT structure                          */
45992393Syz155240 /*              type(I) - type of log entry to create                       */
46002393Syz155240 /*                                                                          */
46012393Syz155240 /* Creates a NAT log entry.                                                 */
46022393Syz155240 /* ------------------------------------------------------------------------ */
46033448Sdh155122 void nat_log(nat, type, ifs)
46042393Syz155240 struct nat *nat;
46052393Syz155240 u_int type;
46063448Sdh155122 ipf_stack_t *ifs;
46072393Syz155240 {
46082393Syz155240 #ifdef	IPFILTER_LOG
46092393Syz155240 # ifndef LARGE_NAT
46102393Syz155240 	struct ipnat *np;
46112393Syz155240 	int rulen;
46122393Syz155240 # endif
46132393Syz155240 	struct natlog natl;
46142393Syz155240 	void *items[1];
46152393Syz155240 	size_t sizes[1];
46162393Syz155240 	int types[1];
46172393Syz155240 
46182393Syz155240 	natl.nl_inip = nat->nat_inip;
46192393Syz155240 	natl.nl_outip = nat->nat_outip;
46202393Syz155240 	natl.nl_origip = nat->nat_oip;
46212393Syz155240 	natl.nl_bytes[0] = nat->nat_bytes[0];
46222393Syz155240 	natl.nl_bytes[1] = nat->nat_bytes[1];
46232393Syz155240 	natl.nl_pkts[0] = nat->nat_pkts[0];
46242393Syz155240 	natl.nl_pkts[1] = nat->nat_pkts[1];
46252393Syz155240 	natl.nl_origport = nat->nat_oport;
46262393Syz155240 	natl.nl_inport = nat->nat_inport;
46272393Syz155240 	natl.nl_outport = nat->nat_outport;
46282393Syz155240 	natl.nl_p = nat->nat_p;
46292393Syz155240 	natl.nl_type = type;
46302393Syz155240 	natl.nl_rule = -1;
46312393Syz155240 # ifndef LARGE_NAT
46322393Syz155240 	if (nat->nat_ptr != NULL) {
46333448Sdh155122 		for (rulen = 0, np = ifs->ifs_nat_list; np;
46343448Sdh155122 		     np = np->in_next, rulen++)
46352393Syz155240 			if (np == nat->nat_ptr) {
46362393Syz155240 				natl.nl_rule = rulen;
46372393Syz155240 				break;
46382393Syz155240 			}
46392393Syz155240 	}
46402393Syz155240 # endif
46412393Syz155240 	items[0] = &natl;
46422393Syz155240 	sizes[0] = sizeof(natl);
46432393Syz155240 	types[0] = 0;
46442393Syz155240 
46453448Sdh155122 	(void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1, ifs);
46462393Syz155240 #endif
46472393Syz155240 }
46482393Syz155240 
46492393Syz155240 
46502393Syz155240 #if defined(__OpenBSD__)
46512393Syz155240 /* ------------------------------------------------------------------------ */
46522393Syz155240 /* Function:    nat_ifdetach                                                */
46532393Syz155240 /* Returns:     Nil                                                         */
46542393Syz155240 /* Parameters:  ifp(I) - pointer to network interface                       */
46552393Syz155240 /*                                                                          */
46562393Syz155240 /* Compatibility interface for OpenBSD to trigger the correct updating of   */
46572393Syz155240 /* interface references within IPFilter.                                    */
46582393Syz155240 /* ------------------------------------------------------------------------ */
46593448Sdh155122 void nat_ifdetach(ifp, ifs)
46602393Syz155240 void *ifp;
46613448Sdh155122 ipf_stack_t *ifs;
46622393Syz155240 {
46633448Sdh155122 	frsync(ifp, ifs);
46642393Syz155240 	return;
46652393Syz155240 }
46662393Syz155240 #endif
46672393Syz155240 
46682393Syz155240 
46692393Syz155240 /* ------------------------------------------------------------------------ */
46703448Sdh155122 /* Function:    fr_ipnatderef                                               */
46713448Sdh155122 /* Returns:     Nil                                                         */
46723448Sdh155122 /* Parameters:  isp(I) - pointer to pointer to NAT rule                     */
46733448Sdh155122 /* Write Locks: ipf_nat                                                     */
46743448Sdh155122 /*                                                                          */
46753448Sdh155122 /* ------------------------------------------------------------------------ */
46763448Sdh155122 void fr_ipnatderef(inp, ifs)
46773448Sdh155122 ipnat_t **inp;
46783448Sdh155122 ipf_stack_t *ifs;
46793448Sdh155122 {
46803448Sdh155122 	ipnat_t *in;
46813448Sdh155122 
46823448Sdh155122 	in = *inp;
46833448Sdh155122 	*inp = NULL;
46843448Sdh155122 	in->in_space++;
46853448Sdh155122 	in->in_use--;
46863448Sdh155122 	if (in->in_use == 0 && (in->in_flags & IPN_DELETE)) {
46873448Sdh155122 		if (in->in_apr)
46883448Sdh155122 			appr_free(in->in_apr);
46893448Sdh155122 		KFREE(in);
46903448Sdh155122 		ifs->ifs_nat_stats.ns_rules--;
46913448Sdh155122 #ifdef notdef
46923448Sdh155122 #if SOLARIS
46933448Sdh155122 		if (ifs->ifs_nat_stats.ns_rules == 0)
46943448Sdh155122 			ifs->ifs_pfil_delayed_copy = 1;
46953448Sdh155122 #endif
46963448Sdh155122 #endif
46973448Sdh155122 	}
46983448Sdh155122 }
46993448Sdh155122 
47003448Sdh155122 
47013448Sdh155122 /* ------------------------------------------------------------------------ */
47022393Syz155240 /* Function:    fr_natderef                                                 */
47032393Syz155240 /* Returns:     Nil                                                         */
47042393Syz155240 /* Parameters:  isp(I) - pointer to pointer to NAT table entry              */
47052393Syz155240 /*                                                                          */
47062393Syz155240 /* Decrement the reference counter for this NAT table entry and free it if  */
47072393Syz155240 /* there are no more things using it.                                       */
47082393Syz155240 /* ------------------------------------------------------------------------ */
47093448Sdh155122 void fr_natderef(natp, ifs)
47102393Syz155240 nat_t **natp;
47113448Sdh155122 ipf_stack_t *ifs;
47122393Syz155240 {
47132393Syz155240 	nat_t *nat;
47142393Syz155240 
47152393Syz155240 	nat = *natp;
47162393Syz155240 	*natp = NULL;
47173448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
47182393Syz155240 	nat->nat_ref--;
47192393Syz155240 	if (nat->nat_ref == 0)
47203448Sdh155122 	    nat_delete(nat, NL_EXPIRE, ifs);
47213448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
47222393Syz155240 }
47232393Syz155240 
47242393Syz155240 
47252393Syz155240 /* ------------------------------------------------------------------------ */
47262393Syz155240 /* Function:    fr_natclone                                                 */
47272393Syz155240 /* Returns:     ipstate_t* - NULL == cloning failed,                        */
47282393Syz155240 /*                           else pointer to new state structure            */
47292393Syz155240 /* Parameters:  fin(I) - pointer to packet information                      */
47302393Syz155240 /*              is(I)  - pointer to master state structure                  */
47312393Syz155240 /* Write Lock:  ipf_nat                                                     */
47322393Syz155240 /*                                                                          */
47332393Syz155240 /* Create a "duplcate" state table entry from the master.                   */
47342393Syz155240 /* ------------------------------------------------------------------------ */
47352393Syz155240 static nat_t *fr_natclone(fin, nat)
47362393Syz155240 fr_info_t *fin;
47372393Syz155240 nat_t *nat;
47382393Syz155240 {
47392393Syz155240 	frentry_t *fr;
47402393Syz155240 	nat_t *clone;
47412393Syz155240 	ipnat_t *np;
47423448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
47432393Syz155240 
47442393Syz155240 	KMALLOC(clone, nat_t *);
47452393Syz155240 	if (clone == NULL)
47462393Syz155240 		return NULL;
47472393Syz155240 	bcopy((char *)nat, (char *)clone, sizeof(*clone));
47482393Syz155240 
47492393Syz155240 	MUTEX_NUKE(&clone->nat_lock);
47502393Syz155240 
47512393Syz155240 	clone->nat_aps = NULL;
47522393Syz155240 	/*
47532393Syz155240 	 * Initialize all these so that nat_delete() doesn't cause a crash.
47542393Syz155240 	 */
47552393Syz155240 	clone->nat_tqe.tqe_pnext = NULL;
47562393Syz155240 	clone->nat_tqe.tqe_next = NULL;
47572393Syz155240 	clone->nat_tqe.tqe_ifq = NULL;
47582393Syz155240 	clone->nat_tqe.tqe_parent = clone;
47592393Syz155240 
47602393Syz155240 	clone->nat_flags &= ~SI_CLONE;
47612393Syz155240 	clone->nat_flags |= SI_CLONED;
47622393Syz155240 
47632393Syz155240 	if (clone->nat_hm)
47642393Syz155240 		clone->nat_hm->hm_ref++;
47652393Syz155240 
47663448Sdh155122 	if (nat_insert(clone, fin->fin_rev, ifs) == -1) {
47672393Syz155240 		KFREE(clone);
47682393Syz155240 		return NULL;
47692393Syz155240 	}
47702393Syz155240 	np = clone->nat_ptr;
47712393Syz155240 	if (np != NULL) {
47723448Sdh155122 		if (ifs->ifs_nat_logging)
47733448Sdh155122 			nat_log(clone, (u_int)np->in_redir, ifs);
47742393Syz155240 		np->in_use++;
47752393Syz155240 	}
47762393Syz155240 	fr = clone->nat_fr;
47772393Syz155240 	if (fr != NULL) {
47782393Syz155240 		MUTEX_ENTER(&fr->fr_lock);
47792393Syz155240 		fr->fr_ref++;
47802393Syz155240 		MUTEX_EXIT(&fr->fr_lock);
47812393Syz155240 	}
47822393Syz155240 
47832393Syz155240 	/*
47842393Syz155240 	 * Because the clone is created outside the normal loop of things and
47852393Syz155240 	 * TCP has special needs in terms of state, initialise the timeout
47862393Syz155240 	 * state of the new NAT from here.
47872393Syz155240 	 */
47882393Syz155240 	if (clone->nat_p == IPPROTO_TCP) {
47893448Sdh155122 		(void) fr_tcp_age(&clone->nat_tqe, fin, ifs->ifs_nat_tqb,
47902393Syz155240 				  clone->nat_flags);
47912393Syz155240 	}
47922393Syz155240 #ifdef	IPFILTER_SYNC
47932393Syz155240 	clone->nat_sync = ipfsync_new(SMC_NAT, fin, clone);
47942393Syz155240 #endif
47953448Sdh155122 	if (ifs->ifs_nat_logging)
47963448Sdh155122 		nat_log(clone, NL_CLONE, ifs);
47972393Syz155240 	return clone;
47982393Syz155240 }
47992393Syz155240 
48002393Syz155240 
48012393Syz155240 /* ------------------------------------------------------------------------ */
48022393Syz155240 /* Function:   nat_wildok                                                   */
48032393Syz155240 /* Returns:    int - 1 == packet's ports match wildcards                    */
48042393Syz155240 /*                   0 == packet's ports don't match wildcards              */
48052393Syz155240 /* Parameters: nat(I)   - NAT entry                                         */
48062393Syz155240 /*             sport(I) - source port                                       */
48072393Syz155240 /*             dport(I) - destination port                                  */
48082393Syz155240 /*             flags(I) - wildcard flags                                    */
48092393Syz155240 /*             dir(I)   - packet direction                                  */
48102393Syz155240 /*                                                                          */
48112393Syz155240 /* Use NAT entry and packet direction to determine which combination of     */
48122393Syz155240 /* wildcard flags should be used.                                           */
48132393Syz155240 /* ------------------------------------------------------------------------ */
48142393Syz155240 static INLINE int nat_wildok(nat, sport, dport, flags, dir)
48152393Syz155240 nat_t *nat;
48162393Syz155240 int sport;
48172393Syz155240 int dport;
48182393Syz155240 int flags;
48192393Syz155240 int dir;
48202393Syz155240 {
48212393Syz155240 	/*
48222393Syz155240 	 * When called by       dir is set to
48232393Syz155240 	 * nat_inlookup         NAT_INBOUND (0)
48242393Syz155240 	 * nat_outlookup        NAT_OUTBOUND (1)
48252393Syz155240 	 *
48262393Syz155240 	 * We simply combine the packet's direction in dir with the original
48272393Syz155240 	 * "intended" direction of that NAT entry in nat->nat_dir to decide
48282393Syz155240 	 * which combination of wildcard flags to allow.
48292393Syz155240 	 */
48302393Syz155240 
48312393Syz155240 	switch ((dir << 1) | nat->nat_dir)
48322393Syz155240 	{
48332393Syz155240 	case 3: /* outbound packet / outbound entry */
48342393Syz155240 		if (((nat->nat_inport == sport) ||
48352393Syz155240 		    (flags & SI_W_SPORT)) &&
48362393Syz155240 		    ((nat->nat_oport == dport) ||
48372393Syz155240 		    (flags & SI_W_DPORT)))
48382393Syz155240 			return 1;
48392393Syz155240 		break;
48402393Syz155240 	case 2: /* outbound packet / inbound entry */
48412393Syz155240 		if (((nat->nat_outport == sport) ||
48422393Syz155240 		    (flags & SI_W_DPORT)) &&
48432393Syz155240 		    ((nat->nat_oport == dport) ||
48442393Syz155240 		    (flags & SI_W_SPORT)))
48452393Syz155240 			return 1;
48462393Syz155240 		break;
48472393Syz155240 	case 1: /* inbound packet / outbound entry */
48482393Syz155240 		if (((nat->nat_oport == sport) ||
48492393Syz155240 		    (flags & SI_W_DPORT)) &&
48502393Syz155240 		    ((nat->nat_outport == dport) ||
48512393Syz155240 		    (flags & SI_W_SPORT)))
48522393Syz155240 			return 1;
48532393Syz155240 		break;
48542393Syz155240 	case 0: /* inbound packet / inbound entry */
48552393Syz155240 		if (((nat->nat_oport == sport) ||
48562393Syz155240 		    (flags & SI_W_SPORT)) &&
48572393Syz155240 		    ((nat->nat_outport == dport) ||
48582393Syz155240 		    (flags & SI_W_DPORT)))
48592393Syz155240 			return 1;
48602393Syz155240 		break;
48612393Syz155240 	default:
48622393Syz155240 		break;
48632393Syz155240 	}
48642393Syz155240 
48652393Syz155240 	return(0);
48662393Syz155240 }
48672393Syz155240 
48682393Syz155240 
48692393Syz155240 /* ------------------------------------------------------------------------ */
48702393Syz155240 /* Function:    nat_mssclamp                                                */
48712393Syz155240 /* Returns:     Nil                                                         */
48722393Syz155240 /* Parameters:  tcp(I)    - pointer to TCP header                           */
48732393Syz155240 /*              maxmss(I) - value to clamp the TCP MSS to                   */
48742393Syz155240 /*              csump(I)  - pointer to TCP checksum                         */
48752393Syz155240 /*                                                                          */
48762393Syz155240 /* Check for MSS option and clamp it if necessary.  If found and changed,   */
48772393Syz155240 /* then the TCP header checksum will be updated to reflect the change in    */
48782393Syz155240 /* the MSS.                                                                 */
48792393Syz155240 /* ------------------------------------------------------------------------ */
48802958Sdr146992 static void nat_mssclamp(tcp, maxmss, csump)
48812393Syz155240 tcphdr_t *tcp;
48822393Syz155240 u_32_t maxmss;
48832393Syz155240 u_short *csump;
48842393Syz155240 {
48852393Syz155240 	u_char *cp, *ep, opt;
48862393Syz155240 	int hlen, advance;
48872393Syz155240 	u_32_t mss, sumd;
48882393Syz155240 
48892393Syz155240 	hlen = TCP_OFF(tcp) << 2;
48902393Syz155240 	if (hlen > sizeof(*tcp)) {
48912393Syz155240 		cp = (u_char *)tcp + sizeof(*tcp);
48922393Syz155240 		ep = (u_char *)tcp + hlen;
48932393Syz155240 
48942393Syz155240 		while (cp < ep) {
48952393Syz155240 			opt = cp[0];
48962393Syz155240 			if (opt == TCPOPT_EOL)
48972393Syz155240 				break;
48982393Syz155240 			else if (opt == TCPOPT_NOP) {
48992393Syz155240 				cp++;
49002393Syz155240 				continue;
49012393Syz155240 			}
49022393Syz155240 
49032393Syz155240 			if (cp + 1 >= ep)
49042393Syz155240 				break;
49052393Syz155240 			advance = cp[1];
49062393Syz155240 			if ((cp + advance > ep) || (advance <= 0))
49072393Syz155240 				break;
49082393Syz155240 			switch (opt)
49092393Syz155240 			{
49102393Syz155240 			case TCPOPT_MAXSEG:
49112393Syz155240 				if (advance != 4)
49122393Syz155240 					break;
49132393Syz155240 				mss = cp[2] * 256 + cp[3];
49142393Syz155240 				if (mss > maxmss) {
49152393Syz155240 					cp[2] = maxmss / 256;
49162393Syz155240 					cp[3] = maxmss & 0xff;
49172393Syz155240 					CALC_SUMD(mss, maxmss, sumd);
49182958Sdr146992 					fix_outcksum(csump, sumd);
49192393Syz155240 				}
49202393Syz155240 				break;
49212393Syz155240 			default:
49222393Syz155240 				/* ignore unknown options */
49232393Syz155240 				break;
49242393Syz155240 			}
49252393Syz155240 
49262393Syz155240 			cp += advance;
49272393Syz155240 		}
49282393Syz155240 	}
49292393Syz155240 }
49302393Syz155240 
49312393Syz155240 
49322393Syz155240 /* ------------------------------------------------------------------------ */
49332393Syz155240 /* Function:    fr_setnatqueue                                              */
49342393Syz155240 /* Returns:     Nil                                                         */
49352393Syz155240 /* Parameters:  nat(I)- pointer to NAT structure                            */
49362393Syz155240 /*              rev(I) - forward(0) or reverse(1) direction                 */
49372393Syz155240 /* Locks:       ipf_nat (read or write)                                     */
49382393Syz155240 /*                                                                          */
49392393Syz155240 /* Put the NAT entry on its default queue entry, using rev as a helped in   */
49402393Syz155240 /* determining which queue it should be placed on.                          */
49412393Syz155240 /* ------------------------------------------------------------------------ */
49423448Sdh155122 void fr_setnatqueue(nat, rev, ifs)
49432393Syz155240 nat_t *nat;
49442393Syz155240 int rev;
49453448Sdh155122 ipf_stack_t *ifs;
49462393Syz155240 {
49472393Syz155240 	ipftq_t *oifq, *nifq;
49482393Syz155240 
49492393Syz155240 	if (nat->nat_ptr != NULL)
49502393Syz155240 		nifq = nat->nat_ptr->in_tqehead[rev];
49512393Syz155240 	else
49522393Syz155240 		nifq = NULL;
49532393Syz155240 
49542393Syz155240 	if (nifq == NULL) {
49552393Syz155240 		switch (nat->nat_p)
49562393Syz155240 		{
49572393Syz155240 		case IPPROTO_UDP :
49583448Sdh155122 			nifq = &ifs->ifs_nat_udptq;
49592393Syz155240 			break;
49602393Syz155240 		case IPPROTO_ICMP :
49613448Sdh155122 			nifq = &ifs->ifs_nat_icmptq;
49622393Syz155240 			break;
49632393Syz155240 		case IPPROTO_TCP :
49643448Sdh155122 			nifq = ifs->ifs_nat_tqb + nat->nat_tqe.tqe_state[rev];
49652393Syz155240 			break;
49662393Syz155240 		default :
49673448Sdh155122 			nifq = &ifs->ifs_nat_iptq;
49682393Syz155240 			break;
49692393Syz155240 		}
49702393Syz155240 	}
49712393Syz155240 
49722393Syz155240 	oifq = nat->nat_tqe.tqe_ifq;
49732393Syz155240 	/*
49742393Syz155240 	 * If it's currently on a timeout queue, move it from one queue to
49752393Syz155240 	 * another, else put it on the end of the newly determined queue.
49762393Syz155240 	 */
49772393Syz155240 	if (oifq != NULL)
49783448Sdh155122 		fr_movequeue(&nat->nat_tqe, oifq, nifq, ifs);
49792393Syz155240 	else
49803448Sdh155122 		fr_queueappend(&nat->nat_tqe, nifq, nat, ifs);
49812393Syz155240 	return;
49822393Syz155240 }
49833448Sdh155122 
49843448Sdh155122 /* Function:    nat_getnext                                                 */
49853448Sdh155122 /* Returns:     int - 0 == ok, else error                                   */
49863448Sdh155122 /* Parameters:  t(I)   - pointer to ipftoken structure                      */
49873448Sdh155122 /*              itp(I) - pointer to ipfgeniter_t structure                  */
49883448Sdh155122 /*                                                                          */
49893448Sdh155122 /* Fetch the next nat/ipnat structure pointer from the linked list and      */
49903448Sdh155122 /* copy it out to the storage space pointed to by itp_data.  The next item  */
49913448Sdh155122 /* in the list to look at is put back in the ipftoken struture.             */
49923448Sdh155122 /* If we call ipf_freetoken, the accompanying pointer is set to NULL because*/
49933448Sdh155122 /* ipf_freetoken will call a deref function for us and we dont want to call */
49943448Sdh155122 /* that twice (second time would be in the second switch statement below.   */
49953448Sdh155122 /* ------------------------------------------------------------------------ */
49963448Sdh155122 static int nat_getnext(t, itp, ifs)
49973448Sdh155122 ipftoken_t *t;
49983448Sdh155122 ipfgeniter_t *itp;
49993448Sdh155122 ipf_stack_t *ifs;
50003448Sdh155122 {
50013448Sdh155122 	hostmap_t *hm, *nexthm = NULL, zerohm;
50023448Sdh155122 	ipnat_t *ipn, *nextipnat = NULL, zeroipn;
50033448Sdh155122 	nat_t *nat, *nextnat = NULL, zeronat;
50043448Sdh155122 	int error = 0;
50053448Sdh155122 
50063448Sdh155122 	READ_ENTER(&ifs->ifs_ipf_nat);
50073448Sdh155122 	switch (itp->igi_type)
50083448Sdh155122 	{
50093448Sdh155122 	case IPFGENITER_HOSTMAP :
50103448Sdh155122 		hm = t->ipt_data;
50113448Sdh155122 		if (hm == NULL) {
50123448Sdh155122 			nexthm = ifs->ifs_ipf_hm_maplist;
50133448Sdh155122 		} else {
50143448Sdh155122 			nexthm = hm->hm_hnext;
50153448Sdh155122 		}
50163448Sdh155122 		if (nexthm != NULL) {
50173448Sdh155122 			if (nexthm->hm_hnext == NULL) {
50183448Sdh155122 				t->ipt_alive = 0;
50193796Szf203873 				ipf_unlinktoken(t, ifs);
50203796Szf203873 				KFREE(t);
50213448Sdh155122 			} else {
50223448Sdh155122 				/*MUTEX_ENTER(&nexthm->hm_lock);*/
50233448Sdh155122 				nexthm->hm_ref++;
50243448Sdh155122 				/*MUTEX_EXIT(&nextipnat->hm_lock);*/
50253448Sdh155122 			}
50263448Sdh155122 
50273448Sdh155122 		} else {
50283448Sdh155122 			bzero(&zerohm, sizeof(zerohm));
50293448Sdh155122 			nexthm = &zerohm;
50303796Szf203873 			ipf_freetoken(t, ifs);
50313448Sdh155122 		}
50323448Sdh155122 		break;
50333448Sdh155122 
50343448Sdh155122 	case IPFGENITER_IPNAT :
50353448Sdh155122 		ipn = t->ipt_data;
50363448Sdh155122 		if (ipn == NULL) {
50373448Sdh155122 			nextipnat = ifs->ifs_nat_list;
50383448Sdh155122 		} else {
50393448Sdh155122 			nextipnat = ipn->in_next;
50403448Sdh155122 		}
50413448Sdh155122 		if (nextipnat != NULL) {
50423448Sdh155122 			if (nextipnat->in_next == NULL) {
50433448Sdh155122 				t->ipt_alive = 0;
50443796Szf203873 				ipf_unlinktoken(t, ifs);
50453796Szf203873 				KFREE(t);
50463448Sdh155122 			} else {
50473448Sdh155122 				/* MUTEX_ENTER(&nextipnat->in_lock); */
50483448Sdh155122 				nextipnat->in_use++;
50493448Sdh155122 				/* MUTEX_EXIT(&nextipnat->in_lock); */
50503448Sdh155122 			}
50513448Sdh155122 		} else {
50523448Sdh155122 			bzero(&zeroipn, sizeof(zeroipn));
50533448Sdh155122 			nextipnat = &zeroipn;
50543796Szf203873 			ipf_freetoken(t, ifs);
50553448Sdh155122 		}
50563448Sdh155122 		break;
50573448Sdh155122 
50583448Sdh155122 	case IPFGENITER_NAT :
50593448Sdh155122 		nat = t->ipt_data;
50603448Sdh155122 		if (nat == NULL) {
50613448Sdh155122 			nextnat = ifs->ifs_nat_instances;
50623448Sdh155122 		} else {
50633448Sdh155122 			nextnat = nat->nat_next;
50643448Sdh155122 		}
50653448Sdh155122 		if (nextnat != NULL) {
50663448Sdh155122 			if (nextnat->nat_next == NULL) {
50673448Sdh155122 				t->ipt_alive = 0;
50683796Szf203873 				ipf_unlinktoken(t, ifs);
50693796Szf203873 				KFREE(t);
50703448Sdh155122 			} else {
50713448Sdh155122 				MUTEX_ENTER(&nextnat->nat_lock);
50723448Sdh155122 				nextnat->nat_ref++;
50733448Sdh155122 				MUTEX_EXIT(&nextnat->nat_lock);
50743448Sdh155122 			}
50753448Sdh155122 		} else {
50763448Sdh155122 			bzero(&zeronat, sizeof(zeronat));
50773448Sdh155122 			nextnat = &zeronat;
50783796Szf203873 			ipf_freetoken(t, ifs);
50793448Sdh155122 		}
50803448Sdh155122 		break;
50813448Sdh155122 	}
50823448Sdh155122 
50833448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
50843448Sdh155122 
50853448Sdh155122 	switch (itp->igi_type)
50863448Sdh155122 	{
50873448Sdh155122 	case IPFGENITER_HOSTMAP :
50883448Sdh155122 		if (hm != NULL) {
50893448Sdh155122 			WRITE_ENTER(&ifs->ifs_ipf_nat);
50903448Sdh155122 			fr_hostmapderef(&hm);
50913448Sdh155122 			RWLOCK_EXIT(&ifs->ifs_ipf_nat);
50923448Sdh155122 		}
50933796Szf203873 		if (nexthm->hm_hnext != NULL)
50943796Szf203873 			t->ipt_data = nexthm;
50953448Sdh155122 		error = COPYOUT(nexthm, itp->igi_data, sizeof(*nexthm));
50963448Sdh155122 		if (error != 0)
50973448Sdh155122 			error = EFAULT;
50983448Sdh155122 		break;
50993448Sdh155122 
51003448Sdh155122 	case IPFGENITER_IPNAT :
51013448Sdh155122 		if (ipn != NULL)
51023448Sdh155122 			fr_ipnatderef(&ipn, ifs);
51033796Szf203873 		if (nextipnat->in_next != NULL)
51043796Szf203873 			t->ipt_data = nextipnat;
51053448Sdh155122 		error = COPYOUT(nextipnat, itp->igi_data, sizeof(*nextipnat));
51063448Sdh155122 		if (error != 0)
51073448Sdh155122 			error = EFAULT;
51083448Sdh155122 		break;
51093448Sdh155122 
51103448Sdh155122 	case IPFGENITER_NAT :
51113448Sdh155122 		if (nat != NULL)
51123448Sdh155122 			fr_natderef(&nat, ifs);
51133796Szf203873 		if (nextnat->nat_next != NULL)
51143796Szf203873 			t->ipt_data = nextnat;
51153448Sdh155122 		error = COPYOUT(nextnat, itp->igi_data, sizeof(*nextnat));
51163448Sdh155122 		if (error != 0)
51173448Sdh155122 			error = EFAULT;
51183448Sdh155122 		break;
51193448Sdh155122 	}
51203448Sdh155122 
51213448Sdh155122 	return error;
51223448Sdh155122 }
51233448Sdh155122 
51243448Sdh155122 
51253448Sdh155122 /* ------------------------------------------------------------------------ */
51263448Sdh155122 /* Function:    nat_iterator                                                */
51273448Sdh155122 /* Returns:     int - 0 == ok, else error                                   */
51283448Sdh155122 /* Parameters:  token(I) - pointer to ipftoken structure                    */
51293448Sdh155122 /*              itp(I) - pointer to ipfgeniter_t structure                  */
51303448Sdh155122 /*                                                                          */
51313448Sdh155122 /* This function acts as a handler for the SIOCGENITER ioctls that use a    */
51323448Sdh155122 /* generic structure to iterate through a list.  There are three different  */
51333448Sdh155122 /* linked lists of NAT related information to go through: NAT rules, active */
51343448Sdh155122 /* NAT mappings and the NAT fragment cache.                                 */
51353448Sdh155122 /* ------------------------------------------------------------------------ */
51363448Sdh155122 static int nat_iterator(token, itp, ifs)
51373448Sdh155122 ipftoken_t *token;
51383448Sdh155122 ipfgeniter_t *itp;
51393448Sdh155122 ipf_stack_t *ifs;
51403448Sdh155122 {
51413448Sdh155122 	int error;
51423448Sdh155122 
51433448Sdh155122 	if (itp->igi_data == NULL)
51443448Sdh155122 		return EFAULT;
51453448Sdh155122 
51463448Sdh155122 	token->ipt_subtype = itp->igi_type;
51473448Sdh155122 
51483448Sdh155122 	switch (itp->igi_type)
51493448Sdh155122 	{
51503448Sdh155122 	case IPFGENITER_HOSTMAP :
51513448Sdh155122 	case IPFGENITER_IPNAT :
51523448Sdh155122 	case IPFGENITER_NAT :
51533448Sdh155122 		error = nat_getnext(token, itp, ifs);
51543448Sdh155122 		break;
51553448Sdh155122 	case IPFGENITER_NATFRAG :
51563448Sdh155122 		error = fr_nextfrag(token, itp, &ifs->ifs_ipfr_natlist,
51573448Sdh155122 				    &ifs->ifs_ipfr_nattail,
51583448Sdh155122 				    &ifs->ifs_ipf_natfrag, ifs);
51593448Sdh155122 		break;
51603448Sdh155122 	default :
51613448Sdh155122 		error = EINVAL;
51623448Sdh155122 		break;
51633448Sdh155122 	}
51643448Sdh155122 
51653448Sdh155122 	return error;
51663448Sdh155122 }
5167