xref: /onnv-gate/usr/src/uts/common/inet/ipf/ip_nat.c (revision 11210:646209cf7ff1)
12393Syz155240 /*
26295San207044  * Copyright (C) 1995-2004 by Darren Reed.
32393Syz155240  *
42393Syz155240  * See the IPFILTER.LICENCE file for details on licencing.
52393Syz155240  *
68624SDarren.Reed@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
72393Syz155240  * Use is subject to license terms.
82393Syz155240  */
92393Syz155240 
102393Syz155240 #if defined(KERNEL) || defined(_KERNEL)
112393Syz155240 # undef KERNEL
122393Syz155240 # undef _KERNEL
132393Syz155240 # define        KERNEL	1
142393Syz155240 # define        _KERNEL	1
152393Syz155240 #endif
162393Syz155240 #include <sys/errno.h>
172393Syz155240 #include <sys/types.h>
182393Syz155240 #include <sys/param.h>
192393Syz155240 #include <sys/time.h>
202393Syz155240 #include <sys/file.h>
212393Syz155240 #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \
222393Syz155240     defined(_KERNEL)
232393Syz155240 # include "opt_ipfilter_log.h"
242393Syz155240 #endif
252393Syz155240 #if !defined(_KERNEL)
262393Syz155240 # include <stdio.h>
272393Syz155240 # include <string.h>
282393Syz155240 # include <stdlib.h>
292393Syz155240 # define _KERNEL
302393Syz155240 # ifdef __OpenBSD__
312393Syz155240 struct file;
322393Syz155240 # endif
332393Syz155240 # include <sys/uio.h>
342393Syz155240 # undef _KERNEL
352393Syz155240 #endif
362393Syz155240 #if defined(_KERNEL) && (__FreeBSD_version >= 220000)
372393Syz155240 # include <sys/filio.h>
382393Syz155240 # include <sys/fcntl.h>
392393Syz155240 #else
402393Syz155240 # include <sys/ioctl.h>
412393Syz155240 #endif
422393Syz155240 #if !defined(AIX)
432393Syz155240 # include <sys/fcntl.h>
442393Syz155240 #endif
452393Syz155240 #if !defined(linux)
462393Syz155240 # include <sys/protosw.h>
472393Syz155240 #endif
482393Syz155240 #include <sys/socket.h>
492393Syz155240 #if defined(_KERNEL)
502393Syz155240 # include <sys/systm.h>
512393Syz155240 # if !defined(__SVR4) && !defined(__svr4__)
522393Syz155240 #  include <sys/mbuf.h>
532393Syz155240 # endif
542393Syz155240 #endif
552393Syz155240 #if defined(__SVR4) || defined(__svr4__)
562393Syz155240 # include <sys/filio.h>
572393Syz155240 # include <sys/byteorder.h>
582393Syz155240 # ifdef _KERNEL
592393Syz155240 #  include <sys/dditypes.h>
602393Syz155240 # endif
612393Syz155240 # include <sys/stream.h>
622393Syz155240 # include <sys/kmem.h>
632393Syz155240 #endif
642393Syz155240 #if __FreeBSD_version >= 300000
652393Syz155240 # include <sys/queue.h>
662393Syz155240 #endif
672393Syz155240 #include <net/if.h>
682393Syz155240 #if __FreeBSD_version >= 300000
692393Syz155240 # include <net/if_var.h>
702393Syz155240 # if defined(_KERNEL) && !defined(IPFILTER_LKM)
712393Syz155240 #  include "opt_ipfilter.h"
722393Syz155240 # endif
732393Syz155240 #endif
742393Syz155240 #ifdef sun
752393Syz155240 # include <net/af.h>
762393Syz155240 #endif
772393Syz155240 #include <net/route.h>
782393Syz155240 #include <netinet/in.h>
792393Syz155240 #include <netinet/in_systm.h>
802393Syz155240 #include <netinet/ip.h>
812393Syz155240 
822393Syz155240 #ifdef RFC1825
832393Syz155240 # include <vpn/md5.h>
842393Syz155240 # include <vpn/ipsec.h>
852393Syz155240 extern struct ifnet vpnif;
862393Syz155240 #endif
872393Syz155240 
882393Syz155240 #if !defined(linux)
892393Syz155240 # include <netinet/ip_var.h>
902393Syz155240 #endif
912393Syz155240 #include <netinet/tcp.h>
922393Syz155240 #include <netinet/udp.h>
932393Syz155240 #include <netinet/ip_icmp.h>
942393Syz155240 #include "netinet/ip_compat.h"
952393Syz155240 #include <netinet/tcpip.h>
962393Syz155240 #include "netinet/ip_fil.h"
972393Syz155240 #include "netinet/ip_nat.h"
982393Syz155240 #include "netinet/ip_frag.h"
992393Syz155240 #include "netinet/ip_state.h"
1002393Syz155240 #include "netinet/ip_proxy.h"
1013448Sdh155122 #include "netinet/ipf_stack.h"
1022393Syz155240 #ifdef	IPFILTER_SYNC
1032393Syz155240 #include "netinet/ip_sync.h"
1042393Syz155240 #endif
1052393Syz155240 #if (__FreeBSD_version >= 300000)
1062393Syz155240 # include <sys/malloc.h>
1072393Syz155240 #endif
1082393Syz155240 /* END OF INCLUDES */
1092393Syz155240 
1102393Syz155240 #undef	SOCKADDR_IN
1112393Syz155240 #define	SOCKADDR_IN	struct sockaddr_in
1122393Syz155240 
1132393Syz155240 #if !defined(lint)
1142393Syz155240 static const char sccsid[] = "@(#)ip_nat.c	1.11 6/5/96 (C) 1995 Darren Reed";
1152393Syz155240 static const char rcsid[] = "@(#)$Id: ip_nat.c,v 2.195.2.42 2005/08/11 19:51:36 darrenr Exp $";
1162393Syz155240 #endif
1172393Syz155240 
1182393Syz155240 
1192393Syz155240 /* ======================================================================== */
1202393Syz155240 /* How the NAT is organised and works.                                      */
1212393Syz155240 /*                                                                          */
1222393Syz155240 /* Inside (interface y) NAT       Outside (interface x)                     */
1232393Syz155240 /* -------------------- -+- -------------------------------------           */
1242393Syz155240 /* Packet going          |   out, processsed by fr_checknatout() for x      */
1252393Syz155240 /* ------------>         |   ------------>                                  */
1262393Syz155240 /* src=10.1.1.1          |   src=192.1.1.1                                  */
1272393Syz155240 /*                       |                                                  */
1282393Syz155240 /*                       |   in, processed by fr_checknatin() for x         */
1292393Syz155240 /* <------------         |   <------------                                  */
1302393Syz155240 /* dst=10.1.1.1          |   dst=192.1.1.1                                  */
1312393Syz155240 /* -------------------- -+- -------------------------------------           */
1322393Syz155240 /* fr_checknatout() - changes ip_src and if required, sport                 */
1332393Syz155240 /*             - creates a new mapping, if required.                        */
1342393Syz155240 /* fr_checknatin()  - changes ip_dst and if required, dport                 */
1352393Syz155240 /*                                                                          */
1362393Syz155240 /* In the NAT table, internal source is recorded as "in" and externally     */
1372393Syz155240 /* seen as "out".                                                           */
1382393Syz155240 /* ======================================================================== */
1392393Syz155240 
1402393Syz155240 
1413448Sdh155122 static	int	nat_clearlist __P((ipf_stack_t *));
1423448Sdh155122 static	void	nat_addnat __P((struct ipnat *, ipf_stack_t *));
1433448Sdh155122 static	void	nat_addrdr __P((struct ipnat *, ipf_stack_t *));
1443448Sdh155122 static	int	fr_natgetent __P((caddr_t, ipf_stack_t *));
1453448Sdh155122 static	int	fr_natgetsz __P((caddr_t, ipf_stack_t *));
1463448Sdh155122 static	int	fr_natputent __P((caddr_t, int, ipf_stack_t *));
1473448Sdh155122 static	void	nat_tabmove __P((nat_t *, ipf_stack_t *));
1482393Syz155240 static	int	nat_match __P((fr_info_t *, ipnat_t *));
1492393Syz155240 static	INLINE	int nat_newmap __P((fr_info_t *, nat_t *, natinfo_t *));
1502393Syz155240 static	INLINE	int nat_newrdr __P((fr_info_t *, nat_t *, natinfo_t *));
1512393Syz155240 static	hostmap_t *nat_hostmap __P((ipnat_t *, struct in_addr,
1523448Sdh155122 				    struct in_addr, struct in_addr, u_32_t,
1533448Sdh155122 				    ipf_stack_t *));
1542393Syz155240 static	INLINE	int nat_icmpquerytype4 __P((int));
1557176Syx160601 static	int	nat_ruleaddrinit __P((ipnat_t *));
1567176Syx160601 static	int	nat_siocaddnat __P((ipnat_t *, ipnat_t **, int, ipf_stack_t *));
1577176Syx160601 static	void	nat_siocdelnat __P((ipnat_t *, ipnat_t **, int, ipf_stack_t *));
1583448Sdh155122 static	INLINE	int nat_icmperrortype4 __P((int));
1592393Syz155240 static	INLINE	int nat_finalise __P((fr_info_t *, nat_t *, natinfo_t *,
1602393Syz155240 				      tcphdr_t *, nat_t **, int));
1614380Sjojemann static	INLINE	int nat_resolverule __P((ipnat_t *, ipf_stack_t *));
1622958Sdr146992 static	void	nat_mssclamp __P((tcphdr_t *, u_32_t, u_short *));
1633448Sdh155122 static	int	nat_getnext __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *));
1643448Sdh155122 static	int	nat_iterator __P((ipftoken_t *, ipfgeniter_t *, ipf_stack_t *));
1658170SJohn.Ojemann@Sun.COM static	int	nat_flushtable __P((int, ipf_stack_t *));
1662393Syz155240 
1676253San207044 #define NAT_HAS_L4_CHANGED(n)	\
1686295San207044  	(((n)->nat_flags & (IPN_TCPUDPICMP)) && \
1696295San207044  	(n)->nat_inport != (n)->nat_outport)
1702393Syz155240 
1718170SJohn.Ojemann@Sun.COM 
1722393Syz155240 /* ------------------------------------------------------------------------ */
1732393Syz155240 /* Function:    fr_natinit                                                  */
1742393Syz155240 /* Returns:     int - 0 == success, -1 == failure                           */
1752393Syz155240 /* Parameters:  Nil                                                         */
1762393Syz155240 /*                                                                          */
1772393Syz155240 /* Initialise all of the NAT locks, tables and other structures.            */
1782393Syz155240 /* ------------------------------------------------------------------------ */
fr_natinit(ifs)1793448Sdh155122 int fr_natinit(ifs)
1803448Sdh155122 ipf_stack_t *ifs;
1812393Syz155240 {
1822393Syz155240 	int i;
1832393Syz155240 
1843448Sdh155122 	KMALLOCS(ifs->ifs_nat_table[0], nat_t **,
1853448Sdh155122 		 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz);
1863448Sdh155122 	if (ifs->ifs_nat_table[0] != NULL)
1873448Sdh155122 		bzero((char *)ifs->ifs_nat_table[0],
1883448Sdh155122 		      ifs->ifs_ipf_nattable_sz * sizeof(nat_t *));
1892393Syz155240 	else
1902393Syz155240 		return -1;
1912393Syz155240 
1923448Sdh155122 	KMALLOCS(ifs->ifs_nat_table[1], nat_t **,
1933448Sdh155122 		 sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz);
1943448Sdh155122 	if (ifs->ifs_nat_table[1] != NULL)
1953448Sdh155122 		bzero((char *)ifs->ifs_nat_table[1],
1963448Sdh155122 		      ifs->ifs_ipf_nattable_sz * sizeof(nat_t *));
1972393Syz155240 	else
1982393Syz155240 		return -2;
1992393Syz155240 
2003448Sdh155122 	KMALLOCS(ifs->ifs_nat_rules, ipnat_t **,
2013448Sdh155122 		 sizeof(ipnat_t *) * ifs->ifs_ipf_natrules_sz);
2023448Sdh155122 	if (ifs->ifs_nat_rules != NULL)
2033448Sdh155122 		bzero((char *)ifs->ifs_nat_rules,
2043448Sdh155122 		      ifs->ifs_ipf_natrules_sz * sizeof(ipnat_t *));
2052393Syz155240 	else
2062393Syz155240 		return -3;
2072393Syz155240 
2083448Sdh155122 	KMALLOCS(ifs->ifs_rdr_rules, ipnat_t **,
2093448Sdh155122 		 sizeof(ipnat_t *) * ifs->ifs_ipf_rdrrules_sz);
2103448Sdh155122 	if (ifs->ifs_rdr_rules != NULL)
2113448Sdh155122 		bzero((char *)ifs->ifs_rdr_rules,
2123448Sdh155122 		      ifs->ifs_ipf_rdrrules_sz * sizeof(ipnat_t *));
2132393Syz155240 	else
2142393Syz155240 		return -4;
2152393Syz155240 
2163448Sdh155122 	KMALLOCS(ifs->ifs_maptable, hostmap_t **,
2173448Sdh155122 		 sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz);
2183448Sdh155122 	if (ifs->ifs_maptable != NULL)
2193448Sdh155122 		bzero((char *)ifs->ifs_maptable,
2203448Sdh155122 		      sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz);
2212393Syz155240 	else
2222393Syz155240 		return -5;
2232393Syz155240 
2243448Sdh155122 	ifs->ifs_ipf_hm_maplist = NULL;
2253448Sdh155122 
2263448Sdh155122 	KMALLOCS(ifs->ifs_nat_stats.ns_bucketlen[0], u_long *,
2273448Sdh155122 		 ifs->ifs_ipf_nattable_sz * sizeof(u_long));
2283448Sdh155122 	if (ifs->ifs_nat_stats.ns_bucketlen[0] == NULL)
2293448Sdh155122 		return -1;
2303448Sdh155122 	bzero((char *)ifs->ifs_nat_stats.ns_bucketlen[0],
2313448Sdh155122 	      ifs->ifs_ipf_nattable_sz * sizeof(u_long));
2323448Sdh155122 
2333448Sdh155122 	KMALLOCS(ifs->ifs_nat_stats.ns_bucketlen[1], u_long *,
2343448Sdh155122 		 ifs->ifs_ipf_nattable_sz * sizeof(u_long));
2353448Sdh155122 	if (ifs->ifs_nat_stats.ns_bucketlen[1] == NULL)
2363448Sdh155122 		return -1;
2373448Sdh155122 	bzero((char *)ifs->ifs_nat_stats.ns_bucketlen[1],
2383448Sdh155122 	      ifs->ifs_ipf_nattable_sz * sizeof(u_long));
2393448Sdh155122 
2403448Sdh155122 	if (ifs->ifs_fr_nat_maxbucket == 0) {
2413448Sdh155122 		for (i = ifs->ifs_ipf_nattable_sz; i > 0; i >>= 1)
2423448Sdh155122 			ifs->ifs_fr_nat_maxbucket++;
2433448Sdh155122 		ifs->ifs_fr_nat_maxbucket *= 2;
2442393Syz155240 	}
2452393Syz155240 
2463448Sdh155122 	fr_sttab_init(ifs->ifs_nat_tqb, ifs);
2472393Syz155240 	/*
2482393Syz155240 	 * Increase this because we may have "keep state" following this too
2492393Syz155240 	 * and packet storms can occur if this is removed too quickly.
2502393Syz155240 	 */
2513448Sdh155122 	ifs->ifs_nat_tqb[IPF_TCPS_CLOSED].ifq_ttl = ifs->ifs_fr_tcplastack;
2523448Sdh155122 	ifs->ifs_nat_tqb[IPF_TCP_NSTATES - 1].ifq_next = &ifs->ifs_nat_udptq;
2533448Sdh155122 	ifs->ifs_nat_udptq.ifq_ttl = ifs->ifs_fr_defnatage;
2543448Sdh155122 	ifs->ifs_nat_udptq.ifq_ref = 1;
2553448Sdh155122 	ifs->ifs_nat_udptq.ifq_head = NULL;
2563448Sdh155122 	ifs->ifs_nat_udptq.ifq_tail = &ifs->ifs_nat_udptq.ifq_head;
2573448Sdh155122 	MUTEX_INIT(&ifs->ifs_nat_udptq.ifq_lock, "nat ipftq udp tab");
2583448Sdh155122 	ifs->ifs_nat_udptq.ifq_next = &ifs->ifs_nat_icmptq;
2593448Sdh155122 	ifs->ifs_nat_icmptq.ifq_ttl = ifs->ifs_fr_defnaticmpage;
2603448Sdh155122 	ifs->ifs_nat_icmptq.ifq_ref = 1;
2613448Sdh155122 	ifs->ifs_nat_icmptq.ifq_head = NULL;
2623448Sdh155122 	ifs->ifs_nat_icmptq.ifq_tail = &ifs->ifs_nat_icmptq.ifq_head;
2633448Sdh155122 	MUTEX_INIT(&ifs->ifs_nat_icmptq.ifq_lock, "nat icmp ipftq tab");
2643448Sdh155122 	ifs->ifs_nat_icmptq.ifq_next = &ifs->ifs_nat_iptq;
2653448Sdh155122 	ifs->ifs_nat_iptq.ifq_ttl = ifs->ifs_fr_defnatipage;
2663448Sdh155122 	ifs->ifs_nat_iptq.ifq_ref = 1;
2673448Sdh155122 	ifs->ifs_nat_iptq.ifq_head = NULL;
2683448Sdh155122 	ifs->ifs_nat_iptq.ifq_tail = &ifs->ifs_nat_iptq.ifq_head;
2693448Sdh155122 	MUTEX_INIT(&ifs->ifs_nat_iptq.ifq_lock, "nat ip ipftq tab");
2703448Sdh155122 	ifs->ifs_nat_iptq.ifq_next = NULL;
2712393Syz155240 
2722393Syz155240 	for (i = 0; i < IPF_TCP_NSTATES; i++) {
2733448Sdh155122 		if (ifs->ifs_nat_tqb[i].ifq_ttl < ifs->ifs_fr_defnaticmpage)
2743448Sdh155122 			ifs->ifs_nat_tqb[i].ifq_ttl = ifs->ifs_fr_defnaticmpage;
2752393Syz155240 #ifdef LARGE_NAT
2763448Sdh155122 		else if (ifs->ifs_nat_tqb[i].ifq_ttl > ifs->ifs_fr_defnatage)
2773448Sdh155122 			ifs->ifs_nat_tqb[i].ifq_ttl = ifs->ifs_fr_defnatage;
2782393Syz155240 #endif
2792393Syz155240 	}
2802393Syz155240 
2812393Syz155240 	/*
2822393Syz155240 	 * Increase this because we may have "keep state" following
2832393Syz155240 	 * this too and packet storms can occur if this is removed
2842393Syz155240 	 * too quickly.
2852393Syz155240 	 */
2863448Sdh155122 	ifs->ifs_nat_tqb[IPF_TCPS_CLOSED].ifq_ttl =
2873448Sdh155122 	    ifs->ifs_nat_tqb[IPF_TCPS_LAST_ACK].ifq_ttl;
2883448Sdh155122 
2893448Sdh155122 	RWLOCK_INIT(&ifs->ifs_ipf_nat, "ipf IP NAT rwlock");
2903448Sdh155122 	RWLOCK_INIT(&ifs->ifs_ipf_natfrag, "ipf IP NAT-Frag rwlock");
2913448Sdh155122 	MUTEX_INIT(&ifs->ifs_ipf_nat_new, "ipf nat new mutex");
2923448Sdh155122 	MUTEX_INIT(&ifs->ifs_ipf_natio, "ipf nat io mutex");
2933448Sdh155122 
2943448Sdh155122 	ifs->ifs_fr_nat_init = 1;
2958170SJohn.Ojemann@Sun.COM 	ifs->ifs_nat_last_force_flush = ifs->ifs_fr_ticks;
2962393Syz155240 	return 0;
2972393Syz155240 }
2982393Syz155240 
2992393Syz155240 
3002393Syz155240 /* ------------------------------------------------------------------------ */
3012393Syz155240 /* Function:    nat_addrdr                                                  */
3022393Syz155240 /* Returns:     Nil                                                         */
3032393Syz155240 /* Parameters:  n(I) - pointer to NAT rule to add                           */
3042393Syz155240 /*                                                                          */
3052393Syz155240 /* Adds a redirect rule to the hash table of redirect rules and the list of */
3062393Syz155240 /* loaded NAT rules.  Updates the bitmask indicating which netmasks are in  */
3072393Syz155240 /* use by redirect rules.                                                   */
3082393Syz155240 /* ------------------------------------------------------------------------ */
nat_addrdr(n,ifs)3093448Sdh155122 static void nat_addrdr(n, ifs)
3102393Syz155240 ipnat_t *n;
3113448Sdh155122 ipf_stack_t *ifs;
3122393Syz155240 {
3132393Syz155240 	ipnat_t **np;
3142393Syz155240 	u_32_t j;
3152393Syz155240 	u_int hv;
3162393Syz155240 	int k;
3172393Syz155240 
3182393Syz155240 	k = count4bits(n->in_outmsk);
3192393Syz155240 	if ((k >= 0) && (k != 32))
3203448Sdh155122 		ifs->ifs_rdr_masks |= 1 << k;
3212393Syz155240 	j = (n->in_outip & n->in_outmsk);
3223448Sdh155122 	hv = NAT_HASH_FN(j, 0, ifs->ifs_ipf_rdrrules_sz);
3233448Sdh155122 	np = ifs->ifs_rdr_rules + hv;
3242393Syz155240 	while (*np != NULL)
3252393Syz155240 		np = &(*np)->in_rnext;
3262393Syz155240 	n->in_rnext = NULL;
3272393Syz155240 	n->in_prnext = np;
3282393Syz155240 	n->in_hv = hv;
3292393Syz155240 	*np = n;
3302393Syz155240 }
3312393Syz155240 
3322393Syz155240 
3332393Syz155240 /* ------------------------------------------------------------------------ */
3342393Syz155240 /* Function:    nat_addnat                                                  */
3352393Syz155240 /* Returns:     Nil                                                         */
3362393Syz155240 /* Parameters:  n(I) - pointer to NAT rule to add                           */
3372393Syz155240 /*                                                                          */
3382393Syz155240 /* Adds a NAT map rule to the hash table of rules and the list of  loaded   */
3392393Syz155240 /* NAT rules.  Updates the bitmask indicating which netmasks are in use by  */
3402393Syz155240 /* redirect rules.                                                          */
3412393Syz155240 /* ------------------------------------------------------------------------ */
nat_addnat(n,ifs)3423448Sdh155122 static void nat_addnat(n, ifs)
3432393Syz155240 ipnat_t *n;
3443448Sdh155122 ipf_stack_t *ifs;
3452393Syz155240 {
3462393Syz155240 	ipnat_t **np;
3472393Syz155240 	u_32_t j;
3482393Syz155240 	u_int hv;
3492393Syz155240 	int k;
3502393Syz155240 
3512393Syz155240 	k = count4bits(n->in_inmsk);
3522393Syz155240 	if ((k >= 0) && (k != 32))
3533448Sdh155122 		ifs->ifs_nat_masks |= 1 << k;
3542393Syz155240 	j = (n->in_inip & n->in_inmsk);
3553448Sdh155122 	hv = NAT_HASH_FN(j, 0, ifs->ifs_ipf_natrules_sz);
3563448Sdh155122 	np = ifs->ifs_nat_rules + hv;
3572393Syz155240 	while (*np != NULL)
3582393Syz155240 		np = &(*np)->in_mnext;
3592393Syz155240 	n->in_mnext = NULL;
3602393Syz155240 	n->in_pmnext = np;
3612393Syz155240 	n->in_hv = hv;
3622393Syz155240 	*np = n;
3632393Syz155240 }
3642393Syz155240 
3652393Syz155240 
3662393Syz155240 /* ------------------------------------------------------------------------ */
3672393Syz155240 /* Function:    nat_delrdr                                                  */
3682393Syz155240 /* Returns:     Nil                                                         */
3692393Syz155240 /* Parameters:  n(I) - pointer to NAT rule to delete                        */
3702393Syz155240 /*                                                                          */
3712393Syz155240 /* Removes a redirect rule from the hash table of redirect rules.           */
3722393Syz155240 /* ------------------------------------------------------------------------ */
nat_delrdr(n)3737176Syx160601 void nat_delrdr(n)
3742393Syz155240 ipnat_t *n;
3752393Syz155240 {
3762393Syz155240 	if (n->in_rnext)
3772393Syz155240 		n->in_rnext->in_prnext = n->in_prnext;
3782393Syz155240 	*n->in_prnext = n->in_rnext;
3792393Syz155240 }
3802393Syz155240 
3812393Syz155240 
3822393Syz155240 /* ------------------------------------------------------------------------ */
3832393Syz155240 /* Function:    nat_delnat                                                  */
3842393Syz155240 /* Returns:     Nil                                                         */
3852393Syz155240 /* Parameters:  n(I) - pointer to NAT rule to delete                        */
3862393Syz155240 /*                                                                          */
3872393Syz155240 /* Removes a NAT map rule from the hash table of NAT map rules.             */
3882393Syz155240 /* ------------------------------------------------------------------------ */
nat_delnat(n)3897176Syx160601 void nat_delnat(n)
3902393Syz155240 ipnat_t *n;
3912393Syz155240 {
3922393Syz155240 	if (n->in_mnext != NULL)
3932393Syz155240 		n->in_mnext->in_pmnext = n->in_pmnext;
3942393Syz155240 	*n->in_pmnext = n->in_mnext;
3952393Syz155240 }
3962393Syz155240 
3972393Syz155240 
3982393Syz155240 /* ------------------------------------------------------------------------ */
3992393Syz155240 /* Function:    nat_hostmap                                                 */
4002393Syz155240 /* Returns:     struct hostmap* - NULL if no hostmap could be created,      */
4012393Syz155240 /*                                else a pointer to the hostmapping to use  */
4022393Syz155240 /* Parameters:  np(I)   - pointer to NAT rule                               */
4032393Syz155240 /*              real(I) - real IP address                                   */
4042393Syz155240 /*              map(I)  - mapped IP address                                 */
4052393Syz155240 /*              port(I) - destination port number                           */
4062393Syz155240 /* Write Locks: ipf_nat                                                     */
4072393Syz155240 /*                                                                          */
4082393Syz155240 /* Check if an ip address has already been allocated for a given mapping    */
4092393Syz155240 /* that is not doing port based translation.  If is not yet allocated, then */
4102393Syz155240 /* create a new entry if a non-NULL NAT rule pointer has been supplied.     */
4112393Syz155240 /* ------------------------------------------------------------------------ */
nat_hostmap(np,src,dst,map,port,ifs)4123448Sdh155122 static struct hostmap *nat_hostmap(np, src, dst, map, port, ifs)
4132393Syz155240 ipnat_t *np;
4142393Syz155240 struct in_addr src;
4152393Syz155240 struct in_addr dst;
4162393Syz155240 struct in_addr map;
4172393Syz155240 u_32_t port;
4183448Sdh155122 ipf_stack_t *ifs;
4192393Syz155240 {
4202393Syz155240 	hostmap_t *hm;
4212393Syz155240 	u_int hv;
4222393Syz155240 
4232393Syz155240 	hv = (src.s_addr ^ dst.s_addr);
4242393Syz155240 	hv += src.s_addr;
4252393Syz155240 	hv += dst.s_addr;
4262393Syz155240 	hv %= HOSTMAP_SIZE;
4273448Sdh155122 	for (hm = ifs->ifs_maptable[hv]; hm; hm = hm->hm_next)
4282393Syz155240 		if ((hm->hm_srcip.s_addr == src.s_addr) &&
4292393Syz155240 		    (hm->hm_dstip.s_addr == dst.s_addr) &&
4302393Syz155240 		    ((np == NULL) || (np == hm->hm_ipnat)) &&
4312393Syz155240 		    ((port == 0) || (port == hm->hm_port))) {
4322393Syz155240 			hm->hm_ref++;
4332393Syz155240 			return hm;
4342393Syz155240 		}
4352393Syz155240 
4362393Syz155240 	if (np == NULL)
4372393Syz155240 		return NULL;
4382393Syz155240 
4392393Syz155240 	KMALLOC(hm, hostmap_t *);
4402393Syz155240 	if (hm) {
4413448Sdh155122 		hm->hm_hnext = ifs->ifs_ipf_hm_maplist;
4423448Sdh155122 		hm->hm_phnext = &ifs->ifs_ipf_hm_maplist;
4433448Sdh155122 		if (ifs->ifs_ipf_hm_maplist != NULL)
4443448Sdh155122 			ifs->ifs_ipf_hm_maplist->hm_phnext = &hm->hm_hnext;
4453448Sdh155122 		ifs->ifs_ipf_hm_maplist = hm;
4463448Sdh155122 
4473448Sdh155122 		hm->hm_next = ifs->ifs_maptable[hv];
4483448Sdh155122 		hm->hm_pnext = ifs->ifs_maptable + hv;
4493448Sdh155122 		if (ifs->ifs_maptable[hv] != NULL)
4503448Sdh155122 			ifs->ifs_maptable[hv]->hm_pnext = &hm->hm_next;
4513448Sdh155122 		ifs->ifs_maptable[hv] = hm;
4522393Syz155240 		hm->hm_ipnat = np;
4532393Syz155240 		hm->hm_srcip = src;
4542393Syz155240 		hm->hm_dstip = dst;
4552393Syz155240 		hm->hm_mapip = map;
4562393Syz155240 		hm->hm_ref = 1;
4572393Syz155240 		hm->hm_port = port;
4587176Syx160601 		hm->hm_v = 4;
4592393Syz155240 	}
4602393Syz155240 	return hm;
4612393Syz155240 }
4622393Syz155240 
4632393Syz155240 
4642393Syz155240 /* ------------------------------------------------------------------------ */
4655417Sjojemann /* Function:    fr_hostmapdel                                              */
4662393Syz155240 /* Returns:     Nil                                                         */
4675417Sjojemann /* Parameters:  hmp(I) - pointer to pointer to hostmap structure            */
4682393Syz155240 /* Write Locks: ipf_nat                                                     */
4692393Syz155240 /*                                                                          */
4702393Syz155240 /* Decrement the references to this hostmap structure by one.  If this      */
4712393Syz155240 /* reaches zero then remove it and free it.                                 */
4722393Syz155240 /* ------------------------------------------------------------------------ */
fr_hostmapdel(hmp)4735417Sjojemann void fr_hostmapdel(hmp)
4745417Sjojemann struct hostmap **hmp;
4752393Syz155240 {
4765417Sjojemann 	struct hostmap *hm;
4775417Sjojemann 
4785417Sjojemann 	hm = *hmp;
4795417Sjojemann 	*hmp = NULL;
4805417Sjojemann 
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 
4932393Syz155240 
4942393Syz155240 /* ------------------------------------------------------------------------ */
4952393Syz155240 /* Function:    fix_outcksum                                                */
4962393Syz155240 /* Returns:     Nil                                                         */
4972958Sdr146992 /* Parameters:  sp(I)  - location of 16bit checksum to update               */
4982393Syz155240 /*              n((I)  - amount to adjust checksum by                       */
4992393Syz155240 /*                                                                          */
5002393Syz155240 /* Adjusts the 16bit checksum by "n" for packets going out.                 */
5012393Syz155240 /* ------------------------------------------------------------------------ */
fix_outcksum(sp,n)5022958Sdr146992 void fix_outcksum(sp, n)
5032393Syz155240 u_short *sp;
5042393Syz155240 u_32_t n;
5052393Syz155240 {
5062393Syz155240 	u_short sumshort;
5072393Syz155240 	u_32_t sum1;
5082393Syz155240 
5092393Syz155240 	if (n == 0)
5102393Syz155240 		return;
5112393Syz155240 
5122393Syz155240 	sum1 = (~ntohs(*sp)) & 0xffff;
5132393Syz155240 	sum1 += (n);
5142393Syz155240 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
5152393Syz155240 	/* Again */
5162393Syz155240 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
5172393Syz155240 	sumshort = ~(u_short)sum1;
5182393Syz155240 	*(sp) = htons(sumshort);
5192393Syz155240 }
5202393Syz155240 
5212393Syz155240 
5222393Syz155240 /* ------------------------------------------------------------------------ */
5232393Syz155240 /* Function:    fix_incksum                                                 */
5242393Syz155240 /* Returns:     Nil                                                         */
5252958Sdr146992 /* Parameters:  sp(I)  - location of 16bit checksum to update               */
5262393Syz155240 /*              n((I)  - amount to adjust checksum by                       */
5272393Syz155240 /*                                                                          */
5282393Syz155240 /* Adjusts the 16bit checksum by "n" for packets going in.                  */
5292393Syz155240 /* ------------------------------------------------------------------------ */
fix_incksum(sp,n)5302958Sdr146992 void fix_incksum(sp, n)
5312393Syz155240 u_short *sp;
5322393Syz155240 u_32_t n;
5332393Syz155240 {
5342393Syz155240 	u_short sumshort;
5352393Syz155240 	u_32_t sum1;
5362393Syz155240 
5372393Syz155240 	if (n == 0)
5382393Syz155240 		return;
5392393Syz155240 
5402393Syz155240 	sum1 = (~ntohs(*sp)) & 0xffff;
5412393Syz155240 	sum1 += ~(n) & 0xffff;
5422393Syz155240 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
5432393Syz155240 	/* Again */
5442393Syz155240 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
5452393Syz155240 	sumshort = ~(u_short)sum1;
5462393Syz155240 	*(sp) = htons(sumshort);
5472393Syz155240 }
5482393Syz155240 
5492393Syz155240 
5502393Syz155240 /* ------------------------------------------------------------------------ */
5512393Syz155240 /* Function:    fix_datacksum                                               */
5522393Syz155240 /* Returns:     Nil                                                         */
5532393Syz155240 /* Parameters:  sp(I)  - location of 16bit checksum to update               */
5542393Syz155240 /*              n((I)  - amount to adjust checksum by                       */
5552393Syz155240 /*                                                                          */
5562393Syz155240 /* Fix_datacksum is used *only* for the adjustments of checksums in the     */
5572393Syz155240 /* data section of an IP packet.                                            */
5582393Syz155240 /*                                                                          */
5592393Syz155240 /* The only situation in which you need to do this is when NAT'ing an       */
5602393Syz155240 /* ICMP error message. Such a message, contains in its body the IP header   */
5612393Syz155240 /* of the original IP packet, that causes the error.                        */
5622393Syz155240 /*                                                                          */
5632393Syz155240 /* You can't use fix_incksum or fix_outcksum in that case, because for the  */
5642393Syz155240 /* kernel the data section of the ICMP error is just data, and no special   */
5652393Syz155240 /* processing like hardware cksum or ntohs processing have been done by the */
5662393Syz155240 /* kernel on the data section.                                              */
5672393Syz155240 /* ------------------------------------------------------------------------ */
fix_datacksum(sp,n)5682393Syz155240 void fix_datacksum(sp, n)
5692393Syz155240 u_short *sp;
5702393Syz155240 u_32_t n;
5712393Syz155240 {
5722393Syz155240 	u_short sumshort;
5732393Syz155240 	u_32_t sum1;
5742393Syz155240 
5752393Syz155240 	if (n == 0)
5762393Syz155240 		return;
5772393Syz155240 
5782393Syz155240 	sum1 = (~ntohs(*sp)) & 0xffff;
5792393Syz155240 	sum1 += (n);
5802393Syz155240 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
5812393Syz155240 	/* Again */
5822393Syz155240 	sum1 = (sum1 >> 16) + (sum1 & 0xffff);
5832393Syz155240 	sumshort = ~(u_short)sum1;
5842393Syz155240 	*(sp) = htons(sumshort);
5852393Syz155240 }
5862393Syz155240 
5872393Syz155240 
5882393Syz155240 /* ------------------------------------------------------------------------ */
5892393Syz155240 /* Function:    fr_nat_ioctl                                                */
5902393Syz155240 /* Returns:     int - 0 == success, != 0 == failure                         */
5912393Syz155240 /* Parameters:  data(I) - pointer to ioctl data                             */
5922393Syz155240 /*              cmd(I)  - ioctl command integer                             */
5932393Syz155240 /*              mode(I) - file mode bits used with open                     */
5948170SJohn.Ojemann@Sun.COM /*              uid(I)  - uid of caller                                     */
5958170SJohn.Ojemann@Sun.COM /*              ctx(I)  - pointer to give the uid context                   */
5968170SJohn.Ojemann@Sun.COM /*              ifs     - ipf stack instance                                */
5972393Syz155240 /*                                                                          */
5982393Syz155240 /* Processes an ioctl call made to operate on the IP Filter NAT device.     */
5992393Syz155240 /* ------------------------------------------------------------------------ */
fr_nat_ioctl(data,cmd,mode,uid,ctx,ifs)6003448Sdh155122 int fr_nat_ioctl(data, cmd, mode, uid, ctx, ifs)
6012393Syz155240 ioctlcmd_t cmd;
6022393Syz155240 caddr_t data;
6033448Sdh155122 int mode, uid;
6043448Sdh155122 void *ctx;
6053448Sdh155122 ipf_stack_t *ifs;
6062393Syz155240 {
6072393Syz155240 	ipnat_t *nat, *nt, *n = NULL, **np = NULL;
6082393Syz155240 	int error = 0, ret, arg, getlock;
6092393Syz155240 	ipnat_t natd;
6102393Syz155240 
6112393Syz155240 #if (BSD >= 199306) && defined(_KERNEL)
6122393Syz155240 	if ((securelevel >= 2) && (mode & FWRITE))
6132393Syz155240 		return EPERM;
6142393Syz155240 #endif
6152393Syz155240 
6162393Syz155240 #if defined(__osf__) && defined(_KERNEL)
6172393Syz155240 	getlock = 0;
6182393Syz155240 #else
6192393Syz155240 	getlock = (mode & NAT_LOCKHELD) ? 0 : 1;
6202393Syz155240 #endif
6212393Syz155240 
6222393Syz155240 	nat = NULL;     /* XXX gcc -Wuninitialized */
6232393Syz155240 	if (cmd == (ioctlcmd_t)SIOCADNAT) {
6242393Syz155240 		KMALLOC(nt, ipnat_t *);
6252393Syz155240 	} else {
6262393Syz155240 		nt = NULL;
6272393Syz155240 	}
6282393Syz155240 
6292393Syz155240 	if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) {
6302393Syz155240 		if (mode & NAT_SYSSPACE) {
6312393Syz155240 			bcopy(data, (char *)&natd, sizeof(natd));
6322393Syz155240 			error = 0;
6332393Syz155240 		} else {
6342393Syz155240 			error = fr_inobj(data, &natd, IPFOBJ_IPNAT);
6352393Syz155240 		}
6362393Syz155240 
6372393Syz155240 	}
6382393Syz155240 
6392393Syz155240 	if (error != 0)
6402393Syz155240 		goto done;
6412393Syz155240 
6422393Syz155240 	/*
6432393Syz155240 	 * For add/delete, look to see if the NAT entry is already present
6442393Syz155240 	 */
6452393Syz155240 	if ((cmd == (ioctlcmd_t)SIOCADNAT) || (cmd == (ioctlcmd_t)SIOCRMNAT)) {
6462393Syz155240 		nat = &natd;
6472393Syz155240 		if (nat->in_v == 0)	/* For backward compat. */
6482393Syz155240 			nat->in_v = 4;
6492393Syz155240 		nat->in_flags &= IPN_USERFLAGS;
6502393Syz155240 		if ((nat->in_redir & NAT_MAPBLK) == 0) {
6512393Syz155240 			if ((nat->in_flags & IPN_SPLIT) == 0)
6522393Syz155240 				nat->in_inip &= nat->in_inmsk;
6532393Syz155240 			if ((nat->in_flags & IPN_IPRANGE) == 0)
6542393Syz155240 				nat->in_outip &= nat->in_outmsk;
6552393Syz155240 		}
6563448Sdh155122 		MUTEX_ENTER(&ifs->ifs_ipf_natio);
6573448Sdh155122 		for (np = &ifs->ifs_nat_list; ((n = *np) != NULL);
6583448Sdh155122 		     np = &n->in_next)
6595828Sjojemann 			if (bcmp((char *)&nat->in_flags, (char *)&n->in_flags,
6605828Sjojemann 			    IPN_CMPSIZ) == 0) {
6615828Sjojemann 				if (nat->in_redir == NAT_REDIRECT &&
6625828Sjojemann 				    nat->in_pnext != n->in_pnext)
6635828Sjojemann 					continue;
6642393Syz155240 				break;
6655828Sjojemann 			}
6662393Syz155240 	}
6672393Syz155240 
6682393Syz155240 	switch (cmd)
6692393Syz155240 	{
6703448Sdh155122 	case SIOCGENITER :
6713448Sdh155122 	    {
6723448Sdh155122 		ipfgeniter_t iter;
6733448Sdh155122 		ipftoken_t *token;
6743448Sdh155122 
6753448Sdh155122 		error = fr_inobj(data, &iter, IPFOBJ_GENITER);
6763448Sdh155122 		if (error != 0)
6773448Sdh155122 			break;
6783448Sdh155122 
6793448Sdh155122 		token = ipf_findtoken(iter.igi_type, uid, ctx, ifs);
6803448Sdh155122 		if (token != NULL)
6813448Sdh155122 			error  = nat_iterator(token, &iter, ifs);
6823448Sdh155122 		else
6833448Sdh155122 			error = ESRCH;
6843448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_tokens);
6853448Sdh155122 		break;
6863448Sdh155122 	    }
6872393Syz155240 #ifdef  IPFILTER_LOG
6882393Syz155240 	case SIOCIPFFB :
6892393Syz155240 	{
6902393Syz155240 		int tmp;
6912393Syz155240 
6922393Syz155240 		if (!(mode & FWRITE))
6932393Syz155240 			error = EPERM;
6942393Syz155240 		else {
6953448Sdh155122 			tmp = ipflog_clear(IPL_LOGNAT, ifs);
6967433SJohn.Ojemann@Sun.COM 			error = BCOPYOUT((char *)&tmp, (char *)data,
6977433SJohn.Ojemann@Sun.COM 					sizeof(tmp));
6987433SJohn.Ojemann@Sun.COM 			if (error != 0)
6997433SJohn.Ojemann@Sun.COM 				error = EFAULT;
7002393Syz155240 		}
7012393Syz155240 		break;
7022393Syz155240 	}
7032393Syz155240 	case SIOCSETLG :
7047433SJohn.Ojemann@Sun.COM 		if (!(mode & FWRITE)) {
7052393Syz155240 			error = EPERM;
7067433SJohn.Ojemann@Sun.COM 		} else {
7077433SJohn.Ojemann@Sun.COM 			error = BCOPYIN((char *)data,
7087433SJohn.Ojemann@Sun.COM 					(char *)&ifs->ifs_nat_logging,
7097433SJohn.Ojemann@Sun.COM 					sizeof(ifs->ifs_nat_logging));
7107433SJohn.Ojemann@Sun.COM 			if (error != 0)
7117433SJohn.Ojemann@Sun.COM 				error = EFAULT;
7122393Syz155240 		}
7132393Syz155240 		break;
7142393Syz155240 	case SIOCGETLG :
7157433SJohn.Ojemann@Sun.COM 		error = BCOPYOUT((char *)&ifs->ifs_nat_logging, (char *)data,
7167433SJohn.Ojemann@Sun.COM 				sizeof(ifs->ifs_nat_logging));
7177433SJohn.Ojemann@Sun.COM 		if (error != 0)
7187433SJohn.Ojemann@Sun.COM 			error = EFAULT;
7192393Syz155240 		break;
7202393Syz155240 	case FIONREAD :
7213448Sdh155122 		arg = ifs->ifs_iplused[IPL_LOGNAT];
7227433SJohn.Ojemann@Sun.COM 		error = BCOPYOUT(&arg, data, sizeof(arg));
7237433SJohn.Ojemann@Sun.COM 		if (error != 0)
7247433SJohn.Ojemann@Sun.COM 			error = EFAULT;
7252393Syz155240 		break;
7262393Syz155240 #endif
7272393Syz155240 	case SIOCADNAT :
7282393Syz155240 		if (!(mode & FWRITE)) {
7292393Syz155240 			error = EPERM;
7302393Syz155240 		} else if (n != NULL) {
7312393Syz155240 			error = EEXIST;
7322393Syz155240 		} else if (nt == NULL) {
7332393Syz155240 			error = ENOMEM;
7342393Syz155240 		}
7352393Syz155240 		if (error != 0) {
7363448Sdh155122 			MUTEX_EXIT(&ifs->ifs_ipf_natio);
7372393Syz155240 			break;
7382393Syz155240 		}
7392393Syz155240 		bcopy((char *)nat, (char *)nt, sizeof(*n));
7403448Sdh155122 		error = nat_siocaddnat(nt, np, getlock, ifs);
7413448Sdh155122 		MUTEX_EXIT(&ifs->ifs_ipf_natio);
7422393Syz155240 		if (error == 0)
7432393Syz155240 			nt = NULL;
7442393Syz155240 		break;
7452393Syz155240 	case SIOCRMNAT :
7462393Syz155240 		if (!(mode & FWRITE)) {
7472393Syz155240 			error = EPERM;
7482393Syz155240 			n = NULL;
7492393Syz155240 		} else if (n == NULL) {
7502393Syz155240 			error = ESRCH;
7512393Syz155240 		}
7522393Syz155240 
7532393Syz155240 		if (error != 0) {
7543448Sdh155122 			MUTEX_EXIT(&ifs->ifs_ipf_natio);
7552393Syz155240 			break;
7562393Syz155240 		}
7573448Sdh155122 		nat_siocdelnat(n, np, getlock, ifs);
7583448Sdh155122 
7593448Sdh155122 		MUTEX_EXIT(&ifs->ifs_ipf_natio);
7602393Syz155240 		n = NULL;
7612393Syz155240 		break;
7622393Syz155240 	case SIOCGNATS :
7633448Sdh155122 		ifs->ifs_nat_stats.ns_table[0] = ifs->ifs_nat_table[0];
7643448Sdh155122 		ifs->ifs_nat_stats.ns_table[1] = ifs->ifs_nat_table[1];
7653448Sdh155122 		ifs->ifs_nat_stats.ns_list = ifs->ifs_nat_list;
7663448Sdh155122 		ifs->ifs_nat_stats.ns_maptable = ifs->ifs_maptable;
7673448Sdh155122 		ifs->ifs_nat_stats.ns_maplist = ifs->ifs_ipf_hm_maplist;
7683448Sdh155122 		ifs->ifs_nat_stats.ns_nattab_max = ifs->ifs_ipf_nattable_max;
7693448Sdh155122 		ifs->ifs_nat_stats.ns_nattab_sz = ifs->ifs_ipf_nattable_sz;
7703448Sdh155122 		ifs->ifs_nat_stats.ns_rultab_sz = ifs->ifs_ipf_natrules_sz;
7713448Sdh155122 		ifs->ifs_nat_stats.ns_rdrtab_sz = ifs->ifs_ipf_rdrrules_sz;
7723448Sdh155122 		ifs->ifs_nat_stats.ns_hostmap_sz = ifs->ifs_ipf_hostmap_sz;
7733448Sdh155122 		ifs->ifs_nat_stats.ns_instances = ifs->ifs_nat_instances;
7743448Sdh155122 		ifs->ifs_nat_stats.ns_apslist = ifs->ifs_ap_sess_list;
7753448Sdh155122 		error = fr_outobj(data, &ifs->ifs_nat_stats, IPFOBJ_NATSTAT);
7762393Syz155240 		break;
7772393Syz155240 	case SIOCGNATL :
7782393Syz155240 	    {
7792393Syz155240 		natlookup_t nl;
7802393Syz155240 
7812393Syz155240 		if (getlock) {
7823448Sdh155122 			READ_ENTER(&ifs->ifs_ipf_nat);
7832393Syz155240 		}
7842393Syz155240 		error = fr_inobj(data, &nl, IPFOBJ_NATLOOKUP);
7857176Syx160601 		if (nl.nl_v != 6)
7867176Syx160601 			nl.nl_v = 4;
7872393Syz155240 		if (error == 0) {
7887176Syx160601 			void *ptr;
7897176Syx160601 
7907176Syx160601 			switch (nl.nl_v)
7917176Syx160601 			{
7927176Syx160601 			case 4:
7937176Syx160601 				ptr = nat_lookupredir(&nl, ifs);
7947176Syx160601 				break;
7957176Syx160601 #ifdef	USE_INET6
7967176Syx160601 			case 6:
7977176Syx160601 				ptr = nat6_lookupredir(&nl, ifs);
7987176Syx160601 				break;
7997176Syx160601 #endif
8007176Syx160601 			default:
8017176Syx160601 				ptr = NULL;
8027176Syx160601 				break;
8037176Syx160601 			}
8047176Syx160601 
8057176Syx160601 			if (ptr != NULL) {
8062393Syz155240 				error = fr_outobj(data, &nl, IPFOBJ_NATLOOKUP);
8072393Syz155240 			} else {
8082393Syz155240 				error = ESRCH;
8092393Syz155240 			}
8102393Syz155240 		}
8112393Syz155240 		if (getlock) {
8123448Sdh155122 			RWLOCK_EXIT(&ifs->ifs_ipf_nat);
8132393Syz155240 		}
8142393Syz155240 		break;
8152393Syz155240 	    }
8162393Syz155240 	case SIOCIPFFL :	/* old SIOCFLNAT & SIOCCNATL */
8172393Syz155240 		if (!(mode & FWRITE)) {
8182393Syz155240 			error = EPERM;
8192393Syz155240 			break;
8202393Syz155240 		}
8212393Syz155240 		if (getlock) {
8223448Sdh155122 			WRITE_ENTER(&ifs->ifs_ipf_nat);
8232393Syz155240 		}
8247433SJohn.Ojemann@Sun.COM 		error = BCOPYIN(data, &arg, sizeof(arg));
8257433SJohn.Ojemann@Sun.COM 		if (error != 0) {
8267433SJohn.Ojemann@Sun.COM 			error = EFAULT;
8277433SJohn.Ojemann@Sun.COM 		} else {
8288170SJohn.Ojemann@Sun.COM 			if (arg == FLUSH_LIST)
8297433SJohn.Ojemann@Sun.COM 				ret = nat_clearlist(ifs);
8308170SJohn.Ojemann@Sun.COM 			else if (VALID_TABLE_FLUSH_OPT(arg))
8318170SJohn.Ojemann@Sun.COM 				ret = nat_flushtable(arg, ifs);
8327433SJohn.Ojemann@Sun.COM 			else
8337433SJohn.Ojemann@Sun.COM 				error = EINVAL;
8347433SJohn.Ojemann@Sun.COM 		}
8352393Syz155240 		if (getlock) {
8363448Sdh155122 			RWLOCK_EXIT(&ifs->ifs_ipf_nat);
8372393Syz155240 		}
8382393Syz155240 		if (error == 0) {
8397433SJohn.Ojemann@Sun.COM 			error = BCOPYOUT(&ret, data, sizeof(ret));
8407433SJohn.Ojemann@Sun.COM 			if (error != 0)
8417433SJohn.Ojemann@Sun.COM 				error = EFAULT;
8422393Syz155240 		}
8432393Syz155240 		break;
8442393Syz155240 	case SIOCPROXY :
8453448Sdh155122 		error = appr_ioctl(data, cmd, mode, ifs);
8462393Syz155240 		break;
8472393Syz155240 	case SIOCSTLCK :
8482393Syz155240 		if (!(mode & FWRITE)) {
8492393Syz155240 			error = EPERM;
8502393Syz155240 		} else {
8517433SJohn.Ojemann@Sun.COM 			error = fr_lock(data, &ifs->ifs_fr_nat_lock);
8522393Syz155240 		}
8532393Syz155240 		break;
8542393Syz155240 	case SIOCSTPUT :
8552761Sjojemann 		if ((mode & FWRITE) != 0) {
8563448Sdh155122 			error = fr_natputent(data, getlock, ifs);
8572393Syz155240 		} else {
8582393Syz155240 			error = EACCES;
8592393Syz155240 		}
8602393Syz155240 		break;
8612393Syz155240 	case SIOCSTGSZ :
8623448Sdh155122 		if (ifs->ifs_fr_nat_lock) {
8632393Syz155240 			if (getlock) {
8643448Sdh155122 				READ_ENTER(&ifs->ifs_ipf_nat);
8652393Syz155240 			}
8663448Sdh155122 			error = fr_natgetsz(data, ifs);
8672393Syz155240 			if (getlock) {
8683448Sdh155122 				RWLOCK_EXIT(&ifs->ifs_ipf_nat);
8692393Syz155240 			}
8702393Syz155240 		} else
8712393Syz155240 			error = EACCES;
8722393Syz155240 		break;
8732393Syz155240 	case SIOCSTGET :
8743448Sdh155122 		if (ifs->ifs_fr_nat_lock) {
8752393Syz155240 			if (getlock) {
8763448Sdh155122 				READ_ENTER(&ifs->ifs_ipf_nat);
8772393Syz155240 			}
8783448Sdh155122 			error = fr_natgetent(data, ifs);
8792393Syz155240 			if (getlock) {
8803448Sdh155122 				RWLOCK_EXIT(&ifs->ifs_ipf_nat);
8812393Syz155240 			}
8822393Syz155240 		} else
8832393Syz155240 			error = EACCES;
8842393Syz155240 		break;
8853448Sdh155122 	case SIOCIPFDELTOK :
8867433SJohn.Ojemann@Sun.COM 		error = BCOPYIN((caddr_t)data, (caddr_t)&arg, sizeof(arg));
8877433SJohn.Ojemann@Sun.COM 		if (error != 0) {
8887433SJohn.Ojemann@Sun.COM 			error = EFAULT;
8897433SJohn.Ojemann@Sun.COM 		} else {
8907433SJohn.Ojemann@Sun.COM 			error = ipf_deltoken(arg, uid, ctx, ifs);
8917433SJohn.Ojemann@Sun.COM 		}
8923448Sdh155122 		break;
8932393Syz155240 	default :
8942393Syz155240 		error = EINVAL;
8952393Syz155240 		break;
8962393Syz155240 	}
8972393Syz155240 done:
8982393Syz155240 	if (nt)
8992393Syz155240 		KFREE(nt);
9002393Syz155240 	return error;
9012393Syz155240 }
9022393Syz155240 
9032393Syz155240 
9042393Syz155240 /* ------------------------------------------------------------------------ */
9052393Syz155240 /* Function:    nat_siocaddnat                                              */
9062393Syz155240 /* Returns:     int - 0 == success, != 0 == failure                         */
9072393Syz155240 /* Parameters:  n(I)       - pointer to new NAT rule                        */
9082393Syz155240 /*              np(I)      - pointer to where to insert new NAT rule        */
9092393Syz155240 /*              getlock(I) - flag indicating if lock on ipf_nat is held     */
9102393Syz155240 /* Mutex Locks: ipf_natio                                                   */
9112393Syz155240 /*                                                                          */
9122393Syz155240 /* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
9132393Syz155240 /* from information passed to the kernel, then add it  to the appropriate   */
9142393Syz155240 /* NAT rule table(s).                                                       */
9152393Syz155240 /* ------------------------------------------------------------------------ */
nat_siocaddnat(n,np,getlock,ifs)9163448Sdh155122 static int nat_siocaddnat(n, np, getlock, ifs)
9172393Syz155240 ipnat_t *n, **np;
9182393Syz155240 int getlock;
9193448Sdh155122 ipf_stack_t *ifs;
9202393Syz155240 {
9212393Syz155240 	int error = 0, i, j;
9222393Syz155240 
9234380Sjojemann 	if (nat_resolverule(n, ifs) != 0)
9244380Sjojemann 		return ENOENT;
9252393Syz155240 
9262393Syz155240 	if ((n->in_age[0] == 0) && (n->in_age[1] != 0))
9272393Syz155240 		return EINVAL;
9282393Syz155240 
9292393Syz155240 	n->in_use = 0;
9302393Syz155240 	if (n->in_redir & NAT_MAPBLK)
9312393Syz155240 		n->in_space = USABLE_PORTS * ~ntohl(n->in_outmsk);
9322393Syz155240 	else if (n->in_flags & IPN_AUTOPORTMAP)
9332393Syz155240 		n->in_space = USABLE_PORTS * ~ntohl(n->in_inmsk);
9342393Syz155240 	else if (n->in_flags & IPN_IPRANGE)
9352393Syz155240 		n->in_space = ntohl(n->in_outmsk) - ntohl(n->in_outip);
9362393Syz155240 	else if (n->in_flags & IPN_SPLIT)
9372393Syz155240 		n->in_space = 2;
9382393Syz155240 	else if (n->in_outmsk != 0)
9392393Syz155240 		n->in_space = ~ntohl(n->in_outmsk);
9402393Syz155240 	else
9412393Syz155240 		n->in_space = 1;
942*11210SPaul.Wernau@Sun.COM 	if ((n->in_flags & NAT_TCPUDPICMPQ) && (n->in_redir != NAT_REDIRECT)) {
9438624SDarren.Reed@Sun.COM 		if (ntohs(n->in_pmax) < ntohs(n->in_pmin))
9448624SDarren.Reed@Sun.COM 			return EINVAL;
9458624SDarren.Reed@Sun.COM 	}
9462393Syz155240 
9472393Syz155240 	/*
9482393Syz155240 	 * Calculate the number of valid IP addresses in the output
9492393Syz155240 	 * mapping range.  In all cases, the range is inclusive of
9502393Syz155240 	 * the start and ending IP addresses.
9512393Syz155240 	 * If to a CIDR address, lose 2: broadcast + network address
9522393Syz155240 	 *                               (so subtract 1)
9532393Syz155240 	 * If to a range, add one.
9542393Syz155240 	 * If to a single IP address, set to 1.
9552393Syz155240 	 */
9562393Syz155240 	if (n->in_space) {
9572393Syz155240 		if ((n->in_flags & IPN_IPRANGE) != 0)
9582393Syz155240 			n->in_space += 1;
9592393Syz155240 		else
9602393Syz155240 			n->in_space -= 1;
9612393Syz155240 	} else
9622393Syz155240 		n->in_space = 1;
9632393Syz155240 
9647176Syx160601 #ifdef	USE_INET6
9657176Syx160601 	if (n->in_v == 6 && (n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0 &&
9667176Syx160601 	    !IP6_ISONES(&n->in_out[1]) && !IP6_ISZERO(&n->in_out[1]))
9677176Syx160601 		IP6_ADD(&n->in_out[0], 1, &n->in_next6)
9687176Syx160601 	else if (n->in_v == 6 &&
9697176Syx160601 	    (n->in_flags & IPN_SPLIT) && (n->in_redir & NAT_REDIRECT))
9707176Syx160601 		n->in_next6 = n->in_in[0];
9717176Syx160601 	else if (n->in_v == 6)
9727176Syx160601 		n->in_next6 = n->in_out[0];
9737176Syx160601 	else
9747176Syx160601 #endif
9752393Syz155240 	if ((n->in_outmsk != 0xffffffff) && (n->in_outmsk != 0) &&
9762393Syz155240 	    ((n->in_flags & (IPN_IPRANGE|IPN_SPLIT)) == 0))
9772393Syz155240 		n->in_nip = ntohl(n->in_outip) + 1;
9782393Syz155240 	else if ((n->in_flags & IPN_SPLIT) &&
9792393Syz155240 		 (n->in_redir & NAT_REDIRECT))
9802393Syz155240 		n->in_nip = ntohl(n->in_inip);
9812393Syz155240 	else
9822393Syz155240 		n->in_nip = ntohl(n->in_outip);
9837176Syx160601 
9842393Syz155240 	if (n->in_redir & NAT_MAP) {
9852393Syz155240 		n->in_pnext = ntohs(n->in_pmin);
9862393Syz155240 		/*
9872393Syz155240 		 * Multiply by the number of ports made available.
9882393Syz155240 		 */
9892393Syz155240 		if (ntohs(n->in_pmax) >= ntohs(n->in_pmin)) {
9902393Syz155240 			n->in_space *= (ntohs(n->in_pmax) -
9912393Syz155240 					ntohs(n->in_pmin) + 1);
9922393Syz155240 			/*
9932393Syz155240 			 * Because two different sources can map to
9942393Syz155240 			 * different destinations but use the same
9952393Syz155240 			 * local IP#/port #.
9962393Syz155240 			 * If the result is smaller than in_space, then
9972393Syz155240 			 * we may have wrapped around 32bits.
9982393Syz155240 			 */
9992393Syz155240 			i = n->in_inmsk;
10002393Syz155240 			if ((i != 0) && (i != 0xffffffff)) {
10012393Syz155240 				j = n->in_space * (~ntohl(i) + 1);
10022393Syz155240 				if (j >= n->in_space)
10032393Syz155240 					n->in_space = j;
10042393Syz155240 				else
10052393Syz155240 					n->in_space = 0xffffffff;
10062393Syz155240 			}
10072393Syz155240 		}
10082393Syz155240 		/*
10092393Syz155240 		 * If no protocol is specified, multiple by 256 to allow for
10102393Syz155240 		 * at least one IP:IP mapping per protocol.
10112393Syz155240 		 */
10122393Syz155240 		if ((n->in_flags & IPN_TCPUDPICMP) == 0) {
10132393Syz155240 				j = n->in_space * 256;
10142393Syz155240 				if (j >= n->in_space)
10152393Syz155240 					n->in_space = j;
10162393Syz155240 				else
10172393Syz155240 					n->in_space = 0xffffffff;
10182393Syz155240 		}
10192393Syz155240 	}
10202393Syz155240 
10212393Syz155240 	/* Otherwise, these fields are preset */
10222393Syz155240 
10232393Syz155240 	if (getlock) {
10243448Sdh155122 		WRITE_ENTER(&ifs->ifs_ipf_nat);
10252393Syz155240 	}
10262393Syz155240 	n->in_next = NULL;
10272393Syz155240 	*np = n;
10282393Syz155240 
10292393Syz155240 	if (n->in_age[0] != 0)
10303448Sdh155122 	    n->in_tqehead[0] = fr_addtimeoutqueue(&ifs->ifs_nat_utqe,
10313448Sdh155122 						  n->in_age[0], ifs);
10322393Syz155240 
10332393Syz155240 	if (n->in_age[1] != 0)
10343448Sdh155122 	    n->in_tqehead[1] = fr_addtimeoutqueue(&ifs->ifs_nat_utqe,
10353448Sdh155122 						  n->in_age[1], ifs);
10362393Syz155240 
10372393Syz155240 	if (n->in_redir & NAT_REDIRECT) {
10382393Syz155240 		n->in_flags &= ~IPN_NOTDST;
10397176Syx160601 		switch (n->in_v)
10407176Syx160601 		{
10417176Syx160601 		case 4 :
10427176Syx160601 			nat_addrdr(n, ifs);
10437176Syx160601 			break;
10447176Syx160601 #ifdef	USE_INET6
10457176Syx160601 		case 6 :
10467176Syx160601 			nat6_addrdr(n, ifs);
10477176Syx160601 			break;
10487176Syx160601 #endif
10497176Syx160601 		default :
10507176Syx160601 			break;
10517176Syx160601 		}
10522393Syz155240 	}
10532393Syz155240 	if (n->in_redir & (NAT_MAP|NAT_MAPBLK)) {
10542393Syz155240 		n->in_flags &= ~IPN_NOTSRC;
10557176Syx160601 		switch (n->in_v)
10567176Syx160601 		{
10577176Syx160601 		case 4 :
10587176Syx160601 			nat_addnat(n, ifs);
10597176Syx160601 			break;
10607176Syx160601 #ifdef	USE_INET6
10617176Syx160601 		case 6 :
10627176Syx160601 			nat6_addnat(n, ifs);
10637176Syx160601 			break;
10647176Syx160601 #endif
10657176Syx160601 		default :
10667176Syx160601 			break;
10677176Syx160601 		}
10682393Syz155240 	}
10692393Syz155240 	n = NULL;
10703448Sdh155122 	ifs->ifs_nat_stats.ns_rules++;
10712393Syz155240 	if (getlock) {
10723448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);			/* WRITE */
10732393Syz155240 	}
10742393Syz155240 
10752393Syz155240 	return error;
10762393Syz155240 }
10772393Syz155240 
10782393Syz155240 
10792393Syz155240 /* ------------------------------------------------------------------------ */
10802393Syz155240 /* Function:    nat_resolvrule                                              */
10814380Sjojemann /* Returns:     int - 0 == success, -1 == failure                           */
10822393Syz155240 /* Parameters:  n(I)  - pointer to NAT rule                                 */
10832393Syz155240 /*                                                                          */
10844380Sjojemann /* Resolve some of the details inside the NAT rule.  Includes resolving	    */
10854380Sjojemann /* any specified interfaces and proxy labels, and determines whether or not */
10864380Sjojemann /* all proxy labels are correctly specified.				    */
10874380Sjojemann /*									    */
10884380Sjojemann /* Called by nat_siocaddnat() (SIOCADNAT) and fr_natputent (SIOCSTPUT).     */
10892393Syz155240 /* ------------------------------------------------------------------------ */
nat_resolverule(n,ifs)10904380Sjojemann static int nat_resolverule(n, ifs)
10912393Syz155240 ipnat_t *n;
10923448Sdh155122 ipf_stack_t *ifs;
10932393Syz155240 {
10942393Syz155240 	n->in_ifnames[0][LIFNAMSIZ - 1] = '\0';
10957176Syx160601 	n->in_ifps[0] = fr_resolvenic(n->in_ifnames[0], n->in_v, ifs);
10962393Syz155240 
10972393Syz155240 	n->in_ifnames[1][LIFNAMSIZ - 1] = '\0';
10982393Syz155240 	if (n->in_ifnames[1][0] == '\0') {
10992393Syz155240 		(void) strncpy(n->in_ifnames[1], n->in_ifnames[0], LIFNAMSIZ);
11002393Syz155240 		n->in_ifps[1] = n->in_ifps[0];
11012393Syz155240 	} else {
11027176Syx160601 		n->in_ifps[1] = fr_resolvenic(n->in_ifnames[1], n->in_v, ifs);
11032393Syz155240 	}
11042393Syz155240 
11052393Syz155240 	if (n->in_plabel[0] != '\0') {
11063448Sdh155122 		n->in_apr = appr_lookup(n->in_p, n->in_plabel, ifs);
11074380Sjojemann 		if (n->in_apr == NULL)
11084380Sjojemann 			return -1;
11092393Syz155240 	}
11104380Sjojemann 	return 0;
11112393Syz155240 }
11122393Syz155240 
11132393Syz155240 
11142393Syz155240 /* ------------------------------------------------------------------------ */
11152393Syz155240 /* Function:    nat_siocdelnat                                              */
11162393Syz155240 /* Returns:     int - 0 == success, != 0 == failure                         */
11172393Syz155240 /* Parameters:  n(I)       - pointer to new NAT rule                        */
11182393Syz155240 /*              np(I)      - pointer to where to insert new NAT rule        */
11192393Syz155240 /*              getlock(I) - flag indicating if lock on ipf_nat is held     */
11202393Syz155240 /* Mutex Locks: ipf_natio                                                   */
11212393Syz155240 /*                                                                          */
11222393Syz155240 /* Handle SIOCADNAT.  Resolve and calculate details inside the NAT rule     */
11232393Syz155240 /* from information passed to the kernel, then add it  to the appropriate   */
11242393Syz155240 /* NAT rule table(s).                                                       */
11252393Syz155240 /* ------------------------------------------------------------------------ */
nat_siocdelnat(n,np,getlock,ifs)11263448Sdh155122 static void nat_siocdelnat(n, np, getlock, ifs)
11272393Syz155240 ipnat_t *n, **np;
11282393Syz155240 int getlock;
11293448Sdh155122 ipf_stack_t *ifs;
11302393Syz155240 {
11317176Syx160601 	int i;
11327176Syx160601 
11332393Syz155240 	if (getlock) {
11343448Sdh155122 		WRITE_ENTER(&ifs->ifs_ipf_nat);
11352393Syz155240 	}
11362393Syz155240 	if (n->in_redir & NAT_REDIRECT)
11372393Syz155240 		nat_delrdr(n);
11382393Syz155240 	if (n->in_redir & (NAT_MAPBLK|NAT_MAP))
11392393Syz155240 		nat_delnat(n);
11403448Sdh155122 	if (ifs->ifs_nat_list == NULL) {
11413448Sdh155122 		ifs->ifs_nat_masks = 0;
11423448Sdh155122 		ifs->ifs_rdr_masks = 0;
11437176Syx160601 		for (i = 0; i < 4; i++) {
11447176Syx160601 			ifs->ifs_nat6_masks[i] = 0;
11457176Syx160601 			ifs->ifs_rdr6_masks[i] = 0;
11467176Syx160601 		}
11472393Syz155240 	}
11482393Syz155240 
11492393Syz155240 	if (n->in_tqehead[0] != NULL) {
11502393Syz155240 		if (fr_deletetimeoutqueue(n->in_tqehead[0]) == 0) {
11514817San207044 			fr_freetimeoutqueue(n->in_tqehead[0], ifs);
11522393Syz155240 		}
11532393Syz155240 	}
11542393Syz155240 
11552393Syz155240 	if (n->in_tqehead[1] != NULL) {
11562393Syz155240 		if (fr_deletetimeoutqueue(n->in_tqehead[1]) == 0) {
11573448Sdh155122 			fr_freetimeoutqueue(n->in_tqehead[1], ifs);
11582393Syz155240 		}
11592393Syz155240 	}
11602393Syz155240 
11612393Syz155240 	*np = n->in_next;
11622393Syz155240 
11632393Syz155240 	if (n->in_use == 0) {
11642393Syz155240 		if (n->in_apr)
11652393Syz155240 			appr_free(n->in_apr);
11662393Syz155240 		KFREE(n);
11673448Sdh155122 		ifs->ifs_nat_stats.ns_rules--;
11682393Syz155240 	} else {
11692393Syz155240 		n->in_flags |= IPN_DELETE;
11702393Syz155240 		n->in_next = NULL;
11712393Syz155240 	}
11722393Syz155240 	if (getlock) {
11733448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);			/* READ/WRITE */
11742393Syz155240 	}
11752393Syz155240 }
11762393Syz155240 
11772393Syz155240 
11782393Syz155240 /* ------------------------------------------------------------------------ */
11792393Syz155240 /* Function:    fr_natgetsz                                                 */
11802393Syz155240 /* Returns:     int - 0 == success, != 0 is the error value.                */
11812393Syz155240 /* Parameters:  data(I) - pointer to natget structure with kernel pointer   */
11822393Syz155240 /*                        get the size of.                                  */
11832393Syz155240 /*                                                                          */
11842393Syz155240 /* Handle SIOCSTGSZ.                                                        */
11852393Syz155240 /* Return the size of the nat list entry to be copied back to user space.   */
11862393Syz155240 /* The size of the entry is stored in the ng_sz field and the enture natget */
11872393Syz155240 /* structure is copied back to the user.                                    */
11882393Syz155240 /* ------------------------------------------------------------------------ */
fr_natgetsz(data,ifs)11893448Sdh155122 static int fr_natgetsz(data, ifs)
11902393Syz155240 caddr_t data;
11913448Sdh155122 ipf_stack_t *ifs;
11922393Syz155240 {
11932393Syz155240 	ap_session_t *aps;
11942393Syz155240 	nat_t *nat, *n;
11952393Syz155240 	natget_t ng;
11967433SJohn.Ojemann@Sun.COM 	int err;
11977433SJohn.Ojemann@Sun.COM 
11987433SJohn.Ojemann@Sun.COM 	err = BCOPYIN(data, &ng, sizeof(ng));
11997433SJohn.Ojemann@Sun.COM 	if (err != 0)
12007433SJohn.Ojemann@Sun.COM 		return EFAULT;
12012393Syz155240 
12022393Syz155240 	nat = ng.ng_ptr;
12032393Syz155240 	if (!nat) {
12043448Sdh155122 		nat = ifs->ifs_nat_instances;
12052393Syz155240 		ng.ng_sz = 0;
12062393Syz155240 		/*
12072393Syz155240 		 * Empty list so the size returned is 0.  Simple.
12082393Syz155240 		 */
12092393Syz155240 		if (nat == NULL) {
12107433SJohn.Ojemann@Sun.COM 			err = BCOPYOUT(&ng, data, sizeof(ng));
12117433SJohn.Ojemann@Sun.COM 			if (err != 0) {
12127433SJohn.Ojemann@Sun.COM 				return EFAULT;
12137433SJohn.Ojemann@Sun.COM 			} else {
12147433SJohn.Ojemann@Sun.COM 				return 0;
12157433SJohn.Ojemann@Sun.COM 			}
12162393Syz155240 		}
12172393Syz155240 	} else {
12182393Syz155240 		/*
12192393Syz155240 		 * Make sure the pointer we're copying from exists in the
12202393Syz155240 		 * current list of entries.  Security precaution to prevent
12212393Syz155240 		 * copying of random kernel data.
12222393Syz155240 		 */
12233448Sdh155122 		for (n = ifs->ifs_nat_instances; n; n = n->nat_next)
12242393Syz155240 			if (n == nat)
12252393Syz155240 				break;
12262393Syz155240 		if (!n)
12272393Syz155240 			return ESRCH;
12282393Syz155240 	}
12292393Syz155240 
12302393Syz155240 	/*
12312393Syz155240 	 * Incluse any space required for proxy data structures.
12322393Syz155240 	 */
12332393Syz155240 	ng.ng_sz = sizeof(nat_save_t);
12342393Syz155240 	aps = nat->nat_aps;
12352393Syz155240 	if (aps != NULL) {
12362393Syz155240 		ng.ng_sz += sizeof(ap_session_t) - 4;
12372393Syz155240 		if (aps->aps_data != 0)
12382393Syz155240 			ng.ng_sz += aps->aps_psiz;
12392393Syz155240 	}
12402393Syz155240 
12417433SJohn.Ojemann@Sun.COM 	err = BCOPYOUT(&ng, data, sizeof(ng));
12427433SJohn.Ojemann@Sun.COM 	if (err != 0)
12437433SJohn.Ojemann@Sun.COM 		return EFAULT;
12442393Syz155240 	return 0;
12452393Syz155240 }
12462393Syz155240 
12472393Syz155240 
12482393Syz155240 /* ------------------------------------------------------------------------ */
12492393Syz155240 /* Function:    fr_natgetent                                                */
12502393Syz155240 /* Returns:     int - 0 == success, != 0 is the error value.                */
12512393Syz155240 /* Parameters:  data(I) - pointer to natget structure with kernel pointer   */
12522393Syz155240 /*                        to NAT structure to copy out.                     */
12532393Syz155240 /*                                                                          */
12542393Syz155240 /* Handle SIOCSTGET.                                                        */
12552393Syz155240 /* Copies out NAT entry to user space.  Any additional data held for a      */
12562393Syz155240 /* proxy is also copied, as to is the NAT rule which was responsible for it */
12572393Syz155240 /* ------------------------------------------------------------------------ */
fr_natgetent(data,ifs)12583448Sdh155122 static int fr_natgetent(data, ifs)
12592393Syz155240 caddr_t data;
12603448Sdh155122 ipf_stack_t *ifs;
12612393Syz155240 {
12622393Syz155240 	int error, outsize;
12632393Syz155240 	ap_session_t *aps;
12642393Syz155240 	nat_save_t *ipn, ipns;
12652393Syz155240 	nat_t *n, *nat;
12662393Syz155240 
12672393Syz155240 	error = fr_inobj(data, &ipns, IPFOBJ_NATSAVE);
12682393Syz155240 	if (error != 0)
12692393Syz155240 		return error;
12702393Syz155240 
12712393Syz155240 	if ((ipns.ipn_dsize < sizeof(ipns)) || (ipns.ipn_dsize > 81920))
12722393Syz155240 		return EINVAL;
12732393Syz155240 
12742393Syz155240 	KMALLOCS(ipn, nat_save_t *, ipns.ipn_dsize);
12752393Syz155240 	if (ipn == NULL)
12762393Syz155240 		return ENOMEM;
12772393Syz155240 
12782393Syz155240 	ipn->ipn_dsize = ipns.ipn_dsize;
12792393Syz155240 	nat = ipns.ipn_next;
12802393Syz155240 	if (nat == NULL) {
12813448Sdh155122 		nat = ifs->ifs_nat_instances;
12822393Syz155240 		if (nat == NULL) {
12833448Sdh155122 			if (ifs->ifs_nat_instances == NULL)
12842393Syz155240 				error = ENOENT;
12852393Syz155240 			goto finished;
12862393Syz155240 		}
12872393Syz155240 	} else {
12882393Syz155240 		/*
12892393Syz155240 		 * Make sure the pointer we're copying from exists in the
12902393Syz155240 		 * current list of entries.  Security precaution to prevent
12912393Syz155240 		 * copying of random kernel data.
12922393Syz155240 		 */
12933448Sdh155122 		for (n = ifs->ifs_nat_instances; n; n = n->nat_next)
12942393Syz155240 			if (n == nat)
12952393Syz155240 				break;
12962393Syz155240 		if (n == NULL) {
12972393Syz155240 			error = ESRCH;
12982393Syz155240 			goto finished;
12992393Syz155240 		}
13002393Syz155240 	}
13012393Syz155240 	ipn->ipn_next = nat->nat_next;
13022393Syz155240 
13032393Syz155240 	/*
13042393Syz155240 	 * Copy the NAT structure.
13052393Syz155240 	 */
13062393Syz155240 	bcopy((char *)nat, &ipn->ipn_nat, sizeof(*nat));
13072393Syz155240 
13082393Syz155240 	/*
13092393Syz155240 	 * If we have a pointer to the NAT rule it belongs to, save that too.
13102393Syz155240 	 */
13112393Syz155240 	if (nat->nat_ptr != NULL)
13122393Syz155240 		bcopy((char *)nat->nat_ptr, (char *)&ipn->ipn_ipnat,
13132393Syz155240 		      sizeof(ipn->ipn_ipnat));
13142393Syz155240 
13152393Syz155240 	/*
13162393Syz155240 	 * If we also know the NAT entry has an associated filter rule,
13172393Syz155240 	 * save that too.
13182393Syz155240 	 */
13192393Syz155240 	if (nat->nat_fr != NULL)
13202393Syz155240 		bcopy((char *)nat->nat_fr, (char *)&ipn->ipn_fr,
13212393Syz155240 		      sizeof(ipn->ipn_fr));
13222393Syz155240 
13232393Syz155240 	/*
13242393Syz155240 	 * Last but not least, if there is an application proxy session set
13252393Syz155240 	 * up for this NAT entry, then copy that out too, including any
13262393Syz155240 	 * private data saved along side it by the proxy.
13272393Syz155240 	 */
13282393Syz155240 	aps = nat->nat_aps;
13292393Syz155240 	outsize = ipn->ipn_dsize - sizeof(*ipn) + sizeof(ipn->ipn_data);
13302393Syz155240 	if (aps != NULL) {
13312393Syz155240 		char *s;
13322393Syz155240 
13332393Syz155240 		if (outsize < sizeof(*aps)) {
13342393Syz155240 			error = ENOBUFS;
13352393Syz155240 			goto finished;
13362393Syz155240 		}
13372393Syz155240 
13382393Syz155240 		s = ipn->ipn_data;
13392393Syz155240 		bcopy((char *)aps, s, sizeof(*aps));
13402393Syz155240 		s += sizeof(*aps);
13412393Syz155240 		outsize -= sizeof(*aps);
13422393Syz155240 		if ((aps->aps_data != NULL) && (outsize >= aps->aps_psiz))
13432393Syz155240 			bcopy(aps->aps_data, s, aps->aps_psiz);
13442393Syz155240 		else
13452393Syz155240 			error = ENOBUFS;
13462393Syz155240 	}
13472393Syz155240 	if (error == 0) {
13482393Syz155240 		error = fr_outobjsz(data, ipn, IPFOBJ_NATSAVE, ipns.ipn_dsize);
13492393Syz155240 	}
13502393Syz155240 
13512393Syz155240 finished:
13522393Syz155240 	if (ipn != NULL) {
13532393Syz155240 		KFREES(ipn, ipns.ipn_dsize);
13542393Syz155240 	}
13552393Syz155240 	return error;
13562393Syz155240 }
13572393Syz155240 
13586253San207044 /* ------------------------------------------------------------------------ */
13596253San207044 /* Function:    nat_calc_chksum_diffs					    */
13606253San207044 /* Returns:     void							    */
13616253San207044 /* Parameters:  nat	-	pointer to NAT table entry		    */
13626253San207044 /*                                                                          */
13636253San207044 /* Function calculates chksum deltas for IP header (nat_ipsumd) and TCP/UDP */
13646253San207044 /* headers (nat_sumd). The things for L4 (UDP/TCP) get complicated when     */
13656253San207044 /* we are dealing with partial chksum offload. For these cases we need to   */
13666253San207044 /* compute a 'partial chksum delta'. The 'partial chksum delta'is stored    */
13676253San207044 /* into nat_sumd[1], while ordinary chksum delta for TCP/UDP is in 	    */
13686253San207044 /* nat_sumd[0]. 							    */
13696253San207044 /*									    */
13706253San207044 /* The function accepts initialized NAT table entry and computes the deltas */
13716253San207044 /* from nat_inip/nat_outip members. The function is called right before	    */
13726253San207044 /* the new entry is inserted into the table.				    */
13736253San207044 /*									    */
13746253San207044 /* The ipsumd (IP hedaer chksum delta adjustment) is computed as a chksum   */
13756253San207044 /* of delta between original and new IP addresses.			    */
13766253San207044 /*									    */
13776253San207044 /* the nat_sumd[0] (TCP/UDP header chksum delta adjustment) is computed as  */
13786253San207044 /* a chkusm of delta between original an new IP addrress:port tupples.	    */
13796253San207044 /*									    */
13806253San207044 /* Some facts about chksum, we should remember:				    */
13816253San207044 /*	IP header chksum covers IP header only				    */
13826253San207044 /*									    */
13836253San207044 /*	TCP/UDP chksum covers data payload and so called pseudo header	    */
13846253San207044 /*		SRC, DST IP address					    */
13856253San207044 /*		SRC, DST Port						    */
13866253San207044 /*		length of payload					    */
13876253San207044 /*									    */
13886253San207044 /* The partial chksum delta (nat_sumd[1] is used to adjust db_ckusm16	    */
13896253San207044 /* member of dblk_t structure. The db_ckusm16 member is not part of 	    */
13906253San207044 /* IP/UDP/TCP header it is 16 bit value computed by NIC driver with partial */
13916253San207044 /* chksum offload capacbility for every inbound packet. The db_cksum16 is   */
13926253San207044 /* stored along with other IP packet data in dblk_t structure and used in   */
13936253San207044 /* for IP/UDP/TCP chksum validation later in ip.c. 			    */
13946253San207044 /*									    */
13956253San207044 /* The partial chksum delta (adjustment, nat_sumd[1]) is computed as chksum */
13966253San207044 /* of delta between new and orig address. NOTE: the order of operands for   */
13976253San207044 /* partial delta operation is swapped compared to computing the IP/TCP/UDP  */
13986253San207044 /* header adjustment. It is by design see (IP_CKSUM_RECV() macro in ip.c).  */
13996253San207044 /*									    */
14006253San207044 /* ------------------------------------------------------------------------ */
nat_calc_chksum_diffs(nat)14017176Syx160601 void nat_calc_chksum_diffs(nat)
14026253San207044 nat_t *nat;
14036253San207044 {
14046253San207044 	u_32_t	sum_orig = 0;
14056253San207044 	u_32_t	sum_changed = 0;
14066253San207044 	u_32_t	sumd;
14076253San207044 	u_32_t	ipsum_orig = 0;
14086253San207044 	u_32_t	ipsum_changed = 0;
14096253San207044 
14107176Syx160601 	if (nat->nat_v != 4 && nat->nat_v != 6)
14117176Syx160601 		return;
14127176Syx160601 
14136253San207044 	/*
14146253San207044 	 * the switch calculates operands for CALC_SUMD(),
14156253San207044 	 * which will compute the partial chksum delta.
14166253San207044 	 */
14176253San207044 	switch (nat->nat_dir)
14186253San207044 	{
14196253San207044 	case NAT_INBOUND:
14206253San207044 		/*
14216253San207044 		 * we are dealing with RDR rule (DST address gets
14226253San207044 		 * modified on packet from client)
14236253San207044 		 */
14247176Syx160601 		if (nat->nat_v == 4) {
14257176Syx160601 			sum_changed = LONG_SUM(ntohl(nat->nat_inip.s_addr));
14267176Syx160601 			sum_orig = LONG_SUM(ntohl(nat->nat_outip.s_addr));
14277176Syx160601 		} else {
14287176Syx160601 			sum_changed = LONG_SUM6(&nat->nat_inip6);
14297176Syx160601 			sum_orig = LONG_SUM6(&nat->nat_outip6);
14307176Syx160601 		}
14316253San207044 		break;
14326253San207044 	case NAT_OUTBOUND:
14336253San207044 		/*
14346253San207044 		 * we are dealing with MAP rule (SRC address gets
14356253San207044 		 * modified on packet from client)
14366253San207044 		 */
14377176Syx160601 		if (nat->nat_v == 4) {
14387176Syx160601 			sum_changed = LONG_SUM(ntohl(nat->nat_outip.s_addr));
14397176Syx160601 			sum_orig = LONG_SUM(ntohl(nat->nat_inip.s_addr));
14407176Syx160601 		} else {
14417176Syx160601 			sum_changed = LONG_SUM6(&nat->nat_outip6);
14427176Syx160601 			sum_orig = LONG_SUM6(&nat->nat_inip6);
14437176Syx160601 		}
14446253San207044 		break;
14456253San207044 	default: ;
14466253San207044 		break;
14476253San207044 	}
14486253San207044 
14496253San207044 	/*
14506253San207044 	 * we also preserve CALC_SUMD() operands here, for IP chksum delta
14516253San207044 	 * calculation, which happens at the end of function.
14526253San207044 	 */
14536253San207044 	ipsum_changed = sum_changed;
14546253San207044 	ipsum_orig = sum_orig;
14556253San207044 	/*
14566253San207044 	 * NOTE: the order of operands for partial chksum adjustment
14576253San207044 	 * computation has to be swapped!
14586253San207044 	 */
14596253San207044 	CALC_SUMD(sum_changed, sum_orig, sumd);
14606253San207044 	nat->nat_sumd[1] = (sumd & 0xffff) + (sumd >> 16);
14616253San207044 
14626295San207044 	if (nat->nat_flags & (IPN_TCPUDP | IPN_ICMPQUERY)) {
14636253San207044 
14646253San207044 		/*
14656253San207044 		 * switch calculates operands for CALC_SUMD(), which will
14666253San207044 		 * compute the full chksum delta.
14676253San207044 		 */
14686253San207044 		switch (nat->nat_dir)
14696253San207044 		{
14706253San207044 		case NAT_INBOUND:
14717176Syx160601 			if (nat->nat_v == 4) {
14727176Syx160601 				sum_changed = LONG_SUM(
14737176Syx160601 				    ntohl(nat->nat_inip.s_addr) +
14747176Syx160601 				    ntohs(nat->nat_inport));
14757176Syx160601 				sum_orig = LONG_SUM(
14767176Syx160601 				    ntohl(nat->nat_outip.s_addr) +
14777176Syx160601 				    ntohs(nat->nat_outport));
14787176Syx160601 			} else {
14797176Syx160601 				sum_changed = LONG_SUM6(&nat->nat_inip6) +
14807176Syx160601 				    ntohs(nat->nat_inport);
14817176Syx160601 				sum_orig = LONG_SUM6(&nat->nat_outip6) +
14827176Syx160601 				    ntohs(nat->nat_outport);
14837176Syx160601 			}
14846253San207044 			break;
14856253San207044 		case NAT_OUTBOUND:
14867176Syx160601 			if (nat->nat_v == 4) {
14877176Syx160601 				sum_changed = LONG_SUM(
14887176Syx160601 				    ntohl(nat->nat_outip.s_addr) +
14897176Syx160601 				    ntohs(nat->nat_outport));
14907176Syx160601 				sum_orig = LONG_SUM(
14917176Syx160601 				    ntohl(nat->nat_inip.s_addr) +
14927176Syx160601 				    ntohs(nat->nat_inport));
14937176Syx160601 			} else {
14947176Syx160601 				sum_changed = LONG_SUM6(&nat->nat_outip6) +
14957176Syx160601 				    ntohs(nat->nat_outport);
14967176Syx160601 				sum_orig = LONG_SUM6(&nat->nat_inip6) +
14977176Syx160601 				    ntohs(nat->nat_inport);
14987176Syx160601 			}
14996253San207044 			break;
15006253San207044 		default: ;
15016253San207044 			break;
15026253San207044 		}
15036253San207044 
15046253San207044 		CALC_SUMD(sum_orig, sum_changed, sumd);
15056253San207044 		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
15066295San207044 
15076295San207044 		if (!(nat->nat_flags & IPN_TCPUDP)) {
15086295San207044 			/*
15096295San207044 			 * partial HW chksum offload works for TCP/UDP headers only,
15106295San207044 			 * so we need to enforce full chksum adjustment for ICMP
15116295San207044 			 */
15126295San207044 			nat->nat_sumd[1] = nat->nat_sumd[0];
15136295San207044 		}
15146253San207044 	}
15156253San207044 	else
15166253San207044 		nat->nat_sumd[0] = nat->nat_sumd[1];
15176253San207044 
15186253San207044 	/*
15196253San207044 	 * we may reuse the already computed nat_sumd[0] for IP header chksum
15206253San207044 	 * adjustment in case the L4 (TCP/UDP header) is not changed by NAT.
15216253San207044 	 */
15227176Syx160601 	if (nat->nat_v == 4) {
15237176Syx160601 		if (NAT_HAS_L4_CHANGED(nat)) {
15247176Syx160601 			/*
15257176Syx160601 			 * bad luck, NAT changes also the L4 header, use IP
15267176Syx160601 			 * addresses to compute chksum adjustment for IP header.
15277176Syx160601 			 */
15287176Syx160601 			CALC_SUMD(ipsum_orig, ipsum_changed, sumd);
15297176Syx160601 			nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
15307176Syx160601 		} else {
15317176Syx160601 			/*
15327176Syx160601 			 * the NAT does not change L4 hdr -> reuse chksum
15337176Syx160601 			 * adjustment for IP hdr.
15347176Syx160601 			 */
15357176Syx160601 			nat->nat_ipsumd = nat->nat_sumd[0];
15367176Syx160601 
15377176Syx160601 			/*
15387176Syx160601 			 * if L4 header does not use chksum - zero out deltas
15397176Syx160601 			 */
15407176Syx160601 			if (!(nat->nat_flags & IPN_TCPUDP)) {
15417176Syx160601 				nat->nat_sumd[0] = 0;
15427176Syx160601 				nat->nat_sumd[1] = 0;
15437176Syx160601 			}
15446295San207044 		}
15456253San207044 	}
15466253San207044 
15476253San207044 	return;
15486253San207044 }
15492393Syz155240 
15502393Syz155240 /* ------------------------------------------------------------------------ */
15512393Syz155240 /* Function:    fr_natputent                                                */
15522393Syz155240 /* Returns:     int - 0 == success, != 0 is the error value.                */
15538170SJohn.Ojemann@Sun.COM /* Parameters:  data(I)    - pointer to natget structure with NAT           */
15548170SJohn.Ojemann@Sun.COM /*                           structure information to load into the kernel  */
15552393Syz155240 /*              getlock(I) - flag indicating whether or not a write lock    */
15562393Syz155240 /*                           on ipf_nat is already held.                    */
15578170SJohn.Ojemann@Sun.COM /*              ifs        - ipf stack instance                             */
15582393Syz155240 /*                                                                          */
15592393Syz155240 /* Handle SIOCSTPUT.                                                        */
15602393Syz155240 /* Loads a NAT table entry from user space, including a NAT rule, proxy and */
15612393Syz155240 /* firewall rule data structures, if pointers to them indicate so.          */
15622393Syz155240 /* ------------------------------------------------------------------------ */
fr_natputent(data,getlock,ifs)15633448Sdh155122 static int fr_natputent(data, getlock, ifs)
15642393Syz155240 caddr_t data;
15652393Syz155240 int getlock;
15663448Sdh155122 ipf_stack_t *ifs;
15672393Syz155240 {
15682393Syz155240 	nat_save_t ipn, *ipnn;
15692393Syz155240 	ap_session_t *aps;
15702393Syz155240 	nat_t *n, *nat;
15712393Syz155240 	frentry_t *fr;
15722393Syz155240 	fr_info_t fin;
15732393Syz155240 	ipnat_t *in;
15742393Syz155240 	int error;
15752393Syz155240 
15762393Syz155240 	error = fr_inobj(data, &ipn, IPFOBJ_NATSAVE);
15772393Syz155240 	if (error != 0)
15782393Syz155240 		return error;
15792393Syz155240 
15802393Syz155240 	/*
15818170SJohn.Ojemann@Sun.COM 	 * Trigger automatic call to nat_flushtable() if the
15824817San207044 	 * table has reached capcity specified by hi watermark.
15834817San207044 	 */
15848170SJohn.Ojemann@Sun.COM 	if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_level_hi)
15854817San207044 		ifs->ifs_nat_doflush = 1;
15864817San207044 
15874817San207044 	/*
15887432SJohn.Ojemann@Sun.COM 	 * If automatic flushing did not do its job, and the table
15897432SJohn.Ojemann@Sun.COM 	 * has filled up, don't try to create a new entry.
15907432SJohn.Ojemann@Sun.COM 	 */
15917432SJohn.Ojemann@Sun.COM 	if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) {
15927432SJohn.Ojemann@Sun.COM 		ifs->ifs_nat_stats.ns_memfail++;
15937432SJohn.Ojemann@Sun.COM 		return ENOMEM;
15947432SJohn.Ojemann@Sun.COM 	}
15957432SJohn.Ojemann@Sun.COM 
15967432SJohn.Ojemann@Sun.COM 	/*
15972393Syz155240 	 * Initialise early because of code at junkput label.
15982393Syz155240 	 */
15992393Syz155240 	in = NULL;
16002393Syz155240 	aps = NULL;
16012393Syz155240 	nat = NULL;
16022393Syz155240 	ipnn = NULL;
16032393Syz155240 
16042393Syz155240 	/*
16052393Syz155240 	 * New entry, copy in the rest of the NAT entry if it's size is more
16062393Syz155240 	 * than just the nat_t structure.
16072393Syz155240 	 */
16082393Syz155240 	fr = NULL;
16092393Syz155240 	if (ipn.ipn_dsize > sizeof(ipn)) {
16102393Syz155240 		if (ipn.ipn_dsize > 81920) {
16112393Syz155240 			error = ENOMEM;
16122393Syz155240 			goto junkput;
16132393Syz155240 		}
16142393Syz155240 
16152393Syz155240 		KMALLOCS(ipnn, nat_save_t *, ipn.ipn_dsize);
16162393Syz155240 		if (ipnn == NULL)
16172393Syz155240 			return ENOMEM;
16182393Syz155240 
16192393Syz155240 		error = fr_inobjsz(data, ipnn, IPFOBJ_NATSAVE, ipn.ipn_dsize);
16202393Syz155240 		if (error != 0) {
16212393Syz155240 			error = EFAULT;
16222393Syz155240 			goto junkput;
16232393Syz155240 		}
16242393Syz155240 	} else
16252393Syz155240 		ipnn = &ipn;
16262393Syz155240 
16272393Syz155240 	KMALLOC(nat, nat_t *);
16282393Syz155240 	if (nat == NULL) {
16292393Syz155240 		error = ENOMEM;
16302393Syz155240 		goto junkput;
16312393Syz155240 	}
16322393Syz155240 
16332393Syz155240 	bcopy((char *)&ipnn->ipn_nat, (char *)nat, sizeof(*nat));
16342393Syz155240 	/*
16352393Syz155240 	 * Initialize all these so that nat_delete() doesn't cause a crash.
16362393Syz155240 	 */
16372393Syz155240 	bzero((char *)nat, offsetof(struct nat, nat_tqe));
16382393Syz155240 	nat->nat_tqe.tqe_pnext = NULL;
16392393Syz155240 	nat->nat_tqe.tqe_next = NULL;
16402393Syz155240 	nat->nat_tqe.tqe_ifq = NULL;
16412393Syz155240 	nat->nat_tqe.tqe_parent = nat;
16422393Syz155240 
16432393Syz155240 	/*
16442393Syz155240 	 * Restore the rule associated with this nat session
16452393Syz155240 	 */
16462393Syz155240 	in = ipnn->ipn_nat.nat_ptr;
16472393Syz155240 	if (in != NULL) {
16482393Syz155240 		KMALLOC(in, ipnat_t *);
16492393Syz155240 		nat->nat_ptr = in;
16502393Syz155240 		if (in == NULL) {
16512393Syz155240 			error = ENOMEM;
16522393Syz155240 			goto junkput;
16532393Syz155240 		}
16542393Syz155240 		bzero((char *)in, offsetof(struct ipnat, in_next6));
16552393Syz155240 		bcopy((char *)&ipnn->ipn_ipnat, (char *)in, sizeof(*in));
16562393Syz155240 		in->in_use = 1;
16572393Syz155240 		in->in_flags |= IPN_DELETE;
16582393Syz155240 
16593448Sdh155122 		ATOMIC_INC(ifs->ifs_nat_stats.ns_rules);
16603448Sdh155122 
16614380Sjojemann 		if (nat_resolverule(in, ifs) != 0) {
16624380Sjojemann 			error = ESRCH;
16634380Sjojemann 			goto junkput;
16644380Sjojemann 		}
16652393Syz155240 	}
16662393Syz155240 
16672393Syz155240 	/*
16682393Syz155240 	 * Check that the NAT entry doesn't already exist in the kernel.
16692393Syz155240 	 */
16707176Syx160601 	if (nat->nat_v != 6)
16717176Syx160601 		nat->nat_v = 4;
16722393Syz155240 	bzero((char *)&fin, sizeof(fin));
16732393Syz155240 	fin.fin_p = nat->nat_p;
16744414Sjojemann 	fin.fin_ifs = ifs;
16752393Syz155240 	if (nat->nat_dir == NAT_OUTBOUND) {
16762393Syz155240 		fin.fin_data[0] = ntohs(nat->nat_oport);
16772393Syz155240 		fin.fin_data[1] = ntohs(nat->nat_outport);
16782508Syz155240 		fin.fin_ifp = nat->nat_ifps[0];
16792393Syz155240 		if (getlock) {
16803448Sdh155122 			READ_ENTER(&ifs->ifs_ipf_nat);
16812393Syz155240 		}
16827176Syx160601 
16837176Syx160601 		switch (nat->nat_v)
16847176Syx160601 		{
16857176Syx160601 		case 4:
16867176Syx160601 			fin.fin_v = nat->nat_v;
16877176Syx160601 			n = nat_inlookup(&fin, nat->nat_flags, fin.fin_p,
16887176Syx160601 			    nat->nat_oip, nat->nat_outip);
16897176Syx160601 			break;
16907176Syx160601 #ifdef USE_INET6
16917176Syx160601 		case 6:
16927176Syx160601 			n = nat6_inlookup(&fin, nat->nat_flags, fin.fin_p,
16937176Syx160601 			    &nat->nat_oip6.in6, &nat->nat_outip6.in6);
16947176Syx160601 			break;
16957176Syx160601 #endif
16967176Syx160601 		default:
16977176Syx160601 			n = NULL;
16987176Syx160601 			break;
16997176Syx160601 		}
17007176Syx160601 
17012393Syz155240 		if (getlock) {
17023448Sdh155122 			RWLOCK_EXIT(&ifs->ifs_ipf_nat);
17032393Syz155240 		}
17042393Syz155240 		if (n != NULL) {
17052393Syz155240 			error = EEXIST;
17062393Syz155240 			goto junkput;
17072393Syz155240 		}
17082393Syz155240 	} else if (nat->nat_dir == NAT_INBOUND) {
17092393Syz155240 		fin.fin_data[0] = ntohs(nat->nat_inport);
17102393Syz155240 		fin.fin_data[1] = ntohs(nat->nat_oport);
17112508Syz155240 		fin.fin_ifp = nat->nat_ifps[1];
17122393Syz155240 		if (getlock) {
17133448Sdh155122 			READ_ENTER(&ifs->ifs_ipf_nat);
17142393Syz155240 		}
17157176Syx160601 
17167176Syx160601 		switch (nat->nat_v)
17177176Syx160601 		{
17187176Syx160601 		case 4:
17197176Syx160601 			n = nat_outlookup(&fin, nat->nat_flags, fin.fin_p,
17207176Syx160601 			    nat->nat_inip, nat->nat_oip);
17217176Syx160601 			break;
17227176Syx160601 #ifdef USE_INET6
17237176Syx160601 		case 6:
17247176Syx160601 			n = nat6_outlookup(&fin, nat->nat_flags, fin.fin_p,
17257176Syx160601 			    &nat->nat_inip6.in6, &nat->nat_oip6.in6);
17267176Syx160601 			break;
17277176Syx160601 #endif
17287176Syx160601 		default:
17297176Syx160601 			n = NULL;
17307176Syx160601 			break;
17317176Syx160601 		}
17327176Syx160601 
17332393Syz155240 		if (getlock) {
17343448Sdh155122 			RWLOCK_EXIT(&ifs->ifs_ipf_nat);
17352393Syz155240 		}
17362393Syz155240 		if (n != NULL) {
17372393Syz155240 			error = EEXIST;
17382393Syz155240 			goto junkput;
17392393Syz155240 		}
17402393Syz155240 	} else {
17412393Syz155240 		error = EINVAL;
17422393Syz155240 		goto junkput;
17432393Syz155240 	}
17442393Syz155240 
17452393Syz155240 	/*
17462393Syz155240 	 * Restore ap_session_t structure.  Include the private data allocated
17472393Syz155240 	 * if it was there.
17482393Syz155240 	 */
17492393Syz155240 	aps = nat->nat_aps;
17502393Syz155240 	if (aps != NULL) {
17512393Syz155240 		KMALLOC(aps, ap_session_t *);
17522393Syz155240 		nat->nat_aps = aps;
17532393Syz155240 		if (aps == NULL) {
17542393Syz155240 			error = ENOMEM;
17552393Syz155240 			goto junkput;
17562393Syz155240 		}
17572393Syz155240 		bcopy(ipnn->ipn_data, (char *)aps, sizeof(*aps));
17582393Syz155240 		if (in != NULL)
17592393Syz155240 			aps->aps_apr = in->in_apr;
17602393Syz155240 		else
17612393Syz155240 			aps->aps_apr = NULL;
17622393Syz155240 		if (aps->aps_psiz != 0) {
17632393Syz155240 			if (aps->aps_psiz > 81920) {
17642393Syz155240 				error = ENOMEM;
17652393Syz155240 				goto junkput;
17662393Syz155240 			}
17672393Syz155240 			KMALLOCS(aps->aps_data, void *, aps->aps_psiz);
17682393Syz155240 			if (aps->aps_data == NULL) {
17692393Syz155240 				error = ENOMEM;
17702393Syz155240 				goto junkput;
17712393Syz155240 			}
17722393Syz155240 			bcopy(ipnn->ipn_data + sizeof(*aps), aps->aps_data,
17732393Syz155240 			      aps->aps_psiz);
17742393Syz155240 		} else {
17752393Syz155240 			aps->aps_psiz = 0;
17762393Syz155240 			aps->aps_data = NULL;
17772393Syz155240 		}
17782393Syz155240 	}
17792393Syz155240 
17802393Syz155240 	/*
17812393Syz155240 	 * If there was a filtering rule associated with this entry then
17822393Syz155240 	 * build up a new one.
17832393Syz155240 	 */
17842393Syz155240 	fr = nat->nat_fr;
17852393Syz155240 	if (fr != NULL) {
17862393Syz155240 		if ((nat->nat_flags & SI_NEWFR) != 0) {
17872393Syz155240 			KMALLOC(fr, frentry_t *);
17882393Syz155240 			nat->nat_fr = fr;
17892393Syz155240 			if (fr == NULL) {
17902393Syz155240 				error = ENOMEM;
17912393Syz155240 				goto junkput;
17922393Syz155240 			}
17932393Syz155240 			ipnn->ipn_nat.nat_fr = fr;
17942393Syz155240 			(void) fr_outobj(data, ipnn, IPFOBJ_NATSAVE);
17952393Syz155240 			bcopy((char *)&ipnn->ipn_fr, (char *)fr, sizeof(*fr));
17964380Sjojemann 
17974380Sjojemann 			fr->fr_ref = 1;
17984380Sjojemann 			fr->fr_dsize = 0;
17994380Sjojemann 			fr->fr_data = NULL;
18004380Sjojemann 			fr->fr_type = FR_T_NONE;
18014380Sjojemann 
18022393Syz155240 			MUTEX_NUKE(&fr->fr_lock);
18032393Syz155240 			MUTEX_INIT(&fr->fr_lock, "nat-filter rule lock");
18042393Syz155240 		} else {
18054380Sjojemann 			if (getlock) {
18064380Sjojemann 				READ_ENTER(&ifs->ifs_ipf_nat);
18074380Sjojemann 			}
18083448Sdh155122 			for (n = ifs->ifs_nat_instances; n; n = n->nat_next)
18092393Syz155240 				if (n->nat_fr == fr)
18102393Syz155240 					break;
18112393Syz155240 
18122393Syz155240 			if (n != NULL) {
18132393Syz155240 				MUTEX_ENTER(&fr->fr_lock);
18142393Syz155240 				fr->fr_ref++;
18152393Syz155240 				MUTEX_EXIT(&fr->fr_lock);
18162393Syz155240 			}
18174380Sjojemann 			if (getlock) {
18184380Sjojemann 				RWLOCK_EXIT(&ifs->ifs_ipf_nat);
18194380Sjojemann 			}
18202393Syz155240 			if (!n) {
18212393Syz155240 				error = ESRCH;
18222393Syz155240 				goto junkput;
18232393Syz155240 			}
18242393Syz155240 		}
18252393Syz155240 	}
18262393Syz155240 
18272393Syz155240 	if (ipnn != &ipn) {
18282393Syz155240 		KFREES(ipnn, ipn.ipn_dsize);
18292393Syz155240 		ipnn = NULL;
18302393Syz155240 	}
18312393Syz155240 
18326253San207044 	nat_calc_chksum_diffs(nat);
18336253San207044 
18342393Syz155240 	if (getlock) {
18353448Sdh155122 		WRITE_ENTER(&ifs->ifs_ipf_nat);
18362393Syz155240 	}
18377176Syx160601 
18387176Syx160601 	nat_calc_chksum_diffs(nat);
18397176Syx160601 
18407176Syx160601 	switch (nat->nat_v)
18417176Syx160601 	{
18427176Syx160601 	case 4 :
18437176Syx160601 		error = nat_insert(nat, nat->nat_rev, ifs);
18447176Syx160601 		break;
18457176Syx160601 #ifdef USE_INET6
18467176Syx160601 	case 6 :
18477176Syx160601 		error = nat6_insert(nat, nat->nat_rev, ifs);
18487176Syx160601 		break;
18497176Syx160601 #endif
18507176Syx160601 	default :
18517176Syx160601 		break;
18527176Syx160601 	}
18537176Syx160601 
18542393Syz155240 	if ((error == 0) && (aps != NULL)) {
18553448Sdh155122 		aps->aps_next = ifs->ifs_ap_sess_list;
18563448Sdh155122 		ifs->ifs_ap_sess_list = aps;
18572393Syz155240 	}
18582393Syz155240 	if (getlock) {
18593448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
18602393Syz155240 	}
18612393Syz155240 
18622393Syz155240 	if (error == 0)
18632393Syz155240 		return 0;
18642393Syz155240 
18652393Syz155240 	error = ENOMEM;
18662393Syz155240 
18672393Syz155240 junkput:
18682393Syz155240 	if (fr != NULL)
18693448Sdh155122 		(void) fr_derefrule(&fr, ifs);
18702393Syz155240 
18712393Syz155240 	if ((ipnn != NULL) && (ipnn != &ipn)) {
18722393Syz155240 		KFREES(ipnn, ipn.ipn_dsize);
18732393Syz155240 	}
18742393Syz155240 	if (nat != NULL) {
18752393Syz155240 		if (aps != NULL) {
18762393Syz155240 			if (aps->aps_data != NULL) {
18772393Syz155240 				KFREES(aps->aps_data, aps->aps_psiz);
18782393Syz155240 			}
18792393Syz155240 			KFREE(aps);
18802393Syz155240 		}
18812393Syz155240 		if (in != NULL) {
18822393Syz155240 			if (in->in_apr)
18832393Syz155240 				appr_free(in->in_apr);
18842393Syz155240 			KFREE(in);
18852393Syz155240 		}
18862393Syz155240 		KFREE(nat);
18872393Syz155240 	}
18882393Syz155240 	return error;
18892393Syz155240 }
18902393Syz155240 
18912393Syz155240 
18922393Syz155240 /* ------------------------------------------------------------------------ */
18932393Syz155240 /* Function:    nat_delete                                                  */
18948170SJohn.Ojemann@Sun.COM /* Returns:     int	- 0 if entry deleted. Otherwise, ref count on entry */
18958170SJohn.Ojemann@Sun.COM /* Parameters:  nat	- pointer to the NAT entry to delete		    */
18968170SJohn.Ojemann@Sun.COM /*		logtype	- type of LOG record to create before deleting	    */
18978170SJohn.Ojemann@Sun.COM /*		ifs	- ipf stack instance				    */
18982393Syz155240 /* Write Lock:  ipf_nat                                                     */
18992393Syz155240 /*                                                                          */
19002393Syz155240 /* Delete a nat entry from the various lists and table.  If NAT logging is  */
19012393Syz155240 /* enabled then generate a NAT log record for this event.                   */
19022393Syz155240 /* ------------------------------------------------------------------------ */
nat_delete(nat,logtype,ifs)19038170SJohn.Ojemann@Sun.COM int nat_delete(nat, logtype, ifs)
19042393Syz155240 struct nat *nat;
19052393Syz155240 int logtype;
19063448Sdh155122 ipf_stack_t *ifs;
19072393Syz155240 {
19082393Syz155240 	struct ipnat *ipn;
19097432SJohn.Ojemann@Sun.COM 	int removed = 0;
19102393Syz155240 
19113448Sdh155122 	if (logtype != 0 && ifs->ifs_nat_logging != 0)
19123448Sdh155122 		nat_log(nat, logtype, ifs);
19133448Sdh155122 
19142393Syz155240 	/*
19157432SJohn.Ojemann@Sun.COM 	 * Start by removing the entry from the hash table of nat entries
19167432SJohn.Ojemann@Sun.COM 	 * so it will not be "used" again.
19177432SJohn.Ojemann@Sun.COM 	 *
19187432SJohn.Ojemann@Sun.COM 	 * It will remain in the "list" of nat entries until all references
19197432SJohn.Ojemann@Sun.COM 	 * have been accounted for.
19202393Syz155240 	 */
19217432SJohn.Ojemann@Sun.COM 	if ((nat->nat_phnext[0] != NULL) && (nat->nat_phnext[1] != NULL)) {
19227432SJohn.Ojemann@Sun.COM 		removed = 1;
19237432SJohn.Ojemann@Sun.COM 
19243448Sdh155122 		ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
19253448Sdh155122 		ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
19262393Syz155240 
19272393Syz155240 		*nat->nat_phnext[0] = nat->nat_hnext[0];
19282393Syz155240 		if (nat->nat_hnext[0] != NULL) {
19292393Syz155240 			nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
19302393Syz155240 			nat->nat_hnext[0] = NULL;
19312393Syz155240 		}
19322393Syz155240 		nat->nat_phnext[0] = NULL;
19332393Syz155240 
19342393Syz155240 		*nat->nat_phnext[1] = nat->nat_hnext[1];
19352393Syz155240 		if (nat->nat_hnext[1] != NULL) {
19362393Syz155240 			nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
19372393Syz155240 			nat->nat_hnext[1] = NULL;
19382393Syz155240 		}
19392393Syz155240 		nat->nat_phnext[1] = NULL;
19402393Syz155240 
19412393Syz155240 		if ((nat->nat_flags & SI_WILDP) != 0)
19423448Sdh155122 			ifs->ifs_nat_stats.ns_wilds--;
19432393Syz155240 	}
19442393Syz155240 
19457432SJohn.Ojemann@Sun.COM 	/*
19467432SJohn.Ojemann@Sun.COM 	 * Next, remove it from the timeout queue it is in.
19477432SJohn.Ojemann@Sun.COM 	 */
19487432SJohn.Ojemann@Sun.COM 	fr_deletequeueentry(&nat->nat_tqe);
19497432SJohn.Ojemann@Sun.COM 
19502393Syz155240 	if (nat->nat_me != NULL) {
19512393Syz155240 		*nat->nat_me = NULL;
19522393Syz155240 		nat->nat_me = NULL;
19532393Syz155240 	}
19542393Syz155240 
19555055Sdr146992 	MUTEX_ENTER(&nat->nat_lock);
19568624SDarren.Reed@Sun.COM  	if (nat->nat_ref > 1) {
19575055Sdr146992 		nat->nat_ref--;
19585055Sdr146992 		MUTEX_EXIT(&nat->nat_lock);
19597432SJohn.Ojemann@Sun.COM  		if (removed)
19607432SJohn.Ojemann@Sun.COM  			ifs->ifs_nat_stats.ns_orphans++;
19618170SJohn.Ojemann@Sun.COM 		return (nat->nat_ref);
19622393Syz155240 	}
19635055Sdr146992 	MUTEX_EXIT(&nat->nat_lock);
19645055Sdr146992 
19657432SJohn.Ojemann@Sun.COM 	nat->nat_ref = 0;
19667432SJohn.Ojemann@Sun.COM 
19675055Sdr146992 	/*
19687432SJohn.Ojemann@Sun.COM 	 * If entry had already been removed,
19697432SJohn.Ojemann@Sun.COM 	 * it means we're cleaning up an orphan.
19705055Sdr146992 	 */
19717432SJohn.Ojemann@Sun.COM  	if (!removed)
19727432SJohn.Ojemann@Sun.COM  		ifs->ifs_nat_stats.ns_orphans--;
19732393Syz155240 
19742393Syz155240 #ifdef	IPFILTER_SYNC
19752393Syz155240 	if (nat->nat_sync)
19762393Syz155240 		ipfsync_del(nat->nat_sync);
19772393Syz155240 #endif
19782393Syz155240 
19797432SJohn.Ojemann@Sun.COM 	/*
19807432SJohn.Ojemann@Sun.COM 	 * Now remove it from master list of nat table entries
19817432SJohn.Ojemann@Sun.COM 	 */
19827432SJohn.Ojemann@Sun.COM 	if (nat->nat_pnext != NULL) {
19837432SJohn.Ojemann@Sun.COM 		*nat->nat_pnext = nat->nat_next;
19847432SJohn.Ojemann@Sun.COM 		if (nat->nat_next != NULL) {
19857432SJohn.Ojemann@Sun.COM 			nat->nat_next->nat_pnext = nat->nat_pnext;
19867432SJohn.Ojemann@Sun.COM 			nat->nat_next = NULL;
19877432SJohn.Ojemann@Sun.COM 		}
19887432SJohn.Ojemann@Sun.COM 		nat->nat_pnext = NULL;
19897432SJohn.Ojemann@Sun.COM 	}
19908170SJohn.Ojemann@Sun.COM 
19912393Syz155240 	if (nat->nat_fr != NULL)
19923448Sdh155122 		(void)fr_derefrule(&nat->nat_fr, ifs);
19932393Syz155240 
19942393Syz155240 	if (nat->nat_hm != NULL)
19955417Sjojemann 		fr_hostmapdel(&nat->nat_hm);
19962393Syz155240 
19972393Syz155240 	/*
19982393Syz155240 	 * If there is an active reference from the nat entry to its parent
19992393Syz155240 	 * rule, decrement the rule's reference count and free it too if no
20002393Syz155240 	 * longer being used.
20012393Syz155240 	 */
20022393Syz155240 	ipn = nat->nat_ptr;
20032393Syz155240 	if (ipn != NULL) {
20042393Syz155240 		ipn->in_space++;
20052393Syz155240 		ipn->in_use--;
20062393Syz155240 		if (ipn->in_use == 0 && (ipn->in_flags & IPN_DELETE)) {
20072393Syz155240 			if (ipn->in_apr)
20082393Syz155240 				appr_free(ipn->in_apr);
20092393Syz155240 			KFREE(ipn);
20103448Sdh155122 			ifs->ifs_nat_stats.ns_rules--;
20112393Syz155240 		}
20122393Syz155240 	}
20132393Syz155240 
20142393Syz155240 	MUTEX_DESTROY(&nat->nat_lock);
20152393Syz155240 
20163448Sdh155122 	aps_free(nat->nat_aps, ifs);
20173448Sdh155122 	ifs->ifs_nat_stats.ns_inuse--;
20182393Syz155240 
20192393Syz155240 	/*
20202393Syz155240 	 * If there's a fragment table entry too for this nat entry, then
20212393Syz155240 	 * dereference that as well.  This is after nat_lock is released
20222393Syz155240 	 * because of Tru64.
20232393Syz155240 	 */
20243448Sdh155122 	fr_forgetnat((void *)nat, ifs);
20252393Syz155240 
20262393Syz155240 	KFREE(nat);
20278170SJohn.Ojemann@Sun.COM 
20288170SJohn.Ojemann@Sun.COM 	return (0);
20292393Syz155240 }
20302393Syz155240 
20312393Syz155240 
20322393Syz155240 /* ------------------------------------------------------------------------ */
20332393Syz155240 /* Function:    nat_clearlist                                               */
20342393Syz155240 /* Returns:     int - number of NAT/RDR rules deleted                       */
20352393Syz155240 /* Parameters:  Nil                                                         */
20362393Syz155240 /*                                                                          */
20372393Syz155240 /* Delete all rules in the current list of rules.  There is nothing elegant */
20382393Syz155240 /* about this cleanup: simply free all entries on the list of rules and     */
20392393Syz155240 /* clear out the tables used for hashed NAT rule lookups.                   */
20402393Syz155240 /* ------------------------------------------------------------------------ */
nat_clearlist(ifs)20413448Sdh155122 static int nat_clearlist(ifs)
20423448Sdh155122 ipf_stack_t *ifs;
20432393Syz155240 {
20443448Sdh155122 	ipnat_t *n, **np = &ifs->ifs_nat_list;
20452393Syz155240 	int i = 0;
20462393Syz155240 
20473448Sdh155122 	if (ifs->ifs_nat_rules != NULL)
20483448Sdh155122 		bzero((char *)ifs->ifs_nat_rules,
20493448Sdh155122 		      sizeof(*ifs->ifs_nat_rules) * ifs->ifs_ipf_natrules_sz);
20503448Sdh155122 	if (ifs->ifs_rdr_rules != NULL)
20513448Sdh155122 		bzero((char *)ifs->ifs_rdr_rules,
20523448Sdh155122 		      sizeof(*ifs->ifs_rdr_rules) * ifs->ifs_ipf_rdrrules_sz);
20532393Syz155240 
20542393Syz155240 	while ((n = *np) != NULL) {
20552393Syz155240 		*np = n->in_next;
20562393Syz155240 		if (n->in_use == 0) {
20572393Syz155240 			if (n->in_apr != NULL)
20582393Syz155240 				appr_free(n->in_apr);
20592393Syz155240 			KFREE(n);
20603448Sdh155122 			ifs->ifs_nat_stats.ns_rules--;
20612393Syz155240 		} else {
20622393Syz155240 			n->in_flags |= IPN_DELETE;
20632393Syz155240 			n->in_next = NULL;
20642393Syz155240 		}
20652393Syz155240 		i++;
20662393Syz155240 	}
20673448Sdh155122 	ifs->ifs_nat_masks = 0;
20683448Sdh155122 	ifs->ifs_rdr_masks = 0;
20697176Syx160601 	for (i = 0; i < 4; i++) {
20707176Syx160601 		ifs->ifs_nat6_masks[i] = 0;
20717176Syx160601 		ifs->ifs_rdr6_masks[i] = 0;
20727176Syx160601 	}
20732393Syz155240 	return i;
20742393Syz155240 }
20752393Syz155240 
20762393Syz155240 
20772393Syz155240 /* ------------------------------------------------------------------------ */
20782393Syz155240 /* Function:    nat_newmap                                                  */
20792393Syz155240 /* Returns:     int - -1 == error, 0 == success                             */
20802393Syz155240 /* Parameters:  fin(I) - pointer to packet information                      */
20812393Syz155240 /*              nat(I) - pointer to NAT entry                               */
20822393Syz155240 /*              ni(I)  - pointer to structure with misc. information needed */
20832393Syz155240 /*                       to create new NAT entry.                           */
20842393Syz155240 /*                                                                          */
20852393Syz155240 /* Given an empty NAT structure, populate it with new information about a   */
20862393Syz155240 /* new NAT session, as defined by the matching NAT rule.                    */
20872393Syz155240 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
20882393Syz155240 /* to the new IP address for the translation.                               */
20892393Syz155240 /* ------------------------------------------------------------------------ */
nat_newmap(fin,nat,ni)20902393Syz155240 static INLINE int nat_newmap(fin, nat, ni)
20912393Syz155240 fr_info_t *fin;
20922393Syz155240 nat_t *nat;
20932393Syz155240 natinfo_t *ni;
20942393Syz155240 {
20952393Syz155240 	u_short st_port, dport, sport, port, sp, dp;
20962393Syz155240 	struct in_addr in, inb;
20972393Syz155240 	hostmap_t *hm;
20982393Syz155240 	u_32_t flags;
20992393Syz155240 	u_32_t st_ip;
21002393Syz155240 	ipnat_t *np;
21012393Syz155240 	nat_t *natl;
21022393Syz155240 	int l;
21033448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
21042393Syz155240 
21052393Syz155240 	/*
21062393Syz155240 	 * If it's an outbound packet which doesn't match any existing
21072393Syz155240 	 * record, then create a new port
21082393Syz155240 	 */
21092393Syz155240 	l = 0;
21102393Syz155240 	hm = NULL;
21112393Syz155240 	np = ni->nai_np;
21122393Syz155240 	st_ip = np->in_nip;
21132393Syz155240 	st_port = np->in_pnext;
21142393Syz155240 	flags = ni->nai_flags;
21152393Syz155240 	sport = ni->nai_sport;
21162393Syz155240 	dport = ni->nai_dport;
21172393Syz155240 
21182393Syz155240 	/*
21192393Syz155240 	 * Do a loop until we either run out of entries to try or we find
21202393Syz155240 	 * a NAT mapping that isn't currently being used.  This is done
21212393Syz155240 	 * because the change to the source is not (usually) being fixed.
21222393Syz155240 	 */
21232393Syz155240 	do {
21242393Syz155240 		port = 0;
21252393Syz155240 		in.s_addr = htonl(np->in_nip);
21262393Syz155240 		if (l == 0) {
21272393Syz155240 			/*
21282393Syz155240 			 * Check to see if there is an existing NAT
21292393Syz155240 			 * setup for this IP address pair.
21302393Syz155240 			 */
21312393Syz155240 			hm = nat_hostmap(np, fin->fin_src, fin->fin_dst,
21323448Sdh155122 					 in, 0, ifs);
21332393Syz155240 			if (hm != NULL)
21342393Syz155240 				in.s_addr = hm->hm_mapip.s_addr;
21352393Syz155240 		} else if ((l == 1) && (hm != NULL)) {
21365417Sjojemann 			fr_hostmapdel(&hm);
21372393Syz155240 		}
21382393Syz155240 		in.s_addr = ntohl(in.s_addr);
21392393Syz155240 
21402393Syz155240 		nat->nat_hm = hm;
21412393Syz155240 
21422393Syz155240 		if ((np->in_outmsk == 0xffffffff) && (np->in_pnext == 0)) {
21432393Syz155240 			if (l > 0)
21442393Syz155240 				return -1;
21452393Syz155240 		}
21462393Syz155240 
21472393Syz155240 		if (np->in_redir == NAT_BIMAP &&
21482393Syz155240 		    np->in_inmsk == np->in_outmsk) {
21492393Syz155240 			/*
21502393Syz155240 			 * map the address block in a 1:1 fashion
21512393Syz155240 			 */
21522393Syz155240 			in.s_addr = np->in_outip;
21532393Syz155240 			in.s_addr |= fin->fin_saddr & ~np->in_inmsk;
21542393Syz155240 			in.s_addr = ntohl(in.s_addr);
21552393Syz155240 
21562393Syz155240 		} else if (np->in_redir & NAT_MAPBLK) {
21572393Syz155240 			if ((l >= np->in_ppip) || ((l > 0) &&
21582393Syz155240 			     !(flags & IPN_TCPUDP)))
21592393Syz155240 				return -1;
21602393Syz155240 			/*
21612393Syz155240 			 * map-block - Calculate destination address.
21622393Syz155240 			 */
21632393Syz155240 			in.s_addr = ntohl(fin->fin_saddr);
21642393Syz155240 			in.s_addr &= ntohl(~np->in_inmsk);
21652393Syz155240 			inb.s_addr = in.s_addr;
21662393Syz155240 			in.s_addr /= np->in_ippip;
21672393Syz155240 			in.s_addr &= ntohl(~np->in_outmsk);
21682393Syz155240 			in.s_addr += ntohl(np->in_outip);
21692393Syz155240 			/*
21702393Syz155240 			 * Calculate destination port.
21712393Syz155240 			 */
21722393Syz155240 			if ((flags & IPN_TCPUDP) &&
21732393Syz155240 			    (np->in_ppip != 0)) {
21742393Syz155240 				port = ntohs(sport) + l;
21752393Syz155240 				port %= np->in_ppip;
21762393Syz155240 				port += np->in_ppip *
21772393Syz155240 					(inb.s_addr % np->in_ippip);
21782393Syz155240 				port += MAPBLK_MINPORT;
21792393Syz155240 				port = htons(port);
21802393Syz155240 			}
21812393Syz155240 
21822393Syz155240 		} else if ((np->in_outip == 0) &&
21832393Syz155240 			   (np->in_outmsk == 0xffffffff)) {
21842393Syz155240 			/*
21852393Syz155240 			 * 0/32 - use the interface's IP address.
21862393Syz155240 			 */
21872393Syz155240 			if ((l > 0) ||
21882393Syz155240 			    fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp,
21893448Sdh155122 				       &in, NULL, fin->fin_ifs) == -1)
21902393Syz155240 				return -1;
21912393Syz155240 			in.s_addr = ntohl(in.s_addr);
21922393Syz155240 
21932393Syz155240 		} else if ((np->in_outip == 0) && (np->in_outmsk == 0)) {
21942393Syz155240 			/*
21952393Syz155240 			 * 0/0 - use the original source address/port.
21962393Syz155240 			 */
21972393Syz155240 			if (l > 0)
21982393Syz155240 				return -1;
21992393Syz155240 			in.s_addr = ntohl(fin->fin_saddr);
22002393Syz155240 
22012393Syz155240 		} else if ((np->in_outmsk != 0xffffffff) &&
22022393Syz155240 			   (np->in_pnext == 0) && ((l > 0) || (hm == NULL)))
22032393Syz155240 			np->in_nip++;
22042393Syz155240 
22052393Syz155240 		natl = NULL;
22062393Syz155240 
22072393Syz155240 		if ((flags & IPN_TCPUDP) &&
22082393Syz155240 		    ((np->in_redir & NAT_MAPBLK) == 0) &&
22092393Syz155240 		    (np->in_flags & IPN_AUTOPORTMAP)) {
22102393Syz155240 			/*
22112393Syz155240 			 * "ports auto" (without map-block)
22122393Syz155240 			 */
22132393Syz155240 			if ((l > 0) && (l % np->in_ppip == 0)) {
22142393Syz155240 				if (l > np->in_space) {
22152393Syz155240 					return -1;
22162393Syz155240 				} else if ((l > np->in_ppip) &&
22172393Syz155240 					   np->in_outmsk != 0xffffffff)
22182393Syz155240 					np->in_nip++;
22192393Syz155240 			}
22202393Syz155240 			if (np->in_ppip != 0) {
22212393Syz155240 				port = ntohs(sport);
22222393Syz155240 				port += (l % np->in_ppip);
22232393Syz155240 				port %= np->in_ppip;
22242393Syz155240 				port += np->in_ppip *
22252393Syz155240 					(ntohl(fin->fin_saddr) %
22262393Syz155240 					 np->in_ippip);
22272393Syz155240 				port += MAPBLK_MINPORT;
22282393Syz155240 				port = htons(port);
22292393Syz155240 			}
22302393Syz155240 
22312393Syz155240 		} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
22322393Syz155240 			   (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) {
22332393Syz155240 			/*
22342393Syz155240 			 * Standard port translation.  Select next port.
22352393Syz155240 			 */
22367259Sdr146992 			if (np->in_flags & IPN_SEQUENTIAL) {
22377259Sdr146992 				port = np->in_pnext;
22387259Sdr146992 			} else {
22397259Sdr146992 				port = ipf_random() % (ntohs(np->in_pmax) -
22408624SDarren.Reed@Sun.COM 						       ntohs(np->in_pmin) + 1);
22417259Sdr146992 				port += ntohs(np->in_pmin);
22427259Sdr146992 			}
22437259Sdr146992 			port = htons(port);
22447259Sdr146992 			np->in_pnext++;
22452393Syz155240 
22462393Syz155240 			if (np->in_pnext > ntohs(np->in_pmax)) {
22472393Syz155240 				np->in_pnext = ntohs(np->in_pmin);
22482393Syz155240 				if (np->in_outmsk != 0xffffffff)
22492393Syz155240 					np->in_nip++;
22502393Syz155240 			}
22512393Syz155240 		}
22522393Syz155240 
22532393Syz155240 		if (np->in_flags & IPN_IPRANGE) {
22542393Syz155240 			if (np->in_nip > ntohl(np->in_outmsk))
22552393Syz155240 				np->in_nip = ntohl(np->in_outip);
22562393Syz155240 		} else {
22572393Syz155240 			if ((np->in_outmsk != 0xffffffff) &&
22582393Syz155240 			    ((np->in_nip + 1) & ntohl(np->in_outmsk)) >
22592393Syz155240 			    ntohl(np->in_outip))
22602393Syz155240 				np->in_nip = ntohl(np->in_outip) + 1;
22612393Syz155240 		}
22622393Syz155240 
22632393Syz155240 		if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
22642393Syz155240 			port = sport;
22652393Syz155240 
22662393Syz155240 		/*
22672393Syz155240 		 * Here we do a lookup of the connection as seen from
22682393Syz155240 		 * the outside.  If an IP# pair already exists, try
22692393Syz155240 		 * again.  So if you have A->B becomes C->B, you can
22702393Syz155240 		 * also have D->E become C->E but not D->B causing
22712393Syz155240 		 * another C->B.  Also take protocol and ports into
22722393Syz155240 		 * account when determining whether a pre-existing
22732393Syz155240 		 * NAT setup will cause an external conflict where
22742393Syz155240 		 * this is appropriate.
22752393Syz155240 		 */
22762393Syz155240 		inb.s_addr = htonl(in.s_addr);
22772393Syz155240 		sp = fin->fin_data[0];
22782393Syz155240 		dp = fin->fin_data[1];
22792393Syz155240 		fin->fin_data[0] = fin->fin_data[1];
22802393Syz155240 		fin->fin_data[1] = htons(port);
22812393Syz155240 		natl = nat_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
22822393Syz155240 				    (u_int)fin->fin_p, fin->fin_dst, inb);
22832393Syz155240 		fin->fin_data[0] = sp;
22842393Syz155240 		fin->fin_data[1] = dp;
22852393Syz155240 
22862393Syz155240 		/*
22872393Syz155240 		 * Has the search wrapped around and come back to the
22882393Syz155240 		 * start ?
22892393Syz155240 		 */
22902393Syz155240 		if ((natl != NULL) &&
22912393Syz155240 		    (np->in_pnext != 0) && (st_port == np->in_pnext) &&
22922393Syz155240 		    (np->in_nip != 0) && (st_ip == np->in_nip))
22932393Syz155240 			return -1;
22942393Syz155240 		l++;
22952393Syz155240 	} while (natl != NULL);
22962393Syz155240 
22972393Syz155240 	if (np->in_space > 0)
22982393Syz155240 		np->in_space--;
22992393Syz155240 
23002393Syz155240 	/* Setup the NAT table */
23012393Syz155240 	nat->nat_inip = fin->fin_src;
23022393Syz155240 	nat->nat_outip.s_addr = htonl(in.s_addr);
23032393Syz155240 	nat->nat_oip = fin->fin_dst;
23042393Syz155240 	if (nat->nat_hm == NULL)
23052393Syz155240 		nat->nat_hm = nat_hostmap(np, fin->fin_src, fin->fin_dst,
23063448Sdh155122 					  nat->nat_outip, 0, ifs);
23072393Syz155240 
23082393Syz155240 	if (flags & IPN_TCPUDP) {
23092393Syz155240 		nat->nat_inport = sport;
23102393Syz155240 		nat->nat_outport = port;	/* sport */
23112393Syz155240 		nat->nat_oport = dport;
23122393Syz155240 		((tcphdr_t *)fin->fin_dp)->th_sport = port;
23132393Syz155240 	} else if (flags & IPN_ICMPQUERY) {
23142393Syz155240 		((icmphdr_t *)fin->fin_dp)->icmp_id = port;
23152393Syz155240 		nat->nat_inport = port;
23162393Syz155240 		nat->nat_outport = port;
23172393Syz155240 	}
23182393Syz155240 
23192393Syz155240 	ni->nai_ip.s_addr = in.s_addr;
23202393Syz155240 	ni->nai_port = port;
23212393Syz155240 	ni->nai_nport = dport;
23222393Syz155240 	return 0;
23232393Syz155240 }
23242393Syz155240 
23252393Syz155240 
23262393Syz155240 /* ------------------------------------------------------------------------ */
23272393Syz155240 /* Function:    nat_newrdr                                                  */
23282393Syz155240 /* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
23292393Syz155240 /*                    allow rule to be moved if IPN_ROUNDR is set.          */
23302393Syz155240 /* Parameters:  fin(I) - pointer to packet information                      */
23312393Syz155240 /*              nat(I) - pointer to NAT entry                               */
23322393Syz155240 /*              ni(I)  - pointer to structure with misc. information needed */
23332393Syz155240 /*                       to create new NAT entry.                           */
23342393Syz155240 /*                                                                          */
23352393Syz155240 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
23362393Syz155240 /* to the new IP address for the translation.                               */
23372393Syz155240 /* ------------------------------------------------------------------------ */
nat_newrdr(fin,nat,ni)23382393Syz155240 static INLINE int nat_newrdr(fin, nat, ni)
23392393Syz155240 fr_info_t *fin;
23402393Syz155240 nat_t *nat;
23412393Syz155240 natinfo_t *ni;
23422393Syz155240 {
23432393Syz155240 	u_short nport, dport, sport;
23445828Sjojemann 	struct in_addr in, inb;
23455828Sjojemann 	u_short sp, dp;
23462393Syz155240 	hostmap_t *hm;
23472393Syz155240 	u_32_t flags;
23482393Syz155240 	ipnat_t *np;
23495828Sjojemann 	nat_t *natl;
23502393Syz155240 	int move;
23513448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
23522393Syz155240 
23532393Syz155240 	move = 1;
23542393Syz155240 	hm = NULL;
23552393Syz155240 	in.s_addr = 0;
23562393Syz155240 	np = ni->nai_np;
23572393Syz155240 	flags = ni->nai_flags;
23582393Syz155240 	sport = ni->nai_sport;
23592393Syz155240 	dport = ni->nai_dport;
23602393Syz155240 
23612393Syz155240 	/*
23622393Syz155240 	 * If the matching rule has IPN_STICKY set, then we want to have the
23632393Syz155240 	 * same rule kick in as before.  Why would this happen?  If you have
23642393Syz155240 	 * a collection of rdr rules with "round-robin sticky", the current
23652393Syz155240 	 * packet might match a different one to the previous connection but
23662393Syz155240 	 * we want the same destination to be used.
23672393Syz155240 	 */
23682393Syz155240 	if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) ==
23692393Syz155240 	    (IPN_ROUNDR|IPN_STICKY)) {
23702393Syz155240 		hm = nat_hostmap(NULL, fin->fin_src, fin->fin_dst, in,
23713448Sdh155122 				 (u_32_t)dport, ifs);
23722393Syz155240 		if (hm != NULL) {
23732393Syz155240 			in.s_addr = ntohl(hm->hm_mapip.s_addr);
23742393Syz155240 			np = hm->hm_ipnat;
23752393Syz155240 			ni->nai_np = np;
23762393Syz155240 			move = 0;
23772393Syz155240 		}
23782393Syz155240 	}
23792393Syz155240 
23802393Syz155240 	/*
23812393Syz155240 	 * Otherwise, it's an inbound packet. Most likely, we don't
23822393Syz155240 	 * want to rewrite source ports and source addresses. Instead,
23832393Syz155240 	 * we want to rewrite to a fixed internal address and fixed
23842393Syz155240 	 * internal port.
23852393Syz155240 	 */
23862393Syz155240 	if (np->in_flags & IPN_SPLIT) {
23872393Syz155240 		in.s_addr = np->in_nip;
23882393Syz155240 
23892393Syz155240 		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
23902393Syz155240 			hm = nat_hostmap(np, fin->fin_src, fin->fin_dst,
23913448Sdh155122 					 in, (u_32_t)dport, ifs);
23922393Syz155240 			if (hm != NULL) {
23932393Syz155240 				in.s_addr = hm->hm_mapip.s_addr;
23942393Syz155240 				move = 0;
23952393Syz155240 			}
23962393Syz155240 		}
23972393Syz155240 
23982393Syz155240 		if (hm == NULL || hm->hm_ref == 1) {
23992393Syz155240 			if (np->in_inip == htonl(in.s_addr)) {
24002393Syz155240 				np->in_nip = ntohl(np->in_inmsk);
24012393Syz155240 				move = 0;
24022393Syz155240 			} else {
24032393Syz155240 				np->in_nip = ntohl(np->in_inip);
24042393Syz155240 			}
24052393Syz155240 		}
24062393Syz155240 
24072393Syz155240 	} else if ((np->in_inip == 0) && (np->in_inmsk == 0xffffffff)) {
24082393Syz155240 		/*
24092393Syz155240 		 * 0/32 - use the interface's IP address.
24102393Syz155240 		 */
24113448Sdh155122 		if (fr_ifpaddr(4, FRI_NORMAL, fin->fin_ifp, &in, NULL,
24123448Sdh155122 			   fin->fin_ifs) == -1)
24132393Syz155240 			return -1;
24142393Syz155240 		in.s_addr = ntohl(in.s_addr);
24152393Syz155240 
24162393Syz155240 	} else if ((np->in_inip == 0) && (np->in_inmsk== 0)) {
24172393Syz155240 		/*
24182393Syz155240 		 * 0/0 - use the original destination address/port.
24192393Syz155240 		 */
24202393Syz155240 		in.s_addr = ntohl(fin->fin_daddr);
24212393Syz155240 
24222393Syz155240 	} else if (np->in_redir == NAT_BIMAP &&
24232393Syz155240 		   np->in_inmsk == np->in_outmsk) {
24242393Syz155240 		/*
24252393Syz155240 		 * map the address block in a 1:1 fashion
24262393Syz155240 		 */
24272393Syz155240 		in.s_addr = np->in_inip;
24282393Syz155240 		in.s_addr |= fin->fin_daddr & ~np->in_inmsk;
24292393Syz155240 		in.s_addr = ntohl(in.s_addr);
24302393Syz155240 	} else {
24312393Syz155240 		in.s_addr = ntohl(np->in_inip);
24322393Syz155240 	}
24332393Syz155240 
24342393Syz155240 	if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
24352393Syz155240 		nport = dport;
24362393Syz155240 	else {
24372393Syz155240 		/*
24382393Syz155240 		 * Whilst not optimized for the case where
24392393Syz155240 		 * pmin == pmax, the gain is not significant.
24402393Syz155240 		 */
24412393Syz155240 		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
24422393Syz155240 		    (np->in_pmin != np->in_pmax)) {
24432393Syz155240 			nport = ntohs(dport) - ntohs(np->in_pmin) +
24442393Syz155240 				ntohs(np->in_pnext);
24452393Syz155240 			nport = htons(nport);
24462393Syz155240 		} else
24472393Syz155240 			nport = np->in_pnext;
24482393Syz155240 	}
24492393Syz155240 
24502393Syz155240 	/*
24512393Syz155240 	 * When the redirect-to address is set to 0.0.0.0, just
24522393Syz155240 	 * assume a blank `forwarding' of the packet.  We don't
24532393Syz155240 	 * setup any translation for this either.
24542393Syz155240 	 */
24552393Syz155240 	if (in.s_addr == 0) {
24562393Syz155240 		if (nport == dport)
24572393Syz155240 			return -1;
24582393Syz155240 		in.s_addr = ntohl(fin->fin_daddr);
24592393Syz155240 	}
24602393Syz155240 
24615828Sjojemann 	/*
24625828Sjojemann 	 * Check to see if this redirect mapping already exists and if
24635828Sjojemann 	 * it does, return "failure" (allowing it to be created will just
24645828Sjojemann 	 * cause one or both of these "connections" to stop working.)
24655828Sjojemann 	 */
24665828Sjojemann 	inb.s_addr = htonl(in.s_addr);
24675828Sjojemann 	sp = fin->fin_data[0];
24685828Sjojemann 	dp = fin->fin_data[1];
24695828Sjojemann 	fin->fin_data[1] = fin->fin_data[0];
24705828Sjojemann 	fin->fin_data[0] = ntohs(nport);
24715828Sjojemann 	natl = nat_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
24725828Sjojemann 		    (u_int)fin->fin_p, inb, fin->fin_src);
24735828Sjojemann 	fin->fin_data[0] = sp;
24745828Sjojemann 	fin->fin_data[1] = dp;
24755828Sjojemann 	if (natl != NULL)
24765828Sjojemann 		return (-1);
24775828Sjojemann 
24782393Syz155240 	nat->nat_inip.s_addr = htonl(in.s_addr);
24792393Syz155240 	nat->nat_outip = fin->fin_dst;
24802393Syz155240 	nat->nat_oip = fin->fin_src;
24812393Syz155240 
24822393Syz155240 	ni->nai_ip.s_addr = in.s_addr;
24832393Syz155240 	ni->nai_nport = nport;
24842393Syz155240 	ni->nai_port = sport;
24852393Syz155240 
24862393Syz155240 	if (flags & IPN_TCPUDP) {
24872393Syz155240 		nat->nat_inport = nport;
24882393Syz155240 		nat->nat_outport = dport;
24892393Syz155240 		nat->nat_oport = sport;
24902393Syz155240 		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
24912393Syz155240 	} else if (flags & IPN_ICMPQUERY) {
24922393Syz155240 		((icmphdr_t *)fin->fin_dp)->icmp_id = nport;
24932393Syz155240 		nat->nat_inport = nport;
24942393Syz155240 		nat->nat_outport = nport;
24952393Syz155240 	}
24962393Syz155240 
24972393Syz155240 	return move;
24982393Syz155240 }
24992393Syz155240 
25002393Syz155240 /* ------------------------------------------------------------------------ */
25012393Syz155240 /* Function:    nat_new                                                     */
25022393Syz155240 /* Returns:     nat_t* - NULL == failure to create new NAT structure,       */
25032393Syz155240 /*                       else pointer to new NAT structure                  */
25042393Syz155240 /* Parameters:  fin(I)       - pointer to packet information                */
25052393Syz155240 /*              np(I)        - pointer to NAT rule                          */
25062393Syz155240 /*              natsave(I)   - pointer to where to store NAT struct pointer */
25072393Syz155240 /*              flags(I)     - flags describing the current packet          */
25082393Syz155240 /*              direction(I) - direction of packet (in/out)                 */
25092393Syz155240 /* Write Lock:  ipf_nat                                                     */
25102393Syz155240 /*                                                                          */
25112393Syz155240 /* Attempts to create a new NAT entry.  Does not actually change the packet */
25122393Syz155240 /* in any way.                                                              */
25132393Syz155240 /*                                                                          */
25142393Syz155240 /* This fucntion is in three main parts: (1) deal with creating a new NAT   */
25152393Syz155240 /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
25162393Syz155240 /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
25172393Syz155240 /* and (3) building that structure and putting it into the NAT table(s).    */
25182393Syz155240 /* ------------------------------------------------------------------------ */
nat_new(fin,np,natsave,flags,direction)25192393Syz155240 nat_t *nat_new(fin, np, natsave, flags, direction)
25202393Syz155240 fr_info_t *fin;
25212393Syz155240 ipnat_t *np;
25222393Syz155240 nat_t **natsave;
25232393Syz155240 u_int flags;
25242393Syz155240 int direction;
25252393Syz155240 {
25262393Syz155240 	tcphdr_t *tcp = NULL;
25272393Syz155240 	hostmap_t *hm = NULL;
25282393Syz155240 	nat_t *nat, *natl;
25292393Syz155240 	u_int nflags;
25302393Syz155240 	natinfo_t ni;
25312393Syz155240 	int move;
25323448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
25333448Sdh155122 
25344817San207044 	/*
25358170SJohn.Ojemann@Sun.COM 	 * Trigger automatic call to nat_flushtable() if the
25364817San207044 	 * table has reached capcity specified by hi watermark.
25374817San207044 	 */
25388170SJohn.Ojemann@Sun.COM 	if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_level_hi)
25394817San207044 		ifs->ifs_nat_doflush = 1;
25404817San207044 
25417432SJohn.Ojemann@Sun.COM 	/*
25427432SJohn.Ojemann@Sun.COM 	 * If automatic flushing did not do its job, and the table
25437432SJohn.Ojemann@Sun.COM 	 * has filled up, don't try to create a new entry.
25447432SJohn.Ojemann@Sun.COM 	 */
25453448Sdh155122 	if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) {
25463448Sdh155122 		ifs->ifs_nat_stats.ns_memfail++;
25472393Syz155240 		return NULL;
25482393Syz155240 	}
25492393Syz155240 
25502393Syz155240 	move = 1;
25512393Syz155240 	nflags = np->in_flags & flags;
25522393Syz155240 	nflags &= NAT_FROMRULE;
25532393Syz155240 
25542393Syz155240 	ni.nai_np = np;
25552393Syz155240 	ni.nai_nflags = nflags;
25562393Syz155240 	ni.nai_flags = flags;
25572393Syz155240 
25582393Syz155240 	/* Give me a new nat */
25592393Syz155240 	KMALLOC(nat, nat_t *);
25602393Syz155240 	if (nat == NULL) {
25613448Sdh155122 		ifs->ifs_nat_stats.ns_memfail++;
25622393Syz155240 		/*
25632393Syz155240 		 * Try to automatically tune the max # of entries in the
25642393Syz155240 		 * table allowed to be less than what will cause kmem_alloc()
25652393Syz155240 		 * to fail and try to eliminate panics due to out of memory
25662393Syz155240 		 * conditions arising.
25672393Syz155240 		 */
25683448Sdh155122 		if (ifs->ifs_ipf_nattable_max > ifs->ifs_ipf_nattable_sz) {
25693448Sdh155122 			ifs->ifs_ipf_nattable_max = ifs->ifs_nat_stats.ns_inuse - 100;
25702393Syz155240 			printf("ipf_nattable_max reduced to %d\n",
25713448Sdh155122 				ifs->ifs_ipf_nattable_max);
25722393Syz155240 		}
25732393Syz155240 		return NULL;
25742393Syz155240 	}
25752393Syz155240 
25762393Syz155240 	if (flags & IPN_TCPUDP) {
25772393Syz155240 		tcp = fin->fin_dp;
25782393Syz155240 		ni.nai_sport = htons(fin->fin_sport);
25792393Syz155240 		ni.nai_dport = htons(fin->fin_dport);
25802393Syz155240 	} else if (flags & IPN_ICMPQUERY) {
25812393Syz155240 		/*
25822393Syz155240 		 * In the ICMP query NAT code, we translate the ICMP id fields
25832393Syz155240 		 * to make them unique. This is indepedent of the ICMP type
25842393Syz155240 		 * (e.g. in the unlikely event that a host sends an echo and
25852393Syz155240 		 * an tstamp request with the same id, both packets will have
25862393Syz155240 		 * their ip address/id field changed in the same way).
25872393Syz155240 		 */
25882393Syz155240 		/* The icmp_id field is used by the sender to identify the
25892393Syz155240 		 * process making the icmp request. (the receiver justs
25902393Syz155240 		 * copies it back in its response). So, it closely matches
25912393Syz155240 		 * the concept of source port. We overlay sport, so we can
25922393Syz155240 		 * maximally reuse the existing code.
25932393Syz155240 		 */
25942393Syz155240 		ni.nai_sport = ((icmphdr_t *)fin->fin_dp)->icmp_id;
25952393Syz155240 		ni.nai_dport = ni.nai_sport;
25962393Syz155240 	}
25972393Syz155240 
25982393Syz155240 	bzero((char *)nat, sizeof(*nat));
25992393Syz155240 	nat->nat_flags = flags;
26003448Sdh155122 	nat->nat_redir = np->in_redir;
26012393Syz155240 
26022393Syz155240 	if ((flags & NAT_SLAVE) == 0) {
26033448Sdh155122 		MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
26042393Syz155240 	}
26052393Syz155240 
26062393Syz155240 	/*
26072393Syz155240 	 * Search the current table for a match.
26082393Syz155240 	 */
26092393Syz155240 	if (direction == NAT_OUTBOUND) {
26102393Syz155240 		/*
26112393Syz155240 		 * We can now arrange to call this for the same connection
26122393Syz155240 		 * because ipf_nat_new doesn't protect the code path into
26132393Syz155240 		 * this function.
26142393Syz155240 		 */
26152393Syz155240 		natl = nat_outlookup(fin, nflags, (u_int)fin->fin_p,
26162393Syz155240 				     fin->fin_src, fin->fin_dst);
26172393Syz155240 		if (natl != NULL) {
26184380Sjojemann 			KFREE(nat);
26192393Syz155240 			nat = natl;
26202393Syz155240 			goto done;
26212393Syz155240 		}
26222393Syz155240 
26232393Syz155240 		move = nat_newmap(fin, nat, &ni);
26242393Syz155240 		if (move == -1)
26252393Syz155240 			goto badnat;
26262393Syz155240 
26272393Syz155240 		np = ni.nai_np;
26282393Syz155240 	} else {
26292393Syz155240 		/*
26302393Syz155240 		 * NAT_INBOUND is used only for redirects rules
26312393Syz155240 		 */
26322393Syz155240 		natl = nat_inlookup(fin, nflags, (u_int)fin->fin_p,
26332393Syz155240 				    fin->fin_src, fin->fin_dst);
26342393Syz155240 		if (natl != NULL) {
26354380Sjojemann 			KFREE(nat);
26362393Syz155240 			nat = natl;
26372393Syz155240 			goto done;
26382393Syz155240 		}
26392393Syz155240 
26402393Syz155240 		move = nat_newrdr(fin, nat, &ni);
26412393Syz155240 		if (move == -1)
26422393Syz155240 			goto badnat;
26432393Syz155240 
26442393Syz155240 		np = ni.nai_np;
26452393Syz155240 	}
26462393Syz155240 
26472393Syz155240 	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
26482393Syz155240 		if (np->in_redir == NAT_REDIRECT) {
26492393Syz155240 			nat_delrdr(np);
26503448Sdh155122 			nat_addrdr(np, ifs);
26512393Syz155240 		} else if (np->in_redir == NAT_MAP) {
26522393Syz155240 			nat_delnat(np);
26533448Sdh155122 			nat_addnat(np, ifs);
26542393Syz155240 		}
26552393Syz155240 	}
26562393Syz155240 
26572393Syz155240 	if (nat_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) {
26582393Syz155240 		goto badnat;
26592393Syz155240 	}
26606253San207044 
26616253San207044 	nat_calc_chksum_diffs(nat);
26626253San207044 
26632393Syz155240 	if (flags & SI_WILDP)
26643448Sdh155122 		ifs->ifs_nat_stats.ns_wilds++;
26657432SJohn.Ojemann@Sun.COM 	fin->fin_flx |= FI_NEWNAT;
26662393Syz155240 	goto done;
26672393Syz155240 badnat:
26683448Sdh155122 	ifs->ifs_nat_stats.ns_badnat++;
26692393Syz155240 	if ((hm = nat->nat_hm) != NULL)
26705417Sjojemann 		fr_hostmapdel(&hm);
26712393Syz155240 	KFREE(nat);
26722393Syz155240 	nat = NULL;
26732393Syz155240 done:
26742393Syz155240 	if ((flags & NAT_SLAVE) == 0) {
26753448Sdh155122 		MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
26762393Syz155240 	}
26772393Syz155240 	return nat;
26782393Syz155240 }
26792393Syz155240 
26802393Syz155240 
26812393Syz155240 /* ------------------------------------------------------------------------ */
26822393Syz155240 /* Function:    nat_finalise                                                */
26832393Syz155240 /* Returns:     int - 0 == sucess, -1 == failure                            */
26842393Syz155240 /* Parameters:  fin(I) - pointer to packet information                      */
26852393Syz155240 /*              nat(I) - pointer to NAT entry                               */
26862393Syz155240 /*              ni(I)  - pointer to structure with misc. information needed */
26872393Syz155240 /*                       to create new NAT entry.                           */
26882393Syz155240 /* Write Lock:  ipf_nat                                                     */
26892393Syz155240 /*                                                                          */
26902393Syz155240 /* This is the tail end of constructing a new NAT entry and is the same     */
26912393Syz155240 /* for both IPv4 and IPv6.                                                  */
26922393Syz155240 /* ------------------------------------------------------------------------ */
26932393Syz155240 /*ARGSUSED*/
nat_finalise(fin,nat,ni,tcp,natsave,direction)26942393Syz155240 static INLINE int nat_finalise(fin, nat, ni, tcp, natsave, direction)
26952393Syz155240 fr_info_t *fin;
26962393Syz155240 nat_t *nat;
26972393Syz155240 natinfo_t *ni;
26982393Syz155240 tcphdr_t *tcp;
26992393Syz155240 nat_t **natsave;
27002393Syz155240 int direction;
27012393Syz155240 {
27022393Syz155240 	frentry_t *fr;
27032393Syz155240 	ipnat_t *np;
27043448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
27052393Syz155240 
27062393Syz155240 	np = ni->nai_np;
27072393Syz155240 
27082958Sdr146992 	COPYIFNAME(fin->fin_ifp, nat->nat_ifnames[0], fin->fin_v);
27092958Sdr146992 
27102393Syz155240 #ifdef	IPFILTER_SYNC
27112393Syz155240 	if ((nat->nat_flags & SI_CLONE) == 0)
27122393Syz155240 		nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat);
27132393Syz155240 #endif
27142393Syz155240 
27152393Syz155240 	nat->nat_me = natsave;
27162393Syz155240 	nat->nat_dir = direction;
27172508Syz155240 	nat->nat_ifps[0] = np->in_ifps[0];
27182508Syz155240 	nat->nat_ifps[1] = np->in_ifps[1];
27192393Syz155240 	nat->nat_ptr = np;
27202393Syz155240 	nat->nat_p = fin->fin_p;
27217176Syx160601 	nat->nat_v = fin->fin_v;
27222393Syz155240 	nat->nat_mssclamp = np->in_mssclamp;
27232393Syz155240 	fr = fin->fin_fr;
27242393Syz155240 	nat->nat_fr = fr;
27252393Syz155240 
27262393Syz155240 	if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0))
27272393Syz155240 		if (appr_new(fin, nat) == -1)
27282393Syz155240 			return -1;
27292393Syz155240 
27303448Sdh155122 	if (nat_insert(nat, fin->fin_rev, ifs) == 0) {
27313448Sdh155122 		if (ifs->ifs_nat_logging)
27323448Sdh155122 			nat_log(nat, (u_int)np->in_redir, ifs);
27332393Syz155240 		np->in_use++;
27342393Syz155240 		if (fr != NULL) {
27352393Syz155240 			MUTEX_ENTER(&fr->fr_lock);
27362393Syz155240 			fr->fr_ref++;
27372393Syz155240 			MUTEX_EXIT(&fr->fr_lock);
27382393Syz155240 		}
27392393Syz155240 		return 0;
27402393Syz155240 	}
27412393Syz155240 
27422393Syz155240 	/*
27432393Syz155240 	 * nat_insert failed, so cleanup time...
27442393Syz155240 	 */
27452393Syz155240 	return -1;
27462393Syz155240 }
27472393Syz155240 
27482393Syz155240 
27492393Syz155240 /* ------------------------------------------------------------------------ */
27502393Syz155240 /* Function:   nat_insert                                                   */
27512393Syz155240 /* Returns:    int - 0 == sucess, -1 == failure                             */
27522393Syz155240 /* Parameters: nat(I) - pointer to NAT structure                            */
27532393Syz155240 /*             rev(I) - flag indicating forward/reverse direction of packet */
27542393Syz155240 /* Write Lock: ipf_nat                                                      */
27552393Syz155240 /*                                                                          */
27562393Syz155240 /* Insert a NAT entry into the hash tables for searching and add it to the  */
27572393Syz155240 /* list of active NAT entries.  Adjust global counters when complete.       */
27582393Syz155240 /* ------------------------------------------------------------------------ */
nat_insert(nat,rev,ifs)27593448Sdh155122 int	nat_insert(nat, rev, ifs)
27602393Syz155240 nat_t	*nat;
27612393Syz155240 int	rev;
27623448Sdh155122 ipf_stack_t *ifs;
27632393Syz155240 {
27642393Syz155240 	u_int hv1, hv2;
27652393Syz155240 	nat_t **natp;
27662393Syz155240 
27672393Syz155240 	/*
27682393Syz155240 	 * Try and return an error as early as possible, so calculate the hash
27692393Syz155240 	 * entry numbers first and then proceed.
27702393Syz155240 	 */
27712393Syz155240 	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
27722393Syz155240 		hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport,
27732393Syz155240 				  0xffffffff);
27742393Syz155240 		hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1 + nat->nat_oport,
27753448Sdh155122 				  ifs->ifs_ipf_nattable_sz);
27762393Syz155240 		hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport,
27772393Syz155240 				  0xffffffff);
27782393Syz155240 		hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2 + nat->nat_oport,
27793448Sdh155122 				  ifs->ifs_ipf_nattable_sz);
27802393Syz155240 	} else {
27812393Syz155240 		hv1 = NAT_HASH_FN(nat->nat_inip.s_addr, 0, 0xffffffff);
27823448Sdh155122 		hv1 = NAT_HASH_FN(nat->nat_oip.s_addr, hv1,
27833448Sdh155122 				  ifs->ifs_ipf_nattable_sz);
27842393Syz155240 		hv2 = NAT_HASH_FN(nat->nat_outip.s_addr, 0, 0xffffffff);
27853448Sdh155122 		hv2 = NAT_HASH_FN(nat->nat_oip.s_addr, hv2,
27863448Sdh155122 				  ifs->ifs_ipf_nattable_sz);
27872393Syz155240 	}
27882393Syz155240 
27893448Sdh155122 	if (ifs->ifs_nat_stats.ns_bucketlen[0][hv1] >= ifs->ifs_fr_nat_maxbucket ||
27903448Sdh155122 	    ifs->ifs_nat_stats.ns_bucketlen[1][hv2] >= ifs->ifs_fr_nat_maxbucket) {
27912393Syz155240 		return -1;
27922393Syz155240 	}
27932393Syz155240 
27942393Syz155240 	nat->nat_hv[0] = hv1;
27952393Syz155240 	nat->nat_hv[1] = hv2;
27962393Syz155240 
27972393Syz155240 	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
27982393Syz155240 
27992393Syz155240 	nat->nat_rev = rev;
28002393Syz155240 	nat->nat_ref = 1;
28012393Syz155240 	nat->nat_bytes[0] = 0;
28022393Syz155240 	nat->nat_pkts[0] = 0;
28032393Syz155240 	nat->nat_bytes[1] = 0;
28042393Syz155240 	nat->nat_pkts[1] = 0;
28052393Syz155240 
28062393Syz155240 	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
28073448Sdh155122 	nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 4, ifs);
28082393Syz155240 
28092393Syz155240 	if (nat->nat_ifnames[1][0] !='\0') {
28102393Syz155240 		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
28113448Sdh155122 		nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 4, ifs);
28122393Syz155240 	} else {
28132393Syz155240 		(void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0],
28142393Syz155240 			       LIFNAMSIZ);
28152393Syz155240 		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
28162393Syz155240 		nat->nat_ifps[1] = nat->nat_ifps[0];
28172393Syz155240 	}
28182393Syz155240 
28193448Sdh155122 	nat->nat_next = ifs->ifs_nat_instances;
28203448Sdh155122 	nat->nat_pnext = &ifs->ifs_nat_instances;
28213448Sdh155122 	if (ifs->ifs_nat_instances)
28223448Sdh155122 		ifs->ifs_nat_instances->nat_pnext = &nat->nat_next;
28233448Sdh155122 	ifs->ifs_nat_instances = nat;
28243448Sdh155122 
28253448Sdh155122 	natp = &ifs->ifs_nat_table[0][hv1];
28262393Syz155240 	if (*natp)
28272393Syz155240 		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
28282393Syz155240 	nat->nat_phnext[0] = natp;
28292393Syz155240 	nat->nat_hnext[0] = *natp;
28302393Syz155240 	*natp = nat;
28313448Sdh155122 	ifs->ifs_nat_stats.ns_bucketlen[0][hv1]++;
28323448Sdh155122 
28333448Sdh155122 	natp = &ifs->ifs_nat_table[1][hv2];
28342393Syz155240 	if (*natp)
28352393Syz155240 		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
28362393Syz155240 	nat->nat_phnext[1] = natp;
28372393Syz155240 	nat->nat_hnext[1] = *natp;
28382393Syz155240 	*natp = nat;
28393448Sdh155122 	ifs->ifs_nat_stats.ns_bucketlen[1][hv2]++;
28403448Sdh155122 
28413448Sdh155122 	fr_setnatqueue(nat, rev, ifs);
28423448Sdh155122 
28433448Sdh155122 	ifs->ifs_nat_stats.ns_added++;
28443448Sdh155122 	ifs->ifs_nat_stats.ns_inuse++;
28452393Syz155240 	return 0;
28462393Syz155240 }
28472393Syz155240 
28482393Syz155240 
28492393Syz155240 /* ------------------------------------------------------------------------ */
28502393Syz155240 /* Function:    nat_icmperrorlookup                                         */
28512393Syz155240 /* Returns:     nat_t* - point to matching NAT structure                    */
28522393Syz155240 /* Parameters:  fin(I) - pointer to packet information                      */
28532393Syz155240 /*              dir(I) - direction of packet (in/out)                       */
28542393Syz155240 /*                                                                          */
28552393Syz155240 /* Check if the ICMP error message is related to an existing TCP, UDP or    */
28562393Syz155240 /* ICMP query nat entry.  It is assumed that the packet is already of the   */
28572393Syz155240 /* the required length.                                                     */
28582393Syz155240 /* ------------------------------------------------------------------------ */
nat_icmperrorlookup(fin,dir)28592393Syz155240 nat_t *nat_icmperrorlookup(fin, dir)
28602393Syz155240 fr_info_t *fin;
28612393Syz155240 int dir;
28622393Syz155240 {
28632393Syz155240 	int flags = 0, minlen;
28642393Syz155240 	icmphdr_t *orgicmp;
28652393Syz155240 	tcphdr_t *tcp = NULL;
28662393Syz155240 	u_short data[2];
28672393Syz155240 	nat_t *nat;
28682393Syz155240 	ip_t *oip;
28692393Syz155240 	u_int p;
28702393Syz155240 
28712393Syz155240 	/*
28722393Syz155240 	 * Does it at least have the return (basic) IP header ?
28732393Syz155240 	 * Only a basic IP header (no options) should be with an ICMP error
28742393Syz155240 	 * header.  Also, if it's not an error type, then return.
28752393Syz155240 	 */
28762393Syz155240 	if ((fin->fin_hlen != sizeof(ip_t)) || !(fin->fin_flx & FI_ICMPERR))
28772393Syz155240 		return NULL;
28782393Syz155240 
28792393Syz155240 	/*
28802393Syz155240 	 * Check packet size
28812393Syz155240 	 */
28822393Syz155240 	oip = (ip_t *)((char *)fin->fin_dp + 8);
28832393Syz155240 	minlen = IP_HL(oip) << 2;
28842393Syz155240 	if ((minlen < sizeof(ip_t)) ||
28852393Syz155240 	    (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen))
28862393Syz155240 		return NULL;
28872393Syz155240 	/*
28882393Syz155240 	 * Is the buffer big enough for all of it ?  It's the size of the IP
28892393Syz155240 	 * header claimed in the encapsulated part which is of concern.  It
28902393Syz155240 	 * may be too big to be in this buffer but not so big that it's
28912393Syz155240 	 * outside the ICMP packet, leading to TCP deref's causing problems.
28922393Syz155240 	 * This is possible because we don't know how big oip_hl is when we
28932393Syz155240 	 * do the pullup early in fr_check() and thus can't gaurantee it is
28942393Syz155240 	 * all here now.
28952393Syz155240 	 */
28962393Syz155240 #ifdef  _KERNEL
28972393Syz155240 	{
28982393Syz155240 	mb_t *m;
28992393Syz155240 
29002393Syz155240 	m = fin->fin_m;
29012393Syz155240 # if defined(MENTAT)
29022393Syz155240 	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr)
29032393Syz155240 		return NULL;
29042393Syz155240 # else
29052393Syz155240 	if ((char *)oip + fin->fin_dlen - ICMPERR_ICMPHLEN >
29062393Syz155240 	    (char *)fin->fin_ip + M_LEN(m))
29072393Syz155240 		return NULL;
29082393Syz155240 # endif
29092393Syz155240 	}
29102393Syz155240 #endif
29112393Syz155240 
29122393Syz155240 	if (fin->fin_daddr != oip->ip_src.s_addr)
29132393Syz155240 		return NULL;
29142393Syz155240 
29152393Syz155240 	p = oip->ip_p;
29162393Syz155240 	if (p == IPPROTO_TCP)
29172393Syz155240 		flags = IPN_TCP;
29182393Syz155240 	else if (p == IPPROTO_UDP)
29192393Syz155240 		flags = IPN_UDP;
29202393Syz155240 	else if (p == IPPROTO_ICMP) {
29212393Syz155240 		orgicmp = (icmphdr_t *)((char *)oip + (IP_HL(oip) << 2));
29222393Syz155240 
29232393Syz155240 		/* see if this is related to an ICMP query */
29242393Syz155240 		if (nat_icmpquerytype4(orgicmp->icmp_type)) {
29252393Syz155240 			data[0] = fin->fin_data[0];
29262393Syz155240 			data[1] = fin->fin_data[1];
29272393Syz155240 			fin->fin_data[0] = 0;
29282393Syz155240 			fin->fin_data[1] = orgicmp->icmp_id;
29292393Syz155240 
29302393Syz155240 			flags = IPN_ICMPERR|IPN_ICMPQUERY;
29312393Syz155240 			/*
29322393Syz155240 			 * NOTE : dir refers to the direction of the original
29332393Syz155240 			 *        ip packet. By definition the icmp error
29342393Syz155240 			 *        message flows in the opposite direction.
29352393Syz155240 			 */
29362393Syz155240 			if (dir == NAT_INBOUND)
29372393Syz155240 				nat = nat_inlookup(fin, flags, p, oip->ip_dst,
29382393Syz155240 						   oip->ip_src);
29392393Syz155240 			else
29402393Syz155240 				nat = nat_outlookup(fin, flags, p, oip->ip_dst,
29412393Syz155240 						    oip->ip_src);
29422393Syz155240 			fin->fin_data[0] = data[0];
29432393Syz155240 			fin->fin_data[1] = data[1];
29442393Syz155240 			return nat;
29452393Syz155240 		}
29462393Syz155240 	}
29472393Syz155240 
29482393Syz155240 	if (flags & IPN_TCPUDP) {
29492393Syz155240 		minlen += 8;		/* + 64bits of data to get ports */
29502393Syz155240 		if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen)
29512393Syz155240 			return NULL;
29522393Syz155240 
29532393Syz155240 		data[0] = fin->fin_data[0];
29542393Syz155240 		data[1] = fin->fin_data[1];
29552393Syz155240 		tcp = (tcphdr_t *)((char *)oip + (IP_HL(oip) << 2));
29562393Syz155240 		fin->fin_data[0] = ntohs(tcp->th_dport);
29572393Syz155240 		fin->fin_data[1] = ntohs(tcp->th_sport);
29582393Syz155240 
29592393Syz155240 		if (dir == NAT_INBOUND) {
29602393Syz155240 			nat = nat_inlookup(fin, flags, p, oip->ip_dst,
29612393Syz155240 					   oip->ip_src);
29622393Syz155240 		} else {
29632393Syz155240 			nat = nat_outlookup(fin, flags, p, oip->ip_dst,
29642393Syz155240 					    oip->ip_src);
29652393Syz155240 		}
29662393Syz155240 		fin->fin_data[0] = data[0];
29672393Syz155240 		fin->fin_data[1] = data[1];
29682393Syz155240 		return nat;
29692393Syz155240 	}
29702393Syz155240 	if (dir == NAT_INBOUND)
29712393Syz155240 		return nat_inlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
29722393Syz155240 	else
29732393Syz155240 		return nat_outlookup(fin, 0, p, oip->ip_dst, oip->ip_src);
29742393Syz155240 }
29752393Syz155240 
29762393Syz155240 
29772393Syz155240 /* ------------------------------------------------------------------------ */
29782393Syz155240 /* Function:    nat_icmperror                                               */
29792393Syz155240 /* Returns:     nat_t* - point to matching NAT structure                    */
29802393Syz155240 /* Parameters:  fin(I)    - pointer to packet information                   */
29812393Syz155240 /*              nflags(I) - NAT flags for this packet                       */
29822393Syz155240 /*              dir(I)    - direction of packet (in/out)                    */
29832393Syz155240 /*                                                                          */
29842393Syz155240 /* Fix up an ICMP packet which is an error message for an existing NAT      */
29852393Syz155240 /* session.  This will correct both packet header data and checksums.       */
29862393Syz155240 /*                                                                          */
29872393Syz155240 /* This should *ONLY* be used for incoming ICMP error packets to make sure  */
29882393Syz155240 /* a NAT'd ICMP packet gets correctly recognised.                           */
29892393Syz155240 /* ------------------------------------------------------------------------ */
nat_icmperror(fin,nflags,dir)29902393Syz155240 nat_t *nat_icmperror(fin, nflags, dir)
29912393Syz155240 fr_info_t *fin;
29922393Syz155240 u_int *nflags;
29932393Syz155240 int dir;
29942393Syz155240 {
29952693Sjojemann 	u_32_t sum1, sum2, sumd, psum1, psum2, psumd, sumd2;
29962393Syz155240 	struct in_addr in;
29972693Sjojemann 	icmphdr_t *icmp, *orgicmp;
29982693Sjojemann 	int dlen;
29992693Sjojemann 	udphdr_t *udp;
30002393Syz155240 	tcphdr_t *tcp;
30012393Syz155240 	nat_t *nat;
30022393Syz155240 	ip_t *oip;
30032393Syz155240 	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY)))
30042393Syz155240 		return NULL;
30052693Sjojemann 
30062393Syz155240 	/*
30072693Sjojemann 	 * nat_icmperrorlookup() looks up nat entry associated with the
30082693Sjojemann 	 * offending IP packet and returns pointer to the entry, or NULL
30092693Sjojemann 	 * if packet wasn't natted or for `defective' packets.
30102393Syz155240 	 */
30112693Sjojemann 
30122393Syz155240 	if ((fin->fin_v != 4) || !(nat = nat_icmperrorlookup(fin, dir)))
30132393Syz155240 		return NULL;
30142393Syz155240 
30152393Syz155240 	sumd2 = 0;
30162393Syz155240 	*nflags = IPN_ICMPERR;
30172393Syz155240 	icmp = fin->fin_dp;
30182393Syz155240 	oip = (ip_t *)&icmp->icmp_ip;
30192693Sjojemann 	udp = (udphdr_t *)((((char *)oip) + (IP_HL(oip) << 2)));
30202693Sjojemann 	tcp = (tcphdr_t *)udp;
30212693Sjojemann 	dlen = fin->fin_plen - ((char *)udp - (char *)fin->fin_ip);
30222393Syz155240 
30232393Syz155240 	/*
30242393Syz155240 	 * Need to adjust ICMP header to include the real IP#'s and
30252693Sjojemann 	 * port #'s.  There are three steps required.
30262693Sjojemann 	 *
30272393Syz155240 	 * Step 1
30282693Sjojemann 	 * Fix the IP addresses in the offending IP packet and update
30292693Sjojemann 	 * ip header checksum to compensate for the change.
30302393Syz155240 	 *
30312693Sjojemann 	 * No update needed here for icmp_cksum because the ICMP checksum
30322693Sjojemann 	 * is calculated over the complete ICMP packet, which includes the
30332693Sjojemann 	 * changed oip IP addresses and oip->ip_sum.  These two changes
30342693Sjojemann 	 * cancel each other out (if the delta for the IP address is x,
30352693Sjojemann 	 * then the delta for ip_sum is minus x).
30362393Syz155240 	 */
30372393Syz155240 
30382393Syz155240 	if (oip->ip_dst.s_addr == nat->nat_oip.s_addr) {
30392393Syz155240 		sum1 = LONG_SUM(ntohl(oip->ip_src.s_addr));
30402393Syz155240 		in = nat->nat_inip;
30412393Syz155240 		oip->ip_src = in;
30422393Syz155240 	} else {
30432393Syz155240 		sum1 = LONG_SUM(ntohl(oip->ip_dst.s_addr));
30442393Syz155240 		in = nat->nat_outip;
30452393Syz155240 		oip->ip_dst = in;
30462393Syz155240 	}
30472393Syz155240 
30482393Syz155240 	sum2 = LONG_SUM(ntohl(in.s_addr));
30492393Syz155240 	CALC_SUMD(sum1, sum2, sumd);
30502393Syz155240 	fix_datacksum(&oip->ip_sum, sumd);
30512393Syz155240 
30522393Syz155240 	/*
30532693Sjojemann 	 * Step 2
30542693Sjojemann 	 * Perform other adjustments based on protocol of offending packet.
30552393Syz155240 	 */
30562693Sjojemann 
30572693Sjojemann 	switch (oip->ip_p) {
30582693Sjojemann 		case IPPROTO_TCP :
30592693Sjojemann 		case IPPROTO_UDP :
30602393Syz155240 
30612393Syz155240 			/*
30622693Sjojemann 			* For offending TCP/UDP IP packets, translate the ports
30632693Sjojemann 			* based on the NAT specification.
30642693Sjojemann 			*
30652693Sjojemann 			* Advance notice : Now it becomes complicated :-)
30662693Sjojemann 			*
30672693Sjojemann 			* Since the port and IP addresse fields are both part
30682693Sjojemann 			* of the TCP/UDP checksum of the offending IP packet,
30692693Sjojemann 			* we need to adjust that checksum as well.
30702693Sjojemann 			*
30712693Sjojemann 			* To further complicate things, the TCP/UDP checksum
30722693Sjojemann 			* may not be present.  We must check to see if the
30732693Sjojemann 			* length of the data portion is big enough to hold
30742693Sjojemann 			* the checksum.  In the UDP case, a test to determine
30752693Sjojemann 			* if the checksum is even set is also required.
30762693Sjojemann 			*
30772693Sjojemann 			* Any changes to an IP address, port or checksum within
30782693Sjojemann 			* the ICMP packet requires a change to icmp_cksum.
30792693Sjojemann 			*
30802693Sjojemann 			* Be extremely careful here ... The change is dependent
30812693Sjojemann 			* upon whether or not the TCP/UPD checksum is present.
30822693Sjojemann 			*
30832693Sjojemann 			* If TCP/UPD checksum is present, the icmp_cksum must
30842693Sjojemann 			* compensate for checksum modification resulting from
30852693Sjojemann 			* IP address change only.  Port change and resulting
30862693Sjojemann 			* data checksum adjustments cancel each other out.
30872693Sjojemann 			*
30882693Sjojemann 			* If TCP/UDP checksum is not present, icmp_cksum must
30892693Sjojemann 			* compensate for port change only.  The IP address
30902693Sjojemann 			* change does not modify anything else in this case.
30912693Sjojemann 			*/
30922693Sjojemann 
30932693Sjojemann 			psum1 = 0;
30942693Sjojemann 			psum2 = 0;
30952693Sjojemann 			psumd = 0;
30962693Sjojemann 
30972693Sjojemann 			if ((tcp->th_dport == nat->nat_oport) &&
30982693Sjojemann 			    (tcp->th_sport != nat->nat_inport)) {
30992393Syz155240 
31002393Syz155240 				/*
31012693Sjojemann 				 * Translate the source port.
31022693Sjojemann 				 */
31032693Sjojemann 
31042693Sjojemann 				psum1 = ntohs(tcp->th_sport);
31052693Sjojemann 				psum2 = ntohs(nat->nat_inport);
31062693Sjojemann 				tcp->th_sport = nat->nat_inport;
31072693Sjojemann 
31082693Sjojemann 			} else if ((tcp->th_sport == nat->nat_oport) &&
31092693Sjojemann 				    (tcp->th_dport != nat->nat_outport)) {
31102693Sjojemann 
31112693Sjojemann 				/*
31122693Sjojemann 				 * Translate the destination port.
31132393Syz155240 				 */
31142693Sjojemann 
31152693Sjojemann 				psum1 = ntohs(tcp->th_dport);
31162693Sjojemann 				psum2 = ntohs(nat->nat_outport);
31172693Sjojemann 				tcp->th_dport = nat->nat_outport;
31182693Sjojemann 			}
31192693Sjojemann 
31202693Sjojemann 			if ((oip->ip_p == IPPROTO_TCP) && (dlen >= 18)) {
31212693Sjojemann 
31222693Sjojemann 				/*
31232693Sjojemann 				 * TCP checksum present.
31242693Sjojemann 				 *
31252693Sjojemann 				 * Adjust data checksum and icmp checksum to
31262693Sjojemann 				 * compensate for any IP address change.
31272693Sjojemann 				 */
31282693Sjojemann 
31292693Sjojemann 				sum1 = ntohs(tcp->th_sum);
31302693Sjojemann 				fix_datacksum(&tcp->th_sum, sumd);
31312693Sjojemann 				sum2 = ntohs(tcp->th_sum);
31322693Sjojemann 				sumd2 = sumd << 1;
31332393Syz155240 				CALC_SUMD(sum1, sum2, sumd);
31342393Syz155240 				sumd2 += sumd;
31352693Sjojemann 
31362693Sjojemann 				/*
31372693Sjojemann 				 * Also make data checksum adjustment to
31382693Sjojemann 				 * compensate for any port change.
31392693Sjojemann 				 */
31402693Sjojemann 
31412693Sjojemann 				if (psum1 != psum2) {
31422693Sjojemann 					CALC_SUMD(psum1, psum2, psumd);
31432693Sjojemann 					fix_datacksum(&tcp->th_sum, psumd);
31442393Syz155240 				}
31452693Sjojemann 
31462693Sjojemann 			} else if ((oip->ip_p == IPPROTO_UDP) &&
31472693Sjojemann 				   (dlen >= 8) && (udp->uh_sum != 0)) {
31482693Sjojemann 
31492693Sjojemann 				/*
31502693Sjojemann 				 * The UDP checksum is present and set.
31512693Sjojemann 				 *
31522693Sjojemann 				 * Adjust data checksum and icmp checksum to
31532693Sjojemann 				 * compensate for any IP address change.
31542693Sjojemann 				 */
31552693Sjojemann 
31562693Sjojemann 				sum1 = ntohs(udp->uh_sum);
31572693Sjojemann 				fix_datacksum(&udp->uh_sum, sumd);
31582693Sjojemann 				sum2 = ntohs(udp->uh_sum);
31592693Sjojemann 				sumd2 = sumd << 1;
31602693Sjojemann 				CALC_SUMD(sum1, sum2, sumd);
31612393Syz155240 				sumd2 += sumd;
31622393Syz155240 
31632393Syz155240 				/*
31642693Sjojemann 				 * Also make data checksum adjustment to
31652693Sjojemann 				 * compensate for any port change.
31662393Syz155240 				 */
31672693Sjojemann 
31682693Sjojemann 				if (psum1 != psum2) {
31692693Sjojemann 					CALC_SUMD(psum1, psum2, psumd);
31702693Sjojemann 					fix_datacksum(&udp->uh_sum, psumd);
31712693Sjojemann 				}
31722693Sjojemann 
31732693Sjojemann 			} else {
31742693Sjojemann 
31752693Sjojemann 				/*
31762693Sjojemann 				 * Data checksum was not present.
31772693Sjojemann 				 *
31782693Sjojemann 				 * Compensate for any port change.
31792693Sjojemann 				 */
31802693Sjojemann 
31812693Sjojemann 				CALC_SUMD(psum2, psum1, psumd);
31822693Sjojemann 				sumd2 += psumd;
31832393Syz155240 			}
31842693Sjojemann 			break;
31852693Sjojemann 
31862693Sjojemann 		case IPPROTO_ICMP :
31872693Sjojemann 
31882693Sjojemann 			orgicmp = (icmphdr_t *)udp;
31892693Sjojemann 
31902693Sjojemann 			if ((nat->nat_dir == NAT_OUTBOUND) &&
31912693Sjojemann 			    (orgicmp->icmp_id != nat->nat_inport) &&
31922693Sjojemann 			    (dlen >= 8)) {
31932393Syz155240 
31942393Syz155240 				/*
31952393Syz155240 				 * Fix ICMP checksum (of the offening ICMP
31962393Syz155240 				 * query packet) to compensate the change
31972393Syz155240 				 * in the ICMP id of the offending ICMP
31982393Syz155240 				 * packet.
31992393Syz155240 				 *
32002393Syz155240 				 * Since you modify orgicmp->icmp_id with
32012393Syz155240 				 * a delta (say x) and you compensate that
32022393Syz155240 				 * in origicmp->icmp_cksum with a delta
32032393Syz155240 				 * minus x, you don't have to adjust the
32042393Syz155240 				 * overall icmp->icmp_cksum
32052393Syz155240 				 */
32062693Sjojemann 
32072393Syz155240 				sum1 = ntohs(orgicmp->icmp_id);
32082393Syz155240 				sum2 = ntohs(nat->nat_inport);
32092393Syz155240 				CALC_SUMD(sum1, sum2, sumd);
32102393Syz155240 				orgicmp->icmp_id = nat->nat_inport;
32112393Syz155240 				fix_datacksum(&orgicmp->icmp_cksum, sumd);
32122693Sjojemann 
32132693Sjojemann 			} /* nat_dir can't be NAT_INBOUND for icmp queries */
32142693Sjojemann 
32152693Sjojemann 			break;
32162693Sjojemann 
32172693Sjojemann 		default :
32182693Sjojemann 
32192693Sjojemann 			break;
32202693Sjojemann 
32212693Sjojemann 	} /* switch (oip->ip_p) */
32222693Sjojemann 
32232693Sjojemann 	/*
32242693Sjojemann 	 * Step 3
32252693Sjojemann 	 * Make the adjustments to icmp checksum.
32262693Sjojemann 	 */
32272693Sjojemann 
32282693Sjojemann 	if (sumd2 != 0) {
32292693Sjojemann 		sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
32302693Sjojemann 		sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
32312958Sdr146992 		fix_incksum(&icmp->icmp_cksum, sumd2);
32322393Syz155240 	}
32332393Syz155240 	return nat;
32342393Syz155240 }
32352393Syz155240 
32362393Syz155240 
32372393Syz155240 /*
32382393Syz155240  * NB: these lookups don't lock access to the list, it assumed that it has
32392393Syz155240  * already been done!
32402393Syz155240  */
32412393Syz155240 
32422393Syz155240 /* ------------------------------------------------------------------------ */
32432393Syz155240 /* Function:    nat_inlookup                                                */
32442393Syz155240 /* Returns:     nat_t* - NULL == no match,                                  */
32452393Syz155240 /*                       else pointer to matching NAT entry                 */
32462393Syz155240 /* Parameters:  fin(I)    - pointer to packet information                   */
32472393Syz155240 /*              flags(I)  - NAT flags for this packet                       */
32482393Syz155240 /*              p(I)      - protocol for this packet                        */
32492393Syz155240 /*              src(I)    - source IP address                               */
32502393Syz155240 /*              mapdst(I) - destination IP address                          */
32512393Syz155240 /*                                                                          */
32522393Syz155240 /* Lookup a nat entry based on the mapped destination ip address/port and   */
32532393Syz155240 /* real source address/port.  We use this lookup when receiving a packet,   */
32542393Syz155240 /* we're looking for a table entry, based on the destination address.       */
32552393Syz155240 /*                                                                          */
32562393Syz155240 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
32572393Syz155240 /*                                                                          */
32582393Syz155240 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
32592393Syz155240 /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
32602393Syz155240 /*                                                                          */
32612393Syz155240 /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
32622393Syz155240 /*            the packet is of said protocol                                */
32632393Syz155240 /* ------------------------------------------------------------------------ */
nat_inlookup(fin,flags,p,src,mapdst)32642393Syz155240 nat_t *nat_inlookup(fin, flags, p, src, mapdst)
32652393Syz155240 fr_info_t *fin;
32662393Syz155240 u_int flags, p;
32672393Syz155240 struct in_addr src , mapdst;
32682393Syz155240 {
32692393Syz155240 	u_short sport, dport;
32702393Syz155240 	ipnat_t *ipn;
32712393Syz155240 	u_int sflags;
32722393Syz155240 	nat_t *nat;
32732393Syz155240 	int nflags;
32742393Syz155240 	u_32_t dst;
32752393Syz155240 	void *ifp;
32762393Syz155240 	u_int hv;
32773448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
32782393Syz155240 
32792393Syz155240 	if (fin != NULL)
32802393Syz155240 		ifp = fin->fin_ifp;
32812393Syz155240 	else
32822393Syz155240 		ifp = NULL;
32832393Syz155240 	sport = 0;
32842393Syz155240 	dport = 0;
32852393Syz155240 	dst = mapdst.s_addr;
32862393Syz155240 	sflags = flags & NAT_TCPUDPICMP;
32872393Syz155240 
32882393Syz155240 	switch (p)
32892393Syz155240 	{
32902393Syz155240 	case IPPROTO_TCP :
32912393Syz155240 	case IPPROTO_UDP :
32922393Syz155240 		sport = htons(fin->fin_data[0]);
32932393Syz155240 		dport = htons(fin->fin_data[1]);
32942393Syz155240 		break;
32952393Syz155240 	case IPPROTO_ICMP :
32962393Syz155240 		if (flags & IPN_ICMPERR)
32972393Syz155240 			sport = fin->fin_data[1];
32982393Syz155240 		else
32992393Syz155240 			dport = fin->fin_data[1];
33002393Syz155240 		break;
33012393Syz155240 	default :
33022393Syz155240 		break;
33032393Syz155240 	}
33042393Syz155240 
33052393Syz155240 
33062393Syz155240 	if ((flags & SI_WILDP) != 0)
33072393Syz155240 		goto find_in_wild_ports;
33082393Syz155240 
33092393Syz155240 	hv = NAT_HASH_FN(dst, dport, 0xffffffff);
33103448Sdh155122 	hv = NAT_HASH_FN(src.s_addr, hv + sport, ifs->ifs_ipf_nattable_sz);
33113448Sdh155122 	nat = ifs->ifs_nat_table[1][hv];
33122393Syz155240 	for (; nat; nat = nat->nat_hnext[1]) {
33137176Syx160601 		if (nat->nat_v != 4)
33147176Syx160601 			continue;
33157176Syx160601 
33162508Syz155240 		if (nat->nat_ifps[0] != NULL) {
33172508Syz155240 			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
33182508Syz155240 				continue;
33192508Syz155240 		} else if (ifp != NULL)
33202508Syz155240 			nat->nat_ifps[0] = ifp;
33212508Syz155240 
33222393Syz155240 		nflags = nat->nat_flags;
33232393Syz155240 
33242393Syz155240 		if (nat->nat_oip.s_addr == src.s_addr &&
33252393Syz155240 		    nat->nat_outip.s_addr == dst &&
33262393Syz155240 		    (((p == 0) &&
33272393Syz155240 		      (sflags == (nat->nat_flags & IPN_TCPUDPICMP)))
33282393Syz155240 		     || (p == nat->nat_p))) {
33292393Syz155240 			switch (p)
33302393Syz155240 			{
33312393Syz155240 #if 0
33322393Syz155240 			case IPPROTO_GRE :
33332393Syz155240 				if (nat->nat_call[1] != fin->fin_data[0])
33342393Syz155240 					continue;
33352393Syz155240 				break;
33362393Syz155240 #endif
33372393Syz155240 			case IPPROTO_ICMP :
33382393Syz155240 				if ((flags & IPN_ICMPERR) != 0) {
33392393Syz155240 					if (nat->nat_outport != sport)
33402393Syz155240 						continue;
33412393Syz155240 				} else {
33422393Syz155240 					if (nat->nat_outport != dport)
33432393Syz155240 						continue;
33442393Syz155240 				}
33452393Syz155240 				break;
33462393Syz155240 			case IPPROTO_TCP :
33472393Syz155240 			case IPPROTO_UDP :
33482393Syz155240 				if (nat->nat_oport != sport)
33492393Syz155240 					continue;
33502393Syz155240 				if (nat->nat_outport != dport)
33512393Syz155240 					continue;
33522393Syz155240 				break;
33532393Syz155240 			default :
33542393Syz155240 				break;
33552393Syz155240 			}
33562393Syz155240 
33572393Syz155240 			ipn = nat->nat_ptr;
33582393Syz155240 			if ((ipn != NULL) && (nat->nat_aps != NULL))
33592393Syz155240 				if (appr_match(fin, nat) != 0)
33602393Syz155240 					continue;
33612393Syz155240 			return nat;
33622393Syz155240 		}
33632393Syz155240 	}
33642393Syz155240 
33652393Syz155240 	/*
33662393Syz155240 	 * So if we didn't find it but there are wildcard members in the hash
33672393Syz155240 	 * table, go back and look for them.  We do this search and update here
33682393Syz155240 	 * because it is modifying the NAT table and we want to do this only
33692393Syz155240 	 * for the first packet that matches.  The exception, of course, is
33702393Syz155240 	 * for "dummy" (FI_IGNORE) lookups.
33712393Syz155240 	 */
33722393Syz155240 find_in_wild_ports:
33732393Syz155240 	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
33742393Syz155240 		return NULL;
33753448Sdh155122 	if (ifs->ifs_nat_stats.ns_wilds == 0)
33762393Syz155240 		return NULL;
33772393Syz155240 
33783448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
33792393Syz155240 
33802393Syz155240 	hv = NAT_HASH_FN(dst, 0, 0xffffffff);
33813448Sdh155122 	hv = NAT_HASH_FN(src.s_addr, hv, ifs->ifs_ipf_nattable_sz);
33823448Sdh155122 
33833448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
33843448Sdh155122 
33853448Sdh155122 	nat = ifs->ifs_nat_table[1][hv];
33862393Syz155240 	for (; nat; nat = nat->nat_hnext[1]) {
33877176Syx160601 		if (nat->nat_v != 4)
33887176Syx160601 			continue;
33897176Syx160601 
33902508Syz155240 		if (nat->nat_ifps[0] != NULL) {
33912508Syz155240 			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
33922508Syz155240 				continue;
33932508Syz155240 		} else if (ifp != NULL)
33942508Syz155240 			nat->nat_ifps[0] = ifp;
33952393Syz155240 
33962393Syz155240 		if (nat->nat_p != fin->fin_p)
33972393Syz155240 			continue;
33982393Syz155240 		if (nat->nat_oip.s_addr != src.s_addr ||
33992393Syz155240 		    nat->nat_outip.s_addr != dst)
34002393Syz155240 			continue;
34012393Syz155240 
34022393Syz155240 		nflags = nat->nat_flags;
34032393Syz155240 		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
34042393Syz155240 			continue;
34052393Syz155240 
34062393Syz155240 		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
34072393Syz155240 			       NAT_INBOUND) == 1) {
34082393Syz155240 			if ((fin->fin_flx & FI_IGNORE) != 0)
34092393Syz155240 				break;
34102393Syz155240 			if ((nflags & SI_CLONE) != 0) {
34112393Syz155240 				nat = fr_natclone(fin, nat);
34122393Syz155240 				if (nat == NULL)
34132393Syz155240 					break;
34142393Syz155240 			} else {
34153448Sdh155122 				MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
34163448Sdh155122 				ifs->ifs_nat_stats.ns_wilds--;
34173448Sdh155122 				MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
34182393Syz155240 			}
34192393Syz155240 			nat->nat_oport = sport;
34202393Syz155240 			nat->nat_outport = dport;
34212393Syz155240 			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
34223448Sdh155122 			nat_tabmove(nat, ifs);
34232393Syz155240 			break;
34242393Syz155240 		}
34252393Syz155240 	}
34262393Syz155240 
34273448Sdh155122 	MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
34282393Syz155240 
34292393Syz155240 	return nat;
34302393Syz155240 }
34312393Syz155240 
34322393Syz155240 
34332393Syz155240 /* ------------------------------------------------------------------------ */
34342393Syz155240 /* Function:    nat_tabmove                                                 */
34352393Syz155240 /* Returns:     Nil                                                         */
34362393Syz155240 /* Parameters:  nat(I) - pointer to NAT structure                           */
34372393Syz155240 /* Write Lock:  ipf_nat                                                     */
34382393Syz155240 /*                                                                          */
34392393Syz155240 /* This function is only called for TCP/UDP NAT table entries where the     */
34402393Syz155240 /* original was placed in the table without hashing on the ports and we now */
34412393Syz155240 /* want to include hashing on port numbers.                                 */
34422393Syz155240 /* ------------------------------------------------------------------------ */
nat_tabmove(nat,ifs)34433448Sdh155122 static void nat_tabmove(nat, ifs)
34442393Syz155240 nat_t *nat;
34453448Sdh155122 ipf_stack_t *ifs;
34462393Syz155240 {
34472393Syz155240 	nat_t **natp;
34482393Syz155240 	u_int hv;
34492393Syz155240 
34502393Syz155240 	if (nat->nat_flags & SI_CLONE)
34512393Syz155240 		return;
34522393Syz155240 
34532393Syz155240 	/*
34542393Syz155240 	 * Remove the NAT entry from the old location
34552393Syz155240 	 */
34562393Syz155240 	if (nat->nat_hnext[0])
34572393Syz155240 		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
34582393Syz155240 	*nat->nat_phnext[0] = nat->nat_hnext[0];
34593448Sdh155122 	ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
34602393Syz155240 
34612393Syz155240 	if (nat->nat_hnext[1])
34622393Syz155240 		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
34632393Syz155240 	*nat->nat_phnext[1] = nat->nat_hnext[1];
34643448Sdh155122 	ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
34652393Syz155240 
34662393Syz155240 	/*
34672393Syz155240 	 * Add into the NAT table in the new position
34682393Syz155240 	 */
34692393Syz155240 	hv = NAT_HASH_FN(nat->nat_inip.s_addr, nat->nat_inport, 0xffffffff);
34702393Syz155240 	hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport,
34713448Sdh155122 			 ifs->ifs_ipf_nattable_sz);
34722393Syz155240 	nat->nat_hv[0] = hv;
34733448Sdh155122 	natp = &ifs->ifs_nat_table[0][hv];
34742393Syz155240 	if (*natp)
34752393Syz155240 		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
34762393Syz155240 	nat->nat_phnext[0] = natp;
34772393Syz155240 	nat->nat_hnext[0] = *natp;
34782393Syz155240 	*natp = nat;
34793448Sdh155122 	ifs->ifs_nat_stats.ns_bucketlen[0][hv]++;
34802393Syz155240 
34812393Syz155240 	hv = NAT_HASH_FN(nat->nat_outip.s_addr, nat->nat_outport, 0xffffffff);
34822393Syz155240 	hv = NAT_HASH_FN(nat->nat_oip.s_addr, hv + nat->nat_oport,
34833448Sdh155122 			 ifs->ifs_ipf_nattable_sz);
34842393Syz155240 	nat->nat_hv[1] = hv;
34853448Sdh155122 	natp = &ifs->ifs_nat_table[1][hv];
34862393Syz155240 	if (*natp)
34872393Syz155240 		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
34882393Syz155240 	nat->nat_phnext[1] = natp;
34892393Syz155240 	nat->nat_hnext[1] = *natp;
34902393Syz155240 	*natp = nat;
34913448Sdh155122 	ifs->ifs_nat_stats.ns_bucketlen[1][hv]++;
34922393Syz155240 }
34932393Syz155240 
34942393Syz155240 
34952393Syz155240 /* ------------------------------------------------------------------------ */
34962393Syz155240 /* Function:    nat_outlookup                                               */
34972393Syz155240 /* Returns:     nat_t* - NULL == no match,                                  */
34982393Syz155240 /*                       else pointer to matching NAT entry                 */
34992393Syz155240 /* Parameters:  fin(I)   - pointer to packet information                    */
35002393Syz155240 /*              flags(I) - NAT flags for this packet                        */
35012393Syz155240 /*              p(I)     - protocol for this packet                         */
35022393Syz155240 /*              src(I)   - source IP address                                */
35032393Syz155240 /*              dst(I)   - destination IP address                           */
35042393Syz155240 /*              rw(I)    - 1 == write lock on ipf_nat held, 0 == read lock. */
35052393Syz155240 /*                                                                          */
35062393Syz155240 /* Lookup a nat entry based on the source 'real' ip address/port and        */
35072393Syz155240 /* destination address/port.  We use this lookup when sending a packet out, */
35082393Syz155240 /* we're looking for a table entry, based on the source address.            */
35092393Syz155240 /*                                                                          */
35102393Syz155240 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
35112393Syz155240 /*                                                                          */
35122393Syz155240 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
35132393Syz155240 /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
35142393Syz155240 /*                                                                          */
35152393Syz155240 /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
35162393Syz155240 /*            the packet is of said protocol                                */
35172393Syz155240 /* ------------------------------------------------------------------------ */
nat_outlookup(fin,flags,p,src,dst)35182393Syz155240 nat_t *nat_outlookup(fin, flags, p, src, dst)
35192393Syz155240 fr_info_t *fin;
35202393Syz155240 u_int flags, p;
35212393Syz155240 struct in_addr src , dst;
35222393Syz155240 {
35232393Syz155240 	u_short sport, dport;
35242393Syz155240 	u_int sflags;
35252393Syz155240 	ipnat_t *ipn;
35262393Syz155240 	u_32_t srcip;
35272393Syz155240 	nat_t *nat;
35282393Syz155240 	int nflags;
35292393Syz155240 	void *ifp;
35302393Syz155240 	u_int hv;
35313448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
35322508Syz155240 
35333894Sjojemann 	ifp = fin->fin_ifp;
35342508Syz155240 
35352393Syz155240 	srcip = src.s_addr;
35362393Syz155240 	sflags = flags & IPN_TCPUDPICMP;
35372393Syz155240 	sport = 0;
35382393Syz155240 	dport = 0;
35392393Syz155240 
35402393Syz155240 	switch (p)
35412393Syz155240 	{
35422393Syz155240 	case IPPROTO_TCP :
35432393Syz155240 	case IPPROTO_UDP :
35442393Syz155240 		sport = htons(fin->fin_data[0]);
35452393Syz155240 		dport = htons(fin->fin_data[1]);
35462393Syz155240 		break;
35472393Syz155240 	case IPPROTO_ICMP :
35482393Syz155240 		if (flags & IPN_ICMPERR)
35492393Syz155240 			sport = fin->fin_data[1];
35502393Syz155240 		else
35512393Syz155240 			dport = fin->fin_data[1];
35522393Syz155240 		break;
35532393Syz155240 	default :
35542393Syz155240 		break;
35552393Syz155240 	}
35562393Syz155240 
35572393Syz155240 	if ((flags & SI_WILDP) != 0)
35582393Syz155240 		goto find_out_wild_ports;
35592393Syz155240 
35602393Syz155240 	hv = NAT_HASH_FN(srcip, sport, 0xffffffff);
35613448Sdh155122 	hv = NAT_HASH_FN(dst.s_addr, hv + dport, ifs->ifs_ipf_nattable_sz);
35623448Sdh155122 	nat = ifs->ifs_nat_table[0][hv];
35632393Syz155240 	for (; nat; nat = nat->nat_hnext[0]) {
35647176Syx160601 		if (nat->nat_v != 4)
35657176Syx160601 			continue;
35667176Syx160601 
35672508Syz155240 		if (nat->nat_ifps[1] != NULL) {
35682508Syz155240 			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
35692508Syz155240 				continue;
35702508Syz155240 		} else if (ifp != NULL)
35712508Syz155240 			nat->nat_ifps[1] = ifp;
35722508Syz155240 
35732393Syz155240 		nflags = nat->nat_flags;
35742508Syz155240 
35752393Syz155240 		if (nat->nat_inip.s_addr == srcip &&
35762393Syz155240 		    nat->nat_oip.s_addr == dst.s_addr &&
35772393Syz155240 		    (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP)))
35782393Syz155240 		     || (p == nat->nat_p))) {
35792393Syz155240 			switch (p)
35802393Syz155240 			{
35812393Syz155240 #if 0
35822393Syz155240 			case IPPROTO_GRE :
35832393Syz155240 				if (nat->nat_call[1] != fin->fin_data[0])
35842393Syz155240 					continue;
35852393Syz155240 				break;
35862393Syz155240 #endif
35872393Syz155240 			case IPPROTO_TCP :
35882393Syz155240 			case IPPROTO_UDP :
35892393Syz155240 				if (nat->nat_oport != dport)
35902393Syz155240 					continue;
35912393Syz155240 				if (nat->nat_inport != sport)
35922393Syz155240 					continue;
35932393Syz155240 				break;
35942393Syz155240 			default :
35952393Syz155240 				break;
35962393Syz155240 			}
35972393Syz155240 
35982393Syz155240 			ipn = nat->nat_ptr;
35992393Syz155240 			if ((ipn != NULL) && (nat->nat_aps != NULL))
36002393Syz155240 				if (appr_match(fin, nat) != 0)
36012393Syz155240 					continue;
36022393Syz155240 			return nat;
36032393Syz155240 		}
36042393Syz155240 	}
36052393Syz155240 
36062393Syz155240 	/*
36072393Syz155240 	 * So if we didn't find it but there are wildcard members in the hash
36082393Syz155240 	 * table, go back and look for them.  We do this search and update here
36092393Syz155240 	 * because it is modifying the NAT table and we want to do this only
36102393Syz155240 	 * for the first packet that matches.  The exception, of course, is
36112393Syz155240 	 * for "dummy" (FI_IGNORE) lookups.
36122393Syz155240 	 */
36132393Syz155240 find_out_wild_ports:
36142393Syz155240 	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
36152393Syz155240 		return NULL;
36163448Sdh155122 	if (ifs->ifs_nat_stats.ns_wilds == 0)
36172393Syz155240 		return NULL;
36182393Syz155240 
36193448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
36202393Syz155240 
36212393Syz155240 	hv = NAT_HASH_FN(srcip, 0, 0xffffffff);
36223448Sdh155122 	hv = NAT_HASH_FN(dst.s_addr, hv, ifs->ifs_ipf_nattable_sz);
36233448Sdh155122 
36243448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
36253448Sdh155122 
36263448Sdh155122 	nat = ifs->ifs_nat_table[0][hv];
36272393Syz155240 	for (; nat; nat = nat->nat_hnext[0]) {
36287176Syx160601 		if (nat->nat_v != 4)
36297176Syx160601 			continue;
36307176Syx160601 
36312508Syz155240 		if (nat->nat_ifps[1] != NULL) {
36322508Syz155240 			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
36333448Sdh155122 				continue;
36342508Syz155240 		} else if (ifp != NULL)
36352508Syz155240 			nat->nat_ifps[1] = ifp;
36362393Syz155240 
36372393Syz155240 		if (nat->nat_p != fin->fin_p)
36382393Syz155240 			continue;
36392393Syz155240 		if ((nat->nat_inip.s_addr != srcip) ||
36402393Syz155240 		    (nat->nat_oip.s_addr != dst.s_addr))
36412393Syz155240 			continue;
36422393Syz155240 
36432393Syz155240 		nflags = nat->nat_flags;
36442393Syz155240 		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
36452393Syz155240 			continue;
36462393Syz155240 
36472393Syz155240 		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
36482393Syz155240 			       NAT_OUTBOUND) == 1) {
36492393Syz155240 			if ((fin->fin_flx & FI_IGNORE) != 0)
36502393Syz155240 				break;
36512393Syz155240 			if ((nflags & SI_CLONE) != 0) {
36522393Syz155240 				nat = fr_natclone(fin, nat);
36532393Syz155240 				if (nat == NULL)
36542393Syz155240 					break;
36552393Syz155240 			} else {
36563448Sdh155122 				MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
36573448Sdh155122 				ifs->ifs_nat_stats.ns_wilds--;
36583448Sdh155122 				MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
36592393Syz155240 			}
36602393Syz155240 			nat->nat_inport = sport;
36612393Syz155240 			nat->nat_oport = dport;
36622393Syz155240 			if (nat->nat_outport == 0)
36632393Syz155240 				nat->nat_outport = sport;
36642393Syz155240 			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
36653448Sdh155122 			nat_tabmove(nat, ifs);
36662393Syz155240 			break;
36672393Syz155240 		}
36682393Syz155240 	}
36692393Syz155240 
36703448Sdh155122 	MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
36712393Syz155240 
36722393Syz155240 	return nat;
36732393Syz155240 }
36742393Syz155240 
36752393Syz155240 
36762393Syz155240 /* ------------------------------------------------------------------------ */
36772393Syz155240 /* Function:    nat_lookupredir                                             */
36782393Syz155240 /* Returns:     nat_t* - NULL == no match,                                  */
36792393Syz155240 /*                       else pointer to matching NAT entry                 */
36802393Syz155240 /* Parameters:  np(I) - pointer to description of packet to find NAT table  */
36812393Syz155240 /*                      entry for.                                          */
36822393Syz155240 /*                                                                          */
36832393Syz155240 /* Lookup the NAT tables to search for a matching redirect                  */
36842393Syz155240 /* ------------------------------------------------------------------------ */
nat_lookupredir(np,ifs)36853448Sdh155122 nat_t *nat_lookupredir(np, ifs)
36862393Syz155240 natlookup_t *np;
36873448Sdh155122 ipf_stack_t *ifs;
36882393Syz155240 {
36892393Syz155240 	fr_info_t fi;
36902393Syz155240 	nat_t *nat;
36912393Syz155240 
36922393Syz155240 	bzero((char *)&fi, sizeof(fi));
36932393Syz155240 	if (np->nl_flags & IPN_IN) {
36942393Syz155240 		fi.fin_data[0] = ntohs(np->nl_realport);
36952393Syz155240 		fi.fin_data[1] = ntohs(np->nl_outport);
36962393Syz155240 	} else {
36972393Syz155240 		fi.fin_data[0] = ntohs(np->nl_inport);
36982393Syz155240 		fi.fin_data[1] = ntohs(np->nl_outport);
36992393Syz155240 	}
37002393Syz155240 	if (np->nl_flags & IPN_TCP)
37012393Syz155240 		fi.fin_p = IPPROTO_TCP;
37022393Syz155240 	else if (np->nl_flags & IPN_UDP)
37032393Syz155240 		fi.fin_p = IPPROTO_UDP;
37042393Syz155240 	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
37052393Syz155240 		fi.fin_p = IPPROTO_ICMP;
37062393Syz155240 
37073448Sdh155122 	fi.fin_ifs = ifs;
37082393Syz155240 	/*
37092393Syz155240 	 * We can do two sorts of lookups:
37102393Syz155240 	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
37112393Syz155240 	 * - default: we have the `in' and `out' address, look for `real'.
37122393Syz155240 	 */
37132393Syz155240 	if (np->nl_flags & IPN_IN) {
37142393Syz155240 		if ((nat = nat_inlookup(&fi, np->nl_flags, fi.fin_p,
37152393Syz155240 					np->nl_realip, np->nl_outip))) {
37162393Syz155240 			np->nl_inip = nat->nat_inip;
37172393Syz155240 			np->nl_inport = nat->nat_inport;
37182393Syz155240 		}
37192393Syz155240 	} else {
37202393Syz155240 		/*
37212393Syz155240 		 * If nl_inip is non null, this is a lookup based on the real
37222393Syz155240 		 * ip address. Else, we use the fake.
37232393Syz155240 		 */
37242393Syz155240 		if ((nat = nat_outlookup(&fi, np->nl_flags, fi.fin_p,
37252393Syz155240 					 np->nl_inip, np->nl_outip))) {
37262393Syz155240 
37272393Syz155240 			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
37282393Syz155240 				fr_info_t fin;
37292393Syz155240 				bzero((char *)&fin, sizeof(fin));
37302393Syz155240 				fin.fin_p = nat->nat_p;
37312393Syz155240 				fin.fin_data[0] = ntohs(nat->nat_outport);
37322393Syz155240 				fin.fin_data[1] = ntohs(nat->nat_oport);
37333448Sdh155122 				fin.fin_ifs = ifs;
37342393Syz155240 				if (nat_inlookup(&fin, np->nl_flags, fin.fin_p,
37352393Syz155240 						 nat->nat_outip,
37362393Syz155240 						 nat->nat_oip) != NULL) {
37372393Syz155240 					np->nl_flags &= ~IPN_FINDFORWARD;
37382393Syz155240 				}
37392393Syz155240 			}
37402393Syz155240 
37412393Syz155240 			np->nl_realip = nat->nat_outip;
37422393Syz155240 			np->nl_realport = nat->nat_outport;
37432393Syz155240 		}
37442393Syz155240  	}
37452393Syz155240 
37462393Syz155240 	return nat;
37472393Syz155240 }
37482393Syz155240 
37492393Syz155240 
37502393Syz155240 /* ------------------------------------------------------------------------ */
37512393Syz155240 /* Function:    nat_match                                                   */
37522393Syz155240 /* Returns:     int - 0 == no match, 1 == match                             */
37532393Syz155240 /* Parameters:  fin(I)   - pointer to packet information                    */
37542393Syz155240 /*              np(I)    - pointer to NAT rule                              */
37552393Syz155240 /*                                                                          */
37562393Syz155240 /* Pull the matching of a packet against a NAT rule out of that complex     */
37572393Syz155240 /* loop inside fr_checknatin() and lay it out properly in its own function. */
37582393Syz155240 /* ------------------------------------------------------------------------ */
nat_match(fin,np)37592393Syz155240 static int nat_match(fin, np)
37602393Syz155240 fr_info_t *fin;
37612393Syz155240 ipnat_t *np;
37622393Syz155240 {
37632393Syz155240 	frtuc_t *ft;
37642393Syz155240 
37652393Syz155240 	if (fin->fin_v != 4)
37662393Syz155240 		return 0;
37672393Syz155240 
37682393Syz155240 	if (np->in_p && fin->fin_p != np->in_p)
37692393Syz155240 		return 0;
37702393Syz155240 
37712393Syz155240 	if (fin->fin_out) {
37722393Syz155240 		if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK)))
37732393Syz155240 			return 0;
37742393Syz155240 		if (((fin->fin_fi.fi_saddr & np->in_inmsk) != np->in_inip)
37752393Syz155240 		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
37762393Syz155240 			return 0;
37772393Syz155240 		if (((fin->fin_fi.fi_daddr & np->in_srcmsk) != np->in_srcip)
37782393Syz155240 		    ^ ((np->in_flags & IPN_NOTDST) != 0))
37792393Syz155240 			return 0;
37802393Syz155240 	} else {
37812393Syz155240 		if (!(np->in_redir & NAT_REDIRECT))
37822393Syz155240 			return 0;
37832393Syz155240 		if (((fin->fin_fi.fi_saddr & np->in_srcmsk) != np->in_srcip)
37842393Syz155240 		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
37852393Syz155240 			return 0;
37862393Syz155240 		if (((fin->fin_fi.fi_daddr & np->in_outmsk) != np->in_outip)
37872393Syz155240 		    ^ ((np->in_flags & IPN_NOTDST) != 0))
37882393Syz155240 			return 0;
37892393Syz155240 	}
37902393Syz155240 
37912393Syz155240 	ft = &np->in_tuc;
37922393Syz155240 	if (!(fin->fin_flx & FI_TCPUDP) ||
37932393Syz155240 	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
37942393Syz155240 		if (ft->ftu_scmp || ft->ftu_dcmp)
37952393Syz155240 			return 0;
37962393Syz155240 		return 1;
37972393Syz155240 	}
37982393Syz155240 
37992393Syz155240 	return fr_tcpudpchk(fin, ft);
38002393Syz155240 }
38012393Syz155240 
38022393Syz155240 
38032393Syz155240 /* ------------------------------------------------------------------------ */
38042393Syz155240 /* Function:    nat_update                                                  */
38052393Syz155240 /* Returns:     Nil                                                         */
38067333SJohn.Ojemann@Sun.COM /* Parameters:	fin(I) - pointer to packet information			    */
38077333SJohn.Ojemann@Sun.COM /*		nat(I) - pointer to NAT structure			    */
38082393Syz155240 /*              np(I)     - pointer to NAT rule                             */
38097333SJohn.Ojemann@Sun.COM /* Locks:	nat_lock						    */
38102393Syz155240 /*                                                                          */
38112393Syz155240 /* Updates the lifetime of a NAT table entry for non-TCP packets.  Must be  */
38122393Syz155240 /* called with fin_rev updated - i.e. after calling nat_proto().            */
38132393Syz155240 /* ------------------------------------------------------------------------ */
nat_update(fin,nat,np)38142393Syz155240 void nat_update(fin, nat, np)
38152393Syz155240 fr_info_t *fin;
38162393Syz155240 nat_t *nat;
38172393Syz155240 ipnat_t *np;
38182393Syz155240 {
38192393Syz155240 	ipftq_t *ifq, *ifq2;
38202393Syz155240 	ipftqent_t *tqe;
38213448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
38222393Syz155240 
38232393Syz155240 	tqe = &nat->nat_tqe;
38242393Syz155240 	ifq = tqe->tqe_ifq;
38252393Syz155240 
38262393Syz155240 	/*
38272393Syz155240 	 * We allow over-riding of NAT timeouts from NAT rules, even for
38282393Syz155240 	 * TCP, however, if it is TCP and there is no rule timeout set,
38292393Syz155240 	 * then do not update the timeout here.
38302393Syz155240 	 */
38312393Syz155240 	if (np != NULL)
38322393Syz155240 		ifq2 = np->in_tqehead[fin->fin_rev];
38332393Syz155240 	else
38342393Syz155240 		ifq2 = NULL;
38352393Syz155240 
38362393Syz155240 	if (nat->nat_p == IPPROTO_TCP && ifq2 == NULL) {
38373448Sdh155122 		(void) fr_tcp_age(&nat->nat_tqe, fin, ifs->ifs_nat_tqb, 0);
38382393Syz155240 	} else {
38392393Syz155240 		if (ifq2 == NULL) {
38402393Syz155240 			if (nat->nat_p == IPPROTO_UDP)
38413448Sdh155122 				ifq2 = &ifs->ifs_nat_udptq;
38422393Syz155240 			else if (nat->nat_p == IPPROTO_ICMP)
38433448Sdh155122 				ifq2 = &ifs->ifs_nat_icmptq;
38442393Syz155240 			else
38453448Sdh155122 				ifq2 = &ifs->ifs_nat_iptq;
38462393Syz155240 		}
38472393Syz155240 
38483448Sdh155122 		fr_movequeue(tqe, ifq, ifq2, ifs);
38492393Syz155240 	}
38502393Syz155240 }
38512393Syz155240 
38522393Syz155240 
38532393Syz155240 /* ------------------------------------------------------------------------ */
38542393Syz155240 /* Function:    fr_checknatout                                              */
38552393Syz155240 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
38562393Syz155240 /*                     0 == no packet translation occurred,                 */
38572393Syz155240 /*                     1 == packet was successfully translated.             */
38582393Syz155240 /* Parameters:  fin(I)   - pointer to packet information                    */
38592393Syz155240 /*              passp(I) - pointer to filtering result flags                */
38602393Syz155240 /*                                                                          */
38612393Syz155240 /* Check to see if an outcoming packet should be changed.  ICMP packets are */
38622393Syz155240 /* first checked to see if they match an existing entry (if an error),      */
38632393Syz155240 /* otherwise a search of the current NAT table is made.  If neither results */
38642393Syz155240 /* in a match then a search for a matching NAT rule is made.  Create a new  */
38652393Syz155240 /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
38662393Syz155240 /* packet header(s) as required.                                            */
38672393Syz155240 /* ------------------------------------------------------------------------ */
fr_checknatout(fin,passp)38682393Syz155240 int fr_checknatout(fin, passp)
38692393Syz155240 fr_info_t *fin;
38702393Syz155240 u_32_t *passp;
38712393Syz155240 {
38727131Sdr146992 	ipnat_t *np = NULL, *npnext;
38732393Syz155240 	struct ifnet *ifp, *sifp;
38742393Syz155240 	icmphdr_t *icmp = NULL;
38752393Syz155240 	tcphdr_t *tcp = NULL;
38762393Syz155240 	int rval, natfailed;
38772393Syz155240 	u_int nflags = 0;
38782393Syz155240 	u_32_t ipa, iph;
38792393Syz155240 	int natadd = 1;
38802393Syz155240 	frentry_t *fr;
38812393Syz155240 	nat_t *nat;
38823448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
38833448Sdh155122 
38847432SJohn.Ojemann@Sun.COM 	if (ifs->ifs_fr_nat_lock != 0)
38857432SJohn.Ojemann@Sun.COM 		return 0;
38867432SJohn.Ojemann@Sun.COM 	if (ifs->ifs_nat_stats.ns_rules == 0 && ifs->ifs_nat_instances == NULL)
38872393Syz155240 		return 0;
38882393Syz155240 
38892393Syz155240 	natfailed = 0;
38902393Syz155240 	fr = fin->fin_fr;
38912393Syz155240 	sifp = fin->fin_ifp;
38922393Syz155240 	if ((fr != NULL) && !(fr->fr_flags & FR_DUP) &&
38933894Sjojemann 	    fr->fr_tifs[fin->fin_rev].fd_ifp &&
38943894Sjojemann 	    fr->fr_tifs[fin->fin_rev].fd_ifp != (void *)-1)
38953894Sjojemann 		fin->fin_ifp = fr->fr_tifs[fin->fin_rev].fd_ifp;
38962393Syz155240 	ifp = fin->fin_ifp;
38972393Syz155240 
38982393Syz155240 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
38992393Syz155240 		switch (fin->fin_p)
39002393Syz155240 		{
39012393Syz155240 		case IPPROTO_TCP :
39022393Syz155240 			nflags = IPN_TCP;
39032393Syz155240 			break;
39042393Syz155240 		case IPPROTO_UDP :
39052393Syz155240 			nflags = IPN_UDP;
39062393Syz155240 			break;
39072393Syz155240 		case IPPROTO_ICMP :
39082393Syz155240 			icmp = fin->fin_dp;
39092393Syz155240 
39102393Syz155240 			/*
39112393Syz155240 			 * This is an incoming packet, so the destination is
39122393Syz155240 			 * the icmp_id and the source port equals 0
39132393Syz155240 			 */
39142393Syz155240 			if (nat_icmpquerytype4(icmp->icmp_type))
39152393Syz155240 				nflags = IPN_ICMPQUERY;
39162393Syz155240 			break;
39172393Syz155240 		default :
39182393Syz155240 			break;
39192393Syz155240 		}
39202393Syz155240 
39212393Syz155240 		if ((nflags & IPN_TCPUDP))
39222393Syz155240 			tcp = fin->fin_dp;
39232393Syz155240 	}
39242393Syz155240 
39252393Syz155240 	ipa = fin->fin_saddr;
39262393Syz155240 
39273448Sdh155122 	READ_ENTER(&ifs->ifs_ipf_nat);
39282393Syz155240 
39292393Syz155240 	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
39302393Syz155240 	    (nat = nat_icmperror(fin, &nflags, NAT_OUTBOUND)))
39312393Syz155240 		/*EMPTY*/;
39322393Syz155240 	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
39332393Syz155240 		natadd = 0;
39342393Syz155240 	else if ((nat = nat_outlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p,
39352393Syz155240 				      fin->fin_src, fin->fin_dst))) {
39362393Syz155240 		nflags = nat->nat_flags;
39372393Syz155240 	} else {
39382393Syz155240 		u_32_t hv, msk, nmsk;
39392393Syz155240 
39402393Syz155240 		/*
39417433SJohn.Ojemann@Sun.COM 		 * There is no current entry in the nat table for this packet.
39427433SJohn.Ojemann@Sun.COM 		 *
39437433SJohn.Ojemann@Sun.COM 		 * If the packet is a fragment, but not the first fragment,
39447433SJohn.Ojemann@Sun.COM 		 * then don't do anything.  Otherwise, if there is a matching
39457433SJohn.Ojemann@Sun.COM 		 * nat rule, try to create a new nat entry.
39462393Syz155240 		 */
39477433SJohn.Ojemann@Sun.COM 		if ((fin->fin_off != 0) && (fin->fin_flx & FI_TCPUDP))
39487433SJohn.Ojemann@Sun.COM 			goto nonatfrag;
39497433SJohn.Ojemann@Sun.COM 
39502393Syz155240 		msk = 0xffffffff;
39513448Sdh155122 		nmsk = ifs->ifs_nat_masks;
39522393Syz155240 maskloop:
39532393Syz155240 		iph = ipa & htonl(msk);
39543448Sdh155122 		hv = NAT_HASH_FN(iph, 0, ifs->ifs_ipf_natrules_sz);
39557131Sdr146992 		for (np = ifs->ifs_nat_rules[hv]; np; np = npnext) {
39567131Sdr146992 			npnext = np->in_mnext;
39572508Syz155240 			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
39582393Syz155240 				continue;
39592393Syz155240 			if (np->in_v != fin->fin_v)
39602393Syz155240 				continue;
39612393Syz155240 			if (np->in_p && (np->in_p != fin->fin_p))
39622393Syz155240 				continue;
39632393Syz155240 			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
39642393Syz155240 				continue;
39652393Syz155240 			if (np->in_flags & IPN_FILTER) {
39662393Syz155240 				if (!nat_match(fin, np))
39672393Syz155240 					continue;
39682393Syz155240 			} else if ((ipa & np->in_inmsk) != np->in_inip)
39692393Syz155240 				continue;
39702393Syz155240 
39712393Syz155240 			if ((fr != NULL) &&
39722393Syz155240 			    !fr_matchtag(&np->in_tag, &fr->fr_nattag))
39732393Syz155240 				continue;
39742393Syz155240 
39752393Syz155240 			if (*np->in_plabel != '\0') {
39762393Syz155240 				if (((np->in_flags & IPN_FILTER) == 0) &&
39772393Syz155240 				    (np->in_dport != tcp->th_dport))
39782393Syz155240 					continue;
39792393Syz155240 				if (appr_ok(fin, tcp, np) == 0)
39802393Syz155240 					continue;
39812393Syz155240 			}
39822393Syz155240 
39837131Sdr146992 			ATOMIC_INC32(np->in_use);
39847131Sdr146992 			RWLOCK_EXIT(&ifs->ifs_ipf_nat);
39857131Sdr146992 			WRITE_ENTER(&ifs->ifs_ipf_nat);
39867131Sdr146992 			nat = nat_new(fin, np, NULL, nflags, NAT_OUTBOUND);
39877131Sdr146992 			if (nat != NULL) {
39887131Sdr146992 				np->in_use--;
39892393Syz155240 				np->in_hits++;
39907131Sdr146992 				MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
39912393Syz155240 				break;
39927131Sdr146992 			}
39937131Sdr146992 			natfailed = -1;
39947131Sdr146992 			npnext = np->in_mnext;
39957131Sdr146992 			fr_ipnatderef(&np, ifs);
39967131Sdr146992 			MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
39972393Syz155240 		}
39982393Syz155240 		if ((np == NULL) && (nmsk != 0)) {
39992393Syz155240 			while (nmsk) {
40002393Syz155240 				msk <<= 1;
40012393Syz155240 				if (nmsk & 0x80000000)
40022393Syz155240 					break;
40032393Syz155240 				nmsk <<= 1;
40042393Syz155240 			}
40052393Syz155240 			if (nmsk != 0) {
40062393Syz155240 				nmsk <<= 1;
40072393Syz155240 				goto maskloop;
40082393Syz155240 			}
40092393Syz155240 		}
40102393Syz155240 	}
40112393Syz155240 
40127433SJohn.Ojemann@Sun.COM nonatfrag:
40132393Syz155240 	if (nat != NULL) {
40142393Syz155240 		rval = fr_natout(fin, nat, natadd, nflags);
40152393Syz155240 		if (rval == 1) {
40162393Syz155240 			MUTEX_ENTER(&nat->nat_lock);
40177333SJohn.Ojemann@Sun.COM 			nat_update(fin, nat, nat->nat_ptr);
40187333SJohn.Ojemann@Sun.COM 			nat->nat_bytes[1] += fin->fin_plen;
40197333SJohn.Ojemann@Sun.COM 			nat->nat_pkts[1]++;
40208624SDarren.Reed@Sun.COM 			fin->fin_pktnum = nat->nat_pkts[1];
40212393Syz155240 			MUTEX_EXIT(&nat->nat_lock);
40222393Syz155240 		}
40232393Syz155240 	} else
40242393Syz155240 		rval = natfailed;
40253448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
40262393Syz155240 
40272393Syz155240 	if (rval == -1) {
40282393Syz155240 		if (passp != NULL)
40292393Syz155240 			*passp = FR_BLOCK;
40302393Syz155240 		fin->fin_flx |= FI_BADNAT;
40312393Syz155240 	}
40322393Syz155240 	fin->fin_ifp = sifp;
40332393Syz155240 	return rval;
40342393Syz155240 }
40352393Syz155240 
40362393Syz155240 /* ------------------------------------------------------------------------ */
40372393Syz155240 /* Function:    fr_natout                                                   */
40382393Syz155240 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
40392393Syz155240 /*                     1 == packet was successfully translated.             */
40402393Syz155240 /* Parameters:  fin(I)    - pointer to packet information                   */
40412393Syz155240 /*              nat(I)    - pointer to NAT structure                        */
40422393Syz155240 /*              natadd(I) - flag indicating if it is safe to add frag cache */
40432393Syz155240 /*              nflags(I) - NAT flags set for this packet                   */
40442393Syz155240 /*                                                                          */
40452393Syz155240 /* Translate a packet coming "out" on an interface.                         */
40462393Syz155240 /* ------------------------------------------------------------------------ */
fr_natout(fin,nat,natadd,nflags)40472393Syz155240 int fr_natout(fin, nat, natadd, nflags)
40482393Syz155240 fr_info_t *fin;
40492393Syz155240 nat_t *nat;
40502393Syz155240 int natadd;
40512393Syz155240 u_32_t nflags;
40522393Syz155240 {
40532393Syz155240 	icmphdr_t *icmp;
40542393Syz155240 	u_short *csump;
40552958Sdr146992 	u_32_t sumd;
40562393Syz155240 	tcphdr_t *tcp;
40572393Syz155240 	ipnat_t *np;
40582393Syz155240 	int i;
40593448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
40602393Syz155240 
40617176Syx160601 	if (fin->fin_v == 6) {
40627176Syx160601 #ifdef	USE_INET6
40637176Syx160601 		return fr_nat6out(fin, nat, natadd, nflags);
40647176Syx160601 #else
40657176Syx160601 		return NULL;
40667176Syx160601 #endif
40677176Syx160601 	}
40687176Syx160601 
40692958Sdr146992 #if SOLARIS && defined(_KERNEL)
40707513SDarren.Reed@Sun.COM 	net_handle_t net_data_p = ifs->ifs_ipf_ipv4;
40712958Sdr146992 #endif
40722958Sdr146992 
40732393Syz155240 	tcp = NULL;
40742393Syz155240 	icmp = NULL;
40752393Syz155240 	csump = NULL;
40762393Syz155240 	np = nat->nat_ptr;
40772393Syz155240 
40784712Szf203873 	if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
40792393Syz155240 		(void) fr_nat_newfrag(fin, 0, nat);
40802958Sdr146992 
40812393Syz155240 	/*
40822393Syz155240 	 * Fix up checksums, not by recalculating them, but
40832393Syz155240 	 * simply computing adjustments.
40842393Syz155240 	 * This is only done for STREAMS based IP implementations where the
40852393Syz155240 	 * checksum has already been calculated by IP.  In all other cases,
40862393Syz155240 	 * IPFilter is called before the checksum needs calculating so there
40872393Syz155240 	 * is no call to modify whatever is in the header now.
40882393Syz155240 	 */
40892958Sdr146992 	ASSERT(fin->fin_m != NULL);
40902958Sdr146992 	if (fin->fin_v == 4 && !NET_IS_HCK_L3_FULL(net_data_p, fin->fin_m)) {
40912393Syz155240 		if (nflags == IPN_ICMPERR) {
40922958Sdr146992 			u_32_t s1, s2;
40932393Syz155240 
40942393Syz155240 			s1 = LONG_SUM(ntohl(fin->fin_saddr));
40952393Syz155240 			s2 = LONG_SUM(ntohl(nat->nat_outip.s_addr));
40962393Syz155240 			CALC_SUMD(s1, s2, sumd);
40972958Sdr146992 
40982958Sdr146992 			fix_outcksum(&fin->fin_ip->ip_sum, sumd);
40992393Syz155240 		}
41002393Syz155240 #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
41012393Syz155240     defined(linux) || defined(BRIDGE_IPF)
41022393Syz155240 		else {
41032393Syz155240 			/*
41042393Syz155240 			 * Strictly speaking, this isn't necessary on BSD
41052393Syz155240 			 * kernels because they do checksum calculation after
41062393Syz155240 			 * this code has run BUT if ipfilter is being used
41072393Syz155240 			 * to do NAT as a bridge, that code doesn't exist.
41082393Syz155240 			 */
41092393Syz155240 			if (nat->nat_dir == NAT_OUTBOUND)
41102958Sdr146992 				fix_outcksum(&fin->fin_ip->ip_sum,
41112958Sdr146992 					    nat->nat_ipsumd);
41122393Syz155240 			else
41132958Sdr146992 				fix_incksum(&fin->fin_ip->ip_sum,
41142958Sdr146992 				 	   nat->nat_ipsumd);
41152393Syz155240 		}
41162393Syz155240 #endif
41172393Syz155240 	}
41182393Syz155240 
41192393Syz155240 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
41202393Syz155240 		if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) {
41212393Syz155240 			tcp = fin->fin_dp;
41222393Syz155240 
41232393Syz155240 			tcp->th_sport = nat->nat_outport;
41242393Syz155240 			fin->fin_data[0] = ntohs(nat->nat_outport);
41252393Syz155240 		}
41262393Syz155240 
41272393Syz155240 		if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) {
41282393Syz155240 			icmp = fin->fin_dp;
41292393Syz155240 			icmp->icmp_id = nat->nat_outport;
41302393Syz155240 		}
41312393Syz155240 
41322393Syz155240 		csump = nat_proto(fin, nat, nflags);
41332393Syz155240 	}
41342393Syz155240 
41352393Syz155240 	fin->fin_ip->ip_src = nat->nat_outip;
41362393Syz155240 
41372393Syz155240 	/*
41382393Syz155240 	 * The above comments do not hold for layer 4 (or higher) checksums...
41392393Syz155240 	 */
41402958Sdr146992 	if (csump != NULL && !NET_IS_HCK_L4_FULL(net_data_p, fin->fin_m)) {
41412958Sdr146992 		if (nflags & IPN_TCPUDP &&
41422958Sdr146992 	   	    NET_IS_HCK_L4_PART(net_data_p, fin->fin_m))
41432958Sdr146992 			sumd = nat->nat_sumd[1];
41442958Sdr146992 		else
41452958Sdr146992 			sumd = nat->nat_sumd[0];
41462958Sdr146992 
41472393Syz155240 		if (nat->nat_dir == NAT_OUTBOUND)
41482958Sdr146992 			fix_outcksum(csump, sumd);
41492393Syz155240 		else
41502958Sdr146992 			fix_incksum(csump, sumd);
41512393Syz155240 	}
41522393Syz155240 #ifdef	IPFILTER_SYNC
41532393Syz155240 	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
41542393Syz155240 #endif
41552393Syz155240 	/* ------------------------------------------------------------- */
41562393Syz155240 	/* A few quick notes:						 */
41572393Syz155240 	/*	Following are test conditions prior to calling the 	 */
41582393Syz155240 	/*	appr_check routine.					 */
41592393Syz155240 	/*								 */
41602393Syz155240 	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
41612393Syz155240 	/*	with a redirect rule, we attempt to match the packet's	 */
41622393Syz155240 	/*	source port against in_dport, otherwise	we'd compare the */
41632393Syz155240 	/*	packet's destination.			 		 */
41642393Syz155240 	/* ------------------------------------------------------------- */
41652393Syz155240 	if ((np != NULL) && (np->in_apr != NULL)) {
41662393Syz155240 		i = appr_check(fin, nat);
41672393Syz155240 		if (i == 0)
41682393Syz155240 			i = 1;
41692393Syz155240 	} else
41702393Syz155240 		i = 1;
41717131Sdr146992 	ifs->ifs_nat_stats.ns_mapped[1]++;
41722393Syz155240 	fin->fin_flx |= FI_NATED;
41732393Syz155240 	return i;
41742393Syz155240 }
41752393Syz155240 
41762393Syz155240 
41772393Syz155240 /* ------------------------------------------------------------------------ */
41782393Syz155240 /* Function:    fr_checknatin                                               */
41792393Syz155240 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
41802393Syz155240 /*                     0 == no packet translation occurred,                 */
41812393Syz155240 /*                     1 == packet was successfully translated.             */
41822393Syz155240 /* Parameters:  fin(I)   - pointer to packet information                    */
41832393Syz155240 /*              passp(I) - pointer to filtering result flags                */
41842393Syz155240 /*                                                                          */
41852393Syz155240 /* Check to see if an incoming packet should be changed.  ICMP packets are  */
41862393Syz155240 /* first checked to see if they match an existing entry (if an error),      */
41872393Syz155240 /* otherwise a search of the current NAT table is made.  If neither results */
41882393Syz155240 /* in a match then a search for a matching NAT rule is made.  Create a new  */
41892393Syz155240 /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
41902393Syz155240 /* packet header(s) as required.                                            */
41912393Syz155240 /* ------------------------------------------------------------------------ */
fr_checknatin(fin,passp)41922393Syz155240 int fr_checknatin(fin, passp)
41932393Syz155240 fr_info_t *fin;
41942393Syz155240 u_32_t *passp;
41952393Syz155240 {
41962393Syz155240 	u_int nflags, natadd;
41977131Sdr146992 	ipnat_t *np, *npnext;
41982393Syz155240 	int rval, natfailed;
41992393Syz155240 	struct ifnet *ifp;
42002393Syz155240 	struct in_addr in;
42012393Syz155240 	icmphdr_t *icmp;
42022393Syz155240 	tcphdr_t *tcp;
42032393Syz155240 	u_short dport;
42042393Syz155240 	nat_t *nat;
42052393Syz155240 	u_32_t iph;
42063448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
42073448Sdh155122 
42087432SJohn.Ojemann@Sun.COM 	if (ifs->ifs_fr_nat_lock != 0)
42097432SJohn.Ojemann@Sun.COM 		return 0;
42107432SJohn.Ojemann@Sun.COM 	if (ifs->ifs_nat_stats.ns_rules == 0 && ifs->ifs_nat_instances == NULL)
42112393Syz155240 		return 0;
42122393Syz155240 
42132393Syz155240 	tcp = NULL;
42142393Syz155240 	icmp = NULL;
42152393Syz155240 	dport = 0;
42162393Syz155240 	natadd = 1;
42172393Syz155240 	nflags = 0;
42182393Syz155240 	natfailed = 0;
42192393Syz155240 	ifp = fin->fin_ifp;
42202393Syz155240 
42212393Syz155240 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
42222393Syz155240 		switch (fin->fin_p)
42232393Syz155240 		{
42242393Syz155240 		case IPPROTO_TCP :
42252393Syz155240 			nflags = IPN_TCP;
42262393Syz155240 			break;
42272393Syz155240 		case IPPROTO_UDP :
42282393Syz155240 			nflags = IPN_UDP;
42292393Syz155240 			break;
42302393Syz155240 		case IPPROTO_ICMP :
42312393Syz155240 			icmp = fin->fin_dp;
42322393Syz155240 
42332393Syz155240 			/*
42342393Syz155240 			 * This is an incoming packet, so the destination is
42352393Syz155240 			 * the icmp_id and the source port equals 0
42362393Syz155240 			 */
42372393Syz155240 			if (nat_icmpquerytype4(icmp->icmp_type)) {
42382393Syz155240 				nflags = IPN_ICMPQUERY;
42392393Syz155240 				dport = icmp->icmp_id;
42402393Syz155240 			} break;
42412393Syz155240 		default :
42422393Syz155240 			break;
42432393Syz155240 		}
42442393Syz155240 
42452393Syz155240 		if ((nflags & IPN_TCPUDP)) {
42462393Syz155240 			tcp = fin->fin_dp;
42472393Syz155240 			dport = tcp->th_dport;
42482393Syz155240 		}
42492393Syz155240 	}
42502393Syz155240 
42512393Syz155240 	in = fin->fin_dst;
42522393Syz155240 
42533448Sdh155122 	READ_ENTER(&ifs->ifs_ipf_nat);
42542393Syz155240 
42552393Syz155240 	if ((fin->fin_p == IPPROTO_ICMP) && !(nflags & IPN_ICMPQUERY) &&
42562393Syz155240 	    (nat = nat_icmperror(fin, &nflags, NAT_INBOUND)))
42572393Syz155240 		/*EMPTY*/;
42582393Syz155240 	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
42592393Syz155240 		natadd = 0;
42602393Syz155240 	else if ((nat = nat_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p,
42612393Syz155240 				     fin->fin_src, in))) {
42622393Syz155240 		nflags = nat->nat_flags;
42632393Syz155240 	} else {
42642393Syz155240 		u_32_t hv, msk, rmsk;
42652393Syz155240 
42667433SJohn.Ojemann@Sun.COM 		/*
42677433SJohn.Ojemann@Sun.COM 		 * There is no current entry in the nat table for this packet.
42687433SJohn.Ojemann@Sun.COM 		 *
42697433SJohn.Ojemann@Sun.COM 		 * If the packet is a fragment, but not the first fragment,
42707433SJohn.Ojemann@Sun.COM 		 * then don't do anything.  Otherwise, if there is a matching
42717433SJohn.Ojemann@Sun.COM 		 * nat rule, try to create a new nat entry.
42727433SJohn.Ojemann@Sun.COM 		 */
42737433SJohn.Ojemann@Sun.COM 		if ((fin->fin_off != 0) && (fin->fin_flx & FI_TCPUDP))
42747433SJohn.Ojemann@Sun.COM 			goto nonatfrag;
42757433SJohn.Ojemann@Sun.COM 
42763448Sdh155122 		rmsk = ifs->ifs_rdr_masks;
42772393Syz155240 		msk = 0xffffffff;
42782393Syz155240 maskloop:
42792393Syz155240 		iph = in.s_addr & htonl(msk);
42803448Sdh155122 		hv = NAT_HASH_FN(iph, 0, ifs->ifs_ipf_rdrrules_sz);
42817131Sdr146992 		for (np = ifs->ifs_rdr_rules[hv]; np; np = npnext) {
42827131Sdr146992 			npnext = np->in_rnext;
42832393Syz155240 			if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
42842393Syz155240 				continue;
42852393Syz155240 			if (np->in_v != fin->fin_v)
42862393Syz155240 				continue;
42872393Syz155240 			if (np->in_p && (np->in_p != fin->fin_p))
42882393Syz155240 				continue;
42892393Syz155240 			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
42902393Syz155240 				continue;
42912393Syz155240 			if (np->in_flags & IPN_FILTER) {
42922393Syz155240 				if (!nat_match(fin, np))
42932393Syz155240 					continue;
42942393Syz155240 			} else {
42952393Syz155240 				if ((in.s_addr & np->in_outmsk) != np->in_outip)
42962393Syz155240 					continue;
42972393Syz155240 				if (np->in_pmin &&
42982393Syz155240 				    ((ntohs(np->in_pmax) < ntohs(dport)) ||
42992393Syz155240 				     (ntohs(dport) < ntohs(np->in_pmin))))
43002393Syz155240 					continue;
43012393Syz155240 			}
43022393Syz155240 
43032393Syz155240 			if (*np->in_plabel != '\0') {
43042393Syz155240 				if (!appr_ok(fin, tcp, np)) {
43052393Syz155240 					continue;
43062393Syz155240 				}
43072393Syz155240 			}
43082393Syz155240 
43097131Sdr146992 			ATOMIC_INC32(np->in_use);
43107131Sdr146992 			RWLOCK_EXIT(&ifs->ifs_ipf_nat);
43117131Sdr146992 			WRITE_ENTER(&ifs->ifs_ipf_nat);
43122393Syz155240 			nat = nat_new(fin, np, NULL, nflags, NAT_INBOUND);
43132393Syz155240 			if (nat != NULL) {
43147131Sdr146992 				np->in_use--;
43152393Syz155240 				np->in_hits++;
43167131Sdr146992 				MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
43172393Syz155240 				break;
43187131Sdr146992 			}
43197131Sdr146992 			natfailed = -1;
43207131Sdr146992 			npnext = np->in_rnext;
43217131Sdr146992 			fr_ipnatderef(&np, ifs);
43227131Sdr146992 			MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
43232393Syz155240 		}
43242393Syz155240 
43252393Syz155240 		if ((np == NULL) && (rmsk != 0)) {
43262393Syz155240 			while (rmsk) {
43272393Syz155240 				msk <<= 1;
43282393Syz155240 				if (rmsk & 0x80000000)
43292393Syz155240 					break;
43302393Syz155240 				rmsk <<= 1;
43312393Syz155240 			}
43322393Syz155240 			if (rmsk != 0) {
43332393Syz155240 				rmsk <<= 1;
43342393Syz155240 				goto maskloop;
43352393Syz155240 			}
43362393Syz155240 		}
43372393Syz155240 	}
43387433SJohn.Ojemann@Sun.COM 
43397433SJohn.Ojemann@Sun.COM nonatfrag:
43402393Syz155240 	if (nat != NULL) {
43412393Syz155240 		rval = fr_natin(fin, nat, natadd, nflags);
43422393Syz155240 		if (rval == 1) {
43432393Syz155240 			MUTEX_ENTER(&nat->nat_lock);
43447333SJohn.Ojemann@Sun.COM 			nat_update(fin, nat, nat->nat_ptr);
43457333SJohn.Ojemann@Sun.COM 			nat->nat_bytes[0] += fin->fin_plen;
43467333SJohn.Ojemann@Sun.COM 			nat->nat_pkts[0]++;
43478624SDarren.Reed@Sun.COM 			fin->fin_pktnum = nat->nat_pkts[0];
43482393Syz155240 			MUTEX_EXIT(&nat->nat_lock);
43492393Syz155240 		}
43502393Syz155240 	} else
43512393Syz155240 		rval = natfailed;
43523448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
43532393Syz155240 
43542393Syz155240 	if (rval == -1) {
43552393Syz155240 		if (passp != NULL)
43562393Syz155240 			*passp = FR_BLOCK;
43572393Syz155240 		fin->fin_flx |= FI_BADNAT;
43582393Syz155240 	}
43592393Syz155240 	return rval;
43602393Syz155240 }
43612393Syz155240 
43622393Syz155240 
43632393Syz155240 /* ------------------------------------------------------------------------ */
43642393Syz155240 /* Function:    fr_natin                                                    */
43652393Syz155240 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
43662393Syz155240 /*                     1 == packet was successfully translated.             */
43672393Syz155240 /* Parameters:  fin(I)    - pointer to packet information                   */
43682393Syz155240 /*              nat(I)    - pointer to NAT structure                        */
43692393Syz155240 /*              natadd(I) - flag indicating if it is safe to add frag cache */
43702393Syz155240 /*              nflags(I) - NAT flags set for this packet                   */
43712393Syz155240 /* Locks Held:  ipf_nat (READ)                                              */
43722393Syz155240 /*                                                                          */
43732393Syz155240 /* Translate a packet coming "in" on an interface.                          */
43742393Syz155240 /* ------------------------------------------------------------------------ */
fr_natin(fin,nat,natadd,nflags)43752393Syz155240 int fr_natin(fin, nat, natadd, nflags)
43762393Syz155240 fr_info_t *fin;
43772393Syz155240 nat_t *nat;
43782393Syz155240 int natadd;
43792393Syz155240 u_32_t nflags;
43802393Syz155240 {
43812393Syz155240 	icmphdr_t *icmp;
43825274Syx160601 	u_short *csump;
43832393Syz155240 	tcphdr_t *tcp;
43842393Syz155240 	ipnat_t *np;
43852393Syz155240 	int i;
43863448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
43872393Syz155240 
43887176Syx160601 	if (fin->fin_v == 6) {
43897176Syx160601 #ifdef	USE_INET6
43907176Syx160601 		return fr_nat6in(fin, nat, natadd, nflags);
43917176Syx160601 #else
43927176Syx160601 		return NULL;
43937176Syx160601 #endif
43947176Syx160601 	}
43957176Syx160601 
43962958Sdr146992 #if SOLARIS && defined(_KERNEL)
43977513SDarren.Reed@Sun.COM 	net_handle_t net_data_p = ifs->ifs_ipf_ipv4;
43982958Sdr146992 #endif
43992958Sdr146992 
44002393Syz155240 	tcp = NULL;
44012393Syz155240 	csump = NULL;
44022393Syz155240 	np = nat->nat_ptr;
44032393Syz155240 	fin->fin_fr = nat->nat_fr;
44042393Syz155240 
44054712Szf203873 	if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
44064712Szf203873 		(void) fr_nat_newfrag(fin, 0, nat);
44074712Szf203873 
44082393Syz155240 	if (np != NULL) {
44092393Syz155240 
44102393Syz155240 	/* ------------------------------------------------------------- */
44112393Syz155240 	/* A few quick notes:						 */
44122393Syz155240 	/*	Following are test conditions prior to calling the 	 */
44132393Syz155240 	/*	appr_check routine.					 */
44142393Syz155240 	/*								 */
44152393Syz155240 	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
44162393Syz155240 	/*	with a map rule, we attempt to match the packet's	 */
44172393Syz155240 	/*	source port against in_dport, otherwise	we'd compare the */
44182393Syz155240 	/*	packet's destination.			 		 */
44192393Syz155240 	/* ------------------------------------------------------------- */
44202393Syz155240 		if (np->in_apr != NULL) {
44212393Syz155240 			i = appr_check(fin, nat);
44222393Syz155240 			if (i == -1) {
44232393Syz155240 				return -1;
44242393Syz155240 			}
44252393Syz155240 		}
44262393Syz155240 	}
44272393Syz155240 
44282393Syz155240 #ifdef	IPFILTER_SYNC
44292393Syz155240 	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
44302393Syz155240 #endif
44312393Syz155240 
44322393Syz155240 	fin->fin_ip->ip_dst = nat->nat_inip;
44332393Syz155240 	fin->fin_fi.fi_daddr = nat->nat_inip.s_addr;
44342393Syz155240 	if (nflags & IPN_TCPUDP)
44352393Syz155240 		tcp = fin->fin_dp;
44362393Syz155240 
44372393Syz155240 	/*
44382393Syz155240 	 * Fix up checksums, not by recalculating them, but
44392393Syz155240 	 * simply computing adjustments.
44402393Syz155240 	 * Why only do this for some platforms on inbound packets ?
44412393Syz155240 	 * Because for those that it is done, IP processing is yet to happen
44422393Syz155240 	 * and so the IPv4 header checksum has not yet been evaluated.
44432393Syz155240 	 * Perhaps it should always be done for the benefit of things like
44442393Syz155240 	 * fast forwarding (so that it doesn't need to be recomputed) but with
44452393Syz155240 	 * header checksum offloading, perhaps it is a moot point.
44462393Syz155240 	 */
44472393Syz155240 #if !defined(_KERNEL) || defined(MENTAT) || defined(__sgi) || \
44482393Syz155240      defined(__osf__) || defined(linux)
44492393Syz155240 	if (nat->nat_dir == NAT_OUTBOUND)
44502958Sdr146992 		fix_incksum(&fin->fin_ip->ip_sum, nat->nat_ipsumd);
44512393Syz155240 	else
44522958Sdr146992 		fix_outcksum(&fin->fin_ip->ip_sum, nat->nat_ipsumd);
44532393Syz155240 #endif
44542393Syz155240 
44552393Syz155240 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
44562393Syz155240 		if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) {
44572393Syz155240 			tcp->th_dport = nat->nat_inport;
44582393Syz155240 			fin->fin_data[1] = ntohs(nat->nat_inport);
44592393Syz155240 		}
44602393Syz155240 
44612393Syz155240 
44622393Syz155240 		if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) {
44632393Syz155240 			icmp = fin->fin_dp;
44642393Syz155240 
44652393Syz155240 			icmp->icmp_id = nat->nat_inport;
44662393Syz155240 		}
44672393Syz155240 
44682393Syz155240 		csump = nat_proto(fin, nat, nflags);
44692393Syz155240 	}
44702393Syz155240 
44715274Syx160601 	/*
44725274Syx160601 	 * In case they are being forwarded, inbound packets always need to have
44735274Syx160601 	 * their checksum adjusted even if hardware checksum validation said OK.
44745274Syx160601 	 */
44755274Syx160601 	if (csump != NULL) {
44765274Syx160601 		if (nat->nat_dir == NAT_OUTBOUND)
44775274Syx160601 			fix_incksum(csump, nat->nat_sumd[0]);
44785274Syx160601 		else
44795274Syx160601 			fix_outcksum(csump, nat->nat_sumd[0]);
44805274Syx160601 	}
44815274Syx160601 
44822958Sdr146992 #if SOLARIS && defined(_KERNEL)
44832958Sdr146992 	if (nflags & IPN_TCPUDP &&
44842958Sdr146992 	    NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) {
44855274Syx160601 		/*
44865274Syx160601 		 * Need to adjust the partial checksum result stored in
44875274Syx160601 		 * db_cksum16, which will be used for validation in IP.
44885274Syx160601 		 * See IP_CKSUM_RECV().
44895274Syx160601 		 * Adjustment data should be the inverse of the IP address
44905274Syx160601 		 * changes, because db_cksum16 is supposed to be the complement
44915274Syx160601 		 * of the pesudo header.
44925274Syx160601 		 */
44935274Syx160601 		csump = &fin->fin_m->b_datap->db_cksum16;
44945274Syx160601 		if (nat->nat_dir == NAT_OUTBOUND)
44955274Syx160601 			fix_outcksum(csump, nat->nat_sumd[1]);
44965274Syx160601 		else
44975274Syx160601 			fix_incksum(csump, nat->nat_sumd[1]);
44985274Syx160601 	}
44992958Sdr146992 #endif
45005274Syx160601 
45017131Sdr146992 	ifs->ifs_nat_stats.ns_mapped[0]++;
45022393Syz155240 	fin->fin_flx |= FI_NATED;
45032393Syz155240 	if (np != NULL && np->in_tag.ipt_num[0] != 0)
45042393Syz155240 		fin->fin_nattag = &np->in_tag;
45052393Syz155240 	return 1;
45062393Syz155240 }
45072393Syz155240 
45082393Syz155240 
45092393Syz155240 /* ------------------------------------------------------------------------ */
45102393Syz155240 /* Function:    nat_proto                                                   */
45112393Syz155240 /* Returns:     u_short* - pointer to transport header checksum to update,  */
45122393Syz155240 /*                         NULL if the transport protocol is not recognised */
45132393Syz155240 /*                         as needing a checksum update.                    */
45142393Syz155240 /* Parameters:  fin(I)    - pointer to packet information                   */
45152393Syz155240 /*              nat(I)    - pointer to NAT structure                        */
45162393Syz155240 /*              nflags(I) - NAT flags set for this packet                   */
45172393Syz155240 /*                                                                          */
45182393Syz155240 /* Return the pointer to the checksum field for each protocol so understood.*/
45192393Syz155240 /* If support for making other changes to a protocol header is required,    */
45202393Syz155240 /* that is not strictly 'address' translation, such as clamping the MSS in  */
45212393Syz155240 /* TCP down to a specific value, then do it from here.                      */
45222393Syz155240 /* ------------------------------------------------------------------------ */
nat_proto(fin,nat,nflags)45232393Syz155240 u_short *nat_proto(fin, nat, nflags)
45242393Syz155240 fr_info_t *fin;
45252393Syz155240 nat_t *nat;
45262393Syz155240 u_int nflags;
45272393Syz155240 {
45282393Syz155240 	icmphdr_t *icmp;
45297176Syx160601 	struct icmp6_hdr *icmp6;
45302393Syz155240 	u_short *csump;
45312393Syz155240 	tcphdr_t *tcp;
45322393Syz155240 	udphdr_t *udp;
45332393Syz155240 
45342393Syz155240 	csump = NULL;
45352393Syz155240 	if (fin->fin_out == 0) {
45362393Syz155240 		fin->fin_rev = (nat->nat_dir == NAT_OUTBOUND);
45372393Syz155240 	} else {
45382393Syz155240 		fin->fin_rev = (nat->nat_dir == NAT_INBOUND);
45392393Syz155240 	}
45402393Syz155240 
45412393Syz155240 	switch (fin->fin_p)
45422393Syz155240 	{
45432393Syz155240 	case IPPROTO_TCP :
45442393Syz155240 		tcp = fin->fin_dp;
45452393Syz155240 
45462393Syz155240 		csump = &tcp->th_sum;
45472393Syz155240 
45482393Syz155240 		/*
45492393Syz155240 		 * Do a MSS CLAMPING on a SYN packet,
45502393Syz155240 		 * only deal IPv4 for now.
45512393Syz155240 		 */
45522393Syz155240 		if ((nat->nat_mssclamp != 0) && (tcp->th_flags & TH_SYN) != 0)
45532958Sdr146992 			nat_mssclamp(tcp, nat->nat_mssclamp, csump);
45542393Syz155240 
45552393Syz155240 		break;
45562393Syz155240 
45572393Syz155240 	case IPPROTO_UDP :
45582393Syz155240 		udp = fin->fin_dp;
45592393Syz155240 
45602393Syz155240 		if (udp->uh_sum)
45612393Syz155240 			csump = &udp->uh_sum;
45622393Syz155240 		break;
45632393Syz155240 
45642393Syz155240 	case IPPROTO_ICMP :
45652393Syz155240 		icmp = fin->fin_dp;
45662393Syz155240 
45672393Syz155240 		if ((nflags & IPN_ICMPQUERY) != 0) {
45682393Syz155240 			if (icmp->icmp_cksum != 0)
45692393Syz155240 				csump = &icmp->icmp_cksum;
45702393Syz155240 		}
45712393Syz155240 		break;
45727176Syx160601 
45737176Syx160601 	case IPPROTO_ICMPV6 :
45747176Syx160601 		icmp6 = fin->fin_dp;
45757176Syx160601 
45767176Syx160601 		if ((nflags & IPN_ICMPQUERY) != 0) {
45777176Syx160601 			if (icmp6->icmp6_cksum != 0)
45787176Syx160601 				csump = &icmp6->icmp6_cksum;
45797176Syx160601 		}
45807176Syx160601 		break;
45812393Syz155240 	}
45822393Syz155240 	return csump;
45832393Syz155240 }
45842393Syz155240 
45852393Syz155240 
45862393Syz155240 /* ------------------------------------------------------------------------ */
45872393Syz155240 /* Function:    fr_natunload                                                */
45882393Syz155240 /* Returns:     Nil                                                         */
45898170SJohn.Ojemann@Sun.COM /* Parameters:  ifs - ipf stack instance                                  */
45902393Syz155240 /*                                                                          */
45912393Syz155240 /* Free all memory used by NAT structures allocated at runtime.             */
45922393Syz155240 /* ------------------------------------------------------------------------ */
fr_natunload(ifs)45933448Sdh155122 void fr_natunload(ifs)
45943448Sdh155122 ipf_stack_t *ifs;
45952393Syz155240 {
45962393Syz155240 	ipftq_t *ifq, *ifqnext;
45972393Syz155240 
45983448Sdh155122 	(void) nat_clearlist(ifs);
45998170SJohn.Ojemann@Sun.COM 	(void) nat_flushtable(FLUSH_TABLE_ALL, ifs);
46002393Syz155240 
46012393Syz155240 	/*
46022393Syz155240 	 * Proxy timeout queues are not cleaned here because although they
46032393Syz155240 	 * exist on the NAT list, appr_unload is called after fr_natunload
46042393Syz155240 	 * and the proxies actually are responsible for them being created.
46052393Syz155240 	 * Should the proxy timeouts have their own list?  There's no real
46062393Syz155240 	 * justification as this is the only complication.
46072393Syz155240 	 */
46083448Sdh155122 	for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) {
46092393Syz155240 		ifqnext = ifq->ifq_next;
46102393Syz155240 		if (((ifq->ifq_flags & IFQF_PROXY) == 0) &&
46112393Syz155240 		    (fr_deletetimeoutqueue(ifq) == 0))
46123448Sdh155122 			fr_freetimeoutqueue(ifq, ifs);
46132393Syz155240 	}
46142393Syz155240 
46153448Sdh155122 	if (ifs->ifs_nat_table[0] != NULL) {
46163448Sdh155122 		KFREES(ifs->ifs_nat_table[0],
46173448Sdh155122 		       sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz);
46183448Sdh155122 		ifs->ifs_nat_table[0] = NULL;
46192393Syz155240 	}
46203448Sdh155122 	if (ifs->ifs_nat_table[1] != NULL) {
46213448Sdh155122 		KFREES(ifs->ifs_nat_table[1],
46223448Sdh155122 		       sizeof(nat_t *) * ifs->ifs_ipf_nattable_sz);
46233448Sdh155122 		ifs->ifs_nat_table[1] = NULL;
46242393Syz155240 	}
46253448Sdh155122 	if (ifs->ifs_nat_rules != NULL) {
46263448Sdh155122 		KFREES(ifs->ifs_nat_rules,
46273448Sdh155122 		       sizeof(ipnat_t *) * ifs->ifs_ipf_natrules_sz);
46283448Sdh155122 		ifs->ifs_nat_rules = NULL;
46292393Syz155240 	}
46303448Sdh155122 	if (ifs->ifs_rdr_rules != NULL) {
46313448Sdh155122 		KFREES(ifs->ifs_rdr_rules,
46323448Sdh155122 		       sizeof(ipnat_t *) * ifs->ifs_ipf_rdrrules_sz);
46333448Sdh155122 		ifs->ifs_rdr_rules = NULL;
46342393Syz155240 	}
46353448Sdh155122 	if (ifs->ifs_maptable != NULL) {
46363448Sdh155122 		KFREES(ifs->ifs_maptable,
46373448Sdh155122 		       sizeof(hostmap_t *) * ifs->ifs_ipf_hostmap_sz);
46383448Sdh155122 		ifs->ifs_maptable = NULL;
46392393Syz155240 	}
46403448Sdh155122 	if (ifs->ifs_nat_stats.ns_bucketlen[0] != NULL) {
46413448Sdh155122 		KFREES(ifs->ifs_nat_stats.ns_bucketlen[0],
46423448Sdh155122 		       sizeof(u_long *) * ifs->ifs_ipf_nattable_sz);
46433448Sdh155122 		ifs->ifs_nat_stats.ns_bucketlen[0] = NULL;
46442393Syz155240 	}
46453448Sdh155122 	if (ifs->ifs_nat_stats.ns_bucketlen[1] != NULL) {
46463448Sdh155122 		KFREES(ifs->ifs_nat_stats.ns_bucketlen[1],
46473448Sdh155122 		       sizeof(u_long *) * ifs->ifs_ipf_nattable_sz);
46483448Sdh155122 		ifs->ifs_nat_stats.ns_bucketlen[1] = NULL;
46492393Syz155240 	}
46502393Syz155240 
46513448Sdh155122 	if (ifs->ifs_fr_nat_maxbucket_reset == 1)
46523448Sdh155122 		ifs->ifs_fr_nat_maxbucket = 0;
46533448Sdh155122 
46543448Sdh155122 	if (ifs->ifs_fr_nat_init == 1) {
46553448Sdh155122 		ifs->ifs_fr_nat_init = 0;
46563448Sdh155122 		fr_sttab_destroy(ifs->ifs_nat_tqb);
46573448Sdh155122 
46583448Sdh155122 		RW_DESTROY(&ifs->ifs_ipf_natfrag);
46593448Sdh155122 		RW_DESTROY(&ifs->ifs_ipf_nat);
46603448Sdh155122 
46613448Sdh155122 		MUTEX_DESTROY(&ifs->ifs_ipf_nat_new);
46623448Sdh155122 		MUTEX_DESTROY(&ifs->ifs_ipf_natio);
46633448Sdh155122 
46643448Sdh155122 		MUTEX_DESTROY(&ifs->ifs_nat_udptq.ifq_lock);
46653448Sdh155122 		MUTEX_DESTROY(&ifs->ifs_nat_icmptq.ifq_lock);
46663448Sdh155122 		MUTEX_DESTROY(&ifs->ifs_nat_iptq.ifq_lock);
46672393Syz155240 	}
46682393Syz155240 }
46692393Syz155240 
46702393Syz155240 
46712393Syz155240 /* ------------------------------------------------------------------------ */
46722393Syz155240 /* Function:    fr_natexpire                                                */
46732393Syz155240 /* Returns:     Nil                                                         */
46748170SJohn.Ojemann@Sun.COM /* Parameters:  ifs - ipf stack instance                                    */
46752393Syz155240 /*                                                                          */
46762393Syz155240 /* Check all of the timeout queues for entries at the top which need to be  */
46772393Syz155240 /* expired.                                                                 */
46782393Syz155240 /* ------------------------------------------------------------------------ */
fr_natexpire(ifs)46793448Sdh155122 void fr_natexpire(ifs)
46803448Sdh155122 ipf_stack_t *ifs;
46812393Syz155240 {
46822393Syz155240 	ipftq_t *ifq, *ifqnext;
46832393Syz155240 	ipftqent_t *tqe, *tqn;
46842393Syz155240 	int i;
46852393Syz155240 	SPL_INT(s);
46862393Syz155240 
46872393Syz155240 	SPL_NET(s);
46883448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
46893448Sdh155122 	for (ifq = ifs->ifs_nat_tqb, i = 0; ifq != NULL; ifq = ifq->ifq_next) {
46902393Syz155240 		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
46913448Sdh155122 			if (tqe->tqe_die > ifs->ifs_fr_ticks)
46922393Syz155240 				break;
46932393Syz155240 			tqn = tqe->tqe_next;
46948170SJohn.Ojemann@Sun.COM 			(void) nat_delete(tqe->tqe_parent, NL_EXPIRE, ifs);
46952393Syz155240 		}
46962393Syz155240 	}
46972393Syz155240 
46983448Sdh155122 	for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) {
46992393Syz155240 		ifqnext = ifq->ifq_next;
47002393Syz155240 
47012393Syz155240 		for (tqn = ifq->ifq_head; ((tqe = tqn) != NULL); i++) {
47023448Sdh155122 			if (tqe->tqe_die > ifs->ifs_fr_ticks)
47032393Syz155240 				break;
47042393Syz155240 			tqn = tqe->tqe_next;
47058170SJohn.Ojemann@Sun.COM 			(void) nat_delete(tqe->tqe_parent, NL_EXPIRE, ifs);
47062393Syz155240 		}
47072393Syz155240 	}
47082393Syz155240 
47093448Sdh155122 	for (ifq = ifs->ifs_nat_utqe; ifq != NULL; ifq = ifqnext) {
47102393Syz155240 		ifqnext = ifq->ifq_next;
47112393Syz155240 
47122393Syz155240 		if (((ifq->ifq_flags & IFQF_DELETE) != 0) &&
47132393Syz155240 		    (ifq->ifq_ref == 0)) {
47143448Sdh155122 			fr_freetimeoutqueue(ifq, ifs);
47152393Syz155240 		}
47162393Syz155240 	}
47172393Syz155240 
47184817San207044 	if (ifs->ifs_nat_doflush != 0) {
47198170SJohn.Ojemann@Sun.COM 		(void) nat_flushtable(FLUSH_TABLE_EXTRA, ifs);
47204817San207044 		ifs->ifs_nat_doflush = 0;
47214817San207044 	}
47224817San207044 
47233448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
47242393Syz155240 	SPL_X(s);
47252393Syz155240 }
47262393Syz155240 
47272393Syz155240 
47282393Syz155240 /* ------------------------------------------------------------------------ */
47292958Sdr146992 /* Function:    fr_nataddrsync                                              */
47302393Syz155240 /* Returns:     Nil                                                         */
47312958Sdr146992 /* Parameters:  ifp(I) -  pointer to network interface                      */
47322958Sdr146992 /*              addr(I) - pointer to new network address                    */
47332393Syz155240 /*                                                                          */
47342393Syz155240 /* Walk through all of the currently active NAT sessions, looking for those */
47352958Sdr146992 /* which need to have their translated address updated (where the interface */
47362958Sdr146992 /* matches the one passed in) and change it, recalculating the checksum sum */
47372958Sdr146992 /* difference too.                                                          */
47382393Syz155240 /* ------------------------------------------------------------------------ */
fr_nataddrsync(v,ifp,addr,ifs)47397176Syx160601 void fr_nataddrsync(v, ifp, addr, ifs)
47407176Syx160601 int v;
47412393Syz155240 void *ifp;
47427176Syx160601 void *addr;
47433448Sdh155122 ipf_stack_t *ifs;
47442393Syz155240 {
47452393Syz155240 	u_32_t sum1, sum2, sumd;
47462393Syz155240 	nat_t *nat;
47472958Sdr146992 	ipnat_t *np;
47482393Syz155240 	SPL_INT(s);
47492393Syz155240 
47503448Sdh155122 	if (ifs->ifs_fr_running <= 0)
47512393Syz155240 		return;
47522393Syz155240 
47532393Syz155240 	SPL_NET(s);
47543448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
47553448Sdh155122 
47563448Sdh155122 	if (ifs->ifs_fr_running <= 0) {
47573448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
47582393Syz155240 		return;
47592393Syz155240 	}
47602393Syz155240 
47612958Sdr146992 	/*
47622958Sdr146992 	 * Change IP addresses for NAT sessions for any protocol except TCP
47632958Sdr146992 	 * since it will break the TCP connection anyway.  The only rules
47642958Sdr146992 	 * which will get changed are those which are "map ... -> 0/32",
47652958Sdr146992 	 * where the rule specifies the address is taken from the interface.
47662958Sdr146992 	 */
47673448Sdh155122 	for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) {
47682958Sdr146992 		if (addr != NULL) {
47692958Sdr146992 			if (((ifp != NULL) && ifp != (nat->nat_ifps[0])) ||
47702958Sdr146992 			    ((nat->nat_flags & IPN_TCP) != 0))
47712958Sdr146992 				continue;
47727176Syx160601 			if ((np = nat->nat_ptr) == NULL)
47732393Syz155240 				continue;
47747176Syx160601 			if (v == 4 && np->in_v == 4) {
47757176Syx160601 				if (np->in_nip || np->in_outmsk != 0xffffffff)
47767176Syx160601 					continue;
47777176Syx160601 				/*
47787176Syx160601 				 * Change the map-to address to be the same as
47797176Syx160601 				 * the new one.
47807176Syx160601 				 */
47817176Syx160601 				sum1 = nat->nat_outip.s_addr;
47827176Syx160601 				nat->nat_outip = *(struct in_addr *)addr;
47837176Syx160601 				sum2 = nat->nat_outip.s_addr;
47847176Syx160601 			} else if (v == 6 && np->in_v == 6) {
47857176Syx160601 				if (!IP6_ISZERO(&np->in_next6.in6) ||
47867176Syx160601 				    !IP6_ISONES(&np->in_out[1].in6))
47877176Syx160601 					continue;
47887176Syx160601 				/*
47897176Syx160601 				 * Change the map-to address to be the same as
47907176Syx160601 				 * the new one.
47917176Syx160601 				 */
47927176Syx160601 				nat->nat_outip6.in6 = *(struct in6_addr *)addr;
47937176Syx160601 			} else
47947176Syx160601 				continue;
47952958Sdr146992 
47962958Sdr146992 		} else if (((ifp == NULL) || (ifp == nat->nat_ifps[0])) &&
47977176Syx160601 		    !(nat->nat_flags & IPN_TCP) && (np = nat->nat_ptr)) {
47987176Syx160601 			if (np->in_v == 4 && (v == 4 || v == 0)) {
47997176Syx160601 				struct in_addr in;
48007176Syx160601 				if (np->in_outmsk != 0xffffffff || np->in_nip)
48017176Syx160601 					continue;
48027176Syx160601 				/*
48037176Syx160601 				 * Change the map-to address to be the same as
48047176Syx160601 				 * the new one.
48057176Syx160601 				 */
48067176Syx160601 				sum1 = nat->nat_outip.s_addr;
48077176Syx160601 				if (fr_ifpaddr(4, FRI_NORMAL, nat->nat_ifps[0],
48087176Syx160601 					       &in, NULL, ifs) != -1)
48097176Syx160601 					nat->nat_outip = in;
48107176Syx160601 				sum2 = nat->nat_outip.s_addr;
48117176Syx160601 			} else if (np->in_v == 6 && (v == 6 || v == 0)) {
48127176Syx160601 				struct in6_addr in6;
48137176Syx160601 				if (!IP6_ISZERO(&np->in_next6.in6) ||
48147176Syx160601 				    !IP6_ISONES(&np->in_out[1].in6))
48157176Syx160601 					continue;
48167176Syx160601 				/*
48177176Syx160601 				 * Change the map-to address to be the same as
48187176Syx160601 				 * the new one.
48197176Syx160601 				 */
48207176Syx160601 				if (fr_ifpaddr(6, FRI_NORMAL, nat->nat_ifps[0],
48217176Syx160601 					       (void *)&in6, NULL, ifs) != -1)
48227176Syx160601 					nat->nat_outip6.in6 = in6;
48237176Syx160601 			} else
48247176Syx160601 				continue;
48252958Sdr146992 		} else {
48262958Sdr146992 			continue;
48272393Syz155240 		}
48282958Sdr146992 
48292958Sdr146992 		if (sum1 == sum2)
48302958Sdr146992 			continue;
48312958Sdr146992 		/*
48322958Sdr146992 		 * Readjust the checksum adjustment to take into
48332958Sdr146992 		 * account the new IP#.
48342958Sdr146992 		 */
48352958Sdr146992 		CALC_SUMD(sum1, sum2, sumd);
48362958Sdr146992 		/* XXX - dont change for TCP when solaris does
48372958Sdr146992 		 * hardware checksumming.
48382958Sdr146992 		 */
48392958Sdr146992 		sumd += nat->nat_sumd[0];
48402958Sdr146992 		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
48412958Sdr146992 		nat->nat_sumd[1] = nat->nat_sumd[0];
48422393Syz155240 	}
48432393Syz155240 
48443448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
48452958Sdr146992 	SPL_X(s);
48462958Sdr146992 }
48472958Sdr146992 
48482958Sdr146992 
48492958Sdr146992 /* ------------------------------------------------------------------------ */
48502958Sdr146992 /* Function:    fr_natifpsync                                               */
48512958Sdr146992 /* Returns:     Nil                                                         */
48522958Sdr146992 /* Parameters:  action(I) - how we are syncing                              */
48532958Sdr146992 /*              ifp(I)    - pointer to network interface                    */
48542958Sdr146992 /*              name(I)   - name of interface to sync to                    */
48552958Sdr146992 /*                                                                          */
48562958Sdr146992 /* This function is used to resync the mapping of interface names and their */
48572958Sdr146992 /* respective 'pointers'.  For "action == IPFSYNC_RESYNC", resync all       */
48582958Sdr146992 /* interfaces by doing a new lookup of name to 'pointer'.  For "action ==   */
48592958Sdr146992 /* IPFSYNC_NEWIFP", treat ifp as the new pointer value associated with      */
48602958Sdr146992 /* "name" and for "action == IPFSYNC_OLDIFP", ifp is a pointer for which    */
48612958Sdr146992 /* there is no longer any interface associated with it.                     */
48622958Sdr146992 /* ------------------------------------------------------------------------ */
fr_natifpsync(action,v,ifp,name,ifs)48637176Syx160601 void fr_natifpsync(action, v, ifp, name, ifs)
48647176Syx160601 int action, v;
48652958Sdr146992 void *ifp;
48662958Sdr146992 char *name;
48673448Sdh155122 ipf_stack_t *ifs;
48682958Sdr146992 {
48692958Sdr146992 #if defined(_KERNEL) && !defined(MENTAT) && defined(USE_SPL)
48702958Sdr146992 	int s;
48712958Sdr146992 #endif
48722958Sdr146992 	nat_t *nat;
48732958Sdr146992 	ipnat_t *n;
48747176Syx160601 	int nv;
48752958Sdr146992 
48763448Sdh155122 	if (ifs->ifs_fr_running <= 0)
48772958Sdr146992 		return;
48782958Sdr146992 
48792958Sdr146992 	SPL_NET(s);
48803448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
48813448Sdh155122 
48823448Sdh155122 	if (ifs->ifs_fr_running <= 0) {
48833448Sdh155122 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
48842958Sdr146992 		return;
48852958Sdr146992 	}
48862958Sdr146992 
48872958Sdr146992 	switch (action)
48882958Sdr146992 	{
48892958Sdr146992 	case IPFSYNC_RESYNC :
48903448Sdh155122 		for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) {
48917176Syx160601 			nv = (v == 0) ? nat->nat_v : v;
48927176Syx160601 			if (nat->nat_v != nv)
48937176Syx160601 				continue;
48942958Sdr146992 			if ((ifp == nat->nat_ifps[0]) ||
48952958Sdr146992 			    (nat->nat_ifps[0] == (void *)-1)) {
48962958Sdr146992 				nat->nat_ifps[0] =
48977176Syx160601 				    fr_resolvenic(nat->nat_ifnames[0], nv, ifs);
48982958Sdr146992 			}
48992958Sdr146992 
49002958Sdr146992 			if ((ifp == nat->nat_ifps[1]) ||
49012958Sdr146992 			    (nat->nat_ifps[1] == (void *)-1)) {
49022958Sdr146992 				nat->nat_ifps[1] =
49037176Syx160601 				    fr_resolvenic(nat->nat_ifnames[1], nv, ifs);
49042958Sdr146992 			}
49052958Sdr146992 		}
49062958Sdr146992 
49073448Sdh155122 		for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) {
49087176Syx160601 			nv = (v == 0) ? (int)n->in_v : v;
49097176Syx160601 			if ((int)n->in_v != nv)
49107176Syx160601 				continue;
49112958Sdr146992 			if (n->in_ifps[0] == ifp ||
49122958Sdr146992 			    n->in_ifps[0] == (void *)-1) {
49132958Sdr146992 				n->in_ifps[0] =
49147176Syx160601 				    fr_resolvenic(n->in_ifnames[0], nv, ifs);
49152958Sdr146992 			}
49162958Sdr146992 			if (n->in_ifps[1] == ifp ||
49172958Sdr146992 			    n->in_ifps[1] == (void *)-1) {
49182958Sdr146992 				n->in_ifps[1] =
49197176Syx160601 				    fr_resolvenic(n->in_ifnames[1], nv, ifs);
49202958Sdr146992 			}
49212958Sdr146992 		}
49222958Sdr146992 		break;
49232958Sdr146992 	case IPFSYNC_NEWIFP :
49243448Sdh155122 		for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) {
49257176Syx160601 			if (nat->nat_v != v)
49267176Syx160601 				continue;
49272958Sdr146992 			if (!strncmp(name, nat->nat_ifnames[0],
49282958Sdr146992 				     sizeof(nat->nat_ifnames[0])))
49292958Sdr146992 				nat->nat_ifps[0] = ifp;
49302958Sdr146992 			if (!strncmp(name, nat->nat_ifnames[1],
49312958Sdr146992 				     sizeof(nat->nat_ifnames[1])))
49322958Sdr146992 				nat->nat_ifps[1] = ifp;
49332958Sdr146992 		}
49343448Sdh155122 		for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) {
49357176Syx160601 			if ((int)n->in_v != v)
49367176Syx160601 				continue;
49372958Sdr146992 			if (!strncmp(name, n->in_ifnames[0],
49382958Sdr146992 				     sizeof(n->in_ifnames[0])))
49392958Sdr146992 				n->in_ifps[0] = ifp;
49402958Sdr146992 			if (!strncmp(name, n->in_ifnames[1],
49412958Sdr146992 				     sizeof(n->in_ifnames[1])))
49422958Sdr146992 				n->in_ifps[1] = ifp;
49432958Sdr146992 		}
49442958Sdr146992 		break;
49452958Sdr146992 	case IPFSYNC_OLDIFP :
49463448Sdh155122 		for (nat = ifs->ifs_nat_instances; nat; nat = nat->nat_next) {
49477176Syx160601 			if (nat->nat_v != v)
49487176Syx160601 				continue;
49492958Sdr146992 			if (ifp == nat->nat_ifps[0])
49502958Sdr146992 				nat->nat_ifps[0] = (void *)-1;
49512958Sdr146992 			if (ifp == nat->nat_ifps[1])
49522958Sdr146992 				nat->nat_ifps[1] = (void *)-1;
49532958Sdr146992 		}
49543448Sdh155122 		for (n = ifs->ifs_nat_list; (n != NULL); n = n->in_next) {
49557176Syx160601 			if ((int)n->in_v != v)
49567176Syx160601 				continue;
49572958Sdr146992 			if (n->in_ifps[0] == ifp)
49582958Sdr146992 				n->in_ifps[0] = (void *)-1;
49592958Sdr146992 			if (n->in_ifps[1] == ifp)
49602958Sdr146992 				n->in_ifps[1] = (void *)-1;
49612958Sdr146992 		}
49622958Sdr146992 		break;
49632393Syz155240 	}
49643448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
49652393Syz155240 	SPL_X(s);
49662393Syz155240 }
49672393Syz155240 
496811105SAlexandr.Nedvedicky@Sun.COM #if SOLARIS2 >= 10
496911105SAlexandr.Nedvedicky@Sun.COM /* ------------------------------------------------------------------------ */
497011105SAlexandr.Nedvedicky@Sun.COM /* Function:	fr_natifindexsync					    */
497111105SAlexandr.Nedvedicky@Sun.COM /* Returns:	void							    */
497211105SAlexandr.Nedvedicky@Sun.COM /* Parameters:	ifp	  - interface, which is being sync'd		    */
497311105SAlexandr.Nedvedicky@Sun.COM /*		newifp	  - new ifindex value for interface		    */
497411105SAlexandr.Nedvedicky@Sun.COM /*              ifs	  - IPF's stack					    */
497511105SAlexandr.Nedvedicky@Sun.COM /*                                                                          */
497611105SAlexandr.Nedvedicky@Sun.COM /* Write Locks: assumes ipf_mutex is locked				    */
497711105SAlexandr.Nedvedicky@Sun.COM /*                                                                          */
497811105SAlexandr.Nedvedicky@Sun.COM /* Updates all interface index references in NAT rules and NAT entries.	    */
497911105SAlexandr.Nedvedicky@Sun.COM /* the index, which is about to be updated must match ifp value.	    */
498011105SAlexandr.Nedvedicky@Sun.COM /* ------------------------------------------------------------------------ */
fr_natifindexsync(ifp,newifp,ifs)498111105SAlexandr.Nedvedicky@Sun.COM void fr_natifindexsync(ifp, newifp, ifs)
498211105SAlexandr.Nedvedicky@Sun.COM void *ifp;
498311105SAlexandr.Nedvedicky@Sun.COM void *newifp;
498411105SAlexandr.Nedvedicky@Sun.COM ipf_stack_t *ifs;
498511105SAlexandr.Nedvedicky@Sun.COM {
498611105SAlexandr.Nedvedicky@Sun.COM 	nat_t *nat;
498711105SAlexandr.Nedvedicky@Sun.COM 	ipnat_t *n;
498811105SAlexandr.Nedvedicky@Sun.COM 
498911105SAlexandr.Nedvedicky@Sun.COM 	WRITE_ENTER(&ifs->ifs_ipf_nat);
499011105SAlexandr.Nedvedicky@Sun.COM 
499111105SAlexandr.Nedvedicky@Sun.COM 	for (nat = ifs->ifs_nat_instances; nat != NULL; nat = nat->nat_next) {
499211105SAlexandr.Nedvedicky@Sun.COM 		if (ifp == nat->nat_ifps[0])
499311105SAlexandr.Nedvedicky@Sun.COM 			nat->nat_ifps[0] = newifp;
499411105SAlexandr.Nedvedicky@Sun.COM 
499511105SAlexandr.Nedvedicky@Sun.COM 		if (ifp == nat->nat_ifps[1])
499611105SAlexandr.Nedvedicky@Sun.COM 			nat->nat_ifps[1] = newifp;
499711105SAlexandr.Nedvedicky@Sun.COM 	}
499811105SAlexandr.Nedvedicky@Sun.COM 
499911105SAlexandr.Nedvedicky@Sun.COM 	for (n = ifs->ifs_nat_list; n != NULL; n = n->in_next) {
500011105SAlexandr.Nedvedicky@Sun.COM 		if (ifp == n->in_ifps[0])
500111105SAlexandr.Nedvedicky@Sun.COM 			n->in_ifps[0] = newifp;
500211105SAlexandr.Nedvedicky@Sun.COM 
500311105SAlexandr.Nedvedicky@Sun.COM 		if (ifp == n->in_ifps[1])
500411105SAlexandr.Nedvedicky@Sun.COM 			n->in_ifps[1] = newifp;
500511105SAlexandr.Nedvedicky@Sun.COM 	}
500611105SAlexandr.Nedvedicky@Sun.COM 
500711105SAlexandr.Nedvedicky@Sun.COM 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
500811105SAlexandr.Nedvedicky@Sun.COM }
500911105SAlexandr.Nedvedicky@Sun.COM #endif
50102393Syz155240 
50112393Syz155240 /* ------------------------------------------------------------------------ */
50122393Syz155240 /* Function:    nat_icmpquerytype4                                          */
50132393Syz155240 /* Returns:     int - 1 == success, 0 == failure                            */
50142393Syz155240 /* Parameters:  icmptype(I) - ICMP type number                              */
50152393Syz155240 /*                                                                          */
50162393Syz155240 /* Tests to see if the ICMP type number passed is a query/response type or  */
50172393Syz155240 /* not.                                                                     */
50182393Syz155240 /* ------------------------------------------------------------------------ */
nat_icmpquerytype4(icmptype)50192393Syz155240 static INLINE int nat_icmpquerytype4(icmptype)
50202393Syz155240 int icmptype;
50212393Syz155240 {
50222393Syz155240 
50232393Syz155240 	/*
50242393Syz155240 	 * For the ICMP query NAT code, it is essential that both the query
50252393Syz155240 	 * and the reply match on the NAT rule. Because the NAT structure
50262393Syz155240 	 * does not keep track of the icmptype, and a single NAT structure
50272393Syz155240 	 * is used for all icmp types with the same src, dest and id, we
50282393Syz155240 	 * simply define the replies as queries as well. The funny thing is,
50292393Syz155240 	 * altough it seems silly to call a reply a query, this is exactly
50302393Syz155240 	 * as it is defined in the IPv4 specification
50312393Syz155240 	 */
50322393Syz155240 
50332393Syz155240 	switch (icmptype)
50342393Syz155240 	{
50352393Syz155240 
50362393Syz155240 	case ICMP_ECHOREPLY:
50372393Syz155240 	case ICMP_ECHO:
50382393Syz155240 	/* route aedvertisement/solliciation is currently unsupported: */
50392393Syz155240 	/* it would require rewriting the ICMP data section            */
50402393Syz155240 	case ICMP_TSTAMP:
50412393Syz155240 	case ICMP_TSTAMPREPLY:
50422393Syz155240 	case ICMP_IREQ:
50432393Syz155240 	case ICMP_IREQREPLY:
50442393Syz155240 	case ICMP_MASKREQ:
50452393Syz155240 	case ICMP_MASKREPLY:
50462393Syz155240 		return 1;
50472393Syz155240 	default:
50482393Syz155240 		return 0;
50492393Syz155240 	}
50502393Syz155240 }
50512393Syz155240 
50522393Syz155240 
50532393Syz155240 /* ------------------------------------------------------------------------ */
50542393Syz155240 /* Function:    nat_log                                                     */
50552393Syz155240 /* Returns:     Nil                                                         */
50562393Syz155240 /* Parameters:  nat(I)  - pointer to NAT structure                          */
50572393Syz155240 /*              type(I) - type of log entry to create                       */
50582393Syz155240 /*                                                                          */
50592393Syz155240 /* Creates a NAT log entry.                                                 */
50602393Syz155240 /* ------------------------------------------------------------------------ */
nat_log(nat,type,ifs)50613448Sdh155122 void nat_log(nat, type, ifs)
50622393Syz155240 struct nat *nat;
50632393Syz155240 u_int type;
50643448Sdh155122 ipf_stack_t *ifs;
50652393Syz155240 {
50662393Syz155240 #ifdef	IPFILTER_LOG
50672393Syz155240 # ifndef LARGE_NAT
50682393Syz155240 	struct ipnat *np;
50692393Syz155240 	int rulen;
50702393Syz155240 # endif
50712393Syz155240 	struct natlog natl;
50722393Syz155240 	void *items[1];
50732393Syz155240 	size_t sizes[1];
50742393Syz155240 	int types[1];
50752393Syz155240 
50767176Syx160601 	natl.nlg_inip = nat->nat_inip6;
50777176Syx160601 	natl.nlg_outip = nat->nat_outip6;
50787176Syx160601 	natl.nlg_origip = nat->nat_oip6;
50797176Syx160601 	natl.nlg_bytes[0] = nat->nat_bytes[0];
50807176Syx160601 	natl.nlg_bytes[1] = nat->nat_bytes[1];
50817176Syx160601 	natl.nlg_pkts[0] = nat->nat_pkts[0];
50827176Syx160601 	natl.nlg_pkts[1] = nat->nat_pkts[1];
50837176Syx160601 	natl.nlg_origport = nat->nat_oport;
50847176Syx160601 	natl.nlg_inport = nat->nat_inport;
50857176Syx160601 	natl.nlg_outport = nat->nat_outport;
50867176Syx160601 	natl.nlg_p = nat->nat_p;
50877176Syx160601 	natl.nlg_type = type;
50887176Syx160601 	natl.nlg_rule = -1;
50897176Syx160601 	natl.nlg_v = nat->nat_v;
50902393Syz155240 # ifndef LARGE_NAT
50912393Syz155240 	if (nat->nat_ptr != NULL) {
50923448Sdh155122 		for (rulen = 0, np = ifs->ifs_nat_list; np;
50933448Sdh155122 		     np = np->in_next, rulen++)
50942393Syz155240 			if (np == nat->nat_ptr) {
50957176Syx160601 				natl.nlg_rule = rulen;
50962393Syz155240 				break;
50972393Syz155240 			}
50982393Syz155240 	}
50992393Syz155240 # endif
51002393Syz155240 	items[0] = &natl;
51012393Syz155240 	sizes[0] = sizeof(natl);
51022393Syz155240 	types[0] = 0;
51032393Syz155240 
51043448Sdh155122 	(void) ipllog(IPL_LOGNAT, NULL, items, sizes, types, 1, ifs);
51052393Syz155240 #endif
51062393Syz155240 }
51072393Syz155240 
51082393Syz155240 
51092393Syz155240 #if defined(__OpenBSD__)
51102393Syz155240 /* ------------------------------------------------------------------------ */
51112393Syz155240 /* Function:    nat_ifdetach                                                */
51122393Syz155240 /* Returns:     Nil                                                         */
51132393Syz155240 /* Parameters:  ifp(I) - pointer to network interface                       */
51142393Syz155240 /*                                                                          */
51152393Syz155240 /* Compatibility interface for OpenBSD to trigger the correct updating of   */
51162393Syz155240 /* interface references within IPFilter.                                    */
51172393Syz155240 /* ------------------------------------------------------------------------ */
nat_ifdetach(ifp,ifs)51183448Sdh155122 void nat_ifdetach(ifp, ifs)
51192393Syz155240 void *ifp;
51203448Sdh155122 ipf_stack_t *ifs;
51212393Syz155240 {
51223448Sdh155122 	frsync(ifp, ifs);
51232393Syz155240 	return;
51242393Syz155240 }
51252393Syz155240 #endif
51262393Syz155240 
51272393Syz155240 
51282393Syz155240 /* ------------------------------------------------------------------------ */
51293448Sdh155122 /* Function:    fr_ipnatderef                                               */
51303448Sdh155122 /* Returns:     Nil                                                         */
51317131Sdr146992 /* Parameters:  inp(I) - pointer to pointer to NAT rule                     */
51323448Sdh155122 /* Write Locks: ipf_nat                                                     */
51333448Sdh155122 /*                                                                          */
51343448Sdh155122 /* ------------------------------------------------------------------------ */
fr_ipnatderef(inp,ifs)51353448Sdh155122 void fr_ipnatderef(inp, ifs)
51363448Sdh155122 ipnat_t **inp;
51373448Sdh155122 ipf_stack_t *ifs;
51383448Sdh155122 {
51393448Sdh155122 	ipnat_t *in;
51403448Sdh155122 
51413448Sdh155122 	in = *inp;
51423448Sdh155122 	*inp = NULL;
51433448Sdh155122 	in->in_use--;
51443448Sdh155122 	if (in->in_use == 0 && (in->in_flags & IPN_DELETE)) {
51453448Sdh155122 		if (in->in_apr)
51463448Sdh155122 			appr_free(in->in_apr);
51473448Sdh155122 		KFREE(in);
51483448Sdh155122 		ifs->ifs_nat_stats.ns_rules--;
51493448Sdh155122 #ifdef notdef
51503448Sdh155122 #if SOLARIS
51513448Sdh155122 		if (ifs->ifs_nat_stats.ns_rules == 0)
51523448Sdh155122 			ifs->ifs_pfil_delayed_copy = 1;
51533448Sdh155122 #endif
51543448Sdh155122 #endif
51553448Sdh155122 	}
51563448Sdh155122 }
51573448Sdh155122 
51583448Sdh155122 
51593448Sdh155122 /* ------------------------------------------------------------------------ */
51602393Syz155240 /* Function:    fr_natderef                                                 */
51612393Syz155240 /* Returns:     Nil                                                         */
51628170SJohn.Ojemann@Sun.COM /* Parameters:  natp - pointer to pointer to NAT table entry                */
51638170SJohn.Ojemann@Sun.COM /*              ifs  - ipf stack instance                                   */
51642393Syz155240 /*                                                                          */
51652393Syz155240 /* Decrement the reference counter for this NAT table entry and free it if  */
51662393Syz155240 /* there are no more things using it.                                       */
51675055Sdr146992 /*                                                                          */
51685055Sdr146992 /* IF nat_ref == 1 when this function is called, then we have an orphan nat */
51695055Sdr146992 /* structure *because* it only gets called on paths _after_ nat_ref has been*/
51705055Sdr146992 /* incremented.  If nat_ref == 1 then we shouldn't decrement it here        */
51715055Sdr146992 /* because nat_delete() will do that and send nat_ref to -1.                */
51725055Sdr146992 /*                                                                          */
51735055Sdr146992 /* Holding the lock on nat_lock is required to serialise nat_delete() being */
51745055Sdr146992 /* called from a NAT flush ioctl with a deref happening because of a packet.*/
51752393Syz155240 /* ------------------------------------------------------------------------ */
fr_natderef(natp,ifs)51763448Sdh155122 void fr_natderef(natp, ifs)
51772393Syz155240 nat_t **natp;
51783448Sdh155122 ipf_stack_t *ifs;
51792393Syz155240 {
51802393Syz155240 	nat_t *nat;
51812393Syz155240 
51822393Syz155240 	nat = *natp;
51832393Syz155240 	*natp = NULL;
51845055Sdr146992 
51855055Sdr146992 	MUTEX_ENTER(&nat->nat_lock);
51865055Sdr146992 	if (nat->nat_ref > 1) {
51875055Sdr146992 		nat->nat_ref--;
51885055Sdr146992 		MUTEX_EXIT(&nat->nat_lock);
51895055Sdr146992 		return;
51905055Sdr146992 	}
51915055Sdr146992 	MUTEX_EXIT(&nat->nat_lock);
51925055Sdr146992 
51933448Sdh155122 	WRITE_ENTER(&ifs->ifs_ipf_nat);
51948170SJohn.Ojemann@Sun.COM 	(void) nat_delete(nat, NL_EXPIRE, ifs);
51953448Sdh155122 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
51962393Syz155240 }
51972393Syz155240 
51982393Syz155240 
51992393Syz155240 /* ------------------------------------------------------------------------ */
52002393Syz155240 /* Function:    fr_natclone                                                 */
52012393Syz155240 /* Returns:     ipstate_t* - NULL == cloning failed,                        */
52028170SJohn.Ojemann@Sun.COM /*                           else pointer to new NAT structure              */
52038170SJohn.Ojemann@Sun.COM /* Parameters:  fin(I)   - pointer to packet information                    */
52048170SJohn.Ojemann@Sun.COM /*              nat(I)   - pointer to master NAT structure                  */
52052393Syz155240 /* Write Lock:  ipf_nat                                                     */
52062393Syz155240 /*                                                                          */
52078170SJohn.Ojemann@Sun.COM /* Create a "duplicate" NAT table entry from the master.                    */
52082393Syz155240 /* ------------------------------------------------------------------------ */
fr_natclone(fin,nat)52097176Syx160601 nat_t *fr_natclone(fin, nat)
52102393Syz155240 fr_info_t *fin;
52112393Syz155240 nat_t *nat;
52122393Syz155240 {
52132393Syz155240 	frentry_t *fr;
52142393Syz155240 	nat_t *clone;
52152393Syz155240 	ipnat_t *np;
52163448Sdh155122 	ipf_stack_t *ifs = fin->fin_ifs;
52172393Syz155240 
52188170SJohn.Ojemann@Sun.COM 	/*
52198170SJohn.Ojemann@Sun.COM 	 * Trigger automatic call to nat_flushtable() if the
52208170SJohn.Ojemann@Sun.COM 	 * table has reached capcity specified by hi watermark.
52218170SJohn.Ojemann@Sun.COM 	 */
52228170SJohn.Ojemann@Sun.COM 	if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_level_hi)
52238170SJohn.Ojemann@Sun.COM 		ifs->ifs_nat_doflush = 1;
52248170SJohn.Ojemann@Sun.COM 
52258170SJohn.Ojemann@Sun.COM 	/*
52268170SJohn.Ojemann@Sun.COM 	 * If automatic flushing did not do its job, and the table
52278170SJohn.Ojemann@Sun.COM 	 * has filled up, don't try to create a new entry.
52288170SJohn.Ojemann@Sun.COM 	 */
52298170SJohn.Ojemann@Sun.COM 	if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) {
52308170SJohn.Ojemann@Sun.COM 		ifs->ifs_nat_stats.ns_memfail++;
52318170SJohn.Ojemann@Sun.COM 		return NULL;
52328170SJohn.Ojemann@Sun.COM 	}
52338170SJohn.Ojemann@Sun.COM 
52342393Syz155240 	KMALLOC(clone, nat_t *);
52352393Syz155240 	if (clone == NULL)
52362393Syz155240 		return NULL;
52372393Syz155240 	bcopy((char *)nat, (char *)clone, sizeof(*clone));
52382393Syz155240 
52392393Syz155240 	MUTEX_NUKE(&clone->nat_lock);
52402393Syz155240 
52412393Syz155240 	clone->nat_aps = NULL;
52422393Syz155240 	/*
52432393Syz155240 	 * Initialize all these so that nat_delete() doesn't cause a crash.
52442393Syz155240 	 */
52452393Syz155240 	clone->nat_tqe.tqe_pnext = NULL;
52462393Syz155240 	clone->nat_tqe.tqe_next = NULL;
52472393Syz155240 	clone->nat_tqe.tqe_ifq = NULL;
52482393Syz155240 	clone->nat_tqe.tqe_parent = clone;
52492393Syz155240 
52502393Syz155240 	clone->nat_flags &= ~SI_CLONE;
52512393Syz155240 	clone->nat_flags |= SI_CLONED;
52522393Syz155240 
52532393Syz155240 	if (clone->nat_hm)
52542393Syz155240 		clone->nat_hm->hm_ref++;
52552393Syz155240 
52563448Sdh155122 	if (nat_insert(clone, fin->fin_rev, ifs) == -1) {
52572393Syz155240 		KFREE(clone);
52582393Syz155240 		return NULL;
52592393Syz155240 	}
52602393Syz155240 	np = clone->nat_ptr;
52612393Syz155240 	if (np != NULL) {
52623448Sdh155122 		if (ifs->ifs_nat_logging)
52633448Sdh155122 			nat_log(clone, (u_int)np->in_redir, ifs);
52642393Syz155240 		np->in_use++;
52652393Syz155240 	}
52662393Syz155240 	fr = clone->nat_fr;
52672393Syz155240 	if (fr != NULL) {
52682393Syz155240 		MUTEX_ENTER(&fr->fr_lock);
52692393Syz155240 		fr->fr_ref++;
52702393Syz155240 		MUTEX_EXIT(&fr->fr_lock);
52712393Syz155240 	}
52722393Syz155240 
52732393Syz155240 	/*
52742393Syz155240 	 * Because the clone is created outside the normal loop of things and
52752393Syz155240 	 * TCP has special needs in terms of state, initialise the timeout
52762393Syz155240 	 * state of the new NAT from here.
52772393Syz155240 	 */
52782393Syz155240 	if (clone->nat_p == IPPROTO_TCP) {
52793448Sdh155122 		(void) fr_tcp_age(&clone->nat_tqe, fin, ifs->ifs_nat_tqb,
52802393Syz155240 				  clone->nat_flags);
52812393Syz155240 	}
52822393Syz155240 #ifdef	IPFILTER_SYNC
52832393Syz155240 	clone->nat_sync = ipfsync_new(SMC_NAT, fin, clone);
52842393Syz155240 #endif
52853448Sdh155122 	if (ifs->ifs_nat_logging)
52863448Sdh155122 		nat_log(clone, NL_CLONE, ifs);
52872393Syz155240 	return clone;
52882393Syz155240 }
52892393Syz155240 
52902393Syz155240 
52912393Syz155240 /* ------------------------------------------------------------------------ */
52922393Syz155240 /* Function:   nat_wildok                                                   */
52932393Syz155240 /* Returns:    int - 1 == packet's ports match wildcards                    */
52942393Syz155240 /*                   0 == packet's ports don't match wildcards              */
52952393Syz155240 /* Parameters: nat(I)   - NAT entry                                         */
52962393Syz155240 /*             sport(I) - source port                                       */
52972393Syz155240 /*             dport(I) - destination port                                  */
52982393Syz155240 /*             flags(I) - wildcard flags                                    */
52992393Syz155240 /*             dir(I)   - packet direction                                  */
53002393Syz155240 /*                                                                          */
53012393Syz155240 /* Use NAT entry and packet direction to determine which combination of     */
53022393Syz155240 /* wildcard flags should be used.                                           */
53032393Syz155240 /* ------------------------------------------------------------------------ */
nat_wildok(nat,sport,dport,flags,dir)53047176Syx160601 int nat_wildok(nat, sport, dport, flags, dir)
53052393Syz155240 nat_t *nat;
53062393Syz155240 int sport;
53072393Syz155240 int dport;
53082393Syz155240 int flags;
53092393Syz155240 int dir;
53102393Syz155240 {
53112393Syz155240 	/*
53122393Syz155240 	 * When called by       dir is set to
53132393Syz155240 	 * nat_inlookup         NAT_INBOUND (0)
53142393Syz155240 	 * nat_outlookup        NAT_OUTBOUND (1)
53152393Syz155240 	 *
53162393Syz155240 	 * We simply combine the packet's direction in dir with the original
53172393Syz155240 	 * "intended" direction of that NAT entry in nat->nat_dir to decide
53182393Syz155240 	 * which combination of wildcard flags to allow.
53192393Syz155240 	 */
53202393Syz155240 
53212393Syz155240 	switch ((dir << 1) | nat->nat_dir)
53222393Syz155240 	{
53232393Syz155240 	case 3: /* outbound packet / outbound entry */
53242393Syz155240 		if (((nat->nat_inport == sport) ||
53252393Syz155240 		    (flags & SI_W_SPORT)) &&
53262393Syz155240 		    ((nat->nat_oport == dport) ||
53272393Syz155240 		    (flags & SI_W_DPORT)))
53282393Syz155240 			return 1;
53292393Syz155240 		break;
53302393Syz155240 	case 2: /* outbound packet / inbound entry */
53312393Syz155240 		if (((nat->nat_outport == sport) ||
53322393Syz155240 		    (flags & SI_W_DPORT)) &&
53332393Syz155240 		    ((nat->nat_oport == dport) ||
53342393Syz155240 		    (flags & SI_W_SPORT)))
53352393Syz155240 			return 1;
53362393Syz155240 		break;
53372393Syz155240 	case 1: /* inbound packet / outbound entry */
53382393Syz155240 		if (((nat->nat_oport == sport) ||
53392393Syz155240 		    (flags & SI_W_DPORT)) &&
53402393Syz155240 		    ((nat->nat_outport == dport) ||
53412393Syz155240 		    (flags & SI_W_SPORT)))
53422393Syz155240 			return 1;
53432393Syz155240 		break;
53442393Syz155240 	case 0: /* inbound packet / inbound entry */
53452393Syz155240 		if (((nat->nat_oport == sport) ||
53462393Syz155240 		    (flags & SI_W_SPORT)) &&
53472393Syz155240 		    ((nat->nat_outport == dport) ||
53482393Syz155240 		    (flags & SI_W_DPORT)))
53492393Syz155240 			return 1;
53502393Syz155240 		break;
53512393Syz155240 	default:
53522393Syz155240 		break;
53532393Syz155240 	}
53542393Syz155240 
53552393Syz155240 	return(0);
53562393Syz155240 }
53572393Syz155240 
53582393Syz155240 
53592393Syz155240 /* ------------------------------------------------------------------------ */
53602393Syz155240 /* Function:    nat_mssclamp                                                */
53612393Syz155240 /* Returns:     Nil                                                         */
53622393Syz155240 /* Parameters:  tcp(I)    - pointer to TCP header                           */
53632393Syz155240 /*              maxmss(I) - value to clamp the TCP MSS to                   */
53642393Syz155240 /*              csump(I)  - pointer to TCP checksum                         */
53652393Syz155240 /*                                                                          */
53662393Syz155240 /* Check for MSS option and clamp it if necessary.  If found and changed,   */
53672393Syz155240 /* then the TCP header checksum will be updated to reflect the change in    */
53682393Syz155240 /* the MSS.                                                                 */
53692393Syz155240 /* ------------------------------------------------------------------------ */
nat_mssclamp(tcp,maxmss,csump)53702958Sdr146992 static void nat_mssclamp(tcp, maxmss, csump)
53712393Syz155240 tcphdr_t *tcp;
53722393Syz155240 u_32_t maxmss;
53732393Syz155240 u_short *csump;
53742393Syz155240 {
53752393Syz155240 	u_char *cp, *ep, opt;
53762393Syz155240 	int hlen, advance;
53772393Syz155240 	u_32_t mss, sumd;
53782393Syz155240 
53792393Syz155240 	hlen = TCP_OFF(tcp) << 2;
53802393Syz155240 	if (hlen > sizeof(*tcp)) {
53812393Syz155240 		cp = (u_char *)tcp + sizeof(*tcp);
53822393Syz155240 		ep = (u_char *)tcp + hlen;
53832393Syz155240 
53842393Syz155240 		while (cp < ep) {
53852393Syz155240 			opt = cp[0];
53862393Syz155240 			if (opt == TCPOPT_EOL)
53872393Syz155240 				break;
53882393Syz155240 			else if (opt == TCPOPT_NOP) {
53892393Syz155240 				cp++;
53902393Syz155240 				continue;
53912393Syz155240 			}
53922393Syz155240 
53932393Syz155240 			if (cp + 1 >= ep)
53942393Syz155240 				break;
53952393Syz155240 			advance = cp[1];
53962393Syz155240 			if ((cp + advance > ep) || (advance <= 0))
53972393Syz155240 				break;
53982393Syz155240 			switch (opt)
53992393Syz155240 			{
54002393Syz155240 			case TCPOPT_MAXSEG:
54012393Syz155240 				if (advance != 4)
54022393Syz155240 					break;
54032393Syz155240 				mss = cp[2] * 256 + cp[3];
54042393Syz155240 				if (mss > maxmss) {
54052393Syz155240 					cp[2] = maxmss / 256;
54062393Syz155240 					cp[3] = maxmss & 0xff;
54072393Syz155240 					CALC_SUMD(mss, maxmss, sumd);
54082958Sdr146992 					fix_outcksum(csump, sumd);
54092393Syz155240 				}
54102393Syz155240 				break;
54112393Syz155240 			default:
54122393Syz155240 				/* ignore unknown options */
54132393Syz155240 				break;
54142393Syz155240 			}
54152393Syz155240 
54162393Syz155240 			cp += advance;
54172393Syz155240 		}
54182393Syz155240 	}
54192393Syz155240 }
54202393Syz155240 
54212393Syz155240 
54222393Syz155240 /* ------------------------------------------------------------------------ */
54232393Syz155240 /* Function:    fr_setnatqueue                                              */
54242393Syz155240 /* Returns:     Nil                                                         */
54252393Syz155240 /* Parameters:  nat(I)- pointer to NAT structure                            */
54262393Syz155240 /*              rev(I) - forward(0) or reverse(1) direction                 */
54272393Syz155240 /* Locks:       ipf_nat (read or write)                                     */
54282393Syz155240 /*                                                                          */
54292393Syz155240 /* Put the NAT entry on its default queue entry, using rev as a helped in   */
54302393Syz155240 /* determining which queue it should be placed on.                          */
54312393Syz155240 /* ------------------------------------------------------------------------ */
fr_setnatqueue(nat,rev,ifs)54323448Sdh155122 void fr_setnatqueue(nat, rev, ifs)
54332393Syz155240 nat_t *nat;
54342393Syz155240 int rev;
54353448Sdh155122 ipf_stack_t *ifs;
54362393Syz155240 {
54372393Syz155240 	ipftq_t *oifq, *nifq;
54382393Syz155240 
54392393Syz155240 	if (nat->nat_ptr != NULL)
54402393Syz155240 		nifq = nat->nat_ptr->in_tqehead[rev];
54412393Syz155240 	else
54422393Syz155240 		nifq = NULL;
54432393Syz155240 
54442393Syz155240 	if (nifq == NULL) {
54452393Syz155240 		switch (nat->nat_p)
54462393Syz155240 		{
54472393Syz155240 		case IPPROTO_UDP :
54483448Sdh155122 			nifq = &ifs->ifs_nat_udptq;
54492393Syz155240 			break;
54502393Syz155240 		case IPPROTO_ICMP :
54513448Sdh155122 			nifq = &ifs->ifs_nat_icmptq;
54522393Syz155240 			break;
54532393Syz155240 		case IPPROTO_TCP :
54543448Sdh155122 			nifq = ifs->ifs_nat_tqb + nat->nat_tqe.tqe_state[rev];
54552393Syz155240 			break;
54562393Syz155240 		default :
54573448Sdh155122 			nifq = &ifs->ifs_nat_iptq;
54582393Syz155240 			break;
54592393Syz155240 		}
54602393Syz155240 	}
54612393Syz155240 
54622393Syz155240 	oifq = nat->nat_tqe.tqe_ifq;
54632393Syz155240 	/*
54642393Syz155240 	 * If it's currently on a timeout queue, move it from one queue to
54652393Syz155240 	 * another, else put it on the end of the newly determined queue.
54662393Syz155240 	 */
54672393Syz155240 	if (oifq != NULL)
54683448Sdh155122 		fr_movequeue(&nat->nat_tqe, oifq, nifq, ifs);
54692393Syz155240 	else
54703448Sdh155122 		fr_queueappend(&nat->nat_tqe, nifq, nat, ifs);
54712393Syz155240 	return;
54722393Syz155240 }
54733448Sdh155122 
54745417Sjojemann /* ------------------------------------------------------------------------ */
54753448Sdh155122 /* Function:    nat_getnext                                                 */
54763448Sdh155122 /* Returns:     int - 0 == ok, else error                                   */
54773448Sdh155122 /* Parameters:  t(I)   - pointer to ipftoken structure                      */
54783448Sdh155122 /*              itp(I) - pointer to ipfgeniter_t structure                  */
54795417Sjojemann /*              ifs - ipf stack instance                                    */
54803448Sdh155122 /*                                                                          */
54815417Sjojemann /* Fetch the next nat/ipnat/hostmap structure pointer from the linked list  */
54825417Sjojemann /* and copy it out to the storage space pointed to by itp.  The next item   */
54833448Sdh155122 /* in the list to look at is put back in the ipftoken struture.             */
54843448Sdh155122 /* ------------------------------------------------------------------------ */
nat_getnext(t,itp,ifs)54853448Sdh155122 static int nat_getnext(t, itp, ifs)
54863448Sdh155122 ipftoken_t *t;
54873448Sdh155122 ipfgeniter_t *itp;
54883448Sdh155122 ipf_stack_t *ifs;
54893448Sdh155122 {
54903448Sdh155122 	hostmap_t *hm, *nexthm = NULL, zerohm;
54913448Sdh155122 	ipnat_t *ipn, *nextipnat = NULL, zeroipn;
54923448Sdh155122 	nat_t *nat, *nextnat = NULL, zeronat;
54935417Sjojemann 	int error = 0, count;
54945417Sjojemann 	char *dst;
54955417Sjojemann 
54965417Sjojemann 	if (itp->igi_nitems == 0)
54975417Sjojemann 		return EINVAL;
54983448Sdh155122 
54993448Sdh155122 	READ_ENTER(&ifs->ifs_ipf_nat);
55005417Sjojemann 
55016518Sjojemann 	/*
55026518Sjojemann 	 * Get "previous" entry from the token and find the next entry.
55036518Sjojemann 	 */
55043448Sdh155122 	switch (itp->igi_type)
55053448Sdh155122 	{
55063448Sdh155122 	case IPFGENITER_HOSTMAP :
55073448Sdh155122 		hm = t->ipt_data;
55083448Sdh155122 		if (hm == NULL) {
55093448Sdh155122 			nexthm = ifs->ifs_ipf_hm_maplist;
55103448Sdh155122 		} else {
55115417Sjojemann 			nexthm = hm->hm_next;
55123448Sdh155122 		}
55133448Sdh155122 		break;
55143448Sdh155122 
55153448Sdh155122 	case IPFGENITER_IPNAT :
55163448Sdh155122 		ipn = t->ipt_data;
55173448Sdh155122 		if (ipn == NULL) {
55183448Sdh155122 			nextipnat = ifs->ifs_nat_list;
55193448Sdh155122 		} else {
55203448Sdh155122 			nextipnat = ipn->in_next;
55213448Sdh155122 		}
55223448Sdh155122 		break;
55233448Sdh155122 
55243448Sdh155122 	case IPFGENITER_NAT :
55253448Sdh155122 		nat = t->ipt_data;
55263448Sdh155122 		if (nat == NULL) {
55273448Sdh155122 			nextnat = ifs->ifs_nat_instances;
55283448Sdh155122 		} else {
55293448Sdh155122 			nextnat = nat->nat_next;
55303448Sdh155122 		}
55315417Sjojemann 		break;
55325417Sjojemann 	default :
55335417Sjojemann 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
55345417Sjojemann 		return EINVAL;
55355417Sjojemann 	}
55365417Sjojemann 
55375417Sjojemann 	dst = itp->igi_data;
55385417Sjojemann 	for (count = itp->igi_nitems; count > 0; count--) {
55396518Sjojemann 		/*
55406518Sjojemann 		 * If we found an entry, add a reference to it and update the token.
55416518Sjojemann 		 * Otherwise, zero out data to be returned and NULL out token.
55426518Sjojemann 		 */
55435417Sjojemann 		switch (itp->igi_type)
55445417Sjojemann 		{
55455417Sjojemann 		case IPFGENITER_HOSTMAP :
55465417Sjojemann 			if (nexthm != NULL) {
55475417Sjojemann 				ATOMIC_INC32(nexthm->hm_ref);
55485417Sjojemann 				t->ipt_data = nexthm;
55493448Sdh155122 			} else {
55505417Sjojemann 				bzero(&zerohm, sizeof(zerohm));
55515417Sjojemann 				nexthm = &zerohm;
55525417Sjojemann 				t->ipt_data = NULL;
55535417Sjojemann 			}
55545417Sjojemann 			break;
55555417Sjojemann 		case IPFGENITER_IPNAT :
55565417Sjojemann 			if (nextipnat != NULL) {
55575417Sjojemann 				ATOMIC_INC32(nextipnat->in_use);
55585417Sjojemann 				t->ipt_data = nextipnat;
55595417Sjojemann 			} else {
55605417Sjojemann 				bzero(&zeroipn, sizeof(zeroipn));
55615417Sjojemann 				nextipnat = &zeroipn;
55625417Sjojemann 				t->ipt_data = NULL;
55635417Sjojemann 			}
55645417Sjojemann 			break;
55655417Sjojemann 		case IPFGENITER_NAT :
55665417Sjojemann 			if (nextnat != NULL) {
55673448Sdh155122 				MUTEX_ENTER(&nextnat->nat_lock);
55683448Sdh155122 				nextnat->nat_ref++;
55693448Sdh155122 				MUTEX_EXIT(&nextnat->nat_lock);
55705417Sjojemann 				t->ipt_data = nextnat;
55715417Sjojemann 			} else {
55725417Sjojemann 				bzero(&zeronat, sizeof(zeronat));
55735417Sjojemann 				nextnat = &zeronat;
55745417Sjojemann 				t->ipt_data = NULL;
55753448Sdh155122 			}
55765417Sjojemann 			break;
55775417Sjojemann 		default :
55785417Sjojemann 			break;
55793448Sdh155122 		}
55805417Sjojemann 
55815417Sjojemann 		/*
55826518Sjojemann 		 * Now that we have ref, it's save to give up lock.
55835417Sjojemann 		 */
55845417Sjojemann 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
55855417Sjojemann 
55866518Sjojemann 		/*
55876518Sjojemann 		 * Copy out data and clean up references and token as needed.
55886518Sjojemann 		 */
55895417Sjojemann 		switch (itp->igi_type)
55905417Sjojemann 		{
55915417Sjojemann 		case IPFGENITER_HOSTMAP :
55925417Sjojemann 			error = COPYOUT(nexthm, dst, sizeof(*nexthm));
55936518Sjojemann 			if (error != 0)
55945417Sjojemann 				error = EFAULT;
55956518Sjojemann 			if (t->ipt_data == NULL) {
55966518Sjojemann 				ipf_freetoken(t, ifs);
55976518Sjojemann 				break;
55985417Sjojemann 			} else {
55996518Sjojemann 				if (hm != NULL) {
56006518Sjojemann 					WRITE_ENTER(&ifs->ifs_ipf_nat);
56016518Sjojemann 					fr_hostmapdel(&hm);
56026518Sjojemann 					RWLOCK_EXIT(&ifs->ifs_ipf_nat);
56036518Sjojemann 				}
56046518Sjojemann 				if (nexthm->hm_next == NULL) {
56056518Sjojemann 					ipf_freetoken(t, ifs);
56066518Sjojemann 					break;
56076518Sjojemann 				}
56085417Sjojemann 				dst += sizeof(*nexthm);
56095417Sjojemann 				hm = nexthm;
56105417Sjojemann 				nexthm = nexthm->hm_next;
56115417Sjojemann 			}
56125417Sjojemann 			break;
56136518Sjojemann 
56145417Sjojemann 		case IPFGENITER_IPNAT :
56155417Sjojemann 			error = COPYOUT(nextipnat, dst, sizeof(*nextipnat));
56166518Sjojemann 			if (error != 0)
56175417Sjojemann 				error = EFAULT;
56186518Sjojemann 			if (t->ipt_data == NULL) {
56196518Sjojemann 				ipf_freetoken(t, ifs);
56206518Sjojemann 				break;
56215417Sjojemann 			} else {
56226518Sjojemann 				if (ipn != NULL) {
56236518Sjojemann 					WRITE_ENTER(&ifs->ifs_ipf_nat);
56246518Sjojemann 					fr_ipnatderef(&ipn, ifs);
56256518Sjojemann 					RWLOCK_EXIT(&ifs->ifs_ipf_nat);
56266518Sjojemann 				}
56276518Sjojemann 				if (nextipnat->in_next == NULL) {
56286518Sjojemann 					ipf_freetoken(t, ifs);
56296518Sjojemann 					break;
56306518Sjojemann 				}
56315417Sjojemann 				dst += sizeof(*nextipnat);
56325417Sjojemann 				ipn = nextipnat;
56335417Sjojemann 				nextipnat = nextipnat->in_next;
56345417Sjojemann 			}
56355417Sjojemann 			break;
56366518Sjojemann 
56375417Sjojemann 		case IPFGENITER_NAT :
56385417Sjojemann 			error = COPYOUT(nextnat, dst, sizeof(*nextnat));
56396518Sjojemann 			if (error != 0)
56405417Sjojemann 				error = EFAULT;
56416518Sjojemann 			if (t->ipt_data == NULL) {
56426518Sjojemann 				ipf_freetoken(t, ifs);
56436518Sjojemann 				break;
56445417Sjojemann 			} else {
56456518Sjojemann 				if (nat != NULL)
56466518Sjojemann 					fr_natderef(&nat, ifs);
56476518Sjojemann 				if (nextnat->nat_next == NULL) {
56486518Sjojemann 					ipf_freetoken(t, ifs);
56496518Sjojemann 					break;
56506518Sjojemann 				}
56515417Sjojemann 				dst += sizeof(*nextnat);
56525417Sjojemann 				nat = nextnat;
56535417Sjojemann 				nextnat = nextnat->nat_next;
56545417Sjojemann 			}
56555417Sjojemann 			break;
56565417Sjojemann 		default :
56575417Sjojemann 			break;
56585417Sjojemann 		}
56595417Sjojemann 
56605417Sjojemann 		if ((count == 1) || (error != 0))
56615417Sjojemann 			break;
56625417Sjojemann 
56635417Sjojemann 		READ_ENTER(&ifs->ifs_ipf_nat);
56643448Sdh155122 	}
56653448Sdh155122 
56663448Sdh155122 	return error;
56673448Sdh155122 }
56683448Sdh155122 
56693448Sdh155122 
56703448Sdh155122 /* ------------------------------------------------------------------------ */
56713448Sdh155122 /* Function:    nat_iterator                                                */
56723448Sdh155122 /* Returns:     int - 0 == ok, else error                                   */
56733448Sdh155122 /* Parameters:  token(I) - pointer to ipftoken structure                    */
56743448Sdh155122 /*              itp(I) - pointer to ipfgeniter_t structure                  */
56753448Sdh155122 /*                                                                          */
56763448Sdh155122 /* This function acts as a handler for the SIOCGENITER ioctls that use a    */
56773448Sdh155122 /* generic structure to iterate through a list.  There are three different  */
56783448Sdh155122 /* linked lists of NAT related information to go through: NAT rules, active */
56793448Sdh155122 /* NAT mappings and the NAT fragment cache.                                 */
56803448Sdh155122 /* ------------------------------------------------------------------------ */
nat_iterator(token,itp,ifs)56813448Sdh155122 static int nat_iterator(token, itp, ifs)
56823448Sdh155122 ipftoken_t *token;
56833448Sdh155122 ipfgeniter_t *itp;
56843448Sdh155122 ipf_stack_t *ifs;
56853448Sdh155122 {
56863448Sdh155122 	int error;
56873448Sdh155122 
56883448Sdh155122 	if (itp->igi_data == NULL)
56893448Sdh155122 		return EFAULT;
56903448Sdh155122 
56913448Sdh155122 	token->ipt_subtype = itp->igi_type;
56923448Sdh155122 
56933448Sdh155122 	switch (itp->igi_type)
56943448Sdh155122 	{
56953448Sdh155122 	case IPFGENITER_HOSTMAP :
56963448Sdh155122 	case IPFGENITER_IPNAT :
56973448Sdh155122 	case IPFGENITER_NAT :
56983448Sdh155122 		error = nat_getnext(token, itp, ifs);
56993448Sdh155122 		break;
57003448Sdh155122 	case IPFGENITER_NATFRAG :
57013448Sdh155122 		error = fr_nextfrag(token, itp, &ifs->ifs_ipfr_natlist,
57023448Sdh155122 				    &ifs->ifs_ipfr_nattail,
57033448Sdh155122 				    &ifs->ifs_ipf_natfrag, ifs);
57043448Sdh155122 		break;
57053448Sdh155122 	default :
57063448Sdh155122 		error = EINVAL;
57073448Sdh155122 		break;
57083448Sdh155122 	}
57093448Sdh155122 
57103448Sdh155122 	return error;
57113448Sdh155122 }
57124817San207044 
57134817San207044 
57148170SJohn.Ojemann@Sun.COM /* ---------------------------------------------------------------------- */
57158170SJohn.Ojemann@Sun.COM /* Function:    nat_flushtable						  */
57168170SJohn.Ojemann@Sun.COM /* Returns:     int - 0 == success, -1 == failure			  */
57178170SJohn.Ojemann@Sun.COM /* Parameters:  flush_option - how to flush the active NAT table	  */
57188170SJohn.Ojemann@Sun.COM /*              ifs - ipf stack instance				  */
57198170SJohn.Ojemann@Sun.COM /* Write Locks: ipf_nat							  */
57208170SJohn.Ojemann@Sun.COM /*									  */
57218170SJohn.Ojemann@Sun.COM /* Flush NAT tables.  Three actions currently defined:                    */
57228170SJohn.Ojemann@Sun.COM /*									  */
57238170SJohn.Ojemann@Sun.COM /* FLUSH_TABLE_ALL	: Flush all NAT table entries			  */
57248170SJohn.Ojemann@Sun.COM /*									  */
57258170SJohn.Ojemann@Sun.COM /* FLUSH_TABLE_CLOSING	: Flush entries with TCP connections which	  */
57268170SJohn.Ojemann@Sun.COM /*			  have started to close on both ends using	  */
57278170SJohn.Ojemann@Sun.COM /*			  ipf_flushclosing().				  */
57288170SJohn.Ojemann@Sun.COM /*									  */
57298170SJohn.Ojemann@Sun.COM /* FLUSH_TABLE_EXTRA	: First, flush entries which are "almost" closed. */
57308170SJohn.Ojemann@Sun.COM /*			  Then, if needed, flush entries with TCP	  */
57318170SJohn.Ojemann@Sun.COM /*			  connections which have been idle for a long	  */
57328170SJohn.Ojemann@Sun.COM /*			  time with ipf_extraflush().			  */
57338170SJohn.Ojemann@Sun.COM /* ---------------------------------------------------------------------- */
nat_flushtable(flush_option,ifs)57348170SJohn.Ojemann@Sun.COM static int nat_flushtable(flush_option, ifs)
57358170SJohn.Ojemann@Sun.COM int flush_option;
57364817San207044 ipf_stack_t *ifs;
57374817San207044 {
57388170SJohn.Ojemann@Sun.COM         nat_t *nat, *natn;
57398170SJohn.Ojemann@Sun.COM         int removed;
57408170SJohn.Ojemann@Sun.COM         SPL_INT(s);
57418170SJohn.Ojemann@Sun.COM 
57428170SJohn.Ojemann@Sun.COM         removed = 0;
57438170SJohn.Ojemann@Sun.COM 
57448170SJohn.Ojemann@Sun.COM         SPL_NET(s);
57458170SJohn.Ojemann@Sun.COM         switch (flush_option)
57468170SJohn.Ojemann@Sun.COM         {
57478170SJohn.Ojemann@Sun.COM         case FLUSH_TABLE_ALL:
57488170SJohn.Ojemann@Sun.COM 		natn = ifs->ifs_nat_instances;
57498170SJohn.Ojemann@Sun.COM 		while ((nat = natn) != NULL) {
57508170SJohn.Ojemann@Sun.COM 			natn = nat->nat_next;
57518170SJohn.Ojemann@Sun.COM 			if (nat_delete(nat, NL_FLUSH, ifs) == 0)
57528170SJohn.Ojemann@Sun.COM 				removed++;
57534817San207044 		}
57548170SJohn.Ojemann@Sun.COM                 break;
57558170SJohn.Ojemann@Sun.COM 
57568170SJohn.Ojemann@Sun.COM         case FLUSH_TABLE_CLOSING:
57578170SJohn.Ojemann@Sun.COM                 removed = ipf_flushclosing(NAT_FLUSH,
57588170SJohn.Ojemann@Sun.COM 					   IPF_TCPS_CLOSE_WAIT,
57598170SJohn.Ojemann@Sun.COM 					   ifs->ifs_nat_tqb,
57608170SJohn.Ojemann@Sun.COM 					   ifs->ifs_nat_utqe,
57618170SJohn.Ojemann@Sun.COM 					   ifs);
57628170SJohn.Ojemann@Sun.COM                 break;
57638170SJohn.Ojemann@Sun.COM 
57648170SJohn.Ojemann@Sun.COM         case FLUSH_TABLE_EXTRA:
57658170SJohn.Ojemann@Sun.COM                 removed = ipf_flushclosing(NAT_FLUSH,
57668170SJohn.Ojemann@Sun.COM 					   IPF_TCPS_FIN_WAIT_2,
57678170SJohn.Ojemann@Sun.COM 					   ifs->ifs_nat_tqb,
57688170SJohn.Ojemann@Sun.COM 					   ifs->ifs_nat_utqe,
57698170SJohn.Ojemann@Sun.COM 					   ifs);
57708170SJohn.Ojemann@Sun.COM 
57718170SJohn.Ojemann@Sun.COM                 /*
57728170SJohn.Ojemann@Sun.COM                  * Be sure we haven't done this in the last 10 seconds.
57738170SJohn.Ojemann@Sun.COM                  */
57748170SJohn.Ojemann@Sun.COM                 if (ifs->ifs_fr_ticks - ifs->ifs_nat_last_force_flush <
57758170SJohn.Ojemann@Sun.COM                     IPF_TTLVAL(10))
57768170SJohn.Ojemann@Sun.COM                         break;
57778170SJohn.Ojemann@Sun.COM                 ifs->ifs_nat_last_force_flush = ifs->ifs_fr_ticks;
57788170SJohn.Ojemann@Sun.COM                 removed += ipf_extraflush(NAT_FLUSH,
57798170SJohn.Ojemann@Sun.COM 					  &ifs->ifs_nat_tqb[IPF_TCPS_ESTABLISHED],
57808170SJohn.Ojemann@Sun.COM 					  ifs->ifs_nat_utqe,
57818170SJohn.Ojemann@Sun.COM 					  ifs);
57828170SJohn.Ojemann@Sun.COM                 break;
57838170SJohn.Ojemann@Sun.COM 
57848170SJohn.Ojemann@Sun.COM         default: /* Flush Nothing */
57858170SJohn.Ojemann@Sun.COM                 break;
57868170SJohn.Ojemann@Sun.COM         }
57878170SJohn.Ojemann@Sun.COM 
57888170SJohn.Ojemann@Sun.COM         SPL_X(s);
57898170SJohn.Ojemann@Sun.COM         return (removed);
57904817San207044 }
57918624SDarren.Reed@Sun.COM 
57928624SDarren.Reed@Sun.COM 
57938624SDarren.Reed@Sun.COM /* ------------------------------------------------------------------------ */
57948624SDarren.Reed@Sun.COM /* Function:    nat_uncreate                                                */
57958624SDarren.Reed@Sun.COM /* Returns:     Nil                                                         */
57968624SDarren.Reed@Sun.COM /* Parameters:  fin(I) - pointer to packet information                      */
57978624SDarren.Reed@Sun.COM /*                                                                          */
57988624SDarren.Reed@Sun.COM /* This function is used to remove a NAT entry from the NAT table when we   */
57998624SDarren.Reed@Sun.COM /* decide that the create was actually in error. It is thus assumed that    */
58008624SDarren.Reed@Sun.COM /* fin_flx will have both FI_NATED and FI_NATNEW set. Because we're dealing */
58018624SDarren.Reed@Sun.COM /* with the translated packet (not the original), we have to reverse the    */
58028624SDarren.Reed@Sun.COM /* lookup. Although doing the lookup is expensive (relatively speaking), it */
58038624SDarren.Reed@Sun.COM /* is not anticipated that this will be a frequent occurance for normal     */
58048624SDarren.Reed@Sun.COM /* traffic patterns.                                                        */
58058624SDarren.Reed@Sun.COM /* ------------------------------------------------------------------------ */
nat_uncreate(fin)58068624SDarren.Reed@Sun.COM void nat_uncreate(fin)
58078624SDarren.Reed@Sun.COM fr_info_t *fin;
58088624SDarren.Reed@Sun.COM {
58098624SDarren.Reed@Sun.COM 	ipf_stack_t *ifs = fin->fin_ifs;
58108624SDarren.Reed@Sun.COM 	int nflags;
58118624SDarren.Reed@Sun.COM 	nat_t *nat;
58128624SDarren.Reed@Sun.COM 
58138624SDarren.Reed@Sun.COM 	switch (fin->fin_p)
58148624SDarren.Reed@Sun.COM 	{
58158624SDarren.Reed@Sun.COM 	case IPPROTO_TCP :
58168624SDarren.Reed@Sun.COM 		nflags = IPN_TCP;
58178624SDarren.Reed@Sun.COM 		break;
58188624SDarren.Reed@Sun.COM 	case IPPROTO_UDP :
58198624SDarren.Reed@Sun.COM 		nflags = IPN_UDP;
58208624SDarren.Reed@Sun.COM 		break;
58218624SDarren.Reed@Sun.COM 	default :
58228624SDarren.Reed@Sun.COM 		nflags = 0;
58238624SDarren.Reed@Sun.COM 		break;
58248624SDarren.Reed@Sun.COM 	}
58258624SDarren.Reed@Sun.COM 
58268624SDarren.Reed@Sun.COM 	WRITE_ENTER(&ifs->ifs_ipf_nat);
58278624SDarren.Reed@Sun.COM 
58288624SDarren.Reed@Sun.COM 	if (fin->fin_out == 0) {
58298624SDarren.Reed@Sun.COM 		nat = nat_outlookup(fin, nflags, (u_int)fin->fin_p,
58308624SDarren.Reed@Sun.COM 				    fin->fin_dst, fin->fin_src);
58318624SDarren.Reed@Sun.COM 	} else {
58328624SDarren.Reed@Sun.COM 		nat = nat_inlookup(fin, nflags, (u_int)fin->fin_p,
58338624SDarren.Reed@Sun.COM 				   fin->fin_src, fin->fin_dst);
58348624SDarren.Reed@Sun.COM 	}
58358624SDarren.Reed@Sun.COM 
58368624SDarren.Reed@Sun.COM 	if (nat != NULL) {
58378624SDarren.Reed@Sun.COM 		ifs->ifs_nat_stats.ns_uncreate[fin->fin_out][0]++;
58388839SDarren.Reed@Sun.COM 		(void) nat_delete(nat, NL_DESTROY, ifs);
58398624SDarren.Reed@Sun.COM 	} else {
58408624SDarren.Reed@Sun.COM 		ifs->ifs_nat_stats.ns_uncreate[fin->fin_out][1]++;
58418624SDarren.Reed@Sun.COM 	}
58428624SDarren.Reed@Sun.COM 
58438624SDarren.Reed@Sun.COM 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
58448624SDarren.Reed@Sun.COM }
5845