xref: /onnv-gate/usr/src/uts/common/inet/ipf/ip_nat6.c (revision 8624:0c81faef90eb)
17176Syx160601 /*
27176Syx160601  * Copyright (C) 1995-2003 by Darren Reed.
37176Syx160601  *
47176Syx160601  * See the IPFILTER.LICENCE file for details on licencing.
57176Syx160601  *
6*8624SDarren.Reed@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
77176Syx160601  * Use is subject to license terms.
87176Syx160601  */
97176Syx160601 
107176Syx160601 #if defined(KERNEL) || defined(_KERNEL)
117176Syx160601 # undef KERNEL
127176Syx160601 # undef _KERNEL
137176Syx160601 # define        KERNEL	1
147176Syx160601 # define        _KERNEL	1
157176Syx160601 #endif
167176Syx160601 #include <sys/errno.h>
177176Syx160601 #include <sys/types.h>
187176Syx160601 #include <sys/param.h>
197176Syx160601 #include <sys/time.h>
207176Syx160601 #include <sys/file.h>
217176Syx160601 #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \
227176Syx160601     defined(_KERNEL)
237176Syx160601 # include "opt_ipfilter_log.h"
247176Syx160601 #endif
257176Syx160601 #if !defined(_KERNEL)
267176Syx160601 # include <stdio.h>
277176Syx160601 # include <string.h>
287176Syx160601 # include <stdlib.h>
297176Syx160601 # define _KERNEL
307176Syx160601 # ifdef __OpenBSD__
317176Syx160601 struct file;
327176Syx160601 # endif
337176Syx160601 # include <sys/uio.h>
347176Syx160601 # undef _KERNEL
357176Syx160601 #endif
367176Syx160601 #if defined(_KERNEL) && (__FreeBSD_version >= 220000)
377176Syx160601 # include <sys/filio.h>
387176Syx160601 # include <sys/fcntl.h>
397176Syx160601 #else
407176Syx160601 # include <sys/ioctl.h>
417176Syx160601 #endif
427176Syx160601 #if !defined(AIX)
437176Syx160601 # include <sys/fcntl.h>
447176Syx160601 #endif
457176Syx160601 #if !defined(linux)
467176Syx160601 # include <sys/protosw.h>
477176Syx160601 #endif
487176Syx160601 #include <sys/socket.h>
497176Syx160601 #if defined(_KERNEL)
507176Syx160601 # include <sys/systm.h>
517176Syx160601 # if !defined(__SVR4) && !defined(__svr4__)
527176Syx160601 #  include <sys/mbuf.h>
537176Syx160601 # endif
547176Syx160601 #endif
557176Syx160601 #if defined(__SVR4) || defined(__svr4__)
567176Syx160601 # include <sys/filio.h>
577176Syx160601 # include <sys/byteorder.h>
587176Syx160601 # ifdef _KERNEL
597176Syx160601 #  include <sys/dditypes.h>
607176Syx160601 # endif
617176Syx160601 # include <sys/stream.h>
627176Syx160601 # include <sys/kmem.h>
637176Syx160601 #endif
647176Syx160601 #if __FreeBSD_version >= 300000
657176Syx160601 # include <sys/queue.h>
667176Syx160601 #endif
677176Syx160601 #include <net/if.h>
687176Syx160601 #if __FreeBSD_version >= 300000
697176Syx160601 # include <net/if_var.h>
707176Syx160601 # if defined(_KERNEL) && !defined(IPFILTER_LKM)
717176Syx160601 #  include "opt_ipfilter.h"
727176Syx160601 # endif
737176Syx160601 #endif
747176Syx160601 #ifdef sun
757176Syx160601 # include <net/af.h>
767176Syx160601 #endif
777176Syx160601 #include <net/route.h>
787176Syx160601 #include <netinet/in.h>
797176Syx160601 #include <netinet/in_systm.h>
807176Syx160601 #include <netinet/ip.h>
817176Syx160601 
827176Syx160601 #ifdef RFC1825
837176Syx160601 # include <vpn/md5.h>
847176Syx160601 # include <vpn/ipsec.h>
857176Syx160601 extern struct ifnet vpnif;
867176Syx160601 #endif
877176Syx160601 
887176Syx160601 #if !defined(linux)
897176Syx160601 # include <netinet/ip_var.h>
907176Syx160601 #endif
917176Syx160601 #include <netinet/tcp.h>
927176Syx160601 #include <netinet/udp.h>
937176Syx160601 #include <netinet/ip_icmp.h>
947176Syx160601 #include "netinet/ip_compat.h"
957176Syx160601 #include <netinet/tcpip.h>
967176Syx160601 #include "netinet/ip_fil.h"
977176Syx160601 #include "netinet/ip_nat.h"
987176Syx160601 #include "netinet/ip_frag.h"
997176Syx160601 #include "netinet/ip_state.h"
1007176Syx160601 #include "netinet/ip_proxy.h"
1017176Syx160601 #include "netinet/ipf_stack.h"
1027176Syx160601 #ifdef	IPFILTER_SYNC
1037176Syx160601 #include "netinet/ip_sync.h"
1047176Syx160601 #endif
1057176Syx160601 #if (__FreeBSD_version >= 300000)
1067176Syx160601 # include <sys/malloc.h>
1077176Syx160601 #endif
1087176Syx160601 /* END OF INCLUDES */
1097176Syx160601 
1107176Syx160601 #undef	SOCKADDR_IN
1117176Syx160601 #define	SOCKADDR_IN	struct sockaddr_in
1127176Syx160601 
1137176Syx160601 #if !defined(lint)
1147176Syx160601 static const char rcsid[] = "@(#)$Id: ip_nat6.c,v 1.2 2008/02/14 21:05:50 darrenr Exp $";
1157176Syx160601 #endif
1167176Syx160601 
1177176Syx160601 static	hostmap_t *nat6_hostmap __P((ipnat_t *, i6addr_t *, i6addr_t *,
1187176Syx160601 				    i6addr_t *, u_32_t, ipf_stack_t *));
1197176Syx160601 static	INLINE	int nat6_newmap __P((fr_info_t *, nat_t *, natinfo_t *));
1207176Syx160601 static	INLINE	int nat6_newrdr __P((fr_info_t *, nat_t *, natinfo_t *));
1217176Syx160601 static	INLINE	int nat6_finalise __P((fr_info_t *, nat_t *, natinfo_t *,
1227176Syx160601 				      tcphdr_t *, nat_t **, int));
1237176Syx160601 static	void	nat6_tabmove __P((nat_t *, ipf_stack_t *));
1247176Syx160601 static	int	nat6_match __P((fr_info_t *, ipnat_t *));
1257176Syx160601 static	INLINE	int nat_icmpquerytype6 __P((int));
1267176Syx160601 
1277176Syx160601 
1287176Syx160601 /* ------------------------------------------------------------------------ */
1297176Syx160601 /* Function:    nat6_addrdr                                                 */
1307176Syx160601 /* Returns:     Nil                                                         */
1317176Syx160601 /* Parameters:  n(I) - pointer to NAT rule to add                           */
1327176Syx160601 /*                                                                          */
1337176Syx160601 /* Adds a redirect rule to the hash table of redirect rules and the list of */
1347176Syx160601 /* loaded NAT rules.  Updates the bitmask indicating which netmasks are in  */
1357176Syx160601 /* use by redirect rules.                                                   */
1367176Syx160601 /* ------------------------------------------------------------------------ */
nat6_addrdr(n,ifs)1377176Syx160601 void nat6_addrdr(n, ifs)
1387176Syx160601 ipnat_t *n;
1397176Syx160601 ipf_stack_t *ifs;
1407176Syx160601 {
1417176Syx160601 	ipnat_t **np;
1427176Syx160601 	i6addr_t j;
1437176Syx160601 	u_int hv;
1447176Syx160601 	int k;
1457176Syx160601 
1467176Syx160601 	k = count6bits(n->in_out[1].i6);
1477176Syx160601 	if ((k >= 0) && (k != 128))
1487176Syx160601 		ifs->ifs_rdr6_masks[k >> 5] |= 1 << (k & 31);
1497176Syx160601 	IP6_AND(&n->in_out[0], &n->in_out[1], &j);
1507176Syx160601 	hv = NAT_HASH_FN6(&j, 0, ifs->ifs_ipf_rdrrules_sz);
1517176Syx160601 	np = ifs->ifs_rdr_rules + hv;
1527176Syx160601 	while (*np != NULL)
1537176Syx160601 		np = &(*np)->in_rnext;
1547176Syx160601 	n->in_rnext = NULL;
1557176Syx160601 	n->in_prnext = np;
1567176Syx160601 	n->in_hv = hv;
1577176Syx160601 	*np = n;
1587176Syx160601 }
1597176Syx160601 
1607176Syx160601 
1617176Syx160601 /* ------------------------------------------------------------------------ */
1627176Syx160601 /* Function:    nat6_addnat                                                 */
1637176Syx160601 /* Returns:     Nil                                                         */
1647176Syx160601 /* Parameters:  n(I) - pointer to NAT rule to add                           */
1657176Syx160601 /*                                                                          */
1667176Syx160601 /* Adds a NAT map rule to the hash table of rules and the list of  loaded   */
1677176Syx160601 /* NAT rules.  Updates the bitmask indicating which netmasks are in use by  */
1687176Syx160601 /* redirect rules.                                                          */
1697176Syx160601 /* ------------------------------------------------------------------------ */
nat6_addnat(n,ifs)1707176Syx160601 void nat6_addnat(n, ifs)
1717176Syx160601 ipnat_t *n;
1727176Syx160601 ipf_stack_t *ifs;
1737176Syx160601 {
1747176Syx160601 	ipnat_t **np;
1757176Syx160601 	i6addr_t j;
1767176Syx160601 	u_int hv;
1777176Syx160601 	int k;
1787176Syx160601 
1797176Syx160601 	k = count6bits(n->in_in[1].i6);
1807176Syx160601 	if ((k >= 0) && (k != 128))
1817176Syx160601 		ifs->ifs_nat6_masks[k >> 5] |= 1 << (k & 31);
1827176Syx160601 	IP6_AND(&n->in_in[0], &n->in_in[1], &j);
1837176Syx160601 	hv = NAT_HASH_FN6(&j, 0, ifs->ifs_ipf_natrules_sz);
1847176Syx160601 	np = ifs->ifs_nat_rules + hv;
1857176Syx160601 	while (*np != NULL)
1867176Syx160601 		np = &(*np)->in_mnext;
1877176Syx160601 	n->in_mnext = NULL;
1887176Syx160601 	n->in_pmnext = np;
1897176Syx160601 	n->in_hv = hv;
1907176Syx160601 	*np = n;
1917176Syx160601 }
1927176Syx160601 
1937176Syx160601 
1947176Syx160601 /* ------------------------------------------------------------------------ */
1957176Syx160601 /* Function:    nat6_hostmap                                                */
1967176Syx160601 /* Returns:     struct hostmap* - NULL if no hostmap could be created,      */
1977176Syx160601 /*                                else a pointer to the hostmapping to use  */
1987176Syx160601 /* Parameters:  np(I)   - pointer to NAT rule                               */
1997176Syx160601 /*              real(I) - real IP address                                   */
2007176Syx160601 /*              map(I)  - mapped IP address                                 */
2017176Syx160601 /*              port(I) - destination port number                           */
2027176Syx160601 /* Write Locks: ipf_nat                                                     */
2037176Syx160601 /*                                                                          */
2047176Syx160601 /* Check if an ip address has already been allocated for a given mapping    */
2057176Syx160601 /* that is not doing port based translation.  If is not yet allocated, then */
2067176Syx160601 /* create a new entry if a non-NULL NAT rule pointer has been supplied.     */
2077176Syx160601 /* ------------------------------------------------------------------------ */
nat6_hostmap(np,src,dst,map,port,ifs)2087176Syx160601 static struct hostmap *nat6_hostmap(np, src, dst, map, port, ifs)
2097176Syx160601 ipnat_t *np;
2107176Syx160601 i6addr_t *src, *dst, *map;
2117176Syx160601 u_32_t port;
2127176Syx160601 ipf_stack_t *ifs;
2137176Syx160601 {
2147176Syx160601 	hostmap_t *hm;
2157176Syx160601 	u_int hv;
2167176Syx160601 
2177176Syx160601 	hv = (src->i6[3] ^ dst->i6[3]);
2187176Syx160601 	hv += (src->i6[2] ^ dst->i6[2]);
2197176Syx160601 	hv += (src->i6[1] ^ dst->i6[1]);
2207176Syx160601 	hv += (src->i6[0] ^ dst->i6[0]);
2217176Syx160601 	hv += src->i6[3];
2227176Syx160601 	hv += src->i6[2];
2237176Syx160601 	hv += src->i6[1];
2247176Syx160601 	hv += src->i6[0];
2257176Syx160601 	hv += dst->i6[3];
2267176Syx160601 	hv += dst->i6[2];
2277176Syx160601 	hv += dst->i6[1];
2287176Syx160601 	hv += dst->i6[0];
2297176Syx160601 	hv %= HOSTMAP_SIZE;
2307176Syx160601 	for (hm = ifs->ifs_maptable[hv]; hm; hm = hm->hm_next)
2317176Syx160601 		if (IP6_EQ(&hm->hm_srcip6, src) &&
2327176Syx160601 		    IP6_EQ(&hm->hm_dstip6, dst) &&
2337176Syx160601 		    ((np == NULL) || (np == hm->hm_ipnat)) &&
2347176Syx160601 		    ((port == 0) || (port == hm->hm_port))) {
2357176Syx160601 			hm->hm_ref++;
2367176Syx160601 			return hm;
2377176Syx160601 		}
2387176Syx160601 
2397176Syx160601 	if (np == NULL)
2407176Syx160601 		return NULL;
2417176Syx160601 
2427176Syx160601 	KMALLOC(hm, hostmap_t *);
2437176Syx160601 	if (hm) {
2447176Syx160601 		hm->hm_hnext = ifs->ifs_ipf_hm_maplist;
2457176Syx160601 		hm->hm_phnext = &ifs->ifs_ipf_hm_maplist;
2467176Syx160601 		if (ifs->ifs_ipf_hm_maplist != NULL)
2477176Syx160601 			ifs->ifs_ipf_hm_maplist->hm_phnext = &hm->hm_hnext;
2487176Syx160601 		ifs->ifs_ipf_hm_maplist = hm;
2497176Syx160601 
2507176Syx160601 		hm->hm_next = ifs->ifs_maptable[hv];
2517176Syx160601 		hm->hm_pnext = ifs->ifs_maptable + hv;
2527176Syx160601 		if (ifs->ifs_maptable[hv] != NULL)
2537176Syx160601 			ifs->ifs_maptable[hv]->hm_pnext = &hm->hm_next;
2547176Syx160601 		ifs->ifs_maptable[hv] = hm;
2557176Syx160601 		hm->hm_ipnat = np;
2567176Syx160601 		hm->hm_src = *src;
2577176Syx160601 		hm->hm_dst = *dst;
2587176Syx160601 		hm->hm_map = *map;
2597176Syx160601 		hm->hm_ref = 1;
2607176Syx160601 		hm->hm_port = port;
2617176Syx160601 		hm->hm_v = 6;
2627176Syx160601 	}
2637176Syx160601 	return hm;
2647176Syx160601 }
2657176Syx160601 
2667176Syx160601 
2677176Syx160601 /* ------------------------------------------------------------------------ */
2687176Syx160601 /* Function:    nat6_newmap                                                 */
2697176Syx160601 /* Returns:     int - -1 == error, 0 == success                             */
2707176Syx160601 /* Parameters:  fin(I) - pointer to packet information                      */
2717176Syx160601 /*              nat(I) - pointer to NAT entry                               */
2727176Syx160601 /*              ni(I)  - pointer to structure with misc. information needed */
2737176Syx160601 /*                       to create new NAT entry.                           */
2747176Syx160601 /*                                                                          */
2757176Syx160601 /* Given an empty NAT structure, populate it with new information about a   */
2767176Syx160601 /* new NAT session, as defined by the matching NAT rule.                    */
2777176Syx160601 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
2787176Syx160601 /* to the new IP address for the translation.                               */
2797176Syx160601 /* ------------------------------------------------------------------------ */
nat6_newmap(fin,nat,ni)2807176Syx160601 static INLINE int nat6_newmap(fin, nat, ni)
2817176Syx160601 fr_info_t *fin;
2827176Syx160601 nat_t *nat;
2837176Syx160601 natinfo_t *ni;
2847176Syx160601 {
2857176Syx160601 	u_short st_port, dport, sport, port, sp, dp;
2867176Syx160601 	i6addr_t in, st_ip;
2877176Syx160601 	hostmap_t *hm;
2887176Syx160601 	u_32_t flags;
2897176Syx160601 	ipnat_t *np;
2907176Syx160601 	nat_t *natl;
2917176Syx160601 	int l;
2927176Syx160601 	ipf_stack_t *ifs = fin->fin_ifs;
2937176Syx160601 
2947176Syx160601 	/*
2957176Syx160601 	 * If it's an outbound packet which doesn't match any existing
2967176Syx160601 	 * record, then create a new port
2977176Syx160601 	 */
2987176Syx160601 	l = 0;
2997176Syx160601 	hm = NULL;
3007176Syx160601 	np = ni->nai_np;
3017176Syx160601 	st_ip = np->in_next6;
3027176Syx160601 	st_port = np->in_pnext;
3037176Syx160601 	flags = ni->nai_flags;
3047176Syx160601 	sport = ni->nai_sport;
3057176Syx160601 	dport = ni->nai_dport;
3067176Syx160601 
3077176Syx160601 	/*
3087176Syx160601 	 * Do a loop until we either run out of entries to try or we find
3097176Syx160601 	 * a NAT mapping that isn't currently being used.  This is done
3107176Syx160601 	 * because the change to the source is not (usually) being fixed.
3117176Syx160601 	 */
3127176Syx160601 	do {
3137176Syx160601 		port = 0;
3147176Syx160601 		in = np->in_next6;
3157176Syx160601 		if (l == 0) {
3167176Syx160601 			/*
3177176Syx160601 			 * Check to see if there is an existing NAT
3187176Syx160601 			 * setup for this IP address pair.
3197176Syx160601 			 */
3207176Syx160601 			hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6,
3217176Syx160601 					 &in, 0, ifs);
3227176Syx160601 			if (hm != NULL)
3237176Syx160601 				in = hm->hm_map;
3247176Syx160601 		} else if ((l == 1) && (hm != NULL)) {
3257176Syx160601 			fr_hostmapdel(&hm);
3267176Syx160601 		}
3277176Syx160601 
3287176Syx160601 		nat->nat_hm = hm;
3297176Syx160601 
3307176Syx160601 		if (IP6_ISONES(&np->in_out[1]) && (np->in_pnext == 0)) {
3317176Syx160601 			if (l > 0)
3327176Syx160601 				return -1;
3337176Syx160601 		}
3347176Syx160601 
3357176Syx160601 		if (np->in_redir == NAT_BIMAP &&
3367176Syx160601 		    IP6_EQ(&np->in_in[1], &np->in_out[1])) {
3377176Syx160601 			i6addr_t temp;
3387176Syx160601 			/*
3397176Syx160601 			 * map the address block in a 1:1 fashion
3407176Syx160601 			 */
3417176Syx160601 			temp.i6[0] = fin->fin_src6.i6[0] &
3427176Syx160601 					~np->in_in[1].i6[0];
3437176Syx160601 			temp.i6[1] = fin->fin_src6.i6[1] &
3447176Syx160601 					~np->in_in[1].i6[1];
3457176Syx160601 			temp.i6[2] = fin->fin_src6.i6[2] &
3467176Syx160601 					~np->in_in[1].i6[2];
3477176Syx160601 			temp.i6[3] = fin->fin_src6.i6[3] &
3487176Syx160601 					~np->in_in[1].i6[3];
3497176Syx160601 			in = np->in_out[0];
3507176Syx160601 			IP6_MERGE(&in, &temp, &np->in_in[0]);
3517176Syx160601 
3527176Syx160601 #ifdef	NEED_128BIT_MATH
3537176Syx160601 		} else if (np->in_redir & NAT_MAPBLK) {
3547176Syx160601 			if ((l >= np->in_ppip) || ((l > 0) &&
3557176Syx160601 			     !(flags & IPN_TCPUDP)))
3567176Syx160601 				return -1;
3577176Syx160601 			/*
3587176Syx160601 			 * map-block - Calculate destination address.
3597176Syx160601 			 */
3607176Syx160601 			IP6_MASK(&in, &fin->fin_src6, &np->in_in[1]);
3617176Syx160601 			in = ntol(in);
3627176Syx160601 			inb = in;
3637176Syx160601 			in /= np->in_ippip;
3647176Syx160601 			in &= ntohl(~np->in_out[1]);
3657176Syx160601 			in += ntohl(np->in_out[0]);
3667176Syx160601 			/*
3677176Syx160601 			 * Calculate destination port.
3687176Syx160601 			 */
3697176Syx160601 			if ((flags & IPN_TCPUDP) &&
3707176Syx160601 			    (np->in_ppip != 0)) {
3717176Syx160601 				port = ntohs(sport) + l;
3727176Syx160601 				port %= np->in_ppip;
3737176Syx160601 				port += np->in_ppip *
3747176Syx160601 					(inb.s_addr % np->in_ippip);
3757176Syx160601 				port += MAPBLK_MINPORT;
3767176Syx160601 				port = htons(port);
3777176Syx160601 			}
3787176Syx160601 #endif
3797176Syx160601 
3807176Syx160601 		} else if (IP6_ISZERO(&np->in_out[0]) &&
3817176Syx160601 		    IP6_ISONES(&np->in_out[1])) {
3827176Syx160601 			/*
3837176Syx160601 			 * 0/128 - use the interface's IP address.
3847176Syx160601 			 */
3857176Syx160601 			if ((l > 0) ||
3867176Syx160601 			    fr_ifpaddr(6, FRI_NORMAL, fin->fin_ifp,
3877176Syx160601 				       (void *)&in, NULL, fin->fin_ifs) == -1)
3887176Syx160601 				return -1;
3897176Syx160601 
3907176Syx160601 		} else if (IP6_ISZERO(&np->in_out[0]) &&
3917176Syx160601 		    IP6_ISZERO(&np->in_out[1])) {
3927176Syx160601 			/*
3937176Syx160601 			 * 0/0 - use the original source address/port.
3947176Syx160601 			 */
3957176Syx160601 			if (l > 0)
3967176Syx160601 				return -1;
3977176Syx160601 			in = fin->fin_src6;
3987176Syx160601 
3997176Syx160601 		} else if (!IP6_ISONES(&np->in_out[1]) &&
4007176Syx160601 			   (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) {
4017176Syx160601 			IP6_INC(&np->in_next6);
4027176Syx160601 		}
4037176Syx160601 
4047176Syx160601 		natl = NULL;
4057176Syx160601 
4067176Syx160601 		if ((flags & IPN_TCPUDP) &&
4077176Syx160601 		    ((np->in_redir & NAT_MAPBLK) == 0) &&
4087176Syx160601 		    (np->in_flags & IPN_AUTOPORTMAP)) {
4097176Syx160601 			/*EMPTY*/;
4107176Syx160601 #ifdef	NEED_128BIT_MATH
4117176Syx160601 			/*
4127176Syx160601 			 * XXX "ports auto" (without map-block)
4137176Syx160601 			 */
4147176Syx160601 			if ((l > 0) && (l % np->in_ppip == 0)) {
4157176Syx160601 				if (l > np->in_space) {
4167176Syx160601 					return -1;
4177176Syx160601 				} else if ((l > np->in_ppip) &&
4187176Syx160601 				    !IP6_ISONES(&np->in_out[1])) {
4197176Syx160601 					IP6_INC(&np->in_next6);
4207176Syx160601 				}
4217176Syx160601 			}
4227176Syx160601 			if (np->in_ppip != 0) {
4237176Syx160601 				port = ntohs(sport);
4247176Syx160601 				port += (l % np->in_ppip);
4257176Syx160601 				port %= np->in_ppip;
4267176Syx160601 				port += np->in_ppip *
4277176Syx160601 					(ntohl(fin->fin_src6) %
4287176Syx160601 					 np->in_ippip);
4297176Syx160601 				port += MAPBLK_MINPORT;
4307176Syx160601 				port = htons(port);
4317176Syx160601 			}
4327176Syx160601 #endif
4337176Syx160601 
4347176Syx160601 		} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
4357176Syx160601 			   (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) {
4367176Syx160601 			/*
4377176Syx160601 			 * Standard port translation.  Select next port.
4387176Syx160601 			 */
4397259Sdr146992 			if (np->in_flags & IPN_SEQUENTIAL) {
4407259Sdr146992 				port = np->in_pnext;
4417259Sdr146992 			} else {
4427259Sdr146992 				port = ipf_random() % (ntohs(np->in_pmax) -
4437259Sdr146992 						       ntohs(np->in_pmin));
4447259Sdr146992 				port += ntohs(np->in_pmin);
4457259Sdr146992 			}
4467259Sdr146992 			port = htons(port);
4477259Sdr146992 			np->in_pnext++;
4487176Syx160601 
4497176Syx160601 			if (np->in_pnext > ntohs(np->in_pmax)) {
4507176Syx160601 				np->in_pnext = ntohs(np->in_pmin);
4517176Syx160601 				if (!IP6_ISONES(&np->in_out[1])) {
4527176Syx160601 					IP6_INC(&np->in_next6);
4537176Syx160601 				}
4547176Syx160601 			}
4557176Syx160601 		}
4567176Syx160601 
4577176Syx160601 		if (np->in_flags & IPN_IPRANGE) {
4587176Syx160601 			if (IP6_GT(&np->in_next6, &np->in_out[1]))
4597176Syx160601 				np->in_next6 = np->in_out[0];
4607176Syx160601 		} else {
4617176Syx160601 			i6addr_t a1, a2;
4627176Syx160601 
4637176Syx160601 			a1 = np->in_next6;
4647176Syx160601 			IP6_INC(&a1);
4657176Syx160601 			IP6_AND(&a1, &np->in_out[1], &a2);
4667176Syx160601 			if (!IP6_ISONES(&np->in_out[1]) &&
4677176Syx160601 			    IP6_GT(&a2, &np->in_out[0])) {
4687176Syx160601 				IP6_ADD(&np->in_out[0], 1, &np->in_next6);
4697176Syx160601 			}
4707176Syx160601 		}
4717176Syx160601 
4727176Syx160601 		if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
4737176Syx160601 			port = sport;
4747176Syx160601 
4757176Syx160601 		/*
4767176Syx160601 		 * Here we do a lookup of the connection as seen from
4777176Syx160601 		 * the outside.  If an IP# pair already exists, try
4787176Syx160601 		 * again.  So if you have A->B becomes C->B, you can
4797176Syx160601 		 * also have D->E become C->E but not D->B causing
4807176Syx160601 		 * another C->B.  Also take protocol and ports into
4817176Syx160601 		 * account when determining whether a pre-existing
4827176Syx160601 		 * NAT setup will cause an external conflict where
4837176Syx160601 		 * this is appropriate.
4847176Syx160601 		 */
4857176Syx160601 		sp = fin->fin_data[0];
4867176Syx160601 		dp = fin->fin_data[1];
4877176Syx160601 		fin->fin_data[0] = fin->fin_data[1];
4887176Syx160601 		fin->fin_data[1] = htons(port);
4897176Syx160601 		natl = nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
4907176Syx160601 		    (u_int)fin->fin_p, &fin->fin_dst6.in6, &in.in6);
4917176Syx160601 		fin->fin_data[0] = sp;
4927176Syx160601 		fin->fin_data[1] = dp;
4937176Syx160601 
4947176Syx160601 		/*
4957176Syx160601 		 * Has the search wrapped around and come back to the
4967176Syx160601 		 * start ?
4977176Syx160601 		 */
4987176Syx160601 		if ((natl != NULL) &&
4997176Syx160601 		    (np->in_pnext != 0) && (st_port == np->in_pnext) &&
5007176Syx160601 		    !IP6_ISZERO(&np->in_next6) &&
5017176Syx160601 		    IP6_EQ(&st_ip, &np->in_next6))
5027176Syx160601 			return -1;
5037176Syx160601 		l++;
5047176Syx160601 	} while (natl != NULL);
5057176Syx160601 
5067176Syx160601 	if (np->in_space > 0)
5077176Syx160601 		np->in_space--;
5087176Syx160601 
5097176Syx160601 	/* Setup the NAT table */
5107176Syx160601 	nat->nat_inip6 = fin->fin_src6;
5117176Syx160601 	nat->nat_outip6 = in;
5127176Syx160601 	nat->nat_oip6 = fin->fin_dst6;
5137176Syx160601 	if (nat->nat_hm == NULL)
5147176Syx160601 		nat->nat_hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6,
5157176Syx160601 		    &nat->nat_outip6, 0, ifs);
5167176Syx160601 
5177176Syx160601 	if (flags & IPN_TCPUDP) {
5187176Syx160601 		nat->nat_inport = sport;
5197176Syx160601 		nat->nat_outport = port;	/* sport */
5207176Syx160601 		nat->nat_oport = dport;
5217176Syx160601 		((tcphdr_t *)fin->fin_dp)->th_sport = port;
5227176Syx160601 	} else if (flags & IPN_ICMPQUERY) {
5237176Syx160601 		((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port;
5247176Syx160601 		nat->nat_inport = port;
5257176Syx160601 		nat->nat_outport = port;
5267176Syx160601 	}
5277176Syx160601 
5287176Syx160601 	ni->nai_port = port;
5297176Syx160601 	ni->nai_nport = dport;
5307176Syx160601 	return 0;
5317176Syx160601 }
5327176Syx160601 
5337176Syx160601 
5347176Syx160601 /* ------------------------------------------------------------------------ */
5357176Syx160601 /* Function:    nat6_newrdr                                                 */
5367176Syx160601 /* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
5377176Syx160601 /*                    allow rule to be moved if IPN_ROUNDR is set.          */
5387176Syx160601 /* Parameters:  fin(I) - pointer to packet information                      */
5397176Syx160601 /*              nat(I) - pointer to NAT entry                               */
5407176Syx160601 /*              ni(I)  - pointer to structure with misc. information needed */
5417176Syx160601 /*                       to create new NAT entry.                           */
5427176Syx160601 /*                                                                          */
5437176Syx160601 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
5447176Syx160601 /* to the new IP address for the translation.                               */
5457176Syx160601 /* ------------------------------------------------------------------------ */
nat6_newrdr(fin,nat,ni)5467176Syx160601 static INLINE int nat6_newrdr(fin, nat, ni)
5477176Syx160601 fr_info_t *fin;
5487176Syx160601 nat_t *nat;
5497176Syx160601 natinfo_t *ni;
5507176Syx160601 {
5517176Syx160601 	u_short nport, dport, sport;
5527176Syx160601 	i6addr_t in;
5537176Syx160601 	u_short sp, dp;
5547176Syx160601 	hostmap_t *hm;
5557176Syx160601 	u_32_t flags;
5567176Syx160601 	ipnat_t *np;
5577176Syx160601 	nat_t *natl;
5587176Syx160601 	int move;
5597176Syx160601 	ipf_stack_t *ifs = fin->fin_ifs;
5607176Syx160601 
5617176Syx160601 	move = 1;
5627176Syx160601 	hm = NULL;
5637176Syx160601 	in.i6[0] = 0;
5647176Syx160601 	in.i6[1] = 0;
5657176Syx160601 	in.i6[2] = 0;
5667176Syx160601 	in.i6[3] = 0;
5677176Syx160601 	np = ni->nai_np;
5687176Syx160601 	flags = ni->nai_flags;
5697176Syx160601 	sport = ni->nai_sport;
5707176Syx160601 	dport = ni->nai_dport;
5717176Syx160601 
5727176Syx160601 	/*
5737176Syx160601 	 * If the matching rule has IPN_STICKY set, then we want to have the
5747176Syx160601 	 * same rule kick in as before.  Why would this happen?  If you have
5757176Syx160601 	 * a collection of rdr rules with "round-robin sticky", the current
5767176Syx160601 	 * packet might match a different one to the previous connection but
5777176Syx160601 	 * we want the same destination to be used.
5787176Syx160601 	 */
5797176Syx160601 	if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) ==
5807176Syx160601 	    (IPN_ROUNDR|IPN_STICKY)) {
5817176Syx160601 		hm = nat6_hostmap(NULL, &fin->fin_src6, &fin->fin_dst6, &in,
5827176Syx160601 		    (u_32_t)dport, ifs);
5837176Syx160601 		if (hm != NULL) {
5847176Syx160601 			in = hm->hm_map;
5857176Syx160601 			np = hm->hm_ipnat;
5867176Syx160601 			ni->nai_np = np;
5877176Syx160601 			move = 0;
5887176Syx160601 		}
5897176Syx160601 	}
5907176Syx160601 
5917176Syx160601 	/*
5927176Syx160601 	 * Otherwise, it's an inbound packet. Most likely, we don't
5937176Syx160601 	 * want to rewrite source ports and source addresses. Instead,
5947176Syx160601 	 * we want to rewrite to a fixed internal address and fixed
5957176Syx160601 	 * internal port.
5967176Syx160601 	 */
5977176Syx160601 	if (np->in_flags & IPN_SPLIT) {
5987176Syx160601 		in = np->in_next6;
5997176Syx160601 
6007176Syx160601 		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
6017176Syx160601 			hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6,
6027176Syx160601 			    &in, (u_32_t)dport, ifs);
6037176Syx160601 			if (hm != NULL) {
6047176Syx160601 				in = hm->hm_map;
6057176Syx160601 				move = 0;
6067176Syx160601 			}
6077176Syx160601 		}
6087176Syx160601 
6097176Syx160601 		if (hm == NULL || hm->hm_ref == 1) {
6107176Syx160601 			if (IP6_EQ(&np->in_in[0], &in)) {
6117176Syx160601 				np->in_next6 = np->in_in[1];
6127176Syx160601 				move = 0;
6137176Syx160601 			} else {
6147176Syx160601 				np->in_next6 = np->in_in[0];
6157176Syx160601 			}
6167176Syx160601 		}
6177176Syx160601 
6187176Syx160601 	} else if (IP6_ISZERO(&np->in_in[0]) &&
6197176Syx160601 	    IP6_ISONES(&np->in_in[1])) {
6207176Syx160601 		/*
6217176Syx160601 		 * 0/128 - use the interface's IP address.
6227176Syx160601 		 */
6237176Syx160601 		if (fr_ifpaddr(6, FRI_NORMAL, fin->fin_ifp, (void *)&in, NULL,
6247176Syx160601 			   fin->fin_ifs) == -1)
6257176Syx160601 			return -1;
6267176Syx160601 
6277176Syx160601 	} else if (IP6_ISZERO(&np->in_in[0]) &&
6287176Syx160601 	    IP6_ISZERO(&np->in_in[1])) {
6297176Syx160601 		/*
6307176Syx160601 		 * 0/0 - use the original destination address/port.
6317176Syx160601 		 */
6327176Syx160601 		in = fin->fin_dst6;
6337176Syx160601 
6347176Syx160601 	} else if (np->in_redir == NAT_BIMAP &&
6357176Syx160601 	    IP6_EQ(&np->in_in[1], &np->in_out[1])) {
6367176Syx160601 		i6addr_t temp;
6377176Syx160601 		/*
6387176Syx160601 		 * map the address block in a 1:1 fashion
6397176Syx160601 		 */
6407176Syx160601 		temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_in[1].i6[0];
6417176Syx160601 		temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_in[1].i6[1];
6427176Syx160601 		temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_in[1].i6[2];
6437176Syx160601 		temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_in[1].i6[3];
6447176Syx160601 		in = np->in_in[0];
6457176Syx160601 		IP6_MERGE(&in, &temp, &np->in_in[1]);
6467176Syx160601 	} else {
6477176Syx160601 		in = np->in_in[0];
6487176Syx160601 	}
6497176Syx160601 
6507176Syx160601 	if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
6517176Syx160601 		nport = dport;
6527176Syx160601 	else {
6537176Syx160601 		/*
6547176Syx160601 		 * Whilst not optimized for the case where
6557176Syx160601 		 * pmin == pmax, the gain is not significant.
6567176Syx160601 		 */
6577176Syx160601 		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
6587176Syx160601 		    (np->in_pmin != np->in_pmax)) {
6597176Syx160601 			nport = ntohs(dport) - ntohs(np->in_pmin) +
6607176Syx160601 				ntohs(np->in_pnext);
6617176Syx160601 			nport = htons(nport);
6627176Syx160601 		} else
6637176Syx160601 			nport = np->in_pnext;
6647176Syx160601 	}
6657176Syx160601 
6667176Syx160601 	/*
6677176Syx160601 	 * When the redirect-to address is set to 0.0.0.0, just
6687176Syx160601 	 * assume a blank `forwarding' of the packet.  We don't
6697176Syx160601 	 * setup any translation for this either.
6707176Syx160601 	 */
6717176Syx160601 	if (IP6_ISZERO(&in)) {
6727176Syx160601 		if (nport == dport)
6737176Syx160601 			return -1;
6747176Syx160601 		in = fin->fin_dst6;
6757176Syx160601 	}
6767176Syx160601 
6777176Syx160601 	/*
6787176Syx160601 	 * Check to see if this redirect mapping already exists and if
6797176Syx160601 	 * it does, return "failure" (allowing it to be created will just
6807176Syx160601 	 * cause one or both of these "connections" to stop working.)
6817176Syx160601 	 */
6827176Syx160601 	sp = fin->fin_data[0];
6837176Syx160601 	dp = fin->fin_data[1];
6847176Syx160601 	fin->fin_data[1] = fin->fin_data[0];
6857176Syx160601 	fin->fin_data[0] = ntohs(nport);
6867176Syx160601 	natl = nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
6877176Syx160601 	    (u_int)fin->fin_p, &in.in6, &fin->fin_src6.in6);
6887176Syx160601 	fin->fin_data[0] = sp;
6897176Syx160601 	fin->fin_data[1] = dp;
6907176Syx160601 	if (natl != NULL)
6917176Syx160601 		return -1;
6927176Syx160601 
6937176Syx160601 	nat->nat_inip6 = in;
6947176Syx160601 	nat->nat_outip6 = fin->fin_dst6;
6957176Syx160601 	nat->nat_oip6 = fin->fin_src6;
6967176Syx160601 	if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
6977176Syx160601 		nat->nat_hm = nat6_hostmap(np, &fin->fin_src6,
6987176Syx160601 		    &fin->fin_dst6, &in, (u_32_t)dport, ifs);
6997176Syx160601 
7007176Syx160601 	ni->nai_nport = nport;
7017176Syx160601 	ni->nai_port = sport;
7027176Syx160601 
7037176Syx160601 	if (flags & IPN_TCPUDP) {
7047176Syx160601 		nat->nat_inport = nport;
7057176Syx160601 		nat->nat_outport = dport;
7067176Syx160601 		nat->nat_oport = sport;
7077176Syx160601 		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
7087176Syx160601 	} else if (flags & IPN_ICMPQUERY) {
7097176Syx160601 		((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport;
7107176Syx160601 		nat->nat_inport = nport;
7117176Syx160601 		nat->nat_outport = nport;
7127176Syx160601 	}
7137176Syx160601 
7147176Syx160601 	return move;
7157176Syx160601 }
7167176Syx160601 
7177176Syx160601 /* ------------------------------------------------------------------------ */
7187176Syx160601 /* Function:    nat6_new                                                    */
7197176Syx160601 /* Returns:     nat_t* - NULL == failure to create new NAT structure,       */
7207176Syx160601 /*                       else pointer to new NAT structure                  */
7217176Syx160601 /* Parameters:  fin(I)       - pointer to packet information                */
7227176Syx160601 /*              np(I)        - pointer to NAT rule                          */
7237176Syx160601 /*              natsave(I)   - pointer to where to store NAT struct pointer */
7247176Syx160601 /*              flags(I)     - flags describing the current packet          */
7257176Syx160601 /*              direction(I) - direction of packet (in/out)                 */
7267176Syx160601 /* Write Lock:  ipf_nat                                                     */
7277176Syx160601 /*                                                                          */
7287176Syx160601 /* Attempts to create a new NAT entry.  Does not actually change the packet */
7297176Syx160601 /* in any way.                                                              */
7307176Syx160601 /*                                                                          */
7317176Syx160601 /* This fucntion is in three main parts: (1) deal with creating a new NAT   */
7327176Syx160601 /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
7337176Syx160601 /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
7347176Syx160601 /* and (3) building that structure and putting it into the NAT table(s).    */
7357176Syx160601 /* ------------------------------------------------------------------------ */
nat6_new(fin,np,natsave,flags,direction)7367176Syx160601 nat_t *nat6_new(fin, np, natsave, flags, direction)
7377176Syx160601 fr_info_t *fin;
7387176Syx160601 ipnat_t *np;
7397176Syx160601 nat_t **natsave;
7407176Syx160601 u_int flags;
7417176Syx160601 int direction;
7427176Syx160601 {
7437176Syx160601 	tcphdr_t *tcp = NULL;
7447176Syx160601 	hostmap_t *hm = NULL;
7457176Syx160601 	nat_t *nat, *natl;
7467176Syx160601 	u_int nflags;
7477176Syx160601 	natinfo_t ni;
7487176Syx160601 	int move;
7497176Syx160601 	ipf_stack_t *ifs = fin->fin_ifs;
7507176Syx160601 
7517176Syx160601 	/*
7528170SJohn.Ojemann@Sun.COM 	 * Trigger automatic call to ipf_extraflush() if the
7537176Syx160601 	 * table has reached capcity specified by hi watermark.
7547176Syx160601 	 */
7558170SJohn.Ojemann@Sun.COM 	if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_level_hi)
7567176Syx160601 		ifs->ifs_nat_doflush = 1;
7577176Syx160601 
7587176Syx160601 	if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) {
7597176Syx160601 		ifs->ifs_nat_stats.ns_memfail++;
7607176Syx160601 		return NULL;
7617176Syx160601 	}
7627176Syx160601 
7637176Syx160601 	move = 1;
7647176Syx160601 	nflags = np->in_flags & flags;
7657176Syx160601 	nflags &= NAT_FROMRULE;
7667176Syx160601 
7677176Syx160601 	ni.nai_np = np;
7687176Syx160601 	ni.nai_nflags = nflags;
7697176Syx160601 	ni.nai_flags = flags;
7707176Syx160601 
7717176Syx160601 	/* Give me a new nat */
7727176Syx160601 	KMALLOC(nat, nat_t *);
7737176Syx160601 	if (nat == NULL) {
7747176Syx160601 		ifs->ifs_nat_stats.ns_memfail++;
7757176Syx160601 		/*
7767176Syx160601 		 * Try to automatically tune the max # of entries in the
7777176Syx160601 		 * table allowed to be less than what will cause kmem_alloc()
7787176Syx160601 		 * to fail and try to eliminate panics due to out of memory
7797176Syx160601 		 * conditions arising.
7807176Syx160601 		 */
7817176Syx160601 		if (ifs->ifs_ipf_nattable_max > ifs->ifs_ipf_nattable_sz) {
7827176Syx160601 			ifs->ifs_ipf_nattable_max =
7837176Syx160601 			    ifs->ifs_nat_stats.ns_inuse - 100;
7847176Syx160601 			printf("ipf_nattable_max reduced to %d\n",
7857176Syx160601 			    ifs->ifs_ipf_nattable_max);
7867176Syx160601 		}
7877176Syx160601 		return NULL;
7887176Syx160601 	}
7897176Syx160601 
7907176Syx160601 	if (flags & IPN_TCPUDP) {
7917176Syx160601 		tcp = fin->fin_dp;
7927176Syx160601 		ni.nai_sport = htons(fin->fin_sport);
7937176Syx160601 		ni.nai_dport = htons(fin->fin_dport);
7947176Syx160601 	} else if (flags & IPN_ICMPQUERY) {
7957176Syx160601 		/*
7967176Syx160601 		 * In the ICMP query NAT code, we translate the ICMP id fields
7977176Syx160601 		 * to make them unique. This is indepedent of the ICMP type
7987176Syx160601 		 * (e.g. in the unlikely event that a host sends an echo and
7997176Syx160601 		 * an tstamp request with the same id, both packets will have
8007176Syx160601 		 * their ip address/id field changed in the same way).
8017176Syx160601 		 *
8027176Syx160601 		 * The icmp_id field is used by the sender to identify the
8037176Syx160601 		 * process making the icmp request. (the receiver justs
8047176Syx160601 		 * copies it back in its response). So, it closely matches
8057176Syx160601 		 * the concept of source port. We overlay sport, so we can
8067176Syx160601 		 * maximally reuse the existing code.
8077176Syx160601 		 */
8087176Syx160601 		ni.nai_sport = ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id;
8097176Syx160601 		ni.nai_dport = ni.nai_sport;
8107176Syx160601 	}
8117176Syx160601 
8127176Syx160601 	bzero((char *)nat, sizeof (*nat));
8137176Syx160601 	nat->nat_flags = flags;
8147176Syx160601 	nat->nat_redir = np->in_redir;
8157176Syx160601 
8167176Syx160601 	if ((flags & NAT_SLAVE) == 0) {
8177176Syx160601 		MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
8187176Syx160601 	}
8197176Syx160601 
8207176Syx160601 	/*
8217176Syx160601 	 * Search the current table for a match.
8227176Syx160601 	 */
8237176Syx160601 	if (direction == NAT_OUTBOUND) {
8247176Syx160601 		/*
8257176Syx160601 		 * We can now arrange to call this for the same connection
8267176Syx160601 		 * because ipf_nat_new doesn't protect the code path into
8277176Syx160601 		 * this function.
8287176Syx160601 		 */
8297176Syx160601 		natl = nat6_outlookup(fin, nflags, (u_int)fin->fin_p,
8307176Syx160601 		    &fin->fin_src6.in6, &fin->fin_dst6.in6);
8317176Syx160601 		if (natl != NULL) {
8327176Syx160601 			KFREE(nat);
8337176Syx160601 			nat = natl;
8347176Syx160601 			goto done;
8357176Syx160601 		}
8367176Syx160601 
8377176Syx160601 		move = nat6_newmap(fin, nat, &ni);
8387176Syx160601 		if (move == -1)
8397176Syx160601 			goto badnat;
8407176Syx160601 
8417176Syx160601 		np = ni.nai_np;
8427176Syx160601 	} else {
8437176Syx160601 		/*
8447176Syx160601 		 * NAT_INBOUND is used only for redirects rules
8457176Syx160601 		 */
8467176Syx160601 		natl = nat6_inlookup(fin, nflags, (u_int)fin->fin_p,
8477176Syx160601 		    &fin->fin_src6.in6, &fin->fin_dst6.in6);
8487176Syx160601 		if (natl != NULL) {
8497176Syx160601 			KFREE(nat);
8507176Syx160601 			nat = natl;
8517176Syx160601 			goto done;
8527176Syx160601 		}
8537176Syx160601 
8547176Syx160601 		move = nat6_newrdr(fin, nat, &ni);
8557176Syx160601 		if (move == -1)
8567176Syx160601 			goto badnat;
8577176Syx160601 
8587176Syx160601 		np = ni.nai_np;
8597176Syx160601 	}
8607176Syx160601 
8617176Syx160601 	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
8627176Syx160601 		if (np->in_redir == NAT_REDIRECT) {
8637176Syx160601 			nat_delrdr(np);
8647176Syx160601 			nat6_addrdr(np, ifs);
8657176Syx160601 		} else if (np->in_redir == NAT_MAP) {
8667176Syx160601 			nat_delnat(np);
8677176Syx160601 			nat6_addnat(np, ifs);
8687176Syx160601 		}
8697176Syx160601 	}
8707176Syx160601 
8717176Syx160601 	if (nat6_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) {
8727176Syx160601 		goto badnat;
8737176Syx160601 	}
8747176Syx160601 
8757176Syx160601 	nat_calc_chksum_diffs(nat);
8767176Syx160601 
8777176Syx160601 	if (flags & SI_WILDP)
8787176Syx160601 		ifs->ifs_nat_stats.ns_wilds++;
8797176Syx160601 	goto done;
8807176Syx160601 badnat:
8817176Syx160601 	ifs->ifs_nat_stats.ns_badnat++;
8827176Syx160601 	if ((hm = nat->nat_hm) != NULL)
8837176Syx160601 		fr_hostmapdel(&hm);
8847176Syx160601 	KFREE(nat);
8857176Syx160601 	nat = NULL;
8867176Syx160601 done:
8877176Syx160601 	if ((flags & NAT_SLAVE) == 0) {
8887176Syx160601 		MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
8897176Syx160601 	}
8907176Syx160601 	return nat;
8917176Syx160601 }
8927176Syx160601 
8937176Syx160601 
8947176Syx160601 /* ------------------------------------------------------------------------ */
8957176Syx160601 /* Function:    nat6_finalise                                               */
8967176Syx160601 /* Returns:     int - 0 == sucess, -1 == failure                            */
8977176Syx160601 /* Parameters:  fin(I) - pointer to packet information                      */
8987176Syx160601 /*              nat(I) - pointer to NAT entry                               */
8997176Syx160601 /*              ni(I)  - pointer to structure with misc. information needed */
9007176Syx160601 /*                       to create new NAT entry.                           */
9017176Syx160601 /* Write Lock:  ipf_nat                                                     */
9027176Syx160601 /*                                                                          */
9037176Syx160601 /* This is the tail end of constructing a new NAT entry and is the same     */
9047176Syx160601 /* for both IPv4 and IPv6.                                                  */
9057176Syx160601 /* ------------------------------------------------------------------------ */
9067176Syx160601 /*ARGSUSED*/
nat6_finalise(fin,nat,ni,tcp,natsave,direction)9077176Syx160601 static INLINE int nat6_finalise(fin, nat, ni, tcp, natsave, direction)
9087176Syx160601 fr_info_t *fin;
9097176Syx160601 nat_t *nat;
9107176Syx160601 natinfo_t *ni;
9117176Syx160601 tcphdr_t *tcp;
9127176Syx160601 nat_t **natsave;
9137176Syx160601 int direction;
9147176Syx160601 {
9157176Syx160601 	frentry_t *fr;
9167176Syx160601 	ipnat_t *np;
9177176Syx160601 	ipf_stack_t *ifs = fin->fin_ifs;
9187176Syx160601 
9197176Syx160601 	np = ni->nai_np;
9207176Syx160601 
9217176Syx160601 	COPYIFNAME(fin->fin_ifp, nat->nat_ifnames[0], fin->fin_v);
9227176Syx160601 
9237176Syx160601 #ifdef	IPFILTER_SYNC
9247176Syx160601 	if ((nat->nat_flags & SI_CLONE) == 0)
9257176Syx160601 		nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat);
9267176Syx160601 #endif
9277176Syx160601 
9287176Syx160601 	nat->nat_me = natsave;
9297176Syx160601 	nat->nat_dir = direction;
9307176Syx160601 	nat->nat_ifps[0] = np->in_ifps[0];
9317176Syx160601 	nat->nat_ifps[1] = np->in_ifps[1];
9327176Syx160601 	nat->nat_ptr = np;
9337176Syx160601 	nat->nat_p = fin->fin_p;
9347176Syx160601 	nat->nat_v = fin->fin_v;
9357176Syx160601 	nat->nat_mssclamp = np->in_mssclamp;
9367176Syx160601 	fr = fin->fin_fr;
9377176Syx160601 	nat->nat_fr = fr;
9387176Syx160601 	nat->nat_v = 6;
9397176Syx160601 
9407176Syx160601 #ifdef	IPF_V6_PROXIES
9417176Syx160601 	if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0))
9427176Syx160601 		if (appr_new(fin, nat) == -1)
9437176Syx160601 			return -1;
9447176Syx160601 #endif
9457176Syx160601 
9467176Syx160601 	if (nat6_insert(nat, fin->fin_rev, ifs) == 0) {
9477176Syx160601 		if (ifs->ifs_nat_logging)
9487176Syx160601 			nat_log(nat, (u_int)np->in_redir, ifs);
9497176Syx160601 		np->in_use++;
9507176Syx160601 		if (fr != NULL) {
9517176Syx160601 			MUTEX_ENTER(&fr->fr_lock);
9527176Syx160601 			fr->fr_ref++;
9537176Syx160601 			MUTEX_EXIT(&fr->fr_lock);
9547176Syx160601 		}
9557176Syx160601 		return 0;
9567176Syx160601 	}
9577176Syx160601 
9587176Syx160601 	/*
9597176Syx160601 	 * nat6_insert failed, so cleanup time...
9607176Syx160601 	 */
9617176Syx160601 	return -1;
9627176Syx160601 }
9637176Syx160601 
9647176Syx160601 
9657176Syx160601 /* ------------------------------------------------------------------------ */
9667176Syx160601 /* Function:   nat6_insert                                                  */
9677176Syx160601 /* Returns:    int - 0 == sucess, -1 == failure                             */
9687176Syx160601 /* Parameters: nat(I) - pointer to NAT structure                            */
9697176Syx160601 /*             rev(I) - flag indicating forward/reverse direction of packet */
9707176Syx160601 /* Write Lock: ipf_nat                                                      */
9717176Syx160601 /*                                                                          */
9727176Syx160601 /* Insert a NAT entry into the hash tables for searching and add it to the  */
9737176Syx160601 /* list of active NAT entries.  Adjust global counters when complete.       */
9747176Syx160601 /* ------------------------------------------------------------------------ */
nat6_insert(nat,rev,ifs)9757176Syx160601 int nat6_insert(nat, rev, ifs)
9767176Syx160601 nat_t	*nat;
9777176Syx160601 int	rev;
9787176Syx160601 ipf_stack_t *ifs;
9797176Syx160601 {
9807176Syx160601 	u_int hv1, hv2;
9817176Syx160601 	nat_t **natp;
9827176Syx160601 
9837176Syx160601 	/*
9847176Syx160601 	 * Try and return an error as early as possible, so calculate the hash
9857176Syx160601 	 * entry numbers first and then proceed.
9867176Syx160601 	 */
9877176Syx160601 	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
9887176Syx160601 		hv1 = NAT_HASH_FN6(&nat->nat_inip6, nat->nat_inport,
9897176Syx160601 				  0xffffffff);
9907176Syx160601 		hv1 = NAT_HASH_FN6(&nat->nat_oip6, hv1 + nat->nat_oport,
9917176Syx160601 				  ifs->ifs_ipf_nattable_sz);
9927176Syx160601 		hv2 = NAT_HASH_FN6(&nat->nat_outip6, nat->nat_outport,
9937176Syx160601 				  0xffffffff);
9947176Syx160601 		hv2 = NAT_HASH_FN6(&nat->nat_oip6, hv2 + nat->nat_oport,
9957176Syx160601 				  ifs->ifs_ipf_nattable_sz);
9967176Syx160601 	} else {
9977176Syx160601 		hv1 = NAT_HASH_FN6(&nat->nat_inip6, 0, 0xffffffff);
9987176Syx160601 		hv1 = NAT_HASH_FN6(&nat->nat_oip6, hv1,
9997176Syx160601 				  ifs->ifs_ipf_nattable_sz);
10007176Syx160601 		hv2 = NAT_HASH_FN6(&nat->nat_outip6, 0, 0xffffffff);
10017176Syx160601 		hv2 = NAT_HASH_FN6(&nat->nat_oip6, hv2,
10027176Syx160601 				  ifs->ifs_ipf_nattable_sz);
10037176Syx160601 	}
10047176Syx160601 
10057176Syx160601 	if ((ifs->ifs_nat_stats.ns_bucketlen[0][hv1] >=
10067176Syx160601 	    ifs->ifs_fr_nat_maxbucket) ||
10077176Syx160601 	    (ifs->ifs_nat_stats.ns_bucketlen[1][hv2] >=
10087176Syx160601 	    ifs->ifs_fr_nat_maxbucket)) {
10097176Syx160601 		return -1;
10107176Syx160601 	}
10117176Syx160601 
10127176Syx160601 	nat->nat_hv[0] = hv1;
10137176Syx160601 	nat->nat_hv[1] = hv2;
10147176Syx160601 
10157176Syx160601 	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
10167176Syx160601 
10177176Syx160601 	nat->nat_rev = rev;
10187176Syx160601 	nat->nat_ref = 1;
10197176Syx160601 	nat->nat_bytes[0] = 0;
10207176Syx160601 	nat->nat_pkts[0] = 0;
10217176Syx160601 	nat->nat_bytes[1] = 0;
10227176Syx160601 	nat->nat_pkts[1] = 0;
10237176Syx160601 
10247176Syx160601 	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
10257176Syx160601 	nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 6, ifs);
10267176Syx160601 
10277176Syx160601 	if (nat->nat_ifnames[1][0] !='\0') {
10287176Syx160601 		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
10297176Syx160601 		nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 6, ifs);
10307176Syx160601 	} else {
10317176Syx160601 		(void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0],
10327176Syx160601 			       LIFNAMSIZ);
10337176Syx160601 		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
10347176Syx160601 		nat->nat_ifps[1] = nat->nat_ifps[0];
10357176Syx160601 	}
10367176Syx160601 
10377176Syx160601 	nat->nat_next = ifs->ifs_nat_instances;
10387176Syx160601 	nat->nat_pnext = &ifs->ifs_nat_instances;
10397176Syx160601 	if (ifs->ifs_nat_instances)
10407176Syx160601 		ifs->ifs_nat_instances->nat_pnext = &nat->nat_next;
10417176Syx160601 	ifs->ifs_nat_instances = nat;
10427176Syx160601 
10437176Syx160601 	natp = &ifs->ifs_nat_table[0][hv1];
10447176Syx160601 	if (*natp)
10457176Syx160601 		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
10467176Syx160601 	nat->nat_phnext[0] = natp;
10477176Syx160601 	nat->nat_hnext[0] = *natp;
10487176Syx160601 	*natp = nat;
10497176Syx160601 	ifs->ifs_nat_stats.ns_bucketlen[0][hv1]++;
10507176Syx160601 
10517176Syx160601 	natp = &ifs->ifs_nat_table[1][hv2];
10527176Syx160601 	if (*natp)
10537176Syx160601 		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
10547176Syx160601 	nat->nat_phnext[1] = natp;
10557176Syx160601 	nat->nat_hnext[1] = *natp;
10567176Syx160601 	*natp = nat;
10577176Syx160601 	ifs->ifs_nat_stats.ns_bucketlen[1][hv2]++;
10587176Syx160601 
10597176Syx160601 	fr_setnatqueue(nat, rev, ifs);
10607176Syx160601 
10617176Syx160601 	ifs->ifs_nat_stats.ns_added++;
10627176Syx160601 	ifs->ifs_nat_stats.ns_inuse++;
10637176Syx160601 	return 0;
10647176Syx160601 }
10657176Syx160601 
10667176Syx160601 
10677176Syx160601 /* ------------------------------------------------------------------------ */
10687176Syx160601 /* Function:    nat6_icmperrorlookup                                        */
10697176Syx160601 /* Returns:     nat_t* - point to matching NAT structure                    */
10707176Syx160601 /* Parameters:  fin(I) - pointer to packet information                      */
10717176Syx160601 /*              dir(I) - direction of packet (in/out)                       */
10727176Syx160601 /*                                                                          */
10737176Syx160601 /* Check if the ICMP error message is related to an existing TCP, UDP or    */
10747176Syx160601 /* ICMP query nat entry.  It is assumed that the packet is already of the   */
10757176Syx160601 /* the required length.                                                     */
10767176Syx160601 /* ------------------------------------------------------------------------ */
nat6_icmperrorlookup(fin,dir)10777176Syx160601 nat_t *nat6_icmperrorlookup(fin, dir)
10787176Syx160601 fr_info_t *fin;
10797176Syx160601 int dir;
10807176Syx160601 {
10817176Syx160601 	int flags = 0, minlen;
10827176Syx160601 	struct icmp6_hdr *orgicmp;
10837176Syx160601 	tcphdr_t *tcp = NULL;
10847176Syx160601 	u_short data[2];
10857176Syx160601 	nat_t *nat;
10867176Syx160601 	ip6_t *oip6;
10877176Syx160601 	u_int p;
10887176Syx160601 
10897176Syx160601 	minlen = 40;
10907176Syx160601 	/*
10917176Syx160601 	 * Does it at least have the return (basic) IP header ?
10927176Syx160601 	 * Only a basic IP header (no options) should be with an ICMP error
10937176Syx160601 	 * header.  Also, if it's not an error type, then return.
10947176Syx160601 	 */
10957176Syx160601 	if (!(fin->fin_flx & FI_ICMPERR))
10967176Syx160601 		return NULL;
10977176Syx160601 
10987176Syx160601 	/*
10997176Syx160601 	 * Check packet size
11007176Syx160601 	 */
11017176Syx160601 	if (fin->fin_plen < ICMP6ERR_IPICMPHLEN)
11027176Syx160601 		return NULL;
11037176Syx160601 	oip6 = (ip6_t *)((char *)fin->fin_dp + 8);
11047176Syx160601 
11057176Syx160601 	/*
11067176Syx160601 	 * Is the buffer big enough for all of it ?  It's the size of the IP
11077176Syx160601 	 * header claimed in the encapsulated part which is of concern.  It
11087176Syx160601 	 * may be too big to be in this buffer but not so big that it's
11097176Syx160601 	 * outside the ICMP packet, leading to TCP deref's causing problems.
11107176Syx160601 	 * This is possible because we don't know how big oip_hl is when we
11117176Syx160601 	 * do the pullup early in fr_check() and thus can't gaurantee it is
11127176Syx160601 	 * all here now.
11137176Syx160601 	 */
11147176Syx160601 #ifdef  _KERNEL
11157176Syx160601 	{
11167176Syx160601 	mb_t *m;
11177176Syx160601 
11187176Syx160601 	m = fin->fin_m;
11197176Syx160601 # if defined(MENTAT)
11207176Syx160601 	if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr)
11217176Syx160601 		return NULL;
11227176Syx160601 # else
11237176Syx160601 	if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN >
11247176Syx160601 	    (char *)fin->fin_ip + M_LEN(m))
11257176Syx160601 		return NULL;
11267176Syx160601 # endif
11277176Syx160601 	}
11287176Syx160601 #endif
11297176Syx160601 
11307176Syx160601 	if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src))
11317176Syx160601 		return NULL;
11327176Syx160601 
11337176Syx160601 	p = oip6->ip6_nxt;
11347176Syx160601 	if (p == IPPROTO_TCP)
11357176Syx160601 		flags = IPN_TCP;
11367176Syx160601 	else if (p == IPPROTO_UDP)
11377176Syx160601 		flags = IPN_UDP;
11387176Syx160601 	else if (p == IPPROTO_ICMPV6) {
11397176Syx160601 		orgicmp = (struct icmp6_hdr *)(oip6 + 1);
11407176Syx160601 
11417176Syx160601 		/* see if this is related to an ICMP query */
11427176Syx160601 		if (nat_icmpquerytype6(orgicmp->icmp6_type)) {
11437176Syx160601 			data[0] = fin->fin_data[0];
11447176Syx160601 			data[1] = fin->fin_data[1];
11457176Syx160601 			fin->fin_data[0] = 0;
11467176Syx160601 			fin->fin_data[1] = orgicmp->icmp6_id;
11477176Syx160601 
11487176Syx160601 			flags = IPN_ICMPERR|IPN_ICMPQUERY;
11497176Syx160601 			/*
11507176Syx160601 			 * NOTE : dir refers to the direction of the original
11517176Syx160601 			 *        ip packet. By definition the icmp error
11527176Syx160601 			 *        message flows in the opposite direction.
11537176Syx160601 			 */
11547176Syx160601 			if (dir == NAT_INBOUND)
11557176Syx160601 				nat = nat6_inlookup(fin, flags, p,
11567176Syx160601 				    &oip6->ip6_dst, &oip6->ip6_src);
11577176Syx160601 			else
11587176Syx160601 				nat = nat6_outlookup(fin, flags, p,
11597176Syx160601 				    &oip6->ip6_dst, &oip6->ip6_src);
11607176Syx160601 			fin->fin_data[0] = data[0];
11617176Syx160601 			fin->fin_data[1] = data[1];
11627176Syx160601 			return nat;
11637176Syx160601 		}
11647176Syx160601 	}
11657176Syx160601 
11667176Syx160601 	if (flags & IPN_TCPUDP) {
11677176Syx160601 		minlen += 8;		/* + 64bits of data to get ports */
11687176Syx160601 		if (fin->fin_plen < ICMPERR_ICMPHLEN + minlen)
11697176Syx160601 			return NULL;
11707176Syx160601 
11717176Syx160601 		data[0] = fin->fin_data[0];
11727176Syx160601 		data[1] = fin->fin_data[1];
11737176Syx160601 		tcp = (tcphdr_t *)(oip6 + 1);
11747176Syx160601 		fin->fin_data[0] = ntohs(tcp->th_dport);
11757176Syx160601 		fin->fin_data[1] = ntohs(tcp->th_sport);
11767176Syx160601 
11777176Syx160601 		if (dir == NAT_INBOUND) {
11787176Syx160601 			nat = nat6_inlookup(fin, flags, p,
11797176Syx160601 			    &oip6->ip6_dst, &oip6->ip6_src);
11807176Syx160601 		} else {
11817176Syx160601 			nat = nat6_outlookup(fin, flags, p,
11827176Syx160601 			    &oip6->ip6_dst, &oip6->ip6_src);
11837176Syx160601 		}
11847176Syx160601 		fin->fin_data[0] = data[0];
11857176Syx160601 		fin->fin_data[1] = data[1];
11867176Syx160601 		return nat;
11877176Syx160601 	}
11887176Syx160601 	if (dir == NAT_INBOUND)
11897176Syx160601 		return nat6_inlookup(fin, 0, p, &oip6->ip6_dst, &oip6->ip6_src);
11907176Syx160601 	else
11917176Syx160601 		return nat6_outlookup(fin, 0, p, &oip6->ip6_dst,
11927176Syx160601 		    &oip6->ip6_src);
11937176Syx160601 }
11947176Syx160601 
11957176Syx160601 
11967176Syx160601 /* ------------------------------------------------------------------------ */
11977176Syx160601 /* Function:    nat6_icmperror                                              */
11987176Syx160601 /* Returns:     nat_t* - point to matching NAT structure                    */
11997176Syx160601 /* Parameters:  fin(I)    - pointer to packet information                   */
12007176Syx160601 /*              nflags(I) - NAT flags for this packet                       */
12017176Syx160601 /*              dir(I)    - direction of packet (in/out)                    */
12027176Syx160601 /*                                                                          */
12037176Syx160601 /* Fix up an ICMP packet which is an error message for an existing NAT      */
12047176Syx160601 /* session.  This will correct both packet header data and checksums.       */
12057176Syx160601 /*                                                                          */
12067176Syx160601 /* This should *ONLY* be used for incoming ICMP error packets to make sure  */
12077176Syx160601 /* a NAT'd ICMP packet gets correctly recognised.                           */
12087176Syx160601 /* ------------------------------------------------------------------------ */
nat6_icmperror(fin,nflags,dir)12097176Syx160601 nat_t *nat6_icmperror(fin, nflags, dir)
12107176Syx160601 fr_info_t *fin;
12117176Syx160601 u_int *nflags;
12127176Syx160601 int dir;
12137176Syx160601 {
12147176Syx160601 	u_32_t sum1, sum2, sumd, psum1, psum2, psumd, sumd1;
12157176Syx160601 	i6addr_t in;
12167176Syx160601 	struct icmp6_hdr *icmp6, *orgicmp;
12177176Syx160601 	int dlen;
12187176Syx160601 	udphdr_t *udp;
12197176Syx160601 	tcphdr_t *tcp;
12207176Syx160601 	nat_t *nat;
12217176Syx160601 	ip6_t *oip6;
12227176Syx160601 	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY)))
12237176Syx160601 		return NULL;
12247176Syx160601 
12257176Syx160601 	/*
12267176Syx160601 	 * nat6_icmperrorlookup() looks up nat entry associated with the
12277176Syx160601 	 * offending IP packet and returns pointer to the entry, or NULL
12287176Syx160601 	 * if packet wasn't natted or for `defective' packets.
12297176Syx160601 	 */
12307176Syx160601 
12317176Syx160601 	if ((fin->fin_v != 6) || !(nat = nat6_icmperrorlookup(fin, dir)))
12327176Syx160601 		return NULL;
12337176Syx160601 
12347176Syx160601 	sumd1 = 0;
12357176Syx160601 	*nflags = IPN_ICMPERR;
12367176Syx160601 	icmp6 = fin->fin_dp;
12377176Syx160601 	oip6 = (ip6_t *)((char *)icmp6 + sizeof (*icmp6));
12387176Syx160601 	udp = (udphdr_t *)(((char *)oip6) + sizeof (*oip6));
12397176Syx160601 	tcp = (tcphdr_t *)udp;
12407176Syx160601 	dlen = fin->fin_plen - ((char *)udp - (char *)fin->fin_ip);
12417176Syx160601 
12427176Syx160601 	/*
12437176Syx160601 	 * Need to adjust ICMP header to include the real IP#'s and
12447176Syx160601 	 * port #'s.  There are three steps required.
12457176Syx160601 	 *
12467176Syx160601 	 * Step 1
12477176Syx160601 	 * No update needed for ip6 header checksum.
12487176Syx160601 	 *
12497176Syx160601 	 * Unlike IPv4, we need to update icmp_cksum for IPv6 address
12507176Syx160601 	 * changes because there's no ip_sum change to cancel it.
12517176Syx160601 	 */
12527176Syx160601 
12537176Syx160601 	if (IP6_EQ((i6addr_t *)&oip6->ip6_dst, &nat->nat_oip6)) {
12547176Syx160601 		sum1 = LONG_SUM6((i6addr_t *)&oip6->ip6_src);
12557176Syx160601 		in = nat->nat_inip6;
12567176Syx160601 		oip6->ip6_src = in.in6;
12577176Syx160601 	} else {
12587176Syx160601 		sum1 = LONG_SUM6((i6addr_t *)&oip6->ip6_dst);
12597176Syx160601 		in = nat->nat_outip6;
12607176Syx160601 		oip6->ip6_dst = in.in6;
12617176Syx160601 	}
12627176Syx160601 
12637176Syx160601 	sum2 = LONG_SUM6(&in);
12647176Syx160601 	CALC_SUMD(sum1, sum2, sumd);
12657176Syx160601 
12667176Syx160601 	/*
12677176Syx160601 	 * Step 2
12687176Syx160601 	 * Perform other adjustments based on protocol of offending packet.
12697176Syx160601 	 */
12707176Syx160601 
12717176Syx160601 	switch (oip6->ip6_nxt) {
12727176Syx160601 		case IPPROTO_TCP :
12737176Syx160601 		case IPPROTO_UDP :
12747176Syx160601 
12757176Syx160601 			/*
12767176Syx160601 			* For offending TCP/UDP IP packets, translate the ports
12777176Syx160601 			* based on the NAT specification.
12787176Syx160601 			*
12797176Syx160601 			* Advance notice : Now it becomes complicated :-)
12807176Syx160601 			*
12817176Syx160601 			* Since the port and IP addresse fields are both part
12827176Syx160601 			* of the TCP/UDP checksum of the offending IP packet,
12837176Syx160601 			* we need to adjust that checksum as well.
12847176Syx160601 			*
12857176Syx160601 			* To further complicate things, the TCP/UDP checksum
12867176Syx160601 			* may not be present.  We must check to see if the
12877176Syx160601 			* length of the data portion is big enough to hold
12887176Syx160601 			* the checksum.  In the UDP case, a test to determine
12897176Syx160601 			* if the checksum is even set is also required.
12907176Syx160601 			*
12917176Syx160601 			* Any changes to an IP address, port or checksum within
12927176Syx160601 			* the ICMP packet requires a change to icmp_cksum.
12937176Syx160601 			*
12947176Syx160601 			* Be extremely careful here ... The change is dependent
12957176Syx160601 			* upon whether or not the TCP/UPD checksum is present.
12967176Syx160601 			*
12977176Syx160601 			* If TCP/UPD checksum is present, the icmp_cksum must
12987176Syx160601 			* compensate for checksum modification resulting from
12997176Syx160601 			* IP address change only.  Port change and resulting
13007176Syx160601 			* data checksum adjustments cancel each other out.
13017176Syx160601 			*
13027176Syx160601 			* If TCP/UDP checksum is not present, icmp_cksum must
13037176Syx160601 			* compensate for port change only.  The IP address
13047176Syx160601 			* change does not modify anything else in this case.
13057176Syx160601 			*/
13067176Syx160601 
13077176Syx160601 			psum1 = 0;
13087176Syx160601 			psum2 = 0;
13097176Syx160601 			psumd = 0;
13107176Syx160601 
13117176Syx160601 			if ((tcp->th_dport == nat->nat_oport) &&
13127176Syx160601 			    (tcp->th_sport != nat->nat_inport)) {
13137176Syx160601 
13147176Syx160601 				/*
13157176Syx160601 				 * Translate the source port.
13167176Syx160601 				 */
13177176Syx160601 
13187176Syx160601 				psum1 = ntohs(tcp->th_sport);
13197176Syx160601 				psum2 = ntohs(nat->nat_inport);
13207176Syx160601 				tcp->th_sport = nat->nat_inport;
13217176Syx160601 
13227176Syx160601 			} else if ((tcp->th_sport == nat->nat_oport) &&
13237176Syx160601 				    (tcp->th_dport != nat->nat_outport)) {
13247176Syx160601 
13257176Syx160601 				/*
13267176Syx160601 				 * Translate the destination port.
13277176Syx160601 				 */
13287176Syx160601 
13297176Syx160601 				psum1 = ntohs(tcp->th_dport);
13307176Syx160601 				psum2 = ntohs(nat->nat_outport);
13317176Syx160601 				tcp->th_dport = nat->nat_outport;
13327176Syx160601 			}
13337176Syx160601 
13347176Syx160601 			if ((oip6->ip6_nxt == IPPROTO_TCP) && (dlen >= 18)) {
13357176Syx160601 
13367176Syx160601 				/*
13377176Syx160601 				 * TCP checksum present.
13387176Syx160601 				 *
13397176Syx160601 				 * Adjust data checksum and icmp checksum to
13407176Syx160601 				 * compensate for any IP address change.
13417176Syx160601 				 */
13427176Syx160601 
13437176Syx160601 				sum1 = ntohs(tcp->th_sum);
13447176Syx160601 				fix_datacksum(&tcp->th_sum, sumd);
13457176Syx160601 				sum2 = ntohs(tcp->th_sum);
13467176Syx160601 				CALC_SUMD(sum1, sum2, sumd);
13477176Syx160601 				sumd1 += sumd;
13487176Syx160601 
13497176Syx160601 				/*
13507176Syx160601 				 * Also make data checksum adjustment to
13517176Syx160601 				 * compensate for any port change.
13527176Syx160601 				 */
13537176Syx160601 
13547176Syx160601 				if (psum1 != psum2) {
13557176Syx160601 					CALC_SUMD(psum1, psum2, psumd);
13567176Syx160601 					fix_datacksum(&tcp->th_sum, psumd);
13577176Syx160601 				}
13587176Syx160601 
13597176Syx160601 			} else if ((oip6->ip6_nxt == IPPROTO_UDP) &&
13607176Syx160601 				   (dlen >= 8) && (udp->uh_sum != 0)) {
13617176Syx160601 
13627176Syx160601 				/*
13637176Syx160601 				 * The UDP checksum is present and set.
13647176Syx160601 				 *
13657176Syx160601 				 * Adjust data checksum and icmp checksum to
13667176Syx160601 				 * compensate for any IP address change.
13677176Syx160601 				 */
13687176Syx160601 
13697176Syx160601 				sum1 = ntohs(udp->uh_sum);
13707176Syx160601 				fix_datacksum(&udp->uh_sum, sumd);
13717176Syx160601 				sum2 = ntohs(udp->uh_sum);
13727176Syx160601 				CALC_SUMD(sum1, sum2, sumd);
13737176Syx160601 				sumd1 += sumd;
13747176Syx160601 
13757176Syx160601 				/*
13767176Syx160601 				 * Also make data checksum adjustment to
13777176Syx160601 				 * compensate for any port change.
13787176Syx160601 				 */
13797176Syx160601 
13807176Syx160601 				if (psum1 != psum2) {
13817176Syx160601 					CALC_SUMD(psum1, psum2, psumd);
13827176Syx160601 					fix_datacksum(&udp->uh_sum, psumd);
13837176Syx160601 				}
13847176Syx160601 
13857176Syx160601 			} else {
13867176Syx160601 
13877176Syx160601 				/*
13887176Syx160601 				 * Data checksum was not present.
13897176Syx160601 				 *
13907176Syx160601 				 * Compensate for any port change.
13917176Syx160601 				 */
13927176Syx160601 
13937176Syx160601 				CALC_SUMD(psum2, psum1, psumd);
13947176Syx160601 				sumd1 += psumd;
13957176Syx160601 			}
13967176Syx160601 			break;
13977176Syx160601 
13987176Syx160601 		case IPPROTO_ICMPV6 :
13997176Syx160601 
14007176Syx160601 			orgicmp = (struct icmp6_hdr *)udp;
14017176Syx160601 
14027176Syx160601 			if ((nat->nat_dir == NAT_OUTBOUND) &&
14037176Syx160601 			    (orgicmp->icmp6_id != nat->nat_inport) &&
14047176Syx160601 			    (dlen >= 8)) {
14057176Syx160601 
14067176Syx160601 				/*
14077176Syx160601 				 * Fix ICMP checksum (of the offening ICMP
14087176Syx160601 				 * query packet) to compensate the change
14097176Syx160601 				 * in the ICMP id of the offending ICMP
14107176Syx160601 				 * packet.
14117176Syx160601 				 *
14127176Syx160601 				 * Since you modify orgicmp->icmp_id with
14137176Syx160601 				 * a delta (say x) and you compensate that
14147176Syx160601 				 * in origicmp->icmp_cksum with a delta
14157176Syx160601 				 * minus x, you don't have to adjust the
14167176Syx160601 				 * overall icmp->icmp_cksum
14177176Syx160601 				 */
14187176Syx160601 
14197176Syx160601 				sum1 = ntohs(orgicmp->icmp6_id);
14207176Syx160601 				sum2 = ntohs(nat->nat_inport);
14217176Syx160601 				CALC_SUMD(sum1, sum2, sumd);
14227176Syx160601 				orgicmp->icmp6_id = nat->nat_inport;
14237176Syx160601 				fix_datacksum(&orgicmp->icmp6_cksum, sumd);
14247176Syx160601 
14257176Syx160601 			} /* nat_dir can't be NAT_INBOUND for icmp queries */
14267176Syx160601 
14277176Syx160601 			break;
14287176Syx160601 
14297176Syx160601 		default :
14307176Syx160601 
14317176Syx160601 			break;
14327176Syx160601 
14337176Syx160601 	} /* switch (oip6->ip6_nxt) */
14347176Syx160601 
14357176Syx160601 	/*
14367176Syx160601 	 * Step 3
14377176Syx160601 	 * Make the adjustments to icmp checksum.
14387176Syx160601 	 */
14397176Syx160601 
14407176Syx160601 	if (sumd1 != 0) {
14417176Syx160601 		sumd1 = (sumd1 & 0xffff) + (sumd1 >> 16);
14427176Syx160601 		sumd1 = (sumd1 & 0xffff) + (sumd1 >> 16);
14437176Syx160601 		fix_incksum(&icmp6->icmp6_cksum, sumd1);
14447176Syx160601 	}
14457176Syx160601 	return nat;
14467176Syx160601 }
14477176Syx160601 
14487176Syx160601 
14497176Syx160601 /*
14507176Syx160601  * NB: these lookups don't lock access to the list, it assumed that it has
14517176Syx160601  * already been done!
14527176Syx160601  */
14537176Syx160601 
14547176Syx160601 /* ------------------------------------------------------------------------ */
14557176Syx160601 /* Function:    nat6_inlookup                                               */
14567176Syx160601 /* Returns:     nat_t* - NULL == no match,                                  */
14577176Syx160601 /*                       else pointer to matching NAT entry                 */
14587176Syx160601 /* Parameters:  fin(I)    - pointer to packet information                   */
14597176Syx160601 /*              flags(I)  - NAT flags for this packet                       */
14607176Syx160601 /*              p(I)      - protocol for this packet                        */
14617176Syx160601 /*              src(I)    - source IP address                               */
14627176Syx160601 /*              mapdst(I) - destination IP address                          */
14637176Syx160601 /*                                                                          */
14647176Syx160601 /* Lookup a nat entry based on the mapped destination ip address/port and   */
14657176Syx160601 /* real source address/port.  We use this lookup when receiving a packet,   */
14667176Syx160601 /* we're looking for a table entry, based on the destination address.       */
14677176Syx160601 /*                                                                          */
14687176Syx160601 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
14697176Syx160601 /*                                                                          */
14707176Syx160601 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
14717176Syx160601 /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
14727176Syx160601 /*                                                                          */
14737176Syx160601 /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
14747176Syx160601 /*            the packet is of said protocol                                */
14757176Syx160601 /* ------------------------------------------------------------------------ */
nat6_inlookup(fin,flags,p,src,mapdst)14767176Syx160601 nat_t *nat6_inlookup(fin, flags, p, src, mapdst)
14777176Syx160601 fr_info_t *fin;
14787176Syx160601 u_int flags, p;
14797176Syx160601 struct in6_addr *src, *mapdst;
14807176Syx160601 {
14817176Syx160601 	u_short sport, dport;
14827176Syx160601 	u_int sflags;
14837176Syx160601 	nat_t *nat;
14847176Syx160601 	int nflags;
14857176Syx160601 	i6addr_t dst;
14867176Syx160601 	void *ifp;
14877176Syx160601 	u_int hv;
14887176Syx160601 	ipf_stack_t *ifs = fin->fin_ifs;
14897176Syx160601 
14907176Syx160601 	if (fin != NULL)
14917176Syx160601 		ifp = fin->fin_ifp;
14927176Syx160601 	else
14937176Syx160601 		ifp = NULL;
14947176Syx160601 	sport = 0;
14957176Syx160601 	dport = 0;
14967176Syx160601 	dst.in6 = *mapdst;
14977176Syx160601 	sflags = flags & NAT_TCPUDPICMP;
14987176Syx160601 
14997176Syx160601 	switch (p)
15007176Syx160601 	{
15017176Syx160601 	case IPPROTO_TCP :
15027176Syx160601 	case IPPROTO_UDP :
15037176Syx160601 		sport = htons(fin->fin_data[0]);
15047176Syx160601 		dport = htons(fin->fin_data[1]);
15057176Syx160601 		break;
15067176Syx160601 	case IPPROTO_ICMPV6 :
15077176Syx160601 		if (flags & IPN_ICMPERR)
15087176Syx160601 			sport = fin->fin_data[1];
15097176Syx160601 		else
15107176Syx160601 			dport = fin->fin_data[1];
15117176Syx160601 		break;
15127176Syx160601 	default :
15137176Syx160601 		break;
15147176Syx160601 	}
15157176Syx160601 
15167176Syx160601 
15177176Syx160601 	if ((flags & SI_WILDP) != 0)
15187176Syx160601 		goto find_in_wild_ports;
15197176Syx160601 
15207176Syx160601 	hv = NAT_HASH_FN6(&dst, dport, 0xffffffff);
15217176Syx160601 	hv = NAT_HASH_FN6(src, hv + sport, ifs->ifs_ipf_nattable_sz);
15227176Syx160601 	nat = ifs->ifs_nat_table[1][hv];
15237176Syx160601 	for (; nat; nat = nat->nat_hnext[1]) {
15247176Syx160601 		if (nat->nat_v != 6)
15257176Syx160601 			continue;
15267176Syx160601 
15277176Syx160601 		if (nat->nat_ifps[0] != NULL) {
15287176Syx160601 			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
15297176Syx160601 				continue;
15307176Syx160601 		} else if (ifp != NULL)
15317176Syx160601 			nat->nat_ifps[0] = ifp;
15327176Syx160601 
15337176Syx160601 		nflags = nat->nat_flags;
15347176Syx160601 
15357176Syx160601 		if (IP6_EQ(&nat->nat_oip6, src) &&
15367176Syx160601 		    IP6_EQ(&nat->nat_outip6, &dst) &&
15377176Syx160601 		    (((p == 0) &&
15387176Syx160601 		    (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) ||
15397176Syx160601 		    (p == nat->nat_p))) {
15407176Syx160601 			switch (p)
15417176Syx160601 			{
15427176Syx160601 #if 0
15437176Syx160601 			case IPPROTO_GRE :
15447176Syx160601 				if (nat->nat_call[1] != fin->fin_data[0])
15457176Syx160601 					continue;
15467176Syx160601 				break;
15477176Syx160601 #endif
15487176Syx160601 			case IPPROTO_ICMPV6 :
15497176Syx160601 				if ((flags & IPN_ICMPERR) != 0) {
15507176Syx160601 					if (nat->nat_outport != sport)
15517176Syx160601 						continue;
15527176Syx160601 				} else {
15537176Syx160601 					if (nat->nat_outport != dport)
15547176Syx160601 						continue;
15557176Syx160601 				}
15567176Syx160601 				break;
15577176Syx160601 			case IPPROTO_TCP :
15587176Syx160601 			case IPPROTO_UDP :
15597176Syx160601 				if (nat->nat_oport != sport)
15607176Syx160601 					continue;
15617176Syx160601 				if (nat->nat_outport != dport)
15627176Syx160601 					continue;
15637176Syx160601 				break;
15647176Syx160601 			default :
15657176Syx160601 				break;
15667176Syx160601 			}
15677176Syx160601 
15687176Syx160601 #ifdef	IPF_V6_PROXIES
15697176Syx160601 			ipn = nat->nat_ptr;
15707176Syx160601 			if ((ipn != NULL) && (nat->nat_aps != NULL))
15717176Syx160601 				if (appr_match(fin, nat) != 0)
15727176Syx160601 					continue;
15737176Syx160601 #endif
15747176Syx160601 			return nat;
15757176Syx160601 		}
15767176Syx160601 	}
15777176Syx160601 
15787176Syx160601 	/*
15797176Syx160601 	 * So if we didn't find it but there are wildcard members in the hash
15807176Syx160601 	 * table, go back and look for them.  We do this search and update here
15817176Syx160601 	 * because it is modifying the NAT table and we want to do this only
15827176Syx160601 	 * for the first packet that matches.  The exception, of course, is
15837176Syx160601 	 * for "dummy" (FI_IGNORE) lookups.
15847176Syx160601 	 */
15857176Syx160601 find_in_wild_ports:
15867176Syx160601 	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
15877176Syx160601 		return NULL;
15887176Syx160601 	if (ifs->ifs_nat_stats.ns_wilds == 0)
15897176Syx160601 		return NULL;
15907176Syx160601 
15917176Syx160601 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
15927176Syx160601 
15937176Syx160601 	hv = NAT_HASH_FN6(&dst, 0, 0xffffffff);
15947176Syx160601 	hv = NAT_HASH_FN6(src, hv, ifs->ifs_ipf_nattable_sz);
15957176Syx160601 
15967176Syx160601 	WRITE_ENTER(&ifs->ifs_ipf_nat);
15977176Syx160601 
15987176Syx160601 	nat = ifs->ifs_nat_table[1][hv];
15997176Syx160601 	for (; nat; nat = nat->nat_hnext[1]) {
16007176Syx160601 		if (nat->nat_v != 6)
16017176Syx160601 			continue;
16027176Syx160601 
16037176Syx160601 		if (nat->nat_ifps[0] != NULL) {
16047176Syx160601 			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
16057176Syx160601 				continue;
16067176Syx160601 		} else if (ifp != NULL)
16077176Syx160601 			nat->nat_ifps[0] = ifp;
16087176Syx160601 
16097176Syx160601 		if (nat->nat_p != fin->fin_p)
16107176Syx160601 			continue;
16117176Syx160601 		if (IP6_NEQ(&nat->nat_oip6, src) ||
16127176Syx160601 		    IP6_NEQ(&nat->nat_outip6, &dst))
16137176Syx160601 			continue;
16147176Syx160601 
16157176Syx160601 		nflags = nat->nat_flags;
16167176Syx160601 		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
16177176Syx160601 			continue;
16187176Syx160601 
16197176Syx160601 		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
16207176Syx160601 			       NAT_INBOUND) == 1) {
16217176Syx160601 			if ((fin->fin_flx & FI_IGNORE) != 0)
16227176Syx160601 				break;
16237176Syx160601 			if ((nflags & SI_CLONE) != 0) {
16247176Syx160601 				nat = fr_natclone(fin, nat);
16257176Syx160601 				if (nat == NULL)
16267176Syx160601 					break;
16277176Syx160601 			} else {
16287176Syx160601 				MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
16297176Syx160601 				ifs->ifs_nat_stats.ns_wilds--;
16307176Syx160601 				MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
16317176Syx160601 			}
16327176Syx160601 			nat->nat_oport = sport;
16337176Syx160601 			nat->nat_outport = dport;
16347176Syx160601 			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
16357176Syx160601 			nat6_tabmove(nat, ifs);
16367176Syx160601 			break;
16377176Syx160601 		}
16387176Syx160601 	}
16397176Syx160601 
16407176Syx160601 	MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
16417176Syx160601 
16427176Syx160601 	return nat;
16437176Syx160601 }
16447176Syx160601 
16457176Syx160601 
16467176Syx160601 /* ------------------------------------------------------------------------ */
16477176Syx160601 /* Function:    nat6_tabmove                                                */
16487176Syx160601 /* Returns:     Nil                                                         */
16497176Syx160601 /* Parameters:  nat(I) - pointer to NAT structure                           */
16507176Syx160601 /* Write Lock:  ipf_nat                                                     */
16517176Syx160601 /*                                                                          */
16527176Syx160601 /* This function is only called for TCP/UDP NAT table entries where the     */
16537176Syx160601 /* original was placed in the table without hashing on the ports and we now */
16547176Syx160601 /* want to include hashing on port numbers.                                 */
16557176Syx160601 /* ------------------------------------------------------------------------ */
nat6_tabmove(nat,ifs)16567176Syx160601 static void nat6_tabmove(nat, ifs)
16577176Syx160601 nat_t *nat;
16587176Syx160601 ipf_stack_t *ifs;
16597176Syx160601 {
16607176Syx160601 	nat_t **natp;
16617176Syx160601 	u_int hv;
16627176Syx160601 
16637176Syx160601 	if (nat->nat_flags & SI_CLONE)
16647176Syx160601 		return;
16657176Syx160601 
16667176Syx160601 	/*
16677176Syx160601 	 * Remove the NAT entry from the old location
16687176Syx160601 	 */
16697176Syx160601 	if (nat->nat_hnext[0])
16707176Syx160601 		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
16717176Syx160601 	*nat->nat_phnext[0] = nat->nat_hnext[0];
16727176Syx160601 	ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
16737176Syx160601 
16747176Syx160601 	if (nat->nat_hnext[1])
16757176Syx160601 		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
16767176Syx160601 	*nat->nat_phnext[1] = nat->nat_hnext[1];
16777176Syx160601 	ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
16787176Syx160601 
16797176Syx160601 	/*
16807176Syx160601 	 * Add into the NAT table in the new position
16817176Syx160601 	 */
16827176Syx160601 	hv = NAT_HASH_FN6(&nat->nat_inip6, nat->nat_inport, 0xffffffff);
16837176Syx160601 	hv = NAT_HASH_FN6(&nat->nat_oip6, hv + nat->nat_oport,
16847176Syx160601 			 ifs->ifs_ipf_nattable_sz);
16857176Syx160601 	nat->nat_hv[0] = hv;
16867176Syx160601 	natp = &ifs->ifs_nat_table[0][hv];
16877176Syx160601 	if (*natp)
16887176Syx160601 		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
16897176Syx160601 	nat->nat_phnext[0] = natp;
16907176Syx160601 	nat->nat_hnext[0] = *natp;
16917176Syx160601 	*natp = nat;
16927176Syx160601 	ifs->ifs_nat_stats.ns_bucketlen[0][hv]++;
16937176Syx160601 
16947176Syx160601 	hv = NAT_HASH_FN6(&nat->nat_outip6, nat->nat_outport, 0xffffffff);
16957176Syx160601 	hv = NAT_HASH_FN6(&nat->nat_oip6, hv + nat->nat_oport,
16967176Syx160601 			 ifs->ifs_ipf_nattable_sz);
16977176Syx160601 	nat->nat_hv[1] = hv;
16987176Syx160601 	natp = &ifs->ifs_nat_table[1][hv];
16997176Syx160601 	if (*natp)
17007176Syx160601 		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
17017176Syx160601 	nat->nat_phnext[1] = natp;
17027176Syx160601 	nat->nat_hnext[1] = *natp;
17037176Syx160601 	*natp = nat;
17047176Syx160601 	ifs->ifs_nat_stats.ns_bucketlen[1][hv]++;
17057176Syx160601 }
17067176Syx160601 
17077176Syx160601 
17087176Syx160601 /* ------------------------------------------------------------------------ */
17097176Syx160601 /* Function:    nat6_outlookup                                              */
17107176Syx160601 /* Returns:     nat_t* - NULL == no match,                                  */
17117176Syx160601 /*                       else pointer to matching NAT entry                 */
17127176Syx160601 /* Parameters:  fin(I)   - pointer to packet information                    */
17137176Syx160601 /*              flags(I) - NAT flags for this packet                        */
17147176Syx160601 /*              p(I)     - protocol for this packet                         */
17157176Syx160601 /*              src(I)   - source IP address                                */
17167176Syx160601 /*              dst(I)   - destination IP address                           */
17177176Syx160601 /*              rw(I)    - 1 == write lock on ipf_nat held, 0 == read lock. */
17187176Syx160601 /*                                                                          */
17197176Syx160601 /* Lookup a nat entry based on the source 'real' ip address/port and        */
17207176Syx160601 /* destination address/port.  We use this lookup when sending a packet out, */
17217176Syx160601 /* we're looking for a table entry, based on the source address.            */
17227176Syx160601 /*                                                                          */
17237176Syx160601 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
17247176Syx160601 /*                                                                          */
17257176Syx160601 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
17267176Syx160601 /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
17277176Syx160601 /*                                                                          */
17287176Syx160601 /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
17297176Syx160601 /*            the packet is of said protocol                                */
17307176Syx160601 /* ------------------------------------------------------------------------ */
nat6_outlookup(fin,flags,p,src,dst)17317176Syx160601 nat_t *nat6_outlookup(fin, flags, p, src, dst)
17327176Syx160601 fr_info_t *fin;
17337176Syx160601 u_int flags, p;
17347176Syx160601 struct in6_addr *src , *dst;
17357176Syx160601 {
17367176Syx160601 	u_short sport, dport;
17377176Syx160601 	u_int sflags;
17387176Syx160601 	nat_t *nat;
17397176Syx160601 	int nflags;
17407176Syx160601 	void *ifp;
17417176Syx160601 	u_int hv;
17427176Syx160601 	ipf_stack_t *ifs = fin->fin_ifs;
17437176Syx160601 
17447176Syx160601 	ifp = fin->fin_ifp;
17457176Syx160601 
17467176Syx160601 	sflags = flags & IPN_TCPUDPICMP;
17477176Syx160601 	sport = 0;
17487176Syx160601 	dport = 0;
17497176Syx160601 
17507176Syx160601 	switch (p)
17517176Syx160601 	{
17527176Syx160601 	case IPPROTO_TCP :
17537176Syx160601 	case IPPROTO_UDP :
17547176Syx160601 		sport = htons(fin->fin_data[0]);
17557176Syx160601 		dport = htons(fin->fin_data[1]);
17567176Syx160601 		break;
17577176Syx160601 	case IPPROTO_ICMPV6 :
17587176Syx160601 		if (flags & IPN_ICMPERR)
17597176Syx160601 			sport = fin->fin_data[1];
17607176Syx160601 		else
17617176Syx160601 			dport = fin->fin_data[1];
17627176Syx160601 		break;
17637176Syx160601 	default :
17647176Syx160601 		break;
17657176Syx160601 	}
17667176Syx160601 
17677176Syx160601 	if ((flags & SI_WILDP) != 0)
17687176Syx160601 		goto find_out_wild_ports;
17697176Syx160601 
17707176Syx160601 	hv = NAT_HASH_FN6(src, sport, 0xffffffff);
17717176Syx160601 	hv = NAT_HASH_FN6(dst, hv + dport, ifs->ifs_ipf_nattable_sz);
17727176Syx160601 	nat = ifs->ifs_nat_table[0][hv];
17737176Syx160601 	for (; nat; nat = nat->nat_hnext[0]) {
17747176Syx160601 		if (nat->nat_v != 6)
17757176Syx160601 			continue;
17767176Syx160601 
17777176Syx160601 		if (nat->nat_ifps[1] != NULL) {
17787176Syx160601 			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
17797176Syx160601 				continue;
17807176Syx160601 		} else if (ifp != NULL)
17817176Syx160601 			nat->nat_ifps[1] = ifp;
17827176Syx160601 
17837176Syx160601 		nflags = nat->nat_flags;
17847176Syx160601 
17857176Syx160601 		if (IP6_EQ(&nat->nat_inip6, src) &&
17867176Syx160601 		    IP6_EQ(&nat->nat_oip6, dst) &&
17877176Syx160601 		    (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) ||
17887176Syx160601 		    (p == nat->nat_p))) {
17897176Syx160601 			switch (p)
17907176Syx160601 			{
17917176Syx160601 #if 0
17927176Syx160601 			case IPPROTO_GRE :
17937176Syx160601 				if (nat->nat_call[1] != fin->fin_data[0])
17947176Syx160601 					continue;
17957176Syx160601 				break;
17967176Syx160601 #endif
17977176Syx160601 			case IPPROTO_TCP :
17987176Syx160601 			case IPPROTO_UDP :
17997176Syx160601 				if (nat->nat_oport != dport)
18007176Syx160601 					continue;
18017176Syx160601 				if (nat->nat_inport != sport)
18027176Syx160601 					continue;
18037176Syx160601 				break;
18047176Syx160601 			default :
18057176Syx160601 				break;
18067176Syx160601 			}
18077176Syx160601 
18087176Syx160601 #ifdef	IPF_V6_PROXIES
18097176Syx160601 			ipn = nat->nat_ptr;
18107176Syx160601 			if ((ipn != NULL) && (nat->nat_aps != NULL))
18117176Syx160601 				if (appr_match(fin, nat) != 0)
18127176Syx160601 					continue;
18137176Syx160601 #endif
18147176Syx160601 			return nat;
18157176Syx160601 		}
18167176Syx160601 	}
18177176Syx160601 
18187176Syx160601 	/*
18197176Syx160601 	 * So if we didn't find it but there are wildcard members in the hash
18207176Syx160601 	 * table, go back and look for them.  We do this search and update here
18217176Syx160601 	 * because it is modifying the NAT table and we want to do this only
18227176Syx160601 	 * for the first packet that matches.  The exception, of course, is
18237176Syx160601 	 * for "dummy" (FI_IGNORE) lookups.
18247176Syx160601 	 */
18257176Syx160601 find_out_wild_ports:
18267176Syx160601 	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
18277176Syx160601 		return NULL;
18287176Syx160601 	if (ifs->ifs_nat_stats.ns_wilds == 0)
18297176Syx160601 		return NULL;
18307176Syx160601 
18317176Syx160601 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
18327176Syx160601 
18337176Syx160601 	hv = NAT_HASH_FN6(src, 0, 0xffffffff);
18347176Syx160601 	hv = NAT_HASH_FN6(dst, hv, ifs->ifs_ipf_nattable_sz);
18357176Syx160601 
18367176Syx160601 	WRITE_ENTER(&ifs->ifs_ipf_nat);
18377176Syx160601 
18387176Syx160601 	nat = ifs->ifs_nat_table[0][hv];
18397176Syx160601 	for (; nat; nat = nat->nat_hnext[0]) {
18407176Syx160601 		if (nat->nat_v != 6)
18417176Syx160601 			continue;
18427176Syx160601 
18437176Syx160601 		if (nat->nat_ifps[1] != NULL) {
18447176Syx160601 			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
18457176Syx160601 				continue;
18467176Syx160601 		} else if (ifp != NULL)
18477176Syx160601 			nat->nat_ifps[1] = ifp;
18487176Syx160601 
18497176Syx160601 		if (nat->nat_p != fin->fin_p)
18507176Syx160601 			continue;
18517176Syx160601 		if (IP6_NEQ(&nat->nat_inip6, src) ||
18527176Syx160601 		    IP6_NEQ(&nat->nat_oip6, dst))
18537176Syx160601 			continue;
18547176Syx160601 
18557176Syx160601 		nflags = nat->nat_flags;
18567176Syx160601 		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
18577176Syx160601 			continue;
18587176Syx160601 
18597176Syx160601 		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
18607176Syx160601 			       NAT_OUTBOUND) == 1) {
18617176Syx160601 			if ((fin->fin_flx & FI_IGNORE) != 0)
18627176Syx160601 				break;
18637176Syx160601 			if ((nflags & SI_CLONE) != 0) {
18647176Syx160601 				nat = fr_natclone(fin, nat);
18657176Syx160601 				if (nat == NULL)
18667176Syx160601 					break;
18677176Syx160601 			} else {
18687176Syx160601 				MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
18697176Syx160601 				ifs->ifs_nat_stats.ns_wilds--;
18707176Syx160601 				MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
18717176Syx160601 			}
18727176Syx160601 			nat->nat_inport = sport;
18737176Syx160601 			nat->nat_oport = dport;
18747176Syx160601 			if (nat->nat_outport == 0)
18757176Syx160601 				nat->nat_outport = sport;
18767176Syx160601 			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
18777176Syx160601 			nat6_tabmove(nat, ifs);
18787176Syx160601 			break;
18797176Syx160601 		}
18807176Syx160601 	}
18817176Syx160601 
18827176Syx160601 	MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
18837176Syx160601 
18847176Syx160601 	return nat;
18857176Syx160601 }
18867176Syx160601 
18877176Syx160601 
18887176Syx160601 /* ------------------------------------------------------------------------ */
18897176Syx160601 /* Function:    nat6_lookupredir                                            */
18907176Syx160601 /* Returns:     nat_t* - NULL == no match,                                  */
18917176Syx160601 /*                       else pointer to matching NAT entry                 */
18927176Syx160601 /* Parameters:  np(I) - pointer to description of packet to find NAT table  */
18937176Syx160601 /*                      entry for.                                          */
18947176Syx160601 /*                                                                          */
18957176Syx160601 /* Lookup the NAT tables to search for a matching redirect                  */
18967176Syx160601 /* ------------------------------------------------------------------------ */
nat6_lookupredir(np,ifs)18977176Syx160601 nat_t *nat6_lookupredir(np, ifs)
18987176Syx160601 natlookup_t *np;
18997176Syx160601 ipf_stack_t *ifs;
19007176Syx160601 {
19017176Syx160601 	fr_info_t fi;
19027176Syx160601 	nat_t *nat;
19037176Syx160601 
19047176Syx160601 	bzero((char *)&fi, sizeof (fi));
19057176Syx160601 	if (np->nl_flags & IPN_IN) {
19067176Syx160601 		fi.fin_data[0] = ntohs(np->nl_realport);
19077176Syx160601 		fi.fin_data[1] = ntohs(np->nl_outport);
19087176Syx160601 	} else {
19097176Syx160601 		fi.fin_data[0] = ntohs(np->nl_inport);
19107176Syx160601 		fi.fin_data[1] = ntohs(np->nl_outport);
19117176Syx160601 	}
19127176Syx160601 	if (np->nl_flags & IPN_TCP)
19137176Syx160601 		fi.fin_p = IPPROTO_TCP;
19147176Syx160601 	else if (np->nl_flags & IPN_UDP)
19157176Syx160601 		fi.fin_p = IPPROTO_UDP;
19167176Syx160601 	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
19177176Syx160601 		fi.fin_p = IPPROTO_ICMPV6;
19187176Syx160601 
19197176Syx160601 	fi.fin_ifs = ifs;
19207176Syx160601 	/*
19217176Syx160601 	 * We can do two sorts of lookups:
19227176Syx160601 	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
19237176Syx160601 	 * - default: we have the `in' and `out' address, look for `real'.
19247176Syx160601 	 */
19257176Syx160601 	if (np->nl_flags & IPN_IN) {
19267176Syx160601 		if ((nat = nat6_inlookup(&fi, np->nl_flags, fi.fin_p,
19277176Syx160601 					 &np->nl_realip6, &np->nl_outip6))) {
19287176Syx160601 			np->nl_inipaddr = nat->nat_inip6;
19297176Syx160601 			np->nl_inport = nat->nat_inport;
19307176Syx160601 		}
19317176Syx160601 	} else {
19327176Syx160601 		/*
19337176Syx160601 		 * If nl_inip is non null, this is a lookup based on the real
19347176Syx160601 		 * ip address. Else, we use the fake.
19357176Syx160601 		 */
19367176Syx160601 		if ((nat = nat6_outlookup(&fi, np->nl_flags, fi.fin_p,
19377176Syx160601 					  &np->nl_inip6, &np->nl_outip6))) {
19387176Syx160601 			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
19397176Syx160601 				fr_info_t fin;
19407176Syx160601 				bzero((char *)&fin, sizeof (fin));
19417176Syx160601 				fin.fin_p = nat->nat_p;
19427176Syx160601 				fin.fin_data[0] = ntohs(nat->nat_outport);
19437176Syx160601 				fin.fin_data[1] = ntohs(nat->nat_oport);
19447176Syx160601 				fin.fin_ifs = ifs;
19457176Syx160601 				if (nat6_inlookup(&fin, np->nl_flags, fin.fin_p,
19467176Syx160601 						 &nat->nat_outip6.in6,
19477176Syx160601 						 &nat->nat_oip6.in6) != NULL) {
19487176Syx160601 					np->nl_flags &= ~IPN_FINDFORWARD;
19497176Syx160601 				}
19507176Syx160601 			}
19517176Syx160601 
19527176Syx160601 			np->nl_realip6 = nat->nat_outip6.in6;
19537176Syx160601 			np->nl_realport = nat->nat_outport;
19547176Syx160601 		}
19557176Syx160601  	}
19567176Syx160601 
19577176Syx160601 	return nat;
19587176Syx160601 }
19597176Syx160601 
19607176Syx160601 
19617176Syx160601 /* ------------------------------------------------------------------------ */
19627176Syx160601 /* Function:    nat6_match                                                  */
19637176Syx160601 /* Returns:     int - 0 == no match, 1 == match                             */
19647176Syx160601 /* Parameters:  fin(I)   - pointer to packet information                    */
19657176Syx160601 /*              np(I)    - pointer to NAT rule                              */
19667176Syx160601 /*                                                                          */
19677176Syx160601 /* Pull the matching of a packet against a NAT rule out of that complex     */
19687176Syx160601 /* loop inside fr_checknat6in() and lay it out properly in its own function.*/
19697176Syx160601 /* ------------------------------------------------------------------------ */
nat6_match(fin,np)19707176Syx160601 static int nat6_match(fin, np)
19717176Syx160601 fr_info_t *fin;
19727176Syx160601 ipnat_t *np;
19737176Syx160601 {
19747176Syx160601 	frtuc_t *ft;
19757176Syx160601 
19767176Syx160601 	if (fin->fin_v != 6)
19777176Syx160601 		return 0;
19787176Syx160601 
19797176Syx160601 	if (np->in_p && fin->fin_p != np->in_p)
19807176Syx160601 		return 0;
19817176Syx160601 
19827176Syx160601 	if (fin->fin_out) {
19837176Syx160601 		if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK)))
19847176Syx160601 			return 0;
19857176Syx160601 		if (IP6_MASKNEQ(&fin->fin_src6, &np->in_in[1], &np->in_in[0])
19867176Syx160601 		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
19877176Syx160601 			return 0;
19887176Syx160601 		if (IP6_MASKNEQ(&fin->fin_dst6, &np->in_src[1], &np->in_src[0])
19897176Syx160601 		    ^ ((np->in_flags & IPN_NOTDST) != 0))
19907176Syx160601 			return 0;
19917176Syx160601 	} else {
19927176Syx160601 		if (!(np->in_redir & NAT_REDIRECT))
19937176Syx160601 			return 0;
19947176Syx160601 		if (IP6_MASKNEQ(&fin->fin_src6, &np->in_src[1], &np->in_src[0])
19957176Syx160601 		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
19967176Syx160601 			return 0;
19977176Syx160601 		if (IP6_MASKNEQ(&fin->fin_dst6, &np->in_out[1], &np->in_out[0])
19987176Syx160601 		    ^ ((np->in_flags & IPN_NOTDST) != 0))
19997176Syx160601 			return 0;
20007176Syx160601 	}
20017176Syx160601 
20027176Syx160601 	ft = &np->in_tuc;
20037176Syx160601 	if (!(fin->fin_flx & FI_TCPUDP) ||
20047176Syx160601 	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
20057176Syx160601 		if (ft->ftu_scmp || ft->ftu_dcmp)
20067176Syx160601 			return 0;
20077176Syx160601 		return 1;
20087176Syx160601 	}
20097176Syx160601 
20107176Syx160601 	return fr_tcpudpchk(fin, ft);
20117176Syx160601 }
20127176Syx160601 
20137176Syx160601 
20147176Syx160601 /* ------------------------------------------------------------------------ */
20157176Syx160601 /* Function:    fr_checknat6out                                             */
20167176Syx160601 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
20177176Syx160601 /*                     0 == no packet translation occurred,                 */
20187176Syx160601 /*                     1 == packet was successfully translated.             */
20197176Syx160601 /* Parameters:  fin(I)   - pointer to packet information                    */
20207176Syx160601 /*              passp(I) - pointer to filtering result flags                */
20217176Syx160601 /*                                                                          */
20227176Syx160601 /* Check to see if an outcoming packet should be changed.  ICMP packets are */
20237176Syx160601 /* first checked to see if they match an existing entry (if an error),      */
20247176Syx160601 /* otherwise a search of the current NAT table is made.  If neither results */
20257176Syx160601 /* in a match then a search for a matching NAT rule is made.  Create a new  */
20267176Syx160601 /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
20277176Syx160601 /* packet header(s) as required.                                            */
20287176Syx160601 /* ------------------------------------------------------------------------ */
fr_checknat6out(fin,passp)20297176Syx160601 int fr_checknat6out(fin, passp)
20307176Syx160601 fr_info_t *fin;
20317176Syx160601 u_32_t *passp;
20327176Syx160601 {
20337176Syx160601 	struct ifnet *ifp, *sifp;
20347176Syx160601 	int rval, natfailed;
20357176Syx160601 	ipnat_t *np = NULL;
20367176Syx160601 	u_int nflags = 0;
20377176Syx160601 	i6addr_t ipa, iph;
20387176Syx160601 	int natadd = 1;
20397176Syx160601 	frentry_t *fr;
20407176Syx160601 	nat_t *nat;
20417176Syx160601 	ipf_stack_t *ifs = fin->fin_ifs;
20427176Syx160601 
20437176Syx160601 	if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0)
20447176Syx160601 		return 0;
20457176Syx160601 
20467176Syx160601 	natfailed = 0;
20477176Syx160601 	fr = fin->fin_fr;
20487176Syx160601 	sifp = fin->fin_ifp;
20497176Syx160601 	if ((fr != NULL) && !(fr->fr_flags & FR_DUP) &&
20507176Syx160601 	    fr->fr_tifs[fin->fin_rev].fd_ifp &&
20517176Syx160601 	    fr->fr_tifs[fin->fin_rev].fd_ifp != (void *)-1)
20527176Syx160601 		fin->fin_ifp = fr->fr_tifs[fin->fin_rev].fd_ifp;
20537176Syx160601 	ifp = fin->fin_ifp;
20547176Syx160601 
20557176Syx160601 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
20567176Syx160601 		switch (fin->fin_p)
20577176Syx160601 		{
20587176Syx160601 		case IPPROTO_TCP :
20597176Syx160601 			nflags = IPN_TCP;
20607176Syx160601 			break;
20617176Syx160601 		case IPPROTO_UDP :
20627176Syx160601 			nflags = IPN_UDP;
20637176Syx160601 			break;
20647176Syx160601 		case IPPROTO_ICMPV6 :
20657176Syx160601 			/*
20667176Syx160601 			 * This is an incoming packet, so the destination is
20677176Syx160601 			 * the icmp6_id and the source port equals 0
20687176Syx160601 			 */
20697176Syx160601 			if ((fin->fin_flx & FI_ICMPQUERY) != 0)
20707176Syx160601 				nflags = IPN_ICMPQUERY;
20717176Syx160601 			break;
20727176Syx160601 		default :
20737176Syx160601 			break;
20747176Syx160601 		}
20757176Syx160601 
20767176Syx160601 #ifdef	IPF_V6_PROXIES
20777176Syx160601 		if ((nflags & IPN_TCPUDP))
20787176Syx160601 			tcp = fin->fin_dp;
20797176Syx160601 #endif
20807176Syx160601 	}
20817176Syx160601 
20827176Syx160601 	ipa = fin->fin_src6;
20837176Syx160601 
20847176Syx160601 	READ_ENTER(&ifs->ifs_ipf_nat);
20857176Syx160601 
20867176Syx160601 	if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) &&
20877176Syx160601 	    (nat = nat6_icmperror(fin, &nflags, NAT_OUTBOUND)))
20887176Syx160601 		/*EMPTY*/;
20897176Syx160601 	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
20907176Syx160601 		natadd = 0;
20917176Syx160601 	else if ((nat = nat6_outlookup(fin, nflags|NAT_SEARCH,
20927176Syx160601 		    (u_int)fin->fin_p, &fin->fin_src6.in6,
20937176Syx160601 		    &fin->fin_dst6.in6))) {
20947176Syx160601 		nflags = nat->nat_flags;
20957176Syx160601 	} else {
20967176Syx160601 		u_32_t hv, nmsk;
20977176Syx160601 		i6addr_t msk;
20987176Syx160601 		int i;
20997176Syx160601 
21007176Syx160601 		/*
21017176Syx160601 		 * If there is no current entry in the nat table for this IP#,
21027176Syx160601 		 * create one for it (if there is a matching rule).
21037176Syx160601 		 */
21047176Syx160601 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
21057176Syx160601 		i = 3;
21067176Syx160601 		msk.i6[0] = 0xffffffff;
21077176Syx160601 		msk.i6[1] = 0xffffffff;
21087176Syx160601 		msk.i6[2] = 0xffffffff;
21097176Syx160601 		msk.i6[3] = 0xffffffff;
21107176Syx160601 		nmsk = ifs->ifs_nat6_masks[3];
21117176Syx160601 		WRITE_ENTER(&ifs->ifs_ipf_nat);
21127176Syx160601 maskloop:
21137176Syx160601 		IP6_AND(&ipa, &msk, &iph);
21147176Syx160601 		hv = NAT_HASH_FN6(&iph, 0, ifs->ifs_ipf_natrules_sz);
21157176Syx160601 		for (np = ifs->ifs_nat_rules[hv]; np; np = np->in_mnext)
21167176Syx160601 		{
21177176Syx160601 			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
21187176Syx160601 				continue;
21197176Syx160601 			if (np->in_v != 6)
21207176Syx160601 				continue;
21217176Syx160601 			if (np->in_p && (np->in_p != fin->fin_p))
21227176Syx160601 				continue;
21237176Syx160601 			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
21247176Syx160601 				continue;
21257176Syx160601 			if (np->in_flags & IPN_FILTER) {
21267176Syx160601 				if (!nat6_match(fin, np))
21277176Syx160601 					continue;
21287176Syx160601 			} else if (!IP6_MASKEQ(&ipa, &np->in_in[1],
21297176Syx160601 			    &np->in_in[0]))
21307176Syx160601 				continue;
21317176Syx160601 
21327176Syx160601 			if ((fr != NULL) &&
21337176Syx160601 			    !fr_matchtag(&np->in_tag, &fr->fr_nattag))
21347176Syx160601 				continue;
21357176Syx160601 
21367176Syx160601 #ifdef	IPF_V6_PROXIES
21377176Syx160601 			if (*np->in_plabel != '\0') {
21387176Syx160601 				if (((np->in_flags & IPN_FILTER) == 0) &&
21397176Syx160601 				    (np->in_dport != tcp->th_dport))
21407176Syx160601 					continue;
21417176Syx160601 				if (appr_ok(fin, tcp, np) == 0)
21427176Syx160601 					continue;
21437176Syx160601 			}
21447176Syx160601 #endif
21457176Syx160601 
21467176Syx160601 			if (nat = nat6_new(fin, np, NULL, nflags,
21477176Syx160601 					   NAT_OUTBOUND)) {
21487176Syx160601 				np->in_hits++;
21497176Syx160601 				break;
21507176Syx160601 			} else
21517176Syx160601 				natfailed = -1;
21527176Syx160601 		}
21537176Syx160601 		if ((np == NULL) && (i >= 0)) {
21547176Syx160601 			while (i >= 0) {
21557176Syx160601 				while (nmsk) {
21567176Syx160601 					msk.i6[i] = htonl(ntohl(msk.i6[i])<<1);
21577176Syx160601 					if ((nmsk & 0x80000000) != 0) {
21587176Syx160601 						nmsk <<= 1;
21597176Syx160601 						goto maskloop;
21607176Syx160601 					}
21617176Syx160601 					nmsk <<= 1;
21627176Syx160601 				}
21637176Syx160601 				msk.i6[i--] = 0;
21647176Syx160601 				if (i >= 0) {
21657176Syx160601 					nmsk = ifs->ifs_nat6_masks[i];
21667176Syx160601 					if (nmsk != 0)
21677176Syx160601 						goto maskloop;
21687176Syx160601 				}
21697176Syx160601 			}
21707176Syx160601 		}
21717176Syx160601 		MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
21727176Syx160601 	}
21737176Syx160601 
21747176Syx160601 	if (nat != NULL) {
21757176Syx160601 		rval = fr_nat6out(fin, nat, natadd, nflags);
2176*8624SDarren.Reed@Sun.COM 	} else {
21777176Syx160601 		rval = natfailed;
2178*8624SDarren.Reed@Sun.COM 	}
21797176Syx160601 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
21807176Syx160601 
21817176Syx160601 	if (rval == -1) {
21827176Syx160601 		if (passp != NULL)
21837176Syx160601 			*passp = FR_BLOCK;
21847176Syx160601 		fin->fin_flx |= FI_BADNAT;
21857176Syx160601 	}
21867176Syx160601 	fin->fin_ifp = sifp;
21877176Syx160601 	return rval;
21887176Syx160601 }
21897176Syx160601 
21907176Syx160601 /* ------------------------------------------------------------------------ */
21917176Syx160601 /* Function:    fr_nat6out                                                  */
21927176Syx160601 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
21937176Syx160601 /*                     1 == packet was successfully translated.             */
21947176Syx160601 /* Parameters:  fin(I)    - pointer to packet information                   */
21957176Syx160601 /*              nat(I)    - pointer to NAT structure                        */
21967176Syx160601 /*              natadd(I) - flag indicating if it is safe to add frag cache */
21977176Syx160601 /*              nflags(I) - NAT flags set for this packet                   */
21987176Syx160601 /*                                                                          */
21997176Syx160601 /* Translate a packet coming "out" on an interface.                         */
22007176Syx160601 /* ------------------------------------------------------------------------ */
fr_nat6out(fin,nat,natadd,nflags)22017176Syx160601 int fr_nat6out(fin, nat, natadd, nflags)
22027176Syx160601 fr_info_t *fin;
22037176Syx160601 nat_t *nat;
22047176Syx160601 int natadd;
22057176Syx160601 u_32_t nflags;
22067176Syx160601 {
22077176Syx160601 	struct icmp6_hdr *icmp6;
22087176Syx160601 	u_short *csump;
22097176Syx160601 	tcphdr_t *tcp;
22107176Syx160601 	ipnat_t *np;
22117176Syx160601 	int i;
22127176Syx160601 	ipf_stack_t *ifs = fin->fin_ifs;
22137176Syx160601 
22147176Syx160601 #if SOLARIS && defined(_KERNEL)
22157513SDarren.Reed@Sun.COM 	net_handle_t net_data_p = ifs->ifs_ipf_ipv6;
22167176Syx160601 #endif
22177176Syx160601 
22187176Syx160601 	tcp = NULL;
22197176Syx160601 	icmp6 = NULL;
22207176Syx160601 	csump = NULL;
22217176Syx160601 	np = nat->nat_ptr;
22227176Syx160601 
22237176Syx160601 	if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
22247176Syx160601 		(void) fr_nat_newfrag(fin, 0, nat);
22257176Syx160601 
22267176Syx160601 	MUTEX_ENTER(&nat->nat_lock);
22277176Syx160601 	nat->nat_bytes[1] += fin->fin_plen;
22287176Syx160601 	nat->nat_pkts[1]++;
22297176Syx160601 	MUTEX_EXIT(&nat->nat_lock);
22307176Syx160601 
22317176Syx160601 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
22327176Syx160601 		if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) {
22337176Syx160601 			tcp = fin->fin_dp;
22347176Syx160601 
22357176Syx160601 			tcp->th_sport = nat->nat_outport;
22367176Syx160601 			fin->fin_data[0] = ntohs(nat->nat_outport);
22377176Syx160601 		}
22387176Syx160601 
22397176Syx160601 		if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) {
22407176Syx160601 			icmp6 = fin->fin_dp;
22417176Syx160601 			icmp6->icmp6_id = nat->nat_outport;
22427176Syx160601 		}
22437176Syx160601 
22447176Syx160601 		csump = nat_proto(fin, nat, nflags);
22457176Syx160601 	}
22467176Syx160601 
22477176Syx160601 	fin->fin_ip6->ip6_src = nat->nat_outip6.in6;
22487176Syx160601 	fin->fin_src6 = nat->nat_outip6;
22497176Syx160601 
22507176Syx160601 	nat_update(fin, nat, np);
22517176Syx160601 
22527176Syx160601 	/*
22537176Syx160601 	 * TCP/UDP/ICMPv6 checksum needs to be adjusted.
22547176Syx160601 	 */
22557176Syx160601 	if (csump != NULL && (!(nflags & IPN_TCPUDP) ||
22567176Syx160601 	    !NET_IS_HCK_L4_FULL(net_data_p, fin->fin_m))) {
22577176Syx160601 		if (nflags & IPN_TCPUDP &&
22587176Syx160601 	   	    NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) {
22597176Syx160601 			if (nat->nat_dir == NAT_OUTBOUND)
22607176Syx160601 				fix_outcksum(csump, nat->nat_sumd[1]);
22617176Syx160601 			else
22627176Syx160601 				fix_incksum(csump, nat->nat_sumd[1]);
22637176Syx160601 		} else {
22647176Syx160601 			if (nat->nat_dir == NAT_OUTBOUND)
22657176Syx160601 				fix_outcksum(csump, nat->nat_sumd[0]);
22667176Syx160601 			else
22677176Syx160601 				fix_incksum(csump, nat->nat_sumd[0]);
22687176Syx160601 		}
22697176Syx160601 	}
22707176Syx160601 #ifdef	IPFILTER_SYNC
22717176Syx160601 	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
22727176Syx160601 #endif
22737176Syx160601 	/* ------------------------------------------------------------- */
22747176Syx160601 	/* A few quick notes:						 */
22757176Syx160601 	/*	Following are test conditions prior to calling the 	 */
22767176Syx160601 	/*	appr_check routine.					 */
22777176Syx160601 	/*								 */
22787176Syx160601 	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
22797176Syx160601 	/*	with a redirect rule, we attempt to match the packet's	 */
22807176Syx160601 	/*	source port against in_dport, otherwise	we'd compare the */
22817176Syx160601 	/*	packet's destination.			 		 */
22827176Syx160601 	/* ------------------------------------------------------------- */
22837176Syx160601 	if ((np != NULL) && (np->in_apr != NULL)) {
22847176Syx160601 		i = appr_check(fin, nat);
22857176Syx160601 		if (i == 0)
22867176Syx160601 			i = 1;
22877176Syx160601 	} else
22887176Syx160601 		i = 1;
22897176Syx160601 	ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[1]);
22907176Syx160601 	fin->fin_flx |= FI_NATED;
22917176Syx160601 	return i;
22927176Syx160601 }
22937176Syx160601 
22947176Syx160601 
22957176Syx160601 /* ------------------------------------------------------------------------ */
22967176Syx160601 /* Function:    fr_checknat6in                                              */
22977176Syx160601 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
22987176Syx160601 /*                     0 == no packet translation occurred,                 */
22997176Syx160601 /*                     1 == packet was successfully translated.             */
23007176Syx160601 /* Parameters:  fin(I)   - pointer to packet information                    */
23017176Syx160601 /*              passp(I) - pointer to filtering result flags                */
23027176Syx160601 /*                                                                          */
23037176Syx160601 /* Check to see if an incoming packet should be changed.  ICMP packets are  */
23047176Syx160601 /* first checked to see if they match an existing entry (if an error),      */
23057176Syx160601 /* otherwise a search of the current NAT table is made.  If neither results */
23067176Syx160601 /* in a match then a search for a matching NAT rule is made.  Create a new  */
23077176Syx160601 /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
23087176Syx160601 /* packet header(s) as required.                                            */
23097176Syx160601 /* ------------------------------------------------------------------------ */
fr_checknat6in(fin,passp)23107176Syx160601 int fr_checknat6in(fin, passp)
23117176Syx160601 fr_info_t *fin;
23127176Syx160601 u_32_t *passp;
23137176Syx160601 {
23147176Syx160601 	u_int nflags, natadd;
23157176Syx160601 	int rval, natfailed;
23167176Syx160601 	struct ifnet *ifp;
23177176Syx160601 	struct icmp6_hdr *icmp6;
23187176Syx160601 	tcphdr_t *tcp;
23197176Syx160601 	u_short dport;
23207176Syx160601 	ipnat_t *np;
23217176Syx160601 	nat_t *nat;
23227176Syx160601 	i6addr_t ipa, iph;
23237176Syx160601 	ipf_stack_t *ifs = fin->fin_ifs;
23247176Syx160601 
23257176Syx160601 	if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0)
23267176Syx160601 		return 0;
23277176Syx160601 
23287176Syx160601 	tcp = NULL;
23297176Syx160601 	icmp6 = NULL;
23307176Syx160601 	dport = 0;
23317176Syx160601 	natadd = 1;
23327176Syx160601 	nflags = 0;
23337176Syx160601 	natfailed = 0;
23347176Syx160601 	ifp = fin->fin_ifp;
23357176Syx160601 
23367176Syx160601 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
23377176Syx160601 		switch (fin->fin_p)
23387176Syx160601 		{
23397176Syx160601 		case IPPROTO_TCP :
23407176Syx160601 			nflags = IPN_TCP;
23417176Syx160601 			break;
23427176Syx160601 		case IPPROTO_UDP :
23437176Syx160601 			nflags = IPN_UDP;
23447176Syx160601 			break;
23457176Syx160601 		case IPPROTO_ICMPV6 :
23467176Syx160601 			icmp6 = fin->fin_dp;
23477176Syx160601 
23487176Syx160601 			/*
23497176Syx160601 			 * This is an incoming packet, so the destination is
23507176Syx160601 			 * the icmp_id and the source port equals 0
23517176Syx160601 			 */
23527176Syx160601 			if ((fin->fin_flx & FI_ICMPQUERY) != 0) {
23537176Syx160601 				nflags = IPN_ICMPQUERY;
23547176Syx160601 				dport = icmp6->icmp6_id;
23557176Syx160601 			} break;
23567176Syx160601 		default :
23577176Syx160601 			break;
23587176Syx160601 		}
23597176Syx160601 
23607176Syx160601 		if ((nflags & IPN_TCPUDP)) {
23617176Syx160601 			tcp = fin->fin_dp;
23627176Syx160601 			dport = tcp->th_dport;
23637176Syx160601 		}
23647176Syx160601 	}
23657176Syx160601 
23667176Syx160601 	ipa = fin->fin_dst6;
23677176Syx160601 
23687176Syx160601 	READ_ENTER(&ifs->ifs_ipf_nat);
23697176Syx160601 
23707176Syx160601 	if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) &&
23717176Syx160601 	    (nat = nat6_icmperror(fin, &nflags, NAT_INBOUND)))
23727176Syx160601 		/*EMPTY*/;
23737176Syx160601 	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
23747176Syx160601 		natadd = 0;
23757176Syx160601 	else if ((nat = nat6_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p,
23767176Syx160601 	    &fin->fin_src6.in6, &ipa.in6))) {
23777176Syx160601 		nflags = nat->nat_flags;
23787176Syx160601 	} else {
23797176Syx160601 		u_32_t hv, rmsk;
23807176Syx160601 		i6addr_t msk;
23817176Syx160601 		int i;
23827176Syx160601 
23837176Syx160601 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
23847176Syx160601 		i = 3;
23857176Syx160601 		msk.i6[0] = 0xffffffff;
23867176Syx160601 		msk.i6[1] = 0xffffffff;
23877176Syx160601 		msk.i6[2] = 0xffffffff;
23887176Syx160601 		msk.i6[3] = 0xffffffff;
23897176Syx160601 		rmsk = ifs->ifs_rdr6_masks[3];
23907176Syx160601 		WRITE_ENTER(&ifs->ifs_ipf_nat);
23917176Syx160601 		/*
23927176Syx160601 		 * If there is no current entry in the nat table for this IP#,
23937176Syx160601 		 * create one for it (if there is a matching rule).
23947176Syx160601 		 */
23957176Syx160601 maskloop:
23967176Syx160601 		IP6_AND(&ipa, &msk, &iph);
23977176Syx160601 		hv = NAT_HASH_FN6(&iph, 0, ifs->ifs_ipf_rdrrules_sz);
23987176Syx160601 		for (np = ifs->ifs_rdr_rules[hv]; np; np = np->in_rnext) {
23997176Syx160601 			if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
24007176Syx160601 				continue;
24017176Syx160601 			if (np->in_v != fin->fin_v)
24027176Syx160601 				continue;
24037176Syx160601 			if (np->in_p && (np->in_p != fin->fin_p))
24047176Syx160601 				continue;
24057176Syx160601 			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
24067176Syx160601 				continue;
24077176Syx160601 			if (np->in_flags & IPN_FILTER) {
24087176Syx160601 				if (!nat6_match(fin, np))
24097176Syx160601 					continue;
24107176Syx160601 			} else {
24117176Syx160601 				if (!IP6_MASKEQ(&ipa, &np->in_out[1],
24127176Syx160601 				    &np->in_out[0]))
24137176Syx160601 					continue;
24147176Syx160601 				if (np->in_pmin &&
24157176Syx160601 				    ((ntohs(np->in_pmax) < ntohs(dport)) ||
24167176Syx160601 				     (ntohs(dport) < ntohs(np->in_pmin))))
24177176Syx160601 					continue;
24187176Syx160601 			}
24197176Syx160601 
24207176Syx160601 #ifdef	IPF_V6_PROXIES
24217176Syx160601 			if (*np->in_plabel != '\0') {
24227176Syx160601 				if (!appr_ok(fin, tcp, np)) {
24237176Syx160601 					continue;
24247176Syx160601 				}
24257176Syx160601 			}
24267176Syx160601 #endif
24277176Syx160601 
24287176Syx160601 			nat = nat6_new(fin, np, NULL, nflags, NAT_INBOUND);
24297176Syx160601 			if (nat != NULL) {
24307176Syx160601 				np->in_hits++;
24317176Syx160601 				break;
24327176Syx160601 			} else
24337176Syx160601 				natfailed = -1;
24347176Syx160601 		}
24357176Syx160601 
24367176Syx160601 		if ((np == NULL) && (i >= 0)) {
24377176Syx160601 			while (i >= 0) {
24387176Syx160601 				while (rmsk) {
24397176Syx160601 					msk.i6[i] = htonl(ntohl(msk.i6[i])<<1);
24407176Syx160601 					if ((rmsk & 0x80000000) != 0) {
24417176Syx160601 						rmsk <<= 1;
24427176Syx160601 						goto maskloop;
24437176Syx160601 					}
24447176Syx160601 					rmsk <<= 1;
24457176Syx160601 				}
24467176Syx160601 				msk.i6[i--] = 0;
24477176Syx160601 				if (i >= 0) {
24487176Syx160601 					rmsk = ifs->ifs_rdr6_masks[i];
24497176Syx160601 					if (rmsk != 0)
24507176Syx160601 						goto maskloop;
24517176Syx160601 				}
24527176Syx160601 			}
24537176Syx160601 		}
24547176Syx160601 		MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
24557176Syx160601 	}
24567176Syx160601 	if (nat != NULL) {
24577176Syx160601 		rval = fr_nat6in(fin, nat, natadd, nflags);
2458*8624SDarren.Reed@Sun.COM 	} else {
24597176Syx160601 		rval = natfailed;
2460*8624SDarren.Reed@Sun.COM 	}
24617176Syx160601 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
24627176Syx160601 
24637176Syx160601 	if (rval == -1) {
24647176Syx160601 		if (passp != NULL)
24657176Syx160601 			*passp = FR_BLOCK;
24667176Syx160601 		fin->fin_flx |= FI_BADNAT;
24677176Syx160601 	}
24687176Syx160601 	return rval;
24697176Syx160601 }
24707176Syx160601 
24717176Syx160601 
24727176Syx160601 /* ------------------------------------------------------------------------ */
24737176Syx160601 /* Function:    fr_nat6in                                                   */
24747176Syx160601 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
24757176Syx160601 /*                     1 == packet was successfully translated.             */
24767176Syx160601 /* Parameters:  fin(I)    - pointer to packet information                   */
24777176Syx160601 /*              nat(I)    - pointer to NAT structure                        */
24787176Syx160601 /*              natadd(I) - flag indicating if it is safe to add frag cache */
24797176Syx160601 /*              nflags(I) - NAT flags set for this packet                   */
24807176Syx160601 /* Locks Held:  ipf_nat (READ)                                              */
24817176Syx160601 /*                                                                          */
24827176Syx160601 /* Translate a packet coming "in" on an interface.                          */
24837176Syx160601 /* ------------------------------------------------------------------------ */
fr_nat6in(fin,nat,natadd,nflags)24847176Syx160601 int fr_nat6in(fin, nat, natadd, nflags)
24857176Syx160601 fr_info_t *fin;
24867176Syx160601 nat_t *nat;
24877176Syx160601 int natadd;
24887176Syx160601 u_32_t nflags;
24897176Syx160601 {
24907176Syx160601 	struct icmp6_hdr *icmp6;
24917176Syx160601 	u_short *csump;
24927176Syx160601 	tcphdr_t *tcp;
24937176Syx160601 	ipnat_t *np;
24947176Syx160601 	ipf_stack_t *ifs = fin->fin_ifs;
24957176Syx160601 
24967176Syx160601 #if SOLARIS && defined(_KERNEL)
24977513SDarren.Reed@Sun.COM 	net_handle_t net_data_p = ifs->ifs_ipf_ipv6;
24987176Syx160601 #endif
24997176Syx160601 
25007176Syx160601 	tcp = NULL;
25017176Syx160601 	csump = NULL;
25027176Syx160601 	np = nat->nat_ptr;
25037176Syx160601 	fin->fin_fr = nat->nat_fr;
25047176Syx160601 
25057176Syx160601 	if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
25067176Syx160601 		(void) fr_nat_newfrag(fin, 0, nat);
25077176Syx160601 
25087176Syx160601 #ifdef	IPF_V6_PROXIES
25097176Syx160601 	if (np != NULL) {
25107176Syx160601 
25117176Syx160601 	/* ------------------------------------------------------------- */
25127176Syx160601 	/* A few quick notes:						 */
25137176Syx160601 	/*	Following are test conditions prior to calling the 	 */
25147176Syx160601 	/*	appr_check routine.					 */
25157176Syx160601 	/*								 */
25167176Syx160601 	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
25177176Syx160601 	/*	with a map rule, we attempt to match the packet's	 */
25187176Syx160601 	/*	source port against in_dport, otherwise	we'd compare the */
25197176Syx160601 	/*	packet's destination.			 		 */
25207176Syx160601 	/* ------------------------------------------------------------- */
25217176Syx160601 		if (np->in_apr != NULL) {
25227176Syx160601 			i = appr_check(fin, nat);
25237176Syx160601 			if (i == -1) {
25247176Syx160601 				return -1;
25257176Syx160601 			}
25267176Syx160601 		}
25277176Syx160601 	}
25287176Syx160601 #endif
25297176Syx160601 
25307176Syx160601 #ifdef	IPFILTER_SYNC
25317176Syx160601 	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
25327176Syx160601 #endif
25337176Syx160601 
25347176Syx160601 	MUTEX_ENTER(&nat->nat_lock);
25357176Syx160601 	nat->nat_bytes[0] += fin->fin_plen;
25367176Syx160601 	nat->nat_pkts[0]++;
25377176Syx160601 	MUTEX_EXIT(&nat->nat_lock);
25387176Syx160601 
25397176Syx160601 	fin->fin_ip6->ip6_dst = nat->nat_inip6.in6;
25407176Syx160601 	fin->fin_dst6 = nat->nat_inip6;
25417176Syx160601 
25427176Syx160601 	if (nflags & IPN_TCPUDP)
25437176Syx160601 		tcp = fin->fin_dp;
25447176Syx160601 
25457176Syx160601 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
25467176Syx160601 		if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) {
25477176Syx160601 			tcp->th_dport = nat->nat_inport;
25487176Syx160601 			fin->fin_data[1] = ntohs(nat->nat_inport);
25497176Syx160601 		}
25507176Syx160601 
25517176Syx160601 
25527176Syx160601 		if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) {
25537176Syx160601 			icmp6 = fin->fin_dp;
25547176Syx160601 
25557176Syx160601 			icmp6->icmp6_id = nat->nat_inport;
25567176Syx160601 		}
25577176Syx160601 
25587176Syx160601 		csump = nat_proto(fin, nat, nflags);
25597176Syx160601 	}
25607176Syx160601 
25617176Syx160601 	nat_update(fin, nat, np);
25627176Syx160601 
25637176Syx160601 	/*
25647176Syx160601 	 * In case they are being forwarded, inbound packets always need to have
25657176Syx160601 	 * their checksum adjusted even if hardware checksum validation said OK.
25667176Syx160601 	 */
25677176Syx160601 	if (csump != NULL) {
25687176Syx160601 		if (nat->nat_dir == NAT_OUTBOUND)
25697176Syx160601 			fix_incksum(csump, nat->nat_sumd[0]);
25707176Syx160601 		else
25717176Syx160601 			fix_outcksum(csump, nat->nat_sumd[0]);
25727176Syx160601 	}
25737176Syx160601 
25747176Syx160601 #if SOLARIS && defined(_KERNEL)
25757176Syx160601 	if (nflags & IPN_TCPUDP &&
25767176Syx160601 	    NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) {
25777176Syx160601 		/*
25787176Syx160601 		 * Need to adjust the partial checksum result stored in
25797176Syx160601 		 * db_cksum16, which will be used for validation in IP.
25807176Syx160601 		 * See IP_CKSUM_RECV().
25817176Syx160601 		 * Adjustment data should be the inverse of the IP address
25827176Syx160601 		 * changes, because db_cksum16 is supposed to be the complement
25837176Syx160601 		 * of the pesudo header.
25847176Syx160601 		 */
25857176Syx160601 		csump = &fin->fin_m->b_datap->db_cksum16;
25867176Syx160601 		if (nat->nat_dir == NAT_OUTBOUND)
25877176Syx160601 			fix_outcksum(csump, nat->nat_sumd[1]);
25887176Syx160601 		else
25897176Syx160601 			fix_incksum(csump, nat->nat_sumd[1]);
25907176Syx160601 	}
25917176Syx160601 #endif
25927176Syx160601 
25937176Syx160601 	ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[0]);
25947176Syx160601 	fin->fin_flx |= FI_NATED;
25957176Syx160601 	if (np != NULL && np->in_tag.ipt_num[0] != 0)
25967176Syx160601 		fin->fin_nattag = &np->in_tag;
25977176Syx160601 	return 1;
25987176Syx160601 }
25997176Syx160601 
26007176Syx160601 
26017176Syx160601 /* ------------------------------------------------------------------------ */
26027176Syx160601 /* Function:    nat_icmpquerytype6                                          */
26037176Syx160601 /* Returns:     int - 1 == success, 0 == failure                            */
26047176Syx160601 /* Parameters:  icmptype(I) - ICMP type number                              */
26057176Syx160601 /*                                                                          */
26067176Syx160601 /* Tests to see if the ICMP type number passed is a query/response type or  */
26077176Syx160601 /* not.                                                                     */
26087176Syx160601 /* ------------------------------------------------------------------------ */
nat_icmpquerytype6(icmptype)26097176Syx160601 static INLINE int nat_icmpquerytype6(icmptype)
26107176Syx160601 int icmptype;
26117176Syx160601 {
26127176Syx160601 
26137176Syx160601 	/*
26147176Syx160601 	 * For the ICMP query NAT code, it is essential that both the query
26157176Syx160601 	 * and the reply match on the NAT rule. Because the NAT structure
26167176Syx160601 	 * does not keep track of the icmptype, and a single NAT structure
26177176Syx160601 	 * is used for all icmp types with the same src, dest and id, we
26187176Syx160601 	 * simply define the replies as queries as well. The funny thing is,
26197176Syx160601 	 * altough it seems silly to call a reply a query, this is exactly
26207176Syx160601 	 * as it is defined in the IPv4 specification
26217176Syx160601 	 */
26227176Syx160601 
26237176Syx160601 	switch (icmptype)
26247176Syx160601 	{
26257176Syx160601 
26267176Syx160601 	case ICMP6_ECHO_REPLY:
26277176Syx160601 	case ICMP6_ECHO_REQUEST:
26287176Syx160601 	/* route aedvertisement/solliciation is currently unsupported: */
26297176Syx160601 	/* it would require rewriting the ICMP data section            */
26307176Syx160601 	case ICMP6_MEMBERSHIP_QUERY:
26317176Syx160601 	case ICMP6_MEMBERSHIP_REPORT:
26327176Syx160601 	case ICMP6_MEMBERSHIP_REDUCTION:
26337176Syx160601 	case ICMP6_WRUREQUEST:
26347176Syx160601 	case ICMP6_WRUREPLY:
26357176Syx160601 	case MLD6_MTRACE_RESP:
26367176Syx160601 	case MLD6_MTRACE:
26377176Syx160601 		return 1;
26387176Syx160601 	default:
26397176Syx160601 		return 0;
26407176Syx160601 	}
26417176Syx160601 }
2642