1254401Scy/*
2254401Scy * Copyright (C) 2012 by Darren Reed.
3254401Scy *
4254401Scy * See the IPFILTER.LICENCE file for details on licencing.
5254401Scy */
6254401Scy#if defined(KERNEL) || defined(_KERNEL)
7254401Scy# undef KERNEL
8255332Scy# undef _KERNEL
9254401Scy# define        KERNEL	1
10255332Scy# define        _KERNEL	1
11254401Scy#endif
12254401Scy#include <sys/errno.h>
13254401Scy#include <sys/types.h>
14254401Scy#include <sys/param.h>
15254401Scy#include <sys/time.h>
16254401Scy#include <sys/file.h>
17254401Scy#if defined(_KERNEL) && defined(__NetBSD_Version__) && \
18254401Scy    (__NetBSD_Version__ >= 399002000)
19254401Scy# include <sys/kauth.h>
20254401Scy#endif
21254401Scy#if !defined(_KERNEL)
22254401Scy# include <stdio.h>
23254401Scy# include <string.h>
24254401Scy# include <stdlib.h>
25255332Scy# define _KERNEL
26254401Scy# ifdef ipf_nat6__OpenBSD__
27254401Scystruct file;
28254401Scy# endif
29254401Scy# include <sys/uio.h>
30255332Scy# undef _KERNEL
31254401Scy#endif
32369277Scy#if defined(_KERNEL) && defined(__FreeBSD__)
33254401Scy# include <sys/filio.h>
34254401Scy# include <sys/fcntl.h>
35254401Scy#else
36254401Scy# include <sys/ioctl.h>
37254401Scy#endif
38254401Scy# include <sys/fcntl.h>
39254401Scy# include <sys/protosw.h>
40254401Scy#include <sys/socket.h>
41254401Scy#if defined(_KERNEL)
42254401Scy# include <sys/systm.h>
43344833Scy# if !defined(__SVR4)
44254401Scy#  include <sys/mbuf.h>
45254401Scy# endif
46254401Scy#endif
47344833Scy#if defined(__SVR4)
48254401Scy# include <sys/filio.h>
49254401Scy# include <sys/byteorder.h>
50255332Scy# ifdef _KERNEL
51254401Scy#  include <sys/dditypes.h>
52254401Scy# endif
53254401Scy# include <sys/stream.h>
54254401Scy# include <sys/kmem.h>
55254401Scy#endif
56369277Scy#if defined(__FreeBSD__)
57254401Scy# include <sys/queue.h>
58254401Scy#endif
59254401Scy#include <net/if.h>
60369277Scy#if defined(__FreeBSD__)
61254401Scy# include <net/if_var.h>
62254401Scy#endif
63254401Scy#ifdef sun
64254401Scy# include <net/af.h>
65254401Scy#endif
66254401Scy#include <net/route.h>
67254401Scy#include <netinet/in.h>
68254401Scy#include <netinet/in_systm.h>
69254401Scy#include <netinet/ip.h>
70254401Scy
71254401Scy#ifdef RFC1825
72254401Scy# include <vpn/md5.h>
73254401Scy# include <vpn/ipsec.h>
74254401Scyextern struct ifnet vpnif;
75254401Scy#endif
76254401Scy
77254401Scy# include <netinet/ip_var.h>
78254401Scy#include <netinet/tcp.h>
79254401Scy#include <netinet/udp.h>
80254401Scy#include <netinet/ip_icmp.h>
81254401Scy#include "netinet/ip_compat.h"
82254401Scy#include <netinet/tcpip.h>
83254401Scy#include "netinet/ip_fil.h"
84254401Scy#include "netinet/ip_nat.h"
85254401Scy#include "netinet/ip_frag.h"
86254401Scy#include "netinet/ip_state.h"
87254401Scy#include "netinet/ip_proxy.h"
88254401Scy#include "netinet/ip_lookup.h"
89254401Scy#include "netinet/ip_dstlist.h"
90254401Scy#include "netinet/ip_sync.h"
91369277Scy#if defined(__FreeBSD__)
92254401Scy# include <sys/malloc.h>
93254401Scy#endif
94254401Scy#ifdef HAS_SYS_MD5_H
95254401Scy# include <sys/md5.h>
96254401Scy#else
97254401Scy# include "md5.h"
98254401Scy#endif
99254401Scy/* END OF INCLUDES */
100254401Scy
101254401Scy#undef	SOCKADDR_IN
102254401Scy#define	SOCKADDR_IN	struct sockaddr_in
103254401Scy
104254401Scy#if !defined(lint)
105254401Scystatic const char rcsid[] = "@(#)$Id: ip_nat6.c,v 1.22.2.20 2012/07/22 08:04:23 darren_r Exp $";
106254401Scy#endif
107254401Scy
108254401Scy#ifdef USE_INET6
109369245Sgit2svnstatic struct hostmap *ipf_nat6_hostmap(ipf_nat_softc_t *, ipnat_t *,
110254401Scy					     i6addr_t *, i6addr_t *,
111369245Sgit2svn					     i6addr_t *, u_32_t);
112369245Sgit2svnstatic int ipf_nat6_match(fr_info_t *, ipnat_t *);
113369245Sgit2svnstatic void ipf_nat6_tabmove(ipf_nat_softc_t *, nat_t *);
114369245Sgit2svnstatic int ipf_nat6_decap(fr_info_t *, nat_t *);
115369245Sgit2svnstatic int ipf_nat6_nextaddr(fr_info_t *, nat_addr_t *, i6addr_t *,
116369245Sgit2svn				  i6addr_t *);
117369245Sgit2svnstatic int ipf_nat6_icmpquerytype(int);
118369245Sgit2svnstatic int ipf_nat6_out(fr_info_t *, nat_t *, int, u_32_t);
119369245Sgit2svnstatic int ipf_nat6_in(fr_info_t *, nat_t *, int, u_32_t);
120369245Sgit2svnstatic int ipf_nat6_builddivertmp(ipf_nat_softc_t *, ipnat_t *);
121369245Sgit2svnstatic int ipf_nat6_nextaddrinit(ipf_main_softc_t *, char *,
122369245Sgit2svn				      nat_addr_t *, int, void *);
123369245Sgit2svnstatic int ipf_nat6_insert(ipf_main_softc_t *, ipf_nat_softc_t *,
124369245Sgit2svn				nat_t *);
125254401Scy
126254401Scy
127254401Scy#define	NINCLSIDE6(y,x)	ATOMIC_INCL(softn->ipf_nat_stats.ns_side6[y].x)
128254401Scy#define	NBUMPSIDE(y,x)	softn->ipf_nat_stats.ns_side[y].x++
129254401Scy#define	NBUMPSIDE6(y,x)	softn->ipf_nat_stats.ns_side6[y].x++
130254401Scy#define	NBUMPSIDE6D(y,x) \
131254401Scy			do { \
132254401Scy				softn->ipf_nat_stats.ns_side6[y].x++; \
133254401Scy				DT(x); \
134254401Scy			} while (0)
135254401Scy#define	NBUMPSIDE6DX(y,x,z) \
136254401Scy			do { \
137254401Scy				softn->ipf_nat_stats.ns_side6[y].x++; \
138254401Scy				DT(z); \
139254401Scy			} while (0)
140254401Scy
141254401Scy
142254401Scy/* ------------------------------------------------------------------------ */
143254401Scy/* Function:    ipf_nat6_ruleaddrinit                                       */
144254401Scy/* Returns:     int   - 0 == success, else failure                          */
145254401Scy/* Parameters:  in(I) - NAT rule that requires address fields to be init'd  */
146254401Scy/*                                                                          */
147254401Scy/* For each of the source/destination address fields in a NAT rule, call    */
148254401Scy/* ipf_nat6_nextaddrinit() to prepare the structure for active duty.  Other */
149254401Scy/* IPv6 specific actions can also be taken care of here.                    */
150254401Scy/* ------------------------------------------------------------------------ */
151254401Scyint
152254401Scyipf_nat6_ruleaddrinit(softc, softn, n)
153254401Scy	ipf_main_softc_t *softc;
154254401Scy	ipf_nat_softc_t *softn;
155254401Scy	ipnat_t *n;
156254401Scy{
157254401Scy	int idx, error;
158254401Scy
159254401Scy	if (n->in_redir == NAT_BIMAP) {
160254401Scy		n->in_ndstip6 = n->in_osrcip6;
161254401Scy		n->in_ndstmsk6 = n->in_osrcmsk6;
162254401Scy		n->in_odstip6 = n->in_nsrcip6;
163254401Scy		n->in_odstmsk6 = n->in_nsrcmsk6;
164254401Scy
165254401Scy	}
166254401Scy
167254401Scy	if (n->in_redir & NAT_REDIRECT)
168254401Scy		idx = 1;
169254401Scy	else
170254401Scy		idx = 0;
171254401Scy	/*
172254401Scy	 * Initialise all of the address fields.
173254401Scy	 */
174254401Scy	error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_osrc, 1,
175254401Scy				      n->in_ifps[idx]);
176254401Scy	if (error != 0)
177254401Scy		return error;
178254401Scy
179254401Scy	error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_odst, 1,
180254401Scy				      n->in_ifps[idx]);
181254401Scy	if (error != 0)
182254401Scy		return error;
183254401Scy
184254401Scy	error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_nsrc, 1,
185254401Scy				      n->in_ifps[idx]);
186254401Scy	if (error != 0)
187254401Scy		return error;
188254401Scy
189254401Scy	error = ipf_nat6_nextaddrinit(softc, n->in_names, &n->in_ndst, 1,
190254401Scy				      n->in_ifps[idx]);
191254401Scy	if (error != 0)
192254401Scy		return error;
193254401Scy
194254401Scy	if (n->in_redir & NAT_DIVERTUDP)
195254401Scy		ipf_nat6_builddivertmp(softn, n);
196254401Scy	return 0;
197254401Scy}
198254401Scy
199254401Scy
200254401Scy/* ------------------------------------------------------------------------ */
201254401Scy/* Function:    ipf_nat6_addrdr                                             */
202254401Scy/* Returns:     Nil                                                         */
203254401Scy/* Parameters:  n(I) - pointer to NAT rule to add                           */
204254401Scy/*                                                                          */
205254401Scy/* Adds a redirect rule to the hash table of redirect rules and the list of */
206254401Scy/* loaded NAT rules.  Updates the bitmask indicating which netmasks are in  */
207254401Scy/* use by redirect rules.                                                   */
208254401Scy/* ------------------------------------------------------------------------ */
209254401Scyvoid
210254401Scyipf_nat6_addrdr(softn, n)
211254401Scy	ipf_nat_softc_t *softn;
212254401Scy	ipnat_t *n;
213254401Scy{
214254401Scy	i6addr_t *mask;
215254401Scy	ipnat_t **np;
216254401Scy	i6addr_t j;
217254401Scy	u_int hv;
218254401Scy	int k;
219254401Scy
220254401Scy	if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) {
221254401Scy		k = count6bits(n->in_nsrcmsk6.i6);
222254401Scy		mask = &n->in_nsrcmsk6;
223254401Scy		IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j);
224254401Scy		hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz);
225254401Scy
226254401Scy	} else if (n->in_odstatype == FRI_NORMAL) {
227254401Scy		k = count6bits(n->in_odstmsk6.i6);
228254401Scy		mask = &n->in_odstmsk6;
229254401Scy		IP6_AND(&n->in_odstip6, &n->in_odstmsk6, &j);
230254401Scy		hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_rdrrules_sz);
231254401Scy	} else {
232254401Scy		k = 0;
233254401Scy		hv = 0;
234254401Scy		mask = NULL;
235254401Scy	}
236254401Scy	ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_rdr_mask);
237254401Scy
238254401Scy	np = softn->ipf_nat_rdr_rules + hv;
239254401Scy	while (*np != NULL)
240254401Scy		np = &(*np)->in_rnext;
241254401Scy	n->in_rnext = NULL;
242254401Scy	n->in_prnext = np;
243254401Scy	n->in_hv[0] = hv;
244254401Scy	n->in_use++;
245254401Scy	*np = n;
246254401Scy}
247254401Scy
248254401Scy
249254401Scy/* ------------------------------------------------------------------------ */
250254401Scy/* Function:    ipf_nat6_addmap                                             */
251254401Scy/* Returns:     Nil                                                         */
252254401Scy/* Parameters:  n(I) - pointer to NAT rule to add                           */
253254401Scy/*                                                                          */
254254401Scy/* Adds a NAT map rule to the hash table of rules and the list of  loaded   */
255254401Scy/* NAT rules.  Updates the bitmask indicating which netmasks are in use by  */
256254401Scy/* redirect rules.                                                          */
257254401Scy/* ------------------------------------------------------------------------ */
258254401Scyvoid
259254401Scyipf_nat6_addmap(softn, n)
260254401Scy	ipf_nat_softc_t *softn;
261254401Scy	ipnat_t *n;
262254401Scy{
263254401Scy	i6addr_t *mask;
264254401Scy	ipnat_t **np;
265254401Scy	i6addr_t j;
266254401Scy	u_int hv;
267254401Scy	int k;
268254401Scy
269254401Scy	if (n->in_osrcatype == FRI_NORMAL) {
270254401Scy		k = count6bits(n->in_osrcmsk6.i6);
271254401Scy		mask = &n->in_osrcmsk6;
272254401Scy		IP6_AND(&n->in_osrcip6, &n->in_osrcmsk6, &j);
273254401Scy		hv = NAT_HASH_FN6(&j, 0, softn->ipf_nat_maprules_sz);
274254401Scy	} else {
275254401Scy		k = 0;
276254401Scy		hv = 0;
277254401Scy		mask = NULL;
278254401Scy	}
279254401Scy	ipf_inet6_mask_add(k, mask, &softn->ipf_nat6_map_mask);
280254401Scy
281254401Scy	np = softn->ipf_nat_map_rules + hv;
282254401Scy	while (*np != NULL)
283254401Scy		np = &(*np)->in_mnext;
284254401Scy	n->in_mnext = NULL;
285254401Scy	n->in_pmnext = np;
286254401Scy	n->in_hv[1] = hv;
287254401Scy	n->in_use++;
288254401Scy	*np = n;
289254401Scy}
290254401Scy
291254401Scy
292254401Scy/* ------------------------------------------------------------------------ */
293254401Scy/* Function:    ipf_nat6_del_rdr                                             */
294254401Scy/* Returns:     Nil                                                         */
295254401Scy/* Parameters:  n(I) - pointer to NAT rule to delete                        */
296254401Scy/*                                                                          */
297254401Scy/* Removes a NAT rdr rule from the hash table of NAT rdr rules.             */
298254401Scy/* ------------------------------------------------------------------------ */
299254401Scyvoid
300254401Scyipf_nat6_delrdr(softn, n)
301254401Scy	ipf_nat_softc_t *softn;
302254401Scy	ipnat_t *n;
303254401Scy{
304254401Scy	i6addr_t *mask;
305254401Scy	int k;
306254401Scy
307254401Scy	if ((n->in_redir & NAT_BIMAP) == NAT_BIMAP) {
308254401Scy		k = count6bits(n->in_nsrcmsk6.i6);
309254401Scy		mask = &n->in_nsrcmsk6;
310254401Scy	} else if (n->in_odstatype == FRI_NORMAL) {
311254401Scy		k = count6bits(n->in_odstmsk6.i6);
312254401Scy		mask = &n->in_odstmsk6;
313254401Scy	} else {
314254401Scy		k = 0;
315254401Scy		mask = NULL;
316254401Scy	}
317254401Scy	ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_rdr_mask);
318254401Scy
319254401Scy	if (n->in_rnext != NULL)
320254401Scy		n->in_rnext->in_prnext = n->in_prnext;
321254401Scy	*n->in_prnext = n->in_rnext;
322254401Scy	n->in_use--;
323254401Scy}
324254401Scy
325254401Scy
326254401Scy/* ------------------------------------------------------------------------ */
327254401Scy/* Function:    ipf_nat6_delmap                                             */
328254401Scy/* Returns:     Nil                                                         */
329254401Scy/* Parameters:  n(I) - pointer to NAT rule to delete                        */
330254401Scy/*                                                                          */
331254401Scy/* Removes a NAT map rule from the hash table of NAT map rules.             */
332254401Scy/* ------------------------------------------------------------------------ */
333254401Scyvoid
334254401Scyipf_nat6_delmap(softn, n)
335254401Scy	ipf_nat_softc_t *softn;
336254401Scy	ipnat_t *n;
337254401Scy{
338254401Scy	i6addr_t *mask;
339254401Scy	int k;
340254401Scy
341254401Scy	if (n->in_osrcatype == FRI_NORMAL) {
342254401Scy		k = count6bits(n->in_osrcmsk6.i6);
343254401Scy		mask = &n->in_osrcmsk6;
344254401Scy	} else {
345254401Scy		k = 0;
346254401Scy		mask = NULL;
347254401Scy	}
348254401Scy	ipf_inet6_mask_del(k, mask, &softn->ipf_nat6_map_mask);
349254401Scy
350254401Scy	if (n->in_mnext != NULL)
351254401Scy		n->in_mnext->in_pmnext = n->in_pmnext;
352254401Scy	*n->in_pmnext = n->in_mnext;
353254401Scy	n->in_use--;
354254401Scy}
355254401Scy
356254401Scy
357254401Scy/* ------------------------------------------------------------------------ */
358254401Scy/* Function:    ipf_nat6_hostmap                                            */
359254401Scy/* Returns:     struct hostmap* - NULL if no hostmap could be created,      */
360254401Scy/*                                else a pointer to the hostmapping to use  */
361254401Scy/* Parameters:  np(I)   - pointer to NAT rule                               */
362254401Scy/*              real(I) - real IP address                                   */
363254401Scy/*              map(I)  - mapped IP address                                 */
364254401Scy/*              port(I) - destination port number                           */
365254401Scy/* Write Locks: ipf_nat                                                     */
366254401Scy/*                                                                          */
367254401Scy/* Check if an ip address has already been allocated for a given mapping    */
368254401Scy/* that is not doing port based translation.  If is not yet allocated, then */
369254401Scy/* create a new entry if a non-NULL NAT rule pointer has been supplied.     */
370254401Scy/* ------------------------------------------------------------------------ */
371254401Scystatic struct hostmap *
372254401Scyipf_nat6_hostmap(softn, np, src, dst, map, port)
373254401Scy	ipf_nat_softc_t *softn;
374254401Scy	ipnat_t *np;
375254401Scy	i6addr_t *src, *dst, *map;
376254401Scy	u_32_t port;
377254401Scy{
378254401Scy	hostmap_t *hm;
379254401Scy	u_int hv;
380254401Scy
381254401Scy	hv = (src->i6[3] ^ dst->i6[3]);
382254401Scy	hv += (src->i6[2] ^ dst->i6[2]);
383254401Scy	hv += (src->i6[1] ^ dst->i6[1]);
384254401Scy	hv += (src->i6[0] ^ dst->i6[0]);
385254401Scy	hv += src->i6[3];
386254401Scy	hv += src->i6[2];
387254401Scy	hv += src->i6[1];
388254401Scy	hv += src->i6[0];
389254401Scy	hv += dst->i6[3];
390254401Scy	hv += dst->i6[2];
391254401Scy	hv += dst->i6[1];
392254401Scy	hv += dst->i6[0];
393369275Scy	hv %= softn->ipf_nat_hostmap_sz;
394254401Scy	for (hm = softn->ipf_hm_maptable[hv]; hm; hm = hm->hm_next)
395254401Scy		if (IP6_EQ(&hm->hm_osrc6, src) &&
396254401Scy		    IP6_EQ(&hm->hm_odst6, dst) &&
397254401Scy		    ((np == NULL) || (np == hm->hm_ipnat)) &&
398254401Scy		    ((port == 0) || (port == hm->hm_port))) {
399254401Scy			softn->ipf_nat_stats.ns_hm_addref++;
400254401Scy			hm->hm_ref++;
401254401Scy			return hm;
402254401Scy		}
403254401Scy
404254401Scy	if (np == NULL) {
405254401Scy		softn->ipf_nat_stats.ns_hm_nullnp++;
406254401Scy		return NULL;
407254401Scy	}
408254401Scy
409254401Scy	KMALLOC(hm, hostmap_t *);
410254401Scy	if (hm) {
411254401Scy		hm->hm_next = softn->ipf_hm_maplist;
412254401Scy		hm->hm_pnext = &softn->ipf_hm_maplist;
413254401Scy		if (softn->ipf_hm_maplist != NULL)
414254401Scy			softn->ipf_hm_maplist->hm_pnext = &hm->hm_next;
415254401Scy		softn->ipf_hm_maplist = hm;
416254401Scy		hm->hm_hnext = softn->ipf_hm_maptable[hv];
417254401Scy		hm->hm_phnext = softn->ipf_hm_maptable + hv;
418254401Scy		if (softn->ipf_hm_maptable[hv] != NULL)
419254401Scy			softn->ipf_hm_maptable[hv]->hm_phnext = &hm->hm_hnext;
420254401Scy		softn->ipf_hm_maptable[hv] = hm;
421254401Scy		hm->hm_ipnat = np;
422254401Scy		np->in_use++;
423254401Scy		hm->hm_osrcip6 = *src;
424254401Scy		hm->hm_odstip6 = *dst;
425254401Scy		hm->hm_nsrcip6 = *map;
426254401Scy		hm->hm_ndstip6.i6[0] = 0;
427254401Scy		hm->hm_ndstip6.i6[1] = 0;
428254401Scy		hm->hm_ndstip6.i6[2] = 0;
429254401Scy		hm->hm_ndstip6.i6[3] = 0;
430254401Scy		hm->hm_ref = 1;
431254401Scy		hm->hm_port = port;
432254401Scy		hm->hm_hv = hv;
433254401Scy		hm->hm_v = 6;
434254401Scy		softn->ipf_nat_stats.ns_hm_new++;
435254401Scy	} else {
436254401Scy		softn->ipf_nat_stats.ns_hm_newfail++;
437254401Scy	}
438254401Scy	return hm;
439254401Scy}
440254401Scy
441254401Scy
442254401Scy/* ------------------------------------------------------------------------ */
443254401Scy/* Function:    ipf_nat6_newmap                                             */
444254401Scy/* Returns:     int - -1 == error, 0 == success                             */
445254401Scy/* Parameters:  fin(I) - pointer to packet information                      */
446254401Scy/*              nat(I) - pointer to NAT entry                               */
447254401Scy/*              ni(I)  - pointer to structure with misc. information needed */
448254401Scy/*                       to create new NAT entry.                           */
449254401Scy/*                                                                          */
450254401Scy/* Given an empty NAT structure, populate it with new information about a   */
451254401Scy/* new NAT session, as defined by the matching NAT rule.                    */
452254401Scy/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
453254401Scy/* to the new IP address for the translation.                               */
454254401Scy/* ------------------------------------------------------------------------ */
455254401Scyint
456254401Scyipf_nat6_newmap(fin, nat, ni)
457254401Scy	fr_info_t *fin;
458254401Scy	nat_t *nat;
459254401Scy	natinfo_t *ni;
460254401Scy{
461254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
462254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
463254401Scy	u_short st_port, dport, sport, port, sp, dp;
464254401Scy	i6addr_t in, st_ip;
465254401Scy	hostmap_t *hm;
466254401Scy	u_32_t flags;
467254401Scy	ipnat_t *np;
468254401Scy	nat_t *natl;
469254401Scy	int l;
470254401Scy
471254401Scy	/*
472254401Scy	 * If it's an outbound packet which doesn't match any existing
473254401Scy	 * record, then create a new port
474254401Scy	 */
475254401Scy	l = 0;
476254401Scy	hm = NULL;
477254401Scy	np = ni->nai_np;
478254401Scy	st_ip = np->in_snip6;
479254401Scy	st_port = np->in_spnext;
480254401Scy	flags = nat->nat_flags;
481254401Scy
482254401Scy	if (flags & IPN_ICMPQUERY) {
483254401Scy		sport = fin->fin_data[1];
484254401Scy		dport = 0;
485254401Scy	} else {
486254401Scy		sport = htons(fin->fin_data[0]);
487254401Scy		dport = htons(fin->fin_data[1]);
488254401Scy	}
489254401Scy
490254401Scy	/*
491254401Scy	 * Do a loop until we either run out of entries to try or we find
492254401Scy	 * a NAT mapping that isn't currently being used.  This is done
493254401Scy	 * because the change to the source is not (usually) being fixed.
494254401Scy	 */
495254401Scy	do {
496254401Scy		port = 0;
497254401Scy		in = np->in_nsrc.na_nextaddr;
498254401Scy		if (l == 0) {
499254401Scy			/*
500254401Scy			 * Check to see if there is an existing NAT
501254401Scy			 * setup for this IP address pair.
502254401Scy			 */
503254401Scy			hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6,
504254401Scy					      &fin->fin_dst6, &in, 0);
505254401Scy			if (hm != NULL)
506254401Scy				in = hm->hm_nsrcip6;
507254401Scy		} else if ((l == 1) && (hm != NULL)) {
508254401Scy			ipf_nat_hostmapdel(softc, &hm);
509254401Scy		}
510254401Scy
511254401Scy		nat->nat_hm = hm;
512254401Scy
513254401Scy		if (IP6_ISONES(&np->in_nsrcmsk6) && (np->in_spnext == 0)) {
514254401Scy			if (l > 0) {
515254401Scy				NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_1);
516254401Scy				return -1;
517254401Scy			}
518254401Scy		}
519254401Scy
520254401Scy		if ((np->in_redir == NAT_BIMAP) &&
521254401Scy		    IP6_EQ(&np->in_osrcmsk6, &np->in_nsrcmsk6)) {
522254401Scy			i6addr_t temp;
523254401Scy			/*
524254401Scy			 * map the address block in a 1:1 fashion
525254401Scy			 */
526254401Scy			temp.i6[0] = fin->fin_src6.i6[0] &
527254401Scy				     ~np->in_osrcmsk6.i6[0];
528254401Scy			temp.i6[1] = fin->fin_src6.i6[1] &
529254401Scy				     ~np->in_osrcmsk6.i6[1];
530254401Scy			temp.i6[2] = fin->fin_src6.i6[2] &
531254401Scy				     ~np->in_osrcmsk6.i6[0];
532254401Scy			temp.i6[3] = fin->fin_src6.i6[3] &
533254401Scy				     ~np->in_osrcmsk6.i6[3];
534254401Scy			in = np->in_nsrcip6;
535254401Scy			IP6_MERGE(&in, &temp, &np->in_osrc);
536254401Scy
537254401Scy#ifdef NEED_128BIT_MATH
538254401Scy		} else if (np->in_redir & NAT_MAPBLK) {
539254401Scy			if ((l >= np->in_ppip) || ((l > 0) &&
540254401Scy			     !(flags & IPN_TCPUDP))) {
541254401Scy				NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_2);
542254401Scy				return -1;
543254401Scy			}
544254401Scy			/*
545254401Scy			 * map-block - Calculate destination address.
546254401Scy			 */
547254401Scy			IP6_MASK(&in, &fin->fin_src6, &np->in_osrcmsk6);
548254401Scy			in = ntohl(in);
549254401Scy			inb = in;
550254401Scy			in.s_addr /= np->in_ippip;
551254401Scy			in.s_addr &= ntohl(~np->in_nsrcmsk6);
552254401Scy			in.s_addr += ntohl(np->in_nsrcaddr6);
553254401Scy			/*
554254401Scy			 * Calculate destination port.
555254401Scy			 */
556254401Scy			if ((flags & IPN_TCPUDP) &&
557254401Scy			    (np->in_ppip != 0)) {
558254401Scy				port = ntohs(sport) + l;
559254401Scy				port %= np->in_ppip;
560254401Scy				port += np->in_ppip *
561254401Scy					(inb.s_addr % np->in_ippip);
562254401Scy				port += MAPBLK_MINPORT;
563254401Scy				port = htons(port);
564254401Scy			}
565254401Scy#endif
566254401Scy
567254401Scy		} else if (IP6_ISZERO(&np->in_nsrcaddr) &&
568254401Scy			   IP6_ISONES(&np->in_nsrcmsk)) {
569254401Scy			/*
570254401Scy			 * 0/32 - use the interface's IP address.
571254401Scy			 */
572254401Scy			if ((l > 0) ||
573254401Scy			    ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp,
574254401Scy				       &in, NULL) == -1) {
575254401Scy				NBUMPSIDE6DX(1, ns_new_ifpaddr,
576254401Scy					     ns_new_ifpaddr_1);
577254401Scy				return -1;
578254401Scy			}
579254401Scy
580254401Scy		} else if (IP6_ISZERO(&np->in_nsrcip6) &&
581254401Scy			   IP6_ISZERO(&np->in_nsrcmsk6)) {
582254401Scy			/*
583254401Scy			 * 0/0 - use the original source address/port.
584254401Scy			 */
585254401Scy			if (l > 0) {
586254401Scy				NBUMPSIDE6DX(1, ns_exhausted, ns_exhausted_3);
587254401Scy				return -1;
588254401Scy			}
589254401Scy			in = fin->fin_src6;
590254401Scy
591254401Scy		} else if (!IP6_ISONES(&np->in_nsrcmsk6) &&
592254401Scy			   (np->in_spnext == 0) && ((l > 0) || (hm == NULL))) {
593254401Scy			IP6_INC(&np->in_snip6);
594254401Scy		}
595254401Scy
596254401Scy		natl = NULL;
597254401Scy
598254401Scy		if ((flags & IPN_TCPUDP) &&
599254401Scy		    ((np->in_redir & NAT_MAPBLK) == 0) &&
600254401Scy		    (np->in_flags & IPN_AUTOPORTMAP)) {
601254401Scy#ifdef NEED_128BIT_MATH
602254401Scy			/*
603254401Scy			 * "ports auto" (without map-block)
604254401Scy			 */
605254401Scy			if ((l > 0) && (l % np->in_ppip == 0)) {
606254401Scy				if ((l > np->in_ppip) &&
607254401Scy				    !IP6_ISONES(&np->in_nsrcmsk)) {
608254401Scy					IP6_INC(&np->in_snip6)
609254401Scy				}
610254401Scy			}
611254401Scy			if (np->in_ppip != 0) {
612254401Scy				port = ntohs(sport);
613254401Scy				port += (l % np->in_ppip);
614254401Scy				port %= np->in_ppip;
615254401Scy				port += np->in_ppip *
616254401Scy					(ntohl(fin->fin_src6) %
617254401Scy					 np->in_ippip);
618254401Scy				port += MAPBLK_MINPORT;
619254401Scy				port = htons(port);
620254401Scy			}
621254401Scy#endif
622254401Scy
623254401Scy		} else if (((np->in_redir & NAT_MAPBLK) == 0) &&
624254401Scy			   (flags & IPN_TCPUDPICMP) && (np->in_spnext != 0)) {
625254401Scy                        /*
626254401Scy                         * Standard port translation.  Select next port.
627254401Scy                         */
628254401Scy                        if (np->in_flags & IPN_SEQUENTIAL) {
629254401Scy                                port = np->in_spnext;
630254401Scy                        } else {
631254401Scy				port = ipf_random() % (np->in_spmax -
632254401Scy						       np->in_spmin + 1);
633254401Scy                                port += np->in_spmin;
634254401Scy                        }
635254401Scy                        port = htons(port);
636254401Scy                        np->in_spnext++;
637254401Scy
638254401Scy			if (np->in_spnext > np->in_spmax) {
639254401Scy				np->in_spnext = np->in_spmin;
640254401Scy				if (!IP6_ISONES(&np->in_nsrcmsk6)) {
641254401Scy					IP6_INC(&np->in_snip6);
642254401Scy				}
643254401Scy			}
644254401Scy		}
645254401Scy
646254401Scy		if (np->in_flags & IPN_SIPRANGE) {
647254401Scy			if (IP6_GT(&np->in_snip, &np->in_nsrcmsk))
648254401Scy				np->in_snip6 = np->in_nsrcip6;
649254401Scy		} else {
650254401Scy			i6addr_t a1, a2;
651254401Scy
652254401Scy			a1 = np->in_snip6;
653254401Scy			IP6_INC(&a1);
654254401Scy			IP6_AND(&a1, &np->in_nsrcmsk6, &a2);
655254401Scy
656254401Scy			if (!IP6_ISONES(&np->in_nsrcmsk6) &&
657254401Scy			    IP6_GT(&a2, &np->in_nsrcip6)) {
658254401Scy				IP6_ADD(&np->in_nsrcip6, 1, &np->in_snip6);
659254401Scy			}
660254401Scy		}
661254401Scy
662254401Scy		if ((port == 0) && (flags & (IPN_TCPUDPICMP|IPN_ICMPQUERY)))
663254401Scy			port = sport;
664254401Scy
665254401Scy		/*
666254401Scy		 * Here we do a lookup of the connection as seen from
667254401Scy		 * the outside.  If an IP# pair already exists, try
668254401Scy		 * again.  So if you have A->B becomes C->B, you can
669254401Scy		 * also have D->E become C->E but not D->B causing
670254401Scy		 * another C->B.  Also take protocol and ports into
671254401Scy		 * account when determining whether a pre-existing
672254401Scy		 * NAT setup will cause an external conflict where
673254401Scy		 * this is appropriate.
674254401Scy		 */
675254401Scy		sp = fin->fin_data[0];
676254401Scy		dp = fin->fin_data[1];
677254401Scy		fin->fin_data[0] = fin->fin_data[1];
678254401Scy		fin->fin_data[1] = ntohs(port);
679254401Scy		natl = ipf_nat6_inlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
680254401Scy					 (u_int)fin->fin_p, &fin->fin_dst6.in6,
681254401Scy					 &in.in6);
682254401Scy		fin->fin_data[0] = sp;
683254401Scy		fin->fin_data[1] = dp;
684254401Scy
685254401Scy		/*
686254401Scy		 * Has the search wrapped around and come back to the
687254401Scy		 * start ?
688254401Scy		 */
689254401Scy		if ((natl != NULL) &&
690254401Scy		    (np->in_spnext != 0) && (st_port == np->in_spnext) &&
691254401Scy		    (!IP6_ISZERO(&np->in_snip6) &&
692254401Scy		     IP6_EQ(&st_ip, &np->in_snip6))) {
693254401Scy			NBUMPSIDE6D(1, ns_wrap);
694254401Scy			return -1;
695254401Scy		}
696254401Scy		l++;
697254401Scy	} while (natl != NULL);
698254401Scy
699254401Scy	/* Setup the NAT table */
700254401Scy	nat->nat_osrc6 = fin->fin_src6;
701254401Scy	nat->nat_nsrc6 = in;
702254401Scy	nat->nat_odst6 = fin->fin_dst6;
703254401Scy	nat->nat_ndst6 = fin->fin_dst6;
704254401Scy	if (nat->nat_hm == NULL)
705254401Scy		nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6,
706254401Scy					       &fin->fin_dst6,
707254401Scy					       &nat->nat_nsrc6, 0);
708254401Scy
709254401Scy	if (flags & IPN_TCPUDP) {
710254401Scy		nat->nat_osport = sport;
711254401Scy		nat->nat_nsport = port;	/* sport */
712254401Scy		nat->nat_odport = dport;
713254401Scy		nat->nat_ndport = dport;
714254401Scy		((tcphdr_t *)fin->fin_dp)->th_sport = port;
715254401Scy	} else if (flags & IPN_ICMPQUERY) {
716254401Scy		nat->nat_oicmpid = fin->fin_data[1];
717254401Scy		((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = port;
718254401Scy		nat->nat_nicmpid = port;
719254401Scy	}
720254401Scy	return 0;
721254401Scy}
722254401Scy
723254401Scy
724254401Scy/* ------------------------------------------------------------------------ */
725254401Scy/* Function:    ipf_nat6_newrdr                                             */
726254401Scy/* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
727254401Scy/*                    allow rule to be moved if IPN_ROUNDR is set.          */
728254401Scy/* Parameters:  fin(I) - pointer to packet information                      */
729254401Scy/*              nat(I) - pointer to NAT entry                               */
730254401Scy/*              ni(I)  - pointer to structure with misc. information needed */
731254401Scy/*                       to create new NAT entry.                           */
732254401Scy/*                                                                          */
733254401Scy/* ni.nai_ip is passed in uninitialised and must be set, in host byte order,*/
734254401Scy/* to the new IP address for the translation.                               */
735254401Scy/* ------------------------------------------------------------------------ */
736254401Scyint
737254401Scyipf_nat6_newrdr(fin, nat, ni)
738254401Scy	fr_info_t *fin;
739254401Scy	nat_t *nat;
740254401Scy	natinfo_t *ni;
741254401Scy{
742254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
743254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
744254401Scy	u_short nport, dport, sport;
745254401Scy	u_short sp, dp;
746254401Scy	hostmap_t *hm;
747254401Scy	u_32_t flags;
748254401Scy	i6addr_t in;
749254401Scy	ipnat_t *np;
750254401Scy	nat_t *natl;
751254401Scy	int move;
752254401Scy
753254401Scy	move = 1;
754254401Scy	hm = NULL;
755254401Scy	in.i6[0] = 0;
756254401Scy	in.i6[1] = 0;
757254401Scy	in.i6[2] = 0;
758254401Scy	in.i6[3] = 0;
759254401Scy	np = ni->nai_np;
760254401Scy	flags = nat->nat_flags;
761254401Scy
762254401Scy	if (flags & IPN_ICMPQUERY) {
763254401Scy		dport = fin->fin_data[1];
764254401Scy		sport = 0;
765254401Scy	} else {
766254401Scy		sport = htons(fin->fin_data[0]);
767254401Scy		dport = htons(fin->fin_data[1]);
768254401Scy	}
769254401Scy
770254401Scy	/* TRACE sport, dport */
771254401Scy
772254401Scy
773254401Scy	/*
774254401Scy	 * If the matching rule has IPN_STICKY set, then we want to have the
775254401Scy	 * same rule kick in as before.  Why would this happen?  If you have
776254401Scy	 * a collection of rdr rules with "round-robin sticky", the current
777254401Scy	 * packet might match a different one to the previous connection but
778254401Scy	 * we want the same destination to be used.
779254401Scy	 */
780254401Scy	if (((np->in_flags & (IPN_ROUNDR|IPN_SPLIT)) != 0) &&
781254401Scy	    ((np->in_flags & IPN_STICKY) != 0)) {
782254401Scy		hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6,
783254401Scy				      &fin->fin_dst6, &in, (u_32_t)dport);
784254401Scy		if (hm != NULL) {
785254401Scy			in = hm->hm_ndstip6;
786254401Scy			np = hm->hm_ipnat;
787254401Scy			ni->nai_np = np;
788254401Scy			move = 0;
789254401Scy		}
790254401Scy	}
791254401Scy
792254401Scy	/*
793254401Scy	 * Otherwise, it's an inbound packet. Most likely, we don't
794254401Scy	 * want to rewrite source ports and source addresses. Instead,
795254401Scy	 * we want to rewrite to a fixed internal address and fixed
796254401Scy	 * internal port.
797254401Scy	 */
798254401Scy	if (np->in_flags & IPN_SPLIT) {
799254401Scy		in = np->in_dnip6;
800254401Scy
801254401Scy		if ((np->in_flags & (IPN_ROUNDR|IPN_STICKY)) == IPN_STICKY) {
802254401Scy			hm = ipf_nat6_hostmap(softn, NULL, &fin->fin_src6,
803254401Scy					      &fin->fin_dst6, &in,
804254401Scy					      (u_32_t)dport);
805254401Scy			if (hm != NULL) {
806254401Scy				in = hm->hm_ndstip6;
807254401Scy				move = 0;
808254401Scy			}
809254401Scy		}
810254401Scy
811254401Scy		if (hm == NULL || hm->hm_ref == 1) {
812254401Scy			if (IP6_EQ(&np->in_ndstip6, &in)) {
813254401Scy				np->in_dnip6 = np->in_ndstmsk6;
814254401Scy				move = 0;
815254401Scy			} else {
816254401Scy				np->in_dnip6 = np->in_ndstip6;
817254401Scy			}
818254401Scy		}
819254401Scy
820254401Scy	} else if (IP6_ISZERO(&np->in_ndstaddr) &&
821254401Scy		   IP6_ISONES(&np->in_ndstmsk)) {
822254401Scy		/*
823254401Scy		 * 0/32 - use the interface's IP address.
824254401Scy		 */
825254401Scy		if (ipf_ifpaddr(softc, 6, FRI_NORMAL, fin->fin_ifp,
826254401Scy			       &in, NULL) == -1) {
827254401Scy			NBUMPSIDE6DX(0, ns_new_ifpaddr, ns_new_ifpaddr_2);
828254401Scy			return -1;
829254401Scy		}
830254401Scy
831254401Scy	} else if (IP6_ISZERO(&np->in_ndstip6) &&
832254401Scy		   IP6_ISZERO(&np->in_ndstmsk6)) {
833254401Scy		/*
834254401Scy		 * 0/0 - use the original destination address/port.
835254401Scy		 */
836254401Scy		in = fin->fin_dst6;
837254401Scy
838254401Scy	} else if (np->in_redir == NAT_BIMAP &&
839254401Scy		   IP6_EQ(&np->in_ndstmsk6, &np->in_odstmsk6)) {
840254401Scy		i6addr_t temp;
841254401Scy		/*
842254401Scy		 * map the address block in a 1:1 fashion
843254401Scy		 */
844254401Scy		temp.i6[0] = fin->fin_dst6.i6[0] & ~np->in_osrcmsk6.i6[0];
845254401Scy		temp.i6[1] = fin->fin_dst6.i6[1] & ~np->in_osrcmsk6.i6[1];
846254401Scy		temp.i6[2] = fin->fin_dst6.i6[2] & ~np->in_osrcmsk6.i6[0];
847254401Scy		temp.i6[3] = fin->fin_dst6.i6[3] & ~np->in_osrcmsk6.i6[3];
848254401Scy		in = np->in_ndstip6;
849254401Scy		IP6_MERGE(&in, &temp, &np->in_ndstmsk6);
850254401Scy	} else {
851254401Scy		in = np->in_ndstip6;
852254401Scy	}
853254401Scy
854254401Scy	if ((np->in_dpnext == 0) || ((flags & NAT_NOTRULEPORT) != 0))
855254401Scy		nport = dport;
856254401Scy	else {
857254401Scy		/*
858254401Scy		 * Whilst not optimized for the case where
859254401Scy		 * pmin == pmax, the gain is not significant.
860254401Scy		 */
861254401Scy		if (((np->in_flags & IPN_FIXEDDPORT) == 0) &&
862254401Scy		    (np->in_odport != np->in_dtop)) {
863254401Scy			nport = ntohs(dport) - np->in_odport + np->in_dpmax;
864254401Scy			nport = htons(nport);
865254401Scy		} else {
866254401Scy			nport = htons(np->in_dpnext);
867254401Scy			np->in_dpnext++;
868254401Scy			if (np->in_dpnext > np->in_dpmax)
869254401Scy				np->in_dpnext = np->in_dpmin;
870254401Scy		}
871254401Scy	}
872254401Scy
873254401Scy	/*
874254401Scy	 * When the redirect-to address is set to 0.0.0.0, just
875254401Scy	 * assume a blank `forwarding' of the packet.  We don't
876254401Scy	 * setup any translation for this either.
877254401Scy	 */
878254401Scy	if (IP6_ISZERO(&in)) {
879254401Scy		if (nport == dport) {
880254401Scy			NBUMPSIDE6D(0, ns_xlate_null);
881254401Scy			return -1;
882254401Scy		}
883254401Scy		in = fin->fin_dst6;
884254401Scy	}
885254401Scy
886254401Scy	/*
887254401Scy	 * Check to see if this redirect mapping already exists and if
888254401Scy	 * it does, return "failure" (allowing it to be created will just
889254401Scy	 * cause one or both of these "connections" to stop working.)
890254401Scy	 */
891254401Scy	sp = fin->fin_data[0];
892254401Scy	dp = fin->fin_data[1];
893254401Scy	fin->fin_data[1] = fin->fin_data[0];
894254401Scy	fin->fin_data[0] = ntohs(nport);
895254401Scy	natl = ipf_nat6_outlookup(fin, flags & ~(SI_WILDP|NAT_SEARCH),
896254401Scy				  (u_int)fin->fin_p, &in.in6,
897254401Scy				  &fin->fin_src6.in6);
898254401Scy	fin->fin_data[0] = sp;
899254401Scy	fin->fin_data[1] = dp;
900254401Scy	if (natl != NULL) {
901254401Scy		NBUMPSIDE6D(0, ns_xlate_exists);
902254401Scy		return -1;
903254401Scy	}
904254401Scy
905254401Scy	nat->nat_ndst6 = in;
906254401Scy	nat->nat_odst6 = fin->fin_dst6;
907254401Scy	nat->nat_nsrc6 = fin->fin_src6;
908254401Scy	nat->nat_osrc6 = fin->fin_src6;
909254401Scy	if ((nat->nat_hm == NULL) && ((np->in_flags & IPN_STICKY) != 0))
910254401Scy		nat->nat_hm = ipf_nat6_hostmap(softn, np, &fin->fin_src6,
911254401Scy					       &fin->fin_dst6, &in,
912254401Scy					       (u_32_t)dport);
913254401Scy
914254401Scy	if (flags & IPN_TCPUDP) {
915254401Scy		nat->nat_odport = dport;
916254401Scy		nat->nat_ndport = nport;
917254401Scy		nat->nat_osport = sport;
918254401Scy		nat->nat_nsport = sport;
919254401Scy		((tcphdr_t *)fin->fin_dp)->th_dport = nport;
920254401Scy	} else if (flags & IPN_ICMPQUERY) {
921254401Scy		nat->nat_oicmpid = fin->fin_data[1];
922254401Scy		((struct icmp6_hdr *)fin->fin_dp)->icmp6_id = nport;
923254401Scy		nat->nat_nicmpid = nport;
924254401Scy	}
925254401Scy
926254401Scy	return move;
927254401Scy}
928254401Scy
929254401Scy/* ------------------------------------------------------------------------ */
930254401Scy/* Function:    ipf_nat6_add                                                */
931254401Scy/* Returns:     nat6_t*      - NULL == failure to create new NAT structure, */
932254401Scy/*                             else pointer to new NAT structure            */
933254401Scy/* Parameters:  fin(I)       - pointer to packet information                */
934254401Scy/*              np(I)        - pointer to NAT rule                          */
935254401Scy/*              natsave(I)   - pointer to where to store NAT struct pointer */
936254401Scy/*              flags(I)     - flags describing the current packet          */
937254401Scy/*              direction(I) - direction of packet (in/out)                 */
938254401Scy/* Write Lock:  ipf_nat                                                     */
939254401Scy/*                                                                          */
940254401Scy/* Attempts to create a new NAT entry.  Does not actually change the packet */
941254401Scy/* in any way.                                                              */
942254401Scy/*                                                                          */
943254401Scy/* This fucntion is in three main parts: (1) deal with creating a new NAT   */
944254401Scy/* structure for a "MAP" rule (outgoing NAT translation); (2) deal with     */
945254401Scy/* creating a new NAT structure for a "RDR" rule (incoming NAT translation) */
946254401Scy/* and (3) building that structure and putting it into the NAT table(s).    */
947254401Scy/*                                                                          */
948254401Scy/* NOTE: natsave should NOT be used top point back to an ipstate_t struct   */
949254401Scy/*       as it can result in memory being corrupted.                        */
950254401Scy/* ------------------------------------------------------------------------ */
951254401Scynat_t *
952254401Scyipf_nat6_add(fin, np, natsave, flags, direction)
953254401Scy	fr_info_t *fin;
954254401Scy	ipnat_t *np;
955254401Scy	nat_t **natsave;
956254401Scy	u_int flags;
957254401Scy	int direction;
958254401Scy{
959254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
960254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
961254401Scy	hostmap_t *hm = NULL;
962254401Scy	nat_t *nat, *natl;
963254401Scy	natstat_t *nsp;
964254401Scy	u_int nflags;
965254401Scy	natinfo_t ni;
966254401Scy	int move;
967344833Scy#if SOLARIS && defined(_KERNEL) && defined(ICK_M_CTL_MAGIC)
968254401Scy	qpktinfo_t *qpi = fin->fin_qpi;
969254401Scy#endif
970254401Scy
971254401Scy	nsp = &softn->ipf_nat_stats;
972254401Scy
973254401Scy	if ((nsp->ns_active * 100 / softn->ipf_nat_table_max) >
974254401Scy	    softn->ipf_nat_table_wm_high) {
975254401Scy		softn->ipf_nat_doflush = 1;
976254401Scy	}
977254401Scy
978254401Scy	if (nsp->ns_active >= softn->ipf_nat_table_max) {
979254401Scy		NBUMPSIDE6(fin->fin_out, ns_table_max);
980254401Scy		return NULL;
981254401Scy	}
982254401Scy
983254401Scy	move = 1;
984254401Scy	nflags = np->in_flags & flags;
985254401Scy	nflags &= NAT_FROMRULE;
986254401Scy
987254401Scy	ni.nai_np = np;
988254401Scy	ni.nai_dport = 0;
989254401Scy	ni.nai_sport = 0;
990254401Scy
991254401Scy	/* Give me a new nat */
992254401Scy	KMALLOC(nat, nat_t *);
993254401Scy	if (nat == NULL) {
994254401Scy		NBUMPSIDE6(fin->fin_out, ns_memfail);
995254401Scy		/*
996254401Scy		 * Try to automatically tune the max # of entries in the
997254401Scy		 * table allowed to be less than what will cause kmem_alloc()
998254401Scy		 * to fail and try to eliminate panics due to out of memory
999254401Scy		 * conditions arising.
1000254401Scy		 */
1001254401Scy		if ((softn->ipf_nat_table_max > softn->ipf_nat_table_sz) &&
1002254401Scy		    (nsp->ns_active > 100)) {
1003254401Scy			softn->ipf_nat_table_max = nsp->ns_active - 100;
1004254401Scy			printf("table_max reduced to %d\n",
1005254401Scy				softn->ipf_nat_table_max);
1006254401Scy		}
1007254401Scy		return NULL;
1008254401Scy	}
1009254401Scy
1010254401Scy	if (flags & IPN_ICMPQUERY) {
1011254401Scy		/*
1012254401Scy		 * In the ICMP query NAT code, we translate the ICMP id fields
1013254401Scy		 * to make them unique. This is indepedent of the ICMP type
1014254401Scy		 * (e.g. in the unlikely event that a host sends an echo and
1015254401Scy		 * an tstamp request with the same id, both packets will have
1016254401Scy		 * their ip address/id field changed in the same way).
1017254401Scy		 */
1018254401Scy		/* The icmp6_id field is used by the sender to identify the
1019254401Scy		 * process making the icmp request. (the receiver justs
1020254401Scy		 * copies it back in its response). So, it closely matches
1021254401Scy		 * the concept of source port. We overlay sport, so we can
1022254401Scy		 * maximally reuse the existing code.
1023254401Scy		 */
1024254401Scy		ni.nai_sport = fin->fin_data[1];
1025254401Scy		ni.nai_dport = 0;
1026254401Scy	}
1027254401Scy
1028254401Scy	bzero((char *)nat, sizeof(*nat));
1029254401Scy	nat->nat_flags = flags;
1030254401Scy	nat->nat_redir = np->in_redir;
1031254401Scy	nat->nat_dir = direction;
1032254401Scy	nat->nat_pr[0] = fin->fin_p;
1033254401Scy	nat->nat_pr[1] = fin->fin_p;
1034254401Scy
1035254401Scy	/*
1036254401Scy	 * Search the current table for a match and create a new mapping
1037254401Scy	 * if there is none found.
1038254401Scy	 */
1039254401Scy	if (np->in_redir & NAT_DIVERTUDP) {
1040254401Scy		move = ipf_nat6_newdivert(fin, nat, &ni);
1041254401Scy
1042254401Scy	} else if (np->in_redir & NAT_REWRITE) {
1043254401Scy		move = ipf_nat6_newrewrite(fin, nat, &ni);
1044254401Scy
1045254401Scy	} else if (direction == NAT_OUTBOUND) {
1046254401Scy		/*
1047254401Scy		 * We can now arrange to call this for the same connection
1048254401Scy		 * because ipf_nat6_new doesn't protect the code path into
1049254401Scy		 * this function.
1050254401Scy		 */
1051254401Scy		natl = ipf_nat6_outlookup(fin, nflags, (u_int)fin->fin_p,
1052254401Scy					  &fin->fin_src6.in6,
1053254401Scy					  &fin->fin_dst6.in6);
1054254401Scy		if (natl != NULL) {
1055254401Scy			KFREE(nat);
1056254401Scy			nat = natl;
1057254401Scy			goto done;
1058254401Scy		}
1059254401Scy
1060254401Scy		move = ipf_nat6_newmap(fin, nat, &ni);
1061254401Scy	} else {
1062254401Scy		/*
1063254401Scy		 * NAT_INBOUND is used for redirects rules
1064254401Scy		 */
1065254401Scy		natl = ipf_nat6_inlookup(fin, nflags, (u_int)fin->fin_p,
1066254401Scy					 &fin->fin_src6.in6,
1067254401Scy					 &fin->fin_dst6.in6);
1068254401Scy		if (natl != NULL) {
1069254401Scy			KFREE(nat);
1070254401Scy			nat = natl;
1071254401Scy			goto done;
1072254401Scy		}
1073254401Scy
1074254401Scy		move = ipf_nat6_newrdr(fin, nat, &ni);
1075254401Scy	}
1076254401Scy	if (move == -1)
1077254401Scy		goto badnat;
1078254401Scy
1079254401Scy	np = ni.nai_np;
1080254401Scy
1081254401Scy	nat->nat_mssclamp = np->in_mssclamp;
1082254401Scy	nat->nat_me = natsave;
1083254401Scy	nat->nat_fr = fin->fin_fr;
1084254401Scy	nat->nat_rev = fin->fin_rev;
1085254401Scy	nat->nat_ptr = np;
1086254401Scy	nat->nat_dlocal = np->in_dlocal;
1087254401Scy
1088254401Scy	if ((np->in_apr != NULL) && ((nat->nat_flags & NAT_SLAVE) == 0)) {
1089254401Scy		if (ipf_proxy_new(fin, nat) == -1) {
1090254401Scy			NBUMPSIDE6D(fin->fin_out, ns_appr_fail);
1091254401Scy			goto badnat;
1092254401Scy		}
1093254401Scy	}
1094254401Scy
1095254401Scy	nat->nat_ifps[0] = np->in_ifps[0];
1096254401Scy	if (np->in_ifps[0] != NULL) {
1097254401Scy		COPYIFNAME(np->in_v[0], np->in_ifps[0], nat->nat_ifnames[0]);
1098254401Scy	}
1099254401Scy
1100254401Scy	nat->nat_ifps[1] = np->in_ifps[1];
1101254401Scy	if (np->in_ifps[1] != NULL) {
1102254401Scy		COPYIFNAME(np->in_v[1], np->in_ifps[1], nat->nat_ifnames[1]);
1103254401Scy	}
1104254401Scy
1105254401Scy	if (ipf_nat6_finalise(fin, nat) == -1) {
1106254401Scy		goto badnat;
1107254401Scy	}
1108254401Scy
1109254401Scy	np->in_use++;
1110254401Scy
1111254401Scy	if ((move == 1) && (np->in_flags & IPN_ROUNDR)) {
1112254401Scy		if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_REDIRECT) {
1113254401Scy			ipf_nat6_delrdr(softn, np);
1114254401Scy			ipf_nat6_addrdr(softn, np);
1115254401Scy		} else if ((np->in_redir & (NAT_REDIRECT|NAT_MAP)) == NAT_MAP) {
1116254401Scy			ipf_nat6_delmap(softn, np);
1117254401Scy			ipf_nat6_addmap(softn, np);
1118254401Scy		}
1119254401Scy	}
1120254401Scy
1121254401Scy	if (flags & SI_WILDP)
1122254401Scy		nsp->ns_wilds++;
1123254401Scy	softn->ipf_nat_stats.ns_proto[nat->nat_pr[0]]++;
1124254401Scy
1125254401Scy	goto done;
1126254401Scybadnat:
1127254401Scy	NBUMPSIDE6(fin->fin_out, ns_badnatnew);
1128254401Scy	if ((hm = nat->nat_hm) != NULL)
1129254401Scy		ipf_nat_hostmapdel(softc, &hm);
1130254401Scy	KFREE(nat);
1131254401Scy	nat = NULL;
1132254401Scydone:
1133254401Scy	if (nat != NULL && np != NULL)
1134254401Scy		np->in_hits++;
1135254401Scy	if (natsave != NULL)
1136254401Scy		*natsave = nat;
1137254401Scy	return nat;
1138254401Scy}
1139254401Scy
1140254401Scy
1141254401Scy/* ------------------------------------------------------------------------ */
1142254401Scy/* Function:    ipf_nat6_finalise                                           */
1143254401Scy/* Returns:     int - 0 == sucess, -1 == failure                            */
1144254401Scy/* Parameters:  fin(I) - pointer to packet information                      */
1145254401Scy/*              nat(I) - pointer to NAT entry                               */
1146254401Scy/* Write Lock:  ipf_nat                                                     */
1147254401Scy/*                                                                          */
1148254401Scy/* This is the tail end of constructing a new NAT entry and is the same     */
1149254401Scy/* for both IPv4 and IPv6.                                                  */
1150254401Scy/* ------------------------------------------------------------------------ */
1151254401Scy/*ARGSUSED*/
1152254401Scyint
1153254401Scyipf_nat6_finalise(fin, nat)
1154254401Scy	fr_info_t *fin;
1155254401Scy	nat_t *nat;
1156254401Scy{
1157254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
1158254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1159254401Scy	u_32_t sum1, sum2, sumd;
1160254401Scy	frentry_t *fr;
1161254401Scy	u_32_t flags;
1162254401Scy
1163254401Scy	flags = nat->nat_flags;
1164254401Scy
1165254401Scy	switch (fin->fin_p)
1166254401Scy	{
1167254401Scy	case IPPROTO_ICMPV6 :
1168254401Scy		sum1 = LONG_SUM6(&nat->nat_osrc6);
1169254401Scy		sum1 += ntohs(nat->nat_oicmpid);
1170254401Scy		sum2 = LONG_SUM6(&nat->nat_nsrc6);
1171254401Scy		sum2 += ntohs(nat->nat_nicmpid);
1172254401Scy		CALC_SUMD(sum1, sum2, sumd);
1173254401Scy		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
1174254401Scy
1175254401Scy		sum1 = LONG_SUM6(&nat->nat_odst6);
1176254401Scy		sum2 = LONG_SUM6(&nat->nat_ndst6);
1177254401Scy		CALC_SUMD(sum1, sum2, sumd);
1178254401Scy		nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16);
1179254401Scy		break;
1180254401Scy
1181254401Scy	case IPPROTO_TCP :
1182254401Scy	case IPPROTO_UDP :
1183254401Scy		sum1 = LONG_SUM6(&nat->nat_osrc6);
1184254401Scy		sum1 += ntohs(nat->nat_osport);
1185254401Scy		sum2 = LONG_SUM6(&nat->nat_nsrc6);
1186254401Scy		sum2 += ntohs(nat->nat_nsport);
1187254401Scy		CALC_SUMD(sum1, sum2, sumd);
1188254401Scy		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
1189254401Scy
1190254401Scy		sum1 = LONG_SUM6(&nat->nat_odst6);
1191254401Scy		sum1 += ntohs(nat->nat_odport);
1192254401Scy		sum2 = LONG_SUM6(&nat->nat_ndst6);
1193254401Scy		sum2 += ntohs(nat->nat_ndport);
1194254401Scy		CALC_SUMD(sum1, sum2, sumd);
1195254401Scy		nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16);
1196254401Scy		break;
1197254401Scy
1198254401Scy	default :
1199254401Scy		sum1 = LONG_SUM6(&nat->nat_osrc6);
1200254401Scy		sum2 = LONG_SUM6(&nat->nat_nsrc6);
1201254401Scy		CALC_SUMD(sum1, sum2, sumd);
1202254401Scy		nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
1203254401Scy
1204254401Scy		sum1 = LONG_SUM6(&nat->nat_odst6);
1205254401Scy		sum2 = LONG_SUM6(&nat->nat_ndst6);
1206254401Scy		CALC_SUMD(sum1, sum2, sumd);
1207254401Scy		nat->nat_sumd[0] += (sumd & 0xffff) + (sumd >> 16);
1208254401Scy		break;
1209254401Scy	}
1210254401Scy
1211254401Scy	/*
1212254401Scy	 * Compute the partial checksum, just in case.
1213254401Scy	 * This is only ever placed into outbound packets so care needs
1214254401Scy	 * to be taken over which pair of addresses are used.
1215254401Scy	 */
1216254401Scy	if (nat->nat_dir == NAT_OUTBOUND) {
1217254401Scy		sum1 = LONG_SUM6(&nat->nat_nsrc6);
1218254401Scy		sum1 += LONG_SUM6(&nat->nat_ndst6);
1219254401Scy	} else {
1220254401Scy		sum1 = LONG_SUM6(&nat->nat_osrc6);
1221254401Scy		sum1 += LONG_SUM6(&nat->nat_odst6);
1222254401Scy	}
1223254401Scy	sum1 += nat->nat_pr[1];
1224254401Scy	nat->nat_sumd[1] = (sum1 & 0xffff) + (sum1 >> 16);
1225254401Scy
1226254401Scy	if ((nat->nat_flags & SI_CLONE) == 0)
1227254401Scy		nat->nat_sync = ipf_sync_new(softc, SMC_NAT, fin, nat);
1228254401Scy
1229254401Scy	if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
1230254401Scy		nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]);
1231254401Scy	}
1232254401Scy
1233254401Scy	if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
1234254401Scy		nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]);
1235254401Scy	}
1236254401Scy
1237254401Scy	nat->nat_v[0] = 6;
1238254401Scy	nat->nat_v[1] = 6;
1239254401Scy
1240254401Scy	if (ipf_nat6_insert(softc, softn, nat) == 0) {
1241254401Scy		if (softn->ipf_nat_logging)
1242254401Scy			ipf_nat_log(softc, softn, nat, NL_NEW);
1243254401Scy		fr = nat->nat_fr;
1244254401Scy		if (fr != NULL) {
1245254401Scy			MUTEX_ENTER(&fr->fr_lock);
1246254401Scy			fr->fr_ref++;
1247254401Scy			MUTEX_EXIT(&fr->fr_lock);
1248254401Scy		}
1249254401Scy		return 0;
1250254401Scy	}
1251254401Scy
1252254401Scy	NBUMPSIDE6D(fin->fin_out, ns_unfinalised);
1253254401Scy	/*
1254254401Scy	 * nat6_insert failed, so cleanup time...
1255254401Scy	 */
1256254401Scy	if (nat->nat_sync != NULL)
1257254401Scy		ipf_sync_del_nat(softc->ipf_sync_soft, nat->nat_sync);
1258254401Scy	return -1;
1259254401Scy}
1260254401Scy
1261254401Scy
1262254401Scy/* ------------------------------------------------------------------------ */
1263254401Scy/* Function:    ipf_nat6_insert                                             */
1264254401Scy/* Returns:     int - 0 == sucess, -1 == failure                            */
1265254401Scy/* Parameters:  softc(I) - pointer to soft context main structure           */
1266254401Scy/*              softn(I) - pointer to NAT context structure                 */
1267254401Scy/*              nat(I) - pointer to NAT structure                           */
1268254401Scy/* Write Lock:  ipf_nat                                                     */
1269254401Scy/*                                                                          */
1270254401Scy/* Insert a NAT entry into the hash tables for searching and add it to the  */
1271254401Scy/* list of active NAT entries.  Adjust global counters when complete.       */
1272254401Scy/* ------------------------------------------------------------------------ */
1273254401Scystatic int
1274254401Scyipf_nat6_insert(softc, softn, nat)
1275254401Scy	ipf_main_softc_t *softc;
1276254401Scy	ipf_nat_softc_t *softn;
1277254401Scy	nat_t *nat;
1278254401Scy{
1279254401Scy	u_int hv1, hv2;
1280254401Scy	u_32_t sp, dp;
1281254401Scy	ipnat_t *in;
1282254401Scy
1283254401Scy	/*
1284254401Scy	 * Try and return an error as early as possible, so calculate the hash
1285254401Scy	 * entry numbers first and then proceed.
1286254401Scy	 */
1287254401Scy	if ((nat->nat_flags & (SI_W_SPORT|SI_W_DPORT)) == 0) {
1288254401Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
1289254401Scy			sp = nat->nat_osport;
1290254401Scy			dp = nat->nat_odport;
1291254401Scy		} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
1292254401Scy			sp = 0;
1293254401Scy			dp = nat->nat_oicmpid;
1294254401Scy		} else {
1295254401Scy			sp = 0;
1296254401Scy			dp = 0;
1297254401Scy		}
1298254401Scy		hv1 = NAT_HASH_FN6(&nat->nat_osrc6, sp, 0xffffffff);
1299254401Scy		hv1 = NAT_HASH_FN6(&nat->nat_odst6, hv1 + dp,
1300254401Scy				   softn->ipf_nat_table_sz);
1301254401Scy
1302254401Scy		/*
1303254401Scy		 * TRACE nat6_osrc6, nat6_osport, nat6_odst6,
1304254401Scy		 * nat6_odport, hv1
1305254401Scy		 */
1306254401Scy
1307254401Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
1308254401Scy			sp = nat->nat_nsport;
1309254401Scy			dp = nat->nat_ndport;
1310254401Scy		} else if ((nat->nat_flags & IPN_ICMPQUERY) != 0) {
1311254401Scy			sp = 0;
1312254401Scy			dp = nat->nat_nicmpid;
1313254401Scy		} else {
1314254401Scy			sp = 0;
1315254401Scy			dp = 0;
1316254401Scy		}
1317254401Scy		hv2 = NAT_HASH_FN6(&nat->nat_nsrc6, sp, 0xffffffff);
1318254401Scy		hv2 = NAT_HASH_FN6(&nat->nat_ndst6, hv2 + dp,
1319254401Scy				   softn->ipf_nat_table_sz);
1320254401Scy		/*
1321254401Scy		 * TRACE nat6_nsrcaddr, nat6_nsport, nat6_ndstaddr,
1322254401Scy		 * nat6_ndport, hv1
1323254401Scy		 */
1324254401Scy	} else {
1325254401Scy		hv1 = NAT_HASH_FN6(&nat->nat_osrc6, 0, 0xffffffff);
1326254401Scy		hv1 = NAT_HASH_FN6(&nat->nat_odst6, hv1,
1327254401Scy				   softn->ipf_nat_table_sz);
1328254401Scy		/* TRACE nat6_osrcip6, nat6_odstip6, hv1 */
1329254401Scy
1330254401Scy		hv2 = NAT_HASH_FN6(&nat->nat_nsrc6, 0, 0xffffffff);
1331254401Scy		hv2 = NAT_HASH_FN6(&nat->nat_ndst6, hv2,
1332254401Scy				   softn->ipf_nat_table_sz);
1333254401Scy		/* TRACE nat6_nsrcip6, nat6_ndstip6, hv2 */
1334254401Scy	}
1335254401Scy
1336254401Scy	nat->nat_hv[0] = hv1;
1337254401Scy	nat->nat_hv[1] = hv2;
1338254401Scy
1339254401Scy	MUTEX_INIT(&nat->nat_lock, "nat entry lock");
1340254401Scy
1341254401Scy	in = nat->nat_ptr;
1342254401Scy	nat->nat_ref = nat->nat_me ? 2 : 1;
1343254401Scy
1344254401Scy	nat->nat_ifnames[0][LIFNAMSIZ - 1] = '\0';
1345254401Scy	nat->nat_ifps[0] = ipf_resolvenic(softc, nat->nat_ifnames[0],
1346254401Scy					  nat->nat_v[0]);
1347254401Scy
1348254401Scy	if (nat->nat_ifnames[1][0] != '\0') {
1349254401Scy		nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
1350254401Scy		nat->nat_ifps[1] = ipf_resolvenic(softc, nat->nat_ifnames[1],
1351254401Scy						  nat->nat_v[1]);
1352254401Scy	} else if (in->in_ifnames[1] != -1) {
1353254401Scy		char *name;
1354254401Scy
1355254401Scy		name = in->in_names + in->in_ifnames[1];
1356254401Scy		if (name[1] != '\0' && name[0] != '-' && name[0] != '*') {
1357254401Scy			(void) strncpy(nat->nat_ifnames[1],
1358254401Scy				       nat->nat_ifnames[0], LIFNAMSIZ);
1359254401Scy			nat->nat_ifnames[1][LIFNAMSIZ - 1] = '\0';
1360254401Scy			nat->nat_ifps[1] = nat->nat_ifps[0];
1361254401Scy		}
1362254401Scy	}
1363254401Scy	if ((nat->nat_ifps[0] != NULL) && (nat->nat_ifps[0] != (void *)-1)) {
1364254401Scy		nat->nat_mtu[0] = GETIFMTU_6(nat->nat_ifps[0]);
1365254401Scy	}
1366254401Scy	if ((nat->nat_ifps[1] != NULL) && (nat->nat_ifps[1] != (void *)-1)) {
1367254401Scy		nat->nat_mtu[1] = GETIFMTU_6(nat->nat_ifps[1]);
1368254401Scy	}
1369254401Scy
1370254401Scy	return ipf_nat_hashtab_add(softc, softn, nat);
1371254401Scy}
1372254401Scy
1373254401Scy
1374254401Scy/* ------------------------------------------------------------------------ */
1375254401Scy/* Function:    ipf_nat6_icmperrorlookup                                    */
1376254401Scy/* Returns:     nat6_t* - point to matching NAT structure                    */
1377254401Scy/* Parameters:  fin(I) - pointer to packet information                      */
1378254401Scy/*              dir(I) - direction of packet (in/out)                       */
1379254401Scy/*                                                                          */
1380254401Scy/* Check if the ICMP error message is related to an existing TCP, UDP or    */
1381254401Scy/* ICMP query nat entry.  It is assumed that the packet is already of the   */
1382254401Scy/* the required length.                                                     */
1383254401Scy/* ------------------------------------------------------------------------ */
1384254401Scynat_t *
1385254401Scyipf_nat6_icmperrorlookup(fin, dir)
1386254401Scy	fr_info_t *fin;
1387254401Scy	int dir;
1388254401Scy{
1389254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
1390254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1391254401Scy	struct icmp6_hdr *icmp6, *orgicmp;
1392254401Scy	int flags = 0, type, minlen;
1393254401Scy	nat_stat_side_t *nside;
1394254401Scy	tcphdr_t *tcp = NULL;
1395254401Scy	u_short data[2];
1396254401Scy	ip6_t *oip6;
1397254401Scy	nat_t *nat;
1398254401Scy	u_int p;
1399254401Scy
1400254401Scy	minlen = 40;
1401254401Scy	icmp6 = fin->fin_dp;
1402254401Scy	type = icmp6->icmp6_type;
1403254401Scy	nside = &softn->ipf_nat_stats.ns_side6[fin->fin_out];
1404254401Scy	/*
1405254401Scy	 * Does it at least have the return (basic) IP header ?
1406254401Scy	 * Only a basic IP header (no options) should be with an ICMP error
1407254401Scy	 * header.  Also, if it's not an error type, then return.
1408254401Scy	 */
1409254401Scy	if (!(fin->fin_flx & FI_ICMPERR)) {
1410254401Scy		ATOMIC_INCL(nside->ns_icmp_basic);
1411254401Scy		return NULL;
1412254401Scy	}
1413254401Scy
1414254401Scy	/*
1415254401Scy	 * Check packet size
1416254401Scy	 */
1417254401Scy	if (fin->fin_plen < ICMP6ERR_IPICMPHLEN) {
1418254401Scy		ATOMIC_INCL(nside->ns_icmp_size);
1419254401Scy		return NULL;
1420254401Scy	}
1421254401Scy	oip6 = (ip6_t *)((char *)fin->fin_dp + 8);
1422254401Scy
1423254401Scy	/*
1424254401Scy	 * Is the buffer big enough for all of it ?  It's the size of the IP
1425254401Scy	 * header claimed in the encapsulated part which is of concern.  It
1426254401Scy	 * may be too big to be in this buffer but not so big that it's
1427254401Scy	 * outside the ICMP packet, leading to TCP deref's causing problems.
1428254401Scy	 * This is possible because we don't know how big oip_hl is when we
1429254401Scy	 * do the pullup early in ipf_check() and thus can't gaurantee it is
1430254401Scy	 * all here now.
1431254401Scy	 */
1432255332Scy#ifdef  _KERNEL
1433254401Scy	{
1434254401Scy	mb_t *m;
1435254401Scy
1436254401Scy	m = fin->fin_m;
1437369272Scy# if SOLARIS
1438254401Scy	if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN >
1439254401Scy	    (char *)m->b_wptr) {
1440254401Scy		ATOMIC_INCL(nside->ns_icmp_mbuf);
1441254401Scy		return NULL;
1442254401Scy	}
1443254401Scy# else
1444254401Scy	if ((char *)oip6 + fin->fin_dlen - ICMPERR_ICMPHLEN >
1445254401Scy	    (char *)fin->fin_ip + M_LEN(m)) {
1446254401Scy		ATOMIC_INCL(nside->ns_icmp_mbuf);
1447254401Scy		return NULL;
1448254401Scy	}
1449254401Scy# endif
1450254401Scy	}
1451254401Scy#endif
1452254401Scy
1453254401Scy	if (IP6_NEQ(&fin->fin_dst6, &oip6->ip6_src)) {
1454254401Scy		ATOMIC_INCL(nside->ns_icmp_address);
1455254401Scy		return NULL;
1456254401Scy	}
1457254401Scy
1458254401Scy	p = oip6->ip6_nxt;
1459254401Scy	if (p == IPPROTO_TCP)
1460254401Scy		flags = IPN_TCP;
1461254401Scy	else if (p == IPPROTO_UDP)
1462254401Scy		flags = IPN_UDP;
1463254401Scy	else if (p == IPPROTO_ICMPV6) {
1464254401Scy		orgicmp = (struct icmp6_hdr *)(oip6 + 1);
1465254401Scy
1466254401Scy		/* see if this is related to an ICMP query */
1467254401Scy		if (ipf_nat6_icmpquerytype(orgicmp->icmp6_type)) {
1468254401Scy			data[0] = fin->fin_data[0];
1469254401Scy			data[1] = fin->fin_data[1];
1470254401Scy			fin->fin_data[0] = 0;
1471254401Scy			fin->fin_data[1] = orgicmp->icmp6_id;
1472254401Scy
1473254401Scy			flags = IPN_ICMPERR|IPN_ICMPQUERY;
1474254401Scy			/*
1475254401Scy			 * NOTE : dir refers to the direction of the original
1476254401Scy			 *        ip packet. By definition the icmp error
1477254401Scy			 *        message flows in the opposite direction.
1478254401Scy			 */
1479254401Scy			if (dir == NAT_INBOUND)
1480254401Scy				nat = ipf_nat6_inlookup(fin, flags, p,
1481254401Scy						        &oip6->ip6_dst,
1482254401Scy						        &oip6->ip6_src);
1483254401Scy			else
1484254401Scy				nat = ipf_nat6_outlookup(fin, flags, p,
1485254401Scy							 &oip6->ip6_dst,
1486254401Scy							 &oip6->ip6_src);
1487254401Scy			fin->fin_data[0] = data[0];
1488254401Scy			fin->fin_data[1] = data[1];
1489254401Scy			return nat;
1490254401Scy		}
1491254401Scy	}
1492254401Scy
1493254401Scy	if (flags & IPN_TCPUDP) {
1494254401Scy		minlen += 8;		/* + 64bits of data to get ports */
1495254401Scy		/* TRACE (fin,minlen) */
1496254401Scy		if (fin->fin_plen < ICMPERR_IPICMPHLEN + minlen) {
1497254401Scy			ATOMIC_INCL(nside->ns_icmp_short);
1498254401Scy			return NULL;
1499254401Scy		}
1500254401Scy
1501254401Scy		data[0] = fin->fin_data[0];
1502254401Scy		data[1] = fin->fin_data[1];
1503254401Scy		tcp = (tcphdr_t *)(oip6 + 1);
1504254401Scy		fin->fin_data[0] = ntohs(tcp->th_dport);
1505254401Scy		fin->fin_data[1] = ntohs(tcp->th_sport);
1506254401Scy
1507254401Scy		if (dir == NAT_INBOUND) {
1508254401Scy			nat = ipf_nat6_inlookup(fin, flags, p, &oip6->ip6_dst,
1509254401Scy						&oip6->ip6_src);
1510254401Scy		} else {
1511254401Scy			nat = ipf_nat6_outlookup(fin, flags, p, &oip6->ip6_dst,
1512254401Scy						 &oip6->ip6_src);
1513254401Scy		}
1514254401Scy		fin->fin_data[0] = data[0];
1515254401Scy		fin->fin_data[1] = data[1];
1516254401Scy		return nat;
1517254401Scy	}
1518254401Scy	if (dir == NAT_INBOUND)
1519254401Scy		nat = ipf_nat6_inlookup(fin, 0, p, &oip6->ip6_dst,
1520254401Scy					&oip6->ip6_src);
1521254401Scy	else
1522254401Scy		nat = ipf_nat6_outlookup(fin, 0, p, &oip6->ip6_dst,
1523254401Scy					 &oip6->ip6_src);
1524254401Scy
1525254401Scy	return nat;
1526254401Scy}
1527254401Scy
1528254401Scy
1529254401Scy/* result = ip1 - ip2 */
1530254401Scyu_32_t
1531254401Scyipf_nat6_ip6subtract(ip1, ip2)
1532254401Scy	i6addr_t *ip1, *ip2;
1533254401Scy{
1534254401Scy	i6addr_t l1, l2, d;
1535254401Scy	u_short *s1, *s2, *ds;
1536254401Scy	u_32_t r;
1537254401Scy	int i, neg;
1538254401Scy
1539254401Scy	neg = 0;
1540254401Scy	l1 = *ip1;
1541254401Scy	l2 = *ip2;
1542254401Scy	s1 = (u_short *)&l1;
1543254401Scy	s2 = (u_short *)&l2;
1544254401Scy	ds = (u_short *)&d;
1545254401Scy
1546254401Scy	for (i = 7; i > 0; i--) {
1547254401Scy		if (s1[i] > s2[i]) {
1548254401Scy			ds[i] = s2[i] + 0x10000 - s1[i];
1549254401Scy			s2[i - 1] += 0x10000;
1550254401Scy		} else {
1551254401Scy			ds[i] = s2[i] - s1[i];
1552254401Scy		}
1553254401Scy	}
1554254401Scy	if (s2[0] > s1[0]) {
1555254401Scy		ds[0] = s2[0] + 0x10000 - s1[0];
1556254401Scy		neg = 1;
1557254401Scy	} else {
1558254401Scy		ds[0] = s2[0] - s1[0];
1559254401Scy	}
1560254401Scy
1561254401Scy	for (i = 0, r = 0; i < 8; i++) {
1562254401Scy		r += ds[i];
1563254401Scy	}
1564254401Scy
1565254401Scy	return r;
1566254401Scy}
1567254401Scy
1568254401Scy
1569254401Scy/* ------------------------------------------------------------------------ */
1570254401Scy/* Function:    ipf_nat6_icmperror                                          */
1571254401Scy/* Returns:     nat6_t* - point to matching NAT structure                    */
1572254401Scy/* Parameters:  fin(I)    - pointer to packet information                   */
1573254401Scy/*              nflags(I) - NAT flags for this packet                       */
1574254401Scy/*              dir(I)    - direction of packet (in/out)                    */
1575254401Scy/*                                                                          */
1576254401Scy/* Fix up an ICMP packet which is an error message for an existing NAT      */
1577254401Scy/* session.  This will correct both packet header data and checksums.       */
1578254401Scy/*                                                                          */
1579254401Scy/* This should *ONLY* be used for incoming ICMP error packets to make sure  */
1580254401Scy/* a NAT'd ICMP packet gets correctly recognised.                           */
1581254401Scy/* ------------------------------------------------------------------------ */
1582254401Scynat_t *
1583254401Scyipf_nat6_icmperror(fin, nflags, dir)
1584254401Scy	fr_info_t *fin;
1585254401Scy	u_int *nflags;
1586254401Scy	int dir;
1587254401Scy{
1588254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
1589254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1590254401Scy	u_32_t sum1, sum2, sumd, sumd2;
1591254401Scy	i6addr_t a1, a2, a3, a4;
1592254401Scy	struct icmp6_hdr *icmp6;
1593254401Scy	int flags, dlen, odst;
1594254401Scy	u_short *csump;
1595254401Scy	tcphdr_t *tcp;
1596254401Scy	ip6_t *oip6;
1597254401Scy	nat_t *nat;
1598254401Scy	void *dp;
1599254401Scy
1600254401Scy	if ((fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
1601254401Scy		NBUMPSIDE6D(fin->fin_out, ns_icmp_short);
1602254401Scy		return NULL;
1603254401Scy	}
1604254401Scy
1605254401Scy	/*
1606254401Scy	 * ipf_nat6_icmperrorlookup() will return NULL for `defective' packets.
1607254401Scy	 */
1608254401Scy	if ((fin->fin_v != 6) || !(nat = ipf_nat6_icmperrorlookup(fin, dir))) {
1609254401Scy		NBUMPSIDE6D(fin->fin_out, ns_icmp_notfound);
1610254401Scy		return NULL;
1611254401Scy	}
1612254401Scy
1613254401Scy	tcp = NULL;
1614254401Scy	csump = NULL;
1615254401Scy	flags = 0;
1616254401Scy	sumd2 = 0;
1617254401Scy	*nflags = IPN_ICMPERR;
1618254401Scy	icmp6 = fin->fin_dp;
1619254401Scy	oip6 = (ip6_t *)((u_char *)icmp6 + sizeof(*icmp6));
1620254401Scy	dp = (u_char *)oip6 + sizeof(*oip6);
1621254401Scy	if (oip6->ip6_nxt == IPPROTO_TCP) {
1622254401Scy		tcp = (tcphdr_t *)dp;
1623254401Scy		csump = (u_short *)&tcp->th_sum;
1624254401Scy		flags = IPN_TCP;
1625254401Scy	} else if (oip6->ip6_nxt == IPPROTO_UDP) {
1626254401Scy		udphdr_t *udp;
1627254401Scy
1628254401Scy		udp = (udphdr_t *)dp;
1629254401Scy		tcp = (tcphdr_t *)dp;
1630254401Scy		csump = (u_short *)&udp->uh_sum;
1631254401Scy		flags = IPN_UDP;
1632254401Scy	} else if (oip6->ip6_nxt == IPPROTO_ICMPV6)
1633254401Scy		flags = IPN_ICMPQUERY;
1634254401Scy	dlen = fin->fin_plen - ((char *)dp - (char *)fin->fin_ip);
1635254401Scy
1636254401Scy	/*
1637254401Scy	 * Need to adjust ICMP header to include the real IP#'s and
1638254401Scy	 * port #'s.  Only apply a checksum change relative to the
1639254401Scy	 * IP address change as it will be modified again in ipf_nat6_checkout
1640254401Scy	 * for both address and port.  Two checksum changes are
1641254401Scy	 * necessary for the two header address changes.  Be careful
1642254401Scy	 * to only modify the checksum once for the port # and twice
1643254401Scy	 * for the IP#.
1644254401Scy	 */
1645254401Scy
1646254401Scy	/*
1647254401Scy	 * Step 1
1648254401Scy	 * Fix the IP addresses in the offending IP packet. You also need
1649254401Scy	 * to adjust the IP header checksum of that offending IP packet.
1650254401Scy	 *
1651254401Scy	 * Normally, you would expect that the ICMP checksum of the
1652254401Scy	 * ICMP error message needs to be adjusted as well for the
1653254401Scy	 * IP address change in oip.
1654254401Scy	 * However, this is a NOP, because the ICMP checksum is
1655254401Scy	 * calculated over the complete ICMP packet, which includes the
1656254401Scy	 * changed oip IP addresses and oip6->ip6_sum. However, these
1657254401Scy	 * two changes cancel each other out (if the delta for
1658254401Scy	 * the IP address is x, then the delta for ip_sum is minus x),
1659254401Scy	 * so no change in the icmp_cksum is necessary.
1660254401Scy	 *
1661254401Scy	 * Inbound ICMP
1662254401Scy	 * ------------
1663254401Scy	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
1664254401Scy	 * - response to outgoing packet (a,b)=>(c,b) (OIP_SRC=c,OIP_DST=b)
1665254401Scy	 * - OIP_SRC(c)=nat6_newsrcip,          OIP_DST(b)=nat6_newdstip
1666254401Scy	 *=> OIP_SRC(c)=nat6_oldsrcip,          OIP_DST(b)=nat6_olddstip
1667254401Scy	 *
1668254401Scy	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
1669254401Scy	 * - response to outgoing packet (c,a)=>(b,a) (OIP_SRC=b,OIP_DST=a)
1670254401Scy	 * - OIP_SRC(b)=nat6_olddstip,          OIP_DST(a)=nat6_oldsrcip
1671254401Scy	 *=> OIP_SRC(b)=nat6_newdstip,          OIP_DST(a)=nat6_newsrcip
1672254401Scy	 *
1673254401Scy	 * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
1674254401Scy	 * - response to outgoing packet (a,b)=>(c,d) (OIP_SRC=c,OIP_DST=d)
1675254401Scy	 * - OIP_SRC(c)=nat6_newsrcip,          OIP_DST(d)=nat6_newdstip
1676254401Scy	 *=> OIP_SRC(c)=nat6_oldsrcip,          OIP_DST(d)=nat6_olddstip
1677254401Scy	 *
1678254401Scy	 * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
1679254401Scy	 * - response to outgoing packet (d,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
1680254401Scy	 * - OIP_SRC(b)=nat6_olddstip,          OIP_DST(a)=nat6_oldsrcip
1681254401Scy	 *=> OIP_SRC(b)=nat6_newdstip,          OIP_DST(a)=nat6_newsrcip
1682254401Scy	 *
1683254401Scy	 * Outbound ICMP
1684254401Scy	 * -------------
1685254401Scy	 * MAP rule, SRC=a,DST=b -> SRC=c,DST=b
1686254401Scy	 * - response to incoming packet (b,c)=>(b,a) (OIP_SRC=b,OIP_DST=a)
1687254401Scy	 * - OIP_SRC(b)=nat6_olddstip,          OIP_DST(a)=nat6_oldsrcip
1688254401Scy	 *=> OIP_SRC(b)=nat6_newdstip,          OIP_DST(a)=nat6_newsrcip
1689254401Scy	 *
1690254401Scy	 * RDR rule, SRC=a,DST=b -> SRC=a,DST=c
1691254401Scy	 * - response to incoming packet (a,b)=>(a,c) (OIP_SRC=a,OIP_DST=c)
1692254401Scy	 * - OIP_SRC(a)=nat6_newsrcip,          OIP_DST(c)=nat6_newdstip
1693254401Scy	 *=> OIP_SRC(a)=nat6_oldsrcip,          OIP_DST(c)=nat6_olddstip
1694254401Scy	 *
1695254401Scy	 * REWRITE out rule, SRC=a,DST=b -> SRC=c,DST=d
1696254401Scy	 * - response to incoming packet (d,c)=>(b,a) (OIP_SRC=c,OIP_DST=d)
1697254401Scy	 * - OIP_SRC(c)=nat6_olddstip,          OIP_DST(d)=nat6_oldsrcip
1698254401Scy	 *=> OIP_SRC(b)=nat6_newdstip,          OIP_DST(a)=nat6_newsrcip
1699254401Scy	 *
1700254401Scy	 * REWRITE in rule, SRC=a,DST=b -> SRC=c,DST=d
1701254401Scy	 * - response to incoming packet (a,b)=>(c,d) (OIP_SRC=b,OIP_DST=a)
1702254401Scy	 * - OIP_SRC(b)=nat6_newsrcip,          OIP_DST(a)=nat6_newdstip
1703254401Scy	 *=> OIP_SRC(a)=nat6_oldsrcip,          OIP_DST(c)=nat6_olddstip
1704254401Scy	 */
1705254401Scy
1706254401Scy	if (((fin->fin_out == 0) && ((nat->nat_redir & NAT_MAP) != 0)) ||
1707254401Scy	    ((fin->fin_out == 1) && ((nat->nat_redir & NAT_REDIRECT) != 0))) {
1708254401Scy		a1 = nat->nat_osrc6;
1709254401Scy		a4.in6 = oip6->ip6_src;
1710254401Scy		a3 = nat->nat_odst6;
1711254401Scy		a2.in6 = oip6->ip6_dst;
1712254401Scy		oip6->ip6_src = a1.in6;
1713254401Scy		oip6->ip6_dst = a3.in6;
1714254401Scy		odst = 1;
1715254401Scy	} else {
1716254401Scy		a1 = nat->nat_ndst6;
1717254401Scy		a2.in6 = oip6->ip6_dst;
1718254401Scy		a3 = nat->nat_nsrc6;
1719254401Scy		a4.in6 = oip6->ip6_src;
1720254401Scy		oip6->ip6_dst = a3.in6;
1721254401Scy		oip6->ip6_src = a1.in6;
1722254401Scy		odst = 0;
1723254401Scy	}
1724254401Scy
1725254401Scy	sumd = 0;
1726254401Scy	if (IP6_NEQ(&a3, &a2) || IP6_NEQ(&a1, &a4)) {
1727254401Scy		if (IP6_GT(&a3, &a2)) {
1728254401Scy			sumd = ipf_nat6_ip6subtract(&a2, &a3);
1729254401Scy			sumd--;
1730254401Scy		} else {
1731254401Scy			sumd = ipf_nat6_ip6subtract(&a2, &a3);
1732254401Scy		}
1733254401Scy		if (IP6_GT(&a1, &a4)) {
1734254401Scy			sumd += ipf_nat6_ip6subtract(&a4, &a1);
1735254401Scy			sumd--;
1736254401Scy		} else {
1737254401Scy			sumd += ipf_nat6_ip6subtract(&a4, &a1);
1738254401Scy		}
1739254401Scy		sumd = ~sumd;
1740254401Scy	}
1741254401Scy
1742254401Scy	sumd2 = sumd;
1743254401Scy	sum1 = 0;
1744254401Scy	sum2 = 0;
1745254401Scy
1746254401Scy	/*
1747254401Scy	 * Fix UDP pseudo header checksum to compensate for the
1748254401Scy	 * IP address change.
1749254401Scy	 */
1750254401Scy	if (((flags & IPN_TCPUDP) != 0) && (dlen >= 4)) {
1751254401Scy		u_32_t sum3, sum4;
1752254401Scy		/*
1753254401Scy		 * Step 2 :
1754254401Scy		 * For offending TCP/UDP IP packets, translate the ports as
1755254401Scy		 * well, based on the NAT specification. Of course such
1756254401Scy		 * a change may be reflected in the ICMP checksum as well.
1757254401Scy		 *
1758254401Scy		 * Since the port fields are part of the TCP/UDP checksum
1759254401Scy		 * of the offending IP packet, you need to adjust that checksum
1760254401Scy		 * as well... except that the change in the port numbers should
1761254401Scy		 * be offset by the checksum change.  However, the TCP/UDP
1762254401Scy		 * checksum will also need to change if there has been an
1763254401Scy		 * IP address change.
1764254401Scy		 */
1765254401Scy		if (odst == 1) {
1766254401Scy			sum1 = ntohs(nat->nat_osport);
1767254401Scy			sum4 = ntohs(tcp->th_sport);
1768254401Scy			sum3 = ntohs(nat->nat_odport);
1769254401Scy			sum2 = ntohs(tcp->th_dport);
1770254401Scy
1771254401Scy			tcp->th_sport = htons(sum1);
1772254401Scy			tcp->th_dport = htons(sum3);
1773254401Scy		} else {
1774254401Scy			sum1 = ntohs(nat->nat_ndport);
1775254401Scy			sum2 = ntohs(tcp->th_dport);
1776254401Scy			sum3 = ntohs(nat->nat_nsport);
1777254401Scy			sum4 = ntohs(tcp->th_sport);
1778254401Scy
1779254401Scy			tcp->th_dport = htons(sum3);
1780254401Scy			tcp->th_sport = htons(sum1);
1781254401Scy		}
1782254401Scy		sumd += sum1 - sum4;
1783254401Scy		sumd += sum3 - sum2;
1784254401Scy
1785254401Scy		if (sumd != 0 || sumd2 != 0) {
1786254401Scy			/*
1787254401Scy			 * At this point, sumd is the delta to apply to the
1788254401Scy			 * TCP/UDP header, given the changes in both the IP
1789254401Scy			 * address and the ports and sumd2 is the delta to
1790254401Scy			 * apply to the ICMP header, given the IP address
1791254401Scy			 * change delta that may need to be applied to the
1792254401Scy			 * TCP/UDP checksum instead.
1793254401Scy			 *
1794254401Scy			 * If we will both the IP and TCP/UDP checksums
1795254401Scy			 * then the ICMP checksum changes by the address
1796254401Scy			 * delta applied to the TCP/UDP checksum.  If we
1797254401Scy			 * do not change the TCP/UDP checksum them we
1798254401Scy			 * apply the delta in ports to the ICMP checksum.
1799254401Scy			 */
1800254401Scy			if (oip6->ip6_nxt == IPPROTO_UDP) {
1801254401Scy				if ((dlen >= 8) && (*csump != 0)) {
1802254401Scy					ipf_fix_datacksum(csump, sumd);
1803254401Scy				} else {
1804254401Scy					sumd2 = sum4 - sum1;
1805254401Scy					if (sum1 > sum4)
1806254401Scy						sumd2--;
1807254401Scy					sumd2 += sum2 - sum3;
1808254401Scy					if (sum3 > sum2)
1809254401Scy						sumd2--;
1810254401Scy				}
1811254401Scy			} else if (oip6->ip6_nxt == IPPROTO_TCP) {
1812254401Scy				if (dlen >= 18) {
1813254401Scy					ipf_fix_datacksum(csump, sumd);
1814254401Scy				} else {
1815254401Scy					sumd2 = sum4 - sum1;
1816254401Scy					if (sum1 > sum4)
1817254401Scy						sumd2--;
1818254401Scy					sumd2 += sum2 - sum3;
1819254401Scy					if (sum3 > sum2)
1820254401Scy						sumd2--;
1821254401Scy				}
1822254401Scy			}
1823254401Scy			if (sumd2 != 0) {
1824254401Scy				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
1825254401Scy				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
1826254401Scy				sumd2 = (sumd2 & 0xffff) + (sumd2 >> 16);
1827254401Scy				ipf_fix_incksum(0, &icmp6->icmp6_cksum,
1828254401Scy						sumd2, 0);
1829254401Scy			}
1830254401Scy		}
1831254401Scy	} else if (((flags & IPN_ICMPQUERY) != 0) && (dlen >= 8)) {
1832254401Scy		struct icmp6_hdr *orgicmp;
1833254401Scy
1834254401Scy		/*
1835254401Scy		 * XXX - what if this is bogus hl and we go off the end ?
1836254401Scy		 * In this case, ipf_nat6_icmperrorlookup() will have
1837254401Scy		 * returned NULL.
1838254401Scy		 */
1839254401Scy		orgicmp = (struct icmp6_hdr *)dp;
1840254401Scy
1841254401Scy		if (odst == 1) {
1842254401Scy			if (orgicmp->icmp6_id != nat->nat_osport) {
1843254401Scy
1844254401Scy				/*
1845254401Scy				 * Fix ICMP checksum (of the offening ICMP
1846254401Scy				 * query packet) to compensate the change
1847254401Scy				 * in the ICMP id of the offending ICMP
1848254401Scy				 * packet.
1849254401Scy				 *
1850254401Scy				 * Since you modify orgicmp->icmp6_id with
1851254401Scy				 * a delta (say x) and you compensate that
1852254401Scy				 * in origicmp->icmp6_cksum with a delta
1853254401Scy				 * minus x, you don't have to adjust the
1854254401Scy				 * overall icmp->icmp6_cksum
1855254401Scy				 */
1856254401Scy				sum1 = ntohs(orgicmp->icmp6_id);
1857254401Scy				sum2 = ntohs(nat->nat_osport);
1858254401Scy				CALC_SUMD(sum1, sum2, sumd);
1859254401Scy				orgicmp->icmp6_id = nat->nat_oicmpid;
1860254401Scy				ipf_fix_datacksum(&orgicmp->icmp6_cksum, sumd);
1861254401Scy			}
1862254401Scy		} /* nat6_dir == NAT_INBOUND is impossible for icmp queries */
1863254401Scy	}
1864254401Scy	return nat;
1865254401Scy}
1866254401Scy
1867254401Scy
1868254401Scy/*
1869254401Scy *       MAP-IN    MAP-OUT   RDR-IN   RDR-OUT
1870254401Scy * osrc    X       == src    == src      X
1871254401Scy * odst    X       == dst    == dst      X
1872254401Scy * nsrc  == dst      X         X      == dst
1873254401Scy * ndst  == src      X         X      == src
1874254401Scy * MAP = NAT_OUTBOUND, RDR = NAT_INBOUND
1875254401Scy */
1876254401Scy/*
1877254401Scy * NB: these lookups don't lock access to the list, it assumed that it has
1878254401Scy * already been done!
1879254401Scy */
1880254401Scy/* ------------------------------------------------------------------------ */
1881254401Scy/* Function:    ipf_nat6_inlookup                                           */
1882254401Scy/* Returns:     nat6_t*   - NULL == no match,                               */
1883254401Scy/*                          else pointer to matching NAT entry              */
1884254401Scy/* Parameters:  fin(I)    - pointer to packet information                   */
1885254401Scy/*              flags(I)  - NAT flags for this packet                       */
1886254401Scy/*              p(I)      - protocol for this packet                        */
1887254401Scy/*              src(I)    - source IP address                               */
1888254401Scy/*              mapdst(I) - destination IP address                          */
1889254401Scy/*                                                                          */
1890254401Scy/* Lookup a nat entry based on the mapped destination ip address/port and   */
1891254401Scy/* real source address/port.  We use this lookup when receiving a packet,   */
1892254401Scy/* we're looking for a table entry, based on the destination address.       */
1893254401Scy/*                                                                          */
1894254401Scy/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
1895254401Scy/*                                                                          */
1896254401Scy/* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
1897254401Scy/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
1898254401Scy/*                                                                          */
1899254401Scy/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
1900254401Scy/*            the packet is of said protocol                                */
1901254401Scy/* ------------------------------------------------------------------------ */
1902254401Scynat_t *
1903254401Scyipf_nat6_inlookup(fin, flags, p, src, mapdst)
1904254401Scy	fr_info_t *fin;
1905254401Scy	u_int flags, p;
1906254401Scy	struct in6_addr *src , *mapdst;
1907254401Scy{
1908254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
1909254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
1910254401Scy	u_short sport, dport;
1911254401Scy	grehdr_t *gre;
1912254401Scy	ipnat_t *ipn;
1913254401Scy	u_int sflags;
1914254401Scy	nat_t *nat;
1915254401Scy	int nflags;
1916254401Scy	i6addr_t dst;
1917254401Scy	void *ifp;
1918254401Scy	u_int hv;
1919254401Scy
1920254401Scy	ifp = fin->fin_ifp;
1921254401Scy	sport = 0;
1922254401Scy	dport = 0;
1923254401Scy	gre = NULL;
1924254401Scy	dst.in6 = *mapdst;
1925254401Scy	sflags = flags & NAT_TCPUDPICMP;
1926254401Scy
1927254401Scy	switch (p)
1928254401Scy	{
1929254401Scy	case IPPROTO_TCP :
1930254401Scy	case IPPROTO_UDP :
1931254401Scy		sport = htons(fin->fin_data[0]);
1932254401Scy		dport = htons(fin->fin_data[1]);
1933254401Scy		break;
1934254401Scy	case IPPROTO_ICMPV6 :
1935254401Scy		if (flags & IPN_ICMPERR)
1936254401Scy			sport = fin->fin_data[1];
1937254401Scy		else
1938254401Scy			dport = fin->fin_data[1];
1939254401Scy		break;
1940254401Scy	default :
1941254401Scy		break;
1942254401Scy	}
1943254401Scy
1944254401Scy
1945254401Scy	if ((flags & SI_WILDP) != 0)
1946254401Scy		goto find_in_wild_ports;
1947254401Scy
1948254401Scy	hv = NAT_HASH_FN6(&dst, dport, 0xffffffff);
1949254401Scy	hv = NAT_HASH_FN6(src, hv + sport, softn->ipf_nat_table_sz);
1950254401Scy	nat = softn->ipf_nat_table[1][hv];
1951254401Scy	/* TRACE dst, dport, src, sport, hv, nat */
1952254401Scy
1953254401Scy	for (; nat; nat = nat->nat_hnext[1]) {
1954254401Scy		if (nat->nat_ifps[0] != NULL) {
1955254401Scy			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
1956254401Scy				continue;
1957254401Scy		}
1958254401Scy
1959254401Scy		if (nat->nat_pr[0] != p)
1960254401Scy			continue;
1961254401Scy
1962254401Scy		switch (nat->nat_dir)
1963254401Scy		{
1964254401Scy		case NAT_INBOUND :
1965254401Scy			if (nat->nat_v[0] != 6)
1966254401Scy				continue;
1967254401Scy			if (IP6_NEQ(&nat->nat_osrc6, src) ||
1968254401Scy			    IP6_NEQ(&nat->nat_odst6, &dst))
1969254401Scy				continue;
1970254401Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
1971254401Scy				if (nat->nat_osport != sport)
1972254401Scy					continue;
1973254401Scy				if (nat->nat_odport != dport)
1974254401Scy					continue;
1975254401Scy
1976254401Scy			} else if (p == IPPROTO_ICMPV6) {
1977254401Scy				if (nat->nat_osport != dport) {
1978254401Scy					continue;
1979254401Scy				}
1980254401Scy			}
1981254401Scy			break;
1982254401Scy		case NAT_OUTBOUND :
1983254401Scy			if (nat->nat_v[1] != 6)
1984254401Scy				continue;
1985254401Scy			if (IP6_NEQ(&nat->nat_ndst6, src) ||
1986254401Scy			    IP6_NEQ(&nat->nat_nsrc6, &dst))
1987254401Scy				continue;
1988254401Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
1989254401Scy				if (nat->nat_ndport != sport)
1990254401Scy					continue;
1991254401Scy				if (nat->nat_nsport != dport)
1992254401Scy					continue;
1993254401Scy
1994254401Scy			} else if (p == IPPROTO_ICMPV6) {
1995254401Scy				if (nat->nat_osport != dport) {
1996254401Scy					continue;
1997254401Scy				}
1998254401Scy			}
1999254401Scy			break;
2000254401Scy		}
2001254401Scy
2002254401Scy
2003254401Scy		if ((nat->nat_flags & IPN_TCPUDP) != 0) {
2004254401Scy			ipn = nat->nat_ptr;
2005254401Scy#ifdef IPF_V6_PROXIES
2006254401Scy			if ((ipn != NULL) && (nat->nat_aps != NULL))
2007254401Scy				if (appr_match(fin, nat) != 0)
2008254401Scy					continue;
2009254401Scy#endif
2010254401Scy		}
2011254401Scy		if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
2012254401Scy			nat->nat_ifps[0] = ifp;
2013254401Scy			nat->nat_mtu[0] = GETIFMTU_6(ifp);
2014254401Scy		}
2015254401Scy		return nat;
2016254401Scy	}
2017254401Scy
2018254401Scy	/*
2019254401Scy	 * So if we didn't find it but there are wildcard members in the hash
2020254401Scy	 * table, go back and look for them.  We do this search and update here
2021254401Scy	 * because it is modifying the NAT table and we want to do this only
2022254401Scy	 * for the first packet that matches.  The exception, of course, is
2023254401Scy	 * for "dummy" (FI_IGNORE) lookups.
2024254401Scy	 */
2025254401Scyfind_in_wild_ports:
2026254401Scy	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
2027254401Scy		NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_1);
2028254401Scy		return NULL;
2029254401Scy	}
2030254401Scy	if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
2031254401Scy		NBUMPSIDE6D(0, ns_lookup_nowild);
2032254401Scy		return NULL;
2033254401Scy	}
2034254401Scy
2035254401Scy	RWLOCK_EXIT(&softc->ipf_nat);
2036254401Scy
2037254401Scy	hv = NAT_HASH_FN6(&dst, 0, 0xffffffff);
2038254401Scy	hv = NAT_HASH_FN6(src, hv, softn->ipf_nat_table_sz);
2039254401Scy	WRITE_ENTER(&softc->ipf_nat);
2040254401Scy
2041254401Scy	nat = softn->ipf_nat_table[1][hv];
2042254401Scy	/* TRACE dst, src, hv, nat */
2043254401Scy	for (; nat; nat = nat->nat_hnext[1]) {
2044254401Scy		if (nat->nat_ifps[0] != NULL) {
2045254401Scy			if ((ifp != NULL) && (ifp != nat->nat_ifps[0]))
2046254401Scy				continue;
2047254401Scy		}
2048254401Scy
2049254401Scy		if (nat->nat_pr[0] != fin->fin_p)
2050254401Scy			continue;
2051254401Scy
2052254401Scy		switch (nat->nat_dir)
2053254401Scy		{
2054254401Scy		case NAT_INBOUND :
2055254401Scy			if (nat->nat_v[0] != 6)
2056254401Scy				continue;
2057254401Scy			if (IP6_NEQ(&nat->nat_osrc6, src) ||
2058254401Scy			    IP6_NEQ(&nat->nat_odst6, &dst))
2059254401Scy				continue;
2060254401Scy			break;
2061254401Scy		case NAT_OUTBOUND :
2062254401Scy			if (nat->nat_v[1] != 6)
2063254401Scy				continue;
2064254401Scy			if (IP6_NEQ(&nat->nat_ndst6, src) ||
2065254401Scy			    IP6_NEQ(&nat->nat_nsrc6, &dst))
2066254401Scy				continue;
2067254401Scy			break;
2068254401Scy		}
2069254401Scy
2070254401Scy		nflags = nat->nat_flags;
2071254401Scy		if (!(nflags & (NAT_TCPUDP|SI_WILDP)))
2072254401Scy			continue;
2073254401Scy
2074254401Scy		if (ipf_nat_wildok(nat, (int)sport, (int)dport, nflags,
2075254401Scy				   NAT_INBOUND) == 1) {
2076254401Scy			if ((fin->fin_flx & FI_IGNORE) != 0)
2077254401Scy				break;
2078254401Scy			if ((nflags & SI_CLONE) != 0) {
2079254401Scy				nat = ipf_nat_clone(fin, nat);
2080254401Scy				if (nat == NULL)
2081254401Scy					break;
2082254401Scy			} else {
2083254401Scy				MUTEX_ENTER(&softn->ipf_nat_new);
2084254401Scy				softn->ipf_nat_stats.ns_wilds--;
2085254401Scy				MUTEX_EXIT(&softn->ipf_nat_new);
2086254401Scy			}
2087254401Scy
2088254401Scy			if (nat->nat_dir == NAT_INBOUND) {
2089254401Scy				if (nat->nat_osport == 0) {
2090254401Scy					nat->nat_osport = sport;
2091254401Scy					nat->nat_nsport = sport;
2092254401Scy				}
2093254401Scy				if (nat->nat_odport == 0) {
2094254401Scy					nat->nat_odport = dport;
2095254401Scy					nat->nat_ndport = dport;
2096254401Scy				}
2097254401Scy			} else {
2098254401Scy				if (nat->nat_osport == 0) {
2099254401Scy					nat->nat_osport = dport;
2100254401Scy					nat->nat_nsport = dport;
2101254401Scy				}
2102254401Scy				if (nat->nat_odport == 0) {
2103254401Scy					nat->nat_odport = sport;
2104254401Scy					nat->nat_ndport = sport;
2105254401Scy				}
2106254401Scy			}
2107254401Scy			if ((nat->nat_ifps[0] == NULL) && (ifp != NULL)) {
2108254401Scy				nat->nat_ifps[0] = ifp;
2109254401Scy				nat->nat_mtu[0] = GETIFMTU_6(ifp);
2110254401Scy			}
2111254401Scy			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
2112254401Scy			ipf_nat6_tabmove(softn, nat);
2113254401Scy			break;
2114254401Scy		}
2115254401Scy	}
2116254401Scy
2117254401Scy	MUTEX_DOWNGRADE(&softc->ipf_nat);
2118254401Scy
2119254401Scy	if (nat == NULL) {
2120254401Scy		NBUMPSIDE6DX(0, ns_lookup_miss, ns_lookup_miss_2);
2121254401Scy	}
2122254401Scy	return nat;
2123254401Scy}
2124254401Scy
2125254401Scy
2126254401Scy/* ------------------------------------------------------------------------ */
2127254401Scy/* Function:    ipf_nat6_tabmove                                            */
2128254401Scy/* Returns:     Nil                                                         */
2129254401Scy/* Parameters:  nat(I) - pointer to NAT structure                           */
2130254401Scy/* Write Lock:  ipf_nat                                                     */
2131254401Scy/*                                                                          */
2132254401Scy/* This function is only called for TCP/UDP NAT table entries where the     */
2133254401Scy/* original was placed in the table without hashing on the ports and we now */
2134254401Scy/* want to include hashing on port numbers.                                 */
2135254401Scy/* ------------------------------------------------------------------------ */
2136254401Scystatic void
2137254401Scyipf_nat6_tabmove(softn, nat)
2138254401Scy	ipf_nat_softc_t *softn;
2139254401Scy	nat_t *nat;
2140254401Scy{
2141254401Scy	nat_t **natp;
2142254401Scy	u_int hv0, hv1;
2143254401Scy
2144254401Scy	if (nat->nat_flags & SI_CLONE)
2145254401Scy		return;
2146254401Scy
2147254401Scy	/*
2148254401Scy	 * Remove the NAT entry from the old location
2149254401Scy	 */
2150254401Scy	if (nat->nat_hnext[0])
2151254401Scy		nat->nat_hnext[0]->nat_phnext[0] = nat->nat_phnext[0];
2152254401Scy	*nat->nat_phnext[0] = nat->nat_hnext[0];
2153254401Scy	softn->ipf_nat_stats.ns_side[0].ns_bucketlen[nat->nat_hv[0]]--;
2154254401Scy
2155254401Scy	if (nat->nat_hnext[1])
2156254401Scy		nat->nat_hnext[1]->nat_phnext[1] = nat->nat_phnext[1];
2157254401Scy	*nat->nat_phnext[1] = nat->nat_hnext[1];
2158254401Scy	softn->ipf_nat_stats.ns_side[1].ns_bucketlen[nat->nat_hv[1]]--;
2159254401Scy
2160254401Scy	/*
2161254401Scy	 * Add into the NAT table in the new position
2162254401Scy	 */
2163254401Scy	hv0 = NAT_HASH_FN6(&nat->nat_osrc6, nat->nat_osport, 0xffffffff);
2164254401Scy	hv0 = NAT_HASH_FN6(&nat->nat_odst6, hv0 + nat->nat_odport,
2165254401Scy			   softn->ipf_nat_table_sz);
2166254401Scy	hv1 = NAT_HASH_FN6(&nat->nat_nsrc6, nat->nat_nsport, 0xffffffff);
2167254401Scy	hv1 = NAT_HASH_FN6(&nat->nat_ndst6, hv1 + nat->nat_ndport,
2168254401Scy			   softn->ipf_nat_table_sz);
2169254401Scy
2170254401Scy	if (nat->nat_dir == NAT_INBOUND || nat->nat_dir == NAT_DIVERTIN) {
2171254401Scy		u_int swap;
2172254401Scy
2173254401Scy		swap = hv0;
2174254401Scy		hv0 = hv1;
2175254401Scy		hv1 = swap;
2176254401Scy	}
2177254401Scy
2178254401Scy	/* TRACE nat_osrc6, nat_osport, nat_odst6, nat_odport, hv0 */
2179254401Scy	/* TRACE nat_nsrc6, nat_nsport, nat_ndst6, nat_ndport, hv1 */
2180254401Scy
2181254401Scy	nat->nat_hv[0] = hv0;
2182254401Scy	natp = &softn->ipf_nat_table[0][hv0];
2183254401Scy	if (*natp)
2184254401Scy		(*natp)->nat_phnext[0] = &nat->nat_hnext[0];
2185254401Scy	nat->nat_phnext[0] = natp;
2186254401Scy	nat->nat_hnext[0] = *natp;
2187254401Scy	*natp = nat;
2188254401Scy	softn->ipf_nat_stats.ns_side[0].ns_bucketlen[hv0]++;
2189254401Scy
2190254401Scy	nat->nat_hv[1] = hv1;
2191254401Scy	natp = &softn->ipf_nat_table[1][hv1];
2192254401Scy	if (*natp)
2193254401Scy		(*natp)->nat_phnext[1] = &nat->nat_hnext[1];
2194254401Scy	nat->nat_phnext[1] = natp;
2195254401Scy	nat->nat_hnext[1] = *natp;
2196254401Scy	*natp = nat;
2197254401Scy	softn->ipf_nat_stats.ns_side[1].ns_bucketlen[hv1]++;
2198254401Scy}
2199254401Scy
2200254401Scy
2201254401Scy/* ------------------------------------------------------------------------ */
2202254401Scy/* Function:    ipf_nat6_outlookup                                          */
2203254401Scy/* Returns:     nat6_t*  - NULL == no match,                                */
2204254401Scy/*                         else pointer to matching NAT entry               */
2205254401Scy/* Parameters:  fin(I)   - pointer to packet information                    */
2206254401Scy/*              flags(I) - NAT flags for this packet                        */
2207254401Scy/*              p(I)     - protocol for this packet                         */
2208254401Scy/*              src(I)   - source IP address                                */
2209254401Scy/*              dst(I)   - destination IP address                           */
2210254401Scy/*              rw(I)    - 1 == write lock on  held, 0 == read lock.        */
2211254401Scy/*                                                                          */
2212254401Scy/* Lookup a nat entry based on the source 'real' ip address/port and        */
2213254401Scy/* destination address/port.  We use this lookup when sending a packet out, */
2214254401Scy/* we're looking for a table entry, based on the source address.            */
2215254401Scy/*                                                                          */
2216254401Scy/* NOTE: THE PACKET BEING CHECKED (IF FOUND) HAS A MAPPING ALREADY.         */
2217254401Scy/*                                                                          */
2218254401Scy/* NOTE: IT IS ASSUMED THAT  IS ONLY HELD WITH A READ LOCK WHEN             */
2219254401Scy/*       THIS FUNCTION IS CALLED WITH NAT_SEARCH SET IN nflags.             */
2220254401Scy/*                                                                          */
2221254401Scy/* flags   -> relevant are IPN_UDP/IPN_TCP/IPN_ICMPQUERY that indicate if   */
2222254401Scy/*            the packet is of said protocol                                */
2223254401Scy/* ------------------------------------------------------------------------ */
2224254401Scynat_t *
2225254401Scyipf_nat6_outlookup(fin, flags, p, src, dst)
2226254401Scy	fr_info_t *fin;
2227254401Scy	u_int flags, p;
2228254401Scy	struct in6_addr *src , *dst;
2229254401Scy{
2230254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
2231254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2232254401Scy	u_short sport, dport;
2233254401Scy	u_int sflags;
2234254401Scy	ipnat_t *ipn;
2235254401Scy	nat_t *nat;
2236254401Scy	void *ifp;
2237254401Scy	u_int hv;
2238254401Scy
2239254401Scy	ifp = fin->fin_ifp;
2240254401Scy	sflags = flags & IPN_TCPUDPICMP;
2241254401Scy	sport = 0;
2242254401Scy	dport = 0;
2243254401Scy
2244254401Scy	switch (p)
2245254401Scy	{
2246254401Scy	case IPPROTO_TCP :
2247254401Scy	case IPPROTO_UDP :
2248254401Scy		sport = htons(fin->fin_data[0]);
2249254401Scy		dport = htons(fin->fin_data[1]);
2250254401Scy		break;
2251254401Scy	case IPPROTO_ICMPV6 :
2252254401Scy		if (flags & IPN_ICMPERR)
2253254401Scy			sport = fin->fin_data[1];
2254254401Scy		else
2255254401Scy			dport = fin->fin_data[1];
2256254401Scy		break;
2257254401Scy	default :
2258254401Scy		break;
2259254401Scy	}
2260254401Scy
2261254401Scy	if ((flags & SI_WILDP) != 0)
2262254401Scy		goto find_out_wild_ports;
2263254401Scy
2264254401Scy	hv = NAT_HASH_FN6(src, sport, 0xffffffff);
2265254401Scy	hv = NAT_HASH_FN6(dst, hv + dport, softn->ipf_nat_table_sz);
2266254401Scy	nat = softn->ipf_nat_table[0][hv];
2267254401Scy
2268254401Scy	/* TRACE src, sport, dst, dport, hv, nat */
2269254401Scy
2270254401Scy	for (; nat; nat = nat->nat_hnext[0]) {
2271254401Scy		if (nat->nat_ifps[1] != NULL) {
2272254401Scy			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
2273254401Scy				continue;
2274254401Scy		}
2275254401Scy
2276254401Scy		if (nat->nat_pr[1] != p)
2277254401Scy			continue;
2278254401Scy
2279254401Scy		switch (nat->nat_dir)
2280254401Scy		{
2281254401Scy		case NAT_INBOUND :
2282254401Scy			if (nat->nat_v[1] != 6)
2283254401Scy				continue;
2284254401Scy			if (IP6_NEQ(&nat->nat_ndst6, src) ||
2285254401Scy			    IP6_NEQ(&nat->nat_nsrc6, dst))
2286254401Scy				continue;
2287254401Scy
2288254401Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
2289254401Scy				if (nat->nat_ndport != sport)
2290254401Scy					continue;
2291254401Scy				if (nat->nat_nsport != dport)
2292254401Scy					continue;
2293254401Scy
2294254401Scy			} else if (p == IPPROTO_ICMPV6) {
2295254401Scy				if (nat->nat_osport != dport) {
2296254401Scy					continue;
2297254401Scy				}
2298254401Scy			}
2299254401Scy			break;
2300254401Scy		case NAT_OUTBOUND :
2301254401Scy			if (nat->nat_v[0] != 6)
2302254401Scy				continue;
2303254401Scy			if (IP6_NEQ(&nat->nat_osrc6, src) ||
2304254401Scy			    IP6_NEQ(&nat->nat_odst6, dst))
2305254401Scy				continue;
2306254401Scy
2307254401Scy			if ((nat->nat_flags & IPN_TCPUDP) != 0) {
2308254401Scy				if (nat->nat_odport != dport)
2309254401Scy					continue;
2310254401Scy				if (nat->nat_osport != sport)
2311254401Scy					continue;
2312254401Scy
2313254401Scy			} else if (p == IPPROTO_ICMPV6) {
2314254401Scy				if (nat->nat_osport != dport) {
2315254401Scy					continue;
2316254401Scy				}
2317254401Scy			}
2318254401Scy			break;
2319254401Scy		}
2320254401Scy
2321254401Scy		ipn = nat->nat_ptr;
2322254401Scy#ifdef IPF_V6_PROXIES
2323254401Scy		if ((ipn != NULL) && (nat->nat_aps != NULL))
2324254401Scy			if (appr_match(fin, nat) != 0)
2325254401Scy				continue;
2326254401Scy#endif
2327254401Scy
2328254401Scy		if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
2329254401Scy			nat->nat_ifps[1] = ifp;
2330254401Scy			nat->nat_mtu[1] = GETIFMTU_6(ifp);
2331254401Scy		}
2332254401Scy		return nat;
2333254401Scy	}
2334254401Scy
2335254401Scy	/*
2336254401Scy	 * So if we didn't find it but there are wildcard members in the hash
2337254401Scy	 * table, go back and look for them.  We do this search and update here
2338254401Scy	 * because it is modifying the NAT table and we want to do this only
2339254401Scy	 * for the first packet that matches.  The exception, of course, is
2340254401Scy	 * for "dummy" (FI_IGNORE) lookups.
2341254401Scy	 */
2342254401Scyfind_out_wild_ports:
2343254401Scy	if (!(flags & NAT_TCPUDP) || !(flags & NAT_SEARCH)) {
2344254401Scy		NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_3);
2345254401Scy		return NULL;
2346254401Scy	}
2347254401Scy	if (softn->ipf_nat_stats.ns_wilds == 0 || (fin->fin_flx & FI_NOWILD)) {
2348254401Scy		NBUMPSIDE6D(1, ns_lookup_nowild);
2349254401Scy		return NULL;
2350254401Scy	}
2351254401Scy
2352254401Scy	RWLOCK_EXIT(&softc->ipf_nat);
2353254401Scy
2354254401Scy	hv = NAT_HASH_FN6(src, 0, 0xffffffff);
2355254401Scy	hv = NAT_HASH_FN6(dst, hv, softn->ipf_nat_table_sz);
2356254401Scy
2357254401Scy	WRITE_ENTER(&softc->ipf_nat);
2358254401Scy
2359254401Scy	nat = softn->ipf_nat_table[0][hv];
2360254401Scy	for (; nat; nat = nat->nat_hnext[0]) {
2361254401Scy		if (nat->nat_ifps[1] != NULL) {
2362254401Scy			if ((ifp != NULL) && (ifp != nat->nat_ifps[1]))
2363254401Scy				continue;
2364254401Scy		}
2365254401Scy
2366254401Scy		if (nat->nat_pr[1] != fin->fin_p)
2367254401Scy			continue;
2368254401Scy
2369254401Scy		switch (nat->nat_dir)
2370254401Scy		{
2371254401Scy		case NAT_INBOUND :
2372254401Scy			if (nat->nat_v[1] != 6)
2373254401Scy				continue;
2374254401Scy			if (IP6_NEQ(&nat->nat_ndst6, src) ||
2375254401Scy			    IP6_NEQ(&nat->nat_nsrc6, dst))
2376254401Scy				continue;
2377254401Scy			break;
2378254401Scy		case NAT_OUTBOUND :
2379254401Scy			if (nat->nat_v[0] != 6)
2380254401Scy			continue;
2381254401Scy			if (IP6_NEQ(&nat->nat_osrc6, src) ||
2382254401Scy			    IP6_NEQ(&nat->nat_odst6, dst))
2383254401Scy				continue;
2384254401Scy			break;
2385254401Scy		}
2386254401Scy
2387254401Scy		if (!(nat->nat_flags & (NAT_TCPUDP|SI_WILDP)))
2388254401Scy			continue;
2389254401Scy
2390254401Scy		if (ipf_nat_wildok(nat, (int)sport, (int)dport, nat->nat_flags,
2391254401Scy				   NAT_OUTBOUND) == 1) {
2392254401Scy			if ((fin->fin_flx & FI_IGNORE) != 0)
2393254401Scy				break;
2394254401Scy			if ((nat->nat_flags & SI_CLONE) != 0) {
2395254401Scy				nat = ipf_nat_clone(fin, nat);
2396254401Scy				if (nat == NULL)
2397254401Scy					break;
2398254401Scy			} else {
2399254401Scy				MUTEX_ENTER(&softn->ipf_nat_new);
2400254401Scy				softn->ipf_nat_stats.ns_wilds--;
2401254401Scy				MUTEX_EXIT(&softn->ipf_nat_new);
2402254401Scy			}
2403254401Scy
2404254401Scy			if (nat->nat_dir == NAT_OUTBOUND) {
2405254401Scy				if (nat->nat_osport == 0) {
2406254401Scy					nat->nat_osport = sport;
2407254401Scy					nat->nat_nsport = sport;
2408254401Scy				}
2409254401Scy				if (nat->nat_odport == 0) {
2410254401Scy					nat->nat_odport = dport;
2411254401Scy					nat->nat_ndport = dport;
2412254401Scy				}
2413254401Scy			} else {
2414254401Scy				if (nat->nat_osport == 0) {
2415254401Scy					nat->nat_osport = dport;
2416254401Scy					nat->nat_nsport = dport;
2417254401Scy				}
2418254401Scy				if (nat->nat_odport == 0) {
2419254401Scy					nat->nat_odport = sport;
2420254401Scy					nat->nat_ndport = sport;
2421254401Scy				}
2422254401Scy			}
2423254401Scy			if ((nat->nat_ifps[1] == NULL) && (ifp != NULL)) {
2424254401Scy				nat->nat_ifps[1] = ifp;
2425254401Scy				nat->nat_mtu[1] = GETIFMTU_6(ifp);
2426254401Scy			}
2427254401Scy			nat->nat_flags &= ~(SI_W_DPORT|SI_W_SPORT);
2428254401Scy			ipf_nat6_tabmove(softn, nat);
2429254401Scy			break;
2430254401Scy		}
2431254401Scy	}
2432254401Scy
2433254401Scy	MUTEX_DOWNGRADE(&softc->ipf_nat);
2434254401Scy
2435254401Scy	if (nat == NULL) {
2436254401Scy		NBUMPSIDE6DX(1, ns_lookup_miss, ns_lookup_miss_4);
2437254401Scy	}
2438254401Scy	return nat;
2439254401Scy}
2440254401Scy
2441254401Scy
2442254401Scy/* ------------------------------------------------------------------------ */
2443254401Scy/* Function:    ipf_nat6_lookupredir                                        */
2444254401Scy/* Returns:     nat6_t* - NULL == no match,                                 */
2445254401Scy/*                       else pointer to matching NAT entry                 */
2446254401Scy/* Parameters:  np(I) - pointer to description of packet to find NAT table  */
2447254401Scy/*                      entry for.                                          */
2448254401Scy/*                                                                          */
2449254401Scy/* Lookup the NAT tables to search for a matching redirect                  */
2450254401Scy/* The contents of natlookup_t should imitate those found in a packet that  */
2451254401Scy/* would be translated - ie a packet coming in for RDR or going out for MAP.*/
2452254401Scy/* We can do the lookup in one of two ways, imitating an inbound or         */
2453254401Scy/* outbound  packet.  By default we assume outbound, unless IPN_IN is set.  */
2454254401Scy/* For IN, the fields are set as follows:                                   */
2455254401Scy/*     nl_real* = source information                                        */
2456254401Scy/*     nl_out* = destination information (translated)                       */
2457254401Scy/* For an out packet, the fields are set like this:                         */
2458254401Scy/*     nl_in* = source information (untranslated)                           */
2459254401Scy/*     nl_out* = destination information (translated)                       */
2460254401Scy/* ------------------------------------------------------------------------ */
2461254401Scynat_t *
2462254401Scyipf_nat6_lookupredir(np)
2463254401Scy	natlookup_t *np;
2464254401Scy{
2465254401Scy	fr_info_t fi;
2466254401Scy	nat_t *nat;
2467254401Scy
2468254401Scy	bzero((char *)&fi, sizeof(fi));
2469254401Scy	if (np->nl_flags & IPN_IN) {
2470254401Scy		fi.fin_data[0] = ntohs(np->nl_realport);
2471254401Scy		fi.fin_data[1] = ntohs(np->nl_outport);
2472254401Scy	} else {
2473254401Scy		fi.fin_data[0] = ntohs(np->nl_inport);
2474254401Scy		fi.fin_data[1] = ntohs(np->nl_outport);
2475254401Scy	}
2476254401Scy	if (np->nl_flags & IPN_TCP)
2477254401Scy		fi.fin_p = IPPROTO_TCP;
2478254401Scy	else if (np->nl_flags & IPN_UDP)
2479254401Scy		fi.fin_p = IPPROTO_UDP;
2480254401Scy	else if (np->nl_flags & (IPN_ICMPERR|IPN_ICMPQUERY))
2481254401Scy		fi.fin_p = IPPROTO_ICMPV6;
2482254401Scy
2483254401Scy	/*
2484254401Scy	 * We can do two sorts of lookups:
2485254401Scy	 * - IPN_IN: we have the `real' and `out' address, look for `in'.
2486254401Scy	 * - default: we have the `in' and `out' address, look for `real'.
2487254401Scy	 */
2488254401Scy	if (np->nl_flags & IPN_IN) {
2489254401Scy		if ((nat = ipf_nat6_inlookup(&fi, np->nl_flags, fi.fin_p,
2490254401Scy					     &np->nl_realip6,
2491254401Scy					     &np->nl_outip6))) {
2492254401Scy			np->nl_inip6 = nat->nat_odst6.in6;
2493254401Scy			np->nl_inport = nat->nat_odport;
2494254401Scy		}
2495254401Scy	} else {
2496254401Scy		/*
2497254401Scy		 * If nl_inip is non null, this is a lookup based on the real
2498254401Scy		 * ip address. Else, we use the fake.
2499254401Scy		 */
2500254401Scy		if ((nat = ipf_nat6_outlookup(&fi, np->nl_flags, fi.fin_p,
2501254401Scy					      &np->nl_inip6, &np->nl_outip6))) {
2502254401Scy
2503254401Scy			if ((np->nl_flags & IPN_FINDFORWARD) != 0) {
2504254401Scy				fr_info_t fin;
2505254401Scy				bzero((char *)&fin, sizeof(fin));
2506254401Scy				fin.fin_p = nat->nat_pr[0];
2507254401Scy				fin.fin_data[0] = ntohs(nat->nat_ndport);
2508254401Scy				fin.fin_data[1] = ntohs(nat->nat_nsport);
2509254401Scy				if (ipf_nat6_inlookup(&fin, np->nl_flags,
2510254401Scy						     fin.fin_p,
2511254401Scy						     &nat->nat_ndst6.in6,
2512254401Scy						     &nat->nat_nsrc6.in6) !=
2513254401Scy				    NULL) {
2514254401Scy					np->nl_flags &= ~IPN_FINDFORWARD;
2515254401Scy				}
2516254401Scy			}
2517254401Scy
2518315079Scy			np->nl_realip6 = nat->nat_odst6.in6;
2519315079Scy			np->nl_realport = nat->nat_odport;
2520254401Scy		}
2521254401Scy 	}
2522254401Scy
2523254401Scy	return nat;
2524254401Scy}
2525254401Scy
2526254401Scy
2527254401Scy/* ------------------------------------------------------------------------ */
2528254401Scy/* Function:    ipf_nat6_match                                              */
2529254401Scy/* Returns:     int - 0 == no match, 1 == match                             */
2530254401Scy/* Parameters:  fin(I)   - pointer to packet information                    */
2531254401Scy/*              np(I)    - pointer to NAT rule                              */
2532254401Scy/*                                                                          */
2533254401Scy/* Pull the matching of a packet against a NAT rule out of that complex     */
2534254401Scy/* loop inside ipf_nat6_checkin() and lay it out properly in its own        */
2535254401Scy/* function.                                                                */
2536254401Scy/* ------------------------------------------------------------------------ */
2537254401Scystatic int
2538254401Scyipf_nat6_match(fin, np)
2539254401Scy	fr_info_t *fin;
2540254401Scy	ipnat_t *np;
2541254401Scy{
2542254401Scy	frtuc_t *ft;
2543254401Scy	int match;
2544254401Scy
2545254401Scy	match = 0;
2546254401Scy	switch (np->in_osrcatype)
2547254401Scy	{
2548254401Scy	case FRI_NORMAL :
2549254401Scy		match = IP6_MASKNEQ(&fin->fin_src6, &np->in_osrcmsk6,
2550254401Scy				    &np->in_osrcip6);
2551254401Scy		break;
2552254401Scy	case FRI_LOOKUP :
2553254401Scy		match = (*np->in_osrcfunc)(fin->fin_main_soft, np->in_osrcptr,
2554254401Scy					   6, &fin->fin_src6, fin->fin_plen);
2555254401Scy		break;
2556254401Scy	}
2557254401Scy	match ^= ((np->in_flags & IPN_NOTSRC) != 0);
2558254401Scy	if (match)
2559254401Scy		return 0;
2560254401Scy
2561254401Scy	match = 0;
2562254401Scy	switch (np->in_odstatype)
2563254401Scy	{
2564254401Scy	case FRI_NORMAL :
2565254401Scy		match = IP6_MASKNEQ(&fin->fin_dst6, &np->in_odstmsk6,
2566254401Scy				    &np->in_odstip6);
2567254401Scy		break;
2568254401Scy	case FRI_LOOKUP :
2569254401Scy		match = (*np->in_odstfunc)(fin->fin_main_soft, np->in_odstptr,
2570254401Scy					   6, &fin->fin_dst6, fin->fin_plen);
2571254401Scy		break;
2572254401Scy	}
2573254401Scy
2574254401Scy	match ^= ((np->in_flags & IPN_NOTDST) != 0);
2575254401Scy	if (match)
2576254401Scy		return 0;
2577254401Scy
2578254401Scy	ft = &np->in_tuc;
2579254401Scy	if (!(fin->fin_flx & FI_TCPUDP) ||
2580254401Scy	    (fin->fin_flx & (FI_SHORT|FI_FRAGBODY))) {
2581254401Scy		if (ft->ftu_scmp || ft->ftu_dcmp)
2582254401Scy			return 0;
2583254401Scy		return 1;
2584254401Scy	}
2585254401Scy
2586254401Scy	return ipf_tcpudpchk(&fin->fin_fi, ft);
2587254401Scy}
2588254401Scy
2589254401Scy
2590254401Scy/* ------------------------------------------------------------------------ */
2591254401Scy/* Function:    ipf_nat6_checkout                                           */
2592254401Scy/* Returns:     int - -1 == packet failed NAT checks so block it,           */
2593254401Scy/*                     0 == no packet translation occurred,                 */
2594254401Scy/*                     1 == packet was successfully translated.             */
2595254401Scy/* Parameters:  fin(I)   - pointer to packet information                    */
2596254401Scy/*              passp(I) - pointer to filtering result flags                */
2597254401Scy/*                                                                          */
2598254401Scy/* Check to see if an outcoming packet should be changed.  ICMP packets are */
2599254401Scy/* first checked to see if they match an existing entry (if an error),      */
2600254401Scy/* otherwise a search of the current NAT table is made.  If neither results */
2601254401Scy/* in a match then a search for a matching NAT rule is made.  Create a new  */
2602254401Scy/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
2603254401Scy/* packet header(s) as required.                                            */
2604254401Scy/* ------------------------------------------------------------------------ */
2605254401Scyint
2606254401Scyipf_nat6_checkout(fin, passp)
2607254401Scy	fr_info_t *fin;
2608254401Scy	u_32_t *passp;
2609254401Scy{
2610254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
2611254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2612254401Scy	struct icmp6_hdr *icmp6 = NULL;
2613254401Scy	struct ifnet *ifp, *sifp;
2614254401Scy	tcphdr_t *tcp = NULL;
2615254401Scy	int rval, natfailed;
2616254401Scy	ipnat_t *np = NULL;
2617254401Scy	u_int nflags = 0;
2618254401Scy	i6addr_t ipa, iph;
2619254401Scy	int natadd = 1;
2620254401Scy	frentry_t *fr;
2621254401Scy	nat_t *nat;
2622254401Scy
2623254401Scy	if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0)
2624254401Scy		return 0;
2625254401Scy
2626254401Scy	icmp6 = NULL;
2627254401Scy	natfailed = 0;
2628254401Scy	fr = fin->fin_fr;
2629254401Scy	sifp = fin->fin_ifp;
2630254401Scy	if (fr != NULL) {
2631254401Scy		ifp = fr->fr_tifs[fin->fin_rev].fd_ptr;
2632254401Scy		if ((ifp != NULL) && (ifp != (void *)-1))
2633254401Scy			fin->fin_ifp = ifp;
2634254401Scy	}
2635254401Scy	ifp = fin->fin_ifp;
2636254401Scy
2637254401Scy	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
2638254401Scy		switch (fin->fin_p)
2639254401Scy		{
2640254401Scy		case IPPROTO_TCP :
2641254401Scy			nflags = IPN_TCP;
2642254401Scy			break;
2643254401Scy		case IPPROTO_UDP :
2644254401Scy			nflags = IPN_UDP;
2645254401Scy			break;
2646254401Scy		case IPPROTO_ICMPV6 :
2647254401Scy			icmp6 = fin->fin_dp;
2648254401Scy
2649254401Scy			/*
2650254401Scy			 * Apart from ECHO request and reply, all other
2651254401Scy			 * informational messages should not be translated
2652254401Scy			 * so as to keep IPv6 working.
2653254401Scy			 */
2654254401Scy			if (icmp6->icmp6_type > ICMP6_ECHO_REPLY)
2655254401Scy				return 0;
2656254401Scy
2657254401Scy			/*
2658254401Scy			 * This is an incoming packet, so the destination is
2659254401Scy			 * the icmp6_id and the source port equals 0
2660254401Scy			 */
2661254401Scy			if ((fin->fin_flx & FI_ICMPQUERY) != 0)
2662254401Scy				nflags = IPN_ICMPQUERY;
2663254401Scy			break;
2664254401Scy		default :
2665254401Scy			break;
2666254401Scy		}
2667254401Scy
2668254401Scy		if ((nflags & IPN_TCPUDP))
2669254401Scy			tcp = fin->fin_dp;
2670254401Scy	}
2671254401Scy
2672254401Scy	ipa = fin->fin_src6;
2673254401Scy
2674254401Scy	READ_ENTER(&softc->ipf_nat);
2675254401Scy
2676254401Scy	if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) &&
2677254401Scy	    (nat = ipf_nat6_icmperror(fin, &nflags, NAT_OUTBOUND)))
2678254401Scy		/*EMPTY*/;
2679254401Scy	else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
2680254401Scy		natadd = 0;
2681254401Scy	else if ((nat = ipf_nat6_outlookup(fin, nflags|NAT_SEARCH,
2682254401Scy					   (u_int)fin->fin_p,
2683254401Scy					   &fin->fin_src6.in6,
2684254401Scy					   &fin->fin_dst6.in6))) {
2685254401Scy		nflags = nat->nat_flags;
2686254401Scy	} else if (fin->fin_off == 0) {
2687254401Scy		u_32_t hv, nmsk = 0;
2688254401Scy		i6addr_t *msk;
2689254401Scy
2690254401Scy		/*
2691254401Scy		 * If there is no current entry in the nat table for this IP#,
2692254401Scy		 * create one for it (if there is a matching rule).
2693254401Scy		 */
2694254401Scymaskloop:
2695254401Scy		msk = &softn->ipf_nat6_map_active_masks[nmsk];
2696254401Scy		IP6_AND(&ipa, msk, &iph);
2697254401Scy		hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_maprules_sz);
2698254401Scy		for (np = softn->ipf_nat_map_rules[hv]; np; np = np->in_mnext) {
2699254401Scy			if ((np->in_ifps[1] && (np->in_ifps[1] != ifp)))
2700254401Scy				continue;
2701254401Scy			if (np->in_v[0] != 6)
2702254401Scy				continue;
2703254401Scy			if (np->in_pr[1] && (np->in_pr[1] != fin->fin_p))
2704254401Scy				continue;
2705254401Scy			if ((np->in_flags & IPN_RF) &&
2706254401Scy			    !(np->in_flags & nflags))
2707254401Scy				continue;
2708254401Scy			if (np->in_flags & IPN_FILTER) {
2709254401Scy				switch (ipf_nat6_match(fin, np))
2710254401Scy				{
2711254401Scy				case 0 :
2712254401Scy					continue;
2713254401Scy				case -1 :
2714254401Scy					rval = -1;
2715254401Scy					goto outmatchfail;
2716254401Scy				case 1 :
2717254401Scy				default :
2718254401Scy					break;
2719254401Scy				}
2720254401Scy			} else if (!IP6_MASKEQ(&ipa, &np->in_osrcmsk,
2721254401Scy					       &np->in_osrcip6))
2722254401Scy				continue;
2723254401Scy
2724254401Scy			if ((fr != NULL) &&
2725254401Scy			    !ipf_matchtag(&np->in_tag, &fr->fr_nattag))
2726254401Scy				continue;
2727254401Scy
2728254401Scy#ifdef IPF_V6_PROXIES
2729254401Scy			if (np->in_plabel != -1) {
2730254401Scy				if (((np->in_flags & IPN_FILTER) == 0) &&
2731254401Scy				    (np->in_odport != fin->fin_data[1]))
2732254401Scy					continue;
2733254401Scy				if (appr_ok(fin, tcp, np) == 0)
2734254401Scy					continue;
2735254401Scy			}
2736254401Scy#endif
2737254401Scy
2738254401Scy			if (np->in_flags & IPN_NO) {
2739254401Scy				np->in_hits++;
2740254401Scy				break;
2741254401Scy			}
2742254401Scy
2743254401Scy			MUTEX_ENTER(&softn->ipf_nat_new);
2744254401Scy			nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_OUTBOUND);
2745254401Scy			MUTEX_EXIT(&softn->ipf_nat_new);
2746254401Scy			if (nat != NULL) {
2747254401Scy				np->in_hits++;
2748254401Scy				break;
2749254401Scy			}
2750254401Scy			natfailed = -1;
2751254401Scy		}
2752254401Scy		if ((np == NULL) && (nmsk < softn->ipf_nat6_map_max)) {
2753254401Scy			nmsk++;
2754254401Scy			goto maskloop;
2755254401Scy		}
2756254401Scy	}
2757254401Scy
2758254401Scy	if (nat != NULL) {
2759254401Scy		rval = ipf_nat6_out(fin, nat, natadd, nflags);
2760254401Scy		if (rval == 1) {
2761254401Scy			MUTEX_ENTER(&nat->nat_lock);
2762254401Scy			ipf_nat_update(fin, nat);
2763254401Scy			nat->nat_bytes[1] += fin->fin_plen;
2764254401Scy			nat->nat_pkts[1]++;
2765254401Scy			MUTEX_EXIT(&nat->nat_lock);
2766254401Scy		}
2767254401Scy	} else
2768254401Scy		rval = natfailed;
2769254401Scyoutmatchfail:
2770254401Scy	RWLOCK_EXIT(&softc->ipf_nat);
2771254401Scy
2772254401Scy	switch (rval)
2773254401Scy	{
2774254401Scy	case -1 :
2775254401Scy		if (passp != NULL) {
2776254401Scy			NBUMPSIDE6D(1, ns_drop);
2777254401Scy			*passp = FR_BLOCK;
2778254401Scy			fin->fin_reason = FRB_NATV6;
2779254401Scy		}
2780254401Scy		fin->fin_flx |= FI_BADNAT;
2781254401Scy		NBUMPSIDE6D(1, ns_badnat);
2782254401Scy		break;
2783254401Scy	case 0 :
2784254401Scy		NBUMPSIDE6D(1, ns_ignored);
2785254401Scy		break;
2786254401Scy	case 1 :
2787254401Scy		NBUMPSIDE6D(1, ns_translated);
2788254401Scy		break;
2789254401Scy	}
2790254401Scy	fin->fin_ifp = sifp;
2791254401Scy	return rval;
2792254401Scy}
2793254401Scy
2794254401Scy/* ------------------------------------------------------------------------ */
2795254401Scy/* Function:    ipf_nat6_out                                                */
2796254401Scy/* Returns:     int - -1 == packet failed NAT checks so block it,           */
2797254401Scy/*                     1 == packet was successfully translated.             */
2798254401Scy/* Parameters:  fin(I)    - pointer to packet information                   */
2799254401Scy/*              nat(I)    - pointer to NAT structure                        */
2800254401Scy/*              natadd(I) - flag indicating if it is safe to add frag cache */
2801254401Scy/*              nflags(I) - NAT flags set for this packet                   */
2802254401Scy/*                                                                          */
2803254401Scy/* Translate a packet coming "out" on an interface.                         */
2804254401Scy/* ------------------------------------------------------------------------ */
2805254401Scystatic int
2806254401Scyipf_nat6_out(fin, nat, natadd, nflags)
2807254401Scy	fr_info_t *fin;
2808254401Scy	nat_t *nat;
2809254401Scy	int natadd;
2810254401Scy	u_32_t nflags;
2811254401Scy{
2812254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
2813254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
2814254401Scy	struct icmp6_hdr *icmp6;
2815254401Scy	tcphdr_t *tcp;
2816254401Scy	ipnat_t *np;
2817254401Scy	int skip;
2818254401Scy	int i;
2819254401Scy
2820254401Scy	tcp = NULL;
2821254401Scy	icmp6 = NULL;
2822254401Scy	np = nat->nat_ptr;
2823254401Scy
2824254401Scy	if ((natadd != 0) && (fin->fin_flx & FI_FRAG) && (np != NULL))
2825254401Scy		(void) ipf_frag_natnew(softc, fin, 0, nat);
2826254401Scy
2827254401Scy	/*
2828254401Scy	 * Address assignment is after the checksum modification because
2829254401Scy	 * we are using the address in the packet for determining the
2830254401Scy	 * correct checksum offset (the ICMP error could be coming from
2831254401Scy	 * anyone...)
2832254401Scy	 */
2833254401Scy	switch (nat->nat_dir)
2834254401Scy	{
2835254401Scy	case NAT_OUTBOUND :
2836254401Scy		fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6;
2837254401Scy		fin->fin_src6 = nat->nat_nsrc6;
2838254401Scy		fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6;
2839254401Scy		fin->fin_dst6 = nat->nat_ndst6;
2840254401Scy		break;
2841254401Scy
2842254401Scy	case NAT_INBOUND :
2843254401Scy		fin->fin_ip6->ip6_src = nat->nat_odst6.in6;
2844254401Scy		fin->fin_src6 = nat->nat_ndst6;
2845254401Scy		fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6;
2846254401Scy		fin->fin_dst6 = nat->nat_nsrc6;
2847254401Scy		break;
2848254401Scy
2849254401Scy	case NAT_DIVERTIN :
2850254401Scy	    {
2851254401Scy		mb_t *m;
2852254401Scy
2853254401Scy		skip = ipf_nat6_decap(fin, nat);
2854254401Scy		if (skip <= 0) {
2855254401Scy			NBUMPSIDE6D(1, ns_decap_fail);
2856254401Scy			return -1;
2857254401Scy		}
2858254401Scy
2859254401Scy		m = fin->fin_m;
2860254401Scy
2861369272Scy#if SOLARIS && defined(_KERNEL)
2862254401Scy		m->b_rptr += skip;
2863254401Scy#else
2864254401Scy		m->m_data += skip;
2865254401Scy		m->m_len -= skip;
2866254401Scy
2867254401Scy# ifdef M_PKTHDR
2868254401Scy		if (m->m_flags & M_PKTHDR)
2869254401Scy			m->m_pkthdr.len -= skip;
2870254401Scy# endif
2871254401Scy#endif
2872254401Scy
2873254401Scy		MUTEX_ENTER(&nat->nat_lock);
2874254401Scy		ipf_nat_update(fin, nat);
2875254401Scy		MUTEX_EXIT(&nat->nat_lock);
2876254401Scy		fin->fin_flx |= FI_NATED;
2877254401Scy		if (np != NULL && np->in_tag.ipt_num[0] != 0)
2878254401Scy			fin->fin_nattag = &np->in_tag;
2879254401Scy		return 1;
2880254401Scy		/* NOTREACHED */
2881254401Scy	    }
2882254401Scy
2883254401Scy	case NAT_DIVERTOUT :
2884254401Scy	    {
2885254401Scy		udphdr_t *uh;
2886254401Scy		ip6_t *ip6;
2887254401Scy		mb_t *m;
2888254401Scy
2889254401Scy		m = M_DUP(np->in_divmp);
2890254401Scy		if (m == NULL) {
2891254401Scy			NBUMPSIDE6D(1, ns_divert_dup);
2892254401Scy			return -1;
2893254401Scy		}
2894254401Scy
2895254401Scy		ip6 = MTOD(m, ip6_t *);
2896254401Scy
2897254401Scy		ip6->ip6_plen = htons(fin->fin_plen + 8);
2898254401Scy
2899254401Scy		uh = (udphdr_t *)(ip6 + 1);
2900254401Scy		uh->uh_ulen = htons(fin->fin_plen);
2901254401Scy
2902254401Scy		PREP_MB_T(fin, m);
2903254401Scy
2904254401Scy		fin->fin_ip6 = ip6;
2905254401Scy		fin->fin_plen += sizeof(ip6_t) + 8;	/* UDP + new IPv4 hdr */
2906254401Scy		fin->fin_dlen += sizeof(ip6_t) + 8;	/* UDP + old IPv4 hdr */
2907254401Scy
2908254401Scy		nflags &= ~IPN_TCPUDPICMP;
2909254401Scy
2910254401Scy		break;
2911254401Scy	    }
2912254401Scy
2913254401Scy	default :
2914254401Scy		break;
2915254401Scy	}
2916254401Scy
2917254401Scy	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
2918254401Scy		u_short *csump;
2919254401Scy
2920254401Scy		if ((nat->nat_nsport != 0) && (nflags & IPN_TCPUDP)) {
2921254401Scy			tcp = fin->fin_dp;
2922254401Scy
2923254401Scy			switch (nat->nat_dir)
2924254401Scy			{
2925254401Scy			case NAT_OUTBOUND :
2926254401Scy				tcp->th_sport = nat->nat_nsport;
2927254401Scy				fin->fin_data[0] = ntohs(nat->nat_nsport);
2928254401Scy				tcp->th_dport = nat->nat_ndport;
2929254401Scy				fin->fin_data[1] = ntohs(nat->nat_ndport);
2930254401Scy				break;
2931254401Scy
2932254401Scy			case NAT_INBOUND :
2933254401Scy				tcp->th_sport = nat->nat_odport;
2934254401Scy				fin->fin_data[0] = ntohs(nat->nat_odport);
2935254401Scy				tcp->th_dport = nat->nat_osport;
2936254401Scy				fin->fin_data[1] = ntohs(nat->nat_osport);
2937254401Scy				break;
2938254401Scy			}
2939254401Scy		}
2940254401Scy
2941254401Scy		if ((nat->nat_nsport != 0) && (nflags & IPN_ICMPQUERY)) {
2942254401Scy			icmp6 = fin->fin_dp;
2943254401Scy			icmp6->icmp6_id = nat->nat_nicmpid;
2944254401Scy		}
2945254401Scy
2946254401Scy		csump = ipf_nat_proto(fin, nat, nflags);
2947254401Scy
2948254401Scy		/*
2949254401Scy		 * The above comments do not hold for layer 4 (or higher)
2950254401Scy		 * checksums...
2951254401Scy		 */
2952254401Scy		if (csump != NULL) {
2953254401Scy			if (nat->nat_dir == NAT_OUTBOUND)
2954254401Scy				ipf_fix_outcksum(fin->fin_cksum, csump,
2955254401Scy						 nat->nat_sumd[0],
2956254401Scy						 nat->nat_sumd[1] +
2957254401Scy						 fin->fin_dlen);
2958254401Scy			else
2959254401Scy				ipf_fix_incksum(fin->fin_cksum, csump,
2960254401Scy						nat->nat_sumd[0],
2961254401Scy						nat->nat_sumd[1] +
2962254401Scy						fin->fin_dlen);
2963254401Scy		}
2964254401Scy	}
2965254401Scy
2966254401Scy	ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
2967254401Scy	/* ------------------------------------------------------------- */
2968254401Scy	/* A few quick notes:                                            */
2969254401Scy	/*      Following are test conditions prior to calling the       */
2970254401Scy	/*      ipf_proxy_check routine.                                 */
2971254401Scy	/*                                                               */
2972254401Scy	/*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
2973254401Scy	/*      with a redirect rule, we attempt to match the packet's   */
2974254401Scy	/*      source port against in_dport, otherwise we'd compare the */
2975254401Scy	/*      packet's destination.                                    */
2976254401Scy	/* ------------------------------------------------------------- */
2977254401Scy	if ((np != NULL) && (np->in_apr != NULL)) {
2978254401Scy		i = ipf_proxy_check(fin, nat);
2979369541Sgit2svn		if (i == -1) {
2980254401Scy			NBUMPSIDE6D(1, ns_ipf_proxy_fail);
2981254401Scy		}
2982254401Scy	} else {
2983254401Scy		i = 1;
2984254401Scy	}
2985254401Scy	fin->fin_flx |= FI_NATED;
2986254401Scy	return i;
2987254401Scy}
2988254401Scy
2989254401Scy
2990254401Scy/* ------------------------------------------------------------------------ */
2991254401Scy/* Function:    ipf_nat6_checkin                                            */
2992254401Scy/* Returns:     int - -1 == packet failed NAT checks so block it,           */
2993254401Scy/*                     0 == no packet translation occurred,                 */
2994254401Scy/*                     1 == packet was successfully translated.             */
2995254401Scy/* Parameters:  fin(I)   - pointer to packet information                    */
2996254401Scy/*              passp(I) - pointer to filtering result flags                */
2997254401Scy/*                                                                          */
2998254401Scy/* Check to see if an incoming packet should be changed.  ICMP packets are  */
2999254401Scy/* first checked to see if they match an existing entry (if an error),      */
3000254401Scy/* otherwise a search of the current NAT table is made.  If neither results */
3001254401Scy/* in a match then a search for a matching NAT rule is made.  Create a new  */
3002254401Scy/* NAT entry if a we matched a NAT rule.  Lastly, actually change the       */
3003254401Scy/* packet header(s) as required.                                            */
3004254401Scy/* ------------------------------------------------------------------------ */
3005254401Scyint
3006254401Scyipf_nat6_checkin(fin, passp)
3007254401Scy	fr_info_t *fin;
3008254401Scy	u_32_t *passp;
3009254401Scy{
3010254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3011254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3012254401Scy	struct icmp6_hdr *icmp6;
3013254401Scy	u_int nflags, natadd;
3014254401Scy	int rval, natfailed;
3015254401Scy	struct ifnet *ifp;
3016254401Scy	i6addr_t ipa, iph;
3017254401Scy	tcphdr_t *tcp;
3018254401Scy	u_short dport;
3019254401Scy	ipnat_t *np;
3020254401Scy	nat_t *nat;
3021254401Scy
3022254401Scy	if (softn->ipf_nat_stats.ns_rules == 0 || softn->ipf_nat_lock != 0)
3023254401Scy		return 0;
3024254401Scy
3025254401Scy	tcp = NULL;
3026254401Scy	icmp6 = NULL;
3027254401Scy	dport = 0;
3028254401Scy	natadd = 1;
3029254401Scy	nflags = 0;
3030254401Scy	natfailed = 0;
3031254401Scy	ifp = fin->fin_ifp;
3032254401Scy
3033254401Scy	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
3034254401Scy		switch (fin->fin_p)
3035254401Scy		{
3036254401Scy		case IPPROTO_TCP :
3037254401Scy			nflags = IPN_TCP;
3038254401Scy			break;
3039254401Scy		case IPPROTO_UDP :
3040254401Scy			nflags = IPN_UDP;
3041254401Scy			break;
3042254401Scy		case IPPROTO_ICMPV6 :
3043254401Scy			icmp6 = fin->fin_dp;
3044254401Scy
3045254401Scy			/*
3046254401Scy			 * Apart from ECHO request and reply, all other
3047254401Scy			 * informational messages should not be translated
3048254401Scy			 * so as to keep IPv6 working.
3049254401Scy			 */
3050254401Scy			if (icmp6->icmp6_type > ICMP6_ECHO_REPLY)
3051254401Scy				return 0;
3052254401Scy
3053254401Scy			/*
3054254401Scy			 * This is an incoming packet, so the destination is
3055254401Scy			 * the icmp6_id and the source port equals 0
3056254401Scy			 */
3057254401Scy			if ((fin->fin_flx & FI_ICMPQUERY) != 0) {
3058254401Scy				nflags = IPN_ICMPQUERY;
3059254401Scy				dport = icmp6->icmp6_id;
3060254401Scy			} break;
3061254401Scy		default :
3062254401Scy			break;
3063254401Scy		}
3064254401Scy
3065254401Scy		if ((nflags & IPN_TCPUDP)) {
3066254401Scy			tcp = fin->fin_dp;
3067254401Scy			dport = fin->fin_data[1];
3068254401Scy		}
3069254401Scy	}
3070254401Scy
3071254401Scy	ipa = fin->fin_dst6;
3072254401Scy
3073254401Scy	READ_ENTER(&softc->ipf_nat);
3074254401Scy
3075254401Scy	if ((fin->fin_p == IPPROTO_ICMPV6) && !(nflags & IPN_ICMPQUERY) &&
3076254401Scy	    (nat = ipf_nat6_icmperror(fin, &nflags, NAT_INBOUND)))
3077254401Scy		/*EMPTY*/;
3078254401Scy	else if ((fin->fin_flx & FI_FRAG) && (nat = ipf_frag_natknown(fin)))
3079254401Scy		natadd = 0;
3080254401Scy	else if ((nat = ipf_nat6_inlookup(fin, nflags|NAT_SEARCH,
3081254401Scy					  (u_int)fin->fin_p,
3082254401Scy					  &fin->fin_src6.in6, &ipa.in6))) {
3083254401Scy		nflags = nat->nat_flags;
3084254401Scy	} else if (fin->fin_off == 0) {
3085254401Scy		u_32_t hv, rmsk = 0;
3086254401Scy		i6addr_t *msk;
3087254401Scy
3088254401Scy		/*
3089254401Scy		 * If there is no current entry in the nat table for this IP#,
3090254401Scy		 * create one for it (if there is a matching rule).
3091254401Scy		 */
3092254401Scymaskloop:
3093254401Scy		msk = &softn->ipf_nat6_rdr_active_masks[rmsk];
3094254401Scy		IP6_AND(&ipa, msk, &iph);
3095254401Scy		hv = NAT_HASH_FN6(&iph, 0, softn->ipf_nat_rdrrules_sz);
3096254401Scy		for (np = softn->ipf_nat_rdr_rules[hv]; np; np = np->in_rnext) {
3097254401Scy			if (np->in_ifps[0] && (np->in_ifps[0] != ifp))
3098254401Scy				continue;
3099254401Scy			if (np->in_v[0] != 6)
3100254401Scy				continue;
3101254401Scy			if (np->in_pr[0] && (np->in_pr[0] != fin->fin_p))
3102254401Scy				continue;
3103254401Scy			if ((np->in_flags & IPN_RF) && !(np->in_flags & nflags))
3104254401Scy				continue;
3105254401Scy			if (np->in_flags & IPN_FILTER) {
3106254401Scy				switch (ipf_nat6_match(fin, np))
3107254401Scy				{
3108254401Scy				case 0 :
3109254401Scy					continue;
3110254401Scy				case -1 :
3111254401Scy					rval = -1;
3112254401Scy					goto inmatchfail;
3113254401Scy				case 1 :
3114254401Scy				default :
3115254401Scy					break;
3116254401Scy				}
3117254401Scy			} else {
3118254401Scy				if (!IP6_MASKEQ(&ipa, &np->in_odstmsk6,
3119254401Scy						&np->in_odstip6)) {
3120254401Scy					continue;
3121254401Scy				}
3122254401Scy				if (np->in_odport &&
3123254401Scy				    ((np->in_dtop < dport) ||
3124254401Scy				     (dport < np->in_odport)))
3125254401Scy					continue;
3126254401Scy			}
3127254401Scy
3128254401Scy#ifdef IPF_V6_PROXIES
3129254401Scy			if (np->in_plabel != -1) {
3130254401Scy				if (!appr_ok(fin, tcp, np)) {
3131254401Scy					continue;
3132254401Scy				}
3133254401Scy			}
3134254401Scy#endif
3135254401Scy
3136254401Scy			if (np->in_flags & IPN_NO) {
3137254401Scy				np->in_hits++;
3138254401Scy				break;
3139254401Scy			}
3140254401Scy
3141254401Scy			MUTEX_ENTER(&softn->ipf_nat_new);
3142254401Scy			nat = ipf_nat6_add(fin, np, NULL, nflags, NAT_INBOUND);
3143254401Scy			MUTEX_EXIT(&softn->ipf_nat_new);
3144254401Scy			if (nat != NULL) {
3145254401Scy				np->in_hits++;
3146254401Scy				break;
3147254401Scy			}
3148254401Scy			natfailed = -1;
3149254401Scy		}
3150254401Scy
3151254401Scy		if ((np == NULL) && (rmsk < softn->ipf_nat6_rdr_max)) {
3152254401Scy			rmsk++;
3153254401Scy			goto maskloop;
3154254401Scy		}
3155254401Scy	}
3156254401Scy	if (nat != NULL) {
3157254401Scy		rval = ipf_nat6_in(fin, nat, natadd, nflags);
3158254401Scy		if (rval == 1) {
3159254401Scy			MUTEX_ENTER(&nat->nat_lock);
3160254401Scy			ipf_nat_update(fin, nat);
3161254401Scy			nat->nat_bytes[0] += fin->fin_plen;
3162254401Scy			nat->nat_pkts[0]++;
3163254401Scy			MUTEX_EXIT(&nat->nat_lock);
3164254401Scy		}
3165254401Scy	} else
3166254401Scy		rval = natfailed;
3167254401Scyinmatchfail:
3168254401Scy	RWLOCK_EXIT(&softc->ipf_nat);
3169254401Scy
3170254401Scy	switch (rval)
3171254401Scy	{
3172254401Scy	case -1 :
3173254401Scy		if (passp != NULL) {
3174254401Scy			NBUMPSIDE6D(0, ns_drop);
3175254401Scy			*passp = FR_BLOCK;
3176254401Scy			fin->fin_reason = FRB_NATV6;
3177254401Scy		}
3178254401Scy		fin->fin_flx |= FI_BADNAT;
3179254401Scy		NBUMPSIDE6D(0, ns_badnat);
3180254401Scy		break;
3181254401Scy	case 0 :
3182254401Scy		NBUMPSIDE6D(0, ns_ignored);
3183254401Scy		break;
3184254401Scy	case 1 :
3185254401Scy		NBUMPSIDE6D(0, ns_translated);
3186254401Scy		break;
3187254401Scy	}
3188254401Scy	return rval;
3189254401Scy}
3190254401Scy
3191254401Scy
3192254401Scy/* ------------------------------------------------------------------------ */
3193254401Scy/* Function:    ipf_nat6_in                                                 */
3194254401Scy/* Returns:     int - -1 == packet failed NAT checks so block it,           */
3195254401Scy/*                     1 == packet was successfully translated.             */
3196254401Scy/* Parameters:  fin(I)    - pointer to packet information                   */
3197254401Scy/*              nat(I)    - pointer to NAT structure                        */
3198254401Scy/*              natadd(I) - flag indicating if it is safe to add frag cache */
3199254401Scy/*              nflags(I) - NAT flags set for this packet                   */
3200254401Scy/* Locks Held:   (READ)                                              */
3201254401Scy/*                                                                          */
3202254401Scy/* Translate a packet coming "in" on an interface.                          */
3203254401Scy/* ------------------------------------------------------------------------ */
3204254401Scystatic int
3205254401Scyipf_nat6_in(fin, nat, natadd, nflags)
3206254401Scy	fr_info_t *fin;
3207254401Scy	nat_t *nat;
3208254401Scy	int natadd;
3209254401Scy	u_32_t nflags;
3210254401Scy{
3211254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3212254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3213254401Scy	struct icmp6_hdr *icmp6;
3214254401Scy	u_short *csump;
3215254401Scy	tcphdr_t *tcp;
3216254401Scy	ipnat_t *np;
3217254401Scy	int skip;
3218254401Scy	int i;
3219254401Scy
3220254401Scy	tcp = NULL;
3221254401Scy	csump = NULL;
3222254401Scy	np = nat->nat_ptr;
3223254401Scy	fin->fin_fr = nat->nat_fr;
3224254401Scy
3225254401Scy	if (np != NULL) {
3226254401Scy		if ((natadd != 0) && (fin->fin_flx & FI_FRAG))
3227254401Scy			(void) ipf_frag_natnew(softc, fin, 0, nat);
3228254401Scy
3229254401Scy	/* ------------------------------------------------------------- */
3230254401Scy	/* A few quick notes:                                            */
3231254401Scy	/*      Following are test conditions prior to calling the       */
3232254401Scy	/*      ipf_proxy_check routine.                                 */
3233254401Scy	/*                                                               */
3234254401Scy	/*      A NULL tcp indicates a non TCP/UDP packet.  When dealing */
3235254401Scy	/*      with a map rule, we attempt to match the packet's        */
3236254401Scy	/*      source port against in_dport, otherwise we'd compare the */
3237254401Scy	/*      packet's destination.                                    */
3238254401Scy	/* ------------------------------------------------------------- */
3239254401Scy		if (np->in_apr != NULL) {
3240254401Scy			i = ipf_proxy_check(fin, nat);
3241254401Scy			if (i == -1) {
3242254401Scy				NBUMPSIDE6D(0, ns_ipf_proxy_fail);
3243254401Scy				return -1;
3244254401Scy			}
3245254401Scy		}
3246254401Scy	}
3247254401Scy
3248254401Scy	ipf_sync_update(softc, SMC_NAT, fin, nat->nat_sync);
3249254401Scy
3250254401Scy	/*
3251254401Scy	 * Fix up checksums, not by recalculating them, but
3252254401Scy	 * simply computing adjustments.
3253254401Scy	 * Why only do this for some platforms on inbound packets ?
3254254401Scy	 * Because for those that it is done, IP processing is yet to happen
3255254401Scy	 * and so the IPv4 header checksum has not yet been evaluated.
3256254401Scy	 * Perhaps it should always be done for the benefit of things like
3257254401Scy	 * fast forwarding (so that it doesn't need to be recomputed) but with
3258254401Scy	 * header checksum offloading, perhaps it is a moot point.
3259254401Scy	 */
3260254401Scy
3261254401Scy	switch (nat->nat_dir)
3262254401Scy	{
3263254401Scy	case NAT_INBOUND :
3264254401Scy		if ((fin->fin_flx & FI_ICMPERR) == 0) {
3265254401Scy			fin->fin_ip6->ip6_src = nat->nat_nsrc6.in6;
3266254401Scy			fin->fin_src6 = nat->nat_nsrc6;
3267254401Scy		}
3268254401Scy		fin->fin_ip6->ip6_dst = nat->nat_ndst6.in6;
3269254401Scy		fin->fin_dst6 = nat->nat_ndst6;
3270254401Scy		break;
3271254401Scy
3272254401Scy	case NAT_OUTBOUND :
3273254401Scy		if ((fin->fin_flx & FI_ICMPERR) == 0) {
3274254401Scy			fin->fin_ip6->ip6_src = nat->nat_odst6.in6;
3275254401Scy			fin->fin_src6 = nat->nat_odst6;
3276254401Scy		}
3277254401Scy		fin->fin_ip6->ip6_dst = nat->nat_osrc6.in6;
3278254401Scy		fin->fin_dst6 = nat->nat_osrc6;
3279254401Scy		break;
3280254401Scy
3281254401Scy	case NAT_DIVERTIN :
3282254401Scy	    {
3283254401Scy		udphdr_t *uh;
3284254401Scy		ip6_t *ip6;
3285254401Scy		mb_t *m;
3286254401Scy
3287254401Scy		m = M_DUP(np->in_divmp);
3288254401Scy		if (m == NULL) {
3289254401Scy			NBUMPSIDE6D(0, ns_divert_dup);
3290254401Scy			return -1;
3291254401Scy		}
3292254401Scy
3293254401Scy		ip6 = MTOD(m, ip6_t *);
3294254401Scy		ip6->ip6_plen = htons(fin->fin_plen + sizeof(udphdr_t));
3295254401Scy
3296254401Scy		uh = (udphdr_t *)(ip6 + 1);
3297254401Scy		uh->uh_ulen = ntohs(fin->fin_plen);
3298254401Scy
3299254401Scy		PREP_MB_T(fin, m);
3300254401Scy
3301254401Scy		fin->fin_ip6 = ip6;
3302254401Scy		fin->fin_plen += sizeof(ip6_t) + 8;	/* UDP + new IPv6 hdr */
3303254401Scy		fin->fin_dlen += sizeof(ip6_t) + 8;	/* UDP + old IPv6 hdr */
3304254401Scy
3305254401Scy		nflags &= ~IPN_TCPUDPICMP;
3306254401Scy
3307254401Scy		break;
3308254401Scy	    }
3309254401Scy
3310254401Scy	case NAT_DIVERTOUT :
3311254401Scy	    {
3312254401Scy		mb_t *m;
3313254401Scy
3314254401Scy		skip = ipf_nat6_decap(fin, nat);
3315254401Scy		if (skip <= 0) {
3316254401Scy			NBUMPSIDE6D(0, ns_decap_fail);
3317254401Scy			return -1;
3318254401Scy		}
3319254401Scy
3320254401Scy		m = fin->fin_m;
3321254401Scy
3322369272Scy#if SOLARIS && defined(_KERNEL)
3323254401Scy		m->b_rptr += skip;
3324254401Scy#else
3325254401Scy		m->m_data += skip;
3326254401Scy		m->m_len -= skip;
3327254401Scy
3328254401Scy# ifdef M_PKTHDR
3329254401Scy		if (m->m_flags & M_PKTHDR)
3330254401Scy			m->m_pkthdr.len -= skip;
3331254401Scy# endif
3332254401Scy#endif
3333254401Scy
3334254401Scy		ipf_nat_update(fin, nat);
3335254401Scy		fin->fin_flx |= FI_NATED;
3336254401Scy		if (np != NULL && np->in_tag.ipt_num[0] != 0)
3337254401Scy			fin->fin_nattag = &np->in_tag;
3338254401Scy		return 1;
3339254401Scy		/* NOTREACHED */
3340254401Scy	    }
3341254401Scy	}
3342254401Scy	if (nflags & IPN_TCPUDP)
3343254401Scy		tcp = fin->fin_dp;
3344254401Scy
3345254401Scy	if (!(fin->fin_flx & FI_SHORT) && (fin->fin_off == 0)) {
3346254401Scy		if ((nat->nat_odport != 0) && (nflags & IPN_TCPUDP)) {
3347254401Scy			switch (nat->nat_dir)
3348254401Scy			{
3349254401Scy			case NAT_INBOUND :
3350254401Scy				tcp->th_sport = nat->nat_nsport;
3351254401Scy				fin->fin_data[0] = ntohs(nat->nat_nsport);
3352254401Scy				tcp->th_dport = nat->nat_ndport;
3353254401Scy				fin->fin_data[1] = ntohs(nat->nat_ndport);
3354254401Scy				break;
3355254401Scy
3356254401Scy			case NAT_OUTBOUND :
3357254401Scy				tcp->th_sport = nat->nat_odport;
3358254401Scy				fin->fin_data[0] = ntohs(nat->nat_odport);
3359254401Scy				tcp->th_dport = nat->nat_osport;
3360254401Scy				fin->fin_data[1] = ntohs(nat->nat_osport);
3361254401Scy				break;
3362254401Scy			}
3363254401Scy		}
3364254401Scy
3365254401Scy
3366254401Scy		if ((nat->nat_odport != 0) && (nflags & IPN_ICMPQUERY)) {
3367254401Scy			icmp6 = fin->fin_dp;
3368254401Scy
3369254401Scy			icmp6->icmp6_id = nat->nat_nicmpid;
3370254401Scy		}
3371254401Scy
3372254401Scy		csump = ipf_nat_proto(fin, nat, nflags);
3373254401Scy	}
3374254401Scy
3375254401Scy	/*
3376254401Scy	 * The above comments do not hold for layer 4 (or higher) checksums...
3377254401Scy	 */
3378254401Scy	if (csump != NULL) {
3379254401Scy		if (nat->nat_dir == NAT_OUTBOUND)
3380254401Scy			ipf_fix_incksum(0, csump, nat->nat_sumd[0], 0);
3381254401Scy		else
3382254401Scy			ipf_fix_outcksum(0, csump, nat->nat_sumd[0], 0);
3383254401Scy	}
3384254401Scy	fin->fin_flx |= FI_NATED;
3385254401Scy	if (np != NULL && np->in_tag.ipt_num[0] != 0)
3386254401Scy		fin->fin_nattag = &np->in_tag;
3387254401Scy	return 1;
3388254401Scy}
3389254401Scy
3390254401Scy
3391254401Scy/* ------------------------------------------------------------------------ */
3392254401Scy/* Function:    ipf_nat6_newrewrite                                         */
3393254401Scy/* Returns:     int - -1 == error, 0 == success (no move), 1 == success and */
3394254401Scy/*                    allow rule to be moved if IPN_ROUNDR is set.          */
3395254401Scy/* Parameters:  fin(I) - pointer to packet information                      */
3396254401Scy/*              nat(I) - pointer to NAT entry                               */
3397254401Scy/*              ni(I)  - pointer to structure with misc. information needed */
3398254401Scy/*                       to create new NAT entry.                           */
3399254401Scy/* Write Lock:  ipf_nat                                                     */
3400254401Scy/*                                                                          */
3401254401Scy/* This function is responsible for setting up an active NAT session where  */
3402254401Scy/* we are changing both the source and destination parameters at the same   */
3403254401Scy/* time.  The loop in here works differently to elsewhere - each iteration  */
3404254401Scy/* is responsible for changing a single parameter that can be incremented.  */
3405254401Scy/* So one pass may increase the source IP#, next source port, next dest. IP#*/
3406254401Scy/* and the last destination port for a total of 4 iterations to try each.   */
3407254401Scy/* This is done to try and exhaustively use the translation space available.*/
3408254401Scy/* ------------------------------------------------------------------------ */
3409254401Scyint
3410254401Scyipf_nat6_newrewrite(fin, nat, nai)
3411254401Scy	fr_info_t *fin;
3412254401Scy	nat_t *nat;
3413254401Scy	natinfo_t *nai;
3414254401Scy{
3415254401Scy	int src_search = 1;
3416254401Scy	int dst_search = 1;
3417254401Scy	fr_info_t frnat;
3418254401Scy	u_32_t flags;
3419254401Scy	u_short swap;
3420254401Scy	ipnat_t *np;
3421254401Scy	nat_t *natl;
3422254401Scy	int l = 0;
3423254401Scy	int changed;
3424254401Scy
3425254401Scy	natl = NULL;
3426254401Scy	changed = -1;
3427254401Scy	np = nai->nai_np;
3428254401Scy	flags = nat->nat_flags;
3429254401Scy	bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
3430254401Scy
3431254401Scy	nat->nat_hm = NULL;
3432254401Scy
3433254401Scy	do {
3434254401Scy		changed = -1;
3435254401Scy		/* TRACE (l, src_search, dst_search, np) */
3436254401Scy
3437254401Scy		if ((src_search == 0) && (np->in_spnext == 0) &&
3438254401Scy		    (dst_search == 0) && (np->in_dpnext == 0)) {
3439254401Scy			if (l > 0)
3440254401Scy				return -1;
3441254401Scy		}
3442254401Scy
3443254401Scy		/*
3444254401Scy		 * Find a new source address
3445254401Scy		 */
3446254401Scy		if (ipf_nat6_nextaddr(fin, &np->in_nsrc, &frnat.fin_src6,
3447254401Scy				 &frnat.fin_src6) == -1) {
3448254401Scy			return -1;
3449254401Scy		}
3450254401Scy
3451254401Scy		if (IP6_ISZERO(&np->in_nsrcip6) &&
3452254401Scy		    IP6_ISONES(&np->in_nsrcmsk6)) {
3453254401Scy			src_search = 0;
3454254401Scy			if (np->in_stepnext == 0)
3455254401Scy				np->in_stepnext = 1;
3456254401Scy
3457254401Scy		} else if (IP6_ISZERO(&np->in_nsrcip6) &&
3458254401Scy			   IP6_ISZERO(&np->in_nsrcmsk6)) {
3459254401Scy			src_search = 0;
3460254401Scy			if (np->in_stepnext == 0)
3461254401Scy				np->in_stepnext = 1;
3462254401Scy
3463254401Scy		} else if (IP6_ISONES(&np->in_nsrcmsk)) {
3464254401Scy			src_search = 0;
3465254401Scy			if (np->in_stepnext == 0)
3466254401Scy				np->in_stepnext = 1;
3467254401Scy
3468254401Scy		} else if (!IP6_ISONES(&np->in_nsrcmsk6)) {
3469254401Scy			if (np->in_stepnext == 0 && changed == -1) {
3470254401Scy				IP6_INC(&np->in_snip);
3471254401Scy				np->in_stepnext++;
3472254401Scy				changed = 0;
3473254401Scy			}
3474254401Scy		}
3475254401Scy
3476254401Scy		if ((flags & IPN_TCPUDPICMP) != 0) {
3477254401Scy			if (np->in_spnext != 0)
3478254401Scy				frnat.fin_data[0] = np->in_spnext;
3479254401Scy
3480254401Scy			/*
3481254401Scy			 * Standard port translation.  Select next port.
3482254401Scy			 */
3483254401Scy			if ((flags & IPN_FIXEDSPORT) != 0) {
3484254401Scy				np->in_stepnext = 2;
3485254401Scy			} else if ((np->in_stepnext == 1) &&
3486254401Scy				   (changed == -1) && (natl != NULL)) {
3487254401Scy				np->in_spnext++;
3488254401Scy				np->in_stepnext++;
3489254401Scy				changed = 1;
3490254401Scy				if (np->in_spnext > np->in_spmax)
3491254401Scy					np->in_spnext = np->in_spmin;
3492254401Scy			}
3493254401Scy		} else {
3494254401Scy			np->in_stepnext = 2;
3495254401Scy		}
3496254401Scy		np->in_stepnext &= 0x3;
3497254401Scy
3498254401Scy		/*
3499254401Scy		 * Find a new destination address
3500254401Scy		 */
3501254401Scy		/* TRACE (fin, np, l, frnat) */
3502254401Scy
3503254401Scy		if (ipf_nat6_nextaddr(fin, &np->in_ndst, &frnat.fin_dst6,
3504254401Scy				      &frnat.fin_dst6) == -1)
3505254401Scy			return -1;
3506254401Scy
3507254401Scy		if (IP6_ISZERO(&np->in_ndstip6) &&
3508254401Scy		    IP6_ISONES(&np->in_ndstmsk6)) {
3509254401Scy			dst_search = 0;
3510254401Scy			if (np->in_stepnext == 2)
3511254401Scy				np->in_stepnext = 3;
3512254401Scy
3513254401Scy		} else if (IP6_ISZERO(&np->in_ndstip6) &&
3514254401Scy			   IP6_ISZERO(&np->in_ndstmsk6)) {
3515254401Scy			dst_search = 0;
3516254401Scy			if (np->in_stepnext == 2)
3517254401Scy				np->in_stepnext = 3;
3518254401Scy
3519254401Scy		} else if (IP6_ISONES(&np->in_ndstmsk6)) {
3520254401Scy			dst_search = 0;
3521254401Scy			if (np->in_stepnext == 2)
3522254401Scy				np->in_stepnext = 3;
3523254401Scy
3524254401Scy		} else if (!IP6_ISONES(&np->in_ndstmsk6)) {
3525254401Scy			if ((np->in_stepnext == 2) && (changed == -1) &&
3526254401Scy			    (natl != NULL)) {
3527254401Scy				changed = 2;
3528254401Scy				np->in_stepnext++;
3529254401Scy				IP6_INC(&np->in_dnip6);
3530254401Scy			}
3531254401Scy		}
3532254401Scy
3533254401Scy		if ((flags & IPN_TCPUDPICMP) != 0) {
3534254401Scy			if (np->in_dpnext != 0)
3535254401Scy				frnat.fin_data[1] = np->in_dpnext;
3536254401Scy
3537254401Scy			/*
3538254401Scy			 * Standard port translation.  Select next port.
3539254401Scy			 */
3540254401Scy			if ((flags & IPN_FIXEDDPORT) != 0) {
3541254401Scy				np->in_stepnext = 0;
3542254401Scy			} else if (np->in_stepnext == 3 && changed == -1) {
3543254401Scy				np->in_dpnext++;
3544254401Scy				np->in_stepnext++;
3545254401Scy				changed = 3;
3546254401Scy				if (np->in_dpnext > np->in_dpmax)
3547254401Scy					np->in_dpnext = np->in_dpmin;
3548254401Scy			}
3549254401Scy		} else {
3550254401Scy			if (np->in_stepnext == 3)
3551254401Scy				np->in_stepnext = 0;
3552254401Scy		}
3553254401Scy
3554254401Scy		/* TRACE (frnat) */
3555254401Scy
3556254401Scy		/*
3557254401Scy		 * Here we do a lookup of the connection as seen from
3558254401Scy		 * the outside.  If an IP# pair already exists, try
3559254401Scy		 * again.  So if you have A->B becomes C->B, you can
3560254401Scy		 * also have D->E become C->E but not D->B causing
3561254401Scy		 * another C->B.  Also take protocol and ports into
3562254401Scy		 * account when determining whether a pre-existing
3563254401Scy		 * NAT setup will cause an external conflict where
3564254401Scy		 * this is appropriate.
3565254401Scy		 *
3566254401Scy		 * fin_data[] is swapped around because we are doing a
3567254401Scy		 * lookup of the packet is if it were moving in the opposite
3568254401Scy		 * direction of the one we are working with now.
3569254401Scy		 */
3570254401Scy		if (flags & IPN_TCPUDP) {
3571254401Scy			swap = frnat.fin_data[0];
3572254401Scy			frnat.fin_data[0] = frnat.fin_data[1];
3573254401Scy			frnat.fin_data[1] = swap;
3574254401Scy		}
3575254401Scy		if (fin->fin_out == 1) {
3576254401Scy			natl = ipf_nat6_inlookup(&frnat,
3577254401Scy					    flags & ~(SI_WILDP|NAT_SEARCH),
3578254401Scy					    (u_int)frnat.fin_p,
3579254401Scy					    &frnat.fin_dst6.in6,
3580254401Scy					    &frnat.fin_src6.in6);
3581254401Scy
3582254401Scy		} else {
3583254401Scy			natl = ipf_nat6_outlookup(&frnat,
3584254401Scy					     flags & ~(SI_WILDP|NAT_SEARCH),
3585254401Scy					     (u_int)frnat.fin_p,
3586254401Scy					     &frnat.fin_dst6.in6,
3587254401Scy					     &frnat.fin_src6.in6);
3588254401Scy		}
3589254401Scy		if (flags & IPN_TCPUDP) {
3590254401Scy			swap = frnat.fin_data[0];
3591254401Scy			frnat.fin_data[0] = frnat.fin_data[1];
3592254401Scy			frnat.fin_data[1] = swap;
3593254401Scy		}
3594254401Scy
3595254401Scy		/* TRACE natl, in_stepnext, l */
3596254401Scy
3597254401Scy		if ((natl != NULL) && (l > 8))	/* XXX 8 is arbitrary */
3598254401Scy			return -1;
3599254401Scy
3600254401Scy		np->in_stepnext &= 0x3;
3601254401Scy
3602254401Scy		l++;
3603254401Scy		changed = -1;
3604254401Scy	} while (natl != NULL);
3605254401Scy	nat->nat_osrc6 = fin->fin_src6;
3606254401Scy	nat->nat_odst6 = fin->fin_dst6;
3607254401Scy	nat->nat_nsrc6 = frnat.fin_src6;
3608254401Scy	nat->nat_ndst6 = frnat.fin_dst6;
3609254401Scy
3610254401Scy	if ((flags & IPN_TCPUDP) != 0) {
3611254401Scy		nat->nat_osport = htons(fin->fin_data[0]);
3612254401Scy		nat->nat_odport = htons(fin->fin_data[1]);
3613254401Scy		nat->nat_nsport = htons(frnat.fin_data[0]);
3614254401Scy		nat->nat_ndport = htons(frnat.fin_data[1]);
3615254401Scy	} else if ((flags & IPN_ICMPQUERY) != 0) {
3616254401Scy		nat->nat_oicmpid = fin->fin_data[1];
3617254401Scy		nat->nat_nicmpid = frnat.fin_data[1];
3618254401Scy	}
3619254401Scy
3620254401Scy	return 0;
3621254401Scy}
3622254401Scy
3623254401Scy
3624254401Scy/* ------------------------------------------------------------------------ */
3625254401Scy/* Function:    ipf_nat6_newdivert                                          */
3626254401Scy/* Returns:     int - -1 == error, 0 == success                             */
3627254401Scy/* Parameters:  fin(I) - pointer to packet information                      */
3628254401Scy/*              nat(I) - pointer to NAT entry                               */
3629254401Scy/*              ni(I)  - pointer to structure with misc. information needed */
3630254401Scy/*                       to create new NAT entry.                           */
3631254401Scy/* Write Lock:  ipf_nat                                                     */
3632254401Scy/*                                                                          */
3633254401Scy/* Create a new NAT divert session as defined by the NAT rule.  This is     */
3634254401Scy/* somewhat different to other NAT session creation routines because we     */
3635254401Scy/* do not iterate through either port numbers or IP addresses, searching    */
3636254401Scy/* for a unique mapping, however, a complimentary duplicate check is made.  */
3637254401Scy/* ------------------------------------------------------------------------ */
3638254401Scyint
3639254401Scyipf_nat6_newdivert(fin, nat, nai)
3640254401Scy	fr_info_t *fin;
3641254401Scy	nat_t *nat;
3642254401Scy	natinfo_t *nai;
3643254401Scy{
3644254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3645254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3646254401Scy	fr_info_t frnat;
3647254401Scy	ipnat_t *np;
3648254401Scy	nat_t *natl;
3649254401Scy	int p;
3650254401Scy
3651254401Scy	np = nai->nai_np;
3652254401Scy	bcopy((char *)fin, (char *)&frnat, sizeof(*fin));
3653254401Scy
3654254401Scy	nat->nat_pr[0] = 0;
3655254401Scy	nat->nat_osrc6 = fin->fin_src6;
3656254401Scy	nat->nat_odst6 = fin->fin_dst6;
3657254401Scy	nat->nat_osport = htons(fin->fin_data[0]);
3658254401Scy	nat->nat_odport = htons(fin->fin_data[1]);
3659254401Scy	frnat.fin_src6 = np->in_snip6;
3660254401Scy	frnat.fin_dst6 = np->in_dnip6;
3661254401Scy
3662254401Scy	if (np->in_redir & NAT_DIVERTUDP) {
3663254401Scy		frnat.fin_data[0] = np->in_spnext;
3664254401Scy		frnat.fin_data[1] = np->in_dpnext;
3665254401Scy		frnat.fin_flx |= FI_TCPUDP;
3666254401Scy		p = IPPROTO_UDP;
3667254401Scy	} else {
3668254401Scy		frnat.fin_flx &= ~FI_TCPUDP;
3669254401Scy		p = IPPROTO_IPIP;
3670254401Scy	}
3671254401Scy
3672254401Scy	if (fin->fin_out == 1) {
3673254401Scy		natl = ipf_nat6_inlookup(&frnat, 0, p, &frnat.fin_dst6.in6,
3674254401Scy					 &frnat.fin_src6.in6);
3675254401Scy
3676254401Scy	} else {
3677254401Scy		natl = ipf_nat6_outlookup(&frnat, 0, p, &frnat.fin_dst6.in6,
3678254401Scy					  &frnat.fin_src6.in6);
3679254401Scy	}
3680254401Scy
3681254401Scy	if (natl != NULL) {
3682254401Scy		NBUMPSIDE6D(fin->fin_out, ns_divert_exist);
3683254401Scy		return -1;
3684254401Scy	}
3685254401Scy
3686254401Scy	nat->nat_nsrc6 = frnat.fin_src6;
3687254401Scy	nat->nat_ndst6 = frnat.fin_dst6;
3688254401Scy	if (np->in_redir & NAT_DIVERTUDP) {
3689254401Scy		nat->nat_nsport = htons(frnat.fin_data[0]);
3690254401Scy		nat->nat_ndport = htons(frnat.fin_data[1]);
3691254401Scy	}
3692254401Scy	nat->nat_pr[fin->fin_out] = fin->fin_p;
3693254401Scy	nat->nat_pr[1 - fin->fin_out] = p;
3694254401Scy
3695254401Scy	if (np->in_redir & NAT_REDIRECT)
3696254401Scy		nat->nat_dir = NAT_DIVERTIN;
3697254401Scy	else
3698254401Scy		nat->nat_dir = NAT_DIVERTOUT;
3699254401Scy
3700254401Scy	return 0;
3701254401Scy}
3702254401Scy
3703254401Scy
3704254401Scy/* ------------------------------------------------------------------------ */
3705254401Scy/* Function:    nat6_builddivertmp                                          */
3706254401Scy/* Returns:     int - -1 == error, 0 == success                             */
3707254401Scy/* Parameters:  np(I) - pointer to a NAT rule                               */
3708254401Scy/*                                                                          */
3709254401Scy/* For divert rules, a skeleton packet representing what will be prepended  */
3710254401Scy/* to the real packet is created.  Even though we don't have the full       */
3711254401Scy/* packet here, a checksum is calculated that we update later when we       */
3712254401Scy/* fill in the final details.  At present a 0 checksum for UDP is being set */
3713254401Scy/* here because it is expected that divert will be used for localhost.      */
3714254401Scy/* ------------------------------------------------------------------------ */
3715254401Scystatic int
3716254401Scyipf_nat6_builddivertmp(softn, np)
3717254401Scy	ipf_nat_softc_t *softn;
3718254401Scy	ipnat_t *np;
3719254401Scy{
3720254401Scy	udphdr_t *uh;
3721254401Scy	size_t len;
3722254401Scy	ip6_t *ip6;
3723254401Scy
3724254401Scy	if ((np->in_redir & NAT_DIVERTUDP) != 0)
3725254401Scy		len = sizeof(ip6_t) + sizeof(udphdr_t);
3726254401Scy	else
3727254401Scy		len = sizeof(ip6_t);
3728254401Scy
3729254401Scy	ALLOC_MB_T(np->in_divmp, len);
3730254401Scy	if (np->in_divmp == NULL) {
3731254401Scy		ATOMIC_INCL(softn->ipf_nat_stats.ns_divert_build);
3732254401Scy		return -1;
3733254401Scy	}
3734254401Scy
3735254401Scy	/*
3736254401Scy	 * First, the header to get the packet diverted to the new destination
3737254401Scy	 */
3738254401Scy	ip6 = MTOD(np->in_divmp, ip6_t *);
3739254401Scy	ip6->ip6_vfc = 0x60;
3740254401Scy	if ((np->in_redir & NAT_DIVERTUDP) != 0)
3741254401Scy		ip6->ip6_nxt = IPPROTO_UDP;
3742254401Scy	else
3743254401Scy		ip6->ip6_nxt = IPPROTO_IPIP;
3744254401Scy	ip6->ip6_hlim = 255;
3745254401Scy	ip6->ip6_plen = 0;
3746254401Scy	ip6->ip6_src = np->in_snip6.in6;
3747254401Scy	ip6->ip6_dst = np->in_dnip6.in6;
3748254401Scy
3749254401Scy	if (np->in_redir & NAT_DIVERTUDP) {
3750254401Scy		uh = (udphdr_t *)((u_char *)ip6 + sizeof(*ip6));
3751254401Scy		uh->uh_sum = 0;
3752254401Scy		uh->uh_ulen = 8;
3753254401Scy		uh->uh_sport = htons(np->in_spnext);
3754254401Scy		uh->uh_dport = htons(np->in_dpnext);
3755254401Scy	}
3756254401Scy
3757254401Scy	return 0;
3758254401Scy}
3759254401Scy
3760254401Scy
3761254401Scy#define	MINDECAP	(sizeof(ip6_t) + sizeof(udphdr_t) + sizeof(ip6_t))
3762254401Scy
3763254401Scy/* ------------------------------------------------------------------------ */
3764254401Scy/* Function:    nat6_decap                                                  */
3765254401Scy/* Returns:     int - -1 == error, 0 == success                             */
3766254401Scy/* Parameters:  fin(I) - pointer to packet information                      */
3767254401Scy/*              nat(I) - pointer to current NAT session                     */
3768254401Scy/*                                                                          */
3769254401Scy/* This function is responsible for undoing a packet's encapsulation in the */
3770254401Scy/* reverse of an encap/divert rule.  After removing the outer encapsulation */
3771254401Scy/* it is necessary to call ipf_makefrip() again so that the contents of 'fin'*/
3772254401Scy/* match the "new" packet as it may still be used by IPFilter elsewhere.    */
3773254401Scy/* We use "dir" here as the basis for some of the expectations about the    */
3774254401Scy/* outer header.  If we return an error, the goal is to leave the original  */
3775254401Scy/* packet information undisturbed - this falls short at the end where we'd  */
3776254401Scy/* need to back a backup copy of "fin" - expensive.                         */
3777254401Scy/* ------------------------------------------------------------------------ */
3778254401Scystatic int
3779254401Scyipf_nat6_decap(fin, nat)
3780254401Scy	fr_info_t *fin;
3781254401Scy	nat_t *nat;
3782254401Scy{
3783254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3784254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3785254401Scy	char *hdr;
3786254401Scy	int skip;
3787254401Scy	mb_t *m;
3788254401Scy
3789254401Scy	if ((fin->fin_flx & FI_ICMPERR) != 0) {
3790254401Scy		return 0;
3791254401Scy	}
3792254401Scy
3793254401Scy	m = fin->fin_m;
3794254401Scy	skip = fin->fin_hlen;
3795254401Scy
3796254401Scy	switch (nat->nat_dir)
3797254401Scy	{
3798254401Scy	case NAT_DIVERTIN :
3799254401Scy	case NAT_DIVERTOUT :
3800254401Scy		if (fin->fin_plen < MINDECAP)
3801254401Scy			return -1;
3802254401Scy		skip += sizeof(udphdr_t);
3803254401Scy		break;
3804254401Scy
3805254401Scy	case NAT_ENCAPIN :
3806254401Scy	case NAT_ENCAPOUT :
3807254401Scy		if (fin->fin_plen < (skip + sizeof(ip6_t)))
3808254401Scy			return -1;
3809254401Scy		break;
3810254401Scy	default :
3811254401Scy		return -1;
3812254401Scy		/* NOTREACHED */
3813254401Scy	}
3814254401Scy
3815254401Scy	/*
3816254401Scy	 * The aim here is to keep the original packet details in "fin" for
3817254401Scy	 * as long as possible so that returning with an error is for the
3818254401Scy	 * original packet and there is little undoing work to do.
3819254401Scy	 */
3820254401Scy	if (M_LEN(m) < skip + sizeof(ip6_t)) {
3821254401Scy		if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1)
3822254401Scy			return -1;
3823254401Scy	}
3824254401Scy
3825254401Scy	hdr = MTOD(fin->fin_m, char *);
3826254401Scy	fin->fin_ip6 = (ip6_t *)(hdr + skip);
3827254401Scy
3828254401Scy	if (ipf_pr_pullup(fin, skip + sizeof(ip6_t)) == -1) {
3829254401Scy		NBUMPSIDE6D(fin->fin_out, ns_decap_pullup);
3830254401Scy		return -1;
3831254401Scy	}
3832254401Scy
3833254401Scy	fin->fin_hlen = sizeof(ip6_t);
3834254401Scy	fin->fin_dlen -= skip;
3835254401Scy	fin->fin_plen -= skip;
3836254401Scy	fin->fin_ipoff += skip;
3837254401Scy
3838254401Scy	if (ipf_makefrip(sizeof(ip6_t), (ip_t *)hdr, fin) == -1) {
3839254401Scy		NBUMPSIDE6D(fin->fin_out, ns_decap_bad);
3840254401Scy		return -1;
3841254401Scy	}
3842254401Scy
3843254401Scy	return skip;
3844254401Scy}
3845254401Scy
3846254401Scy
3847254401Scy/* ------------------------------------------------------------------------ */
3848254401Scy/* Function:    nat6_nextaddr                                               */
3849254401Scy/* Returns:     int - -1 == bad input (no new address),                     */
3850254401Scy/*                     0 == success and dst has new address                 */
3851254401Scy/* Parameters:  fin(I) - pointer to packet information                      */
3852254401Scy/*              na(I)  - how to generate new address                        */
3853254401Scy/*              old(I) - original address being replaced                    */
3854254401Scy/*              dst(O) - where to put the new address                       */
3855254401Scy/* Write Lock:  ipf_nat                                                     */
3856254401Scy/*                                                                          */
3857254401Scy/* This function uses the contents of the "na" structure, in combination    */
3858254401Scy/* with "old" to produce a new address to store in "dst".  Not all of the   */
3859254401Scy/* possible uses of "na" will result in a new address.                      */
3860254401Scy/* ------------------------------------------------------------------------ */
3861254401Scystatic int
3862254401Scyipf_nat6_nextaddr(fin, na, old, dst)
3863254401Scy	fr_info_t *fin;
3864254401Scy	nat_addr_t *na;
3865254401Scy	i6addr_t *old, *dst;
3866254401Scy{
3867254401Scy	ipf_main_softc_t *softc = fin->fin_main_soft;
3868254401Scy	ipf_nat_softc_t *softn = softc->ipf_nat_soft;
3869254401Scy	i6addr_t newip, new;
3870254401Scy	u_32_t amin, amax;
3871254401Scy	int error;
3872254401Scy
3873254401Scy	new.i6[0] = 0;
3874254401Scy	new.i6[1] = 0;
3875254401Scy	new.i6[2] = 0;
3876254401Scy	new.i6[3] = 0;
3877254401Scy	amin = na->na_addr[0].in4.s_addr;
3878254401Scy
3879254401Scy	switch (na->na_atype)
3880254401Scy	{
3881254401Scy	case FRI_RANGE :
3882254401Scy		amax = na->na_addr[1].in4.s_addr;
3883254401Scy		break;
3884254401Scy
3885254401Scy	case FRI_NETMASKED :
3886254401Scy	case FRI_DYNAMIC :
3887254401Scy	case FRI_NORMAL :
3888254401Scy		/*
3889254401Scy		 * Compute the maximum address by adding the inverse of the
3890254401Scy		 * netmask to the minimum address.
3891254401Scy		 */
3892254401Scy		amax = ~na->na_addr[1].in4.s_addr;
3893254401Scy		amax |= amin;
3894254401Scy		break;
3895254401Scy
3896254401Scy	case FRI_LOOKUP :
3897254401Scy		break;
3898254401Scy
3899254401Scy	case FRI_BROADCAST :
3900254401Scy	case FRI_PEERADDR :
3901254401Scy	case FRI_NETWORK :
3902254401Scy	default :
3903254401Scy		return -1;
3904254401Scy	}
3905254401Scy
3906254401Scy	error = -1;
3907254401Scy	switch (na->na_function)
3908254401Scy	{
3909254401Scy	case IPLT_DSTLIST :
3910254401Scy		error = ipf_dstlist_select_node(fin, na->na_ptr, dst->i6,
3911254401Scy						NULL);
3912254401Scy		break;
3913254401Scy
3914254401Scy	case IPLT_NONE :
3915254401Scy		/*
3916254401Scy		 * 0/0 as the new address means leave it alone.
3917254401Scy		 */
3918254401Scy		if (na->na_addr[0].in4.s_addr == 0 &&
3919254401Scy		    na->na_addr[1].in4.s_addr == 0) {
3920254401Scy			new = *old;
3921254401Scy
3922254401Scy		/*
3923254401Scy		 * 0/32 means get the interface's address
3924254401Scy		 */
3925254401Scy		} else if (IP6_ISZERO(&na->na_addr[0].in6) &&
3926254401Scy			   IP6_ISONES(&na->na_addr[1].in6)) {
3927254401Scy			if (ipf_ifpaddr(softc, 6, na->na_atype,
3928254401Scy				       fin->fin_ifp, &newip, NULL) == -1) {
3929254401Scy				NBUMPSIDE6(fin->fin_out, ns_ifpaddrfail);
3930254401Scy				return -1;
3931254401Scy			}
3932254401Scy			new = newip;
3933254401Scy		} else {
3934254401Scy			new.in6 = na->na_nextip6;
3935254401Scy		}
3936254401Scy		*dst = new;
3937254401Scy		error = 0;
3938254401Scy		break;
3939254401Scy
3940254401Scy	default :
3941254401Scy		NBUMPSIDE6(fin->fin_out, ns_badnextaddr);
3942254401Scy		break;
3943254401Scy	}
3944254401Scy
3945254401Scy	return error;
3946254401Scy}
3947254401Scy
3948254401Scy
3949254401Scy/* ------------------------------------------------------------------------ */
3950254401Scy/* Function:    ipf_nat6_nextaddrinit                                       */
3951254401Scy/* Returns:     int - 0 == success, else error number                       */
3952254401Scy/* Parameters:  na(I)      - NAT address information for generating new addr*/
3953254401Scy/*              base(I)    - start of where to find strings                 */
3954254401Scy/*              initial(I) - flag indicating if it is the first call for    */
3955254401Scy/*                           this "na" structure.                           */
3956254401Scy/*              ifp(I)     - network interface to derive address            */
3957254401Scy/*                           information from.                              */
3958254401Scy/*                                                                          */
3959254401Scy/* This function is expected to be called in two scenarious: when a new NAT */
3960254401Scy/* rule is loaded into the kernel and when the list of NAT rules is sync'd  */
3961254401Scy/* up with the valid network interfaces (possibly due to them changing.)    */
3962254401Scy/* To distinguish between these, the "initial" parameter is used.  If it is */
3963254401Scy/* 1 then this indicates the rule has just been reloaded and 0 for when we  */
3964254401Scy/* are updating information.  This difference is important because in       */
3965254401Scy/* instances where we are not updating address information associated with  */
3966254401Scy/* a network interface, we don't want to disturb what the "next" address to */
3967254401Scy/* come out of ipf_nat6_nextaddr() will be.                                 */
3968254401Scy/* ------------------------------------------------------------------------ */
3969254401Scystatic int
3970254401Scyipf_nat6_nextaddrinit(softc, base, na, initial, ifp)
3971254401Scy	ipf_main_softc_t *softc;
3972254401Scy	char *base;
3973254401Scy	nat_addr_t *na;
3974254401Scy	int initial;
3975254401Scy	void *ifp;
3976254401Scy{
3977254401Scy	switch (na->na_atype)
3978254401Scy	{
3979254401Scy	case FRI_LOOKUP :
3980254401Scy		if (na->na_subtype == 0) {
3981254401Scy			na->na_ptr = ipf_lookup_res_num(softc, IPL_LOGNAT,
3982254401Scy							na->na_type,
3983254401Scy							na->na_num,
3984254401Scy							&na->na_func);
3985254401Scy		} else if (na->na_subtype == 1) {
3986254401Scy			na->na_ptr = ipf_lookup_res_name(softc, IPL_LOGNAT,
3987254401Scy							 na->na_type,
3988254401Scy							 base + na->na_num,
3989254401Scy							 &na->na_func);
3990254401Scy		}
3991254401Scy		if (na->na_func == NULL) {
3992254401Scy			IPFERROR(60072);
3993254401Scy			return ESRCH;
3994254401Scy		}
3995254401Scy		if (na->na_ptr == NULL) {
3996254401Scy			IPFERROR(60073);
3997254401Scy			return ESRCH;
3998254401Scy		}
3999254401Scy		break;
4000254401Scy	case FRI_DYNAMIC :
4001254401Scy	case FRI_BROADCAST :
4002254401Scy	case FRI_NETWORK :
4003254401Scy	case FRI_NETMASKED :
4004254401Scy	case FRI_PEERADDR :
4005254401Scy		if (ifp != NULL)
4006254401Scy			(void )ipf_ifpaddr(softc, 6, na->na_atype, ifp,
4007254401Scy					   &na->na_addr[0],
4008254401Scy					   &na->na_addr[1]);
4009254401Scy		break;
4010254401Scy
4011254401Scy	case FRI_SPLIT :
4012254401Scy	case FRI_RANGE :
4013254401Scy		if (initial)
4014254401Scy			na->na_nextip6 = na->na_addr[0].in6;
4015254401Scy		break;
4016254401Scy
4017254401Scy	case FRI_NONE :
4018254401Scy		IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6);
4019254401Scy		return 0;
4020254401Scy
4021254401Scy	case FRI_NORMAL :
4022254401Scy		IP6_ANDASSIGN(&na->na_addr[0].in6, &na->na_addr[1].in6);
4023254401Scy		break;
4024254401Scy
4025254401Scy	default :
4026254401Scy		IPFERROR(60074);
4027254401Scy		return EINVAL;
4028254401Scy	}
4029254401Scy
4030254401Scy	if (initial && (na->na_atype == FRI_NORMAL)) {
4031254401Scy		if (IP6_ISZERO(&na->na_addr[0].in6)) {
4032254401Scy			if (IP6_ISONES(&na->na_addr[1].in6) ||
4033254401Scy			    IP6_ISZERO(&na->na_addr[1].in6)) {
4034254401Scy				return 0;
4035254401Scy			}
4036254401Scy		}
4037254401Scy
4038254401Scy		na->na_nextip6 = na->na_addr[0].in6;
4039254401Scy		if (!IP6_ISONES(&na->na_addr[1].in6)) {
4040254401Scy			IP6_INC(&na->na_nextip6);
4041254401Scy		}
4042254401Scy	}
4043254401Scy
4044254401Scy	return 0;
4045254401Scy}
4046254401Scy
4047254401Scy
4048254401Scy/* ------------------------------------------------------------------------ */
4049254401Scy/* Function:    ipf_nat6_icmpquerytype                                      */
4050254401Scy/* Returns:     int - 1 == success, 0 == failure                            */
4051254401Scy/* Parameters:  icmptype(I) - ICMP type number                              */
4052254401Scy/*                                                                          */
4053254401Scy/* Tests to see if the ICMP type number passed is a query/response type or  */
4054254401Scy/* not.                                                                     */
4055254401Scy/* ------------------------------------------------------------------------ */
4056254401Scystatic int
4057254401Scyipf_nat6_icmpquerytype(icmptype)
4058254401Scy	int icmptype;
4059254401Scy{
4060254401Scy
4061254401Scy	/*
4062254401Scy	 * For the ICMP query NAT code, it is essential that both the query
4063254401Scy	 * and the reply match on the NAT rule. Because the NAT structure
4064254401Scy	 * does not keep track of the icmptype, and a single NAT structure
4065254401Scy	 * is used for all icmp types with the same src, dest and id, we
4066254401Scy	 * simply define the replies as queries as well. The funny thing is,
4067254401Scy	 * altough it seems silly to call a reply a query, this is exactly
4068254401Scy	 * as it is defined in the IPv4 specification
4069254401Scy	 */
4070254401Scy
4071254401Scy	switch (icmptype)
4072254401Scy	{
4073254401Scy
4074254401Scy	case ICMP6_ECHO_REPLY:
4075254401Scy	case ICMP6_ECHO_REQUEST:
4076254401Scy	/* route aedvertisement/solliciation is currently unsupported: */
4077254401Scy	/* it would require rewriting the ICMP data section            */
4078254401Scy	case ICMP6_MEMBERSHIP_QUERY:
4079254401Scy	case ICMP6_MEMBERSHIP_REPORT:
4080254401Scy	case ICMP6_MEMBERSHIP_REDUCTION:
4081254401Scy	case ICMP6_WRUREQUEST:
4082254401Scy	case ICMP6_WRUREPLY:
4083254401Scy	case MLD6_MTRACE_RESP:
4084254401Scy	case MLD6_MTRACE:
4085254401Scy		return 1;
4086254401Scy	default:
4087254401Scy		return 0;
4088254401Scy	}
4089254401Scy}
4090254401Scy#endif /* USE_INET6 */
4091