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