xref: /illumos-gate/usr/src/uts/common/inet/ipf/ip_nat6.c (revision 55fea89d)
1d6c23f6fSyx /*
2d6c23f6fSyx  * Copyright (C) 1995-2003 by Darren Reed.
3d6c23f6fSyx  *
4d6c23f6fSyx  * See the IPFILTER.LICENCE file for details on licencing.
5d6c23f6fSyx  *
633f2fefdSDarren Reed  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
7d6c23f6fSyx  * Use is subject to license terms.
8d6c23f6fSyx  */
9d6c23f6fSyx 
10d6c23f6fSyx #if defined(KERNEL) || defined(_KERNEL)
11d6c23f6fSyx # undef KERNEL
12d6c23f6fSyx # undef _KERNEL
13d6c23f6fSyx # define        KERNEL	1
14d6c23f6fSyx # define        _KERNEL	1
15d6c23f6fSyx #endif
16d6c23f6fSyx #include <sys/errno.h>
17d6c23f6fSyx #include <sys/types.h>
18d6c23f6fSyx #include <sys/param.h>
19d6c23f6fSyx #include <sys/time.h>
20d6c23f6fSyx #include <sys/file.h>
21d6c23f6fSyx #if defined(__NetBSD__) && (NetBSD >= 199905) && !defined(IPFILTER_LKM) && \
22d6c23f6fSyx     defined(_KERNEL)
23d6c23f6fSyx # include "opt_ipfilter_log.h"
24d6c23f6fSyx #endif
25d6c23f6fSyx #if !defined(_KERNEL)
26d6c23f6fSyx # include <stdio.h>
27d6c23f6fSyx # include <string.h>
28d6c23f6fSyx # include <stdlib.h>
29d6c23f6fSyx # define _KERNEL
30d6c23f6fSyx # ifdef __OpenBSD__
31d6c23f6fSyx struct file;
32d6c23f6fSyx # endif
33d6c23f6fSyx # include <sys/uio.h>
34d6c23f6fSyx # undef _KERNEL
35d6c23f6fSyx #endif
36d6c23f6fSyx #if defined(_KERNEL) && (__FreeBSD_version >= 220000)
37d6c23f6fSyx # include <sys/filio.h>
38d6c23f6fSyx # include <sys/fcntl.h>
39d6c23f6fSyx #else
40d6c23f6fSyx # include <sys/ioctl.h>
41d6c23f6fSyx #endif
42d6c23f6fSyx #if !defined(AIX)
43d6c23f6fSyx # include <sys/fcntl.h>
44d6c23f6fSyx #endif
45d6c23f6fSyx #if !defined(linux)
46d6c23f6fSyx # include <sys/protosw.h>
47d6c23f6fSyx #endif
48d6c23f6fSyx #include <sys/socket.h>
49d6c23f6fSyx #if defined(_KERNEL)
50d6c23f6fSyx # include <sys/systm.h>
51d6c23f6fSyx # if !defined(__SVR4) && !defined(__svr4__)
52d6c23f6fSyx #  include <sys/mbuf.h>
53d6c23f6fSyx # endif
54d6c23f6fSyx #endif
55d6c23f6fSyx #if defined(__SVR4) || defined(__svr4__)
56d6c23f6fSyx # include <sys/filio.h>
57d6c23f6fSyx # include <sys/byteorder.h>
58d6c23f6fSyx # ifdef _KERNEL
59d6c23f6fSyx #  include <sys/dditypes.h>
60d6c23f6fSyx # endif
61d6c23f6fSyx # include <sys/stream.h>
62d6c23f6fSyx # include <sys/kmem.h>
63d6c23f6fSyx #endif
64d6c23f6fSyx #if __FreeBSD_version >= 300000
65d6c23f6fSyx # include <sys/queue.h>
66d6c23f6fSyx #endif
67d6c23f6fSyx #include <net/if.h>
68d6c23f6fSyx #if __FreeBSD_version >= 300000
69d6c23f6fSyx # include <net/if_var.h>
70d6c23f6fSyx # if defined(_KERNEL) && !defined(IPFILTER_LKM)
71d6c23f6fSyx #  include "opt_ipfilter.h"
72d6c23f6fSyx # endif
73d6c23f6fSyx #endif
74d6c23f6fSyx #ifdef sun
75d6c23f6fSyx # include <net/af.h>
76d6c23f6fSyx #endif
77d6c23f6fSyx #include <net/route.h>
78d6c23f6fSyx #include <netinet/in.h>
79d6c23f6fSyx #include <netinet/in_systm.h>
80d6c23f6fSyx #include <netinet/ip.h>
81d6c23f6fSyx 
82d6c23f6fSyx #ifdef RFC1825
83d6c23f6fSyx # include <vpn/md5.h>
84d6c23f6fSyx # include <vpn/ipsec.h>
85d6c23f6fSyx extern struct ifnet vpnif;
86d6c23f6fSyx #endif
87d6c23f6fSyx 
88d6c23f6fSyx #if !defined(linux)
89d6c23f6fSyx # include <netinet/ip_var.h>
90d6c23f6fSyx #endif
91d6c23f6fSyx #include <netinet/tcp.h>
92d6c23f6fSyx #include <netinet/udp.h>
93d6c23f6fSyx #include <netinet/ip_icmp.h>
94d6c23f6fSyx #include "netinet/ip_compat.h"
95d6c23f6fSyx #include <netinet/tcpip.h>
96d6c23f6fSyx #include "netinet/ip_fil.h"
97d6c23f6fSyx #include "netinet/ip_nat.h"
98d6c23f6fSyx #include "netinet/ip_frag.h"
99d6c23f6fSyx #include "netinet/ip_state.h"
100d6c23f6fSyx #include "netinet/ip_proxy.h"
101d6c23f6fSyx #include "netinet/ipf_stack.h"
102d6c23f6fSyx #ifdef	IPFILTER_SYNC
103d6c23f6fSyx #include "netinet/ip_sync.h"
104d6c23f6fSyx #endif
105d6c23f6fSyx #if (__FreeBSD_version >= 300000)
106d6c23f6fSyx # include <sys/malloc.h>
107d6c23f6fSyx #endif
108d6c23f6fSyx /* END OF INCLUDES */
109d6c23f6fSyx 
110d6c23f6fSyx #undef	SOCKADDR_IN
111d6c23f6fSyx #define	SOCKADDR_IN	struct sockaddr_in
112d6c23f6fSyx 
113d6c23f6fSyx #if !defined(lint)
114d6c23f6fSyx static const char rcsid[] = "@(#)$Id: ip_nat6.c,v 1.2 2008/02/14 21:05:50 darrenr Exp $";
115d6c23f6fSyx #endif
116d6c23f6fSyx 
117d6c23f6fSyx static	hostmap_t *nat6_hostmap __P((ipnat_t *, i6addr_t *, i6addr_t *,
118d6c23f6fSyx 				    i6addr_t *, u_32_t, ipf_stack_t *));
119d6c23f6fSyx static	INLINE	int nat6_newmap __P((fr_info_t *, nat_t *, natinfo_t *));
120d6c23f6fSyx static	INLINE	int nat6_newrdr __P((fr_info_t *, nat_t *, natinfo_t *));
121d6c23f6fSyx static	INLINE	int nat6_finalise __P((fr_info_t *, nat_t *, natinfo_t *,
122d6c23f6fSyx 				      tcphdr_t *, nat_t **, int));
123d6c23f6fSyx static	void	nat6_tabmove __P((nat_t *, ipf_stack_t *));
124d6c23f6fSyx static	int	nat6_match __P((fr_info_t *, ipnat_t *));
125d6c23f6fSyx static	INLINE	int nat_icmpquerytype6 __P((int));
126d6c23f6fSyx 
127d6c23f6fSyx 
128d6c23f6fSyx /* ------------------------------------------------------------------------ */
129d6c23f6fSyx /* Function:    nat6_addrdr                                                 */
130d6c23f6fSyx /* Returns:     Nil                                                         */
131d6c23f6fSyx /* Parameters:  n(I) - pointer to NAT rule to add                           */
132d6c23f6fSyx /*                                                                          */
133d6c23f6fSyx /* Adds a redirect rule to the hash table of redirect rules and the list of */
134d6c23f6fSyx /* loaded NAT rules.  Updates the bitmask indicating which netmasks are in  */
135d6c23f6fSyx /* use by redirect rules.                                                   */
136d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_addrdr(n,ifs)137d6c23f6fSyx void nat6_addrdr(n, ifs)
138d6c23f6fSyx ipnat_t *n;
139d6c23f6fSyx ipf_stack_t *ifs;
140d6c23f6fSyx {
141d6c23f6fSyx 	ipnat_t **np;
142d6c23f6fSyx 	i6addr_t j;
143d6c23f6fSyx 	u_int hv;
144d6c23f6fSyx 	int k;
145d6c23f6fSyx 
146d6c23f6fSyx 	k = count6bits(n->in_out[1].i6);
147d6c23f6fSyx 	if ((k >= 0) && (k != 128))
148d6c23f6fSyx 		ifs->ifs_rdr6_masks[k >> 5] |= 1 << (k & 31);
149d6c23f6fSyx 	IP6_AND(&n->in_out[0], &n->in_out[1], &j);
150d6c23f6fSyx 	hv = NAT_HASH_FN6(&j, 0, ifs->ifs_ipf_rdrrules_sz);
151d6c23f6fSyx 	np = ifs->ifs_rdr_rules + hv;
152d6c23f6fSyx 	while (*np != NULL)
153d6c23f6fSyx 		np = &(*np)->in_rnext;
154d6c23f6fSyx 	n->in_rnext = NULL;
155d6c23f6fSyx 	n->in_prnext = np;
156d6c23f6fSyx 	n->in_hv = hv;
157d6c23f6fSyx 	*np = n;
158d6c23f6fSyx }
159d6c23f6fSyx 
160d6c23f6fSyx 
161d6c23f6fSyx /* ------------------------------------------------------------------------ */
162d6c23f6fSyx /* Function:    nat6_addnat                                                 */
163d6c23f6fSyx /* Returns:     Nil                                                         */
164d6c23f6fSyx /* Parameters:  n(I) - pointer to NAT rule to add                           */
165d6c23f6fSyx /*                                                                          */
166d6c23f6fSyx /* Adds a NAT map rule to the hash table of rules and the list of  loaded   */
167d6c23f6fSyx /* NAT rules.  Updates the bitmask indicating which netmasks are in use by  */
168d6c23f6fSyx /* redirect rules.                                                          */
169d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_addnat(n,ifs)170d6c23f6fSyx void nat6_addnat(n, ifs)
171d6c23f6fSyx ipnat_t *n;
172d6c23f6fSyx ipf_stack_t *ifs;
173d6c23f6fSyx {
174d6c23f6fSyx 	ipnat_t **np;
175d6c23f6fSyx 	i6addr_t j;
176d6c23f6fSyx 	u_int hv;
177d6c23f6fSyx 	int k;
178d6c23f6fSyx 
179d6c23f6fSyx 	k = count6bits(n->in_in[1].i6);
180d6c23f6fSyx 	if ((k >= 0) && (k != 128))
181d6c23f6fSyx 		ifs->ifs_nat6_masks[k >> 5] |= 1 << (k & 31);
182d6c23f6fSyx 	IP6_AND(&n->in_in[0], &n->in_in[1], &j);
183d6c23f6fSyx 	hv = NAT_HASH_FN6(&j, 0, ifs->ifs_ipf_natrules_sz);
184d6c23f6fSyx 	np = ifs->ifs_nat_rules + hv;
185d6c23f6fSyx 	while (*np != NULL)
186d6c23f6fSyx 		np = &(*np)->in_mnext;
187d6c23f6fSyx 	n->in_mnext = NULL;
188d6c23f6fSyx 	n->in_pmnext = np;
189d6c23f6fSyx 	n->in_hv = hv;
190d6c23f6fSyx 	*np = n;
191d6c23f6fSyx }
192d6c23f6fSyx 
193d6c23f6fSyx 
194d6c23f6fSyx /* ------------------------------------------------------------------------ */
195d6c23f6fSyx /* Function:    nat6_hostmap                                                */
196d6c23f6fSyx /* Returns:     struct hostmap* - NULL if no hostmap could be created,      */
197d6c23f6fSyx /*                                else a pointer to the hostmapping to use  */
198d6c23f6fSyx /* Parameters:  np(I)   - pointer to NAT rule                               */
199d6c23f6fSyx /*              real(I) - real IP address                                   */
200d6c23f6fSyx /*              map(I)  - mapped IP address                                 */
201d6c23f6fSyx /*              port(I) - destination port number                           */
202d6c23f6fSyx /* Write Locks: ipf_nat                                                     */
203d6c23f6fSyx /*                                                                          */
204d6c23f6fSyx /* Check if an ip address has already been allocated for a given mapping    */
205d6c23f6fSyx /* that is not doing port based translation.  If is not yet allocated, then */
206d6c23f6fSyx /* create a new entry if a non-NULL NAT rule pointer has been supplied.     */
207d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_hostmap(np,src,dst,map,port,ifs)208d6c23f6fSyx static struct hostmap *nat6_hostmap(np, src, dst, map, port, ifs)
209d6c23f6fSyx ipnat_t *np;
210d6c23f6fSyx i6addr_t *src, *dst, *map;
211d6c23f6fSyx u_32_t port;
212d6c23f6fSyx ipf_stack_t *ifs;
213d6c23f6fSyx {
214d6c23f6fSyx 	hostmap_t *hm;
215d6c23f6fSyx 	u_int hv;
216d6c23f6fSyx 
217d6c23f6fSyx 	hv = (src->i6[3] ^ dst->i6[3]);
218d6c23f6fSyx 	hv += (src->i6[2] ^ dst->i6[2]);
219d6c23f6fSyx 	hv += (src->i6[1] ^ dst->i6[1]);
220d6c23f6fSyx 	hv += (src->i6[0] ^ dst->i6[0]);
221d6c23f6fSyx 	hv += src->i6[3];
222d6c23f6fSyx 	hv += src->i6[2];
223d6c23f6fSyx 	hv += src->i6[1];
224d6c23f6fSyx 	hv += src->i6[0];
225d6c23f6fSyx 	hv += dst->i6[3];
226d6c23f6fSyx 	hv += dst->i6[2];
227d6c23f6fSyx 	hv += dst->i6[1];
228d6c23f6fSyx 	hv += dst->i6[0];
229d6c23f6fSyx 	hv %= HOSTMAP_SIZE;
230d6c23f6fSyx 	for (hm = ifs->ifs_maptable[hv]; hm; hm = hm->hm_next)
231d6c23f6fSyx 		if (IP6_EQ(&hm->hm_srcip6, src) &&
232d6c23f6fSyx 		    IP6_EQ(&hm->hm_dstip6, dst) &&
233d6c23f6fSyx 		    ((np == NULL) || (np == hm->hm_ipnat)) &&
234d6c23f6fSyx 		    ((port == 0) || (port == hm->hm_port))) {
235d6c23f6fSyx 			hm->hm_ref++;
236d6c23f6fSyx 			return hm;
237d6c23f6fSyx 		}
238d6c23f6fSyx 
239d6c23f6fSyx 	if (np == NULL)
240d6c23f6fSyx 		return NULL;
241d6c23f6fSyx 
242d6c23f6fSyx 	KMALLOC(hm, hostmap_t *);
243d6c23f6fSyx 	if (hm) {
244d6c23f6fSyx 		hm->hm_hnext = ifs->ifs_ipf_hm_maplist;
245d6c23f6fSyx 		hm->hm_phnext = &ifs->ifs_ipf_hm_maplist;
246d6c23f6fSyx 		if (ifs->ifs_ipf_hm_maplist != NULL)
247d6c23f6fSyx 			ifs->ifs_ipf_hm_maplist->hm_phnext = &hm->hm_hnext;
248d6c23f6fSyx 		ifs->ifs_ipf_hm_maplist = hm;
249d6c23f6fSyx 
250d6c23f6fSyx 		hm->hm_next = ifs->ifs_maptable[hv];
251d6c23f6fSyx 		hm->hm_pnext = ifs->ifs_maptable + hv;
252d6c23f6fSyx 		if (ifs->ifs_maptable[hv] != NULL)
253d6c23f6fSyx 			ifs->ifs_maptable[hv]->hm_pnext = &hm->hm_next;
254d6c23f6fSyx 		ifs->ifs_maptable[hv] = hm;
255d6c23f6fSyx 		hm->hm_ipnat = np;
256d6c23f6fSyx 		hm->hm_src = *src;
257d6c23f6fSyx 		hm->hm_dst = *dst;
258d6c23f6fSyx 		hm->hm_map = *map;
259d6c23f6fSyx 		hm->hm_ref = 1;
260d6c23f6fSyx 		hm->hm_port = port;
261d6c23f6fSyx 		hm->hm_v = 6;
262d6c23f6fSyx 	}
263d6c23f6fSyx 	return hm;
264d6c23f6fSyx }
265d6c23f6fSyx 
266d6c23f6fSyx 
267d6c23f6fSyx /* ------------------------------------------------------------------------ */
268d6c23f6fSyx /* Function:    nat6_newmap                                                 */
269d6c23f6fSyx /* Returns:     int - -1 == error, 0 == success                             */
270d6c23f6fSyx /* Parameters:  fin(I) - pointer to packet information                      */
271d6c23f6fSyx /*              nat(I) - pointer to NAT entry                               */
272d6c23f6fSyx /*              ni(I)  - pointer to structure with misc. information needed */
273d6c23f6fSyx /*                       to create new NAT entry.                           */
274d6c23f6fSyx /*                                                                          */
275d6c23f6fSyx /* Given an empty NAT structure, populate it with new information about a   */
276d6c23f6fSyx /* new NAT session, as defined by the matching NAT rule.                    */
277d6c23f6fSyx /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
278d6c23f6fSyx /* to the new IP address for the translation.                               */
279d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_newmap(fin,nat,ni)280d6c23f6fSyx static INLINE int nat6_newmap(fin, nat, ni)
281d6c23f6fSyx fr_info_t *fin;
282d6c23f6fSyx nat_t *nat;
283d6c23f6fSyx natinfo_t *ni;
284d6c23f6fSyx {
285d6c23f6fSyx 	u_short st_port, dport, sport, port, sp, dp;
286d6c23f6fSyx 	i6addr_t in, st_ip;
287d6c23f6fSyx 	hostmap_t *hm;
288d6c23f6fSyx 	u_32_t flags;
289d6c23f6fSyx 	ipnat_t *np;
290d6c23f6fSyx 	nat_t *natl;
291d6c23f6fSyx 	int l;
292d6c23f6fSyx 	ipf_stack_t *ifs = fin->fin_ifs;
293d6c23f6fSyx 
294d6c23f6fSyx 	/*
295d6c23f6fSyx 	 * If it's an outbound packet which doesn't match any existing
296d6c23f6fSyx 	 * record, then create a new port
297d6c23f6fSyx 	 */
298d6c23f6fSyx 	l = 0;
299d6c23f6fSyx 	hm = NULL;
300d6c23f6fSyx 	np = ni->nai_np;
301d6c23f6fSyx 	st_ip = np->in_next6;
302d6c23f6fSyx 	st_port = np->in_pnext;
303d6c23f6fSyx 	flags = ni->nai_flags;
304d6c23f6fSyx 	sport = ni->nai_sport;
305d6c23f6fSyx 	dport = ni->nai_dport;
306d6c23f6fSyx 
307d6c23f6fSyx 	/*
308d6c23f6fSyx 	 * Do a loop until we either run out of entries to try or we find
309d6c23f6fSyx 	 * a NAT mapping that isn't currently being used.  This is done
310d6c23f6fSyx 	 * because the change to the source is not (usually) being fixed.
311d6c23f6fSyx 	 */
312d6c23f6fSyx 	do {
313d6c23f6fSyx 		port = 0;
314d6c23f6fSyx 		in = np->in_next6;
315d6c23f6fSyx 		if (l == 0) {
316d6c23f6fSyx 			/*
317d6c23f6fSyx 			 * Check to see if there is an existing NAT
318d6c23f6fSyx 			 * setup for this IP address pair.
319d6c23f6fSyx 			 */
320d6c23f6fSyx 			hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6,
321d6c23f6fSyx 					 &in, 0, ifs);
322d6c23f6fSyx 			if (hm != NULL)
323d6c23f6fSyx 				in = hm->hm_map;
324d6c23f6fSyx 		} else if ((l == 1) && (hm != NULL)) {
325d6c23f6fSyx 			fr_hostmapdel(&hm);
326d6c23f6fSyx 		}
327d6c23f6fSyx 
328d6c23f6fSyx 		nat->nat_hm = hm;
329d6c23f6fSyx 
330d6c23f6fSyx 		if (IP6_ISONES(&np->in_out[1]) && (np->in_pnext == 0)) {
331d6c23f6fSyx 			if (l > 0)
332d6c23f6fSyx 				return -1;
333d6c23f6fSyx 		}
334d6c23f6fSyx 
335d6c23f6fSyx 		if (np->in_redir == NAT_BIMAP &&
336d6c23f6fSyx 		    IP6_EQ(&np->in_in[1], &np->in_out[1])) {
337d6c23f6fSyx 			i6addr_t temp;
338d6c23f6fSyx 			/*
339d6c23f6fSyx 			 * map the address block in a 1:1 fashion
340d6c23f6fSyx 			 */
341d6c23f6fSyx 			temp.i6[0] = fin->fin_src6.i6[0] &
342d6c23f6fSyx 					~np->in_in[1].i6[0];
343d6c23f6fSyx 			temp.i6[1] = fin->fin_src6.i6[1] &
344d6c23f6fSyx 					~np->in_in[1].i6[1];
345d6c23f6fSyx 			temp.i6[2] = fin->fin_src6.i6[2] &
346d6c23f6fSyx 					~np->in_in[1].i6[2];
347d6c23f6fSyx 			temp.i6[3] = fin->fin_src6.i6[3] &
348d6c23f6fSyx 					~np->in_in[1].i6[3];
349d6c23f6fSyx 			in = np->in_out[0];
350d6c23f6fSyx 			IP6_MERGE(&in, &temp, &np->in_in[0]);
351d6c23f6fSyx 
352d6c23f6fSyx #ifdef	NEED_128BIT_MATH
353d6c23f6fSyx 		} else if (np->in_redir & NAT_MAPBLK) {
354d6c23f6fSyx 			if ((l >= np->in_ppip) || ((l > 0) &&
355d6c23f6fSyx 			     !(flags & IPN_TCPUDP)))
356d6c23f6fSyx 				return -1;
357d6c23f6fSyx 			/*
358d6c23f6fSyx 			 * map-block - Calculate destination address.
359d6c23f6fSyx 			 */
360d6c23f6fSyx 			IP6_MASK(&in, &fin->fin_src6, &np->in_in[1]);
361d6c23f6fSyx 			in = ntol(in);
362d6c23f6fSyx 			inb = in;
363d6c23f6fSyx 			in /= np->in_ippip;
364d6c23f6fSyx 			in &= ntohl(~np->in_out[1]);
365d6c23f6fSyx 			in += ntohl(np->in_out[0]);
366d6c23f6fSyx 			/*
367d6c23f6fSyx 			 * Calculate destination port.
368d6c23f6fSyx 			 */
369d6c23f6fSyx 			if ((flags & IPN_TCPUDP) &&
370d6c23f6fSyx 			    (np->in_ppip != 0)) {
371d6c23f6fSyx 				port = ntohs(sport) + l;
372d6c23f6fSyx 				port %= np->in_ppip;
373d6c23f6fSyx 				port += np->in_ppip *
374d6c23f6fSyx 					(inb.s_addr % np->in_ippip);
375d6c23f6fSyx 				port += MAPBLK_MINPORT;
376d6c23f6fSyx 				port = htons(port);
377d6c23f6fSyx 			}
378d6c23f6fSyx #endif
379d6c23f6fSyx 
380d6c23f6fSyx 		} else if (IP6_ISZERO(&np->in_out[0]) &&
381d6c23f6fSyx 		    IP6_ISONES(&np->in_out[1])) {
382d6c23f6fSyx 			/*
383d6c23f6fSyx 			 * 0/128 - use the interface's IP address.
384d6c23f6fSyx 			 */
385d6c23f6fSyx 			if ((l > 0) ||
386d6c23f6fSyx 			    fr_ifpaddr(6, FRI_NORMAL, fin->fin_ifp,
387d6c23f6fSyx 				       (void *)&in, NULL, fin->fin_ifs) == -1)
388d6c23f6fSyx 				return -1;
389d6c23f6fSyx 
390d6c23f6fSyx 		} else if (IP6_ISZERO(&np->in_out[0]) &&
391d6c23f6fSyx 		    IP6_ISZERO(&np->in_out[1])) {
392d6c23f6fSyx 			/*
393d6c23f6fSyx 			 * 0/0 - use the original source address/port.
394d6c23f6fSyx 			 */
395d6c23f6fSyx 			if (l > 0)
396d6c23f6fSyx 				return -1;
397d6c23f6fSyx 			in = fin->fin_src6;
398d6c23f6fSyx 
399d6c23f6fSyx 		} else if (!IP6_ISONES(&np->in_out[1]) &&
400d6c23f6fSyx 			   (np->in_pnext == 0) && ((l > 0) || (hm == NULL))) {
401d6c23f6fSyx 			IP6_INC(&np->in_next6);
402d6c23f6fSyx 		}
403d6c23f6fSyx 
404d6c23f6fSyx 		natl = NULL;
405d6c23f6fSyx 
406d6c23f6fSyx 		if ((flags & IPN_TCPUDP) &&
407d6c23f6fSyx 		    ((np->in_redir & NAT_MAPBLK) == 0) &&
408d6c23f6fSyx 		    (np->in_flags & IPN_AUTOPORTMAP)) {
409d6c23f6fSyx 			/*EMPTY*/;
410d6c23f6fSyx #ifdef	NEED_128BIT_MATH
411d6c23f6fSyx 			/*
412d6c23f6fSyx 			 * XXX "ports auto" (without map-block)
413d6c23f6fSyx 			 */
414d6c23f6fSyx 			if ((l > 0) && (l % np->in_ppip == 0)) {
415d6c23f6fSyx 				if (l > np->in_space) {
416d6c23f6fSyx 					return -1;
417d6c23f6fSyx 				} else if ((l > np->in_ppip) &&
418d6c23f6fSyx 				    !IP6_ISONES(&np->in_out[1])) {
419d6c23f6fSyx 					IP6_INC(&np->in_next6);
420d6c23f6fSyx 				}
421d6c23f6fSyx 			}
422d6c23f6fSyx 			if (np->in_ppip != 0) {
423d6c23f6fSyx 				port = ntohs(sport);
424d6c23f6fSyx 				port += (l % np->in_ppip);
425d6c23f6fSyx 				port %= np->in_ppip;
426d6c23f6fSyx 				port += np->in_ppip *
427d6c23f6fSyx 					(ntohl(fin->fin_src6) %
428d6c23f6fSyx 					 np->in_ippip);
429d6c23f6fSyx 				port += MAPBLK_MINPORT;
430d6c23f6fSyx 				port = htons(port);
431d6c23f6fSyx 			}
432d6c23f6fSyx #endif
433d6c23f6fSyx 
434d6c23f6fSyx 		} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
435d6c23f6fSyx 			   (flags & IPN_TCPUDPICMP) && (np->in_pnext != 0)) {
436d6c23f6fSyx 			/*
437d6c23f6fSyx 			 * Standard port translation.  Select next port.
438d6c23f6fSyx 			 */
439ab073b32Sdr 			if (np->in_flags & IPN_SEQUENTIAL) {
440ab073b32Sdr 				port = np->in_pnext;
441ab073b32Sdr 			} else {
442ab073b32Sdr 				port = ipf_random() % (ntohs(np->in_pmax) -
443ab073b32Sdr 						       ntohs(np->in_pmin));
444ab073b32Sdr 				port += ntohs(np->in_pmin);
445ab073b32Sdr 			}
446ab073b32Sdr 			port = htons(port);
447ab073b32Sdr 			np->in_pnext++;
448d6c23f6fSyx 
449d6c23f6fSyx 			if (np->in_pnext > ntohs(np->in_pmax)) {
450d6c23f6fSyx 				np->in_pnext = ntohs(np->in_pmin);
451d6c23f6fSyx 				if (!IP6_ISONES(&np->in_out[1])) {
452d6c23f6fSyx 					IP6_INC(&np->in_next6);
453d6c23f6fSyx 				}
454d6c23f6fSyx 			}
455d6c23f6fSyx 		}
456d6c23f6fSyx 
457d6c23f6fSyx 		if (np->in_flags & IPN_IPRANGE) {
458d6c23f6fSyx 			if (IP6_GT(&np->in_next6, &np->in_out[1]))
459d6c23f6fSyx 				np->in_next6 = np->in_out[0];
460d6c23f6fSyx 		} else {
461d6c23f6fSyx 			i6addr_t a1, a2;
462d6c23f6fSyx 
463d6c23f6fSyx 			a1 = np->in_next6;
464d6c23f6fSyx 			IP6_INC(&a1);
465d6c23f6fSyx 			IP6_AND(&a1, &np->in_out[1], &a2);
466d6c23f6fSyx 			if (!IP6_ISONES(&np->in_out[1]) &&
467d6c23f6fSyx 			    IP6_GT(&a2, &np->in_out[0])) {
468d6c23f6fSyx 				IP6_ADD(&np->in_out[0], 1, &np->in_next6);
469d6c23f6fSyx 			}
470d6c23f6fSyx 		}
471d6c23f6fSyx 
472d6c23f6fSyx 		if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
473d6c23f6fSyx 			port = sport;
474d6c23f6fSyx 
475d6c23f6fSyx 		/*
476d6c23f6fSyx 		 * Here we do a lookup of the connection as seen from
477d6c23f6fSyx 		 * the outside.  If an IP# pair already exists, try
478d6c23f6fSyx 		 * again.  So if you have A->B becomes C->B, you can
479d6c23f6fSyx 		 * also have D->E become C->E but not D->B causing
480d6c23f6fSyx 		 * another C->B.  Also take protocol and ports into
481d6c23f6fSyx 		 * account when determining whether a pre-existing
482d6c23f6fSyx 		 * NAT setup will cause an external conflict where
483d6c23f6fSyx 		 * this is appropriate.
484d6c23f6fSyx 		 */
485d6c23f6fSyx 		sp = fin->fin_data[0];
486d6c23f6fSyx 		dp = fin->fin_data[1];
487d6c23f6fSyx 		fin->fin_data[0] = fin->fin_data[1];
488d6c23f6fSyx 		fin->fin_data[1] = htons(port);
489d6c23f6fSyx 		natl = nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
490d6c23f6fSyx 		    (u_int)fin->fin_p, &fin->fin_dst6.in6, &in.in6);
491d6c23f6fSyx 		fin->fin_data[0] = sp;
492d6c23f6fSyx 		fin->fin_data[1] = dp;
493d6c23f6fSyx 
494d6c23f6fSyx 		/*
495d6c23f6fSyx 		 * Has the search wrapped around and come back to the
496d6c23f6fSyx 		 * start ?
497d6c23f6fSyx 		 */
498d6c23f6fSyx 		if ((natl != NULL) &&
499d6c23f6fSyx 		    (np->in_pnext != 0) && (st_port == np->in_pnext) &&
500d6c23f6fSyx 		    !IP6_ISZERO(&np->in_next6) &&
501d6c23f6fSyx 		    IP6_EQ(&st_ip, &np->in_next6))
502d6c23f6fSyx 			return -1;
503d6c23f6fSyx 		l++;
504d6c23f6fSyx 	} while (natl != NULL);
505d6c23f6fSyx 
506d6c23f6fSyx 	if (np->in_space > 0)
507d6c23f6fSyx 		np->in_space--;
508d6c23f6fSyx 
509d6c23f6fSyx 	/* Setup the NAT table */
510d6c23f6fSyx 	nat->nat_inip6 = fin->fin_src6;
511d6c23f6fSyx 	nat->nat_outip6 = in;
512d6c23f6fSyx 	nat->nat_oip6 = fin->fin_dst6;
513d6c23f6fSyx 	if (nat->nat_hm == NULL)
514d6c23f6fSyx 		nat->nat_hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6,
515d6c23f6fSyx 		    &nat->nat_outip6, 0, ifs);
516d6c23f6fSyx 
517d6c23f6fSyx 	if (flags & IPN_TCPUDP) {
518d6c23f6fSyx 		nat->nat_inport = sport;
519d6c23f6fSyx 		nat->nat_outport = port;	/* sport */
520d6c23f6fSyx 		nat->nat_oport = dport;
521d6c23f6fSyx 		((tcphdr_t *)fin->fin_dp)->th_sport = port;
522d6c23f6fSyx 	} else if (flags & IPN_ICMPQUERY) {
523d6c23f6fSyx 		((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port;
524d6c23f6fSyx 		nat->nat_inport = port;
525d6c23f6fSyx 		nat->nat_outport = port;
526d6c23f6fSyx 	}
527*55fea89dSDan Cross 
528d6c23f6fSyx 	ni->nai_port = port;
529d6c23f6fSyx 	ni->nai_nport = dport;
530d6c23f6fSyx 	return 0;
531d6c23f6fSyx }
532d6c23f6fSyx 
533d6c23f6fSyx 
534d6c23f6fSyx /* ------------------------------------------------------------------------ */
535d6c23f6fSyx /* Function:    nat6_newrdr                                                 */
536d6c23f6fSyx /* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
537d6c23f6fSyx /*                    allow rule to be moved if IPN_ROUNDR is set.          */
538d6c23f6fSyx /* Parameters:  fin(I) - pointer to packet information                      */
539d6c23f6fSyx /*              nat(I) - pointer to NAT entry                               */
540d6c23f6fSyx /*              ni(I)  - pointer to structure with misc. information needed */
541d6c23f6fSyx /*                       to create new NAT entry.                           */
542d6c23f6fSyx /*                                                                          */
543d6c23f6fSyx /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
544d6c23f6fSyx /* to the new IP address for the translation.                               */
545d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_newrdr(fin,nat,ni)546d6c23f6fSyx static INLINE int nat6_newrdr(fin, nat, ni)
547d6c23f6fSyx fr_info_t *fin;
548d6c23f6fSyx nat_t *nat;
549d6c23f6fSyx natinfo_t *ni;
550d6c23f6fSyx {
551d6c23f6fSyx 	u_short nport, dport, sport;
552d6c23f6fSyx 	i6addr_t in;
553d6c23f6fSyx 	u_short sp, dp;
554d6c23f6fSyx 	hostmap_t *hm;
555d6c23f6fSyx 	u_32_t flags;
556d6c23f6fSyx 	ipnat_t *np;
557d6c23f6fSyx 	nat_t *natl;
558d6c23f6fSyx 	int move;
559d6c23f6fSyx 	ipf_stack_t *ifs = fin->fin_ifs;
560d6c23f6fSyx 
561d6c23f6fSyx 	move = 1;
562d6c23f6fSyx 	hm = NULL;
563d6c23f6fSyx 	in.i6[0] = 0;
564d6c23f6fSyx 	in.i6[1] = 0;
565d6c23f6fSyx 	in.i6[2] = 0;
566d6c23f6fSyx 	in.i6[3] = 0;
567d6c23f6fSyx 	np = ni->nai_np;
568d6c23f6fSyx 	flags = ni->nai_flags;
569d6c23f6fSyx 	sport = ni->nai_sport;
570d6c23f6fSyx 	dport = ni->nai_dport;
571d6c23f6fSyx 
572d6c23f6fSyx 	/*
573d6c23f6fSyx 	 * If the matching rule has IPN_STICKY set, then we want to have the
574d6c23f6fSyx 	 * same rule kick in as before.  Why would this happen?  If you have
575d6c23f6fSyx 	 * a collection of rdr rules with "round-robin sticky", the current
576d6c23f6fSyx 	 * packet might match a different one to the previous connection but
577d6c23f6fSyx 	 * we want the same destination to be used.
578d6c23f6fSyx 	 */
579d6c23f6fSyx 	if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) ==
580d6c23f6fSyx 	    (IPN_ROUNDR|IPN_STICKY)) {
581d6c23f6fSyx 		hm = nat6_hostmap(NULL, &fin->fin_src6, &fin->fin_dst6, &in,
582d6c23f6fSyx 		    (u_32_t)dport, ifs);
583d6c23f6fSyx 		if (hm != NULL) {
584d6c23f6fSyx 			in = hm->hm_map;
585d6c23f6fSyx 			np = hm->hm_ipnat;
586d6c23f6fSyx 			ni->nai_np = np;
587d6c23f6fSyx 			move = 0;
588d6c23f6fSyx 		}
589d6c23f6fSyx 	}
590d6c23f6fSyx 
591d6c23f6fSyx 	/*
592d6c23f6fSyx 	 * Otherwise, it's an inbound packet. Most likely, we don't
593d6c23f6fSyx 	 * want to rewrite source ports and source addresses. Instead,
594d6c23f6fSyx 	 * we want to rewrite to a fixed internal address and fixed
595d6c23f6fSyx 	 * internal port.
596d6c23f6fSyx 	 */
597d6c23f6fSyx 	if (np->in_flags & IPN_SPLIT) {
598d6c23f6fSyx 		in = np->in_next6;
599d6c23f6fSyx 
600d6c23f6fSyx 		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
601d6c23f6fSyx 			hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6,
602d6c23f6fSyx 			    &in, (u_32_t)dport, ifs);
603d6c23f6fSyx 			if (hm != NULL) {
604d6c23f6fSyx 				in = hm->hm_map;
605d6c23f6fSyx 				move = 0;
606d6c23f6fSyx 			}
607d6c23f6fSyx 		}
608d6c23f6fSyx 
609d6c23f6fSyx 		if (hm == NULL || hm->hm_ref == 1) {
610d6c23f6fSyx 			if (IP6_EQ(&np->in_in[0], &in)) {
611d6c23f6fSyx 				np->in_next6 = np->in_in[1];
612d6c23f6fSyx 				move = 0;
613d6c23f6fSyx 			} else {
614d6c23f6fSyx 				np->in_next6 = np->in_in[0];
615d6c23f6fSyx 			}
616d6c23f6fSyx 		}
617d6c23f6fSyx 
618d6c23f6fSyx 	} else if (IP6_ISZERO(&np->in_in[0]) &&
619d6c23f6fSyx 	    IP6_ISONES(&np->in_in[1])) {
620d6c23f6fSyx 		/*
621d6c23f6fSyx 		 * 0/128 - use the interface's IP address.
622d6c23f6fSyx 		 */
623d6c23f6fSyx 		if (fr_ifpaddr(6, FRI_NORMAL, fin->fin_ifp, (void *)&in, NULL,
624d6c23f6fSyx 			   fin->fin_ifs) == -1)
625d6c23f6fSyx 			return -1;
626d6c23f6fSyx 
627d6c23f6fSyx 	} else if (IP6_ISZERO(&np->in_in[0]) &&
628d6c23f6fSyx 	    IP6_ISZERO(&np->in_in[1])) {
629d6c23f6fSyx 		/*
630d6c23f6fSyx 		 * 0/0 - use the original destination address/port.
631d6c23f6fSyx 		 */
632d6c23f6fSyx 		in = fin->fin_dst6;
633d6c23f6fSyx 
634d6c23f6fSyx 	} else if (np->in_redir == NAT_BIMAP &&
635d6c23f6fSyx 	    IP6_EQ(&np->in_in[1], &np->in_out[1])) {
636d6c23f6fSyx 		i6addr_t temp;
637d6c23f6fSyx 		/*
638d6c23f6fSyx 		 * map the address block in a 1:1 fashion
639d6c23f6fSyx 		 */
640d6c23f6fSyx 		temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_in[1].i6[0];
641d6c23f6fSyx 		temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_in[1].i6[1];
642d6c23f6fSyx 		temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_in[1].i6[2];
643d6c23f6fSyx 		temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_in[1].i6[3];
644d6c23f6fSyx 		in = np->in_in[0];
645d6c23f6fSyx 		IP6_MERGE(&in, &temp, &np->in_in[1]);
646d6c23f6fSyx 	} else {
647d6c23f6fSyx 		in = np->in_in[0];
648d6c23f6fSyx 	}
649d6c23f6fSyx 
650d6c23f6fSyx 	if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
651d6c23f6fSyx 		nport = dport;
652d6c23f6fSyx 	else {
653d6c23f6fSyx 		/*
654d6c23f6fSyx 		 * Whilst not optimized for the case where
655d6c23f6fSyx 		 * pmin == pmax, the gain is not significant.
656d6c23f6fSyx 		 */
657d6c23f6fSyx 		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
658d6c23f6fSyx 		    (np->in_pmin != np->in_pmax)) {
659d6c23f6fSyx 			nport = ntohs(dport) - ntohs(np->in_pmin) +
660d6c23f6fSyx 				ntohs(np->in_pnext);
661d6c23f6fSyx 			nport = htons(nport);
662d6c23f6fSyx 		} else
663d6c23f6fSyx 			nport = np->in_pnext;
664d6c23f6fSyx 	}
665d6c23f6fSyx 
666d6c23f6fSyx 	/*
667d6c23f6fSyx 	 * When the redirect-to address is set to 0.0.0.0, just
668d6c23f6fSyx 	 * assume a blank `forwarding' of the packet.  We don't
669d6c23f6fSyx 	 * setup any translation for this either.
670d6c23f6fSyx 	 */
671d6c23f6fSyx 	if (IP6_ISZERO(&in)) {
672d6c23f6fSyx 		if (nport == dport)
673d6c23f6fSyx 			return -1;
674d6c23f6fSyx 		in = fin->fin_dst6;
675d6c23f6fSyx 	}
676d6c23f6fSyx 
677d6c23f6fSyx 	/*
678d6c23f6fSyx 	 * Check to see if this redirect mapping already exists and if
679d6c23f6fSyx 	 * it does, return "failure" (allowing it to be created will just
680d6c23f6fSyx 	 * cause one or both of these "connections" to stop working.)
681d6c23f6fSyx 	 */
682d6c23f6fSyx 	sp = fin->fin_data[0];
683d6c23f6fSyx 	dp = fin->fin_data[1];
684d6c23f6fSyx 	fin->fin_data[1] = fin->fin_data[0];
685d6c23f6fSyx 	fin->fin_data[0] = ntohs(nport);
686d6c23f6fSyx 	natl = nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
687d6c23f6fSyx 	    (u_int)fin->fin_p, &in.in6, &fin->fin_src6.in6);
688d6c23f6fSyx 	fin->fin_data[0] = sp;
689d6c23f6fSyx 	fin->fin_data[1] = dp;
690d6c23f6fSyx 	if (natl != NULL)
691d6c23f6fSyx 		return -1;
692d6c23f6fSyx 
693d6c23f6fSyx 	nat->nat_inip6 = in;
694d6c23f6fSyx 	nat->nat_outip6 = fin->fin_dst6;
695d6c23f6fSyx 	nat->nat_oip6 = fin->fin_src6;
696d6c23f6fSyx 	if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
697d6c23f6fSyx 		nat->nat_hm = nat6_hostmap(np, &fin->fin_src6,
698d6c23f6fSyx 		    &fin->fin_dst6, &in, (u_32_t)dport, ifs);
699d6c23f6fSyx 
700d6c23f6fSyx 	ni->nai_nport = nport;
701d6c23f6fSyx 	ni->nai_port = sport;
702d6c23f6fSyx 
703d6c23f6fSyx 	if (flags & IPN_TCPUDP) {
704d6c23f6fSyx 		nat->nat_inport = nport;
705d6c23f6fSyx 		nat->nat_outport = dport;
706d6c23f6fSyx 		nat->nat_oport = sport;
707d6c23f6fSyx 		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
708d6c23f6fSyx 	} else if (flags & IPN_ICMPQUERY) {
709d6c23f6fSyx 		((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport;
710d6c23f6fSyx 		nat->nat_inport = nport;
711d6c23f6fSyx 		nat->nat_outport = nport;
712d6c23f6fSyx 	}
713d6c23f6fSyx 
714d6c23f6fSyx 	return move;
715d6c23f6fSyx }
716d6c23f6fSyx 
717d6c23f6fSyx /* ------------------------------------------------------------------------ */
718d6c23f6fSyx /* Function:    nat6_new                                                    */
719d6c23f6fSyx /* Returns:     nat_t* - NULL == failure to create new NAT structure,       */
720d6c23f6fSyx /*                       else pointer to new NAT structure                  */
721d6c23f6fSyx /* Parameters:  fin(I)       - pointer to packet information                */
722d6c23f6fSyx /*              np(I)        - pointer to NAT rule                          */
723d6c23f6fSyx /*              natsave(I)   - pointer to where to store NAT struct pointer */
724d6c23f6fSyx /*              flags(I)     - flags describing the current packet          */
725d6c23f6fSyx /*              direction(I) - direction of packet (in/out)                 */
726d6c23f6fSyx /* Write Lock:  ipf_nat                                                     */
727d6c23f6fSyx /*                                                                          */
728d6c23f6fSyx /* Attempts to create a new NAT entry.  Does not actually change the packet */
729d6c23f6fSyx /* in any way.                                                              */
730d6c23f6fSyx /*                                                                          */
731d6c23f6fSyx /* This fucntion is in three main parts: (1) deal with creating a new NAT   */
732d6c23f6fSyx /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
733d6c23f6fSyx /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
734d6c23f6fSyx /* and (3) building that structure and putting it into the NAT table(s).    */
735d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_new(fin,np,natsave,flags,direction)736d6c23f6fSyx nat_t *nat6_new(fin, np, natsave, flags, direction)
737d6c23f6fSyx fr_info_t *fin;
738d6c23f6fSyx ipnat_t *np;
739d6c23f6fSyx nat_t **natsave;
740d6c23f6fSyx u_int flags;
741d6c23f6fSyx int direction;
742d6c23f6fSyx {
743d6c23f6fSyx 	tcphdr_t *tcp = NULL;
744d6c23f6fSyx 	hostmap_t *hm = NULL;
745d6c23f6fSyx 	nat_t *nat, *natl;
746d6c23f6fSyx 	u_int nflags;
747d6c23f6fSyx 	natinfo_t ni;
748d6c23f6fSyx 	int move;
749d6c23f6fSyx 	ipf_stack_t *ifs = fin->fin_ifs;
750d6c23f6fSyx 
751d6c23f6fSyx 	/*
752ea8244dcSJohn Ojemann 	 * Trigger automatic call to ipf_extraflush() if the
753d6c23f6fSyx 	 * table has reached capcity specified by hi watermark.
754d6c23f6fSyx 	 */
755ea8244dcSJohn Ojemann 	if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_level_hi)
756d6c23f6fSyx 		ifs->ifs_nat_doflush = 1;
757d6c23f6fSyx 
758d6c23f6fSyx 	if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) {
759d6c23f6fSyx 		ifs->ifs_nat_stats.ns_memfail++;
760d6c23f6fSyx 		return NULL;
761d6c23f6fSyx 	}
762d6c23f6fSyx 
763d6c23f6fSyx 	move = 1;
764d6c23f6fSyx 	nflags = np->in_flags & flags;
765d6c23f6fSyx 	nflags &= NAT_FROMRULE;
766d6c23f6fSyx 
767d6c23f6fSyx 	ni.nai_np = np;
768d6c23f6fSyx 	ni.nai_nflags = nflags;
769d6c23f6fSyx 	ni.nai_flags = flags;
770d6c23f6fSyx 
771d6c23f6fSyx 	/* Give me a new nat */
772d6c23f6fSyx 	KMALLOC(nat, nat_t *);
773d6c23f6fSyx 	if (nat == NULL) {
774d6c23f6fSyx 		ifs->ifs_nat_stats.ns_memfail++;
775d6c23f6fSyx 		/*
776d6c23f6fSyx 		 * Try to automatically tune the max # of entries in the
777d6c23f6fSyx 		 * table allowed to be less than what will cause kmem_alloc()
778d6c23f6fSyx 		 * to fail and try to eliminate panics due to out of memory
779d6c23f6fSyx 		 * conditions arising.
780d6c23f6fSyx 		 */
781d6c23f6fSyx 		if (ifs->ifs_ipf_nattable_max > ifs->ifs_ipf_nattable_sz) {
782d6c23f6fSyx 			ifs->ifs_ipf_nattable_max =
783d6c23f6fSyx 			    ifs->ifs_nat_stats.ns_inuse - 100;
784d6c23f6fSyx 			printf("ipf_nattable_max reduced to %d\n",
785d6c23f6fSyx 			    ifs->ifs_ipf_nattable_max);
786d6c23f6fSyx 		}
787d6c23f6fSyx 		return NULL;
788d6c23f6fSyx 	}
789d6c23f6fSyx 
790d6c23f6fSyx 	if (flags & IPN_TCPUDP) {
791d6c23f6fSyx 		tcp = fin->fin_dp;
792d6c23f6fSyx 		ni.nai_sport = htons(fin->fin_sport);
793d6c23f6fSyx 		ni.nai_dport = htons(fin->fin_dport);
794d6c23f6fSyx 	} else if (flags & IPN_ICMPQUERY) {
795d6c23f6fSyx 		/*
796d6c23f6fSyx 		 * In the ICMP query NAT code, we translate the ICMP id fields
797d6c23f6fSyx 		 * to make them unique. This is indepedent of the ICMP type
798d6c23f6fSyx 		 * (e.g. in the unlikely event that a host sends an echo and
799d6c23f6fSyx 		 * an tstamp request with the same id, both packets will have
800d6c23f6fSyx 		 * their ip address/id field changed in the same way).
801d6c23f6fSyx 		 *
802d6c23f6fSyx 		 * The icmp_id field is used by the sender to identify the
803d6c23f6fSyx 		 * process making the icmp request. (the receiver justs
804d6c23f6fSyx 		 * copies it back in its response). So, it closely matches
805d6c23f6fSyx 		 * the concept of source port. We overlay sport, so we can
806d6c23f6fSyx 		 * maximally reuse the existing code.
807d6c23f6fSyx 		 */
808d6c23f6fSyx 		ni.nai_sport = ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id;
809d6c23f6fSyx 		ni.nai_dport = ni.nai_sport;
810d6c23f6fSyx 	}
811d6c23f6fSyx 
812d6c23f6fSyx 	bzero((char *)nat, sizeof (*nat));
813d6c23f6fSyx 	nat->nat_flags = flags;
814d6c23f6fSyx 	nat->nat_redir = np->in_redir;
815d6c23f6fSyx 
816d6c23f6fSyx 	if ((flags & NAT_SLAVE) == 0) {
817d6c23f6fSyx 		MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
818d6c23f6fSyx 	}
819d6c23f6fSyx 
820d6c23f6fSyx 	/*
821d6c23f6fSyx 	 * Search the current table for a match.
822d6c23f6fSyx 	 */
823d6c23f6fSyx 	if (direction == NAT_OUTBOUND) {
824d6c23f6fSyx 		/*
825d6c23f6fSyx 		 * We can now arrange to call this for the same connection
826d6c23f6fSyx 		 * because ipf_nat_new doesn't protect the code path into
827d6c23f6fSyx 		 * this function.
828d6c23f6fSyx 		 */
829d6c23f6fSyx 		natl = nat6_outlookup(fin, nflags, (u_int)fin->fin_p,
830d6c23f6fSyx 		    &fin->fin_src6.in6, &fin->fin_dst6.in6);
831d6c23f6fSyx 		if (natl != NULL) {
832d6c23f6fSyx 			KFREE(nat);
833d6c23f6fSyx 			nat = natl;
834d6c23f6fSyx 			goto done;
835d6c23f6fSyx 		}
836d6c23f6fSyx 
837d6c23f6fSyx 		move = nat6_newmap(fin, nat, &ni);
838d6c23f6fSyx 		if (move == -1)
839d6c23f6fSyx 			goto badnat;
840d6c23f6fSyx 
841d6c23f6fSyx 		np = ni.nai_np;
842d6c23f6fSyx 	} else {
843d6c23f6fSyx 		/*
844d6c23f6fSyx 		 * NAT_INBOUND is used only for redirects rules
845d6c23f6fSyx 		 */
846d6c23f6fSyx 		natl = nat6_inlookup(fin, nflags, (u_int)fin->fin_p,
847d6c23f6fSyx 		    &fin->fin_src6.in6, &fin->fin_dst6.in6);
848d6c23f6fSyx 		if (natl != NULL) {
849d6c23f6fSyx 			KFREE(nat);
850d6c23f6fSyx 			nat = natl;
851d6c23f6fSyx 			goto done;
852d6c23f6fSyx 		}
853d6c23f6fSyx 
854d6c23f6fSyx 		move = nat6_newrdr(fin, nat, &ni);
855d6c23f6fSyx 		if (move == -1)
856d6c23f6fSyx 			goto badnat;
857d6c23f6fSyx 
858d6c23f6fSyx 		np = ni.nai_np;
859d6c23f6fSyx 	}
860d6c23f6fSyx 
861d6c23f6fSyx 	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
862d6c23f6fSyx 		if (np->in_redir == NAT_REDIRECT) {
863d6c23f6fSyx 			nat_delrdr(np);
864d6c23f6fSyx 			nat6_addrdr(np, ifs);
865d6c23f6fSyx 		} else if (np->in_redir == NAT_MAP) {
866d6c23f6fSyx 			nat_delnat(np);
867d6c23f6fSyx 			nat6_addnat(np, ifs);
868d6c23f6fSyx 		}
869d6c23f6fSyx 	}
870d6c23f6fSyx 
871d6c23f6fSyx 	if (nat6_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) {
872d6c23f6fSyx 		goto badnat;
873d6c23f6fSyx 	}
874d6c23f6fSyx 
875d6c23f6fSyx 	nat_calc_chksum_diffs(nat);
876d6c23f6fSyx 
877d6c23f6fSyx 	if (flags & SI_WILDP)
878d6c23f6fSyx 		ifs->ifs_nat_stats.ns_wilds++;
879d6c23f6fSyx 	goto done;
880d6c23f6fSyx badnat:
881d6c23f6fSyx 	ifs->ifs_nat_stats.ns_badnat++;
882d6c23f6fSyx 	if ((hm = nat->nat_hm) != NULL)
883d6c23f6fSyx 		fr_hostmapdel(&hm);
884d6c23f6fSyx 	KFREE(nat);
885d6c23f6fSyx 	nat = NULL;
886d6c23f6fSyx done:
887d6c23f6fSyx 	if ((flags & NAT_SLAVE) == 0) {
888d6c23f6fSyx 		MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
889d6c23f6fSyx 	}
890d6c23f6fSyx 	return nat;
891d6c23f6fSyx }
892d6c23f6fSyx 
893d6c23f6fSyx 
894d6c23f6fSyx /* ------------------------------------------------------------------------ */
895d6c23f6fSyx /* Function:    nat6_finalise                                               */
896d6c23f6fSyx /* Returns:     int - 0 == sucess, -1 == failure                            */
897d6c23f6fSyx /* Parameters:  fin(I) - pointer to packet information                      */
898d6c23f6fSyx /*              nat(I) - pointer to NAT entry                               */
899d6c23f6fSyx /*              ni(I)  - pointer to structure with misc. information needed */
900d6c23f6fSyx /*                       to create new NAT entry.                           */
901d6c23f6fSyx /* Write Lock:  ipf_nat                                                     */
902d6c23f6fSyx /*                                                                          */
903d6c23f6fSyx /* This is the tail end of constructing a new NAT entry and is the same     */
904d6c23f6fSyx /* for both IPv4 and IPv6.                                                  */
905d6c23f6fSyx /* ------------------------------------------------------------------------ */
906d6c23f6fSyx /*ARGSUSED*/
nat6_finalise(fin,nat,ni,tcp,natsave,direction)907d6c23f6fSyx static INLINE int nat6_finalise(fin, nat, ni, tcp, natsave, direction)
908d6c23f6fSyx fr_info_t *fin;
909d6c23f6fSyx nat_t *nat;
910d6c23f6fSyx natinfo_t *ni;
911d6c23f6fSyx tcphdr_t *tcp;
912d6c23f6fSyx nat_t **natsave;
913d6c23f6fSyx int direction;
914d6c23f6fSyx {
915d6c23f6fSyx 	frentry_t *fr;
916d6c23f6fSyx 	ipnat_t *np;
917d6c23f6fSyx 	ipf_stack_t *ifs = fin->fin_ifs;
918d6c23f6fSyx 
919d6c23f6fSyx 	np = ni->nai_np;
920d6c23f6fSyx 
921d6c23f6fSyx 	COPYIFNAME(fin->fin_ifp, nat->nat_ifnames[0], fin->fin_v);
922d6c23f6fSyx 
923d6c23f6fSyx #ifdef	IPFILTER_SYNC
924d6c23f6fSyx 	if ((nat->nat_flags & SI_CLONE) == 0)
925d6c23f6fSyx 		nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat);
926d6c23f6fSyx #endif
927d6c23f6fSyx 
928d6c23f6fSyx 	nat->nat_me = natsave;
929d6c23f6fSyx 	nat->nat_dir = direction;
930d6c23f6fSyx 	nat->nat_ifps[0] = np->in_ifps[0];
931d6c23f6fSyx 	nat->nat_ifps[1] = np->in_ifps[1];
932d6c23f6fSyx 	nat->nat_ptr = np;
933d6c23f6fSyx 	nat->nat_p = fin->fin_p;
934d6c23f6fSyx 	nat->nat_v = fin->fin_v;
935d6c23f6fSyx 	nat->nat_mssclamp = np->in_mssclamp;
936d6c23f6fSyx 	fr = fin->fin_fr;
937d6c23f6fSyx 	nat->nat_fr = fr;
938d6c23f6fSyx 	nat->nat_v = 6;
939d6c23f6fSyx 
940d6c23f6fSyx #ifdef	IPF_V6_PROXIES
941d6c23f6fSyx 	if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0))
942d6c23f6fSyx 		if (appr_new(fin, nat) == -1)
943d6c23f6fSyx 			return -1;
944d6c23f6fSyx #endif
945d6c23f6fSyx 
946d6c23f6fSyx 	if (nat6_insert(nat, fin->fin_rev, ifs) == 0) {
947d6c23f6fSyx 		if (ifs->ifs_nat_logging)
948d6c23f6fSyx 			nat_log(nat, (u_int)np->in_redir, ifs);
949d6c23f6fSyx 		np->in_use++;
950d6c23f6fSyx 		if (fr != NULL) {
951d6c23f6fSyx 			MUTEX_ENTER(&fr->fr_lock);
952d6c23f6fSyx 			fr->fr_ref++;
953d6c23f6fSyx 			MUTEX_EXIT(&fr->fr_lock);
954d6c23f6fSyx 		}
955d6c23f6fSyx 		return 0;
956d6c23f6fSyx 	}
957d6c23f6fSyx 
958d6c23f6fSyx 	/*
959d6c23f6fSyx 	 * nat6_insert failed, so cleanup time...
960d6c23f6fSyx 	 */
961d6c23f6fSyx 	return -1;
962d6c23f6fSyx }
963d6c23f6fSyx 
964d6c23f6fSyx 
965d6c23f6fSyx /* ------------------------------------------------------------------------ */
966d6c23f6fSyx /* Function:   nat6_insert                                                  */
967d6c23f6fSyx /* Returns:    int - 0 == sucess, -1 == failure                             */
968d6c23f6fSyx /* Parameters: nat(I) - pointer to NAT structure                            */
969d6c23f6fSyx /*             rev(I) - flag indicating forward/reverse direction of packet */
970d6c23f6fSyx /* Write Lock: ipf_nat                                                      */
971d6c23f6fSyx /*                                                                          */
972d6c23f6fSyx /* Insert a NAT entry into the hash tables for searching and add it to the  */
973d6c23f6fSyx /* list of active NAT entries.  Adjust global counters when complete.       */
974d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_insert(nat,rev,ifs)975d6c23f6fSyx int nat6_insert(nat, rev, ifs)
976d6c23f6fSyx nat_t	*nat;
977d6c23f6fSyx int	rev;
978d6c23f6fSyx ipf_stack_t *ifs;
979d6c23f6fSyx {
980d6c23f6fSyx 	u_int hv1, hv2;
981d6c23f6fSyx 	nat_t **natp;
982d6c23f6fSyx 
983d6c23f6fSyx 	/*
984d6c23f6fSyx 	 * Try and return an error as early as possible, so calculate the hash
985d6c23f6fSyx 	 * entry numbers first and then proceed.
986d6c23f6fSyx 	 */
987d6c23f6fSyx 	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
988d6c23f6fSyx 		hv1 = NAT_HASH_FN6(&nat->nat_inip6, nat->nat_inport,
989d6c23f6fSyx 				  0xffffffff);
990d6c23f6fSyx 		hv1 = NAT_HASH_FN6(&nat->nat_oip6, hv1 + nat->nat_oport,
991d6c23f6fSyx 				  ifs->ifs_ipf_nattable_sz);
992d6c23f6fSyx 		hv2 = NAT_HASH_FN6(&nat->nat_outip6, nat->nat_outport,
993d6c23f6fSyx 				  0xffffffff);
994d6c23f6fSyx 		hv2 = NAT_HASH_FN6(&nat->nat_oip6, hv2 + nat->nat_oport,
995d6c23f6fSyx 				  ifs->ifs_ipf_nattable_sz);
996d6c23f6fSyx 	} else {
997d6c23f6fSyx 		hv1 = NAT_HASH_FN6(&nat->nat_inip6, 0, 0xffffffff);
998d6c23f6fSyx 		hv1 = NAT_HASH_FN6(&nat->nat_oip6, hv1,
999d6c23f6fSyx 				  ifs->ifs_ipf_nattable_sz);
1000d6c23f6fSyx 		hv2 = NAT_HASH_FN6(&nat->nat_outip6, 0, 0xffffffff);
1001d6c23f6fSyx 		hv2 = NAT_HASH_FN6(&nat->nat_oip6, hv2,
1002d6c23f6fSyx 				  ifs->ifs_ipf_nattable_sz);
1003d6c23f6fSyx 	}
1004d6c23f6fSyx 
1005d6c23f6fSyx 	if ((ifs->ifs_nat_stats.ns_bucketlen[0][hv1] >=
1006d6c23f6fSyx 	    ifs->ifs_fr_nat_maxbucket) ||
1007d6c23f6fSyx 	    (ifs->ifs_nat_stats.ns_bucketlen[1][hv2] >=
1008d6c23f6fSyx 	    ifs->ifs_fr_nat_maxbucket)) {
1009d6c23f6fSyx 		return -1;
1010d6c23f6fSyx 	}
1011d6c23f6fSyx 
1012d6c23f6fSyx 	nat->nat_hv[0] = hv1;
1013d6c23f6fSyx 	nat->nat_hv[1] = hv2;
1014d6c23f6fSyx 
1015d6c23f6fSyx 	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
1016d6c23f6fSyx 
1017d6c23f6fSyx 	nat->nat_rev = rev;
1018d6c23f6fSyx 	nat->nat_ref = 1;
1019d6c23f6fSyx 	nat->nat_bytes[0] = 0;
1020d6c23f6fSyx 	nat->nat_pkts[0] = 0;
1021d6c23f6fSyx 	nat->nat_bytes[1] = 0;
1022d6c23f6fSyx 	nat->nat_pkts[1] = 0;
1023d6c23f6fSyx 
1024d6c23f6fSyx 	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
1025d6c23f6fSyx 	nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 6, ifs);
1026d6c23f6fSyx 
1027d6c23f6fSyx 	if (nat->nat_ifnames[1][0] !='\0') {
1028d6c23f6fSyx 		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
1029d6c23f6fSyx 		nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 6, ifs);
1030d6c23f6fSyx 	} else {
1031d6c23f6fSyx 		(void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0],
1032d6c23f6fSyx 			       LIFNAMSIZ);
1033d6c23f6fSyx 		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
1034d6c23f6fSyx 		nat->nat_ifps[1] = nat->nat_ifps[0];
1035d6c23f6fSyx 	}
1036d6c23f6fSyx 
1037d6c23f6fSyx 	nat->nat_next = ifs->ifs_nat_instances;
1038d6c23f6fSyx 	nat->nat_pnext = &ifs->ifs_nat_instances;
1039d6c23f6fSyx 	if (ifs->ifs_nat_instances)
1040d6c23f6fSyx 		ifs->ifs_nat_instances->nat_pnext = &nat->nat_next;
1041d6c23f6fSyx 	ifs->ifs_nat_instances = nat;
1042d6c23f6fSyx 
1043d6c23f6fSyx 	natp = &ifs->ifs_nat_table[0][hv1];
1044d6c23f6fSyx 	if (*natp)
1045d6c23f6fSyx 		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
1046d6c23f6fSyx 	nat->nat_phnext[0] = natp;
1047d6c23f6fSyx 	nat->nat_hnext[0] = *natp;
1048d6c23f6fSyx 	*natp = nat;
1049d6c23f6fSyx 	ifs->ifs_nat_stats.ns_bucketlen[0][hv1]++;
1050d6c23f6fSyx 
1051d6c23f6fSyx 	natp = &ifs->ifs_nat_table[1][hv2];
1052d6c23f6fSyx 	if (*natp)
1053d6c23f6fSyx 		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
1054d6c23f6fSyx 	nat->nat_phnext[1] = natp;
1055d6c23f6fSyx 	nat->nat_hnext[1] = *natp;
1056d6c23f6fSyx 	*natp = nat;
1057d6c23f6fSyx 	ifs->ifs_nat_stats.ns_bucketlen[1][hv2]++;
1058d6c23f6fSyx 
1059d6c23f6fSyx 	fr_setnatqueue(nat, rev, ifs);
1060d6c23f6fSyx 
1061d6c23f6fSyx 	ifs->ifs_nat_stats.ns_added++;
1062d6c23f6fSyx 	ifs->ifs_nat_stats.ns_inuse++;
1063d6c23f6fSyx 	return 0;
1064d6c23f6fSyx }
1065d6c23f6fSyx 
1066d6c23f6fSyx 
1067d6c23f6fSyx /* ------------------------------------------------------------------------ */
1068d6c23f6fSyx /* Function:    nat6_icmperrorlookup                                        */
1069d6c23f6fSyx /* Returns:     nat_t* - point to matching NAT structure                    */
1070d6c23f6fSyx /* Parameters:  fin(I) - pointer to packet information                      */
1071d6c23f6fSyx /*              dir(I) - direction of packet (in/out)                       */
1072d6c23f6fSyx /*                                                                          */
1073d6c23f6fSyx /* Check if the ICMP error message is related to an existing TCP, UDP or    */
1074d6c23f6fSyx /* ICMP query nat entry.  It is assumed that the packet is already of the   */
1075d6c23f6fSyx /* the required length.                                                     */
1076d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_icmperrorlookup(fin,dir)1077d6c23f6fSyx nat_t *nat6_icmperrorlookup(fin, dir)
1078d6c23f6fSyx fr_info_t *fin;
1079d6c23f6fSyx int dir;
1080d6c23f6fSyx {
1081d6c23f6fSyx 	int flags = 0, minlen;
1082d6c23f6fSyx 	struct icmp6_hdr *orgicmp;
1083d6c23f6fSyx 	tcphdr_t *tcp = NULL;
1084d6c23f6fSyx 	u_short data[2];
1085d6c23f6fSyx 	nat_t *nat;
1086d6c23f6fSyx 	ip6_t *oip6;
1087d6c23f6fSyx 	u_int p;
1088d6c23f6fSyx 
1089d6c23f6fSyx 	minlen = 40;
1090d6c23f6fSyx 	/*
1091d6c23f6fSyx 	 * Does it at least have the return (basic) IP header ?
1092d6c23f6fSyx 	 * Only a basic IP header (no options) should be with an ICMP error
1093d6c23f6fSyx 	 * header.  Also, if it's not an error type, then return.
1094d6c23f6fSyx 	 */
1095d6c23f6fSyx 	if (!(fin->fin_flx & FI_ICMPERR))
1096d6c23f6fSyx 		return NULL;
1097d6c23f6fSyx 
1098d6c23f6fSyx 	/*
1099d6c23f6fSyx 	 * Check packet size
1100d6c23f6fSyx 	 */
1101d6c23f6fSyx 	if (fin->fin_plen < ICMP6ERR_IPICMPHLEN)
1102d6c23f6fSyx 		return NULL;
1103d6c23f6fSyx 	oip6 = (ip6_t *)((char *)fin->fin_dp + 8);
1104d6c23f6fSyx 
1105d6c23f6fSyx 	/*
1106d6c23f6fSyx 	 * Is the buffer big enough for all of it ?  It's the size of the IP
1107d6c23f6fSyx 	 * header claimed in the encapsulated part which is of concern.  It
1108d6c23f6fSyx 	 * may be too big to be in this buffer but not so big that it's
1109d6c23f6fSyx 	 * outside the ICMP packet, leading to TCP deref's causing problems.
1110d6c23f6fSyx 	 * This is possible because we don't know how big oip_hl is when we
1111d6c23f6fSyx 	 * do the pullup early in fr_check() and thus can't gaurantee it is
1112d6c23f6fSyx 	 * all here now.
1113d6c23f6fSyx 	 */
1114d6c23f6fSyx #ifdef  _KERNEL
1115d6c23f6fSyx 	{
1116d6c23f6fSyx 	mb_t *m;
1117d6c23f6fSyx 
1118d6c23f6fSyx 	m = fin->fin_m;
1119d6c23f6fSyx # if defined(MENTAT)
1120d6c23f6fSyx 	if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr)
1121d6c23f6fSyx 		return NULL;
1122d6c23f6fSyx # else
1123d6c23f6fSyx 	if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN >
1124d6c23f6fSyx 	    (char *)fin->fin_ip + M_LEN(m))
1125d6c23f6fSyx 		return NULL;
1126d6c23f6fSyx # endif
1127d6c23f6fSyx 	}
1128d6c23f6fSyx #endif
1129d6c23f6fSyx 
1130d6c23f6fSyx 	if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src))
1131d6c23f6fSyx 		return NULL;
1132d6c23f6fSyx 
1133d6c23f6fSyx 	p = oip6->ip6_nxt;
1134d6c23f6fSyx 	if (p == IPPROTO_TCP)
1135d6c23f6fSyx 		flags = IPN_TCP;
1136d6c23f6fSyx 	else if (p == IPPROTO_UDP)
1137d6c23f6fSyx 		flags = IPN_UDP;
1138d6c23f6fSyx 	else if (p == IPPROTO_ICMPV6) {
1139d6c23f6fSyx 		orgicmp = (struct icmp6_hdr *)(oip6 + 1);
1140d6c23f6fSyx 
1141d6c23f6fSyx 		/* see if this is related to an ICMP query */
1142d6c23f6fSyx 		if (nat_icmpquerytype6(orgicmp->icmp6_type)) {
1143d6c23f6fSyx 			data[0] = fin->fin_data[0];
1144d6c23f6fSyx 			data[1] = fin->fin_data[1];
1145d6c23f6fSyx 			fin->fin_data[0] = 0;
1146d6c23f6fSyx 			fin->fin_data[1] = orgicmp->icmp6_id;
1147d6c23f6fSyx 
1148d6c23f6fSyx 			flags = IPN_ICMPERR|IPN_ICMPQUERY;
1149d6c23f6fSyx 			/*
1150d6c23f6fSyx 			 * NOTE : dir refers to the direction of the original
1151d6c23f6fSyx 			 *        ip packet. By definition the icmp error
1152d6c23f6fSyx 			 *        message flows in the opposite direction.
1153d6c23f6fSyx 			 */
1154d6c23f6fSyx 			if (dir == NAT_INBOUND)
1155d6c23f6fSyx 				nat = nat6_inlookup(fin, flags, p,
1156d6c23f6fSyx 				    &oip6->ip6_dst, &oip6->ip6_src);
1157d6c23f6fSyx 			else
1158d6c23f6fSyx 				nat = nat6_outlookup(fin, flags, p,
1159d6c23f6fSyx 				    &oip6->ip6_dst, &oip6->ip6_src);
1160d6c23f6fSyx 			fin->fin_data[0] = data[0];
1161d6c23f6fSyx 			fin->fin_data[1] = data[1];
1162d6c23f6fSyx 			return nat;
1163d6c23f6fSyx 		}
1164d6c23f6fSyx 	}
1165d6c23f6fSyx 
1166d6c23f6fSyx 	if (flags & IPN_TCPUDP) {
1167d6c23f6fSyx 		minlen += 8;		/* + 64bits of data to get ports */
1168d6c23f6fSyx 		if (fin->fin_plen < ICMPERR_ICMPHLEN + minlen)
1169d6c23f6fSyx 			return NULL;
1170d6c23f6fSyx 
1171d6c23f6fSyx 		data[0] = fin->fin_data[0];
1172d6c23f6fSyx 		data[1] = fin->fin_data[1];
1173d6c23f6fSyx 		tcp = (tcphdr_t *)(oip6 + 1);
1174d6c23f6fSyx 		fin->fin_data[0] = ntohs(tcp->th_dport);
1175d6c23f6fSyx 		fin->fin_data[1] = ntohs(tcp->th_sport);
1176d6c23f6fSyx 
1177d6c23f6fSyx 		if (dir == NAT_INBOUND) {
1178d6c23f6fSyx 			nat = nat6_inlookup(fin, flags, p,
1179d6c23f6fSyx 			    &oip6->ip6_dst, &oip6->ip6_src);
1180d6c23f6fSyx 		} else {
1181d6c23f6fSyx 			nat = nat6_outlookup(fin, flags, p,
1182d6c23f6fSyx 			    &oip6->ip6_dst, &oip6->ip6_src);
1183d6c23f6fSyx 		}
1184d6c23f6fSyx 		fin->fin_data[0] = data[0];
1185d6c23f6fSyx 		fin->fin_data[1] = data[1];
1186d6c23f6fSyx 		return nat;
1187d6c23f6fSyx 	}
1188d6c23f6fSyx 	if (dir == NAT_INBOUND)
1189d6c23f6fSyx 		return nat6_inlookup(fin, 0, p, &oip6->ip6_dst, &oip6->ip6_src);
1190d6c23f6fSyx 	else
1191d6c23f6fSyx 		return nat6_outlookup(fin, 0, p, &oip6->ip6_dst,
1192d6c23f6fSyx 		    &oip6->ip6_src);
1193d6c23f6fSyx }
1194d6c23f6fSyx 
1195d6c23f6fSyx 
1196d6c23f6fSyx /* ------------------------------------------------------------------------ */
1197d6c23f6fSyx /* Function:    nat6_icmperror                                              */
1198d6c23f6fSyx /* Returns:     nat_t* - point to matching NAT structure                    */
1199d6c23f6fSyx /* Parameters:  fin(I)    - pointer to packet information                   */
1200d6c23f6fSyx /*              nflags(I) - NAT flags for this packet                       */
1201d6c23f6fSyx /*              dir(I)    - direction of packet (in/out)                    */
1202d6c23f6fSyx /*                                                                          */
1203d6c23f6fSyx /* Fix up an ICMP packet which is an error message for an existing NAT      */
1204d6c23f6fSyx /* session.  This will correct both packet header data and checksums.       */
1205d6c23f6fSyx /*                                                                          */
1206d6c23f6fSyx /* This should *ONLY* be used for incoming ICMP error packets to make sure  */
1207d6c23f6fSyx /* a NAT'd ICMP packet gets correctly recognised.                           */
1208d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_icmperror(fin,nflags,dir)1209d6c23f6fSyx nat_t *nat6_icmperror(fin, nflags, dir)
1210d6c23f6fSyx fr_info_t *fin;
1211d6c23f6fSyx u_int *nflags;
1212d6c23f6fSyx int dir;
1213d6c23f6fSyx {
1214d6c23f6fSyx 	u_32_t sum1, sum2, sumd, psum1, psum2, psumd, sumd1;
1215d6c23f6fSyx 	i6addr_t in;
1216d6c23f6fSyx 	struct icmp6_hdr *icmp6, *orgicmp;
1217d6c23f6fSyx 	int dlen;
1218d6c23f6fSyx 	udphdr_t *udp;
1219d6c23f6fSyx 	tcphdr_t *tcp;
1220d6c23f6fSyx 	nat_t *nat;
1221d6c23f6fSyx 	ip6_t *oip6;
1222d6c23f6fSyx 	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY)))
1223d6c23f6fSyx 		return NULL;
1224d6c23f6fSyx 
1225d6c23f6fSyx 	/*
1226d6c23f6fSyx 	 * nat6_icmperrorlookup() looks up nat entry associated with the
1227d6c23f6fSyx 	 * offending IP packet and returns pointer to the entry, or NULL
1228d6c23f6fSyx 	 * if packet wasn't natted or for `defective' packets.
1229d6c23f6fSyx 	 */
1230d6c23f6fSyx 
1231d6c23f6fSyx 	if ((fin->fin_v != 6) || !(nat = nat6_icmperrorlookup(fin, dir)))
1232d6c23f6fSyx 		return NULL;
1233d6c23f6fSyx 
1234d6c23f6fSyx 	sumd1 = 0;
1235d6c23f6fSyx 	*nflags = IPN_ICMPERR;
1236d6c23f6fSyx 	icmp6 = fin->fin_dp;
1237d6c23f6fSyx 	oip6 = (ip6_t *)((char *)icmp6 + sizeof (*icmp6));
1238d6c23f6fSyx 	udp = (udphdr_t *)(((char *)oip6) + sizeof (*oip6));
1239d6c23f6fSyx 	tcp = (tcphdr_t *)udp;
1240d6c23f6fSyx 	dlen = fin->fin_plen - ((char *)udp - (char *)fin->fin_ip);
1241d6c23f6fSyx 
1242d6c23f6fSyx 	/*
1243d6c23f6fSyx 	 * Need to adjust ICMP header to include the real IP#'s and
1244d6c23f6fSyx 	 * port #'s.  There are three steps required.
1245d6c23f6fSyx 	 *
1246d6c23f6fSyx 	 * Step 1
1247d6c23f6fSyx 	 * No update needed for ip6 header checksum.
1248d6c23f6fSyx 	 *
1249d6c23f6fSyx 	 * Unlike IPv4, we need to update icmp_cksum for IPv6 address
1250d6c23f6fSyx 	 * changes because there's no ip_sum change to cancel it.
1251d6c23f6fSyx 	 */
1252d6c23f6fSyx 
1253d6c23f6fSyx 	if (IP6_EQ((i6addr_t *)&oip6->ip6_dst, &nat->nat_oip6)) {
1254d6c23f6fSyx 		sum1 = LONG_SUM6((i6addr_t *)&oip6->ip6_src);
1255d6c23f6fSyx 		in = nat->nat_inip6;
1256d6c23f6fSyx 		oip6->ip6_src = in.in6;
1257d6c23f6fSyx 	} else {
1258d6c23f6fSyx 		sum1 = LONG_SUM6((i6addr_t *)&oip6->ip6_dst);
1259d6c23f6fSyx 		in = nat->nat_outip6;
1260d6c23f6fSyx 		oip6->ip6_dst = in.in6;
1261d6c23f6fSyx 	}
1262d6c23f6fSyx 
1263d6c23f6fSyx 	sum2 = LONG_SUM6(&in);
1264d6c23f6fSyx 	CALC_SUMD(sum1, sum2, sumd);
1265d6c23f6fSyx 
1266d6c23f6fSyx 	/*
1267d6c23f6fSyx 	 * Step 2
1268d6c23f6fSyx 	 * Perform other adjustments based on protocol of offending packet.
1269d6c23f6fSyx 	 */
1270d6c23f6fSyx 
1271d6c23f6fSyx 	switch (oip6->ip6_nxt) {
1272d6c23f6fSyx 		case IPPROTO_TCP :
1273d6c23f6fSyx 		case IPPROTO_UDP :
1274d6c23f6fSyx 
1275d6c23f6fSyx 			/*
1276d6c23f6fSyx 			* For offending TCP/UDP IP packets, translate the ports
1277d6c23f6fSyx 			* based on the NAT specification.
1278d6c23f6fSyx 			*
1279d6c23f6fSyx 			* Advance notice : Now it becomes complicated :-)
1280d6c23f6fSyx 			*
1281d6c23f6fSyx 			* Since the port and IP addresse fields are both part
1282d6c23f6fSyx 			* of the TCP/UDP checksum of the offending IP packet,
1283d6c23f6fSyx 			* we need to adjust that checksum as well.
1284d6c23f6fSyx 			*
1285d6c23f6fSyx 			* To further complicate things, the TCP/UDP checksum
1286d6c23f6fSyx 			* may not be present.  We must check to see if the
1287d6c23f6fSyx 			* length of the data portion is big enough to hold
1288d6c23f6fSyx 			* the checksum.  In the UDP case, a test to determine
1289d6c23f6fSyx 			* if the checksum is even set is also required.
1290d6c23f6fSyx 			*
1291d6c23f6fSyx 			* Any changes to an IP address, port or checksum within
1292d6c23f6fSyx 			* the ICMP packet requires a change to icmp_cksum.
1293d6c23f6fSyx 			*
1294d6c23f6fSyx 			* Be extremely careful here ... The change is dependent
1295d6c23f6fSyx 			* upon whether or not the TCP/UPD checksum is present.
1296d6c23f6fSyx 			*
1297d6c23f6fSyx 			* If TCP/UPD checksum is present, the icmp_cksum must
1298d6c23f6fSyx 			* compensate for checksum modification resulting from
1299d6c23f6fSyx 			* IP address change only.  Port change and resulting
1300d6c23f6fSyx 			* data checksum adjustments cancel each other out.
1301d6c23f6fSyx 			*
1302d6c23f6fSyx 			* If TCP/UDP checksum is not present, icmp_cksum must
1303d6c23f6fSyx 			* compensate for port change only.  The IP address
1304d6c23f6fSyx 			* change does not modify anything else in this case.
1305d6c23f6fSyx 			*/
1306d6c23f6fSyx 
1307d6c23f6fSyx 			psum1 = 0;
1308d6c23f6fSyx 			psum2 = 0;
1309d6c23f6fSyx 			psumd = 0;
1310d6c23f6fSyx 
1311d6c23f6fSyx 			if ((tcp->th_dport == nat->nat_oport) &&
1312d6c23f6fSyx 			    (tcp->th_sport != nat->nat_inport)) {
1313d6c23f6fSyx 
1314d6c23f6fSyx 				/*
1315d6c23f6fSyx 				 * Translate the source port.
1316d6c23f6fSyx 				 */
1317d6c23f6fSyx 
1318d6c23f6fSyx 				psum1 = ntohs(tcp->th_sport);
1319d6c23f6fSyx 				psum2 = ntohs(nat->nat_inport);
1320d6c23f6fSyx 				tcp->th_sport = nat->nat_inport;
1321d6c23f6fSyx 
1322d6c23f6fSyx 			} else if ((tcp->th_sport == nat->nat_oport) &&
1323d6c23f6fSyx 				    (tcp->th_dport != nat->nat_outport)) {
1324d6c23f6fSyx 
1325d6c23f6fSyx 				/*
1326d6c23f6fSyx 				 * Translate the destination port.
1327d6c23f6fSyx 				 */
1328d6c23f6fSyx 
1329d6c23f6fSyx 				psum1 = ntohs(tcp->th_dport);
1330d6c23f6fSyx 				psum2 = ntohs(nat->nat_outport);
1331d6c23f6fSyx 				tcp->th_dport = nat->nat_outport;
1332d6c23f6fSyx 			}
1333d6c23f6fSyx 
1334d6c23f6fSyx 			if ((oip6->ip6_nxt == IPPROTO_TCP) && (dlen >= 18)) {
1335d6c23f6fSyx 
1336d6c23f6fSyx 				/*
1337d6c23f6fSyx 				 * TCP checksum present.
1338d6c23f6fSyx 				 *
1339d6c23f6fSyx 				 * Adjust data checksum and icmp checksum to
1340d6c23f6fSyx 				 * compensate for any IP address change.
1341d6c23f6fSyx 				 */
1342d6c23f6fSyx 
1343d6c23f6fSyx 				sum1 = ntohs(tcp->th_sum);
1344d6c23f6fSyx 				fix_datacksum(&tcp->th_sum, sumd);
1345d6c23f6fSyx 				sum2 = ntohs(tcp->th_sum);
1346d6c23f6fSyx 				CALC_SUMD(sum1, sum2, sumd);
1347d6c23f6fSyx 				sumd1 += sumd;
1348d6c23f6fSyx 
1349d6c23f6fSyx 				/*
1350d6c23f6fSyx 				 * Also make data checksum adjustment to
1351d6c23f6fSyx 				 * compensate for any port change.
1352d6c23f6fSyx 				 */
1353d6c23f6fSyx 
1354d6c23f6fSyx 				if (psum1 != psum2) {
1355d6c23f6fSyx 					CALC_SUMD(psum1, psum2, psumd);
1356d6c23f6fSyx 					fix_datacksum(&tcp->th_sum, psumd);
1357d6c23f6fSyx 				}
1358d6c23f6fSyx 
1359d6c23f6fSyx 			} else if ((oip6->ip6_nxt == IPPROTO_UDP) &&
1360d6c23f6fSyx 				   (dlen >= 8) && (udp->uh_sum != 0)) {
1361d6c23f6fSyx 
1362d6c23f6fSyx 				/*
1363d6c23f6fSyx 				 * The UDP checksum is present and set.
1364d6c23f6fSyx 				 *
1365d6c23f6fSyx 				 * Adjust data checksum and icmp checksum to
1366d6c23f6fSyx 				 * compensate for any IP address change.
1367d6c23f6fSyx 				 */
1368d6c23f6fSyx 
1369d6c23f6fSyx 				sum1 = ntohs(udp->uh_sum);
1370d6c23f6fSyx 				fix_datacksum(&udp->uh_sum, sumd);
1371d6c23f6fSyx 				sum2 = ntohs(udp->uh_sum);
1372d6c23f6fSyx 				CALC_SUMD(sum1, sum2, sumd);
1373d6c23f6fSyx 				sumd1 += sumd;
1374d6c23f6fSyx 
1375d6c23f6fSyx 				/*
1376d6c23f6fSyx 				 * Also make data checksum adjustment to
1377d6c23f6fSyx 				 * compensate for any port change.
1378d6c23f6fSyx 				 */
1379d6c23f6fSyx 
1380d6c23f6fSyx 				if (psum1 != psum2) {
1381d6c23f6fSyx 					CALC_SUMD(psum1, psum2, psumd);
1382d6c23f6fSyx 					fix_datacksum(&udp->uh_sum, psumd);
1383d6c23f6fSyx 				}
1384d6c23f6fSyx 
1385d6c23f6fSyx 			} else {
1386d6c23f6fSyx 
1387d6c23f6fSyx 				/*
1388d6c23f6fSyx 				 * Data checksum was not present.
1389d6c23f6fSyx 				 *
1390d6c23f6fSyx 				 * Compensate for any port change.
1391d6c23f6fSyx 				 */
1392d6c23f6fSyx 
1393d6c23f6fSyx 				CALC_SUMD(psum2, psum1, psumd);
1394d6c23f6fSyx 				sumd1 += psumd;
1395d6c23f6fSyx 			}
1396d6c23f6fSyx 			break;
1397d6c23f6fSyx 
1398d6c23f6fSyx 		case IPPROTO_ICMPV6 :
1399d6c23f6fSyx 
1400d6c23f6fSyx 			orgicmp = (struct icmp6_hdr *)udp;
1401d6c23f6fSyx 
1402d6c23f6fSyx 			if ((nat->nat_dir == NAT_OUTBOUND) &&
1403d6c23f6fSyx 			    (orgicmp->icmp6_id != nat->nat_inport) &&
1404d6c23f6fSyx 			    (dlen >= 8)) {
1405d6c23f6fSyx 
1406d6c23f6fSyx 				/*
1407d6c23f6fSyx 				 * Fix ICMP checksum (of the offening ICMP
1408d6c23f6fSyx 				 * query packet) to compensate the change
1409d6c23f6fSyx 				 * in the ICMP id of the offending ICMP
1410d6c23f6fSyx 				 * packet.
1411d6c23f6fSyx 				 *
1412d6c23f6fSyx 				 * Since you modify orgicmp->icmp_id with
1413d6c23f6fSyx 				 * a delta (say x) and you compensate that
1414d6c23f6fSyx 				 * in origicmp->icmp_cksum with a delta
1415d6c23f6fSyx 				 * minus x, you don't have to adjust the
1416d6c23f6fSyx 				 * overall icmp->icmp_cksum
1417d6c23f6fSyx 				 */
1418d6c23f6fSyx 
1419d6c23f6fSyx 				sum1 = ntohs(orgicmp->icmp6_id);
1420d6c23f6fSyx 				sum2 = ntohs(nat->nat_inport);
1421d6c23f6fSyx 				CALC_SUMD(sum1, sum2, sumd);
1422d6c23f6fSyx 				orgicmp->icmp6_id = nat->nat_inport;
1423d6c23f6fSyx 				fix_datacksum(&orgicmp->icmp6_cksum, sumd);
1424d6c23f6fSyx 
1425d6c23f6fSyx 			} /* nat_dir can't be NAT_INBOUND for icmp queries */
1426d6c23f6fSyx 
1427d6c23f6fSyx 			break;
1428d6c23f6fSyx 
1429d6c23f6fSyx 		default :
1430d6c23f6fSyx 
1431d6c23f6fSyx 			break;
1432d6c23f6fSyx 
1433d6c23f6fSyx 	} /* switch (oip6->ip6_nxt) */
1434d6c23f6fSyx 
1435d6c23f6fSyx 	/*
1436d6c23f6fSyx 	 * Step 3
1437d6c23f6fSyx 	 * Make the adjustments to icmp checksum.
1438d6c23f6fSyx 	 */
1439d6c23f6fSyx 
1440d6c23f6fSyx 	if (sumd1 != 0) {
1441d6c23f6fSyx 		sumd1 = (sumd1 & 0xffff) + (sumd1 >> 16);
1442d6c23f6fSyx 		sumd1 = (sumd1 & 0xffff) + (sumd1 >> 16);
1443d6c23f6fSyx 		fix_incksum(&icmp6->icmp6_cksum, sumd1);
1444d6c23f6fSyx 	}
1445d6c23f6fSyx 	return nat;
1446d6c23f6fSyx }
1447d6c23f6fSyx 
1448d6c23f6fSyx 
1449d6c23f6fSyx /*
1450d6c23f6fSyx  * NB: these lookups don't lock access to the list, it assumed that it has
1451d6c23f6fSyx  * already been done!
1452d6c23f6fSyx  */
1453d6c23f6fSyx 
1454d6c23f6fSyx /* ------------------------------------------------------------------------ */
1455d6c23f6fSyx /* Function:    nat6_inlookup                                               */
1456d6c23f6fSyx /* Returns:     nat_t* - NULL == no match,                                  */
1457d6c23f6fSyx /*                       else pointer to matching NAT entry                 */
1458d6c23f6fSyx /* Parameters:  fin(I)    - pointer to packet information                   */
1459d6c23f6fSyx /*              flags(I)  - NAT flags for this packet                       */
1460d6c23f6fSyx /*              p(I)      - protocol for this packet                        */
1461d6c23f6fSyx /*              src(I)    - source IP address                               */
1462d6c23f6fSyx /*              mapdst(I) - destination IP address                          */
1463d6c23f6fSyx /*                                                                          */
1464d6c23f6fSyx /* Lookup a nat entry based on the mapped destination ip address/port and   */
1465d6c23f6fSyx /* real source address/port.  We use this lookup when receiving a packet,   */
1466d6c23f6fSyx /* we're looking for a table entry, based on the destination address.       */
1467d6c23f6fSyx /*                                                                          */
1468d6c23f6fSyx /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
1469d6c23f6fSyx /*                                                                          */
1470d6c23f6fSyx /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
1471d6c23f6fSyx /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
1472d6c23f6fSyx /*                                                                          */
1473d6c23f6fSyx /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
1474d6c23f6fSyx /*            the packet is of said protocol                                */
1475d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_inlookup(fin,flags,p,src,mapdst)1476d6c23f6fSyx nat_t *nat6_inlookup(fin, flags, p, src, mapdst)
1477d6c23f6fSyx fr_info_t *fin;
1478d6c23f6fSyx u_int flags, p;
1479d6c23f6fSyx struct in6_addr *src, *mapdst;
1480d6c23f6fSyx {
1481d6c23f6fSyx 	u_short sport, dport;
1482d6c23f6fSyx 	u_int sflags;
1483d6c23f6fSyx 	nat_t *nat;
1484d6c23f6fSyx 	int nflags;
1485d6c23f6fSyx 	i6addr_t dst;
1486d6c23f6fSyx 	void *ifp;
1487d6c23f6fSyx 	u_int hv;
1488d6c23f6fSyx 	ipf_stack_t *ifs = fin->fin_ifs;
1489d6c23f6fSyx 
1490d6c23f6fSyx 	if (fin != NULL)
1491d6c23f6fSyx 		ifp = fin->fin_ifp;
1492d6c23f6fSyx 	else
1493d6c23f6fSyx 		ifp = NULL;
1494d6c23f6fSyx 	sport = 0;
1495d6c23f6fSyx 	dport = 0;
1496d6c23f6fSyx 	dst.in6 = *mapdst;
1497d6c23f6fSyx 	sflags = flags & NAT_TCPUDPICMP;
1498d6c23f6fSyx 
1499d6c23f6fSyx 	switch (p)
1500d6c23f6fSyx 	{
1501d6c23f6fSyx 	case IPPROTO_TCP :
1502d6c23f6fSyx 	case IPPROTO_UDP :
1503d6c23f6fSyx 		sport = htons(fin->fin_data[0]);
1504d6c23f6fSyx 		dport = htons(fin->fin_data[1]);
1505d6c23f6fSyx 		break;
1506d6c23f6fSyx 	case IPPROTO_ICMPV6 :
1507d6c23f6fSyx 		if (flags & IPN_ICMPERR)
1508d6c23f6fSyx 			sport = fin->fin_data[1];
1509d6c23f6fSyx 		else
1510d6c23f6fSyx 			dport = fin->fin_data[1];
1511d6c23f6fSyx 		break;
1512d6c23f6fSyx 	default :
1513d6c23f6fSyx 		break;
1514d6c23f6fSyx 	}
1515d6c23f6fSyx 
1516d6c23f6fSyx 
1517d6c23f6fSyx 	if ((flags & SI_WILDP) != 0)
1518d6c23f6fSyx 		goto find_in_wild_ports;
1519d6c23f6fSyx 
1520d6c23f6fSyx 	hv = NAT_HASH_FN6(&dst, dport, 0xffffffff);
1521d6c23f6fSyx 	hv = NAT_HASH_FN6(src, hv + sport, ifs->ifs_ipf_nattable_sz);
1522d6c23f6fSyx 	nat = ifs->ifs_nat_table[1][hv];
1523d6c23f6fSyx 	for (; nat; nat = nat->nat_hnext[1]) {
1524d6c23f6fSyx 		if (nat->nat_v != 6)
1525d6c23f6fSyx 			continue;
1526d6c23f6fSyx 
1527d6c23f6fSyx 		if (nat->nat_ifps[0] != NULL) {
1528d6c23f6fSyx 			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
1529d6c23f6fSyx 				continue;
1530d6c23f6fSyx 		} else if (ifp != NULL)
1531d6c23f6fSyx 			nat->nat_ifps[0] = ifp;
1532d6c23f6fSyx 
1533d6c23f6fSyx 		nflags = nat->nat_flags;
1534d6c23f6fSyx 
1535d6c23f6fSyx 		if (IP6_EQ(&nat->nat_oip6, src) &&
1536d6c23f6fSyx 		    IP6_EQ(&nat->nat_outip6, &dst) &&
1537d6c23f6fSyx 		    (((p == 0) &&
1538d6c23f6fSyx 		    (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) ||
1539d6c23f6fSyx 		    (p == nat->nat_p))) {
1540d6c23f6fSyx 			switch (p)
1541d6c23f6fSyx 			{
1542d6c23f6fSyx #if 0
1543d6c23f6fSyx 			case IPPROTO_GRE :
1544d6c23f6fSyx 				if (nat->nat_call[1] != fin->fin_data[0])
1545d6c23f6fSyx 					continue;
1546d6c23f6fSyx 				break;
1547d6c23f6fSyx #endif
1548d6c23f6fSyx 			case IPPROTO_ICMPV6 :
1549d6c23f6fSyx 				if ((flags & IPN_ICMPERR) != 0) {
1550d6c23f6fSyx 					if (nat->nat_outport != sport)
1551d6c23f6fSyx 						continue;
1552d6c23f6fSyx 				} else {
1553d6c23f6fSyx 					if (nat->nat_outport != dport)
1554d6c23f6fSyx 						continue;
1555d6c23f6fSyx 				}
1556d6c23f6fSyx 				break;
1557d6c23f6fSyx 			case IPPROTO_TCP :
1558d6c23f6fSyx 			case IPPROTO_UDP :
1559d6c23f6fSyx 				if (nat->nat_oport != sport)
1560d6c23f6fSyx 					continue;
1561d6c23f6fSyx 				if (nat->nat_outport != dport)
1562d6c23f6fSyx 					continue;
1563d6c23f6fSyx 				break;
1564d6c23f6fSyx 			default :
1565d6c23f6fSyx 				break;
1566d6c23f6fSyx 			}
1567d6c23f6fSyx 
1568d6c23f6fSyx #ifdef	IPF_V6_PROXIES
1569d6c23f6fSyx 			ipn = nat->nat_ptr;
1570d6c23f6fSyx 			if ((ipn != NULL) && (nat->nat_aps != NULL))
1571d6c23f6fSyx 				if (appr_match(fin, nat) != 0)
1572d6c23f6fSyx 					continue;
1573d6c23f6fSyx #endif
1574d6c23f6fSyx 			return nat;
1575d6c23f6fSyx 		}
1576d6c23f6fSyx 	}
1577d6c23f6fSyx 
1578d6c23f6fSyx 	/*
1579d6c23f6fSyx 	 * So if we didn't find it but there are wildcard members in the hash
1580d6c23f6fSyx 	 * table, go back and look for them.  We do this search and update here
1581d6c23f6fSyx 	 * because it is modifying the NAT table and we want to do this only
1582d6c23f6fSyx 	 * for the first packet that matches.  The exception, of course, is
1583d6c23f6fSyx 	 * for "dummy" (FI_IGNORE) lookups.
1584d6c23f6fSyx 	 */
1585d6c23f6fSyx find_in_wild_ports:
1586d6c23f6fSyx 	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
1587d6c23f6fSyx 		return NULL;
1588d6c23f6fSyx 	if (ifs->ifs_nat_stats.ns_wilds == 0)
1589d6c23f6fSyx 		return NULL;
1590d6c23f6fSyx 
1591d6c23f6fSyx 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
1592d6c23f6fSyx 
1593d6c23f6fSyx 	hv = NAT_HASH_FN6(&dst, 0, 0xffffffff);
1594d6c23f6fSyx 	hv = NAT_HASH_FN6(src, hv, ifs->ifs_ipf_nattable_sz);
1595d6c23f6fSyx 
1596d6c23f6fSyx 	WRITE_ENTER(&ifs->ifs_ipf_nat);
1597d6c23f6fSyx 
1598d6c23f6fSyx 	nat = ifs->ifs_nat_table[1][hv];
1599d6c23f6fSyx 	for (; nat; nat = nat->nat_hnext[1]) {
1600d6c23f6fSyx 		if (nat->nat_v != 6)
1601d6c23f6fSyx 			continue;
1602d6c23f6fSyx 
1603d6c23f6fSyx 		if (nat->nat_ifps[0] != NULL) {
1604d6c23f6fSyx 			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
1605d6c23f6fSyx 				continue;
1606d6c23f6fSyx 		} else if (ifp != NULL)
1607d6c23f6fSyx 			nat->nat_ifps[0] = ifp;
1608d6c23f6fSyx 
1609d6c23f6fSyx 		if (nat->nat_p != fin->fin_p)
1610d6c23f6fSyx 			continue;
1611d6c23f6fSyx 		if (IP6_NEQ(&nat->nat_oip6, src) ||
1612d6c23f6fSyx 		    IP6_NEQ(&nat->nat_outip6, &dst))
1613d6c23f6fSyx 			continue;
1614d6c23f6fSyx 
1615d6c23f6fSyx 		nflags = nat->nat_flags;
1616d6c23f6fSyx 		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
1617d6c23f6fSyx 			continue;
1618d6c23f6fSyx 
1619d6c23f6fSyx 		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
1620d6c23f6fSyx 			       NAT_INBOUND) == 1) {
1621d6c23f6fSyx 			if ((fin->fin_flx & FI_IGNORE) != 0)
1622d6c23f6fSyx 				break;
1623d6c23f6fSyx 			if ((nflags & SI_CLONE) != 0) {
1624d6c23f6fSyx 				nat = fr_natclone(fin, nat);
1625d6c23f6fSyx 				if (nat == NULL)
1626d6c23f6fSyx 					break;
1627d6c23f6fSyx 			} else {
1628d6c23f6fSyx 				MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
1629d6c23f6fSyx 				ifs->ifs_nat_stats.ns_wilds--;
1630d6c23f6fSyx 				MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
1631d6c23f6fSyx 			}
1632d6c23f6fSyx 			nat->nat_oport = sport;
1633d6c23f6fSyx 			nat->nat_outport = dport;
1634d6c23f6fSyx 			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
1635d6c23f6fSyx 			nat6_tabmove(nat, ifs);
1636d6c23f6fSyx 			break;
1637d6c23f6fSyx 		}
1638d6c23f6fSyx 	}
1639d6c23f6fSyx 
1640d6c23f6fSyx 	MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
1641d6c23f6fSyx 
1642d6c23f6fSyx 	return nat;
1643d6c23f6fSyx }
1644d6c23f6fSyx 
1645d6c23f6fSyx 
1646d6c23f6fSyx /* ------------------------------------------------------------------------ */
1647d6c23f6fSyx /* Function:    nat6_tabmove                                                */
1648d6c23f6fSyx /* Returns:     Nil                                                         */
1649d6c23f6fSyx /* Parameters:  nat(I) - pointer to NAT structure                           */
1650d6c23f6fSyx /* Write Lock:  ipf_nat                                                     */
1651d6c23f6fSyx /*                                                                          */
1652d6c23f6fSyx /* This function is only called for TCP/UDP NAT table entries where the     */
1653d6c23f6fSyx /* original was placed in the table without hashing on the ports and we now */
1654d6c23f6fSyx /* want to include hashing on port numbers.                                 */
1655d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_tabmove(nat,ifs)1656d6c23f6fSyx static void nat6_tabmove(nat, ifs)
1657d6c23f6fSyx nat_t *nat;
1658d6c23f6fSyx ipf_stack_t *ifs;
1659d6c23f6fSyx {
1660d6c23f6fSyx 	nat_t **natp;
1661d6c23f6fSyx 	u_int hv;
1662d6c23f6fSyx 
1663d6c23f6fSyx 	if (nat->nat_flags & SI_CLONE)
1664d6c23f6fSyx 		return;
1665d6c23f6fSyx 
1666d6c23f6fSyx 	/*
1667d6c23f6fSyx 	 * Remove the NAT entry from the old location
1668d6c23f6fSyx 	 */
1669d6c23f6fSyx 	if (nat->nat_hnext[0])
1670d6c23f6fSyx 		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
1671d6c23f6fSyx 	*nat->nat_phnext[0] = nat->nat_hnext[0];
1672d6c23f6fSyx 	ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
1673d6c23f6fSyx 
1674d6c23f6fSyx 	if (nat->nat_hnext[1])
1675d6c23f6fSyx 		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
1676d6c23f6fSyx 	*nat->nat_phnext[1] = nat->nat_hnext[1];
1677d6c23f6fSyx 	ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
1678d6c23f6fSyx 
1679d6c23f6fSyx 	/*
1680d6c23f6fSyx 	 * Add into the NAT table in the new position
1681d6c23f6fSyx 	 */
1682d6c23f6fSyx 	hv = NAT_HASH_FN6(&nat->nat_inip6, nat->nat_inport, 0xffffffff);
1683d6c23f6fSyx 	hv = NAT_HASH_FN6(&nat->nat_oip6, hv + nat->nat_oport,
1684d6c23f6fSyx 			 ifs->ifs_ipf_nattable_sz);
1685d6c23f6fSyx 	nat->nat_hv[0] = hv;
1686d6c23f6fSyx 	natp = &ifs->ifs_nat_table[0][hv];
1687d6c23f6fSyx 	if (*natp)
1688d6c23f6fSyx 		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
1689d6c23f6fSyx 	nat->nat_phnext[0] = natp;
1690d6c23f6fSyx 	nat->nat_hnext[0] = *natp;
1691d6c23f6fSyx 	*natp = nat;
1692d6c23f6fSyx 	ifs->ifs_nat_stats.ns_bucketlen[0][hv]++;
1693d6c23f6fSyx 
1694d6c23f6fSyx 	hv = NAT_HASH_FN6(&nat->nat_outip6, nat->nat_outport, 0xffffffff);
1695d6c23f6fSyx 	hv = NAT_HASH_FN6(&nat->nat_oip6, hv + nat->nat_oport,
1696d6c23f6fSyx 			 ifs->ifs_ipf_nattable_sz);
1697d6c23f6fSyx 	nat->nat_hv[1] = hv;
1698d6c23f6fSyx 	natp = &ifs->ifs_nat_table[1][hv];
1699d6c23f6fSyx 	if (*natp)
1700d6c23f6fSyx 		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
1701d6c23f6fSyx 	nat->nat_phnext[1] = natp;
1702d6c23f6fSyx 	nat->nat_hnext[1] = *natp;
1703d6c23f6fSyx 	*natp = nat;
1704d6c23f6fSyx 	ifs->ifs_nat_stats.ns_bucketlen[1][hv]++;
1705d6c23f6fSyx }
1706d6c23f6fSyx 
1707d6c23f6fSyx 
1708d6c23f6fSyx /* ------------------------------------------------------------------------ */
1709d6c23f6fSyx /* Function:    nat6_outlookup                                              */
1710d6c23f6fSyx /* Returns:     nat_t* - NULL == no match,                                  */
1711d6c23f6fSyx /*                       else pointer to matching NAT entry                 */
1712d6c23f6fSyx /* Parameters:  fin(I)   - pointer to packet information                    */
1713d6c23f6fSyx /*              flags(I) - NAT flags for this packet                        */
1714d6c23f6fSyx /*              p(I)     - protocol for this packet                         */
1715d6c23f6fSyx /*              src(I)   - source IP address                                */
1716d6c23f6fSyx /*              dst(I)   - destination IP address                           */
1717d6c23f6fSyx /*              rw(I)    - 1 == write lock on ipf_nat held, 0 == read lock. */
1718d6c23f6fSyx /*                                                                          */
1719d6c23f6fSyx /* Lookup a nat entry based on the source 'real' ip address/port and        */
1720d6c23f6fSyx /* destination address/port.  We use this lookup when sending a packet out, */
1721d6c23f6fSyx /* we're looking for a table entry, based on the source address.            */
1722d6c23f6fSyx /*                                                                          */
1723d6c23f6fSyx /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
1724d6c23f6fSyx /*                                                                          */
1725d6c23f6fSyx /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
1726d6c23f6fSyx /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
1727d6c23f6fSyx /*                                                                          */
1728d6c23f6fSyx /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
1729d6c23f6fSyx /*            the packet is of said protocol                                */
1730d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_outlookup(fin,flags,p,src,dst)1731d6c23f6fSyx nat_t *nat6_outlookup(fin, flags, p, src, dst)
1732d6c23f6fSyx fr_info_t *fin;
1733d6c23f6fSyx u_int flags, p;
1734d6c23f6fSyx struct in6_addr *src , *dst;
1735d6c23f6fSyx {
1736d6c23f6fSyx 	u_short sport, dport;
1737d6c23f6fSyx 	u_int sflags;
1738d6c23f6fSyx 	nat_t *nat;
1739d6c23f6fSyx 	int nflags;
1740d6c23f6fSyx 	void *ifp;
1741d6c23f6fSyx 	u_int hv;
1742d6c23f6fSyx 	ipf_stack_t *ifs = fin->fin_ifs;
1743d6c23f6fSyx 
1744d6c23f6fSyx 	ifp = fin->fin_ifp;
1745d6c23f6fSyx 
1746d6c23f6fSyx 	sflags = flags & IPN_TCPUDPICMP;
1747d6c23f6fSyx 	sport = 0;
1748d6c23f6fSyx 	dport = 0;
1749d6c23f6fSyx 
1750d6c23f6fSyx 	switch (p)
1751d6c23f6fSyx 	{
1752d6c23f6fSyx 	case IPPROTO_TCP :
1753d6c23f6fSyx 	case IPPROTO_UDP :
1754d6c23f6fSyx 		sport = htons(fin->fin_data[0]);
1755d6c23f6fSyx 		dport = htons(fin->fin_data[1]);
1756d6c23f6fSyx 		break;
1757d6c23f6fSyx 	case IPPROTO_ICMPV6 :
1758d6c23f6fSyx 		if (flags & IPN_ICMPERR)
1759d6c23f6fSyx 			sport = fin->fin_data[1];
1760d6c23f6fSyx 		else
1761d6c23f6fSyx 			dport = fin->fin_data[1];
1762d6c23f6fSyx 		break;
1763d6c23f6fSyx 	default :
1764d6c23f6fSyx 		break;
1765d6c23f6fSyx 	}
1766d6c23f6fSyx 
1767d6c23f6fSyx 	if ((flags & SI_WILDP) != 0)
1768d6c23f6fSyx 		goto find_out_wild_ports;
1769d6c23f6fSyx 
1770d6c23f6fSyx 	hv = NAT_HASH_FN6(src, sport, 0xffffffff);
1771d6c23f6fSyx 	hv = NAT_HASH_FN6(dst, hv + dport, ifs->ifs_ipf_nattable_sz);
1772d6c23f6fSyx 	nat = ifs->ifs_nat_table[0][hv];
1773d6c23f6fSyx 	for (; nat; nat = nat->nat_hnext[0]) {
1774d6c23f6fSyx 		if (nat->nat_v != 6)
1775d6c23f6fSyx 			continue;
1776d6c23f6fSyx 
1777d6c23f6fSyx 		if (nat->nat_ifps[1] != NULL) {
1778d6c23f6fSyx 			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
1779d6c23f6fSyx 				continue;
1780d6c23f6fSyx 		} else if (ifp != NULL)
1781d6c23f6fSyx 			nat->nat_ifps[1] = ifp;
1782d6c23f6fSyx 
1783d6c23f6fSyx 		nflags = nat->nat_flags;
1784*55fea89dSDan Cross 
1785d6c23f6fSyx 		if (IP6_EQ(&nat->nat_inip6, src) &&
1786d6c23f6fSyx 		    IP6_EQ(&nat->nat_oip6, dst) &&
1787d6c23f6fSyx 		    (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) ||
1788d6c23f6fSyx 		    (p == nat->nat_p))) {
1789d6c23f6fSyx 			switch (p)
1790d6c23f6fSyx 			{
1791d6c23f6fSyx #if 0
1792d6c23f6fSyx 			case IPPROTO_GRE :
1793d6c23f6fSyx 				if (nat->nat_call[1] != fin->fin_data[0])
1794d6c23f6fSyx 					continue;
1795d6c23f6fSyx 				break;
1796d6c23f6fSyx #endif
1797d6c23f6fSyx 			case IPPROTO_TCP :
1798d6c23f6fSyx 			case IPPROTO_UDP :
1799d6c23f6fSyx 				if (nat->nat_oport != dport)
1800d6c23f6fSyx 					continue;
1801d6c23f6fSyx 				if (nat->nat_inport != sport)
1802d6c23f6fSyx 					continue;
1803d6c23f6fSyx 				break;
1804d6c23f6fSyx 			default :
1805d6c23f6fSyx 				break;
1806d6c23f6fSyx 			}
1807d6c23f6fSyx 
1808d6c23f6fSyx #ifdef	IPF_V6_PROXIES
1809d6c23f6fSyx 			ipn = nat->nat_ptr;
1810d6c23f6fSyx 			if ((ipn != NULL) && (nat->nat_aps != NULL))
1811d6c23f6fSyx 				if (appr_match(fin, nat) != 0)
1812d6c23f6fSyx 					continue;
1813d6c23f6fSyx #endif
1814d6c23f6fSyx 			return nat;
1815d6c23f6fSyx 		}
1816d6c23f6fSyx 	}
1817d6c23f6fSyx 
1818d6c23f6fSyx 	/*
1819d6c23f6fSyx 	 * So if we didn't find it but there are wildcard members in the hash
1820d6c23f6fSyx 	 * table, go back and look for them.  We do this search and update here
1821d6c23f6fSyx 	 * because it is modifying the NAT table and we want to do this only
1822d6c23f6fSyx 	 * for the first packet that matches.  The exception, of course, is
1823d6c23f6fSyx 	 * for "dummy" (FI_IGNORE) lookups.
1824d6c23f6fSyx 	 */
1825d6c23f6fSyx find_out_wild_ports:
1826d6c23f6fSyx 	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
1827d6c23f6fSyx 		return NULL;
1828d6c23f6fSyx 	if (ifs->ifs_nat_stats.ns_wilds == 0)
1829d6c23f6fSyx 		return NULL;
1830d6c23f6fSyx 
1831d6c23f6fSyx 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
1832d6c23f6fSyx 
1833d6c23f6fSyx 	hv = NAT_HASH_FN6(src, 0, 0xffffffff);
1834d6c23f6fSyx 	hv = NAT_HASH_FN6(dst, hv, ifs->ifs_ipf_nattable_sz);
1835d6c23f6fSyx 
1836d6c23f6fSyx 	WRITE_ENTER(&ifs->ifs_ipf_nat);
1837d6c23f6fSyx 
1838d6c23f6fSyx 	nat = ifs->ifs_nat_table[0][hv];
1839d6c23f6fSyx 	for (; nat; nat = nat->nat_hnext[0]) {
1840d6c23f6fSyx 		if (nat->nat_v != 6)
1841d6c23f6fSyx 			continue;
1842d6c23f6fSyx 
1843d6c23f6fSyx 		if (nat->nat_ifps[1] != NULL) {
1844d6c23f6fSyx 			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
1845d6c23f6fSyx 				continue;
1846d6c23f6fSyx 		} else if (ifp != NULL)
1847d6c23f6fSyx 			nat->nat_ifps[1] = ifp;
1848d6c23f6fSyx 
1849d6c23f6fSyx 		if (nat->nat_p != fin->fin_p)
1850d6c23f6fSyx 			continue;
1851d6c23f6fSyx 		if (IP6_NEQ(&nat->nat_inip6, src) ||
1852d6c23f6fSyx 		    IP6_NEQ(&nat->nat_oip6, dst))
1853d6c23f6fSyx 			continue;
1854d6c23f6fSyx 
1855d6c23f6fSyx 		nflags = nat->nat_flags;
1856d6c23f6fSyx 		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
1857d6c23f6fSyx 			continue;
1858d6c23f6fSyx 
1859d6c23f6fSyx 		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
1860d6c23f6fSyx 			       NAT_OUTBOUND) == 1) {
1861d6c23f6fSyx 			if ((fin->fin_flx & FI_IGNORE) != 0)
1862d6c23f6fSyx 				break;
1863d6c23f6fSyx 			if ((nflags & SI_CLONE) != 0) {
1864d6c23f6fSyx 				nat = fr_natclone(fin, nat);
1865d6c23f6fSyx 				if (nat == NULL)
1866d6c23f6fSyx 					break;
1867d6c23f6fSyx 			} else {
1868d6c23f6fSyx 				MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
1869d6c23f6fSyx 				ifs->ifs_nat_stats.ns_wilds--;
1870d6c23f6fSyx 				MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
1871d6c23f6fSyx 			}
1872d6c23f6fSyx 			nat->nat_inport = sport;
1873d6c23f6fSyx 			nat->nat_oport = dport;
1874d6c23f6fSyx 			if (nat->nat_outport == 0)
1875d6c23f6fSyx 				nat->nat_outport = sport;
1876d6c23f6fSyx 			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
1877d6c23f6fSyx 			nat6_tabmove(nat, ifs);
1878d6c23f6fSyx 			break;
1879d6c23f6fSyx 		}
1880d6c23f6fSyx 	}
1881d6c23f6fSyx 
1882d6c23f6fSyx 	MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
1883d6c23f6fSyx 
1884d6c23f6fSyx 	return nat;
1885d6c23f6fSyx }
1886d6c23f6fSyx 
1887d6c23f6fSyx 
1888d6c23f6fSyx /* ------------------------------------------------------------------------ */
1889d6c23f6fSyx /* Function:    nat6_lookupredir                                            */
1890d6c23f6fSyx /* Returns:     nat_t* - NULL == no match,                                  */
1891d6c23f6fSyx /*                       else pointer to matching NAT entry                 */
1892d6c23f6fSyx /* Parameters:  np(I) - pointer to description of packet to find NAT table  */
1893d6c23f6fSyx /*                      entry for.                                          */
1894d6c23f6fSyx /*                                                                          */
1895d6c23f6fSyx /* Lookup the NAT tables to search for a matching redirect                  */
1896d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_lookupredir(np,ifs)1897d6c23f6fSyx nat_t *nat6_lookupredir(np, ifs)
1898d6c23f6fSyx natlookup_t *np;
1899d6c23f6fSyx ipf_stack_t *ifs;
1900d6c23f6fSyx {
1901d6c23f6fSyx 	fr_info_t fi;
1902d6c23f6fSyx 	nat_t *nat;
1903d6c23f6fSyx 
1904d6c23f6fSyx 	bzero((char *)&fi, sizeof (fi));
1905d6c23f6fSyx 	if (np->nl_flags & IPN_IN) {
1906d6c23f6fSyx 		fi.fin_data[0] = ntohs(np->nl_realport);
1907d6c23f6fSyx 		fi.fin_data[1] = ntohs(np->nl_outport);
1908d6c23f6fSyx 	} else {
1909d6c23f6fSyx 		fi.fin_data[0] = ntohs(np->nl_inport);
1910d6c23f6fSyx 		fi.fin_data[1] = ntohs(np->nl_outport);
1911d6c23f6fSyx 	}
1912d6c23f6fSyx 	if (np->nl_flags & IPN_TCP)
1913d6c23f6fSyx 		fi.fin_p = IPPROTO_TCP;
1914d6c23f6fSyx 	else if (np->nl_flags & IPN_UDP)
1915d6c23f6fSyx 		fi.fin_p = IPPROTO_UDP;
1916d6c23f6fSyx 	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
1917d6c23f6fSyx 		fi.fin_p = IPPROTO_ICMPV6;
1918d6c23f6fSyx 
1919d6c23f6fSyx 	fi.fin_ifs = ifs;
1920d6c23f6fSyx 	/*
1921d6c23f6fSyx 	 * We can do two sorts of lookups:
1922d6c23f6fSyx 	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
1923d6c23f6fSyx 	 * - default: we have the `in' and `out' address, look for `real'.
1924d6c23f6fSyx 	 */
1925d6c23f6fSyx 	if (np->nl_flags & IPN_IN) {
1926d6c23f6fSyx 		if ((nat = nat6_inlookup(&fi, np->nl_flags, fi.fin_p,
1927d6c23f6fSyx 					 &np->nl_realip6, &np->nl_outip6))) {
1928d6c23f6fSyx 			np->nl_inipaddr = nat->nat_inip6;
1929d6c23f6fSyx 			np->nl_inport = nat->nat_inport;
1930d6c23f6fSyx 		}
1931d6c23f6fSyx 	} else {
1932d6c23f6fSyx 		/*
1933d6c23f6fSyx 		 * If nl_inip is non null, this is a lookup based on the real
1934d6c23f6fSyx 		 * ip address. Else, we use the fake.
1935d6c23f6fSyx 		 */
1936d6c23f6fSyx 		if ((nat = nat6_outlookup(&fi, np->nl_flags, fi.fin_p,
1937d6c23f6fSyx 					  &np->nl_inip6, &np->nl_outip6))) {
1938d6c23f6fSyx 			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
1939d6c23f6fSyx 				fr_info_t fin;
1940d6c23f6fSyx 				bzero((char *)&fin, sizeof (fin));
1941d6c23f6fSyx 				fin.fin_p = nat->nat_p;
1942d6c23f6fSyx 				fin.fin_data[0] = ntohs(nat->nat_outport);
1943d6c23f6fSyx 				fin.fin_data[1] = ntohs(nat->nat_oport);
1944d6c23f6fSyx 				fin.fin_ifs = ifs;
1945d6c23f6fSyx 				if (nat6_inlookup(&fin, np->nl_flags, fin.fin_p,
1946d6c23f6fSyx 						 &nat->nat_outip6.in6,
1947d6c23f6fSyx 						 &nat->nat_oip6.in6) != NULL) {
1948d6c23f6fSyx 					np->nl_flags &= ~IPN_FINDFORWARD;
1949d6c23f6fSyx 				}
1950d6c23f6fSyx 			}
1951d6c23f6fSyx 
1952d6c23f6fSyx 			np->nl_realip6 = nat->nat_outip6.in6;
1953d6c23f6fSyx 			np->nl_realport = nat->nat_outport;
1954d6c23f6fSyx 		}
1955d6c23f6fSyx  	}
1956d6c23f6fSyx 
1957d6c23f6fSyx 	return nat;
1958d6c23f6fSyx }
1959d6c23f6fSyx 
1960d6c23f6fSyx 
1961d6c23f6fSyx /* ------------------------------------------------------------------------ */
1962d6c23f6fSyx /* Function:    nat6_match                                                  */
1963d6c23f6fSyx /* Returns:     int - 0 == no match, 1 == match                             */
1964d6c23f6fSyx /* Parameters:  fin(I)   - pointer to packet information                    */
1965d6c23f6fSyx /*              np(I)    - pointer to NAT rule                              */
1966d6c23f6fSyx /*                                                                          */
1967d6c23f6fSyx /* Pull the matching of a packet against a NAT rule out of that complex     */
1968d6c23f6fSyx /* loop inside fr_checknat6in() and lay it out properly in its own function.*/
1969d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat6_match(fin,np)1970d6c23f6fSyx static int nat6_match(fin, np)
1971d6c23f6fSyx fr_info_t *fin;
1972d6c23f6fSyx ipnat_t *np;
1973d6c23f6fSyx {
1974d6c23f6fSyx 	frtuc_t *ft;
1975d6c23f6fSyx 
1976d6c23f6fSyx 	if (fin->fin_v != 6)
1977d6c23f6fSyx 		return 0;
1978d6c23f6fSyx 
1979d6c23f6fSyx 	if (np->in_p && fin->fin_p != np->in_p)
1980d6c23f6fSyx 		return 0;
1981d6c23f6fSyx 
1982d6c23f6fSyx 	if (fin->fin_out) {
1983d6c23f6fSyx 		if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK)))
1984d6c23f6fSyx 			return 0;
1985d6c23f6fSyx 		if (IP6_MASKNEQ(&fin->fin_src6, &np->in_in[1], &np->in_in[0])
1986d6c23f6fSyx 		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
1987d6c23f6fSyx 			return 0;
1988d6c23f6fSyx 		if (IP6_MASKNEQ(&fin->fin_dst6, &np->in_src[1], &np->in_src[0])
1989d6c23f6fSyx 		    ^ ((np->in_flags & IPN_NOTDST) != 0))
1990d6c23f6fSyx 			return 0;
1991d6c23f6fSyx 	} else {
1992d6c23f6fSyx 		if (!(np->in_redir & NAT_REDIRECT))
1993d6c23f6fSyx 			return 0;
1994d6c23f6fSyx 		if (IP6_MASKNEQ(&fin->fin_src6, &np->in_src[1], &np->in_src[0])
1995d6c23f6fSyx 		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
1996d6c23f6fSyx 			return 0;
1997d6c23f6fSyx 		if (IP6_MASKNEQ(&fin->fin_dst6, &np->in_out[1], &np->in_out[0])
1998d6c23f6fSyx 		    ^ ((np->in_flags & IPN_NOTDST) != 0))
1999d6c23f6fSyx 			return 0;
2000d6c23f6fSyx 	}
2001d6c23f6fSyx 
2002d6c23f6fSyx 	ft = &np->in_tuc;
2003d6c23f6fSyx 	if (!(fin->fin_flx & FI_TCPUDP) ||
2004d6c23f6fSyx 	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
2005d6c23f6fSyx 		if (ft->ftu_scmp || ft->ftu_dcmp)
2006d6c23f6fSyx 			return 0;
2007d6c23f6fSyx 		return 1;
2008d6c23f6fSyx 	}
2009d6c23f6fSyx 
2010d6c23f6fSyx 	return fr_tcpudpchk(fin, ft);
2011d6c23f6fSyx }
2012d6c23f6fSyx 
2013d6c23f6fSyx 
2014d6c23f6fSyx /* ------------------------------------------------------------------------ */
2015d6c23f6fSyx /* Function:    fr_checknat6out                                             */
2016d6c23f6fSyx /* Returns:     int - -1 == packet failed NAT checks so block it,           */
2017d6c23f6fSyx /*                     0 == no packet translation occurred,                 */
2018d6c23f6fSyx /*                     1 == packet was successfully translated.             */
2019d6c23f6fSyx /* Parameters:  fin(I)   - pointer to packet information                    */
2020d6c23f6fSyx /*              passp(I) - pointer to filtering result flags                */
2021d6c23f6fSyx /*                                                                          */
2022d6c23f6fSyx /* Check to see if an outcoming packet should be changed.  ICMP packets are */
2023d6c23f6fSyx /* first checked to see if they match an existing entry (if an error),      */
2024d6c23f6fSyx /* otherwise a search of the current NAT table is made.  If neither results */
2025d6c23f6fSyx /* in a match then a search for a matching NAT rule is made.  Create a new  */
2026d6c23f6fSyx /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
2027d6c23f6fSyx /* packet header(s) as required.                                            */
2028d6c23f6fSyx /* ------------------------------------------------------------------------ */
fr_checknat6out(fin,passp)2029d6c23f6fSyx int fr_checknat6out(fin, passp)
2030d6c23f6fSyx fr_info_t *fin;
2031d6c23f6fSyx u_32_t *passp;
2032d6c23f6fSyx {
2033d6c23f6fSyx 	struct ifnet *ifp, *sifp;
2034d6c23f6fSyx 	int rval, natfailed;
2035d6c23f6fSyx 	ipnat_t *np = NULL;
2036d6c23f6fSyx 	u_int nflags = 0;
2037d6c23f6fSyx 	i6addr_t ipa, iph;
2038d6c23f6fSyx 	int natadd = 1;
2039d6c23f6fSyx 	frentry_t *fr;
2040d6c23f6fSyx 	nat_t *nat;
2041d6c23f6fSyx 	ipf_stack_t *ifs = fin->fin_ifs;
2042d6c23f6fSyx 
2043d6c23f6fSyx 	if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0)
2044d6c23f6fSyx 		return 0;
2045d6c23f6fSyx 
2046d6c23f6fSyx 	natfailed = 0;
2047d6c23f6fSyx 	fr = fin->fin_fr;
2048d6c23f6fSyx 	sifp = fin->fin_ifp;
2049d6c23f6fSyx 	if ((fr != NULL) && !(fr->fr_flags & FR_DUP) &&
2050d6c23f6fSyx 	    fr->fr_tifs[fin->fin_rev].fd_ifp &&
2051d6c23f6fSyx 	    fr->fr_tifs[fin->fin_rev].fd_ifp != (void *)-1)
2052d6c23f6fSyx 		fin->fin_ifp = fr->fr_tifs[fin->fin_rev].fd_ifp;
2053d6c23f6fSyx 	ifp = fin->fin_ifp;
2054d6c23f6fSyx 
2055d6c23f6fSyx 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
2056d6c23f6fSyx 		switch (fin->fin_p)
2057d6c23f6fSyx 		{
2058d6c23f6fSyx 		case IPPROTO_TCP :
2059d6c23f6fSyx 			nflags = IPN_TCP;
2060d6c23f6fSyx 			break;
2061d6c23f6fSyx 		case IPPROTO_UDP :
2062d6c23f6fSyx 			nflags = IPN_UDP;
2063d6c23f6fSyx 			break;
2064d6c23f6fSyx 		case IPPROTO_ICMPV6 :
2065d6c23f6fSyx 			/*
2066d6c23f6fSyx 			 * This is an incoming packet, so the destination is
2067d6c23f6fSyx 			 * the icmp6_id and the source port equals 0
2068d6c23f6fSyx 			 */
2069d6c23f6fSyx 			if ((fin->fin_flx & FI_ICMPQUERY) != 0)
2070d6c23f6fSyx 				nflags = IPN_ICMPQUERY;
2071d6c23f6fSyx 			break;
2072d6c23f6fSyx 		default :
2073d6c23f6fSyx 			break;
2074d6c23f6fSyx 		}
2075d6c23f6fSyx 
2076d6c23f6fSyx #ifdef	IPF_V6_PROXIES
2077d6c23f6fSyx 		if ((nflags & IPN_TCPUDP))
2078d6c23f6fSyx 			tcp = fin->fin_dp;
2079d6c23f6fSyx #endif
2080d6c23f6fSyx 	}
2081d6c23f6fSyx 
2082d6c23f6fSyx 	ipa = fin->fin_src6;
2083d6c23f6fSyx 
2084d6c23f6fSyx 	READ_ENTER(&ifs->ifs_ipf_nat);
2085d6c23f6fSyx 
2086d6c23f6fSyx 	if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) &&
2087d6c23f6fSyx 	    (nat = nat6_icmperror(fin, &nflags, NAT_OUTBOUND)))
2088d6c23f6fSyx 		/*EMPTY*/;
2089d6c23f6fSyx 	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
2090d6c23f6fSyx 		natadd = 0;
2091d6c23f6fSyx 	else if ((nat = nat6_outlookup(fin, nflags|NAT_SEARCH,
2092d6c23f6fSyx 		    (u_int)fin->fin_p, &fin->fin_src6.in6,
2093d6c23f6fSyx 		    &fin->fin_dst6.in6))) {
2094d6c23f6fSyx 		nflags = nat->nat_flags;
2095d6c23f6fSyx 	} else {
2096d6c23f6fSyx 		u_32_t hv, nmsk;
2097d6c23f6fSyx 		i6addr_t msk;
2098d6c23f6fSyx 		int i;
2099d6c23f6fSyx 
2100d6c23f6fSyx 		/*
2101d6c23f6fSyx 		 * If there is no current entry in the nat table for this IP#,
2102d6c23f6fSyx 		 * create one for it (if there is a matching rule).
2103d6c23f6fSyx 		 */
2104d6c23f6fSyx 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
2105d6c23f6fSyx 		i = 3;
2106d6c23f6fSyx 		msk.i6[0] = 0xffffffff;
2107d6c23f6fSyx 		msk.i6[1] = 0xffffffff;
2108d6c23f6fSyx 		msk.i6[2] = 0xffffffff;
2109d6c23f6fSyx 		msk.i6[3] = 0xffffffff;
2110d6c23f6fSyx 		nmsk = ifs->ifs_nat6_masks[3];
2111d6c23f6fSyx 		WRITE_ENTER(&ifs->ifs_ipf_nat);
2112d6c23f6fSyx maskloop:
2113d6c23f6fSyx 		IP6_AND(&ipa, &msk, &iph);
2114d6c23f6fSyx 		hv = NAT_HASH_FN6(&iph, 0, ifs->ifs_ipf_natrules_sz);
2115d6c23f6fSyx 		for (np = ifs->ifs_nat_rules[hv]; np; np = np->in_mnext)
2116d6c23f6fSyx 		{
2117d6c23f6fSyx 			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
2118d6c23f6fSyx 				continue;
2119d6c23f6fSyx 			if (np->in_v != 6)
2120d6c23f6fSyx 				continue;
2121d6c23f6fSyx 			if (np->in_p && (np->in_p != fin->fin_p))
2122d6c23f6fSyx 				continue;
2123d6c23f6fSyx 			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
2124d6c23f6fSyx 				continue;
2125d6c23f6fSyx 			if (np->in_flags & IPN_FILTER) {
2126d6c23f6fSyx 				if (!nat6_match(fin, np))
2127d6c23f6fSyx 					continue;
2128d6c23f6fSyx 			} else if (!IP6_MASKEQ(&ipa, &np->in_in[1],
2129d6c23f6fSyx 			    &np->in_in[0]))
2130d6c23f6fSyx 				continue;
2131d6c23f6fSyx 
2132d6c23f6fSyx 			if ((fr != NULL) &&
2133d6c23f6fSyx 			    !fr_matchtag(&np->in_tag, &fr->fr_nattag))
2134d6c23f6fSyx 				continue;
2135d6c23f6fSyx 
2136d6c23f6fSyx #ifdef	IPF_V6_PROXIES
2137d6c23f6fSyx 			if (*np->in_plabel != '\0') {
2138d6c23f6fSyx 				if (((np->in_flags & IPN_FILTER) == 0) &&
2139d6c23f6fSyx 				    (np->in_dport != tcp->th_dport))
2140d6c23f6fSyx 					continue;
2141d6c23f6fSyx 				if (appr_ok(fin, tcp, np) == 0)
2142d6c23f6fSyx 					continue;
2143d6c23f6fSyx 			}
2144d6c23f6fSyx #endif
2145d6c23f6fSyx 
2146d6c23f6fSyx 			if (nat = nat6_new(fin, np, NULL, nflags,
2147d6c23f6fSyx 					   NAT_OUTBOUND)) {
2148d6c23f6fSyx 				np->in_hits++;
2149d6c23f6fSyx 				break;
2150d6c23f6fSyx 			} else
2151d6c23f6fSyx 				natfailed = -1;
2152d6c23f6fSyx 		}
2153d6c23f6fSyx 		if ((np == NULL) && (i >= 0)) {
2154d6c23f6fSyx 			while (i >= 0) {
2155d6c23f6fSyx 				while (nmsk) {
2156d6c23f6fSyx 					msk.i6[i] = htonl(ntohl(msk.i6[i])<<1);
2157d6c23f6fSyx 					if ((nmsk & 0x80000000) != 0) {
2158d6c23f6fSyx 						nmsk <<= 1;
2159d6c23f6fSyx 						goto maskloop;
2160d6c23f6fSyx 					}
2161d6c23f6fSyx 					nmsk <<= 1;
2162d6c23f6fSyx 				}
2163d6c23f6fSyx 				msk.i6[i--] = 0;
2164d6c23f6fSyx 				if (i >= 0) {
2165d6c23f6fSyx 					nmsk = ifs->ifs_nat6_masks[i];
2166d6c23f6fSyx 					if (nmsk != 0)
2167d6c23f6fSyx 						goto maskloop;
2168d6c23f6fSyx 				}
2169d6c23f6fSyx 			}
2170d6c23f6fSyx 		}
2171d6c23f6fSyx 		MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
2172d6c23f6fSyx 	}
2173d6c23f6fSyx 
2174d6c23f6fSyx 	if (nat != NULL) {
2175d6c23f6fSyx 		rval = fr_nat6out(fin, nat, natadd, nflags);
217633f2fefdSDarren Reed 	} else {
2177d6c23f6fSyx 		rval = natfailed;
217833f2fefdSDarren Reed 	}
2179d6c23f6fSyx 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
2180d6c23f6fSyx 
2181d6c23f6fSyx 	if (rval == -1) {
2182d6c23f6fSyx 		if (passp != NULL)
2183d6c23f6fSyx 			*passp = FR_BLOCK;
2184d6c23f6fSyx 		fin->fin_flx |= FI_BADNAT;
2185d6c23f6fSyx 	}
2186d6c23f6fSyx 	fin->fin_ifp = sifp;
2187d6c23f6fSyx 	return rval;
2188d6c23f6fSyx }
2189d6c23f6fSyx 
2190d6c23f6fSyx /* ------------------------------------------------------------------------ */
2191d6c23f6fSyx /* Function:    fr_nat6out                                                  */
2192d6c23f6fSyx /* Returns:     int - -1 == packet failed NAT checks so block it,           */
2193d6c23f6fSyx /*                     1 == packet was successfully translated.             */
2194d6c23f6fSyx /* Parameters:  fin(I)    - pointer to packet information                   */
2195d6c23f6fSyx /*              nat(I)    - pointer to NAT structure                        */
2196d6c23f6fSyx /*              natadd(I) - flag indicating if it is safe to add frag cache */
2197d6c23f6fSyx /*              nflags(I) - NAT flags set for this packet                   */
2198d6c23f6fSyx /*                                                                          */
2199d6c23f6fSyx /* Translate a packet coming "out" on an interface.                         */
2200d6c23f6fSyx /* ------------------------------------------------------------------------ */
fr_nat6out(fin,nat,natadd,nflags)2201d6c23f6fSyx int fr_nat6out(fin, nat, natadd, nflags)
2202d6c23f6fSyx fr_info_t *fin;
2203d6c23f6fSyx nat_t *nat;
2204d6c23f6fSyx int natadd;
2205d6c23f6fSyx u_32_t nflags;
2206d6c23f6fSyx {
2207d6c23f6fSyx 	struct icmp6_hdr *icmp6;
2208d6c23f6fSyx 	u_short *csump;
2209d6c23f6fSyx 	tcphdr_t *tcp;
2210d6c23f6fSyx 	ipnat_t *np;
2211d6c23f6fSyx 	int i;
2212d6c23f6fSyx 	ipf_stack_t *ifs = fin->fin_ifs;
2213d6c23f6fSyx 
2214af5f29ddSToomas Soome #if defined(SOLARIS) && defined(_KERNEL)
22157ddc9b1aSDarren Reed 	net_handle_t net_data_p = ifs->ifs_ipf_ipv6;
2216d6c23f6fSyx #endif
2217d6c23f6fSyx 
2218d6c23f6fSyx 	tcp = NULL;
2219d6c23f6fSyx 	icmp6 = NULL;
2220d6c23f6fSyx 	csump = NULL;
2221d6c23f6fSyx 	np = nat->nat_ptr;
2222d6c23f6fSyx 
2223d6c23f6fSyx 	if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
2224d6c23f6fSyx 		(void) fr_nat_newfrag(fin, 0, nat);
2225d6c23f6fSyx 
2226d6c23f6fSyx 	MUTEX_ENTER(&nat->nat_lock);
2227d6c23f6fSyx 	nat->nat_bytes[1] += fin->fin_plen;
2228d6c23f6fSyx 	nat->nat_pkts[1]++;
2229d6c23f6fSyx 	MUTEX_EXIT(&nat->nat_lock);
2230*55fea89dSDan Cross 
2231d6c23f6fSyx 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
2232d6c23f6fSyx 		if ((nat->nat_outport != 0) && (nflags & IPN_TCPUDP)) {
2233d6c23f6fSyx 			tcp = fin->fin_dp;
2234d6c23f6fSyx 
2235d6c23f6fSyx 			tcp->th_sport = nat->nat_outport;
2236d6c23f6fSyx 			fin->fin_data[0] = ntohs(nat->nat_outport);
2237d6c23f6fSyx 		}
2238d6c23f6fSyx 
2239d6c23f6fSyx 		if ((nat->nat_outport != 0) && (nflags & IPN_ICMPQUERY)) {
2240d6c23f6fSyx 			icmp6 = fin->fin_dp;
2241d6c23f6fSyx 			icmp6->icmp6_id = nat->nat_outport;
2242d6c23f6fSyx 		}
2243d6c23f6fSyx 
2244d6c23f6fSyx 		csump = nat_proto(fin, nat, nflags);
2245d6c23f6fSyx 	}
2246d6c23f6fSyx 
2247d6c23f6fSyx 	fin->fin_ip6->ip6_src = nat->nat_outip6.in6;
2248d6c23f6fSyx 	fin->fin_src6 = nat->nat_outip6;
2249d6c23f6fSyx 
2250d6c23f6fSyx 	nat_update(fin, nat, np);
2251d6c23f6fSyx 
2252d6c23f6fSyx 	/*
2253d6c23f6fSyx 	 * TCP/UDP/ICMPv6 checksum needs to be adjusted.
2254d6c23f6fSyx 	 */
2255d6c23f6fSyx 	if (csump != NULL && (!(nflags & IPN_TCPUDP) ||
2256d6c23f6fSyx 	    !NET_IS_HCK_L4_FULL(net_data_p, fin->fin_m))) {
2257d6c23f6fSyx 		if (nflags & IPN_TCPUDP &&
2258d6c23f6fSyx 	   	    NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) {
2259d6c23f6fSyx 			if (nat->nat_dir == NAT_OUTBOUND)
2260d6c23f6fSyx 				fix_outcksum(csump, nat->nat_sumd[1]);
2261d6c23f6fSyx 			else
2262d6c23f6fSyx 				fix_incksum(csump, nat->nat_sumd[1]);
2263d6c23f6fSyx 		} else {
2264d6c23f6fSyx 			if (nat->nat_dir == NAT_OUTBOUND)
2265d6c23f6fSyx 				fix_outcksum(csump, nat->nat_sumd[0]);
2266d6c23f6fSyx 			else
2267d6c23f6fSyx 				fix_incksum(csump, nat->nat_sumd[0]);
2268d6c23f6fSyx 		}
2269d6c23f6fSyx 	}
2270d6c23f6fSyx #ifdef	IPFILTER_SYNC
2271d6c23f6fSyx 	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
2272d6c23f6fSyx #endif
2273d6c23f6fSyx 	/* ------------------------------------------------------------- */
2274d6c23f6fSyx 	/* A few quick notes:						 */
2275d6c23f6fSyx 	/*	Following are test conditions prior to calling the 	 */
2276d6c23f6fSyx 	/*	appr_check routine.					 */
2277d6c23f6fSyx 	/*								 */
2278d6c23f6fSyx 	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
2279d6c23f6fSyx 	/*	with a redirect rule, we attempt to match the packet's	 */
2280d6c23f6fSyx 	/*	source port against in_dport, otherwise	we'd compare the */
2281d6c23f6fSyx 	/*	packet's destination.			 		 */
2282d6c23f6fSyx 	/* ------------------------------------------------------------- */
2283d6c23f6fSyx 	if ((np != NULL) && (np->in_apr != NULL)) {
2284d6c23f6fSyx 		i = appr_check(fin, nat);
2285d6c23f6fSyx 		if (i == 0)
2286d6c23f6fSyx 			i = 1;
2287d6c23f6fSyx 	} else
2288d6c23f6fSyx 		i = 1;
2289d6c23f6fSyx 	ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[1]);
2290d6c23f6fSyx 	fin->fin_flx |= FI_NATED;
2291d6c23f6fSyx 	return i;
2292d6c23f6fSyx }
2293d6c23f6fSyx 
2294d6c23f6fSyx 
2295d6c23f6fSyx /* ------------------------------------------------------------------------ */
2296d6c23f6fSyx /* Function:    fr_checknat6in                                              */
2297d6c23f6fSyx /* Returns:     int - -1 == packet failed NAT checks so block it,           */
2298d6c23f6fSyx /*                     0 == no packet translation occurred,                 */
2299d6c23f6fSyx /*                     1 == packet was successfully translated.             */
2300d6c23f6fSyx /* Parameters:  fin(I)   - pointer to packet information                    */
2301d6c23f6fSyx /*              passp(I) - pointer to filtering result flags                */
2302d6c23f6fSyx /*                                                                          */
2303d6c23f6fSyx /* Check to see if an incoming packet should be changed.  ICMP packets are  */
2304d6c23f6fSyx /* first checked to see if they match an existing entry (if an error),      */
2305d6c23f6fSyx /* otherwise a search of the current NAT table is made.  If neither results */
2306d6c23f6fSyx /* in a match then a search for a matching NAT rule is made.  Create a new  */
2307d6c23f6fSyx /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
2308d6c23f6fSyx /* packet header(s) as required.                                            */
2309d6c23f6fSyx /* ------------------------------------------------------------------------ */
fr_checknat6in(fin,passp)2310d6c23f6fSyx int fr_checknat6in(fin, passp)
2311d6c23f6fSyx fr_info_t *fin;
2312d6c23f6fSyx u_32_t *passp;
2313d6c23f6fSyx {
2314d6c23f6fSyx 	u_int nflags, natadd;
2315d6c23f6fSyx 	int rval, natfailed;
2316d6c23f6fSyx 	struct ifnet *ifp;
2317d6c23f6fSyx 	struct icmp6_hdr *icmp6;
2318d6c23f6fSyx 	tcphdr_t *tcp;
2319d6c23f6fSyx 	u_short dport;
2320d6c23f6fSyx 	ipnat_t *np;
2321d6c23f6fSyx 	nat_t *nat;
2322d6c23f6fSyx 	i6addr_t ipa, iph;
2323d6c23f6fSyx 	ipf_stack_t *ifs = fin->fin_ifs;
2324d6c23f6fSyx 
2325d6c23f6fSyx 	if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0)
2326d6c23f6fSyx 		return 0;
2327d6c23f6fSyx 
2328d6c23f6fSyx 	tcp = NULL;
2329d6c23f6fSyx 	icmp6 = NULL;
2330d6c23f6fSyx 	dport = 0;
2331d6c23f6fSyx 	natadd = 1;
2332d6c23f6fSyx 	nflags = 0;
2333d6c23f6fSyx 	natfailed = 0;
2334d6c23f6fSyx 	ifp = fin->fin_ifp;
2335d6c23f6fSyx 
2336d6c23f6fSyx 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
2337d6c23f6fSyx 		switch (fin->fin_p)
2338d6c23f6fSyx 		{
2339d6c23f6fSyx 		case IPPROTO_TCP :
2340d6c23f6fSyx 			nflags = IPN_TCP;
2341d6c23f6fSyx 			break;
2342d6c23f6fSyx 		case IPPROTO_UDP :
2343d6c23f6fSyx 			nflags = IPN_UDP;
2344d6c23f6fSyx 			break;
2345d6c23f6fSyx 		case IPPROTO_ICMPV6 :
2346d6c23f6fSyx 			icmp6 = fin->fin_dp;
2347d6c23f6fSyx 
2348d6c23f6fSyx 			/*
2349d6c23f6fSyx 			 * This is an incoming packet, so the destination is
2350d6c23f6fSyx 			 * the icmp_id and the source port equals 0
2351d6c23f6fSyx 			 */
2352d6c23f6fSyx 			if ((fin->fin_flx & FI_ICMPQUERY) != 0) {
2353d6c23f6fSyx 				nflags = IPN_ICMPQUERY;
2354*55fea89dSDan Cross 				dport = icmp6->icmp6_id;
2355d6c23f6fSyx 			} break;
2356d6c23f6fSyx 		default :
2357d6c23f6fSyx 			break;
2358d6c23f6fSyx 		}
2359*55fea89dSDan Cross 
2360d6c23f6fSyx 		if ((nflags & IPN_TCPUDP)) {
2361d6c23f6fSyx 			tcp = fin->fin_dp;
2362d6c23f6fSyx 			dport = tcp->th_dport;
2363d6c23f6fSyx 		}
2364d6c23f6fSyx 	}
2365d6c23f6fSyx 
2366d6c23f6fSyx 	ipa = fin->fin_dst6;
2367d6c23f6fSyx 
2368d6c23f6fSyx 	READ_ENTER(&ifs->ifs_ipf_nat);
2369d6c23f6fSyx 
2370d6c23f6fSyx 	if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) &&
2371d6c23f6fSyx 	    (nat = nat6_icmperror(fin, &nflags, NAT_INBOUND)))
2372d6c23f6fSyx 		/*EMPTY*/;
2373d6c23f6fSyx 	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
2374d6c23f6fSyx 		natadd = 0;
2375d6c23f6fSyx 	else if ((nat = nat6_inlookup(fin, nflags|NAT_SEARCH, (u_int)fin->fin_p,
2376d6c23f6fSyx 	    &fin->fin_src6.in6, &ipa.in6))) {
2377d6c23f6fSyx 		nflags = nat->nat_flags;
2378d6c23f6fSyx 	} else {
2379d6c23f6fSyx 		u_32_t hv, rmsk;
2380d6c23f6fSyx 		i6addr_t msk;
2381d6c23f6fSyx 		int i;
2382d6c23f6fSyx 
2383d6c23f6fSyx 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
2384d6c23f6fSyx 		i = 3;
2385d6c23f6fSyx 		msk.i6[0] = 0xffffffff;
2386d6c23f6fSyx 		msk.i6[1] = 0xffffffff;
2387d6c23f6fSyx 		msk.i6[2] = 0xffffffff;
2388d6c23f6fSyx 		msk.i6[3] = 0xffffffff;
2389d6c23f6fSyx 		rmsk = ifs->ifs_rdr6_masks[3];
2390d6c23f6fSyx 		WRITE_ENTER(&ifs->ifs_ipf_nat);
2391d6c23f6fSyx 		/*
2392d6c23f6fSyx 		 * If there is no current entry in the nat table for this IP#,
2393d6c23f6fSyx 		 * create one for it (if there is a matching rule).
2394d6c23f6fSyx 		 */
2395d6c23f6fSyx maskloop:
2396d6c23f6fSyx 		IP6_AND(&ipa, &msk, &iph);
2397d6c23f6fSyx 		hv = NAT_HASH_FN6(&iph, 0, ifs->ifs_ipf_rdrrules_sz);
2398d6c23f6fSyx 		for (np = ifs->ifs_rdr_rules[hv]; np; np = np->in_rnext) {
2399d6c23f6fSyx 			if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
2400d6c23f6fSyx 				continue;
2401d6c23f6fSyx 			if (np->in_v != fin->fin_v)
2402d6c23f6fSyx 				continue;
2403d6c23f6fSyx 			if (np->in_p && (np->in_p != fin->fin_p))
2404d6c23f6fSyx 				continue;
2405d6c23f6fSyx 			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
2406d6c23f6fSyx 				continue;
2407d6c23f6fSyx 			if (np->in_flags & IPN_FILTER) {
2408d6c23f6fSyx 				if (!nat6_match(fin, np))
2409d6c23f6fSyx 					continue;
2410d6c23f6fSyx 			} else {
2411d6c23f6fSyx 				if (!IP6_MASKEQ(&ipa, &np->in_out[1],
2412d6c23f6fSyx 				    &np->in_out[0]))
2413d6c23f6fSyx 					continue;
2414d6c23f6fSyx 				if (np->in_pmin &&
2415d6c23f6fSyx 				    ((ntohs(np->in_pmax) < ntohs(dport)) ||
2416d6c23f6fSyx 				     (ntohs(dport) < ntohs(np->in_pmin))))
2417d6c23f6fSyx 					continue;
2418d6c23f6fSyx 			}
2419d6c23f6fSyx 
2420d6c23f6fSyx #ifdef	IPF_V6_PROXIES
2421d6c23f6fSyx 			if (*np->in_plabel != '\0') {
2422d6c23f6fSyx 				if (!appr_ok(fin, tcp, np)) {
2423d6c23f6fSyx 					continue;
2424d6c23f6fSyx 				}
2425d6c23f6fSyx 			}
2426d6c23f6fSyx #endif
2427d6c23f6fSyx 
2428d6c23f6fSyx 			nat = nat6_new(fin, np, NULL, nflags, NAT_INBOUND);
2429d6c23f6fSyx 			if (nat != NULL) {
2430d6c23f6fSyx 				np->in_hits++;
2431d6c23f6fSyx 				break;
2432d6c23f6fSyx 			} else
2433d6c23f6fSyx 				natfailed = -1;
2434d6c23f6fSyx 		}
2435d6c23f6fSyx 
2436d6c23f6fSyx 		if ((np == NULL) && (i >= 0)) {
2437d6c23f6fSyx 			while (i >= 0) {
2438d6c23f6fSyx 				while (rmsk) {
2439d6c23f6fSyx 					msk.i6[i] = htonl(ntohl(msk.i6[i])<<1);
2440d6c23f6fSyx 					if ((rmsk & 0x80000000) != 0) {
2441d6c23f6fSyx 						rmsk <<= 1;
2442d6c23f6fSyx 						goto maskloop;
2443d6c23f6fSyx 					}
2444d6c23f6fSyx 					rmsk <<= 1;
2445d6c23f6fSyx 				}
2446d6c23f6fSyx 				msk.i6[i--] = 0;
2447d6c23f6fSyx 				if (i >= 0) {
2448d6c23f6fSyx 					rmsk = ifs->ifs_rdr6_masks[i];
2449d6c23f6fSyx 					if (rmsk != 0)
2450d6c23f6fSyx 						goto maskloop;
2451d6c23f6fSyx 				}
2452d6c23f6fSyx 			}
2453d6c23f6fSyx 		}
2454d6c23f6fSyx 		MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
2455d6c23f6fSyx 	}
2456d6c23f6fSyx 	if (nat != NULL) {
2457d6c23f6fSyx 		rval = fr_nat6in(fin, nat, natadd, nflags);
245833f2fefdSDarren Reed 	} else {
2459d6c23f6fSyx 		rval = natfailed;
246033f2fefdSDarren Reed 	}
2461d6c23f6fSyx 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
2462d6c23f6fSyx 
2463d6c23f6fSyx 	if (rval == -1) {
2464d6c23f6fSyx 		if (passp != NULL)
2465d6c23f6fSyx 			*passp = FR_BLOCK;
2466d6c23f6fSyx 		fin->fin_flx |= FI_BADNAT;
2467d6c23f6fSyx 	}
2468d6c23f6fSyx 	return rval;
2469d6c23f6fSyx }
2470d6c23f6fSyx 
2471d6c23f6fSyx 
2472d6c23f6fSyx /* ------------------------------------------------------------------------ */
2473d6c23f6fSyx /* Function:    fr_nat6in                                                   */
2474d6c23f6fSyx /* Returns:     int - -1 == packet failed NAT checks so block it,           */
2475d6c23f6fSyx /*                     1 == packet was successfully translated.             */
2476d6c23f6fSyx /* Parameters:  fin(I)    - pointer to packet information                   */
2477d6c23f6fSyx /*              nat(I)    - pointer to NAT structure                        */
2478d6c23f6fSyx /*              natadd(I) - flag indicating if it is safe to add frag cache */
2479d6c23f6fSyx /*              nflags(I) - NAT flags set for this packet                   */
2480d6c23f6fSyx /* Locks Held:  ipf_nat (READ)                                              */
2481d6c23f6fSyx /*                                                                          */
2482d6c23f6fSyx /* Translate a packet coming "in" on an interface.                          */
2483d6c23f6fSyx /* ------------------------------------------------------------------------ */
fr_nat6in(fin,nat,natadd,nflags)2484d6c23f6fSyx int fr_nat6in(fin, nat, natadd, nflags)
2485d6c23f6fSyx fr_info_t *fin;
2486d6c23f6fSyx nat_t *nat;
2487d6c23f6fSyx int natadd;
2488d6c23f6fSyx u_32_t nflags;
2489d6c23f6fSyx {
2490d6c23f6fSyx 	struct icmp6_hdr *icmp6;
2491d6c23f6fSyx 	u_short *csump;
2492d6c23f6fSyx 	tcphdr_t *tcp;
2493d6c23f6fSyx 	ipnat_t *np;
2494d6c23f6fSyx 	ipf_stack_t *ifs = fin->fin_ifs;
2495d6c23f6fSyx 
2496af5f29ddSToomas Soome #if defined(SOLARIS) && defined(_KERNEL)
24977ddc9b1aSDarren Reed 	net_handle_t net_data_p = ifs->ifs_ipf_ipv6;
2498d6c23f6fSyx #endif
2499d6c23f6fSyx 
2500d6c23f6fSyx 	tcp = NULL;
2501d6c23f6fSyx 	csump = NULL;
2502d6c23f6fSyx 	np = nat->nat_ptr;
2503d6c23f6fSyx 	fin->fin_fr = nat->nat_fr;
2504d6c23f6fSyx 
2505d6c23f6fSyx 	if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
2506d6c23f6fSyx 		(void) fr_nat_newfrag(fin, 0, nat);
2507d6c23f6fSyx 
2508d6c23f6fSyx #ifdef	IPF_V6_PROXIES
2509d6c23f6fSyx 	if (np != NULL) {
2510d6c23f6fSyx 
2511d6c23f6fSyx 	/* ------------------------------------------------------------- */
2512d6c23f6fSyx 	/* A few quick notes:						 */
2513d6c23f6fSyx 	/*	Following are test conditions prior to calling the 	 */
2514d6c23f6fSyx 	/*	appr_check routine.					 */
2515d6c23f6fSyx 	/*								 */
2516d6c23f6fSyx 	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
2517d6c23f6fSyx 	/*	with a map rule, we attempt to match the packet's	 */
2518d6c23f6fSyx 	/*	source port against in_dport, otherwise	we'd compare the */
2519d6c23f6fSyx 	/*	packet's destination.			 		 */
2520d6c23f6fSyx 	/* ------------------------------------------------------------- */
2521d6c23f6fSyx 		if (np->in_apr != NULL) {
2522d6c23f6fSyx 			i = appr_check(fin, nat);
2523d6c23f6fSyx 			if (i == -1) {
2524d6c23f6fSyx 				return -1;
2525d6c23f6fSyx 			}
2526d6c23f6fSyx 		}
2527d6c23f6fSyx 	}
2528d6c23f6fSyx #endif
2529d6c23f6fSyx 
2530d6c23f6fSyx #ifdef	IPFILTER_SYNC
2531d6c23f6fSyx 	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
2532d6c23f6fSyx #endif
2533d6c23f6fSyx 
2534d6c23f6fSyx 	MUTEX_ENTER(&nat->nat_lock);
2535d6c23f6fSyx 	nat->nat_bytes[0] += fin->fin_plen;
2536d6c23f6fSyx 	nat->nat_pkts[0]++;
2537d6c23f6fSyx 	MUTEX_EXIT(&nat->nat_lock);
2538d6c23f6fSyx 
2539d6c23f6fSyx 	fin->fin_ip6->ip6_dst = nat->nat_inip6.in6;
2540d6c23f6fSyx 	fin->fin_dst6 = nat->nat_inip6;
2541d6c23f6fSyx 
2542d6c23f6fSyx 	if (nflags & IPN_TCPUDP)
2543d6c23f6fSyx 		tcp = fin->fin_dp;
2544d6c23f6fSyx 
2545d6c23f6fSyx 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
2546d6c23f6fSyx 		if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) {
2547d6c23f6fSyx 			tcp->th_dport = nat->nat_inport;
2548d6c23f6fSyx 			fin->fin_data[1] = ntohs(nat->nat_inport);
2549d6c23f6fSyx 		}
2550d6c23f6fSyx 
2551d6c23f6fSyx 
2552d6c23f6fSyx 		if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) {
2553d6c23f6fSyx 			icmp6 = fin->fin_dp;
2554d6c23f6fSyx 
2555d6c23f6fSyx 			icmp6->icmp6_id = nat->nat_inport;
2556d6c23f6fSyx 		}
2557d6c23f6fSyx 
2558d6c23f6fSyx 		csump = nat_proto(fin, nat, nflags);
2559d6c23f6fSyx 	}
2560d6c23f6fSyx 
2561d6c23f6fSyx 	nat_update(fin, nat, np);
2562d6c23f6fSyx 
2563d6c23f6fSyx 	/*
2564d6c23f6fSyx 	 * In case they are being forwarded, inbound packets always need to have
2565d6c23f6fSyx 	 * their checksum adjusted even if hardware checksum validation said OK.
2566d6c23f6fSyx 	 */
2567d6c23f6fSyx 	if (csump != NULL) {
2568d6c23f6fSyx 		if (nat->nat_dir == NAT_OUTBOUND)
2569d6c23f6fSyx 			fix_incksum(csump, nat->nat_sumd[0]);
2570d6c23f6fSyx 		else
2571d6c23f6fSyx 			fix_outcksum(csump, nat->nat_sumd[0]);
2572d6c23f6fSyx 	}
2573d6c23f6fSyx 
2574af5f29ddSToomas Soome #if defined(SOLARIS) && defined(_KERNEL)
2575d6c23f6fSyx 	if (nflags & IPN_TCPUDP &&
2576d6c23f6fSyx 	    NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) {
2577d6c23f6fSyx 		/*
2578d6c23f6fSyx 		 * Need to adjust the partial checksum result stored in
2579d6c23f6fSyx 		 * db_cksum16, which will be used for validation in IP.
2580d6c23f6fSyx 		 * See IP_CKSUM_RECV().
2581d6c23f6fSyx 		 * Adjustment data should be the inverse of the IP address
2582d6c23f6fSyx 		 * changes, because db_cksum16 is supposed to be the complement
2583d6c23f6fSyx 		 * of the pesudo header.
2584d6c23f6fSyx 		 */
2585d6c23f6fSyx 		csump = &fin->fin_m->b_datap->db_cksum16;
2586d6c23f6fSyx 		if (nat->nat_dir == NAT_OUTBOUND)
2587d6c23f6fSyx 			fix_outcksum(csump, nat->nat_sumd[1]);
2588d6c23f6fSyx 		else
2589d6c23f6fSyx 			fix_incksum(csump, nat->nat_sumd[1]);
2590d6c23f6fSyx 	}
2591d6c23f6fSyx #endif
2592d6c23f6fSyx 
2593d6c23f6fSyx 	ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[0]);
2594d6c23f6fSyx 	fin->fin_flx |= FI_NATED;
2595d6c23f6fSyx 	if (np != NULL && np->in_tag.ipt_num[0] != 0)
2596d6c23f6fSyx 		fin->fin_nattag = &np->in_tag;
2597d6c23f6fSyx 	return 1;
2598d6c23f6fSyx }
2599d6c23f6fSyx 
2600d6c23f6fSyx 
2601d6c23f6fSyx /* ------------------------------------------------------------------------ */
2602d6c23f6fSyx /* Function:    nat_icmpquerytype6                                          */
2603d6c23f6fSyx /* Returns:     int - 1 == success, 0 == failure                            */
2604d6c23f6fSyx /* Parameters:  icmptype(I) - ICMP type number                              */
2605d6c23f6fSyx /*                                                                          */
2606d6c23f6fSyx /* Tests to see if the ICMP type number passed is a query/response type or  */
2607d6c23f6fSyx /* not.                                                                     */
2608d6c23f6fSyx /* ------------------------------------------------------------------------ */
nat_icmpquerytype6(icmptype)2609d6c23f6fSyx static INLINE int nat_icmpquerytype6(icmptype)
2610d6c23f6fSyx int icmptype;
2611d6c23f6fSyx {
2612d6c23f6fSyx 
2613d6c23f6fSyx 	/*
2614d6c23f6fSyx 	 * For the ICMP query NAT code, it is essential that both the query
2615d6c23f6fSyx 	 * and the reply match on the NAT rule. Because the NAT structure
2616d6c23f6fSyx 	 * does not keep track of the icmptype, and a single NAT structure
2617d6c23f6fSyx 	 * is used for all icmp types with the same src, dest and id, we
2618d6c23f6fSyx 	 * simply define the replies as queries as well. The funny thing is,
2619d6c23f6fSyx 	 * altough it seems silly to call a reply a query, this is exactly
2620d6c23f6fSyx 	 * as it is defined in the IPv4 specification
2621d6c23f6fSyx 	 */
2622d6c23f6fSyx 
2623d6c23f6fSyx 	switch (icmptype)
2624d6c23f6fSyx 	{
2625d6c23f6fSyx 
2626d6c23f6fSyx 	case ICMP6_ECHO_REPLY:
2627d6c23f6fSyx 	case ICMP6_ECHO_REQUEST:
2628d6c23f6fSyx 	/* route aedvertisement/solliciation is currently unsupported: */
2629d6c23f6fSyx 	/* it would require rewriting the ICMP data section            */
2630d6c23f6fSyx 	case ICMP6_MEMBERSHIP_QUERY:
2631d6c23f6fSyx 	case ICMP6_MEMBERSHIP_REPORT:
2632d6c23f6fSyx 	case ICMP6_MEMBERSHIP_REDUCTION:
2633d6c23f6fSyx 	case ICMP6_WRUREQUEST:
2634d6c23f6fSyx 	case ICMP6_WRUREPLY:
2635d6c23f6fSyx 	case MLD6_MTRACE_RESP:
2636d6c23f6fSyx 	case MLD6_MTRACE:
2637d6c23f6fSyx 		return 1;
2638d6c23f6fSyx 	default:
2639d6c23f6fSyx 		return 0;
2640d6c23f6fSyx 	}
2641d6c23f6fSyx }
2642