xref: /illumos-gate/usr/src/uts/common/inet/ipf/ip_nat6.c (revision b9ccdc5a)
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 			port = htons(np->in_pnext++);
442 
443 			if (np->in_pnext > ntohs(np->in_pmax)) {
444 				np->in_pnext = ntohs(np->in_pmin);
445 				if (!IP6_ISONES(&np->in_out[1])) {
446 					IP6_INC(&np->in_next6);
447 				}
448 			}
449 		}
450 
451 		if (np->in_flags & IPN_IPRANGE) {
452 			if (IP6_GT(&np->in_next6, &np->in_out[1]))
453 				np->in_next6 = np->in_out[0];
454 		} else {
455 			i6addr_t a1, a2;
456 
457 			a1 = np->in_next6;
458 			IP6_INC(&a1);
459 			IP6_AND(&a1, &np->in_out[1], &a2);
460 			if (!IP6_ISONES(&np->in_out[1]) &&
461 			    IP6_GT(&a2, &np->in_out[0])) {
462 				IP6_ADD(&np->in_out[0], 1, &np->in_next6);
463 			}
464 		}
465 
466 		if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
467 			port = sport;
468 
469 		/*
470 		 * Here we do a lookup of the connection as seen from
471 		 * the outside.  If an IP# pair already exists, try
472 		 * again.  So if you have A->B becomes C->B, you can
473 		 * also have D->E become C->E but not D->B causing
474 		 * another C->B.  Also take protocol and ports into
475 		 * account when determining whether a pre-existing
476 		 * NAT setup will cause an external conflict where
477 		 * this is appropriate.
478 		 */
479 		sp = fin->fin_data[0];
480 		dp = fin->fin_data[1];
481 		fin->fin_data[0] = fin->fin_data[1];
482 		fin->fin_data[1] = htons(port);
483 		natl = nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
484 		    (u_int)fin->fin_p, &fin->fin_dst6.in6, &in.in6);
485 		fin->fin_data[0] = sp;
486 		fin->fin_data[1] = dp;
487 
488 		/*
489 		 * Has the search wrapped around and come back to the
490 		 * start ?
491 		 */
492 		if ((natl != NULL) &&
493 		    (np->in_pnext != 0) && (st_port == np->in_pnext) &&
494 		    !IP6_ISZERO(&np->in_next6) &&
495 		    IP6_EQ(&st_ip, &np->in_next6))
496 			return -1;
497 		l++;
498 	} while (natl != NULL);
499 
500 	if (np->in_space > 0)
501 		np->in_space--;
502 
503 	/* Setup the NAT table */
504 	nat->nat_inip6 = fin->fin_src6;
505 	nat->nat_outip6 = in;
506 	nat->nat_oip6 = fin->fin_dst6;
507 	if (nat->nat_hm == NULL)
508 		nat->nat_hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6,
509 		    &nat->nat_outip6, 0, ifs);
510 
511 	if (flags & IPN_TCPUDP) {
512 		nat->nat_inport = sport;
513 		nat->nat_outport = port;	/* sport */
514 		nat->nat_oport = dport;
515 		((tcphdr_t *)fin->fin_dp)->th_sport = port;
516 	} else if (flags & IPN_ICMPQUERY) {
517 		((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port;
518 		nat->nat_inport = port;
519 		nat->nat_outport = port;
520 	}
521 
522 	ni->nai_port = port;
523 	ni->nai_nport = dport;
524 	return 0;
525 }
526 
527 
528 /* ------------------------------------------------------------------------ */
529 /* Function:    nat6_newrdr                                                 */
530 /* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
531 /*                    allow rule to be moved if IPN_ROUNDR is set.          */
532 /* Parameters:  fin(I) - pointer to packet information                      */
533 /*              nat(I) - pointer to NAT entry                               */
534 /*              ni(I)  - pointer to structure with misc. information needed */
535 /*                       to create new NAT entry.                           */
536 /*                                                                          */
537 /* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
538 /* to the new IP address for the translation.                               */
539 /* ------------------------------------------------------------------------ */
540 static INLINE int nat6_newrdr(fin, nat, ni)
541 fr_info_t *fin;
542 nat_t *nat;
543 natinfo_t *ni;
544 {
545 	u_short nport, dport, sport;
546 	i6addr_t in;
547 	u_short sp, dp;
548 	hostmap_t *hm;
549 	u_32_t flags;
550 	ipnat_t *np;
551 	nat_t *natl;
552 	int move;
553 	ipf_stack_t *ifs = fin->fin_ifs;
554 
555 	move = 1;
556 	hm = NULL;
557 	in.i6[0] = 0;
558 	in.i6[1] = 0;
559 	in.i6[2] = 0;
560 	in.i6[3] = 0;
561 	np = ni->nai_np;
562 	flags = ni->nai_flags;
563 	sport = ni->nai_sport;
564 	dport = ni->nai_dport;
565 
566 	/*
567 	 * If the matching rule has IPN_STICKY set, then we want to have the
568 	 * same rule kick in as before.  Why would this happen?  If you have
569 	 * a collection of rdr rules with "round-robin sticky", the current
570 	 * packet might match a different one to the previous connection but
571 	 * we want the same destination to be used.
572 	 */
573 	if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) ==
574 	    (IPN_ROUNDR|IPN_STICKY)) {
575 		hm = nat6_hostmap(NULL, &fin->fin_src6, &fin->fin_dst6, &in,
576 		    (u_32_t)dport, ifs);
577 		if (hm != NULL) {
578 			in = hm->hm_map;
579 			np = hm->hm_ipnat;
580 			ni->nai_np = np;
581 			move = 0;
582 		}
583 	}
584 
585 	/*
586 	 * Otherwise, it's an inbound packet. Most likely, we don't
587 	 * want to rewrite source ports and source addresses. Instead,
588 	 * we want to rewrite to a fixed internal address and fixed
589 	 * internal port.
590 	 */
591 	if (np->in_flags & IPN_SPLIT) {
592 		in = np->in_next6;
593 
594 		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
595 			hm = nat6_hostmap(np, &fin->fin_src6, &fin->fin_dst6,
596 			    &in, (u_32_t)dport, ifs);
597 			if (hm != NULL) {
598 				in = hm->hm_map;
599 				move = 0;
600 			}
601 		}
602 
603 		if (hm == NULL || hm->hm_ref == 1) {
604 			if (IP6_EQ(&np->in_in[0], &in)) {
605 				np->in_next6 = np->in_in[1];
606 				move = 0;
607 			} else {
608 				np->in_next6 = np->in_in[0];
609 			}
610 		}
611 
612 	} else if (IP6_ISZERO(&np->in_in[0]) &&
613 	    IP6_ISONES(&np->in_in[1])) {
614 		/*
615 		 * 0/128 - use the interface's IP address.
616 		 */
617 		if (fr_ifpaddr(6, FRI_NORMAL, fin->fin_ifp, (void *)&in, NULL,
618 			   fin->fin_ifs) == -1)
619 			return -1;
620 
621 	} else if (IP6_ISZERO(&np->in_in[0]) &&
622 	    IP6_ISZERO(&np->in_in[1])) {
623 		/*
624 		 * 0/0 - use the original destination address/port.
625 		 */
626 		in = fin->fin_dst6;
627 
628 	} else if (np->in_redir == NAT_BIMAP &&
629 	    IP6_EQ(&np->in_in[1], &np->in_out[1])) {
630 		i6addr_t temp;
631 		/*
632 		 * map the address block in a 1:1 fashion
633 		 */
634 		temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_in[1].i6[0];
635 		temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_in[1].i6[1];
636 		temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_in[1].i6[2];
637 		temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_in[1].i6[3];
638 		in = np->in_in[0];
639 		IP6_MERGE(&in, &temp, &np->in_in[1]);
640 	} else {
641 		in = np->in_in[0];
642 	}
643 
644 	if ((np->in_pnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
645 		nport = dport;
646 	else {
647 		/*
648 		 * Whilst not optimized for the case where
649 		 * pmin == pmax, the gain is not significant.
650 		 */
651 		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
652 		    (np->in_pmin != np->in_pmax)) {
653 			nport = ntohs(dport) - ntohs(np->in_pmin) +
654 				ntohs(np->in_pnext);
655 			nport = htons(nport);
656 		} else
657 			nport = np->in_pnext;
658 	}
659 
660 	/*
661 	 * When the redirect-to address is set to 0.0.0.0, just
662 	 * assume a blank `forwarding' of the packet.  We don't
663 	 * setup any translation for this either.
664 	 */
665 	if (IP6_ISZERO(&in)) {
666 		if (nport == dport)
667 			return -1;
668 		in = fin->fin_dst6;
669 	}
670 
671 	/*
672 	 * Check to see if this redirect mapping already exists and if
673 	 * it does, return "failure" (allowing it to be created will just
674 	 * cause one or both of these "connections" to stop working.)
675 	 */
676 	sp = fin->fin_data[0];
677 	dp = fin->fin_data[1];
678 	fin->fin_data[1] = fin->fin_data[0];
679 	fin->fin_data[0] = ntohs(nport);
680 	natl = nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
681 	    (u_int)fin->fin_p, &in.in6, &fin->fin_src6.in6);
682 	fin->fin_data[0] = sp;
683 	fin->fin_data[1] = dp;
684 	if (natl != NULL)
685 		return -1;
686 
687 	nat->nat_inip6 = in;
688 	nat->nat_outip6 = fin->fin_dst6;
689 	nat->nat_oip6 = fin->fin_src6;
690 	if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
691 		nat->nat_hm = nat6_hostmap(np, &fin->fin_src6,
692 		    &fin->fin_dst6, &in, (u_32_t)dport, ifs);
693 
694 	ni->nai_nport = nport;
695 	ni->nai_port = sport;
696 
697 	if (flags & IPN_TCPUDP) {
698 		nat->nat_inport = nport;
699 		nat->nat_outport = dport;
700 		nat->nat_oport = sport;
701 		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
702 	} else if (flags & IPN_ICMPQUERY) {
703 		((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport;
704 		nat->nat_inport = nport;
705 		nat->nat_outport = nport;
706 	}
707 
708 	return move;
709 }
710 
711 /* ------------------------------------------------------------------------ */
712 /* Function:    nat6_new                                                    */
713 /* Returns:     nat_t* - NULL == failure to create new NAT structure,       */
714 /*                       else pointer to new NAT structure                  */
715 /* Parameters:  fin(I)       - pointer to packet information                */
716 /*              np(I)        - pointer to NAT rule                          */
717 /*              natsave(I)   - pointer to where to store NAT struct pointer */
718 /*              flags(I)     - flags describing the current packet          */
719 /*              direction(I) - direction of packet (in/out)                 */
720 /* Write Lock:  ipf_nat                                                     */
721 /*                                                                          */
722 /* Attempts to create a new NAT entry.  Does not actually change the packet */
723 /* in any way.                                                              */
724 /*                                                                          */
725 /* This fucntion is in three main parts: (1) deal with creating a new NAT   */
726 /* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
727 /* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
728 /* and (3) building that structure and putting it into the NAT table(s).    */
729 /* ------------------------------------------------------------------------ */
730 nat_t *nat6_new(fin, np, natsave, flags, direction)
731 fr_info_t *fin;
732 ipnat_t *np;
733 nat_t **natsave;
734 u_int flags;
735 int direction;
736 {
737 	tcphdr_t *tcp = NULL;
738 	hostmap_t *hm = NULL;
739 	nat_t *nat, *natl;
740 	u_int nflags;
741 	natinfo_t ni;
742 	int move;
743 	ipf_stack_t *ifs = fin->fin_ifs;
744 
745 	/*
746 	 * Trigger automatic call to nat_extraflush() if the
747 	 * table has reached capcity specified by hi watermark.
748 	 */
749 	if (NAT_TAB_WATER_LEVEL(ifs) > ifs->ifs_nat_flush_lvl_hi)
750 		ifs->ifs_nat_doflush = 1;
751 
752 	if (ifs->ifs_nat_stats.ns_inuse >= ifs->ifs_ipf_nattable_max) {
753 		ifs->ifs_nat_stats.ns_memfail++;
754 		return NULL;
755 	}
756 
757 	move = 1;
758 	nflags = np->in_flags & flags;
759 	nflags &= NAT_FROMRULE;
760 
761 	ni.nai_np = np;
762 	ni.nai_nflags = nflags;
763 	ni.nai_flags = flags;
764 
765 	/* Give me a new nat */
766 	KMALLOC(nat, nat_t *);
767 	if (nat == NULL) {
768 		ifs->ifs_nat_stats.ns_memfail++;
769 		/*
770 		 * Try to automatically tune the max # of entries in the
771 		 * table allowed to be less than what will cause kmem_alloc()
772 		 * to fail and try to eliminate panics due to out of memory
773 		 * conditions arising.
774 		 */
775 		if (ifs->ifs_ipf_nattable_max > ifs->ifs_ipf_nattable_sz) {
776 			ifs->ifs_ipf_nattable_max =
777 			    ifs->ifs_nat_stats.ns_inuse - 100;
778 			printf("ipf_nattable_max reduced to %d\n",
779 			    ifs->ifs_ipf_nattable_max);
780 		}
781 		return NULL;
782 	}
783 
784 	if (flags & IPN_TCPUDP) {
785 		tcp = fin->fin_dp;
786 		ni.nai_sport = htons(fin->fin_sport);
787 		ni.nai_dport = htons(fin->fin_dport);
788 	} else if (flags & IPN_ICMPQUERY) {
789 		/*
790 		 * In the ICMP query NAT code, we translate the ICMP id fields
791 		 * to make them unique. This is indepedent of the ICMP type
792 		 * (e.g. in the unlikely event that a host sends an echo and
793 		 * an tstamp request with the same id, both packets will have
794 		 * their ip address/id field changed in the same way).
795 		 *
796 		 * The icmp_id field is used by the sender to identify the
797 		 * process making the icmp request. (the receiver justs
798 		 * copies it back in its response). So, it closely matches
799 		 * the concept of source port. We overlay sport, so we can
800 		 * maximally reuse the existing code.
801 		 */
802 		ni.nai_sport = ((struct icmp6_hdr *)fin->fin_dp)->icmp6_id;
803 		ni.nai_dport = ni.nai_sport;
804 	}
805 
806 	bzero((char *)nat, sizeof (*nat));
807 	nat->nat_flags = flags;
808 	nat->nat_redir = np->in_redir;
809 
810 	if ((flags & NAT_SLAVE) == 0) {
811 		MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
812 	}
813 
814 	/*
815 	 * Search the current table for a match.
816 	 */
817 	if (direction == NAT_OUTBOUND) {
818 		/*
819 		 * We can now arrange to call this for the same connection
820 		 * because ipf_nat_new doesn't protect the code path into
821 		 * this function.
822 		 */
823 		natl = nat6_outlookup(fin, nflags, (u_int)fin->fin_p,
824 		    &fin->fin_src6.in6, &fin->fin_dst6.in6);
825 		if (natl != NULL) {
826 			KFREE(nat);
827 			nat = natl;
828 			goto done;
829 		}
830 
831 		move = nat6_newmap(fin, nat, &ni);
832 		if (move == -1)
833 			goto badnat;
834 
835 		np = ni.nai_np;
836 	} else {
837 		/*
838 		 * NAT_INBOUND is used only for redirects rules
839 		 */
840 		natl = nat6_inlookup(fin, nflags, (u_int)fin->fin_p,
841 		    &fin->fin_src6.in6, &fin->fin_dst6.in6);
842 		if (natl != NULL) {
843 			KFREE(nat);
844 			nat = natl;
845 			goto done;
846 		}
847 
848 		move = nat6_newrdr(fin, nat, &ni);
849 		if (move == -1)
850 			goto badnat;
851 
852 		np = ni.nai_np;
853 	}
854 
855 	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
856 		if (np->in_redir == NAT_REDIRECT) {
857 			nat_delrdr(np);
858 			nat6_addrdr(np, ifs);
859 		} else if (np->in_redir == NAT_MAP) {
860 			nat_delnat(np);
861 			nat6_addnat(np, ifs);
862 		}
863 	}
864 
865 	if (nat6_finalise(fin, nat, &ni, tcp, natsave, direction) == -1) {
866 		goto badnat;
867 	}
868 
869 	nat_calc_chksum_diffs(nat);
870 
871 	if (flags & SI_WILDP)
872 		ifs->ifs_nat_stats.ns_wilds++;
873 	goto done;
874 badnat:
875 	ifs->ifs_nat_stats.ns_badnat++;
876 	if ((hm = nat->nat_hm) != NULL)
877 		fr_hostmapdel(&hm);
878 	KFREE(nat);
879 	nat = NULL;
880 done:
881 	if ((flags & NAT_SLAVE) == 0) {
882 		MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
883 	}
884 	return nat;
885 }
886 
887 
888 /* ------------------------------------------------------------------------ */
889 /* Function:    nat6_finalise                                               */
890 /* Returns:     int - 0 == sucess, -1 == failure                            */
891 /* Parameters:  fin(I) - pointer to packet information                      */
892 /*              nat(I) - pointer to NAT entry                               */
893 /*              ni(I)  - pointer to structure with misc. information needed */
894 /*                       to create new NAT entry.                           */
895 /* Write Lock:  ipf_nat                                                     */
896 /*                                                                          */
897 /* This is the tail end of constructing a new NAT entry and is the same     */
898 /* for both IPv4 and IPv6.                                                  */
899 /* ------------------------------------------------------------------------ */
900 /*ARGSUSED*/
901 static INLINE int nat6_finalise(fin, nat, ni, tcp, natsave, direction)
902 fr_info_t *fin;
903 nat_t *nat;
904 natinfo_t *ni;
905 tcphdr_t *tcp;
906 nat_t **natsave;
907 int direction;
908 {
909 	frentry_t *fr;
910 	ipnat_t *np;
911 	ipf_stack_t *ifs = fin->fin_ifs;
912 
913 	np = ni->nai_np;
914 
915 	COPYIFNAME(fin->fin_ifp, nat->nat_ifnames[0], fin->fin_v);
916 
917 #ifdef	IPFILTER_SYNC
918 	if ((nat->nat_flags & SI_CLONE) == 0)
919 		nat->nat_sync = ipfsync_new(SMC_NAT, fin, nat);
920 #endif
921 
922 	nat->nat_me = natsave;
923 	nat->nat_dir = direction;
924 	nat->nat_ifps[0] = np->in_ifps[0];
925 	nat->nat_ifps[1] = np->in_ifps[1];
926 	nat->nat_ptr = np;
927 	nat->nat_p = fin->fin_p;
928 	nat->nat_v = fin->fin_v;
929 	nat->nat_mssclamp = np->in_mssclamp;
930 	fr = fin->fin_fr;
931 	nat->nat_fr = fr;
932 	nat->nat_v = 6;
933 
934 #ifdef	IPF_V6_PROXIES
935 	if ((np->in_apr != NULL) && ((ni->nai_flags & NAT_SLAVE) == 0))
936 		if (appr_new(fin, nat) == -1)
937 			return -1;
938 #endif
939 
940 	if (nat6_insert(nat, fin->fin_rev, ifs) == 0) {
941 		if (ifs->ifs_nat_logging)
942 			nat_log(nat, (u_int)np->in_redir, ifs);
943 		np->in_use++;
944 		if (fr != NULL) {
945 			MUTEX_ENTER(&fr->fr_lock);
946 			fr->fr_ref++;
947 			MUTEX_EXIT(&fr->fr_lock);
948 		}
949 		return 0;
950 	}
951 
952 	/*
953 	 * nat6_insert failed, so cleanup time...
954 	 */
955 	return -1;
956 }
957 
958 
959 /* ------------------------------------------------------------------------ */
960 /* Function:   nat6_insert                                                  */
961 /* Returns:    int - 0 == sucess, -1 == failure                             */
962 /* Parameters: nat(I) - pointer to NAT structure                            */
963 /*             rev(I) - flag indicating forward/reverse direction of packet */
964 /* Write Lock: ipf_nat                                                      */
965 /*                                                                          */
966 /* Insert a NAT entry into the hash tables for searching and add it to the  */
967 /* list of active NAT entries.  Adjust global counters when complete.       */
968 /* ------------------------------------------------------------------------ */
969 int nat6_insert(nat, rev, ifs)
970 nat_t	*nat;
971 int	rev;
972 ipf_stack_t *ifs;
973 {
974 	u_int hv1, hv2;
975 	nat_t **natp;
976 
977 	/*
978 	 * Try and return an error as early as possible, so calculate the hash
979 	 * entry numbers first and then proceed.
980 	 */
981 	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
982 		hv1 = NAT_HASH_FN6(&nat->nat_inip6, nat->nat_inport,
983 				  0xffffffff);
984 		hv1 = NAT_HASH_FN6(&nat->nat_oip6, hv1 + nat->nat_oport,
985 				  ifs->ifs_ipf_nattable_sz);
986 		hv2 = NAT_HASH_FN6(&nat->nat_outip6, nat->nat_outport,
987 				  0xffffffff);
988 		hv2 = NAT_HASH_FN6(&nat->nat_oip6, hv2 + nat->nat_oport,
989 				  ifs->ifs_ipf_nattable_sz);
990 	} else {
991 		hv1 = NAT_HASH_FN6(&nat->nat_inip6, 0, 0xffffffff);
992 		hv1 = NAT_HASH_FN6(&nat->nat_oip6, hv1,
993 				  ifs->ifs_ipf_nattable_sz);
994 		hv2 = NAT_HASH_FN6(&nat->nat_outip6, 0, 0xffffffff);
995 		hv2 = NAT_HASH_FN6(&nat->nat_oip6, hv2,
996 				  ifs->ifs_ipf_nattable_sz);
997 	}
998 
999 	if ((ifs->ifs_nat_stats.ns_bucketlen[0][hv1] >=
1000 	    ifs->ifs_fr_nat_maxbucket) ||
1001 	    (ifs->ifs_nat_stats.ns_bucketlen[1][hv2] >=
1002 	    ifs->ifs_fr_nat_maxbucket)) {
1003 		return -1;
1004 	}
1005 
1006 	nat->nat_hv[0] = hv1;
1007 	nat->nat_hv[1] = hv2;
1008 
1009 	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
1010 
1011 	nat->nat_rev = rev;
1012 	nat->nat_ref = 1;
1013 	nat->nat_bytes[0] = 0;
1014 	nat->nat_pkts[0] = 0;
1015 	nat->nat_bytes[1] = 0;
1016 	nat->nat_pkts[1] = 0;
1017 
1018 	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
1019 	nat->nat_ifps[0] = fr_resolvenic(nat->nat_ifnames[0], 6, ifs);
1020 
1021 	if (nat->nat_ifnames[1][0] !='\0') {
1022 		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
1023 		nat->nat_ifps[1] = fr_resolvenic(nat->nat_ifnames[1], 6, ifs);
1024 	} else {
1025 		(void) strncpy(nat->nat_ifnames[1], nat->nat_ifnames[0],
1026 			       LIFNAMSIZ);
1027 		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
1028 		nat->nat_ifps[1] = nat->nat_ifps[0];
1029 	}
1030 
1031 	nat->nat_next = ifs->ifs_nat_instances;
1032 	nat->nat_pnext = &ifs->ifs_nat_instances;
1033 	if (ifs->ifs_nat_instances)
1034 		ifs->ifs_nat_instances->nat_pnext = &nat->nat_next;
1035 	ifs->ifs_nat_instances = nat;
1036 
1037 	natp = &ifs->ifs_nat_table[0][hv1];
1038 	if (*natp)
1039 		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
1040 	nat->nat_phnext[0] = natp;
1041 	nat->nat_hnext[0] = *natp;
1042 	*natp = nat;
1043 	ifs->ifs_nat_stats.ns_bucketlen[0][hv1]++;
1044 
1045 	natp = &ifs->ifs_nat_table[1][hv2];
1046 	if (*natp)
1047 		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
1048 	nat->nat_phnext[1] = natp;
1049 	nat->nat_hnext[1] = *natp;
1050 	*natp = nat;
1051 	ifs->ifs_nat_stats.ns_bucketlen[1][hv2]++;
1052 
1053 	fr_setnatqueue(nat, rev, ifs);
1054 
1055 	ifs->ifs_nat_stats.ns_added++;
1056 	ifs->ifs_nat_stats.ns_inuse++;
1057 	return 0;
1058 }
1059 
1060 
1061 /* ------------------------------------------------------------------------ */
1062 /* Function:    nat6_icmperrorlookup                                        */
1063 /* Returns:     nat_t* - point to matching NAT structure                    */
1064 /* Parameters:  fin(I) - pointer to packet information                      */
1065 /*              dir(I) - direction of packet (in/out)                       */
1066 /*                                                                          */
1067 /* Check if the ICMP error message is related to an existing TCP, UDP or    */
1068 /* ICMP query nat entry.  It is assumed that the packet is already of the   */
1069 /* the required length.                                                     */
1070 /* ------------------------------------------------------------------------ */
1071 nat_t *nat6_icmperrorlookup(fin, dir)
1072 fr_info_t *fin;
1073 int dir;
1074 {
1075 	int flags = 0, minlen;
1076 	struct icmp6_hdr *orgicmp;
1077 	tcphdr_t *tcp = NULL;
1078 	u_short data[2];
1079 	nat_t *nat;
1080 	ip6_t *oip6;
1081 	u_int p;
1082 
1083 	minlen = 40;
1084 	/*
1085 	 * Does it at least have the return (basic) IP header ?
1086 	 * Only a basic IP header (no options) should be with an ICMP error
1087 	 * header.  Also, if it's not an error type, then return.
1088 	 */
1089 	if (!(fin->fin_flx & FI_ICMPERR))
1090 		return NULL;
1091 
1092 	/*
1093 	 * Check packet size
1094 	 */
1095 	if (fin->fin_plen < ICMP6ERR_IPICMPHLEN)
1096 		return NULL;
1097 	oip6 = (ip6_t *)((char *)fin->fin_dp + 8);
1098 
1099 	/*
1100 	 * Is the buffer big enough for all of it ?  It's the size of the IP
1101 	 * header claimed in the encapsulated part which is of concern.  It
1102 	 * may be too big to be in this buffer but not so big that it's
1103 	 * outside the ICMP packet, leading to TCP deref's causing problems.
1104 	 * This is possible because we don't know how big oip_hl is when we
1105 	 * do the pullup early in fr_check() and thus can't gaurantee it is
1106 	 * all here now.
1107 	 */
1108 #ifdef  _KERNEL
1109 	{
1110 	mb_t *m;
1111 
1112 	m = fin->fin_m;
1113 # if defined(MENTAT)
1114 	if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN > (char *)m->b_wptr)
1115 		return NULL;
1116 # else
1117 	if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN >
1118 	    (char *)fin->fin_ip + M_LEN(m))
1119 		return NULL;
1120 # endif
1121 	}
1122 #endif
1123 
1124 	if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src))
1125 		return NULL;
1126 
1127 	p = oip6->ip6_nxt;
1128 	if (p == IPPROTO_TCP)
1129 		flags = IPN_TCP;
1130 	else if (p == IPPROTO_UDP)
1131 		flags = IPN_UDP;
1132 	else if (p == IPPROTO_ICMPV6) {
1133 		orgicmp = (struct icmp6_hdr *)(oip6 + 1);
1134 
1135 		/* see if this is related to an ICMP query */
1136 		if (nat_icmpquerytype6(orgicmp->icmp6_type)) {
1137 			data[0] = fin->fin_data[0];
1138 			data[1] = fin->fin_data[1];
1139 			fin->fin_data[0] = 0;
1140 			fin->fin_data[1] = orgicmp->icmp6_id;
1141 
1142 			flags = IPN_ICMPERR|IPN_ICMPQUERY;
1143 			/*
1144 			 * NOTE : dir refers to the direction of the original
1145 			 *        ip packet. By definition the icmp error
1146 			 *        message flows in the opposite direction.
1147 			 */
1148 			if (dir == NAT_INBOUND)
1149 				nat = nat6_inlookup(fin, flags, p,
1150 				    &oip6->ip6_dst, &oip6->ip6_src);
1151 			else
1152 				nat = nat6_outlookup(fin, flags, p,
1153 				    &oip6->ip6_dst, &oip6->ip6_src);
1154 			fin->fin_data[0] = data[0];
1155 			fin->fin_data[1] = data[1];
1156 			return nat;
1157 		}
1158 	}
1159 
1160 	if (flags & IPN_TCPUDP) {
1161 		minlen += 8;		/* + 64bits of data to get ports */
1162 		if (fin->fin_plen < ICMPERR_ICMPHLEN + minlen)
1163 			return NULL;
1164 
1165 		data[0] = fin->fin_data[0];
1166 		data[1] = fin->fin_data[1];
1167 		tcp = (tcphdr_t *)(oip6 + 1);
1168 		fin->fin_data[0] = ntohs(tcp->th_dport);
1169 		fin->fin_data[1] = ntohs(tcp->th_sport);
1170 
1171 		if (dir == NAT_INBOUND) {
1172 			nat = nat6_inlookup(fin, flags, p,
1173 			    &oip6->ip6_dst, &oip6->ip6_src);
1174 		} else {
1175 			nat = nat6_outlookup(fin, flags, p,
1176 			    &oip6->ip6_dst, &oip6->ip6_src);
1177 		}
1178 		fin->fin_data[0] = data[0];
1179 		fin->fin_data[1] = data[1];
1180 		return nat;
1181 	}
1182 	if (dir == NAT_INBOUND)
1183 		return nat6_inlookup(fin, 0, p, &oip6->ip6_dst, &oip6->ip6_src);
1184 	else
1185 		return nat6_outlookup(fin, 0, p, &oip6->ip6_dst,
1186 		    &oip6->ip6_src);
1187 }
1188 
1189 
1190 /* ------------------------------------------------------------------------ */
1191 /* Function:    nat6_icmperror                                              */
1192 /* Returns:     nat_t* - point to matching NAT structure                    */
1193 /* Parameters:  fin(I)    - pointer to packet information                   */
1194 /*              nflags(I) - NAT flags for this packet                       */
1195 /*              dir(I)    - direction of packet (in/out)                    */
1196 /*                                                                          */
1197 /* Fix up an ICMP packet which is an error message for an existing NAT      */
1198 /* session.  This will correct both packet header data and checksums.       */
1199 /*                                                                          */
1200 /* This should *ONLY* be used for incoming ICMP error packets to make sure  */
1201 /* a NAT'd ICMP packet gets correctly recognised.                           */
1202 /* ------------------------------------------------------------------------ */
1203 nat_t *nat6_icmperror(fin, nflags, dir)
1204 fr_info_t *fin;
1205 u_int *nflags;
1206 int dir;
1207 {
1208 	u_32_t sum1, sum2, sumd, psum1, psum2, psumd, sumd1;
1209 	i6addr_t in;
1210 	struct icmp6_hdr *icmp6, *orgicmp;
1211 	int dlen;
1212 	udphdr_t *udp;
1213 	tcphdr_t *tcp;
1214 	nat_t *nat;
1215 	ip6_t *oip6;
1216 	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY)))
1217 		return NULL;
1218 
1219 	/*
1220 	 * nat6_icmperrorlookup() looks up nat entry associated with the
1221 	 * offending IP packet and returns pointer to the entry, or NULL
1222 	 * if packet wasn't natted or for `defective' packets.
1223 	 */
1224 
1225 	if ((fin->fin_v != 6) || !(nat = nat6_icmperrorlookup(fin, dir)))
1226 		return NULL;
1227 
1228 	sumd1 = 0;
1229 	*nflags = IPN_ICMPERR;
1230 	icmp6 = fin->fin_dp;
1231 	oip6 = (ip6_t *)((char *)icmp6 + sizeof (*icmp6));
1232 	udp = (udphdr_t *)(((char *)oip6) + sizeof (*oip6));
1233 	tcp = (tcphdr_t *)udp;
1234 	dlen = fin->fin_plen - ((char *)udp - (char *)fin->fin_ip);
1235 
1236 	/*
1237 	 * Need to adjust ICMP header to include the real IP#'s and
1238 	 * port #'s.  There are three steps required.
1239 	 *
1240 	 * Step 1
1241 	 * No update needed for ip6 header checksum.
1242 	 *
1243 	 * Unlike IPv4, we need to update icmp_cksum for IPv6 address
1244 	 * changes because there's no ip_sum change to cancel it.
1245 	 */
1246 
1247 	if (IP6_EQ((i6addr_t *)&oip6->ip6_dst, &nat->nat_oip6)) {
1248 		sum1 = LONG_SUM6((i6addr_t *)&oip6->ip6_src);
1249 		in = nat->nat_inip6;
1250 		oip6->ip6_src = in.in6;
1251 	} else {
1252 		sum1 = LONG_SUM6((i6addr_t *)&oip6->ip6_dst);
1253 		in = nat->nat_outip6;
1254 		oip6->ip6_dst = in.in6;
1255 	}
1256 
1257 	sum2 = LONG_SUM6(&in);
1258 	CALC_SUMD(sum1, sum2, sumd);
1259 
1260 	/*
1261 	 * Step 2
1262 	 * Perform other adjustments based on protocol of offending packet.
1263 	 */
1264 
1265 	switch (oip6->ip6_nxt) {
1266 		case IPPROTO_TCP :
1267 		case IPPROTO_UDP :
1268 
1269 			/*
1270 			* For offending TCP/UDP IP packets, translate the ports
1271 			* based on the NAT specification.
1272 			*
1273 			* Advance notice : Now it becomes complicated :-)
1274 			*
1275 			* Since the port and IP addresse fields are both part
1276 			* of the TCP/UDP checksum of the offending IP packet,
1277 			* we need to adjust that checksum as well.
1278 			*
1279 			* To further complicate things, the TCP/UDP checksum
1280 			* may not be present.  We must check to see if the
1281 			* length of the data portion is big enough to hold
1282 			* the checksum.  In the UDP case, a test to determine
1283 			* if the checksum is even set is also required.
1284 			*
1285 			* Any changes to an IP address, port or checksum within
1286 			* the ICMP packet requires a change to icmp_cksum.
1287 			*
1288 			* Be extremely careful here ... The change is dependent
1289 			* upon whether or not the TCP/UPD checksum is present.
1290 			*
1291 			* If TCP/UPD checksum is present, the icmp_cksum must
1292 			* compensate for checksum modification resulting from
1293 			* IP address change only.  Port change and resulting
1294 			* data checksum adjustments cancel each other out.
1295 			*
1296 			* If TCP/UDP checksum is not present, icmp_cksum must
1297 			* compensate for port change only.  The IP address
1298 			* change does not modify anything else in this case.
1299 			*/
1300 
1301 			psum1 = 0;
1302 			psum2 = 0;
1303 			psumd = 0;
1304 
1305 			if ((tcp->th_dport == nat->nat_oport) &&
1306 			    (tcp->th_sport != nat->nat_inport)) {
1307 
1308 				/*
1309 				 * Translate the source port.
1310 				 */
1311 
1312 				psum1 = ntohs(tcp->th_sport);
1313 				psum2 = ntohs(nat->nat_inport);
1314 				tcp->th_sport = nat->nat_inport;
1315 
1316 			} else if ((tcp->th_sport == nat->nat_oport) &&
1317 				    (tcp->th_dport != nat->nat_outport)) {
1318 
1319 				/*
1320 				 * Translate the destination port.
1321 				 */
1322 
1323 				psum1 = ntohs(tcp->th_dport);
1324 				psum2 = ntohs(nat->nat_outport);
1325 				tcp->th_dport = nat->nat_outport;
1326 			}
1327 
1328 			if ((oip6->ip6_nxt == IPPROTO_TCP) && (dlen >= 18)) {
1329 
1330 				/*
1331 				 * TCP checksum present.
1332 				 *
1333 				 * Adjust data checksum and icmp checksum to
1334 				 * compensate for any IP address change.
1335 				 */
1336 
1337 				sum1 = ntohs(tcp->th_sum);
1338 				fix_datacksum(&tcp->th_sum, sumd);
1339 				sum2 = ntohs(tcp->th_sum);
1340 				CALC_SUMD(sum1, sum2, sumd);
1341 				sumd1 += sumd;
1342 
1343 				/*
1344 				 * Also make data checksum adjustment to
1345 				 * compensate for any port change.
1346 				 */
1347 
1348 				if (psum1 != psum2) {
1349 					CALC_SUMD(psum1, psum2, psumd);
1350 					fix_datacksum(&tcp->th_sum, psumd);
1351 				}
1352 
1353 			} else if ((oip6->ip6_nxt == IPPROTO_UDP) &&
1354 				   (dlen >= 8) && (udp->uh_sum != 0)) {
1355 
1356 				/*
1357 				 * The UDP checksum is present and set.
1358 				 *
1359 				 * Adjust data checksum and icmp checksum to
1360 				 * compensate for any IP address change.
1361 				 */
1362 
1363 				sum1 = ntohs(udp->uh_sum);
1364 				fix_datacksum(&udp->uh_sum, sumd);
1365 				sum2 = ntohs(udp->uh_sum);
1366 				CALC_SUMD(sum1, sum2, sumd);
1367 				sumd1 += sumd;
1368 
1369 				/*
1370 				 * Also make data checksum adjustment to
1371 				 * compensate for any port change.
1372 				 */
1373 
1374 				if (psum1 != psum2) {
1375 					CALC_SUMD(psum1, psum2, psumd);
1376 					fix_datacksum(&udp->uh_sum, psumd);
1377 				}
1378 
1379 			} else {
1380 
1381 				/*
1382 				 * Data checksum was not present.
1383 				 *
1384 				 * Compensate for any port change.
1385 				 */
1386 
1387 				CALC_SUMD(psum2, psum1, psumd);
1388 				sumd1 += psumd;
1389 			}
1390 			break;
1391 
1392 		case IPPROTO_ICMPV6 :
1393 
1394 			orgicmp = (struct icmp6_hdr *)udp;
1395 
1396 			if ((nat->nat_dir == NAT_OUTBOUND) &&
1397 			    (orgicmp->icmp6_id != nat->nat_inport) &&
1398 			    (dlen >= 8)) {
1399 
1400 				/*
1401 				 * Fix ICMP checksum (of the offening ICMP
1402 				 * query packet) to compensate the change
1403 				 * in the ICMP id of the offending ICMP
1404 				 * packet.
1405 				 *
1406 				 * Since you modify orgicmp->icmp_id with
1407 				 * a delta (say x) and you compensate that
1408 				 * in origicmp->icmp_cksum with a delta
1409 				 * minus x, you don't have to adjust the
1410 				 * overall icmp->icmp_cksum
1411 				 */
1412 
1413 				sum1 = ntohs(orgicmp->icmp6_id);
1414 				sum2 = ntohs(nat->nat_inport);
1415 				CALC_SUMD(sum1, sum2, sumd);
1416 				orgicmp->icmp6_id = nat->nat_inport;
1417 				fix_datacksum(&orgicmp->icmp6_cksum, sumd);
1418 
1419 			} /* nat_dir can't be NAT_INBOUND for icmp queries */
1420 
1421 			break;
1422 
1423 		default :
1424 
1425 			break;
1426 
1427 	} /* switch (oip6->ip6_nxt) */
1428 
1429 	/*
1430 	 * Step 3
1431 	 * Make the adjustments to icmp checksum.
1432 	 */
1433 
1434 	if (sumd1 != 0) {
1435 		sumd1 = (sumd1 & 0xffff) + (sumd1 >> 16);
1436 		sumd1 = (sumd1 & 0xffff) + (sumd1 >> 16);
1437 		fix_incksum(&icmp6->icmp6_cksum, sumd1);
1438 	}
1439 	return nat;
1440 }
1441 
1442 
1443 /*
1444  * NB: these lookups don't lock access to the list, it assumed that it has
1445  * already been done!
1446  */
1447 
1448 /* ------------------------------------------------------------------------ */
1449 /* Function:    nat6_inlookup                                               */
1450 /* Returns:     nat_t* - NULL == no match,                                  */
1451 /*                       else pointer to matching NAT entry                 */
1452 /* Parameters:  fin(I)    - pointer to packet information                   */
1453 /*              flags(I)  - NAT flags for this packet                       */
1454 /*              p(I)      - protocol for this packet                        */
1455 /*              src(I)    - source IP address                               */
1456 /*              mapdst(I) - destination IP address                          */
1457 /*                                                                          */
1458 /* Lookup a nat entry based on the mapped destination ip address/port and   */
1459 /* real source address/port.  We use this lookup when receiving a packet,   */
1460 /* we're looking for a table entry, based on the destination address.       */
1461 /*                                                                          */
1462 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
1463 /*                                                                          */
1464 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
1465 /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
1466 /*                                                                          */
1467 /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
1468 /*            the packet is of said protocol                                */
1469 /* ------------------------------------------------------------------------ */
1470 nat_t *nat6_inlookup(fin, flags, p, src, mapdst)
1471 fr_info_t *fin;
1472 u_int flags, p;
1473 struct in6_addr *src, *mapdst;
1474 {
1475 	u_short sport, dport;
1476 	u_int sflags;
1477 	nat_t *nat;
1478 	int nflags;
1479 	i6addr_t dst;
1480 	void *ifp;
1481 	u_int hv;
1482 	ipf_stack_t *ifs = fin->fin_ifs;
1483 
1484 	if (fin != NULL)
1485 		ifp = fin->fin_ifp;
1486 	else
1487 		ifp = NULL;
1488 	sport = 0;
1489 	dport = 0;
1490 	dst.in6 = *mapdst;
1491 	sflags = flags & NAT_TCPUDPICMP;
1492 
1493 	switch (p)
1494 	{
1495 	case IPPROTO_TCP :
1496 	case IPPROTO_UDP :
1497 		sport = htons(fin->fin_data[0]);
1498 		dport = htons(fin->fin_data[1]);
1499 		break;
1500 	case IPPROTO_ICMPV6 :
1501 		if (flags & IPN_ICMPERR)
1502 			sport = fin->fin_data[1];
1503 		else
1504 			dport = fin->fin_data[1];
1505 		break;
1506 	default :
1507 		break;
1508 	}
1509 
1510 
1511 	if ((flags & SI_WILDP) != 0)
1512 		goto find_in_wild_ports;
1513 
1514 	hv = NAT_HASH_FN6(&dst, dport, 0xffffffff);
1515 	hv = NAT_HASH_FN6(src, hv + sport, ifs->ifs_ipf_nattable_sz);
1516 	nat = ifs->ifs_nat_table[1][hv];
1517 	for (; nat; nat = nat->nat_hnext[1]) {
1518 		if (nat->nat_v != 6)
1519 			continue;
1520 
1521 		if (nat->nat_ifps[0] != NULL) {
1522 			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
1523 				continue;
1524 		} else if (ifp != NULL)
1525 			nat->nat_ifps[0] = ifp;
1526 
1527 		nflags = nat->nat_flags;
1528 
1529 		if (IP6_EQ(&nat->nat_oip6, src) &&
1530 		    IP6_EQ(&nat->nat_outip6, &dst) &&
1531 		    (((p == 0) &&
1532 		    (sflags == (nat->nat_flags & IPN_TCPUDPICMP))) ||
1533 		    (p == nat->nat_p))) {
1534 			switch (p)
1535 			{
1536 #if 0
1537 			case IPPROTO_GRE :
1538 				if (nat->nat_call[1] != fin->fin_data[0])
1539 					continue;
1540 				break;
1541 #endif
1542 			case IPPROTO_ICMPV6 :
1543 				if ((flags & IPN_ICMPERR) != 0) {
1544 					if (nat->nat_outport != sport)
1545 						continue;
1546 				} else {
1547 					if (nat->nat_outport != dport)
1548 						continue;
1549 				}
1550 				break;
1551 			case IPPROTO_TCP :
1552 			case IPPROTO_UDP :
1553 				if (nat->nat_oport != sport)
1554 					continue;
1555 				if (nat->nat_outport != dport)
1556 					continue;
1557 				break;
1558 			default :
1559 				break;
1560 			}
1561 
1562 #ifdef	IPF_V6_PROXIES
1563 			ipn = nat->nat_ptr;
1564 			if ((ipn != NULL) && (nat->nat_aps != NULL))
1565 				if (appr_match(fin, nat) != 0)
1566 					continue;
1567 #endif
1568 			return nat;
1569 		}
1570 	}
1571 
1572 	/*
1573 	 * So if we didn't find it but there are wildcard members in the hash
1574 	 * table, go back and look for them.  We do this search and update here
1575 	 * because it is modifying the NAT table and we want to do this only
1576 	 * for the first packet that matches.  The exception, of course, is
1577 	 * for "dummy" (FI_IGNORE) lookups.
1578 	 */
1579 find_in_wild_ports:
1580 	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
1581 		return NULL;
1582 	if (ifs->ifs_nat_stats.ns_wilds == 0)
1583 		return NULL;
1584 
1585 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
1586 
1587 	hv = NAT_HASH_FN6(&dst, 0, 0xffffffff);
1588 	hv = NAT_HASH_FN6(src, hv, ifs->ifs_ipf_nattable_sz);
1589 
1590 	WRITE_ENTER(&ifs->ifs_ipf_nat);
1591 
1592 	nat = ifs->ifs_nat_table[1][hv];
1593 	for (; nat; nat = nat->nat_hnext[1]) {
1594 		if (nat->nat_v != 6)
1595 			continue;
1596 
1597 		if (nat->nat_ifps[0] != NULL) {
1598 			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
1599 				continue;
1600 		} else if (ifp != NULL)
1601 			nat->nat_ifps[0] = ifp;
1602 
1603 		if (nat->nat_p != fin->fin_p)
1604 			continue;
1605 		if (IP6_NEQ(&nat->nat_oip6, src) ||
1606 		    IP6_NEQ(&nat->nat_outip6, &dst))
1607 			continue;
1608 
1609 		nflags = nat->nat_flags;
1610 		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
1611 			continue;
1612 
1613 		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
1614 			       NAT_INBOUND) == 1) {
1615 			if ((fin->fin_flx & FI_IGNORE) != 0)
1616 				break;
1617 			if ((nflags & SI_CLONE) != 0) {
1618 				nat = fr_natclone(fin, nat);
1619 				if (nat == NULL)
1620 					break;
1621 			} else {
1622 				MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
1623 				ifs->ifs_nat_stats.ns_wilds--;
1624 				MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
1625 			}
1626 			nat->nat_oport = sport;
1627 			nat->nat_outport = dport;
1628 			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
1629 			nat6_tabmove(nat, ifs);
1630 			break;
1631 		}
1632 	}
1633 
1634 	MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
1635 
1636 	return nat;
1637 }
1638 
1639 
1640 /* ------------------------------------------------------------------------ */
1641 /* Function:    nat6_tabmove                                                */
1642 /* Returns:     Nil                                                         */
1643 /* Parameters:  nat(I) - pointer to NAT structure                           */
1644 /* Write Lock:  ipf_nat                                                     */
1645 /*                                                                          */
1646 /* This function is only called for TCP/UDP NAT table entries where the     */
1647 /* original was placed in the table without hashing on the ports and we now */
1648 /* want to include hashing on port numbers.                                 */
1649 /* ------------------------------------------------------------------------ */
1650 static void nat6_tabmove(nat, ifs)
1651 nat_t *nat;
1652 ipf_stack_t *ifs;
1653 {
1654 	nat_t **natp;
1655 	u_int hv;
1656 
1657 	if (nat->nat_flags & SI_CLONE)
1658 		return;
1659 
1660 	/*
1661 	 * Remove the NAT entry from the old location
1662 	 */
1663 	if (nat->nat_hnext[0])
1664 		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
1665 	*nat->nat_phnext[0] = nat->nat_hnext[0];
1666 	ifs->ifs_nat_stats.ns_bucketlen[0][nat->nat_hv[0]]--;
1667 
1668 	if (nat->nat_hnext[1])
1669 		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
1670 	*nat->nat_phnext[1] = nat->nat_hnext[1];
1671 	ifs->ifs_nat_stats.ns_bucketlen[1][nat->nat_hv[1]]--;
1672 
1673 	/*
1674 	 * Add into the NAT table in the new position
1675 	 */
1676 	hv = NAT_HASH_FN6(&nat->nat_inip6, nat->nat_inport, 0xffffffff);
1677 	hv = NAT_HASH_FN6(&nat->nat_oip6, hv + nat->nat_oport,
1678 			 ifs->ifs_ipf_nattable_sz);
1679 	nat->nat_hv[0] = hv;
1680 	natp = &ifs->ifs_nat_table[0][hv];
1681 	if (*natp)
1682 		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
1683 	nat->nat_phnext[0] = natp;
1684 	nat->nat_hnext[0] = *natp;
1685 	*natp = nat;
1686 	ifs->ifs_nat_stats.ns_bucketlen[0][hv]++;
1687 
1688 	hv = NAT_HASH_FN6(&nat->nat_outip6, nat->nat_outport, 0xffffffff);
1689 	hv = NAT_HASH_FN6(&nat->nat_oip6, hv + nat->nat_oport,
1690 			 ifs->ifs_ipf_nattable_sz);
1691 	nat->nat_hv[1] = hv;
1692 	natp = &ifs->ifs_nat_table[1][hv];
1693 	if (*natp)
1694 		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
1695 	nat->nat_phnext[1] = natp;
1696 	nat->nat_hnext[1] = *natp;
1697 	*natp = nat;
1698 	ifs->ifs_nat_stats.ns_bucketlen[1][hv]++;
1699 }
1700 
1701 
1702 /* ------------------------------------------------------------------------ */
1703 /* Function:    nat6_outlookup                                              */
1704 /* Returns:     nat_t* - NULL == no match,                                  */
1705 /*                       else pointer to matching NAT entry                 */
1706 /* Parameters:  fin(I)   - pointer to packet information                    */
1707 /*              flags(I) - NAT flags for this packet                        */
1708 /*              p(I)     - protocol for this packet                         */
1709 /*              src(I)   - source IP address                                */
1710 /*              dst(I)   - destination IP address                           */
1711 /*              rw(I)    - 1 == write lock on ipf_nat held, 0 == read lock. */
1712 /*                                                                          */
1713 /* Lookup a nat entry based on the source 'real' ip address/port and        */
1714 /* destination address/port.  We use this lookup when sending a packet out, */
1715 /* we're looking for a table entry, based on the source address.            */
1716 /*                                                                          */
1717 /* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
1718 /*                                                                          */
1719 /* NOTE: IT IS ASSUMED THAT ipf_nat IS ONLY HELD WITH A READ LOCK WHEN      */
1720 /*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
1721 /*                                                                          */
1722 /* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
1723 /*            the packet is of said protocol                                */
1724 /* ------------------------------------------------------------------------ */
1725 nat_t *nat6_outlookup(fin, flags, p, src, dst)
1726 fr_info_t *fin;
1727 u_int flags, p;
1728 struct in6_addr *src , *dst;
1729 {
1730 	u_short sport, dport;
1731 	u_int sflags;
1732 	nat_t *nat;
1733 	int nflags;
1734 	void *ifp;
1735 	u_int hv;
1736 	ipf_stack_t *ifs = fin->fin_ifs;
1737 
1738 	ifp = fin->fin_ifp;
1739 
1740 	sflags = flags & IPN_TCPUDPICMP;
1741 	sport = 0;
1742 	dport = 0;
1743 
1744 	switch (p)
1745 	{
1746 	case IPPROTO_TCP :
1747 	case IPPROTO_UDP :
1748 		sport = htons(fin->fin_data[0]);
1749 		dport = htons(fin->fin_data[1]);
1750 		break;
1751 	case IPPROTO_ICMPV6 :
1752 		if (flags & IPN_ICMPERR)
1753 			sport = fin->fin_data[1];
1754 		else
1755 			dport = fin->fin_data[1];
1756 		break;
1757 	default :
1758 		break;
1759 	}
1760 
1761 	if ((flags & SI_WILDP) != 0)
1762 		goto find_out_wild_ports;
1763 
1764 	hv = NAT_HASH_FN6(src, sport, 0xffffffff);
1765 	hv = NAT_HASH_FN6(dst, hv + dport, ifs->ifs_ipf_nattable_sz);
1766 	nat = ifs->ifs_nat_table[0][hv];
1767 	for (; nat; nat = nat->nat_hnext[0]) {
1768 		if (nat->nat_v != 6)
1769 			continue;
1770 
1771 		if (nat->nat_ifps[1] != NULL) {
1772 			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
1773 				continue;
1774 		} else if (ifp != NULL)
1775 			nat->nat_ifps[1] = ifp;
1776 
1777 		nflags = nat->nat_flags;
1778 
1779 		if (IP6_EQ(&nat->nat_inip6, src) &&
1780 		    IP6_EQ(&nat->nat_oip6, dst) &&
1781 		    (((p == 0) && (sflags == (nflags & NAT_TCPUDPICMP))) ||
1782 		    (p == nat->nat_p))) {
1783 			switch (p)
1784 			{
1785 #if 0
1786 			case IPPROTO_GRE :
1787 				if (nat->nat_call[1] != fin->fin_data[0])
1788 					continue;
1789 				break;
1790 #endif
1791 			case IPPROTO_TCP :
1792 			case IPPROTO_UDP :
1793 				if (nat->nat_oport != dport)
1794 					continue;
1795 				if (nat->nat_inport != sport)
1796 					continue;
1797 				break;
1798 			default :
1799 				break;
1800 			}
1801 
1802 #ifdef	IPF_V6_PROXIES
1803 			ipn = nat->nat_ptr;
1804 			if ((ipn != NULL) && (nat->nat_aps != NULL))
1805 				if (appr_match(fin, nat) != 0)
1806 					continue;
1807 #endif
1808 			return nat;
1809 		}
1810 	}
1811 
1812 	/*
1813 	 * So if we didn't find it but there are wildcard members in the hash
1814 	 * table, go back and look for them.  We do this search and update here
1815 	 * because it is modifying the NAT table and we want to do this only
1816 	 * for the first packet that matches.  The exception, of course, is
1817 	 * for "dummy" (FI_IGNORE) lookups.
1818 	 */
1819 find_out_wild_ports:
1820 	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH))
1821 		return NULL;
1822 	if (ifs->ifs_nat_stats.ns_wilds == 0)
1823 		return NULL;
1824 
1825 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
1826 
1827 	hv = NAT_HASH_FN6(src, 0, 0xffffffff);
1828 	hv = NAT_HASH_FN6(dst, hv, ifs->ifs_ipf_nattable_sz);
1829 
1830 	WRITE_ENTER(&ifs->ifs_ipf_nat);
1831 
1832 	nat = ifs->ifs_nat_table[0][hv];
1833 	for (; nat; nat = nat->nat_hnext[0]) {
1834 		if (nat->nat_v != 6)
1835 			continue;
1836 
1837 		if (nat->nat_ifps[1] != NULL) {
1838 			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
1839 				continue;
1840 		} else if (ifp != NULL)
1841 			nat->nat_ifps[1] = ifp;
1842 
1843 		if (nat->nat_p != fin->fin_p)
1844 			continue;
1845 		if (IP6_NEQ(&nat->nat_inip6, src) ||
1846 		    IP6_NEQ(&nat->nat_oip6, dst))
1847 			continue;
1848 
1849 		nflags = nat->nat_flags;
1850 		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
1851 			continue;
1852 
1853 		if (nat_wildok(nat, (int)sport, (int)dport, nflags,
1854 			       NAT_OUTBOUND) == 1) {
1855 			if ((fin->fin_flx & FI_IGNORE) != 0)
1856 				break;
1857 			if ((nflags & SI_CLONE) != 0) {
1858 				nat = fr_natclone(fin, nat);
1859 				if (nat == NULL)
1860 					break;
1861 			} else {
1862 				MUTEX_ENTER(&ifs->ifs_ipf_nat_new);
1863 				ifs->ifs_nat_stats.ns_wilds--;
1864 				MUTEX_EXIT(&ifs->ifs_ipf_nat_new);
1865 			}
1866 			nat->nat_inport = sport;
1867 			nat->nat_oport = dport;
1868 			if (nat->nat_outport == 0)
1869 				nat->nat_outport = sport;
1870 			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
1871 			nat6_tabmove(nat, ifs);
1872 			break;
1873 		}
1874 	}
1875 
1876 	MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
1877 
1878 	return nat;
1879 }
1880 
1881 
1882 /* ------------------------------------------------------------------------ */
1883 /* Function:    nat6_lookupredir                                            */
1884 /* Returns:     nat_t* - NULL == no match,                                  */
1885 /*                       else pointer to matching NAT entry                 */
1886 /* Parameters:  np(I) - pointer to description of packet to find NAT table  */
1887 /*                      entry for.                                          */
1888 /*                                                                          */
1889 /* Lookup the NAT tables to search for a matching redirect                  */
1890 /* ------------------------------------------------------------------------ */
1891 nat_t *nat6_lookupredir(np, ifs)
1892 natlookup_t *np;
1893 ipf_stack_t *ifs;
1894 {
1895 	fr_info_t fi;
1896 	nat_t *nat;
1897 
1898 	bzero((char *)&fi, sizeof (fi));
1899 	if (np->nl_flags & IPN_IN) {
1900 		fi.fin_data[0] = ntohs(np->nl_realport);
1901 		fi.fin_data[1] = ntohs(np->nl_outport);
1902 	} else {
1903 		fi.fin_data[0] = ntohs(np->nl_inport);
1904 		fi.fin_data[1] = ntohs(np->nl_outport);
1905 	}
1906 	if (np->nl_flags & IPN_TCP)
1907 		fi.fin_p = IPPROTO_TCP;
1908 	else if (np->nl_flags & IPN_UDP)
1909 		fi.fin_p = IPPROTO_UDP;
1910 	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
1911 		fi.fin_p = IPPROTO_ICMPV6;
1912 
1913 	fi.fin_ifs = ifs;
1914 	/*
1915 	 * We can do two sorts of lookups:
1916 	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
1917 	 * - default: we have the `in' and `out' address, look for `real'.
1918 	 */
1919 	if (np->nl_flags & IPN_IN) {
1920 		if ((nat = nat6_inlookup(&fi, np->nl_flags, fi.fin_p,
1921 					 &np->nl_realip6, &np->nl_outip6))) {
1922 			np->nl_inipaddr = nat->nat_inip6;
1923 			np->nl_inport = nat->nat_inport;
1924 		}
1925 	} else {
1926 		/*
1927 		 * If nl_inip is non null, this is a lookup based on the real
1928 		 * ip address. Else, we use the fake.
1929 		 */
1930 		if ((nat = nat6_outlookup(&fi, np->nl_flags, fi.fin_p,
1931 					  &np->nl_inip6, &np->nl_outip6))) {
1932 			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
1933 				fr_info_t fin;
1934 				bzero((char *)&fin, sizeof (fin));
1935 				fin.fin_p = nat->nat_p;
1936 				fin.fin_data[0] = ntohs(nat->nat_outport);
1937 				fin.fin_data[1] = ntohs(nat->nat_oport);
1938 				fin.fin_ifs = ifs;
1939 				if (nat6_inlookup(&fin, np->nl_flags, fin.fin_p,
1940 						 &nat->nat_outip6.in6,
1941 						 &nat->nat_oip6.in6) != NULL) {
1942 					np->nl_flags &= ~IPN_FINDFORWARD;
1943 				}
1944 			}
1945 
1946 			np->nl_realip6 = nat->nat_outip6.in6;
1947 			np->nl_realport = nat->nat_outport;
1948 		}
1949  	}
1950 
1951 	return nat;
1952 }
1953 
1954 
1955 /* ------------------------------------------------------------------------ */
1956 /* Function:    nat6_match                                                  */
1957 /* Returns:     int - 0 == no match, 1 == match                             */
1958 /* Parameters:  fin(I)   - pointer to packet information                    */
1959 /*              np(I)    - pointer to NAT rule                              */
1960 /*                                                                          */
1961 /* Pull the matching of a packet against a NAT rule out of that complex     */
1962 /* loop inside fr_checknat6in() and lay it out properly in its own function.*/
1963 /* ------------------------------------------------------------------------ */
1964 static int nat6_match(fin, np)
1965 fr_info_t *fin;
1966 ipnat_t *np;
1967 {
1968 	frtuc_t *ft;
1969 
1970 	if (fin->fin_v != 6)
1971 		return 0;
1972 
1973 	if (np->in_p && fin->fin_p != np->in_p)
1974 		return 0;
1975 
1976 	if (fin->fin_out) {
1977 		if (!(np->in_redir & (NAT_MAP|NAT_MAPBLK)))
1978 			return 0;
1979 		if (IP6_MASKNEQ(&fin->fin_src6, &np->in_in[1], &np->in_in[0])
1980 		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
1981 			return 0;
1982 		if (IP6_MASKNEQ(&fin->fin_dst6, &np->in_src[1], &np->in_src[0])
1983 		    ^ ((np->in_flags & IPN_NOTDST) != 0))
1984 			return 0;
1985 	} else {
1986 		if (!(np->in_redir & NAT_REDIRECT))
1987 			return 0;
1988 		if (IP6_MASKNEQ(&fin->fin_src6, &np->in_src[1], &np->in_src[0])
1989 		    ^ ((np->in_flags & IPN_NOTSRC) != 0))
1990 			return 0;
1991 		if (IP6_MASKNEQ(&fin->fin_dst6, &np->in_out[1], &np->in_out[0])
1992 		    ^ ((np->in_flags & IPN_NOTDST) != 0))
1993 			return 0;
1994 	}
1995 
1996 	ft = &np->in_tuc;
1997 	if (!(fin->fin_flx & FI_TCPUDP) ||
1998 	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
1999 		if (ft->ftu_scmp || ft->ftu_dcmp)
2000 			return 0;
2001 		return 1;
2002 	}
2003 
2004 	return fr_tcpudpchk(fin, ft);
2005 }
2006 
2007 
2008 /* ------------------------------------------------------------------------ */
2009 /* Function:    fr_checknat6out                                             */
2010 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
2011 /*                     0 == no packet translation occurred,                 */
2012 /*                     1 == packet was successfully translated.             */
2013 /* Parameters:  fin(I)   - pointer to packet information                    */
2014 /*              passp(I) - pointer to filtering result flags                */
2015 /*                                                                          */
2016 /* Check to see if an outcoming packet should be changed.  ICMP packets are */
2017 /* first checked to see if they match an existing entry (if an error),      */
2018 /* otherwise a search of the current NAT table is made.  If neither results */
2019 /* in a match then a search for a matching NAT rule is made.  Create a new  */
2020 /* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
2021 /* packet header(s) as required.                                            */
2022 /* ------------------------------------------------------------------------ */
2023 int fr_checknat6out(fin, passp)
2024 fr_info_t *fin;
2025 u_32_t *passp;
2026 {
2027 	struct ifnet *ifp, *sifp;
2028 	int rval, natfailed;
2029 	ipnat_t *np = NULL;
2030 	u_int nflags = 0;
2031 	i6addr_t ipa, iph;
2032 	int natadd = 1;
2033 	frentry_t *fr;
2034 	nat_t *nat;
2035 	ipf_stack_t *ifs = fin->fin_ifs;
2036 
2037 	if (ifs->ifs_nat_stats.ns_rules == 0 || ifs->ifs_fr_nat_lock != 0)
2038 		return 0;
2039 
2040 	natfailed = 0;
2041 	fr = fin->fin_fr;
2042 	sifp = fin->fin_ifp;
2043 	if ((fr != NULL) && !(fr->fr_flags & FR_DUP) &&
2044 	    fr->fr_tifs[fin->fin_rev].fd_ifp &&
2045 	    fr->fr_tifs[fin->fin_rev].fd_ifp != (void *)-1)
2046 		fin->fin_ifp = fr->fr_tifs[fin->fin_rev].fd_ifp;
2047 	ifp = fin->fin_ifp;
2048 
2049 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
2050 		switch (fin->fin_p)
2051 		{
2052 		case IPPROTO_TCP :
2053 			nflags = IPN_TCP;
2054 			break;
2055 		case IPPROTO_UDP :
2056 			nflags = IPN_UDP;
2057 			break;
2058 		case IPPROTO_ICMPV6 :
2059 			/*
2060 			 * This is an incoming packet, so the destination is
2061 			 * the icmp6_id and the source port equals 0
2062 			 */
2063 			if ((fin->fin_flx & FI_ICMPQUERY) != 0)
2064 				nflags = IPN_ICMPQUERY;
2065 			break;
2066 		default :
2067 			break;
2068 		}
2069 
2070 #ifdef	IPF_V6_PROXIES
2071 		if ((nflags & IPN_TCPUDP))
2072 			tcp = fin->fin_dp;
2073 #endif
2074 	}
2075 
2076 	ipa = fin->fin_src6;
2077 
2078 	READ_ENTER(&ifs->ifs_ipf_nat);
2079 
2080 	if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) &&
2081 	    (nat = nat6_icmperror(fin, &nflags, NAT_OUTBOUND)))
2082 		/*EMPTY*/;
2083 	else if ((fin->fin_flx & FI_FRAG) && (nat = fr_nat_knownfrag(fin)))
2084 		natadd = 0;
2085 	else if ((nat = nat6_outlookup(fin, nflags|NAT_SEARCH,
2086 		    (u_int)fin->fin_p, &fin->fin_src6.in6,
2087 		    &fin->fin_dst6.in6))) {
2088 		nflags = nat->nat_flags;
2089 	} else {
2090 		u_32_t hv, nmsk;
2091 		i6addr_t msk;
2092 		int i;
2093 
2094 		/*
2095 		 * If there is no current entry in the nat table for this IP#,
2096 		 * create one for it (if there is a matching rule).
2097 		 */
2098 		RWLOCK_EXIT(&ifs->ifs_ipf_nat);
2099 		i = 3;
2100 		msk.i6[0] = 0xffffffff;
2101 		msk.i6[1] = 0xffffffff;
2102 		msk.i6[2] = 0xffffffff;
2103 		msk.i6[3] = 0xffffffff;
2104 		nmsk = ifs->ifs_nat6_masks[3];
2105 		WRITE_ENTER(&ifs->ifs_ipf_nat);
2106 maskloop:
2107 		IP6_AND(&ipa, &msk, &iph);
2108 		hv = NAT_HASH_FN6(&iph, 0, ifs->ifs_ipf_natrules_sz);
2109 		for (np = ifs->ifs_nat_rules[hv]; np; np = np->in_mnext)
2110 		{
2111 			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
2112 				continue;
2113 			if (np->in_v != 6)
2114 				continue;
2115 			if (np->in_p && (np->in_p != fin->fin_p))
2116 				continue;
2117 			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
2118 				continue;
2119 			if (np->in_flags & IPN_FILTER) {
2120 				if (!nat6_match(fin, np))
2121 					continue;
2122 			} else if (!IP6_MASKEQ(&ipa, &np->in_in[1],
2123 			    &np->in_in[0]))
2124 				continue;
2125 
2126 			if ((fr != NULL) &&
2127 			    !fr_matchtag(&np->in_tag, &fr->fr_nattag))
2128 				continue;
2129 
2130 #ifdef	IPF_V6_PROXIES
2131 			if (*np->in_plabel != '\0') {
2132 				if (((np->in_flags & IPN_FILTER) == 0) &&
2133 				    (np->in_dport != tcp->th_dport))
2134 					continue;
2135 				if (appr_ok(fin, tcp, np) == 0)
2136 					continue;
2137 			}
2138 #endif
2139 
2140 			if (nat = nat6_new(fin, np, NULL, nflags,
2141 					   NAT_OUTBOUND)) {
2142 				np->in_hits++;
2143 				break;
2144 			} else
2145 				natfailed = -1;
2146 		}
2147 		if ((np == NULL) && (i >= 0)) {
2148 			while (i >= 0) {
2149 				while (nmsk) {
2150 					msk.i6[i] = htonl(ntohl(msk.i6[i])<<1);
2151 					if ((nmsk & 0x80000000) != 0) {
2152 						nmsk <<= 1;
2153 						goto maskloop;
2154 					}
2155 					nmsk <<= 1;
2156 				}
2157 				msk.i6[i--] = 0;
2158 				if (i >= 0) {
2159 					nmsk = ifs->ifs_nat6_masks[i];
2160 					if (nmsk != 0)
2161 						goto maskloop;
2162 				}
2163 			}
2164 		}
2165 		MUTEX_DOWNGRADE(&ifs->ifs_ipf_nat);
2166 	}
2167 
2168 	if (nat != NULL) {
2169 		rval = fr_nat6out(fin, nat, natadd, nflags);
2170 		if (rval == 1) {
2171 			MUTEX_ENTER(&nat->nat_lock);
2172 			nat->nat_ref++;
2173 			MUTEX_EXIT(&nat->nat_lock);
2174 			nat->nat_touched = ifs->ifs_fr_ticks;
2175 			fin->fin_nat = nat;
2176 		}
2177 	} else
2178 		rval = natfailed;
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_data_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 		if (rval == 1) {
2459 			MUTEX_ENTER(&nat->nat_lock);
2460 			nat->nat_ref++;
2461 			MUTEX_EXIT(&nat->nat_lock);
2462 			nat->nat_touched = ifs->ifs_fr_ticks;
2463 			fin->fin_nat = nat;
2464 			fin->fin_state = nat->nat_state;
2465 		}
2466 	} else
2467 		rval = natfailed;
2468 	RWLOCK_EXIT(&ifs->ifs_ipf_nat);
2469 
2470 	if (rval == -1) {
2471 		if (passp != NULL)
2472 			*passp = FR_BLOCK;
2473 		fin->fin_flx |= FI_BADNAT;
2474 	}
2475 	return rval;
2476 }
2477 
2478 
2479 /* ------------------------------------------------------------------------ */
2480 /* Function:    fr_nat6in                                                   */
2481 /* Returns:     int - -1 == packet failed NAT checks so block it,           */
2482 /*                     1 == packet was successfully translated.             */
2483 /* Parameters:  fin(I)    - pointer to packet information                   */
2484 /*              nat(I)    - pointer to NAT structure                        */
2485 /*              natadd(I) - flag indicating if it is safe to add frag cache */
2486 /*              nflags(I) - NAT flags set for this packet                   */
2487 /* Locks Held:  ipf_nat (READ)                                              */
2488 /*                                                                          */
2489 /* Translate a packet coming "in" on an interface.                          */
2490 /* ------------------------------------------------------------------------ */
2491 int fr_nat6in(fin, nat, natadd, nflags)
2492 fr_info_t *fin;
2493 nat_t *nat;
2494 int natadd;
2495 u_32_t nflags;
2496 {
2497 	struct icmp6_hdr *icmp6;
2498 	u_short *csump;
2499 	tcphdr_t *tcp;
2500 	ipnat_t *np;
2501 	ipf_stack_t *ifs = fin->fin_ifs;
2502 
2503 #if SOLARIS && defined(_KERNEL)
2504 	net_data_t net_data_p = ifs->ifs_ipf_ipv6;
2505 #endif
2506 
2507 	tcp = NULL;
2508 	csump = NULL;
2509 	np = nat->nat_ptr;
2510 	fin->fin_fr = nat->nat_fr;
2511 
2512 	if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
2513 		(void) fr_nat_newfrag(fin, 0, nat);
2514 
2515 #ifdef	IPF_V6_PROXIES
2516 	if (np != NULL) {
2517 
2518 	/* ------------------------------------------------------------- */
2519 	/* A few quick notes:						 */
2520 	/*	Following are test conditions prior to calling the 	 */
2521 	/*	appr_check routine.					 */
2522 	/*								 */
2523 	/* 	A NULL tcp indicates a non TCP/UDP packet.  When dealing */
2524 	/*	with a map rule, we attempt to match the packet's	 */
2525 	/*	source port against in_dport, otherwise	we'd compare the */
2526 	/*	packet's destination.			 		 */
2527 	/* ------------------------------------------------------------- */
2528 		if (np->in_apr != NULL) {
2529 			i = appr_check(fin, nat);
2530 			if (i == -1) {
2531 				return -1;
2532 			}
2533 		}
2534 	}
2535 #endif
2536 
2537 #ifdef	IPFILTER_SYNC
2538 	ipfsync_update(SMC_NAT, fin, nat->nat_sync);
2539 #endif
2540 
2541 	MUTEX_ENTER(&nat->nat_lock);
2542 	nat->nat_bytes[0] += fin->fin_plen;
2543 	nat->nat_pkts[0]++;
2544 	MUTEX_EXIT(&nat->nat_lock);
2545 
2546 	fin->fin_ip6->ip6_dst = nat->nat_inip6.in6;
2547 	fin->fin_dst6 = nat->nat_inip6;
2548 
2549 	if (nflags & IPN_TCPUDP)
2550 		tcp = fin->fin_dp;
2551 
2552 	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
2553 		if ((nat->nat_inport != 0) && (nflags & IPN_TCPUDP)) {
2554 			tcp->th_dport = nat->nat_inport;
2555 			fin->fin_data[1] = ntohs(nat->nat_inport);
2556 		}
2557 
2558 
2559 		if ((nat->nat_inport != 0) && (nflags & IPN_ICMPQUERY)) {
2560 			icmp6 = fin->fin_dp;
2561 
2562 			icmp6->icmp6_id = nat->nat_inport;
2563 		}
2564 
2565 		csump = nat_proto(fin, nat, nflags);
2566 	}
2567 
2568 	nat_update(fin, nat, np);
2569 
2570 	/*
2571 	 * In case they are being forwarded, inbound packets always need to have
2572 	 * their checksum adjusted even if hardware checksum validation said OK.
2573 	 */
2574 	if (csump != NULL) {
2575 		if (nat->nat_dir == NAT_OUTBOUND)
2576 			fix_incksum(csump, nat->nat_sumd[0]);
2577 		else
2578 			fix_outcksum(csump, nat->nat_sumd[0]);
2579 	}
2580 
2581 #if SOLARIS && defined(_KERNEL)
2582 	if (nflags & IPN_TCPUDP &&
2583 	    NET_IS_HCK_L4_PART(net_data_p, fin->fin_m)) {
2584 		/*
2585 		 * Need to adjust the partial checksum result stored in
2586 		 * db_cksum16, which will be used for validation in IP.
2587 		 * See IP_CKSUM_RECV().
2588 		 * Adjustment data should be the inverse of the IP address
2589 		 * changes, because db_cksum16 is supposed to be the complement
2590 		 * of the pesudo header.
2591 		 */
2592 		csump = &fin->fin_m->b_datap->db_cksum16;
2593 		if (nat->nat_dir == NAT_OUTBOUND)
2594 			fix_outcksum(csump, nat->nat_sumd[1]);
2595 		else
2596 			fix_incksum(csump, nat->nat_sumd[1]);
2597 	}
2598 #endif
2599 
2600 	ATOMIC_INCL(ifs->ifs_nat_stats.ns_mapped[0]);
2601 	fin->fin_flx |= FI_NATED;
2602 	if (np != NULL && np->in_tag.ipt_num[0] != 0)
2603 		fin->fin_nattag = &np->in_tag;
2604 	return 1;
2605 }
2606 
2607 
2608 /* ------------------------------------------------------------------------ */
2609 /* Function:    nat_icmpquerytype6                                          */
2610 /* Returns:     int - 1 == success, 0 == failure                            */
2611 /* Parameters:  icmptype(I) - ICMP type number                              */
2612 /*                                                                          */
2613 /* Tests to see if the ICMP type number passed is a query/response type or  */
2614 /* not.                                                                     */
2615 /* ------------------------------------------------------------------------ */
2616 static INLINE int nat_icmpquerytype6(icmptype)
2617 int icmptype;
2618 {
2619 
2620 	/*
2621 	 * For the ICMP query NAT code, it is essential that both the query
2622 	 * and the reply match on the NAT rule. Because the NAT structure
2623 	 * does not keep track of the icmptype, and a single NAT structure
2624 	 * is used for all icmp types with the same src, dest and id, we
2625 	 * simply define the replies as queries as well. The funny thing is,
2626 	 * altough it seems silly to call a reply a query, this is exactly
2627 	 * as it is defined in the IPv4 specification
2628 	 */
2629 
2630 	switch (icmptype)
2631 	{
2632 
2633 	case ICMP6_ECHO_REPLY:
2634 	case ICMP6_ECHO_REQUEST:
2635 	/* route aedvertisement/solliciation is currently unsupported: */
2636 	/* it would require rewriting the ICMP data section            */
2637 	case ICMP6_MEMBERSHIP_QUERY:
2638 	case ICMP6_MEMBERSHIP_REPORT:
2639 	case ICMP6_MEMBERSHIP_REDUCTION:
2640 	case ICMP6_WRUREQUEST:
2641 	case ICMP6_WRUREPLY:
2642 	case MLD6_MTRACE_RESP:
2643 	case MLD6_MTRACE:
2644 		return 1;
2645 	default:
2646 		return 0;
2647 	}
2648 }
2649