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