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